【笔记】注解与反射
注解(Annotation)
对程序做出解释,可以被其他程序所读取,进行信息处理
常见的内置注解
@Override
:修饰方法,表示打算重写超类中的方法声明。@Deprecated
:修饰方法、属性、类,表示不鼓励程序员使用这样的元素,通常是因为其很危险或有更好的选择。@SuperWarnings
:用于抑制警告信息。
元注解
负责注解其他注解,Java定义了4个标准的meta-annotation
类型,被用来对其他注解类型做说明。
@Target
:用于描述注解的使用范围。@Rentention
:用于描述注解的生命周期(SOURCE < CLASS < RUNTIME)。@Document
:说明该注解将被包含在Javadoc中。@Inherited
:说明子类可以继承父类中的该注解。
自定义注解
格式: public @interface 注解名 {定义体}
-
使用
@interface
自定义注解时,自动继承了java.lang.annotation.Annotation
接口。 -
其中的每个方法实际上时声明了一个配置参数。
-
方法的名称就是参数的名称。
-
返回值类型就是参数的类型(只能是基本类型、Class、String、enum)。
-
可通过
default
来声明参数的默认值。 -
如果只有一个参数成员,一般命名为value(因为在使用时可以省略参数名)。
-
注解元素必须要有值,定义注解袁术时,经常用空字符串、0作为默认值。
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "";
int id() default -1;
}
反射(Reflection)
反射机制允许程序在运行期间借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性和方法。
加载完类之后,在堆内存中,就产生了一个 Class 类型的对象(一个类只有一个 Class 对象),这个对象就包含了完整的类的结构信息。 我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
动态语言&&静态语言
运行时结构可变的语言即动态语言,例如新的函数、对象、甚至代码都可以被引进,已有的函数可以被删除或是其他结构上的变化。如Object-C、C#、JavaScript、PHP、Python等。
运行时结构不可改变的语言即静态语言。如Java、C、C++。
而反射机制使得Java获得了类似动态语言的特性。
反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
- ...........
反射相关的主要API
java.lang.Class
:代表一个类java.lang.reflect.Method
:代表类的方法java.lang.reflect.Field
:代表类的成员变量java.lang.reflect.Constructor
:代表类的构造器- ...........
Class 类
对于每个类而言,JRE 都为其保留了一个不变的 Class 类型的对象。
Class 类是 Reflection 的根源,针对任何你想动态加载、运行的类,唯有先获得相应的 Class 对象。
更深入理解Class类型对象可以去看类的内存分析
获取 Class 类的实例
我们在使用反射时,需要先获得我们需要的类,java.lang.Class
用来表示 Java 中类型(class/interface/[]/enum/annotation/primitive type/void)本身。
- 已知一个类的全类名,通过
Class.forName()
获取(最常用) - 已知具体的类,通过类的 class 属性获取(最安全可靠,程序性能最高)
- 已知类的实例,调用该实例的
getClass()
方法获取 Class 对象 - 内置基本数据类型可以直接用 **类名.Type **获取
- 利用 ClassLoader 获取
package com.hzyuan.reflection;
//先创建一个实体类
public class User {
int id;
String name;
public User() {
}
public User(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
package com.hzyuan.reflection;
//获取Class类实例
public class TestReflect {
public static void main(String[] args) {
//通过Class.forName()获取
Class c1;
try {
c1 = Class.forName("com.hzyuan.reflection.User");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//通过类的class属性获取
Class c2 = User.class;
//实例的getClass()方法获取
User user = new User();
Class c3 = user.getClass();
//内置基本数据类型可以直接用类名.Type获取
Class c4 = Integer.TYPE;
}
}
Class 类的常用方法
反射就是把java类中的各种成分映射成一个个的Java对象
方法名 | 功能说明 |
---|---|
static ClassforName(String name) |
返回指定类名name的Class对象 |
Object newInstance() |
调用缺省构造函数,返回Class对象的一个实例 |
getName() |
返回此 Class 对象所表示的实体(类、接口、数组类或void)的名称 |
Class getSuperClass() |
返回当前 Class 对象的父类的 Class 对象 |
Class[] getInterfaces() |
获取当前 Class 对象的接口 |
ClassLoader getClassLoader() |
返回该类的类加载器 |
Constructor[] getConstructors() |
返回一个包含某些 Constructor 对象的数组 |
Method getMethod(String name,Class<?>... parameterTypes) |
返回一个 Method 对象,此对象的形参类型为parameterTypes |
Field[] getDeclaredFields() |
返回该类的所有字段,不包括父类(仅自定义)。 |
public class TestReflect {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1 = Class.forName("com.hzyuan.reflection.User");
//获取全类名
String name = c1.getName();
//获取简单类名
String simpleName = c1.getSimpleName();
//获取该类的全部public字段(包括父类)
Field[] fields = c1.getFields();
//根据字段名获取该类的public字段
//这里会出错,因为在User类里name是private字段
Field field = c1.getField("name");
//获取该类的全部字段(不包括父类)
Field[] declaredFields = c1.getDeclaredFields();
//根据字段名获取该类的字段
Field declaredField = c1.getDeclaredField("name");
//获取该类的所有public方法(包括父类)
Method[] methods = c1.getMethods();
//根据方法名获取该类的public方法
Method method1 = c1.getMethod("getName");
//获取带参的public方法
Method method2 = c1.getMethod("setName",String.class);
//获取该类的所有方法(不包括父类)
Method[] declaredMethods = c1.getDeclaredMethods();
//根据方法名获取该类的方法
Method declaredMethod1 = c1.getDeclaredMethod("getName");
//获取带参的方法
Method declaredMethod2 = c1.getDeclaredMethod("setName", String.class);
//获取该类的所有public构造器
Constructor[] constructors = c1.getConstructors();
//根据构造器的参数类型获取指定的public构造器,不写为无参构造器
Constructor constructor1 = c1.getConstructor();
Constructor constructor2 = c1.getConstructor(int.class,String.class);
//获取该类的所有构造器
Constructor[] declaredConstructors = c1.getDeclaredConstructors();
//根据构造器的参数类型获取指定的构造器,不写为无参构造器
Constructor declaredConstructor1 = c1.getDeclaredConstructor();
Constructor declaredConstructor2 = c1.getDeclaredConstructor(int.class,String.class);
}
}
动态创建对象
//方法一:通过newInstance实例化,使用的是无参构造器
//默认获取的是Object类型,可通过类型转换变为所需类型
User user = (User) c1.newInstance();
//方法二:获取构造器并创建对象
Constructor constructor = c1.getDeclaredConstructor(int.class,String.class);
Object object = constructor.newInstance(1, "hzyuan");
通过反射调用方法和操作属性
//反射方式进行方法调用
//先获取方法
Method setName = c1.getDeclaredMethod("setName", String.class);
//通过获取的方法来调用(invoke),第一个参数是调用方法的对象,其余为调用方法所需的参数
setName.invoke(object,"yuan");
//通过反射操作属性
Field name = c1.getDeclaredField("name");
//不能直接操作私有属性,我们需要跳过安全检测。
//Method、Field和Constructor对象都有setAccessible()方法。
//跳过安全检测能提高性能
name.setAccessible(true);
//通过get/set方法操作字段
name.set(object,"hzyuan");
name.get(object);
反射操作泛型
Java 采用泛型擦除的机制来引入泛型。也就是说一旦编译完成,所有和泛型有关的类型全部擦除。
为了通过反射操作这些类型,Java 新增了 ParamenterizedType
、GenericArrayType
、TypeVariable
、WildcardType
四种类型来代表不能被归一到 Class 类中的类型但是又和原始类型齐名的类型。
ParameterizedType
:表示一种参数化类型。比如List<T>
GenericArrayType
:表示一种元素类型是参数化类型或类型变量的数组类型。比如List<T>[]
TypeVarible
:是各种类型变量的公共父接口。WildcardType
:代表一种通配符类型表达式。比如? extends Number
public class TestReflect{
//List<String>[]是GenericArrayType
//Map<String[],User>是ParameterizedType
//List<? extends Number>是ParameterizedType
//? extends Number是WildcardType
public List<String>[] test(Map<String[],User> map,List<? extends Number> list){
return null;
}
public static void main(String[] args) throws NoSuchMethodException {
//获取测试方法
Method method = TestReflect.class.getMethod("test", Map.class, List.class);
//获取方法所有的参数类型
Type[] genericParameterTypes = method.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);
}
}
}
System.out.println("========================");
//获取方法返回值的参数类型
Type genericReturnType = method.getGenericReturnType();
System.out.println(genericReturnType);
//List<String>[]的类型是GenericArrayType
if (genericReturnType instanceof GenericArrayType){
Type genericComponentType = ((GenericArrayType)genericReturnType)
.getGenericComponentType();
//genericComponentType是List<String>,类型是ParameterizedType
System.out.println(genericComponentType);
}
}
}
反射操作注解
先创建注解
@Target(ElementType.TYPE)
@Retention( RetentionPolicy.RUNTIME)
public @interface MyTable {
String value();
}
@Target(ElementType.FIELD)
@Retention( RetentionPolicy.RUNTIME)
public @interface MyField {
String columnName();
String type();
int length();
}
获取注解
@MyTable("db_student")
public class Student {
@MyField( columnName = "db_id", type = "int", length = 10 )
private int id;
@MyField( columnName = "db_name", type = "varchar", length = 5 )
private String name;
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c = Class.forName("com.hzyuan.annotation_reflect.Student");
//获取类上的注解
Annotation[] annotations = c.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获得注解的value值
MyTable myTable = (MyTable) c.getDeclaredAnnotation(MyTable.class);
String value = myTable.value();
System.out.println(value);
//获取字段上的注解
Field field = c.getDeclaredField("name");
MyField myField = field.getDeclaredAnnotation(MyField.class);
System.out.println(myField.columnName());
System.out.println(myField.type());
System.out.println(myField.length());
}
}
优点&&缺点
优点:可以实现动态创建对象和编译,体现出很大的灵活性。
缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作。
本文来自博客园,作者:hzyuan,转载请注明原文链接:https://www.cnblogs.com/hzyuan/p/15747178.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)