JAVA物件導向基礎
一、類與例項
物件:萬物皆物件,所有的東西都是物件,物件是一個自包含的實體,用一組可識別的特性和行為來標識。
類:是具有相同屬性和功能的物件的抽象集合
例項:就是一個真實的物件
例項化:建立物件的過程,使用new關鍵字來建立
public void Test()
{
Cat cat = new Cat();//將Cat類例項化
}
'Cat cat = new Cat();'做了兩件事
Cat cat;//宣告一個Cat的物件,物件名為cat
cat = new Cat();//將此cat物件例項化
二、構造方法
構造方法又叫建構函式,就是對類進行初始化。與類同名,無返回值,不需要void,在new的時候呼叫。
Cat cat = new Cat()
其中,Cat()就是構造方法。
所有類都有構造方法,不定義構造方法系統會預設生產空的構造方法。定義構造方法,則預設的構造就方法會消失。
public class Cat
{
//宣告Cat類的私有字串變數name
private String name = "";
//定義Cat類的構造方法,引數是輸入一個字串。
public Cat(String name)
{
this.name = name;//將引數賦值給私有變數name
}
}
三、方法過載
方法過載提供建立同名的多個方法的能力,但是這些方法需使用不同的引數型別,不只構造方法可以過載,普通方法也可以過載。
方法名相同,引數型別或個數必須要有所不同。
方法過載可在不改變原方法的基礎上,新增功能。
public class Cat
{
private String name = "";
public Cat(String name)
{
this.name = name;
}
//將構造方法過載
public Cat()
{
this.name = "無名";
}
}
四、屬性與修飾符
屬性
屬性:是一個方法或一對方法,在呼叫它的程式碼看來,它是一個欄位,即屬性適合於以欄位的方式使用方法呼叫的場合。
欄位:是儲存類要滿足其設計所需要的資料,欄位都是與類相關的變數。
//宣告一個內部欄位,private,預設為3
private int shoutNum = 3;
//ShoutNum屬性,注意是public
public int ShoutNum
{
//get表示外界呼叫時可以得到shoutNum的值
public int get(){
return shoutNum;
}
//set表示外界可以給內部的shoutNum賦值
public void set(int value){
shoutNum = value;
}
}
屬性有兩個方法,get和set。
get:訪問器返回與宣告的屬性相同的資料型別,呼叫時可以得到內部的欄位的值或引用。
set:呼叫屬性時可以給內部的欄位或引用賦值。
修飾符
public:表示它所修飾的類成員可以允許其它任何類來訪問,俗稱公有的。
private:表示只允許同一個類中的成員訪問,其它類包括它的子類無法訪問,俗稱私有的。
通常欄位都是private,即私有變數(一般是首字母小寫或前加‘_’),而屬性都是public,即公有變數(首字母大寫)。
final
- 修飾變數:修飾變數時必須賦初值且不呢改變,修飾引用變數不能再指向其他物件。
- 修飾方法:方法前面加上final關鍵字,代表這個方法不可被子類重寫。
- 修飾類:表示這個類不能被繼承,類中的成員可以根據需要設為final,final類中的所有成員方法都會被隱飾地指定為final方法。
注:類的private方法會被隱飾地指定為final
protected:繼承的子類可以對基類(父類)有完全訪問權。對子類公開,不對其它類公開。
五、封裝
每個物件都包含它能進行操作所需的所有資訊,因此物件不必依賴其它物件來完成自己的操作,能夠避免物件屬性賦值的隨意性。
封裝的好處:
- 良好的封裝能減少耦合(相互作用相互影響的關係)。
- 類內部的實現可以自由地修改。
- 具有清晰的對外介面。(對外的屬性和方法)set和get方法。
this關鍵字是對當前內的簡化。
提供專門對外的set和get方法對屬性進行設定,而不能用類.屬性的方法修改值。
public class Student{
private int age;//age屬性私有化
public Student(int age){
this.age = age;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
}
六、繼承
java只支援單繼承,不允許多繼承。
子類繼承父類:
- 子類擁有父類所有的屬性和方法(包括私有屬性和私有方法),但是父類中的私有屬性和方法子類無法訪問,只是擁有。
- 子類擁有自己的屬性和方法,即子類可以在父類的基礎上做擴充套件。
- 子類可以用自己的方式重寫父類的方法。
package Test;
class Students {
int age;//id屬性私有化
String name;//name屬性私有化
Students(int age,String name){
this.age = age;
this.name = name;
}
public void printInfo() {
System.out.println("父類方法 年齡:"+age+" 姓名:"+name);
}
}
class Student extends Students{
Student(int age, String name) {
super(age, name);
}
//重寫父類同名方法
@Override
public void printInfo() {
System.out.println("子類方法 年齡:"+age+" 姓名:"+name);
}
//使用super呼叫父類的方法
public void FatherprintInfo() {
super.printInfo();
}
}
public class test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Student s2 = new Student(19,"張三");
s2.printInfo();
s2.FatherprintInfo();
}
}
//子類方法 年齡:19 姓名:張三
//父類方法 年齡:19 姓名:張三
super關鍵字
- super用於在子類呼叫父類的重名方法(不重名也能引用)。
- super在子類中引用重名的變數(不重名也能引用)。
- super()可以用於直接呼叫父類的構造方法。
繼承的優點
繼承使得所有子類公共的的部分都放在了父類,使得程式碼得到了共享,就可以避免重複,繼承使得修改和擴充套件繼承而來的實現都較為容易。
繼承的缺點
繼承也是有缺點的,那就是父類做出修改,子類就不得不進行改變。另外,繼承也會破壞包裝,父類實現的細節暴露給子類。
總結:只有合理的應用繼承才能發揮好的作用。例如貓繼承動物,而動物不能繼承貓。
七、多型的淺理解
多型表示不同的物件執行相同的操作,但是要通過自己的實現程式碼來執行。
注意點:
- 子類以父類的身份出現。
- 子類在工作時以自己的方式來實現。
- 子類以父類身份出現時,子類特有的屬性和方法不可以使用,只能用父類存在的方法。
多型有兩種情形
編譯時多型:
- 過載(overload)多個同名的不同方法。
執行時多型:
- 覆蓋(override),子類對父類方法進行覆蓋(重寫)。
- 動態繫結--也稱為虛方法呼叫,正真的方法在執行時才確定。
- 在呼叫方法時,程式會正確地呼叫子類物件的方法。
多型中成員的特點
1.多型成員變數:編譯執行看左邊。
People p = new Student();
System.out.println(p.name)//p是People中的值,只能取到父類中的值。
2.多型成員方法:編譯看左邊,執行看右邊。
People p1 = new Student();
System.out.println(p1.printInfo())//p1的表面上型別是People,但實際上是Student,所以呼叫的是重寫後的方法。
instancof關鍵字
用於判斷某個物件是否屬性某種資料型別。
返回型別為布林型別。
People p1 = new Student();
System.out.println(p1 instancof Student);
//true
多型的轉型
向上轉型
將子類物件賦值給父類變數,稱之為向上轉型。
//People[] people = {new Student(19,"張三"), new Teacher(30,"老師A")};
People people[];//宣告一個人類陣列
people = new People[1];//例項化最多兩個人類物件
//向上轉型
people[0] = new Student(19,"張三");
people[1] = new Teacher(30,"老師A");
//foreach遍歷物件
for(People item : people)
{
item.printInfo();
}
//年齡:19 姓名:張三
//年齡:30 姓名:老師A
向下轉型
將父類變數轉換為子類變數,稱之為向下轉型。向下轉型格式如下:
子類型別 變數名 = (子類型別) 父類型別的變數
for(int i=0;i<people.length;i++){
if(people[i] instanceof Student){
//向下轉型
Student student = (Student) people[i];
}else if(people[i] instanceof Teacher){
Teacher teacher = (Teacher) people[i];
}else{
System.out.println("程式錯誤。");
}
}
八、抽象類
仔細觀察,你會發現,People類其實根本不能例項化,一個學生長什麼樣子,可以想象。new People;即例項化一個人,一個人長什麼樣?
People是一個抽象的名詞,沒有其具體物件與之對應。
Java執行把類和方法宣告為abstract,即抽象類和抽象方法。
abstract class People{//加abstract關鍵字,表面抽象類。
......
//在方法返回值前加abstract表明此方法是抽象方法,抽象方法沒有方法體,直接在括號後面加上";"
protected abstract void printInfo();
}
抽象類注意點:
- 抽象類不能例項化。
- 抽象方法必須被子類重寫。
- 如果類中有抽象方法,那麼類就必須定義為抽象類,不論是否還包含其它的一般方法。
讓抽象類擁有儘可能多的共同程式碼,擁有儘可能少的資料。
抽象類通常代表一個抽象的概念,它提供一個繼承的出發點,當設計一個新的抽象類時,一定是用來繼承的,所有在一個繼承關係形成的等級結構裡,樹葉節點應當是具體類。而樹葉節點均應當是抽象類。
九、介面(interface)
介面就是把隱式公共方法和屬性組合起來,以封裝特定功能的一個集合,一旦類實現了介面,類就可以職稱介面所指定的所有屬性和成員,宣告介面的語法和抽象類完全相同,但不允許提供介面中任何成員的執行方式。所以介面不能例項化,不能有構造方法和欄位;不能有修飾符(如public,private等);不能宣告虛擬的或靜態的等。還有實現介面的類就必須要屬性介面中的所有方法和屬性。
一個類可以支援多個介面,多個類也可以支援相同的介面。介面的命名,前面要加一個大寫的字母'I',這是規範。
介面用interface宣告,而不是class,介面名稱前要加‘I’,介面中的方法或屬性前面不能有修飾符、方法沒有方法體。
//宣告一個IPeople介面,此介面有一個printInfo方法
interface IPeople{
void printInfo();
}
使用介面實現學生類
class Student implements IPeople{
public void printInfo(){
System.out.println("執行正確。");
}
}
區分抽象類和介面
抽象類可以給出一些成員的實現,介面卻不包含成員的實現,抽象類的抽象成員可被子類部分實現,介面的成員需要實現類完全實現,一個類只能繼承一個抽象類,但可實現多個介面等。
區分:
- 類是對物件的抽象;抽象類是對類的抽象;介面是對行為的抽象。
- 如果行為跨越不同類的物件,可使用介面;對於一些相似的類物件,用繼承抽象類。(實現介面和繼承抽象類並不衝突)。
- 從設計者角度來講,抽象類是從子類物件中發現了公共的東西,泛化出父類,然後子類繼承父類。而介面是根本不知子類的存在,方法如何實現還不確認,提前定義。
十、泛型
泛型是具有佔位符(型別引數)的類、結構、介面和方法,這些佔位符是類、結構、介面和方法所儲存或使用的一個或多個型別的佔位符。泛型集合類可以將型別引數用作它所儲存的對像的型別的佔位符;型別引數作為其欄位和其方法的引數型別出現。
IList arrayPeople;//宣告一個集合變數,可以用介面IList,也可以直接宣告“ArrayList arrayPeople;”
用法就是在IList和List後面加'
//宣告一泛型集合變數,用介面IList,注意:IList表示此集合變數只能接受People型別,其它型別不接受,也可以直接宣告“List ”
IList<People> arrayPeople;
//例項化List物件,需要指定List<T>的'T'是People
arrayPeople = new List<People>();
泛型的使用
泛型有三種使用的方式,分別為:泛型類、泛型介面、泛型方法。
public class People<T>{
//T stands for "Type"
private T t;
public void set(T t){this.t = t;}
public T get(return t);
}
十一、註解
Java註解是附加在程式碼中的一些元資訊,用於一些工具在編譯、執行時進行解析和使用,起到說明,配置的功能。註解不會也不呢影響程式碼的實際邏輯,僅僅起到輔助性的作用。
註解 Annotation 實現原理與自定義註解例子(opens new window)
十二、反射
什麼是反射
可以獲取任意一個類的所有屬性和方法,還可以呼叫這些屬性和方法。
Java反射主要提供以下功能:
- 在執行時判斷任意一個物件所屬的類;
- 在執行時構造任意一個類的物件;
- 在執行時判斷任意一個類所具有的成員變數和方法(通過反射甚至可以呼叫private方法);
- 在執行時呼叫任意一個物件的方法;
重點:是執行時而不是編譯時。
反射機制的優缺點
- 優點:可以讓程式碼更加靈活,為各種框架提供開箱即用的功能提供了便利。
- 缺點:讓我們在執行時有了分析操作類的能力,增加了安全問題。比如可以無視泛型引數的安全檢查(泛型引數的安全檢查發生在編譯時)。另外,反射的效能也會稍差點。不過,對框架來說影響不大。Java Reflection: Why is it so slow?
反射的應用場景
我們平時大部分時候都是在寫業務程式碼,很少會接觸到直接使用反射機制的場景。
但是,這並不代表反射沒有用。相反,正是因為反射,你才能這麼輕鬆地使用各種框架。像 Spring/Spring Boot、MyBatis 等等框架中都大量使用了反射機制。
這些框架中也大量使用了動態代理,而動態代理的實現也依賴反射。
十三、異常
在Java中,所有的異常都有一個共同的祖先java.lang包中的Throwable類,Throwable類有兩個重要的子類Error(錯誤)和Exception(異常)。其中Error用來表示JVM(Java虛擬機器)無法處理的錯誤,Exception又分為兩種:
- 受檢查異常:需要用try-catch語句捕獲並進行處理,並且可以從異常中恢復。
- 不受檢查異常:java程式碼在編譯過程中,即使不處理不受檢查異常也可以正常通過編譯。是執行時的錯誤,例如除0會引發ArithmeticException(算術錯誤異常),此時程式崩潰並且無法恢復。RuntimeException及其子類都統稱為非受檢查異常。
Throwable類常用方法
public string getMessage()//返回異常發生時的簡要描述。
public string toString()//返回異常發生時的詳細資訊。
public string getLocalizedMessage()//返回異常物件的本地化資訊。使用Throwable的子類覆蓋這個方法,可以生成本地化資訊。如果子類沒有覆蓋該方法,則該方法返回的資訊與getMessage()返回結果相同。
public void printStackTrace()//在控制檯上列印Throwable物件封裝的異常資訊。
try-catch-finally
try塊:用於捕獲異常,後面可以接零個或多個catch塊,如果沒有catch塊,則必須跟一個finally塊。
catch塊:用於處理try捕獲到的異常。
finally塊:無論是否捕獲或處理異常,finally塊裡的語句都會被執行。當在try塊或catch塊中遇到return語句時,finally語句塊將在方法返回之前被執行。
在下面三種特殊情況下,finally塊不會被執行:
- 在try或finally塊中用了System.exit(int)退出程式。如果System.exit(int)在異常語句之後,finally還是會被執行。
- 程式所在的執行緒死亡
- 關閉CPU
注意:當try語句和finally語句中都有return語句時,在方法返回之前,finally語句的內容將被執行,並且finally與的返回值會覆蓋原始的返回值,如下:
public class Test{
public static int f(int value){
try{
return value*value;
}finally{
if(value == 2)
return 0;
}
}
}
如果呼叫f(2),返回值將是0,因為try語句的返回值被finally語句的返回值覆蓋。
使用try-with-resources來代替try-catch-finally
- 適用範圍(資源的定義):任何實現java.lang.AutoCloseable或者java.io.Closeable的物件。
- 關閉資源和finally塊的執行順序:在try-with-resources語句中,任何catch或finally塊在宣告的資源關閉後執行。
《Effecitve Java》中明確指出:
面對必須要關閉的資源,我們總是應該優先使用try-with-resources而不是try-finally。隨之產生的程式碼更簡短,更清晰,產生的異常對我們也更有用。try-with-resources語句讓我們更簡短清晰,產生的異常對我們更有用。try-with-resources語句讓我們更容易編寫必須要關閉的資源的程式碼,若採用try-finally則幾乎做不到這點。
Java中類似於InputStream、OutputStream、Scanner、PrintWriter等的資源都需要我們呼叫close()方法來手動的關閉。
使用try-catch-finally語句實現:
//讀取文字檔案的內容
Scanner scanner = null;
try{
scanner = new Scanner(new File("D://read.txt"));
while(scanner.hasNext())
System.out.println(scanner.nextLine());
}catch (FileNotFoundException e){
e.printStackTrace();
}finally{
if(scanner != null)
scanner.close();
}
使用Java 7之後的try-with-resources語句改造上面的程式碼:
try (Scanner scanner = new Scanner(new File("D:\\read.txt"))){
while(scanner.hasNext())
System.out.println(scanner.nextLine());
}catch (FileNotFoundException e){
e.printStackTrace();
}
通過使用分號分隔符,可以在try-with-resources塊中宣告多個資源。
try(BufferredInputStream bin = new BufferredInputStream(new FileInputStream(new File("test.txt"))); BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))){
int b;
while((b = bin.read()) != -1)
bout.write(b);
}catch (IOException e)
{
e.printStackTrace();
}