打破双亲代理机制是否可以自己定义一个 String 类
面试的时候被问了一个问题:能否打破双亲代理机制是否可以自己定义一个String 类,如果包名和java.lang.String 一致呢?
当时一脸懵逼,这方面的知识还是缺乏啊,下来之后写了代码之后试了一下,答案是:可以
详细一点回答是:使用非 “java.* ” 开头的包名是可以的,但包名不能以 “java.* ” 开头。
类加载器
package myClassLoader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MyClassLoader extends ClassLoader {
// 类加载器名称
private String loaderName;
public MyClassLoader(String loaderName) {
// 让系统类加载器成为该 类加载器的父加载器
super();
this.loaderName = loaderName;
}
public MyClassLoader(ClassLoader parent, String loaderName) {
// 显示指定该类加载器的父加载器
super(parent);
this.loaderName = loaderName;
}
@Override
public String toString() {
return this.loaderName;
}
/**
* 获取.class文件的字节数组
*
* @param name
* @return
* @throws FileNotFoundException
*/
private byte[] loaderClassData(String name) throws IOException {
FileInputStream in = new FileInputStream(name);
byte[] b = new byte[1024];
ByteArrayOutputStream out = new ByteArrayOutputStream();
int len = 0;
while((len = in.read(b)) != -1){
out.write(b, 0, len);
}
out.close();
b = out.toByteArray();
return b;
}
/**
* 获取Class对象
*/
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
byte[] b = null;
try {
b = loaderClassData(name);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return defineClass(null, b, 0, b.length);
}
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
MyClassLoader loader1 = new MyClassLoader("MyClassLoader");
//两个自定String类,一个的包名为java.lang.String,第二个为Custom.String
//Class<?> clazz = loader1.loadClass("D:/customclass/java/lang/String.class");
Class<?> clazz = loader1.loadClass("D:/customclass/Custom/String.class");
Object obj = clazz.newInstance();
System.out.println(obj.getClass().getName());
Method m = clazz.getMethod("hello", null);
m.invoke(obj, null);
System.out.println(clazz.getName());
}
}
两个自定义string类
package java.lang;
public class String {
public void hello(){
System.out.println("I'm String class");
}
}
package Custom;
public class String {
public void hello(){
System.out.println("I'm String class");
}
}
运行结果
包名为 java.lang.String 的类
Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.lang
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.lang.ClassLoader.defineClass(Unknown Source)
at myClassLoader.MyClassLoader.findClass(MyClassLoader.java:65)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at myClassLoader.MyClassLoader.main(MyClassLoader.java:72)
包名为Custom.String的类
Custom.String
I'm String class
Custom.String
看到这里大家应该明白了吧,JVM基于安全考虑,自己写的类的package名称不可以”java. * ”开头,否则不会被类加载器加载。对于自己写java.lang.String,肯定也不会被加载成功了,如果是其它名字开头的,如Custom.String,就能正确加载