由浅到深理解java反射

1.基础概念

  class类:

  1.1java是面向对象的,但是在java中存在两种东西不是面向对象的

    一种是普通的数据类型,这也是封装数据类存在的原因.

    二种是静态静态成员.

  1.2所以我们首先要理解,类也是一种对象,类是java.lang.Class类的对象.

  1.3反射的操作其实是执行了编译,获得了类的编译信息,也就是字节码.

  1.4获取类类型可以有三种方式:

DemoGetClassType.java
/**
 * Created by garfield on 2016/10/11.
 * 三种方式获得类类型
 */
public class DemoGetClassType {
    public static void main(String[] args) {
        //car是一个对象
        Car car = new Car();
        //那么既然Car类型为一个对象,那么如何表示Car这个类:
        //查看Class源码发现类是有构造方法的,但是是私有的,只有java虚拟机才能创建Class实例,所以我们要通过其他方法来表示类对象
        //类是Class的实例对象,称之为该类的类类型(class type)

        //可以有三种写法
        //第一种:每一个类都有一个静态成员:class
        Class c1 = Car.class;

        //第二种:利用getClass()方法
        Class c2 = car.getClass();

        //类类型是相等的
        System.out.println(c1 == c2);
        Class c3 = null;
        try {
            c3 = Class.forName("com.learn.reflect.a_GetClassType.Car");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println(c2 == c3);

        //通过类类型可以创建类实例,其实际是通过调用类的无参构造方法,
        try {
            Car car1 = (Car)c1.newInstance();
            car1.run();
        } catch (InstantiationException e) {
            //如果丢失无参构造方法,抛出InstantiationException,也就是实例化异常
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }


    }
}
class Car {

    void run(){
        System.out.println("i can run");
    }
}

2.动态加载类

  2.1在类对象的forName()方法中,是可以表示动态加载类的,此处区分:

    一,在编译时刻加载的类是静态加载类

    二,运行时刻加载类是动态加载类

  2.2利用动态时刻加载可以避免一个功能出错的时候整个系统都不能运行

DemoStaticLoad.java
/**
 * Created by garfield on 2016/10/13.
 */
public class DemoStaticLoad {

    public static void main(String[] args) {
        //new创建对象 是静态加载类,在编译时刻就需要加载任何可能使用到的类
        //这个类当中,由于都是new加载,所以当bus或者jeep有任何一个类不存在时,编译报错
        //此时应当考虑使用动态加载,尤其是当类数量多的时候
        if("bus".equals(args[0])){
            Bus bus = new Bus();
            bus.start();
        }
        if("jeep".equals(args[0])){
            Jeep jeep = new Jeep();
            jeep.start();
        }
    }
}

DemoDynasticLoad.java

/**
 * Created by garfield on 2016/10/13.
 */
public class DemoDynasticLoad {

    public static void main(String[] args) {
        try {
            //动态加载类,在运行时刻编译,只有当运行类不存在时才会报错
            //但凡这种多功能编程,都应该设计成这种不用非重新编译不可的结构
            Class Wheel = Class.forName(args[0]);
            Wheel wheel = (Wheel) Wheel.newInstance();
            wheel.start();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

3.获取类的信息

  3.1普通的数据类型以及void同样存在类类型

  3.2可以获取类的方法,成员变量,构造函数,包名,父类,接口等信息

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

/**
 * Created by garfield on 2016/10/13.
 * 打印出关于输入类型的方法和参数,成员变量,构造函数
 */
public class DemoGetClassInformation {
    public static void main(String[] args) {
        printClassInformation("aa");
        printClassInformation(11);
    }

    static void printClassInformation(Object obj) {
        Class objClass = obj.getClass();
        System.out.println(objClass.getName());
        //获取不带前缀的类名
        System.out.println(objClass.getSimpleName());
        /**
         * 看源码可以看出,getMethods返回了所有的public方法,其中包括继承而来的方法
         * 如果想获取本身的所有方法,用getDeclaredMethods()
         */
        Method[] methods = objClass.getMethods();

        for (Method method : methods) {
            System.out.println("================================");
            System.out.println("方法名称是" + method.getName());
            System.out.println("返回值是:" + method.getReturnType().getName());
            //获取参数列表,返回值是参数的类类型
            Class[] params = method.getParameterTypes();
            for (Class param : params) {
                System.out.println("参数:" + param.getName());
            }
        }
        /**
         * java.lang.reflect.Field封装了成员变量
         * 与方法相仿,getFields()获得的是public变量,如果要获取本身声明的私有变量,采用getDeclaredFields()
         */
        System.out.println("=======打印成员变量=======");
        Field[] fields = objClass.getDeclaredFields();
        for (Field field : fields) {
            System.out.println("成员变量类型:" + field.getType().getName() + ", 成员变量名" + field.getName());
        }

        /**
         * java.lang.Constructor封装构造函数
         * 与上述相仿,getConstructors()得到public构造函数,getDeclaredConstructors()获得声明构造函数
         */
        System.out.println("=======打印构造函数=======");
        Constructor[] constructors = objClass.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println("构造函数名" + constructor.getName());
            Class[] conParas = constructor.getParameterTypes();
            for (Class conPara : conParas) {
                System.out.println("参数:" + conPara.getName());
            }

        }
    }
}

4.方法的反射

  4.1需要理解方法反射的操作 method.invoke(对象,参数列表)

  4.2调用方法是传入的是实例和参数

DemoMethodReflection.java
/**
 * Created by garfield on 2016/10/13.
 * 方法反射实例
 */
public class DemoMethodReflection {
    public static void main(String[] args) {
        //先得到类信息
        MathCount mathCount = new MathCount();
        Class mathCountClass = mathCount.getClass();
        try {
            /**
             * 获得方法,然后进行方法反射,如果有返回值可以获得返回值
             * 注意可变参数两种写法
             * 1.new class[]{int.class,int.class}
             * 2.int.class,int.class
             */
            Method method =  mathCountClass.getMethod("add",new Class[]{int.class,int.class});
            method.invoke(mathCount,new Object[]{22,44});
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }

}
class MathCount {

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

5.集合的本质有助于我们理解方法反射和正常调用的不同(反射时绕过了编译)

  5.1集合中通过反射可以放入不同类型的元素

  5.2不同泛型的集合,其本质是同一个类类型

DemoSetNature.java

/**
 * Created by garfield on 2016/10/13.
 */
public class DemoSetNature {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList();
        ArrayList<String> arrayList1 = new ArrayList<String>();
        Class arrayListClass = arrayList.getClass();
        Class arrayList1Class = arrayList1.getClass();
        System.out.println(arrayList1Class == arrayListClass);
        /**
         * 结果为true说明,编译之后集合的泛型是去泛型化的
         * 泛型只在编译阶段有效
         */
        try {
            Method method = arrayListClass.getMethod("add",Object.class);
            //!绕过编译之后可以添加其他类型的数据
            method.invoke(arrayList1,200);
            System.out.println(arrayList1);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}