永川网站建设熊掌号,做网站开专票税钱是多少个点,池州最好的网站建设,中国互联网网站性能内部类三连击#xff1a;
《内部类详解————匿名内部类》
《内部类详解————局部内部类》
《内部类详解————静态嵌套类》
应用场景
由于匿名内部类不利于代码的重用#xff0c;因此#xff0c;一般在确定此内部类只会使用一次时#xff0c;才会使用匿名内部…内部类三连击
《内部类详解————匿名内部类》
《内部类详解————局部内部类》
《内部类详解————静态嵌套类》
应用场景
由于匿名内部类不利于代码的重用因此一般在确定此内部类只会使用一次时才会使用匿名内部类。
形式
public class OutterClass {public Runnable task() {return new Runnable() {Overridepublic void run() {System.out.println(匿名内部类...);}};}
}
这种实现方式是不是很眼熟呢 // 初始化线程实例Thread thread new Thread(new Runnable() {Overridepublic void run() {System.out.println(匿名内部类...);}});
我们为线程创建一个Runnable子类实例的方式就是一种匿名内部类的写法。我们通过这种没有名字的类实现了将实现类下称子类实例创建与子类定义结合在一起的优雅格式这也就是所谓的“使用类的定义直接创建实例”。
上面的代码是实现了Runnable接口并重写了其中的run()方法当然我们可以自己定义一个类非接口然后通过这种匿名内部类的方式来隐式的继承并重写基类中的方法。
不论是继承父类还是实现接口实际上拿到的是父类或接口的引用。这个父类引用实际指向的是一个由匿名内部类定义的类的实例。因此这个被继承的父类或接口必须是事先存在的。否则编译器会提示你创建这个类。
使用规则
经过查阅资料和实操得出的匿名内部类的几条规则
规则一匿名内部类中的方法都是通过父类引用访问的所以如果定义了一个在父类中没有的方法那么这个方法是不能被这个父类引用调用到的。可以仅仅作为匿名内部类中方法之间的代码共享。
规则二匿名内部类既可以继承父类也可以实现接口但是不能两者兼备。而且如果实现接口也只能实现一个接口。
规则三匿名内部类中不可能有构造器。但可通过实例初始化块 来达到构造器的效果但是也不能重载实例初始化方法即仅有一个这样的“构造器”。关于实例初始化《Java静态初始化实例初始化以及构造方法》
规则四在匿名内部类中如果希望使用一个其外部定义的对象那么编译器会要求其参数引用是final的。
关于第四条规则这里牵涉了一个重要的且比较复杂的问题。
使用案例
/** 定义接口*/
public interface MyInterface {void doSomething();
}
public class TryUsingAnonymousClass {// 外部类成员方法public MyInterface useMyInterface() {final int number 201855;// jdk1.8后可以省略finalfinal Object obj new Object();// jdk1.8后可以省略finalMyInterface myInterface new MyInterface() {// 匿名内部类Overridepublic void doSomething() {System.out.println(匿名内部类中使用基本数据类型 number);System.out.println(匿名内部类中使用引用数据类型 obj);}};return myInterface;}public static void main(String[] args) {TryUsingAnonymousClass tc new TryUsingAnonymousClass();MyInterface inter tc.useMyInterface();inter.doSomething();}
}
输出
匿名内部类中使用基本数据类型201855
匿名内部类中使用引用数据类型java.lang.Object15db9742
我们通过匿名内部类的方式实现了接口MyInterface并使用了外部类的成员方法useMyInterface() 中定义的两个局部变量
int number 201855;
Object obj new Object();
在jdk1.8之后新增了effectively final功能开发者可以不必显式地使用final关键字来修饰局部内部类或匿名内部类中用到的局部变量由系统默认添加。
因此我们在匿名内部类中用到的局部变量必须为常量对于基本类型其值恒定不变对于引用类型其引用即指向的地址恒定不变。
如果强行改值则会报错这是在1.8程序上未使用final定义number时的尝试系统果然默认此值为final的 不得不引出的局部变量与匿名内部类实例生命周期问题
我们知道成员方法中的局部变量是在运行期进行定义和初始化的而局部内部类包括匿名内部类虽然是在方法中定义的但是它却依然会在编译期实现从java文件到class文件的转化即编译成class文件。
编译期在前运行期在后。而我们却要在编译期使用运行期定义的变量
怎么办我们脑海中浮现了两个在编译期便能取得常量的相关关键字static 和 final 但显然static无法定义局部变量。
那final能为我们的程序带来什么
翻阅《Java编程思想》中对final关键字的剖析第四版140页
一个永不改变的编译时常量。
《深入理解Java虚拟机JVM高级特性与最佳实践》中第二版168页对于Class文件常量池也做出了相关解释
常量池博主注此常量池为class文件常量池非运行时常量池两者最大的区别是后者具有动态性
中主要存放两大类常量字面量和符号引用。字面量比较接近于Java语言层的常量概念如文本字符串、声明为final的常量值等。
匿名内部类被编译成了class文件它将final定义的局部变量编译进了class文件的常量池中因此我们会看上面的代码
public static void main(String[] args) {TryUsingAnonymousClass tc new TryUsingAnonymousClass();MyInterface inter tc.useMyInterface();inter.doSomething();}
int型局部变量number和Object类型obj在方法useMyInterface()执行完毕之后即结束了生命周期但是在下面通过调用inter对象的doSomething()方法依然可以有效的输出这两个值说明这两个常量并没有受到外部类方法执行完毕而导致局部变量生命周期结束的问题实际上number和obj已经存在于匿名内部类对应的class文件中的常量池中。
虽然final修饰的常量解决了在编译期拿到运行期的变量的问题但是final带来的副作用是这个值无法改变。
对于需要改变局部变量值的情况我们可以通过在匿名内部类中使用赋值的方式学名引用拷贝 .0来“接管”局部变量的值然后我们就可以随意更改这个值了。
综上就是最近对匿名内部类的研究和讨论。结合了final关键字的用法和class文件常量池来多角度讨论匿名内部类的final常量问题。后期如果有什么新的理解还会继续更新。文中的错别字和排版不适感博主已经进行了纠错和修改如果各位在阅读时发现了任何错误都请在文末留言。