日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

【编程语言】Java基础进阶——面向对象部分

發布時間:2025/5/22 java 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【编程语言】Java基础进阶——面向对象部分 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

斷斷續續終于把男神的Java都看完了,只是囫圇吞棗走馬觀花罷了,還有好多地方不是很理解,很多細節都沒能理清。之后再邊動手邊復習一遍,然后開始嘗試做點東西吧!

?

0. 一些小tips

·為避免出現錯誤提示:The local variable may not have been initialized.應該每個函數內的局部變量都定義初始化。即保持良好的編程習慣,每個變量都定義初始化。

·String s = “”;

·boolean b = false;

·int a = 0;

?

·為避免構造方法的歧義重載,良好的編程習慣是默認建立一個空的構造方法:

<類名>()

{

}

·每一個類中都可以寫一個main方法,用來測試這個類的一些功能和屬性

·時刻謹記對象變量只是對象的管理者而非對象本身,聲明對象變量并沒有創建對象,就好比聲明一個指針,指針并不會指向任何有意義的內存空間一樣。對象變量和指針變量,兩者都需被具體賦值,才能進行接下來的操作。

·需要對字符串進行很多復雜操作并會產生大量新的字符串時,為減少系統開銷,應使用StringBuffer,最后用toString()輸出結果字符串即可

1. 類與對象

·對象是實體,需要被創建,可以為我們做事情

·類是規范,根據類的定義來創建對象

類定義了對象,而對象是類的實體。

對象 = 屬性 + 服務

封裝:把數據和對數據的操作放在一起,由操作來保護數據,數據不對外公開。

其中,

·數據:屬性或狀態 //? 類的成員變量

·操作:函數?????? // 類的成員函數

定義一個類,即是封裝的過程:

類中的變量都是類的成員變量(數據),類中的函數都是類的成員函數(對數據的操作)。

可以形象地將類描述成一個切開一半的咸蛋:蛋黃是數據,而包裹在蛋黃外面的蛋白則是對數據的操作,由蛋白包裹覆蓋蛋黃以起到保護的作用,就是用對數據的操作來保護數據。

用類創建(new)一個對象,語法上表達為:

<類> <對象名> = new <類>();

定義出來的變量是對象變量,是對象的管理者而非所有者(即對象變量不是對象本身,類似指針與被指針指向的內存區域的關系)。

一個類可以任意創建多個對象,每個對象都是獨立不同的。在Debug中可以看到每個對象變量的細節:對象內部的變量當前的值,對應的value項會顯示對象所屬的類名以及id等。

在創建對象變量之后,就可以用.運算符進行對象的操作了,即調用對象的某個函數,寫法為:

<對象>.<對象的成員函數名>();

?

1)函數與成員變量

在函數中可以直接寫成員變量的名字來訪問成員變量,就好比C中的函數可以直接用全局變量的名字一樣。

函數是通過對象來調用的:

·v.insertMoney();

·這次調用臨時建立了insertMoney()和V之間的關系,讓insertMoney()內部的成員變量指的是v的成員變量。

this是成員函數的一個特殊的固有本地變量,它表達了調用這個函數的那個對象(可以用指針的思想來理解:有一個固有的對象管理者this,函數調用時this臨時指向了.前面的那個對象,以表達當前調用函數的具體是哪個對象)

this可以寫在函數內部,用來表示此次調用該函數的那個對象,因此,

·可以用this來區分同名的 對象的成員變量 與 函數內部的本地變量:

class A {int a;void function( int a ) {this.a = a; // 若不用this則語法上是錯誤的,this.a表達的是對象的成員變量,而a則是函數function()內的參數,即一個本地變量,兩者是不同的 } }

?

·也可以用來方便地(偷懶少打幾個字)來調用其他成員函數:

1 class B { 2 int a; 3 … ; 4 5 void function1() 6 { 7 this.function2(); //等同于function2();在eclipse上輸入.之后會列出該類中的全部成員函數,然后選擇所需即可自動填補 8 9 } 10 void function2() 11 { 12 … ; 13 } 14 }

?

2)本地變量與成員變量

·定義在函數內部的是本地變量,其生存期和作用域都是函數內部

·成員變量是定義在類內函數外的,生存期是對象的生存期,作用域是類內部

3)成員變量初始化

在Java中,不允許存在未使用的本地變量,而成員變量的初始化則是自動的。

·成員變量在定義的地方就可以給初始值

·沒有初始值的成員變量會自動獲得0值 // 這里的0值是廣義的:0/false/null

·對象變量的0值表示它沒有管理任何對象,也可以主動給null值

·定義初始化時可以使用調用函數的返回值,甚至可以使用已經定義過的成員變量的值

4)構造函數與函數重載

如果有一個成員函數的名字與類名完全相同,則在創建這個類的每一個對象的時候會自動調用這個函數,即構造函數,也稱構造器。

構造函數:在構造(創建)對象的時候會被自動調用的函數,執行時先執行構造函數外面的定義初始化,之后再進入構造函數,最后構造函數結束返回new處,將構造出來的對象交給對象變量管理。構造函數不能有返回類型(包括void,void可以理解為這個函數有返回類型,類型是空(void),沒有返回值)。

在Java中,函數可以重載:

·一個類可以有多個構造函數,只要它們的參數表不同

·創建對象的時候給出不同的參數值,就會自動調用不同的構造函數

·通過this()還可以調用其他構造函數

·一個類里的同名但參數不同的函數構成了重載關系

關于this():只能在構造函數內的第一句處使用一次。寫法:

1 class A { 2 int a; 3 … ; 4 A() // 構造函數,與類名完全一致且無返回類型 5 { 6 … ; 7 } 8 A( int a ) // 多個同名但參數表不同的函數實現函數重載 9 { 10 this(); //在A(int a)內重載A(),只能在函數內第一句處使用一次 11 … ; 12 } 13 }

?

?

2. 對象交互

面向對象程序設計的第一步,就是在問題領域中識別出有效的對象,然后從識別出的對象中抽象出類來。

面向對象的核心在于看到在問題中有些什么樣的東西,每樣東西有什么樣的屬性,這些東西之間是如何交互的。

?

一個類中的成員變量可以使其他類的對象,最后得到的該類的對象是由多個其他類的對象組成的(就好比C中的結構體嵌套結構體一樣,Java中是對象包含對象,但Java中的對象能做的事情比C中的結構體多太多了)。

?

面向對象設計思想:

對象和對象之間的聯系緊密程度叫做耦合。對象和對象的耦合程度越緊,表現在源代碼上,就是它們的代碼是互相依賴、互相牽制的。我們理想的模型,是對象和對象之間的耦合要盡可能的松,平行的對象要盡量減少直接聯系,讓更高層次的對象來提供通信服務。

即,同一個類的每一個對象都盡可能的相互獨立,彼此之間沒有直接聯系。這樣在編寫對象甲的時候就不用去考慮對象乙的情況,編寫對象乙的時候也不需要考慮對象甲。讓兩者之間的交互由更高級的對象丙(同時包含對象甲和對象乙)來完成。

?

封裝,就是把數據和對這些數據的操作放在一起,并且用這些操作把數據掩蓋起來,是面向對象的基本概念之一,也是最核心的概念。

一個非常直截了當的手段來保證在類的設計的時候做到封裝:

·所有的成員變量必須是private的,這樣就避免別人任意使用你的內部數據;

·所有public的函數,只是用來實現這個類的對象或類自己要提供的服務的,而不是用來直接訪問數據的。除非對數據的訪問就是這個類及對象的服務。簡單地說,給每個成員變量提供一對用于讀寫的get/set函數也是不合適的設計。

?

封閉的訪問屬性:private(私有的):

·只有這個類的內部可以訪問(即如果在別的類中創造了該類的對象,也無法通過這個對象來訪問private的成員變量,因為不是在該類的內部)

·類的內部指的是成員函數和定義初始化

·這個限制是針對類的而不是針對對象的:即,對于私有的理解是從代碼的層面而非運行的層面上來看的。只要對private的成員變量的訪問是在該類的內部發生的,這個操作就是合理的。也就是說,同一個類的不同對象之間可以互相訪問私有成員。

?

開放的訪問屬性:public(公共的):

·任何部分(其他類)都可以自由訪問

如果一個類是public的:

·表明任何類都可以利用該類定義變量

·該類所處源文件的文件名必須與該類名相同,否則會報錯:The public type <class name> must be defined in its own file.修改建議為:Rename compilation unit to ‘<class name>.java’

?

編譯單元:

·一個源代碼文件(.java文件)是一個編譯單元(compilation unit),編譯時一次僅對一個編譯單元進行編譯。

·一個編譯單元中可以有很多Java類,但只能有一個類是public的。如果類不是public的,則該類只能在所處的包中起作用。

?

若在成員前未加任何關鍵字來修飾限定它,那么表明該成員是friendly的,意為與該成員所屬類位于同一個包(package)的其他類可以訪問它。

?

包:

包是Java的類庫管理機制,它借助文件系統的目錄來管理類庫,一個包就是一個目錄,一個包內的所有的類必須放在一個目錄下,那個目錄的名字必須是包的名字。

?

每一個Java源文件的第一個語句一定是package ?<包名>;這句話表明了這個public類屬于哪個包,也就是與public類同名的Java源文件位于那個與包同名的文件夾下。在Eclipse中,如果一個文件中沒有package語句,則表明該類屬于一個沒有名字的默認包(default package)。

?

如果在一個類中使用了另外的與其不在同一個包中的類,則有兩種方式:

1)在使用該類的時候寫出類的全名:<包名>.<類名>

2)使用import,寫法如下:

import <包名>.<類名>(即類的全名)

這樣寫的好處是:

清晰:在查找其他包的類時只需要看import處的那一列即可。

方便:在一個類中使用import的類只需要寫類名而不用寫類的全名。

同時import還可以寫成:

import <包名>.*(此處的*的含義是通配符)

這樣的寫法表示import該包里的全部類。一般來說不建議這種寫法,因為有可能會發生重名沖突。

注意,即使import過所需其他包的類,也無法訪問該類的friendly成員,因為friendly成員只能被同一個包的其他類訪問,不同包則不能。

?

包中包:

在建立新的包時如果起名為<已有包>.<包名>,則意味著這個新的包是一個包中包,在文件系統里體現為與該包同名的文件夾是已有包同名文件夾的子文件夾,在import類時則寫成:

import <包名>.<包名>.<類名>;

此時兩個包之間的.就等同于文件目錄路徑中的\

?

具體體現在文件系統中的層級依次為:

workspace

??? project

?????? src

?????????? package

????????????? *.java

?????? bin

?????????? package

????????????? *.class

例:

workspace

??? Project_1? //? 文件夾名與項目名一致,為Project_1

?????? src??? //? 文件夾名即src,按包分類存放.java文件(源文件)

?????????? clock? // 文件夾名與包名一致,為clock

????????????? Clock.java // 文件名與類名一致,為Clock

????????????? Display.java? //? 文件名與類名一致,為Display

????????????? time // 文件夾名為time,該包即是包中包,包名為clock.time

Time.java? //? 文件名與類名一致,為Time,該類全名為clock.time.Time

?????? bin??? //? 文件夾名即bin,按包分類存放.class文件(字節碼文件)

?????????? clock?

????????????? Clock.class??

????????????? Display.class

????????????? time??

????????????????? Time.class

??? Project_2

?????? src

?????????? a.java // 如果是默認包,則文件直接保存在src目錄下

?????? bin

  • class
  • ?

    在以上的情況下,如果在Clock.java文件中想要使用Time類,則應該在Clock類的前面寫import clock.time.Time; ? ?// 包的路徑是 ..\clock\time

    ?

    static,類變量與類函數:

    ?

    類是描述,對象是實體。在類里所描述的成員變量,是位于這個類的每一個對象中的。

    而如果某個成員有static關鍵字做修飾,它就不再屬于每一個對象,而是屬于整個類的了。

    通過每個對象都可以訪問到這些類變量和類函數,但是也可以通過類的名字來訪問它們。類函數由于不屬于任何對象,因此也沒有辦法建立與調用它們的對象的關系,就不能訪問任何非static的成員變量和成員函數了。

    ?

    static與public和private一樣,是用來修飾變量或函數的關鍵字。

    static意味著它所修飾的那個東西不屬于類中的任何對象,而屬于這個類。

    ?

    在類體中,如果定義一個變量/函數,在前面加上關鍵字static,則表明這個變量/函數是類變量/函數而非成員變量/函數:

    ·成員變量/函數位于每個對象之中而非這個對象的類之中,即同一個類的每個對象擁有屬于自己的成員變量/函數,對象與對象之間的成員變量/函數相互獨立沒有聯系;

    ·而static變量/函數則是類變量/函數,類變量/函數屬于整個類,而不屬于這個類的任何一個對象,但每個對象都可以訪問到它。

    有兩種方式可以訪問類變量/函數:

    <對象名>.<類變量/函數名>或<類名>.<類變量/函數名>

    前者是通過對象來訪問類變量/函數,因此只要屬于同一個類,任何對象都可以這樣寫。

    后者是通過類來訪問類變量/函數,更建議使用這種寫法,以便與對象的成員變量/函數清晰地區分開來。

    ?

    關于static的規則:

    ·如果一個函數是static的,那么它只能訪問static變量/函數

    ·static的變量和函數既可以通過類名來訪問,也可以通過某個對象名訪問,但通過對象名訪問時并不能獲得該對象的具體信息。

    ·如果在定義static變量時定義初始化,那么這個初始化只會在加載這個類的時候(即創建類變量,為其分配內存空間時)做一次,而與對象的創建無關。

    ?

    3. 容器

    程序設計的思想之一:人機交互與業務邏輯分離開來。在做業務邏輯的時候,只專注于數據本身,而不要去考慮人機交互方面的東西。因此,對于一個數據的讀取,不要在對象的方法里用單純的System.out.println()將它輸出,而是要把數據返回,由上層調用這個對象的函數來決定,得到這個返回值之后該做什么操作。這樣一方面使得函數的用法更加靈活(而不單單只能用來輸出),另一方面更符合面向對象程序設計的思想。人機交互與業務邏輯的統一標準由接口的設計來實現,做項目的時候,先定義接口,再實現具體功能。

    ?

    ?

    1)順序容器:ArrayList<Type>

    ·ArrayList<String> notes = new ArrayList<String>;

    意為創建一個array list of string類型的對象變量notes

    ·ArrayList是系統類庫中的一個泛型類,java.util.ArrayList

    ·大小可以隨意改變

    ·有很多現成的庫函數可以用,需要閱讀官方文檔來了解更多信息

    關于系統類庫的使用:

    ·是否充分理解系統類庫的類

    ·是否充分了解類庫中各種函數的功能和使用方法

    ?

    容器類:

    ·用來存放任意數量的對象

    ·有兩個類型:

    ·容器的類型

    ·元素的類型

    ?

    2)對象數組:

    ·對象數組中的每個元素都是對象的管理者而非對象本身

    ·對象數組的for-each情況與基礎數據類型的情況不同:

    class Value {private int i = 0;public void set( int i ) { this.i = i; }public int get() { return i; } }public class TestArrayList {public static void main(String[] args) {Value[] a = new Value[10];for ( int i = 0 ; i < a.length; i++ ) {a[i] = new Value();a[i].set(i);}for ( Value v : a ) {System.out.println(v.get());}for ( Value v : a) {v.set(0);}for ( Value v : a ) {System.out.println(v.get());}}}

    ?

    ?

    則第一次輸出是0~9十個數,而第二次輸出是十個0。由此可見,對象數組的for-each可以對數組內容進行修改。

    ·對于容器類來說,for-each循環也可以用

    ?

    3)集合容器:Set

    ·集合的概念與數學中的集合完全一致:

    ·集合中的每個元素都是不重復的,即每個元素具有唯一的值

    ·集合中的元素沒有順序

    ·集合也是一種泛型類:HashSet<String> s = new HashSet<String>();

    ?

    HashSet和ArrayList的對象都可以用System.out.println()直接輸出,輸出結果為[]中顯示元素值,其中ArraryList的值是順序的,而Set中是無序的。

    ?

    toString():

    任何一個Java類,只要在其中實現:

    public String toString()

    {

    ??? return <String類型字符串>;

    }

    就可以直接寫成System.out.println( <該類的對象名> );

    這種寫法將會使System.out直接調用該類中的toString(),并輸出toString()返回的相應結果(String)。

    如果類中沒有toString(),System.out.println( <該類的對象名> );則會輸出對象變量的值,即對象實體的引用,類似輸出指針會輸出指針指向的地址,格式為<類的全名>@<十六進制數>。

    ?

    4)散列表

    HashMap<Integer, String> a = new HashMap<Integer, String>();

    數據結構中的散列表里所有的東西都是以一對值的形式存放的:Key鍵 Value值

    關于Integer:容器中的類型必須是對象而不能是基本類型的數據,所以對于整型int,應使用其包裹類型Integer。

    對于散列表,每個Key一定是唯一的,所以如果對于同一個鍵多次放入不同值,最后保存的是最后一次放入的值。

    HashMap的一些方法:

    a.put( <key>, <value> ); // key->value

    a.get( <key> );????? // return value

    a.containsKey( <key> );? // return true / false

    a.keySet();?? // HashMap a中所有key的HashSet

    a.keySet().size();?????? // HashSet()的size()方法

    ?

    對HashMap的for-each:

    for ( Integer k : a.keySet() ) {?????? // 枚舉所有key

    ??? String s = a.get(k);

    ??? System.out.println(s);????? //? 輸出key對應的value

    }

    ?

    4.繼承

    繼承是面向對象語言的重要特征之一,沒有繼承的語言只能被稱作“使用對象的語言”。繼承是非常簡單而強大的設計思想,它提供了我們代碼重用和程序組織的有力工具。基于已有的設計創造新的設計,就是面向對象程序設計中的繼承。在繼承中,新的類不是憑空產生的,而是基于一個已經存在的類而定義出來的。通過繼承,新的類自動獲得了基礎類中所有的成員,包括成員變量和方法,包括各種訪問屬性的成員,無論是public還是private。當然,在這之后,程序員還可以加入自己的新的成員,包括變量和方法。顯然,通過繼承來定義新的類,遠比從頭開始寫一個新的類要簡單快捷和方便。繼承是支持代碼重用的重要手段之一。

    ?

    對理解繼承來說,最重要的事情是,知道哪些東西被繼承了,或者說,子類從父類那里得到了什么。答案是:所有的東西,所有的父類的成員,包括變量和方法,都成為了子類的成員,除了構造方法。構造方法是父類所獨有的,因為它們的名字就是類的名字,所以父類的構造方法在子類中不存在。除此之外,子類繼承得到了父類所有的成員。

    但是得到不等于可以隨便使用。每個成員有不同的訪問屬性,子類繼承得到了父類所有的成員,但是不同的訪問屬性使得子類在使用這些成員時有所不同:有些父類的成員直接成為子類的對外的界面,有些則被深深地隱藏起來,即使子類自己也不能直接訪問。

    ?

    public的成員直接成為子類的public的成員,protected的成員也直接成為子類的protected的成員。Java的protected的意思是包內和子類可訪問,所以它比缺省的訪問屬性要寬一些。而對于父類的缺省的未定義訪問屬性的成員來說,他們是在父類所在的包內可見,如果子類不屬于父類的包,那么在子類里面,這些缺省屬性的成員和private的成員是一樣的:不可見。父類的private的成員在子類里仍然是存在的,只是子類中不能直接訪問。我們不可以在子類中重新定義繼承得到的成員的訪問屬性。如果我們試圖重新定義一個在父類中已經存在的成員變量,那么我們是在定義一個與父類的成員變量完全無關的變量,在子類中我們可以訪問這個定義在子類中的變量,在父類的方法中訪問父類的那個。盡管它們同名但是互不影響。

    ?

    在構造一個子類的對象時,父類的構造方法也是會被調用的,而且父類的構造方法在子類的構造方法之前被調用。在程序運行過程中,子類對象的一部分空間存放的是父類對象。因為子類從父類得到繼承,在子類對象初始化過程中可能會使用到父類的成員。所以父類的空間正是要先被初始化的,然后子類的空間才得到初始化。在這個過程中,如果父類的構造方法需要參數,如何傳遞參數就很重要了。

    ?

    子類比父類更加具體。例如鳥是父類,那么啄木鳥,貓頭鷹,麻雀,鴕鳥都是鳥的子類。

    子類通過繼承得到了父類的所有東西,包括private的成員,盡管可能無法直接訪問和使用,但繼承自父類的東西確實存在,父類中所有public和protected的成員,在子類中都可以直接拿來使用。

    ?

    關鍵字:extends

    class thisClass extends superclass {?? // 表明thisClass是superClass 的子類

    ???

    }

    用關鍵字super表示父類,以區分調用的同名函數是子類中的還是父類中的,就像this用來表示當前對象,以區分同名變量是局部的還是成員的一樣。

    ?

    在創建一個子類的對象時的代碼執行順序總是:

    1子類的構造器中的super()(如果有super的話)

    2父類構造器中的super()(如果有super的話)

    3父類成員變量定義初始化(如果有初始化的話)

    ????????????????? ↓

    4父類構造器內部

    5子類成員變量定義初始化(如果有初始化的話)

    6子類構造器內部

    ?

    也就是說,在構造一個子類的對象時,父類的構造方法在子類的構造方法之前被調用。

    構造器是構造對象時的入口,但并不是最優先被執行的,成員變量定義初始化最優先,若無定義初始化則跳過。

    在子類中,無論是否存在super(),在構造子類的對象時都會自動調用父類的構造器:若無super,則默認調用父類無參構造器,此時若父類中存在有參構造器,但沒有無參構造器則會報錯;若有super(<參數表>),則會根據參數表重載相應的父類構造器。

    ?

    ?

    5.多態變量與造型

    ?

    對象變量是多態變量,可以保存其聲明類型的對象,或該類型的任何子類型的對象。

    ?

    子類和子類型

    ·類定義了類型

    ·子類定義了子類型

    ·子類的對象可以被當作父類的對象來使用

    ·賦值給父類的變量

    ·傳遞給需要父類對象的函數

    ·放進存放父類對象的容器

    ?

    子類的對象可以賦值給父類的對象變量。

    如Vehicle類是父類,其子類有Car,Bicycle等,那么Bicycle類和Car類的對象都可以賦值給Vehicle類的對象變量:

    Vehicle v = new Vehicle();

    Vehicle v = new Car();

    Vehicle v = new Bicycle();

    ?

    子類和參數傳遞:

    子類的對象可以傳遞給需要父類對象的函數。

    子類的對象可以放在存放父類對象的容器里。

    ?

    多態變量

    ·Java的對象變量是多態的,它們能保存不止一種類型的對象

    ·它們可以保存的是聲明類型的變量,或聲明類型的子類的對象

    ·當把子類的對象賦值給父類的變量時,就發生了向上造型

    ?

    靜態類型與動態類型

    ·一個對象變量的聲明類型就是其靜態類型

    ·一個對象變量當前指向的對象的類型是動態類型

    因此Java中的對象變量總是有兩個類型,一個是聲明類型,一個是動態類型,只是有時這兩個類型可能是一致的。

    ?

    造型cast

    ·一個類型的對象賦值給另一個類型的對象變量的過程

    ·子類的對象可以賦值給父類的變量

    ·注意!Java中不存在對象對對象的賦值

    ·父類的對象不能賦值給子類的變量

    ·當父類的變量當前指向的是子類的對象時,可以用造型來賦值,否則會出現異常:ClassCastException

    ?

    如,Vehicle是父類Car是子類:

    Vehicle v;

    Car c = new Car();

    v = c; //? 可以

    c = v; //? 編譯錯誤

    c = (Car)v;?? // 造型,此時v指向的是Car的對象,即v的動態類型是Car,可以用造型來賦值給Car類型的變量

    再如,Item是父類,CD是子類:

    Item item = new Item();

    CD cd = new CD();

    item = cd; // 可以

    CD cc = (CD)item; ??? //? 造型,如果沒有(CD)則報錯

    ?

    造型

    ·用括號圍起類型放在值的前面

    ·對象本身并沒有發生任何變化

    ·所以不是“類型轉換”:類型轉換是將原來的值變成轉換類型的值之后賦值,而造型是將當前類型的值(對象)看作是造型類型的值來看待,并沒有修改替換成為另外類型的值

    ·運行時有機制來檢查這樣的轉化是否合理

    ·ClassCastException

    ?

    向上造型:把子類的對象賦值給父類的變量,(<父類型>)可以省略

    ·拿一個子類的對象,當作父類的對象來用

    ·向上造型是默認的,不需要運算符

    ·向上造型總是安全的

    ?

    向下造型:把子類的對象通過父類變量賦值給子類變量,即父類變量當前指向子類對象,需要用到(<子類型>)

    ?

    6.多態

    ?

    如果子類的方法覆蓋了父類的方法,我們也說父類的那個方法在子類有了新的版本或者新的實現。覆蓋的新版本具有與老版本相同的方法簽名:相同的方法名稱和參數表。因此,對于外界來說,子類并沒有增加新的方法,仍然是在父類中定義過的那個方法。不同的是,這是一個新版本,所以通過子類的對象調用這個方法,執行的是子類自己的方法。

    ?

    覆蓋關系并不說明父類中的方法已經不存在了,而是當通過一個子類的對象調用這個方法時,子類中的方法取代了父類的方法,父類的這個方法被“覆蓋”起來而看不見了。而當通過父類的對象調用這個方法時,實際上執行的仍然是父類中的這個方法。注意我們這里說的是對象而不是變量,因為一個類型為父類的變量有可能實際指向的是一個子類的對象。

    ?

    當調用一個方法時,究竟應該調用哪個方法,這件事情叫做綁定。綁定表明了調用一個方法的時候,我們使用的是哪個方法。綁定有兩種:一種是早綁定,又稱靜態綁定,這種綁定在編譯的時候就確定了;另一種是晚綁定,即動態綁定。動態綁定在運行的時候根據變量當時實際所指的對象的類型動態決定調用的方法。Java缺省使用動態綁定。

    ?

    函數調用的綁定

    ·當通過對象變量調用函數的時候,調用哪個函數這件事情可以叫做綁定

    ·靜態綁定:根據變量的聲明類型來決定

    ·動態綁定:根據變量的動態類型來決定

    ·在成員函數中調用其它成員函數也是通過this這個對象變量來調用的

    ?

    覆蓋/重寫override

    ·子類和父類中存在名稱和參數表完全相同的函數,這一對函數構成覆蓋關系

    ·通過父類的變量調用存在覆蓋關系的函數時,會調用變量當時所管理的對象所屬類的函數

    ?

    所謂的多態就是,通過一個對象變量調用一個函數時,并不去判斷對象變量的動態類型可能是什么,當程序執行的時候,讓其自動通過當前動態類型來執行相應的函數。

    ?

    在函數重寫時,函數的前一句話應該是@Override,子類中的重寫函數的聲明部分(即函數頭)應與父類的完全一致,包括限定詞,返回值類型,函數名,參數表

    ?

    如:

    @Override

    public String toString()

    {

    ??? return <String>;

    }

    ?

    @Override的作用是,告訴編譯器接下來的函數是對父類同名函數的重寫(即覆蓋),如果接下來的函數與父類同名函數有任何不一致的地方,則會報錯;如果沒有@Override,則不會對接下來的函數做任何限制,同樣地也就不再必須是對父類函數的重寫。

    ?

    Java類型系統的根:Object類

    Java是一種單根結構的語言,Java中的所有類默認看作是Object類的子類,即,所有的類都是繼承自Object的。

    ?

    Object類的函數

    ·toString()

    ·equals()

    ·可通過eclipse補全功能查看其他函數

    ?

    可擴展性:代碼不需要經過修改就可以適應新的內容或數據

    可維護性:代碼經過修改可以適應新的內容或數據

    ?

    7.設計原則

    ?

    ?

    1)消除代碼復制:

    程序中存在相似甚至相同的代碼塊,是非常低級的代碼質量問題。

    ?

    代碼復制存在的問題是,如果需要修改一個副本,那么就必須同時修改所有其他的副本,否則就存在不一致的問題。這增加了維護程序員的工作量,而且存在造成錯誤的潛在危險。很可能發生的一種情況是,維護程序員看到一個副本被修改好了,就以為所有要修改的地方都已經改好了。因為沒有任何明顯跡象可以表明另外還有一份一樣的副本代碼存在,所以很可能會遺漏還沒被修改的地方。

    ?

    消除代碼復制的兩個基本手段,就是函數和父類。

    ?

    2)用封裝來降低耦合

    ·類和類之間的關系稱作耦合

    ·耦合越低越好,保持距離是形成良好代碼的關鍵

    ?

    要評判某些設計比其他的設計優秀,就得定義一些在類的設計中重要的術語,以用來討論設計的優劣。對于類的設計來說,有兩個核心術語:耦合和聚合。耦合這個詞指的是類和類之間的聯系。之前的章節中提到過,程序設計的目標是一系列通過定義明確的接口通信來協同工作的類。耦合度反映了這些類聯系的緊密度。我們努力要獲得低的耦合度,或者叫作松耦合(loose coupling)。

    ?

    耦合度決定修改應用程序的容易程度。在一個緊耦合的結構中,對一個類的修改也會導致對其他一些類的修改。這是要努力避免的,否則,一點小小的改變就可能使整個應用程序發生改變。另外,要想找到所有需要修改的地方,并一一加以修改,卻是一件既困難又費時的事情。另一方面,在一個松耦合的系統中,常常可以修改一個類,但同時不會修改其他類,而且整個程序還可以正常運作。

    ?

    聚合與程序中一個單獨的單元所承擔的任務的數量和種類相對應有關,它是針對類或方法這樣大小的程序單元而言的理想情況下,一個代碼單元應該負責一個聚合的任務(也就是說,一個任務可以被看作是一個邏輯單元)。一個方法應該實現一個邏輯操作,而一個類應該代表一定類型的實體。聚合理論背后的要點是重用:如果一個方法或類是只負責一件定義明確的事情,那么就很有可能在另外不同的上下文環境中使用。遵循這個理論的一個額外的好處是,當程序某部分的代碼需要改變時,在某個代碼單元中很可能會找到所有需要改變的相關代碼段。

    ?

    3)增加可擴展性:

    ·可以運行的代碼 != 良好的代碼

    ·對代碼做維護的時候最能看出代碼的質量

    ·可擴展性的意思就是代碼的某些部分不需要經過修改就能適應將來可能的變化

    ?

    ·用接口來實現聚合

    ·在類的內部實現新的方法,把具體細節徹底隱藏在類的內部

    ·今后某一功能如何實現就和外部無關了

    ?

    ·用容器來實現靈活性

    ·減少“硬編碼”

    ·對于隨時變化數量的同類事物,用容器來實現,比如HashMap

    ?

    ·框架+數據提高可擴展性

    ·從程序中識別出框架和數據,以代碼實現框架,將部分功能以數據的方式加載,這樣能在很大程度上實現可擴展性。

    ?

    8.抽象

    ?

    我們用abstract關鍵字來定義抽象類。抽象類的作用僅僅是表達接口,而不是具體的實現細節。抽象類中可以存在抽象方法。抽象方法也是使用abstract關鍵字來修飾。抽象的方法是不完全的,它只是一個方法簽名而完全沒有方法體。

    ?

    如果一個類有了一個抽象的方法,這個類就必須聲明為抽象類。如果父類是抽象類,那么子類必須覆蓋所有在父類中的抽象方法,否則子類也成為一個抽象類。一個抽象類可以沒有任何抽象方法,所有的方法都有方法體,但是整個類是抽象的。設計這樣的抽象類主要是為了防止制造它的對象出來。

    ?

    抽象abstract

    ·抽象函數:表達概念而無法實現具體代碼的函數

    ·帶有abstract修飾的函數

    ·public abstract void method();?? // 必須結尾帶分號,而不能是空的大括號{},就像C中的函數聲明

    ·抽象類:表達概念而無法構造出實體的類

    ·有抽象函數的類一定是抽象類

    ·抽象類不能制造對象

    ·但是可以定義變量:任何繼承了該抽象類的非抽象子類的對象可以賦給這個抽象類的變量,即由抽象類變量管理其非抽象子類的對象。

    ?

    實現抽象函數

    ·繼承自抽象類的子類必須覆蓋所有父類中的抽象函數,這里的對父類的抽象函數的覆蓋稱作實現implement

    ·否則子類也是抽象類。因為有繼承自父類的抽象函數未重寫,只要一個類中有抽象函數,這個類就必須是抽象類。

    ?

    寫法:

    public abstract class Shape {

    ??? public abstract void draw(Graphics g); // 只有函數頭加;沒有函數體

    }

    ?

    public class Line extends Shape { // Line是繼承自抽象類Shape的非抽象子類

    ??? … …

    ??? @Override

    ??? public void draw(Graphics g)??? //對抽象類Shape的抽象方法draw的具體實現

    {

    ??? … …

    }

    }

    ?

    兩種抽象

    ·與具體相對:表示一種概念而非實體

    ·與細節相對:表示在一定程度上忽略細節而著眼大局

    ?

    數據與表現分離

    ·程序的業務邏輯與表現無關

    ·表現可以是圖形的也可以是文本的

    ·表現可以是當地的也可以是遠程的

    ·負責表現的類只管根據數據來表現

    ·負責數據的類只管數據的存放

    ·一旦數據更新以后,表現仍然可以不變

    ·不用去精心設計哪個局部需要更新

    ·簡化了程序邏輯

    ·這是在計算機運算速度提高的基礎上實現的

    責任驅動的設計

    ·將程序要實現的功能分攤到適合的類/對象中去是設計中非常重要的一環

    ?

    網格化

    ·圖形界面本身有更高的解析度

    ·但是將畫面網格化以后,數據就更容易處理了

    ?

    9.接口

    ?

    接口interface

    ·類表達的是對某類東西的描述,而接口表達的是抽象的概念和規范

    ·接口是純抽象類

    ??? ·所有的成員函數都是抽象函數

    ·所有的成員變量都是public static final

    ·因此不需特意聲明abstract

    ·接口規定了長什么樣,但不管里面有什么東西

    ·在Java中,接口就是一種特殊的class

    ·它與class地位相同

    ·接口類型的變量可以被賦值任何實現了這個接口的類的對象

    ?

    寫法:

    public interface Cell {

    int size;? // public static final

    ??? void draw( … );?? // abstract

    }

    ?

    實現接口

    ·類用extends表達子類繼承父類,用implements表達一個類實現了一個接口

    ·一個類可以實現很多接口

    ·接口可以繼承接口,但不能繼承類

    ·接口不能實現接口

    ?

    寫法:

    public class Fox extends Animal implements Cell {

    ??? … …

    ??? @Override

    ??? public void draw()

    {

    ?????? … …

    }

    }

    ?

    面向接口的編程方式

    ·設計程序時先定義接口,再實現類

    ·任何需要在函數間傳入傳出的一定是接口而不是具體的類

    ·是Java的成功關鍵之一,因為極適合多人同時寫一個大程序

    ·也是Java被批評的要點之一,因為代碼量會迅速膨脹,整體顯得臃腫

    ?

    10.控制反轉與MVC設計模式

    ?

    內部類就是指一個類定義在另一個類的內部,從而成為外部類的一個成員。因此一個類中可以有成員變量、方法,還可以有內部類。實際上Java的內部類可以被稱為成員類,內部類實際上是它所在類的成員。所以內部類也就具有和成員變量、成員方法相同的性質。比如,成員方法可以訪問私有變量,那么成員類也可以訪問私有變量了。也就是說,成員類中的成員方法都可以訪問成員類所在類的私有變量。內部類最重要的特點就是能夠訪問外部類的所有成員。

    ?

    內部類

    ·定義在別的類的內部或函數內部的類

    ·內部類能直接訪問外部的全部資源

    ·包括任何私有成員,因為內部類也是類的成員之一

    ·外部是函數時,只能訪問那個函數里的final的變量

    ?

    匿名類

    ·在new對象的時候給出的類的定義形成了匿名類

    ·匿名類一定是內部類,可以繼承某類,也可以實現某接口

    ·Swing的消息機制廣泛使用匿名類

    ?

    寫法:

    new 類名/接口名() {

    ??? … …

    }

    如:

    JButton butStep = new JButton(“OneStep”);

    butStep.addActionListener(new ActionListener() {

    // addActionListener()是JButton類的方法,ActionListener是JButton的接口

    ??? @Override

    ??? public void actionPerformed()

    ??? {

    ?????? ……

    }

    });??? // 定義匿名類,實現ActionListener接口

    ?

    注入反轉·控制反轉

    ·由按鈕(別人寫的類)公布一個守聽者接口和一對注冊/注銷方法

    ·根據需要編寫代碼實現那個接口,將守聽者對象注冊在按鈕上

    ·一旦按鈕被按下,就會反過來調用你的守聽者對象的某個方法

    ?

    MVC

    ·數據、表現和控制三者分離,各負其責

    ·M = Model(模型)

    ·V = View(表現)

    ·C = Control(控制)

    ·模型:保存和維護數據,提供接口讓外部修改數據,通知表現需要刷新

    ·表現:從模型獲得數據,根據數據畫出表現

    ·控制:從用戶得到輸入,根據輸入調整數據

    ·在MVC中,很重要的一件事是,Control和View之間沒有任何聯系。用戶在界面上所做的操作(控制)不直接修改界面上的顯示(表現),即,用來接收用戶操作/輸入的代碼并不直接修改在屏幕上顯示的內容,而是用戶的輸入來調整內部的數據,再由內部數據去觸發表現刷新。

    ·這樣做的好處是,每一部分都很單純,不需要去考慮其他細節的東西。

    ?

    11.異常

    ?

    捕捉異常

    Try {

    ??? //? 可能產生異常的代碼

    } catch(Type1 id1) {

    ??? //? 處理Type1異常的代碼

    } catch(Type2 id2) {

    ??? //? 處理Type2異常的代碼

    } catch(Type3 id3) {

    ??? //? 處理Type3異常的代碼

    }

    ?

    ·得到異常對象之后

    ??? ·String getMessage();

    ??? ·String toString();

    ??? ·void printStackTrace();

    ·并不能回到發生異常的地方,具體的處理邏輯取決于業務邏輯需要

    ?

    再度拋出

    catch(Exception e) {

    ??? System.err.println(“An exception was thrown”);

    ??? throw e;

    }

    ·如果在這個層面上需要處理,但并不能做最終的決定,就catch之后再throw

    ?

    讀取文件使用異常機制的例子(偽碼描述):

    try {

    ??? open the file;

    ??? determine its size;

    ??? allocate that much memory;

    ??? read the file into memory;

    ??? close the file;

    } catch ( fileOpenFailed ) {

    ??? doSomething;

    } catch ( sizeDeterminationFailed ) {

    ??? doSomething;

    } catch ( memoryAllocationFailed ) {

    ??? doSomething;

    } catch ( readFailed ) {

    ??? doSomething;

    } catch ( fileClosedFailed ) {

    ??? doSomething;

    }

    ?

    異常

    ·有不尋常的事情發生了

    ·當這個事情發生的時候,原本打算要接著做的事情不能再繼續了,必須得要停下來,讓其他地方的某段代碼來處理

    ·異常機制最大的好處是清晰地分開了正常業務邏輯代碼和遇到情況時的處理代碼

    ?

    異常聲明

    ·如果函數可能拋出異常,就必須在函數頭部加以聲明

    ??? void f() throws TooBig, TooSmall, DivZero { … }

    ·可以聲明并不會真的拋出的異常,以使函數更加靈活,增加可擴展性,方便以后的補充修改

    ?

    可以拋出的

    ·任何繼承了Throwable類的對象

    ·Exception類繼承了Throwable

    ??? ·throw new Exception();

    ??? ·throw new Exception(“HELP”);

    ?

    catch匹配異常

    ·Is-A的關系

    ·拋出的子類異常會被捕捉父類異常的catch捕捉到

    ?

    捕捉任何異常

    catch(Exception e) {

    ??? System.err.println(“Caught an exception”);

    }

    ?

    運行時刻異常

    ·像ArrayIndexOutOfBoundsException這樣的異常是不需要聲明的

    ·但是如果沒有適當的機制來捕捉,就會最終導致程序終止

    ?

    異常聲明遇到繼承關系

    ·當覆蓋一個函數的時候,子類不能聲明拋出比父類版本更多的異常

    ·在子類的構造函數中,必須聲明父類可能拋出的全部異常,之后還可以增加子類自己的異常聲明

    ?

    12.流

    ?

    流的基礎類

    ·InputStream

    ·OutputStream

    ·都是對字節進行操作:字節流

    ?

    InputStream

    ·read()

    ??? ·int read()

    ??? ·read(byte b[])

    ??? ·read(byte[], int off, int len)

    ??? ·skip(long n)

    ·int available()

    ·mark()

    ·reset()

    ·boolean markSupported()

    ·close()

    ?

    ?

    OutputStream

    ·write()

    ·write(int b)

    ·write(byte b[])

    ·write(byte b[], int off, int len)

    ·flush()

    ·close()

    ?

    文件流

    ·FileInputStream

    ·FileOutputStream

    ·對文件作讀寫操作,也是字節

    ·實際工程中已經較少使用

    ·更常用的是以在內存數據或通信數據上建立的流,如數據庫的二進制數據讀寫或網絡端口通信

    ·具體的文件讀寫往往有更專業的類,比如配置文件和日志文件

    ?

    流過濾器

    ·以一個介質流對象為基礎層層構建過濾器流,最終形成的流對象能在數據的輸入輸出過程中,逐層使用過濾器流的方法來讀寫數據

    ?

    Data

    ·DataInputStream

    ·DataOutputStream

    ·用以讀寫二進制方式表達的基本數據類型的數據

    ?

    文本流

    ?

    Reader/Writer

    ·二進制數據采用InputStream/OutputStream

    ·文本數據采用Reader/Writer

    ?

    在流上建立文本處理

    PrintWriter pw == new PrintWriter(

    new BufferedWriter(

    new OutputStreamWriter(

    new FileOutputStream(“abc.txt”))));

    ?

    Reader

    ·常用的是BufferedReader

    ·readLine()

    ?

    LineNumberReader

    ·可以得到行號

    ·getLineNumber()

    ?

    FileReader

    ·InputStreamReader類的子類,所有方法都從父類中繼承而來

    ·FileReader(File file)

    ·在給定從中讀取數據的File的情況下創建一個新FileReader

    ·FileReader(String fileName)

    ??? ·在給定從中讀取數據的文件名的情況下創建一個新FileReader

    ·FileReader不能指定編碼轉換方式

    ?

    漢字編碼

    ·InputStreamReader(InputStream in)

    ??? 創建一個使用默認字符集的InputStreamReader

    ·InputStreamReader(InputStream in, Charset cs)

    ??? 創建使用給定字符集的InputStreamReader

    ·InputStreamReader(InputStream in, CharsetDecoder dec)

    ??? 創建使用給定字符集解碼器的InputStreamReader

    ·InputStreamReader(InputStream in, String charsetName)

    ??? 創建使用指定字符集的InputStreamReader

    ?

    格式化輸入輸出

    ·PrintWriter

    ??? ·format();

    ??? ·printf();用法與C一致

    ??? ·print();

    ??? ·println();

    ·Scanner

    ·在InputStream或Reader上建立一個Scanner對象可以從流中的文本中解析出以文本表達的各種基本類型

    ·next…();

    ?

    Stream/Reader/Scanner

    數據是二進制的

    是->用InputStream

    否->表達的是文本

    ??? 是->用Reader

    ??? 否->用Scanner

    ?

    ?

    阻塞/非阻塞

    ·read()函數是阻塞的,在讀到所需的內容之前會停下來等

    ??? ·使用read()的更“高級”的函數,如newInt()、readLine()都是這樣的

    ??? ·所以常用單獨的線程來做socket讀的等待,或使用nio的channel選擇機制

    ·對于socket,可以設置SO時間

    ??? ·setSoTimeout(int timeOut)

    ?

    ?對象串行化

    ·ObjectInputStream類

    ??? ·readObject()

    ·ObjectOutputStream類

    ??? ·writeObjeact()

    ·Serializable接口

    ?

    轉載于:https://www.cnblogs.com/lilinilil/p/8855288.html

    總結

    以上是生活随笔為你收集整理的【编程语言】Java基础进阶——面向对象部分的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。