一、继承
1.1继承的概念
- 继承是Java面向对象编程的基石,允许创建分等级层次的类
- 继承就是子类继承父类的行为和特征,使得子类对象有父类的实例域或方法
- 子类从父类继承方法,使子类具有父类相同的行为
- 继承是从已有的类中派生出新的类,新的类能吸收已有的
举例:
- 兔子和山羊的父类是食草动物,老虎和狮子的父类是食肉动物
- 食草动物和食肉动物的父类是动物
- 所以继承要符合的条件是is-a
- 父类比子类的概括性更强,子类比父类更加具体化
- 虽然食草动物和食肉动物都属于动物的子类,但是两者的行为和特征都有所差别,所以子类会有父类的一般特征但也有自身的特征
1.2类的继承方式
在Java中可以使用extends关键字申明一个类是从另一个类继承下来的,格式如下
class 父类 {
}
class 子类 extends 父类 {
}
代码形式如下:
//子类Student继承父类People
public class Student extends People
- Student称之为子类(派生类),People称之为父类(基类或者超类)
作用:当子类继承父类后,就可以使用父类的方法和属性了
- 举例一:子类继承了父类,就可以调用父类的方法
父类:
//父类
public class People {
public void say(){
System.out.println("Hello World");
}
}
子类:利用关键字“extends"进行继承
//子类
public class Men extends People {
}
启动项:
public class DemoApplication {
public static void main(String[] args) {
//利用new关键字创建一个子类的对象
Men man = new Men();
//通过子类对象调用父类的方法
man.say();
}
}
结论:子类man可以通过继承父类People从而调用父类中的方法say()
- 举例2:有一个Dog类,有一个Cat类
狗类:
//狗类
public class Dog {
public String name;
public int age;
public void dogCall(){
System.out.println(name + "汪汪汪");
}
public void eat() {
System.out.println(name + "吃饭");
}
}
猫类:
public class Cat {
public String name;
public int age;
public void dogCall(){
System.out.println(name + "喵喵喵");
}
public void eat() {
System.out.println(name + "吃饭");
}
}
观察发现Dog类和Cat类有相同的成员变量和成员方法,为了避免写重复代码,将其中相同的部分提取出来,组成一个父类Animal:
父类Animal:包含有Dog类和Cat类公共的属性和方法
public class Animal {
//Dog类和Cat类公共的属性
public String name;
public int age;
//Dog类和Cat类公共的方法
public void eat() {
System.out.println(name + "吃饭");
}
}
将Dog类和Cat类的相同属性和方法都放到一个类中,组成了父类动物类,父类动物类在原来的特性上面加以拓展,增加Dog的新功能(例如这里Dog类的功能是"汪汪叫“),就有了Dog类,那么Dog就需要继承动物类
- Dog和Cat类都需要继承Animal类
- Animal类就称为 父类/ 基类 /超类
- Dog类 / Cat类 就称为 子类 / 派生类
- 继承之后子类可以复用父类的成员,子类只需要关心自己独有的属性或行为
- 子类可以继承父类的属性和行为,但不能继承父类的构造器
- 子类拥有父类非private的属性、方法
- 子类继承父类之后,必须添加属于自己的新成员变量或者属性,否则继承就没有意义了
增加父类Animal之后的Dog类:
//Dog通过extends关键字继承父类Animal
public class Dog extends Animal {
//子类Dog独有的方法
public void dogCall(){
System.out.println(name + "汪汪汪");
}
}
增加父类Animal之后的Cat类:
//Cat类通过extends关键字继承父类Animal
public class Cat extends Animal {
//子类Cat独有的方法
public void catCall(){
System.out.println(name + "喵喵喵");
}
}
二、父类成员访问
子类将父类的成员继承了下来,包括成员属性和成员方法,下面探讨如何访问
2.1 子类访问父类的成员变量
- 访问父类与子类不同名的成员变量:直接访问
//父类
public class Animal {
//Dog类和Cat类公共的属性
public String name;
public int age;
//Dog类和Cat类公共的方法
public void eat() {
System.out.println(name + "吃饭");
}
}
//子类
public class Dog extends Animal {
//这里的colour就是子类与父类不同名的成员变量
public String colour;
//子类Dog独有的方法
public void dogCall(){
System.out.println(name + "汪汪汪");
}
}
- 访问父类成员的属性被private修饰时,前面介绍过被private修饰的变量只能在同一个包同一个文件的同一个类下使用,那么此时如何访问:可以利用Generate进行封装
例如:在父类中的age属性使用private修饰,即使DOG继承了父类,也不能对这个属性进行访问,解决办法是对这个属性进行封装,
父类Animal:利用右键快捷键选择Generate对属性进行封装
//父类
public class Animal {
//Dog类和Cat类公共的属性
public String name;
private int age;
//右键选择Generate进行封装
public int getAge() {
return age;
}
//右键选择Generate进行封装
public void setAge(int age) {
this.age = age;
}
//Dog类和Cat类公共的方法
public void eat() {
System.out.println(name + "吃饭");
}
}
子类Dog类:
//子类
public class Dog extends Animal {
//这里的colour就是子类与父类不同名的成员变量
public String colour;
//子类Dog独有的方法
public void dogCall(){
//将从父类继承的name属性赋值
String name = "大黄";
//通过getAge访问父类的私有属性age
System.out.println(name + getAge() + "汪汪汪");
}
}
启动项:
public class DemoApplication {
public static void main(String[] args) {
//通过New关键字进行创建dog对象
Dog dog = new Dog();
//通过子类对象访问父类的私有属性age并进行赋值
dog.setAge(1);
//通过对象dog调用子类方法
dog.dogCall();
}
}
打印输出结果:
大黄1汪汪汪
- 访问父类与子类同名的成员变量
父类:
//父类
public class Animal {
String name = "阿黄";
//Dog类和Cat类公共的方法
public void eat() {
System.out.println(name + "吃饭");
}
}
子类:
//子类
public class Dog extends Animal {
//子类Dog独有的方法
public void dogCall(){
//将从父类继承的name属性赋值
String name = "阿福";
System.out.println(name + "汪汪汪");
}
}
启动项:
//启动项
public class DemoApplication {
public static void main(String[] args) {
//通过New关键字进行创建dog对象
Dog dog = new Dog();
//通过对象dog调用子类方法
dog.dogCall();
}
}
打印输出:打印输出的为子类中的赋值
阿福汪汪汪
父类中有属性名为name,子类中也有属性名name,且他们的类型相同,我们通过打印可以看到打印输出的为子类name成员变量的值
在子类成员方法中或者通过子类成员对象访问成员变量时
- 如果访问的成员变量子类有,优先访问子类
- 如果访问的成员变量子类没有,访问从父类继承下来的成员变量,如果父类也没有,则编译报错
- 如果访问的成员变量子类和父类同名,根据就近原则优先访问子类的成员变量,
- 也可以通过关键字类访问成员变量(访问子类本类的用this,访问父类的用super)
2.2 子类访问父类的成员方法
- 访问父类中与子类不同名的方法:直接访问
父类:父类方法eat()
//父类
public class Animal {
String name = "阿黄";
//父类的方法eat()
public void eat() {
System.out.println(name + "吃饭");
}
}
子类:子类方法dogCall()
//子类
public class Dog extends Animal {
//子类Dog独有的方法dogCall()
public void dogCall(){
//将父类的方法一起添加进来,方便调用
eat();
//将从父类继承的name属性赋值
String name = "阿福";
System.out.println(name + "汪汪汪");
}
}
启动项:
//启动项
public class DemoApplication {
public static void main(String[] args) {
//通过New关键字进行创建dog对象
Dog dog = new Dog();
//通过对象dog调用子类方法
dog.dogCall();
}
}
打印输出:
阿黄吃饭
阿福汪汪汪
- 访问父类与子类中同名的方法:优先访问子类
父类:eat()方法
//父类
public class Animal {
String name = "阿黄";
//父类的方法eat()
public void eat() {
System.out.println(name + "吃饭");
}
}
子类:eat()方法
//子类
public class Dog extends Animal {
//子类Dog的方法eat()
public void eat(){
//将父类的方法一起添加进来,方便调用
//将从父类继承的name属性赋值
String name = "阿福";
System.out.println(name + "汪汪汪");
}
}
启动项:
//启动项
public class DemoApplication {j
public static void main(String[] args) {
//通过New关键字进行创建dog对象
Dog dog = new Dog();
//通过对象dog调用子类方法
dog.eat();
}
}
打印输出:
阿福汪汪汪
结论:当访问父类和子类同名的方法时,优先访问子类的成员方法
那么现在存在这样一个问题:
如果父类和子类的成员变量和成员方法相同,访问时优先级肯定时子类更高,如何通过子类的方法去访问父类的成员变量或者方法呢
三、Super关键字详解
上面验证了如果子类和父类的变量或者方法相同,那么通过子类的方法去访问,一定会访问到子类的方法,那么如何才能访问到父类中相同的方法和变量呢,就是使用super关键字
super.成员变量 :表示访问父类的成员变量
super.成员方法 : 表示访问父类的成员方法
super(): 表示访问父类的构造方法
super关键字的作用是:在子类中访问父类的成员方法或者变量
如果我们在子类中看到了super关键字,那么一定是访问了父类的成员方法或者变量
父类:
//父类
public class Animal {
String name = "阿黄";
//父类的方法eat()
public void eat() {
System.out.println(name + "吃饭");
}
}
子类:利用super关键字访问父类中同名变量name
//子类
public class Dog extends Animal {
//子类Dog的方法eat()
public void eat(){
//将从父类继承的name属性赋值
String name = "阿福";
//利用super关键字访问父类中同名的成员变量name
System.out.println(super.name + "汪汪汪");
}
}
启动项:
public class DemoApplication {
public static void main(String[] args) {
//通过New关键字进行创建dog对象
Dog dog = new Dog();
//通过对象dog调用子类方法
dog.eat();
}
}
打印输出:成功利用super关键字调用了父类的name
阿黄汪汪汪
在上述代码中,我们在Dog类的eat方法中,使用了super.name,即调用了父类的“name=阿黄”,所以打印的name是父类中的name,通过运行结果来看也确实如此
- super关键字只能在非静态方法中使用
- super在子类方法中,访问父类的成员方法和变量
三、子类的构造器
子类之所以叫子类,因为它继承了父类,但子类是不继承父类的构造器的,他只是进行一个调用,
- 如果父类的构造器没有参数,则子类的构造器不需要使用super关键字调用父类的构造器,系统会自动调用父类的无参构造
- 如果父类的构造器带有参数,则必须在子类构造器中通过super关键字调用父类构造器并赋予相应的形参列表
- super(实参列表)必须在构造器的第一行
父类:
//父类
public class Animal {
private int age;
//父类的无参构造
public Animal() {
System.out.println("Animal()的无参构造");
}
//父类的有参构造
public Animal(int age) {
System.out.println("Animal()的有参构造");
this.age = age;
}
}
Dog类:利用super调用父类中的有参构造器
//子类
public class Dog extends Animal {
private int age;
//自动调用父类的无参构造
public Dog() {
System.out.println("Dog的无参构造");
}
public Dog(int age) {
//利用super调用父类中的有参构造器
super(200);
System.out.println("Dog的有参构造"+age);
this.age = age;
}
}
Cat类:利用super调用父类中的有参构造器
//Cat类通过extends关键字继承父类Animal
public class Cat extends Animal {
private int age;
//调用的无参构造
public Cat() {
System.out.println("Cat的无参构造");
}
public Cat(int age) {
//利用super调用父类中的有参构造
super(100);
System.out.println("Cat的有参构造"+age);
this.age = age;
}
}
启动项:
public class DemoApplication {
public static void main(String[] args) {
System.out.println("Dog类继承-----------");
Dog dog = new Dog();
Dog dog1 = new Dog(300);
System.out.println("Cat类继承------------");
Cat cat = new Cat();
new Cat(400);
}
}
控制台打印输出:
Dog类继承-----------
Animal()的无参构造
Dog的无参构造
Animal()的有参构造
Dog的有参构造300
Cat类继承------------
Animal()的无参构造
Cat的无参构造
Animal()的有参构造
Cat的有参构造400