Java开发——反射机制

前言:

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

一、反射的概念:

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

二、Class类是什么:

一下通过代码来演示:

        Test test = new Test();
        //官网说,以下三种方式c1 ,c2 ,c3表示了Test类的类类型(class type)
       //任何一个类都是Class的一个实例对象,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.Integer
int numberOfLeadingZeros(int);
int numberOfTrailingZeros(int);
int bitCount(int);
boolean equals(java.lang.Object);
。。。省略。。。。
获取类java.lang.Integer的成员变量:
int MIN_VALUE
int MAX_VALUE
java.lang.Class TYPE
int SIZE
int 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竟然指向同一个类类型。说明一个类只对一个类类型。

//输出为true
System.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的大小:3
list1的内容:[100, 200, Hello]

猜你喜欢

转载自blog.csdn.net/sixingmiyi39473/article/details/78311336