反射教学:写出风骚的Java代码的基本功

反射是java语言中的一个特性,反射机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。 这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。 反射被视为动态语言的关键。
以上是java关于反射的八股文,可能不是很好背,但是绝大部分java程序员都不会在实际开发中去使用反射。
合理使用反射,可以让代码再不影响性能的基础上更加优美,拓展性更加的好。
下面从几个点来讲解反射的使用

反射的一些问题

使用反射一定性能差?

很多程序员可能
都会以反射性能不好的原因去拒绝使用反射。想要合理(风骚)的使用反射,首先需要了解反射为什么性能不好。反射的性能问题总体来说有三点
1.反射调用过程中会产生大量的临时对象,这些对象会消耗内存,增加gc频率,从而影响性能。
2.反射调用方法时会从方法数组中遍历查找,并且会检查可见性等操作会耗时。
3.反射在达到一定次数时,会动态编写字节码并加载到内存中,这个字节码没有经过编译器优化,也不能享受JIT优化。

那么如何去优化:
1.如果是使用反射去读取字段,读取方法,可以使用一个公共类在初始化的时候扫描运行过程中可能需要使用的反射的类,并缓存反射后的一些结果,如Field,Methord方法。
2.不在超高并发的请求链路中使用反射中的invoke,field的set,get等操作,因为不能享受JIT优化,不过问题不大,实际测试,影响其实很小(只要使用地方不太多,是可以使用的)。

总结一下就是,只需要合理设置缓存,考虑性能问题,在任何地方使用反射都没啥大问题,你的编程的思想才是限制你程序性能的最主要的因素。

反射能做什么?

1. 获取当前的类的class信息

Class my = this.getClass();
        System.out.println("我是谁"+my.getName());
        System.out.println("我有哪些成员变量"+ Arrays.toString(my.getDeclaredFields()));
        System.out.println("我有哪些成员变量(包含私有,但不包括继承的)"+ Arrays.toString(my.getDeclaredFields()));
        System.out.println("我有哪些方法"+ Arrays.toString(my.getMethods()));
        System.out.println("我有哪些方法(包含私有,但不包括继承的方法)"+ Arrays.toString(my.getDeclaredMethods()));
        System.out.println("我有哪些注解(包含继承)"+ Arrays.toString(my.getAnnotations()));
        System.out.println("我有哪些注解(不包含继承)"+ Arrays.toString(my.getDeclaredAnnotations()));

这样一来,就获取了当前类的class对象,并获得了成员变量,方法,注解一系列的信息。

2.对成员变量进行操作

Field fieldOne = this.getClass().getDeclaredField("dateList");
        System.out.println("字段是什么类型?"+fieldOne.getType());
        //这个字段是不是Collection接口的实现类?
        if (Collection.class.isAssignableFrom(fieldOne.getType())
                && fieldOne.getGenericType() instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) fieldOne.getGenericType();
            Type[] fieldArgTypes = parameterizedType.getActualTypeArguments();
            for (Type fieldArgType : fieldArgTypes) {
                System.out.println("集合类型是什么类型的集合?"+(Class) fieldArgType);
            }
        }
        //给这个字段赋值,注意:类型需要兼容,可以是子类
        fieldOne.set(this, Lists.newArrayList("123123","1212"));
        System.out.println("通过反射后设置进去了什么值,通过反射获取出来看看"+fieldOne.get(this));

通过getType可以直接拿到class,并且通过getGenericType返回的ParameterizedType中的ActualTypeArguments可以获取到字段类型<T>中的类型class。也可以直接调用set/get获取字段的值或者是设置字段的值

3.对方法进行操作

拿到了xxx.class对象的时候,直接调用getMethod或者getMethods方法就可以,可以拿到方法的Method对象。这里,如果是常用方法,可以把这个对象缓存起来。Method最常用的方法是method.invoke(),通过传入对象本身和方法对应的入参,调用方法。此外,如果是私有方法,可以使用method.setAccessible(true)取消权限检查

Method testMethod = my.getMethod("test",String.class,Integer.class);
testMethod.setAccessible(true);
String result = (String)testMethod.invoke(my,"haha", 1);
System.out.println(result);

4.对抽象父类的泛型获取子类的具体填充类型。

开发的时候有时候父类形如 XXX<T>的,需要在父类定义一些通用的方法,但是这时候需要创建一个T类型的示例,这时候因为java擦除的特效,T的属性是被擦除的,需要如何去获得T的类型呢。
利用反射原理,子类的T如果已经确定,调用父类的方法,是可以获得当前this对象的T的class或者是实例.
关键方法 getActualTypeArguments。这个方法可以获得当前泛型类型的数组,作用在实例对象上。

private T createModel() {

        try {
            Type superClass = getClass().getGenericSuperclass();
            //关键代码,拿到当前类<>中的第一个类型例如<K.V>,getActualTypeArguments的返回K下标为0,V下标为1
            Type type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
            Class<?> clazz = getRawType(type);
            return (T) clazz.newInstance();
        } catch (Exception e) {
            log.error("AbstactYsConfig createModel error",e);
        }
        return null;
    }

    private static Class<?> getRawType(Type type) {
        if (type instanceof Class) {
            return (Class) type;
        } else if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            Type rawType = parameterizedType.getRawType();
            return (Class) rawType;
        } else if (type instanceof GenericArrayType) {
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
            return Array.newInstance(getRawType(componentType), 0).getClass();
        } else if (type instanceof TypeVariable) {
            return Object.class;
        } else if (type instanceof WildcardType) {
            return getRawType(((WildcardType) type).getUpperBounds()[0]);
        } else {
            String className = type == null ? "null" : type.getClass().getName();
            throw new IllegalArgumentException("Expected a Class, ParameterizedType, or GenericArrayType, but <" + type + "> is of type " + className);
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容