- 文件構成
- 文件名
一個類對應一個.java文件,如:public class Point是在Point.java文件中。
一個包內的非public方法,可以包含在經常被調用的public類中(這種情況下,.java與.class可以不對應——如內部類(筆者注))。 -
文件的位置
對於myProject.framework包,其文件全路徑爲<ProjectRoot>/myProject/framework。
對於JP.co.esm.wiki.extremedomo包,其文件全路徑爲。<ProjectRoot>/JP/co/esm/wiki/extremedomo -
測試類名
類Point的測試類:命名爲PointTest.java。
包JP.co.esm.wiki.extremedomo的測試類:命名爲ExtremeDomoTest.java。
其它規則——ClassName類的單元測試類,命名爲ClassNameUt(Ut是UnitTest的簡寫)。 -
測試類的位置
測試類一般位於被測試類的同一目錄下,或該目錄的名爲“test”的子目錄下。
原因:如果測試類不與被測試的對象在一起,其存儲路徑容易被忘記。而測試類與產品的分離工作可以交給一些測試工具來進行(如:makefile, Ant的build.xml 等)。
其它規則——把測試類的子目錄命名爲“ut ”。
其它規則——分離測試類,如包JP.co.esm.wiki.extremedomo的測試類放在包test.JP.co.esm.wiki.extremedomo中。
- 文件名
- 命名規則
- 包名
以“.”進行區分。如:JP.co.your.domainname.projectname - 文件名
根據編譯程序的規則,公有類名必須與文件字相同(包括大小寫)。 - 類名
以大寫字母開頭,並以大寫字母區分單詞。如:HelloWorld - 異常類名(例外類名)
類名以Exception結尾。如:IllegalArgumentException - 接口名
與類的命名規則相同。如:Attributed
如果有必要與類相區別,則以“I ”開頭。如:IAttributed。
再有,如果這個接口表示的是一種可歸屬於各種不同類的不同對象的屬性,這些屬性是根據對象“有能力”做的事情來定義的,則命名以-able結尾表示該接口具有某種功能。如:Runnable,Clonabe, Serializable, … - 封裝類名
特別指出的是,如果有必要與接口進行區分,實現類名可以以Impl結束。如:AttributedImpl - 抽象類名
當抽象類找不到合適的名字的時候,可以把Abstract 放在子類的開頭。
如:AbstractBeforeSubClassName - 常量(static final)
全爲大寫字母,並以“_”區分單詞。如:UPPER_CASE_WITH_UNDERSCORES - 方法名
以小寫字母開頭,以大寫字母區分單詞。如:HelloWorld - 工廠方法(factory method)名(用於創建對象的方法)
X newX()
X createX() - 轉換方法(把一個對象類型轉換成另一個對象類型的方法)
X toX() - 取得屬性的方法
X x()
X getX() // 處理JavaBeans 中的屬性(推薦)
boolean isEnabled() // 處理JavaBeans 中的屬性(推薦) - 設置屬性的方法
void setX(X value) // 處理JavaBeans 中的屬性(推薦) - 返回boolean值的方法
is + 形容詞,can + 動詞,has + 過去分詞,動詞的第三人稱單數形式(簡稱“動詞三單”或“三單元動詞”),動詞三單+ 名詞。
boolean isEmpty() // 處理JavaBeans 中的屬性(推薦)
boolean empty() // 不行!這個動詞不能很好地表達‘空’這個意義
boolean canGet()
boolean hasChanged()
boolean contains(Object)
boolean containsKey(Key) - boolean變量
形容詞,is + 形容詞,can + 動詞,has + 過去分詞,動詞三單,動詞三單+ 名詞。
boolean isEmpty
boolean dirty
boolean containsMoreElements - 英語和日語
基本上所有的識別子使用英文來表示,另外做成日英對應用字典,在整個工程生命週期中做維持(維護,保護) -
名稱的對稱性
在給類、方法命名時,注意使用諸如下面的那些具有對稱性的詞彙。
add/remove
insert/delete
get/set
start/stop
begin/end
send/receive
first/last
get/release
put/get
up/down
show/hide
source/target
open/close
sorece/ destination
increment/ destination
lock/ unlock
old/ new
next/ previous -
循環計數
小範圍內進行的循環計數,在迭代程序中按順序使用i, j, k(作爲控制循環的參數)。 -
使用域小的名字
對於使用域小的變量名,可以採用類型的簡化形式。
如:ServeletContext sc = getServletContext(); -
省略意義的名字
可以根據變量參數名來領會意義的參數。
反例:copy(s1, s2)
正例:copy(from, to) 或 copy(source, destination) -
無意義的名字
謹慎使用如Info, Data, Temp, Str, Buf這樣的名字。
反例:double temp = Math.sqrt(b*b - 4*a*c);
正例:double determinant = Math.sqrt(b*b - 4*a*c); -
大寫和小寫
雖然(java語言中)區分大小寫,但不可只以大小寫來區分名字。 -
其它
另外,有時候也根據項目的要求來使用以下的的命名規則。
局部(local)變量:lower_case_with_underscore
private/protected變量:_prefixUnderscore 或 suffixUnderscore_
static private/protected變量:__twoPrefixUnderscores 或 twoSuffixUnderscores__
- 包名
-
使用原則
-
編碼方式
編碼方式是以Sun Microsystems, Inc 的 JDK爲標準的。基本方式與K&R 的C語言編碼方式相同,類和方法的定義開始的“{”不另起一行寫。
/**//* COPYRIGHT ... =====⇒版權信息寫的文件開頭。注意這裏不是“/**”,而是“/*”* ...
*/
package myProject.util; =====⇒接下來是package,空一行列出import
import java.util.Stack;
import java.util.Vector;
/** *//** =====⇒ |在類定義之前(緊挨着類定義),從“/**”開始寫註釋。第一* Stack を表現するクラス. |行簡要地介紹類,用半角句號結束。下面進行詳細的說* オブジェクトのpush, pop が可能. |明。另起一行時,以“*”開始,與前面的第一個“*”相呼應。
*
* @author Kenji Hiranabe =====⇒@author部分必須寫
*/
public class Stack ...{ =====⇒類定義開始,“{”不換行
/** *//** =====⇒|方法的定義與類相同。如果有@param, @return, * 要素を追加する. |@exception,必須寫。?--按照必要的@see等。--?
* @param item 追加する要素
*/ |定義開始,“{”不換行;
public void push(Object item) ...{ =====⇒|縮進,1TAB=4 SPACE。if (itemCapacity <= itemCount) ...{ =====⇒if, while等的關鍵字與“(”間空一格。(方法名之後的“(”沒有空格)。“(”之後沒有空格,演算子前後各一個空格。“)”之後加一個空格,與“}”連接。
// ...
} else ...{ =====⇒注意if/ else的“{”與“}”的位置
// ...
}
}
/** *//**
* 先頭要素を取得する.先頭要素は取り除かれる.
* @return 先頭要素
*/
public Object pop() ...{
// ...
return top; =====⇒return值不用“()”括起來
}
} -
長的行
一行最多爲80個字符,如果超過了就需要斷行。斷行的方法有:1)利用局部變量;2)根據逗號換行;3)在優先級低的運算符前斷行。
如:
double length = Math.sqrt(Math.pow(Math.random(), 2.0) +
Math.pow(Math.random(), 2.0));
//方法1
double xSquared = Math.pow(Math.random(), 2.0);
double ySquared = Math.pow(Math.random(), 2.0);
double length = Math.sqrt(xSquared + ySquared);
//方法2
double length = Math.sqrt(Math.pow(Math.random(), 2.0,
Math.pow(Math.random(), 2.0);
//方法3
return this == obj
|| (this.obj instanceof MyClass
&& this.field == obj.field); -
長的聲明行
如果類或方法的聲明很長,1)根據extends/implements/throws換行;2)根據逗號換行。
如:
public class LongNameClassImplemenation
extends AbstractImplementation,
implements Serializable, Cloneable ...{
private void longNameInternalIOMethod(int a, int b)
throws IOException ...{
// …
}
public void longMethodSignature(int a, int b, int c,
int d, int e, int f) ...{
// …
}
// …
} -
import
在import中,儘量不要使用“*”;從同一個包中引入三個以上的類時,用“*”。
理由:增強可讀性。 -
abstract class vs. interface
儘量不使用抽象類(abstract class),而使用接口(interface)。抽象類僅在有一部分被封裝,一部分是抽象方法時使用。
理由:接口可以有多重繼承,但類只能是單一繼承。 -
public variable
實例變量(筆者理解爲“類字段”)不推薦設爲public,應對它們設置合適的訪問方法。
理由:對象指向的標準。類的內部狀態被隨意訪問不好。
但是,如果滿足下面的條件,把實例變量設爲public,直接對它們進行訪問也是可以的:-
這個實例變量與別的實例變量相獨立,他單獨的變更不會影響內部的整合性。
-
對任一個都有getX()/ set()方法。
- インスタンス変數の実裝が將來に渡って変更されないことが根拠付けられる.
另外,如果不滿足上面的條件,但要十分注意速度的情況下,不受這個限制。(但還是要慎重使用)
如:在Stack類中,不能將itemCount屬性設爲public,在Point類中,將x,y設爲public也可以(十分注意速度的時候,ex.Java3D 的 Vector/Point類)。
Stack s = new Stack();
s.itemCount = 79; //這種情況會使內部狀態瓦解
Point p = new Point();
p.x = 30; //不會使內部狀態瓦解
-
- 初始化
不以初始化爲目的(參數沒有被null初始化)。還有,不能兩次初始化。
反例:
class PoorInitialization ...{
private String name = "initial_name";
public void Sample() ...{
name = "initial_name";
}
} - 避免static變量
極力避免使用static變量(類變量)。(static final常量除外)
理由:static變量叫Semi-Global比較好,より文脈依存なコードを招き,副作用を覆いかくしてしまう.(static變量,也可以稱爲準全局變量。更由於上下文的依賴關係編碼的引用,會產生未預料到的效果。-- 網友 更正於2008/04/07) - private vs. protected
與其使用private,不如使用protected。
理由:private は確実にそのクラス外からの使用をシャットアウトできるが,クライアン
トが,より細かいチューニングをsubclass 化によって行うことを出來なくしてしまう.
另:有時候需要使用private。如果使用的是protected,會對之後繼承它的所有子類產生影響。 - get/set方法
避免胡亂生成用於訪問實例變量的public getX()/setX()方法。有必要進行討論,作成更有意義的方法。
理由:多數實例變量與其它的實例變量有着依賴關係。不能破壞類內部的整合性。 - 變量隱藏
避免使用與超級類變量相同的變量名。
理由:一般來說那種情況都存在bug。如果有這方面的意圖,要對其進行註釋。 - 數組聲明
數據聲明爲Type[] arrayName。
由於:Type arrayName[]不過是C語言留下來的習慣。
如:
static void main(String[] args); --- ○
static void main(String args[]); --- × -
public方法
類的public方法是以“自動販賣機的接口”爲目標的。設計成易懂,就算使用方法錯了,也不會影響內部的整合性。還有,可能的話,實行按合同進行的設計,把類的不變條件和方法的事前事後條件一起用代碼表現出來。 -
狀態取得和狀態變更的分離
方法是被設計成(只)做“一件事”。特別的,狀態變更和狀態取得這兩服務不能用一個方法來實現。負責狀態變更的方法return值爲void。Stack的例子中,top()和removeTop()這兩步要比pop()要好。
理由1:只做一件事的方法容易懂。(Stack的例子中,慣用pop()方法)。
理由2:並行性的控制,容易做到對異常安全的保護(參考:在C++中,pop()方法不能做到異常安全,所以pop()被作成了不能返回值的式樣)。
理由3:容易做子類化地擴展。 -
this的return
即使假裝考慮了方便客戶,也應該避免return this。
理由:叫做 a.meth1().meth2().meth3() 的連鎖一般會成爲synchronization上的問題之源。 -
方法的多重定義
避免過多使用根據引數類型來區分的方法(引數數量不同的可以)。特別是和繼承一起使用的話較麻煩。如:
× : draw(Line), draw(Rectangle)
○ : drawLine(Line), drawRectangle(Rectangle)
○ : draw(Shape) - equals()和hashCode()
由於重載Object.equals()方法,同是hashCode()方法也能重載。反之亦然。
理由:因爲對應Container類(Hashtable)等。 - clone()
如果使用clone()方法,需要封裝Cloneable並清楚標明。
如:
class Foo implements Cloneable ...{
// ...
public Object clone() ...{
try ...{
Foo foo = (Foo) super.clone();
// Foo 類屬性的克隆
// ...
} catch (CloneNotSupportedException e) ...{
// 因爲implements Cloneable, 所以不能發生
throw new InternalError();
}
}
}
理由:shallow copy中不好的例子很多。 - 缺省構造方法
如果有可能,無論什麼時候都要準備缺省的構造函數(沒有參數的那種)。
理由:在Class.newInstance()裏,可以根據類名的字符串來創建類。 - abstract method in abstract classes
在abstract類中寫no-op的方法,明確聲明爲abstract方法。並且,如果可以準備可公用的缺省的封裝,將其聲明爲protected,使子類可以在一行寫處理。
理由:java編譯器能檢查出沒有被封裝的abstract方法,可以避免所謂忘記單個被封裝的bug。 - Object的同值比較
使用equals()方法對Object進行比較,而不使用“==”。特別的,String 的比較中必須使用“==”。
理由:如果封裝者準備了equals()這個方法,就是希望使用這個封裝。equals()的缺省封裝,僅僅是“==”而已。
理由:單元測試使用的是assertEquals中的equals(),所以可以簡單地寫同值測試。 - 聲明和初始化
局部變量與初始值一起聲明。
理由:最小化變量的假定值。
反例:
void f(int start) ...{
int i, j; // 無初始值聲明
// 更多的代碼
// ...
i = start + 1;
j = i + 1;
// 使用i, j
}
正例:
void f(int start) ...{
// 更多的代碼
// ...
// 使用前進行聲明和初始化
int i = start + 1;
int j = i + 1;
// 使用i, j
} - 局部變量的重複利用不好
與其重複印使用某局部變量,不如新聲明並初始化一個。
理由:最小化變量的假定值。
理由:有利於編譯程序的最優化。
反例:
void f(int N, int delta) ...{
int i; // 無初始值聲明
for (i = 0; i < N; i++) ...{
// 使用 i
}
for (i = 0; i < N; i++) ...{// 還使用i
if (...) ...{
break;
}
}
if (i != N) ...{ // 判斷是否回到最後時使用了i
// ...
}
i = N – delta*2; // 再使用
// ...
}
正例:
void f(int N, int delta) ...{
for (int i = 0; i < N; i++) ...{
// 使用i
}
for (int i = 0; i < N; i++) ...{
// 使用其他的iif (...) ...{
found = true;
break;
}
}
if (found) ...{
// ...
}
int total = N – delta*2; // 有其他含義的變量
// ...
} - if/ while條件中的“=”
if, while的條件中,必須使用代入符號“=” 。
理由:一般情況下都是bug。只要不是boolean類型的,java編譯都能捕捉這種bug。 - 比較大小的運算符
儘量使用“<”,“<=”,儘量避免使用“>”,“>=”。
理由:統一大小方向,右邊的值大於左邊的值,以避免混亂。 - Cast
Cast儘可能用instanceof的條件文包圍。
C cx = null;
if (x instanceof C)
cx = (C) x;
else
evasiveAction();
理由:在這裏,經常會習慣性地考慮:“如果不是這個對象的實例呢?”。不過,如果可以判斷不能進行類型轉換(Cast)的時候就有bug,這種情況不在此限制以內。 - 異常類
?--異常類有使用泛圍大的特點,難以讀取多次使用的程序流。--?
對於異常類,如果可以使用JDK標準包中已有的內容,儘量加以利用,而不是新創建一個異常類。
如:IOException, NoSuchFileException, IllegalArgumentException,等,都是常用的異常類。
如果要創建新的異常類,要把它作爲對應包的全體接口來討論。 - 方法參數的變更是不好的
原則上方法參數需要輸入, 不用返回。即在方法內部不調用改變參數狀態的方法。不在返回參數中代入新的對象(如果可能的話設爲final)。
反例:
void moveX(Point p, int dx) ...{
p.setX(p.getX() + dx); // 參數變更(儘量避免)
}
void moveX(Point p, int dx) ...{
p = new Point(p.getX() + dx, p.getY());
// 這裏不傳遞給調用程序
}
例外:需要注意性參的情況下 - 方法參數的名字
應使方法參數容易讀取。特別是在與實例變量重複時,活用this,可以使參數的讀取較爲容易。
反例:
void reset(int x_, int y_) ...{
x = x_;
y = y_;
}
正例:
void reset(int x, int y) ...{ // 不要將參數名取爲 x_, y_ 等
this.x = x;
this.y = y;
} - toString()
toString()方法如果可能要隨時封裝。
理由1:用System.out.println(object)可隨時打印。
理由2:如果在單元測試等(過程)中失敗了,其顯示容易被區分。 - switch, if/else的循環處理是不好的
在用switch文進行分支處理的時候,要考慮到這是否爲不良設計的徵兆,同時還要考慮是否可以用多態性來實現。特別是有2個以上相同的switch時,一定要用多態性、FactoryMethod(工廠方法)、Prototype(原型)等來實現。if/else也是一樣的。並且,如果同樣的用於檢查null的if很多,考慮採用NullObject類型。 - String和基本類型的變換
從int到String的互逆變換,如下(其它基本類型也一樣)
String s = String.valueOf(i);
int i = Integer.parseInt(s);
理由:雖然有其它的方法,但上面的方法最易懂最有效。
其它方法:(不推薦)
String s = “” + i;
String s = new Integer(i).toString();
String s = Integer.toString(i); // 這樣不好
int i = new Integer(s).intValue();
int i = Integer.valueOf(s).intValue(); - collection
如果環境允許,請使用JDK1.2以後的collection類。也就是說,不用Vector、Hashtable,Enumeration,而用List(ArrayList),Map(HashMap),Iterator。
理由1:可以簡潔地使用有邏輯、有連貫性的方法名。
理由2:通運List,Set,Map接口,或不改變接口就能實現替換。
理由3:由於有同步化的操作,可以寫成高速的代碼(有這樣的可能性)。
參考:JDK1.2 collection使用指南 http://www.objectclub.jp/technicaldoc/java/jdk
-
- 註釋
- javadoc的活用
多用“/**註釋*/”的註釋。這樣的註釋,由與javadoc同樣的工具可以變爲HTML形式的文檔。
java的註釋有3種類型。
/** ... */ javadoc註釋。輸出爲html形式的文檔
/* ... */ 一般的註釋。內部的
// 一般的註釋。內部的
public類,方法,域必須要加“/** ... */ ”的註釋。 - 長註釋
註釋要跨多行時,應在最初用一句話概括,然後後面加長註釋。 - javadoc標籤(tag)
“/** ... */ ”註釋中,從“@”開始是關鍵字(javadoc標籤)。
@author author-name
@param paramName description
@return description of return value
@exception exceptionName description
@see string
@see URL
@see classname#methodname
param, return有特別要注意之處,要進行主旨註釋。如,用於輸出的參數,被變更時。
例1:
/** *//**
* 取得邊界框.
*
* @param b 邊界框(用於內存與速度效率,輸出參數)
*/
void getBBox(BBox b) ...{ b.min = this.min; b.max = this.max; } - 類註釋
“/** ... */ ”用於功能概要,也就是外部規格的描述,寫在類方法的定義開始之前。這個註釋的第一行有特別處理。即,被html的Method Index(方法目錄)使用。因此,最開始的第一行是註釋對象的外部功能的簡短說明。這行以半角“.”或<br>HTML標籤結束。接着第一行進行功能說明。
如:
/** *//**
* 表現堆的類. 堆爲先進後出的數據結構.
* <p>
* 元素總數保存到count中,元素保存到Vector中.
*
* @see util.Vector
* @author yourNameHere
*/
public class Stack ...{
/** *//**
* 當前的元素總數. 非負且小於capacity .
*/
protected int count;
/** *//**
* 取出上面的一個元素. 元素總數減1.
*
* <pre>
* 使用例子:
* Stack s = new Stack(10);
* s.push(99);
* int i = pop(); // i必須是99
* </pre>
*
* @return 上面的元素
*/
public int pop() ...{ ... }
}
在註釋中,寫“使用例子”等的時候,也可用<pre> </pre>圍住自動進行縮排,以避免改行。 - // 和 /* */
方法和類的內部的註釋,是用“ /* */ ”還是“ // ”,可以根據註釋的長度來判斷。
一行註釋最好用“ // ”。
例1:
/**//*
* 戦略:
* 1. まずnode を探す
* 2. clone する
* 3. inserter にclone を追加要請
* 4. 成功したら,node を削除
*/
例2:
int index = -1; // -1 表示不合法
參考:這個方法最好。
static final int INVALID= -1;
int index = INVALID; - Design by Contract (根據合同進行設計)
因爲要按照合同進行設計,所以在工程中添加Assert類。用Assert類來表現合同。
如:
class Stack ...{
private int capacity;
private int itemCount;
public void push(Object o) ...{
Assert.require(o != null); // 事前條件
// ...
// ...
Assert.ensure(this.contains(o)); // 事後條件
}
public boolean invariant() ...{ // 不變條件
Assert.invariant(0 <= capacity);
Assert.invariant(0 <= itemCount);
Assert.invariant(itemCount <= capacity);
return true;
}
}
補充:在J2SE1.4以後,assert爲關鍵字。
- javadoc的活用
- 性能
- 首先進行檢測
性能的改善從檢測開始。不能亂猜。 - new
在java中new費時間。多重循環中調用new的時候,如需要,使用輸出參數。
X getX() ...{
return new X(this.value);
}
上面代碼比較慢時,讓調用程序new,如下,
void getX(X x) ...{
x.setValue(this.value);
} - synchronized
synchronized(同步的)費時間。不要同步化所有類,而只對必要的部分進行同步。再有,Vector, Hashtable有默認的同步化重載。最好使用ArrayList,HashMap對必要的部分進行同步。(用Collections.synchronizedCollection進行外部同步) - 爲變量賦null值
當有大量不使用的變量時,積極把它們設爲null。特別是,數組的元素(對性能有嚴格要求的情況下)。
理由:有利於垃圾回收處理(ガベージコレクション)。
- 首先進行檢測
- 其它
- 在自己重新編制前進行討論
別人創建的類中有時需要新的方法,若要自己繼承(extends)該類來創創建新的類,或將該類作爲實例變量作成類時,首先要與該類的作者商談。如通用形式滿足其要求,則可使用原來的類。 - 複雜的設計不好
設計有迷惑時,多數情況是注重‘Simplicyty’方法,或要與java語言的特性良好一致。java語言的設計原理是KISS(Keep It Small and Simple)。同時,在後面的維護中,“Simplicyty”也很重要。 - 性能調整應在測試後
沒有編碼從一開始就注意性能的。要優先保證易讀、易維護。性能應在測試後進行改善。 - 過於精巧的代碼不好
要寫一般的java程序員都能理解的代碼。要假定對於運算符的順序、初始化的規則等,誰都不能肯定有自信,要用()明確運算順序,進行明確的初始化,這樣才易讀。
反例:return cond == 0 ? a < b && b < c : d == 1;
正例:return (cond == 0) ? ((a < b) && (b < c)) : (d == 1);
反例:
// 寫成單位行列,但費時間,誰也不好讀。
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++)
M[i-1][j-1] = (i/j)* (j/i); - 沒有100%的正確
這裏所寫的,並是100%作爲標準。如有困惑要進行整理、討論。常會有有充分理由不用規則的情況。本編碼標準的目的是希望能對團結協作的團對有所幫助。
- 在自己重新編制前進行討論
- 謝辭
このコーディング標準をまとめるにあたって,太田健一郎さん,慄原哲也さん,高橋徹さ
ん,小藪隆史さん,牛尾剛さん,井芹義博,山崎貴弘さんから有用なコメントを頂きまし
た.ありがとうございました. - 參考資料
Kenji Hiranabe, Java コーディング標準(オリジナル)
http://www.objectclub.jp/community/codingstandard/CodingStd.doc
Jeff Langr, Essential JAVA STYLE – Patterns for Implementation
1999, Prentice Hall
Doug Lea’s Draft Java Coding Standard
http://gee.cs.oswego.edu/dl/html/javaCodingStd.html
Mark Fussell’s Java Development Standards
http://www.chimu.com/publications/javaStandards/index.html
Macadamian Technology coding conventions for C++ and Java
http://www.macadamian.com/codingconventions.htm
AmbySoft Inc. Java Coding Standards, Elements of Java Style
http://www.ambysoft.com/essays/javaCodingStandards.html
http://www.ambysoft.com/books/elementsJavaStyle.html
Javasoft coding standards
http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html
以上
------------------------------------
以上爲自己學習時譯,僅供參考學習。如有譯得不妥或解釋不妥的地方,望您指出,以學習。其中紅色部分爲不理解或不會譯的地方,有待學習。
謝謝!
——xiaohuaidan717