在java.lang.String.toCharArray
的JDK源代码中,它不使用Arrays.copyOf
来实现此功能,它说:
由于类初始化顺序问题,无法使用Arrays.copyOf
什么是“类初始化顺序问题”?
public char[] toCharArray() {
// Cannot use Arrays.copyOf because of class initialization order issues
char result[] = new char[value.length];
System.arraycopy(value, 0, result, 0, value.length);
return result;
}
答案 0 :(得分:43)
我对此做了一个测试。以下是我所做的工作:
String.java
的源代码。修改其toCharArray
方法以使用Arrays.copyOf
。
public char[] toCharArray() {
// Cannot use Arrays.copyOf because of class initialization order issues
/*char result[] = new char[value.length];
System.arraycopy(value, 0, result, 0, value.length);
return result;*/
return Arrays.copyOf(value, value.length);
}
编译此修改后的String
,并将其保存回JRE的rt.jar。
编写一个简单的HelloWorld Java代码。
编译&使用java
程序运行代码。
最后,我明白了:
PS D:\> & 'C:\Program Files (x86)\Java\jdk1.8.0_121\jre\bin\java.exe' StringTest
Error occurred during initialization of VM
java.lang.NullPointerException
at java.util.Hashtable.remove(Hashtable.java:491)
at java.lang.System.initProperties(Native Method)
at java.lang.System.initializeSystemClass(System.java:1166)
我们可以看到确实存在initialization
错误。因为System.initProperties
是本机方法,我无法检查其代码。
但是,我们可以猜测为什么会发生这种情况:
System.initProperties
在初始化系统属性时需要处理一些字符串。String.toCharArray
从这些字符串中获取char数组。Arrays.copyOf
,但此时&这次,Arrays
尚未加载/初始化。class initializing request
(我不确定这个,请告诉我,如果我错了!!),哪个将导致NullPointerException
并使VM退出。2018.04.10更新。
我想感谢@Radiodef的暗示。但是当我尝试使用C ++代码时,我被许多执行路径所阻止,如果不运行或调试OpenJDK的JVM,我无法处理这些路径。
然后,我改变了策略。我做了一些基于上面的测试,我做了几天。
这一次,我不打算将Arrays.copyOf
与String.toCharArray
一起使用,相反,我试图找出在JVM初始化时哪些代码会调用toCharArray
方法。
所以我修改String
,向它添加两个静态变量:
public static int count;
public static Throwable[] logs = new Throwable[10000];
其中count
用于计算toCharArray
的调用,logs
用于保留这些调用的堆栈跟踪。
在toCharArray
方法中:
public char[] toCharArray() {
if (count < logs.length) {
try {
throw new RuntimeException();
} catch (Throwable e) {
logs[count] = e;
}
}
count++;
// Cannot use Arrays.copyOf because of class initialization order issues
char result[] = new char[value.length];
System.arraycopy(value, 0, result, 0, value.length);
return result;
}
完成后,我再次编译String
并将其保存回rt.jar。
然后,我编写了一个测试程序来打印count
和调用堆栈跟踪:
Class<String> clazz = String.class;
Field count = clazz.getDeclaredField("count");
System.out.println(count.getInt(null));
Field logs = clazz.getDeclaredField("logs");
Throwable[] arr = (Throwable[]) logs.get(null);
for (Throwable e : arr) {
if (e != null)
e.printStackTrace(System.out);
}
我们无法直接在代码中访问String.count & String.logs
,因为编译器(javac
)无法识别这些字段并会导致编译错误。这就是为什么我使用reflect-way来做到这一点。
运行我们刚刚编写的程序,结果将是:
525
java.lang.RuntimeException
at java.lang.String.toCharArray(String.java:2889)
at sun.nio.cs.ext.GBK.initb2c(Unknown Source)
at sun.nio.cs.ext.GBK.newDecoder(Unknown Source)
at java.lang.StringCoding$StringDecoder.<init>(Unknown Source)
at java.lang.StringCoding$StringDecoder.<init>(Unknown Source)
at java.lang.StringCoding.decode(Unknown Source)
at java.lang.String.<init>(String.java:414)
at java.lang.String.<init>(String.java:479)
at java.lang.System.initProperties(Native Method)
at java.lang.System.initializeSystemClass(Unknown Source)
......
java.lang.RuntimeException
at java.lang.String.toCharArray(String.java:2889)
at sun.net.www.ParseUtil.encodePath(Unknown Source)
at sun.misc.URLClassPath$FileLoader.getResource(Unknown Source)
at sun.misc.URLClassPath.getResource(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)
多长的调用列表。然而,它比以前的测试更清晰。我们可以清楚地看到哪些类调用了toCharArray
。