Java 101: Foundations

Java 101: Classes and objects in Java

Learn how to make classes, fields, methods, constructors, and objects work together in your Java applications

Classes, fields, methods, constructors, and objects are the building blocks of object-based Java applications. This article will teach 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. If you want to go a little further with fields and methods, you may also download the free Java 101 primer showcasing constants, recursion, and other techniques in object-based programming.

Note that examples in Java 101: Foundations mini-series are based on Java 8.

Note that this article assumes general familiarity with the Java programming model, including class loading, compilation, and garbage collection. It also assumes that you are familiar with elementary Java language features, including operators and statements. This in-depth introduction to coding with classes and objects will not address those topics. See earlier articles in the Java 101: Foundations mini-series for basic information about the Java platform.

download
Source code for "Java 101: Classes and objects in Java." Created by Jeff Friesen for JavaWorld.

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. Let's take a look at the standard syntax for declaring a Java class:


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.

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 classfiles: 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.

1 2 3 Page 1
View Comments
Join the discussion
Be the first to comment on this article. Our Commenting Policies