1. Class类

对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了那些接口。
对于每个类而言,JRE都为其保留了一个不变Class类型的对象。
一个Class对象包含了特定于某个结构(class/interface/enum/annotation/primitive type/void/[])有关信息

  • Class本身也是一个类
  • Class对象只能由系统建立对象
  • 一个加载的类在JVM中只有一个Class实例
  • 一个Class对象对应的是一个加载到JVM中的一个.class文件
  • 每个类的实例都会记得自己是由那个Class实例所生成
  • 通过Class可以完整的得到一个类中的所有被加载的结构
  • Class类时Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

1.1Class类的常用方法

static ClassforName(String name):返回指定类名name的Class对象
Object newInstance():调用缺省构造函数,返回Class对象的一个实例
getName():返回次Class对象所表示的实体(类,接口,数组类或void)的名称
Class getSuperClass():返回当前Class对象的父类的Class对象
ClassLoader getClassLoader():返回该类的类加载器
Constructor[] getConstructors():返回一个包含某些Constructor对象的数组
Method getMethod(String name,Class.. T):返回一个Method对象,此对象的形参类型为paramType
Field[] getDeclaredFields():返回Field对象的一个数组

1.2获取Class类的实例

  • 若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高
    Class clazz=Person.class;
  • 已知某个类的实例,调用该实例的getClass()方法获取Class对象
    Class clazz=person.getClass();
  • 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException
    Class clazz=Class.forName("demo-1.Student");
  • 内置基本数据类型可以直接用类名.Type获取
  • 还可以利用Class Loader获取

1.3哪些类型可以有Class对象

  • class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
  • interface:接口
  • []:数组
  • enum:枚举
  • annotation:注解@interface
  • primitive:基本数据类型
  • void
    案例代码如下:
    package snow.dong.reflection;
    import java.lang.annotation.ElementType;
    //所有类型的Class
    public class TestClass {
     public static void main(String[] args) {
         Class c1=Object.class;//类
         Class c2=Comparable.class;//接口
         Class c3=String[].class;//一维数组
         Class c4=int[][].class;//二维数组
         Class c5=Override.class;//注解
         Class c6= ElementType.class;//枚举
         Class c7=Integer.class;//基本数据类型
         Class c8=void.class;//void
         Class c9=Class.class;//Class
         //打印信息
         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);
         //只要元素类型与维度一样,就是同一个class
         int[] a=new int[10];
         int[] b=new int[100];
         System.out.println(a.getClass().hashCode());
         System.out.println(b.getClass().hashCode());
     }
    }
    #结果如下:
    class java.lang.Object
    interface java.lang.Comparable
    class [Ljava.lang.String;
    class [[I
    interface java.lang.Override
    class java.lang.annotation.ElementType
    class java.lang.Integer
    void
    class java.lang.Class
    460141958
    460141958

2.获取运行时类的完整结构

2.1 通过反射获取运行时类的完整结构

  • 能够获得filed、Method、Constructor,SuperClass,Interface,Annotation

2.2 有Class对象,能做什么

  • 创建类的对象:调用Class对象的newInstance()方法
    • 1类必须有一个无参数的构造器
    • 2类的构造器的访问权限需要足够
      但是,只要在操作的时候明确调用类中的构造器,并将参数传递进去,才可以实例化对象
    • 1通过Class类的getDeclaredconstructor(Class... patameterType)取得本类的指定形参类型的构造器。
    • 2向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
    • 3通过Constructor实例化对象
  • 调用指定的方法(调用类中的方法,通过Method类来完成)
    • 1通过Class类的getMethod(String name,Class...parameterTypes)方法获取一个Method对象,并设置此方法操作所需要的的参数类型。
    • 2之后使用Object invoke(Object obj,Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息
      图片说明
  • Object invoke(Object obj,Object...args)
    • Object对应着原方法的返回值,若原方法没有返回值,返回null
    • 若原方法为静态方法,此时形参Object obj 可为null
    • 若原方法形参列表为空,则Object[] args为null
    • 若原方法声明为private,则需要在调用次invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private方法
  • setAccessible
    • Method 和Filed,Constructor对象都有setAccessible()方法
    • setAccessible作用是启动和禁用安全检查的开关
    • 参数为true则指示反射的对象在使用时应该取消Java语言的访问检查
    • 提高反射的效率。如果代码中必须用到反射,而该句代码需要频繁的被调用,那么请设置为true
    • 使得原本无法访问的私有成员也可以访问
    • 设置为false则指示反射的对象应该实施Java语言访问检查
      package snow.dong.reflection;
      import java.lang.reflect.Constructor;
      import java.lang.reflect.Field;
      import java.lang.reflect.InvocationTargetException;
      import java.lang.reflect.Method;
      public class TestReflection {
      public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
         //获得Class对象
         Class c1=Class.forName("snow.dong.reflection.User");
         //构造一个对象
         User user=(User)c1.newInstance();//本质上调用了类的无参构造器
         System.out.println(user);
         System.out.println("----通过构造器创建对象----");
         Constructor constructor=c1.getDeclaredConstructor(String.class,int.class,String.class);
         User user1=(User)constructor.newInstance("snowdong",18,"man");
         System.out.println(user1);
         System.out.println("----通过反射调用普通方法----");
         Method setName=c1.getDeclaredMethod("setName",String.class);
         //invoke:激活
         //(对象,"方法的值")
         setName.invoke(user,"snowDong01");
         System.out.println(user.getName());
         System.out.println("----通过反射操作属性----");
         Field gender=c1.getDeclaredField("gender");
         //不能直接操作私有属性,需关闭程序的安全检测,如下
         gender.setAccessible(true);
         gender.set(user,"不男不女");
         System.out.println(user.getGender());
      }
      }
      #//运行结果如下:
      #User{name='null', age=0, gender='null'}
      #----通过构造器创建对象----
      #User{name='snowdong', age=18, gender='man'}
      #----通过反射调用普通方法----
      #snowDong01
      #----通过反射操作属性----
      #不男不女

2.3反射操作泛型

  • Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除。
  • 为了通过反射操作这些类型,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。
  • ParameterizedType:表示一种参数化类型,比如Collection
  • GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
  • TypeVariable:是各种类型变量的公共父接口
  • WildcardType:代表一种通配符类型表达式
    具体案例代码说明:
    package snow.dong.reflection;
    import java.lang.reflect.Method;
    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    import java.util.List;
    import java.util.Map;
    public class TestGenericType {
     public void test01(Map<String,User> map, List<User> list){
         System.out.println("test01");
     }
     public Map<String,User> test02(){
         System.out.println("test02");
         return null;
     }
     public static void main(String[] args) throws NoSuchMethodException {
         Method method=TestGenericType.class.getDeclaredMethod("test01", Map.class, List.class);
         Type[] genericParameterTypes = method.getGenericParameterTypes();
         for (Type genericParameterType : genericParameterTypes) {
             System.out.println("#--------"+genericParameterType);
             if(genericParameterType instanceof ParameterizedType){
                 Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                 for (Type actualTypeArgument : actualTypeArguments) {
                     System.out.println(actualTypeArgument);
                 }
             }
         }
         method=TestGenericType.class.getDeclaredMethod("test02", null);
         Type genericReturnType = method.getGenericReturnType();
         System.out.println("#--------"+genericReturnType);
         if(genericReturnType instanceof ParameterizedType){
             Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
             for (Type actualTypeArgument : actualTypeArguments) {
                 System.out.println(actualTypeArgument);
             }
         }
     }
    }
    #//返回结果
    #--------java.util.Map<java.lang.String, snow.dong.reflection.User>
    class java.lang.String
    class snow.dong.reflection.User
    #--------java.util.List<snow.dong.reflection.User>
    class snow.dong.reflection.User
    #--------java.util.Map<java.lang.String, snow.dong.reflection.User>
    class java.lang.String
    class snow.dong.reflection.User

2.4反射操作注解

利用注解和反射完成类和表结构的映射关系,代码如下:

package snow.dong.reflection;
import java.lang.annotation.*;
import java.lang.reflect.Field;
public class TestAnnotation {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1=Class.forName("snow.dong.reflection.Student");
        //通过反射获得注解
        Annotation[] annotations=c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
        //获得注解的value值
        Tabledong tabledong= (Tabledong) c1.getAnnotation(Tabledong.class);
        String value=tabledong.value();
        System.out.println(value);
        //获得类指定的注解
        Field f=c1.getDeclaredField("name");
        Fileddong fileddong=f.getAnnotation(Fileddong.class);
        System.out.println(fileddong.columnName());
        System.out.println(fileddong.type());
        System.out.println(fileddong.length());
    }
}
@Tabledong("db_student")
class Student{
    @Fileddong(columnName = "db_id",type = "int",length = 10)
    private int id;
    @Fileddong(columnName = "db_age",type = "int",length = 10)
    private int age;
    @Fileddong(columnName = "db_name",type = "varchar",length = 3)
    private String name;
    public Student() {
    }
    public Student(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 "Student{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}
//类名的注解
@Target(ElementType.TYPE)//表示在类上面的注解
@Retention(RetentionPolicy.RUNTIME)
@interface Tabledong{
    String value();
}
//属性的注解
@Target(ElementType.FIELD)//表示在类上面的注解
@Retention(RetentionPolicy.RUNTIME)
@interface Fileddong{
    String columnName();
    String type();
    int length();
}
#//运行结果
@snow.dong.reflection.Tabledong(value=db_student)
db_student
db_name
varchar
3