多态(polymorphism)是面向对象三大特征之一。同一行为,通过不同的子类,可以体现出来的不同的形态。
多态指的是同一个方法调用,由于对象不同可能会有不同的行为。现实生活中,同一个方法,具体实现会完全不同。 比如:同样是调用人的“休息”方法,张三是睡觉,李四是旅游,王五是听音乐; 同样是调用人“吃饭”的方法,中国人用筷子吃饭,英国人用刀叉吃饭,印度人用手吃饭。
编译器类型指的是‘=’左边的类型,运行期类型指的是‘=’右边的类型。当有继承关系时,可能发生编译期类型和运行期类型不同的情况,即编译期类型是父类类型,运行期类型是子类类型。即:父类引用指向子类对象
多态的要点:
- 多态是方法的多态,不是属性的多态(多态与属性无关)。
- 多态的存在要有3个必要条件:继承,方法重写,父类引用指向子类对象。
- 父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。
编写代码,为多态做准备:
public class Programmer {
public String name ="proName";//姓名
//1.子类继承的
public void writeCode(){
System.out.println("writing code............");
}
//2.子类重写的
public void eat(){
System.out.println("eating with mouse.............");
}
}
public class Chinese extends Programmer{
public String name = "ChinName";
//1.从父类继承一个方法 writeCode()
//2.重写父类的一个方法 eat
public void eat() {
System.out.println(" Chinese eat rice with chopsticks.....");
}
//3.子类特有的方法
public void playShadowBoxing(){
System.out.println("Chinese play showdowBoxing every day......");
}
}
public class English extends Programmer {
//1.从父类继承一个方法 writeCode()
//2.重写父类的一个方法 eat
public void eat() {
System.out.println("English eating meat with knife.....");
}
//3.子类特有的方法
public void raceHorse(){
System.out.println("English like racing horse......");
}
}
public class Italian extends Programmer {
//1.从父类继承一个方法 writeCode()
//2.重写父类的一个方法 eat
public void eat() {
System.out.println("Italian eating pizza with hand......");
}
//3.子类特有的方法
public void playFootBall(){
System.out.println("Italian like play football.....");
}
}
实现eat()多态
public class Test {
// public static void showEat(Chinese ch){
// ch.eat();
// }
// public static void showEat(Italian it){
// it.eat();
// }
// public static void showEat(English en){
// en.eat();
// }
public static void showEat(Programmer pro){
pro.eat();
}
public static void main(String[] args) {
//Chinese ch = new Chinese();
Programmer ch = new Chinese();
showEat(ch);
English en = new English();
showEat(en);
Italian it = new Italian();
showEat(it);
Programmer pro = new Programmer();
showEat(pro);
}
}
public class Test2 {
public static void main(String[] args) {
Chinese ch = new Chinese();
ch.writeCode();//从父类继承的
ch.eat();//重写父类的
ch.playShadowBoxing();//子类特有的方法
Programmer ch2 = new Chinese();
ch2.writeCode(); //非多态
ch2.eat(); // 多态 调用的是子类重写的方法
//ch2.playShadowBoxing(); // 无法调用子类特有的方法
}
}
使用父类做方法的形参,是多态使用最多的场合。即使增加了新的子类,方法也无需改变,符合开闭原则。
父类引用做方法的形参,实参可以是任意的子类对象,可以通过不同的子类对象实现不同的行为方式。另外即使增加了新的子类,方法也无需改变,提高了扩展性,符合开闭原则。
多态之向上转型
将子类对象赋给父类引用,称为向上转型(upcasting),自动进行类型转换。
向上转型可以调用的子类继承的方法,但不能调用子类特有的方法。需要特别理解的是如果子类重写了父类的方法,向上转型后通过父类引用调用的却是真实子类重写的方法,
向上转型-代码示例:
public class TestPoly {
public static void main(String[] args) {
//基本数据类型的自动转换
int n = 10;
System.out.println( n );
double d = n; // 左>右 自动转换
System.out.println(d);
//引用数据类型的自动转换
Programmer programmer = new Chinese(); // 自动转换 向上转型 左>右
programmer.writeCode();
programmer.eat();
//programmer.playShadowBoxing();
}
}
如何理解向上转型?
- 招聘程序员,来个英国籍程序员,满足要求,不需要特别声明
- 不管是哪个国籍的,写到代码都是Java代码
- 中午休息了,大家都去食堂开始吃饭,原形毕露
- 老板随便找一个程序员,说一起赛马吧,不可以;因为对方可能是中国或意大利程序员
多态之向下转型
将父类的引用变量转换为子类类型,称为向下转型(downcasting)。向下转型后就可以调用子类特有的方法了。
- 需要进行强制转换Chinese ch = (Chinese)pro;
- 强制转换不是做手术,必须转换成真实子类型,否则ClassCastException;
- 向下转型之前肯定发生了向上转型
- 为了避免ClassCastException,向下转型之前使用instanceof先判断一下
pro instanceof Italian 对象 instanceof 类或者接口
使用instancof的前提:左边的对象和右边的类型在继承树上有上下级关系
向下转型-代码示例:
public class TestPoly2 {
public static void main(String[] args) {
//基本数据类型的强制转换
double d = 3.14;
System.out.println(d);
int n = (int)d; // 左 < 右 做手术
System.out.println(n);
//引用数据类型的强制转换
Programmer programmer = new Chinese();
programmer.eat();//多态
//programmer.playShadowBoxing();
// Chinese ch = (Chinese) programmer; // 左<右 不做手术,必须转换成原来//的真实子类型
// ch.playShadowBoxing();//
// English en = (English)programmer;
// en.raceHorse();
if(programmer instanceof Chinese){
Chinese ch = (Chinese) programmer;
ch.playShadowBoxing();
}else if(programmer instanceof English){
English en = (English) programmer;
en.raceHorse();
}else{
Italian it = (Italian) programmer;
it.playFootBall();
}
//java.lang.ClassCastException:
// com.bjsxt.poly0.Chinese cannot be cast to com.bjsxt.poly0.English
System.out.println(programmer instanceof Chinese); //false
System.out.println(programmer instanceof English); //true
System.out.println(programmer instanceof Programmer);//true
System.out.println(programmer instanceof Object);//true
//System.out.println(programmer instanceof String);
}
}
需要注意的点:多态之和方法有关,和属性无关。入下面示例所示。
多态和属性无关-代码示例:
public class TestPoly3 {
public static void main(String[] args) {
Chinese chinese = new Chinese();
System.out.println(chinese.name);
Programmer programmer = new Programmer();
System.out.println(programmer.name);
Programmer programmer2 = new Chinese();
System.out.println(programmer2.name); //ChinName proName
programmer2.eat();
}
}
多态实现简单工厂模式-返回值是父类类型
不仅可以使用父类做方法的形参,还可以使用父类做方法的返回值类型,真实返回的对象可以是该类的任意一个子类对象。
代码示例:
public class TestPoly4 {
public static void main(String[] args) {
//自己培养了一个程序员
//Programmer pro = new Chinese();
Programmer pro = SxtSchool.getProgrammer("en");
//让程序员干活
pro.writeCode();
}
}
class SxtSchool{
public static Programmer getProgrammer(String type){
Programmer pro = null;
if("ch".equals(type)){
pro = new Chinese();
}else if("en".equals(type)){
pro = new English();
}else{
pro = new Italian();
}
return pro;
}
}
以上代码其实是简单工厂模式的实现,它是解决大量对象创建问题的一个解决方案。将创建和使用分开,工厂负责创建,使用者直接调用即可。简单工厂模式的基本要求是
- 定义一个static方法,通过类名直接调用
- 返回值类型是父类类型,返回的可以是其任意子类类型
- 传入一个字符串类型的参数,工厂根据参数创建对应的子类产品