• java笔记反射进阶之总结与详解


    一.反射进阶之动态设置类的私有域

    "封装"是Java的三大特性之一,为了能更好保证其封装性,我们往往需要将域设置成私有的,

    然后通过提供相对应的set和get方法来操作这个域。但是我们仍然可以用java的反射机制来

    修改类的私有域,由于修改类的私有域会破坏Java"封装"的特性,故请慎重操作。

    主要技术:
        Field类提供有关类或接口的单个字段的信息,以及对它的动态访问权限。
        访问的字段可能是一个类(静态)字段或实例字段。
       
            常用方法:
            set(Object obj,Object value)-----: 将指定对象变量上此Field对象表示的字段设置为指定的新值
            setBoolean(Object obj,boolean z)-: 将字段使得设置为指定对象上的一个boolean值
            setDouble(Object obj,double d)---: 将字段的值设置为指定对象上的一个double值
            setInt(Object obj,int i)---------: 将字段的值设置为指定对象上的一个int值
            setAccessible(boolean flag)------: 将此对象的accessible标志设置为指定的布尔值
           
           注:对于私有域,外部类调用的时候一定要使用setAccessible()方法将其可见性设置为true才能设置新值,
                否则将会抛出异。

    --支持知识共享,转载请标注地址"http://www.cnblogs.com/XHJT/p/3922160.html "——和佑博客园,谢谢~~-- 

    实例代码:

    package com.xhj.reflection_excetion;
    
    import java.lang.reflect.Field;
    
    /**
     * 动态修改类的私有域
     * 
     * @author XIEHEJUN
     * 
     */
    public class DynamicChangePrivate {
        private String userName;
        private int userAge;
        private String userAddress;
        private boolean userGender;
    
        public DynamicChangePrivate(String userName, int userAge,
                String userAddress, boolean userGender) {
            super();
            this.userName = userName;
            this.userAge = userAge;
            this.userAddress = userAddress;
            this.userGender = userGender;
        }
    
        public DynamicChangePrivate() {
            super();
        }
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public int getUserAge() {
            return userAge;
        }
    
        public void setUserAge(int userAge) {
            this.userAge = userAge;
        }
    
        public String getUserAddress() {
            return userAddress;
        }
    
        public void setUserAddress(String userAddress) {
            this.userAddress = userAddress;
        }
    
        public boolean isUserGender() {
            return userGender;
        }
    
        public void setUserGender(boolean userGender) {
            this.userGender = userGender;
        }
    
        /**
         * 根据获得的性别的真假,获取其String值: true--为男 false--为女
         * 
         * @param userGender
         * @return
         */
        public String getGender(boolean userGender) {
            if (userGender) {
                return "男";
            } else {
                return "女";
            }
        }
    
        public static void main(String[] args) {
            DynamicChangePrivate user = new DynamicChangePrivate("小黑", 21,
                    "北京路华西小区3单元446", true);
            Class<?> clazz = user.getClass();
            System.out.println("通过反射取得的对象全称为:" + clazz.getName());
            try {
    
                // 获取要修改的类的字段名称
                Field userName = clazz.getDeclaredField("userName");
                Field userAge = clazz.getDeclaredField("userAge");
                Field userAddress = clazz.getDeclaredField("userAddress");
                Field userGender = clazz.getDeclaredField("userGender");
    
                // 修改并输出新旧名称
                System.out.print("原名称为:" + user.getUserName());
                userName.set(user, "晓晓");
                System.out.println("\t\t\t修改后的名称为:" + user.getUserName());
    
                // 修改并输出新旧年龄
                System.out.print("原年龄为:" + user.getUserAge());
                userAge.set(user, 24);
                System.out.println("\t\t\t修改后的年龄为:" + user.getUserAge());
    
                // 修改并输出新旧地址
                System.out.print("原地址为:" + user.getUserAddress());
                userAddress.set(user, "石景山八角南里3单元506");
                System.out.println("\t修改后的地址为:" + user.getUserAddress());
    
                // 修改并输出新旧性别
                System.out.print("原性别为:" + user.getGender(user.isUserGender()));
                userGender.set(user, false);
                System.out.println("\t\t\t修改后的性别为:"
                        + user.getGender(user.isUserGender()));
    
            } catch (SecurityException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
    
        }
    
    }

    结果为:

    image

    注:任何类型的域,都可以通过set(Object obj, Object value) 方法将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
    但是Field本身也提供了其他类型相对应的set方法,如 setBoolean(Object obj, boolean z),setDouble(Object obj, double d)

    等。另外,通过Field也可以设置public.protected域,但一般情况下很少这么设置,尤其是public域。在这里特别要注意的是:一定

    要明确修改的含义,不要轻易的通过反射来修改类的私有域,因为这破坏了java面向对象"封装"的特性。

    二、反射进阶之动态调用类的方法

    我们知道Java是一种面向对象的语言,对他而言,一切都是对象,因此要调用类的方法,只能通过建立类的对象来调用。当然如果是
    静态的方法,那就可以直接通过类本身来调用,而不需要建立类的对象。那么还有没有其他可以调用类方法的方式呢??

    在Java的反射的机制中,提供了比较另类的调用方式,既可以根据需要指定要调用的方法,而不必在编程时确定。调用的方法不仅权限于public
    的,还可以是private的。

    Method类提供类或接口上单独某个方法(以及如何访问该方法)的信息,所反映的方法可能是类方法或实例方法(包括抽象方法)。它允许在匹配
    要调用的实参与底层方法的形参时进行扩展转换,但是如果要进行收缩转换,则会抛出"非法参数异常"--IllegalArgumentExcetion。使用invoke()
    可以实现动态调用方法:
       public Object invoke(Object obj,Object...args)throws IllegalArgumentException,IllegalAccessException,InvocationTargetExcetion
        obj--要调用的方法的类对象
        args--方法调用的参数

       
       注:对于私有域,外部类调用的时候一定要使用setAccessible,并且设置为true
       
    实例代码:

    package com.xhj.reflection_excetion;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    /**
     * 实现动态调用类的方法
     * 
     * @author XIEHEJUN
     * 
     */
    public class DynamicCallMethod {
    
        public static void main(String[] args) {
            try {
                // 运行时动态调用Math的abs()静态方法
                System.out.println("运行时动态调用Math的abs()静态方法");
                Method meth = Math.class.getDeclaredMethod("abs", Integer.TYPE);
                Integer a = (Integer) meth.invoke(null, new Integer(-1));
                System.out.println("-1的绝对值为:" + a);
    
                // 运行时动态调用String的非静态方法contains()
                System.out.println("运行时动态调用String的非静态方法contains()");
                Method strMeth = String.class.getDeclaredMethod("contains",
                        CharSequence.class);
                boolean str = (Boolean) strMeth.invoke(new String("xhjit"), "xhj");
                System.out.println("xhjit中是否包含有xhj——" + str);
    
            } catch (SecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
        }
    
    }

    结果为:

    image

    三、反射进阶之动态实例化类

    在Java中,要实例化一个类即创建一个类的对象,需要通过构造方法来实现。但是在Java中还可以使用另外一种方式来实现,

    那就是通过反射来实例化一个类。
       
        Constructor是Java中提供类的单个构造方法的信息以及访问权限的封装类。
        它允许在将实参与带有底层构造方法的形参的newInstance()匹配时进行扩展转换,
        但是如果发生收缩转换,则抛出IllegalArgumentExcetion。newInstance()方法可以
        使用指定的参数来创建对象:
       public T newInstance(Object...initargs)throws InstantiationException,IllegalAccessException,

                                IllegalArgumentException,InvocationTargetException
           
        initargs: 将作为变量传递给构造方法调用的对象数组。
       
        注:对于私有域,外部类调用的时候一定要使用setAccessible,并且设置为true。
       
    代码实例为:

    package com.xhj.reflection_excetion;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    
    /**
     * 动态实例化类
     * 
     * @author XIEHEJUN
     * 
     */
    public class DynamiCreateClass {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
    
            try {
                // 动态建立一个String对象,
                Constructor<?> construtor = String.class
                        .getConstructor(String.class);
                String str = (String) construtor.newInstance("000123");
                System.out.println(Integer.parseInt(str));
    
                // 动态建立一个txt文件,并将上面初始化后的String值写入文件的当中
                construtor = File.class.getConstructor(String.class);
                String url = "C:\\Users\\XIEHEJUN\\Desktop\\XHj.txt";
                File file = (File) construtor
                        .newInstance(url);
                file.createNewFile();
                if (file.exists()) {
                    str += "---文件创建成功";
                    System.out.println(str);
                    OutputStream os = new FileOutputStream(url);
                    os.write(str.getBytes());
                    os.close();
                } else {
                    System.out.println("创建文件失败");
                }
            } catch (SecurityException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    
    }

    结果为:

    控制台输出

    image

    生成的文件:

    image

    注:在java中有两种不用new就可以建立类对象的方法,即Class.newInstance()Constructor.newInstance();
           区别在于:

                      前者只能调用无参构造方法,而后者却能调用有参构造方法;
                      且前者需要被调用的构造方法可见,后者则在特定情况下运行调用不可见的构造方法

    四、反射进阶之自定义可变数组工具类 

    在Java中,要创建可变数组可通过ArraryList类来实现。除此之外,我们也可以用自定义的方法来实现可变数组。
    这里,我们将使用Java的反射机制实现一个工具类,通过这个工具类,我们就能实现可变数组的创建。

    主要技术:


        Array类提供了动态创建和访问Java数组的方法。它允许在执行get,set操作期间进行扩展转换,但若发生收缩
        转换将抛出IllegalArgumentExcetion。为了创建新的数组对象,需要使用newInstance()方法,它可以根据指定
        的元素类型和长度创建新的数组:


       public static Object newInstance(Class<?> componentType,int length)throws NegativeArraySizeException


            componentType: 表示新数组的组件类型的Class对象
            length: 新数组的长度

         注:Java 中的数组不管是几维的,都是Object类型的s

    代码实例:
    可变数组工具类的实现:

    package com.xhj.reflection_excetion;
    
    import java.lang.reflect.Array;
    
    /**
     * 用反射机制实现可变数组工具类
     * 
     * @author XIEHEJUN
     * 
     */
    public class VariableArrayUtil {
        /**
         * 增加的数组长度值
         */
        private int addLength;
        /**
         * 需要增加长度的原数组
         */
        private Object array;
    
        public int getaddLength() {
            return addLength;
        }
    
        public Object getArray() {
            return array;
        }
    
        /**
         * 可变数组初始化
         * @param addLength 需要增加的数组的长度
         * @param array 需要增加长度的原数组
         */
        public VariableArrayUtil(int addLength, Object array) {
            super();
            this.addLength = addLength;
            this.array = array;
        }
    
        /**
         * 可变数组的实现
         * 
         * @return
         */
        public Object newArrary() {
            Class<?> clazz = array.getClass();
            if (clazz.isArray()) {
                Class<?> type = clazz.getComponentType();
                int length = Array.getLength(array);
                Object new_Array = Array.newInstance(type, length + addLength);
                System.arraycopy(array, 0, new_Array, 0, length);
                return new_Array;
            }
            return null;
        }
    
    }

    测试类:

    package com.xhj.test;
    
    import com.xhj.reflection_excetion.VariableArrayUtil;
    
    /**
     * 可变数组工具类的测试类
     * 
     * @author XIEHEJUN
     * 
     */
    public class Test {
    
        public static void main(String[] args) {
            int[] a = new int[10];
            System.out.println("原数组为:");
            for (int i = 0; i < 10; i++) {
                a[i] = i;
                System.out.print("  " + a[i]);
            }
            System.out.println("\n数组长度为:" + a.length);
            VariableArrayUtil util = new VariableArrayUtil(5, (Object) a);
            int[] b = (int[]) util.newArrary();
            System.out
                    .println("==================================================\n"
                            + "更改后的数组长度为:" + b.length);
            for (int i = 10; i < 15; i++) {
                b[i] = i;
            }
            System.out.println("更改后的数组为:");
            for (int i : b) {
                System.out.print("  " + i);
            }
        }
        
    }

    结果为:

    image

    五、反射进阶之重写toString方法


    为了方便输出对象,在Object中定义了toString的方法,其默认值由类名和哈希码组成。但是很多时候,为了能更好的满足我们的需求,

    我们都是需要重写这个方法的。下面我们将利用Java的反射机制,重写这个方法,并输出类的相关信息。

    代码实例:

    package com.xhj.reflection_excetion;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.lang.reflect.Type;
    
    /**
     * 利用反射重写Object的toString方法
     * 
     * @author XIEHEJUN
     * 
     */
    public class RewriteToString {
        public String toString(Object obj) {
            Class<?> clazz = obj.getClass();
            // 建立一个容器用来存储类的信息
            StringBuilder strBuilder = new StringBuilder();
            strBuilder.append("以下是类的信息:");
            // 类名
            String className = clazz.getSimpleName();
            // 包名
            Package packageName = clazz.getPackage();
            // 公共构造方法
            Constructor<?>[] constructors = clazz.getConstructors();
            // 公共域
            Field[] fields = clazz.getFields();
            // 接口
            Type[] interfaces = clazz.getInterfaces();
            // 公共方法
            Method[] methods = clazz.getMethods();
    
            strBuilder.append("\n此类的简单名称为--" + className);
    
            strBuilder.append("\n此类的包名为--" + packageName);
    
            strBuilder.append("\n此类的公共构造方法有:");
            if (constructors.length > 0) {
                for (Constructor<?> constructor : constructors) {
                    strBuilder.append("\n\t" + constructor);
                }
            } else {
                strBuilder.append("空");
            }
    
            strBuilder.append("\n此类的公共域有:");
            if (fields.length > 0) {
                for (Field field : fields) {
                    strBuilder.append("\n\t" + field);
                }
            } else {
                strBuilder.append("空");
            }
    
            strBuilder.append("\n此类的接口有:");
            if (fields.length > 0) {
                for (Type type : interfaces) {
                    strBuilder.append("\n\t" + type);
                }
            } else {
                strBuilder.append("空");
            }
    
            strBuilder.append("\n此类的公共方法有:");
            if (methods.length > 0) {
                for (Method method : methods) {
                    strBuilder.append("\n\t" + method);
                }
            } else {
                strBuilder.append("空");
            }
            return strBuilder.toString();
        }
    
        public static void main(String[] args) {
            RewriteToString rts = new RewriteToString();
            System.out.println(rts.toString(new StringBuilder()));
        }
    
    }

    结果为:

    image

    注:在实际开发当中,一般而言都是需要重写toString方法的,为了更好的开发,可使用Commons Lang 组件提供的工具类来重写此方法。

    六、反射进阶之动态代理


       代理是Java中很重要的一个特性,使用代理可以在程序运行时创建一个实现指定接口的新类。一般而言,我们只有在程序编译时

        无法确定需要使用哪些接口时才会使用代理机制。代理更多的是用在系统的开发上,它可以为工具类提供更加灵活的特性。

        InvocationHandle 接口是代理实例的调用处理程序实现的接口。每个代理实例都具有一个关联的调用处理程序。

       对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的invoke()方法:


        Object invoke(Object proxy,Method method,Object[]args)throws Throwable
        proxy:代理类
        method: 代理实例上要被调用的方法
        args: 代理实例上方法调用的参数数组

        Proxy接口提供了用于创建动态代理类和实例的静态方法,它是所有动态代理类的父类。


        获得一个指定接口的代理类实例:
       public static Object newProxyInstance(ClassLoader loader,Class<?> [] interfaces,InvocationHandle h)throws IllegalArgumentException
        loader:定义代理类的类加载器
        interfaces:代理类要实现的接口列表
        h:指派方法调用的代理处理程序

    代码实例:

    接口:

    package com.xhj.reflection_excetion.dynamicProxy.bean;
    
    /**
     * 代理类和被代理类的共同接口
     * 
     * @author XIEHEJUN
     * 
     */
    public interface DoBusiness {
        /**
         * 商品交易方式
         */
        public void trading();
    }

    被代理类:

    package com.xhj.reflection_excetion.dynamicProxy.service;
    
    import com.xhj.reflection_excetion.dynamicProxy.bean.DoBusiness;
    
    /**
     * 被代理类--厂家
     * 
     * @author XIEHEJUN
     * 
     */
    public class Product implements DoBusiness {
    
        @Override
        public void trading() {
            System.out.println("厂家直销");
        }
    
    }

    代理处理器:

    package com.xhj.reflection_excetion.dynamicProxy.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    /**
     * 代理处理器--商家代理
     * 
     * @author XIEHEJUN
     * 
     */
    public class DynamicProxy implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            System.out.println("商家代理定点销售");
            return null;
        }
    }

    测试类:

    package com.xhj.reflection_excetion.dynamicProxy;
    
    import java.lang.reflect.Proxy;
    
    import com.xhj.reflection_excetion.dynamicProxy.bean.DoBusiness;
    import com.xhj.reflection_excetion.dynamicProxy.proxy.DynamicProxy;
    import com.xhj.reflection_excetion.dynamicProxy.service.Product;
    
    public class Test {
    
        public static void main(String[] args) {
            DoBusiness p = new Product();
            System.out.print("没有启用代理模式---");
            p.trading();
            ClassLoader loader = p.getClass().getClassLoader();
            p = (DoBusiness) Proxy.newProxyInstance(loader,
                    Product.class.getInterfaces(), new DynamicProxy());
            System.out.print("启用动态代理模式---");
            p.trading();
        }
    
    }

    结果为:

    image

    注:
    java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:

    Interface InvocationHandler:该接口中仅定义了一个方法Object:invoke(Object obj,Method method, Object[] args)。
                                                在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的trading(),

                                                args为该方法的参数数组。这个抽象方法在代理类(代理处理类)中动态实现。
    Proxy:该类即为动态代理类。
        Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):
            返回代理类的一个实例,返回后的代理类可以当作被代理类使用 (可使用被代理类的在接口中声明过的方法)。

    总结:
      动态代理类是一个实现在创建类并运行时指定接口列表的类,
        1.代理接口是代理类所实现的一个接口。
        2.代理实例是代理类的一个实例。
        3.每一个代理实例都有一个关联的调用处理程序对象,它可以实现接口InvocationHandler。
        4.通过调用代理处理器中的Invoke方法实现代理,并传递代理实例,识别调用方法以及方法上的参数数组。
        5.调用对象加载器以及代理处理器中的方法,并以代理实例为结果返回。

    知识重在总结和梳理,只有不断地去学习并运用,才能化为自己的东西。由于本人进阶猿类时间尚短,故此博客即是我学习,工作的笔记,也是和大家交流,相互提升技术的平台~希望大家不吝赐教~~ --但管努力,莫问前程,事在人为,功不唐捐。--和佑博客园
  • 相关阅读:
    easyUI之tree
    MSSQL索引优化
    MongoDB学习笔记(一) MongoDB介绍及安装
    项目经理必备的11种人际关系技能
    http协议详细介绍
    ERP存储过程
    UVA1339 UVALive3213 POJ2159 ZOJ2658 Ancient Cipher【密码】
    UVA1588 UVALive3712 POJ3158 Kickdown
    UVA1588 UVALive3712 POJ3158 Kickdown
    UVA10340 POJ1936 ZOJ1970 All in All【字符串匹配】
  • 原文地址:https://www.cnblogs.com/XHJT/p/3922160.html
Copyright © 2020-2023  润新知