java反射(Java Reflection)是指在程序运行时获取已知名称的类或已有对象相关信息的一种机制,包括类的方法、属性、父类等信息,还包括实例的创建和实例类型的判断。
在常规程序设计中,我们调用类一般都是显示调用的,如
package com.madman.base.reflection;
public class Reflection_01 {
public static void main(String[] args) {
A a = new A();//显示调用 直接new A()
a.sayHello("Hello Word");
}
static class A {
public void sayHello(String str) {
System.out.println("sayHello()方法正在执行,参数:" + str);
}
}
}
试想一下,根据上面的例子能否根据类名A,找出A的属性和方法,甚至构造函数, 根据类名反射出类的属性和方法,简单而言就是反射,反过来获得结果。
反射常用的几个类
- Class类:代表一个类
- Constructor类:代表类的构造方法
- Field类:代表类的成员变量,也就是类的属性字段
- Method类:代表类的方法
通过上面几个类就能解析出无穷多的系统类和自定义的类,能解析出类的方法和构造等等
通过反射得到构造函数、方法、属性值
案例:
package com.madman.base.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Reflection_02 {
// 定义了两个属性
String name;
int age;
// 默认的无参构造方法
public Reflection_02() {
}
// 有参构造方法
public Reflection_02(String rname, Integer rage) {
this.name = rname;
this.age = rage;
}
// 自定义方法
public void sayHello(String str) {
System.out.println("sayHello()方法正在执行,参数:" + str);
}
public static void main(String[] args) {
try {
Class<?> classInfo = Class.forName("com.madman.base.reflection.Reflection_02");
System.out.println("打印所有的构造函数开始......");
Constructor<?>[] constructors = classInfo.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("构造函数:" + constructor.toString());
}
System.out.println("打印所有的构造函数结束......");
System.out.println("打印所有的方法开始......");
Method[] methods = classInfo.getDeclaredMethods();
for (Method method : methods) {
System.out.println("打印方法:" + method.toString());
}
System.out.println("打印所有的方法结束......");
System.out.println("打印所有的属性开始......");
Field[] fields = classInfo.getDeclaredFields();
for (Field field : fields) {
System.out.println("打印属性值:" + field.toString());
}
System.out.println("打印所有的属性结束......");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
打印所有的构造函数开始......
构造函数:public com.madman.base.reflection.Reflection_02()
构造函数:public com.madman.base.reflection.Reflection_02(java.lang.String,java.lang.Integer)
打印所有的构造函数结束......
打印所有的方法开始......
打印方法:public static void com.madman.base.reflection.Reflection_02.main(java.lang.String[])
打印方法:public void com.madman.base.reflection.Reflection_02.sayHello(java.lang.String)
打印所有的方法结束......
打印所有的属性开始......
打印属性值:java.lang.String com.madman.base.reflection.Reflection_02.name
打印属性值:int com.madman.base.reflection.Reflection_02.age
打印所有的属性结束......
上述的代码就通过发射打印出了当前这个类的构造函数,类定义的方法,还有类的自定义属性,还可以获取类的父类已经接口之类,具体看API
通过上述的代码,我们可以知道了A的结构,但是并没有对A进行实例化,怎样才能用反射实现A的实例呢,上述代码中A类我们定义了两个构造函数(我们都知道类如果定义了有参的构造函数,那么必须定义无参的构造函数,要不然就编译不过去了,new的时候会编译失败),要想A实例化,就需要通过A的构造函数去完成。
实例化的几种方式
package com.madman.base.reflection;
import com.sun.org.apache.xpath.internal.SourceTree;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Reflection_03 {
// 定义了两个属性
String name;
int age;
// 默认的无参构造方法
public Reflection_03() {
System.out.println("class A 默认构造函数");
}
// 有参构造方法
public Reflection_03(String rname, Integer rage) {
System.out.println("class A 两个参数的构造函数");
this.name = rname;
this.age = rage;
}
// 自定义方法
public void sayHello(String str) {
System.out.println("sayHello()方法正在执行,参数:" + str);
}
public static void main(String[] args) {
try {
Class<?> classInfo = Class.forName("com.madman.base.reflection.Reflection_03");
// 第一种实例化方法 直接通过classInfo.newInstance();调用默认的无参构造函数
Reflection_03 r1 = (Reflection_03) classInfo.newInstance();
r1.sayHello("r1");
// 第二种 通过获取构造器的参数来获取实例
Constructor<?>[] constructors = classInfo.getConstructors();
// 这里去除构造器数组的第一个构造器 默认是无参的构造器 可以打印constructors看下每个构造器对应什么参数
Reflection_03 r2 = (Reflection_03) constructors[0].newInstance();
r2.sayHello("r2");
Reflection_03 r3 = (Reflection_03) constructors[1].newInstance(new Object[] { "madman", 25 });
r3.sayHello("r3");
// 第三种 直接根据构造函数的类型获取构造器
Constructor<?> c1 = classInfo.getConstructor();
c1.newInstance();
Constructor<?> c2 = classInfo.getConstructor(new Class[] { String.class, Integer.class });
c2.newInstance(new Object[] { "heihei", 12 });
// Constructor<?> c2 = classInfo.getConstructor(String.class, Integer.class);
// c2.newInstance("haha", 12);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class A 默认构造函数
sayHello()方法正在执行,参数:r1
class A 默认构造函数
sayHello()方法正在执行,参数:r2
class A 两个参数的构造函数
sayHello()方法正在执行,参数:r3
class A 默认构造函数
class A 两个参数的构造函数
Process finished with exit code 0
通过上述操作,我们发现通过反射可以实例化对象了,对象实例化那我们就可以通过实例化对象进行方法和属性的操作了,就能完成基本功能了
接下来我们讲一下通过反射实例化但不显示指定类型的时候怎么操作类的方法。
package com.madman.base.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Reflection_04 {
// 定义了两个属性
String name;
int age;
// 默认的无参构造方法
public Reflection_04() {
System.out.println("class A 默认构造函数");
}
// 有参构造方法
public Reflection_04(String rname, Integer rage) {
System.out.println("class A 两个参数的构造函数");
this.name = rname;
this.age = rage;
}
// 自定义方法
public void sayHello(String str) {
System.out.println("sayHello()方法正在执行,参数:" + str);
}
public static void main(String[] args) {
try {
Class<?> classInfo = Class.forName("com.madman.base.reflection.Reflection_04");
// 这里实例化了对象 但是没有显示的转换成Reflection_04,但是却能成功的执行Reflection_04的sayHello()方法
Object obj = classInfo.getConstructor().newInstance();
Method sayHello = classInfo.getMethod("sayHello", String.class);
sayHello.invoke(obj, "Hello Word");
} catch (Exception e) {
e.printStackTrace();
}
}
}
class A 默认构造函数
sayHello()方法正在执行,参数:Hello Word
Process finished with exit code 0
总结一下上面的几点
总结上述几点发现,我们只需要指定类的引用地址,通过反射就能知道关于类的构造函数,属性,方法,那么反而言之,我们只需要知道类的引用字符串,方法名字符串,方法参数值,就能执行这个类的这个方法了
补充几点网上别人说的
获取Class的几种方式
package com.madman.base.reflection;
public class StudentTest {
public static void main(String[] args) {
try {
// 第一种方式获取Class对象
// 这一new 产生一个Student对象,一个Class对象。
Student stu1 = new Student();
// 获取Class对象
Class stuClass = stu1.getClass();
System.out.println(stuClass.getName());
// 第二种方式获取Class对象
Class stuClass2 = Student.class;
System.out.println(stuClass == stuClass2);
// 第三种方式获取Class对象
// 注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
Class stuClass3 = Class.forName("com.madman.base.reflection.Student");
// 判断三种方式是否获取的是同一个Class对象
System.out.println(stuClass3 == stuClass2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Class了的几个属性记录
获取构造器
- public Constructor[] getConstructors():所有"公有的"构造方法
- public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
- public Constructor getConstructor(Class… parameterTypes):获取单个的"公有的"构造方法
- public Constructor getDeclaredConstructor(Class… parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
- Constructor.setAccessible(暴力访问(忽略掉访问修饰符) ,一般在调用私有方法的时候使用)
获取方法 - public Constructor[] getConstructors():所有"公有的"构造方法
- public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
- public Constructor getConstructor(Class… parameterTypes):获取单个的"公有的"构造方法:
- public Constructor getDeclaredConstructor(Class… parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
获取成员变量 - Field[] getFields():获取所有的"公有字段"
- Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认
- public Field getField(String fieldName):获取某个"公有的"字段;
- public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
获取Class的接口
Class<?>[] interfaces = classInfo.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println("接口:" + anInterface.toString());
}
//获取Class的父类
classInfo.getSuperclass();
Field getField(String name) 根据变量名,返回一个具体的具有public属性的成员变量 Field[] getFields() 返回具有public属性的成员变量的数组 Field getDeclaredField(String name) 根据变量名,返回一个成员变量(不分public和非public属性) Field[] getDelcaredField() 返回所有成员变量组成的数组(不分public和非public属性) Method getMethod(String name, Class[] params) 根据方法名和参数,返回一个具体的具有public属性的方法 Method[] getMethods() 返回所有具有public属性的方法数组 Method getDeclaredMethod(String name, Class[] params) 根据方法名和参数,返回一个具体的方法(不分public和非public属性) Method[] getDeclaredMethods() 返回该类中的所有的方法数组(不分public和非public属性) Constructor getConstructor(Class[] params) 根据构造函数的参数,返回一个具体的具有public属性的构造函数 Constructor getConstructors() 返回所有具有public属性的构造函数数组 Constructor getDeclaredConstructor(Class[] params) 根据构造函数的参数,返回一个具体的构造函数(不分public和非public属性) Constructor getDeclaredConstructors() 返回该类中所有的构造函数数组(不分public和非public属性)