Java开发——反射机制

来源:互联网 发布:月薪8000能招到java 编辑:程序博客网 时间:2024/06/04 23:32

前言:

在面向对象的世界里,万事万物皆对象。但是在java语言中,静态的成员、普通数据类型除外,静态成员属于类,而不是对象,而普通数据类型有对应的包装类来弥补它。类是不是对象呢?类是(哪个类的对象呢?)谁的对象呢?类是对象,类是java.lang.Class类的实例对象

一、反射的概念:

主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。
反射是java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接。但是反射使用不当会成本很高!
只看概念会几乎毫无感觉,通俗的讲可以这样理解:
反射的过程认为是将.class文件(也就是字节码文件)反编译为.java文件(Java源文件)。然后利用反射机制来得到类的属性、方法、构造方法。

二、Class类是什么:

一下通过代码来演示:

        Test test = new Test();        //官网说,以下三种方式c1 ,c2 ,c3表示了Test类的类类型(class type)       //任何一个类都是Class的一个实例对象,这个实例对象就是类类型        // 这个实例对象有三种表示方式:        //第一种表示方式:这是实际在告诉我们,任何一个类都有一个隐含的静态成员变量class;        Class c1 =  Test.class;        //第二种表示方式:已经类的实例,通过类的实例来获取Class的实例对象        Class c2 = test.getClass();        //第三种表示方式:        try {            Class c3 = Class.forName("Test");        } catch (ClassNotFoundException e) {            e.printStackTrace();        }        //结果是true,表明不管使用哪一种方式,一个类只能是Class类的一个实例对象        System.out.println(c1==c2);//true        System.out.println(c1==c2);//true        //我们可以通过类类型,创建该类的实例对象        //即我们可以通过c1,c2,c3,来创建Test类的实例对象        try {            Test test1 = (Test)c1.newInstance();        } catch (InstantiationException e) {            e.printStackTrace();        } catch (IllegalAccessException e) {            e.printStackTrace();        }

三、java 动态加载类

上面提到的第三种方法,Class.forName(“类的全称”),不仅表示了类的类类型,还代表了动态加载类。
我们一定要区分编译、运行编译时刻加载类是静态加载类、运行时刻加载类是动态加载类。
下面演示一个Java动态加载类的简单实例:
我们创建一个接口Subject,表示考试科目,并创建两个科目Mathmatic和Physics来实现Subject接口。

package com.tong.reflect;public interface Subject {    void printGrade();//打印该科目成绩}
package com.tong.reflect;public class Mathmatic implements Subject {    @Override    public void printGrade() {        System.out.println("打印数学成绩");    }}
package com.tong.reflect;public class physics implements Subject {    @Override    public void printGrade() {        System.out.println("打印物理成绩");    }}

然后就可以利用Java反射机制来动态加载类,并创建类的实例。在后续过程中如果还需要增加科目,只要实现Subject接口,并编译为字节码文件。一下为测试代码:

 public static void main(String[] args) {        try {            //动态加载类            Class c1 = Class.forName(args[0]);            //动态创建类的实例            Subject subject = (Subject) c1.newInstance();            subject.printGrade();//打印成绩        } catch (ClassNotFoundException e) {            e.printStackTrace();        }catch (InstantiationException e) {            e.printStackTrace();        } catch (IllegalAccessException e) {            e.printStackTrace();        }    }

四、通过Java反射获取类的成员变量、方法、构造函数

定义类ClassUtils用来获取类的方法:

public class ClassUtils {    /*    获取类的方法     */    public void printMethod(Object obj){        Class cl = obj.getClass();        System.out.println("打印类的名称:" + cl.getName());        /*        getMethods()获得的是类的所有的public方法,包括从父类继承来的方法        getDeclaredMethods()获取的是自己声明的方法,不问访问权限         */        Method[] method = cl.getMethods();        for(Method m:method){            Class returnType = m.getReturnType();            //打印返回值类型            System.out.print(returnType.getName() + " ");            //打印方法名            System.out.print(m.getName() + "(");            Class[] parameterType = m.getParameterTypes();            for(int i = 0;i<parameterType.length;i++){                if(i==parameterType.length-1)                    System.out.print(parameterType[i].getName());                else                    System.out.print(parameterType[i].getName() + ",");            }            System.out.println(");");        }    }    /*    获取类的成员变量     */    public void printField(Object obj){        Class cl = obj.getClass();        System.out.println("获取类" + cl.getName() +  "的成员变量:");        Field[] fields = cl.getFields();        for(Field f:fields){            Class type = f.getType();            System.out.println(type.getName() + " " + f.getName());        }    }    /*    获取类的构造方法     */    public void printConstructor(Object obj){        Class cl = obj.getClass();        System.out.println("获取类" + cl.getName() +  "的构造方法:");        Constructor[] constructors  = cl.getDeclaredConstructors();        for(Constructor con:constructors){            System.out.print(con.getName() + "(");            Class[] parametertype = con.getParameterTypes();            for(Class para:parametertype){                System.out.print(para.getName() + ",");            }            System.out.println(")");        }    }

测试:

 public static void main(String[] args) {        ClassUtils classUtils = new ClassUtils();        try {            //打印类中的方法            classUtils.printMethod(new Integer(1));            classUtils.printField(new Integer(1));            classUtils.printConstructor(new Integer(1));        } catch (Exception e) {            e.printStackTrace();        }    }

运行结果:

打印类的名称:java.lang.Integerint numberOfLeadingZeros(int);int numberOfTrailingZeros(int);int bitCount(int);boolean equals(java.lang.Object);。。。省略。。。。获取类java.lang.Integer的成员变量:int MIN_VALUEint MAX_VALUEjava.lang.Class TYPEint SIZEint BYTES获取类java.lang.Integer的构造方法:java.lang.Integer(int,)java.lang.Integer(java.lang.String,)

五、方法的反射

即通过反射来运行类中的方法。
方法反射的操作
method.invoke(对象,参数列表)

public class A {    //调用无参数的print    public void print(){        System.out.println("输入参数为空。。。。。");    }    //调用输入为整形的print    public void print(int a,int b){        System.out.println("输入参数为:" + a + "  " + b);    }    //调用输入参数为字符串的print    public void print(String a,String b){        System.out.println("输入参数为:" + a + "  " + b);    }}

测试代码:

   public static void main(String[] args) {        A a = new A();        Class cl = a.getClass();        Method m = null;        Object obj = null;        try {            //调用无参数的print            m = cl.getMethod("print",new Class[]{});            obj = m.invoke(a,new Object[]{});            //调用输入为整形的print            m = cl.getMethod("print",new Class[]{int.class,int.class});            obj = m.invoke(a,new Object[]{1,2});            //调用输入参数为字符串的print            m = cl.getMethod("print",new Class[]{String.class,String.class});            obj = m.invoke(a,new Object[]{"Hello","World"});        } catch (NoSuchMethodException e) {            e.printStackTrace();        } catch (IllegalAccessException e) {            e.printStackTrace();        } catch (InvocationTargetException e) {            e.printStackTrace();        }    }

六、通过反射来认识泛型的本质

Java泛型是JDK 5引入的一个特性,它允许我们定义类和接口的时候使用参数类型,泛型在集合框架中被广泛使用。Java中泛型是防止错误输入的,永远记住,泛型是一个编译时的概念,只在编译阶段有效,绕过编译泛型就无效了,来看下面的代码。

若是往list1中添加String类型元素,就会编译报错,如下代码:

     ArrayList<Integer> list1 = new ArrayList<Integer>();     list1.add(100);     list1.add(200);     //此时如果往list1中添加String类型,就会报错,编译不通过     //list1.add("Hello");     ArrayList<String> list2 = new ArrayList<String>();     list2.add("AAAA");     list2.add("BBBB");     list2.add("CCCC");

但是执行下语句时,发现list1和list2竟然指向同一个类类型。说明一个类只对一个类类型。

//输出为trueSystem.out.println(list1.getClass()==list2.getClass());

当通过反射机制来运行方法,就可以往声明为存放Integer类型的ArrayList中存放String类型数据。即绕过了编译就绕过了泛型

ArrayList<Integer> list1 = new ArrayList<Integer>();        list1.add(100);        list1.add(200);        //此时如果往list1中添加String类型,就会报错,编译不通过        //list1.add("Hello");        ArrayList<String> list2 = new ArrayList<String>();        list2.add("AAAA");        list2.add("BBBB");        list2.add("CCCC");        Class cl = list1.getClass();        try {            Method m = cl.getMethod("add",new Class[]{Object.class});            Object obj = m.invoke(list1,new Object[]{"Hello"});            System.out.println("list1的大小:"+list1.size());            System.out.println("list1的内容:"+list1);        } catch (NoSuchMethodException e) {            e.printStackTrace();        } catch (IllegalAccessException e) {            e.printStackTrace();        } catch (InvocationTargetException e) {            e.printStackTrace();        }

运行结果:

list1的大小:3list1的内容:[100, 200, Hello]