Thread的使用、线程的几个重要操作和状态【JavaEE初阶】

news2025/1/12 8:41:37

一、Thread类的常见构造方法

Thread():

默认无参构造方法,如 :Thread t1 = new Thread();


Thread(Runnable target):

使用 Runnable 创建一个任务,再把 任务 放到线程里面,

如 Thread t2 = new Thread(new MyThread());


Thread(String name):

给线程起一个名字,线程 在操作系统内核里 没有名字,只有一个身份标识~

但是 Java中 为了让程序猿 调试的时候方便理解,这个线程是谁,就在 JVM 里给对应的 Thread对象 加了个名字(JVM中的 Thread对象 和 操作系统内核里面的线程 一一对应)

这个名字对于程序的执行没影响。

如果不手动设置,也会有默认的名字,形如 Thread-0、Thread-1、......


Thread(Runnable target):

使用 Runnable对象 创建线程,并命名

二、Thread 的几个常见属性

getId():获取到的是 线程在 JVM 中的身份标识 

线程中的身份标识是有好几个的:

  • 内核的 PCB 上有标识
  • 用户态线程库里也有标识(pthread,操作系统提供的线程库)
  • JVM 里又有一个标识(JVM Thread类 底层也是调用操作系统的 pthread 库)

三个标识各不相同,但是目的都是作为身份的区分

由于 介绍的是 Java程序,所以我们只需要知道 JVM 中的身份标识即可~


getName():

获取到 自己在 Thread 构造方法里所起的名字 


getState():

PCB 里面有状态,此处得到的状态是 JVM 里面设立的状态体系,这个状态比操作系统内置的状态要更丰富一些


getPriority():

获取到优先级


isDaemon():

这里的后台线程也叫守护线程

  • 前台线程会阻止进程的结束,前台线程的工作没做完,进程是完不了的,
  • 后台线程不会阻止进程的结束,后台线程的工作没做完,进程照样可以结束,
  • 换句话说,进程会保证所有的前台线程都执行完了 才会退出~

类似于 手机app,打开一个app,此时这个app就来到了 "前台",当按到 "回到菜单" 这样的按钮,此时app就切换到 "后台"~
线程也分成 前台线程 和 后台线程, 我们手写的线程默认都是前台线程,包括main也是默认前台,但是我们可以通过setDeamon()设置成后台线程

后台线程不会阻止进程结束,所以 进程退出的时候 不关后台进程是否执行完 就退出了 

 

package thread;
 
public class Demo7 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        },("我的线程"));
        t.start();
    }
}

执行结果:

分析:

由于该线程是一个前台线程,所以需要等待其运行结束,进程才会结束,所以会一直执行下去

再如:我们可以把他手动设置成 后台线程:t.setDaemon(true);

 

package thread;
 
public class Demo7 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        },("我的线程"));
        
        //手动设置成 后台线程
        t.setDaemon(true);
 
        t.start();
 
    }
}

运行结果:

 分析:

通过 setDaemon(true) 可以把线程设置为后台线程,等到主线程执行完,进程就结束了~

需要注意的是,先要 设置线程的前后台,然后再启动线程 


 isAlive判断当前线程是否还存活,调用start前isAlive是false,调用后isAlive是true

虽然线程把run里要干的事干完后就销毁了,pcb也随之释放了,但是Thread t这个对象还不一定被释放,此时isAlive也是false,t的销毁要等GC回收

总结:

package thread;
 
public class Demo7 {
    public static void main(String[] args) {
        Thread t = new Thread(() ->{
            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"我的线程");
 
        t.start();
 
        System.out.println("线程Id:" + t.getId());
        System.out.println("线程名称:" + t.getName());
        System.out.println("线程状态:" + t.getState());
        System.out.println("线程优先级:" + t.getPriority());
        System.out.println("是否后台线程(true/false):" + t.isDaemon());
        System.out.println("是否存活:" + t.isAlive());
    }
}

运行结果:

 注意:这些操作都是获取到的是 一瞬间的状态,而不是持续性的状态~ 


三、和线程相关的几个重要的操作

3.1 启动线程 - start()

new Thread实例的时候,并没有真的在操作系统内核里创建出线程(仅仅只是在安排任务,"跑步时 的 '各就各位,预备' ")!!!

而是 调用 start,才是真正创建出线程("发令枪响")!!!

线程的概念和创建【javaee初阶】_嘎嘎烤鸭的博客-CSDN博客


3.2 中断线程

怎么让线程执行结束,其实方法很简单:只要让线程的 入口方法 执行完了,线程就随之结束了~

主线程 的入口方法,就可以视为 mian方法~

所谓的 "中断线程",就是让线程尽快把 入口方法执行结束~

方法一:直接自己定义 标志位 来区分线程是否要结束~

package thread;
 
public class Demo9 {
    //用一个布尔变量来表示 线程是否要结束
    //这个变量是一个成员变量,而不是局部变量
    private static boolean isQuit =  false;
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (!isQuit) {
                System.out.println("线程运行中......");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("新线程执行结束!");
        });
        t.start();
 
        Thread.sleep(5000);
        System.out.println("控制新线程退出!");
        isQuit = true;
    }
}

 这个代码中,控制线程结束,主要是这个线程里有一个循环,这个循环执行完,就算结束了~

很多时候创建线程都是 让线程完成一些比较复杂的任务,往往都有一些循环,正是因为有循环,执行的时间才可能比较长;如果线程本身执行的很快,刷的一下就结束了,那么也就没有提前控制它的必要了~ 


方法二:使用 Thread类 中自带的标志位~ 

Thread 其实内置了一个标志位,不需要咱们去手动创建标志位Thread.currentThread().isInterrupted()

currentThread() 是 Thread类的静态方法,获取到当前线程的实例,这个方法中有一个线程会调用它
   线程1 调用这个方法,就能返回线程1 的 Thread对象;
   线程2 调用这个方法,就能返回线程2 的 Thread对象~
   类似于 JavaSE 里面的 this~
 
  isInterrupted() 为判定内置的标志,可以获取到标志位的值,为 true 表示线程要被中断~

package thread;
 
public class Demo10 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() ->{
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("线程运行中......");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
 
        Thread.sleep(5000);
        System.out.println("控制新线程退出!");
        t.interrupt();
    }
}

 

 interrupt会做两件事:

  1. 把线程内部的标志位(boolean)设置成true,表示终止
  2. 如果线程在进行sleep,就会触发异常,把sleep唤醒,sleep又把刚才设置的标志位设置回false(继续),这就表示sleep异常被catch完了之后循环还要继续执行

说明:调用了 interrupt,产生了一个异常。异常虽然出现了,但是线程仍然在继续运行。

注意理解 interrupt方法 的行为:

  • 如果 t 线程 没有处在阻塞状态,此时 interrupt 就会修改内置的标志位~
  • 如果 t 线程 正在处于阻塞状态,此时 interrupt 就让线程内部产生阻塞的方法,例如 sleep 抛出异常~

例如:

  1. 线程在sleep中休眠,此时调用interrupt会把t线程唤醒,从sleep中提前返回。
  2. interrupt会触发sleep内部的异常,导致sleep提前返回 

上述循环代码中,正好异常被捕获了~

而捕获之后,啥也没有干 只是打印了一个调用栈:

 

而如果 把上述代码中的  e.printStackTrace(); 给注释掉,那么就啥也不打印,运行结果调用栈也不打印了,直接忽略异常了。

正是因为这样的捕获操作,程序员就可以自行控制线程的退出行为了 

注意:调用interrupt只是通知终止,线程会不会终止,还得看代码怎么写的 

当然,除了 Thread.currentThread().isInterrupted(),Thread类还自带了一个静态方法 interrupted() 也可以访问标志位。使用 Thread.interrupted() 即可.一般掌握一个就可以了 

  • 如果当前线程处于运行的状态,就是修改标志位
  • 如果当前线程处于阻塞的状态,则是触发一个异常,线程内部就会通过这个异常被唤醒

3.3 等待线程 - join()

我们已经知道,线程是一个随机调度的过程,线程之间的执行顺序完全是随机的,看系统的调度

一般情况下写代码,其实是比较讨厌这种随机性的,更需要能够让顺序给确定下来

join() 等待线程就是一种确定线程执行顺序的辅助手段,来控制两个线程的结束顺序。

join() 的行为:

  1. 如果被等待的线程 还没执行完,就阻塞等待~
  2. 如果被等待的线程 已经执行完了,就直接返回~

如:咱们不可以确定两个线程的开始执行顺序,但是可以通过 join() 来控制两个线程的结束顺序~

 private static void concurrency() {
        //concurrency 的意思是 "并发"
        long begin = System.currentTimeMillis();
        Thread t1 = new Thread(() ->{
            int a = 0;
            for(long i = 0; i < count; i++) {
                a++;
            }
        });
 
        Thread t2 = new Thread(() ->{
            int a = 0;
            for(long i = 0; i < count; i++) {
                a++;
            }
        });
 
        t1.start();
        t2.start();
 
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
 
        long end = System.currentTimeMillis();
        System.out.println("多线程并发执行的时间:" + (end-begin) + "毫秒");
 
    }

我们知道,main、t1、t2 三个线程是随机调度执行的~

但是此处的需求是,希望让 t1、t2 都执行完了之后,main再进行计时,来统计执行时间~

就是希望 t1、t2 先执行完,main 后执行完~

咱们无法干预 调度器的行为(调度器还是该咋随机咋随机),但是可以让 main 线程进行等待~

就在 main 里分别调用了 t1.join() 和 t2.join() ~ 

  • t1.join() —> main阻塞,等待 t1 执行完~
  • t2.join() —> main阻塞,等待 t2 执行完~ 

当 t1、t2 都执行完了以后,main 解除阻塞,然后才能继续往下执行,才能执行完 

main 阻塞了,就不参与调度了,但是 t1、t2 仍然参与调度,它们的执行还是会 随机调度、交替执行的~ 

main 有点特殊,不太方便 join() 

一般情况下,想让谁阻塞,谁就调用 join() 即可  

如:要实现让 t2 等待 t1,main 等待 t2,就可以 main 去调用 t2.join(),t2 调用 t1.join() 即可 

package thread;
 
public class Demo11 {
    private static Thread t1 = null;
    private static Thread t2 = null;
 
    public static void main(String[] args) throws InterruptedException {
        System.out.println("main begin");
        t1 = new Thread(() -> {
            System.out.println("t1 begin");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t1 end");
        });
        t1.start();
 
        t2 = new Thread(() -> {
            System.out.println("t2 begin");
            try {
                t1.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t2 end");
        });
        t2.start();
 
        t2.join();
        System.out.println("main end");
    }
}

 

package thread;
//控制 main 先运行 t1,t1 执行完 再执行 t2
public class Demo12 {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("main begin");
        Thread t1 = new Thread(() -> {
            System.out.println("t1 begin");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t1 end");
        });
        t1.start();
 
        //等待 t1 结束
        t1.join();
 
        Thread t2 = new Thread(() -> {
            System.out.println("t2 begin");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t2 end");
        });
 
        t2.start();
        t2.join();
        System.out.println("main end");
    }
}

 

当然,join() 还有其他的版本 

 在实际的开发过程中,在一般情况下,都不会使用 "死等" 的方式,因为 "死等" 的方式有风险。

万一代码出了 bug 没控制好,就很容易让服务器 "卡死",无法继续工作。

更多的情况下是 等待的时候预期好最多等多久,超过时间了就需要做出一些措施。


3.4 获取到线程引用

为了对线程进行操作,就需要获取到线程的引用

这里的操作,就是指:线程等待、线程中断、获取各种线程的属性 

如果是 继承 Thread类,重写 run方法,可以直接在 run 中使用 this 即可获取到线程的实例 

但是,如果是 Runnable 或者 Lambda,this 就不行了(指向的就不是 Thread实例) 

更通用的方法是,使用 Thread.currentThread() ,currentThread() 是一个静态方法,其特点是 哪个线程来调用这个方法,得到的线程就是哪个实例 


3.5 休眠线程 - sleep()

sleep() 能够让线程休眠一会儿~

前面已经所介绍了 sleep() 的使用方法,现在来画图介绍一下 sleep() 到底是如何使用的~

注意:

如果写了一个 sleep(1000),那么也不一定 就会在1000ms 之后就上 CPU 运行~

1000ms 之后只是把这个 PCB 放回了就绪队列!!!至于说这个线程啥时候执行,还得看调度器的 调度

因此,sleep(1000) 意味着休眠时间不小于 1000ms,实际的休眠时间会略大于 1000ms,这个误差精度在 10ms 以下  


四、线程的状态

4.1 Java 线程中的基本状态

操作系统中 进程的状态 有 阻塞状态、就绪状态、执行状态~

而在 Java/JVM里 线程中也有一些状态,更是对此做出了一些细分~

  1. New:安排了工作,还未开始行动,创建了 Thread对象,但是还没有调用 start方法,系统内核里面没有线程~
  2. Runnable:就绪状态,包含了两个意思:
    1. 正在 CPU上运行
    2. 还没有在 CPU 上运行,但是已经准备好了
  3. Blocked:等待锁~
  4. Waiting:线程中调用了 wait方法~
  5. Time Waiting:线程中通过 sleep方法 进入的阻塞~
  6. Terminated:工作完成了,系统里面的线程已经执行完毕,销毁了(相当于线程的 run方法 执行完了),但是 Thread对象 还在 

 Blocked、Waiting、Time Waiting 三种状态都是 阻塞状态,只不过是产生阻塞状态的原因不一样,Java里面使用不同的状态来表示了 

package thread;
 
public class Demo13 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        });
 
        //在 t.start() 之前获取,获取到的是 线程还未创建的状态
        System.out.println(t.getState());
 
        t.start();
        t.join();
 
        //在 t.join() 之后获取,获取到的是线程已经结束之后的状态
        System.out.println(t.getState());
    }
}


4.2 线程之间的状态是如何转换的

 

主干道是:New => Runnable => Terminated 

在 Runnable状态会根据特定的代码进入支线任务,这些 "支线任务" 都是 "阻塞状态" 

这三种 "阻塞状态",进入的方式不一样,同时阻塞的时间也不同,被唤醒的方式也不同 

如:sleep() 等到时间到了自动唤醒,至于 wait() 和 synchronized() 是如何唤醒的以后会介绍的 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/45513.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kamiya丨Kamiya艾美捷小鼠血红蛋白ELISA说明书

Kamiya艾美捷小鼠血红蛋白ELISA预期用途&#xff1a; 小鼠血红蛋白ELISA是一种高灵敏度的双位点酶联免疫分析&#xff08;ELISA&#xff09;小鼠生物样品中血红蛋白的测定。仅供研究使用。 引言 血红蛋白&#xff08;HM&#xff09;是红细胞中的含铁氧转运蛋白。它吸收肺部的…

[附源码]计算机毕业设计springboot行程规划app

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Vue.js 加入高德地图的实现方法

一、功能需求 1.根据输入内容进行模糊查询&#xff0c;选择地址后在地图上插上标记&#xff0c;并更新经纬度坐标显示 2.在地图点击后&#xff0c;根据回传的左边更新地址信息和坐标显示 二、准备 1.申请高德地图账号&#xff0c;创建应用 2.在应用管理中 获得key 和安全密…

如何获取Power VR GPU数据

什么是Power VR PowerVR 是Imagination公司生产的一种GPU系列。早期的苹果A系列芯片优秀的GPU性能大多是基于Power VR GPU实现的。从早期的游戏机实体建模到现在的移动终端设备图形计算中都能够见到它的身影&#xff0c;虽中间有多次动荡&#xff0c;至今依旧在汽车HMI系统中占…

面板模型进行熵值法分析

背景说明 熵值法&#xff08;熵权法&#xff09;是一种研究指标权重的研究方法&#xff0c;比如有5个指标&#xff0c;分别为指标1到指标5&#xff0c;并且有很多样本&#xff08;比如100个样本&#xff09;&#xff0c;即100行*5列数据&#xff0c;此时研究该5个指标的权重分…

可编程直流电源的特点都有哪些呢?

可编程直流电源是用数字或编码器输入&#xff0c;非电位器调节电压、电流的直流电源。无论是在研发&#xff0c;还是生产测试中&#xff0c;可编程直流电源的宽范围电压输出都适于测试和分析组件、电路、模块和完整器件的特性。那么可编程直流电源的特点都有哪些呢?下面安泰测…

【Java系列】一篇文章阐述常见问题及解决方法

返回主篇章         &#x1f447; 【Java】才疏学浅小石Java问道之路 Java常见问题及解决方法问题1问题2问题3问题4问题1 问题提出&#xff1a; 解决问题&#xff1a; 源文件名不存在或者写错当前路径错误后缀名隐藏问题 问题2 问题提出&#xff1a; 解决问题&a…

加码中文 AIGC,IDEA 推出“盖亚计划”

作者 | 伍杏玲 出品 | CSDN 近年来&#xff0c;AIGC 话题火爆 AI 技术人的朋友圈。由于深度学习模型不断完善、开源模式推动&#xff0c;AIGC 的迭代速度呈爆发式发展。据 Gartner 发布的 2022 年需要探索的重要战略技术趋势显示&#xff0c;生成式 AI 是其中引人注目的人工智…

uniapp开发小程序-工作笔记

开发背景 公司开始安排本人开发微信小程序&#xff0c;于是乎研究了一点关于uniapp的api和注意的点。仅仅是个人笔记…可能有错误&#xff0c;希望各位指出。 uniapp开发小程序 应用生命周期 onLaunch 初始化完成时 只触发一次 onShow 后台进入前台 显示页面的时候 onHide 前台…

全志V853 NPU 转换部署 YOLO V5 模型

NPU 转换部署 YOLO V5 模型 本文以 YOLO v5s 模型为例&#xff0c;详述 ONNX 模型在 V853 平台的转换与部署的流程。 模型的准备 YOLO v5 目前开源于 Github&#xff0c;链接【GitHub - ultralytics/yolov5: YOLOv5 &#x1f680; in PyTorch > ONNX > CoreML > TF…

【Android App】实战项目之虚拟现实(VR)的全景相册(附源码和演示视频 可用于学习和大作业)

需要源码请点赞关注收藏后评论区留言私信~~~ 不管是绘画还是摄影&#xff0c;都是把三维的物体投影到平面上&#xff0c;其实仍旧呈现二维的模拟画面。 随着科技的发展&#xff0c;传统的成像手段越来越凸显出局限性&#xff0c;缘由在于人们需要一种更逼真更接近现实的技术&am…

基于有偏距离权值(Weighted cubic O-MOMS with warping)三次O-MOMS插值理论的图像超分辨重构研究-附Matlab程序

⭕⭕ 目 录 ⭕⭕✳️ 一、图像超分辨率重构原理✳️ 二、三次O-MOMS插值重构理论与实验分析✳️ 2.1 三次O-MOMS(Cubic O-MOMS)插值理论与实验验证✳️ 2.2 有偏距离三次O-MOMS插值重构理论与实验验证✳️ 2.3 权重三次O-MOMS插值理论与实验验证✳️ 2.4 有偏距离权值三次O-MOM…

[附源码]Python计算机毕业设计Django茶叶销售微信小程序

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

Hive 查看和修改 tez 容器的资源

1. 查看当前的配置 1.1 进入 AppMaster 或 History 进入运行中&#xff0c;或者运行完作业对应的 “Tracking URL”。以下示例是已经运行完的 job。 1.2 进入 tez-ui 进入 tez-ui 之后&#xff0c;点击 Configurations 1.3 查看配置 输入要查询的配置项&#xff0c;点击…

随smart登陆欧洲,亿咖通科技踏上出海新征程

随着全新smart精灵#1正式登陆欧洲&#xff0c;全球移动出行科技公司亿咖通科技同步向欧洲车主揭晓其搭载于新车上的下一代智能座舱系统&#xff0c;并正式将其出海战略向前推进关键一步&#xff0c;成为中国智能化出海的一座崭新里程碑。 全新smart精灵#1预计将于2022年底开始&…

π220N31兼容代替TI ISO1540DR 低功耗 3.0kVrms 双向I2C 隔离器

π220N31兼容代替TI ISO1540DR 低功耗 3.0kVrms 双向I2C 隔离器&#xff0c;I2C隔离器输入和输出采用二氧化硅(SiO2)介质隔离&#xff0c;可阻断高电压并防止噪声电流进入控制侧&#xff0c;避免电路干扰和损坏敏感器件。与光电耦合器相比&#xff0c;在功能、性能、尺寸和功耗…

2022年全国最新消防设施操作员模拟真题题库及答案

百分百题库提供消防设施操作员考试试题、消防设施操作员考试预测题、消防设施操作员考试真题、消防设施操作员证考试题库等,提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 124、消防电梯设置应符合下列哪些( )要求 A.消防电梯载重量不应小于800kg …

麦芽糖-阿奇霉素 maltose-Azithromycin

麦芽糖-阿奇霉素 maltose-Azithromycin 中文名称&#xff1a;麦芽糖-阿奇霉素 英文名称&#xff1a;maltose-Azithromycin 别称&#xff1a;阿奇霉素修饰麦芽糖&#xff0c;阿奇霉素-麦芽糖 PEG接枝修饰麦芽糖 麦芽糖-聚乙二醇-阿奇霉素 Azithromycin-PEG-maltose 阿…

@企业主们看过来,用华为云CDN给你的网页加个速

企业主们看过来&#xff0c;用华为云CDN给你的网页加个速 前段时间参加秋招的时候&#xff0c;被问到了一个问题&#xff0c;CND是干啥的&#xff0c;什么是CND&#xff0c;面试官问我这个问题的时候&#xff0c;我暗窃喜这不是我的强项吗&#xff01;&#xff01;&#xff01;…

【C++面向对象程序设计】CH5 继承与派生(续)——虚基类

目录 前言 一、虚基类的作用 二、虚基类的初始化 三、例【5.9】在【例5.8】中在teacher类和student类之上增加一个共同的基类person&#xff0c;人员的一些基本数据放在person中 四、多层多重继承用虚基类 五、虚基类的构造函数 六、多重继承如何工作 七、虚拟继承 八…