目录
概念:
多态是同一个行为具有多个不同表现形式或形态的能力。
优点:
- 1. 消除类型之间的耦合关系
- 2. 可替换性
- 3. 可扩充性
- 4. 接口性
- 5. 灵活性
- 6. 简化性
多态的引入:
现在有一个Master要对各种动物喂食,请用代码实现。
首先想到的是继承,再编写各种类。
Animal类
public class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Food类
public class Food {
private String name;
public Food(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
这里子类我就举一个例子,其他都是一样的
public class Cat extends Animal {
public Cat(String name) {
super(name);
}
}
1.传统方式
Master类
public class Master {
private String name;
public Master(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 主人给小狗喂骨头
public void feed(Dog dog, Bone bone) {
System.out.println("主人" + name + "给" + dog.getName() + "喂" + bone.getName() + "吃");
}
//主人给小猫喂鱼
public void feed(Cat cat, Fish fish) {
System.out.println("主人" + name + "给" + cat.getName() + "喂" + fish.getName() + "吃");
}
}
测试类
public class Poly01 {
public static void main(String[] args) {
Master master = new Master("张三");
Dog dog = new Dog("大黄");
Bone bone = new Bone("大骨头");
master.feed(dog, bone);
Cat cat = new Cat("汤姆");
Fish fish = new Fish("三文鱼");
master.feed(cat, fish);
Pig pig = new Pig("小猪佩奇");
Rice rice = new Rice("黑米");
master.feed(pig, rice);
}
}
2.新技术,使用多态
Master类
public class Master {
private String name;
public Master(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void feed(Animal animal, Food food) {
System.out.println("主人" + name + "给" + animal.getName() + "喂" + food.getName() + "吃");
}
}
测试类
public class Poly02 {
public static void main(String[] args) {
Master master = new Master("张三");
Animal animal = new Dog("大黄");
Food food = new Bone("大骨头");
master.feed(animal, food);
animal = new Cat("汤姆");
food = new Fish("三文鱼");
master.feed(animal, food);
animal = new Pig("小猪佩奇");
food = new Rice("黑米");
master.feed(animal, food);
}
}
先不看Master类,看一下测试类,这里可以发现创建了一个animal就可以接收它的所有子类实例对象(就是new出来的对象),food也是同样的道理,这就是多态的一种实现。
重载和重写体现多态:
public class PloyMethod {
public static void main(String[] args) {
//方法重载体现多态
A a = new A();
//这里我们传入不同的参数,就会调用不同sum方法,就体现多态
System.out.println(a.sum(10, 20));
System.out.println(a.sum(10, 20, 30));
//方法重写体现多态
B b = new B();
a.say();
b.say();
}
}
class B {//父类
public void say() {
System.out.println("B say() 方法被调用...");
}
}
class A extends B {//子类
public int sum(int n1, int n2) {//和下面sum 构成重载
return n1 + n2;
}
public int sum(int n1, int n2, int n3) {
return n1 + n2 + n3;
}
//方法重写体现多态
public void say() {
System.out.println("A say() 方法被调用...");
}
}
重载,对于sum方法来说,用a调用sum方法传入不同的参数列表调用不同的sum方法体现了多态。
重写,对于say方法来说,用a调用say方法和用b调用say方法调用了不同的say方法体现了多态。
编译类型和运行类型
编译类型:编译阶段编译器认为的类型
运行类型:程序运行后编译器实际操作的类型
对象的多态:
用上面的例子看来
public class Poly02 {
public static void main(String[] args) {
Master master = new Master("张三");
Animal animal = new Dog("大黄");
Food food = new Bone("大骨头");
master.feed(animal, food);
animal = new Cat("汤姆");
food = new Fish("三文鱼");
master.feed(animal, food);
animal = new Pig("小猪佩奇");
food = new Rice("黑米");
master.feed(animal, food);
}
}
1. 一个对象的编译类型和运行类型可以不一致
可以用同类型接收,也可以用其父类接收。Animal类型的变量可以接受了一个Dog类型的对象。
2.编译类型再定义对象时,就确定了,不能改变
3.运行类型是可以变化的
animal又接收了Cat类型的对象,然后又接收了Pig类型的对象。
4.编译类型看定义时 = 号的左边,运行类型看 = 号的右边
public class Master {
private String name;
public Master(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void feed(Animal animal, Food food) {
System.out.println("主人" + name + "给" + animal.getName() + "喂" + food.getName() + "吃");
}
}
这里feed方法接收实参的时候使用多态,用Animal类型接收动物类或其子类,用Food类型来接收食物类或其子类。
多态细节(复习必看):
多态的前提:
两个类存在继承关系
向上转型:
1.本质:
父类的引用指向了子类的对象
2.语法
父类类型 引用名 = new 子类类型(形参列表);
3.特点:
编译类型看左边,运行类型看右边。可以调用父类中的所有成员(需遵守访问权限),不能调用子类中特有成员,最终运行结果看子类的具体实现。
看下面例子:
public class Animal {
String name = "动物";
int age = 10;
public void sleep() {
System.out.println("睡");
}
public void run() {
System.out.println("跑");
}
public void eat() {
System.out.println("吃");
}
public void show() {
System.out.println("hello,你好");
}
}
public class Cat extends Animal {
public void eat() {//方法重写
System.out.println("猫吃鱼");
}
public void catchMouse() {//Cat特有方法
System.out.println("猫抓老鼠");
}
}
public class PolyDetail {
public static void main(String[] args) {
Animal animal = new Cat();
Object object = new Cat();//可以 Object 也是 Cat的父类
animal.eat();
animal.run();
animal.show();
animal.sleep();
//animal.catchMouse();//错误
}
}
运行结果
发现不能调用子类Cat类特有的catchMouse方法。
向下转型:
语法:
子类类型 引用名 = (子类类型) 父类引用;
1.只能转父类的引用,不能转父类的对象
2.要求父类的引用必须指向的时当前目标类型的对象
3.可以调用子类类型中的所有成员
public class PolyDetail {
public static void main(String[] args) {
Animal animal = new Cat();
Cat cat = (Cat) animal;
cat.catchMouse();//猫抓老鼠
Animal animal1 = new Cat();
Cat cat1 = (Cat) animal1;
}
}
属性没有重写一说
看下面代码的运行结果
public class PolyDetail02 {
public static void main(String[] args) {
Base base = new Sub();
System.out.println(base.count);
Sub sub = new Sub();
System.out.println(sub.count);
Sub sub1 = (Sub) base;
System.out.println(sub1.count);
}
}
class Base { //父类
int count = 10;//属性
}
class Sub extends Base { //子类
int count = 20;//属性
}
后面两个结果第二个结果最容易理解,第三个是因为sub1接收了已经转成Sub类型的引用,调用的当然是Sub类中的count,但是第一个怎么理解呢,之前不是说代码最终执行的是运行类型吗,那不是看右边的Sub类型的count吗。这里属性是不可以这么看的,属性是直接用编译类型。
技巧:方法看=右边,属性看=左边
instanceof关键字(复习必看)
语法:
引用1 instanceof 类型2
返回类型是boolean,如果引用1的类型(运行类型,必须是一个引用)和类型2的类型相同或者是其子类返回true,否则返回false,
举个例子
public class PolyDetail03 {
public static void main(String[] args) {
BB bb = new BB();
System.out.println(bb instanceof BB);//true
System.out.println(bb instanceof AA);//true
//aa 编译类型 AA,运行类型是BB
AA aa = new BB();
System.out.println(aa instanceof AA);//true
System.out.println(aa instanceof BB);//true
Object obj = new Object();
System.out.println(obj instanceof AA);//false
String str = "hello";
//System.out.println(str instanceof AA);//没有关系
System.out.println(str instanceof Object);//true
}
}
class AA {} //父类
class BB extends AA {} //子类
看到这里我想到了之前在一本书上看到了一个细节,有兴趣的朋友可以自行查找,是第18条建议。
《编写高质量代码:改善Java程序的151个建议》
这是书的链接
这本书刚开始看的时候有的地方没有看懂,现在回过头看来,很多都又有所领悟,当然我还没有看完,这本书真的不错。刚开始看不懂没关系,先做标记,继续看后面的建议,但是我还是建议,看不懂的时候还是要让看个大概,以后学习到的时候会想起来这部分有细节。
数组多态
多态的应用场景还有很多
我举一个数组多态的例子:
Person类
public class Person {
private String name;
private int age;
public Person(String name, int age) {
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 + "\tage=" + age;
}
public void ss() {
System.out.println("assa");
}
}
Teacher类
public 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;
}
@Override
public String say() {
return "老师:>" + super.say() + "\tsalary=" + salary;
}
public void teach() {
System.out.println("老师教课~~~~~");
}
}
Student类
public class Student extends Person {
private double score;
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
@Override
public String say() {
return "学生:>" + super.say() + "\tscore=" + score;
}
public void study() {
System.out.println("学生学习~~~~~");
}
}
测试类
public class PolyArray {
public static void main(String[] args) {
Person[] person = new Person[5];
person[0] = new Person("Tom", 18);
person[1] = new Student("cs", 19, 100);
person[2] = new Student("wjf", 19, 100);
person[3] = new Teacher("hsp", 30, 20000);
person[4] = new Teacher("spw", 30, 20000);
for(int i = 0; i < person.length; i++) {
System.out.println(person[i].say());
if(person[i] instanceof Student) {
((Student)person[i]).study();
//Student student = (Student) person[i];
//student.study();
} else if(person[i] instanceof Teacher) {
((Teacher)person[i]).teach();
//Teacher teacher = (Teacher) person[i];
//teacher.teach();
} else if(person[i] instanceof Person) {
} else {
System.out.println("输入有误");
}
}
Student student = new Student("1", 2, 3);
student.ss();//自己找不到可以找父类
Person person1 = new Person("张三", 28);
//Student student1 = (Student)person1;//有参数就不好搞了
//student1.ss();
System.out.println("hello world!");
}
}
创建一个元素的类型都是Person的数组,每个元素可以使用多态执行各自的业务。