【Java并发】父类能调用子类的方法吗?

简介: 【Java并发】父类能调用子类的方法吗?
文章开始前,我们先抛出一个链式问题:
Java中父类能调用子类的方法吗?
如果不可以,原因?如果可以,如何做?
每种实现方式,在实际项目被允许吗?
不被允许的原因有哪些?
如果在实际项目中,的确有这样的特殊业务场景,应如何“正确的” - “变相”的去实现?

1.Java中父类能调用子类的方法吗?

答案:能

1.1 实现方式描述

(1)在父类中直接new子类相关对象或者通过构造函数传入子类对象,然后调用其方法
(2)将子类相关方法声明为static,在父类中调用子类的static方法
(3)在父类中通过反射调用子类的相关方法
(4)通过注册监听,然后通过回调接口调用子类相关方法
在看了相关描述之后,大家肯定迫切想看一下,四种方法的代码如何进行编写,或者您已经想到了。

1.2 四种实现方式的代码

接下来我们一起看一下这四种“奇葩”的实现方式的代码。
测试代码很简单,包含三个类:
TestTwo 测试功能类、Father父类、Son子类

package test;

/**
 * 父类
 * @author itbird
 *
 */
public class Father {
    private SonFuctionListener mListenr;

    public Father() {

    }

    /**
     * 在父类中直接new子类相关对象,然后调用其方法
     */
    public void realizeWay1() {
        Son son = new Son();
        son.testSonMethod("在父类中直接new子类相关对象,然后调用其方法");
    }

    /**
     * 在父类中调用子类的static方法
     */
    public void realizeWay2() {
        Son.testSonMethod1("在父类中调用子类的static方法");
    }

    /**
     * 在父类中通过反射调用子类的相关方法
     */
    public void realizeWay3() {
        try {
            Class cls = Class.forName("test.Son");
            Son son = (Son) cls.newInstance();
            son.testSonMethod("在父类中通过反射调用子类的相关方法");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 通过监听回调
     * @param listener
     */
    public void setSonListener(SonFuctionListener listener) {
        mListenr = listener;
    }

    public void realizeWay4() {
        if (mListenr != null) {
            mListenr.callTestMethod1();
        }
    }

    public interface SonFuctionListener {
        void callTestMethod1();
    }
}
package test;

import test.Father.SonFuctionListener;

/**
 * 子类
 * @author itbird
 *
 */
public class Son extends Father implements SonFuctionListener{

    public Son() {
        setSonListener(this);
    }

    public void testSonMethod(String str) {
        System.out.println(str + ": this is testSonMethod");
    }

    public static void testSonMethod1(String str) {
        System.out.println(str + ": this is static testSonMethod");
    }

    @Override
    public void callTestMethod1() {
        testSonMethod("通过监听回调");
    }
    
}
package test;

/**
 * 测试功能类
 * @author itbird
 * 
 */
public class TestTwo {
    public static void main(String[] args) {
        Son son = new Son();
        //在父类中直接new子类相关对象,然后调用其方法
        son.realizeWay1();
        //在父类中调用子类的static方法
        son.realizeWay2();
        //在父类中通过反射调用子类的相关方法
        son.realizeWay3();
        //通过监听回调
        son.realizeWay4();
    }
}

看一下四种实现方式,可以达到我们预期结果:
运行结果.png

文章看到这,各位看客先暂停一下,不要继续往下阅读,先细细看一下上面的例子,然后思考几个问题。

上面代码是否有问题?
有哪些 奇葩点
是否违背了某些Java的设计原则?
是否存在内存方面或者明显的代码风险、问题?

2.上述几种实现方式,在实际项目被允许吗?

如果各位看官细细品味了上面的样例代码,并且稍微熟悉面向对象的继承和多态特性的话,会得出一个显而易见的结论,不允许
看到这儿,如果仍有看官,不太明白为什么不允许话,小编只能在上面的样例基础上,简单提醒以下几点,还需各位看官在实际项目研发过程中细细品味 & 时常总结,敬请见谅。因为毕竟涉及到面向对象的基本属性、项目经验,有些东西本来就是约定、规则、经验,无法进行进一步解释。

(1)Java继承概念中,父类是不确定子类的,但子类可以确定其父类-- 多态特性的来源之一
(2)父类是不可调用子类的方法的,但子类可以调用父类所有非private的方法- 继承特性的特征之一
(3)存在明显的代码漏洞,例如:因为Java继承的关系,所以类进行加载时,是先加载的父类,才去加载子类,如果恰巧这是父类的某个方法调用了子类的方法,而子类方法的某些常量因为子类还未加载没有实例化,就会直接导致程序崩溃
(4)如果通过上述方法实现了相关效果,那么请这样做的各位Coder反思一个问题,您的子类继承父类的意义到底在哪里?

3.如果在实际项目中,的确有这样的特殊业务场景,即有些实现并不确定,需要具体子类去实现,但是又必须在父类规定其调用顺序与场景,应如何做?

同样,先上代码,各位先认真品味

package test;

/**
 * 父类
 * @author itbird
 *
 */
public abstract class Father {
    // 基本方法
    protected abstract void doSomething();

    // 基本方法
    protected abstract void doAnything();

    // 模板方法
    public final void templateMethod() {
        /*
         * 调用基本方法,完成相关的逻辑
         */
        this.doAnything();
        this.doSomething();
    }
}
package test;


/**
 * 子类
 * @author itbird
 *
 */
public class Son extends Father{

    @Override
    protected void doSomething() {
        System.out.println("Son doSomething");
    }

    @Override
    protected void doAnything() {
        System.out.println("Son doAnything");
    }

    
}
package test;

/**
 * 测试功能类
 * @author itbird
 * 
 */
public class TestTwo {
    public static void main(String[] args) {
        Son son = new Son();
        son.templateMethod();
    }
}

运行结果.png

通用类图如下:
通用类图.png
基本方法:基本方法也叫做基本操作,是由子类实现的方法,并且在模板方法中被调用。
模板方法:可以有一个或者几个,一般是具体的方法,也就是一个框架,实现对基本方法的调度,完成固定的逻辑。

优点:
● 封装不变部分,扩展可变部分
● 提取公共部分代码,便于维护
● 行为由父类控制,子类实现

缺点:
按照我们的设计习惯,抽象类负责声明最抽象、最一般的事物属性和方法,实现类完成具体的事物属性和方法。但是模板方法模式却颠倒了,抽象类定义了部分抽象方法,由子类实现,子类执行的结果影响了父类的结果,也就是子类对父类产生了影响,这在复杂的项目中,会带来代码阅读的难度,而且也会让新手产生不适感。

总结:模板模式就是在模板方法中对基本方法的调用。

目录
相关文章
|
2月前
|
消息中间件 Java Kafka
在Java中实现分布式事务的常用框架和方法
总之,选择合适的分布式事务框架和方法需要综合考虑业务需求、性能、复杂度等因素。不同的框架和方法都有其特点和适用场景,需要根据具体情况进行评估和选择。同时,随着技术的不断发展,分布式事务的解决方案也在不断更新和完善,以更好地满足业务的需求。你还可以进一步深入研究和了解这些框架和方法,以便在实际应用中更好地实现分布式事务管理。
|
2月前
|
Java
java小工具util系列5:java文件相关操作工具,包括读取服务器路径下文件,删除文件及子文件,删除文件夹等方法
java小工具util系列5:java文件相关操作工具,包括读取服务器路径下文件,删除文件及子文件,删除文件夹等方法
100 9
|
2天前
|
存储 Java 索引
Java快速入门之数组、方法
### Java快速入门之数组与方法简介 #### 一、数组 数组是一种容器,用于存储同种数据类型的多个值。定义数组时需指定数据类型,如`int[]`只能存储整数。数组的初始化分为静态和动态两种: - **静态初始化**:直接指定元素,系统自动计算长度,如`int[] arr = {1, 2, 3};` - **动态初始化**:手动指定长度,系统给定默认值,如`int[] arr = new int[3];` 数组访问通过索引完成,索引从0开始,最大索引为`数组.length - 1`。遍历数组常用`for`循环。常见操作包括求和、找最值、统计特定条件元素等。
|
2月前
|
安全 Java 开发者
Java中WAIT和NOTIFY方法必须在同步块中调用的原因
在Java多线程编程中,`wait()`和`notify()`方法是实现线程间协作的关键。这两个方法必须在同步块或同步方法中调用,这一要求背后有着深刻的原因。本文将深入探讨为什么`wait()`和`notify()`方法必须在同步块中调用,以及这一机制如何确保线程安全和避免死锁。
60 4
|
2月前
|
Java
深入探讨Java中的中断机制:INTERRUPTED和ISINTERRUPTED方法详解
在Java多线程编程中,中断机制是协调线程行为的重要手段。了解和正确使用中断机制对于编写高效、可靠的并发程序至关重要。本文将深入探讨Java中的`Thread.interrupted()`和`Thread.isInterrupted()`方法的区别及其应用场景。
83 4
|
2月前
|
Java 数据处理 数据安全/隐私保护
Java处理数据接口方法
Java处理数据接口方法
32 1
|
2月前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
3月前
|
Java API
Java 对象释放与 finalize 方法
关于 Java 对象释放的疑惑解答,以及 finalize 方法的相关知识。
73 17
|
2月前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
149 4
|
2月前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####