Java基础--输入输出
输入/输出类库
流
流是指在计算机的输入与输出之间的数据的序列,而Java中的数据流有 字节流
和 字符流
之分;
就流的运动方向而言,流可分为输入流(input stream)和输出流(output stream),输入流代表从外设流入计算机的数据序列;输出流代表从计算机流向外设的数据序列。流、程序、外设之间的关系如下图:
一、概述
在Java中,输入输出流用于实现程序与外部数据源(如文件、网络连接等)之间的数据传输。最基本的流类有字节流 InputStream
(输入流)和 OutputStream
(输出流),以及字符流 Reader
(输入字符流)和 Writer
(输出字符流)。字节流以字节为单位处理数据,适用于各种二进制数据的读写;字符流则以字符为单位,更适合处理文本数据,尤其是涉及多字节字符(如汉字)的情况。
二、InputStream
类(字节输入流)
read()
方法
public int read()
:从输入流的当前位置处读入一个字节(8位)的二进制数据,将其转换为 0 - 255 的整型量返回。若输入流当前位置无数据了,则返回 -1,表示已读到流的末尾。例如,读取一个简单的二进制文件时,可以通过不断调用该方法逐个字节获取文件内容:
InputStream inputStream = new FileInputStream("example.bin");
int data;
while ((data = inputStream.read())!= -1) {
// 对读取到的字节数据进行处理,比如存储到其他数据结构中
}
inputStream.close();
public int read(byte b[])
:从输入流当前位置连续读入多个字节,将其保存到byte
型数组b
中,并返回实际读入的字节数。例如,一次读取一定数量字节到数组中:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class FileReadExample {
public static void main(String[] args) {
byte[] buffer = new byte[1024];
// 使用 try-with-resources 自动关闭 InputStream
try (InputStream inputStream = new FileInputStream("example.txt")) {
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
// 处理读取到的字节数据块
// 例如,将其转换为字符串(注意:这里只是简单示例,实际转换可能需要更多处理)
String data = new String(buffer, 0, bytesRead);
System.out.print(data);
}
} catch (IOException e) {
e.printStackTrace();
// 可以添加更多的错误处理逻辑
}
inputStream.close();
}
}
内部指针
每个 InputStream
流对象都有一个内部指针,初始时位于流的第一个数据处。每当读入一个字节,指针自动后移一个字节,始终指向未被读取的下一个数据位置,用于确定下次读取数据的起始点。
close()
方法
使用完输入流后,必须调用 close()
方法关闭流,以断开 Java 程序与外设数据源的连接,释放相关系统资源,避免资源泄露。
InputStream
子类的继承关系及特点
FileInputStream
:用于从文件中读取字节数据,可指定文件路径来创建实例,常用于读取本地文件内容。FilterInputStream
:是装饰类的抽象超类,可对其他输入流进行功能增强,比如添加缓冲等功能,其子类有BufferedInputStream
等。DataInputStream
:允许从输入流中读取基本数据类型(如int
、double
等)以及String
类型的数据,不过读取时要按照写入的顺序来进行读取操作。BufferedInputStream
:为输入流添加缓冲功能,减少与底层数据源的频繁交互,提高读取效率,通常会结合其他输入流一起使用,如BufferedInputStream bis = new BufferedInputStream(new FileInputStream("file.txt"));
。
三、OutputStream
类(字节输出流)
write()
方法
public void write(int b)
:将参数b
的低位字节写入到输出流。例如,向一个字节输出流写入单个字节数据:
OutputStream outputStream = new FileOutputStream("output.bin");
outputStream.write(65); // 写入 ASCII 码为 65 的字符 'A'
outputStream.close();
public void write(byte b[])
:将byte
型数组b
中的全部字节顺序写入到输出流,可用于批量写入字节数据,比如将一个缓冲区中的数据一次性写入文件等:
byte[] data = "Hello, World!".getBytes();
OutputStream outputStream = new FileOutputStream("output.txt");
outputStream.write(data);
outputStream.close();
flush()
方法
对于具备缓冲功能的子类(如 BufferedOutputStream
),write()
方法所写的数据先存放在流的缓冲区中,等到缓冲区满了才统一向外设执行写操作。但有时需要在缓冲区未满时就将数据写到外设中,这时就要调用 flush()
方法强制清空缓冲区,把现有数据立即写入外设。例如,在网络传输中,及时发送部分数据时可能就需要调用 flush()
方法。
close()
方法
和 InputStream
的 close()
方法类似,使用完输出流后调用此方法关闭流,释放资源,断开与外设数据源的连接。
OutputStream
子类的继承关系及特点
FileOutputStream
:用于将字节数据写入文件,可指定文件路径和创建模式(如覆盖原有文件或追加内容等)来创建实例,实现向文件写入内容的功能。FilterOutputStream
:也是装饰类的抽象超类,用于增强其他输出流功能,像PrintStream
等是其子类。PrintStream
:常用于输出格式化的文本数据,它提供了方便的print()
和println()
方法,可将各种基本数据类型和对象转换为字符串并输出,默认会自动刷新缓冲区(autoFlush
属性设置为true
),常用来输出到控制台(System.out
就是PrintStream
类型)。DataOutputStream
:允许将基本数据类型(如int
、double
等)以及String
类型的数据按照特定格式写入到输出流中,方便后续按相应格式读取。
四、Reader
类(字符输入流)
read()
方法
public int read()
:从输入流的当前位置读入一个字符,并将其作为 int
类型返回(实际返回的是字符对应的 Unicode 码值),若当前位置无数据则返回 -1。如果读取过程中出现错误,会抛出 IOException
类异常。例如,读取一个文本文件中的字符:
Reader reader = new FileReader("example.txt");
int ch;
while ((ch = reader.read())!= -1) {
char character = (char) ch;
// 对读取到的字符进行处理,比如判断、拼接等操作
}
reader.close();
public int read(char array[])
:从输入流当前位置连续读入多个字符,保存到 char
型数组 array
中,并返回实际读入的字符数。例如,一次读取多个字符到数组中:
char[] buffer = new char[1024];
Reader reader = new FileReader("example.txt");
int charsRead = reader.read(buffer);
while (charsRead!= -1) {
// 处理读取到的字符数组内容,比如输出到控制台等
charsRead = reader.read(buffer);
}
reader.close();
-
定位指针的方法:每个字符流有一个指针,初始位于流的第一个字符处,每读入一个字符,指针自动后移一个字符位置,指向未被读取的下一个字符。并且提供了一些控制指针位置的方法,如
skip(long n)
可将指针从当前位置向后跳动n
个字节;mark()
能在指针位置做一个标记;reset()
可将指针移动到mark()
标记的位置,方便在流中进行有回溯需求的读取操作。 -
close()
方法:同字节流的close()
方法作用一样,使用完Reader
类的输入流后要调用此方法关闭流,释放资源,断开与外设数据源的连接。 -
Reader
子类的继承关系及特点
BufferedReader
:为字符输入流添加缓冲功能,提高读取效率,还提供了方便的readLine()
方法,可一次读取一行文本(以换行符为分隔),常用于按行读取文本文件内容,比如:
BufferedReader br = new BufferedReader(new FileReader("example.txt"));
String line;
while ((line = br.readLine())!= null) {
// 处理每行文本内容
}
br.close();
InputStreamReader
:是字节流到字符流的桥梁,可将InputStream
转换为Reader
,通常用于处理一些底层是字节流但想按字符方式读取的情况,比如从网络输入流中读取文本数据时可能会用到。FileReader
:用于从文件中读取字符数据,它内部其实是基于FileInputStream
和InputStreamReader
来实现的,构造时只需指定文件路径即可简单地读取文本文件内容。
五、Writer
类(字符输出流)
Writer
子类的继承关系及特点PrintWriter
:提供了方便的print()
和println()
方法用于输出格式化的文本数据,可将各种基本数据类型和对象转换为字符串输出,并且支持自动刷新缓冲区等功能,常用来向控制台或文件输出文本内容,比如:
PrintWriter pw = new PrintWriter(new FileWriter("output.txt"));
pw.println("This is a test.");
pw.close();
BufferedWriter
:为字符输出流添加缓冲功能,减少与底层外设的频繁交互,提高写入效率,它提供了newLine()
方法用于输出换行符,例如:
BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"));
bw.write("First line");
bw.newLine();
bw.write("Second line");
bw.close();
OutputStreamWriter
:是字符流到字节流的桥梁,可将字符流转换为字节流输出,常用于将字符数据按特定编码格式写入到底层字节流对应的外设中,比如指定编码将字符写入文件等情况。FileWriter
:用于将字符数据写入文件,可指定文件路径和创建模式来创建实例,实现向文件写入文本内容的功能,其内部是基于OutputStreamWriter
和FileOutputStream
来实现的。
输入
从控制台读取用户输入(标准输入)
- 使用
Scanner
类(常用方式):Scanner
类位于java.util
包中,它提供了方便的方法来读取不同类型的数据。
示例代码,读取整数、字符串等不同类型数据:
import java.util.Scanner;
public class InputExample {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个整数:");
int num = scanner.nextInt();
System.out.println("你输入的整数是: " + num);
System.out.println("请输入一个字符串:");
String str = scanner.next();
System.out.println("你输入的字符串是: " + str);
scanner.close();
}
}
- 首先需要创建一个
Scanner
对象,并将System.in
(标准输入流,代表控制台输入)传递给它。 - 通过
nextInt()
方法可以读取用户输入的整数,next()
方法可以读取以空白字符(空格、制表符、换行符等)为分隔的下一个字符串。除此之外,还有nextDouble()
用于读取双精度浮点数,nextBoolean()
用于读取布尔值等,能满足多种类型数据的读取需求。 - 注意在使用完
Scanner
后,建议调用close()
方法关闭它,以释放相关资源。
获取特定类型数据的方法,可以查看手册:
Scanner
类中提供了获取 byte\short\int\long\float\double \boolean\String
类型变量的方法。
注意:没有提供获取char
类型变量的方法。需要使用next().charAt(0)
进行获取。
IDEA快捷用法:这里在补全输入类型时使用的快捷键是 Alt + Enter
(注意,要写完;
后再使用快捷键进行补齐类型)
- 使用
BufferedReader
类(更高效但稍复杂些):BufferedReader
结合InputStreamReader
可以实现从控制台读取输入。
示例代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class BufferedReaderExample {
public static void main(String[] args) {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
try {
System.out.println("请输入一个字符串:");
String str = reader.readLine();
System.out.println("你输入的字符串是: " + str);
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 先通过
InputStreamReader
将标准输入流System.in
转换为字符流,再用BufferedReader
对其进行缓冲,提高读取效率。 - 使用
readLine()
方法读取一行字符串,不过它会抛出IOException
,所以需要放在try-catch
块中来处理可能出现的异常情况。
从文件读取输入
- 使用
FileReader
和BufferedReader
组合(按字符读取文件内容):
示例代码如下,用于读取一个文本文件的内容:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class FileInputExample {
public static void main(String[] args) {
try {
BufferedReader reader = new BufferedReader(new FileReader("example.txt"));
String line;
while ((line = reader.readLine())!= null) {
System.out.println(line);
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 首先创建
FileReader
对象,指定要读取的文件路径(这里假设example.txt
在当前项目目录下,如果文件在其他目录需要写全路径),它用于将文件以字符流的形式打开。 - 然后将
FileReader
传递给BufferedReader
来缓冲字符流,提升读取效率。 - 通过循环调用
readLine()
方法逐行读取文件内容,直到读取到null
,表示文件末尾,同时要注意在try-catch
块中处理IOException
异常。
- 使用
FileInputStream
和BufferedInputStream
组合(按字节读取文件内容):
这种方式适合处理二进制文件等需要按字节读取的情况,示例代码:
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class ByteFileInputExample {
public static void main(String[] args) {
try {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("example.bin"));
int data;
while ((data = bis.read())!= -1) {
System.out.print((char) data);
}
bis.close();
// 注意这里直接将字节转为字符输出可能存在问题,若为非文本文件结果会乱码,实际中按需处理数据
// 比如对于图片等二进制文件,可进行相应的字节处理,而非转为字符输出
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 创建
FileInputStream
对象用于打开文件的字节流,然后将其传递给BufferedInputStream
进行缓冲。 - 通过循环调用
read()
方法逐字节读取文件内容,返回值为-1
时表示读到文件末尾,同样需要在try-catch
块中处理IOException
异常。
从命令行输入
编写一个应用程序求若干个数的平均数,原始数字要求从命令行输入。应用程序中
main
方法的参数String
类型的数组args
能接受用户从命令行键入的参数。
public class Test
{
public static void main(String args[])
{
double n, sum = 0;
for (int i = 0; i < args.length; i++) {
sum += Double.valueOf(args[i]);
}
n = sum / args.length;
System.out.println("average=" + n);
}
}
注意:
- 参数的个数可以利用
args.length
来取得。 - 参数 “12.34”、“34.45”、“21212121”分别赋给了main方法的参数arges[0]、arges[1]和arges[2]。
- 由于arges[0]、arges[1]和arges[2]是String类型的,所以要利用
Double.valueOf(String s)
方法将String类型转化成Double类型。
在IDEA中(这里我是的版本是 IntelliJ IDEA 2022.2.3),使用命令行执行代码的步骤:
方法一:
-
点击下方选项卡中的
Terminal
(终端),进入你要编译运行Java源文件的路径下(在默认情况下,终端初始路径就是IDEA打开工程的路径),使用cd
命令 + 路径名 的方式进入目标目录。 -
切换到正确目录后,在命令行中输入以下命令来编译代码:
javac Test.java
;如果代码没有语法错误,编译成功后会在同一目录下生成Test.class
文件。
-
在编译成功后,就可以执行代码:
java Test 1.5 2.5 3.0
(注意,你的源文件放在一个包文件下,编译器会自动添加包含这个包文件的语句,如我的Test源文件在experiment包下,在首行会为我自动添加package experiment;
,但是在使用命令行进行编译运行时,不能包含这个这个语句,否则会出现错误: 找不到或无法加载主类 Test
。所以在编译之前可以将该语句注释掉重新编译运行即可。当然如果这个源文件不在一个包下,就没有上述问题)
方法二:
使用首部选项卡中的Run下的Run/Debug Configurations:
然后直接运行即可。
输出
输出到控制台(标准输出)
- 使用
System.out.println()
和System.out.print()
(常用方式):System.out.println()
用于输出内容后自动换行,例如:
System.out.println("Hello, World!"); // 输出 Hello, World! 并换行
System.out.print()
只输出内容,不会自动换行,例如:
System.out.print("Hello");
System.out.print(" World!"); // 输出 Hello World! 在同一行
- 使用
System.out.printf()
(格式化输出,类似C语言的printf
):
它可以按照指定的格式来输出数据,示例:
int num = 10;
double d = 3.14;
System.out.printf("整数是: %d,小数是: %.2f\n", num, d);
// 按照格式输出,%d 表示整数格式,%f 表示浮点数格式,%.2f 表示保留两位小数的浮点数格式
输出到文件
- 使用
FileWriter
和BufferedWriter
组合(按字符写入文件):
示例代码,用于将一些内容写入到文本文件中:
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class FileOutputExample {
public static void main(String[] args) {
try {
BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"));
writer.write("这是要写入文件的内容");
writer.newLine(); // 写入换行符
writer.write("第二行内容");
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 先创建
FileWriter
对象,指定要写入的文件路径(如果文件不存在会自动创建,若存在则覆盖原内容),它将内容以字符流的形式准备写入文件。 - 再将
FileWriter
传递给BufferedWriter
进行缓冲处理,提高写入效率。 - 通过
write()
方法写入内容,newLine()
方法写入换行符,最后记得关闭writer
以确保内容正确写入文件并释放资源。
- 使用
FileOutputStream
和BufferedOutputStream
组合(按字节写入文件):
适用于写入二进制文件等情况,示例代码:
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteFileOutputExample {
public static void main(String[] args) {
try {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("output.bin"));
byte[] data = {65, 66, 67}; // 示例字节数据,比如可以是图片等二进制文件的部分字节
bos.write(data);
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 创建
FileOutputStream
对象用于打开文件的字节流通道,然后将其传递给BufferedOutputStream
进行缓冲。 - 通过
write()
方法将字节数组等字节数据写入文件,最后关闭输出流确保数据写入并释放资源。
TIP: 获取一个[a,b]范围的随机整数
int num = (int)(Math.random() + (b - a +1)) + a;