初始继承
首先我们来看一段代码:
class dog{
private String name;
private int age;
public void eat(){
System.out.println(this.name+"正在吃饭!!!");
}
public void bark(){
System.out.println(this.name+"汪汪汪!!!");
}
}
class Cat{
private String name;
private int age;
public void eat(){
System.out.println(this.name+"正在吃饭!!!");
}
public void bark(){
System.out.println(this.name+"喵喵喵!!!");
}
}
我们可以发现 dog 和 cat类中具有很多的公共部分,那么如何简单化代码呢 ?这时候我们就需要提到一个新概念:继承!
继承机制:是面向对象程序设计的使代码可以重复利用的手段,它允许程序员在保持原有类的特性的基础上进行扩展,增加新功能,这样产生的类称之为派生类。
继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程,继承主要解决的问题是:共性的提取,实现代码复用。
所以上述代码 我们可以进行修改:
class Animal {
private String name;
private int age;
public Animal(String name, int age) {
this.name = name;
;
this.age = age;
}
public String getName() {
return name;
}
public void eat() {
System.out.println(this.name + "正在吃饭!!!");
}
}
class Dog extends Animal {
public Dog(String name, int age) {
super(name, age);
}
public void bark() {
System.out.println(super.getName() + "汪汪汪!!!");
}
}
class Cat extends Animal {
public Cat(String name, int age) {
super(name, age);
}
public void bark() {
System.out.println(super.getName() + "喵喵喵!!!");
}
}
public class Test {
public static void main(String[] args) {
Dog dog=new Dog("huahua",11);
Cat cat=new Cat("qiqi",12);
dog.bark();
dog.eat();
cat.bark();
cat.eat();
}
}
运行结果:
这段代码 中所涉及的super是个关键字 我们可以向下继续阅读 来了解什么是super!
子类构造方法
上诉代码中 我调用了 两次构造方法 这是为什么呢 这里我们就要提到有一句话;现有父再有子!即:在子类对象的构造时,需要先调用父类构造方法,然后才能执行子类的构造方法!
注意:
1.若父类显式定义无参或者默认构造方法,在子类构造方法种隐含着super()调用父类构造方法!
2.如果父类的构造方法是带参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败!(可参考 最上面代码)
3.在子类构造方法中 super()调用父类构造时,必须是子类第一条语句!
4.super()只能在子类构造方法中出现一次,并且不能与this同时出现!(防止重复构造)
super和this
由于设计的问题 或是因为场景需要,子类和父类具有相同的名字的成员,如果在子类中想要访问父类的同名成员,我们就需要用到super关键字,类似this关键字,作用:在子类中访问父类 成员
super和this都可以调用成员变量和方法,那么他们的区别是什么?
相同点:
1.都是java中的关键字
2.只能在类的静态方法中使用,用来访问非静态成员方法和字段
3.在构造方法调用时,必须是构造方法的第一句,并且不能同时是存在
不同点:
1.this是当前对象的引用,当前对象即调用实例化方法的对象,super相当于是子类对象中从父类继承下来的部分成员的引用
2.在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
3.在构造方法中:this()用于调用本类的构造方法,super()用于调用父类的构造方法,两种不能同时在构造方法中出现
4.构造方法中一定会存在super()的调用,用户不写会自动生成,但是this()不会。
代码块执行顺序
/**
* Created with IntelliJ IDEA.
* Description:
* User: ilook
* Date: 2023-05-22
* Time: 12:29
*/
class People{
private String name;
private int age;
public People(String name,int age){
this.name=name;
this.age=age;
System.out.println("父类构造方法!");
}
{
System.out.println("父类实例化代码块!");
}
static{
System.out.println("父类静态代码块!");
}
}
class Student extends People{
public Student(String name,int age){
super(name,age);
System.out.println("子类构造方法!");
}
{
System.out.println("子类实例化代码!");
}
static{
System.out.println("子类静态代码!");
}
}
public class Test1 {
public static void main(String[] args) {
Student s1=new Student("huahua",11);
System.out.println("========================");
Student s2=new Student("qiqi",12);
}
}
运行结果:
通过分析 我们可以得出以下结论:
1.父类静态代码块优先于子类静态代码块,并且静态代码块 执行最早
2.父类实例化代码优先于父类的构造方法
3.子类的实例化代码优先于子类构造方法
4.static静态代码块 只能执行一次
protected 关键字
为了实现封装的特性,java中引用了访问限定符,这次 我们来研究一下 在继承中 访问限定符的作用域问题:
class B{
private int a;
protected int b;
public int c;
int d;//不给访问限定符 默认认为是default
}
//在同一个包内
class C extends B{
public void method(){
super.a=10;//报错 父类private在相同的包内子类不可见
super.b=11;
super.c=13;
super.d=14;
}
}
//不同的包内
class C extends B{
public void method(){
super.a=10;//报错 父类private 在不同的包内子类不可见
super.b=11;
super.c=13;
super.d=14;//报错 默认访问修饰限定符在不同的包内不能访问
}
}
注意:父类中的private成员变量虽然在子类中不能直接访问,到那时也继承到子类中
继承方式
下面 是java中只支持以下几种继承方式:
注意:Java不同于C++ java不支持多继承。
一般的情况 我们的继承最多不会超过三层,如果太多我们就可以考虑代码的重构,如果想要终止继承,就需要使用一个关键字,final
final关键字
我们可以将final关键字 类比成 C++中的const,它可以修饰变量,成员方法,以及类。
1.修饰变量
final int a=10;
a=100;//报错
2.修饰类
3.修饰方法:表示该方法不能重写,后续会详细介绍。