目录
内部类
什么是内部类?
将一个类定义在另一个类的内部,这就是内部类。内部类和普通类一样,都是类,都可以定义属性、方法 (包括构造方法,静态方法等等)。内部类本身就是类的一个属性,与其他属性定义方式一致。
内部类的分类有哪些?
内部类可以分为四种:成员内部类(其实包含静态内部类和非静态内部类)、局部内部类、匿名内部类。
静态内部类:
-
静态内部类是使用static修饰的内部类,其内可以定义非静态成员,也可以定义静态成员。
解释:静态内部类是一个比较特殊的情况,一旦内部类使用static修饰,那么这个内部类就升级为顶级类。除了写在一个类的内部以外,静态内部类具备所有外部类的特性,即静态内部类中可以定义静态属性和方法等。 -
静态内部类中的方法(成员方法、静态方法)只能访问外部类的静态成员,不能访问外部类的非静态成员。
-
静态内部类可以被4大权限修饰符修饰,被public修饰则任意位置的其他类都可以访问,被private修饰只能被外部类内部访问。
-
静态内部类内部的静态成员,可以直接使用外部类.静态内部类.静态成员访问。
public class Outer {
private static int radius = 1;
static class StaticInner {
public void visit() {
System.out.println("visit outer static variable:" + radius);
}
}
}
静态内部类中方法可以访问外部类所有的静态变量,而不可访问外部类的非静态变量;静态内部类的创建方式,new 外部类.静态内部类(),如下:
Outer.StaticInner inner = new Outer.StaticInner();
inner.visit();
成员内部类:
定义在类内部,成员位置上的非静态类,就是成员内部类。
- 成员内部类可以被任何的访问修饰符修饰。
- 成员内部类的内部不能定义静态成员。
- 成员内部类也是类,可以继承类,可以实现接口,方法也可以重写,重载,this和super随便使用。
- 成员内部类可以访问外部类所有的变量和方法,包括静态和非静态,私有和公有。
- 其它类如何访问成员内部类,被public修饰的成员内部类,可以被不同包的其他类访问;其他情况和普通类一样…
public class Outer {
private static int radius = 1;
private int count =2;
class Inner {
public void visit() {
System.out.println("visit outer static variable:" + radius);
System.out.println("visit outer variable:" + count);
}
}
}
成员内部类可以访问外部类所有的变量和方法,包括静态和非静态,私有和公有。成员内部类依赖于外部类的实例,它的创建方式外部类实例.new 内部类(),如下:
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.visit();
局部内部类:
-
定义在方法中的内部类,就是局部内部类。其作用域仅限于方法内,方法外部无法访问该内部类。
-
局部内部类就像是方法里面的一个局部变量一样,是不能有 public、protected、private 以及 static 修饰符的。
-
局部内部类不能定义静态成员。
-
局部内部类只能访问方法中定义的 final 类型的局部变量
解释:
当方法被调用运行完毕之后,局部变量就已消亡了。但内部类对象可能还存在,
直到没有被引用时才会消亡。此时就会出现一种情况,就是内部类要访问一个不存在的局部变量;
==>使用final修饰符不仅会保持对象的引用不会改变,而且编译器还会持续维护这个对象在回调方法中的生命周期.
局部内部类并不是直接调用方法传进来的参数,而是内部类将传进来的参数通过自己的构造器备份到了自己的内部,
自己内部的方法调用的实际是自己的属性而不是外部类方法的参数;
防止被篡改数据,而导致内部类得到的值不一致
public class Outer {
private int out_a = 1;
private static int STATIC_b = 2;
public void testFunctionClass(){
int inner_c =3;
class Inner {
private void fun(){
System.out.println(out_a);
System.out.println(STATIC_b);
System.out.println(inner_c);
}
}
Inner inner = new Inner();
inner.fun();
}
public static void testStaticFunctionClass(){
int d =3;
class Inner {
private void fun(){
// System.out.println(out_a); 编译错误,定义在静态方法中的局部类不可以访问外部类的实例变量
System.out.println(STATIC_b);
System.out.println(d);
}
}
Inner inner = new Inner();
inner.fun();
}
}
定义在实例方法中的局部类可以访问外部类的所有变量和方法,定义在静态方法中的局部类只能访问外部类的静态变量和方法。局部内部类的创建方式,在对应方法内,new 内部类(),如下:
public static void testStaticFunctionClass(){
class Inner {
}
Inner inner = new Inner();
}
匿名内部类:
匿名内部类就是没有名字的内部类,日常开发中使用的比较多。
public class Outer {
private void test(final int i) {
new Service() {
public void method() {
for (int j = 0; j < i; j++) {
System.out.println("匿名内部类" );
}
}
}.method();
}
}
//匿名内部类必须继承或实现一个已有的接口
interface Service{
void method();
}
除了没有名字,匿名内部类还有以下特点:
- 首先匿名内部类也是内部类的一种,只不过它没名字
- 匿名内部类必须继承一个抽象类或者实现一个接口。
- 匿名内部类不能定义任何静态成员和静态方法。
- 当所在的方法的形参需要被匿名内部类使用时,必须声明为 final。
- 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
- 匿名内部类最常用的使用场景就是快速创建抽象类或接口的实例。
- 如果某个类判定只使用一次,那么就不要通过new关键字创建那个类的对象,而是使用匿名内部类的方式。
匿名内部类创建方式:
new 类/接口{
//匿名内部类实现部分
}
总结:除了静态内部类可以定义静态成员外,其他内部内中都不能定义静态成员。
内部类的优点:
我们为什么要使用内部类呢?因为它有以下优点:
- 一个内部类对象可以访问创建它的外部类对象的内容,包括私有数据!
- 内部类不为同一包的其他类所见,具有很好的封装性;
- 内部类有效实现了“多重继承”,优化 java 单继承的缺陷。
- 匿名内部类可以很方便的定义回调。
内部类有哪些应用场景:
- 一些多算法场合
- 解决一些非面向对象的语句块。
- 适当使用内部类,使得代码更加灵活和富有扩展性。
- 当某个类除了它的外部类,不再被其他的类使用时。
局部内部类和匿名内部类访问局部变量的时候,为什么变量必须要加上final?
局部内部类和匿名内部类访问局部变量的时候,为什么变量必须要加上final呢?它内部原理是什么呢?
先看这段代码:
public class Outer {
void outMethod(){
final int a =10;
class Inner {
void innerMethod(){
System.out.println(a);
}
}
}
}
以上例子,为什么要加final呢?是因为生命周期不一致, 局部变量直接存储在栈中,当方法执行结束后,非final的局部变量就被销毁。而局部内部类对局部变量的引用依然存在,如果局部内部类要调用局部变量时,就会出错。加了final,可以确保局部内部类使用的变量与外层的局部变量区分开,解决了这个问题。
为什么内部类调用的外部变量必须是final修饰的?
因为生命周期的原因。方法中的局部变量,方法结束后这个变量就要释放掉,final保证这个变量始终指向一个对象。首先,内部类和外部类其实是处于同一个级别,内部类不会因为定义在方法中就会随着方法的执行完毕而跟随者被销毁。问题就来了,如果外部类的方法中的变量不定义final,那么当外部类方法执行完毕的时候,这个局部变量肯定也就被GC了,然而内部类的某个方法还没有执行完,这个时候他所引用的外部变量已经找不到了。如果定义为final,java会将这个变量复制一份作为成员变量内置于内部类中,这样的话,由于final所修饰的值始终无法改变,所以这个变量所指向的内存区域就不会变。
为了解决:局部变量的生命周期与局部内部类的对象的生命周期的不一致性问题
内部类相关,看程序说出运行结果
public class Outer {
private int age = 12;
class Inner {
private int age = 13;
public void print() {
int age = 14;
System.out.println("局部变量:" + age);
System.out.println("内部类变量:" + this.age);
System.out.println("外部类变量:" + Outer.this.age);
}
}
public static void main(String[] args) {
Outer.Inner in = new Outer().new Inner();
in.print();
}
}
运行结果:
局部变量:14
内部类变量:13
外部类变量:12