面试题:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
答:是值传递。Java 编程语言只有值传递参数。当一个对象实例作为一个参数被传递到方法中时,参数的值就是该对象的引用的一个副本。指向同一个对象,对象的内容可以在被调用的方法中改变,但对象的引用(不是引用的副本)是永远不会改变的。
在Java语言中基本类型和String都是标准的值传递,而对象(包括数组)也是值传递,不过他们传递的值是地址的副本,无论形参如何变化,这个地址是不会改变的,改变的是这个地址所存储的内容。
在Java中,当基本类型(包括String)作为参数传入方法时,无论该参数在方法内怎样被改变,外部的变量原型总是不变的。
这就叫做“值传递”,即方法操作的是参数变量(也就是原型变量的一个值的拷贝)改变的也只是原型变量的一个拷贝而已,而非变量本身。所以变量原型并不会随之改变。
对于一个对象作为形式参数时,传递的其实是这个实参的地址值,所以形参内容改变,实参的内容也就改变了,因为他们指向同一个地址,有个特别要注意的地方,如果形参中有两个对象参数A,B,在方法中把B=A,实际是把A的地址赋值给了B,这时候形参B如果发生内容改变,实际是改变的A地址的内容,与实参B的地址内容就没有关系了:可以参考下面的例子:
static void aMethod(StringBuffer sf1, StringBuffer sf2) {
sf1.append(sf2);
sf2 = sf1;
sf2.append("s");
}
public static void main(String[] args) {
StringBuffer sf1 = new StringBuffer("A");
StringBuffer sf2 = new StringBuffer("B");
Test.aMethod(sf1, sf2);
System.out.println(sf1 + ":" + sf2);
}
输出结果为 :ABs:B
=============================================================================================
String s1="accp" ;
String s2="accp"
Java内部将此语句转化为以下几个步骤:
(1)先定义一个名为s1的对String类的对象引用变量:String s1;
(2)在栈中查找有没有存放值为"accp"的地址,如果没有,则开辟一个存放字面值为"accp"的地址,接着创建一个新的String类的对象o,并将o 的字符串值指向这个地址,而且在栈中这个地址旁边记下这个引用的对象o。如果已经有了值为"accp"的地址,则查找对象o,并返回o的地址。所以当栈中存在你想创建的变量的值的时候,其实是引用已经存在栈中的地址,不是新建的。
所以s1==s2为true
String str = "hello" 如果之前有String对象是hello的值的话那str直接就指向之前的那个对象了,不再重新new一个对象了
String str = new String("hello");无论以前有没有都重新new一个新的
==========================new String()创建几个对象关问问题=================================
- String str=new String("aaa");
这行代码究竟创建了几个String对象呢?答案是2个,而不是3个。由于new String("aaa")相当于"aaa"与一个就是创建出来的放在堆时原实例对象,而另一个就是放在常量池中的 "aaa" 对象,当然这里的str本身只是一个引用,放在栈里,用来指向堆中创建出来的对象。
常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。
1. String str="aaa";
只创建1个对象。这里涉及到字符串常量池,在JVM中有一个字符串池,它用来保存很多可以被共享的String对象,这样如果我们在使用同样字面字符串时,它就使用字符串池中同字面的字符串。当然我们可以使用String对象的intern()方法来访问String对象在字符串池中所对应的常量对象。
上面这行代码被执行的时候,JVM先到字符串池中查找,看是否已经存在值为"aaa"的对象,如果存在,则不再创建新的对象,直接返回已存在对象的引用;如果不存在,则先创建这个对象,然后把它加入到字符串池中,再将它的引用返回。
1. String str1="aaa";
2. String str2="aaa";
也只创建1个对象。能过上面的解释这个就更清楚了,在执行第二行代码时,aaa字符串对象在池中已存在,所以直接返回池中已存在的那个字符串对象。
1. String str="aaa"+"bbb";
还是只创建1个对象。由于常量字符串是在编译的时候就也被确定的,又因"aaa"和"bbb"都是常量,因此变量str的值在编译时就可以确定。这行代码编译后的与String str="aaabbb";是一样的,这与我们平时好像不太一样啊?一般使用“+”连接两个字符串都会产生另一个新的字符对象。下面我们看一下例子就明白了:
1. String str1 = "aaa";
2. String str2 = "bbb";
3. String str3 = "aaabbb";
4.
5. String str4 = "aaa" + "bbb";//不会产生新的字符串对象
6. System.out.println(str3 == str4);//true
7.
8. str4 = str1 + "bbb";//会产生新的字符串对象
9. System.out.println(str3 == str4);//false
10.
11. str4 = str1 + str2;//会产生新的字符串对象
12. System.out.println(str3 == str4);//false
从上面例子我们就可以得出:使用“+”连接的两个字符串本身就是字面常量字符串时,如果池中存在这样连接后的字符串,则是不会重新创建对象,而是直接引用池中的字符串对象;如果“+”连接的两字符串中只要有一个不是字面常量串(即定义过的),是会产生新的字符串对象。
凡事也有例外,这个也不例外:如果“+”连接的字符串中两个或一个不是“字面常量”,但如果定义成常量字符串时,情况又有变化:
1. final String str1 = "aaa";
2. final String str2 = "bbb";
3. String str3 = "aaabbb";
4.
5.
6. String str4 = str1 + str2;
7. System.out.println(str3 == str4);//true
但如果先定义final字符串,但未在定义处初始化,而初始化在块中,如下:
- //此时str1与str2相当于变量,而不是常,因为块是在运行时才能确定,在编译时不能确定
1. final static String str1;
2. final static String str2;
3. static {
4. str1 ="aaa";
5. str2 ="bbb";
6. }
7. public static void main(String[] args){
8. String str3 = str1 + str2;
9. String str4 ="aaabbb";
10. System.out.println(str3==str4); //输出为false
11. }
12. String str=" ";与String str=new String();
13. str=" "会放入池中,但new String()不会放入池中