java的值传递和引用传递(详细)

本文记录了java中的值传递和引用传递,String的两种赋值方式的一些学习要点。


形参传递

  • java中的形参传递都是单向传递,传递的是原变量的副本
  • 在方法中改变的是副本的值,而不是原变量的

值传递和引用传递

  • java语言中只有值传递(call by value),但是java的方法参数中有两种类型——基本数据类型和对象引用。 其实当传递object时,有人认为是call by reference,其实传递的是这个reference的副本
  • 传递基本数据类型时,不改变原变量的内容和地址

例子如下:

        public class TestCall { 
        static void add(int a,int b){  //测试值传递  
            a = a+1;  
            b=3;  
            System.out.println("方法中的a: "+a);        
        }  
        public static void main(String []args){  
            int a = 0;  
            int b=0;  
            add(a,b);  
            System.out.println("a: "+a);  
            System.out.println("b: "+b);  
        }
        }

输出:
方法中的a: 1
a: 0
b: 0        
  • 传递对象和引用类型时,不改变原变量的地址,但可以改变原变量的内容

因为当副本的引用改变时,原变量的引用并没有改变,但副本引用指向的是原变量的地址空间,所以当副本改变内容时,原变量的内容就发生了改变。


关于String


String与int 的相互转换的方法

1、String转化成int

    int i = Integer.parseInt(String); //1
    int i = Integer.valueOf(s).intValue();//2

注意:字符串转化为Double、Float或Long的方法大同小异

第一种方法直接使用静态方法,不产生多余对象,但会抛出异常
第二种方法中Integer.valueOf(s)相当于new Integer(Integer.parseInt(s)),也会抛出异常,但会多产生一个对象

2、int转化为String

    String s = String.valueOf(i); //1
    String s = Integer.toString(i);//2
    String s = ""+i;//3

注意:Double、Float或Long转化为字符串的方法大同小异

第三种方法会产生两个对象,第一种方法直接使用String的静态方法,不会产生多余的对象


创建String对象的两种方法


到底创建几个对象,两种方法的区别?

首先来看一个例子:

        public class TestCall { 
        public static void main(String []args){

        String a = "abc";//1
        String b= a;//2
        String c = "abc"; //3
        String d = new String("abc");//4
        String e = new String("eee");//5

        //比较引用指向的地址空间是否相等,即字符串所在的地址
        System.out.println(a==b); //true
        System.out.println(a==c); //true
        System.out.println(a==d); //false

        //比较引用指向的空间的内容是否相等,即字符串的内容
        System.out.println(a.equals(b)); //true
        System.out.println(a.equals(c)); //true
        System.out.println(a.equals(d)); //true

        /*说明:
         * 1、创建了一个字符串常量对象“abc”。
         * 具体:创建String对象的引用a,检查常量池里有没有“abc”,
         * 发现没有,把“abc”放入常量池中,把引用指向这一块空间
         * 
         * 2、赋值操作。创建String对象的引用b,将b指向a所指向的地址,即常量池中的“abc”
         * 
         * 3、没有创建对象。创建String对象的引用c,检查常量池里有没有“abc”,
         * 发现有,直接把引用指向这一块空间
         * 
         * 以上三个步骤后,a b c其实都是指向字符串常量池中的“abc”
         * 
         * 4、new创建了一个对象。创建String对象的引用d,检查常量池里有没有“abc”,
         *  如果有,用new在堆中申请一块空间,将字符串池中的“abc”复制给该空间,再把引用d指向该空间
         * 
         * 5、创建了两个对象,一个是字符串常量“eee”,一个是new创建的。
         * 具体:创建String对象的引用d,检查字符串常量池里有没有“abc”,
         * 发现没有,首先在堆中申请一块匿名空间存放“eee”字符串对象,
         * 然后new String把这个字符串对象拷贝一份到堆中,返回该字符串对象(把引用d指向堆中的字符串),
         * 此时那块匿名空间成了内存垃圾
         * 
         * 4、5说明使用new创建字符串时,可能会创建1或2个对象
         * 
         * */
            }
        }

注意:关于产生几个对象的进一步说明

再看一个例子:
String str = “a”+”b”;产生几个对象?
答案是3个,字符串常量区存储”a”,”b”,”ab”三个对象

String str = “a”+new String(“b”);产生几个对象?
答案是3个,字符串常量区存储”a”,”b”,堆中存储new String(“b”)的对象。

声明:

只有使用引号包含文本的方式创建的String对象之间使用“+”连接产生的新对象才会被加入字符串池中。对于所有包含new方式新建对象(包括null)的“+”连接表达式,它所产生的新对象都不会被加入字符串池中



堆还是栈?

上面这个例子并没有说明String str是存放在堆中还是栈中。
事实上,关键点是区分str是成员变量还是局部变量,如果是局部变量在方法体内,它就存储在栈中,如果是成员变量那么就跟随成员对象存储在堆中。

比如:

        class A{  
        String str = new String("abc") //  str是成员变量,存放堆中  
        public void getA(){  
        return str;
        }  
        }  

        class A{  
        public void getA(){  
        String str = new String(“abc”)  //str是局部变量,存放栈中 
        return str;  
        }  
        }


总结


  • 对于基本类型参数,在方法体内对参数进行重新赋值,并不会改变原有变量的值。

  • 对于引用类型参数,在方法体内对参数进行重新赋予引用,并不会改变原有变量所持有的引用。

  • 方法体内对参数进行运算,不影响原有变量的值。

  • 方法体内对参数所指向对象的属性进行操作,将改变原有变量所指向对象的属性值。

也就是说,对于基本数据类型,实现的是传值,只是个形参,不会改变原有值。对于引用数据类型,对这个引用进行操作,其实也是相当于对形参的操作,不会改变原来的引用。但是,当对这个引用的属性进行操作的时候,相当于CPP中的传址调用,可以改变这个引用的属性的值。

举个例子:

    public class Main {  

    private static void getMiddleOne(boolean b, Boolean boo, Boolean[] arr){  
        b = true;  //形参,不会改变原有值  
        boo = new Boolean(true);  //引用变量的直接操作相当于值传递,不会改变原来的引用变量  
        arr[0] = true;  //引用变量的属性的操作,会改变原有引用的属性,相当于传址调用  
    }  
    //测试  
    public static void main(String[] args) {  
        boolean b = false;  
        Boolean boo = new Boolean(false);  
        Boolean[] arr = new Boolean[]{false};  

        getMiddleOne(b, boo, arr);  

        System.out.println(b);    
        System.out.println(boo.toString());  
        System.out.println(arr[0]);  

        /** 
         * output: 
         *      false 
         *      false 
         *      true 
         */  
    }  
}  


创建了几个对象的深入探讨

(可参考文章:String,到底创建了多少个对象?

更多的了解

Java中String类两种实例化的区别(图解)
JAVA中String类以形参传递到函数里面,修改后外面引用不能获取到更改后的值
在java方法中改变传递的参数的值
String,到底创建了多少个对象?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值