JAVA反射机制

[toc]

反射机制:

允许程序在运行时取得任何一个已知名称的class的内部信息,容许程序在运行时加载、探知、使用编译期间未知的class。加载一个运行时才得知名称的Class,获得其完整结构。

  1. 获取到Java中要反射类的字节码
    获取字节码方式:(实例化Class类对象的方法)
  • Class.forName(className)
  • 类名.class
  • this.getClass()
  1. 将字节码中的方法,变量,构造函数等映射成相应的 MethodFiledConstructor 等类,这些类提供了丰富的方法可以被我们所使用。

Object类是Java反射机制的源头,即可以通过对象反射求出类的名称。
Class类使java允许通过一个对象来找到其所在类的信息。

  • 实例化对象
    类中无参构造方法存在

    PS:操作中类(Person)中必须存在无参构造方法,否则无法实例化。否则必须明确指定要调用的构造方法,并传递参数。
    操作步骤:
    • 1.通过Class类中的 getConstructors()取得本类中全部构造方法
    • 2.向构造方法中传递一个对象数组,包含了构造方法中所需的各个参数。
    • 3.通过 Constructor实例化对象
      类中无参构造方法不存在

反射借助的类


反射的功能


反射机制的深入——取得类的结构

通过反射得到一个类的完整结构,需要使用 `java.lang.reflect `中的几个类
  1. 通过反射调用类的方法
public Method getMethod(String name,Class<?> ... parameterTypes) 
                            throws NoSuchMehtodException,SercurityException
public Object invoke(Object obj,Object...args) 
                     throws IllegalAccessException,
                           IllegalArgumentException,
                           InvocationTargetException
无参
有参
  1. 通过反射调用类setter及getter
    setter及getter方法是一个标准的属性访问方法,如果一个类的属性被封装,必须通过setter及getter设置取得,此方法的操作之所以这样规定,主要由于反射机制可以给予支持。
package org.lxh.demo15.invokedemo ;
import java.lang.reflect.Method ;
public class InvokeSetGetDemo{
    public static void main(String args[]){
        Class<?> c1 = null ;
        Object obj = null ;
        try{
            c1 = Class.forName("org.lxh.demo15.Person") ;   // 实例化Class对象
        }catch(Exception e){}
        try{
            obj = c1.newInstance() ;
        }catch(Exception e){}
        setter(obj,"name","Emma",String.class) ;    // 调用setter方法
        setter(obj,"age",21,int.class) ;    // 调用setter方法
        System.out.print("姓名:") ;
        getter(obj,"name") ;
        System.out.print("年龄:") ;
        getter(obj,"age");
    }
    /**
        Object obj:要操作的对象
        String att:要操作的属性
        Object value:要设置的属性内容
        Class<?> type:要设置的属性类型
    */
    public static void setter(Object obj,String att,Object value,Class<?> type){
        try{
            Method met = obj.getClass().getMethod("set"+initStr(att),type) ;    // 得到setter方法
            met.invoke(obj,value) ; // 设置setter的内容
        }catch(Exception e){
            e.printStackTrace() ;
        }
    }
    public static void getter(Object obj,String att){
        try{
            Method met = obj.getClass().getMethod("get"+initStr(att)) ; // 得到setter方法
            System.out.println(met.invoke(obj)) ;   // 调用getter取得内容
        }catch(Exception e){
            e.printStackTrace() ;
        }
    }
    public static String initStr(String old){   // 将单词的首字母大写
        String str = old.substring(0,1).toUpperCase() + old.substring(1) ;
        return str ;
    }
}
  1. 通过反射调用属性
    得到公共属性:
public Field getField(String name) 
                    throws NoSuchFieldException,SecurityException

得到本类属性

public Field getDeclaredField(String name) 
                    throws NoSuchFieldException,SecurityException

取得属性内容

public Object get(Object obj) 
                    throws IllegalArgumentException,IllegalAccessException

设置属性内容

public Object set(Object obj,Object value) 
                    throws IllegalArgumentException,IllegalAccessException

访问私有属性必须让这个属性可见(设置为true)

public void setAccessible(boolean flag) 
                    throws SecurityException
  1. 通过反射操作数组
    Class类中存在以下一个方法
public Class<?> getComponentType() 

得到数组下标内容:get(Objcet array,int index);
修改内容:set(Objcet array,int index,Object value);
开辟新数组:newInstance(Class<?> componentType,int ... dimensions)

取得数组信息并修改内容:
修改的过程就是一个创建的过程,创建一个新的,然后把原有内容拷贝到新数组中。

package org.lxh.demo15.invokedemo ;
import java.lang.reflect.Array ;
public class ChangeArrayDemo{
    public static void main(String args[]) throws Exception{
        int temp[] = {1,2,3} ;// 声明一整型数组
        int newTemp[] = (int []) arrayInc(temp,5) ; // 重新开辟空间5
        print(newTemp) ;
        System.out.println("\n-------------------------") ;
        String t[] = {"lxh","mldn","mldnjava"} ;
        String nt[] = (String [])arrayInc(t,8) ;
        print(nt) ;
    }
    public static Object arrayInc(Object obj,int len){
        Class<?> c = obj.getClass() ;
        Class<?> arr = c.getComponentType() ;   // 得到数组的
        Object newO = Array.newInstance(arr,len) ;  // 开辟新的大小
        int co = Array.getLength(obj) ;
        System.arraycopy(obj,0,newO,0,co) ; // 拷贝内容
        return newO ;
    }
    public static void print(Object obj){   // 数组输出
        Class<?> c = obj.getClass() ;
        if(!c.isArray()){   // 判断是否是数组
            return;
        }
        Class<?> arr = c.getComponentType() ;
        System.out.println(arr.getName()+"数组的长度是:" + Array.getLength(obj)) ;     // 输出数组信息
        for(int i=0;i<Array.getLength(obj);i++){
            System.out.print(Array.get(obj,i) + "、") ;  // 通过Array输出
        }
    }
}

反射机制的应用

通过Modifier.toString()还原修饰符

        Class<?> c1 = null ;        // 声明Class对象
        try{
            c1 = Class.forName("org.lxh.demo15.Person") ;   // 实例化对象
        }catch(ClassNotFoundException e){
            e.printStackTrace() ;
        }
        Constructor<?> con[] = c1.getConstructors() ;   // 取得一个类中的全部构造
        for(int i=0;i<con.length;i++){
            Class<?> p[] = con[i].getParameterTypes() ;     // 得到构造方法中的全部参数
            System.out.print("构造方法:" ) ;     // 输出构造,直接打印
            int mo = con[i].getModifiers() ; // 得到所要的访问权限
            System.out.print(Modifier.toString(mo) + " ") ; // 得到修饰符
            System.out.print(con[i].getName()) ;    // 取得构造方法的名字
            System.out.print("(") ;
            for(int j=0;j<p.length;j++){
                System.out.print(p[j].getName() + " arg" + i) ;
                if(j<p.length-1){
                    // 判断此是否是最后一个参数
                    System.out.print(",");  // 输出“,”
                }
            }
            System.out.println("){}") ;
        }

编译方式

动态编译最大限度发挥了Java的灵活性,体现了多态的应用,有以降低类之间的藕合性


反射机制优缺点

  • 优点:就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中 它的灵活性就表现的十分明显。

  • 缺点:对性能有影响。
    使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。并且它饶过了源码,会干扰原来的内部逻辑。


动态代理

代理设计:一个操作的接口有两个子类,一个是真实主题的实现类,另一个是代理类。代理实现类要完成比真实主题实现类更多的内容,本身还需要处理一些与具体业务有关的程序代码。

静态代理

import java.lang.reflect.InvocationHandler ;
import java.lang.reflect.Proxy ;
import java.lang.reflect.Method ;
interface Subject{
    public String say(String name,int age) ;    // 定义抽象方法say
}
class RealSubject implements Subject{   // 实现接口
    public String say(String name,int age){
        return "姓名:" + name + ",年龄:" + age ;
    }
};
class MyInvocationHandler implements InvocationHandler{
    private Object obj ;
    public Object bind(Object obj){
        this.obj = obj ;    // 真实主题类
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this) ;
    }
    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
        Object temp = method.invoke(this.obj,args) ;    // 调用方法
        return temp ;
    }
};
public class DynaProxyDemo{
    public static void main(String args[]){
        Subject sub = (Subject)new MyInvocationHandler().bind(new RealSubject()) ;
        String info = sub.say("Emma",21) ;
        System.out.println(info) ;
    }
};

InvocationHandler接口
刻意将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉ProxySubject.

public interface InvocationHandler{
          public Object invoke(Object proxy,Method method,Object[] args)  throws Throwable
}
  • Object proxy:被代理的对象
  • Method method:要调用的方法
  • Object[] args:方法调用时所需要的参数

Proxy类
专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,提供了如下操作方法

public static Objcet newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException
  • ClassLoader loader:类加载器
  • Class<?>[] interfaces:得到全部的接口
  • InvocationHandler h:得到InvocationHandler接口的子类实例

ArrayList的动态代理类

final List<String> list = new ArrayList<String>();
        List<String> proxyInstance = (List<String>) Proxy.newProxyInstance(list.getClass().getClassLoader(),
                list.getClass().getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        return method.invoke(list, args);
                    }
                });
        proxyInstance.add("你好");
        System.out.println(list);

动静态代理的区别&使用场景

  • 静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类
  • 静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。
  • 动态代理是实现 JDK 里的 InvocationHandler 接口的 invoke 方法,但注意的是代理的是接口,也就是你的业务类必须要实现接口,通过 Proxy 里的 newProxyInstance 得到代理对象。
  • 还有一种动态代理 CGLIB ,代理的是类,不需要业务类继承接口,通过派生的子类来实现代理。通过在运行时,动态修改字节码达到修改类的目的。
  • AOP 编程就是基于动态代理实现的,比如著名的 Spring 框架、 Hibernate 框架等都是动态代理的使用例子。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容