- 浏览: 220599 次
- 性别:
- 来自: 广州
文章分类
最新评论
-
zi_wu_xian:
顶5楼,POI操作word和excel还是太复杂了,并且针对d ...
使用POI操作Excel和Word -
贝塔ZQ:
poi操作word、excel代码好多啊,用插件试试吧,网上不 ...
使用POI操作Excel和Word -
wap816:
@CacheEvict(value = ...
SpringEL详解及应用 -
string2020:
List<Map<String,Map<St ...
开源工具 — Apache Commons Collections -
uniqueX:
mark!
并发编程 — 判断线程安全
前言:由于上一个星期工作繁忙,利用上下班和晚上睡前空余的时间拜读了秦小波老师的《改善Java程序的151建议》,感觉廓然开朗,注意到了很多平时在编写代码中并不会注意的问题,甚至感觉自己对Java只是略懂皮毛,不足以登大雅之堂,特此与读者分享读书笔记,以下内容摘自《改善Java程序的151建议》一书和笔者的理解
Java高质量代码系列文章
面向对象篇:http://ray-yui.iteye.com/blog/1926984
数据类型篇:http://ray-yui.iteye.com/blog/1927251
字符串篇:http://ray-yui.iteye.com/blog/1927647
数组与集合(1):http://ray-yui.iteye.com/blog/1928170
数组与集合(2):http://ray-yui.iteye.com/blog/1930155
枚举与注解:http://ray-yui.iteye.com/blog/1931408
泛型与发射:http://ray-yui.iteye.com/blog/1933127
异常:http://ray-yui.iteye.com/blog/1938946
杂:http://ray-yui.iteye.com/blog/1942591
在Java5后推出了泛型,使我们在编译期间操作集合或类时更加的安全,更方便代码的阅读,而让身为编译性语言的Java提供动态性的反射技术,更是在框架开发中大行其道,从而让Java活起来,下面看一下在使用泛型和反射需要注意和了解的事情
1.Java的泛型是类型擦除的
Java中的泛型是在编译期间有效的,在运行期间将会被删除,也就是所有泛型参数类型在编译后都会被清除掉.请看以下例子
public static void test(List<Integer> testParameter) { } public static void test(List<String> testParameter) { }
以上是一个最最简单的重载例子,在编译后泛型类型是会被擦除的,所以无论是List<Integer>或List<String>都会变成List<E>,所以参数相同,重载不成立,无法编译通过,而且读者可以尝试将不同泛型的类getClass()看看,他们的Class是一样的
2.不能初始化泛型参数和数组
请观察以下代码
class Test<T> { //编译不通过 private T t = new T(); //编译不通过 private T[] tArray = new T[5]; //编译通过 private List<T> list = new ArrayList<T>(); }
Java语言是一门强类型,编译型的安全语言,它需要确保运行期的稳定性和安全性,所以在编译时编译器需要严格的检查我们所声明的类型,在编译期间编译器需要获取T类型,但泛型在编译期间已经被擦除了,所以new T()和new T[5]都会出现编译错误,而为什么ArrayList却能成功初始化?这是因为ArrayList表面是泛型,但在编译期间已经由ArrayList内部转型成为了Object,有兴趣的读者可以看一下ArrayList的源码,源码中容纳元素的是private transient Object[] elementData,是Object类型的数组
3.强制声明泛型的实际类型
在使用泛型时,在大多数情况下应该声明泛型的类型,例如该集合是存放User对象的,就应该使用List<User>来声明,这样可以尽量减少类型的转换,若只使用List而不声明泛型,则该集合等同于List<Object>
4.注意泛型没有继承说
请看以下例子
public static void main(String[] args) { // 编译不通过 List<Object> list = new ArrayList<Integer>(); // or List<Object> list = new ArrayList<Double>(); }
上面代码的业务逻辑为有一组元素,但我不确定元素时整形还是浮点型,当我在确定后创建对应的泛型实现类,刚接触泛型的读者有可能会犯以上错误,理解为泛型之间是可以继承的,例如声明Object的泛型实际上创建Integer泛型,错误认为泛型之间也存在继承关系,但这是不正确的,泛型是帮助开发者编译期间类型检查安全.我们可以换种方式实现以上业务逻辑
public static void main(String[] args) { //Number 为Integer 和 Double 的公共父类 List<Number> numberList = new ArrayList<Number>(); //使用通配符,代表实际类型是Number类型的子类 List<? extends Number> numberList2 = new ArrayList<Integer>(); //or List<? extends Number> numberList3 = new ArrayList<Double>(); }
5.不同场景使用不同的通配符
泛型当中支持通配符,可以单独使用 '?' 来表示任意类型,也可以使用刚才上面例子中的extends关键字表示传入参数是某一个类或接口的子类,也可以使用super关键字表示接受某一个类型的父类型,extends和super的写法一样,extends是限定上界,super为限定下界
6.建议采用顺序为List<T> List<?> List<Object>
以上三者都可以容纳所有的对象,但使用的顺序应该是首选List<T>,然后List<?>,最后才使用List<Object>,原因是List<T>是确定为某一类型的,安全的,也是Java所推荐的,而List<?>代表为任意类型,与List<T>类似,而List<Object>中全部为Object类型,这与不使用泛型无异,而List<T>是可读可写的,因为已经明确了T类型,而其他两者在读取时需要进行向下转型,造成了不必要的转型
7.使用多重边界限定
现在有一个业务需求,收钱时需要是我们本公司的员工(继承User),同时亦需要具备收银员的功能(实现Cashier),此时我们当然可以想到接受1个User然后判断User是否实现了Cashier接口,但在泛型的世界中,我们可以怎么做?请看以下例子
public static <T extends User & Cashier> void CollectMoney(T t){ }
以上例子就表明,限定了上界,只要是User和Cashier的子类才可以作为参数传递到方法当中
======================我是泛型和反射的分界线====================
8.注意Class类的特殊性
Java语言是先把Java源文件编译成后缀为class的字节码文件,然后通过ClassLoader机制把类文件加载到内存当中,最后生成实例执行的,在描述一个类是,Java使用了一个元类来对类进行描述,这就是Class类,他是一个描述类的类,所以注定Class类是特殊的
1.Class类无构造函数,Class对象加载时由JVM通过调用类加载器的
defineClass方法来构造Class对象
2.Class类还可以描述基本数据类型,由于基本类型并不是Java中的对象,它们
一般存在于栈,但Class仍然可以对它们进行描述,例如使用int.class
3.其对象都是单例模式,一个Class的实现对象描述一个类,并且只描述一个类
所以只要是该被描述的类所有对象都只有一个Class实例
4.Class类是Java反射的入口
9.适时选择getDeclaredXXX和getXXX
Class类中提供了很多getDeclaredXXX和getXXX的方法,请看以下例子
public static void main(String[] args) { Class<User> cls = User.class; //获取类方法 cls.getDeclaredMethods(); cls.getMethods(); //获取类构造函数 cls.getDeclaredConstructors(); cls.getConstructors(); //获取类属性 cls.getDeclaredFields(); cls.getFields(); }
getXXX的方式是获取所有公共的(public)级别的,包括从父类继承的方法,而getDeclaredXXX的方式是获取所有的,包括公共的(public),私有的(private),不受限与访问权限,
10.反射访问属性或方法时将Accessible设置为true
在调用构造函数或方法的invoke前检查accessible已经是公认的写法,例如以下代码
public static void main(String[] args) throws Exception { Class<User> cls = User.class; //创建对象 User user = cls.newInstance(); //获取test方法 Method method = cls.getDeclaredMethod("test"); //检查Accessible属性 if(!method.isAccessible()){ method.setAccessible(true); } method.invoke(user); }
读者可以尝试获取Class的getMethod,也就是公开的方法,再输出isAccessible,可以看到输出的其实也是false,其实因为accessible属性的语义并不是我们理解的访问权限,而是指是否进行安全检查,而安全监察是非常消耗资源的,所以反射提供了Accessible可选项,让开发者逃避安全检查,有兴趣的读者可以查看AccessibleObject类观察其源码了解安全检查.
11.使用forName动态加载类
forName相信各位读者不会陌生,在使用JDBC时要动态加载数据库驱动就是使用forName的方式进行加载,同时亦可以从外部配置文件中读取类的全路径字符串进行加载,在使用forName时,被加载的类就会被加载到内存当中,只会加载类,并不会执行任何代码,而我们的数据库驱动就是利用static代码块来执行操作的,因为当类被加载到内存中时,会执行static代码块
12.使用反射让模板方法更强大
模板方法的定义是,定义一个操作的算法骨架,将某些步骤延迟到子类当中实现,而实现细节由子类决定,父类只决定骨架,以下是一个传统模板方法的事例
public abstract class Test { public final void doSomething() { System.out.println("start..."); doInit(); System.out.println("end....."); } protected abstract void doInit(); }
此时子类只需要继承Test类实现doInit()方法即可嵌入到doSomething中,现在我们有一个需求,若我在doSomething中需要调用一系列的方法才能完成doSomething呢?而且我调用方法的数量并不确定,只需遵从某些规则则可将方法添加到doSomething方法当中.请看以下代码
public abstract class Test { public final void doSomething() throws Exception { Method[] methods = this.getClass().getDeclaredMethods(); System.out.println("start..."); for (Method method : methods) { if (this.checkInitMethod(method)) { method.invoke(this); } } System.out.println("end....."); } private boolean checkInitMethod(Method method) { // 方法名初始是否为init return method.getName().startsWith("init") // 是否为public修饰 && Modifier.isPublic(method.getModifiers()) // 返回值是否为void && method.getReturnType().equals(Void.TYPE) // 是否没有参数 && !method.isVarArgs() // 是否抽象类型 && !Modifier.isAbstract(method.getModifiers()); } }
看到上面的代码,读者是否有似曾相识的感觉?在使用Junit3时是不是只要遵守方法的签名约定,就能成为测试类?使用这种反射可以让模板方法更强大,下次需要使用多个方法在模板方法中时,不要创建多个抽象方法,尝试使用以上方式
13.不要过分关注反射的效率
反射的效率是一个老生常谈的问题,普通的调用方法,创建类,在反射的情况下需要调用诸多API才能实现,效率当然要比普通情况下低下,但在项目当中真正引起性能问题的地方,绝大数不会是由反射引起的,而反射带给我们的却是如此的美妙,当今的各系列开源框架,几乎都存在反射的身影,而且大量存在更比比皆是,让Java这个沉睡的美女活起来的,非反射莫属
总结:
笔者在本文章中只从《改善Java程序的151建议》中提取部分进行归纳性叙述,推荐各位读者购买这本书,该书不仅从事例中学习,而且涉及到原理,底层的实现,不仅告诉你应该怎么做,还告诉你为什么要这样做.
评论
12 楼
tanshenghui
2014-07-02
getXXX的方式是获取所有公共的(public)级别的,包括从父类继承的方法,而getDeclaredXXX的方式是获取所有的,包括公共的(public),私有的(private),不受限与访问权限,
getDeclaredXXX的方式是获取自身的方法或属性,不包括父类继承的,这点应该说明。
getDeclaredXXX的方式是获取自身的方法或属性,不包括父类继承的,这点应该说明。
11 楼
ray_yui
2013-09-10
jj356302304 写道
jj356302304 写道
我有一个问题,在泛型第4点
List<Object> list = new ArrayList<Integer>();
这样编译是报错的.但是在第一点时你说,泛型在编译之后是擦除的,如果这个说法成立.那么这句代码在编译后可以认为是
List<Object> list = new ArrayList<Object>();
但是为什么编译时会报错呢?难道说编译后没有类型擦除!
谢谢
List<Object> list = new ArrayList<Integer>();
这样编译是报错的.但是在第一点时你说,泛型在编译之后是擦除的,如果这个说法成立.那么这句代码在编译后可以认为是
List<Object> list = new ArrayList<Object>();
但是为什么编译时会报错呢?难道说编译后没有类型擦除!
谢谢
楼主的这句话有点问题"也就是所有泛型参数类型在编译后都会被清除掉.",应该改为"也就是所有泛型参数类型在运行时都会被清除掉",那么这个就不是问题了
根据刚才的提问进行了测试,在编译时报出如下错误
Method test(List<String>) has the same erasure test(List<E>) as another method in type,具有相同的擦除在另一个方法,希望帮助到你解决问题
10 楼
ray_yui
2013-09-10
jj356302304 写道
jj356302304 写道
我有一个问题,在泛型第4点
List<Object> list = new ArrayList<Integer>();
这样编译是报错的.但是在第一点时你说,泛型在编译之后是擦除的,如果这个说法成立.那么这句代码在编译后可以认为是
List<Object> list = new ArrayList<Object>();
但是为什么编译时会报错呢?难道说编译后没有类型擦除!
谢谢
List<Object> list = new ArrayList<Integer>();
这样编译是报错的.但是在第一点时你说,泛型在编译之后是擦除的,如果这个说法成立.那么这句代码在编译后可以认为是
List<Object> list = new ArrayList<Object>();
但是为什么编译时会报错呢?难道说编译后没有类型擦除!
谢谢
楼主的这句话有点问题"也就是所有泛型参数类型在编译后都会被清除掉.",应该改为"也就是所有泛型参数类型在运行时都会被清除掉",那么这个就不是问题了
编译报错!=编译后,所以就能理解为,编译不通过,那就不存在编译后,是Java对你的编译报错出了错误,所以编译后类型擦除的说话是没问题的
9 楼
jj356302304
2013-09-10
jj356302304 写道
我有一个问题,在泛型第4点
List<Object> list = new ArrayList<Integer>();
这样编译是报错的.但是在第一点时你说,泛型在编译之后是擦除的,如果这个说法成立.那么这句代码在编译后可以认为是
List<Object> list = new ArrayList<Object>();
但是为什么编译时会报错呢?难道说编译后没有类型擦除!
谢谢
List<Object> list = new ArrayList<Integer>();
这样编译是报错的.但是在第一点时你说,泛型在编译之后是擦除的,如果这个说法成立.那么这句代码在编译后可以认为是
List<Object> list = new ArrayList<Object>();
但是为什么编译时会报错呢?难道说编译后没有类型擦除!
谢谢
楼主的这句话有点问题"也就是所有泛型参数类型在编译后都会被清除掉.",应该改为"也就是所有泛型参数类型在运行时都会被清除掉",那么这个就不是问题了
8 楼
ray_yui
2013-09-10
jj356302304 写道
我有一个问题,在泛型第4点
List<Object> list = new ArrayList<Integer>();
这样编译是报错的.但是在第一点时你说,泛型在编译之后是擦除的,如果这个说法成立.那么这句代码在编译后可以认为是
List<Object> list = new ArrayList<Object>();
但是为什么编译时会报错呢?难道说编译后没有类型擦除!
谢谢
List<Object> list = new ArrayList<Integer>();
这样编译是报错的.但是在第一点时你说,泛型在编译之后是擦除的,如果这个说法成立.那么这句代码在编译后可以认为是
List<Object> list = new ArrayList<Object>();
但是为什么编译时会报错呢?难道说编译后没有类型擦除!
谢谢
非常感谢你的提问,这里是我的问题,由于篇幅的问题,我省略了协变和逆变的概念,这是因为由于Java为了保证安全性,必须保证泛型参数是固定的,所以它不允许一个泛型参数可以同时包含两种类型,即父子关系也不可以,除非使用通配符,这是泛型限定的
7 楼
jj356302304
2013-09-10
我有一个问题,在泛型第4点
List<Object> list = new ArrayList<Integer>();
这样编译是报错的.但是在第一点时你说,泛型在编译之后是擦除的,如果这个说法成立.那么这句代码在编译后可以认为是
List<Object> list = new ArrayList<Object>();
但是为什么编译时会报错呢?难道说编译后没有类型擦除!
谢谢
List<Object> list = new ArrayList<Integer>();
这样编译是报错的.但是在第一点时你说,泛型在编译之后是擦除的,如果这个说法成立.那么这句代码在编译后可以认为是
List<Object> list = new ArrayList<Object>();
但是为什么编译时会报错呢?难道说编译后没有类型擦除!
谢谢
6 楼
ray_yui
2013-09-02
jadymrhu 写道
为嘛是151条建议,这是一个质数
我是无辜的..那书名就是151
5 楼
jadymrhu
2013-09-02
为嘛是151条建议,这是一个质数
4 楼
areha001
2013-08-30
Mark , 不错
3 楼
hebad90
2013-08-30
泛型那里学习了。
2 楼
tongyi55555
2013-08-29
多谢分享,根据楼主的建议去看看151这本书。
1 楼
jacking124
2013-08-29
支持你,写到不错的,学习了!
发表评论
-
并发编程 — 实现线程安全
2015-08-12 09:03 2312并发编程系列文章: 初解线程池:http://r ... -
并发编程 — 优化小技巧
2015-08-10 23:54 0并发编程系列文章: 初解线程池:http://r ... -
并发编程 — 判断线程安全
2015-08-05 10:11 5576并发编程系列文章: ... -
并发编程 — volatile
2015-07-30 10:11 2214并发编程系列文章: 初解线程池:http://r ... -
数据库储存不确定实体
2014-07-08 09:52 2297相信在项目开发当中都曾经遇到过,有某些要储蓄到数 ... -
同步操作降低效率解惑
2014-06-30 10:20 3308相信在读者刚接触Java的时候,都曾经学习到线程 ... -
并发编程 — 并发数据结构
2014-06-24 09:58 6647并发编程系列文章: 初解线程池:http://r ... -
并发编程 — 并发数据类型
2014-06-16 10:03 5459并发编程系列文章: 初解线程池:http://r ... -
并发编程 — 详解线程池
2014-06-03 09:51 6020本文章需要对JDK5 Executor框架有所了解,请读者先 ... -
并发编程 — 初解线程池
2014-05-28 09:57 7564并发编程系列文章: 初解线程池:http://r ... -
线程池详解
2014-05-26 18:20 0本文章需要对JDK5 Executor框架有所了 ... -
高级字节码生成工具 — Javassist
2014-03-11 09:57 6434AOP系列文章: Spring AOP:http ... -
实现AOP — CGLIB
2014-03-06 09:50 3998AOP系列文章: Sp ... -
实现AOP — Spring AOP
2014-03-03 10:02 4276AOP系列文章: Spring AOP:http ... -
cglib
2014-01-11 19:11 0什么是CGLIB? CGLIB是一个强大的高性能 ... -
Java高质量代码之 — 杂
2013-09-16 09:53 4755前言:由于上一个星期工作繁忙,利用上下班和晚上睡前空余的时间 ... -
Java高质量代码之 — 异常
2013-09-09 09:35 6767前言:由于上一个星期工作繁忙,利用上下班和晚上睡前空余的时间 ... -
Java高质量代码之 — 枚举与注解
2013-08-27 09:59 15844前言:由于上一个星期工作繁忙,利用上下班和晚上睡前空余的时间 ... -
Java高质量代码之 — 数组与集合(2)
2013-08-23 17:24 2923前言:由于上一个星期工作繁忙,利用上下班和晚上睡前空余的时间 ... -
Java高质量代码之 — 数组与集合(1)
2013-08-21 12:24 7260前言:由于上一个星期工作繁忙,利用上下班和晚上睡前空余的时间 ...
相关推荐
在Java编程语言中,泛型和反射是两个非常重要的特性,它们在软件开发中有着广泛的应用。本篇文章将深入探讨这两个概念以及它们在实际开发中的小运用。 首先,我们来看泛型(Generics)。泛型是在Java SE 5.0引入的...
总结来说,Java反射提供了在运行时探索和操作类的能力,而泛型则增强了类型安全和代码复用。在`Testrefl.java`的例子中,这两者结合在一起,实现了动态创建和操作对象的能力,显示了Java编程的强大灵活性。理解并...
Java泛型是Java编程语言中一个强大的特性,它允许在定义类、接口...理解并熟练运用泛型,对于编写高质量的Java代码至关重要。在实际开发中,根据需求合理使用泛型,可以避免许多潜在的运行时错误,并增强代码的灵活性。
总之,泛型和反射机制在Hibernate中的结合,使得开发人员能够更加方便地处理Java对象与数据库之间的映射关系,同时提供了更好的类型安全性和代码复用性。通过理解这两个核心概念,开发者可以更好地驾驭Hibernate,...
Java 泛型是Java编程语言中的一个重要特性...熟练掌握泛型能够帮助开发者编写出更高质量的代码,减少类型转换的困扰,并能更好地利用Java的集合框架。在实际编程中,理解并善用泛型将极大地提升程序的可靠性和维护性。
熟练掌握泛型的使用,对于编写高质量的Java程序至关重要。通过深入理解泛型的各个方面,包括类型参数、泛型类和方法、边界限制、通配符、类型擦除以及泛型与多态的结合,开发者可以编写出更加高效且易于理解的代码。
通过理解和熟练运用这些知识点,开发者可以编写出更加健壮、可读性更强的Java代码,同时减少潜在的类型转换错误,提高代码质量。在实际开发中,尤其是在处理数据结构和算法时,Java泛型扮演着至关重要的角色。
通过学习《高质量Java程序设计》,开发者不仅可以掌握Java语言的核心技术,还能理解如何编写出高效、可扩展、易于维护的代码,这对于任何Java开发者的专业成长都至关重要。这本书的PDF版本可以在超星阅览等平台上...
Java泛型是JDK 1.5引入的重要特性,它为Java编程提供了类型安全的集合框架,使得在编译时期就能进行类型检查,避免了...泛型是现代Java编程不可或缺的一部分,理解和掌握泛型对于编写高质量的Java应用程序至关重要。
通过阅读"JAVA5.0泛型指南",开发者将能够熟练地利用泛型提高代码质量,减少类型转换,提升程序的可读性和可维护性。同时,对泛型的理解也有助于更好地利用Java的其他高级特性,如Lambda表达式和流API。
### Java泛型编程详解 #### 一、引言 Java泛型编程是在JDK 1.5版本之后引入的一项重要特性,它...通过理解和掌握泛型的基本概念、工作原理及其高级特性,开发者可以更好地利用这一特性来构建高质量的Java应用程序。
《高质量Java程序设计》是一本深入探讨如何编写高效、可靠且易于维护的Java代码的书籍。源代码提供了书中实例的实现,是理解理论知识并进行实践操作的重要资源。以下是基于这个主题的一些Java编程核心知识点: 1. *...
Java泛型是Java编程语言中的一个特性,它允许在定义类、接口和方法时...理解和掌握泛型的用法对于编写高质量的Java程序至关重要。在实际开发中,合理运用泛型能有效提升代码的可维护性和安全性,减少潜在的运行时错误。
### Java泛型指导 #### 知识点概览 1. **Java泛型的基本概念** 2. **泛型的优点** 3. **泛型的基本用法** 4. **泛型类与泛型方法** ...理解并掌握泛型的概念和用法对于开发高质量的Java应用至关重要。
然而,值得注意的是,Java2Pas可能无法完美地处理所有Java特性,比如泛型、反射、线程等高级特性,这些在转换后可能需要额外的代码调整。此外,由于Delphi的接口(interface)和Java的接口概念有所不同,转换后可能...
7. **第7章 JAVA高级应用**: 这一章可能涵盖了除上述内容外的其他高级Java话题,如反射、代理、注解、泛型等,这些都是Java进阶开发者必须掌握的技能。 8. **第8章 JAVA 8新特性**: Java8引入了大量新特性,如...
以上内容只是Java泛型基础知识的一部分,实际开发中还有许多更高级的应用,例如类型安全的构造函数、泛型与枚举、泛型与反射等。深入理解和熟练运用泛型能显著提升Java代码的质量和效率。阅读提供的文档,如"java ...
Java是一种广泛使用的面向对象的编程语言,以其跨平台、健壮性和安全性著称。这个"Java学习部分代码"的资源集合旨在为初学者和有一定...同时,别忘了查阅官方文档和其他高质量的学习资料,以获取最新的信息和技术趋势。
枚举(Enum)和泛型(Generic)是Java编程语言中的两种强大特性,...理解并熟练掌握这两种特性,对于编写高质量、安全、易维护的代码至关重要。在日常开发中,我们应该充分利用它们的优势,以提高代码的效率和可读性。
Java是一种广泛使用的面向对象的编程语言,以其跨平台、高性能和丰富的类库而著名。"java实用代码源代码"这个标题表明我们即将探讨的是实际应用中的Java编程代码,这些代码可能涵盖各种功能和应用场景,旨在提高开发...