记录生活中的点点滴滴

0%

Java中的内部类

这一篇文章来写写Java中的内部类。

内部类(inner class)是定义在另一个类中的类,为什么需要内部类呢?有两个原因:

  • 内部类可以对同一个包中的其他类隐藏
  • 内部类方法可以访问定义这个类的作用域中的数据,包括原本私有的数据

内部类可以分为四类:普通内部类、静态内部类、匿名内部类、局部内部类。

普通内部类

这个是最常见的内部类之一了,其定义也很简单,在一个类里面作为类的一个字段直接定义就可以了,例:

1
2
3
4
5
public class InnerClassTest{
public class InnerClassA{

}
}

在这种定义方式下,普通内部类对象依赖外部对象而存在,即在创建一个普通内部类对象时首先需要创建其外部类对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class InnerClassTest {
public int outField1 = 1;
protected int outField2 = 2;
int outField3 = 3;
private int outField4 = 4;

public class InnerClassA{
public int field1 = 5;
protected int field2 = 6;
int field3 = 7;
private int field4 = 8;
// static int field5 = 5;//编译错误!普通内部类中不能定义static属性
public InnerClassA(){
System.out.println("创建 "+this.getClass().getSimpleName()+" 对象");
System.out.println("其外部类 outField1 字段的值为:"+outField1);
System.out.println("其外部类 outField2 字段的值为:"+outField2);
System.out.println("其外部类 outField3 字段的值为:"+outField3);
System.out.println("其外部类 outField4 字段的值为:"+outField4);
}
}

public InnerClassTest(){
//创建内部类
InnerClassA inner = new InnerClassA();
System.out.println("创建 "+this.getClass().getSimpleName()+" 对象");
System.out.println("其内部类 field1 字段的值为:"+inner.field1);
System.out.println("其内部类 field2 字段的值为:"+inner.field2);
System.out.println("其内部类 field3 字段的值为:"+inner.field3);
System.out.println("其内部类 field4 字段的值为:"+inner.field4);
}
}

进行测试:

1
2
3
4
5
6
public class TestDemo {
public static void main(String[] args) {
InnerClassTest test = new InnerClassTest();
InnerClassTest.InnerClassA innerClassA = test.new InnerClassA();
}
}

输出结果:

我们注意到,内部类对象可以访问外部类对象中所有访问权限的字段,同时,外部类对象也可以通过内部类的对象引用来访问内部类中定义的所有访问权限的字段。

静态内部类

我们知道,一个类的静态成员独立于这个类的任何一个对象存在,只要在具有访问权限的地方,我们就可以通过 类名.静态成员名 的形式访问这个静态成员,同样的,静态内部类也是作为一个外部类的静态成员而存在。创建一个类的静态内部类对象不需要依赖其外部类对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class InnerClassTest {
public int outField1 = 1;

public static class InnerClassA{
public int field1 = 5;
protected int field2 = 6;
int field3 = 7;
private int field4 = 8;
static int field5 = 5;//静态内部类中可以定义static属性
public InnerClassA(){
System.out.println("创建 "+this.getClass().getSimpleName()+" 对象");
// System.out.println("其外部类 outField1 字段的值为:"+outField1);//编译错误,静态方法内部不能访问外部类的非静态字段
}
}

public InnerClassTest(){
//创建内部类
InnerClassA inner = new InnerClassA();
System.out.println("创建 "+this.getClass().getSimpleName()+" 对象");
System.out.println("其内部类 field1 字段的值为:"+inner.field1);
System.out.println("其内部类 field2 字段的值为:"+inner.field2);
System.out.println("其内部类 field3 字段的值为:"+inner.field3);
System.out.println("其内部类 field4 字段的值为:"+inner.field4);
}
}

进行测试:

1
2
3
4
5
6
7
public class TestDemo {
public static void main(String[] args) {
InnerClassTest outer = new InnerClassTest();
System.out.println("-------------");
InnerClassTest.InnerClassA inner = new InnerClassTest.InnerClassA();
}
}

输出结果:

可以看到,静态内部类就像一个静态成员一样,创建其对象无需依赖外部类对象,因为它是独立于所有的类的对象的。但是,对于静态内部类,它也无法访问外部类的非静态成员,因为外部类的非静态成员是属于每一个外部对象的,而本身静态内部类就是独立于外部对象的,所以不能访问。而外部类依然可以访问静态内部类对象的所有访问权限的成员,这一点与普通内部类无异。

匿名内部类

匿名内部类有多种形式,其中最常见的一种形式莫过于在方法参数中新建一个接口对象 / 类对象,并且实现这个接口声明 / 类中原有的方法了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class InnerClassTest {
public int outField1 = 1;
protected int outField2 = 2;
int outField3 = 3;
private int outField4 = 4;
public InnerClassTest(){
System.out.println("创建 "+this.getClass().getSimpleName()+" 对象");
}
//自定义接口
interface MyListener{
void onclick(Object object);
}
public void anonymousTest(){
//匿名内部类1
MyListener myListener = new MyListener() {
@Override
public void onclick(Object object) {
System.out.println("点击事件..." + object);
System.out.println("其外部类 outField1 字段的值为:" + outField1);
System.out.println("其外部类 outField2 字段的值为:" + outField2);
System.out.println("其外部类 outField3 字段的值为:" + outField3);
System.out.println("其外部类 outField4 字段的值为:" + outField4);
}
};
//匿名内部类2
myListener.onclick(new Object(){
@Override
public String toString(){
return "俺叫obj";
}
});
}
}

进行测试:

1
2
3
4
5
6
public class TestDemo {
public static void main(String[] args) {
InnerClassTest outer = new InnerClassTest();
outer.anonymousTest();
}
}

输出结果:

上面代码有两处产生了匿名内部类:

1、直接 new 一个接口,并实现这个接口的方法,在这个过程实际上会创建一个匿名内部类实现这个接口,并重写接口的方法,然后创建一个这个匿名内部类的对象给前面的类型引用

2、new 一个已经存在的类/抽象类,并且选择性的实现这个类中的一个或者多个非final的方法,这个过程会创建一个匿名内部类对象继承对应的类,并且重写对应方法。

同样的,匿名类也可以使用外部类的属性,但是外部类不能使用匿名内部类中定义的属性,因为是匿名内部类,外部类根本得不到这个类的类名,也就无法获得相关信息。

局部内部类

局部内部类使用的比较少,其声明在一个方法体 / 一段代码块的内部,而且不在定义类的定义域之内便无法使用,其提供的功能使用匿名内部类都可以实现,而本身匿名内部类可以写得比它更简洁,因此局部内部类用的比较少。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class InnerClassTest {
private int field = 1;
public void localInnerTest(){
class A{
// static int inner_field = 2;//不能定义static字段
public String name = "localInner";
public A(){
System.out.println("创建 "+this.getClass().getSimpleName()+" 对象");
System.out.println("其外部类 field 字段的值为:"+field);
}
}
A a = new A();
}
public InnerClassTest(){
// new A();//不能创建局部内部类
System.out.println("创建 "+this.getClass().getSimpleName()+" 对象");
}
}

进行测试:

1
2
3
4
5
6
public class TestDemo {
public static void main(String[] args) {
InnerClassTest outer = new InnerClassTest();
outer.localInnerTest();
}
}

输出结果:

同样的,局部内部类能访问外部类的所有访问权限的字段,但是外部类不能使用局部内部类中定义的属性。