细数那些Java程序员最容易犯那些错
java作为最受欢迎程度榜榜首语言,自然是广大开发者使用最多的语言。正因为有如此广泛的使用性,java开发中发生异常也比比皆是,接下来我们就来看看那些java开发中最容易出现的那些错误。
1、重复造轮子
一个明显的错误就是Java程序员习惯性的忽略已经存在的大量的库。在你决定造一个轮子之间,我建议你试着先搜一下是否有已经存在库。例如日志方面,有logback,新log4j,网络方面,有Netty或者Akka。有一些库,已经逐步变成了标准,比如Java8中加入的Joda-Time。 下面讲述的是我上一个项目中的个人经历。有一部分用于HTML转义的代码是一个开发自己完成的。这个代码正常工作了多年,但是又一次遇到了一个用户输入,代码陷入了死循环。这个用户发现应用没有反应,又重新输入了一遍,服务器因为这个死循环挂了。如果这个开发使用已有的HTML转义工具,比如Google Guava项目提供的HtmlEscaper,这个严重的问题可能就不会出现。并且现在市面上流行的大部分的开源库,背后都有团队和社区在支持,类似这样的错误,都能够及时的被修复。
2、在Switch-Case中错误的使用break
这是一个很尴尬的问题,但是仍然在实际开发中经常出现。瀑布特性在switch语句中有时会非常有用,但是必要的break关键字的缺失,有时会带来灾难性的后果。比如在下面的代码中,如果在case 0中忘记放一个break关键字,代码会继续向下执行,就会在Zero之后再输出一个One: 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这样的工具检查出来。
3、忘记释放资源
一旦打开一个文件,或者建立一个网络连接,一个非常重要的习惯是记得关闭资源。并且一定记得,如果在使用类似这样的资源过程中出现了错误,在异常处理中,也需要做对应的关闭操作。可能有人会说,FileInputStream对象在GC的时候,Java终结器(finalizer)会自动调用其close()方法,但是我们知道,我们无法预知GC在什么时候开始,所以我们无法预知在执行GC之前,会有多少资源无法及时关闭。为了避免这种情况,Java7推出的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(); } } }
try-with-resources语法适用于所有实现了AutoClosable接口的类。它能保证每一个资源及时的关闭。
4、内存泄露
Java使用自动内存管理,所以大部分时间,我们都不会去关心内存的分配和释放,但是,这并不意味着Java开发人员需要忽略内存。在Java应用中,内存的问题也经常出现。我们知道,对象如果没有被引用了,这个对象就会被释放,但是并不意味着,就不会出现内存泄露的问题。在Java中,造成内存泄露的原因有很多,但最容易出现的情况就是对象引用无法释放,因为GC在回收堆内存的时候,如果一个对象仍然被其他对象引用,这个对象空间是不会被回收的,举个例子,如果在类中,有一个静态字段引用到一个集合,假如我们没有手动的在使用完成这个集合之后,将他设置为null,那么这个集合及这个集合中的对象,是永远不会被回收的,因为类静态字段是不会被GC的。 比如还有一种造成内存泄露的原因,就是一组对象互相引用对方,就是我们经常说的循环引用,因为循环引用,所以GC不能确定这些互相引用的对象是否还有继续存活的必要。还有一种情况,就是使用JNI时的非堆内存泄露。 一个典型的内存泄露例子: final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);final Deque 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(); }
在上面的例子中,我们创建了两个定时任务。第一个定时任务,从deque中获取了最后的一个数字”numbers”并判断,如果这个数字能被51整除,则打印该数字和deque的大小。第二个定时任务,不断的向deque中添加数据。两个任务都间隔10ms执行。如果这个代码执行,你会发现,deque的大小会持续的增加,直到deque中的数据占满整个堆空间。为了阻止这种情况的发生,我们可以使用pollLast方法来代替peekLast方法,因为pollLast方法会在拿到最后一个元素之后,把这个元素从deque中移除。
5、过度产生垃圾数据
过度产生垃圾数据的意思,是程序运行中大量产生短声明周期的对象。这回导致GC频繁的执行,从内存中回收空间,GC的执行是需要完成堆扫描的,这对系统的性能影响是非常大的。下面是一个小例子: String oneMillionHello = "";for (int i = 0; i < 1000000; i++) { oneMillionHello = oneMillionHello + "Hello!"; } System.out.println(oneMillionHello.substring(0, 6));
在Java中,字符串是不可变的,所以每一次循环都会创建一个新的字符串对象。为了改进这种代码,我们可以使用StringBuilder来代替: StringBuilder oneMillionHelloSB = new StringBuilder(); for (int i = 0; i < 1000000; i++) { oneMillionHelloSB.append("Hello!"); } System.out.println(oneMillionHelloSB.toString().substring(0, 6));
第二个版本的代码,在执行的时候会提高不少的性能。
这些错误每个都很经典,解决它们对你的能力绝对是极大的提升。当然这并不是全部,如果还想了解关于这方面的其他内容,不妨关注我们,接下来,我还会为你带来容易出现的其他问题。希望可以帮你在java之路上扫清障碍。
细数那些Java程序员最容易犯那些错的更多相关文章
- 惊呆了!Java程序员最常犯的错竟然是这10个
和绝大多数的程序员一样,我也非常的宅.周末最奢侈的享受就是逛一逛技术型网站,比如说 programcreek,这个小网站上有一些非常有意思的主题.比如说:Java 程序员最常犯的错竟然是这 10 个, ...
- Java程序员最常犯的错误盘点之Top 10
1. 数组转ArrayList 为了实现把一个数组转换成一个ArrayList,很多Java程序员会使用如下的代码: Arrays.asList确实会返回一个ArrayList对象,但是该类是Arra ...
- Java程序员岗位
Java程序员岗位面试题有哪些? 1.面向对象的特征有哪些方面(1)抽象:抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面.抽象并不打算了解全部问题,而只是选择 ...
- JAVA程序员面试宝典
程序员面试之葵花宝典 面向对象的特征有哪些方面 1. 抽象:抽象就是忽略一个主题中与当前目标2. 无关的那些方面,3. 以便更充分地注意与当前目标4. 有关的方面.抽象并不5. 打算了解全部问题 ...
- 聊聊阿里社招面试,谈谈“野生”Java程序员学习的道路
引言 很尴尬的是,这个类型的文章其实之前笔者就写过,原文章里,笔者自称LZ(也就是楼主,有人说是老子的简写,笔者只想说,这位同学你站出来,保证不打死你,-_-),原文章名称叫做<回答阿里社招面试 ...
- Java程序员面试之葵花宝典
程序员面试之葵花宝典 1.面向对象的特征有哪些方面 抽象:抽象就是忽略一个主题中与当前目标 无关的那些方面, 以便更充分地注意与当前目标有关的方面.抽象并不打算了解全部问题,而 只是选择其中的一部 ...
- Java程序员面试题收集(6)
<!————————————————————————————基础题122道,代码题19道————————————————————————————> JAVA相关基础知识1.面向对象的特征有 ...
- [转载]一个标准java程序员的进阶过程
第一阶段:Java程序员 技术名称 内 容 说明 Java语法基础 基本语法.数组.类.继承.多态.抽象类.接口.object对象.常用类(Math\Arrarys\S ...
- Java程序员
从生存.制胜.发展三个方面入手,为大家展示出程序员求职与工作的一幅3D全景图像.本书中既有在公司中的生存技巧,又有高手达人的进阶策略,既有求职攻略的按图索骥,又有入职后生产环境的破解揭秘. 书中浓缩了 ...
随机推荐
- Ubuntu 14.04.2配置rsync
ubuntu系统自带rsync,首先配置/etc/default/rsync,启用daemon模式 修改/etc/rsyncd.conf配置文件 cat /etc/rsyncd.conf # samp ...
- 合并K个有序链表
方法一:采用归并的思想将链表两两合并,再将两两合并后的链表做同样的操作直到合并为只有一个新的链表为止. 归类的时间复杂度是O(logn),合并两个链表的时间复杂度是O(n),则总的时间复杂度大概是O( ...
- Java 多线程实战
Java多线程 public class ThreadTest { public static void main(String[] args) throws InterruptedException ...
- MySQL 开启慢查询日志与普通日志
一.开启满查询日志 1.查看慢查询日志 SHOW VARIABLES LIKE '%slow%'; 2.开启慢查询日志 set GLOBAL slow_query_log =on; 3.设置慢查询日志 ...
- 关于异常System.ArgumentException
什么是System.ArgumentException 当向方法提供的参数之一无效时引发的异常. 继承 Object Exception SystemException ArgumentExcepti ...
- 开源项目 04 PdfSharp
using PdfSharp.Drawing; using PdfSharp.Pdf; using System; using System.Collections.Generic; using Sy ...
- Uncaught SyntaxError: Unexpected token o
浏览器报Uncaught SyntaxError: Unexpected token o 这原因是你ajax获取数据的时候对数据进行错误操作,比如使用了 JSON.parse(data) 对数据进行转 ...
- Linux离线安装Docker
1.从官方下载Docker安装包并上传至虚拟机 https://download.docker.com/linux/static/stable/x86_64/ 2.解压安装包 tar -xvf doc ...
- NFV实验平台
NFV架构如下图所示. NFVI对应于数据平面,数据平面转发数据并提供用于运行网络服务的资源. MANO对应于控制平面,该控制平面负责构建各种VNF之间的连接以及编排NFVI中的资源. VNF层对应于 ...
- grandle Project sync failed.please fix your project and try again
Android Studio导入项目或者新建项目想运行的时候可能会报错Gradle project sync failed. Please fix your project and try again ...