美文网首页
2019-04-21

2019-04-21

作者: 云师兄 | 来源:发表于2019-04-21 21:25 被阅读0次

    平台无关性

    1555814006393.png

    Java源码首先被编译成字节码,再由不同平台的JVM进行解析,Java语言在不同平台上运行不需要重新编译,Java虚拟机在执行字节码的时候,把字节码转换为具体平台上的机器码。

    反射

    示例代码如下:

    首先编写一个类:

    public class ReflectTest {
    
        private String name;
    
        public String getName(){
            return name;
        }
    
        public void publicMethod(String val){
            System.out.println("enter publicMethod:"+val);
        }
    
        private void privateMethod(String val){
            System.out.println("enter privateMethod:"+val);
        }
    }
    

    里面定义了私有属性,私有方法和公有方法。下面通过反射机制来访问这些属性和方法。

    // 1. 获取class对象
    Class rc = Class.forName("com.example.demo.ReflectTest");
    
    // 2. 创建反射类实例
    ReflectTest reflectTest = (ReflectTest) rc.newInstance();
    
    // 3. 获取类名
    System.out.println("Class Name is :"+rc.getName());
    
    // 4. getDeclaredMethod()可以获取除了父类和接口外的所有方法(只要是它自己定义的方法)
    Method privateMethod = rc.getDeclaredMethod("privateMethod",String.class);
    privateMethod.setAccessible(true);
    // 5. 执行私有方法
    privateMethod.invoke(reflectTest,"hello");
    
    // 6.getMethod()只能获取有访问权限的方法,私有方法不行
    Method publicMethod = rc.getMethod("publicMethod", String.class);
    publicMethod.invoke(reflectTest,"world");
    
    //7. 获取和修改私有属性
    Field name = rc.getDeclaredField("name");
    name.setAccessible(true);
    name.set(reflectTest,"yun");
    
    // 7.1 打印设置的属性
    Method getNameMethod = rc.getMethod("getName");
    System.out.println(getNameMethod.invoke(reflectTest));
    

    getDeclaredMethod()方法和getMethod()方法的区别在于:

    • getDeclaredMethod()可以获取除了父类和接口定义的方法以外的所有方法
    • getMethod()只可以获取有访问权限的方法,如private方法不能获取

    类的加载过程

    1555822218454.png

    ClassLoader的种类

    • BootStrapClassLoader::C++编写,加载核心库java.*

    • ExtClassLoader:Java编写,加载扩展库javax.*

    • AppClassLoader:Java编写,加载程序所在目录

    • 自定义ClassLoader:Java编写,定制化加载

    BootStrapClassLoader

    BootStrapClassLoader作为核心的类加载器,加载出核心库给应用使用,开发者一般调用不到这个类。

    ExtClassLoader

    Java编写,加载扩展库javax.*。从其源码可以看出,它只获取java.ext.dirs目录下的class文件:

    private static File[] getExtDirs() {
        String var0 = System.getProperty("java.ext.dirs");
        File[] var1;
        ......
    

    AppClassLoader

    Java编写,加载程序所在目录,也就是我们项目中自己编写生成的class文件。从源码中可以看出,它会去加载
    java.class.path目录下的class文件:

    public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
        final String var1 = System.getProperty("java.class.path");
        ......
    

    自定义ClassLoader

    在某些特殊情况下,可能需要自定义类加载器,下面是一个示例:

    首先创建一个类加载器:

    public class MyClassLoader extends ClassLoader {
        private String path;
        private String classLoaderName;
    
        public MyClassLoader(String path,String classLoaderName){
            this.path = path;
            this.classLoaderName = classLoaderName;
        }
    
        @Override
        public Class findClass(String name){
            byte[] b = loadClassData(name);
            return defineClass(name,b,0,b.length);
        }
    
        private byte[] loadClassData(String name){
            name = path+name+".class";
            InputStream in = null;
            ByteArrayOutputStream out = null;
            try{
                in = new FileInputStream(new File(name));
                out = new ByteArrayOutputStream();
    
                int i=0;
                while((i=in.read()) != -1){
                    out.write(i);
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                try{
                    out.close();
                    in.close();
                } catch (Exception e){
                    e.printStackTrace();
                }
            }
            return out.toByteArray();
        }
    }
    

    自定义类加载器最主要的就是实现findClass()这个方法,里面的逻辑就是获取class文件,将其以字节数组的形式传给defineClass()方法去执行(这个方法父类已经实现,不需要我们再去实现)。

    接下来我们在桌面上创建一个类:

    public class Test {
        static{
            System.out.println("hello world");
        }
    }
    

    并将其编译出Test.class文件,准备加载。

    最后编写测试类测试:

    MyClassLoader myClassLoader = new MyClassLoader("C:\\Users\\Administrator\\Desktop\\","myClassLoader");
    Class c = myClassLoader.loadClass("Test");
    c.newInstance();
    

    经过测试,我们自定义的类加载器已经可以正常加载class文件,并创建类的对象了,静态代码块的打印也正常显示了。

    loadClassForName的区别

    前面我们知道在反射的时候使用loadClass来加载类:

    // 1. 获取class对象
    Class rc = Class.forName("com.example.demo.ReflectTest");
    // 2. 创建反射类实例
    ReflectTest reflectTest = (ReflectTest) rc.newInstance();
    

    而我们在使用自定义类加载器的时候使用ForName来加载:

    MyClassLoader myClassLoader = new MyClassLoader("C:\\Users\\Administrator\\Desktop\\","myClassLoader");
    Class c = myClassLoader.loadClass("Test");
    c.newInstance();
    

    那使用Class.forName()myClassLoader.loadClass()这两种方式加载的区别是什么呢?

    • 使用ClassLoader.loadClass()加载时,只对类进行加载操作,即类的加载第一阶段:将class文件加载到内存中,并未对类进行初始化等其他操作,所以类的static静态代码块中代码并未得到执行
    • 而使用Class.forName()加载时,对类的加载三个节点都会执行,即加载,链接,初始化。所以类定义的静态代码块也会执行。

    类加载器的双亲委派机制

    四种类加载器相互协作。判断一个Class是否被加载过的逻辑是:首先判断自定义加载器是否已经加载过,如果没有再委派给它的父加载器去查看,一直往上进行判断。

    1555820192013.png

    为什么使用双亲委派机制去加载类:

    • 避免多个类加载器重复加载,之前某个加载器加载过就不需要再重复加载了。

    双亲委派机制体现在ClassLoader类的源码中:

        protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
        {
            synchronized (getClassLoadingLock(name)) {
                // First, check if the class has already been loaded
                Class<?> c = findLoadedClass(name);
                if (c == null) {
                    long t0 = System.nanoTime();
                    try {
                        if (parent != null) {
                            c = parent.loadClass(name, false);
                        } else {
                            c = findBootstrapClassOrNull(name);
                        }
                    } catch (ClassNotFoundException e) {
                    }
    
                    if (c == null) {
                        long t1 = System.nanoTime();
                        c = findClass(name);
                        ......
                    }
                }
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            }
        }
    

    相关文章

      网友评论

          本文标题:2019-04-21

          本文链接:https://www.haomeiwen.com/subject/cmksgqtx.html