String不可变不可被继承源码分析

来源:互联网 发布:阿里云域名解析到主机 编辑:程序博客网 时间:2024/06/07 18:54

是否可变,String类能否被继承

  • 重点,String是不可变的!String类不能被继承!
  • String类是不可变的。一个String实例,创建完成后就无法改变。
  • 能改变的只是String的引用,状态,对象内的成员变量都无法改变,无论是基本数据类型还是引用数据类型。

区分对象和对象引用

//一个字符串,s1这个是一个存放物理地址的引用对象,s1———》指向 "hello"String s1 = "hello";System.out.println(s1);//这里实际情况是新创建了一个字符串“world”, s1————》指向改变"world"s1 = "world"System.out.println(s1);结果: helloworld
  • 进一步说明时候String不可变
String s1 = "hello";System.out.println(s1);//将字符e替换成a,实现是替换之后,创建了一个新的String对象,并不影响s1s1.repalce('e','a');System.out.println(s1);结果:hellohello
  • 补充说明一点,引用数据类型的引用,根据底层实现,其实是一个引用–>另一个引用–>堆区内存中保存的‘对象’

String类的final

  • String 类在 JDK 7 源码
//可以看出 String 是 final 类型的,表示该类不能被其他类继承。//String 字符串是常量,其值在实例创建后就不能被修改,但字符串缓冲区支持字符串的引用的改变public final class String implements java.io.Serializable,      Comparable<String>, CharSequence{...}
  • String的成员变量
  • 这是一个字符数组,并且是 final 类型,用于存储字符串内容。 fianl 关键字修饰,一旦被创建,其值无法改变。
    String 其实是基于字符数组 char[] 实现的。
/**The value isused for character storage.*/private final char value[];

看上去像改变String的方法其实无法改变String

  • String的replace(char oldChar, char newChar)方法。
public String replace(char oldChar, char newChar) {        if (oldChar != newChar) {            int len = value.length;            int i = -1;            char[] val = value; /* avoid getfield opcode */            while (++i < len) {                if (val[i] == oldChar) {                    break;                }            }            if (i < len) {                char buf[] = new char[len];                for (int j = 0; j < i; j++) {                    buf[j] = val[j];                }                while (i < len) {                    char c = val[i];                    buf[i] = (c == oldChar) ? newChar : c;                    i++;                }                //这行代码是关键,返回的是一个新的String。                return new String(buf, true);            }        }        return this;    }
  • 其他方法的内部都是重新创建新的String对象,并且返回这个新的对象,原来的对象无法改变。这也是replace, substring,toLowerCase等方法都存在返回值的原因。

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 Worlds = Hello_World
  • 在这个过程中,s始终引用的同一个String对象,但是再反射前后,这个String对象发生了变化, 正常使用String的时候,是不可能会去用到映射的,所以,这只是知识体系中的知识点,并不是编程代码的应用。这个反射的实例还可以说明一个问题:如果一个对象,他组合的其他对象的状态是可以改变的,那么这个对象很可能不是不可变对象。例如一个Car对象,它组合了一个Wheel对象,虽然这个Wheel对象声明成了private final 的,但是这个Wheel对象内部的状态可以改变, 那么就不能很好的保证Car对象不可变。
阅读全文
1 0
原创粉丝点击