简单模拟Java中反射的应用场景

有人说Java是一门静态语言。那么何为静态语言,动态语言又是什么?

1、动态语言
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以 被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运 行时代码可以根据某些条件改变自身结构。 主要动态语言:Object-C、C#、JavaScript、PHP、Python、Erlang。
2、静态语言
与动态语言相对应的,运行时结构不可变的语言就是静态语言。如C、 C++。

Java不是动态语言,但也不能简单的说成静态语言。Java可以称之为“准动态语言”。即Java有一定的动 态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。
Java的动态性让编程的时候更加灵活!

因此我们可以通过反射,将Java“变成”动态语言,即可以通过反射改变程序运行时的结构。举一个简单的例子模拟一下:

package day_12_30.reflection_test;

import day_12_30.reflection.java.Person;

import java.util.Date;
import java.util.Random;

/**
 * @author soberw
 * @Classname RefTest
 * @Description 体会反射的动态性
 * @Date 2021-12-30 20:56
 */
public class RefTest {

    public static void main(String[] args) throws Exception {
        Random random = new Random();
        for (int i = 0; i < 10; i++) {
            int j = random.nextInt(3);
            Class clazz = switch (j) {
                case 1 -> User.class;
                case 2 -> Date.class;
                default -> Person.class;
            };
            Object o = clazz.getDeclaredConstructor().newInstance();
            System.out.println(o);
        }
    }
}

class User {
    private String name;
    private int age;
    public User() {}
    {
        this.age = 18;
        this.name = "wang";
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

在这里插入图片描述
在程序跑起来之前谁也不知道程序会造哪个类的对象。

下面我再简单举例两个Java的反射机制在实际开发中的应用场景,帮助大家更好的掌握反射:
例如我们在连接数据库的时候,随着需求的改变,可能今天会用到MySQL,明天又用到Oracle了,那我们总不能频繁的去创建对象,多麻烦。这时候我们就可以用反射的机制去设计实现,简单举例一下:
我有一个连接接口:

public interface Connect {
    /**
     * 保存数据库文件
     */
    void safe();
}

有两个实现类:

public class MysqlConnect implements Connect{
    @Override
    public void safe() {
        System.out.println("数据保存在mysql中...");
    }
}

public class OracleConnect implements Connect{
    @Override
    public void safe() {
        System.out.println("数据保存在oracle中...");
    }
}

现在我想随时去切换调用哪个数据库,只需要声明一个文件,保存全类名,这里命名为:bean.properties
在这里插入图片描述
在这里插入图片描述
然后我再创建一个工厂类,通过工厂类去调用数据库连接:

package day_12_31.mybean;

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Properties;

/**
 * @author soberw
 * @Classname MyBeanFactory
 * @Description
 * @Date 2021-12-31 9:00
 */
public class MyBeanFactory {
    private static Properties props;
    private static String className;
    static{
        String path = MyBeanFactory.class.getClassLoader().getResource("data/bean.properties").getPath();
        props = new Properties();
        try {
            props.load(new FileInputStream(path));
        } catch (IOException e) {
            e.printStackTrace();
        }
        className = props.getProperty("user");
    }

    public static Object getBean(){
        Class clazz = null;
        try {
            clazz = Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        try {
            return clazz.getDeclaredConstructor().newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }
}

测试如下:

public class FactoryTest {
    public static void main(String[] args) {
        Object bean = MyBeanFactory.getBean();
        Connect c = (Connect) bean;
        c.safe();
    }
}

在这里插入图片描述
此时如果想改为mysql连接,只需在配置文件里改就行了:
在这里插入图片描述
当然方法远不止这一种,我们都知道,注解在Java高级部分也是非常重要的,很多框架大量运用到了注解,比如JPA是基于注解的,Spring2.5以上都是基于注解的,甚至在一定程度上来说:框架 = 注解 + 反射 + 设计模式。
而且使用注解比配置文件会更高效,满足了工厂设计模式的高内聚低耦合的性质。
下面我就简单模拟一下:
首先声明一个注解类:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "";
}

编写新的工厂类:

package day_12_31.mybean;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * @author soberw
 * @Classname AnnotationFactory
 * @Description
 * @Date 2021-12-31 10:00
 */
public class AnnotationFactory {
    private static String packageName = "day_12_31.mybean";
    private static List<String> list = new ArrayList<>();

    static {
        StringBuilder path = new StringBuilder(AnnotationFactory.class.getClassLoader().getResource("").getPath());
        String[] split = packageName.split("[.]");
        for (String s : split) {
            path.append(s).append("/");
        }
        File file = new File(path.toString());
        File[] files = file.listFiles();
        for (File f : files) {
            String s = f.getName().substring(0, f.getName().lastIndexOf("."));
            String className = packageName + "." + s;
            list.add(className);
        }
    }

    public static Object getBean() {
        for (String s : list) {
            try {
                if (Class.forName(s).isAnnotationPresent(MyAnnotation.class)) {
                    return Class.forName(s).getDeclaredConstructor().newInstance();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

测试一下:

package day_12_31.mytest;

import day_12_31.mybean.AnnotationFactory;
import day_12_31.mybean.Connect;

/**
 * @author soberw
 * @Classname AnnnotationFactoryTest
 * @Description
 * @Date 2021-12-31 9:59
 */
public class AnnotationFactoryTest {
    public static void main(String[] args) {
        Object bean = AnnotationFactory.getBean();
        if (bean != null) {
            Connect c = (Connect) bean;
            c.safe();
        }else {
            System.out.println("数据保存失败...");
        }
    }
}

在这里插入图片描述
我们想要连接哪个数据库,就在连接类上添加注解就行了,也不用再去修改配置文件了:
在这里插入图片描述
那么这就是Java中反射的应用场景,当然,在实际中,其应用场景会更加广泛,所以,熟练掌握反射,在实际开发中会更加得心应手!

posted @ 2022-02-09 19:44  soberw-  阅读(84)  评论(0编辑  收藏  举报