旁门左道——修改Java核心类

真的不能自定义String类吗?

Posted by     "Connor" on Tuesday, December 10, 2019

TOC

可以自定义修改java的String类吗?

这应该是一道经典的面试题,我以前的回答也无非这两点,都是从自定义类加载器入手的:

  • SecurityCheck(SecurityException)限制。
  • 自定义类加载器正常必须使用defineClass——>defineClass1,但是defineClass1是个native方法,绕过java命名空间限制也不能自定义加载方式,除非你能修改这个native实现。

但是真的不能对核心类库进行修改吗?

我最近学习了一个新的知识点,可以对其进行修改,这就是指定bootclasspath启动参数的方式,指定优先加载我们自己的类即可。

示例

本示例在java8环境下,java9的机制已经大改了,文后会分析。

这里没有直接修改String类是因为main方法参数使用了,实验起来会不方便,所以修改StringBuilder来展示效果。

其实因为修改String类编译能通过,运行时报找不到main方法,至于原因还未探究。

代码如下:

└─experiment
    │  TestClass.class
    │
    └─java
        └─lang
                StringBuilder.class

StringBuilder.java

package java.lang;

public final class StringBuilder
        extends AbstractStringBuilder
        implements java.io.Serializable, CharSequence  {
	public int lastIndexOf(String str) {
		System.out.println(str);
        return 1;
    }
    ... 省略其他方法
}

TestClass.java

import java.lang.StringBuilder;

public class TestClass {
	public static void main(String[] args) {
		StringBuilder builder = new StringBuilder();
        int i = builder.lastIndexOf("Success Modify StringBuilder");
		System.out.println(i);
    }
}

编译运行

❯ javac .\StringBuilder.java
❯ javac .\TestClass.java
❯ java -Xbootclasspath/p:. TestClass
Success Modify StringBuilder
1
# 指定新的 bootclasspath,替换 java.* 包的内部实现
java -Xbootclasspath:<your_boot_classpath> your_App
 
# a 意味着 append,将指定目录添加到 bootclasspath 后面,后执行
java -Xbootclasspath/a:<your_dir> your_App
 
# p 意味着 prepend,将指定目录添加到 bootclasspath 前面,先执行
java -Xbootclasspath/p:<your_dir> your_App

Java9以上由于引入模块化系统的机制修改为给模块打patch

确认要修改的类文件已经编译好,并按照对应模块(假设是 java.base)结构存放, 然后,给模块打补丁:

java --patch-module java.base=your_patch yourApp

参考文章

  1. 请介绍类加载过程,什么是双亲委派模型?