译文:Java程序员最易范的十大错误

一直想翻译这篇文章,今天借Danny布置作业的机会,花了大半天时间把它翻完。发布之前搜了搜,在我之前还没有人翻译这篇David Reilly的<Top Ten Errors Java Programmers Make>,这里中文名暂定为《Java程序员最易范的十大错误》。

翻译这么长的文章还是第一次,其中肯定有很多问题。望大家能够及时指出错误..

By David Reilly:Top Ten Errors Java Programmers Make
David Reilly:Java程序员最易范的十大错误


Whether you program regularly in Java, and know it like the back of your hand, or whether you're new to the language or a casual programmer, you'll make mistakes.
无论你的Java编程规范与否,或你了解它如你的手背一般。再或你是一个新手或临时程序员,你都会范错误。

It's natural, it's human, and guess what? You'll more than likely make the same.mistakes that others do, over and over again. Here's my top ten list of errors that we all seem to make at one time or another, how to spot them, and how to fix them.

这是很自然、极富人性的。为什么?你或多或少的和其他人一样一次又一次的范同样的错误。在这里通过我的十大错误列表,我们能同时的弄清楚如何认清以及如何克服它们。

10. Accessing non-static member variables from static methods (such as main)Many programmers, particularly when first introduced to Java, have problems with accessing member variables from their main method.
10. 有许多程序员从静态方法中访问非静态成员(例如Main),尤其初次接触Java的人,他们的问题往往是出在从Main方法中访问成员变量。


The method signature for main is marked static - meaning that we don't need to create an instance of the class to invoke the main method. For example, a Java Virtual Machine (JVM) could call the class MyApplication like this :
Main中的方法签名被标记成静态,这意味着我们不需要创造一个被Main方法调用的类的实例化。例如,Java虚拟机可以像下面这样调用MyApplication类。
MyApplication.main ( command_line_args );

This means, however, that there isn't an instance of MyApplication - it doesn't have any member variables to access!
这意味着,它们仍然不是一个MyApplication的实例或者它们没有被成员变量访问!

Take for example the following application, which will generate a compiler error message.
分析下面这个应用的例子,它将产生一个编译错误的信息。


public class StaticDemo {
public String my_member_variable = "somedata";
public static void main (String args[])
{
// Access a non-static member from static method
System.out.println ("This generates a compiler error" + my_member_variable ); }
}



If you want to access its member variables from a non-static method (like main), you must create an instance of the object.

如果你想从一个非静态方法访问它的成员变量(如Main),你必须创造一个对象的实例。
Here's a simple example of how to correctly write code to access non-static member variables, by first creating an instance of the object.

这里是一个简单的例子,它说明了怎样编写通过创造一个对象实例访问非静态成员变量


public class NonStaticDemo {
public String my_member_variable = "somedata";
public static void main (String args[]) {
NonStaticDemo demo = new NonStaticDemo();
// Access member variable of demo
System.out.println ("This WON'T generate an error" + demo.my_member_variable ); }
}



9. Mistyping the name of a method when overriding
9. 当覆盖时键入一个方法名

Overriding allows programmers to replace a method's implementation with new code. Overriding is a handy feature, and most OO programmers make heavy use of it.
覆盖允许程序员用新代码替换一个方法的实现。覆盖是具有便利的特点,大多书面向对象程序员乐意使用它。

If you use the AWT 1.1 event handling model, you'll often override listener implementations to provide custom functionality. One easy trap to fall into with overriding, is to mistype the method name. If you mistype the name, you're no longer overriding a method - you're creating an entirely new method, but with the same parameter and return type.

如果你使用AWT1.1时间处理模型,你将常常覆盖Listener的实现以提供制定的功能。无类型方法名是覆盖中常见的陷阱。如果你用了无类型的方法名,你不会较长时间的覆盖一个方法---你创造了一个全新的方法,但是它拥有同样的参数和返回类型。

public class MyWindowListener extends WindowAdapter {
// This should be WindowClosed
public void WindowClose(WindowEvent e) {
// Exit when user closes window
System.exit(0); } };



Compilers won't pick up on this one, and the problem can be quite frustrating to detect. In the past, I've looked at a method, believed that it was being called, and taken ages to spot the problem. The symptom of this error will be that your code isn't being called, or you think the method has skipped over its code. The only way to ever be certain is to add a println statement, to record a message in a log file, or to use good trace debugger (like Visual J++ or Borland JBuilder) and step through line by line.
它不能通过编译,问题被显而易见的捕捉。在过去,我看见一个方法即相信它被调用和获得一个发现的问题。这个错误的症状将使你的代码不能被调用,或者你可以认为方法跳过了代码。一种曾经被认为唯一的方式是增加一个输入语句,在日志文件中记录一条信息,或者充分的利用跟踪调试程序(例如Visual J++ 或 Borland JBuilder)一行行的调试。

If your method still isn't being called, then it's likely you've mistyped the name.
如果你的方法仍不能被调用,那么它很可能是一个无类型名。

8. Comparison assignment ( = rather than == )
8. 对照任务(= 胜于 ==)

This is an easy error to make. If you're used other languages before, such as Pascal, you'll realize just how poor a choice this was by the language's designers. In Pascal, for example, we use the := operator for assignment, and leave = for comparison. This looks like a throwback to C/C++, from which Java draws its roots.Fortunately, even if you don't spot this one by looking at code on the screen, your compiler will. Most commonly, it will report an error message like this : "Can't convert xxx to boolean", where xxx is a Java type that you're assigning instead of comparing.
这是一个容易范的错误。如果你之前用过其它语言,如Pascal,你将认识到语言的设计者们的选择是何等的乏味。在Pascal中,例如,我们使用":="操作符来进行分配,和用"="进行比较。这看起来像是退回到了c/c++,即来自Java的根源。
幸运的是,即使你没有从屏幕上的代码中发现这一点,你的编译器也会这么做。通常,它将反馈一个像这样的错误报告:"不能转换xxx成布鲁型",这里的xxx是一个你指派的带比较性质的Java类型。

7. Comparing two objects ( == instead of .equals)
7. 两对象的比较(== 替代 .equals)

When we use the == operator, we are actually comparing two object references, to see if they point to the same object. We cannot compare, for example, two strings for equality, using the == operator. We must instead use the .equals method, which is a method inherited by all classes from java.lang.Object.Here's the correct way to compare two strings.
当我们使用 "==" 操作符,看它们如果是指向同样的对象,我们实际上是比较了两个对象的引用。我们并不能真正的比较,例如,两个相等的的字符串使用"=="操作符。我们必须替换掉".equals" 方法,那是因为继承了一个来自java.lang.Object所有类方法。这里我们以正确的方式比较两个字符串。


String abc = "abc"; String def = "def"; // Bad way
if ( (abc + def) == "abcdef" ) { ... };/ Good way
if ( (abc + def).equals("abcdef") ) { ... }



6. Confusion over passing by value, and passing by reference
6. 混淆值的传递和引用传递

This can be a frustrating problem to diagnose, because when you look at the code, you might be sure that its passing by reference, but find that its actually being passed by value. Java uses both, so you need to understand when you're passing by value, and when you're passing by reference.

这能诊断问题阻止它的发生,所以当你看代码时,你多半确信它传递了引用,但是你会发现它却传递的是值。Java两者都会使用,所以你必须了解它的运作,当你传递值和传递引用的时候。

When you pass a primitive data type, such as a char, int, float, or double, to a function then you are passing by value. That means that a copy of the data type is duplicated, and passed to the function. If the function chooses to modify that value, it will be modifying the copy only. Once the function finishes, and control is returned to the returning function, the "real" variable will be untouched, and no changes will have been saved. If you need to modify a primitive data type, make it a return value for a function, or wrap it inside an object.
当你传递一个原始数据类型,正如用char、int、float或double定义一个函数时,你其实正在传递一个值。这意味着一个复制的数据类型被拷贝出来了,即传递到一个函数。如果函数选择修改其值,则被修改的仅仅是它的拷贝。一旦函数结束,控制器会回到返回函数,实时变量将未受影响,没有改变的地方将被存储。
如果你需要修改原始数据类型,使它返回一个函数值或者隐藏它的一个内部对象

When you pass a Java object, such as an array, a vector, or a string, to a function then you are passing by reference. Yes - a String is actually an object, not a primitive data type. So that means that if you pass an object to a function, you are passing a reference to it, not a duplicate. Any changes you make to the object's member variables will be permanent - which can be either good or bad, depending on whether this was what you intended.On a side note, since String contains no methods to modify its contents, you might as well be passing by value.
当你传递一个Java对象时,就像你传递引用时函数中的一个数组、一个矢量、一个字符串。的确一个字符串是一个真实的对象,而不是一个原始数据类型。所以意味着如果你传递一个对象到函数,你也就传递了一个引用到函数,而并不是副本。你创造的对象成员变量的任何改变将是永恒的---不是好的就是坏的,这依赖于你最初的打算。有一点注意,字符串包含无方法修改的内容时,你最好通过值来传递。

5. Writing blank exception handlers
5. 编写空的异常处理

I know it's very tempting to write blank exception handlers, and to just ignore errors. But if you run into problems, and haven't written any error messages, it becomes almost impossible to find out the cause of the error. Even the simplest exception handler can be of benefit. For example, put a try { .. } catch Exception around your code, to catch ANY type of exception, and print out the message. You don't need to write a custom handler for every exception (though this is still good programming practice). Don't ever leave it blank, or you won't know what's happening.For example:
我知道空白的异常处理如忽略错误般非常的诱人。但是如果你在程序中运行的话,不会提示任何的错误提示,它会变得几乎不可能找到错误的原因。即使最简单的异常处理也是对程序排错极为有利的。例如,用一个try { .. } catch来包围你的代码,即可以捕获任何类型的异常和输出信息。你不需要为每一个异常写一个习惯性的处理方式(尽管这仍是一个好的编程习惯)不要让它留空白,因为你将不知道会发生什么。例如:
public static void main(String args[]) {
try { // Your code goes here.. }
catch (Exception e){
System.out.println ("Err - " + e ); }
}



4. Forgetting that Java is zero-indexed
4. 忘记在Java中数组是从0开始索引

If you've come from a C/C++ background, you may not find this quite as much a problem as those who have used other languages. In Java, arrays are zero-indexed, meaning that the first element's index is actually 0. Confused? Let's look at a quick .example:

如果你拥有C/C++的编程背景,你可能不会发现相同的问题在使用其它语言时。
在Java中,数组是以0开始索引的,这意味着第一个索引元素是0。困惑了?让我们看看下面这个例子:
// Create an array of three strings
String[] strArray = new String[3]; // First element's index is actually 0
strArray[0] = "First string";// Second element's index is actually 1
strArray[1] = "Second string"; // Final element's index is actually 2
strArray[2] = "Third and final string";




In this example, we have an array of three strings, but to access elements of the array we actually subtract one. Now, if we were to try and access strArray[3], we'd be accessing the fourth element. This will case an ArrayOutOfBoundsException to be thrown - the most obvious sign of forgetting the zero-indexing rule.
在例子中,我们定义了一个三个字符串的数组,但是访问数组的元素我们减去了一个。现在,如果试着访问strArray[3],则我们是访问了第四个元素。这样将导致一个ArrayOutOfBoundsException异常被抛出,这是忘记在Java中数组是从0开始索引规则时最常见异常提示。

Other areas where zero-indexing can get you into trouble is with strings. Suppose you wanted to get a character at a particular offset within a string. Using the String.charAt(int) function you can look this information up - but under Java,the String class is also zero-indexed. That means than the first character is at offset 0, and second at offset 1. You can run into some very frustrating problems unless you are aware of this - particularly if you write applications with heavy string processing. You can be working on the wrong character, and also throw exceptions at run-time. Just like the ArrayOutOfBoundsException, there is a string equivalent. Accessing beyond the bounds of a String will cause a StringIndexOutOfBoundsException to be thrown, as demonstrated by this example.
其它地方的从0开始索引规则也会让你陷入困境,例如在字符串中。假设你想在一个字符串的特定偏移量中获得一个字符。使用String.charAt(int)函数你可以看到这个信息,基于Java,0开始索引规则在字符串中也同样适用。这意味着第一个字符的偏移量是0,第二个字符的偏移量是1。你可以很不 嚣于这一些,除非你已经完成了这点:编写了大量的包含字符串处理的应用程序。你可以在编写代码时出现错误的字符,可以通过抛出异常在运行的时候捕获它们。就像抛出一个
ArrayOutOfBoundsException,与一个字符串等价。访问时超越了字符串的界限,将导致一个StringIndexOutOfBoundsException下面的例子证实了这种情况.
public class StrDemo {
public static void main (String args[]) {
String abc = "abc"; System.out.println ("Char at offset 0 :" + abc.charAt(0) );
System.out.println ("Char at offset 1 : " + abc.charAt(1) );
System.out.println ("Char at offset 2 : " + abc.charAt(2) );
// This line should throw a StringIndexOutOfBoundsException
System.out.println ("Char at offset 3 : " + abc.charAt(3) ); } }




Note too, that zero-indexing doesn't just apply to arrays, or to Strings. Other parts of Java are also indexed, but not always consistently. The java.util.Date, and java.util.Calendar classes start their months with 0, but days start normally with 1. This problem is demonstrated by the following application.

这里也要注意。0开始索引规则不被应用到数组或字符串中。Java的其它部分也会被索引,但不总是一贯性的。在java.util.Date和java.util.Calendar类中月份是以0开始,但天数的一般是以1开始。这个问题在下面这个应用中得以证明。


import java.util.Date;
import java.util.Calendar;
public class ZeroIndexedDate {
public static void main (String args[]) {
// Get today's date
Date today = new Date();
// Print return value of getMonth
System.out.println ("Date.getMonth() returns : " + today.getMonth());
// Get today's date using a Calendar
Calendar rightNow = Calendar.getInstance();
// Print return value of get ( Calendar.MONTH )
System.out.println ("Calendar.get (month) returns : " + rightNow.get ( Calendar.MONTH ));
}
}


Zero-indexing is only a problem if you don't realize that its occurring.

If you think you're running into a problem, always consult your API documentation.
如果你不引起重视,在使用从0开始索引规则时总会出现问题。如果你不想在运行时陷入其中的话,请常查阅你的API文档。

3. Preventing concurrent access to shared variables by threads
3. 防止线程在共享变量中并行存取

When writing multi-threaded applications, many programmers (myself included) often cut corners, and leave their applications and applets vulnerable to thread conflicts. When two or more threads access the same data concurrently, there exists the possibility (and Murphy's law holding, the probability) that two threads will access or modify the same data at the same time. Don't be fooled into thinking that such problems won't occur on single-threaded processors. While accessing some data (performing a read), your thread may be suspended, and another thread scheduled. It writes its data, which is then overwritten when the first thread makes its changes.

当两个或更多的线程同时访问同样的数据时,它们可能当编写多线程应用时,许多程序员(包括我自己也是)常常喜欢抄近路,常遗忘应用程序和小应用程序具有线程冲突的缺点。(概率遵循墨菲法则)同时访问或修改同一个数据。不要被这些东西搅昏了,这些问题不会出现在单线程处理中。当存取数据(执行读操作)你的线程可能因其它线程的介入而暂停。第二个线程写入时会覆盖第一个线程的修改的地方。

Such problems are not just limited to multi-threaded applications or applets. If you write Java APIs, or JavaBeans, then your code may not be thread-safe. Even if you never write a single application that uses threads, people that use your code WILL.

这样的问题不仅仅局限与多线程应用程序或小应用程序之中。如果你写过Java APIs或JavaBeans,那么你的代码可能不具线程安全性。即使你从不使用线程写一个单独的应用程序,人们也有会有使用你的代码的意愿。


For the sanity of others, if not yourself, you should always take precautions to prevent concurrent access to shared data.How can this problem be solved? The simplest method is to make your variables private (but you do that already, right?) and to use synchronized accessor methods. Accessor methods allow access to private member variables, but in a controlled manner.
其他意识清醒的人中如果不包括你,你应该尽可能的防范共享数据时的并行存取。怎样能解决这个问题?最简单的方法是使用静态变量(你是否已经这么做了?)或使用同步存取方法。存取方法允许访问静态成员变量,但仅能在控制方式中。


Take the following accessor methods, which provide a safe way to change the value of a counter.
感受下面的存储程序,它以一种安全的方式修改计数器的值。


public class MyCounter {
private int count = 0; // count starts at zero
public synchronized void setCount(int amount) { count = amount; }
public synchronized int getCount() { return count; }
}


2. Capitalization errors
2. 大写错误

This is one of the most frequent errors that we all make. It's so simple to do, and sometimes one can look at an uncapitalized variable or method and still not spot the problem. I myself have often been puzzled by these errors, because I recognize that the method or variable does exist, but don't spot the lack of capitalization.While there's no silver bullet for detecting this error, you can easily train yourself to make less of them. There's a very simple trick you can learn.
这是我们最容易范的错误。其实它做起来的确很简单,但有时却明明看到一个未大写的变量或方法却仍不能发现问题。我自己常常为此而迷惑不解,因为我认为这些方法和变量都是有意义的,但忽略了它们未大写。这里不能用银子弹帮你检测错误,你只能训练自己减少它们的发生。这里有一个非常简单的窍门,你可以试着学习它。

all methods and member variables in the Java API begin with lowercase letters
在Java API中所有的方法和变量成员都以小写字母开头。

all methods and member variables use capitalization where a new word begins e.g - getDoubleValue()
所有的方法和成员变量的新词开始都使用大写字母,例如:getDoubleValue()

If you use this pattern for all of your member variables and classes, and then make a conscious effort to get it right, you can gradually reduce the number of mistakes you'll make. It may take a while, but it can save some serious head scratching in the future.

如果使用这样的方式记忆你的成员变量和类,努力把它变成一种感觉,你就能逐步的错误的发生。这可能需要一段时间,但它也许能在未来挽救一些重大失误。



(drum roll)
And the number one error that Java programmers make !!!!!
接下来是排名第一的Java程序员易范的错误!!!!

1. Null pointers!
1. 空指针!

Null pointers are one of the most common errors that Java programmers make. Compilers can't check this one for you - it will only surface at runtime, and if you don't discover it, your users certainly will.When an attempt to access an object is made, and the reference to that object is null, a NullPointerException will be thrown.

空指针是Java程序员易范的最常见错误之一。编译器是不能检查到的---它将仅仅表现在运行时,如果你不能发现的话,你的用户将会发现它。当创建了一个试图访问一个对象和空对象的引用时,一个NullPointerException将被抛出。

The cause of null pointers can be varied, but generally it means that either you haven't initialized an object, or you haven't checked the return value of a function.

由于空指针能够被改变,但通常它意味着你不能初始化一个对象或者你不能检查一个函数中的返回值。



Many functions return null to indicate an error condition - but unless you check your return values, you'll never know what's happening. Since the cause is an error condition, normal testing may not pick it up - which means that your users will end up discovering the problem for you. If the API function indicates that null may be returned, be sure to check this before using the object reference!

许多函数返回一个指示错误条件的Null---但是除非你检查你的返回值,你将不会知道发生了什么。由于一个错误的条件,正常的测试可能不会获得结果---这意味着你的用户将由于你的失误发现问题。如果API函数指明了Null可能被返回,所以在使用对象引用前要确切的检查。

Another cause is where your initialization has been sloppy, or where it is conditional. For example, examine the following code, and see if you can spot the problem.

它原因有可能是你不规范的初始化或者它是有条件的。例如,测试一下代码,看你是否能发现问题所在。




public static void main(String args[]) {
// Accept up to 3 parameters
String[] list = new String[3];
int index = 0;
while ( (index < args.length) && ( index < 3 ) ) { list[index++] = args[index]; }
// Check all the parameters
for (int i = 0; i < list.length; i++) {
if (list[i].equals "-help") { // ... }
else if (list[i].equals "-cp")
{ // ... } // else .. } }



This code (while a contrived example), shows a common mistake. Under some circumstances, where the user enters three or more parameters, the code will run fine. If no parameters are entered, you'll get a NullPointerException at runtime. Sometimes your variables (the array of strings) will be initialized, and other times they won't. One easy solution is to check BEFORE you attempt to access a variable in an array that it is not equal to null.

以上代码(一个人为的例子)显示了常见的错误。在一些情况中,用户键入三个或更多参数,代码将运行得很好。如果没有参数被键入,你将在运行时得到一个NullPointerException。有时你的变量(字符串的数组)将被初始化,但其它时候并不会这样。一种简单的解决方案是检查BEFORE 你尝试访问数组中一个不等于Null的变量。



Summary
概要

These errors represent but some of the many that we all make. Though it is impossible to completely eliminate errors from the coding process, with care and practice you can avoid repeating the same ones. Rest assured, however, that all Java programmers encounter the same sorts of problems. It's comforting to know, that while you work late into the night tracking down an error, someone, somewhere, sometime, will make the same mistake!

这些错误只是我们常范问题的一个代表。尽管它们不可能从编码中完全去除,注意它们你可以避免重复的范同样的错误。放心,所有的Java程序员都会遇到同样的问题。令人欣慰的是,当你在夜深人静是追踪一个错误的时候,某人在某地的某时也在范着和你同样的错误!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值