多态概述: 同一个对象在不同时刻表现出来的不同形态,即简单理解为对象的不同形态
用猫举例如下就是猫对象的不同形态: 猫 cat = new 猫() => 此时猫是猫 动物 animal = new 猫() => 此时猫是动物
多态的前提: 1、有继承/实现关系 2、有方法重写 3、有父类引用指向子类对象
多态的练习
xxxxxxxxxx
package ch8;
public class a_1_1Animal {
public void eat() {
System.out.println("动物类的eat方法");
}
}
xxxxxxxxxx
package ch8;
//使用多态的第一个前提, 继承
public class a_1_2Cat extends a_1_1Animal {
//使用多态的第二个前提, 方法重写
public void eat() {
System.out.println("我是猫类里面的方法重写");
}
//第三个前提写在测试类里面
}
xxxxxxxxxx
package ch8;
public class a_1_3测试 {
public class AnimalDemo {
public static void main(String[] args) {
//使用多态的第三个前提, 父类引用指向子类对象
a_1_1Animal a = new a_1_2Cat();
//注意上面的a_1_1Animal是父类,a_1_2Cat是子类
}
}
}
多态中成员访问特点
例如
a_2_1Animal a = new a_2_2Cat();
下面的介绍里面所说的"左边"指的是a_2_1Animal父类
下面的介绍里面所说的"有边"指的是a_2_2Cat子类
下面的介绍里面所说的"编译"可以理解为书写时是否报错
下面的介绍里面所说的"执行"可以理解为运行时以谁的类为准
介绍
xxxxxxxxxx
1、成员变量:
编译看左边,即书写时是否报错取决于父类是否有这个变量
执行看左边,即运行时父类的优先级高或理解为只输出父类里面的变量
2、成员方法:
编译看左边,即书写时是否报错取决于父类是否有这个方法
执行看右边,即运行时父类的优先级高或理解为只输出父类里面的方法
思考: 造成成员变量和成员方法的访问不一样的原因是什么。答案: 因为成员方法有重写,而成员变量没有
多态中成员访问特点的练习
package ch8;
public class a_2_1Animal {
public int age = 40;
public void eat() {
System.out.println("动物类的eat()方法");
}
}
xxxxxxxxxx
package ch8;
//继承
public class a_2_2Cat extends a_2_1Animal {
public int age = 20;
public int weight = 10;
public void eat() {
System.out.println("猫类中的重写方法-重写eat()");
}
public void playGame() {
System.out.println("猫-playGame方法");
}
}
xxxxxxxxxx
package ch8;
public class a_2_3测试 {
public static void main(String[] args) {
//先要有父类引用指向子类对象
a_2_1Animal a = new a_2_2Cat();
System.out.println(a.age);
//下面那行会报错,因为父类a_2_1Animal里面没有weight变量
//System.out.println(a.weight);
//上面的age输出40,说明多态中访问的是父类中的成员变量
a.eat();
//下面那行会报错,因为父类a_2_1Animal里面没有playGame方法
//a.playGame();
//上面的eat输出的是子类a_2_2Cat重写之后的方法的输出,说明多态中访问的是子类中的成员方法
}
}
多态的好处和弊端
多态的好处:提高了程序的扩展性 具体体现为:定义方法的时候,使用父类型作为参数,将来在使用的时候,使用具体的子类型参与操作即可
多态的弊端:不能使用子类的特有功能
好处和弊端的练习
x
package ch8;
public class a_3_1Animal {
public void eat() {
System.out.println("动物类");
}
}
x
package ch8;
//继承
public class a_3_2Cat extends a_3_1Animal {
public void eat() {
System.out.println("猫类中的重写方法-重写父类的eat()");
}
}
x
package ch8;
//动物操作类
public class a_3_3AnimalOperator {
//带参方法,参数是猫类的
public void useAnimal(a_3_2Cat c) { //相当于a_2_2Cat c = new a_2_2Cat();
c.eat();
}
//--------------------------------------------------------------------------------
//带参方法,参数是狗类的
public void useAnimal(a_3_5Dog d) { //相当于a_2_2Cat c = new a_2_2Cat();
d.eat();
}
//-----------------------------------------------------------------------------------
//上面的类每增加一个太麻烦了 。下面是优化写法(即可以把上面的猫类和狗类的代码全部注释,不影响),同时包含了上面的猫类和狗类的代码
//原理:父类型作为参数
public void useAnimal(a_3_1Animal a) {
//相当于a_2_1Animal a = new a_3_2Cat()
//也包含了a_2_1Animal a = new a_3_5Dog()
//所以这里更少的代码完成了上面的猫类和狗类的同样的作用。原理是成员方法编译看左边,执行看右边
a.eat();
//如果此时再新建一个pig类文件,我们只需要在新建的pig类里面重写一次方法
//再去'a_3_4测试'里面创建pig对象就行了
//随便添加多少个类都行
//弊端:如果在这些动物子类里面定义一个特有方法,并且在这里调用,那么是不允许的,会报错
//比如在pig类里面写一个sleep()方法,在用如下的代码调用一下
//a.sleep();
//上面那行就会报错
//原理:不允许子类访问子类自身的特有方法
}
}
xxxxxxxxxx
package ch8;
public class a_3_4测试 {
public static void main(String[] args) {
//创建动物操作类的对象,调用方法
a_3_3AnimalOperator ao = new a_3_3AnimalOperator();
//先创建一个猫类对象,因为a_3_3AnimalOperator里面的useAnimal的c传的参数是猫类的
a_3_2Cat c = new a_3_2Cat();
ao.useAnimal(c);
//--------------------------------------------------------------------------------
//先创建一个狗类对象,因为a_3_3AnimalOperator里面的useAnimal的d传的参数是狗类的
a_3_5Dog d = new a_3_5Dog();
ao.useAnimal(d);
//创建pig对象
a_3_6pig p = new a_3_6pig();
ao.useAnimal(p);
}
}
xxxxxxxxxx
package ch8;
//继承
public class a_3_5Dog extends a_3_1Animal {
public void eat() {
System.out.println("狗类中的重写方法-重写父类的eat()");
}
}
x
package ch8;
//别忘了继承
public class a_3_6pig extends a_3_1Animal {
public void eat() {
System.out.println("猪类中的重写方法-重写父类的eat()");
}
//探究弊端,先写一个特有方法
public void sleep() {
System.out.println("睡");
}
}
多态中的转型
向上转型: 1、从子到父 2、父类引用指向子类对象
向下转型: 1、从父到子 2、父类引用转为子类对象
转型的练习
xxxxxxxxxx
package ch8;
public class a_4_1Animal {
public void eat() {
System.out.println("动物类");
}
}
xxxxxxxxxx
package ch8;
public class a_4_2Cat extends a_4_1Animal{
public void eat() {
System.out.println("猫类中的重写方法-重写父类的eat()");
}
public void playGame() {
System.out.println("猫类的playGame特有方法");
}
}
xxxxxxxxxx
package ch8;
public class a_4_3测试 {
/*向上转型:
1、从子到父
2、父类引用指向子类对象
向下转型:
1、从父到子
2、父类引用转为子类对象*/
public static void main(String[] args) {
//按照多态的方式创建一个对象
//右边是创建子类对象,左边是父类引用。下面那行可以理解为父类引用指向子类对象(把子类对象赋值给父类的引用),即向上转型
a_4_1Animal a = new a_4_2Cat();
//调用方法
a.eat();
/*//调用猫类的特有方法,如下,会报错,原理是编译看左边,a_4_1Animal里面没有playGame方法,所以会红字报错
//a.playGame();
//如果此时我们就想使用猫类的特有方法,解决方法如下
//先创建猫类对象
a_4_2Cat c = new a_4_2Cat();
c.eat();
c.playGame(); //就可以使用猫类的特有方法playGame了
//弊端: 又新建了一个猫类对象,没有必要。思考如何在不建猫类对象的情况下,使用猫类的特有方法。如下使用向下转型*/
//父类引用转为子类对象(把父类引用a强转为子类对象a_4_2Cat c,再把值赋值给猫类),即向下转型
a_4_2Cat c = (a_4_2Cat)a;
//就可以正常使用猫类的特有方法playGame了
c.eat();
c.playGame();
}
}
猫和狗_多态版
需求:采用多态的思想实现猫和狗的案例,并测试
思路:
x
定义动物类
1、成员变量: 姓名,年龄
2、构造方法: 无参,带参
3、成员方法: get和set方法,吃饭()
定义猫类,继承动物类
1、构造方法: 无参,带参
2、成员方法: 重写吃饭()
定义狗类,继承动物类
1、构造方法: 无参,带参
2、成员方法: 重写吃饭()
定义测试类,并进行测试
猫和狗_多态版的练习
xxxxxxxxxx
package ch8;
public class a_5_1Animal {
//定义成员变量
private String name;
private int age;
//无参构造方法
public a_5_1Animal() {
}
//带参构造方法
public a_5_1Animal(String name, int age) {
this.name = name;
this.age = age;
}
//get和set方法
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 void eat() {
System.out.println("动物类的eat方法");
}
}
xxxxxxxxxx
package ch8;
//继承动物类
public class a_5_2Cat extends a_5_1Animal {
//无参构造方法
public a_5_2Cat() {
}
//带参构造方法
public a_5_2Cat(String name, int age) {
super(name, age);
}
//重写动物类的eat方法
public void eat() {
System.out.println("猫类中的重写方法-重写父类的eat()");
}
}
x
package ch8;
//继承动物类
public class a_5_3Dog extends a_5_1Animal {
//无参构造方法
public a_5_3Dog() {
}
//带参构造方法
public a_5_3Dog(String name, int age) {
super(name, age);
}
public void eat() {
System.out.println("狗类中的重写方法-重写父类的eat()");
}
}
xxxxxxxxxx
package ch8;
public class a_5_4测试 {
public static void main(String[] args) {
//多态的形式创建猫类对象。无参,a_5_2Cat()
a_5_1Animal a = new a_5_2Cat();
a.setName("猫名-无参");
a.setAge(2);
System.out.println(a.getName() + "," + a.getAge());
a.eat();
//多态的形式创建猫类对象。带参,a_5_2Cat("猫名-带参",4);
a = new a_5_2Cat("猫名-带参", 4);
System.out.println(a.getName() + "," + a.getAge());
a.eat();
}
}