众所周知,外部顶级类的类名需和类文件名相同,只能使用public和default。而内部类是指在外部类的内部再定义一个类,类名不需要和文件名相同。内部类可以是静态static的,也可用public,default(包限定),protected和private修饰。
内部类:
概念:
我们所说的内部类,官方的叫法是嵌套类(Nested Classes)。嵌套类包括静态内部类(Static Nested Classes)和内部类(Inner Classes)。而非静态内部类分为成员内部类,局部内部类(Local Classes)和匿名内部类(Anonymous Classes)。
内部类是一个编译是的概念,一旦编译成功,就会成为完全不同的两个类,分别为outer.class和outer$inner.class类。所以内部类的成员变量/方法名可以和外部类的相同。
内部类有什么作用?
1.内部类可以很好的实现隐藏
一般的非内部类,是不允许有 private 与protected权限的,但内部类可以
2.内部类拥有外围类的所有元素的访问权限 (private修饰也能访问)
3.可是实现多重继承 (让多个内部类分别继承多个其他类,使外部类可以同时获取多个其他类的属性)
4.可以避免修改接口而实现同一个类中两种同名方法的调用。(外部类继承,让内部类实现接口)
静态内部类:
形如:
public class OuterClass {
private String name;
static class StaticInerCls{
private String name;
}
}
静态内部类除了访问权限修饰符比外围类多以外, 和外围类没有区别, 只是代码上将静态内部类组织在了外部类里面。
创建静态内部类:以Class.Iner的形式
OuterClass.StaticInerCls staticInerCls = new OuterClass.StaticInerCls();
静态内部类与普通内部类的区别
- 静态内部类不持有外部类的引用 在普通内部类中,我们可以直接访问外部类的属性、方法,即使是private类型也可以访问,这是因为内部类持有一个外部类的引用,可以自由访问。而静态内部类,则只可以访问外部类的静态方法和静态属性(即使是private权限也能访问,这是由其代码位置所决定的),其他则不能访问。
- 静态内部类不依赖外部类 普通内部类与外部类之间是相互依赖的关系,内部类实例不能脱离外部类实例,也就是说它们会同生同死,一起声明,一起被垃圾回收器回收。而静态内部类是可以独立存在的,即使外部类消亡了,静态内部类还是可以存在的。
非静态内部类:
非静态内部类能访问外部类的一切成员, 包括私有成员。外部类虽然不能直接访问内部类的成员, 但是可以通过内部类的实例访问内部类的私有成员。
成员内部类:
形如:
public class OuterCls {
private String name;
public String getName(){
return name;
}
class InerCls{
private String name;
public String getName(){
return name;
}
}
}
成员内部类可以直接使用外部类的所有成员和方法,即使是private修饰的。而外部类要访问内部类的所有成员变量和方法,内需要通过内部类的对象来获取。(谁叫它是亲儿子呢?) 要注意的是,成员内部类不能含有static的变量和方法。因为成员内部类需要先创建了外部类,才能创建它自己的。
创建内部类对象方法,以object.new Iner的形式:
OuterCls outerCls = new OuterCls();
OuterCls.InerCls inerCls = outerCls.new InerCls();
成员内部类不能有static修饰的成员,但是却允许定义常量。
public class OuterClass {
private String name;
static class StaticInerCls{
private String name;
}
class InerCls{
private String name;
private static int id; //不允许,会报错
private static final int TYPE = 0; //允许
}
}
静态类的主要特性:
1:仅包含静态成员。
2:无法实例化。
3:是密封的。
4:不能包含实例构造函数。
为什么普通内部类不能有静态变量
1.成员内部类 之所以叫做成员 就是说他是类实例的一部分 而不是类的一部分。
2.结构上来说 他和你声明的成员变量是一样的地位 一个特殊的成员变量 而静态的变量是类的一部分和实例无关。
3.非静态内部类能访问外部类的一切属性;静态内部类只能访问外部类的静态属性。
内部类中要想访问外部类的非静态成员,需要使用外部类进行引导:外部内名.this.属性的名称;想要访问外部类静态成员时,可以省略this:外部类名.属性的名称。
/**
* 获取外部类静态属性
* @return
*/
public String getParentName() {
return OuterClass.staticName;
}
/**
* 获取外部类非静态属性
* @return
*/
public String getParentName() {
return OuterClass.this.name;
}
局部内部类:
指内部类定义在方法体内,只能在该方法或条件的作用域内才能使用,退出这写作用域就无法引用。
作为非静态内部类的一种特殊形式, 非静态内部类的所有限制对局部类同样成立。局部类不仅可以访问外部类的所有成员,还可以访问方法体的局部变量,但必须是final修饰的局部变量。
为什么局部类访问局部变量,变量必须加上final?
场景:
Object method(){
int localVariable = 0;
class Inner{
void println(){
System.out.println("localVariable " + localVariable++);
}
}
Object in = new Inner();
return in;
}
}
这里的localVariable会变红,提示需要给localVariable变量加final修饰。
解析:这是作用域的问题。在方法method执行完成后,局部变量value就失效了,而在new Inner()产生的in对象还存在obj的引用,这样对象就访问了一个不存在的变量,是不允许的。iner还存在,在外面和后续调用该局部变量时,这个局部变量可能已经失效了。但为什么加上final就可以保证能访问呢?这里Java采用了一种copy local variable的方法实现,定义为final的变量,会拷贝一份存到局部内部类中,后续使用持续维护这个对象在生命周期内,所以可以继续访问。
修改之后:
Object method(){
final int[] localVariable = {0};
class Inner {
void println(){
System.out.println("localVariable " + localVariable[0]++);
}
}
Object in = new Inner();
return in;
}
还有一种解释是局部变量可能在方法中值被修改,而导致内部类得到的值不一致,于是用final来让该引用不可改变。
注:final可以修饰的范围有类,方法,属性。修饰类,该类不可以被继承;修饰方法,该方法不可以被子类重写;修饰变量,该变量值不能被修改。
匿名内部类:
定义:
new抽象类或者是接口,能够获得一个抽象类或者是接口的一个实现类对象,这个实现类对象没有名字,所以称之为匿名内部类。本质是一个继承了类或者实现了接口的子类匿名对象。
匿名内部类的条件:
1.匿名内部类不能有构造方法。
2.匿名内部类不能定义任何静态成员、方法和类。
3.匿名内部类不能是public,protected,private,static。
4.只能创建匿名内部类的一个实例。
5.一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
6.因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。
/**
* 匿名内部类
*/
private Thread thread = new Thread(new Runnable() {
@Override
public void run() {
}
});
Runnable只是一个接口,却可以直接new出一个实例,创建的对象是这个接口的一个实现类对象,没有名字,只能使用一次。
参考:https://juejin.cn/post/6986449929426173965