Java日常开发的21个坑,你踩过几个?

1. Java的坑之字符串拼接

Java在字符串拼接的时候,如果使用的是加号拼接,每次拼接过程都会生成一个新的字符串对象,会对性能造成一定影响。因此,在Java中进行字符串拼接时,建议使用StringBuilder或者StringBuffer进行拼接,这两种类可以降低不必要的开销。

// bad example

String result = "";

for (int i = 0; i < list.size(); i++) {

result += list.get(i);

}

// good example

StringBuilder builder = new StringBuilder();

for (int i = 0; i < list.size(); i++) {

builder.append(list.get(i));

}

String result = builder.toString();

1.1 字符串格式化

Java中的字符串格式化也是需要注意的地方。如果在多次拼接字符串时,需要使用格式化,可以使用String.format()方法进行格式化。不能使用字符串拼接或加号进行格式化,因为这会生成多个中间字符串对象,非常低效。

// bad example

String result = "Name: " + name + ", Age: " + age;

// good example

String result = String.format("Name: %s, Age: %d", name, age);

2. Java的坑之控制流语句

Java中的控制流语句也存在一些误区。

2.1 for循环中的变量

在for循环中定义的变量,会在循环结束后继续存在于作用域中。因此,在循环外部不能再定义同名变量。这一点容易被忽略。

// bad example

for (int i = 0; i < list.size(); i++) {

// ...

}

int i = 10; // error: variable i is already defined in method

// good example

int i;

for (i = 0; i < list.size(); i++) {

// ...

}

i = 10; // OK

2.2 Break和Continue

Java的break和continue语句也需要注意。如果在循环中使用break或continue,一定要注意代码执行的顺序,否则可能会陷入无限循环。

// bad example: 无限循环

while (true) {

for (int i = 0; i < 10; i++) {

if (i == 5) {

continue; // 跳过5,进入下一次循环

}

System.out.println(i);

}

}

// good example: 正常退出

outer:

for (int i = 0; i < 10; i++) {

for (int j = 0; j < 10; j++) {

if (i == 5 && j == 5) {

break outer; // 退出所有循环

}

System.out.println("i=" + i + ", j=" + j);

}

}

3. Java的坑之异常处理

Java中的异常处理是一个非常重要的话题。在处理异常时,一定要注意抛出异常的时机和异常的处理方式。

3.1 不要过度捕获异常

在捕获异常时,一定要注意不要过度捕获异常。如果过度捕获异常,会使得代码逻辑变得不清晰,也会对性能产生影响。

// bad example

try {

// ...

} catch (Exception e) {

// do nothing

}

// good example

try {

// ...

} catch (IOException e) {

// handle IOException

} catch (SQLException e) {

// handle SQLException

}

3.2 不要忽略异常

在处理异常时,遇到异常一定要加以处理,并且要在处理时给出明确的提示。不能简单地忽略异常或者只打印日志。

// bad example

try {

// ...

} catch (Exception e) {

e.printStackTrace();

}

// good example

try {

// ...

} catch (IOException e) {

System.out.println("Failed to read file: " + e.getMessage());

} catch (SQLException e) {

System.out.println("Failed to query database: " + e.getMessage());

}

3.3 使用finally释放资源

在处理资源时,一定要使用finally语句块来释放资源。不能简单地使用try语句块释放资源,因为如果在try块中发生了异常,资源就不能被释放,这会导致资源泄露。

// bad example

FileInputStream input = null;

try {

input = new FileInputStream("file.txt");

// ...

} catch (IOException e) {

// handle IOException

} finally {

if (input != null) {

try {

input.close();

} catch (IOException e) {

// ignore IOException

}

}

}

// good example

try (FileInputStream input = new FileInputStream("file.txt")) {

// ...

} catch (IOException e) {

// handle IOException

}

4. Java的坑之并发编程

Java的并发编程也需要注意一些问题。

4.1 线程安全

在多线程环境中,一定要注意线程安全。线程不安全的代码会产生非常严重的后果。

// bad example

static int count = 0;

public static void main(String[] args) throws Exception {

for (int i = 0; i < 10; i++) {

new Thread(() -> {

count++;

}).start();

}

Thread.sleep(1000); // wait for all threads finish

System.out.println("count=" + count);

}

// good example

static AtomicInteger count = new AtomicInteger(0);

public static void main(String[] args) throws Exception {

for (int i = 0; i < 10; i++) {

new Thread(() -> {

count.incrementAndGet();

}).start();

}

Thread.sleep(1000); // wait for all threads finish

System.out.println("count=" + count);

}

4.2 死锁

在多线程环境中,死锁是一个非常危险的问题。因此,在并发编程中一定要注意避免死锁。

// bad example

Thread t1 = new Thread(() -> {

synchronized(A) {

try { Thread.sleep(100); } catch (InterruptedException e) {}

synchronized(B) {}

}

});

Thread t2 = new Thread(() -> {

synchronized(B) {

try { Thread.sleep(100); } catch (InterruptedException e) {}

synchronized(A) {}

}

});

t1.start();

t2.start();

t1.join();

t2.join();

// good example

Thread t1 = new Thread(() -> {

try {

if (A.tryLock()) {

try {

Thread.sleep(100);

if (B.tryLock()) {

try {

// ...

} finally {

B.unlock();

}

}

} finally {

A.unlock();

}

}

} catch (InterruptedException e) {

// ...

}

});

Thread t2 = new Thread(() -> {

try {

if (B.tryLock()) {

try {

Thread.sleep(100);

if (A.tryLock()) {

try {

// ...

} finally {

A.unlock();

}

}

} finally {

B.unlock();

}

}

} catch (InterruptedException e) {

// ...

}

});

t1.start();

t2.start();

t1.join();

t2.join();

5. Java的坑之集合类

Java的集合类也需要注意。

5.1 集合类初始化

在集合类初始化时,一定要指定集合的初始容量。如果没有指定初始容量,会带来不必要的开销。

// bad example

List<String> list = new ArrayList<>();

// good example

List<String> list = new ArrayList<>(32);

5.2 遍历集合类

在遍历集合类时,一定要使用迭代器进行遍历。不能使用for循环或者forEach循环进行遍历,因为它们可能会导致遍历过程中集合的结构发生修改,从而产生ConcurrentModificationException异常。

// bad example

List<String> list = new ArrayList<>();

list.add("a");

list.add("b");

list.add("c");

for (String s : list) {

if (s.equals("b")) {

list.remove(s); // error: ConcurrentModificationException

}

}

// good example

List<String> list = new ArrayList<>();

list.add("a");

list.add("b");

list.add("c");

Iterator<String> iter = list.iterator();

while (iter.hasNext()) {

String s = iter.next();

if (s.equals("b")) {

iter.remove();

}

}

5.3 使用equals()比较对象

在比较对象内容时,一定要使用equals()方法。不能简单地使用==比较,因为==比较的是对象的引用是否相等,而不是对象的内容是否相等。

String s1 = "abc";

String s2 = new String("abc");

boolean b1 = (s1 == s2); // false

boolean b2 = s1.equals(s2); // true

后端开发标签