功能:Java注解的介绍和反射使用
功能:Java注解的介绍和反射使用
一、注解#
1、注解介绍#
java注解(Annotation),又称为java标注,是jdk5.0引入的一种机制。
Java 语言中的类、方法、变量、参数和包等都可以被标注,对这些代码段进行解释,编译时生成class时,标注也可以被编译。在运行时,java可以通过反射获取到注解内容,进行一些骚操作,进而简化开发。
2、注解分类#
Java 定义了一些注解,有些比较常见
@Override
:检查方法是否重写父类方法@Deprecated
:标记方法过时@SuppressWarnings
:忽略警告元注解,标注注解的注解,一切注解的开始
@Retention
:使用范围,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问@Documented
:标记这些注解是否包含在用户文档中@Target
:作用范围,可以标记哪些代码块,方法,类或者是字段等其他@Inherited
:标记这个注解是继承于哪个注解类java7后加入的注解
@SafeVarargs
:Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告@FunctionalInterface
:Java 8 开始支持,标识一个匿名函数或函数式接口@Repeatable
:Java 8 开始支持,标识某注解可以在同一个声明上使用多次
3、自定义注解#
1)定义语法#
@Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value() default ""; String[] params() default {}; }
如上:
@Documented
:此注解将包含在用户文档中@Target
: ElementType.Type是说,该注解可以在类、接口(包含注解)、枚举上使用@Retention
:此注解将编译至class文件中,在运行时,会被虚拟机读取使用- 和定义接口不同的是,注解的定义前添加
@
号- 如果是字段名是value,则使用注解时可以省略字段名
2)RetentionPolicy,作用范围枚举#
package java.lang.annotation; public enum RetentionPolicy { SOURCE, // Annotation信息仅存在于编译器处理期间,编译后该注解不存在 CLASS, // 编译器将Annotation存储于类对应的class文件中 RUNTIME // 编译器将Annotation存储于class文件中,并且可由JVM读入 }
3)ElementType,使用范围枚举#
package java.lang.annotation; public enum ElementType { // 类,接口(包括注解),枚举 TYPE, // 字段,包括枚举字段 FIELD, // 方法 METHOD, // 方法参数,括号内的形参 PARAMETER, // 构造方法 CONSTRUCTOR, // 局部变量 LOCAL_VARIABLE, // 注解 ANNOTATION_TYPE, // 包 PACKAGE, // Type parameter declaration,@since 1.8 TYPE_PARAMETER, // Use of a type,@since 1.8 TYPE_USE }
二、java反射#
1、反射介绍#
1)反射是什么#
简单的来说,反射就是运行时才知道操作的类是什么,并且在运行阶段有虚拟机进行实例化,可知道内部所有的(包括private私有的)属性和方法,这种机制叫做反射
java之所以有了这种机制,才会成为一门准动态语言
动态语言和静态语言的区别
- 动态语言:是指一类在运行时,也可以改变程序结构的语言,加入新的函数,对象,甚至是代码都可以被引入,可以根据某些条件改变自身结构
- 主要语言有:C#、JavaScript、PHP、Python
- 静态语言:相对于动态语言,在运行时结构不可改变的语言就是静态语言
- 主要语言有:Java、C、C++
在java有了反射之后,java就可以称为准动态语言,反射使得java有了一定的动态性,我们可以通过这种机制,让编程更加灵活,玩出骚操作。
2)简单明白反射作用#
在程序开发之初,程序员往往都知道自己需要使用到某些类,这样实例化对象是没问题的,程序也是可以正常访问的,如下
程序员知道要把东西给学生,所以new Student()
进行实例化
public class SomeThingTest { public static void main(String[] args) { Student student = new Student(); student.give("一个红包"); } } abstract class Person { public abstract void give(String some); } class Student extends Person{ @Override public void give(String some) { System.out.println("给了学生:" + some); } } class Teacher extends Person{ @Override public void give(String some) { System.out.println("给了老师:" + some); } }
那程序员不知道要把东西交给谁呢?程序进行不下去了,所以为了解决此类问题,反射机制诞生了,如下
运行结果相差不大,但内部实现机制完全不一致
public class SomeThingTest { public static void main(String[] args) throws Exception { Class cls = Class.forName("com.banmoon.something.Teacher"); Method giveMethod = cls.getMethod("give", String.class); Object teacher = cls.newInstance(); giveMethod.invoke(teacher, "一个苹果"); } }
问题:
com.banmoon.something.Teacher
,这段字符串还不是程序员写死的,就算内部是反射实现的,那这样岂不是多此一举?非也非也,我给大家看一段常用的配置文件,大家就明白了
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/test username: root password: 1234 熟悉吗,那个数据库连接驱动和数据库连接池,那些开发框架的程序员,他们可不知道我们使用的是什么数据库和什么连接池,所以在我们指定对应的驱动路径后,java虚拟机才反射去获取对应的驱动实例。
这样一来,可以说反射机制是框架设计的灵魂,若没有反射,也没有如此丰富全面的java框架,庞大的java生态系统
2、反射使用#
1)反射获取Class对象#
在java中,万物皆对象。所以类在反射出来后产生的对象便是Class
获取反射的3种方式,其中2、3种方法的使用是在编码阶段都清楚类的前提下使用的
- 使用Class对象的静态方法,forName(),根据类的全路径进行加载
- 通过类名.class获取该类的Class对象
- 使用实例对象.getClass()获取该类的Class对象
public class SomeThingTest { public static void main(String[] args) throws ClassNotFoundException { Class cla1 = Class.forName("java.lang.String"); Class cla2 = String.class; Class cla3 = "abc".getClass(); } } 问题:以下
cla3
和cla4
是否为同个对象?
public class SomeThingTest { public static void main(String[] args) throws ClassNotFoundException { Class cla3 = "abc".getClass(); Class cla4 = new String("123").getClass(); System.out.println(cla3.hashCode()); System.out.println(cla4.hashCode()); } }
哪些类型可以有Class对象
- class:普通类,内部类,静态内部类,局部内部类,匿名内部类
- interface:接口
- []:数组
- enum:枚举
- annotation:注解
- 基本数据类型:int等
- void
2)反射获取类的属性和方法#
2.1)写几个类
abstract class Person implements Serializable { private String name; public Person() { } public Person(String name) { this.name = name; } public abstract void give(String some); public String getName() { return name; } public void setName(String name) { this.name = name; } } class Student extends Person{ @Override public void give(String some) { System.out.println("给了学生:" + some); } public static void staticMothed(){ System.out.println("静态方法"); } } class Teacher extends Person{ public String subject; public String getPost() { return post; } public void setPost(String post) { this.post = post; } private String post; public Teacher() { } private Teacher(String name, String subject, String post) { super(name); this.subject = subject; this.post = post; } @Override public void give(String some) { System.out.println("给了老师:" + some); } public void teach(String content){ System.out.println("教学方法一"); } private void teach(String content, Person person){ System.out.println("教学方法二"); } }
2.2)反射使用
import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class SomeThingTest { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { // 获取Teacher类的class对象 Class teacherClass = Teacher.class; // 获取Teacher类的名字 System.out.println(teacherClass.getName()); System.out.println(teacherClass.getSimpleName()); System.out.println("============= 分割线 =============="); // 获取Teacher类的属性,只能获取到public权限的 Field[] fieldArr = teacherClass.getFields(); for (Field field : fieldArr) { System.out.println(field); } System.out.println("============= 分割线 =============="); // 获取Teacher类的属性,可以获取所有权限的属性 fieldArr = teacherClass.getDeclaredFields(); for (Field field : fieldArr) { System.out.println(field); } System.out.println("============= 分割线 =============="); // 获取Teacher类的方法,只能获取到public权限的,且可以获取到继承父类的方法 Method[] methodArr = teacherClass.getMethods(); for (Method method : methodArr) { System.out.println(method); } System.out.println("============= 分割线 =============="); // 获取Teacher类的方法,可以获取所有权限的方法,获取不到继承父类的方法 methodArr = teacherClass.getDeclaredMethods(); for (Method method : methodArr) { System.out.println(method); } System.out.println("============= 分割线 =============="); // 指定获取Teacher类的方法 System.out.println(teacherClass.getMethod("teach", String.class)); System.out.println(teacherClass.getDeclaredMethod("teach", String.class, Person.class)); System.out.println("============= 分割线 =============="); // 获取Teacher类的构造器,只能获取到public权限的 Constructor[] constructorArr = teacherClass.getConstructors(); for (Constructor constructor : constructorArr) { System.out.println(constructor); } System.out.println("============= 分割线 =============="); // 获取Teacher类的构造器,可以获取所有权限的构造器 constructorArr = teacherClass.getDeclaredConstructors(); for (Constructor constructor : constructorArr) { System.out.println(constructor); } System.out.println("============= 分割线 =============="); // 拥有无参构造器时,直接进行实例化,不推荐 Teacher teacher = (Teacher) teacherClass.newInstance(); // 指定获取Teacher类的构造器,并实例化对象 Constructor constructor = teacherClass.getConstructor(); teacher = (Teacher) constructor.newInstance(); System.out.println(String.format("姓名:%s,职务:%s,科目:%s", teacher.getName(), teacher.getPost(), teacher.subject)); constructor = teacherClass.getDeclaredConstructor(String.class, String.class, String.class); constructor.setAccessible(true);// 设置访问权限,为true时可访问私有private teacher = (Teacher) constructor.newInstance("半月无霜", "教导主任", "计算机"); System.out.println(String.format("姓名:%s,职务:%s,科目:%s", teacher.getName(), teacher.getPost(), teacher.subject)); System.out.println("============= 分割线 =============="); // 调用方法 Method teachMethod = teacherClass.getDeclaredMethod("teach", String.class, Person.class); teachMethod.setAccessible(true);// 设置访问权限,为true时可访问私有private teachMethod.invoke(teacherClass.newInstance(), "教学", new Student()); } }
3)反射获取注解#
3.1)写两个注解和类
import lombok.Data; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface MyClassInfo{ String value(); } @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface MyFieldInfo{ String value(); String mappingType(); } @Data @MyClassInfo("用户类") class User{ @MyFieldInfo(value = "主键ID", mappingType = "int") private Integer id; @MyFieldInfo(value = "用户名", mappingType = "varchar") private String username; @MyFieldInfo(value = "密码", mappingType = "varchar") private String password; }
3.2)反射使用注解
public class SomeThingTest { public static void main(String[] args) { Class<User> userClass = User.class; Annotation[] annotationArr = userClass.getDeclaredAnnotations(); for (Annotation annotation : annotationArr) { System.out.println(annotation); } MyClassInfo myClassInfo = userClass.getAnnotation(MyClassInfo.class); System.out.println(myClassInfo.value()); Field[] fieldArr = userClass.getDeclaredFields(); for (Field field : fieldArr) { MyFieldInfo myFieldInfo = field.getDeclaredAnnotation(MyFieldInfo.class); System.out.println(String.format("%s[value:%s,mappingType:%s]", field.getName(), myFieldInfo.value(), myFieldInfo.mappingType())); } } }
4)反射获取泛型#
4.1)写两个泛型的方法
public class Person{ public void setList(List<String> list){ } public Map<String, Object> returnMap() { return null; } }
4.2)反射使用泛型
public class SomeThingTest { public static void main(String[] args) throws NoSuchMethodException { Method setList = Person.class.getDeclaredMethod("setList", List.class); Type[] genericParameterTypes = setList.getGenericParameterTypes(); for (Type genericParameterType : genericParameterTypes) { System.out.println(genericParameterType); if (genericParameterType instanceof ParameterizedType){ // 获取List中的泛型 Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument.getTypeName()); } } } System.out.println("============= 分割线 =============="); Method returnMap = Person.class.getDeclaredMethod("returnMap"); Type type = returnMap.getGenericReturnType(); System.out.println(type); if (type instanceof ParameterizedType){ // 获取Map中的泛型 Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument.getTypeName()); } } } }
5)在线JDK8API文档#
现在,已经知道了反射使用,去剖析框架源码时的你真帅
附上jdk8的在线API文档,祝你前程似锦
作者: 半月无霜
出处:https://www.cnblogs.com/banmoon/p/14255256.html
本站使用「CC BY 4.0」创作共享协议,转载请在文章明显位置注明作者及出处。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!