反射和注解

反射

反射机制

将类的各个组成部分封装成其他对象,这就是反射机制。

java代码的三个阶段

第一阶段:Source:源代码阶段

  • 从java文件按成员变量,构造方法,成员方法通过javac编译成class文件。
  • 其实就是在编译器中写代码,产生 .java 文件的时候,以及将这个 .java 文件 javac 生成为 .claa 字节码文件的过程都是源代码阶段。

第二阶段:Class:类对象阶段

  • 通过类加载器(ClassLoader)把class文件中的成员变量,构造方法,成员方法加载到内存中。
  • 在源代码阶段的.class文件中存储着,成员变量,构造方法,成员方法等内容。通过类加载器将 .class 文件加载到内存后,.clss文件里面的内容都需要存起来,java中有一个Class的类,这个类就是用来存储类加载器从.class文件中读取到的内容的。所以类的各个组成就被封装成立 成员变量(Field[]),构造方法(Constructor[]),成员方法(Method[])等对象。

第三阶段:Runtime:运行时阶段

  • 可以创建对象,调用对象的方法。也可以通过newInstance()根据java类型生成对象。

反射的好处

  1. 可以在程序运行过程中,操作成员变量,成员方法,构造方法等对象。
  2. 可以解耦合,提高程序扩展性。

获取Class

因为Filed , Constructor , Method等都是Class文件的属性,所以要想操作这些对象,就必须获取Class。

获取Class的三种方式(这三种方式对应了java代码的三个阶段):

  • 第一种:

    • Class.forName("全类名")//将字节码文件加载进内存,返回Class
      
  • 第二种:

    • //加载进内存之后,已经知道类名了。所以通过类名获取。
      类名.Class;
      
  • 第三种:

    • //已经有对象了,可以通过对象获取。
      对象.getClass;//这个方法是封装到Object里面的
      

注意:这三种方式获取的Class对象都是同一个,因为一个.class字节码文件,在程序运行中,只会被加载一次。

使用Class

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Student {
    private String name;
    private int age;
    public String a;
    protected Integer ss;


    /**
     * 这是玩的方法
     * @param n
     */
    public void play(String n){
        System.out.println("玩..." + n);
    }
    
    public void hh(int q) {
        System.out.println(q);
    }

    private void study(){
        System.out.println("这个学生正在学习");
    }

}
  1. 获取成员变量

    public class ReflectDemo5 {
        public static void main(String[] args) throws Exception {
            Student student = Student.builder().age(1).name("sm").a("nn").build();
            Class<Student> studentClass = Student.class;
            //获取所有是public修饰的成员变量
            Field[] fields = studentClass.getFields();
            for (Field f : fields) {
                //获取成员变量的名字
                System.out.println(f.getName());//a
                //获取属性对应的值
                System.out.println(f.get(student));//nn
            }
    
            System.out.println("--------------");
            //获取所有所有成员变量,包括私有的
            Field[] fields1 = studentClass.getDeclaredFields();
            for (Field field : fields1) {
                System.out.print(field.getName() + " ");//name age a ss 
            }
            System.out.println();
    
            //获取私有成员变量的值
            Field name = studentClass.getDeclaredField("name");
            //设置无障碍为true(暴力反射),需要操作所有的private修改的属性时,都需要设置无障碍
            name.setAccessible(true);
            System.out.println(name.get(student));//sm
    
            //修改私有的成员变量
            Field age = student.getClass().getDeclaredField("age");
            age.setAccessible(true);
            System.out.println(age.get(student)); //1
            age.set(student, 50);
            System.out.println(age.get(student));//50
    
        }
    }
    
    
  2. 获取构造方法

    public class ReflectDemo5 {
        public static void main(String[] args) throws Exception {
            Student student = Student.builder().age(1).name("sm").a("nn").build();
            Class<Student> studentClass = Student.class;
    
            //获取构造方法
            Constructor<?>[] constructors = studentClass.getConstructors();
            for (Constructor c : constructors) {
                System.out.println(c);
            }
    
            //使用构造方法创建对象,如果需要获取无参构造,这里不传参数就可以了
            Constructor<Student> constructor = studentClass.getConstructor(String.class , int.class , String.class,Integer.class);
            //创建对象 , 因为上面获取的是有参的构造方法,所以这里必须传参数
            Student student1 = constructor.newInstance("SuperMonkey" ,20 , "hhh",30);
            System.out.println(student1);
    
            //还有一种创建对象的方式 , 已被弃用,而且这样创建只能调用无参构造方法,不能向里面传递参数
            Student student2 = studentClass.newInstance();
            System.out.println(student2);
    
        }
    }
    
  3. 获取成员方法

    public class ReflectDemo5 {
        public static void main(String[] args) throws Exception {
            Student student = Student.builder().age(1).name("sm").a("nn").build();
            Class<Student> studentClass = Student.class;
    
            //获取所有public修饰的方法
            Method[] methods = studentClass.getMethods();
            for (Method m : methods) {
                System.out.println(m.getName());
            }
    
            //执行public修饰的方法 , 如果这个方法有参数,需要在后面添加参数类型对应的.class
            Method play = studentClass.getMethod("play" , String.class);
            play.invoke(student, "游戏");
            Method hh = studentClass.getMethod("hh" , int.class);
            hh.invoke(student, 1);
            Method getName = studentClass.getMethod("getName");
            Object o = getName.invoke(student); // 这个 o 是方法执行的返回值。
    
            //执行private修饰我的方法
            Method study = studentClass.getDeclaredMethod("study");
            study.setAccessible(true);
            study.invoke(student);
    
    
        }
    }
    
    
  4. 获取类名

    public class ReflectDemo5 {
        public static void main(String[] args) throws Exception {
            Student student = Student.builder().age(1).name("sm").a("nn").build();
            Class<Student> studentClass = Student.class;
    
            //获取全路径类名
            System.out.println(studentClass.getName());
    
            //获取简洁类名
            System.out.println(studentClass.getSimpleName());
    
        }
    }
    

注解

概述

注解也叫做元数据,是1.5只有的新特性,用来对元素进行说明。

主要作用

  • 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查(如 @Override)
  • 编写文档:通过代码里标识的注解生成文档(生成doc文档,结合javadoc命令使用)
  • 代码分析(只有这个功能是由程序员操作的)

JDK中预定义的注解

JDK中自定义了很多注解,举例需要了解的三个注解:

@Override:检测被该注解标注的方法是否是继承自父类/接口的。

@Deprecated:该注解标注的内容,表示已经过时。

@SuppressWarnings:压制警告,一般传递参数 @SuppressWarnings(“all”)

自定义注解的定义格式

元注解
public @interface MyAnno {

	//属性列表;注解中的属性其实就是接口中的抽象方法
 String name();

 int age();

 int value();
 
 String address() default "beiji";
}

**本质:**通过 javap 反编译可以知道,注解的本质其实就是一个继承了 java.lang.annotation.Annotation 接口的一个接口

**属性:**接口中的抽象方法

​ 属性的返回值类型只能是:

​ 1.java中的基本数据类。

​ 2.String。

​ 3.枚举。

​ 4.注解。

​ 5.以上类型的数组。

属性赋值:

/**
 * @Author superMonkey
 * @Date 2022/7/30 18:26
 * @Description TODO
 * 1.如果只有一个属性需要赋值,并且属性的名称是value的话,可以省略
 * 2.数组赋值是,使用{}包裹,如果数据里只有一个值,{}可以省略
 * 3.对属性默认赋值 default,没有使用default的话,在使用注解时就必须赋值
 */
@MyAnno(name = "superMonkey",age = 2,value = 2 ,num = {2.3 , 8.0})
public class AnnotationDemo1 {

}

元注解

用于描述注解的注解被叫做元注解。

常用的元注解有:

  1. @Target:描述注解能够作用的位置。
    • ElementType取值:
      • TYPE:可以用在类上。
      • METHOD:可以用在方法上。
      • FIELD:可以作用于成员变量上。
  2. @Retention:描述注解被保留的阶段。
    • @Retention(RetentionPolicy.RUNTIME) 表示当前被描述的注解,会保留到class字节码文件中,并被JVM读到。
  3. @Documented:描述注解是否被抽取到api文档中。
  4. @Inherited:描述这个注解是否被子类继承。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {

    String name();

    int age();

    int value();

    String address() default "beiji";

    double[] num();
}

解析注解

注解一般都是和反射联合使用的。比如我现在需要获取注解的属性值

/**
 * @Author superMonkey
 * @Date 2022/7/31 0:07
 * @Description TODO
 */
public class AnnotationDemo3 {
    public static void main(String[] args) {
        Class<AnnotationDemo1> annotationDemo1Class = AnnotationDemo1.class;
        MyAnno annotation = annotationDemo1Class.getAnnotation(MyAnno.class);
        System.out.println(annotation.age());
        System.out.println(annotation.name());
        System.out.println(annotation.address());
        System.out.println(annotation.num()[0]);
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值