java 反射机制学习

来源:互联网 发布:淘宝上的小样是真的吗 编辑:程序博客网 时间:2024/05/18 02:14

有时候听别人说反射反射的,也是一脸茫然,当时就不能愉快的交流了。所以今天赶紧看看啥是反射,提高一下自己。

说到反射,不得不提 Class

public final class Class extends Object implements Serializable, GenericDeclaration, Type, AnnotatedElementjava.lang.Objectjava.lang.Class<T>

这个类的定义是这样的。

Class的实例表示正在运行的Java应用程序中的类和接口。 枚举是一种类,注解是一种接口。 每个数组也属于一个类,它反映为一个Class对象,由具有相同元素类型和维数的所有数组共享。 原始Java类型(boolean,byte,char,short,int,long,float和double)和关键字void也表示为Class对象。

Class 没有公共构造函数。 相反,Class对象由Java虚拟机在(通过调用类加载器ClassLoader中的defineClass方法)加载类的时候自动创建。

 /*     * Constructor. Only the Java Virtual Machine creates Class     * objects.     */    private Class() {}

Class 构造函数是私有的,那么我们就不能通过new的方式来得到Class的实例,那我们如何得到Class 的实例呢。有三种方法。

  1. 使用Class 的静态方法。
//通过指定的 className ,返回和这个类或者接口相关的Class对象。/**className   类的全路径名称。initialize  该类是否应该被初始化loader      类加载器*/forName(String className);forName(String className, boolean initialize, ClassLoader loader);

2 通过一个具体类的.class 方法。

//Person 类 Class c = Person.class;

3 通过一个具体的类的对象的.getClass ()方法。

 Person person = new Person(); Class c1 = person.getClass();

另外:如果要获得基本数据类型的类对象,还可以这样。

//获取int类型的类对象。 Class c2=Integer.TYPE;

在java程序运行期间,如果我们要产生某个类的对象,Java虚拟机(JVM)会检查该类型的Class对象是否已被加载。如果没有被加载,JVM会根据类的名称找到.class文件并加载它。一旦某个类型的Class对象已被加载到内存,就可以用它来产生该类型的所有对象

现在我们获得了Class 的对象,那么我们可以通过这个Class干什么呢?

  1. 获取成员变量Field
  2. 获取成员方法Method
  3. 获取构造函数Constructor

    先定义几个接口和类。

    Animal

public class Animal {    private int eyes;    protected String name;    public float weight;    float height;    public Animal() {    }    public Animal(int eyes, String name, float weight, float height) {        this.eyes = eyes;        this.name = name;        this.weight = weight;        this.height = height;    }    public int getEyes() {        return eyes;    }    public void setEyes(int eyes) {        this.eyes = eyes;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public float getWeight() {        return weight;    }    public void setWeight(float weight) {        this.weight = weight;    }    public float getHeight() {        return height;    }    public void setHeight(float height) {        this.height = height;    }}

RunInterface

public interface RunInterface {    int FieldInRunInterface = 0;    void run();    void jump();}

LiveInterface

public interface LiveInterface {    int FieldInLiveInterface = 1;    void eat();    void sleep();}

Person 类继承Animal,实现接口RunInterface,LiveInterface

public class Person extends Animal implements RunInterface, LiveInterface {    private int age;    private String msg = "good morning 2017";    public Person() {    }    public Person(String name) {        this.name = name;        System.out.println(name);    }    private Person(int age, String name) {        this.age = age;        this.name = name;        System.out.println(name);    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    public void fun() {        System.out.println("fun");    }    private void info(String name, int age) {        System.out.printf("ReflectActivity 我叫" + name + ",今年" + age + "岁");    }    @Override    public void run() {    }    @Override    public void jump() {    }    @Override    public void eat() {    }    @Override    public void sleep() {    }}

用Person 类来做测试。

1. 获取Person 类成员变量Field,有两个方法。

/**这个方法查找的是public 类型的变量,包括父类的和实现的接口中的。返回一个Field对象,该对象反映由此Class对象表示的类或接口的指定的公共成员变量。 name参数是一个String,指定所需字段的名称。查找算法是这样的1如果查找的类声明了这样一个public的成员变量,就是要查找的Field。2如果1中没有找到,就从当前类直接实现的接口中递归查找。3如果2中也没找到,就从当前类的父类递归查找。4上面三步找不到抛出NoSuchFieldException。*/getField(String name)

通过getField(String name)获取成员变量。

        Class c = Person.class;        try {            Object o = c.newInstance();//生成一个Person实例            Field field = c.getField("FieldInLiveInterface");            int a = field.getInt(o);            System.out.println(a + "");//输出1        } catch (Exception e) {            e.printStackTrace();        }
/** 返回一个Field对象,该对象反映由此Class对象表示的类或接口的指定的已声明字段。 name参数是一个字符串,指定所需字段的简单名称。*/getDeclaredField(String name)

通过getDeclaredField(String name)获取私有的成员变量。

        Class c = Person.class;        try {            Object o = c.newInstance();//生成一个Person实例            Field field1 = c.getDeclaredField("msg");            field1.setAccessible(true);            String msg = (String) field1.get(o);            System.out.println(msg);//输出good morning 2017        } catch (Exception e) {            e.printStackTrace();        }

注意
1:Object o = c.newInstance();使用当前类的默认public型的无参构造函数。来构造当前类的实例。如果没有public型的无参构造函数,就必须使用下文所提到的方法来获取构造函数,然后再构造相应的对象。
2: field1.setAccessible(true);表示反射得到的成员变量在使用的时候,不做java的访问可见性(就是成员变量的可见性 private public 等等)检查。
3:String msg = (String) field1.get(o); get(Object obj),把得到的成员变量的值(如果是基本数据类型,会把返回值自动包装成一个Object.),提取到obj中。如果获取的成员变量是static类型的话,get(Object obj)方法的参数obj 可以为null。

既然能够获取成员变量的值,那能够修改成员变量的值吗。当然可以。

 Class c = Person.class;        try {            Object o = c.newInstance();            Field field1 = c.getDeclaredField("msg");            field1.setAccessible(true);            String msg = (String) field1.get(o);            System.out.println(msg);            field1.set(o,"good by 2016");            String msgModified=(String) field1.get(o);            System.out.println(msgModified);        } catch (Exception e) {            e.printStackTrace();        }
//输出结果显示修改成功。good morning 2017good by 2016

获取当前类所有的public 成员变量,包括父类的和实现的接口中的。

//返回结果是一个数组//如果该类没有public成员变量,或者它表示数组类,原始类型或void,返回的数组长度为0.Field[] getFields ()
 Class c = Person.class; Field[] fields = c.getFields();     for (Field field : fields) {     System.out.println(field.getName());  }

输出结果

FieldInRunInterfaceFieldInLiveInterfaceweight

获取当前类所有的成员变量包括private,protected,public,default (package)类型,不包括继承的成员变量。

Field[] declaredFields = c.getDeclaredFields();    for (Field declaredField : declaredFields) {         System.out.println(declaredField.getName());     }

输出结果

agemsg

2. 获取成员方法Method

获取一个public 类型的方法。包括父类的和接口中的。

/**    name 表示方法名    Class...<?> parameterTypes 表示方法参数的类型。*/getMethod(String name, Class..<?> parameterTypes)
  try {            Method method = c.getMethod("setEyes", int.class);            System.out.println("name=" + method.getName() + ",return type= " + method.getReturnType());            Method method1 = c.getMethod("run");            System.out.println("name=" + method1.getName() + ",return type= " + method1.getReturnType());            Method method2 = c.getMethod("setAge",int.class);            System.out.println("name=" + method2.getName() + ",return type= " + method2.getReturnType());        } catch (NoSuchMethodException e) {            e.printStackTrace();        }
//输出结果name=setEyes,return type= voidname=run,return type= voidname=setAge,return type= void

Method getDeclaredMethod 获取该类的任意类型的方法,不包括从父类继承的方法。但是包括从接口中实现的方法。

 Method method1 = c.getDeclaredMethod("run");            System.out.println("name=" + method1.getName() + ",return type= " + method1.getReturnType());            Method method2 = c.getDeclaredMethod("info",String.class,int.class);            System.out.println("name=" + method2.getName() + ",return type= " + method2.getReturnType());            Method method3 = c.getDeclaredMethod("jump");            System.out.println("name=" + method3.getName() + ",return type= " + method3.getReturnType());
//输出结果name=run,return type= voidname=info,return type= voidname=jump,return type= void

Method[] getMethods ():获取当前类所有的public类型的方法,包括继承父类的和接口中的。如果没有public方法,或者该类是原始数据类型或者void类型,返回的数组的长度是0.

Method[] methods = c.getMethods();        for (Method method : methods) {            System.out.println(method.getName());        }
//输出结果runsleepgetAgesetAgefunjumpeatgetNamesetNamegetEyessetEyesgetWeightsetWeightgetHeightsetHeightwaitwaitwaitequalstoStringhashCodegetClassnotifynotifyAll

Method[] getDeclaredMethods ():获取当前类所有的方法,但是不包括继承父类的。如果该类是原始数据类型或者Array类型或者void类型,返回的数组的长度是0。

 Method[] declaredMethods = c.getDeclaredMethods();        for (Method method : declaredMethods) {            System.out.println(method.getName());        }
//输出结果runsleepgetAgesetAgefuninfojumpeat

既然愉快的获得了类的方法,那么可不可以愉快的调用一下呢?No Problem!

 Class c = Person.class; Method method2 = c.getDeclaredMethod("info", String.class, int.class);            System.out.println("name=" + method2.getName() + ",return type= " + method2.getReturnType());            method2.setAccessible(true);//info()方法是私有的。            method2.invoke(c.newInstance(), "dumingwei", 25);
//输出结果name=info,return type= voidReflectActivity 我叫dumingwei,今年25

3. 获取构造函数Constructor

//获取public 类型的构造函数Constructor<T> getConstructor (Class...<?> parameterTypes)
 Constructor constructor = c.getConstructor(String.class); System.out.println(constructor.getName()); //输出结果com.example.model.Person
//获取构造函数,public ,private 都不惧。Constructor<T> getDeclaredConstructor (Class...<?> parameterTypes)
 Constructor constructor1 = c.getDeclaredConstructor(int.class, String.class);System.out.println(constructor1.getName());//输出结果com.example.model.Person

getConstructors (),获取所有public 类型的构造函数

 Constructor[] constructors = c.getConstructors();            for (Constructor constructor : constructors) {                System.out.println(constructor);            }  //输出结果  //public com.example.model.Person(java.lang.String)  //public com.example.model.Person()

getDeclaredConstructors (),获取所有的构造函数。

Constructor[] declaredConstructors = c.getDeclaredConstructors();            for (Constructor constructor : declaredConstructors) {                System.out.println(constructor);            }
//输出结果//这个private 好刺眼。private com.example.model.Person(int,java.lang.String)public com.example.model.Person(java.lang.String)public com.example.model.Person()

注意:如果要获取的类对象是一个静态内部类获取构造函数 和非静态内部类获取构造函数的区别。

我们在Person 类里面加一个静态内部类。

public static class Man {        int beerCount;//能喝几瓶啤酒        public Man() {        }        public Man(int beerCount) {            this.beerCount = beerCount;        }        public int getBeerCount() {            return beerCount;        }        public void setBeerCount(int beerCount) {            this.beerCount = beerCount;            System.out.println("你能喝几瓶啤酒?" + beerCount);        }    }

然后获取 Man 类的构造函数。

 Class clz = Person.Man.class;            Constructor constructor = clz.getConstructor(int.class);            System.out.println(constructor.getName());            Constructor constructor1 = clz.getDeclaredConstructor();            System.out.println(constructor1.getName());
//输出结果,好像没什么问题。com.example.model.Person$Mancom.example.model.Person$Man

把Man 类去掉 static 修饰符,变成一个非静态内部类。然后使用同样的方法再去获取Man的构造函数,就直接报错了。

java.lang.NoSuchMethodException: com.example.model.Person$Man.<init>(int)    at java.lang.Class.getConstructor0(Class.java:3082)    at java.lang.Class.getConstructor(Class.java:1825)    at com.example.MyClass.main(MyClass.java:86)    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)    at java.lang.reflect.Method.invoke(Method.java:498)    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

正确的获取非静态内部类的方式。第一个参数传入外部类的类对象。

 Class clz = Person.Man.class;            Constructor constructor = clz.getConstructor(Person.class,int.class);            System.out.println(constructor.getName());            Constructor constructor1 = clz.getDeclaredConstructor(Person.class);            System.out.println(constructor1.getName());
//输出结果com.example.model.Person$Mancom.example.model.Person$Man

至于为什么这样就可以,现在还不明白,我猜是如果不传入一个外部类对象,java虚拟机无法正确的加载这个内部类。(有待以后学习验证)。

关于 java 反射就总结到这里,关于反射的原理,什么时候应该使用反射的一系列问题,自己现在并不明白,以后再慢慢研究。
参考链接:
【1】http://www.jianshu.com/p/1a60d55a94cd
【2】http://blog.csdn.net/u014082714/article/details/50004843
【3】https://developer.android.google.cn/reference/java/lang/Class.html

0 0
原创粉丝点击