如果一个类定义在另一个类的内部,这个类就是内部类。
为什么要用内部类,什么场景下需要用内部类
-
根据面向对象设计原则,需要定义一个新的类,并对相关操作和属性进行封装。
-
新类仅同另一个类有密切依赖关系或者逻辑关系,其它类基本不会用到它;或者这个新的类仅是为了另一个类提供服务。
-
当发现一个类需要继承一个以上的类时,此时需要检查一下此类是否符合类的单一功能原则,是否应该定义新的类,是否需要定义一个内部类来继承另外的类,同时也能实现功能需求。
-
需要对一个抽象类或者接口做具体实现,但是这个实现只有一个地方会调用他,此时没必要专门定义一个单独的类,可以用局部内部类或者匿名内部类。
内部类的优点
-
内部类继承的类和外部类继承的类不同,这样对于外部类来说,变相的实现了多继承功能。
-
方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。
-
相对于直接将内部类的功能揉在外部类中——即不定义一个新的内部类——来说,定义一个内部类也能更充分利用JIT的优化功能,也更符合类的单一功能设计原则。
-
方便编写事件驱动程序
内部类的语法规则
内部类分为四种:成员内部、局部内部类、匿名内部类、静态内部类。
常规内部类 / 成员内部类
可以将成员内部类当做外部类的一个成员,与外部类的实例域和成员方法是同级的,是与外部类的实例域和成员方法的地位相同的存在。因此:
同实例域一样,成员内部类也可以声明为private的,也只有内部类可以声明为private。成员内部类的访问控制也同外部类的实例域相同。
同成员方法类似,成员内部类中不允许定义静态成员,即不能有静态变量和静态方法。
同外部类的成员方法一样,内部类可以直接访问其所在外部类内的其它成员,包括域和方法。可以显示地通过outer.fieldName | outer.method()
,也可以不用outer而直接访问。
当在另外的类中构造内部类的实例时,首先要有一个外部类的实例对象outer。
public class Test{
public static void main(String [] args){
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
}
}
public class Outer{
public class Inner{
}
}
当在外部类中调用成员内部类的方法时,需要遵循一般类的访问控制规则,如果内部类中的成员为private时,外部类是没有访问权限的。
常规内部类的对象inner会持有外围类对象outer的引用。因为编译器在编译阶段会自动改造内部类的构造方法——不论内部类的构造方法是否有显式的定义,都会对构造方法添加一个参数outer,并将此参数声明为改造后的构造方法的第一个参数。
而常规内部类的创建上也可以体现这一点:
public class Test{
public static void main(){
Outer outer = new Outer();
Inner inner = outer.new Inner();
outer.outerM1();
inner.innerM2();
}
}
public class Outer{
private String outerStr;
public void outerM1(){
new Inner().innerM2();
}
// 成员内部类,地位等同于所在外部类的实例域和成员方法,访问控制符的对成员内部类的作用也与他们相同
class Inner{ //如果声明为private,那么除其所在外部类外其它类无法访问,一般情况下会定义为private。
// 成员内部类中定义的实例域和方法,其访问权限同普通类内的实例域和成员方法一样。
private String innerStr;
public Inner(){
}
//可以直接访问外部类中的实例域、直接调用外部类中的方法。
public void innerM1(){
outerM2();
}
public void innerM2(){
outerStr="outerStrByInnerM2";
}
}
}
局部内部类
在方法或者代码块中定义的类,如同方法内部的局部变量一样。
其可见性也只在所在代码块内。
public class Outer{
public static void main(String[] args){
new Outer().outerM1();
}
public void outerM1(){
// 局部内部类
class LocalInner
{
public void innerM1(){
System.out.println(" innerM1");
}
}
// 实例化并调用方法
new LocalInner().innerM1();
}
}
匿名内部类
匿名内部类也是定义在代码块中的类,但是没有显式地声明类的名称。一般是对接口的实现或者类的继承,在定义的同时会直接实例化出一个对象并调用方法。线程的创建和使用是最常见使用匿名内部类的场景。
public class Test{
public static void main(String[] args){
new Test().m1();
}
public void m1(){
new Thread(
new Runnable(){
public void run(){
try{
Thread.sleep(1000);
System.out.println("thread end");
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
).start();
}
}
静态内部类
在内部类不需要访问外围类对象的时候,应该使用静态内部类。也可以称静态内部类为嵌套类(nested class)。
静态内部类只能访问外部类的静态成员。
单例模式可以用静态内部类实现。
public class Singleton{
// 利用JVM自身的类加载和初始化机制,既能确保单例,又能确保实例只在需要的时候被初始化
// 加载,链接(校验、准备、解析),初始化(只在用户程序主动调用时发生)
private static class InstanceHolder{
public static final Singleton INSTANCE = new Singleton();
}
// 构造函数私有化
private Singleton(){}
// 对外提供一个获取单例的方法
public static Singleton getInstance(){
return InstanceHolder.INSTANCE ;
}
}