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),它是定义在其他类的内部.内部类作为其外部类的一个成员,与其他成员一样, ...
随机推荐
- 最简单取得外网IP
#coding=utf-8import urllib2import re print re.search('\d+\.\d+\.\d+\.\d+',urllib2.urlopen("http ...
- windows下安装pip教程
下载地址是:https://pypi.python.org/pypi/pip#downloads 下载完成之后,解压到一个文件夹,用CMD控制台进入解压目录,输入: python setup.py i ...
- Java生鲜电商平台-服务器部署设计与架构
Java生鲜电商平台-服务器部署设计与架构 补充说明:Java开源生鲜电商平台-服务器部署设计与架构,指的是通过服务器正式上线整个项目,进行正式的运营. 回顾整个章节,我们涉及到以下几个方面: 1. ...
- 百度云BCC安装WordPress镜像
重装系统 在BCC实例中,重装系统选择WordPress. Centos 6.5 x64Apache 2.2.15: Web 主目录:/home/www/default 配置文件目录:/etc/htt ...
- IntelliJ IDEA 中使用 Lambok (注解无效问题的解决)
一,概述 Lambok可以说一个能很大提高开发效率的插件,只要在使用注解的方式就能实现很多常用的功能.如常用的@Data能在编译阶段自动生成toString方法,getter方法setter方法等. ...
- 4.InfluxDB-InfluxQL基础语法教程--基本select语句
本文翻译自官网,官网地址:(https://docs.influxdata.com/influxdb/v1.7/query_language/data_exploration/) 基本语法如下: SE ...
- Linux Tools 之 iostat 工具总结
iostat是Linux中被用来监控系统的I/O设备活动情况的工具,是input/output statistics的缩写.它可以生成三种类型的报告: CPU利用率报告 设备利用率报告 网络文件系统报 ...
- Ubuntu下部署Shipyard管理docker
使用k8s对于我这种新人来说,难度有点大.遂尝试使用Shipyard这个docker web ui工具来进行管理,以方便入门. 首先,我们还是需要在我们的主机上安装docker. 然后官方提供了自动安 ...
- USACO Cow Cars
洛谷 P2909 [USACO08OPEN]牛的车Cow Cars https://www.luogu.org/problemnew/show/P2909 JDOJ 2584: USACO 2008 ...
- JAVA基础概念(二)
一.java修饰符和使用场景 修饰符是用来定义类.方法或者变量的访问权限,分为两大类: 访问修饰符: 限定类.属性.方法是否可以被程序里其他部分访问和调用. private<default< ...