注解和反射(全方面解析)(通过反射获取对象的注解)
前言:注解和反射是一切框架的底层 本文很多是学习狂神视频时做的笔记,感谢狂神,也有加入了自己的理解和思考,希望作为一个整理,也能对大家有所帮助。
注解(java.Annotation)
什么是注解
1.不是程序本身,可以对程序作出解释
2.可以被其他程序(如编译器等)读取
内置注解
@Override 重写
@Deprecated 指这段代码过时了
@SuppressWarning("all") 抑制编译时候的警告信息,需要一个参数
元注解
@Target
class learn extends Object{ @Override public String toString() { return "learn{}"; } @MyAnnotation() public void say(){ } } @Target(value = {ElementType.METHOD}) @interface MyAnnotation{ }复制代码
例如lombol的源码中标记了运行时才有效果。
Retention 美[rɪˈtenʃn] n.保持 维持;
Retention默认RUNTIME
自定义注解
class Person{ @MyAnno(name="lcg",id = 1,schools = {"bjtu","cn"}) public void say(){ } } @Target(value = {ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @interface MyAnno{ String name() default "xixi"; int id() default -1; String[] schools(); }复制代码
反射机制(java.Reflection)
Java是静态语言,因为有了反射机制变成了动态语言
反射:即在运行状态中,对于任意一个类,都能够知道这个类的所以属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性。这种动态获取信息及动态调用对象方法的功能叫Java的反射机制。
静态语言和动态语言
动态语言
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。
主要动态语言:Object-C、JavaScript、PHP、Python、Erlang。
静态语言
与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++、C#。
PS:C#不是动态语言,但是MS有将.NET支持动态语言的趋势,3.0吸收了一定动态特征,比如 匿名函数,临时类型,临时变量等
动态语言示例:
function Person(name){ this.name=name; } Person.prototype.getName=function(){ return this.name; } var person=new Person("okok"); alert(person.getName()); person.getName=function(){return "nono"}; alert(person.getName());复制代码
Java反射机制概述
Demo
package com.company; public class learn1 { public static void main(String[] args) throws ClassNotFoundException { Class c1=Class.forName("com.company.Person"); System.out.println(c1); Class c2=Class.forName("com.company.Person"); /** * 一个类只有一个class对象 * 一个类被加载后,类的整个结构多会被封装在class对象中 */ System.out.println("c1.hashCode"+c1.hashCode()); System.out.println("c2.hashCode"+c2.hashCode()); System.out.println("c1==c2: "+(c2.hashCode()==c1.hashCode()));//true } } class User{ String username; String password; int number; public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public void setNumber(int number) { this.number = number; } public String getUsername() { return username; } public String getPassword() { return password; } public int getNumber() { return number; } public User() { } public User(String username, String password, int number) { this.username = username; this.password = password; this.number = number; } }复制代码
输出
class com.company.Person c1.hashCode692404036 c2.hashCode692404036 c1==c2: true Process finished with exit code 0复制代码
理解Class类并获取Class实例
package com.company; public class learn1 { public static void main(String[] args) throws ClassNotFoundException { Class c1=Class.forName("com.company.Person1"); System.out.println(c1); Class c2=Class.forName("com.company.Person1"); /** * 一个类只有一个class对象 * 一个类被加载后,类的整个结构多会被封装在class对象中 */ System.out.println("c1.hashCode"+c1.hashCode()); System.out.println("c2.hashCode"+c2.hashCode()); System.out.println("c1==c2: "+(c2.hashCode()==c1.hashCode()));//true User user=new User(); //方式一,通过对象获得 Class aClass1=user.getClass(); System.out.println(aClass1.hashCode()); //方式二,通过forname获得 Class aClass2=Class.forName("com.company.User"); System.out.println(aClass2.hashCode()); //方式三,通过类型.class Class<User> aClass3 = User.class; System.out.println(aClass3.hashCode()); //方法四:基本内置类型的包装类都有一个Type的属性 Class<Integer> integerClass = Integer.TYPE; System.out.println(integerClass); //方式五,获取父类类型 Class<AdminUser> adminUserClass = AdminUser.class; Class aClass = adminUserClass.getSuperclass(); System.out.println(adminUserClass+" "+adminUserClass.hashCode()); System.out.println(aClass+" "+aClass.hashCode()); } } class AdminUser extends User{ @Override public void login() { System.out.println("Admin login..."); } } class User{ String username; String password; int number; public void login(){ System.out.println("User login..."); } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public void setNumber(int number) { this.number = number; } public String getUsername() { return username; } public String getPassword() { return password; } public int getNumber() { return number; } public User() { } public User(String username, String password, int number) { this.username = username; this.password = password; this.number = number; } }复制代码
类的加载原理与ClassLoader
public class learn3 { public static void main(String[] args){ A a=new A(); System.out.println(A.m); } } class A{ static { System.out.println("A类静态代码块初始化"); m=300; } static int m=100;//如果m在静态代码块前面的话,m就是300 public A(){ System.out.println("A类的无参构造初始化"); } } output: A类静态代码块初始化 A类的无参构造初始化 100 Process finished with exit code 0复制代码
加载到内存,会产生一个类对应Class对象
链接,链接结束后m=0
初始化
<clinit>(){ System.out.println("A类静态代码块初始化"); m=300; m=100; }复制代码
分析类初始化
子类没有被加载
因为在链接阶段就已经设置类变量默认初始值了
常量在链接阶段就存入调用类的常量池中了
类加载器的作用(了解)
获取运行时类的结构
有了Class对象,能做什么
动态创建对象
直接用字节码文件获取对应实例
// 调用无参构造器 ,若是没有,则会报异常 Object o = clazz.newInstance();复制代码
有带参数的构造函数的类,先获取到其构造对象,再通过该构造方法类获取实例:
//获取构造函数类的对象 Constroctor constroctor = clazz.getConstructor(String.class,Integer.class); ![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/275ddf5d2a8f40968a0c56805934c39e~tplv-k3u1fbpfcp-watermark.image?) // 使用构造器对象的newInstance方法初始化对象 Object obj = constroctor.newInstance("龙哥", 29); 复制代码
调用指定方法
Demo
public class Calculator{ public double add(double score1,double score2){ return score1 + score2; } public void print(){ System.out.println("OK"); } public static double mul(double score1,double score2){ return score1 * score2; } } public class CalculatorTest { public static void main(String[] args) throws Exception { //通过类的.class属性获取 Class<Calculator> clz = Calculator.class; //或者通过类的完整路径获取,这个方法由于不能确定传入的路径是否正确,这个方法会抛ClassNotFoundException // Class<Calculator> clz = Class.forName("test.Calculator"); //或者new一个实例,然后通过实例的getClass()方法获取 // Calculator s = new Calculator(); // Class<Calculator> clz = s.getClass(); //1. 获取类中带有方法签名的mul方法,getMethod第一个参数为方法名,第二个参数为mul的参数类型数组 Method method = clz.getMethod("mul", new Class[]{double.class,double.class}); //invoke 方法的第一个参数是被调用的对象,这里是静态方法故为null,第二个参数为给将被调用的方法传入的参数 Object result = method.invoke(null, new Object[]{2.0,2.5}); //如果方法mul是私有的private方法,按照上面的方法去调用则会产生异常NoSuchMethodException,这时必须改变其访问属性 //method.setAccessible(true);//私有的方法通过发射可以修改其访问权限 System.out.println(result);//结果为5.0 //2. 获取类中的非静态方法 Method method_2 = clz.getMethod("add", new Class[]{double.class,double.class}); //这是实例方法必须在一个对象上执行 Object result_2 = method_2.invoke(new Calculator(), new Object[]{2.0,2.5}); System.out.println(result_2);//4.5 //3. 获取没有方法签名的方法print Method method_3 = clz.getMethod("print", new Class[]{}); Object result_3 = method_3.invoke(new Calculator(), null);//result_3为null,该方法不返回结果 } }复制代码
获取属性值
package com.company.myp1; import java.lang.reflect.Field; public class learn1 { public static void main(String[] args) throws IllegalAccessException { Dog d1=new Dog("xixi",11); Dog d2=new Dog("hehe",22); //获取某属性 System.out.println(ClassUtil.getField(d1.getClass(),"name")); //获取某属性值 Object o1=ClassUtil.getPropertyValue(d1,"name"); System.out.println(o1); //获取全部属性值 Field[] declaredFields = d1.getClass().getDeclaredFields(); for (int i = 0; i < declaredFields.length; i++) { System.out.println(declaredFields[i].getName()+" "+declaredFields[i].get(d2)); } } } class Dog{ String name; int age; public Dog() { } public Dog(String name, int age) { this.name = name; this.age = age; } } class ClassUtil { public static Object getPropertyValue(Object obj, String propertyName) throws IllegalAccessException { Class<?> Clazz = obj.getClass(); Field field; if ((field = getField(Clazz, propertyName)) == null) return null; field.setAccessible(true); return field.get(obj); } public static Field getField(Class<?> clazz, String propertyName) { if (clazz == null) return null; try { return clazz.getDeclaredField(propertyName);//getDeclaredFiled 仅能获取类本身的属性成员(包括私有、共有、保护) } catch (NoSuchFieldException e) { return getField(clazz.getSuperclass(), propertyName);//getField 仅能获取类(及其父类可以自己测试) public属性成员 } } }复制代码
性能对比分析
反射操作范型
反射操作注解
练习Demo,生成sql语句
package com.company.com.learn1; import java.lang.annotation.*; import java.lang.reflect.Field; public class Demo { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException { Class c1 = Class.forName("com.company.com.learn1.Student"); //根据包名获取Class Annotation[] annotations=c1.getAnnotations();//方法返回此元素上存在的所有注解 for (int i = 0; i < annotations.length; i++) { System.out.println(annotations[i]);//只获得了注解,想要注解里面的值 } //获得注解value的值 System.out.println("==============="); TableName tableName = (TableName) c1.getAnnotation(TableName.class); System.out.println(tableName.value()); //获得类指定的注解 System.out.println("==============="); Field f=c1.getDeclaredField("name"); FieldName fieldName=f.getAnnotation(FieldName.class); System.out.println(fieldName.type()); System.out.println(fieldName.lengtn()); System.out.println(fieldName.columnName()); //获得所有字段的列名,类型和长度 System.out.println("==============="); Field[] fieldNames = c1.getDeclaredFields(); for (int i = 0; i < fieldNames.length; i++) { FieldName annotation = fieldNames[i].getAnnotation(FieldName.class); System.out.println(annotation.columnName()+" "+annotation.type()+" "+annotation.lengtn()); } //获得所有字段的属性名,属性值和注解 System.out.println("==============="); Student s=new Student(1001,18,"小明"); //s.getClass().getAnnotation(); TableName annotation = s.getClass().getAnnotation(TableName.class); String mytableName=annotation.value(); Field[] declaredFields = s.getClass().getDeclaredFields(); StringBuffer value=new StringBuffer(); StringBuffer column=new StringBuffer(); for (int i = 0; i < declaredFields.length; i++) { declaredFields[i].setAccessible(true); System.out.println("属性名:" + declaredFields[i].getName() + " 属性值:" + declaredFields[i].get(s)+" 注解columnname "+ declaredFields[i].getAnnotation(FieldName.class).columnName()); value.append(declaredFields[i].get(s)); column.append(declaredFields[i].getAnnotation(FieldName.class).columnName()); if(i!=declaredFields.length-1){ value.append(" , "); column.append(" , "); } } //模仿自己生成sql System.out.println("==============="); System.out.println("利用反射和注解生成sql语句:"); String sql="INSERT INTO "+mytableName+" ( "+column+" ) VALUES "+" ( "+value+" )"; System.out.println(sql); } } @TableName("db_student") class Student{ @FieldName(columnName = "db_id",type = "int",lengtn = 10) private int id; @FieldName(columnName = "db_age",type = "int",lengtn = 10) private int age; @FieldName(columnName = "db_name",type = "varchar",lengtn = 30) private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Student(int id, int age, String name) { this.id = id; this.age = age; this.name = name; } public Student() { } } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface TableName{ String value(); } @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface FieldName{ String columnName(); String type(); int lengtn(); }复制代码
输出:
@com.company.com.learn1.TableName(value=db_student) =============== db_student =============== varchar 30 db_name =============== db_id int 10 db_age int 10 db_name varchar 30 =============== 属性名:id 属性值:1001 注解columnname db_id 属性名:age 属性值:18 注解columnname db_age 属性名:name 属性值:小明 注解columnname db_name =============== 利用反射和注解生成sql语句: INSERT INTO db_student ( db_id , db_age , db_name ) VALUES ( 1001 , 18 , 小明 ) Process finished with exit code 0
作者:feburary
链接:https://juejin.cn/post/7034760945498849293