✨✨欢迎大家来到Celia的博客✨✨
🎉🎉创作不易,请点赞关注,多多支持哦🎉🎉
所属专栏:JAVA
目录
引言
在Java中,我们可以定义一个类来描述一个对象,但在某些情况下,被描述的对象的属性比较特殊,甚至某些对象之间还会有一定的关联,这个时候就需要通过继承来实现这些复杂的关系。
比如:猫猫、狗狗,它们都属于动物的范畴。
一、继承
有如下代码:
class Dog{ public String name; public int age; public Dog(String name, int age) { this.name = name; this.age = age; } public void eat(){ System.out.println(this.name + " " + "正在吃狗粮"); } } class Cat{ public String name; public int age; public Cat(String name, int age) { this.name = name; this.age = age; } public void eat(){ System.out.println(this.name + " " + "正在吃猫粮"); } }
我们发现,这两个类的定义有共同之处:都具有名字、年龄。
那我们能不能简化一下代码,把名字、年龄单独抽取出来呢?
1.1 继承的概念
继承(inheritance)机制:是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生新的类,称派生类(子类)。继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用。
1.2 继承语法
class 子类名 extends 父类名{ //... }
extends 是Java中继承的关键字,用来实现继承。我们可以额外定义一个Animal类,让Dog和Cat类继承它,代码如下:
class Animal{ public String name; public int age; } class Dog extends Animal{ //继承 public Dog(String name, int age) { this.name = name; this.age = age; } public void eat(){ System.out.println(this.name + " " + "正在吃狗粮"); } } class Cat extends Animal{ public Cat(String name, int age) { this.name = name; this.age = age; } public void eat(){ System.out.println(this.name + " " + "正在吃猫粮"); } }
- 我们把继承的类叫做子类/派生类,被继承的类叫做父类/基类/超类
- 父类的成员方法和成员变量会一并继承到子类当中。
- 子类可以当作包含了父类的成员变量,并且可以使用父类的成员方法。
1.3 父类成员的访问
1.3.1 子类中访问父类的成员变量
1.3.1.1 子类父类中的成员变量不同名
class A{
int a;
int b;
}
public class Text extends A{
int c;
public void func(){
a = 10;//访问父类的成员变量
b = 20;//访问父类的成员变量
c = 30;//访问自己的的成员变量
}
}
1.3.1.2 子类父类中的成员变量同名
class A{
int a;
int b;
}
public class Text extends A{
int b;
public void func(){
a = 10;//访问父类的成员变量
b = 20;//访问自己的成员变量
}
}
结论:
在 子类方法中/通过子类对象 访问父类的成员变量时:
如果子类有,优先访问子类自己的成员变量。
如果子类没有,父类有,则访问父类的成员变量。
如果子类父类都没有,则报错。
1.3.2 在子类中访问父类的成员方法
1.3.2.1 子类父类中的成员方法不同名
class A{
public void f(){
System.out.println("xxx");
}
}
public class Text extends A{
public void func(){
System.out.println("aaa");
}
public void func2(){
func();//访问子类自己的
f();//访问父类的
}
}
1.3.2.2 子类父类中的成员方法同名
class A{
public void f(){
System.out.println("xxx");
}
}
public class Text extends A{
@Override
public void f(){
System.out.println("aaa");
}
public void func2(){
f();//访问子类自己的,构成方法重写
}
}
方法重写是指子类在继承父类的同时,重新定义父类中已经存在的方法。子类可以根据自己的需要来改变方法的实现,但方法的名称、参数列表和返回类型必须与父类中被重写的方法一致。此时调用的是父类的方法,但是实际执行的是子类中重写的方法。这种在运行时才确定调用哪个方法的情况叫做动态绑定。
结论:
- 通过子类对象访问父类与子类中不同名的成员方法时,优先在子类中找,如果子类没有,再去父类中找,找到则访问,未找到则报错。
- 如果子类对象访问父类和子类中同名方法时,如果参数列表不同,构成重载,会根据传入参数列表的不同在编译时确定调用哪个方法(静态绑定)。如果参数列表相同,构成重写,则会在运行时调用子类重写的方法(动态绑定)。
1.4 super关键字
如果子类和父类有同名的成员变量,会优先调用子类的成员变量。如果想要调用父类的成员变量,this引用是做不到的。这时,就需要用到super关键字,来专门访问父类成员。
class Animal{
public String name = "小黑";
public int age;
}
class Dog extends Animal{
public String name = "汪汪";
public void func(){
System.out.println(super.name);//在这里会输出“小黑”
}
}
在以上代码中,如果我们调用func,会输出小黑:
注意事项:super 和 this 一样,都是依赖于对象的,所以只能在非静态方法中使用。
1.4.1 子类构造方法
如果一个子类继承了一个父类,那么在子类的构造方法中,必须先调用父类的构造方法。
class Animal{
public String name;
public int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
}
class Dog extends Animal{
public Dog(String name, int age) {
//先调用父类的构造方法
super(name, age);
//再给子类的其他成员变量进行初始化.....
}
}
public class Text {
public static void main(String[] args) {
Dog dog = new Dog("小黑",10);
System.out.println(dog.name + dog.age);
}
}
注意事项:
- 调用父类的构造方法必须在子类构造方法的第一行。
- 如果父类没有自定义的构造方法,编译器会自动添加一个空的构造方法。
1.5 关于super和this
- super和this都可以在成员方法中访问:成员变量和成员方法
- super是访问父类的部分成员变量和成员方法
- this是访问当前对象的(包括继承的)成员变量和成员方法
- super和this都可以在构造方法中使用,但是由于它们都必须在第一行,故在构造方法中,this和super不能同时出现
- 在有继承关系的子类的构造方法中,一定存在super的调用,如果用户不写,编译器会自动添加,但如果用户不写this,则没有
- 在多继承体系下,super只能访问直系父类,不能访问父类的父类
1.6 关于继承方式
- 单继承:A->B
- 多层继承:A->B->C
- 不同类继承同一个类: A->B C->B