Java之反射

本文详细介绍了Java的反射机制,包括通过反射获取类信息、字段、方法及构造器,并展示了如何动态创建对象和执行方法。此外,还探讨了Java的动态代理,解释了如何利用反射创建代理对象以增强功能,以及动态代理在方法调用中的作用。最后,通过示例展示了如何通过反射获取注解属性,以用于生成SQL语句。
摘要由CSDN通过智能技术生成

Java是“准动态语言”

  • 动态语言:在运行时可以改变其结构的语言,例如新的函数、对象、甚至代码可以被引进等。比如C#,JavaScript,Python等等。
  • 静态语言:运行时结构不可变的语言。比如,C、C++、Java
  • java虽然是静态语言,但是我们可以称其为“准动态语言”,java具有一定的动态性,我们可以通过反射机制获得类似于动态语言的特性。
  • Class对象,即Class类的一个实例对象,是编译得到的Class文件经过类加载器加载进内存(堆)之后的存在形式。

什么是反射

        反射就是把Java类中的各个成分映射成一个个的Java对象(Fields,Methods,Constructor,Array等类型的对象)。即在运行状态中,对于任意一个类,都能够知道这个类的所以属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性。这种动态获取信息及动态调用对象方法的功能叫Java的反射机制。

package com.jing.annotation;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Test08 {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {

        // 1. 获取class对象
        Class userClass = Class.forName("com.jing.reflection.User");

        // 获取类的信息
        // 2.1 获取类的字段 Declared可以获取全部属性,包括私有的
        Field[] fields = userClass.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("-------------------------");
        // 根据字段名获取特定字段
        Field age = userClass.getDeclaredField("age");
        System.out.println(age);

        System.out.println("======================================");

        // 2.2 获取类的方法 Declared可以获取本类的全部方法,包括私有的
        // getMethods():获取本类及父类的public方法
        Method[] methods = userClass.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("-------------------------");
        // 根据方法名和参数类型获取特定方法
        Method setAge = userClass.getMethod("setAge", int.class);
        System.out.println(setAge);
        Method A = userClass.getMethod("A", null);
        System.out.println(A);

        System.out.println("======================================");

        // 3.1 获取类的构造器
        Constructor[] constructors = userClass.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        
        /*
        程序执行结果:
        
        private java.lang.String com.jing.reflection.User.name
        private int com.jing.reflection.User.age
        -------------------------
        private int com.jing.reflection.User.age
        ======================================
        public java.lang.String com.jing.reflection.User.toString()
        public void com.jing.reflection.User.B()
        public java.lang.String com.jing.reflection.User.getName()
        public void com.jing.reflection.User.setName(java.lang.String)
        public void com.jing.reflection.User.A()
        public void com.jing.reflection.User.setAge(int)
        public int com.jing.reflection.User.getAge()
        -------------------------
        public void com.jing.reflection.User.setAge(int)
        public void com.jing.reflection.User.A()
        ======================================
        public com.jing.reflection.User()
        public com.jing.reflection.User(java.lang.String,int)
        
         */
    }
}

反射的使用

1. 通过反射动态创建对象

package com.jing.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

// 利用反射动态创建对象
public class Test09 {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        // 1. 获得Class对象
        Class c1 = Class.forName("com.jing.reflection.User");

        // 2.1 通过newInstance创建对象,前提:必须有无参构造
        User u1 = (User) c1.newInstance();
        System.out.println(u1);

        System.out.println("=======================================================");

        // 2.2 通过Constructor构造器创建对象
        // 2.2.1 通过有参构造器创建
        Constructor constructor = c1.getDeclaredConstructor(String.class, int.class);
        User u2 = (User) constructor.newInstance(null, 0);  // 必须带参数
        System.out.println(u2);
        System.out.println("-------------------------------------------------------");
        // 2.2.2 通过无参构造器创建
        Constructor constructor1 = c1.getDeclaredConstructor();
        User u3 = (User) constructor1.newInstance();
        System.out.println(u3);
    }
}

/*
User{name='null', age=0}
=======================================================
User{name='null', age=0}
-------------------------------------------------------
User{name='null', age=0}
 */

2. 通过反射动态执行方法(在程序运行过程中,在需要的地方,动态获取方法Method,并执行(为什么不直接调用,对象名.方法,因为在写代码时,还不确定何时何地调用哪个方法);重复利用现有代码,而无需显式定义相同功能的方法)

package com.jing.reflection;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

// 动态执行方法
public class Test10 {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {

        // 1. 获取Class对象
        Class c1 = Class.forName("com.jing.reflection.User");

        // 2. 利用反射创建User对象
        User u1 = (User) c1.newInstance();

        // 3. 利用反射获取方法并执行
        Method A = c1.getDeclaredMethod("A", String.class);
        // 执行私有方法,关闭安全监测
        A.setAccessible(true);
        Object resultA = A.invoke(u1, "YuJing");
        System.out.println("=======================================");

        Method B = c1.getDeclaredMethod("B");
        Object resultB = B.invoke(u1);

        /*
        有参数方法A被执行YuJing
        =======================================
        无参数方法B被执行
         */


        /*
        动态代理:
            · 调用处理器InvocationHandler中的invoke方法,参数:代理对象,Method方法,方法参数;invoke方法被自动调用,来处理代理对象对Method方法的调用
            · 在invoke方法中,method.invoke(目标对象, 参数args),这是一种典型的在程序运行过程中方法被动态获取并执行。
            · 利用这种灵活性,可以实现对基础方法的前后增强。即将方法摘取出来,然后在前后进行扩展,相当于横向切入一些功能,不影响原有功能。
            · 基础方法仍由真实对象负责执行,扩展功能由代理对象完成
            · 这种非调用执行方法的方式,可以实现传递哪个Method对象,就执行哪个Method对象对应的方法。
            · 在程序运行过程中,可以利用反射获取并执行已存在的方法,而无需在自己的代码中再写一份,代码重用性可可扩展性都得到了提高

         */
    }
}

        动态代理只调用处理器:

/**
 * Proxy类:动态获取代理实例
 * InvocationHandler接口:执行代理实例的方法,并返回结果
 *
 * 一个动态代理类代理一类业务(一类业务一般封装到一个接口中),
 * 所以一个动态代理类可以代理多个类,即实现了同一接口的类(因为Proxy是根据接口创建代理实例)
 */

// 通用的调用处理类
public class GeneralInvocationHandler implements InvocationHandler {

    // 被代理的接口
    private Object target;

    public void setTarget(Object target) {
        this.target = target;
    }

    // 调用处理方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(target, args);
    }
}

        动态代理之测试类: 


public class Client {

    public static void main(String[] args) {
        // 1. 创建真实对象
        UserServiceImpl userService = new UserServiceImpl();

        // 2.1 获取调用处理器
        GeneralInvocationHandler invocationHandler = new GeneralInvocationHandler();
        // 2.2 设置具体的被代理的真实对象
        invocationHandler.setTarget(userService);

        // 3. 获取代理对象
        // 所谓代理对象并不是什么复杂的东西,简单理解就是一个类实例
        // 扩展了真实对象的功能,这交给invoke方法实现
        UserService proxy = (UserService) Proxy.newProxyInstance(
                GeneralInvocationHandler.class.getClassLoader(),
                userService.getClass().getInterfaces(), // 代理的接口
                invocationHandler);

        // 4. 执行方法
        proxy.add();
    }
}

3. 通过反射动态操作属性:get/set

package com.jing.reflection;

import java.lang.reflect.Field;

public class Test11 {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
        // 1. 获取Class对象
        Class c1 = Class.forName("com.jing.reflection.User");

        // 2. 利用反射创建User对象
        User u1 = (User) c1.newInstance();

        // 3. 利用反射获取并操作属性,get/set
        Field name = c1.getDeclaredField("name");
        // 关闭安全监测,开启操作私有属性的权限
        name.setAccessible(true);
        name.set(u1, "YuJing");
        String nameValue = (String) name.get(u1);
        System.out.println(nameValue);
        System.out.println(u1.getName());
    }
}
/*
YuJing
YuJing
 */

备注:Constructor、Method、Field都有一个setAccessible()方法,来设置是否关闭安全监测,即是否开启操作私有构造器/方法/属性的权限。在使用反射时,关闭检测可以提高运行效率(如果不关闭,每次操作都会检测,降低程序运行效率)

4. 反射获取泛型

5. 反射操作注解

        演示程序:通过反射获得注解中的属性值,然后根据属性值生成相应的SQL语句,去操作数据库

package com.jing.reflection;

import java.lang.annotation.*;
import java.lang.reflect.Field;

/**
 * 演示:利用反射获取注解
 *
 * 实际应用:
 *      ORM(对象关系映射),将java实体类的类名,属性 映射成 数据库的表名和字段(字段名、字段类型、字段长度)
 *      映射:获取注解,然后获取注解的属性
 *      表名和字段可以用来生成SQL语句
 */
public class Test12 {

    public static void main(String[] args) throws NoSuchFieldException {
        // 1. 获取Class对象
        Class studentClass = Student.class;

        // 2.1 获取类上注解
        Table table = (Table) studentClass.getAnnotation(Table.class);
        // 2.2 获取注解属性——表名
        String value = table.value();
        System.out.println("表名:" + value);

        // 3.1 获取属性上的注解
        Field[] declaredFields = studentClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            String fieldName = declaredField.getName();
            Column column = declaredField.getAnnotation(Column.class);
            System.out.println("属性" + fieldName + ":" );
            System.out.print(column.colName() + " ");
            System.out.print(column.colType() + " ");
            System.out.print(column.length() + " ");
            System.out.println();
        }
    }
}

/*
表名:db_student
属性id:
db_id int 10 
属性name:
db_name varchar 20 
属性age:
db_age int 10
 */

@Table("db_student")
class Student{
    @Column(colName = "db_id", colType = "int", length = 10)
    private int id;
    @Column(colName = "db_name", colType = "varchar", length = 20)
    private String name;
    @Column(colName = "db_age", colType = "int", length = 10)
    private int age;

    public Student() {

    }

    public Student(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
    String value();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Column{
    String colName();
    String colType();
    int length();
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值