java中自定义String会怎样?

为了验证java的双亲委派模型模型,小编我决定自己做个试验。

包名和java自身String所在的java.lang包名不一致时的情况

首先,我们自定义一个包名不是java.lang的String(注意如果包名不是java.lang直接将java自定的String拷贝过来会报错,所以为了省去麻烦,此处小编没有使用从官方拷贝过来的String)


package com.school.eution.accommodation;

public final class String {
    /** The value is used for character storage. */
    private final char value[] = {};

    /** Cache the hash code for the string */
    private int hash; // Default to 0

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;

    public String(int hash) {
        this.hash = hash;
    }

    public String(){

    }

    static{
        System.out.println("静态--自定义String");
    }

    {
        System.out.println("动态--自定义String");
    }
    public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

}

测试代码中自定义了自己的类加载器,去加载自定义com.school.eution.accommodation.String

package com.school.eution.accommodation;
 
import java.io.IOException;
import java.io.InputStream;

public class Test {
    public static void main(java.lang.String[] args) {
        ClassLoader myLoad = new ClassLoader() {

            @Override
            public Class<?> loadClass(java.lang.String name)throws ClassNotFoundException {
                java.lang.String fileName = name.substring(name.lastIndexOf(".")+1)+".class";
                InputStream is = getClass().getResourceAsStream(fileName);
                if (null == is) {
                    return super.loadClass(name);
                }
                try {
                    byte[] b= new byte[is.available()];
                    is.read(b);
                    return defineClass(name,b,0,b.length);
                } catch (IOException e) {
                    throw new ClassNotFoundException();
                }

            }
        };
        try {
            Object obj = myLoad.loadClass("com.school.eution.accommodation.String").newInstance();
            System.out.println(obj.getClass());
            // 这个obj使用的是自定义的classLoad 与 虚拟机自带的不是一个类加载器,所以返回false
            System.out.println(obj instanceof String);
            System.out.println();
        } catch (InstantiationException | IllegalAccessException
                | ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

输出结果:

静态--自定义String
动态--自定义String
class com.school.eution.accommodation.String
false

分析:小编使用了自定义的类加载器,如果自定义String和java自带的String不在同一个包下是可以加载的,而且我们看到了自定义String代码块的输出。

 

自定义String包名也叫java.lang时的情况

1、自定义String在自己起的名字也叫java.lang包下,测试用例在另一个包下

自定义String中加了代码块标识(这里之所以使用从官方拷贝来的String,是因为编写自己的类加载器的时候会用到String中的一些方法,所以使用的从官方拷贝过来的String):

测试用例: 

总结:发现测试结果中并没有打印出自己String代码块中的标识。原因就在于双亲委派机制,用户自定义的加载器会将加载请求向上委派给父类加载器,如果父类加载器还有自己的父加载器,就继续向上委派。最终会由最顶层的加载器执行本次请求。顶层的加载器就是启动类加载器(Bootstrap ClassLoader),启动类加载器会在自己的加载范围内(比如rt.jar包)下查找java.lang.String(原本的String就是在rt.jar包下)。此时在rt.jar包下找到了java.lang.String进行加载,所以自己写的String虽然也在自定的包java.lang下也没用呀。除非父类加载器在自己的加载范围内找不到要加载的类,才会反过来向下让子类加载器加载。

 

2、测试用例和自定义String都在java.lang包下

首先,拷贝java自带的String放到自己的java.lang包下,然后在自己拷贝的String中加上代码块标识,以便测试的时候能和java自带的String区别开来。(这里之所以使用从官方拷贝来的String,是因为编写自己的类加载器的时候会用到String中的一些方法,所以使用的从官方拷贝过来的String)

package java.lang;
public final class String
        implements java.io.Serializable, Comparable<String>, CharSequence {


    static {
        System.out.println("静态代码块--自定义String");
    }

    {
        System.out.println("一般代码块--自定义String");
    }

    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;

    /**
     * Class String is special cased within the Serialization Stream Protocol.
     *
     * A String instance is written into an ObjectOutputStream according to
     * <a href="{@docRoot}/../platform/serialization/spec/output.html">
     * Object Serialization Specification, Section 6.2, "Stream Elements"</a>
     */
    private static final ObjectStreamField[] serialPersistentFields =
            new ObjectStreamField[0];

    /**
     * Initializes a newly created {@code String} object so that it represents
     * an empty character sequence.  Note that use of this constructor is
     * unnecessary since Strings are immutable.
     */
    public String() {
        this.value = "".value;
    }
    //由于String代码太多,此处省略
    // ....




}

测试用例:

package java.lang;
 
import java.io.IOException;
import java.io.InputStream;

/**
 * 类加载器与instanceof关键字演示
 *
 * @author pocher
 */
public class ClassLoaderTest {
	
	public static void main(String[] args) {
		ClassLoader myLoad = new ClassLoader() {

			@Override
			public Class<?> loadClass(String name)throws ClassNotFoundException {
				String fileName = name.substring(name.lastIndexOf(".")+1)+".class";
				InputStream is = getClass().getResourceAsStream(fileName);
				if (null == is) {
					return super.loadClass(name);
				}
				try {
					byte[] b= new byte[is.available()];
					is.read(b);
					return defineClass(name,b,0,b.length);
				} catch (IOException e) {
					throw new ClassNotFoundException();
				}
				
			}
		};
		
		try {
			Object obj = myLoad.loadClass("java.lang.String").newInstance();
			System.out.println(obj.getClass());
			// 这个obj使用的是自定义的classLoad 与 虚拟机自带的不是一个类加载器,所以返回false
			System.out.println(obj instanceof ClassLoaderTest);
			System.out.println();
		} catch (InstantiationException | IllegalAccessException
				| ClassNotFoundException e) {
			e.printStackTrace();
		}
			
	}
	
}

测试结果:

java.lang.SecurityException: Prohibited package name: java.lang
	at java.lang.ClassLoader.preDefineClass(ClassLoader.java:662)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:761)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" Disconnected from the target VM, address: '127.0.0.1:56457', transport: 'socket'

点击第一行的错误发现是这样官方定义的:这个包名受保护。

 /* Determine protection domain, and check that:
        - not define java.* class,
        - signer of this class matches signers for the rest of the classes in
          package.
    */
    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;
    }

总结:自己虽然可以自定义一个叫java.lang.String的类的,但是因为java会检查包名,如果在java.lang包下自定义一个String类,并在改包下启动测试不管有没有用到String,只要在自定义的java.lang包下启动就会报“java.lang.SecurityException: Prohibited package name: java.lang”的错。但是如果启动类和自定义String不在同一个包下是可以的。但是这样的话,除了类名和java自带的String一样外,就和普通类没啥区别了。就算代码完全和官方的String代码一直,但由于双亲委派模型的存在也没有任何鸟用呀,又不会加载自定的String(即便改自定义String的包名也叫java.lang)


 

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值