Java基礎之:OOP——多型
多型(polymorphic)即多種形態,是程式基于封裝和繼承之后的另外一種應用,
首先我們先看一個案例,了解為什么要使用多型,
實作一個應用 : 1.小范既是兒子 也是 父親 (多種形態),2.兒子用錢買糖 , 父親賣報紙給商家賺錢
package polymorphic_ClassTest;
?
public class PolyTest {
?
public static void main(String[] args) {
Father father = new Father("父親(小范)",40);
Son son = new Son("兒子(小范)",20);
Candy candy = new Candy("買糖果");
Newspaper newspaper = new Newspaper("賣報紙");
tarding(son,candy); //兒子買糖果
tarding(father,newspaper); //父親賣報紙
//通過多型引入,我們也可以體現 兒子賣報紙 , 父親買糖果
tarding(father,candy);
tarding(son, newspaper);
}
//試想如果 父親也要買糖果,那么就又需要重寫一個tarding方法.....
//會有很多的組合方式,為了不重寫tarding方法,引入多型的概念
//使用以前的封裝+繼承方法實作:
public static void tarding(Son son,Candy candy) {
System.out.println(son.getName() + "買" + candy.getName());
}
public static void tarding(Father father,Newspaper newspaper) {
System.out.println(father.getName() + "賣" + newspaper.getName());
}
//使用多型來實作:
public static void tarding(Person person,Goods goods) {
//實作原理: Son和Father繼承于Person類,Candy和Newspaper繼承于Goods類
System.out.println(person.getName() + " 交易 " + goods.getName());
}
?
}
?
class Person{
private String name;
?
public Person(String name) {
super();
this.name = name;
}
?
public Person() {
super();
}
?
public String getName() {
return name;
}
?
public void setName(String name) {
this.name = name;
}
?
}
?
class Son extends Person{
private int age;
public Son(String name, int age) {
super(name);
this.age = age;
}
}
?
class Father extends Person{
private int age;
public Father(String name, int age) {
super(name);
this.age = age;
}
}
?
class Goods{ //商品
private String name;
?
public Goods(String name) {
super();
this.name = name;
}
?
public Goods() {
super();
}
?
public String getName() {
return name;
}
?
public void setName(String name) {
this.name = name;
}
}
?
class Candy extends Goods{
public Candy(String name) {
super(name);
}
}
?
class Newspaper extends Goods{
public Newspaper(String name) {
super(name);
}
}
運行結果

多型的具體體現
重寫與多載
public class PolyOverLoad {
?
public static void main(String[] args) {
//方法多載體現多型
T t = new T();
t.say(100);
t.say("tom");
}
?
}
?
class T {
public void say(String name) {
System.out.println("hi " + name);
}
public void say(int num) {
System.out.println("hello" + num);
}
}
?
//=======================================================
?
public class PolyOverride {
?
public static void main(String[] args) {
?
AA a = new AA();
a.hi("jack");
BB b = new BB();
b.hi("tom");
}
?
}
?
class AA {
public void hi(String name) {
System.out.println("AA " + name);
}
}
?
class BB extends AA {
@Override
public void hi(String name) { //子類hi 重寫 父類的 hi
System.out.println("BB " + name);
}
}
物件的多型(編譯型別與運行型別)
package polymorphic;
?
public class PolyTest {
?
public static void main(String[] args) {
/*
* 語法:父類名 物件 = new 子類構造器;(父類參考指向子類物件)
* 此時animal實際存在兩種型別:1.編譯型別 ;2.運行型別
* 編譯型別:編譯器識別時的型別,即等號左邊的型別,這里animal的編譯型別就是Animal
* 在程式員編譯時,只能訪問編譯型別有的 方法和屬性
* 對于物件的編譯型別而言,是不變的,
* 運行型別:JVM運行時的型別,即等號右邊的型別,這里animal的運行型別就是Dog
* 對于物件的運行型別而言,是可變的,
*/
//向上轉型 語法:父型別別 父類物件 = new 子型別別();
Animal animal = new Dog();
animal.eat();
// animal.run(); //報錯 :The method run() is undefined for the type Animal
animal = new Cat();//改變了animal的運行型別,但編譯型別不變
animal.eat();
animal.show(); //1.首先尋找在Cat中的show 2.若沒有則向上尋找父類Animal的show
//向下轉型 語法: 子型別別 子類物件 = (子型別別)父類物件
Cat cat = (Cat)animal;//這里是創建了一個Cat參考,讓cat 指向 animal指向的那個堆地址空間
// Dog dog = (dog)animal;//要想這樣向下強行轉換型別,必須滿足 animal堆空間中的型別就是cat
cat.drink();
}
?
}
?
class Animal{
public void eat() {
System.out.println("eat......");
}
public void show() {
System.out.println("show..........");
}
}
?
class Dog extends Animal{
@Override
public void eat() {
System.out.println("Dog eat......");
}
public void run() {
System.out.println("run........");
}
}
?
class Cat extends Animal{
@Override
public void eat() {
System.out.println("Cat eat......");
}
public void drink() {
System.out.println("drink........");
}
// public void show() {
// System.out.println("Cat show..........");
// }
}

結合實際案例理解編譯型別與運行型別
- 對于編譯型別和運行型別,通過這樣一個現實的例子來理解,
- 大家都知道披著羊皮的狼,那可能也會有披著羊皮的人,
- 那么這里的羊皮就是編譯型別,羊皮是始終不變的,不管是誰披上它,它都是羊皮,
- 而我們可以把編譯器看成一個很"膚淺"的家伙,它只會看到表面的東西,所以如果在編譯型別中沒有的方法,不可以通過物件進行呼叫(即使在運行型別中確實存在此方法),
- 這里的狼和人就是運行型別,運行型別是可變的,(羊皮可能被任何東西給披上)
- 相對于編譯器而言JVM就顯得有"內涵"一些了,運行型別就是在JVM運行程式時,物件實際的型別,所以運行型別可以呼叫在運行型別中有的方法,
- 就好比,披著羊皮的狼,你以為它是吃草的,編譯器也認為它是吃草的,但它實際上是吃肉的,JVM在運行時也認為它是吃肉的,
案例說明(向上轉型與向下轉型)
向上轉型 語法:父型別別 父類物件 = new 子型別別();
對于向上轉型而言,就是將父類參考指向子類物件,
向上轉型可以通過改變運行型別的方式,通過一個父類參考訪問多個子類物件,
例如:
Animal animal = new Dog();
animal = new Cat();
向下轉型 語法: 子型別別 子類物件 = (子型別別)父類物件;
對于向下轉型而言,就是將父類物件強制轉換為子類物件,
所以要做到向下轉型,前提條件就是父類物件原本的運行型別就是子型別別,
例如:
Animal animal = new Dog();
Dog dog = (dog)animal;
但要注意的是,向下轉型是將一個子類參考Dog指向了原來在堆空間創建的那個Dog物件,
而animal同樣指向堆空間中的Dog物件,所以向下轉型之后 animal (父類參考)本身不受影響,
屬性多型
對于型別的屬性而言,沒有編譯型別與運行型別的說法,
即屬性只認編譯型別,通過多型宣告后,訪問屬性時,也只會回傳編譯型別中的屬性對應的值,
public class PolyProperties {
?
public static void main(String[] args) {
Base base = new Base();
System.out.println(base.n); // 200
Base base2 = new Sub();
System.out.println(base2.n); // 屬性沒有重寫之說!屬性的值看編譯型別
}
}
?
class Base {
public int n = 200;
}
?
class Sub extends Base {
public int n = 300;
}
instanceOf關鍵字
instanceOf關鍵字用于比較 物件的型別 是否是指定型別或其子類
public class InstanceOfTest {
public static void main(String[] args) {
AA bb = new BB();
//instanceOf 比較運算子,用于判斷某個物件的運行型別是否為XX型別或XX型別的子型別
System.out.println(bb instanceof BB); // T
System.out.println(bb instanceof AA); // T
System.out.println(bb instanceof Object); // T
Object obj = new Object();
System.out.println(obj instanceof AA);// F
}
}
?
?
class AA{
}
?
class BB extends AA{
}
Java的動態系結機制
-
當呼叫物件方法的時候,該方法會和該物件的記憶體地址/運行型別系結,
-
當呼叫物件屬性時,沒有動態系結機制,哪里宣告,哪里使用,
class A {
public int i = 10;
public int sum() {
return getI() + 10;
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}
?
class B extends A {
public int i = 20;
// public int sum() {//注銷?
// return i + 20;
//}
public int getI() {
return i;
}
// public int sum1() {//注銷?
// return i + 10;
// }
}
?
public class Test{
public static void main(String args[]){
A a = new B(); //不注銷 注銷
System.out.println(a.sum()); //40 =》 30
System.out.println(a.sum1()); //30 =》 20
//這里要注意,getI()方法是動態系結在B物件上的,所以在呼叫A類的sum()方法時,getI()仍然會回傳B類中的I的值,
}
}
多型應用案例
應用實體:現有一個繼承結構如下:要求創建五個年齡不等的Person1、Student [2]和Teacher[2]物件,
呼叫子類特有的方法,比如Teacher 有一個 teach , Student 有一個 study怎么呼叫
提示 : [實作在多型陣列呼叫各個物件的方法]遍歷+instanceof + 向下轉型
package polymorphic_PolyArrays;
public class PolyArrays {
?
public static void main(String[] args) {
?
Person[] persons = {new Person("jack", 10), new Student("tom",20, 78), new Student("king",21, 68)
, new Teacher("老王", 50, 10000), new Teacher("老李", 45, 20000)};
Traverse(persons);
}
public static void Traverse(Person[] person) {
for (int i = 0; i < person.length; i++) {
if(person[i] instanceof Student) {
// ((Student)person[i]).study(); //這種方式更好
Student stu = (Student)person[i];
stu.study();
}else if(person[i] instanceof Teacher) {
// ((Teacher)person[i]).teach(); //這種方式更好
Teacher tea = (Teacher)person[i];
tea.teach();
}else {
System.out.println(person[i].say());
}
}
}
}
?
class Person {
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String say() {
return "資訊 name= " + name + " age= " + age;
}
}
?
class Student extends Person {
private double score;
?
public double getScore() {
return score;
}
?
public void setScore(double score) {
this.score = score;
}
?
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
public void study() {
System.out.println("學生 " + getName() + " is studying java...");
}
}
?
class Teacher extends Person {
private double salary;
?
public Teacher(String name, int age, double salary) {
super(name, age);
this.salary = salary;
}
?
public double getSalary() {
return salary;
}
?
public void setSalary(double salary) {
this.salary = salary;
}
public void teach() {
System.out.println("老師 " + getName() + " is teaching java ");
}
}
