Java基礎系列1:Java面向對象

該系列博文會告訴你如何從入門到進階,一步步地學習Java基礎知識,並上手進行實戰,接着瞭解每一個Java知識點背後的實現原理,更完整地瞭解整個Java技術體系,造成本身的知識框架。java

 

概述:

Java是面向對象的程序設計語言,Java語言提供了定義類、成員變量、方法等最基本的功能。類可被認爲是一種自定義的數據類型,可使用類來定義變量,全部使用類定義的變量都是引用變量,它們將會引用到類的對象。類用於描述客觀世界裏某一類對象的共同特徵,而對象則是類的具體存在,Java程序使用類的構造器來建立該類的對象。程序員

 

對象和類:

Java是面向對象的程序設計語言,類是面向對象的重要內容,能夠把類當成一種自定義類型,可使用類來定義變量,這種類型的變量統稱爲引用變量。也就是說,全部類是引用類型。對象是由類建立出來的,能夠說類時對象的抽象,對象是類的實例。編程

 

對象的概念:

Java 是面向對象的編程語言,對象就是面向對象程序設計的核心。所謂對象就是真實世界中的實體,對象與實體是一一對應的,也就是說現實世界中每個實體都是一個對象,它是一種具體的概念。對象有如下特色:數組

  • 對象具備屬性和行爲。
  • 對象具備變化的狀態。
  • 對象具備惟一性。
  • 對象都是某個類別的實例。
  • 一切皆爲對象,真實世界中的全部事物均可以視爲對象。

 

面向對象與面向過程:

一、面向過程:

面向過程是一種以事件爲中心的編程思想,編程的時候把解決問題的步驟分析出來,而後用函數把這些步驟實現,在一步一步的具體步驟中再按順序調用函數。安全

 

 

 

咱們以五子棋爲例來解釋一下面向過程是如何解決問題的:數據結構

下過五子棋的同窗都知道,首先要找兩我的,而後把棋譜擺放好,其中一方手持黑棋,另外一方手持白旗,通常約定白棋先動,而後黑棋在動,這樣每人一步,直到某一方先湊成五子一條線便爲贏。這是咱們日常下五子棋的過程,那麼用面向過程該如何表示呢?app

咱們能夠將下五子棋的過程抽象成以下步驟:框架

(1)開始遊戲(2)黑子先走(3)繪製畫面(4)判斷輸贏(5)輪到白子(6)繪製畫面(7)判斷輸贏(8)返回步驟(2) (9)輸出最後結果。編程語言

接着咱們用面向過程來實現五子棋這一程序:模塊化

下五子棋{

    開始遊戲();

    黑子先走();

    繪製畫面();

    判斷輸贏();

    輪到白子();

    繪製畫面();

    判斷輸贏();

    返回到 黑子先走();

    輸出最後結果;

}

  

可見,面向過程始終關注的是怎麼一步一步地判斷棋局輸贏的,經過控制代碼,從而實現函數的順序執行。

 

二、面向對象:

​ 一種基於面向過程的新編程思想,顧名思義就是該思想是站在對象的角度思考問題,咱們把多個功能合理放到不一樣對象裏,強調的是具有某些功能的對象。

具有某種功能的實體,稱爲對象。面向對象最小的程序單元是:類。面向對象更加符合常規的思惟方式,穩定性好,可重用性強,易於開發大型軟件產品,有良好的可維護性。

Java編程思想一書中有一段對面向對象的總結很是清晰到位,可謂是面向對象的精華所在:

一、萬物皆對象

二、程序時對象的集合,它們經過發送消息來告知彼此所須要作的

三、每一個對象都有本身的由其餘對象所構成的存儲

四、每一個對象都擁有其類型

五、某一特定類型的全部對象均可以接收一樣的消息

 

三、二者優缺點比較:

(1)、面向過程:

優勢:

  流程化使得編程任務明確,在開發以前基本考慮了實現方式和最終結果,具體步驟清楚,便於節點分析。

  效率高,面向過程強調代碼的短小精悍,善於結合數據結構來開發高效率的程序。

缺點:

  須要深刻的思考,耗費精力,代碼重用性低,擴展能力差,後期維護難度比較大。

 

(2)、面向對象:

優勢:

  結構清晰,程序是模塊化和結構化,更加符合人類的思惟方式;

  易擴展,代碼重用率高,可繼承,可覆蓋,能夠設計出低耦合的系統;

  易維護,系統低耦合的特色有利於減小程序的後期維護工做量。

缺點:

  開銷大,當要修改對象內部時,對象的屬性不容許外部直接存取,因此要增長許多沒有其餘意義、只負責讀或寫的行爲。這會爲編程工做增長負擔,增長運行開銷,而且使程序顯得臃腫。

  性能低,因爲面向更高的邏輯抽象層,使得面向對象在實現的時候,不得不作出性能上面的犧牲,計算時間和空間存儲大小都開銷很大。

 

面向對象的三大特性:

概述:

一、繼承:

  繼承是面向對象的三大特徵之一,也是實現軟件複用的重要手段。Java的繼承具備單繼承的特色,每一個子類只有一個直接父類。

二、封裝:

  封裝(Encapsulation)是面向對象的三大特徵之一,它指的是將對象的狀態信息隱藏在對象內部,不容許外部程序直接訪問對象內部信息,而是經過該類所提供的方法來實現對內部信息的操做和訪問。

  封裝是面向對象編程語言對客觀世界的模擬,在客觀世界裏,對象的狀態信息都被隱藏在對象內部,外界沒法直接操做和修改。好比說一我的的年齡,年齡只會隨着時間的流逝而逐漸增加,不能隨意修改人的年齡。對一個類或對象實現良好的封裝,能夠實現如下目的。

三、多態:

  Java引用變量有兩個類型:一個是編譯時類型,一個是運行時類型。編譯時類型由聲明該變量時使用的類型決定,運行時類型由實際賦給該變量的對象決定。若是編譯時類型和運行時類型不一致,就可能出現所謂的多態(Polymorphism)。

  多態的做用是消除類型之間的耦合關係。

 

詳解:

1、繼承:

一、繼承的概念:

程序來源於生活,也爲生活所用。咱們先從生活中的例子來看一下什麼是繼承:

如今有一個農場主,家有良田萬頃,每一年收入不少,他有一個兒子,就是咱們口中的富二代。有一天農場主不幸去世了,那麼他手下的農田和財產都是誰的了,毫無疑問,固然是他兒子的了(若是你好好努力,未來你兒子有很大機會是富二代哦)。那麼他兒子原本一無全部,如今頃刻間多了須要Money,農田,房子等等,也就是擁有了他父親的全部物資財富,這個咱們就稱之爲繼承。

Java的繼承經過extends關鍵字來實現,實現繼承的類被稱爲子類,被繼承的類被稱爲父類,有的也稱其爲基類、超類。父類和子類的關係,是一種通常和特殊的關係。例如水果和蘋果的關係,蘋果繼承了水果,蘋果是水果的子類,則蘋果是一種特殊的水果。

class Fruit{
	public double weight;
	public void info() {
		System.out.println("我是一個水果,重"+weight+"g");
	}
}

public class Apple extends Fruit{
	public static void main(String[] args) {
		//建立Apple對象
		Apple apple=new Apple();
		//Apple對象自己並無weight成員變量
		//可是Apple的父類用於weight變量,因此Apple也能夠方位
		apple.weight=88;
		apple.info();
	}

}

結果:我是一個水果,重88.0g

  

二、重寫父類的方法:

子類擴展了父類,子類是一個特殊的父類。大部分時候,子類老是以父類爲基礎,額外增長新的成員變量和方法。但有一種狀況例外:子類須要重寫父類的方法。例如鳥類都包含了飛翔方法,其中鴕鳥是一種特殊的鳥類,所以鴕鳥應該是鳥的子類,所以它也將從鳥類得到飛翔方法,但這個飛翔方法明顯不適合鴕鳥,爲此,鴕鳥須要重寫鳥類的方法。

//父類
class Bird{
	public void fly() {
		System.out.println("我在天空自由的飛翔");
	}
}

public class Ostrich extends Bird {
	//重寫Bird的fly方法
	public void fly() {
		System.out.println("我只能在地上奔跑");
	}
	
	public static void main(String[] args) {
		//建立Ostrich對象
		Ostrich ostrich=new Ostrich();
		ostrich.fly();
	}
}

結果:我只能在地上奔跑

  

重寫時須要注意:

一、返回值類型

二、方法名

三、參數類型及個數

都要與父類繼承的方法相同,才叫方法的重寫。

 

重寫與重載的區別:

重寫:相對繼承而言,子類中對父類已經存在的方法進行區別化的修改。

重載:在同一個類中處理不一樣數據的多個相同方法名的多態手段。重載方法名相同,參數列表不一樣。

 

 

三、繼承的初始化順序:

先思考一下下面代碼的輸出結果:

class Animal{
	public Animal() {
		System.out.println("我是父類動物");
	}
}

class Humanity extends Animal{
	public Humanity() {
		System.out.println("我是父類人類");
	}
}

public class Student extends Humanity{
	
	public Student() {
		System.out.println("我是子類學生");
	}

	public static void main(String[] args) {
		Student student=new Student();
	}
}

  

不要看結果,本身先思考一下

 

輸出結果:

結果

 

 

是否是和你思考的結果同樣,不同的同窗接着往下看:

Java中繼承初始化順序以下:

一、初始化父類再初始化子類

二、先執行初始化對象中屬性,再執行構造方法中的初始化。

基於上面兩點,咱們就知道實例化一個子類,java程序的執行順序是:

父類對象屬性初始化---->父類對象構造方法---->子類對象屬性初始化—>子類對象構造方法

下面放上一張形象的圖:

 

 

 

四、final關鍵字:

final 關鍵字可用於修飾類、變量和方法,final關鍵字有點相似C#裏的sealed關鍵字,用於表示它修飾的類、方法和變量不可改變。

final修飾變量時,表示該變量一旦得到了初始值就不可被改變,final既能夠修飾成員變量(包括類變量和實例變量),也能夠修飾局部變量、形參。有的書上介紹說final修飾的變量不能被賦值,這種說法是錯誤的!嚴格的說法是,final修飾的變量不可被改變,一旦得到了初始值,該final變量的值就不能被從新賦值。因爲final變量得到初始值以後不能被從新賦值,所以final修飾成員變量和修飾局部變量時有必定的不一樣。

一、final修飾變量:

  ☆:final修飾成員變量:一旦有了初始值,就不能被從新賦值。final修飾的成員變量必須由程序顯示的指定初始值。final修飾類變量必須在靜態初始化塊中指定初始值或聲明該類變量時指定初始值,並且只能在兩個地方其中之一指定;final修飾實例變量必須在非靜態初始化塊、聲明該實例變量或構造器中指定初始值,並且只能在三個地方的其中之一指定。

  ☆:final修飾局部變量:系統不會對局部變量進行初始化,局部變量必須由程序員顯式初始化。所以使用final修飾局部變量時,既能夠在定義時指定默認值,也能夠不指定默認值。

  ☆:final修飾基本類型變量與引用類型變量區別:當使用final修飾基本類型變量時,不能對基本類型變量從新賦值,所以基本類型變量不能被改變。但對於引用類型變量而言,它保存的僅僅是一個引用,final只保證這個引用類型變量所引用的地址不會改變,即一直引用同一個對象,但這個對象徹底能夠發生改變。

import java.util.Arrays;

public class FinalTest {
	
	public static void main(String[] args) {
		//final修飾數組變量,arr只是一個引用變量
		final int[] arr= {3,90,33,12};
		System.out.println(Arrays.toString(arr));
		
		//對數組進行排序,合法
		Arrays.sort(arr);
		System.out.println(Arrays.toString(arr));
		//對數組元素進行賦值,合法
		arr[2]=109;
		System.out.println(Arrays.toString(arr));
		
		//下面語句對arr從新賦值,非法
		//arr=null;
		
	}

}

  

二、final修飾方法:

  final修飾的方法不可被重寫

 

三、final修飾類:

  final修飾的類不能夠有之類

 

五、this關鍵字:

this是自身的一個對象,表明對象自己,能夠理解爲:指向對象自己的一個指針。

this的用法:

一、普通的直接引用

二、形參與成員名字重名,用this來區分

public class Student{
	
	String username;
	String password;
	
	public Student(String username,String password) {
		//this 表明當前對象 也就是下面的student
		this.username=username;
		this.password=password;
	}

	public static void main(String[] args) {
		Student student=new Student("jack","123");
	}
}

  

三、引用構造函數,這個放在super關鍵字中說

 

六、super關鍵字:

super能夠理解爲是指向本身超(父)類對象的一個指針,而這個超類指的是離本身最近的一個父類。

super的用法:

一、普通的直接引用:與this相似,super至關因而指向當前對象的父類

super.name:引用父類的變量

super.add():引用父類的方法

二、子類中的成員變量或方法與父類中的成員變量或方法同名:

class Humanity{
	public void eat() {
		System.out.println("動物吃肉");
	}
}

public class Student extends Humanity{
	
	public void eat() {
		//super調用父類中的同名方法
		super.eat();
		System.out.println("人吃飯");
	}

	public static void main(String[] args) {
		Student student=new Student();
		student.eat();
	}
}

  

結果:

動物吃肉
人吃飯

  

三、引用構造函數:

super(參數):調用父類中的某一個構造函數(應該爲構造函數中的第一條語句)。
this(參數):調用本類中另外一種形式的構造函數(應該爲構造函數中的第一條語句)。
class Humanity{
	
	public Humanity() {
		System.out.println("父類無參構造");
	}
	
	public Humanity(String s) {
		System.out.println("父類有參構造======"+s);
	}
	
}

public class Student extends Humanity{
	
	public Student() {
		super();//調用父類的無參構造方法
		System.out.println("子類無參構造");
	}
	
	
	public Student(String s) {
		super(s);//調用父類的有參構造
		System.out.println("子類的有參構造======"+s);
	}
	
	public Student(String username,String password) {
		this(username);//調用本類的有參構造
		System.out.println("子類帶兩個參數的構造函數======"+username+"======"+password);
	}
	
	public static void main(String[] args) {
		Student student=new Student();
		Student student2=new Student("小明");
		Student student3=new Student("小明","123");
	}
	
}

  

輸出結果:

父類無參構造
子類無參構造
父類有參構造======小明
子類的有參構造======小明
父類有參構造======小明
子類的有參構造======小明
子類帶兩個參數的構造函數======小明======123

  

 

2、封裝:

一、封裝的概念與優勢:

封裝(Encapsulation)是面向對象的三大特徵之一,它指的是將對象的狀態信息隱藏在對象內部,不容許外部程序直接訪問對象內部信息,而是經過該類所提供的方法來實現對內部信息的操做和訪問。


封裝是面向對象編程語言對客觀世界的模擬,在客觀世界裏,對象的狀態信息都被隱藏在對象內部,外界沒法直接操做和修改。就如剛剛說的Person對象的age變量,只能隨着歲月的流逝,age纔會增長,一般不能隨意修改Person對象的age。對一個類或對象實現良好的封裝,能夠實現如下目的。

  1. 隱藏類的實現細節。
  2. 讓使用者只能經過事先預約的方法來訪問數據,從而能夠在該方法里加入控制邏輯,限制對成員變量的不合理訪問。
  3. 可進行數據檢查,從而有利於保證對象信息的完整性。
  4. 便於修改,提升代碼的可維護性。

 

爲了實現良好的封裝,須要從兩個方面考慮。

  1. 將對象的成員變量和實現細節隱藏起來,不容許外部直接訪問。
  2. 把方法暴露出來,讓方法來控制對這些成員變量進行安全的訪問和操做。

 

二、訪問修飾符:

Java提供了3個訪問修飾符:public、protected和private,另外還有一個默認的修飾符default,Java的訪問控制級別以下圖所示:

 

 

下面來詳細介紹一下四個訪問修飾符:

  • private(當前類訪問權限):若是類裏的一個成員(包括成員變量、方法和構造器等)使用private訪問控制符來修飾,則這個成員只能在當前類的內部被訪問。很顯然,這個訪問控制符用於修飾成員變量最合適,使用它來修飾成員變量就能夠把成員變量隱藏在該類的內部。
  • default(包訪問權限):若是類裏的一個成員(包括成員變量、方法和構造器等)或者一個外部類不使用任何訪問控制符修飾,就稱它是包訪問權限的,default 訪問控制的成員或外部類能夠被相同包下的其餘類訪問。關於包的介紹請看5.4.3節。
  • protected(子類訪問權限):若是一個成員(包括成員變量、方法和構造器等)使用protected訪問控制符修飾,那麼這個成員既能夠被同一個包中的其餘類訪問,也能夠被不一樣包中的子類訪問。在一般狀況下,若是使用protected來修飾一個方法,一般是但願其子類來重寫這個方法。
  • public(公共訪問權限):這是一個最寬鬆的訪問控制級別,若是一個成員(包括成員變量、方法和構造器等)或者一個外部類使用public訪問控制符修飾,那麼這個成員或外部類就能夠被全部類訪問,無論訪問類和被訪問類是否處於同一個包中,是否具備父子繼承關係。

掌握了訪問修飾符後,咱們就能夠來使用封裝了。

public class Person {
	
	private String username;
	private Integer age;
	
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	
}

  

上述代碼Person類中有兩個成員變量username和age,它們都是私有變量,外部不能訪問,可是提供了get和set方法,經過這兩個方法即可以修改和獲取Person類中的相關數據,這就是封裝

 

三、java中的包是什麼?

記得我上初中的時候,班級有兩個同名的同窗,都叫王健,老師每次叫王健的時候他倆不知道叫的是誰,後來加上性別區分,一個叫男王健,一個叫女王健,此次區分開。

那麼咱們在java中會不會遇到這種狀況呢?固然會,好比就Person這個類而言,在一個大型項目中,多人協做開發,你寫了一個類叫Person,我也寫個類叫Person,那麼該如何區分這兩個類呢?總不能一個叫男Person,一個叫女Person吧,哈哈。這時候java就引入了包的機制,容許在類名前面加上一個前綴來限制這個類,提供了類的多層命名空間

 

注意:

一、package語句必須做爲源文件的第一條非註釋性語句,一個源文件只能指定一個包,即只能包含一條package語句,該源文件中能夠定義多個類,則這些類將所有位於該包下。


二、若是沒有顯式指定package語句,則處於默認包下。在實際企業開發中,一般不會把類定義在默認包下,但本書中的大量示例程序爲了簡單起見,都沒有顯式指定package語句。


三、同一個包下的類能夠自由訪問

 

 

3、多態:

一、多態的概念:

多態是指容許不一樣類的對象對同一消息作出響應。即同一消息能夠根據發送對象的不一樣而採用多種不一樣的行爲方式。(發送消息就是函數調用)

 

二、多態的做用:

消除類型之間的耦合關係。

 

三、多態產生的條件:

  一、要有繼承;

  二、要有重寫;

  三、父類引用指向子類對象。

 

四、多態的優勢:

一、可替換性(substitutability)。多態對已存在代碼具備可替換性。例如,多態對圓Circle類工做,對其餘任何圓形幾何體,如圓環,也一樣工做。

二、可擴充性(extensibility)。多態對代碼具備可擴充性。增長新的子類不影響已存在類的多態性、繼承性,以及其餘特性的運行和操做。實際上新加子類更容易得到多態功能。例如,在實現了圓錐、半圓錐以及半球體的多態基礎上,很容易增添球體類的多態性。

三、接口性(interface-ability)。多態是超類經過方法簽名,向子類提供了一個共同接口,由子類來完善或者覆蓋它而實現的。如圖8.3 所示。圖中超類Shape規定了兩個實現多態的接口方法,computeArea()以及computeVolume()。子類,如Circle和Sphere爲了實現多態,完善或者覆蓋這兩個接口方法。

四、靈活性(flexibility)。它在應用中體現了靈活多樣的操做,提升了使用效率。

五、簡化性(simplicity)。多態簡化對應用軟件的代碼編寫和修改過程,尤爲在處理大量對象的運算和操做時,這個特色尤其突出和重要。

 Java中多態的實現方式:接口實現,繼承父類進行方法重寫,同一個類中進行方法重載。

 

五、經典的多態案例:

class Animal{
	public void eat() {
		
	}
}


class Cat extends Animal
{
	public void eat()
	{
		System.out.println("吃魚");
	}
	public void catchMouse()
	{
		System.out.println("抓老鼠");
	}
}
 
class Dog extends Animal
{
	public void eat()
	{
		System.out.println("吃骨頭");
	}
	public void kanJia()
	{
		System.out.println("看家");
	}
}

public class AnimalTest {

	 public static void main(String[] args) {
		Animal cat=new Cat();//向上轉型
		cat.eat();
		Animal dog=new Dog();
		dog.eat();
          //Cat c=(Cat)cat;//向上轉型 } }

  

Animal是父類,它有兩個之類分別是Dog和Cat,之類分別重寫了父類的eat方法。

輸出結果:

吃魚
吃骨頭

  

從輸出結果能夠看出,一樣都是Animal,可是卻有不一樣的行爲表現,這就是多態

 

六、向上轉型和向下轉型:

一、向上轉型:就以上述的父類Animal和一個子類Dog來講明,當父類的引用能夠指向子類的對象時,就是向上類型轉換:Animal cat=new Cat();

二、向下轉型:向下類型轉換(強制類型轉換),是大類型轉換到小類型(有風險,可能出現數據溢出)。例如:Cat c=(Cat)cat

 

七、重寫和重載:

重寫:父類與子類之間的多態性,對父類的函數進行從新定義。若是在子類中定義某方法與其父類有相同的名稱和參數,咱們說該方法被重寫 (Overriding)。在Java中,子類可繼承父類中的方法,而不須要從新編寫相同的方法。

重載:方法重載是讓類以統一的方式處理不一樣類型數據的一種手段。多個同名函數同時存在,具備不一樣的參數個數/類型。重載是一個類中多態性的一種表現。

 

重載發生在一個類當中,重寫發生在兩個類當中,可是這兩個類是父子類的關係。

相關文章
相關標籤/搜索