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.