成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Java和C++在細節上的差異:接口與內部類

開發 后端
本文主要從接口與內部類以及異常和斷言方面講解了Java和C++在細節上的差異。

繼上篇文章:Java和C++在細節上的差異:枚舉與反射

 六、接口與內部類:

1. 接口和抽象類:Java通過interface關鍵字來表示接口,接口中不能包含非靜態域字段,所有的域成員均是公有的抽象方法,如Comparable接口,如果希望利用Arrays.sort方法,數組的成員必須實現該接口。抽象類中包含抽象方法,和接口一樣抽象類也不能被實例化。

1) 接口不能被實例化,但是可以聲明接口的變量指向其實現類的對象。

2) 每個類只能有一個超類,但是可以實現多個接口。

以下為Java的接口和抽象類的定義方式:

  1. public interface Comparable { 
  2. int compareTo(Object other); 
  3.  
  4. public interface Comparable<T> { 
  5. int compareTo(T other); 
  6.  
  7. abstract class Employee implements Comparable { 
  8. public abstract int compareTo(Object other); 

在C++中同樣存在接口和抽象類的概念,也和Java一樣不能被實例化,但是并沒有相應的關鍵字存在,而是以一種潛在規則的方式存在,見如下代碼:

  1. //Comparable對象聲明的方法中只有純虛方法存在(析構函數除外),且沒有任何成員變量。 
  2. class Comparable { 
  3. public
  4. virtual ~Comparable() {} 
  5. //compareTo為純虛方法 
  6. virtual int compareTo(Comparable& other) = 0
  7.  
  8. //Employee對象中存在部分純虛方法,且可以有成員變量存在。 
  9. class Employee { 
  10. public
  11. virtual int compareTo(Comparable& other) { return 0; } 
  12. virtual int backgroud() = 0
  13.  
  14. private
  15. int _age; 

在C++的實現中,基于接口編程,同時導出C接口的工廠方法對于跨編譯器極為重要,該方式比較類似于Windows中的COM技術。

C++支持多重繼承,因此也存在虛基類(菱形結構)等問題帶來的負面影響,既子類的兩個父類中同時存在相同簽名的虛方法。見如下代碼:

  1. class TcpServerTask { 
  2. public
  3. virtual void run() {} 
  4.  
  5. class SentObjectTask { 
  6. public
  7. virtual void run() {} 
  8.  
  9. class TcpServerSentTask : public TcpServerTask, public SentObjectTask { } 

2. 對象克隆: Object對象中存在protected類型的clone方法,該方法將會完成子類對象clone的缺省操作,既對象域字段的淺拷貝,如果該對象的成員均為原始類型,如int、float等,或者為不可變類型,如String。這樣的淺拷貝將能夠達到對象clone的預期。換言之,如果對象內部存在可變對象的引用,淺拷貝將會帶來原始對象和cloned對象引用相同對象引用的問題。如果希望避免該問題的發生,子類需要實現Cloneable接口。這里需要指出的是Cloneable接口并未提供clone方法,只是提供了一種契約簽名。子類真正做的還是重載Object方法中的clone方法,由于Object中該方法為protected方法,所以caller不能直接調用它,只能將子類的clone方法聲明為共有類型,caller才能調用。

  1. //該實現類使用淺拷貝已經可以滿足其需要了 
  2. public class implements Cloneable { 
  3. //這里已經提升了clone方法的級別為public。 
  4. public Employee clone() throws CloneNotSupportedException { 
  5. return (Employee)super.clone(); 
  6. //深拷貝clone方法,必須clone對象內部所有可變的實例域,其中這些可變類 
  7. //必須全部都實現了自己的clone方法,否則將會跑出異常。 
  8. public class Employee implements Cloneable { 
  9. public Employee clone() throws CloneNotSupportedException { 
  10. //缺省clone完成了域字段的按位淺拷貝。 
  11. Employee cloned = (Employee)super.clone(); 
  12. cloned.hireday = (Date)hireday.clone(); 
  13. private Date hireday; 

注:數組對象可以通過Array的clone(public)方法完成元素的拷貝。

在C++中由于并不存在Object這樣的單根結構的框架,因此C++是以另外一種方式表現該問題的,既缺省拷貝構造和缺省等于操作符重載。和Java類似,這兩個方法也是member bitwise拷貝的,但這是由編譯器在生成對象模型時自動完成的缺省行為,如果該類重載了拷貝構造函數和等于操作符,在需要copy的時候則會調用重載后的方法,類的實現者應該在這兩個方法中完成深拷貝。C++中還可以通過將這兩個方法顯示的聲明為private類型的方法來禁用這種對象之間的copy行為,一旦出現,編譯器將會在在編譯器報錯。在C++中還存在一個explicit的關鍵字,可以有效的防止編譯器通過自行推演隱式的調用對象的拷貝構造函數和等于操作符函數,見如下代碼:

  1. //該類將會使用缺省的copy constructor,因此也會出現兩個對象 
  2. //引用相同_name變量地址的問題。 
  3. class Employee { 
  4. private
  5. char* _name; 
  6. }; 
  7. //該類由于將這兩個方法私有化,一旦出現對象的隱式拷貝構造, 
  8. //將會導致編譯錯誤。 
  9. class Employee { 
  10. private
  11. Employee(Employee& other); 
  12. const Employee& operator= (Employee& other); 
  13. private
  14. char* _name; 
  15. }; 
  16. //將會調用重載后的這兩個函數 
  17. class Employee { 
  18. Employee(Employee& other); 
  19. const Employee& operator= (Employee& other); 
  20. private
  21. char* _name; 
  22. }; 

注:C++中有一種被稱為引用計數的技術,經常會用在這個地方,以便提高對象copy的效率。

3. 接口與回調:嚴格意義上講,回調這個屬于更多的應用于C/C++這些支持基于過程編程的語言,Java中的回調是通過接口的方式來實現的,由于在接口的實現類中可以附帶更多的信息,因此其表達能力要由于C/C++中的函數指針,見如下代碼:

  1. public class Thread { 
  2. public Thread(Runnable r) {} 
  3.  
  4. public class MyTask implements Runnable { 
  5. public MyTask(int taskID) { 
  6. _taskID = taskID; 
  7.  
  8. public void setOk(bool ok) { 
  9. _ok = ok; 
  10.  
  11. public void run() {} 
  12.  
  13. public static void main(String[] args){ 
  14. MyTask t = new MyTask(5); 
  15. Thread thrd = new Thread(t); 
  16. t.setOk(true); 
  17. thrd.start(); 

這里的Runnable參數既為接口,Thread對象在啟動的時候會調用該接口實現對象的run方法,但是在調用之前可以給該實現類傳入更多的狀態等相關數據,以便在線程類調用run方法時可以得到更多的信息。
以下為回調函數在C/C++中的實現:

  1. typedef int(*TestCallback)(int,int); 
  2. int testCaller(TestCallback cb,int a,int b) { 
  3. return cb(a,b); 
  4.  
  5. int testCallback(int a,int b) { 
  6. return a * b; 
  7.  
  8. int main() { 
  9. TestCallback cb = testCallback; 
  10. return testCall(cb,5,6); 

在C++中還可以通過模板以更加松散的方式完成類似Java的基于接口的回調(Java的回調方式,C++完全可以做到),見如下代碼:

  1. template<typename T> 
  2. class Thread { 
  3. public
  4. Thread(T* r) _r = r {} 
  5. void start() { if (_r) _r->run(); } 
  6. private
  7. T* _r; 

在以上的實現中,T無需是某個接口的實現類,只要保證該類型包含run()方法即可,注意:C++中的模板是引用才編譯的方式,如果沒有任何Thread<T>的聲明,不會導致任何編譯錯誤,只有當聲明的類型對象中不包含run()方法時才會導致編譯錯誤。

4. 內部類:Java中內部類可以為私有內部類,既只有外部類可以訪問該內部類,而Java外部類的可見性只有包可見和public兩種。C++中的內部類比較類似于Java中的靜態內部類,只是一種作用域限制的行為,以下為Java非靜態內部類的說明:

1) 內部類可以訪問外部類的所有域成員和域字段,這也同樣包括私有的字段和成員。

2) Java的編譯器在構造外部類調用內部類構造方法時,自動將外部類的this變量作為一個隱式參數傳給了內部類的構造函數,內部類則在構造函數中保留了this變量的引用,該行為為編譯器隱式行為。

  1. public class Employee { 
  2. public class InnerClass { 
  3. bool test() { 
  4. //這里的_jobYears為外部類域字段。 
  5. return _jobYears > 10
  6.  
  7. public Employee(int jobYears,String name) {  
  8. _name = name;  
  9. _jobYears = jobYears;  
  10. _salary = 0
  11.  
  12. public void raiseSalary() { 
  13. //編譯器的會將以下構造隱式替換為InnerClass inner = new InnerClass(this); 
  14. //因為Java在為其編譯的時候發現InnerClass為非靜態內部類,則自動添加了以下構造: 
  15. //public InnerClass(Employee e) 
  16. InnerClass inner = new InnerClass(); 
  17. if (test())  
  18. _salary += 1000
  19. private String _name; 
  20. private int _jobYears; 
  21. private int _salary; 

注:針對以上事例,內部類InnerClass可以通過Employee.this._jobYears的全稱來顯式的代替_jobYears > 10 中的_jobYears。反過來在raiseSalary方法中可以通過this.new InnerClass()語法格式更加明確的創建InnerClass的對象。

  1. public class Employee {  
  2. public class InnerClass {  
  3. bool test() {  
  4. //這里的_jobYears為外部類域字段。  
  5. return Employee.this._jobYears > 10;  
  6. }  
  7. }  
  8.   
  9. public Employee(int jobYears,String name) {   
  10. _name = name;   
  11. _jobYears = jobYears;   
  12. _salary = 0;  
  13. }  
  14.   
  15. public void raiseSalary() {  
  16. //這里也可以不使用this作為內部該內部類對象的外部類對象  
  17. //引用,可以根據需要替換為其他外部類對象的引用,如:  
  18. // Employee other = new Employee();  
  19. // InnerClass innser = other.new InnerClass();  
  20. InnerClass inner = this.new InnerClass();  
  21. if (test())   
  22. _salary += 1000;  
  23. }  
  24. ......  
  25. }  

注:在外部類的作用域之外調用public內部類的語法為 OutClass.InnerClass。

3) 局部內部類的可見范圍僅僅限于聲明該局部類的函數內部,見如下代碼:

  1. public void start() { 
  2. class TimePrinter implements ActionListener { 
  3. public void actionPerformed(ActionEvent e) { 
  4. Date now = new Date(); 
  5. System.out.println("At the tone,the time is " + now); 
  6. //beep為外部類的域字段 
  7. if (beep) 
  8. Tookkit.getDefaultToolkit().beep(); 
  9. ActionListener l = new TimePrinter(); 
  10. new Timer(interval,l).start(); 

局部類同樣可以訪問函數內部的局部變量,但是要求該變量必須是final的。

  1. public void start(final bool beep) { 
  2. class TimePrinter implements ActionListener { 
  3. public void actionPerformed(ActionEvent e) { 
  4. Date now = new Date(); 
  5. System.out.println("At the tone,the time is " + now); 
  6. //beep為外部函數的局部變量。 
  7. if (beep) 
  8. Tookkit.getDefaultToolkit().beep(); 
  9. ActionListener l = new TimePrinter(); 
  10. new Timer(interval,l).start(); 

為了規避局部類只能訪問final局部變量的限制,既一次賦值之后不能再被重新賦值。但是我們可以通過數組的方式進行巧妙的規避,在下例中數組counter對象本身是final的,因此他不可以被重新賦值,然而其引用的數組元素則可以被重新賦值,見下例:

  1. public void test() { 
  2. final int[] counter = new int[1]; 
  3. for (int i = 0; i < dates.length; ++i) { 
  4. dates[i] = new Date() { 
  5. public int compareTo(Date other) { 
  6. //這里如果counter不是數組,而是被定義為final int counter, 
  7. //則會導致編譯失敗。 
  8. counter[0]++; 
  9. return super.compareTo(other); 

C++中同樣可以做到這些,其規則和Java的主要差異為C++的內部類無法直接訪問外部類的任何成員。

  1. class OuterClass { 
  2. public
  3. void testOuter() { 
  4. class FunctionInnerClass { 
  5. public
  6. void test() { 
  7. printf("This is FunctionInnerClass.\n"); 
  8. }; 
  9. FunctionInnerClass innerClass; 
  10. innerClass.test(); 
  11. }; 
  12.  
  13. int main() 
  14. OuterClass outer; 
  15. outer.testOuter(); 
  16. return 0; 

4) 匿名內部類,其基本規則和局部內部類相似,差別在于該內部類不能有聲明構造函數,這主要是因為Java要求類的構造函數和類名相同,而匿名內部類自身沒有類名,因此在new新對象的時候,傳入的構造函數參數為超類的構造函數參數。C++中不支持匿名類。見下例:

  1. public void start(final bool beep) { 
  2. ActionListener l = new ActionListener() { 
  3. public void actionPerformed(ActionEvent e) { 
  4. Date now = new Date(); 
  5. System.out.println("At the tone,the time is " + now); 
  6. //beep為外部函數的局部變量。 
  7. if (beep) 
  8. Tookkit.getDefaultToolkit().beep(); 
  9. new Timer(interval,l).start(); 

5) 靜態內部類,其功能和C++中的嵌套類非常相似,但是和Java自身的非靜態內部類之間還是存在一些差異,如靜態內部類不能直接訪問外圍類的對象引用域字段,但是可以訪問外部類的static域字段(包括private)。在Java中只有內部類可以被定義為static的,外圍類是不可以這樣定義的。

  1. public class TestMain { 
  2. private static boolean classField = false
  3. private boolean objectField = false
  4. static class InnerClass { 
  5. public void test() { 
  6. //這里由于classField是靜態域字段,所以靜態內部類可以直接訪問, 
  7. //但是對于objectField對象域字段而言,由于靜態內部類中沒有包含 
  8. //外部類的引用,因此不能直接訪問objectField. 
  9. if (classField)  
  10. System.out.println("Hello."); 
  11.  
  12. public static void main(String[] args) { 
  13. classField = true
  14. new InnerClass().test(); 

以下示例中的內部類只能是靜態內部類,因為該外部類的靜態方法在返回內部類的實例時,無法將一個外部類的對象引用傳遞給該內部類,因為必須要求該內部類為靜態內部類,否則將會報編譯錯誤。

  1. public class TestMain { 
  2. static class InnerClass { 
  3. public void test() { 
  4. System.out.println("Hello.\n"); 
  5.  
  6. private static InnerClass createInnerClass() { 
  7. return new InnerClass(); 
  8. public static void main(String[] args) { 
  9. createInnerClass().test(); 

如果InnerClass不是靜態內部類,則需要將上例改寫為:

  1. public class TestMain { 
  2. class InnerClass { 
  3. public void test() { 
  4. System.out.println("Hello.\n"); 
  5.  
  6. private static InnerClass createInnerClass() { 
  7. //為了確保InnerClass可以得到外部類的對象引用。 
  8. return new TestMain().new InnerClass(); 
  9. public static void main(String[] args) { 
  10. createInnerClass().test(); 

6) 代理類:通過以下代碼step by step解釋代理類的機制

  1. import java.lang.reflect.InvocationHandler; 
  2. import java.lang.reflect.Proxy; 
  3. import java.util.Arrays; 
  4. import java.util.Random; 
  5.  
  6. public class TestMain { 
  7. public static void main(String[] args) { 
  8. Object[] elements = new Object[1000]; 
  9. for (int i = 0; i < elements.length; ++i) { 
  10. Integer v = i + 1
  11. //h(調用處理接口)是代理類的核心處理單元。由于代理類對象只是包含了InvocationHandler 
  12. //這樣一個對象實例,并且是存放于超類Proxy中的,而實際的被代理實例必須存放于InvocationHandler 
  13. //的實現類中,如這里的Integer對象v。其中的核心代理代碼也是在InvocationHandler子類的 
  14. //invoke方法中完成的。 
  15. InvocationHandler h = new TraceHandler(v); 
  16. //1. 第一個參數表示ClassLoader,這里使用缺省加載器,因此傳入null即可。 
  17. //2. 第二個參數表示該代理類需要implement的接口數組(Java中可以實現多個接口)。 
  18. //3. 調用處理器接口,是代理類如果實現代理的核心,后面會介紹該類。 
  19. //4. 將該代理類作為Integer的代理存入數組。 
  20. elements[i] = Proxy.newProxyInstance(nullnew Class[] {Comparable.class}, h); 
  21. Integer key = new Random().nextInt(elements.length) + 1
  22. //1. 由于代理類也都實現Comparable接口,因此可以用于Arrays.binarySearch中。 
  23. //2. 對代理類進行二分查找的比較時,將會直接調用代理類的compareTo方法。 
  24. //3. 該自動生成的Proxy的子類,其中的compareTo方法會將所有外部調用的信息,連同 
  25. // 方法名一并傳給其內部調用處理器對象的invoke方法,并調用該方法(invoke). 
  26. //4. 這里Proxy子類會將所有實例化時指定接口(Comparable)的方法(compareTo),以及 
  27. // Object中toString、equals和hashCode方法的調用都會傳遞給調用處理器的invoke方法。 
  28. //5. 因此在輸出結果中不僅可以看到compareTo方法的調用被打印出,toString也可打印。 
  29. int result = Arrays.binarySearch(elements, key); 
  30. if (result >= 0
  31. System.out.println(elements[result]); 
  32.  
  33. class TraceHandler implements InvocationHandler { 
  34. //由于Proxy的子類是動態生成的,其具體的實現也是編譯器動態生成后傳給JVM的。 
  35. //因此這里是整個代理機制中唯一存放被代理對象的地方。 
  36. public TraceHandler(Object t) { 
  37. target = t; 
  38.  
  39. //在此例中,該方法是被Comparable接口中的compareTo方法調用的,該實現邏輯是位于該 
  40. //動態生成的Proxy子類中,如  
  41. //public MyProxy extends Proxy implements Comparable { 
  42. // int compareTo(Object other) { h.invoke(...); } 
  43. @Override 
  44. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
  45. //打印出實際被調用方法的名稱和參數值。 
  46. System.out.print(target); 
  47. System.out.print("." + method.getName() + "("); 
  48. if (args != null) { 
  49. for (int i = 0; i < args.length; ++i) { 
  50. System.out.print(args[i]); 
  51. if (i < args.length - 1
  52. System.out.print(", "); 
  53. System.out.println(")"); 
  54. //交給被代理類做實際的比較。 
  55. return method.invoke(target, args); 
  56. private Object target = null
  57. /* 輸出結果如下: 
  58. 500.compareTo(128) 
  59. 250.compareTo(128) 
  60. 125.compareTo(128) 
  61. 187.compareTo(128) 
  62. 156.compareTo(128) 
  63. 140.compareTo(128) 
  64. 132.compareTo(128) 
  65. 128.compareTo(128) 
  66. 128.toString() 
  67. 128 */ 

#p#

七、異常和斷言:

1. 異常處理:

1) 異常規范表示對于"已檢查"(checked)異常,如FileNotFoundException等,既在程序運行期間可以預測到的邏輯問題引發的異常,對于該類異常,需要在包含該異常的函數聲明部分標識出來,該函數可能會引發此類異常,如:

  1. public Image loadImage(String s) throws IOException, MalformedURLException 

如果在loadImage中仍然存在其他"已檢查",但是沒有在函數的異常規范中聲明出來,那么將會導致編譯失敗,因此對于函數中所有"已檢查"必須按照Java異常規范的要求,在函數的聲明中予以標識。對于該函數(loadImage)的調用者而言,在調用該函數時,必須將其放入try塊中,同時在catch字句中捕捉異常規范中標識的異?;蛩麄兊某悺?br /> 對于"運行時異常"(unchecked or runtime),由于大多是程序Bug或JVM問題所致,因此不可預測性極強,如ArrayIndexOutOfBoundException,對于該類異常無需在函數的異常規范部分予以聲明。

在C++標準中也同樣存在異常規范的說法,如

  1. File* loadFile(const char* s) throw std::bad_error 

所不同的是C++沒有明確的要求如果函數內部拋出了該異常,則必須在函數聲明的異常規范部分予以聲明,對于函數調用者而言也同樣沒有這樣的規定,必須捕獲其中的異常,因此異常規范在目前的C++編譯器中只是一種提示性的聲明。Java和C++在異常規范方面還存在的另一個區別是,C++中,如果函數沒有throw字句,那么該函數仍然可以拋出任何異常,但是對于Java的"已檢查"(checked)異常則必須通過throws字句聲明。

2) 如何拋出異常,在此方面,Java和C++沒有太大的差異,唯一的不同是Java拋出的異常必須是Throwable的實現類,C++中則沒有這樣的限制,也不存在這樣的異常接口。見如下代碼:

  1. public void howToThrowException() { 
  2. if (someErrorOccurred) 
  3. //1. 通過throw關鍵字直接拋出指定異常類型的對象即可。 
  4. throw new MyCheckedException(); 

3) 異常捕捉:由于Java中所有的異常均繼承自Throwable,所以catch(Throwable e)可以捕捉所有類型的異常,無論該異常是否為checked or unchecked異常,但是C++中并不存在這樣的異常祖先接口,因此如果想達到這樣的效果需要使用catch(...)關鍵字,這同樣表示捕捉所有的異常。

4) 異常鏈:當異常第一次拋出并且被catch住的時候,catch塊中的代碼可以再次拋出捕捉到的異常,同時也可以為了使上層業務邏輯能夠得到更加清晰的判斷,在第一次捕捉到異常后重新定義一個新的異常并再次拋出。有的時候,如果上層邏輯在需要的時候依然可以看到原始異常,將會對錯誤的處理更加合理。在Java中可以通過異常鏈的方式達到這樣的效果,見如下代碼:

  1. public void testExceptionChain() throws MyCustomizedFileException { 
  2. try { 
  3. FileInputStream in = new FileInputStream("myfile"); 
  4. catch (FileNotFoundException e) { 
  5. //定義了新的,準備再次被拋出的異常對象。 
  6. Throwable te = new MyCustomizedFileException("access file error."); 
  7. //將原始異常鏈接到該異常對象的內部,以供之后需要時通過getCause()方法重新獲取。 
  8. te.initCause(e); 
  9. throw te; 
  10.  
  11. public static void main(String[] args) { 
  12. try { 
  13. testExceptionChain(); 
  14. catch (MyCustomizedFileException e) { 
  15. //獲取該異常對象的原始異常。 
  16. Throwable te = e.getCause(); 
  17. System.out.println(te.getClass().getName()); 
  18. /* 輸出結果如下: 
  19. FileNotFoundException 
  20. */ 

5) finally字句:在Java的異常機制中存在finally這樣的關鍵字,其塊中的代碼無論異常是否發生都將會被執行,從而可以確保函數內部分配或者打開的資源都能在函數內部進行釋放或者關閉,如Socket連接、DB連接,見如下代碼:

  1. public void testFinally() { 
  2. InputStream in = null
  3. try { 
  4. in = new FileInputStream("myfile"); 
  5. catch (IOException e) { 
  6. //TODO: do something for this exception. 
  7. finally { 
  8. in.close(); 
  9. //Do the following code. 

在以上的代碼中,無論try塊中異常是否發生,finally塊中的代碼"in.close()" 都將會在函數退出之前或catch處理之后被執行,從而保證了FileInputStream對象能夠在函數退出之前被關閉。然而這樣的做法仍然可能導致一些影響代碼流程的問題,如果try塊中的代碼沒有產生異常,而是在finally中的in.close引發了異常,那么整個try{}catch{}finally{}代碼塊之后的代碼將不會被執行,而是直接退出該函數,同時拋出in.close()引發的異常給該函數的調用者。修正代碼如下:

  1. public void testFinally() { 
  2. InputStream in = null
  3. try { 
  4. in = new FileInputStream("myfile"); 
  5. catch (IOException e) { 
  6. //TODO: do something for this exception. 
  7. finally { 
  8. try { 
  9. in.close(); 
  10. catch (IOException e) { 
  11. //Do the following code. 

在C++中,由于對象是可以在棧上聲明并且分配空間的,當棧退出后會自行調用該對象的析構函數,因此該對象的資源釋放代碼可以放在類的析構函數中。該方式對于一個多出口的函數而言也是非常有效的,特別是對于加鎖和解鎖操作需要在同一個函數中完成,為了防止在某個退出分支前意外的漏掉解鎖操作,可以采用該技巧,見如下代碼:

  1. template<typename LockT> 
  2. class ScopedLock { 
  3. public
  4. ScopedLock(T& lock) : _lock(lock) { 
  5. _lock.lock(); 
  6.  
  7. ~ScopedLock() { 
  8. _lock.unlock(); 
  9. private
  10. LockT _lock; 
  11. }; 
  12.  
  13. void testFunc() { 
  14. ScopedLock s1(myLock); 
  15. if (cond1) { 
  16. return
  17. else if (cond2) { 
  18. //TODO: do something 
  19. return
  20. else { 
  21. //TODO: do something 
  22. return

對于以上代碼,無論函數從哪個分支退出,s1的析構函數都將調用,因此myLock的解鎖操作也會被調用。

6) 異常堆棧跟蹤:通過Throwable的getStackTrace方法獲取在異常即將被拋出的時間點上程序的調用堆棧,這樣有利于日志的輸出和錯誤的分析,見如下代碼:

  1. public void testStackTrace() { 
  2. try { 
  3. //TODO: call function, which may be raise some exception. 
  4. catch (Throwable e) { 
  5. StackTraceElement[] frames = e.getStackTrace(); 
  6. for (StackTraceElement f : frames) { 
  7. System.out.printf("Filename is = %s\n",f.getFileName()); 
  8. System.out.printf("LineNumber is = %d\n",f.getLineNumber()); 
  9. System.out.printf("ClassName is = %s\n",f.getClassName()); 
  10. System.out.printf("Methodname is = %s\n",f.getMethodName()); 
  11. System.out.printf("isNativeMethod = %s\n",f.isNativeMethod() ? "true" : "false"); 

也可以直接通過Throwable對象函數當前函數的運行棧信息,見如下代碼:

  1. public static void main(String[] args) { 
  2. Throwable e = new Throwable(); 
  3. StackTraceElement[] frames = e.getStackTrace(); 
  4. for (StackTraceElement f : frames) { 
  5. System.out.printf("Filename is = %s\n",f.getFileName()); 
  6. System.out.printf("LineNumber is = %d\n",f.getLineNumber()); 
  7. System.out.printf("ClassName is = %s\n",f.getClassName()); 
  8. System.out.printf("Methodname is = %s\n",f.getMethodName()); 
  9. System.out.printf("isNativeMethod = %s\n",f.isNativeMethod() ? "true" : "false");  
  10. }  
  11. /* 輸入如下: 
  12. Filename is = TestMain.java 
  13. LineNumber is = 3 
  14. ClassName is = TestMain 
  15. Methodname is = main 
  16. isNativeMethod = false */ 

C++語言本身并未提供這樣的方法,只是提供了__FUNCTION__、__LINE__、__FILE__這樣的3個宏來獲取當前函數的函數名、行號和文件名,但是無法得到調用棧信息,如果確實需要這樣的信息,只能通過操作系統的工具包來協助完成(僅針對Debug版本),目前Windows(vc)和Linux(gcc)都提供這樣的開發包。

2. 斷言:是主要用于開發、調試和系統集成測試期間進行Debug的一種方式和技巧,語法如下:

  1. assert condition OR assert condition : expression 

其中assert為關鍵字,當condition為false時,程序運行中斷,同時報出指定的錯誤信息,如果使用assert的后面一種形式,expression的結果將會同時輸出,這樣更有助于錯誤的判斷,見如下兩種代碼形式:

  1. public static void main(String[] args) { 
  2. int a = 5
  3. assert a > 10 : a; 
  4. System.out.println("Ok."); 
  5. /* 輸出結果: 
  6. Exception in thread "main" java.lang.AssertionError: 5 
  7. at TestMain.main(TestMain.java:4) 
  8. */ 
  9. public static void main(String[] args) { 
  10. int[] a = null
  11. assert a != null
  12. System.out.println("Ok."); 
  13. /* 輸出結果: 
  14. Exception in thread "main" java.lang.AssertionError 
  15. at TestMain.main(TestMain.java:4) 
  16. */ 

在eclipse中,缺省情況下斷言是被禁用的,如果需要開啟斷言,則需要在Run(Debug) As->Run(Debug) Configurations...->Arguments->VM arguments中添加"-enableassertions" 運行期參數。如果斷言被禁用,assert中的代碼不會被執行,因此在系統發布后也不會影響程序的運行時效率。使用者也可以通過該命令行參數-ea:MyClass -ea:com.mypackage.mylib 來指定需要啟用斷言的class和package,如果啟用的是package,那么該包內的所有class都將啟用斷言。在C++中,是依靠crt中的assert(cond)函數來實現的,如果cond為false,程序將會立即停止,但是在使用前首先需要保證assert.h文件被包含進當前文件,再有就是當前編譯的程序必須是Debug版本,對于Release版本,無論Win32和Linux,斷言的語句都將不會被執行。

原文鏈接:http://www.cnblogs.com/stephen-liu74/archive/2011/08/09/2131740.html

【系列文章】

  1. Java和C++在細節上的差異:泛型程序設計
  2. Java和C++在細節上的差異:程序設計結構
  3. Java和C++在細節上的差異:枚舉與反射
責任編輯:林師授 來源: Stephen_Liu的博客
相關推薦

2011-12-06 10:48:32

Java

2011-12-06 09:42:51

Java

2011-12-06 12:16:58

Java

2009-06-01 08:48:19

作用域變量作用域對象作用域

2023-10-19 13:24:00

Java工具

2020-01-15 11:14:21

Java算法排序

2020-12-14 10:23:23

Java內部類外部類

2024-07-01 12:48:00

C++內部類開發

2010-01-28 15:22:12

C++嵌套類

2009-08-26 18:00:07

C#內部類

2010-02-05 15:32:33

Java內部類

2020-09-21 07:00:42

Java內部類接口

2012-11-08 09:49:30

C++Java程序員

2009-06-11 13:08:29

Java內部類Java編程思想

2011-07-20 16:30:42

C++

2011-03-29 14:11:15

內部類

2011-07-21 15:44:33

Java內部類

2010-08-26 10:41:45

C#內部類

2009-06-11 11:07:25

Java局部內部類Final類型

2020-12-31 05:35:53

MySQL 8.0MySQL 5.7JSON
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91视视频在线观看入口直接观看 | 九色在线观看 | 999国产精品视频 | 国产一级片免费在线观看 | 女同久久| 日韩一区二区三区视频 | 一区日韩 | 亚洲国产精品一区二区第一页 | 久久精品99久久 | 欧美人人| 日本三级播放 | 成人午夜网站 | 欧美日韩亚洲国产综合 | 欧洲视频一区二区 | 欧美日韩国产免费 | 国产欧美精品一区二区 | 久久久久国产 | 91精品国产综合久久小仙女图片 | 午夜影视 | 欧美精品久久久 | 天天天天操 | 亚洲福利网站 | 日韩成人精品在线观看 | 国产wwwcom| 日韩欧美在线观看视频 | 久久一日本道色综合久久 | www.蜜桃av | 久久久999精品 | 狠狠干天天干 | 亚洲日本欧美 | 久久九九色 | 黄色网址在线免费播放 | 亚洲精品国产电影 | 久久精品国产一区二区三区 | 在线成人免费视频 | 日本公妇乱淫xxxⅹ 国产在线不卡 | 国产高清视频 | 亚洲免费视频网址 | 久久影音先锋 | a级片在线观看 | 久久99精品久久 |