匿名内部类使用注意事项
- 匿名内部类也就是没有名字的内部类
- 正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编
- 但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口
- 匿名内部类中是不能定义构造函数的
使用的形参为什么要final
- 我们给匿名内部类传递参数的时候,若该形参在内部类中需要被使用,那么该形参必须要为final。也就是说:当所在的方法的形参需要被内部类里面使用时,该形参必须为final
- 为什么必须要为final呢?
- 首先我们知道在内部类编译成功后,它会产生一个class文件,该class文件与外部类并不是同一class文件,仅仅只保留对外部类的引用。当外部类传入的参数需要被内部类调用时,从java程序的角度来看是直接被调用
- 在内部类中的属性和外部方法的参数两者从外表上看是同一个东西,但实际上却不是,所以他们两者是可以任意变化的,也就是说在内部类中我对属性的改变并不会影响到外部的形参,而然这从程序员的角度来看这是不可行的,毕竟站在程序的角度来看这两个根本就是同一个,如果内部类该变了,而外部方法的形参却没有改变这是难以理解和不可接受的,所以为了保持参数的一致性,就规定使用final来避免形参的不改变。
- 简单理解就是,拷贝引用,为了避免引用值发生改变,例如被外部类的方法修改等,而导致内部类得到的值不一致,于是用final来让该引用不可改变。
- 故如果定义了一个匿名内部类,并且希望它使用一个其外部定义的参数,那么编译器会要求该参数引用是final的。
匿名内部类初始化
我们一般都是利用构造器来完成某个实例的初始化工作的,但是匿名内部类是没有构造器的!那怎么来初始化匿名内部类呢?使用构造代码块!利用构造代码块能够达到为匿名内部类创建一个构造器的效果
常见的匿名内部类实例
public class Main {
public static void main(String[] args) {
Runnable r = () -> {
for (int i = 0; i < 5; i++) {
System.out.println(i + "");
}
};
Thread thread = new Thread(r);
thread.start();
}
}
Java内部类小结
-
成员内部类
也是最常规的内部类。该内部类相当于位于外部类的成员的位置。该内部类可以访问外部类的所有成员和方法,即使是private.
public class Main { public static void main(String[] args) { Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); inner.print("Inner 1"); inner = outer.getInner(); inner.print("Inner 2"); } } class Outer { public class Inner { public void print(String string) { System.out.println(string); } } public Inner getInner() { return new Inner(); } }
-
静态内部类
- 该类被修饰为static。同时作为静态内部类,他不能访问外部类非静态的成员或方法。
- 访问方式如上所述。
- 一般书写静态类时,不涉及成员变量。
public class Main { public static void main(String[] args) { Outer.Inner inner = new Outer.Inner(); inner.print("emmmm"); } } class Outer { static class Inner { public void print(String string) { System.out.println(string); } } public Inner getInner() { return new Inner(); } }
-
方法内部类
类被创建在外部类方法中
- 可以访问外部类中的成员
- 不能被static private修饰,因为他不再是成员位置,只有成员才能被修饰为static..…
- 因此内部类不能有静态成员
- 内部类要访问外部方法中的局部变量时,该局部变量需要被修饰成final。
class People { public People() { } } class Man{ public Man() { } public People getWoman() { class Woman extends People { // 局部内部类 int age = 18; } return new Woman(); } }
-
匿名内部类
- 就是一个内部类的简化版
- 定义匿名内部类的前提是,该类必须继承一个外部类或者实现接口。
具体实现如下图
这是一个继承类的正常版,一共分为这几步
为什么要有内部类
- 内部类是为了更好的封装,把内部类封装在外部类里,不允许同包其他类访问
- 内部类中的属性和方法即使是外部类也不能直接访问
- 相反内部类可以直接访问外部类的属性和方法,即使private
- 利于回调函数的编写。PS:回调函数是函数的迭代
- 当描述事物,如身体里的大脑时,大脑在身体内部,可以通过内部类直观描述
内部类特点
- 内部类对象不仅指向该内部类,还指向实例化该内部类的外部类对象的内存。
- 内部类和普通类一样可以重写Object类的方法,如toString方法;并且有构造函数,执行顺序依旧是先初始化属性,再执行构造函数
- 在编译完之后,会出现(外部类.class)和(外部类s内部类.class)两个类文件名。
- 内部类可以被修饰为private,只能被外部类所访问。事实上一般也都是如此书写。
- 内部类可以被写在外部类的任意位置,如成员位置,方法内。
内部类对象的建立
-
在同包其他类以及main方法中(内部类没有private修饰),先创建外部类对象,再通过外部类对象创建内部类
Out outer = new Out(); Out.In inner = outer.new In();
-
通过匿名外部类创建内部类
Out.In inner = outer.new In();
-
在外部类里
可直接创建对象,如
In inner=new In();
或者直接new In();
内部类访问
- 静态时,静态内部类只能访问外部类静态成员;非静态内部类都可以直接访问。(原因是:内部类有一个外部类名.this的指引)当访问外部类静态成员出现重名时,通过(外部类名.静态成员变量名)访问。如
Out.show();
- 重名情况下,非静态时,内部类访问自己内部类通过this.变量名。访问外部类通过(外部类名.this.变量名)访问。如
Out.this.show();
- 在没有重名的情况下,无论静态非静态,内部类直接通过变量名访问外部成员变量。
外部类访问
-
内部类为非静态时,外部类访问内部类,必须建立内部类对象。
-
内部类为静态时,外部类访问非静态成员,通过(外部类对象名.内部类名.方法名)访问,如
new Out().In.function()
-
内部类为静态时,外部类访问静态成员时,直接通过(外部类名.内部类名.方法名)
,如Out.In.funchtion();