JAVA學習周記(三)

1、繼承

繼承就是在已經(jīng)存在的類的基礎上再進行擴展,從而產(chǎn)生新的類。已經(jīng)存在的類稱為父類、超類或基類,而新產(chǎn)生的類稱為子類或派生類。

繼承所表達的就是對象類之間的相交關系,它使得某類對象可以繼承另外一類對象的數(shù)據(jù)成員和成員方法。若類B繼承類A,則屬于B的對象便具有類A的全部或部分性質(zhì)(數(shù)據(jù)屬性)功能(操作),我們稱被繼承的類A為基類、父類或超類,而稱繼承類B為A的派生類或子類。

代碼試例:汽車類{

? ? ?汽車有四個輪子屬性

? ? ? 引擎屬性

? ? ? ?方向盤屬性

如何駕駛()? ? ? ?//方法函數(shù)

如何保養(yǎng)()? ? ? ?//方法函數(shù)

}

寶馬廠家的敞篷跑車繼承汽車類

{

如何打開敞篷? ? ? ? ? //方法函數(shù)

}

繼承的類型

繼承可以分為單繼承和多繼承。單繼承是指一個子類最多只能有一個父類。多繼承是一個子類可以有倆個以上的父類。Java語言中的類只支持單繼承,多繼承是通過接口來間接實現(xiàn)的。

派生類

Java中類的繼承是通過extends來修飾的,通過extends的關鍵字表明前者具備后者的公共的成員變量和方法,再具備了所有公共的成員變量和方法后,本身還能定義一些特有的成員變量和方法?;菊Z法如下:

訪問控制符? [修飾符]? class? 類名? extends? 父類名{

.........

}

例題:定義一個學生類Student,繼承自person類。

定義父類 person 如下:

public class person{

private String name;

private int age;

public void setName(String name){

this.name=name;

}

public String getName(){

return name;

}

public void setAge(String age){

this.age=age;

}

public int getAge(){

return Age;

}

}

定義子類 Student 類如下:

public class Student extends Person{

private String school;

public void setSchool(String school){

this.school=school;

}

public String getSchool(){

return school;

}

public static void main(String[] args){

Student st=new student();

st.setName("奚佳欣");

st.setAge(20);

st.stSchool("西安歐亞學院");

systen.out.println("姓名:? "+st.getName()+"? 年齡:? "+st.getAge()+"? 學校:? "+st.getSchool());

? }

}

以上程序中的Student類擴充了Person類,增加了學校的屬性及對應的setter和getter方法,由于Person類中已經(jīng)存在name和age倆個屬性,也就是說此時student類中已經(jīng)存在了3個屬性及3組setter和getter方法,所以在Student類中不需要重新聲明著倆個屬性,這時就需要考慮是否可以將person類中的內(nèi)容繼續(xù)保留到Student類中,也就是繼承。

使用super調(diào)用父類的構造方法

子類如果想使用父類的構造方法,必須在子類的構造方法中使用,并且必須使用關鍵字super來表示,而且super必須是子類構造方法中第一個語句。

例題:修改上個例題,分別加入帶參數(shù)的構造函數(shù)

定義父類Person1類:

public class Peson1{

private String name;

private int age;

public Personq(String name ,int age){

this.name = name;

this.age = age;

System.out.println("我是"+this.name);

System.out.println("我的年齡是"+this.age);

? ? ? }

}

定義子類Student1類:

public class student1 extends Person1{

private String school;

public Student(String name, int age,String school){

super("奚佳欣",20);

this.school = school;

system.out.println("我來自"+this.school);

}

public ?static?vold? main(String[] ages){

Students st=new Student1("奚佳欣",20,"西安歐亞學院");

}

}

super用法總結:

1.在子類構造方法中要調(diào)用父類的構造方法,用"super(參數(shù)列表)"的方式調(diào)用,參數(shù)不是必須的。同時還要注意的一點是:"super(參數(shù)列表)"這條語句只能用在字參數(shù)構造方法中的第一行。

2.當子類方法中的局部變量或者子類的成員變量與父類變量同名是,也就是子類局部變量父類成員變量事,用“super.成員變量名”來引用父類成員變量。當然,如果父類的成員變量沒有被覆蓋,也可以用“super.成員變量名”來引用父類成員變量,但這樣是不必要的。

3.當子類的成員方法覆蓋了父類的成員方法時,也就是子類和父類有完全相同的方法定義(但方法可以不同),此時,用“super.方法名(參數(shù)列表)”的方式訪問父類的方法。

4.super關鍵字只能用在類體中非靜態(tài)部分,如構造函數(shù)與成員方法中,若在main()函數(shù)中調(diào)用或用在靜態(tài)方法中則會編譯出錯,報出Cannot use super in a static context 的錯誤。

泛型數(shù)組

1、Java的數(shù)組允許在運行時確定數(shù)組的大小,但要改變數(shù)組的大小就不容易了,解決辦法是使用ArrayList類,它是一個采用類型參數(shù)的泛型類,如:ArrayList<Employee>,尖括號里的是類名,表示這個數(shù)組列表類里存儲的元素是Employee類的對象,如下聲明一個保存Employee對象的數(shù)組列表:

ArrayList<Employee> staff = new ArrayList<Employee>();

2、操作數(shù)組列表

① 使用add方法可以將元素添加到數(shù)組列表中,如:staff.add(new Employee(“Tom”,...))會添加元素到末尾,如:staff.add(i, e)會添加元素到第i個位置,后面的元素向后移一位

② 使用remove方法刪除一個元素,如:

1)staff.remove(1) //表示刪除標號為1的元素,并返回被刪除的那個元素

2)staff.remove(e) //其中e是一個對象的引用,如果在列表中找到e對象,刪除它并返回true,找不到則返回false

③ 可以使用for each循環(huán)對數(shù)組列表遍歷,如:for(Employee e : staff){...}

④ 如果已經(jīng)清楚或能夠估計出數(shù)組可能存儲的元素數(shù)量,就可以在填充數(shù)組之前調(diào)用ensureCapacity方法,如:staff.ensureCapacity(100)或ArrayList<Employee> staff = new ArrayList<Employee>(100)

—— 這個方法和數(shù)組的大小有很重要的區(qū)別:如果數(shù)組分配100個元素的存儲空間,數(shù)組就有100個空位置可以使用,而容量為100個元素的數(shù)組列表只是擁有保存100個元素的潛力,在最初,甚至完成初始化構造之后,數(shù)組列表根本就不含有任何元素,此時 數(shù)組列表.size() 等于0

⑤ size方法將返回數(shù)組列表中包含的實際元素數(shù)目,如:staff.size()

⑥ 一旦能夠確定數(shù)組列表的大小不再發(fā)生改變,就可以調(diào)用trimToSize方法,這個方法將存儲區(qū)域的大小調(diào)整為當前元素數(shù)量所需要的存儲空間數(shù)目,垃圾回收器將回收多余的存儲空間

—— 一旦整理了數(shù)組列表的大小,添加新元素就需要花時間再次移動存儲塊,所以應該在確認不會再添加任何元素時再調(diào)用trimToSize

⑦ 對于數(shù)組列表變量,如a和b,對于語句:a = b,表示讓a和b引用同一個數(shù)組列表,并沒有發(fā)生列表里元素的復制,這點和C++不同

3、訪問數(shù)組列表元素

① 要設置第i個元素(注:數(shù)組列表的下標從0開始),可以:staff.set(i, harry)

—— 只有i小于數(shù)組列表大小時才能調(diào)用list.set(i,x),例如初始化了一個有存儲100個元素潛力的數(shù)組列表,此時它的大小為0,所以list.set(0, x)這句是錯誤的,應使用add方法為數(shù)組列表添加新元素,而不要使用set方法,它只負責替換數(shù)組中已經(jīng)存在的元素內(nèi)容

② 要獲得第i個元素,可以:staff.get(i)

③ 想要像數(shù)組那樣訪問數(shù)組列表元素,可以使用toArray方法將數(shù)組列表元素拷貝到一個數(shù)組中:list.toArray(a)

對象包裝器與自動打包

1、Java為所有的基本類型提供了相對應的類,稱為包裝器(Integer、Long、Float、Double、Short、Byte、Character、Void、Boolean(前六個派生于公共的超類Number)),包裝器是不可變的,不允許更改包裝在其中的值,包裝器還是final的,因此不能定義它們的子類,如對于數(shù)組列表,這樣寫是不允許的:ArrayList<int>,需要寫成:ArrayList<Integer> list = new ArrayList<Integer>()

—— ArrayList<Integer>的效率遠遠低于int[]數(shù)組,所以此時數(shù)組列表更適合構造小型集合,因為這樣操作起來更方便

2、自動打包

① 如果把int變量賦給Integer對象,Java會自動打包,如:list.add(3) 會自動換成 list.add(new Integer(3))

② 如果把一個Integer對象賦給int值時,會自動拆包,如:Integer n = 3; n++;等會自動拆開對象包,然后進行計算,最后將結果存入對象包內(nèi)

—— 注意應避免使用 == 符合來判斷兩個包裝器對象是否相等,因為這有可能是檢測的是對象是否指向同一個存儲區(qū)域,應該使用equals方法來進行比較

—— 打包和拆包是編譯器認可的,而不是虛擬機

參數(shù)數(shù)量可變的方法

使用“...”符號來定義可變參數(shù),如printf方法的定義是:public PrintStream printf(String fmt, Object... args){ },用戶自己也可以定義可變參數(shù)方法,并將參數(shù)指定為任意類型,甚至是基本類型,如下面的代碼計算若干個數(shù)值的最大值:

public static double max(double... values){

double largest = Double.MIN_VALUE;

for(double v : values)

if(v > largest) largest = v;

return largest;

}

—— 注意,雖然“...”符號在運行程序時類似于把參數(shù)變?yōu)閿?shù)組,但并不等價

① 如上面的max方法的參數(shù)是“double[] d”,那么在調(diào)用max方法應該這樣:double d = new double[]{1.0, 2.0}; max(d);或者max(new double[]{1.0, 2.2, 4,3});

② 如果max的參數(shù)是“double... values”,那么調(diào)用max方法應該這樣:max(1.2, 1.3, 2.3);

枚舉類

枚舉類型(enum type)是指由一組固定的常量組成合法的類型。Java中由關鍵字enum來定義一個枚舉類型。下面就是java枚舉類型的定義。

publicenumSeason {

?SPRING, SUMMER, AUTUMN, WINER;

}

特點

Java定義枚舉類型的語句很簡約。它有以下特點:

1) 使用關鍵字enum 2) 類型名稱,比如這里的Season 3) 一串允許的值,比如上面定義的春夏秋冬四季 4) 枚舉可以單獨定義在一個文件中,也可以嵌在其它Java類中。

除了這樣的基本要求外,用戶還有一些其他選擇

5) 枚舉可以實現(xiàn)一個或多個接口(Interface) 6) 可以定義新的變量 7) 可以定義新的方法 8) 可以定義根據(jù)具體枚舉值而相異的類

用法

常量


switch


向枚舉中添加新方法


覆蓋枚舉的方法


實現(xiàn)接口


使用接口組織枚舉

反射

在運行狀態(tài)中,對于任意一個類,都能夠獲取到這個類的所有屬性和方法,對于任意一個對象,都能夠調(diào)用它的任意一個方法和屬性(包括私有的方法和屬性),這種動態(tài)獲取的信息以及動態(tài)調(diào)用對象的方法的功能就稱為java語言的反射機制。通俗點講,通過反射,該類對我們來說是完全透明的,想要獲取任何東西都可以。

    想要使用反射機制,就必須要先獲取到該類的字節(jié)碼文件對象(.class),通過字節(jié)碼文件對象,就能夠通過該類中的方法獲取到我們想要的所有信息(方法,屬性,類名,父類名,實現(xiàn)的所有接口等等),每一個類對應著一個字節(jié)碼文件也就對應著一個Class類型的對象,也就是字節(jié)碼文件對象。

    獲取字節(jié)碼文件對象的三種方式。

1、Class clazz1 = Class.forName("全限定類名");  //通過Class類中的靜態(tài)方法forName,直接獲取到一個類的字節(jié)碼文件對象,此時該類還是源文件階段,并沒有變?yōu)樽止?jié)碼文件。

2、Class clazz2? = Person.class;    //當類被加載成.class文件時,此時Person類變成了.class,在獲取該字節(jié)碼文件對象,也就是獲取自己, 該類處于字節(jié)碼階段。

3、Class clazz3 = p.getClass();    //通過類的實例獲取該類的字節(jié)碼文件對象,該類處于創(chuàng)建對象階段

繼承設計的技巧

(1)將公共操作和域放置在超類

?(2)不要使用受保護的域

有些程序員認為,將大多數(shù)的實例域定義為protected是一個不錯的主意,只有這樣,子類才能夠在需要的時候直接訪問他們。然而,protected 機制并不能夠帶來更好的保護,其原因主要有兩點。第一,子類集合是無限制的,任何一個人都能夠由某個類派生一個子類,并編寫代碼以直接訪問 protected的實例域,從而破壞了封裝性。第二,在Java程序設計語言中,在同一個包中的所有類都可以訪問protected域,而不管它是否為 這個類的子類。

?????? (3)使用繼承實現(xiàn)“is-a”關系

?????? 使用繼承很容易得到節(jié)省代碼的目的,但有時候也被人們?yōu)E用了。例如,假設需要定義一個鐘點工(Contractor)類。鐘點工的信息包含姓名和雇傭日 期,但是沒有薪水。他們按小時計薪,并且不會因為拖延時間而獲得加薪。這似乎在誘導人們由Employee派生出子類Constractor,然后再增加 一個hourlyWage域。

class Contractor extends Employee

{

?????? ….

?????? private double hourlyWage;

}


?????? 這并不是一個好主意。因為這樣一來,每個鐘點工對象中都包含了薪水和計時工資這兩個域。在實現(xiàn)打印支票或稅單方法的時候,會到來無盡的麻煩,并且會多些很多代碼。

?????? 鐘點工與雇員之間不屬于“is-a”關系。鐘點工不是特殊的雇員。

?????? (4)除非所有繼承的方法都有意義,否則不要使用繼承。

?????? 假設想編寫一個Holiday類。毫無疑問,每個假日也是一日,并且一日可以用GregorianCalendar類的實例表示,因此可以使用繼承。

class Holiday extends GregorianCalendar

{

?????? ………….

}


?????? 很遺憾,在繼承的操作中,假日集不是封閉的。在GregorianCalendar中有一個共有方法add,可以將假日轉換成非假日:

Holiday Christmas;

?????? christmas.add(Calendar.DAY_OF_MONTH,12);

?????? 因此,繼承對于這個例子來時并不太適宜。

(5)在覆蓋方法的時候,不要改變預期的行為

?????? 置換原則不僅應用于語法,而且也可以應用于行為,這似乎更加重要。在覆蓋一個方法的時候,不應該毫無緣由的改變行為的內(nèi)涵。就這一點而言,編譯器不會提供 任何幫助,即編譯器不會檢查重定義的方法是否有意義。例如,可以重定義Holiday類中的add方法“修正”原方法的問題,或什么也不做,或拋出一個異 常,或繼續(xù)到下一個假日。然而這些都違反了置換原則,語句序列

int d1=x.get(Calendar.DAY_OF_MONTH);

x.add(Calendar.DAY_OF_MONTH,1);

int d2=x.get(Calendar.DAY_OF_MONTH);

System.out.println(d2-d1);


?????? 不管x屬于GregorianCalendar類,還是屬于Holiday類,執(zhí)行上述語句后都應該得到預期的行為。

當然,這樣可能會引起某些爭議。人們可能就預期行為的含義爭論不休。例如,有些人爭論說,置換原則要求Manager.equals不處理bonus域, 因為Employee.equals沒有它。實際上,憑空討論這些問題毫無意義。關鍵在于,在覆蓋子類中的方法時,不要偏離最初的實際想法。

(6)使用多態(tài),而非類型信息。

?????? 無論什么時候,對于下面這種形式的代碼:

if(x is of type1)

action1(x);

else if (x is of type2)

?????? action2(x)


都應該考慮使用多態(tài)性。

action1與 action2表示的是相同的概念嗎?如果是相同的概念,就應該為這個概念定義一個方法,并將其放置在兩個類的超類或接口中,然后,就可以調(diào)用x.action( );以便使用多態(tài)性提供的動態(tài)分派機制執(zhí)行相應的動作。

使用多態(tài)犯法或接口編寫的代碼比使用對多種類型進行檢測的代碼更加易于為何和擴展。

(7)不要過多地使用反射

反射機制使得人們可以通過在運行時查看域和方法,讓人們編寫出更具有通行的程序。這種功能對于編寫系統(tǒng)程序來說及其實用,但是通常不是用于編寫應用程序。反射是很脆弱的,即編譯器很難幫助人們發(fā)現(xiàn)程序中的錯誤。任何錯誤只能在運行時才被發(fā)現(xiàn),并導致異常。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 5繼承 5.1 類、超類和子類 重用部分代碼,并保留所有域?!癷s-a”關系,用extends表示。 已存在的類被...
    我快要上天啦閱讀 952評論 1 3
  • 一、基礎知識:1、JVM、JRE和JDK的區(qū)別:JVM(Java Virtual Machine):java虛擬機...
    殺小賊閱讀 2,563評論 0 4
  • 一:java概述: 1,JDK:Java Development Kit,java的開發(fā)和運行環(huán)境,java的開發(fā)...
    慕容小偉閱讀 1,942評論 0 10
  • 大小宗師的拳架有什么不同,好像大家也不怎么關心,估計都忙著練拳了,沒人在乎大小宗師。 楊澄甫大宗師沒有留下拳架視頻...
    一代鬃獅閱讀 425評論 0 1
  • 忍耐的意義
    付知閱讀 276評論 0 0

友情鏈接更多精彩內(nèi)容