通过反射获取类信息和注解信息
一、什么是反射
Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
二、反射优缺点
1、优点:在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。
2、缺点:(1)反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;
(2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
三、反射的基本使用
-
通过反射获取类信息的三种方式:
-
//"reflex.test"为带包名的类路径 //该Class为test类的类元信息。 Class test = Class.forName("reflex.test")
-
//test为类名 Class test = test.class;
-
//先实例化类。再通过类的引用变量获取Class对象 test test = new test(); Class test = test.getClass();
三种方式中,常用第一种,第二种对象都有了还要反射干什么,第三种需要导入类包,依赖太强,不导包就抛编译错误。一般都使用第一种,一个字符串可以传入也可以写在配置文件中等多种方法。
注意: 通过反射获取Class类信息都是一个类元信息。所以上面三种方法获取反射类后的内存地址和hashcode地址都是一样的。(相当于一个类的模板。从始至终都只有一个。在jvm(jdk1.8)中的metaspace元空间中)
-
-
通过反射获取类中构造方法、类内字段信息、类内方法等
- 反射类.getDeclaredFields()。是获取该反射类中所有字段(一般为被private修饰符所修饰的字段)
- 反射类.getFields()。是获取该反射类中所有类内公共字段(一般为被public修饰符所修饰的字段)和父类公共字段
- 反射类.getDeclaredMethods()。是获取该反射类中所有类内方法
- 反射类.getMethods()。是获取该反射类中所有类内公共方法(一般为被public修饰符所修饰的字段)和父类公共方法
- 反射类.getDeclaredConstructors()。是获取该反射类中所有类内构造方法
- 反射类.getConstructors()。是获取该反射类中所有类内公共构造方法(一般为被public修饰符所修饰的字段)
- 代码测试如下:
package reflex; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.security.AlgorithmConstraints; /** * @PROJECT_NAME: deemo * @DESCRIPTION: * @USER: zhaodongjian * @DATE: 2021/7/28 15:13 */ public class test02 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { String a = "23"; int i = Integer.parseInt(a); System.out.println("=====================字段========================"); Class aClass = Class.forName("reflex.test"); // Field[] declaredFields = aClass.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println("类内全部的"+declaredField); } declaredFields = aClass.getFields(); for (Field declaredField : declaredFields) { System.out.println("类public(包括父类)"+declaredField); } System.out.println("=====================方法========================"); Method[] declaredMethods = aClass.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println("类内全部的"+declaredMethod); } declaredMethods = aClass.getMethods(); for (Method declaredMethod : declaredMethods) { System.out.println("类public(包括父类)"+declaredMethod); } //此处是使用invoke()方法改变类内私有成员变量 //通过newInstance()获取test类的实例化对象 test o = (test)aClass.newInstance(); //通过aClass.getMethod("方法明",该方法的参数类型数组)获取反射类方法或实列方法。后面的参数类型可以有多个。 Method setName = aClass.getMethod("setName", String.class); System.out.println("指定方法:"+setName); //使用反射类方法.invoke("实例化对象","要改变的参数值") setName.invoke(o,"八嘎雅鹿"); System.out.println(o.getName()); System.out.println("=====================构造器========================"); Constructor[] constructors = aClass.getDeclaredConstructors(); for (Constructor constructor : constructors) { System.out.println("类内全部的"+constructor); } constructors = aClass.getConstructors(); for (Constructor constructor : constructors) { System.out.println("类public()"+constructor); } Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class,boolean.class); System.out.println(declaredConstructor); test o1 = (test)aClass.newInstance(); //获取本类的私有字段flag。(注意用getDeclaredField()方法获取。如果用getField()会报NoSuchFieldException异常) Field flag = aClass.getDeclaredField("flag"); //去除权限控制访问修饰符。即无视private修饰符即可进行字段值修改。 //不加---flag.setAccessible(true);这句会---(Class reflex.test02 can not access a member of class reflex.test with modifiers "private") flag.setAccessible(true); flag.set(o1,false); System.out.println("去除权限控制访问修饰符后的flag--"+o1.isFlag()); } } class test extends father{ private String name; private int age; private boolean flag; public int aa; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } public test() { } public test(String name, int age, boolean flag) { this.name = name; this.age = age; this.flag = flag; } private test(String name, int age){ this.name = name; this.age = age; } private void gettest(){ System.out.println("1"); } } class father{ private String fathername; public int fatherage; public String getFathername() { return fathername; } public void setFathername(String fathername) { this.fathername = fathername; } public int getFatherage() { return fatherage; } public void setFatherage(int fatherage) { this.fatherage = fatherage; } public father(String fathername, int fatherage) { this.fathername = fathername; this.fatherage = fatherage; } public father() { } }
运行结果如下:
=====================属性======================== 类内全部的private java.lang.String reflex.test.name 类内全部的private int reflex.test.age 类内全部的private boolean reflex.test.flag 类内全部的public int reflex.test.aa 类public(包括父类)public int reflex.test.aa 类public(包括父类)public int reflex.father.fatherage =====================方法======================== 类内全部的public java.lang.String reflex.test.getName() 类内全部的public void reflex.test.setName(java.lang.String) 类内全部的public boolean reflex.test.isFlag() 类内全部的public void reflex.test.setFlag(boolean) 类内全部的public void reflex.test.setAge(int) 类内全部的public int reflex.test.getAge() 类内全部的private void reflex.test.gettest() 类public(包括父类)public java.lang.String reflex.test.getName() 类public(包括父类)public void reflex.test.setName(java.lang.String) 类public(包括父类)public boolean reflex.test.isFlag() 类public(包括父类)public void reflex.test.setFlag(boolean) 类public(包括父类)public void reflex.test.setAge(int) 类public(包括父类)public int reflex.test.getAge() 类public(包括父类)public int reflex.father.getFatherage() 类public(包括父类)public void reflex.father.setFathername(java.lang.String) 类public(包括父类)public void reflex.father.setFatherage(int) 类public(包括父类)public java.lang.String reflex.father.getFathername() 类public(包括父类)public final void java.lang.Object.wait() throws java.lang.InterruptedException 类public(包括父类)public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException 类public(包括父类)public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException 类public(包括父类)public boolean java.lang.Object.equals(java.lang.Object) 类public(包括父类)public java.lang.String java.lang.Object.toString() 类public(包括父类)public native int java.lang.Object.hashCode() 类public(包括父类)public final native java.lang.Class java.lang.Object.getClass() 类public(包括父类)public final native void java.lang.Object.notify() 类public(包括父类)public final native void java.lang.Object.notifyAll() 制定方法:public void reflex.test.setName(java.lang.String) 八嘎雅鹿 =====================构造器======================== 类内全部的public reflex.test() 类内全部的private reflex.test(java.lang.String,int) 类内全部的public reflex.test(java.lang.String,int,boolean) 类public()public reflex.test() 类public()public reflex.test(java.lang.String,int,boolean) public reflex.test(java.lang.String,int,boolean) 去除权限控制访问修饰符后的flag--false
-
通过反射获取泛型参数
- 通过反射获取方法的信息
- 反射方法.getGenericParameterTypes()获取泛型参数类型数组
public class Geneictest { public Map<String,test> test(){ System.out.println("testMap"); return null; } public void test02(Map<String,test> map,List<test> list){ } public static void main(String[] args) throws NoSuchMethodException { Method test02 = Geneictest.class.getMethod("test02", Map.class, List.class); //方法反射获取泛型参数类型数组 Type[] genericParameterTypes = test02.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("actual"+actualTypeArgument); } } } } }
//运行结果: java.util.Map<java.lang.String, reflex.test> actualclass java.lang.String actualclass reflex.test java.util.List<reflex.test> actualclass reflex.test
-
反射获取注解信息
(1)注解(Annotation)。Java类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。
(2)自定义注解: (这里只是简单的写两个自定义注解)
//Target标识这个注解应该是哪种 Java 成员。如类、方法、或者类内字段 //Retention标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。 //RetentionPolicy.RUNTIME指的是在运行时访问。 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface MyTable{ String value(); } @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface MyTableFiled{ String columnName(); String type(); int length(); }
-
public class AnnotationTest { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException { Class demo = Class.forName("reflex.demo"); Annotation[] annotations = demo.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } MyTable myTable = (MyTable)demo.getAnnotation(MyTable.class); System.out.println(myTable.value()); //获取某个属性的注解信息 System.out.println("================获取某个属性的注解信息================="); //先通过getDeclaredField获取声明字段类型 Field f = demo.getDeclaredField("name"); //在字段领域获取其注解反射类 MyTableFiled annotation = f.getAnnotation(MyTableFiled.class); //获取该字段的注解的值 System.out.println(annotation.columnName()); System.out.println(annotation.type()); System.out.println(annotation.length()); } } @MyTable("demo") class demo{ @MyTableFiled(columnName = "name",type = "varchar",length = 10) private String name; @MyTableFiled(columnName = "age",type = "int",length = 2) private int age; @MyTableFiled(columnName = "height",type = "int",length = 10) private int height; public demo() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } public demo(String name, int age, int height) { this.name = name; this.age = age; this.height = height; } } //运行结果: @reflex.MyTable(value=demo) demo ================获取某个属性的注解信息================= name varchar 10
-
通过反射获取类相关信息就介绍到这。如有错误。希望大佬批评指正哈。