Java 的 I/O 操作主要是指使用 Java 进行输入、输出操作,绝大多数传统 I/O 操作
类都在 java.io 包中。流(stream)是指在计算机的输入与输出直接运动的数据序列。
通过把不同类型的数据源之间的数据序列抽象为流,用统一的方式进行表示、处理。
File类:
File类可以定位文件:进行删除、获取文本本身信息等操作。
但是不能读写文件内容。
读写文件数据需要使用Java中的IO流,如字节流与字符流等
方法名称 | 说明 |
---|---|
public File(String pathname) | 根据文件路径创建文件对象 |
public File(String parent, String child) | 从父路径名字符串和子路径名字符串创建文件对象 |
public File(File parent, String child) | 根据父路径对应文件对象和子路径名字符串创建文件对象 |
注意:
File file = new File(“模块名\\src\\a.txt”);
抽象路径就是定义时使用的路径
方法名称 | 说明 |
---|---|
public boolean isDirectory() | 测试此抽象路径名表示的File是否为文件夹,文件夹不存在时也返回false |
public boolean isFile() | 测试此抽象路径名表示的File是否为文件,文件不存在时也返回false |
public boolean exists() | 测试此抽象路径名表示的File是否存在 |
public String getAbsolutePath() | 返回此抽象路径名的绝对路径字符串 |
public String getPath() | 获取我们定义时的路径 |
public String getName() | 返回由此抽象路径名表示的文件或文件夹的名称,带后缀 |
public long lastModified() | 返回文件最后修改的时间毫秒值 |
public long length() | 返回该文件的大小,字节数 |
String getParent() | 返回此 File 对象所对应目录(最后一级子目录)的父路径名 |
对于lastModified()方法,返回的是时间毫秒值,我们可以通过SimpleDateFormat对象来转化为具体时间,示例:
long time = file.lastModified();
System.out.println(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(time));
方法名称 | 说明 |
---|---|
public boolean createNewFile() | 创建一个新的空的文件,几乎不用这个方法,因为使用IO流时会自动帮我们创建文件 |
public boolean mkdir() | 只能创建一级文件夹,既可以在一个已经存在的文件夹里再创建一个文件夹(一级),如果在不存在的文件夹里创建文件夹会失败(多级) |
public boolean mkdirs() | 可以创建多级文件夹,因此一般使用这个 |
public boolean delete() | 删除由此抽象路径名表示的文件或空文件夹(为了安全),即使在文件被占用时也可以删 |
注意:
delete删除方法是不进回收站直接删除的,并且默认只能删除文件与空文件夹
以上创建方法都是创建File类对象时写一个不存在的路径文件,再使用这个对象的createNewFile()等方法创建
删除方法是创建File对象后调用这个对象的delete方法,就能将定义时的路径文件删除
方法名称 | 说明 |
---|---|
public String[] list() | 获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回。 |
public File[] listFiles() | 获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点),会将文件和文件夹都放进这个File数组中 |
注意:
package com.exp4.prj4.s1file;
import java.io.File;
public class FileRecursionList1 {
public static int dirNum = -1;
public static int fileNum;
public static void main(String[] args) {
File file = new File("D:\\eclipse\\lab");
print(file);
System.out.println("目录个数:" + dirNum);
System.out.println("文件个数:" + fileNum);
}
public static void print(File file){
if(file != null){
if (file.isDirectory()){
dirNum++;
File[] files = file.listFiles();
if (files != null && files.length > 0){
for (File f : files) {
print(f);
}
}
}else{
fileNum++;
System.out.println(file);
}
}
}
}
package com.exp4.prj4.s1file;
import java.io.File;
public class Search {
public static void main(String[] args) {
// 2. 调用函数开始寻找
searchFile(new File("D:\\"), "druid.properties");
}
/**
* 1. 定义方法递归遍历某文件夹,寻找目标文件
* @param file 文件夹对象
* @param fileName 目标文件名称
*/
public static void searchFile(File file, String fileName){
// 3. 判断是否为空以及是否是文件夹
if (file != null && file.isDirectory()){
// 4. 获取一级目录对象
File[] files = file.listFiles();
// 5. 判断数组对象是否为空以及是否为空数组
if (files != null && files.length > 0){
for (File f : files) {
// 6. 如果是文件直接判断是不是目标文件
if (f.isFile()){
if (f.getName().contains(fileName)) {
System.out.println("已找到文件,文件路径为: ");
System.out.println(f.getAbsolutePath());
}
}else {
// 7. 负责继续递归
searchFile(f, fileName);
}
}
}
}else{
System.out.println("传入File对象有误");
}
}
}
计算机底层不可以直接存储字符的。计算机中底层只能存储二进制(0、1),而且二进制可以转换成十进制
因此我们对字符进行十进制编号,再交给计算机底层转换为二进制编号进行存储
这套编号规则就是字符集
字符>=字节,一字节=1B,一个字节由八位二进制数组成(8bit)
ASCII(American Standard Code for Information Interchange,美国信息交换标准代码):包括了数字、英文、符号,表示不了中文
ASCII使用1个字节存储一个字符,一个字节是8位,总共可以表示128个字符信息,对于英文,数字来说是够用的
中文window系统默认的码表。兼容ASCII码表,也包含了几万个汉字,并支持繁体汉字以及部分日韩文字。
**注意:**GBK是中国的码表,一个中文以两个字节的形式存储。但不包含世界上所有国家的文字,并且英文还是采用一个字节进行存储
注意:
java中的编码与解码都是通过String实现的,String提供了一套用于编码与解码的API(方法)
编码就是将指定文字转换为字节
String提供了以下方法供使用者对字符进行编码:
方法名称 | 说明 |
---|---|
byte[] getBytes() | 使用平台的默认字符集(UTF-8)将该 String编码为一系列字节,将结果存储到新的字节数组中 |
byte[] getBytes(String charsetName) | 使用指定的字符集(如"GBK")将该 String编码为一系列字节,将结果存储到新的字节数组中,该方法会抛一个异常,怕你字符集名称写错 |
示例:
String s = "abc生是种花家人";
byte[] bytes = s.getBytes(); // 会默认使用UTF-8解码
System.out.println(bytes.length);
// 因为UTF-8中文使用三个字节,因此以上会输出(3+18=21)
System.out.println(Arrays.toString(bytes));
// 结果:[97, 98, 99, -25, -108, -97, -26, -104, -81, -25, -89, -115, -24, -118, -79, -27, -82, -74, -28, -70, -70]
byte[] bytes2 = s.getBytes("GBK"); // 使用GBK解码
System.out.println(bytes2.length);
// 因为GBK中文使用俩个字节,因此以上会输出(3+12=15)
System.out.println(Arrays.toString(bytes2));
// 结果:[97, 98, 99, -55, -6, -54, -57, -42, -42, -69, -88, -68, -46, -56, -53]
解码:把字节转换为对应的字符(中文)
String的提供了以下构造器供使用者实现对字符的解码:
构造器 | 说明 |
---|---|
String(byte[] bytes) | 通过使用平台的默认字符集(UTF-8)解码指定的字节数组来返回字符 |
String(byte[] bytes, String charsetName) | 通过指定的字符集解码指定的字节数组来返回字符 |
String(byte[] bytes, int off, int len) | 按照指定顺序参数 ,off 为起始下标,参数 len 为长度,对bytes中的数据进行解码 |
IO流按方向分:输入流,输出流
按流中的数据最小单位分:字节流,字符流
其中字节流可以操作所有类型的数据,因为一切数据都是以字节组成,我们一般使用字节流操作音视频文件,不操作文本文件
而字符流只能操作文本文件,我们也一般使用字符流操作文本文件(包括.java文件,.txt文件等)
高级流对象的创建一般需要包装一个低级流对象进去
高级流也称处理流,低级流也称节点流
节点流(低级流)是指直接操作数据读写的流,如FileInputStream,FileReader等
处理流(高级流)是指对已存在的流进行封装使功能更加强大灵活的流,如BufferedInputStream,BufferedReader,PrintWriter等
处理流和节点流体现了Java的装饰设计模式
因此IO流总共可以分为四大类:
流的概念:在IO流中,不管是字节还是字符,都可以当水一样在内存中流动,因此有的read()方法一次只能读取一个字节,就相当于一滴水一滴水的流动,一滴水一滴水的读取,因此流对象可以比作管道,用于接数据的字节数组可以比作桶
流使用步骤:
先看一下字节流的类结构:
InputStream是字节输入流的顶级父类,抽象类,其中定义的方法有:
方法名 | 作用 |
---|---|
int read() | 从输入流中读取一个字节,把它转换为 0-255 之间的整数返回,返回十进制下的这个字节 |
int read(byte[] b) | 从输入流中读取若干字节,保存到参数 b 指定的字节数组中 |
int read(byte[] b, int off, int len) | 从输入流中读取若干个字节,把它们保存到参数 b 指定的字节数组中。参数 off 指定在字节数组中开始保存数据的起始下标,参数 len 指定读取的字节数目 |
void close() | 关闭输入流 |
int available() | 返回可以从输入流中读取的字节数目 |
skip(long n) | 从输入流中跳过参数 n 指定数目的字节 |
boolean markSupported(), void mark(int readLimit), void reset() | 用于重复读入数据 |
OutputStream是字节输出流的顶级父类,抽象类,其中定义的方法有:
方法名 | 作用 |
---|---|
void write(int b) | 向输出流写出一个字节(作为 int 类型的 b 只取低位字节) |
void write(byte[] b) | 把参数 b 指定的字节数组中的所有字节写到输出流 |
void write(byte[] b,int off, int len) | 把参数 b 指定的字节数组中的若干字节写到输出流,参数 off 为起始下标,参数 len 为长度 |
void close() | 关闭输出流,会自动刷新数据 |
void flush() | OutputStream 类本身的 flush 方法不执行任何操作,其一些带缓冲区的子类覆盖了 flush 方法,该方法强制把缓冲区内的数据写到输出流中,即刷新数据的方法 |
注意:
写数据必须使用flush()方法刷新数据,不然数据可能还在内存中缓冲程序就已经结束了
构造器 | 说明 |
---|---|
public FileInputStream(File file) | 创建字节输入流管道与源文件对象接通 |
public FileInputStream(String pathname) | 创建字节输入流管道与源文件路径接通 |
方法名称 | 说明 |
---|---|
public int read() | 每次读取一个字节返回,如果字节已经没有可读的返回-1 |
public int read(byte[] buffer) | 每次读取一个字节数组到buffer中,返回读取的字节数,如果字节已经没有可读的返回-1 |
其中第二个方法的使用注意点:
其中每次只读取一个字节存在问题:
而每次只读取一个字节数组存在的问题:
虽然性能得到了提升,但是仍然无法避免读取中文时的乱码问题
例如数组长度只为2,此时就无法在UTF-8的情况下读取中文
因此我们以后只使用这个方法进行字节的复制进行文件拷贝,当需要读取字节内容时使用字符流
一次读取一个文件:
File file = new File("D://a.txt");
InputStream is = new FileInputStream(file);
byte[] bytes = new byte[(int)file.length()];
// 因为数组长度只能是int,而length()方法返回long,所以需要强制转换
方法名称 | 说明 |
---|---|
public byte[] readAllBytes() throws IOException | 直接将当前字节输入流对应的文件对象的字节数据装到一个字节数组返回 |
InputStream is = new FileInputStream("D://a.txt");
byte[] bytes = is.readAllBytes();
代码示例:
public class Demo {
public static void main(String[] args) throws Exception {
InputStream is = new FileInputStream("D://a.txt");
// 演示一个一个字节的读取,但以后几乎不使用这种方法
int b;
while ((b = is.read()) != -1){
System.out.print((char)b);
}
System.out.println();
System.out.println("-----------------------");
InputStream is2 = new FileInputStream("D://a.txt");
// 演示一个又一个字节数组的读取,但以后解析文本内容不使用这个方法,使用字符流
// 这种方法一般用于复制字节
byte[] bytes = new byte[3];
int len; // 用于接收返回的读取字节数
while ((len = is2.read(bytes)) != -1){
System.out.print(new String(bytes,0, len));
}
// 关闭流:
is2.close();
is.close();
}
}
构造器 | 说明 |
---|---|
public FileOutputStream(File file) | 创建字节输出流管道与源文件对象接通 |
public FileOutputStream(File file,boolean append) | 创建字节输出流管道与源文件对象接通,可追加数据,使用输出流会默认先将文件原有内容清空再写数据,写true表示追加数据管道,就不会清空原有数据了 |
public FileOutputStream(String filepath) | 创建字节输出流管道与源文件路径接通 |
public FileOutputStream(String filepath,boolean append) | 创建字节输出流管道与源文件路径接通,可追加数据,使用输出流会默认先将文件原有内容清空再写数据,写true表示追加数据管道,就不会清空原有数据了 |
方法名称 | 说明 |
---|---|
public void write(int a) | 写一个字节出去 |
public void write(byte[] buffer) | 写一个字节数组出去 |
public void write(byte[] buffer , int off , int len) | 写一个字节数组的一部分出去。参数 off 为起始下标,参数 len 为长度 |
flush() | 刷新流,使用这个方法之后还可以继续写数据 |
close() | 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据 |
注意:
OutputStream is = new FileOutputStream("D://a.txt"); // 目标文件可以不存在
// 一个一个字节写:
os.write('98');
os.write('a');
os.write('我'); // 识别不出来,乱码,因为中文有三个字节
// 使用字节数组写中文:
byte[] bytes = "我是中国人".getBytes();
os.write(bytes);
// 写换行符:
os.write("\r\n".getBytes(StandardCharsets.UTF_8));
因为任何文件的底层都是字节,拷贝是一字不漏的转移字节,只要前后文件格式、编码一致没有任何问题
因此我们对于字节流的使用经常是用来进行文件的拷贝
而读取文本文档我们一般不使用字节流而使用字符流
当我们需要进行文件夹的拷贝时就需要进行遍历了,将文件拷贝,将文件夹创建
思路:
示例:
public class Demo {
public static void main(String[] args) throws Exception {
InputStream is = new FileInputStream("D:/a.txt");
OutputStream os = new FileOutputStream("D://b.txt");
byte[] bytes = new byte[1024];
int len; // 使用len记录读取的字节数,再将len字节写入,防止写入过多
while ((len = is.read(bytes)) != -1){
os.write(bytes,0,len);
}
os.close();
is.close();
}
}
因为finally代码块是最终一定要执行的,而读写文件都会报异常,为了代码的健壮性,我们可以在代码执行完毕的最后在finally块释放资源,同时使代码更加简洁易懂
JDK 7和JDK9中都简化了资源释放操作,在JDK7之后我们可以将流对象的定义放在try()块中的括号里,这样子无论有没有出异常都会自动帮我们释放资源
而JDK9中我们可以先在try/catch块前定义对象,只需在try()块的括号中放入对象名就会自动帮我们释放资源
但这个方法多少有点鸡肋,定义在前面,但是try/catch语句块之后对象资源都被释放了,我们先定义了又有什么用,后面又用不了,因此我们一般不用这个方法
或许在流对象是方法传递进来的参数时使用这个方法有点用
注意:
public abstract class InputStream implements Closeable {}
public abstract class OutputStream implements Closeable, Flushable{}
代码示例:
public class Demo {
public static void main(String[] args) {
try (
InputStream is = new FileInputStream("D:/a.txt");
OutputStream os = new FileOutputStream("D://b.txt");
){
byte[] bytes = new byte[1024];
int len; // 使用len记录读取的字节数,再将len字节写入,防止写入过多
while ((len = is.read(bytes)) != -1){
os.write(bytes,0,len);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
照样先看一下字符流的类结构:
Reader是字符输入流的顶级父类,抽象类,其中定义的方法有:
方法名 | 作用 |
---|---|
int read() | 从输入流中读取一个字符,以 int 整数返回,读取不到数据(读取完时返回-1) |
int read(char[] b) | 从输入流中读取若干字符,保存到参数 b 指定的字符 |
int read(char[] b, int off, int len) | 数组中从输入流中读取若干个字符,把它们保存到参数 b 指定的字符数组中,参数 off 为起始下标,参数 len 为长度 |
void close() | 关闭输入流 |
skip(long n) | 从输入流中跳过参数 n 指定数目的字符 |
boolean markSupported(), void mark(int readLimit), void reset() | 用于重复读入数据 |
Writer是字符输出流的顶级父类,抽象类,其中定义的方法有:
方法名称 | 作用 |
---|---|
void write(int c) | 向输出流写出一个字符 |
void write(char[] b) | 把字符数组中的所有字符写到输出流 |
void write(char[] b,int off, int len) | 参数 off 为起始下标,参数 len 为长度 |
void flush() | Writer 类本身的 flush 方法不执行任何操作,其一些带缓冲区的子类覆盖了 flush 方法,该方法强制把缓冲区内的数据写到输出流中,即刷新数据 |
void close() | 关闭输出流,会自动刷新数据 |
Writer append(char c) | 向当前流追加字符 c,并返回当前字符输出流实例 |
注意:
作用:以内存为基准,把磁盘文件中的数据以字符的形式读取到内存中去
构造器:
构造器 | 说明 |
---|---|
public FileReader(File file) | 创建字符输入流管道与源文件对象接通 |
public FileReader(String pathname) | 创建字符输入流管道与源文件路径接通 |
方法名称 | 说明 |
---|---|
public int read() | 每次读取一个字符返回,如果字符已经没有可读的返回-1 |
public int read(char[] buffer) | 每次读取一个字符数组,返回读取的字符个数,如果字符已经没有可读的返回-1 |
int read(char[] b, int off, int len) | 数组中从输入流中读取若干个字符,把它们保存到参数 b 指定的字符数组中,参数 off 为起始下标,参数 len 为长度 |
注意:
构造器 | 说明 |
---|---|
public FileWriter(File file) | 创建字符输出流管道与源文件对象接通 |
public FileWriter(File file,boolean append) | 创建字符输出流管道与源文件对象接通,设置为true可追加数据 |
public FileWriter(String filepath) | 创建字符输出流管道与源文件路径接通 |
public FileWriter(String filepath,boolean append) | 创建字符输出流管道与源文件路径接通,设置为true可追加数据 |
方法名称 | 说明 |
---|---|
void write(int c) | 写一个字符 |
void write(char[] cbuf) | 写入一个字符数组 |
void write(char[] cbuf, int off, int len) | 写入字符数组的一部分 |
void write(String str) | 写一个字符串 |
void write(String str, int off, int len) | 写一个字符串的一部分 |
flush() | 刷新流,刷新完还可以继续写数据 |
close() | 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据 |
注意:
fw.write(“\r\n”);
高效原因:
缓冲流对象会在内存中创造一个缓冲区数组(缓冲管道),默认大小是8192字节(8kb),将原始流的数据先加载到这个8kb的缓冲区中
这样子我们每次读取数据时都是以内存的形式直接在缓冲区中拿,性能更好
而写入数据的时候也会创建一个8KB缓冲池(数组),数据就直接写入到缓冲池中去,写数据性能极高了。
缓冲流分类:
其中字符缓冲流都是Reader和Writer的直接子类
而字节缓冲流分别继承了FilterInputStream和FilterOutputStream,这俩个父类的父类的就是InputStream和OutputStrean
构造器 | 说明 |
---|---|
public BufferedInputStream(InputStream is) | 可以把低级的字节输入流包装成一个高级的缓冲字节输入流管道,从而提高字节输入流读数据的性能 |
public BufferedOutputStream(OutputStream os) | 可以把低级的字节输出流包装成一个高级的缓冲字节输出流,从而提高写数据的性能 |
构造器 | 说明 |
---|---|
public BufferedReader(Reader r) | 可以把低级的字符输入流包装成一个高级的缓冲字符输入流管道,从而提高字符输入流读数据的性能 |
public BufferedWriter(Writer w) | 可以把低级的字符输出流包装成一个高级的缓冲字符输出流管道,从而提高字符输出流写数据的性能 |
方法 | 说明 |
---|---|
public String readLine() | 读取一行数据返回,如果读取没有完毕,无行可读返回null |
读取时使用这个方法更加方便与符合逻辑
方法 | 说明 |
---|---|
public void newLine() | 换行操作 |
换行执行这个方法会更加方便
注意:
用于字符流中新增的俩个方法是父类中没有定义的方法,因此在创建字符缓冲流对象时不能使用多态的写法
在我们使用字符流读取文本文件时虽然一般不会产生中文乱码问题,但是当文档编码与代码编码不一致时仍然会产生中文乱码问题
这个时候就需要使用字符转换流
下面以字符输入转换流为例讲解:
转换流会先提取文件(GBK)的原始字节流,原始字节不会存在问题
然后把字节流以指定编码转换成字符输入流,这样字符输入流中的字符就不乱码
而字符输出转换流会用指定编码把字节输出流转换成字符输出流,从而可以指定写出去的字符编码
字符转换流分类:
其中文件字符输入流FileReader就是继承自InputStreamReader
文件字符输出流FileWrite也是继承自OutputStreamWrite
使用步骤:
构造器 | 说明 |
---|---|
public InputStreamReader(InputStream is) | 可以把原始的字节流按照代码默认编码转换成字符输入流。几乎不用,与默认的FileReader一样 |
public InputStreamReader(InputStream is ,String charset) | 可以把原始的字节流按照指定编码转换成字符输入流,这样字符流中的字符就不乱码了(重点) |
阅读FileReader源码后发现,在FileReader的默认构造器中,就是调用父类InputStreamReader的默认编码构造器,因此我们一般不会使用这个默认编码构造器
FileReader部分源码:
public FileReader(File file) throws FileNotFoundException {
super(new FileInputStream(file));
}
代码示例:
public class Demo {
public static void main(String[] args) {
// 使用转换流读取GBK编码的文件
try (
// 先创建字节输入流对象
InputStream is = new FileInputStream("D:/a.txt");
// 以GBK编码将字节转换为字符
Reader isr = new InputStreamReader(is, "GBK");
// 再将字符流包装到缓冲流中
BufferedReader br = new BufferedReader(isr);
){
String str;
while ((str = br.readLine()) != null){
System.out.println(str);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
控制写出去的字符编码有俩种方式:
可以把字符以指定编码获取字节后再使用字节输出流写出去,如:
“我爱你中国”.getBytes(“编码”)不推荐,太繁琐
使用字符输出转换流实现
字符输入转换流作用:可以把字节输出流按照指定编码转换成字符输出流
构造器:
构造器 | 说明 |
---|---|
public OutputStreamWriter(OutputStream os) | 可以把原始的字节输出流按照代码默认编码转换成字符输出流。几乎不用。 |
public OutputStreamWriter(OutputStream os,String charset) | 可以把原始的字节输出流按照指定编码转换成字符输出流(重点) |
阅读FileWriter源码后发现,在FileWriter的默认构造器中,就是调用父类OutputStreamWriter的默认编码构造器,因此我们一般不会使用这个默认编码构造器
FileWriter部分源码:
public FileWriter(String fileName) throws IOException {
super(new FileOutputStream(fileName));
}
构造器 | 说明 |
---|---|
public ObjectOutputStream(OutputStream out) | 把低级字节输出流包装成高级的对象字节输出流 |
方法名称 | 说明 |
---|---|
public final void writeObject(Object obj) | 把对象写出去到对象序列化流的文件中去,因为是独有方法,所以不能用多态的写法创建对象 |
构造器 | 说明 |
---|---|
public ObjectInputStream(InputStream out) | 把低级字节输入流包装成高级的对象字节输入流 |
方法名称 | 说明 |
---|---|
public Object readObject() | 把存储到磁盘文件中去的对象数据恢复成内存中的对象返回 |
作用:打印流可以实现方便、高效的打印数据到文件中去。打印流一般是指:PrintStream,PrintWriter两个类
其中PrintStream属于字节流
PrintWrite属于字符流
方便原因:可以实现打印什么数据就是什么数据,例如打印整数97写出去就是97,打印boolean的true,写出去就是true,不会出现打97出来的是97对应的’a’这种情况
高效原因:内部已经包装好了缓冲流
构造器 | 说明 |
---|---|
public PrintStream(OutputStream os) | 打印流直接通向字节输出流管道,需要追加数据时使用这个构造器 |
public PrintStream(File f) | 打印流直接通向文件对象 |
public PrintStream(String filepath) | 打印流直接通向文件路径 |
public PrintStream(File f, String charset) | 创建指定编码的打印流对象通向文件对象 |
观察源码发现,使用String filepath创建PrintStream对象实际上会在构造器内部根据路径创建FileInputStream对象
使用File f创建亦然
方法 | 说明 |
---|---|
public void print() | 打印任意类型的数据出去,而不是写 |
public void println() | 带换行 |
void flush() | 刷新数据 |
void close() | 关闭打印流,会自动刷新数据 |
构造器 | 说明 |
---|---|
public PrintWriter(OutputStream os) | 打印流直接通向字节输出流管道,需要追加数据时使用这个构造器 |
public PrintWriter (Writer w) | 打印流直接通向字符输出流管道 |
public PrintWriter (File f) | 打印流直接通向文件对象 |
public PrintWriter (String filepath) | 打印流直接通向文件路径 |
public PrintWriter (String filepath, String charset) | 创建指定编码的打印流对象通向文件对象 |
方法 | 说明 |
---|---|
public void print() | 打印任意类型的数据出去,是打印而不是写 |
public void println() | 带换行 |
void flush() | 刷新数据 |
void close() | 关闭打印流,会自动刷新数据 |
PrintStream ps = new PrintStream("文件地址");
System.setOut(ps); // 把系统打印流对象out改成我们的打印流
// 之后再使用打印语句就会打印到该文件中
其实在我们使用的System.out.println中,out就是一个打印流对象!
out的本质:public static final java.io.PrintStream out
因此我们平时使用的打印语句中的println方法和打印流的println方法是一样的
在源码中是在静态代码块中将out初始化为PrintStream对象,并默认打印到控制台中(其实控制台也是一个文件(out文件夹里))