java基础之反射

1、简介

反射库(reflection library)提供了一个非常丰富的工具集,以便编写能够动态的操纵java代码中的程序。这项功能被大量应用于javaBeans中,他是java组件的体系结构。特别是在设计或运行中添加新类时,能够快速应用开发工具动态地查询新添加类的能力。能够分析类能力的程序被称为反射(reflective)。

反射机制功能:

  • 在运行时分析类的能力。
  • 在运行时查看对象,例如,编写一个toString方法供所有类使用。
  • 实现数组的操作代码。
  • 利用Method对象,这个对象很象C++中的函数指针。

2、Class类

在程序运行期间,java运行时系统始终为所有的对象维护被一个称为运行时的类型标识。这个信息保存着每个对象所属的类的足迹。虚拟机利用运行时信息选择相应的方法执行。

然而可以通过专门的java类访问这些信息。保存这些信息的类被称为Class。Object的getClass方法会返回一个Class的实例。

如同一个Employee对象表示一个特定的雇员属性一样,一个Class对象将表示一个特定的类的属性。最常用的Class方法是getName。这个方法返回的就是类的名字。例如下面这条语句:

System.out.println(e.getClass().getName()+" "+e.getName())
//如果e是一个雇员 打印结果为:Employee Harry Hacker
//如果e是一个经理 打印结果为:Manager Harry Hacker

如果类在一个包里,包的名字将作为类名的一部分:

Date d=new Date();
Class c1=d.getClass();
String name=c1.getName();//java.util.Date
//调用静态方法 forName
String className="java.util.Date";
Class clazz=Class.forName(className);

如果类名保存在字符串中,并且在运行中可改变,就可以使用这个方法。只有在class是类名或接口名时才能执行。否则,forName将抛出一个checkedexception(已检查异常)。使用该方法,应该提供一个异常处理器。

2.1 java.lang.Class

static Class forName(String className);

返回描述为className的Class对象

Object newInstance();

返回该类的一个实例

2.2 java.lang.reflect.Constructor

Object newInstance(Object[] args);

构造一个这个构造其所属类的新实例

参数:args 这是提供给构造器的参数。

3、利用反射分析类的能力

        在java.lang.reflect包中有三个类Field,Method 和Construtor分别用于描述类的域,方法和构造器。这三个类都有getName方法用于返回项目的名称。Field类有一个getType方法,用来返回描述域所属类型的对象。Method和Constructor类有能够报告参数类型的方法。这三个类还有一个叫做getModifiers方法,它将返回一个整型数值,用不同的位开关描述public和static这样的修饰符使用的状况。另外,还可以用Modifier类中的静态方法来分析getModifiers返回的整型数值进行分析,另外还可以利用,Modifier.toString方法将修饰法打印出来。

        Class中的getFields、getMethods和getConstructors方法将分别返回类中声明的全部域、方法和构造器、其中包括超类的公有成员。Class的getDeclareFields,getDeclareFieldsMethods和getDeclareFieldsConstructors方法将放回类中声明的全部域,方法和构造器,其中包括私有和受保护的成员,但不包括超类的成员。

下面这个例子将打印出类的全部信息:

 public static void getAllMember(String ClassName) throws ClassNotFoundException {
       Class clazz=Class.forName(ClassName);
        System.out.println(Modifier.toString(clazz.getModifiers())+" "+clazz.getName()+" extends "+clazz.getSuperclass().getName()+"{");
        for(Field i:clazz.getDeclaredFields()){
            System.out.println(Modifier.toString(i.getModifiers())+" "+i.getType().getName()+" "+i.getName()+";");
        }

        for (Constructor i:clazz.getConstructors()){
            System.out.print(Modifier.toString(i.getModifiers())+" "+i.getName()+"(");
            int count=0;
            for(Class c:i.getParameterTypes()){
                System.out.print(c.getName());
                count++;
                if(count!=i.getParameterCount()){
                    System.out.print(",");
                }
            }
            System.out.print("){};");
            System.out.println();
        }

        for(Method i:clazz.getDeclaredMethods()){
            System.out.print(Modifier.toString(i.getModifiers())+" "+i.getReturnType().getName()+" "+i.getName()+"(");
            int count=0;
            for(Class c:i.getParameterTypes()){
                System.out.print(c.getName());
                count++;
                if(count!=i.getParameterCount()){
                    System.out.print(",");
                }
            }
            System.out.print("){};");
            System.out.println();
        }

        System.out.println("}");
    }
}

下面是常用的方法介绍:

  • Field[] getFields()
  • Field[] getDeclaredFields()

getFields方法返回了一个Field对象的数组,这些对象记录了这个类或其超类的公有方域。getDeclareField方法也将返回包含Fields对象的数组,这些对象记录了这个类的全部域。如果类中没有没有域,或者Class对象描述的是基本数据类型或者数组类型,这些方法返回长度为0的数组。

  • Method[] getMethods()
  • Method[] getDeclareMethods()

返回包含Method对象的数组:getMethods将返回所有的公共方法,包含从超类继承的公有方法;getDeclaredMethods返回这个类或接口的全部方法,但不有超类继承的方法。

  • Constructor[] getConstructors()
  • Constructor[] getDeclaredConstructors()

返回包含Constructor对象的数组,其中包含了Class对象所描述的类的所有公有构造器或所有构造器。

  • Class getDeclaringClass()

返回一个用于描述类中定义的构造器、方法或域的修饰符的整型数值。使用Modifier类中的这个方法可以分析这个值。

  • Class[] getExceptionTypes()(在Constructor和Method类中)

返回一个用于描述方法抛出的异常类型的class对象数组。

  • int getModifiers()

返回一个用于描述构造器、方法或域的修饰符的整型数值。使用Modifier类中的这个方法可以分析这个返回值。

  • String getName()

返回一个用于描述构造器、方法或域名的字符串。

  • Class[] getParameterTypes()(在Constructor和Method类中)

返回一个用于描述参数类型的Class对象数组。

  • Class getReturnType()(在Method类中)

返回一个描述返回类型的Class对象。

  • static String toString()(在Method类中)

返回对应的modifiers位设置的修饰符的字符串的表示。

  • static boolean isAbstract(int modifiers)
  • static boolean isFinal(int modifiers)
  • static boolean isInterface(int modifiers)
  • static boolean isNative(int modifiers)
  • static boolean isPrivate(int modifiers)
  • static boolean isProtected(int modifiers)
  • static boolean isPublic(int modifiers)
  • static boolean isStrict(int modifiers)
  • static boolean isSynchonizedt(int modifiers)
  • static boolean isVolatile(int modifiers)

这些方法将检测方法名中对应的修饰符在modifiers值中的位。

4、在运行时使用反射分析对象

        在编写程序时,如果知道想查看的域名和类型,查看指定的域是一件很容易的事。而利用反射机制可以查看在编译时还不清楚的对象域。

        查看对象域的关键方法是Field类中get方法。如果f是一个Field类型对象(例如,通过getDeclareFields得到的对象),obj是某个包含f域的对象,f.get(obj)将返回一个对象,其值为obj域的当前值。

Employee harry = new Employee("Harry Hacker",35000,10,1,1989);
Class c1=harry.getClass();
Field f=c1.getDeclareField("name");
Object v=f.get(harry);//v就是Harry Hacker

        实际上,上面这段代码存在一个问题。由于name是一个是一个私有域,所以get方法将会抛出一个异常IllegalAccessException。只有利用get方法才能得到可访问域的值。除非拥有访问权限,否则java安全机制只允许查看任意对象有哪些域,而不允许读取他们的值。

        反射机制的默认行为受限于java的访问控制。然而,如果一个java程序没有受到安全管理器的控制,就可以覆盖访问控制。为了达到这个目的,就需要调用Field,Method和Constructor对象的setAccessible方法。

        setAccessible方法是AccessibleObject类中的一个方法,他是Field,Method和Constructor对象的公共超类。这个特性是为调试、持久储存和相似机制提供的。

        get方法还有一个需解决的问题。name是String类型,因此把它当作Object类型返回没什么问题。但是,假定我们想查看salary域。它属于double类型,而java数值类型不是对象。要想解决这个问题,可以使用Field类中的getDouble,也可以调用get方法,此时反射机制将会自动将这个域打包到相应的对象包装器中,这里将打包成Double。

        当然,可以获得就可以设置。调用f.set(obj,value)可以将obj对象的f域值设置成新值。

下面写一个可供任意类使用的通用toString方法。其中使用getDeclaredFields获得所有数据域,然后使用setAcessible将所有域设成可访问的。对于每个域,获得了名字和值。

public class ObjectAnalyzer {

    ArrayList visited;

    {
        visited=new ArrayList();
    }

    public String toString(Object o){

        if(o==null) return "null";

        if(visited.contains(o)) return "...";

        visited.add(o);

        Class clazz=o.getClass();

        if(clazz==String.class){
            return (String)o;
        }

        if(clazz.isArray()){
            String r=clazz.getComponentType()+"[]{";

            for(int i=0;i< Array.getLength(o);i++){
                if(i>0) r+=",";

                Object val=Array.get(o, i);

                if(clazz.getComponentType().isPrimitive()) r+=val;
                else r+=toString(val);
            }
            return r+="}";
        }

        String r=clazz.getName();

        do{
            r+="[";
            Field[] fields=clazz.getDeclaredFields();
            AccessibleObject.setAccessible(fields,true);
            for(Field f:fields){
                if(!Modifier.isStatic(f.getModifiers())){
                    if(!r.endsWith("[")){
                        r+=",";
                    }

                    r+=f.getName()+"=";

                    try{
                        Class t=f.getType();
                        Object val=f.get(o);
                        if(t.isPrimitive()) r+=val;
                        else r+=toString(val);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
                r+="]";
                clazz=clazz.getSuperclass();
            }
        }while(clazz!=null);

        return r;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值