Java学习笔记--注解和反射
2021/4/10 18:13:40
本文主要是介绍Java学习笔记--注解和反射,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
注解和反射
1. 注解
注解作用:
- 对程序做出解释
- 被其他程序读取
注解格式:
- @注释名,还可以添加一些参数值,例如@SuppressWarnings(value="unchecked").
注解使用在哪里
可以附加在package,class,method,filed,上面,相当于添加了额外的辅助信息,可以通过反射机制对这些元数据进行访问
内置注解
1. @Override
该注释只用于修饰方法,表示重写超类的一个方法,可以让让编译器检查该方法是否正确地实现了覆写
2. Deprecated
该注释可以用于修饰方法、属性、类,表示不鼓励使用,因为使用存在危险或有更好的选择
3. @SuppresseWarnings("para")
告诉编译器忽略此处代码产生的警告,需要参数才能正确的使用,例如 @SuppresseWarnings("all"),
@SuppresseWarnings("unchecked"), @SuppresseWarnings(value={"unchecked","deprecation"})
package com.annotation; import java.util.ArrayList; import java.util.List; public class TestAnnotation { //override 方法重写注解 @Override public String toString() { return super.toString(); } //Deprecated 不推荐使用,但是任然可以使用 @Deprecated public static void test(){ System.out.println("test"); } //SuppressWarnings(" ")忽视警告,可以放在方法上 @SuppressWarnings("all") public void test01(){ List list = new ArrayList(); } }
元注解
元注解用于解释其他注解
@Target
表示注解使用范围
- 类或接口:
ElementType.TYPE
; - 字段:
ElementType.FIELD
; - 方法:
ElementType.METHOD
; - 构造方法:
ElementType.CONSTRUCTOR
; - 方法参数:
ElementType.PARAMETER
。
@Retention
Retention 定义了Annotation的生命周期
- 仅编译期:
RetentionPolicy.SOURCE
; - 仅class文件:
RetentionPolicy.CLASS
; - 运行期:
RetentionPolicy.RUNTIME
。
如果@Retention
不存在,则该Annotation
默认为CLASS
。因为通常我们自定义的Annotation
都是RUNTIME
,所以,务必要加上@Retention(RetentionPolicy.RUNTIME)
这个元注解
@Inherited
使用@Inherited
定义子类是否可继承父类定义的Annotation
。@Inherited
仅针对@Target(ElementType.TYPE)
类型的annotation
有效,并且仅针对class
的继承,对interface
的继承无效
@Documented
表明该注解将被包含于javadoc内
public class TestMetaAnnotation { @MyAnnotation public void testMetaAnnotation(){ } } //定义一个元注解 //Target 表示注解使用范围 @Target(value = ElementType.METHOD) //Retention 定义了Annotation的生命周期 // runtime>class>source @Retention(value = RetentionPolicy.RUNTIME) //表明该注解将被包含于javadoc内 @Documented //子类可以继承父类的注解 @Inherited @interface MyAnnotation{ }
自定义注解@interface
使用@interface自动继承Annotation接口
public class TestCreateAnnotation { @MyAnnotation02(name = "",age = 10) public void test(){ } @MyAnnotation03("Fuck") //只有一个参数时直接赋值 public void test03(){ } } //创建注解 @Target({ElementType.TYPE,ElementType.METHOD}) @interface MyAnnotation02{ //注解参数 : paraType paraName() |(default value); String name() default ""; //如果不设置默认值,使用注解时必须给参数赋值 int age(); //使用时必须赋值 int id() default -1; //default -1 表示默认不存在 String[] school() default {"Tsinghua","Peking"}; } @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation03{ String value(); //只有一个参数成员时,参数名一般为value }
2.反射
3.1 反射机制
- 反射机制允许程序在运行期间借助与Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性以及方法
- 加载完类后,在堆内存的方法区产生了一个Class类型的对象,这个对象包含了完整的类的结构信息,我们可以通过这个对象看到类的结构
- 反射实现了语言的动态创建和编译,具有很大的灵活性
- 反射机制是一种解释型的操作,执行效率较低
3.2 Class类
Class类的常用方法
获取类的Class对象
-
对于已知的类,直接通过类的class对象获取,该方法最为可靠,性能最好
Class clz = Cls.class;
-
对于某个实例,调用getClass方法获取
Class clz = cls.getClass();
-
通过类的路径以及类名,调用Class类的静态方法forName方法获取
Class clz = forName("packegePath.className");
-
内置基本数据类型直接使用 className.Type获取
-
ClassLoader获取
//获取Class类对象 public class TestGetClassInstance { public static void main(String[] args) throws ClassNotFoundException { Person person = new Person(); System.out.println("He is "+person.name); Person s1 = new Student(); Class cs = s1.getClass(); //通过类名直接获取 Class c1 = Person.class; System.out.println(c1.hashCode()); //forName 方法获取 Class c2 = Class.forName("com.Reflection.Demo01.Person"); System.out.println(c2.hashCode()); //实例.getClass()方法获取 Class c3 = person.getClass(); System.out.println(c3.hashCode()); //c1 ~ c3 The same HashCode // 基本内置类型的包装类的Type属性 Class c4 = Integer.TYPE; System.out.println(c4); //获得父类类型(实际类型) Class c5 = cs.getSuperclass(); System.out.println(c5); } } class Person{ public String name; public Person() { } @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; } } class Student extends Person{ public Student() { this.name="Student"; } } class Teacher extends Person{ public Teacher() { this.name = "Teacher"; } }
拥有Class类型的对象
public class TestClassesPossessedClassAttribute { 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 = ElementType.class; //枚举 Class c7 = Integer.class; //基本数据类型 Class c8 = void.class; //void Class c9 = Class.class; //Class //alt+鼠标左键选中一列数据 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); //只要元素的类型与维度一样,就是同一个class int[] a = new int[10]; int[] b = new int[100]; System.out.println(a.getClass().hashCode()); //Same HashCode System.out.println(b.getClass().hashCode());
类加载过程
public class TestLoadClass { public static void main(String[] args) { A a = new A(); System.out.println(A.m); /* 1.加载到内存,生成类的Class对象 A.class;TestLoadClass.class; 2.链接,为类变量分配内存和赋初值 m=0; 3.初始化 执行类构造器方法<clinit>()将所有类变量赋值以及静态代码块合并形成 <clinit>(){ static { System.out.println("Static Filed"); m = 200; } static int m= 100; } */ } } class A{ static { System.out.println("Static Filed"); m = 200; } /* m=200; m=100 */ static int m= 100; public A(){ System.out.println("A类的无参构造初始化"); } }
类的初始化
public class TestInitClass { static { //1.main方法所在类初始化 System.out.println("buying a gun"); } public static void main(String[] args) throws ClassNotFoundException { //2.主动引用new //Robber robber = new Robber(); //3.反射引用 //Class.forName("com.Reflection.Demo01.Robber"); //不会产生类的初始化 //1.调用子类继承父类的静态变量和方法时 //System.out.println(Robber.moneyBefore); //Robber.b(); //2.初始化类的数组时 Robber[] robbers = new Robber[10]; //3.引用常量(链接阶段就存入调用类的常量池了) System.out.println(Robber.escapeMoney); } } //测试类初始化 class Criminal{ static double moneyBefore = 20; static { System.out.println("Gives me your fucking money!"); } static void b(){ System.out.println("父类静态方法调用"); } } class Robber extends Criminal{ static { System.out.println("This is a robbery! Put your hands up!!"); double getMoney = 292898; } static double robberMoney = 8361303.222; static final double escapeMoney = 1000; }
类加载器作用
双亲委派机制
当某个类加载器需要加载某个.class
文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。
public class TestClassLoader { public static void main(String[] args) { //获取系统类加载器 ClassLoader classLoader = ClassLoader.getSystemClassLoader(); System.out.println(classLoader); // 获取系统类加载器父类---扩展类加载器 classLoader = classLoader.getParent(); System.out.println(classLoader); // 获取扩展类加载器父类---根加载器(无法获取:null) classLoader = classLoader.getParent(); System.out.println(classLoader); //测试当前类的ClassLoader classLoader = TestLoadClass.class.getClassLoader(); System.out.println(classLoader); //测试JDK内置类的加载器 classLoader = String.class.getClassLoader(); System.out.println(classLoader); //获得系统类加载器路径 System.out.println(System.getProperty("java.class.path")); } }
3.3 反射操作
1. 获取类的结构
package com.reflection.Demo02; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; //Testing using the class object to obtain the contents of a class public class TestGetClassInfo { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException { // Get the Class of a class by path Class c1 = Class.forName("com.reflection.Demo01.User"); //Get ClassName System.out.println(c1.getName()); //package + ClassName System.out.println(c1.getSimpleName()); //ClassName System.out.println(); //Get all public properties Field[] fields = c1.getFields(); //getField() only used on properties with decoration of public for (Field field : fields) { //out: null ,no method decorated with public System.out.println(field); } //Get all properties fields = c1.getDeclaredFields(); //getDeclaredFields() used on all types of properties for (Field field : fields) { System.out.println(field); } System.out.println(); //Get particular property //only public //Field name = c1.getField("name"); -> NoSuchFieldException //out.println(name); Field name = c1.getDeclaredField("name"); System.out.println(name); System.out.println(); //Get methods Method[] methods = c1.getMethods();//all public methods include methods inherited superClass or superInterface for (Method method : methods) { System.out.println(method); } System.out.println(); methods = c1.getDeclaredMethods(); for (Method method : methods) { //all methods exclude inherited System.out.println(method); } System.out.println(); //Get particular method getMethod(name,paraType.class) Method method1 =c1.getMethod("getName",null); Method method2 =c1.getMethod("setName",String.class); System.out.println(method1); System.out.println(method2); System.out.println(); //Get Constructors Constructor[] constructors = c1.getConstructors(); for (Constructor constructor : constructors) { //public constructors System.out.println(constructor); } System.out.println(); constructors = c1.getDeclaredConstructors(); for (Constructor constructor : constructors) {//all System.out.println(constructor); } System.out.println(); //get particular public constructor getConstructor(para.class|null); Constructor constructor = c1.getConstructor(String.class); System.out.println(constructor); System.out.println(); //get particular constructor getDeclaredConstructor(para.class|null); constructor = c1.getDeclaredConstructor(String.class); System.out.println(constructor); constructor = c1.getDeclaredConstructor(null); System.out.println(constructor); } }
获取父类:getSuperclass()
获取继承的接口:getInterfaces()
如果是两个Class
实例,要判断一个向上转型是否成立,可以调用isAssignableFrom()
:
2.反射创建对象
方法1:Class.newInstance() 方法(java9后弃用)
//获取对象 Class c1 = User.class; //构造一个对象 User user = (User)c1.newInstance(); System.out.println(user);
局限:它只能调用该类的public无参数构造方法。
方法2:通过构造器对象newInstance()方法创建对象
Constructor constructor = c1.getDeclaredConstructor(String.class,String.class,int.class,String.class); User user2 = (User)constructor.newInstance("001","wang",20,"Male"); System.out.println(user2);
调用非public
的Constructor
时,必须首先通过setAccessible(true)
设置允许访问。
3.反射调用方法
Method sN = c1.getDeclaredMethod("setName",String.class); sN.invoke(user2,"Liu");//method.invoke(obj,value) System.out.println(user2.getName());
method对象包含信息
getName()
:返回方法名称,例如:"getScore"
;getReturnType()
:返回方法返回值类型,也是一个Class实例,例如:String.class
;getParameterTypes()
:返回方法的参数类型,是一个Class数组,例如:{String.class, int.class}
;getModifiers()
:返回方法的修饰符,它是一个int
,不同的bit表示不同的含义。
如果获取到的Method表示一个静态方法,调用静态方法时,由于无需指定实例对象,所以invoke
方法传入的第一个参数永远为null
。
调用非public方法,我们通过Method.setAccessible(true)
允许其调用:
4.设置字段值
Field name= c1.getDeclaredField("name"); name.setAccessible(true); //不能直接操作私有属性,需要设置访问安全检查为true name.set(user2,"Li"); //field.set(fieldObj,value) System.out.println(user2.getName());
一个Field
对象包含了一个字段的所有信息:
getName()
:返回字段名称,例如,"name"
;getType()
:返回字段类型,也是一个Class
实例,例如,String.class
;getModifiers()
:返回字段的修饰符,它是一个int
,不同的bit表示不同的含义。
修改非public
字段,需要首先调用setAccessible(true)
5.SetAccessible
public class TestSetAcc { public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { User user = new User(); long start = System.currentTimeMillis(); for (int i = 0; i < 1_000_000_000; i++) { user.getName(); } long end = System.currentTimeMillis(); System.out.println("普通方式调用方法十亿次"+(end-start)+"ms"); //反射方式 TestSetAcc.test(); TestSetAcc.test01(); } public static void test() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Class c1 = User.class; User user = new User(); Method method= c1.getDeclaredMethod("getName",null); method.setAccessible(true); long start = System.currentTimeMillis(); for (int i = 0; i < 1_000_000_000; i++) { method.invoke(user,null); } long end = System.currentTimeMillis(); System.out.println("关闭检测方式调用十亿次"+(end-start)+"ms"); } public static void test01() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Class c1 = User.class; User user = new User(); Method method= c1.getDeclaredMethod("getName",null); method.setAccessible(false); long start = System.currentTimeMillis(); for (int i = 0; i < 1_000_000_000; i++) { method.invoke(user,null); } long end = System.currentTimeMillis(); System.out.println("检测方式调用十亿次"+(end-start)+"ms"); } }
6.反射操作泛型
//反射获取泛型 public class TestReflectionGeneric { public void test01(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 = TestReflectionGeneric.class.getDeclaredMethod("test01",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(); Method method01 = TestReflectionGeneric.class.getDeclaredMethod("test02",null); Type genericReturnTypeType = method01.getGenericReturnType(); if(genericReturnTypeType instanceof ParameterizedType){ Type[] actualTypeArguments= ((ParameterizedType) genericReturnTypeType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); } } } }
7.反射操作注解
//反射操作注解 public class TestReflectionAnnotation { public static void main(String[] args) throws NoSuchFieldException { //反射获得类的注解 Class c1 = Student.class; Annotation[] annotations = c1.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } //获得注解的value Table table = (Table) c1.getAnnotation(Table.class); System.out.println(table.value()); //获得指定注解 Field field = c1.getDeclaredField("id"); FieldAnn annotation = field.getAnnotation(FieldAnn.class); System.out.println(annotation.columnName()); System.out.println(annotation.type()); System.out.println(annotation.length()); } } @Table("TableStudent") class Student{ @FieldAnn(type = "varchar",length = 10,columnName = "db_id") private String id; @FieldAnn(type = "db_int",length = 10,columnName = "db_age") private int age ; @FieldAnn(type = "varchar",length = 10,columnName = "dn_name") private String name; public Student() { } public String getId() { return id; } public void setId(String 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; } } //class annotation @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface Table{ String value(); } //field annotation @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface FieldAnn{ String columnName(); String type(); int length(); }
这篇关于Java学习笔记--注解和反射的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-25JAVA语音识别项目项目实战入门教程
- 2024-11-25JAVA云原生项目实战入门教程
- 2024-11-25Java语音识别项目入门:新手必读指南
- 2024-11-25Java语音识别项目入门:轻松开始你的第一个语音识别项目
- 2024-11-25Java语音识别项目入门详解
- 2024-11-25Java语音识别项目教程:从零开始的详细指南
- 2024-11-25JAVA语音识别项目教程:初学者指南
- 2024-11-25Java语音识别项目教程:初学者指南
- 2024-11-25JAVA云原生入门:新手指南与基础教程
- 2024-11-25Java云原生入门:从零开始的全面指南