Classes and Objects

In the first chapter, you learnt that a class is a template and you create objects using the class template.

Before you understand more on objects, let us understand the basic computer and how it stores data. Every computer has two types of memory;

  • Hard disk - Data stored on Hard disk is permanent and remains on computer between computer restarts. E.g., your java class file that you saved on your hard disk will be available even if you shutdown and restart your computer.
  • Random Access Memory (a.k.a RAM) - The data stored in RAM is temporary. The data stored on RAM will be marked for deletion as soon as the program which created the data finishes its jobSearch. Any remaining data is also deleted as soon as computer shuts down. RAM is the play arena where your java programs run. Your java program creates objects and variables in RAM. Which also means that, these objects will vanish as soon you shut down your computer or after the program ends. And the next time you start your computer again and rerun your program, all the objects and variables will be recreated.

Object Oriented Programming

Java is an object oriented programming (a.k.a. OOP) language. What it means by that is, you can write a Java program by making use of OOP principles which includes; ability to create objects, ability to use inheritance, polymorphism, method overriding, method overloading etc., on objects. You will understand all these terms in detail in this lesson. So far we were more focused on understanding logic flow, simple algorithms and basic constructs of programming, which are mostly common across all types of programming languages.

Almost all software projects which incorporates OOP principles, mimic the real world business or enterprise. OOP was introduced for the very reason to help mimic the real world. The reason for that is, it is easy to understand your software system, when each and every class makes sense with the real world objects that we can easily relate to. Let us take an example;

Supposing you were tasked to create an online book store in Java, how would you start?

To begin with you list out all the possible classes that you may have to create to mimic the real world book store. Let us list out some basic templates:

  • We need a class for representing Books
  • We need a class for representing Customers
  • We need a class for representing Shopping Carts
  • We need a class to calculate billing for a customer
  • We need a class to hold the shipping information of the customer

Although the list goes on, let us first take the class Book to understand the basic principles. A book store will keep many books and we need a way to represent every single book in the software. For this you first create a class called Book like so:

class Book {

}

Even though the above class is a valid class, it is empty and does not depict anything useful. To make it useful, you can add the following constructs into your class;

  1. Add instance or class variables - these constructs hold the state of an object
  2. Add methods - these constructs help define the behavior of an object
  3. Add constructors - these construct are used to create new objects

Let us add these three constructs to make our Book class more useful.

Instance Variables

Instance variables are added when ever you want to hold a unique state of every object. To mimic the real world book, we at least need the following instance variables in Book class

  • Book Title
  • Book Author
  • Book Price

Here is the class with these variables added

class Book{
    String title;
    String author;
    double price;
}

Now we can create an object of class Book and hold some important characteristics of a book. Since books are in a bookstore, we will create another class called BookStore which will have the main method.

public class BookStore{

    public static void main(String[] args){
        Book book1 = new Book();
        book1.title = "Harry Potter and the Sorcerer's Stone";
        book1.author = "J.K. Rowling";
        book1.price = 29;

        System.out.println(book1.title);
    }
}

class Book{
    String title;
    String author;
    double price;
}

Output: Harry Potter and the Sorcerer's Stone

In the above example, you initialized the three variables (title, author and price) using the dot(.) operator on the object. E.g.;

book1.price = 29;

and you accessed the initialized value for printing, using the dot operator again in the system.out.println statement: book.title

Notice the new keyword used to create a Book object. Once the object is created using the new keyword, it is assigned to a variable called book1 which is of type Book. So, you instantiated an object of class Book and you initialized its instance variables which identifies the specific book of interest. Now if you want to create a second book, then you would instantiate the Book class again to get another book. You can again set its instance variables which identify the second book.

public class BookStore{

    public static void main(String[] args){
        Book book1 = new Book();
        book1.title = "Harry Potter and the Sorcerer's Stone";
        book1.author = "J.K. Rowling";
        book1.price = 29;

        System.out.println(book1.title);

        Book book2 = new Book();
        book2.title = "Harry Potter and the Prisoner of Azkaban";
        book2.author = "J.K. Rowling";
        book2.price = 31;

        System.out.println(book2.title);
    }
}

class Book{
    String title;
    String author;
    double price;
}

Output: Harry Potter and the Sorcerer's Stone
Harry Potter and the Prisoner of Azkaban

What is happening in RAM?

As and when you create objects, a new space is allocated to keep the objects and its instance values. The more objects you create, the more of your RAM gets filled up with the created objects. Each object uniquely identifies one and only one book. Now you have successfully implemented OOP concept in creating books!

The figure below shows the depiction of how objects may occupy the RAM area.

image alt text

Encapsulation

Although the above initialization of variables work, it is not a recommended way for initialization. You want to encapsulate the instance variables and give methods to set (initialize) the values and get (retrieve) the values. Encapsulation helps keep the data safe from being manipulated from other classes. You see, the data for title, author and price variables belongs to the Book class and hence you should provide a method in Book class which can be used to change the values for these variables. Without encapsulation, any other class can directly access the data kept inside any object by using the dot operator, like the way it is done in the previous example. So let us fix the code:

 public class BookStore{

     public static void main(String[] args){
         Book book1 = new Book();
         book1.setTitle("Harry Potter and the Sorcerer's Stone");
         book1.setAuthor("J.K. Rowling");
         book1.setPrice(29);

         System.out.println(book1.getTitle());

         Book book2 = new Book();
         book2.setTitle("Harry Potter and the Prisoner of Azkaban");
         book2.setAuthor("J.K. Rowling");
         book2.setPrice(31);

         System.out.println(book2.getTitle());
     }
 }

 class Book{

     private String title;
     private String author;
     private double price;

     public void setTitle(String title){
         this.title = title;
     }
     public void setAuthor(String author){
         this.author = author;
     }
     public void setPrice(double price){
         this.price = price;
     }

     public String getTitle(){
         return this.title;
     }
     public String getAuthor(){
         return this.author;
     }
     public double getPrice(){
         return this.price;
     }
 }

bulb

this keyword used in the above example, refers to the current object which is being invoked. this is used with the dot operator to refer to instance variables, methods and constructors of the given object. You will learn on constructors shortly.

 

Steps in encapsulation

  • Declare all variables of a class private. Using private modifier ensures that only the methods of that object alone can modify the variables.

  • Add setter methods which can be used to set the variable values.

  • Add getter methods which can be used to retrieve the variable values.

  • Both getter and setter methods are optional. If you do not want any other object to either set or get the data that is kept inside an object, you do not need to give these methods. Also, you can add more statements in the methods to either validate or manipulate the given value before setting/getting the variable. This is the real reason behind encapsulation.

Inheritance (IS-A relationship)

Inheritance is another important feature of OOP. It is a mechanism by which one class (a.k.a child class) is allowed to keep the variables and methods declared in another class (a.k.a parent class). The child class is declared by extending the parent class using the extend keyword. Inheritance allows us to create hierarchy among classes to help organize classes in a meaningful way.

In our examples, let us assume that our BookStore sells two types of books: E-Books and Hard covered books. Both types of books need the instance variables that are declared above. However E-Books need an extra instance variable to hold the URL of the book and the Hard covered books do not need this variable. But the hard covered books need an instance variable to hold the value of the quantity of books in stock, which the E-Books do not need. Instead of creating two classes and repeating the common variables, you can keep the above class Book and create two child classes called EBook and HardCoveredBook by extending the parent class Book, thereby inheriting the common variables. Lets do that as below:


class Book{

     private String title;
     private String author;
     private double price;

     public void setTitle(String title){
         this.title = title;
     }
     public void setAuthor(String author){
         this.author = author;
     }
     public void setPrice(double price){
         this.price = price;
     }

     public String getTitle(){
         return this.title;
     }
     public String getAuthor(){
         return this.author;
     }
     public double getPrice(){
         return this.price;
     }
 }

class EBook extends Book {

    private String url;

    public void setUrl(String url){
        this.url = url;
    }
}

class HardCoveredBook extends Book {

    private int qtyInStock;

    public void setQtyInStock(int qty){
        this.qtyInStock= qty;
    }
}

public class BookStore{

     public static void main(String[] args){
         EBook book1 = new EBook();
         book1.setTitle("Harry Potter and the Sorcerer's Stone");
         book1.setAuthor("J.K. Rowling");
         book1.setPrice(29);
         book1.setUrl("http://ebooks.zzz/harrypotter");

         System.out.println(book1.getTitle());

         HardCoveredBook book2 = new HardCoveredBook();
         book2.setTitle("Harry Potter and the Prisoner of Azkaban");
         book2.setAuthor("J.K. Rowling");
         book2.setPrice(31);
         book2.setQtyInStock(20);

         System.out.println(book2.getTitle());
     }
 }

Notice that you are able to set title, author and price variables on EBook and HardCoveredBook even though they are not declared in EBook or HardCoveredBook classes. You are able to do that because you extended the Book class which has these variables. Try removing the extend keyword and see if you can still access the variables.

Points to note

  • You use extends keyword to make a child of another class.

  • Parent class is also called Super class

  • Child class is also called Sub class

  • All the parents methods, variables and constructors are visible to the child except for the ones declared private

  • When ever you instantiate a child, a corresponding parent object is also created along with the child.

 

IS-A relationship

  • An instance of a child class is not only of the type of the child class but also of the parent. In the above example, instance book1 is of type EBook and also of type Book. In OOP we say book1 IS-A EBook and book1 IS-A Book also. You will learn more on IS-A relationship and polymorphism in the next chapter.

Constructors

In the above examples, you were able to construct an instance using the new keyword using the default constructor. Default constructor is defined in the top level class called Object. Every class in Java inherits from the top level Object class. That is reason you did not have to define the default constructor in your class. However when you add any other constructor, you have to override the default constructor of the Object class.

Constructor is defined very similar to a method except the name of the constructor should be the same as the class name and constructors do not have a return type.

Here is an example:

class Book{

     private String title;
     private String author;
     private double price;

     public void setTitle(String title){
         this.title = title;
     }
     public void setAuthor(String author){
         this.author = author;
     }
     public void setPrice(double price){
         this.price = price;
     }

     public String getTitle(){
         return this.title;
     }
     public String getAuthor(){
         return this.author;
     }
     public double getPrice(){
         return this.price;
     }

     // default no argument constructor. 
     // Need not define if there are no other constructors defined.
     public Book(){

     }

     // constructor which takes one argument title. 
     // The instance value of title is initialized in this constructor
     public Book(String title){
         this.title = title;
     }

     // constructor which takes two argument; tile and author. 
     // The instance value of title and author is initialized in this constructor
     public Book(String title, String author){
         this.title = title;
         this.author = author;
     }


     public static void main(String[] args){
         Book book1  = new Book();
         book1.setTitle("Harry Potter and the Sorcerer's Stone");
         System.out.println(book1.getTitle());

         Book book2 = new Book("Harry Potter and the Prisoner of Azkaban");
         System.out.println(book2.getTitle());
     }
 }

Output:

Harry Potter and the Sorcerer's Stone Harry Potter and the Prisoner of Azkaban

In the above example, you see three constructors defined for Book class

  • No argument constructor. If there are no other constructors defined, then there is no need to define this constructor as every class inherits the no argument constructor from the top level class Object.
  • Constructor which takes one argument title. The argument value 'title' initializes the instance variable 'title' when this constructor is invoked.
  • Constructor which takes two arguments; title and author - Here also argument values received through the constructor, initializes the instance variables of the object when this constructor is invoked.

Exercise: Can you add one more constructor which takes three arguments 'title', 'author' and 'price' ?

Notice the output of the program. You did not have to set the title using the setter method when you used the second constructor. You add additional constructors when you have all the variable values right at the time the object is instantiated. That way you initialize the values through the constructors and save yourself from adding many setter statements in the code.

Aggregation (HAS-A relationship)

Aggregation is relationship between two classes. When one class references another then we say that first class HAS-A second class inside it. This is called aggregation. You have already seen aggregation in earlier examples. If you see the BookStore in the above code, you will see that BookStore class instantiates Book class inside its main method. Aggregation helps us to modularize code into separate entities.

Let us take another example. Let us assume you want to model a school in Java. You would start with defining the following classes: School, Teacher, Student and let us say you also want to represent address of each of these entities. Then you would define an Address class. Here is one hypothetical example

class Address{

    private String streetAddress;
    private String zipcode;
    private String city;
    private String country;

    // setter and getter method go here. Not shown to keep code simple
}

class School {

    private String name;
    private Address address;

}

class Student{
    private String name;
    private Address address;
    private String grade;
    private String age;
    private String gender;
}

class Teacher{
    private String name;
    private Address address;
    private String subject;
}

In the above hypothetical example, Address object is referenced by School, Student and Teacher. The instances of all of them need another object which keeps the address information. In other words, instance of Student object HAS-A Address object, instance of Teacher object HAS-A Address object and finally instance of School object HAS-A Address object. You can get to a specific address object by first using the reference to the object which is holding it. This is called Aggregation. This is most of the time a one-way association. In the above example, you cannot get the reference of the School, Teacher or Student instances by having an Address instance. But the other way around is possible.

results matching ""

    No results matching ""