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

三、反射

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);
    }
}

运行结果:

java静态类在什么时候加载_System_02

  • 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;
         = 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) {
         = 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