那你想那个构造函数为什么写 成this.id=id;如果参数类型是原始类型,那么传过来的就是这个参数的一个副本,也就是这个原始参数的值,这个跟之前所谈的传值是一样的。如果在函数中改变了副本的 值不会改变原始的值. 如果参数类型是引用类型,那么传过来的就是这个引用参数的副本,这个副本存放的是参数的地址。如果在函数中没有改变这个副本的地址,而是改变了地址中的 值,那么在函数内的改变会影响到传入的参数。如果在函数中改变了副本的地址,如new一个,那么副本就指向了一个新的地址,此时传入的参数还是指向原来的 地址,所以不会改变参数的值。你第一个是传一个对象的引用过去。 还把这个对象的值改变了。 第二个,String 自己new了个。 只在方法内有效。比如下面代码: public class Test5 { public static void main(String[] args) { List<People> list = new ArrayList<People>(); People people = new People(); people.id = 1; list.add(people); people.id = 2; list.add(people); for (People p : list) { System.out.println(p.id); } }}class People { public int id; } 你觉得会输出1,2 吗?
因为String 是不可变类。如果要想是可变的,那么就用可以使用StringBuffer。
String类里面的value private final char value[];public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; StringBuffer类里面的value public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence { //////////////////////// abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * The value is used for character storage. */ char[] value;
这是一个很基础但涉及知识面非常广的问题。这里面有三个概念: 1.java的方法传递方式是值传递的。 这句话什么含义呢?它是指针对基本类型boolean/byte/char/short/int/long/float/double则传递字面值,而针对其他Object类型,则传递该对象的引用的地址值(这里很容易让人以为是引用传递)。同时这还涉及到虚拟机对方法调用的局部变量表的使用机制,可以简单理解为,新开辟了一个内存区域用来保存方法中传入的参数值。基本类型的内存区域存着字面值,而对象存的是引用指向的地址值。2.String是不可变类。 不可变类是指该类所有与状态相关的成员变量,都是声明为final型的,如楼上所描述,String里面用于描绘字符串值的private final char value[];是final的,即他有且只有在初始化始被赋值。3.方法区的常量池和堆区的对象变量概念。 诸如String s = "123";这样的代码,实际上是java提供的语法糖,他表示的真实含义是String s = new String("123").intern(); 这句代码什么意思呢?他表示,先去方法区的常量池中搜索有没有字符串常量"123",如果有,则直接返回"123"在常量池中的引用地址;如果没有,则在方法区中扩展常量池增加"123"字符串的对象,并返回引用地址。 而String ss = new String("123");则是显式的在堆区新建对象,返回引用。如下代码 String s = new String("123").intern(); String ss = new String("123"); String sss = "123"; System.out.println(s==ss); System.out.println(s==sss); 其中==操作符表示比较引用地址。自己尝试下结果。 明确了以上三个概念。解释你的两段代码就很容易了。 public static void fun(Test t) { t.temp = "456"; } 这里的"456"在常量池中创建新的对象并返回引用,为t.temp赋了新的引用地址,指向常量区"456"。public static void main( String args[]) { String s = "123"; System.out.println(s); fun(s); System.out.println(s); } public static void fun(String t) { t = "456"; }其中fun方法中的参数String t的生命周期只在这一次方法调用。方法载入时,传参将s指向的地址值赋给了t。 fun方法同样在常量池中创建新的对象(称为c)并返回引用,但将引用地址赋给了我们这里的String t,也就是说,t的指向地址从s指向的地址值变为了c所指向的地址值。这并不影响s。所以发生了,你看到的现在这个现象。如果对第二段代码还不是很理解,可以把你第一段代码稍加改造,就能理解的非常透彻。 class Test { String temp = "123"; } public class MyClass { public static void main( String args[]) { Test t1 = new Test(); System.out.println(t1.temp); fun(t1); System.out.println(t1.temp); } public static void fun(Test t) { t. = new Test(); //仅增加这一行 t.temp = "456"; }
如果参数类型是引用类型,那么传过来的就是这个引用参数的副本,这个副本存放的是参数的地址。如果在函数中没有改变这个副本的地址,而是改变了地址中的 值,那么在函数内的改变会影响到传入的参数。如果在函数中改变了副本的地址,如new一个,那么副本就指向了一个新的地址,此时传入的参数还是指向原来的 地址,所以不会改变参数的值。你第一个是传一个对象的引用过去。
还把这个对象的值改变了。
第二个,String 自己new了个。
只在方法内有效。比如下面代码:
public class Test5 { public static void main(String[] args) { List<People> list = new ArrayList<People>(); People people = new People(); people.id = 1;
list.add(people); people.id = 2;
list.add(people); for (People p : list) {
System.out.println(p.id);
}
}}class People {
public int id;
}
你觉得会输出1,2 吗?
private final char value[];public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
StringBuffer类里面的value public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
////////////////////////
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;
1.java的方法传递方式是值传递的。
这句话什么含义呢?它是指针对基本类型boolean/byte/char/short/int/long/float/double则传递字面值,而针对其他Object类型,则传递该对象的引用的地址值(这里很容易让人以为是引用传递)。同时这还涉及到虚拟机对方法调用的局部变量表的使用机制,可以简单理解为,新开辟了一个内存区域用来保存方法中传入的参数值。基本类型的内存区域存着字面值,而对象存的是引用指向的地址值。2.String是不可变类。
不可变类是指该类所有与状态相关的成员变量,都是声明为final型的,如楼上所描述,String里面用于描绘字符串值的private final char value[];是final的,即他有且只有在初始化始被赋值。3.方法区的常量池和堆区的对象变量概念。
诸如String s = "123";这样的代码,实际上是java提供的语法糖,他表示的真实含义是String s = new String("123").intern();
这句代码什么意思呢?他表示,先去方法区的常量池中搜索有没有字符串常量"123",如果有,则直接返回"123"在常量池中的引用地址;如果没有,则在方法区中扩展常量池增加"123"字符串的对象,并返回引用地址。
而String ss = new String("123");则是显式的在堆区新建对象,返回引用。如下代码 String s = new String("123").intern();
String ss = new String("123");
String sss = "123";
System.out.println(s==ss);
System.out.println(s==sss);
其中==操作符表示比较引用地址。自己尝试下结果。
明确了以上三个概念。解释你的两段代码就很容易了。 public static void fun(Test t)
{
t.temp = "456";
}
这里的"456"在常量池中创建新的对象并返回引用,为t.temp赋了新的引用地址,指向常量区"456"。public static void main( String args[])
{
String s = "123";
System.out.println(s);
fun(s);
System.out.println(s);
}
public static void fun(String t)
{
t = "456";
}其中fun方法中的参数String t的生命周期只在这一次方法调用。方法载入时,传参将s指向的地址值赋给了t。
fun方法同样在常量池中创建新的对象(称为c)并返回引用,但将引用地址赋给了我们这里的String t,也就是说,t的指向地址从s指向的地址值变为了c所指向的地址值。这并不影响s。所以发生了,你看到的现在这个现象。如果对第二段代码还不是很理解,可以把你第一段代码稍加改造,就能理解的非常透彻。
class Test
{
String temp = "123";
}
public class MyClass
{
public static void main( String args[])
{
Test t1 = new Test();
System.out.println(t1.temp);
fun(t1);
System.out.println(t1.temp);
}
public static void fun(Test t)
{
t. = new Test(); //仅增加这一行
t.temp = "456";
}
}如此,看看打印的结果,理解了么?
相信楼主明白了。
多谢啊,明白了。你说有个常量区我就明白了,原先看书上说,String不可变可是第一个明明是改变了,现在才发现还是没变,只是在常量区新建了空间,堆区并没改变