Java常见陷阱(pitfalls)分析

1 using == to compare primitive wrappers objects such as Integer

Integer int1_1 = Integer.valueOf(“1”);Integer int1_2 = Integer.valueOf(1);System.out.println(“int1_1 == int1_2: ” + (int1_1 == int1_2)); // trueSystem.out.println(“int1_1 equals int1_2: ” + int1_1.equals(int1_2)); // trueInteger int2_3 = Integer.valueOf(“1000”);Integer int2_2 = Integer.valueOf(1000);System.out.println(“int2_3 == int2_4: ” + (int2_3 == int2_4)); // falseSystem.out.println(“int2_3 equals int2_4: ” + int2_3.equals(int2_4)); // true

The JVM maintains a cache of Integer objects for the range -128 to 127. For values in this range, the Integer. valueOf() will return the cached value rather than creating a new one. Thus, in the first example the Integer.valueOf(1) and Integer.valueOf(“1”) calls returned the same cached Integer instance. By contrast, in the second example the Integer. valueOf(1000) and Integer. valueOf(“1000”) both created and returned new Integer objects.

JVM维护范围为128到127的整数对象缓存。对于此范围内的值,为整数。valueOf()将返回缓存的值,而不是创建新值。因此,在第一个示例中,Integer.valueOf(1) 和 Integer.valueOf(“1”)调用返回了相同的缓存整数实例。相比之下,在第二个示例中,valueOf(1000) 和 Integer. valueOf(“1000”)创建并返回了新的整数对象。

The == operator for reference types tests for reference equality (i. e. the same object). Therefore, in the first example int1_1 == int1_2 is true because the references are the same. In the second example int2_1 == int2_2 is false because the references are different.

引用类型的==运算符测试引用相等性(即相同的对象)。因此,在第一个示例中,int1_1 == int1_2是真的,因为引用是相同的。在第二个示例中,int2_1 == int2_2为false,因为引用不同。

2 using == to compare strings

public class Hello { public static void main(String[] args) { if(args.length > 0) if(args[0] == “hello”) { // always false, one point to heap , another point to iteral pool System.out.println(“Hello back to you”); else System.out.println(“Are you feeling grumpy today?”); }}

The correct way to test strings is to use the equals(Object) method.

public class Hello2 { public static void main(String[] args) { if(args.length > 0) { if(args[0].equals(“hello”)) System.out.println(“Hello back to you”); else System.out.println(“Are you feeling grumpy today?”); } }}

字符串常量出现时,会先查找常量池,已定义的话不会再定义,引用相同空间的常量值即可:

public class Test1 { public static void main(String[] args) { String s1 = “hello”; // point to iteral pool String s2 = “hello”; // point to the same above if(s1 == s2) System.out.println(“same”); // same else System.out.println(“different”); }}

3 forgetting to free resources

private static void printFileJava6() throws IOException { FileInputStream input; try { input = new FileInputStream(“file.txt”); int data = input.read(); while(data != -1){ System.out.print((char) data); data = input.read(); } } finally { if(input != null) { input.close(); // don’t forget } }}

4 thinking of variables as objects

No Java variable represents an object.

String foo; // NOT AN OBJECT

Neither does any Java array contain objects.

String bar[] = new String[100]; // No member is an object.

The equality operator does NOT test that two objects are equal.

相等运算符不会测试两个对象是否相等。

Applying the equality (==) operator to reference values tests if the values refer to the same object. It does not test whether two (different) objects are “equal” in the intuitive sense.

将相等(==)运算符应用于引用值将测试这些值是否引用同一对象。它不测试两个(不同)物体在直觉上是否“相等”。

Method calls do NOT pass objects at all.

方法调用根本不传递对象。

Java method calls use pass by value1 to pass arguments and return a result.

Java方法调用使用pass-by-value1传递参数并返回结果。

When you pass a reference value to a method, you’re actually passing a reference to an object by value, which means that it is creating a copy of the object reference.

向方法传递引用值时,实际上是按值传递对对象的引用,这意味着它正在创建对象引用的副本。

As long as both object references are still pointing to the same object, you can modify that object from either reference, and this is what causes confusion for some.

只要两个对象引用仍然指向同一个对象,就可以从任意一个引用修改该对象,这就是导致某些人混淆的原因。

However, you are not passing an object by reference2. The distinction is that if the object reference copy is modified to point to another object, the original object reference will still point to the original object.

但是,您没有通过引用2传递对象。区别在于,如果修改对象引用副本以指向另一个对象,则原始对象引用仍将指向原始对象。

void f(Point foo) { foo.x = 42; foo = new Point(3, 4); // Point local foo at a different object.}

5 Not understanding that String is an immutable class

New Java programmers often forget, or fail to fully comprehend, that the Java String class is immutable. This leads to problems like the one in the following example:

新的Java程序员经常忘记或无法完全理解Java字符串类是不可变的。这会导致以下示例中的问题:

public class Shout { public static void main(String[] args) { for(String s : args) { s.toUpperCase(); System.out.print(s); System.out.print(” “); } System.out.println(); }}

The above code is supposed to print command line arguments in upper case. Unfortunately, it does not work, the case of the arguments is not changed.

上述代码应该以大写形式打印命令行参数。不幸的是,它不起作用,参数的情况没有改变。

In reality, the toUpperCase() method returns a String object which is an uppercase version of the String that you call it on. This will probably be a new String object, but if s was already all uppercase, the result could be the existing string.

实际上,toUpperCase()方法返回一个字符串对象,该对象是调用它的字符串的大写版本。这可能是一个新的字符串对象,但如果s已经全部大写,则结果可能是现有字符串。

6 combining assignment and side-effects

i += a[i++] + b[i–]; // more sequence points and side-effects, not allowed

7 Using ‘==’ to test a boolean

Sometimes a new Java programmer will write code like this:

public void check(boolean ok) { if(ok == true) { // redundancy, Note ‘ok == true’ System.out.println(“It is OK”); }}

8 Using ‘assert’ for argument or user input validation

A question that occasionally on StackOverflow is whether it is appropriate to use assert to validate arguments supplied to a method, or even inputs provided by the user.

StackOverflow上偶尔出现的一个问题是,使用assert验证提供给方法的参数,甚至验证用户提供的输入是否合适。

The simple answer is that it is not appropriate.

简单的答案是,这是不合适的。

Better alternatives include:

更好的替代方案包括:

Throwing an IllegalArgumentException using custom code.

使用自定义代码引发IllegalArgumentException。

Using the Preconditions methods available in Google Guava library.

使用Google Guava库中可用的前提条件方法。

Using the Validate methods available in Apache Commons Lang3 library.

使用Apache Commons Lang3库中可用的验证方法。

9 Wildcard imports can make your code fragile

Consider the following partial example:

import com.example.somelib.*; // not recommendimport com.acme.otherlib.*; //public class Test { private Context x = new Context(); // from com.example.somelib …}

10 other syntax pitfall

10.1 Missing a ‘break’ in a ‘switch’ case

10.2 Declaring classes with the same names as standard classes

10.3 Leaving out braces: the “dangling if” and “dangling else” problems

10.4 Octal literals ( 010 is not 10)

10.5 Ignoring method visibility

10.6 Misplaced semicolons and missing braces

10.7 Overloading instead of overriding

10.8 Auto-Unboxing Null Objects into Primitives

11 Threads and Concurrency related

11.1 Too many threads makes an application slower

11.2 incorrect use of wait() / notify()

11.3 Shared variables require proper synchronization

11.4 Thread creation is relatively expensive

12 Nulls and NullPointerException

12.1 Using null to represent an empty array or collection

12.2 Not checking if an I/O stream isn’t even initialized when closing it

12.3 Returning null instead of throwing an exception

12.4 Unnecessary use of Primitive Wrappers can lead to NullPointerExceptions

13 Exception usage

13.1 Catching Throwable, Exception, Error or RuntimeException

13.2 Ignoring or squashing exceptions

13.3 Throwing Throwable, Exception, Error or RuntimeException

13.4 Using exceptions for normal flowcontrol

13.5 Directly subclassing `Throwable

13.6 Catching InterruptedException

13.7 Excessive or inappropriate stacktraces

14 memory leaks related

Java manages memory automatically. You are not required to free memory manually. An object’s memory on the heap may be freed by a garbage collector when the object is no longer reachable by a live thread.

Java自动管理内存。您无需手动释放内存。当活动线程无法访问对象时,垃圾收集器可能会释放堆上的对象内存。

However, you can prevent memory from being freed, by allowing objects to be reachable that are no longer needed. Whether you call this a memory leak or memory packratting, the result is the same — an unnecessary increase in allocated memory.

但是,您可以通过允许不再需要的对象可以访问来防止内存被释放。无论您将其称为内存泄漏还是内存打包,结果都是一样的——分配的内存会不必要地增加。

Memory leaks in Java can happen in various ways, but the most common reason is everlasting object references, because the garbage collector can’t remove objects from the heap while there are still references to them.

Java中的内存泄漏可能以各种方式发生,但最常见的原因是永久的对象引用,因为垃圾收集器无法在仍然存在对对象的引用时从堆中移除对象。

15 Performance Issues

15.1 String concatenation in a loop does not scale

15.2 Using size() to test if a collection is empty is inefficient

15.3 Interning strings so that you can use == is a bad idea

15.4 Using ‘new’ to create primitive wrapper instances is inefficient

15.5 Efficiency concerns with regular expressions

15.6 Small reads / writes on unbuffered streams are inefficient

15.7 Over-use of primitive wrapper types is inefficient

15.8 The overheads of creating log messages

15.9 Iterating a Map’s keys can be inefficient

15.10 Calling System.gc() is inefficient

15.11 Calling ‘new String(String)’ is inefficient

ref

《Java Notes For Professionals》

-End-

郑重声明:本文内容及图片均整理自互联网,不代表本站立场,版权归原作者所有,如有侵权请联系管理员(admin#wlmqw.com)删除。
(0)
用户投稿
上一篇 2022年6月19日
下一篇 2022年6月19日

相关推荐

联系我们

联系邮箱:admin#wlmqw.com
工作时间:周一至周五,10:30-18:30,节假日休息