Java私塾跟我学系列——JAVA篇 第四章Java类和对象
?
| 教學目標: i面向對象基礎 i掌握對象的三大特性 i掌握Java類的構建 i掌握如何使用Java類 i理解引用類型 i理解按值傳遞和按引用傳遞 i深入理解變量 i掌握包裝類 i理解類型轉換 i理解Java類的基本運行順序 ? |
一、面向對象初步????????????????????????????????????????????
1.什么是對象?????????????????????????????????????????????????????????????????????
對象是真實世界中的物體在人腦中的映象,包括實體對象和邏輯對象。實體對象指的是我們能在現實生活中能看得見、摸得著,實際存在的東西,比如:人,桌子,椅子等。邏輯對象是針對非具體物體,但是在邏輯上存在的東西的反映,比如:人與人的關系。為了簡單,這里討論的對象都是實體對象。
2.對象的基本構成????????????????????????????????????????????????????????????????????
初次接觸對象,我們從實體對象入手,因為看得見、摸得著會比較容易理解。
分析實體對象的構成,發現有這樣一些共同點,這些實體對象都有自己的屬性,這些屬性用來決定了對象的具體表現,比如:人有身高、體重等。
除了這些靜態的,用于描述實體對象的基本情況外,實體對象還有自己的動作,通過這些動作能夠完成一定的功能,我們稱之為方法,比如:人的手能動,能夠寫字,能夠刷牙等。
對象同時具備這些靜態屬性和動態的功能。
3.如何進行對象抽象???????????????????????????????????????????????????????????????????
抽象是在思想上把各種對象或現象之間的共同的本質屬性抽取出來而舍去個別的非本質的屬性的思維方法。也就是說把一系列相同或類似的實體對象的特點抽取出來,采用一個統一的表達方式,這就是抽象。
比如:張三這個人身高180cm,體重75kg,會打籃球,會跑步
????????? 李四這個人身高170cm,體重70kg,會踢足球
現在想要采用一個統一的對象來描述張三和李四,那么我們就可以采用如下的表述方法來表述:
人{
靜態屬性:
????? 姓名;
????? 身高;
????? 體重;
動態動作:
????? 打籃球();
????? 跑步();
????? 踢足球();
}
這個“人”這個對象就是對張三和李四的抽象,那么如何表述張三這個具體的個體呢:
人{
靜態屬性:
姓名=張三;
????? 身高 = 180cm;
? ????體重 = 75kg;
動態動作:
????? 打籃球(); //相應的打籃球的功能實現
????? 跑步();//相應的跑步的功能實現
????? 踢足球();
}
如何表述李四這個具體的個體呢:
人{
靜態屬性:
????? 姓名=李四;
????? 身高 = 170cm;
????? 體重 = 70kg;
動態動作:
????? 打籃球();
????? 跑步();
????? 踢足球();//相應的踢足球的功能實現
}
對實體對象的抽象一定要很好的練習,可以把你所看到的任何物體都拿來抽象,“一切皆對象”。要練習到,你看到的沒有物體,全是對象就好了。
?
4.抽象對象和實體對象的關系????????????????????????????????????????????????????????????
仔細觀察上面的抽象對象——“人”,和具體的實體對象:“張三”、“李四”。你會發現,抽象對象只有一個,實體對象卻是無數個,通過對抽象對象設置不同的屬性,賦予不同的功能,那么就能夠表示不同的實體對象。
這樣就大大簡化了對象的描述工作,使用一個對象就可以統一地描述某一類實體了,在需要具體的實體的時候,分別設置不同的值就可以表示具體對象了。
?
5.Java中的類和對象?????????????????????????????????????????????????????????????
5.1 Java中的類
把抽象出來的對象使用Java表達出來,那就是類class。類在Java編程語言中作為定義新類型的一種途徑,類聲明可定義新類型并描述這些類型是如何實現的。接下來將會學習許多關于類的特性。
比如前面討論過的“人”使用Java表達出來就是一個類。
?
5.2 Java中的對象
?????? Java中的對象是在Java中一個類的實例,也稱實例對象。實例就是實際例子。
類可被認為是一個模板------你正在描述的一個對象模型。一個對象就是你每次使用的時候創建的一個類的實例的結果。
比如前面討論的張三和李四,他們就是“人”這個類的實例。
?
二、面向對象三大特征????????????????????????????????????????
1.封裝?????????????????????????????????????????????????????????????????????????
封裝這個詞聽起來好象是將什么東西包裹起來不要別人看見一樣,就好象是把東西裝進箱子里面,這樣別人就不知道箱子里面裝的是什么東西了。其實JAVA中的封裝這個概念也就和這個是差不多的意思。
封裝是JAVA面向對象的特點的表現,封裝是一種信息隱蔽技術。它有兩個含義:即把對象的全部屬性和全部服務結合在一起,形成一個不可分割的獨立單位;以及盡可能隱藏對象的內部結構。也就是說,如果我們使用了封裝技術的話,別人就只能用我們做出來的東西而看不見我們做的這個東西的內部結構了。
?
封裝的功能
-???????? 隱藏對象的實現細節
-???????? 迫使用戶去使用一個界面訪問數據
-???????? 使代碼更好維護
封裝迫使用戶通過方法訪問數據能保護對象的數據不被誤修改,還能使對象的重用變得更簡單。數據隱藏通常指的就是封裝。它將對象的外部界面與對象的實現區分開來,隱藏實現細節。迫使用戶去使用外部界面,即使實現細節改變,還可通過界面承擔其功能而保留原樣,確保調用它的代碼還繼續工作。封裝使代碼維護更簡單。
2.繼承?????????????????????????????????????????????????????????????????????????
is a 關系 —— 子對象
在面向對象世界里面,常常要創建某對象(如:一個職員對象),然后需要一個該基本對象的更專業化的版本,比如,可能需要一個經理的對象。顯然經理實際上是一個職員,經理和職員具有is a的關系,經理只是一個帶有附加特征的職員。因此,需要有一種辦法從現有對象來創建一個新對象。這個方式就是繼承。
“繼承”是面向對象軟件技術當中的一個概念。如果一個對象A繼承自另一個對象B,就把這個A稱為"B的子對象",而把B稱為"A的父對象"。繼承可以使得子對象具有父對象的各種屬性和方法,而不需要再次編寫相同的代碼。在令子對象繼承父對象的同時,可以重新定義某些屬性,并重寫某些方法,即覆蓋父對象的原有屬性和方法,使其獲得與父對象不同的功能。
3.多態?????????????????????????????????????????????????????????????????????????
同一行為的多種不同表達,或者同一行為的多種不同實現就叫做多態。
還是用剛才經理和職員這個例子來舉例:人事部門需要對公司所有職員統一制作胸卡(一般也就是門禁卡,進出公司證明身份使用),制作的師傅說,只要告訴我一個人員的信息,就可以制作出一份胸卡,簡化一下就是:一位職員的信息對應一份胸卡。
這個時候,對胸卡制作的師傅而言,所有的人都是職員,無所謂是經理還是普通職員。也就是說,對于傳遞職員信息這樣一個行為,存在多種不同的實現,既可以傳遞經理的信息,也可以傳遞普通職員的信息。這就是多態的表現。
再舉一個例子:比如我們說“筆”這個對象,它就有很多不同的表達或實現,比如有鋼筆、鉛筆、圓珠筆等等。那么我說“請給我一支筆”,你給我鋼筆、鉛筆或者圓珠筆都可以,這里的“筆”這個對象就具備多態。
?
三、Java類的基本構成???????????????????????????????????????
1.Java類的定義形式?????????????????????????????????????? ????????????????????????
一個完整的Java類通常由下面六個部分組成:
包定義語句?? (一句)
import語句? (若干句)
類定義{
?????? 成員變量
構造方法
?????? 成員方法
}
其中:只有類定義和“{}”是不可或缺的,其余部分都可以根據需要來定義。
下面分別來學習各個部分的基本規則,看看如何寫Java的類。
2.包????????????????????????????????????????????????????? ?????????????????????
2.1 包是什么?
在Java中,包是類、接口或其它包的集合,包主要用來將類組織起來成為組,從而對類進行管理。
2.2 包能干什么?
包對于下列工作非常有用:?????????????????
(1):包允許您將包含類代碼的文件組織起來,易于查找和使用適當的類。
(2):包不止是包含類和接口,還能夠包含其它包。形成層次的包空間。
(3):它有助于避免命名沖突。當您使用很多類時,確保類和方法名稱的唯一性是非常困難的。包能夠形成層次命名空間,縮小了名稱沖突的范圍,易于管理名稱。?
?
為便于管理數目眾多的類,Java語言中引入了“包”的概念,可以說是對定義的Java類進行“分組”,將多個功能相關的類定義到一個“包”中,以解決命名沖突、引用不方便、安全性等問題。
就好似當今的戶籍制度,每個公民除有自己的名字“張三”、“李四”外還被規定了他的戶籍地。假定有兩個人都叫張三,只稱呼名字就無法區分他們,但如果事先登記他們的戶籍分別在北京和上海,就可以很容易的用“北京的張三”、“上海的張三”將他們區分開來。如果北京市仍有多個張三,還可以細分為“北京市.海淀區的張三”、“北京市.西城區.平安大街的張三”等等,直到能惟一標識每個“張三”為止。
JDK中定義的類就采用了“包”機制進行層次式管理,下圖顯示了其組織結構的一部分:
| ? |
| lang包 |
| java包 |
| io 包 |
| System類 |
| String類 |
| Object類 |
從圖中可以看出,一個名為java的包中又包含了兩個子包:io包和lang包。lang包中包含了System, String, Object三個類的定義。事實上,Java包中既可以包含類的定義,也可以包含子包,或同時包含兩者。
簡而言之:從邏輯上講,包是一組相關類的集合;從物理上講,同包即同目錄。
但是千萬注意:不能將一般的目錄看作是包!?
2.1 JDK中常用的包
java.lang —— 包含一些Java語言的核心類,包含構成Java語言設計基礎的類。在此包中定義的最重要的一個類是“Object”,代表類層次的根,Java是一個單根系統,最終的根就是“Object”,這個類會在后面講到。
??? Java并不具有“自由”的方法,例如,不屬于任何類的方法,Java中的所有方法必須始終屬于某個類。經常需要使用數據類型轉換方法。Java在Java.lang包中定義了“包裝對象”類,使我們能夠實現數據類型轉換。如Boolean、Character、Integer、Long、Float和Double,這些在后面會講到。
??? 此包中的其它類包括:
l???????? Math —— 封裝最常用的數學方法,如正弦、余弦和平方根。
l???????? String,StringBuffer —— 封裝最常用的字符串操作。
?? 你不必顯示導入該包,該Java包通常已經導入。
java.awt —— 包含了構成抽象窗口工具集(abstract window toolkits)的多個類,這些類被用來構建和管理應用程序的圖形用戶界面(GUI)。
javax.swing —— 完全Java版的圖形用戶界面(GUI)解決方案,提供了很多完備的組件,可以應對復雜的桌面系統構建。
java.net —— 包含執行與網絡相關的操作的類,如URL, Socket, ServerSocket等。
java.io —— 包含能提供多種輸入/輸出功能的類。
java.util —— 包含一些實用工具類,如定義系統特性、使用與日期日歷相關的方法。還有重要的集合框架。?
2.2 Java中如何表達包 —
package語句
Java語言使用package語句來實現包的定義。package語句必須作為Java源文件的第一條語句,指明該文件中定義的類所在的包。若缺省該語句,則指定為無名包,其語法格式為:
package ?pkg1[.pkg2[.pkg3…]];????????? //“[]”表示可選
Java編譯器把包對應于文件系統的目錄管理,因此包也可以嵌套使用,即一個包中可以含有類的定義也可以含有子包,其嵌套層數沒有限制。package語句中,用‘.’來指明包的層次;
程序package的使用:Test.java
package p1;
public class Test{
public void display(){
????????????? System.out.println("in? method display()");
}
}
Java語言要求包聲明的層次和實際保存類的字節碼文件的目錄結構存在對應關系,以便將來使用該類時能通過包名(也就是目錄名)查找到所需要的類文件。簡單地說就是包的層次結構需要和文件夾的層次對應。
注意:每個源文件只有一個包的聲明,而且包名應該全部小寫。
具體來說,程序員要做以下工作:
2.3 編譯和生成包?
如果在程序Test.java中已定義了包p1,就必須將編譯生成的字節碼文件Test.class保存在與包名同名的子目錄中,可以選用下述兩種方式之一:
采用下述命令編譯:
?????? javac? Test.java
則編譯器會在當前目錄下生成Test.class文件,再在適合位置手動創建一個名為p1的子目錄,將Test.class復制到該p1目錄下。
采用簡化的編譯命令,就是可以帶包編譯
?????? javac -d destpath Test.java??
歸入該包的類的字節代碼文件應放在java的類庫所在路徑的destpath子目錄下。現在包的相對位置已經決定了,但java類庫的路徑還是不定的。事實上,java可以有多個存放類庫的目錄,其中的缺省路徑為java目錄下的lib子目錄,你可以通過使用-classpath選項來確定你當前想選擇的類庫路徑。除此之外,你還可以在CLASSPATH環境變量中設置類庫路徑。destpath為目標路徑,可以是本地的任何絕對或相對路徑。則編譯器會自動在destpath目錄下建立一個子目錄p1,并將生成的.class文件自動保存到destpath/p1下。例如:
javac -d? .\? Test.java
javac -d? C:\test\? Test.java?
2.4 帶包運行?
? 運行帶包的程序,需要使用類的全路徑,也就是帶包的路徑,比如上面的那個程序,就使用如下的代碼進行運行:
? java p1.Test
3.import 語句???????????????????????????????????????????????????? ???????????????????
為了能夠使用某一個包的成員,我們需要在Java程序中明確導入該包。使用“import”語句可完成此功能。在java源文件中import語句應位于package語句之后,所有類的定義之前,可以有0~多條,其語法格式為:
import package1[.package2…].(classname|*);
java運行時環境將到CLASSPATH + package1.[package2…]路徑下尋找并載入相應的字節碼文件classname.class。“*”號為通配符,代表所有的類。也就是說import語句為編譯器指明了尋找類的途徑。
例,使用import語句引入類程序:TestPackage.java
import? p1.Test;?? //或者import p1.*;
public class TestPackage{
public static void main(String args[]){
Test t = new Test();??? //Test類在p1包中定義
t.display();
}
}
java編譯器默認為所有的java程序引入了JDK的java.lang包中所有的類(import java.lang.*;),其中定義了一些常用類:System、String、Object、Math等。因此我們可以直接使用這些類而不必顯式引入。但使用其它非無名包中的類則必須先引入、后使用。
3.1 Java類搜尋方式
程序中的import語句標明要引入p1包中的Test類,假定環境變量CLASSPATH的值為“.;C:\jdk6\lib;D:\ex”,java運行環境將依次到下述可能的位置尋找并載入該字節碼文件Test.class:
.\p1\Test.class
C:\jdk6\lib\p1\Test.class
D:\ex\p1\Test.class
其中,“.”代表當前路徑,如果在第一個路徑下就找到了所需的類文件,則停止搜索。否則依次搜索后續路徑,如果在所有的路徑中都未找到所需的類文件,則編譯或運行出錯。?
4.訪問修飾符????????????????????????????????????????????????????? ???????????????
Java語言允許對類中定義的各種屬性和方法進行訪問控制,即規定不同的保護等級來限制對它們的使用。為什么要這樣做?Java語言引入類似訪問控制機制的目的在于實現信息的封裝和隱藏。Java語言為對類中的屬性和方法進行有效地訪問控制,將它們分為四個等級:private, 無修飾符, protected, public,具體規則如下:
表? Java類成員的訪問控制?
變量和方法可以處于四個訪問級別中的一個:公共,受保護,無修飾符或私有。類可以在公共或無修飾級別。
??? 變量、方法或類有缺省(無修飾符)訪問性,如果它沒有顯式受保護修飾符作為它的聲明的一部分的話。這種訪問性意味著,訪問可以來自任何方法,當然這些方法只能在作為對象的同一個包中的成員類當中。
以修飾符protected標記的變量或方法實際上比以缺省訪問控制標記的更易訪問。一個protected方法或變量可以從同一個包中的類當中的任何方法進行訪問,也可以是從任何子類中的任何方法進行訪問。當它適合于一個類的子類但不是不相關的類時,就可以使用這種受保護訪問來訪問成員。
5.類定義??????? ??????????????????????????????????????????????????????????????
Java程序的基本單位是類,你建立類之后,就可用它來建立許多你需要的對象。Java把每一個可執行的成分都變成類。
類的定義形式如下:
[一般修飾符] ?class ? {
??????????? []
??????????? []
??????????? []
??? }
這里,類名要是合法的標識符。在類定義的開始與結束處必須使用花括號。你也許想建立一個矩形類,那么可以用如下代碼:
public class Rectangle{
??? ......//矩形具體的屬性和方法
}
6.構造方法????????????????????????????????????????????????????? ???????????????
6.1 什么是構造方法?
類有一個特殊的成員方法叫作構造方法,它的作用是創建對象并初始化成員變量。在創建對象時,會自動調用類的構造方法。
6.2 構造方法定義規則
Java中的構造方法必須與該類具有相同的名字,并且沒有方法的返回類型(包括沒有void)。另外,構造方法一般都應用public類型來說明,這樣才能在程序任意的位置創建類的實例--對象。
6.3 示例
下面是一個Rectangle類的構造方法,它帶有兩個參數,分別表示矩形的長和寬:?
public class Rectangle{
int width;
int height;
public Rectangle(int w,int h) {
width=w;
height=h;
}
}
6.4 說明
每個類至少有一個構造方法。如果不寫一個構造方法,Java編程語言將提供一個默認的,該構造方法沒有參數,而且方法體為空。
注意:如果一個類中已經定義了構造方法則系統不再提供默認的構造方法。
7.析構方法????????????????????????????????????????????????????? ????????????????
析構方法finalize的功能是:當對象被從內存中刪除時,該成員方法將會被自動調用。通常,在析構方法內,你可以填寫用來回收對象內部的動態空間的代碼。
特別注意:當我們去調用析構方法的時候,并不會引起該對象實例從內存中刪除,而是不會起到任何作用。
在Java編程里面,一般不需要我們去寫析構方法,這里只是了解一下就可以了。
8.屬性?????????????????????????????????????????????????????????? ??????????????
8.1 屬性是什么?
簡單點說,屬性就是對象所具有的靜態屬性。
8.2 定義規則
?????? Java類中屬性的聲明采用如下格式:?
訪問修飾符 修飾符 類型 屬性名稱=初始值;
訪問修飾符:可以使用四種不同的訪問修飾符中的一種,包括public(公共的)、protected(受保護的),無修飾符和private(私有的)。public訪問修飾符表示屬性可以從任何其它代碼調用。private表示屬性只可以由該類中的其它方法來調用。protected將在以后的課程中討論。
修飾符:是對屬性特性的描述,例如后面會學習到的:static、final等等。
類型:屬性的數據類型,可以是任意的類型。
屬性名稱:任何合法標識符
初始值:賦值給屬性的初始值。如果不設置,那么會自動進行初始化,基本類型使用缺省值,對象類型自動初始化為null。?
8.3 說明
屬性有時候也被稱為成員變量、實例變量、域,它們經常被互換使用。?
9.方法?????????????????????????????????????????????????????????? ??????????????
9.1 方法是什么?
方法就是對象所具有的動態功能。
9.2 定義規則
Java類中方法的聲明采用以下格式:
訪問修飾符 修飾符 返回值類型 方法名稱 (參數列表) throws 異常列表 {方法體}
訪問修飾符:可以使用四種不同的訪問修飾符中的一種,包括public(公共的)、protected(受保護的),無修飾符和private(私有的)。public訪問修飾符表示方法可以從任何其它代碼調用。private表示方法只可以由該類中的其它方法來調用。protected將在以后的課程中討論。
修飾符:是對方法特性的描述,例如后面會學習到的:static、final、abstract、synchronized等等。
返回值類型:表示方法返回值的類型。如果方法不返回任何值,它必須聲明為void(空)。Java技術對返回值是很嚴格的,例如,如果聲明某方法返回一個int值,那么方法必須從所有可能的返回路徑中返回一個int值(只能在等待返回該int值的上下文中被調用。)
方法名稱:可以是任何合法標識符,并帶有用已經使用的名稱為基礎的某些限制條件。
??? 參數列表:允許將參數值傳遞到方法中。列舉的元素由逗號分開,而每一個元素包含一個類型和一個標識符。在下面的方法中只有一個形式參數,用int類型和標識符days來聲明:public void test(int days){}
??? throws 異常列表:子句導致一個運行時錯誤(異常)被報告到調用的方法中,以便以合適的方式處理它。異常在后面的課程中介紹。
花括號內是方法體,即方法的具體語句序列。?
9.3 示例
比如現在有一個“車”的類Car,“車”具有一些基本的屬性,比如四個輪子,一個方向盤,車的品牌等等。當然,車也具有自己的功能,也就是方法,比如車能夠“開動”——run。要想車子能夠開動,需要給車子添加汽油,也就是說,需要為run方法傳遞一些參數“油”進去。車子跑起來過后,我們需要知道當前車輛運行的速度,就需要run方法具有返回值“當前的速度”。
package cn.javass.javatest;
public class Car {// 車這個類
??? private String make;// 一個車的品牌
??? private int tyre;// 一個車具有輪胎的個數
??? private int wheel;// 一個車具有方向盤的個數?
??? public Car() {
?????? // 初始化屬性
?????? ?make = "BMW";// 車的品牌是寶馬
?????? ?tyre = 4;// 一個車具有4個輪胎
?????? ?wheel = 1;// 一個車具有一個方向盤
??? }
???
??? public double run(int oil) {
?????? // 進行具體的功能處理
?????? return 200.0;
??? }
}
9.4 形參和實參
形參:就是形式參數的意思。是在定義方法名的時候使用的參數,用來標識方法接收的參數類型,在調用該方法時傳入。
實參:就是實際參數的意思。是在調用方法時傳遞給該方法的實際參數。?
比如:上面的例子中“int oil”就是個形式參數,這里只是表示需要加入汽油,這個方法才能正常運行,但具體加入多少,要到真正使用的時候,也就是調用這個方法的時候才具體確定,加入調用的時候傳入“80”,這就是個實際參數。
形參和實參有如下基本規則:
(1)形參和實參的類型必須要一致,或者要符合隱含轉換規則
(2)形參類型不是引用類型時,在調用該方法時,是按值傳遞的。在該方法運行時,形參和實參是不同的變量,它們在內存中位于不同的位置,形參將實參的值復制一份,在該方法運行結束的時候形參被釋放,而實參內容不會改變。
(3)形參類型是引用類型時,在調用該方法時,是按引用傳遞的。運行時,傳給方法的是實參的地址,在方法體內部使用的也是實參的地址,即使用的就是實參本身對應的內存空間。所以在函數體內部可以改變實參的值。?
9.5 參數可變的方法?? (學習了數組和包裝類后再學習此內容)
從JDK5.0開始,提供了參數可變的方法。
當不能確定一個方法的入口參數的個數時,5.0以前版本的Java中,通常的做法是將多個參數放在一個數組或者對象集合中作為參數來傳遞,5.0版本以前的寫法是:
int sum(Integer[] numbers){…}
//在別處調用該方法
sum(new Integer[] {12,13,20});?
而在5.0版本中可以寫為:
int sum(Integer... numbers){//方法內的操作}
注意:方法定義中是三個點
//在別處調用該方法
sum(12,13,20);//正確
sum(10,11); //正確
也就是說,傳入參數的個數并不確定。但請注意:傳入參數的類型必須是一致的,究其本質,就是一個數組。
顯然,JDK5.0版本的寫法更為簡易,也更為直觀,尤其是方法的調用語句,不僅簡化很多,而且更符合通常的思維方式,更易于理解。?
四、如何使用一個Java類??????????????????????????????? ???????
前面學習了如何定義一個類,下面來學習如何使用一個類。
1.new關鍵字?????????????????????????????????????????????????????????? ??????????????
??? 假如定義了一個表示日期的類,有三個整數變量;日、月和年的意義即由這些整數變量給出。如下所示:
?????? class MyDate? {
????? ????? int day;
????? ????? int month;
????? ????? int year;
????? }
名稱MyDate按照大小寫的有關約定處理,而不是由語意要求來定。???
那么怎么來使用這個類呢:在你可以使用變量之前,實際內存必須被分配。這個工作是通過使用關鍵字new來實現的。如下所示:
???? ?在一個方法體中,聲明
????? MyDate today;
????? today = new MyDate();?????
第一個語句(聲明)僅為引用分配了足夠的空間,而第二個語句則通過調用對象的構造方法為構成MyDate的三個整數分配了空間。對象的賦值使變量today重新正確地引用新的對象。這兩個操作被完成后,MyDate對象的內容則可通過today進行訪問。
??? 關鍵字new意味著內存的分配和初始化,new 調用的方法就是類的構造方法。
??? 使用一個語句同時為引用today和由引用today所指的對象分配空間也是可能的。
????? MyDate today = new MyDate();
2.如何使用對象中的屬性和方法?????????????????????????????????????????????????????????? ????????
?????? 要調用對象中的屬性和方法,使用“.”操作符。
例如:
???? today.day? =? 26;
???? today.month = 7;
???? today.year = 2008;?
3.this關鍵字????????????????? ?????????????????????????????????????????????????
關鍵字this是用來指向當前對象或類實例的,功能說明如下:
3.1 點取成員
this.day指的是調用當前對象的day字段,示例如下:
public class MyDate {
? private int day, month, year;
? public void tomorrow() {
??? this.day = this.day + 1;
? ? ??//其他代碼
? }
}?
??? Java編程語言自動將所有實例變量和方法引用與this關鍵字聯系在一起,因此,使用關鍵字在某些情況下是多余的。下面的代碼與前面的代碼是等同的。
public class MyDate {
? private int day, month, year;
? public void tomorrow() {
?? ?day = day + 1; // 在day前面沒有使用this
??? //其他代碼
? }
}
3.2 區分同名變量
? 也有關鍵字this使用不多余的情況。如,需要在某些完全分離的類中調用一個方法,并將當前對象的一個引用作為參數傳遞時。例如:
Birthday bDay = new Birthday (this);
還有一種情況,就是在類屬性上定義的變量和方法內部定義的變量相同的時候,到底是調用誰呢?例如:
public class Test{
? int i = 2;
? public void t(){
??? int i = 3; //跟屬性的變量名稱是相同的
??? System.out.println(“實例變量i=”+ this.i);
??? System.out.println(“方法內部的變量i=”+ i);
? }
}?
也就是說:“this.變量”調用的是當前屬性的變量值,直接使用變量名稱調用的是相對距離最近的變量的值。?
3.3 作為方法名來初始化對象
? 也就是相當于調用本類的其它構造方法,它必須作為構造方法的第一句。示例如下:
public class Test {
??? public Test(){
?????? this(3);//在這里調用本類的另外的構造方法
??? }
??? public Test(int a){????
??? }
??? public static void main(String[] args) {
?????? Test t = new Test();
??? }??
}?
五、引用類型????????????????????????????????????????????????
1.引用類型是什么????????????????? ????????????????????????????????????????????????
一般引用類型(reference type)指向一個對象,不是原始值,指向對象的變量是引用變量。
在Java里面除去基本數據類型的其它類型都是引用數據類型。Java程序運行時,會為引用類型分配一定量的存儲空間并解釋該存儲空間的內容。
示例如下:
public class MyDate{
?? private int day=8;
?? private int month=8;
?? private int year=2008;
?
?? public MyDate(int day, int month, int year){…}
?? public void print(){…}
}
public class TestMyDate{
?? public static void main(String args[]){
???? MyDate today = new MyDate(23,7,2008);//這個today變量
//就是一個引用類型的變量
?? }
}
?
2.引用類型的賦值????????????????? ????????????????????????????????????????????????
??? 在Java編程語言中,用類的一個類型聲明的變量被指定為引用類型,這是因為它正在引用一個非原始類型,這對賦值具有重要的意義。請看下列代碼片段:
???? int x = 7;
???? int y = x;
???? String s = “Hello”;
???? String t = s;
??? 四個變量被創建:兩個原始類型 int 和兩個引用類型String。x的值是7,而這個值被復制到y;x 和 y是兩個獨立的變量且其中任何一個的進一步的變化都不對另外一個構成影響。
至于變量 s 和 t,只有一個String 對象存在, 它包含了文本“Hello”,s和 t均引用這個單一的對象。
將變量t 重新定義為:t=”World”; 則新的對象World被創建,而 t 引用這個對象。上述過程被描述如下?
3.按值傳遞還是按引用傳遞????????????? ?????????????????????????????????????????????
這個在Java里面是經常被提起的問題,也有一些爭論,似乎最后還有一個所謂的結論:“在Java里面參數傳遞都是按值傳遞”。事實上,這很容易讓人迷惑,下面先分別看看什么是按值傳遞,什么是按引用傳遞,只要能正確理解,至于稱作按什么傳遞就不是個大問題了。
3.1 按值傳遞是什么?
指的是在方法調用時,傳遞的參數是按值的拷貝傳遞。示例如下:
public class TempTest {
??? private void test1(int a){
?????? //做點事情
??? }??
??? public static void main(String[] args) {
?????? TempTest t = new TempTest();
?????? int a = 3;
?????? t.test1(a);//這里傳遞的參數a就是按值傳遞
??? }??
}
按值傳遞重要特點:傳遞的是值的拷貝,也就是說傳遞后就互不相關了。
示例如下:
public class TempTest {
??? private void test1(int a){
?????? a = 5;
?????? System.out.println("test1方法中的a==="+a);
??? }??
??? public static void main(String[] args) {
?????? TempTest t = new TempTest();
?????? int a = 3;
?????? t.test1(a);//傳遞后,test1方法對變量值的改變不影響這里的a
?????? System.out.println("main方法中的a==="+a);
??? }??
}
運行結果是:
test1方法中的a===5
main方法中的a===3?
3.2 按引用傳遞是什么?
指的是在方法調用時,傳遞的參數是按引用進行傳遞,其實傳遞的引用的地址,也就是變量所對應的內存空間的地址。
示例如下:
public class TempTest {
??? private void test1(A a){??????????
??? }??
??? public static void main(String[] args) {
?????? TempTest t = new TempTest();
?????? A a = new A();
?????? t.test1(a); //這里傳遞的參數a就是按引用傳遞
??? }??
}
class A{
??? public int age = 0;
}
3.3 按引用傳遞的重要特點
傳遞的是值的引用,也就是說傳遞前和傳遞后都指向同一個引用(也就是同一個內存空間)。
示例如下:
第1行 public class TempTest {
第2行? private void test1(A a){
第3行????? a.age = 20;
第4行????? System.out.println("test1方法中的age="+a.age);
第5行? }??
第6行? public static void main(String[] args) {
第7行????? TempTest t = new TempTest();
第8行????? A a = new A();
第9行????? a.age = 10;
第10行???? t.test1(a);
第11行???? System.out.println("main方法中的age="+a.age);
第12行 }??
第13行 }
第14行 class A{
第15行 public int age = 0;
第16行 }
運行結果如下:
test1方法中的age=20
main方法中的age=20?
3.4 理解按引用傳遞的過程——內存分配示意圖
要想正確理解按引用傳遞的過程,就必須學會理解內存分配的過程,內存分配示意圖可以輔助我們去理解這個過程。
用上面的例子來進行分析:
(1):運行開始,運行第8行,創建了一個A的實例,內存分配示意如下:
| 這是一個A的實例 此時age = 0; |
main方法中的變量a???
(2):運行第9行,是修改A實例里面的age的值,運行后內存分配示意如下:?
| 這是一個A的實例 此時age = 10; |
(3):運行第10行,是把main方法中的變量a所引用的內存空間地址,按引用傳遞給test1方法中的a變量。請注意:這兩個a變量是完全不同的,不要被名稱相同所蒙蔽。
內存分配示意如下:
main方法中的變量a?
賦值給? (按引用傳遞)?
test1方法中的變量a?
由于是按引用傳遞,也就是傳遞的是內存空間的地址,所以傳遞完成后形成的新的內存示意圖如下:
main方法中的變量a?
test1方法中的變量a?
也就是說:是兩個變量都指向同一個空間。
(4):運行第3行,為test1方法中的變量a指向的A實例的age進行賦值,完成后形成的新的內存示意圖如下:?
main方法中的變量a?
test1方法中的變量a
此時A實例的age值的變化是由test1方法引起的
(5):運行第4行,根據此時的內存示意圖,輸出test1方法中的age=20
(6):運行第11行,根據此時的內存示意圖,輸出main方法中的age=20?
3.5 對上述例子的改變
理解了上面的例子,可能有人會問,那么能不能讓按照引用傳遞的值,相互不影響呢?就是test1方法里面的修改不影響到main方法里面呢?
方法是在test1方法里面新new一個實例就可以了。改變成下面的例子,其中第3行為新加的:?
第1行 public class TempTest {
第2行? private void test1(A a){
第3行????? a = new A();//新加的一行
第4行????? a.age = 20;
第5行????? System.out.println("test1方法中的age="+a.age);
第6行? }??
第7行? public static void main(String[] args) {
第8行????? TempTest t = new TempTest();
第9行????? A a = new A();
第10行???? a.age = 10;
第11行???? t.test1(a);
第12行???? System.out.println("main方法中的age="+a.age);
第13行 }??
第14行}
第15行class A{
第16行 public int age = 0;
第17行}?
運行結果為:
test1方法中的age=20
main方法中的age=10?
為什么這次的運行結果和前面的例子不一樣呢,還是使用內存示意圖來理解一下?
3.6 再次理解按引用傳遞
(1) 運行開始,運行第9行,創建了一個A的實例,內存分配示意如下:?
main方法中的變量a?????
(2) 運行第10行,是修改A實例里面的age的值,運行后內存分配示意如下:?
main方法中的變量a?
???(3)運行第11行,是把main方法中的變量a所引用的內存空間地址,按引用傳遞給test1方法中的a變量。請注意:這兩個a變量是完全不同的,不要被名稱相同所蒙蔽。
內存分配示意如下:??
| 這是一個A的實例 此時age = 10; |
賦值給? (按引用傳遞)?
test1方法中的變量a?
由于是按引用傳遞,也就是傳遞的是內存空間的地址,所以傳遞完成后形成的新的內存示意圖如下:?
main方法中的變量a
test1方法中的變量a?
也就是說:是兩個變量都指向同一個空間。?
(4)運行第3行,為test1方法中的變量a重新生成了新的A實例的,完成后形成的新的內存示意圖如下:?
| 這是一個A的實例 此時age = 10; |
main方法中的變量a??
test1方法中的變量a?
(5)運行第4行,為test1方法中的變量a指向的新的A實例的age進行賦值,完成后形成的新的內存示意圖如下:?
main方法中的變量a?
test1方法中的變量a??
注意:這個時候test1方法中的變量a的age被改變,而main方法中的是沒有改變的。?
(6) 運行第5行,根據此時的內存示意圖,輸出test1方法中的age=20
(7) 運行第12行,根據此時的內存示意圖,輸出main方法中的age=10?
3.7 說明
(1)“在Java里面參數傳遞都是按值傳遞”這句話的意思是:按值傳遞是傳遞的值的拷貝,按引用傳遞其實傳遞的是引用的地址值,所以統稱按值傳遞。
(2)在Java里面只有基本類型和按照下面這種定義方式的String是按值傳遞,其它的都是按引用傳遞。就是直接使用雙引號定義字符串方式:String str = “Java私塾”;??
六、再談變量???????????????????????????????????????????????
1.實例變量和局部變量????????????????????? ???????????????????????????????????????
在方法外定義的變量主要是實例變量(不帶static 修飾的),它們是在使用new Xxxx ()創建一個對象時被分配內存空間的。每當創建一個對象時,系統就為該類的所有實例變量分配存儲空間;創建多個對象就有多份實例變量。通過對象的引用就可以訪問實例變量。
在方法內定義的變量或方法的參數被稱為局部(local)變量,有時也被用為自動(automatic)、臨時(temporary)或棧(stack)變量。
??? 方法參數變量定義在一個方法調用中傳送的自變量,每次當方法被調用時,一個新的變量就被創建并且一直存在到程序的運行跳離了該方法。
當執行進入一個方法遇到局部變量的聲明語句時,局部變量被創建,當執行離開該方法時,局部變量被取消,也就是該方法結束時局部變量的生命周期也就結束了。
因而,局部變量有時也被引用為“臨時或自動”變量。在成員方法內定義的變量對該成員變量是“局部的”,因而,你可以在幾個成員方法中使用相同的變量名而代表不同的變量。該方法的應用如下所示:
public class Test {
??? private int i; // Test類的實例變量
??? public int firstMethod() {
?????? int j = 1; // 局部變量
?????? // 這里能夠訪問i和j
?????? System.out.println("firstMethod 中 i="+i+",j="+j);
?????? return 1;
??? } // firstMethod()方法結束?
??? public int secondMethod(float f) { //method parameter
?????? int j = 2; //局部變量,跟firstMethod()方法中的j是不同的
?????? // 這個j的范圍是限制在secondMethod()中的
?????? // 在這個地方,可以同時訪問i,j,f
?????? System.out.println("secondMethod 中 i="+i+",j="+j+",f="+f);
?????? return 2;
??? }
??? public static void main(String[] args) {
?????? Test t = new Test();
?????? t.firstMethod();
?????? t.secondMethod(3);
??? }
}?
2.變量初始化???????????????????????????????????????????????????????? ???????????
在Java程序中,任何變量都必須經初始化后才能被使用。當一個對象被創建時,實例變量在分配內存空間時按程序員指定的初始化值賦值,否則系統將按下列默認值進行初始化:
| byte | 0 |
| short | 0 |
| int | 0 |
| long | 0L |
| float | 0.0f |
| double | 0.0d |
| char | '\u0000' |
| boolean | false |
| 所有引用類型 | null |
注意── 一個具有空值null的引用不引用任何對象。試圖使用它引用的對象將會引起一個異常。異常是出現在運行時的錯誤,這將在模塊“異常”中討論。
在方法外定義的變量被自動初始化。局部變量必須在使用之前做“手工”(由程序員進行)初始化。如果編譯器能夠確認一個變量在初始化之前可能被使用的情形,編譯器將報錯。
public class Test {
private int i; //Test類的實例變量
??? public void test1() {
?????? int x = (int) (Math.random() * 100);
?????? int y;
?????? int z;
?????? if (x > 50) {
?????????? y = 9;
?????? }
?????? z = y + x; // 將會引起錯誤,因為y可能還沒有被初始化就使用了
??? }
??? public static void main(String[] args) {
?????? Test t = new Test();
?????? t.test1();
??? }
}
3.變量的范圍(scope)???????????????????????????????????????????????????????? ??????????
Java變量的范圍有四個級別:類級、對象實例級、方法級、塊級。
(1)類級變量又稱全局級變量,在對象產生之前就已經存在,就是后面會學到的static變量。
(2)對象實例級,就是前面學到的屬性變量
(3)方法級:就是在方法內部定義的變量,就是前面學到的局部變量。
(4)塊級:就是定義在一個塊內部的變量,變量的生存周期就是這個塊,出了這個塊就消失了。
示例如下:
public class Test {
??? private static String name="Java私塾";//類級
??? private int i; // 對象實例級,Test類的實例變量
??? {//屬性塊,在類初始化屬性時候運行
?????? int j = 2;//塊級?????
??? }
??? public void test1() {
?????? int j = 3;//方法級
?????? if(j==3){
?????????? int k = 5;//塊級
?????? }
?????? //這里不能訪問塊級的變量,塊級變量只能在塊內部訪問
?????? System.out.println("name="+name+",i="+i+",j="+j);
??? }
??? public static void main(String[] args) {
?????? Test t = new Test();
?????? t.test1();
??? }
}
運行結果:
name=Java私塾,i=0,j=3
3.1 訪問說明
(1)方法內部除了能訪問方法級的變量,還可以訪問類級和實例級的變量
(2)塊內部能夠訪問類級、實例級變量,如果塊被包含在方法內部,它還可以訪問方法級的變量。
(3)變量當然是要在被訪問前被定義和初始化,不能訪問后面才定義的變量。?
七、包裝類?????????????????????????????????????????? ???????
雖然Java語言是典型的面向對象編程語言,但其中的8種基本數據類型并不支持面向對象的編程機制,基本類型的數據不具備“對象”的特性——不攜帶屬性、沒有方法可調用。沿用它們只是為了迎合人類根深蒂固的習慣,并的確能簡單、有效地進行常規數據處理。
這種借助于非面向對象技術的做法有時也會帶來不便,比如引用類型數據均繼承了Object類的特性,要轉換為String類型(經常有這種需要)時只要簡單調用Object類中定義的toString()即可,而基本數據類型轉換為String類型則要麻煩得多。為解決此類問題,Java語言引入了封裝類的概念,在JDK中針對各種基本數據類型分別定義相應的引用類型,并稱之為包裝類(Wrapper Classes)。
下表描述了基本數據類型及對應的包裝類
| 基本數據類型 | 對應的包裝類 |
| boolean | Boolean |
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| char | Character |
| float | Float |
| double | Double |
?
每個包裝類的對象可以封裝一個相應的基本類型的數據,并提供了其它一些有用的功能。包裝類對象一經創建,其內容(所封裝的基本類型數據值)不可改變。
例,包裝類用法程序:Wrapper.java
public class Wrapper{
?????? public static void main(String args[]){
????????????? int i = 500;
????????????? Integer t = new Integer(i);
????????????? int j = t.intValue();????????? // j = 500
????????????? String s = t.toString(); ? // s = "500"
????????????? System.out.println(t);
????????????? Integer t1 = new Integer(500);
????????????? System.out.println(t.equals(t1));
?????? }
}
程序運行結果為:
500
true?
包裝類一個常用的功能就是把字符串類型的數據造型成為對應的基本數據類型,如下示例:
String str = "123";
??? int a = Integer.parseInt(str);
更過的功能還請查看JDK文檔。
八、類型轉換???????????????????????????????????? ????????????
在賦值的信息可能丟失的地方,編譯器需要程序員用類型轉換(type cast)的方法確認賦值。Java中的類型轉換分成:強制類型轉換、自動升級類型轉換和后面將會學習到的向上造型。
1.強制類型轉換???????????????????????????????????????????????????????? ????????????
把某種類型強制轉換成另外一種類型就叫做強制類型轉換。
例如,可以將一個long值“擠壓”到一個int變量中。顯式轉型做法如下:
long? bigValue = 99L;
int? squashed = (int)(bigValue);
在上述程序中,期待的目標類型被放置在圓括號中,并被當作表達式的前綴,該表達式必須被更改。一般來講,建議用圓括號將需要轉型的全部表達式封閉。否則,轉型操作的優先級可能引起問題。
注意:強制類型轉換只能用在原本就是某個類型,但是被表示成了另外一種類型的時候,可以把它強制轉換回來。強制轉換并不能在任意的類型間進行轉換。
比如上面的例子:99這個數本來就是一個int的數,但是它通過在后面添加L來表示成了一個long型的值,所以它才能夠通過強制轉換來轉換回int類型。
2.升級和表達式的類型轉換???????????????????????????????????????????????????????? ?????????
當沒有信息丟失時,變量可被自動升級為一個較長的形式(如:int至long的升級)
??? long bigval = 6; // 6 是int 類型, OK
??? int smallval = 99L; // 99L 是 long 型, 非法
??? double z = 12.414F; // 12.414F 是 float型, OK
??? float z1 = 12.414; // 12.414 是 double型, 非法
一般來講,如果變量類型至少和表達式類型一樣大(位數相同),則你可認為表達式是賦值兼容的。
3.表達式的升級類型轉換???????????????????????????????????????????????????????
對 + 運算符來說,當兩個操作數是原始數據類型時,其結果至少有一個int,并且有一個通過提升操作數到結果類型,或通過提升結果至一個較寬類型操作數而計算的值,這可能會導致溢出或精度丟失。例如:
short? a,b,c;
a=1;
b=2;
c= a+b;
上述程序會出錯是因為在執行“+”操作前,a和b會從short提升至int,兩個int相加的結果也是int,然后把一個int的值賦值給c,但是c是short型的,所以出錯。如果c被聲明為一個int,或按如下操作進行類型轉換:
?? c = (short)(a+b);
則上述代碼將會成功通過。
?????? 尤其在四則運算表達式里面,如果不強制進行類型轉換,那么運算最后的結果就是精度最高的那個操作數決定的。比如:
3*5.0的結果就是double型的,應該定義成為:double? a = 3 * 5.0;
4.自動包裝(裝箱)和解包(拆箱)??????????????????????????????????????????????????????????????
自動包裝:就是把基礎數據類型自動封裝并轉換成對應的包裝類的對象。
自動解包:就是把包裝類的對象自動解包并轉換成對應的基礎數據類型。
示例如下:
public class Test {
??? public static void main(String args[]) {
??? ??? Integer a1 = 5;//自動包裝
?????? int a2 = new Integer(5);//自動解包
?????? System.out.println("a1="+a1+",a2="+a2);
??? }
}
運行結果:a1=5,a2=5?
九、Java類的基本運行順序???????????????????????????????????
作為程序員,應該對自己寫的程序具備充分的掌控能力,應該清楚程序的基本運行過程,否則糊里糊涂的,不利于對程序的理解和控制,也不利于技術上的發展。
我們以下面的類來說明一個基本的Java類的運行順序:?
第1行 public class Test {
第2行?? private String name = "Java私塾";
第3行?? private int age = 2;
第4行?? public Test(){
第5行????? age = 1000;//期望能到1000年,呵呵
第6行?? }
第7行?? public static void main(String[] args) {
第8行????? Test t = new Test();
第9行????? System.out.println(t.name+"的年齡是"+t.age+"年");
第10行? }??
第11行 }
運行的基本順序是:
(1):先運行到第7行,這是程序的入口
(2):然后運行到第8行,這里要new一個Test,就要調用Test的構造方法
(3):就運行到第4行,注意:可能很多人覺得接下來就應該運行第5行了,錯!初始化一個類,必須先初始化它的屬性
(4):因此運行到第2行,然后是第3行
(5):屬性初始化完過后,才回到構造方法,執行里面的代碼,也就是第5行
(6):然后是第6行,表示new一個Test實例完成
(7):然后回到main方法中執行第9行
(8):然后是第10行?
運行的結果是:Java私塾的年齡是1000年
說明:這里只是說明一個基本的運行過程,沒有考慮更多復雜的情況。??
作業?????????????????????????????????
1.當你試圖編譯和執行下面的程序時會發生什么?
class Mystery{
?? String s;
?? public static void main(String[] args){
???? Mystery m=new Mystery();
???? m.go();?? ??
?? }
?? void Mystery(){
???? s="constructor";
?? }
?? void go(){
???? System.out.println(s);
?? }
}
選擇下面的正確答案:
A 編譯不通過
B 編譯通過但運行時產生異常
C 代碼運行但屏幕上看不到任何東西
D 代碼運行,屏幕上看到 constructor
E 代碼運行,屏幕上看到 null?
以下是編程題:
1.編寫一個名為MyPoint類,其中含有int類型的x 和y 屬性,并寫出有參和無參的兩個構造方法,以及getX和setX、getY 和setY 方法,再重寫toString方法用來顯示對象的x、y的值,如顯示(1,2),然后進行調試。?
2.將上面的MyPoint類對象x和y屬性的值由命令行輸入,根據命令行參數個數,若不帶任何命令行參數,則顯示(0,0);若傳一個參數,則打印(此參數值,0);若傳兩個參數,則打印(第一個參數值,第二個參數值)。?
3.設計一個銀行帳戶類(Account),具有戶名(accountName)、帳號(accountNo)、余額(balance)等屬性,以及存款(deposit)、取款(withdraw)等方法,并對此類進行測試。?
4.設計個Circle類,其屬性為圓心點(類型為前面設計的類MyPoint)和半徑,并為此類編寫以下三個方法:
??? 一是計算圓面積的calArea()方法;
??? 二是計算周長的calLength();
??? 三是boolean inCircle(MyPoint mp)方法,功能是測試作為參數的某個點是否在當前對象圓內(圓內,包括圓上返回true;在圓外,返回false)。
5.創建一個桌子Table類,該類中與桌子名稱、重量、桌面寬度、長度和桌子高度屬性,
???? 以及以下幾個方法。
???? 1) area(): 計算桌面的面積。
???? 2) display(): 在屏幕上輸出所有成員變量的值。
???? 3) changeWeight(int w): 改變桌子重量。?????????????????
???? 然后在main放中實現創建一個桌子對象,計算桌面的面積,改變桌子重量,并在屏幕上輸出所有桌子屬性的值。????
???? 上面任務完成后,再為table類添加幾個構造方法,并在main方法中調用不同的構造方法,創建幾張桌子。然后進行調試。
6.編寫一個MyArray 類,其中添加一個屬性為整型數組,再添加一個構造方法對數組賦初值,以及為該類添加數組求和方法,返回求和得到的值。最后寫一個測試類利用MyArray類計算數組的求和值并輸出。
Java私塾跟我學系列——JAVA篇? 網址:http://www.javass.cn? 電話:010-68434236
來自 “ ITPUB博客 ” ,鏈接:http://blog.itpub.net/26660100/viewspace-715589/,如需轉載,請注明出處,否則將追究法律責任。
轉載于:http://blog.itpub.net/26660100/viewspace-715589/
總結
以上是生活随笔為你收集整理的Java私塾跟我学系列——JAVA篇 第四章Java类和对象的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MP3剪切器
- 下一篇: android高品质游戏,看看你out没