Java反射

反射

反射概述:

​ Java中的反射(Reflection)机制就是指在运行过程中,对于任意一个类,都能够知道这个类的属性和方法,对于任意一个对象都能够调用它的任意一个属性和方法,这种动态获取信息和动态调用方法的功能被称之为Java语言的反射机制。

反射的使用:

获取Class对象的三种方式

一、使用 Class.forName() 的静态方法

Class.forName(String className)方法可以通过类或接口的名称(一个字符串或完全限定名)来获取对应的Class对象

Class<?> cls = Class.forName("java.lang.String");

二、直接获取某个类的Class(最安全,性能最好)

Class<String> cls = String.class;

三、调用某个对象的getClass()方法

Class<String> cls = str.getClass();

判断是否为某个类的实例

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

public native boolean isInstance(Object obj);

创建实例

通过反射来创建对象有两种方式

使用Class对象的newInstance()方法

Class<?> c = String.class;
Object str = c.newInstance();

通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法

// 获取String所对应的Class对象
Class<?> c = String.class;
// 获取String类带一个String参数的构造器
Constructor constructor = c.getConstructor(String.class);
// 根据构造器创建实例
Object obj = constructor.newInstance("23333");
System.out.println(obj);

获取方法

获取某个Class对象的方法集合,主要有以下几个方法:

  • getDeclaredMethods()方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
public Method[] getDeclaredMethods() throws SecurityException
  • getMethods()方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。
public Method[] getMethods() throws SecurityException
  • getMethod()方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象。
public Method getMethod(String name, Class<?>... parameterTypes)

测试:

public class Test {
    public static void main(String[] args) throws Exception {
        //通过Class.forName来创建反射对象
        Class<?> user = Class.forName("com.ysh.fanshe.User");
        //通过反射对象来获取User实例,调用了无参构造
        Object o = user.newInstance();
        //返回这个类所有的public方法,包括继承类的
        Method[] methods = user.getMethods();
        for (Method method : methods) {
            //获取方法名
            System.out.println(method.getName());
        }
        System.out.println("===========================");
        //获取User类所有方法,不包括继承的
        Method[] declaredMethods = user.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            //遍历所有方法名
            System.out.println(declaredMethod.getName());
        }
        System.out.println("==========================");
        //根据方法名(第一个参数)获取,后面的参数为参数列表中类的Class对象,是一个可变参数
        Method add = user.getMethod("add", String.class);
        System.out.println(add.getName());
		//获取私有的
        Method update = user.getDeclaredMethod("update");
        System.out.println(update.getName());
    }
}
class User{
    public String add(String name){
        System.out.println("添加成功");
        return name;
    }
    public void delete(){
        System.out.println("删除方法");
    }
    private void update(){
        System.out.println("我是私有方法");
    }
}

获取类变量

获取的方法同Method相似,主要是这几个方法,在此不再赘述:

  • Field getField(String name)访问公有的成员变量。
  • Field[] getDeclaredFields():所有已声明的成员变量。但不能得到其父类的成员变量。
  • Field[] getFields()Field[] getDeclaredFields()用法同上。
		//获取属性,私有的,其他同理
        Field name = user.getDeclaredField("name");
        //getType获取该属性的类型,返回的是一个Class对象
        //SimpleName获取一个类的简单名称,不带包路径的名
        System.out.println(name.getType().getSimpleName());
        //getName获取该属性的变量名,返回String
        System.out.println(name.getName());
        //getModifiers获取属性的修饰符,通过Modifier类的静态方法toString来转换为字符串
        String s = Modifier.toString(name.getModifiers());
        System.out.println(s);

调用方法(重点)

当我们从类中获取了一个方法后,我们就可以用invoke()方法来调用这个方法。invoke方法的原型为:

public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException,
 InvocationTargetException
	   //通过Class.forName来创建反射对象
        Class<?> user = Class.forName("com.ysh.fanshe.User");
        //通过反射对象来获取User实例,调用了无参构造
        Object o = user.newInstance();
	   //根据方法名(第一个参数)获取,后面的参数为参数列表中类的Class对象,是一个可变参数
        Method add = user.getMethod("add", String.class);
        System.out.println(add.getName());
        //invoke调用方法,第一个参数为对象,第二个参数为传入的参数,返回值为该方法的返回值
        Object a = add.invoke(o, "易水寒");
        System.out.println(a);

利用反射创建数组

数组在Java里是比较特殊的一种类型,它可以赋值给一个Object Reference。下面我们看一看利用反射创建数组的例子:

public static void TestArray() throws ClassNotFoundException {
    // 使用`java.lang.reflect.Array`反射创建长度为25的字符串数组.
    Class<?> cls = Class.forName("java.lang.String");
    Object array = Array.newInstance(cls, 25);
    // 往数组里添加内容
    Array.set(array,0, "hello");
    Array.set(array,1, "Java");
    Array.set(array,2, "Go");
    Array.set(array,3, "Scala");
    Array.set(array,4, "Clojure");
    // 获取某一项的内容
    System.out.println(Array.get(array, 3));
}

使用反射获取信息

​ Class类提供了大量的实例方法来获取该Class对象所对应的详细信息,Class类大致包含如下方法,其中每个方法都包含多个重载版本,因此我们只是做简单的介绍,详细请参考JDK文档

1、获取类内信息

  • 构造器: Constructor<T> getConstructor(Class<?>... parameterTypes)
  • 包含的方法: Method getMethod(String name, Class<?>... parameterTypes)
  • 包含的属性: Field getField(String name)
  • 包含的Annotation: <A extends Annotation> A getAnnotation(Class<A> annotationClass)
  • 内部类: Class<?>[] getDeclaredClasses()
  • 外部类: Class<?> getDeclaringClass()
  • 所实现的接口: Class<?>[] getInterfaces()
  • 修饰符: int getModifiers()
  • 所在包: Package getPackage()
  • 类名: String getName()
  • 简称: String getSimpleName()

2、判断类本身信息的方法

  • 是否注解类型: boolean isAnnotation()
  • 是否使用了该Annotation修饰: boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
  • 是否匿名类: boolean isAnonymousClass()
  • 是否数组: boolean isArray()
  • 是否枚举: boolean isEnum()
  • 是否原始类型: boolean isPrimitive()
  • 是否接口: boolean isInterface()
  • obj是否是该Class的实例: boolean isInstance(Object obj)

3、使用反射获取泛型信息

​ 为了通过反射操作泛型以迎合实际开发的需要, Java新增了java.lang.reflect.ParameterizedTypejava.lang.reflect.GenericArrayTypejava.lang.reflect.TypeVariablejava.lang.reflect.WildcardType几种类型来代表不能归一到Class类型但是又和原始类型同样重要的类型。

  • ParameterizedType: 一种参数化类型, 比如Collection
  • GenericArrayType: 一种元素类型是参数化类型或者类型变量的数组类型
  • TypeVariable: 各种类型变量的公共接口
  • WildcardType: 一种通配符类型表达式, 如?? extends Number? super Integer

代码示例:

public class Client {
 
    private Map<String, Object> objectMap;
 
    public void test(Map<String, User> map, String string) {
    }
 
    public Map<User, Bean> test() {
        return null;
    }
 
    /**
     * 测试属性类型
     *
     * @throws NoSuchFieldException
     */
    @Test
    public void testFieldType() throws NoSuchFieldException {
        Field field = Client.class.getDeclaredField("objectMap");
        Type gType = field.getGenericType();
        // 打印type与generic type的区别
        System.out.println(field.getType());
        System.out.println(gType);
        System.out.println("**************");
        if (gType instanceof ParameterizedType) {
            ParameterizedType pType = (ParameterizedType) gType;
            Type[] types = pType.getActualTypeArguments();
            for (Type type : types) {
                System.out.println(type.toString());
            }
        }
    }
 
    /**
     * 测试参数类型
     *
     * @throws NoSuchMethodException
     */
    @Test
    public void testParamType() throws NoSuchMethodException {
        Method testMethod = Client.class.getMethod("test", Map.class, String.class);
        Type[] parameterTypes = testMethod.getGenericParameterTypes();
        for (Type type : parameterTypes) {
            System.out.println("type -> " + type);
            if (type instanceof ParameterizedType) {
                Type[] actualTypes = ((ParameterizedType) type).getActualTypeArguments();
                for (Type actualType : actualTypes) {
                    System.out.println("\tactual type -> " + actualType);
                }
            }
        }
    }
 
    /**
     * 测试返回值类型
     *
     * @throws NoSuchMethodException
     */
    @Test
    public void testReturnType() throws NoSuchMethodException {
        Method testMethod = Client.class.getMethod("test");
        Type returnType = testMethod.getGenericReturnType();
        System.out.println("return type -> " + returnType);
        if (returnType instanceof ParameterizedType) {
            Type[] actualTypes = ((ParameterizedType) returnType).getActualTypeArguments();
            for (Type actualType : actualTypes) {
                System.out.println("\tactual type -> " + actualType);
            }
        }
    }
}
posted @ 2021-02-04 15:01  煜煜大宝贝呀  阅读(85)  评论(0编辑  收藏  举报