什么是反射
Java 反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是 JVM 得到 class 对象之后,再通过 class 对象进行反编译,从而获取对象的各种信息。
反射是框架设计的灵魂
Java 属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到 JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。
反射就是把 Java 类中的各个组成部分进行解剖,并映射成一个个的 Java 对象,拿到这些对象后可以做一些事情
既然说反射是解剖 Java 类中的各个组成部分,所以说咱们得知道一个类中有哪些部分?
例如,一个类有:构造方法,方法,成员变量等信息,利用反射技术咱们可以把这些组成部分映射成一个个对象
拿到映射后的构造方法,可以用它来生成对象;拿到映射后的方法,可以调用它来执行对应的方法;拿到映射后的字段,可以用它来获取或改变对应字段的值;
什么是 Class 类
在面向对象的世界里,万事万物皆是对象。
在 java 语言中,static 修饰的东西不是对象,但是它属于类。
普通的数据类型不是对象,例如:int a = 5;
它不是面向对象,但是它有其包装类 Integer 或者分装类来弥补了它。
除了以上两种不是面向对象,其余的,包括类也有它的面向对象,类是 java.lang.Class
的实例化对象。也就是说: Class A{}
当我创建了 A 类,那么类 A 本身就是一个对象,java.lang.Class
的实例对象。
那么这个对象又该怎么表示呢?
我们先看一下下面这段代码:
1 2 3 4 5 6
| public class Demo() { F a = new A(); }
class A { }
|
这里的 A
的实例化对象就可以用 a
表达出来。
同理 A
类也是一个实例化对象:java.lang.Class
类的实例化对象。
获取 Class 对象的四种方式
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
| package com.shiguang;
public class Demo { public static void main(String[] args) {
A f = new A();
Class c1 = A.class; System.out.println(c1);
Class c2 = f.getClass(); System.out.println(c2);
Class c3 = null; try { c3 = Class.forName("com.shiguang.A"); System.out.println(c3); } catch (ClassNotFoundException classNotFoundException) { classNotFoundException.printStackTrace(); }
ClassLoader classLoader = c1.getClassLoader(); try { Class c4 = classLoader.loadClass("com.shiguang.A"); System.out.println(c4); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
class A { }
|
方法的反射
我们知道一个类里一般有构造函数、方法、成员变量(字段/属性)这三部分组成
首先尝试获取一个类中所有的方法
在 java 里面,万事万物都是对象,方法也是对象,方法是 Method 类的对象,一个成员方法就是一个 Method 的对象,那么 Method 就封装了对这个成员
如果我们要获得所有的方法,可以用 getMethods()
方法
这个方法返回的是表示此类中公共(Public)方法的 Method 对象的数组,包括父类继承而来的
如果我们要获取所有该类自己声明的方法,就可以用 getDeclaredMethods()
方法,这个方法是不问访问权限的
尝试写一个工具类
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
| public class ClassUtils { public static void printClassMethodMessage(Object obj) { Class c = obj.getClass(); System.out.println("类的名称是:" + c.getName());
Method[] ms = c.getMethods();
for (int i = 0; i < ms.length; i++) { Class returnType = ms[i].getReturnType(); System.out.print("返回值类型的名字:" + returnType.getName() + " "); System.out.print("方法的名称:" + ms[i].getName() + "("); Class[] paramTypes = ms[i].getParameterTypes(); for (Class class1 : paramTypes) { System.out.print("参数类型:" + class1.getName() + ","); } System.out.println(")"); System.out.println("-----------------------------"); } } }
|
测试
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Demo {
public static void main(String[] args) { A a = new A(); ClassUtils.printClassMethodMessage(a); } }
class A { public Double getMoney(Double money, Integer age) { return null; } }
|
结果
类的名称是:A
返回值类型的名字:java.lang.Double 方法的名称:getMoney(参数类型:java.lang.Double,参数类型:java.lang.Integer,)
成员变量的反射
1 2 3 4 5 6 7 8 9 10 11 12
| public class ClassUtils { public static void printClassMethodMessage(Object obj) { Class c = obj.getClass();
Field[] fs = c.getDeclaredFields(); for (Field f : fs) { System.out.println(f); } } }
|
测试
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Demo {
public static void main(String[] args) { A a = new A(); ClassUtils.printClassMethodMessage(a); } }
class A { private String name; private Integer age; private Double money; }
|
结果
private java.lang.String A.name
private java.lang.Integer A.age
private java.lang.Double A.money
构造函数的反射
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class ClassUtils { public static void printClassMethodMessage(Object obj) { Class c = obj.getClass();
Constructor[] cs = c.getDeclaredConstructors();
for (Constructor constructor : cs) { System.out.print(constructor.getName() + "("); Class[] paramTypes = constructor.getParameterTypes(); for (Class class1 : paramTypes) { System.out.print(class1.getName() + ","); } System.out.println(")"); } } }
|
测试
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
| public class Demo {
public static void main(String[] args) { A a = new A(); ClassUtils.printClassMethodMessage(a); } }
class A { private String name; private Integer age; private Double money;
public A() { }
public A(String name) { this.name = name; }
public A(String name, Integer age, Double money) { this.name = name; this.age = age; this.money = money; } }
|
结果
A()
A(java.lang.String,)
A(java.lang.String,java.lang.Integer,java.lang.Double,)
Class 类的动态加载类
如何动态加载一个类呢?
首先我们需要区分什么是动态加载?什么是静态加载?
我们普遍认为编译时刻加载的类是静态加载类,运行时刻加载的类是动态加载类
new 创建对象是静态加载类(newInstance 是动态加载类),在编译时刻就需要加载所有可能用到的类,而所以不管你用不用这个类。此时如果有一个类找不到,那么编译就无法通过
我们想要的就是我需要用哪个类就加载那个类,也就是常说的:运行时刻加载,动态加载类
写一个需要动态加载的类
1 2 3 4 5 6 7 8 9 10 11
| package com.shiguang;
public class Demo {
public void start() { System.out.println("Demo start"); } }
|
写一个测试类,测试动态加载
1 2 3 4 5 6 7 8 9 10 11 12
| public class DemoTest { public static void main(String[] args) { try { Class<?> c = Class.forName("com.shiguang.Demo"); Demo demo = (Demo) c.newInstance(); demo.start(); } catch (Exception e) { e.printStackTrace(); } } }
|
结果:
Demo start