Java学习笔记14

Object类是所有类、数组、枚举类的父类,是类层次结构的根类。每个类都使用Object作为超类。所有对象(包括

数组)都实现这个类的方法。

Object类实现了以下方法:

我们来看看源码中clone()方法是如何定义的:

protected native Object clone() throws CloneNotSupportedException;

我们看到clone()方法前用了native来修饰,说明native方法的效率一般来说高于Java中的非native方法。其次用了

protected访问权限来修饰,也就是说你想应用clone()方法必须继承Object,在Java中所有的类缺省继承Object类,

所以可以忽略。

zui  接着我们看看clone()方法的作用,在官方文档是这样说的:

创建并返回此对象的一个副本。“副本”的准确含义可能依赖于对象的类。这样做的目的是,对于任何对象x
表达式:

x.clone() != x

为 true。

表达式:

x.clone().getClass() == x.getClass()

也为 true,但这些并非必须要满足的要求。

一般情况下:

x.clone().equals(x)

true,但这并非必须要满足的要求。

按照惯例,返回的对象应该通过调用super.clone 获得。如果一个类及其所有的超类(Object 除外)都遵守此约

定,则 x.clone().getClass() == x.getClass()

按照惯例,此方法返回的对象应该独立于该对象(正被复制的对象)。要获得此独立性,在super.clone 返回对象

之前,有必要对该对象的一个或多个字段进行修改。这通常意味着要复制包含正在被复制对象的内部“深层结构”的

所有可变对象,并使用对副本的引用替换对这些对象的引用。如果一个类只包含基本字段或对不变对象的引用,那

么通常不需要修改
super.clone
返回的对象中的字段。

Object 类的clone 方法执行特定的复制操作。首先,如果此对象的类不能实现接口
Cloneable,则会抛出

CloneNotSupportedException。注意,所有的数组都被视为实现接口Cloneable。否则,此方法会创建此对象的类的

一个新实例,并像通过分配那样,严格使用此对象相应字段的内容初始化该对象的所有字段;这些字段的内容没有

被自我复制。所以,此方法执行的是该对象的“浅表复制”,而不“深层复制”操作。

Object 类本身不实现接口Cloneable,所以在类为
Object 的对象上调用 clone 方法将会导致在运行时抛出异常。

将以上内容进行总结,得出:

1、实现Cloneable接口。

2、重载clone()方法。

3、实现clone()方法时通过super.clone();调用Object实现的clone()方法来得到该对象的副本。

接着我们来看看clone()方法的两种克隆方式:

克隆的对象:

public class CloneObject implements Serializable{
	private String name ;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

(1)浅度克隆:

public class SimpleClone implements Cloneable ,Serializable {	

    public CloneObject cloneObject = null;

	public SimpleClone(CloneObject cloneObject) {
		this.cloneObject = cloneObject;
	}

	public Object clone() {
		SimpleClone newSimpleClone =  null;
		try {
			newSimpleClone = (SimpleClone)super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return newSimpleClone;
	}
}

浅度克隆测试:

public class TestClone {

	public static void main(String[] arg) {
		CloneObject obj1 = new CloneObject();
		obj1.setName("cloneOne");
		CloneObject obj2 = new CloneObject();
		obj2.setName("cloneTwo");

		SimpleClone simpleClone1 = new SimpleClone(obj1);
		SimpleClone simpleClone2 = new SimpleClone(obj2);

		simpleClone2 = (SimpleClone)simpleClone1.clone();

		simpleClone2.cloneObject.setName("cloneThree");

		System.out.println(simpleClone1.cloneObject.getName());
		System.out.println(simpleClone2.cloneObject.getName());
	}

}

测试结果:

cloneThree

cloneThree

以上程序创建了两个对象obj1和obj2,并分别为name赋值“cloneOne”和“cloneTwo”,然后分别赋值给定义的两个

实现浅度克隆的类。我们看到以上程序为simpleClone1进行了克隆,并将克隆后的对象赋值给simpleClone2,之后

再对simpleClone进行重新赋值。最后我们看输出的结果,发现,simpleClone1.cloneObject.getName是随着

simpleClone2改变而改变,也就是说浅度克隆仅仅克隆了该对象的所有Field值,并没有对引用类型的Field值所引用

的对象进行克隆,实际上克隆的对象指向的还是同一个块内存地址。

(2)深度克隆:

public class DepthClone {
	public  final static Object objectCopy(Object oldObj) {
		Object newObj = null;
		try {
			ByteArrayOutputStream bo = new ByteArrayOutputStream();
			ObjectOutputStream oo = new ObjectOutputStream(bo);
			oo.writeObject(oldObj);
			ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
			ObjectInputStream oi= new ObjectInputStream(bi);
			newObj = oi.readObject();
		} catch (IOException e) {
			e.printStackTrace();
		}catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

		return newObj;
	}
}

深度克隆测试:

public class TestClone {

	public static void main(String[] arg) {
		CloneObject obj1 = new CloneObject();
		obj1.setName("cloneOne");
		CloneObject obj2 = new CloneObject();
		obj2.setName("cloneTwo");

		SimpleClone simpleClone1 = new SimpleClone(obj1);
		SimpleClone simpleClone2 = new SimpleClone(obj2);

		simpleClone2 = (SimpleClone)DepthClone.objectCopy(simpleClone1);
		simpleClone2.cloneObject.setName("cloneThree");

		System.out.println(simpleClone1.cloneObject.getName());
		System.out.println(simpleClone2.cloneObject.getName());
	}

}

测试结果:

cloneOne

cloneThree

以上程序通过Java的对象序列化机制,将对象的状态转换成字节流,接着通过这些值再生成新的对象,最后我们

发现,simpleClone1.cloneObject.getName是不随着simpleClone2改变而改变,也就是说simpleClone1

和simpleClone2是被存在两个不同内存中。在使用 Java的序列化时,值得注意的是,如果这个类做了改动,然后

重新编译,那么这是反序列化刚才的对象就会产生异常,这时可以通过添加serialVersionUID属性来解决。

转载请注明出处:http://blog.csdn.net/hai_qing_xu_kong/article/details/43908709   情绪控_

时间: 2024-10-19 23:46:33

Java学习笔记14的相关文章

我的java学习笔记(14)关于反射(part 3)

1.java.lang.reflect包中的Array类允许动态地创建数组. 2.在调用Array类中的静态方法newInstance是需要提供两个参数,一个是数组的元素类型,一个是数组的长度. 3.Class类的getComponentType方法可以确定数组对应的类型. 4.整型数组类型int[]可以被转换成Object,但不能转换成对象数组. 相关的实例代码 import java.lang.reflect.*; import java.util.*; public class test

java学习笔记8--接口总结

接着前面的学习: java学习笔记7--抽象类与抽象方法 java学习笔记6--类的继承.Object类 java学习笔记5--类的方法 java学习笔记4--对象的初始化与回收 java学习笔记3--类与对象的基础 java学习笔记2--数据类型.数组 java学习笔记1--开发环境平台总结 本文地址:http://www.cnblogs.com/archimedes/p/java-study-note8.html,转载请注明源地址. 生活中的接口: 什么是接口? 一个Java接口是一些方法特

java学习笔记3——java关键字

java学习笔记3——java关键字 虽然老师说不用刻意的去记忆,但是我还是在网上找到了非常详细的注解,再次收藏 关键字的类型表: 各个关键字的详细注解和实例,按首字母排序: 1.abstract abstract 关键字可以修改类或方法. abstract 类可以扩展(增加子类),但不能直接实例化. abstract 方法不在声明它的类中实现,但必须在某个子类中重写. -示例- public abstract class MyClass{ } public abstract String my

【Java学习笔记之二十六】深入理解Java匿名内部类

在[Java学习笔记之二十五]初步认知Java内部类中对匿名内部类做了一个简单的介绍,但是内部类还存在很多其他细节问题,所以就衍生出这篇博客.在这篇博客中你可以了解到匿名内部类的使用.匿名内部类要注意的事项.如何初始化匿名内部类.匿名内部类使用的形参为何要为final. 一.使用匿名内部类内部类 匿名内部类由于没有名字,所以它的创建方式有点儿奇怪.创建格式如下: new 父类构造器(参数列表)|实现接口() { //匿名内部类的类体部分 } 在这里我们看到使用匿名内部类我们必须要继承一个父类或者

Java学习笔记之继承

一.继承的基础 在Java术语中,被继承的类叫超类(superclass),继承超类的类叫子类(subclass). 举例说明: 1 class Box 2 { 3 public double width; 4 public double height; 5 public double depth; 6 7 //重载构造方法 8 public Box(Box ob) 9 { 10 width = ob.width; 11 height = ob.height; 12 depth = ob.dep

Java学习笔记之接口

一.接口的概念与定义 首先考虑一个简单的接口的定义: public interface Output { int MAX_LINE = 40; void out(); void getData(String msg); } 定义接口使用关键字interface 修饰符interface前面的public可以省略,如果省略,则采用默认访问控制,即只有在相同包结构的代码才可以访问此接口 接口不可以有构造方法(区别于类中的构造方法) 接口里面的所有成员,包括常量.方法等都是public访问权限,所以在

Swift学习笔记(14)--方法

1.分类 方法分为实例方法和类型方法 实例方法(Instance Methods):与java中的类似,略 类型方法(Type Methods):与java.oc中的类方法类似.声明类的类型方法,在方法的func关键字之前加上关键字class:声明结构体和枚举的类型方法,在方法的func关键字之前加上关键字static. 方法的参数名参见学习笔记的函数章节 2.在实例方法中修改值类型和self 结构体和枚举是值类型.一般情况下,值类型的属性不能在它的实例方法中被修改. 但是,如果你确实需要在某个

springmvc学习笔记(14)-springmvc校验

springmvc学习笔记(14)-springmvc校验 springmvc学习笔记14-springmvc校验 校验理解 springmvc校验需求 环境准备 配置校验器 在pojo中添加校验规则 捕获和显示校验错误信息 分组校验 本文主要介绍springmvc校验,包括环境准备,校验器配置,pojo张添加校验规则,捕获和显示检验错误信息以及分组校验简单示例. 校验理解 项目中,通常使用较多是前端的校验,比如页面中js校验.对于安全要求较高点建议在服务端进行校验. 服务端校验: 控制层con

mybatis学习笔记(14)-mybatis整合ehcache

mybatis学习笔记(14)-mybatis整合ehcache mybatis学习笔记14-mybatis整合ehcache 分布缓存 整合方法掌握 整合ehcache 加入ehcache的配置文件 ehcache是一个分布式缓存框架 分布缓存 我们系统为了提高系统并发,性能.一般对系统进行分布式部署(集群部署方式) 不使用分布缓存,缓存的数据在各各服务单独存储,不方便系统开发.所以要使用分布式缓存对缓存数据进行集中管理. mybatis无法实现分布式缓存,需要和其它分布式缓存框架进行整合.