【笔记】注解与反射

注解(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 新增了 ParamenterizedTypeGenericArrayTypeTypeVariableWildcardType 四种类型来代表不能被归一到 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,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作。

posted @   hzyuan  阅读(24)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)

喜欢请打赏

扫描二维码打赏

支付宝打赏

点击右上角即可分享
微信分享提示