当前位置: 首页 > 科技观察

当心陷阱!Java10个常见错误

时间:2023-03-17 18:50:28 科技观察

本文转载自公众号《读书芯》(ID:AI_Discovery)有些错误真是防不胜防。但是,根据笔者面试过数十名从菜鸟到高级技术负责人的软件工程师的经验来看,更多的情况是应聘者对基本概念的掌握存在差距。在这篇文章中,作者根据我作为技术主管和面试官的经历,列出了Java开发人员最常犯的错误,看看我是不是中枪了。1.忽略访问修饰符虽然有点莫名其妙,但考生确实经常忘记Java中受保护的访问修饰符的范围。可能因为面试过程中的焦虑和紧张,他们通常只能回答其中一个:protectedfields,methodsandconstructorscanbeaccessedfromsubclasses。受保护的字段、方法和构造函数可以从同一个包中访问。此外,包范围帮助许多开发人员编写自己的测试:可以从测试路径访问受保护的方法。所以忘记这个属性就相当于在面试中说你从来没有写过测试!2.字符串连接如果你使用很多字符串或者大字符串,你会在连接过程中浪费很多内存。上面的示例创建了一些StringBuilder和String对象:准确地说是10.000.000StringBuilder和10.000.001String。解释:退后一步,看看发生了什么。当使用+运算符进行字符串连接时,会创建一个中间对象来存储连接的结果,然后将结果分配给目标对象。在上面的示例中,总共创建了3个对象:2个用于文本,1个用于连接,它是第一个字符串结果加上第二个字??符串“world!”的副本。因为String是不可变的,所以这种字符串连接是可能的。但是编译器很聪明,把代码变成了下面这样(Java9+不适用,因为它使用了StringContactFacotry,但是结果很相似):这个优化去除了中间连接对象,内存被2个String字面量和1个StringBuilder分割占据。总体而言,字符串对象的数量从O(n2)下降到O(n)。回到第一个例子,编译器优化代码如下:编译器只是优化了内连接,但是这会创建很多StringBuilder和String对象!连接字符串的正确方法如下,只需一个StringBuilder和一个String。3.不使用equals()如果你使用==(比较运算符)而不是调用equals()函数,那么你需要改变这个习惯,结果可能会出人意料。说明:当您想比较两个字符串以及任何其他对象时,不要使用==。==只比较两个操作数的对象引用(内存地址比较)而不是内容。在上面的示例中,string无法启动字符串驻留机制:它与x具有不同的内存地址。4.返回null作者多次发现这个方法:返回null的问题是强制调用者对结果做null检查;在这种情况下,如果没有项目,调用者将返回一个空列表。开发人员总是希望返回异常或特殊对象(如空列表),否则使用该代码的应用程序将受到NullPointerException的影响。5.密码是字符串在字符串对象中存储用户提供的密码是一个安全问题,字符串容易受到内存攻击。应该使用Char[],就像JPasswordField和Password4j所做的那样。但是,如果您谈论的是Web应用程序,大多数Web容器都将HttpServletRequest对象中的明文密码作为字符串传递,因此开发人员对此无能为力。图源:unsplash说明:字符串由Java虚拟机(JVM)缓存(驻留),存储在PermGen空间(Java8之前)或堆空间。在这两种情况下,缓存值仅在垃圾收集发生后才被删除:这意味着无法知道何时从字符串池中删除特定值,因为垃圾收集器的行为是不确定的。另一个问题是字符串是不可变的,因此无法清除它们。但是char[]是可变的,可以在处理后删除(例如,用0替换每个元素)。通过这个简单的技巧,攻击者只能在内存中找到一个全为零的数组,而不是明文密码。6.传递null传递null就是想当然地认为调用代码可以管理null。如果不能,则应用程序必须抛出NullPointerException。此外,显式传递null会使代码越来越混乱。下面是一个典型的例子:当调用init()时,没有可用的User对象。那么,如果没有User,为什么要调用一个操作User的函数呢?如果您需要grantAccessToUser()中的逻辑,您应该从其他函数中提取并使用它,而不是传递null。7.Heavymethods以下示例可能会导致系统性能损失:Pattern.compile()是一个非常耗费资源的函数,不应在每次检查字符串是否匹配相同模式时都调用它。说明:Pattern.compile()预编译模式以使用更快的内存表示。与单个匹配相比,此操作需要大量的计算能力。提高性能的经典方法是在静态字段中缓存Pattern对象,如下所示:每次使用相同的资源密集型无状态对象时都应使用此解决方案。8.这段代码在迭代处理集合时会抛出ConcurrentModificationException。解释:列表迭代器在迭代时从列表中移除某项时表现不佳,例如跳过元素、重复元素、索引数组末尾等。这就是为什么许多集合更容易抛出oncurrentModificationException的原因。使用低级数组迭代器:9.使用“返回代码”而不是抛出异常从某种意义上说,开发人员认为异常是不祥之兆,因此他们倾向于编写返回奇怪值的函数,如-1或“C_ERR”。这是值得创建自定义异常的典型案例。该示例可以重写如下:如您所见,代码的可读性和可维护性要高得多。调用者只需要读取DeviceStartException的内容,而不必处理每个返回码。10.使用StringBuffer由于StringBuffer的同步特性,这个例子会产生大量的内存使用。在更复杂的环境中,读者可能会错误地认为一些不必要的同步是必要的。如果您的项目中有StringBuffer,可能是因为某些遗留API(即Java5之前的版本)需要它,很少是因为您的代码试图在并发环境中附加String。改用StringBuilder:在Java5中引入,它的所有操作都是异步的。这只是我在面试和活跃项目中看到的一些错误。我还没有提到面向对象编程(OOP)、设计模式、过度设计、内存泄漏等的陷阱。来源:xkcd如果你有这些问题,那么是时候改变你的编码风格了。这并不难,避免这些陷阱可以增强开发人员的体验,并让这个人更主动地为下一次面试做好准备。使用像SonarQube这样的静态代码分析器,它可以指出实际错误并突出显示潜在错误。资料来源:Unsplash更重要的是要不断学习,不仅是语法,还有任何编程语言背后的理论。多敲代码多练习,让小错误远离你~