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 thedot
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 thedot
operator that you learned about in the preceding section: returning values from methods, thethis
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 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:
Annotations can be applied to a program’s declarations of classes, fields, methods, and other program elements.
- 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.
The annotation appears first, often (by convention) on its own line, and may include elements with named or unnamed values:
or@Author(
name = “Benjamin Franklin”,
date = “3/27/2003”
)
class MyClass() { }
If there is just one element named “value,” then the name may be omitted, as in:@SuppressWarnings(value = “unchecked”)
void myMethod() { }
Also, if an annotation has no elements, the parentheses may be omitted, as in:@SuppressWarnings(“unchecked”)
void myMethod() { }
@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:
To add this same metadata with an annotation, you must first define the annotation type. The syntax for doing this is: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
}
The annotation type definition looks somewhat like an interface definition where the keyword@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
}
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”).
While it’s not required to use this annotation when overriding a method, it helps to prevent errors. If a method marked with// mark method as a superclass method
// that has been overridden
@Override
int overriddenMethod() { }
@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, calledapt
. In release 6 of the JDK, the functionality ofapt
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
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:
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.public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY
}
Here is some code that shows you how to use the
Day
enum defined above:
The output is: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();}
}
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 staticvalues
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 thePlanet
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));
}
In the following example,
Note: All enums implicitly extendjava.lang.Enum
. Since Java does not support multiple inheritance, an enum cannot extend anything else.
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.
In addition to its properties and constructor,
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.
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):
If you runpublic 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));
}
}
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
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:
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 ofclass OuterClass {
…
class NestedClass {
…
}
}
OuterClass
, a nested class can be declaredprivate
,public
,protected
, or package private. (Recall that outer classes can only be declaredpublic
or package private.)
Terminology: Nested classes are divided into two categories: static and non-static. Nested classes that are declaredstatic
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:
For example, to create an object for the static nested class, use this syntax:OuterClass.StaticNestedClass
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:
An instance ofclass OuterClass {
…
class InnerClass {
…
}
}InnerClass
can exist only within an instance ofOuterClass
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
To instantiate an inner class, you must first instantiate the outer class. Then, create the inner object within the outer object with this syntax:
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.OuterClass.InnerClass innerObject = outerObject.new InnerClass();
More on Classes
This section covers more aspects of classes that depend on using object references and thedot
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
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: onePoint
object and twoRectangle
objects. You will need all three source files to compile this program.
This program creates, manipulates, and displays information about various objects. Here’s the output: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);
}
}
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
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 aBicycle
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 ofBicycle
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 ofBicycle
and adds the fieldseatHeight
and a method to set it (mountain bikes have seats that can be moved up and down as the terrain demands).
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
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:The data type of the value returned by an expression depends on the elements used in the expression. The expressionint 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”);
cadence = 0
returns anint
because the assignment operator returns a value of the same data type as its left-hand operand; in this case,cadence
is anint
. As you can see from the other expressions, an expression can return other types of values as well, such asboolean
orString
.
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 (;
).
Such statements are called expression statements. Here are some examples of expression statements.
- Assignment expressions
- Any use of
++
or—
- Method invocations
- Object creation expressions
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:aValue = 8933.234; // assignment statement
aValue++; // increment statement
System.out.println(“Hello World!”); // method invocation statement
Bicycle myBike = new Bicycle(); // object creation 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 Statementsdouble aValue = 8933.234; //declaration statement
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
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.