• Java基础(十一)回调(callback)与对象克隆(Cloneable)


      一、回调

      1.回调是一种常见的程序设计模式,可以指出某个特定时间发生时应该采取的动作。

      在java.swing包中有一个类Timer类,可以使用它在到达指定的时间间隔作出什么动作。那么就有两个问题,即设置时间间隔和告知定时器到达时间间隔时的操作。

      具体的实现是,将ActionListener类的对象传递给定时器,然后定时器就会调用传递进来的对象的方法。例如:TimePrinter类实现了 ActionListener接口中的actionPerformed方法,动作就是打印当前时间并响铃一声。

    class TimePrinter implements ActionListener
    {  
       public void actionPerformed(ActionEvent event)
       {  
          System.out.println("At the tone, the time is " + new Date());  // 打印当前时间
          Toolkit.getDefaultToolkit().beep();                  // 响铃一声
       }
    }

      然后,创建一个ActionListener对象,并将这个对象传递给Timer定时器的构造器并设置时间间隔为10秒,然后启动定时器,这样,在程序启动后,每隔十秒钟就会在控制台打印当前时间并响铃一声。

    public static void main(String[] args)
       {  
          ActionListener listener = new TimePrinter();
    
          Timer t = new Timer(10000, listener);
          t.start();
    
       }

      

      二、对象克隆

      1.克隆和引用的区别

      由于alice1原变量和alice1_copy副本都是同一个对象的引用,所以任何一个变量改变都会影响另一个变量。

      而克隆会使得副本的初始状态和原原变量一样,但是之后它们各自会有自己的不同状态,彼此不会互相影响。

          Employee alice1 = new Employee("Alice Adams", 75000, 1987, 12, 15);
          Employee alice1_copy = alice1;
          System.out.println(alice1_copy.getSalary());  // 打印:75000.0
          alice1_copy.raiseSalary(10);
          System.out.println(alice1_copy.getSalary());  // 打印:82500.0
          System.out.println(alice1.getSalary());      // 打印:82500.0

      2.浅拷贝

      先来看一下Employee类的实例域:

       private String name;
       private double salary;
       private Date hireDay;
    • 其中salary是标准的数据类型double,
    • 而name是String类型,String类的声明为public final class String{...},name是String类的一个不可变对象。
    • hireDay是Date类型,Date也是一个类,声明为public class Date{...},hireDay是Date类的一个可变对象。

      默认的clone方法(浅拷贝)的实现过程是:

    • 如果对象中的所有数据域都是数值或其他基本类型,则可以正常拷贝这些域(例如,salary)。
    • 如果对象中包含可变子对象的引用(hireDay),那么拷贝这些域就相当于得到相同子对象的另一个引用,这种情况下,原对象和克隆的对象会共享这些域信息。
    • 如果原对象和浅克隆对象共享的子对象(name)是不可变的,即子对象属于一个不可变的类,例如String,那么这种共享就是安全的。

      3.深拷贝

      为了成功在浅拷贝的基础上成功克隆可变子对象,需要重新定义clone方法来建立一个深拷贝,即在可变的子对象上调用clone来修补默认的clone方法。如下面的例子,首先调用父类的clone方法来得到浅拷贝的cloned对象,其中包含了成功拷贝的name和salary域,然后调用Date类的对象的clone方法获得成功拷贝的Date域,然后让cloned的hireDay域引用这个域,从而实现了深拷贝。

      深拷贝的步骤:

    • 实现Cloneable接口
    • 重新定义clone方法,并将修饰符设置为public
    • 在clone方法中,先调用super.clone()方法浅拷贝基本类型域和不可变子对象,然后使用不含有其他子对象的可变子对象调用clone方法得到副本,最后将这个副本赋值给浅拷贝获得对象的对应域。
       public Employee clone() throws CloneNotSupportedException
       {
          // call Object.clone()
          Employee cloned = (Employee) super.clone();
    
          // clone mutable fields
          cloned.hireDay = (Date) hireDay.clone();
    
          return cloned;
       }

      4.需要注意的问题

      (1)Cloneable接口中没有任何东西,它只是一个标记接口,唯一的作用就是允许在类型查询中使用instanceOf。

      (2)Object类中的clone方法声明为protected,根据前面的知识我们知道,子类可以调用父类中protected修饰的方法,而且所有的类都是Object类的子类。那么直接使用下面的代码还是会出现错误。

      这里的锅应该由clone方法背。因为子类的对象只能调用protected的clone方法来克隆它自己的对象,而不能克隆alice1对象中的其他对象,如String对象name,Date对象hireDay,因此就会报错。解决办法是重新定义clone方法并且修改protected为public才可以允许克隆alice1的所有的对象(如上例)。

    Employee clone = (Employee)alice1.clone();  // 会报错:The method clone() from the type Object is not visible

      这样的代码就不会有错,因为hireDay对象中没有其他子对象:

    Date dc = (Date) hireDay.clone();

      (3)如果在一个对象上调用clone,但这个对象的类并没有实现Cloneable接口,Object类的clone方法就会抛出一个CloneNotSupportedException异常,因此最好是在使用clone的方法周围捕获这个异常。

    public static void main(String[] args)
       {
          try
          {
             Employee original = new Employee("John Q. Public", 50000);
             original.setHireDay(2000, 1, 1);
             Employee copy = original.clone();  // 重写的clone()方法为public的,因此可以调用。
             copy.raiseSalary(10);
             copy.setHireDay(2002, 12, 31);
             System.out.println("original=" + original);  // 打印:original=Employee[name=John Q. Public,salary=50000.0,hireDay=Sat Jan 01 00:00:00 GMT+08:00 2000]
             System.out.println("copy=" + copy);   // 打印:copy=Employee[name=John Q. Public,salary=55000.0,hireDay=Tue Dec 31 00:00:00 GMT+08:00 2002]
          }
          catch (CloneNotSupportedException e)
          {
             e.printStackTrace();
          }
       }

       (4)final修饰符

      Java中final修饰的类不能被继承,final修饰的类的对象为不可变对象,不可变对象一旦创建完成,就不会被改变了。

      类中final修饰的方法不能被子类继承,即对子类不可见(private)。

      final修饰的常量不能被修改,只能被赋值一次,并且必须初始化。

  • 相关阅读:
    智能手表如何救人一命?
    人工智能、机器学习和认知计算入门指南
    PO VO BO DTO POJO DAO的解释
    web UI框架推荐
    面向切面编程AOP
    阿里巴巴java开发规范
    如何理解Spring IOC
    HTML5 3D旋转图片相册
    JSON总结笔记
    轮播图---可以动态添加图片,(封装成一个函数)
  • 原文地址:https://www.cnblogs.com/BigJunOba/p/9329539.html
Copyright © 2020-2023  润新知