Java Class类和反射

摩森特沃 2021年04月15日 774次浏览

Class类

Class类是用来保存Java虚拟机(JVM)运行时类型信息的类,其内部封装了一个对象或接口运行时的方法、变量、接口、类名、类修饰符等信息。

Class类没有公共构造方法。Class对象是在加载类时由JVM以及通过调用类加载器中的defineClass方法自动构造的,因此不能显式地声明一个Class对象。JVM为每个类(型)管理一个独一无二的Class对象。当程序运行时,JVM首先检查所要加载的类对应的Class对象是否已经加载,如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。

基本的Java类型(boolean、byte、char、short、int、long、float和double)和关键字void也都对应一个Class对象。每个数组属于被映射为Class对象的一个类,所有具有相同元素类型和维数的数组都共享该Class对象(详见扩展内容章节)。

一般某个类的Class对象被载入内存,它就用来创建这个类的所有对象。

Class类的常用方法

获取Class对象的方法

  • .class
Class userClass = User.class;
  • Class.forName()

Class.forName()方式适用于运行时动态获取Class对象,只需将类的完全限定名作为forName方法的参数即可获取

try{
    Class userClass= Class.forName("com.most.User");
}
catch(ClassNotFoundException e){
    System.out.println("找不到User类");
}
  • 实例对象的getClass方法
User user = new User();
Class userClass = user.getClass();

获取类的信息

  • 获取类名
  • getName():获取包含包信息的类名
  • getTypeName():获取类的类型名称,包含包信息
    • 除数组外,getTypeName()和getName()获取的值相同
  • getSimpleName():只是获取类名,不包含包信息,是对getTypeName()获取的字符串的截取
package com.most;

public class User {
    public static void main(String[] args) {
        Class userClass = User.class;
	System.out.println("------------------普通类类名------------------");
        System.out.println(userClass.getName());
        System.out.println(userClass.getTypeName());
        System.out.println(userClass.getSimpleName());
        Class petClass = Pet.class;
	System.out.println("------------------内部类类名------------------");
        System.out.println(petClass.getName());
        System.out.println(petClass.getTypeName());
        System.out.println(petClass.getSimpleName());
        Pet[] pets = new Pet[2];
        Class<? extends Pet[]> petsClass = pets.getClass();
	System.out.println("------------------数组类类名------------------");
        System.out.println(petsClass.getName());
        System.out.println(petsClass.getTypeName());
        System.out.println(petsClass.getSimpleName());
    }

    private static class Pet {

    }
}

输出结果

------------------普通类类名------------------
com.most.User
com.most.User
User
------------------内部类类名------------------
com.most.User1$Pet
com.most.User1$Pet
Pet
------------------数组类类名------------------
[Lcom.most.User1$Pet;
com.most.User1$Pet[]
Pet[]
  • 获取类修饰符
  • 类修饰符有public、private等类型,getModifiers()可以获取一个类的修饰符,但是返回的结果是int,结合Modifier提供的方法,就可以确认修饰符的类型
package com.most;

import java.lang.reflect.Modifier;

public class User {
    public static void main(String[] args) {
        Class userClass = User.class;
	System.out.println("------------------普通类类修饰符------------------");
        System.out.println(userClass.getModifiers());
        System.out.println(Modifier.isPublic(userClass.getModifiers()));

        Class petClass = Pet.class;
	System.out.println("------------------内部类类修饰符------------------");
        System.out.println(petClass.getModifiers());
        System.out.println(Modifier.isPublic(petClass.getModifiers()));
    }

    private class Pet{

    }
}

输出结果

------------------普通类类修饰符------------------
1
true
------------------内部类类修饰符------------------
2
false

获取包信息

  • getPackage():获取包信息
package com.most;

public class User {
    public static void main(String[] args) {
        Class petClass = Pet.class;
	System.out.println("------------------包信息------------------");
        System.out.println(petClass.getPackage());
    }

    private class Pet {

    }
}

输出结果

------------------包信息------------------
package com.most

获取父类的Class对象

  • getSuperclass():返回直接继承的父类,不包含泛型参数
  • getGenericSuperclass():返回直接继承的父类,包含泛型参数
package com.most;

import java.lang.reflect.Type;
import java.util.List;

public class User {
    public static void main(String[] args) {
        Class petClass = Pet.class;
        Class superclass = petClass.getSuperclass();
        Type genericSuperclass = petClass.getGenericSuperclass();
        System.out.println("------------------父类信息------------------");
	System.out.println(superclass.getName());
        System.out.println(superclass.getTypeName());
        System.out.println(genericSuperclass.getTypeName());
    }

    private static class Pet extends Animal<String> {

    }

    private static class Animal<T> {
        private List<T> foods;
    }
}

输出结果

------------------父类信息------------------
com.most.User$Animal
com.most.User$Animal
com.most.User$Animal<java.lang.String>

获取接口信息

  • getInterfaces():返回直接实现的接口数组,不包含泛型参数
  • getGenericInterfaces():返回直接实现的接口数组,包含泛型参数
import java.lang.reflect.Type;
import java.util.List;

public class User {
    public static void main(String[] args) {
        Class petClass = Pet.class;
        Class[] interfaces = petClass.getInterfaces();
        Type[] genericInterfaces = petClass.getGenericInterfaces();
	System.out.println("------------------接口信息------------------");
        for (Class anInterface : interfaces) {
            System.out.println(anInterface.getTypeName());
        }
        System.out.println("------------------泛型接口信息------------------");
        for (Type genericInterface : genericInterfaces) {
            System.out.println(genericInterface.getTypeName());
        }
    }

    private static class Pet extends Animal<String> implements Flyable<Integer> {

    }

    private static class Animal<T> {
        private List<T> foods;
    }

    private interface Flyable<T> {

    }
}

输出结果

------------------接口信息------------------
com.most.User$Flyable
------------------泛型接口信息------------------
com.most.User$Flyable<java.lang.Integer>

获取构造函数信息

  • 获取构造函数
  • getConstructor(Class<?>... types):获取声明为public的参数为传入参数列表的构造函数
  • getDeclaredConstructor(Class<?>... types):获取所有声明的参数为传入参数列表的构造函数,和修饰符无关
  • getConstructors():获取所有声明为public的构造函数
  • getDeclaredConstructors():获取所有声明的构造函数,和修饰符无关
  • 获取构造函数的参数
  • getParameterTypes():获取指定构造函数的参数类型数组
package com.most;

import java.lang.reflect.Constructor;

public class User {
    public static void main(String[] args) throws Exception {
        Class petClass = Pet.class;
        Constructor[] constructors = petClass.getConstructors();
        Constructor[] declaredConstructors = petClass.getDeclaredConstructors();
        Constructor specific = petClass.getConstructor(String.class);
        System.out.println("------------------所有public构造函数------------------");
        for (Constructor constructor : constructors) {
            System.out.println(constructor.getName());
        }
        System.out.println("------------------所有声明的构造函数------------------");
        for (Constructor constructor : declaredConstructors) {
            System.out.println(constructor.getName());
        }
        System.out.println("------------------指定参数的构造函数------------------");
        System.out.println(specific.getName());
        System.out.println("------------------指定参数的构造函数的参数------------------");
        Class[] parameterTypes = specific.getParameterTypes();
        for (Class parameterType : parameterTypes) {
            System.out.println(parameterType.getName());
        }
    }

    private static class Pet {
        private String name;

        private Pet() {
        }

        public Pet(String name) {
            this.name = name;
        }
    }
}

输出结果

------------------所有public构造函数------------------
com.most.User$Pet
------------------所有声明的构造函数------------------
com.most.User$Pet
com.most.User$Pet
------------------指定参数的构造函数------------------
com.most.User$Pet
------------------指定参数的构造函数的参数------------------
java.lang.String

获取方法信息(包括成员方法和类方法)

  • 获取方法
  • getMethod(Class<?>... types):获取声明为public的参数为传入参数列表的方法
  • getDeclaredMethod(Class<?>... types):获取声明的参数为传入参数列表的方法,和修饰符无关
  • getMethods():获取到所有public修饰的方法,返回Method[]数组
  • getDeclaredMethods():获取所有声明的方法,和修饰符无关
  • 注意:无参的getMethods()和getDeclaredMethods()都只能获取到类声明的方法,不能获取到继承父类的方法,带参的getMethod()和getDeclaredMethod()可以获取到继承自父类的方法
  • 获取方法参数
  • getParameterTypes():获取指定方法的参数类型数组
  • 获取方法返回类型
  • getReturnType():获取指定方法的返回类型
package com.most;

import java.lang.reflect.Method;

public class User {
    public static void main(String[] args) throws Exception {
        Class petClass = Pet.class;
        System.out.println("------------------所有声明方法------------------");
        Method[] methods = petClass.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method.getName());
        }
        Method specific = petClass.getMethod("sayHi", String.class);
        System.out.println("------------------指定方法------------------");
        System.out.println(specific.getName());
        System.out.println("------------------参数类型------------------");
        Class<?>[] parameterTypes = specific.getParameterTypes();
        for (Class<?> parameterType : parameterTypes) {
            System.out.println(parameterType.getName());
        }
        System.out.println("------------------返回类型------------------");
        Class<?> returnType = specific.getReturnType();
        System.out.println(returnType.getName());
    }

    private static class Pet extends Animal {
        private String name;

        public void showName() {
            System.out.println(name);
        }

        public static void sayHello() {
            System.out.println("Hello!");
        }

        @Override
        public String toString() {
            return "Pet[name=" + name + "]";
        }
    }

    private static class Animal {

        public String sayHi(String name) {
            return "Hi! " + name;
        }
    }
}

输出结果

------------------所有声明方法------------------
toString
showName
sayHello
------------------指定方法------------------
sayHi
------------------参数类型------------------
java.lang.String
------------------返回类型------------------
java.lang.String

获取变量信息(包括成员变量和类变量)

  • 获取变量
  • getField(String type):获取声明为public的参数为传入参数列表的方法
  • getDeclaredField(String type):获取声明的参数为传入参数列表的方法,和修饰符无关
  • getFields():获取到所有public修饰的变量,返回Field[]数组
  • getDeclaredFields():获取所有声明的变量,和修饰符无关
  • 注意:以上四种方法都不能获取到继承自父类的变量
  • 获取变量类型
  • getType():获取变量类型
package com.most;

import java.lang.reflect.Field;

public class User {
    public static void main(String[] args) throws Exception {
        Class petClass = Pet.class;
        System.out.println("------------------所有声明成员变量------------------");
        Field[] fields = petClass.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getName());
        }
        // 以下注释语句会抛出异常
//        Field feet = petClass.getDeclaredField("age");
        System.out.println("------------------指定成员变量------------------");
        System.out.println(petClass.getDeclaredField("name"));
        System.out.println("------------------成员变量数据类型------------------");
        Field name = petClass.getDeclaredField("name");
        Class<?> parameterType = name.getType();
        System.out.println(parameterType.getName());
    }

    public static class Pet extends Animal {
        private static Integer count = 0;
        private String name;
    }

    public static class Animal {
        public Integer age;
    }
}

执行结果

------------------所有声明成员变量------------------
count
name
------------------指定成员变量------------------
private java.lang.String com.most.User$Pet.name
------------------成员变量数据类型------------------
java.lang.String

获取注解信息

  • 以下以获取类注解信息来进行说明,成员变量,方法,参数与此类似
  • getAnnotations():获取类的所有注解信息集合
  • getDeclaredAnnotations():获取类直接声明的注解信息集合
  • getAnnotationsByType(Class type): 获取所有指定类型的注解信息集合
  • getDeclaredAnnotationsByType(Class type):获取所有直接声明的指定类型的注解信息集合
  • getAnnotation(Class type):获取指定类型的注解信息
  • getDeclaredAnnotation(Class type):获取直接声明的指定类型的注解信息
  • isAnnotationPresent(Class type):判断是否具有某个注解,包括通过继承得到的注解

注意:以上获取注解的方法中,带Declare的方法不能获取到通过继承得到的注解,不带Declare的方法可以获取到通过继承得到的注解

获取泛型信息

  • 泛型属性类型
  • getGenericType():获取到类的泛型变量(静态属性或实例属性)信息
  • 泛型方法参数类型
  • getGenericParameterTypes():获取方法的泛型参数信息
  • 泛型方法返回类型
  • getGenericReturnType():获取方法的泛型返回值信息
package com.most;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

public class User {
    public static void main(String[] args) throws Exception {
        System.out.println("------------------泛型属性类型------------------");
        Field field = Pet.class.getDeclaredField("foods");
        Type genericFieldType = field.getGenericType();
        if (genericFieldType instanceof ParameterizedType) {
            ParameterizedType aType = (ParameterizedType) genericFieldType;
            Type[] fieldArgTypes = aType.getActualTypeArguments();
            for (Type fieldArgType : fieldArgTypes) {
                Class fieldArgClass = (Class) fieldArgType;
                System.out.println("genericFieldType = " + fieldArgClass);
            }
        }

        System.out.println("------------------泛型方法参数类型------------------");
        Method setMethod = Pet.class.getMethod("setFoods", List.class);
        Type[] genericParameterTypes = setMethod.getGenericParameterTypes();
        for(Type genericParameterType : genericParameterTypes){
            if(genericParameterType instanceof ParameterizedType){
                ParameterizedType aType = (ParameterizedType) genericParameterType;
                Type[] parameterArgTypes = aType.getActualTypeArguments();
                for(Type parameterArgType : parameterArgTypes){
                    Class parameterArgClass = (Class) parameterArgType;
                    System.out.println("genericFieldType = " + parameterArgClass);
                }
            }
        }

        System.out.println("------------------泛型方法返回类型------------------");
        Method getMethod = Pet.class.getMethod("getFoods");
        Type returnType = getMethod.getGenericReturnType();
        if(returnType instanceof ParameterizedType){
            ParameterizedType type = (ParameterizedType) returnType;
            Type[] typeArguments = type.getActualTypeArguments();
            for(Type typeArgument : typeArguments){
                Class typeArgClass = (Class) typeArgument;
                System.out.println("genericFieldType = " + typeArgClass);
            }
        }
    }

    public static class Pet {
        private List<String> foods;

        public List<String> getFoods() {
            return foods;
        }

        public void setFoods(List<String> foods) {
            this.foods = foods;
        }
    }
}

输出结果

------------------泛型属性类型------------------
genericFieldType = class java.lang.String
------------------泛型方法参数类型------------------
genericFieldType = class java.lang.String
------------------泛型方法返回类型------------------
genericFieldType = class java.lang.String

Java反射

Java反射机制是指在运行状态中,对于任意一个类,通过Class类中的方法,动态访问这个类创建的对象的任意一个方法和属性。

反射的常用方法

初始化对象

  • 获取到类的构造器之后,通过newInstance()方法就可以生成类对象
package com.most;

import java.lang.reflect.Constructor;

public class User {
    public static void main(String[] args) throws Exception {
        Class petClass = Pet.class;
        Constructor specific = petClass.getConstructor(String.class);
        Pet instance = (Pet) specific.newInstance("猪小明");
        System.out.println(instance);
    }

    private static class Pet {
        private String name;

        public Pet() {
        }

        public Pet(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "Pet[name=" + name + "]";
        }
    }
}

输出结果

Pet[name=猪小明]

invoke()方法

package com.most;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class User {
    public static void main(String[] args) throws Exception {
        Class petClass = Pet.class;
	// 如果内部类没有使用static的修饰符,则此处会报错,这种情况下有以下两种方式解决
	// 第一种:使用static修饰
	// 第二种:将内部类移到User类外部
        Constructor constructors = petClass.getDeclaredConstructor();
        Method method = petClass.getMethod("sayHi", String.class);
        System.out.println("------------------方法调用结果------------------");
        System.out.println(method.invoke(constructors.newInstance(), "猪小明"));
    }

    public static class Pet extends Animal {
    }

    public static class Animal {

        public String sayHi(String name) {
            return "Hi! " + name;
        }
    }
}

特别注意:在以上示例中,如果内部类没有static修饰符时,通过getDeclaredConstructor()将获取不到对应的构造函数,需使用static修饰或者将Pet类移到User类之外才能获取到

输出结果

------------------方法调用结果------------------
Hi! 猪小明
  • 使用反射可以在运行时检查和调用类声明的成员方法,可以用来检测某个类是否有getter和setter方法(getter和setter是java bean必须有的方法),getter和setter方法有下面的一些规律:
    • getter方法以get为前缀,无参,有返回值
    • setter方法以set为前缀,有一个参数,返回值可有可无

成员变量赋值和取值

  • 一旦获取到成员变量的Field引用,就可以获取通过get()方法获取变量值,通过set()方法给变量赋值
package com.most;

import java.lang.reflect.Field;

public class User {
    public static void main(String[] args) throws Exception {
        Pet pet = new Pet("猪小明");
        Class petClass = Pet.class;

        Field field = petClass.getDeclaredField("name");
        field.setAccessible(true);
        System.out.println("------------------获取属性的值------------------");
        Object value = field.get(pet);
        System.out.println(value);
        System.out.println("------------------设置属性的值------------------");
        field.set(pet, "猪小强");
        value = field.get(pet);
        System.out.println(value);
    }

    public static class Pet extends Animal {
        private String name;

        public Pet(String name) {
            this.name = name;
        }
    }

    public static class Animal {

        public String sayHi(String name) {
            return "Hi! " + name;
        }
    }
}

执行结果

------------------获取属性的值------------------
猪小明
------------------设置属性的值------------------
猪小强

访问私有属性和私有方法

  • 使用getDeclaredMethod(Class<?>... types)/getDeclaredField(String type)和getDeclaredMethods()/getDeclaredFields()方法可以获取到私有属性和方法
  • 访问前需要调用各自的setAccessible(true)后才能调用私有方法和访问属性

扩展内容

基础数据类型及其包装类的成员变量赋值和取值常用操作

基础数据类型和包装类型判断

  • 判断一个类是否为基础数据类型
/**
 * 判断是否是基础数据类型,包括int,double,long等8中基础数据类型和void
 */
public static boolean isCommonDataType(Class clazz){
    return clazz.isPrimitive();
}
  • 判断一个类是否为基础类型的包装类型
/**
  * 判断是否是基础数据类型的包装类型
  *
  * @param clz
  * @return
  */                                        
public static boolean isWrapClass(Class clz) {
    try {
        return ((Class) clz.getField("TYPE").get(null)).isPrimitive();
    } catch (Exception e) {
        return false;
    }
}

判断一个类型是否为int或者Integer

  • 前置等式

int.class == Integer.TYPE
int.class.equals(Integer.TYPE)

  • 判断方式
boolean isInteger = c.equals(Integer.class) || c.equals(Integer.TYPE);
boolean isInteger = c.equals(Integer.class) || c.equals(int.class);
boolean isInteger = c == Integer.class || c == Integer.TYPE;
boolean isInteger = c == Integer.class || c == int.class ;

各种数据类型的Class对象表示方法

// 基础类型,以int为例
int
// 基础类型包装类,以Integer为例
class java.lang.Integer
// 普通类,以下Animal为自定义的一个普通类
class com.most.obj.Animal
// 接口,以下Flyable为自定义的一个接口
interface com.most.obj.Flyable
// 基础类型数组,以int[]为例
class [I
// 基础类型包装类数组,以Integer[]为例
class [Ljava.lang.Integer;
// 字符串数组
class [Ljava.lang.String;
// 自定义类数组,以下Animal为自定义的一个普通类
class [Lcom.most.obj.Animal;
// 自定义接口,以下Flyable为自定义的一个接口
class [Lcom.most.obj.Flyable;

引用参考