java面向對象基礎(一)

来源:https://www.cnblogs.com/f-ck-need-u/archive/2017/12/27/8127522.html
-Advertisement-
Play Games

基礎 類有屬性和方法,它們對本類有效(作用範圍)。類的屬性就是成員變數,它預設會賦值初始化。類的方法是類具有的一些行為。 類是抽象的,將它們實例化後就是對象(通過new進行實例化),各實例化後的對象都具有這些成員變數的屬性,且賦有具體的值,如果某對象沒有為成員變數賦值,則採用預設初始化時的值。每個n ...


基礎

類有屬性和方法,它們對本類有效(作用範圍)。類的屬性就是成員變數,它預設會賦值初始化。類的方法是類具有的一些行為。

類是抽象的,將它們實例化後就是對象(通過new進行實例化),各實例化後的對象都具有這些成員變數的屬性,且賦有具體的值,如果某對象沒有為成員變數賦值,則採用預設初始化時的值。每個new出來的對象都有自己獨立的成員變數,但某個類的所有對象都共用類的方法,因為類方法只是一段放在代碼區的代碼,只有執行調用類方法時才會產生相關內容。

有了實例化後的對象,就可以引用對象的屬性並調用對象的方法(實際上是類的方法,方法是共用的,並不屬於某個單獨的對象),這樣就可以實現這個對象的相關操作。引用對象的屬性方式為"對象名.成員變數",調用對象的方法的方式為"對象名.方法"雖說方法是各對象共用的,但顯然,"對象名1.方法1"的方法1執行時,方法內部的變數採用的都是對象名1的成員變數。

示例分析:

以一個三維空間上的點類來說,點具有三維坐標xyz,x、y、z就是它們的屬性,需要定義為點類的成員變數。點可以求出它到原點的距離、到另一個點的距離,求距離就是通過類的方法(函數)實現。通過new這個點類,就可以實實在在地創建一個點,new一次就一個點,每個點都有自己的xyz屬性,每個點的成員變數都在new出來的時候和對象一起存放在heap記憶體區。每個點都擁有大家共用的求距離方法getDistance()。於是,就可以將這個點類定義為下麵的形式:

class Point {

    //定義成員變數,即三個坐標,坐標可能是小數,因此定義為double類型
    double x; 
    double y;
    double z;

    //定義構造方法,使得以後在new一個點對象的時候為點的成員變數xyz賦值
    Point(double _x,double _y,double _z) {
        x = _x;
        y = _y;
        z = _z;
    }
    //有了構造方法,就可以new對象的時候賦值,例如賦值點p對象的xyz分別為1/2/3:
    //Point p =  new Point(1.0,2.0,3.0);

    //定義求兩點間距離的方法getDistance()。
    //涉及到兩個點:一個是調用該方法的點對象自身,一個是目標點對象
    //因此,需要將目標點對象作為方法的形參,並使用目標點的坐標屬性
    double getDistance(Point px) {
        return (x-px.x)*(x-px.x)+(y-px.y)*(y-px.y)+(z-px.z)*(z-px.z);
    }

    //有了方法,以後就可以調用該方法求距離,例如,求點(1,2,3)到原點(0,0,0)的距離
    //Point p = new Point(1.0,2.0,3.0);
    //Point p1 = new Point(0.0,0.0,0.0);
    //System.out.println(p.getDistance(p1));
    //這表示調用點p的方法,求點p到原點p1的距離,
    //p1的屬性賦值給方法的形參px(px指向點p1對象),因此其坐標值為(0,0,0)
    //因為調用的是點p的方法,因此方法中的x/y/z是點p的成員變數值,即(1,2,3)
}

將上述代碼整理,並寫一個main方法,就可以實現一個計算兩點距離的小程式。例如,TestPoint.java文件內是如下內容:

class Point {
    double x,y,z;

    Point(double _x,double _y,double _z) {
        x = _x;
        y = _y;
        z = _z;
    }

    double getDistance(Point p){
        return (x-p.x)*(x-p.x)+(y-p.y)*(y-p.y)+(z-p.z)*(z-p.z);
    }
}

public class TestPoint {
    public static void main(String[] args) {
        Point p = new Point(1.0,2.0,3.0);
        Point p1 = new Point(1.0,0.0,0.0);
        System.out.println(p.getDistance(p1));
    }
}

從上面的例子中可以感受到,面向對象更抽象地說是面向類。在實現某個功能的時候,例如求兩個三維點之間的距離時,將點的屬性和求距離的方法定義到點類中,以後就不用管點的xyz屬性、求三維點之間距離的表達式方法,只要在需要時面向這個類new出點對象,它就有了點的xyz屬性,再調用點對象的求距離的方法就可以了。有了面向對象,求三維點距離時,只需知道兩件事:為成員變數xyz賦值;記得點類中的方法的名稱。這就像為了查看文本內容執行cat命令一樣,只要記得cat命令的名稱、功能和選項參數即可,無需關心cat的內部機制是如何讀取文本內容並將其顯示出來的。

在定義一個方法時,需要考慮三個問題:方法的名稱如何取、方法的參數、方法是否有返回值。方法的名稱暫且不說,方法的參數必須要考慮清楚,例如求兩點的距離時,參數可以是某個點的坐標,也可以直接是一個點對象。如果已經定義了點類,那麼使用點對象作為參數更符合面向對象的原則;方法的返回值同樣重要,返回值決定了這個方法的性質,例如判斷兩點間的距離是否大於20,就應該返回布爾類型,而不是double類型。

構造方法

構造方法和類同名,它的作用是在對象被new出來時做初始化行為。因為構造方法的目的是初始化,因此構造方法必須不能有返回值,即不能寫上數據類型或void關鍵字。

例如:

class Point {
    double x,y,z;

    //構造方法,註意其名稱必須為Point()
    Point(double _x,double _y,double _z) {
        x = _x;
        y = _y;
        z = _z;
    }
}

以後就可以在new對象的時候進行初始化,例如:

Point p = new Point(1,2,3);

如果沒有顯式定義構造方法,則隱含了一個空構造方法,例如下麵的代碼中,兩個class是完全等價的。

class Point {
    double x,y,z;
}

class Point {
    double x,y,z;
    Point() {}
}

因為初始化有預設的值,所以它們還等價於(0.0是初始化double時的預設值):

class Point {
    double x,y,z;
    Point() {
        x = 0.0;
        y = 0.0;
        z = 0.0;
    }
}

正因為有隱含的空構造方法,才能在new對象的時候不使用任何參數就能進行成員變數的初始化。例如:

Point p = new Point();

在new對象的時候,對象的參數必須和構造方法完全對應,例如定義了構造方法Point(double _x,double _y,double _z)後,就只能new Point(value1,value2,value3),而不能不接任何參數new Point()或接少於3個的參數new Point(value1,value2)

方法的重載(overload)

當兩個或多個方法的名稱相同,只有參數不同時(可以是參數的個數、參數的數據類型不同),它們就構成了方法的重載。

方法的重載大大減少了方法數量的定義。例如,要求兩個值中較大者,考慮到值可以是整形也可以是小數,於是使用如下方式:

public class Num {

    void intMax(int a,int b) {
        System.out.println(a>b ? a : b);
    }

    void doubleMax(double a,double b) {
        System.out.println(a>b ? a : b);
    }

    public static void main(String[] args) {
        Num n = new Num();
        n.intMax(2,3);
        n.doubleMax(2.0,3.0);
    }
}

這裡第一個方法intMax()和第二個方法doubleMax()實際上是重覆的,僅僅只是參數類型上不同。這樣的設計很不方便,不僅在比較數值時不知道應該調用哪一個方法,還要知道各個方法的區別。

而使用重載就不再有這樣的問題。

public class Num {

    void Max(int a,int b) {
        System.out.println(a>b ? a : b);
    }

    void Max(double a,double b) {
        System.out.println(a>b ? a : b);
    }

    public static void main(String[] args) {
        Num n = new Num();
        n.Max(2,3);
        n.Max(2.0,3.0);
    }
}

這兩個Max方法名稱相同,僅僅只是參數不同。在調用Max()的時候,根據傳遞的實參(2,3)和(2.0,3.0),它能能夠區分出前者應該使用第一個Max(),後者使用第二個Max()。

重載的本質是在調用方法時能夠通過傳遞的參數個數、參數的值篩選出具體應該使用哪個方法。

例如下麵的方法中,前4個都能構成方法重載,而最後一個不能,因為它的定義方式不同。從本質上來說,是因為調用Max()傳遞兩個int整型數值時,無法確定是選擇第一個方法還是最後一個方法,而它們又正好是衝突的,因此它們不構成重載。

void Max(int a,int b) {}
void Max(int a,int b,int c) {}
void Max() {}
void Max(char a,char b)
Max(int a,int b) {}

this關鍵字

在類的方法定義中使用this關鍵字可以代表該方法的對象的引用,它是new出來的對象中指向對象自身的關鍵字。當必須指出當前所使用方法的對象是誰時需要使用this,使用this還可以避免成員變數和形參重名的問題。

public class TestThis {
    int i = 100;
    TestThis(int i) {
        this.i = i;   //i為形參i(就近原則),this.i代表的是對象中的i,即成員變數i
                      //this在此避免了成員變數和形參同名的問題
    }

    TestThis increment() {
        ++i;           //由tt.increment()調用,因此i是成員變數
        return this;   //返回的this代表TestThis類的對象自身
                       //this在此表示對象自身
    }

    void print() {
        System.out.println("i=" + i);
    }

public static void main(String[] args) {
    TestThis tt = new TestThis(10);
    System.out.println("i= " + tt.i);
    tt.increment().increment().print();
    }
}

上述示例中,new TestThis創建了一個TestThis對象,tt指向該對象。tt.increment()表示調用一次tt對象的方法,此時i自增一次,並返回this,this代表的對象正是tt指向的對象,是TestThis類的對象,所以他也有increment()方法,所以還可以繼續執行increment()方法,最後再次返回this,最後執行print()方法,輸出自增兩次後的i值。

註意,雖然可以return this來返回自身對象的引用,但卻不能使用return super來返回父類對象的引用。也就是說,父類對象只能操作其內某個成員,不能直接返回父對象整體。

static關鍵字

static聲明的成員變數為靜態成員變數,它是該類的共用變數。在第一次使用時被初始化,對於該類的所有對象來說,static成員變數只有一份。

可以通過對象引用或直接通過類名來引用靜態成員。即使在沒有new出任何對象時,也能直接引用靜態成員,因為它屬於類假如類名為T,靜態成員變數有i,靜態方法有m(),則可以直接使用T.i和T.m()分別引用。當new出來T的一個對象t時,可以使用t.i或t.m(),這和使用T.i和T.m()是完全等價的。(實際上,在不產生衝突的情況下,即使不寫類名也可以直接引用靜態變數、靜態方法)

用static聲明的方法為靜態方法,靜態方法不是針對某個對象來調用的。在調用靜態方法時,不會將對象的引用傳遞給它,所以在static方法中不可訪問非static的成員,即靜態方法中不可以訪問非靜態成員變數和其他非靜態方法。換句話說,因為靜態方法屬於類,靜態方法看不到heap中各對象中的成員,它只能看到data segment中的靜態成員。

public class Student {
    static int cnt = 0;  //static成員變數
    int id;
    String name;

    Student(String name) {
        id = ++cnt;
        this.name = name;
    }

    public void info() {
        System.out.println("id=" + id + ", name=" + name);
    }

    public static void main(String[] args) {
        Student.cnt = 100;   //在new之前就可以使用類名直接引用static成員變數並賦值
                             //還可以直接寫為"cnt = 100;"
        Student s1 = new Student("Malongshuai");
        Student s2 = new Student("Gaoxiaofang");
        s1.info();
        s2.info();
    }
}

在上面的例子中,靜態變數cnt使用類名直接訪問,並使用靜態變數cnt作為成員變數id的賦值基礎("id=++cnt;")。由於靜態變數只在最初進行了賦值,後續一直都通過自增的方式進行改變,這是靜態變數的廣為使用的功能:"充當計數器"

如果把上面的static關鍵字去掉,並註釋Student.cnt行,再編譯運行,那麼id的輸出結果將總是1,因為cnt作為成員變數被初始化,所有對象的cnt都一樣,從而導致id的值也一樣。

無論是靜態變數還是靜態方法,它們都可以在new出對象之前直接引用,這時還不存在對象,因此在靜態方法中無法使用非靜態的成員變數(它們還不存在)。正如上面的public static void main(),它是靜態的,可以直接引用cnt,它不需要在運行時先去new一個對象才能執行,否則main就太"不智能",每次執行都要先new出對象。

如果將static關鍵字去掉,在編譯時將報如下錯誤:

λ javac Student.java
Student.java:16: 錯誤: 無法從靜態上下文中引用非靜態 變數 cnt
        Student.cnt=100;
               ^
1 個錯誤

 

註:若您覺得這篇文章還不錯請點擊右下角推薦,您的支持能激發作者更大的寫作熱情,非常感謝!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • #有‘*’為重點import timeprint(time.time())#以秒的形式返回******time.sleep(3) ******print(time.clock()) #cpu執行的時間print(time.gmtime()) # 結構化時間,本初子午線那裡的時間time.struct ...
  • 一 前段時間自學了一段時間的Python,想著濃一點項目來練練手。看著大佬們一說就是爬了100W+的數據就非常的羡慕,不過對於我這種初學者來說,也就爬一爬圖片。 我相信很多人的第一個爬蟲程式都是爬去貼吧的圖片,嗯,我平時不玩貼吧,加上我覺得豆瓣挺良心的,我就爬了豆瓣首頁上面的圖片。其實最剛開始是想爬 ...
  • 本文簡單的介紹了Servlet處理響應的基本流程以及Servlet的生命周期 ...
  • 一、wxPython介紹 1、wxPython是Python語言的一套優秀的GUI圖形庫。wxPython可以很方便的創建完整的、功能鍵全的GUI用戶界面。 wxPython是作為優秀的跨平臺GUI庫wxWidgets的Python封裝和Python模塊的方式提供給用戶的。 2、wxPython是跨 ...
  • 原題是這樣的: 給出一個字元串數組S,找到其中所有的亂序字元串(Anagram)。如果一個字元串是亂序字元串,那麼他存在一個字母集合相同,但順序不同的字元串也在S中。 樣例 對於字元串數組 ["lint","intl","inlt","code"] 返回 ["lint","inlt","intl"] ...
  • Pandas基礎篇 Pandas基於Numpy開發,提供了很多高級的數據處理功能。 1、Pandas中的數據對象 Series和DataFrame是Pandas中最常用的兩個對象。 1.1 Series對象 是Pandas中最基本的對象,可用Numpy的數組處理函數直接對Series對象進行處理。支 ...
  • 許可權修飾符 許可權修飾符包括public、private、protected和不加任何修飾符的default,它們都可以修飾方法和變數。其中public和預設的default(不加任何修飾符)這兩個還可以修飾class。private和protected修飾類的情況只能在使用內部類時修飾,正常情況下不 ...
  • 類路徑(classpath) java編譯器編譯.java文件和java虛擬機執行.class文件時的路徑和寫法不一樣。 在沒有設置任何classpath環境變數的情況下,javac可以編譯全路徑的.java文件。例如: 編譯後,在.java同路徑目錄下生成class文件。 預設java虛擬機要從c ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...