方法重載(overload)參數爲基本類型的狀況 java
方法重載時若是一系列名字相同的方法接受的參數的類型是不同的基本類型,那麼重載方法的選擇規則是,將接收的參數一級一級向上提高,直到找到合適的方法爲止,不然編譯出錯。 c++
public static void main(String args[]){ char c ='a'; new Test2().f1(c);//int } public void f1(byte c){ System.out.println("byte"); } public void f1(int c){ System.out.println("int"); } public void f1(float c){ System.out.println("float"); } public void f1(double c){ System.out.println("char"); } public void f1(long c){ System.out.println("long"); }上面這個例子,char向上提高最近的一個是int,因此選擇f1(int)方法。
public static void main(String args[]){ double d = 10.34; new Test2().f1((float)d); } public void f1(byte c){ System.out.println("byte"); } public void f1(int c){ System.out.println("int"); } public void f1(float c){ System.out.println("float"); }上面這個例子,必須將double強制轉換成下面的類型,不然編譯出錯,由於找不到合適的方法。
java的構造函數 程序員
java中若是咱們寫一個類時沒有寫構造函數,則java編譯器會提供一個默認的無參數的構造函數。 數組
若是咱們提供了構造函數,則不會有默認的,這時若是咱們還想要默認的就必須顯示的寫進去。 app
java中的this jvm
java中的this指向當前的對象,在對象內部的方法中調用其它內部方法時不須要使用this,這個時候儘可能不要加上this,有個原則:爲了程序簡潔易讀能不寫this就不要寫。 函數
this還能夠用於在一個構造函數中調用同個類的另一個構造函數。 this
public class Test3 { private int i; private String s; public Test3(int iv){ i = iv; } public Test3(String sv){ s =sv; } public Test3(String sv,int iv){ this(sv); // this(iv);//不能調用兩次 } public Test3(){ int j=10; // this(j);//必須是第一句 } public void go(){ // this(4);//不在構造函數內 } }從上面這個例子能夠看出:
1. 使用this方式調用構造函數只能出如今構造函數中 lua
2. 使用this方式調用構造函數必須出如今構造函數的第一句,而不能出如今後面。 spa
3. 在一個構造函數中經過this調用另外的構造函數只能調用一次,若是調用屢次會編譯出錯。
java中的static
java中的static方法中不能調用非static的方法和變量,可是若是傳個引用的參數過來或者在方法中new一個對象獲得引用,就能夠。
public class Test4 { public static void testStatic(SubTest st){//傳個引用 st.nomal(); } public static void testStatic(){ SubTest st = new SubTest();//new個對象 st.nomal(); } } class SubTest{ public void nomal(){ } }java中的finalize()和垃圾回收
當java的垃圾回收器決定回收堆內存的時候,首先會調用要回收的對象的finalize方法,接着纔開始回收堆內存。
java中全部的對象自己都在堆上,垃圾回收器能夠回收它們的內存,爲何還要有finalize方法呢,在回收方面finalize惟一可能的用途就是當java使用native方法調用C或C++代碼的時候,C或C++代碼中的malloc會在堆中分配內存,這個時候必須在finalize中調用一個native方法,這個native方法中會經過free去釋放。
finalize和c++中的析構函數是不同的。
C++中對象內存分配會有兩種狀況,一種是經過相似於聲明的方式定義的對象,這種對象分配在棧上,會在對象的做用域結束的時候調用對象的析構函數,還有一種是使用new定義的對象,這種對象分配在堆上,必須經過程序員寫出delete代碼來銷燬。總之能保證對象空間都被實時回收。
而java的finalize和垃圾回收並不必定會執行,垃圾回收只有在垃圾回收器以爲內存快不夠的時候纔會回收,若是程序結束時系統內存還不少,那麼垃圾回收器不會進行回收工做,程序結束後對象的內存雖然獲得釋放,可是finalize就沒有機會執行了。
咱們可使用System.gc()來建議垃圾回收器進行回收,但僅僅是建議,垃圾回收器並不必定會回收。
使用finalize除了用於調用native方法回收C和C++的對象內存還能夠進行終止狀態驗證,有的時候咱們要求對象在結束時某個狀態必定得是某個值,爲了檢驗防止出錯,能夠寫在finalize中,在finalize中進行驗證,雖然執行一次finalize不必定會執行,可是程序運行的次數增長後總會執行finalize,這個時候若是狀態有錯就能檢測到。
java的垃圾回收器是如何工做的
java在堆上分配對象速度很快,比C++在堆上分配對象速度快得多,接近於C++在棧上分配對象的速度。
java之因此在堆上分配內存速度快,是由於垃圾回收器的存在。垃圾回收器一邊回收一邊緊縮堆中的對象。
垃圾回收器可能的工做方式有:
1.reference counting
缺點是速度慢,而且若是有環狀的引用鏈,雖然引用數目不是0,可是仍是應該回收,這個時候會出問題。這個方法用的很少,幾乎沒有回收器會使用。
2.任何存活的堆對象均可以追溯到棧中或靜態存儲區域中的某個引用,這個鏈可能通過幾層對象,但最終必定能找到。所以順着棧中或靜態存儲區中的引用縱向尋找,就會找到全部存活的對象,而剩下的就能夠當垃圾處理了。對於環狀引用的狀況,這種機制找不到,能夠正確的看成垃圾來處理。
對於第二種方案,jvm使用自適應性的垃圾回收方案,使用stop-and-copy和mark-and-sweep交叉的方式來回收。
stop-and-copy指的是當回收時,首先中止程序的運行,而後將存活的堆對象複製到新的堆中,留在原來的堆中的對象就都是垃圾對象,能夠被回收,而新堆中的對象是緊密排列的,至關於作了壓縮。這種方法有兩點缺點,一是要維護兩個堆,浪費一倍空間,爲了解決這一問題好多jvm以恰好知足須要的大小的塊爲單位來分配堆。第二就是當堆內存逐漸趨於穩定,沒有什麼大的垃圾產生時,複製進程仍然會將一個堆中的對象拷貝到另外一個堆中,這樣浪費了空間和時間。
這個時候就須要mark-and-sweep方法了,早期的jvm版本一直使用mark-and-sweep方法,這種方法對廣泛狀況來講要慢一些,可是當堆趨於穩定的時候要快一些。這種方法也是從堆中或靜態存儲區中的引用開始尋找,每當找到一個存活對象就作一個標記,當整個遍歷過程結束以後便開始清理。
stop-and-copy和mark-and-sweep都須要先中止程序再開始清理,而不是在後臺執行清理工做。
如前文所述,在這裏所討論的Java虛擬機中,內存分配以較大的「塊」爲單位。若是對象較大,它會佔用單獨的塊。嚴格來講,「中止-複製」要求在釋放舊有對象以前,必須先把全部存活對象從舊堆複製到新堆,這將致使大量內存複製行爲。有了塊以後,垃圾回收器在回收的時候就能夠往未被使用的塊裏拷貝對象了。每一個塊都用相應的代數(generation count)來記錄它是否還存活。一般,若是塊在某處被引用,其代數會增長。垃圾回收器將對上次回收動做以後新分配的塊進行整理,這對處理大量短命的臨時對象頗有幫助。垃圾回收器會按期進行完整的清理動做——大型對象仍然不會被複制(只是其代數會增長),內含小型對象的那些塊則被複制並整理。
jvm會監視垃圾回收的效率,若是當前堆趨於穩定不怎麼產生垃圾,則轉向mark-and-sweep策略,若是堆變得瑣碎,有許多垃圾產生,則轉向stop-and-copy策略,所以說是自適應的。
JIT編譯器
JIT編譯器部分或所有將程序轉化成本地機器代碼,這樣就不須要被JVM解釋執行,而是直接被機器執行,從而加快了運行速度。
使用JIT編譯器一種方案就是使用JIT編譯器編譯全部的代碼。這樣有兩個缺點:一是增長了編譯時間,二是增長了可執行代碼的空間。
另外一種方案是lazy evaluation,當運行時第一次建立某個類的實例的時候,那個類會被加載,class文件被讀取,字節碼被加載到內存中,只有在代碼運行的時候纔會JIT編譯。這樣不會被執行的代碼永遠不會JIT編譯。
java成員初始化
Java能夠保證任何變量在使用前必定會被初始化,若是有方法中的局部變量沒有初始化就使用則編譯器會報錯。
對於類的成員變量,若是是基礎類型,則初始化爲0及0對應的值,好比boolean爲false,char爲空格,以此類推;若是是引用,則初始化爲null。
這和C++是不一樣的,C++對於基礎類型,若是局部變量沒有初始化則提供一個隨機值,若是全局變量沒初始化則提供默認值0,若是成員變量沒初始化則要看類的位置,若是是全局變量的類則賦默認值0,若是是局部或類的成員則賦隨機值。對於類,不論在局部變量仍是全局變量仍是成員變量都會調用無參數的構造函數初始化,若是沒有無參數的構造函數則會編譯錯誤,若是無參數的構造函數沒有對類中某個成員變量初始化,若是這個類是全局變量則那個變量會被賦默認的0,若是那個類是局部的或另外一個類的成員,則那個變量會賦隨機值。
java中類的成員變量能夠在定義給出初始化的值,這在C++中會編譯出錯:
public class InitialValues2{ boolean bool = true; char ch = 'x'; byte b = 47; Student stu = new Student(); ... }也能夠用方法來初始化:
public class InitialValues3{ public int i = f(); public int f(){ return 11; } }用於初始化的方法能夠有參數,可是不能傳遞給這些方法還沒有定義的類的成員變量做爲參數:
public class InitialValues4{ public int i = f(j);//錯誤 int j = 10; public int m = f(j);//正確 public int f(int j){ return j*10; } }在構造函數中初始化時,類的成員變量首先會默認初始化爲0及0對應的值,接着再被初始化爲構造函數中給的值。
public class Counter{ int i; public Counter(){ i=7; } }對於上面這個例子,i首先初始化爲0,接着初始化爲7。
初始化的順序
在一個類中,變量初始化的順序取決於它們在類中定義的前後順序,變量的定義能夠分佈在類的各個地方,包括在兩個方法定義之間,成員變量老是會在全部方法被調用以前初始化,包括構造方法。
public class Test7 { public static void main(String[] args) { B b = new B(); } } class A{ A(){ System.out.println("A"); } } class B{ A a1 = new A(); B(){ System.out.println("B"); } A a2 = new A(); }上面的例子輸出A,A,B,說明a1,a2在B的構造方法被調用以前初始化。
static數據的初始化
static關鍵字不能用於局部變量,所以只能用於成員變量。若是你不初始化一個static的成員變量,若是它是基礎類型則獲得標準初始值,若是是引用則初始化爲null。
初始化的順序:static的先初始化,若是它們沒有由於以前的對象建立而初始化,而後是非靜態的對象初始化。
public class Test8 { public static void main(String[] args) { new C(); new C(); } } class A1 { public A1(String s){ System.out.println("A1:"+s); } } class A2 { public A2(String s){ System.out.println("A2:"+s); } } class C{ public static A1 a1 = new A1("a1"); public A1 a2 = new A1("a2"); public static A2 a3 = new A2("a3"); }上面的例子輸出:A1:a1,A2:a3,A1:a2,A1:a2。可見static的a1和a3先被初始化,而後是a2,在第二個C建立的時候,static的a1和a3已經被初始化過了,所以不用再初始化,只有a2再被初始化。
建立對象的過程
1.雖然構造方法沒有標識爲static的,但其實是static的。以Dog對象爲例,當一個對象被建立時或者一個Dog類的靜態的屬性或方法被訪問時,java解釋器必須定位Dog.class。
2.Dog.class被加載,它全部的靜態部分進行初始化。所以static的屬性在類第一次被加載時初始化。
3.當new一個Dog時,Dog的構造進程首先在堆上爲Dog分配足夠的空間。
4.這個空間初始化爲0,至關於對象的變量默認初始化爲0及對應值。
5.在字段定義時顯示的初始化被執行。
6.構造器鏈被執行。
顯示的static初始化代碼段
public class Spoon{ static int i; static{ i = 47; } }
和靜態變量初始化同樣,static初始化塊中的語句只有在所在類被建立或所在類中靜態屬性或靜態方法被訪問的時候纔會執行,只在最初訪問時執行一次。
非靜態的實例初始化代碼段
java提供了和static初始化代碼段相似的非static初始化代碼段,用來初始化非靜態變量。
public class Test16 { { i = 10; j = 20; } int i,j; }這種代碼使得不管哪一個構造方法被調用,實例的變量都能在構造方法執行以前初始化。
static和非static的代碼初始化段均可以寫在變量聲明的前面,就像上面Test16那樣。
java的Array
java的Array定義的方式:int[] a1;或int a1[]。
這兩種定義方式都只是爲一個指向數組的引用分配了空間,而且肯定這個數組只能存放int,沒有真正的初始化數組。
能夠經過以下方式初始化數組:
int[] a1 = {1,2,3,4,5};
int a2 = a1;//a2與a1指向同一個數組
若是能夠肯定長度,則使用int[] a = new int[20];來初始化,這樣的初始化若是是基礎類型則默認初始化爲0及對應值,若是是引用則初始化爲null。
Integer[] ia = new Integer[10]; System.out.println(ia[0]);//null對於上面的例子,當初始化非基礎類型的數組時,只是初始化了一個引用的數組,而且引用的值都是null。
經過autobox機制,能夠給ia放入int,例如:
Integer[] ia = new Integer[10]; ia[0] = new Integer(0); ia[1] = 1;//autoboxing System.out.println(ia[0]); System.out.println(ia[1]);
對於非基礎類型的對象的數組,也能夠用大括號的方式初始化,以下:
Integer[] a = { new Integer(1), new Integer(2), 3//autoboxing }; System.out.println(Arrays.toString(a));//[1,2,3] Integer[] b = new Integer[]{ new Integer(1), new Integer(2), 3//autoboxing }; System.out.println(Arrays.toString(b));//[1,2,3]
大括號初始化方式的例子:
public class Test20 { public static void main(String args[]){ Test21.main(new String[]{"abc","123","abc123"}); } } class Test21{ public static void main(String args[]){ System.out.println(Arrays.toString(args));//[abc, 123, abc123] }
Arrays.toString格式化數組
java.util包中的Arrays的toString方法能夠將一維數組格式化爲先後中括號,中間逗號分隔的形式。
int[] a = new int[20]; for(int i=0;i<a.length;i++){ a[i] = i; } System.out.println(Arrays.toString(a));//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]java的可變參數列表
public static void main(String[] args) { // TODO Auto-generated method stub testVarargs(1,2,3);//能夠傳逗號分隔的 ArrayList<Integer> list = new ArrayList<Integer>(); list.add(1); list.add(2); list.add(3); //testVarargs(list);//不能傳list testVarargs(new Integer[]{1,2,3});//能夠傳數組 } public static void testVarargs(Integer ... iv){ for(int i:iv){ System.out.print(i); } System.out.println(); }
注意:若是方法參數要求可變參數列表,則能夠傳逗號分隔的,也能夠傳數組;但若是方法參數要求數組,則只能傳數組,不能穿逗號分隔的。
可變參數列表方法的重載
public static void main(String[] args) { // TODO Auto-generated method stub f('a','b');//first //f();wrong f(1,2);//second //f('a',1);The method f(Character...) in the type Temp11 is not applicable for the arguments (char, int) //f(1,'a');同上 //f('a',new Integer(1));基本同上 //f(new Integer(1),'a');基本同上 } public static void f(Character... chs){ System.out.println("first"); } public static void f(Integer ... is){ System.out.println("second"); }從上面代碼看出,重載仍是找類型最接近的,f()有歧義,所以編譯不經過。至於最後幾個不經過,好比f('a',1),是由於雖然1能夠autobox成Integer,可是'a'不能轉成Integer,因此不能按照second f,1不能自動轉Character,因此不能按照first f。若是把second f的參數改爲int,則只有f('a','b')和f()編譯不經過,f('a','b')不經過是由於'a'既能autobox成Character又能自動轉成int,於是有歧義。若是把first f改爲char,則 f('a','b')不會編譯出錯,由於找到了最優匹配。
public static void main(String[] args) { // TODO Auto-generated method stub f(1,'a');//first //f('a','b');The method f(float, Character[]) is ambiguous for the type Test12 } public static void f(float i,Character ... args){ System.out.println("first"); } public static void f(Character ... args){ System.out.println("second"); }上面這個例子,f(1,'a')之因此選擇first是由於1能夠自動轉成float而不能自動轉成Character,若是float改爲Float則編譯出錯,由於1不能自動轉Float和Character。f('a','b')出錯的緣由是既能夠autobox成Character又能夠自動轉float,出現歧義。
總之要注意:
1.函數重載老是找最匹配的。
2.基礎類型只能包裝成對應的那個類,不能轉成別的包裝類型,當autobox與自動向上轉換共存時,就會出現歧義。
java數組類型的強制轉換
java數組類型的強制轉換方法以下:
Integer[] ia = new Integer[10]; Object[] da = (Object[])ia;只有有繼承關係的數組才能夠強制轉換,Integer數組不能轉成Double數組。
System.out.println打印某個類的實例
默認狀況下,使用System.out.println打印某個類的實例時,會輸出實例的類名+@+十六進制表示的實例的地址。
若是本身實現覆蓋了Object的toString()方法,則會打印toString()放回的字符串。
Object的getClass方法
Object的getClass方法返回實例對應的類,打印那個類將返回對應的類的名字及相關信息。
System.out.println(t14.getClass());//class test.Test14 int[] a = {1,2,3}; System.out.println(a.getClass());//class [I
對於最後一個,[表示是後面類型的數組,I表示基礎類型int。
main函數的兩種寫法
1.public static void main(String[] args)
2.public static void main(String ... args)
枚舉類型
當咱們建立一個枚舉類型時,編譯器會自動幫咱們產生三個方法:toString()返回枚舉類型的實例的名字,ordinal()放回枚舉類型某個實例值的順序,static values()按順序返回枚舉類型全部的值的數組。
enum常常和switch一塊兒用,更有意義。
例子:
public class Test16 { public static void main(String[] args) { // TODO Auto-generated method stub State active = State.ACTIVE; System.out.println(active);//調用toString()輸出ACTIVE for(State state:State.values()){ System.out.println(state+" order is "+state.ordinal()); /* * REGISTERED order is 0 ACTIVE order is 1 BINDED order is 2 LOCKED order is 3 */ } switch(active){ case REGISTERED: //... break; case ACTIVE: //... break; case BINDED: //... break; case LOCKED: //... break; default: //.. break; } } } enum State{ REGISTERED,ACTIVE,BINDED,LOCKED }