Classes and Objects

Classes and Objects

With the knowledge you now have of the basics of the Java programming language, you can learn to write your own classes. In this lesson, you will find information about defining your own classes, including declaring member variables, methods, and constructors.

You will learn to use your classes to create objects, and how to use the objects you create.


This lesson also covers nesting classes within other classes, enumerations, and annotations.


Classes


This section shows you the anatomy of a class, and how to declare fields, methods, and constructors.

Objects


This section covers creating and using objects. You will learn how to instantiate an object, and, once instantiated, how to use the dot operator to access the object’s instance variables and methods.

More on Classes


This section covers more aspects of classes that depend on using object references and the dot operator that you learned about in the preceding section: returning values from methods, the this keyword, class vs. instance members, and access control.

Nested Classes


Static nested classes, inner classes, anonymous inner classes, and local classes are covered.

Enum Types


This section covers enumerations, specialized classes that allow you to define and use sets of constants.

Annotations


Annotations allow you to add information to your program that is not actually part of the program. This section describes three built-in annotations that you should know about.

Annotations

Annotations

Annotations provide data about a program that is not part of the program itself. They have no direct effect on the operation of the code they annotate.

Annotations have a number of uses, among them:



  • Information for the compiler — Annotations can be used by the compiler to detect errors or suppress warnings.


  • Compiler-time and deployment-time processing — Software tools can process annotation information to generate code, XML files, and so forth.


  • Runtime processing — Some annotations are available to be examined at runtime.
Annotations can be applied to a program’s declarations of classes, fields, methods, and other program elements.

The annotation appears first, often (by convention) on its own line, and may include elements with named or unnamed values:


@Author(
name = “Benjamin Franklin”,
date = “3/27/2003”
)
class MyClass() { }
or
@SuppressWarnings(value = “unchecked”)
void myMethod() { }
If there is just one element named “value,” then the name may be omitted, as in:
@SuppressWarnings(“unchecked”)
void myMethod() { }
Also, if an annotation has no elements, the parentheses may be omitted, as in:
@Override
void mySuperMethod() { }

Documentation

Many annotations replace what would otherwise have been comments in code.

Suppose that a software group has traditionally begun the body of every class with comments providing important information:


public class Generation3List extends Generation2List {

// Author: John Doe
// Date: 3/17/2002
// Current revision: 6
// Last modified: 4/12/2004
// By: Jane Doe
// Reviewers: Alice, Bill, Cindy

// class code goes here

}

To add this same metadata with an annotation, you must first define the annotation type. The syntax for doing this is:
@interface ClassPreamble {
String author();
String date();
int currentRevision() default 1;
String lastModified() default “N/A”;
String lastModifiedBy() default “N/A”;
String[] reviewers(); // Note use of array
}
The annotation type definition looks somewhat like an interface definition where the keyword interface is preceded by the @ character (@ = “AT” as in Annotation Type). Annotation types are, in fact, a form of interface, which will be covered in a later lesson. For the moment, you do not need to understand interfaces.

The body of the annotation definition above contains annotation type element declarations, which look a lot like methods. Note that they may define optional default values.


Once the annotation type has been defined, you can use annotations of that type, with the values filled in, like this:


@ClassPreamble (
author = “John Doe”,
date = “3/17/2002”,
currentRevision = 6,
lastModified = “4/12/2004”,
lastModifiedBy = “Jane Doe”
reviewers = {“Alice”, “Bob”, “Cindy”} // Note array notation
)
public class Generation3List extends Generation2List {

// class code goes here

}





Note: To make the information in @ClassPreamble appear in Javadoc-generated documentation, you must annotate the @ClassPreamble definition itself with the @Documented annotation:
import java.lang.annotation.*; // import this to use @Documented

@Documented
@interface ClassPreamble {

// Annotation element definitions

}





Annotations Used by the Compiler

There are three annotation types that are predefined by the language specification itself: @Deprecated, @Override, and @SuppressWarnings.

@Deprecated—the @Deprecated annotation indicates that the marked element is deprecated and should no longer be used. The compiler generates a warning whenever a program uses a method, class, or field with the @Deprecated annotation. When an element is deprecated, it should also be documented using the Javadoc @deprecated tag, as shown in the following example. The use of the “@” symbol in both Javadoc comments and in annotations is not coincidental—they are related conceptually. Also, note that the Javadoc tag starts with a lowercase “d” and the annotation starts with an uppercase “D”.


   // Javadoc comment follows
/**
* @deprecated
* explanation of why it was deprecated

*/
@Deprecated
static void deprecatedMethod() { }
}

@Override—the @Override annotation informs the compiler that the element is meant to override an element declared in a superclass (overriding methods will be discussed in the the lesson titled “Interfaces and Inheritance”).


   // mark method as a superclass method
// that has been overridden
@Override
int overriddenMethod() { }
While it’s not required to use this annotation when overriding a method, it helps to prevent errors. If a method marked with @Override fails to correctly override a method in one of its superclasses, the compiler generates an error.

@SuppressWarnings—the @SuppressWarnings annotation tells the compiler to suppress specific warnings that it would otherwise generate. In the example below, a deprecated method is used and the compiler would normally generate a warning. In this case, however, the annotation causes the warning to be suppressed.


   // use a deprecated method and tell
// compiler not to generate a warning
@SuppressWarnings(“deprecation”)
void useDeprecatedMethod() {
objectOne.deprecatedMethod(); //deprecation warning – suppressed
}

Every compiler warning belongs to a category. The Java Language Specification lists two categories: “deprecation” and “unchecked.” The “unchecked” warning can occur when interfacing with legacy code written before the advent of generics (discussed in the lesson titled “Generics”). To suppress more than one category of warnings, use the following syntax:


@SuppressWarnings({“unchecked”, “deprecation”})

Annotation Processing

The more advanced uses of annotations include writing an annotation processor that can read a Java program and take actions based on its annotations. It might, for example, generate auxiliary source code, relieving the programmer of having to create boilerplate code that always follows predictable patterns. To facilitate this task, release 5.0 of the JDK includes an annotation processing tool, called apt. In release 6 of the JDK, the functionality of apt is a standard part of the Java compiler.

To make annotation information available at runtime, the annotation type itself must be annotated with @Retention(RetentionPolicy.RUNTIME), as follows:


import java.lang.annotation.*; 

@Retention(RetentionPolicy.RUNTIME)
@interface AnnotationForRuntime {

// Elements that give information
// for runtime processing

}


Enum Types

Enum Types

An enum type is a type whose fields consist of a fixed set of constants. Common examples include compass directions (values of NORTH, SOUTH, EAST, and WEST) and the days of the week.

Because they are constants, the names of an enum type’s fields are in uppercase letters.


In the Java programming language, you define an enum type by using the enum keyword. For example, you would specify a days-of-the-week enum type as:


public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY
}
You should use enum types any time you need to represent a fixed set of constants. That includes natural enum types such as the planets in our solar system and data sets where you know all possible values at compile time—for example, the choices on a menu, command line flags, and so on.

Here is some code that shows you how to use the Day enum defined above:


public class EnumTest {
Day day;

public EnumTest(Day day) {
this.day = day;
}

public void tellItLikeItIs() {
switch (day) {
case MONDAY: System.out.println(“Mondays are bad.”);
break;

case FRIDAY: System.out.println(“Fridays are better.”);
break;

case SATURDAY:
case SUNDAY: System.out.println(“Weekends are best.”);
break;

default: System.out.println(“Midweek days are so-so.”);
break;
}
}

public static void main(String[] args) {
EnumTest firstDay = new EnumTest(Day.MONDAY);
firstDay.tellItLikeItIs();
EnumTest thirdDay = new EnumTest(Day.WEDNESDAY);
thirdDay.tellItLikeItIs();
EnumTest fifthDay = new EnumTest(Day.FRIDAY);
fifthDay.tellItLikeItIs();
EnumTest sixthDay = new EnumTest(Day.SATURDAY);
sixthDay.tellItLikeItIs();
EnumTest seventhDay = new EnumTest(Day.SUNDAY);
seventhDay.tellItLikeItIs();

}
}

The output is:
Mondays are bad.
Midweek days are so-so.
Fridays are better.
Weekends are best.
Weekends are best.

Java programming language enum types are much more powerful than their counterparts in other languages. The enum declaration defines a class (called an enum type). The enum class body can include methods and other fields. The compiler automatically adds some special methods when it creates an enum. For example, they have a static values method that returns an array containing all of the values of the enum in the order they are declared. This method is commonly used in combination with the for-each construct to iterate over the values of an enum type. For example, this code from the Planet class example below iterates over all the planets in the solar system.


for (Planet p : Planet.values()) {
System.out.printf(“Your weight on %s is %f%n”,
p, p.surfaceWeight(mass));
}




Note: All enums implicitly extend java.lang.Enum. Since Java does not support multiple inheritance, an enum cannot extend anything else.


In the following example, Planet is an enum type that represents the planets in the solar system. They are defined with constant mass and radius properties.

Each enum constant is declared with values for the mass and radius parameters. These values are passed to the constructor when the constant is created. Java requires that the constants be defined first, prior to any fields or methods. Also, when there are fields and methods, the list of enum constants must end with a semicolon.





Note: The constructor for an enum type must be package-private or private access. It automatically creates the constants that are defined at the beginning of the enum body. You cannot invoke an enum constructor yourself.


In addition to its properties and constructor, Planet has methods that allow you to retrieve the surface gravity and weight of an object on each planet. Here is a sample program that takes your weight on earth (in any unit) and calculates and prints your weight on all of the planets (in the same unit):
public enum Planet {
MERCURY (3.303e+23, 2.4397e6),
VENUS (4.869e+24, 6.0518e6),
EARTH (5.976e+24, 6.37814e6),
MARS (6.421e+23, 3.3972e6),
JUPITER (1.9e+27, 7.1492e7),
SATURN (5.688e+26, 6.0268e7),
URANUS (8.686e+25, 2.5559e7),
NEPTUNE (1.024e+26, 2.4746e7);

private final double mass; // in kilograms
private final double radius; // in meters
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}
private double mass() { return mass; }
private double radius() { return radius; }

// universal gravitational constant (m3 kg-1 s-2)
public static final double G = 6.67300E-11;

double surfaceGravity() {
return G * mass / (radius * radius);
}
double surfaceWeight(double otherMass) {
return otherMass * surfaceGravity();
}
public static void main(String[] args) {
double earthWeight = Double.parseDouble(args[0]);
double mass = earthWeight/EARTH.surfaceGravity();
for (Planet p : Planet.values())
System.out.printf(“Your weight on %s is %f%n”,
p, p.surfaceWeight(mass));
}
}

If you run Planet.class from the command line with an argument of 175, you get this output:
$ java Planet 175
Your weight on MERCURY is 66.107583
Your weight on VENUS is 158.374842
Your weight on EARTH is 175.000000
Your weight on MARS is 66.279007
Your weight on JUPITER is 442.847567
Your weight on SATURN is 186.552719
Your weight on URANUS is 158.397260
Your weight on NEPTUNE is 199.207413

Nested Classes


Nested Classes

The Java programming language allows you to define a class within another class. Such a class is called a nested class and is illustrated here:
class OuterClass {

class NestedClass {

}
}
A nested class is a member of its enclosing class and, as such, has access to other members of the enclosing class, even if they are declared private. As a member of OuterClass, a nested class can be declared private, public, protected, or package private. (Recall that outer classes can only be declared public or package private.)





Terminology: Nested classes are divided into two categories: static and non-static. Nested classes that are declared static are simply called static nested classes. Non-static nested classes are called inner classes.



class OuterClass {

static class StaticNestedClass {

}
class InnerClass {

}
}

Why Use Nested Classes?

There are several compelling reasons for using nested classes, among them:

  • It is a way of logically grouping classes that are only used in one place.

  • It increases encapsulation.

  • Nested classes can lead to more readable and maintainable code.

Logical grouping of classes—If a class is useful to only one other class, then it is logical to embed it in that class and keep the two together. Nesting such “helper classes” makes their package more streamlined.


Increased encapsulation—Consider two top-level classes, A and B, where B needs access to members of A that would otherwise be declared private. By hiding class B within class A, A’s members can be declared private and B can access them. In addition, B itself can be hidden from the outside world.


More readable, maintainable code—Nesting small classes within top-level classes places the code closer to where it is used.


Static Nested Classes

As with class methods and variables, a static nested class is associated with its outer class. And like static class methods, a static nested class cannot refer directly to instance variables or methods defined in its enclosing class — it can use them only through an object reference.





Note: A static nested class interacts with the instance members of its outer class (and other classes) just like any other top-level class. In effect, a static nested class is behaviorally a top-level class that has been nested in another top-level class for packaging convenience.



Static nested classes are accessed using the enclosing class name:


OuterClass.StaticNestedClass
For example, to create an object for the static nested class, use this syntax:
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();

Inner Classes

As with instance methods and variables, an inner class is associated with an instance of its enclosing class and has direct access to that object’s methods and fields. Also, because an inner class is associated with an instance, it cannot define any static members itself.

Objects that are instances of an inner class exist within an instance of the outer class. Consider the following classes:


class OuterClass {

class InnerClass {

}
}

An instance of InnerClass can exist only within an instance of OuterClass and has direct access to the methods and fields of its enclosing instance. The next figure illustrates this idea.

An InnerClass Exists Within an Instance of OuterClass.


An InnerClass Exists Within an Instance of OuterClass


To instantiate an inner class, you must first instantiate the outer class. Then, create the inner object within the outer object with this syntax:


OuterClass.InnerClass innerObject = outerObject.new InnerClass();
Additionally, there are two special kinds of inner classes: local classes and anonymous classes (also called anonymous inner classes). Both of these will be discussed briefly in the next section.

More on Classes

More on Classes


This section covers more aspects of classes that depend on using object references and the dot operator that you learned about in the preceding sections on objects:

  • Returning values from methods.

  • The this keyword.

  • Class vs. instance members.

  • Access control.

Objects

Objects

A typical Java program creates many objects, which as you know, interact by invoking methods Through these object interactions, a program can carry out various tasks, such as implementing a GUI, running an animation, or sending and receiving information over a network. Once an object has completed the work for which it was created, its resources are recycled for use by other objects.

Here’s a small program, called CreateObjectDemo, that creates three objects: one Point object and two Rectangle objects. You will need all three source files to compile this program.


public class CreateObjectDemo {

public static void main(String[] args) {

//Declare and create a point object
//and two rectangle objects.
Point originOne = new Point(23, 94);
Rectangle rectOne = new Rectangle(originOne, 100, 200);
Rectangle rectTwo = new Rectangle(50, 100);

//display rectOne’s width, height, and area
System.out.println(“Width of rectOne: ” +
rectOne.width);
System.out.println(“Height of rectOne: ” +
rectOne.height);
System.out.println(“Area of rectOne: ” + rectOne.getArea());

//set rectTwo’s position
rectTwo.origin = originOne;

//display rectTwo’s position
System.out.println(“X Position of rectTwo: ”
+ rectTwo.origin.x);
System.out.println(“Y Position of rectTwo: ”
+ rectTwo.origin.y);

//move rectTwo and display its new position
rectTwo.move(40, 72);
System.out.println(“X Position of rectTwo: ”
+ rectTwo.origin.x);
System.out.println(“Y Position of rectTwo: ”
+ rectTwo.origin.y);
}
}

This program creates, manipulates, and displays information about various objects. Here’s the output:
Width of rectOne: 100
Height of rectOne: 200
Area of rectOne: 20000
X Position of rectTwo: 23
Y Position of rectTwo: 94
X Position of rectTwo: 40
Y Position of rectTwo: 72

The following three sections use the above example to describe the life cycle of an object within a program. From them, you will learn how to write code that creates and uses objects in your own programs. You will also learn how the system cleans up after an object when its life has ended.


Classes


Classes

The introduction to object-oriented concepts in the lesson titled Object-oriented Programming Concepts used a bicycle class as an example, with racing bikes, mountain bikes, and tandem bikes as subclasses. Here is sample code for a possible implementation of a Bicycle class, to give you an overview of a class declaration. Subsequent sections of this lesson will back up and explain class declarations step by step. For the moment, don`t concern yourself with the details.
public class Bicycle {
// the Bicycle class has three fields
public int cadence;
public int gear;
public int speed;
// the Bicycle class has one constructor
public Bicycle(int startCadence, int startSpeed, int startGear) {
gear = startGear;
cadence = startCadence;
speed = startSpeed;
}
// the Bicycle class has four methods
public void setCadence(int newValue) {
cadence = newValue;
}
public void setGear(int newValue) {
gear = newValue;
}
public void applyBrake(int decrement) {
speed -= decrement;
}
public void speedUp(int increment) {
speed += increment;
}
}

A class declaration for a MountainBike class that is a subclass of Bicycle might look like this:


public class MountainBike extends Bicycle {
// the MountainBike subclass has one field
public int seatHeight;
// the MountainBike subclass has one constructor
public MountainBike(int startHeight, int startCadence, int startSpeed, int startGear) {
super(startCadence, startSpeed, startGear);
seatHeight = startHeight;
}
// the MountainBike subclass has one method
public void setHeight(int newValue) {
seatHeight = newValue;
}
}
MountainBike inherits all the fields and methods of Bicycle and adds the field seatHeight and a method to set it (mountain bikes have seats that can be moved up and down as the terrain demands).

Language Basics

Language Basics


Variables


You’ve already learned that objects store their state in fields. However, the Java programming language also uses the term “variable” as well. This section discusses this relationship, plus variable naming rules and conventions, basic data types (primitive types, character strings, and arrays), default values, and literals.

Operators


This section describes the operators of the Java programming language. It presents the most commonly-used operators first, and the less commonly-used operators last. Each discussion includes code samples that you can compile and run.

Expressions, Statements, and Blocks


Operators may be used in building expressions, which compute values; expressions are the core components of statements; statements may be grouped into blocks. This section discusses expressions, statements, and blocks using example code that you’ve already seen.

Control Flow Statements


This section describes the control flow statements supported by the Java programming language. It covers the decisions-making, looping, and branching statements that enable your programs to conditionally execute particular blocks of code.

Expressions, Statements, and Blocks

Expressions, Statements, and Blocks

Now that you understand variables and operators, it’s time to learn about expressions, statements, and blocks. Operators may be used in building expressions, which compute values; expressions are the core components of statements; statements may be grouped into blocks.

Expressions

An expression is a construct made up of variables, operators, and method invocations, which are constructed according to the syntax of the language, that evaluates to a single value. You’ve already seen examples of expressions, illustrated in bold below:
     int cadence = 0;
anArray[0] = 100;
System.out.println(“Element 1 at index 0: ” + anArray[0]);

int result = 1 + 2; // result is now 3
if(value1 == value2) System.out.println(“value1 == value2”);

The data type of the value returned by an expression depends on the elements used in the expression. The expression cadence = 0 returns an int because the assignment operator returns a value of the same data type as its left-hand operand; in this case, cadence is an int. As you can see from the other expressions, an expression can return other types of values as well, such as boolean or String.

The Java programming language allows you to construct compound expressions from various smaller expressions as long as the data type required by one part of the expression matches the data type of the other. Here’s an example of a compound expression:



1 * 2 * 3

In this particular example, the order in which the expression is evaluated is unimportant because the result of multiplication is independent of order; the outcome is always the same, no matter in which order you apply the multiplications. However, this is not true of all expressions. For example, the following expression gives different results, depending on whether you perform the addition or the division operation first:


x + y / 100     // ambiguous

You can specify exactly how an expression will be evaluated using balanced parenthesis: ( and ). For example, to make the previous expression unambiguous, you could write the following:



(x + y) / 100 // unambiguous, recommended

If you don’t explicitly indicate the order for the operations to be performed, the order is determined by the precedence assigned to the operators in use within the expression. Operators that have a higher precedence get evaluated first. For example, the division operator has a higher precedence than does the addition operator. Therefore, the following two statements are equivalent:


x + y / 100 

x + (y / 100) // unambiguous, recommended


When writing compound expressions, be explicit and indicate with parentheses which operators should be evaluated first. This practice makes code easier to read and to maintain.


Statements

Statements are roughly equivalent to sentences in natural languages. A statement forms a complete unit of execution. The following types of expressions can be made into a statement by terminating the expression with a semicolon (;).

  • Assignment expressions

  • Any use of ++ or

  • Method invocations

  • Object creation expressions
Such statements are called expression statements. Here are some examples of expression statements.
aValue = 8933.234;                      // assignment statement
aValue++; // increment statement
System.out.println(“Hello World!”); // method invocation statement
Bicycle myBike = new Bicycle(); // object creation statement
In addition to expression statements, there are two other kinds of statements: declaration statements and control flow statements. A declaration statement declares a variable. You’ve seen many examples of declaration statements already:
double aValue = 8933.234; //declaration statement
Finally, control flow statements regulate the order in which statements get executed. You’ll learn about control flow statements in the next section, Control Flow Statements

Blocks

A block is a group of zero or more statements between balanced braces and can be used anywhere a single statement is allowed. The following example, BlockDemo, illustrates the use of blocks:
class BlockDemo {
public static void main(String[] args) {
boolean condition = true;
if (condition) { // begin block 1
System.out.println(“Condition is true.”);
} // end block one
else { // begin block 2
System.out.println(“Condition is false.”);
} // end block 2
}
}

Control Flow Statements

Control Flow Statements


The statements inside your source files are generally executed from top to bottom, in the order that they appear. Control flow statements, however, break up the flow of execution by employing decision making, looping, and branching, enabling your program to conditionally execute particular blocks of code. This section describes the decision-making statements (if-then, if-then-else, switch), the looping statements (for, while, do-while), and the branching statements (break, continue, return) supported by the Java programming language.