What Java code is used to define a class?
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.
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 ]
}
modifiers
Modifiers to the class definition, which may include:
public
causes the class to be visible outside the package; without this, the class is only visible to other code defined in the same package (which may not necessarily reside in the same file). public
and the default (package-private) are the only access levels allowed for top-level classes. If the class is declared within another class, the protected
and private
access levels can also be used.
abstract
declares that the class can not be instantiated (i.e., we can’t create instance objects of the class). It may have zero or more methods that are not implemented, including abstract
methods defined in this class, abstract
methods inherited from a superclass, or methods declared (but not implemented) in an interface implemented by the class.
static
can be applied as a modifier when a class is defined as a nested class inside another class; it cannot be used on a top-level class.
final
declares that the class cannot be extended—that is, another class cannot be defined which extends
this class.
sealed
declares that the class can’t be extended, except for one or more classes specified in the permits
clause.
non-sealed
is used to declare that a class referenced in the permits
clause of its superclass can be extended. (For any class not listed in the permits
clause of its immediate superclass, this is redundant, and should not be used.)
ClassName
Name of class—typically a simple or compound (multi-word) noun, and always written in UpperCamelCase (aka PascalCase).
SuperClassName
Name of superclass. If none is specified, then java.lang.Object
is the superclass.
ImplementedInterfaceName
Name of interface implemented by this class. Multiple interfaces can be implemented by a single class.
PermittedSubClass
Name of subclass permitted to extend an otherwise-sealed
superclass. Multiple subclases may be specified; all classes specified in a permits
clause must be accessible at compile type of the (sealed
) superclass.
fields
Fields of the class. Typically, field names are written in camelCase, though constants (static final
fields of primitive or deeply immutable object types) are usually in UPPER_SNAKE_CASE (aka SCREAMING_SNAKE_CASE).
staticInitializer
Class initialization code, enclosed in static {…}
. This code is executed automatically when the class is first loaded into memory.
There may be multiple static initializers, though this is rarely a good idea—and even more rarely necessary.
instanceInitializer
Instance initialization code, enclosed in {…}
. This code is executed automatically when an instance of the class is first created by new
, prior to any constructor invocation.
There may be multiple instance initializers, though this is rarely a good idea; it is even more rarely necessary.
constructors
Class instance constructors. These must always have the same name as the class, including case. Within the body of a constructor, the instance being initialized may be referenced via this
.
If no constructors are defined for the class, the compiler will automatically create a constructor with no parameters, known as the default constructor. As described in Instance initialization (below), the compiler automatically inserts super();
at the start of the default constructor (and many other constructors as well).
methods
Methods of the class; these may be either static
(associated with, and invoked in the context of, the class itself) or non-static
(invoked in the context of an instance of the class; the instance may be referenced via this
within the body of such as method). Method names are written in camelCase.
nested classes, interfaces, enums
A class may contain nested classes (including enumerations) and interfaces. Nested classes may be static
or non-static
(nested enum
and interface
definitions are always implicitly static
); like a static
method, a static
nested class is in the context of the entire enclosing class, not a single instance; thus, the code in a static
nested class cannot reference non-static
methods or fields of the enclosing class.
Note that the code of the enclosing class can access the members of a nested class
, interface
, or enum
, even if that nested type is declared private
.
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).
A class is loaded (into the memory of the running JVM) and initialized the first time any of the following occurs:
A static
member of that class is referenced—i.e., a static
field is accessed, or a static
method is invoked.1 Any such reference or invocation blocks (i.e., is suspended) until the class is completely loaded and initialized, at which time it resumes.
A constructor
of that class is invoked, on instantiation of the class (i.e., creation of an object instance) with the new
keyword. Once again, invocation of the constructor blocks until the class is completely loaded and initialized, at which time invocation of the constructor (more generally, instance initialization) resumes.
A subclass of the class needs to be loaded, for either of the above reasons. When this happens, the loading and initialization of the subclass is suspended until the given class is loaded and initialized, at which time the loading of the subclass resumes.
Initialization of the class includes execution of the following:
Loading and initialization of the superclass, if that class has not yet been initialized.
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.)
When a class is instantiated—that is, when an instance of the class is created, using new ClassName(…)
—execution follows these steps:
If the class is not already loaded and fully initialized, it is loaded and initialized via the sequence above.
new
is executed, creating the instance (i.e., allocating memory and establishing the object’s identity).
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.
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.)
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).
Finally, the body of the constructor (following any implicit or explicit invocation of another constructor) is executed.
static {
[ statements ]
}
statements
Class initialization statements. These are executed automatically on the first reference to a class; thus, a static initializer is executed just once.
Values of static
fields can be assigned in these statements—including the initial assignment of values to static final
fields.
{
[ statements ]
}
statements
Instance initialization statements. These are automatically executed immediately on instance creation (by new
), before constructor invocation.
Assignment of values to fields that are non-static
(including initial assignment of final
fields that are non-static
), or static
(but not static final
) can be performed in this block.
[ modifiers ] ClassName([ paramType paramName [, ... [ paramType... arrayParamName ] ] ])
[ throws ExceptionClassName [ , ... ] ] {
[ constructorChainInvocation ]
[ statements ]
}
modifiers
Because of the special nature of constructors, and the restrictions on the way they’re defined and invoked, the only modifiers allowed on constructors are the access-level modifiers:
public
causes the constructor to be visible outside the class and the package.
protected
makes the constructor visible within this package and any subclasses.
Without any access modifier, the constructor is visible only within this class and package (i.e., the package-private access level).
private
causes the constructor to be visible only within the class.
The scope modifier static
is not allowed on a constructor, and will result in a compiler error. Remember, in the case of static
methods, such methods have no access to the current instance (this
). On the other hand, a constructor’s primary job is initializing the state of the current instance; thus, it would make no sense for a constructor to be static
. For related reasons, neither final
nor synchronized
are allowed as modifiers on a constructor.
ClassName
The name of the constructor always matches the simple name of the class, including case.
paramType
Type of a formal parameter of the constructor. If this is a primitive type (e.g., int
, char
, double
), then the value of the corresponding actual parameter (i.e., argument) passed by the caller will be available to the constructor, associated with the specified parameter name; changes to the parameter’s value in the body of the constructor have no effect on the caller. If the parameter is of an object type—that is, if the parameter type is an array type (specified with brackets) or a class name—then the value of a reference to the caller’s object is passed to the constructor; if the object type allows it, the constructor may invoke methods on the object, assign values to an array’s elements, or assign values to an object’s fields, and may thus modify the caller’s object. (Note that String
objects—among many others—are immutable: When the contents of a String
are modified, a new String
object is created. Thus, if a caller passes a String
argument on constructor invocation, and the constructor modifies the value of that String
parameter, it has no effect on the caller, which is still referencing the original String
, not the new one created implicitly by reassigning the value within the constructor.)
paramName
Name of a formal parameter of the constructor. Note that this is the name by which the parameter is known in the constructor itself; the parameter name is irrelevant to the caller.
paramType...
Type of a varargs parameter. This is a formal parameter that can be provided with zero or more arguments (of the specified type) on invocation; within the constructor, the parameter is treated as an array of the specified type.
ExceptionClassName
Name of exception class that this constructor may throw. If the exception class is a checked exception, an invoking method must either catch exceptions of this class, catch exceptions of an superclass, or include a corresponding throws
clause in the method declaration. If this constructor is invoked by another constructor of the same class or a subclass (constructor chaining), that invoking constructor must include a corresponding throws
clause in the constructor declaration.
Great care must be taken, when defining a constructor that throws an exception, to avoid leaving an instance in a half-initialized state, especially if it a non-final
class with public
or protected
members referring to other objects. Such instances are potential attack vectors: Malicious code can be written in a subclass, accessing such public
or protected
members even after an exception is thrown by the constructor. Remember: the new
keyword creates the object instance, and the constructor initializes the state; thus, even if the constructor throws an exception, the instance has been created.
constructorChainInvocation
A constructor may use this(…);
to invoke another constructor of the same class, or super(…);
to invoke a constructor of the immediate superclass—but not both. If such an invocation is included it must be the first statement in the constructor. This is called constructor chaining.
If the first line of a constructor body is neither super(…);
nor this(…);
, the Java compiler inserts an implicit super();
statement—that is, an implicit invocation of the parent class’s no-parameter constructor—in the bytecode. If the parent class defines one or more constructors, but not a no-parameter constructor, or if the class defines a private
no-parameter constructor, this behavior of the compiler will result in a compiler error.
statements
The statements making up the body of the constructor.
[ modifiers ] fieldType fieldName [ = initialValue ];
}
modifiers
Modifiers to the field definition.
Access modifiers
public
causes the field to be visible outside the class and the package. Following the principle of encapsulation, the only fields we will declare as public
will be final
; in most cases, they will also be static
.
protected
makes the method visible within this package and any subclasses. In practice, we don’t often use this access level for fields, preferring to give the subclasses access to fields via accessors and mutators.
Without any access modifier, the method is visible only within this package (package-private access level). We will generally use this access level on fields in test classes only.
private
causes the method to be visible only within the class. In a well-encapsulated class, all fields (with the possible exception of one or more static final
constants) are private
.
Scope modifiers
static
declares that the field is associated with the class itself, rather than with instances of the class. If one instance of the class modifies the value of such a field, the value is changed for all instances of the class.
Without a scope modifier, the field is associated with instances of the class. Each instance of the class has its own field with this definition, and a change made by one instance doesn’t affect the value of this field for any other instances.
Other common modifiers
final
declares that a field will be assigned only a single value, and that the assignment will take place in class initialization (for a static
field) or instance initialization (for an instance field). If the relevant initialization doesn’t assign a value to such a field, compilation will fail.
transient
declares that this field should not be included in instance serialization/deserialization (a general process of turning an object into a sequence of bytes that can be written to a file, memory buffer, or network connection, and also reconstructing an instance from such a sequence of bytes). In addition to this modifier being used in the serialization/deserialization mechanism included since Java 1.1, some higher-level serialization libraries (e.g., Gson for JSON serialization/deserialization) and database persistence libraries (e.g., the Room and JPA ORMs) respect this modifier as well, and will ignore transient
fields.
fieldType
The type of data to be stored in the field. Note that for array fields, the array portion of the declaration may follow the field type, or the field name. For example, int[] data
and int data[]
both declare the field data
as a reference to an array of int
values.
fieldName
The name of the field, which can be used in expressions and as an assignment target. In our style guide (and in common with almost all Java style guides), this is always written in lowerCamelCase
, unless the field is declared static final
and is deeply immutable, in which case is may be written in UPPER_SNAKE_CASE
.
initiialValue
Specifies the initial (and for a final
field, the only) value assigned to the field. When this form (known as declaration-with-assignment) is used to assign a value in the statement in which it is declared, the assignment is treated as a simple static
initializer (for a static
field) or instance initializer (for a non-static
field), and is executed in top-to-bottom order with other initializers with the same scope.
[ modifiers ] returnType methodName([ paramType paramName [ , ... [ paramType... arrayParamName ] ] ])
[ throws ExceptionClassName [ , ... ] ] {
[ statements ]
}
modifiers
Modifiers to the method definition.
Access modifiers
public
causes the method to be visible outside the class and the package.
protected
makes the method visible within this package and any subclasses.
Without any access modifier, the method is visible only within this package (package-private access level).
private
causes the method to be visible only within the class.
Scope modifiers
static
declares that the method is associated with the class itself, rather than with instances of the class.
Without a scope modifier, the method is associated with instances of the class; within the method code, the this
keyword refers to the current instance.
Other common modifiers
final
prevents the method from being overridden in a subclass.
synchronized
prevents this method, and any other methods of the class similary marked as synchronized
, from executing on multiple threads at the same time for the same instance of the class. In other words, it declares that the method is a critical section.
abstract
declares that the method will be declared, but not implemented, in this class. Instead of curly braces enclosing the statements that make up the method body, the declaration (up to and including a throws
clause, if any) is followed by a semicolon. If any method in a class is abstract
, then the entire class must be marked as abstract
; the reverse, however, is not true: an abstract
need not have any methods that are also abstract.
returnType
The type of the result returned by the method. A method that declares a return type, but does not have a return
statement (or has at least one execution flow path without a return
statement) will not compile successfully. A method with the void
return type does not return a result; it may have one or more return
statements, but they are not allowed to include a return value expression.
methodName
The name of the method, which can subsequently be used in invocations of the method. In our style guide (and in common with almost all Java style guides), this is always written in lowerCamelCase
paramType
Type of a formal parameter of the method. If this is a primitive (e.g., int
, char
, double
), then the value of the corresponding actual parameter (i.e., argument) passed by the caller will be available to the method, associated with the specified parameter name; changes to the parameter’s value in the body of the method have no effect on the caller. If the parameter is of an object type—that is, if the parameter type is an array type (specified with brackets) or a class name—then the value of a reference to the caller’s object is passed to the method; if the object type allows it, the method may invoke methods on the object, assign values to an array’s elements, or assign values to an object’s fields, and may thus modify the caller’s object. (Note that String
objects—among many others— are immutable: When the contents of a String
are modified, a new String
object is created. Thus, if a caller passes a String
argument on method invocation, and the method modifies the value of that String
parameter, it has no effect on the caller, which is still referencing the original String
, not the new one created implicitly by reassigning the value within the method.)
paramName
Name of a formal parameter of the method. Note that this is the name by which the parameter is known in the method itself; the parameter name is irrelevant to the caller.
paramType...
Type of a varargs parameter. This is a formal parameter that can be provided with zero or more arguments (of the specified type) on invocation; within the method, the parameter is treated as an array of the specified type.
For example, the java.io.PrintStream
class (the System.out
object is an instance of this class) has a method named printf
, defined as printf(String format, Object... args)
. That means that we pass a format string to this method, along with zero or more additional objects; the method code processes the format string, along with any additional arguments, and prints the result.
ExceptionClassName
Name of exception class that this method may throw. (If the exception class is a checked exception, then an invoking method must either catch exceptions of this class, catch exceptions of a superclass, or include a corresponding throws
clause in the method declaration.)
statements
The statements making up the body of the method.
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. ↩