在类的内部编写的类就叫内部类!
成员内部类
特点:
- 成员内部类可以无条件的访问外部类的所有元素
- 外部类访问内部类需要先创建一个内部类对象
- 成员内部类是依附外部类而存在
应用场景:不想被其他类公开使用的类,因为外部类只能定义为public
或者为缺省。类是属于对象的,所以不能有静态属性。
可以访问外部类所有元素的原理: 编译器会在编译时生成外部类和内部类两个字节码文件,还会给内部类的无参构造函数增减一个参数,为外部类的一个应用,且指向了外部类,故可以随意使用外部类的所有元素。(说明:内部类依赖于外部类的优先创建,不然会初始化失败。)
class Outer{
String str = "hello";
public Outer(String str){
this.str = str;
}
class Inner {
public void showStr(){
System.out.println(str);
}
}
}
局部内部类
定义在一个方法或者一个作用域里面的类,局部内部类的访问仅限于方法内或者作用域内局部内部类就像是方法里面的一个局部变量一样 ,是不能有public、protected、 privatel以及 static修饰符的。
使用场景: 局部内部类访问方法中定义的final类型的局部变量,局部类完全对外部隐藏,比较少用
问题:为什么访问所在方法的局部变量,必须要有final修饰?
原因(本质是生命周期问题) :
- 内部类new出来的对象在堆内存中;
- 局部变量跟着方法,在栈内存中;
- 方法运行完,立刻出栈,局部变量跟着消失;
- 但new出来的对象,会在堆内存持续存在,直到垃圾回收;
- 所以,要将该内存复制到常量池才能保存继续使用。
class Outer {
private String name ;
Outer( String name ) {
this . name = name ;
}
void asyncHello( ) {
Runnable r = new Runnable( ) {
@Override
public void run( ) {
System. out . println( "Hello," + Outer. this . name ) ;
}
};
new Thread( r) . start( ) ;
}
}
匿名内部类
往常,我们要使用接口方法,得先定义该接口的实现类–>重写该接口的所有抽象方法–> new实现类使用。而如果接口的实现类只是用唯一的一次,那么这种情况就可以省略该实现类的定义,而改为使用[ 匿名内部类]
使用场景: 适用于快速构造对象,lambda表达式等场景
匿名内部类包含以下内部分:
- 操作符: new
- 一个要实现的接口或要继承的类
- 一对括号,如果匿名子类与实例化普通类的语法类似,如果有构造参数;如果是实现一个接口,只需要一对空括号即可
- 一段被“{}”括起来类声明主体
- 末尾的“,”号(因为匿名类的声明是一个表达式,是语句的一部分,因此要以分号结尾)
代码案例:
接口
public interface MyInterface{
void method();
}
使用匿名内部类
public static void main(String[] args) {
MyInterface my = new MyInterface(){
public void method(){
@Override
System. out . println( "匿名内部类的方法") ;
}
}
}
静态内部类
静态内部类是不需要依赖于外部类的,并且他不能使用外部类的非static成员变量或者方法,这个和普通静态方法不能访问非静态方法或变量的原理一样,具体可以了解一下类的加载生命周期。
使用场景: 一个类与另一个类关联性比较强,但是又可以不依赖外部类而独立存在。比如HashMap与Node。静态内部类是属于类的,跟普通类使用没什么区别。
简单的说就是静态变量或方法是属于类的,非静态方法或变量是属于对象的,jvm加载类的时候就为类的静态变量或方法分配内存的,而非静态的需要等到要初始化对象的时候才给分配内存,而这个过程是动态的,也就是等到我们什么时候想用,才会有类的初始化过程。
代码案例
class Outer {
private static String NAME = " OUTER" ;
private String name ;
Outer( String name ) {
this.name = name ;
}
static class StaticNested {
void hello( ) {
System.out . println( "Hello,+ Outer . NAME) ;
}
}
}
内部类的简单应用:
- 每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个接口的实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整。
- 方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏
- 方便编写事件驱动程序
- 方便编写线程代码