注解和反射-笔记

定义:
  1. 注解是一种能被添加到java代码中的元数据,类、方法、变量、参数和包都可以用注解来修饰。注解对于它所修饰的代码并没有直接的影响。
  2. 注解是一种元数据形式。即注解是属于java的一种数据类型,和类、接口、数组、枚举类似。
  3. 注解用来修饰,类、方法、变量、参数、包。
  4. 注解不会对所修饰的代码产生直接的影响。
  5. 注解又许多用法,其中有:为编译器提供信息 - 注解能被编译器检测到错误或抑制警告。编译时和部署时的处理 - 软件工具能处理注解信息从而生成代码,XML文件等等。运行时的处理 - 有些注解在运行时能被检测到。
一、自定义注解和使用
  • 第一步,定义注解——相当于定义标记;

  • 第二步,配置注解——把标记打在需要用到的程序代码中;

  • 第三步,解析注解——在编译期或运行时检测到标记,并进行特殊操作。

  • @Target注解,是专门用来限定某个自定义注解能够被应用在哪些Java元素上面的。

    TYPE,

    /** 属性的声明 */
    FIELD,
    
    /** 方法的声明 */
    METHOD,
    
    /** 方法形式参数声明 */
    PARAMETER,
    
    /** 构造方法的声明 */
    CONSTRUCTOR,
    
    /** 局部变量声明 */
    LOCAL_VARIABLE,
    
    /** 注解类型声明 */
    ANNOTATION_TYPE,
    
    /** 包的声明 */
    PACKAGE
    
  • @Retention注解,翻译为持久力、保持力。即用来修饰自定义注解的生命力。

    - 注解的生命周期有三个阶段:1Java源文件阶段;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。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。

二、反射机制的功能

  1. 在运行时判断任意一个对象所属的类。
  2. 在运行时构造任意一个类的对象。
  3. 在运行时判断任意一个类所具有的成员变量和方法。
  4. 在运行时调用任意一个对象的方法。
  5. 生成动态代理。

三、性能对比分析

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");
}
  1. 通过反射获取类
    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 + '\'' +
                '}';
    }
    
  2. 通过反射获取类中的各种信息

    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);
    
  3. 反射获取类中的加载器
    //获取系统类的加载器==用户类加载器
    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);
    
  4. 通过反射创建对象
    //获得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());
    

    注:部分内用由多出转载,如有异议,请联系我删除

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值