Содержание
| Предыдущая | СледующаяГЛАВА 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
Этот пример объявляет:
Test
static
) переменную
класса x
, которая представляет собой член
класса Test
main
, который представляет
собой член класса Test
args
метода main
x
метода 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.util
l также
объявляет класс 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.util
l доступный пакет
и затем смотрим, чтобы увидеть, доступен ли тип 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
(...), тогда доступ разрешается.- Если доступ осуществляется с помощью выражения создания экземпляра класса
new
T (...), тогда доступ не разрешается. (К конструктору с модификатором доступа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) для
того, чтобы сделать имя типа, объявленное в этом
пакете, доступным.Имена классовых типов должны быть существительными или фразами с существительными, не очень длинными, в составном варианте каждое слово должно начинаться с прописной буквы. Например:
ClassLoader
SecurityManager
Thread
Dictionary
BufferedInputStream
Аналогично, имена интерфейсных типов должны
быть короткими и понятными, не очень длинными, в составном варианте каждое
слово должно начинаться с прописной буквы. Имя
может быть существительным или фразой с
существительными, которая
свойственна для случая, когда интерфейс
используется, как абстрактный суперкласс,
например java.io.DataInput
и java.io.DataOutput
; или
это может быть прилагательное, описывающее режим
работы, что касается интерфейсов java.lang.Runnable
и java.lang.Cloneable
.
Скрытие имен классовых и интерфейсных типов довольно редко. Имена полей, параметров, и локальных переменных обычно не скрывают имена типов, потому что они традиционно начинаются с маленькой буквы, в то время как имена типов традиционно начинаются с заглавной буквы.
Имена методов должны быть глаголами или фразами с глаголами, в составном варианте, должны начинаться с маленькой буквы, а последующие слова должны начинаться с прописных букв. Имеются некоторые дополнительные специфические соглашения для имен методов :
get
и set
для атрибута, который является
переменной V должны быть get
V и set
V.
Например - методы getPriority
(§20.20.22) и setPriority
(§20.20.23)
классаjava.lang.Thread
.length
, как в классе java.lang.String
(§20.12.11).is
V.
Например - метод isInterrupted
класса java.lang.Thread
(§20.20.32)toString
класса java.lang.Object
t (§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.System
off
иlen
, всякий раз, когда предполагается смещение и длина по образцу параметров для методов чтения и записи интерфейсовDataInput
иDataOutput
ofjava.io
Односимвольных локальных переменных или имен параметра следует избегать, кроме как для временных переменных и переменных цикла, или где переменная указывает на непримечательное значение данного типа. Стандартные односимвольные имена:
b
для byte
c
для char
d
для double
e
для исключенияf
для float
i
, j
и k
для целых чиселl
для long
O
для Object
S
для String
v
для произвольного значения некоторого
типаСледует избегать имен локальных переменных или имен параметров, которые состоят только из двух или трех заглавных букв, чтобы не возникло потенциальных конфликтов с начальными кодами страны и именами домена, которые являются первым компонентом однозначных имен пакета (§7.7).
Содержание
| Предыдущая | Следующая