Java注解和反射(静态语言和动态语言、类加载器、反射)

Java注解和反射(静态语言和动态语言、类加载器、反射)

一、注解(Annotation)

Java不注解是程序本身 ,但可以对程序作出解释.(这一点和注释(comment)没什么区别) , 可以被其他程序(比如:编译器等)读取

  • 定义格式

    注解是以"@注释名"在代码中存在的 , 还可以添加一些参数值 , 例如@SuppressWarnings(value=“unchecked”).

  • 使用场景

    可以附加在package , class , method , field 等上面 , 相当于给他们添加了额外的辅助信息, 我们可以通过反射机制编程实现对这些元数据的访问

  • 内置注解

    • @Override : 定义在 java.lang.Override 中 , 此注释只适用于修辞方法 , 表示一个方法声明打算 重写超类中的另一个方法声明
    • @Deprecated : 定义在java.lang.Deprecated中 , 此注释可以用于修辞方法 , 属性 , 类 , 表示不 鼓励程序员使用这样的元素 , 通常是因为它很危险或者存在更好的选择
    • @SuppressWarnings : 定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息,需添加特定参数才能使用,例如“all”,“unchecked”,"value={“unchecked”,“deprecation”}"等
  • 元注解

    元注解的作用就是负责注解其他注解 , Java定义了4个标准的meta-annotation类型,他们被用来 提供对其他annotation类型作说明

    • @Target : 用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
    • @Retention : 表示需要在什么级别保存该注释信息 , 用于描述注解的生命周期 (SOURCE < CLASS < RUNTIME)
    • @Document:说明该注解将被包含在javadoc中
    • @Inherited:说明子类可以继承父类中的该注解
  • 自定义注解

    使用 @interface自定义注解时 , 自动继承了java.lang.annotation.Annotation接口

    • @ interface用来声明一个注解 , 格式 : public @ interface 注解名 { 定义内容 }

    • 其中的每一个方法实际上是声明了一个配置参数.

    • 方法的名称就是参数的名称.

    • 返回值类型就是参数的类型 ( 返回值只能是基本类型,Class , String , enum ).

    • 可以通过default来声明参数的默认值 .

    • 如果只有一个参数成员 , 一般参数名为value

    • 注解元素必须要有值 , 我们定义注解元素时 , 经常使用空字符串,0作为默认值 .

      import java.lang.annotation.*;
      
      @MyAnnotation
      public class Test1 {
          //有默认值可以不写,无默认值的要赋值
          @MyAnnotation(name = "张",age = 18,schools = "邮电大学",classs = "电子")
          public void test(){}
      
          //只有一个参数时,默认使用value()做参数名,使用时可以省略参数名
          @MyAnnotation2("西邮")
          public void test2(){}
      
      }
      
      @Target(value = {ElementType.METHOD,ElementType.TYPE,ElementType.FIELD}) //作用域
      @Retention(RetentionPolicy.RUNTIME)//运行时级别
      @Documented//生成Doc文档
      @Inherited //子类可以继承父类的注解
      @interface MyAnnotation{
          //参数类型  参数名
          String name() default "";
          int age() default 0;
          int id() default -1; //-1表示不存在
          String classs() default "";
      
          String[] schools() default {"西开","西邮"};
      
      }
      
      @Target(value = ElementType.METHOD)
      @Retention(RetentionPolicy.RUNTIME)
      @interface MyAnnotation2{
          String value();
      }
      

二、反射

一、静态语言和动态语言
  • 动态语言

    • 概述:是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被 引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代 码可以根据某些条件改变自身结构
    • 主要动态语言:Object-C、C#、JavaScript、PHP、Python等。
  • 静态语言

    • 概述:与动态语言相对应的,运行时结构不可变的语言就是静态语言。
    • 主要静态语言:Java、C、C++
  • Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性, 我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更 加灵活

二、类加载器
  • 类的加载

    当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化

    • 加载

      就是指将class文件读入内存,并为之创建一个Class对象。任何类被使用时系统都会建立一Class对象

    • 连接

      验证 是否有正确的内部结构,并和其他类协调一致

      准备 负责为类的静态成员分配内存,并设置默认初始化值

      解析 虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程

    • 初始化

      JVM进行类的初始化

      执行类构造器()方法的过程。类构造器()方法是由编译期自动收集类中所有类变量的赋值动作和静态 代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)

      当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化

      虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步

  • 类的加载过程

    • 创建类的实例
    • 类的静态变量,或者为静态变量赋值
    • 类的静态方法
    • 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
    • 初始化某个类的子类
    • 直接使用java.exe命令来运行某个主类
  • 类加载器

    负责将.class文件加载到内在中,并为之生成对应的Class对象

  • 类加载器的组成

    • System ClassLoader 系统类加载器

      负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径

    • Extension ClassLoader 扩展类加载器

      负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下ext目录

    • Bootstrap ClassLoader 根类加载器

      也被称为引导类加载器,负责Java核心类的加载,用C++编写

  • 案例

    public class TestClassLoader {
        public static void main(String[] args) throws ClassNotFoundException {
    
            //1.获得系统类加载器
            ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
            System.out.println(systemClassLoader);
    
            //2.获得系统类加载器的父类加载器---扩展类加载器
            ClassLoader parent = systemClassLoader.getParent();
            System.out.println(parent);
    
            //3.扩展类加载器的父类---根加载器(最底层 c++编写)
            ClassLoader parent1 = parent.getParent();
            System.out.println(parent1);
    
            //4.测试当前类是哪个加载器加载的
            ClassLoader classLoader = Class.forName("org.westos.Test0607.TestClassLoader").getClassLoader();
            System.out.println(classLoader);
    
            //5.测试Object类是哪个加载器加载的
            ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
            System.out.println(classLoader1);
    
            //系统类加载器可以加载类的路径
            String property = System.getProperty("java.class.path");
            System.out.println(property);
        }
    }
    

    运行结果:

    在这里插入图片描述

三、反射

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

  • Class类

    Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass方法自动构造

  • 获取Class类的实例

    • 若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高

      Class personClass = Person.class;
      
    • 已知某个类的实例,调用该实例的getClass()方法获取Class对象

      Person person = new Person();
      Class personClass = person.getClass();
      
    • 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取, 可能抛出ClassNotFoundException

      public static void main(String[] args) throws ClassNotFoundException {
           Class aClass = Class.forName("org.westos.homework0406.Person");
      }
      
    • 内置基本数据类型可以直接用类名.Type 获取Class对象

      Class<Integer> integerClass = Integer.TYPE;
      
  • 有Class对象的类型

    • class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
    • interface:接口
    • []:数组
    • enum:枚举
    • annotation:注解@interface
    • primitive type:基本数据类型
    • void
    import java.lang.annotation.ElementType;
    
    public class TestReflection3 {
        public static void main(String[] args) {
            Class<Object> c1 = Object.class; //类
            Class<Runnable> c2 = Runnable.class;//接口
            Class<String[]> c3 = String[].class;//一维数组
            Class<int[][]> c4 = int[][].class;//二维数组
            Class<ElementType> c5 = ElementType.class;//枚举
            Class<Override> c6 = Override.class;//注解
            Class<Void> c7 = Void.class;//void
            Class<Class> c8 = Class.class;
    
            //数组类型一样,同一个维度,只能有一个class对象
            String[] a = new String[10];
            String[] b = new String[11];
            Class<? extends String[]> c9 = a.getClass();
            Class<? extends String[]> c10 = b.getClass();
            System.out.println(c9==c10);
    
            System.out.println(c1);
            System.out.println(c2);
            System.out.println(c3);
            System.out.println(c4);
            System.out.println(c5);
            System.out.println(c6);
            System.out.println(c7);
            System.out.println(c8);
            System.out.println(c9);
            System.out.println(c10);
        }
    }
    

    运行结果:
    在这里插入图片描述

  • Class对象获取构造方法并使用

    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    
    public class Test10 {
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            Class<Person> personClass = Person.class;
            //获取所有构造方法
            Constructor<?>[] constructors = personClass.getDeclaredConstructors();
            for (Constructor<?> constructor : constructors) {
                System.out.println(constructor);
            }
            //获取空参构造方法
            Constructor<Person> constructor = personClass.getConstructor(null);
            System.out.println(constructor);
            //获取有参构造
            Constructor<Person> constructor1 = personClass.getConstructor(String.class);
            System.out.println(constructor1);
    
            //通过获取到的构造方法创建对象
            //Object obj = con.newInstance(null);
            Person person = constructor.newInstance();
            System.out.println(person);
        }
    }
    
    //输出:
    public org.westos.Test0607.Person()
    public org.westos.Test0607.Person(java.lang.String)
    public org.westos.Test0607.Person()
    public org.westos.Test0607.Person(java.lang.String)
    Person{name='null'}
    
  • 通过反射获取私有构造方法

    AccessibleObject 类是 Field、Method 和 Constructor 对象的父类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。

    对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获取字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。常用方法如下:

    public void setAccessible(boolean flag) throws SecurityException

    参数值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。参数值为 false 则指示反射的对象应该实施 Java 语言访问检查。

    • 获取私有构造方法,步骤如下

      • 获取到Class对象

      • 获取指定的构造方法

      • 暴力访问, 通过setAccessible(boolean flag)方法

      • 通过构造方法类Constructor中的方法,创建对象

    • 案例

    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    
    public class Test11 {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InvocationTargetException {
            //1,获取到Class对象
            Class c = Class.forName("org.westos.Test0607.Person");//包名.类名
    
            //2,获取指定的构造方法
            //private Person(String name, int age)
            Constructor con = c.getDeclaredConstructor(String.class);
    
            //3,暴力反射
            con.setAccessible(true);//取消 Java 语言访问检查
    
            //4,通过构造方法类中的功能,创建对象
            Object obj = con.newInstance("小明");
            System.out.println(obj);
        }
    }
    
    //运行结果:
    Person{name='小明'}
    
  • 通过反射获取成员变量并使用

    在反射机制中,把类中的成员变量使用类Field表示。可通过Class类中提供的方法获取成员变量:

    • 返回一个成员变量

      public Field getField(String name) 获取指定的 public修饰的变量

      public Field getDeclaredField(String name) 获取指定的任意变量

    • 返回多个成员变量

      public Field[] getFields() 获取所有public 修饰的变量

      public Field[] getDeclaredFields() 获取所有的 变量 (包含私有)

    • 案例

    import java.lang.reflect.Field;
    
    public class Test12 {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException {
            //获取Class对象
            Class c = Class.forName("org.westos.Test0607.Animal");
    
            //获取成员变量
            //Field[] fields = c.getFields();
            Field[] fields = c.getDeclaredFields();
            for (Field field : fields) {
                System.out.println(field);
            }
            System.out.println("-----------------");
            //一个变量
            //public int name;
            Field field = c.getDeclaredField("age");
            System.out.println(field);
        }
    }
    
    //运行结果
    private java.lang.String org.westos.Test0607.Animal.name
    private int org.westos.Test0607.Animal.age
    -----------------
    private int org.westos.Test0607.Animal.age
    
    • 获取对象并赋值
    import java.lang.reflect.Field;
    
    public class Test13 {
        public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
            //通过放射获取对象
            Class<?> c = Class.forName("org.westos.Test0607.Animal");
            Object o = c.newInstance();
    
            //反射获取成员变量
            Field name = c.getDeclaredField("name");
            Field age = c.getDeclaredField("age");
    
            //取消 Java 语言访问检查
            name.setAccessible(true);
            age.setAccessible(true);
    
            //赋值
            name.set(o,"旺财");
            age.set(o,3);
    
            System.out.println(o.toString());
        }
    }
    
    //运行结果
    Animal{name='旺财', age=3}
    
  • 通过反射获取成员方法并使用

    在反射机制中,把类中的成员方法使用类Method表示。可通过Class类中提供的方法获取成员方法:

    • 返回获取一个方法:

      • public Method getMethod(String name, Class<?>… parameterTypes)

        获取public 修饰的方法

      • public Method getDeclaredMethod(String name, Class<?>… parameterTypes)

        获取任意的方法,包含私有的

        参数1: name 要查找的方法名称; 参数2: parameterTypes 该方法的参数类型

    • 返回获取多个方法:

      • public Method[] getMethods() 获取本类与父类中所有public 修饰的方法

      • public Method[] getDeclaredMethods() 获取本类中所有的方法(包含私有的)

    • public Object invoke(Object obj, Object… args)

      执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。

    • 案例

    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class Test14 {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            //获取Class对象
            Class<?> c = Class.forName("org.westos.Test0607.Animal");
    
            //获取构造方法
            Constructor<?> constructor = c.getConstructor(String.class,int.class);
    
            //获取对象
            Object o = constructor.newInstance("小黄",3);
    
            //获取指定方法
            Method getAge = c.getDeclaredMethod("getAge",null);
    
            //执行找到的方法
            Object invoke = getAge.invoke(o, null);
            System.out.println(invoke);
    
        }
    }
    
    //运行结果
    3
    
  • 反射操作泛型

    • Java采用泛型擦除的机制来引入泛型 , Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题 , 但是 , 一旦编译完成 , 所有和泛型有关的类型全部擦除

    • 为了通过反射操作这些类型 , Java新增了 ParameterizedType , GenericArrayType , TypeVariable 和 WildcardType 几种类型来代表不能被归一到Class类中的类型但是又和原 始类型齐名的类型

    • ParameterizedType : 表示一种参数化类型,比如Collection

    • GenericArrayType : 表示一种元素类型是参数化类型或者类型变量的数组类型

    • TypeVariable : 是各种类型变量的公共父接口

    • WildcardType : 代表一种通配符类型表达式

    • public Class<?> getReturnType() 返回一个 Class 对象,该对象描述了此 Method 对象所表示的方法的正式返回类型

    • public Type getGenericReturnType()

      返回表示由此 Method 对象所表示方法的正式返回类型的 Type 对象。
      如果返回类型是参数化类型,则返回的 Type 对象必须实际反映源代码中所用参数的实际类型。
      如果返回类型是类型变量或参数化类型,则创建它。否则将解析它。

    • public Type getActualTypeArguments 获得真实的类型参数

    • 案例

    import java.lang.reflect.Method;
    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    import java.util.List;
    import java.util.Map;
    
    //测试反射获取泛型
    @SuppressWarnings("all")
    public class Test08 {
    
        //带有泛型参数的方法
        public void test01(Map<String, User> map, List<User> list) {
            System.out.println("test01");
        }
    
        //带有泛型返回值的方法
        public Map<Integer, User> test2() {
            System.out.println("test02");
            return null;
        }
    
        public static void main(String[] args) throws Exception {
            //获得指定方法的泛型信息
    
            Method method = Test08.class.getDeclaredMethod("test01", Map.class, List.class);
    
            //获得泛型参数类型信息
            Type[] t = method.getGenericParameterTypes();
    
            for (Type type : t) {
                System.out.println("#" + type);
                if (type instanceof ParameterizedType) {
                    //getActualTypeArguments 获得真实的类型参数
                    Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
                    for (Type actualTypeArgument : actualTypeArguments) {
                        System.out.println("真实的泛型类型:" + actualTypeArgument);
                    }
                }
            }
    
            //获得返回值泛型信息
    
            Method method2 = Test08.class.getMethod("test2", null);
    
            //获得泛型参数类型信息
            //getGenericReturnType获得泛型返回值信息
            Type genericReturnType = method2.getGenericReturnType();
    
            if (genericReturnType instanceof ParameterizedType) {
                //getActualTypeArguments 获得真实的类型参数
                Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println("真实的返回值泛型类型:" + actualTypeArgument);
                }
            }
    
    
        }
    }
    
    //输出:
    #java.util.Map<java.lang.String, com.kuang.reflection.User>
    真实的泛型类型:class java.lang.String
    真实的泛型类型:class com.kuang.reflection.User
    #java.util.List<com.kuang.reflection.User>
    真实的泛型类型:class com.kuang.reflection.User
    真实的返回值泛型类型:class java.lang.Integer
    真实的返回值泛型类型:class com.kuang.reflection.User
    
  • 反射操作注解

    import java.lang.annotation.*;
    //使用反射读取注解
    /*
    1.定义注解
    2.在类中使用注解
    3.使用反射获取注解
     */
    public class Test8 {
        public static void main(String[] args) throws Exception{
            //通过反射获取注解信息
            Class<?> c = Class.forName("org.westos.Test0607.student2");
            Annotation[] annotations = c.getAnnotations();
            for (Annotation annotation : annotations) {
                System.out.println(annotation);
            }
    
            //通过注解的value方法获得注解的值
            Table annotation = c.getAnnotation(Table.class);
            System.out.println(annotation.value());
    
            java.lang.reflect.Field id = c.getDeclaredField("id");
            System.out.println(id);
    
            //获得字段的注解
            Field annotation1 = id.getAnnotation(Field.class);
            //获得注解的参数信息
            System.out.println(annotation1.columnName());
            System.out.println(annotation1.length());
            System.out.println(annotation1.type());
        }
    }
    
    //学生实体类
    @Table("db_student")
    class student2{
        @Field(columnName = "id",type = "int",length = 10)
        private int id;
        @Field(columnName = "db_age",type = "int",length = 3)
        private int age;
        @Field(columnName = "db_name",type = "varchar",length = 20)
        private String name;
    
        public student2() {
        }
    
        public student2(int id, int age, String name) {
            this.id = id;
            this.age = age;
            this.name = name;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "student2{" +
                    "id=" + id +
                    ", age=" + age +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
    //表名---类名的注解
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface Table{
        String value();
    }
    
    //字段类的注解
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface Field{
        //参数类型  参数名
        String columnName();//列名
        String type();//类型
        int length();//长度
    }
    
    //输出:
    @org.westos.Test0607.Table(value=db_student)
    db_student
    private int org.westos.Test0607.student2.id
    id
    10
    int
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值