有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误

有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误

Java 是一种复杂的编程语言,很长一段时间以来一直主导着许多生态系统。可移植性、自动垃圾回收及其温和的学习曲线是使其成为软件开发的绝佳选择的一些因素。但是,与任何其他编程语言一样,它仍然容易受到开发人员错误的影响。

本文探讨了 Java 开发人员最常犯的 10 大错误以及避免这些错误的一些方法。

Java 是一种编程语言,但随着时间的推移,它已经广泛存在于可以使用软件的任何地方。Java 采用面向对象编程的概念设计,消除了其他语言(如 C 或 C++)、垃圾回收和架构无关的虚拟机的复杂性,创造了一种新的编程方式。此外,它的学习曲线很平缓,并且似乎成功地遵守了自己的座右铭——一次编写,到处运行,这几乎总是正确的; 但 Java 问题仍然存在。我将解决我认为是最常见错误的 10 个 Java 问题。

java项目开发技术难点

常见错误 #1:忽略现有库

Java 开发人员忽视无数用 Java 编写的库绝对是一个错误。在重新发明轮子之前,请尝试搜索可用的库 – 其中许多库已经在其存在的多年中得到了完善,并且可以免费使用。这些可以是日志库(如 和 Log4j),也可以是网络相关库(如 Netty 或 NIO)。一些库(如 Joda-Time)已成为事实上的标准。

常见错误 #2:在 -case 块中缺少 ‘break’ 关键字

这些 Java 问题可能非常令人尴尬,有时直到在生产环境中运行后才会被发现。 语句中的透传行为通常很有用;但是,如果不希望出现此类行为,则缺少 “break” 关键字可能会导致灾难性的结果。如果你忘记在下面的代码示例中的 “case 0” 中输入一个 “break”java项目开发技术难点,程序将写 “Zero” 后跟 “One”,因为这里的控制流将遍历整个 “” 语句,直到它到达 “break”。例如:

void () {

int = 0;

() {

case 0:

.out.(“Zero”);

case 1:

.out.(“One”);

break;

case 2:

.out.(“Two”);

break;

:

.out.(“”);

在大多数情况下,更简洁的解决方案是使用多态性并将具有特定行为的代码移动到单独的类中。可以使用静态代码分析器(例如 和 PMD)来检测诸如此类的 Java 错误。

常见错误 #3:忘记释放资源

每次程序打开文件或网络连接时,Java 初学者在使用完资源后释放资源非常重要。如果在对此类资源执行操作期间引发任何异常,则应采取类似的谨慎措施。有人可能会争辩说 有一个终结器,它在垃圾回收事件上调用 close() 方法;但是,由于我们无法确定垃圾回收周期何时开始,因此 流可能会无限期地消耗计算机资源。事实上,Java 7 中专门针对这种情况引入了一个非常有用且简洁的语句,称为 try-with-:

void () {

try( input = new (“file.txt”)) {

int data = input.read();

while(data != -1){

.out.print((char) data);

data = input.read();

常见错误 #4:内存泄漏

此类内存泄漏背后的另一个潜在原因是一组对象相互引用,导致循环依赖关系,因此垃圾回收器无法决定是否需要这些具有交叉依赖关系引用的对象。另一个问题是使用 JNI 时非堆内存中的泄漏。

可能如下所示:

final vice vice = .ol(1);

final Deque = new ();

final = new (51);

vice.(() -> {

= .();

if ( != null && .().() == 0) {

.out.(“: ” + );

.out.(“Deque size: ” + .size());

}, 10, 10, .);

vice.(() -> {

.add(new (.()));

}, 10, 10, .);

try {

vice.(1, .DAYS);

} catch ( e) {

e.();

此示例创建两个计划任务。第一个任务从名为 “” 的 deque 中获取最后一个数字,并打印数字和 deque 大小,以防数字能被 51 整除。第二个任务将数字放入 deque 中。这两个任务都以固定速率计划,每 10 毫秒运行一次。如果执行了代码,您将看到 deque 的大小永久增加。这最终将导致 deque 被占用所有可用堆内存的对象填充。为了防止这种情况同时保留此程序的语义,我们可以使用不同的方法从 deque 中获取数字:“”。与方法 “” 相反,“” 返回元素并将其从双端队列中删除java项目开发技术难点,而 “” 仅返回最后一个元素。

要了解有关 Java 中内存泄漏的更多信息,请参阅我们揭开此问题神秘面纱的文章。

常见错误 #5:过度的垃圾分配

当程序创建大量生存期较短的对象时,可能会发生过多的垃圾分配。垃圾回收器持续工作,从内存中删除不需要的对象,这会对应用程序的性能产生负面影响。一个简单的例子:

= “”;

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

= + “Hello!”;

.out.(.(0, 6));

在 Java 开发中,字符串是不可变的。因此,在每次迭代时,都会创建一个新字符串。为了解决这个问题,我们应该使用一个可变的 :

= new ();

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

.(“Hello!”);

.out.(.().(0, 6));

java项目开发技术难点

虽然第一个版本需要相当长的时间来执行,但使用 的版本在明显更少的时间内生成结果。

常见错误 #6:无需使用 Null 引用

避免过度使用 null 是一种很好的做法。例如,最好从方法返回空数组或集合,而不是 null,因为它可以帮助防止 。

List = .();

for ( : ) {

();

如果 () 在某人没有帐户时返回 nulljava项目开发技术难点,则将引发 。要解决此问题有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误,需要进行 null 检查。但是,如果返回的不是 null,而是空列表,则 不再是问题。此外,代码更简洁,因为我们不需要对变量 进行 null 检查。

为了处理想要避免 null 的其他情况,可以使用不同的策略。这些策略之一是使用 类型,它可以是空对象或某个值的包装:

= .();

if(.()) {

.out.(.get());

事实上,Java 8 提供了一个更简洁的解决方案:

= .();

.(.out::);

常见错误 #7:忽略异常

处理异常通常很诱人。但是,对于初学者和经验丰富的 Java 开发人员来说,最佳实践是处理它们。异常是故意引发的,因此在大多数情况下,我们需要解决导致这些异常的问题。不要忽视这些事件。如有必要,您可以重新引发它,向用户显示错误对话框,或向日志添加消息。至少,应该解释为什么没有处理异常,以便让其他开发人员知道原因。

= .();

try {

.show();

} catch ( e) {

// Maybe, man. Who cares, ?

突出显示异常无关紧要的更清晰方法是将此消息编码到异常的变量名称中,如下所示:

try { .(); } catch ( ) { }

常见错误 #8:并发修改异常

当使用迭代器对象提供的方法以外的方法迭代集合时修改集合时,会发生此异常。例如,我们有一个帽子列表,我们想删除所有有耳罩的帽子:

List hats = new ();

hats.add(new ()); // that one has ear flaps

hats.add(new ());

hats.add(new ());

for (IHat hat : hats) {

if (hat.()) {

hats.(hat);

如果我们运行此代码,将引发 “”,因为代码在迭代集合时修改了集合。如果处理同一列表的多个线程之一正在尝试修改集合,而其他线程正在迭代该集合,则可能会发生相同的异常。在多个线程中并发修改集合是很自然的事情,但应该使用并发编程工具箱中的常用工具来处理,例如同步锁、为并发修改采用的特殊集合等。在单线程和多线程情况下解决此 Java 问题的方式存在细微差别。下面简要讨论了在单线程方案中可以处理此问题的一些方法:

收集对象并在另一个循环中删除它们

List = new ();

for (IHat hat : hats) {

if (hat.()) {

.add(hat);

for (IHat hat : ) {

hats.(hat);

使用 . 方法

此方法更简洁,并且不需要创建其他集合:

= hats.();

while (.()) {

IHat hat = .next();

if (hat.()) {

.();

使用 的方法

当修改后的集合实现 List 接口时,使用 list 迭代器是合适的。实现 接口的迭代器不仅支持 操作,还支持 add 和 set 操作。 实现了 接口,因此该示例看起来与 方法几乎相同。唯一的区别是 hat 迭代器的类型,以及我们使用 “()” 方法获取该迭代器的方式。下面的代码段显示了如何使用 “.” 和 “.add” 方法将每顶帽子替换为带有宽边帽的耳罩:

IHat = new ();

= hats.();

while (.()) {

IHat hat = .next();

if (hat.()) {

.();

.add();

使用 , 和 add 方法调用可以替换为对 set:

IHat = new ();

= hats.();

while (.()) {

IHat hat = .next();

if (hat.()) {

.set(); // set of and add

使用 Java 8 中引入的流方法在 Java 8 中,程序员能够将集合转换为流,并根据某些条件筛选该流。下面是一个 api 如何帮助我们过滤 hat 并避免 “” 的示例。

hats = hats.().((hat -> !hat.()))

.(.(::new));

“.” 方法将创建一个包含过滤后帽子的新 。如果大量项目要满足过滤条件,从而导致 较大,则这可能是一个问题;因此,应谨慎使用。使用 Java 8 中提供的 List. 方法Java 8 中提供的另一种解决方案,显然也是最简洁的,是使用“”方法:

hats.(IHat::);

就是这样。在后台,它使用 “.” 来完成该行为。

使用专用集合

还有其他针对不同情况进行调整的集合,例如 “” 和 “”。

并发集合修改的另一个可能错误是从集合创建流,并在流迭代期间修改后备集合。流的一般规则是避免在流查询期间修改基础集合。以下示例将显示处理流的错误方式:

List = hats.().peek(hat -> {

if (hat.()) {

hats.(hat);

}).(.(::new));

常见错误 #9:破坏契约

有时,由标准库或第三方供应商提供的代码依赖于应遵守的规则才能使工作正常。例如,它可以是 和 ,当遵循它们时,可以保证 Java 集合框架中的一组集合以及使用 和 方法的其他类的工作。不遵守 并不是那种总是导致异常或破坏代码编译的错误;这更棘手,因为有时它会在没有任何危险迹象的情况下更改应用程序行为。错误代码可能会滑入生产版本并导致一大堆不需要的效果。这可能包括不良的 UI 行为、错误的数据报告、应用程序性能不佳、数据丢失等。幸运的是,这些灾难性的错误并不经常发生。我已经提到了 和 。它用于依赖于哈希和比较对象的集合,如 和 。简单地说,该合约包含两个规则:

违反合约的第一条规则会导致在尝试从 中检索对象时出现问题。第二条规则表示具有相同哈希代码的对象不一定相等。让我们来看看打破第一条规则的影响:

class Boat {

name;

Boat( name) {

this.name = name;

@

( o) {

if (this == o) true;

if (o == null || () != o.()) false;

Boat boat = (Boat) o;

!(name != null ? !name.(boat.name) : boat.name != null);

@

int () {

(int) (Math.() * 5000);

如您所见,类 Boat 覆盖了 和 方法。但是,它破坏了协定,因为 每次调用时都会返回同一对象的随机值。以下代码很可能在哈希集中找不到名为 “” 的船,尽管我们之前添加了这种船:

void main( args) {

Set boats = new ();

boats.add(new Boat(“”));

.out.(“We have a boat named ” : %bn”, boats.(new Boat(“”)));

的另一个示例涉及 方法。以下是官方 java 文档中描述其功能的引述:

的一般约定是,当 虚拟机已确定任何线程(尚未终止)都无法再访问此对象时,将调用它,除非是由于某个其他对象或类的 所执行的操作已准备好终止。 方法可以执行任何操作,包括使此对象再次可供其他线程使用;但是, 的通常目的是在对象被不可撤销地丢弃之前执行清理操作。例如,表示 input/ 连接的对象的 方法可能会执行显式 I/O 事务,以便在永久丢弃对象之前中断连接。

”常见错误 #10:使用 Raw 类型而不是参数化类型

根据 Java 规范,原始类型是未参数化的类型,或者是未从 R 的超类或超接口继承的类 R 的非静态成员。在 Java 中引入泛型之前,没有原始类型的替代品。它从 1.5 版本开始支持泛型编程有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误,泛型无疑是一个重大的改进。但是,由于向后兼容性的原因,留下了一个可能会破坏类型系统的陷阱。让我们看一下以下示例:

List = new ();

.add(10);

.add(“”);

.(n -> .out.((int) n * 2));

这里我们有一个定义为原始 的数字列表。由于它的 type 没有用 type 参数指定,我们可以向其中添加任何对象。但在最后一行中,我们将元素转换为 int,将其加倍,并将加倍的数字打印到标准输出。此代码将编译而不会出错,但一旦运行,它将引发运行时异常,因为我们尝试将字符串强制转换为整数。显然,如果我们向类型系统隐藏必要的信息,它就无法帮助我们编写安全的代码。要解决这个问题,我们需要指定要存储在集合中的对象类型:

List = new ();

.add(10);

.add(“”);

.(n -> .out.((int) n * 2));

与原始版本的唯一区别是定义集合的行:

List = new ();

修复的代码无法编译,因为我们尝试将字符串添加到预期仅存储整数的集合中。编译器将显示一个错误,并指向我们尝试将字符串 “” 添加到列表中的行。参数化泛型类型总是一个好主意。这样,编译器就能够进行所有可能的类型检查,并将类型系统不一致导致运行时异常的可能性降至最低。

结论

Java 作为平台简化了软件开发中的许多事情,它依赖于复杂的 JVM 和语言本身。但是,它的功能,例如删除手动内存管理或体面的 OOP 工具,并不能消除普通 Java 开发人员面临的所有问题。与往常一样,知识、实践和此类 Java 教程是避免和解决应用程序错误的最佳方法 – 因此,请了解您的库、阅读 Java、阅读 JVM 文档和编写程序。也不要忘记静态代码分析器,因为它们可以指向实际的 bug 并突出显示潜在的 bug。

公告:
1. 本站所有资源来源于用户上传和网络,如有侵权请联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系站长处理!
6. 本站不售卖代码,资源标价只是站长收集整理的辛苦费!如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
7. 站长QQ号码 2205675299

资源库 - 资源分享下载网 » 有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误

常见问题FAQ

关于资源售价和售后服务的说明?
本站所有资源的标价均为本站收集资源的辛苦费,不代表资源本身的价值。软件是高智慧高价值的商品,不可能是白菜价。本站资源标价只是赞助费用,收取的赞助费仅用来维持本站的日常运营!毕竟收集整理资料需要投入云计算资源和站长大量的精力。
代码有没有售后服务和技术支持?
由于代码的运行具有不可预见性,本站不保证代码完整可运行,不提供技术支持和售后服务。 本站原创代码都是站长自己开发的,可以有偿提供技术支持服务。 网站里标明【亲测】的代码都是站长亲测过的,其他的代码由于精力有限,没有一一测试,不能保证代码就一定能够使用,更没有技术支持服务,下载前请自行斟酌。
有没有搭建服务?
由于搭建服务比较费时费力,所以本站除了原创代码外均不提供搭建服务。本站分享代码纯属兴趣爱好,不以盈利为目的,请勿咨询有没有搭建服务,谢谢理解。
链接地址失效了怎么办?
请带上资源链接地址联系客服,工作时间内我们看到后将第一时间回复。
关于解压密码
本站资源一般都没有加密,如果发现需要解压密码的,那么就输入 hao.35dc.com 试试。

发表评论

资源库,由老程序员细心甄别、精心筛选,只为提供优质的源码资源

关于我们 联系我们