小白都能学会的Java注解与反射机制

Annotation[] annotations = clz.getAnnotations();

for (Annotation annotation : annotations) {

System.out.println(annotation.toString());

}

OperType operType = clz.getAnnotation(OperType.class);

System.out.println(operType);

OperType[] operTypes = clz.getAnnotationsByType(OperType.class);

for (OperType type : operTypes) {

System.out.println(type.toString());

}

}

}

// 输出结果为

@com.nobody.OperTypes(value=[@com.nobody.OperType(value=[add]), @com.nobody.OperType(value=[update])])

null

@com.nobody.OperType(value=[add])

@com.nobody.OperType(value=[update])

在Java8中,ElementType枚举新增了两个枚举成员,分别为TYPE_PARAMETER和TYPE_USE,TYPE_PARAMETER标识注解可以作用于类型参数,TYPE_USE标识注解可以作用于标注任意类型(除了Class)。

Java反射机制

===================================================================

我们先了解下什么是静态语言和动态语言。动态语言是指在运行时可以改变其自身结构的语言。例如新的函数,对象,甚至代码可以被引进,已有的函数可以被删除或者结构上的一些变化。简单说即是在运行时代码可以根据某些条件改变自身结构。动态语言主要有C#,Object-C,JavaScript,PHP,Python等。静态语言是指运行时结构不可改变的语言,例如Java,C,C++等。

Java不是动态语言,但是它可以称为准动态语言,因为Java可以利用反射机制获得类似动态语言的特性,Java的动态性让它在编程时更加灵活。

反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性以及方法等。类在被加载完之后,会在堆内存的方法区中生成一个Class类型的对象,一个类只有一个Class对象,这个对象包含了类的结构信息。我们可以通过这个对象看到类的结构。

比如我们可以通过Class clz = Class.forName("java.lang.String");获得String类的Class对象。我们知道每个类都隐式继承Object类,Object类有个getClass()方法也能获取Class对象。

Java反射机制提供的功能

  1. 在运行时判断任意一个对象所属的类

  2. 在运行时构造任意一个类的对象

  3. 在运行时判断任意一个类具有的成员变量和方法

  4. 在运行时获取泛型信息

  5. 在运行时调用任意一个对象的成员变量和方法

  6. 在运行时获取注解

  7. 生成动态代理

Java反射机制的优缺点

  • 优点:实现动态创建对象和编译,有更加的灵活性。

  • 缺点:对性能有影响。使用反射其实是一种解释操作,即告诉JVM我们想要做什么,然后它满足我们的要求,所以总是慢于直接执行相同的操作。

Java反射相关的主要API

  • java.lang.Class:代表一个类

  • java.lang.reflect.Method:代表类的方法

  • java.lang.reflect.Field:代表类的成员变量

  • java.lang.reflect.Constructor:代表类的构造器

我们知道在运行时通过反射可以准确获取到注解信息,其实以上类(Class,Method,Field,Constructor等)都直接或间接实现了AnnotatedElement接口,并实现了它定义的方法,AnnotatedElement接口的作用主要用于表示正在JVM中运行的程序中已使用注解的元素,通过该接口提供的方法可以获取到注解信息。

java.lang.Class 类

============================================================================

在Java反射中,最重要的是Class这个类了。Class本身也是一个类。当程序想要使用某个类时,如果此类还未被加载到内存中,首先会将类的class文件字节码加载到内存中,并将这些静态数据转换为方法区的运行时数据结构,然后生成一个Class类型的对象(Class对象只能由系统创建),一个类只有一个Class对象,这个对象包含了类的结构信息。我们可以通过这个对象看到类的结构。每个类的实例都会记得自己是由哪个Class实例所生成的。

通过Class对象可以知道某个类的属性,方法,构造器,注解,以及实现了哪些接口等信息。注意,只有class,interface,enum,annotation,primitive type,void,[] 等才有Class对象。

package com.nobody;

import java.lang.annotation.ElementType;

import java.util.Map;

public class TestClass {

public static void main(String[] args) {

// 类

Class myClassClass = MyClass.class;

// 接口

Class mapClass = Map.class;

// 枚举

Class elementTypeClass = ElementType.class;

// 注解

Class overrideClass = Override.class;

// 原生类型

Class integerClass = Integer.class;

// 空类型

Class voidClass = void.class;

// 一维数组

Class<String[]> aClass = String[].class;

// 二维数组

Class<String[][]> aClass1 = String[][].class;

// Class类也有Class对象

Class classClass = Class.class;

System.out.println(myClassClass);

System.out.println(mapClass);

System.out.println(elementTypeClass);

System.out.println(overrideClass);

System.out.println(integerClass);

System.out.println(voidClass);

System.out.println(aClass);

System.out.println(aClass1);

System.out.println(classClass);

}

}

// 输出结果

class com.nobody.MyClass

interface java.util.Map

class java.lang.annotation.ElementType

interface java.lang.Override

class java.lang.Integer

void

class [Ljava.lang.String;

class [[Ljava.lang.String;

class java.lang.Class

获取Class对象的方法

  1. 如果知道具体的类,可通过类的class属性获取,这种方法最安全可靠并且性能最高。Class clz = User.class;

  2. 通过类的实例的getClass()方法获取。Class clz = user.getClass();

  3. 如果知道一个类的全限定类名,并且在类路径下,可通过Class.forName()方法获取,但是可能会抛出ClassNotFoundException。Class clz = Class.forName("com.nobody.User");

  4. 内置的基本数据类型可以直接通过类名.Type获取。Class<Integer> clz = Integer.TYPE;

  5. 通过类加载器ClassLoader获取

Class类的常用方法

  • public static Class<?> forName(String className):创建一个指定全限定类名的Class对象

  • public T newInstance():调用Class对象所代表的类的无参构造方法,创建一个实例

  • public String getName():返回Class对象所代表的类的全限定名称。

  • public String getSimpleName():返回Class对象所代表的类的简单名称。

  • public native Class<? super T> getSuperclass():返回Class对象所代表的类的父类的Class对象,这是一个本地方法

  • public Class<?>[] getInterfaces():返回Class对象的接口

  • public Field[] getFields():返回Class对象所代表的实体的public属性Field对象数组

  • public Field[] getDeclaredFields():返回Class对象所代表的实体的所有属性Field对象数组

  • public Field getDeclaredField(String name):获取指定属性名的Field对象

  • public Method[] getDeclaredMethods():返回Class对象所代表的实体的所有Method对象数组

  • public Method getDeclaredMethod(String name, Class<?>… parameterTypes):返回指定名称和参数类型的Method对象

  • myClassClass.getDeclaredConstructors();:返回所有Constructor对象的数组

  • public ClassLoader getClassLoader():返回当前类的类加载器

在反射中经常会使用到Method的invoke方法,即public Object invoke(Object obj, Object... args),我们简单说明下:

  • 第一个Object对应原方法的返回值,若原方法没有返回值,则返回null。

  • 第二个Object对象对应调用方法的实例,若原方法为静态方法,则参数obj可为null。

  • 第二个Object对应若原方法形参列表,若参数为空,则参数args为null。

  • 若原方法声明为private修饰,则调用invoke方法前,需要显示调用方法对象的method.setAccessible(true)方法,才可访问private方法。

反射操作泛型

=================================================================

泛型是JDK 1.5的一项新特性,它的本质是参数化类型(Parameterized Type)的应用,也就是说所操作的数据类型被指定为一个参数,在用到的时候再指定具体的类型。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口和泛型方法。

在Java中,采用泛型擦除的机制来引入泛型,泛型能编译器使用javac时确保数据的安全性和免去强制类型转换问题,泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。并且一旦编译完成,所有和泛型有关的类型会被全部擦除。

Java新增了ParameterizedTypeGenericArrayTypeTypeVariableWildcardType等几种类型,能让我们通过反射操作这些类型。

  • ParameterizedType:表示一种参数化类型,比如Collection

  • GenericArrayType:表示种元素类型是参数化类型或者类型变量的数组类型

  • TypeVariable:是各种类型变量的公共父接口

  • WildcardType:代表种通配符类型表达式

package com.nobody;

import java.lang.reflect.Method;

import java.lang.reflect.ParameterizedType;

import java.lang.reflect.Type;

import java.util.Map;

public class TestReflectGenerics {

public Map<String, Person> test(Map<String, Integer> map, Person person) {

return null;

}

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

// 获取test方法对象

Method test = TestReflectGenerics.class.getDeclaredMethod(“test”, Map.class, Person.class);

// 获取方法test的参数类型

Type[] genericParameterTypes = test.getGenericParameterTypes();

for (Type genericParameterType : genericParameterTypes) {

System.out.println(“方法参数类型:” + genericParameterType);

// 如果参数类型等于参数化类型

if (genericParameterType instanceof ParameterizedType) {

// 获得真实参数类型

Type[] actualTypeArguments =

((ParameterizedType) genericParameterType).getActualTypeArguments();

for (Type actualTypeArgument : actualTypeArguments) {

System.out.println(" " + actualTypeArgument);

}

}

}

// 获取方法test的返回值类型

Type genericReturnType = test.getGenericReturnType();

System.out.println(“返回值类型:” + genericReturnType);

// 如果参数类型等于参数化类型

if (genericReturnType instanceof ParameterizedType) {

// 获得真实参数类型

Type[] actualTypeArguments =

((ParameterizedType) genericReturnType).getActualTypeArguments();

for (Type actualTypeArgument : actualTypeArguments) {

System.out.println(" " + actualTypeArgument);

}

}

}

}

class Person {}

// 输出结果

方法参数类型:java.util.Map<java.lang.String, java.lang.Integer>

class java.lang.String

class java.lang.Integer

方法参数类型:class com.nobody.Person

返回值类型:java.util.Map<java.lang.String, com.nobody.Person>

class java.lang.String

class com.nobody.Person

反射操作注解

=================================================================

在Java运行时,通过反射获取代码中的注解是比较常用的手段了,获取到了注解之后,就能知道注解的所有信息了,然后根据信息进行相应的操作。下面通过一个例子,获取类和属性的注解,解析映射为数据库中的表信息。

package com.nobody;

import java.lang.annotation.*;

public class AnalysisAnnotation {

public static void main(String[] args) throws Exception {

Class<?> aClass = Class.forName(“com.nobody.Book”);

// 获取类的指定注解,并且获取注解的值

Table annotation = aClass.getAnnotation(Table.class);

String value = annotation.value();

System.out.println(“Book类映射的数据库表名:” + value);

java.lang.reflect.Field bookName = aClass.getDeclaredField(“bookName”);

TableField annotation1 = bookName.getAnnotation(TableField.class);

System.out.println(“bookName属性映射的数据库字段属性 - 列名:” + annotation1.colName() + “,类型:”

  • annotation1.type() + “,长度:” + annotation1.length());

java.lang.reflect.Field price = aClass.getDeclaredField(“price”);

TableField annotation2 = price.getAnnotation(TableField.class);

System.out.println(“price属性映射的数据库字段属性 - 列名:” + annotation2.colName() + “,类型:”

  • annotation2.type() + “,长度:” + annotation2.length());

}

}

// 作用于类的注解,用于解析表数据

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@interface Table {

// 表名

String value();

}

// 作用于字段,用于解析表列

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

@interface TableField {

// 列名

String colName();

// 列类型

String type();

// 长度

int length();
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

最后

小编在这里分享些我自己平时的学习资料,由于篇幅限制,pdf文档的详解资料太全面,细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!

程序员代码面试指南 IT名企算法与数据结构题目最优解

这是” 本程序员面试宝典!书中对IT名企代码面试各类题目的最优解进行了总结,并提供了相关代码实现。针对当前程序员面试缺乏权威题目汇总这一-痛点, 本书选取将近200道真实出现过的经典代码面试题,帮助广“大程序员的面试准备做到万无一失。 “刷”完本书后,你就是“题王”!

image.png

《TCP-IP协议组(第4版)》

本书是介绍TCP/IP协议族的经典图书的最新版本。本书自第1版出版以来,就广受读者欢迎。

本书最新版进行」护元,以体境计算机网络技不的最新发展,全书古有七大部分共30草和7个附录:第一部分介绍一些基本概念和基础底层技术:第二部分介绍网络层协议:第三部分介绍运输层协议;第四部分介绍应用层协议:第五部分介绍下一代协议,即IPv6协议:第六部分介绍网络安全问题:第七部分给出了7个附录。

image.png

Java开发手册(嵩山版)

这个不用多说了,阿里的开发手册,每次更新我都会看,这是8月初最新更新的**(嵩山版)**

image.png

MySQL 8从入门到精通

本书主要内容包括MySQL的安装与配置、数据库的创建、数据表的创建、数据类型和运算符、MySQL 函数、查询数据、数据表的操作(插入、更新与删除数据)、索引、存储过程和函数、视图、触发器、用户管理、数据备份与还原、MySQL 日志、性能优化、MySQL Repl ication、MySQL Workbench、 MySQL Utilities、 MySQL Proxy、PHP操作MySQL数据库和PDO数据库抽象类库等。最后通过3个综合案例的数据库设计,进步讲述 MySQL在实际工作中的应用。

image.png

Spring5高级编程(第5版)

本书涵盖Spring 5的所有内容,如果想要充分利用这一领先的企业级 Java应用程序开发框架的强大功能,本书是最全面的Spring参考和实用指南。

本书第5版涵盖核心的Spring及其与其他领先的Java技术(比如Hibemate JPA 2.Tls、Thymeleaf和WebSocket)的集成。本书的重点是介绍如何使用Java配置类、lambda 表达式、Spring Boot以及反应式编程。同时,将与企业级应用程序开发人员分享一些见解和实际经验,包括远程处理、事务、Web 和表示层,等等。

image.png

JAVA核心知识点+1000道 互联网Java工程师面试题

image.png

image.png

企业IT架构转型之道 阿里巴巴中台战略思想与架构实战

本书讲述了阿里巴巴的技术发展史,同时也是-部互联网技 术架构的实践与发展史。

image.png
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
所有内容,如果想要充分利用这一领先的企业级 Java应用程序开发框架的强大功能,本书是最全面的Spring参考和实用指南。

本书第5版涵盖核心的Spring及其与其他领先的Java技术(比如Hibemate JPA 2.Tls、Thymeleaf和WebSocket)的集成。本书的重点是介绍如何使用Java配置类、lambda 表达式、Spring Boot以及反应式编程。同时,将与企业级应用程序开发人员分享一些见解和实际经验,包括远程处理、事务、Web 和表示层,等等。

[外链图片转存中…(img-B6MqBMkM-1712432179528)]

JAVA核心知识点+1000道 互联网Java工程师面试题

[外链图片转存中…(img-9b72ORqd-1712432179529)]

[外链图片转存中…(img-YCI7cOdg-1712432179529)]

企业IT架构转型之道 阿里巴巴中台战略思想与架构实战

本书讲述了阿里巴巴的技术发展史,同时也是-部互联网技 术架构的实践与发展史。

[外链图片转存中…(img-4B7RwdpQ-1712432179529)]
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值