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

简介: 是否可变,String类能否被继承重点,String是不可变的!String类不能被继承!String类是不可变的。

是否可变,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);

结果: 
hello
world
  • 进一步说明时候String不可变
String s1 = "hello";
System.out.println(s1);
//将字符e替换成a,实现是替换之后,创建了一个新的String对象,并不影响s1
s1.repalce('e','a');
System.out.println(s1);

结果:
hello
hello
  • 补充说明一点,引用数据类型的引用,根据底层实现,其实是一个引用–>另一个引用–>堆区内存中保存的‘对象’

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 World
s = Hello_World
  • 在这个过程中,s始终引用的同一个String对象,但是再反射前后,这个String对象发生了变化, 正常使用String的时候,是不可能会去用到映射的,所以,这只是知识体系中的知识点,并不是编程代码的应用。这个反射的实例还可以说明一个问题:如果一个对象,他组合的其他对象的状态是可以改变的,那么这个对象很可能不是不可变对象。例如一个Car对象,它组合了一个Wheel对象,虽然这个Wheel对象声明成了private final 的,但是这个Wheel对象内部的状态可以改变, 那么就不能很好的保证Car对象不可变。
相关文章
|
6月前
|
安全
StringBuffer,StringBuilder的区别及其源码分析
StringBuffer,StringBuilder的区别及其源码分析
|
7月前
|
存储 缓存 安全
【 Java中String源码分析(JVM视角你不来看看?】
【 Java中String源码分析(JVM视角你不来看看?】
|
存储 算法 Java
String类【JDK源码分析】
String类【JDK源码分析】
66 0
|
7月前
|
Java 索引
正则表达式源码分析--三个常用类--分组、捕获、反向引用--String 类中使用正则表达式的代码示例和图
正则表达式源码分析--三个常用类--分组、捕获、反向引用--String 类中使用正则表达式的代码示例和图
87 0
|
安全 Java 编译器
Java中的String实例化、Annotation注解类、继承的多态和Object类(附带相关面试题)
1.java中String两种实例化对象2.Annotation注解类 3.继承的多态 4.Object类
128 0
Java中的String实例化、Annotation注解类、继承的多态和Object类(附带相关面试题)
|
安全 Java API
StringBuffer类【JDK源码分析】
StringBuffer类【JDK源码分析】
79 0
|
XML 设计模式 算法
Tomcat源码分析之getParameter(String)与getQueryString()
Tomcat源码分析之getParameter(String)与getQueryString()
236 0
|
存储 缓存 Java
String源码分析
首先,将一个类分为几个部分,分别是类定义(继承,实现接口等),全局变量,方法,内部类等等,再分别对这几个部分进行说明,这样到最后类的全貌也就比较直观了
182 0
|
缓存 安全 Java
Java StringBuilder和StringBuffer源码分析
StringBuilder与StringBuffer是两个常用的操作字符串的类。大家都知道,StringBuilder是线程不安全的,而StringBuffer是线程安全的。前者是JDK1.5加入的,后者在JDK1.0就有了。下面分析一下它们的内部实现。
115 0
|
存储 安全 Java
JDK源码分析之String、StringBuilder和StringBuffer
JDK源码分析之String、StringBuilder和StringBuffer
JDK源码分析之String、StringBuilder和StringBuffer