一、回调

  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修饰的常量不能被修改,只能被赋值一次,并且必须初始化。

Java基础(十一)回调(callback)与对象克隆(Cloneable)的更多相关文章

  1. Java基础(十一) Stream I/O and Files

    Java基础(十一) Stream I/O and Files 1. 流的概念 程序的主要任务是操纵数据.在Java中,把一组有序的数据序列称为流. 依据操作的方向,能够把流分为输入流和输出流两种.程 ...

  2. Java基础十一--多态

    Java基础十一--多态 一.多态定义 简单说:就是一个对象对应着不同类型. 多态在代码中的体现: 父类或者接口的引用指向其子类的对象. /* 对象的多态性. class 动物 {} class 猫 ...

  3. Java基础--接口回调(接口 对象名 = new 类名)理解

    接口 对象名1 = new 类名和类名 对象名2 = new 类名的区别是什么? 实例 /** *Person.java 接口 */ public interface Person { void in ...

  4. java 基础知识九 类与对象

    java  基础知识九  类与对象 1.OO(Object–Oriented )面向对象,OO方法(Object-Oriented Method,面向对象方法,面向对象的方法)是一种把面向对象的思想应 ...

  5. java基础(十一) 枚举类型

    枚举类型Enum的简介 1.什么是枚举类型 枚举类型: 就是由一组具有名的值的有限集合组成新的类型.(即新的类). 好像还是不懂,别急,咱们先来看一下 为什么要引入枚举类型 在没有引入枚举类型前,当我 ...

  6. Java基础 之二 类和对象

    1.基础概念 1) 概念 简单来说,类是具有相同特征事物的抽象,比如有轮子的都可以抽象为车:对象则可以看做类的具体实例,比如创建一个法拉利的车,就是车这个类的实例. 抽象.封装.继承.多态是类的四个特 ...

  7. 【Java基础】创建和销毁对象

    Num1:考虑用静态工厂方法代替构造器 对于类而言,常见的方法是提供一个公有的构造器,但其实还有一种方法叫做静态工厂方法(static factory method),它只是一个返回类的实例静态方法. ...

  8. java基础十一[远程部署的RMI](阅读Head First Java记录)

    方法的调用都是发生在相同堆上的两个对象之间(同一台机器的Java虚拟机),如果想要调用另一台机器上的对象,可以通过Socket进行输入/输出. 远程过程调用需要创建出4种东西:服务器.客户端.服务器辅 ...

  9. JAVA基础学习day22--IO流四-对象序列化、管道流、RandomAccessFile、DataStream、ByteArrayStream、转换流的字符编码

    一.对象序列化 1.1.对象序列化 被操作的对象需要实现Serializable接口 1.2.对象序列化流ObjectOutputStream与ObjectInputStream ObjectInpu ...

  10. Java基础毕向东day05 对象与对象的区别,匿名内部类,函数的执行流程。

    1.Car c = new Car(); Car c2 = new Car(); 1> c 和 c2之间的区别? public static void main(String[] args) { ...

随机推荐

  1. Vue.js+vue-element搭建属于自己的后台管理模板:Vue.js快速入门(二)

    Vue.js+vue-element搭建属于自己的后台管理模板:Vue.js快速入门(二) 前言 上篇文章对Vue.js有了初步理解,接下来我们把Vue.js基础语法快速的过一遍,先混个脸熟留个印象就 ...

  2. append追加的html片段,添加点击事件没有反应,解决!

    对于追加的元素来说,属于未来展现的内容,我们此时不能使用一般点击事情处理,需要用未来事件绑定. 例如:我点击车牌号显示车辆详情,如下绑定. //绑定未来元素 $("body").d ...

  3. javaweb技术入门

    JavaWeb巩固和进阶 1.如何配置外部应用? 方法一: server.xml 在<Host>中添加如下配置 <Context path="/xxx" docB ...

  4. Python采集VIP收费QQ音乐,一起来听周董最新的《说好不哭》,省3块不香吗?

    环境: windows python3.6.5 模块: requests selenium json re urllib 环境与模块介绍完毕后,就可以来实行我们的操作了. 第1步: 通过一个解析网站: ...

  5. ArcGISEngine中GP工具奇怪错误问题error(s) have been detected for layer

    运行时环境:使用CADToGeodatabase工具执行DWG文件转gdb过程,多次执行(即执行完一个dwg转gdb,再执行另一个dwg转gdb),执行失败 错误描述:首先执行CADToGeodata ...

  6. 主动降噪技术(ANC)的前生今世–行业分析

    一 原始玩家 1978年,创始人Amar G. Bose博士在从欧洲飞往波士顿的飞机上,发现飞机引擎的噪音扫净了他戴耳机欣赏音乐的兴致.这激发了他在BOSE公司进行一项消噪科技的研究.弹指之间,十年过 ...

  7. MongoDB 学习笔记之 replica set搭建

    Replica set搭建: 修改mongodb.conf文件,指明replSet 登入客户端,指定副本集成员,进行初始化, 如果priority需要调整,使用reconfig()方法.Seconda ...

  8. JNA的步骤、简单实例以及资料整理

    1.步骤 1.编写dll文件,放入项目的bin目录(在window上是dll文件,在Linux上是so文件,dll和so都是由C程序生成)  2.新建接口继承Library  3.加载对应的dll或者 ...

  9. LitePal的基本用法

    快速配置   1. 引入Jar包或源码 首先我们需要将LitePal的jar包引入到项目当中,可以点击这里查看LitePal的最新版本,选择你需要的下载即可.下载好了jar包之后,把它复制到项目的li ...

  10. 文件/大文件上传功能实现(JS+PHP)全过程

    文件/大文件上传功能实现(JS+PHP) 参考博文:掘金-橙红年代 前端大文件上传 路漫漫 其修远 PHP + JS 实现大文件分割上传 本文是学习文件上传后的学习总结文章,从无到有实现文件上传功能, ...