天天看點

JavaSE-類與對象1

1.類與對象的定義與使用

① 類與對象的概念

    所謂類,是一個共性的概念,而對象指的是一個具體的、可以使用的事物。

    首先産生類(類是生産對象的藍圖),而後才可以産生對象,對象的所有行為一定在類中進行了完整的定義。

類的組成:

1.屬性(變量,描述每個對象的具體特點)

2.方法(操作的行為)

② 類與對象的定義與使用

  • 定義一個類

文法如下:

class 類名稱{

    屬性1;

    屬性2;

    ...

    屬性n;

    方法1(){};

    方法2(){};

    ...

    方法n(){};

}

注意:此時的方法不再由主類直接調用,而是由該類的執行個體化對象進行調用

類對象執行個體化文法:

類名稱 類對象名稱 = new 類名稱();

示例:定義一個Person類并執行個體化類對象

class Person{
    public String name;
    public int age;
    public Person(String n,int a){
        name = n;
        age = a;
    }
    public void getPersonInfo(){
        System.out.println("姓名:" + name + ",年齡" + age);
    }
}
public class Test{
    Person p1 = new Person("李四",19);//直接通過構造函數執行個體化
    Person p2 = new Person();
    //通過對象修改類屬性、調用類方法
    p2.name = "張三";
    p2.age = 18;
    p1.getPersonInfo();
    p2.getPersonInfo();
}
           

注意:隻要出現了關鍵字new,就開辟了記憶體空間;

          Java中,所謂的性能調優,調整的就是記憶體問題。

③ 對象記憶體分析

       棧記憶體(虛拟機局部變量表):存放局部變量(包含編譯期可知的各種基本資料類型,對象引用-即堆記憶體位址,可以簡單了解為對象的名稱)。

      Java棧是與線程對應起來的,每當建立一個線程,JVM就會為這個線程建立一個對應的Java棧。

      堆記憶體:儲存真正的資料。即對象的屬性資訊。

代碼示例:

class Person{
    String name;
    int age;
}
public class Test{
    public static void main(String[] args){
        Person per = new Person(); 
        per.name = "張三";
        per.age = 30;
    }
}
           

對于主函數中的第一條語句:Person per = new Person();//在堆上配置設定記憶體并産生Person類的對象per引用這部分記憶體。

記憶體分析如下:

JavaSE-類與對象1

接下來兩句:

  per.name = "張三";

  per.age = 30;

記憶體圖分析如下:

JavaSE-類與對象1

注意:對象必須在執行個體化之後調用,否則會産生NullPointerExecption(運作時錯誤),編譯時不會出錯;

        隻有引用類型(數組、類、接口)才會産生此類異常,是以在後續寫代碼過程中如果出現此類異常,就可以根據出錯位置檢視引用資料類型是否初始化。

④ 引用傳遞分析

本質:一塊堆記憶體可以被多個棧記憶體所指向

代碼示例:

Person p1 = new Person();
Person p2 = new Person();
p2 = p1;
           

記憶體分析圖如下:

JavaSE-類與對象1

    垃圾空間:沒有任何棧記憶體指向的堆記憶體空間

    所有的垃圾空間會不定期GC,GC不會影響性能,是以在開發中一定要控制好對象的産生數量(無用的對象盡可能少産生)

2.private實作封裝處理&構造方法(匿名對象)

① private實作封裝處理

    以上所寫的所有程式都屬于無封裝程式。

    在不進行封裝的情況下,在主類中可以任意對Person類的對象屬性進行修改。此時,要回避此類問題,讓類内部操作堆外部不可見(對象不能直接操作屬性),可以使用private進行封裝。

示例:在Person類中使用private封裝屬性

private String name;
private int age;
           

    此時使用private堆屬性進行了封裝,要通路私有屬性,按照Java的設計原則必須在類中提供以下兩種方法:

1.getter方法:主要用于進行屬性内容的設定和修改

2.getter方法:主要用于屬性内容的取得

示例:擴充Person類中的方法,實作對Person類屬性的封裝

class Person{
    private String name;
    private int age;
    public void setName(String a){
        name = n;
    }
    public String getName(){
        return name;
    }
    public void setAge(int i){
        if(i > 0 && i <= 100){
            age = i;
        }else{
            age = 0;
        }
    }
    public int getAge(){
        return  age;
    }
    public void getPersonInfo(){
        return "姓名:"+name+",年齡:"+age;
    }
}
public class Test{
    public static void main(String[] args){
        Person p = new Person();
        p.setName("張三");
        p.setAge(18);
        p.getPersonInfo();
    }
}
           

    通過以上代碼我麼可以發現,private實作封裝的最大特征在于:隻允許本類通路,而不允許外部類通路。

  • 類的設計原則:

      1)在編寫類時,類中的所有屬性必須使用private封裝

      2)屬性若要被外部通路,必須定義setter、getter方法。

②構造方法與匿名對象

回顧産生對象的方法:

①類名稱 ②對象名稱 = ③new ④類名稱();
  • ① 任何對象都有對應的類,類時對象的藍圖 ;
  • ② 對象名稱是一個唯一的标記,引用一塊堆記憶體;
  • ③ new表示開辟新的堆記憶體空間;
  • ④ 構造方法

通過上述分析可知,所謂的構造方法就是使用關鍵字new執行個體化新對象來進行調用的操作方法。對于構造方法的定義,也需要遵循以下原則:

1.方法名必須與類名稱相同;

2.構造方法沒有傳回值類型聲明;

3.每一個類中一定至少存在一個構造方法(沒有明确定義,則系統自動生成一個無參構造)

問題:構造方法無傳回值,為什麼沒有void聲明?

   首先我們先來看一下類的組成:屬性、構造方法、普通方法

1.屬性是在對象開辟堆記憶體時開辟的空間;

2.構造方法是在使用new後調用的;

3.普通方法是在空間開辟了,構造方法執行之後可以多次調用的。

   部分代碼示例:

public void Person(){} //命名不标準的普通方法
public Person(){}    //無參構造方法
           

         從以上描述我們可以看出,編譯器是根據程式結構來區分普通方法和構造方法的,是以在構造方法前沒有傳回值類型聲明。

        若類中定義了構造方法,則預設的無參構造将不再生成。

示例:使用構造方法設定對象屬性

class Person{
    private String name;
    private int age;
    public Person(String n,int a){
        name = n;
        age = a;
    }    
    public void setName(String n){
        name = n ;
    }
    public String getName(){
        return name;
    }
    public void setAge(int i){
        if (i>0&&i<=200) {
            age = i ;
        }else {
            age = 0 ;
        }
    }
    public int getAge(){
        return age;
    }
    public void getPersonInfo(){
        System.out.println("姓名:"+name+",年齡:"+age);
    }
}
public class Test{
    public static void main(String[] args) {
        Person person = new Person("張三",-200);//使用構造函數對對象屬性進行指派
        person.getPersonInfo();
    }
}
           

       構造方法的調用和對象記憶體配置設定幾乎是同步完成的,是以我們可以利用構造方法來為類中的屬性進行初始化操作(可以避免多次的setter調用)

構造方法重載:參數類型或個數不同

示例:

public Person(){

    System.out.println("---無參構造---");    

}

public Person(String n){

    name = n;

    System.out.println("---有參構造---");

}

建議:若幹構造方法,最好按照參數個數升序或降序編寫;

          在進行類定義時:①定義屬性-->②定義構造方法-->③定義普通方法

範例:匿名對象

new Person("張三",20).getPersonInfo();

注意:由于匿名對象不會有任何的棧空間指向,是以使用一次後就成為垃圾空間。

3.this關鍵字

① 用途

    ⅰthis調用本類屬性

    ⅱ this調用本類方法

    ⅲ this表示目前對象

② this調用本類屬性

代碼示例:不用this關鍵字時

class Person{
    private String name;
    private int age;
    public Person(String name,int age){
        name = name ;
        age = age ;
    }
    public String getPersonInfo(){
        return "姓名:" + name + ",年齡:"+age;
    }
}
public class Test{
    public static void main(String[] args) {
        Person per = new Person("張三",20);
        System.out.println(per.getPersonInfo());
    }
}
           

      通過上述代碼我們可以發現:當參數與類中屬性同名時,類中屬性無法被正确指派。此時若加上this關鍵字就可以解決這個問題。

示例:使用this關鍵字修改上述代碼

public Person(String name,int age){
    this.name = name;
    this.age = age;
}
           

為了避免類似問題,隻要在類中方法通路類中屬性,一定要加this關鍵字。

②this調用本類方法

this調用本類方法有兩種情況:

    ⅰ調用普通方法:this.方法名稱(參數);

    ⅱ 調用構造方法:this(參數);

示例:this調用普通方法

class Person{
    private String name;
    private int age;
    public Person(String name,int age){
        this.name = name;
        this,age = age;
        this.print(); //this調用普通方法
    }
    public String getPersonInfo(){
    return "姓名:" + name + ",年齡:"+age;
  }
  public void print(){
    System.out.println("*****************");
  }
}
public class Test{
    public static void main(String[] args) {
        Person per = new Person("張三",20);
        System.out.println(per.getPersonInfo());
    }
}
           

示例:多個構造函數同時存在

class Person{
    private String name;
    private int age;
    public Person(){
        System.out.println("********産生一個新的Person對象********");
    }
    public Person(String name){
        System.out.println("********産生一個新的Person對象********");
        this.name = name ;
    }
    public Person(String name,int age){
        System.out.println("********産生一個新的Person對象********");
        this.name = name ;
        this.age = age ;
    }
    public String getPersonInfo(){
        return "姓名:" + name + ",年齡:"+age;
    }
}
public class Test{
    public static void main(String[] args) {
        Person per1 = new Person();
        Person per2 = new Person("張三");
        Person per3 = new Person("李四",20);
        System.out.println(per1.getPersonInfo());
        System.out.println(per2.getPersonInfo());
        System.out.println(per3.getPersonInfo());
    }
}
           

Java中支援構造函數的互相調用

示例:this調用構造函數(上述代碼的修改)

public Person(){
    System.out.println("---産生一個新的Person對象---");
}
public Person(String name){
    this();//調用本類無參構造
    this.name = name;
}
public  Person(String name,int age){
    this(name);//調用本類有參構造
    this.age = age;
}
           
  • 使用this調用構造方法時應注意:

1.this調用構造方法的語句必須放在構造方法首行;

2.使用this調用構造方法時,必須留有出口(即不能成環)

④ 使用this表示目前對象

示例:

class Person{
    public void print(){
        System.out.println("[PRINT]方法:"+this);
    }
}
public class Test{
    public static void main(String[] args) {
        Person p1 = new Person();
        System.out.println("[MAIN]方法:"+p1);
        p1.print();
        System.out.println("=================");
        Person p2 = new Person();
        System.out.println("[MAIN]方法:"+p2);
        p2.print();
    }
}
           

    隻要對象調用了本類中的方法,這個this就表示目前執行的對象。

5.static關鍵字

static可以修飾屬性和方法

① static屬性(類屬性)

示例:執行個體屬性的記憶體分析

class Person{
    String Country = "中國";
    String name;
    int age;
    public void getPersonInfo(){
        System.out.println("姓名:"+this.name+",年齡:"+this.age+",國家:"+this.Country);
    }
}
public class Test{
    public static void main(String[] args){
        Person p1 = new Person();
        p1.name = "張三";
        p1.age = 19;
        Person p2 = newPerson();
        p2.name = "李四";
        p2.age = 20;
        p1.getPersonInfo();
        p2.getPersonInfo();
        
    }    
}
           

記憶體圖解:

JavaSE-類與對象1

    傳統屬性所具備的特征:儲存在堆記憶體中,且每個對象獨享屬性。

    描述共享屬性,隻需要再屬性前添加static關鍵字即可。

    static屬性又稱為類屬性,儲存在全局資料區的記憶體之中,所有對象都可以進行該資料區的通路。

若修改上述代碼堆country的定義為:

static String Country = "中國";

此時記憶體分析圖如下:

JavaSE-類與對象1

結論:

1.通路static屬性(類屬性)應使用類名稱.屬性名

2.所有非static屬性(執行個體變量)必須在對象執行個體化後使用,而static屬性(類屬性)不受對象執行個體化控制。

此時,如果我們修改country屬性,所有對象都同步此值

定義類時,如何選擇執行個體變量和類屬性變量?

1.定義類時,一般情況下不會考慮static屬性,以非static屬性為主

2.如果需要描述共享屬性的概念,或者不受對象執行個體化的控制,使用static

② static方法(類方法)

使用static定義的方法,直接通過類名通路

示例:

class Person{
    private static String country;
    private String name;
    private int age;
    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }
    public static void setCountry(String c){
        country = c;
    }
    public void getPersonInfo(){
        System.out.println("姓名:"+this.name+",年齡:"+this.age+",國家:"+this.country);
    }
}
public class Test{
    public static void main(){
        Person.setCountry("中國");
        Person person = new Person("張三",18);
        person.getPersonInfo();
    }
}
           

注意:

    (1)所有static方法不允許調用非static定義的屬性或方法;

    (2)所有的非static方法允許通路static方法或屬性。

使用static定義方法的目的:某些方法不希望受類的控制,即可以在沒有執行個體化對象的時候執行(廣泛存在于工具類中)