1. 反射
反射(reflection):程序可以运行时访问、检测和修改它本身状态或行为的能力。
– 增加动态性
– 降低耦合度,增加了程序灵活性(人们不想修改代码,只需改动配置文件)
– java.lang.reflect包 (Java11)
- 可以在运行时访问和使用编译期间完全未知的类。
–使用时进行加载,要有class文件 - 给Java插上动态语言特性的翅膀
- 反射直接对class文件进行操作
- 但是需要注意的是反射使用不当会造成很高的资源消耗!
反射机制:
- 在编译期可以使用并不知道的类。
–编译时并不确定是哪个类被加载了,而是在程序运行的时候才加载、探知。 - 要正确使用Java反射机制就得使用java.lang.Class这个类。它是Java反射机制的起源。
当一个类被加载以后,Java虚拟机就会自动产生一个Class对象。通过这个Class对象我们就能获得加载到虚拟机当中这个Class对象对应的方法、成员以及构造方法的声明和定义等信息。
– jvm加载类的条件,类class文件存在
– 若class不存在,只需要编译此class文件,再在配置文件中配置(动态性的实现)
反射的作用:
- 在运行时判断任意一个对象所属的类
- 在运行中查看和操作对象
• 基于反射自由创建对象(反射能构建出无法直接访问的类(private))
• set或者get到无法访问的成员变量
• 调用不可访问的方法 - 实现通用的数组操作代码
- 类似函数指针的功能
2. 反射接口
Class:类型标识
–当一个类被加载以后,Java虚拟机就会自动产生一个Class对象
–JVM为每个对象都保留其类型标识信息,包含方法、成员以及构造方法的声明和定义等信息
– Java.lang.Class (Java11)
– 参考代码
2.1获取Class方式:
- obj.getClass()
Student stu1 = new Student();//new 产生一个Student对象,一个Class对象
Class stuClass1 = stu1.getClass();//获取Class对象
System.out.println(stuClass1.getName());
- 通过Class类的静态方法:forName(String className)(常用)
– className是真实路径,包名.类名
– String className可以从配置文件加载,降低耦合性,实现动态性
package fanshe;
Class stuClass2 = Class.forName("fanshe.Student");//注意此字符串必须是真实路径,包名.类名
System.out.println(stuClass1 == stuClass2);//判断是否获取的是同一个Class对象
- 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
Class stuClass2 = Student.class;
- 在运行期间,一个类,只有一个Class对象产生。
- 三种方式常用第二种,
第一种对象都有了还要反射干什么。
第三种需要导入类的包,依赖太强,不导包就抛编译错误。
一般都第二种,一个字符串可以传入也可写在配置文件中等多种方法。
2.2 获取成员变量并调用
-
获取批量
Field[ ] getFields():返回本类和所有父类的public属性 Field[ ] getDeclaredFields();返回本类自己定义的属性,包括private,但不包括父类属性
-
获取单个
public Field getField(String fieldName):获取某个public属性; public Field getDeclaredField(String fieldName):获取某个属性(可以是私有的)
- 可以通过 setAccessible(true);//使得private临时变为public
import java.lang.reflect.Field;
class A
{
public int age;
private String name;
public A(int age, String name)
{
this.age = age;
this.name = name;
}
}
public class FieldTest {
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
A obj = new A(20, "Tom");
Class c = obj.getClass();
//获取本类及父类所有的public字段
Field[] fs = c.getFields();
System.out.println(fs[0].getName() + ":" + fs[0].get(obj));
//获取本类所有声明的字段
Field[] fs2 = c.getDeclaredFields();
for(Field f : fs2)
{
f.setAccessible(true);//使得private临时变为public
System.out.println(f.getName() + ":" + f.get(obj));
}
Field fs3 = c.getField("name");
System.out.println(fs3);
}
}
2.3 获取成员方法并调用
-
获取批量
public Method[ ] getMethods():获取所有public方法;(包含了父类的方法也包含Object类) public Method[ ] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
-
获取单个
public Method getMethod(String name,Class<?>... parameterTypes): 参数: name : 方法名; Class ... : 形参的Class类型对象 -- 无参则不加 public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
-
调用方法:
Method --> public Object invoke(Object obj,Object... args): 参数说明: obj : 要调用方法的对象; -- 非静态方法需要传递obj,静态方法不需要 args:调用方式时所传递的实参; -- 有参函数传递args,无参函数传递 null
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import static java.lang.System.out;
class B {
public void f1() {
out.println("B.f1()...");
}
private String f2(String s) {
out.println("B.f2()...");
return s;
}
}
public class MethodTest {
public static void main(String[] args)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
B obj = new B();
Class c = obj.getClass();
// 获取public方法 包括父类和父接口
Method[] ms = c.getMethods();
for (Method m : ms) {
if ("f1".equals(m.getName())) {
m.invoke(obj, null); //invoke调用方法,非静态需要传递obj
}
}
// 获得该类的单个方法
Method ms2 = c.getDeclaredMethod("f2",String.class);
ms2.setAccessible(true);
String result = (String) ms2.invoke(obj, "abc");
out.println(result);
}
}
2.4 获取构造方法并调用
-
获取批量
public Constructor[] getConstructors():所有"public"构造方法 public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
-
获取单个:
public Constructor getConstructor(Class... parameterTypes):获取单个的"public"构造方法: public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
-
调用构造方法:
弃用 Class.newInstance(Object... initargs) -- 会直接调用该类的无参构造函数进行实例化 改用为: Class.getDeclaredConstructor().newInstance() -- getDeclaredConstructor()方法会根据他的参数对该类的构造函数进行搜索并返回对应的构造函数,没有参数就返回该类的无参构造函数,然后再通过newInstance进行实例化。
package fanshe;
public class Student {
//---------------构造方法-------------------
//(默认的构造方法)
Student(String str){
System.out.println("(默认)的构造方法 s = " + str);
}
//无参构造方法
public Student(){
System.out.println("调用了公有、无参构造方法执行了。。。");
}
//有一个参数的构造方法
public Student(char name){
System.out.println("姓名:" + name);
}
//有多个参数的构造方法
public Student(String name ,int age){
System.out.println("姓名:"+name+"年龄:"+ age);//这的执行效率有问题,以后解决。
}
//受保护的构造方法
protected Student(boolean n){
System.out.println("受保护的构造方法 n = " + n);
}
//私有构造方法
private Student(int age){
System.out.println("私有的构造方法 年龄:"+ age);
}
package fanshe;
import java.lang.reflect.Constructor;
public class Constructors {
public static void main(String[] args) throws Exception {
//1.加载Class对象
Class clazz = Class.forName("fanshe.Student");
//2.获取所有公有构造方法
System.out.println("**********************所有公有构造方法*********************************");
Constructor[] conArray = clazz.getConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
conArray = clazz.getDeclaredConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
System.out.println("*****************获取公有、无参的构造方法*******************************");
Constructor con = clazz.getConstructor(null);
//1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
//2>、返回的是描述这个无参构造函数的类对象。
System.out.println("con = " + con);
//调用构造方法
Object obj = con.newInstance();
// System.out.println("obj = " + obj);
// Student stu = (Student)obj;
System.out.println("******************获取私有构造方法,并调用*******************************");
con = clazz.getDeclaredConstructor(char.class);
System.out.println(con);
//调用构造方法
con.setAccessible(true);//暴力访问(忽略掉访问修饰符)
obj = con.newInstance('男');
}
}
2.5 获取父类信息
sonClass.getSuperclass():获取父类Class
sonClass.getInterfaces() :获取接口Class
class Father { }
class Son extends Father
implements Cloneable, Comparable
{
protected Object clone() throws CloneNotSupportedException
{
return super.clone();
}
public int compareTo(Object o) {
return 0;
}
}
public class SuperTest {
public static void main(String[] args) {
Son son = new Son();
Class c = son.getClass();
Class father = c.getSuperclass();
System.out.println(father.getName());
Class[] inters = c.getInterfaces();
for(Class inter : inters)
{
System.out.println(inter.getName());
}
}
}
3. 反射应用
• 数据库连接
• 数组扩充器
• 动态执行方法
• Json和Java对象互转
• Tomcat的Servlet对象创建
• MyBatis的OR/M
• Spring的Bean容器
• org.reflections包介绍
- jbdc数据库连接
//构建Java和数据库之间的桥梁介质
try{
Class.forName("com.mysql.jdbc.Driver");
//Class.forName(className, true, currentLoader)
//通知类加载器加载此类的class文件
System.out.println("注册驱动成功!");
}catch(ClassNotFoundException e1){
System.out.println("注册驱动失败!");
e1.printStackTrace();
return;
}