内部类主要定义在类的内部,定义内部类的作用,主要是因为不希望该类作为大家共同使用访问的类。

成员内部类

  • 成员内部类就是作为外部类的成员(没有static修饰),可以直接使用外部类的所有成员和方法,即使是private。
  • 成员内部类位于外部类内部,可以直接调用外部类的所有方法(静态方法和非静态方法)
  • 成员内部类可以添加任意访问修饰符(public protected 默认 private)因为它的地位就是一个成员。
  • 外部类要访问内部类的所有成员变量或方法,则需要通过内部类的对象来获取
  • 注意:成员内部类不能含有 static 的变量和方法。
  • 成员内部类的定义如下: ```java public class 外部类{ public class 内部类{

    } }

  1. - 内部类的实例化:
  2. ```java
  3. 外部类 对象 = new 外部类();
  4. 外部类.内部类 对象2=对象.new 内部类();
  • 如果外部类和成员内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类.this.成员)去访问。 ```java package cn.java.money.innerclass.demo04;

public class Outer { private int i = 10; String str = “str”; static String sta = “static”; static Inner inner = new Outer().new Inner();

  1. private void outer_method() {
  2. //外部类访问成员内部类,创建对象再访问, 可以访问成员内部类的私有属性和方法
  3. Inner inner = new Inner();
  4. inner.inner_method();
  5. System.out.println(inner.i);
  6. }
  7. public static void outer_method_static() {
  8. // 局部变量都不能用static修饰
  9. //static int i = 100;
  10. // 外部类的静态方法中 不能使用内部类
  11. // Inner inner = new Inner();
  12. }
  13. public class Inner {
  14. //成员内部类中不能使用static,
  15. //static int i = 100;
  16. // public static void inner_method_static() { }
  17. private int i = 100;
  18. private void inner_method() {
  19. // 成员内部类可以直接使用外部类的所有成员
  20. System.out.println(i);
  21. System.out.println(str);
  22. System.out.println(sta);
  23. outer_method();
  24. outer_method_static();
  25. }
  26. // 其他第三方的类可以访问
  27. public void inner_method2() {
  28. // 成员内部类可以直接使用外部类的所有成员
  29. System.out.println(i);
  30. System.out.println(str);
  31. System.out.println(sta);
  32. //outer_method(); 会形成循环调用
  33. // outer_method_static();
  34. }
  35. }
  36. public Inner getInnerInstance() {
  37. return new Inner();
  38. }

}

  1. ```java
  2. package cn.java.money.innerclass.demo04;
  3. public class Test {
  4. public static void main(String[] args) {
  5. //外部其他类,使用成员内部类的两种种方式
  6. // 方式一
  7. Outer outer = new Outer();
  8. Outer.Inner inner = outer.new Inner();
  9. inner.inner_method2();
  10. // 方式二 在外部类中,编写一个方法,可以返回Inner对象
  11. Outer.Inner innerInstance = outer.getInnerInstance();
  12. //简写
  13. Outer.Inner inner1 = new Outer().new Inner();
  14. Outer.Inner innerInstance1 = new Outer().getInnerInstance();
  15. }
  16. }

静态内部类

  • 被static修饰的成员内部类
  • 静态内部类可以直接访问外部类的所有静态成员,包括私有的,但不能直接访问非静态成员
  • 静态内部类可以添加任意访问修饰符
  • 作用域:整个外部类
  • 外部类访问静态内部类
    • 对于静态内部类的静态属性和方法,可用通过类名访问
      • Inner.inner_method_static(); // 在外部类中可以省略内部类名
    • 对于静态内部类的非静态方法和属性,可以通过创建对象访问
      • Inner inner = new Inner();
  • 外部其他类访问静态内部类(这时候访问权限起作用了)
    • 对于静态内部类的静态属性和方法,可用通过类名访问
      • Outer.Inner.inner_method_static();
    • 对于静态内部类的非静态方法和属性,可以通过创建对象访问
      • Outer.Inner inner = new Outer.Inner();
  • 如果外部类和静态内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类.成员)去访问。 ```java package cn.java.money.innerclass.demo05;

public class Outer { static String sta = “static”;

  1. private void outer_method() {
  2. //外部类访问静态成员内部类,创建对象再访问, 可以访问成员内部类的私有属性和方法
  3. Inner inner = new Inner();
  4. inner.inner_method();
  5. System.out.println(inner.i2);
  6. //静态内部类的静态方法和属性,外部类可以直接访问 包括私有的
  7. System.out.println(Inner.i);
  8. Inner.inner_method_static();
  9. }
  10. public static void outer_method_static() {
  11. // 局部变量都不能用static修饰
  12. //static int i = 100;
  13. // 外部类的静态方法中 可以使用静态内部类
  14. Inner inner = new Inner();
  15. System.out.println(Inner.i);
  16. Inner.inner_method_static();
  17. }
  18. public static class Inner {
  19. //静态成员内部类中可以使用static,
  20. static int i = 100;
  21. public static void inner_method_static() {
  22. }
  23. private int i2 = 100;
  24. private void inner_method() {
  25. // 静态内部类可以直接访问外部类的所欲静态成员,包括私有的,但不能直接访问非静态成员
  26. System.out.println(sta);
  27. outer_method_static();
  28. }
  29. public void inner_method_public() {
  30. }
  31. }
  32. public static Inner getInnerInstance() {
  33. return new Inner();
  34. }

}

  1. ```java
  2. package cn.java.money.innerclass.demo05;
  3. public class Test {
  4. // 前提 外部类访问静态内部类
  5. public static void main(String[] args) {
  6. Outer.Inner inner = new Outer.Inner();
  7. //这里内部类的成员访问修饰符 就起作用了
  8. inner.inner_method_public();
  9. //不能通过静态内部了的实例调用静态方法
  10. //可以通过静态内部类直接访问
  11. Outer.Inner.inner_method_static();
  12. System.out.println(Outer.Inner.i);
  13. Outer.Inner innerInstance = Outer.getInnerInstance();
  14. }
  15. }

局部内部类

  • 局部内部类是指内部类定义在方法和作用域内。通俗来说,就是在外部内的方法中定义的内部类就是局部内部类。
  • 局部内部类不能添加访问修饰符,但是可以使用final修饰,因为局部内部类还是一个类,可以被继承。
  • 局部内部类的作用域,仅仅在定义它的方法或代码块中。
  • 局部内部类和局部变量的位置等价,因此可以在代码块中定义。因此,局部内部类的实例化也只能在方法中进行。
  • 局部内部类可以访问外部类的所有成员,包括私有成员。
    • 局部内部类可以直接访问外部类(包括局部内部类所在方法的局部变量)。
    • 外部类(也就是当前方法中,即作用域)访问局部内部类,要先new 局部内部类。
  • 局部内部类的调用,其实就是外部类调用局部内部类所在的方法。
  • 外部其他类不能访问局部内部类。
  • 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类.this.成员)去访问。
  • 局部内部类中不能有 static 修饰的 成员变量 和方法 和 静态代码块 ,所以局部内部类的加载只能通过new 局部内部类来加载。
  • 局部内部类可以定义在静态和非静态方法内。
  • 注意:局部内部类方法中想要使用局部变量,该变量必须声明为 final 类型,但是Java8以后,编译器会自定添加final。
  • 对于局部内部类,只有在方法的局部变量被标记为final或局部变量是effctively final的,内部类才能使用它们

    匿名内部类

  • 匿名内部类是一个类,是类就有类名,只是这个类名在编译器生成,我们无法使用。

  • 匿名内部类编译以后就是当前类的一个内部类。
  • 每创建一个匿名内部类,编译器就会生成一个对应的内部类。
  • 匿名内部类的本质是抽象类和接口的实现。
  • 匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,一次从语法上看,它既有定义类的特征,也有创建对象的特征。
  • 匿名内部类可以直接访问外部类的所有成员,包含私有的。
  • 不能添加访问修饰符,因为类时由编译器生成的,你没法添加,但是接受匿名内部类对象的变量可以。
  • 匿名内部类的作用域 可以定义由成员变量接受,也可以由局部变量接受,如果么有成员变量接受,就只能定义在方法或代码块中
  • 其他外部类无法访问匿名内部类。
  • 如果外部类和匿名内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类.this.成员)去访问。
  • 匿名内部类的成员,只能自己访问,外部类无法访问。
  • 匿名内部类的使用
    • 匿名内部类当做实参直接传递
    • 匿名内内部类可以当作方法的返回值。 ```java package cn.java.money.innerclass.demo03;

public class Outer { int i = 10; String str = “str”; static String sta = “static”;

  1. public void outer_method() {
  2. //外部类无法访问匿名内部类
  3. }
  4. public static void outer_method_static() {
  5. }
  6. //编译完成以后,编译器给会每一个匿名内部类分配一个类名(类名格式:外部类$N)
  7. //当我们new A()的时候,JVM知道我们要实例化的内部类是哪一个,也就是A接口的实现类
  8. //匿名内部类只能使用一次,是因为我们每 new一个匿名内部类(new A()),编译器就会生成一个对应的内部类,
  9. //因此 new A() 的 getClass() 和 再次new A()的 getClass()的值是不一样的,可能一个是 外部类$1,另外一个是$3
  10. //指向匿名内部类实例的引用可以反复使用
  11. A a = new A() {
  12. @Override
  13. public void cry() {
  14. System.out.println(a.getClass());//查看运行时类型,同时说明匿名内部类不是真的没有类名
  15. //匿名内部类可以直接访问外部类的所有成员,包含私有的。
  16. //内部类是随着外部类的加载而加载,因此内部类可以访问所有的外部类成员
  17. System.out.println(i);
  18. System.out.println(b);
  19. System.out.println(str);
  20. System.out.println(sta);
  21. outer_method();
  22. outer_method_static();
  23. }
  24. };
  25. //A接口只有一个抽象方法, 可以用lamda
  26. A a1 = () -> {
  27. System.out.println(a.getClass());
  28. };
  29. //匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,一次从语法上看,它既有定义类的特征,也有创建对象的特征。
  30. B b = new B() {
  31. @Override
  32. public void cry() {
  33. int i = 10;
  34. //如果外部类和匿名内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类.this.成员)去访问。
  35. System.out.println(Outer.this.i);
  36. }
  37. @Override
  38. public void run() {
  39. }
  40. };
  41. static A a2 = new A() {
  42. @Override
  43. public void cry() {
  44. System.out.println(a2.getClass());
  45. }
  46. };
  47. // 这个类的运行时类型为 Father
  48. Father father = new Father("jack");
  49. //基于类的内部类演示, 这样的内部类其实就是当前类的子类
  50. //这个类的运行时类型为 Father$N
  51. Father father2 = new Father("jack") {
  52. @Override
  53. public void tet() {
  54. System.out.println("我是Father的匿名内部类的test");
  55. }
  56. };
  57. public void test() {
  58. //匿名内部类,想要直接调用新增的方法 如下
  59. new Father("") {
  60. public void ff() {
  61. System.out.println("我是匿名内部类新增的方法,或者说是拓展的功能");
  62. }
  63. }.ff();
  64. }
  65. public static void main(String[] args) {
  66. //外部类调用匿名内部类中方法
  67. Outer outer = new Outer();
  68. outer.a.cry();
  69. Outer.a2.cry();
  70. outer.father.tet();
  71. System.out.println(outer.father.getClass());
  72. outer.father2.tet();
  73. System.out.println(outer.father2.getClass());
  74. }

}

interface A { void cry(); }

interface B { void cry();

  1. void run();

}

class Father { private String name;

  1. public Father(String name) {
  2. this.name = name;
  3. }
  4. public void tet() {
  5. System.out.println("Father 的test方法");
  6. }

}

class C { public void test(A a) { a.cry(); }

  1. public static void main(String[] args) {
  2. //匿名内部类当做实参直接传递
  3. new C().test(Outer.a2);
  4. new C().test(new A() {
  5. @Override
  6. public void cry() {
  7. }
  8. });
  9. new C().test(() -> {
  10. });
  11. }

} ``` 特别注意:
在使用匿名内部类时,要记住以下几个原则。

  1. 匿名内部类不能有构造方法。
  2. 匿名内部类不能定义任何静态成员,方法和类。
  3. 匿名内部类不能使用public,protected,private,static。
  4. 只能创建匿名内部类的一个实例。
  5. 一个匿名内部类一定时在 new 后面,用其隐含实现一个接口或实现一个类。
  6. 因匿名内部类为局部内部类,所以,局部内部类的所有限制都对其有效。
  7. 内部类只能访问外部类的静态变量或静态方法。
  8. 内部类当中的 this 指的是匿名内部类本身,如果使用外部类中的 this,则“外部类.this”。

使用内部类的主要原因有:

  • 内部类可以访问外部类中的数据,包括私有的数据。
  • 内部类可以对同一个包中的其他类隐藏起来。
  • 当想要定义一个回调函数且不想编写大量代码时,使用匿名(anonymous)内部类比较便捷。
  • 减少类的命名冲突。

静态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有。没有这个引用就意味着:
1、 它的创建是不需要依赖于外围类的。
2、 它不能使用任何外围类的非static成员变量和方法。