Java基础(二十一)——反射(机制、获取Class对象、获取构造方法、获取成员方法、反射案例)

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 类里面的方法和参数。只需要改动配置文件,就可以执行不同的方法。这里的功能可以说是框架也用到了,是部分底层源码。

猜你喜欢

转载自blog.csdn.net/qq_41824825/article/details/121478251
今日推荐