Java的反射:在实际的开发场景中,反射的应用是很常见的,比如我目前开发的小组的一大哥,超级喜欢用反射,部的不说,反射确实很好用,看过spring源码的小伙伴应该也知道,spring源码里面全部都是反射,感兴趣的可以去康一康。
@TOC
前言
提示:以下是本篇文章正文内容,下面案例可供参考
一、什么是反射?为什么会有反射?
现在已有一个已经上线的应用,需要给这个应用添加一些新功能,该怎么解决呢?总不能,这边把这个程序给停掉,然后说: 你们等一下,我们这边要进行升级,大概得2天...(emmm,好像这样也不是不可以O(∩_∩)O哈哈~)
实际解决方案:1.停止服务器,然后进行源码修改2.重新开发3.使用反射技术
Class对象的介绍
其实说起反射的话,都是跟着注解一起使用的情况比较多,提起反射又不得不引入枚举注解等概念,但是今天不讲这个,只讲反射的基础。
在Java中描述事务使用的类,同时也可以使用接口给事务增加额外的功能【扩展功能】,给事务双方定义规则。以及使用枚举可以完成某个事务具有特定对象的功能。而这些描述完的所有程序,在编译完毕统一会生成各自的class文件。 也就是说,经过编译的就会生成特殊的文件:class文件------>字节码文件
我觉得学习一个新的事务,最快的方法就是去看视频,这一点能够让你快速使用*=*但是你想要搞懂其中的原理,这个时候你就要去看官方文档或者手册api,要安静,要耐心才能学有所成。
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。Class类它可以表示Java中的任何内容(类、接口、枚举、注解、数组、基本类型、void类型、成员变量、成员函数、构造函数、静态代码块、构造代码块等)。Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的
在学习面向对象的时候,我们介绍说当使用到某个类的时候,JVM会从硬盘上把class文件加载到方法区中。接着JVM会在堆内存中创建一个Class对象,使用这个对象表示当前的那个class文件。
上面一些概念都是最基本的,基本上可以随便百度-----或者去看文档都会有的。
直接上重点。
二、获取Class对象的三种方式
1. 第一种
非抽象的类都可以有自己的真实对象,这时我们就可以通过这些对象反方向推出它们所属的class文件,只要找到这个class文件,就已经找到了Class的对象在Object类中,有个getClass方法,就可以获取到任何一个真实对象对应的Class对象(class文件)。
输出结果到控制台
2. 第二种
使用类型的class属性直接获取
打印到控制台
3. 第三种
第二种方式获取Class的时候需要知道具体的类型的名字。而真实开发中,类型的名字是不知道,都是通过配置文件读取回来的。在Class类中有个静态的方法
三、动态创建一个类的实例对象
已经获取到了Class对象,即就是获取到某个类的class文件对象。现在我们不使用Java中的new关键字来创建这个class文件所描述的那个事物的真实对象, 而通过Class这个对象,动态的创建这个类的真实对象。在Class类中有newInstance方法,可以动态的创建出当前这个class文件对象的真实对象。
注意:使用Class类中的newInstance 方法的时候,要求class文件中必须有空参数公开的构造函数。如果没有使用就会报错。
四、反射构造方法
在Class类中有getConstructor方法可以获取到某个class文件中的构造函数
/*
* 反射类中的公开的带有参数的构造函数
* public Person(String name, int age)
* Constructor<T> getConstructor(Class<?>... parameterTypes)
*
* 在一个类中可以有重名的构造函数,它们怎么区别:
* 在一个类中的重载的函数,是通过参数列表的类型不同在区分。
* 在真正要反射某类的函数时,是需要在反射的指定函数的参数,
* 通过这个参数确定具体反射的是哪个函数
*
* 在Class类中的getConstructor 方法中接受的参数表示的是需要反射的某个
* 构造函数上的参数列表的类型
*/
五、反射私有的构造方法
六、反射成员变量
之前偷懒,觉得这个地方很简单,但是后来发现还是有必要上代码进行展示的。
在Class类中提供的getMethod方法上接受一个String xxx,xxx就是表示需要反射的那个方法的名字。
因为在一个类中可以有很多不同名的方法。 在放射的时候需要指定这个方法 的名字,同时再一个类中还可能出现方法的重载,这个时候还需要指定反射的是哪个方法的参数类型。
public void demo_1() throws Exception{
//获取Class对象
Class clazz = Class.forName("Person");
/*
* 反射成员方法:
* public void setName(String name)
* 类中的非静态的成员方法,需要对象调用,我们反射到方法之后,最后肯定是要运行这个方法
* 这时肯定还是需要对象的
*
* Method getMethod(String name, Class<?>... parameterTypes)
* String name 反射的方法的名字
* Class<?>... parameterTypes 反射的方法接受的参数类型
*/
Method method = clazz.getMethod("setName", String.class);
/*
* 让反射到的一个方法运行,需要使用Method类中的invoke方法
*
* Object invoke(Object obj, Object... args)
*
* invoke方法中的第一个参数 Object obj:表示的是当前需要调用这个方法的那个对象
* invoke方法中的第二个参数Object... args:
* 表示的是真正需要运行的某个类中被反射的那个方法需要接收的真实参数
* 在调用Method类中的invoke方法的时候,其实底层是在运行被反射的那个方法,
* 既然是某个方法在运行,那么方法运行完之后可能会有返回值
*/
Object obj = clazz.newInstance();
Object value = method.invoke(obj, "赵四"); //这句代码就是在调用反射到Person类中的setName方法
System.out.println(obj);
System.out.println(value);
}
反射类中私有,静态的,不需要参数的,且有返回值的方法
public void demo_2() throws Exception{
/*
* 反射Person类中的私有的 静态的成员方法
* private static void say()
*/
Class clazz = Class.forName("Person");
/*
* 反射方法
* 反射的方法不需要接受参数的时候,在反射时参数的类型可以书写成null
*/
Method method = clazz.getDeclaredMethod("say", null);
/*
* 由于方法是私有的,这时需要取消Java的权限检查
*/
method.setAccessible(true);
/*
* 调用invoke方法,让被反射的方法运行
* 由于这个方法是静态的,运行的时候是不需要对象的
*
* 这时在调用invoke方法的时候,对象书写null,由于不需要参数,真实的参数也书写成null
*/
Object value = method.invoke(null, null);
System.out.println(value);
}
public void demo_3() throws Exception {
Class clazz = Class.forName("Person");
/*
* 反射私有的非静态的,接收参数,有返回值的方法
* private List<String> demo(List<String> list)
*/
Method method = clazz.getDeclaredMethod("demo", java.util.List.class );
//反射一个Person对象
Object obj = clazz.newInstance();
/*
* 准备一个List集合
*/
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("abc");
list.add("abc");
list.add("cba");
list.add("cba");
list.add("nba");
list.add("nba");
list.add("aaa");
list.add("bbbb");
//在使用之前取消Java的权限检查
method.setAccessible(true);
//调用被反射的方法
Object value = method.invoke(obj, list);
System.out.println(value);
}