Java基础--注解、反射
一、注解(annotation)
1、什么是注解?
从jdk5开始,java增加了annotation(注解),annotation是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。
2、annotation与注释的区别:
(1)annotation不是程序本身,可以对程序进行解释,此处可以理解为注释。但是annotation可以被其他程序(比如编译器)读取,并进行处理。
(2)注解与注释最大的区别就是注解存在被处理的流程,即注解是会被程序处理的。
3、注解的格式:
(1)以 “@注释名” 的形式在代码中存在。
(2)注解可以附加在程序元素( 包、类、构造器、方法、成员变量、参数、局域变量 )上面,为其添加额外的辅助信息,可以通过反射机制访问这些数据。
(3)annotation不能运行,其只有成员变量,没有方法。annotation与public、final等修饰符地位类似,属于程序元素的一部分,但不能作为程序元素使用。
4、常见注解:
(1)@override
定义在java.lang.override中,此注释只用于修饰方法,表示重写一个父类的方法。
【举例:】 @override public string tostring() { return "hello"; }
(2)@deprecated
定义在java.land.deprecated中,此注释可用于修饰方法、属性、类,表示该方法、类、属性不推荐使用(废弃)。在方法、类、属性上会有一条删除线(形如tostring())。
【举例:】 @deprecated public string tostring() { return "timertaskdemo []"; }
(3)@suppresswarnings
定义在java.lang.suppresswarnings中,用来阻止编译时的警告信息。其使用时需要设置参数。
【参数为:】 deprecation,使用了过时的类或方法的警告。 unchecked,执行了未检查的转换时的异常,比如集合未指定泛型。 fallthrough,当在switch语句发生case穿透时的警告。 path,当类路径、源文件路径不存在时的警告。 serial,可序列化类缺少serialversionuid时的警告。 finally,任何finally不能完成时的警告。 all,以上所有警告。 【格式:】 @suppresswarnings("all") 或者 @suppresswarnings(value = { "serial", "unchecked" })
5、元注解:
(1)元注解的作用就是负责注解其他的注解。java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它 annotation类型作说明。存在于java.lang.annotation中。
(2)元注解分类:
@target
@retention
@documented
@inherited
(3)@target元注解:
用于描述注解的使用范围。annotation可被用于 packages、types(类、接口、枚举、annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在annotation类型的声明中使用了target可更加明晰其修饰的目标。
【格式:】 public @interface target { elementtype[] value(); } 【参数:elementtype】 constructor:用于描述构造器 field:用于描述成员变量 local_variable:用于描述局部变量 method:用于描述方法 package:用于描述包 parameter:用于描述参数 type:用于描述类、接口(包括注解类型) 或enum声明 【举例:】 @target({java.lang.annotation.elementtype.type, java.lang.annotation.elementtype.field, java.lang.annotation.elementtype.method, java.lang.annotation.elementtype.parameter, java.lang.annotation.elementtype.constructor, java.lang.annotation.elementtype.local_variable})
(4)@retention元注解:
用于描述注解的声明周期。某些annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为annotation与class在使用上是被分离的)。使用这个meta-annotation可以对 annotation的“生命周期”限制。
【格式:】 public @interface retention { retentionpolicy value(); } 【参数:retentionpoicy】 source:在源文件中有效(即源文件保留) class:在class文件中有效(即class保留) runtime:在运行时有效(即运行时保留,可以通过反射机制读取) 【举例:】 @retention(retentionpolicy.source)
(5)@documented元注解:
用于描述其它类型的annotation应该被作为被标注的程序成员的公共api,因此可以被例如javadoc此类的工具文档化。documented是一个标记注解,没有成员。
(6)@inherited元注解:
@inherited 元注解是一个标记注解,@inherited阐述了某个被标注的类型是被继承的。如果一个使用了@inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
注意:
@inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。
当@inherited annotation类型标注的annotation的retention是retentionpolicy.runtime,则反射api增强了这种继承性。如果我们使用java.lang.reflect去查询一个@inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。
6、自定义注解:
(1)自定义注解时,需要使用@interface用来声明一个注解,其会自动继承java.lang.annotation.annotation接口。
【格式:】 public @interface 注解名 {定义体} 【或者:】 public @interface 注解名 { 类型 value() default 默认值; //这里是参数,不是抽象方法。 } 其中定义体实质是声明了一个配置参数(注:此处不是抽象方法)。 1、方法名指的是参数名。 2、返回值类型指的是参数的类型(只能为基本类型、class、string、enum、annotation类型、以及以上所有类型的数组)。 3、可以通过default来声明参数的默认值。 4、如果只有一个参数,那么参数名(方法名)一般为value。 5、只能使用public, default两个权限修饰符。
(2)方法:
判断类或者方法是否有注解 boolean isannotationpresent(class<? extends annotation> annotationclass) 获得注解对象 <a extends annotation> a getannotation(class<a> annotationclass) //获取指定注解 annotation[] getannotations() //获取当前元素上的所有注解
【举例:】 package com.test; import java.lang.annotation.annotation; import java.lang.annotation.elementtype; import java.lang.annotation.retention; import java.lang.annotation.retentionpolicy; import java.lang.annotation.target; import java.lang.reflect.field; @target(elementtype.type) @retention(retentionpolicy.runtime) @interface table { string value(); } @target(elementtype.field) @retention(retentionpolicy.runtime) @interface fielddemo { string columnname() default ""; string type() default ""; int length() default 10; } @table("student") class student { @fielddemo(columnname = "id", length = 10, type = "int") private int id; @fielddemo(columnname = "name", length = 20, type = "varchar") private string name; public int getid() { return id; } public void setid(int id) { this.id = id; } public string getname() { return name; } public void setname(string name) { this.name = name; } } public class annotationdemo { public static void main(string[] args) { try { // 获取student类的信息 class classdemo = class.forname("com.test.student"); // class<student> classdemo = (class<student>)class.forname("com.test.student"); system.out.println(classdemo); // 输出class com.test.student // 获取当前元素上的所有注解,此时获取的是@table annotation[] annotations = classdemo.getannotations(); for (annotation annotation : annotations) { system.out.println(annotation); } // 输出@com.test.table(value=student) // 直接获取指定的某注解 table table = (table) classdemo.getannotation(table.class); system.out.println(table.value()); // 输出student // 获取类的属性的注解 field field = classdemo.getdeclaredfield("name"); // 获取指定注解 fielddemo fielddemo = field.getannotation(fielddemo.class); // 输出name varchar 20 system.out.println(fielddemo.columnname() + " " + fielddemo.type() + " " + fielddemo.length()); } catch (exception e) { e.printstacktrace(); } } }
二、反射机制
1、什么是反射?
java反射机制指的是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;即这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
java中与反射相关的类,放在java.lang.reflect包中。
2、class类
class对象包含了一个类的完整的结构信息。通过class对象,可以对类进行操作,即为反射。
(1)规则:
class类拥有泛型作为定义。
class 类的实例表示正在运行的 java 应用程序中的类和接口。
class类没有public构造方法。
class对象是在加载类时由java 虚拟机以及通过调用类加载器中的defineclass 方法自动构造的。
一个类只有一个class对象。
(2)内置class实例(class对象):
byte.class, short.class, int.class, long.class, char.class, float.class, double.class, boolean.class, void.class. 注: int.class != integer.class 。 int.class == integer.type 。
(3)对于数组类型class实例:
每个数组属于被映射为 class 对象的一个类,所有具有相同元素类型和维数的数组都共享该class 对象。
int[] a = new int[100]; int[] b = new int[10]; long[] c = new long[10]; int[][] d = new int[10][2]; system.out.println(a.getclass());//输出class [i system.out.println(b.getclass());//输出class [i system.out.println(c.getclass());//输出class [j system.out.println(d.getclass());//输出class [[i system.out.println(a.getclass() == b.getclass());//输出true
(4)获取class实例的方法:
【方法1:】 根据传入的参数动态装载一个类,并且做类的初始化。 class.forname()方法 【方法2:】 获得对象运行时所指的真正对象(多态的场合返回子类的类名)。 class.getclass() 方法 【方法3:】 jvm将使用类a的类装载器,将类a装入内存(前提是:类a还没有装入内存),不对类a做类的初始化工作.返回类a的class的对象。 a.class属性
(5)通过class实例创建对象:
class.newinstance()方法 。调用默认构造函数,获得一个实例 class.newinstance方法与new的区别 newinstance: 弱类型。低效率。只能调用无参构造。 new:强类型。相对高效。能调用任何public构造。
(6)常用方法:
【获得构造器:】 constructor<t> getdeclaredconstructor(class<?>...) 获得指定构造方法 constructor<?>[] getdeclaredconstructors() 获得所有构造方法(声明顺序) constructor<t> getconstructor(class<?>...) 获得权限为public的指定构造方法 constructor<?>[] getconstructors() 获得权限为public的所有构造方法 【获得普通方法(成员方法):】 method[] getdeclaredmethods() 获得该类中定义的所有方法(不包含父类继承) method getdeclaredmethod(string name, class<?>... parametertypes) 根据该类中定义的指定方法(不包含父类继承) method[] getmethods() 获得权限为public的所有的方法 (包含父类继承) method getmethod(string name, class<?>... parametertypes) 获得权限为public的指定的方法 (包含父类继承) 【获得属性(成员变量):】 field[] getdeclaredfields() 获得该类中定义的所有属性(不包含继承) field getdeclaredfield(string name)获得该类中定义的指定属性(不包含继承) field[] getfields() 获得该类中所有public的属性(包含继承) field getfield (string name) 获得该类中指定的public属性(包含继承) 【获得内部类:】 class<?>[] getdeclaredclasses() 获得所有内部类 (不包含继承) class<?>[] getclasses() 获得所有权限为public的内部类(包含继承) 【其他:】 package getpackage() 获得package对象 string getname() 获得类的全称,即包名+类名 string getsimplename() 获得类的简称,即类名 class<? super t> getsuperclass() 获得继承的类 class<?>[] getinterfaces() 获得实现的接口
(7)获得构造器后,可以进行的操作
(8)获得成员方法后,可以进行的操作
(9)获得成员变量后,可以进行的操作
package com.test; import java.lang.reflect.constructor; import java.lang.reflect.field; import java.lang.reflect.method; class teacher { private string name; private int age; public teacher() { } public teacher(string name, int age) { this.name = name; this.age = age; } 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 class reflectiondemo { public static void main(string[] args) { try { // 加载teacher.class对象 class<teacher> teacherclass = (class<teacher>) class.forname("com.test.teacher"); // 获取无参构造器,若teacher类没有无参构造方法,则会报错 teacher teacher = teacherclass.newinstance(); system.out.println(teacher + ", " + teacher.getname() + ", " + teacher.getage()); // 获取有参构造器 constructor<teacher> constructor = teacherclass.getdeclaredconstructor(string.class, int.class); teacher teacher2 = constructor.newinstance("tom", 20); system.out.println(teacher2 + ", " + teacher2.getname() + ", " + teacher2.getage()); // 获取成员方法 teacher teacher3 = teacherclass.newinstance(); method method = teacherclass.getdeclaredmethod("setage", int.class); method.invoke(teacher3, 30); system.out.println(teacher3.getage()); method method2 = teacherclass.getdeclaredmethod("getage"); system.out.println(method2.invoke(teacher3)); // 获取成员变量 teacher teacher4 = teacherclass.newinstance(); field field = teacherclass.getdeclaredfield("age"); field.setaccessible(true);// 忽略安全检查,可以获取private类型的数据,破坏封装性。 system.out.println(field.get(teacher4)); } catch (exception e) { e.printstacktrace(); } } } 【结果:】 com.test.teacher@2a139a55, null, 0 com.test.teacher@15db9742, tom, 20 30 30 0
3、反射机制性能问题
setaccessible,是启用和禁用安全检查的开关,其值为true时,表示禁用java语言访问的安全性检查,为false时,表示启用安全性检查,将其值设为true,可以提高反射的效率。
未完待续。。。。