《Thinking in java》-学习笔记(12)

第12章 传递和返回对象

1.别名问题

”别名“就是多个句柄指向同一个对象,如果有人向对象写入东西,就会产生别名问题。

通常我们调用一个方法是为了产生返回值,或者用它改变为其调用方法的那个对象的状态。

很少需要调用一个方法来处理它的参数;这叫作利用方法的”副作用“。

解决别名的办法是制作副本。

2.制作本地副本

句柄有自己的作用域,而对象没有;不存在本地对象,只有本地句柄。

克隆对象,利用克隆制作本地副本。

Cloneable接口存在的原因:

(1)可能有一个上溯造型句柄指向一个基础类型,而不知道它是否真的能克隆那个对象,

在这种情况下,可以用instanceof关键字调查句柄是否确实同一个能克隆的对象连接。

(2)考虑到我们可能不愿意所有对象类型都能克隆,Object.clone()会验证一个类是否真的
是实现了Cloneable接口。如果没有实现的话,会抛出CloneNotSupportedException违例。

class MyObject implements Cloneable{
	int i;
	MyObject(int ii){	i=ii;}
	public Object clone(){
		Object o=null;
		try{
			o=super.clone();
		}catch(CloneNotSupportedException e){
			P.rintln("MyObject can't clone");
		}
		return o;
	}
	public  String toString(){
		return Integer.toString(i);
	}
}

如上所示:让一个类能够克隆,需要做的工作如下:

(1)实现Cloneable接口

(2)覆盖clone()

(3)在Clone()中调用super.clone()

(4)在clone()中捕获违例;

3.克隆合成对象

下面是书上的例子:注意看 //Must clone handles:这个注释下面的代码

package c12;
class DepthReading implements Cloneable{
	private double depth;
	public DepthReading(double depth){
		this.depth=depth;
	}
	public Object clone(){
		Object o=null;
		try{
			o=super.clone();
		}catch(CloneNotSupportedException e){
			e.printStackTrace();
		}
		return o;
	}
}
class TemperatureReading implements Cloneable{
	private long time;
	private double temperature;
	public TemperatureReading(double temperature){
		time=System.currentTimeMillis();
		this.temperature=temperature;
	}
	public Object clone(){
		Object o=null;
		try{
			o=super.clone();
		}catch(CloneNotSupportedException e){
			e.printStackTrace();
		}
		return o;
	}
}
class OceanReading implements Cloneable{
	private DepthReading depth;
	private TemperatureReading temperature;
	public OceanReading(double tdata,double ddata){
		temperature =new TemperatureReading(tdata);
		depth=new DepthReading(ddata);
	}
	public Object clone(){
		OceanReading o=null;
		try{
			o=(OceanReading)super.clone();
		}catch(CloneNotSupportedException e){
			e.printStackTrace();
		}
		//Must clone handles:
		 o.depth=(DepthReading)o.depth.clone();
		 o.temperature=(TemperatureReading)o.temperature.clone();
		 return o; //Upcasts back to Object
	}
}
public class DeepCopy {
	public static void main(String[] args){
		OceanReading reading=new OceanReading(33.9,100.5);
		OceanReading r=(OceanReading)reading.clone();
		System.out.println(reading+"  "+r);
	}
}

在合成类中,调用完super.clone()后,还需要为合成类内的每个句柄调用clone() 。

对于数组克隆会有一点陷阱,大家可以看我的另一篇Java-克隆数组

4.通过序列化进行深层复制

序列化比克隆的执行效率低,而且还不稳定,所以还是使用克隆比较好。

5.克隆的控制

下面是书上的例子,主要讲的是控制对象能不能克隆的方法。

package c12;
//Can't clone this because it doesn't 
//override clone():
class Ordinary{}

//Overrides clone,but doesn't implement
//Cloneable:
class WrongClone extends Ordinary{
	public Object clone() {
		Object o=null;
		try{
			o=super.clone(); //Throws exception
		}catch(CloneNotSupportedException e){
			e.printStackTrace();
		}
		return o;
	}
}

//Does all the right things for cloning:
class IsCloneable extends Ordinary implements Cloneable{
	public Object clone() {
		Object o=null;
		try{
			o=super.clone(); //Throws exception
		}catch(CloneNotSupportedException e){
			e.printStackTrace();
		}
		return o;
	}
}

//Turn off cloning by throwing the exception:
class NoMore extends IsCloneable{
	public Object clone() {
		Object o=null;
		try{
			throw new CloneNotSupportedException();
		}catch(CloneNotSupportedException e){
			e.printStackTrace();
		}
		return o;
	}
}

class TryMore extends NoMore{
	public Object clone() {
		Object o=null;
		o=super.clone(); //Throws exception
		return o;
	}
}

class BackOn extends NoMore{
	private BackOn duplicate(BackOn b){
		//Somehow make a copy of b
		//and return that copy. This is a dummy
		//copy,just  to make the point:
		return new BackOn();
	}
	public Object clone(){
		//Doesn't call NoMore.clone():
		return duplicate(this);
	}
}

//Can't inherit from this,so can't override
//the clone method like in BackOn:
final class ReallyNoMore extends NoMore{}

public class CheckCloneable {
	static Ordinary tryToClone(Ordinary ord){
		String id=ord.getClass().getName();
		Ordinary x=null;
		if(ord instanceof Cloneable){
			System.out.println("Attmpting "+id);
			x=(Ordinary)((IsCloneable)ord).clone();
			System.out.println("Cloned  "+id);
		}
		return x; 
	}
	public static void main(String[] args){
		//Upcasting:
		Ordinary[] ord={
				new IsCloneable(),
				new WrongClone(),
				new NoMore(),
				new TryMore(),
				new BackOn(),
				new ReallyNoMore(),
		};
		Ordinary x=new Ordinary();
		//This won't compile since clone() is
		//protected in Object:
		//! x=(Ordinary)x.clone();
		//tryToClone() checks first to see if
		//a class implements Cloneable:
		for(int i=0;i<ord.length;i++)
			tryToClone(ord[i]);
	}
}

6.副本构建器

Java中的副本构建器 不适合我们用,会丢失信息。

7.只读类

处理别名的问题,还可以用只读类,所有的句柄都不能修改对象,所以对于对象来说是安全的,

没有副作用。Java标准库就包含了”封装器“类,可以用于所有基本数据类型,不能修改,只读。

下面是创建只读类的例子:

package c12;
import oypj.tools.*;
public class Immutable1 {
	private int data;
	public Immutable1(int initVal){
		data=initVal;
	}
	public int  read(){	return data;}
	public boolean nonzero(){	return data!=0;}
	public Immutable1 quadruple(){
		return new Immutable1(data*4);
	}
	static void f(Immutable1 i1){
		Immutable1 quad=i1.quadruple();
		P.rintln("i1= "+i1.read());
		P.rintln("quad= "+quad.read());
	}
	public static void main(String[] args){
		Immutable1 x=new Immutable1(47);
		P.rintln("x="+x.read());
		f(x);
		P.rintln("x="+x.read());
	}
}

8.只读类的弊端

要修改对象的话就要创建新的对象,还会涉及更频繁的垃圾收集,像上面那个只读类的例子一样。

解决这个问题我们可以用”同志“类,使其可以修改。例如:

package c12;
class Mutable{
	private int data;
	public Mutable(int initVal){
		data=initVal;
	}
	public Mutable add(int x){
		data+=x;
		return this;
	}
	public Mutable multiply(int x){
		data*=x;
		return this;
	}
	public Immutable2 makeImmutable2(){
		return new Immutable2(data);
	}
}

public class Immutable2 {
	private int data;
	public Immutable2(int  initVal){
		data=initVal;
	}
	public int read(){return data;}
	public boolean nonzero(){return data!=0;}
	public Immutable2 add(int x){
		return new Immutable2(data +x);
	}
	public Immutable2 multiply(int x){
		return new Immutable2(data*x);
	}
	public Mutable makeMutable(){
		return new Mutable(data);
	}
	public static Immutable2 modify1(Immutable2 y){
		Immutable2 val=y.add(12);
		val=val.multiply(3);
		val=val.add(11);
		val=val.multiply(2);
		return val;
	}
	//This produces the same result:
	public static Immutable2  modify2(Immutable2 y){
		Mutable m=y.makeMutable();
		m.add(12).multiply(3).add(11).multiply(2);
		return m.makeImmutable2();
	}
	public static void main(String[] args){
		Immutable2 i2=new Immutable2(47);
		Immutable2 r1=modify1(i2);
		Immutable2 r2=modify2(i2);
		System.out.println("i2 ="+i2.read());
		System.out.println("r1 ="+r1.read());
		System.out.println("r2 ="+r2.read());
	}
}

9.不变字串

String类对象被设计成不可变的,每个方法都创建和返回一个新的String对象。但是当使用”+ ” “+=“运算符连接多个

字符串的时候,要创建很多新类,导致执行效率变低。对于String来说StringBuffer就是它的同志类,利用append()

方法可以连接多个字符串,而且不会创建新的类,提高了执行效率。当我们创建字串的时候,编译器会帮我们调

用StringBuffer,来达到优化的目的。

猜你喜欢

转载自blog.csdn.net/a614528195/article/details/81434858