信息发布软件,b2b软件,广告发布软件

 找回密码
 立即注册
搜索
查看: 2159|回复: 2
打印 上一主题 下一主题

[『 Java 图文教程』] JAVA中浅复制与深复制和多方法实例教程

  [复制链接]

774

主题

858

帖子

5500

积分

积分
5500
跳转到指定楼层
宣传软件楼主
发表于 2016-10-17 11:06:47 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

软件教程首图:

软件教程分类:Java 图文教程 

软件图文教程视频教程分类:软件图文教程 

软件教程难易程度:软件初级教程 

软件教程发布日期:2016-10-17

软件教程关键字:JAVA中浅复制与深复制

① 本信息收集于网络,如有不对的地方欢迎联系我纠正!
② 本信息免费收录,不存在价格的问题!
③ 如果您的网站也想这样出现在这里,请您加好友情链接,我当天会审核通过!

④友情链接关键字:软件定制网站 网址:http://www.postbbs.com

软件教程详细描述
本帖最后由 信息发布软件 于 2016-10-17 11:09 编辑

 1.浅复制与深复制概念

⑴浅复制(浅克隆)

被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。


⑵深复制(深克隆)

被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。


  2.Java的clone()方法

⑴clone方法将对象复制了一份并返回给调用者。一般而言,clone()方法满足:

①对任何的对象x,都有x.clone() !=x//克隆对象与原对象不是同一个对象

②对任何的对象x,都有x.clone().getClass()= =x.getClass()//克隆对象与原对象的类型一样

③如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立。


⑵Java中对象的克隆

①为了获取对象的一份拷贝,我们可以利用Object类的clone()方法。

②在派生类中覆盖基类的clone()方法,并声明为public。

③在派生类的clone()方法中,调用super.clone()。

④在派生类中实现Cloneable接口。


请看如下代码:


class Student implements Cloneable

{

String name;

int age;

Student(String name,int age)

{

this.name=name;

this.age=age;

}

public Object clone()

{

Object o=null;

try

{

o=(Student)super.clone();//Object中的clone()识别出你要复制的是哪一

// 个对象。

}


catch(CloneNotSupportedException e)

{

System.out.println(e.toString());

}

return o;

}

}


public static void main(String[] args)

{

Student s1=new Student("zhangsan",18);

Student s2=(Student)s1.clone();

s2.name="lisi";

s2.age=20;

System.out.println("name="+s1.name+","+"age="+s1.age);//修改学生2后,不影响

//学生1的值。

}


说明:

①为什么我们在派生类中覆盖Object的clone()方法时,一定要调用super.clone()呢?在运行时刻,Object中的clone()识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。

②继承自java.lang.Object类的clone()方法是浅复制。以下代码可以证明之。


class Professor

{

String name;

int age;

Professor(String name,int age)

{

this.name=name;

this.age=age;

}

}

class Student implements Cloneable

{

String name;//常量对象。

int age;

Professor p;//学生1和学生2的引用值都是一样的。

Student(String name,int age,Professor p)

{

this.name=name;

this.age=age;

this.p=p;

}

public Object clone()

{

Student o=null;

try

{

o=(Student)super.clone();

}


catch(CloneNotSupportedException e)

{

System.out.println(e.toString());

}

o.p=(Professor)p.clone();

return o;

}

}

public static void main(String[] args)

{

Professor p=new Professor("wangwu",50);

Student s1=new Student("zhangsan",18,p);

Student s2=(Student)s1.clone();

s2.p.name="lisi";

s2.p.age=30;

System.out.println("name="+s1.p.name+","+"age="+s1.p.age);//学生1的教授

//成为lisi,age为30。

}

  那应该如何实现深层次的克隆,即修改s2的教授不会影响s1的教授?代码改进如下。


改进使学生1的Professor不改变(深层次的克隆)

class Professor implements Cloneable

{

String name;

int age;

Professor(String name,int age)

{

this.name=name;

this.age=age;

}

public Object clone()

{

Object o=null;

try

{

o=super.clone();

}

catch(CloneNotSupportedException e)

{

System.out.println(e.toString());

}

return o;

}

}

class Student implements Cloneable

{

String name;

int age;

Professor p;

Student(String name,int age,Professor p)

{

this.name=name;

this.age=age;

this.p=p;

}


public Object clone()

{

Student o=null;

try

{

o=(Student)super.clone();

}

catch(CloneNotSupportedException e)

{

System.out.println(e.toString());

}

o.p=(Professor)p.clone();

return o;

}

}

public static void main(String[] args)

{

Professor p=new Professor("wangwu",50);

Student s1=new Student("zhangsan",18,p);

Student s2=(Student)s1.clone();

s2.p.name="lisi";

s2.p.age=30;

System.out.println("name="+s1.p.name+","+"age="+s1.p.age);//学生1的教授不改变。

}


  3.利用串行化来做深复制

把对象写到流里的过程是串行化(Serilization)过程,但是在Java程序师圈子里又非常形象地称为“冷冻”或者“腌咸菜(picking)”过程;而把对象从流中读出来的并行化(Deserialization)过程则叫做“解冻”或者“回鲜(depicking)”过程。应当指出的是,写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面,因此“腌成咸菜”的只是对象的一个拷贝,Java咸菜还可以回鲜。

在Java语言里深复制一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里(腌成咸菜),再从流里读出来(把咸菜回鲜),便可以重建对象。

如下为深复制源代码。

public Object deepClone()

{

//将对象写到流里

ByteArrayOutoutStream bo=new ByteArrayOutputStream();

ObjectOutputStream oo=new ObjectOutputStream(bo);

oo.writeObject(this);

//从流里读出来

ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());

ObjectInputStream oi=new ObjectInputStream(bi);

return(oi.readObject());

}


这样做的前提是对象以及对象内部所有引用到的对象都是可串行化的,否则,就需要仔细考察那些不可串行化的对象可否设成transient,从而将之排除在复制过程之外。上例代码改进如下。


class Professor implements Serializable

{

String name;

int age;

Professor(String name,int age)

{

this.name=name;

this.age=age;

}


}

class Student implements Serializable

{

String name;//常量对象。

int age;

Professor p;//学生1和学生2的引用值都是一样的。

Student(String name,int age,Professor p)

{

this.name=name;

this.age=age;

this.p=p;

}

public Object deepClone() throws IOException,

OptionalDataException,ClassNotFoundException

{

//将对象写到流里

ByteArrayOutoutStream bo=new ByteArrayOutputStream();

ObjectOutputStream oo=new ObjectOutputStream(bo);

oo.writeObject(this);

//从流里读出来

ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());

ObjectInputStream oi=new ObjectInputStream(bi);

return(oi.readObject());

}


}

public static void main(String[] args)

{

Professor p=new Professor("wangwu",50);

Student s1=new Student("zhangsan",18,p);

Student s2=(Student)s1.deepClone();

s2.p.name="lisi";

s2.p.age=30;

System.out.println("name="+s1.p.name+","+"age="+s1.p.age); //学生1的教授不改变。

}



untoJava对象的强、软、弱和虚引用和多方法实例教程next深入Java对象及元素的存储区域和实例教程
回复

使用道具 举报

774

主题

858

帖子

5500

积分

积分
5500
信息发布软件沙发
 楼主| 发表于 2016-10-17 11:11:26 | 只看该作者
实例方法2

假如说你想复制一个简单变量。很简单:

  • int apples = 5;
  • int pears = apples;

                int apples = 5;                int pears = apples;
不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short,float,double.long)同样适用于该类情况。

但是如果你复制的是一个对象,情况就有些复杂了。

假设说我是一个beginner,我会这样写:

  • class Student {
  •     private int number;
  •     public int getNumber() {
  •         return number;
  •     }
  •     public void setNumber(int number) {
  •         this.number = number;
  •     }
  • }
  • public class Test {
  •     public static void main(String args[]) {
  •         Student stu1 = new Student();
  •         stu1.setNumber(12345);
  •         Student stu2 = stu1;
  •         System.out.println("学生1:" + stu1.getNumber());
  •         System.out.println("学生2:" + stu2.getNumber());
  •     }
  • }

class Student {        private int number;        public int getNumber() {                return number;        }        public void setNumber(int number) {                this.number = number;        }        }public class Test {                public static void main(String args[]) {                                Student stu1 = new Student();                stu1.setNumber(12345);                Student stu2 = stu1;                                System.out.println("学生1:" + stu1.getNumber());                System.out.println("学生2:" + stu2.getNumber());        }}

打印结果:

  • 学生1:12345
  • 学生2:12345

学生1:12345学生2:12345
这里我们自定义了一个学生类,该类只有一个number字段。
我们新建了一个学生实例,然后将该值赋值给stu2实例。(Student stu2 = stu1;)
再看看打印结果,作为一个新手,拍了拍胸腹,对象复制不过如此,
难道真的是这样吗?

我们试着改变stu2实例的number字段,再打印结果看看:

  • stu2.setNumber(54321);
  • System.out.println("学生1:" + stu1.getNumber());
  • System.out.println("学生2:" + stu2.getNumber());

                stu2.setNumber(54321);                        System.out.println("学生1:" + stu1.getNumber());                System.out.println("学生2:" + stu2.getNumber());
打印结果:


  • 学生1:54321
  • 学生2:54321

学生1:54321学生2:54321
这就怪了,为什么改变学生2的学号,学生1的学号也发生了变化呢?


原因出在(stu2 = stu1) 这一句。该语句的作用是将stu1的引用赋值给stu2,
这样,stu1和stu2指向内存堆中同一个对象。如图:
JAVA中浅复制与深复制和多方法实例教程 b2b软件

那么,怎样才能达到复制一个对象呢?
是否记得万类之王Object。它有11个方法,有两个protected的方法,其中一个为clone方法。
该方法的签名是:
protected native Object clone() throws CloneNotSupportedException;

因为每个类直接或间接的父类都是Object,因此它们都含有clone()方法,但是因为该方法是protected,所以都不能在类外进行访问。
要想对一个对象进行复制,就需要对clone方法覆盖。

一般步骤是(浅复制):
1. 被复制的类需要实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常) 该接口为标记接口(不含任何方法)
2. 覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象,(native为本地方法)

下面对上面那个方法进行改造:

  • class Student implements Cloneable{
  •     private int number;
  •     public int getNumber() {
  •         return number;
  •     }
  •     public void setNumber(int number) {
  •         this.number = number;
  •     }
  •     @Override
  •     public Object clone() {
  •         Student stu = null;
  •         try{
  •             stu = (Student)super.clone();
  •         }catch(CloneNotSupportedException e) {
  •             e.printStackTrace();
  •         }
  •         return stu;
  •     }
  • }
  • public class Test {
  •     public static void main(String args[]) {
  •         Student stu1 = new Student();
  •         stu1.setNumber(12345);
  •         Student stu2 = (Student)stu1.clone();
  •         System.out.println("学生1:" + stu1.getNumber());
  •         System.out.println("学生2:" + stu2.getNumber());
  •         stu2.setNumber(54321);
  •         System.out.println("学生1:" + stu1.getNumber());
  •         System.out.println("学生2:" + stu2.getNumber());
  •     }
  • }

class Student implements Cloneable{        private int number;        public int getNumber() {                return number;        }        public void setNumber(int number) {                this.number = number;        }                @Override        public Object clone() {                Student stu = null;                try{                        stu = (Student)super.clone();                }catch(CloneNotSupportedException e) {                        e.printStackTrace();                }                return stu;        }}public class Test {                public static void main(String args[]) {                                Student stu1 = new Student();                stu1.setNumber(12345);                Student stu2 = (Student)stu1.clone();                                System.out.println("学生1:" + stu1.getNumber());                System.out.println("学生2:" + stu2.getNumber());                                stu2.setNumber(54321);                        System.out.println("学生1:" + stu1.getNumber());                System.out.println("学生2:" + stu2.getNumber());        }}
打印结果:


  • 学生1:12345
  • 学生2:12345
  • 学生1:12345
  • 学生2:54321

学生1:12345学生2:12345学生1:12345学生2:54321
如果你还不相信这两个对象不是同一个对象,那么你可以看看这一句:


  • System.out.println(stu1 == stu2); // false

                System.out.println(stu1 == stu2); // false
上面的复制被称为浅复制(Shallow Copy),还有一种稍微复杂的深度复制(deep copy):
我们在学生类里再加一个Address类。

  • class Address  {
  •     private String add;
  •     public String getAdd() {
  •         return add;
  •     }
  •     public void setAdd(String add) {
  •         this.add = add;
  •     }
  • }
  • class Student implements Cloneable{
  •     private int number;
  •     private Address addr;
  •     public Address getAddr() {
  •         return addr;
  •     }
  •     public void setAddr(Address addr) {
  •         this.addr = addr;
  •     }
  •     public int getNumber() {
  •         return number;
  •     }
  •     public void setNumber(int number) {
  •         this.number = number;
  •     }
  •     @Override
  •     public Object clone() {
  •         Student stu = null;
  •         try{
  •             stu = (Student)super.clone();
  •         }catch(CloneNotSupportedException e) {
  •             e.printStackTrace();
  •         }
  •         return stu;
  •     }
  • }
  • public class Test {
  •     public static void main(String args[]) {
  •         Address addr = new Address();
  •         addr.setAdd("杭州市");
  •         Student stu1 = new Student();
  •         stu1.setNumber(123);
  •         stu1.setAddr(addr);
  •         Student stu2 = (Student)stu1.clone();
  •         System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
  •         System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
  •     }
  • }

class Address  {        private String add;        public String getAdd() {                return add;        }        public void setAdd(String add) {                this.add = add;        }        }class Student implements Cloneable{        private int number;        private Address addr;                public Address getAddr() {                return addr;        }        public void setAddr(Address addr) {                this.addr = addr;        }        public int getNumber() {                return number;        }        public void setNumber(int number) {                this.number = number;        }                @Override        public Object clone() {                Student stu = null;                try{                        stu = (Student)super.clone();                }catch(CloneNotSupportedException e) {                        e.printStackTrace();                }                return stu;        }}public class Test {                public static void main(String args[]) {                                Address addr = new Address();                addr.setAdd("杭州市");                Student stu1 = new Student();                stu1.setNumber(123);                stu1.setAddr(addr);                                Student stu2 = (Student)stu1.clone();                                System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());                System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());        }}
打印结果:


  • 学生1:123,地址:杭州市
  • 学生2:123,地址:杭州市

学生1:123,地址:杭州市学生2:123,地址:杭州市
乍一看没什么问题,真的是这样吗?

我们在main方法中试着改变addr实例的地址。

  • addr.setAdd("西湖区");
  • System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
  • System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());

                addr.setAdd("西湖区");                                System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());                System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
打印结果:


  • 学生1:123,地址:杭州市
  • 学生2:123,地址:杭州市
  • 学生1:123,地址:西湖区
  • 学生2:123,地址:西湖区

学生1:123,地址:杭州市学生2:123,地址:杭州市学生1:123,地址:西湖区学生2:123,地址:西湖区
这就奇怪了,怎么两个学生的地址都改变了?

原因是浅复制只是复制了addr变量的引用,并没有真正的开辟另一块空间,将值复制后再将引用返回给新对象。
所以,为了达到真正的复制对象,而不是纯粹引用复制。我们需要将Address类可复制化,并且修改clone方法,完整代码如下:


  • package abc;
  • class Address implements Cloneable {
  •     private String add;
  •     public String getAdd() {
  •         return add;
  •     }
  •     public void setAdd(String add) {
  •         this.add = add;
  •     }
  •     @Override
  •     public Object clone() {
  •         Address addr = null;
  •         try{
  •             addr = (Address)super.clone();
  •         }catch(CloneNotSupportedException e) {
  •             e.printStackTrace();
  •         }
  •         return addr;
  •     }
  • }
  • class Student implements Cloneable{
  •     private int number;
  •     private Address addr;
  •     public Address getAddr() {
  •         return addr;
  •     }
  •     public void setAddr(Address addr) {
  •         this.addr = addr;
  •     }
  •     public int getNumber() {
  •         return number;
  •     }
  •     public void setNumber(int number) {
  •         this.number = number;
  •     }
  •     @Override
  •     public Object clone() {
  •         Student stu = null;
  •         try{
  •             stu = (Student)super.clone();   //浅复制  
  •         }catch(CloneNotSupportedException e) {
  •             e.printStackTrace();
  •         }
  •         stu.addr = (Address)addr.clone();   //深度复制  
  •         return stu;
  •     }
  • }
  • public class Test {
  •     public static void main(String args[]) {
  •         Address addr = new Address();
  •         addr.setAdd("杭州市");
  •         Student stu1 = new Student();
  •         stu1.setNumber(123);
  •         stu1.setAddr(addr);
  •         Student stu2 = (Student)stu1.clone();
  •         System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
  •         System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
  •         addr.setAdd("西湖区");
  •         System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());
  •         System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());
  •     }
  • }

package abc;class Address implements Cloneable {        private String add;        public String getAdd() {                return add;        }        public void setAdd(String add) {                this.add = add;        }                @Override        public Object clone() {                Address addr = null;                try{                        addr = (Address)super.clone();                }catch(CloneNotSupportedException e) {                        e.printStackTrace();                }                return addr;        }}class Student implements Cloneable{        private int number;        private Address addr;                public Address getAddr() {                return addr;        }        public void setAddr(Address addr) {                this.addr = addr;        }        public int getNumber() {                return number;        }        public void setNumber(int number) {                this.number = number;        }                @Override        public Object clone() {                Student stu = null;                try{                        stu = (Student)super.clone();        //浅复制                }catch(CloneNotSupportedException e) {                        e.printStackTrace();                }                stu.addr = (Address)addr.clone();        //深度复制                return stu;        }}public class Test {                public static void main(String args[]) {                                Address addr = new Address();                addr.setAdd("杭州市");                Student stu1 = new Student();                stu1.setNumber(123);                stu1.setAddr(addr);                                Student stu2 = (Student)stu1.clone();                                System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());                System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());                                addr.setAdd("西湖区");                                System.out.println("学生1:" + stu1.getNumber() + ",地址:" + stu1.getAddr().getAdd());                System.out.println("学生2:" + stu2.getNumber() + ",地址:" + stu2.getAddr().getAdd());        }}
打印结果:


  • 学生1:123,地址:杭州市
  • 学生2:123,地址:杭州市
  • 学生1:123,地址:西湖区
  • 学生2:123,地址:杭州市

学生1:123,地址:杭州市学生2:123,地址:杭州市学生1:123,地址:西湖区学生2:123,地址:杭州市
这样结果就符合我们的想法了。
总结:浅拷贝是指在拷贝对象时,对于基本数据类型的变量会重新复制一份,而对于引用类型的变量只是对引用进行拷贝,
没有对引用指向的对象进行拷贝。
而深拷贝是指在拷贝对象时,同时会对引用指向的对象进行拷贝。
区别就在于是否对  对象中的引用变量所指向的对象进行拷贝。

最后我们可以看看API里其中一个实现了clone方法的类:
java.util.Date:

  • /**
  • * Return a copy of this object.
  • */
  • public Object clone() {
  •     Date d = null;
  •     try {
  •         d = (Date)super.clone();
  •         if (cdate != null) {
  •             d.cdate = (BaseCalendar.Date) cdate.clone();
  •         }
  •     } catch (CloneNotSupportedException e) {} // Won't happen  
  •     return d;
  • }

    /**     * Return a copy of this object.     */    public Object clone() {        Date d = null;        try {            d = (Date)super.clone();            if (cdate != null) {                d.cdate = (BaseCalendar.Date) cdate.clone();            }        } catch (CloneNotSupportedException e) {} // Won't happen        return d;    }
该类其实也属于深度复制。


回复 支持 反对

使用道具 举报

774

主题

858

帖子

5500

积分

积分
5500
推广工具板凳
 楼主| 发表于 2016-10-17 11:12:30 | 只看该作者
实例教程3

Java语言的一个优点就是取消了指针的概念,但也导致了许多程序员在编程中常常忽略了对象与引用的区别,本文会试图澄清这一概念。并且由于Java不能通过简单的赋值来解决对象复制的问题,在开发过程中,也常常要要应用clone()方法来复制对象。本文会让你了解什么是影子clone与深度clone,认识它们的区别、优点及缺点。
      看到这个标题,是不是有点困惑:Java语言明确说明取消了指针,因为指针往往是在带来方便的同时也是导致代码不安全的根源,同时也会使程序的变得非常复杂难以理解,滥用指针写成的代码不亚于使用早已臭名昭著的"GOTO"语句。Java放弃指针的概念绝对是极其明智的。但这只是在Java语言中没有明确的指针定义,实质上每一个new语句返回的都是一个指针的引用,只不过在大多时候Java中不用关心如何操作这个"指针",更不用象在操作C++的指针那样胆战心惊。唯一要多多关心的是在给函数传递对象的时候。

[java] view plain copy


  • package com.zoer.src;  
  •   
  • public class ObjRef {  
  •     Obj aObj = new Obj();  
  •     int aInt = 11;  
  •   
  •     public void changeObj(Obj inObj) {  
  •         inObj.str = "changed value";  
  •     }  
  •   
  •     public void changePri(int inInt) {  
  •         inInt = 22;  
  •     }  
  •   
  •     public static void main(String[] args) {  
  •         ObjRef oRef = new ObjRef();  
  •   
  •         System.out.println("Before call changeObj() method: " + oRef.aObj);  
  •         oRef.changeObj(oRef.aObj);  
  •         System.out.println("After call changeObj() method: " + oRef.aObj);  
  •   
  •         System.out.println("==================Print Primtive=================");  
  •         System.out.println("Before call changePri() method: " + oRef.aInt);  
  •         oRef.changePri(oRef.aInt);  
  •         System.out.println("After call changePri() method: " + oRef.aInt);  
  •   
  •     }  
  • }  

[java] view plain copy


  • package com.zoer.src;  
  •   
  • public class Obj {  
  •   
  •     String str = "init value";  
  •   
  •     public String toString() {  
  •         return str;  
  •     }  
  • }  

      这段代码的主要部分调用了两个很相近的方法,changeObj()和changePri()。唯一不同的是它们一个把对象作为输入参数,另一个把Java中的基本类型int作为输入参数。并且在这两个函数体内部都对输入的参数进行了改动。看似一样的方法,程序输出的结果却不太一样。changeObj()方法真正的把输入的参数改变了,而changePri()方法对输入的参数没有任何的改变。

      从这个例子知道Java对对象和基本的数据类型的处理是不一样的。和C语言一样,当把Java的基本数据类型(如int,char,double等)作为入口参数传给函数体的时候,传入的参数在函数体内部变成了局部变量,这个局部变量是输入参数的一个拷贝,所有的函数体内部的操作都是针对这个拷贝的操作,函数执行结束后,这个局部变量也就完成了它的使命,它影响不到作为输入参数的变量。这种方式的参数传递被称为"值传递"。而在Java中用对象作为入口参数的传递则缺省为"引用传递",也就是说仅仅传递了对象的一个"引用",这个"引用"的概念同C语言中的指针引用是一样的。当函数体内部对输入变量改变时,实质上就是在对这个对象的直接操作。
      除了在函数传值的时候是"引用传递",在任何用"="向对象变量赋值的时候都是"引用传递"。就是类似于给变量再起一个别名。两个名字都指向内存中的同一个对象。
      在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的。在Java语言中,用简单的赋值语句是不能满足这种需求的。要满足这种需求虽然有很多途径,但实现clone()方法是其中最简单,也是最高效的手段。
      Java的所有类都默认继承java.lang.Object类,在java.lang.Object类中有一个方法clone()。JDK API的说明文档解释这个方法将返回Object对象的一个拷贝。要说明的有两点:一是拷贝对象返回的是一个新对象,而不是一个引用。二是拷贝对象与用new操作符返回的新对象的区别就是这个拷贝已经包含了一些原来对象的信息,而不是对象的初始信息。
      怎样应用clone()方法?

      一个很典型的调用clone()代码如下:

[java] view plain copy


  • public class CloneClass implements Cloneable {  
  •     public int aInt;  
  •   
  •     public Object clone() {  
  •         CloneClass o = null;  
  •         try {  
  •             o = (CloneClass) super.clone();  
  •         } catch (CloneNotSupportedException e) {  
  •             e.printStackTrace();  
  •         }  
  •         return o;  
  •     }  
  • }  

      有三个值得注意的地方,一是希望能实现clone功能的CloneClass类实现了Cloneable接口,这个接口属于java.lang包,java.lang包已经被缺省的导入类中,所以不需要写成java.lang.Cloneable。另一个值得请注意的是重载了clone()方法。最后在clone()方法中调用了super.clone(),这也意味着无论clone类的继承结构是什么样的,super.clone()直接或间接调用了java.lang.Object类的clone()方法。下面再详细的解释一下这几点。
      应该说第三点是最重要的,仔细观察一下Object类的clone()一个native方法,native方法的效率一般来说都是远高于java中的非native方法。这也解释了为什么要用Object中clone()方法而不是先new一个类,然后把原始对象中的信息赋到新对象中,虽然这也实现了clone功能。对于第二点,也要观察Object类中的clone()还是一个protected属性的方法。这也意味着如果要应用clone()方法,必须继承Object类,在Java中所有的类是缺省继承Object类的,也就不用关心这点了。然后重载clone()方法。还有一点要考虑的是为了让其它类能调用这个clone类的clone()方法,重载之后要把clone()方法的属性设置为public。
       那么clone类为什么还要实现Cloneable接口呢?稍微注意一下,Cloneable接口是不包含任何方法的!其实这个接口仅仅是一个标志,而且这个标志也仅仅是针对Object类中clone()方法的,如果clone类没有实现Cloneable接口,并调用了Object的clone()方法(也就是调用了super.Clone()方法),那么Object的clone()方法就会抛出CloneNotSupportedException异常。
      以上是clone的最基本的步骤,想要完成一个成功的clone,还要了解什么是"影子clone"和"深度clone"。
      什么是影子clone?

[java] view plain copy


  • package com.zoer.src;  
  •   
  • class UnCloneA {  
  •     private int i;  
  •   
  •     public UnCloneA(int ii) {  
  •         i = ii;  
  •     }  
  •   
  •     public void doublevalue() {  
  •         i *= 2;  
  •     }  
  •   
  •     public String toString() {  
  •         return Integer.toString(i);  
  •     }  
  • }  
  •   
  • class CloneB implements Cloneable {  
  •     public int aInt;  
  •     public UnCloneA unCA = new UnCloneA(111);  
  •   
  •     public Object clone() {  
  •         CloneB o = null;  
  •         try {  
  •             o = (CloneB) super.clone();  
  •         } catch (CloneNotSupportedException e) {  
  •             e.printStackTrace();  
  •         }  
  •         return o;  
  •     }  
  • }  
  •   
  • public class ObjRef {  
  •     public static void main(String[] a) {  
  •         CloneB b1 = new CloneB();  
  •         b1.aInt = 11;  
  •         System.out.println("before clone,b1.aInt = " + b1.aInt);  
  •         System.out.println("before clone,b1.unCA = " + b1.unCA);  
  •   
  •         CloneB b2 = (CloneB) b1.clone();  
  •         b2.aInt = 22;  
  •         b2.unCA.doublevalue();  
  •         System.out.println("=================================");  
  •         System.out.println("after clone,b1.aInt = " + b1.aInt);  
  •         System.out.println("after clone,b1.unCA = " + b1.unCA);  
  •         System.out.println("=================================");  
  •         System.out.println("after clone,b2.aInt = " + b2.aInt);  
  •         System.out.println("after clone,b2.unCA = " + b2.unCA);  
  •     }  
  • }  

       输出结果:
before clone,b1.aInt = 11
before clone,b1.unCA = 111
=================================
after clone,b1.aInt = 11
after clone,b1.unCA = 222
=================================
after clone,b2.aInt = 22
after clone,b2.unCA = 222

       输出的结果说明int类型的变量aInt和UnCloneA的实例对象unCA的clone结果不一致,int类型是真正的被clone了,因为改变了b2中的aInt变量,对b1的aInt没有产生影响,也就是说,b2.aInt与b1.aInt已经占据了不同的内存空间,b2.aInt是b1.aInt的一个真正拷贝。相反,对b2.unCA的改变同时改变了b1.unCA,很明显,b2.unCA和b1.unCA是仅仅指向同一个对象的不同引用!从中可以看出,调用Object类中clone()方法产生的效果是:先在内存中开辟一块和原始对象一样的空间,然后原样拷贝原始对象中的内容。对基本数据类型,这样的操作是没有问题的,但对非基本类型变量,我们知道它们保存的仅仅是对象的引用,这也导致clone后的非基本类型变量和原始对象中相应的变量指向的是同一个对象。
       大多时候,这种clone的结果往往不是我们所希望的结果,这种clone也被称为"影子clone"。要想让b2.unCA指向与b2.unCA不同的对象,而且b2.unCA中还要包含b1.unCA中的信息作为初始信息,就要实现深度clone。
       怎么进行深度clone?
       把上面的例子改成深度clone很简单,需要两个改变:一是让UnCloneA类也实现和CloneB类一样的clone功能(实现Cloneable接口,重载clone()方法)。二是在CloneB的clone()方法中加入一句o.unCA = (UnCloneA)unCA.clone();

[java] view plain copy


  • package com.zoer.src;  
  •   
  • class UnCloneA implements Cloneable {  
  •     private int i;  
  •   
  •     public UnCloneA(int ii) {  
  •         i = ii;  
  •     }  
  •   
  •     public void doublevalue() {  
  •         i *= 2;  
  •     }  
  •   
  •     public String toString() {  
  •         return Integer.toString(i);  
  •     }  
  •   
  •     public Object clone() {  
  •         UnCloneA o = null;  
  •         try {  
  •             o = (UnCloneA) super.clone();  
  •         } catch (CloneNotSupportedException e) {  
  •             e.printStackTrace();  
  •         }  
  •         return o;  
  •     }  
  • }  
  •   
  • class CloneB implements Cloneable {  
  •     public int aInt;  
  •     public UnCloneA unCA = new UnCloneA(111);  
  •   
  •     public Object clone() {  
  •         CloneB o = null;  
  •         try {  
  •             o = (CloneB) super.clone();  
  •         } catch (CloneNotSupportedException e) {  
  •             e.printStackTrace();  
  •         }  
  •         o.unCA = (UnCloneA) unCA.clone();  
  •         return o;  
  •     }  
  • }  
  •   
  • public class CloneMain {  
  •     public static void main(String[] a) {  
  •         CloneB b1 = new CloneB();  
  •         b1.aInt = 11;  
  •         System.out.println("before clone,b1.aInt = " + b1.aInt);  
  •         System.out.println("before clone,b1.unCA = " + b1.unCA);  
  •   
  •         CloneB b2 = (CloneB) b1.clone();  
  •         b2.aInt = 22;  
  •         b2.unCA.doublevalue();  
  •         System.out.println("=================================");  
  •         System.out.println("after clone,b1.aInt = " + b1.aInt);  
  •         System.out.println("after clone,b1.unCA = " + b1.unCA);  
  •         System.out.println("=================================");  
  •         System.out.println("after clone,b2.aInt = " + b2.aInt);  
  •         System.out.println("after clone,b2.unCA = " + b2.unCA);  
  •     }  
  • }  


输出结果:
before clone,b1.aInt = 11
before clone,b1.unCA = 111
=================================
after clone,b1.aInt = 11
after clone,b1.unCA = 111
=================================
after clone,b2.aInt = 22
after clone,b2.unCA = 222

      可以看出,现在b2.unCA的改变对b1.unCA没有产生影响。此时b1.unCA与b2.unCA指向了两个不同的UnCloneA实例,而且在CloneB b2 = (CloneB)b1.clone();调用的那一刻b1和b2拥有相同的值,在这里,b1.i = b2.i = 11。
        要知道不是所有的类都能实现深度clone的。例如,如果把上面的CloneB类中的UnCloneA类型变量改成StringBuffer类型,看一下JDK API中关于StringBuffer的说明,StringBuffer没有重载clone()方法,更为严重的是StringBuffer还是一个final类,这也是说我们也不能用继承的办法间接实现StringBuffer的clone。如果一个类中包含有StringBuffer类型对象或和StringBuffer相似类的对象,我们有两种选择:要么只能实现影子clone,要么就在类的clone()方法中加一句(假设是SringBuffer对象,而且变量名仍是unCA): o.unCA = new StringBuffer(unCA.toString()); //原来的是:o.unCA = (UnCloneA)unCA.clone();
       还要知道的是除了基本数据类型能自动实现深度clone以外,String对象是一个例外,它clone后的表现好象也实现了深度clone,虽然这只是一个假象,但却大大方便了我们的编程。
       Clone中String和StringBuffer的区别
       应该说明的是,这里不是着重说明String和StringBuffer的区别,但从这个例子里也能看出String类的一些与众不同的地方。
       下面的例子中包括两个类,CloneC类包含一个String类型变量和一个StringBuffer类型变量,并且实现了clone()方法。在StrClone类中声明了CloneC类型变量c1,然后调用c1的clone()方法生成c1的拷贝c2,在对c2中的String和StringBuffer类型变量用相应的方法改动之后打印结果:

[java] view plain copy


  • package com.zoer.src;  
  •   
  • class CloneC implements Cloneable {  
  •     public String str;  
  •     public StringBuffer strBuff;  
  •   
  •     public Object clone() {  
  •         CloneC o = null;  
  •         try {  
  •             o = (CloneC) super.clone();  
  •         } catch (CloneNotSupportedException e) {  
  •             e.printStackTrace();  
  •         }  
  •         return o;  
  •     }  
  •   
  • }  
  •   
  • public class StrClone {  
  •     public static void main(String[] a) {  
  •         CloneC c1 = new CloneC();  
  •         c1.str = new String("initializeStr");  
  •         c1.strBuff = new StringBuffer("initializeStrBuff");  
  •         System.out.println("before clone,c1.str = " + c1.str);  
  •         System.out.println("before clone,c1.strBuff = " + c1.strBuff);  
  •   
  •         CloneC c2 = (CloneC) c1.clone();  
  •         c2.str = c2.str.substring(0, 5);  
  •         c2.strBuff = c2.strBuff.append(" change strBuff clone");  
  •         System.out.println("=================================");  
  •         System.out.println("after clone,c1.str = " + c1.str);  
  •         System.out.println("after clone,c1.strBuff = " + c1.strBuff);  
  •         System.out.println("=================================");  
  •         System.out.println("after clone,c2.str = " + c2.str);  
  •         System.out.println("after clone,c2.strBuff = " + c2.strBuff);  
  •     }  
  • }  


执行结果:

[java] view plain copy


  • <span style="font-family:'Microsoft YaHei';"><span style="font-size:16px;">before clone,c1.str = initializeStr  
  • before clone,c1.strBuff = initializeStrBuff  
  • =================================  
  • after clone,c1.str = initializeStr  
  • after clone,c1.strBuff = initializeStrBuff change strBuff clone  
  • =================================  
  • after clone,c2.str = initi  
  • after clone,c2.strBuff = initializeStrBuff change strBuff clone  
  • </span></span>  


        打印的结果可以看出,String类型的变量好象已经实现了深度clone,因为对c2.str的改动并没有影响到c1.str!难道Java把Sring类看成了基本数据类型?其实不然,这里有一个小小的把戏,秘密就在于c2.str = c2.str.substring(0,5)这一语句!实质上,在clone的时候c1.str与c2.str仍然是引用,而且都指向了同一个String对象。但在执行c2.str = c2.str.substring(0,5)的时候,它作用相当于生成了一个新的String类型,然后又赋回给c2.str。这是因为String被Sun公司的工程师写成了一个不可更改的类(immutable class),在所有String类中的函数都不能更改自身的值。


回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

相关导读
信息发布软件AIWROK软件滑动方法集合示例
AIWROK软件滑动方法集合示例
信息发布软件AIWROK软件安卓AIWROK汇集软件点击
AIWROK软件安卓AIWROK汇集软件点击
信息发布软件苹果系统点击方法综合示例
苹果系统点击方法综合示例
信息发布软件AIWROK苹果系统找图方法完整示例集合
AIWROK苹果系统找图方法完整示例集合
信息发布软件苹果系统找图方法完整示例集合
苹果系统找图方法完整示例集合
信息发布软件苹果IOS系统找字OCR方法例子
苹果IOS系统找字OCR方法例子
信息发布软件AIWORK软件数组高级示例
AIWORK软件数组高级示例
信息发布软件AIWROK软件运算符封装库示例
AIWROK软件运算符封装库示例
信息发布软件AIWROK软件语法运行小示例
AIWROK软件语法运行小示例
信息发布软件AIWROK软件JS循环小示例
AIWROK软件JS循环小示例
信息发布软件AIWROK软件H5网页被主脚本获取值用法
AIWROK软件H5网页被主脚本获取值用法
信息发布软件AIWROK软件创建可暂停恢复的多线程任务
AIWROK软件创建可暂停恢复的多线程任务
信息发布软件AIWROK软件类型转换方法例子
AIWROK软件类型转换方法例子
信息发布软件AIWROK软件H5脚本执行与进度显示
AIWROK软件H5脚本执行与进度显示 .
信息发布软件AIWROK软件根据时间段执行异步任务支持多线程并行处理
AIWROK软件根据时间段执行异步任务支持多线程并行处理
信息发布软件H5自动开关执行脚本功能演示
H5自动开关执行脚本功能演示
信息发布软件AIWROK软件H5单选脚本运行示例
AIWROK软件H5单选脚本运行示例
信息发布软件H5任务脚本选择与执行中心
H5任务脚本选择与执行中心
信息发布软件H5里CheckBox控件演示
H5里CheckBox控件演示
信息发布软件AIWROK软件正则用法实际例子
AIWROK软件正则用法实际例子
信息发布软件AIWROK软件权限管理器实现
AIWROK软件权限管理器实现
信息发布软件AIWORK软件节点方法无碍示例子
AIWORK软件节点方法无碍示例子
信息发布软件JSON.stringify 和 JSON.parse 完整示例
JSON.stringify 和 JSON.parse 完整示例
信息发布软件AIWROK软件展示JavaScript各种语句标识符的用法
AIWROK软件展示JavaScript各种语句标识符的用法
信息发布软件JS巧妙地组合使用各种条件语句
JS巧妙地组合使用各种条件语句
信息发布软件AIWROK手机数据库MySQL数据库截图片批量上传操作脚本
AIWROK手机数据库MySQL数据库截图片批量上传操作脚本
信息发布软件HID中文输入智能打字功能
HID中文输入智能打字功能
信息发布软件AIWROK软件对象工具函数库例子
AIWROK软件对象工具函数库例子
信息发布软件AIWROK软件H5交互演示黄色主题
AIWROK软件H5交互演示黄色主题
信息发布软件H5单按钮执行脚本示例
H5单按钮执行脚本示例
信息发布软件苹果H5界面完整调用脚本示例
苹果H5界面完整调用脚本示例
信息发布软件AIWROK软件平台设备信息全面检测工具例子
AIWROK软件平台设备信息全面检测工具例子
信息发布软件AIWROK创建和放大日志窗口并展示动态内容
AIWROK创建和放大日志窗口并展示动态内容
信息发布软件AIWROK软件device相关方法获取设备信息例子
AIWROK软件device相关方法获取设备信息例子[/backcolor]
信息发布软件数据库MySQL实时内容随机调用
数据库MySQL实时内容随机调用
信息发布软件AIWROK软件分享一个特效苹果H5页面
AIWROK软件分享一个特效苹果H5页面
信息发布软件数据库MYQ业务流程心跳程序启动
数据库MYQ业务流程心跳程序启动
信息发布软件数据库MySQL功能支持创建表插入中文数据查询删除功能例子
数据库MySQL功能支持创建表插入中文数据查询删除功能例子
信息发布软件AIWROK软件Zip 高级操作复杂示例
AIWROK软件Zip 高级操作复杂示例
信息发布软件AIWROK软件txt_文件读写方法小结
AIWROK软件txt_文件读写方法小结
信息发布软件AIWROK软件file文件操作方法小结
AIWROK软件file文件操作方法小结
信息发布软件AIWORK软件配置读写H5演示配套脚本
AIWORK软件配置读写H5演示配套脚本
信息发布软件AIWROK配置读写功能演示示例
AIWROK配置读写功能演示示例
信息发布软件AIWROK截图缓存工具
AIWROK截图缓存工具
信息发布软件AIWROK线程许可证工具
AIWROK线程许可证工具
信息发布软件整理了AIWROK环境下常用的Date对象和sleep对象方法
整理了AIWROK环境下常用的Date对象和sleep对象方法
信息发布软件FastUI界面普通用法
FastUI界面普通用法
信息发布软件FastUI界面类[window]方法小结
FastUI界面类[window]方法小结 方法 1:close(关闭指定窗口)方法 2:closeAll(关闭所有窗口)方法 3:loadUI(加载 UI 界面)方法 4:onClose(监听窗口关闭事件)方法 5:onLoad(监听窗口加载事件)方法 6:setFull(设置窗口全屏)方法 7:setHeight(设置窗口高度)方法 8:setHidden(隐藏窗口)方法 9:setLeft(设置窗口 X 轴坐标)方法 10:setTop(设置窗口 Y 轴坐标)方法 11:setVisable(显示隐藏的窗口)方
信息发布软件AIWROK软件按钮监听UI界面与事件监听功能演示
AIWROK软件按钮监听UI界面与事件监听功能演示.
信息发布软件AWIROK软件多选[uiCheckBox]方法小结
AWIROK软件多选方法小结 方法一:findByID 加载多选控件方法二:getAllChecked 获取所有选中项方法三:getAllSelect 获取所有选项方法四:getChecked 获取某个选项是否选中方法五:setChecked 设置某个选项是否选中方法六:setCheckeds 设置多个选项是否选中方法七:setHeight 设置高度
信息发布软件AIWROK日志演示开启日志显示 → 放大 → 关闭代码
AIWROK日志演示开启日志显示 → 放大 → 关闭代码
信息发布软件&#127983;AIWROK数组方法高级应用案例
🏯AIWROK数组方法高级应用案例
信息发布软件AIWROK软件日志悬浮窗简化版自动切换位置
AIWROK软件日志悬浮窗简化版自动切换位置
信息发布软件AIWROK软件String实例演示
AIWROK软件String实例演示
信息发布软件AIWROK软件S内置String类[String]方法小结
AIWROK软件S内置String类[String]方法小结 方法 1:charAt[/backcolor]方法 2:charCodeAt[/backcolor]方法 3:indexOf[/backcolor]方法 4:lastIndexOf[/backcolor]方法 5:length[/backcolor]方法 6:match[/backcolor]方法 7:replace[/backcolor]方法 8:replaceAll[/backcolor]方法 9:split[/backcolor]方法 10:startsWith[/backcolor]方法 11:substr[/backcolor]方法 12:substring[/backcolor]方法 13:trim[/backcol
信息发布软件AIWROK软件完整的WebSocket客户端示例
这段代码是一个完整的WebSocket客户端示例,用于连接到指定的WebSocket服务器并处理各种事件。具体来说,代码的作用如下: 定义服务器地址:首先定义了一个服务器的IP地址和端口号 var ip = "154.37.221.104:8886";。 创建WebSocket对象:尝试创建一个新的WebSocket对象 var ws = new WebSocket();。注意,这里的 new ws() 应该是 new WebSocket()。 添加事件监听器:代码中尝试为WebSocket对象添加事件监听器,但这里有一个错误。
信息发布软件AIWROK软件苹果系统中实现四种基本滑动操作
AIWROK软件苹果系统中实现四种基本滑动操作
信息发布软件hid的滑动没有百分比坐标滑动吗
hid的滑动没有百分比坐标滑动吗
信息发布软件单选控件[uiRadioButton]方法小结
单选控件方法小结 方法 1:加载单选控件[/backcolor]方法 2:获取选中项[/backcolor]方法 3:设置高度[/backcolor]方法 4:设置选中项[/backcolor]
信息发布软件AIWROK软件无障碍触摸操作示例:点击、左右滑动、上下滑动实例
AIWROK软件无障碍触摸操作示例:点击、左右滑动、上下滑动实例
信息发布软件AIWROK软件安卓随机工具应用函数生成
AIWROK软件安卓随机工具应用函数生成
信息发布软件用在AIWORK软件代码中的实用符号分类整理2
用在AIWORK软件代码中的实用符号分类整理2 软件IDE用Emoji符号分类整理(含用途说明)一、表情与情感1. 微笑 [*]😀 笑脸(基础开心反馈,用于操作成功后的友好提示) [*]😃 笑脸大眼睛(强化开心情绪,用于重要任务完成后的积极反馈) [*]😄 笑脸和微笑的眼睛(温和友好的状态,用于日常交互中的正向回应) [*]😁 带着微笑的眼睛(轻松愉悦的反馈,用于轻度成功或趣味操作) [*]
信息发布软件AIWROK软件图像二值化的各种方法和应用场景
AIWROK软件图像二值化的各种方法和应用场景
信息发布软件AIWROK软件找图区分页面变化和卡死状态
AIWROK软件找图区分页面变化和卡死状态
信息发布软件AIWROK苹果系统Map 数据管理[map]小结
AIWROK苹果系统Map 数据管理[map]小结 方法一:add(添加键值对)[/backcolor]方法二:delete(删除指定键值对)[/backcolor]方法三:clear(清空所有键值对)[/backcolor]方法四:get(根据键获取值)[/backcolor]方法五:getAllValue(获取所有值)[/backcolor]方法六:toString(转换为字符串)[/backcolor]完整示例:

QQ|( 京ICP备09078825号 )

本网站信息发布软件,是可以发布论坛,发送信息到各大博客,各大b2b软件自动发布,好不夸张的说:只要手工能发在电脑打开IE能发的网站,用这个宣传软件就可以仿制动作,进行推送发到您想发送的B2B网站或是信息发布平台上,不管是后台,还是前台,都可以进行最方便的广告发布,这个广告发布软件,可以按月购买,还可以试用软件,对网站的验证码也可以完全自动对信息发布,让客户自动找上门,使企业轻松实现b2b发布,这个信息发布软件,均是本站原创正版开发,拥有正版的血统,想要新功能,欢迎提意见给我,一好的分类信息群发软件在手,舍我其谁。QQ896757558

GMT+8, 2026-2-4 10:53 , Processed in 0.359746 second(s), 46 queries .

宣传软件--信息发布软件--b2b软件广告发布软件

快速回复 返回顶部 返回列表