前言
? ? ? 自學(xué)了4個(gè)多月的安卓顾稀,試著寫(xiě)了一個(gè)小程序避咆,雖然功能按照預(yù)想基本實(shí)現(xiàn)了滋饲,但是也很清楚代碼質(zhì)量肯定不好。在開(kāi)源中國(guó)sonar平臺(tái)進(jìn)行了一下代碼質(zhì)量分析躲雅。
? ? ? 百度了一下技術(shù)債務(wù):術(shù)語(yǔ)”技術(shù)債務(wù)“是由Ward Cunningham首次提出鼎姊,指的是開(kāi)發(fā)團(tuán)隊(duì)在設(shè)計(jì)或架構(gòu)選型時(shí)從短期效應(yīng)的角度選擇了一個(gè)易于實(shí)現(xiàn)的方案,但從長(zhǎng)遠(yuǎn)來(lái)看相赁,這種方案會(huì)帶來(lái)更消極的影響相寇,亦即開(kāi)發(fā)團(tuán)隊(duì)所欠的債務(wù)。(淚流滿(mǎn)面)
主要問(wèn)題列表:
格式:?jiǎn)栴}名字+問(wèn)題出現(xiàn)的次數(shù)
資源未關(guān)閉钮科,打開(kāi)發(fā)現(xiàn)有兩處用到的IO流沒(méi)有關(guān)閉
Conditions should not unconditionally evaluate to "TRUE" or to "FALSE"1
if/else判斷里出現(xiàn)了重復(fù)判斷唤衫,比如在if(a>10)的執(zhí)行體里面又判斷if(a<0),而后者肯定不會(huì)是true
Exception handlers should preserve the original exception13
處理異常的時(shí)候應(yīng)該保留原始的異常情況绵脯,不要直接來(lái)個(gè)catch(Exception e)了事
Throwable.printStackTrace(...) should not be called7
不應(yīng)該直接調(diào)用e.printStackTrace()佳励,而是用Loggers來(lái)處理(就是打Log)。
Loggers的優(yōu)勢(shì)是:Users are able to easily retrieve the logs.
The format of log messages is uniform and allow users to browse the logs easily.
Instance methods should not write to "static" fields6
不要用實(shí)例方法改變靜態(tài)成員蛆挫,理想情況下赃承,靜態(tài)變量只通過(guò)同步的靜態(tài)方法來(lái)改變
"public static" fields should be constant1
公共靜態(tài)成員應(yīng)該加上final,也就是public static final 一般不分家
Thread.run() and Runnable.run() should not be called directly1
不應(yīng)該直接調(diào)用Thread和Runnaale對(duì)象的run方法璃吧,直接調(diào)用run會(huì)使得run方法執(zhí)行在當(dāng)前線(xiàn)程楣导,失去了開(kāi)啟新線(xiàn)程的意義。但有時(shí)候可能會(huì)這樣做畜挨,下面有個(gè)例子筒繁。
Generic exceptions should never be thrown1
不太理解,大意是說(shuō)不要直接拋Error,RuntimeException/Throwable/Exception這樣的通用的異常巴元。我的具體應(yīng)用是:throw new Error("Error copying database")毡咏,給出的建議是:Define and throw a dedicated exception instead of using a generic one(定義并拋出一個(gè)專(zhuān)用的異常來(lái)代替一個(gè)通用的異常)
Class variable fields should not have public accessibility64
類(lèi)變量不要設(shè)置為public,而是設(shè)為private逮刨,再提供get和set方法呕缭。
Sections of code should not be "commented out"30
不要再注釋中出現(xiàn)大量的代碼段堵泽,會(huì)使代碼可讀性變差
Package declaration should match source file directory19
這個(gè)沒(méi)理解,包的聲明應(yīng)該與源文件目錄匹配恢总。
Utility classes should not have public constructors16
工具類(lèi)不應(yīng)該有公共的構(gòu)造器迎罗,也就是說(shuō)至少要有一個(gè)private的構(gòu)造器,如果沒(méi)有片仿,默認(rèn)的構(gòu)造器是public的纹安。
The diamond operator ("<>") should be used12
在定義集合的時(shí)候,等號(hào)右邊的<>內(nèi)不需要再寫(xiě)上元素類(lèi)型砂豌,直接空著就行厢岂。
Lambdas and anonymous classes should not have too many lines9
Lambdas表達(dá)式和匿名內(nèi)部類(lèi)不要寫(xiě)太多行,一般最多寫(xiě)20行阳距。
Anonymous inner classes containing only one method should become lambdas8
只包含一個(gè)方法的匿名內(nèi)部類(lèi)應(yīng)該寫(xiě)成Lambdas表達(dá)式的形式塔粒,增強(qiáng)代碼可讀性
Try-with-resources should be used8
用Try-with-resources的形式取代try/catch/finally的形式,這個(gè)有待于以后學(xué)習(xí)筐摘。
不要寫(xiě)空方法卒茬,除非這種情況:An abstract class may have empty methods, in order to provide default implementations for child classes.
Source files should not have any duplicated blocks7
源文件中不要出現(xiàn)任何重復(fù)的代碼段或行或字符串等。沒(méi)理解蓄拣。
"switch case" clauses should not have too many lines6
"switch case"?每個(gè)case里面的代碼不要太長(zhǎng)扬虚,太長(zhǎng)的話(huà)可以考慮寫(xiě)個(gè)方法代替,主要是為了增強(qiáng)代碼可讀性
Nested blocks of code should not be left empty6
嵌套代碼塊不要是空的球恤,比如 if( a > 0 ) { ?doSomething() ?} else { },這時(shí)候應(yīng)該把后面的else{}去掉荸镊。
Methods should not be too complex6
方法不要太復(fù)雜咽斧,否則難以理解和維護(hù)。
Unused private fields should be removed5
沒(méi)有使用的private的成員變量應(yīng)該移除掉躬存。
Dead stores should be removed5
沒(méi)有用到的本地變量或其他死存儲(chǔ)應(yīng)該移除掉张惹,也就是寫(xiě)方法的時(shí)候,定義的變量如果后來(lái)發(fā)現(xiàn)根本用不到岭洲,要記得刪掉那行代碼宛逗。
"switch" statements should end with a "default" clause4
switch語(yǔ)句應(yīng)該以default結(jié)束,這是一種defensive programming思想
Unused method parameters should be removed4
沒(méi)有用到的方法參數(shù)應(yīng)該移除掉
Control flow statements "if", "for", "while", "switch" and "try" should not be nested too deeply4
if /for/while/try這樣的嵌套不要太復(fù)雜
Useless parentheses around expressions should be removed to prevent any misunderstanding3
沒(méi)有意義的括號(hào)不要隨便加盾剩,以免造成誤解雷激,比如"="兩邊對(duì)象類(lèi)型是相同的,就不要強(qiáng)轉(zhuǎn)告私。
"for" loop stop conditions should be invariant3
for循環(huán)的結(jié)果條件不能是變量屎暇,而應(yīng)該是常量
"static" members should be accessed statically2
static成員是與類(lèi)、靜態(tài)方法相聯(lián)系的驻粟。
具體參考下面的18根悼,我還沒(méi)理解
Primitives should not be boxed just for "String" conversion2
不要使用 4+" "這樣的方式將int值轉(zhuǎn)變?yōu)樽址鞘褂?Integer.toString(4)這樣的方式。
就像Integer.parseInt("我是字符串")這樣挤巡,不要偷懶剩彬。
不要寫(xiě)空類(lèi)
Unused local variables should be removed2
沒(méi)有用到的本地變量要?jiǎng)h掉
"entrySet()" should be iterated when both the key and value are needed2
直接看英文更直接:When only the keys from a map are needed in a loop, iterating the keySet makes sense. But when both the key and the value are needed, it's more efficient to iterate theentrySet, which will give access to both the key and value, instead.
也就是說(shuō),如果只需要Map的Key矿卑,那么直接iterate這個(gè)Map的keySet就可以了襟衰,但是如果Key和value都需要,就iterate這個(gè)Map粪摘。具體看下面的19.
Method parameters, caught exceptions and foreach variables should not be reassigned2
方法參數(shù)/捕獲的異常/foreach的變量不應(yīng)該被重新賦值瀑晒。
Collection.isEmpty() should be used to test for emptiness2
當(dāng)判斷集合是否為空的時(shí)候,不要使用if (myCollection.size() == 0) 這樣的方式徘意,而是使用if (myCollection.isEmpty()這樣的方式苔悦,后者性能更高。
Standard outputs should not be used directly to log anything2
標(biāo)準(zhǔn)輸出不直接打印任何東西椎咧,也就是打log的時(shí)候玖详,不要使用System.out.println("My Message")這樣的方式,而是使用logger.log("My Message")這種方式勤讽。
Generic wildcard types should not be used in return parameters1
通配符不應(yīng)該出現(xiàn)在返回聲明中蟋座。比如這句:List <? extends Animal>getAnimals(){...}, 我們無(wú)法知道“是否可以把a(bǔ) Dog, a Cat 等加進(jìn)去”脚牍,等之后用到這個(gè)方法的時(shí)候向臀,我們沒(méi)必要去考慮這種問(wèn)題(前面引號(hào)里面的)。
Synchronized classes Vector, Hashtable, Stack and StringBuffer should not be used1
不要使用同步的Vector/HashTable/Stack/StringBuffer等诸狭。在早期券膀,出于線(xiàn)程安全問(wèn)題考慮,java API?提供了這些類(lèi)驯遇。但是同步會(huì)極大影響性能芹彬,即使是在同一個(gè)線(xiàn)程中使用他們。
通巢媛可以這樣取代:
ArrayList ?or ?LinkedList ? instead of ?
VectorDeque ?instead of ?
StackHashMap ?instead of ?
HashtableStringBuilder ?instead of
?StringBufferExit methods should not be called
盡量不要調(diào)用system.exit()方法舒帮。
Local Variables should not be declared and then immediately returned or thrown7
本地變量如果賦值之后直接return了,那就直接return本地變量的賦值語(yǔ)句陡叠。
Field names should comply with a naming convention6
命名要規(guī)范
Local variable and method parameter names should comply with a naming convention6
命名要規(guī)范
String literals should not be duplicated5
字符串不應(yīng)該重復(fù)玩郊,如果多次用到同一字符串,建議將該字符串定義為字符串常量匾竿,再引用瓦宜。
Return of boolean expressions should not be wrapped into an "if-then-else" statement3
不要寫(xiě)if ( ?a > 4 ?) { ?return false ?} else { return true }這樣的代碼,直接寫(xiě)return a > 4岭妖。
Static non-final field names should comply with a naming convention2
命名要規(guī)范
Modifiers should be declared in the correct order2
修飾符等要按約定俗成的順序書(shū)寫(xiě) 临庇,例如反璃,寫(xiě)成public static 而不是static public?
The members of an interface declaration or class should appear in a pre-defined order2
與前面的一個(gè)問(wèn)題類(lèi)似,根據(jù)Oracle定義的Java代碼規(guī)范中假夺,不同代碼的出現(xiàn)位置應(yīng)該如下所示:
class and instance variables--Constructors--Methods
Array designators "[]" should be on the type, not the variable2
數(shù)組的括號(hào)要寫(xiě)在類(lèi)型后面淮蜈,而不是變量后面,例如 int[] a 而不是int a[]
Multiple variables should not be declared on the same line1
不要在同一行定義多個(gè)變量
"switch" statements should have at least 3 "case" clauses1
當(dāng)至少有3種或者3種以上的情況時(shí)已卷,才考慮用switch梧田,否則用if/else的形式。
Overriding methods should do more than simply call the same method in the super class1
既然在子類(lèi)中重寫(xiě)了父類(lèi)的某個(gè)方法侧蘸,那就再這個(gè)方法中做些與父類(lèi)方法不同的事情裁眯,否則沒(méi)必要重寫(xiě)。
Statements should be on separate lines1
不要把這樣的代碼寫(xiě)在同一行:if(someCondition) ? ?doSomething()讳癌;而是應(yīng)該寫(xiě)成下面的形式
if(someCondition) {
doSomething()
}
Method names should comply with a naming convention1
命名要規(guī)范
"TODO" tags should be handle? ? TODO標(biāo)簽要及時(shí)處理穿稳,該做的事情不要忘了做
部分規(guī)則詳細(xì)說(shuō)明
1.The members of an interface declaration or class should appear in a pre-defined order
正確的順序如下所示:靜態(tài)成員變量→成員變量→構(gòu)造器→方法
public class Foo{
public static final int OPEN = 4; ?//Class and instance variables
private int field = 0;
public Foo() {...} ? ?//Constructors
public boolean isTrue() {...} ? ?//Methods
}
2.The diamond operator ("<>") should be used
Noncompliant Code Example:不規(guī)范的示例
List<String> ?strings = new ArrayList<String>();? // Noncompliant
Map<String, List<Integer>> map = new HashMap<String, List<Integer>>();? // Noncompliant
Compliant Solution :規(guī)范的示例
List<String> strings = new ArrayList<>();
Map<String, List<Integer>> map = new HashMap<>();
3.Sections of code should not be "commented out"
代碼片段不應(yīng)該出現(xiàn)在注釋中,這樣會(huì)bloat程序晌坤,可讀性變差
Programmers should not comment out code as it bloats programs and reduces readability.
Unused code should be deleted and can be retrieved from source control history if required.
4.Utility classes should not have public constructors
工具類(lèi)不應(yīng)該有public的構(gòu)造器逢艘,也就是工具類(lèi)至少要定義一個(gè)non-public的構(gòu)造器
Utility classes, which are a collection of static members, are not meant to be instantiated. Even abstract utility classes, which can be extended, should not have public constructors.
Java adds an implicit public constructor to every class which does not define at least one explicitly. Hence, at least one non-public constructor should be defined.
class StringUtils { // Noncompliant Code Example
? ? public static String concatenate(String s1, String s2) {
? ? ? ? ? return s1 + s2;
? ? }
}
class StringUtils { //Compliant Solution
? ? private StringUtils() {
? ? }
? ? public static String concatenate(String s1, String s2) {
? ? return s1 + s2;
? ? }
}
5."public static" fields should be constant
公共的靜態(tài)成員應(yīng)該加上final來(lái)修飾
There is no good reason to declare a field "public" and "static" without also declaring it "final". Most of the time this is a kludge to share a state among several objects. But with this approach, any object can do whatever it wants with the shared state, such as setting it to null.
public static Foo foo = new Foo();//不規(guī)范的
public static final Foo FOO = new Foo();//規(guī)范的
6.Class variable fields should not have public accessibility
public class MyClass {
public static final int SOME_CONSTANT = 0;? ? // Compliant - constants are not checked
public String firstName;? ? ? ? ? ? ? ? ? ? ? // Noncompliant
}
public class MyClass {
public static final int SOME_CONSTANT = 0;? ? // Compliant - constants are not checked
private String firstName;? ? ? ? ? ? ? ? ? ? ? // Compliant
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
7.Static non-final field names should comply with a naming convention
public final class MyClass {//Noncompliant Code Example
? ? ? private static String foo_bar;
}
class MyClass {//Compliant Solution
private static String fooBar;
}
8."switch" statements should have at least 3 "case" clauses
當(dāng)有3種或3種情況以上的時(shí)候,才用switch骤菠,否則用if/else
switch statements are useful when there are many different cases depending on the value of the same expression.
For just one or two cases however, the code will be more readable with if statements.
9.String literals should not be duplicated
prepare("action1"); ? ? // Noncompliant - "action1" is duplicated 3 times
execute("action1");
release("action1");
private static final String ACTION_1 = "action1";? // Compliant
prepare(ACTION_1); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// Compliant
execute(ACTION_1);
release(ACTION_1);
10.Return of boolean expressions should not be wrapped into an "if-then-else" statement
if (expression) {//Noncompliant Code Example
? ? ? return true;
} else {
? ? ?return false;
}
return expression;//Compliant Solution
return !!expression;
11.Method parameters, caught exceptions and foreach variables should not be reassigned
方法參數(shù)它改,捕獲的異常,foreach里的變量商乎,都不應(yīng)該重新賦值
class MyClass {//Noncompliant Code Example:不規(guī)范代碼示例
? ? public String name;
? ? public MyClass(String name) {
? ? ? ? ? ? name = name; ? ? ? ? ?// Noncompliant - useless identity assignment
? ? }
? ? public int add(int a, int b) {
? ? ? ? a = a + b; ? ? ? ? ? ? ? ?// Noncompliant
? ? ? ? return a; ? ? ? ? ? ? ? ? // Seems like the parameter is returned as is, what is the point?
? ?}
? ? public static void main(String[] args) {
? ? ? ? MyClass foo = new MyClass();
? ? ? ? int a = 40;
? ? ? ? int b = 2;
? ? ? ? foo.add(a, b);? ? ? ? ? ? ? ? ? // Variable "a" will still hold 40 after this call
? ? }
}
class MyClass {//Compliant Solution:規(guī)范代碼示例
? ? public String name;
? ? public MyClass(String name) {
? ? ? ? ?this.name = name;? ? ? ? ? ? ? // Compliant
? ? }
? ? public int add(int a, int b) {
? ? ? ? return a + b;? ? ? ? ? ? ? ? ? // Compliant
? ? }
? ? public static void main(String[] args) {
? ? MyClass foo = new MyClass();
? ? ? ? int a = 40;
? ? ? ? int b = 2;
? ? ? ? foo.add(a, b);
? ? ?}
}
12.Local Variables should not be declared and then immediately returned or thrown
Noncompliant Code Example:不規(guī)范代碼示例
public long computeDurationInMilliseconds() {
long duration = (((hours * 60) + minutes) * 60 + seconds ) * 1000 ;
return duration;
}
public void doSomething() {
RuntimeException myException = new RuntimeException();
throw myException;
}
Compliant Solution:規(guī)范代碼示例
public long computeDurationInMilliseconds() {
return (((hours * 60) + minutes) * 60 + seconds ) * 1000 ;
}
public void doSomething() {
throw new RuntimeException();
}
13.Thread.run() and Runnable.run() should not be called directly
The purpose of theThread.run()andRunnable.run()methods is to execute code in a separate, dedicated thread. Calling those methods directly doesn't make sense because it causes their code to be executed in the current thread.
Thread和Runnable里面的run方法設(shè)計(jì)的目的是讓run方法里面的代碼在不同的線(xiàn)程中執(zhí)行央拖。如果直接調(diào)用run方法,就會(huì)導(dǎo)致run方法里的代碼在當(dāng)前線(xiàn)程中執(zhí)行截亦,失去意義
Noncompliant Code Example:不規(guī)范的代碼示例
Thread myThread = new Thread(runnable);
myThread.run(); // Noncompliant
Compliant Solution:規(guī)范代碼示例
Thread myThread = new Thread(runnable);
myThread.start(); // Compliant
這部分內(nèi)容為個(gè)人理解爬泥,可以略過(guò)
但在有些情況,也會(huì)直接調(diào)用Runnable的run方法崩瓤,
下面這個(gè)postTaskSafely方法會(huì)保證task永遠(yuǎn)在主線(xiàn)程中執(zhí)行
public static void postTaskInMainThread(Runnable task) {
? ? ?int curThreadId= android.os.Process.myTid();//得到當(dāng)前線(xiàn)程的id
? ? if(curThreadId==getMainThreadId()) {// 如果當(dāng)前線(xiàn)程是主線(xiàn)程
? ? ? ? ? ? task.run();//直接執(zhí)行
? ? }else{// 如果當(dāng)前線(xiàn)程不是主線(xiàn)程
? ? ? ? getMainThreadHandler().post(task);//用主線(xiàn)程的Handler來(lái)post
}
14.Lambdas and anonymous classes should not have too many lines
Anonymous classes and lambdas (with Java 8) are a very convenient and compact way to inject a behavior without having to create a dedicated class. But those anonymous inner classes and lambdas should be used only if the behavior to be injected can be defined in a few lines of code, otherwise the source code can quickly become unreadable.
anonymous class number of lines : at most 20
15.Resources should be closed:該關(guān)閉的一定記得關(guān)閉
Java's garbage collection cannot be relied on to clean up everything. Specifically, connections, streams, files and other classes that implement theCloseableinterface or it's super-interface,AutoCloseable, must be manually closed after creation. Failure to do so will result in a resource leak which could bring first the application and then perhaps the box it's on to their knees.
Noncompliant Code Example:不規(guī)范的代碼示例
? ? OutputStream stream = null;
? ? try{
? ? ? ? for (String property : propertyList) {
? ? ? ? stream = new FileOutputStream("myfile.txt");? // Noncompliant
? ? ? ? // ...
? ? ? ? }
? ? }catch(Exception e){
? ? ? ? // ...
? ? }finally{
? ? ? ? stream.close();? // Multiple streams were opened. Only the last is closed.
? ? }
Compliant Solution:規(guī)范代碼示例
? ? OutputStream stream = null;
? ? try{
? ? ? ? stream = new FileOutputStream("myfile.txt");
? ? ? ? for (String property : propertyList) {
? ? ? ? ? ? // ...
? ? ? ? }
? ?}catch(Exception e){
? ? ? ? // ...
? ?}finally{
? ? ? ?stream.close();
? ?}
16.Exception handlers should preserve the original exception
Noncompliant Code Example:不規(guī)范的代碼示例
// Noncompliant - exception is lost
try { /* ... */ } catch (Exception e) { LOGGER.info("context"); }
// Noncompliant - exception is lost (only message is preserved)
try { /* ... */ } catch (Exception e) { LOGGER.info(e.getMessage()); }
// Noncompliant - exception is lost
try { /* ... */ } catch (Exception e) { throw new RuntimeException("context"); }
Compliant Solution:規(guī)范的代碼示例
try { /* ... */ } catch (Exception e) { LOGGER.info(e); }
try { /* ... */ } catch (Exception e) { throw new RuntimeException(e); }
try {
/* ... */
} catch (RuntimeException e) {
doSomething();
throw e;
} catch (Exception e) {
// Conversion into unchecked exception is also allowed
throw new RuntimeException(e);
}
17.Catches should be combined
Since Java 7 it has been possible to catch multiple exceptions at once. Therefore, when multiplecatchblocks have the same code, they should be combined for better readability.
Note?that this rule is automatically disabled when the project'ssonar.java.sourceis lower than7.
Noncompliant Code Example:不規(guī)范代碼示例
catch (IOException e) {
? ? doCleanup();
? ? logger.log(e);
}catch (SQLException e) { //Noncompliant
? ? doCleanup();
? ? logger.log(e);
?}catch (TimeoutException e) {? // Compliant; block contents are different
? ? ?doCleanup();
? ? ?throw e;
?}
Compliant Solution:規(guī)范代碼示例
catch (IOException|SQLException e) {
? ? doCleanup();
? ? logger.log(e);
?}catch (TimeoutException e) {
? ? doCleanup();
? ? throw e;
}
18."entrySet()" should be iterated when both the key and value are needed
Noncompliant Code Example:不規(guī)范的代碼示例
public void doSomethingWithMap(Map map) {
for (String key : map.keySet()) {? // Noncompliant; for each key the value is retrieved
Object value = map.get(key);
// ...
} ??
} ??
Compliant SolutionL:規(guī)范代碼示例
public void doSomethingWithMap(Map map) {
for (Map.Entry entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
// ...
} ??
} ??
以上內(nèi)容是按照自己的理解,整理歸納踩官,錯(cuò)誤和不當(dāng)之處却桶,歡迎指出,非常感謝蔗牡!