热门标签 | 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



推荐阅读
  • 问题描述现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能;在实际开发过程中 ... [详细]
  • 1、编写一个Java程序在屏幕上输出“你好!”。programmenameHelloworld.javapublicclassHelloworld{publicst ... [详细]
  • 本文详细探讨了在Java中如何将图像对象转换为文件和字节数组(Byte[])的技术。虽然网络上存在大量相关资料,但实际操作时仍需注意细节。本文通过使用JMSL 4.0库中的图表对象作为示例,提供了一种实用的方法。 ... [详细]
  • Hibernate全自动全映射ORM框架,旨在消除sql,是一个持久层的ORM框架1)、基础概念DAO(DataAccessorOb ... [详细]
  • 本文探讨了如何在 Spring MVC 框架下,通过自定义注解和拦截器机制来实现细粒度的权限管理功能。 ... [详细]
  • 函子(Functor)是函数式编程中的一个重要概念,它不仅是一个特殊的容器,还提供了一种优雅的方式来处理值和函数。本文将详细介绍函子的基本概念及其在函数式编程中的应用,包括如何通过函子控制副作用、处理异常以及进行异步操作。 ... [详细]
  • H5技术实现经典游戏《贪吃蛇》
    本文将分享一个使用HTML5技术实现的经典小游戏——《贪吃蛇》。通过H5技术,我们将探讨如何构建这款游戏的两种主要玩法:积分闯关和无尽模式。 ... [详细]
  • 使用Matlab创建动态GIF动画
    动态GIF图可以有效增强数据表达的直观性和吸引力。本文将详细介绍如何利用Matlab软件生成动态GIF图,涵盖基本代码实现与高级应用技巧。 ... [详细]
  • 搭建个人博客:WordPress安装详解
    计划建立个人博客来分享生活与工作的见解和经验,选择WordPress是因为它专为博客设计,功能强大且易于使用。 ... [详细]
  • 在1995年,Simon Plouffe 发现了一种特殊的求和方法来表示某些常数。两年后,Bailey 和 Borwein 在他们的论文中发表了这一发现,这种方法被命名为 Bailey-Borwein-Plouffe (BBP) 公式。该问题要求计算圆周率 π 的第 n 个十六进制数字。 ... [详细]
  • 本问题涉及在给定的无向图中寻找一个至少包含三个节点的环,该环上的节点不重复,并且环上所有边的长度之和最小。目标是找到并输出这个最小环的具体方案。 ... [详细]
  • 洛谷 P4009 汽车加油行驶问题 解析
    探讨了经典算法题目——汽车加油行驶问题,通过网络流和费用流的视角,深入解析了该问题的解决方案。本文将详细阐述如何利用最短路径算法解决这一问题,并提供详细的代码实现。 ... [详细]
  • 本文详细介绍了C++中的构造函数,包括其定义、特点以及如何通过构造函数进行对象的初始化。此外,还探讨了转换构造函数的概念及其在不同情境下的应用,以及如何避免不必要的隐式类型转换。 ... [详细]
  • 解决PHP项目在服务器无法抓取远程网页内容的问题
    本文探讨了在使用PHP进行后端开发时,遇到的一个常见问题:即在本地环境中能够正常通过CURL获取远程网页内容,但在服务器上却无法实现。我们将分析可能的原因并提供解决方案。 ... [详细]
  • 入门指南:使用FastRPC技术连接Qualcomm Hexagon DSP
    本文旨在为初学者提供关于如何使用FastRPC技术连接Qualcomm Hexagon DSP的基础知识。FastRPC技术允许开发者在本地客户端实现远程调用,从而简化Hexagon DSP的开发和调试过程。 ... [详细]
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社区 版权所有