可变对象与不可变对象?

时间:2021-03-07 03:58:54
在core java中看到这样一道例题,如下:

class Test{
  public Date getHireDay(){
       return hireDay;
  }
  private Date hireDay;
}

书中提到: 不要编写返回引用可变对象的访问器方法
应更改为如下:

class Test{
  public Date getHireDay(){
       return (Date)hireDay.clone();
  }
  private Date hireDay;
}

我想问的是,什么是可变对象?
不可变对象是指final类的实例,如String ,Integer这些。
可变对象是是那些没有使用final修饰的类的实例.
不知我这么理解是否正确?
如果不是,那么我们将进行程序设计时该如何判断该对象是否为可变对象或不可变对象?

16 个解决方案

#1


我感觉,也不能千篇一律,上面说的情况可能是为了防止其它程序改变对象的属性,(当某一程序在应用此对象时)
Test t=new Test();
Date d=t.getHireDay()//假如期望得到的是2008-06-16
// other...
setXXX(t)//在此方法里调用了t.setHireDay方法,改变了其值,那么上面的d就出现我们不想要的结果,
如果我们返回的是hireDay的一个克隆,那么就不会出现值被改的情况了,因为这个克隆的对象没有引用(句柄)无法对他进行修改。
但是我们有时想用setXXX(t)去改变我们的hireDay,而且这也是我们所期望的,那么我们就不能用clone。所以说因事而定。

#2


我也感觉这里的可变对象好像并不是不可变对象immutable的反义

#3


不可变对象没有一个严格明确的定义,如果对象创建出来后(通过构造方法)无论如何对此对象进行非暴力操作(不用反射),此对象的状态(实例域的值)都不会发生变化,那么此对象就是不可变的,相应类就是不可变类,跟是否用 final 修饰没关系,final 修饰类是防止此类被继承。要注意,有些对像创建出来后,即便我们不对它进行任何操作(通过调用方法或访问实例域),它的状态也可能发生变化。

#4


立即引伸出一个重要的知识点:深复制,浅复制

#5


引用 1 楼 ayueiloveyou 的回复:
我感觉,也不能千篇一律,上面说的情况可能是为了防止其它程序改变对象的属性,(当某一程序在应用此对象时) 
Test t=new Test(); 
Date d=t.getHireDay()//假如期望得到的是2008-06-16 
// other... 
setXXX(t)//在此方法里调用了t.setHireDay方法,改变了其值,那么上面的d就出现我们不想要的结果, 
如果我们返回的是hireDay的一个克隆,那么就不会出现值被改的情况了,因为这个克隆的对象没有引用(句柄)无法对他进行修…


赞同

#6


不知道你理解值传递和引用传递了没有
因为除了基础对象 String,int,boolea,double,float,shot,char,byte....
是使用值传递的
就是说,这些对象在另外一个方法中被调用时,不会改变他们本身的值,改变的只是他们引用的一个副本

但是,其他所有的实例对象都是使用引用传递的
当你把一个对象通过一个GET方法传递出去以后,在外部可以通过这个引用对象上的方法来改变他对象中属性的值

举个例子

public class myint{
 public int i = 0;
}
public class test {
 int i = 0;
 myint Obj_i = new myint();

 public int getI(){ return i;}
 public myint getMyInt(){ return Obj_i;}
}
public class test2 {
 public static void main(String[] args) {
  test t = new test();
  int i = t.getI();
  myint Obj_i = t.getMyInt();

 // 尝试改变i 和obj_i的值
  i = 10;
  Obj_i.i = 20;

 //显示test上原本的属性
  System.out.println(t.i);   // 输出仍然是0  test上的i并没有被改变
  System.out.println(t.getMyInt().i);   // 输出是20  test上的Obj_i.i的值被改变了
 }
}

#7


该回复于2015-05-26 13:18:22被管理员删除

#8


就是说 如果你想保持一个内部对象的值不被改变,就不能简单的返回出他的引用

#9


恩, 学习了 ^_^,。。。。。。

#10


象创建出来后(通过构造方法)无论如何对此对象进行非暴力操作(不用反射),此对象的状态(实例域的值)都不会发生变化,那么此对象就是不可变的,相应类就是不可变类,跟是否用 final 修饰没关系,final 修饰类是防止此类被继承。

#11


学习了,mark!(竟然说我回复太短了)

#12


引用 6 楼 beiouwolf 的回复:
不知道你理解值传递和引用传递了没有 
因为除了基础对象 String,int,boolea,double,float,shot,char,byte.... 
是使用值传递的 
就是说,这些对象在另外一个方法中被调用时,不会改变他们本身的值,改变的只是他们引用的一个副本 

但是,其他所有的实例对象都是使用引用传递的 
当你把一个对象通过一个GET方法传递出去以后,在外部可以通过这个引用对象上的方法来改变他对象中属性的值 

举个例子 

Java code
public cla…

#13


6楼

#14


其实我觉得不用想的太复杂,这个例子的真正要告诉我们的是不要破环封装性:
不能在别的类中修改本类的私有域,但是别的类调用本类修改器就可以修改。
然而上面的可变对象没有调用本类的修改器就能修改本类的私有域,这就破环了封装。

可变对象就是基本数据类型对象和不可变类对象(类中没有修改器改变其对象的类,例如String类)以外的对象。

#15


不可变类就是这个类的实例一旦创建 无法修改它的属性值
反之就是可变类

#16


引用 6 楼  的回复:
不知道你理解值传递和引用传递了没有
因为除了基础对象 String,int,boolea,double,float,shot,char,byte....
是使用值传递的
就是说,这些对象在另外一个方法中被调用时,不会改变他们本身的值,改变的只是他们引用的一个副本

但是,其他所有的实例对象都是使用引用传递的
当你把一个对象通过一个GET方法传递出去以后,在外部可以通过这个引用对象上的方……

java是按值传递的,就算是引用类型,传递的也是该引用的地址,所以是按值传递的,改变的只是该地址中的变量。

#1


我感觉,也不能千篇一律,上面说的情况可能是为了防止其它程序改变对象的属性,(当某一程序在应用此对象时)
Test t=new Test();
Date d=t.getHireDay()//假如期望得到的是2008-06-16
// other...
setXXX(t)//在此方法里调用了t.setHireDay方法,改变了其值,那么上面的d就出现我们不想要的结果,
如果我们返回的是hireDay的一个克隆,那么就不会出现值被改的情况了,因为这个克隆的对象没有引用(句柄)无法对他进行修改。
但是我们有时想用setXXX(t)去改变我们的hireDay,而且这也是我们所期望的,那么我们就不能用clone。所以说因事而定。

#2


我也感觉这里的可变对象好像并不是不可变对象immutable的反义

#3


不可变对象没有一个严格明确的定义,如果对象创建出来后(通过构造方法)无论如何对此对象进行非暴力操作(不用反射),此对象的状态(实例域的值)都不会发生变化,那么此对象就是不可变的,相应类就是不可变类,跟是否用 final 修饰没关系,final 修饰类是防止此类被继承。要注意,有些对像创建出来后,即便我们不对它进行任何操作(通过调用方法或访问实例域),它的状态也可能发生变化。

#4


立即引伸出一个重要的知识点:深复制,浅复制

#5


引用 1 楼 ayueiloveyou 的回复:
我感觉,也不能千篇一律,上面说的情况可能是为了防止其它程序改变对象的属性,(当某一程序在应用此对象时) 
Test t=new Test(); 
Date d=t.getHireDay()//假如期望得到的是2008-06-16 
// other... 
setXXX(t)//在此方法里调用了t.setHireDay方法,改变了其值,那么上面的d就出现我们不想要的结果, 
如果我们返回的是hireDay的一个克隆,那么就不会出现值被改的情况了,因为这个克隆的对象没有引用(句柄)无法对他进行修…


赞同

#6


不知道你理解值传递和引用传递了没有
因为除了基础对象 String,int,boolea,double,float,shot,char,byte....
是使用值传递的
就是说,这些对象在另外一个方法中被调用时,不会改变他们本身的值,改变的只是他们引用的一个副本

但是,其他所有的实例对象都是使用引用传递的
当你把一个对象通过一个GET方法传递出去以后,在外部可以通过这个引用对象上的方法来改变他对象中属性的值

举个例子

public class myint{
 public int i = 0;
}
public class test {
 int i = 0;
 myint Obj_i = new myint();

 public int getI(){ return i;}
 public myint getMyInt(){ return Obj_i;}
}
public class test2 {
 public static void main(String[] args) {
  test t = new test();
  int i = t.getI();
  myint Obj_i = t.getMyInt();

 // 尝试改变i 和obj_i的值
  i = 10;
  Obj_i.i = 20;

 //显示test上原本的属性
  System.out.println(t.i);   // 输出仍然是0  test上的i并没有被改变
  System.out.println(t.getMyInt().i);   // 输出是20  test上的Obj_i.i的值被改变了
 }
}

#7


该回复于2015-05-26 13:18:22被管理员删除

#8


就是说 如果你想保持一个内部对象的值不被改变,就不能简单的返回出他的引用

#9


恩, 学习了 ^_^,。。。。。。

#10


象创建出来后(通过构造方法)无论如何对此对象进行非暴力操作(不用反射),此对象的状态(实例域的值)都不会发生变化,那么此对象就是不可变的,相应类就是不可变类,跟是否用 final 修饰没关系,final 修饰类是防止此类被继承。

#11


学习了,mark!(竟然说我回复太短了)

#12


引用 6 楼 beiouwolf 的回复:
不知道你理解值传递和引用传递了没有 
因为除了基础对象 String,int,boolea,double,float,shot,char,byte.... 
是使用值传递的 
就是说,这些对象在另外一个方法中被调用时,不会改变他们本身的值,改变的只是他们引用的一个副本 

但是,其他所有的实例对象都是使用引用传递的 
当你把一个对象通过一个GET方法传递出去以后,在外部可以通过这个引用对象上的方法来改变他对象中属性的值 

举个例子 

Java code
public cla…

#13


6楼

#14


其实我觉得不用想的太复杂,这个例子的真正要告诉我们的是不要破环封装性:
不能在别的类中修改本类的私有域,但是别的类调用本类修改器就可以修改。
然而上面的可变对象没有调用本类的修改器就能修改本类的私有域,这就破环了封装。

可变对象就是基本数据类型对象和不可变类对象(类中没有修改器改变其对象的类,例如String类)以外的对象。

#15


不可变类就是这个类的实例一旦创建 无法修改它的属性值
反之就是可变类

#16


引用 6 楼  的回复:
不知道你理解值传递和引用传递了没有
因为除了基础对象 String,int,boolea,double,float,shot,char,byte....
是使用值传递的
就是说,这些对象在另外一个方法中被调用时,不会改变他们本身的值,改变的只是他们引用的一个副本

但是,其他所有的实例对象都是使用引用传递的
当你把一个对象通过一个GET方法传递出去以后,在外部可以通过这个引用对象上的方……

java是按值传递的,就算是引用类型,传递的也是该引用的地址,所以是按值传递的,改变的只是该地址中的变量。