Home Java Introduction Decisions Loops Classes Access Inheritance Inner Classes Arrays Exceptions Strings Generics Container Input/Output MultiThreading jdk8 Versions Books

Classes Basics


Contents

Introduction

This chapter examines some of the basic features of classes.

Reference

A reference is a handle to an object. It "points" to the object created on the heap. Anything created with the keyword "new" will return an object.

File: Reference.java
interface PersonInterface
{
  public int getId() ;

}

class Person implements PersonInterface
{
    int id ;
    String firstName ;
    String laststName ;

    //a static data member does not need
    //an instance of an object. It is not
    //associated with an object.
    static int NoPersonObjects = 0 ;

    //Constructor called when creating the object
    Person()
    {
      NoPersonObjects++ ;
    }

    public int getId()
     {
        return id ;
     }
    //A static method is like a global method
    static int getNoPersonObjects()
     {
        //Cannot access class data members
        //id firstName
        return NoPersonObjects ;
     }

}


public class Reference
{
   public static void incrementPersonId( Person personObject )
   {
      //Any changes made here are made in the object in the heap
      //and are reflected in the original object
      personObject.id++ ;
   }

   public static void main( String args[] )
     {
       //Compiler will not allow
       //Person personObject ;
       //null means there does not exist a value
       //yet for the personObject1
       Person personObject1 = null ;
       System.out.println( personObject1 ) ;

       // Can't call a method without an object
       // getId()

       //We get a handle/reference but it's not the
       //raw memory address to the person object.
       //Person object is created on the heap
       personObject1 = new Person() ;
       personObject1.id = 1 ;

       System.out.println( "personObject.getId(): " +
       personObject1.getId()  ) ;

       Person personObject2 = new Person() ;
       personObject2.id = 2 ;

       incrementPersonId( personObject2  ) ;

       System.out.println( "personObject2.id: " +
           personObject2.id ) ;

       //Using the static method getNoPersonObjects()
       System.out.println( "static method Person.getNoPersonObjects():" +
          Person.getNoPersonObjects() ) ;
       System.out.println( "static method personObject1.getNoPersonObjects():" +
          personObject1.getNoPersonObjects() ) ;

      //Using the static data member NoPersonObjects
       System.out.println( "static data member Person.NoPersonObjects:" +
          Person.NoPersonObjects ) ;
       System.out.println( "static data member personObject1.NoPersonObjects:" +
          personObject1.NoPersonObjects ) ;

       PersonInterface personInterfaceObject = personObject2 ;
       //System.out.println( personInterfaceObject.id ) ;
       //Can call methods defined in the interface
       System.out.println( personInterfaceObject.getId() ) ;
     }

}
C:\WebSite\Learn\2\Java\intro>java Reference
null
personObject.getId(): 1
personObject2.id: 3
static method Person.getNoPersonObjects():2
static method personObject1.getNoPersonObjects():2
static data member Person.NoPersonObjects:2
static data member personObject1.NoPersonObjects:2
3

At the top of this class we have defined an interface. An interface has just the method declarations and not the definition. A class can implement an interface and we can construct class objects and assign them to interface variables. A class can implement multiple interfaces. It allows for a more flexible scheme in our OOP. Normally we will not be creating just a single class but multiple classes, interfaces that will be working together. Take the "Vector" class in "java.util" package. It implements the following interfaces: "Serializable, Cloneable, Iterable, Collection, List, RandomAccess" and extends "AbstractList" which in turn extends "AbstractCollection.
The "Person" class has data members, methods. If we create an object we get a new copy of the data members every time.
       personObject1 = new Person() ;
       personObject1.id = 1 ;

       System.out.println( "personObject.getId(): " +
       personObject1.getId()  ) ;

       Person personObject2 = new Person() ;
       personObject2.id = 2 ;
We have 2 objects "personObject1" and "personObject2" and each one has their own copy of the "id" data member. However a static data member has only 1 copy regardless of how many object of the class are created. We can think of static data members as global members. In order to access the static data member/methods we can use the "." notation with either the class name or an object name.
      //Using the static data member NoPersonObjects
       System.out.println( "static data member Person.NoPersonObjects:" +
          Person.NoPersonObjects ) ;
       System.out.println( "static data member personObject1.NoPersonObjects:" +
          personObject1.NoPersonObjects ) ;

       PersonInterface personInterfaceObject = personObject2 ;
       //System.out.println( personInterfaceObject.id ) ;
       //Can call methods defined in the interface
       System.out.println( personInterfaceObject.getId() ) ;

 
We create a Person object using a special method called the constructor.
personObject1 = new Person() ;
The "new Person()" causes the constructor of the Person class to be called
and in turn increments the static data member "NoPersonObjects" . This data
member keeps track of how many "Person" objects are created in our program.
We print this value at the end of the program and see that the value is 2
indicating that Person 2 objects got created during the duration of the program.

We can pass the object as an argument to another function:
       Person personObject2 = new Person() ;
       personObject2.id = 2 ;

       incrementPersonId( personObject2  ) ;
The "personObject2" is a reference and we pass it to the method "incrementPersonId". Java has pass by value and what that means is the reference is copied by value in the method:
   public static void incrementPersonId( Person personObject )
The "personObject" in the method now contains the reference and any changes made inside the method are reflected back in the calling function of "main" .
     System.out.println( "personObject2.id: " +
           personObject2.id ) ;

    personObject2.id: 3
    Change is reflected after calling the incrementPersonId method.
We can also assign a "Person" object to an interface object that it implements.
       PersonInterface personInterfaceObject = personObject2 ;
We can now invoke the methods of the interface. The "Person" class must implement all the methods of the interface else we will get a compiler error when compiling "Person.java" .
Exercise 1. What does the below file print ?

File: PassByValue.java
public class PassByValue
{

    public static void changeValues( int i1 , int i2 )
     {
        i1 = 21  ;
        i2 = 22 ;
     }

    public static void main(String[] args)
    {
         int x1 = 10  ;
         int x2 = 11  ;
         changeValues( x1 , x2 )   ;
         System.out.println( x1 )  ;
         System.out.println( x2 )  ;
    }

}

single object hierarchy

In Java all classes whether explicity or implicitly derive from the class "Object".This is called a single object hierarchy.

File: SingleHierarchy.java

class Person
{
    int id ;
    String firstName ;
    String laststName ;


}

class AnotherPerson
{
    int id ;
    String firstName ;
    String lastName ;

   public void setValue(int idP)
    {
       id = idP ;
    }
   public void setValue(int idP, String firstNameP )
    {
          id = idP ;
          firstName = firstNameP ;
    }
   public void setValue(int idP , String firstNameP ,
         String lastNameP )
    {
          id = idP ;
          firstName = firstNameP ;
          lastName = lastNameP ;
    }

    public String toString()
     {
        return( "id: " + id + " Name: " +
               firstName + " " +
                lastName  ) ;
     }


}



public class SingleHierarchy
{

   public static void main( String args[] )
     {
         Person personObject1 = new Person() ;
         //Uses the Object class toString method
         System.out.println( personObject1 ) ;

         System.out.println( Integer.toHexString(personObject1.hashCode())  ) ;

         AnotherPerson  personObject2 = new AnotherPerson() ;



         //Method Overloading
          personObject2.setValue( 1 )  ;
          personObject2.setValue( 1 , "Jack" )  ;
          personObject2.setValue( 1 , "Jack" , "Dempsey" )  ;

         //Uses the AnotherPerson class toString method
         //Method Overriding
         System.out.println( personObject2 ) ;



     }

}

C:\WebSite\Learn\2\Java\intro>java SingleHierarchy
Person@15db9742
15db9742
id: 1 Name: Jack Dempsey




in the below code:
        Person personObject1 = new Person() ;
         //Uses the Object class toString method
         System.out.println( personObject1 ) ;

The JDK is using the "toString()" method of the Object class to print the object. This prints the class of the name and the hashcode. In the next line we call "hashCode()" which is another method of the Object class. We have 2 important concepts for methods "overriding" and "overloading" . Method "overriding" applie to an inherited class when it's method has the same signature as the base method. Method "overloading" is the ability to have methods by the same name that differ in their arguments.
The "setValue()" method has 3 variations differeing in the number of arguments passed to it. We also have another class "AnotherPerson" that has the "toString()" method that overrides the "Object toString()" method and doing a "println" invokes the method and we get more readable output.
Another method that "Object" has is the "equals" method. We do have the "==" comparator that compares 2 objects. If the objects are the same ( have the same references ) then it return true else it returns false. The "Object" class has an "equals" method that does the same comparision as "==". However we can change that by overriding it.

File: Equals.java

class Person
{
    int id ;
    String firstName ;
    String laststName ;


}

class AnotherPerson
{
    int id ;
    String firstName ;
    String lastName ;
    public boolean equals( AnotherPerson obj1 )
     {
         return(  id == obj1.id ) ;
     }

}



public class Equals
{

   public static void main( String args[] )
     {
         Person personObject1 = new Person() ;
         personObject1.id = 1 ;

         Person personObject2 = new Person() ;
         personObject2.id = 1 ;

         System.out.println( personObject1 == personObject1 ) ;
         System.out.println( personObject1 == personObject2 ) ;

         //By default equals works like ==
         System.out.println( personObject1.equals( personObject1)   ) ;
         System.out.println( personObject1.equals( personObject2 ) ) ;


         AnotherPerson personObject3 = new AnotherPerson() ;
         personObject3.id = 1 ;

         AnotherPerson personObject4 = new AnotherPerson() ;
         personObject4.id = 1 ;

         System.out.println( "---------------" ) ;
         //Works as before
         System.out.println( personObject3 == personObject3 ) ;
         System.out.println( personObject3 == personObject4 ) ;

         //Custom behavior
         System.out.println( personObject3.equals( personObject4)   ) ;
         System.out.println( personObject3.equals( personObject4 ) ) ;


     }

}
C:\WebSite\Learn\2\Java\intro>java Equals
true
false
true
false
---------------
true
false
true
true

Exercise 2.
Read about the Object class methods at:
https://docs.oracle.com/javase/8/docs/api/index.html?java/lang/Object.html

Examine the source code for the Object class and check the
"equals" method.