笔记内容:

  1. 什么是流
  2. 字节流
  3. 字符流
  4. 序列化
  5. 数据操作流(操作基本数据类型的流)DataInputStream
  6. 打印流
  7. Properties 集合

什么是流:

流是个抽象的概念,是对输入输出设备的抽象,输入流可以看作一个输入通道,输出流可以看作一个输出通道。

输入流是相对程序而言的,外部传入数据给程序需要借助输入流。

输出流是相对程序而言的,程序把数据传输到外部需要借助输出流。

注意:输入和输出都是相对于程序而言的

什么是字节流?

字节流--传输过程中,传输数据的最基本单位是字节的流。

什么是字符流?

字符流--传输过程中,传输数据的最基本单位是字符的流。

字节输出流:

字符编码方式不同,有时候一个字符使用的字节数也不一样,比如ASCLL方式编码的字符,占一个字节;而UTF-8方式编码的字符,一个英文字符需要一个字节,一个中文需要三个字节。

字节数据是二进制形式的,要转成我们能识别的正常字符,需要选择正确的编码方式。我们生活中遇到的乱码问题就是字节数据没有选择正确的编码方式来显示成字符。

从本质上来讲,写数据(即输出)的时候,字节也好,字符也好,本质上都是没有标识符的,需要去指定编码方式。

但读数据的时候,如果我们需要去“看数据”,那么字节流的数据需要指定字符编码方式,这样我们才能看到我们能识别的字符;而字符流,因为已经选择好了字符编码方式,通常不需要再改了(除非定义的字符编码方式与数据原有的编码方式不一致!)

在传输方面上,由于计算机的传输本质都是字节,而一个字符由多个字节组成,转成字节之前先要去查表转成字节,所以传输时有时候会使用缓冲区。

字节流

字节流的类通常以stream结尾

字节输入流:

字节输入流的基类是abstract class InputStream ,是一个抽象类所以实例化的时候应该实例化它的子类,常见的字节输入流有:

  • InputStream 字节输入流基类
  • FileInputStream
  • BufferedInputStream 【BufferedInputStream不是InputStream的直接实现子类,是FilterInputStream的子类】
  • DataInputStream 数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。
  • 他们的区别与用途:

InputStream是字节输入流的抽象基类,InputStream作为基类,给它的基类定义了几个通用的函数:

  • read(byte[] b):从流中读取b的长度个字节的数据存储到b中,返回结果是读取的字节个数(当再次读时,如果返回-1说明到了结尾,没有了数据)
  • read(byte[] b, int off, int len):从流中从off的位置开始读取len个字节的数据存储到b中,返回结果是实际读取到的字节个数(当再次读时,如果返回-1说明到了结尾,没有了数据)
  • close():关闭流,释放资源。

    FileInputStream主要用来操作文件输入流,它除了可以使用基类定义的函数外,它还实现了基类的read()函数(无参的):

  • read():从流中读取1个字节的数据,返回结果是一个int,(如果编码是以一个字节一个字符的,可以尝试转成char,用来查看数据)。

    BufferedInputStream带有缓冲的意思,普通的读是从硬盘里面读,而带有缓冲区之后,BufferedInputStream已经提前将数据封装到内存中,内存中操作数据要快,所以它的效率要要非缓冲的要高。它除了可以使用基类定义的函数外,它还实现了基类的read()函数(无参的):

  • read():从流中读取1个字节的数据,返回结果是一个int,(如果编码是以一个字节一个字符的,可以尝试转成char,用来查看数据)。

使用:

  • InputStream是抽象基类,所以它不可以创建对象,但它可以用来“接口化编程”,因为大部分子类的函数基类都有定义,所以利用基类来调用函数。
  • FileInputStream是用来读文件数据的流,所以它需要一个文件对象用来实例化,这个文件可以是一个File对象,也可以是文件名路径字符串.【这里文件不存在会抛处异常,所以需要处理异常】

  • BufferedInputStream是一种封装别的流以提高效率的流,所以它的初始化需要一个的InputStream流对象。


字节输出流:

常用的字节输出流主要有:

  • OutputStream
  • FileOutputStream
  • BufferedOutputStream

【BufferedOutputStream不是OutputStream的直接实现子类,是FilterOutputStream的子类】

他们的区别与用途:

OutputStream是字节输出流的基类, OutputStream作为基类,给它的基类定义了几个通用的函数:
  • write(byte[] b):将b的长度个字节数据写到输出流中。
  • write(byte[] b,int off,int len):从b的off位置开始,获取len个字节数据,写到输出流中。
  • flush():刷新输出流,把数据马上写到输出流中。
  • close():关闭流,释放系统资源。
  • FileOutputStream是用于写文件的输出流,它除了可以使用基类定义的函数外,还实现了OutputStream的抽象函数write(int b):
  • write(int b):将b转成一个字节数据,写到输出流中。
BufferedOutputStream像上面那个BufferedInputStream一样,都可以提高效率。它除了可以使用基类定义的函数外,它还实现了OutputStream的抽象函数write(int b):
  • write(int b):将b转成一个字节数据,写到输出流中。

使用:

OutputStream是抽象基类,所以它不能实例化,但它可以用于接口化编程。

FileOutputStream是用于写文件的输出流,所以它需要一个文件作为实例化参数,这个文件可以是File对象,也可以是文件路径字符串。

【如果文件不存在,那么将自动创建。】

【FileOutputStream实例化时可以给第二个参数,第二个参数是是否使用追加写入默认,为true时代表在原有文件内容后面追加写入数据,默认为false】

BufferedOutputStream需要一个输出流作为实例化参数。

创建输出流对象的步骤:

  1. 创建流对象
  2. 读数据或者写数据
  3. 关流,释放资源

字符流

注意事项:非文本数据不能用字符流处理

字符输出流:

常见的字符输出流有:

  • Writer
  • OutputStreamWriter
  • FileWriter
  • BufferedWriter

他们的区别与用途:

Writer是字符输出流的抽象基类, ,它定义了以下几个函数

  • write(char[] cbuf) :往输出流写入一个字符数组。
  • write(int c) :往输出流写入一个字符。
  • write(String str) :往输出流写入一串字符串。
  • write(String str, int off, int len):往输出流写入字符串的一部分。
  • close() :关闭流,释放资源。 【这个还是抽象的,写出来是说明有这个关闭功能】
  • flush():刷新输出流,把数据马上写到输出流中。 【这个还是抽象的,写出来是说明有这个关闭功能】

注意:

append方法返回值类型是write说明可以链式编程

  • Writer append(char c)
  •       将指定字符添加到此 writer。
  • Writer append(CharSequence csq)
  •       将指定字符序列添加到此 writer。
  • Writer append(CharSequence csq, int start, int end)
  •       将指定字符序列的子序列添加到此 writer.Appendable。
OutputStreamWriter可以使我们直接往流中写字符串数据,它里面会帮我们根据字符编码方式来把字符数据转成字节数据再写给输出流,它相当于一个中介\桥梁。
FileWriter与OutputStreamWriter功能类似,我们可以直接往流中写字符串数据,FileWriter内部会根据字符编码方式来把字符数据转成字节数据再写给输出流。

FileWriter是便捷流,可以直接修改文件

FileWrite的构造方法:

  • FileWriter(File file, boolean append)
  • FileWriter(String fileName)
  • FileWriter(String fileName, boolean append)
BufferedWriter比FileWriter还高级一点,它利用了缓冲区来提高写的效率。它还多出了一个函数:
  • newLine() :写入一个换行符。

使用

Writer 是一个抽象基类,不能实例化,但可以用于接口化编程。

OutputStreamWriter 需要一个输入流对象作为实例化参数。

- FileWriter 需要一个文件对象来实例化,可以是File类对象,也可以是文件的路径字符串。

- BufferedWriter

++注意:filewrite更方便,但是不能规定编码表

++

字符输入流:

字符流的类通常以reader和writer结尾

字符输入流:

常见的字符输入流有:

  • Reader
  • InputStreamReader
  • FileReader
  • BufferedReader

他们的区别与用途:

Reader是字符输入流的抽象基类 ,它定义了以下几个函数:

  • read() :读取单个字符,返回结果是一个int,需要转成char;到达流的末尾时,返回-1
  • read(char[] cbuf):读取cbuf的长度个字符到cbuf这种,返回结果是读取的字符数,到达流的末尾时,返回-1
  • close()  :关闭流,释放占用的系统资源。

InputStreamReader 可以把InputStream中的字节数据流根据字符编码方式转成字符数据流。它除了可以使用基类定义的函数,它自己还实现了以下函数:

  • read(char[] cbuf, int offset, int length) :从offset位置开始,读取length个字符到cbuf中,返回结果是实际读取的字符数,到达流的末尾时,返回-1

FileReader

可以把FileInputStream中的字节数据转成根据字符编码方式转成字符数据流。

BufferedReader

可以把字符输入流进行封装,将数据进行缓冲,提高读取效率。它除了可以使用基类定义的函数,它自己还实现了以下函数:

  • read(char[] cbuf, int offset, int length) :从offset位置开始,读取length个字符到cbuf中,返回结果是实际读取的字符数,到达流的末尾时,返回-1
  • readLine() :读取一个文本行,以行结束符作为末尾,返回结果是读取的字符串。如果已到达流末尾,则返回 null

使用

  • Reader 是一个抽象基类,不能实例化,但可以用于接口化编程。
  • InputStreamReader需要一个字节输入流对象作为实例化参数。还可以指定第二个参数,第二个参数是字符编码方式,可以是编码方式的字符串形式,也可以是一个字符集对象。

FileReader 需要一个文件对象作为实例化参数,可以是File类对象,也可以是文件的路径字符串

便捷流,可以直接对文件修改



BufferReader需要一个字符输入流对象作为实例化参数。



补充:

刷新缓冲区的时机:

a. 使用flush
b. 缓冲区满了
c. 关闭流的时候也会刷新缓冲区

flush()和close()的区别:

flush: 强制刷新缓冲区, flush之后还能写数据
close: 关流,释放系统资源. close之后不能再写数据了
因为FileInputStream实现了Closeable接口,这是jdk1.8之后提供的,会自动关闭流
在idea中创建的。text文件时UTF-8编码

在windows中创建.txt文件默认的编码是GBK编码

字节流处理文本数据不是很方便 ,因为字节流每次只传一个字节,但是中文使用的编码表是UnIcode编码是占两个字节,而且字节流处理文本不可以规定编码格式

字符流底层=字节流+编码表

输出流实现换行:

  1. 如何实现数据的换行

    Window:\r\n

    Linux: \n

    Mac: \r

    Java提供了一个统一的API进行换行(以后再讲)

    高级记事本能够自动识别不同平台的换行符,

    notepad是 Windows 自带的记事本,只能识别 \r\n

JDK7新特性,自动关闭流

JDK7 提供了新特性 try with resource 语句

所有实现了 AutoCloseable 接口的类 都可以使用 try with resource语句

Interface AutoCloseable:

try ...with ... resource 的结构

try (资源的声明) {

} catch {

}

注意事项:只有在()中声明的资源,才可以自动释放资源

练习:

1.复制文本 (字符流和字节流都可以)

2.复制图片或视频 (只能使用字节流)

3.将Arraylist中的String存储到文本文件,每一个元素占一行 (使用Bufferedwrite )

4.从文本文件中读取文件,每一行作为集合的一个元素 (使用Bufferedreader)

序列化:

序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化(将对象转换成二进制)。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。

将需要序化的类实现Serializable接口就可以了,该接口没有任何方法,可以理解为一个标记,即表明这个类可以序列化。

注意的是被关键字static、transient修饰的变量不能被序列化。在被序列化后,transient修饰的变量会被设为初始值。如int型的是0、对象型的是null.

1.概念

序列化:把Java对象转换为字节序列的过程。   反序列化:把字节序列恢复为Java对象的过程。

2.用途

对象的序列化主要有两种用途: 1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中; 2) 在网络上传送对象的字节序列。

##### ++注意++:通过序列化和反序列化可以实现拷贝对象(深拷贝)

测试代码:

package com.cskaoyan.Login.HomeWork;
//3. 完成一个对象的序列化和反序列化。 /*
ObjectInputStream:
能够从磁盘或者网略等设备读取二进制,并将它还原成对象 构造方法:
ObjectInputStream(InputStream in) 方法:
Object readObject() 通过序列化和反序列化可以实现拷贝对象(深拷贝)
*/ import java.io.*; public class HomeWork_02 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student s=new Student("赵塬",98,89,78);
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("Object.txt"));
oos.writeObject(s);
oos.close();
ObjectInputStream ois =new ObjectInputStream(new FileInputStream("Object.txt"));
Student s1=(Student)ois.readObject();
System.out.println(s1);
System.out.println(s==s1);//false
//Studnet类已经重写了equals方法
System.out.println(s.equals(s1));//true }
}

5.内存操作流DataInputStream

类 DataInputStream

  java.io.FilterInputStream

      继承者 java.io.DataInputStream

测试代码:

import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException; /*
DataOutputStream:
能够操作基本数据类型
构造方法;
DataOutputStream(OutputStream out) 注意事项:
==数据是按照什么顺序写的,就必须按照什么的顺序读==
*/
public class DataOutputStreamDemo1 {
public static void main(String[] args) throws IOException {
byte b = 10;
short s = 20;
int i = 30;
long l = 40;
float f = 1.23f;
double d = 3.14;
char c = '中';
boolean flag = false; DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeByte(b);
dos.writeShort(s);
dos.writeInt(i);
dos.writeFloat(f);
dos.writeDouble(d);
dos.writeChar(c);
dos.writeBoolean(flag);
dos.close();
}
}
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException; /*
DataInputStream:
能操作基本数据类型:
构造方法:
DataInputStream(InputStream in) */
public class DataInputStreamDemo1 {
public static void main(String[] args) throws IOException {
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
System.out.println(dis.readByte());
System.out.println(dis.readShort());
System.out.println(dis.readInt()); System.out.println(dis.readLong()); System.out.println(dis.readFloat());
System.out.println(dis.readDouble()); // java.io.EOFException "end of file"
System.out.println(dis.readChar());
System.out.println(dis.readBoolean());
dis.close();
}
}

打印流:

(1) 字节打印流 PrintStream

    字符打印流 PrintWriter

(2)特点:

  1.  A:只操作目的地,不操作数据源
  2.  B:可以==操作任意类型==的数据
  3.  C:如果启用了自动刷新,能够换行并刷新,只有特定的方法才能自动刷新(PrintWriter:println,printf,format)
  4.  D:可以直接操作文件
    问题:哪些流可以直接操作文件呢?FileInputStream
  •   FileOutputStream
  •   FileReader
  •   FileWriter
  •   PrintStream
  •   PrintWriter
    
      	看API,如果其构造方法能够同时接收File和String类型的参数,一般都是可以直接操作文件的
流的分类:
    基本流: 可以直接读写文件
高级流:在基本流的基础上提供了一些其他的功能

(3)构造方法:

PrintStream(File file)
PrintStream(String fileName)
PrintStream(OutputStream out)
PrintStream(OutputStream out, boolean autoFlush)

(4)方法:

print() : 参数:基本数据类型、char[]、Object
println():通过写入行分隔符字符串终止当前行。
println(): 参数:基本数据类型、char[]、Object[其实等价于素质三联 bw.write(),bw.newLine(),bw.flush ()]

(5)复制文本文件

	BufferedReader br = new BufferedReader(new FileReader("a.txt"));
PrintWriter pw = new PrintWriter(new FileWriter("b.txt"),true); String line = null;
while((line=br.readLine())!=null) {
pw.println(line);
} pw.close();
br.close();

标准输入输出流

(1) 概念

  • System类中的字段: in,out
  • 它们各代表了系统标准的输入和输出设备
  • 默认的输入设备是键盘,输出设备是显示屏
  • System.in的类型是InputStream;
  • Syatem.out的类型是PrintStream是OutputStream子类FilterOutputStream的子类

System类中的两个成员变量:

public static final InputStream in “标准”输入流。

public static final PrintStream out “标准”输出流。

	InputStream is = System.in;
PrintStream ps = System.out;

(2)三种键盘录入方式(System.in)

  1.  A:main方法的args接收参数
    意义不大
  2.  B:System.in通过BufferedReader进行包装
  3.      InputStream标准输入流是字节流,每次只能读取一个字节,要想读取一行就需要使用BufferedReader进行包装,字符缓冲流BufferedReader是字符缓冲流,但是InputSteream是字节流,所以还要需要InputStreamReader将它转换成字符流
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
  4.  C:Scanner(JDk1.5)
    Scanner sc = new Scanner(System.in);

(3)输出语句的原理和如何使用字符流输出数据

	A:原理
这输出语句其本质就是IO流操作把数据输出到控制台
System.out.println("helloworld"); PrintStream ps = System.out;
ps.println("helloworld");
B:把System.out用字符缓冲流包装一下使用
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

Properties

(1)概述

Properties:属性集合类。是一个可以和IO流相结合使用的集合类。

Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。

是Hashtable的子类,说明是一个Map集合。

(2)构造方法:

Properties()
Properties(Properties defaults)
//把Proterties当作普通集合使用
Properties p1 = new Properties();
// System.out.println(p1);
p1.put("邓超", "孙俪");
p1.put("刘强东", "章泽天");
p1.put("王宝强", "马蓉"); /*System.out.println(p1.get("刘强东"));
System.out.println(p1.remove("邓超")); System.out.println(p1);
System.out.println(p1.size());*/
//遍历
/*Set<Map.Entry<Object, Object>> entries = p1.entrySet();
for(Map.Entry<Object, Object> entry : entries) {
System.out.println(entry);
}*/

(3)特殊功能:

  • Object setProperty(String key,String value):添加元素
  • String getProperty(String key):获取元素
  • Set stringPropertyNames():获取所有的键的集合

(4)和流相关的一些方法:

这里的集合必须是Properties集合:

  • void store(Writer writer, String comments)//String comments列表属性描述
  • void store(OutputStream out, String comments)
  • void storeToXML(OutputStream os, String comment)//发出一个表示此表中包含的所有属性的 XML 文档
  • void loadFromXML(InputStream in)
  • void load(InputStream inStream)
  • public void load(Reader reader):把文件中的数据读取到集合中
//将集合元素写入文件
public static void main(String[] args) throws IOException {
Properties p1 = new Properties();
p1.put("邓超", "孙俪");
p1.put("刘强东", "章泽天");
p1.put("王宝强", "马蓉"); //p1.store(new FileWriter("a.properties"), "first try");
p1.storeToXML(new FileOutputStream("a.xml"), "first try");
}
//将文件内的数据加载到集合中
public static void main(String[] args) throws IOException {
Properties properties = new Properties();
System.out.println(properties);
/*properties.load(new FileReader("a.properties"));
System.out.println(properties);*/ properties.loadFromXML(new FileInputStream("a.xml"));
System.out.println(properties);
}

注意:

用于配置文件的读取

IO流总结---- 字节流 ,字符流, 序列化 ,数据操作流,打印流 , Properties 集合的更多相关文章

  1. python学习-- Django REST framework 序列化数据操作

    一.为什么要返回json数据? 一般来说前端要用到从后台返回的数据来渲染页面的时候,这时候后台就需要向前端返回json类型的数据,简单直观便于理解 ,就类似于 {"xxx":{[& ...

  2. 【Java IO流】字节流和字符流详解

    字节流和字符流 对于文件必然有读和写的操作,读和写就对应了输入和输出流,流又分成字节和字符流. 1.从对文件的操作来讲,有读和写的操作——也就是输入和输出. 2.从流的流向来讲,有输入和输出之分. 3 ...

  3. IO流(字节流,字符流)

    一,概述 IO流(input output):用来处理设备之间的数据. Java对数据的操作是通过流的对象. Java用于操作流的对象都在IO包中. 流是一组有顺序的,有起点和终点的字节集合,是对数据 ...

  4. IO流输入 输出流 字符字节流

    一.流 1.流的概念 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作. ...

  5. IO流(字节流,字符流,缓冲流)

    一:IO流的分类(组织架构) 根据处理数据类型的不同分为:字节流和字符流 根据数据流向不同分为:输入流和输出流   这么庞大的体系里面,常用的就那么几个,我们把它们抽取出来,如下图:   二:字符字节 ...

  6. IO流的Properties集合,序列化流与反序列化流,打印流及commons-IO

    内容介绍 Properties集合 序列化流与反序列化流 打印流 commons-IO Properties类 Properties类介绍 Properties 类表示了一个持久的属性集.Proper ...

  7. Core Java - 流(Stream) - 字节流和字符流(一)

    0. 概述: Java中基于流的I/O构建在4个抽象类之上, 其中2个是字节流,另外2个是字符流. 字节流: InputStream / OutputStream 当操作字节或其它二进制对象时,应当使 ...

  8. 16、IO (Properties、序列化流、打印流、CommonsIO)

    Properties集合的特点 * A: Properties集合的特点 * a: Properties类介绍 * Properties 类表示了一个持久的属性集.Properties 可保存在流中或 ...

  9. IO流之字节流

    IO流分类 按照数据流向 输入流:从外界(键盘.网络.文件…)读取数据到内存 输出流:用于将程序中的数据写出到外界(显示器.文件…) 数据源 目的地 交通工具 按照数据类型 字节流:主要用来处理字节或 ...

随机推荐

  1. C# 结构与类

    结构是一种可以包含数据成员和方法成员的值类型数据结构.为结构分配数据时不需要从托管堆中分配内存,结构类型的变量直接包含了该结构的数据.结构中可以包含构造函数,常量,字段方法,属性,运算符,事件和嵌套类 ...

  2. FCC---Animate Elements at Variable Rates----一闪一闪亮晶晶,不同的闪动节奏

    There are a variety of ways to alter the animation rates of similarly animated elements. So far, thi ...

  3. 工具类ToastUtil 避免在子线程中使用抛异常 "Can't create handler inside thread that has not called Looper.prepare()"

    package com.example.kbr.utils; import android.view.Gravity; import android.widget.Toast; import io.r ...

  4. 中文代码之Django官方入门:建立模型

    参考编写你的第一个 Django 应用,第 2 部分 创建项目后,首先用中文命名应用: $ python3 manage.py startapp 投票 之后在models.py建立模型,其他各种相关配 ...

  5. 计算机基础 python入门

    1.计算机基础 计算机组成: 输入输出设备内. 存储器 .cpu .电源 .显卡 中央处理器(cpu) 处理各种数据 相当于人的大脑 内存 存储数据 相当于临时记忆 硬盘 存储数据 相当于人的永久记忆 ...

  6. Bitbucket与git上传源码的使用方法

    本文链接:https://blog.csdn.net/nomisshe/article/details/19625555 Bitbucket使用方法   一.软件及SSH keys: 由于我的Bitb ...

  7. 海思屏幕HAL代码解析

    显示屏幕(LCD)模块提供屏幕相关功能接口,调用者为上层应用模块(含 init.状态机.ui),上下文依赖关系,如图 3-7 所示. 系统框架为 linux+Huawei LiteOS 双系统架构,媒 ...

  8. 使用Azure进行自动化机器学习

    什么是自动化机器学习? 自动化的机器学习,也称为 AutoML,让数据科研人员. 分析人员和开发人员,同时维护模型质量构建具有高缩放性. 效率和工作效率的机器学习模型. 自动化机器学习生成的机器学习模 ...

  9. echarts使用简介

    1. echarts是百度开源的一个前端图表库 2. 官网地址: https://www.echartsjs.com/zh/index.html 3. 使用介绍: 3.1. 下载echarts.js ...

  10. 7.Python网络编程_多线程共享全局变量问题

    Python多线程支持全局变量的共享操作,但是它存在很多问题,先来看以下程序,该程序理论上执行完毕后全局变量g_num的值应该是2000000,但是在实际运行中,结果不足理论值 import thre ...