更新时间:2022-03-17 16:35:08 来源:极悦 浏览1366次
Java是一种复杂的编程语言,在很长一段时间内一直主导着许多生态系统。可移植性、自动垃圾收集及其温和的学习曲线使其成为软件开发中的绝佳选择。但是,与任何其他编程语言一样,它仍然容易受到开发人员错误的影响。本文探讨了Java开发人员最常犯的几个错误以及避免这些错误的一些方法。
1:忽略现有库
对于Java开发人员来说,忽略无数用Java编写的库绝对是一个错误。在重新发明轮子之前,请尝试搜索可用的库——其中许多库在它们存在的多年中已经过完善并且可以免费使用。这些可能是日志库,如logback和Log4j,或网络相关库,如Netty或Akka。一些库,例如Joda-Time,已经成为事实上的标准。
以下是我之前的一个项目的个人经验。负责HTML转义的代码部分是从头开始编写的。它多年来一直运行良好,但最终它遇到了用户输入,导致它陷入无限循环。用户发现服务没有响应,尝试使用相同的输入重试。最终,服务器上为这个应用程序分配的所有CPU都被这个无限循环占用了。如果这个简单的HTML转义工具的作者决定使用可用于HTML转义的知名库之一,例如来自Google Guava的HtmlEscapers,这可能不会发生。至少,对于大多数有社区支持的流行库来说,错误会被社区更早地发现并修复。
2:在Switch-Case块中缺少“break”关键字
这些Java问题可能非常令人尴尬,有时直到在生产环境中运行才被发现。switch语句中的失败行为通常很有用;但是,如果不希望出现这种行为,则缺少“break”关键字可能会导致灾难性的结果。如果你在下面的代码示例中忘记在“case 0”中添加“break”,程序将写“Zero”后跟“One”,因为这里的控制流会遍历整个“switch”语句,直到它达到了“休息”。例如:
public static void switchCasePrimer() {
int caseIndex = 0;
switch (caseIndex) {
case 0:
System.out.println("Zero");
case 1:
System.out.println("One");
break;
case 2:
System.out.println("Two");
break;
default:
System.out.println("Default");
}
}
在大多数情况下,更简洁的解决方案是使用多态性并将具有特定行为的代码移动到单独的类中。可以使用静态代码分析器(例如FindBugs和PMD)检测诸如此类的Java错误。
3:忘记释放资源
每次程序打开文件或网络连接时,Java初学者在完成使用后释放资源非常重要。如果在对此类资源的操作期间引发任何异常,则应采取类似的谨慎态度。有人可能会争辩说FileInputStream有一个终结器,它在垃圾收集事件上调用close()方法。但是,由于我们无法确定垃圾回收周期何时开始,输入流可能会无限期地消耗计算机资源。事实上,Java 7中特别针对这种情况引入了一个非常有用且简洁的语句,称为try-with-resources:
private static void printFileJava7() throws IOException {
try(FileInputStream input = new FileInputStream("file.txt")) {
int data = input.read();
while(data != -1){
System.out.print((char) data);
data = input.read();
}
}
}
此语句可用于任何实现AutoClosable接口的对象。它确保在语句结束时关闭每个资源。
4:内存泄漏
Java使用自动内存管理,虽然忘记手动分配和释放内存是一种解脱,但这并不意味着Java初学者不应该知道内存在应用程序中是如何使用的。内存分配问题仍然存在。只要程序创建了对不再需要的对象的引用,它就不会被释放。在某种程度上,我们仍然可以称之为内存泄漏。Java中的内存泄漏可能以多种方式发生,但最常见的原因是持久的对象引用,因为垃圾收集器无法在仍然存在对它们的引用时从堆中删除对象。可以通过使用包含一些对象集合的静态字段定义类来创建这样的引用,并且在不再需要该集合后忘记将该静态字段设置为null。
这种内存泄漏背后的另一个潜在原因是一组对象相互引用,导致循环依赖,因此垃圾收集器无法决定是否需要这些具有交叉依赖引用的对象。另一个问题是使用JNI时非堆内存中的泄漏。
原始泄漏示例可能如下所示:
final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
final Deque<BigDecimal> numbers = new LinkedBlockingDeque<>();
final BigDecimal divisor = new BigDecimal(51);
scheduledExecutorService.scheduleAtFixedRate(() -> {
BigDecimal number = numbers.peekLast();
if (number != null && number.remainder(divisor).byteValue() == 0) {
System.out.println("Number: " + number);
System.out.println("Deque size: " + numbers.size());
}
}, 10, 10, TimeUnit.MILLISECONDS);
scheduledExecutorService.scheduleAtFixedRate(() -> {
numbers.add(new BigDecimal(System.currentTimeMillis()));
}, 10, 10, TimeUnit.MILLISECONDS);
try {
scheduledExecutorService.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
e.printStackTrace();
}
此示例创建两个计划任务。第一个任务从名为“numbers”的双端队列中获取最后一个数字,并打印数字和双端队列大小,以防该数字可被51整除。第二个任务将数字放入双端队列。这两个任务都以固定的速率安排,每10毫秒运行一次。如果代码被执行,你会看到双端队列的大小一直在增加。这最终将导致双端队列被消耗所有可用堆内存的对象填充。为了在保留该程序的语义的同时防止这种情况,我们可以使用不同的方法从双端队列中获取数字:“pollLast”。与“peekLast”方法相反,“pollLast”返回元素并将其从双端队列中删除,而“peekLast”只返回最后一个元素。
以上就是极悦小编介绍的"零基础Java学习教程中常见的四条大错",希望对大家有帮助,如有疑问,请在线咨询,有专业老师随时为您服务。
0基础 0学费 15天面授
Java就业班有基础 直达就业
业余时间 高薪转行
Java在职加薪班工作1~3年,加薪神器
工作3~5年,晋升架构
提交申请后,顾问老师会电话与您沟通安排学习