java面向对象中级-多态
1. 多态的基本介绍
方法或对象具有多种形态。是面向对象的三大特征,多态是建立在封装和继承之上的。
通俗一点来说:谈论多态时,你可以想象成人们怎么对待不同类型的宠物。无论是狗、猫还是其他宠物,人们都会用相似的方式对待它们,比如喂食、抚摸等。这种统一的对待方式就是多态的一种体现。
扩展:多态是指让不同的对象可以用同样的方式被处理。在编程中,这意味着你可以使用一个通用的接口或方法来处理不同的对象,无论它们是什么类型。这样做的好处是,你可以写更通用的代码,而不需要为每个对象类型编写特定的代码。
2. 多态的具体体现
- 重写和重载就体现多态
案例:
public class PolyMethod {
public static void main(String[] args) {
//方法重载体现多态
A a = new A();
//我们传入不同的参数,就会调用不同的方法,就体现多态
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) {
return n1 + n2;
}
public int sum(int n1,int n2, int n3) {
return n1 + n2 + n3;
}
public void say() {
System.out.println("A say()方法被调用");
}
}
-
对象的多态(核心)
1)一个对象的编译类型可以和运行类型不一致
2)编译类型在定义对象时,就确定了,不能改变
3)运行类型是可以变化的
4)编译类型看定义时 =的左边,运行类型看 =右边Animal animal = new Dog();//(animal编译类型是Animal,运行类型是Dog) Animal animal = new Cat();//(animal的运行类型变成了Cat,编译类型仍然是Animal)
案例:
程序入口:
public class PolyObject { public static void main(String[] args) { //对象多态的特点 //animal 编译类型就是 Animal,运行类型是Dog Animal animal = new Dog(); animal.cry();//运行类型是Dog,所以cry就是dog的Dog cry() //animal是编译类型,运行类型就是 Cat; animal = new Cat(); animal.cry(); } }
父类:
public class Animal { public void cry() { System.out.println("Animal cry()动物在叫"); } }
两个子类
public class Cat extends Animals { public Cat(String name) { super(name); } }
public class Dog extends Animals { public Dog(String name) { super(name); } }
3. 多态注意事项和细节讨论
多态的前提是:两个类(对象)存在继承关系
1.多态的向上转型
-
本质:父类的引用指向了子类的对象
-
语法:父类类型 引用名 = new子类类型();
-
特点:编译类型看左边,运行类型看右边。
可以调用父类中的所有成员(需要遵守访问权限)
不能调用子类中的特有成员;
最终运行结果看子类的具体体现!案例:
程序入口
public class PolyDetail { public static void main(String[] args) { //向上转型,父类引用指向了子类的对象 Animal animal = new Cat(); // animal.catchMouse(); 错误 -无法调用子类的特有方法 //你也可以调用父类和子类的方法:去理解上面的概念 } }
父类:
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("你好"); } }
子类:
public class Cat extends Animal { public void eat() {//方法重写 System.out.println("猫吃鱼"); } public void catchMouse() {//Cat特有方法 System.out.println("猫捉老鼠"); } }
2.多态的向下转型
- 语法:子类类型:子类类型 引用名 = (子类类型) 父类引用
- 只能强转父类的引用,不能强转父类的对象
- 要求父类的引用必须指向的是当前目标类型的对象
- 可以调用子类类型中所有的成员
class Animal {
void makeSound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Dog barks");
}
void fetch() {
System.out.println("Dog fetches a ball");
}
}
在这个例子中我们先向上转型:
Animal animal = new Dog(); // 向上转型
然后我们尝试向下转型:
Dog dog = (Dog) animal; // 向下转型
备注: 必须先向上转型(将子类对象赋值给父类引用),然后才能向下转型,以上案例为前四个概念的案例
- 属性没有重写之说!属性的值看编译类型(属性的值看编译类型,方法的重写看运行类型)
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);
}
}
class Base {
int count = 10;
}
class Sub extends Base {
int count = 20;
}
- instanceof比较操作符,用于判断对象的类型是否为XX类型或XX类型的子类型
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 bb是AA的子类型
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);//错误的
String str = "hello";
System.out.println(str instanceof Object);//true
}
}
class AA{}
class BB extends AA {}
3.java的动态绑定机制
动态绑定机制:
- 当调用对象方法的时候,该方法回合该对象内存地址/运行类型绑定。
- 当调用对象的属性时,没有动态绑定机制,哪里声明,那里使用。
public class DynamicBinding {
public static void main(String[] args) {
A a = new B();
System.out.println(a.sum());//40
System.out.println(a.sum1());//30
}
}
class A {//父类
public int i = 10;
public int sum() {
return getI() + 10;
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}
class B extends A {
public int i = 20;
public int sum() {
return i + 20;
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}
4.多态的应用
1.多态数组
-
应用实例:想在有一个继承结构:要求创建一个Person对象、两个Student和两个Teacher对象,统一放在数组中,并调用每个对象的say() 方法。(Person 属性name age 方法:say() ,Teacher特有属性saalary,Student特有属性score)备注:属性private
Person类
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
setName(name);
setAge(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 + "\t" + age;
}
}
Student类
public class Student extends Person {
private double score;
public Student(String name,int age,double score) {
super(name,age);
this.score = score;
setScore(score);
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
//重写父类say
public String say() {
return super.say() + "\t" + score;
}
}
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;
}
//重写父类say方法
@Override
public String say() {
return super.say() + "\t" + salary;
}
}
程序入口
public class PloyArray {
public static void main(String[] args) {
//两个student,两个teacher,统一放入数组
Person[] persons = new Person[5];
persons[0] = new Person("jack",20);
persons[1] = new Student("jackz",19,89.5);
persons[2] = new Student("smith", 19, 100);
persons[3] = new Teacher("scott",30,20000);
persons[4] = new Teacher("king",50,24000);
for (int i = 0; i < persons.length; i++) {
System.out.println(persons[i].say());
}
}
}
2.多态参数(参数的多态性)
介绍:方法定义的形参类型为父类类型,实参类型允许为子类类型
参数多态性是一种面向对象编程的特性,它允许你在一个方法中使用父类类型的参数,但在调用这个方法时传递子类对象。这样,你可以用同一个方法来处理不同子类的对象,让代码更加通用和灵活。
想象你有一个“工具箱”,里面有不同种类的工具。这些工具是各自的类,比如锤子、螺丝刀、扳手等。现在,你想写一个“修理”的方法来修理这些工具。不同的工具有不同的修理方式,但你不希望为每个工具写一个独立的修理方法。
// 员工类,表示一个基本的员工信息
class Employee {
private String name; // 姓名
private double monthlySalary; // 月工资
// 构造方法,用于初始化员工的姓名和月工资
public Employee(String name, double monthlySalary) {
this.name = name;
this.monthlySalary = monthlySalary;
}
// 计算并返回年工资
public double getAnnualSalary() {
return monthlySalary * 12;
}
}
// 经理类,继承自员工类,表示一个经理的信息
class Manager extends Employee {
private double bonus; // 奖金
// 构造方法,用于初始化经理的姓名、月工资和奖金
public Manager(String name, double monthlySalary, double bonus) {
super(name, monthlySalary);
this.bonus = bonus;
}
// 经理特有的管理方法
public void manage() {
System.out.println("Manager " + getName() + " is managing.");
}
// 重写父类方法,计算并返回年工资(包括奖金)
@Override
public double getAnnualSalary() {
return super.getAnnualSalary() + bonus;
}
}
// 普通员工类,继承自员工类,表示一个普通员工的信息
class RegularEmployee extends Employee {
// 构造方法,用于初始化普通员工的姓名和月工资
public RegularEmployee(String name, double monthlySalary) {
super(name, monthlySalary);
}
// 普通员工特有的工作方法
public void work() {
System.out.println("Regular employee " + getName() + " is working.");
}
}
// 主类,用于测试不同类型的员工
public class EmployeeHierarchy {
public static void main(String[] args) {
Manager manager = new Manager("Alice", 5000, 1000);
RegularEmployee employee = new RegularEmployee("Bob", 3000);
manager.manage();
System.out.println("Manager's Annual Salary: " + manager.getAnnualSalary());
employee.work();
System.out.println("Employee's Annual Salary: " + employee.getAnnualSalary());
}
}