• Java基础之反射


    Java反射是指运行时获取类信息,进而在运行时动态构造对象、调用对象方法及修改对象属性的机制。百度百科的定义:“JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

    一、反射的用途

    Java的反射机制可以做3件事:运行时创建对象、运行时调用方法、运行时读写属性。进而实现以下功能:
    调用一些私有方法,实现黑科技。比如双卡短信发送、设置状态栏颜色、自动挂电话等。

    实现序列化与反序列化,比如PO的ORM,Json解析等。

    实现跨平台兼容,比如JDK中的SocketImpl的实现。

    通过xml或注解,实现依赖注入(DI),注解处理,动态代理,单元测试等功能。比如Retrofit、Spring或者Dagger。

    二、Java反射的优缺点

    优点: 

    (1)能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性。 
    (2)与Java动态编译相结合,可以实现无比强大的功能 

    缺点: 
    (1)使用反射的性能较低 
    (2)使用反射相对来说不安全 
    (3)破坏了类的封装性,可以通过反射获取这个类的私有方法和属性 

    三、Java反射的用法

    主要反射API,用来生成在当前JAVA虚拟机中的类、接口或者对象的信息。

    ●Class类:反射的核心类,可以获取类的属性,方法等内容信息。

    ●Field类:Java.lang.reflect.表示类的属性,可以获取和设置类的中属性值。

    ●Method类:Java.lang.reflect。表示类的方法,它可以用来获取类中方法的信息或者执行方法

    Construcor类:Java.lang.reflect。表示类的构造方法。

    使用步骤:

    ●获得想操作类的Java.lang.Class对象
    ●调用Class的方法
    ●使用反射API来操作这些信息

    Class类没有构造方法,获取其对象的三种方法:

    对象.getClass()

    类.class

    Class.forName();其中,forName( )方法需要重点掌握,因为它可以在类不确定的情况下实例化Class,更具灵活性。

    class Test {   
    
    }   
    
    public class Demo {   
        public static void main(String[] args) {   
            //方式一:   
            Test t = new Test();   
            Class<? extends Test> c1 = t.getClass();   
            System.out.println(c1);   
    
            //方式二:   
            //为了避免特殊性,这里不用Test类,而用java库中的String类   
            Class<String> c2 = String.class;   
            System.out.println(c2);   
    
            //方式三:   
            //forName()方法会抛出异常   
            Class<?> c3 = null;   
            try {   
                c3 = Class.forName("Test");   
            } catch (ClassNotFoundException e) {   
                e.printStackTrace();   
            }   
            System.out.println(c3);   
        }   
    }

    常用反射api示例:

    public class Demo {   
        //下面的几个方法抛出来的异常太多,为了代码的紧凑性,这里就直接抛给虚拟机了   
        public static void main(String[] args) throws Exception {   
            Class<?> c = null;   
            try {   
                c = Class.forName("java.lang.String");   
            } catch (ClassNotFoundException e) {   
                e.printStackTrace();   
            }   
            char[] ch = {'h','e','l','l','o'};   
            String s = null;   
            //获得Class类对象的有参构造方法,括号里面参数的写法是:类型.class   
            Constructor<?> con = c.getConstructor(char[].class);   
            //用此构造方法构造一个新的字符串对象,参数为一个char数组   
            s = (String) con.newInstance(ch);   
            System.out.println("构造的字符串:" + s);   
    
    //获得Class类对象的有参构造方法,括号里面参数的写法是:类型.class   
            Constructor<?> con = c.getConstructor(char[].class);   
            //用此构造方法构造一个新的字符串对象,参数为一个char数组   
            s = (String) con.newInstance(ch);   
            System.out.println("构造的字符串:" + s);   
    
    //这里的getConstructors()方法返回的是一个Constructor数组   
            Constructor<?>[] cons = c.getConstructors();   
            System.out.println(Arrays.toString(cons));  
     
            Class<?>[] in = c.getInterfaces();   
            System.out.println(Arrays.toString(in)); 
    //只有一个父类
            Class<?> su = c.getSuperclass();   
            System.out.println(su); 
    //获取所有方法
        Method[] m = c.getMethods();    
            for (int i = 0; i < m.length; i++) {   
                System.out.println(m[i]);   
            }   
    //获取所有属性
        Field[] f = c.getDeclaredFields();   
            for (int i = 0; i < f.length; i++) {   
                System.out.println(f[i]);   
            }   
    
        }   
    }    
    class Person {   
        private String name;   
    
        public Person(String name) {   
            this.name = name;   
        }   
    
        public String toString() {   
            return "姓名: " + this.name;   
        }   
    }   
    
    public class Demo {   
        public static void main(String[] args) throws Exception {   
            Person p = new Person("王二狗");   
            System.out.println(p);   
            Class<?> c = p.getClass();   
    
            //定义要修改的属性   
            Field f = c.getDeclaredField("name");   
            f.setAccessible(true);   
            //修改属性,传入要设置的对象和值   
            f.set(p, "张二蛋");   
            System.out.println(p);   
    
       //getMethod()方法需要传入方法名,和参数类型   
            Method m1 = c.getMethod("print", int.class);   
            //invoke()表示调用的意思,需要传入对象和参数   
            m1.invoke(p, 10);   
    
            Method m2 = c.getMethod("say", String.class);   
            //这里的null表示不由对象调用,也就是静态方法   
            m2.invoke(null, "你妹");   
        }   
    }

    四、相关问题:java中如何让一个方法不能被反射调用?

    AccessibleObject的setAccessible方法可以使一个private方法(字段)能够被invoke方法调用;同时这种调用存在特例,如Class类的Constructord对象。那么:
    1.我可以写一个方法(字段),使它被setAccessible(true)时抛出SecurityException吗?
    2.存在如同Class类的Constructord对象这样可以抛出SecurityException的方法吗?
    3.如果问题“1”的答案为否,那么Class类的Constructord对象是以何种方式成为特例的呢?


    1.我可以写一个方法(字段),使它被setAccessible(true)时抛出SecurityException吗?
    我的理解是“不能”,除非你改写jdk源代码。即使你使用了java的安全管理器,也只能管理自己代码的安全策略,当你的代码打成jar包被别人使用时,别人用反射可以访问你的一切,除了你说的类的构造函数。如果你使用了如同stackoverflow上那人写的safeMethod方法,会有一些效果。但那种情况不是阻止别人访问你的method,而是使用反射访问你的method时,该method会主动抛出异常。要想让setAccessible(true)时抛异常,不可能。

    2.存在如同Class类的Constructord对象这样可以抛出SecurityException的方法吗?
    存在。例如public Method getDeclaredMethod(String name, Class<?>... parameterTypes)方法,只要违反了安全策略,都可以抛出SecurityException方法。但这依然不能阻止别人使用反射访问你的资源。

    3.如果问题“1”的答案为否,那么Class类的Constructord对象是以何种方式成为特例的呢?
    不知道。猜测一下,是否java内置的安全策略就把Class类的Constructor方法设为不可使用反射访问的呢?反正我试了试,直接就抛出了java.lang.NoSuchMethodException异常,还轮不到抛出SecurityException异常。

    链接:https://www.zhihu.com/question/47896687/answer/108160444

  • 相关阅读:
    Instagram/IGListKit 实践谈(UICollectionView框架)
    谈UIView Animation编程艺术
    Swift Protobuf 初探 —— 继 XML 后,JSON 也要被淘汰了吗
    APP缓存数据线程安全问题
    玩转UICollectionViewLayout
    iOS常用宏 定义
    性能优化之NSDateFormatter
    iOS 用自签名证书实现 HTTPS 请求的原理
    iOS开发调试Reveal使用
    iOS配置SSO授权
  • 原文地址:https://www.cnblogs.com/doit8791/p/7406994.html
Copyright © 2020-2023  润新知