开发文章

java的引用传递

最近看着李兴华讲师的Java视频教程学习java,关于java引用传递方面的知识的总结。


基础知识

java

  1. 栈内存空间:保存所有的对象名称(更准确地说是保存了引用的堆内存空间的地址)
  2. 堆内存空间:保存具体对象的具体属性内容。
  3. 全局数据区:保存static类型的属性
  4. 全局代码区:保存所有的方法定义

实例分析

复制内容到剪贴板
  1. class Person  
  2. {  
  3.     private String name;  
  4.     private int age;  
  5.     private static String city = "北京";  
  6.     // 构造函数  
  7.     public Person(){}  
  8.     public Person(String name, int age)  
  9.     {  
  10.         this.name = name;  
  11.         this.age = age;  
  12.     }  
  13. }  
  14.   
  15. public class Test  
  16. {  
  17.     public static void main(String args[])  
  18.     {  
  19.         Person person = new Person("张三",20);  
  20.     }  
  21. }  

以上产生的person对象的内存关系如下图:

内存关系.png

javaString

两种实例化方式

  1. 直接赋值: String str = “Hello”;
  2. 构造方法赋值 String str = new String(“Hello”);

  • 直接赋值:只开辟一块内存空间,字符串内容可以自动入池,供下次使用。
  • 构造方法赋值:开辟两块内存空间,有一块将成为垃圾,并且不能自动入池,需要使用intern()手动入池。

实例代码分析

#####直接赋值

复制内容到剪贴板
  1. public class StringDemo  
  2. {  
  3.     public static void main(String args[])  
  4.     {  
  5.         // 直接赋值  
  6.         String str1 = "Hello";  
  7.         // 直接赋值  
  8.         String str2 = "Hello";  
  9.         // 直接赋值  
  10.         String str3 = "Hello";  
  11.         System.out.println(str1 == str2);  
  12.         System.out.println(str1 == str3);  
  13.         System.out.println(str2 == str3);  
  14.     }  
  15. }  

程序运行结果:
true
true
true


由程序执行结果可知:str1、str2、str3 3个字符串的内存地址完全相同,也就是说,实际上只开辟了一段堆内存空间。
内存分析图:

 

内存分析图.png

#####构造方法赋值

复制内容到剪贴板
  1. public class StringDemo  
  2. {  
  3.     public static void main(String args [])  
  4.     {  
  5.         // 构造方法赋值  
  6.         String str1 = new String("Hello");  
  7.         // 构造方法赋值  
  8.         String str2 = new String("Hello");  
  9.         System.out.println(str1 == str2);  
  10.     }  
  11. }  

程序结果:
false


由程序执行结果可知:str1、str2、 2 个字符串的内存地址不相同,也就是说,使用构造方法实例化的String类对象内容不会保存在字符串对象池中,即不能狗进行共享数据操作。


构造方法赋值分析
由于每一个字符串都是一个String类的匿名对象,所以首先会在堆内存中开辟一段内存空间保存字符串”Hello”,而后又使用关键字new开辟了另一块内存空间,并把之前定义的字符串常量的内存空间的内容赋给new开辟的空间,而此时之前定义的字符串常量的内存空间将不会有任何栈内存指向,就成成为垃圾,等待垃圾收集器(GC)不定期回收。


内存分析图:

内存分析图.png

由上述的结论还可以知道:


代码实例分析:

复制内容到剪贴板
  1. public class TestDemo  
  2. {  
  3.     public static void main(String args [])  
  4.     {  
  5.         String str = "Hello ";  
  6.         String str1 = "Hello ";  
  7.         String str2 = "Hello ";  
  8.         // str、 str1指向同一块内存空间  
  9.         System.out.println(str == str1 && str1 == str2) ;  
  10.         str += "World";  
  11.         // str和str2是否仍指向同一块内存空间  
  12.         System.out.println(str == str1)  ;  
  13.         System.out.println(str1 == str2)  ;  
  14.         System.out.println("str = " + str) ;  
  15.         System.out.println("str1 = " + str1);  
  16.     }  
  17. }  

 

程序运行结果:
true
false
true
str = Hello World
str1 = Hello
str2 = Hello


由程序执行结果可知,开始str、str1和str2指向同一块堆内存空间,改变str(连接”World”)之后,str指向的堆内存空间发生改变,而原str所指的堆内存空间的内容没有发生改变。


内存分析图

内存分析图.png

java

引用传递的本质是:


代码实例分析


范例一(自定义类对象作为函数参数传递)

复制内容到剪贴板
  1. class Demo  
  2. {  
  3.     private int data = 10;  
  4.     public Demo(){}  
  5.     public Demo(int data)  
  6.     {  
  7.         this.data = data;  
  8.     }  
  9.     public int getData()  
  10.     {  
  11.         return this.data;  
  12.     }  
  13. }  
  14. public class TestDemo  
  15. {  
  16.     public static void main(String args [])  
  17.     {  
  18.         Demo demo = new Demo(100);  
  19.         fun(demo); // 等价于Demo temp = demo  
  20.         System.out.println(demo.getData());  
  21.     }  
  22.     public static void fun(Demo temp)// 接受引用  
  23.     {  
  24.         temp.setData(30);// 修改属性内容  
  25.     }  
  26. }  

程序运行结果:
30


结果分析
本程序首先在主方法中实例化了一个Demo对象,同时为类中的data属性赋值为100,之后将类对象传递给fun()方法由于类本身属于引用数据类型,所以fun()方法中的修改直接影响原始对象的内容。


内存关系图

内存关系图.png

范例二(String类对象作为函数参数传递)

复制内容到剪贴板
  1. public class TestDemo  
  2. {  
  3.     public static void main(String args [])  
  4.     {  
  5.         String str = "Hello";  // 自定义字符串  
  6.         fun(str); // 引用传递: String temp = str  
  7.         System.out.println(str);  
  8.     }  
  9.     public static void fun(String temp)  
  10.     {  
  11.         temp = "World";  
  12.     }  
  13. }  

程序运行结果:
Hello


通过程序运行结果可以发现,由于String类的内容不可变,所以当修改字符串数据(temp = “World”;)时就发生一个引用关系的变更,temp将指向新的堆内存空间。由于temp数据是方法的局部变量,所以方法执行完毕后,原始的str对象内容并不会发生任何改变。所以使用String类作为引用操作类型操作,关键是:String



可以把String类看成基本数据类型。由于基本数据类型本身不牵扯到内存关系,而且传递时也只是值传递,不是内存地址传递,这样的特点是:方法里不管做何种修改,都不会影响原数据的内容。

内存关系图.png

 

 

范例三(包含String类属性的自定义类作为函数参数传递)

复制内容到剪贴板
  1. class Demo  
  2. {  
  3.     private String data;  
  4.     public Demo(){}  
  5.     public Demo(String data)  
  6.     {  
  7.         this.data = data;  
  8.     }  
  9.     public void setData(String data)  
  10.     {  
  11.         this.data = data;  
  12.     }  
  13.     public String getData()  
  14.     {  
  15.         return this.data;  
  16.     }  
  17. }  
  18.   
  19. public class  TestDemo  
  20. {  
  21.     public static void main(String args [])  
  22.     {  
  23.         Demo demo = new Demo("Hello"); // 对象实例化  
  24.         fun(demo); // 引用传递:Demo temp = demo  
  25.         System.out.println(demo.getData());  
  26.     }  
  27.     public static void fun(Demo temp)  
  28.     {  
  29.         temp.setData("World");  
  30.     }  
  31. }  

程序运行结果:
World


本程序和范例一从本质上将没有本质上的区别,唯一的区别在于本次使用了String作为Demo类的属性。如果把String类看成基本数据类型,可以得到如下内存分析图:

内存分析图.png

 

但实际上,更完整的内存关系应该表示为“Demo对象(栈)中包含了String的引用(data是String的名字存储在栈,而字符串的内容则存储在堆),Demo对象的堆内存中保存着data(栈内存)的引用关系”,完整的内存关系图如下:

完整的内存关系图.png

感谢 lym152898 支持 磐实编程网 原文地址:
blog.csdn.net/lym152898/article/details/54411956

文章信息

发布时间:2017-01-15

作者:lym152898

发布者:aquwcw

浏览次数: