栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

通过案列代码带你理解Java反射机制(小白版)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

通过案列代码带你理解Java反射机制(小白版)

前言

很多讲反射的文章,总是喜欢引用概念,摘抄一段百度反射的定义,说实话,这种概念对没什么基础的同学来说,压根整不明白,像天书,或者有的仅仅告诉你反射的语法,说反射是框架的灵魂,但却不举例看看反射能做什么;

对我来说,学了一门技术,一定得明白它对于我们代码中,该怎么样去用,所以,今天这篇介绍反射的文章,偏向于应用,开头先介绍一下语法,怎么去用反射,最后出了三道题,看看如何去用反射去解决问题,加深大家对反射的印象


目录

前言

一、反射的概念

二、获取Class类

1、新建一个User类

2、获取Class类的语法

三、获取构造方法,并用构造方法创建实例

四、通过反射获取成员变量

1、获得共有成员变量

2、获得私有成员变量的值

五、通过反射获取类的方法

1、获得公有方法并调用

2、获得私有方法并调用

五、来做几道反射的编程题吧

1、通过反射去越过泛型检查

2、通过配置文件去运行某个类的某个方法

3、通过参数,动态去获取对象的字段

总结




一、反射的概念

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

 哈哈,听了这个概念是不是很懵逼,你可以简单的理解为在程序运行中,你想获得一个类有哪些属性哪些方法,但你有没有这个类的实现,我们可以通过他的Class类(每一个类在虚拟机中都有一个对应的Class类),我们可以通过解剖这个Class类去反向获得我们需要的类;Class类对象会将一个类的方法、变量、接口、类名、类修饰符等信息告诉运行的程序。


二、获取Class类


1、新建一个User类
public class User {
    public String name;
    private Integer age;

    public User() {
        System.out.println("调用无参构造");
    }

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public void say(){
        System.out.println("Hello " + this.getName());
    }

    public void say(String name){
        System.out.println("Hello " +name);
    }

    private void see(){
        System.out.println("See" + age);
    }

    private void see(Integer age){
        System.out.println("See" + age);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + ''' +
                ", age=" + age +
                '}';
    }
}

2、获取Class类的语法

获取Class对象的方式有3中,比较常用的是第三中,通过类名去获取,像JDBC加载驱动也是用的这一种

public class GetClazz {
    public static void main(String[] args) {
        try {
            //通过路径获取
            Class clazz1 = Class.forName("com.company.pojo.User");
            System.out.println(clazz1);
            //通过类获取
            Class clazz2 = User.class;
            System.out.println(clazz2);
            //通过示例获取
            User user = new User();
            Class clazz3 = user.getClass();
            System.out.println(clazz3);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

三、获取构造方法,并用构造方法创建实例

Java将类的构造方法封装成了Constructor对象,我们可以通过反射去得到这个对象,并且通过构造方法创建类的实例

public class ConstructorTest {
    public static void main(String[] args) throws Exception{
        Class clazz = Class.forName("com.company.pojo.User");
        //无参构造
        Constructor constructor = clazz.getConstructor();
        User user = (User) constructor.newInstance();
        System.out.println("通过无参构造方法生成:"+user);
        //有参构造
        Constructor constructor1 = clazz.getConstructor(String.class, Integer.class);
        User user1 = (User) constructor1.newInstance("tyshawn", 23);
        System.out.println("通过有参构造方法生成:"+user1);

    }
}

四、通过反射获取成员变量

类的成员变量被封装成Field类,我们得到类的成员变量的封装后,可以通过Field对象去修改一个实例的某个成员变量的值

1、获得共有成员变量
public class PublicFieldTest {
    public static void main(String[] args) throws Exception{
        Class clazz = Class.forName("com.company.pojo.User");
        User user = (User) clazz.newInstance();
        System.out.println("获得对象:"+user);
        //获取共有成员变量
        Field name = clazz.getField("name");
        //可以通过成员变量方法给对应的对象赋值
        name.set(user, "tyshaw");
        System.out.println("通过反射去改变反射生成的对象属性:"+user);

    }
}

2、获得私有成员变量的值
public class PrivateFieldTest {
    public static void main(String[] args) throws Exception{
        Class clazz = Class.forName("com.company.pojo.User");
        User user = (User) clazz.newInstance();
        //获取共有成员变量
        Field age = clazz.getDeclaredField("age");
        //去除私有权限,如果没有这句话,调用set会报错
        age.setAccessible(true);
        //赋值
        age.set(user, 23);
        System.out.println(user);
    }
}

五、通过反射获取类的方法

类的方法被封装成了一个Method对象,我们可以通过反射得到这个对象,得到这个对象后,就能通过反射去调用这个类的方法

1、获得公有方法并调用
public class PublicMethodTest {
    public static void main(String[] args) throws Exception{
        Class clazz = Class.forName("com.company.pojo.User");
        Constructor constructor = clazz.getConstructor(String.class, Integer.class);
        User user = (User) constructor.newInstance("zhangsan", 23);

        //获取无参成员方法
        Method say = clazz.getMethod("say");
        say.invoke(user);

        //获取有参成员方法
        Method say1 = clazz.getMethod("say", String.class);
        say1.invoke(user, "Tom"); //Hello Tom

    }
}

2、获得私有方法并调用
public class PrivateMethodTest {
    public static void main(String[] args) throws Exception{
        Class clazz = Class.forName("com.company.pojo.User");
        Constructor constructor = clazz.getConstructor(String.class, Integer.class);
        User user = (User) constructor.newInstance("zhangsan", 23);

        //获取无参成员方法
        Method method = clazz.getDeclaredMethod("see");
        //去除私有权限
        method.setAccessible(true);
        method.invoke(user); // Hello zhangsan

        //获取有参成员方法
        Method method1 = clazz.getDeclaredMethod("see", Integer.class);
        //去除私有权限
        method1.setAccessible(true);
        method1.invoke(user, 11); //Hello Tom

    }
}

五、来做几道反射的编程题吧

学了反射的语法和一些概念,那就来尝试一下看看通过反射能解决什么问题吧

正所谓,光说不练,假把式

1、通过反射去越过泛型检查

如果我有一个ArrayList集合,泛型设置为Integer类型,但我想在保留泛型的情况下去在集合中新增一个String字符串,该怎么去实现?

答案:

public class Solution_01 {
    public static void main(String[] args) throws Exception {
        String s = "hello world";
        ArrayList list = new ArrayList<>();
        list.add(10);
        Class aClass = list.getClass();
        //拿到集合的add方法
        Method add = aClass.getMethod("add",Object.class);
        add.invoke(list,s);
        System.out.println(list);
    }
}

 看上面的代码,正常情况,list直接调用add方法会检查泛型,但通过反射去获取它的Method方法封装类,在去调用,就能越过泛型检查,往里面添加一个String类型;

2、通过配置文件去运行某个类的某个方法

想象一个场景,在学校,如果来的是学生,那就学习,如果是老师,那就教书,如果是食堂阿姨,那就打饭,学校里有很多人有各自的身份,每个身份有各自需要做的事情(方法),而且后续还会有新的角色;

把这个场景抽象为代码,那就是

if(学生){
    调用学习方法
}else if(老师){
    教书
}else if(教授){
    科研
}else if(保安){
    保卫学校
}。。。。

这样有什么弊端,每个、新增一个角色,就得去新增或删除一个角色,就得去改一次代码这样非常不灵活;如何用反射去解决呢?

我们可以新建一个配置文件config.txt

className=com.company.train.bean.Student
methodName=study

新建执行方法

public class Solution_02 {
    public static void main(String[] args) throws Exception {
        
        Properties properties = new Properties();
        FileReader fileReader = new FileReader("D:\IDEAProjects\reflection-demo\src\com\company\train\config\class.txt");
        properties.load(fileReader);
        fileReader.close();

        String className = properties.getProperty("className");
        String methodName = properties.getProperty("methodName");
        Class aClass = Class.forName(className);
        Constructor constructor = aClass.getConstructor();
        Object o = constructor.newInstance();
        Method method = aClass.getMethod(methodName); //study
        method.invoke(o);
    }
}

如果用反射,我们可以把需要的身份直接在配置文件配置,需要调用那个方法,直接就在配置文件配置,如果有新的角色,也只需要新增实体类,然后只需要去该配置文件的类和方法,而不用去动代码,解耦合

3、通过参数,动态去获取对象的字段

有一个实体类,里面有X个成员变量(这里因为篇幅只写了三个),成员变量的命名是FXX,你需要根据上下文,获得序号为X的某个成员变量值,该怎么去获取?比如输入为2,输出F2

public class Entity {

    private Integer F1;

    private Integer F2;

    private Integer F3;

    public Integer getF1() {
        return F1;
    }

    public void setF1(Integer f1) {
        F1 = f1;
    }

    public Integer getF2() {
        return F2;
    }

    public void setF2(Integer f2) {
        F2 = f2;
    }

    public Integer getF3() {
        return F3;
    }

    public void setF3(Integer f3) {
        F3 = f3;
    }
}

如果没有学过反射,可以这样写:

 int fun(Entity entity,int i){
        if (i==1){
            return entity.getF1();
        }
        if (i==2){
            return entity.getF2();
        }
        if (i==3){
            return entity.getF3();
        }
        return -1;
    }

这样写有什么坏处,这里只有3个变量,那就得有三个判断,那如果有100个变量,1000个变量呢,如果还是这样写,那对代码结构简直是一种灾难!

我们可以用反射去解决,只需要几行代码,你可以通过反射去动态获取需要调用的方法

int reflection(Entity entity,int i) throws Exception{
        Class aClass = entity.getClass();

        Method method = aClass.getMethod("F0" + i);
        Integer invoke = (Integer)method.invoke(entity);
        return invoke;
    }



总结

        怎么理解反射,反射就是程序在运行时,通过Class类,获取类的名称、package信息、所有属性、方法、注解、类型、类加载器,这些方法都是封装在其各自的对象中,比如Filed,Method,获得了这些对象,我们可以通过这些组件,比如构造方法类去创建实例,Method实例去执行类的方法,成员变量实例去赋值,;

反射可以动态的去获取不同的类和执行不同的方法,它的优点就是很灵活,代码简洁,可以提高代码的复用率,外部调用方便;它的缺点是性能消耗更大,而且用反射会模糊程序内部逻辑,更复杂。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/657368.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号