一、Java只有按值调用
-
对象变量和引用的关系:
在Java中,任何对象变量的值都是对存储在另外一个地方的一个对象的引用。new操作符的返回值也是一个引用。下列语句:
Date deadline = new Date();
有两个部分。表达式new Date()构造了一个Date类型的对象,并且它的值是对新创建对象的引用。这个引用存储在变量deadline中。
一定要认识到:一个对象变量并没有实际包含一个对象,而仅仅引用一 个对象。简单来说,对象变量在栈空间中存储的内容是存储在堆内存空间中的该对象的地址,这就是上面提到的对对象的(地址空间的)引用。
-
按值调用和按引用调用的含义:
按值调用:表示方法接受的是调用者提供的值。
按引用调用:表示方法接受的是调用者提供的变量地址。
Java程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝。
而C++提供了以上两种参数传递的方式。先学C++的表示被迷惑了很久…
个人理解:Java传递方法中传递的是参数值的一个拷贝,而不是参数值的地址的拷贝。
因此在方法中传递对象变量时,实际传递的是对象变量的值,即存储在堆内存空间中的对象的地址。
所以实参和形参始终引用同一个对象的前提条件是形参没有主动改变其内容(堆内存中对象的地址)。 -
当形参主动改变了其内容时,实参和形参就不能再画上等号了。
//传入的是p对象在堆内存中的地址的副本
Person p = new Person("Jack");
testPerson(p);
System.out.println("main name = " + p.getName()); //Jack
private static void testPerson(Person p) {
p = new Person("Rose");
System.out.println("testPerson name = " + p.getName());
}
3/11更新,再补一个例子
1、for(double d:ds)
d = 100 * Math.random(); //基础类型的值传递,数组的内容没变
2、for(int i = 0; i < ds.length; i++)
ds[i] = Math.random() * 100; //正确更改
最后引用书上的总结:
一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)。
一个方法可以改变一个对象参数的状态。
一个方法不能让对象参数引用一个新的对象。
二、String是final类
String、StringBuilder、StringBuffer
-
String
字符串常量,final类型,因此String无法被继承,内部设计的原型是final类型的字符数组,且在String类中没有暴露出该数组,因此数组的内容即String的内容在创建后不能更改,每次值的改变都会生成一个新的String对象。
再结合上面提到的Java传递的是值,当String对象作为实参传给形参后,若在方法内将形参的值更改了,形参将引用另一个String对象,而不会影响实参。在编译期确认的String字符串保存在常量池,而运行时创建出来的,保存在堆内存空间中。
String s1 = "hello";
String s2 = "hello"; //s1==s2 true,都位于常量池
String s1 = "hello";
String s2 = new String("hello"); //false s2保存在堆内存
String s1 = "helloWorld";
//编译器直接在编译期将拼接表达式转换成s2
String s2 = "hello" + "World"; //true
String s1 = "helloWorld";
String s2 = "hello";
//等价于s2 = new StringBuilder(“hello”).append(“World”).toString()
s2 += "World"; //false,s2保存在堆内存
-
StringBuffer
字符串变量,每次都是在现有的对象上进行操作,不会产生新对象,对字符串内容频繁改变时可使用StringBuffer,更改内容的方法是append和insert方法。不存在类似于String的直接赋值的方法。线程安全
-
StringBuilder
和StringBufffer方法和功能相同,非线程安全,效率高
-
使用策略:
对于字符串拼接次数少的可直接使用String +,对于循环中字符串拼接必须使用StringBuilder/StringBuffer,避免每次都生成新的StringBuilder对象。
结语:感谢您的阅读!如有错误之处,烦请指出,再次感谢,祝工作顺心,生活愉快~