Java基础-内部类

内部类(inner class)

内部类是定义在一个类中的类,可分为内部类、局部内部类、匿名内部类、静态内部类。

之前一直很纠结为什么需要使用内部类?主要有三个原因:

  1. 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。
  2. 内部类可以对同一个包中的其他类隐藏起来。
  3. 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。

> 注释:回调函数就是一个参数,将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数。这个过程就叫做回调,就是回头调用的意思。主函数的事先干完,回头再调用传进来的那个函数

内部类 Example

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;

public class InnerClassTest {

    public static void main(String[] args) {
        TalkingClock clock = new TalkingClock(1000,true);
        clock.start();

        JOptionPane.showMessageDialog(null,"确定退出?");
        System.exit(0);//系统停止
    }

}
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为javax.swing包下的,不是java.util.Timer
        Timer t = new Timer(interval,listener);
        t.start();
    }
	// 内部类
    public class TimePrinter implements ActionListener{
        public void actionPerformed(ActionEvent event){
            System.out.println("time is"+new Date());
            if (beep)Toolkit.getDefaultToolkit().beep();// flag:1 这里引用了TalkingClock对象的域中的变量
            // 这个类是所有实际类的抽象超类,抽象窗口工具包的实现的子类Toolkit类用于绑定各种组件
            //*到特定的本地工具包实现。比如在windows上就是调用windows的本地工具包发声音
        }
    }
}

注释:flag:1 为了能让程序执行,内部类的对象总有一个隐式引用,它指向了创建它的外部类对象。这个引用在内部类的定义中是不可见的。
在这里插入图片描述
通过javac -encoding utf-8 InnerClassTest.java编译后可以看见字节码文件(指定编码否则中文报错)
在这里插入图片描述
再使用反编译 javap -private TalkingClock$TimePrinter
这里可以清晰的看到编译器为了引用外围类,生成了一个附加的实例域this$0,这个是有编译器合成的,在自己编写的代码中不能够引用它
并且上边的例子中
ActionListener listener = new TimePrinter();等价ActionListener listener = this.new TimePrinter();
if (beep)Toolkit.getDefaultToolkit().beep();等价if (TalkingClock1.this.beep) Toolkit.getDefaultToolkit().beep();
通常this限定词是多余的,不过,可以通过显示地命名将外围类引用设置为其他的对象。例如:如果对象TimePrinter是一个共有内部类,对于任意的语音时钟都可以构造一个TimePrinter:
TalkingClock jabberer = new TalkingClock(1000.true);
TalkingClock.TimePrinter listener = jabberer.new TimePrinter();
// 在外围类的作用域之外,可以这样引用内部类OuterClass.InnerClass

综上所述:内部类是一种编译器现象,与虚拟机无关。编译器会把内部类翻译成用$分隔外部类名与内部类名的常规类文件,而且虚拟机则对此一无所知。

局部内部类

局部内部类有一个优势,就是对外部世界可以完全隐藏起来,即使是TalkingClock类中的其他代码也不能访问它,除了start方法之外,没有任何方法知道TimePrinter类的存在。

局部内部类Example

public void start(){
    // 局部内部类不能用public、private访问说明符进行声明,它的作用域只在这个局部类块中
    class TimePrinter implements ActionListener{

        public void actionPerformed(ActionEvent event){
            System.out.println("time is"+new Date());
            if (TalkingClock1.this.beep) Toolkit.getDefaultToolkit().beep();

        }
    }

    ActionListener listener = new TimePrinter();
    Timer t = new Timer(interval,listener);
    t.start();
}

注释:实际上局部类还有一个优点,就是它能访问局部变量,只是那些局部变量必须事实上为final,说明一旦赋值就不能再改变。

class TalkingClock{
	// 这是Java SE8 的写法,再次之前需要用final限制如 public void start(int interval, final boolean beep)
    public void start(int interval, boolean beep){
        // 局部内部类不能用public、private访问说明符进行声明,它的作用域只在这个局部类块中
        class TimePrinter implements ActionListener{

            public void actionPerformed(ActionEvent event){
                System.out.println("time is"+new Date());
                if (beep) Toolkit.getDefaultToolkit().beep();
            }
        }

        ActionListener listener = new TimePrinter();
        Timer t = new Timer(interval,listener);
        t.start();
    }
}

同样编译后再反编译,可以看到进行了变量备份
在这里插入图片描述

匿名内部类

将局部内部类在深入一下,假如只创建这个类的一个对象,就不必命名了。
通用语法规则:
new SuperType(construction parameters){
inner class methods and data
}

匿名内部类Example

    public void start(int interval, boolean beep){

        ActionListener listener = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("time is"+new Date());
                if (beep) Toolkit.getDefaultToolkit().beep();
            }
        };
        Timer t = new Timer(interval,listener);
        t.start();
    }

注释:习惯的做法是用匿名内部类实现事件监听和其他回调。如今最好还是用lambda表达式

public void start(int interval, boolean beep){
    Timer t = new Timer(interval,event->{
        System.out.println("time is"+new Date());
        if (beep) Toolkit.getDefaultToolkit().beep();
    });
    t.start();
}

静态内部类

有时候内部类只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外围类对象,这就可以将内部类声明为static,以便取消产生的引用。

特例:静态内部类单例模式

public class SingleTon{
  private SingleTon(){}
 
  private static class SingleTonHoler{
     private static SingleTon INSTANCE = new SingleTon();
 }
 
  public static SingleTon getInstance(){
    return SingleTonHoler.INSTANCE;
  }
}
首先内部静态类和其成员必须是public或者protected的,public时可以直接从外部访问,protected则只能通过继承访问

如果其成员和方法都为静态的
可以直接访问,如
A.B.静态成员
A.B.静态方法

但是如果其成员和方法都不是静态的,则需要通过创建对象的方式访问
比如
public class A {
	public static class B {
		public String c = "";
		public void D() {
		}
	}
}
要访问内部静态类B的成员c和方法D
则需要创建对象才行,如
A.B ab = new A.B();
String w = ab.c;
ab.D() 

本文引用java核心技术 卷1,仅作学习积累

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 下列说法中关于内部类的说法正确的是( )。 A. 内部类只能访问外部类的静态成员 B. 内部类只能在外部类的方法中被实例化 C. 内部类可以访问外部类的私有成员 D. 内部类不能继承其他类 答案:C。内部类可以访问外部类的私有成员。 2. 下列代码的输出结果是( )。 class Outer { private int num = 10; class Inner { public void print() { System.out.println("num = " + num); } } } public class Test { public static void main(String[] args) { Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); inner.print(); } } A. num = 10 B. num = 0 C. 编译错误 D. 运行时错误 答案:A。内部类可以访问外部类的私有成员,因此可以输出num的值。 3. 下列代码的输出结果是( )。 class Outer { private int num = 10; public void method() { class Inner { public void print() { System.out.println("num = " + num); } } Inner inner = new Inner(); inner.print(); } } public class Test { public static void main(String[] args) { Outer outer = new Outer(); outer.method(); } } A. num = 10 B. num = 0 C. 编译错误 D. 运行时错误 答案:A。方法内部定义的局部内部类可以访问外部类的成员变量,因此可以输出num的值。 4. 下列说法中关于静态内部类的说法正确的是( )。 A. 静态内部类不能访问外部类的静态成员 B. 静态内部类只能在外部类的静态方法中被实例化 C. 静态内部类不能访问外部类的非静态成员 D. 静态内部类不能继承其他类 答案:A。静态内部类不能访问外部类的非静态成员,但可以访问外部类的静态成员。 5. 下列代码的输出结果是( )。 class Outer { private int num = 10; static class Inner { public void print() { System.out.println("num = " + num); } } } public class Test { public static void main(String[] args) { Outer.Inner inner = new Outer.Inner(); inner.print(); } } A. num = 10 B. num = 0 C. 编译错误 D. 运行时错误 答案:C。静态内部类不能访问外部类的非静态成员,因此编译错误。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值