一、简介

Java程序中的对象有两种类型:编译时类型和运行时类型(Animal cat = new Cat();),通过反射机制可以在程序运行时获取对象和类的真实信息。

二、Class对象

1、获取Class对象

每个类被加载后,在JVM中会生成一个对应的Class对象,通过该对象就可以访问到JVM中的这个类。可以通过以下三种方式获取:

  • 使用java.lang.Class.forName(String)

    参数为类的全路径名称,例如:Class.forName("com.strikeback.model.Animal")

  • 使用类的class属性

    例如:Animal.class。此种方法与前一种相比,有以下优势:

    • 在编译阶段就能检查用到的Class是否存在,代码更健壮。

    • 无需调用方法,性能更好。

  • 调用(对象的)java.lang.Object.getClass()方法

    例如:

      Animal animal = new Animal();
      animal.getClass();
    

2、Class对象常用方法

样例使用的Animal类:

package com.strikeback.model;

public class Animal implements Cloneable, Comparable<Animal>{

	private int age;
	private String name;
	public float weight;
	
	public Animal(){}
	
	private Animal(int age){
		this.age = age;
	}
	public Animal(String name){
		this.name = name;
	}
	
	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;
	}

	public void bark(){}
	
	public void bark(String voice){
		System.out.println("Voice:" + voice);
	}
	
	private void eat(Animal food){}

	private void introduce(){
		System.out.println("My name is " + name);
	}

	@Override
	public int compareTo(Animal o) {
		return 0;
	}
	
	public class Cat extends Animal{
		
	}
}
  • getName()&getSimpleName()

    getName():返回Class对象所对应类的名称

    getSimpleName():返回Class对象所对应类的简称

      Class<?> clazz = Animal.class;
      System.out.println(clazz.getName());
      System.out.println(clazz.getSimpleName());
    

    输出:

      com.strikeback.model.Animal
      Animal
    
  • String getCanonicalName()

    返回Java语言规范定义的基础类的规范名称。 如果基础类没有规范名称(即,如果它是本地或匿名类或其组件类型不具有规范名称的数组),则返回null。

      Class<?> clazz = Animal.class;
      System.out.println(clazz.getCanonicalName());
    
      Class<Cat> catClass = Animal.Cat.class;
      System.out.println(catClass.getName());
      System.out.println(catClass.getSimpleName());
      System.out.println(catClass.getCanonicalName());
    
      Class<? extends Object[]> arrClass = new Object[0].getClass();
      System.out.println(arrClass.getName());
      System.out.println(arrClass.getSimpleName());
      System.out.println(arrClass.getCanonicalName());
    

    输出:

      com.strikeback.model.Animal
    
      com.strikeback.model.Animal$Cat
      Cat
      com.strikeback.model.Animal.Cat
    
      [Ljava.lang.Object;
      Object[]
      java.lang.Object[]
    
  • Package getPackage()

    获取Class对象所对应类的包名

      Class<?> clazz = Animal.class;
      System.out.println(clazz.getPackage());//package com.strikeback.model
    
  • Constructor<T> getConstructor(Class<?>... parameterTypes)

    获取Class对象所表示类的指定参数的public构器器。

      Class<?> clazz = Animal.class;
      //无参构造器
      System.out.println(clazz.getConstructor());
      //有参构造器
      System.out.println(clazz.getConstructor(String.class));
      //获取私有构造器
      System.out.println(clazz.getConstructor(Integer.class));
    

    输出:

      public com.strikeback.model.Animal()
      public com.strikeback.model.Animal(java.lang.String)
      java.lang.NoSuchMethodException: com.strikeback.model.Animal.<init>(int)
    
  • Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

    获取指定的构造器,不受访问级别限制

      Class<?> clazz = Animal.class;
      //获取私有构造器
      System.out.println(clazz.getDeclaredConstructor(int.class));
    

    输出:private com.strikeback.model.Animal(int)

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

    获取指定的public方法

      Class<?> clazz = Animal.class;
      //无参方法
      System.out.println(clazz.getMethod("bark"));
      //有参方法
      System.out.println(clazz.getMethod("bark", String.class));
      //获取私有方法
      System.out.println(clazz.getMethod("eat", Animal.class));
    

    输出:

      public void com.strikeback.model.Animal.bark()
      public void com.strikeback.model.Animal.bark(java.lang.String)
      java.lang.NoSuchMethodException: com.strikeback.model.Animal.eat(com.strikeback.model.Animal)
    
  • Method getDeclaredMethod(String name, Class<?>... parameterTypes)

    获取指定的方法,不受访问级别的限制

      Class<?> clazz = Animal.class;
      //获取私有方法
      System.out.println(clazz.getDeclaredMethod("eat", Animal.class));
    
  • Field getField(String name)

    获取指定的public属性

      Class<?> clazz = Animal.class;
      //获取public属性
      System.out.println(clazz.getField("weight"));
      //获取private属性
      System.out.println(clazz.getField("name"));
    

    输出:

      public float com.strikeback.model.Animal.weight
      java.lang.NoSuchFieldException: name
    
  • Field getDeclaredField(String name)

    获取指定的属性,不受访问级别限制

      Class<?> clazz = Animal.class;
      //获取private属性
      System.out.println(clazz.getDeclaredField("name"));
    

    输出:private java.lang.String com.strikeback.model.Animal.name

  • Class<?>[] getInterfaces()

    获取该Class对象所对应的类实现的所有接口

      Class<?> clazz = Animal.class;
      for(Class<?> itf : clazz.getInterfaces()){
          System.out.println(itf.getName());
      }
    

    输出:

      java.lang.Cloneable
      java.lang.Comparable
    
  • Class<?>[] getDeclaredClasses()

    返回该Class对象所对应的类中包含的所有内部类

      Class<?> clazz = Animal.class;
      for(Class<?> cls : clazz.getDeclaredClasses()){
          System.out.println(cls.getName());
      }
    

    输出:com.strikeback.model.Animal$Cat

  • Class<?> getDeclaringClass()

    返回该Class对象所对应的类所在的外部类

      Class<Cat> catClass = Animal.Cat.class;
      System.out.println(catClass.getDeclaringClass().getName());
    

    输出:com.strikeback.model.Animal

  • boolean isInstance(Object obj)

    判断obj是否是此Class对象的实例,与instanceof操作符功能类似

  • boolean isAnnotation()

    判断该Class对象所对应的类是否是接口

  • boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

    判断该Class对象上是否存在annotationClass的注解

  • boolean isAnonymousClass()

    判断该Class对象是否是一个匿名类

  • boolean isArray()

    判断该Class对象是否是数组

  • boolean isEnum()

    判断该Class对象是否是枚举

  • boolean isInterface()

    判断该Class对象是否是接口

……

三、使用反射

1、创建对象

  • 使用Class对象的newInstance()方法

    此种方式要求要创建的类必须有默认的构造器,执行此方法时实际上是使用默认构造器来创建实例的。

      Class<?> clazz = Class.forName("com.strikeback.model.Animal");
      Animal animal = (Animal) clazz.newInstance();
    
  • 使用Constructor对象的newInstance()方法

    先使用Class对象获取指定的Constructor对象,然后调用Constructor对象的newInstance()方法创建实例。使用这种方式可以调用指定构造器。

      //获取Class对象
      Class<?> clazz = Class.forName("com.strikeback.model.Animal");
      //获取指定构造器
      Constructor<?> constructor = clazz.getDeclaredConstructor(String.class);
      //创建对象
      Animal animal = (Animal) constructor.newInstance("Coco");
      System.out.println(animal.getName());//Coco
    

一般只有当程序中需要动态创建某个类的对象时才会使用反射,因为通过反射创建对象时性能会低一点;通常在开发一些通用性广的框架、平台时会大量使用反射。

2、调用方法

可以通过Class对象获取指定的Method对象,之后就可以通过Method对象的invoke方法来调用该方法。

Object invoke(Object obj, Object... args)
Class<?> clazz = Class.forName("com.strikeback.model.Animal");
Animal animal = (Animal) clazz.newInstance();
Method method = clazz.getMethod("bark", String.class);
method.invoke(animal, "miemie");

输出:Voice:miemie

通过Method对象的invoke()方法来调用方法时必须要有访问该方法的权限;如果需要调用某个对象的private方法,可以先调用setAccessible(true)方法取消Java对访问权限的检查。

Class<?> clazz = Class.forName("com.strikeback.model.Animal");
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class);
Animal animal = (Animal) constructor.newInstance("Coco");
Method method = clazz.getDeclaredMethod("introduce");
method.setAccessible(true);
method.invoke(animal);

输出:My name is Coco

3、访问属性

可以通过Class对象获取指定的Field对象,之后可以通过setget方法设置或获取对象的该属性值:

  • set(Object obj, Object value)setXxx(Object obj, Xxx value)

    obj对象设置此属性的值为value

  • get(Object obj)getXxx(Object obj)

    获取obj对象中该属性的值

Class<?> clazz = Class.forName("com.strikeback.model.Animal");
Animal animal = (Animal) clazz.newInstance();
Field field = clazz.getDeclaredField("name");
//取消访问权限的检查
field.setAccessible(true);
field.set(animal, "Tom");
System.out.println(field.get(animal));//Tom

field = clazz.getDeclaredField("age");
//取消访问权限的检查
field.setAccessible(true);
field.setInt(animal, 5);
System.out.println(field.getInt(animal));//5

4、处理数组

可以使用Array类来动态创建数组、操作数组元素。

  • 创建数组

    • static Object newInstance(Class<?> componentType, int length)

      创建指定类型和长度的数组。

        //创建一维数组
        Object array = Array.newInstance(String.class, 3);
        System.out.println(Array.getLength(array));//3
      
    • static Object newInstance(Class<?> componentType, int... dimensions)

      创建指定类型和维度的数组。

        //创建二维数组
        Object array = Array.newInstance(int.class, 2, 3);
        System.out.println(Array.getLength(array));//2
        System.out.println(Array.getLength(Array.get(array, 0)));//3
      
  • 操作数组元素

    • static native Object get(Object array, int index)Xxx getXxx(Object array, int index)

      获取数组array中指定位置的元素。

    • static native void set(Object array, int index, Object value)void setXxx(Object array, int index, Xxx value)

      设置数组array中index位置的值为value。

      Object array = Array.newInstance(int.class, 2, 3);
      Array.set(array, 0, new int[]{1, 2, 3});
      Array.set(array, 1, new int[]{4, 5, 6});
      Object arr = Array.get(array, 0);
      System.out.println(Array.getInt(arr, 0));//1
      System.out.println(Array.getInt(arr, 1));//2
      System.out.println(Array.getInt(arr, 2));//3
    
参考资料: