Pull to refresh

Comments 30

По-моему, всё описанное в этой статье обязан знать каждый Java-программист. Иначе он не Java-программист.
А вот, к примеру, то, что нельзя создавать массивы параметризованных типов или как приводятся массивы ссылочных типов — более специфические знания. Хотелось бы узнать побольше о подобных тонкостях.
Таким образом, я не Java программист :) Ну, а если серьезно, то я не согласен. К примеру, я пришел к Java из C++. Поэтому просто не задавался целью изучить нюансы языка. При этом вполне успешно зарабатываю себе на жизнь программированием на Java. А сертификация — хороший повод вернуться к основам.
Объявления String[] s[], t; и String[][] s, t; неравноправны. Поскольку в первом случае t — одномерный массив, а во втором — двумерный. Иными словами скобки справа от идентификатора относятся к нему и только к нему.
Простите, не понял вас. И в том, и вдругом случае объявляется самый что ни на есть двумерный массив.
  1.  
  2. public class Application {
  3.         public static void main(String[] args) {
  4.                 String[] s[] = new String [5][5];
  5.                 s[0][0] = new String("Hello");
  6.                 s[0][1] = new String ("World");
  7.                 System.out.println(s[0][1]);
  8.                 System.out.println(s[0][0]);
  9.         }
  10. };
  11.  

Ну попробуйте тогда скомпилировать следующий код:
int[] s[], t;
t = new int[1];
t[0] = new int[1];
Пропустил пару скобок, не тот, а этот код:
int[] s[], t;
t = new int[1][];
t[0] = new int[1];
или этот:
int[] s[], t;
t = new int[1][1];
Простите мое невежество конечно, но разве объявление нескольких переменных в одной строке не является дурным тоном, как раз по причине ненужного усложнения для восприятия и как следствие увеличения вероятности ошибок по невнимательности? (данное предложение очевидно является примером такого кода :) )
За такие объявления, что в моем примере, действительно нужно наказывать. Но не вижу ничего страшного таком коде — аналог структуры из C (конечно если это внутренний класс, или хотя бы не открытый):
class Coords { int x, y; }
Вы слишком категорично написали, что члены класса, объявленные как private не наследуются. Доступа к ним из подкласса действительно нет, но фактически в подклассе они есть. Или я запутался в терминологии (что значит наследуется / не наследуется), или одно из двух.
Вот, для примера
public class TestClass 
{
	private int var;
	
	public TestClass(int var)
	{
		this.var = var;
	}
	
	public void printVar()
	{
		System.out.println("var = " + var);
	}
}

public class TestNewClass extends TestClass
{
	private float newVar; 
	
	public TestNewClass(int var)
	{
		super(var);
		this.newVar = var * 2.0f;
	}
	
	public void printNewVar()
	{
		System.out.println("newVar = " + newVar);
	}
}

public static void main(String[] args)
{
	TestNewClass tnc = new TestNewClass(8);
	tnc.printVar();
	tnc.printNewVar();
}


исправно выводит
var = 8
newVar = 16.0
Вы использовали в выражении формальный параметр конструктора, а не поле суперкласса. Для проверки утверждения вам стоило бы использовать super.var, но это работать не будет т.к. поле приватное.
По всей видимости автор имел в виду, что приватные методы не наследуются. И это действительно так.
Под наследованием поля я имею в виду явное использование кода суперкласса в потомках. Как вам уже правильно заметили, esin, ваш код использует не поле суперкласса, а параметр, имеющий такое же имя. Кстати, хороший пример на внимательность. Код, получивший модификатор private, может быть выполнен только в рамках того же класса, где он написан. Ни потомки, ни коллеги по пакету его использовать не смогут.
Если я правильно помню, то java наследование преобразует в композицию. Таким образом инициация приватных полей родителя выполняется, но потомок его просто не видит.
Если вызвать конструктор родителя, то будет инициализация радителя. Нет — нет.
А конструктор родителя разве не вызывается при создании экземпляра потомка?
Вызывается конструктор по умолчанию. Поля родителя инициализируются или им, или значениями по умолчанию.

Можно вызвать super(..) для вызова кастомного конструктора. Только конструктор по умолчанию вызван не будет, а будет вместо него вызван кастомный.
Собственно напрашивается вопрос, как тогда можно создать экземпляр потомка, не проинициализировав предка. Не представляю как это возможно.
Тонкостей в модификаторах доступа не так уж и много. Те что вы перечислили — весьма очевидны, за исключением поведения protected-членов. Кстати, и this и super — ССЫЛКИ, на текущий класс и на суперкласс соответствено. И только по ним доступны защищённые поля, будь эти ссылки указаны явно или нет.
super ссылкой не является, мы можем его использовать для вызова конструктора суперкласса или для доступа к полю/методу, но мы не сможем присвоить super переменной, в отличии от this.

Object a = this; //Хорошо
Object b = super; //Ошибка компиляции
Под наследованием подразумевается также и возможность переопределения (overriding). Если метод не наследуется — следовательно его нельзя переопределить. Поля же нельзя преопределить, будь они хоть public. И на этот нюанс, насколько мне известно, в том экзамене есть ловушки.
Ньюанс в том, что поля в наследниках могут быть «скрыты». И на этом действительно можно погореть, потому как в реальной жизни вряд ли хватит ума создавать неприватные переменные, а уж тем более скрывать их в наследниках путем создания переменных с такими же именами.))
Да, действительно, я неправ. Оказалось надо было просто обратиться к официальной документации Java.

A subclass does not inherit the private members of its parent class. However, if the superclass has public or protected methods for accessing its private fields, these can also be used by the subclass.
Было бы интересно узнать, как создать в Java неизменяемый массив. Насколько я знаю, это невозможно, в отличие от коллекций.
Огромное спасибо за статью, увидел пару моментов, которых не знал :)
Не могли бы Вы чуть подробнее пояснить фразу «Кроме того, будучи однажды унаследованным классом вне пакета, поле становится закрытым для любых классов, за исключением дальнейших наследников.» Как — то я её совсем не понял. Ерунда какая-то. Класс не может знать, унаследован ли он кем-то и уж тем более в зависимости от этого менять модификатор доступа к полю на новый, ещё неведомый науке. Вы явно говорите про что — то другое и я не могу понять про что :)
Не за что :) Для этого и пишу. Смотрите, пусть у вас имеется базовый класс в пакете test.

  1.  
  2. package org.kimrgrey.scjp.test;
  3.  
  4. public class BaseClass {
  5.         protected int protectedValue;
  6.        
  7.         public BaseClass() {
  8.                 this.protectedValue = 1;
  9.         }
  10. }
  11.  


Как я говорил, поле protectedValue будет доступно всем членам пакета, даже если это не наследник.
Теперь вы создаете наследника в соседнем пакете — main.

  1.  
  2. package org.kimrgrey.scjp.main;
  3.  
  4. import org.kimrgrey.scjp.test.BaseClass;
  5.  
  6. public class OtherPackageSubclass extends BaseClass {
  7.         public OtherPackageSubclass() {
  8.                 this.protectedValue = 2;
  9.         }
  10. }
  11.  


Наследник к защищенному полю доступ все еще имеет. Его соседи по пакету — нет. То есть поведение модификатора protected после наследования слегка изменилось. Это больше не доступ в том же пакете + наследники. Это я и имел в виду.

При этом есть одна деталь. Классы из пакета test не смогут получить доступ к protected полю по ссылке. Вот такой код отлично работает:

  1.  
  2. package org.kimrgrey.scjp.test;
  3.  
  4. import org.kimrgrey.scjp.main.OtherPackageSubclass;
  5.  
  6. public class SamePackageAccess {
  7.         public SamePackageAccess() {
  8.                 OtherPackageSubclass a = new OtherPackageSubclass();
  9.                 System.out.println(a.protectedValue);
  10.         }
  11. }
  12.  


>> Классы из пакета test не смогут получить доступ к protected полю по ссылке

Опечатка. Классы из пакета test смогут получить доступ к protected полю по ссылке.
Спасибо, теперь понял что Вы имели ввиду, хотя изначальная формулировка простите просто жуть :)
А так — то всё логично и понятно если понимать, что protectedValue относится к классу BaseClass и то, что наследник имеет право пользования не делает его хозяином грубо говоря. При наследовании ничего не меняется, поле не становится «закрытым для любых классов». Кто его видел — продолжают видеть, кто не видел, тот продолжает не видеть. Разница только для самого наследника. Потому что это поле по прежнему принадлежит BaseClass, а не наследнику. Можно представить как композицию, в которой ключевое слово extends даёт инкапсулируещему объекту больше прав видеть определённые члены инкапсулируемого :)
Поправьте:
a.protectedValue = 10; // Line 10: по ссылке не могут обращаться даже наследники BaseClass
на
a.protectedValue = 10; // Line 12: по ссылке не могут обращаться даже наследники BaseClass
Спасибо, поправил. Хотя лучше все таки, на мой взгляд, замечания по опечаткам в ЛС.
Sign up to leave a comment.

Articles