Содержание | Предыдущая | Следующая


ГЛАВА 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) , или как часть квалифицированного имени поля или метода. Имя пакета может появиться в выражении только как часть квалифицированного имени для классового или интерфейсного типов.

6.1 Объявления

Объявление вводит объект в программу на языке Ява и включает идентификатор (§3.8), который может быть использован в имени для обращения к этому объекту. Объявленный объект может быть одним из следующих:

Конструкторы (§8.6) также вводятся объявлениями, но используют имя того класса, в котором они объявлены, а не новое имя.

6.2 Имена и идентификаторы

Имя используется, чтобы обратиться к объекту, объявленному в программе на языке Ява.

Имеются две формы имен: простое имя и квалифицированное имя. Простое имя представляет собой единственный идентификатор. Квалифицированное имя состоит из имени, знака ".", и идентификатора.

В определении значения имени (§6.5) , язык Ява, принимает во внимание контекст, в котором появляется имя. Ява различает по контексту, где имя должно означать (ссылаться на) пакет (§6.5.3) , тип (§6.5.4) , переменную или значение в выражении (§6.5.5) , или метод (§6.5.6).

Не все идентификаторы в программах на языке Ява, представляют собой часть имени. Идентификаторы также используются в следующих ситуациях:

В примере:


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 внутри помеченного оператора.

6.3 Область действия простого имени

Область действия описания представляет собой часть программы, внутри которой к объекту, объявленному описанием можно обратиться, используя простое имя:


	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, которое объявляется на три строки позже.

Эти правила означают, что описания классовых и интерфейсных типов не обязательно должны появляться перед тем, как эти типы используются.

В примере:

package points;

class Point {
	int x, y;
	PointList list;
	Point next;
}

class PointList {
	Point first;
}

использование PointList в классе Point корректно, потому что область действия классового типа по имени PointList включает как класс Point так и класс PointList, равно как и любые другие описания типа в других модулях компиляции пакета points.

6.3.1 Скрытые имена

Некоторые описания могут быть скрыты (§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

Этот пример объявляет:

Поскольку область действия переменной класса включает все тело класса (§8.2) ,переменная класса x будет доступна повсюду во всем теле метода main. В этом примере, однако, переменная класса x скрыта внутри тела метода main объявлением локальной переменной x.

Локальная переменная имеет область действия в оставшейся части блока, в котором она объявлена (§14.3.2); в данном случае это остаток метода main, а именно его инициализатор "0" и вызовы print и println.

Это означает, что:

Если следовать стандартным соглашениям именования (§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 обнаруживаются различные описания в зависимости от контекста использования:

Пример:

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, который может быть импортирован по шаблону.

6.4 Члены и наследование

Пакеты и ссылочные типы содержат члены. Члены пакета (§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.

6.4.1 Члены пакета

Член пакета (§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.

6.4.2 Члены классового типа

Члены классового типа (§8.2) - это поля и методы. Членами классового типа могут являться:

Конструкторы (§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.

6.4.3 Члены интерфейсного типа

Члены интерфейсного типа (§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.

6.4.4 Члены типа-массив

Членами типа массив (§10.7) являются следующие:

Пример:


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[].

6.5 Определение значения имени

Значение имени в языке Ява зависит от контекста, в котором оно используется. Определение значения имени требует трех шагов. Во-первых, в зависимости от контекста и синтаксиса имена делятся на пять категорий: PackageName, TypeName, ExpressionName, MethodName или AmbiguousName (неоднозначное имя). Во-вторых, имя, которое изначально классифицируется своим контекстом в качестве AmbiguousName, переклассифицируется по определенным правилам области действия, чтобы представлять собой PackageName, TypeName или ExpressionName. В-третьих, результирующая категория указывает конечное определение значения имени (или ошибку времени компиляции, если имя не имеет значения).

Использование контекста в языке Ява помогает минимизировать конфликты по именам между объектами различных видов. Такие конфликты бывают редко, если при присваивание имен следуют соглашениям, описанным в §6.8 . Тем не менее, конфликты могут появляться непреднамеренно как типы, разработанные разными программистами или разными организациями. Например, типы, методы, и поля могут иметь одинаковые имена. В языке Ява никогда не возникало проблем при различении метода и поля с одинаковыми именами, поскольку применение контекста всегда отличает вызов метода от вызова области.

6.5.1 Синтаксическая классификация имени согласно контексту

Имя синтаксически классифицируется в качестве PackageName в следующих контекстах:

Имя синтаксически классифицируется в качестве TypeName в следующих контекстах:

Имя синтаксически классифицируется в качестве ExpressionName в следующих контекстах:

Имя синтаксически классифицируется в качестве MethodName в следующем контексте:

Имя синтаксически классифицируется в качестве AmbiguousName в следующих контекстах:

6.5.2 Переклассификация контекстно-неоднозначных имен

AmbiguousName переклассифицируетя в следующих случаях:

В качестве примера рассмотрим следующий придуманный "библиотечный код":

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. Они затем переклассифицируются:

6.5.3 Значение имен пакетов

Значение имени классифицированного как PackageName определено следующим образом.

6.5.3.1 Простые имена пакетов

Если имя пакета состоит из одного Identifier, тогда этот идентификатор указывает пакет верхнего уровня с именем того же идентификатора. Если пакет с тем именем не доступен, как определено базовой системой (§7.4.3) , тогда происходит ошибка времени компиляции.

6.5.3.2 Составные имена пакетов

Если имя пакета имеет вид Q.Id, то Q должно также быть именем пакета. Имя пакета Q.Id обозначает пакет, который представляет собой член с именем Id внутри пакета с именем Q. Если Q не имя доступного пакета, или Id не имя доступного подпакета этого пакета, то происходит ошибка времени компиляции.

6.5.4 Значение имен типов

Значение имени классифицированного как TypeName определено следующим образом.

6.5.4.1 Простые имена типов

Если имя типа состоит из одного Identifier, тогда идентификатор должен появляться в области действий описания типа с этим именем, либо происходит ошибка времени компиляции. Возможно, что идентификатор появляется внутри области действий более чем одного типа с этим именем, в этом случае тип, указанный именем, определен следующим образом:

Такой порядок при рассмотрении описаний типа принят, чтобы выбрать наиболее подходящий из двух или более применимых описаний типа.

6.5.4.2 Составные имена типов

Если имя типа имеет вид 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

В этом примере:

6.5.5 Значение имен выражения

Значение имени классифицированного как ExpressionName определено следующим образом.

6.5.5.1 Простые имена выражения

Если имя выражения состоит из одного 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), тогда происходит ошибка времени компиляции.

В примере:


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

6.5.5.2 Квалифицированные имена выражения

Если имя выражения имеет форму Q.Id, тогда Q уже классифицировано как имя пакета, имя типа, или имя выражения:

Пример:


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.

6.5.6 Значение имен методов

MethodName может появляться только в выражении вызова метода (§ 15.11). Значение имени, классифицированного как MethodName, определено следующим образом.

6.5.6.1 Простые имена методов

Если имя метода состоит из отдельного Identifier, тогда Identifier - это имя метода, используемое для вызова метода. Identifier должен быть именем по крайней мере одного метода класса или интерфейса, в пределах которого он появляется. См. § 15.11 для дальнейшего обсуждения интерпретации простых имен методов в выражении вызова метода.

6.5.6.2 Квалифицированные имена методов

Если имя метода имеет форму Q.Id, тогда Q классифицируется уже как имя пакета, имя типа, или имя выражения. Если Q - имя пакета, тогда происходит ошибка времени компиляции. Иначе, Id- имя метода, используемое для вызова метода. Если Q - имя типа, тогда Id должен определять по крайней мере один static-метод типа Q. Если Q - имя выражения, и T - тип выражения Q; тогда Id должен определять по крайней мере один метод типа T. См. § 15.11 для дальнейшего обсуждения интерпретации квалифицированного имени метода в выражении вызова метода.

6.6 Квалифицированные имена и управление доступом

Квалифицированные имена - средства доступа к членам пакетов и ссылочных типов; средствами доступа являются выражения доступа к полю (§ 15.10) и выражения вызова метода (§ 15.11). Все три средства доступа синтаксически похожи тем, что появляется лексема “.”, которой предшествует некоторый указатель на пакет, тип или выражение, имеющее тип, за которой следует Identifier, и этот идентификатор является именем члена пакета или типа. Они все вместе известны как конструкции квалифицированного доступа.

Язык Ява предусматривает механизмы управления доступом, устраняющие зависимость пользователей, работающих с пакетами или классами, от деталей реализации этого пакета или класса. Управление доступом применяется к квалифицированному доступу и вызову конструкторов с помощью выражений создания экземпляров класса (§ 15.8), явных вызовов конструкторов (§ 8.6.5) и метода newInstance класса Class (§20.3.6).

Если доступ разрешен, тогда говорят, что объект является доступным.

6.6.1 Определение доступа

6.6.2 Модификатор доступа protected

Если член или конструктор объекта объявлен с модификатором доступа protected, то к нему можно обращаться вне пакета, в котором он объявлен только с помощью кода, ответственного за реализацию этого объекта. Пусть C - класс, в котором объявлен член или конструктор с модификатором доступа protected, и S - подкласс класса C, в котором используется этот член или конструктор с модификатором доступа protected. Тогда:

6.6.3 Пример управления доступом

В качестве примеров управления доступом, рассмотрим два модуля компиляции:

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:

Смотрите пример из § 6.6.7, где модификатор protected ограничивает доступ.

6.6.4 Пример: доступ к классам с модификаторами public и с модификаторами, не являющимися public

Если в классе отсутствует модификатор доступа 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.

6.6.5 Пример: поля, методы и конструкторы c доступом по умолчанию

Если ни один из модификаторов доступа (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, тогда эта программа вызывала бы бесконечную рекурсию до тех пор, пока бы не произошла ошибка по переполнению стека.

6.6.6 Пример: поля, методы и конструкторы с модификатором public

Член или конструктор 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.

6.6.7 Пример: поля, методы, и конструкторы с типом доступа protected

Рассмотрите этот пример, где объявляется пакет 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).

6.6.8 Пример: поля, методы, и конструкторы с типом доступа private

Член или конструктор класса с модификатором доступа 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.

6.7 Полностью определенные имена

Каждый пакет, класс, интерфейс, тип массив и примитивный тип имеет полностью определенное имя. Из этого следует, что каждый тип, кроме пустого, имеет полностью определенное имя.

Примеры:

В примере:

package points;

class Point { int x, y; }

class PointVec {
	Point[] vec;
}

полностью определенным именем класса Point является "points.Point"; полностью определенным именем класса PointVec является "points.PointVec"; и полностью определенным именем типа поля vec класса PointVec является "points.Point[]".

6.8 Соглашения по именования

Ява-система и стандартные классы пытаются использовать всякий раз, когда это возможно, имена, выбранные в соответствии с соглашениями, представленными здесь. Эти соглашения делают программы более читабельными, и помогают избегать некоторых конфликтов имен.

Мы рекомендуем использовать эти соглашения во всех программах на языке Ява. Однако, не стоит слепо следовать этим правилам, если в вашем случае следует поступить иначе. Так, например, методы sin и cos класса java.lang.Math имеют обычные математические названия, хотя эти имена методов не соответствуют соглашениям языка Ява, потому что они короткие и не являются глаголами.

6.8.1 Имена пакетов

Имена пакетов, которые должны будут стать широко доступными, следует формировать, как описано в § 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, зарезервированы для обозначения стандартных пакетов Ява-системы.

Когда имена пакетов встречаются в выражениях:

6.8.2 Имена классовых и интерфейсных типов.

Имена классовых типов должны быть существительными или фразами с существительными, не очень длинными, в составном варианте каждое слово должно начинаться с прописной буквы. Например:


ClassLoader
SecurityManager
Thread
Dictionary
BufferedInputStream

Аналогично, имена интерфейсных типов должны быть короткими и понятными, не очень длинными, в составном варианте каждое слово должно начинаться с прописной буквы. Имя может быть существительным или фразой с существительными, которая свойственна для случая, когда интерфейс используется, как абстрактный суперкласс, например java.io.DataInput и java.io.DataOutput; или это может быть прилагательное, описывающее режим работы, что касается интерфейсов java.lang.Runnable и java.lang.Cloneable.

Скрытие имен классовых и интерфейсных типов довольно редко. Имена полей, параметров, и локальных переменных обычно не скрывают имена типов, потому что они традиционно начинаются с маленькой буквы, в то время как имена типов традиционно начинаются с заглавной буквы.

6.8.3 Имена методов.

Имена методов должны быть глаголами или фразами с глаголами, в составном варианте, должны начинаться с маленькой буквы, а последующие слова должны начинаться с прописных букв. Имеются некоторые дополнительные специфические соглашения для имен методов :

Если всякий раз по возможности организовывать имена методов в новом классе, руководствуясь именами в уже существующем классе, который является подобным, особенно в классе из стандартных Java Application Programming Interface классов, то это облегчит их использование.

Имена методов не могут скрывать или быть скрытыми другими именами (§6.5.6).

6.8.4 Имена полей.

Имена полей, которые объявлены без модификатора final, должны начинаться составном варианте с маленькой буквы и первые буквы последующих слов должны быть прописными. Обратите внимание, что хорошо разработанные классы в Яве имеют очень немного public или protected полей, кроме полей которые являются константами (final, static поля) (§6.8.5).

Поля должны иметь имена, которые являются существительными, фразами с существительными, или аббревиатурами для существительных. Например – поля buf, pos и count класса java.io.ByteArrayInputStream (§22.6) и поле bytesTransferred класса java.io.InterruptedIOException (§22.30.1).

Скрытие имен полей очень редко.

6.8.5 Имена констант.

Имена констант в интерфейсных типах должны быть, и final переменные классовых типов могут быть, последовательностью из одного или более слов, акронимов или аббревиатур, запись которых состоит только из заглавных букв с компонентами, отделяемыми символом подчеркивания "_". Имена констант должны быть понятны и не должны содержать слишком много аббревиатур. Традиционно они могут быть любой частью речи. Примеры имен для констант : MIN_VALUE, MAX_VALUE, MIN_RADIX и MAX_RADIX класса java.lang.Character.

Группа констант, которые представляют альтернативные значения множества, или, менее часто, при наложении маски битов в целочисленном значении, иногда полезно определять общим акронимом как префиксная часть имени, как в:


interface ProcessStates {
	int PS_RUNNING = 0;
	int PS_SUSPENDED = 1;
}

Скрытие имен констант очень редко:

6.8.6 Локальная переменная и имена параметра.

Локальная переменная и имена параметра должны быть короткими, однако имеющими смысловое значение. Они часто представляют собой короткие последовательности маленьких букв, которые не являются словами. Например:

Односимвольных локальных переменных или имен параметра следует избегать, кроме как для временных переменных и переменных цикла, или где переменная указывает на непримечательное значение данного типа. Стандартные односимвольные имена:

Следует избегать имен локальных переменных или имен параметров, которые состоят только из двух или трех заглавных букв, чтобы не возникло потенциальных конфликтов с начальными кодами страны и именами домена, которые являются первым компонентом однозначных имен пакета (§7.7).


Содержание | Предыдущая | Следующая