Вложенные и внутренние классы в Java - Вопросы

Всего: 13 вопросов

1. 

Что такое внутренний класс в Java?

Внутренний класс (inner class) — это нестатический класс, объявленный внутри другого класса, «класс в классе». Каждый объект внутреннего класса связан с объектом внешнего класса и имеет доступ ко всем его членам, включая private поля и методы.

2. 

Какие виды вложенных (внутренних) классов есть в 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();
}
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);
        }
    }
}
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();

В строке 6 не хватает точки с запятой. Объявление анонимного класса — это часть выражения присваивания, поэтому после закрывающей фигурной скобки нужна ; — };

9. 

Чем статический вложенный класс отличается от внутреннего класса в Java?

Объект внутреннего класса всегда связан с объектом внешнего класса: он создаётся через outer.new Inner(), напрямую видит поля внешнего объекта и хранит скрытую ссылку на него. Статический вложенный класс такой связи не имеет: он создаётся через new Outer.Nested() и напрямую видит только статические члены внешнего класса (к остальным — через явную ссылку на объект). Если доступ к внешнему объекту не нужен, класс лучше объявить static — так не будет скрытой ссылки, которая может удерживать внешний объект в памяти.

10. 

Почему локальный и анонимный классы могут использовать только 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?

До 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-методы своего внутреннего (или статического вложенного) класса через ссылку на его объект, например street.house = 78;, а внутренний класс видит private-члены внешнего. Модификатор private ограничивает доступ на уровне класса верхнего уровня, а не отдельного вложенного класса.

Страница 1 из 1