java反射学习(结合工厂方法)

来源:互联网 发布:stm8单片机 编辑:程序博客网 时间:2024/06/06 03:32

java反射的应用

  1. 什么是反射?
    JAVA反射机制是就是运行的状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
  2. 反射的应用场景?
    在讲反射的应用场景之前,先来讨论一个开发当中碰到的场景。一步步深入,最后就会发现反射对于代码的质量会有一个质的提升。
  3. 假设我们的数据库是mysql,那么我们的代码中会出现如下代码
Class.forName("com.mysql.jdbc.Driver");conn = DriverManager.getConnection(url, user, password);

而如果我们的数据库是oracle,我们的代码会如下:

Class.forName("oracle.jdbc.driver.OracleDriver");conn = DriverManager.getConnection(url, user, password);

如果这时候数据库换成了sql server,我们也只需要修改一下Class.forName()的参数。这对于开发者来说是相当方便的,如果java没有提供jdbc。一切和数据库的交互我们都需要从底层实现,那么菜鸟的代码就可能会出现下面这种情况:

        String dbName = "mysql";        if(dbName.equals("mysql")){            //实例化mysql相关的类        }else if(dbName.equals("oracle")){            //实例化oracle相关的类        }

可能会有更加硬编码的写法,如下:

//在客户端代码中Driver driver = newe OracleDriver(xxx);conn = driver.getConnection(url,username,password);

那么当有一天我们接到一个需求,只能使用mysql数据库,那么这是灾难性的。因为我们需要找出代码中所有实例化Oracle相关类的地方,将其替换成mysql的相关类。而由于mysql和oracle的差别,可能要修改的东西更加多。我们想要的显然的是只需要换一个数据库类型就不用改代码了。那么我们修改一下代码改成如下:

Driver driver = DriverFactory.getDriver();conn = driver.getConnection(url,username,password);

此时,我们只需要修改getDriver方法即可了。客户端全局的代码并不需要修改,这就是解耦。也体现了解耦的好处。所以客户端代码不要和一个具体的类相关联。但是这样也有缺陷,我们虽然修改方便了,但是违背了开闭原则,怎么样才能在切换数据库的时候不修改这个工厂类呢?
很简单,将代码改成如下:

String dbName = "mysql";Driver driver = DriverFactory.getDriver("mysql");conn = driver.getConnection(url,username,password);public Driver getDriver(String dbName){        if(dbName.equals("mysql")){            //实例化mysql相关的类            return ...        }else if(dbName.equals("oracle")){            //实例化oracle相关的类            return ...        }}

通过传参的方式来保证我们遵循开闭原则。但是这样也有问题,因为我们这样仅仅解决了切换的问题,并没有考虑增加数据库的情况。比如说我们这时候需要添加一种数据库类型,如sql server。那么我们又得修改getDriver(xxx)方法(添加一条if语句)。这又违背了开闭原则。那么我们怎么做到不用if,switch等语句就实现分支判断呢?
这里就要用到反射了。代码如下:

public Driver getDriver(String dbName){        //dbName是类的全路径名        Driver driver = (Driver)Class.forName("dbName").newInstance();        return driver;}

这样就可以根据需要动态切换和添加数据库类型,而不用修改原有的代码。这就是反射的最大的应用之一。
这里的代码并不是合理的java代码,我只是为了讲解反射的好处而假设代码这么写。这里并没有提供反射相关的细节,仅仅是提供了一种动态编程的思路。
一个反射的demo

package com.wsy.testReflect;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;/** * 测试java反射 * @author shuyweng * */public class TestReflect {    private String fieldOne;    private String fieldTwo;    public String getFieldOne() {        return fieldOne;    }    public void setFieldOne(String fieldOne) {        this.fieldOne = fieldOne;    }    public String getFieldTwo() {        return fieldTwo;    }    public void setFieldTwo(String fieldTwo) {        this.fieldTwo = fieldTwo;    }    public TestReflect(String test){        System.out.println("测试~");    }    //反射的类必须要有无参的构造方法    public TestReflect(){        System.out.println("测试~");    }    //测试invoke方法~    public void test(String param){        System.out.println("反射测试:" + param);    }    /**     * @param args     * @throws ClassNotFoundException     * @throws InstantiationException     * @throws IllegalAccessException     */    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {        // InstantiationException 使用反射的时候不存在带参数        Class<?> c1 = null;        Object obj = null;        // 利用反射获得一个类的类        c1 = Class.forName("com.wsy.testReflect.TestReflect");        // 获取类的全限定名儿        System.out.println(c1.getName());        //获得一个类的构造方法        Constructor<?> cons[] = c1.getConstructors();        //getDeclaredField是可以获取一个类的所有字段.         //getField只能获取类的public 字段.        Field[] fields = c1.getDeclaredFields();        Method[] method = c1.getMethods();        for(int i = 0; i < cons.length; i++){            System.out.println("构造方法:" + cons[i].getName());            System.out.println("属性:" + fields[i].getName());            //普通方法,包含wait            System.out.println("普通方法:" + method[i].getName());        }        try {            obj = c1.newInstance();            Method testMethod = c1.getMethod("test", String.class);            //java.lang.IllegalArgumentException 不合法参数            testMethod.invoke(obj, "String");            //Field field = c1.getDeclaredField("fieldOne");            Field field = obj.getClass().getDeclaredField("fieldOne");            System.out.println("获得属性成功");            //指定私有属性可以被外部访问~            field.setAccessible(true);            field.set(obj, "test");            //在通过反射获得一个对象的属性的时候可以通过field.get方法来获得其属性的值,其中get的参数支持Object            System.out.println("测试属性:" + field.get(obj));        } catch (Exception e) {            e.printStackTrace();        }    }}
原创粉丝点击