詳解Java的物件建立
阿新 • • 發佈:2020-07-28
# 1. 前言
在[《還不清楚怎樣面向物件?》](https://mp.weixin.qq.com/s/_G6RjwthUybz5KsDsLo7kQ)和[《面向物件再探究》](https://mp.weixin.qq.com/s/NQK3aYXUh3eOwmwmP_VluQ)兩篇文章中,都介紹了關於面向物件程式設計的概念和特點。其中也涉及到了許多程式碼,比如:
```java
Dog dog = new Dog();
```
這篇文章就主要來談談建立物件時的具體操作。
# 2. 引入例子
下面是一個`Dog`類:
```java
/**
* @author Xing Xiaoguan (xingrenguanxue)
*/
public class Dog {
private String name;
private int age;
private String address;
public void say() {
System.out.println("我叫" + name + ",今年" + age + "歲了,家住" + address + ",汪汪汪...");
}
//getters 和 setters
}
```
下面是一個`Test`類,建立了一個`Dog`物件,然後進行相關操作:
```java
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.setName("哮天犬");
dog.setAge(1);
dog.setAddress("光明小區")
dog.say();
}
}
```
輸出:
```
我叫小黑,今年1歲了,家住光明小區,汪汪汪...
```
# 3. 物件和物件變數
物件是根據類創造出來的,我們使用的是具體的物件,而不是類。要想使用物件,那物件必須得先被創建出來才行。下面一行程式碼是建立物件的語句:
```java
Dog dog = new Dog();
```
我們將其分成三部分來看:
(1)`new `:如果你想建立一個物件,那麼必須使用`new`操作符。
(2)`Dog()`:這是一個構造器,構造器是一個特殊的方法。通過呼叫該構造器,我們可以建立並初始化一個物件出來。
`new Dog()`連起來就能正確地創建出一個物件了。但是我們創造出來的物件並不只會使用一次,而會使用許多次,所以我們需要給該物件 **“取一個名字”**,保證它“隨叫隨到”。這就需要第三部分了:
(3)`Dog dog`:宣告一個`Dog`型別的、名為`dog`的變數。它就類似於`int number`,宣告一個`int`型別的`number`變數。
然後我們使用`=`進行賦值(引用),便給創建出的物件 **“取一個名字”** 叫`dog`,以後可以稱它為`dog`物件。
這裡可能會出現一個誤區,認為:`Dog dog`部分便能創建出一個`dog`物件,這是錯誤的。應當明確:`dog`從頭到尾都只是一個**變數**而已。這個變數和使用`int number`、`String str`等方式宣告的變數,除了型別不同之外並無差別。
**真正創建出物件的是`new Dog()`部分。**
下面解釋一下 **“取一個名字”** 是什麼意思。
在Java中,物件變數中儲存的並不是物件,真正的變數在記憶體的某個地方躺著呢。該變數記錄的是物件在記憶體中的位置,我們有了物件變數,就有了物件的位置,有了位置,就能找到真正的物件。
看下面的程式碼:
```java
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();//創建出一個dog物件
System.out.println(dog);//列印dog
}
}
列印結果:basic.Dog@1b6d3586
```
`1b6d3586`便是`dog`物件在記憶體中的地址。
# 4. 構造器
前面提到,建立一個物件的關鍵在於使用`new`操作符和構造器。構造器是一個特殊的方法,通過呼叫構造器,我們可以建立並初始化一個物件出來。
構造器的特點:
1. 有一個訪問修飾符
2. 構造器的名字和類名相同
3. 構造器沒有返回值
4. 構造器要和`new`操作符一起使用
5. 構造器可以有0個、1個或多個構造器
6. 一個類中可以有多個構造器
## 4.1. 無參構造器
即沒有引數的構造器,如`Dog()`。無參構造器是Java預設的構造器,如果你在編寫類時沒有寫構造器,那麼Java會在類中預設提供一個無參構造器。
在`Dog`類中並沒有寫構造器,`Dog`類預設擁有下面的無參構造器:
```java
public Dog() {
}
```
## 4.2. 有參構造器
有參構造器,即有引數的構造器。例如:
```java
public Dog(String n, int a, String addr) {
name = n;
age = a;
address = addr;
}
```
這個有參構造器非常簡單,分別給`n`、`a`引數傳值,然後給物件的屬性賦值。美中不足的是引數的變數名取得不夠“見名知意”,所以我們通常這樣寫:
```java
public Dog(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
```
引數的名字和物件的成員變數名一樣,這樣,引數的意義就很清晰了。在賦值的時候,使用`this`關鍵字區分二者,因為`this`代表我們所建立的物件,`this.name`即物件的成員變數。
有了有參構造器,我們就可以在建立物件時初始化物件的屬性,比如:
```java
public static void main(String[] args) {
Dog dog = new Dog("小狗有參", 1, "地球");
dog.say();
}
```
輸出:
```
我叫小狗有參,今年1歲了,家住地球,汪汪汪...
```
## 4.3. 多個構造器的使用
先了解一個概念——**過載**(overloading)。過載是指在一個類中,有幾個方法的方法名字相同,而引數不同。返回值型別可以相同也可以不相同。注意:**每個過載的方法的引數列表必須獨一無二**。
> 注意:引數列表的獨一無二是指**引數列表的型別的獨一無二**。
>
> 千萬不要以為`func(String name)`和`func(String address)`這兩個方法的引數列表是不同的。它倆應該這樣看:`func(String)`和`func(String)`,所以**這倆方法是相同的**。
下圖是`String`類中的部分方法的過載情況,可從中體會引數列表的獨一無二:
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9naXRlZS5jb20veGluZ3Jlbmd1YW54dWUvYmxvZy1pbWFnZXMvcmF3L21hc3Rlci9pbWdzLzIwMjAwNzI4MTMzNDI4LnBuZw?x-oss-process=image/format,png)
為什麼要求引數列表必須獨一無二呢?
這就得介紹另一個概念——方法的簽名。方法的簽名是指要完整地描述一個方法,需要指出**方法名**和**引數型別**。注意:方法的返回型別不是簽名的一部分。
所以在Java中,方法名和引數列表能確定一個方法。不存在方法名和引數列表相同,而返回型別不同的方法們。
而過載要求的是方法名相同,引數列表不同。從方法的簽名角度來看,過載方法之間本就是不同的方法。
由於過載的存在,我們可以在一個類中編寫多個引數列表不同的構造器,使該類具有多種建立物件的形式。比如:
```java
public class Dog {
private String name;
private int age;
private String address;
public Dog() {//無參構造器,有其他構造器存在,系統不會預設提供
}
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
public Dog(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
public void say() {
System.out.println("我叫" + name + ",今年" + age + "歲了,家住" + address + ",汪汪汪...");
}
//getters 和 setters
}
```
寫了三種構造器,便有三種建立物件的方式:
```java
Dog dog = new Dog("小狗", 1, "太陽系");
Dog dog1 = new Dog("小狗1", 2);
Dog dog2 = new Dog();
```
編譯器會根據我們提供的引數匹配到合適的構造器。
注意:無參構造器只有在我們沒有編寫任何構造器時,系統才會預設提供。所以當一個類中有其他構造器時,如果我們需要無參構造器,那麼必須手動編寫出來,系統不會預設提供。
## 4.4. 構造器之間的關係
在類中,一個構造器可以呼叫另一個構造器。如下例:
```java
/**
* @author Xing Xiaoguan (xingrenguanxue)
*/
public class Dog {
private String name;
private int age;
private String address;
public Dog(String name, int age) {
this(name, age, "銀河系");//呼叫另一個構造器
System.out.println("兩個引數的構造器");
}
public Dog(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
System.out.println("三個引數的構造器");
}
public void say() {
System.out.println("我叫" + name + ",今年" + age + "歲了,家住" + address + ",汪汪汪...");
}
//getters and setters...
}
```
使用`Dog(String, int)`建立物件:
```java
public class Test {
public static void main(String[] args) {
Dog dog = new Dog("小狗有參", 1);
dog.say();
}
}
```
輸出:
```java
三個引數的構造器
兩個引數的構造器
我叫小狗有參,今年1歲了,家住銀河系,汪汪汪...
```
我們在構造器的**第一行語句**中使用`this(...)`來呼叫另一個構造器。注意:一定要是第一行語句。
# 5. 物件屬性的初始化
所謂初始化,就是我們在建立物件同時設定物件的屬性值。
## 5.1. 預設的屬性值
當我們建立物件時如果不顯式地設定屬性值,物件的屬性值會被初始化為預設值。
數值的預設值為`0`,布林值的預設值為`false`,物件引用的預設值為`null`。
如下例中的`Dog`類的屬性值:
```java
public class Dog {
private String name;
private int age;
private String address;
public void say() {
System.out.println("我叫" + name + ",今年" + age + "歲了,家住" + address + ",汪汪汪...");
}
}
```
建立物件:
```java
public static void main(String[] args) {
Dog dog = new Dog();
dog.say();
}
```
輸出:
```java
我叫null,今年0歲了,家住null,汪汪汪...
```
## 5.2. 直接設定屬性值
我們可以在類中直接設定類的屬性值,這樣一來,根據該類建立的物件的屬性值就確定了。
如下例中`Dog`類的屬性值:
```java
public class Dog {
private String name = "小黑";
private int age = 2;
private String address = "太陽系";
public void say() {
System.out.println("我叫" + name + ",今年" + age + "歲了,家住" + address + ",汪汪汪...");
}
}
```
這時再建立物件:
```java
public static void main(String[] args) {
Dog dog = new Dog();
dog.say();
}
```
輸出:
```
我叫小黑,今年2歲了,家住太陽系,汪汪汪...
```
## 5.3. 使用構造器初始化
(一) 當構造器中沒有顯式地設定物件的屬性值時,這些屬性值會被初始化為預設值。如下面的無參構造器:
```java
public Dog() {
}
```
建立物件:
```java
public static void main(String[] args) {
Dog dog = new Dog();
dog.say();
}
```
輸出:
```java
我叫null,今年0歲了,家住null,汪汪汪...
```
(二)可以在無參構造器的方法體中初始化屬性值:
```java
public Dog() {
name = "小黑";
age = 2;
address = "宇宙";
}
```
建立物件:
```java
public static void main(String[] args) {
Dog dog = new Dog();
dog.say();
}
```
輸出:
```
我叫小黑,今年2歲了,家住宇宙,汪汪汪...
```
(三)也可以使用有參構造器,物件會按照我們傳入的變數初始化屬性值:
```java
public Dog(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
```
建立物件:
```java
public static void main(String[] args) {
Dog dog = new Dog("小黑", 3, "南極");
dog.say();
}
```
輸出:
```java
我叫小黑,今年3歲了,家住南極,汪汪汪...
```
>如有錯誤,還請