原文:Java基础之反射
为了简便,就对原文作一个小总结
反射是框架设计的灵魂
使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码)
一、反射的概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
反射就是把java类中的各种成分映射成一个个的Java对象,例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
二、反射机制的作用
1.反编译:.class–>.java
2.通过反射机制访问java对象的属性,方法,构造方法等;
三、Class对象的由来
Class对象的由来是将class文件读入内存,并为之创建一个Class对象。
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型)
Class 没有公共构造方法。 Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。
怎么获取Class对象:
1 Object.getClass();
2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性,通过class属性获取
3 通过Class类的静态方法:forName(String className)(常用)
三种方式常用第三种,第一种对象都有了还要反射干什么。第二种需要导入类的包,依赖太强,不导包就抛编译错误。一般都第三种,一个字符串可以传入也可写在配置文件中等多种方法。
四、反射中常用到的Class类的方法
1.构造方法
方法 | 说明 |
---|---|
Constructor<?>[] getConstructors() | 返回所有的公有构造方法 |
Constructor getConstructor(Class<?>… parameterTypes) | 按(若干)参数类型返回公有具体构造方法 |
Constructor<?>[] getDeclaredConstructors() | 返回所有构造方法 |
Constructor getDeclaredConstructor(Class<?>… parameterTypes) | 按(若干)参数类型返回所有具体构造方法 |
通过Constructor对象创建对象
Object obj=Constructor.newInstance(对应构造器若干参数(实参))后强制转换成对应对象类型即可
注意
当用私有构造方法创建对象时,获取到Constructor后需要调用constructor.setAccessible(true)后才能调用newInstance方法
2.方法
方法 | 说明 |
---|---|
Method[] getMethods() | 返回所有公有方法 |
Method getMethod(String name, Class<?>… parameterTypes) | 按照方法名和对应若干参数类型返回具体公有方法 |
Method[] getDeclaredMethods() | 返回所有方法 |
Method getDeclaredMethod(String name, Class<?>… parameterTypes) | 按照方法名和对应若干参数类型返回具体方法 |
方法的调用
Object result=method.invoke(反射创建的Object对象,若干参数)后强制转化result为对应返回类型即可
注意
当调用私有方法时,获取到Method后需要调用method.setAccessible(true)后才能调用invoke方法
3.属性
方法 | 说明 |
---|---|
Field[] getFields() | 返回所有公有属性 |
Field getField(String name) | 按属性名返回对应公有属性 |
Field[] getDeclaredFields() | 返回所有属性 |
Field getDeclaredField(String name) | 按属性名返回对应属性 |
属性的设置
field.set(反射创建的Object对象,值)
注意
当设置私用属性时,获取到Field后需要调用field.setAccessible(true)后才能设置属性值
4、通过反射运行配置文件内容
优点:可以动态配置运行类和属性
可以看到加载类和设置实参和设置属性值都是value,我们可以通过Properties类加载配置文件来动态加载类对象信息
方法 | 说明 |
---|---|
load(FileReader in) | 加载配置文件 |
getProperty(String key) | 通过key获取属性值 |
put(String key,Object value) | 添加属性 |
store(FileOutputStream out,null) | 将属性保存到文件中 |
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;
/*
* 我们利用反射和配置文件,可以使:应用程序更新时,对源码无需进行任何修改
* 我们只需要将新类发送给客户端,并修改配置文件即可
*/
public class Demo {
public static void main(String[] args) throws Exception {
//通过反射获取Class对象
Class stuClass = Class.forName(getValue("className"));//"cn.fanshe.Student"
//2获取show()方法
Method m = stuClass.getMethod(getValue("methodName"));//show
//3.调用show()方法
m.invoke(stuClass.getConstructor().newInstance());
}
//此方法接收一个key,在配置文件中获取相应的value
public static String getValue(String key) throws IOException{
Properties pro = new Properties();//获取配置文件的对象
FileReader in = new FileReader("pro.txt");//获取输入流
pro.load(in);//将流加载到配置文件对象中
in.close();
return pro.getProperty(key);//返回根据key获取的value值
}
}
5、通过反射越过泛型检查
泛型用在编译期(泛型的错误全来自编译错误,泛型集合不协变),先检查后编译再泛型擦除(消失掉,回到原始类)。所以是代码运行时可以通过反射越过泛型检查的
import java.lang.reflect.Method;
import java.util.ArrayList;
/* * 通过反射越过泛型检查 *
* 例如:有一个String泛型的集合,怎样能向这个集合中加一个Integer类型的值? */
public class Demo {
public static void main(String[] args)throws Exception{
ArrayList<String> strList = new ArrayList<>();
strList.add("aaa");
strList.add("bbb");
// strList.add(100); //获取ArrayList的Class对象,反向的调用add()方法,添加数据
Class listClass = strList.getClass();
//得到 strList 对象的字节码 对象
//获取add()方法
Method m = listClass.getMethod("add", Object.class);
//调用add()方法
m.invoke(strList, 100);
//遍历集合
for(Object obj : strList){
System.out.println(obj);
}
}
}