1 对Java的理解
1. 平台无关性 :一次编译,到处运行
2 .GC垃圾回收 :垃圾回收机制,不用手动的释放堆内存。
3. 语言特性 :包括泛型、反射、lambda表达式
4.面向对象 :封装、继承、多态
5 .类库 :集合、网络库等
6.异常处理
2 Compile Once,Run Anywhere 如何实现(平台无关性)
过程:
编译时:用Javac 指令,将Java源码编译生成字节码,并存入到 .class 文件中。
在终端进行编译如下:Javac 会对源码进行语法检查、校验等
在调用Javac指令后。就将Java文件编译成了Java.class文件。Java.class文件保存的就是Java源码翻译成的二进制字节码,也就是说Java类文件中的属性、方法以及 类中的常量信息都会被分别存储在 .class 文件中,在 .class 文件中还会添加一个共有的静态常量属性 .class, 这个属性记录了类的相关信息,即类型信息,是class的一个实例。
之后在调用Java 指令运行,该指令让JVM解析字节码文件,并将其加载到内存,最后转换成操作系统能识别的机器码。
通过指令JavaP -C 来对 .class文件进行反编译,查看字节码
总结:
Java源码首先被编译成字节码,再由不同平台的JVM进行解析,Java语言在不同的平台上运行时不需要重新编译,Java虚拟机在执行字节码文件时,把字节码文件转换成具体平台上的机器指令。
问题:为什么JVM不直接将源码解析成机器码去执行?
1.如果解析成机器码。那么每次执行的时候都需要各种检查。
2.兼容性:生成中间的字节码,也可以将别的语言解析成字节码,兼容新更高。
3 JVM如何加载 . class 文件
JVM的架构(JVM 是内存中的虚拟机)
Class Loader : 依据特定的格式,加载Class文件到内存。
Execution Engine(解释器):,对加载到内存中的class文件的命令进行解析。(解析class文件中的字节码,并提交给操作系统去执行)
Native Interface(本地接口):融合不同开发语言的原生库为Java所用。
Runtime Data Area:JVM内存空间结构模型 (所写的程序都会被加载到这里)
4 反射
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类所有属性和方法;对于任意一个对象,都能够调用他的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。
写一个反射的例子
1 编写一个robot类 ,包含私有成员变量、公有方法、私有方法。
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;
}
}
2, 用反射来获取robot类中的属性和方法
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 {
//forname 来获取类
Class rc = Class.forName("Reflect.Robot");
//newInstance 来创建类的实例
Robot r=(Robot) rc.newInstance();
System.out.println("class name is "+rc.getName());
//getDeclareMethod 获取Robot类声明的throwhello方法.getDeclareMethod 不能获取类继承、实现的方法
Method gethello=rc.getDeclaredMethod("throwhello", String.class);
//setAccessible要设置为true, 默认是false,同时因为throwhello方法是私有的
gethello.setAccessible(true);
//invoke 执行指定的方法 ,传入的参数分别为实例r,和throwhello方法中需要的参数
Object str=gethello.invoke(r,"bob");
System.out.println("gethello result is "+str);
//getmethod 只能获取类中public的方法 还可以获取继承和实现的方法
Method sayhi=rc.getMethod("sayHi", String.class);
//没用定义robot的name 因此输出为welcome null
sayhi.invoke(r, "welcome");
//获取类的属性
Field name=rc.getDeclaredField("name");
name.setAccessible(true);
name.set(r,"Alice");
sayhi.invoke(r,"welcomr");//welcomr Alice
}
}
总结: 反射就是把Java类中的一个个成分映射成Java对象。
1 . 通过Class.forName获取指定路径的Class对象。
2. 通过newInstance创建对象实例
3. 通过getMethod可以获取类中的所有public方法(包含继承和实现的方法)
4 通过getDeclaredMethod 获取指定对象的方法(不包含继承和实现的方法)
5 通过setAccessible 设置true,获取私有方法。
6 通过Class.getField 获取类中的字段属性,进行赋值。
7 通过invoke 执行指定方法。
5 Class Loader
问题: 类从编译到执行的过程,以Robot类为例?
1. 编译器将 Robot. java 源文件编译为 Robot.class 字节码文件。(通过Javac指令)
2. ClassLoader 将字节码转换为JVM中的Class<Robot>对象 (ClassLoader 获取的字节码是以byte数组的形式传递过来的)
3 JVM 利用Class<Robot>对象实例化为Rotbot 对象。
ClassLoader
ClassLoader 在Java中具有非常重要的作用,它主要工作在Class 装载的加载阶段,其主要作用是从系统外部获得Class二进制数据流。它是Java的核心组件,所有的Class都是有ClassLoader进行加载的,ClassLoader负责通过将Class文件里的二进制数据流装载进系统,然后交给Java虚拟机进行连接、初始化等操作。
ClassLoader 的种类
1. BootStrapClassLoader: C++ 编写,加载核心库Java.*(自带核心库,由JVM内核实现)
2 ExtClassLoader: java 编写,加载扩展库Javax.*
3 AppClassLoader:Java编写,加载程序所在目录
4 Custom ClassLoader (自定义的): Java编写,定制化加载。
自定义ClassLoader的实现:
关键函数:
6 ClassLoader的双亲委派机制
问题:ClassLoader双亲委派机制的加载过程:
1 . 自底而上检查类是否已经被加载
Custom ClassLoader -----APP ClassLoader ---- Extension Classloader----BootStrap ClassLoader
2 自下而上尝试加载类
BootStrap :加载 jre/lib/rt.jar 或者Xbootclasspath 选项指定的jar包
Extension:加载jre/lib/ext/*.iar 或者 D Java.ext.dirs 指定目录下的jar包
APP: j加载ClassPath 或者D Java.calss.path 所指定的目录下的类和jar包
问题: 为什么要使用双亲委派机制去加载类?
避免多份同样字节码的加载
7 类的加载方式
1. 隐私加载:new
2 . 显示加载:LoadClass 、 forName等
类的装载过程:(ClassLoader的装载过程)
1 加载: 通过ClassLoader 加载class文件字节码。生成Class对象
2 链接: 校验:检查加载的class的正确性和安全性。
准备:为类变量分配存储空间并设置类变量的初始值
解析:JVM将常量池内的符号引用转化为直接引用。
3 初始化 :执行类变量赋值和静态代码块
问题: LoadClass 和forNamede 区别?
1.Class.forName 得到的class是已经初始化完成的
2. ClassLoader.LoadClass 得到的class是还没有链接的、
例 :我们首先定义一个带静态代码块的类,我们知道只有类在初始化之后才会执行静态代码块
接着我们使用LoadClass去加载这个类:发现什么都没有打印出来,也就是说类的静态代码块没有被执行
然后我们使用forName来加载这个类,发现静态代码块被执行了,说明完成了初始化