写点什么

Java 注解与反射 基础

作者:卢衍飞
  • 2022-11-25
    山东
  • 本文字数:8273 字

    阅读完需:约 27 分钟

注解与反射 基础什么是注解 Annotation 注解


Annotation 的作用:不是程序本身,可以对程序做出解释。可以被其他程序(比如 编译器等)读取


annotation 的格式: 注解是以 ”@注释名“ 再代码中存在的,还可以添加一写参数值,例如 @SupperWarnings(value = "unchecked")


Annotation 在哪可以使用? 可以在 package,class,method,field 等上面,相当于给他们添加额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问


内置注解 @Override 重写


@Deprecated 不鼓励使用,但是可以使用


@SuppreWarning 警告


元注解元注解的作用是负责注解其他注解


@Target


@Retention


@Documented


@ Inherited


package opp2;


import java.lang.annotation.*;


public class demo01 {public void test(){


}
复制代码


}


//定义一个注解//Target 表示我们的注解可以用在上面地方 @Target(value = {ElementType.METHOD, ElementType.TYPE})


//Retention 表示我们的注解在上面地方有效//RUNTIME >CLASS >SOURCES@Retention(value = RetentionPolicy.RUNTIME)


//Documented 表示释放将我们的注解生成在 JavaDoc 中 @Documented


//Inherited 子类可以继承父类的注解 @Inherited@interface MyAnnotation{


}自定义注解使用 @interface 自定义注解时,自动继承 import java.lang.annotation.Annotation 接口


@interface 用来声明一个注解,格式: public @interface 注解名{定义内容}其中的每一个方法实际上声明的是一个配置函数方法的名称就是参数的名称返回值类型就是参数的类型(返回值只能是基本类型,class,string,enum)可以通过 default 来声明参数默认值如果只有一个参数成员,一般参数名为 value 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0 作为默认值 package opp2;


import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;


public class demo02 {


//注解可以显示赋值,如果没有赋值就必须赋值@MyAnnotation2(name = "钟离")public void test(){}
@MyAnnotation3("")public void test2(){}
复制代码


}


@Target(value = {ElementType.METHOD, ElementType.TYPE})@Retention(value = RetentionPolicy.RUNTIME)@interface MyAnnotation2{//直接的参数:参数类型 +参数名()String name();String color() default "";int age() default 0;int id() default -1;


String[] schools() default {""};
复制代码


}


@Target(value = {ElementType.METHOD, ElementType.TYPE})@Retention(value = RetentionPolicy.RUNTIME)@interface MyAnnotation3{String value();}反射 java.Reflection


时 Java 被视为动态语言的关键,反射机制允许程序在执行期借助 Reflection api 获得任何类的信息,并能直接操作任意对象的内部属性及方法。


获得反射对象 getClass()


package opp2;


/**


  • 什么是反射*/public class demo03 {public static void main(String[] args) throws ClassNotFoundException {Class<?> c1 = Class.forName("opp2.User");Class<?> c2 = Class.forName("opp2.User");Class<?> c3 = Class.forName("opp2.User");System.out.println(c1);

  • }}


//实体类 class User{private String name;private int id;private int age;


public User(){
}
public User(String name, int id, int age) { this.name = name; this.id = id; this.age = age;}
public String getName() { return name;}
public void setName(String name) { this.name = 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;}
@Overridepublic String toString() { return "User{" + "name='" + name + '\'' + ", id=" + id + ", age=" + age + '}';}
复制代码


}得到 class 类的几种方式 Java 注解与反射 基础插图


package opp2;


public class demo04 {public static void main(String[] args) throws ClassNotFoundException {Person person = new student();System.out.println(person.name);


    //方式一 :通过对象获得    Class c1 = person.getClass();    System.out.println(c1.hashCode());
//方法二:forName 获得 Class c2 = Class.forName("opp2.student"); System.out.println(c2.hashCode());
//方法三: 类名.class获得 Class c3 = student.class; System.out.println(c3.hashCode());
//方法四 基本内置类型的包装类都有一个Type属性 Class c4 = Integer.TYPE; System.out.println(c4);
//方法五:获得父类类型 Class c5 = c1.getSuperclass(); System.out.println(c5);}
复制代码


}


class Person{String name;public Person(){}


public Person(String name) {    this.name = name;}
@Overridepublic String toString() { return "Person{" + "name='" + name + '\'' + '}';}
复制代码


}class student extends Person{public student(){this.name = "学生";}}


class teacher extends Person{public teacher(String name) {this.name = "老师";}}所有类型的 Class 对象 class:外部类、成员(内部成员、外部成员),局部内部类,匿名内部类


interface:接口


[] :数组


enum:枚举


annotation:注解 @interface


primitive type:基本数据类型


void


package opp2;


import javax.xml.bind.Element;


public class demo {public static void main(String[] args) {Class c1 = Object.class; //类 Class c2 = Comparable.class; //接口 Class c3 = String[].class; //一维数组 Class c4 = int[][].class; //二维数组 Class c5 = Override.class; //注解 Class c6 = Element.class; //枚举 Class c7 = Integer.class; //基本数据类型 Class c8 = void.class; //voidClass c9 = Class.class; //Class


    System.out.println(c1);    System.out.println(c2);    System.out.println(c3);    System.out.println(c4);    System.out.println(c5);    System.out.println(c6);    System.out.println(c7);    System.out.println(c8);    System.out.println(c9);
int[] a = new int[10]; int[] b = new int[100]; System.out.println(a.getClass().hashCode()); System.out.println(b.getClass().hashCode()); //只要元素类型与维度一样,就是同一个class}
复制代码


}类加载内存分析加载------>链接---->初始化


package opp2;


public class demo06 {public static void main(String[] args) {A a = new A();System.out.println(a.m);/*** 1.【加载】到内存,会产生一个类对应的 Class 对象* 2.【链接】,链接后, m = 0* 3.【初始化】:* <clint>(){* system.out.println("A 类的静态代码块初始化");* m = 300;* m = 100;* }*/}}


class A{static {System.out.println("A 类的静态代码块");m = 300;}


/** * m = 300 *    ↓ * m = 100 */static int m = 100;public A(){    System.out.println("A类的无参构造初始化");}
复制代码


}分析类初始化类的主动引用(一定会发生初始化)类的被动引用(不会发生类的初始化)package opp2;


public class demo07 {


static {    System.out.println("main类被加载");}
public static void main(String[] args) throws ClassNotFoundException { //1. 主动引用 S s = new S(); //反射也会主动引用 Class.forName("opp.demo07.S");
//2. 不会产生类的引用 System.out.println(S.b); //静态域 访问 S[] array = new S[5]; //数组访问 System.out.println(S.M); //访问常量}
复制代码


}


class F{static int b = 2;static {System.out.println("父类被加载");}}


class S extends F{static {System.out.println("子类被加载");m = 300;}static int m = 100;static final int M = 1; //常量}类加载器 Java 注解与反射 基础插图 1


package opp2;


public class demo08 {public static void main(String[] args) throws ClassNotFoundException {//获取系统类的加载器 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();System.out.println(systemClassLoader);//获取系统类加载器的父类加载器->扩展加载器 ClassLoader parent = systemClassLoader.getParent();System.out.println(parent);


    //获取扩展类的父类加载器--->根加载器(c/c++ ,java无法获取,显示为 null)    ClassLoader parent1 = parent.getParent();    System.out.println(parent1);    //null
//测试当前类是哪个加载器加载的 ClassLoader aClass = Class.forName("opp2.demo08").getClassLoader(); System.out.println(aClass);
//测试JDK aClass = Class.forName("java.lang.Object").getClassLoader(); System.out.println(aClass); //null
//如何让获得系统类加载器可以加载的路径 System.out.println(System.getProperty("java.class.path"));
}
复制代码


}获取类的运行时结构通过反射获取运行时类的完整结构


Field、Method、Constructor、Superclass、Interface、Annotation


package opp2;


import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;


public class demo09 {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {Class<?> c1 = Class.forName("opp2.User");


    User user = new User();    //c1 = user.getClass();
//获得类的名字 System.out.println(c1.getName()); System.out.println(c1.getSimpleName());
//获得类的属性 Field[] field = c1.getFields(); //只能找到public属性
复制代码


// for (Field field1 : field){// System.out.println(field1);// }//field = c1.getDeclaredFields();for (Field fields : field){ //找到全部属性 System.out.println(fields);}


    Field name = c1.getDeclaredField("name");//获得指定属性的值    System.out.println(name);
//获得类的方法 System.out.println("====================================================="); Method[] methods = c1.getMethods(); //获取本类及父类的全部public方法 for (Method method :methods){ System.out.println(method); }
methods = c1.getDeclaredMethods(); //获取本类的所有方法 System.out.println("============================"); for (Method method:methods){
System.out.println("getDeclaredMethods " + method ); }
//获得指定方法 System.out.println("======================="); Method getName = c1.getMethod("getName",null); Method setName = c1.getMethod("setName",String.class); System.out.println(getName); System.out.println(setName);
//获得构造器 System.out.println("======================"); Constructor[] constructors = c1.getConstructors(); for (Constructor constructor:constructors){ System.out.println(constructor); } constructors = c1.getDeclaredConstructors(); for (Constructor constructor: constructors){ System.out.println("getDeclaredConstructors" + constructor); }
//获得指定构造器 Constructor<?> declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class); System.out.println("指定" + declaredConstructor);
}
复制代码


}动态创建对象执行方法能做啥?


步骤:


通过 Class 类的 getDeclaredConstructor(Class ... parameterType)获取本类的指定形参类型的构造器向构造器的行参中传递一个对象素组进去,里面包含了构造器中所需的各个参数通过 Constructor 实例化对象 package opp2;


import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;


public class demo10 {public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {


    //获得对象    Class<?> c1 = Class.forName("opp2.User");
//构造对象 User user = (User)c1.newInstance(); //本质是构造了无参构造器 System.out.println(user);
//通过构造器创建对象 Constructor<?> declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class); User user1 = (User) declaredConstructor.newInstance("摩拉克斯",001,18); System.out.println(user1);
//通过反射调用普通方法 User user2 =(User) c1.newInstance(); //通过反射获取一个方法 Method setName = c1.getDeclaredMethod("setName", String.class); //invoke:激活 //(对象,方法的值) setName.invoke(user2,"巴巴托斯"); System.out.println(user2.getName());
//通过反射操作属性 User user3 = (User) c1.newInstance(); Field name = c1.getDeclaredField("name");
name.setAccessible(true); //关闭安全检测。因为原来的的属性是private,不能直接访问,所以需要关闭安全检测 name.set(user3,"巴尔泽布"); System.out.println(user3.getName());
}
复制代码


}性能对比分析 setAccessible:启动和禁用访问安全检查的开关。参数值为 true 则指反射的对象在使用时应该取消 java 语言访问检查


提高反射效率。如果代码中必须使用反射,而该句代码需要频发的被调用,那么请设置为 true 使得原本无法访问的私有成员也可以访问参数值为 false 则指反射的对象在使用时对 java 语言进行访问检查


package opp2;


import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;


public class demo11 {public static void test1(){User user = new User();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 test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {    User user = new User();    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 test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { User user = new User(); 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 InvocationTargetException, NoSuchMethodException, IllegalAccessException { test1(); test2(); test3();}
复制代码


}获取泛型信息 Java 采用泛型擦除的机制来引入泛型,Java 中的泛型仅仅是给编辑器 javac 使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除


为了通过反射操作这些类型,Java 新增了 ParameterizedType,GenericArrayType,TypeVariable 和 WindcardType 几种类型来代表不能被归一到 Class 类中的类型但是又和原始类型齐名的类型


ParameterizedType:表示一种参数化类型,不如 CollectionGenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型 TypeVariable:是各种类型变量的公共父接口 WindcardType:代表一种通配符类型的公示 package opp2;


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 demo12 {public void test(Map<String,User> map, List<User>list){System.out.println("test01");}public Map<String,User> test02(){System.out.println("test02");return null;}

  • public static void main(String[] args) throws NoSuchMethodException {Method method = demo12.class.getMethod("test", Map.class, List.class);Type[] genericParameterTypes = method.getGenericParameterTypes();for (Type ge : genericParameterTypes){System.out.println("#" + ge);if (ge instanceof ParameterizedType){Type[] actualTypeArguments = ((ParameterizedType) ge).getActualTypeArguments();for (Type act : actualTypeArguments){System.out.println(act);}}}method = demo12.class.getMethod("test02", null);Type genericParameterType = method.getGenericReturnType();if (genericParameterType instanceof ParameterizedType){Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();for (Type act : actualTypeArguments){System.out.println(act);}}}}获取注解信息 ORM 对象关系映射


package opp2;


import java.lang.annotation.*;import java.lang.reflect.Field;


/**


  • 练习反射操作注解*/public class demo13 {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {Class<?> c1 = Class.forName("opp2.Student2");Annotation[] annotation = c1.getAnnotations();


// Field name = c1.getDeclaredField("name");Field name = c1.getDeclaredField("id");Fk annotation1 = name.getAnnotation(Fk.class);System.out.println(annotation1.columnName());System.out.println(annotation1.Type());System.out.println(annotation1.length());


}
复制代码


}


@TableM("db_student")class Student2{@Fk(columnName = "db_id",Type = "int", length = 10)private int id;@Fk(columnName = "db_age",Type = "int", length = 10)private int age;@Fk(columnName = "db_name", Type = "varchar", length = 3)private String name;public Student2(){


}
public Student2(int id, int age, String name) { this.id = id; this.age = age; this.name = 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;}
@Overridepublic String toString() { return "Student2{" + "id=" + id + ", age=" + age + ", name='" + name + '\'' + '}';}
复制代码


}


//类名的注解 @Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@interface TableM{String value();}


//属性的注解 @Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@interface Fk{String columnName();String Type();int length();}

发布于: 刚刚阅读数: 4
用户头像

卢衍飞

关注

君子藏器于腹,待时而动. 2022-11-08 加入

宜塔阿里巴巴低代码高级开发师 腾讯云V加开发者认证 HarmonyOS应用开发者认证 华为云云享专家Java开发工程师

评论

发布
暂无评论
Java 注解与反射 基础_Java_卢衍飞_InfoQ写作社区