重写(覆盖)
子类中有一个方法,与父类中的一个方法,名称、返回类型、参数都一样
PS:1.子类返回类型若是父类返回类型的子类,也定义为重写。如String是Object的子类
2.子类方法不能缩小父类的访问权限(public -> protected ->默认 -> private),可以扩大
重载就是多个方法同名,但形参不同。
重写为子类与父类方法相同
实例
1.编写一个person类,包含私有属性name,age,构造器,方法say
2.编写一个student类,继承person,增加私有属性,id,score,构造器,定义方法say
3.main方法中分别创建Person和Student对象,调用say方法
Person类
package com.practice;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String say(){
return name + age;
}
}
Student类
package com.practice;
public class Student extends Person{
private int id;
private double score;
public Student() {
}
public Student(String name, int age, int id, double score) {
super(name, age);
this.id = id;
this.score = score;
}
public String say(){
return super.say() + id + score;
}
}
main方法
package com.practice;
public class test {
public static void main(String[] args) {
Person test1 = new Person("jack",10);
Student test2 = new Student("sad",18,999,15);
System.out.println(test1.say());
System.out.println(test2.say());
}
}
输出结果
多态
- 多态是同一个行为具有不同的表现形式或形态的能力
- 同一方法可以根据发送对象的不同而采用不同的行为方式
- 方法或对象具有多种形态,多态是建立在封装和继承之上的
前提:两个对象(类)存在继承关系
特点
- 多态成员变量:编译运行看左边
- 多态成员方法:编译看左边,运行看右边
public class test { public static void main(String[] args) { Animal one = new Dog(); System.out.println(one.age); one.eat(); } } class Animal { int age = 10; public void eat() { System.out.println("AAA"); } } class Dog extends Animal { int age = 20; public void eat(){ System.out.println("BBB"); } }
输出结果
方法体现多态
public class A {
public void say(){
System.out.println("A中的say()被调用");
}
}
public class B extends A{
//重载
public int getSum(int n1,int n2){
return n1+n2;
}
public int getSum(int n1,int n2,int n3){
return n1+n2+n3;
}
//重写
@Override
public void say() {
System.out.println("B中的say()");
}
}
package com.practice;
public class test {
public static void main(String[] args) {
//方法重载体现多态
B b = new B();
//传入不同的参数,就会调用不同的sum方法,就体现了多态
b.getSum(1,2);
b.getSum(1,2,3);
//方法重写体现多态
A a = new A();
a.say();
b.say();
}
}
对象多态(核心)
1.一个对象的编译类型和运行类型可以不一致
父类引用可以指向子类对象
2.编译类型定义对象时就确定,不可改变
3.运行类型可以变化
4.等号左边编译类型,右边运行类型
Animal animal= new Dog(); // 编译类型是Animal,运行类型Dog
animal = new Cat(); // Animal Cat
package com.practice;
public class Animal {
public void cry(){
System.out.println("哭");
}
}
public class Dog extends Animal {
public void cry() {
System.out.println("狗");
}
}
public class Cat extends Animal{
@Override
public void cry() {
System.out.println("猫");
}
}
public class test {
public static void main(String[] args) {
//对象多态
//编译类型已经确定 Animal ,对象类型Dog
Animal animal = new Dog();
animal.cry();
//运行类型可变
//编译类型不可变 Animal,运行类型 Cat
animal = new Cat();
animal.cry();
}
}
例如,存在父类动物,子类猫狗,父类食物,子类骨头,鱼
有一个主人类,存在方法feed(),可以只写一个方法
public void feed(Animal animal,Food food){
}
Animal animal1 = new Dog();
Food food1 = new Bone();
Animal animal2 = new Cat();
Food food2 = new Fish();
向上转型
1.本质:父类的引用指向子类的对象
Animal animal = new Dog();
2.可以调用父类中的所有成员(遵循访问权限),但子类特有成员不可调用
能调用哪些成员,是由编译类型决定的
3.调用方法时,从子类开始查找方法
向下转型
1.子类类型 引用名 = (子类类型) 父类引用;
Dog dog = (Dog) animal;
只能强转父类的引用,不能强转父类的对象
2.父类的引用必须指向的是当前目标类型的对象,比如当前例子,animal不能强转为Cat
3.向下转型后可调用子类特有成员,因为此时,编译类型Dog,运行类型Dog(向下转型就是为了调用特有方法)
注意
1.属性不能重写,直接调用属性时,值看编译类型(与向上转型第二点,调用成员相同)
方法看运行类型
2.instanceof比较操作符,用于判断对象的类型是否为XX类型或XX类型的子类型
public class test1 {
public static void main(String[] args) {
BBB LLL =new BBB();
System.out.println( LLL instanceof AAA);
System.out.println( LLL instanceof BBB);
System.out.println( LLL instanceof CCC);
}
}
class AAA{}
class BBB extends AAA{}
class CCC extends BBB{}
输出结果
JAVA的动态绑定机制(核心)
1.调用对象方法时,该方法会和该对象的,内存地址/对象类型,绑定
2.调用对象属性时,看编译类型,没有动态绑定机制,哪里声明哪里使用
韩顺平P358
例题,思考二
应用
1.多态数组
数组定义类型为父类类型,保存的实际元素类型为子类类型
主要体系,测试类定义数组
Person类
package com.practice;
public class Person {
private String name;
private int age;
public Person() {
}
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 + "\t" + age;
}
}
student类
package com.practice;
public class Student extends Person {
private double score;
public Student() {
}
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;
}
public String say() {
return super.say() + "\t" + score;
}
public void study(){
System.out.println("study调用");
}
}
Teacher类
package com.practice;
public class Teacher extends Person{
private double Salary;
public Teacher() {
}
public Teacher(String name, int age, double salary) {
super(name, age);
Salary = salary;
}
public double getSalary() {
return Salary;
}
public void setSalary(double salary) {
Salary = salary;
}
public String say() {
return super.say() + "\t" + Salary;
}
public void teach(){
System.out.println("teach调用");
}
}
测试类
package com.practice;
public class PloyArray {
public static void main(String[] args) {
Person [] persons = new Person[5];
persons[0] = new Person("王",24);
persons[1] = new Student("温",23,100);
persons[2] = new Student("宜",22,80);
persons[3] = new Teacher("柴",21,5000);
persons[4] = new Teacher("姬",20,3000);
//循环遍历多态数组,调用say()
for (int i = 0; i < persons.length; i++) {
System.out.println(persons[i].say());
}
}
}
输出结果
第二问代码测试类代码
package com.practice;
public class PloyArray {
public static void main(String[] args) {
Person [] persons = new Person[5];
persons[0] = new Person("王",24);
persons[1] = new Student("温",23,100);
persons[2] = new Student("宜",22,80);
persons[3] = new Teacher("柴",21,5000);
persons[4] = new Teacher("姬",20,3000);
//循环遍历多态数组,调用say()
for (int i = 0; i < persons.length; i++) {
System.out.println(persons[i].say());
if(persons[i] instanceof Student){
Student student = (Student) persons[i]; //向下转型
student.study();
}
if(persons[i] instanceof Teacher){
Teacher teacher = (Teacher) persons[i];
teacher.teach();
}
}
}
}
输出结果
2.多态参数
方法定义的形参类型为父类类型,实参类型允许为子类类型
和上面的,喂养动物实例相同,就不贴详细代码了
Dog dog = new Dog();
Bone bone = new Bone();
Cat cat = new Cat();
Fish fish = new Fish();
public void FeedAnimal(Animal animal,Food food){
}
思考
一
输出结果
1.若Base的display方法改为private,b.display()报错
原因:直接调用属性看编译类型,方法看运行类型,b的编译类型为Base,运行类型为Sub,故运行的是Sub中的display(),
2.我个人还存在一个问题:当Base的display方法改为private,报错理由是Base的display是私有,但应该用的是Sub中的display,原因是,若在Sub中display被重写了,那么通过子类实例向上转型得到的父类引用调用 display
方法时,将根据运行时类型调用相应的重写版本。
二
直接调用属性时,才看编译类型,此题调用的a.sum()是方法,故用的是B的i值
答案:40,30
三
输出结果
我是A类
我是B类的有参构造
我是C类的有参构造
我是C类的无参构造
getClass()
Object中的一个方法,用于查看运行类型