java的引用传递



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


基础知识

java的常用内存空间

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

实例分析

class Person
{
    private String name;
    private int age;
    private static String city = "北京";
    // 构造函数
    public Person(){}
    public Person(String name, int age)
    {
        this.name = name;
        this.age = age;
    }
}

public class Test
{
    public static void main(String args[])
    {
        Person person = new Person("张三",20);
    }
}


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


java的String类

两种实例化方式

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

两种实例化的区别

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

实例代码分析

#####直接赋值

public class StringDemo
{
    public static void main(String args[])
    {
        // 直接赋值
        String str1 = "Hello";
        // 直接赋值
        String str2 = "Hello";
        // 直接赋值
        String str3 = "Hello";
        System.out.println(str1 == str2);
        System.out.println(str1 == str3);
        System.out.println(str2 == str3);
    }
}


程序运行结果:

true

true

true



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

内存分析图:



#####构造方法赋值

public class StringDemo
{
    public static void main(String args [])
    {
        // 构造方法赋值
        String str1 = new String("Hello");
        // 构造方法赋值
        String str2 = new String("Hello");
        System.out.println(str1 == str2);
    }
}


程序结果:

false



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



构造方法赋值分析

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



内存分析图:

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

字符串的内容一旦声明,则不可改变。字符串内容的更改,实际上改变的是字符串对象的引用过程,并且会伴随大量垃圾的产生



代码实例分析:

public class TestDemo
{
    public static void main(String args [])
    {
        String str = "Hello ";
        String str1 = "Hello ";
        String str2 = "Hello ";
        // str、 str1指向同一块内存空间
        System.out.println(str == str1 && str1 == str2) ;
        str += "World";
        // str和str2是否仍指向同一块内存空间
        System.out.println(str == str1)  ;
        System.out.println(str1 == str2)  ;
        System.out.println("str = " + str) ;
        System.out.println("str1 = " + str1);
    }
}


程序运行结果:

true

false

true

str = Hello World

str1 = Hello

str2 = Hello



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



内存分析图

java的引用传递

引用传递的本质是:同一块堆内存空间,同时被多个栈内存指向,不同的栈可以修改同一块堆内存空间的内容


代码实例分析


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

class Demo
{
    private int data = 10;
    public Demo(){}
    public Demo(int data)
    {
        this.data = data;
    }
    public int getData()
    {
        return this.data;
    }
}
public class TestDemo
{
    public static void main(String args [])
    {
        Demo demo = new Demo(100);
        fun(demo); // 等价于Demo temp = demo
        System.out.println(demo.getData());
    }
    public static void fun(Demo temp)// 接受引用
    {
        temp.setData(30);// 修改属性内容
    }
}


程序运行结果:

30



结果分析

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



内存关系图


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

public class TestDemo
{
    public static void main(String args [])
    {
        String str = "Hello";  // 自定义字符串
        fun(str); // 引用传递: String temp = str
        System.out.println(str);
    }
    public static void fun(String temp)
    {
        temp = "World";
    }
}


程序运行结果:

Hello



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



简单理解:

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



内存关系图


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

class Demo
{
    private String data;
    public Demo(){}
    public Demo(String data)
    {
        this.data = data;
    }
    public void setData(String data)
    {
        this.data = data;
    }
    public String getData()
    {
        return this.data;
    }
}

public class  TestDemo
{
    public static void main(String args [])
    {
        Demo demo = new Demo("Hello"); // 对象实例化
        fun(demo); // 引用传递:Demo temp = demo
        System.out.println(demo.getData());
    }
    public static void fun(Demo temp)
    {
        temp.setData("World");
    }
}


程序运行结果:

World



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

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

时间: 2024-08-06 07:36:16

java的引用传递的相关文章

Java Object 引用传递和值传递

Java Object 引用传递和值传递 @author ixenos Java中的引用传递: 除了在将参数传递给方法(或函数)的时候是"值传递",传递对象引用的副本,在任何用"="向引用对象变量赋值的时候都是"引用传递",传递对象的引用给另一个变量. 参数传递,传递引用的副本,这看起来是引用传递,实则是传递了副本,这已经是值传递的概念了: 变量赋值,传递引用,这算引用传递 Java参数传递中没有引用传递都是值传递 1.在 Java 应用程序中永

如何理解java的引用传递

1. 数组的引用传递 public class TestArray { public static void changeAry1(int[] ary){ int[] ary1 = {9,9,9}; ary = ary1; } public static void changeAry2(int[] ary){ ary[0] = 100; } public static void main(String[] args) { int[] ary = {1,2,3}; System.out.print

java中引用传递和值传递的理解

按值调用(call by value):表示方法接收的是调用者提供的值 按引用调用(call by reference):表示方法接收的是调用者提供的地址 一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值 理解: 对于java来说,是不存在引用调用的,它总是按值调用 如何理解红字?方法是得到所有参数值的一个拷贝,方法无法修改传递给它的任何变量值的内容 示例1: public static void main(String[] args) { int temp = 0;

Java中的值传递与“引用传递”

首先,Java没有 引用传递 这么一说. Java只有值传递,传递的都是值,基本数据类型传递的是值,引用类型传递的是地址值. 我来理一下这其中犹如米线跟米面绞在一起了,再跟粉丝混在一起的关系. 好的,我们来看第一个例子: public static void main(String[] args) { int a = 1; int b = 4; System.out.println("Before : a=" + a + "," + "b=" +

请注意,java中没有引用传递-----转载

1 说明:本文的适用对象为java初学者.如果有读者发现文章中有叙述不妥之处,请指正. 2 3 今天在论坛上有人提了一个关于java中调用函数时有没有引用传递的问题,可谓是吵的不可开交.有人说java只有值传递,也有人说java既有值传递也有引用传递,那么java中到底有没有引用传递呢,下面我来分析一下. 4 5 一.首先来明确一下"值传递"和"引用传递的"区别 6 7 值传递:是对所传递参数进行一次副本拷贝,对参数的修改只是对副本的修改,函数调用结束,副本丢弃,原

请注意:java中没有引用传递

说明:本文的适用对象为java初学者.如果有读者发现文章中有叙述不妥之处,请指正. 今天在论坛上有人提了一个关于java中调用函数时有没有引用传递的问题,可谓是吵的不可开交.有人说java只有值传递,也有人说java既有值传递也有引用传递,那么java中到底有没有引用传递呢,下面我来分析一下. 一.首先来明确一下"值传递"和"引用传递的"区别 值传递:是对所传递参数进行一次副本拷贝,对参数的修改只是对副本的修改,函数调用结束,副本丢弃,原来的变量不变(即实参不变)引

项目中遇到的引用传递引发的问题

最近项目部署在服务器以上出现了一个问题,就是下拉框出现了本来不应该存在在这个下拉框中的分类,领导让我和另一个实习生一起找原因在哪,下拉框中的内容是程序起来的时候加载到缓存中的,代码是这样 码表会被放到一个HashMap里,大致就是map<1001,<1,"竞赛">>  这个意思.当时我全部思维都在考虑是不是加载过程中出现了问题,甚至想到了是不是因为线程不安全,在插入当前键的时候,脏读了.但是觉得又不太可能,当前key里面多的仅仅是另一个key全部的数据,不会这么

java 引用传递和值传递

1.为什么要分值传递和引用传递: 基本类型存在在栈中,复合类型(对象)存在堆中.操作栈的速度要快于堆,且对象的复制相比基本类型不仅浪费内存而且速度比较慢. 从这里就可以看出来:对象是按照引用传递(数据库事务封装Connection对象传递的时候最能体现这点):基本类型是按照值传递. 2.那为什么String类型传递后会表现的值传递的特性:这并不是由于值传递而导致的,而是由于String类的不可变性(只读特性)导致的.String类型按照引用传递,但是一旦改变了,就变成新的对象了,原来引用指向的对

java值传递or引用传递解惑

java中的參数传递本质上仅仅有值传递,无论你传的是什么,传过去的都仅仅是一个副本而已,这个副本作为方法的局部变量保存在栈中. 1.假设參数类型为基本数据类型,改动这个值并不会影响作为參数传进来的那个变量,由于你改动的是方法的局部变量,是一个副本. 2.假设传的是一个对象的引用,也是一样的,也是一个副本,可是这个副本和作为參数传进来的那个引用指向的是内存中的同一个对象,所以你通过这个副本也能够操作那个对象.可是假设你改动这个引用本身,比方让他指向内存中的另外一个对象,原来作为參数传进来的那个引用