文章目录
🐧Java多态
🐟多态的引出
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;
}
}
public class Dog extends Animal{
public Dog(String name) {
super(name);
}
}
public class Cat extends Animal{
public Cat(String name) {
super(name);
}
}
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 Bone extends Food {
public Bone(String name) {
super(name);
}
}
public class Fish extends Food {
public Fish(String name) {
super(name);
}
}
public class Master {
private String name;
public Master(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());
}
//如果动物很多, 食物很多
//===> feed 方法很多, 不利于管理和维护
//Pig --> Rice
//Tiger --> Meat ...
}
public class Poly01 {
public static void main(String[] args) {
Master tom = new Master("tom");
Dog dog = new Dog("旺财");
Bone bone = new Bone("大棒骨");
tom.feed(dog, bone);
System.out.println("======================");
Cat cat = new Cat("花喵");
Fish fish = new Fish("大黄鱼");
tom.feed(cat, fish);
}
}
🐟方法的多态
〇多态基本介绍
- 方法或对象具有多种形态. 是面向对象的第三大特征, 多态是建立在封装和继承之上的.
〇多态的具体体现
- 方法的多态
PloyMethod.java
- 重写和重载就体现多态
public class polyMethod {
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()方法被调用...");
}
}
🐟对象的多态
(1) 一个对象的编译类型和运行类型可以不一致
(2) 编译类型在定义对象时, 就确定了, 不能改变
(3) 运行类型是可以改变的
(4) 编译类型看定义时 = 号 的左边, 运行类型看 = 号的 右边
Animal animal = new Dog(); [animal 编译类型是Animal, 运行类型是Dog]
animal = new Cat(); [animal 的运行类型变成了Cat, 编译类型仍然是 Animal]
public class Animal {
public void cry() {
System.out.println("Animal cry() Animal在叫...");
}
}
public class Cat extends Animal {
public void cry() {
System.out.println("Cat cry() 小猫喵喵叫...");
}
}
public class Dog extends Animal {
public void cry() {
System.out.println("Dog cry() 小狗汪汪叫...");
}
}
public class PolyObject {
public static void main(String[] args) {
//体验对象多态的特点
//animal编译类型是Animal, 运行类型是Dog
Animal animal = new Dog();
//因为运行时, 这时即执行到该行时, animal的运行类型是Dog, 所以这个cry就是Dog的cry
animal.cry();//小狗汪汪叫...
//animal 编译类型是 Animal, 运行类型是 Cat
animal = new Cat();
animal.cry();//小猫喵喵叫...
}
}
🐟快速入门
改善主人喂食 食物的问题
//使用多态机制, 可以统一地管理主人喂食 食物的问题
//animal 编译类型是Animal, 它可以接收(指向) Animal子类的对象
//food 编译类型是Food, 它可以指向 Food子类的对象
public void feed(Animal animal, Food food) {
System.out.println("主人 " + name + " 给 " + animal.getName() + " 喂 " + food.getName());
}
测试
public class Poly01 {
public static void main(String[] args) {
Master tom = new Master("tom");
//添加给小猪 喂 米饭
System.out.println("======================");
Pig pig = new Pig("佩奇~");
Rice rice = new Rice("米饭~");
tom.feed(pig, rice);
}
}
🐟注意事项和细节
com.zzw.poly_.detail_包: PolyDetail.java
多态的前提
两个对象(类)存在继承关系
多态的向上转型
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 PoyDetail {
public static void main(String[] args) {
//向上转型: 父类的引用指向了之类的对象
//语法: 父类类型 引用名 = new 子类类型();
Animal animal = new Cat();
Object obj = new Cat();//可以吗? 可以 Object 也是 Cat的父类
//向上转型调用方法的规则如下:
//(1)可以调用父类中的所有成员(需要遵守访问权限)
//(2)但是不能调用子类的特有的成员
//(3)因为在编译阶段, 能调用哪些成员, 是由编译类型来决定的
//animal.catchMouse();错误
//(4)最终的运行效果看子类(运行类型)的具体实现
animal.eat();//猫吃鱼... 即调用方法时, 按照从子类(运行类型)开始查找方法
// ,然后调用, 规则和前面我们讲的方法调用规则一致
animal.run();//跑
animal.show();//hello, 你好
animal.sleep();//睡
System.out.println("ok~");
}
}
多态的向下转型
1)语法: 子类类型 引用名 = (子类类型) 父类引用;
2)只能强转父类的引用, 不能强转父类的对象
3)要求父类的引用必须指向的是当前目标类型的对象
4)当向下转型后, 可以调用子类类型的所有成员
public class PoyDetail {
public static void main(String[] args) {
//向上转型: 父类的引用指向了之类的对象
//语法: 父类类型 引用名 = new 子类类型();
Animal animal = new Cat();
//我们希望可以调用Cat的 catchMouse方法
//多态的向下转型
//(1)语法: 子类类型 引用名 = (子类类型) 父类引用;
//cat 编译类型 Cat, 运行类型 Cat
Cat cat = (Cat) animal;
System.out.println("animal=" + animal);//com.zzw.poly_.detail_.Cat@5cad8086
System.out.println("cat=" + cat);//com.zzw.poly_.detail_.Cat@5cad8086
cat.catchMouse();//猫抓老鼠
//(2)要求父类的引用必须指向的是当前目标类型的对象
Dog dog = (Dog) animal;//可以吗?
System.out.println("ok~");
}
}
属性没有重写之说
属性的值看编译类型 PolyDetail02.java
public class PolyDetail02 {
public static void main(String[] args) {
//属性没有重写之说! 属性的值看编译类型
Base base = new Sub();//向上转型
System.out.println(base.count);//? 直接看编译类型 10
Sub sub = new Sub();
System.out.println(sub.count);//? 20
}
}
class Base { //父类
int count = 10;//属性
}
class Sub extends Base { //子类
int count = 20;//属性
}
instanceOf比较操作符
用于判断对象的运行类型是否为XX类型(接口/类)或XX类型的子类型 PolyDetail03.java
public class PolyDetail03 {
public static void main(String[] args) {
BB bb = new BB();
//instanceOf,比较操作符
//用于判断对象的类型是否为XX类型或XX类型的子类型
System.out.println(bb instanceof BB);// true
System.out.println(bb instanceof AA);// true
System.out.println("=======================");
//aa 编译类型 AA, 运行类型是BB
AA aa = new BB();
System.out.println(aa instanceof AA);//true
System.out.println(aa instanceof BB);//true
System.out.println("=======================");
Object obj = new Object();
System.out.println(obj instanceof AA);//false
System.out.println("=======================");
String str = "hello";
//Inconvertible types; cannot cast 'java.lang.String' to 'com.zzw.poly_.detail_.AA'
//System.out.println(str instanceof AA);
System.out.println(str instanceof Object);//true
}
}
class AA {} //父类
class BB extends AA{} //子类
🐟课堂练习
com.zzw.poly_.exercise_包: PolyExercise01.java
public class PolyExercise01 {
public static void main(String[] args) {
double d = 13.4;
long l = (long) d;//ok
System.out.println(l);//13
//Inconvertible types; cannot cast 'int' to 'boolean'
int i = 5;
boolean b = (boolean) i;//不对, boolean -> int是不可以的
Object obj = "Hello";//可以, 向上转型
String objStr = (String) obj;//可以, 向下转型
System.out.println(objStr);//hello
Object objPri = new Integer(5);//可以, 向上转型
//要求父类的引用必须指向的是当前目标类型的对象
String str = (String) objPri;//错误, 指向Integer的父类引用, 转成String
Integer str1 = (Integer) objPri;//可以, 向下转型
}
}
PolyExercise02.java
public class PolyExercise02 {//主类
public static void main(String[] args) {
Sub sub = new Sub();
System.out.println(sub.count);//20
sub.display();//20
Base base = sub;//向上转型
System.out.println(base == sub);//true
System.out.println(base.count);//10
//调用方法时, 从运行类型开始建立查找的关系
base.display();//20
}
}
class Base {//父类
int count = 10;
public void display() {
//属性没有重写一说!属性的值看编译类型
System.out.println(this.count);//10
}
}
class Sub extends Base {//子类
int count = 20;
public void display() {
System.out.println(this.count);//20
}
}
🐟动态绑定机制
Java重要特性: 动态绑定机制 DynamicBinding.java
com.zzw.poly_.dynamic_
public class DynamicBinding {
public static void main(String[] args) {
A a = new B();//向上转型
//a.sum()实际上调用的是运行类型的sum方法, 从运行类型开始建立查找关系
// a的 编译类型 A, 运行类型 B
System.out.println(a.sum());// 取消注销子类的sum方法, 输出40
System.out.println(a.sum());// 注销子类的sum方法后, 输出30
System.out.println(a.sum1());//取消注销子类的sum1方法, 输出30
System.out.println(a.sum1());//注销子类的sum1方法, 输出20
}
}
class A { //父类
public int i = 10;
//动态绑定过机制:
//1.当调用对象方法的时候, 该方法会和该对象的内存地址/运行类型绑定
//2.当调用对象属性时, 没有动态绑定机制, 即哪里声明, 哪里使用
public int sum() {//父类的sum()
//调用子类的getI()
return getI() + 10;// 20+10
}
public int sum1() {
//属性没有重写一说! 属性的值看编译类型
return this.i + 10;// 10+10
}
public int getI() {
return i;//10
}
}
class B extends A {
public int i = 20;
//public int sum() {
// return i + 20;// 20+20
//}
//public int sum1() {
// return i + 10;// 20+10
//}
//getI()方法被重写了
public int getI() {
return i;//20
}
}
🐟多态数组
1)多态数组, com.zzw.poly_.polyarr包
PolyArr,java
数组的定义类型为父类类型, 里面保存的实际元素类型为子类类型
应用实例: 现有一个继承结构如下, 要求创建1个Person对象, 2个Student对象和2个Teacher对象, 统一放在数组中, 并调用每个对象的say方法.
应用实例升级: 如果调用子类特有的方法, 比如Teacher有一个teach方法, Student有一个study方法, 怎么调用?
public class Person {
private String name;
private int age;
public String say() {
return "姓名 " + name + "\t年龄 " + age;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
}
public class Student extends Person {
private double score;
//重写父类的say方法
public String say() {
return super.say() + "\t成绩 " + score;
}
//特有方法
public void study() {
System.out.println("学生 " + getName() + " 正在学Java课程...");
}
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
public double getScore() {
return score;
}
}
public class Teacher extends Person {
private double salary;
//重写父类的say方法
public String say() {
return super.say() + "\t薪水 " + salary;
}
//特有方法
public void teach() {
System.out.println("老师 " + getName() + " 正在讲Java课程...");
}
public Teacher(String name, int age, double salary) {
super(name, age);
this.salary = salary;
}
public double getSalary() {
return salary;
}
}
public class PolyArr {
public static void main(String[] args) {
//现有一个继承结构如下, 要求创建1个Person对象,2个Student对象2个Teacher对象,
//统一放在数组中, 并调用每个对象的say方法.
Person[] persons = new Person[5];
persons[0] = new Person("赵志伟", 23);
persons[1] = new Student("赵培竹", 22, 98);
persons[2] = new Student("mary", 21, 88);
persons[3] = new Teacher("jack", 22, 15000);
persons[4] = new Teacher("scott", 20, 17000);
//循环遍历多态数组, 调用say方法
for (int i = 0; i < persons.length; i++) {
//提示: person[i] 编译类型是 Person, 运行类型是根据实际情况由JVM机来判断
System.out.println(persons[i].say());//动态绑定机制
//不能调用子类的特有的成员
//因为在编译阶段, 能调用哪些成员, 是由编译类型来决定的
if (persons[i] instanceof Student) {//判断person[i] 的运行类型是不是Student
Student student = (Student) persons[i];
student.study();//向下转型
} else if (persons[i] instanceof Teacher) {
Teacher teacher = (Teacher) persons[i];
teacher.teach();
} else if (persons[i] instanceof Person) {
} else {
System.out.println("你的类型有误, 请自己检查...");
}
}
}
}
🐟多态参数
方法定义的形参类型为父类类型, 实参类型允许为子类类型
应用实例1: 前面的主人喂动物
应用实例2: com.zzw.poly_.polyparameter_包
PolyParameter.java
定义员工类Employee, 包含姓名和月工资[private], 以及计算年工资getAnnual的方法. 普通工人和经理继承了员工, 经理类多了奖金bonus属性和管理manage方法, 普通工人类多了work方法, 普通工人和经理类要求分别重写getAnnual方法. 如果没说什么字段,默认private
测试类中添加一个方法showEmpAnnual(Employee e), 实现获取任何员工对象的年工资, 并在main方法中调用该方法 [e.getAnnual()]
测试类中添加一个方法testWork(). 如果是普通员工, 则调用work方法; 如果是经理, 则调用manage方法
public class Employee {
private String name;//姓名
private double salary;//月工资
//计算年工资
public Double getAnnual() {
return 12 * salary;
}
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
}
public class Worker extends Employee {
public Worker(String name, double salary) {
super(name, salary);
}
public void work() {
System.out.println("普通员工 " + getName() + " 工作中...");
}
public Double getAnnual() {//因为普通员工没有其它的收入, 则直接调用父类的方法即可
return super.getAnnual();
}
}
public class Manager extends Employee {
private double bonus;//奖金
public Manager(String name, double salary, double bonus) {
super(name, salary);
this.bonus = bonus;
}
public void manage() {
System.out.println("经理 " + getName() + " 在管理工作中...");
}
//重写获取年薪的方法
public Double getAnnual() {
return super.getAnnual() + bonus;
}
}
public class PolyParameter {
public static void main(String[] args) {
Worker tom = new Worker("tom", 3000);
Manager mary = new Manager("mary", 5000, 200000);
PolyParameter polyParameter = new PolyParameter();
polyParameter.showEmpAnnual(tom);
polyParameter.showEmpAnnual(mary);//动态绑定机制
polyParameter.testWork(tom);
polyParameter.testWork(mary);
}
public void showEmpAnnual(Employee e) {
System.out.println(e.getAnnual());
}
public void testWork(Employee e) {
if (e instanceof Worker) {
Worker worker = (Worker) e;//向下转型
worker.work();
} else if (e instanceof Manager) {
Manager manager = (Manager) e;//向下转型
manager.manage();
} else {
System.out.println("你的类型有误...");
}
}
}
🐧Object类详解
🐟"=="运算符
- 判断引用类型
“==”运算符比较两个引用类型时用来判断它们的地址是否相等,即判断它们是否指向同一个对象。
String str1 = new String("eee");
String str2 = new String("eee");
//同一个对象
System.out.println(str1 == str2);//false
A a = new A();
A b = a;
A c = b;
//指向的是同一个对象
System.out.println(a == c);//true
B b = a;//class B extends A(){}
System.out.println(b == a);//true
- 判断基本数据类型
"=="运算符比较的是两个基本数据类型值是否相等
int i = 10;
double d = 10.0;
System.out.println(i == d);//true
🐟equals方法
==和equals的对比 com.zzw.object_:
Equals01.java
== 是一个比较运算符
- ==: 既可以判断基本类型, 又可以判断引用类型
- ==: 如果判断基本类型, 判断的是值是否相等.
- ==: 如果判断引用类型, 判断的是地址是否相等, 即判断是不是同一个对象
equals是Object类中的方法,只能判断引用类型。
默认比较两个对象是否是同一个对象
public boolean equals(Object obj) {
return (this == obj);
}
- String重写equals()
String作为Object的子类,重写了父类的equals方法,用于判断内容是否相等。
public boolean equals(Object anObject) {
//首先判断是否是同一个对象
if (this == anObject) {
return true;
}
//如果传进来的参数是字符串,判断内容是否相等
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
//既不是同一个对象,参数又不是字符串
return false;
}
运行实例
String str1 = new String("eee");
String str2 = new String("eee");
String str3 = new String("eee333");
Person person1 = new Person("zzw", 22);
//Object类中的equals方法只能判断两个对象的地址是否相等(引用类型)
//String、Integer作为Object的子类,重写了equals方法,用于判断内容是否相等
System.out.println("如果是同一个对象,返回true");
System.out.println(str1.equals(str1));
System.out.println(person1.equals(person1));
System.out.println("如果传进来的是字符串,值相等,返回true");
System.out.println(str1.equals(str2));
System.out.println("如果传进来的是字符串,值不相等,返回false");
System.out.println(str2.equals(str3));
System.out.println("如果既不是同一个对象,传进来的又不是一个字符串,返回false");
System.out.println(str1.equals(person1));
- Integer重写equals()
Integer作为Object的子类,重写了父类的equals方法,integer.equals()比较的是值是否相等
public boolean equals(Object obj) {
//如果传进来的参数是Integer类型,则比较值是否相等
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
//如果传进来的参数不是Integer类型,则返回false
return false;
}
运行实例
System.out.println("integer.equals()比较的是值");
Integer integer1 = new Integer(1000);
Integer integer2 = new Integer(1000);
int n1 = 1000;
System.out.println(integer1.equals(integer2));//true
System.out.println(integer1.equals(n1));//true
- 图解
名称 | 概念 | 用于基本数据类型 | 用于引用类型 |
---|---|---|---|
== | 比较运算符 | 可以,判断值是否相等 | 可以,判断指向的是否是同一个对象 |
equals | Object类的方法 | 不可以 | 可以,默认判断两个对象是否是同一个对象。 |
- 自己重写equals方法
目标:用于判断两个Person对象的内容是否相等
思路:从Object类中copy一份过来
public boolean equals(Object obj) {
//默认是比较的两个对象的地址
return (this == obj);
}
重写后的equals方法
//子类重写了Object的方法
@Override
public boolean equals(Object obj) {
//this是调用这个方法的对象
if(this == obj) {
return true;
}
-------------------------------------------------------------------------------------------
/*写法一*/
//如果obj的运行类型是Person
if(obj instanceof Person) {
//向下转型
Person p = (Person) obj;
//两层含义:1.如果内容相等,返回true;2.如果内容不相等,返回false
return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;
}
//如果不是Person类型的对象
return false;
-------------------------------------------------------------------------------------------
/*写法二:过关斩将法*/
if(!(obj instanceof Doctor)) {
return false;
}
Doctor doctor = (Doctor)obj;
return doctor.age == this.age && doctor.gender == this.gender &&
doctor.job.equals(this.job) && doctor.name.equals(this.name) &&
doctor.salary == this.salary;
}
主类:
public class EqualsExercise01 {
public static void main(String[] args) {
Person person1 = new Person("zzw", 22, '男');
Person person2 = new Person("zzw", 22, '男');
System.out.println(person1.equals(person2));
}
}
Person类
class Person {
private String name;
private int age;
private char gender;
public Person(String name, int age, char gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
@Override
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
if(obj instanceof Person) {
Person p = (Person) obj;
return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;
}
return false;
}
}
- 课堂练习
public class EqualsExercise02 {
public static void main(String[] args) {
Person1 person1 = new Person1();
Person1 person2 = new Person1();
person1.name = "zzw";
person2.name = "zzw";
System.out.println(person1 == person2);//不是同一个对象,返回false
System.out.println(person1.name.equals(person2.name));//String已经重写了equals方法,返回true
System.out.println(person1.equals(person2));//Person没有重写equals方法,此时还是比较地址是否相同
}
}
class Person1 {
public String name;
}
🐟hashCode()方法
public class HashCode_ {
public static void main(String[] args) {
AA aa = new AA();
AA aa1 = new AA();
AA aa2 = new AA();
aa2 = aa;
System.out.println("aa,aa2指向同一个对象");
System.out.println("aa.hashCode()=" +aa.hashCode());
System.out.println("aa2.hashCode()=" + aa2.hashCode());
System.out.println("aa1指向不同对象");
System.out.println("aa1.hashCode()=" + aa1.hashCode());
}
}
class AA { }
运行结果
🐟toString()方法
源码:
public String toString() { return getClass().getName() + "@" + >Integer.toHexString(hashCode()); }
(1)获取类的全类名(包名+类名)
(2)将对象的hashCode值转为十六进制
- 重写toString()方法
1.alt + insert -> toString键
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
2.调用toString(), 直接输出类名
public static void main(String[] args) {
Monster monster = new Monster("小钻风", "巡山的", 20000);
System.out.println(monster);//默认等于monster.toString();
}
🐟finalize()方法
Car car = new Car(); car = null; //垃圾回收机制的调用,由系统决定 System.gc();
1.在销毁对象前,会调用该对象的finalize()方法,可以写一些业务逻辑代码在该方法中,比如释放资源:数据库连接、或者是打开的文件;
2.如果不重写finalize()这个方法,则会默认调用Object的finalize()方法;
class Car {
private String name;
@Override
protected void finalize() throws Throwable {
System.out.println("我们销毁这个对象:" + name);
System.out.println("释放了资源………");
}
}
🐧断点调试-breakpoint
1.F7,alt+shift+F7
跳入-Step Into【默认不允许跳入class中】
强制进入-Force Step Into
2.F8,
跳过-Step Over:[一行语句一行语句的执行]
3.Shift+F8,
跳出-Step Out
4.F9,
Resume,执行到下一个断点
动态下断点,既可以在自己写的代码中设置断点,也可以在源码中设置断点
配置:settings->Build,Execution,Deployment->Debugger->Stepping->取消勾选java., javax.