如果尝试使用反射将值设置为String类的值[]字段,则Java 6和Java 7会产生不同的结果

Tin*_*iny 2 java string reflection java-6 java-7

以下代码尝试使用Java 7将值设置为private final char value[]String类的字段.

package test;

import java.lang.reflect.Field;

public final class Test 
{
    static
    {
        try
        {
            Field value = String.class.getDeclaredField("value");
            value.setAccessible(true);
            value.set("Hello World", value.get("1234567890"));
        }
        catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e)
        {
            System.out.println(e.toString());
        }
    }

    public static void main(String[] args) 
    {
        System.out.println("Hello World");
    }
}
Run Code Online (Sandbox Code Playgroud)

它默默地显示1234567890在控制台上,毫无疑问.


当我尝试使用Java 6做同样的事情,如下所示,

package test;

import java.lang.reflect.Field;

public final class Test
{
    static
    {
        try
        {
            Field value = String.class.getDeclaredField("value");
            value.setAccessible(true);
            value.set("Hello World", value.get("1234567890"));
        }
        catch (IllegalArgumentException e)
        {
            System.out.println(e.toString());
        }
        catch (IllegalAccessException e)
        {
            System.out.println(e.toString());
        }
        catch (NoSuchFieldException e)
        {
            System.out.println(e.toString());
        }
        catch (SecurityException e)
        {
            System.out.println(e.toString());
        }
    }
    public static void main(String[] args)
    {
        System.out.println("Hello World");
    }
}
Run Code Online (Sandbox Code Playgroud)

它会导致抛出以下异常.

线程"main"java.lang.ArrayIndexOutOfBoundsException中的异常

value.get("1234567890")此语句中的长度value.set("Hello World", value.get("1234567890"));大于或等于String 时,它可以工作Hello World

例如,

如果以下语句(如前面的代码片段中所示)

value.set("Hello World", value.get("1234567890"));
Run Code Online (Sandbox Code Playgroud)

由以下内容替换

value.set("Hello World", value.get("12345678901"));
Run Code Online (Sandbox Code Playgroud)

那么,当方法的第二个参数的长度小于第一个参数时,为什么这不适用于Java 6(或者可能更低,我没试过)set()

顺便说一句,我可以理解,不建议以这种方式处理带有反射的私有字段,这是最糟糕的.

Jon*_*eet 5

那么当set()方法的第二个参数的长度小于第一个参数时,为什么这不适用于Java 6(或者可能更低,我没试过)?

在Java 6中,您正在设置将value字符数组设置为新引用 - 但您不会更改指定字符串引用的部分的其他字段char[].

我不记得确切的字段名称,但它是这样的:

char[] value;
int offset;
int count;
Run Code Online (Sandbox Code Playgroud)

因此,对于"覆盖"整个字符数组的字符串,offset将为0并且count将是value.length.现在如果value更短的 替换char[],但不改变count,则offset + count超出数组的末尾......它超出了数组的范围.这样做是为了使操作像substring不需要复制字符数据 - 它们只是创建一个新对象,该对象引用现有数组,具有不同的偏移/计数值.

在Java 7中,从更新5开始,字符串没有此偏移/计数概念; 相反,每个字符串都有自己的字符数组,开头和结尾都是隐式的.我怀疑这就是为什么它在Java 7中适合你的原因.