Java 拾遗

原创 2007年09月24日 18:55:00

最近在工作中,经常发现java中的一些小细节,自己以前都没有注意,发现之后还是有点新鲜的,不过都是很基础的东西(也许是自己java基础太过薄弱,嘿嘿),就记录在这里,以予自乐。

 

1、  方法重载的问题

 

public class Test ...{
    
private Test(Object o) ...{
        System.out.println(
"Object");
    }

    
    
private Test(double[] o) ...{
        System.out.println(
"double array");
    }


    
public static void main(String[] args) ...{
        
new Test(null);
    }

}

 

上述代码的输出结果会是什么呢?"Object""double array"?还是其它?

答案是"double array",由于null的特殊性,在这里看似既符合Object有符合double[],但是java的设计者似乎懂得近水楼台先得月这个俗语,于是java对象的匹配顺序是从低层次到高层次,数组对象继承于Object,也就是层次比Object低,因此这里输出的是"double array"而不是"Object"

 

2、奇数的问题

是否能用下面的方法确定其唯一参数是奇数?

public static boolean isOdd(int i) ...{
        System.out.println(i 
% 2);
        
return i % 2 == 1;
    }

看到这个你就会想到小学老师教的“能被2整除的是偶数,不能被2整除的是奇数”,于是乎很自然的就认为这是正确的。那么我告诉你正确答案:错!大错特错!翻翻教科书,奇数跟偶数的概念只适用于自然数,而这里的参数iint型的,它包含了负数,因此也就错了!(TX们,看书要仔细啊!)

3、无符号右移的问题

while (i != 0...{
            i 
>>>= 1;
        }

上面的代码中,要声明怎样的一个i,才能使循环变成无限循环?

很难想吧?那就提供几个选项缩小范围吧:

A short i = -1

B int i = -1

C long i = -1L

D byte i = -1

E char i = 1

还是不知道答案吧?那就告诉你吧,答案应该是AD

>>>”在java中被称为:“无符号”右移位运算符,它使用了“零扩展”:无论正负,都在高位插入0。而对charbyte或者short进行移位处理,那么在移位进行之前,它们会自动转换成一个int,因此“零扩展”不再发生,于是AD无论怎么移始终都等于-1,也就变成了无限循环。

 

4try-catch的问题

public class Test ...{

    
public static void main(String[] args) ...{
        
try ...{
            System.out.print(
"1");
            Runtime.getRuntime().addShutdownHook(
new Thread() ...{
                
public void run() ...{
                    System.out.print(
"2");
                }

            }
);
            System.exit(
0);
        }
 finally ...{
            System.out.print(
"3");
        }

    }

}

上面的代码有错吗?错在哪?如果没有错,那么其输出结果又是什么?

答案:代码没有错,其输出结果是“12”。

很出乎意料是不是?如果不是真的运行了程序,相信很多朋友都说不出正确结果,这么短短的一段代码却藏着多处陷阱。“try后必须紧跟至少一个catch,“finally的代码无论如何都要执行…”,相信很多朋友都可以这么背出来。但是真实的情况如你所见,try可以跳过catch直接跟finally的,而且finally的代码也有不被执行的情况。不明白?那你再仔细看看那句System.exit(0)吧,它意味着什么呢?执行它就代表你的程序(线程)结束,既然程序都结束了,finally当然就执行不到了,这跟return是有区别的,要特别注意!

 

既然提到try的问题,那就顺便把在网上看到的另外一篇介绍try之古怪的文章转过来吧,相信你看了后会大跌眼镜!

原帖:http://ajaxgo.javaeye.com/blog/118979

==========================以下内容为转载===============================

 

今天无意中,同事发现了一个有趣的现象,首先来看一下以下代码:

 

java 代码会发现,18行加载了一下变量b中的值,所以,其实,在执行代码的第8行时,并不是执行return,而只是往内存中存储变量b的值。

 

public class Test ...{    
    
private static int todo() ...{    
        
int b=1;    
        
try...{    
            
int a=2/0;    
        }
 catch(Exception e) ...{    
            System.out.println(
"in catch"); 
            
return 1;    
        }
 finally ...{    
            System.out.println(
"in final");    
        }
    
        
return 2;    
    }
    
    
/** *//**   
     * 
@param args   
     
*/
   
    
public static void main(String[] args) ...{    
        System.out.println(todo());    
   
    }
    
}
   

此时,运行结果为

in catch

in final

1

 

看到这里,少部分人已经开始不明白了,我们继续看另一则代码(请注意细小的差别):

 

java 代码

public class Test ...{       
    
private static int todo() ...{       
        
int b=1;       
        
try...{       
            
int a=2/0;       
        }
 catch(Exception e) ...{       
            System.out.println(
"in catch");    
            
return b;       
        }
 finally ...{       
            System.out.println(
"in final");       
        }
       
        
return 2;       
    }
       
    
/** *//**     
     * 
@param args     
     
*/
      
    
public static void main(String[] args) ...{       
        System.out.println(todo());       
      
    }
       
}
    

此时,运行结果依旧是

in catch

in final

1

 

但是通过单步执行,会发现,两种代码的执行顺序有少许差别

 

第一种代码的try/catch单步执行顺序为

7->10->8

 

第二种代码的try/catch单步执行顺序为

7->8->10->8

 

 

这里我们对其进行了研究,从中间码入手,首先来分别看看两者的中间码:

 

 

上述第一块代码的中间码

11  ldc "in catch"> [22]   

13  invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]   

16  getstatic java.lang.System.out : java.io.PrintStream [16]   

19  ldc "in final"> [30]   

21  invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]   

24  iconst_1   

25  ireturn  

 

可以发现,明显的执行顺序,1行把装入字符串转入内存,2行打印,4行在把字符串装入内存,5行再打印,7行返回

 

上述第二块代码的中间码

13  ldc "in catch"> [22]   

15  invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]   

18  iload_0 [b]   

19  istore_3   

20  getstatic java.lang.System.out : java.io.PrintStream [16]   

23  ldc "in final"> [30]   

25  invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]   

28  iload_3

29  ireturn  

 

 

 

以上的过程,分析下中间码,还是可以理解的,但是非常非常让人奇怪的是,如下的代码(请还是注意不同的细节)

 

java 代码 

public class Test ...{          
    
private static int todo() ...{          
        
int b=1;          
        
try...{          
            
int a=2/0;          
        }
 catch(Exception e) ...{          
            System.out.println(
"in catch");       
            
return b;          
        }
 finally ...{   
            b
=3;        
            System.out.println(
"in final");          
        }
          
        
return 2;          
    }
          
    
/** *//**       
     * 
@param args       
     
*/
         
    
public static void main(String[] args) ...{          
        System.out.println(todo());          
         
    }
          
}
    

此时运行结果照样是

in catch

in final

1

 

单步执行结果是 7->8->10->11->8

想不通的是,根据单步执行,在finally里对b进行了修改,但是,返回的仍旧是1

 

再来看看这个代码的中间码:

 

第三个代码的中间码

13  ldc "in catch"> [22]   

15  invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]   

18  iload_0 [b]   

19  istore_3   

20  iconst_3   

21  istore_0 [b]   

22  getstatic java.lang.System.out : java.io.PrintStream [16]   

25  ldc "in final"> [30]   

27  invokevirtual java.io.PrintStream.println(java.lang.String) : void [24]   

30  iload_3   

31  ireturn  

 

因为,其实我没怎么学过汇编,只是初步的分析了

3行加载b的地址,4行在地址3储存b的值(此时地址3的值为1),5行加载一个常量,6行把常量存入b的地址(此时b的值为3),10行加载地址3的值,最后返回值。

不知道这样的解释是否合理,也请,希望能够得到一个对于try./catch块执行问题的通俗答案,谢谢!

 

==========================以上内容为转载===============================

 

好了,由于时间关系,本次拾遗就到此结束了,以后会陆续补充些其它经典问题上来,一来供自己备忘,二来给java初学者一些警示,希望通过此文能减少大家编码时出现的错误!哪怕是有一位朋友觉得有用,我也会非常高兴!

 

Java咖啡群欢迎大家加入(加群者送分,顶帖者也送分哈),群号:60446231
本群以讨论IT行业话题为主,本着交流信息的原则,为大家营造一个轻松欢娱的环境,给平时枯燥的工作增添一些调味剂.
当然,技术问题同样在讨论范围之中.
本人在这里欢迎大家的到来,并预祝大家五一快乐.(鼓掌...谢谢...嘿嘿...)
 
Java基础群:49827836
java初学者提供帮助,在讨论及解决问题中共同成长。

Java基础拾遗(一)

关于Java常见基础知识的查缺补漏,学习总结笔记~
  • qq_25827845
  • qq_25827845
  • 2017年07月29日 20:59
  • 1497

数学拾遗

布尔运算 与非(NAND)或非(NOR)不同于异或或者异或 与非门真值表: A B Y 0 0 1 0 1 1 1 0 1 1 1 0 逻辑表...
  • lanchunhui
  • lanchunhui
  • 2015年11月23日 23:11
  • 594

Python拾遗

#简单的来看帮助文档input(...)和raw_input(...)有如下区别 >>> help(input) Help on built-in function input in module _...
  • xmnathan
  • xmnathan
  • 2014年07月25日 09:14
  • 1059

Python内容拾遗

1.1运算符 in eg: print “hello” in “sdsfdhello” #True2.1 int 2.1.1 创建对象的两种方式: n1 = 123 ...
  • WxyangID
  • WxyangID
  • 2016年11月07日 12:35
  • 164

基础拾遗------特性详解【含常用过滤器实例】

基础拾遗 基础拾遗------特性详解 基础拾遗------webservice详解 基础拾遗------redis详解 基础拾遗------反射详解 基础拾遗------委托详解 基础拾...
  • qcq8208
  • qcq8208
  • 2017年01月20日 18:07
  • 273

Google MapReduce - 阅读拾遗

MapReduce 之前用的和学的都比较多了,这里简单记一下论文中的一些小收获和感受 本文参考:Google MapReduce 中文版 1.0 版 译者 alex,原文地址 http:/...
  • u010557442
  • u010557442
  • 2017年09月28日 21:26
  • 110

python语法基础拾遗

原文来自http://www.jianshu.com/p/d5bc50d8e0a2,摘录自己不熟悉的部分记下来以便日后查阅。因为是为了自己看而写的,故基本是提纲性质,非常简略,大家有疑惑的话请在评论区...
  • qq_35539645
  • qq_35539645
  • 2017年09月29日 16:45
  • 41

C#拾遗之读写操作

最近一直在学C#语言,在写一些程序的时候难免遇到C#输入输出的问题,除了葛老师讲的一些东西,我也在看网上的一些关于C#程序设计的视频讲解,在看到C#流程控制结构的这一章节,看到了一个很好的关于C#读写...
  • erlian1992
  • erlian1992
  • 2015年03月29日 14:04
  • 998

python拾遗(一)前言

从大三到现在,学习python也有两年了,也许python不是我学习的第一门语言,也不是我的主要工作语言,但却是我最喜欢的语言,喜欢它带给我的成就感,并且不断激励我学习下去。知乎上有篇文章介绍了pyt...
  • iloster
  • iloster
  • 2015年12月09日 21:08
  • 467

计算机底层知识拾遗(三)理解磁盘的机制

磁盘是一种重要的存储器,位于主存结构的下方,是永久存储的介质。在计算机底层知识拾遗(一)理解虚拟内存机制 这篇中说了虚拟内存是面向磁盘的,理解磁盘的工作原理对理解计算机的很多概念有很大的帮助。尤其是在...
  • ITer_ZC
  • ITer_ZC
  • 2015年01月22日 18:45
  • 4428
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Java 拾遗
举报原因:
原因补充:

(最多只允许输入30个字)