Java反射(Reflex)机制

news2024/9/22 17:58:18

反射概述

Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射
在这里插入图片描述

优缺点

  • 优点:可以实现动态创建对象和编译,体现很大的灵活性。
  • 缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作。

反射相关API

  1. java.lang.Class:代表一个类
  2. java.lang.reflect.Method:代表类的方法
  3. java.lang.reflect.Field:代表类的成员变量
  4. java.lang.reflect.Constructor:代表类的构造器

反射机制提供的功能

  • 在运行时处理注解
  • 在运行时获取泛型
  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 生成动态代理

反射相关知识点

Class类

  • 在Object类中定义了getClass方法,此方法将被所有的子类继承。
    在这里插入图片描述

  • 这个方法返回值的类型是一个Class类,这个类是Java反射的源头。

  • 实际上所谓的反射:从程序的运行结果来看,就是可以通过对象反射求出类的名称。

Class类特点

● Class本身也是一个类。
● Class对象只能由系统建立对象。
● 一个加载的类在JVM中只会有一个Class实例。
● 一个Class对象对应的是一个加载到JVM中的一个.class文件。
● 每个类的实例都会记得自己是由哪个Class实例所生成。
● 通过Class可以完整地得到一个类中的所有被加载的结构。
● Class类是Reflection的根源,针对任何你想动态加载、运行的类、唯有先获得相应的Class对象。

Class类的常用方法

在这里插入图片描述

获取Class类的几种方式

  1. 若已知具体的类,可以通过类的class属性获取,该方法最为安全可靠,程序性能最高。 Class clazz = Person.class;
  2. 若已知某个类的实例,调用该实例(对象)的getClass方法获取Class对象。 Class clazz = person.getClass();
  3. 若一致一个类的全限定类名,且该类的类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException异常。
    Class clazz = Class.forName("com.demo.entity.User");
  4. 内置基本数据类型可以直接用类名.Type获得。
  5. 还可以利用ClassLoader获得。
    代码示例
public class ClassCreate {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person = new Student();
        System.out.println("这个人是:" + person.name);

        //方式一:通过对象获得
        Class<? extends Person> c1 = person.getClass();
        System.out.println(c1.hashCode());

        //方式二:forname获得
        Class<?> c2 = Class.forName("bjpowernode.annotationAndReflex.Student");
        System.out.println(c2.hashCode());

        //方式三:通过类名.class获得
        Class<Student> c3 = Student.class;
        System.out.println(c3.hashCode());

        //方式四,基本内置类型的包装类都有一个Type属性
        Class<Integer> c4 = Integer.TYPE;
        System.out.println(c4);

        //获得父类类型
        Class<?> c5 = c1.getSuperclass();
        System.out.println(c5);
    }
}

class Person{
    String name;
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    public Person() {}
    public Person(String name) {this.name = name;}
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}
class Student extends Person{
    public Student(){
        this.name="学生";
    }
}
class Teacher extends Person{
    public Teacher(){
        this.name="老师";
    }
}

哪些类型可以有class对象

在这里插入图片描述
代码示例

public class ClassType {
    public static void main(String[] args) {
        Class c1 = Object.class; //类
        Class c2 = Comparable.class; //接口
        Class c3 = String[].class; //一维数组
        Class c4 = int[][].class; //二维数组
        Class c5 = Override.class; //注解
        Class c6 = ElementType.class; //枚举
        Class c7 = Integer.class; //基本数据类型
        Class c8 = void.class; //void,空类型
        Class c9 = Class.class; //class

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);

        //只要元素类型与维度一样,就是同一个Class
        int[] a = new int[10];
        int[] b = new int[100];
        System.out.println(a.getClass().hashCode());
        System.out.println(b.getClass().hashCode());
    }
}

在这里插入图片描述
在这里插入图片描述

有了Class对象,我们可以做什么?

  1. 创建类的对象:调用Class对象的newlnstance()方法。
    ○ 类必须有一个无参的构造器。
    ○ 类的构造器的访问权限需要足够。
  2. 没有无参构造器,是否可以创建对象?可以
    ○ 通过CLass类的getDeclaredConstructor(Class…parameter)取得本类的指定形参类型的构造器。
    ○ 向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
    ○ 通过Constructor实例化对象。

代码示例

public class Test01 {
    public static void main(String[] args) throws Exception {
        // 获得Class对象
        Class<?> c1 = Class.forName("bjpowernode.annotationAndReflex.User");

        // 构造一个对象
        //User user = (User) c1.newInstance();//本质是调用了类的无参构造器

        // 通过构造器创建对象
        //Constructor<?> constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        //User user = (User) constructor.newInstance("张三", 001, 18); //有参构造

        // 通过反射调用普通方法
        User user = (User) c1.newInstance();
        // 通过反射获取一个方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        // invoke();激活,第一个参数传递对象,第二个参数方法的值
        setName.invoke(user,"张三");
        System.out.println(user.getName());

        //通过反射操作属性
        User u = (User) c1.newInstance();
        Field name = c1.getDeclaredField("name");

        // 不能直接操作私有属性,需要关闭程序的安全检测,属性或方法的setAccessible值设为true
        name.setAccessible(true);
        name.set(u,"三儿");
        System.out.println(u.getName());
    }
}

调用指定的方法

通过反射,调用类中的方法,通过Method类完成。
1、通过Class类的getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
2、之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。
在这里插入图片描述
Object invoke(Object obj, Object … args)

Object对应原方法的返回值,若原方法无返回值,此时返回null
若原方法若为静态方法,此时形参Object obj可为null
若原方法形参列表为空,则Object[] args为null
若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。

SetAccessible()方法
在这里插入图片描述

性能分析

public class Performance {
    //普通方式调用
    public static void test01() {
        User user = new User();
        
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            user.getName();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("普通方式执行10亿次:" + (endTime - startTime) + "ms");
    }
    
    //反射方式调用
    public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user = new User();
        Class c1 = user.getClass();
        
        Method getName = c1.getDeclaredMethod("getName", null);
        
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user,null);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("反射方式执行10亿次:" + (endTime - startTime) + "ms");
    }
    
    //反射方式调用,关闭检测
    public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user = new User();
        Class c1 = user.getClass();
        
        Method getName = c1.getDeclaredMethod("getName", null);
        getName.setAccessible(true);
        
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user,null);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("反射方式执行10亿次,关闭检测:" + (endTime - startTime) + "ms");
   }
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        test01(); //9ms
        test02(); //5699ms
        test03(); //1959ms
    }
}

反射操作泛型

Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除。
为了通过反射操作这些类型,Java新增了ParameterizedType , GenericArrayType ,TypeVariable和 WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型.

ParameterizedType:表示一种参数化类型,比如Collection< String>,简单理解就是带<>的参数(带泛型)
GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
TypeVariable:是各种类型变量的公共父接口
WildcardType:代表一种通配符类型表达式

通过反射获取泛型

public class GenericParadigm {
    public void test01(Map<String, User> map, List<User>list) {
        System.out.println("test01");
    }
    public Map<String, User> test02() {
        System.out.println("test02");
        return null;
    }
    public static void main(String[] args) throws NoSuchMethodException {
        //根据反射获取方法
        Method method = Demo13_Generic.class.getMethod("test01", Map.class, List.class);
        
        //获取方法的参数
        Type[] genericExceptionTypes = method.getGenericParameterTypes();
        for (Type genericExceptionType : genericExceptionTypes) {
            System.out.println("#" + genericExceptionType);
            //判断参数是否是参数化类型
            if (genericExceptionType instanceof ParameterizedType){
                //强转后获取参数化类型
                Type[] actualTypeArguments = ((ParameterizedType) genericExceptionType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }
            }
        }
        
        System.out.println("====================================");
        Method method2 = Demo13_Generic.class.getMethod("test02",null);
        
        //获取返回值的类型
        Type genericReturnType = method2.getGenericReturnType();
        if (genericReturnType instanceof ParameterizedType){
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println(actualTypeArgument);
            }
        }
    }
}

反射操作注解

getAnnotations、getAnnotation
了解什么是ORM? Object relationship Mapping -->对象关系映射
需求:利用注解和反射完成类和表结构的映射关系

public class Test_ORM {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("cn.doris.reflection.Student2");
        
        //通过反射获取注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
        
        //获得注解value
        TableDoris tableDoris = (TableDoris) c1.getAnnotation(TableDoris.class);
        String value = tableDoris.value();
        System.out.println(value);
        
        //获得类指定的注解
        Field name = c1.getDeclaredField("name");
        FiledDoris annotation = name.getAnnotation(FiledDoris.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
    }
}
@TableDoris("db_student")
class Student2 {
    @FiledDoris(columnName = "db_id", type = "int", length = 10)
    private int id;
    @FiledDoris(columnName = "db_age", type = "int", length = 3)
    private int age;
    @FiledDoris(columnName = "db_name", type = "varchar", length = 200)
    private String name;
    public Student2() {
    }
    public Student2(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }
    @Override
    public String toString() {
        return "Student2{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
//类名注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableDoris {
    String value();
}
//属性注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FiledDoris {
    String columnName();
    String type();
    int length();
} 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/32858.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【linux】linux实操篇之软件包管理

前言 关于这一快软件包管理知识点不多&#xff0c;我们主要认识一下rpm和yum这两个软件包管理工具&#xff0c;主要还是yum的使用&#xff01; rpm 包的管理 一种用于互联网下载包的打包及安装工具&#xff0c;它包含在某些 Linux 分发版中。它生成具有 .rpm 扩展名的文件。…

QQ plot 的解读

QQ plot全称是Quantile-Quantile Plot&#xff0c;分位数-分位数图是通过比较两个概率分布的分位数对这两个概率分布进行比较的概率图方法。 这个图形的形式非常简单&#xff0c;有点类似RNA-seq中评价两个样本相关性的散点图&#xff08;图1&#xff09;。这类图形为什么那么…

Java小技能:多级菜单排序并返回树结构菜单列表

文章目录 引言I 生成树形结构菜单列表1.1 获取全部菜单1.2 获取一级菜单,递归获取子节点。1.3 实体1.4 DtoII 常见问题2.1 no instance(s) of type variable(s) R exist so that void conforms to R,2.2 集合filter过滤Integer数值为空问题解决方案引言 需求: 服务商角色配置…

面试官:说说你了解的分布式 ID 生成方案

为什么需要分布式 ID 对于单体系统来说&#xff0c;主键 ID 常用主键自动的方式进行设置。这种 ID 生成方法在单体项目是可行的&#xff0c;但是对于分布式系统&#xff0c;分库分表之后就不适应了。比如订单表数据量太大了&#xff0c;分成了多个库&#xff0c;如果还采用数据…

创业可以做什么项目,六个轻资产创业项目推荐

​2022年已经接近尾声了&#xff0c;你有为下一年做好计划嘛&#xff0c;今年并不是平静的一年&#xff0c;口罩总是反反复复的出现&#xff0c;很多人萌生了创业的想法&#xff0c;那有没有不怕口罩的创业项目呢&#xff1f;虽然创业并不容易&#xff0c;但当你有了正确的方向…

【优化充电】粒子群算法电动汽车充电动态优化策略【含Matlab源码 2163期】

⛄一、粒子群算法电动汽车充电优化 1 电动汽车充电负荷估算 电动汽车的充电负荷主要与电动汽车起始充电时刻和充电时长相关,而起始充电时刻是由电动汽车用户的到家时间决定的,充电时长主要与电动汽车的行驶里程和充电倍率相关。 目前电动汽车还没有大规模运营, 只能通过统计燃…

笔试强训day1

一、选择题 第一题&#xff1a; 题解&#xff1a;y123返回值为123&#xff0c;是非零数&#xff0c;所以y123总是正确&#xff0c;因此循环条件由x<4控制&#xff0c;答案 为C 第二题&#xff1a; %5表示输出总共占据了五列&#xff0c;%.3表示只取字符串左边三个字符并且…

傻白入门芯片设计,wafer/die/chip/cell(一)

1.wafer&#xff1a; 晶圆&#xff0c;指一整个圆形的晶圆硅片。如果问及CPU的原料是什么&#xff0c;大家都会轻而易举的给出答案—是硅。这是不假&#xff0c;但硅又来自哪里呢&#xff1f;其实就是那些最不起眼的沙子。不过不是随便抓一把沙子就可以做原料的&#xff0c;一定…

HTTP 协议

1 HTTP 协议的介绍 HTTP&#xff08;Hyper Transfer Protocol&#xff09;&#xff1a;超文本传输协议HTTP 协议是基于 TCP/IP 协议的超文本&#xff1a;比普通文本更加强大传输协议&#xff1a;客户端和服务器的通信规则&#xff08;握手规则&#xff09; 注意&#xff1a; J…

【计算机毕业设计】21.二手拍卖系统maven源码

一、系统截图&#xff08;需要演示视频可以私聊&#xff09; 引言 近年来&#xff0c;电子商务发展的愈趋成熟使得人们的消费方式以及消费观念发生巨大改变&#xff0c;网上竞拍的拍卖模式随之发展起来。大学拍卖网旨在为湘大学生提供一个线上拍卖的交易平台。平台展示的商品大…

STM32CubeMX时钟树(72MHZ主频配置)

目录 一些基础概念 时钟树配置图 第一步 第二步 这里我只是配置常用的72MHZ主频&#xff0c;很多时候新手都在时钟树这里被劝退了。其实不知道没关系&#xff0c;我用STM32这么久了&#xff0c;也只知道大概。我们绝大多数时候不需要配置这个时钟&#xff0c;记住72MHZ主频…

LeetCode-795-区间子数组个数

1、双指针 根据题意&#xff0c;我们可以将数组中的数分为这三类&#xff1a;1、小于leftleftleft的数&#xff1b;2、大于等于leftleftleft且小于等于rightrightright的数&#xff1b;3、大于rightrightright的数。因此若我们使用双指针来维护区间[last2,last1][last2,last1]…

Nacos与Eureka中的高性能读写并发架构设计

Nacos、Eureka都是微服务领域内熟知、常用的注册中心组件。只不过呢&#xff0c;Nacos还多了个功能身份就是配置中心。从目前流行与随着Spring Cloud Alibaba发展来看&#xff0c;Nacos使用得更加多&#xff0c;也是趋势所在。 注册中心原理 注册中心原理其实很简单&#xff…

【18】Java常见的面试题汇总(Spring/Spring MVC)

目录 1. 为什么要使用 spring&#xff1f; 2. 解释一下什么是 aop&#xff1f; 3. 解释一下什么是 ioc&#xff1f; 4. spring 有哪些主要模块&#xff1f; 5. spring 常用的注入方式有哪些&#xff1f; 6. spring 中的 bean 是线程安全的吗&#xff1f; 7. spring 支持…

【Java八股文总结】之外卖平台项目整理

文章目录一、项目介绍1.1 项目整体介绍1.2 主要模块介绍二、项目开发2.1 后台管理系统开发2.1.1 员工管理employee1、员工后台登录2、员工退出3、过滤器4、新增员工5、员工信息分页查询↑&#xff08;参加上面&#xff09;6、修改员工信息7、根据id查询员工信息&#xff0c;回显…

后台开发的学习日记

后台开发的学习日记 Java后台开发的日记&#xff1a;Push一下自己每天都要学习后台 后台开发学习日志-Day1后台开发的学习日记Day1: 路线及资料的汇总一、应该选择什么语言&#xff1f;二、学习路线的规划及资料的汇总整理Day1: 路线及资料的汇总 第一天主要是路线的准备及资料…

Pytorch学习笔记(二)官方60min入门教程之自动微分

目录 一.相关包及函数介绍 二.雅各比向量积 三.练习代码 一.相关包及函数介绍 autograd 包是 PyTorch 中所有神经网络的核心。首先让我们简要地介绍它&#xff0c;然后我们将会去训练我们的第一个神经网络。该 autograd 软件包为 Tensors 上的所有操作提供自动微分。它是一…

显卡天梯图2022年11月新版 显卡性能排行榜天梯图

1 RTX 3090Ti 2 RTX 3090 3 RX 6900 XT水冷版 我用的显卡就是活动时8折抢购的太划算了 http://www.adiannao.cn/dq 4 RTX 3080 Ti 5 RX 6900 XT 6 Titan RTX 7 RTX 3080 8 RX 6800 XT 9 RX 6800 10 RTX 3070 Ti

C++数据结构X篇_01_数据结构的基本概念

从本篇开始学习数据结构相关概念。 数据结构的基本概念1 数据结构的相关概念1.1 为什么要学习数据结构1.2 数据结构中的基本概念2 算法2.1 算法的概念2.2 算法和数据结构的区别2.3 算法特性2.4 算法效率的度量2.4.1 事后统计法2.4.2 事前分析估算2.4.3 大O表示法2.4.3.1采用大O…

从事先进计算的工程师对此都有什么感想?

电子计算机最初诞生于二十世纪&#xff0c;体积庞大的初代机型运算能力有限&#xff0c;随着计算技术的升级完善&#xff0c;现在多样小巧的计算机及手机的计算能力呈指数级增长&#xff0c;更是成为人们生活密不可分的综合性助手。 先进计算是在计算的基础上诞生的全新概念&a…