定义:
- 注解是一种能被添加到java代码中的元数据,类、方法、变量、参数和包都可以用注解来修饰。注解对于它所修饰的代码并没有直接的影响。
- 注解是一种元数据形式。即注解是属于java的一种数据类型,和类、接口、数组、枚举类似。
- 注解用来修饰,类、方法、变量、参数、包。
- 注解不会对所修饰的代码产生直接的影响。
- 注解又许多用法,其中有:为编译器提供信息 - 注解能被编译器检测到错误或抑制警告。编译时和部署时的处理 - 软件工具能处理注解信息从而生成代码,XML文件等等。运行时的处理 - 有些注解在运行时能被检测到。
一、自定义注解和使用
-
第一步,定义注解——相当于定义标记;
-
第二步,配置注解——把标记打在需要用到的程序代码中;
-
第三步,解析注解——在编译期或运行时检测到标记,并进行特殊操作。
-
@Target注解,是专门用来限定某个自定义注解能够被应用在哪些Java元素上面的。
TYPE,
/** 属性的声明 */ FIELD, /** 方法的声明 */ METHOD, /** 方法形式参数声明 */ PARAMETER, /** 构造方法的声明 */ CONSTRUCTOR, /** 局部变量声明 */ LOCAL_VARIABLE, /** 注解类型声明 */ ANNOTATION_TYPE, /** 包的声明 */ PACKAGE
-
@Retention注解,翻译为持久力、保持力。即用来修饰自定义注解的生命力。
- 注解的生命周期有三个阶段:1、Java源文件阶段;2、编译到class文件阶段;3、运行期阶段。同样使用了RetentionPolicy枚举类型定义了三个阶段: - 如果一个注解被定义为RetentionPolicy.SOURCE,则它将被限定在Java源文件中,那么这个注解即不会参与编译也不会在运行期起任何作用,这个注解就和一个注释是一样的效果,只能被阅读Java文件的人看到; - 如果一个注解被定义为RetentionPolicy.CLASS,则它将被编译到Class文件中,那么编译器可以在编译时根据注解做一些处理动作,但是运行时JVM(Java虚拟机)会忽略它,我们在运行期也不能读取到; - 如果一个注解被定义为RetentionPolicy.RUNTIME,那么这个注解可以在运行期的加载阶段被加载到Class对象中。那么在程序运行阶段,我们可以通过反射得到这个注解,并通过判断是否有这个注解或这个注解中属性的值,从而执行不同的程序代码段。我们实际开发中的自定义注解几乎都是使用的**RetentionPolicy.RUNTIME**; - 在默认的情况下,自定义注解是使用的RetentionPolicy.CLASS。
-
@Documented注解,是被用来指定自定义注解是否能随着被定义的java文件生成到JavaDoc文档当中。
-
@Inherited注解,是指定某个自定义注解如果写在了父类的声明部分,那么子类的声明部分也能自动拥有该注解。@Inherited注解只对那些@Target被定义为ElementType.TYPE的自定义注解起作用。
@interface annotations1{
String name() default "呆呆";
int age() default 21;
String address() default "河南周口";
}
//创建一个注解类
@Target(ElementType.TYPE) //设置注解可使用的范围在类上面
@Retention(RetentionPolicy.RUNTIME) //设置该注解在什么时候运行
@interface table{
String value();
}
//创建属性的注解
@Target(ElementType.FIELD) //设置注解可使用的范围在类上面
@Retention(RetentionPolicy.RUNTIME) //设置该注解在什么时候运行
@interface Fieldd{
String column();
String type();
int length();
}
@annotations1
public class Student {
public String address;
private String name;
}
--------------------------------------
@table("db_Student")
class Student{
@Fieldd(column = "db_name",type = "vachar",length = 10)
private String name;
@Fieldd(column = "db_address",type = "vachar",length = 10)
private String address;
@Fieldd(column = "db_age",type = "int",length = 10)
private int age;
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
//先获取类
Class c = Class.forName("反射操作注解.Student");
//通过类的反射获得注解
Annotation[] an = c.getAnnotations();
for (Annotation a:an){
System.out.println(a); //输出该注解
}
//获得指定的注解的值
table ta = (table) c.getAnnotation(table.class);
String value = ta.value();
System.out.println(value);
//获得类指定的注解
Field field = c.getDeclaredField("name"); //获取指定的属性
Fieldd fiedd = field.getAnnotation(Fieldd.class); //获取该属性上面的注解
System.out.println(fiedd.column());
System.out.println(fiedd.type());
System.out.println(fiedd.length());
}
二、注解的特殊语法
特殊语法一:
如果注解本身没有注解类型元素,那么在使用注解的时候可以省略(),直接写为:@注解名,它和标准语法@注解名()等效!
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE})
@Documented
public @interface FirstAnnotation {
//等效于@FirstAnnotation()
@FirstAnnotation
public class JavaBean{
特殊语法二:
如果注解本本身只有一个注解类型元素,而且命名为value,那么在使用注解的时候可以直接使用:@注解名(注解值),其等效于:@注解名(value = 注解值)
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE})
@Documented
public @interface SecondAnnotation {
String value();
//等效于@ SecondAnnotation(value = "this is second annotation")
@SecondAnnotation("this is annotation")
public class JavaBean{
特殊用法三:
如果注解中的某个注解类型元素是一个数组类型,在使用时又出现只需要填入一个值的情况,那么在使用注解时可以直接写为:@注解名(类型名 = 类型值),它和标准写法:@注解名(类型名 = {类型值})等效!
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE})
@Documented
public @interface ThirdAnnotation {
String[] name();
//等效于@ ThirdAnnotation(name = {"this is third annotation"})
@ ThirdAnnotation(name = "this is third annotation")
public class JavaBean{
//省略实现部分
}
特殊用法四:
如果一个注解的@Target是定义为Element.PACKAGE,那么这个注解是配置在package-info.java中的,而不能直接在某个类的package代码上面配置。
三、反射机制
一、什么是反射:
(个人理解,钩子,就是在运行的时候动态创建获取对象,可以做外挂)
(1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
(2)Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。
二、反射机制的功能
- 在运行时判断任意一个对象所属的类。
- 在运行时构造任意一个类的对象。
- 在运行时判断任意一个类所具有的成员变量和方法。
- 在运行时调用任意一个对象的方法。
- 生成动态代理。
三、性能对比分析
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
test1();
test2();
test3();
}
//普通方式运行十亿次
public static void test1(){
Student stu = new Student();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
stu.getName();
}
long stoptTime = System.currentTimeMillis();
System.out.println(stoptTime-startTime+"ms");
}
//通过反射方式运行十亿次
public static void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Student stu = new Student();
Class c= stu.getClass();
Method method = c.getDeclaredMethod("getName",null);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
method.invoke(stu,null);
}
long stoptTime = System.currentTimeMillis();
System.out.println(stoptTime-startTime+"ms");
}
//通过反射方式关闭检测运行十亿次
public static void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Student stu = new Student();
Class c= stu.getClass();
Method method = c.getDeclaredMethod("getName",null);
method.setAccessible(true);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
method.invoke(stu,null);
}
long stoptTime = System.currentTimeMillis();
System.out.println(stoptTime-startTime+"ms");
}
-
通过反射获取类
public static void main(String[] args) throws ClassNotFoundException { Person person = new Person(); System.out.println(person); //第一种方式 对象.getClass() Class c1 = person.getClass(); System.out.println(c1); //第二种方式 forName Class c2 = Class.forName("获取类的几种方式.Person"); System.out.println(c2); //第三种方式 通过类名获得 Class c3 = Person.class; System.out.println(c3); //第四种 基本数据类型有个Type属性 Class c4 = Integer.TYPE; System.out.println(c4); //第五种 通过对象获取父类 Class c5 =c1.getSuperclass(); System.out.println(c5); } class Person { String name = "daidai"; public Person() { } public Person(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; }
-
通过反射获取类中的各种信息
Class c = Class.forName("自定义注解.Student"); //获得类的属性 Field[] fields = c.getFields(); //只能获取Public属性 for(Field field:fields){ System.out.println(field); } fields = c.getDeclaredFields(); //获取全部属性 for(Field field:fields){ System.out.println(field); } Field fieldName = c.getDeclaredField("name"); //获得指定属性 System.out.println(fieldName); System.out.println("==========================="); //获得类的方法 Method[] methods = c.getMethods(); //只能获取public方法 for (Method method:methods){ System.out.println("正常的:"+method); } methods = c.getDeclaredMethods(); //获取自定义的方法 for (Method method:methods){ System.out.println("全部的:"+method); } Method methodName = c.getMethod("test", int.class); //获取指定方法 System.out.println(methodName); System.out.println("================================"); //获取类中的构造器 Constructor[] constructors = c.getConstructors(); //获取public构造器 for (Constructor constructor:constructors){ System.out.println(constructor); } constructors = c.getDeclaredConstructors(); //获取自定义构造器 for (Constructor constructor:constructors){ System.out.println(constructor); } Constructor constructor = c.getConstructor(String.class,String.class); //获取指定构造器 System.out.println(constructor);
-
反射获取类中的加载器
//获取系统类的加载器==用户类加载器 ClassLoader classLoader=ClassLoader.getSystemClassLoader(); System.out.println(classLoader); //获取系统类加载器的父类加载器==扩展类的加载器 ClassLoader parent = classLoader.getParent(); System.out.println(parent); //获取扩展类加载器的父类加载器==根类加载器 无法直接获取 ClassLoader parent1 = parent.getParent(); System.out.println(parent1); //测试当前类是哪个类加载的---->系统类加载器 ClassLoader classLoader1 = Class.forName("获取类加载器.Test1").getClassLoader(); System.out.println(classLoader1); //测试Object类由那个加载器加载的---->根类加载器,无法直接获取 ClassLoader classLoader2 = Class.forName("java.lang.Object").getClassLoader(); System.out.println(classLoader2);
-
通过反射创建对象
//获得Class对象 Class c = Class.forName("自定义注解.Student"); //调用无参构造创建对象 Student stu = (Student)c.newInstance(); System.out.println(stu); //通过构造器构造对象 Constructor constructor = c.getDeclaredConstructor(String.class,String.class); Student stu2 = (Student) constructor.newInstance("呆呆","莹莹"); System.out.println(stu2); System.out.println("============================="); //通过反射调用普通方法 Student stu3 = (Student) c.newInstance(); //通过反射获取一个方法 Method method = c.getDeclaredMethod("setName", String.class); method.invoke(stu3,"王者荣耀"); System.out.println(stu3.getName()); //通过反射操作属性 Student stu4 = (Student) c.newInstance(); Field name = c.getDeclaredField("name"); //不能操作私有属性 name.setAccessible(true); //关闭权限检测 name.set(stu4,"鲁大师"); System.out.println(stu4.getName());
注:部分内用由多出转载,如有异议,请联系我删除