Java基础-反射
标签:Java基础

反射(Reflect)

1. Class 类

在面向对象的世界里,万事万物皆对象

Java语言中,有两样东西不是面向对象的,一个是普通的数据类型(int,double)等,但它有相关的包装类来弥补了,还有就是静态的成员,它是属于类的。类是对象,它是java.lang.Class类的实例对象 There is a class named Class

任何一个类都是Class的实例对象,这个实例对象有三种表示方式:

  1. 第一种表示方式--->实际在告诉我们任何一个类都有一个隐含的静态成员变量class
Class c1=Foo.class;
  1. 第二种表达方式,已经知道该类的对象通过getClass方法获得
 Class c2=foo1.getClass();
  1. 第三种表达方式
Class.forName("类的全称");

c1,c2,c3表示了Foo类的类类型(class type), 类也是对象,是Class类的实例对象, 这个对象我们称为该类的类类型,一个类只可能是Class类的一个实例对象。所以c1,c2,c3相等。

2. 获的类的方法,成员变量,构造函数

获得方法:

 public static void printClassMessage(Object obj){
//        要获取类的信息,首先要获取类的类类型
        Class c=obj.getClass();
//        获取类的名称
        System.out.println("类的名称是:"+c.getName());
        /**
         * Method类,方法对象
         * 一个成员方法就是一个Method对象
         * getMethods()方法获取的是所有的public的函数,包括父类继承而来的
         * getDeclaredMethods()获取的是所有该类自己声明的方法,不管访问权限
         */
        Method[] ms=c.getMethods();//c.getDeclaredMethods();
        for (int i = 0; i < ms.length; i++) {
//            得到方法的返回值类型的类类型
            Class returnType=ms[i].getReturnType();
            System.out.print(returnType.getName()+" ");
//            得到方法的名称
            System.out.print(ms[i].getName()+"(");
//            获取参数类型--->得到的是参数列表的类型的类类型
            Class[] paramType=ms[i].getParameterTypes();
            for (Class cl :paramType) {
                System.out.print(cl.getName()+", ");
            }
            System.out.println(")");
        }

    }

获得成员变量:

 /**
     * 成员变量也是对象
     * java.lang.reflect.Field
     * Field类封装了关于成员变量的信息
     * getFields() 方法获取的是所有的public的成员变量的信息
     * getDeclaredFields获取的是该类自己声明的成员变量信息
     */
    public static void printField(Object object) {
        Class c=object.getClass();
        Field[] fields=c.getDeclaredFields();
        for (Field field :fields) {
//                得到成员变量的类型的类类型
            Class fieldType=field.getType();
            String typeName=fieldType.getName();
//                得到成员变量的名称
            String fieldName=field.getName();
            System.out.println(typeName+" "+fieldName);
        }
    }

获得构造函数:

public static void printContruction(Object object){
        Class c=object.getClass();
        Constructor[] constructors=c.getDeclaredConstructors();
        for (Constructor constructor :constructors) {
            System.out.print(constructor.getName()+"(");
            Class[] paramType=constructor.getParameterTypes();
            for (Class class1 : paramType) {
                System.out.print(class1.getName()+", ");
            }
            System.out.println(")");
        }

    }

3. 方法的反射

如何获得某个方法:

方法的名称和方法的参数列表才能唯一决定某个方法。

方法反射的操作:

method.invoke(对象,参数列表)

package com.liuyao.reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * Created By liuyao on 2018/5/3 20:33.
 */
public class MethodDemo1 {
    public static void main(String[] args) {
//        要获取print(int,int)方法
//        1. 要获取一个方法就是获取类的信息,获取类的信息首先要获取类类型
//        2. 获取方法,由名称和参数列表来决定
//        3. getMethod获取的是public方法
//        getDeclaredMethod获取的是自己声明的方法
        A a=new A();
        Class c=a.getClass();
        try {

            //第一种方式:
            Method m1=c.getDeclaredMethod("print",int.class,int.class);
            Object o1=m1.invoke(a,10,20);

//            第二种方式:
            Method m2=c.getDeclaredMethod("print",new Class[]{String.class,String.class});
            Object o2=m2.invoke(a,new Object[]{"A","B"});
//           第三种方式:
            Method m3=c.getDeclaredMethod("print");
            m3.invoke(a);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

}

class A{
    public void print(int a,int b){
        System.out.println(a+b);
    }

    public void print(String a,String b){
        System.out.println(a.toUpperCase()+" "+b.toLowerCase());
    }

    public void print(){
        System.out.println("NULL");
    }
}

4. 集合的反射

package com.liuyao.reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

/**
 * Created By liuyao on 2018/5/3 21:01.
 */
public class ClassDemo4 {
    public static void main(String[] args) {
        ArrayList list=new ArrayList();
        ArrayList<String> list1=new ArrayList<>();
        list1.add("hello");
//        list1.add(20);  错误的

        Class c1=list.getClass();
        Class c2=list1.getClass();
        System.out.println(c1==c2);
//        反射的操作都是编译之后的操作
//        c1==c2 结果返回true,说明编译之后的泛型是去泛型化的,即编译之后没有了泛型
//        Java中的集合泛型,是为防止错误输入的,只在编译阶段有效,绕过编译就无效了。
//        验证:我们可以通过方法的反射来操作,绕过编译

        try {
            Method method=c1.getDeclaredMethod("add",Object.class);
            method.invoke(list1,100);

            System.out.println(list1.size());


//            此时不能通过foreach遍历,将会出现类型不匹配
//            for (String s:list1) {
//                System.out.println(s);
//            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

说明编译之后的泛型是去泛型化的,即编译之后没有了泛型,Java中的集合泛型,是为防止错误输入的,只在编译阶段有效,绕过编译就无效了。

5. 访问私有属性和方法

访问私有属性和方法,在使用前要通过AccessibleObject类(Constructor、 Field和Method类的基类)中的setAccessible()方法来抑制Java访问权限的检查

比如:

// 获取Method对象
        Method method = classType.getDeclaredMethod("sayHello",new Class[] { String.class });
        method.setAccessible(true); // 抑制Java的访问控制检查
  • 5 min read

CONTRIBUTORS


  • 5 min read