Saturday, April 16, 2011

Dissection of Java Object


Understanding java.lang.Object


Everything in java is an object. All classes extend the java.lang.Object directly or indirectly. Object class is center of attraction in java technology. Object is a basic building block of java programming, means every object is an instance of object class.
Why everything in java is an instance of Object class and why every class extends Object class directly or indirectly?
Something needs to be common which is share by all the objects and which hold the valid reference for any class instance supported by the underlying programming language.
Code snippets 1:
Declare simple MyObjects class in Java.
public class MyObjects
{
// specific code
}
This code snippet is equivalent to the code snippet 2
Code snippet 2
public class MyObjects extends Object
{
// specific code
}

Before details investigations of Object class, understand the execution flow of various blocks and constructor to initiate the instance of requested class.
Java supports the block level scope and that leverage the extreme power to the java language.

Code snippet 3:
Understand the execution of static block, normal block and constructor while initializing instance of a class.
/**
 * SuperClass.java
 * Created on Apr 15, 2011
 */

package com.dissections.object;

/**
 * @author Ashish.Chudasama
 */
public class SuperClass
{

      // This static block
      static
      {
      // This block executes once only when class loader loads the class
      //into the memory
            System.out.println("Static Block Of SuperClass");
      }
     
      // This is normal block
      //This block executed every time when a new instance is created
      {
            //This block executes first then constructor
            System.out.println("Normal Block Of SuperClass");
      }
     
      public SuperClass()
      {
            //This super refers to the object class
            // Inherently which is super class of every object in java
            super();
            System.out.println("Constructor of SuperClass");
      }

     
}


Test the Code snippet 3

package com.dissections.object;

public class TestStub
{
    public static void main(String[] args)
    {
        new SuperClass();
    }

}


Output:
Static Block Of SuperClass
Normal Block Of SuperClass
Constructor of SuperClass

                                              
Key points:

1)      Static block executes only once when object is initialize by the class loader into the memory.
2)      If more than one static block present in a same class then static block executed the order in which they are declare in a java class file.
3)      If class specifies more than one static block then  for the optimization purpose compiler converts  in to single static block.
4)      Normal block is copied  inside the constructor by the compiler during the compilation time.

Why we need the normal block?

Java supports the constructor overloading by which we can specify the various constructor with different argument list. This feature is called compile time polymorphism. Sometime all constructor required set of common initialization, such code can be place in normal block. In C++ we have inline function by which function call is replace by the compiler with the actual code. This is compiler smartness and gives the programmer to structure the code.

Code snippet 4
This code shows how the various blocks compile to single unit of work. This code is before compilation.
/**
 * SuperClass.java
 * Created on Apr 15, 2011
 */

package com.dissections.object;

/**
 * @author Ashish.Chudasama
 */
public class SuperClass
{

      // This static block
      static
      {
      // This block executes once only when class loader loads the class
      //into the memory
            System.out.println("Static Block Of SuperClass");
      }
      static
    {
    // This block executes once only when class loader loads the class
    //into the memory
        System.out.println("Static Block Of 2 SuperClass");
    }

      // This is normal block
      //This block executed every time when a new instance is created
      {
            //This block executes first then constructor
            System.out.println("Normal Block 1 Of SuperClass");
      }
     
     
      // This is normal block
    //This block executed every time when a new instance is created
    {
        //This block executes first then constructor
        System.out.println("Normal Block 2 Of SuperClass");
    }
      public SuperClass()
      {
            //This super refers to the object class
            // Inherently which is super class of every object in java
            super();
            System.out.println("Constructor of SuperClass");
      }

      public SuperClass(String test)
    {
        //This super refers to the object class
        // Inherently which is super class of every object in java
        super();
        System.out.println("Constructor of SuperClass" + test);
    }
     
}

Same code after compilation (which is present in .class file) I have decompile the .class file to demonstrate the complier smartness.
package com.dissections.object;

import java.io.PrintStream;

public class SuperClass
{
 // Static block compile to single block
  static
  {
    System.out.println("Static Block Of SuperClass");

    System.out.println("Static Block Of 2 SuperClass");
  }

  public SuperClass()
  {
// normal block append to each constructor
    System.out.println("Normal Block Of SuperClass");

    System.out.println("Normal Block Of 2");

    System.out.println("Constructor of SuperClass");
  }

  public SuperClass(String test)
  {
    System.out.println("Normal Block Of SuperClass");

    System.out.println("Normal Block Of 2");

    System.out.println("Constructor of SuperClass");
  }
}

Now understand the constructor calling and block execution with respect to inheritance.
Code snippet 5
/**
 * DerivedClass.java
 * Created on Apr 15, 2011
 */

package com.dissections.object;

/**
 * @author Ashish.Chudasama
 */
public class DerivedClass extends SuperClass
{
      // This static block
      static
      {
      // This block executes once only when class loader loads the class
      //into the memory
            System.out.println("DerivedClass Block Of SuperClass");
      }
     
      // This is normal block
      //This block executed every time when a new instance is created
      {
            //This block executes first then constructor
            System.out.println("DerivedClass Block Of SuperClass");
      }
     
      public DerivedClass()
      {
            //This super refers to the constructor of the SuperClass class
            super();
            System.out.println("DerivedClass of SuperClass");
      }
}

Test the derive class execution
package com.dissections.object;

public class TestStub
{
    public static void main(String[] args)
    {
        System.out.println("---- create first instance of class --------");
        new DerivedClass();
        System.out.println("---- create second instance of class --------");
        new DerivedClass();
    }

}

Output:
---- create first instance of class --------
Static Block Of SuperClass
static Block Of DerivedClass
Normal Block  Of SuperClass
Constructor of SuperClass
normal Block Of DerivedClass
constructor of DerivedClass

---- create second instance of class --------
Normal Block  Of SuperClass
Constructor of SuperClass
normal Block Of DerivedClass
constructor of DerivedClass

Key points:
1)      Class loader loads  all  require class into the memory.
2)      Super class loaded first into the jvm, hence its static block executed first.
3)      Once the super class loaded successfully, then class loader loads derived class and executes the static block of the derived class.
4)      Derived class instance can’t be created before creating instance of superclass because derived class inherits the characteristic of superclass. Hence superclass constructor is executed and then derived class constructor. ( Normal block is place inside the constructor hence normal block executed very time when new instance is created . static block executes only once)

Decompile the java.lang.Object class file.  Majority of methods declares in Object class are native and static in nature… as shown in code snippet 6.
Code snippet 6

Inspect the code snippet carefully then majority of methods are final and native

package java.lang;   

public class Object
{
private static native void registerNatives();
public final native Class<?> getClass();
public native int hashCode();
public boolean equals(Object paramObject)
 {
    return (this == paramObject);
 }
protected native Object clone() throws CloneNotSupportedException;

public String toString()
{   
 return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
public final native void notify();
public final native void notifyAll();
public final native void wait(long paramLong) throws InterruptedException;
public final void wait(long paramLong, int paramInt) throws InterruptedException
  {
if (paramLong < 0L)
      {
         throw new IllegalArgumentException("timeout value is negative");
      }

      if ((paramInt < 0) || (paramInt > 999999))
        {
            throw new IllegalArgumentException("nanosecond timeout value out              of range");
        }

      if ((paramInt >= 500000) || ((paramInt != 0) && (paramLong == 0L)))
      {
        paramLong += 1L;
      }
        wait(paramLong);
    }

    public final void wait() throws InterruptedException
    {
        wait(0L);
    }

    protected void finalize() throws Throwable
    {
    }
    static
    {
        registerNatives();
    }
}

Key points
1)      Only byte code is platform independent whereas JVM/JRE are platform dependent.
2)      Java object calls platform specific library files to represent the object as per the machine type.
3)      Native methods  does not declare any implementations but that methods are implemented in platform specific libraries.
4)      Object class specifies the static block to register itself to the native runtime routines which is known to jvm. Like Class.forName we used to register the driver class during the JDBC programming.
5)      Java language support the multithreading at the Software level that is why multithreaded application behave differently on different platform. Also threading is also affected by the policy implemented by the OS to schedule to process. That is why notify , wait and notifyAll are declare as native as well as final.
6)      Method hashCode() declare as native but does not finalized because to identify object uniquely native methods return the actual hashcode assigned to the newly created objects. The object equals method compare the reference by default and returns true false value.
7)      It is necessary, if two objects are same then its hashcode must be same, but it is not necessary that if two objects are logically same means equals methods returns true then its hashcode is same.
8)      Hash code and Equals method used by the collection classes to identify the distinct objects.




4 comments:

  1. The article is really clear to me.
    I have been using java for 12 years and I never think about gathering the common part of constructors
    in a simple block. thank you.

    Just wanna raise somthing though.
    In the second constructor of the snapset 5
    should we have

    System.out.println("Constructor of SuperClass" + test);

    instead of

    System.out.println("Constructor of SuperClass");

    ?

    ReplyDelete
  2. snapset 4 after compilation, sorry.

    ReplyDelete
  3. In code snippet 2 the right code should be this:

    public class MyObjects extends Object
    {
    public MyObjects() {
    super();
    }
    }

    ReplyDelete
  4. HI Claxon ,

    Yes UR right but if there is no default constructor then compiler assigned default constructor to the class as you shown in your code snippet. in code snippet 2 i m demonstrating that both the code are same.

    ReplyDelete