xuefeng_yang的专栏

为装逼而生

Java反射是什么鬼

动态与静态

这里的动态和静态指的是编程语言,常见的静态语言有:C#、C++、Java等,常见的动态语言有:JavaScript、Python、Ruby、PHP等。
主要的区别:
静态语言通常需要编译才能运行,比如java文件需要编译成字节码文件、C++也需要编译成汇编代码才能执行。动态语言通常不需要编译只需要相应的解析器去解析即可,比如js代码需要浏览器的脚本解析引擎去解析执行。
优缺点:
动态语言更灵活、但速度相对来说要慢(但是语言的快慢之争在不需要高度渲染的情况下比如游戏开发的时候已经没有争论的必要,因外往往拖慢速度的是数据库的访问和网络IO操作。),静态语言就相对没这么灵活了,因外需要编译,所以在编译时期变量类型和值就已经确定了。但是速度相对更快。
example:

    var oDivs=document.getElementsByTagName('div');
    for(var i=0,i<oDivs.length,i++){
        //动态的给div加一个index属性
        oDivs[i].index=i;
    }

上面的例子就是动态语言的好处,可以在运行的时候给对象增加一个属性,这个在静态语言中无法实现(后面讲的黑科技可以实现类似的鬼东西)。

Java反射浅析

java的反射(java.lang.reflect)其实就是为了解决java自身作为静态语言的不足之处,它允许用户在运行时期动态的生成对象并且访问对象的成员,因为作为java语言本身而言通常情况下是在代码进行编译的时候生成相应的class文件,class文件反应的就是java类内部执行的流程和包涵的成员,java虚拟机加载的就是class文件,new一个对象也就是指的加载一份相应的class文件到内存中。所以通常情况下要使用的对象在编译的时候已经确定了。反射的诞生以为着用户可以绕过编译,直接在运行时期按需执行。
反射常常运用在调用某些隐藏api、框架开发上面。比如ORM框架、EventBus 等。因为这些东西都是在编译时期不确定具体要映射的类或者调用的对象。
java提供了一套完成的api供用户在运行时期操作对象:

名字 含义 获取方式
Class 类对象 Class.forName/class.class/obj.getClass()
Field 代表类的属性 class.getFields()
Method 代表类的方法 class.getMethod()
Constructor 构造对象 class.newInstance()

Class 属于类对象,java中所以的对象都是Class的实例 包括基本的数据类型

    Class<?> clazz1 =int.class;
    Class<?> clazz2 =double.class;
    Class<?> clazz3 =byte.class;
    ………………………………………………………………
    //每一种基本类型都有对应的Class类型

要在运行期操作对象必须获取相应的Class对象。

构造对象

获取一个类的class对象有三种方式:
1.编译时期知道类名,例如String.class,可以直接通过类型获取class对象;

2.编译时期可以获取相应类的实例对象时,例如 Person p =new Person(); p.getClass(); 这种方式也可以获取class对象;

3.知道完整类名例如:Class.forName(“com.lang.String”);

//编译时期可以获取类
Class<?> clazz =String.class;

//编译时期可以获取对象
Person p =new Person();
lass<?> clazz2 = p.getClass();

//知道完整的类名
Class<?> class3 = Class.forName("com.reflect.Person");

获取成员

获取成员包括属性和方法

        Class<?> clazz =String.class;

            clazz.getDeclaredField("toString");
            Field[] fields = clazz.getFields();

            Method method=clazz.getDeclaredMethod("subString", int.class,int.class);
            Method [] methods=clazz.getMethods();

有Declared修饰的方法表示不包含继承和实现的方法方法的定义就是在本类中。否则会包涵父类的方法。所以getFields().length>=getDeclaredFields().length。getMethods().length>=getDeclaredMethods().length.

操作成员

通过class构造一个对象

        try{
        //根据完整的类名获取Class对象
        Class<?> clazz=Class.forName("com.reflect.Person");
        //更加class对象创建具体的实例  
        Object obj = clazz.newInstance();
        //向下转换 成功获得一个person实例
        Person  p =(Person) obj;
        }catch(Exception e){
            e.printStackTrace();
        }

通过构造方法构造对象

    try{
        //根据完整的类名获取Class对象
        Class<?> clazz=Class.forName("com.reflect.Person");
        //获取无参构造方法
        Constructor<?> constructor = clazz.getDeclaredConstructor();
        //获取有参数构造方法 参数类型是传的对应的class类型(每一个类型包括基本类型都有一个对应的class类型)
        Constructor<?> constructor2 = clazz.getDeclaredConstructor(String.class,int.class);
        //创建实例
        Object newInstance = constructor.newInstance();
        Object newINstance2 =constructor2.newInstance("xuefengyang",23);
        //先下转换
        Person p =(Person)newInstance;
        }catch(Exception e){
            e.printStackTrace();
        }

访问属性


        try{
        //根据完整的类名获取Class对象
        Class<?> clazz=Class.forName("com.reflect.Person");
        //获取类本身定义的属性数组
        Field[] declaredFields = clazz.getDeclaredFields();
        //编译属性
        for (Field field : declaredFields) {
            //获取属性名称
            String name = field.getName();
            //获取属性类型
            String typeName = field.getType().getName();
            //获取属性修饰符
            String modifier = Modifier.toString(field.getModifiers());
            System.out.println("name->"+name+"\n"+"typename->"+typeName+"\n"+"modifier->"+modifier);
            System.out.println("------------------------------------------------------------");
        }
        }catch(Exception e){
            e.printStackTrace();
        }

输出如下:

name->name
typename->java.lang.String
modifier->private
------------------------------------------------------------
name->age
typename->int
modifier->private
------------------------------------------------------------
        try{
        //根据完整的类名获取Class对象
        Class<?> clazz=Class.forName("com.reflect.Person");
        //实例化对象
        Object obj = clazz.newInstance();
        //获取自定的属性
        Field field = clazz.getDeclaredField("name");
        //获取属性的值
        String name=(String)field.get(obj);
        //打印属性
        System.out.println("name:"+name);
        }catch(Exception e){
            e.printStackTrace();
        }

上面代码执行报错了

java.lang.IllegalAccessException: Class com.reflect.Client can not access a member of class com.reflect.Person with modifiers "private"at sun.reflect.Reflection.ensureMemberAccess(Unknown Source)

错误描述:不能访问私有成员 。
没关系反射提供了相应的访问私有成员的api
只需要在上面加上一点点小的修改即可


        try{
        //根据完整的类名获取Class对象
        Class<?> clazz=Class.forName("com.reflect.Person");
        //实例化对象
        Object obj = clazz.newInstance();
        //获取自定的属性
        Field field = clazz.getDeclaredField("name");
        //设置可以访问
        field.setAccessible(true);
        //设置属性值
        field.set(obj, "xuefengyang");
        //获取属性的值
        String name=(String)field.get(obj);
        //打印属性
        System.out.println("name->"+name);
        }catch(Exception e){
            e.printStackTrace();
        }

结果:

name->xuefengyang

调用方法

        //调用方法 
        Method method = clazz.getDeclaredMethod("print");
        //私有方法同样需要设置可访问标示
        method.setAccessible(true);
        //调用方法 obj 表示的是调用的方法所属的对象,后面可能再跟方法所需的参数,但是这个方法本身没有参数就不需要传递(invok的第二个参数是可变长度的)
        method.invoke(obj);

结果:

Person [name=xuefengyang, age=23]

通过反射能够在运行时期获取对象并且操作对象 绕过了编译。

杂谈

我们知道jdk1.5的时候增加了非常多的新特性其中一个就是泛型
example:

List<String> list=new ArrayList<String>();
list.add("item");

通过泛型我们就可以对list添加的元素的类型做一个约定只能为String类型

List<String> list=new ArrayList<String>();
list.add("item");
list.add(100); //编译报错

如果加入非String类型的元素就会导致编译报错 ,刚刚讲了反射可以绕过编译。所以我们用反射试试。


        List<String> list=new ArrayList<String>();
        list.add("item");
        Class<? extends List> clazz = list.getClass();
        try {
            Method method = clazz.getMethod("add",Object.class);
            method.invoke(list, 1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("size -> "+list.size());
        System.out.println("obj -> "+list);

结果:

size -> 2
obj -> [item, 1]

通过反射把数字1加入了泛型为String的集合中,表明泛型是在编译阶段对类型进行的限制。 运行时期同样可以加入任何类型的数据。

最后

权当做笔记,大神勿喷!!!

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xuefeng_yang/article/details/49497415
文章标签: java 反射
个人分类: Java
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

Java反射是什么鬼

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭