改善 Java 程序的151个建议之泛型和反射

1.Java泛型是类型擦除的

Java的泛型在编译期有效,在运行期被删除,也就是说所有的泛型参数类型在编译后都会被清除掉。

public class Foo{
//listMethod接收数组参数并进行重载
    public void arrayMethod(String[] strArray){
    }
    public void arrayMethod(Integer[] strArray){
    }
    //listMethod接收泛型list参数并进行重载
    public void listMethod(List<String> strArray){
    }
    public void listMethod(List<Integer> strArray){
    }
}

这个程序是无法编译的,List<String>和List<Integer>在编译时查出类型后的都是List<E>,造成方法签名重复。这就是Java泛型擦除引起的问题。在编译后所有的泛型类型都会做相应的转化,转换规则如下:

  • List<String>、List<Integer>、List<T>擦除后的类型是List
  • List<String>[]擦除后的类型为List[]
  • List<? extends E>、List<? super E>擦除后的类型为List<E>
  • List<T extends Seriailzable &Cloneable>擦除后为List<Seriailzable>

Java编译后的字节码中没有泛型的任何信息。比如Foo<T>类只有一份Foo.class,不管是Foo<String>还是Foo<Integer>引用的都是同一字节码。

2.不能初始化泛型参数和数组

泛型类型在编译期被擦除,我们在类初始化时将无法获得泛型的,比如这样的代码:

class Foo<T>{
    private T t = new T();//1.编译不通过
    private T[] tArr = new T[5];//2.编译不通过
    private List<T> list = new ArrayList<T>();//3.编译通过
}

1,2编译不通过是因为编译期在编译时需要获得T类型,但泛型在编译期类型已经被擦除了,所以new T(),new T[5]都会报错,那为什么3编译通过呢?其实ArrayList表面是泛型的,其实已经在编译期转型为Object了,详细可以查看ArrayList的源代码。

在某些情况下,我们确实需要泛型数组,那该怎么处理呢?代码如下:

class Foo<T>{
    //不在初始化,由构造函数初始化
    private T t;
    private T[] tArray;
    private List<T> list = new ArrayList<T>();
    //构造函数初始化
    public Foo(){
        try{
            Class<?> tType = Class.forName("");
            t = (T)tType.newInstance();
            tArray = (T[])Array.newInstance(tType,5);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

此时运行就没有任何问题了。剩下的问题就是怎么在运行期获得T的类型,也就是tType参数,一般情况下泛型类型是无法获取的,不过在客户端调用时多传输一个T类型的class就会解决问题。

类的成员变量是在类初始化前初始化的,所以要求在初始化前它必须具有明确的类型,否则就只能声明,不能初始化

3.不同场景使用不同的泛型通配符

java泛型支持通配符,可以单独使用一个?,也可以使用extends关键字表示一个类(接口)的子类型,也可以使用super关键字表示一个类(接口)的父类型,使用规则如下:

  • 泛型结构只支持读操作则限定上界(extends关键字)
public static <E> void read(List<? extends E> list){
    for(E e : list){
        //业务逻辑操作
    }
}
  • 泛型结构只支持写操作则限定下界(super关键字)
public static <E> void write(List<? super E> list){
    list.add(1);
    list.add(1.2);
}

如果一个泛型结构既用作“读操作”,又用作“写操作”,直接使用确定的泛型类即可,如List<E>

4.适时选择getDeclaredxxx和getxxx

Java的Class类提供了很多的getDeclaredxxx方法和getxxx方法,例如getDeclaredMethod和getMethod成对出现,getDeclaredConstructors也是成对出现,两者的区别如下:

  • getMethod方法获得的是所有public访问级别的方法,包括父类继承的方法
  • getDeclaredMethod获得的是自身类的所有方法,包括公用(public)方法,私有(private)方法等,而且不受限于访问权限

5.反射访问属性或方法时将Accessible设置为true

Accessible的属性并不是我们语法层次理解的访问权限,而是指是否更容易获得,是否进行安全检查。我们知道,修改一个类或方法或执行方法时受Java安全体系的制约,而安全的处理是非常消耗资源的(性能非常低),因此对于运行期要执行的方法或要修改的属性就提供了Accessible可选项;由开发者决定是否要逃避安全体系的检查

  • 设置Accessible为true可以提升性能20倍以上,但可以运行private方法,访问private私有属性等

6.动态加载不适合数组

如果forName要加载一个类,那它首先必须是一个类(8个基本类型排除在外),它们不是一个具体的类,其次,它必须具有可追索的类路径。

在Java中,数组是一个非常特殊的类,虽然它是一个类,但没有定义路径,例如这样的代码:

public static void main(String[] args)throw Exception{
    String[] strs = new String[10];
    Class.forName("java.lang.String[]");
}

运行结果报错!虽然数组是一个类,但编译器编译后会为不同的数组类型产生不同的类:

元素类型 编译后的类型
byte[] [B
char[] [C
Double[] [D
Float[] [F
Int[] [I
Long[] [J
Short[] [S
Boolean[] [Z
引用类型(如String[]) [L 引用类型(如:[Ljava.lang.String)

反射不能定义一个数组,可以使用Array数组反射类来动态加载,代码如下:

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

推荐阅读更多精彩内容