(1)面向對象編程有三個特徵,即封裝、繼承和多態。
封裝隱藏了類的內部實現機制,從而可以在不影響使用者的前提下改變類的內部結構,同時保護了數據。
繼承是爲了重用父類代碼,同時爲實現多態性作準備。那麼什麼是多態呢?
方法的重寫、重載與動態連接構成多態性。Java之所以引入多態的概念,原因之一是它在類的繼承問題上和C++不同,後者允許多繼承,這確 實給其帶來的非常強大的功能,但是複雜的繼承關係也給C++開發者帶來了更大的麻煩,爲了規避風險,Java只允許單繼承,派生類與基類間有IS-A的關 系(即“貓”is a “動物”)。這樣做雖然保證了繼承關係的簡單明瞭,但是勢必在功能上有很大的限制,所以,Java引入了多態性的概念以彌補這點的不足,此外,抽象類和接口也是解決單繼承規定限制的重要手段。同時,多態也是面向對象編程的精髓所在。
要理解多態性,首先要知道什麼是“向上轉型”。
我定義了一個子類Cat,它繼承了Animal類,那麼後者就是前者是父類。我可以通過Cat cat = new Cat();
實例化一個Cat的對象,這個不難理解。但當我這樣定義時:Animal an = new Cat(); 這代表什麼意思呢?
很簡單,它表示我定義了一個Animal類型的引用,指向新建的Cat類型的對象。由於Cat是繼承自它的父類Animal,所以 Animal類型的引用是可以指向Cat類型的對象的。那麼這樣做有什麼意義呢?因爲子類是對父類的一個改進和擴充,所以一般子類在功能上較父類更強大, 屬性較父類更獨特,定義一個父類類型的引用指向一個子類的對象既可以使用子類強大的功能,又可以抽取父類的共性。所以,父類類型的引用可以調用父類中定義 的所有屬性和方法,而對於子類中定義而父類中沒有的方法,它是無可奈何的;同時,父類中的一個方法只有在在父類中定義而在子類中沒有重寫的情況下,纔可以 被父類類型的引用調用;對於父類中定義的方法,如果子類中重寫了該方法,那麼父類類型的引用將會調用子類中的這個方法,這就是動態連接。
對於多態,可以總結它爲:
一、使用父類類型的引用指向子類的對象;
二、該引用只能調用父類中定義的方法和變量;
三、如果子類中重寫了父類中的一個方法,那麼在調用這個方法的時候,將會調用子類中的這個方法;(動態連接、動態調用)
四、變量不能被重寫(覆蓋),”重寫“的概念只針對方法。
3、 static 有那些特點和使用的“侷限”?
1、 static在java中到底代表什麼,爲何要用它?
static――靜態――“指定位置“
首先,我們來看看java的內存:java把內存分爲棧內存和堆內存,棧內存用來存放一些基本類型的變量和數組及對象的引用變量,而堆內存主要是來放置對象的。
用static的修飾的變量和方法,實際上是指定了這些變量和方法在內存中的“固定位置”-static storage。既然要有“固定位置”那麼他們的“大小”似乎就是固定的了,有了固定位置和固定大小的特徵了,在棧中或堆中開闢空間那就是非常的方便了。如果靜態的變量或方法在不出其作用域的情況下,其引用句柄是不會發生改變的。
我們常看到:static變量有點類似於C中的全局變量的概念;靜態表示的是內存的共享,就是它的每一個實例都指向同一個內存地址。把static拿來,就是告訴JVM它是靜態的,它的引用(含間接引用)都是指向同一個位置,在那個地方,你把它改了,它就不會變成原樣,你把它清理了,它就不會回來了。我們常可看到類似以下的例子來說明這個問題:
class Student{
static int numberOfStudents=0;
Student()
{
numberOfStudents++;
}
}每一次創建一個新的Student實例時,成員numberOfStudents都會不斷的遞增,並且所有的Student實例都訪問同一個numberOfStudents變量,實際上int numberOfStudents變量在內存中只存儲在一個位置上。多個實例共享一個變量似乎不足以讓我們對static那麼的熱情,實際上java引入static卻有另外的含義:
(1)、引用static的方法和變量,不需要和實例捆綁在一起,這可以提高代碼的編寫的效率,這樣的例子我們隨處可見;
(2)、java的主類中main()方法本身就是一個static的,所以main方法的執行就是在沒有產生新的實例的情況;對於靜態的東西,JVM在加載類時,就在內存中開闢了這些靜態的空間。
(4)、使用一種靜態的方法的編程通常叫做防禦(defensive)編程,它可以在API供應商突然中斷支持的情況下保護代碼
2、 static在java中怎麼用?
使用static時,要記着我闡述的static代表什麼。
static使用非常的簡單,如果要修飾一個靜態塊只需:staic {……..}即可(常用靜態塊來初始化一些變量); 靜態方法就參照main()的形式:訪問標識 static returnType method(…) {};靜態變量就是:static type fields;
在使用靜態的方法時,可以直接用類名來引用,不需要創建實例(當然創建實例也是可以的),例如,System.out,String.valueOf()等等。
3、 static 有那些特點和使用的“侷限”?
從上面的分析可知,static的東西在類加載時,就分配了內存空間,即編譯時就爲這些成員變量的實例分配了空間。
那麼在static塊內和方法體內,我們能給它放一個在內存中還沒有着落的變量?顯然與我們先前所說的相左。static的東西,人家是在static storage中有“指定位置“的,如果我們茫然的在static的作用域中放置一個普通的變量,那麼編譯時JVM就毫不客氣的給你個異常:
non-static variable a cannot be referenced from a static context或non-static method Test() cannot be referenced from a static context(注:Test()是我試驗時的一個例子),除非我在static中現場開闢空間,用new來要內存。
對於static的初始化問題,我們還是值得討論的。現看下面的例子:
//StaticInit show the static decorated initialization
package com.blogchina.qb2049;
public class StaticInit
{
static int i;
int a;
public StaticInit()
{
a=6;
System.out.println("a 的初始化"+a);
}
public static void main(String[] args)
{
new StaticInit();
}
static
{
i=5;
System.out.println("i 的初始化"+i);
}
}運行結果如下:i 的初始化5 a 的初始化6 靜態塊的初始化要早於非靜態的,原因就是在於這些東西是在類裝載時就開始初始化了。說起static的“侷限“,總結起來就是:在static的方法中僅能夠調用其他的static方法和static變量;在static方法中不能以任何方式引用this或super;static變量在定義時必須進行初始化,並且初始化的時間早於非靜態。還有一個侷限我需要具體的說明一下,static的變量的初始化僅能一次,如下例:
//Static.java, initialize only one
class T1
{
static int t=1;
T1(int b)
{
t=b;
}
}
public class Static
{
T1 t1=new T1(2);
T1 t2=new T1(3);