Часть 1
Инкапсуляция и наследование
- Запомните разницу в терминах, определяющих отношения между классами. IS-A — это когда один класс наследует другой класс (extends) или реализует интерфейс (implements). HAS-A говорят, когда в одном классе используется ссылка на объект другого класса.
- Код, написанный по принципу инкапсуляции, содержит private переменные, доступ к которым осуществляется через методы (setVarName и getVarName).
- Наследование — базовая концепция, лежащая в основе полиморфизма, приведения типов, перегрузки и переопределения методов.
- В java все классы — потомки Object.
Полиморфизм
- Нельзя изменить тип ссылочной переменной. Но переменная может ссылаться на объект потомка своего класса. То есть на один объект могут ссылаться переменные разных типов. Главное, чтобы все эти типы были суперклассами для класса объекта. Следовательно, переменная типа Object может ссылаться на объект любого типа.
List<Integer> list; list = new ArrayList<Integer>(); //ArrayList и LinkedList реализуют интерфейс List, поэтому мы можем так делать list = new LinkedList<Integer>(); list = new String(); //Ошибка при компиляции
- Только тип ссылочной переменной определяет то, какие методы объекта вы можете вызвать. Иными словами, если вы хотите использовать методы, специфичные для данного класса, то вы не сможете вызвать их из переменной типа суперкласса.
public class Fruit { public void eat(){ System.out.println("NomNomNom"); } } class Apple extends Fruit{ public void grow(){ System.out.println("I'm growing"); } public static void main(String[] args) throws InterruptedException { Fruit fruit; fruit = new Apple(); fruit.eat(); //Метод eat описан в суперклассе, мы можем к нему обращаться fruit.grow(); //Ошибка! Метод grow специфичен для класса Apple, поэтому мы не можем обращаться к нему через переменную типа Fruit } }
- Полиморфизм работает только с методами объекта, но никак не со статичными методами.
Перегрузка и переопределение
- Методы могут быть перегружены и переопределены. Конструкторы — только перегружены.
- При переопределении методы
- Должны иметь неизменный список агрументов.
- Должны иметь неизменный тип возвращаемого значения.
- Уровень доступа не может быть ниже наследованного метода.
- Не должны бросать новых exception'ов.
- Переопределяться могут только наследованные методы.
- Получить доступ к переопределяемому методу можно так:
public class Fruit { public void eat(){ System.out.println("NomNomNom"); } } class Apple extends Fruit{ public void grow(){ System.out.println("I'm growing"); } public void eat(){ System.out.println("NomAppleNom"); } public void test(){ eat();//Вызовет переопределенный метод super.eat();//Вызовет метод суперкласса } }
- При перегрузке методы
- Должны иметь новый список аргументов.
- Могут иметь новый тип возвращаемого значения.
- Могут иметь новый уровень доступа без ограничений.
- Могут бросать новые exception'ы
- Методы суперкласса могут быть перегружены в наследнике.
- Полиморфизм применим только к переопределению — не к перезагрузке.
Типы возвращаемых значений
- Можно использовать null и массивы в качестве возвращаемого значения.
- Если тип возвращаемого значения примитивный (byte, int, float etc), то java выполнит неявное приведение типов, если это возможно.
public class Fruit { public int eat(){ byte b = 127; return b; //Компилятор автоматически расширит byte b до int. } public int grow(){ long l = 42; return l; //Ошибка! Несмотря на то что int вполне может представить число 42 без потерь, //нам необходимо использовать явное приведение return (int) l; } }
- Для void-методов можно использовать ключевое слово return, если вы хотите прервать метод.
- Если метод возвращает объект, в качестве возвращаемого значения можно использовать наследников класса этого объекта.
Конструкторы
- Каждый класс при создании объекта сначала вызывает конструктор. В каждый конструктор компилятор автоматически вставляет вызов конструктора суперкласса без параметров. Таким образом, сначала всегда выполняется конструктор Object, затем конструктор первого наследника Object и так далее.
- Конструкторы имеют такое же имя, как и у класса, любой уровень доступа, но не имеют возвращаемого значения.
- Если вы не создадите конструктор, его за вас создаст компилятор. Сгенерированый конструктор не имеет принимаемых аргументов, а его тело состоит из вызова конструктора суперкласса super();.
- Абстрактные классы имеют конструктор, а интерфейсы — нет.
- Если суперкласс не имеет конструктора без аргументов, то вам нужно явно объявить в конструкторе класса вызов super(); и передать необходимые переменные.
- Конструкторы никогда не наследуются, но они могут перегружаться
- Конструкторы можно напрямую вызывать только из других конструкторов при помощи this(); и super();.
- Пример:
public class Fruit { public Fruit(String str){ System.out.println("Fruit constructor"); } } class Apple extends Fruit{ public Apple(){ super("apple"); System.out.println("Apple's constructor"); } } public class GreenApple extends Apple{ public GreenApple(){ System.out.println("GreenApple's constructor"); }; Вывод: Fruit constructor Apple's constructor GreenApple's constructor }
Статичные методы и переменные
- Используйте статичные методы, если его поведение не зависит от состояния объекта.
- Статичные методы имеют доступ только к статичным переменным.
- Статичные члены привязаны не к объекту, а к классу.
- Статичные методы не могут быть переопределены.
Автор: jstudent