java编程陷阱
1.首先什么是陷阱
簡潔的定義:? 陷阱,是指那些能夠正常編譯,但是在執(zhí)行時卻產(chǎn)生事與愿違的,有時候甚至是災(zāi)難性后果的程序代碼。
廣義的定義:? 任何可能導(dǎo)致程序員把大量的時間浪費在開發(fā)工具的使用上而不是最終軟件的進展上的語言特性、API或系統(tǒng),都可以稱呼為陷阱。
2、陷阱的分類
3、分析陷阱三重奏
a.癥狀或者問題? 首先找到是哪一個代碼造成的問題,陷阱的類型是什么。
b.問題的根源? 這個是揭示陷阱最重要的一個部分,我們要深入底層,了解可能導(dǎo)致程序員絆腳的詳細內(nèi)部工作過程、無效的假設(shè)或者API的缺陷。
c.解決方案? 這個是分析陷阱的最后一個步驟,最終給出一個程序?qū)崿F(xiàn)和運行結(jié)果。
例子1:找奇數(shù)
1 //找奇數(shù) 2 public static boolean isOdd(int i) { 3 return i % 2 == 1; 4 } 5 6 public static void main(String[] args) { 7 System.out.println(isOdd(1)); 8 System.out.println(isOdd(2)); 9 System.out.println(isOdd(3)); 10 System.out.println(isOdd(-1)); 11 System.out.println(isOdd(-2)); 12 }你會發(fā)現(xiàn)所有的負(fù)數(shù)在isOdd()方法里返回的都是false
更正方法:
1 public static boolean isOdd(int i) { 2 return i % 2 != 0; 3 }?
例子2:浮點數(shù)相減
1 public static void main(String[] args) { 2 System.out.println(2.0-1.1); 3 }你覺得得數(shù)會是多少?0.9?還是0.9000.....?
答案是:0.8999999999999999 可是我要的是0.9啊! 那怎么怎么辦呢?
1 public static void main(String[] args) { 2 System.out.println(new BigDecimal("2.0").subtract(new BigDecimal("1.1"))); 3 }這樣就實現(xiàn)了變成0.9了
如果你覺得太麻煩了那你還能這樣:
?
1 System.out.printf("%.1f",2.0-1.1);?
例子3、長整數(shù)
1 public static void main(String[] args) { 2 final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000; 3 final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000; 4 System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY); 5 }你一看覺得得數(shù)是多少? 很多人回答會是 1000 因為大家都能算的出來 ?但是你運行發(fā)現(xiàn)得數(shù)是5 ?你會說MICROS_PER_DAY 是long啊 應(yīng)該不會溢出啊,
這是因為在java中 24 默認(rèn)是int型的 那么24 * 60 * 60 * 1000 * 1000都會是int型的 當(dāng)然就會溢出 最后會把溢出后的值給MICROS_PER_DAY 所以最后的值會莫名其妙
改正:
1 public static void main(String[] args) { 2 final long MICROS_PER_DAY = 24L * 60 * 60 * 1000 * 1000; 3 final long MILLIS_PER_DAY = 24L * 60 * 60 * 1000; 4 System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY); 5 }NO.4? 互換內(nèi)容
1 public static void main(String[] args) { 2 int x = 1234; 3 int y = 5678; 4 x ^= y^= x^= y; 5 System.out.println(" x = " + x + " , y =" + y); 6 }在c或者C++中有人見過這樣的交換兩個數(shù)的方法 。但是在java中有點不相同 結(jié)果是??x = 0 , y =1234
其實它要的原理是這樣的:
1 public static void main(String[] args) { 2 int x = 1234; 3 int y = 5678; 4 y = x ^ y; 5 x = x ^ y; 6 y = x ^ y; 7 System.out.println(" x = " + x + " , y =" + y); 8 }當(dāng)由于java和c/C++存在差異所以才會出錯。
更正:
1 public static void main(String[] args) { 2 int x = 1234; 3 int y = 5678; 4 y = (x ^= (y^= x)) ^ y; 5 System.out.println(" x = " + x + " , y =" + y); 6 }No 5 :隨機數(shù)的問題
1 public static void main(String[] args) { 2 Random random = new Random(); 3 StringBuffer sb = null; 4 switch (random.nextInt(3)) { 5 case 1: 6 sb = new StringBuffer('P'); 7 break; 8 case 2: 9 sb = new StringBuffer('M'); 10 break; 11 default: 12 sb = new StringBuffer('G'); 13 break; 14 } 15 16 sb.append('a'); 17 sb.append('i'); 18 sb.append('n'); 19 System.out.println(sb.toString()); 20 }你覺得會打印出什么? 很多人會說是 "Pain" 或者 "Main" 或者 "Gain" 。
其實都錯了 ?答案只有一個:"ain"
其實主要的原因來自StringBuffer的構(gòu)成方法 當(dāng)你使用?new StringBuffer('G')是'G'會解釋成int 型 ?所以調(diào)用的構(gòu)造方法是:
StringBuffer(int capacity)?構(gòu)造一個不帶字符,但具有指定初始容量的字符串緩沖區(qū)。
而不是
StringBuffer(String str)?構(gòu)造一個字符串緩沖區(qū),并將其內(nèi)容初始化為指定的字符串內(nèi)容
所以改正的方法不言而喻了。
NO.6? 無情的增量操作
1 public static void main(String[] args) { 2 int j = 0 ; 3 for (int i = 0; i < 100 ; i++) { 4 j = j++; 5 } 6 System.out.println(j); 7 8 }這個題目你一個不注意就會喊出 輸出100 ;
其實答案是 0 ?這里的 j = j++; 從第一次賦值開始 j 就是被賦給了0 因為j++是后綴++ 賦值的時候還沒有增值。
NO.7? 整數(shù)邊界的問題
1 public static final int MAX = Integer.MAX_VALUE; 2 public static final int START = MAX - 100; 3 public static void main(String[] args) { 4 int j = 0 ; 5 for (int i = START; i <= MAX ; i++) { 6 j++; 7 } 8 System.out.println(j); 9 10 }?
你可能會很快的回答100 然后細心地看了下 說 101?
其實答案都不是 ?程序在無限循環(huán)著?
這是因為Integer.MAX_VALUE + 1 =?Integer.MIN_VALUE 所以在for循環(huán)中 i=MAX 后 i++ 這時的 i 變成Integer.MIN_VALUE 了
?依然滿足條件
要循環(huán)結(jié)束 在for中的 i <= MAX 變成 i < MAX 即可。
?
?NO.8? 計數(shù)器的問題
1 public static void main(String[] args) { 2 int minutes = 0 ; 3 for (int ms = 0; ms < 60 * 60 * 1000; ms++) { 4 if(ms % 60*1000 == 0) { 5 minutes ++ ; 6 } 7 } 8 System.out.println(minutes); 9 }?你有可能一下子就說出了是 輸出 60 因為能整除 60 * 1000 的只能是 1 * 60 * 1000 ,2 * 60 * 1000 ....一直到 60 * 60 * 1000 一共60 個?
但是 答案讓你失望了 ?是60000
其實 這個的陷阱是在?ms % 60*1000 == 0 的操作符的優(yōu)先級上 ?% 和 * 是同一級的 所以其實他是(ms % 60) *1000 == 0 這樣的 滿足ms % 60 這個就多了
?
NO.9? 優(yōu)柔寡斷的返回值
1 public static void main(String[] args) { 2 System.out.println(decisor()); 3 } 4 5 public static boolean decisor(){ 6 try { 7 return true; 8 } finally { 9 return false; 10 } 11 }你覺得會返回的是什么?
答案是 false;
那你再看一個:
NO.10 ?你好,再見
1 public static void main(String[] args) { 2 try { 3 System.out.println("Hello world"); 4 System.exit(0); 5 } finally{ 6 System.out.println("GoodBye world"); 7 } 8 }你又會覺得會返回的是什么?
答案是 僅僅只有?Hello world
NO.11 ?打開Javac進程
?
1 public static void main(String[] args) { 2 try { 3 Runtime runtime = Runtime.getRuntime(); 4 Process proc = runtime.exec("C:\\Program Files\\Internet Explorer\\iexplore.exe"); 5 int exitVal = proc.exitValue(); 6 System.out.println("process exitVal :" + exitVal); 7 } catch (IOException e) { 8 e.printStackTrace(); 9 } 10 }?
運行時你會發(fā)現(xiàn)IE啟動了 ?但是拋異常了
1 Exception in thread "main" java.lang.IllegalThreadStateException: process has not exited 2 at java.lang.ProcessImpl.exitValue(Native Method) 3 at Test.main(Test.java:9)這個異常的原因是在我們打開IE的時候就執(zhí)行了proc.exitValue()即返回退出時的值。可是我們還沒有退出IE啊 所以才會報異常
解決方法:給成
int exitVal = proc.waitFor();這樣看起來沒什么啊,是啊 很好理解 那你再看一個
1 public static void main(String[] args) { 2 try{ 3 Runtime rt = Runtime.getRuntime(); 4 Process proc = rt.exec("javac"); 5 int exitVal = proc.waitFor(); 6 System.out.println("Process exitValue: " + exitVal); 7 } catch (Throwable t){ 8 t.printStackTrace(); 9 } 10 }你運行的時候會發(fā)現(xiàn)程序會死在那里: 這是因為javac會一直等待它要執(zhí)行的東西 所以一直在等待:
改正:
1 public static void main(String[] args) { 2 try{ 3 Runtime rt = Runtime.getRuntime(); 4 Process proc = rt.exec("javac"); 5 InputStream stderr = proc.getErrorStream(); 6 InputStreamReader isr = new InputStreamReader(stderr); 7 BufferedReader br = new BufferedReader(isr); 8 String line = null; 9 System.out.println("<ERROR>"); 10 while ( (line = br.readLine()) != null) 11 System.out.println(line); 12 System.out.println("</ERROR>"); 13 int exitVal = proc.waitFor(); 14 System.out.println("Process exitValue: " + exitVal); 15 } catch (Throwable t){ 16 t.printStackTrace(); 17 } 18 }運行結(jié)果:
<ERROR> 用法:javac <選項> <源文件> 其中,可能的選項包括:-g 生成所有調(diào)試信息-g:none 不生成任何調(diào)試信息-g:{lines,vars,source} 只生成某些調(diào)試信息-nowarn 不生成任何警告-verbose 輸出有關(guān)編譯器正在執(zhí)行的操作的消息-deprecation 輸出使用已過時的 API 的源位置-classpath <路徑> 指定查找用戶類文件和注釋處理程序的位置-cp <路徑> 指定查找用戶類文件和注釋處理程序的位置-sourcepath <路徑> 指定查找輸入源文件的位置-bootclasspath <路徑> 覆蓋引導(dǎo)類文件的位置-extdirs <目錄> 覆蓋安裝的擴展目錄的位置-endorseddirs <目錄> 覆蓋簽名的標(biāo)準(zhǔn)路徑的位置-proc:{none,only} 控制是否執(zhí)行注釋處理和/或編譯。-processor <class1>[,<class2>,<class3>...]要運行的注釋處理程序的名稱;繞過默認(rèn)的搜索進程-processorpath <路徑> 指定查找注釋處理程序的位置-d <目錄> 指定存放生成的類文件的位置-s <目錄> 指定存放生成的源文件的位置-implicit:{none,class} 指定是否為隱式引用文件生成類文件 -encoding <編碼> 指定源文件使用的字符編碼-source <版本> 提供與指定版本的源兼容性-target <版本> 生成特定 VM 版本的類文件-version 版本信息-help 輸出標(biāo)準(zhǔn)選項的提要-Akey[=value] 傳遞給注釋處理程序的選項-X 輸出非標(biāo)準(zhǔn)選項的提要-J<標(biāo)志> 直接將 <標(biāo)志> 傳遞給運行時系統(tǒng)</ERROR> Process exitValue: 2NO.12? 日志粒度的控制
1 public class BadLogger { 2 private Logger m_log = null; 3 4 public BadLogger(Level l){ 5 ConsoleHandler ch = new ConsoleHandler(); 6 m_log = Logger.getLogger("no2.BadLogger.logger"); 7 ch.setLevel(l); 8 } 9 10 public void test(){ 11 System.out.println("The level for the log is: "+ m_log.getLevel()); 12 m_log.finest("This is a test for finest"); 13 m_log.finer("This is a test for finer"); 14 m_log.fine("This is a test for fine"); 15 m_log.info("This is a test for info"); 16 m_log.warning("This is a warning test"); 17 m_log.severe("This is a severe test"); 18 } 19 20 public static void main(String[] args){ 21 Level loglevel = Level.INFO; 22 if ( args.length !=0 ){ 23 if ( args[0].equals("ALL") ){ 24 loglevel = Level.ALL; 25 } 26 else if ( args[0].equals("FINE") ){ 27 loglevel = Level.FINE; 28 } 29 else if ( args[0].equals("FINEST") ){ 30 loglevel = Level.FINEST; 31 } 32 else if ( args[0].equals("WARNING") ){ 33 loglevel = Level.WARNING; 34 } 35 else if ( args[0].equals("SEVERE") ) { 36 loglevel = Level.SEVERE; 37 } 38 } 39 BadLogger logex = new BadLogger(loglevel); 40 logex.test(); 41 } 42 }這是錯誤的寫法 ?你會發(fā)現(xiàn)不管你傳什么參數(shù)結(jié)果都是:
1 The level for the log is: null 2 2012-10-24 19:21:26 no2.BadLogger test 3 信息: This is a test for info 4 2012-10-24 19:21:26 no2.BadLogger test 5 警告: This is a warning test 6 2012-10-24 19:21:26 no2.BadLogger test 7 嚴(yán)重: This is a severe test改正:
1 public class BadLogger { 2 private Logger m_log = null; 3 4 public BadLogger(Level l){ 5 ConsoleHandler ch = new ConsoleHandler(); 6 m_log = Logger.getLogger("no2.BadLogger.logger"); 7 m_log.addHandler(ch); 8 m_log.setLevel(l); 9 m_log.setUseParentHandlers(false); 10 ch.setLevel(l); 11 } 12 13 public void test(){ 14 System.out.println("The level for the log is: "+ m_log.getLevel()); 15 m_log.finest("This is a test for finest"); 16 m_log.finer("This is a test for finer"); 17 m_log.fine("This is a test for fine"); 18 m_log.info("This is a test for info"); 19 m_log.warning("This is a warning test"); 20 m_log.severe("This is a severe test"); 21 } 22 23 public static void main(String[] args){ 24 Level loglevel = Level.INFO; 25 if ( args.length !=0 ){ 26 if ( args[0].equals("ALL") ){ 27 loglevel = Level.ALL; 28 } 29 else if ( args[0].equals("FINE") ){ 30 loglevel = Level.FINE; 31 } 32 else if ( args[0].equals("FINEST") ){ 33 loglevel = Level.FINEST; 34 } 35 else if ( args[0].equals("WARNING") ){ 36 loglevel = Level.WARNING; 37 } 38 else if ( args[0].equals("SEVERE") ) { 39 loglevel = Level.SEVERE; 40 } 41 } 42 BadLogger logex = new BadLogger(loglevel); 43 logex.test(); 44 } 45 }這個時候輸入ALL這個參數(shù)結(jié)果就為:
1 The level for the log is: ALL 2 2012-10-24 19:23:31 no2.BadLogger test 3 最好: This is a test for finest 4 2012-10-24 19:23:31 no2.BadLogger test 5 較好: This is a test for finer 6 2012-10-24 19:23:31 no2.BadLogger test 7 良好: This is a test for fine 8 2012-10-24 19:23:31 no2.BadLogger test 9 信息: This is a test for info 10 2012-10-24 19:23:31 no2.BadLogger test 11 警告: This is a warning test 12 2012-10-24 19:23:31 no2.BadLogger test 13 嚴(yán)重: This is a severe test?NO.13? 遍歷容器的一些陷阱
three哪去了?
public class BadVisitor {public static void main(String[] args) {Vector v = new Vector();v.add("one");v.add("two");v.add("three"); v.add("four");Enumeration enume = v.elements();while (enume.hasMoreElements()){String s = (String) enume.nextElement();if (s.equals("two"))v.remove("two");else{System.out.println(s);}}System.out.println("What's really there...");enume = v.elements();while (enume.hasMoreElements()){String s = (String) enume.nextElement();System.out.println(s); }} }運行結(jié)果:
1 one 2 four 3 What's really there... 4 one 5 three 6 four改正:
1 public class GoodVisitor { 2 public static void main(String[] args) { 3 Vector v = new Vector(); 4 v.add("one"); v.add("two"); v.add("three"); v.add("four"); 5 Iterator iter = v.iterator(); 6 while (iter.hasNext()){ 7 String s = (String) iter.next(); 8 if (s.equals("two")) 9 iter.remove(); 10 else{ 11 System.out.println(s); 12 } 13 } 14 System.out.println("What's really there..."); 15 iter = v.iterator(); 16 while (iter.hasNext()){ 17 String s = (String) iter.next(); 18 System.out.println(s); 19 } 20 } 21 }?
?
?
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/yixiwenwen/archive/2012/10/24/2737473.html
總結(jié)
- 上一篇: 无法加载文件 C:/Windows/Mi
- 下一篇: 控制弹出窗口样式