Java中继承详解

为什么要继承?

面相对象思想中提出了继承的概念,专门用来进行共性抽取,实现代码复用。

继承概念

子类将父类的一些属性与方法继承过来,然后自己再添加一些新的特性。

继承的语法

1:在Java中如果要表示类之间的继承关系,需要借助extends关键字

2:继承的是:除了父类的构造方法外的所有。

3:单继承:一个子类只能继承一个父类,不可能继承2个或多个类。

4: 子类继承父类中的除构造方法外的所有东西,但是父类中的private修饰的方法和字段,
在子类中是无法被访问的。

public class Dog extends Animal{
void bark(){
System.out.println(name + "汪汪汪~~~");
}
}

父类成员访问

一:子类中访问父类的成员变量

1.子类和父类不存在同名成员变量
public class Base {
int a;
int b;
}
public class Derived extends Base{
int c;
public void method(){
a = 10; // 访问从父类中继承下来的a
b = 20; // 访问从父类中继承下来的b
c = 30; // 访问子类自己的c
}
}
2:子类和父类成员变量同名
public class Base {
int a;
int b;
int c;
}
/
public class Derived extends Base{
int a; // 与父类中成员a同名,且类型相同
char b; // 与父类中成员b同名,但类型不同
public void method(){
a = 100; // 访问父类继承的a,还是子类自己新增的a?
b = 101; // 访问父类继承的b,还是子类自己新增的b?
c = 102; // 子类没有c,访问的肯定是从父类继承下来的c
}
}

在子类方法中 或者 通过子类对象访问成员时

如果访问的成员变量子类中有,优先访问自己的成员变量

如果访问的成员变量与父类中成员变量同名,则优先访问自己的.

成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找。

二:子类中访问父类的成员方法

1:成员方法名字不同
public class Base {
public void methodA(){
System.out.println("Base中的methodA()");
}
}
public class Derived extends Base{
public void methodB(){
System.out.println("Derived中的methodB()方法");
}
public void methodC(){
methodB(); // 访问子类自己的methodB()
methodA(); // 访问父类继承的methodA()
}
}

成员方法没有同名时,在子类方法中或者通过子类对象访问方法时,则优先访问自己的,自己没有时 再到父类中找,如果父类中也没有则报错。

2:成员方法名字相同
public class Base {
public void methodA(){
System.out.println("Base中的methodA()");
}
public void methodB(){
System.out.println("Base中的methodB()");
}
}
public class Derived extends Base{
public void methodA(int a) {
System.out.println("Derived中的method(int)方法");
}
public void methodB(){
System.out.println("Derived中的methodB()方法");
}
public void methodC(){
methodA(); // 没有传参,访问父类中的methodA()
methodA(20); // 传递int参数,访问子类中的methodA(int)
methodB(); // 直接访问,则永远访问到的都是子类中的methodB(),基类的无法访问到
}
}

(1):通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到 则访问,否则编译报错

(2):通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用 方法适传递的参数选择合适的方法访问,如果没有则报错;如果父类和子类同名方法的原型一致(重写-后 面讲),则只能访问到子类的,父类的无法通过派生类对象直接访问到。

super 关键字的引入

子类和父类中可能会存在相同名称的成员,如果要在子类方法中访问父类同名成 员时,该如何操作?直接访

问是无法做到的,Java提供了super关键字,该关键字主要作用:在子类方法中访问父 类的成员(字段和方法)。

public class Base {
int a;
int b;
public void methodA(){
System.out.println("Base中的methodA()");
}
public void methodB(){
System.out.println("Base中的methodB()");
}
}
public class Derived extends Base{
int a; // 与父类中成员变量同名且类型相同
char b; // 与父类中成员变量同名但类型不同
// 与父类中methodA()构成重载
public void methodA(int a) {
System.out.println("Derived中的method()方法");
}
// 与基类中methodB()构成重写(即原型一致,重写后序详细介绍)
public void methodB(){
System.out.println("Derived中的methodB()方法");
}
public void methodC(){
// 对于同名的成员变量,直接访问时,访问的都是子类的
a = 100; // 等价于: this.a = 100;
b = 101; // 等价于: this.b = 101;
// 注意:this是当前对象的引用
// 访问父类的成员变量时,需要借助super关键字
// super是获取到子类对象中从基类继承下来的部分
super.a = 200;
super.b = 201;
// 父类和子类中构成重载的方法,直接可以通过参数列表区分清访问父类还是子类方法
methodA(); // 没有传参,访问父类中的methodA()
methodA(20); // 传递int参数,访问子类中的methodA(int)
// 如果在子类中要访问重写的基类方法,则需要借助super关键字
methodB(); // 直接访问,则永远访问到的都是子类中的methodA(),基类的无法访问到
super.methodB(); // 访问基类的methodB()
}
}

注意:

1:只能在非静态方法中使用

2: 在子类方法中,访问父类的成员变量和方法。

子类构造方法

子类对象构造时,需要先调用基类构造方法,然后执行子类的构造方法

public class Base {
public Base(){
System.out.println("Base()");
}
}
public class Derived extends Base{
public Derived(){
// super(); // 注意子类构造方法中默认会调用基类的无参构造方法:super(),
// 用户没有写时,编译器会自动添加,而且super()必须是子类构造方法中第一条语句,
// 并且只能出现一次
System.out.println("Derived()");
}
}
public class Test {
public static void main(String[] args) {
Derived d = new Derived();
}
}
结果打印:
Base()
Derived()

在子类构造方法中,并没有写任何关于基类构造的代码,但是在构造子类对象时,先执行基类的构造方法,然后执 行子类的构造方法,因为:子类对象中成员是有两部分组成的,基类继承下来的以及子类新增加的部分 。父子父子 肯定是先有父再有子,所以在构造子类对象时候 ,先要调用基类的构造方法,将从基类继承下来的成员构造完整 ,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整

注意:

1: 若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构 造方法。

2: 如果父类构造方法是带有参数的,此时编译器不会再给子类生成默认的构造方法,此时需要用户为子类显式 定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败。//实践

3:在子类构造方法中,super(…)调用父类构造时,必须是子类构造函数中第一条语句。

4: super(…)只能在子类构造方法中出现一次,并且不能和this同时出现

super和this

在这里插入图片描述

相同点:

1: 只能在类的非静态方法中使用,用来访问非静态成员方法和字段

2: 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在

不同点:

1: this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成员的引用

2:在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性

3: this是非静态成员方法的一个隐藏参数,super不是隐藏的参数

4:在构造方法中:this(…)用于调用本类构造方法,super(…)用于调用父类构造方法,两种调用不能同时在构造 方法中出现

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

protected 关键字

为了实现封装特性,Java中引入了访问限定符,主要限定:类或者类中成员能否在类外或者其 他包中被访问。

在这里插入图片描述

// 为了掩饰基类中不同访问权限在子类中的可见性,为了简单类B中就不设置成员方法了
// extend01包中
public class B {
private int a;
protected int b;
public int c;
int d;
}
// extend01包中
// 同一个包中的子类
public class D extends B{
public void method(){
// super.a = 10; // 编译报错,父类private成员在相同包子类中不可见
super.b = 20; // 父类中protected成员在相同包子类中可以直接访问
super.c = 30; // 父类中public成员在相同包子类中可以直接访问
super.d = 40; // 父类中默认访问权限修饰的成员在相同包子类中可以直接访问
}
}
// extend02包中
// 不同包中的子类
public class C extends B {
public void method(){
// super.a = 10; // 编译报错,父类中private成员在不同包子类中不可见
super.b = 20; // 父类中protected修饰的成员在不同包子类中可以直接访问
super.c = 30; // 父类中public修饰的成员在不同包子类中可以直接访问
//super.d = 40; // 父类中默认访问权限修饰的成员在不同包子类中不能直接访问
}
}
// extend02包中
// 不同包中的类
public class TestC {
public static void main(String[] args) {
C c = new C();
c.method();
// System.out.println(c.a); // 编译报错,父类中private成员在不同包其他类中不可见
// System.out.println(c.b); // 父类中protected成员在不同包其他类中不能直接访问
System.out.println(c.c); // 父类中public成员在不同包其他类中可以直接访问
// System.out.println(c.d); // 父类中默认访问权限修饰的成员在不同包其他类中不能直接访问
}
}

注意:

1: Test C 是一个普通的类,不是子类,也不是父类。

2: j a v a中有很多的类,每个类中又有2个东西:字段(成员变量)和方法。

字段:不是方法的都是字段。

3:方法(不管是不是静态的方法)里面可以创建对象,方法里面可以干大多数想干的事。

4:如果没有加访问修饰限定符,那默认的是包访问权限,只能在同一个包中进行访问。

举一反三

class anmail{
    public String name;
    public int age;
    public void jihe(){
        System.out.println(this.name+" "+this.age+" "+"eat!");
    }
    public anmail(String name){
        this.name=name;

    }
    public anmail(int age){
        this.age=age;
    }
}

class dog extends anmail {
    
    public int a;

    public dog(int a){
        super(a);//相当与anmail(a)
    }
    /*
    重点:
    public dog(int a){
        super(this.a);
        //因为本类当中的a还没有初始化。
    }
    这样写是错误的。
    例子看下面的图片
    */

    public void func(int x) {
        this.a = x;
    }
}

public class Text15 {
    public static void main(String[] args) {
        bird bird1=new bird("鸟");
        bird1.age=12;
        dog dog1=new dog(10);
        dog1.func(10);
        dog1.name="牛仔";
        bird1.jihe() ;
        dog1.jihe();
    }
}

在这里插入图片描述

执行逻辑:

编译器通过public找到Z类;然后找到Z类中的main方法;然后创建了一个Z对象,然后调用Z的构造方法;

从而进入Z的构造方法,因为Z继承了X ,所以Z是X的子类,所以要先进行构造父类,

其实public Z(super())里面有一个隐藏的super

{

}

现在要进行构造父类X,在进行X构造的时候,先构造(初始化)X的成员变量,再构造(初始化)X的代码块,最后构造(初始化)X的构造方法。

所以先进行y对象的创建,然后调用Y的构造方法;同样在进行Y构造的时候,先构造(初始化)Y的成员变量,再构造(初始化)Y的代码块,最后构造(初始化)Y的构造方法。

所以打印Y后回到类X打印X,回到Z的构造方法,同样在进行Z构造的时候,先构造(初始化)Z的成员变量,再构造(初始化)Z的代码块,最后构造(初始化)Z的构造方法。所以先打印Y再打印Z。

即打印:YXYZ

理解同一个包中的不同类:–>

在这里插入图片描述

理解private:

在这里插入图片描述
在这里插入图片描述

private 修饰的成员,只能被在同一个包中,同一个类下创建的对象访问。
//例如:在Animal类中有一个private修饰的成员,其只能在Animal类中创建的以Animal为模板的对象中访问。
1:一个包中有很多的类,编译器会自动的从public修饰的类中寻找main方法,如果没有找到的话就去其他的类寻找。
2:类的方法中只能调用其他方法,不可以声明实例一个方法
3:main方法是一个static修饰的方法,他不可以访问非静态的方法与变量
4:在方法里面可以创建一个对象,在方法外面类里面也可以创建一个对象。
5:在静态的方法中,访问非静态的变量和方法时,需要对象的引用。
    
    例如:
    
    public class Shut extends Animal {
    public  static void method(Shut shut1){
        System.out.println(shut1.name);//对象的引用
    }
    public static void main(String[] args) {

        Shut shut1=new Shut();
        method(shut1);
        shut1.name="111";
        shut1.age=10;

    }
}

对protected 中“可以访问不同包中的子类”的进一步理解

在这里插入图片描述
在这里插入图片描述

继承的方式:

一般我们不希望出现超过三层的继承关系

如果想从语法上进行限制继承, 就可以使用 final 关键字

   shut1.name="111";
    shut1.age=10;

}

一般我们不希望出现超过三层的继承关系

如果想从语法上进行限制继承, 就可以使用 final 关键字

少年的肩应该担起草长莺飞和清风明月, 女孩的眼应该藏下星辰大海和万丈光芒。

  • 12
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
Java的DelayQueue是一个基于优先级队列实现的延迟队列。它可以用于定时任务调度、缓存过期等场景。 DelayQueue的元素必须实现Delayed接口,该接口继承自Comparable接口,因此元素需要实现compareTo方法,以便在队列维护元素的优先级。 DelayQueue的元素按照延迟时间的大小进行排序,即延迟时间短的元素排在队列的前面。当从队列取出元素时,只有延迟时间到了的元素才会被取出。 以下是一个使用DelayQueue的简单示例: ```java import java.util.concurrent.DelayQueue; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; public class DelayQueueDemo { public static void main(String[] args) throws InterruptedException { DelayQueue<DelayedElement> queue = new DelayQueue<>(); queue.add(new DelayedElement("task1", 3000)); // 延迟3秒执行 queue.add(new DelayedElement("task2", 2000)); // 延迟2秒执行 queue.add(new DelayedElement("task3", 1000)); // 延迟1秒执行 while (!queue.isEmpty()) { DelayedElement element = queue.take(); // 取出元素 System.out.println(System.currentTimeMillis() + ": " + element); } } } class DelayedElement implements Delayed { private String name; private long expireTime; public DelayedElement(String name, long delay) { this.name = name; this.expireTime = System.currentTimeMillis() + delay; } @Override public long getDelay(TimeUnit unit) { return unit.convert(expireTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS); } @Override public int compareTo(Delayed o) { return Long.compare(this.getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS)); } @Override public String toString() { return "DelayedElement{" + "name='" + name + '\'' + ", expireTime=" + expireTime + '}'; } } ``` 在上面的示例,我们创建了一个DelayQueue对象,并向其添加了三个DelayedElement元素,分别表示3秒、2秒和1秒后执行的任务。然后在一个循环不断取出元素,直到队列为空。由于每个元素的延迟时间不同,因此取出的顺序也是不同的。 以上就是JavaDelayQueue的简单使用方法。需要注意的是,DelayQueue是线程安全的,可以在多线程环境下使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橙-橙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值