每日一言
All of life’s journeys come with meetings, partings, and reunions. – Meryl Stryfe
from Trigun
BufferedReader & System.in
System.in
是java中的一个静态成员变量,属于 java.lang.System
类,它是一个 InputStream
,绑定到键盘,直接使用它进行读取有许多不方便之处:
- 它只能以字节为单位读取数据。
- 读取数据需要手动处理字节到字符的转换,以及处理不同数据类型(例如整数、浮点数等)。
- 中文字符通常占用多个字节,如果一次读取的字节数不足以包含一个完整的中文字符,则需要多次读取并将结果拼接起来。
- 需要手动处理异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import java.io.IOException;
public class PrintSystemIn { public static void main(String[] args) { try { System.out.println("Enter a character:"); int byteRead = System.in.read(); if (byteRead != -1) { System.out.println("You entered: " + (char) byteRead); } } catch (IOException e) { System.err.println("Error reading input: " + e.getMessage()); } } }
|

所以,直接通过System.in来进行程序输入是极为不方便的。
所以我们通常将System.in包装在一个InputStreamReader类中,将读取的字节流转换为字符流。在java中,char类型占用两个字节的空间,对于文本数据,我们可以通过InputStreamReader类来将原始的字节流合并为字符流,将两个字节的内容合并的一个char类型数据。
再将整体包装在一个BuffereredReader中,实现缓冲读取字节流转化为字符流的过程。
需要引用以下头文件:
1 2 3 4 5
| import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException;
import java.io.*;
|
创建方式如下:
1 2
| BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
|
BufferedReader 对象创建后,我们便可以使用 read() 方法从控制台读取一个字符,或者用 readLine() 方法读取一个字符串。
但是在使用BufferedReader进行读取之前,我们还需要了解一点东西。如,我们使用如下的方式进行输入时:
1 2 3 4 5 6 7 8 9 10 11 12
| import java.io.BufferedReader; import java.io.InputStreamReader;
public class BfReader { public static void main(String[] args) { System.out.println("请输入一个字符:"); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); char c = (char)br.read(); System.out.println("您输入的字符是:" +c); } }
|
编译器会进行如下报错:

Unhandled exception type IOException:未处理的 IOException 异常类型
BufferedReader.read()方法会抛出一个异常 IOException
,因此我们需要使用 try-catch
来进行异常处理,这也就是我们之前引用的第三个头文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException;
public class BfReader { public static void main(String[] args) { System.out.println("请输入一个字符:"); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); try { char c = (char) br.read(); System.out.println("您输入的字符是:" + c); } catch (IOException e) { System.err.println("读取字符时发生错误: " + e.getMessage()); } } }
|
此时就能够正常的读取并打印中文了:

稍作修改就可以改为读取字符串:
注意,read()返回的是一个char类型,readline()返回的是一个String类型。
1 2 3 4 5 6 7 8 9
| public class BfReader { public static void main(String[] args) throws IOException { System.out.println("请输入字符串:"); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String str = br.readLine(); System.out.println("您输入的字符是:" + str); } }
|

此处我们展示了第二种异常处理的方式:这种异常处理方式并不直接在main()的内部进行异常处理,而是将异常处理交给更高层,让代码看起来更加简洁,关于异常处理的详细内容,我们后续再来详细了解。
读取int、float、double类型数据
通过BufferedReader读取int、float、double类型需要借助三个java静态函数,分别是:
1 2 3
| int intValue = Integer.parseInt(bf.readLine()); float floatValue = Float.parseFloat(bf.readLine()); double doubleValue = Double.parseDouble(bf.readLine());
|
这三个函数的参数传入参数都是String类型,所以一定要通过 readLine()
方法进行读取,读取的字符串形式一定为一个标准的整数、小数。否则会触发 NumberFormatException
的异常。
Xxx.parseXxx()
函数会将读取的字符串形式的数据转化为对应的数据类型返回。
读取代码示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader;
public class ReadInput {
public static void main(String[] args) { BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
try { System.out.println("请输入一个字符串:"); String text = bf.readLine(); System.out.println("你输入的字符串是: " + text);
System.out.println("请输入一个整数:"); int intValue = Integer.parseInt(bf.readLine()); System.out.println("你输入的整数是: " + intValue);
System.out.println("请输入一个浮点数:"); float floatValue = Float.parseFloat(bf.readLine()); System.out.println("你输入的浮点数是: " + floatValue);
System.out.println("请输入一个双精度浮点数:"); double doubleValue = Double.parseDouble(bf.readLine()); System.out.println("你输入的双精度浮点数是: " + doubleValue);
} catch (IOException e) { System.err.println("读取输入时发生错误: " + e.getMessage()); } catch (NumberFormatException e) { System.err.println("输入的数字格式不正确: " + e.getMessage()); } } }
|
这种读取方式只允许我们一行输入一个数据,比较繁琐。

Scanner类
java.util.Scanner 是 Java5 的新特征,我们可以通过 Scanner 类来获取用户的输入。
引头文件
1
| import java.util.Scanner;
|
创建方法:
1
| Scanner s = new Scanner(System.in);
|
next()读取
在使用next读取前,我们通常使用hasNext()方法来检测是否还有内容未读取。
next()方法会以空格,回车等为分割符,一次只会读取一个单词,hasNext()方法会检测是否还有未读的单词,直到读入EOF返回false。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import java.util.Scanner; public class ScannerDemo { public static void main(String[] args) { Scanner scan = new Scanner(System.in);
System.out.println("next方式接收:"); while (scan.hasNext()) { String str1 = scan.next(); System.out.println("输入的数据为:" + str1); } System.out.println("next方式接收结束"); scan.close(); } }
|

可以通过按 Ctrl + Z
然后按 Enter
键来发送 EOF(End Of File)信号,从而结束输入。
nextLine()读取
在使用nextLine读取前,我们通常使用hasNextLine()方法来检测是否还有内容未读取。
nextLine()方法将以换行符为分割符,一次读取一行的内容。hasNextLine()方法会检测是否还有未读的行,直到读入EOF返回false。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import java.util.Scanner; public class ScannerDemo { public static void main(String[] args) { Scanner scan = new Scanner(System.in);
System.out.println("nextLine方式接收:"); while (scan.hasNextLine()) { String str1 = scan.nextLine(); System.out.println("输入的数据为:" + str1); } System.out.println("nextLine方式接收结束"); scan.close(); } }
|

读取int、float、double类型数据
Scanner类同样对于int、float、double类型的数据提供了支持。但是在读取之前同样建议通过函数 hasNextXxx()
进行验证,再通过nextXxx()来读取。
读取示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| import java.util.Scanner;
public class ReadInput {
public static void main(String[] args) { Scanner scanner = new Scanner(System.in);
try { System.out.println("请输入一个字符串:"); if (scanner.hasNextLine()) { String text = scanner.nextLine(); System.out.println("你输入的字符串是: " + text); } else { System.out.println("你没有输入任何字符串"); }
System.out.println("请输入一个整数、一个浮点数、一个双精度浮点数:"); if (scanner.hasNextInt()) { int intValue = scanner.nextInt(); System.out.println("你输入的整数是: " + intValue); } else { System.out.println("输入的不是整数!"); }
if(scanner.hasNextFloat()) { float floatValue = scanner.nextFloat(); System.out.println("你输入的浮点数是: " + floatValue); } else { System.out.println("输入的不是浮点数!"); }
if(scanner.hasNextDouble()) { double doubleValue = scanner.nextDouble(); System.out.println("你输入的双精度浮点数是: " + doubleValue); } else { System.out.println("输入的不是双精度浮点数!"); }
} catch (Exception e) { System.err.println("读取输入时发生错误: " + e.getMessage()); } finally { scanner.close(); } } }
|

这样我们就无需对于每一行的数据格式进行严格要求,只要通过空格将我们想要输入的数据隔开就好。
控制台output
java中的控制台输出,通过 System.out
对象来实现。System.out
是 java.io.PrintStream
的一个指向控制台的实例。
我们这里先介绍三种标准的输出函数
System.out.print(data)
作用:将指定的数据输出到控制台, 不换行 。
1 2 3 4 5 6 7 8 9
| public class PrintExample { public static void main(String[] args) { System.out.print("Hello, "); System.out.print("World!"); System.out.print(123); System.out.print(3.14); } }
|
运行结果:

System.out.println(data)
作用:将指定的数据输出到控制台,每次输出换行。
1 2 3 4 5 6 7 8 9
| public class PrintlnExample { public static void main(String[] args) { System.out.println("Hello, "); System.out.println("World!"); System.out.println(123); System.out.println(3.14); } }
|
运行结果:

对于以上两函数的data可以是:
- int,long,double等数字类型
- char类型
- String类型
- boolean类型
也可以是:
第一种情况输出的内容就是变量的值。对于boolean类型输出的就是 true
或 false
。
我们详细讲解一下当传入参数为对象时,程序会输出什么。
首先我们写这样一个简单的程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class PrintlnExample { public static void main(String[] args) { MyClass mc1 = new MyClass(10); System.out.println(mc1); } }
class MyClass{ int a; String str = "It's my class, a = " + a; public MyClass(int x){ a = x; } }
|
我们创建了一个对象,并且用println打印它。
运行结果:

打印的结果为:类名
+ @
+ 对象的哈希码的十六进制表示
好,现在又出现了一个陌生的名词:“对象的哈希码的十六进制表示”。我们先暂时忽视这一概念的内部实现,只需知道该值是根据对象的内部状态计算出来的。
现在我们给MyClass类添加一个名为:toString()
的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class PrintlnExample { public static void main(String[] args) { MyClass mc1 = new MyClass(10); System.out.println(mc1); } }
class MyClass{ int a; String str = "It's my class, a = " + a; public MyClass(int x){ a = x; } public String toString(){ return str; } }
|
运行结果:

打印结果变为了我们编写的toString()函数的返回值。
由此,我们可以总结打印对象的过程如下:
- 检查对象的toString()方法
System.out.println()
会首先检查传入的对象是否定义了 toString()
方法。
- 如果对象所属的类重写了
toString()
方法,那么 System.out.println()
将调用这个重写后的 toString()
方法,并打印该方法返回的字符串。
- 如果对象所属的类没有重写
toString()
方法,那么 System.out.println()
将调用该对象从 java.lang.Object
类继承来的默认 toString()
方法。
- 默认
toString()
方法:
java.lang.Object
类提供的默认 toString()
方法会返回一个字符串,该字符串包含以下信息:
- 对象的类名(包括包名,如果存在)。
@
符号。
- 对象的哈希码的十六进制表示。
- 例如,默认的输出格式可能类似于
com.example.MyClass@1a2b3c4d
。
System.err.println()
System.err.println 用于将文本输出到标准错误流(standard error stream)的方法,它与System.out.println的及其相似,但是输出目标不同。
该方法主要目的是将error信息与正常output的输出进行区分,当他们可以分别被重定向到不同位置。
1 2 3 4 5
| try {
} catch (Exception e) { System.err.println("错误信息"); }
|
控制台的输入输出只涉及到字符流的输入输出,但是对于文件的输入输出,可能涉及字符流和字节流两种情况。
字符流的文件输入
字符流的文件输入我们依然可以使用控制台input中的两种方法,只需要将System.in改为对应的文件即可。
假设在如下路径下的文件
1
| D:\windows\desktop\text.txt
|
用Scanner读入:
1 2 3
| ring FilePath = "D:\windows\desktop\text.txt" File file = new File(FliePath); Scanner sc = new Scanner(file);
|
BufferedReader 读入:
1
| BufferedReader bf = new BufferedReader(new InputStreamReader(new FileInputStream(filePath)));
|
与Scanner直接将 System.in
替换为 File
对象不同,BufferedReader需要将 System.in
替换为 FileInputStream
对象。
或者可以直接使用:
1
| BufferedReader br = new BufferedReader(new java.io.FileReader(filePath))
|
差距对比:
特性 |
BufferedReader(new FileReader(filePath)) |
BufferedReader(new InputStreamReader(new FileInputStream(filePath))) |
底层输入流 |
FileReader |
FileInputStream |
字符编码处理 |
使用系统默认编码 |
需要显式指定编码 |
编码问题 |
可能出现乱码 |
可以通过指定编码避免乱码 |
代码复杂度 |
简单 |
稍复杂 |
性能 |
相对较低 |
相对较高 |
适用场景 |
读取文本文件,简单场景 |
读取文本文件,需要控制编码,性能要求较高场景 |
字符流的文件输出
字符流的文件输出我们可以通过对于System.out的重定向来实现。
引入头文件:
1 2
| import java.io.FileOutputStream; import java.io.PrintStream;
|
创建一个FileOutputStream类,指定输出文件
1
| FileOutputStream fos = new FileOutputStream("Output.txt");
|
创建PrintStream,将FileOutputStream包裹
1
| PrintStream ps = new PrintStream(fos);
|
使用System.setOut(),将字符流输出重定向到文件
之后就可以使用System.out.print等函数进行文件输出。
针对System.err的重定向则使用方法:System.setErr()
使用完毕后关闭文件流:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintStream;
public class RedirectSystemOutToFile { public static void main(String[] args){ try { FileOutputStream fos = new FileOutputStream("output.txt");
PrintStream ps = new PrintStream(fos);
System.setOut(ps);
System.out.println("成功定向到文件!! 恭喜你!!");
ps.close(); fos.close(); } catch (FileNotFoundException e) { System.err.println("Error creating output file: " + e.getMessage()); } catch (Exception e) { System.err.println("Error closing stream: " + e.getMessage()); } } }
|
恢复控制台输出
如果在文件输出结束后,你想要恢复文件输出,则需要在更改为文件输出之前,保存原本的输出流
1
| PrintStream originalOut = System.out;
|
恢复:
1
| System.setOut(originalOut);
|
将你从控制台输入的字符转存到文件保存起来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import java.io.File; import java.io.FileOutputStream; import java.io.PrintStream; import java.util.Scanner; public class RecordYourInput { public static void main(String[] args){ try { File file = new File("input.txt"); Scanner sc = new Scanner(System.in); PrintStream os = System.out; FileOutputStream fos = new FileOutputStream(file); PrintStream ps = new PrintStream(fos); System.setOut(ps); while(sc.hasNextLine()){ String input = sc.nextLine(); System.out.println(input); if(input.equals("exit")){ break; } } } catch (Exception e) { System.err.println("Error: " + e.getMessage()); } } }
|
我们运行程序输入如下字符:

可以看到工作目录下生成input.txt,内容如下:

字节流的文件输入
对于很多文件,如图片、视频等文件,他们的内容不是以字符编码进行存放,而是以字节为单位进行读取,对于这些文件,我们就不能像读取 .txt
文件一样通过字符流读取他们,而是需要通过字节流的方式进行读取。
字节流的input通过 FileInputStream
类来实现,创建一个字节输入流:
1 2
| File f = new File("C:/java/hello"); InputStream in = new FileInputStream(f);
|
方法 |
描述 |
示例代码 |
int read() |
读取一个字节的数据,返回值为 0 到 255 之间的整数。如果到达流的末尾,返回 -1。 |
int data = in.read(); |
int read(byte[] b) |
从输入流中读取字节,并将其存储在字节数组 b 中,返回实际读取的字节数。如果到达流的末尾,返回 -1。 |
byte[] buffer = new byte[1024]; int bytesRead = in.read(buffer); |
int read(byte[] b, int off, int len) |
从输入流中读取最多 len 个字节,并将它们存储在字节数组 b 的 off 偏移位置,返回实际读取的字节数。如果到达流的末尾,返回 -1。 |
byte[] buffer = new byte[1024]; int bytesRead = in.read(buffer, 0, buffer.length); |
long skip(long n) |
跳过并丢弃输入流中的 n 个字节,返回实际跳过的字节数。 |
long skippedBytes = in.skip(100); |
int available() |
返回可以读取的字节数(不阻塞)。 |
int availableBytes = in.available(); |
void close() |
关闭输入流并释放与该流相关的所有资源。 |
in.close(); |
void mark(int readlimit) |
在流中的当前位置设置标记,readlimit 是可以读取的字节数上限。 |
in.mark(1024); |
void reset() |
将流重新定位到上次标记的位置,如果没有标记或标记失效,抛出 IOException 。 |
in.reset(); |
boolean markSupported() |
检查当前输入流是否支持 mark() 和 reset() 操作。 |
boolean isMarkSupported = in.markSupported(); |
字节流的文件输出
字节流的文件输出通过 FileOutputStream
类来实现,创建一个文件输出流:
1 2
| File f = new File("C:/java/hello"); OutputStream fOut = new FileOutputStream(f);
|
方法 |
描述 |
示例代码 |
void write(int b) |
将指定的字节写入输出流,b 的低 8 位将被写入流中。 |
fOut.write(255); |
void write(byte[] b) |
将字节数组 b 中的所有字节写入输出流。 |
byte[] data = "Hello".getBytes(); fOut.write(data); |
void write(byte[] b, int off, int len) |
将字节数组 b 中从偏移量 off 开始的 len 个字节写入输出流。 |
byte[] data = "Hello".getBytes(); fOut.write(data, 0, data.length); |
void flush() |
刷新输出流并强制写出所有缓冲的数据,确保数据被立即写入目标输出。 |
fOut.flush(); |
void close() |
关闭输出流并释放与该流相关的所有资源。关闭后不能再写入。 |
fOut.close(); |
java程序更换win11壁纸
在本地路径下存有一张好看的图片:D:\OneDrive\图片\picture\19.png

我希望通过一个java程序来帮我将图片替换为我电脑的壁纸。
首先我们需要知道:
- win11壁纸路径为:
C:\Users\%username%\AppData\Roaming\Microsoft\Windows\Themes
目录下的 TranscodedWallpaper
文件,并无文件后缀。
所以我们只需要读取我们喜欢的图片的数据,再覆盖原本的壁纸文件即可。
这是我现在的桌面:

我们执行下面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import java.io.*;
public class WallPaperChange { public static void main(String[] args){ String sourcePath = "D:\\OneDrive\\图片\\picture\\19.png"; String targetPath = "C:\\Users\\XXY\\AppData\\Roaming\\Microsoft\\Windows\\Themes\\TranscodedWallpaper"; File sourceFile = new File(sourcePath); File targetFile = new File(targetPath); try (InputStream s = new FileInputStream(sourceFile); OutputStream t = new FileOutputStream(targetFile)) { byte[] b = new byte[s.available()]; s.read(b); t.write(b); System.out.println("Wallpaper changed successfully"); } catch (FileNotFoundException e) { System.err.println("File not found: " + e.getMessage()); } catch (IOException e) { System.err.println("An I/O error occurred: " + e.getMessage()); } } }
|
现在我们通过任务管理器将Windows资源管理器重新启动;

可以看到壁纸已经更换为我们想要的壁纸:
