那么a的值看似改变了,其实也是同样的误区。再次说明, a只是一个引用, 不是真正的字符串对象,在调用a.replace(‘A’, ‘a’)时, 方法内部创建了一个新的String对象,并把这个心的对象重新赋给了引用a。String中replace方法的源码可以说明问题:

  读者可以自己查看其他方法,都是在方法内部重新创建新的String对象,并且返回这个新的对象,原来的对象是不会被改变的。这也是为什么像replace, substring,toLowerCase等方法都存在返回值的原因。也是为什么像下面这样调用不会改变对象的值:
  String ss = "123456";
  System.out.println("ss = " + ss);
  ss.replace('1', '0');
  System.out.println("ss = " + ss);
  打印结果:
  ss = 123456
  ss = 123456
  String对象真的不可变吗?
  从上文可知String的成员变量是private final 的,也是初始化之后不可改变。那么在这几个成员中, value比较特殊,因为他是一个引用变量,而不是真正的对象。value是final修饰的,也是说final不能再指向其他数组对象,那么我能改变value指向的数组吗? 比如将数组中的某个位置上的字符变为下划线“_”。 至少在我们自己写的普通代码中不能够做到,因为我们根本不能够访问到这个value引用,更不能通过这个引用去修改数组。
  那么用什么方式可以访问私有成员呢? 没错,用反射, 可以反射出String对象中的value属性, 进而改变通过获得的value引用改变数组的结构。下面是实例代码:
  public static void testReflection() throws Exception {
  //创建字符串"Hello World", 并赋给引用s
  String s = "Hello World";
  System.out.println("s = " + s); //Hello World
  //获取String类中的value字段
  Field valueFieldOfString = String.class.getDeclaredField("value");
  //改变value属性的访问权限
  valueFieldOfString.setAccessible(true);
  //获取s对象上的value属性的值
  char[] value = (char[]) valueFieldOfString.get(s);
  //改变value所引用的数组中的第5个字符
  value[5] = '_';
  System.out.println("s = " + s);  //Hello_World
  }
  打印结果为:
  s = Hello World
  s = Hello_World
  在这个过程中,s始终引用的同一个String对象,但是再反射前后,这个String对象发生了变化, 也是说,通过反射是可以修改所谓的“不可变”对象的。但是一般我们不这么做。这个反射的实例还可以说明一个问题:如果一个对象,他组合的其他对象的状态是可以改变的,那么这个对象很可能不是不可变对象。例如一个Car对象,它组合了一个Wheel对象,虽然这个Wheel对象声明成了private final 的,但是这个Wheel对象内部的状态可以改变, 那么不能很好的保证Car对象不可变。