Java 11 Developer Certification - Java Class Structure

December 01, 2020

What we are covering in this lesson

  1. Skeleton of Java Class
  2. Access Modifiers
  3. Class Body Structure
  4. Instance Initializer
  5. Static Initializer
  6. Special cases - super and this keywords

Skeleton of Java Class

As we already know, an Object is an instance of Class. Lets look at the main components which define the structure of a Java class.

  • zero or one package statement - must be first line (excluding comments)
  • zero or many import statements - follows package statement and before any code
  • java.lang.* is by default imported in any Java class
  • If you are creating a class with the same name as any class in the java.lang package, you’ll need to either add a single import statement with a name conflicts or use a Fully Qualified Class Name (FQCN) on the java.lang class
  • Next we can have one or many type references, type can be class, interface or enum
  • Only one public type reference is allowed in a single source file, with the exception of nested reference types
  • The source file name must match the outermost public types reference name

A java class type consists of two sections, the declaration and the class body block. Blocks are contained in bracket sets, left and right curly braces. The class declaration requires only the reserved word class, and the Typeidentifier, which will be the name of the class. So basically the format here is {classModifier} class Typeidentifier [TypeParameters] [Superclass] [Superinterfaces]. The absolute minimal class declaration is shown below class MiminalClass{}

Section Description
Class Modifier Modifiers declare the accessibility of the class as well as additional information about the class {abstract, static, final}
Type Parameters A class is generic if it declares one or more type variables
Superclass This section is optional, the extends clause in a normal class declaration specifies the direct superclass of the current class
Superinterfaces The section is also optional, the implements clause in a class declaration lists the names of interfaces that are direct superinterfaces of the class being declared
  • It will result in a compile-time error if the same keyword appears more than once as a modifier for a class declaration or if a class declaration has more than one of the access modifiers public, protected and private.
  • The class modifier section can declare zero or 1 access modifier.
  • If no modifier is specified, it is by default package access, also known as package-private.

Access Modifiers

We can have only below access modifiers for any class

  • public
  • private
  • protected

Along with one or zero access modifiers, the class can declare zero or more of the following modifiers

  • abstract - the class must have one or more methods that are declared abstract. It expects a sub-class to implement fully the abstract methods. abstract class implies that we cannot create an object directly from that class.
  • static - the static modifier pertains only to member classes and not to top level, or local or anonymous classes.
  • final - declares that a class’ definition is complete and no subclasses are desired or required. A final class cannot have its name in an extends declaration of another class.
  • strictfp - The affect of a strictfp modifier is to restrict floating-point calculations, to ensure portability across platforms.

Below we have examples of valid declarations of a class

public class Test   //Java file will be Test.java
public abstract class Test  //cannot create an object directly from it
static class Test   //the class is static only if it is a member of another class
public final class Test //final class cannot be in extends clause - it cannot have a subclass

public class Test extends SuperTest   //Test class is subclass of SuperTest class
public abstract class Test extends SuperTest    //Abstract class can be subtype of a class
public class Test implements TestInterface  //Test class implementing an interface
public class Test extends SuperTest implements TestInterface    //extending a class and implementing an interface
public class Test extends SuperTest implements TestInterface, AnotherInterface    //extending a class and implementing an multiple interface
public interface OneInterface extends AnotherInterface  //One interface can extend another interface

A class can extend only a single class since multiple inheritance is not supported in Java. But we can implement multiple interface along with extending a single class. An interface can extend another interface.

One more important thing to note is that abstract and final and mutually exclusive modifiers. abstract requires the class to extend and complete it whereas final states that no class can extend it.

We cannot use same access modifier twice in a declaration, or use two access modifiers in the declaration. So something like public public class Test or public private class Test are invalid declarations.

Some more examples of invalid declarations are listed below

public public class Test
public private class Test
public class Test extends SuperTest, AnotherSuperTest   //Cannot extend 2 classes

public class SuperTest {}
public class Test implements SuperTest  //SuperTest is a class so need to extend it

public interface InterfaceOne {}
public class Test extends InterfaceOne  //class needs to implement interface

Class Body Structure

The class body structure defined in Javadoc can be doung [here|https://docs.oracle.com/javase/specs/jls/se11/html/jls-8.html#jls-ClassBody]. Lets have a look at some of the details below.

The class body follows the below structure

ClassBody:
    { {ClassBodyDeclaration} }

ClassBodyDeclaration:
    ClassMemberDeclaration
    InstanceInitializer
    StaticInitializer
    ConstructorDeclaration
  • The class body may contain declarations of members of the class, that is, fields, methods, classes, and interfaces.
  • The class body may also contain instance initializers, static initializers, and declarations of constructors for the class.
  • Constructors, static initializers, and instance initializers are NOT members, and therefore are not inherited.
  • Declarations can be in any order but with some limitations:

    • The order of declarations is important in the execution of initializers, which are executed in the order that they’re defined.
    • An initializer block cannot use an unqualified variable defined and initialised after it’s declaration in a statement, other than making an assignment to the variable.
    • A static initializer block cannot use an unqualified static variable declared and initialised, after it’s declaration in a statement, other than making an assignment to the variable.
    • An instance variable cannot use an unqualified reference to another instance variable, declared after it’s declaration, in it’s assignment statement.
  • Fields, methods, and member types of a class type, may have the same name, since they are used in different contexts and are disambiguated by different lookup procedures. However, this is discouraged as a matter of style.

Instance Initializer

Instance Initializer is a block of code declared in a class, whose statements are executed when an instance of a class is created.

  • A return statement cannot appear anywhere within an instance initializer.
  • You can have multiple initializer blocks, they’ll be executed in the order they are declared.
  • Initializer blocks are executed prior to any constructor block of code being executed, but after any call to the parent type’s constructor.
  • These blocks are not inherited by subclasses.

Static Initializer

The static initializer is a block of code, declared in a class prefaced by the keyword ‘static’, whose statements are executed when the class is initialised.

  • A return statement also cannot appear anywhere within a static initializer.
  • The keywords to this and super are not permitted in this code section.
  • this and super refer back to instances, and class or static initializers, do not refer to a single instance.
  • Any type variable declared outside the static initializer, cannot appear anywhere with a static initializer.
  • You can have multiple static initializer blocks, they’ll be executed in the order they are declared.
  • These static blocks are not inherited by subclasses.

If we have initializer blocks and constructors, the initializer blocks will get executed first and then the constructors will come into picture.

We will now have a look at a sample code which will explain the theory we have read above.

package maxCode.online.Initializers;

public class InitializerOrder {
	public static void main(String[] args) {
		System.out.println(new InitExample());
	}
}

class InitExample {
	static int statementOrder;
	int a = clarify("assigning a", statementOrder);

	// initializer 1
	{
		// we reference variable declared above in initializer statement
		clarify("initializer 1", this.a);
	}

	// we reference variable declared above in declaration and assignment
	int b = clarify("assigning b", this.a);

	// initializer 2
	{
		// we reference variable declared above in initializer statement
		clarify("initializer 2", this.b);
	}

	// Constructor
	InitExample() {
		// we reference variable declared below in constructor statement
		d = clarify("constructor", this.d);
	}

	// we reference variable declared above in declaration and assignment
	int c = clarify("assigning c", this.b);

	// initializer 3
	{
		// we reference variable declared above in initializer statement
		clarify("initializer 3", this.c);
	}

	int clarify(String message, int passedVariable) {
		statementOrder++;
		System.out.println(statementOrder + " " + message);
		return statementOrder;
	}

	int d;

	public String toString() {
		return this.a + ", " + this.b + ", " + this.c + ", " + this.d;
	}
}

Output:

1 assigning a
2 initializer 1
3 assigning b
4 initializer 2
5 assigning c
6 initializer 3
7 constructor
1, 3, 5, 7

statementOrder is a static variable and a method which will update the static variable each time this method is called. The message displayed along with it helps us to identify the sequence in which the statements are getting executed. We also have a constructor and the output clearly shows that the constructor is getting called at the end. So, all variables and initializer blocks are executed prior to code in the constructor regardless of where in the class body the constructor is declared, because we’ve got an initializer block of code below and above the constructor Also, the initializer block code gets executed in the order in which it is set in the class. To sum up, we can use instance variables in statements in the initializer blocks, the assignment declarations of instance variables themselves, as well as the constructor.

Remember that variable and initializer statements are always executed in the same order that they’re declared but before any statements in a constructor with the exception of the super() statement, implied or explicit.

Now lets look at the code for static initializer

package maxCode.online.Initializers;

class StaticInitExample {
	static int statementOrder;
	static int firstVariable = clarifyOrder("assigning firstVariable");

	// Static initializer 1
	static {
		clarifyOrder("executing initializer 1");
	}

	static int secondVariable = clarifyOrder("assigning secondVariable");

	// Static initializer 2
	static {
		clarifyOrder("executing initializer 2");
	}

	static int clarifyOrder(String message) {
		statementOrder++;
		System.out.println(statementOrder + " " + message);
		return statementOrder;
	}
}

public class StaticInitializer {
	public static void main(String[] args) {
		System.out.println("statements made so far = " + StaticInitExample.statementOrder);
	}
}

Output

1 assigning firstVariable
2 executing initializer 1
3 assigning secondVariable
4 executing initializer 2
statements made so far = 4

The output is similar to initializer block examples, such that they’re executed in the order that they’re declared. These statements though only get executed when the class itself is initialised.

Special Cases - super and this keywords

In the previous example, we followed all the rules and used this keyword everywhere to reference the variable. There is a separate concept called Forward Variable Declaration and we will be looking at it in this section.

package maxCode.online.Initializers;

class Thing {

	// Constructor
	Thing() {
		secondString = "b";
	}

	// Initializer
	{
		firstString = "a";
	}

	// Static Initializer
	static {
		// System.out.println(thirdString);		//This will cause Illegal Forward Reference
		thirdString = "c";
	}

	// Two instance variables
	String firstString;
	String secondString;

	// static variable
	static String thirdString;

	public String toString() {
		return firstString + secondString + thirdString;
	}
}

public class ForwardReference {
	public static void main(String[] args) {
		Thing one = new Thing();
		System.out.println(one);
	}
}

Output

abc

As we already know, if we use both instance initializer and a static initializer block, the static initializer block gets executed first, and then the instance initializer. A static variable initialized in a static block will always be available to all the instance initializer blocks. If we have a static block, and we try to use it in instance initializer or constructor, we will get a compiler error Illegal Forward Reference (Cannot reference a field before it is defined). This means we cannot reference an instance variable, which is declared further on in the code in an initializer block.

In such cases, we can use the this keyword and use the variables, just like we did in the previous code.

When we execute initializer blocks and constructor methods, we are in a state of not quite fully initialised. Initialization occurs in these steps.

  • the JVM checks whether the class has been initialised. If the class hasn’t been initialised, JVM loads and initialises the class.
  • The Java Virtual Machine allocates memory to house data for the new instance.
  • The JVM initialises instance variables to the default values.
  • The JVM executes custom initialization code found in assignment declarations of instance variables, initializer blocks in constructor(s).

We also have the super() keyword which calls the constructor of the parent class. And this is to be noted that super() is always at the first line and we cannot use super() and this() call together in the same constructor!

package maxCode.online.Initializers;

public class SuperClassInitializer {
	public static void main(String[] args) {
		SubClass subClass = new SubClass("Online");
		System.out.println(subClass);
	}
}

class SuperClass {
	String name;

	// Constructor for Super Class
	SuperClass() {
		System.out.println("Parent constructor executes");
	}

	public void setName(String name) {
		this.name = name;
	}

	public String toString() {
		return "My name is " + this.name;
	}
}

class SubClass extends SuperClass {

	// Constructor for Sub Class
	SubClass() {
		System.out.println("Child no args constructor executes");
		setName(name);
	}

	// Constructor for Sub Class
	SubClass(String name) {
		// super();
		this();
		System.out.println("Child single argument constructor executes");
		setName(name);
	}

	// Initializer code
	{
		this.name = "MaxCode";
		System.out.println("Child initializer executes");
		System.out.println(this);
	}
}

Output

Parent constructor executes
Child initializer executes
My name is MaxCode
Child no args constructor executes
Child single argument constructor executes
My name is Online

As we can see in the above code, the super class constructor is executed first, then the child initializer, this() in the parameterized SubClass constructor calls the non-parameterized subclass constructor and then itself gets executed, and finally the System.out.println gets printed.

If we uncomment the super() in subvlass parameterized constructor and comment out this(), the output will change as below, since the call to the non parameterized subclass constructor will no longer be made.

Parent constructor executes
Child initializer executes
My name is MaxCode
Child single argument constructor executes
My name is Online

The call to super was implied and doesn’t need to be called explicitly. It’s still a code before the initializer block, though.

That’s all for this section, we will have a look at the types of fields that can be used in a class and explore more on this keyword in the next section.