Внутренние классы бывают следующих типов:
-вложенные (статические) внутренние классы;
-внутренние классы члены;
-локальные классы;
-анонимные классы.
Имеют доступ только к статическим полям и методам содержащего их класса. Для создания экземпляра не требуется объект внешнего класса.
class OuterClass { private static String secret = "secret string"; //Объявление вложенного класса public static class InnerStaticClass { static void printString(String str) { System.out.println(str); } void printReverseString(String str) { StringBuilder sb = new StringBuilder(str); System.out.println(sb.reverse()); } static void printSecret(){ System.out.println(secret); } } } public class TestClass{ public static void main(String[] args) { String string = "Мама мыла раму"; //Вызов методов вложенного класса OuterClass.InnerStaticClass.printString(string); //Создание экземпляра вложенного класса OuterClass.InnerStaticClass inClass = new OuterClass.InnerStaticClass(); inClass.printReverseString(string); //Обращение к приватным полям внешнего класса через метод внутреннего класса OuterClass.InnerStaticClass.printSecret(); } }
Вложенные статические классы в основном используются для группировки. Пример из кода компилируется в два класса OuterClass$InnerStaticClass.class и OuterClass.class.
Данному виду внутренних классов требуется экземпляр внешнего класса. Он имеет доступ ко всем полям и методам экземпляра внешнего класса, в том числе приватным. Также он может обращаться к статическим методам и полям обрамляющего класса.
class OuterClass { int k = 10; static int i = 20; public class InnerMemberClass{ public <T extends Number> void printArray(T[] arr){ for(T t:arr){ System.out.print(t.toString() + " "); } System.out.println(); staticMethod(); System.out.println("Outer fields: "+k+" "+i); } } public Integer[] genArray(){ Integer[] arrInt = new Integer[] {10,20,30,40,90,80,70}; return arrInt; } public static void staticMethod(){ System.out.println("i'm static"); } } public class TestClass{ public static void main(String[] args) { OuterClass.InnerMemberClass innClass = new OuterClass().new InnerMemberClass(); innClass.printArray(new OuterClass().genArray()); } }
Во внутренних классах-членах нельзя объявлять статические поля, статические методы и перечисления.
Локальные классы создаются в блоках инициализации и в статических блоках java кода. Но чаще всего они используются внутри методов. Они могут обращаться только к финальным полям обрамляющего класса и аргументам метода. Его нельзя создать за пределами блока кода, в котором он описан. Локальный класс не может быть private, public, protected или static.
class OuterClass { final int k = 222; public void someMethod(final int k,int i){ class LocalClass{ public void printArg(){ System.out.println("LocalClass.printArg(field--> "+OuterClass.this.k+" | "+k+" <--arg)"); } } new LocalClass().printArg(); } } public class TestClass{ public static void main(String[] args) { new OuterClass().someMethod(321,123); } }
Анонимные классы используются в месте их создания. Они не имеют имени. Являясь частным случаем локального класса они приемствуют все ограничения локальных классов. Также могут обращаться к финальным локальным переменным внешнего класса и ко всем полям обрамляющего класса. Локальные переменные должны быть финальными для того, что бы пользователь класса был уверен в том, что пока он работает с данными они не изменятся во внешнем коде, что их состояние остаётся актуальным на всём протяжении жизни анонимного класса.
public class TestAnonimClass{ static final int k = 0; public static void main(String[] args) { new Thread(new Runnable() { int k = TestAnonimClass.k; public void run() { for(;;){ k++; if(k%33==0){ System.out.println(k); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } }).start(); } }
После компиляции анонимный класс из примера выше выглядит как TestAnonimClass$1.class, но в программе мы не можем обратиться к нему по такому имени.
«Также могут обращаться к финальным локальным переменным внешнего класса и ко всем полям обрамляющего класса»
— если делается такое разделение на внешний и обрамляющий, то собственно чем внешний класс отличается от обрамляющего?
— чем переменная класса отличается от поля класса?