封装-------访问权限控制:普通类访问控制符最高只能控制到同包下的,但内部类可以私有只允许外部类访问。多继承--------解决方法之一 : 之二是桥接模式匿名内部类使代码简洁 : lambda表达式, 监听器模式,等 2 . 内部类的分类
- 成员内部类静态内部类局部内部类匿名内部类 ( 特殊的局部内部类 )
- 成员内部类
成员内部类不允许有静态属性和方法;成员内部类底层持有外部类对象的引用 , 所以内部类的初始化必须显示初始化外部类 (new OuterClass().new InnerClass()) ;成员内部类可以访问外部类的任何成员属性包括私有属性. (静态属性无法访问,无需多说);内部类和外部类变量重名,需要OuterClass.this.name指代外部类,this.name指代内部类;
ps: 至于为什么能访问外部类的私有属性,是因为编译器动了手脚.javac编译器为外部类生成了access通道方法.相当于get set方法以达到访问私有属性的目的.
public class OuterClass {
private static int source = 100;
private String name = "OuterClass";
private long birthday = 1844548484148L;
//成员内部类
public static void main(String[] args){
//初始化局部内部类必须显示初始化外部类,只有这样内部类访问外部类属性才不会有问题.
InnerClass innerClass = new OuterClass().new InnerClass();
innerClass.fun();
}
public class InnerClass{
private String name = "InnerClass";
private long birthday = 000000L;
//成员内部类不能有静态属性和方法
public void fun(){
System.out.println("外部类name"+ OuterClass.this.name);
System.out.println("内部类name"+ this.name);
}
}
}
- 静态内部类
静态内部类可以拥有 静态/非静态 属性/方法; 是一个完整的类;静态内部类不持有外部类实例的引用, 实例化不需要先实例化外部类; 至于访问外部类静态属性时外部类的加载是jvm完成的;静态内部类只允许访问外部类的任何静态域(包括私有属性),不允许访问非静态域,因为没有持有引用访问不到;
ps: 综上所述 静态内部类是一个完整的类,只是借用了外部类的壳,其他的和普通类没有什么不同.
public class OuterClass {
private static int source = 100;
private String name = "OuterClass";
private long birthday = 1844548484148L;
//静态内部类
public static class StaticInnerClass{
private String name;
private long birthday;
private static int source = 20;
// 静态内部类只能访问外部类的静态域.
public void fun(){
System.out.println("外部类静态属性"+ OuterClass.source);
System.out.println("内部类静态属性"+ source);
}
}
}
- 局部内部类
定义在方法中的内部类,作用域只在方法中;局部内部类可以和成员内部类一样只访问外部类的非静态域. 但是可以访问到局部变量;局部内部类如果要访问局部变量,则局部变量必须由final修饰; 因为局部变量分配在栈上,而内部类对象分配在堆,内部类对象的生命周期会比局部变量长 ; 当方法结束后栈帧弹出, 局部变量就会被销毁. 此时如果内部类对象再访问该局部变量就会出问题 ; JVM的实现方式是 在内部类对象内 保存局部变量的副本 ; 但是额外的,却带来了维护局部变量与其副本数据一致性的性能开销.于是干脆不允许在局部内部类中改变局部变量的值.便有了 局部内部类中访问的局部变量必须声明成final这一规定. 下面是一个例子
public class OuterClass {
private static int source = 100;
private String name = "OuterClass";
private long birthday = 1844548484148L;
public void fun(){
final int a = 0;
//局部内部类
class Runnable implements java.lang.Runnable {
@Override
public void run() {
birthday ++; //局部内部类也可以访问外部类的成员域
int b = a;
b ++;
}
}
new Thread (new Runnable()).start();
}
}
- 匿名内部类
没有名字的局部内部类,可以直接new一个抽象类或接口,只需在new的时候实现其抽象方法;匿名内部类 访问的局部变量也必须用final修饰(因为其本质是局部内部类)lambda表达式是匿名内部类的语法糖,所以访问的局部变量也必须用final修饰
public class OuterClass {
private static int source = 100;
private String name = "OuterClass";
private long birthday = 1844548484148L;
public void fun(){
final int a = 0;
new Thread (()-> {
birthday ++; //局部内部类也可以访问外部类的成员域
int b = a;
b ++;
}).start();
}
}
最后说明一点, 内部类是一个编译器现象,JVM虚拟机并不知道内部类与常规类有什么不同.至于这一点不理解的朋友们可以
参考博客



