Содержание
| Предыдущая | СледующаяГЛАВА 6
Имена используются для обращения к объектам, объявленным в программе на языке Ява. Объявленный объект (§6.1) представляет собой пакет, классовый тип, интерфейсный тип, член (поле или метод) ссылочного типа, параметр (метода, конструктора, или обработчика исключительной ситуации), или локальную переменную.
Имена в программах на языке Ява бывают простые, состоящие из единственного идентификатора, или квалифицированные, состоящие из последовательности идентификаторов отделенных знаками "." (§6.2).
Каждое объявленное имя имеет область действия (§6.3), которая представляет собой часть текста программы на языке Ява, внутри которой объявленный объект может вызываться простым именем.
Пакеты и ссылочные типы (классовые типы, интерфейсные типы и тип массив) содержат члены (§6.4). На член можно сослаться, используя квалифицированное имя N.x, где N представляет собой простое, либо квалифицированное имя, а x идентификатор. Если N имя пакета, тогда x является членом этого пакета – классовым типом, либо интерфейсным типом, либо подпакетом. Если N имя ссылочного типа или переменная этого типа, тогда x - имя члена данного типа – поля или метода.
При определении значения имени (§6.5), Ява использует окружающий контекст для распознавания пакетов, типов, переменных и методов с одним и тем же именем.
В объявлении класса, интерфейса, метода, или
поля может быть задано управление доступом
(§6.6) ,
определяющее когда разрешен доступ к члену.
Доступ – понятие отличное от области действия; доступ определяет часть текста
программы на языке Ява, внутри которой к
объявленному объекту можно обратиться по
квалифицированному имени, с помощью выражения
доступа к полю (§15.10)
или с помощью выражения вызова метода (§15.11) , в
котором метод не задан простым именем. Доступ по
умолчанию означает, что элемент доступен везде
внутри пакета, который содержит его описание;
кроме того возможен доступ, задаваемый
модификаторами public (доступный), protected
(защищенный) и private (скрытый).
Полностью квалифицированные имена (§6.7) и соглашения по именованию (§6.8) , также рассмотрены в этой главе.
Имя поля, параметра, или локальной переменной
могут использоваться в качестве выражения (§15.13.1). Имя
метода может появиться в выражении только как
часть выражения вызова метода (§15.11) . Имя классового типа или
интерфейсного типа может появиться в выражении
только как часть выражения создания экземпляра
класса (§15.8) ,
выражения создания массива (§15.9) , выражения приведения (§15.15) , или в
выражении instanceof (§15.19.2) , или как часть
квалифицированного имени поля или метода. Имя
пакета может появиться в выражении только как
часть квалифицированного имени для классового
или интерфейсного типов.
Объявление вводит объект в программу на языке Ява и включает идентификатор (§3.8), который может быть использован в имени для обращения к этому объекту. Объявленный объект может быть одним из следующих:
package
(§7.4)
поле, одно из следующего:
- метод, один из следующих:
Конструкторы (§8.6) также вводятся объявлениями, но используют имя того класса, в котором они объявлены, а не новое имя.
Имя используется, чтобы обратиться к объекту, объявленному в программе на языке Ява.
Имеются две формы имен: простое имя и квалифицированное имя. Простое имя представляет собой единственный идентификатор. Квалифицированное имя состоит из имени, знака ".", и идентификатора.
В определении значения имени (§6.5) , язык Ява, принимает во внимание контекст, в котором появляется имя. Ява различает по контексту, где имя должно означать (ссылаться на) пакет (§6.5.3) , тип (§6.5.4) , переменную или значение в выражении (§6.5.5) , или метод (§6.5.6).
Не все идентификаторы в программах на языке Ява, представляют собой часть имени. Идентификаторы также используются в следующих ситуациях:
super, которое появляется перед знаком
".".super, которое
появляется перед знаком ".". break (§14.13) и continue (§14.14) , которые ссылаются на метки
операторов.В примере:
class Test {
public static void main(String[] args) {
Class c = System.out.getClass();
System.out.println(c.toString().length() +
args[0].length() + args.length);
}
}
идентификаторы Test, main, и первые
появления args и c не являются
именами; они используются в описаниях, задавая
имена объявленных объектов. В примере есть имена String, Class, System.out.getClass,
System.out.println, c.toString, args и args.length.
Первое появление length не имя, а
идентификатор , записанный в
выражении вызова метода (§15.11) . Второе появление length не имя, а идентификатор появляющийся
в выражении вызова метода (§15.11).
Идентификаторы, использованные в помеченных
операторах и их объединенных break и continue
операторах полностью отличны от использованных
в описаниях. Так, следующий код корректен:
class TestString {
char[] value;
int offset, count;
int indexOf(TestString str, int fromIndex) {
char[] v1 = value, v2 = str.value;
int max = offset + (count - str.count);
int start = offset + ((fromIndex < 0) ? 0 : fromIndex);
i:
for (int i = start; i <= max; i++)
{
int n = str.count, j = i, k = str.offset;
while (n-- != 0) {
if (v1[j++] != v2[k++])
continue i;
}
return i - offset;
}
return -1;
}
}
Этот код был взят из версии класса String
и его метода indexOf (§20.12.26) , где
метка первоначально называлась test.
Изменение метки так, что она получает такое же
имя, как и локальная переменная i, не
скрывает метку в области действия описания i.
Идентификатор max может также
использоваться в качестве метки оператора; метка
не будет скрывать локальную переменную max
внутри помеченного оператора.
Область действия описания представляет собой часть программы, внутри которой к объекту, объявленному описанием можно обратиться, используя простое имя:
package),
определена базовой системой (§7.4.3). Весь код программы на
языке Ява находится внутри области действия
стандартного пакета, именуемого java, так
что на пакет java всегда можно ссылаться из
программы на Ява.
class Test {
int i = j; // ошибка времени компиляции: некорректная опережающая ссылка
int j = 1;
}
тогда как следующий пример компилируется без ошибки:
class Test {
Test() { k = 2; }
int j = 1;
int i = j;
int k;
}
не смотря на то, что конструктор (§8.6) Test обращается к
полю k, которое объявляется на три строки
позже.
for (§14.12)
включает следующее:
- собственный инициализатор
- любые дальнейшие описания правее части ForInit оператора
for- части Expression и ForUpdate оператора
for- содержащийся в Statement
catch оператора try (§14.18)
представляет собой весь блок, ассоциированный с catch.Эти правила означают, что описания классовых и интерфейсных типов не обязательно должны появляться перед тем, как эти типы используются.
В примере:
package points;
class Point {
int x, y;
PointList list;
Point next;
}
class PointList {
Point first;
}
использование PointList в классе Point
корректно, потому что область действия
классового типа по имени PointList включает
как класс Point так и класс PointList, равно как и любые
другие описания типа в других модулях компиляции
пакета points.
Некоторые описания могут быть скрыты (§6.3.1) в части их области действия, другими описаниями того же имени. В таком случае простое имя не может быть использовано для обращения к объявленному объекту.
Пример:
class Test {
static int x = 1;
public static void main(String[] args) {
int x = 0;
System.out.print("x=" + x);
System.out.println(", Test.x=" + Test.x);
}
}
выводит следующее:
x=0, Test.x=1
Этот пример объявляет:
Teststatic) переменную
класса x, которая представляет собой член
класса Testmain, который представляет
собой член класса Testargs метода mainx метода mainПоскольку область действия переменной класса
включает все тело класса (§8.2)
,переменная класса x будет доступна
повсюду во всем теле метода main. В этом
примере, однако, переменная класса x
скрыта внутри тела метода main объявлением
локальной переменной x.
Локальная переменная имеет область действия в
оставшейся части блока, в котором она объявлена (§14.3.2); в
данном случае это остаток метода main, а
именно его инициализатор "0" и вызовы print
и println.
Это означает, что:
x" в вызове print
обращается к значению локальной переменной x.println использует квалифицированное
имя (§6.6) Test.x, которое использует имя
классового типа Test для доступа к
переменной класса x, потому что описание Test.x в этой точке скрыто, к ней
нельзя обратиться с помощью простого имени.Если следовать стандартным соглашениям именования (§6.8) , то скрытие делает распознавание отдельных контекстов имени более сложным. Следующий пример включает скрытие, потому что он не следует стандартному соглашению присваивания имен:
class Point { int x, y; }
class Test {
static Point Point(int x, int y) {
Point p = new Point();
p.x = x; p.y = y;
return p;
}
public static void main(String[] args) {
int Point;
Point[] pa = new Point[2];
for (Point = 0; Point < 2; Point++) {
pa[Point] = new Point();
pa[Point].x = Point;
pa[Point].y = Point;
}
System.out.println(pa[0].x + "," + pa[0].y);
System.out.println(pa[1].x + "," + pa[1].y);
Point p = Point(3, 4);
System.out.println(p.x + "," + p.y);
}
}
Пример компилируется без ошибок и выводит на экран следующее:
0,0 1,1 3,4
Внутри тела main, при просмотре Point
обнаруживаются различные описания в зависимости
от контекста использования:
new Point[2]", двух выражениях создания
экземпляра класса "new Point()", и в начале трех различных
выражений описания локальной переменной, Point это TypeName (§6.5.4) и указывает классовый
тип Point в каждом случае.Point(3,
4)" появление Point
представляет собой MethodName (§6.5.6) и означает класс метода Point.Point.Пример:
import java.util.*;
class Vector {
int val[] = { 1 , 2 };
}
class Test {
public static void main(String[] args) {
Vector v = new Vector();
System.out.println(v.val[0]);
}
}
компилируется и напечатает:
1
используя класс Vector объявленный здесь в
предпочтение классу java.util.Vector, который
может быть импортирован по шаблону.
Пакеты и ссылочные типы содержат члены. Члены пакета (§7) , это подпакеты (§7.1) и все классовые (§8) и интерфейсные (§9) типы, объявленные во всех модулях компиляции (§7.3) пакета. Члены ссылочного типа (§4.3) - это поля (§8.3, §9.3, §10.7) методы (§8.4, §9.4) . Члены объявляются в типе, но могут быть унаследованы, потому что они являются доступными членами суперкласса или суперинтерфейса, и не являются ни скрытыми, ни переопределенными (§8.4.6).
В этом разделе сделан обзор членов пакетов и ссылочных типов, как основа для обсуждения квалифицированных имен и определения значения имен. Для более подробного ознакомления смотри §7.1, §8.2 , §9.2, и §10.7.
Член пакета (§7) - это либо подпакет (§7.1) , либо классовый (§8) или интерфейсный (§9) тип, объявленные в модуле компиляции пакета.
В общем, подпакеты пакета определены базовой
системой (§7.2) .
Однако стандартный пакет java, всегда
включает подпакеты lang, util, io
и net, и может включать другие подпакеты.
Никакие из двух описанных членов одного и того же
пакета не могут иметь одинаковые простые имена (§7.1) , но члены
разных пакетов могут иметь одинаковые простые
имена. Например, возможно такое описание пакета:
package vector;
public class Vector { Object[] vec; }
который имеет член public-класса,
названный Vector, хотя стандартный пакет java.utill также
объявляет класс Vector. Эти
два класса типов различны и имеют различные
полностью квалифицированные имена (§6.7) . Полностью
квалифицированное имя Vector в
данном примере, является vector.Vector,
тогда как java.util.Vector
представляет собой полностью квалифицированное
имя стандартного класса Vector. Поскольку
пакет vector содержит класс с именем Vector,
он не может при этом иметь подпакет с именем Vector.
Члены классового типа (§8.2) - это поля и методы. Членами классового типа могут являться:
Object нет прямого
суперкласса).Конструкторы (§8.6) не являются членами.
Отсутствуют ограничения, запрещающие полю и методу классового типа, иметь одно и то же простое имя.
Класс может иметь два или более поля с одинаковыми простыми именами, если они объявлены в различных интерфейсах и унаследованы. Попытка обратиться к любому полю по его простому имени приводит к ошибке времени компиляции (§6.5.6.2, §8.2).
В примере:
interface Colors {
int WHITE = 0, BLACK = 1;
}
interface Separates {
int CYAN = 0, MAGENTA = 1, YELLOW = 2, BLACK = 3;
}
class Test implements Colors, Separates {
public static void main(String[] args) {
System.out.println(BLACK); // compile-time error: ambiguous
}
}
имя BLACK в методе main неоднозначное,
потому что класс Test имеет два члена с
именем BLACK, один унаследованный от Colors и один от Separates.
Классовый тип может иметь два или более метода с одинаковыми простыми именами, если методы имеют разные сигнатуры (§8.4.2) , то есть, если они имеют разное количество параметров или различные типы параметров, хотя бы в одной позиции параметра. Такой член - метод будем называть перегруженным.
Классовый тип может содержать объявление
метода с тем же именем и сигнатурой, как и у
другого метода, который унаследован от
суперкласса или суперинтерфейса. При этом метод
суперкласса или суперинтерфейса не наследуется.
Если ненаследуемый метод абстрактный (abstract), то говорят, что новое
объявление реализует его; если ненаследуемый
метод не абстрактный, то говорят, что новое
объявление его переопределяет.
В примере:
class Point {
float x, y;
void move(int dx, int dy) { x += dx; y += dy; }
void move(float dx, float dy) { x += dx; y += dy; }
public String toString() { return "("+x+","+y+")"; }
}
класс Point имеет два члена - это методы с
одинаковым именем move. Выбор
перегруженного метода move класса Point
выполняется для каждого вызова метода во время
компиляции в соответствии с процедурой,
приведенной в §15.11.
В этом примере, члены класса Point
представляют собой переменные экземпляра x
и y типа float,
объявленные в Point, два объявленных метода move,
объявленный метод toString, и члены, которые Point неявно наследует из своего прямого
суперкласса Object (§4.3.2)
, как, например, метод hashCode (§20.1.4) . Заметьте, что Point не наследует метод toString (§20.1.2) класса Object,
потому что этот метод перегружается описанием
метода toString в классе Point.
Члены интерфейсного типа (§9.2) - это поля и методы. Членами интерфейсного типа могут быть:
Интерфейс может иметь два или более поля с одинаковыми простыми именами, если они объявлены в различных интерфейсах и унаследованы. Попытка обратиться к какому-нибудь полю по его простому имени приводит к ошибке времени компиляции (§6.5.5.1, §9.2).
В примере:
interface Colors {
int WHITE = 0, BLACK = 1;
}
interface Separates {
int CYAN = 0, MAGENTA = 1, YELLOW = 2, BLACK = 3;
}
interface ColorsAndSeparates extends Colors, Separates {
int DEFAULT = BLACK; // compile-time error: ambiguous
}
члены интерфейса ColorsAndSeparates включают
члены, унаследованные от Colors и члены,
унаследованные от Separates, то есть WHITE,
BLACK (первый из двух), CYAN, MAGENTA, YELLOW
и BLACK (второй из двух). Член с именем BLACK
неоднозначен в интерфейсе ColorsAndSeparates.
Членами типа массив (§10.7) являются следующие:
Object (§4.3.2, §20.1)length, которое
представляет собой константное (final) поле каждого
массива; его тип int, и он содержит число
членов массива.Пример:
class Test {
public static void main(String[] args) {
int[] ia = new int[3];
int[] ib = new int[6];
System.out.println(ia.getClass() == ib.getClass());
System.out.println("ia has length=" + ia.length);
}
}
выведет:
true ia has length=3
Этот пример использует метод getClass,
унаследованный от класса Object, и поле length. Результат сравнения
объектов Class во втором println демонстрирует, что все массивы, чьи
элементы типа int, являются экземплярами
одного и того же типа массив int[].
Значение имени в языке Ява зависит от контекста, в котором оно используется. Определение значения имени требует трех шагов. Во-первых, в зависимости от контекста и синтаксиса имена делятся на пять категорий: PackageName, TypeName, ExpressionName, MethodName или AmbiguousName (неоднозначное имя). Во-вторых, имя, которое изначально классифицируется своим контекстом в качестве AmbiguousName, переклассифицируется по определенным правилам области действия, чтобы представлять собой PackageName, TypeName или ExpressionName. В-третьих, результирующая категория указывает конечное определение значения имени (или ошибку времени компиляции, если имя не имеет значения).
PackageName: Identifier PackageName.Identifier TypeName: Identifier PackageName.Identifier ExpressionName: Identifier AmbiguousName.Identifier MethodName: Identifier AmbiguousName.Identifier AmbiguousName: Identifier AmbiguousName.Identifier
Использование контекста в языке Ява помогает минимизировать конфликты по именам между объектами различных видов. Такие конфликты бывают редко, если при присваивание имен следуют соглашениям, описанным в §6.8 . Тем не менее, конфликты могут появляться непреднамеренно как типы, разработанные разными программистами или разными организациями. Например, типы, методы, и поля могут иметь одинаковые имена. В языке Ява никогда не возникало проблем при различении метода и поля с одинаковыми именами, поскольку применение контекста всегда отличает вызов метода от вызова области.
Имя синтаксически классифицируется в качестве PackageName в следующих контекстах:
Имя синтаксически классифицируется в качестве TypeName в следующих контекстах:
extends в
объявлении класса (§8.1.3)implements в
объявлении класса (§8.1.4)extends в объявлении
интерфейса (§9.1.3)
- В объявлении поля (§8.3, §9.3)
- Как тип результата метода (§8.4, §9.4)
- Как тип формального параметра метода или конструктора (§8.4.1, §8.6.1, §9.4)
- Как тип исключения, которое может генерироваться методом или конструктором (§8.4.4, §8.6.4, §9.4)
- Как тип локальной переменной (§14.3)
- Как тип параметра исключения в операторе
catchоператораtry(§14.18)- Как классовый тип экземпляра, который должен быть создан в выражении создания экземпляра класса (§15.8)
- Как тип элемента массива, который создается выражением создания массива (§15.9)
- Как тип упомянутый в операторе приведения выражения приведения (§15.15)
- Как тип, который следует за операцией отношения
instanceof(§15.19.2)
Имя синтаксически классифицируется в качестве ExpressionName в следующих контекстах:
Имя синтаксически классифицируется в качестве MethodName в следующем контексте:
Имя синтаксически классифицируется в качестве AmbiguousName в следующих контекстах:
AmbiguousName переклассифицируетя в следующих случаях:
- Если Identifier появляется внутри области действий (§6. 3) описания локальной переменной (§14.3) или описании параметра (§8.4.1, §8.6.1, §14.18) с этим именем, тогда AmbiguousName переклассифицируется как ExpressionName.
- В противном случае, рассматривается класс или интерфейс C, внутри описания которого появляется Identifier. Если C имеет одно или несколько полей с тем же именем, которое может быть либо описано внутри него, либо унаследованным, тогда AmbiguousName переклассифицируется как ExpressionName.
- Иначе если тип этого имени был описан в модуле компиляции (§7.3) содержащем Identifier либо описанием одиночного импорта типа (§7.5.1), либо описанием классового или интерфейсного типов (§7.6) , тогда AmbiguousName переклассифицируется как TypeName.
- В противном случае, если тип этого имени описан в другом модуле компиляции (§7.3) пакета (§7.1) модуля компиляции, содержащего Identifier, тогда AmbiguousName переклассифицируется как TypeName.
- Иначе, если тип этого имени объявлен описанием одиночного импорта типа по шаблону модуля компиляции (§7.5.2), содержащем Identifier, тогда AmbiguousName переклассифицируется как TypeName.
- Иначе, если тип этого имени объявлен более чем одним описанием импорта типа по шаблону в модуле компиляции, содержащем Identifier, тогда результатом будет ошибка времени компиляции.
- Иначе, AmbiguousName переклассифицируется как PackageName. Следующим шагом выясняется имеется ли фактически пакет с таким именем.
- Если имя слева от "." переклассифицируется как PackageName, тогда существует еще несколько вариантов:
- если имеется пакет, имя которого совпадает с именем, стоящим слева от "." и этот пакет содержит описание типа, и имя этого типа совпадает с именем Identifier, тогда данное AmbiguousName переклассифицируется как TypeName.
- в противном случае, AmbiguousName переклассифицируется как PackageName. Это определяется на более позднем шаге либо фактически не существует пакета с таким же именем.
- Если имя слева от "." переклассифицируется как TypeName, тогда это AmbiguousName переклассифицируется как ExpressionName.
- Если имя слева от "." переклассифицируется как ExpressionName, тогда это AmbiguousName переклассифицируется как ExpressionName.
В качестве примера рассмотрим следующий придуманный "библиотечный код":
package ORG.rpgpoet;
import java.util.Random;
interface Music { Random[] wizards = new Random[4]; }
и затем рассмотрим код этого примера в другом пакете:
package bazola;
class Gabriel {
static int n = ORG.rpgpoet.Music.wizards.length;
}
Прежде всего, имя ORG.rpgpoet.Music.wizards.length
классифицируется как ExpressionName, потому что его
функция – PostfixExpression. Поэтому, каждое из имен:
ORG.rpgpoet.Music.wizards ORG.rpgpoet.Music ORG.rpgpoet ORG
изначально классифицируется как AmbiguousName. Они затем переклассифицируются:
ORG в любом другом
модуле компиляции пакета bazola, простое имя ORG
переклассифицируется как PackageName.rpgpoet отсутствует в любом модуле
компиляции пакета ORG (и мы знаем, что
такого класса или интерфейса нет, потому что
пакет ORG имеет подпакет с именем rpgpoet),
квалифицированное имя ORG.rpgpoet переклассифицируется в PackageName.ORG.rpgpoet
имеет интерфейсный тип с именем Music,
квалифицированное имя ORG.rpgpoet.Music переклассифицируется в TypeName.
ORG.rpgpoet.Music представляет собой TypeName,
квалифицированное имя ORG.rpgpoet.Music.wizards
переклассифицируется в ExpressionName.Значение имени классифицированного как PackageName определено следующим образом.
Если имя пакета состоит из одного Identifier, тогда этот идентификатор указывает пакет верхнего уровня с именем того же идентификатора. Если пакет с тем именем не доступен, как определено базовой системой (§7.4.3) , тогда происходит ошибка времени компиляции.
Если имя пакета имеет вид Q.Id, то Q должно также быть именем пакета. Имя пакета Q.Id обозначает пакет, который представляет собой член с именем Id внутри пакета с именем Q. Если Q не имя доступного пакета, или Id не имя доступного подпакета этого пакета, то происходит ошибка времени компиляции.
Значение имени классифицированного как TypeName определено следующим образом.
Если имя типа состоит из одного Identifier, тогда идентификатор должен появляться в области действий описания типа с этим именем, либо происходит ошибка времени компиляции. Возможно, что идентификатор появляется внутри области действий более чем одного типа с этим именем, в этом случае тип, указанный именем, определен следующим образом:
Такой порядок при рассмотрении описаний типа принят, чтобы выбрать наиболее подходящий из двух или более применимых описаний типа.
Если имя типа имеет вид Q.Id, то Q должно также быть именем пакета. Имя типа Q.Id обозначает тип, который представляет собой член с именем Id пакета с именем Q. Если Q не имя доступного пакета, или Id не имя типа внутри пакета, или тип с именем Id не доступен внутри пакета (§6.6) , тогда происходит ошибка времени компиляции. Пример:
package wnj.test;
class Test {
public static void main(String[] args) {
java.util.Date date =
new java.util.Date(System.currentTimeMillis());
System.out.println(date.toLocaleString());
}
}
вывел на экран следующее:
Sun Jan 21 22:56:29 1996
В этом примере:
wnj.test должно быть именем пакета в
базовой системе. Это определяется при первом
взгляде на пакет wnj, используя алгоритм,
описанный в §6.5.3.1
, а затем убеждаемся, что подпакет test этого
пакета доступен.java.util.Date (§21.3)
должно указать тип, таким образом, мы первые
используем алгоритм рекурсивного определения,
если java.utill доступный пакет
и затем смотрим, чтобы увидеть, доступен ли тип Date
в этом пакете.Значение имени классифицированного как ExpressionName определено следующим образом.
Если имя выражения состоит из одного Identifier, тогда:
Если поле - переменная экземпляра (§8.3.1.1), имя
выражения должно появиться внутри описания
метода экземпляра (§8.4),
конструктора (§8.6),
или инициализатора переменной экземпляра (§8.3.2.2). Если
оно появляется внутри static-метода (§8.4.3.2),
статического инициализатора (§8.5), или инициализатора для static-переменной (§8.3.1.1, §12.4.2),
тогда происходит ошибка времени компиляции.
- Если нет именно одного члена того класса, который представляет собой поле с тем же именем, тогда результатом будет ошибка времени компиляции.
- Иначе, имя выражения указывает значение единственного члена поля этого имени. Тип имени выражения представляет собой объявленный тип поля. Если Identifier появляется в контексте, который требует переменной и не имеет значения, тогда происходит ошибка времени компиляции.
В примере:
class Test {
static int v;
static final int f = 3;
public static void main(String[] args) {
int i;
i = 1;
v = 2;
f = 33; // compile-time error
System.out.println(i + " " + v + " " + f);
}
}
имена, использованные с левой стороны в
присваиваниях i, v и f
указывают локальную переменную i, поле v,
и значение f (не переменную f, потому что f представляет
собой final-переменную).
Пример порождает ошибку времени компиляции,
потому что последнее присваивание не содержит
переменную с левой стороны. Если ошибочное
присваивание устранено, модифицированный код
может быть откомпилирован и будет выведено:
1 2 3
Если имя выражения имеет форму Q.Id, тогда Q уже классифицировано как имя пакета, имя типа, или имя выражения:
- Если нет единственного доступного (§6.6) члена классового типа, являющегося полем с именем Id, тогда происходит ошибка времени компиляции.
- В противном случае, если единственный доступный член поля, не является переменной класса (то есть, он не объявлен
static), тогда происходит ошибка времени компиляции.- Иначе, если переменная класса объявлена
final, тогда Q.Id задает значение переменной класса. Тип выражения Q.Id представляет собой объявленный тип переменной класса. Если Q.Id появляется в контексте, который требует переменной, а не значения, тогда происходит ошибка времени компиляции.- В противном случае, Q.Id указывает переменную класса. Тип выражения Q.Id представляет собой объявленный тип переменной класса.
- Если нет единственного доступного (§6.6) члена интерфейсного типа, который представляет собой поле с именем Id, тогда происходит ошибка времени компиляции.
- Иначе, Q.Id указывает значение поля. Тип выражения Q.Id представляет собой объявленный тип поля. Если Q.Id появляется в контексте, который требует переменной, а не значения, тогда происходит ошибка времени компиляции.
- Если T не ссылочный тип, происходит ошибка времени компиляции.
- Если нет единственного доступного (§6.6) члена типа T, который представляет собой область с именем Id, тогда происходит ошибка времени компиляции.
- В противном случае, если это поле будет одним из следующих:
- поле интерфейсного типа
final-поле классового типа (которое может быть либо переменной класса, либо переменной экземпляра)final-полеlengthтипа массив
- тогда Q.Id указывает значение поля. Тип выражения Q.Id представляет собой объявленный тип поля. Если Q.Id появляется в контексте, который требует переменной, а не значения, тогда происходит ошибка времени компиляции.
- Иначе, Q.Id указывает переменную, поле Id класса T, которой может быть либо переменная класса, либо переменная экземпляра. Тип выражения Q.Id представляет собой объявленный тип поля.
Пример:
class Point {
int x, y;
static int nPoints;
}
class Test {
public static void main(String[] args) {
int i = 0;
i.x++; // ошибка времени компиляции
Point p = new Point();
p.nPoints(); // ошибка времени компиляции
}
}
встречаются две ошибки времени компиляции,
потому что переменная i типа int не
имеет членов, и потому что nPoints не является
методом класса Point.
MethodName может появляться только в выражении вызова метода (§ 15.11). Значение имени, классифицированного как MethodName, определено следующим образом.
Если имя метода состоит из отдельного Identifier, тогда Identifier - это имя метода, используемое для вызова метода. Identifier должен быть именем по крайней мере одного метода класса или интерфейса, в пределах которого он появляется. См. § 15.11 для дальнейшего обсуждения интерпретации простых имен методов в выражении вызова метода.
Если имя метода имеет форму Q.Id, тогда Q
классифицируется уже как имя пакета, имя типа,
или имя выражения. Если Q - имя пакета, тогда
происходит ошибка времени компиляции. Иначе, Id-
имя метода, используемое для вызова метода. Если Q
- имя типа, тогда Id должен определять по
крайней мере один static-метод
типа Q. Если Q - имя выражения, и T - тип
выражения Q; тогда Id должен определять по
крайней мере один метод типа T. См. § 15.11
для дальнейшего обсуждения интерпретации
квалифицированного имени метода в выражении
вызова метода.
Квалифицированные имена - средства доступа к членам пакетов и ссылочных типов; средствами доступа являются выражения доступа к полю (§ 15.10) и выражения вызова метода (§ 15.11). Все три средства доступа синтаксически похожи тем, что появляется лексема “.”, которой предшествует некоторый указатель на пакет, тип или выражение, имеющее тип, за которой следует Identifier, и этот идентификатор является именем члена пакета или типа. Они все вместе известны как конструкции квалифицированного доступа.
Язык Ява предусматривает механизмы управления
доступом, устраняющие зависимость
пользователей, работающих с пакетами или
классами, от деталей реализации этого пакета или
класса. Управление доступом применяется к
квалифицированному доступу и вызову
конструкторов с помощью выражений создания
экземпляров класса (§ 15.8), явных вызовов
конструкторов (§ 8.6.5) и метода newInstance класса Class (§20.3.6).
Если доступ разрешен, тогда говорят, что объект является доступным.
public, тогда он доступен в любом коде на языке Ява,
который имеет доступ к пакету, в котором этот тип
объявлен. Если классовый или интерфейсный тип не
объявлен с модификатором доступа public, тогда к нему можно обратиться только в том
пакете, в котором он объявлен.
- Если член или конструктор объявлен с модификатором доступа
public, тогда доступ к нему разрешен. Все члены интерфейсов по умолчанию имеют модификатор доступаpublic.- Иначе, если член или конструктор объявлен с модификатором доступа
protected, тогда доступ разрешается, когда один из следующих пунктов является истинным:
- Доступ к члену или конструктору происходит внутри пакета, содержащего класс, в котором объявлен член с модификатором доступа
protected.- Доступ происходит в пределах подкласса класса, в котором объявлен член с модификатором доступа
protected, и доступ является корректным, как описано в § 6.6.2.
- Иначе, если член или конструктор объявлен с модификатором доступа
private, тогда доступ к нему разрешается только внутри класса, в котором он объявлен.- Иначе мы говорим, что имеет место доступ по умолчанию, который разрешен только тогда, когда обращение происходит внутри пакета, в котором объявлен тип.
Если член или конструктор объекта объявлен с
модификатором доступа protected, то к нему
можно обращаться вне пакета, в котором он
объявлен только с помощью кода, ответственного
за реализацию этого объекта. Пусть C - класс, в
котором объявлен член или конструктор с
модификатором доступа protected, и S -
подкласс класса C, в котором используется
этот член или конструктор с модификатором
доступа protected. Тогда:
protected, пусть Id -
имя элемента. Тогда рассмотрим средства
квалифицированного доступа:
- Если доступ к полю осуществляется выражением вида
super.Id, тогда доступ разрешается.- Если доступ осуществляется с помощью квалифицированного имени Q.Id, где Q - это TypeName, в данном случае доступ разрешается, тогда и только тогда, когда Q есть S или подкласс S.
- Если доступ осуществляется с помощью квалифицированного имени Q.Id, где Q - это ExpressionName, в этом случае доступ разрешается тогда и только тогда, когда тип выражения Q есть S или подкласс S.
- Если доступ к полю осуществляется с помощью выражения E.Id, где E классифицируется как Primary - выражение (первичное выражение), или выражением вызова метода E.Id (...), где E является Primary выражением, в этом случае доступ разрешен тогда и только тогда, когда тип E есть S или подкласс S.
protected:
- Если доступ осуществляется с помощью вызова конструктора суперкласса
super(...), тогда доступ разрешается.- Если доступ осуществляется с помощью выражения создания экземпляра класса
newT (...), тогда доступ не разрешается. (К конструктору с модификатором доступаprotectedможно обращаться, используя выражение создания экземпляра класса, только в пределах пакета, в котором он определен.)- Если доступ осуществляется вызовом метода
newInstanceклассаClass(§ 20.3.6), тогда доступ не разрешается.
В качестве примеров управления доступом, рассмотрим два модуля компиляции:
package points;
class PointVec { Point[] vec; }
и:
package points;
public class Point {
protected int x, y;
public void move(int dx, int dy) { x += dx; y += dy; }
public int getX() { return x; }
public int getY() { return y; }
}
которые объявляют два классовых типа в пакете points:
PointVec не объявлен с
модификатором доступа public и не является
частью public-интерфейса
пакета points, но может использоваться
только другими классами пакета.Point объявлен с
модификатором доступа public и доступен
другим пакетам. Это часть public-интерфейса пакета points.move, getX, и getY класса Point
объявлены с модификатором доступа public и
поэтому доступны любому коду на Яве, в котором
используется объект типа Point.x и y объявлены с
модификатором доступа protected и доступны
вне пакета points только в подклассах класса Point,
и только когда они являются полями объектов,
которые реализуются с помощью кода,
обращающегося к ним.Смотрите пример из § 6.6.7, где
модификатор protected ограничивает
доступ.
Если в классе отсутствует модификатор доступа public,
доступ к объявлению класса ограничивается
пакетом, в котором класс объявлен (§ 6.6). В
примере:
package points;
public class Point {
public int x, y;
public void move(int dx, int dy) { x += dx; y += dy; }
}
class PointList {
Point next, prev;
}
два класса объявлены в модуле компиляции. Класс
Point доступен вне пакета, в
то время как класс PointList
доступен только в пределах пакета. Таким образом
модуль компиляции другого пакета может иметь
доступ к классу Point с
помощью выражения points.Point,
или используя его полное квалифицированное имя:
package pointsUser;
class Test {
public static void main(String[] args) {
points.Point p = new points.Point();
System.out.println(p.x + " " + p.y);
}
}
или используя объявление одиночного импорта типа (§ 7.5.1), которое упоминает полное квалифицированное имя таким образом, что после этого может использоваться простое имя:
package pointsUser; import points.Point;
class Test {
public static void main(String[] args) {
Point p = new Point();
System.out.println(p.x + " " + p.y);
}
}
Однако, этот модуль компиляции не может
использовать или импортировать points.PointList,
который объявлен без модификатора public и
поэтому недоступен вне пакета points.
Если ни один из модификаторов доступа (public,
protected или private) не указан, член или
конструктор класса доступен во всем пакете,
который содержит объявление класса, где данный
член класса объявлен, но член или конструктор
класса не доступен ни в каком другом пакете. Если public-класс
имеет метод или конструктор с доступом по
умолчанию, тогда этот метод или конструктор не
доступен или не наследуется подклассом,
объявленным вне этого пакета.
Например, если мы имеем:
package points;
public class Point {
public int x, y;
void move(int dx, int dy) { x += dx; y += dy; }
public void moveAlso(int dx, int dy) { move(dx, dy); }
}
тогда подкласс в другом пакете может объявлять
другой метод move с той же сигнатурой (§
8.4.2) и возвращаемым типом. Так как
первоначальный метод move не доступен в
пакете morepoints, super не может
использоваться:
package morepoints;
public class PlusPoint extends points.Point {
public void move(int dx, int dy) {
super.move(dx, dy); // compile-time error
moveAlso(dx, dy);
}
}
Так как метод move класса Point не
перегружается методом move класса PlusPoint,
метод moveAlso в Point никогда не
вызывает метод move в PlusPoint.
Таким образом, если вы удаляете super.move из PlusPoint
и выполняете тестирующую программу:
import points.Point;
import morepoints.PlusPoint;
class Test {
public static void main(String[] args) {
PlusPoint pp = new PlusPoint();
pp.move(1, 1);
}
}
то она заканчивается успешно. Если метод move
в Point был перегружен методом move в PlusPoint,
тогда эта программа вызывала бы бесконечную
рекурсию до тех пор, пока бы не произошла ошибка
по переполнению стека.
Член или конструктор public-класса
доступен в пакете, в котором он объявлен и в любом
другом пакете, который имеет доступ к пакету, где
содержится его объявление (§ 7.4.4).
Например, в модуле компиляции:
package points;
public class Point {
int x, y;
public void move(int dx, int dy) {
x += dx; y += dy;
moves++;
}
public static int moves = 0;
}
public-класс Point в качестве членов с
модификатором public имеет метод move и
поле moves. Эти public-члены доступны в
любом другом пакете, который имеет доступ к
пакету points. Поля x и y
объявлены без модификатора public и поэтому
доступны только внутри пакета points.
Рассмотрите этот пример, где объявляется пакет points:
package points;
public class Point {
protected int x, y;
void warp(threePoint.Point3d a) {
if (a.z > 0) //ошибка времени компиляции: a.z не доступен
a.delta(this);
}
}
и пакет threePoint:
package threePoint; import points.Point;
public class Point3d extends Point {
protected int z;
public void delta(Point p) {
p.x += this.x; // ошибка времени компиляции: p.x не доступен
p.y += this.y; // ошибка времени компиляции: p.y не доступен
}
public void delta3d(Point3d q) {
q.x += this.x;
q.y += this.y;
q.z += this.z;
}
}
который определяет класс Point3d. В данном
примере в методе delta происходит
ошибка времени компиляции: этот метод не имеет
доступа к членам x и y параметра p,
потому что Point3d (класс, в котором
происходит обращение к полям x и
у) является подклассом Point (класс, в котором x
и y объявлены) и не включает
реализацию класса Point (тип
параметра p). Метод delta3d
может иметь доступ к членам параметра q, потому что класс Point3d
является подклассом Point и включает
реализацию класса Point3d.
В методе delta можно было бы попробовать
привести (§ 5.4, § 15.15) параметр к Point3d,
но это не помогло бы, приводя к исключению, если
класс p во время выполнения
не являлся бы Point3d.
В методе warp также происходит ошибка
времени компиляции: невозможен доступ к полю z параметра а,
потому что класс Point (класс, в котором
происходит обращение к полю z) содержится
в классе Point, (тип параметра a),
но не является подклассом класса Point
(класс, в котором объявлен z).
Член или конструктор класса с модификатором
доступа private доступен только внутри тела
класса, в котором данный член объявлен, и не
наследуется подклассами. В примере:
class Point {
Point() { setMasterID(); }
int x, y;
private int ID;
private static int masterID = 0;
private void setMasterID() { ID = masterID++; }
}
private-члены ID, masterID, и setMasterID могут
использоваться только внутри тела класса Point.
К ним нельзя обращаться с помощью
квалифицированных имен, выражений доступа к
полю, или выражений вызова метода вне тела
объявлений класса Point.
Смотрите § 8.6.8 для примера, как
используется конструктор с модификатором private.
Каждый пакет, класс, интерфейс, тип массив и примитивный тип имеет полностью определенное имя. Из этого следует, что каждый тип, кроме пустого, имеет полностью определенное имя.
boolean, char,
byte, short, int, long, float,
или double.[]".Примеры:
long
является "long".java.lang является "java.lang",
потому что этот пакет является подпакетом lang
пакета java.Object,
который определен в пакете java.lang, является
"java.lang.Object".Enumeration,
который определен в пакете java.util, является
"java.util.Enumeration".double” является “double[]”.java.lang.String [][][][]]".В примере:
package points;
class Point { int x, y; }
class PointVec {
Point[] vec;
}
полностью определенным именем класса Point
является "points.Point"; полностью
определенным именем класса PointVec является
"points.PointVec"; и полностью определенным
именем типа поля vec класса PointVec
является "points.Point[]".
Ява-система и стандартные классы пытаются использовать всякий раз, когда это возможно, имена, выбранные в соответствии с соглашениями, представленными здесь. Эти соглашения делают программы более читабельными, и помогают избегать некоторых конфликтов имен.
Мы рекомендуем использовать эти соглашения во
всех программах на языке Ява. Однако, не стоит
слепо следовать этим правилам, если в вашем
случае следует поступить иначе. Так, например,
методы sin и cos класса java.lang.Math
имеют обычные математические названия, хотя эти
имена методов не соответствуют соглашениям
языка Ява, потому что они короткие и не являются
глаголами.
Имена пакетов, которые должны будут стать
широко доступными, следует формировать, как
описано в § 7.7. Такие имена всегда являются
именами с квалификациями, у которых первый
идентификатор, который называют областью
Интернета, состоит из двух или трех букв верхнего
регистра, например, COM, EDU, GOV, MIL,
NET, ORG, или двухбуквенного ISO-кода
страны, например, UK или JP. Ниже
приведены примеры гипотетических уникальных
имен, которые могли бы быть сформированы
согласно этому соглашению:
COM.JavaSoft.jag.Oak ORG.NPR.pledge.driver UK.ac.city.rugby.game
У имен пакетов, предназначенных только для
локального использования, первый идентификатор
должен начинаться с буквы нижнего регистра, но
такой первый идентификатор не должен совпадать с
идентификатором java; имена пакетов,
которые начинаются с идентификатора java,
зарезервированы для обозначения стандартных
пакетов Ява-системы.
Когда имена пакетов встречаются в выражениях:
import-объявления (§ 7.5) для
того, чтобы сделать имя типа, объявленное в этом
пакете, доступным.Имена классовых типов должны быть существительными или фразами с существительными, не очень длинными, в составном варианте каждое слово должно начинаться с прописной буквы. Например:
ClassLoaderSecurityManagerThreadDictionaryBufferedInputStream
Аналогично, имена интерфейсных типов должны
быть короткими и понятными, не очень длинными, в составном варианте каждое
слово должно начинаться с прописной буквы. Имя
может быть существительным или фразой с
существительными, которая
свойственна для случая, когда интерфейс
используется, как абстрактный суперкласс,
например java.io.DataInput и java.io.DataOutput; или
это может быть прилагательное, описывающее режим
работы, что касается интерфейсов java.lang.Runnable
и java.lang.Cloneable.
Скрытие имен классовых и интерфейсных типов довольно редко. Имена полей, параметров, и локальных переменных обычно не скрывают имена типов, потому что они традиционно начинаются с маленькой буквы, в то время как имена типов традиционно начинаются с заглавной буквы.
Имена методов должны быть глаголами или фразами с глаголами, в составном варианте, должны начинаться с маленькой буквы, а последующие слова должны начинаться с прописных букв. Имеются некоторые дополнительные специфические соглашения для имен методов :
get и set для атрибута, который является
переменной V должны быть getV и setV.
Например - методы getPriority (§20.20.22) и setPriority (§20.20.23)
классаjava.lang.Thread.length, как в классе java.lang.String
(§20.12.11).isV.
Например - метод isInterrupted класса java.lang.Thread (§20.20.32)toString класса java.lang.Objectt (§20.1.2)
и методы toLocaleString (§21.3.27) и toGMTString (§21.3.28)
класса java.util.Date.Если всякий раз по возможности организовывать
имена методов в новом классе, руководствуясь
именами в уже существующем классе, который
является подобным, особенно в классе из
стандартных Java Application Programming Interface классов,
то это облегчит их использование.
Имена методов не могут скрывать или быть скрытыми другими именами (§6.5.6).
Имена полей, которые объявлены без
модификатора final,
должны начинаться составном варианте с
маленькой буквы и первые буквы последующих слов
должны быть прописными. Обратите внимание, что
хорошо разработанные классы в Яве имеют очень
немного public или protected полей, кроме
полей которые являются константами (final, static поля) (§6.8.5).
Поля должны иметь имена, которые являются
существительными, фразами с существительными,
или аббревиатурами для существительных.
Например – поля buf, pos и count
класса java.io.ByteArrayInputStream (§22.6) и поле bytesTransferred
класса java.io.InterruptedIOException (§22.30.1).
Скрытие имен полей очень редко.
import (§7.5)
может обычно использоваться, чтобы сделать
доступным имена типа, объявленные в этом пакете.Имена констант в интерфейсных типах должны
быть, и final переменные классовых типов могут быть,
последовательностью из одного или более слов,
акронимов или аббревиатур, запись которых
состоит только из заглавных букв с компонентами,
отделяемыми символом подчеркивания "_".
Имена констант должны быть понятны и не должны
содержать слишком много аббревиатур.
Традиционно они могут быть любой частью речи.
Примеры имен для констант : MIN_VALUE,
MAX_VALUE, MIN_RADIX и MAX_RADIX класса java.lang.Character.
Группа констант, которые представляют альтернативные значения множества, или, менее часто, при наложении маски битов в целочисленном значении, иногда полезно определять общим акронимом как префиксная часть имени, как в:
interface ProcessStates {
int PS_RUNNING = 0;
int PS_SUSPENDED = 1;
}
Скрытие имен констант очень редко:
Локальная переменная и имена параметра должны быть короткими, однако имеющими смысловое значение. Они часто представляют собой короткие последовательности маленьких букв, которые не являются словами. Например:
cp для переменной, являющейся
ссылкой на ColoredPoint .buf,
являющаяся указателем на buffer некоторого
вида
inиout, всякий раз, когда предполагается некоторый ввод и вывод по образцу полейjava.lang.Systemoffиlen, всякий раз, когда предполагается смещение и длина по образцу параметров для методов чтения и записи интерфейсовDataInputиDataOutputofjava.io
Односимвольных локальных переменных или имен параметра следует избегать, кроме как для временных переменных и переменных цикла, или где переменная указывает на непримечательное значение данного типа. Стандартные односимвольные имена:
b для bytec для chard для doublee для исключенияf для floati, j и k для целых чиселl для longO для ObjectS для Stringv для произвольного значения некоторого
типаСледует избегать имен локальных переменных или имен параметров, которые состоят только из двух или трех заглавных букв, чтобы не возникло потенциальных конфликтов с начальными кодами страны и именами домена, которые являются первым компонентом однозначных имен пакета (§7.7).
Содержание
| Предыдущая | Следующая