1. 简介
在 Java 中可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来说包括这四种:
成员内部类、局部内部类、匿名内部类和静态内部类。
2. 优点
在程序设计中有时会存在一些使用接口很难解决的问题,此时可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决。
可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。
使用内部类可以时程序拥有以下特性:
1)内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立;
2)在单个外部类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类;
3)创建内部类对象的时刻并不依赖于外围类对象的创建;
4)内部类并没有令人迷惑的“is-a”关系,他就是一个独立的实体;
5)内部类提供了更好的封装,除了该外部类,其他类都不能访问;
3. 示例
3.1 成员内部类
3.1.1 成员内部类的修饰符和实例化
成员内部类可以看作是外部类的一个成员,它有以下注意点:
1)成员内部类不能使用static关键字;
2)成员内部类的实例化:成员内部类依赖于外部类,因此必须在外部类实例化后内部类才能实例化。
上述条件说明在外部类的非静态方法中可以实例化内部类;或者在外部类的静态方法中先实例化外部类,再实例化内部类。
1 public class OuterClass1 { 2 3 class InnerClass{ 4 //private static int innerVar = 1; // 成员内部类不能使用静态成员变量 5 6 // private static void innerMethod(){ } // 成员内部类不能使用静态方法 7 } 8 9 public void outMethod(){ 10 InnerClass in = new InnerClass(); // 在外部类的非静态方法中直接实例化内部类 11 } 12 13 public static void main(String[] args){ 14 //InnerClass in = new InnerClass(); // 在外部类的静态方法中不能直接实例化内部类 15 16 // 先创建外部类对象后才能创建内部类对象 17 OuterClass1 out = new OuterClass1(); 18 OuterClass1.InnerClass in = out.new InnerClass(); 19 } 20 }
3.1.2 成员内部类访问外部类
成员内部类在访问外部类时,它有以下注意事项:
1)成员内部类访问外部类的非同名成员变量和方法:直接调用即可;
2)成员内部类访问外部类的同名成员变量和方法:外部类.this.成员变量 / 外部类.this.成员方法;
1 public class OuterClass2 { 2 private String outVar1 = "outVar1"; 3 private static String outVar2 = "outVar2"; 4 private String outVar3 = "outVar3"; 5 6 private void outerMethod(){ 7 System.out.println("outerMethod with same name of OuterClass"); 8 } 9 10 private void outerMethod1(){ 11 System.out.println("outerMethod of OuterClass"); 12 } 13 14 private static void outerMethod2(){ 15 System.out.println("static outerMethod of OuterClass"); 16 } 17 18 class InnerClass{ 19 private String outVar3 = "innerVar3"; 20 21 private void outerMethod(){ 22 System.out.println("outerMethod with same name of InnerClass"); 23 System.out.println(outVar1); // 访问外部类成员变量 24 System.out.println(outVar2); // 访问外部类静态成员变量 25 System.out.println(OuterClass2.this.outVar3); // 访问外部类同名成员变量 26 } 27 28 public void innerMethod(){ 29 outerMethod(); // 访问自身的同名方法 30 OuterClass2.this.outerMethod(); // 访问外部类的同名方法 31 outerMethod1(); // 访问外部类的实例方法 32 outerMethod2(); // 访问外部类的静态方法 33 } 34 } 35 36 public static void main(String[] args){ 37 OuterClass2 out = new OuterClass2(); 38 OuterClass2.InnerClass in = out.new InnerClass(); 39 in.innerMethod(); 40 } 41 }
运行结果如下:
1 outerMethod with same name of InnerClass 2 outVar1 3 outVar2 4 outVar3 5 outerMethod with same name of OuterClass 6 outerMethod of OuterClass 7 static outerMethod of OuterClass
3.1.3 外部类访问成员内部类
外部类在访问成员内部类时,它有以下注意事项:
1)外部类访问成员内部类时必须通过内部类的对象访问;
1 public class OuterClass3 { 2 3 class InnerClass{ 4 private String innerVar1 = "innerVar1"; 5 6 private void outerMethod1(){ 7 System.out.println("outerMethod with same name of InnerClass"); 8 } 9 } 10 11 private void outerMethod1(){ 12 System.out.println("outerMethod with same name of OuterClass"); 13 InnerClass in = new InnerClass(); // 通过内部类对象访问内部类的成员变量和方法 14 System.out.println(in.innerVar1); 15 in.outerMethod1(); 16 } 17 18 private static void outerMethod2(){ 19 System.out.println("static method with same name of OuterClass"); 20 // InnerClass in = new InnerClass(); // error 21 OuterClass3 out = new OuterClass3(); // 通过内部类对象访问内部类的成员变量和方法 22 OuterClass3.InnerClass in = out.new InnerClass(); 23 in.outerMethod1(); 24 } 25 26 public static void main(String[] args){ 27 OuterClass3 out = new OuterClass3(); 28 out.outerMethod1(); 29 outerMethod2(); 30 } 31 }
运行结果如下:
1 outerMethod with same name of OuterClass 2 innerVar1 3 outerMethod with same name of InnerClass 4 static method with same name of OuterClass 5 outerMethod with same name of InnerClass
3.2 静态内部类
3.2.1 静态内部类的说明和使用
静态内部类指使用static关键字修饰的内部类。非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外部类,
但是静态内部类却没有该引用。它的特点如下:
1)静态内部类的创建不依赖于外部类;
2)静态内部类不能使用外部类的任何非静态成员变量和方法;
1 public class OuterClass4 { 2 private String outVar1 = "outVar1"; 3 private static String outVar2 = "outVar2"; 4 private static String outVar3 = "outVar3"; 5 6 public void outMethod1(){ 7 } 8 9 private static void outMethod2(){ 10 } 11 12 static class InnerClass { 13 private String outVar3 = "innerVar3"; 14 15 public InnerClass(){ 16 // System.out.println(outVar1); // 不能访问外部类的非静态变量 17 System.out.println(outVar2); // 可以访问外部类的静态变量 18 System.out.println(OuterClass4.outVar3); // 可以访问外部类的同名静态变量 19 20 //outMethod1(); // 不能访问外部类的非静态方法 21 outMethod2(); // 可以访问外部类的 22 } 23 } 24 25 public static void main(String[] args){ 26 InnerClass in = new InnerClass(); // 可以直接创建内部类对象 27 System.out.println(in.outVar3); 28 } 29 }
运行结果如下:
1 outVar2 2 outVar3 3 innerVar3
3.3 局部内部类
3.3.1局部内部类的说明和使用
局部内部类是定义在一个方法或者作用域中的类,它的使用仅限于其方法或者作用域内,出了方法和作用域就会失效,类似于局部变量。其特点如下:
1)局部内部类是定义在一个方法或者作用域中的类,它的访问权限仅限于其方法或者作用域内;
2)局部内部类类似方法和作用域中的局部变量,不能使用权限访问修饰符和static关键字修饰;
1 class Animal{ } 2 3 public class OuterClass5 { 4 5 // 在方法中使用局部内部类 6 private Animal getDog(){ 7 class Dog extends Animal{ 8 } 9 return new Dog(); 10 } 11 12 // 在作用域中使用局部内部类 13 private Animal getCat(boolean flag){ 14 if(flag){ 15 class Cat extends Animal{ 16 } 17 return new Cat(); 18 } 19 return null; 20 } 21 22 public static void main(String[] args){ 23 OuterClass5 out = new OuterClass5(); 24 Animal a1 = out.getDog(); 25 Animal a2 = out.getCat(true); 26 System.out.println(a1); 27 System.out.println(a2); 28 } 29 }
运行结果如下:
1 InnerClass.OuterClass5$1Dog@6d6f6e28 2 InnerClass.OuterClass5$1Cat@135fbaa4
3.4 匿名内部类
3.4.1 匿名内部类的说明
匿名内部类即是没有名称的内部类,它的使用前提和特点如下:
1)使用匿名内部类需要继承父类或者实现一个接口;
2)匿名内部类不能使用访问修饰符修饰;
3)匿名内部类不能是抽象类,因为在使用它时会直接创建该类的对象;
4)匿名内部类不能定义构造器,因为该类没有类名;
3.4.2 未在抽象类和接口上使用匿名内部类
对于一个抽象类和接口,通常需要使用一个类继承或实现它们,然后再实现其内部的方法,最后使用它。例如:
1 abstract class Parent{ 2 abstract int getNumber(int n); 3 } 4 5 class Child extends Parent{ 6 @Override 7 int getNumber(int n) { 8 return n; 9 } 10 } 11 12 public class Demo { 13 public static void main(String[] args){ 14 Parent p = new Child(); 15 System.out.println(p.getNumber(2)); 16 } 17 }
3.4.3 在抽象类上使用匿名内部类
对于上述抽象类Parent,我们显示地定义了一个Child类继承它,并且重写了其方法,有什么方法可以不写这个Child类呢?
这里引入匿名内部类即可,例如以下示例:
1 abstract class Parent { 2 abstract int getNumber(int n); 3 } 4 5 public class Demo1 { 6 public static void main(String[] args){ 7 Parent p = new Parent() { // 注意,这里使用了匿名内部类 8 @Override 9 public int getNumber(int n) { 10 return n; 11 } 12 }; 13 System.out.println(p.getNumber(2)); 14 } 15 }
3.4.4 在接口上使用匿名内部类
1 interface Parent { 2 int getNumber(int n); 3 } 4 5 public class Demo2 { 6 public static void main(String[] args){ 7 // 直接使用 8 Parent p1 = new Parent() { // 没有出现类名 9 @Override 10 public int getNumber(int n) { 11 return n; 12 } 13 }; 14 System.out.println(p1.getNumber(2)); 15 16 // 使用Lambda表达式 17 Parent1 p2 = n -> n; 18 System.out.println(p2.getNumber(2)); 19 } 20 }
4. 参考文献
https://www.runoob.com/w3cnote/java-inner-class-intro.html
https://www.cnblogs.com/nerxious/archive/2013/01/25/2876489.html
https://blog.csdn.net/guyuealian/article/details/51981163
https://www.cnblogs.com/chenssy/p/3388487.html
!!!