„Ще ви разкажа за « модификаторите на достъп ». Веднъж казах за тях, но повторението е стълб на ученето.“

Можете да контролирате достъпа (видимостта), който другите класове имат до методите и променливите на вашия клас. Модификатор за достъп отговаря на въпроса „Кой има достъп до този метод/променлива?“. Можете да посочите само един модификатор за всеки метод or променлива.

1) « публичен » модификатор.

Променлива, метод or клас, маркирани с публичен модификатор, могат да бъдат достъпни от всяко място в програмата. Това е най-високата степен на откритост: няма ограничения.

2) модификатор « частен ».

Променлива, метод or клас, маркирани с частния модификатор, могат да бъдат достъпни само в класа, където са декларирани. Маркираният метод or променлива е скрит от всички други класове. Това е най-високата степен на поверителност: достъпна само от вашия клас. Такива методи не се наследяват и не могат да бъдат заменени. Освен това те не могат да бъдат достъпни в наследствен клас.

3)  « Модификатор по подразбиране ».

Ако дадена променлива or метод не е маркиран с модификатор, тогава се счита, че е маркиран с модификатора "по подразбиране". Променливите и методите с този модификатор са видими за всички класове в пакета, където са декларирани, и само за тези класове. Този модификатор се нарича още " пакет " or " частен пакет " достъп, намеквайки за факта, че достъпът до променливи и методи е отворен за целия пакет, който съдържа класа.

4) модификатор « защитен ».

Това ниво на достъп е малко по-широко от пакета . Променлива, метод or клас, маркирани със защитения модификатор, могат да бъдат достъпни от неговия пакет (като „пакет“) и от всички наследени класове.

Тази table обяснява всичко:

Тип видимост Ключова дума Достъп
Твоят клас Вашият пакет Потомък Всички класове
Частно частен да Не Не Не
Пакет (без модификатор) да да Не Не
Защитен защитени да да да Не
Обществен публичен да да да да

Има начин лесно да запомните тази table. Представете си, че пишете завещание. Разделяте всичките си неща на четири категории. Кой може да използва вашите неща?

Кой има достъп Модификатор Пример
само  аз частен Личен дневник
семейство (без модификатор) Семейни снимки
Семейство и наследници защитени Семейно имение
всички публичен мемоари

„Много е като да си представим, че класовете в един и същи пакет са част от едно семейство.“

„Искам също така да ви кажа някои интересни нюанси относно методите за отмяна.“

1) Неявно прилагане на абстрактен метод.

Да приемем, че имате следния code:

Код
class Cat
{
 public String getName()
 {
  return "Oscar";
 }
}

И решихте да създадете клас Tiger, който наследява този клас, и да добавите интерфейс към новия клас

Код
class Cat
{
 public String getName()
 {
   return "Oscar";
 }
}
interface HasName
{
 String getName();
 int getWeight();
}
class Tiger extends Cat implements HasName
{
 public int getWeight()
 {
  return 115;
 }

}

Ако просто приложите всички липсващи методи, които IntelliJ IDEA ви казва да приложите, по-късно може да прекарате дълго време в търсене на грешка.

Оказва се, че класът Tiger има метод getName, наследен от Cat, който ще бъде приет като имплементация на метода getName за интерфейса HasName.

— Не виждам нищо страшно в това.

„Не е лошо, има вероятност да се промъкнат грешки.“

Но може да бъде още по-лошо:

Код
interface HasWeight
{
 int getValue();
}
interface HasSize
{
 int getValue();
}
class Tiger extends Cat implements HasWeight, HasSize
{
 public int getValue()
 {
  return 115;
 }
}

Оказва се, че не винаги можете да наследите от множество интерфейси. По-точно, можете да ги наследите, но не можете да ги приложите правилно. Вижте примера. И двата интерфейса изискват да внедрите метода getValue(), но не е ясно Howво трябва да върне: теглото or размера? Това е доста неприятно за справяне.

„Съгласен съм. Искате да приложите метод, но не можете. Вече сте наследor метод със същото име от базовия клас. Той е повреден.“

— Но има добри новини.

2) Разширяване на видимостта. Когато наследите тип, можете да разширите видимостта на метод. Ето How изглежда:

Java code Описание
class Cat
{
 protected String getName()
 {
  return "Oscar";
 }
}
class Tiger extends Cat
{
 public String getName()
 {
  return "Oscar Tiggerman";
 }
}
Разширихме видимостта на метода от protectedна public.
Код Защо това е „законно“
public static void main(String[] args)
{
 Cat cat = new Cat();
 cat.getName();
}
всичко е страхотно Тук дори не знаем, че видимостта е разширена в клас наследник.
public static void main(String[] args)
{
 Tiger tiger = new Tiger();
 tiger.getName();
}
Тук наричаме метода, чиято видимост е разширена.

Ако това не беше възможно, винаги бихме могли да декларираме метод в Tiger:
public String getPublicName()
{
super.getName(); // извикване на защитения метод
}

С други думи, не говорим за нарушение на сигурността.

public static void main(String[] args)
{
 Cat catTiger = new Tiger();
 catTiger.getName();
}
Ако всички условия, необходими за извикване на метод в базов клас ( Cat ), са удовлетворени, тогава те със сигурност са изпълнени за извикване на метода на наследствения тип ( Tiger ). Тъй като ограниченията върху извикването на метода бяха слаби, а не силни.

„Не съм сигурен, че разбрах напълно, но ще запомня, че това е възможно.“

3) Стесняване на типа връщане.

В отменен метод можем да променим връщания тип на стеснен референтен тип.

Java code Описание
class Cat
{
 public Cat parent;
 public Cat getMyParent()
 {
  return this.parent;
 }
 public void setMyParent(Cat cat)
 {
  this.parent = cat;
 }
}
class Tiger extends Cat
{
 public Tiger getMyParent()
 {
  return (Tiger) this.parent;
 }
}
Отменихме метода getMyParentи сега той връща Tigerобект.
Код Защо това е „законно“
public static void main(String[] args)
{
 Cat parent = new Cat();

 Cat me = new Cat();
 me.setMyParent(parent);
 Cat myParent = me.getMyParent();
}
всичко е страхотно Тук дори не знаем, че типът връщане на метода getMyParent е разширен в класа наследник.

Как работи и работи «старият code».

public static void main(String[] args)
{
 Tiger parent = new Tiger();

 Tiger me = new Tiger();
 me.setMyParent(parent);
 Tiger myParent = me.getMyParent();
}
Тук извикваме метода, чийто тип на връщане е стеснен.

Ако това не беше възможно, винаги бихме могли да декларираме метод в Tiger:
public Tiger getMyTigerParent()
{
return (Tiger) this.parent;
}

С други думи, няма нарушения на сигурността и/or нарушения на типа кастинг.

public static void main(String[] args)
{
 Tiger parent = new Tiger();

 Cat me = new Tiger();
 me.setMyParent(parent);
 Cat myParent = me.getMyParent();
}
И тук всичко работи добре, въпреки че разширихме типа на променливите до базовия клас (Cat).

Поради замяна се извиква правилният метод setMyParent.

И няма за Howво да се притеснявате, когато извиквате метода getMyParent , тъй като върнатата стойност, макар и от класа Tiger, все още може да бъде присвоена на променливата myParent на базовия клас (Cat) без ниHowви проблеми.

Обектите Tiger могат безопасно да се съхраняват Howто в променливи Tiger, така и в променливи Cat.

„Да. Разбрах. Когато заменяте методи, трябва да сте наясно How работи всичко това, ако предаваме нашите обекти на code, който може да обработва само базовия клас и не знае нищо за нашия клас.

"Точно! Тогава големият въпрос е защо не можем да стесним типа на връщаната стойност, когато заменяме метод?"

„Очевидно е, че в този случай codeът в базовия клас ще спре да работи:“

Java code Обяснение на проблема
class Cat
{
 public Cat parent;
 public Cat getMyParent()
 {
  return this.parent;
 }
 public void setMyParent(Cat cat)
 {
  this.parent = cat;
 }
}
class Tiger extends Cat
{
 public Object getMyParent()
 {
  if (this.parent != null)
   return this.parent;
  else
   return "I'm an orphan";
 }
}
Претоварихме метода getMyParent и стеснихме типа на неговата връщана стойност.

Тук всичко е наред.

public static void main(String[] args)
{
 Tiger parent = new Tiger();

 Cat me = new Tiger();
 Cat myParent = me.getMyParent();
}
Тогава този code ще спре да работи.

Методът getMyParent може да върне всяко копие на обект, тъй като всъщност се извиква на обект Tiger.

И нямаме проверка преди заданието. По този начин е напълно възможно променливата myParent от тип Cat да съхранява препратка към String.

„Прекрасен пример, Амиго!“

В Java, преди да бъде извикан метод, няма проверка дали обектът има такъв метод. Всички проверки се извършват по време на изпълнение. А [хипотетично] извикване на липсващ метод най-вероятно би накарало програмата да се опита да изпълни несъществуващ byte code. Това в крайна сметка би довело до фатална грешка и операционната система принудително ще затвори програмата.

"Уау. Сега знам."