java cloneable 接口_Cloneable 接口 记号接口(标记接口)
Cloneable 接口指示了一个类提供了一个安全的clone方法。
首先了解Object.clone()方法:
clone是Object超类的一个protected方法,用户代码不能直接调用这个方法。Object的子类只能调用Object超类中受保护的clone方法来克隆它自己的对象,必须重新定义clone为public才能允许所有方法调用这个类的实例的clone方法克隆对象。
clone方法的作用:clone方法对Employee调用它对这个对象一无所知,所以只能逐个域地进行拷贝。a. 如果对象中的所有数据域都是数值或者其他基本类型,拷贝这些域没有问题;b. 但是如果对象中包含子对象的引用,拷贝域就会得到相同子对象的另一个引用(浅拷贝)。这样一来,原对象和克隆的对象仍然会共享一些信息。
浅拷贝的影响:如果原对象和浅克隆对象共享的子对象是不可变的,那么这种共享就是安全的。如果子对象属于一个不可变的类,如String,就是这种情况。或者在对象的生命周期中,子对象一直包含不变的常量,没有更改器会改变它,就是没有方法会生成它的引用,这种情况同样是安全的(举个栗子:下面例子中hiereDay是一个Date对象,Date类的域是可变的,所以hireDay需要深拷贝。而LocalDate的域不可变,如果hireDay是不可变的LocaDate类的一个实例,就无需我们做任何处理了)。
public class Employee implements Cloneable {
private String name;
private double salary;
private Date hireDay;
public Employee(String name, double salary)
{
this.name = name;
this.salary = salary;
hireDay = new Date();
}
@Override
public Employee clone() throws CloneNotSupportedException
{
// call Object.clone() Employee cloned = (Employee) super.clone(); // clone会逐个域的进行拷贝
// clone mutable fields cloned.hireDay = (Date) hireDay.clone();
return cloned;
}
public void setHireDay(int year, int month, int day)
{
Date newHireDay = new GregorianCalendar(year, month - 1, day).getTime();
// Example of instance field mutation hireDay.setTime(newHireDay.getTime());
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
public String toString()
{
return "Employee[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay + "]";
}
}
public class Main {
public static void main(String[] args)
{
Employee employee = new Employee("jack1", 20000);
try {
Employee clone = employee.clone();
employee.raiseSalary(20);
employee.setHireDay(2020, 10, 22);
System.out.println(employee.toString());
System.out.println(clone.toString());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
/*** 输出:Employee[name=jack1,salary=24000.0,hireDay=Thu Oct 22 00:00:00 CST 2020]Employee[name=jack1,salary=20000.0,hireDay=Sun Oct 13 17:47:26 CST 2019]*/
再看一个栗子,hireDay为不可变LocalDate类的实例时(private LocalDate hireDay;),Main.java类代码基本不变(构造函数有改变 -> Employee employee =newEmployee("jack1",20000,2019,10,13);):
public class Employee implements Cloneable {
private String name;
private double salary;
private LocalDate hireDay;
public Employee(String name, double salary, int year, int month, int day)
{
this.name = name;
this.salary = salary;
hireDay = LocalDate.of(year, month, day);
}
@Override
public Employee clone() throws CloneNotSupportedException
{
// call Object.clone() Employee cloned = (Employee) super.clone();
// // clone mutable fields// cloned.hireDay = (Date) hireDay.clone();
return cloned;
}
public void setHireDay(int year, int month, int day)
{
// Date newHireDay = new GregorianCalendar(year, month - 1, day).getTime();// // Example of instance field mutation// hireDay.setTime(newHireDay.getTime()); hireDay = LocalDate.of(year, month, day);
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
public String toString()
{
return "Employee[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay + "]";
}
}
对于一个要实现clone方法的类,需要确定:实现Cloneable接口;
重新定义clone方法,并且制定public访问修饰符。
注意:
Cloneable接口的出现与接口的正常实现没有关系。具体讲,它(Cloneable接口)没有指定clone方法,这个方法是从Object类继承的(应该是实现接口的类从Object类继承)。Cloneable接口的作用只是作为一个标记,指示 类设计者 了解克隆过程。对象对于克隆很“偏执”,如果一个对象请求克隆,但没有实现这个接口,就会生成一个受查异常(报错:java.lang.CloneNotSupportedException)。
Cloneable接口是Java提供的一组标记接口(tagging interface)之一。有些程序员也称之为记号接口(marker interface)。注意:Comparable等接口的通常用途是确保一个类实现一个或一组特定的方法。标记接口不包含任何方法,它唯一的作用就是允许在类型查询中使用instanceof:
if (obj instanceof Cloneable) ..
建议自己程序中不要使用标记接口。
即使clone的默认(浅拷贝)实现能够满足要求,还是需要实现Cloneable接口,将clone方法重新定义为public,再调用super.clone()。
class Employee implements Cloneable()
{
// raise visibility level to public, change return type public Employee clone() throws CloneNotSupportedException
{
return (Employee) super.clone();
}
...
}
如果一个类重写了clone方法,但是类没有声明实现接口 implements Cloneable,类会抛出一个CloneNotSupportedException异常。当然,Employee和Date类实现了Cloneable接口,所以不会抛出这个异常。不过编译器不会了解这一点,所以我们声明了这个异常:
public Employee clone() throws CloneNotSupportedException
这样,子类在不支持克隆时选择抛出一个CloneNotSupportedException异常。
clone没有想象中的那么常用,标准库中只有5%的类实现了clone。
java cloneable 接口_Cloneable 接口 记号接口(标记接口)相关推荐
- 什么是Java Marker Interface(标记接口)
先看看什么是标记接口?标记接口有时也叫标签接口(Tag interface),即接口不包含任何方法.在Java里很容易找到标记接口的例子,比如JDK里的Serializable接口就是一个标记接口. ...
- 什么是Java Marker Interface(标记接口) 1
先看看什么是标记接口?标记接口有时也叫标签接口(Tag interface),即接口不包含任何方法.在Java里很容易找到标记接口的例子,比如JDK里的Serializable接口就是一个标记接口. ...
- java的标记接口_Java中的标记接口?
我被教授,Java中的Marker接口是一个空接口,用于向编译器或JVM发送信号,实现此接口的类的对象必须以特殊方式处理,如序列化,克隆等. 但最近我了解到,它实际上与编译器或JVM无关.例如,在Se ...
- java中的标记接口
在java 中,RandomAccess和Cloneable .Serializable一样,都是标志性接口,不需要任何实现,只是又来表明其实现类具体有某种特质的,实现了Cloneable表明可以拷贝 ...
- java 标记_java – 标记注释与标记接口
在阅读有关Marker接口的文章时,我偶然发现了以下网站:Item 37: Use marker interfaces to define types 根据Joshua Bloch的说法,Marker ...
- 标记接口,注解和注解处理器的前世今生
文章目录 简介 注解的起源和marker interfaces 注解的定义 Retention Target 自定义参数 在运行时使用注解 在编译时使用注解 总结 简介 相信大部分的开发者都用过注解, ...
- 根据XML配置规则导入Excel数据(⑥)ExcelAble 标记接口
就是一个支持Excel标记接口,还提供验证功能. package com.ivfly.xlsbean; import java.io.Serializable; public interface E ...
- Java的类(class)、包(package)和接口(interface)
在Java中,类(class)是用来代表对象的基本单元.对象(object)可以是现实世界中的任何一个实体,它具有若干区别于其它对象的属性和操作.而类则通过为对象定义属性和操作来概括一类实体.它封装了 ...
- Java中常用的类,包,接口
Java中常用的类,包,接口 包名 说明 java.lang 该包提供了Java编程的基础类,例如 Object.Math.String.StringBuffer.System.Thread等,不使用 ...
最新文章
- 华为鲲鹏产业生态加速算力升级,企业数字化转型在山西吹响号角
- 存储过程结果更改编码_Docker安装Minio存储服务器详解
- 皮一皮:论家庭地位...
- EBCDIK,EBCDIC,ASCII,shift JIS間の変換
- 37 Reasons why your Neural Network is not working
- 用hundred造句子_关于冬至的问候短句,冬至文案唯美句子
- excel中线性函数_Excel中特别有用的不常用函数之Indirect函数
- linux5 syscall 流程_Linux的上的程序是如何运行的,api机制是怎么样?
- oracle test传入参数,oracle存储过程,test(测试)时传自定义类型参数问题
- linux虚拟机能通显卡吗,英伟达 GeForce 游戏显卡正式支持虚拟机传递功能,可以完全调用...
- 数据库操作导入导出以及加快查询速度
- Android 应用程序模块: 应用, 任务, 进程, 和线程
- three.js 学习1
- 常用传感器讲解九--雨滴传感器
- 制作粉色少女系列 生日快乐祝福网页(HTML+CSS+JS)
- vue读取Excel并分组处理数据显示
- 360公司 2020秋招 技术综合E卷 在线考试 编程题 第一题 表面积(web前端)
- windows PE 是什么?
- 如何给apk文件签名(一)
- 2021华为软件精英挑战赛(杭厦第20名)