Java之面向物件概述,類,構造方法,static,主方法,物件
一、面向物件概述
面向過程
“面向過程”(Procedure Oriented)是一種以過程為中心的程式設計思想。這些都是以什麼正在發生為主要目標進行程式設計,不同於面向物件的是誰在受影響。與面向物件明顯的不同就是封裝、繼承、類。 “面向過程”(Procedure Oriented)是一種以過程為中心的程式設計思想。“面向過程”也可稱之為“面向記錄”程式設計思想,他們不支援豐富的“面向物件”特性(比如繼承、多型),並且它們不允許混合持久化狀態和域邏輯。 就是分析出解決問題所需要的步驟,然後用函式把這些步驟一步一步實現,使用的時候一個一個依次呼叫就可以了。
面向物件:
面向物件實際上就是對現實世界的物件進行了操作
面向物件程式設計(英語:Object-oriented programming,縮寫:OOP),指一種程式設計範型,同時也是一種程式開發的方法。它將物件作為程式的基本單元,將程式和資料封裝其中,以提高軟體的重用性、靈活性和擴充套件性。 面向物件(Object Oriented)是一種新興的程式設計方法,或者是一種新的程式設計規範(paradigm),其基本思想是使用物件、類、繼承、封裝、多型等基本概念來進行程式設計。從現實世界中客觀存在的事物(即物件)出發來構造軟體系統,並且在系統構造中儘可能運用人類的自然思維方式。
物件:
Object:表示任意存在的事物。
世間萬物皆物件,物件是事物存在的實體。
我們將複雜的問題簡單化,就會考慮物件是有哪些事物組成的。一般將物件劃分為動態部分和靜態部分。
事物也就是物件的靜態部分,也就是不能動的部分,稱為屬性,比如一個人,具備高矮,胖瘦,年齡等屬性,而這個人也會哭泣,微笑,行走等行為,也就是動態部分,人通過探討物件的屬性和觀察物件的行為了解物件。
在計算機的世界中,面向物件程式設計的思想要以物件來思考問題,首先要將現實世界的實體抽象為物件,然後考慮這個物件具備的屬性和行為、
一隻鳥飛行為例:
首先抽象出的物件為這隻鳥,然後識別個物件具備的靜態部分,也就是屬性,有兩隻翅膀,兩隻腳,體重等等,再然後識別這物件的動態部分,也就是行為,這個物件可以飛行,覓食等等,這些行為都是因為這個物件基於其屬性而具有的動作。識別出這些屬性和行為之後,這個物件就被定義完成,然後可以根據這個物件的特性來指定飛行的方案。
類:
類是同一類事物即物件的的統稱,比如人類,鳥類等。
我們可以說物件屬於某一個類,但不能說類屬於某一個物件。
類是構造物件是所依賴的規範,比如鳥具有一對翅膀,他們可以依靠翅膀飛行,這樣具有相同特性和行為的一類事物就稱為類。
可以這樣理解,類就是物件的設計圖。
面向物件三大特徵:
封裝,繼承,多型
以及第四特徵,抽象。
封裝:
封裝是面向物件程式設計的的核心思想,將物件的屬性和行為封裝起來,而將物件的屬性和行為封裝起來的載體就是類,類通常對客戶隱藏其實現的細節。
比如我們使用電腦,只需要使用滑鼠鍵盤操作就可以實現功能,無需知道計算及內部是如何操作的。
我們寫一方法,不用知道他是怎麼實現的,只要能供我們就可以了。
繼承:
當處理一個問題時,可以將一些有用的類保留下來,這些類通常具有相同的屬性,甚至相同的方法,當遇到同樣的問題時可以拿來複用。比如鴿子和鳥類具有相同的屬性和行為,可以在建立鴿子類是將鳥類拿來複用,並且保留鳥類具有的屬性和行為。
繼承主要是利用特定物件之間的共有屬性。例如,矩形是四邊形,矩形和四邊形具有相同特徵,都有四個邊,可以將矩形類看做四邊形的延伸,矩形複用了四邊形的屬性和行為,同時添加了矩形獨有的屬性和行為,比如內角都是直角等,而矩形又延伸出了正方形,正方形複用了矩形的屬性和行為,而又添加了正方形獨有的屬性和行為,比如四邊相等。
矩形是四邊形的子類,而正方形是矩形的子類。
多型:
將父類物件應用於子類的特徵就是多型。
多型性允許以統一的風格編寫程式,以處理種類繁多的已存在的類以及相關類。該統一風格可以由父類來實現,根據父類統一風格的處理,就可以例項化子類的物件。由於整個事件的處理都只依賴父類的方法,所以日後只需要維護和調整父類方法就好。這樣就降低了維護的難度,節省了時間。
比如有一個螺絲類,具有長度粗細螺紋等屬性,我們在建立兩個子類,長螺絲類,短螺絲類,他們都繼承了螺絲類。他們就具有了相同的特性,比如粗細,螺紋密度等。他們也有不同的特性,比如長短。一個螺絲類延伸出不同的子類,子類繼承了父類的特徵,子類有具有自己的特徵,同樣的固定行為,它們可以固定不同的物件,這就是多型化結構。
二、類的特性
1.成員變數:成員變數就是Java中累的屬性。
2.成員方法:成員方法就是Java中類的行為。
3.區域性變數:如果在成員方法內定義一個變數,那這個變數就是區域性變數。
區域性變數是在方法被執行時建立,在方法結束時銷燬。區域性變數在使用時必須進行賦值操作或被初始化,否則會出現編譯錯誤。
4.區域性變數的有效範圍:可以將區域性變數的有效範圍稱為變數的作用域,區域性變數的有效範圍從該變數的宣告開始到該變數的結束為止。
在相互不巢狀的作用域中可以同時宣告兩個名稱和型別完全相同的區域性變數,這兩個變數相互獨立,不會相互干擾。
在巢狀區域中,不可以定義名稱和型別相同的區域性變數。
當類中的成員變數與成員方法中的引數重名時,方法中如何使用成員變數呢?
this關鍵字:在類中。this代表類物件本身。
package com.hanqi.maya.model;
public class Book {
String name="abc";
public void showName(String name){
System.out.println(name);
}
public static void main(String[] args){
Book b=new Book();
b.showName("123");
}
}
執行輸出的name值是引數值,由此可見方法中呼叫的name是引數中的name,而不是成員變數
如果是使用this:
package com.hanqi.maya.model;
public class Book {
String name="abc";
public void showName(String name){
System.out.println(this.name);
}
public static void main(String[] args){
Book b=new Book();
b.showName("123");
}
}
許可權修飾符
三、類的構造方法
構造方法是一個與類同名的方法,物件的建立就是通過構造方法完成的。每當類例項化一個物件時,類都會自動呼叫構造方法。
構造方法就是建立類的物件中執行的方法,也就是物件的初始化方法。
需要注意的是:
在定義構造方法時,構造方法沒有返回值,但這與普通沒有返回值的方法不同,普通沒有返回值的方法使用 public void name() 這種形式定義,但構造方法不需要使用void 關鍵字進行修飾。
物件初始化
在構造方法中可以為成員變數賦值,這樣當例項化一個本類的物件時,相應的成員變數也將被初始化。如果類中沒有明確定義構造方法,則編譯器會自動建立一個不帶引數的預設構造方法。
如果在類中定義的構造方法都不是無參的構造方法,當試圖呼叫無參構造方法例項化一個物件時,編譯器會報錯。所以只有在類中沒有定義任何構造方法時,編譯器才會在該類中自動建立一個不帶引數的構造方法。
簡單來說:
如果我們不定義構造引數,當我們呼叫無參構造方法例項化一個物件時,會自動建立一個無引數構造方法
當我們定義了一個無引數構造方法,當呼叫無參構造方法例項化一個物件時,不會報錯
當我們定義的構造方法都不是無參的構造方法,當試圖呼叫無參構造方法例項化一個物件時,編譯器會報錯
當我們只定義了無引數構造方法,當呼叫構造方法例項化一個物件時給他傳入引數,編譯器會報錯
使用this關鍵字,還可以呼叫類中的構造方法來簡化程式碼
例子:
我們假設這樣一種情況,我們去開會,每個人預設發一瓶水,如果有需要可以要多瓶水,用this呼叫構造方法來簡化程式碼
public class White {
protected int nwhite;
public White(){
/*nwhite=1;
System.out.println("給您"+nwhite+"瓶水");
以上兩行程式碼的效果和下面一行程式碼相同
*/
//用this來呼叫本身的實參構造方法,來傳入一個引數1,代表預設一瓶水
this(1);
}
public White(int nwhite){
this.nwhite=nwhite;
System.out.println("給您"+nwhite+"瓶水");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
White w1=new White();
White w2=new White(5);
}
}
私有構造方法
構造方法和其它方法一樣,也可以用private修飾,私有的構造方法無法在本類外部使用,也就導致本類無法用new例項化,這樣可以控制物件的生成。
四、static靜態修飾符
由static修飾的變數,常量,和方法被稱作靜態變數、靜態常量、和靜態方法。他們都存放在記憶體的“”靜態區”中,這些變數和方法有獨立的生存週期。
記憶體中的靜態區在整個程式執行結束之後才會釋放,所以用靜態修飾的程式碼的生命週期,是整個程式的生命週期。
靜態區:記憶體中的靜態區的變數可以被本類共享,其他類呼叫本類靜態變數和靜態方法時,無需例項化就可以呼叫。
靜態變數
很多時候,不同的類之間需要對同一個變數進行操作,比如一個水池,同時開啟入水口和放水口,進水和出水這兩個動作會同時影響到池中水量,這是池中水量就是一個共享的變數。在Java中,我們把共享的變數用static修飾,該變數就是靜態變數。
public class Pool {
public static int white;
public void inWhite(){
white+=3;
System.out.println("進水一次");
}
public void outWhite(){
if(white>=5){
white-=5;
System.out.println("放水一次");
}else{
white=0;
}
}
public static void main(String[] args) {
Pool in=new Pool();
Pool out=new Pool();
in.inWhite();
System.out.println("水量:"+Pool.white);//這裡的Pool.white,in.white,out.white所代表的是同一個變數
in.inWhite();
System.out.println("水量:"+in.white);
out.outWhite();
System.out.println("水量:"+out.white);
}
}
同一個類的不同例項物件,共用同一個靜態變數,如果一個物件將其改變,另一個物件的靜態變數也會改變。
public class Test {
public static int x;
public int y;
public Test(int x,int y){
this.x=x;
this.y=y;
}
public static void main(String[] args) {
Test a=new Test(0,1); //給x傳0 給y傳1
Test b=new Test(11,111); //給x傳11 給y傳111
System.out.println("a的x的值:"+a.x);
System.out.println("a的y的值:"+a.y);
System.out.println("b的x的值:"+b.x);
System.out.println("b的y的值:"+b.y);
}
}
我們可以看出,靜態變數的值改變了
如下圖所示,兩個物件x靜態變數同時指向了同一塊記憶體區域,而非靜態變數y則是指向了不同的區域。
當a.x給靜態變數賦值,b.x又對靜態變數重新複製,導致了a.x指向的是b.x的新值。
靜態常量
用final static修飾一個成員變數,這個成員變數就會變成一個靜態常量。
舉個例子來看如何使用一個靜態常量,比如我們計算圓的面積和球的體積時,都會用到 π 的值,這是我們就可以定義一個 PI 來作為靜態常量存放 π 的值,而不必去重複定義。
package com.hanqi.maya.model;
public class Grap {
final static double PI=3.1415926;
//PI 作為靜態常量,應該大寫,PI的值不可以被修改
public static void main(String[] args) {
Cir c=new Cir(3);
Sph s=new Sph(3);
}
}
class Cir{
double r;
double m;
public Cir(double r){
this.r=r;
m=Grap.PI*r*r;
System.out.println("圓的半徑:"+r);
System.out.println("圓的面積:"+m);
}
}
class Sph{
double r;
double vol;
public Sph(double r){
this.r=r;
vol=4/3*Grap.PI*r*r*r;
System.out.println("球的半徑:"+r);
System.out.println("球的體積:"+vol);
}
}
靜態方法
我們知道如果想要使用類中的成員方法,需要先將這個類進行例項化,但有些時候我們不想或者無法建立物件時,還要呼叫類中的方法才能夠完成業務邏輯,此時我們就可以使用靜態方法。呼叫類的靜態方法,無需建立物件。
public class Staticd {
public static void show(){
System.out.println("這是靜態方法show()");
}
public static void main(String[] args) {
//不需要通過 new 例項化,直接通過 類名 . 呼叫就可以使用靜態方法
Staticd.show();
}
}
補充:System.out.println( ); 方法就是一個典型的靜態方法,我們沒有建立System物件,就實現了輸出功能,類中的 maiin 方法同樣也是靜態方法。
靜態程式碼塊
在類中的成員方法之外,用static修飾程式碼區域可以稱之為靜態程式碼塊,定義一塊靜態程式碼塊,可以完成類的初始化操作,在類宣告時就會執行。
public class Staticd {
{
System.out.println("這裡是非靜態程式碼塊");
}
//靜態程式碼塊會在非靜態程式碼塊之前執行,跟寫的位置無關
static{
System.out.println("這裡是靜態程式碼塊");
}
public Staticd(){
//構造方法 在 new 的時候執行
System.out.println("這裡是構造方法");
}
//成員方法在呼叫的時候執行
public void method1(){
System.out.println("這裡是成員方法 1");
}
public void method2(){
System.out.println("這裡是成員方法 2");
}
public static void main(String[] args) {
Staticd test =new Staticd();
test.method1();
}
}
有執行結果我們可以得出:
1.靜態程式碼塊在非靜態程式碼塊之前執行
2.構造方法在new的時候執行
3.成員方法在呼叫的時候執行,不呼叫不執行
4.執行的優先順序如上圖所示
五、類的主方法 main 方法
主方法是類的入口點,他定義了程式從何處開始:主方法提供對程式流向的控制,Java編譯器通過主方法來執行程式。
注意:
主方法是靜態的,所以要直接在主方法中呼叫其他方法德華該方法必須也是靜態的。
主方法沒有返回值。
主方法的形參是陣列。
六、物件的特性
物件的建立
Java中使用 new 操作符呼叫構造方法就可以建立一個物件。
物件的引用
在Java中一切都可以看作是物件,但真正的操作識別符號實質上是一個引用。
引用只是存放了一個物件的記憶體地址,並非存放了一個物件,嚴格的說引用和物件是不同的,但是可以將這種區別忽略,如可以簡單的說book是Book類的一個物件,而事實上應該是book包含Book物件的一個引用。
public class Book {
String name="《Java》";
public String getName(){
return name;
}
public static void main(String[] args) {
Book b=new Book();
System.out.println(b.getName());
//輸出的結果完全相同
System.out.println(new Book().name);
}
}
輸出的結果完全一樣, b.getName() 和 new Book().name 是一樣的,說明 new Book() 才是一個真正的物件實體,而 b 只是一個引用,他指向了new Book() 的記憶體地址。
物件的使用
當用戶用new操作符建立了一個物件之後,可以使用 “物件.類成員” 來獲取物件的屬性和行為。物件的屬性和行為在類中是通過類成員變數和成員方法的形式來表示的,所以當物件被例項化之後,也就獲得了相應的屬性和行為。
public class Test1 {
public int i=10;
public void Call(){
System.out.println("呼叫Call方法");
for(i=0;i<3;i++){
System.out.print(i+" ");
}
System.out.println();
}
public static void main(String[] args) {
Test1 t1=new Test1();
Test1 t2=new Test1();
System.out.println("t1.i:"+t1.i);
t1.Call(); //使用成員方法輸出成員變數的值
t2.i=5; //利用物件來呼叫類的成員
System.out.println("t2.i:"+t2.i);
t2.Call();
}
}
物件的銷燬
每個物件都有生命週期,當物件的生命週期結束時,分配給該物件的記憶體地址將被回收。在其它語言中需要手動回收廢棄的物件,但是Java又有一套完整的垃圾回收機制,使用者不必擔心廢棄的物件佔用記憶體,垃圾回收器將回收無用但佔記憶體的資源。
補充:finalize()方法
finalize()是所有類的父類 Object 提供的方法
如果使用者在類中定義了finalize()方法,在垃圾回收是首先呼叫該方法,並且在下一次垃圾回收動作發生時,才能真正回收被物件佔用的記憶體。
垃圾回收器只能回收用new建立的物件,如果某些物件不是通過new在記憶體中獲取了一塊區域,這種物件可能不會被垃圾回收系統識別。
由於垃圾回收不受人為控制,具體執行時間也不確定,所以finalize() 方法也就無法執行,為此,Java提供了System.gc()方法強制啟動垃圾回收器。