Java 内部类

Posted 火磷

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 内部类相关的知识,希望对你有一定的参考价值。

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         //private static void innerMethod(){ }     // 成员内部类不能使用静态方法
 6     }
 7 
 8     public void outMethod(){
 9         InnerClass in = new InnerClass();      // 在外部类的非静态方法中直接实例化内部类
10     }
11 
12     public static void main(String[] args){
13         //InnerClass in = new InnerClass();       // 在外部类的静态方法中不能直接实例化内部类
14 
15         // 先创建外部类对象后才能创建内部类对象
16         OuterClass1 out = new OuterClass1();
17         OuterClass1.InnerClass in = out.new InnerClass();
18         // 外部类的非静态方法之所以可以创建内部类对象,是由于要访问外部类的非静态方法,必须通过外部类的对象访问,
19         // 此时已经创建了外部类对象,满足了先外部对象,再内部对象的条件
20         out.outMethod();
21     }
22 }

3.1.2 成员内部类访问外部类

 成员内部类在访问外部类时,它有以下注意事项:

1)成员内部类访问外部类的非同名成员变量和方法:直接调用即可;

2)成员内部类访问外部类的同名成员变量和方法:外部类.this.成员变量 / 外部类.this.成员方法;

注:无论外部类的成员变量和方法是否使用private修饰皆可访问。

 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关键字修饰的内部类。非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外部类,

静态内部类却没有该引用,这是因为静态内部类是类级别的属性,不需要对象的引用(this)来调用。

它的特点如下:

0)加载外部类时不会直接加载静态内部类,当且仅当静态内部类的静态成员(静态域、构造器、静态方法等)被调用时才会加载;

1)静态内部类的对象创建不依赖于外部类;

2)静态内部类不能使用外部类的任何非静态成员变量和方法,只能访问静态成员变量和方法;

3)外部类可以通过类名.xx直接访问静态内部类的静态方法;

 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         InnerClass in = new InnerClass();
 8         System.out.println(in.outVar3);
 9     }
10 
11     private static void outMethod2(){
12     }
13 
14     private static void outMethod3(){
15         System.out.println(InnerClass.outVar4);          // 可以直接访问静态内部类的静态变量和方法
16         InnerClass in = new InnerClass();
17         in.innerMethod();
18         System.out.println(in.outVar3);                  // 可以通过内部类对象访问实例变量和方法
19     }
20 
21     static class InnerClass {
22         private String outVar3 = "innerVar3";
23         private static String outVar4 = "innerVar4";
24 
25         public InnerClass(){
26 //            System.out.println(outVar1);              // 不能访问外部类的非静态变量
27             System.out.println(outVar2);                // 可以访问外部类的静态变量
28             System.out.println(OuterClass4.outVar3);   //  可以访问外部类的同名静态变量
29 
30             //outMethod1();            // 不能访问外部类的非静态方法
31             outMethod2();              // 可以访问外部类的静态方法
32         }
33 
34         public void innerMethod(){
35         }
36     }
37 
38     public static void main(String[] args){
39         InnerClass in = new InnerClass();       // 可以直接创建内部类对象
40         System.out.println(in.outVar3);
41 
42         OuterClass4 out = new OuterClass4();
43         out.outMethod1();
44         outMethod3();
45     }
46 }

 运行结果如下:

 1 outVar2
 2 outVar3
 3 innerVar3
 4 outVar2
 5 outVar3
 6 innerVar3
 7 innerVar4
 8 outVar2
 9 outVar3
10 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

!!!

以上是关于Java 内部类的主要内容,如果未能解决你的问题,请参考以下文章

# Java 常用代码片段

# Java 常用代码片段

elasticsearch代码片段,及工具类SearchEsUtil.java

片段 - 全局视图变量与本地和内部类侦听器和内存泄漏

为啥片段类应该是公开的?

ForegroundService没有从片段开始?