MethodModifiers:
MethodModifier
MethodModifiersMethodModifier MethodModifier: one of
public protected private
abstract static final synchronized native
The access modifiers public
, protected
, and private
are discussed in §6.6.
A compile-time error occurs if the same modifier appears more than once in a
method declaration, or if a method declaration has more than one of the access
modifiers public
, protected
, and private
. A compile-time error occurs if a
method declaration that contains the keyword abstract
also contains any one of
the keywords private
, static
, final
, native
, or synchronized
.
If two or more method modifiers appear in a method declaration, it is customary, though not required, that they appear in the order consistent with that shown above in the production for MethodModifier.
An abstract
method declaration introduces the method as a member, providing
its signature (name and number and type of parameters), return type, and throws
clause (if any), but does not provide an implementation. The declaration of an
abstract
method m must appear within an abstract
class (call it A); otherwise a
compile-time error results. Every subclass of A that is not abstract
must provide
an implementation for m, or a compile-time error occurs. More precisely, for every
subclass C of the abstract
class A, if C is not abstract
, then there must be some
class B such that all of the following are true:
abstract
, and this declaration is inherited by C, thereby providing an implementation of method m that is visible to C.
If there is no such class B, then a compile-time error occurs.
It is a compile-time error for a private
method to be declared abstract
. It would be impossible for a subclass to implement a private
abstract
method, because private
methods are not visible to subclasses; therefore such a method could never be used.
It is a compile-time error for a static
method to be declared abstract
.
It is a compile-time error for a final
method to be declared abstract
.
An abstract
class can override an abstract
method by providing another abstract
method declaration. This can provide a place to put a documentation comment (§18), or to declare that the set of checked exceptions (§11.2) that can be thrown by that method, when it is implemented by its subclasses, is to be more limited. For example, consider this code:
class BufferEmpty extends Exception { BufferEmpty() { super(); } BufferEmpty(String s) { super(s); } }
class BufferError extends Exception { BufferError() { super(); } BufferError(String s) { super(s); } }
public interface Buffer { char get() throws BufferEmpty, BufferError; }
public abstract class InfiniteBuffer implements Buffer { abstract char get() throws BufferError; }
The overriding declaration of method get
in class InfiniteBuffer
states that
method get
in any subclass of InfiniteBuffer
never throws a BufferEmpty
exception, putatively because it generates the data in the buffer, and thus can never
run out of data.
An instance method that is not abstract
can be overridden by an abstract
method. For example, we can declare an abstract
class Point
that requires its subclasses to implement toString
if they are to be complete, instantiable classes:
abstract class Point { int x, y; public abstract String toString(); }
This abstract
declaration of toString
overrides the non-abstract
toString
method of class Object
(§20.1.2). (Class Object
is the implicit direct superclass
of class Point
.) Adding the code:
class ColoredPoint extends Point { int color; public String toString() { return super.toString() + ": color " + color; // error } }
results in a compile-time error because the invocation super.toString()
refers
to method toString
in class Point
, which is abstract
and therefore cannot be
invoked. Method toString
of class Object
can be made available to class
ColoredPoint
only if class Point
explicitly makes it available through some
other method, as in:
abstract class Point { int x, y; public abstract String toString(); protected String objString() { return super.toString(); } } class ColoredPoint extends Point { int color; public String toString() { return objString() + ": color " + color; // correct } }
A method that is declared static
is called a class method. A class method is
always invoked without reference to a particular object. An attempt to reference
the current object using the keyword this
or the keyword super
in the body of a
class method results in a compile time error. It is a compile-time error for a
static
method to be declared abstract
.
A method that is not declared static
is called an instance method, and sometimes called a non-static
method). An instance method is always invoked with respect to an object, which becomes the current object to which the keywords this
and super
refer during execution of the method body.
A method can be declared final
to prevent subclasses from overriding or hiding
it. It is a compile-time error to attempt to override or hide a final
method.
A private
method and all methods declared in a final
class (§8.1.2.2) are implicitly final
, because it is impossible to override them. It is permitted but not required for the declarations of such methods to redundantly include the final
keyword.
It is a compile-time error for a final
method to be declared abstract
.
At run-time, a machine-code generator or optimizer can easily and safely "inline" the body of a final
method, replacing an invocation of the method with the code in its body, as in the example:
final class Point { int x, y; void move(int dx, int dy) { x += dx; y += dy; } }
class Test { public static void main(String[] args) { Point[] p = new Point[100]; for (int i = 0; i < p.length; i++) { p[i] = new Point(); p[i].move(i, p.length-1-i); } } }
Here, inlining the method move
of class Point
in method main
would transform
the for
loop to the form:
for (int i = 0; i < p.length; i++) { p[i] = new Point(); Point pi = p[i]; pi.x += i; pi.y += p.length-1-i; }
The loop might then be subject to further optimizations.
Such inlining cannot be done at compile time unless it can be guaranteed that Test
and Point
will always be recompiled together, so that whenever Point
-and specifically its move
method-changes, the code for Test.main
will also be updated.
A method that is native
is implemented in platform-dependent code, typically
written in another programming language such as C, C++, FORTRAN, or assembly
language. The body of a native
method is given as a semicolon only, indicating
that the implementation is omitted, instead of a block.
A compile-time error occurs if a native
method is declared abstract
.
For example, the class RandomAccessFile
of the standard package java.io
might declare the following native
methods:
package java.io;
public class RandomAccessFile
implements DataOutput, DataInput { . . . public native void open(String name, boolean writeable) throws IOException; public native int readBytes(byte[] b, int off, int len) throws IOException; public native void writeBytes(byte[] b, int off, int len) throws IOException; public native long getFilePointer() throws IOException; public native void seek(long pos) throws IOException; public native long length() throws IOException; public native void close() throws IOException; }
A synchronized
method acquires a lock (§17.1) before it executes. For a class
(static)
method, the lock associated with the Class
object (§20.3) for the
method's class is used. For an instance method, the lock associated with this
(the
object for which the method was invoked) is used. These are the same locks that
can be used by the synchronized
statement (§14.17); thus, the code:
class Test { int count; synchronized void bump() { count++; } static int classCount; static synchronized void classBump() { classCount++; } }
has exactly the same effect as:
class BumpTest { int count; void bump() { synchronized (this) { count++; } } static int classCount; static void classBump() { try { synchronized (Class.forName("BumpTest")) { classCount++; } } catch (ClassNotFoundException e) { ... } } }
public class Box {
public synchronized Object get() { return contents; }
public synchronized boolean put(Object contents) { return false; return true; }
}
defines a class which is designed for concurrent use. Each instance of the class
Box
has an instance variable contents
that can hold a reference to any object.
You can put an object in a Box
by invoking put
, which returns false
if the box is
already full. You can get something out of a Box
by invoking get
, which returns a
null reference if the box
is empty.
If put
and get
were not synchronized
, and two threads were executing methods for the same instance of Box
at the same time, then the code could misbehave. It might, for example, lose track of an object because two invocations to put
occurred at the same time.
See §17 for more discussion of threads and locks.