Java-类加载

Compile Once,Run Anywhere如何实现?

在这里插入图片描述

JVM如何加载.class文件

Java虚拟机

在这里插入图片描述
Class Loader : 依据特定格式,加载class文件到内存.
Runtime Data Area : JVM内存结构模型
Execution Engine : 对命令进行解析
Native Interface : 融合不同开发语言的原生库为Java所用

反射

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性〕 种动态获取信息以及动态调用对象的方法的功能称为java语言的反射机制.

package reflect;

public class Robot {
    
    
    private String name;

    public void sayHi(String helloSentence) {
    
    
        System.out.println(helloSentence + " " + name);
    }

    private String throwHello(String tag) {
    
    
        return "Hello " + tag;
    }


}

package reflect;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectSample {
    
    
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
    
    
        Class clazz=Class.forName("reflect.Robot");
        Robot robot= (Robot) clazz.newInstance();
        System.out.println("Class name is "+clazz.getName());
        //私有方法调用
        //获取类中声明的方法
        Method getHello=clazz.getDeclaredMethod("throwHello", String.class);
        getHello.setAccessible(true);
        Object str=getHello.invoke(robot,"Tom");
        System.out.println("getHello result is"+str);

        //获取所有公共的方法
        Method sayHi=clazz.getMethod("sayHi", String.class);
        sayHi.invoke(robot,"Welcome");

        //获取声明的属性
        Field name=clazz.getDeclaredField("name");
        name.setAccessible(true);
        name.set(robot,"Alice");
        sayHi.invoke(robot,"Welcome");
    }
}

类从编译到执行的过程

  • 编译器将Robot.java源文件编译为Robot.class字节码对象
  • ClassLoader将字节码转换为JVM的Class对象
  • JVM利用Class对象实例化为Robot对象

ClassLoader

ClassLoader在java中有着非常重要的作用,它主要工作在Class装载阶段,其主要作用是从系统外部获得Class二进制数据流.它是Java的核心组件,所有的Class都是由ClassLoader进行加载的,ClassLoader负责通过将Class文件里的二进制数据流装载进系统,然后交给Java虚拟机进行连接,初始化等操作.

ClassLoader的种类

BootStrapClassLoader : C++编写,加载核心库java.*
ExtClassLoader : Java编写,加载扩展库javax.*
AppClassLoader : Java编写,加载程序所在目录.
自定义ClassLoader : Java编写,定制化加载

自定义ClassLoader的实现

关键函数
findClass
defineClass

在工程外定义一个类.并使用javac编译为class文件

public class Wali {
    
    
	static{
    
    
		System.out.println("Hello, Wali!");
	}
}

自定义类加载器

package customclassloader;

import java.io.*;

//自定义类加载器
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 (FileNotFoundException e) {
    
    
            e.printStackTrace();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            try {
    
    
                out.close();
                in.close();
            } catch (Exception e) {
    
    
                e.printStackTrace();
            }
        }
        return  out.toByteArray();
    }
}

编写测试类

package customclassloader;

public class ClassLoaderChecker {
    
    
    public static void main(String[] args) throws Exception {
    
    
        MyClassLoader m=new MyClassLoader("D:\\MyData\\","myClassLoader");
        Class c=m.loadClass("Wali");
        System.out.println(c.getClassLoader());
        c.newInstance();//触发静态代码块
    }
}

类加载器的双亲委派机制

在这里插入图片描述

  1. 自下向上检查类是否已加载
  2. 自上向下尝试加载类

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

  • 避免多份同样字节码的加载

类的加载方式

  • 隐式加载:new
  • 显式加载:loadClass,forName

loadClass和forName的区别

  • Class.forName得到的class是已经初始化完成的
  • Classloder.loadClass得到的class是还没有链接的.

猜你喜欢

转载自blog.csdn.net/yasuofenglei/article/details/104218137