Top level class (Обычный класс):
Nested class (Вложенный класс):
К классам верхнего уровня модификатор static неприменим.
Класс называется вложенным (Nested class), если он определен внутри другого класса.
Вложенный класс должен создаваться только для того, чтобы обслуживать обрамляющий его класс.
Если вложенный класс оказывается полезен в каком-либо ином контексте, он должен стать классом верхнего уровня.
Вложенные классы имеют доступ ко всем (в том числе приватным) полям и методам внешнего класса, но не наоборот. Из-за этого разрешения использование вложенных классов приводит к некоторому нарушению инкапсуляции.
Существуют четыре категории вложенных классов:
Такие категории классов, за исключением первого, также называют внутренними (Inner class).
Внутренние классы ассоциируются не с внешним классом, а с экземпляром внешнего.
Каждая из категорий имеет рекомендации по своему применению:
Статический вложенный класс имеет прямой доступ только к статическим полям обрамляющего класса.
Простой внутренний класс, может обратиться к любому полю внешнего класса напрямую. В случае, если у вложенного класса уже существует поле с таким же литералом, то обращаться к такому полю следует через ссылку на его экземпляр.
Например: Outer.this.field.
Local inner class (Локальный класс) - это вложенный класс, который может быть декларирован в любом блоке, в котором разрешается декларировать переменные.
Как и простые внутренние классы (Member inner class) локальные классы имеют имена и могут использоваться многократно.
Как и анонимные классы, они имеют окружающий их экземпляр только тогда, когда применяются в нестатическом контексте.
Это вложенный локальный класс без имени, который разрешено декларировать в любом месте обрамляющего класса, разрешающем размещение выражений.
Создание экземпляра анонимного класса происходит одновременно с его объявлением.
В зависимости от местоположения анонимный класс ведет себя как статический либо как нестатический вложенный класс - в нестатическом контексте появляется окружающий его экземпляр.
Перечисления представляют набор логически связанных констант.
Объявление перечисления происходит с помощью оператора enum, после которого идет название перечисления. Затем идет список элементов перечисления через запятую.
Каждый из них явно объявлен как открытый статический финальный член класса.
Если у какого-либо класса не определить конструктор, то компилятор сгенерирует конструктор без аргументов - так называемый «конструктор по умолчанию».
Если вы создали конструктор с аргументами, то конструктор по умолчанию использоваться не будет (если он не создан).
Приватный (помеченный ключевым словом private, скрытый) конструктор может использоваться публичным статическим методом генерации объектов данного класса.
Также доступ к нему разрешен вложенным классам и может использоваться для их нужд.
Запрещает создание экземпляра класса вне методов самого класса, например, чтобы гарантировать существование только одного объекта определённого класса, предположим какого-то ресурса, например БД.
У конструктора по умолчанию отсутствуют какие-либо аргументы.
Конструктор копирования принимает в качестве аргумента уже существующий объект класса для последующего создания его клона.
Конструктор с параметрами имеет в своей сигнатуре аргументы (обычно необходимые для инициализации полей класса).
private(приватный): члены класса доступны только внутри класса.
default, package-private, package level (доступ на уровне пакета): видимость класса/членов класса только внутри пакета. Является модификатором доступа по умолчанию.
protected(защищённый): члены класса доступны внутри пакета и в наследниках.
public (публичный): класс/члены класса доступны всем.
Во время наследования возможно изменения модификаторов доступа в сторону большей видимости (для поддержания соответствия принципу подстановки Барбары Лисков).
Модификатор, применяемый к полю, блоку, методу, внутреннему классу.
Данный модификатор указывает на привязку субъекта к текущему классу.
Модификатор final может применяться к переменным, параметрам методов, полям и методам класса или самим классам.
Статические методы не могут быть переопределены в точном смысле слова, но они могут скрыть родительские статические методы (затирать).
Да, могут быть перегружены. Мы можем иметь два или более статических метода с одинаковым именем, но с различиями во входных параметрах.
Да, могут.
При переопределении метода сужать модификатор доступа нельзя, т.к. это приведет к нарушению принципа подстановки Барбары Лисков. Расширение уровня доступа возможно.
Изменять тип возвращаемого значения при переопределении метода разрешено только в сторону сужения типа (вместо родительского класса - наследника).
При изменении типа, количества, порядка следования аргументов вместо переопределения будет происходить overloading (перегрузка) метода.
Секцию throws метода можно не указывать, но стоит помнить, что она остаётся действительной, если уже определена у метода родительского класса.
Также, возможно добавлять новые исключения, являющиеся наследниками от уже объявленных или исключения RuntimeException.
Порядок следования таких элементов при переопределении значения не имеет.
Да
Объект статического класса не хранит ссылку на конкретный экземпляр внешнего класса.
Объект статического вложенного класса вполне может существовать сам по себе.
Статический вложенный класс может обращаться только к статическим полям и методам внешнего класса.
Объекты статического класса не содержат ссылок на объекты внешнего класса. А самих объектов мы можем создать сколько угодно.
Класс помеченный модификатором abstract называется абстрактным классом.
Создавать экземпляры самого абстрактного класса не разрешается.
Такие классы могут выступать только предками для других классов.
При этом наследниками абстрактного класса могут быть как другие абстрактные классы, так и классы, допускающие создание объектов.
Метод помеченный ключевым словом abstract - абстрактный метод, т.е. метод, который не имеет реализации.
Если в классе присутствует хотя бы один абстрактный метод, то весь класс должен быть объявлен абстрактным.
Использование абстрактных классов и методов позволяет описать некий шаблон объекта, который должен быть реализован в других классах. В них же самих описывается лишь некое общее для всех потомков поведение.
Да
Да, может быть конструктор (для вызовов по цепочке из наследников).
Ключевое слово interface используется для создания полностью абстрактных классов.
Основное предназначение интерфейса - определять каким образом мы можем использовать класс, который его реализует.
Создатель интерфейса определяет имена методов, списки аргументов и типы возвращаемых значений, но не реализует их поведение. Все методы неявно объявляются как public.
Начиная с Java 8 в интерфейсах разрешается размещать реализацию методов по умолчанию default и статических static методов.
Интерфейс также может содержать и поля. В этом случае они автоматически являются публичными public, статическими static и неизменяемыми final.
Статические методы интерфейса похожи на методы по умолчанию, за исключением того, что для них отсутствует возможность переопределения в классах, реализующих интерфейс.
Статические методы в интерфейсе являются частью интерфейса без возможности использовать их для объектов класса реализации;
Методы класса java.lang.Object нельзя переопределить как статические;
Статические методы в интерфейсе используются для обеспечения вспомогательных методов, например, проверки на null, сортировки коллекций и т.д.
Абстрактный класс связывает между собой и объединяет классы, имеющие очень близкую связь (птицы: голуби, воробьи).
В то же время, один и тот же интерфейс могут реализовать классы, у которых вообще нет ничего общего (Flyable: птицы, наркоман, самолет).
Да.
И снова да.
Если класс реализует интерфейс, он может, но не обязан, реализовать методы по-умолчанию, уже реализованные в интерфейсе. Класс наследует реализацию по умолчанию.
Дефолтные методы можно переопределить.
Если некий класс реализует несколько интерфейсов, которые имеют одинаковый метод по умолчанию, то класс должен реализовать метод с совпадающей сигнатурой самостоятельно.
Ситуация аналогична, если один интерфейс имеет метод по умолчанию, а в другом этот же метод является абстрактным - никакой реализации по умолчанию классом не наследуется.
Метод по умолчанию не может переопределить метод класса java.lang.Object.
Помогают реализовывать интерфейсы без страха нарушить работу других классов.
Позволяют избежать создания служебных классов, так как все необходимые методы могут быть представлены в самих интерфейсах.
Дают свободу классам выбрать метод, который нужно переопределить.
Одной из основных причин внедрения методов по умолчанию является возможность коллекций в Java 8 использовать лямбда-выражения.
Через переопределение.
Сначала вызываются все статические блоки в очередности от первого статического блока корневого предка и выше по цепочке иерархии до статических блоков самого класса.
Затем вызываются нестатические блоки инициализации корневого предка, конструктор корневого предка и так далее вплоть до нестатических блоков и конструктора самого класса.
Parent static block(s) → Child static block(s) →
Grandchild static block(s) →
Parent nonstatic block(s) →
Parent constructor →
Child nonstatic block(s) → Child constructor →
Grandchild nonstatic block(s) → Grandchild constructor
Блоки инициализации представляют собой код, заключенный в фигурные скобки и размещаемый внутри класса вне объявления методов или конструкторов.
Существуют статические и нестатические блоки инициализации.
Блок инициализации выполняется перед инициализацией класса загрузчиком классов или созданием объекта класса с помощью конструктора.
Несколько блоков инициализации выполняются в порядке следования в коде класса.
Блок инициализации способен генерировать исключения, если их объявления перечислены в throws всех конструкторов класса.
Блок инициализации возможно создать и в анонимном классе.
class Dog {
private String name;
private String poroda;
private int age;
{
name = "Шарик";
poroda = "овчарка";
age = 2;
}
public Dog(String x, String y, int z){
name = x;
poroda = y;
age = z;
}
}
Статические блоки инициализация используются для выполнения кода, который должен выполняться один раз при инициализации класса загрузчиком классов, в момент предшествующий созданию объектов этого класса при помощи конструктора.
Такой блок (в отличие от нестатических, принадлежащих конкретному объекту класса) принадлежит только самому классу (объекту метакласса Class).
Если блок статический – ExceptionInInitializerError,
Если нестатический – вылетит само исключение.
Для нестатических блоков инициализации, если выбрасывание исключения прописано явным образом требуется, чтобы объявления этих исключений были перечислены в throws всех конструкторов класса. Иначе будет ошибка компиляции.
Для статического блока выбрасывание исключения в явном виде, приводит к ошибке компиляции.
В остальных случаях, взаимодействие с исключениями будет проходить так же как и в любом другом месте. Класс не будет инициализирован, если ошибка происходит в статическом блоке и объект класса не будет создан, если ошибка возникает в нестатическом блоке.
Если возникшее исключение - наследник RuntimeException:
Если возникшее исключение - наследник Error, то в обоих случаях будет выброшено java.lang.Error. Исключение: java.lang.ThreadDeath - смерть потока. В этом случае никакое исключение выброшено не будет.
Object это базовый класс для всех остальных объектов в Java.
Любой класс наследуется от Object и, соответственно, наследуют его методы.
Метод | Описание |
---|---|
equals() |
Служит для сравнения объектов по значению; == по ссылке public boolean equals(Object obj) |
hashCode() |
Возвращает hash код для объекта int hashCode() |
toString() |
Возвращает строковое представление объекта String toString() |
getClass() |
Возвращает класс объекта во время выполнения Class getClass() |
clone() |
Создает и возвращает копию объекта protected Object clone() |
notify() |
Возобновляет поток, ожидающий монитор void notify() |
notifyAll() |
Возобновляет все потоки, ожидающие монитор void notifyAll() |
wait() |
Остановка вызвавшего метод потока до момента пока другой поток не вызовет метод notify() или notifyAll() для этого объекта void wait() |
wait() |
Остановка вызвавшего метод потока на определённое время или пока другой поток не вызовет метод notify() или notifyAll() для этого объекта void wait(long timeout) |
wait() |
Остановка вызвавшего метод потока на определённое время или пока другой поток не вызовет метод notify() или notifyAll() для этого объекта void wait(long timeout, int nanos) |
finalize() |
Может вызываться сборщиком мусора в момент удаления объекта при сборке мусора. protected void finalize() |
HashCode - Общий контракт:
Equals - Основные принципы:
Рефлексивность
x.equals(x) = true
Симметричность
x.equals(y) = true
y.equals(x) = true
Транзитивность
x.equals(y) = true
y.equals(z) = true
x.equals(z) = true
Постоянство или Непротиворечивость
Результат одно и то же число пока объект не изменится
Для любых ссылок на значения х и у, если несколько раз вызвать х.equals(y), постоянно будет возвращаться значение true либо постоянно будет возвращаться значение false при условии, что никакая информация, используемая при сравнении объектов, не поменялась.
Если объекта нет - ложь
x.equals(null) = false
Для любой ненулевой ссылки на значение х выражение х.equals(null) должно возвращать false.
Метод equals() - определяет отношение эквивалентности объектов.
При сравнение объектов с помощью == сравнение происходит лишь между ссылками. При сравнении по переопределённому разработчиком equals() - по внутреннему состоянию объектов.
Для чего нужен метод hashCode()?
Метод hashCode() необходим для вычисления хэш кода переданного в качестве входного параметра объекта.
В Java это целое число, в более широком смысле - битовая строка фиксированной длины, полученная из массива произвольной длины. Этот метод реализован таким образом, что для одного и того же входного объекта, хэш код всегда будет одинаковым.
Почему нельзя реализовать hashcode() который будет гарантированно уникальным для каждого объекта?
Следует понимать, что в Java множество возможных хэш кодов ограничено типом int, а множество объектов ничем не ограничено.
Из-за этого, вполне возможна ситуация, что хэш коды разных объектов могут совпасть:
Есть ли какие-либо рекомендации о том, какие поля следует использовать при подсчете hashCode()?
Общий совет: выбирать поля, которые с большой долью вероятности будут различаться. Для этого необходимо использовать уникальные, лучше всего примитивные поля, например такие как id, uuid. При этом нужно следовать правилу, если поля задействованы при вычислении hashCode(), то они должны быть задействованы и при выполнении equals().
public boolean equals(Object obj) { return (this == obj);}
public native int hashCode();
native означает, что реализация данного метода выполнена на другом языке (здесь на C++) и обычно возвращает адрес объекта в памяти.
Метод equals() - определяет отношение эквивалентности объектов.
При сравнении объектов с помощью == сравнение происходит лишь между ссылками. При сравнении по переопределённому разработчиком equals() - по внутреннему состоянию объектов
Обход всех значимых полей класса и проверка того, что значение поля в текущем объекте и значение того же поля в проверяемом на эквивалентность аргументе соответствуют друг другу.
Если проверки для всех полей прошли успешно, возвращается результат true, в противном случае - false.
@Override
public boolean equals(Object obj) {
if (this == obj) // равен сам себе
return true;
if (obj == null) // не равен нулю
return false;
if (getClass() != obj.getClass()) // объекты одинакового класса
return false;
BlackBox other = (BlackBox) obj; // приведение
if (varA != other.varA) // для конкретных переменных класса
return false;
if (varB != other.varB)
return false;
return true;
}
Классы и методы, которые используют правила этого контракта могут работать некорректно.
Так для HashMap это может привести к тому, что пара «ключ-значение», которая была в нее помещена при использовании нового экземпляра ключа не будет в ней найдена.
В HashSet при добавлении объект сначала сравнивается хэш добавляемого и существующие (быстрая проверка очень экономит время), если хэш разный – то дальше сравнивается по equals.
Множитель создает зависимость значения хэш кода от очередности обработки полей, что в итоге порождает лучшую хэш функцию
Для equals всегда нужно сравнивать типы объектов через getClass.
instanceof не соблюдает правило симметрии в наследниках.
my.equals(child) == true
child.equals(my) == false // должно быть тру
Оператор instanceof нужен, чтобы проверить, был ли объект, на который ссылается переменная X, создан на основе какого-либо класса Y. Оператор instanceof проверяет именно происхождение объекта, а не переменной.