國慶假期已結束,假期8天,所有在家帶娃,體會到了妻子的不容易,須要好好努力來多賺錢了,言歸正傳。10月份開始進去JAVA 高級語法知識學習,本節複習學習的爲:靜態導入、可變參數、加強型for循環、裝拆箱。【轉摘,請註明來源:http://www.cnblogs.com/pony1223/p/7643842.html 】html
1、靜態導入java
一般若是咱們要使用靜態的成員(方法和變量)必須給出提供這個靜態成員的類。可是若是咱們使用靜態導入,使用這些靜態成員的時候無需再給出他們的類名。程序員
靜態導入時JDK5.0引入的新特效,下面經過實例來講明靜態導入的用法:編程
1.先定義一個公共類:數組
package study.javaenhance.util; public class Common { public static final int AGE = 10; public static void output() { System.out.println("Hello World!"); } }
2.在另外一個包中使用時,若是不用靜態導入,是這樣用的:緩存
package study.javaenhance; import study.javaenhance.util.Common; public class StaticImport { public static void main(String[] args) { int a = Common.AGE; System.out.println(a); Common.output(); } }
前面加入了導入語句,將Common類導入,使用其中的靜態成員變量和靜態方法時須要加上類名。app
下面咱們採用靜態導入方式:函數
(1)語法:性能
import static 包名.類名.靜態成員變量;學習
import static 包名.類名.靜態成員函數;
注意導入的是成員變量和方法名。
(2)改寫上面的例子
package study.javaenhance; import static study.javaenhance.util.Common.AGE; import static study.javaenhance.util.Common.output; public class StaticImport { public static void main(String[] args) { int a = AGE; System.out.println(a); output(); } }
或者:
package study.javaenhance; import static study.javaenhance.util.Common.*; public class StaticImport { public static void main(String[] args) { int a = AGE; System.out.println(a); output(); } }
注意點:
1.過分地使用靜態導入會在必定程度上下降代碼的可讀性。
2.若是靜態導入的多個類中存在重複的方法名稱或者成員變量名稱的時候,須要加上類名用於區分.
2、可變參數與重載覆寫
在JDK5.0 以前,咱們知道方法在接受參數的時候,其參數個數是固定的,但會存在某種場景,就該方法接受的某種類型的參數個數是不固定的,可能有1個參數,有2個參數,或者多個參數,但其內在功能是同樣的,那麼這個時候咱們一般採用的重載的方式來來實現,代碼舉例以下:
package study.javaenhance; public class VarableParameter { public static void main(String[] args) { System.out.println(add(1,2)); System.out.println(add(1,2,5)); } public static int add(int x,int y) { return x+y; } public static int add(int x,int y,int z) { return x + y +z; } }
能夠看到add 方法爲求參數的和,可是傳遞過來的參數有2個和3個,後面也有可能傳遞多個過來,那就須要不斷的重載,那麼有沒有辦法優化呢?那就是可變參數的用途:減小代碼量,方便輸入
採用可變參數方式解決:
package study.javaenhance; public class VarableParameter { public static void main(String[] args) { System.out.println(add(1,2)); System.out.println(add(1,2,5)); } public static int add(int ... args) { int sum = 0; for (int i : args) { sum += i; } return sum; } /* public static int add(int x,int y,int z) { return x + y +z; }*/ }
可變參數(Varargs)使程序員能夠聲明一個接受可變數目參數的方法。在調用可變參數的方法時,編譯器會爲該可變參數隱含建立一個數組,在方法體中以數組的形式訪問可變參數。本質上就是一個數組,對於某個聲明瞭可變參數的方法來講,咱們既能夠傳遞離散的值,也能夠傳遞數組對象。但若是將方法中的參數定義爲數組,那麼只能傳遞數組對象而不能傳遞離散的值。
注意點:
1.若是有多個參數,可變參數要定義在最後邊 ... 位於變量類型和變量名之間,先後有無空格均可以;
2.一個方法不可能具備兩個或兩個以上的可變參數。
上面提到了重載,所以這裏須要說明下重載和覆寫的區別:
重載(Overloading)
(1) 方法重載是讓類以統一的方式處理不一樣類型數據的一種手段。多個同名函數同時存在,具備不一樣的參數個數/類型。
重載Overloading是一個類中多態性的一種表現。
(2) Java的方法重載,就是在類中能夠建立多個方法,它們具備相同的名字,但具備不一樣的參數和不一樣的定義。
調用方法時經過傳遞給它們的不一樣參數個數和參數類型來決定具體使用哪一個方法, 這就是多態性。
(3) 重載的時候,方法名要同樣,可是參數類型和個數不同,返回值類型能夠相同也能夠不相同。沒法以返回型別做爲重載函數的區分標準。
重寫(Overriding)
(1) 父類與子類之間的多態性,對父類的函數進行從新定義。若是在子類中定義某方法與其父類有相同的名稱和參數,咱們說該方法被重寫 (Overriding)。在Java中,子類可繼承父類中的方法,而不須要從新編寫相同的方法。
但有時子類並不想原封不動地繼承父類的方法,而是想做必定的修改,這就須要採用方法的重寫。
方法重寫又稱方法覆蓋。
(2)若子類中的方法與父類中的某一方法具備相同的方法名、返回類型和參數表,則新方法將覆蓋原有的方法。
如需父類中原有的方法,可以使用super關鍵字,該關鍵字引用了當前類的父類。
(3)子類函數的訪問修飾權限不能少於父類的;
重寫方法的規則:
一、參數列表必須徹底與被重寫的方法相同,不然不能稱其爲重寫而是重載。
二、返回的類型必須一直與被重寫的方法的返回類型相同,不然不能稱其爲重寫而是重載。
三、訪問修飾符的限制必定要大於被重寫方法的訪問修飾符(public>protected>default>private)
四、重寫方法必定不能拋出新的檢查異常或者比被重寫方法申明更加寬泛的檢查型異常。例如:
父類的一個方法申明瞭一個檢查異常IOException,在重寫這個方法是就不能拋出Exception,只能拋出IOException的子類異常,能夠拋出非檢查異常。
而重載的規則:
一、必須具備不一樣的參數列表;
二、能夠有不責罵的返回類型,只要參數列表不一樣就能夠了;
三、能夠有不一樣的訪問修飾符;
四、能夠拋出不一樣的異常;
3、加強型for循環
For-Each循環也叫加強型的for循環,或者叫foreach循環。
其語法以下:
for(type element: array)
{
System.out.println(element);
}
直接看樣例:
package study.javaenhance; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class ForeachTest { public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5}; System.out.println("----------舊方式遍歷------------"); for(int i=0;i<arr.length;i++) { System.out.println(arr[i]); } System.out.println("---------新方式遍歷-------------"); //新式寫法,加強的for循環 for(int element:arr) { System.out.println(element); } System.out.println("---------遍歷二維數組-------------"); //遍歷二維數組 int[][] arr2 = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}} ; for(int[] row : arr2) { for(int element : row) { System.out.println(element); } } //以三種方式遍歷集合List List<String> list = new ArrayList<String>(); list.add("a"); list.add("b"); list.add("c"); System.out.println("----------方式1-----------"); //第一種方式,普通for循環 for(int i=0;i<list.size();i++) { System.out.println(list.get(i)); } System.out.println("----------方式2-----------"); //第二種方式,使用迭代器 for(Iterator<String> iter = list.iterator();iter.hasNext();) { System.out.println(iter.next()); } System.out.println("----------方式3-----------"); //第三種方式,使用加強型的for循環 for(String str: list) { System.out.println(str); } } }
4、基本數據的自動裝箱和拆箱
在這以前,咱們首先了解回顧下,JAVA種類型的劃分爲基本數據類和引用類型,其中基本數據類型爲:byte short int long float double char boolean;引用類型爲:數組 接口 類
那麼咱們本小節講的即爲基本數據類中的裝箱和拆箱。
自動裝箱:基本類型自動轉爲包裝類(int >> Integer)
自動拆箱:包裝類自動轉爲基本類型(Integer >> int)
包裝類是針對於原生數據類型的包裝。由於有8個原生數據類型,因此對應有8個包裝類。
全部的包裝類(8個)都位於java.lang下。
Java中的8個包裝類分別是:Byte, Short, Integer, Long, Float, Double, Character, Boolean,它們的使用方式都是同樣的,能夠實現原生數據類型與包裝類型的雙向轉換。
下面以主要Integer類爲例說明。
Integer類將int類型的值包裝到一個對象中。
Integer經過下面這個構造方法構造相應的整型數的對象:
public Integer(int value);
public static Integer valueOf(int i);
public int intValue()方法則返回這個包裝類所包裝的整型值。
舉例以下:
package study.javaenhance; import java.util.ArrayList; import java.util.Collection; public class AutoBox { public static void main(String[] args) { Integer iObj = 3; //自動將iObj 拆箱爲了int 類型,而後參與運算. System.out.println(iObj + 12); Collection<Integer> c = new ArrayList<Integer>(); c.add(3);//將int類型的3轉換爲Integer類型並放到集合當中 for (Integer item : c) { System.out.println(item); } } }
下面說一個知識點,看下面的程序:
package study.javaenhance; import java.util.ArrayList; import java.util.Collection; public class AutoBox { public static void main(String[] args) { Integer i1 = 100; Integer i2 = 100; System.out.println(i1 == i2); Integer i3 = Integer.valueOf(100); Integer i4 = Integer.valueOf(100); System.out.println(i3==i4); //2 Integer i5 = 130; Integer i6 = 130; System.out.println(i5 == i6); Integer i7 = Integer.valueOf(130); Integer i8 = Integer.valueOf(130); System.out.println(i7==i8); } }
上面的輸出結果爲:
true
true
false
false
爲何呢?
當兩個數都是100的時候==判斷相等,當兩個數都是130的時候判斷不相等。
查看內部實現代碼可知,Integer類有一個緩存,它會緩存-128~127之間的整數。
當調用valueOf的時候,不會生成新的對象,而是從緩存中取出對象。這樣能夠提升性能。
使用構造方法的時候確定會生成新的對象。
關於自動裝箱和拆箱知識擴充:
自動裝箱拆箱要點:
1.自動裝箱時編譯器調用valueOf將原始類型值轉換成對象,同時自動拆箱時,編譯器經過調用相似intValue(),doubleValue()這類的方法將對象轉換成原始類型值。
2.自動裝箱是將boolean值轉換成Boolean對象,byte值轉換成Byte對象,char轉換成Character對象,float值轉換成Float對象,int轉換成Integer,long轉換成Long,short轉換成Short,自動拆箱則是相反的操做。
什麼時候發生自動裝箱和拆箱
自動裝箱的弊端:
自動裝箱有一個問題,那就是在一個循環中進行自動裝箱操做的狀況,以下面的例子就會建立多餘的對象,影響程序的性能。
Integer sum = 0; for(int i=1000; i<5000; i++){ sum+=i; }
上面的代碼sum+=i能夠當作sum = sum + i,可是+這個操做符不適用於Integer對象,首先sum進行自動拆箱操做,進行數值相加操做,最後發生自動裝箱操做轉換成Integer對象。其內部變化以下
1.sum = sum.intValue() + i;
2.Integer sum = new Integer(result);
因爲咱們這裏聲明的sum爲Integer類型,在上面的循環中會建立將近4000個無用的Integer對象,在這樣龐大的循環中,會下降程序的性能而且加劇了垃圾回收的工做量。所以在咱們編程時,須要注意到這一點,正確地聲明變量類型,避免由於自動裝箱引發的性能問題。
重載與自動裝箱:
當重載趕上自動裝箱時,狀況會比較有些複雜,可能會讓人產生有些困惑。在1.5以前,value(int)和value(Integer)是徹底不相同的方法,開發者不會由於傳入是int仍是Integer調用哪一個方法困惑,可是因爲自動裝箱和拆箱的引入,處理重載方法時稍微有點複雜。一個典型的例子就是ArrayList的remove方法,它有remove(index)和remove(Object)兩種重載,咱們可能會有一點小小的困惑,其實這種困惑是能夠驗證並解開的,經過下面的例子咱們能夠看到,當出現這種狀況時,不會發生自動裝箱操做。
public void test(int num){ System.out.println("method with primitive argument"); } public void test(Integer num){ System.out.println("method with wrapper argument"); } //calling overloaded method AutoboxingTest autoTest = new AutoboxingTest(); int value = 3; autoTest.test(value); //no autoboxing Integer iValue = value; autoTest.test(iValue); //no autoboxing Output: method with primitive argument method with wrapper argument
要注意的事項:自動裝箱和拆箱可使代碼變得簡潔,可是其也存在一些問題和極端狀況下的問題,如下幾點須要咱們增強注意。
1.對象相等比較
這是一個比較容易出錯的地方,」==「能夠用於原始值進行比較,也能夠用於對象進行比較,當用於對象與對象之間比較時,比較的不是對象表明的值,而是檢查兩個對象是不是同一對象,這個比較過程當中沒有自動裝箱發生。進行對象值比較不該該使用」==「,而應該使用對象對應的equals方法。看一個能說明問題的例子。
public class AutoboxingTest { public static void main(String args[]) { // Example 1: == comparison pure primitive – no autoboxing int i1 = 1; int i2 = 1; System.out.println("i1==i2 : " + (i1 == i2)); // true // Example 2: equality operator mixing object and primitive Integer num1 = 1; // autoboxing int num2 = 1; System.out.println("num1 == num2 : " + (num1 == num2)); // true // Example 3: special case - arises due to autoboxing in Java Integer obj1 = 1; // autoboxing will call Integer.valueOf() Integer obj2 = 1; // same call to Integer.valueOf() will return same // cached Object System.out.println("obj1 == obj2 : " + (obj1 == obj2)); // true // Example 4: equality operator - pure object comparison Integer one = new Integer(1); // no autoboxing Integer anotherOne = new Integer(1); System.out.println("one == anotherOne : " + (one == anotherOne)); // false } }
Output:
i1==i2 : true
num1 == num2 : true
obj1 == obj2 : true
one == anotherOne : false
值得注意的是第三個小例子,這是一種極端狀況。obj1和obj2的初始化都發生了自動裝箱操做。可是處於節省內存的考慮,JVM會緩存-128到127的Integer對象。由於obj1和obj2其實是同一個對象。因此使用」==「比較返回true。
2.容易混亂的對象和原始數據值
另外一個須要避免的問題就是混亂使用對象和原始數據值,一個具體的例子就是當咱們在一個原始數據值與一個對象進行比較時,若是這個對象沒有進行初始化或者爲Null,在自動拆箱過程當中obj.xxxValue,會拋出NullPointerException,以下面的代碼
private static Integer count; //NullPointerException on unboxing if( count <= 0){ System.out.println("Count is not started yet"); }
這一點須要特別注意,我以前就犯過這個錯誤。
3.緩存的對象
這個問題就是咱們上面提到的極端狀況,在Java中,會對-128到127的Integer對象進行緩存,當建立新的Integer對象時,若是符合這個這個範圍,而且已有存在的相同值的對象,則返回這個對象,不然建立新的Integer對象。在Java中另外一個節省內存的例子就是字符串常量池。