java对象和引用

今天做到一个经典面试题,才发现自已对的java的对象和引用的知识点掌握的并不是很牢固,这篇文章用来复习回顾这些知识。

  先看下面的程序,

StringBuffer s;
s=new StringBuffer("Hello world!");

解读如下:第一个语句为引用分配了空间,而第二个语句则通过调用类的构造函数为类生成一个实例,并把该对象的地址传递给s。通过该两个操作,对象的内容则可以通过s进行访问----在java中都是通过引用来操控对象的。

 java对象与引用的可以说是互相关联的,却又彼此独立。彼此独立表现为:引用可以改变,它可以指向别的对象,比如,你可以给他另外的对象,如: 

s =new StringBuffer("Java") ;

这样一来,s就和它指向的第一个对象脱离关系了。

从存储空间来说,对象和引用也是独立的,他们存储在不同的地方,对象一般存储在堆中,而引用存储在速度更快的堆栈中。

引用可以指向不同的对象,对象也可以被多个引用操纵,如:

StringBuffer s1=s ;

这条语句使得s1和s指向同一个对象。所以不管使用哪个引用操纵对象,对象的内容都发生改变,并且只有一份,通过s1和s得到的内容自然也是一样的(当然String除外,因为String是一个特殊类,是一个不可变的类),如下程序所示:

StringBuffer s;

s = new StringBuffer("Java");

StringBuffer s1 = s;

s1.append(" World");
System.out.println("s1=" + s1.toString());//打印结果为:s1=Java World
System.out.println("s=" + s.toString());//打印结果为:s=Java World

程序结果表明,s1和s只是两个引用,它们都指向同一个对象,操纵的也是同一个对象。通过它们得到的是同一个对象的内容。

理解了对象和引用的关系,我们接着理解参数传递

  java中只有一种参数传递方式,那就是按值传递,即java中传递任何东西都是传值。如果传入方法的是基本类型的东西,你就得到此基本类型的一份拷贝。如果是传递引用,就得到引用的拷贝。

一般来说,对于基本类型的传递,很容易理解,而对于对象,总让人感觉是按引用传递,那么

用下面的例子来帮助理解:

public class Test {
	 public static void main(String[] args) {
		Student stu1 = new Student("张三","nan");
		Student stu2 = new Student("李四", "男");
//		System.out.println(stu1);
		testObj(stu1, stu2);
		System.out.println(stu1);
		System.out.println(stu2);
		
	}
	 static void testObj(Student s1,Student s2) {
		 s1.setSex("男");
		 s2 =s1 ;
		 System.out.println(s2); 
	 }
}
class Student{
	private String name;
	private String sex ;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	
	public Student(String name, String sex) {
		super();
		this.name = name;
		this.sex = sex;
	}
	public Student() {
		super();
		// TODO Auto-generated constructor stub
	}
	@Override
	public String toString() {
		return "Student [name=" + name + ", sex=" + sex + "]";
	}	
}

请注意看 testObj方法,里面进行的操作是先调用s1.setSex()方法,将性别修改为"男",然后又进行赋值操作,最后打印一次s2

请注意: 当把stu1,stu2传递给参数s1,s2时,s1,s2得到的是stu1,stu2的拷贝,所以s1和stu1指向的是同一个对象,同理s2和stu2也一样。因此使用s1操作影响的其实就是stu1指向的对象,所以调用s1.setSex("男"),stu1指向的对象的内容发生了改变。sex的值从 "nan"变为"男"

对于 s2=s1操作来说,在方法体内改变了s2指向的内容。给s2重新进行赋值(地址进行重新传递)后,s2和stu2已经毫无关联,它已经和stu2指向了不同的地址,对s2进行任何操作,都不会影响stu2指向的对象,所以s2=s1执行前后stu2指向的对象内容并未发生改变。而在此方法体中打印s2 ,应该是  Student [name=张三, sex=男],而回到main方法中,重新打印stu2 ,则应该是Student [name=李四, sex=男] 。 执行程序,得到的结果证明了我们的预期。

Student [name=张三, sex=男]
Student [name=张三, sex=男]
Student [name=李四, sex=男]

事实上,如果我们在testObj()方法中,在s2=s1代码后面,调用s2.setName("王五"); 就会发现stu1的值已经发生了改变。而stu2的值仍没有任何变化。这恰说明了上面的观点。运行结果如下:

Student [name=王五, sex=男]   // testObj方法中打印 s2的结果
Student [name=王五, sex=男]   // main方法中打印 stu1的结果
Student [name=李四, sex=男]   // main方法中打印stu2的结果

猜你喜欢

转载自blog.csdn.net/m0_37564404/article/details/80272351