Java中的字节码:Java源代码经过虚拟机编译器编译后产生的文件(即扩展为.class的文件),它不面向任何特定的处理器,只面向虚拟机。
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
反射就是把java类中的各种成分映射成一个个的Java对象
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型)
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了
(具体可查看源码,或查看Java api详解)
所有类型的Class对象
Class c1 = Object.class; //类
Class c2 = Comparable.class; //接口
Class c3 = String.class; //一维数组
Class c4 = int[][].class; //二维数组
Class c5 = Override.class; //注解
Class c6 = ElementType.class; //枚举
Class c7 = Integer.class; //基本数据类型
Class c8 = void.class; //void
Class c9 = Class.class; //Class
//只要元素类型与维度一致,就是同一个Class
int[] a = new int[10];
int[] b = new int[100];
int[][] c = new int[10][10];
System.out.println(a.getClass().hashCode());
System.out.println(b.getClass().hashCode());
System.out.println(c.getClass().hashCode());
加载,是指Java虚拟机查找字节流(查找.class文件),并且根据字节流创建java.lang.Class对象的过程。这个过程,将类的.class文件中的二进制数据读入内存,放在运行时区域的方法区内。然后在堆中创建java.lang.Class对象,用来封装类在方法区的数据结构。
类加载阶段:
(1)Java虚拟机将.class文件读入内存,并为之创建一个Class对象。
(2)任何类被使用时系统都会为其创建一个且仅有一个Class对象。
(3)这个Class对象描述了这个类创建出来的对象的所有信息,比如有哪些构造方法,都有哪些成员方法,都有哪些成员变量等。
链接包括验证、准备以及解析三个阶段。
(1)验证阶段。主要的目的是确保被加载的类(.class文件的字节流)满足Java虚拟机规范,不会造成安全错误。
(2)准备阶段。负责为类的静态成员分配内存,并设置默认初始值。
(3)解析阶段。将类的二进制数据中的符号引用替换为直接引用。
初始化,则是为标记为常量值的字段赋值的过程。换句话说,只对static修饰的变量或语句块进行初始化。
如果初始化一个类的时候,其父类尚未初始化,则优先初始化其父类。
如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行。
具体说法如下:
执行类构造器
当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化虚拟机会保证一个类的
虚拟机会保证一个类的
public class test03 {
public static void main(String[] args) {
A a = new A();
System.out.println(a.m);
}
/*
1.加载到内存,会产生一个类对应的Class对象
2.链接,结束后 m = 0;
3.初始化
() {
System.out.println("A类静态代码块初始化");
m = 300;
m = 100;
}
m = 100;
*/
}
class A {
static {
System.out.println("A类静态代码块初始化");
m = 300;
}
static int m = 100;
public A() {
System.out.println("A类无参构造初始化");
}
}
当虚拟机启动,先初始化main方法所在的类
new一个类的对象
调用类的静态成员(除了final常量)和静态方法
使用java,lang.reflect包的方法对类进行反射调用
当初始化一个类,如果其父类未被初始化,则先会初始化其父类
public class test04 {
static {
System.out.println("Main类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
//1.主动引用
Son son = new Son();
//反射也会产生主动引用
Class.forName("cn.com.reflection.Son");
//不会产生类的引用的方法
//子类引用父类的值
System.out.println(Son.n);
//数组只是开辟一个内存空间,也不会初始化
Son[] a = new Son[5];
//引用常量也不会初始化
System.out.println(Son.M);
}
}
class Father {
static int n = 2;
static {
System.out.println("父类被加载");
}
}
class Son extends Father {
static {
System.out.println("子类被加载");
m = 300;
}
static int m = 100;
static final int M = 1;
}
类加载器作用是用来把类(class)装载进内存的。JVM规范定义了如下类型的类加载器。
负责java-classpath或-d java.class.path所指的目录下的类与jar包装入工作库,是最常用的加载器。
负责jre/lib/ext或-d java.ext.dirs所指的目录下的类与jar包装入工作库
用c++编写的,是jvm自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取。
自定义加载器—>System Classloader—>Extension Classloader—>Bootstap Classloader(从左至右对应从底到顶)
自底向上检查类是否已装载
自顶向底尝试加载类
自顶向底尝试加载类时,会检查是否存在了该类的包,如果已存在就不会向下加载子类的包。
双亲委派机制保证了安全性。避免创建一些如Java核心类库中同样的命名。