Java Classes: Nested classes, garbage collection and finalization

Like we have nested loops, it is possible to create nested classes in Java, i.e. a class within another class. The scope of such a class depends upon the scope of the enclosing class.

Also, nested classes can even access the private members of the enclosing class although this is not possible in the reverse case. It is also possible to define a nested class within any block scope inside the outer class(enclosing class), i.e. a nested class can be defined within a method or a loop as well.

General Form of Nested Class

The general form of a nested class is shown below:

class OuterClass {
   datatype variable1;

   class InnerClass {
       datatype variable2;
       //body of inner class
    }

    //body of OuterClass
}

The Nested class can have its own set of member methods, instance variables, and constructors.


Static and Non-static Nested Classes

Nested classes can be further divided into two categories: static nested classes and inner classes.

1 – Static Nested classes:

The nested classes that are defined by the keyword static are called static nested classes. Since these classes are static in nature they can not access the instance variables and methods of the outer class. An example is shown below:

/*
 *This program shows the creation of static nested class. 
 */
class StaticDemo {

    public static void main(String args[]) {
        //object to access the static nested class
        NestedClass ob = new NestedClass();
        //calls the method inside the static nested class
        ob.meth();
    }

    //creation of a static nested class
    static class NestedClass {
        void meth() {
            System.out.println("Inside static Inner class");

        }
    }
}

Output:-
Inside static Inner class

2 – Non-static class(Inner class):

The nested classes that are not defined as static fall under this category. These classes are very useful as they can refer to any member of its enclosing classes without the help of any object. It can be categorized further in categories –

  • Inner class
  • Local Inner class
  • Anonymous Inner class

2.1 – Inner class Example

package com.codingeek.java.InnerClass;

/**
 * This program shows a simple inner class that is declared inside a class
 * and has access to all of the member variables of the outer class.
 */
class OuterClass {
    int age = 45;
    String name = "Ram";

    //Calling the inner class inside the outer class. The inner class is not accessible elsewhere.
    void callInner() {
        InnerClass obj = new InnerClass();
        obj.display();
    }

    /**
     * This is a simple inner class which prints the name and age.
     */
    class InnerClass {
        void display() {
            System.out.println("Inside inner class");
            System.out.println(name + " is " + age + " years old.");
        }
    }
}

public class InnerClassDemo {

    public static void main(String args[]) {
        OuterClass ob;
        ob = new OuterClass();

        //calling the callInner() function which calls the inner class inside the body of the outer class
        ob.callInner();
    }
}

Output:-
Inside inner class
Ram is 45 years old.

2.2 – Local Inner Class

Local inner class refers to the inner classes that are defined inside a block. These blocks can be method body, for loop, if clause, etc. These classes are not members of any class(as they are defined inside a method or loop body) hence they cannot have any access modifiers. But they can be declared as abstract or final. Like other inner classes, these classes also have access to the members of the outer or enclosing class where they belong. These are some useful points to remember when working with a local Inner class:

  • The inner class is not visible outside the block where it is defined.
  • In the previous versions, the local inner classes could only access the final local variables of the outer class. But Java 8 allows the local inner class to access the non-final local variables as well.
  • The local inner class can extend an abstract class and can also implement an interface.
  • Local inner classes can only be instantiated from inside the block where it is created.

Here is a simple example of a Local Inner class:

package com.codingeek.java.InnerClass;

/**
 * This program shows the use of local inner class.
 * The inner class is declared inside the body of a method.
 */
public class EnclosingClass {
    int a, b;
    int product;

    //constructor to initialize the instance variables.
    EnclosingClass(int x, int y) {
        a = x;
        b = y;
    }

    public static void main(String args[]) {
        EnclosingClass ob = new EnclosingClass(10, 6);

        //calling the method prod() which contains the LocalInner class in its body.
        ob.prod();
    }

    //The method prod() encloses the inner class within its block.
    void prod() {

        //The variable c needs be declared as final for version 7 or older
        int c = 4;

		 /*
		  * Defining a local inner class which displays the product.
		  */
        class LocalInner {

            void calcProd() {
                System.out.println("Inside inner class");

                product = a * b * c;
                System.out.println("The product is : " + product);
            }
        }

        //Instantiating the local inner class. It should be done within the block where it is declared.
        LocalInner obj = new LocalInner();
        obj.calcProd();
    }
}
 

Output:-
Inside inner class
The product is : 240

2.3 – Anonymous Class

Anonymous classes are so called because they do not have a name. It is used when the objects need to be defined with certain extra functionalities like overriding without the creation of another sub-class. Only a single object is created for an anonymous class. It is usually applied in event handling processes.

Here is a simple example of an anonymous class:

package com.codingeek.java.InnerClass;

/*
 * This program shows a simple anonymous class
 */
public class AnonymousClassDemo {
    public static void main(String args[]) {

        //the anonymous class is present after the object declaration
        AnonymousClassDemo ob = new AnonymousClassDemo() {

            void meth1() {
                meth2();
            }

            void meth2() {
                System.out.println("Inside Anonymous class");
            }
        };

        //calling meth1() which in turn calls meth2() in its body.
        ob.meth1();
    }

    void meth1() {
    }
}

Output:-
Inside Anonymous class

In the above program, the method meth1() has been created as a reference to the method that is defined inside the anonymous class.


Inner class as Private

We know that a class cannot be declared as private. We can get over this by using an inner class because an inner class can be declared as private. This is helpful in the situation where a class needs to have restricted access. An example is shown below:

/*
 *This program defines a private inner class 
 */
class Outer {

    public void meth2() {
        //creating an object to access the inner class
        Inner ob = new Inner();
        ob.meth();
    }

    //declaring a private inner class
    private class Inner {
        public void meth() {
            System.out.println("Inside inner class");
        }
    }
}

public class InnerDemo {

    public static void main(String args[]) {
        //creating object to access the outer class
        Outer obj = new Outer();
        //calling meth2() which in turn calls the inner class
        obj.meth2();
    }
}

Output:-
Inside inner class

Garbage Collection

We already saw how objects are allocated memory using the new operator. Now, that memory after use must go back to the heap so that it can be used by some other program for memory allocation.
In languages like c++, this is done manually by using the delete keyword.
In Java, this is done automatically. An object after use(no longer needed) is destroyed and that memory is automatically restored to the heap memory for later use. This process is known as garbage collection is Java. Garbage collection occurs based on certain conditions like memory limit reached, time elapsed etc during program execution. Also, different run-time implementations have a different approach to garbage collection.


The finalize() Method

In some cases, the user might want the object to perform certain operations before it is destroyed. In this case, Java provides another function by which the user can define the operations that need to be performed just before it is reclaimed by the garbage collector. This is known as finalization. In this process, the user needs to simply define a finalize() method and inside it, the user mentions the actions that the object must perform before it is reclaimed. The garbage collector runs periodically and once it feels that the object must be collected, the compiler runs the finalize() method. The general form of the finalize() method is given below:

protected void finalize() 
{
    //finalization code
}

The finalize() method is declared as protected so as to limit its use to the subclasses(within or outside the package) and the classes inside the same package. It is important to note that finalize() is only called before garbage collection and not before the object goes out-of-scope.

An investment in knowledge always pays the best interest. Hope you like the tutorial. Do come back for more because learning paves way for a better understanding.

Do not forget to share and Subscribe.

Happy coding!! 🙂

Recommended -