Welcome to the public number [sharedCode] committed to mainstream middleware source code analysis, personal website: www.shared-code.com/

Java.lang.String class can be loaded from the Internet to find an article, write very concise, here to share with you

public class MyClassLoader extends ClassLoader{
    public Class findClass(String name){
        byte[] b = null;
        try {
            b = getByte(name);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return defineClass(null, b, 0, b.length);
    }
    private byte[] getByte(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();
        returnb; }}Copy the code

Test class (path1,path2 is where I put the compiled class file) :

public class MyClassLoaderTest {
    public static void main(String [] args) throws Exception{
        MyClassLoader myloader = new MyClassLoader();
        String path1 = "F:/Software/java/jdk/lib/String.class";
        String path2 = "E:/JavaTest/String.class";
        Class c = myloader.findClass(path1);
        //Class c = myloader.findClass(path2);
        Object obj = c.newInstance();
        System.out.println(obj.getClass().getName());
        Method m = c.getMethod("say".null);
        m.invoke(obj, null); }}Copy the code

The following shows several String classes written by myself with different package and other contents the same, compiled in the above path1,path2 respectively.

package java.lang;
    public class String{
        public void say(a){
        System.out.println("I'm String class"); }}Copy the code

The main method is thrown:

Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.lang
Copy the code

If String is written like this (note that package only changes) :

package java;
    public class String{
        public void say(a){
        System.out.println("I'm String class"); }}Copy the code

Throw exceptions are as follows:

Exception in thread "main" java.lang.SecurityException: Prohibited package name: java
Copy the code

Change the package of String to the following

package java.haha;
    public class String{
        public void say(a){
        System.out.println("I'm String class"); }}Copy the code

Or throw an exception:

Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.haha
Copy the code

Note that the three String classes are just different packages. From the above exceptions, the JVM does not allow us to use packages that start with “Java” ourselves. Now look at the following two ways of writing package:

(don’t write package)

public class String{
        public void say(a){
        System.out.println("I'm String class"); }}Copy the code

(Package does not start with “Java”)

package lang.java;
    public class String{
        public void say(a){
        System.out.println("I'm String class"); }}Copy the code

The output of the two writing methods is as follows:

String
I'm String class
Copy the code

At this point, my own String class is successfully loaded by my own class loader, and through reflection, it executes its say() method.

Conclusion:

  • The package name of a self-written class cannot start with “Java”, otherwise it will not be loaded by the class loader. (So java.lang.String that you write will not be successfully loaded, even if you write your own class loader.)
  • Classes whose package does not start with “Java” can be loaded successfully. However, it does not matter if the name of the String class is “String”, “Ergou”, or “ZhangSan”, it is just a very common name, because the package is different, the String class does not conflict with java.lang.String.

Conclusion:

At this point, we know whether our java.lang.String can be loaded. Can’t! Lang.java. String, haha.String, zhangsan.string… Are all strings whose package does not start with “Java”. At this point, he is not that he…

Extension:

Why does the custom class loader still fail to load system classes

public class MyClassLoader extends ClassLoader{
    public Class findClass(String name){
        byte[] b = null;
        try {
            b = getByte(name);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // That's the point
        return defineClass(null, b, 0, b.length); }}Copy the code

The above class inherits ClassLoader and calls the parent class’s defineClass method. We can look at the implementation of the ClassLoader method

protected finalClass<? > defineClass(String name,byte[] b, int off, int len,
                                         ProtectionDomain protectionDomain)
        throws ClassFormatError
    { 
        // Call preDefineClass before class loading.protectionDomain = preDefineClass(name, protectionDomain); String source = defineClassSourceLocation(protectionDomain); Class<? > c = defineClass1(name, b, off, len, protectionDomain, source); postDefineClass(c, protectionDomain);return c;
    }
Copy the code

PreDefineClass is called for validation before class loading. The preDefineClass method imposes strict restrictions on the name of the class

 /* Determine protection domain, and check that: - not define java.* class, - Signer of this class matches signers for the rest of the classes in package. Verify the signature */
    
    private ProtectionDomain preDefineClass(String name, ProtectionDomain pd)
    {
        if(! checkName(name))throw new NoClassDefFoundError("IllegalName: " + name);

        // Note: Checking logic in java.lang.invoke.MemberName.checkForTypeAlias
        // relies on the fact that spoofing is impossible if a class has a name
        // of the form "java.*"
        if((name ! =null) && name.startsWith("java.")) {
            throw new SecurityException
                ("Prohibited package name: " +
                 name.substring(0, name.lastIndexOf('. ')));
        }
        if (pd == null) {
            pd = defaultDomain;
        }

        if(name ! =null) checkCerts(name, pd.getCodeSource());

        return pd;
    }
Copy the code

This method verifies two points:

  1. Classes beginning with Java.* cannot exist
  2. The signer of this class does not match the signature of any other class in the package

The ultimate conclusion

  1. You can define a java.lang.String class, but the system loader will not load your class. Instead, it will load a String from the JDK, so all methods will not be available

  2. Implement your own classloader to try to load your own java.lang.String,

First you have to put it in another path, otherwise the parent delegate mechanism will still load its own system classes

If the parent delegate is broken, an exception is thrown in defineClass and Java is not allowed to be defined. At the beginning of class

So there is no way to load your own java.lang.String anyway