你应该远离的6个Java特性
Nikita Salnikov Tarnovski是plumbr的高级开发者,也是一位应用性能调优的专家,他拥有多年的性能调优经验。近日,Tarnovski撰文谈到了普通开发者应该尽量避免使用的6个Java特性,这些特性常见于各种框架或库当中,但对于普通的应用开发者来说,使用这些特性也许会给你所开发的应用带来灾难。
我曾花费了无数个小时为各种不同的应用排错。根据过往的经验我可以得出这样一个结论,那就是对于大多数开发者来说,你应该远离几个Java SE特性或是APIs。这里所说的大多数开发者指的是一般的Java EE开发者而不是库设计者或是基础设施开发者。
坦白地说,从长远来看,大多数团队都应该远离如下的Java特性。不过凡事总有例外的情况。如果你有一个强大的团队,总是能够清楚地意识到自己在做什么,那就按照你的想法去做就行。但对于大多数情况来说,如果你在项目的开发中使用了下面这几个Java特性,那么从长远来看你是会后悔的。
这些应该远离的Java特性有:
- 反射
- 字节码操纵
- ThreadLocal
- 类加载器
- 弱引用与软引用
- Sockets
下面对这些特性进行逐个分析,看看为什么普通的Java开发者应该远离他们:
反射:在流行的库如Spring和Hibernate中,反射自然有其用武之地。不过内省业务代码在很多时候都不是一件好事,原因有很多,一般情况下我总是建议大家不要使用反射。
首先是代码可读性与工具支持。打开熟悉的IDE,寻找你的Java代码的内部依赖,很容易吧。现在,使用反射来替换掉你的代码然后再试一下,结果如何呢?如果通过反射来修改已经封装好的对象状态,那么结果将会变得更加不可控。请看看如下示例代码:
如果这样做就无法得到编译期的安全保证。就像上面这个示例一样,你会发现如果getDeclaredField()方法调用的参数输错了,那么只有在运行期才能发现。要知道的是,寻找运行期Bug的难度要远远超过编译期的Bug。
最后还要谈谈代价问题。JIT对反射的优化程度是不同的,有些优化时间会更长一些,而有些甚至是无法应用优化。因此,有时反射的性能损失可以达到几个数量级的差别。不过在典型的业务应用中,你可能不会注意到这个代价。
总结一下,我觉得在业务代码中唯一合理(直接)使用反射的场景是通过AOP。除此之外,你最好远离反射这一特性。
字节码操纵:如果在Java EE应用代码中直接使用了CGLIB或是ASM库,那么我建议你好好审视一下。就像方才我提到的反射带来的消极影响,使用字节码操纵所带来的痛苦可能是反射的好几倍之多。
更糟糕的是在编译期你根本就看不到可执行的代码。从本质上来说,你不知道产品中实际运行的是什么代码。因此在面对运行期的问题以及调试时,你要花费更多的时间。
ThreadLocal:看到业务代码中如果出现ThreadLocal会让我感到颤抖,原因有二。首先,借助于ThreadLocal,你可以不必显式通过方法调用就可以传递变量,而且会对这种做法上瘾。在某些情况下这么做可能是合理的,不过如果不小心,那么我可以保证最后代码中会出现大量意想不到的依赖。
第二个原因与我每天的工作有关。将数据存储在ThreadLocal中很容易造成内存泄漏,至少我所看到的十个永久代泄漏中就有一个是由过量使用ThreadLocal导致的。连同类加载器及线程池的使用,“java.lang.OutOfMemoryError:Permgen space”就在不远处等着你呢。
类加载器:首先,类加载器是个很复杂的东西。你必须首先理解他们,包括层次关系、委托机制以及类缓存等等。即便你觉得自己已经精通了类加载器,一开始使用时还是会出现各种各样的问题,很可能会导致类加载器泄漏问题。因此,我建议大家还是将类加载器留给应用服务器使用吧。
弱引用与软引用:关于弱引用与软引用,你是不是只知道他们是什么以及简单的使用方式而已?现在的你对Java内核有了更好的理解,那会不会使用软引用重写所有的缓存呢?这么做可不太好,可不能手里有锤子就到处找鼓敲吧。
你可能很想知道我为什么说缓存不太适用使用软引用吧。毕竟,使用软引用来构建缓存可以很好地说明将某些复杂性委托给GC来完成而不是自己去实现这一准则。
下面来举个例子吧。你使用软引用构建了一个缓存,这样当内存行将耗尽时,GC会介入并开始清理。但现在你根本就无法控制哪些对象会从缓存中删除,很有可能在下一次缓存中不再有这个对象时重新创建一次。如果内存还是很紧张,又触发GC执行了一次清理,那么很有可能会出现一个死循环,应用会占用大量CPU时间,Full GC也会不断执行。
Sockets:java.net.Socket简直太难使用了。我认为它的缺陷归根结底源自其阻塞的本质。在编写具有Web前端的典型的Java EE应用时,你需要高度的并发性来支持大量的用户访问。这时你最不想发生的事情就是让可伸缩性不那么好的线程池呆在那儿,等待着阻塞的Sockets。
现在已经出现了非常棒的第三方库来解决这些问题,别自己写了,尝试一下Netty吧。
各位InfoQ读者,Java出现至今经历了多次版本更迭,每次也都会有诸多新特性的加入。在日常的Java开发中,你认为存在哪些Java特性是很容易导致问题的呢?作者提到不建议在普通的应用开发中使用反射,不过对于一些框架或库的开发,离开反射实际上是无法实现的,例如Spring、Struts2等框架,那么在一般的Java项目开发中,你觉得哪些地方有使用反射的必要呢?换句话说,如果不使用反射就实现不了功能或是需求。文中作者也不建议使用字节码操纵,实际上一些框架在实现某些功能时是必须要使用的,比如说Spring在实现AOP时就使用了Java的动态代理与CGLib库两种方式来达成的。那么对于一般的Java项目来说,哪些地方需要用到字节码操纵呢?欢迎各位读者畅所欲言,一起讨论这些有趣的话题。
原文链接地址:
http://www.infoq.com/cn/news/2013/11/six-java-features-to-avoid
相关推荐
在Java中实现远程监控可以利用Java的多种内置特性,如JMX(Java Management Extensions)、RMI(Remote Method Invocation)以及网络编程API。下面我们将详细探讨如何使用Java来构建一个简单的远程监控系统。 首先...
《深入解析Java面试宝典(2012.6):关键知识点详解》 Java作为一门广泛应用的编程语言,其面试题目往往能精准反映应聘者的技术深度与广度。《Java面试宝典(2012.6)》是一份宝贵的资源,针对面试中出现频率最高的...
给我一个你最常见到的runtimeexception。** - `Exception`是所有异常类的超类,分为检查异常(`Checked Exception`)和运行时异常(`Runtime Exception`)。 - `RuntimeException`是运行时异常的基类,不需要显式处理...
在本Java实训项目“嗖嗖移动大厅”中,我们将深入探讨和应用一系列核心Java技术,包括面向对象编程、I/O流、实用类以及集合...通过参与这样的项目,你可以更好地理解和运用Java语言,从而在实际工作中解决复杂问题。
在Java编程语言中,开发一个可以仿照QQ的隐藏窗体功能是一项有趣且实用的挑战。这个功能使得用户能够将应用程序窗口轻松地隐藏到屏幕边缘,以节省桌面空间或者在不使用时保持整洁的视觉效果。以下是实现这一功能的...
6. **Servlet生命周期**:Servlet的生命周期包括初始化(init)、服务(service)、销毁(destroy)三个阶段。Servlet通常由服务器一次性实例化,然后处理多个请求,提高了效率。与CGI(通用网关接口)相比,Servlet...
如果两个对象通过`equals`方法比较返回`true`,则它们应该拥有相同的`hashCode`值。然而,即使两个对象的`hashCode`不同,只要`equals`返回`true`,这两个对象仍然被认为是相等的。这种情况下,通常是因为`hashCode...
Java中一个类只能继承一个父类。 - **implements**:用于实现接口。一个类可以实现多个接口。 #### 4. 实现interface的方式 - 在Java中,实现接口需要通过`implements`关键字。接口中的所有方法都必须是抽象的,...
在一个`.java`源文件中可以定义多个类,但这些类中只能有一个公共(public)类,并且该公共类的名字必须与文件名相同。其余的类必须声明为私有(private)或者默认访问级别(即不加任何访问修饰符)。例如,如果文件...
- **Math.round(11.5)==12**: `Math.round`方法返回离参数最近的整数,如果参数正好处于两个整数中间,则向远离零的方向舍入。 - **Math.round(-11.5)==-11**: 负数的情况同理。 #### 十一、short 类型的赋值问题 ...
当参数正好位于两个整数中间时,`Math.round()`会向远离零的方向舍入。 #### 9. 类型转换与自动提升 在Java中,表达式`s1 + 1`会导致类型自动提升,`short`类型与`int`类型进行运算时,结果会被提升为`int`类型,...
在一个`.java`源文件中可以包含多个类,但有一个重要的限制:每个源文件只能有一个公共类(public class),并且源文件的名字必须与这个公共类的名字完全相同。 ### 2. Java中的`goto`语句 Java语言最初设计时确实...
在给定的“Outlier(Java).rar”压缩包中,包含了一个用Java编写的异常检测程序,这为我们提供了一个实用的工具来处理异常数据。下面我们将深入探讨异常检测的基本概念、方法以及Java实现的关键点。 首先,异常检测...
- **知识点说明**: 在Java中,一个`.java`源文件可以包含多个类定义,但只能有一个公共类(public class),且该公共类的名字必须与文件名相同。 - **示例**: `public class MainClass { ... }` 和 `class HelperClass...
- `&&`和`&`都可以用于逻辑运算,但`&&`具有短路特性,即当第一个表达式为假时,第二个表达式不会被计算。 - `&`除了逻辑运算外,还支持位运算,用于按位与操作。 #### 五、HashMap与Hashtable的区别 - **HashMap*...
综上所述,该研究通过综合运用3DS MAX的建模能力、VRML的网络交互特性、Java3D的高级控制以及LOD技术的性能优化,建立了一个具有高度交互性的别墅虚拟漫游系统,解决了传统建筑设计中信息传达和修改困难的问题,为...