拥有域名后怎么建设网站,建设网站需要购买虚拟主机吗,百度联系电话多少,珠海网站建设 金蝶目录
前言 :
一、为什么需要多态 : 1.白璧微瑕 : 2.举栗#xff08;请甘雨,刻晴,钟离吃饭#xff09;: 3.代码 : 4.问题 :
二、什么是多态 : 1.定义 : 2.多态的实现步骤#xff08;重要#xff09; :
三、多态的使用 : 1.多态中成员方法的使用#xff08;重要…目录
前言 :
一、为什么需要多态 : 1.白璧微瑕 : 2.举栗请甘雨,刻晴,钟离吃饭: 3.代码 : 4.问题 :
二、什么是多态 : 1.定义 : 2.多态的实现步骤重要 :
三、多态的使用 : 1.多态中成员方法的使用重要 : ①使用规则 : ②代码演示 : ③利用多态实现“旅行者请璃月人吃饭” : 2.多态中成员变量的使用 : ①使用规则 : ②代码演示 :
四、多态的优点和缺点 : 1.益处 : 2.弊端 : 重点
五、类型转换 : Δ前言 : 1.向上转型自动: ①含义 : ②语法格式 : ③代码演示 : 2.向下转型强制: ①含义 : ②语法格式 : ③代码演示 : 3.注意事项 : 4.instanceof关键字重要 ①概述 : ②用法 : ③代码演示 :
六、Java的动态绑定机制重要:
七、多态的应用 :
八、抽象类抽象方法abstract关键字详解 :
九、final关键字详解 :
十、static关键字类变量和类方法详解 :
十一、接口详解 :
十二、Debug详解 :
十三、面向对象总结 : 前言 : Higuys.这篇博文是Java面向对象三大特性篇的第三篇———多态篇。之前我们就一直强调面向对象三大特性中封装是继承的前提继承是多态的前提。多态也是实际开发中运用最多最广泛的一个特性因此多态的重要性不言而喻。本篇博文内容包括但不限于多态的介绍多态的使用详解多态中的类型转化机制动态绑定机制详解抽象类详解final关键字、static关键字详解接口详解等等感谢阅读 这篇博文算是三大特性篇的最后一篇之后up会将三大特性对应的三篇博客合并起来并在此基础之上写一篇总结Java面向对象的十万字博文力争以最通俗易懂的方式让初学者快速入门并理解面向对象的灵魂。 还有是CSDN貌似不欢迎长文章因此up计划将这篇博文中部分内容比较长的知识点单独再出篇博文然后以插入链接的方式呈现给大家。谢谢理解感谢阅读
一、为什么需要多态 : 1.白璧微瑕 : 继承的使用给我们带来了极大的便利不但提高了代码的可维护性而且提高了代码的复用性使我们免于敲写过多繁冗重复的代码。然而甘瓜苦蒂天下物无全美我们承认继承已经是个很的特性了。但在某些情况下继承也会显出它的颓势 2.举栗请甘雨,刻晴,钟离吃饭: 这不马上海灯节就要到了。 旅行者答应要请刻晴钟离和甘雨一起去新月轩吃晚饭。为了让大家开心旅行者提前了解了三个人喜爱的食物 : 刻晴喜欢吃金丝虾球钟离喜欢吃豆腐甘雨则喜欢吃清心。 现在让你用Java来描述这件事情你怎么做 思路 : ①首先刻晴钟离和甘雨是三个不同的对象因此我们需要分别定义三个类来模拟和描述刻晴钟离甘雨。同理金丝虾球杏仁豆腐和清心也是三个不同的对象因此也需要分别定义三个类来模拟这三种食物。而三类人三类食物均有共同属性且刻晴钟离和甘雨均有各自特有的行为因此考虑使用封装和继承特性来实现。 ②假设刻晴钟离和甘雨都是璃月人那么我们就可以定义一个父类来表示璃月人然后让表示刻晴钟离和甘雨的子类去继承表示璃月人的父类。 同理 : 假设金丝虾球豆腐和清心都属于料理那么我们就可以定义一个父类来表示料理然后让表示金丝虾球豆腐和清心的子类去继承表示料理的父类。 ③根据假设先定义一个Liyue_people类然后再分别定义Keqing类Zhongli类和Ganyu类并让它们继承Liyue_people类。同时在Keqing类Zhongli类和Ganyu类中定义它们的特有方法。其中 : Keqing类特有方法——天街巡游 : sky_street_cruise() Zhongli类特有方法——天星 : sky_stars() Ganyu类特有方法——降众天华 : descend_to_heaven() ④同样根据假设先定义Cooking类然后再分别定义Shrimp_balls类Bean_curd类和Qingxin类并让它们继承Cooking类。注意 : 每种料理都要有名字营养和味道三个属性。假设新月轩在海灯节期间会稿活动部分菜肴有优惠我们需要在Cooking类中定义一个cooking_info()方法来打印出料理的基本信息菜品名营养风味等。 ⑤定义Traveler类(旅行者类)并在Traveler类中分别定义方法名为my_treat的三个重载方法第一个方法需要传入一个刻晴类对象和一个金丝虾球对象第二个方法需要传入一个钟离类对象和一个豆腐类对象第三个方法则需要传入一个甘雨类对象和一个清心类对象。利用方法重载可以实现请不同对象吃饭的需求。 ⑥最后定义Treat类进行测试。在Treat类先创建好刻晴钟离和甘雨的吃饭对象以及金丝虾球豆腐和清心的料理对象。之后再定义旅行者类对象并调用旅行者类中的my_treat方法()传入对象参数即可成功请客吃饭过一个完美的海灯节。 关系图如下 : 3.代码 : 为了简洁up直接将Keqing类Zhongli类和Ganyu类都写在Liyue_people类的源文件中将Shrimp_balls类Bean_curd类和Qingxin类都写在Cooking类的源文件中。 Liyue_people类Keqing类Zhongli类和Ganyu类代码如下 :
package knowledge.polymorphism.introduction;//父类 : 璃月人类
public class Liyue_people {//成员变量private String name;private int age;private String gender;//构造器public Liyue_people() {}public Liyue_people(String name, int age, String gender) {this.name name;this.age age;this.gender gender;}//settergetter方法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 getGender() {return gender;}public void setGender(String gender) {this.gender gender;}
}//子类 : 刻晴类
class Keqing extends Liyue_people {public Keqing(String name, int age, String gender) {super(name, age, gender);}//刻晴————天街巡游public void sky_street_cruise() {System.out.println(剑光如我斩尽芜杂);}
}
//子类 : 钟离类
class Zhongli extends Liyue_people {public Zhongli(String name, int age, String gender) {super(name, age, gender);}//钟离————天星public void sky_stars() {System.out.println(天动万象);}
}
//子类 : 甘雨类
class Ganyu extends Liyue_people {public Ganyu(String name, int age, String gender) {super(name, age, gender);}//甘雨————降众天华public void descend_to_heaven() {System.out.println(为了岩王帝君);}
} Cooking类Shrimp_balls类Bean_curd类和Qingxin类代码如下 :
package knowledge.polymorphism.introduction;//父类 : 料理类
public class Cooking {//成员变量private String food_name;private String nutrition;private String flavor;//构造器public Cooking() {}public Cooking(String food_name, String nutrition, String flavor) {this.food_name food_name;this.nutrition nutrition;this.flavor flavor;}//settergetter方法public String getFood_name() {return food_name;}public void setFood_name(String food_name) {this.food_name food_name;}public String getNutrition() {return nutrition;}public void setNutrition(String nutrition) {this.nutrition nutrition;}public String getFlavor() {return flavor;}public void setFlavor(String flavor) {this.flavor flavor;}//打印出菜肴信息的方法public void cooking_info() {System.out.print(\t菜品: this.food_name);System.out.print(\t\t营养 this.nutrition);System.out.println(\t\t风味 this.flavor);}
}//子类 : 金丝虾球类
class Shrimp_balls extends Cooking {public Shrimp_balls(String food_name, String nutrition, String flavor) {super(food_name, nutrition, flavor);}
}
//子类 : 豆腐类
class Bean_curd extends Cooking {public Bean_curd(String food_name, String nutrition, String flavor) {super(food_name, nutrition, flavor);}
}
//子类 : 清心类
class Qingxin extends Cooking {public Qingxin(String food_name, String nutrition, String flavor) {super(food_name, nutrition, flavor);}
} Traveler类代码如下 :
package knowledge.polymorphism.introduction;//旅行者类
public class Traveler {//反正是个哑巴要什么属性//打工人行为 : //1.请刻晴吃金丝虾球public void my_treat(Keqing keqing, Shrimp_balls shrimpBalls) {System.out.println(海灯节 请 keqing.getName() 吃 shrimpBalls.getFood_name());}//2.请钟离吃杏仁豆腐public void my_treat(Zhongli zhongli, Bean_curd bean_curd) {System.out.println(海灯节 请 zhongli.getName() 吃 bean_curd.getFood_name());}//3.请甘雨吃清心public void my_treat(Ganyu ganyu, Qingxin qingxin) {System.out.println(海灯节 请 ganyu.getName() 吃 qingxin.getFood_name());}
}Treat类代码如下 :
package knowledge.polymorphism.introduction;public class Treat {public static void main(String[] args) {//1.创建要吃饭的角色对象//创建刻晴对象Keqing keqing new Keqing(刻晴, 18, 女);//创建钟离对象Zhongli zhongli new Zhongli(摩拉克斯, 6000, 男);//创建甘雨对象Ganyu ganyu new Ganyu(王小美, 3000, 女);//2.创建被吃的食物对象//创建金丝虾球对象Shrimp_balls shrimp_balls new Shrimp_balls(金丝虾球, 蛋白质是的六倍, 鲜香味美);//创建杏仁豆腐对象Bean_curd bean_curd new Bean_curd(杏仁豆腐, 富含微生物, 清甜爽口);//创建清心对象Qingxin qingxin new Qingxin(清心, 妹说就是0卡, 苦的一批);//3.创建旅行者对象Traveler traveler new Traveler();//4.打扫干净屋子再请客System.out.println(欢迎来到新月轩);System.out.println(---------------以下是本店的海灯节热门菜肴 : );shrimp_balls.cooking_info();bean_curd.cooking_info();qingxin.cooking_info();System.out.println(\n旅行者:\哎呀我去整挺好不用挑了全都要\\n);traveler.my_treat(keqing, shrimp_balls); // 传入刻晴对象和金丝虾球对象traveler.my_treat(zhongli, bean_curd); // 传入钟离对象和杏仁豆腐对象traveler.my_treat(ganyu, qingxin); // 传入甘雨对象和清心对象}
}Treat类代码如下 : 4.问题 : 不知道大家发现没有旅行者每请一个角色吃饭都得重新定义一个新的重载方法现在才请了三五个角色吃饭可能还觉得没那么麻烦。但是提瓦特大陆上的角色成百上千啊。到时候难道要定义100个方法1000个方法既然都是请客为什么不能在一个方法中实现呢。就拿方才我们举得栗子来打个比方既然刻晴钟离和甘雨都是璃月人为什么我们不直接定义一个方法来请璃月人吃饭这样不就省事儿多了 这时候我们开始认识到继承特性的一些弊端 : 只有继承的程度无法实现诸如“旅行者直接请璃月人吃饭”的需求。那怎么办害铺垫一大堆废话不就为了讲咱的多态么这不就来了! 二、什么是多态 : 1.定义 : 所谓多态其实字面意思就是多种状态没那么复杂。在Java中方法和对象都可以体现出多态。 ①对于方法多态表现在方法重载和方法重写上。 方法重载 : 同一行为形参不同则表现形式不同。 方法重写 : 同一行为调用者不同则表现形式不同。 ②对于对象多态表现在同一对象在不同情况下表现出不同的状态或行为。对象不仅仅是体现多态更重要的应用多态。因此对象的多态就是Java多态的核心。 2.多态的实现步骤重要 : ①有继承(或实现)关系。继承是多态的前提。实现指实现类或接口后面我们会讲到 ②有方法重写。 ③父类引用指向子类对象。 对于第三条要特别补充一些内容。我们先来举个栗子吧。 eg : 假设有Cat类继承了Animal类如下所示 :
public class Animal {public void eat() {}
}class Cat extends Animal {public void eat() {System.out.println(喜欢吃);}
} 那么父类引用指向子类对象即 : Animal animal new Cat(); Δ解释 : 在上面这行代码中等号左面的animal是一个Animal类型的引用变量但是这个Animal类型的引用所指向的对象即堆空间中真正的对象却是一个Cat类型。这就叫做父类引用指向子类对象。有什么用呢别着急下面多态的使用就会正式介绍到。这里我们要说一点别的内容 : 在多态中我们将等号左边的这个引用变量的类型称为编译类型。而将等号右边的——在堆空间中new出来的——真正的对象的类型称为运行类型。其中编译类型决定了引用变量可以调用哪些属性和行为而运行类型则是在程序运行过程中jvm实际使用的类型。 比方说现在我们通过animal对象来调用eat() 方法因为编译类型是Animal类因此在编译时编译器要判断Animal类有没有eat() 方法。诶有eat() 方法那就可以调用。但在实际调用时jvm会优先使用运行类型Cat类中的eat() 方法因此打印结果为“喜欢吃”。
三、多态的使用 : 1.多态中成员方法的使用重要 : ①使用规则 : 编译看左即左边的编译类型有没有这个成员这决定了我们能不能调用目标成员 运行看右即右边的运行类型中的该成员才是运行中实际使用的成员 ②代码演示 : up就以Animal类为父类Cat类为子类TestMethod类为测试类(置于同一包下)。我们给Animal类定义一些动物共同的属性例如species_name(物种名)average_life(平均寿命)living_habit(生活习性)等定义一些动物共同的行为例如eat(吃)sleep(睡)等。 然后我们在Cat类中重写eat()方法和sleep()方法并在TestMethod类中使用多态根据多态中成员方法的使用规则 : 如果我们以Animal类的引用去调用eat()或者sleep()方法实际运行中优先使用的一定是子类Cat类中的eat()和sleep()方法。 Animal类代码如下 :
package knowledge.polymorphism.about_method;public class Animal {//成员变量private String species_name; //物种名private int average_life; //平均寿命private String living_habit; //生活习性//构造器public Animal() {}public Animal(String species_name, int average_life, String living_habit) {this.species_name species_name;this.average_life average_life;this.living_habit living_habit;}//setter, getter方法public String getSpecies_name() {return species_name;}public void setSpecies_name(String species_name) {this.species_name species_name;}public int getAverage_life() {return average_life;}public void setAverage_life(int average_life) {this.average_life average_life;}public String getLiving_habit() {return living_habit;}public void setLiving_habit(String living_habit) {this.living_habit living_habit;}//成员方法public void eat() {System.out.println(这是Animal类的eat()方法需要被重写捏);}public void sleep() {System.out.println(这是Animal类的sleep()方法需要被重写捏);}
}
/*按下insert键后输入会变成改写模式需要再按下一次才能取消。*/ Cat类代码如下 :
package knowledge.polymorphism.about_method;public class Cat extends Animal {//构造器public Cat() {}public Cat(String species_name, int average_life, String living_habit) {super(species_name, average_life, living_habit);}//重写父类的eat()方法和sleep()方法Overridepublic void eat() {System.out.println(喜欢吃捏);}Overridepublic void sleep() {System.out.println(一般蜷缩着睡觉且不喜欢在光线强烈的地方睡);}//定义成员方法用于打印出猫的基本信息public void cat_info() {System.out.println(猫的物种学名: this.getSpecies_name() \t猫的平均寿命 : this.getAverage_life() \t猫的生活习性 : this.getLiving_habit());}
}TestMethod类代码如下 :
package knowledge.polymorphism.about_method;public class TestMethod {public static void main(String[] args) {//父类引用指向子类对象Animal animal new Cat();animal.setSpecies_name(Felinae); //Felinae是猫的拉丁学名animal.setAverage_life(15);animal.setLiving_habit(喜欢独自生活喜欢干净喜欢肉类);System.out.println(简单介绍一下猫这一物种 : );((Cat)animal).cat_info(); //强转类型后面会讲到。System.out.println(------------------------------------------);//调用成员方法animal.eat();animal.sleep();}
}运行结果 : 果然不出我们所料实际运行的eat() 方法和sleep() 方法是Cat类中重写过后的方法。 为了更进一步验证我们的结论现在我们把Cat类中的两个重写方法都给注释掉如下图所示 : 此时若通过animal引用再次调用eat() 方法和 sleep() 方法因为子类重写方法被注释因此会调用父类的eat() 和 sleep() 方法运行结果如下 : 通过演示相信大家对于多态中成员方法的使用已有了一定的了解。但这时候可能有p小将personable小将指风度翩翩的人出来说了 : 你就用了Cat一个子类也好意思说自己是多态我咋看不出来捏 不愧是p小将一针见血的没错前面我们说过——对象的多态才是Java多态的核心同一对象在不同情况下表现出不同的状态或行为称为对象的多态。现在我们使用父类引用仅调用了Cat一个运行类型没有体现多种状态 好滴于是我们在本包下新建一个Dog类Dog类也继承Animal类。此时继承关系如下图 : 我们先讲Cat类中重写的eat() 方法和sleep方法恢复 如下图所示 : 然后在Dog类中对eat() 和sleep() 方法进行重写并且也定义一个dog_info() 方法用于打印出狗的基本信息。Animal类和Cat类代码保持不变。 Dog类代码如下 :
package knowledge.polymorphism.about_method;public class Dog extends Animal {//构造器public Dog() {}public Dog(String species_name, int average_life, String living_habit) {super(species_name, average_life, living_habit);}//重写父类eat() 方法 和 sleep() 方法Overridepublic void eat() {System.out.println(也喜欢吃捏);}Overridepublic void sleep() {System.out.println(想睡就睡活在当下);}//定义成员方法用于打印出狗的基本信息public void dog_info() {System.out.println(狗的物种学名: this.getSpecies_name() \t狗的平均寿命 : this.getAverage_life() \t狗的生活习性 : this.getLiving_habit());}
}在TestMethod类中当指向Cat对象的animal引用变量调用完成员方法后我们改变animal引用的指向使它指向一个Dog类对象并去调用Dog类中重写的方法。 TestMethod类代码如下 :
package knowledge.polymorphism.about_method;public class TestMethod {public static void main(String[] args) {//1.父类引用指向子类对象Animal animal new Cat();animal.setSpecies_name(Felinae); //Felinae是猫的拉丁学名animal.setAverage_life(15);animal.setLiving_habit(喜欢独自生活喜欢干净喜欢肉类);System.out.println(简单介绍一下猫这一物种 : );((Cat)animal).cat_info(); //强转类型后面会讲到。System.out.println(--------------------------------);//调用成员方法animal.eat();animal.sleep();System.out.println();//2.改变animal引用变量的指向使其指向一个Dog类对象animal new Dog();animal.setSpecies_name(Canis lupus familiaris);animal.setAverage_life(12);animal.setLiving_habit(喜欢啃骨头喜欢嗅闻东西喜欢摇尾巴);System.out.println(简单介绍一下狗这一物种 : );((Dog)animal).dog_info(); //强制向下转型后面会讲到。System.out.println(--------------------------------);//调用成员方法animal.eat();animal.sleep();}
}运行结果 : 通过运行结果我们可以看出 : animal引用变量指向Cat类对象时运行的eat() 和 sleep() 方法就是Cat类中的方法而当我们改变animal引用的指向使其指向Dog类对象时运行的方法就变成了Dog类中的方法。 什么概念 同一引用变量调用相同的方法却因为指向的对象不同而产生了不同的结果。而我们平时习惯于将指向对象的引用变量当作对象的简称。那么此处animal对象体现的不就是多态么。 ③利用多态实现“旅行者请璃月人吃饭” : 有了多态后前面的“旅行者请吃饭”问题便可以迎刃而解了 : 我们只需要在Traveler类旅行者类中定义一个my_treat() 方法但与之前不一样的是形参类型定义为Liyue_people类和Cooking类即角色对象和料理对象各自的父类。如下所示 : public void my_treat(Liyue_people liyue_people, Cooking cooking) {System.out.println(海灯节 请 liyue_people.getName() 吃 cooking.getFood_name());} 注意这时候 因为Liyue_people类是Keqing类Zhongli类和Ganyu类的父类因此不管你传入这三个对象中的哪一个都会形成相当于 Liyue_people liyue_people new 子类对象;的形式即父类引用指向子类对象——多态的形式。此时传入的是哪个对象就调用哪个对象的方法。Cooking类和它的子类也是同理。 璃月人类刻晴类钟离类甘雨类以及料理类金丝虾球类豆腐类清心类代码均不变。Traveler类代码如下 :
package knowledge.polymorphism.introduction;public class Traveler {//利用多态一个方法解决请客吃饭问题public void my_treat(Liyue_people liyue_people, Cooking cooking) {System.out.println(海灯节 请 liyue_people.getName() 吃 cooking.getFood_name());}
}Treat类代码也不变不过注意此时Treat类中使用的my_treat() 方法已变化。 运行结果 : 利用多态将父类类型作为形参一个方法照样实现了我们的需求 2.多态中成员变量的使用 : ①使用规则 : 编译看左即左边的编译类型有没有这个成员这决定了我们能不能调用目标成员 运行看左多态关系中成员变量是不涉及重写的 ②代码演示 : 1up以Fruit类水果类为父类子类Apple类苹果类和Grape类葡萄类分别继承Fruit类以TestField类为测试类。 2我们在父类Fruit类中定义一些水果公有的属性(不用private修饰)例如species_name(物种学名), sweetness(平均甜度), shape(形态特征)。并分别在Apple类和Grape类中定义它们自己的这三个同名属性。 3最后在测试类中分别建立Fruit类和Apple类Grape类间的多态关系并通过Fruit类型的引用去调用这三个属性并输出。根据多态关系中成员变量的使用规则输出的三个属性应该每次都以Fruit类中的为基准。 4为使代码简洁up将两个子类都写在Fruit类的源文件中。Fruit类Apple类Grape类代码如下 :
package knowledge.polymorphism.about_field;//父类 : Fruit类
public class Fruit {//成员变量String species_name 物种学名噢;double sweetness 9.5; //水果的平均甜度大致为8~10左右。String shape 水果的外观形状捏;//构造器public Fruit() {}public Fruit(String species_name, double sweetness, String shape) {this.species_name species_name;this.sweetness sweetness;this.shape shape;}}//子类 : 葡萄类
class Grape extends Fruit {//成员变量String species_name Vitis_vinifera_L; //葡萄的拉丁学名double sweetness 16.5; //葡萄的平均甜度String shape 长得和葡萄差不多; //葡萄的形态特征//构造器public Grape() {super();}public Grape(String species_name, double sweetness, String shape) {super(species_name, sweetness, shape);}
}//子类 : 苹果类
class Apple extends Fruit {//成员变量String species_name Malus_pumila_Mill; //苹果的拉丁学名double sweetness 8.5; //苹果的平均甜度String shape 长得和苹果一样; //苹果的形态特征//构造器public Apple() {super();}public Apple(String species_name, double sweetness, String shape) {super(species_name, sweetness, shape);}
}TestField类代码如下 :
package knowledge.polymorphism.about_field;public class TestField {public static void main(String[] args) {//多态Fruit fruit new Apple();System.out.println(苹果的物种学名 fruit.species_name);System.out.println(苹果的平均甜度 fruit.sweetness);System.out.println(苹果的形态特性 fruit.shape);System.out.println(---------------------------------);//改变fruit引用变量的指向使其指向Grape类对象。fruit new Grape();System.out.println(葡萄的物种学名 fruit.species_name);System.out.println(葡萄的平均甜度 fruit.sweetness);System.out.println(葡萄的形态特性 fruit.shape);}
}运行结果 : 果然不出我们所料不管你fruit引用指向的是哪个子类水果对象直接调用成员变量永远优先使用Fruit类本身的属性。这时候p小将personable小将指风度翩翩的人又要出来bb问了 : tnnd太难弄哒好不容易知道多态有个能解决继承缺陷的用处怎么现在又用不了子类的属性了那你写这么一堆干嘛博主你搁这儿搞笑呢 p小将你先别急。以上演示只是要说明 : 多态关系中直接使用成员变量的规则是编译看左运行看左。即使子类定义了与父类同名的属性但本质上那也是子类特有的属性了。我们之前一直在说封装是继承的前提继承是多态的前提。了解了封装和继承就知道实际开发中通过对象直接调用属性的情况是不常见的不符合封装的要求。我们编写的类应该尽量靠近JavaBean标准。那我们就不能在多态的前提下使用子类的属性了吗当然不是这不多态的优缺点总结就来了。
四、多态的优点和缺点 : 1.益处 : ①可维护性 : 基于继承关系只需要维护父类代码提高了代码的复用性降低了维护工作的工作量。 ②可拓展性 : 把不同的子类对象都当作父类看待屏蔽了不同子类对象间的差异做出了通用的代码派生类的功能可以被基类的方法或引用变量所调用以适应不同的需求实现了向后兼容。 2.弊端 : 重点 父类引用不能直接使用子类的特有成员。 这也很好解释 : 前面在讲多态的实现步骤时我们说过——编译类型决定了引用变量可以调用哪些属性和行为而运行类型则是在程序运行过程中jvm实际使用的类型。父类引用说明编译类型是父类类型以父类类型编译当然只能使用父类中存在的成员。当然这里所说的成员包括成员变量和成员方法这二者在多态关系中的使用略有不同使用的成员变量必须是在父类中存在的且成员变量不涉及重写使用的成员方法也必须是在父类中存在的但是如果子类重写了父类方法优先调用子类重写的方法。 从jvm内存的角度解释就是 : .java文件经javac.exe命令编译后会生成.class的字节码文件当代码中需要使用到某个类该类的字节码文件就会加载进入方法区而jvm识别并执行的就是字节码文件。因此编译类型为父类类型那jvm识别的当然是这个类的字节码文件子类的特有成员根本就不在这个字节码文件里面jvm当然不认识。而对于子类重写的方法父类字节码文件中包含有被重写方法的信息jvm能够识别。而因为父类引用真正指向的是堆空间中的子类类型对象所以此时会优先从堆空间中的子类对象里面找使用子类重写后的方法若子类没有重写根据继承机制则使用父类的该方法。
五、类型转换 : Δ前言 : 当需要使用到子类特有功能比如要使用子类重写后的方法或者要使用子类的特有成员这时候就需要进行类型转换。类型转换分为向上类型转换和向下类型转换两种。其中向下转型是一个重点知识。 1.向上转型自动: ①含义 : 即子类类型转换成父类类型父类引用指向子类对象。向上转型是自动进行的我们的多态就是一种向上转型。eg : Animal animal new Cat(); ②语法格式 : 父类类型 父类引用变量 new 子类类型(); ③代码演示 : 这个说实话没啥好演示的。因为我们前面举过的所有多态的例子都属于向上转型。 请继续看向下转型那才是重点。 2.向下转型强制: ①含义 : 即父类类型转换成子类类型。为什么叫强制类型转化呢? 因为向下转型不会自动发生需要人为强转。并且向下转型改变了编译类型而编译类型决定了我们可以使用哪些成员当编译类型由父类类型转换为子类类型后我们当然可以使用子类的特有成员了。因此我们说要使用子类的特有功能靠的就是向下转型 ②语法格式 : 子类类型 子类引用变量 (子类类型) 父类引用变量。 或者 直接使用 ((子类类型)父类引用变量) 的方式来调用子类特有成员而不去做接收。 什么意思呢给大家举个栗子 : eg : Animal animal new Cat(); Cat cat new (Cat) animal; 后一条语句将Animal类型的引用变量animal转换成了子类Cat类类型的引用变量cat相当于animal和cat两个引用指向了同一Cat对象但堆空间中实际存在的Cat对象本身并没有变化。 ③代码演示 : 演示Ⅰ 我们就先来解决刚刚在演示多态中成员变量的使用时Fruit类引用无法直接调用Apple类和Grape类成员变量的问题。 Fruit类Apple类Grape类代码均不变大家可以往上翻翻看就在上面“多态中成员变量的使用”这一部分。当然懒得去翻也没关系重在演示向下类型转换。我们只需要在TestField类输出语句中的成员变量前使用强制类型转换即可成功输出子类特有成员。 TestField类代码如下 :
package knowledge.polymorphism.about_field;public class TestField {public static void main(String[] args) {//多态Fruit fruit new Apple();System.out.println(苹果的物种学名 ((Apple)fruit).species_name);System.out.println(苹果的平均甜度 ((Apple)fruit).sweetness);System.out.println(苹果的形态特性 ((Apple)fruit).shape);System.out.println(---------------------------------);//改变fruit引用变量的指向使其指向Grape类对象。fruit new Grape();System.out.println(葡萄的物种学名 ((Grape)fruit).species_name);System.out.println(葡萄的平均甜度 ((Grape)fruit).sweetness);System.out.println(葡萄的形态特性 ((Grape)fruit).shape);}
}运行结果 : 演示Ⅱ : 不知道大家还记不记得在“多态中的成员方法的使用”的演示中我们在Cat类和Dog类中分别定义了cat_info()方法和dog_info()方法用于打印出Cat对象和Dog对象的基本信息其实up在当时已用了强制向下转型大家不用再往上翻了我给大家放个截图吧如下 : 当时我们将Animal类型的引用变量animal分别向下转型为了Cat类引用和Dog类引用。以调用它们特有的方法运行结果如下 : 演示Ⅲ : 不知道大家是否还记得在开篇多态的引用中我们举了旅行者请刻晴钟离甘雨吃饭的栗子 当时up还分别在刻晴类钟离类和甘雨类中定义了它们特有的成员方法但是没有再测试类中调用其实就是为了等现在演示呢。 Keqing类Zhongli类Ganyu类各自的特有成员方法如下我还是直接放截图吧 : Treat类请客吃饭的测试类代码如下 :
package knowledge.polymorphism.introduction;public class Treat {public static void main(String[] args) {//在多态关系下调用Keqing类Zhongli类Ganyu类的特有成员方法//刻晴Liyue_people lp1 new Keqing(刻晴, 18, 女);System.out.println(刻晴特有的行为是天街巡游);Keqing kq (Keqing) lp1;kq.sky_street_cruise();System.out.println(------------------------------------------------);//钟离lp1 new Zhongli(摩拉克斯, 6000, 男);System.out.println(钟离特有的行为是天星);Zhongli zl (Zhongli) lp1;zl.sky_stars();System.out.println(------------------------------------------------);//甘雨lp1 new Ganyu(王小美, 3000, 女);System.out.println(甘雨特有的行为是降众天华);Ganyu gy (Ganyu) lp1;gy.descend_to_heaven();}
}运行结果 : 3.注意事项 : ①只有在继承关系的继承上才可以进行类型转换否则会报出ClassCastException类型转换异常。如下图所示 : ②在对引用变量进行向下转型之前必须保证该引用变量指向的——堆空间中真正的对象的类型就是目标类型。重要 比如Animal animal new Cat(); 现在animal引用指向了一个Cat类型的对象如果要对animal引用进行强制向下转型就只能转换成Cat类型的引用如果想转换成其他类型的引用就需要先改变animal引用的指向使其指向目标类型的对象。否则同样会报出类型转换异常。 ③那么我们在进行向下转型之前怎么就能知道——当前引用指向的对象是不是我们想要的目标类型的对象呢 答案是 : 在进行强制类型转化之前使用instanceof关键字来进行判断。 4.instanceof关键字重要 ①概述 : instanceof关键字可以判断指定对象是否为指定的类型并返回一个boolean类型的值常与if条件语句一起使用。instanceof关键字在多态的应用——多态参数和多态数组中也会频繁使用当然多态的应用我们在后面会讲到的。 ②用法 : 对象名 instanceof 数据类型 说明 : 1° 前面的“对象名”即引用变量。 2° 实际参与判断的是引用变量指向的——堆空间中真正的对象。 ③代码演示 : up以Person类为父类让Teacher类和Student类分别继承Person类最后在Test_instanceof类中进行测试。为了代码简洁up将Teacher类和Student类写在了Person类的源文件中。 我们要在测试类干什么 在测试类中我们先建立Person——Teacher类之间的多态然后利用instanceof关键字来判断Person引用指向的对象是否为Teacher类型以及是否为Student类型如果是Teacher类型就利用强制向下转型去调用Teacher类中的特有方法接着改变Person类型引用的指向使其指向Student类型的对象利用instanceof关键字对当前引用指向的对象重新进行判断以确定对象类型若确定当前对象是Student类型同理利用强转去调用Student类特有的成员方法。 Person类Teacher类Student类代码如下 :
package knowledge.polymorphism.ceof_demo;public class Person { //父类/*因为仅演示instanceof关键字因此父类暂时不以JavaBean标准来敲当然等讲到多态的应用时我们还会用到instanceof关键字到时候代码肯定会向JavaBean标准靠拢。这里大伙儿就先将就看看⑧。*/
}class Teacher extends Person { //子类//定义Teacher类的特有成员方法public void teach() {System.out.println(教书育人重在德行有德无德大相径庭。);}
}
class Student extends Person { //子类//定义Student类的特有成员方法public void what_time() {System.out.println(嘿哥们儿几点了还有几分钟下课);}
} Test_instanceof类代码如下 :
package knowledge.polymorphism.ceof_demo;public class Test_instanceof {public static void main(String[] args) {//1.多态先整上。Person person new Teacher();//判断当前对象的类型boolean boolean_1 (person instanceof Teacher);System.out.println(当前对象是Teacher类型吗: boolean_1);boolean boolean_2 (person instanceof Student);System.out.println(当前对象是Student类型吗: boolean_2);System.out.println(----------------------------------------------);//确定对象类型后进行强制向下转换调用该类特有的成员方法if (person instanceof Teacher) {System.out.println(当前对象指向的对象是Teacher类型可以将引用强转为Teacher类型);((Teacher) person).teach();} else if (person instanceof Student) {System.out.println(当前对象指向的对象是Student类型可以将引用强转为Student类型);((Student) person).what_time();}System.out.println(\n\n);//2.改变person引用的指向并重复上一轮的步骤。//判断当前对象的类型person new Student();boolean boolean_3 (person instanceof Teacher);System.out.println(当前对象是Teacher类型吗: boolean_3);boolean boolean_4 (person instanceof Student);System.out.println(当前对象是Student类型吗: boolean_4);System.out.println(----------------------------------------------);//确定对象类型后进行强制向下转换调用该类特有的成员方法if (person instanceof Teacher) {System.out.println(当前对象指向的对象是Teacher类型可以将引用强转为Teacher类型);((Teacher) person).teach();} else if (person instanceof Student) {System.out.println(当前对象指向的对象是Student类型可以将引用强转为Student类型);((Student) person).what_time();}}
}注意测试类中的代码我们是先以一个boolean类型做接收以证明使用instanceof关键字返回的是boolean类型后又搭配 if 条件语句来调用子类特有的方法。其实学了static关键字之后我们就可以把判断部分的代码单独写在main方法外的一个静态方法中不用这么繁冗写两遍了。 运行结果 : 六、Java的动态绑定机制重要: 限于博客字数已过20000从动态绑定机制开始up将把知识点的讲解另外放到一篇博文中然后把博客的链接给大家放在这里以增强大家的阅读体验。感谢理解博文链接如下 :
https://blog.csdn.net/TYRA9/article/details/128880552?spm1001.2014.3001.5501https://blog.csdn.net/TYRA9/article/details/128880552?spm1001.2014.3001.5501
七、多态的应用 : 多态的应用常见有多态数组和多态参数。博文链接如下 :
https://blog.csdn.net/TYRA9/article/details/128920758?spm1001.2014.3001.5501https://blog.csdn.net/TYRA9/article/details/128920758?spm1001.2014.3001.5501
八、抽象类抽象方法abstract关键字详解 : 抽象类详解。博文链接如下 :
https://blog.csdn.net/TYRA9/article/details/129137305?spm1001.2014.3001.5501https://blog.csdn.net/TYRA9/article/details/129137305?spm1001.2014.3001.5501
九、final关键字详解 : final关键字详解。博文链接如下 :
https://blog.csdn.net/TYRA9/article/details/129097055?spm1001.2014.3001.5501https://blog.csdn.net/TYRA9/article/details/129097055?spm1001.2014.3001.5501
十、static关键字类变量和类方法详解 : static关键字详解。博文链接如下 :
https://blog.csdn.net/TYRA9/article/details/129039405?spm1001.2014.3001.5501https://blog.csdn.net/TYRA9/article/details/129039405?spm1001.2014.3001.5501
十一、接口详解 :
https://blog.csdn.net/TYRA9/article/details/129174384?spm1001.2014.3001.5501https://blog.csdn.net/TYRA9/article/details/129174384?spm1001.2014.3001.5501
十二、Debug详解 :
https://blog.csdn.net/TYRA9/article/details/128884528?spm1001.2014.3001.5501https://blog.csdn.net/TYRA9/article/details/128884528?spm1001.2014.3001.5501
十三、多态篇总结 : 以上就是面向对象篇章——多态篇的全部内容了。讲真的多态篇的内容真是巨多要是up不放链接的话可能这一篇的字数就过10万字了。回顾一下我们从一个有趣的例子引入了多态的必要性接着介绍了Java中非常非常重要的动态绑定机制。接着又详细介绍了多态的使用然后是抽象类final关键字static关键字和接口这四大块对多态的补充和应用。最后up又给大家补充了一篇介绍Java Dubug的博文。其实Debug中我也是主要演示了一些最基本的逻辑语句和动绑机制按道理讲Debug更应该放到继承篇。但是我觉得讲面向对象讲得好好的突然插入这么个玩意儿多少有些抽象于是最后还是把Debug放到了多态篇。好滴之后up会出一篇总结性质的博文把Java面向对象三大特性——封装、继承多态三大节合并为一章并且做一些对面向对象基础的引入和内容的完善。我们不见不散感谢阅读