Вы достигли нового уровня

Уровень 15

1. Переопределение методов, реализация абстрактных методов и интерфейсов..

- Я расскажу тебе про «модификаторы доступа». Когда-то я уже рассказывал про них, но повторение – мать учения.

Ты можешь управлять доступом (видимостью) методов и переменных твоего класса из других классов. Модификатор доступа отвечает на вопрос «Кто может обращаться к данному методу/переменной?». Каждому методу или переменной можно указывать только один модификатор.

1) Модификатор «public».

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

2) Модификатор «private».

К переменной, методу или классу, помеченному модификатором private, можно обращаться только из того же класса, где он объявлен. Для всех остальных классов помеченный метод или переменная – невидимы. Это самая высокая степень закрытости – только свой класс. Такие методы не наследуются и не переопределяются. Доступ к ним из класса-наследника также невозможен.

3) «Модификатор по умолчанию».

Если переменная или метод не помечены никаким модификатором, то считается, что они помечены «модификатором по умолчанию». Переменные и методы с таким модификатором видны всем классам пакета, в котором они объявлены, и только им. Этот модификатор еще называют «package» или «package private», намекая, что доступ к переменным и методам открыт для всего пакета, в котором находится их класс

4) Модификатор «protected».

Этот уровень доступа чуть шире, чем package. К переменной, методу или классу, помеченному модификатором protected, можно обращаться из его же пакета (как package), но еще из всех классов, унаследованных от текущего.

Таблица с пояснением:

      
Тип видимости Ключевое слово Доступ
Свой класс Свой пакет Класс - наследник Все классы
Закрытый private Есть Нет Нет Нет
Пакет (нет модификатора) Есть Есть Нет Нет
Защищенный protected Есть Есть Есть Нет
Открытый public Есть Есть Есть Есть

Есть способ, чтобы легко запомнить эту таблицу. Представь себе, что ты составляешь завещание и делишь все вещи на четыре категории. Кто может пользоваться твоими вещами?

Кто имеет доступМодификаторПример
Только я сам private Личный дневник
Семья (нет модификатора) Семейные фотографии
Семья и наследники protected Фамильное поместье
Все public Мемуары

- Если представить, что классы, лежащие в одном пакете, – это одна семья, то очень даже похоже.

- Хочу также рассказать тебе несколько интересных нюансов насчет переопределения методов.

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

Допустим, у тебя есть код:

Код
class Cat
{
 public String getName()
 {
  return "Васька";
 }
}

И ты решил унаследовать от него класс тигр и добавить новому классу интерфейс

Код
class Cat
{
 public String getName()
 {
   return "Васька";
 }
}
interface HasName
{
 String getName();
 int getWeight();
}
class Tiger extends Cat implements HasName
{
 public int getWeight()
 {
  return 115;
 }

}

Если ты просто реализуешь все недостающие методы, которые тебе подскажет Intellij IDEA, то можешь потом долго искать ошибку.

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

- Не вижу в этом ничего страшного.

- Это не очень плохо, это скорее потенциальное место для ошибок.

Но может быть еще хуже:

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

Оказывается, ты не всегда можешь унаследоваться от нескольких интерфейсов. Вернее унаследоваться можешь, а вот корректно их реализовать – нет. Посмотри на пример, оба интерфейса требуют, чтобы ты реализовал метод getValue(), и не ясно, что он должен возвращать: вес(weight) или размер(size). Это довольно-таки неприятная вещь, если тебе придется с ней столкнуться.

- Да, согласен. Хочешь реализовать метод, а не можешь. Вдруг ты уже унаследовал метод с таким же именем от базового класса. Обломись.

- Но есть и приятные новости.

2) Расширение видимости. При переопределении типа разрешается расширить видимость метода. Вот как это выглядит:

КодПояснение
class Cat
{
 protected String getName()
 {
  return "Васька";
 }
}
class Tiger extends Cat
{
 public String getName()
 {
  return "Василий Тигранович";
 }
}
Мы расширили видимость метода с 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(); //вызов protected метода
}

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

- Не уверен, что понял полностью, но то, что так можно делать, запомню.

3) Расширение типа результата.

В перегруженном методе мы можем поменять тип результата, расширив его.

КодПояснение
class Cat
{
 public Cat parent;
 public Cat getMyParent()
 {
  return this.parent;
 }
 public 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 был расширен.

«Старый код» как работал так и работает.
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;
}

Т.е. ни о каком нарушении безопасности и/или контроля приведения типов нет речи.
public static void main(String[] args)
{
 Tiger parent = new Tiger();

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

Благодаря перегрузке вызовется правильный метод setMyParent.

И нет ничего страшного при вызове метода getMyParent, т.к. его результат, хоть и класса Tiger, все равно сможет отлично присвоиться в переменную myParent базового класса (Cat).

Объекты Tiger можно смело хранить как в переменных класса Tiger, так и в переменных класса Cat.

- Ага. Я понял. Надо при переопределении методов беспокоится о том, как все это будет работать, если мы передадим наши объекты в код, который умеет обращаться только с базовым классом, и ничего о нашем классе не знает.

- Именно! Тогда вопрос на засыпку, почему нельзя сузить тип результата при переопределении метода?

- Это же очевидно, тогда перестанет работать код в базовом классе:

КодПояснение проблемы
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 "я - сирота";
 }
}
Мы переопределили метод getMyParent и сузили тип его результата.

Тут все отлично.
public static void main(String[] args)
{
 Tiger parent = new Tiger();

 Cat me = new Tiger();
 Cat myParent = me.getMyParent();
}
Тогда у нас перестанет работать этот код.

Метод getMyParent может вернуть любой объект типа Object, т.к. на самом деле он вызывается у объекта типа Tiger.

А у нас нет проверки перед присваиванием. Тогда вполне возможно, что переменная myParent типа Cat будет хранить ссылку на строку.

- Отличный пример, Амиго!

В Java перед вызовом метода не проверяется, есть ли такой метод у объекта или нет. Все проверки происходит во время выполнения. И [гипотетический] вызов отсутствующего метода, скорее всего, приведет к тому, что программа начнет выполнять байт-код там, где его нет. Это, в конце концов, приведет к фатальной ошибке, и операционная система принудительно закроет программу.

- Ничего себе. Буду знать.

2. Задачи

- Вчера ко мне в дом ворвались грабители, подняли все с ног на голову, перелопатили весь дом, деньги искали.

- Какой ужас, Диего. Что ты сделал?

- Ничего, поржал с них и начал вместе с ними деньги искать. Ха-ха.

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

Задачи
1. Расставить интерфейсы

1. Добавить все возможные интерфейсы из Movable, Sellable, Discountable в класс Clothes.
2. Реализовать их методы.
2. ООП - наследование животных

1. Создать public static класс Goose(Гусь).
2. Создать public static класс Dragon(Дракон).
3. Унаследовать класс Goose от BigAnimal или SmallAnimal, подумать, какой логически больше подходит.
4. Унаследовать класс Dragon от BigAnimal или SmallAnimal, подумать, какой логически больше подходит.
5. В классах Goose и Dragon переопределить метод String getSize(), расширить видимость до максимальной.
6. В классе Goose метод getSize должен возвращать строку "Гусь маленький, " + [getSize родительского класса].
7. В классе Dragon метод getSize должен возвращать строку "Дракон большой, " + [getSize родительского класса].
3. ООП - машинки

1. Для вывода использовать можно только переменные из класса Constants.
2. В классе Ferrari реализуйте метод printlnDesire, чтобы он выводил на экран "Я хочу ездить на Феррари".
3. В классе Lanos реализуйте метод printlnDesire, чтобы он выводил на экран "Я хочу ездить на Ланосе".
4. Создайте public static класс LuxuriousCar(РоскошнаяМашина).
5. Создайте public static класс CheapCar(ДешеваяМашина).
6. Унаследуйте Ferrari и Lanos от CheapCar и LuxuriousCar, подумайте, какой класс для кого.
7. В классе LuxuriousCar реализуйте метод printlnDesire, чтобы он выводил на экран "Я хочу ездить на роскошной машине".
8. В классе CheapCar реализуйте метод printlnDesire, чтобы он выводил на экран "Я хочу ездить на дешевой машине".
9. В класах LuxuriousCar и CheapCar для метода printlnDesire расставьте различными способами модификаторы доступа так, чтобы в классах Ferrari и Lanos выполнялось расширение видимости.
4. ООП - книги

1. Создайте public static класс MarkTwainBook, который наследуется от Book. Имя автора [Mark Twain]. Параметр конструктора - имя книги.
2. В классе MarkTwainBook реализуйте все абстрактные методы.
3. Для метода getBook расширьте тип возвращаемого результата.
4. Создайте по аналогии AgathaChristieBook. Имя автора [Agatha Christie].
5. В классе Book реализуйте тело метода getOutputByBookType так, чтобы он возвращал:
    5.1. agathaChristieOutput для книг Агаты Кристи;
    5.2. markTwainOutput для книг Марка Твена.
5. ООП - исправь ошибки в наследовании

Исправь метод containsBones и всю связанную с ним логику так, чтобы:
1. Поведение программы осталось прежним, т.е. она должна выдавать то же самое, что и выдает сейчас.
2. Метод containsBones должен возвращать тип Object и значение "Yes" вместо true, "No" вместо false.

3. Перегрузка методов

- Привет, Амиго! Пару дней назад я тебе рассказывал о перегрузке методов. Ты все понял?

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

- Отлично! Я вижу, что ты хорошо выучил тот урок. Сегодня я хочу лишь немного расширить твои познания в этом деле. Как ты думаешь, какой метод будет вызван в каждом случае?

Код
class Cat
{
 public static void print(int n)
 {
  System.out.println(n);
 }
 public static void print(short n)
 {
  System.out.println(n);
 }
 public static void print(Integer n)
 {
  System.out.println(n);
 }
 public static void print(String s)
 {
  System.out.println(s);
 }
public static void main(String[] args)
{
  Cat.print(1);
  Cat.print((byte)1);
  Cat.print("1");
  Cat.print(null);
 }
}

- Затрудняюсь ответить.

- В первом случае 1 имеет тип int, у нас есть 100% совпадение метода, который принимает int. Будет вызван первый void print(int n).

Во втором случае, у нас нет метода, который принимает byte. Но есть два метода, которые принимают short и int. По стандарту расширения типов, byte сначала будет расширен до short, а уж затем расширен до int. Вердикт – будет вызван метода void print(short n).

В третьем случае у нас есть 100% совпадение метода, который принимает String. Будет вызван метод void print(String s).

В четвертом случае у нас неопределенность. null не имеет определенного типа, компилятор откажется компилировать этот код. В таком случае нужно написать Cat.print((Integer)null), чтобы вызвать третий метод и Cat.print((String)null), чтобы вызвать четвертый.

- Очень познавательно, спасибо.

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

Код
class Cat
{
 public static void print(short n)
 {
  System.out.println(n);
 }
 public static void print(Integer n)
 {
  System.out.println(n);
 }

 public static void main(String[] args)
 {
  Cat.print((byte)1);
  Cat.print(1);
 }
}

В первом случает, тип byte будет расширен до short и произойдет вызов первого метода: void print(short n).

Во втором случае неявно будет выполнено разрешенное преобразование от int к Integer, и произойдет вызов второго метода void print(Integer n).

- Неожиданно.

- Нет, неожиданно – это тут:

КодОписание
class Cat
{
 public static void print(Object o)
 {
  System.out.println(o);
 }
 public static void print(String s)
 {
  System.out.println(s);
 }

 public static void main(String[] args)
 {
  Cat.print(1);
  Cat.print(null);
 }
}
В первом случае int будет расширен до Integer, а так как нет метода для Integer, то вызовется наиболее подходящий метод, т.е. метод void print(Object o)

Во втором случае, ошибки компиляции не будет и вызовется метод void print(String s), что несколько не очевидно.

Надеюсь, Амиго ты понял, что лучше всего в таких случаях указать оператор преобразования типа, как в случае с (byte), чтобы точно знать, какой метод вызовется.

- Уж от чего, от чего, а от перегрузки методов я никаких проблем не ожидал. И тут – на тебе. Спасибо, Риша, буду держать ухо востро и не расслабляться.

4. Задачи

- Привет, Амиго!

Задачи
1. Что-то лишнее

1. Программа должна выводить следующее:
Это double
Это Object
Это double
Это Integer
Это double

2. Удалите реализации всех лишних методов
2. ООП - Перегрузка

Перегрузите метод printMatrix 8 различными способами. В итоге должно получиться 10 различных методов printMatrix.
3. ООП - Перегрузка - убираем лишнее

1. Подумайте, какая из реализаций метода print будет вызвана.
2. Удалите все лишние реализации метода print.
4. Мужчина или женщина?

1. В методе main создать 2 человека man и woman. man с типом Man, woman с типом Woman.
2. Изменить метод printName так, чтобы он выполнялся для man и woman.
3. Реализация метода printName должна быть одна.
5. Все лишнее - прочь!

Убрать в методе main лишние строки, для которых метод add нереализован.

5. Создание объекта. Порядок вызова конструкторов

- Привет, Амиго! Сейчас я расскажу тебе о процессе создания объекта.

- А что там сложного, дядя Риша? Написал new имя класса, указал правильный конструктор и готово!

- Это так. Но что при этом происходит внутри объекта?

- А что там происходит?

- А вот что! Объект создается в несколько этапов.

1) Сначала выделяется память под все переменные – поля класса.

2) Затем идет инициализация базового класса.

3) Потом всем переменным присваиваются значения, если они указаны.

4) И наконец, вызывается конструктор.

- Выглядит не очень сложно: сначала переменные, затем конструктор.

- Давай посмотрим, как это будет работать на примере двух классов:

ОбозначенияКодОписание


2 --------->
3 --------->




4 --------->
5 --------->





6 --------->



1 --------->
7 --------->
class Pet
{
 int x = 5, y = 5;
 int weight = 10;

 Pet(int x, int y)
 {
  this.x = x;
  this.y = y;
 }
}
class Cat extends Pet
{
 int tailLength = 8;
 int age;
 Cat(int x, int y, int age)
 {
  super(x, y);
  this.age = age;
 }
}
Мы создали два класса: Pet(животное) и Cat(кот).

В классе Cat мы видим явный вызов конструктора базового класса.
Он всегда должен быть в первой строке конструктора.

Вот что произойдет после выделения памяти:
1 – вызов конструктора базового класса
2, 3 – инициализация переменных в Pet
4, 5 – отработает код конструктора Pet

далее начнется процесс инициализации класса Cat
6 – инициализация переменных в Cat
7 – отработает код конструктора Cat
public static void main(String[] args)
{
 Cat cat = new Cat (50, 50, 5);
}

- Что-то немного запутанно. Почему так сложно?

- На самом деле не сложно, если знать что на самом деле происходит:

КодЧто происходит на самом деле
Если у класса нет ни одного конструктора, он будет создан автоматически.
Конструктор по умолчанию 
class Cat
{
 int x = 5;
 int y = 5;
}
class Cat
{
 int x = 5;
 int y = 5;
 public Cat()
 {
 }

}
Если не вызываешь конструктор базового класса, его вызов будет добавлен автоматически.
Вызов конструктора базового класса 
class Pet
{
 public String name;
}
class Pet extends Object
{
 public String name;
 public Pet()
 {
  super();
 }
}
class Cat extends Pet
{
 int x = 5;
 int y = 5;
}
class Cat extends Pet
{
 int x = 5;
 int y = 5;
 public Cat()
 {
  super();
 }

}
Инициализация переменных класса происходит в конструкторе.
Инициализация переменных класса 
class Cat
{
 int x = 5;
 int y = 5;
}
class Cat
{
 int x;
 int y;
 public Cat()
 {
  super();

  this.x = 5;
  this.y = 5;
 }
}
Как все это происходит на самом деле 
class Pet
{
 int x = 5, y = 5;
 int weight = 10;
 Pet(int x, int y)
 {
  this.x = x;
  this.y = y;
 }
}

class Cat extends Pet
{
 int tailLength = 8;
 int age;
 Cat(int x, int y, int age)
 {
  super(x, y);
  this.age = age;
 }
}
class Pet extends Object
{
 int x;
 int y;
 int weight;

 Pet(int x, int y)
 {
  //вызов конструктора базового класса
  super();
  //инициализация переменных
  this.x = 5;
  this.y = 5;
  this.weight = 10;

  //вызов кода конструктора
  this.x = x;
  this.y = y;

 }
}
class Cat extends Pet
{
 int tailLength;
 int age;
 Cat(int x, int y, int age)
 {
  //вызов конструктора базового класса
   super(x, y);
  //инициализация переменных
   this.tailLength = 8;
  //вызов кода конструктора
   this.age = age;
 }
}

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

- Молодец, Амиго, именно так!

6. Задачи

- Привет, Амиго!

Задачи
1. Максимально простой код 1

Упрости код - убери все наследования классов, которые и так будут добавлены автоматически при компиляции

PS: Взаимосвязь между объектами me и zapp - Has-a (использует)
2. Максимально простой код 2

- Посмотри, что, связанное с конструкторами, может быть добавлено при компиляции, но в этой программе уже присутствует.
- Упрости код - удали все конструкторы и вызовы конструкторов супер-классов, которые создаются и добавляются автоматически.

PS: Взаимосвязь между объектами классов NakedCat и NormalCat, SiamCat - Is-a (наследование)
3. Максимально простой код 3

Упрости код:
- убери всё то, что будет генерироваться автоматически при компиляции
- убери все наследования классов, которые и так будут добавлены автоматически при компиляции
- убери все конструкторы, которые создаются и добавляются автоматически.

7. Порядок загрузки классов, статические данные...

- Привет, Амиго! Слышала, Риша рассказал тебе новую и очень интересную тему?!

- Да, Ким.

- Моя тема будет не менее интересной. Я хочу рассказать тебе о загрузке в память классов.

Классы в Java – это файлы на диске, содержащие байт-код – скомпилированный Java-код.

- Да, я помню.

- Java-машина не загружает их без необходимости. Как только где-то в коде происходит обращение к классу, Java-машина проверяет – загружен ли он. И если нет, то загружает и инициализирует его.

Инициализация класса – это присваивание значений всех его статических переменных и вызов всех статических блоков.

- Похоже на вызов конструктора у объекта. А что такое статический блок?

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

Вот как это выглядит:

КодЧто происходит на самом деле
class Cat
{
 public static int catCount = 0 ;
 public static String namePrefix;

 static
 {
  Properties p = new Properties();
  p.loadFromFile("cat.properties");
  namePrefix = p.get("name-prefix");

 }

 public static int maxCatCount = 50;

 static
 {
  Properties p = new Properties();
  p.loadFromFile("max.properties");
  if (p.get("cat-max") != null)
   maxCatCount = p.getInt("cat-max");

 }

}
class Cat
{
 public static int catCount;
 public static String namePrefix;
 public static int maxCatCount;

//статические конструкторы в Java
//запрещены, но если бы они были, то все
//выглядело бы так

 public static Cat()
 {
  catCount = 0;

  Properties p = new Properties();
  p.loadFromFile("cat.properties");
  namePrefix = p.get("name-prefix");


  maxCatCount = 50;

  Properties p2 = new Properties();
  p2.loadFromFile("max.properties");
  if (p2.get("cat-max")!=null)
   maxCatCount = p2.getInt("cat-max");

 }
}

Это очень похоже на то, что происходит при вызове конструктора. Я даже записала это в виде несуществующего статического конструктора.

- Да, я все понял.

- Отлично.

8. Порядок инициализации переменных

- Привет, Амиго! Билаабо сегодня будет рассказывать о порядке инициализации переменных.

Представь, что ты видишь код. Какие значения получат переменные?

Код
class Cat
{
 public int a = 5;
 public int b = a + 1;
 public int c = a * b;
}
class Cat
{
 public int a = getSum();
 public int b = getSum() - a;
 public int c = getSum() - a - b;

 public int getSum()
 {
  return a + b + c;
 }
}

- А разве так можно?

- Конечно. Порядок объявления между методами и полями класса неважен.

Класс загружается сверху вниз, поэтому важно, чтобы поле класса обращалось только к уже загруженным другим полям. В примере поле b может обращаться к a, но ничего не знает о c.

- И что же будет?

- Когда переменные создаются, они получают дефолтовые значения (значения по умолчанию).

КодЧто происходит на самом деле
class Cat
{
 public int a = 5;
 public int b = a + 1;
 public int c = a * b;
}
class Cat
{
 public int a = 0;
 public int b = 0;
 public int c = 0;

 public Cat()
 {
  super();


  a = 5;
  b = a + 1; //5+1 = 6
  c = a * b; //5*6 = 30
 }
}
class Cat
{
 public int a = getSum();
 public int b = getSum() - a;
 public int c = getSum() - a - b;

 public getSum()
 {
  return a + b + c;
 }
}
class Cat
{
 public int a = 0;
 public int b = 0;
 public int c = 0;

 public Cat()
 {
  super();


  a = getSum(); //(a+b+c)=0
  b = getSum() - a; //(a+b+c)-a=b=0
  c = getSum() - a - b; //(a+b+c)-a-b=c=0
 }

 public getSum()
 {
  return a + b + c;
 }
}

- Ух ты! Оказывается, это так просто. Спасибо Билаабо, ты – настоящий друг!

- Ура! У Билаабо появился друг!

9. Задачи на порядок инициализации переменных

- Привет, Амиго!

Задачи
1. Статики 1

В статическом блоке инициализировать labels 5 различными парами.
2. Статики 2

1. В статическом блоке считайте две переменные с консоли А и В с типом int.
2. Не забыть про IOException, который надо обработать в блоке catch.
3. Закрыть поток ввода методом close().
3. Статики 3

1. Создать 7 public полей класса. Убедитесь, что они инициализируются дефолтными значениями.
- intVar с типом int
- doubleVar с типом double
- DoubleVar с типом Double
- booleanVar с типом boolean
- ObjectVar с типом Object
- ExceptionVar с типом Exception
- StringVar с типом String

2. В методе main вывести их значения в заданном порядке.
4. Статики и исключения

В статическом блоке выбросьте RuntimeException
В результате класс не загрузится, и вы увидите сообщение об ошибке вместо значения переменной B

Exception in thread "main" java.lang.ExceptionInInitializerError
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:186)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:113)
Caused by: java.lang.RuntimeException:
at com.javarush.test.level15.lesson09.task04.Solution.clinit(Solution.java:22)


Hint: Нужно погуглить причину, если получилось следующее:
java: initializer must be able to complete normally
java: unreachable statement
5. Статики 4

Внутри статического блока:
1. Создайте класс Cat
2. Внутри Cat создайте поле класса String name = "Пушок"
3. Создайте объект класса Cat - myCat
4. Присвойте статическому объекту Object cat объект, созданный в п.3
5. Выведите в консоль myCat.name

10. Ссылка на вики, переопределение методов/конструкторов

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

Ссылка на вики, переопределение методов/конструкторов

11. Хулио

- Привет, Амиго! Ты попкорн взял? Надеюсь, сегодня будет еще интереснее.

Оригинал видео на YouTube

12. Домашние и бонусные задания

- Здорово, боец!

- Здравия желаю, товарищ капитан!

- У меня для тебя шикарная новость. Вот тебе задания для закрепления полученных навыков. Выполняй их каждый день, и твои навыки будут расти с неимоверной скоростью. Они специально разработаны для выполнения их в Intellij IDEA.

Дополнительные задания для выполнения в Intellij Idea
1. Разные методы для разных типов

1. Считать с консоли данные, пока не введено слово "exit".
2. Для каждого значения вызвать метод print. Если значение:
2.1. содержит точку '.', то вызвать метод print для Double;
2.2. больше нуля, но меньше 128, то вызвать метод print для short;
2.3. больше либо равно 128, то вызвать метод print для Integer;
2.4. иначе, вызвать метод print для String.
2. Тренировка мозга

Найти логическую ошибку: метод doAction в реализации интерфейса Movable должен выдавать "moving".
Исправьте ошибку, при необходимости измените (отрефакторите) интерфейс Movable.
Результат вывода в консоль должен быть:
flying
moving
3. ООП - перегрузка

1.В классе Tree перегрузи метод info(Object s) два раза так, чтобы получилось три метода info(Object s), info(Number s), info(String s).
1.1. Разберись в методе info(Object s). Сделай по аналогии функционал новых методов.
1.2. Например, для метода info(Number s) результат может быть таким "Дерево № 123 , метод Number, параметр Short".
2. В блоке 2 должен вызываться метод info(Object s).
3. В блоке 3 должен вызываться метод info(Number s).
4. В блоке 4 должен вызываться метод info(String s).
4. Закрепляем Singleton pattern

1. Найти в гугле пример для - Singleton pattern Lazy initialization.
2. По образу и подобию в отдельных файлах создать три синглтон класса Sun, Moon, Earth.
3. Реализовать интерфейс Planet для классов Sun, Moon, Earth.
4. В статическом блоке класса Solution вызвать метод readKeyFromConsoleAndInitPlanet.
5. Реализовать функционал метода readKeyFromConsoleAndInitPlanet:
5.1. С консоли считать один параметр типа String.
5.2. Если параметр равен одной из констант интерфейса Planet, то создать соответствующий объект и присвоить его Planet thePlanet, иначе обнулить Planet thePlanet.
5.3. Сравнивать введенный параметр можно только с константами из Planet, нельзя создавать свои строки.
5. Перегрузка конструкторов

1. В классе Solution создайте по 3 конструктора для каждого модификатора доступа.
2. В отдельном файле унаследуйте класс SubSolution от класса Solution.
3. Внутри класса SubSolution создайте конструкторы командой Alt+Insert -> Constructors.
4. Исправьте модификаторы доступа конструкторов в SubSolution так, чтобы они соответствовали конструкторам класса Solution.
6. Порядок загрузки переменных

Разобраться, что в какой последовательности инициализируется.
Исправить порядок инициализации данных так, чтобы результат был следующим:
static void init()
Static block
public static void main
non-static block
static void printAllFields
0
null
Solution constructor
static void printAllFields
6
First name
7. Файл в статическом блоке

1. Инициализируй константу Constants.FILE_NAME полным путем к файлу с данными, который содержит несколько строк.
2. В статическом блоке считай из файла с именем Constants.FILE_NAME все строки и добавь их по-отдельности в List lines.
3. Закрой поток ввода методом close().
8. Дебаг, дебаг, и еще раз дебаг

Программа выводит 0 9, а должна 6 9. Найди одну! ошибку и исправь.
Используй дебаг. Для этого поставь breakpoint-ы(Ctrl+F8), потом зайди в меню Run -> Debug.
F9 - выполнение кода до следующего breakpoint-а
F8 - переход к следующей строке кода
9. Парсер реквестов

Считать с консоли URl ссылку.
Вывести на экран через пробел список всех параметров (Параметры идут после ? и разделяются &, например, lvl=15).
URL содержит минимум 1 параметр.
Если присутствует параметр obj, то передать его значение в нужный метод alert.
alert(double value) - для чисел (дробные числа разделяются точкой)
alert(String value) - для строк

Пример ввода:
http://javarush.ru/alpha/index.html?lvl=15&view&name=Amigo
Пример вывода:
lvl view name

Еще пример ввода:
http://javarush.ru/alpha/index.html?obj=3.14&name=Amigo
Пример вывода:
obj name
double 3.14
10. ООП - наследование

Исправить класс Hrivna так, чтоб избежать ошибку StackOverflowError, класс Money менять нельзя.

- Те задания были для духов. Для дедушек я добавил бонусные задания повышенной сложности. Только для старослужащих.

1. Осваивание статического блока

Задача: 1. В отдельных файлах создать классы Plane и Helicopter, реализующие интерфейс Flyable.
2. Класс Plane должен иметь 1 конструктор с параметром int – количество перевозимых пассажиров.
3. В статическом методе reset класса Solution:
3.1. Считать с консоли параметр типа String. Параметр может быть "plane" или "helicopter".
3.2. Если параметр равен "helicopter", то статическому объекту Flyable result присвоить
объект класса Helicopter.
3.3. Если параметр равен "plane", то считать второй параметр типа int,
статическому объекту Flyable result присвоить объект класса Plane.
4. В статическом блоке инициализировать Flyable result вызвав метод reset.
5. Закрыть поток ввода методом close().
2. Template pattern

Задача: 1. В отдельном файле создать класс DrinkMaker с тремя абстрактными методами:
- void getRightCup() - выбрать подходящую чашку
- void putIngredient() - положить ингредиенты
- void pour() - залить жидкостью
2. В классе DrinkMaker создать и реализовать метод void makeDrink(), который готовит напиток в такой последовательности: выбирает чашку, кладет ингредиенты, заливает жидкостью.
3. В отдельных файлах создать классы LatteMaker и TeaMaker, которые наследуются от DrinkMaker.
4. Распредели следующие фразы между всеми методами в классах LatteMaker и TeaMaker, различные фразы для различных методов.
5. Каждый метод должен выводить в консоль свою фразу не пересекаясь с другими методами.
6. Фразы:
"Заливаем водой"
"Берем чашку для латте"
"Насыпаем чай"
"Берем чашку для чая"
"Заливаем молоком с пенкой"
"Делаем кофе"
3. Факториал

Задача: Написать метод, который вычисляет факториал - произведение всех чисел от 1 до введенного числа включая его.
Пример: 4! = factorial(4) = 1*2*3*4 = 24
1. Ввести с консоли число меньше либо равно 150.
2. Реализовать функцию factorial.
3. Если введенное число меньше 0, то вывести 0.
0! = 1