Classes, fields, methods, constructors, and objects are the building blocks of object-based Java applications. This tutorial teaches you how to declare classes, describe attributes via fields, describe behaviors via methods, initialize objects via constructors, and instantiate objects from classes and access their members. Along the way, you'll also learn about setters and getters, method overloading, setting access levels for fields, constructors, and methods, and more. Note that code examples in this tutorial compile and run under Java 12.
Class declaration
A class is a template for manufacturing objects. You declare a class by specifying the class
keyword followed by a non-reserved identifier that names it. A pair of matching open and close brace characters ({
and }
) follow and delimit the class's body. This syntax appears below:
class identifier { // class body }
By convention, the first letter of a class's name is uppercased and subsequent characters are lowercased (for example, Employee
). If a name consists of multiple words, the first letter of each word is uppercased (such as SavingsAccount
). This naming convention is called CamelCasing.
The following example declares a class named Book
:
class Book { // class body }
A class's body is populated with fields, methods, and constructors. Combining these language features into classes is known as encapsulation. This capability lets us program at a higher level of abstraction (classes and objects) rather than focusing separately on data structures and functionality.
Utility classes
A class can be designed to have nothing to do with object manufacturing. Instead, it exists as a placeholder for class fields and/or class methods. Such a class is known as a utility class. An example of a utility class is the Java standard class library's Math
class. See Java tip: Fields and methods in Java for another example.
Multi-class applications and main()
A Java application is implemented by one or more classes. Small applications can be accommodated by a single class, but larger applications often require multiple classes. In that case one of the classes is designated as the main class and contains the main()
entry-point method. For example, Listing 1 presents an application built using three classes: A
, B
, and C
; C
is the main class.
Listing 1. A Java application with multiple classes
class A { } class B { } class C { public static void main(String[] args) { System.out.println("Application C entry point"); } }
You could declare these three classes in a single source file, such as D.java
. You would then compile this source file as follows:
javac D.java
The compiler generates three class files: A.class
, B.class
, and C.class
. Run this application via the following command:
java C
You should observe the following output:
Application C entry point
Alternatively, you could declare each class in its own source file. By convention, the source file's name matches the class name. You would declare A
in A.java
, for instance. You could then compile these source files separately:
javac A.java javac B.java javac C.java
To save time, you could compile all three source files at once by replacing the file name with an asterisk (but keep the .java
file extension):
javac *.java
Either way, you would run the application via the following command:
java C
When designing multi-class applications, you will designate one of these classes as the main class and locate the main()
method in it. However, there is nothing to prevent you from declaring main()
methods in the other classes, perhaps for testing purposes. This technique is shown in Listing 2.
Listing 2. Declaring more than one main()
method
class A { public static void main(String[] args) { System.out.println("Testing class A"); } } class B { public static void main(String[] args) { System.out.println("Testing class B"); } } class C { public static void main(String[] args) { System.out.println("Application C entry point"); } }
After compiling the source code, you would execute the following commands to test the helper classes A
and B
, and to run the application class C
:
java A java B java C
You would then observe the following lines of output, one line per java
command:
Testing class A Testing class B Application C entry point
Fields: Describing attributes
A class models a real-world entity in terms of state (attributes). For example, a vehicle has a color and a checking account has a balance. A class can also include non-entity state. Regardless, state is stored in variables that are known as fields. A field declaration has the following syntax:
[static] type identifier [ = expression ] ;
A field declaration optionally begins with keyword static
(for a non-entity attribute) and continues with a type
that's followed by a non-reserved identifier
that names the field. The field can be explicitly initialized by specifying =
followed by an expression
with a compatible type. A semicolon terminates the declaration.
The following example declares a pair of fields in Book
:
class Book { String title; int pubYear; // publication year }
The title
and pubYear
field declarations are identical to the variable declarations I presented in "Java 101: Elementary Java language features." These fields are known as instance fields because each object contains its own copy of them.
The title
and pubYear
fields store values for a specific book. However, you might want to store state that is independent of any particular book. For example, you might want to record the total number of Book
objects created. Here's how you would do it:
class Book { // ... static int count; }
This example declares a count
integer field that stores the number of Book
objects created. The declaration begins with the static
keyword to indicate that there is only one copy of this field in memory. Each Book
object can access this copy, and no object has its own copy. For this reason, count
is known as a class field.
Initialization
The previous fields were not assigned values. When you don't explicitly initialize a field, it's implicitly initialized with all of its bits set to zero. You interpret this default value as false
(for boolean
), '\u0000'
(for char
), 0
(for int
), 0L
(for long
), 0.0F
(for float
), 0.0
(for double
), or null
(for a reference type).
However, it is also possible to explicitly initialize a field when the field is declared. For example, you could specify static int count = 0;
(which isn't necessary because count
defaults to 0), String logfile = "log.txt";
, static int ID = 1;
, or even double sinPIDiv2 = Math.sin(Math.PI / 2);
.
Although you can initialize an instance field through direct assignment, it's more common to perform this initialization in a constructor, which I'll demonstrate later. In contrast, a class field (especially a class constant) is typically initialized through direct assignment of an expression to the field.
Lifetime and scope
An instance field is born when its object is created and dies when the object is garbage collected. A class field is born when the class is loaded and dies when the class is unloaded or when the application ends. This property is known as lifetime.
Instance and class fields are accessible from their declarations to the end of their declaring classes. Furthermore, they are accessible to external code in an object context only (for instance fields) or object and class contexts (for class fields) when given suitable access levels. This property is known as scope.
Methods: Describing behaviors
In addition to modeling the state of a real-world entity, a class also models its behaviors. For example, a vehicle supports movement and a checking account supports deposits and withdrawals. A class can also include non-entity behaviors. Regardless, Java programmers use methods to describe behaviors. A method declaration has the following syntax:
[static] returnType identifier ( [parameterList] ) { // method body }
A method declaration optionally begins with keyword static
(for a non-entity behavior) and continues with a returnType
that's followed by a non-reserved identifier
that names the method. The name is then followed by a round bracket-delimited optional parameterList
. A brace-delimited body containing code to execute when the method is called follows.
The return type identifies the type of values that are returned from the method via the return
statement, which I'll discuss later. For example, if a method returns strings, its return type would be set to String
. When a method doesn't return a value, its return type is set to void
.
The parameter list is a comma-separated list of parameter declarations: each declaration consists of a type followed by a non-reserved identifier that names the parameter. A parameter is a variable that receives an argument (an expression value whose type is compatible with its corresponding parameter) when a method or constructor is called.
A parameter is local to its method or constructor. It comes into existence when the method or constructor is called and disappears when the method or constructor returns to its caller. In other words, its lifetime is the method execution. A parameter can be accessed by any code within the method. Its scope is the entire method.
The following example declares four methods in the Book
class:
class Book { // ... String getTitle() { return title; } int getPubYear() { return pubYear; } void setTitle(String _title) { title = _title; } void setPubYear(int _pubYear) { pubYear = _pubYear; } }
The getTitle()
and getPubYear()
methods return the values of their respective fields. They use the return
statement to return these values to the caller. Note that the type of this statement's expression must be compatible with the method's return type.
The setTitle()
and setPubYear()
methods let you set the values of the title
and pubYear
fields. Their return types are set to keyword void
to indicate that they don't return any values to their callers. All four methods are known as instance methods because they affect only the objects on which they are called.
The getTitle()
, getPubYear()
, setTitle()
, and setPubYear()
methods affect a single object's copies of the title
and pubYear
fields. However, you might want to declare a method that's independent of any particular book. For example, you might want to introduce a method that outputs the number of Book
objects, as follows:
class Book { // ... static void showCount() { System.out.println("count = " + count); } }
This example declares a showCount()
method that will output the value of the count
field. The declaration begins with the static
keyword to indicate that this method belongs to the class and cannot access individual object state; no objects need to be created. For this reason, showCount()
is known as a class method.
Local variables
Within a method or constructor, you can declare additional variables as part of its implementation. These variables are known as local variables because they are local to the method/constructor. They exist only while the method or constructor is executing and cannot be accessed from outside the method/constructor. Consider the following example:
static void average(double[] values) { double sum = 0.0; for (int i = 0; i < values.length; i++) sum += values[i]; System.out.println("Average: " + (sum / values.length)); }