注解和反射(所有框架实现所需要的东西)

注解(Annotation)

1、注解入门

  1. Annotation注解的作用:
    • 不是程序本身,可以对程序做出解释。(这一点和注释(comment)没有区别)
    • 可以被其他程序(比如:编译器等)读取
  2. Annotation的格式:
    • 注解是以”@注释名”在代码中存在的,还可以添加一些参数值,例如:@SuppressWarnings(value=”unchecked”)。
  3. Annotation在哪里使用?
    • 可以附加在package,class,method,field等上面,相当于给他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问

2、内置注解

1.@Deprecated

意思是说此方法已过时,是因为有新的API的类替代了此方法。这个被划去的方法仍然是可以正常使用的。(格式不支持删除线,此处代码贴图片)如下图:

image.png

运行结果:

Hi,baby

从结果看,@Deprecated不影响方法使用,只是一个提示。

2.@Override

1.告诉读代码的人,这是一个复写的方法

2.帮助自己检查是否正确的复写了父类中已有的方法告诉读代码的人,这是一个复写的方法。

3.@SuppressWarnings

用于通知java编译器忽略特定的编译警告。

如下图,编译器产生的警告对代码左侧行列的遮挡,有时候会影响我们断点调试时打的断点。

4.@FunctionalInterface

FunctionalInterface注解,在此做一下简单介绍:指定接口必须为函数式接口,如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错。“函数式接口”是指仅仅只包含一个抽象方法的接口;

5.@SafeVarargs

@SafeVarargs注解,只能用于标记构造函数和方法,由于保留策略声明为RUNTIME,所以此注解可以在运行时生效。

使用的时候要注意:@SafeVarargs注解,对于非static或非final声明的方法,不适用,会编译不通过。

3、自定义注解,元注解

  1. 元注解
    • 元注解的作用是负责注解其他注解,Java定义了四个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明。
    • 这些类型和他们所支持的类子在java.lang.annotation包中找到(@Target,@Rtention,@Documented,@Inherited)
  • @Target:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
  • @Rtention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期
  • @Documented:说明该注解将被包含在javadoc中
  • @Inherited:说明子类可以继承父类中的该注解
  • @Repeatable 它允许在相同的程序元素中重复注解,在需要对同一种注解多次使用时,往往需要借助 @Repeatable 注解
  • @Native 注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。
  1. 自定义注解
  1. //测试元注解
  2. @MyAnnotation(school = {"清华大学"})
  3. public class Test02 {
  4. //注解可以显示赋值,如果没有默认值,我们就必须给注解赋值
  5. @MyAnnotation(name = "yinxin",school = {"合工大","西工大"})
  6. public void test(){
  7. }
  8. //当参数是value()时候可以省略value,当参数是name()可以省略name
  9. @MyAnnotation2("value")
  10. public void test2(){
  11. }
  12. }
  13. //定义一个元注解
  14. //Target 表示注解能用在哪些地方
  15. //Retention 表示我们的注解在什么地方还有效RUNTIME>CLASS>SOURCE:分别对应运行期,编译期,源码级
  16. //Documented 表示是否将我们的注解生成在JAVAdoc中
  17. //Inherited 子类可以继承父类中的该注解
  18. @Inherited
  19. @Documented
  20. @Retention(RetentionPolicy.RUNTIME)
  21. @Target(value = {ElementType.METHOD,ElementType.TYPE})
  22. @interface MyAnnotation{
  23. //注解的参数:参数类型+参数名();
  24. String name() default "";
  25. int age() default 0;
  26. int id() default -1;//如果默认值为-1,代表不存在
  27. String[] school();
  28. }
  29. @Retention(RetentionPolicy.RUNTIME)
  30. @Target(value = {ElementType.METHOD,ElementType.TYPE})
  31. @interface MyAnnotation2{
  32. String value();
  33. }

4、反射

静态语言VS动态语言

  1. 动态语言
  • 是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。
  • 主要动态语言:Object-C、C#、JavaScript、PHP、Python等。
  1. 静态语言
  • 与静态语言相对应的,运行时结构不可变的语言就是静态语言。如java、c、c++。
  • Java不是动态语言,但Java可以称之为”准动态语言“即Java有一定的时效性,我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活!

Java Reflection

Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection api取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

加载完类之后,在堆内存的方法区中就产生一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过类的对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以我们形象的称之为反射。

image.png

Class类的常用方法

  1. 常用方法

image.png

  1. 获取Class类的实例
    1. 若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高。:Class clazz=Person.class;
    2. 已知某个类的实例,调用该实例的getClass()方法获取Class对象:Class clazz=Person.getClass();
    3. 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException:Class<?> c1 = Class.forName(“com.xx.xx.类名”);
    4. 内置基本数据类型可以直接用类名.Type
    5. 还可以利用ClassLoader我们之后讲解
  2. Java内存分析

image.png

了解类的加载过程
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。

drawio

**重要:类的加载与ClassLoader的理解
类加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象。
链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。验证:确保加载的类信息符合JVM规范,没有安全方面的问题
准备:正式为类变量 (static) 分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配.
解析:虛拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。初始化:执行类构造器方法的过程。类构造器方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化虚拟机会保证一个类的方法在多线程环境中被正确加锁和同步。
*类加载内存分析

image.png

  1. public class Test05 {
  2. public static void main(String[] args) {
  3. A a = new A();
  4. System.out.println(a.m);
  5. /**
  6. * 1、加载到内存中。会产生一个类对应Class对象
  7. * 2、链接,链接结束后m=0
  8. * 3、初始化
  9. * <clinit>(){
  10. * System.out.println("A类静态代码块初始化");
  11. * m=300;
  12. * m=100;
  13. * }
  14. */
  15. }
  16. }
  17. class A{
  18. static {
  19. System.out.println("A类静态代码块初始化");
  20. m=300;
  21. }
  22. static int m=100;
  23. public A(){
  24. System.out.println("A类的无参构造初始化");
  25. }
  26. }

类加载器的作用
类加载器的作用是用来吧类(class)装载进内存的。JVM规范定义了如下类型的类的加载器。

  1. 反射
    动态创建对象并执行方法
  • 反射拿到一个类的属性(指定和所有)、方法(指定和所有)、构造器(指定和所有)
  1. public class GetClassInfo {
  2. public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
  3. Class userClazz = Class.forName("com.yinxin.reflection.User");
  4. //获得类名
  5. System.out.println(userClazz.getName());
  6. System.out.println(userClazz.getSimpleName());
  7. System.out.println("=================================");
  8. //获得类的属性
  9. // Field[] fields = userClazz.getFields();//没打印出User类的属性,因为此方法只能打印公有属性
  10. // fields = userClazz.getDeclaredFields();//打印出了User类的属性,打印出所有属性
  11. // for (Field field : fields) {
  12. // System.out.println(field);
  13. // }
  14. //获得指定属性的值
  15. // Field name = userClazz.getDeclaredField("name");
  16. // System.out.println(name);
  17. //获得类的方法
  18. // System.out.println("=================================");
  19. // for (Method method : userClazz.getMethods()) {//所有本类方法以及其父类方法
  20. // System.out.println(method);
  21. // }
  22. //获得指定类的方法
  23. // System.out.println(userClazz.getDeclaredMethod("getName"));//指定类的方法
  24. System.out.println("================================");
  25. //获得构造器
  26. Constructor[] constructors = userClazz.getConstructors();
  27. for (Constructor constructor : constructors) {
  28. System.out.println(constructor);
  29. }
  30. //获得指定的构造方法
  31. Constructor constructor = userClazz.getConstructor(int.class, String.class, String.class);
  32. System.out.println(constructor);
  33. }
  34. }
  • 通过反射构造对象和为对象赋值和操作对象的属性
  1. //通过反射构造对象和对对象赋值
  2. public class TestCreateClass {
  3. public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
  4. //获得Class对象
  5. Class UserClazz = Class.forName("com.yinxin.reflection.User");
  6. //构造对象
  7. // User user = (User) UserClazz.newInstance();
  8. // System.out.println(user);
  9. //通过构造器创建对象
  10. // Constructor constructor = UserClazz.getDeclaredConstructor(int.class, String.class, String.class);
  11. // User allArgsUser = (User)constructor.newInstance(19, "大大", "男");
  12. // System.out.println(allArgsUser);
  13. //通过反射调用普通方法
  14. User user = (User) UserClazz.newInstance();
  15. //通过反射获取一个方法
  16. Method setName = UserClazz.getDeclaredMethod("setName", String.class);
  17. setName.invoke(user,"小静");//invoke激活的意思
  18. System.out.println(user.getName());
  19. //通过反射操作属性
  20. User user1 = (User)UserClazz.newInstance();
  21. Field name = UserClazz.getDeclaredField("name");//Class com.yinxin.reflection.TestCreateClass can not access a member of class com.yinxin.reflection.User with modifiers "private"
  22. //此时报错:name是私有变量无法访问
  23. name.setAccessible(true);//取消该属性的安全检测
  24. name.set(user1,"小静2");
  25. System.out.println(user1.getName());
  26. }
  27. }
  • Method和Field、Constructor对象都有setAccessible()方法。
  • setAccessible的作用是启动和禁用访问安全检查的开关
  • 参数值为true则知识反射的对象在使用时应该取消Java语言访问检查。
    • 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。
  • 使得原本无法访问的私有成员也可以访问
  • 参数值为false则指示反射的对象应该实施Java语言访问检查
  • 获取泛型的类型
  1. //获取泛型的类型
  2. public class TestGetGeneric {
  3. public void test01(Map<String,User>map, List<User> list){
  4. System.out.println("test01");
  5. }
  6. public Map<String,User> teste02(){
  7. System.out.println("test02");
  8. return null;
  9. }
  10. public static void main(String[] args) throws NoSuchMethodException {
  11. Method method = TestGetGeneric.class.getMethod("test01", Map.class, List.class);//获取方法
  12. Type[] genericParameterTypes = method.getGenericParameterTypes();//获取方法参数的类型数组
  13. for (Type genericParameterType : genericParameterTypes) {//遍历它
  14. System.out.println(genericParameterType+"=================参数");
  15. if(genericParameterType instanceof ParameterizedType){
  16. Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
  17. for (Type actualTypeArgument : actualTypeArguments) {
  18. System.out.println(actualTypeArgument+"===============泛型类型");
  19. }
  20. }
  21. }
  22. System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
  23. Method test02 = TestGetGeneric.class.getMethod("test02", null);//Map.class
  24. Type genericReturnType = test02.getGenericReturnType();
  25. if(genericReturnType instanceof ParameterizedType){
  26. System.out.println(genericReturnType+"========================参数");
  27. Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();//实际参数类型
  28. for (Type actualTypeArgument : actualTypeArguments) {
  29. System.out.println(actualTypeArgument+"=======================泛型类型");
  30. }
  31. }
  32. }
  33. }
  34. //getGenericParameterTypes获取到对应的参数类型
  • 获取注解信息
  1. //练习反射操作注解
  2. public class GetAnnotationInfo {
  3. public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
  4. Class stuClazz = Class.forName("com.yinxin.reflection.StudentX");
  5. //通过反射获取注解
  6. Annotation[] annotations = stuClazz.getAnnotations();
  7. for (Annotation annotation : annotations) {
  8. System.out.println(annotation);
  9. }
  10. //获得注解的value的值
  11. TableXin tableXin = (TableXin)stuClazz.getAnnotation(TableXin.class);
  12. String value = tableXin.value();
  13. System.out.println(value);
  14. //获得类指定的注解
  15. Field name = stuClazz.getDeclaredField("name");//先获取到这个类的某个属性
  16. FiledXin annotation = name.getAnnotation(FiledXin.class);//再获取这个属性的注解
  17. System.out.println(annotation.columnName());//输出这个类的这个属性上的注解的内容的值
  18. System.out.println(annotation.type());
  19. System.out.println(annotation.length());
  20. }
  21. }
  22. @TableXin("db_student")
  23. class StudentX{
  24. @FiledXin(columnName = "db_id",type = "int",length = 10)
  25. private int id;
  26. @FiledXin(columnName = "db_age",type = "int",length = 10)
  27. private int age;
  28. @FiledXin(columnName = "db_name",type = "varchar",length = 3)
  29. private String name;
  30. public StudentX() {
  31. }
  32. public StudentX(int id, int age, String name) {
  33. this.id = id;
  34. this.age = age;
  35. this.name = name;
  36. }
  37. public int getId() {
  38. return id;
  39. }
  40. public void setId(int id) {
  41. this.id = id;
  42. }
  43. public int getAge() {
  44. return age;
  45. }
  46. public void setAge(int age) {
  47. this.age = age;
  48. }
  49. public String getName() {
  50. return name;
  51. }
  52. public void setName(String name) {
  53. this.name = name;
  54. }
  55. @Override
  56. public String toString() {
  57. return "student{" +
  58. "id=" + id +
  59. ", age=" + age +
  60. ", name='" + name + '\'' +
  61. '}';
  62. }
  63. }
  64. //类名的注解
  65. @Target({ElementType.TYPE})
  66. @Retention(RetentionPolicy.RUNTIME)
  67. @interface TableXin{
  68. String value();
  69. }
  70. //属性的注解
  71. @Target({ElementType.FIELD})
  72. @Retention(RetentionPolicy.RUNTIME)
  73. @interface FiledXin{
  74. String columnName();
  75. String type();
  76. int length();
  77. }