Вложенные и внутренние классы в Java - Вопросы
Всего: 13 вопросов
1. Что такое внутренний класс в Java?
Что такое внутренний класс в Java?
Внутренний класс (inner class) — это нестатический класс, объявленный внутри другого класса, «класс в классе». Каждый объект внутреннего класса связан с объектом внешнего класса и имеет доступ ко всем его членам, включая private поля и методы.
2. Какие виды вложенных (внутренних) классов есть в Java?
Какие виды вложенных (внутренних) классов есть в Java?
- Обычный внутренний класс (member inner class)
- Локальный класс (method-local inner class)
- Анонимный класс (anonymous inner class)
- Статический вложенный класс (static nested class)
Первые три — внутренние классы. По спецификации JLS §8.1.3 статический вложенный класс внутренним не считается, так как не связан с объектом внешнего класса.
3. Где может быть объявлен обычный внутренний класс?
Где может быть объявлен обычный внутренний класс?
Внутри другого класса, но вне любого метода или блока кода — на одном уровне с полями и методами внешнего класса.
4. Street — внутренний класс класса Town. Допишите код, чтобы корректно создать объект класса Street.
public static void main(String[] args) {
Town town = new Town();
??? street = ???;
street.seeTown();
}
Street — внутренний класс класса Town. Допишите код, чтобы корректно создать объект класса Street.
public static void main(String[] args) {
Town town = new Town();
??? street = ???;
street.seeTown();
}public static void main(String[] args) {
Town town = new Town();
Town.Street street = town.new Street();
street.seeTown();
}
Для внутреннего класса нужен объект внешнего класса, поэтому используется синтаксис outer.new Inner().
5. Чем отличается создание объекта внутреннего (нестатического) класса внутри внешнего класса и за его пределами?
Чем отличается создание объекта внутреннего (нестатического) класса внутри внешнего класса и за его пределами?
- В нестатическом методе внешнего класса объект создаётся обычным способом: Street street = new Street(); — внешним объектом неявно становится this.
- В статическом методе внешнего класса (например, main) this нет, поэтому объект внешнего класса нужно указать явно: town.new Street() или new Town().new Street(). Простое имя Street при этом доступно.
- За пределами внешнего класса имя типа уточняется именем внешнего класса: Town.Street street = town.new Street();
6. Замените //1 и //2 так, чтобы выводились ссылки на объект внутреннего класса (Street) и на объект внешнего класса (Town).
public class Town {
public String name = "Kharkiv";
class Street {
public void watchTown() {
System.out.println("Town name is " + name);
System.out.println("Street class ref is " + //1);
System.out.println("Town class ref is " + //2);
}
}
}
Замените //1 и //2 так, чтобы выводились ссылки на объект внутреннего класса (Street) и на объект внешнего класса (Town).
public class Town {
public String name = "Kharkiv";
class Street {
public void watchTown() {
System.out.println("Town name is " + name);
System.out.println("Street class ref is " + //1);
System.out.println("Town class ref is " + //2);
}
}
}public class Town {
public String name = "Kharkiv";
class Street {
public void watchTown() {
System.out.println("Town name is " + name);
System.out.println("Street class ref is " + this);
System.out.println("Town class ref is " + Town.this);
}
}
}
Внутри внутреннего класса this ссылается на его собственный объект, а Town.this — на объект внешнего класса.
7. Где можно создать объект локального класса?
Где можно создать объект локального класса?
Только внутри того же метода (блока), в котором объявлен локальный класс, и только после (ниже) его объявления.
8. Где ошибка в коде?
2. Kangaroo kangaroo = new Kangaroo() {
3. public void movement() {
4. System.out.println("jump");
5. }
6. }
7. Tiger tiger = new Tiger();
Где ошибка в коде?
2. Kangaroo kangaroo = new Kangaroo() {
3. public void movement() {
4. System.out.println("jump");
5. }
6. }
7. Tiger tiger = new Tiger();В строке 6 не хватает точки с запятой. Объявление анонимного класса — это часть выражения присваивания, поэтому после закрывающей фигурной скобки нужна ; — };
9. Чем статический вложенный класс отличается от внутреннего класса в Java?
Чем статический вложенный класс отличается от внутреннего класса в Java?
Объект внутреннего класса всегда связан с объектом внешнего класса: он создаётся через outer.new Inner(), напрямую видит поля внешнего объекта и хранит скрытую ссылку на него. Статический вложенный класс такой связи не имеет: он создаётся через new Outer.Nested() и напрямую видит только статические члены внешнего класса (к остальным — через явную ссылку на объект). Если доступ к внешнему объекту не нужен, класс лучше объявить static — так не будет скрытой ссылки, которая может удерживать внешний объект в памяти.
10. Почему локальный и анонимный классы могут использовать только final или effectively final локальные переменные?
Почему локальный и анонимный классы могут использовать только final или effectively final локальные переменные?
Объект локального или анонимного класса может жить дольше вызова метода (его можно вернуть или передать в другой поток). Поэтому компилятор не хранит ссылку на саму переменную, а копирует её значение в скрытое поле класса. Если бы переменную можно было изменить, копия и оригинал разошлись бы. До Java 7 включительно переменная должна была быть явно объявлена final, начиная с Java 8 достаточно effectively final — значение не меняется после инициализации. Иначе компилятор выдаёт ошибку local variables referenced from an inner class must be final or effectively final.
11. Может ли внутренний класс содержать статические поля и методы? Что означает ошибка illegal static declaration in inner class?
Может ли внутренний класс содержать статические поля и методы? Что означает ошибка illegal static declaration in inner class?
До Java 15 включительно внутренние (обычные, локальные и анонимные) классы могли содержать только константы static final, а любое другое статическое поле или метод давали ошибку illegal static declaration in inner class. Начиная с Java 16 (JEP 395, вместе с record) ограничение снято: внутренние классы могут объявлять статические члены, а внутри метода можно объявлять локальные record, enum и интерфейсы. На старых JDK статический член нужно перенести во внешний класс или сделать вложенный класс static.
12. Когда вместо лямбда-выражения всё ещё нужен анонимный класс?
Когда вместо лямбда-выражения всё ещё нужен анонимный класс?
Лямбда заменяет анонимный класс только для функционального интерфейса (один абстрактный метод, например Runnable или Comparator). Анонимный класс нужен, если:
- нужно унаследовать класс, в том числе абстрактный, а не реализовать интерфейс;
- у интерфейса больше одного абстрактного метода;
- нужны собственные поля (состояние) или вспомогательные методы;
- this должен указывать на сам объект-реализацию: в анонимном классе this — анонимный объект, а в лямбде — объект внешнего класса.
13. Может ли внешний класс обращаться к private-членам своего внутреннего класса?
Может ли внешний класс обращаться к private-членам своего внутреннего класса?
Да. Доступ симметричен: внешний класс может читать и изменять private-поля и вызывать private-методы своего внутреннего (или статического вложенного) класса через ссылку на его объект, например street.house = 78;, а внутренний класс видит private-члены внешнего. Модификатор private ограничивает доступ на уровне класса верхнего уровня, а не отдельного вложенного класса.