Java-反射

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

反射使Java这种静态编译型的语言具有了动态性。

反射具有看透类的能力,类的信息在反射面前都是透明的(包括private的属性和方法都是可以调用)。

Java反射机制主要提供以下功能:

  1. 在运行时判断任意一个对象所属的类。
  2. 在运行时构造任意一个类的对象。
  3. 在运行时判断任意一个类所具有的成员变量和方法。
  4. 在运行时调用任意一个对象的方法。

框架:反射、泛型、注解

学习反射的意义

反射使我们在编译的时候知道类型,而是延迟到运行时获得对象的属性调用对象的方法,使得Java具有动态性。

Hibernate、Sping、MyBatis都是基于反射来实现的,可以说没有反射就没有这些框架。

在这里插入图片描述

在这里插入图片描述

类比学习一下:面向对象抽象过

程众多的人 ----> Person类

众多学生 -----> Stundent类

众多的类 -----> Class类:任何一个类里面都包含这些东西:Field[]、Constructor[]、Method[]

众多的属性 ------> Field类

众多构造方法 ------> Constructor类

众多的普通方法 ------> Method类

在这里插入图片描述

既然Class是描述类的类的类型,那类结构里面包含哪些东西呢: Field、Constructor、Method,同样这些众多的属性、构造函数、方法也有对应的类类型表示他们。

Java.lang.Class;

Java.lang.reflect.Field;

Java.lang.reflect.Method;

Java.lang.reflect.Constructor;

对于类型的学习我们可以参考做月饼的模子,什么样模子就可以做出什么大小,图案的月饼。对于Java里面的int类型是四个字节,那么这个模子就是一个只能存放四个字节的模子,用这个模子做出来的就是int类型。同理Class、Field、Method、Constructor就分别是类、属性、方法、构造函数的模子。

通过Class可以获得类的所有属性Field[]、方法Method[]、构造方法Constructor[]信息。

通过Field可以获得属性的名字、类型、修饰符

通过Method可以获得方法的名字、参数、返回值。


Class:是反射的核心类

每个类加载到内存后,系统都会有唯一的一份==字节码对象==(Person.class/Student.class字节码对象都是Class这个类的实例)

public void testClass() throws ClassNotFoundException {
   // 1.Class.forName(完整路径,包名+类名)
   Class clazz1 = Class.forName("com.situ.day15.Student");
   // 2.类型.class
   Class clazz2 = Student.class;
   // 3.对象.getClass()
   Student student = new Student();
   Class clazz3 = student.getClass();
   
   System.out.println(clazz1 == clazz2);//true
   System.out.println(clazz1 == clazz3);//true
   System.out.println(clazz2 == clazz3);//true
}

Constructor的反射:

在这里插入图片描述

//Studnet构造函数
public Student() {
    }

    public Student(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
public void test02() {
        Class clazz = Student.class;
        Constructor[] constructors = clazz.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
    }

getConstructors()拿到了所有public的构造函数,打印的结果为构造方法的方法签名signature

在这里插入图片描述

若将以上构造函数其中一个访问级别改为private,则getConstructors()便获取不到:

//Studnet构造函数
public Student() {
    }

    private Student(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

在这里插入图片描述

若采用getDeclaredConstructors(),则所有构造函数都能拿到,包括private修饰的

@Test
public void test03() {
    Class clazz = Student.class;
    Constructor[] constructors = clazz.getDeclaredConstructors();
    for (Constructor constructor : constructors) {
        System.out.println(constructor);
    }
}

在这里插入图片描述

通过constructor.getName()、constructor.getModifiers()获取构造方法的名字和修饰符

@Test
public void test03() {
    Class clazz = Student.class;
    Constructor[] constructors = clazz.getDeclaredConstructors();
    for (Constructor constructor : constructors) {
        System.out.println(constructor);
        System.out.println(constructor.getName());
        System.out.println(constructor.getModifiers());
    }
}

其中1代表public、2代表private

在这里插入图片描述


通过getDeclaredConstructor(<?>…parameterTypes)拿到指定参数的构造函数,包括private修饰的。同时调用newInstance()进行对象的创建。注意此时需要加上constructor.setAccessible(true)来设置该构造方法的访问性,因为该构造方法被private修饰,虽然拿到了,但是不能操纵它,如果想要操纵它就需要对其进行访问性设置,否则会报错:java.lang.IllegalAccessException非法访问异常

@Test
    public void test04() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class clazz = Student.class;
        Constructor constructor = clazz.getDeclaredConstructor(Integer.class, String.class, Integer.class);
        constructor.setAccessible(true);
        Student student = (Student) constructor.newInstance(1, "zhangsan", 23);
        System.out.println(student);
    }

在这里插入图片描述


经典案例:利用反射加配置文件加载指定数据库:

良好的设计规范是在代码中不要出现具体的子类代码,因为一旦改动java代码,就需要重新编译、打包、部署到服务器,成本太高,给开发带来不必要的麻烦。采取反射+配置文件的方式可以很好的解决这个问题,需要更改的时候直接改配置文件就行了

public interface IDB {
    public abstract void getConnection();
}
public class MySql implements IDB{
    @Override
    public void getConnection() {
        System.out.println("MySql.getConnection");
    }
}
public class Oracle implements IDB{
    @Override
    public void getConnection() {
        System.out.println("Oracle.getConnection");
    }
}
public class DBDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //IDB db = new MySql();
        //db.getConnection();
        //在代码里面不出现具体子类代码
        //反射+配置文件 property properties
        //获取配置文件输入流
        FileInputStream fileInputStream = new FileInputStream("JavaSE/src/com/bing/reflect/db.properties");
        Properties properties = new Properties();
        //加载输入流
        properties.load(fileInputStream);
        //通过key拿到类名
        String className = properties.getProperty("className");
        Class clazz = Class.forName(className);
        //无参构造方法
        Constructor constructor = clazz.getConstructor();
        IDB db = (IDB) constructor.newInstance();
        db.getConnection();
    }
}

在这里插入图片描述

如果需要换成Oracle只需修改配置文件即可


在这里插入图片描述


在这里插入图片描述

@Test
    public void testMethod() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Class clazz = Student.class;
        Student student = (Student) clazz.newInstance();
        Method method = clazz.getMethod("setName", String.class);
        method.invoke(student, "lisi");
        System.out.println(student);
    }

在这里插入图片描述


在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值