Java开发最常犯的10个错误,打死都不要犯!-程序员宅基地

点击上方“Github爱好者社区”,选择星标

回复“资料”,获取小编整理的一份资料

原文:http://www.programcreek.com/2014/05/top-10-mistakes-java-developers-make/

译文:cnblogs.com/chenpi/p/5508949.html

阅读目录
  • Array转ArrayList

  • 判断一个数组是否包含某个值

  • 在循环内部删除List中的一个元素

  • HashTable与HashMap

  • 使用集合原始类型(raw type)

  • 访问级别

  • ArrayList和LinkedList

  • 可变与不可变

  • 父类和子类的构造方法

  • “”还是构造方法

  • 未来工作

这个列表总结了10个Java开发人员最常犯的错误。

1、Array转ArrayList

当需要把Array转成ArrayList的时候,开发人员经常这样做:

List<String> list = Arrays.asList(arr);

Arrays.asList()会返回一个ArrayList,但是要特别注意,这个ArrayList是Arrays类的静态内部类,并不是java.util.ArrayList类。

java.util.Arrays.ArrayList类实现了set(), get(),contains()方法,但是并没有实现增加元素的方法(事实上是可以调用add方法,但是没有具体实现,仅仅抛出UnsupportedOperationException异常),因此它的大小也是固定不变的。为了创建一个真正的java.util.ArrayList,你应该这样做:

ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(arr));

ArrayList的构造方法可以接收一个Collection类型,而java.util.Arrays.ArrayList已经实现了该接口。

2、判断一个数组是否包含某个值

开发人员经常这样做:

Set<String> set = new HashSet<String>(Arrays.asList(arr));
return set.contains(targetValue);

以上代码可以正常工作,但是没有必要将其转换成set集合,将一个List转成Set需要额外的时间,其实我们可以简单的使用如下方法即可:

Arrays.asList(arr).contains(targetValue);

或者

for(String s: arr){
    if(s.equals(targetValue))
    return true;
}
return false;

第一种方法可读性更强。

3、在循环内部删除List中的一个元素

考虑如下代码,在迭代期间删除元素:

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c","d"));
for (int i = 0; i < list.size(); i++) {
    list.remove(i);
}
System.out.println(list);

结果打印:[b, d]

在上面这个方法中有一系列的问题,当一个元素被删除的时候,list大小减小,然后原先索引指向了其它元素。所以如果你想在循环里通过索引来删除多个元素,将不会正确工作。

你也许知道使用迭代器是在循环里删除元素的正确方式,或许你也知道foreach循环跟迭代器很类似,但事实情况却不是这样,如下代码:

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c","d"));
for (String s : list) {
    if (s.equals("a"))
    list.remove(s);
}

将抛出ConcurrentModificationException异常。

然而接下来的代码却是OK的:

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c","d"));
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
    String s = iter.next();
    if (s.equals("a")) {
        iter.remove();
    }
}

next()方法需要在remove()方法之前被调用,在foreach循环里,编译器会在删除元素操作化调用next方法,这导致了ConcurrentModificationException异常。更多详细信息,可以查看ArrayList.iterator()的源码。

4、HashTable与HashMap

从算法的角度来讲,HashTable是一种数据结构名称。但是在Java中,这种数据结构叫做HashMap。

HashTable与HashMap的一个主要的区别是HashTable是同步的,所以,通常来说,你会使用HashMap,而不是Hashtable。

5、使用集合原始类型(raw type)

在Java中,原始类型(raw type)和无界通配符类型很容易让人混淆。举个Set的例子,Set是原始类型,而Set是无界通配符类型。

请看如下代码,add方法使用了一个原始类型的List作为入参:

public static void add(List list, Object o){
    list.add(o);
}
public static void main(String[] args){
    List<String> list = new ArrayList<String>();
    add(list, 10);
    String s = list.get(0);
}

运行以上代码将会抛出异常:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String at …

使用原始类型集合非常危险,因为它跳过了泛型类型检查,是不安全的。另外,Set, Set, 和Set这三个有很大的不同。

6、访问级别

开发人员经常使用public修饰类字段,虽然这很容易让别人直接通过引用获取该字段的值,但这是一个不好的设计。根据经验,应该尽可能的降低成员属性的访问级别。

7、ArrayList和LinkedList

为什么开发人员经常使用ArrayList和LinkedList,却不知道他们之间的区别,因为它们看起来很像。然而它们之间有着巨大的性能差异。简单的说,如果有大量的增加删除操作并且没有很多的随机访问元素的操作,应该首选LinkedList。

8、可变与不可变

不可变对象有很多优点,如简单、安全等。但是对于每个不同的值都需要一个单独的对象,太多的对象会引起大量垃圾回收,因此在选择可变与不可变的时候,需要有一个平衡。

通常,可变对象用于避免产生大量的中间对象,一个经典的例子是大量字符串的拼接。如果你使用一个不可变对象,将会马上产生大量符合垃圾回收标准的对象,这浪费了CPU大量的时间和精力。使用可变对象是正确的解决方案(StringBuilder);

String result="";
for(String s: arr){
    result = result + s;
}

另外,在有些其它情况下也是需要使用可变对象。例如往一个方法传入一个可变对象,然后收集多种结果,而不需要写太多的语法。另一个例子是排序和过滤:当然,你可以写一个方法来接收原始的集合,并且返回一个排好序的集合,但是那样对于大的集合就太浪费了。

9、父类和子类的构造方法

之所以出现这个编译错误,是因为父类的默认构造方法未定义。在Java中,如果一个类没有定义构造方法,编译器会默认插入一个无参数的构造方法;但是如果一个构造方法在父类中已定义,在这种情况,编译器是不会自动插入一个默认的无参构造方法,这正是以上demo的情况;

对于子类来说,不管是无参构造方法还是有参构造方法,都会默认调用父类的无参构造方法;当编译器尝试在子类中往这两个构造方法插入super()方法时,因为父类没有一个默认的无参构造方法,所以编译器报错;

要修复这个错误,很简单:

1、在父类手动定义一个无参构造方法:

public Super(){
    System.out.println("Super");
}

2、移除父类中自定义的构造方法

3、在子类中自己写上父类构造方法的调用;如super(value);

10、“”还是构造方法

有两种创建字符串的方式:

//1. use double quotes
String x = "abc";
//2. use constructor
String y = new String("abc");

它们之间有什么区别呢?

以下代码提供了一个快速回答:

String a = "abcd";
String b = "abcd";
System.out.println(a == b); // True
System.out.println(a.equals(b)); // True
String c = new String("abcd");
String d = new String("abcd");
System.out.println(c == d); // False
System.out.println(c.equals(d)); // True

更多关于它们内存分配的信息,请参考Create Java String Using ” ” or Constructor??

未来工作

这个列表是我基于大量的github上的开源项目,Stack overflow上的问题,还有一些流行的google搜索的分析。没有明显示的评估证明它们是前10,但它们绝对是很常见的。

如果您不同意任一部分,请留下您的评论。如果您能提出其它一些常见的错误,我将会非常感激。

好啦,今天的分享就到这儿啦,我们下次见啦~



GitHub原创推荐•  牛逼了!DeepFake变脸再次来袭,强势霸屏GitHub热榜!偏门应用“美女脱衣”还会再出现么?•  卧槽!还有这种事!马斯克的SpaceX-API 竟然开源了!登顶GitHub热榜!•  牛!Google 开源的这份工程实践,GitHub标星14.4k,果然大厂出品,必是精品!关注「Github爱好者社区」加星标,每天带你逛Github好玩的项目
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/github_shequ/article/details/106774772

智能推荐

css3卡片前后无限循环翻转效果_卡片翻动效果-程序员宅基地

文章浏览阅读370次,点赞2次,收藏3次。【代码】css3卡片前后无限循环翻转效果。_卡片翻动效果

Linux pstree命令教程:进程树的展示和管理(附实例详解和注意事项)_linux 安装pstree-程序员宅基地

文章浏览阅读1.1k次,点赞10次,收藏11次。pstree是一个Linux命令,它以树状图的形式显示运行中的进程。这种树状图的格式比ps命令更直观,使输出更具视觉吸引力。_linux 安装pstree

linux poweroff无法关机,linux关机重启六种方法(sysrq shutdown halt init ipmi poweroff)-程序员宅基地

文章浏览阅读2.7k次。linux关机重启六种方法(sysrq shutdown halt init ipmi poweroff)我的博客已迁移到xdoujiang.com请去那边和我交流linux关机重启六种方法(sysrqshutdownhaltinit)当前版本cat/etc/debian_version5.0.1一、linux关机命令(前四种)1、shutdown-hnow(-h:haltafter..._linux poweroff关机失败,poweroff -f关机成功

Mac电脑使用:终端的管理员用户和普通用户的自由切换方式_mac 终端切换交互方式-程序员宅基地

文章浏览阅读7.8k次,点赞6次,收藏11次。作为一名IT民工来说,对于电脑的各种操作都需要熟练掌握,不仅要学会Windows电脑的操作,更要学会Mac电脑的操作,不同的操作系统,有些操作是不一样的,就拿终端命令行来说,Windows系统的cmd和Mac系统自带的终端就不一样。 由于觉得Windows系统的cmd命令工具用着不习惯,所以本篇博文只来分享一下Mac电脑系统下自带的终端的关于管理员用户的切换方法,这个知识点虽然比较..._mac 终端切换交互方式

小米5手机设置电信联通VoLTE,解决4G网络不能接打电话收发短信的问题(3)_小米5电信卡不能打电话-程序员宅基地

文章浏览阅读3.3k次,点赞4次,收藏10次。他说按我的操作步骤,到开启Root的步骤后,无论如何也下载不下来Root安装包,导致无法获取Root权限。从我的小米5手机Root成功,到现在才一个来月,难道小米官方就关闭了此型号的Root服务,彻底把这枚钉子户抛弃了?下载一个刷机包->下载并安装magisk->用magic修改刷机包->将刷机包刷入手机->测试成功->舔杯(只是阶段性的胜利,不能大张旗鼓的干杯)备份手机数据-->解锁BL-->刷开发版-->开启Root-->安装文件管理器-->替换相关文件-->测试-->干杯!下面是一些我的维修故事。_小米5电信卡不能打电话

Android开发之TextView中间设置横线,适用于电商项目,商品原价之类的功能。_android textview paint 添加中横线-程序员宅基地

文章浏览阅读539次。[java] view plain copytextview.getPaint().setFlags(Paint. STRIKE_THRU_TEXT_FLAG ); //中间横线 [java] view plain copytextview.getPaint().setFlags(Paint.UNDERLINE__android textview paint 添加中横线

随便推点

CSS3 平面 2D 变换+CSS3 过渡-程序员宅基地

文章浏览阅读2.1k次,点赞104次,收藏87次。HTML5和CSS3-19(20)-1(3)

归一化 (Normalization)、标准化 (Standardization)和中心化/零均值化 (Zero-centered),BN,Batch,批归一化,从归一化到批归一化_组均值和总均值中心化-程序员宅基地

文章浏览阅读1.4w次,点赞20次,收藏91次。目录1.引言:一些基本概念,2.为什么要进行归一化?归一化两个的优点3.为什么要标准化?标准化的好处?4.归一化和标准化的区别5.什么时候用归一化?什么时候用标准化?6. 哪些模型必须归一化/标准化?7、逻辑回归必须要进行标准化吗?8. 为什么需要批归一化(BN)?9. BN的好处10、BN的原理11、BN到底解决了什么Reference:..._组均值和总均值中心化

DataV构建大屏(全屏)数据展示页面_datav大屏-程序员宅基地

文章浏览阅读1.8w次,点赞20次,收藏189次。官方文档地址:DataV1.安装组件库npm安装 npm install @jiaminghi/data-view 2.使用 全局引入// 将自动注册所有组件为全局组件import dataV from '@jiaminghi/data-view'Vue.use(dataV)按需引入按需引入仅支持基于ES module的tree shaking,按需引入示例如下:import { borderBox1 } from '@jiaminghi/data-view'V_datav大屏

LSTNet--结合时间注意力机制的LSTM模型(附源码)-程序员宅基地

文章浏览阅读2.8w次,点赞31次,收藏407次。一、引言 LSTM出现以来,在捕获时间序列依赖关系方面表现出了强大的潜力,直到Transformer的大杀四方。但是,就像我在上一篇博客《RNN与LSTM原理浅析》末尾提到的一样,虽然Transformer在目标检测、目标识别、时间序列预测等各领域都有着优于传统模型的表现,甚至是压倒性的优势。但Transformer所依赖的Multi-Head Attention机制给模型带来了巨大的参数量与计算开销,这使得模型难以满足实时性要求高的任务需求。我也提到,LSTM想与Transformer抗..._时间注意力机制

3D打印发电机壳体部件 结构更轻更强-程序员宅基地

文章浏览阅读145次。在越来越多的人询问零件生产技术后,Saffron Electrical&Power开始探索3D打印的优势。Safran Electrical&Power采用增材技术和系统,与Betatype合作,利用其在3D打印技术和DfAM方面的专业知识。正如Safran Electrical&Power的Mark Craig所解释的那样:..._3d打印 电池部件结构

魅族17定价过高,如何与其他国产手机竞争?-程序员宅基地

文章浏览阅读580次。魅族号称耗费了很长时间打造的魅族17终于发布了,搭载高通的高端芯片骁龙865,定价高达3699元起,看起来比小米10便宜了300元,然而别忘记了如今小米主打性价比的是红米品牌,而同样搭载...