Java JDK Reflect

来源:互联网 发布:网络直播受众群体 编辑:程序博客网 时间:2024/05/16 13:44

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

1、反射的基本概念

如果正常的情况下,如果要使用一个类,则必须按照如下的步骤操作:
1)使用import导入类所在的包(类:java.lang.Class)。
2)明确的使用类名称或接口名称定义对象。
3)通过关键字new进行类对象实例化(构造方法:java.lang.reflect.Constructor).
4)产生对象可以使用”对象.属性”进行类中属性的调用(属性:java.lang.reflect.Field);
而反射的过程呢?不需要有明确类型的对象,所有的对象使用Object表示:
可以直接利用Object与反射机制的混合调用类中的方法。
下面我们来看一下反射中类的关系图:
这里写图片描述

下面就简单说一下图中每个类的功能:
AnnotatedElement:注解元素操作类
Member:提供类和接口,以获取关于类和对象的反射信息。
GenericDeclaration:声明类型变量的所有实体的公共接口
Type:返回这个类的名称(e.g. java.lang.Class)
AccessibleObject:Field,Contrutor,Method类的基本类,用于控制可访问.
Executable:Constructor,Method共享通用功能的超类
Filed:类中的属性
Contrutor:类中的构造器
Method:类中的方法
Annotation:类中的注解
Class:Class代表类的实例(类类型)

2、如何获取一个类类型(Class)

如何取得Class对象:
1. Object类提供了一个返回Class类对象的方法:public Class getClass();
2. 利用”类.class”取得,日后见得最多的就是在Hibernate上
3. 利用Class类的static方法取得:ClassforName(String className);
如果是程序设计人员,使用最多的方法一定是forName()方法,但是如果是使用者,肯定会使用”类.class”通过之前的分析可以知道,工厂设计模式最好利用反射机制来解决耦合问题

3、Object类

我们都知道在Java的世界中一切皆对象。那么我们就有必要了解一下Object类。那么我们就来了解一下Object类中的所有方法以及每一个方法使用的注解事项:
1. 对象clone:Object clone()
clone对象所在的类一定要实现java.lang.Cloneable接口,而且子类只需要调用Object.clone就可以成功
实现clone操作
2. 对象输出 : String toString();
直接输出对象时会默认调用toString()方法。
3. 对象比较:boolean equals(Object obj)
当保存set集合时,会依靠hashCode()和equals()判断是否为重复对象
4. 取得对象的hash码:int hashCode();
可以理解为每一个类对象的唯一编码,比较时会先判断编码是否相同,而后再调用equals()
5. 取得class类对象:Class getClass();
通过一个已经实例化好的对象进行对象的反射操作
6. 线程等待void wait() InterceptorException
执行到此代码时线程要等待执行,一直到执行notify(),notifyAll()方法
7. 唤醒第一个等待线程:void notify();
8. 唤醒全部等待线程:void notifyAll();
9. public void finalize() throws Throwable;
当使用gc回收无用垃圾空间时默认调用

4、Class – 利用反射实例化对象

Class类如果使用了forName()方法之后,就可以使用Class类定义的newInstance方法默认去调用类之中的无参构造方法进行操作 T newInstance.一定要记住,此泛型使用不到。
1)Class Demo类

/** * Created by Carl on 2016/8/13. */class Person{    public Person(){        System.out.println("Peson类的构造方法...");    }    @Override    public String toString() {        return "Peson类的toString()方法...";    }}public class ReflectDemo {    public static void main(String[] args) throws Exception {        Class<?> clazz = Class.forName("com.weimob.carl.user.reflect.Person");        Object obj = clazz.newInstance();        System.out.println(obj);    }}

2)运行结果
这里写图片描述
这个时候就可以非常清楚的发现,在整个程序编写之中,即使不完全知道类的结构,即使不导入包.class,可以进行对象的实现

5、Contrutor – 操作构造方法

但是如果使用反射实例化类对象,必须要求类中存在无参构造方法。因为默认使用Class类的newInstance()
方法只能够找到无参。这个时候只能够取得类之中的构造方法,传递所需要的参数后才可以执行。
在Class类里面定义了可以取得一个类之中方法的操作:
1、取得类之中的全部构造:Constructor[?> getConstructors()
2、取得类之中指定参数的构造:Constructor[T>getConstructor(Class[?>… parameterTypes)
1)范例:取得String类之中的全部构造方法:

public class ReflectDemo {    public static void main(String[] args) throws Exception {        Class<?> clazz = Class.forName("java.lang.String");        Constructor<?>[] constructors = clazz.getConstructors();        for (int i = 0; i < constructors.length; i++) {            System.out.println(constructors[i]);        }    }}

运行结果:
这里写图片描述
所以如果现在要想进行指定构造方法调用,就必须将关注点放在Constructor类之中。在此类中定义了一个实例化对象方法public T newInstance(Object.. initargs)。

2)范例:实例化不含有无参的Person对象

class Person{    private String name;    private Integer age;    public Person(String name, Integer age){        this.name = name;        this.age = age;    }    @Override    public String toString() {        return "姓名: " + this.name + ",年龄: " + this.age;    }}public class ReflectDemo {    public static void main(String[] args) throws Exception {        Class<?> clazz = Class.forName("com.weimob.carl.user.reflect.Person");        Constructor<?> constructor = clazz.getConstructor(String.class, Integer.class);        Object object = constructor.newInstance("carl", 26);        System.out.println(object);    }}

运行结果:
这里写图片描述
正是因为如果是通过构造方法实例化对象规格不统一。所以在进行简单Java类操作的时候就明确给出了,必须有无参构造方法。

6、Method – 调用类中方法

取得了一个类的实例化对象之后,下面主要的任务就是要调用类之中可以使用的操作方法,对于一个类之中的方法实际上由两类:
1)取得父类继承而来的方法和本类方法:
取得全部方法 Methods[] getMethods();
取得指定方法 Method(String name, Class[?>… parameterType)
2)取得本类定义的方法
取得全部方法 Method[] getDeclaredMethods()
取得指定方法 Method getDeclaredMethod(String name, Class[?>… parameterType)
但是以上的操作在方法定义上区别不大,因为方法大部分都是public ,所有两种方式取得的结果是没有区别。
1)范例:取得本类中的方法

class Person{    private String name;    private Integer age;    public Person(String name, Integer age){        this.name = name;        this.age = age;    }    @Override    public String toString() {        return "姓名: " + this.name + ",年龄: " + this.age;    }    public void sayHello(){        System.out.println("hello world...");    }}public class ReflectDemo {    public static void main(String[] args) throws Exception {        Class<?> clazz = Class.forName("com.weimob.carl.user.reflect.Person");        Method[] methods = clazz.getDeclaredMethods();        for(int i = 0; i < methods.length; i++){            System.out.println(methods[i]);        }    }}

运行结果:
这里写图片描述
现在的程序是直接调用Method类之中的toString()方法实现的输出。如果用户有需要也可以自己去整理方法的输出,需要使用到Method类的如下方法.
2)自己去整理方法的输出
1.取得方法修饰符:public int modityiers()
程序之中找的不是public、static等关键字,而是关键字所代表的数字,但是在程序之中必须将其更换为
读懂的信息,则可以借助于Modifyer类完成,此类中可以直接利用方法将数字变为修饰字符串
转换方法:public static String toString(int mod)。
这里写图片描述
2)取得方法的返回类型:public Class[?> getReturnType();
3)取得方法的参数:public Class[?> getParameterTypes();
4)取得所有抛出的异常:public Class[?> getExceptionTypes();

代码如下:

class Person{    private String name;    private Integer age;    public Person(String name, Integer age){        this.name = name;        this.age = age;    }    @Override    public String toString() {        return "姓名: " + this.name + ",年龄: " + this.age;    }    public void sayHello(){        System.out.println("hello world...");    }}public class ReflectDemo {    public static void main(String[] args) throws Exception {        Class<?> clazz = Class.forName("com.weimob.carl.user.reflect.Person");        Method[] methods = clazz.getMethods();        for(int i = 0; i < methods.length; i++){            // 方法修饰符            System.out.print(Modifier.toString(methods[i].getModifiers()));            // 方法返回参数            System.out.print(" " + methods[i].getReturnType().getSimpleName() + " ");            // 方法名            System.out.print(methods[i].getName() + "(");            // 方法参数            Class<?> params[] = methods[i].getParameterTypes();            if(params.length > 0){// 有方法参数                for(int j = 0; j < params.length; j++){                    System.out.print(params[j].getSimpleName() + " ");                    if(j < params.length - 1){                        System.out.print(", ");                    }                }            }            System.out.print(") ");            // 方法异常            Class<?> exceptions[] = methods[i].getExceptionTypes();            if (exceptions.length > 0){                System.out.print(" throws ");                for(int j = 0; j < exceptions.length; j++){                    System.out.print(exceptions[j].getSimpleName());                    if(j < exceptions.length - 1){                        System.out.print(", ");                    }                }            }            System.out.println();// 换行        }    }}

运行结果:
这里写图片描述
此类代码一般只会在编写开发工具的时候出现,而所谓的随笔提示功能就是依据以上的代码实现的。
开发者密切关联最紧密的一定是利用Method调用类中的方法。而且在Method调用类中的方法。
范例:反射调用类中的方法。
调用方法 – public Object invoke(Object obj, Object… args)

class Person{    private String name;    public void setName(String name) {        this.name = name;    }    public String getName() {        return name;    }}public class ReflectDemo {    public static void main(String[] args) throws Exception {        Class<?> clazz = Class.forName("com.weimob.carl.user.reflect.Person");        Object object = clazz.newInstance();        Method setNameMethod = clazz.getMethod("setName", String.class);        Method getNameMethod = clazz.getMethod("getName");        setNameMethod.invoke(object, "carl");        System.out.println(getNameMethod.invoke(object));    }}

运行结果:
这里写图片描述

7、Field – 调用类中的属性(尽量不要去使用)

关于类中的属性也可以直接利用反射进行操作,而支持的方法有两类:
1)取得所有继承而来的属性
取得全部属性 public Field[] getFields()
取得指定属性 public Field getField(String name)
2)取得本类定义的属性
取得本类全部属性public Field getDeclareFields()
取得本类全部属性public Field getDeclareField(String name)
范例:取得一个类之中的属性

interface Message{    String INFO = "hello world.";}class Person{    private String name;    private Integer age;}class Student extends Person implements Message{    private String school;    private Double price;}public class ReflectDemo {    public static void main(String[] args) throws Exception {        Class<?> clazz = Class.forName("com.weimob.carl.user.reflect.Student");        {   // 取得继承而来的全部属性            System.out.println("取得继承而来的全部属性");            Field[] fields = clazz.getFields();            for(int i = 0; i < fields.length; i++){                System.out.println(fields[i]);            }        }        {   // 取得本类定义的全部属性            System.out.println("取得本类定义的全部属性");            Field[] fields = clazz.getDeclaredFields();            for(int i = 0; i < fields.length; i++){                System.out.println(fields[i]);            }        }        {   // 取得父类属性            System.out.println("取得父类属性");            Field[] fields = clazz.getSuperclass().getDeclaredFields();            for(int i = 0; i < fields.length; i++){                System.out.println(fields[i]);            }        }    }}

运行结果:
这里写图片描述
2)在Field类里面还定义有进行属性调用的方法:
1)设置属性内容:public void set(Object obj, Object value)
2)取得属性内容:public Object get(Object obj);
在Constructor、Method、Field三个类上有一个共同的父类 AccessibleOblect,在这个类中定义了可以取消封装操作:public void setAccessible(boolean flag)

class Person{    private String name;}public class ReflectDemo {    public static void main(String[] args) throws Exception {        Class<?> clazz = Class.forName("com.weimob.carl.user.reflect.Person");        Object object = clazz.newInstance();        Field nameField = clazz.getDeclaredField("name");        nameField.setAccessible(true);        nameField.set(object, "carl");        System.out.println(nameField.get(object));    }}

运行结果:
这里写图片描述
在开发之中,只需要灵活使用Class、Constructor、Method、Field就可以使用反射进行一系列操作
的代码实现了。

1 0
原创粉丝点击