前言
哈喽!大家好,我是小简。今天开始学习《Java-面向对象》,此系列是我做的一个 “Java 从 0 到 1 ” 实验,给自己一年左右时间,按照我自己总结的 Java-学习路线,从 0 开始学 Java 知识,并不定期更新所学笔记,期待一年后的蜕变吧!<有同样想法的小伙伴,可以联系我一起交流学习哦!>
面向对象-基础
类与对象
成员方法
方法递归
方法重载
可变参数
作用域
构造方法
对象创建
this关键字
面向对象-中级
包
访问修饰符
封装
继承
super关键字
重写&重载
多态
多态引出
请编写一个程序,Master 类中有一个 feed 方法, 可以完成主人给动物喂食物的信息。
使用传统的方法来解决带来的问题是什么? 如何解决?
代码语言:javascript复制public class Poly_ {
public static void main(String[] args) {
Master tom = new Master("Tom");
Dog jack = new Dog("jack");
Cat jerry = new Cat("jerry");
Bone bone = new Bone("大骨头");
Fish fish = new Fish("小鱼");
tom.feed(jack,bone);
tom.feed(jerry,fish);
}
}
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());
}
}
class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Dog extends Animal {
public Dog(String name) {
super(name);
}
}
class Cat extends Animal {
public Cat(String name) {
super(name);
}
}
class Food {
private String name;
public Food(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Bone extends Food {
public Bone(String name) {
super(name);
}
}
class Fish extends Food {
public Fish(String name) {
super(name);
}
}
带来的问题:
- 如果动物很多,食物很多,feed 方法很多,不利于管理和维护
- 代码的复用性不高,而且不利于代码维护
解决方案: 采用多态解决
代码语言:javascript复制public class Poly_ {
public static void main(String[] args) {
Master tom = new Master("Tom");
Dog jack = new Dog("jack");
Cat jerry = new Cat("jerry");
Bone bone = new Bone("大骨头");
Fish fish = new Fish("小鱼");
tom.feed(jack,bone);
tom.feed(jerry,fish);
}
}
class Master {
private String name;
public Master(String name) {
this.name = name;
}
//使用多态机制,可以统一的管理主人喂食的问题
//animal 编译类型是Animal,可以指向(接收) Animal 子类的对象
//food 编译类型是Food ,可以指向(接收) Food 子类的对象
public void feed(Animal animal, Food food) {
System.out.println("主人" name " 给" animal.getName() " 吃" food.getName());
}
}
class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Dog extends Animal {
public Dog(String name) {
super(name);
}
}
class Cat extends Animal {
public Cat(String name) {
super(name);
}
}
class Food {
private String name;
public Food(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Bone extends Food {
public Bone(String name) {
super(name);
}
}
class Fish extends Food {
public Fish(String name) {
super(name);
}
}
多态介绍
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
重载和重写都体现了多态
一个对象的编译类型和运行类型可以不一致
编译类型在定义对象时,就确定了,不能改变
运行类型是可以变化的
编译类型看定义时=号的左边,运行类型看=号的右边
代码语言:javascript复制Animal animal = new Dog();//animal编译类型是Animal,运行类型Dog
animal = new Cat(); //animal的运行类型变成了Cat, 编译类型仍然是Animal
多态的前提是:两个对象(类)存在继承关系
多态的向上转型
1)本质:父类的引用指向了子类的对象
2)语法:父类类型 引用名 = new 子类类型();
代码语言:javascript复制Animal animal = new Dog();
3)特点:编译类型看左边,运行类型看右边
- 可以调用父类中的所有成员(需遵守访问权限)
- 不能调用子类中特有成员(属性/方法)
- 最终运行效果看子类的具体实现
public class PolyDetail {
public static void main(String[] args) {
//向上转型: 父类的引用指向了子类的对象
Animal_ animal = new Cat_();
//可以调用父类中的所有成员(需遵守访问权限)
animal.eat();//吃
animal.sleep();//睡
//不能调用子类中特有成员(属性/方法)
animal.catchMouse();//错误写法
}
}
class Animal_ {
String name = "动物";
int age = 10;
public void sleep(){
System.out.println("睡");
}
public void eat(){
System.out.println("吃");
}
}
class Cat_ extends Animal_ {
@Override
public void eat(){//方法重写
System.out.println("猫吃鱼");
}
public void catchMouse(){//Cat 特有方法
System.out.println("猫抓老鼠");
}
}
多态的向下转型
1)语法:子类类型引用名 = (子类类型) 父类引用;
代码语言:javascript复制Dog dog = (Dog) animal;
2)只能强转父类的引用,不能强转父类的对象
3)要求父类的引用必须指向的是当前目标类型的对象
4)当向下转型后,可以调用子类类型中所有的成员
代码语言:javascript复制public class PolyDetail {
public static void main(String[] args) {
Animal_ animal = new Cat_();
//向下转型:希望可以调用Cat的catchMouse 方法
Cat_ cat = (Cat_) anima;
cat.eat();//吃
cat.sleep();//睡
cat.catchMouse();//特有方法也可调用
//要求父类的引用必须指向的是当前目标类型的对象
Dog dog = (Dog) animal; //错误写法
}
}
class Animal_ {
String name = "动物";
int age = 10;
public void sleep(){
System.out.println("睡");
}
public void eat(){
System.out.println("吃");
}
}
class Cat_ extends Animal_ {
@Override
public void eat(){//方法重写
System.out.println("猫吃鱼");
}
public void catchMouse(){//Cat 特有方法
System.out.println("猫抓老鼠");
}
}
class Dog_ extends Animal_ {//Dog 是Animal 的子类
}
属性重写问题
属性没有重写之说!属性的值看编译类型
代码语言:javascript复制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 类型的子类型
代码语言:javascript复制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(); //aa 编译类型AA, 运行类型是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 Object);//true
}
}
class AA {} //父类
class BB extends AA {}//子类
动态绑定机制
- 当调用对象方法的时候,该方法会和该对象的运行类型(内存地址)绑定
- 当调用对象属性时,没有动态绑定机制,哪里声明,那里使用
public class DynamicBinding {
public static void main(String[] args) {
//a 的编译类型A, 运行类型B
A a = new B();//向上转型
//运行类型是B,先找B类里是否有sum,有就直接运行
System.out.println(a.sum());//40 调用子类sum
System.out.println(a.sum1());//30 调用子类sum1
}
}
class A {//父类
public int i = 10;
public int sum() {//父类sum()
return getI() 10;
}
public int sum1() {//父类sum1()
return i 10;
}
public int getI() {//父类getI
return i;
}
}
class B extends A {//子类
public int i = 20;
@Override
public int sum() {
return i 20;
}
@Override
public int getI() {//子类getI()
return i;
}
@Override
public int sum1() {
return i 10;
}
}
代码语言:javascript复制public class DynamicBinding {
public static void main(String[] args) {
//a 的编译类型A, 运行类型B
A a = new B();//向上转型
//运行类型是B,先找B类里是否有sum,没有就去父类找
System.out.println(a.sum());//30 调用父类sum 和子类getI
System.out.println(a.sum1());//20 调用父类sum 和父类i
}
}
class A {//父类
public int i = 10;
//动态绑定机制
public int sum() {//父类sum()
return getI() 10;//20 10
}
public int sum1() {//父类sum1()
return i 10;//10 10
}
public int getI() {//父类getI
return i;
}
}
class B extends A {//子类
public int i = 20;
@Override
public int getI() {//子类getI()
return i;
}
}
多态的应用
多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
应用实例:现有一个继承结构如下:要求创建1 个Person 对象、2 个Student 对象和2 个Teacher 对象, 统一放在数组中,并调用每个对象 say 方法,如何调用子类特有的方法,比如 Teacher 有一个 teach , Student 有一个 study
怎么调用?
代码语言:javascript复制public class PolyArray {
public static void main(String[] args) {
//应用实例:现有一个继承结构如下:要求创建1 个Person 对象、
// 2 个Student 对象和2 个Teacher 对象, 统一放在数组中,并调用每个对象say 方法
Person[] persons = new Person[5];
persons[0] = new Person("jack", 20);
persons[1] = new Student("mary", 18, 100);
persons[2] = new Student("smith", 19, 30.1);
persons[3] = new Teacher("scott", 30, 20000);
persons[4] = new Teacher("king", 50, 25000);
//循环遍历多态数组,调用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();
//也可以使用一条语句((Student)persons[i]).study();
} else if(persons[i] instanceof Teacher) {
Teacher teacher = (Teacher)persons[i];
teacher.teach();
} else if(persons[i] instanceof Person){
System.out.println("不做处理...");
} else {
System.out.println("你的类型有误, 请自己检查...");
}
}
}
}
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 String say() {//返回名字和年龄
return name "t" age;
}
}
class Student extends Person {
private double score;
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
//重写父类say
@Override
public String say() {
return "学生" super.say() " score=" score;
}
//特有的方法
public void study() {
System.out.println("学生" getName() " 正在学java...");
}
}
class Teacher extends Person {
private double salary;
public Teacher(String name, int age, double salary) {
super(name, age);
this.salary = salary;
}
//写重写父类的say 方法
@Override
public String say() {
return "老师" super.say() " salary=" salary;
}
//特有方法
public void teach() {
System.out.println("老师" getName() " 正在讲java 课程...");
}
}
多态参数
方法定义的形参类型为父类类型,实参类型允许为子类类型
应用实例: 定义员工类 Employee,包含姓名和月工资[private],以及计算年工资 getAnnual 的方法。普通员工和经理继承了员工,经理类多了奖金 bonus 属性和管理 manage 方法,普通员工类多了 work 方法,普通员工和经理类要求分别重写 getAnnual 方法,测试类中添加一个方法 showEmpAnnual(Employee e),实现获取任何员工对象的年工资,并在 main 方法中调用该方法[e.getAnnual()] 测试类中添加一个方法,testWork,如果是普通员工, 则调用 work 方法,如果是经 理,则调用 manage方法
代码语言:javascript复制public class PolyParameter {
public static void main(String[] args) {
Worker tom = new Worker("tom", 2500);
Manager milan = new Manager("milan", 5000, 200000);
PolyParameter.showEmpAnnual(tom);
PolyParameter.showEmpAnnual(milan);
PolyParameter.testWork(tom);
PolyParameter.testWork(milan);
}
public static void showEmpAnnual(Employee e) {
System.out.println(e.getAnnual());
}
public static void testWork(Employee e) {
if (e instanceof Worker) {
((Worker) e).work();//有向下转型操作
} else if (e instanceof Manager) {
((Manager) e).manage();//有向下转型操作
} else {
System.out.println("不做处理...");
}
}
}
class Employee {
private String name;
private double salary;
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public double getAnnual() {
return 12 * salary;
}
}
class Worker extends Employee {
public Worker(String name, double salary) {
super(name, salary);
}
public void work() {
System.out.println("普通员工" getName() " is working");
}
@Override
public double getAnnual() {
return super.getAnnual();
}
}
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() " is managing");
}
@Override
public double getAnnual() {
return super.getAnnual() bonus;
}
}
Object类
equals
equals 是 Object 类中的方法,只能判断引用类型,默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。比如 Integer,String
== 和 equals 的对比
- == 是一个比较运算符
- 1.既可以判断基本类型,又可以判断引用类型
- 2.如果判断基本类型,判断的是值是否相等。
- 3.如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象
- equals 是 Object 类中的方法,只能判断引用类型,默认判断的是地址是否相等
如何重写 equals 方法