黑马程序员---Java反射

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

2015.10.15

Class类

Class类的实例表示正在运行的类和接口。Class实例不可以显示创建,而是在类被加载时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。
JVM的内存区,除了包含栈,堆外,还有方法区,这是系统分配给JVM的一个存储类型信息的逻辑内存。存储的类型信息包括以下几部分:
1:类的基本信息:使用的JDK版本号。
2:类的详细信息:
(1)常量池:存储与该类有关的所有常量,例如final变量、类名、方法名等。
(2)字段信息:类中声明的每一个字段的信息。如名称、类型、修饰符。例如:private String name=”Most_want” ;字段名是name,类型是String,修饰符是private。
(3)方法信息:类中声明的每一个方法。包含修饰符、返回值类型、方法名、参数类型、异常、方法的字节码文件。
(4)静态区:类中声明的静态变量。
(5)类的ClassLoader引用:该类的类加载器的引用。
(6)Class实例:该类在被JVM加载时生成的Class实例,用来代表被加载的类。
JVM启动后,要使用的类的.class二进制文件就会被加载到方法区生成以上信息。所以可以看到,Class实例是在JVM加载类时候就被自动创建了。在Java中数组也有Class实例,所有具有相同元素类型、维度的数组都共享一个Class实例。基本的Java数据类型(boolean byte short char int long float double)和关键字void也表示为一个Class对象。

获取这个实例有以下几个方法:
通过Object类的getClass()获取。
通过Class类的静态方法:forName()得到。
通过.class获取。
例如获取String类的Class对象:

public class Test{
    public static void main(String[] args)throws Exception{
        String str="Most_want"; 
        Class c1=str.getClass();
        Class c2=Class.forName("java.lang.String");
        Class c3=String.class;

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
    }
}

打印结果均为:class java.lang.String

反射

Java中的所有类都是Class类的对象。我们知道类是结构相同的对象的一种抽象,类中包含字段和方法。同理,Class类是所有类的抽象,所以它包含每个类的构造属性(Constructor)字段属性(Field)和方法属性(Method)。其实,Constructor、Field、Method都是一个类,它们以数组存储Class实例代表的类型信息。Filed类中包含有有关类或接口的单个字段的信息,Constructor类提供关于类的单个构造方法的信息以及对它的访问权限,Method类提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。因此,通过Class实例访问该Class实例对象所代表的的类的类型信息就是反射的基础。
理解了上述之后,再来看下反射机制:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
**所以反射的使用过程大致如下步骤:
A:获取类的Class对象
B:获取需要的属性:构造属性(Constructor对象)字段属性:(Field对象)和方法属性:(Method对象)
C:同过属性对象调方法来创建对象、获取字段或调用方法**

例如:

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args) throws Exception {
        // 获取Person类的Class实例
        Person p = new Person();
        Class c = p.getClass();
        // 获取Person类的有参构造属性对象
        Constructor con = c.getConstructor(String.class);
        // 新建一个Person对象
        Person p2 = (Person) con.newInstance("Most_want");
        p2.show();
        // 获取Person类的名为"name"字段属性对象
        Field field = c.getField("name");
        // 把p2的name字段设置为“小李”
        field.set(p2, "小李");
        p2.show();
        // 获取Person类的名为"show",无参的方法
        Method method = c.getMethod("show", null);
        //通过p2调用Person类的method所表示的方法
        method.invoke(p2, null);
    }
}
//自定义Person类
class Person {
    public String name;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    public void show() {
        System.out.println("Person  name=" + name);
    }
}

我们知道这个JVM加载时把被加载类的所有信息加载到内存,那么private修饰的字段或方法能不能被获取
例如获取当把Person类的”name”私有化后要怎么获取:

import java.lang.reflect.*;

public class Test {
    public static void main(String[] args) throws Exception {
                Person p = new Person();
        Class c = p.getClass();
        //直接获取会抛出异常
        //Field field = c.getField("name");
        //用下面的方法可以获取任何Method属性
        Field field=c.getDeclaredField("name");
        //设置忽略语法检查
        field.setAccessible(true);
        field.set(p, "小李");
        p.show();
    }
}
class Person {
    pricate String name;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    public void show() {
        System.out.println("Person  name=" + name);
    }
}

上面的例子演示了获取私有字段的方法,获取私有方法私有构造的等同。
下面探讨下private 、protected 、public 三种权限的方法属性的获取差别

public class Test {
    public static void main(String[] args) throws Exception {
        // 获取Person类的Class实例
        Person p = new Person();
        Class<? extends Person> c = p.getClass();
        Method m1 = null;
        Method m2 = null;
        Method m3 = null;
        Method m4 = null;
        // 私有方法的获取
        m1 = c.getDeclaredMethod("show1", null);
        m1.setAccessible(true);
        m1.invoke(p, null);

        // 受保护方法的获取
        m2 = c.getDeclaredMethod("show3", null);
        // 无需进行语法设置
        // m2.setAccessible(true);
        m2.invoke(p, null);

        // 最高权限方法的获取
        // 普通方法即可
        m3 = c.getMethod("show2", null);
        // m3 = c.getDeclaredMethod("show2", null);
        // m3.setAccessible(true);
        m3.invoke(p, null);

        // 默认权限方法的获取
        // 此方法抛出异常
        // m4=c.getMethod("show4", null);
        m4 = c.getDeclaredMethod("show4", null);
        // 无需语法设置
        // m4.setAccessible(true);
        m4.invoke(p, null);
    }
}

class Person {
    public Person() {
    }

    private void show1() {
        System.out.println("这是 private viod show1()");
    }

    public void show2() {
        System.out.println("这是 public void show2()");
    }

    protected void show3() {
        System.out.println("这是 protected void show2()");
    }

    void show4() {
        System.out.println("这是 void show2()");
    }

}

由以上例子可以总结:
public修饰的方法,直接获取。
private修饰的方法,需要getDeclaredMethod()获取,并关闭语法检查。
protected修饰的方法,需要getDeclaredMethod()获取,无需关闭语法检查。
默认权限的方法,需要getDeclaredMethod()获取,无需关闭语法检查。
在使用反射的时候,一般我们不知道被反射使用的类型信息,所以etDeclaredMethod()和setAccessible()联用,同理Filed和Constructor的获取一致。

例如:用反射原理制作一个修改任意类对象的字段的方法。

public static void setField(Object obj,String fieldname,Object args)throws Exception{
    Class c=obj.getClass();
    Field field=c.getDeclaredField(fieldname);
    field.setAccessible(true);
    field.set(obj, args);       
}   

动态代理

利用代理可以提供原对象类不具有的额外的功能,例如日志记录,权限检查。Java中利用反射实现的代理就是动态代理。Java中默认包中提供了对接口的代理。代理主要使用java.lang.reflect包中的proxy类和HandlerInvocation接口
用到的方法:
Object invoke(Object proxy, Method method,Object[] args)throws Throwable
在代理实例上处理方法调用并返回结果。proxy是在其上调用方法的代理实例,method是被代理的方法,args是方法的参数如果有的话。

Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
loader是被代理对象的类加载器,interfaces是被代理类的接口数组,h是实现HandlerInvocation接口的引用变量。

以下例子演示了利用代理统计List集合add()耗时:

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;
//自定义InvocationHandler实现类
class MyInvocationHandler implements InvocationHandler {
    //被代理的类
    Object target;

    public MyInvocationHandler(Object target){
        this.target=target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        method.setAccessible(true);
        //添加自定义语句
        long start=System.currentTimeMillis();
        //调用被代理类的方法
        Object result=method.invoke(target, args);
        //添加自定义语句
        long end=System.currentTimeMillis();
        System.out.println("方法调用耗时: "+(end-start)+" (millisecond)");
        return result;
    }
}

public class 静态方法对象调用 {
    public static void main(String[] args) throws Exception {
        //创建接口引用类变量
        List list=new ArrayList();
        //创建自定义InvocationHandler引用变量
        InvocationHandler i=new MyInvocationHandler(list);
        //获取list的代理类
        List proxy=(List)Proxy.newProxyInstance(list.getClass().getClassLoader(), list.getClass().getInterfaces(), i);
        //用代理对象调用方法
        proxy.add("Most_want");

    }
}

由此我们可以总结出代理的基本使用步骤:
A:创建自定义InvocationHandler实现类并重写invoke();
B:创建InvocationHandler引用变量并实例化;
C:创建Proxy实例对象
D:通过C步骤获取的代理对象调用方法。

Class类的 API中常用的几个方法:
创建一个此Class对象代表的类的一个新实例。此方法和调用该类型的空的构造方法创建实例效果一样。
public T newInstance()例如:String a=String.class.newInstance();

获取Class实例代表的(类、接口、数组、基本类型或void)的名称:
String getName()

获取Class实例代表的实体的类加载器:
ClassLoader getClassLoader()

判断Class实例代表的类是否为数组
boolean isArray()

判断Class实例代表的类是否为枚举类
boolean isEnum ( )

获取Class实例代表的实体的超类
Class< ? super T > getSuperclass()

示例
下面的代码利用反射技术打印出一个类的信息:

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

//输入一个完全限定类名,打印出该类的所有字段
public class PrintClass {
    // 被反射的Class对象
    private Class target;

    // 带Class类型参数的构造器
    public PrintClass(Class target) {
        this.target = target;
    }

    // 方法参数为String的构造器,如果指定的字符串无法转换为Class对象,那么抛出异常
    public PrintClass(String target) throws IllegalStateException {
        try {
            this.target = Class.forName(target);
        } catch (Exception e) {
            throw new IllegalStateException("cannot found this Class");
        }
    }

    // 打印该Class的所有信息
    public void printAllMsg() {
        printFields();
        System.out.println("\n\n");
        printConstructors();
        System.out.println("\n\n");
        printMethods();
    }

    // 打印target对象代表的类的所有构造方法
    public void printConstructors() {
        // 获取target的构造器对象数组
        Constructor[] constructors = target.getDeclaredConstructors();
        if (constructors.length == 0)
            return;
        // 遍历该数组,打印每一个构造器的信息
        for (Constructor con : constructors) {
            // 对该构造器对象取消语法检查
            con.setAccessible(true);
            // 构造器的修饰符
            int mod = con.getModifiers();
            String mods = Modifier.toString(mod);
            System.out.print("  " + mods);// 打印修饰符
            // 构造器的名字
            String name = con.getName();
            System.out.print("  " + name);
            // 构造器的参数
            Class[] paramClasses = con.getParameterTypes();
            System.out.print("(");
            // 参数至少为1个才进行打印
            if (paramClasses.length > 0) {
                for (int i = 0; i < paramClasses.length; i++) {
                Class c = paramClasses[i];
                    if (c.isArray()) {
                        Class componentType = c.getComponentType();
                        String[] names = componentType.toString().split("\\.");
                        int index = names.length - 1;
                        String arrName = names[index];
                        System.out.print(" " + arrName + "[]");
                    } else {
                        int off = c.toString().lastIndexOf('.') + 1;
                        String paramClassName = c.toString().substring(off);
                        System.out.print(" " + paramClassName);
                    }
                    if (i < paramClasses.length - 1) {
                        System.out.print(",");
                    }
                }
            }
            System.out.println(");");
        }

    }

    // 打印target对象代表的类的所有字段
    public void printFields() {
        // 获取target的字段对象数组
    Field[] fields = target.getDeclaredFields();
        if (fields.length == 0)
            return;
        // 遍历该数组,打印每一个字段的信息
        for (Field fie : fields) {
            // 对该字段对象取消语法检查
            fie.setAccessible(true);
            // 字段的修饰符
            int mod = fie.getModifiers();
            String mods = Modifier.toString(mod);
            System.out.print("  " + mods);// 打印修饰符
            // 字段的类型
            Class c = fie.getType();
            if (c.isArray()) {
                Class componentType = c.getComponentType();
                String[] names = componentType.toString().split("\\.");
                int index = names.length - 1;
                String arrName = names[index];
                System.out.print(" " + arrName + "[]");
            } else {
                String fieldClassName = c.toString();
                Pattern p = Pattern.compile("[a-zA-Z]+$");
                Matcher m = p.matcher(fieldClassName);
                if (m.find())
                    fieldClassName = m.group();
                System.out.print(" " + fieldClassName);
            }
            // 字段的名字
            String name = fie.getName();
            System.out.println(" " + name + ";");

        }

    }

    // 打印target对象代表的类的所有方法
    public void printMethods() {
        // 获取target的方法对象数组
        Method[] methods = target.getDeclaredMethods();
        if (methods.length == 0)
            return;
        // 遍历该数组,打印每一个方法信息
        for (Method method : methods) {
            // 对该方法对象取消语法检查
            method.setAccessible(true);

            // 方法的修饰符
            int mod = method.getModifiers();
            String modifiers = Modifier.toString(mod);
            System.out.print("  " + modifiers);// 打印修饰符

            // 方法的返回类型
            Class returnClass = method.getReturnType();
            String returnClassName = null;
            if (returnClass.equals(void.class)) {
                returnClassName = "void";
            } else if (returnClass.isArray()) {
                Class componentType = returnClass.getComponentType();
                String[] names = componentType.toString().split("\\.");
                int index = names.length - 1;
                String arrName = names[index];
                returnClassName = arrName + "[]";
            } else {
                int offset = returnClass.toString().lastIndexOf('.') + 1;
                returnClassName = returnClass.toString().substring(offset);
            }
            System.out.print(" " + returnClassName);

            // 方法名
            String name = method.getName();
            System.out.print(" " + name);

            // 方法参数
            Class[] paramClasses = method.getParameterTypes();
            System.out.print("(");
            // 参数至少为1个才进行打印
            if (paramClasses.length > 0) {
                for (int i = 0; i < paramClasses.length; i++) {
                    Class c = paramClasses[i];
                    if (c.isArray()) {
                        Class componentType = c.getComponentType();
                        String[] names = componentType.toString().split("\\.");
                        int index = names.length - 1;
                        String arrName = names[index];
                        System.out.print(" " + arrName + "[]");
                    } else {
                        int off = c.toString().lastIndexOf('.') + 1;
                        String paramClassName = c.toString().substring(off);
                        System.out.print(" " + paramClassName);
                    }
                    if (i < paramClasses.length - 1) {
                        System.out.print(",");
                    }
                }
            }
            System.out.println(" )");

        }
    }

        public static void main(String[] args) throws IllegalStateException {
        new PrintClass(String.class).printAllMsg();
        // new PrintClass("java.lang.Object").printAllMsg();
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值