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

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();
        }
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值