Classes in Java: Anatomy of a Class

What Java code is used to define a class?

Overview

Every programming language has its own rules for grammar, syntax, and punctuation. (There’s a lot more overlap between these three domains in programming languages than there is in natural languages; thus, we often use “syntax” to refer to the combination of all three in a programming language.) Java has a well-deserved reputation for being fairly strict in its rules; certainly, the code we write to define a class needs to follow some rules very precisely.

Structure

Note: In the code fragments illustrating class structure and member syntax on this page, the square brackets are used to indicate optional elements in the syntax of class and class member declarations.

[ modifiers ] class ClassName [ extends SuperClassName ]
    [ implements ImplementedInterfaceName [ , ... ] ]
    [ permits PermittedSubClass [ , ... ] ] {

  [ fields ]

  [ staticInitializer ]

  [ instanceInitializer ]
  
  [ constructors ]
  
  [ methods ]
  
  [ nested classes, interfaces, enums ]

}

Placeholders

Member order

With some restrictions, constructors and class members (fields, methods, and nested classes and interfaces) may be written in any order within the class. The order shown above is widely used, and is dictated by the DDC Java+Android Style Guide. However, there’s a lot of flexibility within that structure. Beyond what is shown here, our style guide has a few more specific rules—but the most important rule is that the order used must be logical, and chosen deliberately (e.g., not just adding each new member to the bottom of the class definition body).

Initialization sequence

Class initialization

A class is loaded (into the memory of the running JVM) and initialized the first time any of the following occurs:

Initialization of the class includes execution of the following:

  1. Loading and initialization of the superclass, if that class has not yet been initialized.

  2. In order of declaration in the class:

    • static field value assignments that are part of field declaration-with-assignment statements.

    • static initializer blocks (zero or more staticInitializer sections, as shown in the structure above).

The statements executed in initialization can include invocation of static methods of the class; however, care should be taken with any exceptions that might be thrown by such methods (or by the code in a static initializer or field assignment that invokes such methods): A static initializer or static field assignment statement may only throw (or re-throw, or leave uncaught) an unchecked exception (that is, an instance of a subclass of java.lang.Error or java.lang.RuntimeException). Any code in (or executed indirectly by) a static initializer that may throw a checked exception (instances of subclasses of java.lang.Throwable, but not subclasses of java.lang.Error or java.lang.RuntimeException) must be enclosed in a try-catch block, and the exception must be caught and handled (not re-thrown) at that point. (Of course, a checked exception may be caught and used as the cause of a new unchecked exception, which can then be thrown by the static initialization code.)

Instance initialization

When a class is instantiated—that is, when an instance of the class is created, using new ClassName(…)—execution follows these steps:

  1. If the class is not already loaded and fully initialized, it is loaded and initialized via the sequence above.

  2. new is executed, creating the instance (i.e., allocating memory and establishing the object’s identity).

  3. The constructor with formal parameters that most closely match the arguments in the constructor call (the class name and parentheses following the new keyword) is selected for execution.

  4. If the selected constructor begins with a chained call to another constructor in the same class or the superclass, that constructor is invoked. If there is no explicit chained constructor call, a no-argument call to the superclass’s constructor is made. (Actually, the insertion of this implicit chained constructor call is performed at compile time; if the superclass doesn’t have a public or protected no-parameter constructor, a compilation error will result.)

  5. After a chained superclass constructor call returns, and before executing subsequent code (if any) in the constructor, the follow instance initialization is performed, in order of declaration in the class:

    • Assignments that are part of non-static field declaration-with-assignment statements.

    • Instance initializer blocks (indicated by instanceInitializer placeholder in the structure above).

  6. Finally, the body of the constructor (following any implicit or explicit invocation of another constructor) is executed.

Static initializer

Structure

static {
  [ statements ]
}

Placeholders

Instance initializer

Structure

{
  [ statements ]
}

Placeholders

Constructor

Structure

[ modifiers ] ClassName([ paramType paramName [, ... [ paramType...  arrayParamName ] ] ])
    [ throws ExceptionClassName [ , ... ] ] { 

  [ constructorChainInvocation ]
  
  [ statements ]

}

Placeholders

Field

Structure

[ modifiers ] fieldType fieldName [ = initialValue ]; 
}

Placeholders

Method

Structure

[ modifiers ] returnType methodName([ paramType paramName [ , ... [ paramType...  arrayParamName ]  ]  ]) 
    [ throws ExceptionClassName [ , ... ] ] { 

  [ statements ]

}

Placeholders

  1. A reference to a static final field that is assigned a value on declaration (i.e., declaration-with-assignment), where the value is a compile-time constant expression (involving literal values, references to other static final fields with compile-time constant values, and simple operations, without any method calls), may be resolved at compile time. In that case, such a reference will not trigger class initialization at runtime.