热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

反射与自定义注解

反射什么是反射Oracle官方对反射的解释:通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。程序中一般的对象的类型都是在编译期就确定下来的,而Java反射

反射


什么是反射

Oracle官方对反射的解释:

通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。程序中一般的对象的类型都是在编译期就确定下来的,而 Java 反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。


反射的核心

Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息

Java属于先编译后执行从.java文件->.class文件->通过类加载机制加载需要用到的类,用不到的就不会被加载到JVM,通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。

使用反射机制可以动态获取当前class的信息 比如方法的信息、注解信息、方法的参数、属性等


反射机制的优缺点

在一个类中 定义了一个私有属性/方法,但是使用反射能使所有属性都访问到,会破解私有属性

1、优点:在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。

2、缺点:

(1)反射会消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么就不需要用反射

(2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题


反射的应用场景

反编译:.class-->.java

1、通过反射机制访问java对象的属性,方法,构造方法等

2、JDBC加载驱动连接 class.forname

Class.forName("com.mysql.jdbc.Driver"); // 动态加载mysql驱动

3、Spring容器框架IOC实例化对象


4、 自定义注解生效(反射+Aop)

5、 第三方核心的框架 mybatis orm


反射技术的使用

Class类 代表类的实体,在运行的Java应用程序中表示类和接口

Field类 代表类的成员变量(成员变量也称为类的属性)

Method类 代表类的方法

Constructor类 代表类的构造方法

1.getFieldgetMethodgetCostructor方法可以获得指定名字的域、方法和构造器。

2.getFieldsgetMethodsgetCostructors方法可以获得类提供的public域、方法和构造器数组,其中包括超类的共有成员。

3.getDeclatedFieldsgetDeclatedMethodsgetDeclaredConstructors方法可以获得类中声明的全部域、方法和构造器,其中包括私有和受保护的成员,但不包括超类的成员。


反射机制使用三种方式创建对象



  • 通过new出来的对象获取class

    UserEntity userEntity = new UserEntity();
    Class userClass = userEntity.getClass();
    // 默认执行无参构造函数
    UserEntity user2 = (UserEntity) userClass.newInstance();
    System.out.println(user2==userEntity);//false


  • 直接获取class

    Class userClass = UserEntity.class;
    UserEntity user2 = (UserEntity) userClass.newInstance();
    System.out.println(user2);


  • 通过完整类名获取class(常用

    Class aClass = Class.forName("com.ylc.entity.UserEntity");
    UserEntity user3 = (UserEntity) aClass.newInstance();
    System.out.println(user3);


运行期间,一个类,只有一个Class对象产生


反射执行构造函数

无参构造函数

Class userClass = Class.forName("com.ylc.entity.UserEntity");
UserEntity userEntity1 = (UserEntity) userClass.newInstance();
System.out.println(userEntity1);

有参构造函数

Class userClass = Class.forName("com.ylc.entity.UserEntity");
Constructor declaredConstructor1 = userClass.getDeclaredConstructor(String.class, Integer.class);
UserEntity ylc = (UserEntity)declaredConstructor1.newInstance("ylc", 22);
System.out.println(ylc.toString());

反射遍历属性并赋值

反射执行给公有属性赋值

getFields方法只能访问到类的公有属性

image-20211024210820630

Class userClass = Class.forName("com.ylc.entity.UserEntity");
Field[] fields = userClass.getFields();
for (Field field : fields) {
System.out.println(field);
}

image-20211024210900255

反射执行给私有属性赋值

getDeclaredFields可以访问到类的所有属性

Class userClass = Class.forName("com.ylc.entity.UserEntity");
Field[] fields = userClass.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}

image-20211024211041479

反射给属性赋值

public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
Class userClass = Class.forName("com.ylc.entity.UserEntity");
UserEntity userEntity = (UserEntity)userClass.newInstance();
//查找到属性
Field pubUserName = userClass.getDeclaredField("pubUserName");
//指定给哪个userEntity对象赋值
pubUserName.set(userEntity,"ylc");
System.out.println(userEntity.pubUserName);
}

这是给公有属性赋值,默认只能访问公有属性,如果要访问私有属性,会报错

image-20211024212217671

反射没有权限访问私有属性,如果需要访问需要设置权限setAccessible

public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
Class userClass = Class.forName("com.ylc.entity.UserEntity");
UserEntity userEntity = (UserEntity)userClass.newInstance();
Field pubUserName = userClass.getDeclaredField("userName");
//设置权限
pubUserName.setAccessible(true);
pubUserName.set(userEntity,"ylc");
System.out.println(userEntity.getUserName());
}

反射调用方法

反射调用公有方法

Class aClass = Class.forName("com.ylc.entity.UserEntity");
UserEntity userEntity = (UserEntity) aClass.newInstance();
Method mayikt = aClass.getDeclaredMethod("mayikt");
//执行方法
mayikt.invoke(userEntity);

反射调用私有方法,需要开启访问权限,不然访问不到

public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
Class userClass = Class.forName("com.ylc.entity.UserEntity");
UserEntity o = (UserEntity)userClass.newInstance();
Method method = userClass.getMethod("HelloWorld");
Object invoke = method.invoke(o);
}

image-20211024213446331

userName.setAccessible(true);

反射调用方法传递参数

private Integer sum(Integer a, Integer b) {
return a + b;
}

public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
Class userClass = Class.forName("com.ylc.entity.UserEntity");
UserEntity o = (UserEntity)userClass.newInstance();
//调用方法传递参数
Method method = userClass.getDeclaredMethod("sum",Integer.class,Integer.class);
method.setAccessible(true);
Integer result = (Integer)method.invoke(o,1,2);
System.out.println(result);
}

image-20211024214807151


通过反射越过泛型检查

泛型验证是在编译期,这时添加一个int类型会报错

image-20211024221117446

编译器编译之后,通过class文件,可以在程序中通过反射破解

ArrayList arrayList = new ArrayList<>();
arrayList.add("ylc");
Class aClass = arrayList.getClass();
Method addMethod = aClass.getDeclaredMethod("add", Object.class);
addMethod.invoke(arrayList, 1);
System.out.println(arrayList);

注解


注解概念

注解用来给类声明附加额外信息,可以标注在类、字段、方法等上面,编译器、JVM以及开发人员等都可以通过反射拿到注解信息,进而做一些相关处理


常用注解

@Override 只能标注在子类覆盖父类的方法上面,有提示的作用

@Deprecated 标注在过时的方法或类上面,有提示的作用

@SuppressWarnings("unchecked") 标注在编译器认为有问题的类、方法等上面,用来取消编译器的警告提示,警告类型有serial、unchecked、unused、all


元注解

元注解用来在声明新注解时指定新注解的一些特性

@Target 指定新注解标注的位置,比如类、字段、方法等,取值有ElementType.Method等

@Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD})

@Retention 指定新注解的信息保留到什么时候,取值有RetentionPolicy.RUNTIME等

@Inherited 指定新注解标注在父类上时可被子类继承


注解的Target

TYPE:类、接口(包括注解类型)和枚举的声明

FIELD:字段声明(包括枚举常量)

METHOD:方法声明

PARAMETER:参数声明

CONSTRUCTOR:构造函数声明

LOCAL_VARIABLE:本地变量声明

ANNOTATION_TYPE:注解类型声明

PACKAGE:包声明

TYPE_PARAMETER:类型参数声明,JavaSE8引进,可以应用于类的泛型声明之处

TYPE_USE:JavaSE8引进,此类型包括类型声明和类型参数声明


获取注解信息

注解方法

@DiyName
private void HelloWorld() {
System.out.println(" ...ylc.....");
}

自定义注解

@Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface DiyName {
}

获取当前方法上的注解

public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException {
Class userClass = Class.forName("com.ylc.entity.UserEntity");
Object o = userClass.newInstance();
Method diyMethod = userClass.getDeclaredMethod("HelloWorld");
DiyName diyName = diyMethod.getDeclaredAnnotation(DiyName.class);
System.out.println(diyName);
}

image-20211024223823580



推荐阅读
  • 实体映射最强工具类:MapStruct真香 ... [详细]
  • 本文详细探讨了Java中的ClassLoader类加载器的工作原理,包括其如何将class文件加载至JVM中,以及JVM启动时的动态加载策略。文章还介绍了JVM内置的三种类加载器及其工作方式,并解释了类加载器的继承关系和双亲委托机制。 ... [详细]
  • 2017-2018年度《网络编程与安全》第五次实验报告
    本报告详细记录了2017-2018学年《网络编程与安全》课程第五次实验的具体内容、实验过程、遇到的问题及解决方案。 ... [详细]
  • 2023年京东Android面试真题解析与经验分享
    本文由一位拥有6年Android开发经验的工程师撰写,详细解析了京东面试中常见的技术问题。涵盖引用传递、Handler机制、ListView优化、多线程控制及ANR处理等核心知识点。 ... [详细]
  • 深入了解 Windows 窗体中的 SplitContainer 控件
    SplitContainer 控件是 Windows 窗体中的一种复合控件,由两个可调整大小的面板和一个可移动的拆分条组成。本文将详细介绍其功能、属性以及如何通过编程方式创建复杂的用户界面。 ... [详细]
  • Startup 类配置服务和应用的请求管道。Startup类ASP.NETCore应用使用 Startup 类,按照约定命名为 Startup。 Startup 类:可选择性地包括 ... [详细]
  • 深入解析Java虚拟机(JVM)架构与原理
    本文旨在为读者提供对Java虚拟机(JVM)的全面理解,涵盖其主要组成部分、工作原理及其在不同平台上的实现。通过详细探讨JVM的结构和内部机制,帮助开发者更好地掌握Java编程的核心技术。 ... [详细]
  • 深入理解Java多线程并发处理:基础与实践
    本文探讨了Java中的多线程并发处理机制,从基本概念到实际应用,帮助读者全面理解并掌握多线程编程技巧。通过实例解析和理论阐述,确保初学者也能轻松入门。 ... [详细]
  • 本文介绍了如何使用 Spring Boot DevTools 实现应用程序在开发过程中自动重启。这一特性显著提高了开发效率,特别是在集成开发环境(IDE)中工作时,能够提供快速的反馈循环。默认情况下,DevTools 会监控类路径上的文件变化,并根据需要触发应用重启。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 本文详细介绍了 Dockerfile 的编写方法及其在网络配置中的应用,涵盖基础指令、镜像构建与发布流程,并深入探讨了 Docker 的默认网络、容器互联及自定义网络的实现。 ... [详细]
  • 深入解析Spring Cloud Ribbon负载均衡机制
    本文详细介绍了Spring Cloud中的Ribbon组件如何实现服务调用的负载均衡。通过分析其工作原理、源码结构及配置方式,帮助读者理解Ribbon在分布式系统中的重要作用。 ... [详细]
  • 深入解析 Apache Shiro 安全框架架构
    本文详细介绍了 Apache Shiro,一个强大且灵活的开源安全框架。Shiro 专注于简化身份验证、授权、会话管理和加密等复杂的安全操作,使开发者能够更轻松地保护应用程序。其核心目标是提供易于使用和理解的API,同时确保高度的安全性和灵活性。 ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
author-avatar
陈醉在线wx
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有