美文网首页
Java的反射

Java的反射

作者: 有财君 | 来源:发表于2020-11-24 22:07 被阅读0次

    1. 反射的简单例子

    我们平时创建一个对象的时候,总是用这样的方法:

    Person person = new Person();
    

    这种方式非常自然,是我们一开始学习Java就在用的一种方式。但是考虑一个场景,看这样一段代码:

    List<String> list = new ArrayList<>();
    ...
    

    过了一段时间以后,我发现使用LinkedList更加靠谱,此时我就会去修改代码:

    List<String> list = new LinkedList<>();
    

    如果有一天,我发现另外一种List实现更好,我就要继续修改代码。这种方式实在是太不优雅了,也会让我修改的成本变得很高,因为每次都要重新编译打包。

    于是我想到了一个办法,就是搞一个参数:

    public List<String> getList(String type) {
        if (type == "ArrayList") {
            return new ArrayList<>(); 
        } else if (type == "LinkedList") {
            return new LinkedList<>();
        }
    }
    

    这样我接收一个参数就好了。比原先的代码要灵活一些,但是没有任何扩展能力——如果我要添加一种新的List实现,我还得加一个if分支出来,还是要修改代码并且要重新编译打包。成本还是很高。

    此时就需要用到反射了。将上面的一段代码改造成下面的样子:

    private List getList(String type) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
            return (List) Class.forName(type).newInstance();
    }
    

    这样的话,入参就是具体的实现类,只要是List接口的实现类都可以传进去,传进去什么,就能得到什么。

    我们来看看具体的应用:

    package tester;
    
    import java.util.List;
    
    public class Main {
        public static void main(String[] args) {
            try {
                List<String> list = getList("java.util.ArrayList");
                System.out.println(list.getClass().getName());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private static List getList(String type) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
            return (List) Class.forName(type).newInstance();
        }
    }
    

    打印的结果就是“java.util.ArrayList”,我们已经成功的达成了目的。

    2. 反射的几个用法

    要了解反射需要了解这么几个概念:

    • Class
    • Constructor
    • Method
    • Field

    从名字上就能看出来都是什么,分别是Class对象,构造器,方法和域。

    后面几个好理解,每个类都会有这么几个东西,但是Class对象就稍微有点难以理解。

    我们回忆一下,javac完成对java文件的编译后生成了什么?就是一个后缀是class的文件。每个类都会产生一个Class对象,这个对象里保存了很多有用的信息,我们只需要知道这一点就够了。

    我们所有的操作都要首先依赖这个Class对象,然后才能进行。

    现在我们来用几种不同的办法来创建一个Person对象:

    package tester;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class Application {
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, ClassNotFoundException {
            //这是一般寻常的新建对象的办法
            Person p1 = new Person();
            p1.setId(1);
            p1.setName("Tom");
    
            //通过Constructor对象来创建实例
            Class clazz = Person.class;
            Constructor constructor = clazz.getConstructor();
            Person p2 = (Person) constructor.newInstance();
            //通过Class对象来找到对象的方法
            Method setName = clazz.getMethod("setName", String.class);
            Method setId = clazz.getMethod("setId", int.class);
            //使用对象
            setId.invoke(p2, 2);
            setName.invoke(p2, "Tony");
            System.out.println(p2);
    
            //通过Class对象来直接创建实例
            Class clazz1 = Class.forName("tester.Person");
            Person p3 = (Person) clazz1.getDeclaredConstructor().newInstance();
            p3.setId(3);
            p3.setName("Jerry");
            System.out.println(p3);
    
        }
    }
    

    可以看出来,反射可以像正常方法一样去创建对象,并调用对象的任何方法。

    注意下面这段代码:

    //通过Class对象来直接创建实例
    Class clazz1 = Class.forName("tester.Person");
    Person p3 = (Person) clazz1.getDeclaredConstructor().newInstance();
    p3.setId(3);
    p3.setName("Jerry");
    System.out.println(p3);
    
    Field id = clazz1.getDeclaredField("id");
    id.setAccessible(true);
    id.setInt(p3, 10);
    System.out.println(p3);
    

    打印的结果是:

    Person[id=3, name='Jerry']
    Person[id=10, name='Jerry']
    

    看似平平无奇,其实这段代码就破坏了封装性。要知道Person类中的所有域都是私有的,只能通过setter去设置。但是在上一段代码中,我绕过了setter直接将p3的id值改成了10。

    3. 谁用到了反射

    写过JDBC代码的人应该都会对这段代码记忆犹新:

    Class.forName("com.mysql.jdbc.Driver");
    

    这段代码在运行时会去加载MySQL的JDBC驱动,这就是典型的反射的用法。

    另外,抽象工厂模式也可以使用反射,利用反射来生成具体的工厂。这段代码逻辑不常见于百度搜索出来结果,请参考《图解设计模式》这本书,如果有机会,我也想写写抽象工厂模式的文章。

    相关文章

      网友评论

          本文标题:Java的反射

          本文链接:https://www.haomeiwen.com/subject/cbewiktx.html