简单了解Java反射|Java基础

673 阅读6分钟

这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战

前言:目前很多三方框架都有用到反射的知识,本文将要介绍反射常用API。

什么是反射?

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。

优缺点

  • 优点 反射提高了Java程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类;反射是其它一些常用语言,如C、C++、Fortran 或者Pascal等都不具备的
  • 缺点
  1. 性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此Java反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。
  2. 使用反射会模糊程序内部逻辑:程序人员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。

反射则是一开始并不知道我们要初始化的类对象是什么,自然也无法使用 new关键字来创建对象,这时,我们使用JDK提供的反射API进行反射调用。反射就是在运行状态中,对于任意一个类,都能知道,调用且改变这个类的所有属性和方法。

Java反射机制主要提供了以下功能:

  1. 在运行时构造任意一个类的对象
  2. 在运行时获取或者修改任意一个类所具有的成员变量和方法
  3. 在运行时调用任意一个对象的方法(属性)

获取Class对象

  • 通过类名获取:类名.class()
  • 通过对象获取:对象名.getClass()
  • 通过全类名获取: Class.forName(全类名)    classLoader.loadClass(全类名)
public static String str = new String("shixf");
 
    public static void main(String[] args) {
        //1.通过类名获取
        Class<String> stringClass = String.class;
        //2.通过对象获取
        Class<? extends String> strClass = str.getClass();
        //3.通过全类名获取
        try {
            //str.getClass().getName() == java.lang.String
            Class<?> aClass = Class.forName(str.getClass().getName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

判断是否为某个类的实例

一般我们常常用"instanceof" 关键字来判断某个类是否为一个类的实例,同时也可以借助反射中Class对象的  " isInstance()" 方法来判断是否为某个类的实例,它是一个native方法。

public native boolean isInstance(Object obj);
 
//判断是否为某个类的类型
public native boolean isAssignableFrom(Class<?> cls);

反射创建实例对象

  • Class.newInstance()
  • 通过Class获取Constructor对象,在调用Constructor.newInstance()方法来创建实例.
    public static Object createInstance_1(Class cls){
        if(null != cls){
            Object o = null;
            try {
                o = cls.newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return o;
        }
        return null;
    }
 
    public static Object createInstance_2(Class cls){
        //获取String所对应的Class对象
         Class<?> c = String.class;
         //获取String类带一个String参数的构造器
        Constructor constructor = null;
        //根据构造器创建实例
        Object obj = null;
        try {
            constructor = c.getConstructor(String.class);
            obj = constructor.newInstance("23333");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }

获取构造器信息

    /**
     * @param parameterTypes  ...代表可传入该类型的数组,单个实例,或者不传参数,
     * 获得使用特殊的参数类型的public构造函数(包括父类)
     */
    public Constructor<T> getConstructor(Class<?>... parameterTypes)
 
 
    /**
     * 获得类的所有公共构造函数
     */
    public Constructor<?>[] getConstructors()
 
 
    /**
     * 获得使用特定参数类型的构造函数(包括私有)
     */
    public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
 
 
    /**
     * 获得类的所有构造函数
     */
    public Constructor<?>[] getDeclaredConstructors()

获取类的成员变量(字段)信息

/**
 * 获取命名的公共字段(包含父类的public成员变量)
 */
public Field getField(String name)
 
 
/**
 * 获得类的所有公共字段(包含父类的public成员变量)
 */
public Field[] getFields()
 
 
/**
 * 获取类本身的属性成员(包括私有、共有、保护)
 */
public Field getDeclaredField(String name)
 
 
/**
 * 获取类本身的所有属性成员(包括私有、共有、保护)
 */
public Field[] getDeclaredFields()

通过反射调用方法

/**
 * 使用特定参数类型,获得命名的公共方法
 */
public Method getMethod(String name, Class<?>... parameterTypes)
 
 
/**
 * 获得类的所有公共方法
 */
 public Method[] getMethods()
 
 
/**
 * 获得类声明的命名的方法,包括私有的。
 */
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
 
/**
 * 获得类声明的所有命名的方法,包括私有的。
 */
public Method[] getDeclaredMethods()

java.lang.IllegalArgumentException: wrong number of arguments

注意:当我们获取到一个方法时,可以调用invoke()方法来调用这个方法。以及需要注意的 “wrong number of arguments”异常

/**
 * 
 * @param obj
 * @param args 此处需要注意,该参数为Object
 * @return
 */
public Object invoke(Object obj, Object... args)
public class GetMethod {
 
    private static GetMethod getMethod = new GetMethod();
    private static String[] strArray = {"1","2","3"};
 
 
    public static void main(String[] args) {
        getMethod();
    }
 
    public void printStr(String[] str){
        for (String s : str) {
            System.out.println(s);
        }
    }
 
    public static void getMethod(){
        Class<?> cls = getMethod.getClass();
        try {
            Method printStr = cls.getMethod("printStr",String[].class);
            //此处需要强制转换为Object,否则抛出wrong number of arguments
            printStr.invoke(cls.newInstance(),(Object) strArray); 
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
}

利用反射创建数组

数组在 Java 里是比较特殊的一种类型,它可以赋值给一个 Object Reference 其中的 Array 类为 java.lang.reflflect.Array类。我们通过 Array.newInstance() 创建数组对象,它的原型是 :

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

反射获取泛型真实类型

当我们对一个泛型类进行反射时,需要得到泛型中的真是数据类型,来完成如json反序列化的操作。此时需要通过Type体系来完成。Type接口包含了一个实现类(Class)个四个接口,分别是:

TypeVariable: 泛型类型变量,可以泛型上下限等信息。
ParameterizedType 具体的泛型类型,可以获得元数据中泛型签名类型(泛型真是类型)
GenericArrayType 当需要描述的类型是泛型类的数组时,比如List[] , map[],此接口会作为Type的实现。
WildcardType 通配符泛型,获得上下限信息。

TypeVariable

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
 
public class TypeVariableTest<K extends Comparable & Serializable, V> {
 
    private K key;
    private V value;
 
    public static void main(String[] args) {
        try {
            Field fKey = TypeVariableTest.class.getDeclaredField("key");
            Field fValue = TypeVariableTest.class.getDeclaredField("value");
            TypeVariable genericTypeKey = (TypeVariable)fKey.getGenericType();
            TypeVariable genericTypeValue = (TypeVariable)fValue.getGenericType();
 
            System.out.println("genericTypeKey.getName(): " + genericTypeKey.getName());
            System.out.println("genericTypeValue.getName(): " + genericTypeValue.getName());
            System.out.println(genericTypeKey.getGenericDeclaration());//class Reflect.TypeVariableTest
            System.out.println(genericTypeValue.getGenericDeclaration());//class Reflect.TypeVariableTest
            System.out.println("K的上界");
            for (Type bound : genericTypeKey.getBounds()) {
                System.out.println(bound);
                //interface java.lang.Comparable
                //interface java.io.Serializable
            }
            System.out.println("V的上界");
            for (Type bound : genericTypeValue.getBounds()) {
                System.out.println(bound);
                //class java.lang.Object
            }
 
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}

ParameterizedType

public class ParameterizedTypeTest {
 
    Map<String, String> mMap;
 
    public static void main(String[] args) {
        try {
            Field map = ParameterizedTypeTest.class.getDeclaredField("mMap");
 
            System.out.println("get mMap GenericType: " + map.getGenericType());//java.util.Map<java.lang.String, java.lang.String>
 
            ParameterizedType pType = (ParameterizedType)map.getGenericType();
            System.out.println(pType.getRawType());//interface java.util.Map
 
            for (Type actualTypeArgument : pType.getActualTypeArguments()) {
                System.out.println(actualTypeArgument);//俩边class java.lang.String
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}

GenericArrayType

public class GenericArrayTypeTest<T> {
 
    List<String>[] lists;
 
    public static void main(String[] args) {
        try {
            Field field = GenericArrayTypeTest.class.getDeclaredField("lists");
            GenericArrayType genericArrayType = (GenericArrayType)field.getGenericType();
            System.out.println(genericArrayType.getGenericComponentType());//java.util.List<java.lang.String>
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}

WildcardType

public class WildcardTypeTest {
 
    private List<? extends Number> up;//上限
    private List<? super String> down;//下限
 
    public static void main(String[] args) {
        try {
            Field up = WildcardTypeTest.class.getDeclaredField("up");
            Field down = WildcardTypeTest.class.getDeclaredField("down");
            //先拿到泛型类型
            ParameterizedType parameterizedTypeUp = (ParameterizedType)up.getGenericType();
            ParameterizedType parameterizedTypeDown = (ParameterizedType)down.getGenericType();
 
            //在从泛型类型里拿到通配符类型
            WildcardType wildcardTypeUp = (WildcardType)parameterizedTypeUp.getActualTypeArguments()[0];
            WildcardType wildcardTypeDown = (WildcardType)parameterizedTypeDown.getActualTypeArguments()[0];
            System.out.println(wildcardTypeUp.getUpperBounds()[0]);//class java.lang.Number
            System.out.println(wildcardTypeDown.getLowerBounds()[0]);//class java.lang.String
 
            System.out.println(wildcardTypeUp);//? extends java.lang.Number
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}