05反射与注解

1. 反射

1.1获取Class对象

  1. /*
  2. * java反射机制是在运行状态中,对于任意一个类,都能知道这个类的所以属性和方法;
  3. * 对于任意一个对象,都能调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法称为java语言的反射机制
  4. * 想要解剖一个类,必须先要获取该类的class对象,而剖析一个类或用反射解决具体问题就是使用相关的API
  5. * (1)java.lang.Class
  6. * (2)java.lang.Reflect
  7. * 所以,class对象是反射的根源*/
  8. public static void main(String[] args) throws ClassNotFoundException {
  9. /*哪些类型可以获取class对象*/
  10. // 1.基本数据类型和void
  11. Class<Integer> integerClass = int.class;
  12. Class<Void> voidClass = void.class;
  13. // 2.类和接口类型
  14. Class<Object> objectClass = Object.class;
  15. Class<Runnable> runnableClass = Runnable.class;
  16. // 3.枚举
  17. Class<ElementType> elementTypeClass = ElementType.class;
  18. // 4.注解
  19. Class<Override> overrideClass = Override.class;
  20. // 5.数组
  21. Class<Array> arrayClass = Array.class;
  22. /*获取class对象的四种方式
  23. * 1.类型名.class
  24. * 2.对象.getClass()
  25. * 3.Class.forName(类型全名称),通常需要配置文件配合使用,可以获取编译期间未知的类型
  26. * 4.ClassLoader的类加载器对象,loadClass(类型全名称)*/
  27. // 第一种类型名.class
  28. Class clazz = Reflect1.class;
  29. // 第二种 对象.getClass()
  30. Reflect1 r1 = new Reflect1();
  31. Class aClass = r1.getClass();
  32. // 第三种 Class.forName(类型全名称)
  33. Class clazz1 = Class.forName("Reflect.User");
  34. /*
  35. * 反射的应用场景
  36. * 各个框架的设计(主要场景)
  37. * 各大框架的内部实现了大量的反射机制,想要深入了解框架,则必须要了解反射机制*/
  38. }

1.2获取类型的详细信息

  1. public class Reflcet2 {
  2. /*
  3. * 反射的应用
  4. * 通过反射获取类型的详细信息
  5. * 可以获取:包,修饰符,类型名,父类(包括泛型父类),父接口(包括泛型父接口),成员(属性,构造器,方法)
  6. * 注解(类上的,方法上的,属性上的)*/
  7. public static void main(String[] args) throws ClassNotFoundException {
  8. Class clazz = Class.forName("Reflect.User");
  9. // 1.获取类名
  10. String name = clazz.getName();
  11. System.out.println(name);
  12. // 2.获取该类实现的所有接口
  13. Class[] interfaces = clazz.getInterfaces();
  14. System.out.println(interfaces);
  15. // 3.获取该类的所有属性
  16. Field[] declaredFields = clazz.getDeclaredFields();
  17. System.out.println(declaredFields);
  18. // 4.获取父类的字节码对象
  19. Class aClass = clazz.getSuperclass();
  20. System.out.println(aClass);
  21. // 5.获取该类的所有方法
  22. Method[] declaredMethods = clazz.getDeclaredMethods();
  23. System.out.println(declaredMethods);
  24. // 6.获取该类的所有构造方法
  25. Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
  26. System.out.println(declaredConstructors);
  27. }
  28. }

1.3通过反射创建任意类型对象

  1. public class Reflcet3 {
  2. public static void main(String[] args)
  3. throws ClassNotFoundException, InstantiationException, IllegalAccessException,
  4. NoSuchMethodException, InvocationTargetException {
  5. /*创建任意引用类型的对象
  6. * 1.直接通过class对象来实例化(要求必须有无参构造)
  7. * 2.通过获取构造器对象来进行实例化*/
  8. // 1.直接通过class对象来实例化
  9. Class<?> aClass = Class.forName("Reflect.User");
  10. // 如果类没有无参构造,就没有无参实例化方法
  11. User o = (User) aClass.newInstance();
  12. o.fn();
  13. // 2.通过获取构造器对象来进行实例化
  14. // 1.获取该类型的Class对象2.获取构造器对象3.创建对象
  15. // 已知的话,都倒是可以这样操作
  16. // 1.Class<User> u = User.class;
  17. Class<?> clazz = Class.forName("Reflect.User");
  18. // 2.获取位置类的构造函数
  19. Constructor constructor = clazz.getDeclaredConstructor(int.class, String.class);
  20. // 3.创建对象
  21. User o1 = (User) constructor.newInstance(18, "老王");
  22. o1.fn();
  23. }
  24. }

1.4通过反射获取任意类型的属性

  1. public class Reflcet4 {
  2. public static void main(String[] args)
  3. throws ClassNotFoundException, NoSuchFieldException, InstantiationException,
  4. IllegalAccessException {
  5. // 操作任意类型的属性
  6. Class clazz = Class.forName("Reflect.User");
  7. Object obj = clazz.newInstance();
  8. // 获取所有的属性名
  9. /*Field[] declaredFields = clazz.getDeclaredFields();
  10. for (Field item : declaredFields) {
  11. System.out.println(item);
  12. }*/
  13. // 获取已知的属性名
  14. // Field gender = clazz.getDeclaredField("gender");
  15. Field gender = clazz.getDeclaredField("age");
  16. // 如果属性值为私有的,则需要设置属性可以访问
  17. gender.setAccessible(true);
  18. // 设置属性值
  19. gender.set(obj, 18);
  20. // 获取属性值
  21. Object value = gender.get(obj);
  22. System.out.println(value);
  23. }
  24. }

1.5通过反射调用任意类型的方法

  1. public class Reflcet5 {
  2. // 反射调用任意类型的方法
  3. public static void main(String[] args)
  4. throws ClassNotFoundException, NoSuchMethodException, InstantiationException,
  5. IllegalAccessException, InvocationTargetException {
  6. // 1.获取该类型的class对象
  7. Class clazz = Class.forName("Reflect.User");
  8. Object obj = clazz.newInstance();
  9. // 2.获取对象方法
  10. // 全部方法
  11. /*Method[] declaredMethods = clazz.getDeclaredMethods();
  12. for (Method item : declaredMethods) {
  13. System.out.println(item);
  14. }*/
  15. // 单个方法
  16. Method fn = clazz.getDeclaredMethod("fn", String.class);
  17. // 3.调用方法 =>如果需要突破权限依旧是调用setAccessible(true)
  18. fn.invoke(obj, "你好啊");
  19. }
  20. }

测试用的代码

  1. public class User {
  2. private int age;
  3. private String name;
  4. public int gender;
  5. public User() {
  6. this.age = 0;
  7. this.name = "无";
  8. }
  9. public User(int age, String name) {
  10. this.age = age;
  11. this.name = name;
  12. }
  13. public void fn() {
  14. System.out.println(name + "今年" + age + "了");
  15. }
  16. public void fn(String str) {
  17. System.out.println(str);
  18. }
  19. }

2. 注解

  1. public class Annotation1 {
  2. /*什么是注解
  3. * 注解是一种代码级别的说明和类,接口平级.相当于一种标记,在程序中加入注解就等于为程序打上某种标记
  4. * javac编译器,开发工具和其他程序可以通过反射来了解你的类及元素上有无标记,看你的程序有什么标记,就去干相应的是去
  5. * 标记可以加在包,类,属性,方法,方法的参数以及局部变量上定义*/
  6. /*
  7. * 注解的作用
  8. * 执行编译期的检查 例如@Override 代表着重写父类的方法
  9. * 分析代码(主要用途:替代配置文件); 用在框架里面,注解开发*/
  10. /*
  11. * JDK提供的3种基本注解
  12. * 1.@Override:描述方法的重写
  13. * 2.@SupperssWarnings:压制警告
  14. * 3.@Deprecated:标记过时*/
  15. public static void main(String[] args)
  16. throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
  17. IllegalAccessException, InstantiationException {
  18. demo1 d = new demo1();
  19. // d.fn(); // 表现出来了
  20. /*自定义标注*/
  21. // 1.获取Demo1字节码对象
  22. Class clazz = demo1.class;
  23. // 2.获取Demo1的注解对象
  24. Annottation2 an = (Annottation2) clazz.getAnnotation(Annottation2.class);
  25. System.out.println(an);
  26. // 3.反射获得方法对象
  27. Method fn2 = clazz.getDeclaredMethod("fn2");
  28. // 想使用就得使用构造函数,给它一个对象
  29. Object o = clazz.newInstance();
  30. fn2.invoke(o);
  31. // System.out.println(fn2);
  32. }
  33. }
  1. /*元注解
  2. * 元注解是使用在自定义的注解,为自定义的注解提供支持的
  3. * 常用的元注解
  4. * @Target:定义该注解作用在什么上面(位置),默认注解可以在任何位置,值为:ElementType的枚举值
  5. * METHOD:方法
  6. * TYPE:类型
  7. * FIELD:字段
  8. * CONSTRUCTOR:构造方法声明
  9. * @Retention:定义该注解保留到那个代码阶段,值为:RetentionPolicy类型,默认只在源码阶段保留
  10. * SOURCE:只在源码上保留
  11. * CLASS:在源码和字节码上保留
  12. * RUNTIME:在所有阶段都保留 */
  13. @Target(value = {ElementType.METHOD, ElementType.TYPE})
  14. @Retention(value = RetentionPolicy.SOURCE)
  15. public @interface Annottation2 {}
  1. @Annottation2
  2. public class demo1 {
  3. @Deprecated
  4. public void fn() {
  5. System.out.println("标记已过时");
  6. }
  7. @Annottation2 // 使用注解时给属性赋值
  8. public void fn2() {
  9. System.out.println("你好");
  10. }
  11. }