内部类(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内部类是如何实现的的更多相关文章

  1. Java内部类final语义实现

    本文描述在java内部类中,经常会引用外部类的变量信息.但是这些变量信息是如何传递给内部类的,在表面上并没有相应的线索.本文从字节码层描述在内部类中是如何实现这些语义的. 本地临时变量 基本类型 fi ...

  2. Java内部类详解

    Java内部类详解 说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就 ...

  3. 黑马----JAVA内部类

    黑马程序员:Java培训.Android培训.iOS培训..Net培训 黑马程序员--JAVA内部类 一.内部类分为显式内部类和匿名内部类. 二.显式内部类 1.即显式声明的内部类,它有类名. 2.显 ...

  4. java 内部类 *** 最爱那水货

    注: 转载于http://blog.csdn.net/jiangxinyu/article/details/8177326 Java语言允许在类中再定义类,这种在其它类内部定义的类就叫内部类.内部类又 ...

  5. java内部类和匿名内部类

    内部类即是包含在类里面的又一个类. java内部类分为: 成员内部类.静态嵌套类.方法内部类.匿名内部类 . 内部类的共性 (1).内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.clas ...

  6. Java内部类小程序(成员内部类,静态内部类,匿名内部类)

    /** * 测试java内部类(成员内部类,静态内部类,匿名内部类) * 局部内部类不常用,就不写了. * @package :java05 * @author shaobn * @Describe ...

  7. [转] Java内部类详解

    作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置 ...

  8. java内部类的作用分析

    提起Java内部类(Inner Class)可能很多人不太熟悉,实际上类似的概念在C++里也有,那就是嵌套类(Nested Class),关于这两者的区别与联系,在下文中会有对比.内部类从表面上看,就 ...

  9. 9)Java内部类(Inner Class)

      内部类:不可以有静态数据,静态方法或者又一个静态内部类      内部类的优点:隐藏类的细节,内部类可以声明为私有.内部类可以访问外部类的对象(包括private) 静态内部类:可以有静态数据,静 ...

  10. JAVA内部类(转)

    源出处:JAVA内部类 在java语言中,有一种类叫做内部类(inner class),也称为嵌入类(nested class),它是定义在其他类的内部.内部类作为其外部类的一个成员,与其他成员一样, ...

随机推荐

  1. oracle创建删除视图

    --删除视图--DROP VIEW CQICC.V_APPLY_RECRUIT; --多表创建视图 CREATE OR REPLACE FORCE VIEW CQICC.V_APPLY_RECRUIT ...

  2. 使用jq操作脚本生成元素的事件

    其实这个很简单,是jq里面的一个delegate操作,具体如下: $("div").delegate("button","click",fu ...

  3. SQLi-LABS Page-2 (Adv Injections) Less23-Less26

    Less-23 GET - Error based - strip comments http://10.10.202.112/sqli/Less-23?id=1' Warning: mysql_fe ...

  4. Mongodb基础 学习小结

    MongoDB是一个基于分布式文件存储的数据库.由C++语言编写.旨在为WEB应用提供可扩展的高性能数据存储解决方案.MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能 ...

  5. GCN python 实现2:利用GCN进行节点分类

    参考:https://www.baidu.com/link?url=5oU-O_YQV8DdSTtRkgzsQ_vuwjJHyUOxqeAKhq98ZA5XtvKW8PNQwXgSlr5GpESRqh ...

  6. python中property简单使用与实现

    property简单使用 class P: """ property简单使用 """ def __init__(self,name): se ...

  7. Js中的对象、构造函数、原型、原型链及继承

    1.对象 在传统的面向过程的程序设计中,会造成函数或变量的冗余.而JS中对象的目的是将所有的具有相同属性或行为的代码整合到一起,形成一个集合,这样就会方便我们管理,例如: var person1={  ...

  8. CF1256(div3 java题解)

    A: 题意:给定A个N元,B个一元,问是否可以凑成S元. 思路:A*i+j=S 即 A*I<=S<=A*I+B 即min(S/N,A)+B>=S: /* @author nimphy ...

  9. HDU1213:How Many Tables(并查集入门)

    -----------刷点水题练习java------------- 题意:给定N点,M边的无向图,问有多少个连通块. 思路:可以搜索;  可以并查集.这里用并查集练习java的数组使用,ans=N, ...

  10. Nginx介绍(一)

    Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务. Nginx最大的特点是对高并发的支持和高效的负载均衡,在高并发的需求场景 ...