Java基础(二十一)——反射
一、反射
1、解释
后续补上
2、延伸
反射是框架的灵魂,所有的框架都需要使用到反射技术,使用框架开发的效率会更高,所有的类在反射面前都能看的一清二楚。比如一些私有方法,私有属性,都能够通过某些手段轻易调用。
3、机制
反射机制::将类的各个部分,组合成一个新类,这个类就是Class。
4、反射获取 Class 对象的三种方法
先定义一个 Student 类,里面定义一些方法跟属性。
a、方法一:obj . getClass(通过对象获取)
b、方法二:类名 . Class( 通过类名获取)
c、方法三:Class.forName(…)(通过包名+类名获取)
一般主要通过这种方法来操作反射。
先看下项目的路径结构:
所以,代码是:
这样一来,就可以获取到 Student 这个类。
5、通过反射获取类的构造方法
先来看下通过反射获取类的构造方法的一些方法:
这个 Student 类,其中方法有公有的也有私有的,有无参也有有参,这里仅贴出部分主要的:
a、getConstructors——获取这个类下所有的公有的构造方法(不包括私有构造方法)
b、getDeclaredConstructors——获取这个类下所有的构造方法(包括私有的)
c、getConstructor()和 newInstance()——获取这个类下单个公有的构造方法;实例化对象
这里再强调一下:如果获取有参构造方法,参数类型个数顺序必须一一对应。
d、getDeclaredConstructor()——获取这个类下单个私有的构造方法
这里再强调一下:因为是私有的,所以需要 setAccessible(true),来暴力反射强制性去除私有化,才能成功调用。
6、通过反射获取成员方法
先来看下通过反射获取成员方法的一些方法:
然后看一下,Student 类的一些成员方法:
要调用成员方法,离不开对象,所以,根据上面的知识,可以通过反射调用构造方法来创建对象,这样就能通过对象来调用到成员方法。这里先实例化对象:
注意:这里不用 Student 来接收,因为每次都需要强转;这里图省事用 Object 类来接收。
a、getMethods()——获取所有的公有方法(包括父类公有的方法;不包括私有)
b、getDeclaredMethods()——获取所有公有以及私有的方法
c、getMethod()——获取单个公有的无参方法以及调用
调用方法:xx . invoke(obj)
d、getDeclaredMethod()——获取单个私有的有参方法以及调用
二、反射案例
1、反射案例一
使用集合时,通常会有泛型进行约束。这里通过反射破解泛型的约束,填入不同的数据类型进去(或者说扩大其数据类型为 Object 类型 就可以存在String):
2、反射案例二
有一个配置文件,还有一些 java 文件,仅改变配置文件里面的信息,就可以调用不同的功能。
详细点说,就是类A里面有不同的功能任君调用;
类B专门负责加载配置文件信息并生成一个对象,方便获取其信息。
类C专门负责获取配置文件里面的信息。
配置文件里面存放类名和方法名。修改不同的方法名,便调用不同的方法。
类A:
public class Student {
private void showinfo1(){
System.out.println("哈哈哈");
}
private void showinfo2(String name,int age,String hobby){
System.out.println(name+" "+age+" "+hobby);
}
}
类B:
package com.qf.cmf.fs_lx2;
public class PropertiesUtils {
//私有的属性。因为静态方法只能调用静态属性
private static PropertiesUtils propertiesUtils;
private Properties properties;
//定义一个私有的构造方法 确保这个类,只有一个实例化的对象。
private PropertiesUtils(){
try {
//实例化 properties 对象
properties = new Properties();
//通过反射来加载配置文件到输入流中
InputStream is = PropertiesUtils.class.getResourceAsStream("demo.properties"); // 这里返回值是流
//加载properties
properties.load(is);
} catch (IOException e) {
e.printStackTrace();
}
}
//定义一个方法来实例化这个对象
//静态方法锁的对象的是class对象
//普通的成员方法锁的是当前对象
public static synchronized PropertiesUtils getInstance(){
// 因为构造方法私有化了,所以创建一个普通方法,通过这个普通方法去调用构造方法
if (propertiesUtils == null){
// 这里意思是,如果这个类是空的,意味着没有创建过对象
propertiesUtils = new PropertiesUtils(); // 没有创建过对象就创建,创建过就直接返回。这个类就只允许创建一个对象
}
return propertiesUtils;
}
//定义一个方法来获取Properties 对象的资源 通过键值对来获取
public String getValue(String key){
return properties.getProperty(key);
}
}
类C:
public class Factory {
public static void main(String[] args) {
//获取配置文件中的资源
try {
String className = PropertiesUtils.getInstance().getValue("className");
String met = PropertiesUtils.getInstance().getValue("met");
String parm = PropertiesUtils.getInstance().getValue("parm");
//获取Class对象
Class cls = Class.forName(className);
Constructor con = cls.getConstructor();
//实例化对象
Object obj = con.newInstance();
//获取方法
Class[] clsArray = getClassArrays(parm);
if (clsArray == null || clsArray.length<=0){
// 满足这个条件,就是私有无参方法
Method mt1 = cls.getDeclaredMethod(met,clsArray);
//使用暴力反射去除私有
mt1.setAccessible(true);
mt1.invoke(obj);
}else {
Method mt1 = cls.getDeclaredMethod(met,clsArray); // 满足这个条件,就是私有有参方法
//使用暴力反射去私有
mt1.setAccessible(true);
mt1.invoke(obj,"张三",18,"打飞机"); // 这里需要传参
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public static Class[] getClassArrays(String parm){
//实例化集合来进行存储
ArrayList<Class> classlist = new ArrayList<>();
if (parm == null || "".equals(parm)){
return null;
}else {
//根据逗号来进行分割
String[] str = parm.split(",");
//对数组进行非空验证
if (str !=null && str.length>0){
for (String s:str) {
if (s.equals("String")){
classlist.add(String.class);
}else if(s.equals("int")){
classlist.add(int.class);
}else {
classlist.add(Object.class);
}
}
}
//将集合转换为数组
return classlist.toArray(new Class[classlist.size()]);
}
}
}
配置文件(properties结尾的文件):
className=com.qf.cmf.fs_lx2.Student
met=showinfo2
parm=String,int,String
这里的配置文件,对应的是 Student 类里面的方法和参数。只需要改动配置文件,就可以执行不同的方法。这里的功能可以说是框架也用到了,是部分底层源码。