Spec-Zone .ru
спецификации, руководства, описания, API
|
ГЛАВА 8
Объявления класса определяют новые ссылочные типы и описывают, как они реализуются (§8.1).
Имя класса имеет как его контекст все описания типа в пакете, в котором класс объявляется (§8.1.1). Класс может быть объявлен abstract
(§8.1.2.1) и должен быть объявлен abstract
если это не полностью реализуется; такой класс нельзя инстанцировать, но может быть расширен подклассами. Класс может быть объявлен final
(§8.1.2.2), когда у этого не может быть подклассов. Если класс объявляется public
, тогда это может быть упомянуто от других пакетов.
Каждый класс кроме Object
расширение (то есть, подкласс) единственный существующий класс (§8.1.3) и может реализовать интерфейсы (§8.1.4).
Тело класса объявляет элементы (поля и методы), статические инициализаторы, и конструкторы (§8.1.5). Контекст имени элемента является всем объявлением класса, которому принадлежит элемент. Поле, метод, и объявления конструктора могут включать модификаторы доступа (§6.6) public
, protected
, или private
. Элементы класса включают и объявленный и наследованные элементы (§8.2). Недавно объявленные поля могут скрыть поля, объявленные в суперклассе или суперинтерфейсе. Недавно объявленные методы могут скрыть, реализовать, или переопределить методы, объявленные в суперклассе или суперинтерфейсе.
Полевые объявления (§8.3) описывают переменные класса, которые воплощаются однажды, и переменные экземпляра, которые недавно воплощаются для каждого экземпляра класса. Поле может быть объявлено final
(§8.3.1.2), когда это не может быть присвоено за исключением части его объявления. Любое полевое объявление может включать инициализатор; объявление a final
поле должно включать инициализатор.
Объявления метода (§8.4) описывают код, который может быть вызван выражениями вызова метода (§15.11). Метод класса вызывается относительно типа класса; метод экземпляра вызывается относительно некоторого определенного объекта, который является экземпляром типа класса. Метод, объявление которого не указывает, как это реализуется, должен быть объявлен abstract
. Метод может быть объявлен final
(§8.4.3.3), когда это не может быть скрыто или переопределено. Метод может быть реализован зависимым от платформы native
код (§8.4.3.4). A synchronized
метод (§8.4.3.5) автоматически блокирует объект прежде, чем выполнить его тело и автоматически разблокировал объект по возврату, как будто при помощи a synchronized
оператор (§14.17), таким образом позволяя его действия синхронизироваться с таковыми из других потоков (§17).
Имена методов могут быть перегружены (§8.4.7).
Статические инициализаторы (§8.5) являются блоками исполняемого кода, который может использоваться, чтобы помочь инициализировать класс, когда он сначала загружается (§12.4).
Конструкторы (§8.6) подобны методам, но не могут быть вызваны непосредственно вызовом метода; они используются, чтобы инициализировать новые экземпляры класса. Как методы, они могут быть перегружены (§8.6.6).
ClassDeclaration:Если класс объявляется в именованном пакете (§7.4.1) с полностью определенным именем P (§6.7), то у класса есть полностью определенное имя P
ClassModifiersoptclass
IdentifierSuperopt
Interfacesopt
ClassBody
.
Идентификатор. Если класс находится в неназванном пакете (§7.4.2), то у класса есть полностью определенный Идентификатор имени. В примере: class Point { int x, y; }класс
Point
объявляется в единице компиляции без package
оператор, и таким образом Point
его полностью определенное имя, тогда как в примере: package vista; class Point { int x, y; }полностью определенное имя класса
Point
vista.Point
. (Имя пакета vista
является подходящим для локального или персонального использования; если бы пакет был предназначен, чтобы быть широко распределенным, то было бы лучше дать этому уникальное имя пакета (§7.7).) Ошибка времени компиляции происходит, если Идентификатор, называя класс появляется как имя какого-либо другого типа класса или интерфейсного типа, объявленного в том же самом пакете (§7.6).
Ошибка времени компиляции происходит, если Идентификатор, называя класс также объявляется как тип объявлением единственного импорта типа (§7.5.1) в единице компиляции (§7.3) содержащий объявление класса.
package test;первая ошибка времени компиляции вызывается двойным объявлением имени
import java.util.Vector;
class Point { int x, y; }
interface Point { // compile-time error #1 int getR(); int getTheta(); }
class Vector { Point[] pts; } // compile-time error #2
Point
как оба a class
и interface
в том же самом пакете. Вторая ошибка, обнаруженная во время компиляции, является попыткой объявить имя Vector
и объявлением типа класса и объявлением единственного импорта типа. Отметьте, однако, что это не ошибка для Идентификатора, который называет класс также, чтобы назвать тип, который иначе мог бы быть импортирован объявлением "импорт типа по требованию" (§7.5.2) в единице компиляции (§7.3) содержащий объявление класса. В примере:
package test;объявление класса
import java.util.*;
class Vector { Point[] pts; } // not a compile-time error
Vector
разрешается даже при том, что есть также класс java.util.Vector
. В пределах этой единицы компиляции, простого имени Vector
обращается к классу test.Vector
, не к java.util.Vector
(который может все еще быть упомянут кодом в пределах единицы компиляции, но только ее полностью определенным именем).
package points;
class Point { int x, y; // coordinates PointColor color; // color of this point Point next; // next point with this colorопределяет два класса, которые используют друг друга в объявлениях их элементов класса. Поскольку тип класса называет
static int nPoints; }
class PointColor { Point first; // first point with this color PointColor(int color) { this.color = color; } private int color; // color components }
Point
и PointColor
имейте весь пакет points
, включая всю текущую единицу компиляции, как их контекст, этот пример компиляции правильно - то есть, ссылка вперед не является проблемой. ClassModifiers:Модификатор доступа
ClassModifier
ClassModifiersClassModifier ClassModifier: one of
public
abstract final
public
обсуждается в §6.6. Ошибка времени компиляции происходит, если тот же самый модификатор появляется не раз в объявлении класса. Если два или больше модификатора класса появляются в объявлении класса, то это общепринято, хотя не требуемый, что они появляются в порядке, непротиворечивом с показанным выше в производстве для ClassModifier. abstract
класс является классом, который является неполным, или считаться неполным. Только abstract
классы могут иметь abstract
методы (§8.4.3.1, §9.4), то есть, методы, которые объявляются, но еще не реализуются. Если класс, который не является abstract
содержит abstract
метод, затем ошибка времени компиляции происходит. Класс имеет abstract
методы, если какое-либо следующее является истиной: abstract
метод (§8.4.3).
abstract
метод от его прямого суперкласса (§8.1.3).
abstract
) и класс ни не объявляет, ни наследовал метод, который реализует его. abstract class Point { int x = 1, y = 1; void move(int dx, int dy) { x += dx; y += dy; alert(); } abstract void alert(); }класс
abstract class ColoredPoint extends Point { int color; }
class SimplePoint extends Point { void alert() { } }
Point
объявляется, который должен быть объявлен abstract
, потому что это содержит объявление abstract
метод называют alert
. Подкласс Point
именованный ColoredPoint
наследовался abstract
метод alert
, таким образом, это должно также быть объявлено abstract
. С другой стороны, подкласс Point
именованный SimplePoint
обеспечивает реализацию alert
, таким образом, это не должно быть abstract
. Ошибка времени компиляции происходит, если попытка предпринимается, чтобы создать экземпляр abstract
класс используя выражение создания экземпляра класса (§15.8). Попытка инстанцировать abstract
класс используя newInstance
метод класса Class
(§20.3.6) вызовет InstantiationException
(§11.5.1), который будет брошен. Таким образом, продолжая пример, только показанный, оператор:
Point p = new Point();привел бы к ошибке времени компиляции; класс
Point
не может быть инстанцирован, потому что это abstract
. Однако, a Point
переменная могла правильно быть инициализирована со ссылкой на любой подкласс Point
, и класс SimplePoint
не abstract
, так оператор: Point p = new SimplePoint();было бы корректно.
Подкласс abstract
класс, который не является самостоятельно abstract
может быть инстанцирован, приводя к выполнению конструктора для abstract
класс и, поэтому, выполнение полевых инициализаторов например переменные того класса. Таким образом, в примере, только данном, инстанцирование a SimplePoint
вызывает конструктора по умолчанию и полевые инициализаторы для x
и y
из Point
выполняться.
Это - ошибка времени компиляции, чтобы объявить abstract
тип класса так, что, не возможно создать подкласс, который реализует весь abstract
методы. Эта ситуация может произойти, если класс имел бы как элементы два abstract
методы, у которых есть та же самая сигнатура метода (§8.4.2), но различные типы возврата. Как пример, объявления:
interface Colorable { void setColor(int color); } abstract class Colored implements Colorable { abstract int setColor(int color); }результат в ошибке времени компиляции: это было бы невозможно для любого подкласса класса
Colored
обеспечить реализацию названного метода setColor
, взятие одного параметра типа int
, это может удовлетворить обоих abstract
спецификации метода, потому что тот в интерфейсе Colorable
требует, чтобы тот же самый метод не возвратил значения, в то время как тот в классе Colored
требует, чтобы тот же самый метод возвратил значение типа int
(§8.4). Тип класса должен быть объявлен abstract
только если намерение состоит в том, что подклассы могут быть созданы, чтобы завершить реализацию. Если намерение состоит в том, чтобы просто предотвратить инстанцирование класса, надлежащий способ выразить, это должно объявить конструктора (§8.6.8) никаких параметров, сделать это private
, никогда не вызывайте это, и не объявляйте никаких других конструкторов. Класс этой формы обычно содержит методы класса и переменные. Класс java.lang.Math
пример класса, который нельзя инстанцировать; его объявление похоже на это:
public final class Math {
private Math() { } // never instantiate this class
. . . declarations of class variables and methods . . .
}
final
если его определение полно, и никакие подклассы не требуются или требуются. Ошибка времени компиляции происходит если имя a final
класс появляется в extends
пункт (§8.1.3) другого class
объявление; это подразумевает это a final
у класса не может быть никаких подклассов. Ошибка времени компиляции происходит, если класс объявляется обоими final
и abstract
, потому что реализация такого класса никогда не могла завершаться (§8.1.2.1). Поскольку a final
у класса никогда нет подклассов, методов a final
класс никогда не переопределяется (§8.4.6.1).
extends
пункт в объявлении класса определяет прямой суперкласс текущего класса. Класс, как говорят, является прямым подклассом класса, который он расширяет. Прямой суперкласс является классом, из реализации которого получается реализация текущего класса. extends
пункт не должен появиться в определении класса java.lang.Object
(§20.1), потому что это - исконный класс и не имеет никакого прямого суперкласса. Если объявление класса для какого-либо другого класса имеет нет extends
пункт, тогда у класса есть класс java.lang.Object
как его неявный прямой суперкласс. Super:Следующее повторяется от §4.3, чтобы сделать представление здесь более четким:
extends
ClassType
ClassType:ClassType должен назвать доступный (§6.6) тип класса, или ошибка времени компиляции происходит. Все классы в текущем пакете доступны. Классы в других пакетах доступны, если хост-система разрешает доступ к пакету (§7.2), и класс объявляется
TypeName
public
. Если указанный ClassType называет класс, который является final
(§8.1.2.2), затем ошибка времени компиляции происходит; final
классам не позволяют иметь подклассы.
отношения следующие:
class Point { int x, y; }
final class ColoredPoint extends Point { int color; }
class Colored3DPoint extends ColoredPoint { int z; } // error
Point
прямой подкласс java.lang.Object
.
java.lang.Object
прямой суперкласс класса Point
.
ColoredPoint
прямой подкласс класса Point
.
Point
прямой суперкласс класса ColoredPoint
. Colored3dPoint
вызывает ошибку времени компиляции, потому что она пытается расшириться final
класс ColoredPoint
. Отношение подкласса является переходным закрытием прямого отношения подкласса. Класс A является подклассом класса C, если любое из следующего является истиной:
отношения следующие:
class Point { int x, y; }
class ColoredPoint extends Point { int color; }
final class Colored3dPoint extends ColoredPoint { int z; }
Point
суперкласс класса ColoredPoint
.
Point
суперкласс класса Colored3dPoint
.
ColoredPoint
подкласс класса Point
.
ColoredPoint
суперкласс класса Colored3dPoint
.
Colored3dPoint
подкласс класса ColoredPoint
.
Colored3dPoint
подкласс класса Point
. вызывает ошибку времени компиляции. Если циркулярные объявленные классы обнаруживаются во время выполнения, поскольку классы загружаются (§12.2), то a
class Point extends ColoredPoint { int x, y; }
class ColoredPoint extends Point { int color; }
ClassCircularityError
бросается. implements
пункт в объявлении класса перечисляет имена интерфейсов, которые являются прямыми суперинтерфейсами объявляемого класса: Interfaces:Следующее повторяется от §4.3, чтобы сделать представление здесь более четким:
implements
InterfaceTypeList InterfaceTypeList:
InterfaceType
InterfaceTypeList,
InterfaceType
InterfaceType:Каждый InterfaceType должен назвать доступное (§6.6) интерфейсный тип, или ошибка времени компиляции - происходит. Все интерфейсы в текущем пакете доступны. Интерфейсы в других пакетах доступны, если хост-система разрешает доступ к пакету (§7.4.4), и интерфейс объявляется
TypeName
public
. Ошибка времени компиляции происходит, если тот же самый интерфейс упоминается два или больше раза в сингле implements
пункт, даже если интерфейс называют по-разному; например, код:
class Redundant implements java.lang.Cloneable, Cloneable { int x; }результаты в ошибке времени компиляции, потому что имена
java.lang.Cloneable
и Cloneable
обратитесь к тому же самому интерфейсу. Интерфейсный тип я - суперинтерфейс типа класса C, если какое-либо следующее является истиной:
public interface Colorable { void setColor(int color); int getColor(); }отношения следующие:
public interface Paintable extends Colorable { int MATTE = 0, GLOSSY = 1; void setFinish(int finish); int getFinish(); }
class Point { int x, y; }
class ColoredPoint extends Point implements Colorable { int color; public void setColor(int color) { this.color = color; } public int getColor() { return color; } }
class PaintedPoint extends ColoredPoint implements Paintable
{ int finish; public void setFinish(int finish) { this.finish = finish; } public int getFinish() { return finish; } }
Paintable
суперинтерфейс класса PaintedPoint
.
Colorable
суперинтерфейс класса ColoredPoint
и класса PaintedPoint
.
Paintable
подынтерфейс интерфейса Colorable
, и Colorable
суперинтерфейс Paintable
, a
s определенный в §9.1.3. PaintedPoint
имеет Colorable
как суперинтерфейс оба, потому что это - суперинтерфейс ColoredPoint
и потому что это - суперинтерфейс Paintable
. Если объявляемый класс не abstract
, объявления методов, определенных в каждом прямом суперинтерфейсе, должны быть реализованы или объявлением в этом классе или существующим объявлением метода, наследованным от прямого суперкласса, потому что класс, который не является abstract
не разрешается иметь abstract
методы (§8.1.2.1).
interface Colorable { void setColor(int color); int getColor(); }вызывает ошибку времени компиляции, потому что
class Point { int x, y; };
class ColoredPoint extends Point implements Colorable { int color; }
ColoredPoint
не abstract
класс, но это не в состоянии обеспечить реализацию методов setColor
и getColor
из интерфейса Colorable
. Разрешается для единственного объявления метода в классе реализовать методы больше чем одного суперинтерфейса. Например, в коде:
interface Fish { int getNumberOfScales(); }
interface Piano { int getNumberOfScales(); }
class Tuna implements Fish, Piano { // You can tune a piano, but can you tuna fish? int getNumberOfScales() { return 91; } }метод
getNumberOfScales
в классе Tuna
имеет имя, подпись, и тип возврата, который соответствует метод, объявленный в интерфейсе Fish
и также соответствует метод, объявленный в интерфейсе Piano
; это, как полагают, реализует обоих. С другой стороны, в ситуации, такой как это:
interface Fish { int getNumberOfScales(); }
interface StringBass { double getNumberOfScales(); }
class Bass implements Fish, StringBass { // This declaration cannot be correct, no matter what type is used. public ??? getNumberOfScales() { return 91; } }невозможно объявить названный метод
getNumberOfScales
с той же самой подписью и возвратом вводят как таковые из обоих методы, объявленные в интерфейсе Fish
и в интерфейсе StringBass
, потому что у класса может быть только один метод с данной подписью (§8.4). Поэтому, для единого класса невозможно реализовать оба интерфейса Fish
и интерфейс StringBass
(§8.4.6). ClassBody:Контекст имени элемента, объявленного в или наследованный типом класса, является всем телом объявления типа класса.
{
ClassBodyDeclarationsopt}
ClassBodyDeclarations:
ClassBodyDeclaration
ClassBodyDeclarationsClassBodyDeclaration ClassBodyDeclaration:
ClassMemberDeclaration
StaticInitializer
ConstructorDeclaration ClassMemberDeclaration:
FieldDeclaration
MethodDeclaration
Object
, у которого нет никакого прямого суперкласса
private
не наследованы подклассами того класса. Только элементы класса, которые объявляются protected
или public
наследованы подклассами, объявленными в пакете кроме того, в котором объявляется класс.Конструкторы и статические инициализаторы не являются элементами и поэтому не наследованы.
class Point { int x, y; private Point() { reset(); } Point(int x, int y) { this.x = x; this.y = y; } private void reset() { this.x = 0; this.y = 0; } }причины четыре ошибки времени компиляции:
class ColoredPoint extends Point { int color; void clear() { reset(); } // error }
class Test { public static void main(String[] args) { ColoredPoint c = new ColoredPoint(0, 0); // error c.reset(); // error } }
ColoredPoint
не имеет никакого конструктора, объявленного с двумя целочисленными параметрами, согласно просьбе использованием в main
. Это иллюстрирует факт это ColoredPoint
не наследовал конструкторов его суперкласса Point
.
ColoredPoint
не объявляет конструкторов, и поэтому конструктор по умолчанию для этого автоматически создается (§8.6.7), и этот конструктор по умолчанию эквивалентен: ColoredPoint () {супер ();} ColoredPoint
. Ошибка состоит в том что конструктор для Point
это не берет параметров, private
, и поэтому не доступно вне класса Point
, даже через вызов конструктора суперкласса (§8.6.5). reset
из класса Point
private
, и поэтому не наследован классом ColoredPoint
. Вызовы метода в методе clear
из класса ColoredPoint
и в методе main
из класса Test
поэтому не корректны. points
пакет объявляет две единицы компиляции:
package points;
public class Point { int x, y;и:
public void move(int dx, int dy) { x += dx; y += dy; } }
package points;
public class Point3d extends Point { int z; public void move(int dx, int dy, int dz) { x += dx; y += dy; z += dz; } }и третья единица компиляции, в другом пакете:
import points.Point3d;
class Point4d extends Point3d { int w; public void move(int dx, int dy, int dz, int dw) { x += dx; y += dy; z += dz; w += dw; // compile-time errors } }Здесь оба класса в
points
компиляция пакета. Класс Point3d
наследовал поля x
и y
из класса Point
, потому что это находится в том же самом пакете как Point
. Класс Point4d
, то, который находится в различном пакете, не наследовало поля x
и y
из класса Point
или поле z
из класса Point3d
, и так не в состоянии скомпилировать. Лучший способ записать третью единицу компиляции был бы:
import points.Point3d;
class Point4d extends Point3d { int w; public void move(int dx, int dy, int dz, int dw) { super.move(dx, dy, dz); w += dw; } }использование
move
метод суперкласса Point3d
обработать dx
, dy
, и dz
. Если Point4d
пишется таким образом, это скомпилирует без ошибок. Point
:
package points;
public class Point {
public int x, y;
protected int useCount = 0;
static protected int totalUseCount = 0;
public void move(int dx, int dy) { x += dx; y += dy; useCount++; totalUseCount++; }
}
public
и protected
поля x
, y
, useCount
и totalUseCount
наследованы во всех подклассах Point
. Поэтому, эта тестовая программа, в другом пакете, может быть скомпилирована успешно: class Test extends points.Point { public void moveBack(int dx, int dy) { x -= dx; y -= dy; useCount++; totalUseCount++; } }
class Point {переменная класса totalMoves может использоваться только в пределах класса
int x, y;
void move(int dx, int dy) { x += dx; y += dy; totalMoves++; }
private static int totalMoves;
void printMoves() { System.out.println(totalMoves); }
}
class Point3d extends Point {
int z;
void move(int dx, int dy, int dz) { super.move(dx, dy); z += dz; totalMoves++; }
}
Point
; это не наследовано подклассом Point3d
. Ошибка времени компиляции происходит в точке где метод move
из класса Point3d
попытки постепенно увеличить totalMoves. public
, экземпляры класса могли бы быть доступными во время выполнения, чтобы кодировать вне пакета, в котором объявляется, есть ли у этого a public
суперкласс или суперинтерфейс. Экземпляр класса может быть присвоен переменной такого public
ввести. Вызов a public
метод объекта, упомянутого такой переменной, может вызвать метод класса, если это реализует или переопределяет метод public
суперкласс или суперинтерфейс. (В этой ситуации обязательно объявляется метод public
, даже при том, что это объявляется в классе, который не является public
.) Рассмотрите единицу компиляции:
package points;
public class Point { public int x, y; public void move(int dx, int dy) { x += dx; y += dy; } }и другая единица компиляции другого пакета:
package morePoints;
class Point3d extends points.Point { public int z; public void move(int dx, int dy, int dz) { super.move(dx, dy); z += dz; } }Вызов
public class OnePoint { static points.Point getOne() { return new Point3d(); } }
morePoints.OnePoint.getOne()
во все же третьем пакете возвратил бы a Point3d
это может использоваться в качестве a Point
, даже при том, что тип Point3d
не доступно вне пакета morePoints
. Метод move
мог тогда быть вызван для того объекта, который допустим потому что метод move
из Point3d
public
(поскольку это должно быть для любого метода, который переопределяет a public
метод должен самостоятельно быть public
, точно так, чтобы ситуации, такие как это удадутся правильно). Поля x
и y
из того объекта мог также быть получен доступ от такого третьего пакета. В то время как поле z
из класса Point3d
public
, не возможно получить доступ к этому полю от кода вне пакета morePoints
, учитывая только ссылку на экземпляр класса Point3d
в переменной p
из типа Point
. Это то, потому что выражение p.z
не корректно, как p
имеет тип Point
и класс Point
не имеет никакого названного поля z
; также, выражение ((Point3d)p).z
не корректно, потому что тип класса Point3d
не может быть отнесен во внешний пакет morePoints
. Объявление поля z
как public
не бесполезно, как бы то ни было. Если должно было быть в пакете morePoints
, a public
подкласс Point4d
из класса Point3d
:
package morePoints;
public class Point4d extends Point3d { public int w; public void move(int dx, int dy, int dz, int dw) { super.move(dx, dy, dz); w += dw; } }тогда класс
Point4d
наследовал бы поле z
, который, будучи public
, мог тогда быть получен доступ кодом в пакетах кроме morePoints
, через переменные и выражения public
ввести Point4d
. FieldDeclaration:FieldModifiers описываются в §8.3.1. Идентификатор в FieldDeclarator может использоваться на имя, чтобы обратиться к полю. Имя поля имеет как его контекст (§6.3) все тело объявления класса, в котором это объявляется. Больше чем одно поле может быть объявлено в единственном полевом объявлении при использовании больше чем одного оператора объявления; FieldModifiers и Тип применяются ко всем операторам объявления в объявлении. Объявления переменной, включающие типы массива, обсуждаются в §10.2.
FieldModifiersoptType
VariableDeclarators
;
VariableDeclarators:
VariableDeclarator
VariableDeclarators,
VariableDeclarator VariableDeclarator:
VariableDeclaratorId
VariableDeclaratorId=
VariableInitializer VariableDeclaratorId:
Identifier
VariableDeclaratorId[ ]
VariableInitializer:
Expression
ArrayInitializer
Это - ошибка времени компиляции для тела объявления класса, чтобы содержать объявления двух полей с тем же самым именем. У методов и полей может быть то же самое имя, так как они используются в различных контекстах и снимаются неоднозначность различными процедурами поиска (§6.5).
Если класс объявляет поле с определенным именем, то объявление того поля, как говорят, скрывает (§6.3.1) любого и все доступные объявления полей с тем же самым именем в суперклассах и суперинтерфейсах класса.
Если полевое объявление скрывает объявление другого поля, у этих двух полей не должно быть того же самого типа.
Класс наследовал от его прямого суперкласса и прямых суперинтерфейсов все поля суперкласса и суперинтерфейсов, которые и доступны, чтобы кодировать в классе и не скрытые объявлением в классе.
Для класса возможно наследовать больше чем одно поле с тем же самым именем (§8.3.3.3). Такая ситуация сам по себе не вызывает ошибку времени компиляции. Однако, любая попытка в пределах тела класса, чтобы обратиться к любому такому полю его простым именем приведет к ошибке времени компиляции, потому что такая ссылка неоднозначна.
Могло бы быть несколько путей, которыми то же самое полевое объявление могло бы быть наследовано от интерфейса. В такой ситуации поле, как полагают, наследовано только однажды, и это может быть упомянуто его простым именем без неоднозначности.
К скрытому полю можно получить доступ при использовании полностью определенного имени (если это static
) или при использовании выражения доступа к полю (§15.10), который содержит ключевое слово super
или бросок к супертипу класса. См. §15.10.2 для обсуждения и примера.
FieldModifiers:Модификаторы доступа
FieldModifier
FieldModifiersFieldModifier FieldModifier: one of
public protected private
final static transient volatile
public
, protected
, и private
обсуждаются в §6.6. Ошибка времени компиляции происходит, если тот же самый модификатор появляется не раз в полевом объявлении, или если у полевого объявления есть больше чем один из модификаторов доступа public
, protected
, и private
. Если два или больше (отличных) полевых модификатора появляются в полевом объявлении, это общепринято, хотя не требуемый, что они появляются в порядке, непротиворечивом с показанным выше в производстве для FieldModifier. static
, там существует точно одно воплощение поля, независимо от того сколько экземпляров (возможно нуль) класса может в конечном счете быть создано. A static
поле, иногда называемое переменной класса, воплощается, когда класс инициализируется (§12.4). Поле, которое не объявляется static
(иногда вызываемый не -static
поле), вызывается переменной экземпляра. Всякий раз, когда новый экземпляр класса создается, новая переменная, связанная с тем экземпляром, создается для каждой переменной экземпляра, объявленной в том классе или любом из его суперклассов.
class Point { int x, y, useCount; Point(int x, int y) { this.x = x; this.y = y; } final static Point origin = new Point(0, 0); }печатные издания:
class Test { public static void main(String[] args) { Point p = new Point(1,1); Point q = new Point(2,2); p.x = 3; p.y = 3; p.useCount++; p.origin.useCount++; System.out.println("(" + q.x + "," + q.y + ")"); System.out.println(q.useCount); System.out.println(q.origin == Point.origin); System.out.println(q.origin.useCount); } }
(2,2) 0 true 1показ того изменения полей
x
, y
, и useCount
из p
не влияет на поля q
, потому что эти поля являются переменными экземпляра в отличных объектах. В этом примере, переменной класса origin
из класса Point
ссылается оба использования имени класса как спецификатор, в Point.origin
, и использование переменных типа класса в выражениях доступа к полю (§15.10), как в p.origin
и q.origin
. Эти два способа получить доступ origin
переменная класса получает доступ к тому же самому объекту, свидетельствуемому фактом что значение ссылочного выражения равенства (§15.20.3): q.origin==Point.origin
true
. Новые доказательства то, что приращение: p.origin.useCount++;заставляет значение q.origin.useCount быть
1
; это так потому что p.origin
и q.origin
обратитесь к той же самой переменной. final
, когда его оператор объявления должен включать переменный инициализатор, или ошибка времени компиляции происходит. И класс и переменные экземпляра (static
и не -static
поля), может быть объявлен final
. Любая попытка присвоиться к a final
поле приводит к ошибке времени компиляции. Поэтому, однажды a final
поле было инициализировано, оно всегда содержит то же самое значение. Если a final
поле содержит ссылку на объект, тогда состояние объекта может быть изменено операциями на объекте, но поле будет всегда обращаться к тому же самому объекту. Это применяется также массивам, потому что массивы являются объектами; если a final
поле содержит ссылку на массив, тогда компоненты массива могут быть изменены операциями на массиве, но поле будет всегда обращаться к тому же самому массиву.
Объявление поля final
может служить полезной документацией, которую ее значение не будет изменять, может помочь избежать программировать ошибки, и может облегчить для компилятора генерировать эффективный код.
class Point { int x, y; int useCount; Point(int x, int y) { this.x = x; this.y = y; } final static Point origin = new Point(0, 0); }класс
Point
объявляет a final
переменная класса origin
. origin
переменная содержит ссылку на объект, который является экземпляром класса Point
чьи координаты (0, 0). Значение переменной Point.origin
никогда не может изменяться, таким образом, это всегда обращается к тому же самому Point
объект, тот создается его инициализатором. Однако, работа на этом Point
объект мог бы изменить свое состояние например, изменяя его useCount
или даже, обманчиво, x
или y
координата. transient
указать, что они не часть постоянного состояния объекта. Если экземпляр класса Point
: class Point { int x, y; transient float rho, theta; }были сохранены к персистентному хранению системной службой, тогда только поля
x
и y
был бы сохранен. Эта спецификация еще не определяет детали таких служб; мы намереваемся обеспечить их в будущей версии этой спецификации. Java обеспечивает второй механизм, который более удобен в некоторых целях: поле может быть объявлено volatile
, когда поток должен согласовать свою рабочую копию поля с основной копией каждый раз, когда это получает доступ к переменной. Кроме того операции на основных копиях одной или более энергозависимых переменных от имени потока выполняются основной памятью в точно порядке что поток, который требуют.
Если в следующем примере один поток неоднократно вызывает метод one
(но не больше, чем Integer.MAX_VALUE
времена (§20.7.2) всего), и другой поток неоднократно вызывает метод two
:
class Test {тогда метод
static int i = 0, j = 0;
static void one() { i++; j++; } static void two() { System.out.println("i=" + i + " j=" + j); }
}
two
мог иногда печатать значение для j
это больше чем значение i
, потому что пример не включает синхронизации и, по правилам, объясненным в §17, совместно используемых значениях i
и j
мог бы быть обновлен не в порядке. Один способ предотвратить это поведение "или порядок" состоял бы в том, чтобы объявить методы one
и two
быть synchronized
(§8.4.3.5):
class Test {Это предотвращает метод
static int i = 0, j = 0;
static synchronized void one() { i++; j++; } static synchronized void two() { System.out.println("i=" + i + " j=" + j); }
}
one
и метод two
от того, чтобы быть выполняемым одновременно, и кроме того гарантирует что совместно используемые значения i
и j
оба обновляются перед методом one
возвраты. Поэтому метод two
никогда не наблюдает значение для j
больше чем это для i
; действительно, это всегда наблюдает то же самое значение для i
и j
. Другой подход должен был бы объявить i
и j
быть volatile
:
class Test {Это позволяет метод
static volatile int i = 0, j = 0;
static void one() { i++; j++; } static void two() { System.out.println("i=" + i + " j=" + j); }
}
one
и метод two
выполняться одновременно, но гарантии что доступы к совместно используемым значениям для i
и j
происходите точно так много раз, и в точно том же самом порядке, как они, кажется, происходят во время выполнения текста программы каждым потоком. Поэтому, метод two
никогда не наблюдает значение для j
больше чем это для i
, потому что каждое обновление к i
должен быть отражен в совместно используемом значении для i
перед обновлением к j
происходит. Возможно, однако, что любой данный вызов метода two
мог бы наблюдать значение для j
это намного больше чем значение, наблюдаемое для i
, потому что метод one
мог бы быть выполнен много раз между моментом когда метод two
выбирает значение i
и момент, когда метод two
выбирает значение j
. См. §17 для большего количества обсуждения и примеров.
Ошибка времени компиляции происходит если a final
переменная также объявляется volatile
.
static
поле), тогда переменный инициализатор оценивается и присвоение, выполняемое точно однажды, когда класс инициализируется (§12.4).
static
), тогда переменный инициализатор оценивается и присвоение, выполняемое каждый раз, когда экземпляр класса создается (§12.5).
class Point { int x = 1, y = 5; }производит вывод:
class Test { public static void main(String[] args) { Point p = new Point(); System.out.println(p.x + ", " + p.y); } }
1, 5потому что присвоения на
x
и y
происходите всякий раз, когда новое Point
создается. Переменные инициализаторы также используются в операторах объявления локальной переменной (§14.3), где инициализатор оценивается и присвоение, выполняемое каждый раз, когда оператор объявления локальной переменной выполняется.
Это - ошибка времени компиляции, если оценка переменного инициализатора для поля класса (или интерфейс) может завершиться резко с проверенным исключением (§11.2).
class Test { static float f = j; // compile-time error: forward reference static int j = 1; static int k = k+1; // compile-time error: forward reference }причины две ошибки времени компиляции, потому что
j
упоминается в инициализации f
прежде j
объявляется и потому что инициализация k
обращается к k
непосредственно. Если ссылка простым именем к какой-либо переменной экземпляра происходит в выражении инициализации для переменной класса, то ошибка времени компиляции происходит.
Если ключевое слово this
(§15.7.2) или ключевое слово super
(§15.10.2, §15.11), происходит в выражении инициализации для переменной класса, затем ошибка времени компиляции происходит.
(Одна тонкость вот то, что во время выполнения, static
переменные, которые являются final
и это инициализируется с постоянными величинами времени компиляции, инициализируются сначала. Это также применяется к таким полям в интерфейсах (§9.3.1). Эти переменные являются "константами", у которых, как никогда будут наблюдать, не будет их начальных значений по умолчанию (§4.5.4), даже окольными программами. См. §12.4.2 и §13.4.8 для большего количества обсуждения.)
class Test { float f = j; int j = 1; int k = k+1; }причины две ошибки времени компиляции, потому что
j
упоминается в инициализации f
прежде j
объявляется и потому что инициализация k
обращается к k
непосредственно. Выражения инициализации например переменные могут использовать простое имя любого static
переменная, объявленная в или наследованный классом, даже тот, объявление которого происходит дословно позже. Таким образом пример:
class Test { float f = j; static int j = 1; }компиляции без ошибки; это инициализирует
j
к 1
когда класс Test
инициализируется, и инициализирует f
к текущей стоимости j
каждый раз экземпляр класса Test
создается. Выражениям инициализации например переменные разрешают обратиться к текущему объекту this
(§15.7.2) и использовать ключевое слово super
(§15.10.2, §15.11).
class Point { static int x = 2; }производит вывод:
class Test extends Point { static double x = 4.7; public static void main(String[] args) {
new Test().printX(); } void printX() { System.out.println(x + " " + super.x); } }
4.7 2потому что объявление
x
в классе Test
скрывает определение x
в классе Point
, так класс Test
не наследовал поле x
от его суперкласса Point
. В пределах объявления класса Test
, простое имя x
обращается к полю, объявленному в пределах класса Test
. Код в классе Test
может обратиться к полю x
из класса Point
как super.x
(или, потому что x
static
, как Point.x
). Если объявление Test.x
удаляется: class Point { static int x = 2; }тогда поле
class Test extends Point { public static void main(String[] args) { new Test().printX(); } void printX() { System.out.println(x + " " + super.x); } }
x
из класса Point
больше не скрывается в пределах класса Test
; вместо этого, простое имя x
теперь обращается к полю Point.x
. Код в классе Test
май все еще обращается к тому же самому полю как super.x
. Поэтому, вывод из этой различной программы: 2 2
class Point { int x = 2; }производит вывод:
class Test extends Point { double x = 4.7; void printBoth() { System.out.println(x + " " + super.x); } public static void main(String[] args) { Test sample = new Test(); sample.printBoth(); System.out.println(sample.x + " " +
((Point)sample).x); } }
4.7 2 4.7 2потому что объявление
x
в классе Test
скрывает определение x
в классе Point
, так класс Test
не наследовал поле x
от его суперкласса Point
. Это должно быть отмечено, однако, это в то время как поле x
из класса Point
не наследован классом Test
, это однако реализуется экземплярами класса Test
. Другими словами, каждый экземпляр класса Test
содержит два поля, один из типа int
и один из типа float
. Оба поля носят имя x
, но в пределах объявления класса Test
, простое имя x
всегда обращается к полю, объявленному в пределах класса Test
. Код в методах экземпляра класса Test
может обратиться к переменной экземпляра x
из класса Point
как super.x
. Код, который использует выражение доступа к полю, чтобы получить доступ к полю x
получит доступ к названному полю x
в классе, обозначенном типом ссылочного выражения. Таким образом, выражение sample.x
доступы a float
значение, переменная экземпляра объявляется в классе Test
, потому что тип переменной выборки Test
, но выражение ((Point)sample).x
получает доступ int
значение, переменная экземпляра объявляется в классе Point
, из-за броска, чтобы ввести Point
.
Если объявление x
удаляется из класса Test
, как в программе:
class Point { static int x = 2; }тогда поле
class Test extends Point { void printBoth() { System.out.println(x + " " + super.x); } public static void main(String[] args) { Test sample = new Test(); sample.printBoth(); System.out.println(sample.x + " " +
((Point)sample).x); } }
x
из класса Point
больше не скрывается в пределах класса Test
. В пределах методов экземпляра в объявлении класса Test
, простое имя x
теперь обращается к полю, объявленному в пределах класса Point
. Код в классе Test
май все еще обращается к тому же самому полю как super.x
. Выражение sample.x
все еще обращается к полю x
в пределах типа Test
, но то поле является теперь наследованным полем, и так обращается к полю x
объявленный в классе Point
. Вывод из этой различной программы: 2 2 2 2
super
(§15.10.2) может использоваться, чтобы получить доступ к таким полям однозначно. В примере:
interface Frob { float v = 2.0f; }
class SuperTest { int v = 3; }
class Test extends SuperTest implements Frob { public static void main(String[] args) { new Test().printV(); } void printV() { System.out.println(v); } }класс
Test
наследовал два названные поля v
, один от его суперкласса SuperTest
и один от его суперинтерфейса Frob
. Это сам по себе разрешается, но ошибка времени компиляции происходит из-за использования простого имени v
в методе printV
: это не может быть определено который v
предназначается. Следующее изменение использует выражение доступа к полю super.v
обратиться к названному полю v
объявленный в классе SuperTest
и использует полностью определенное имя Frob.v
обратиться к названному полю v
объявленный в интерфейсе Frob
:
interface Frob { float v = 2.0f; }
class SuperTest { int v = 3; }
class Test extends SuperTest implements Frob { public static void main(String[] args) { new Test().printV(); } void printV() { System.out.println((super.v + Frob.v)/2); } }Это компилирует и печатает:
2.5Даже если два отличных наследованных поля имеют тот же самый тип, то же самое значение, и являются обоими
final
, любую ссылку на любое поле простым именем считают неоднозначной и приводит к ошибке времени компиляции. В примере:
interface Color { int RED=0, GREEN=1, BLUE=2; }
interface TrafficLight { int RED=0, YELLOW=1, GREEN=2; }
class Test implements Color, TrafficLight { public static void main(String[] args) { System.out.println(GREEN); // compile-time error System.out.println(RED); // compile-time error } }не удивительно что ссылка на
GREEN
должен считаться неоднозначным, потому что класс Test
наследовал два различных объявления для GREEN
с различными значениями. Точка этого примера то, что ссылка на RED
также считается неоднозначным, потому что наследованы два отличных объявления. Факт, что эти два поля называют RED
окажись, иметь тот же самый тип, и то же самое неизменное значение не влияет на это суждение. public interface Colorable { int RED = 0xff0000, GREEN = 0x00ff00, BLUE = 0x0000ff; }поля
public interface Paintable extends Colorable { int MATTE = 0, GLOSSY = 1; }
class Point { int x, y; }
class ColoredPoint extends Point implements Colorable { . . . }
class PaintedPoint extends ColoredPoint implements Paintable { . . .RED
. . . }
RED
, GREEN
, и BLUE
наследованы классом PaintedPoint
оба через его прямой суперкласс ColoredPoint
и через его прямой суперинтерфейс Paintable
. Простые имена RED
, GREEN
, и BLUE
май однако использоваться без неоднозначности в пределах класса PaintedPoint
обратиться к полям, объявленным в интерфейсе Colorable
. MethodDeclaration:MethodModifiers описываются в §8.4.3, пункте Бросков в §8.4.4, и MethodBody в §8.4.5. Объявление метода или определяет тип имеющий значение, что метод возвращает или использует ключевое слово
MethodHeaderMethodBody MethodHeader:
MethodModifiersoptResultType
MethodDeclarator
Throwsopt ResultType:
Type
void
MethodDeclarator:
Identifer(
FormalParameterListopt)
void
указать, что метод не возвращает значение. Идентификатор в MethodDeclarator может использоваться на имя, чтобы обратиться к методу. Класс может объявить метод с тем же самым именем как класс или поле класса.
Для совместимости с более старыми версиями Java форме объявления для метода, который возвращает массив, позволяют поместить (некоторые или весь из) пустых пар скобки, которые формируют объявление типа массива после списка параметров. Это поддерживается устаревающим производством:
MethodDeclarator:но не должен использоваться в новом коде Java.
MethodDeclarator[ ]
Это - ошибка времени компиляции для тела класса, чтобы иметь как элементы два метода с той же самой подписью (§8.4.2) (имя, число параметров, и типы любых параметров). У методов и полей может быть то же самое имя, так как они используются в различных контекстах и снимаются неоднозначность различными процедурами поиска (§6.5).
FormalParameterList:Следующее повторяется от §8.3, чтобы сделать представление здесь более четким:
FormalParameter
FormalParameterList,
FormalParameter FormalParameter:
TypeVariableDeclaratorId
VariableDeclaratorId:Если у метода нет никаких параметров, только пустая пара круглых скобок появляется в объявлении метода.
Identifier
VariableDeclaratorId[ ]
Если у двух формальных параметров, как объявляют, есть то же самое имя (то есть, их объявления упоминают тот же самый Идентификатор), то ошибка времени компиляции происходит.
Когда метод вызывается (§15.11), значения фактических выражений параметра инициализируют недавно создаваемые переменные параметра, каждый объявленный Тип, перед выполнением тела метода. Идентификатор, который появляется в DeclaratorId, может использоваться в качестве простого имени в теле метода, чтобы обратиться к формальному параметру.
Контекст имен формального параметра является всем телом метода. Эти названия параметра не могут быть повторно объявлены как локальные переменные или параметры исключения в пределах метода; то есть, сокрытие имени параметра не разрешается.
Формальные параметры не упоминаются только использование простых имен, никогда при использовании полностью определенных имен (§6.6).
class Point implements Move { int x, y; abstract void move(int dx, int dy); void move(int dx, int dy) { x += dx; y += dy; } }вызывает ошибку времени компиляции, потому что она объявляет два
move
методы с той же самой подписью. Это - ошибка даже при том, что одно из объявлений abstract
. MethodModifiers:Модификаторы доступа
MethodModifier
MethodModifiersMethodModifier MethodModifier: one of
public protected private
abstract static final synchronized native
public
, protected
, и private
обсуждаются в §6.6. Ошибка времени компиляции происходит, если тот же самый модификатор появляется не раз в объявлении метода, или если у объявления метода есть больше чем один из модификаторов доступа public
, protected
, и private
. Ошибка времени компиляции происходит, если объявление метода, которое содержит ключевое слово abstract
также содержит любое из ключевых слов private
, static
, final
, native
, или synchronized
. Если два или больше модификатора метода появляются в объявлении метода, это общепринято, хотя не требуемый, что они появляются в порядке, непротиворечивом с показанным выше в производстве для MethodModifier.
abstract
объявление метода представляет метод, поскольку элемент, обеспечивая его подпись (имя и номер и тип параметров), возвращает тип, и throws
пункт (если кто-либо), но не обеспечивает реализацию. Объявление abstract
метод м. должен появиться в пределах abstract
класс (вызывают это A); иначе ошибка времени компиляции заканчивается. Каждый подкласс, который не является abstract
должен обеспечить реализацию для м., или ошибка времени компиляции происходит. Более точно, для каждого подкласса C abstract
класс A, если C не abstract
, тогда должен быть некоторый класс B так, что, все следующее является истиной: abstract
, и это объявление наследовано C, таким образом обеспечивая реализацию метода м., который видим к C. Это - ошибка времени компиляции для a private
метод, который будет объявлен abstract
. Для подкласса было бы невозможно реализовать a private
abstract
метод, потому что private
методы не видимы к подклассам; поэтому такой метод никогда не мог использоваться.
Это - ошибка времени компиляции для a static
метод, который будет объявлен abstract
.
Это - ошибка времени компиляции для a final
метод, который будет объявлен abstract
.
abstract
класс может переопределить abstract
метод, предоставляя другому abstract
объявление метода. Это может обеспечить место, чтобы поместить комментарий для документации (§18), или объявить, что набор проверенных исключений (§11.2), который может быть брошен тем методом, когда это реализуется его подклассами, должен быть более ограничен. Например, рассмотрите этот код:
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; }
get
в классе InfiniteBuffer
состояния тот метод get
в любом подклассе InfiniteBuffer
никогда броски a BufferEmpty
исключение, предполагаемо потому что это генерирует данные в буфере, и таким образом никогда не может исчерпывать данные. Метод экземпляра, который не является abstract
может быть переопределен abstract
метод. Например, мы можем объявить abstract
класс Point
это требует, чтобы его подклассы реализовали toString
если они должны быть полными, instantiable классы:
abstract class Point { int x, y; public abstract String toString(); }Это
abstract
объявление toString
переопределяет не -abstract
toString
метод класса Object
(§20.1.2). (Класс Object
неявный прямой суперкласс класса Point
.) Добавление кода: class ColoredPoint extends Point { int color; public String toString() { return super.toString() + ": color " + color; // error } }результаты в ошибке времени компиляции, потому что вызов
super.toString()
обращается к методу toString
в классе Point
, который является abstract
и поэтому не может быть вызван. Метод toString
из класса Object
может быть сделан доступным для класса ColoredPoint
только если класс Point
явно делает это доступным через некоторый другой метод, как в: 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 } }
static
вызывается методом класса. Метод класса всегда вызывается независимо от определенного объекта. Попытка сослаться на текущий объект, используя ключевое слово this
или ключевое слово super
в теле класса метод приводит к ошибке времени компиляции. Это - ошибка времени компиляции для a static
метод, который будет объявлен abstract
. Метод, который не объявляется static
вызывается методом экземпляра, и иногда вызывается не -static
метод). Метод экземпляра всегда вызывается относительно объекта, который становится текущим объектом к который ключевые слова this
и super
отнеситесь во время выполнения тела метода.
final
препятствовать тому, чтобы подклассы переопределили или скрыли это. Это - ошибка времени компиляции, чтобы попытаться переопределить или скрыть a final
метод. A private
метод и все методы объявляются в a final
класс (§8.1.2.2) неявно final
, потому что невозможно переопределить их. Это разрешается, но не требуется для объявлений таких методов избыточно включать final
ключевое слово.
Это - ошибка времени компиляции для a final
метод, который будет объявлен abstract
.
Во время выполнения генератор машинного кода или оптимизатор могут легко и безопасно "встроить" тело a final
метод, заменяя вызов метода с кодом в его теле, как в примере:
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); } } }
move
из класса Point
в методе main
преобразовал бы for
цикл к форме: 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; }Цикл мог бы тогда подвергнуться дальнейшей оптимизации.
Такое встраивание не может быть сделано во время компиляции, если ему нельзя гарантировать это Test
и Point
будет всегда перекомпилирован вместе, так, чтобы всякий раз, когда Point
- и определенно move
изменения метода, код для Test.main
будет также обновлен.
native
реализуется в зависимом от платформы коде, обычно записанном в другом языке программирования, таком как C, C++, ФОРТРАН, или ассемблер. Тело a native
метод дается как точка с запятой только, указывая, что реализация опускается вместо блока. Ошибка времени компиляции происходит если a native
метод объявляется abstract
.
Например, класс RandomAccessFile
из стандартного пакета java.io
мог бы объявить следующий native
методы:
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; }
synchronized
метод получает блокировку (§17.1) прежде, чем он выполнится. Для класса (static)
метод, блокировка, связанная с Class
объект (§20.3) для класса метода используется. Для метода экземпляра, блокировка, связанная с this
(объект, для которого был вызван метод) используется. Они - те же самые блокировки, которые могут использоваться synchronized
оператор (§14.17); таким образом, код: class Test { int count; synchronized void bump() { count++; } static int classCount; static synchronized void classBump() { classCount++; } }имеет точно тот же самый эффект как:
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 {определяет класс, который разрабатывается для параллельного использования. Каждый экземпляр класса
private Object boxContents;
public synchronized Object get() { Object contents = boxContents; boxContents = null; return contents; }
public synchronized boolean put(Object contents) { if (boxContents != null) return false; boxContents = contents; return true; }
}
Box
имеет переменную экземпляра contents
это может содержать ссылку на любой объект. Можно поместить объект в a Box
вызывая put
, который возвращается false
если поле уже полно. Можно вытащить что-то из a Box
вызывая get
, который возвращает нулевую ссылку если box
пусто. Если put
и get
не были synchronized
, и два потока выполняли методы для того же самого экземпляра Box
одновременно, тогда код мог неправильно себя вести. Это могло бы, например, потерять след объекта потому что два вызова к put
произошедший одновременно.
См. §17 для большего количества обсуждения потоков и блокировок.
Throws:Ошибка времени компиляции происходит если любой ClassType, упомянутый в a
throws
ClassTypeList ClassTypeList:
ClassType
ClassTypeList,
ClassType
throws
пункт не является классом Throwable
(§20.22) или подкласс Throwable
. Это разрешается, но не требуется упоминать другие исключения (непроверенные) в a throws
пункт. Для каждого проверенного исключения, которое может следовать из выполнения тела метода или конструктора, происходит ошибка времени компиляции, если тот тип исключения или суперкласс того типа исключения не упоминаются в a throws
пункт в объявлении метода или конструктора.
Требование, чтобы объявить проверенные исключения позволяет компилятору гарантировать, что код для того, чтобы обработать такие состояния ошибки был включен. Методы или конструкторы, которые не в состоянии обработать исключительные условия, брошенные как проверенные исключения, будут обычно приводить к ошибке времени компиляции из-за нехватки надлежащего исключения, вводят a throws
пункт. Java таким образом поощряет стиль программирования, где редкий и иначе действительно исключительные условия документируются таким образом.
Предопределенные исключения, которые не проверяются таким образом, являются теми, для которых объявление каждого возможного возникновения было бы невообразимо неудобно:
Error
, например OutOfMemoryError
, бросаются из-за отказа в или виртуальной машины. Многие из них являются результатом отказов редактирования и могут произойти в непредсказуемых точках в выполнении программы Java. Сложные программы могут все же хотеть поймать и попытаться восстановиться с некоторых из этих условий.
RuntimeException
, например NullPointerException
, следствие проверок целостности времени выполнения и бросается или непосредственно из программы Java или в библиотечных подпрограммах. Это выходит за рамки языка Java, и возможно вне состояния искусства, чтобы включать достаточную информацию в программу, чтобы уменьшить до управляемого числа места, где они, как могут доказывать, не происходят. abstract
методы, определенные в интерфейсах, как могут объявлять, не выдают более проверенные исключения чем переопределенный или скрытый метод.
Более точно предположите, что B является классом или интерфейсом, и A является суперклассом или суперинтерфейсом B, и объявление метода n в B переопределяет или скрывает объявление метода м. в A. Если у n есть a throws
у пункта, который упоминает любые проверенные типы исключения, тогда м., должен быть a throws
пункт, и для каждого проверенного типа исключения, перечисленного в throws
пункт n, того же самого класса исключений или одного из его суперклассов должен произойти в throws
пункт м.; иначе, ошибка времени компиляции происходит.
См. §11 для получения дополнительной информации об исключениях и большом примере.
abstract
(§8.4.3.1) или native
(§8.4.3.4). MethodBody:Ошибка времени компиляции происходит, если объявление метода также
Block
;
abstract
или native
и имеет блок для его тела. Ошибка времени компиляции происходит, если объявление метода ни один не abstract
ни native
и имеет точку с запятой для ее тела. Если реализация должна быть обеспечена для метода, но реализация не требует никакого исполняемого кода, тело метода должно быть записано как блок, который не содержит операторов:"{ }
".
Если метод объявляется void
, тогда его тело не должно содержать никого return
оператор (§14.15), у которого есть Выражение.
Если у метода, как объявляют, есть тип возврата, то каждый return
у оператора (§14.15) в его теле должно быть Выражение. Ошибка времени компиляции происходит, если тело метода может обычно завершаться (§14.1). Другими словами метод с типом возврата должен возвратиться только при использовании оператора возврата, который обеспечивает возврат значения; не позволяется "понизиться конец своего тела."
Отметьте, что для метода возможно иметь объявленный тип возврата и все же не содержать операторы возврата. Вот один пример:
class DizzyDean {
int pitch() { throw new RuntimeException("90 mph?!"); }
}
abstract
или не) суперкласса и суперинтерфейсов, которые доступны, чтобы кодировать в классе и ни не переопределяются (§8.4.6.1), ни скрываются (§8.4.6.2) объявлением в классе. abstract
, тогда объявление того метода, как говорят, реализует любого и все объявления abstract
методы с той же самой подписью в суперклассах и суперинтерфейсах класса, который иначе был бы доступен, чтобы кодировать в классе. Ошибка времени компиляции происходит, если метод экземпляра переопределяет a static
метод. В этом отношении переопределение методов отличается от сокрытия полей (§8.3), поскольку это допустимо для переменной экземпляра, чтобы скрыть a static
переменная.
К переопределенному методу можно получить доступ при использовании выражения вызова метода (§15.11), который содержит ключевое слово super
. Отметьте, что полностью определенное имя или бросок к супертипу класса не эффективны при попытке получить доступ к переопределенному методу; в этом отношении переопределение методов отличается от сокрытия полей. См. §15.11.4.10 для обсуждения и примеров этой точки.
static
метод, тогда объявление того метода, как говорят, скрывает любого и все методы с той же самой подписью в суперклассах и суперинтерфейсах класса, который иначе был бы доступен, чтобы кодировать в классе. Ошибка времени компиляции происходит если a static
метод скрывает метод экземпляра. В этом отношении сокрытие методов отличается от сокрытия полей (§8.3), поскольку это допустимо для a static
переменная, чтобы скрыть переменную экземпляра. К скрытому методу можно получить доступ при использовании полностью определенного имени или при использовании выражения вызова метода (§15.11), который содержит ключевое слово super
или бросок к супертипу класса. В этом отношении сокрытие методов подобно сокрытию полей.
void
. Кроме того у объявления метода не должно быть a throws
пункт, который конфликтует (§8.4.4) с тем из любого метода, который это переопределяет или скрывает; иначе, ошибка времени компиляции происходит. В этих отношениях переопределение методов отличается от сокрытия полей (§8.3), поскольку это допустимо для поля, чтобы скрыть поле другого типа. Модификатор доступа (§6.6) переопределения или сокрытия метода должен обеспечить, по крайней мере, такой большой доступ как переопределенный или скрытый метод, или ошибка времени компиляции происходит. Более подробно:
public
, тогда переопределение или сокрытие метода должны быть public
; иначе, ошибка времени компиляции происходит.
protected
, тогда переопределение или сокрытие метода должны быть protected
или public
; иначе, ошибка времени компиляции происходит.
private
; иначе, ошибка времени компиляции происходит. private
метод никогда не доступен для подклассов и так не может быть скрыт или переопределен в техническом смысле тех сроков. Это означает, что подкласс может объявить метод с той же самой подписью как a private
метод в одном из его суперклассов, и нет никакого требования что тип возврата или throws
пункт такого метода имеет любое отношение к таковым private
метод в суперклассе. abstract
, тогда есть два подслучая: abstract
static
, ошибка времени компиляции происходит.
abstract
как полагают, переопределяет, и поэтому реализует, все другие методы от имени класса, который наследовал это. Ошибка времени компиляции происходит, если, сравнивая метод, который не является abstract
с каждым из других из наследованных методов, для любой такой пары, у или их есть различные типы возврата, или у каждого есть тип возврата, и другой void
. Кроме того ошибка времени компиляции происходит, если наследованный метод, который не является abstract
имеет a throws
пункт, который конфликтует (§8.4.4) с тем из любых других из наследованных методов. abstract
, тогда класс обязательно abstract
класс и, как полагают, наследовался весь abstract
методы. Ошибка времени компиляции происходит, если для каких-либо двух таких наследованных методов у или их есть различные типы возврата, или у каждого есть тип возврата, и другой void
. ( throws
пункты не вызывают ошибки в этом случае.) abstract
, потому что методы, которые не являются abstract
наследованы только от прямого суперкласса, не от суперинтерфейсов. Могло бы быть несколько путей, которыми то же самое объявление метода могло бы быть наследовано от интерфейса. Этот факт не вызывает трудности и никогда, себя, результатов в ошибке времени компиляции.
throws
пункты двух методов с тем же самым именем, но различными подписями. Методы переопределяются на основе подписи подписью. Если, например, класс объявляет два public
методы с тем же самым именем, и подкласс переопределяют одного из них, подкласс все еще наследовал другой метод. В этом отношении Java отличается от C++.
Когда метод вызывается (§15.11), число фактических параметров и типы времени компиляции параметров используются, во время компиляции, чтобы определить подпись метода, который будет вызван (§15.11.2). Если метод, который должен быть вызван, будет методом экземпляра, то фактический метод, который будет вызван, будет определен во время выполнения, используя динамический поиск метода (§15.11.4).
class Point {класс
int x = 0, y = 0;
void move(int dx, int dy) { x += dx; y += dy; }
}
class SlowPoint extends Point {
int xLimit, yLimit;
void move(int dx, int dy) { super.move(limit(dx, xLimit), limit(dy, yLimit)); }
static int limit(int d, int limit) { return d > limit ? limit : d < -limit ? -limit : d; }
}
SlowPoint
переопределяет объявления метода move
из класса Point
с его собственным move
метод, который ограничивает расстояние, что точка может углубить каждый вызов метода. Когда move
метод вызывается для экземпляра класса SlowPoint
, определение переопределения в классе SlowPoint
будет всегда вызываться, даже если ссылка на SlowPoint
объект берется от переменной, тип которой Point
. class Point {класс
int x = 0, y = 0;
void move(int dx, int dy) { x += dx; y += dy; }
int color;
}
class RealPoint extends Point {
float x = 0.0f, y = 0.0f;
void move(int dx, int dy) { move((float)dx, (float)dy); }
void move(float dx, float dy) { x += dx; y += dy; }
}
RealPoint
скрывает объявления int
переменные экземпляра x
и y
из класса Point
с его собственным float
переменные экземпляра x
и y
, и переопределяет метод move
из класса Point
с его собственным move
метод. Это также перегружает имя move
с другим методом с различной подписью (§8.4.2). В этом примере, элементах класса RealPoint
включайте переменную экземпляра color
наследованный от класса Point
, float
переменные экземпляра x
и y
объявленный в RealPoint
, и два move
методы, объявленные в RealPoint
.
Какой из них перегруженных move
методы класса RealPoint
будет выбран для любого определенного вызова метода, будет определен во время компиляции перегружающейся процедурой разрешения, описанной в §15.11.
class Point {Здесь класс
int x = 0, y = 0, color;
void move(int dx, int dy) { x += dx; y += dy; }
int getX() { return x; }
int getY() { return y; }
}
class RealPoint extends Point {
float x = 0.0f, y = 0.0f;
void move(int dx, int dy) { move((float)dx, (float)dy); }
void move(float dx, float dy) { x += dx; y += dy; }
float getX() { return x; }
float getY() { return y; }
}
Point
обеспечивает методы getX
и getY
тот возврат значения его полей x
и y
; класс RealPoint
тогда переопределения эти методы, объявляя методы с той же самой подписью. Результатом являются две ошибки во время компиляции, один для каждого метода, потому что типы возврата не соответствуют; методы в классе Point
возвращаемые значения типа int
, но методы переопределения подражателя в классе RealPoint
возвращаемые значения типа float
. class Point {Здесь методы переопределения
int x = 0, y = 0;
void move(int dx, int dy) { x += dx; y += dy; }
int getX() { return x; }
int getY() { return y; }
int color;
}
class RealPoint extends Point {
float x = 0.0f, y = 0.0f;
void move(int dx, int dy) { move((float)dx, (float)dy); }
void move(float dx, float dy) { x += dx; y += dy; }
int getX() { return (int)Math.floor(x); }
int getY() { return (int)Math.floor(y); }
}
getX
и getY
в классе RealPoint
имейте те же самые типы возврата как методы класса Point
то, что они переопределяют, таким образом, этот код может быть успешно скомпилирован. Рассмотрите, тогда, эту тестовую программу:
class Test { public static void main(String[] args) { RealPoint rp = new RealPoint(); Point p = rp; rp.move(1.71828f, 4.14159f); p.move(1, -1); show(p.x, p.y); show(rp.x, rp.y); show(p.getX(), p.getY()); show(rp.getX(), rp.getY()); }Вывод из этой программы:
static void show(int x, int y) { System.out.println("(" + x + ", " + y + ")"); }
static void show(float x, float y) { System.out.println("(" + x + ", " + y + ")"); }
}
(0, 0) (2.7182798, 3.14159) (2, 3) (2, 3)Первая строка вывода иллюстрирует факт что экземпляр
RealPoint
фактически содержит два целочисленных поля, объявленные в классе Point
; только, что их имена скрываются от кода, происходит в пределах объявления класса RealPoint
(и таковые из любых подклассов это могло бы иметь). Когда ссылка на экземпляр класса RealPoint
в переменной типа Point
используется, чтобы получить доступ к полю x
, целочисленное поле x
объявленный в классе Point
получается доступ. Факт, что его значение является нулем, указывает что вызов метода p.move(1,
-1)
не вызывал метод move
из класса Point
; вместо этого, это вызвало метод переопределения move
из класса RealPoint
.
Вторая строка вывода показывает что доступ к полю rp.x
обращается к полю x
объявленный в классе RealPoint
. Это поле имеет тип float
, и эта вторая строка вывода соответственно выводит на экран значения с плавающей точкой. Случайно, это также иллюстрирует факт что имя метода show
перегружается; типы параметров в вызове метода диктуют, какое из этих двух определений будет вызвано.
Последние две строки вывода показывают что вызовы метода p.getX()
и rp.getX()
каждый вызывает getX
метод объявляется в классе RealPoint
. Действительно, нет никакого способа вызвать getX
метод класса Point
для экземпляра класса RealPoint
снаружи тела RealPoint
, независимо от того, что тип переменной мы можем использовать, чтобы содержать ссылку на объект. Таким образом мы видим, что поля и методы ведут себя по-другому: сокрытие отличается от переопределения.
static
) метод может быть вызван при использовании ссылки, тип которой является классом, который фактически содержит объявление метода. В этом отношении сокрытие статических методов отличается от переопределения методов экземпляра. Пример:
class Super { static String greeting() { return "Goodnight"; } String name() { return "Richard"; } }производит вывод:
class Sub extends Super { static String greeting() { return "Hello"; } String name() { return "Dick"; } }
class Test { public static void main(String[] args) { Super s = new Sub(); System.out.println(s.greeting() + ", " + s.name()); } }
Goodnight, Dickпотому что вызов
greeting
использует тип s
, а именно, Super
, выяснять, во время компиляции, который метод класса вызвать, тогда как вызов name
использует класс s
, а именно, Sub
, выяснять, во время выполнения, который метод экземпляра вызвать.
import java.io.OutputStream;
import java.io.IOException;
class BufferOutput {
private OutputStream o;
BufferOutput(OutputStream o) { this.o = o; }
protected byte[] buf = new byte[512];
protected int pos = 0;
public void putchar(char c) throws IOException { if (pos == buf.length) flush(); buf[pos++] = (byte)c; }
public void putstr(String s) throws IOException { for (int i = 0; i < s.length(); i++) putchar(s.charAt(i)); }Этот пример производит вывод:
public void flush() throws IOException { o.write(buf, 0, pos); pos = 0; }
}
class LineBufferOutput extends BufferOutput {
LineBufferOutput(OutputStream o) { super(o); }
public void putchar(char c) throws IOException { super.putchar(c); if (c == '\n') flush(); }
}
class Test { public static void main(String[] args)
throws IOException
{ LineBufferOutput lbo =
new LineBufferOutput(System.out); lbo.putstr("lbo\nlbo"); System.out.print("print\n"); lbo.putstr("\n"); } }
lbo print lboКласс
BufferOutput
реализует очень простую буферизованную версию OutputStream
, сбрасывание вывода, когда буфер полон или flush
вызывается. Подкласс LineBufferOutput
объявляет только конструктора и единственный метод putchar
, который переопределяет метод putchar
из BufferOutput
. Это наследовало методы putstr
и flush
от класса Buffer
.
В putchar
метод a LineBufferOutput
объект, если символьным параметром является новая строка, то это вызывает flush
метод. Критическая точка о переопределении в этом примере то, что метод putstr
, который объявляется в классе BufferOutput
, вызывает putchar
метод определяется текущим объектом this
, который является не обязательно putchar
метод объявляется в классе BufferOutput
.
Таким образом, когда putstr
вызывается в main
использование LineBufferOutput
объект lbo
, вызов putchar
в теле putstr
метод является вызовом putchar
из объекта lbo
, объявление переопределения putchar
это проверяет на новую строку. Это позволяет подкласс BufferOutput
изменить поведение putstr
метод, не пересматривая это.
Документация для класса такой как BufferOutput
, то, который разрабатывается, чтобы быть расширенным, должно ясно указать на то, что является контрактом между классом и его подклассами, и должно ясно указать, что подклассы могут переопределить putchar
метод таким образом. Конструктор BufferOutput
класс, поэтому, не хотел бы изменять реализацию putstr
в будущей реализации BufferOutput
не использовать метод putchar
, потому что это нарушило бы существующие ранее условия контракта с подклассами. См. дальнейшее обсуждение совместимости на уровне двоичных кодов в §13, особенно §13.2.
BadPointException
: class BadPointException extends Exception { BadPointException() { super(); } BadPointException(String s) { super(s); } } class Point { int x, y; void move(int dx, int dy) { x += dx; y += dy; } }Этот пример приводит к ошибке времени компиляции, потому что переопределение метода
class CheckedPoint extends Point { void move(int dx, int dy) throws BadPointException { if ((x + dx) < 0 || (y + dy) < 0) throw new BadPointException(); x += dx; y += dy; } }
move
в классе CheckedPoint
объявляет, что это выдаст проверенное исключение что move
в классе Point
не объявил. Если это не считали ошибкой, invoker метода move
на ссылке типа Point
мог найти контракт между этим и Point
поврежденный, если это исключение было выдано. Удаление throws
пункт не помогает:
class CheckedPoint extends Point { void move(int dx, int dy) { if ((x + dx) < 0 || (y + dy) < 0) throw new BadPointException(); x += dx; y += dy; } }Различная ошибка времени компиляции теперь происходит, потому что тело метода
move
не может выдать проверенное исключение, а именно, BadPointException
, это не появляется в throws
пункт для move
. StaticInitializer:Это - ошибка времени компиляции для статического инициализатора, чтобы быть в состоянии завершиться резко (§14.1, §15.5) с проверенным исключением (§11.2).
static
Block
Статические инициализаторы и инициализаторы переменной класса выполняются в текстовом порядке и, возможно, не обращаются к переменным класса, объявленным в классе, объявления которого появляются дословно после использования, даже при том, что эти переменные класса находятся в контексте. Это ограничение разрабатывается, чтобы поймать, во время компиляции, круговой или иначе уродливые инициализации. Таким образом, оба:
class Z { static int i = j + 2; static int j = 4; }и:
class Z { static { i = j + 2; } static int i, j; static { j = 4; } }результат в ошибках времени компиляции.
Доступы к переменным класса методами не проверяются таким образом, таким образом:
class Z { static int peek() { return j; }производит вывод:
static int i = peek(); static int j = 1; }
class Test { public static void main(String[] args) { System.out.println(Z.i); }
}
0потому что переменный инициализатор для
i
использует метод класса peek
получить доступ к значению переменной j
прежде j
был инициализирован ее переменным инициализатором, в которой точке у этого все еще есть свое значение по умолчанию (§4.5.4). Если a return
оператор (§14.15) появляется где угодно в пределах статического инициализатора, затем ошибка времени компиляции происходит.
Если ключевое слово this
(§15.7.2) или ключевое слово super
(§15.10, §15.11), появляется где угодно в пределах статического инициализатора, затем ошибка времени компиляции происходит.
ConstructorDeclaration:SimpleTypeName в ConstructorDeclarator должен быть простым именем класса, который содержит объявление конструктора; иначе ошибка времени компиляции происходит. Во всех других отношениях, взгляды объявления конструктора точно так же как объявление метода, у которого нет никакого типа результата.
ConstructorModifiersoptConstructorDeclarator
ThrowsoptConstructorBody ConstructorDeclarator:
SimpleTypeName
(
FormalParameterListopt)
class Point { int x, y; Point(int x, int y) { this.x = x; this.y = y; } }Конструкторы вызываются выражениями создания экземпляра класса (§15.8),
newInstance
метод класса Class
(§20.3), преобразованиями и связями, вызванными оператором конкатенации строк + (§15.17.1), и явными вызовами конструктора от других конструкторов (§8.6.5). Конструкторы никогда не вызываются выражениями вызова метода (§15.11). Доступом к конструкторам управляют модификаторы доступа (§6.6). Это полезно, например, в предотвращении инстанцирования, объявляя недоступного конструктора (§8.6.8).
Объявления конструктора не являются элементами. Они никогда не наследованы и поэтому не подвергаются сокрытию или переопределению.
ConstructorModifiers:Модификаторы доступа
ConstructorModifier
ConstructorModifiersConstructorModifier ConstructorModifier: one of
public protected private
public
, protected
, и private
обсуждаются в §6.6. Ошибка времени компиляции происходит, если тот же самый модификатор появляется не раз в объявлении конструктора, или если у объявления конструктора есть больше чем один из модификаторов доступа public
, protected
, и private
. В отличие от методов, конструктор не может быть abstract
, static
, final
, native
, или synchronized
. Конструктор не наследован, таким образом нет никакой потребности объявить это final
и abstract
конструктор никогда не мог реализовываться. Конструктор всегда вызывается относительно объекта, таким образом, не имеет никакого смысла для конструктора быть static
. Нет никакой практической потребности в конструкторе быть synchronized
, потому что это заблокировало бы объект, в стадии строительства, который обычно не делается доступным для других потоков, пока все конструкторы для объекта не завершили свою работу. Нехватка native
конструкторы являются произвольным проектным решением языка, которое облегчает для реализации виртуальной машины Java проверять, что конструкторы суперкласса всегда должным образом вызываются во время объектного создания.
throws
пункт для конструктора идентичен в структуре и поведении к throws
пункт для метода (§8.4.4). this
сопровождаемый заключенным в скобки списком параметров, или явным вызовом конструктора прямого суперкласса, записанного как super
сопровождаемый заключенным в скобки списком параметров. ConstructorBody:Это - ошибка времени компиляции для конструктора, чтобы прямо или косвенно вызвать себя через серию одного или более явного включения вызовов конструктора
{
ExplicitConstructorInvocationoptBlockStatementsopt
}
ExplicitConstructorInvocation:
this (
ArgumentListopt) ;
ArgumentListopt
super () ;
this
.
Если тело конструктора не начинается с явного вызова конструктора, и объявляемый конструктор не является частью исконного класса Object
, тогда тело конструктора, как неявно предполагает компилятор, начинается с вызова конструктора суперкласса"super();
", вызов конструктора его прямого суперкласса, который не берет параметров.
За исключением возможности явных вызовов конструктора, тело конструктора походит на тело метода (§8.4.5). A return
оператор (§14.15) может использоваться в теле конструктора, если это не включает выражение.
class Point {первый конструктор
int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
}
class ColoredPoint extends Point {
static final int WHITE = 0, BLACK = 1;
int color;
ColoredPoint(int x, int y) { this(x, y, WHITE); }
ColoredPoint(int x, int y, int color) { super(x, y);
this.color = color;
}
}
ColoredPoint
вызывает второе, обеспечивая дополнительный параметр; второй конструктор ColoredPoint
вызывает конструктора его суперкласса Point
, проведение координат. Явный оператор вызова конструктора, возможно, не обращается ни к каким переменным экземпляра или методам экземпляра, объявленным в этом классе или никаком суперклассе, или использовании this
или super
в любом выражении; иначе, ошибка времени компиляции происходит. Например, если первый конструктор ColoredPoint
в примере выше были изменены на:
ColoredPoint(int x, int y) { this(x, y, color); }тогда ошибка времени компиляции произошла бы, потому что переменная экземпляра не может использоваться в пределах вызова конструктора суперкласса.
Вызов конструктора прямого суперкласса, появляется ли это фактически как явный оператор вызова конструктора или обеспечивается автоматически (§8.6.7), выполняет дополнительное неявное действие после нормального возврата управления от конструктора: все переменные экземпляра, у которых есть инициализаторы, инициализируются тогда в текстовом порядке, в котором они появляются в объявлении класса. Вызов другого конструктора в том же самом классе, используя ключевое слово this
не выполняет это дополнительное неявное действие.
§12.5 описывает создание и инициализацию новых экземпляров класса.
Object
, тогда у конструктора по умолчанию есть пустое тело.
Если класс объявляется public
, тогда конструктору по умолчанию неявно дают модификатор доступа public
(§6.6); иначе, конструктору по умолчанию подразумевал доступ по умолчанию никакой модификатор доступа. Таким образом, пример:
public class Point { int x, y; }эквивалентно объявлению:
public class Point { int x, y; public Point() { super(); } }где конструктор по умолчанию
public
потому что класс Point
public
. private
. A public
класс может аналогично предотвратить создание экземпляров вне его пакета, объявляя по крайней мере одному конструктору, предотвратить создание конструктора по умолчанию с public
доступ, и объявление никакого конструктора, который является public
.
class ClassOnly { private ClassOnly() { } static String just = "only the lonely"; }класс
ClassOnly
не может быть инстанцирован, в то время как в примере:
package just;
public class PackageOnly { PackageOnly() { } String[] justDesserts = { "cheesecake", "ice cream" }; }класс
PackageOnly
может быть инстанцирован только в пределах пакета just
, в котором это объявляется.
Содержание | Предыдущий | Следующий | Индекс
Спецификация языка Java (HTML, сгенерированный Блинчиком "сюзет" Pelouch 24 февраля 1998)
Авторское право © Sun Microsystems, Inc 1996 года. Все права защищены
Пожалуйста, отправьте любые комментарии или исправления к doug.kramer@sun.com