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.

原文地址:https://www.cnblogs.com/woshimrf/p/java-inner-class.html

时间: 2024-08-30 13:38:58

Java内部类是如何实现的的相关文章

Java内部类

本文是<Java核心技术 卷1>中第六章接口与内部类中关于内部类的阅读总结. Java中的内部类(inner class)是定义在另一个类内部的类.那么内部类有什么用呢?这里主要由三个内部类存在的原因: 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据.即,如果类A中定义了类B,那么类B可以访问类A中的数据,甚至是私有数据,但类A不能访问类B中的私有数据: 内部类可以对同一个包中的其他类隐藏起来.在一个包中,定义一个类时,即使不加上访问权限关键词,这个类也是包内其他类可访问的,不

Java内部类:局部内部类(三)

Java内部类分为4个部分进行阐述,分别为概览.成员内部类.局部内部类和匿名内部类. 在本文中是Java内部类的局部内部类,主要讲局部内部类的概念和在使用局部内部的过程中,需要注意的一个细节. 1.局部内部类的概念 在一个类的方法内部定义另外一个类,那么另外一个类就称作为局部内部类. class OutterClass { void test() { class InnerClass//局部内部类 { } } } 在上述代码中,InnerClass定义在OutterClass的test方法的内部

Java内部类:匿名内部类(四)

Java内部类分为4个部分进行阐述,分别为概览.成员内部类.局部内部类和匿名内部类. 在本文中是Java内部类的匿名内部类,主要讲述匿名内部类的概念.匿名内部类的好处.匿名内部类的使用前提.和匿名内部类的应用场景. 1.匿名内部类的概念 没有类名的类就称作为匿名内部类 2.匿名内部类的好处 简化书写 3.匿名内部类的使用前提 必须存在继承或者实现关系才能使用 4.匿名内部类的应用场景 匿名内部类一般是用于实参 示例代码: package com.rk.innerclass; public cla

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

1 /** 2 * 测试java内部类(成员内部类,静态内部类,匿名内部类) 3 * 局部内部类不常用,就不写了. 4 * @package :java05 5 * @author shaobn 6 * @Describe : 7 * @Time: 2015-9-6 下午8:32:38 8 */ 9 public class TestInner { 10 11 /** 12 * @author shaobn 13 * @Describe : 14 * @param args 15 * @Time

Java内部类的使用小结

内部类是指在一个外部类的内部再定义一个类.类名不需要和文件夹相同. *内部类可以是静态static的,也可用public,default,protected和private修饰.(而外部顶级类即类名和文件名相同的只能使用public和default). 注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类.对于一个名为outer的外部类和其内部定义的名为inner的内部类.编译完成后出现outer.class和outer$inner.class两类.所以内部类的成员变量/方法名可

java内部类和匿名内部类

内部类即是包含在类里面的又一个类. java内部类分为: 成员内部类.静态嵌套类.方法内部类.匿名内部类 . 内部类的共性 (1).内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号 . (2).内部类不能用普通的方式访问.内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否是private的 . (3).内部类声明成静态的,就不能随便的访问外部类的成员变量了,此时内部类只能访问外部类的静态成员变量 . 成员内部类

Java内部类的继承

Java内部类的构造器必须连接到指向其外围类对象的引用(构造内部类必须给它一个外部类对象的引用,内部类依赖于外部类对象),所以在继承内部类的时候,需要在导出类的构造器中手动加入对基类构造器的调用. 因为,在导出类实例化时,并不存在一个外围类对象,以让导出类的实例去连接到它. 所以,我们需要创建一个外围类,然后用一个特定的语法来表明内部类与外围类的关系. 在下例子中,需要给导出类InheritInner一个来自内部类的外围类中的引用.普通的继承,只需在导出类构造器中加入super();,而内部类则

【转】Java 内部类种类及使用解析

Java 内部类种类及使用解析 内部类Inner Class 将相关的类组织在一起,从而降低了命名空间的混乱. 一个内部类可以定义在另一个类里,可以定义在函数里,甚至可以作为一个表达式的一部分. Java中的内部类共分为四种: 静态内部类static inner class (also called nested class) 成员内部类member inner class 局部内部类local inner class 匿名内部类anonymous inner class 静态内部类Static

Java内部类学习总结

目录 目录 概述 非静态内部类 从外部类的非静态方法中实例化内部类 从外部类的静态方法中实例化内部类 内部类的this引用 静态内部类 从外部类的非静态方法中实例化静态内部类 从外部类静态方法中实例化静态内部类 匿名内部类 方法内部类 概述 最近学习python,发现python是支持多继承的,这让我想起Java是通过内部类实现的这套机制.这篇文章不是讲如何通过内部类实现多继承,而是总结一下内部类的类型和使用方法. Java内部类分为: 非静态内部类 静态内部类 局部内部类 匿名内部类 内部类在

[转] Java内部类详解

作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. Java内部类详解 说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就来一探究竟.下面是本文的目录大纲: 一.内部类基础 二.