Java多线程--创建多线程的基本方式一:继承Thread类

文章目录

  • 一、创建和启动线程
    • (1)概述
    • (2)方式1:继承Thread类
      • 1、使用步骤
      • 2、举例
        • 2.1 案例1
        • 2.2 案例2
        • 2.3 案例3
      • 3、两个问题
        • 3.1 问题1
        • 3.2 问题2
      • 4、代码及总结
  • 二、练习
    • (1)方式一
    • (2)方式二

一、创建和启动线程

(1)概述

  • Java语言的JVM允许程序运行多个线程,使用java.lang.Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。 (创建一个线程可以理解为创建这个类的一个对象,一个对象对应一个线程
  • Thread类的特性
    • 每个线程都是通过某个特定Thread对象的run()方法来完成操作的,因此把run()方法体称为线程执行体
    • 通过该Thread对象的start()方法来启动这个线程,而非直接调用run()
    • 要想实现多线程,必须在主线程中创建新的线程对象。

image.png

(2)方式1:继承Thread类

1、使用步骤

Java通过继承Thread类来创建启动多线程的步骤如下:

  1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务
  2. 创建Thread子类的实例,即创建了线程对象
  3. 调用线程对象的start()方法来启动该线程

如下:

image.png

举例代码如下:

package com.atguigu.thread;
//自定义线程类
public class MyThread extends Thread {//定义指定线程名称的构造方法public MyThread(String name) {//调用父类的String参数的构造方法,指定线程的名称super(name);}/*** 重写run方法,完成该线程执行的逻辑*/@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(getName()+":正在执行!"+i);}}
}

测试类:

package com.atguigu.thread;public class TestMyThread {public static void main(String[] args) {//创建自定义线程对象1MyThread mt1 = new MyThread("子线程1");//开启子线程1mt1.start();//创建自定义线程对象2MyThread mt2 = new MyThread("子线程2");//开启子线程2mt2.start();//在主方法中执行for循环for (int i = 0; i < 10; i++) {System.out.println("main线程!"+i);}}
}

image.png

注意:

  1. 如果自己手动调用run()方法,那么就只是普通方法,没有启动多线程模式。
  2. run()方法由JVM调用,什么时候调用,执行的过程控制都有操作系统的CPU调度决定。
  3. 想要启动多线程,必须调用start方法。
  4. 一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上的异常“IllegalThreadStateException”。

2、举例

2.1 案例1

🌋描述:创建一个分线程1,用于遍历100以内的偶数。

🚗分析

<1> 创建一个继承于Thread类的子类

public class EvenNumberTest {}//1.创建一个继承于Thread类的子类
class PrintNumber extends Thread{}

<2> 重写Thread类的run() —>将此线程要执行的操作,声明在此方法体中

public class EvenNumberTest {}//1.创建一个继承于Thread类的子类
class PrintNumber extends Thread{//2.重写Thread类的run() --->将此线程要执行的操作,声明在此方法体中@Overridepublic void run() {for (int i = 1; i <= 100; i++) {if(i%2==0){System.out.println(i);}}}
}

<3> 创建当前Thread的子类的对象

public class EvenNumberTest {public static void main(String[] args) {//3.创建当前Thread的子类的对象PrintNumber t1=new PrintNumber();}
}

<4>通过对象调用start(): 1.启动线程 2.调用当前线程的run()

public class EvenNumberTest {public static void main(String[] args) {//3.创建当前Thread的子类的对象PrintNumber t1=new PrintNumber();//4.通过对象调用start(): 1.启动线程 2.调用当前线程的run()t1.start();}
}//1.创建一个继承于Thread类的子类
class PrintNumber extends Thread{//2.重写Thread类的run() --->将此线程要执行的操作,声明在此方法体中@Overridepublic void run() {for (int i = 1; i <= 100; i++) {if(i%2==0){System.out.println(i);}}}
}

当前PrintNumber类里面没有重写start方法,这就意味着调用的是父类Thread里面的start方法。

这个start()方法有什么作用呢?

image.png

所以start方法有两个作用:1.启动线程 2.调用当前线程的run()方法。

在刚才的代码中,t1调用start()方法,这里的start()方法是父类中的

此时调用了当前线程的run()方法,这个方法在Thread类里面定义的,并且在子类PrintNumber里面被重写了,所以父类中的方法被覆盖了,此时调用的就是子类的run()方法

整体来看,调用t1.start(),子类中的run()方法就被执行了。


🌱整体代码

package yuyi01.thread;/*** ClassName: EvenNumberTest* Package: yuyi01.thread* Description:*      创建一个分线程1,用于遍历100以内的偶数* @Author 雨翼轻尘* @Create 2024/1/19 0019 11:57*/
public class EvenNumberTest {public static void main(String[] args) {//3.创建当前Thread的子类的对象PrintNumber t1=new PrintNumber();//4.通过对象调用start(): 1.启动线程 2.调用当前线程的run()t1.start();}
}//1.创建一个继承于Thread类的子类
class PrintNumber extends Thread{//2.重写Thread类的run() --->将此线程要执行的操作,声明在此方法体中@Overridepublic void run() {for (int i = 1; i <= 100; i++) {if(i%2==0){System.out.println(i);}}}
}

🍺输出结果(部分)

image.png

2.2 案例2

刚才并没有感觉到线程的存在啊,现在修改一下代码:

package yuyi01.thread;public class EvenNumberTest {public static void main(String[] args) {//3.创建当前Thread的子类的对象PrintNumber t1=new PrintNumber();//4.通过对象调用start(): 1.启动线程 2.调用当前线程的run()t1.start();//main()方法所在的线程执行的操作:for (int i = 1; i <= 100; i++) {if(i%2==0){System.out.println(i+"main()做的事情");}}}
}//1.创建一个继承于Thread类的子类
class PrintNumber extends Thread{//2.重写Thread类的run() --->将此线程要执行的操作,声明在此方法体中@Overridepublic void run() {for (int i = 1; i <= 100; i++) {if(i%2==0){System.out.println(i);}}}
}

此时有两个线程:

image.png

再次执行代码:(部分)

image.png

此时它们没有交互,如果数字足够多,会有交叉的数据出现,就是交替执行

这说明两个线程都在执行,前面的执行一下,后面的执行一下。谁先执行都有可能。这里就体现出了两个不同的线程

2.3 案例3

其实这里还有一种方式可以看出来是两个线程在交替执行,需要用到一个方法,后面再说,这里先用一下。

Thread有个方法叫做currentThread()获取当前执行的线程;然后它又有一个getName()方法,即获取线程的名字。

即:

Thread.currentThread().getName()

🌱代码

package yuyi01.thread;/*** ClassName: EvenNumberTest* Package: yuyi01.thread* Description:*      创建一个分线程1,用于遍历100以内的偶数* @Author 雨翼轻尘* @Create 2024/1/19 0019 11:57*/
public class EvenNumberTest {public static void main(String[] args) {//3.创建当前Thread的子类的对象PrintNumber t1=new PrintNumber();//4.通过对象调用start(): 1.启动线程 2.调用当前线程的run()t1.start();//main()方法所在的线程执行的操作:for (int i = 1; i <= 10000; i++) {if(i%2==0){System.out.println(Thread.currentThread().getName() +":" +i+"**********");}}}
}//1.创建一个继承于Thread类的子类
class PrintNumber extends Thread{//2.重写Thread类的run() --->将此线程要执行的操作,声明在此方法体中@Overridepublic void run() {for (int i = 1; i <= 10000; i++) {if(i%2==0){System.out.println(Thread.currentThread().getName() +":" +i);}}}
}

🍺输出结果(部分)

image.png

可以看到,现在的执行结果,前面都有自己线程的名字。

t1线程也有自己的线程名字,默认叫Thread-0。因为此时new的是当前类PrintNumber的对象,默认的是调用super(),是父类Thread里面的构造器,如下:

image.png

所以,当我们不给线程起名字的时候,默认第一个线程就是Thread-0,第二个线程就是Thread-1

注意下面:

image.png

3、两个问题

3.1 问题1

☕问题1

启动线程,包括调用上面的run方法都用的是start()

那么能否使用t1.run()来替换t1.start()的调用,实现分线程的创建和调用?

🍰分析

比如现在这样写:

image.png

运行看一下:

image.png

可以看到不仅没有交替执行,而且还都是main,没有Thread-0了。它认为run()方法是主线程main做的。

此时就是主线程造了一个t1对象,然后就调用了run()这个普通的方法,执行完之后再执行循环输出。只有一条线程了。这就不是多线程问题了。


再举个例子,看看下面的代码是不是多线程:

public class SingleThread {public void method1(String str) {System.out.println(str);}public void method2(String str) {method1(str);}public static void main(String[] args) { //main线程SingleThread s = new SingleThread();s.method2("hello!");}}

首先main方法进去,造了一个当前类的一个对象s,这个对象s调用method2(),method2()里面调用method1()。这里是单线程
判断单线程还是多线程,就看能不能拿一条线穿起来。

比如此时就可以拿一条线穿起来,只有一条执行路径,那就是一个线程,即单线程。如下:

image.png

之前的那个例子,也是类似,就是一个线程。如下:(用鼠标手画的有点不堪入目哈哈)

image.png

start()不一样,一个作用是启动线程,然后是调用run方法

若是仅仅调用run方法的话,线程并没有启动,相当于还是只有主线程。

所以,不能使用t1.run()来替换t1.start()的调用。

3.2 问题2

☕问题2

比如现在启动了一个线程,调用一下start(),打印了1000以内的偶数。代码如下:

package yuyi01.thread;public class EvenNumberTest {public static void main(String[] args) {//3.创建当前Thread的子类的对象PrintNumber t1=new PrintNumber();//4.通过对象调用start(): 1.启动线程 2.调用当前线程的run()t1.start();//main()方法所在的线程执行的操作:for (int i = 1; i <= 1000; i++) {if(i%2==0){System.out.println(Thread.currentThread().getName() +":" +i+"**********");}}}
}//1.创建一个继承于Thread类的子类
class PrintNumber extends Thread{//2.重写Thread类的run() --->将此线程要执行的操作,声明在此方法体中@Overridepublic void run() {for (int i = 1; i <= 1000; i++) {if(i%2==0){System.out.println(Thread.currentThread().getName() +":" +i);}}}
}

现在需要再创建一个分线程,同样要去遍历一下1000以内的偶数

那么此时要怎样去做呢?

🍰分析

直接用t1再次调用start()方法可以吗?如下:

image.png

运行结果:

image.png

可以看到,出现了IllegalThreadStateException的异常。

当我们首次调用start()方法的时候,threadStatus的值是0 ,就没有抛异常。当我们再次调用start()之后,threadStatus状态就不是0了,便会抛异常。

image.png

所以,线程不能start()多次。

不能让已经start()的线程,再次执行start()操作,否则报IllegalThreadStateException非法线程状态的异常。


🎲解决方案

既然上面的方法不行,那么究竟应该怎么做呢?

需要重新创建一个对象,类PrintNumber不需要重新创建了,因为要做的事情一样,都是遍历1000以内的偶数。

所以现在只需要再建立一个对象,然后用这个新的对象去调用start()方法即可。

如下:

image.png

🌱整体代码

package yuyi01.thread;public class EvenNumberTest {public static void main(String[] args) {//3.创建当前Thread的子类的对象PrintNumber t1=new PrintNumber();//4.通过对象调用start(): 1.启动线程 2.调用当前线程的run()t1.start();/** 问题2:再提供一个分线程,用于100以内偶数的遍历** 注意:不能让已经start()的线程,再次执行start()操作,否则报IllegalThreadStateException非法线程状态的异常。* *///t1.start();PrintNumber t2=new PrintNumber();t2.start();//main()方法所在的线程执行的操作:for (int i = 1; i <= 1000; i++) {if(i%2==0){System.out.println(Thread.currentThread().getName() +":" +i+"**********");}}}
}//1.创建一个继承于Thread类的子类
class PrintNumber extends Thread{//2.重写Thread类的run() --->将此线程要执行的操作,声明在此方法体中@Overridepublic void run() {for (int i = 1; i <= 1000; i++) {if(i%2==0){System.out.println(Thread.currentThread().getName() +":" +i);}}}
}

🍺输出结果(部分)

image.png

输出默认的线程名也很好理解,造的第二个对象就是Thread-1

从上面的输出结果可以看到,三个线程交替执行。

4、代码及总结

线程的创建方式一:继承Thread类

步骤:

① 创建一个继承于Thread类的子类

② 重写Thread类的run() —>将此线程要执行的操作,声明在此方法体中

③ 创建当前Thread的子类的对象

④ 通过对象调用start(): 1.启动线程 2.调用当前线程的run()方法

start()是父类Thread的方法。

🌱整体代码总结

package yuyi01.thread;/*** ClassName: EvenNumberTest* Package: yuyi01.thread* Description:*      创建一个分线程1,用于遍历100以内的偶数* @Author 雨翼轻尘* @Create 2024/1/19 0019 11:57*/
public class EvenNumberTest {public static void main(String[] args) {//3.创建当前Thread的子类的对象PrintNumber t1=new PrintNumber();//4.通过对象调用start(): 1.启动线程 2.调用当前线程的run()t1.start();/*问题1:能否使用t1.run()来替换t1.start()的调用,实现分线程的创建和调用?    不能*///t1.run();/** 问题2:再提供一个分线程,用于100以内偶数的遍历** 注意:不能让已经start()的线程,再次执行start()操作,否则报IllegalThreadStateException非法线程状态的异常。* *///t1.start();PrintNumber t2=new PrintNumber();t2.start();//main()方法所在的线程执行的操作:for (int i = 1; i <= 1000; i++) {if(i%2==0){System.out.println(Thread.currentThread().getName() +":" +i+"**********");}}}
}//1.创建一个继承于Thread类的子类
class PrintNumber extends Thread{//2.重写Thread类的run() --->将此线程要执行的操作,声明在此方法体中@Overridepublic void run() {for (int i = 1; i <= 1000; i++) {if(i%2==0){System.out.println(Thread.currentThread().getName() +":" +i);}}}
}

🍺输出结果(部分)

image.png

二、练习

🌋题目描述

练习:创建两个分线程,其中一个线程遍历100以内的偶数,另一个线程遍历100以内的奇数。

(1)方式一

🍰分析

【回顾】

线程的创建方式一:继承Thread类

步骤:

① 创建一个继承于Thread类的子类

② 重写Thread类的run() —>将此线程要执行的操作,声明在此方法体中

③ 创建当前Thread的子类的对象

④ 通过对象调用start(): 1.启动线程 2.调用当前线程的run()方法

【分析】

现在是两个线程,做的事情不一样。run方法里面执行的方法体就不一样了。

两个分线程,这里就不要使用主线程了。

既然两个线程指定的事情不一样,那就写两个run,这就意味着要声明两个类了(要是两个线程做的事情一样,那就只用写一个类,就像上面的案例)。

🌱代码(方式一)

package yuyi01.thread.exer1;/*** ClassName: PrintNumberTest* Package: yuyi01.thread.exer1* Description:** @Author 雨翼轻尘* @Create 2024/1/19 0019 22:38*/
public class PrintNumberTest {public static void main(String[] args) {//方式一EvenNumberPrint t1=new EvenNumberPrint();OddNumberPrint t2=new OddNumberPrint();t1.start();t2.start();}
}//打印偶数
class EvenNumberPrint extends Thread {@Overridepublic void run() {for (int i = 1; i <= 100; i++) {if (i % 2 == 0) {System.out.println(Thread.currentThread().getName() + ":"+i);}}}
}//打印奇数
class OddNumberPrint extends Thread {@Overridepublic void run() {for (int i = 1; i <= 100; i++) {if (i % 2 != 0) {System.out.println(Thread.currentThread().getName() + "-->"+i);}}}
}

🍺输出结果(部分)

image.png

(2)方式二

🍰分析

上面的方式一是一种标准写法,之前还讲过匿名子类的方式,需要提供Thread子类的对象。

所以直接new一个Thread(),因为是匿名子类,后面直接加一对大括号,这样对象就造好了,如下:

new Thread(){}

这时可以声明为t1,然后通过t1去调start(),如下:

Thread t1=new Thread(){};
t1.start();

当然,我们可以不声明它为t1,直接在后面.start(),如下:

new Thread(){}.start();

在大括号里面,可以将run()方法重写一下。如下:

//方式二
new Thread(){//打印偶数public void run() {for (int i = 1; i <= 100; i++) {if (i % 2 == 0) {System.out.println(Thread.currentThread().getName() + ":"+i);}}}
}.start();new Thread(){//打印奇数public void run() {for (int i = 1; i <= 100; i++) {if (i % 2 != 0) {System.out.println(Thread.currentThread().getName() + "-->"+i);}}}
}.start();

方法二可以叫做,创建Thread类的匿名子类的匿名对象,造完对象之后还顺便将start()方法给调用了。

🌱代码(方式二)

package yuyi01.thread.exer1;/*** ClassName: PrintNumberTest* Package: yuyi01.thread.exer1* Description:** @Author 雨翼轻尘* @Create 2024/1/19 0019 22:38*/
public class PrintNumberTest {public static void main(String[] args) {//方式二new Thread(){//打印偶数public void run() {for (int i = 1; i <= 100; i++) {if (i % 2 == 0) {System.out.println(Thread.currentThread().getName() + ":"+i);}}}}.start();new Thread(){//打印奇数public void run() {for (int i = 1; i <= 100; i++) {if (i % 2 != 0) {System.out.println(Thread.currentThread().getName() + "-->"+i);}}}}.start();}
}

🍺输出结果(部分)

image.png

可以看到,方法一与方法二的效果一样。

若是以后,有一个临时需求,造一个线程做个事情,可能都不会按照方式一那么正规地写,都是采用匿名的方式做一下。

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

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

相关文章

索引的概述和性能分析

索引index&#xff0c;是一种有序的数据结构&#xff0c;可以高效的获取数据&#xff0c;在数据库中维护着满足查找特定算法的数据结构&#xff0c;就是索引 无索引的情况&#xff0c;查询数据时会全表扫描&#xff0c;效率极低 索引结构 &#xff08;1&#xff09;二叉树&…

python解决求二叉树的最长同值路径问题

对于给定的一颗二叉树&#xff0c;需要找到最长的路径&#xff0c;并且该路径上的每个节点具有相同的值的问题&#xff0c;对于寻找到的这条路径可以经过根节点也可以不经过根节点&#xff0c;两个节点之间的路径长度是由他们的变数来表示的&#xff0c;给定如下图的二叉树 添加…

【 Qt 快速上手】-②- Qt 环境搭建

文章目录 1. Qt 开发工具概述1.1 Qt Creator 介绍1.2 Visual Studio 介绍1.3 Eclipse 介绍 2. Qt SDK 的下载与安装2.1 Qt SDK 的下载2.2 Qt SDK 的安装2.3 验证 Qt SDK 安装是否成功2.4 Qt 环境变量配置 1. Qt 开发工具概述 Qt 开发环境需要安装三个部分&#xff1a; C编译器…

小伙频繁发朋友圈引发分手,拼命“晒“生活是否成为一种病态行为?

根据《西湖之声》1月19日的报道&#xff0c;一个小伙子因为一天发十几条朋友圈而引起了他女友的不满&#xff0c;女友甚至提出了分手。现如今&#xff0c;朋友圈已经成为每个人自我形象展示的平台&#xff0c;认真发朋友圈不仅是表达自己的一种方式&#xff0c;也是一种自我释放…

redis缓存和本地缓存的应用设计

数据查询顺序 一级缓存&#xff1a;本地缓存 -》二级缓存&#xff1a;redis缓存 -》数据库 本地缓存和分布式缓存 本地缓存&#xff1a;基于jvm, 意思是程序放在哪&#xff0c;数据就存储在哪&#xff0c;不需要网络请求&#xff0c;特别快&#xff0c;但是需要占用jvm的内存…

Python利用Excel读取和存储测试数据完成接口自动化教程

http_request2.py用于发起http请求 #读取多条测试用例#1、导入requests模块import requests#从 class_12_19.do_excel1导入read_data函数from do_excel2 import read_datafrom do_excel2 import write_datafrom do_excel2 import count_case#定义http请求函数COOKIENonedef ht…

Spring Boot Aop 执行顺序

Spring Boot Aop 执行顺序 1. 概述 在 spring boot 项目中&#xff0c;使用 aop 增强&#xff0c;不仅可以很优雅地扩展功能&#xff0c;还可以让一写多用&#xff0c;避免写重复代码&#xff0c;例如&#xff1a;记录接口耗时&#xff0c;记录接口日志&#xff0c;接口权限&…

python 自动化模块 - pyautogui初探

python 自动化模块 - pyautogui 引言一、安装测试二、简单使用三、常用函数总结 引言 在画图软件中使用pyautogui拖动鼠标&#xff0c;画一个螺旋式的正方形 - (源码在下面) PyAutoGUI允许Python脚本控制鼠标和键盘&#xff0c;以自动化与其他应用程序的交互。API的设计非常简…

限流算法之流量控制的平滑之道:滑动时间窗算法

文章目录 引言简介优点缺点样例样例图样例代码 应用场景结论 引言 在互联网应用中&#xff0c;流量控制是一个重要的组件&#xff0c;用于防止系统过载和保护核心资源。常见的限流算法包括固定窗口算法和滑动时间窗算法。本文将重点介绍滑动时间窗算法&#xff0c;并分析其优缺…

基于Abaqus的三种钢筋混凝土梁数值模拟对比研究

混凝土结构抗压强度高&#xff0c;而抗拉强度大约只有其十分之一&#xff0c;在受到竖向荷载&#xff08;包括自重&#xff09;作用下&#xff0c;梁下部会产生拉应力&#xff0c;上部产生压应力&#xff0c;而由于其抗拉强度低&#xff0c;因此很小的荷载即可导致梁下部开裂&a…

Mysql中的日志系统

文章目录 1. 慢查询日志&#xff08;Slow Query Log&#xff09;1.1 是否开启慢查询日志1.2 开启慢查询日志&#xff0c;设置时间阈值1.2.1 修改文件my.ini1.2.2 重启mysql后配置生效 1.3 查看慢查询日志1.3.1 直接用文本编辑器打开1.3.2 使用mysqldumpslow进行分析 2. InnoDB …

C语言从入门到实战——文件操作

文件操作 前言一、 为什么使用文件二、 什么是文件2.1 程序文件2.2 数据文件2.3 文件名 三、 二进制文件和文本文件四、 文件的打开和关闭4.1 流和标准流4.1.1 流4.1.2 标准流 4.2 文件指针4.3 文件的打开和关闭4.4 文件的路径 五、 文件的顺序读写5.1 顺序读写函数介绍fgetcfp…

【音视频原理】图像相关概念 ③ ( RGB 色彩简介 | RGB 排列 | YUV 色彩简介 | YUV 编码好处 )

文章目录 一、RGB 色彩1、RGB 色彩简介2、RGB 排列 二、YUV 色彩1、YUV 色彩简介2、YUV 编码好处 一、RGB 色彩 1、RGB 色彩简介 RGB 是 计算机 中的 颜色编码方法 , 红 ( R ) / 绿 ( G ) / 蓝 ( B ) 三个颜色通道 可以设置不同的值 , 每个 通道 的 颜色值都可以取值 0 ~ 255 ,…

【已解决】namespace “Ui“没有成员 xxx

先说笔者遇到的问题&#xff0c;我创建一个QWidget ui文件&#xff0c;然后编辑的七七八八后&#xff0c;想要用.h与.cpp调用其&#xff0c;编译通过&#xff0c;结果报了这个错误&#xff0c;本方法不是普适性&#xff0c;但是确实解决了这个鸟问题。 问题来源 搭建ui后&…

数据分析师不加班的秘密,三行代码教你Python自动发送邮件

作为一名数据分析师&#xff0c;每天除了做数据日报&#xff0c;还要编写邮件正文和添加数据日报附件&#xff0c;发送给对应的负责人及销售个人&#xff0c;一般一封邮件应包含邮箱账户名、邮箱密码、发送人邮箱、邮件主题以及邮件正文。 本文借助Python工具&#xff0c;使用y…

MyBatis框架基础到进阶

1、为什么要学习MyBatis 如果没有MyBatis框架&#xff0c;我们依靠JDBC和连接池已经能够很好的和数据库进行交互了&#xff0c;而学习MyBatis框架最核心的原因是为了减少SQL语句对代码的侵入性。 因为在过往不管是使用连接池还是JDBC Templete&#xff0c;所有的SQL语句都写在代…

java.sql.SQLException: Failed to fetch schema of XXX 问题

遇到问题&#xff1a;java.sql.SQLException: Failed to fetch schema of pay_record表 i.s.r.d.s.s.c.AbstractTableMetaCache : get table meta of the table pay_record error: Failed to fetch schema of pay_record 原因分析&#xff1a; springcloud项目中使用了se…

上海亚商投顾:沪指冲高回落 旅游板块全天强势

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 沪指昨日冲高回落&#xff0c;创业板指跌近1%&#xff0c;北证50指数跌超3%。旅游、零售板块全天强势&#xf…

python类继承之__init__函数覆盖问题

目录 1.问题描述 2.代码演示 3.总结 在Python这个广受欢迎的编程语言中&#xff0c;类继承是一项强大而精巧的特性。通过类继承&#xff0c;我们可以构建出更加灵活、可重用和易维护的代码&#xff0c;同时实现代码的模块化和扩展性。 但是如果对于熟悉C和java的人而言&…

R语言学习 case2:人口金字塔图

step1&#xff1a;导入库 library(ggplot2) library(ggpol) library(dplyr)step2&#xff1a;读取数据 data <- readxl::read_xlsx("data_new.xlsx", sheet 1) datareadxl::read_xlsx() 是 readxl 包中的函数&#xff0c;用于读取Excel文件。 step3&#xff1…