liuxiaoshui
发布于 2023-12-07 / 17 阅读
0
0

Java 9为何要将String的底层实现由char[]改成了byte[]

Question:

public static void main(String[] args) {

String s = new String("abc");

// 在这中间可以添加N⾏代码,但必须保证s引⽤的指向不变,最终将输出变成abcd

System.out.println(s);

}

其实是想问:如何在不改变s引用的指向的情况下,将输出变成abcd的方法。考虑通过反射。

package com.alau;

import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        String s = new String("abc");
        System.out.println(System.identityHashCode(s));

// 在这中间可以添加N⾏代码,但必须保证s引⽤的指向不变,最终将输出变成abcd
        Field value = s.getClass().getDeclaredField("value");
        value.setAccessible(true);
        value.set(s, "abcd".toCharArray());
        System.out.println(s);


        /*String s = new String("abc");
        // 使用反射机制修改String对象的值
        try {
            // 获取String类中的value字段
            Field valueField = String.class.getDeclaredField("value");
            // 设置该字段为可访问的
            valueField.setAccessible(true);
            // 获取s对象中的value属性的值
            char[] value = (char[]) valueField.get(s);
            // 改变value所引用的数组中的第4个字符
            value[3] = 'd';
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 输出修改后的字符串
        System.out.println(s); // 输出abcd*/

    }
}

执行结果:

Exception in thread "main" java.lang.IllegalArgumentException: Can not set final [B field java.lang.String.value to [C
	at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
	at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
	at java.base/jdk.internal.reflect.UnsafeQualifiedObjectFieldAccessorImpl.set(UnsafeQualifiedObjectFieldAccessorImpl.java:83)
	at java.base/java.lang.reflect.Field.set(Field.java:780)
	at com.alau.Test.main(Test.java:13)

这与预期不符。

进到String类一看,怎么成byte[]了。因为新项目更新了jdk11.所以我环境都是11了。

切回jdk1.8可以正常执行。

D:\dev\tools\jdk1.8.0_131\bin\java.exe -javaagent:D:\dev\tools\idea\ideaIU-2023.1.win\lib\idea_rt.jar=54875:D:\dev\tools\idea\ideaIU-2023.1.win\bin -Dfile.encoding=UTF-8 -classpath D:\dev\tools\jdk1.8.0_131\jre\lib\charsets.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\deploy.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\ext\access-bridge-64.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\ext\cldrdata.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\ext\dnsns.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\ext\jaccess.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\ext\jfxrt.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\ext\localedata.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\ext\nashorn.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\ext\sunec.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\ext\sunjce_provider.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\ext\sunmscapi.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\ext\sunpkcs11.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\ext\zipfs.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\javaws.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\jce.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\jfr.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\jfxswt.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\jsse.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\management-agent.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\plugin.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\resources.jar;D:\dev\tools\jdk1.8.0_131\jre\lib\rt.jar;D:\workspaces\demo\ReflectDemo\ReflectDemo\target\classes com.alau.TestReflect
abcd

Process finished with exit code 0

对比了下jdk11和jdk8的String类

jdk8

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

...
}

jdk11

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {


  
    @Stable
    private final byte[] value;
	
    /**
     * The identifier of the encoding used to encode the bytes in
     * {@code value}. The supported values in this implementation are
     *
     * LATIN1
     * UTF16
     *
     * @implNote This field is trusted by the VM, and is a subject to
     * constant folding if String instance is constant. Overwriting this
     * field after construction will cause problems.
     */
    private final byte coder;//LATIN1或者UTF16
    static final boolean COMPACT_STRINGS;

    static {
        COMPACT_STRINGS = true;
    }

...
}

对比发现,jdk8是char[],jdk11是byte[]。这一改动是从jdk9开始,由此牵扯出下一个问题。

Java 9为何要将String的底层实现由char[]改成了byte[]的原因


评论