目录
1. 继承的应用场景
当类与类之间有共同的属性时,就可以把这些属性拎出来放到另外一个类中。
2. 继承的格式
public class 子类 extends 父类{ }
3.继承的特点
- Java只支持单继承:一个类只能继承一个父类。(类比生活中一个孩子只能有一个爸爸)
- Java不支持多继承,但是支持多层继承:父类还可以继承其父类。(类比生活中我的爸爸也有爸爸,但Java里面不叫做“爷爷”,而叫做“间接父类”)
- Java中所有类的最顶层类都是Object类,即所有类都继承Object类。
- 子类只能继承父类非私有的成员。
4.子类究竟能继承父类的什么东西?
从3个方面来分析:
- 构造方法:不管是私有的还是非私有的,都不能被继承。(构造方法与类名一致性)
- 成员变量:不管是私有的还是非私有的,都能被继承。(私有的不能用)
- 成员方法:只有在虚方法表里的,才能被继承。
ps:什么是虚方法表?
非private、非static、非final
5. 继承中:成员变量的访问特点
重名变量,采用就近原则。
【说明】
name:直接从局部中找;
this.name:从当前类的成员变量开始找;
super.name:从父类开始找。
ps:当然,如果没有局部变量的name和类中的name,只有父类中有name的话,也是可以通过层层查找找到的。
6.重写
应用场景:当父类提供的满足不了子类的需求时,子类自己重写适合自己的方法。
注意事项:①重写的方法格式和父类一致;②只有被添加到虚方法表中的方法才能被重写。
(忘记虚方法表的盆友请看第4点的ps)
7. 多态
(1)多态的定义:对象的多种形态。(类比一头母猪生出各种颜色的小花猪,各有各的形态,但共同点是猪【继承】,特点是花色各异【方法重写】)
(2)多态的前提
- 有继承关系
- 有父类引用指向子类对象
- 有方法的重写
举例:设计一个信息系统,用来存取学生和老师的信息。
1. 抓取学生和老师的共同特性——人,创建Person作为其父类
public class Person {
String name;
int tel;
public Person() {
}
public Person(String name, int tel) {
this.name = name;
this.tel = tel;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getTel() {
return tel;
}
public void setTel(int tel) {
this.tel = tel;
}
public void show(){
}
}
2. 创建子类Student、Teacher,继承Person类,注意要进行方法的重写。
public class Student extends Person{
@Override
//必须要有方法的重写
public void show() {
super.show();
System.out.println("学生的信息:"+ getName() +","+getTel());
}
}
public class Teacher extends Person{
@Override
//必须要有方法的重写
public void show() {
super.show();
System.out.println("老师的信息:"+ getName() +","+getTel());
}
}
3. 测试类种创建学生实例和老师实例,创建函数message()用来接收Student和Teacher的信息,必须使用它们的父类作为参数。(别忘了调用函数哦)
public class Test {
public static void main(String[] args) {
Student s = new Student();
Teacher t = new Teacher();
s.setName("学生");
s.setTel(111);
t.setName("老师");
t.setTel(222);
//调用函数,实现多态
message(s);
message(t);
}
//这个方法既能接收学生,也能接收老师
//只需把参数写成它们的父类Person即可
public static void message(Person p){
p.show();
}
}
运行结果:
(3)多态的好处
使用父类作为参数,可以接收所有子类对象,代码复用性高。
(4)多态调用成员的规则
形如:Animal a = new Dog(); //多态
①调用成员变量:看父类。父类有这个成员变量则编译成功,否则失败。
②调用成员方法:看子类。子类对父类的成员方法进行重写,将父类覆盖,故调用的是子类。
例:Animal 和 Dog 的继承与多态关系:
class Animal{
String type;
/*此处省略构造方法*/
public void eat(){
System.out.println("动物会吃饭");
}
}
class Dog extends Animal{
public void eat{
System.out.println("小狗会吃饭");
}
}
public class Test{
public void static main(){
Animal a = new Dog(); //多态
//多态调用成员变量
system.out.println(a.name); //检索父类是否有该成员变量,若有则正常编译;若无,编译失败
//多态调用成员方法
a.eat(); //若子类有重写,子类覆盖父类的成员方法,故调用的是子类的成员方法
}
}
结果:编译成功(父类有name成员变量);返回结果是“小狗会吃饭”(方法重写)。
结论:若一个方法中的参数是一个类名,那我们就可以传入这个类的所有子类。
(5)多态的综合练习
代码如下:
public class Animal {
int age;
String color;
public Animal() {
}
public Animal(int age, String color) {
this.age = age;
this.color = color;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public void eat(String something){
}
}
//狗继承动物
public class Dog extends Animal{
public Dog() {
}
public Dog(int age, String color) {
super(age, color);
}
public void eat(String something){
System.out.println(getAge()+"岁的"+getColor()+"的狗用两只前腿保住"+something+"一顿猛吃");
}
public void lookHome(){
System.out.println("狗在看家");
}
}
//猫继承动物
public class Cat extends Animal{
public Cat() {
}
public Cat(int age, String color) {
super(age, color);
}
@Override
public void eat(String something) {
System.out.println(getAge()+"岁的"+getColor()+"猫正趴在地上歪着头吃"+something);
}
void catchMouse(){
System.out.println("猫在抓老鼠");
}
}
//饲养员单独开来
public class Person {
String name;
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 void keepPet(Dog dog,String something){
System.out.println(age +"岁的" + name +"在喂狗");
dog.eat(something);
}
public void keepPet(Cat cat,String something){
System.out.println(age +"岁的" + name +"在喂猫");
cat.eat(something);
}
}
//测试类
public class Test {
public static void main(String[] args) {
Person wang = new Person("老王",40);
Dog dog = new Dog(2,"黑色");
wang.keepPet(dog,"骨头");
Person li = new Person("老李",45);
Cat cat = new Cat(3,"黄色");
li.keepPet(cat,"鱼刺");
}
}
利用多态改写代码:
Person中keepPet(),传入的参数是父类Animal类型,子类都可以传参进来。
public void keepPet(Animal a,String something){
//强转
if(a instanceof Dog){ //如果传入的a是狗类型,将其强转为Dog,实例是d
Dog d = (Dog) a;
System.out.println(age +"岁的" + name +"在喂狗");
d.eat(something);
}else if(a instanceof Cat){ //如果传入的a是猫类型,将其强转为Cat,实例是c
Cat c = (Cat) a;
System.out.println(age +"岁的" + name +"在喂猫");
c.eat(something);
}else {
System.out.println("没有这种动物");
}
}
在测试类中:
public class Test {
public static void main(String[] args) {
Person wang = new Person("老王",40);
Animal dog = new Dog(2,"黑色");
//第一个参数是Animal,当方法的参数是一个类时,可以传入它的所有子类
//这里就可以传入Animal的子类:Dog和Cat ,注意:传入的是实例dog而非类型Dog
wang.keepPet(dog,"骨头");
Person li = new Person("老李",45);
Animal cat = new Cat(3,"黄色");
li.keepPet(cat,"鱼刺");
}
}
运行结果:
注意:
该代码在Java 16以上版本中使用了instanceof模式匹配操作符,但在低版本的Java中这不可用,因此会导致编译错误。
Java 16以上版本:
if(a instanceof Dog d){
Java 16以下版本:
if(a instanceof Dog){
Dog d = (Dog) a;
}
8. 抽象类和抽象方法
- 抽象方法的定义格式:
public abstract void eat(); //注意:没有方法体!
- 抽象类的定义格式:
public abstract class dog{ }
例: 青蛙、狗、山羊
【思路】方法中共性但又独特的拿去父类做抽象方法,然后子类重写所有抽象方法。
比如该例中:三种动物都会吃,但是吃的东西又不一样,所以用abstract来修饰该方法。现在请自己动手尝试一下吧。
代码如下:
public abstract class Animals {
String name;
int age;
public Animals() {
}
public Animals(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 abstract void eat();
public void drink(){
System.out.println("我会喝水");
}
}
public class Dog extends Animals{
public Dog() {
}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
public class Sheep extends Animals{
public Sheep() {
}
public Sheep(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("山羊吃草");
}
}
public class frog extends Animals{
public frog() {
}
public frog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("青蛙吃虫子");
}
}
public class Test {
public static void main(String[] args) {
Dog d = new Dog("小黄",5);
System.out.println("狗:"+ d.getName() + "," + d.getAge());
d.drink();
d.eat();
frog f = new frog("小绿",1);
System.out.println("青蛙:"+ f.getName() + "," + f.getAge());
f.drink();
f.eat();
Sheep s = new Sheep("小白",50);
System.out.println("山羊:"+ s.getName() + "," + s.getAge());
s.drink();
s.eat();
}
}
【注】别忘了子类中要写构造方法哦!
运行结果如下:
9. 接口
(1)如何理解接口?
接口就是一种规则,是对行为的抽象。
【应用场景】多个但非全部类的共同行为,将该行为作为接口,接口中写入抽象方法,类实现该接口,并对抽象方法进行重写。
//定义接口
public interface Swimming {
public abstract void Swim(); //抽象方法
}
//狗会游泳(狗刨)
public class Dog extends Animals implements Swimming{//重写抽象方法
public void Swim() {
System.out.println("狗刨");
}}
//青蛙会游泳(蛙泳)
public class frog extends Animals implements Swimming{//重写抽象方法
public void Swim() {
System.out.println("蛙泳");
}
}
注意:
1. 类可以实现多个接口:
public class Student implements Eating,Drinking,Studing{}
2. 类可以在继承一个类的同时实现多个接口:
public class Student extends Person implements Eating,Drinking,Studing{}
(2)如何区分父类中的抽象方法与接口?
如:青蛙、狗和山羊
青蛙 属性:名字,年龄 行为:吃虫子,蛙泳
狗 属性:名字,年龄 行为:吃骨头,狗刨
山羊 属性:名字,年龄 行为:吃草
青蛙、狗和兔子都有“吃”的动作 ——> 父类中的抽象方法 eat()
青蛙和狗有“游泳”的动作,但兔子没有 ——> 接口 swimming
【总结】抽象方法是全部子类的共性,接口是部分子类的共性(倾向于动作)。
【注】它们的共性是:方法都是抽象的。
(3)例子:该例建立在第8点抽象类之上,多了游泳接口:
注意:是在接口下写抽象方法,让需要的类去重写接口的抽象方法。
//父类
public abstract class Animals {
String name;
int age;
public Animals() {
}
public Animals(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 abstract void eat();
public void drink(){
System.out.println("我会喝水");
}
}
//定义接口
public interface Swimming {
public abstract void Swim(); //抽象方法
}
//狗:吃(父类的eat()抽象方法)、游泳(swimming接口的抽象方法Swim)
public class Dog extends Animals implements Swimming{
public Dog() {
}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("狗吃骨头");
}
@Override
public void Swim() {
System.out.println("狗刨");
}
}
//青蛙:吃(父类的eat()抽象方法)、游泳(swimming接口的抽象方法Swim)
public class frog extends Animals implements Swimming{
public frog() {
}
public frog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("青蛙吃虫子");
}
@Override
public void Swim() {
System.out.println("蛙泳");
}
}
//山羊:吃(父类的eat()抽象方法)
public class Sheep extends Animals{
public Sheep() {
}
public Sheep(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("山羊吃草");
}
}
//测试类
public class Test {
public static void main(String[] args) {
Dog d = new Dog("小黄",5);
System.out.println("狗:"+ d.getName() + "," + d.getAge());
d.drink();
d.eat();
d.Swim();
frog f = new frog("小绿",1);
System.out.println("青蛙:"+ f.getName() + "," + f.getAge());
f.drink();
f.eat();
f.Swim();
Sheep s = new Sheep("小白",50);
System.out.println("山羊:"+ s.getName() + "," + s.getAge());
s.drink();
s.eat();
}
}
(4)7-1 定义接口(Biology、Animal)、类(Person)、子类(Pupil)
代码:
public interface Biology {
public abstract void breathe();
}
public interface Animal {
public abstract void eat();
public abstract void sleep();
}
public class Person implements Animal,Biology{
@Override
public void eat() {
System.out.println("我会按时吃饭");
}
@Override
public void sleep() {
System.out.println("早睡早起身体好");
}
@Override
public void breathe() {
System.out.println("我喜欢呼吸新鲜空气");
}
public void think(){
System.out.println("我喜欢思考");
}
}
public class Pupil extends Person{
private String school;
public Pupil() {
}
public Pupil(String school) {
this.school = school;
}
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
}
public class Main {
public static void main(String[] args) {
Pupil zhangsan = new Pupil("福建理工大学");
zhangsan.breathe();
zhangsan.eat();
zhangsan.sleep();
zhangsan.think();
System.out.println("我的学校是" + zhangsan.getSchool());
}
}
运行结果:
发现:若父类实现了接口,那么子类也会实现该接口。
(5)抽象类和接口的综合运用
【思路】
1. 运动员和教练属于人,所以创建父类Person(从中抽取共性:姓名,年龄);
2. 运动员又分为乒乓球运动员和篮球运动员,故将运动员作为父类;教练同理。
3. 说英语是部分类(乒乓球运动员和乒乓球教练)的动作,将其作为接口。
Person(name,age) SpeakEnglish接口
Athlete(study()) Coach(teach())
PingpongAth BlankAth PingpongCoa BlankCoa
(学乒乓球,说英语) (学篮球) (教乒乓球,说英语) (教篮球)
好啦,现在请尝试自己写写代码叭~
//人
public class People {
String name;
int age;
public People() {
}
public People(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 interface SpeakEnglish {
public abstract void speak();
}
//运动员
public abstract class Athlete extends People{
public abstract void study();
}
//教练
public abstract class Coach extends People{
public abstract void teach();
}
//乒乓球运动员
public class PingpongAlth extends Athlete implements SpeakEnglish{
@Override
public void study() {
System.out.println("乒乓球运动员学打乒乓球");
}
@Override
public void speak() {
System.out.println("乒乓球运动员要会说英语");
}
}
//篮球运动员
public class BlankBallAlth extends Athlete{
@Override
public void study() {
System.out.println("篮球运动员学打篮球");
}
}
//乒乓球教练
public class PingpongCoa extends Coach implements SpeakEnglish{
@Override
public void teach() {
System.out.println("乒乓球教练教打乒乓球");
}
@Override
public void speak() {
System.out.println("乒乓球教练要会说英语");
}
}
//篮球教练
public class BlankBallCoa extends Coach{
@Override
public void teach() {
System.out.println("篮球运动员教打篮球");
}
}
//测试类
public class Test {
public static void main(String[] args) {
PingpongAlth pingpongAlth = new PingpongAlth();
pingpongAlth.setName("乒乓球运动员");
pingpongAlth.setAge(20);
System.out.println(pingpongAlth.getName()+ "," + pingpongAlth.getAge());
pingpongAlth.study();
pingpongAlth.speak();
PingpongCoa pingpongCoa = new PingpongCoa();
pingpongCoa.setName("乒乓球教练");
pingpongCoa.setAge(30);
System.out.println(pingpongCoa.getName()+ "," + pingpongCoa.getAge());
pingpongCoa.teach();
pingpongCoa.speak();
BlankBallAlth blankBallAlth = new BlankBallAlth();
blankBallAlth.setName("篮球运动员");
System.out.println(blankBallAlth.getName()+ "," + blankBallAlth.getAge());
blankBallAlth.setAge(22);
blankBallAlth.study();
BlankBallCoa blankBallCoa = new BlankBallCoa();
blankBallCoa.setName("篮球教练");
blankBallCoa.setAge(28);
System.out.println(blankBallCoa.getName()+ "," + blankBallCoa.getAge());
blankBallCoa.teach();
}
}
好啦,今天的分享就到这啦~ 希望对你有所帮助
前路漫漫,修行漫漫,蓦然回首,共勉前行⎛ -᷄ ᴥ -᷅ ⎞೯
ps:本文根据黑马程序员的视频学习,喜欢的小伙伴可以前往观看视频哦ꉂꉂ꒰•̤▿•̤*ૢ꒱
黑马程序员Java零基础视频教程_上部(Java入门,含斯坦福大学练习题+力扣算法题和大厂java面试题)_哔哩哔哩_bilibili