黑马程序员技术交流社区

标题: 【给出我的分析】基础题,绝对难到很多人,不信进来看看 [打印本页]

作者: 李润根    时间: 2013-1-20 22:17
标题: 【给出我的分析】基础题,绝对难到很多人,不信进来看看
本帖最后由 我是菜鸟 于 2013-1-21 15:37 编辑

public static void main(String[] args)
{
        Map<String, Integer> map = new HashMap<String, Integer>();
        String str = "x";
        Integer i = Math.random()*10 > 7? 5 : null;
        System.out.println(i);


        map.put(str, map.get(str)==null ? i : (map.get(str) + (i==null? 1 : i)));
}

这段代码会报NullPointerException,看看哪位能找出来并说出原因


=============================================================================


HashMap<String, Integer>
map.put(str, map.get(str)==null ? i : (map.get(str) + (i==null? 1 : i)));

在编译时期就要确定表达式的类型
i==null? 1 : i,表达式2和3分别是int和Integer,该语句的返回类型是int,可以推出表达式3也就是 (map.get(str) + (i==null? 1 : i))的返回类型为int
可以看作:
map.get(str)==null ? Integer : int,所以当i=5的时候,i可以正常拆箱,但当i=null,拆箱的时候调用null.intValue(),就会报空指针异常

验证:
map.put(str, map.get(str)==null ? 1 : (map.get(str) + (i==null? 1 : i)));
把i改成常量,保证表达式2类型为int,这时无论i等于什么,程序都正常,可以说明问题就出在表达式2的拆箱


所以在使用三元运算符,如果:两边表达式的类型不一致,最好做显式强制类型
map.put(str, map.get(str)==null ? i : (Integer)(map.get(str) + (i==null? 1 : i)));

*如果有问题或疑问,请指出,谢谢















作者: 王少雷    时间: 2013-1-20 22:34
本帖最后由 王少雷 于 2013-1-20 23:18 编辑

我不得不承认楼主灰常有才。~~~~
这张图应该能说明问题吧
其实程序在这里Integer i = Math.random()*10 > 7? 5 : null;我就觉得不对劲
1个NULL 下面不用走了全是NULL
作者: 黑马刘杰    时间: 2013-1-20 22:36
本帖最后由 黑马刘杰 于 2013-1-20 22:37 编辑

map.get(str)==null ? i : (map.get(str) + (i==null? 1 : i))如果i为null这里的map.get(str)==null?i 就是null,所以后面的
(map.get(str) + (i==null? 1 : i))还会执行,而null+1就会报异常。
作者: 王晓斌    时间: 2013-1-20 22:38
问题应该是出在最后一句,我们的目的是把键值对放入集合中,也就是说集合中现在还没有这个键值对~~~那么此时再调用map.get(),根据键取值当然会出现这个异常了~~~
作者: 李润根    时间: 2013-1-20 22:50
王少雷 发表于 2013-1-20 22:34
我不得不承认楼主灰常有才。~~~~
这张图应该能说明问题吧
其实程序在这里Integer i = Math.random()*10 > 7 ...

就算Integer i = null,下面还会继续走了,请验证
作者: 李润根    时间: 2013-1-20 22:51
黑马刘杰 发表于 2013-1-20 22:36
map.get(str)==null ? i : (map.get(str) + (i==null? 1 : i))如果i为null这里的map.get(str)==null?i 就是 ...

null+1不是报异常,是直接err
作者: 李润根    时间: 2013-1-20 22:54
王晓斌 发表于 2013-1-20 22:39
问题应该是出在最后一句,我们的目的是把键值对放入集合中,也就是说集合中现在还没有这个键值对~~~那么 ...

Map<String, Integer> map = new HashMap<String, Integer>();
String str = "x";
System.out.println(map.get(str));

就算没有操作put,还是可以操作get的,请验证
作者: 柴乔军    时间: 2013-1-20 23:06
本帖最后由 柴乔军 于 2013-1-20 23:09 编辑


楼主的程序要么输出5;要么就会报空指针,如果是空指针的话,应该是图中画红圈的i造成的吧,楼主对么?
i是一个null,不是Integer对象


作者: 王少雷    时间: 2013-1-20 23:10
本帖最后由 王少雷 于 2013-1-20 23:18 编辑

我是菜鸟 发表于 2013-1-20 22:50
就算Integer i = null,下面还会继续走了,请验证

是的~,,,莫非是NULL被随机了

作者: 李润根    时间: 2013-1-20 23:10
柴乔军 发表于 2013-1-20 23:06
楼主的程序要么输出5;要么就会报空指针,如果是空指针的话,应该是图中画红圈的i造成的吧,楼主对么?
i ...

不对,请再验证
作者: txl    时间: 2013-1-20 23:20
我也想了很久这个题,根据判断楼上指出问题出处应该是对的,我想jvm可能会调用Integer.intValue()方法或者是Integer中其他方法,但i为null,所以报异常,但直接写 map.put(str,null);不会报错,我真的就黔驴技穷了...
作者: 柴乔军    时间: 2013-1-20 23:20
我是菜鸟 发表于 2013-1-20 23:10
不对,请再验证

i==null? 1 : i
这句话出现的空指针,==判断报出来的i为null;
  1. public class Test1 {
  2.        
  3.         public static void main(String[] args)
  4.     {
  5.             Map<String, Integer> map = new HashMap<String, Integer>();
  6.             String str = "x";
  7.             //Integer i = Math.random()*10 > 7? 5 : null;
  8.             Integer i = 5;
  9.             System.out.println(map.get(str));
  10.             System.out.println(map.get(str) + (i==null? 1 : i));
  11.             System.out.println(map.get(str));
  12.             System.out.println(i);


  13.             map.put(str, map.get(str)==null ? i : (map.get(str) + (i==null? 1 : i)));
  14.     }


  15. }
  16. 结果:
  17. null
  18. Exception in thread "main" java.lang.NullPointerException
  19.         at test.Test1.main(Test1.java:15)

复制代码

作者: 李润根    时间: 2013-1-20 23:20
王少雷 发表于 2013-1-20 23:10
我是菜鸟 发表于 2013-1-20 22:50
就算Integer i = null,下面还会继续走了,请验证
是的~,,,莫非是NUL ...

Map<String, Integer> map = new HashMap<String, Integer>();
String str = "x";
Integer i = null;
System.out.println(i);

map.put(str, map.get(str)==null ? i : (map.get(str) + (i==null? 1 : i)));


可以的,不过会报NullPointerException,这就是所问
作者: 柴乔军    时间: 2013-1-20 23:21
验证了,就算是 map.put("x",null);这样加入,都不会报错
作者: 李润根    时间: 2013-1-20 23:24
黑马唐贤来 发表于 2013-1-20 23:20
我也想了很久这个题,根据判断楼上指出问题出处应该是对的,我想jvm可能会调用Integer.intValue()方法或者是I ...

差不多接近了,再详细指出具体的错误地方
作者: 李润根    时间: 2013-1-20 23:26
柴乔军 发表于 2013-1-20 23:20
i==null? 1 : i
这句话出现的空指针,==判断报出来的i为null;

不对,不过接近了,再仔细看看
作者: 李润根    时间: 2013-1-20 23:27
柴乔军 发表于 2013-1-20 23:21
验证了,就算是 map.put("x",null);这样加入,都不会报错

嗯嗯,再仔细看看,会有发现的
作者: 柴乔军    时间: 2013-1-20 23:30
基础能力有限啊。。。不看了。。。等楼主公布答案。。
作者: 李润根    时间: 2013-1-20 23:34
柴乔军 发表于 2013-1-20 23:30
基础能力有限啊。。。不看了。。。等楼主公布答案。。

;P
再等其他黑马看看,大家交流一下
作者: 黄锦成    时间: 2013-1-20 23:38
本帖最后由 黄锦成 于 2013-1-20 23:43 编辑

是这段代码出错了 (map.get(str) + (i==null? 1 : i))
map.get(str)取出的是null,这个(i==null? 1 : i)总会有值的不是1就是5,当进行运算时i会拆箱变成字符串1或5,但是map.get(str)取出的null就变不了,就产生了空指针异常

map.get(str) == null ? i : (map.get(str) + i):乍一看,以为会等map.get(str) == null 这个代码得出结果才去选择左侧的还是右侧,选定之后才回去执行其实都是先会执行结果出来的
        
作者: txl    时间: 2013-1-20 23:40
我是菜鸟 发表于 2013-1-20 23:24
差不多接近了,再详细指出具体的错误地方

class Test
{
        public static void main(String[] args)
        {
                    System.out.println(((Integer)null).intValue());//楼主.问题绝对在这里!将一个null强转为Integer后调用其方法,编译可以通过.但运行报NullPointerException
        }
}
作者: 黄锦成    时间: 2013-1-20 23:44
王少雷 发表于 2013-1-20 22:34
我不得不承认楼主灰常有才。~~~~
这张图应该能说明问题吧
其实程序在这里Integer i = Math.random()*10 > 7 ...

这个没问题,null是可以赋给引用类型的,我们平时在定义引用类型变量时可以写成 private String str = null;
Integer也可以这样
作者: 黑马刘杰    时间: 2013-1-20 23:46
Integer i =  null;
System.out.println(map.get(str)+i);
也报异常
作者: 焦健    时间: 2013-1-20 23:47

map.put(str, false? i :(map.get(str) + (false? 1 : i)) );
map.put(str, true? i :(map.get(str) + (false? 1 : i)) );
map.put(str,  i );

通过验证知道,在i=null的时候,上面两句无论都是true还是都是false结果都是挂。
但是第三句可以知道map.put直接传入i是没有问题的,那么,我们就可以知道三元
运算符在运算的时候一定是先执行到了后面的语句(map.get(str) + (false? 1 : i))也就
是三元运算符运算后有可能输出的结果,你那么写等效于下面的这三句
            Integer x= (i==null? 1 : i);
            Integer a=(map.get(str) + x);//i是null时将会在这里挂掉
            map.put(str, map.get(str)==null ? i :a);

需要先知道结果才能继续运算,所以你这个代码在运行到上面第二行那里就挂了。

作者: 黄锦成    时间: 2013-1-20 23:48
王晓斌 发表于 2013-1-20 22:38
问题应该是出在最后一句,我们的目的是把键值对放入集合中,也就是说集合中现在还没有这个键值对~~~那么 ...

取没有的,会返回null
作者: 王少雷    时间: 2013-1-21 00:10
黄锦成 发表于 2013-1-20 23:44
这个没问题,null是可以赋给引用类型的,我们平时在定义引用类型变量时可以写成 private String str = nu ...

是的。我也搞错了的。嗯。。。估计是NULL在下面被随机了。呵呵。
作者: 杨政    时间: 2013-1-21 03:52
问题出在map.get(str)吧,进行这项操作时集合中还不存在该键值对,所以进行map.get(str)操作会返回null。你打印map.get(str)结果会是null,但该null不是表示map.get(str)的值,而是表示操作的键值对不存在,你可以打印它得到null,但是不能将它用于运算,否则会抛出NullPointerException!
验证了一下:
class Demo2
{
        public static void main(String[] args)
        {
               
                Map<String, Integer> map = new HashMap<String, Integer>();
               
                String str = "x";
                Integer i = Math.random()*10 > 7? 5 : null;

                System.out.println(map.get(str));//打印map.get(str)没问题
                Integer j = map.get(str)+5; //将map.get(str)用于运算则报NullPointerException
        }
}

作者: 舒远    时间: 2013-1-21 09:43
本帖最后由 舒远 于 2013-1-21 09:50 编辑

map.put(str, map.get(str) == null ? i : Integer.valueOf(map.get(str) + (i == null ? 1 : i)));
最后一句话必须改成如上代码,才能避免空指针的出现。
三元表达式必须保证冒号两边的表达式运算结果的类型一致。
作者: 黑马张英涛    时间: 2013-1-21 10:01
我觉得原因应该是在这里:
  1. map.put(str, map.get(str) == null ? i : (map.get(str) + (i == null ? 1: i)));
复制代码
map.get(str)+....
当 i 为null时,三元运算符后面的值是null+1,这时候jvm可能尝试调用了null.intValue()+1进行运算,于是报了异常


作者: Rancho_Gump    时间: 2013-1-21 12:19
貌似楼主知道正解哎,  我看上面有人回答到点了,楼主没有采纳哎
问题是三元运算符里的 i  指向了NULL造成的
Integer i = null;
true ? i : 其他值;  这样就出现空指针异常。
作者: 李润根    时间: 2013-1-21 12:19
舒远 发表于 2013-1-21 09:43
map.put(str, map.get(str) == null ? i : Integer.valueOf(map.get(str) + (i == null ? 1 : i)));
最后一 ...


也可以如下:
map.put(str, map.get(str) == null ? i : (Integer)(map.get(str) + (i == null ? 1 : i)));

再问:
(map.get(str) + (i == null ? 1 : i)),这个表达式为什么返回类型是int,分析一下
作者: Rancho_Gump    时间: 2013-1-21 12:24
楼主貌似知道正解哎 上面有人提到问题所在了,楼主没有采纳啊
问题就出在三元运算符里 i  变量 i 指向了null
Integer i = null ;
true ? i : 其他值;  此时会报空指针异常
作者: 李润根    时间: 2013-1-21 12:25
张向辉 发表于 2013-1-21 12:19
貌似楼主知道正解哎,  我看上面有人回答到点了,楼主没有采纳哎
问题是三元运算符里的 i  指向了NULL造成 ...

呵呵~~~
大多数都知道i=null或者map.get(str)=null,但为什么会报空指针异常,这没说出来,刚看到有一个说出来了
作者: 舒远    时间: 2013-1-21 12:27
张向辉 发表于 2013-1-21 12:24
楼主貌似知道正解哎 上面有人提到问题所在了,楼主没有采纳啊
问题就出在三元运算符里 i  变量 i 指向了nul ...

问题根本不在这。。多写点代码测试一下就知道了
作者: 舒远    时间: 2013-1-21 12:28
我是菜鸟 发表于 2013-1-21 12:19

也可以如下:
map.put(str, map.get(str) == null ? i : (Integer)(map.get(str) + (i == null ? 1 :  ...

i == null ? 1 : i
这个表达式的类型是int,因为i为null,返回的是1,默认int,这里没有自动装箱的机制。

作者: Rancho_Gump    时间: 2013-1-21 12:41
舒远 发表于 2013-1-21 12:28
i == null ? 1 : i
这个表达式的类型是int,因为i为null,返回的是1,默认int,这里没有自动装箱的机制。 ...

远哥 说的有道理   对于他这道题来说想深了   我测试了他的程序  在 i  不等于null 的情况下不会出异常
问题就出在这
map.put(str, map.get(str)==null ? i : (map.get(str) + (i==null? 1 : i)));
map.get(str)==null  为ture
第一个表达式是 i
第二个表达式也可能出错,不重要了,不执行
i 为 null 就空指针异常了  
这是简单点的考虑
作者: 舒远    时间: 2013-1-21 12:50
张向辉 发表于 2013-1-21 12:41
远哥 说的有道理   对于他这道题来说想深了   我测试了他的程序  在 i  不等于null 的情况下不会出异常
...

你还是没理解:
map.put(str,null);这句话不会报错的。
作者: 方寅生    时间: 2013-1-21 12:51
本帖最后由 方寅生 于 2013-1-21 13:06 编辑

map.put(str, map.get(str)==null ? i : (map.get(str) + (i==null? 1 : i)));
这一句之前加一句
i=i==null? 1 : i;
然后map.put(str, map.get(str)==null ? i : (map.get(str) + (i==null? 1 : i)));;改成
map.put(str, map.get(str)==null ? i : (map.get(str) + i));
作者: 李润根    时间: 2013-1-21 12:54
舒远 发表于 2013-1-21 12:28
i == null ? 1 : i
这个表达式的类型是int,因为i为null,返回的是1,默认int,这里没有自动装箱的机制。 ...

按你说这个表达式的类型是int,因为i为null,返回的是1,默认int,不要忘记了前面还有一个表达式map.get(str),最后的结果是null+1,null需要拆箱,也会报错的
对于整个表达式,map.put(str, map.get(str) == null ? i : (map.get(str) + (i == null ? 1 : i)));表达式3( (map.get(str) + (i == null ? 1 : i)))是执行不到的

有其他人已经在提问这问题了 关于关于三元运算符

作者: Rancho_Gump    时间: 2013-1-21 12:58
太热闹了 哇唔   这样的问题 以后我必须加分!
作者: 黑马张英涛    时间: 2013-1-21 13:24
public static void main(String[] args)
        {
                Map<String, Integer> map = new HashMap<String, Integer>();
                String str = "x";
                Integer i = Math.random()*10 > 7? 5 : null;
                System.out.println(i);
     标记: map.put(str, map.get(str)==null ? i : (map.get(str) + (i==null? 1 : i)));
        }

分析下流程吧:
i=5的话,没问题,不考虑,关键是i=null的时候。
i=null的时候,标记前面的代码没问题,现在执行标记处的代码,首先判断外层的三元运算符,
map.get(str)==null为真,于是该表达式的值为i,但是i的值被该三元运算符的表达式3给修改了,
于是要得到i的值,jvm只能执行表达式3,运行map.get(str),结果为null,这时候jvm并不会报异常,
还要继续运算里面的三元运算符的值,求出i=1,也没问题,然后要计算null+1的值,这时候jvm根据
map的定义的类型得出null+1的值应该是Integer类型,于是会尝试将null解包成int类型进行运算,
这里有可能调用的是null.intValue()方法,但是null里面根本没有这个方法,这时候空指针异常出现了。

这应该就是问题的位置和原因了吧。





作者: cyh8807    时间: 2013-1-21 13:31
本帖最后由 赵彦辉 于 2013-1-21 13:41 编辑

我测试的是这两句抛的空指针异常

  1.           public static void main(String[] args)
  2.       {
  3.               Map<String,Integer>  map = new HashMap<String,Integer>();
  4.               String str = "x";
  5. //             Integer i = Math.random()*10 > 7? 5 : null;
  6. //              System.out.println(i);
  7.               map.put(str, 5);
  8.               map.put(str,null);
  9.              map.put(str,5 + map.get(str));
  10.              map.put(str, 1 + map.get(str));
  11. //             map.put(str, map.get(str)==null ? i : (map.get(str) + (i==null? 1 : i)));
  12.           /*
  13.             map.get(str)==null      i = 5
  14.             map.get(str) !=null      5 + map.get(str)
  15.             
  16.             map.get(str)==null      i = null
  17.             map.get(str) !=null     1 + map.get(str)
  18.             */
  19.              Set<String> keySet = map.keySet();
  20.               for(Iterator<String> it = keySet.iterator();it.hasNext();)
  21.               {
  22.                      String str1 = it.next();
  23.                      System.out.println(str1);
  24.                      System.out.println(map.get(str));
  25.               }
  26.       }
复制代码
map.put(str,5 + map.get(str));
map.put(str, 1 + map.get(str));
请看这两句代码:map.get(str) != null, 那么键所对应的值就为 5 + map.get(str)或者 1 + map.get(str)
我测试的时候就只有这两句抛空指针异常,这个时候具体的键对应的值往Map集合中存放的时候会出错,注释掉这两句程序就OK,
不知道我的答案是不是正解,大家讨论


作者: txl    时间: 2013-1-21 13:44
本帖最后由 黑马唐贤来 于 2013-1-21 13:53 编辑
舒远 发表于 2013-1-21 09:43
map.put(str, map.get(str) == null ? i : Integer.valueOf(map.get(str) + (i == null ? 1 : i)));
最后一 ...


     为什么下面的代码冒号两边类型不一致,依然可以呢        
                  Integer i = 0;
          System.out.print(i==1?i:"1");

还有如下代码
         

public class print {
  public static void main(String[] args)
  {
   Integer i = null;
   System.out.print(i==null?i:new test());
  }
  
}
class test
{

}
求解释



作者: 黑马张英涛    时间: 2013-1-21 13:46
这两句出错的原理跟上面的一样,都是将null跟整数计算时出错,而不是往集合存放的时候出错
作者: Rancho_Gump    时间: 2013-1-21 13:50
本帖最后由 张向辉 于 2013-1-21 13:51 编辑
黑马张英涛 发表于 2013-1-21 13:24
public static void main(String[] args)
        {
                Map map = new HashMap();


i == null ? 1 : i   并没有修改 i 的值   整个表达式的值是1
作者: 李润根    时间: 2013-1-21 13:51
黑马张英涛 发表于 2013-1-21 13:24
public static void main(String[] args)
        {
                Map map = new HashMap();

分析的不错
首先判断外层的三元运算符,map.get(str)==null为真,于是该表达式的值为i,既然已经有值了为什么还要执行表达式3呢?
作者: 黑马张英涛    时间: 2013-1-21 14:00
我是菜鸟 发表于 2013-1-21 13:51
分析的不错
首先判断外层的三元运算符,map.get(str)==null为真,于是该表达式的值为i,既然已经有值了为 ...

说了啊,是因为表达式3改了i的值,如果表达式3跟 i 无关 jvm是不会管表达式3是什么值的吧, 我测试了下直接给他传了个5/0都没错。至于他内部的机理就要聆听高见了
作者: 李润根    时间: 2013-1-21 14:06
黑马张英涛 发表于 2013-1-21 14:00
说了啊,是因为表达式3改了i的值,如果表达式3跟 i 无关 jvm是不会管表达式3是什么值的吧, 我测试了下直 ...

表达式1 ? 表达式2 : 表达式3

表达式1已经为true,执行表达式2,就算表达式3如何修改表达式2的任何变量也不会执行的

例如:
int x =1;
System.out.println(true?x:x++);
结果还是1
作者: 黑马张英涛    时间: 2013-1-21 14:15
我是菜鸟 发表于 2013-1-21 14:06
表达式1 ? 表达式2 : 表达式3

表达式1已经为true,执行表达式2,就算表达式3如何修改表达式2的任何变 ...

恩,其实我内心深处也有这么个疑问。。
你还是公布答案吧




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2