通用编程_通用编程准则
通用編程
本文是我們名為“ 高級Java ”的學院課程的一部分。
 本課程旨在幫助您最有效地使用Java。 它討論了高級主題,包括對象創建,并發,序列化,反射等。 它將指導您完成Java掌握的旅程! 在這里查看 ! 
目錄
1.簡介 2.可變范圍 3.類字段和局部變量 4.方法參數和局部變量 5.裝箱和拆箱 6.接口 7.琴弦 8.命名約定 9.標準庫 10.不變性 11.測試 12.接下來是什么 13.下載源代碼1.簡介
在本部分的教程中,我們將繼續討論Java良好編程風格和健壯設計的一般原理。 我們已經在本教程的前面部分中看到了其中一些原則,但是在此過程中將引入許多新的實用建議,以提高您作為Java開發人員的技能。
2.可變范圍
在本教程的第3部分“ 如何設計類和接口”中 ,我們討論了如何將可見性和可訪問性應用于類和接口成員,從而限制了它們的范圍。 但是,我們尚未討論在方法實現中使用的局部變量。
在Java語言中,每個局部變量(一旦聲明)都有一個作用域。 從聲明位置到聲明的方法(或代碼塊)的末尾,該變量變得可見。因此,只有一條規則可遵循:將局部變量聲明為靠近其所在的位置盡可能使用。 讓我們看一些典型的例子:
for( final Locale locale: Locale.getAvailableLocales() ) {// Some implementation here }try( final InputStream in = new FileInputStream( "file.txt" ) ) {// Some implementation here }在這兩個代碼段中,局部變量的范圍都限于它們在其中聲明的執行塊。一旦塊結束,局部變量將超出范圍,并且不再可見。 看起來簡潔明了,但是隨著Java 8的發布和lambda的引入,使用局部變量的許多眾所周知的習慣用法已經過時了。 讓我們重寫前面示例中的for-each循環,改為使用lambda:
Arrays.stream( Locale.getAvailableLocales() ).forEach( ( locale ) -> {// Some implementation here} );局部變量成為函數的參數,該函數本身作為forEach方法的參數傳遞。
3.類字段和局部變量
Java中的每個方法都屬于某個類(如果是Java 8,則將該接口聲明為default ,則屬于某個接口)。 這樣,在方法實現中使用的局部變量與類成員之間就有名稱沖突的可能性。 Java編譯器可以從范圍中選擇正確的變量,盡管它可能不是打算使用的開發者。 為了向開發人員提示發生此類沖突(警告,突出顯示等)的時間,現代Java IDE進行了大量工作,但在開發時最好考慮一下。 讓我們看一下這個例子:
public class LocalVariableAndClassMember {private long value;public long calculateValue( final long initial ) {long value = initial; value *= 10;value += value;return value;}}該示例看起來很簡單,但是有一個陷阱。 方法calculateValue引入一個具有名稱value的局部變量,并以此隱藏具有相同名稱的類成員。 第08行應該將類成員和局部變量相加,但是它所做的卻大不相同。 正確的版本可能如下所示(使用關鍵字this ):
public class LocalVariableAndClassMember {private long value;public long calculateValue( final long initial ) {long value = initial; value *= 10;value += this.value; return value;}}天真的實現,但它突出了重要的問題,在某些情況下,調試和故障排除可能需要數小時。
4.方法參數和局部變量
Java開發人員經常遇到的另一個陷阱是使用方法參數作為局部變量。 Java允許使用不同的值重新分配非final方法參數(但是,它對原始值沒有任何影響)。 例如:
public String sanitize( String str ) {if( !str.isEmpty() ) {str = str.trim();}str = str.toLowerCase();return str; }它不是一段漂亮的代碼,但足以說明問題:將方法參數str重新分配給另一個值(基本上用作局部變量)。 在所有情況下(無一例外),可以并且應該避免這種模式(例如,通過將方法參數聲明為final )。 例如:
public String sanitize( final String str ) {String sanitized = str;if( !str.isEmpty() ) {sanitized = str.trim();}sanitized = sanitized.toLowerCase();return sanitized; }即使遵循引入局部變量的代價,遵循此簡單規則的代碼也更易于遵循和推理。
5.裝箱和拆箱
裝箱和拆箱都是Java語言中用于在原始類型(如int , long , double )之間轉換為各自的原始類型包裝程序(如Integer , Long , Double )的相同技術的名稱。 在本教程的第4部分“ 如何以及何時使用泛型”中 ,我們已經在將原始類型包裝器作為泛型類型參數討論時看到了它的作用。
盡管Java編譯器試圖通過執行自動裝箱來盡力隱藏這些轉換,但有時它會使情況變得更糟并導致意外的結果。 讓我們看一下這個例子:
public static void calculate( final long value ) {// Some implementation here }final Long value = null;calculate( value );上面的代碼段可以很好地編譯,但是當Long和long之間的轉換發生時,它將在第02行拋出NullPointerException 。 這里的建議是更喜歡使用原始類型(但是,正如我們已經知道的那樣,并非總是可能的)。
6.接口
在本教程的第3部分“ 如何設計類和接口”中 ,我們討論了基于接口和基于契約的開發,并著重強調了這樣的事實,即接口應盡可能優先于具體類。 本節的目的是通過展示真實的示例來說服您再有時間首先考慮接口。
接口不依賴于任何特定的實現(默認方法是一個例外)。 它們只是合同,因此在履行合同的方式上提供了很多自由和靈活性。 當實施涉及外部系統或服務時,這種靈活性變得越來越重要。 讓我們看一下以下簡單接口及其可能的實現:
public interface TimezoneService {TimeZone getTimeZone( final double lat, final double lon ) throws IOException; }public class TimezoneServiceImpl implements TimezoneService {@Overridepublic TimeZone getTimeZone(final double lat, final double lon) throws IOException {final URL url = new URL( String.format("http://api.geonames.org/timezone?lat=%.2f&lng=%.2f&username=demo",lat, lon) );final HttpURLConnection connection = ( HttpURLConnection )url.openConnection();connection.setRequestMethod( "GET" );connection.setConnectTimeout( 1000 );connection.setReadTimeout( 1000 );connection.connect();int status = connection.getResponseCode();if (status == 200) {// Do something here}return TimeZone.getDefault();} }上面的代碼段演示了典型的接口/實現模式。 該實現使用外部HTTP服務( http://api.geonames.org/ )來檢索特定位置的時區。 但是,由于聯系方式是由界面驅動的,因此很容易引入另一種使用數據庫或什至平面文件的實現。 這樣,接口可以極大地幫助設計可測試的代碼。 例如,在每次測試運行時調用外部服務并不總是可行的,因此提供替代的虛擬實現(也稱為存根或模擬)是有意義的:
public class TimezoneServiceTestImpl implements TimezoneService {@Overridepublic TimeZone getTimeZone(final double lat, final double lon) throws IOException {return TimeZone.getDefault();} }此實現可在需要TimezoneService接口的每個地方使用,從而將測試方案與對外部組件的依賴隔離開來。
在Java標準集合庫中封裝了許多適當使用接口的出色示例。 Collection , List , Set ,所有這些接口都有幾種實現的支持,當傾向于使用合同時,這些實現可以無縫且可互換地替換,例如:
public static< T > void print( final Collection< T > collection ) {for( final T element: collection ) {System.out.println( element );} }print( new HashSet< Object >( /* ... */ ) ); print( new ArrayList< Integer >( /* ... */ ) ); print( new TreeSet< String >( /* ... */ ) ); print( new Vector< Long >( /* ... */ ) );7.琴弦
字符串是Java以及大多數編程語言中使用最廣泛的類型之一。 Java語言通過本機支持串聯和比較,大大簡化了字符串常規操作。 另外,Java標準庫提供了許多不同的類來提高字符串操作的效率,這就是我們將在本節中討論的內容。
在Java中,字符串是不可變的對象,以UTF-16格式表示。 每次連接字符串(或執行任何修改原始字符串的操作)時,都會創建String類的新實例。 因此,連接操作可能會變得非常無效,從而導致創建許多中間字符串實例(通常來說,會生成垃圾)。
但是Java標準庫提供了兩個非常有用的類,旨在促進字符串操作: StringBuilder和StringBuffer (它們之間的唯一區別是StringBuffer是線程安全的,而StringBuilder不是)。 讓我們看看使用這些類之一的幾個示例:
final StringBuilder sb = new StringBuilder();for( int i = 1; i <= 10; ++i ) {sb.append( " " );sb.append( i ); }sb.deleteCharAt( 0 ); sb.insert( 0, "[" ); sb.replace( sb.length() - 3, sb.length(), "]" );盡管建議使用StringBuilder / StringBuffer來操作字符串,但是在連接兩個或三個字符串的簡單情況下,它看起來可能會過分殺傷,因此可以使用常規+運算符代替,例如:
String userId = "user:" + new Random().nextInt( 100 );通常,直接連接的更好替代方法是使用字符串格式設置,并且Java標準庫也通過提供靜態幫助器方法String.format來提供幫助。 它支持一組豐富的格式說明符,包括數字,字符,日期/時間等(有關完整參考,請訪問官方文檔 )。 讓我們通過示例來探索格式化的力量:
String.format( "%04d", 1 ); -> 0001 String.format( "%.2f", 12.324234d ); -> 12.32 String.format( "%tR", new Date() ); -> 21:11 String.format( "%tF", new Date() ); -> 2014-11-11 String.format( "%d%%", 12 ); -> 12%String.format方法提供了一種干凈方便的方法來從不同的數據類型構造字符串。 值得一提的是,某些現代Java IDE能夠根據傳遞給String.format方法的參數來分析格式規范,并在檢測到任何不匹配時向開發人員發出警告。
8.命名約定
Java作為一種語言并不能強迫開發人員嚴格遵守任何命名約定,但是社區已經開發了一套易于遵循的規則,這些規則使Java代碼在標準庫和其他任何Java項目中看起來都統一。
- 軟件包名稱以小寫形式輸入: org.junit , com.fasterxml.jackson , javax.json
- 類,枚舉,接口或注釋名稱以大寫字母鍵入: StringBuilder , Runnable , @Override
- 方法或字段名 ( static final除外)以駝峰形式輸入: isEmpty , format , addAll
- 靜態最終字段或枚舉常量名稱以大寫形式鍵入, 并用下劃線 “ _” MIN_RADIX : LOG , MIN_RADIX , INSTANCE
- 局部變量和方法參數名稱以駝峰式鍵入: str , newLength , minimumCapacity
- 泛型類型參數名稱通常以大寫形式表示為一個字符 : T , U , E
通過遵循這些簡單的約定,您正在編寫的代碼將與其他任何庫或框架看起來簡潔明了,并沒有區別,給人的印象是它是由同一人創作的(約定真正起作用的罕見情況之一)。
9.標準庫
無論您從事哪種Java項目,Java標準庫都是您最好的朋友。 是的,很難不同意它們有一些粗糙的邊緣和奇怪的設計決策,但是在99%的情況下,這是專家編寫的高質量代碼。 值得學習。
每個Java版本都為現有庫帶來了許多新功能(可能會淘汰舊功能),并增加了許多新庫。 Java 5帶來了在java.util.concurrent包下協調的新并發庫。 Java 6提供了(很少為人所知)腳本支持( javax.script包)和Java編譯器API(在javax.tools包下)。 Java 7對java.util.concurrent了很多改進,在java.nio.file包下引入了新的I / O庫,并通過java.lang.invoke包支持了動態語言。 最后,Java 8提供了一個期待已久的日期/時間API,該API在java.time程序包下java.time 。
Java作為平臺正在不斷發展,緊跟這一發展非常重要。 每當您打算將第三方庫或框架引入您的項目時,請確保Java標準庫中沒有所需的功能(實際上,有許多專門的高性能算法實現要優于標準庫中的實現)但在大多數情況下,您實際上并不需要它們)。
10.不變性
不變性遍及本教程,在這一部分中,它提醒您:請認真對待不變性。 如果您正在設計的類或正在實現的方法可以提供不變性保證,那么它幾乎可以在任何地方使用,而不必擔心會被并發修改。 這將使您作為開發人員的生活更加輕松(并希望您的隊友的生活也更加輕松)。
11.測試
測試驅動開發(TDD)實踐在Java社區中非常流行,這提高了所編寫代碼的質量標準。 與所有這些考慮TDD在桌子上帶來的好處,這是可悲的,觀察到Java標準庫不包含任何測試框架或棚架今天的。
盡管如此,測試已成為現代Java開發中必不可少的部分,在本節中,我們將介紹使用出色的JUnit框架的一些基礎知識。 本質上,在JUnit中 ,每個測試都是關于預期對象狀態或行為的一組斷言。
編寫出色的測試的秘訣在于使它們簡短而簡單,一次只測試一件事。 作為練習,讓我們編寫一組測試來驗證“ 字符串 ”部分中的String.format函數是否返回了所需的結果。
package com.javacodegeeks.advanced.generic;import static org.junit.Assert.assertThat; import static org.hamcrest.CoreMatchers.equalTo;import org.junit.Test;public class StringFormatTestCase {@Testpublic void testNumberFormattingWithLeadingZeros() {final String formatted = String.format( "%04d", 1 );assertThat( formatted, equalTo( "0001" ) );}@Testpublic void testDoubleFormattingWithTwoDecimalPoints() {final String formatted = String.format( "%.2f", 12.324234d );assertThat( formatted, equalTo( "12.32" ) );} }測試看起來非常可讀,其執行是實例化。 如今,普通的Java項目包含數百個測試用例,為開發人員提供了有關正在開發的回歸或功能的快速反饋。
12.接下來是什么
本教程的這一部分完成了與Java編程實踐和指南相關的一系列討論。 在下一部分中,我們將通過探索Java異常的世界,它們的類型,使用方式和使用時間來返回到語言功能。
13.下載源代碼
這是關于“通用編程準則”的課程,是“高級Java”課程的課程。 您可以在此處下載源代碼: AdvancedJavaPart7
翻譯自: https://www.javacodegeeks.com/2015/09/general-programming-guidelines.html
通用編程
總結
以上是生活随笔為你收集整理的通用编程_通用编程准则的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 绩效工作流_流绩效–您的想法
- 下一篇: 府谷楼盘备案价(府谷楼盘备案)
