Classes

A class definition is the fundamental mechanism for encapsulation of data and functionality in Java. In the object-oriented approach, a class describes a set of similar objects - objects with the same properties and functionality. These objects are called instances of the class. The class declaration defines the data an instance will contain and the functionality (methods) an instance will provide. A class declaration can be thought of as a "template" for creating new instances.

A class declaration also defines a reference type. Values of this type are references to instances of the class, or to instances of subclasses of the class.

A class can be a member of a package, in which case it is called an outer class or top-level class. Or it can be a member of another class, in which case it is a nested class or inner class.

The fully-qualified name of an outer class consists of the class name prefixed with the package name: e.g.,

    java.util.Vector
    javax.swing.text.html.HTML

are fully-qualified names of the class Vector defined in the package java.util, and of the class HTML defined in the package javax.swing.text.html. A nested class is named by prefixing the class name with the name of the containing class: e.g.,

    javax.swing.text.html.HTML.Tag

is the name of the class Tag defined in the class javax.swing.text.html.HTML.

A class may be anonymous, in which case it does not have a name.

A class declaration consists of a heading and a body. The heading specifies the name of the class, the (super)class that the class extends, and any interfaces the class implements. The body is enclosed in braces and follows the heading. The body contains the declarations of class members.

Interfaces

An interface is a structure that is in many ways similar to a class. An interface contains only specification information, and no implementation. Interfaces are considered in a separate section.

Class heading

A class heading consists of the key word class followed by an identifier that names the class.

A class that is a member of a package may be specified public, in which case its scope is global: i.e., it is visible throughout the entire program. For instance,

    public class Circle

Otherwise its scope is limited to the package of which it is a member. That is, the class can be accessed only from package members.

A class may be specified as final, in which case it cannot be extended. For instance,

    public final class Math

A class may be specified as abstract. Abstract classes are discussed below.

A class that is a member of another class may have its scope modified just as any other class member. It may also be declared static. Scope modifiers and static modifiers of class members are explained below.

Modifiers can be written in any order. Thus specifications

    public abstract class List

and

    abstract public class List

are equivalent.

Extends clause and implements clause

If the class explicitly extends a (super)class or implements an interface, these are listed following the name of the class. The superclass is named in an extends clause, consisting of the keyword extends followed by the name of the superclass. For instance,

    public class EnchantedRoom extends Room

A class extends exactly one other class. If an extends clause is not included in the class heading, the class implicitly extends java.lang.Object.

Any interfaces explicitly implemented by the class are named in an implements clause, consisting of the key word implements followed by a comma separated list of interfaces. For example,

    public class Room implements Cloneable, MazeFeature

A class heading may contain both an extends clause and an implements clause. In this case, the extends clause precedes the implements clause.

    public class HexedRoom extends Room implements HexedObject

Extending classes and implementing interfaces are explained below.

Class body

A class body contains a sequence of member declarations, constructor declarations, and initializers, enclosed in braces. Class members are variables, named constants, methods, classes, and interfaces. The heading and the body make up the class declaration. For instance, the class declaration

    public class Circle {
     
        // A constructor declaration:
        public Circle (int radius) {
            this.radius = radius;
        }
     
        // A method declaration:
        public int radius () {
            return this.radius;
        }
     
        // A variable declaration:
        private int radius;
     
    }

defines a class named Circle, with one constructor and two members: a method radius, and a variable also named radius. Objects that are instances of the class are created by invoking the constructor. For example, the constructor invocation

    new Circle(10)

creates a Circle instance, and returns a reference to the newly created instance. The type of the value returned is reference-to-Circle.

Class members

Class members are variables, named constants, methods, classes, and interfaces. Members are either declared in the class, inherited from a superclass, or inherited from an interface that the class implements.

Members are either associated with the class itself or with each instance of the class. If the member is specified as static, it is associated with the class itself rather than with an instance of the class. Examples are:

    static void main (String[] args) { ... }
    static boolean condition (boolean preCondition) { ... }

Non-static members are associated with each instance of the class. We refer to non-static members as "instance members." Suppose, for example, a class Monitor contains the following variable declarations:

    public class Monitor {
        ...
        private static int global = 0;
        private int local;
    }

The first declaration results in creation of a single variable named global. This variable exists whether or not the class is ever instantiated. However, each instance of the class has its own variable named local. The variable is created when the instance is created.

A variable, method, and class defined in the same class can all have the same simple name. (The class Circle shown above has both a method and variable named radius, for instance.) A class can define several methods with the same name as long as the methods differ in the number and/or types of their parameters. (This is called "overloading.") For example, a class can contain the following three methods, all named m:

    public void m (Object obj) { ... }
    public void m (Circle c) { ... }
    public void m (Object obj, Circle c) { ... }

It may not contain both of these:

    public void m () { ... }
    private Object m () { ... }

nor may it contain both of these:

    public void m (int a) { ... }
    private Object m (int b) { ... }

The method name and list of parameter types is the signature of the method. For instance, the first three methods named m mentioned above have signatures

    m (Object)
    m (Circle)
    m (Object, Circle)

A class cannot contain two methods with the same signature.

Members declared "final"

A member variable or method may be declared final:

    public static final int OPEN = 3;
    public final void move (int direction)

A final variable is essentially a named constant. Once assigned, it's value cannot be changed. Static final variables are typically initialized in their declaration, instance final variables in their declaration or in a class constructor.

Note, though, that if the value of a final variable is a reference value, the object referenced may change state. For example, suppose START is defined as

    public static final Room START = entryway;

where entryway is a Room. START will always contain the same value - that is, it will always reference the same object. But the state of the object referenced can change.

A final method cannot be overridden. Overriding is explained below.

Accessing class members

Static members are generally referenced by prefixing the member name with the class name. If the static method condition specified above is defined in the class Require, it can be invoked as

    Require.condition(i >= 0);

An instance member can be accessed by prefixing the member name with a reference to the instance. For example, if monitor is an instance of the class Monitor:

    Monitor monitor = new Monitor(...);

then monitor.local denotes it's instance variable local.

The reference need not be from a variable. It might be obtained by a method or constructor invocation. Suppose the class Monitor contains a method

    public Circle areaSurveyed ()

Then the method invocation

    monitor.areaSurveyed()

produces a reference to a Circle object, and

    monitor.areaSurveyed().radius()

invokes the Circle's radius method.

Clearly, static members cannot reference instance members. For example, if the class Monitor has a static method update, this method cannot reference the variable local.

    public static void update () {
        // This method is not associated with any instance,
        // and so cannot reference instance members
        ...
    }

Accessing a class member within the declaration of its class

Within its class, a member can generally be accessed simply by its name. The name of a static member refers to the member associated with the class; the name of an instance member refers to the member associated with the executing instance. For example, if the class Monitor sketched above contains the method

    public void getGlobal () {
        local = global;
    }

the identifier global refers to the static class variable, while the identifier local refers to the instance variable associated with the Monitor instance that is executing the method.

An exception is that a member variable cannot be accessed by its name alone within the scope of an automatic variable of the same name. For example, the occurrence of the identifier local in the following method setLocal refers to the parameter, not to the instance variable:

    public class Monitor {
        ...
        public void setLocal (int local) {
            global = local;
        }
        ...
        private static int global;
        private int local;
        ...
    }

The keyword "this"

The keyword this refers to the executing instance. Within its class, an instance member can always be accessed by prefixing its simple name with the keyword word this. The following method copies the value of the parameter local to the instance variable local:

    public void setLocal (int local) {
        this.local = local;
    }

A member of an instance of a textually enclosing class can be accessed by qualifying the keyword this. For example, suppose Node is an inner class of Bag:

    public class Bag {
        ...
        private Node first;
        ...
        private class Node {
            ...
            private Node first;
        } // end of class Node
        ...
    } // end of class Bag

Both Bag and Node have instance variables named first. Within the class Node, the Bag's instance variable first can be referenced as Bag.this.first. The class Node might contain a statement such as

    this.first = Bag.this.first;

This assigns the value of the Bag's instance variable first to the Node's instance variable first. If the class Node did not declare an instance variable named first, the Bag's variable could be accessed in Node simply as first.

The keyword "super"

A member defined in an immediate superclass can be accessed in the subclass by prefixing the member name with the keyword super. This is useful for referencing overridden methods or hidden variables. For example, consider the class GrabBag that extends Bag:

    public class Bag {
        ...
        public boolean equals (Object obj) { ... }
        ...
        protected int length;
    }
     
    public class GrabBag extends Bag {
        ...
        public boolean equals (Object obj) { ... }
        ...
        private double length;          // hides inherited length
    }

GrabBag overrides the Bag method equals, and declares its own variable length, hiding the variable length inherited from Bag. In GrabBag, the overridden method can be invoked as super.equals(someObject) and the hidden variable accessed as super.length. Hiding and overriding are explained below.

Controlling access to class members

If an outer class is not declared public, then access to any member of the class (or any member of a nested class) is restricted to the class's package. An outer class declared public has global scope: the class can be referenced anywhere in the program. In the following discussion, we generally assume a public outer class.

A member can be specified as public, protected, or private. Otherwise, the member is restricted or package private.

    public void reset () { ... }
    protected int numberTaken;
    int updateEnrollment (Student s) { ... }
    private class Node { ... }
    private int count;

Public members

Public members have the same scope as the class. For example, if size is a public member of public class Bag

    public class Bag {
        ...
        public int size () { ... }
        ...
    }

then the method size has global scope. The following code

    Bag b = new Bag(...);
    if (b.size() > 0) ...

can occur essentially anywhere in the program.

Private members

The scope of a private member is limited to the (outer) class declaration. For example, if length is a private member of the class Bag,

    public class Bag {
        ...
        private int length;
        ...
    }

then the reference b.length, where b is a Bag, can occur only in the body of Bag: that is, only between the braces { and } that delimit the definition of Bag.

Restricted members

The scope of a restricted member is the package of the outer class. If verify is a restricted Bag member,

    public class Bag {
        ...
        void verify () { ... }
        ...
    }

the reference b.verify(), where b is again a Bag, can occur anywhere in the package of which Bag is a member.

Protected members

Like restricted members, protected members are accessible in the package in which they are declared. A protected member may be accessed from outside the package only by code "responsible for the implementation of the object." That is, protected members are available (as inherited members) in subclasses of the defining class. Suppose limit is a protected member of Bag, and GrabBag is a subclass:

    public class Bag {
        ...
        protected int limit;
        ...
    }
     
    public class GrabBag extends Bag {
        ...
    }

If gb is a GrabBag, the reference gb.limit is legal in Bag, GrabBag, and the package of which Bag is a member. (Since GrabBag is a subclass of Bag, Bag is "responsible for the implementation" of the GrabBag object.) But it is not otherwise legal in a subclass of GrabBag. In particular, the reference b.limit, where b is a Bag, is not valid in GrabBag unless GrabBag and Bag are in the same package.

The combination of packaging, inheritance, and scope specification can lead to very confusing situations. Suppose class Bottom extends class Middle which extends class Top. Suppose further that Top and Bottom are in the same package, and that Middle is in a different package.

Top defines a restricted variable rest and a protected variable prot:

    public class Top {
        ...
        int rest;
        protected int prot;
    }

Middle does not inherit rest since it is not in the same package as Top. And since Bottom extends Middle, Bottom does not inherit rest.

However, since Top and Bottom are in the same package, the rest variable of a Top instance can be accessed in Bottom. For instance, Bottom can contain the method:

    public setRest (Top t) {
        t.rest = 0;
    }

The protected variable prot is inherited by Middle, and hence by Bottom. Both Middle and Bottom can contain the statement:

    this.prot = 0;

or even:

    prot = 0;

Furthermore, Middle can reference the prot member of a Bottom (since as a superclass, Middle code is "responsible" for the implementation of a Bottom), but not of a Top. That is, the class Middle can contain this method:

    public setBottomProt (Bottom b) {
        b.prot = 0;
    }

but not this one:

    public setTopProt (Top t) {
        t.prot = 0;
    }

The class Bottom can contain the method setTopProt because Top and Bottom are in the same package. In fact, the class Bottom can contain the method:

    public setMidProt (Middle m) {
        m.prot = 0;
    }

because Bottom is in the package in which prot is declared.

We suggest the following use of scope specification:

Nested classes

Scope rules always refer to the outer class containing the definition. For example, if Node is an inner class of Bag defined as follows

    public class Bag {
        ...
        private class Node {
            ...
            private Node next;
        }
        ...
        private int length;
    }

the Bag variable length and the Node variable next can be accessed anywhere in the declaration of the class Bag. The class Bag, for instance, could contain the method:

    private Node advanceFrom (Node n, int i) {
        while (i > 0) {
            n = n.next;
            i = i-1;
        }
        return n;
    }

and the class Node could contain:

    private void evaporate () {
        length = length - 1;
        next.evaporate();
        next = null;
    }

Inheritance

Every class (except the class java.lang.Object) extends exactly one other class. The class extended is specified in the class heading:

    public class EnchantedRoom extends Room

If the class heading has no extends clause, the class extends java.lang.Object. The extending class is a subclass of the extended class; the extended class is a superclass of the extending class. Thus EnchantedRoom is a subclass of Room, and Room is a superclass of EnchantedRoom.

The relations "subclass" and "superclass" are transitive. That is, if A extends B, and B extends C, then A is considered to be a subclass of C. But A is an immediate subclass of B only, and B is the immediate superclass of A.

A class may also implement any number of interfaces. These are also specified in the heading:

    public class Ring extends Item implements Wearable, Hexable

The implementing class is a subclass of the interface, and the interface is a superinterface of the class.

A class inherits all the public and protected members of its immediate superclass and of any immediate superinterfaces. That is, members of the superclass or superinterface are also members of the class. A class also inherits restricted members of its immediate superclass if it is in the same package as its superclass.

Since inheriting from an interface is essentially the same as inheriting from a class, we limit our attention to classes in the following discussion.

If, for example, the class Room defines the method getWidth:

    public class Room {
        ...
        public int getWidth () { ... }
        ...
    }

the class EnchantedRoom also has this method as a member. If garden is an EnchantedRoom, the method can be invoked as garden.getWidth().

Subclasses of EnchantedRoom will inherit the method from EnchantedRoom.

Note that while a class does not technically inherit private members of its superclass, it still "has" these members: but they can be accessed only from the superclass declaration. Suppose the class Room contains the following:

    public class Room {
        ...
        public void setWidth (int newWidth) {
            this.width = newWidth;
        }
        ...
        private int width;
    }

The class EnchantedRoom inherits the method setWidth but not the variable width. EnchantedRoom could contain the method

    public void doubleWidth () {
        this.setWidth(this.getWidth()*2);
    }

but neither the method

    public void doubleWidth () {
        this.width = this.width*2;
    }

nor the method

    public void doubleWidth () {
        super.width = super.width*2;
    }

The variable width can only be accessed within the body of Room.

Subtyping

If one class is a subclass of another, then the reference type associated with the first class is a subset of the reference type associated with the second. For instance, if class EnchantedRoom extends class Room, then every EnchantedRoom instance is also a Room. Thus a reference-to-EnchantedRoom is also a reference-to-Room, and the set of references-to-EnchantedRoom is a subset of the set of references-to-Room. The type reference-to-EnchantedRoom is said to be a subtype of the type reference-to-Room.

The importance of subtyping is that an instance of a subclass of class C can be provided in any context requiring an instance of C. If a method requires a Room as argument:

    public void enter (Room room)

the method may be invoked with an EnchantedRoom, or an instance of any other subclass of Room, as an argument.

Hiding

A class that declares a variable with the same name as one it inherits hides the superclass variable. Suppose class Card declares a variable value

    public class Card {
        ...
        public int value = 3;
    }

and subclass HighCard declares a variable of the same name

    public class HighCard extends Card {
        ...
        public double value = 10.0;
    }

The double variable declared in HighCard hides the int declared in Card. In the class HighCard, the identifier value refers to the double. The int can be accessed as super.value. The following method appearing in HighCard

    public void printValues () {
        System.out.println(value + " " + super.value);
    }

will write a line containing

    10.0 3

Note that which variable is accessed depends on the compile-time type of the reference. For example, the following method

    public void printCardValue (Card c) {
        System.out.pritnln(c.value);
    }

will write out 3 even if it is invoked with a HighCard argument:

    printCardValue(new HighCard());

The compile-time type of the parameter c is Card, and it does not matter that c references a HighCard at run-time. It is Card's instance variable value that will be accessed in the method.

Overriding

A class can override an instance method that it inherits, providing a different implementation of the method from its superclass. Suppose the class Line contains a method equals defined as follows:

    public class Line {
        ...
        public boolean equals (Object obj) {
            if (obj instanceof Line)
                return this.length == ((Line)obj).length;
            else
                return false;
        }
     
        private int length;
    }

The subclass ThickLine overrides the method by providing a different implementation:

    public class ThickLine extends Line {
        ...
        public boolean equals (Object obj) {
            if (obj instanceof ThickLine)
                return this.width == ((ThickLine)obj).width;
            else
                return false;
        }
     
        private int width;
    }

Which implementation is executed depends on the run-time class of the instance, not the compile-time type of the reference. (Compare this to hiding, discussed above.) If the method

    public void compareLines (Line line1, Line line2) {
        if (line1.equals(line2))
            ...
    }

is invoked with a ThickLine as the first argument

    compareLines(new ThickLine(...), ...);

the implementation of equals defined in ThickLine will be executed, even thought the compile-time type of the variable line1 is Line, not ThickLine.

The implementation of equals defined in Line can be referenced in ThickLine by using the keyword super. For instance, the method equals in ThickLine might be implemented as follows.

    public boolean equals (Object obj) {
        if (super.equals(obj) && obj instanceof ThickLine)
            return this.width == ((ThickLine)obj).width;
        else
            return false;
    }

(Note: a static method can be hidden, like a variable, but not overridden.)

Overloading

Overloading is semantically quite distinct from overriding. But because of the similarity in syntax, it is easy to confuse the two. Overloading is defining more than one method with the same name in the same class. Overriding involves defining two distinct implementations of a method in different classes.

Suppose the method equals defined in ThickLine is specified as

    public boolean equals (ThickLine obj)

ThickLine inherits the method public boolean equals (Object obj) from Line. Since the method defined in ThickLine has a parameter of a different type, it overloads but does not override the inherited method. ThickLine now has two methods named equal, one inherited:

    public boolean equals (Object obj)

and the other defined in the class:

    public boolean equals (ThickLine obj)

Overloading is resolved at compile-time, with compile-time types. Run-time class is relevant only in override resolution. If

    Line line1 = new ThickLine(...);
    Line line2 = new ThickLine(...);

the compile-time type of both line1 and line2 is Line, regardless of the fact that these variables actually reference ThickLine instances at run-time. The method invocation

    line1.equals(line2)

executes the equals method defined in Line. The compile-time type of line1 is Line, and the class Line has only one equals method. The fact that line1 and line2 both happen to reference ThickLine instances at run-time is of no relevance.

Similarly, if

    ThickLine tline = new ThickLine(...);

the method invocation

    tline.equals(line1)

executes the equals method defined in Line and inherited by ThickLine. The compile-time type of line1 is Line, not ThickLine. A reference-to-Line is also a reference-to-Object, since reference-to-Line is a subtype of reference-to-Object. Reference-to-Line is not a subtype of reference-to-ThickLine. At compile-time, the inherited method is the one that is matched. Once again, the fact that line1 actually refers to a ThickLine at run-time is not relevant.

Abstract classes and abstract methods

A class can be declared abstract, as in

    public abstract class List

An abstract class cannot be instantiated. An abstract class serves only as a base class for building subclasses. (A class that is not abstract is sometimes called "concrete.")

Since a constructor for an abstract class will only be invoked by the constructor of a subclass, it is conventionally declared protected:

    public abstract class List {
     
        protected List () { ... }

An abstract class or an interface may have abstract methods as members. An abstract method has a heading but no body. Syntactically, the body is replaced by a semicolon:

    public abstract List createList ();

A concrete subclass of an abstract class must override inherited abstract methods, providing implementations. Similarly, a concrete class that implements an interface must provide implementations for inherited abstract methods.

Nested classes

A class can be a member of another class, in which case it is a nested class or member class. A member class that is not static is called an inner class. Member classes can be qualified ( e.g., declared private) in ways similar to other class members.

Note, though, that member access is controlled with respect to the outer class. For instance, a private member of an inner class can be accessed in the outer class of which the inner class is a member. The instance variable next of a Node, as defined below, can be accessed anywhere in the declaration of the class Bag:

    public class Bag {
        ...
        private void chopAfter (Node n) {
            n.next = null;      // this is OK
        }
        ...
        private class Node {
            ...
            private Node next;
        } // end of class Node
        ...
    } // end of class Bag

Also note that the reference type defined by an inner class does not depend on the instance of which the class is a component. Suppose the class Outer is defined as follows:

    public class Outer {
     
        public class Inner {
            public void setX (int i) {
                myX = i;
            }
        }
     
        public int myX;
        public Inner myInner;
    }

Each instance of the class Outer has a member class Inner. The setX method of an Inner instance sets the myX variable of the containing Outer instance. For example, consider the following code:

    Outer o1 = new Outer();
    Outer o2 = new Outer();
    Outer.Inner oi1 = o1.new Inner();
    Outer.Inner oi2 = o2.new Inner();
    oi1.setX(1);
    oi2.setX(2);
    System.out.println(o1.myX);
    System.out.println(o2.myX);

First note the syntax. The type name of the inner classes is Outer.Inner. The expression to instantiate the inner class associated with o1 is o1.new Inner(). Invoking oi1.setX modifies o1's instance variable myX. Invoking oi2.setX modifies o2's variable. The output will be

    1
    2

But the reference values oi1 and oi2 are of the same type: reference-to-Outer.Inner. Thus an assignment like

    o1.myInner = oi2;

is syntactically correct. After this assignment, the statement

    o1.myInner.setX(3);

will change o2's instance variable myX.

Anonymous classes

An anonymous class is a nameless class that is simultaneously declared and instantiated in a single expression. Each anonymous class is unique and has only one instance: the instance created when the class is defined.

An anonymous class has a "parent" that is either a class or an interface. The name of the parent is used when declaring and instantiating the class. The syntax for declaring and instantiating an anonymous class is

    new parent
(argument list) class body

Suppose the interface Portable is defined as

    public interface Portable {
        int weight ();
    }

An anonymous class that implements the interface can be declared and instantiated as

    new Portable() {
        public int weight () {
            return 0;
        }
    }

Since the interface specifies the abstract method weight, the implementing anonymous class must implement the method weight.

Note carefully what is happening here. The interface is not instantiated. Rather a new, nameless concrete class that implements the interface is declared, and this new class is instantiated.

The anonymous class is an immediate subclass of Object and implements the interface Portable. We can write:

    Portable balloon =
        new Portable() {
            public int weight () {
                return 0;
            }
        };

Constructors are not explicitly defined for anonymous classes. They are provided by default. The constructors for an anonymous class match the constructors of its immediate superclass. The anonymous class constructor simply invokes the corresponding superclass constructor. For example, the anonymous Portable class defined above is an immediate subclass of Object. Since Object has one parameterless constructor, and so does the anonymous class. On the other hand, if the class Monitor has two constructors, one with no parameters and one with an Observable parameter,

    public Monitor () { ... }

    public Monitor (Observable s) { ... }

then an anonymous subclass of Monitor will also have a two constructors. For instance, we can declare and instantiate an anonymous subclass as follows, assuming that this is an Observable:

    new Monitor (this) {
            public void update (Observable o, Object arg) {
                ...
            }
        };

Initializers

A class declaration can contain static and instance initializers. A initializer is simply a block, optionally preceded by the keyword static. If the block is labeled static, it is a static initializer; otherwise it is an instance initializer.

Initializers are used to initialize static class variables and instance variables. Here's an example from the Java tutorial:

    import java.util.*;
    class Errors {
     
        static ResourceBundle errorStrings;
     
        static {
            try {
                errorStrings =
                    ResourceBundel.getBundle("ErrorStrings");
            } catch (MissingResourceException e) {
                // handle error
            }
        }
    }

The static variable errorStrings must be initialized in a static initializer because initialization involves invoking the method getBundle, which might throw a checked MissingResourceException.

Instance initializers are executed after the superclass constructor, and before the constructor body. For example, if classes A and B are declared as follows:

    class A {
        public A () {
            System.out.print("Two");
        }
     
        { System.out.print("One"); }
    }
     
    class B extends A {
        public B () {
            System.out.println("Four");
        }
     
        { System.out.print("Three"); }
    }

instantiating B results in a line of output containing "OneTwoThreeFour."


Fred Hosch
Last modified: Tue Aug 7 09:11:16 CDT 2001