Day8 Reflection 反射机制
uwupu 啦啦啦啦啦

Java Reflection 反射机制

静态语言 动态语言

动态语言

是一类在运行时可以改变其结构的语言。

即:在运行时代码可以根据某些条件改变自身结构。

主要动态语言:Object-C、C#、JavaScript、PHP、Python等。

静态语言

运行时结构不可变的语言就是静态语言,如Java,C,C++。

Java不是动态语言,但可以称为“准动态语言”。可以通过反射机制获得类似动态语言的特性。

Java Reflection

Reflection反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

1
Class c = Class.forName("java.lang.String")

加载完类之后,在堆内存的方法区就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。通过这个对象可以看到类的结构。

透过这个对象看到类结构的过程,称为反射

功能

在运行时:

  1. 判断任意一个对象所属的类;
  2. 构造任意一个类的对象;
  3. 判断任意一个类所具有的成员变量和方法;
  4. 获取泛型信息;
  5. 调用任意一个对象的成员变量和方法;
  6. 处理注解;
  7. 生产动态代理;

优点 缺点

优点:可以实现动态创建对象和编译;

缺点:这类操作慢于直接执行的操作。

主要API

  • java.lang.Class 代表一个类

  • java.lang.Method 代表类的方法

  • java.lang.Field 代表类的成员变量

  • java.lang.Constructor 代表类的构造器

  • ……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Test1 {
public static void main(String[] args) throws ClassNotFoundException {
//通过反射获取类的class对象
Class c1 = Class.forName("Demo2_Reflection.User");
System.out.println(c1);//获取类
Class c2 = Class.forName("Demo2_Reflection.User");
System.out.println(c1==c2);//true 每个类只有一个Class对象

Method[] methods = c1.getMethods();//获取类的方法
for (Method i : methods){
System.out.println(i.getName());
}
/*
toString
getName
getId
...
*/
}
}

//一个实体类 : 一般用POJO或entity表示
class User{...}//getter setter constructor ...

Class类

Class是Java的一个类。

  • Class对象只能由系统建立对象。
  • 一个加载的类在JVM中只会有一个Class实例;
  • 一个Class对象对应的是一个加载到JVM中的一个class文件;
  • 通过Class可以完整地得到一个类中的所有被加载的结构

常用方法

方法名功能
static ClassforName(String name)返回指定类名name的Class对象
Object newInstance()调用缺省构造函数,返回Class对象的一个实例。
getName()返回Class对象表示的实体的名称
Class getSuperClass()返回当前Class对象分类的Class对象。
Class[] getinterfaces()获取当前Class对象的接口
ClassLoader getClassLoader()返回该类的类加载器
Constructor[] getConstructors()返回一个包含某些Constructor对象的数组。
Method getMethod(String name,Class ... T)返回Method对象,形参类型为paramType
Field[] getDeclaredFiedlds()返回Field对象的一个数组

获取Class对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Demo3_CreateClass {
public static void main(String[] args) throws ClassNotFoundException {

User2 user2 = new User2();

//1. 通过对象获得
Class c1 = user2.getClass();
//2. 通过forname获得
Class c2 = Class.forName("User2");
//3. 通过类名.class获得
Class c3 = User2.class;

//获取父类类型
Class c4 = c1.getSuperclass();
System.out.println(c4);//class java.lang.Object

}
}

哪些类型可以有Class对象

  • class:外部类,成员,局部内部类,匿名内部类;

  • interface接口

  • []数组

  • enum枚举

  • annotation注解@interface

  • void

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Demo4_ClassFromWhere {
public static void main(String[] args) {
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对象
}
}

Java内存分析

类的加载过程

序号过程解释
1类的加载Load将类的class文件读入内存,并为之创建一个java.lang.Class对象。这个过程由类加载器完成。
2类的链接Link将类的二进制数据合并到JVM中。
3类的初始化InitializeJVM负责对类进行初始化。
链接 过程
  • 验证:确保加载的类信息符合JVM规范,没有安全方面问题;
  • 准备:为类变量static分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中分配;
  • 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
类初始化顺序代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Demo5_初始化顺序 {
public static void main(String[] args) {
System.out.println("程序开始");
AClass aClass = new AClass();
System.out.println("程序结束");
/*
运行结果:
程序开始
类代码块初始化
无参构造初始化
程序结束
*/
}
}

class AClass{
static {
System.out.println("类代码块初始化");
}
public AClass(){
System.out.println("无参构造初始化");
}
}
初始化顺序 代码2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Test {
static{
System.out.println("main方法所在类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
//主动引用:new
Son son = new Son();
}
}
class Father{
static {
System.out.println("父类被加载");
}
}
class Son extends Father{
static {
System.out.println("子类被加载");
m = 300;
}
static int m = 100;
static final int M = 1;
}

输出

1
2
3
main方法所在类被加载
父类被加载
子类被加载
初始化 过程
  • 即,执行构造器<clinit>方法的过程。
<clinit>

类构造器<clinit>()方法是由编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器用来构造类信息,而不是用来构造该类对象的构造器

虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。

类的初始化

  • 当发生类的主动引用,则一定会发生类的初始化。
    • 当虚拟机启动,先初始化main方法所在的类。
    • new一个类的对象。(new一个类的数组不计入。)
    • 调用类的静态成员和静态方法(除了final常量)
    • 使用java.lang.reflect包的方法对类进行反射调用。
    • 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类。
  • 类的被动引用,不会发生类的初始化
    • 当访问一个静态域时,只有真正声明这个域的类才会被初始化。
      • 如:当通过子类引用父类的静态变量,不会导致子类初始化
    • 通过数组定义类引用,不会触发此类的初始化。
    • 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中)
1
2
int a = Son.M;//引用常量
Son[] array = new Son[5];//new一个数组,不引发初始化

类加载器

**类加载:**将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。

**类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,将维持加载(缓存)**一段时间。

JVM垃圾回收机制可以回收这些Class对象

Java程序运行过程

类加载器分类

**引导类加载器:**用C++编写,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取。Bootstap Classloader

**扩展类加载器:**负责jre/lib/ext目录下的jar包或-D java.ext.dirs指定目录下的jar包装入工作库。Extension Classloader、ExtClassLoader

**系统类加载器:**负责java --classpath--D java.class.path所指目录下的类与jar包装入工作,是最常用的类加载器。System Classloader、 AppClassLoader

*注释:*Java平台核心库,即rt.jar包。文件位置:jre/lib/rt.jar(内含有如:java.lang.*等类的字节码文件。)

7-zip软件对于rt.jar文件目录的部分截图

代码查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class Demo6_系统类的加载器 {
public static void main(String[] args) throws ClassNotFoundException {
//获取系统类的加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println("系统类加载器: "+systemClassLoader);

//获取系统类加载器的父类加载器->扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println("系统类加载器的父类扩展类加载器: "+parent);

//获取扩展类加载器的父类加载器 -> 根加载器(c/c++)
ClassLoader parent2 = parent.getParent();
System.out.println("扩展类加载器的父类根加载器:"+parent2);


//当前类是哪个加载器加载的
ClassLoader classLoader = Class.forName("Demo6_系统类的加载器").getClassLoader();
System.out.println("自定义类的加载器: "+classLoader);

//JDK内置类是哪个加载器加载的
ClassLoader classLoader2 = Class.forName("java.lang.Object").getClassLoader();
System.out.println("JDK内置类的类加载器: "+classLoader2);
/*
系统类加载器: sun.misc.Launcher$AppClassLoader@18b4aac2
系统类加载器的父类扩展类加载器: sun.misc.Launcher$ExtClassLoader@1b6d3586
扩展类加载器的父类根加载器:null //根加载器不能直接获得
自定义类的加载器: sun.misc.Launcher$AppClassLoader@18b4aac2
JDK内置类的类加载器: null
*/


//获得系统类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
/*
D:\AboutProgram\Env\jdk1.8.0_202\jre\lib\charsets.jar;
D:\AboutProgram\Env\jdk1.8.0_202\jre\lib\deploy.jar;
...
D:\AboutProgram\Env\jdk1.8.0_202\jre\lib\resources.jar;
D:\AboutProgram\Env\jdk1.8.0_202\jre\lib\rt.jar; //这个是rt.jar
D:\ uwupu\ideaProject\Java反射\out\production\Java反射; //项目
D:\Software\IntelliJ IDEA 2021.2.2\lib\idea_rt.jar //idea_rt.jar包
*/


}
}
 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
总字数 163.9k 访客数 访问量