Java内部类是如何实现的
内部类(inner class)是定义在另一个类中的类。
- 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。
- 内部类可以对同一个包中的其他类隐藏起来
- 当想定义一个回调函数且不想编写大量代码时,使用匿名(anonymous)内部类比较便捷。
以下简单举例:

一个时钟需要两个参数:发布通告的时间间隔以及开关铃声的标志。
public class TalkingClock {
private int interval;
private boolean beep;
public TalkingClock(int interval, boolean beep){
this.interval = interval;
this.beep = beep;
}
public void start(){
ActionListener listener = new TimePrinter();
Timer timer = new Timer(interval, listener);
timer.start();
}
public class TimePrinter implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("At the tone, the time is " + new Date());
if (beep) {
Toolkit.getDefaultToolkit().beep();
}
}
}
public static void main(String[] args) {
TalkingClock clock = new TalkingClock(1000, true);
clock.start();
//keep program running until user selects "OK"
JOptionPane.showMessageDialog(null, "Quit program?");
System.exit(0);
}
}
本代码主要想说明的是作为内部类的TimePrinter可以直接访问外部类TalkingClock的私有成员变量beep。
语法和用法大家很容易就学会了,那么内部类是如何实现直接访问其他类的私有成员变量呢?我们知道Java的私有成员变量只有类的实例本身能访问。
在最初学习Java语法的时候,是把内部类当做该类的一个属性去记忆和理解的,因此,自然可以访问私有变量。这个理解,现在看来是错误的。
内部类是一种编译器现象,与虚拟机无关。编译器将会把内部类翻译成用$分隔外部类名与内部类名的常规类文件,而虚拟机对此一无所知。
上述内部类被编译成TalkingClock$TimePrinter :
javap -private TalkingClock\$TimePrinter.class
Compiled from "TalkingClock.java"
public class com.test.java.clazz.innerclass.TalkingClock$TimePrinter implements java.awt.event.ActionListener {
final com.test.java.clazz.innerclass.TalkingClock this$0;
public com.test.java.clazz.innerclass.TalkingClock$TimePrinter(com.test.java.clazz.innerclass.TalkingClock);
public void actionPerformed(java.awt.event.ActionEvent);
}
可以看到,多了一个成员变量 final com.test.java.clazz.innerclass.TalkingClock this$0;,以及构造函数。内部类将通过这种方式持有外部类。
内部类的一个特性是可以访问外部的私有成员变量,这又是如何做到的呢。
查看外部类:
javap -private TalkingClock.class
Compiled from "TalkingClock.java"
public class com.test.java.clazz.innerclass.TalkingClock {
private int interval;
private boolean beep;
public com.test.java.clazz.innerclass.TalkingClock(int, boolean);
public void start();
public static void main(java.lang.String[]);
static boolean access$000(com.test.java.clazz.innerclass.TalkingClock);
}
可以发现,编译器给添加了新的静态方法static boolean access$000(com.test.java.clazz.innerclass.TalkingClock);,显然,我们可以通过类名调用这个方法来获取私有变量,相当于给添加了getter方法暴露私有变量。
查看变异后的内部类的actionPerformed方法:
public void actionPerformed(ActionEvent e) {
System.out.println("At the tone, the time is " + new Date());
if (TalkingClock.access$000(this.this$0)) {
Toolkit.getDefaultToolkit().beep();
}
}
综上,内部类通过编译器修改代码,实现了对类的访问。如果黑客在同包名下调用外部类的access$000,则是可以访问到私有变量的,这算是一个危险的地方吧。
局部内部类
仔细观察示例代码,TimePrinter只在start方法里调用了,我们可以把这个类缩小为局部内部类,即在方法内部声明内部类。
局部内部类可以访问局部变量。
我们将上述demo修改下:
public void start(int interval, boolean beep) {
class TimePrinter implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Every "+interval+", At the tone, the time is " + new Date());
if (beep) {
Toolkit.getDefaultToolkit().beep();
}
}
}
ActionListener listener = new TimePrinter();
Timer timer = new Timer(interval, listener);
timer.start();
}
局部类不能用public或private访问说明符进行说明,它的作用被限定在声明这个局部类的块中。
上述demo,局部内部类TimePrinter会访问局部变量interval和beep。同理,依旧是将这两个变量转换程内部类的成员变量,实现值的传递。
匿名内部类
public class AnonymousInnerClassTest {
@Data
static class Animal {
private String name;
public void sayHi(){
System.out.println("Hi " + name);
}
}
public static void main(String[] args) {
Animal animal = new Animal(){
@Override
public void sayHi() {
System.out.println("Wang");
}
};
animal.sayHi();
}
}
上述简单模拟一种Animal匿名类。编译后的结果为:
'AnonymousInnerClassTest$1.class' 'AnonymousInnerClassTest$Animal.class' AnonymousInnerClassTest.class
编译器生成了3个编译类。
AnonymousInnerClassTest
public class AnonymousInnerClassTest {
public AnonymousInnerClassTest() {
}
public static void main(String[] args) {
com.test.java.clazz.innerclass.AnonymousInnerClassTest.Animal animal = new 1();
animal.sayHi();
}
}
AnonymousInnerClassTest$1
import com.test.java.clazz.innerclass.AnonymousInnerClassTest.Animal;
final class AnonymousInnerClassTest$1 extends Animal {
AnonymousInnerClassTest$1() {
}
public void sayHi() {
System.out.println("Wang");
}
}
AnonymousInnerClassTest$Animal
class AnonymousInnerClassTest$Animal {
private String name;
public void sayHi() {
System.out.println("Hi " + this.name);
}
public AnonymousInnerClassTest$Animal() {
}
//getter setter equals toString
}
即编译器生成一个包访问权限的内部类AnonymousInnerClassTest$Animal, 对于其匿名子类,又生成了final的AnonymousInnerClassTest$1 ,如果还有第二个匿名类,则命名AnonymousInnerClassTest$2.
Java内部类是如何实现的的更多相关文章
- Java内部类final语义实现
本文描述在java内部类中,经常会引用外部类的变量信息.但是这些变量信息是如何传递给内部类的,在表面上并没有相应的线索.本文从字节码层描述在内部类中是如何实现这些语义的. 本地临时变量 基本类型 fi ...
- Java内部类详解
Java内部类详解 说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就 ...
- 黑马----JAVA内部类
黑马程序员:Java培训.Android培训.iOS培训..Net培训 黑马程序员--JAVA内部类 一.内部类分为显式内部类和匿名内部类. 二.显式内部类 1.即显式声明的内部类,它有类名. 2.显 ...
- java 内部类 *** 最爱那水货
注: 转载于http://blog.csdn.net/jiangxinyu/article/details/8177326 Java语言允许在类中再定义类,这种在其它类内部定义的类就叫内部类.内部类又 ...
- java内部类和匿名内部类
内部类即是包含在类里面的又一个类. java内部类分为: 成员内部类.静态嵌套类.方法内部类.匿名内部类 . 内部类的共性 (1).内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.clas ...
- Java内部类小程序(成员内部类,静态内部类,匿名内部类)
/** * 测试java内部类(成员内部类,静态内部类,匿名内部类) * 局部内部类不常用,就不写了. * @package :java05 * @author shaobn * @Describe ...
- [转] Java内部类详解
作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置 ...
- java内部类的作用分析
提起Java内部类(Inner Class)可能很多人不太熟悉,实际上类似的概念在C++里也有,那就是嵌套类(Nested Class),关于这两者的区别与联系,在下文中会有对比.内部类从表面上看,就 ...
- 9)Java内部类(Inner Class)
内部类:不可以有静态数据,静态方法或者又一个静态内部类 内部类的优点:隐藏类的细节,内部类可以声明为私有.内部类可以访问外部类的对象(包括private) 静态内部类:可以有静态数据,静 ...
- JAVA内部类(转)
源出处:JAVA内部类 在java语言中,有一种类叫做内部类(inner class),也称为嵌入类(nested class),它是定义在其他类的内部.内部类作为其外部类的一个成员,与其他成员一样, ...
随机推荐
- 深入理解Java对象
深入理解Java对象(理清关系) 1.对象的创建过程: 所有创建过程如下所示: new 类名 根据new的参数在常量池中定位一个类的符号引用. 如果没有找到这个符号引用,说明类还没有被加载,则进行类的 ...
- Vue.js 源码分析(九) 基础篇 生命周期详解
先来看看官网的介绍: 主要有八个生命周期,分别是: beforeCreate.created.beforeMount.mounted.beforeupdate.updated .beforeDes ...
- maven 学习---Maven 编译打包时如何忽略测试用例
本文地址:http://blog.csdn.net/wirelessqa/article/details/14057305 跳过测试阶段: mvn package -DskipTests 临时性跳过测 ...
- php中mysqli_error($conn)的用法
注意:用函数判断sql语句是否有错需要把这个函数写在mysqli_query("$conn连接语句","$sql语句")后面才能进行检测. 返回值:返回最近调用 ...
- hashlib(hmac)进阶之client跟server交互
首先我还是要强调不管任何相同的字符串通过hashlib加密之后都会产生相同的32位字符串,这个是日常Web中常用的加密算法如果我的client发送一个请求过来我server接受到后就要对该密码进行判断 ...
- nginx反向代理和负载均衡的基本配置
一.反向代理的基本配置 在原本默认的nginx上修改server配置 server { listen 2222;#监听请求端口 server_name 192.168.100.3;#监听请求地址,ng ...
- 【Mysql】了解Mysql中的启动参数和系统变量
一.启动参数 在程序启动时指定的设置项也称之为启动选项(startup options),这些选项控制着程序启动后的行为. 1)在命令行上使用选项 启动服务器程序的命令行后边指定启动选项的通用格式就是 ...
- C语言之整除
除法运算符:/ 当除数和被除数都整形时,就是整除. 当浮点数和整数放到一起运算时,C语言会将整数转换成浮点数,然后进行浮点数的运算. #include<stdio.h> int main( ...
- 5-剑指offer: 和为S的两个数字
题目描述 输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的. 输出描述: 对应每个测试案例,输出两个数,小的先输出. 代码 ...
- HBuilder创建app 3
一.Audio 模块实现开启手机摄像头 基于html5 plus http://www.html5plus.org/doc/zh_cn/audio.html 栗子: <!DOCTYPE html ...