Java反射

来源:互联网 发布:易语言丁丁网络验证 编辑:程序博客网 时间:2024/06/05 14:15

  • Java反射
    • Claass类的使用
    • Java动态加载类
    • 获取方法信息
    • 获取成员变量构造函数信息
    • 方法反射的基本操作
    • 通过反射了解集合泛型的本质

Java反射

1. Claass类的使用

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

​ Java语言中,静态的成员、普通数据类型(但是他有包装类)不是对象。类也是对象,类是java.lang.Class类的实例对象。”There is a class named Class”——有一个类,名字就叫Class,他的实例就是我们每一个对象所属的类。

2)这个对象如何表示?

package com.cie.reflect;public class ClassDemo1 {    public static void main(String[] args) {        // TODO Auto-generated method stub        Foo foo1 = new Foo();   //foo1就表示出来了        //Foo这个类,也是一个实例对象,Class类的实例对象        //任何一个类都是Class的实例对象,这个实例对象有三种表示方式        //第一种表示方式-->实际在告诉我们,任何一个类都有一个隐含的静态成员变量class.        Class c1 = Foo.class;        //第二种表示方式-->已经知道该类的对象通过getClass方法        Class c2 = foo1.getClass();        /*官网,c1,c2表示了Foo类的类类型(class type)         * 万事万物皆对象,         * 类也是对象,是class类的实例对象         * 这个对象我们成为该类的类类型         */        //不管c1或者c2,都代表了Foo类的类类型,一个类只可能是Class类的一个实例对象        System.out.println(c1==c2); //true        //第三种表示方式        Class c3 = null;        try {            c3 = Class.forName("com.cie.reflect.Foo");        } catch (ClassNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        System.out.println(c2==c3);     //true        //我们完全可以通过类的类类型创建该类的对象实例--> 通过c1或c2或c3创建Foo的实例        try {            Foo foo = (Foo) c1.newInstance();   //需要有无参数的构造方法            foo.print();        } catch (InstantiationException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IllegalAccessException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }}class Foo{    void print(){        System.out.println("foo");    }}

2. Java动态加载类

  • Class.forName(“类的全称”)

    不仅表示了类的类类型,还代表了动态加载类

    区分编译、运行

    编译时刻加载类是静态加载类,运行时刻加载类是动态加载类

public class office{    public static void main(String[] args){        //new创建对象是静态加载类,在编译时刻就需要加载所有的可能使用到的类        //通过动态加载类可以解决该问题(用哪个加载哪个,不用不加载,即在运行时刻加载)        if("Word".equals(args[0])){            Word w = new Word();            w.start();        }        if("Excel".equals(args[0])){            Excel e = new Excel();            e.start();        }    }}

可以做如下修改。

public class Word implements OfiiceAble {    @Override    public void start() {        System.out.println("Word...");      }}public interface OfiiceAble {    public void start();}public class OfficeBetter {    public static void main(String[] args) {        try {            //动态加载类,在运行时刻加载            Class c = Class.forName(args[0]);   //参数传入类名            //通过类类型,创建该类的对象            //Word w = (Word)c.newInstance();            //Excel e = (Excel)c.newInstace(); //这两种方式都不合适,因为不确定用到的类具体是哪一个            OfiiceAble oa = (OfiiceAble)c.newInstance();            oa.start();        } catch (Exception e) {            e.printStackTrace();        }    }}

这就是通过动态的方式加载类。

3. 获取方法信息

  • 基本的数据类型、void关键字,都存在类类型。
public class ClassDemo2 {    public static void main(String[] args) {        Class c1 = int.class;   //int的类类型        Class c2 = String.class;    //String类的类类型 (String类字节码)        Class c3 = double.class;    //double这个数据类型的字节码表示方式        Class c4 = Double.class;    //Double这个类的类类型字节码表示方式        Class c5 = void.class;        System.out.println(c1.getName());   //int        System.out.println(c2.getName());   //java.lang.String        //不包含包名的类的名称        System.out.println(c2.getSimpleName()); //String        System.out.println(c5.getName());       //void    }}
public class ClassUtil {    /**     * @return void     * @param obj 该对象所属类的信息     * 打印类的信息——类的成员函数     */    public static void printMethodsMessage(Object obj){        //要获取类的信息,首先要获取类的类类型        Class c = obj.getClass();   //传递的是哪个子类的对象,c就是该子类的类类型        //获取类的名称        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[] paramTypes = ms[i].getParameterTypes();            for (Class class1 : paramTypes) {                System.out.print(class1.getName()+",");            }            System.out.println(")");        }    }   /**     * @return void     * @param obj 该对象所属类的信息     * 打印类的成员变量信息     */    private static void printFieldMessage(Object obj) {        Class c = obj.getClass();        /*         * 成员变量也是对象         * java.lang.reflect.Field         * Field类封装了关于成员变量的操作         * getFields()方法获取的是所有的public的成员变量的信息         * getDeclaredFields()获取的是该类自己声明的成员变量的信息        */        //Fields[] fs = c.getFields();        Field[] fs = c.getDeclaredFields();        for (Field field : fs) {            //得到成员变量类型的类类型            Class fieldType = field.getType();            String typeName = fieldType.getName();            //得到成员变量的名称            String fieldName = field.getName();            System.out.println(typeName+"---"+fieldName);        }    }  }/* * 测试 */public class ClassDemo3 {    public static void main(String[] args) {        String s = "hello";        ClassUtil.printMethodsMessage(s);        Integer n1 = 1;        ClassUtil.printFieldMessage(n1);        ClassUtil.printConMessage(n1);    }}

4. 获取成员变量构造函数信息

    /**     * @return void     * @param obj     * 打印对象的构造函数的信息     */    public static void printConMessage(Object obj){        Class c = obj.getClass();        /*          * 构造函数也是对象         * java.lang.Constructor中封装了构造函数的信息         * getConstructors获取所有的public的构造函数         * getDeclaredConstructors得到所有的构造函数         */        //Constructor[] cs = c.getConstructors();        Constructor[] cs = c.getDeclaredConstructors();        for (Constructor constructor : cs) {            System.out.print(constructor.getName()+"(");            //获取构造函数的参数列表--->得到的是参数列表的类类型            Class[] paramtypes = constructor.getParameterTypes();            for (Class class1 : paramtypes) {                System.out.print(class1.getName()+",");            }            System.out.println(")");        }    }/* * 测试 */public class ClassDemo3 {    public static void main(String[] args) {        Integer n1 = 1;        ClassUtil.printConMessage(n1);    }}

5. 方法反射的基本操作

  • 如何获取某个方法,方法的名称和方法的参数列表才能唯一决定某个方法

    首先要获取类的类型——getClass()方法,然后获取方法——getMethod(名称,参数列表)

  • 方法反射的操作

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

package com.cie.reflect;import java.lang.reflect.Method;public class MethodDemo1 {    public static void main(String[] args) {        // 要获取print(int ,int)方法        // 1.要获取一个方法就是获取类的信息,获取类的信息首先要获取类的类类型        A a1 = new A();        Class c = a1.getClass();        /*         * 2.获取方法,名称和参数列表来决定 getMethod获取的是public方法 getDeclaredMethod自己声明的方法         */        try {            // 处理异常是为了防止该方法不存在            // Method m = c.getMethod("print", new            // Class[]{int.class,int.class});            // getMethod的第二个参数为可变参数。所以也可以写成            Method m = c.getMethod("print", int.class, int.class);            // 方法调用: 传统方法a1.print(10, 20);            /*              * 方法的反射操作是用m对象来进行方法调用,和a1.print调用的效果相同。             * 可以理解为print是个方法对象,m 即为这个对象,原本是用a1对象操作print方法             * 现在用print所代表的的m对象反过来操作a1,即用invoke()操作             * 方法如果没有返回值,返回null,有返回值,返回具体的返回值(原本是Object,所以要做强制类型转换)            */            Object o = m.invoke(a1, new Object[]{10,20});            //Object o = m.invoke(a1, 10,20);            System.out.println("================分割线================");            //获取print(String,String)方法            Method m1 = c.getMethod("print", String.class, String.class);            //用方法进行反射操作            //a1.print("hello", "world");            o = m1.invoke(a1, "hello", "world");            System.out.println("================分割线================");            //获取print()方法            //Method m2 = c.getMethod("print",new Class[]{});            Method m2 = c.getMethod("print");            //m2.invoke(a1, new Object[]{});            m2.invoke(a1);        } catch (Exception e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }}class A {    public void print() {        System.out.println("Hello!");    }    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());    }}

6. 通过反射了解集合泛型的本质

  • 反射的操作、Class的操作、Method的操作、Field的操作都是绕过编译,都是在运行时刻来执行的
package com.cie.reflect;import java.lang.reflect.Method;import java.util.ArrayList;public class MethodDemo2 {    public static void main(String[] args) {        ArrayList list = new ArrayList();        ArrayList<String> list1 = new ArrayList<String>();        list1.add("String");        //list1.add(20);    //赋值错误,编译不通过        Class c1 = list.getClass();        Class c2 = list1.getClass();        System.out.println("c1=c2?"+(c1==c2));  //true        //反射的操作都是编译之后的操作(即运行时)        /*         * c1==c2结果返回true,说明编译之后集合的泛型是去泛型化的         * Java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了。         * 验证:我们可以通过方法的反射来操作,绕过编译        */        try {            Method m = c2.getMethod("add", Object.class);            m.invoke(list1, 20);    //绕过变异操作就绕过了泛型            System.out.println("list1.size="+list1.size());     //2            System.out.println(list1);      //[String, 20]            /*for (String string : list1) { //遍历会出错,因为int值不能转为String                System.out.println(string);            }*/ //现在不能这样遍历,会有类型转换错误        } catch (Exception e) {            e.printStackTrace();        }    } }
0 0
原创粉丝点击