Java中有四种形式的内部类,在开发的过程中需要理清楚何时使用合适的内部类,内部类用好了可以提高编码效率、更好的实现封装、甚至可以巧妙实现多继承。当然,某些内部类用多了会削弱面向对象的设计思想,所以内部类不可滥用,要清楚各种形式内部类的作用和适用场景。
1. 成员内部类,或者叫动态内部类,使用方式如下:
public class Demo {
private String name; //宿主类的成员变量
class InnerDemo {
public void printName() {
//成员内部类可以访问宿主类的动态成员变量,无论是否是private
System.out.println(name);
}
}
public Demo(String name) {
this.name = name;
}
public static void main(String[] args) {
Demo demo = new Demo("Zhangsan");
//实例化成员内部类的时候要先实例化宿主类,然后用宿主类对象.new 来实例化成员内部类的对象
InnerDemo innerDemo = demo.new InnerDemo();
innerDemo.printName();
}
}
成员内部类的好处或使用场景:
1). 可以更好的实现功能隐藏,可以类似成员变量一样用private、protected、public或默认等关键字修饰访问权限;
2). 可以实现多继承,成员内部类也是独立的个体,它和宿主类并不是is-a的关系,而是既能访问宿主类的成员变量(包括private),又能有自己独立的行为逻辑;
3). 当接口中有方法名和继承的父类中的方法名有冲突时可以用这种方式解决;
4). 一个宿主类的对象可以实例化多个成员内部类对象,它们都可以访问宿主类对象的状态/信息,又彼此不干预,比较适合做回调功能。比如发布/订阅模式,宿主类产生消息,各成员内部类对象消费宿主类的消息。
2. 静态内部类,静态内部类和成员内部类的定义方式有点像静态方法和动态方法,静态内部类需要static修饰,实例化静态内部类的对象时无需先实例化宿主类对象,但是静态内部类中的static方法是无法直接访问宿主类中的成员变量的,它可以随时访问宿主类中的静态变量,这个很好理解:
静态是模板,动态是实例,实例是从模板生产出来的,一个模板可以产生千千万万个实例,实例可以随时访问模板的信息,但模板要访问实例的信息必须先得有这个实例对象(得知道是哪个实例,模板就一个,实例那么多)。
静态内部类使用方式如下:
public class Demo {
private String name; //宿主类的成员变量
public static class InnerDemo {
public void printName() {
//静态内部类中要访问宿主类的成员变量的话,要先实例化宿主类
//它可以访问宿主类的private访问级别的变量
Demo demo = new Demo("Lisi");
System.out.println(demo.name);
}
}
public Demo(String name) {
this.name = name;
}
public static void main(String[] args) {
Demo demo = new Demo("Zhangsan");
//实例化静态内部类,用new 宿主类.内部类
InnerDemo innerDemo = new Demo.InnerDemo();
innerDemo.printName();
}
}
静态内部类的好处/使用场景:
1). 它和成员内部类的区别就是要static修饰,不能直接访问宿主类成员变量,但可以间接访问(先实例化或者传入一个对象参数)宿主类对象的private变量或方法。它和宿主类是软引用,GC时不会形成强关联,内存泄漏风险较低;
2). 静态内部类的创建不依赖外部类,可以直接创建,而不需要通过先创建外部类对象来创建静态内部类对象。
其他有些优势和动态内部类类似。
3. 局部内部类,只能定义在方法中或代码块中,生命周期随着方法结束而结束。
局部内部类不能用private, protected...这些访问关键字修饰,有点类似局部变量,它是用来辅助完成比较复杂的功能逻辑,又不对外部可见的使用场景。使用方式如下:
public void testFunction() {
//局部内部类示例,它可以在方法中拥有类的逻辑,可以访问宿主类的成员变量,但宿主类的变量不能被修改过(需要是final)
class InnerDemo implements Runnable {
@Override
public void run() {
System.out.println("Start to run inner demo of object: " + name);
}
}
Thread thread = new Thread(new InnerDemo());
thread.start();
}
局部内部类的好处/使用场景:
1). 可以在某个方法中解决比较复杂的逻辑,这个类又不会被其他外部引用到,随着方法的结束而结束,不会造成资源浪费;
2). 有时我们在使用某个接口或抽象类的时候,比如集合排序比对的Compareable接口,可能需要一次性的基于一个临时类创建对象,这个临时类也不会再被项目中任何其他模块使用到了,就是用局部类,方便省事,还方便阅读;
3). 局部内部类也可以访问宿主类或方法中的变量,但变量必须是final(jdk8以后不用显示定义final,只要变量没被修改过即可);
4). 局部内部类定义好之后可以很方便在方法中重复使用创建对象,而匿名内部类则不行。
4. 匿名内部类,其实这个和局部内部类很像,只是写起来更简单,它不太好用来创建多个对象,但一般情况下,一个方法中也要不了那么多相同类的对象,所以匿名内部类反而使用的很广,就因为方便省事,可读性还好。
public void testFunction() {
//匿名内部类,写起来非常简单直爽,一般用来临时实现某个接口
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(name);
}
});
thread.start();
}
匿名内部类的好处/使用场景:
出了它没有名字,不好重复来new对象,基本上局部内部类有的好处它都有,而且它比局部内部类更方便直爽,尤其是当某个接口只有个别方法时,简直不要太爽,比如Compareable, Runable等。