反射
反射机制
将类的各个组成部分封装成其他对象,这就是反射机制。
java代码的三个阶段
第一阶段:Source:源代码阶段
- 从java文件按成员变量,构造方法,成员方法通过javac编译成class文件。
- 其实就是在编译器中写代码,产生 .java 文件的时候,以及将这个 .java 文件 javac 生成为 .claa 字节码文件的过程都是源代码阶段。
第二阶段:Class:类对象阶段
- 通过类加载器(ClassLoader)把class文件中的成员变量,构造方法,成员方法加载到内存中。
- 在源代码阶段的.class文件中存储着,成员变量,构造方法,成员方法等内容。通过类加载器将 .class 文件加载到内存后,.clss文件里面的内容都需要存起来,java中有一个Class的类,这个类就是用来存储类加载器从.class文件中读取到的内容的。所以类的各个组成就被封装成立 成员变量(Field[]),构造方法(Constructor[]),成员方法(Method[])等对象。
第三阶段:Runtime:运行时阶段
- 可以创建对象,调用对象的方法。也可以通过newInstance()根据java类型生成对象。
反射的好处
- 可以在程序运行过程中,操作成员变量,成员方法,构造方法等对象。
- 可以解耦合,提高程序扩展性。
获取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("这个学生正在学习"); } }
获取成员变量
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 } }获取构造方法
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); } }获取成员方法
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); } }获取类名
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 { }
元注解
用于描述注解的注解被叫做元注解。
常用的元注解有:
- @Target:描述注解能够作用的位置。
- ElementType取值:
- TYPE:可以用在类上。
- METHOD:可以用在方法上。
- FIELD:可以作用于成员变量上。
- @Retention:描述注解被保留的阶段。
- @Retention(RetentionPolicy.RUNTIME) 表示当前被描述的注解,会保留到class字节码文件中,并被JVM读到。
- @Documented:描述注解是否被抽取到api文档中。
- @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]); } }
1007

被折叠的 条评论
为什么被折叠?



