一、什么是注解Annnotation框架MVC、springboot的底层就是注解注解和注释的不同注释是写给人看的注解既是写给人看的也是写给程序比如编译器、虚拟机看的。源码有效的注解就是给编译器看的因为还没运行呢这个注解就“功成身退”了而运行时有效的注解是给虚拟机看的。注解的作用1给程序看通过反射机制实现对元数据的访问2检查程序的正确性。比如Override注解会检查重写的方法名是否写对了。二、3个内置注解1. Override2. Deprecated自己也可以给自己之前写的方法打上Deprecated注解3. SuppressWarnings(参数) 镇压方法中的警告信息三、4个元注解元注解注解的注解写在注解的上一行比如Target描述注解可以放在哪里方法的上面还是类的上面还是字段的上面。。。Retention注解的生命周期SOURE源码时有效 CLASS编译成.class时依然有效 RUNTIME 运行时依然有效一般写这个SOURCE是什么意思我们的Data、Setter、注解都是被RetentionRetentionPolicy.SOURCE去修饰的。写源码时可以看到Setter注解但是编译后.calss文件里多了setter方法而Setter注解却功成身退了用反编译去看。还有Override在编译的时候就告诉编译器要检查重写的语法是否符合父类规定,生成的.class文件不含这些注解。CLASS是什么意思注解被保留到class文件但jvm加载class文件时候被遗弃这是默认的生命周期。这个的例子还没理解到位以后再来填坑Documnet注解本身是否包含在 JavaDoc 文档中。默认情况下注解不会出现在生成的文档里Inherited子类可以继承父类中的注解//Target表明 MyAnnotation 可以用在方法上、类上、字段上、入参上注解上 Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.Type类上,ElementType.FIELD, ElementType.ANNOTATION_TYPE}) //Retention表明 MyAnnotation从源码到运行时都有效也就是一直有效 Retention(value RetentionPolicy.RUNTIME) //Documented 表明将注解 MyAnnotation写入javadoc中 Documented //Inherited表明注解MyAnnotation可被子类继承 Inherited //自定义一个注解 interface MyAnnotation{ }四、自定义注解为什么注解和反射放在一起讲有什么关系吗先放一放这个问题先问自定义注解如何用用来打tag。比如你想标记一批方法用来给程序看那你就自定义一个注解遇到带这类标签的方法就用springAOP的方式执行某一些操作springAOP是通过反射实现的这就回答了刚刚提出的问题“为什么注解和反射放在一起讲有什么关系吗”。别绕晕了标签一定是打给程序看的如果只是单纯的打标签给你自己看的话那不就是注释嘛用interface自定义注解自动继承java.lang.annotation.Annotation接口如果自定义注解只有一个参数建议命名为value。这样使用注解的时候就不用指明参数名了一个参数时//测试注解在类上有效 MyAnnotation public class DiyAnnotation { //注解只有一个参数且命名为value时可以省略 value MyAnnotation2(2022 is wonderful enough! kickboxing!) public void test03(){ } } Target(value {ElementType.METHOD,ElementType.TYPE}) Retention(value RetentionPolicy.RUNTIME) //自定义一个注解 interface MyAnnotation2{ String value() default ; //只有一个参数 }多个参数时//测试注解在类上有效 MyAnnotation public class DiyAnnotation { //测试注解在方法上有效 //三个参数都有默认值注解可以都不写参数 MyAnnotation public static void main(String[] args) { } //也可以写参数 MyAnnotation(name December,age27,word New Year is around the corner!) public void test01(){ } //也可以写部分参数 MyAnnotation(word New start, must be wonderful!) public void test02(){ } } Target(value {ElementType.METHOD,ElementType.TYPE}) Retention(value RetentionPolicy.RUNTIME) //自定义一个注解 interface MyAnnotation{ /* * 注解的参数们 * 必须带括号 * */ String name() default ; //第一个参数默认值为空 int age() default 18; //第二个参数默认值为18 String word() default ; //第三个参数,默认值为空 }四、注解是如何生效的*换个问法为什么加一个Data注解编译后就自动生成了equals等一系列方法为什么给BookServiceImpl加一个Service注解就自动注入了一个bean解析注解的两个阶段/两种方式刚好对应了上面两种场景javac:编译器读取注解然后自动生成代码或者Override对代码进行语法检查java: java虚拟机运行时基于反射去获取类的属性、执行类的方法。JVM扫描到Service方法的时候本质上是执行了BookServiceImpl类的setter方法五、什么是反射Reflection彻底理解反射机制就像Linux认为all is file一样java中一切都是对象那么类是不是对象呢是。类能是谁的对象呢所有的类都是java.lang.Class类的对象用Class c1 类.class的c1去表示因为类也是对象对象可以调用成员方法那么c1就可以用java.lang.Class类定义的成员方法去获得类的各种信息。此外反射被称为框架的灵魂主要是因为它赋予了我们在运行时1获取类的信息 以及2执行类中方法的能力。—— 运行时获取任意一个对象所属的类Class c1【就跟你亲眼见到这个类的.java文件一样】└─—— 获得了Class c1后就能知道这个类的所有属性├─ 包括public、protected、private└─—— 获得了Class c1后就能知道这个类的所有方法├─ 方法的参数└─ 方法的返回值—— 运行时创建任意一个类的对象、调用类的方法。【就跟你打开其他.java文件要new一个这个类的对象开始使用一样】java本身是一个静态语言正是反射让java具有动态性5.1 静态语言和动态语言区分动态语言和动态网页技术静态语言与动态语言静态类型语言在编译时便已确定变量的类型而动态类型语言的变量类型要到程序运行的时候待变量被赋予某个值之后才会具有某种类型静态网页技术与动态网页技术HTML是静态网页技术JSP是动态网页技术动态部分就是% %括起来的java代码。5.2 反射的优缺点优点灵活性静态语言-动态语言利用反射开发出各种框架spring/mybatis/springboot极大方便了我们的开发。缺点慢反射也是生成一个对象和我们直接new一个对象比起来慢了几十几百倍5.3 反射相关的API学习反射就是学习Class对象和java.lang.reflect对应的各种API听到这里放心了吧说到底还是和集合一样学习各种API而且十分简单5.4 获得反射对象5.5 得到Class类对象的5种方式1. java.lang.Class类为了方便讲述我们假设Person类是当前类Class类是反射的根源反射前必须找到相应的Class对象。这个Class对象中存储了Person类对象的真实类、类的属性、方法、实现了哪些接口等一切信息。对于每个类JRE都为其保存了一个不变的Class类型的对象也就是说由系统建立的。Person类的所有对象共享并且记得这个Class类型的对象。User user1 new User(); User user2 new User(); User user3 new User(); System.out.println(user1.hashCode()); System.out.println(user2.hashCode()); System.out.println(user3.hashCode()); /** * 输出 * 2027961269 * 1586270964 * 1642360923 */ Class c1 user1.getClass(); Class c2 user2.getClass(); Class c3 user3.getClass(); System.out.println(c1.hashCode()); System.out.println(c2.hashCode()); System.out.println(c3.hashCode()); /** * 输出 * 1343441044 * 1343441044 * 1343441044 */2. 得到Class类对象的5种方式public class GetClassInstanceTest { public static void main(String[] args) throws ClassNotFoundException { Person person new Student(); //法一通过继承object类的getClass方法 Class c1 person.getClass(); System.out.println(c1); System.out.println(c1.hashCode()); //法二通过Class类的静态方法forName类的路径,需要抛出异常 Class c2 Class.forName(Student); //应该是【包名.类名】例如com.company.reflection.Student System.out.println(c2); System.out.println(c2.hashCode()); //法三类名.class属性 Class c3 Student.class; System.out.println(c3); System.out.println(c3.hashCode()); //法四基本数据类型包装类的TYPE属性 Class c4 Integer.TYPE; System.out.println(c4); System.out.println(c4.hashCode()); //法五已知子类型获取父类型 Class c5 c1.getSuperclass(); System.out.println(c5); System.out.println(c5.hashCode()); } } class Person{ String name; public String getName() { return name; } public void setName(String name) { this.name name; } } class Student extends Person{ public Student() { this.name 学生; } } class Teacher extends Person{ public Teacher(){ this.name老师; } }对应结果class Student356573597class Student356573597class Student356573597int1735600054class Person21685669这5种方式中只有forName是动态加载运行时才加载其他只要出现new了就是在编译时加载其他都是静态加载。5.5 哪些类可以有Class对象几乎所有Class c1 Object.class; //类 Class c2 Comparable.class; //接口 Class c3 String[].class; //一维数组 Class c4 int[][].class; //二维数组 Class c5 Override.class; //注解 Class c6 ElementType.class; //枚举 Class c7 Integer.class; //基本数据类型包装类 Class c8 void.class; //void Class c9 Class.class; //Class类本身 //------类型 getName() System.out.println(c1); // class java.lang.Object System.out.println(c2); // interface java.lang.Comparable System.out.println(c3); // class [Ljava.lang.String; System.out.println(c4); // class [[I System.out.println(c5); // interface java.lang.Override System.out.println(c6); // class java.lang.annotation.ElementType System.out.println(c7); // class java.lang.Integer System.out.println(c8); // void System.out.println(c9); // class java.lang.Class5.6 获取类的运行时结构在module的src文件夹下新建包com.company.empolyee新建User.java文件。public class User{ //私有 private String name; private int age; //共有 public int id; public User(String name, int age, int id) { this.name name; this.age age; this.id id; } 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; } //私有方法 private void laugh(){ } }import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class ClassLoaderTest { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException { Class c1 Class.forName(com.company.empolyee.User); //1.获取类的名字 System.out.println(c1.getName()); //包名类型 System.out.println(c1.getSimpleName()); //只有类名 /** * 输出 * com.company.empolyee.User * User */ System.out.println(); //2.获取类所有的成员变量 //2-1.getFields只能获取public成员变量 Field[] fields c1.getFields(); for (Field field : fields) { System.out.println(field); } /** * 输出 * public int com.company.empolyee.User.id */ //2-2.getDeclaredFields可以获取所有访问权限的成员变量 Field[] declaredFields c1.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println(declaredField); } /** * 输出 * private java.lang.String com.company.empolyee.User.name * private int com.company.empolyee.User.age * public int com.company.empolyee.User.id */ System.out.println(); //3.获取类指定的成员变量 //3-1.getField只能获取public Field id c1.getField(id); System.out.println(id); /** * 输出 * public int com.company.empolyee.User.id */ //3-2.getDeclaredField获取所有权限 Field name c1.getDeclaredField(name); System.out.println(name); /** * 输出 * private java.lang.String com.company.empolyee.User.name */ System.out.println(); //4.获取类所有的成员方法 //4-1.public Method[] methods c1.getMethods(); for (Method method : methods) { System.out.println(method); } /** * 输出不仅输出了当前类自己定义的public成员方法还有继承下来的所有public方法 * public java.lang.String com.company.empolyee.User.getName() * public void com.company.empolyee.User.setName(java.lang.String) * public int com.company.empolyee.User.getAge() * public void com.company.empolyee.User.setAge(int) * public final void java.lang.Object.wait() throws java.lang.InterruptedException * public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException * public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException * public boolean java.lang.Object.equals(java.lang.Object) * public java.lang.String java.lang.Object.toString() * public native int java.lang.Object.hashCode() * public final native java.lang.Class java.lang.Object.getClass() * public final native void java.lang.Object.notify() * public final native void java.lang.Object.notifyAll() */ System.out.println(); //4-2.获取本类的所有权限的所有方法不包括继承来的 Method[] declaredMethods c1.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println(declaredMethod); } /** * 输出 * public java.lang.String com.company.empolyee.User.getName() * public void com.company.empolyee.User.setName(java.lang.String) * public int com.company.empolyee.User.getAge() * private void com.company.empolyee.User.haha() //私有的 * public void com.company.empolyee.User.setAge(int) */ System.out.println(); //5.获取指定的类方法 //5-1.获取指定的public方法 一定要指定方法的参数因为重载 Method method c1.getMethod(setAge, int.class); System.out.println(method); /** * public void com.company.empolyee.User.setAge(int) */ //5-2.获取指定的不限权限方法 Method laugh c1.getDeclaredMethod(laugh, null); System.out.println(laugh); /** * private void com.company.empolyee.User.laugh() */ System.out.println(构造器); //6.获取所有的构造器 //6-1.获取所有public构造器 Constructor[] constructors c1.getConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor); } /** * public com.company.empolyee.User(java.lang.String,int,int) */ //6-2.获取所有不限权限的构造器 Constructor[] declaredConstructors c1.getDeclaredConstructors(); for (Constructor declaredConstructor : declaredConstructors) { System.out.println(declaredConstructors); } /** * [Ljava.lang.reflect.Constructor;45ee12a7 */ System.out.println(单个的构造器); //7.获取指定的构造器 //7-1.获取指定的public构造器 Constructor constructor c1.getConstructor(String.class, int.class,int.class); System.out.println(constructor); /** * public com.company.empolyee.User(java.lang.String,int,int) */ //7-2.获取指定的不限制权限的构造器 Constructor declaredConstructor c1.getDeclaredConstructor(参数) System.out.println(declaredConstructor); } }说明getMethod和getDeclaredMethod方法指定方法的参数时无参函数nullintint.classStringString.class....5.7 通过上述API获得类的信息后怎么用呢—— 动态创建对象、动态执行方法、动态修改属性甚至操作private成员注意01User类必须有显式的无参构造方法才能用c1.newInstance()动态创建对象在java中定义有参构造器后不会自动生成无参构造器C会自动生成。 否则注意02动态调用方法的时候注意使用invoke对象方法的参数方法激活注意03动态操作属性的时候用set对象值方法注意04操作private构造器、属性、方法的时候由于在类外不能操作private成员。我们需要调用setAccessabletrue方法关掉安全检测。that is to say反射可以操作private成员import com.company.empolyee.User; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class ReflectCoreOperation { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { //通过反射获取Class对象 Class c1 Class.forName(com.company.empolyee.User); //动态创建User对象法一反射出的class对象直接调用newInstance() //User user1 (User) c1.newInstance(); //动态创建User对象法二利用反射出的构造器 Constructor constructor c1.getDeclaredConstructor(String.class, int.class, int.class); User user2 (User)constructor.newInstance(December, 28, 28); //动态调用User类方法 Method setName c1.getDeclaredMethod(setName, String.class); setName.invoke(user2,please, hang in there!); //激活 System.out.println(user2.getName()); //动态设置属性 Field name c1.getDeclaredField(name); name.setAccessible(true); name.set(user2,stick it out!); System.out.println(user2.getName()); } }5.8 性能测试测试3种情况的时间长短普通方法进行方法调用、反射方式进行方法调用、关掉安全检测的反射方式进行方法调用import com.company.empolyee.User; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class PerformTest { //普通方式 public static void test01(){ User user new User(December, 28, 28); long startTime System.currentTimeMillis(); for (int i 0; i 1000000000; i) { user.getName(); } long endTime System.currentTimeMillis(); System.out.println(普通方式10亿次 (endTime-startTime)ms); } //反射方式 public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { User user new User(December, 28, 28); Class c1 user.getClass(); Method getName c1.getDeclaredMethod(getName, null); long startTime System.currentTimeMillis(); for (int i 0; i 1000000000; i) { getName.invoke(user,null); } long endTime System.currentTimeMillis(); System.out.println(反射方式10亿次 (endTime-startTime)ms); } //反射方式关掉安全检测 public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { User user new User(December, 28, 28); Class c1 user.getClass(); Method getName c1.getDeclaredMethod(getName, null); getName.setAccessible(true); long startTime System.currentTimeMillis(); for (int i 0; i 1000000000; i) { getName.invoke(user,null); } long endTime System.currentTimeMillis(); System.out.println(反射方式10亿次 (endTime-startTime)ms); } public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { test01(); test02(); test03(); } }普通方式10亿次3ms反射方式10亿次1530ms反射方式10亿次1074ms由此看出1在一开始我们就说了反射的缺点是慢 牺牲时间增加程序的灵活性2对比第二个和第三个结果可以看出关掉安全检测可以节省不少时间。如果程序频繁用到反射可以关掉安全监测来提升效率现在又是牺牲安全增加效率5.9 还能干什么获取泛型的信息一旦编译完成后所有和泛型有关的信息将会被擦除。那如何获取一个类中泛型的信息呢因为类最初被加载的时候所有信息都存储在Class对象里或许Class对象里保存了泛型的信息import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.Map; public class GetGenericInfo { //成员方法的参数是泛型 public void test01(MapString,Integer map, ListInteger list){ System.out.println(test01); } //成员方法的返回值是泛型 public MapString,Integer test02(){ System.out.println(test02); return null; } public static void main(String[] args) throws NoSuchMethodException { Class c1 GetGenericInfo.class; Method method01 c1.getDeclaredMethod(test01, Map.class, List.class); //获取泛型参数的信息 System.out.println(获取泛型参数的信息); Type[] genericParameterTypes method01.getGenericParameterTypes(); for (Type genericParameterType : genericParameterTypes) { System.out.println(genericParameterType); } //进一步获取泛型规定的类型 System.out.println(进一步获取泛型规定的类型); for (Type genericParameterType : genericParameterTypes) { if(genericParameterType instanceof ParameterizedType){ //ParameterizedType参数化类型 Type[] actualTypeArguments ((ParameterizedType) genericParameterType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); } } } Method method02 c1.getDeclaredMethod(test02, null); //获取泛型返回值的信息 System.out.println(获取泛型返回值的信息); Type genericReturnType method02.getGenericReturnType(); System.out.println(genericReturnType); //进一步获取泛型返回值规定的类型 System.out.println(进一步获取泛型返回值规定的类型); if(genericReturnType instanceof ParameterizedType){ //ParameterizedType参数化类型 Type[] actualTypeArguments ((ParameterizedType) genericReturnType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); } } } }获取泛型参数的信息java.util.Mapjava.lang.String, java.lang.Integerjava.util.Listjava.lang.Integer进一步获取泛型规定的类型class java.lang.Stringclass java.lang.Integerclass java.lang.Integer获取泛型返回值的信息java.util.Mapjava.lang.String, java.lang.Integer进一步获取泛型返回值规定的类型class java.lang.Stringclass java.lang.Integer5.10 还能干什么获取注解信息import java.lang.annotation.*; import java.lang.reflect.Field; public class GetAnnotationInfo { public static void main(String[] args) throws NoSuchFieldException { Class c1 Student_Table.class; //1.获取类的注解 Annotation[] annotations c1.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } /** * 输出 * TableName(valuedb_stu) */ //2.获取注解value的值 TableName annotation (TableName)c1.getAnnotation(TableName.class); System.out.println(annotation.value()); /** * 输出 * db_stu */ //3.获取字段的注解 Field name c1.getDeclaredField(name); TableField annotation1 name.getAnnotation(TableField.class); //说明是哪个注解因为一个字段可能被多个注解修饰 System.out.println(annotation1.name()); System.out.println(annotation1.type()); System.out.println(annotation1.length()); /** * 输出 * name * varchar * 3 */ } } TableName(db_stu) class Student_Table{ TableField(name name,type varchar,length 3) String name; TableField(name age,type int,length 1) int age; TableField(name age,type int,length 1) int id; } /** * 数据库表名注解 */ Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) interface TableName{ String value(); } /** * */ Target(ElementType.FIELD) Retention(RetentionPolicy.RUNTIME) interface TableField{ String name(); String type(); int length(); }
java 注解和反射
发布时间:2026/6/12 7:04:03
一、什么是注解Annnotation框架MVC、springboot的底层就是注解注解和注释的不同注释是写给人看的注解既是写给人看的也是写给程序比如编译器、虚拟机看的。源码有效的注解就是给编译器看的因为还没运行呢这个注解就“功成身退”了而运行时有效的注解是给虚拟机看的。注解的作用1给程序看通过反射机制实现对元数据的访问2检查程序的正确性。比如Override注解会检查重写的方法名是否写对了。二、3个内置注解1. Override2. Deprecated自己也可以给自己之前写的方法打上Deprecated注解3. SuppressWarnings(参数) 镇压方法中的警告信息三、4个元注解元注解注解的注解写在注解的上一行比如Target描述注解可以放在哪里方法的上面还是类的上面还是字段的上面。。。Retention注解的生命周期SOURE源码时有效 CLASS编译成.class时依然有效 RUNTIME 运行时依然有效一般写这个SOURCE是什么意思我们的Data、Setter、注解都是被RetentionRetentionPolicy.SOURCE去修饰的。写源码时可以看到Setter注解但是编译后.calss文件里多了setter方法而Setter注解却功成身退了用反编译去看。还有Override在编译的时候就告诉编译器要检查重写的语法是否符合父类规定,生成的.class文件不含这些注解。CLASS是什么意思注解被保留到class文件但jvm加载class文件时候被遗弃这是默认的生命周期。这个的例子还没理解到位以后再来填坑Documnet注解本身是否包含在 JavaDoc 文档中。默认情况下注解不会出现在生成的文档里Inherited子类可以继承父类中的注解//Target表明 MyAnnotation 可以用在方法上、类上、字段上、入参上注解上 Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.Type类上,ElementType.FIELD, ElementType.ANNOTATION_TYPE}) //Retention表明 MyAnnotation从源码到运行时都有效也就是一直有效 Retention(value RetentionPolicy.RUNTIME) //Documented 表明将注解 MyAnnotation写入javadoc中 Documented //Inherited表明注解MyAnnotation可被子类继承 Inherited //自定义一个注解 interface MyAnnotation{ }四、自定义注解为什么注解和反射放在一起讲有什么关系吗先放一放这个问题先问自定义注解如何用用来打tag。比如你想标记一批方法用来给程序看那你就自定义一个注解遇到带这类标签的方法就用springAOP的方式执行某一些操作springAOP是通过反射实现的这就回答了刚刚提出的问题“为什么注解和反射放在一起讲有什么关系吗”。别绕晕了标签一定是打给程序看的如果只是单纯的打标签给你自己看的话那不就是注释嘛用interface自定义注解自动继承java.lang.annotation.Annotation接口如果自定义注解只有一个参数建议命名为value。这样使用注解的时候就不用指明参数名了一个参数时//测试注解在类上有效 MyAnnotation public class DiyAnnotation { //注解只有一个参数且命名为value时可以省略 value MyAnnotation2(2022 is wonderful enough! kickboxing!) public void test03(){ } } Target(value {ElementType.METHOD,ElementType.TYPE}) Retention(value RetentionPolicy.RUNTIME) //自定义一个注解 interface MyAnnotation2{ String value() default ; //只有一个参数 }多个参数时//测试注解在类上有效 MyAnnotation public class DiyAnnotation { //测试注解在方法上有效 //三个参数都有默认值注解可以都不写参数 MyAnnotation public static void main(String[] args) { } //也可以写参数 MyAnnotation(name December,age27,word New Year is around the corner!) public void test01(){ } //也可以写部分参数 MyAnnotation(word New start, must be wonderful!) public void test02(){ } } Target(value {ElementType.METHOD,ElementType.TYPE}) Retention(value RetentionPolicy.RUNTIME) //自定义一个注解 interface MyAnnotation{ /* * 注解的参数们 * 必须带括号 * */ String name() default ; //第一个参数默认值为空 int age() default 18; //第二个参数默认值为18 String word() default ; //第三个参数,默认值为空 }四、注解是如何生效的*换个问法为什么加一个Data注解编译后就自动生成了equals等一系列方法为什么给BookServiceImpl加一个Service注解就自动注入了一个bean解析注解的两个阶段/两种方式刚好对应了上面两种场景javac:编译器读取注解然后自动生成代码或者Override对代码进行语法检查java: java虚拟机运行时基于反射去获取类的属性、执行类的方法。JVM扫描到Service方法的时候本质上是执行了BookServiceImpl类的setter方法五、什么是反射Reflection彻底理解反射机制就像Linux认为all is file一样java中一切都是对象那么类是不是对象呢是。类能是谁的对象呢所有的类都是java.lang.Class类的对象用Class c1 类.class的c1去表示因为类也是对象对象可以调用成员方法那么c1就可以用java.lang.Class类定义的成员方法去获得类的各种信息。此外反射被称为框架的灵魂主要是因为它赋予了我们在运行时1获取类的信息 以及2执行类中方法的能力。—— 运行时获取任意一个对象所属的类Class c1【就跟你亲眼见到这个类的.java文件一样】└─—— 获得了Class c1后就能知道这个类的所有属性├─ 包括public、protected、private└─—— 获得了Class c1后就能知道这个类的所有方法├─ 方法的参数└─ 方法的返回值—— 运行时创建任意一个类的对象、调用类的方法。【就跟你打开其他.java文件要new一个这个类的对象开始使用一样】java本身是一个静态语言正是反射让java具有动态性5.1 静态语言和动态语言区分动态语言和动态网页技术静态语言与动态语言静态类型语言在编译时便已确定变量的类型而动态类型语言的变量类型要到程序运行的时候待变量被赋予某个值之后才会具有某种类型静态网页技术与动态网页技术HTML是静态网页技术JSP是动态网页技术动态部分就是% %括起来的java代码。5.2 反射的优缺点优点灵活性静态语言-动态语言利用反射开发出各种框架spring/mybatis/springboot极大方便了我们的开发。缺点慢反射也是生成一个对象和我们直接new一个对象比起来慢了几十几百倍5.3 反射相关的API学习反射就是学习Class对象和java.lang.reflect对应的各种API听到这里放心了吧说到底还是和集合一样学习各种API而且十分简单5.4 获得反射对象5.5 得到Class类对象的5种方式1. java.lang.Class类为了方便讲述我们假设Person类是当前类Class类是反射的根源反射前必须找到相应的Class对象。这个Class对象中存储了Person类对象的真实类、类的属性、方法、实现了哪些接口等一切信息。对于每个类JRE都为其保存了一个不变的Class类型的对象也就是说由系统建立的。Person类的所有对象共享并且记得这个Class类型的对象。User user1 new User(); User user2 new User(); User user3 new User(); System.out.println(user1.hashCode()); System.out.println(user2.hashCode()); System.out.println(user3.hashCode()); /** * 输出 * 2027961269 * 1586270964 * 1642360923 */ Class c1 user1.getClass(); Class c2 user2.getClass(); Class c3 user3.getClass(); System.out.println(c1.hashCode()); System.out.println(c2.hashCode()); System.out.println(c3.hashCode()); /** * 输出 * 1343441044 * 1343441044 * 1343441044 */2. 得到Class类对象的5种方式public class GetClassInstanceTest { public static void main(String[] args) throws ClassNotFoundException { Person person new Student(); //法一通过继承object类的getClass方法 Class c1 person.getClass(); System.out.println(c1); System.out.println(c1.hashCode()); //法二通过Class类的静态方法forName类的路径,需要抛出异常 Class c2 Class.forName(Student); //应该是【包名.类名】例如com.company.reflection.Student System.out.println(c2); System.out.println(c2.hashCode()); //法三类名.class属性 Class c3 Student.class; System.out.println(c3); System.out.println(c3.hashCode()); //法四基本数据类型包装类的TYPE属性 Class c4 Integer.TYPE; System.out.println(c4); System.out.println(c4.hashCode()); //法五已知子类型获取父类型 Class c5 c1.getSuperclass(); System.out.println(c5); System.out.println(c5.hashCode()); } } class Person{ String name; public String getName() { return name; } public void setName(String name) { this.name name; } } class Student extends Person{ public Student() { this.name 学生; } } class Teacher extends Person{ public Teacher(){ this.name老师; } }对应结果class Student356573597class Student356573597class Student356573597int1735600054class Person21685669这5种方式中只有forName是动态加载运行时才加载其他只要出现new了就是在编译时加载其他都是静态加载。5.5 哪些类可以有Class对象几乎所有Class c1 Object.class; //类 Class c2 Comparable.class; //接口 Class c3 String[].class; //一维数组 Class c4 int[][].class; //二维数组 Class c5 Override.class; //注解 Class c6 ElementType.class; //枚举 Class c7 Integer.class; //基本数据类型包装类 Class c8 void.class; //void Class c9 Class.class; //Class类本身 //------类型 getName() System.out.println(c1); // class java.lang.Object System.out.println(c2); // interface java.lang.Comparable System.out.println(c3); // class [Ljava.lang.String; System.out.println(c4); // class [[I System.out.println(c5); // interface java.lang.Override System.out.println(c6); // class java.lang.annotation.ElementType System.out.println(c7); // class java.lang.Integer System.out.println(c8); // void System.out.println(c9); // class java.lang.Class5.6 获取类的运行时结构在module的src文件夹下新建包com.company.empolyee新建User.java文件。public class User{ //私有 private String name; private int age; //共有 public int id; public User(String name, int age, int id) { this.name name; this.age age; this.id id; } 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; } //私有方法 private void laugh(){ } }import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class ClassLoaderTest { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException { Class c1 Class.forName(com.company.empolyee.User); //1.获取类的名字 System.out.println(c1.getName()); //包名类型 System.out.println(c1.getSimpleName()); //只有类名 /** * 输出 * com.company.empolyee.User * User */ System.out.println(); //2.获取类所有的成员变量 //2-1.getFields只能获取public成员变量 Field[] fields c1.getFields(); for (Field field : fields) { System.out.println(field); } /** * 输出 * public int com.company.empolyee.User.id */ //2-2.getDeclaredFields可以获取所有访问权限的成员变量 Field[] declaredFields c1.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println(declaredField); } /** * 输出 * private java.lang.String com.company.empolyee.User.name * private int com.company.empolyee.User.age * public int com.company.empolyee.User.id */ System.out.println(); //3.获取类指定的成员变量 //3-1.getField只能获取public Field id c1.getField(id); System.out.println(id); /** * 输出 * public int com.company.empolyee.User.id */ //3-2.getDeclaredField获取所有权限 Field name c1.getDeclaredField(name); System.out.println(name); /** * 输出 * private java.lang.String com.company.empolyee.User.name */ System.out.println(); //4.获取类所有的成员方法 //4-1.public Method[] methods c1.getMethods(); for (Method method : methods) { System.out.println(method); } /** * 输出不仅输出了当前类自己定义的public成员方法还有继承下来的所有public方法 * public java.lang.String com.company.empolyee.User.getName() * public void com.company.empolyee.User.setName(java.lang.String) * public int com.company.empolyee.User.getAge() * public void com.company.empolyee.User.setAge(int) * public final void java.lang.Object.wait() throws java.lang.InterruptedException * public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException * public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException * public boolean java.lang.Object.equals(java.lang.Object) * public java.lang.String java.lang.Object.toString() * public native int java.lang.Object.hashCode() * public final native java.lang.Class java.lang.Object.getClass() * public final native void java.lang.Object.notify() * public final native void java.lang.Object.notifyAll() */ System.out.println(); //4-2.获取本类的所有权限的所有方法不包括继承来的 Method[] declaredMethods c1.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println(declaredMethod); } /** * 输出 * public java.lang.String com.company.empolyee.User.getName() * public void com.company.empolyee.User.setName(java.lang.String) * public int com.company.empolyee.User.getAge() * private void com.company.empolyee.User.haha() //私有的 * public void com.company.empolyee.User.setAge(int) */ System.out.println(); //5.获取指定的类方法 //5-1.获取指定的public方法 一定要指定方法的参数因为重载 Method method c1.getMethod(setAge, int.class); System.out.println(method); /** * public void com.company.empolyee.User.setAge(int) */ //5-2.获取指定的不限权限方法 Method laugh c1.getDeclaredMethod(laugh, null); System.out.println(laugh); /** * private void com.company.empolyee.User.laugh() */ System.out.println(构造器); //6.获取所有的构造器 //6-1.获取所有public构造器 Constructor[] constructors c1.getConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor); } /** * public com.company.empolyee.User(java.lang.String,int,int) */ //6-2.获取所有不限权限的构造器 Constructor[] declaredConstructors c1.getDeclaredConstructors(); for (Constructor declaredConstructor : declaredConstructors) { System.out.println(declaredConstructors); } /** * [Ljava.lang.reflect.Constructor;45ee12a7 */ System.out.println(单个的构造器); //7.获取指定的构造器 //7-1.获取指定的public构造器 Constructor constructor c1.getConstructor(String.class, int.class,int.class); System.out.println(constructor); /** * public com.company.empolyee.User(java.lang.String,int,int) */ //7-2.获取指定的不限制权限的构造器 Constructor declaredConstructor c1.getDeclaredConstructor(参数) System.out.println(declaredConstructor); } }说明getMethod和getDeclaredMethod方法指定方法的参数时无参函数nullintint.classStringString.class....5.7 通过上述API获得类的信息后怎么用呢—— 动态创建对象、动态执行方法、动态修改属性甚至操作private成员注意01User类必须有显式的无参构造方法才能用c1.newInstance()动态创建对象在java中定义有参构造器后不会自动生成无参构造器C会自动生成。 否则注意02动态调用方法的时候注意使用invoke对象方法的参数方法激活注意03动态操作属性的时候用set对象值方法注意04操作private构造器、属性、方法的时候由于在类外不能操作private成员。我们需要调用setAccessabletrue方法关掉安全检测。that is to say反射可以操作private成员import com.company.empolyee.User; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class ReflectCoreOperation { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { //通过反射获取Class对象 Class c1 Class.forName(com.company.empolyee.User); //动态创建User对象法一反射出的class对象直接调用newInstance() //User user1 (User) c1.newInstance(); //动态创建User对象法二利用反射出的构造器 Constructor constructor c1.getDeclaredConstructor(String.class, int.class, int.class); User user2 (User)constructor.newInstance(December, 28, 28); //动态调用User类方法 Method setName c1.getDeclaredMethod(setName, String.class); setName.invoke(user2,please, hang in there!); //激活 System.out.println(user2.getName()); //动态设置属性 Field name c1.getDeclaredField(name); name.setAccessible(true); name.set(user2,stick it out!); System.out.println(user2.getName()); } }5.8 性能测试测试3种情况的时间长短普通方法进行方法调用、反射方式进行方法调用、关掉安全检测的反射方式进行方法调用import com.company.empolyee.User; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class PerformTest { //普通方式 public static void test01(){ User user new User(December, 28, 28); long startTime System.currentTimeMillis(); for (int i 0; i 1000000000; i) { user.getName(); } long endTime System.currentTimeMillis(); System.out.println(普通方式10亿次 (endTime-startTime)ms); } //反射方式 public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { User user new User(December, 28, 28); Class c1 user.getClass(); Method getName c1.getDeclaredMethod(getName, null); long startTime System.currentTimeMillis(); for (int i 0; i 1000000000; i) { getName.invoke(user,null); } long endTime System.currentTimeMillis(); System.out.println(反射方式10亿次 (endTime-startTime)ms); } //反射方式关掉安全检测 public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { User user new User(December, 28, 28); Class c1 user.getClass(); Method getName c1.getDeclaredMethod(getName, null); getName.setAccessible(true); long startTime System.currentTimeMillis(); for (int i 0; i 1000000000; i) { getName.invoke(user,null); } long endTime System.currentTimeMillis(); System.out.println(反射方式10亿次 (endTime-startTime)ms); } public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { test01(); test02(); test03(); } }普通方式10亿次3ms反射方式10亿次1530ms反射方式10亿次1074ms由此看出1在一开始我们就说了反射的缺点是慢 牺牲时间增加程序的灵活性2对比第二个和第三个结果可以看出关掉安全检测可以节省不少时间。如果程序频繁用到反射可以关掉安全监测来提升效率现在又是牺牲安全增加效率5.9 还能干什么获取泛型的信息一旦编译完成后所有和泛型有关的信息将会被擦除。那如何获取一个类中泛型的信息呢因为类最初被加载的时候所有信息都存储在Class对象里或许Class对象里保存了泛型的信息import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.Map; public class GetGenericInfo { //成员方法的参数是泛型 public void test01(MapString,Integer map, ListInteger list){ System.out.println(test01); } //成员方法的返回值是泛型 public MapString,Integer test02(){ System.out.println(test02); return null; } public static void main(String[] args) throws NoSuchMethodException { Class c1 GetGenericInfo.class; Method method01 c1.getDeclaredMethod(test01, Map.class, List.class); //获取泛型参数的信息 System.out.println(获取泛型参数的信息); Type[] genericParameterTypes method01.getGenericParameterTypes(); for (Type genericParameterType : genericParameterTypes) { System.out.println(genericParameterType); } //进一步获取泛型规定的类型 System.out.println(进一步获取泛型规定的类型); for (Type genericParameterType : genericParameterTypes) { if(genericParameterType instanceof ParameterizedType){ //ParameterizedType参数化类型 Type[] actualTypeArguments ((ParameterizedType) genericParameterType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); } } } Method method02 c1.getDeclaredMethod(test02, null); //获取泛型返回值的信息 System.out.println(获取泛型返回值的信息); Type genericReturnType method02.getGenericReturnType(); System.out.println(genericReturnType); //进一步获取泛型返回值规定的类型 System.out.println(进一步获取泛型返回值规定的类型); if(genericReturnType instanceof ParameterizedType){ //ParameterizedType参数化类型 Type[] actualTypeArguments ((ParameterizedType) genericReturnType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); } } } }获取泛型参数的信息java.util.Mapjava.lang.String, java.lang.Integerjava.util.Listjava.lang.Integer进一步获取泛型规定的类型class java.lang.Stringclass java.lang.Integerclass java.lang.Integer获取泛型返回值的信息java.util.Mapjava.lang.String, java.lang.Integer进一步获取泛型返回值规定的类型class java.lang.Stringclass java.lang.Integer5.10 还能干什么获取注解信息import java.lang.annotation.*; import java.lang.reflect.Field; public class GetAnnotationInfo { public static void main(String[] args) throws NoSuchFieldException { Class c1 Student_Table.class; //1.获取类的注解 Annotation[] annotations c1.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } /** * 输出 * TableName(valuedb_stu) */ //2.获取注解value的值 TableName annotation (TableName)c1.getAnnotation(TableName.class); System.out.println(annotation.value()); /** * 输出 * db_stu */ //3.获取字段的注解 Field name c1.getDeclaredField(name); TableField annotation1 name.getAnnotation(TableField.class); //说明是哪个注解因为一个字段可能被多个注解修饰 System.out.println(annotation1.name()); System.out.println(annotation1.type()); System.out.println(annotation1.length()); /** * 输出 * name * varchar * 3 */ } } TableName(db_stu) class Student_Table{ TableField(name name,type varchar,length 3) String name; TableField(name age,type int,length 1) int age; TableField(name age,type int,length 1) int id; } /** * 数据库表名注解 */ Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) interface TableName{ String value(); } /** * */ Target(ElementType.FIELD) Retention(RetentionPolicy.RUNTIME) interface TableField{ String name(); String type(); int length(); }