目录
程序计数器(Program Counter Register)
一、平台无关性如何实现?
compile once,run anywhere.
编译:javac指令,将源码编译成字节码,生成.class文件(字节码文件)
运行:java指令,JVM解析.class文件,加载进内存,转化成特定平台的执行指令
二、JVM如何加载.class文件?
-
Java虚拟机
虚拟机是一种抽象化的计算机,在实际计算机上仿真模拟各种计算机功能来实现的。JVM有自己完善的硬件架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。JVM屏蔽了与具体平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。JVM屏蔽底层操作系统平台的不同,并且减少基于原生语言开发的复杂性。只要虚拟机厂商在特定操作系统上实现了虚拟机,定义如何将字节码解析成本操作系统可执行的二进制码(机器码),Java便能实现跨越各种平台。JVM最值得我们学习的两点是JVM内存结构模型和GC。JVM是内存中的虚拟机,JVM的存储就是内存,类、变量、方法都在内存中。
参考:字节码和机器码的区别
-
JVM架构
ClassLoader加载javac编译好的.class文件到内存,不是任意的.class文件都能加载,ClassLoader加载的文件有格式要求。ClassLoader只管加载,只要符合格式要求就能加载,能不能运行由Execution Engine负责;Execution Engine负责对命令进行解析,解析完之后提交到操作系统执行;Native Interface融合不同编程语言为Java所用。Java执行性能绝大多数情况下没有C或者C++高,主流JVM也是基于C++实现的,涉及到一些较高性能的运算时,需要在Java直接调用它们。另外,本着不重复调用的原则,某个库如果已经用到别的语言开发,就不需要再开发一套,而是Java直接对这些库进行调用,为了满足上述要求,JVM在内存中开辟了一块区域处理标记为Native的代码,具体做法是Native Method中登记Native方法,在Execution执行时加载Native方法;程序被加载到Runtime Data Area这里运行。
三、反射
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任何一个对象,都能够调用它的方法和属性。这种动态获取信息和动态调用方法的功能称为Java语言的反射机制。
eg:
package com.reflect;
public class Person {
private String name;
public void sayHi(String str){
System.out.println(str+name);
}
private String throwHello(String name){
return "hello,"+name;
}
}
package com.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 cls=Class.forName("com.reflect.Person");
System.out.println("类名:"+cls.getName());
Person person=(Person)cls.newInstance();
/**private方法的调用*/
//getDeclaredMethod方法的第一个参数是String类型的方法名,第二个参数是方法传入的参数类型.class
//getDeclaredMethod能获取所有修饰符修饰的方法,不能获取继承的方法和实现的接口的方法
Method throwHello=cls.getDeclaredMethod("throwHello",String.class);
//私有的需要设置为true
throwHello.setAccessible(true);
//invoke方法的第一个参数是对象的实例,第二个参数是上述方法传入的参数
Object obj=throwHello.invoke(person,"Zhansan");
System.out.println("throwHello的结果:"+obj);
/**public方法的调用*/
//getMethod只能获取public方法,还能获取继承的方法和实现的接口的方法
Method sayHi=cls.getMethod("sayHi",String.class);
sayHi.invoke(person,"hi,");
/**获取private属性*/
Field name=cls.getDeclaredField("name");
name.setAccessible(true);
name.set(person,"lisi");
sayHi.invoke(person,"hi,");
}
}
执行结果为:
类名:com.reflect.Person
throwHello的结果:hello,Zhansan
hi,null
hi,lisi
反射是将Java类中的各种成分映射成一个个Java对象:Field、Method、Class...
类从编译到执行的过程:1)编译器将Person.java源文件编译为Person.class字节码文件;2)ClassLoader将字节码转换为JVM中的Class<Person>对象(类对象、Class对象,不是new A();之类的对象);3)JVM利用Class<Person>对象实例化为Person对象。
参考:Java中的Class类和Java中的java.lang.Class API 详解
四、ClassLoader类加载器
ClassLoader是一个抽象类,里面最重要的方法是loadClass。
-
ClassLoader的作用
ClassLoader在Java中有着非常重要的作用,主要工作在Class装载的加载阶段,主要作用是从系统外部获得Class二进制数据流。它是Java的核心组件,所有的Class都是由ClassLoader进行加载的,ClassLoader负责通过将Class文件里的二进制数据流装载进系统,然后交给Java虚拟机进行连接、初始化等操作。
-
ClassLoader的种类
1)BootStrapClassLoader:C++编写,加载核心库java.*
2)ExtClassLoader:Java编写,加载扩展库javax.* (在IDEA导航项Navigate下的Class中搜索ExtClassLoader可以看到ExtClassLoader类的源代码,AppClassLoader同理。BootStrapClassLoader是C++编写的,只能去JVM源码中看)
3)AppClassLoader:Java编写,加载程序所在目录(classpath)
4)自定义ClassLoader:Java编写,定制化加载
-
自定义ClassLoader的实现
关键函数
1)findClass:寻找class文件,包括读进二进制流,处理,返回一个Class对象
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
2) defineClass:定义一个类,字节码以byte[]数组形式传递进