网站建设问卷调查/抖音seo代理
文章目录
- 前言
- 1. if语句
- 字节码的解析
- 2. for循环
- 字节码的解析
- 3. while循环
- 4. switch语句
- 5. try-catch语句
- 6. i++ 和++i的字节码
- 7. try-catch-finally
- 8. 参考文档
前言
上一章我们聊了《JVM字节码指令详解》 。本章我们学以致用,聊一下我们常见的一些java语句的特性底层是如何实现。
1. if语句
if语句是我们最常用的判断语句之一,它的底层实现原理是什么呢?可以通过反编译字节码来分析一下。
假设我们有以下的java代码:
public class IfStatement {public static void main(String[] args) {int a = 10;if (a > 0) {System.out.println("a is positive");} else {System.out.println("a is negative or zero");}}
}
可以使用javap命令来反编译字节码:
javap -c IfStatement.class
输出结果如下:
public class IfStatement {// 构造方法public IfStatement();Code:0: aload_0 // 将局部变量表中的第0个元素(通常是this引用)入栈1: invokespecial #1 // 调用父类Object的构造方法4: return // 方法返回// main方法public static void main(java.lang.String[]);Code:0: bipush 10 // 将10压入栈顶2: istore_1 // 将栈顶元素(10)存入局部变量表的第1个位置3: iload_1 // 将局部变量表的第1个位置的元素(10)入栈4: ifle 17 // 判断栈顶元素(10)是否小于等于0,如果是则跳转到指令177: getstatic #2 // 获取System类的out字段,类型是PrintStream10: ldc #3 // 将字符串"a is positive"压入栈顶12: invokevirtual #4 // 调用PrintStream的println方法输出字符串15: goto 23 // 无条件跳转到指令2318: getstatic #2 // 获取System类的out字段,类型是PrintStream21: invokevirtual #5 // 调用PrintStream的println方法输出局部变量表第1个位置的元素(10)24: return // 方法返回
}
字节码的解析
- 在main方法中,首先将10压入栈顶,然后将其存入局部变量表的第1个位置。然后将局部变量表的第1个位置的元素(10)入栈,判断其是否小于等于0,如果是则跳转到指令17,否则执行下一条指令。在指令7-15中,它获取System的out字段,将字符串"a is positive"压入栈顶,然后调用println方法输出这个字符串,最后无条件跳转到指令23。在指令18-21中,它获取System的out字段,然后调用println方法输出局部变量表第1个位置的元素(10)。最后,main方法返回。
2. for循环
for循环是我们常用的循环语句之一,它的底层实现原理是什么呢?我们还是可以通过反编译字节码来分析一下。
假设我们有以下的java代码:
public class ForLoop {public static void main(String[] args) {for (int i = 0; i < 10; i++) {System.out.println("i = " + i);}}
}
可以使用javap命令来反编译字节码:
javap -c ForLoop.class
这是一个包含for循环的Java类ForLoop的字节码输出,下面是中文注释:
public class ForLoop {// 构造方法public ForLoop();Code:0: aload_0 // 将局部变量表中的第0个元素(通常是this引用)入栈1: invokespecial #1 // 调用父类Object的构造方法4: return // 方法返回// main方法public static void main(java.lang.String[]);Code:0: iconst_0 // 将0压入栈顶1: istore_1 // 将栈顶元素(0)存入局部变量表的第1个位置2: iload_1 // 将局部变量表的第1个位置的元素(0)入栈3: bipush 10 // 将10压入栈顶5: if_icmpge 19 // 如果局部变量表的第1个位置的元素(0)大于等于栈顶元素(10),则跳转到指令198: getstatic #2 // 获取System类的out字段,类型是PrintStream11: new #3 // 创建一个StringBuilder类的对象14: dup // 复制栈顶元素,此时栈顶有两个相同的StringBuilder对象引用15: invokespecial #4 // 调用StringBuilder类的构造函数初始化对象18: ldc #5 // 将字符串"i ="压入栈顶20: invokevirtual #6 // 调用StringBuilder的append方法将字符串添加到StringBuilder23: iload_1 // 将局部变量表的第1个位置的元素(0)入栈24: invokevirtual #7 // 调用StringBuilder的append方法将数字添加到StringBuilder27: invokevirtual #8 // 调用StringBuilder的toString方法将StringBuilder转化为字符串30: invokevirtual #9 // 调用PrintStream的println方法输出字符串33: iinc 1, 1 // 将局部变量表的第1个位置的元素(0)增加136: goto 2 // 无条件跳转到指令2,形成循环39: return // 方法返回LineNumberTable:line 3: 0line 4: 8line 3: 33line 6: 39StackMapTable: number_of_entries = 2frame_type = 252 /* append */offset_delta = 2locals = [ int, int ]stack = []frame_type = 250 /* chop */offset_delta = 36
}
字节码的解析
可以看到,for循环的底层实现是通过if_icmpge指令来实现的。在本例中,当i小于10时,会执行第8行的输出语句;否则,会跳转到第39行,结束循环。
- 在main方法中,首先将0压入栈顶,然后将其存入局部变量表的第1个位置。接下来是一个循环,循环条件是局部变量表的第1个位置的元素小于10。在循环体中,它首先获取System的out字段,然后创建一个StringBuilder对象并初始化,然后将字符串"i ="和局部变量表的第1个位置的元素添加到StringBuilder,然后将StringBuilder转化为字符串,然后调用println方法输出字符串。在循环体结束时,它将局部变量表的第1个位置的元素加1,然后无条件跳转到指令2,形成循环。当循环结束时,main方法返回。
3. while循环
while循环是我们常用的循环语句之一,它的底层实现原理是什么呢?我们还是可以通过反编译字节码来分析一下。
假设我们有以下的java代码:
public class WhileLoop {public static void main(String[] args) {int i = 0;while (i < 10) {System.out.println("i = " + i);i++;}}
}
可以使用javap命令来反编译字节码:
javap -c WhileLoop.class
输出结果如下:
public class WhileLoop {public WhileLoop();Code:0: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]);Code:0: iconst_01: istore_12: iload_13: bipush 105: if_icmpge 198: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;11: new #3 // class java/lang/StringBuilder14: dup15: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V18: ldc #5 // String i =20: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;23: iload_124: invokevirtual #7 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;27: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;30: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V33: iinc 1, 136: goto 239: returnLineNumberTable:line 3: 0line 4: 2line 5: 8line 6: 33line 5: 36line 8: 39StackMapTable: number_of_entries = 2frame_type = 252 /* append */offset_delta = 2locals = [ int, int ]stack = []frame_type = 250 /* chop */offset_delta = 36
}
可以看到,while循环的底层实现也是通过if_icmpge指令来实现的。在本例中,当i小于10时,会执行第8行的输出语句;否则,会跳转到第39行,结束循环。
4. switch语句
switch语句是我们常用的分支语句之一,它的底层实现原理是什么呢?我们还是可以通过反编译字节码来分析一下。
假设我们有以下的java代码:
public class SwitchStatement {public static void main(String[] args) {int i = 2;switch (i) {case 1:System.out.println("i is 1");break;case 2:System.out.println("i is 2");break;case 3:System.out.println("i is 3");break;default:System.out.println("i is neither 1, 2 nor 3");break;}}
}
可以使用javap命令来反编译字节码:
javap -c SwitchStatement.class
输出结果如下:
public class SwitchStatement {public SwitchStatement();Code:0: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]);Code:0: iconst_21: istore_12: iload_13: tableswitch { // 1 to 31: 282: 403: 52default: 64}28: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;31: ldc #3 // String i is 133: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V36: goto 7140: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;43: ldc #5 // String i is 245: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V48: goto 7152: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;55: ldc #6 // String i is 357: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V60: goto 7164: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;67: invokevirtual #7 // Method java/io/PrintStream.println:()V70: return71: returnLineNumberTable:line 3: 0line 4: 2line 5: 28line 6: 40line 7: 52line 8: 64line 9: 70line 7: 71StackMapTable: number_of_entries = 5frame_type = 252 /* append */offset_delta = 28locals = [ int ]frame_type = 252 /* append */offset_delta = 11locals = [ int ]frame_type = 252 /* append */offset_delta = 12locals = [ int ]frame_type = 252 /* append */offset_delta = 12locals = [ int ]frame_type = 252 /* append */offset_delta = 3locals = [ int ]
可以看到,switch语句的底层实现是通过tableswitch指令来实现的。在本例中,当i等于1时,会执行第28行的输出语句;当i等于2时,会执行第40行的输出语句;当i等于3时,会执行第52行的输出语句;否则,会执行第64行的输出语句。
5. try-catch语句
try-catch语句是我们常用的异常处理语句之一,它的底层实现原理是什么呢?我们还是可以通过反编译字节码来分析一下。
假设我们有以下的java代码:
public class TryCatchStatement {public static void main(String[] args) {try {int[] arr = new int[3];arr[4] = 5;} catch (ArrayIndexOutOfBoundsException e) {System.out.println("Array index out of bounds!");}}
}
可以使用javap命令来反编译字节码:
javap -c TryCatchStatement.class
输出结果如下:
public class TryCatchStatement {public TryCatchStatement();Code:0: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]);Code:0: iconst_01: newarray int3: astore_14: aload_15: iconst_46: iconst_57: iastore8: goto 1911: astore_112: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;15: ldc #3 // String Array index out of bounds!17: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V20: returnException table:from to target type0 8 11 Class java/lang/ArrayIndexOutOfBoundsExceptionLineNumberTable:line 3: 0line 4: 4line 5: 11line 6: 12line 7: 20StackMapTable: number_of_entries = 2frame_type = 34 /* same */frame_type = 1 /* same_locals_1_stack_item */stack = [ class java/lang/ArrayIndexOutOfBoundsException ]
}
可以看到,try-catch语句的底层实现是通过异常表来实现的。在本例中,当数组下标越界时,会执行第12行的输出语句;否则,会跳转到第20行,继续执行。
6. i++ 和++i的字节码
i++
和 ++i
在语义上有些许不同,在字节码层面也有所体现。下面是它们的字节码层面的解释:
假设 i
是局部变量表的索引为1的变量。
i++
的伪字节码:
iload_1 // 从局部变量表中加载变量 i 到操作数栈顶
iinc 1 by 1 // 将局部变量表中的变量 i 增加1
++i
的伪字节码:
iinc 1 by 1 // 将局部变量表中的变量 i 增加1
iload_1 // 从局部变量表中加载变量 i 到操作数栈顶
可以看到,i++
与 ++i
的主要区别在于加载和增加操作的顺序不同。i++
是先将 i
加载到操作数栈顶,然后再增加 i
的值;而 ++i
是先增加 i
的值,然后再将 i
加载到操作数栈顶。这就解释了 i++
和 ++i
在语义上的不同:i++
是先取值后加1,++i
是先加1后取值。
7. try-catch-finally
在 Java 字节码中,try-catch-finally 结构主要通过异常表(Exception Table)来实现。Java 字节码并没有专门的指令来表示 try、catch 或者 finally 块。相反,它通过在异常表中记录 try 块的开始和结束位置、catch 块的开始位置和要捕获的异常类型,以实现异常处理的流程。
下面是一个简单的 try-catch-finally 代码例子:
void test() {try {System.out.println("try block");throw new Exception();} catch (Exception e) {System.out.println("catch block");} finally {System.out.println("finally block");}
}
对应的字节码指令
0: getstatic #2 // 获取 java/lang/System 类的 out 字段,是 PrintStream 类型
3: ldc #3 // 将常量池中的 "try block" 字符串压入栈顶
5: invokevirtual #4 // 调用 PrintStream 类的 println 方法输出字符串
8: new #5 // 创建一个 java/lang/Exception 类的对象
11: dup // 复制栈顶的元素,此时栈顶有两个相同的异常对象引用
12: invokespecial #6 // 调用 Exception 类的构造函数初始化对象
15: athrow // 抛出栈顶的异常对象
16: astore_1 // 捕获异常并存入局部变量表的第1个位置
17: getstatic #2 // 获取 java/lang/System 类的 out 字段,是 PrintStream 类型
20: ldc #7 // 将常量池中的 "catch block" 字符串压入栈顶
22: invokevirtual #4 // 调用 PrintStream 类的 println 方法输出字符串
25: jsr 26 // 无条件跳转到指令26(finally块的起始位置)
28: goto 34 // 执行完finally块后,跳过 catch 块剩下的代码,进入下一个处理流程
31: astore_2 // 捕获从finally块抛出的异常并存入局部变量表的第2个位置
32: jsr 26 // 无条件跳转到指令26(finally块的起始位置)
35: aload_2 // 从局部变量表的第2个位置加载异常对象至栈顶
36: athrow // 再次抛出该异常对象
37: astore_3 // 捕获异常并存入局部变量表的第3个位置
38: getstatic #2 // 获取 java/lang/System 类的 out 字段,是 PrintStream 类型
41: ldc #8 // 将常量池中的 "finally block" 字符串压入栈顶
43: invokevirtual #4 // 调用 PrintStream 类的 println 方法输出字符串
46: ret 3 // 返回到 astore_3 指令之后的代码
这段字节码中使用了 jsr
和 ret
指令,这两个指令主要用于实现 finally
块的逻辑。jsr
指令会跳转到 finally
块的代码,然后 ret
指令用于返回到 finally
块之前的代码继续执行。
字节码的解释
-
行0-15:这部分对应 try 块的内容。在这个例子中,它首先通过 getstatic 指令获取 System.out 对象,然后通过 ldc 指令加载常量 “try block”,最后调用 println 方法输出这个字符串。然后,它创建一个 Exception 对象并抛出。
-
行16-25:这部分对应 catch 块的内容。当 try 块抛出异常时,执行流程会跳转到这部分。在这个例子中,它首先通过 astore 指令将异常对象存储到局部变量表,然后类似于 try 块的处理,输出 “catch block” 字符串。
-
行37-46:这部分对应 finally 块的内容。无论 try 块是否抛出异常,这部分代码总是会被执行。在这个例子中,它输出 “finally block” 字符串。
-
行25-32和行35-36:这部分是对异常处理的一些额外控制。jsr 和 ret 指令用于实现无条件的跳转,确保 finally 块总是会被执行。
8. 参考文档
- 张亚 《深入理解JVM字节码》
- https://www.jonesjalapat.com/2021/09/11/internal-working-of-java-virtual-machine/
相关文章:

字节码学习之常见java语句的底层原理
文章目录 前言1. if语句字节码的解析 2. for循环字节码的解析 3. while循环4. switch语句5. try-catch语句6. i 和i的字节码7. try-catch-finally8. 参考文档 前言 上一章我们聊了《JVM字节码指令详解》 。本章我们学以致用,聊一下我们常见的一些java语句的特性底层…...

Godot C#连接信号不能像GDScirpt一样自动添加代码
前言 我网上找了好久,发现Godot 对于C# 的支持还有待增强 使用c#脚本有办法像gds那样连接节点自带信号时自动生成信号吗? 百度贴吧 Godot C# How To, Episode 9. Signals With Parameters | Godot Mono 解决方案 把信号拉长,看他的属性 修…...

快速自动化处理JavaScript渲染页面
在进行网络数据抓取时,许多网站使用了JavaScript来动态加载内容,这给传统的网络爬虫带来了一定的挑战。本文将介绍如何使用Selenium和ChromeDriver来实现自动化处理JavaScript渲染页面,并实现有效的数据抓取。 1、Selenium和ChromeDriver简介…...

通过API接口进行商品价格监控,可以按照以下步骤进行操作
要实现通过API接口进行商品价格监控,可以按照以下步骤进行操作: 申请平台账号并选择API接口:根据需要的功能,选择相应的API接口,例如商品API接口、店铺API接口、订单API接口等,这一步骤通常需要我们在相应…...

(vue3)大事记管理系统 文章管理页
[element-plus进阶] 文章列表渲染(带搜索&到分页) 表单架设:当前el-form标签配置一个inline属性,里面的元素就会在一行显示了 中英国际化处理:App.vue中el-config-provider标签包裹组件,意味着整个组…...

springboot 使用RocketMQ客户端生产消费消息DEMO
创建springboot项目省略 项目依赖 注意:当前客户端版本是 5.1.3 ,安装的rocketmq服务的版本要与其对应 <properties><java.version>11</java.version><rocketmq-client-java-version>5.1.3</rocketmq-client-java-version&…...

第三章 内存管理 四、连续分配管理方式
目录 一、内存空间的分配与回收 1、连续分配管理方式 (1)、单一连续分配 优点: 缺点: (2)、固定分区分配 分区大小相等: 分区大小不等: (3)、动态分区…...

npm install报--4048错误和ERR_SOCKET_TIMEOUT问题解决方法之一
一、问题描述 学习vue数字大屏加载动漫效果时,在项目终端页面输入全局下载指令 npm install -g json-server 问题1、报--4048错误 会报如下错误 operation not permitted......errno: -4048code:EPERMsyscall: mkdir......The operation was reiected by your op…...

合并两个有序数组
给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。 注意:最终,合并后数组…...

自动泊车系统设计学习笔记
1 概述 1.1 自动泊车系统研究现状 目前对于自动泊车系统的研究方法通常有两种实现方式: 整个泊车操作可以分为四个阶段:第一阶段车辆向前行驶进行车位识别,第二阶段车辆行驶到准备泊车时的待泊车区域,第三阶段车辆按照规划好的…...

基于Java的家电销售网站管理系统设计与实现(源码+lw+部署文档+讲解等)
文章目录 前言具体实现截图论文参考论文参考详细视频演示为什么选择我自己的网站自己的小程序(小蔡coding)有保障的售后福利 代码参考源码获取 前言 💗博主介绍:✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域…...

设计模式~备忘录模式(memento)-22
目录 (1)优点: (2)缺点: (3)使用场景: (4)注意事项: (5)应用实例: 代码 备忘录模式(memento) 备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对…...

【Agora UID 踩坑记录 Java 数据类型】
目录 负数二进制表示Java中32位无符号数的取法项目踩坑记录Java 0xffffffff隐式类型转换的坑 负数二进制表示 由于计算机中数据都以二进制表示,而负数的二级制是根据正数二进制取补码(补码就是先取反码,然后加1)得到,…...

ESP8285 RTOS SDK OTA
一、官方资源说明 官方指南:空中升级 (OTA) - ESP32 - — ESP-IDF 编程指南 v4.3.6 文档,虽然是正对ESP32的,但是原理是一样的。 官方参考例程:esp-idf\ESP8266_RTOS_SDK\examples\system\ota\,其中包含两个例程&…...

Hadoop3教程(四):HDFS的读写流程及节点距离计算
文章目录 (55)HDFS 写数据流程(56) 节点距离计算(57)机架感知(副本存储节点选择)(58)HDFS 读数据流程参考文献 (55)HDFS 写数据流程 …...

[0xGameCTF 2023] web题解
文章目录 [Week 1]signinbaby_phphello_httprepo_leakping [Week 2]ez_sqli方法一(十六进制绕过)方法二(字符串拼接) ez_upload [Week 1] signin 打开题目,查看下js代码 在main.js里找到flag baby_php <?php /…...

Qt之submodule编译
工作中会遇到这样一种情况:qt应用程序在运行时提示找不到某个qt的动态库。我遇到的是缺少libQt5Websocket.so,因为应用程序是在x86平台银河麒麟v10上开发,能够正常编译运行,然后移植到rk3588(aarch64架构)上…...

Python实现带图形界面的计算器
Python实现带图形界面的计算器 在本文中,我们将使用Python编写一个带有图形用户界面的计算器程序。这个程序将允许用户通过点击按钮或键盘输入数字和操作符,并在显示屏上显示计算结果。 开发环境准备 要运行这个计算器程序,您需要安装Pyth…...

$ vue -Vbash: vue: command not found
$ vue -V bash: vue: command not found报这个错,我们需要找到vue安装路径,添加在环境变量的用户变量中: 1、vue安装路径 2、编辑环境变量 然后重新打开命令框,就可以了...

专业音视频领域中,Pro AV的崛起之路
编者按:在技术进步的加持下,AV行业发展得如何了?本文采访了两位深耕于广播电视行业的技术人,为我们介绍了专业音视频的进展:一位冉冉升起的新星:Pro AV以及FPGA在其中发挥的作用。 美国,拉斯维加…...

vscode 右侧滚动条标记不提示,问题解决纪录
问题描述 用vscode看代码时,我希望在右侧提示一个变量在文件下都在那里使用,在那里赋值,之前该功能是存在的,当我打开一个新的文件夹时这个功能消失了。 解决办法 在setting.json文件下输入 "C_Cpp.intelliSenseEngine&…...

【Java 进阶篇】JavaScript特殊语法详解
JavaScript是一门非常灵活的编程语言,允许开发人员使用多种不同的语法和技巧来解决各种问题。本篇博客将深入探讨JavaScript中的一些特殊语法,这些语法可能不是常规的JavaScript编程知识,但它们对于理解语言的强大之处以及在某些情况下解决问…...

PCL点云处理之配准中的匹配对连线可视化显示 Correspondences(二百一十九)
PCL点云处理之配准中的匹配对连线可视化显示 Correspondences(二百一十九) 一、算法介绍二、算法实现1.可视化代码2.完整代码(特征匹配+可视化)最终效果一、算法介绍 关于点云配准中的匹配对,如果能够可视化将极大提高实验的准确性,还好PCL提供了这样的可视化工具,做法…...

Vue el-table全表搜索,模糊匹配-前端静态查询
后端返回的数据是全部的数据,没有分页,前端需要做的是分页全表模糊查询 代码: //根据关键字对表全局搜索 globalSearch() {//为了拿到对象的列名let filterList Object.keys(this.tableData[0]);if (this.searchWord) {this.tableFilterDat…...

基于html5开发的Win12网页版,抢先体验
据 MSPoweruser 报道,Windows 11虽然刚刚开始步入正轨,但最新爆料称微软已经在开启下一个计划,Windows 12 的开发将在 去年3 月份开始。德国科技网站 Deskmodder.de 称,根据内部消息,微软将在 2022年3 月开始开发 Wind…...

Studio One6.5中文版本下载安装步骤
在唱歌效果调试当中,我们经常给客户安装的几款音频工作站。第一,Studio One 6是PreSonus公司开发的一款功能强大的音频工作平台,具有丰富的音频处理功能和灵活的工作流程。以下是Studio One6的一些主要特点: 1.多轨录音和编辑&…...

Java架构师缓存架构设计解决方案
目录 1 缓存常见的三大问题1.1 缓存雪崩1.2 缓存穿透1.3 缓存击穿2 缓存key的生成策略3 热点数据集中失效的问题4 如何提高缓存的命中率5 缓存和数据库双写不一致的问题6 如何对缓存数据进行分片想学习架构师构建流程请跳转:Java架构师系统架构设计 1 缓存常见的三大问题 缓…...

【玩转Redhat Linux 8.0系列 | 实验—使用Bash shell执行命令】
今天继续分享一些Redhat Linux 8.0的知识,记得关注,会一直更新~ 访问命令行 任务执行清单 在本实验中,您将使用Bash shell来执行命令。 成果 使用Bash shell命令行成功运行简单的程序。 执行用于识别文件类型并显示文本文件部分内容的命…...

Linux系统编程详解
Linux 多线程编程 什么是线程? 与线程类似,线程是允许应用程序并发执行多个任务的一种机制 线程是轻量级的进程(LWP:Light Weight Process),在 Linux 环境下线程的本 质仍是进程。 一个进程可以包含多个线…...

ios设备管理软件iMazing 2.17.11官方中文版新增功能介绍
iMazing 2.17.11官方中文版(ios设备管理软件)是一款管理苹果设备的软件, Windows 平台上的一款帮助用户管理 IOS 手机的应用程序,软件功能非常强大,界面简洁明晰、操作方便快捷,设计得非常人性化。iMazing官方版与苹果设备连接后&…...