Java:与旧的,与新的

Java已经接近第7版了。在我看来,必须要有大量的教科书和训练手册来教导那些基于旧版本Java的教学方法,而这些教学方法现在可以有更好的解决scheme。

什么是一些样板代码的情况,特别是你看到人们通过习惯的力量来实现的代码,你发现自己重构利用最新版本的Java?

枚举。 更换

public static final int CLUBS = 0; public static final int DIAMONDS = 1; public static final int HEARTS = 2; public static final int SPADES = 3; 

 public enum Suit { CLUBS, DIAMONDS, HEARTS, SPADES } 

generics,不再需要创build迭代器来遍历集合中的所有元素。 新版本更好,更易于使用,更易于理解。

编辑:

之前:

 List l = someList; Iterator i = l.getIterator(); while (i.hasNext()) { MyObject o = (MyObject)i.next(); } 

 List<MyObject> l = someList; for (MyObject o : l) { //do something } 

使用types为StringBuffer局部variables来执行string连接。 除非需要同步,否则现在推荐使用StringBuilder ,因为这个类提供了更好的性能(大概是因为它是不同步的)。

从标准input中读取一个string:

Java之前的5

 try { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); String str = reader.readLine(); reader.close(); } catch (IOException e) { System.err.println("error when closing input stream."); } 

Java 5

 Scanner reader = new Scanner(System.in); String str = reader.nextLine(); reader.close(); 

Java 6

 Console reader = System.console(); String str = reader.readLine(); 

这是我看到的一个:

String.split()StringTokenizer

StringTokenizer不推荐用于新的代码,但我仍然看到人们使用它。

至于兼容性,Sun做出了巨大的努力,让Java可以向前兼容。 这部分解释了为什么generics非常复杂。 弃用也可以帮助缓解从旧代码到新代码的转换。

老的代码使用线程而不是线程的许多其他select…这些天,我运行的代码很less仍然需要使用原始线程。 他们会更好地服务于抽象层次,特别是Callable / Futures / Executors 。

看到:

java.util.Timer中

javax.swing.Timer中

java.util.concurrent中。*

VARARGS也是有用的。

例如,您可以使用:

 public int add(int... numbers){ int sum = 0 ; for (int i : numbers){ sum+=i; } return sum ; } 

代替:

 public int add(int n1, int n2, int n3, int n4) ; 

要么

 public int add(List<Integer> numbers) ; 

使用Vectortypes的本地variables来保存对象列表。 除非需要同步,否则现在推荐使用ListList实现,比如ArrayList,因为这个类提供了更好的性能(因为它是不同步的)。

格式化打印是在JDK 1.5中引入的。 所以,而不是使用:

 String str = "test " + intValue + " test " + doubleValue; 

或者使用StringBuilder的等价物,

一个可以使用

 String str = String.format("test %d test %lg", intValue, doubleValue); 

后者更可读,无论是string连接还是string生成器版本。 我仍然发现人们很慢地采用这种风格。 例如Log4j框架,并不使用这个,虽然我相信这样做会大大受益。

原始types和包装types之间的显式转换(例如,Integer转换为int,反之亦然),自Java 1.5以来,通过自动装箱/拆箱自动处理。

一个例子是

 Integer myInteger = 6; int myInt = myInteger.intValue(); 

可以简单地写成

 Integer myInteger = 6; int myInt = myInteger; 

但要小心NullPointerExceptions 🙂

问题1:最明显的情况是generics/types特定的集合。 另一个立即想到的是改进的循环,我觉得是更清洁,更容易理解。

问题2:一般来说,我已经将JVM捆绑在我的应用程序的面向客户的应用程序的一侧。 这使我们可以使用新的语言function,而不必担心JVM不兼容。

如果我没有捆绑JRE,为了兼容性原因,我可能会坚持1.4。

自1.5以来的一个简单的改变,但有一点点不同 – 在Swing API访问JFrame的contentPane:

 myframe.getContentPane().add(mycomponent); 

 myframe.add(mycomponent); 

当然,Enums的引入也改变了以往使用常量的许多应用程序的行为方式。

String.format()极大地改进了string操作,而三元if语句对于使代码更容易阅读非常有帮助。

通用集合使得编码更加抗错误。 旧:

 Vector stringVector = new Vector(); stringVector.add("hi"); stringVector.add(528); // oops! stringVector.add(new Whatzit()); // Oh my, could spell trouble later on! 

新:

 ArrayList<String> stringList = new ArrayList<String>(); stringList.add("hello again"); stringList.add(new Whatzit()); // Won't compile! 

使用迭代器:

 List list = getTheList(); Iterator iter = list.iterator() while (iter.hasNext()) { String s = (String) iter.next(); // .. do something } 

或者有时看到的替代forms:

 List list = getTheList(); for (Iterator iter = list.iterator(); iter.hasNext();) { String s = (String) iter.next(); // .. do something } 

现在全部换成:

 List<String> list = getTheList(); for (String s : list) { // .. do something } 

虽然我承认静态导入很容易被滥用,但我喜欢使用

 import static Math.* ; 

在使用大量math函数的类中。 它可以真正减less你的代码的冗长。 但是,我不会推荐它用于较不为人知的库,因为这会导致混淆。

将数字转换为string:

 String s = n + ""; 

在这种情况下,我认为这样做总是有一个更好的方法:

 String s = String.valueOf(n); 

将现有的数组复制到新的数组中:

在Java之前5

 int[] src = new int[] {1, 2, 3, 4, 5}; int[] dest = new int[src.length]; System.arraycopy(src, 0, dest, 0, src.length); 

Java 6

 int[] src = new int[] {1, 2, 3, 4, 5}; int[] dest = Arrays.copyOf(src, src.length); 

以前,我不得不显式创build一个新的数组,然后将源元素复制到新的数组(调用一个有很多参数的方法)。 现在,语法更清晰,新的数组从方法返回,我不必创build它。 顺便说一句,方法Arrays.copyOf有一个名为Arrays.copyOfRange的变体,它复制源数组的特定区域(非常像System.arraycopy )。

for遍历数组和集合的新构造对我来说是最大的。

现在,当我看到样板循环使用索引variables一个接一个遍历数组时,它让我想要尖叫:

 // AGGHHH!!! int[] array = new int[] {0, 1, 2, 3, 4}; for (int i = 0; i < array.length; i++) { // Do something... } 

使用Java 5中引入的for构造replace上面的代码 :

 // Nice and clean. int[] array = new int[] {0, 1, 2, 3, 4}; for (int n : array) { // Do something... } 

干净,简洁,最重要的是,它给代码的含义 ,而不是显示如何做某事。

显然,代码有意义迭代集合,而不是旧for循环说如何遍历数组。

此外,由于每个元素都是独立于其他元素进行处理的,因此可以为未来的并行处理优化而不必更改代码。 (当然只是猜测。)

与可变参数相关; 从Java 5开始的实用程序方法Arrays.asList()使用可变参数非常有用。

我经常发现自己简化了类似的东西

 List<String> items = new ArrayList<String>(); items.add("one"); items.add("two"); items.add("three"); handleItems(items); 

通过使用

 handleItems(Arrays.asList("one", "two", "three")); 

注释

我不知道到目前为止还没有人提到它,但许多框架依赖于注释,例如Spring和Hibernate。 现在很多人不赞成使用xmlconfiguration文件来支持代码中的注释(尽pipe这意味着在从configuration转换到元代码方面失去了灵活性,但通常是正确的select)。最好的例子是比较EJB 2(和更老的)到EJB 3.0以及如何通过注释简化EJB的编程。

我发现注释在与AspectJ或Spring AOP等一些AOP工具结合使用时也非常有用。 这样的组合可以是非常强大的。

更改JUnit 3样式testing:

 class Test extends TestCase { public void testYadaYada() { ... } } 

到JUnit 4式testing:

 class Test { @Test public void yadaYada() { ... } } 

改进的单例模式。 从技术上讲,这些都是受欢迎的回答枚举,但它是一个重要的子类别。

 public enum Singleton { INSTANCE; public void someMethod() { ... } } 

比…更清洁,更安全

 public class Singleton { public static final Singleton INSTANCE = new Singleton(); private Singleton() { ... } public void someMethod() { ... } } 

转换类以使用generics,从而避免不必要的转换。

好的,现在轮到我了。

我不推荐这些变化的90%。

这并不是说用新代码来使用它们不是一个好主意,但是闯入现有代码来将for循环更改为for(:)循环只是浪费时间和破坏某些东西的机会。 (IIWDFWI)如果有效,不要修复它!

如果你在一个真正的开发公司,那么现在这个变化就成了代码审查,testing和可能的debugging。

如果有人无缘无故地重构了这种types的重构,那么我会给他们无尽的帮助。

另一方面,如果你正在编写代码并改变这一行的内容,请随时清理它。

而且,所有以“表演”为名的build议都需要了解优化的规律。 用两个字,不要! 永远! (谷歌的“优化规则,如果你不相信我”)。

如果你正在对源代码树进行这些操作,我有点担心重构这些行。 到目前为止,这些例子似乎并不是单独改变任何工作代码库的理由,但是如果你正在增加新的function,你应该利用所有新的东西。

在这一天结束的时候,这些例子并没有真正去除锅炉板代码 ,他们只是使用更新的JDKs的可pipe理结构来制作漂亮的锅炉板代码

使代码优雅的大多数方法都不在JDK中。

使用Swing的新DefaultRowSorter对表进行sorting而不是从头开始。

Java的新版本很less破坏现有的代码,所以只需要保留旧的代码,并专注于新function如何让您的生活更轻松。

如果你只保留旧的代码,那么使用新function编写新的代码就不那么可怕了。

string比较,我遇到的真正老派的Java程序员会做:

 String s1 = "...", s2 = "..."; if (s1.intern() == s2.intern()) { .... } 

(据说由于性能的原因)

而现在大多数人只是做:

 String s1 = "...", s2 = "..."; if (s1.equals(s2)) { .... } 

使用vector而不是新的集合。

使用类而不是枚举

  public class Enum { public static final Enum FOO = new Enum(); public static final Enum BAR = new Enum(); } 

使用Thread而不是新的java.util.concurrency包。

使用标记接口而不是注释

值得注意的是,Java 5.0已经出现了五年,从那以后只有很小的变化。 你将不得不在很老的代码上工作,仍然在重构它。