Java日常错误及需要注意细节,持续更新......
记录日常工作中一些容易被忽视的错误及细节,持续更新......
一、问题:HashMap<Long, String>中,用get(Integer key)取不到值
Map<Long, String> map = new HashMap<Long, String>();
map.put(1L, "1");
System.err.println(map.get(1));// null
System.err.println(map.get(1L));//
1.首先想到Long与Integer的hashCode方法不同,Integer-value Long-(int)(value ^ (value >>> 32))
但是!!计算出的hashCode值是相同的,不是问题所在
2.查看HashMap源码:注意加亮部分
先比较key.hash,然后first.key == key || key.equals(first.key)
/**
* 先比较key.hash,然后first.key == key || key.equals(first.key)
*/
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) {
if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
先看first.key == key:"=="比较地址值,l是Long cache[]中的1,o是Integer cache[]中的1,false
Long l = 1L;
Object o = 1;
System.err.println(l == o);// false // 反编译后:
Long l = Long.valueOf(1L);
Object o = Integer.valueOf(1);
System.err.println(l == o);
然后看key.equals(first.key):先检查类型,false
//Long的equals方法
public boolean equals(Object obj) {
if (obj instanceof Long) {
return value == ((Long)obj).longValue();
}
return false;
}
引发新的问题:为什么这个是true?——反编译解决
Long l = 1L;
System.err.println(l == 1);// true // 反编译后:
Long l = Long.valueOf(1L);
System.err.println(l.longValue() == 1L);//编译器直接将1转成1L
二、两个值相等的Integer不“==”
Integer c = 99999;
Integer d = 99999;
System.out.println(c == d);// false
Integer c = 99999;// 反编译:Integer c = Integer.valueOf(99999);
查看Integer源码:
-128 <= i <= 127时,直接在Integer cache[]中取;否则,new Integer(i)
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
结论:
int a = 99999;
int b = 99999;
System.err.println(a == b);// true Integer c = 99999;
Integer d = 99999;
System.out.println(c == d);// false Integer e = 127;
Integer f = 127;
System.out.println(e == f);// true
三、List.remove()方法调用错误
注意list两个remove方法,remove(int index) remove(Object o)
public static void main(String[] args) {
List<Integer> list = new LinkedList<Integer>();
for (int i = 0; i < 9999999; i++) {
list.add(i);
} // remove(int index)
long before = System.currentTimeMillis();
int i = 8888888;
list.remove(i);
long after = System.currentTimeMillis();
System.err.println("index=" + (after - before));// 6ms // remove(Object o)
long before = System.currentTimeMillis();
Integer i = 8888888;
list.remove(i);
long after = System.currentTimeMillis();
System.err.println("Object=" + (after - before));// 96ms }
四、三目运算符与自动拆装箱
Map<String,Boolean> map = new HashMap<String, Boolean>();
Boolean b = (map!=null ? map.get("test") : false);// Exception in thread "main" java.lang.NullPointerException
查问题:
NullPointerException找不出原因
反编译看: ((Boolean)map.get("test")) == null
HashMap map = new HashMap();
Boolean boolean1 = Boolean.valueOf(map == null ? false : ((Boolean)map.get("test")).booleanValue());
结论:
三目运算符的语法规范,参见 jls-15.25。
三目运算符 当第二,第三位操作数分别为基本类型和对象时,其中的对象就会拆箱为基本类型进行操作。
以后注意:
1、保证三目运算符的第二第三位操作数都为对象类型
Map<String,Boolean> map = new HashMap<String, Boolean>();
Boolean b = (map!=null ? map.get("test") : Boolean.FALSE);
2、自动拆装箱问题
Integer integer = 1; // 装箱 Integer integer=Integer.valueOf(1); new Integer()
int i = integer; // 拆箱 int i=integer.intValue();
1)包装对象的数值比较,不能简单的使用==(只有-128到127之间IntegerCache内的数字可以,但是这个范围之外还是需要使用equals
比较)。
2)自动拆箱,如果包装类对象为null,那么自动拆箱时就有可能抛出NPE。
3)如果一个for循环中有大量装箱操作,会浪费很多资源。
五、switch语句忘记break
本来我跟你现在想的一样,一定不会忘,直到遇到了这个问题。
int i = 3;
switch (i) {
case 1:
System.out.println(1);
break;
case 2:
System.out.println(2);
break;
case 3:
System.out.println(3);
// 没有break, 不会有问题
}
当你需要在之后接着case的时候,直接复制case 3,就bug了。
(1)case完一定break,除非特别需要穿透到下一个case;
(2)复制代码前后都要检查是否有问题。
六、数值溢出问题
// 为了更好的展示问题,代码举出的是较极端的情况
public void overFlow(int a) {
int b = 999999 * a; // 6个9 int最大值=2147483647
int limit = 999999999; // 9个9
if (b < limit) {
System.out.println("a*b小于limit");
}
}
如果a传入一个较大的int值,a*999999之后会超过int的最大值
而默认结果是int类型,会将a*999999的结果强转成int,导致不是想要的结果的结果
即使a*999999是大于limit的,强转成int后,b可能会比limit小,甚至可能是负数
解决:
// 用long类型计算(还会用一定风险)
public void overFlow(int a) {
long b = 999999L * a;
int limit = 999999999;
if (b < limit) {
System.out.println("a*999999小于limit");
}
} // 将加法和乘法转变成减法和除法运算
public void overFlow(int a) {
int limit = 999999999;
if (a < limit/999999) {
System.out.println("a*999999小于limit");
}
}
七、对象引用问题
public static void main(String[] args) {
Map<Integer, Inner> map = new HashMap<Integer, Inner>(); // inner1.list [1]
Inner inner1 = new Inner(new LinkedList<Integer>());
inner1.getList().add(1);
map.put(1, inner1); // inner2.list [1, 2]
Inner inner2 = new Inner(map.get(1).getList());
inner2.getList().add(2);
map.put(2, inner2); for (Entry<Integer, Inner> entry : map.entrySet()) {
System.err.println("" + entry.getKey() + " " + entry.getValue().getList());
} /**
* 目的:inner1.list [1] inner2.list [1, 2]
* 结果:inner1.list [1, 2] inner2.list [1, 2]
*/
} static class Inner {
List<Integer> list; public List<Integer> getList() {
return list;
} public Inner(List<Integer> list) {
this.list = list;
}
}
分析:
1.将inner1.list的引用给了inner2.list,nner1.list inner2.list指向的是同一个List。
2.很简单的问题,开发时习惯了构造方法里这样写:this.list = list;
解决:
this.list = list; 改成 this.list = new LinkedList<Integer>(list);
Java日常错误及需要注意细节,持续更新......的更多相关文章
- java 学习必备的软件,持续更新中
小编会持续更新在学习Java过程中需要的软件以及各种文件: 话不多说,看行动! 一:JDK (1)JDK1.8(*64): 链接:https://pan.baidu.com/s/1vM0jNXn2CT ...
- Java中的static(1)【持续更新】——关于Eclipse的No enclosing instance of type ... 错误的理解和改正
No enclosing instance of type SomeClass is accessible. Must qualify the allocation with an enclosing ...
- java 超详细面经整理(持续更新)2019.12.19
目录 Java SE 请你解释HashMap中为什么重写equals还要重写hashcode? 请你介绍一下map的分类和常见的情况 请你讲讲Java里面的final关键字是怎么用的? 请你谈谈关于S ...
- java 超详细面经整理(持续更新)2019.12.18
目录 Java SE 请你谈谈Java中是如何支持正则表达式操作的? 请你简单描述一下正则表达式及其用途. 请你比较一下Java和JavaSciprt? 在Java中如何跳出当前的多重嵌套循环? 讲讲 ...
- JAVA常见面试题问题简述(持续更新中)
JAVA常见面试题问题简述 1. springcloud和dubbo的区别 ①相比之下springcloud 的社区会更加活跃,解决问题的速度也会越来越快,dubbo相对来说如果碰到没有解决的问题,就 ...
- Myeclipse中web project各种常见错误及解决方法(持续更新)
创建web project时的问题 error:Install Dynamic web Module Facet卡住 solution:把网络关掉再创建就可以 Servlet error:The se ...
- 【java学习】实践中总结--持续更新中
目录: 一些定义 配置环境 相关语法 1.一些定义 java中DO的含义: https://blog.csdn.net/canot/article/details/51698047 DAO 中包含了各 ...
- 【java】Java相关学习参考链接(持续更新)
How to do in java,https://howtodoinjava.com/,Java手册,分版本,并且有每个版本的新特性的详细解析. Java World,https://www.jav ...
- java常用代码段整理(持续更新)
FileWriter指定编码格式 FileWriter 默认是用(ISO-8859-1 or US-ASCII)西方编码的,总之不是UTF-8的,而FileWriter类有getEncoding方法, ...
随机推荐
- 2016.6.21——Climbing Stairs
Climbing Stairs 本题收获: 1.斐波那契函数f(n) = f(n-1) + f(n -2) 题目: You are climbing a stair case. It takes n ...
- 【FCS NOI2018】福建省冬摸鱼笔记 day4
第四天. 动态规划专题,讲师:闫神 讲了一些DP优化技巧,然而思想难度好大啊……根本没想到能优化那地步,连DP方程都没有呢. 不过有几题我还是想明白了. 讲了单调队列,决策单调性,四边形不等式,斜率优 ...
- 如何调整Linux内核启动中的驱动初始化顺序-驱动加载优先级
Linux内核为不同驱动的加载顺序对应不同的优先级,定义了一些宏: include\linux\init.h #define pure_initcall(fn) __define_initcall(& ...
- 29 A Quick Guide to Go's Assembler 快速指南汇编程序:使用go语言的汇编器简介
A Quick Guide to Go's Assembler 快速指南汇编程序:使用go语言的汇编器简介 A Quick Guide to Go's Assembler Constants Symb ...
- FTRL算法
稀疏解的作用:内存和时间啊 实际的互联网广告应用需要的是快速地进行model的更新.为了保证快速的更新,训练样本是一条一条地过来的,每来一个样本,model的参数对这个样本进行一次迭代,从而保证了mo ...
- 洛谷P2002消息扩散
传送门啦 这个题就是tarjan强连通分量与入度的例题了. 思路: 利用缩点的思想,先预处理一下所有的强连通分量,然后把每个强连通分量内的所有节点看做一个节点,然后处理一张新图,然后检查每个点的入度, ...
- Linux 相关
一.WCHAN的含义 WCHAN 进程正在睡眠的内核函数名称:该函数的名称是从/root/system.map文件中获得的. 参考:解析ANDROID ps命令执行后各项参数的含义 二.查看线程 ps ...
- EFK收集Kubernetes应用日志
本节内容: EFK介绍 安装配置EFK 配置efk-rbac.yaml文件 配置 es-controller.yaml 配置 es-service.yaml 配置 fluentd-es-ds.yaml ...
- N的阶乘末尾有多少个0
N的阶乘(N!)中的末尾有多少个0? N的阶乘可以分解为: 2的X次方,3的Y次方,4的5次Z方,.....的成绩.由于10 = 2 * 5,所以M只能和X和Z有关,每一对2和5相乘就可以得到一个10 ...
- LoadRunner 11简单使用
LoadRunner 11简单使用 开始菜单-->HP LoadRunner-->applications--->virtual user Generator 1>新建--&g ...