5.3多态性
声明: 此笔记通过观看【尚学堂】+《Java程序设计(赖小平主编)清华大学出版社》感悟整理得出, 若有抄袭,请注明来源联系作者!
多态(polymorphism):多种形态
多态指的是同一个方法调用,由于对象不同可能会有不同的行为。例如:猫、狗是动物,但它们有不同的形态,狗的叫声:旺旺旺,猫的叫声是:喵喵喵~
在面向对象的程序设计中,多态性主要表现为类声明的变量可指向多种不同的对象,其有多种类型的能力。
多态存在的三个条件:继承、方法重写、父类引用指向子类引用
变量不具有多态性!!!!!!
变量不具有多态性!!!!!!
变量不具有多态性!!!!!!
静态(即类变量类方法)也不具有多态!
多态主要表现为:方法的多态性。变量不具有多态!!!!!,静态(即类变量类方法)也不具有多态
优点:极大的增强了程序的可扩展性,提高了代码的可维护性。
【测试 动物类的多态】
package cn.gdlgxy.David;
class Animal{
public void shout() {
System.out.println("叫....");
}
}
class Dog extends Animal{
public void shout() {
System.out.println("汪汪汪....");
}
public void Look() {
System.out.println("看门口");
}
}
class Cat extends Animal{
public void shout() {
System.out.println("喵喵喵.....");
}
}
public class PolyTest {
public static void main(String[] args) {
Animal a = new Animal();
animalShout(a);
Animal d = new Dog();//也可以写成Dog b = new Dog();
animalShout(d);
//产生多态。子类对象Dog给父类Animal引用,a是Animal类型,调用shout方法。
//b传的是Dog,则是调用Dog的shout()方法,传猫则调用猫的shout()方法
animalShout(new Cat());
}
static void animalShout(Animal a) {
a.shout();
}
/*
* 若无多态,则需要创建更多的animalshout()方法
*若传的是狗则需:
* static void animalShout(Dog a) {
a.shout();
}
Dog b = new Dog();
animalShout(b);
* 若传的是猫则与上同理。所以代码较为复杂。这就是多态的好处,使代码更加简洁
*/
}
结果:
叫….
汪汪汪…
喵喵喵…
5.4对象的转型
-
变量的多态性:
对象的变量可以引用本类的对象,也可以引用子类的对象。就是说,对象的真正类型是由常见对象时调用的构造方法决定的,这就是对象变量的多态。(如上转型对象)
父类引用指向子类对象→向上转型,属于自动类型转换;
向上转型后的父类引用变量只调用它编译类型的方法,若要调用子类的新增方法,则需要强制转型→向下转型
【实例测试】
public class PolyTest {
public static void main(String[] args) {
Animal a = new Animal();
animalShout(a);
Animal d = new Dog();//也可以写成Dog d = new Dog();
animalShout(d);
//产生多态。子类对象Dog给父类Animal引用,a是Animal类型,调用shout方法。
//b传的是Dog,则是调用Dog的shout()方法,传猫则调用猫的shout()方法
animalShout(new Cat());
/*对象的转型:
* Animal b = new Dog();
*自动向上转型,编译器认为 d 就是Animal,若 d 想调用Dog的其它方法,
*则需要进行强制转型: Dog d1 = (Dog)d; 强制向下转型
* d1.Look();
*注意:
*若: Animal c = new Cat();
* Dog d2 = (Dog)c;
* 编译会通过,但是,运行会出现 java.lang.ClassCatException:
* 猫不能强制转型为狗。
*/
}
static void animalShout(Animal a) {
a.shout();
}
注:部分代码在上节
- 上转型的特征:
1、 上转型对象只能访问父类中声明的成员变量和成员方法,不可以访问子类新增的成员变量和成员方法。
2、 如果子类重写了父类的方法,则向上转型的对象调用的方法则是重写后的方法
3、 如果父类重新定义了父类的同名变量,则上转型对象引用该变量时是父类定义的变量,而不是子类中定义的变量
【实例测试 向上转型对象】
package cn.gdlgxy.David;
class Animal02{
public final String type = "动物";
private String name;
public Animal02(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void shout02(){
System.out.println("不同动物不同叫声");
}
}
class Dog02 extends Animal02{
public final String type = "狗";
private String color;
public Dog02(String name,String color) {
super(name);
this.color = color;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public void shout02() {
System.out.println(getName()+"汪汪汪....");
}
public void Look() {
System.out.println("看门口");
}
}
class Cat02 extends Animal{
public void shout02() {
System.out.println("喵喵喵.....");
}
}
public class PloyTest02 {
public static void main(String[] args) {
System.out.println("====向上转型对象====");
Animal02 dog = new Dog02("旺财","黑色");
System.out.println("该对象是:"+dog.type);
//上转型对象引用覆盖变量时父类中定义的变量
dog.shout02(); //上转型对象调用重写后的方法
//System.out.println("动物颜色为:"+dog.getColor());
//编译报错,上转型对象不能访问子类新增的方法
}
}
结果:
重新观看《懒惰的小黑笔记02》操作符instanceof,加深了解!
5.5 final关键字
- final关键字的作用:
- 1、 修饰变量:
(1)被修饰的变量一旦赋值就不可以被重新赋值。
(2)final修饰的成员变量必须在构造器结束前赋值,即可在声明的同时赋值,在构造方法中赋值,或在静态代码块中赋值,其后不可更改。
(3)static与final共同修饰的变量,将其看做常量,大写字母命名,且在声明时赋值。
final int 变量(一般用大写,每个单词用“_”分开) = 51;
- 2、 修饰方法:
该方法不能被子类重写,但可以重载(在一定程度上,final方法的执行效率高,因为在调用该方法时,JVM不需要进行重写判断)
final void rest(){}
- 3、 修饰类:
修饰的类不能继承,比如:Math、String等
public final class FinalTest{…} class Student extends
FinalTest{….} //编译出错,final类不能被继承
第五章 应用实例
【实例 测试图形派的继承】
package cn.gdlgxy.test;
import java.util.Scanner;
/**
* 测试图形类
* @author 卟淡
*
*/
class Geom {
private String color;
private boolean isFilled;
public Geom() { //无参构造方法,方便输入
}
public Geom(String color,boolean isFilled) { //有参构造方法
this.color = color;
this.isFilled = isFilled;
}
public String getColor() {
return color;
}
public void setColor(String color) { //设置颜色
this.color = color;
}
public Boolean getIsFilled() {
return isFilled;
}
public void setIsFilled(Boolean isFilled) { //设置填充
this.isFilled = isFilled;
}
public String toString() { //toString方法
return "构建图形颜色:"+color+"\n是否填充:"+isFilled;
}
}
//继承圆形类的矩形类
class Rectangle extends Geom{
private double length;
private double width;
public Rectangle() {
}
public Rectangle(String color,boolean isFilled,double length
,double width) {
super(color,isFilled);
this.length = length;
this.width =width;
}
public double getLength() {
return length;
}
public void setLength(double length)throws Exception {
if(length<0)throw new Exception("长不能小于0");
this.length = length;
}
public double getWidth() {
return width;
}
public void setWidth(double width)throws Exception {
if(width<0)throw new Exception("宽不能小于0");
this.width = width;
}
public double getArea() {
return length*width;
}
}
//继承矩形的长方形类
class Rectlinder extends Rectangle{
private double higth;
public Rectlinder() {
}
public Rectlinder(String color,boolean isFilled,double length
,double width,double higth) {
super(color,isFilled,length,width);
this.higth = higth;
}
public double getHigth() {
return higth;
}
public void setHigth(double higth) {
this.higth = higth;
}
public double getVolume() {
return getArea()*higth;
}
public String toString() {
return super.toString() + "\n长方形的体积为:"+getVolume();
}
}
//测试类
public class GeomTest {
public static void main(String[] args) {
while(true) {
Scanner sc = new Scanner(System.in);
double L,w,h;
String color;
boolean isFilled;
System.out.println("输入颜色、填充(true/false)、底面长、宽和高:");
color = sc.nextLine();
isFilled = sc.nextBoolean();
L = sc.nextDouble();
w = sc.nextDouble();
h = sc.nextDouble();
Rectlinder rectliner = new Rectlinder(color,isFilled,L,w,h);
System.out.println(rectliner.toString());
}
}
}
【正确答案:】
// 有个疑问点:color是String类型,输入的不是颜色也能通过编译!!
/*
* 《java程序设计》答案:
* try {
Scanner sc = new Scanner(System.in);
double L,w,h;
String color;
boolean isFilled;
while(true) {
System.out.print("请输入空格分隔的长方形的颜色、填充(true/false)、底面长"
+ "宽和高\n(end结束程序)");
color =sc.nextLine();
if(color.equals("end")) { //equals类→查看源码
System.out.println("程序结束");
return;
}
isFilled = sc.nextBoolean();
L = sc.nextDouble();
w = sc.nextDouble();
h = sc.nextDouble();
Rectlinder rectliner = new Rectlinder(color,isFilled,L,w,h);
System.out.println(rectliner.toString());
}
}catch(Exception e) {
System.out.println(e);
}
}
}*/
【编译结果:】
【测试 动物的多态】
package cn.gdlgxy.test;
import java.util.Scanner;
/**
* 测试动物的多态性
* @author 卟淡
*
*/
//动物类
class Animal{
private String name;
private int legs;
public Animal(){ //给成员变量赋值
this.name = "人";
this.legs = 4;
}
public Animal(String name,int legs) { //带参数的构造方法
this.name = name;
this.legs = legs;
}
public String getName() { //获取名称
return name;
}
public void setName(String name ) { //设置名称
this.name = name;
}
public int getLegs() { //获取腿数
return legs;
}
public void setLegs(int legs)throws Exception { //设置腿数
if(legs<0)throw new Exception("输入的腿数有误");
this.legs = legs;
}
public void move() { //创建move()方法
System.out.println(name + "在移动"+",他有"+legs+"条腿");
}
public void move(int n) { //带参数的move()方法
if(n>=0) {
System.out.println("移动了"+n+"次" );
n--;
}else {
System.out.println("输入有误!");
}
}
}
//鱼类继承动物类
class Fish extends Animal{
public Fish(String name) { //设置只有名字的构造方法,腿数直接赋值
super(name,0);
}
public void move() { //重写move()方法
System.out.println(getName() +"在水中游来游去"+",他有"
+super.getLegs()+"条腿");
}
}
//马类继承动物类
class Horse extends Animal{
public Horse(String name) {
super(name,4);
}
public void move() { //重写move()方法
System.out.println(getName()+"在草原驰骋"+",他有"
+super.getLegs()+"条腿");
}
}
public class Zoo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("===测试动物的多态性====");
while(true) {
System.out.println("请输入(1表示动物,2表示鱼,3表示马,其它程序结束):");
int num;
num = sc.nextInt();
if(num == 1) {
Animal an= new Animal();
an.move();
}else if(num == 2) {
Animal fish = new Fish("鱼");
//向上转型,覆盖父类的变量,上转型对象调用重写后的方法
fish.move();
}else if(num == 3) {
Animal horse = new Horse("马");
horse.move();
}else {
System.out.println("===程序结束===");
return;
}
}
}
}
【编译结果:】
注:在子类中定义构造方法时,也可以利用super直接给变量赋值。如:super(name,0);
- 本章总结:
本章介绍了面向对象的三大特征:继承、封装和多态。继承是一种由己存在的类型创建一个或多个子类型的机制,即在现有的基础类上构建子类。
Super用于指代父类对象,用super作为前缀,可以引用父类被覆盖的成员变量,调用父类被重写的构造方法以及调用父类的构造方法。Final关键字可以修饰类、变量和方法,一旦赋值,其后不能改变。Final修饰的类不能被继承,修饰的方法不能被子类重写,变量值不可更改。
一个类通过派生或实现接口可演化成多种类型,这就是类的多态性。主要表现在变量的多态性(如对象的向上转型)和方法的多态性。
子类对象有向上转型对象,父类可以直接引用子类的对象;但父类对象没有下转型对象,子类变量不能直接引用父类对象,必须通过强制转型。