1. 程式人生 > >java第六章動手動腦

java第六章動手動腦

部分 混亂 class 運行錯誤 個數 jvm 無法 構造 exce

構造函數(constructor)是一種特殊的方法 。主要用來在創建對象時初始化對象, 即為對象成員變量賦初始值,總與new運算符一起使用在創建對象的語句中 。特別的一個類可以有多個構造函數 ,可根據其參數個數的不同或參數類型的不同來區分它們 即構造函數的重載。構造函數的功能主要用於在類的對象創建時定義初始化的狀態。


構造一個對象,先調用其構造方法,來初始化其成員函數和成員變量。
子類擁有父的成員變量和成員方法,如果不調用,則從父類繼承而來的成員變量和成員方法得不到正確的初始化。
不能反過來調用也是這個原因,因為父類根本不知道子類有神魔變量而且這樣一來子類也得不到初始化的父類變量,導致程序運行出錯!
1.源代碼:

public class ExplorationJDKSource {

public static void main(String[] args) {

System.out.println(new A());

}

}

class A{}

2.結果截圖:

3.結果分析:

前面示例中,main方法實際上調用的是: public void println(Object x),這一方法內部調用了String類的valueOf方法。 valueOf方法內部又調用Object.toString方法: public String toString()

{ return getClass().getName() +"@" + Integer.toHexString(hashCode()); }

hashCode方法是本地方法,由JVM設計者實現: public native int hashCode();

3.下列語句哪一個將引起編譯錯誤?為什麽?哪一個會引起運行時錯誤?為什麽?

m=d;
d=m;
d=(Dog)m;
d=c;
c=(Cat)m;
先進行自我判斷,得出結論後,運行TestCast.java實例代碼,看看你的判斷是否正確

編譯錯誤

d=m;d=c;

不正確 子類對象可以直接賦給基類變量。
基類對象要賦給子類對象變量,必須執行類型轉換,
其語法是:子類對象變量=(子類名)基類對象名;

運行錯誤c=(Cat)m

不正確 轉換混亂。如果類型轉換失敗Java會拋出以下這種異常:ClassCastException

4.下邊的程序運行結果是什麽? 2. 你如何解釋會得到這樣的輸出? 3. 計算機是不會出錯的,之所以得 到這樣的運行結果也是有原因的, 那麽從這些運行結果中,你能總 結出Java的哪些語法特性?

public class ParentChildTest {
public static void main(String[] args) {
Parent parent=new Parent();
parent.printValue();
Child child=new Child();
child.printValue();

parent=child;
parent.printValue();

parent.myValue++;
parent.printValue();

((Child)parent).myValue++;
parent.printValue();

}
}

class Parent{
public int myValue=100;
public void printValue() {
System.out.println("Parent.printValue(),myValue="+myValue);
}
}
class Child extends Parent{
public int myValue=200;
public void printValue() {
System.out.println("Child.printValue(),myValue="+myValue);
}
}
1)

Parent.printValue(),myValue=100

Child.printValue(),myValue=200

Child.printValue(),myValue=200

Child.printValue(),myValue=200

3)

當子類與父類擁有一樣的方法,並且讓一個父類變量引用一個子類對象時,到底調用哪個方法,由對象自己的“真實”類型所決定,這就是說:對象是子類型的,它就調用子類型的方法,是父類型的,它就調用父類型的方法。如果子類與父類有相同的字段,則子類中的字段會代替或隱藏父類的字段,子類方法中訪問的是子類中的字段(而不是父類中的字段)。如果子類方法確實想訪問父類中被隱藏的同名字段,可以用super關鍵字來訪問它。
如果子類被當作父類使用,則通過子類訪問的字段是父類的!

5.為什麽子類的構造方法在運行之前,必須調用父類的構造方法?能不能反過來?為什麽不能反過來?

原因:構造函數用來在創建對象時初始化對象,與new運算符一起使用在創建對象的語句時。子類擁有父類的成員變量和成員方法,如果不調用,則從父類繼承而來的成員變量和成員方法得不到正確的初始化。不可以反過來調用,父類不知道子類有什麽變量,導致子類得不到正確的初始化,程序出錯。

6. 多態含義和用途

讓我們看一個開發場景:

某動物園有一飼養員小李,

每天需要給他所負責飼養的獅子、猴子和鴿子餵食。

請用一個程序來模擬他餵食的過程。

①三種動物對應三個類,每個類定義一個eat()方法,表示吃飼養員給它們的食物。

再設計一個Feeder類代表飼養員,其name字段保存飼養員名字,三個方法分別代表餵養三種不同的動物,其參數分別引用三種動物對象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class Zoo
{
public static void main(String args[])
{
Feeder f = new Feeder("小李");
// 飼養員小李餵養一只獅子
f.feedLion(new Lion());
// 飼養員小李餵養十只猴子
for (int i = 0; i < 10; i++)
{
f.feedMonkey(new Monkey());
}
// 飼養員小李餵養5只鴿子
for (int i = 0; i < 5; i++)
{
f.feedPigeon(new Pigeon());
}
}
}
class Feeder
{
public String name;
public Feeder(String name)
{
this.name = name;
}
public void feedLion(Lion l)
{
l.eat();
}
public void feedPigeon(Pigeon p)
{
p.eat();
}
public void feedMonkey(Monkey m)
{
m.eat();
}
}
class Lion
{
public void eat()
{
System.out.println("我不吃肉誰敢吃肉!");
}
}
class Monkey
{
public void eat()
{
System.out.println("我什麽都吃,尤其喜歡香蕉。");
}
}
class Pigeon
{
public void eat()
{
System.out.println("我要減肥,所以每天只吃一點大米。");
}
}
這種編程方式有什麽不合理的地方?

每次餵食都要創建一次類。重復步驟多。

①引入繼承

定義一個抽象基類Animal,其中定義一個抽象方法eat(),三個子類實現這個抽象方法。

Feeder類的三個餵養方法現在可以合並為一個feedAnimal()方法,註意它接收一個類型為Animal參數,而不是三個具體的動物類型。

依據多態特性,此方法將可以接收任何一個派生自Animal類的子類對象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class Zoo
{
public static void main(String args[])
{
Feeder f = new Feeder("小李");
//飼養員小李餵養一只獅子
f.feedAnimal(new Lion());
//飼養員小李餵養十只猴子
for (int i = 0; i < 10; i++)
{
f.feedAnimal(new Monkey());
}
//飼養員小李餵養5只鴿子
for (int i = 0; i < 5; i++)
{
f.feedAnimal(new Pigeon());
}
}
}
class Feeder
{
public String name;
Feeder(String name)
{
this.name = name;
}
public void feedAnimal(Animal an)
{
an.eat();
}
}
abstract class Animal
{
public abstract void eat();
}
class Lion extends Animal
{
public void eat()
{
System.out.println("我不吃肉誰敢吃肉!");
}
}
class Monkey extends Animal
{
public void eat()
{
System.out.println("我什麽都吃,尤其喜歡香蕉。");
}
}
class Pigeon extends Animal
{
public void eat()
{
System.out.println("我要減肥,所以每天只吃一點大米。");
}
}
①進一步優化餵養一群動物

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package zoo3;
public class Zoo
public static void main(String args[]) {
Feeder f = new Feeder("小李");
Animal[] ans = new Animal[16];
//飼養員小李餵養一只獅子
ans[0] = new Lion();
//飼養員小李餵養十只猴子
for (int i = 0; i < 10; i++) {
ans[1 + i] = new Monkey();
}
//飼養員小李餵養5只鴿子
for (int i = 0; i < 5; i++) {
ans[11 + i] = new Pigeon();
}
f.feedAnimals(ans);
}
}
class Feeder {
public String name;
Feeder(String name) {
this.name = name;
}
public void feedAnimals(Animal[] ans) {
for (Animal an : ans) {
an.eat();
}
}
}
abstract class Animal {
public abstract void eat();
}
class Lion extends Animal {
public void eat() {
System.out.println("我不吃肉誰敢吃肉!");
}
}
class Monkey extends Animal {
public void eat() {
System.out.println("我什麽都吃,尤其喜歡香蕉。");
}
}
class Pigeon extends Animal {
public void eat() {
System.out.println("我要減肥,所以每天只吃一點大米。");
}
}
④第二次重構之後,Feeder類的feedAnimals()方法接收的是一個Animal數組,這有一個限制,就是只能創建固定個數的數組,無法動態地增減動物個數。

想想以下場景:

(1)動物園新進了一些動物

(2)某動物生病不幸死亡

(3)……

我們的代碼能否應付以上的場景?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import java.util.Vector;
public class Zoo {
public static void main(String args[]) {
Feeder f = new Feeder("小李");
Vector<Animal> ans = new Vector<Animal>();
//飼養員小李餵養一只獅子
ans.add(new Lion());
//飼養員小李餵養十只猴子
for (int i = 0; i < 10; i++) {
ans.add(new Monkey());
}
//飼養員小李餵養5只鴿子
for (int i = 0; i < 5; i++) {
ans.add(new Pigeon());
}
f.feedAnimals(ans);
}
}
class Feeder {
public String name;
Feeder(String name) {
this.name = name;
}
//Vector<T>是JDK中提供的一個對象集合,可以隨時向其中加入或移除對象
public void feedAnimals(Vector<Animal> ans) {
for (Animal an : ans) {
an.eat();
}
}
}
abstract class Animal {
public abstract void eat();
}
class Lion extends Animal {
public void eat() {
System.out.println("我不吃肉誰敢吃肉!");
}
}
class Monkey extends Animal {
public void eat() {
System.out.println("我什麽都吃,尤其喜歡香蕉。");
}
}
class Pigeon extends Animal {
public void eat() {
System.out.println("我要減肥,所以每天只吃一點大米。");
}
}
總結:

多態編程有兩種主要形式:

(1)繼承多態:示例程序使用的方法

(2)接口多態:使用接口代替抽象基類。

使用多態最大的好處是:

當你要修改程序並擴充系統時,你需要修改的地方較少,對其它部分代碼的影響較小!千萬不要小看這兩個“較”字!程序規模越大,其優勢就越突出。

java第六章動手動腦