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();//触发静态代码块
}
}
类加载器的双亲委派机制
- 自下向上检查类是否已加载
- 自上向下尝试加载类
为什么要使用双亲委派机制去加载类
- 避免多份同样字节码的加载
类的加载方式
- 隐式加载:new
- 显式加载:loadClass,forName
loadClass和forName的区别
- Class.forName得到的class是已经初始化完成的
- Classloder.loadClass得到的class是还没有链接的.