java核心技术卷阅读笔记九_内部类_代理

文章目录

一 内部类

  • 内部类(inner class) 是定义在另一个类中的类。为什么需要使用内部类呢? 其主要原因有以下三点:
    • 内部类方法可以访问该类定义所在的作用域中的数据, 包括私有的数据。
    • 内部类可以对同一个包中的其他类隐藏起来。
    • 当想要定义一个回调函数且不想编写大量代码时,使用匿名(anonymous) 内部类比较 便捷。

1.1 使用内部类访问对象状态

  • 内部类既可以访问自身的数据域,也可以访问创建它的外围类对象的数据域.
  • 内部类对象拥有一个对外围类对象的引用,这个引用在内部类的定义中是不可见的

如果有 一个 TimePrinter 类是一个常规类,它就需要通过 TalkingClock 类的公有方法访问 beep标志, 而使用内部类可以给予改进, 即不必提供仅用于访问其他类的访问器。

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

        JOptionPane.showMessageDialog(null,"请点击!");
        System.exit(0);
    }
}
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
public class TalkingClock {
    private int interval;
    private boolean beep;

    /** * 内部类 */
    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 void start(){
        ActionListener listener = new TimePrinter();
        Timer t = new Timer(interval,listener);
        t.start();
    }

    public TalkingClock(int interval, boolean beep) {
        this.interval = interval;
        this.beep = beep;
    }

    public TalkingClock() {
    }
}

1.2 内部类的特殊语法规则

表达式 OuterClass.this 表示外围类引用,例如,可以像下面这样编写 TimePrinter 内部类的 actionPerformed方法:

 @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("At the tone, the time is " + new Date());
            if (TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep();
        }

反过来,可以采用下列语法格式更加明确地编写内部对象的构造器:
outerObject.new InnerClass{construction parameters)

    public void start(){
        ActionListener listener = this.new TimePrinter();
        Timer t = new Timer(interval,listener);
        t.start();
    }

通常,this 限定词是多余的。不过,可以通过显式地命名 将外围类引用设置为其他的对象。例如, 如果 TimePrinter 是一个公有内部类,对于任意的语 音时钟都可以构造一个 TimePrinter:

        TalkingClock clock = new TalkingClock(1000,true);
        TalkingClock.TimePrinter printer = clock.new TimePrinter();
  • 需要注意, 在外围类的作用域之外,可以这样引用内 OuterClass.InnerClass

内部类中声明的所有静态域都必须是 final。原因很简单。我们希望一个静态域只 有一个实例, 不过对于每个外部对象, 会分别有一个单独的内部类实例。如果这个域不 是 final, 它可能就不是唯一的。 内部类不能有 static 方法。Java 语言规范对这个限制没有做任何解释。也可以允许有 静态方法,但只能访问外围类的静态域和方法。显然,Java 设计者认为相对于这种复杂 性来说, 它带来的好处有些得不偿失。

1.3 局部内部类

如果仔细地阅读一下 TalkingClock 示例的代码就会发现, TimePrinter 这个类名字只在 start 方法中创建这个类型的对象时使用了一次。 当遇到这类情况时, 可以在一个方法中定义局部类。

 public void  start(){

        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();
            }
        }

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

局部类不能用 public 或 private访问说明符进行声明。它的作用域被限定在声明这个局部 类的块中。
局部类有一个优势, 即对外部世界可以完全地隐藏起来。 即使 TalkingClock 类中的其他 代码也不能访问它。除 start 方法之外, 没有任何方法知道 TimePrinter 类的存在。

与其他内部类相比较, 局部类还有一个优点。它们不仅能够访问包含它们的外部类, 还 可以访问局部变量。不过,那些局部变量必须事实上为 final。这说明,它们一旦赋值就绝不 会改变。这里, 将 TalkingClock 构造器的参数 interva丨和 beep 移至 start 方法中。

  • javaSE8 可以不写final.
public class TalkingClock {


    public void  start(int interval,boolean beep){

        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();
            }
        }

        ActionListener listener = new TimePrinter();
        Timer t = new Timer(interval, listener);
        t.start();
    }
}
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
public class InnerClassTest {
    public static void main(String[] args) {
        TalkingClock clock = new TalkingClock();

        clock.start(1000,true);

        JOptionPane.showMessageDialog(null, "请点击");
        System.exit(0);
    }
}

1.4 匿名内部类

将局部内部类的使用再深人一步。假如只创建这个类的一个对象,就不必命名了。这种类被称为匿名内部类(anonymous inner class)。

public class TalkingClock {


    public void  start(int interval,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 t = new Timer(interval, listener);
        t.start();
    }
}

它的含义是:创建一个实现 ActionListener 接口的类的新 对象,需要实现的方法 actionPerformed定义在括号内。 通常的语法格式为:

new SuperType(constructionparameters) 
	{
	 inner class methodsanddata 
	 } 

SuperType 可以是 ActionListener 这样的接口, 于是内部类就要实现这个接口。 SuperType 也可以是一个类,于是内部类就要扩展它。

由于构造器的名字必须与类名相同, 而匿名类没有类名,所以,匿名类不能有构造器。 取而代之的是,将构造器参数传递给超类(superclass) 构造器。尤其是在内部类实现接口的 时候, 不能有任何构造参数。不仅如此,还要像下面这样提供一组括号:

new InterfaceType() 
	{ 
	methods and data 
	} 

多年来,Java 程序员习惯的做法是用匿名内部类实现事件监听器和其他回调。如今最好 还是使用 lambda 表达式。例如, 这一节前面给出的 start 方法用 lambda 表达式来写会简洁得 多,

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

1.5 静态内部类

  • 什么时候使用静态内部类:

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

只有内部类可以声明为 static。静态内部类的对象除了没有对生成它的外围类对象 的引用特权外, 与其他所冇内部类完全一样.

  • 实例 : 计算数组中最小值和最大值的问题。 只遍历数组一次,并能够同时计算出最小值和最 大值
public class ArrayAIg {

    /** * 静态内部类 * 这个方法必须返冋两个数值, 为此, 可以定义一个包含两个值的类 Pair: */
    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;
        }

        @Override
        public String toString() {
            return "Pair{" +
                    "first=" + first +
                    ", second=" + second +
                    '}';
        }
    }

    /** * 求一个数组中的最大和最小值 */
    public  static  Pair minmax(double[] values){

        // 保持正无穷大类型的常数
        double min = Double.POSITIVE_INFINITY;
        // 保持负无穷大的常数
        double max = Double.NEGATIVE_INFINITY;

        for (Double v : values){
            if(min > v) min=v;
            if(max < v) max=v;
        }
        return new Pair(min, max);
    }
}
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
public class StaticInnerClassTest {

    public static void main(String[] args) {

        double[] d = {234.3,24.5,32,1,3,2,42,2324,1,3};

        // 通过ArrayAlg.Pair 访问
        ArrayAIg.Pair dd = ArrayAIg.minmax(d);

        System.out.println(dd.toString());

    }
}

二 : 代理(跳过)

利用代理可以在运行时创建一个实现了一组给 定接1J 的新类 : 这种功能只有在编译时无法确定需要实现哪个接 U时才冇必要使用。对于应用程序设计人员来说, 遇到这种情况的机会很少。如果对这种高级技术不感兴趣,可以跳过 本节内容。然而,对于系统程序设计人员来说,代理带来的灵活性却十分重要。

2.1 何时使用代理?

假设有一个表示接口的 Class 对象(有可能只包含一个接口),它的确切类型在编译时无 法知道。这确实有些难度。要想构造一个实现这些接口的类,就需要使用 newlnstance 方法 或反射找出这个类的构造器。但是, 不能实例化一个接口,需要在程序处于运行状态时定义 一个新类。

    原文作者:白茅飞雪
    原文地址: https://blog.csdn.net/weixin_42430194/article/details/86624156
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞