内部类

  1. 内部类是定义在另一个类中的类,使用内部类有以下原因:
  • 内部类可以访问该类定义所在的数据,包括私有数据;
  • 内部类可以对同一个包中的其他类隐藏起来;
  • 匿名内部类可以可以定义回调函数并且节省大量代码。
  1. 内部类对象有一个隐式引用(该引用在内部类中不可见),总是指向创建它的外部类对象。
    外围类的引用在内部类的构造器中设置,编译器修改了所有内部类的构造器,都添加了一个外部类的引用。
    只有内部类可以是私有的,外部类只可能是受保护的或者共有的。
package innerClass;

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

/**
 * @author feng
 * @create 2021-04-03 10:45
 */
public class InnerClassTest {

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

        JOptionPane.showMessageDialog(null, "quit program?");
        System.exit(0);
    }
}

/**
 * a clock that prints the time in regular intervals
 */
class TalkingClock{

    private int interval;
    private boolean beep;

    /**
     * constructs a talking clock
     * @param interval
     * @param beep
     */
    public TalkingClock(int interval,boolean beep){
        this.interval = interval;
        this.beep = beep;
    }

    /**
     * starts the clock
     */
    public void start(){
        ActionListener listener = new TimePrinter();
        Timer timer = new Timer(5000, listener);
        timer.start();
    }

    /**
     * inner class
     */
    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();
        }
    }
}

以上代码由于TimePrinter没有定义构造器,所以编译器会自动为内部类生成一个构造器。

public TimePrinter(TalkingClock clock){
    outer = clock;
}

在start方法中创建TimePrinter对象后。编译器会自动为当前内部类构造器添加参数this。e.g.ActionListener listener = new TimePrinter(this);
3. 内部类的特殊语法规则:

  • 内部类中声明的所有静态域必须是final。
  • 内部类不能有static方法,如果有static方法,只能方法外部类的静态域和方法。
  • 表达式OuterClass.this表示外围类引用。
  • 可以用下列语法更加明确的编写内部类构造器:outerObject.new InnerClass(construction parameters),例如:ActionListener listener = this.new TimePrinter();
  • 在外围类的作用域之外,可以这样应用内部类:OuterClass.InnerClass:
    e.g.
    TalkingClock jb = new TalkingClock(50000,true);
    TalkingClock.TimePrinter listener = jb.new TimePrinter();
    
  1. 内部类是一种编译器现象,与虚拟机无关。编译器会把内部类编译成外部类名$内部类名的常规文件,虚拟机对此一无所知。
  2. 局部内部类不能用public或者private访问说明符进行声明,他的作用域被限定在声明它的代码块中,外部类的其他方法也不能访问,可以对外部世界完全隐藏起来。
public void start(){
    class TimePrinter implements ActionListener{
        public void actionPerformed(ActionEvent evnet){
            System.out.println("at the tone,the time is:" + new Date());
            if(beep)
                ToolKit.getDefaultToolkit().beep();
        }
    }
    ActionListener listener = new TimePrinter();
    Timer timer = new Timer(5000, listener);
    timer.start();
}
  1. 局部内部类不仅能访问外部类,还能访问局部变量,不过这些局部变量必须是final的。编译器必须检测对局部变量的访问,为每一个变量建立响应的数据域,并将局部变量拷贝到构造器中,以便将这些数据域初始化为局部变量的副本。
  2. 匿名内部类通常的语法格式为:
    new SuperType(constructor parameters){
        innerclass method and data
    }
    
    其中SuperType可以是接口,也可以是类,由于匿名内部类没有类名。因此没有构造器,它的构造器参数直接传给超类构造器,但是在内部类实现接口的时候,不能有任何的构造器。
    new InterfaceType(){
        methods and data
    }
    
package innerClass;

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

/**
 * @author feng
 * @create 2021-04-03 12:31
 */
public class AnonymousInnerClassTest {
    public static void main(String[] args) {
        TalkingClock clock = new TalkingClock();
        clock.start(5000,true);
        JOptionPane.showMessageDialog(null, "quit program?");
        System.exit(0);
    }
}

class TalkingClock{

    public void start(int iterval, boolean beep) {
        ActionListener listener = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("at the tone,the time is:" + new Date());
                if (beep)
                    Toolkit.getDefaultToolkit().beep();
            }
        };
        Timer timer = new Timer(iterval, listener);
        timer.start();
    }
}
  1. 静态内部类
    • 有时候使用内部类只是为了把一个类隐藏在另一个类中,并不会引用外部类的对象,这时候就可以将该类定义为一个静态内部类,以便取消产生的引用。
    • 在内部类不需要访问外部类对象的时候,应该使用静态内部类,有时又将静态内部类称为嵌套类。
    • 与常规内部类不同,静态内部类可以有静态域和方法。
    • 声明在接口中的内部类自动称为static和public的。
package innerClass;

import java.util.Arrays;

/**
 * 这个类检测静态内部类的应用
 * @author feng
 * @create 2021-04-03 13:02
 */
public class StaticInnerClass {
    public static void main(String[] args) {
        double[] doubles = new double[20];
        for (int i = 0; i < 20; i++) {
            doubles[i] = Math.random() * 100;
        }
        System.out.println(Arrays.toString(doubles));
        ArrayAlg.Pair pair = ArrayAlg.minmax(doubles);
        System.out.println("min=" + pair.getFirst());
        System.out.println("max=" + pair.getSecond());
    }
}

class ArrayAlg{

    /**
     * 静态内部类,一对浮点型数据
     */
    public static class Pair{
        private double first;
        private double second;

        public Pair(double first, double second) {
            this.first = first;
            this.second = second;
        }

        public double getFirst() {
            return first;
        }

        public double getSecond() {
            return second;
        }
    }

    /**
     * 获取随机数组的最大值和最小值
     * @param doubles
     * @return
     */
    public static Pair minmax(double[] doubles) {
        double min = Double.POSITIVE_INFINITY;
        double max = Double.NEGATIVE_INFINITY;

        for (double d : doubles){
            if (min > d)
                min = d;
            if (max < d)
                max = d;
        }
        return new Pair(min, max);
    }
}
上一篇:基于Wemos的wifi避障小车部分代码


下一篇:python 第4关 收纳的艺术 列表 从列表提取[ ] 从列表切片[ : ] 增补元素append 删减元素del 字典