Java开发人员必须避免的11种坏习惯
您想成为code review最喜欢的开发人员吗?
您是否梦想将您的拉取请求与最小变更请求合并?
您也想成为一名专业code review吗?
那您就来对地方了!
您一定想知道,您为何应该关心?
为了回答这个问题,我想说,掌握编写代码时的这些注意事项不仅仅是为了给你的同事留下深刻印象(尽管这是一个不错的奖励)。这是为了编写更易于理解、维护和扩展的代码。从长远来看,通过预先解决潜在问题,可以节省时间和精力。
在本文中,您将了解每个 Java 开发人员都应该掌握的11 大简洁代码实践(好的与坏的)。
那么,让我们开始吧!别忘记点赞,收藏,关注~
1. 整数乘法和除法的移位运算符
坏习惯:使用传统方式进行涉及2 的幂的运算
int result = num * 4; // 传统乘法
int result = num / 2; // 传统除法
解释:
这里,乘法和除法明确使用 *
和 /
运算符执行。这种方法效率较低,尤其是在处理 2 的幂时。
好习惯:使用左移和右移运算符
int result = num << 2; // 左移,相当于num乘以4
int result = num >> 1; // 右移,相当于num除以2
解释:
使用移位运算(<<
表示乘法,>>
表示除法)可以使代码更高效,性能更优化。这在涉及 2 的幂的运算中最有用。
2. 使用 String.valueOf() 进行最佳字符串转换
坏习惯:使用+ 运算符进行字符串连接
String result = "" + 3.1415926; // 使用加号进行字符串拼接
解释:
这里,'+'运算符用于字符串转换,其中涉及隐式字符串连接。这种方法效率低下,尤其是在将大量变量转换为字符串时。
好习惯:使用内置方法进行字符串转换
String result = String.valueOf(3.1415926); // 使用String.valueOf()进行转换
解释:
这里,我们使用valueOf()
进行字符串转换和连接。此方法专门用于将其他数据类型转换为字符串,并针对性能进行了优化。
3. 使用 System.arraycopy 复制数组
坏习惯:手动复制数组
for (int i = 0; i < sourceArray.length; i++) {
targetArray[i] = sourceArray[i]; // 手动复制数组元素
}
解释:
这种方法效率较低,特别是对于大型数组,因为它涉及多次迭代和元素分配。
好习惯:使用System.arraycopy() 或 Arrays.copyOf()复制数组
System.arraycopy(sourceArray, 0, targetArray, 0, sourceArray.length); // 使用System.arraycopy()复制数组
解释:
这里,一行代码将所有元素从 sourceArray
复制到 targetArray
。它在较低的级别上运行,从而比通过数组手动迭代更有效率。
4. 使用 isEmpty() 检查空集合
坏习惯:使用length()或size()来检查字符串或集合是否为空
if (str.length() == 0) { /* 检查字符串是否为空 */ }
if (set.size() == 0) { /* 检查集合是否为空 */ }
解释:
这里,length()
用于检查字符串是否为空,size()
用于检查集合是否为空。这些方法有效,但它们使代码的可读性降低。
好习惯:使用isEmpty()检查字符串或集合是否为空
if (str.isEmpty()) { /* 检查字符串是否为空 */ }
if (set.isEmpty()) { /* 检查集合是否为空 */ }
解释:
isEmpty()
方法可用于检查字符串和集合是否为空。它的时间复杂度为O(1),使其更高效、更易读。
5. 尽量减少循环内的异常处理
坏习惯:在循环内处理异常
for (int i = 0; i < list.size(); i++) {
try {
// 一些可能抛出异常的代码
} catch (Exception e) {
// 处理异常
}
}
解释:
这种方法降低了效率,特别是当循环迭代大型集合并频繁引发异常时。如果循环在遇到异常时终止,则可能不需要在循环中捕获异常。
好习惯:处理循环外的异常
try {
for (int i = 0; i < list.size(); i++) {
// 一些可能抛出异常的代码
}
} catch (Exception e) {
// 处理异常
}
解释:
这里,如果发生异常,循环可以继续执行,并确保捕获循环内抛出的异常。
6. 预编译正则表达式
坏习惯:在运行时编译正则表达式
Pattern pattern = Pattern.compile("regex");
Matcher matcher = pattern.matcher(input);
while (matcher.find()) {
// 处理匹配结果
}
解释:
在这里,正则表达式在每次使用时都会在运行时进行编译。当重复使用相同的正则表达式时,会降低性能。
好习惯:预编译和重用正则表达式
private static final Pattern PATTERN = Pattern.compile("regex");
Matcher matcher = PATTERN.matcher(input);
while (matcher.find()) {
// 处理匹配结果
}
解释:
通过预编译重复的正则表达式,然后在需要时重复使用它,我们可以避免冗余编译并提高性能。
7. 避免在检索前预先检查数据是否存在
坏习惯:
if (map.containsKey(id)) {
Role role = map.get(id); // 检查键是否存在然后检索值
}
解释:
在这里,我们首先检查 id
是否存在于 map
中,然后再检索它。这种预先检查是不必要的,因为如果找不到键,map
的 get
方法将返回 null
。
好习惯:
Role role = map.get(id);
if (role != null) {
// 键存在时执行操作
}
解释:
这里我们直接使用 get
方法从 map
中获取 id
,然后检查 role
是否不为 null
。这种方法避免了冗余检查并且产生了更干净、更高效的代码。
8. 集合到数组的有效转换
坏习惯:
List<String> list = Arrays.asList("a", "b", "c");
String[] array = new String[list.size()];
for (int i = 0; i < list.size(); i++) {
array[i] = list.get(i); // 手动将集合元素复制到数组中
}
解释:
在这种方法中,首先计算列表的大小,然后创建一个新数组。这会影响性能,尤其是对于大型集合。
好习惯:
List<String> list = Arrays.asList("a", "b", "c");
String[] array = list.toArray(new String[0]); // 使用toArray()方法转换为数组
解释:
这里,toArray
用一个空数组(new String[0])来调用。这种方法避免了计算列表大小的需要,并允许 toArray
方法内部处理数组大小调整,从而获得更好的性能和更清晰的代码。
9. 使用默认方法
坏习惯:
public interface Logger {
void logError(String message); // 接口定义的方法需要所有实现类都实现它们
}
public class ConsoleLogger implements Logger {
@Override
public void logError(String message) {
System.err.println(message); // 实现类必须实现接口的方法
}
}
解释:
如果需要在接口中添加新方法(例如 logError
),则必须修改所有实现类,这可能会导致代码维护问题和潜在错误。
好习惯:
public interface Logger {
default void logError(String message) {
System.err.println(message); // 接口定义默认方法,提供默认实现
}
}
public class ConsoleLogger implements Logger {
// 无需重写logError方法,除非需要自定义行为
}
解释:
这里 Logger
接口定义了一个默认方法 (logError
) ,该方法提供了记录错误的默认实现,实现类会自动继承这个默认实现,无需修改。
10. 使用日期/时间 API
坏习惯:使用旧版 Date类
Date date = new Date();
int year = date.getYear(); // 已弃用的方法,返回值为year - 1900。
int month = date.getMonth(); // 已弃用的方法,返回值从0开始。
解释:
这个类有各种问题,例如方法的可变性和缺乏清晰度。此类中的大多数方法(例如 getYear()
、getMonth()
和 getDay()
)已被弃用。
好习惯:使用日期/时间 API 中的类(Java 8及以上版本)
LocalDate date = LocalDate.now();
int year = date.getYear();
int month = date.getMonthValue(); // 返回从1开始的月份。
int dayOfMonth = date.getDayOfMonth();
解释:
在这里,我们使用日期/时间 API中的 LocalDate
类。 LocalDate
是不可变的,确保线程安全,并提供了清晰直观的日期操作方法。
11.正确使用 wait()/notify()
坏习惯:
synchronized (lock) {
lock.notify(); // 在没有检查是否有线程等待时调用notify()
}
解释:
这里, notify() 调用并没有检查是否有任何线程实际在等待锁。如果当前没有线程在等待,这可能会导致 IllegalMonitorStateException,从而违反监视器的状态。
**好习惯: 使用 notifyAll() 而不是 notify(): **
java synchronized(lock) { lock.notifyAll(); 通知所有等待线程 }
**解释: ** notifyAll () 安全地通知所有等待线程,并且如果没有线程在等待,则不起作用,从而避免了出现 IllegalMonitorStateException 的可能性。
好吧,如果您已经读完了这篇文章,那么恭喜您。
感谢阅读。如果你喜欢这篇文章,请点击“