输入、输出流
前记:
- 简单先过了一遍教材的IO知识
- 第一次简单参照JDK1.8文档来学习
一、基本概念
等回去看Java核心技术卷二时再来补充。
两大IO家族:
二、File类
1、定义
来自JDK1.8官方文档:
- 是用来文件和目录路径名的抽象表示。
- 该实例File类是不可变的; 也就是说,一旦创建,由File对象表示的抽象路径名永远不会改变。
- 主要用来获得文件或目录本身的一些信息,不涉及对文件的读取
- 可选系统有关的前缀字符串,如磁盘驱动器符, “/“为UNIX根目录,或**”\\”(因为字符串中\代表转义)**的Microsoft Windows UNC路径
- 注意:file重写了toString()方法。,返回创建该对象时的完整路径(包括文件名等)。
2、构造方法
一共有三种常用构造方法(其实一共有四个):
File file = new File("C:\\Users\\aj\\Desktop\\IO\\1.txt");
File file = new File("C:\\Users\\aj\\Desktop\\IO", "1.txt");
File file = new File(file, "1.txt");//其中,file是一个File对象,是一个路径
3、操作文件
public class TestMain3{
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\aj\\Desktop\\IO\\1.txt");
//File file = new File("C:\\Users\\aj\\Desktop\\IO", "1.txt");
//File file = new File(file, "1.txt");
System.out.println(file.createNewFile());//创建一个新文件,如果已经存在,创建失败
System.out.println(file.getName());//得到简要名称
System.out.println(file.canRead());//判断是否可读
System.out.println(file.canWrite());//判断是否可写
System.out.println(file.exists());//判断是否存在
System.out.println(file.length());//得到文件长度大小,如果是目录为0
System.out.println(file.getAbsolutePath());//得到绝对路径
System.out.println(file.getParent());//得到上一级目录名
System.out.println(file.isFile());//判断是否是一个文件
System.out.println(file.isDirectory());//判断是否是一个目录
System.out.println(file.isHidden());//判断是否是隐藏文件
System.out.println(file.lastModified());//得到文件最后修改时间
System.out.println(file.delete());//删除文件,如果是目录,则要为空才能删除
}
}
/**输出如下:
false
1.txt
true
true
true
0
C:\Users\aj\Desktop\IO\1.txt
C:\Users\aj\Desktop\IO
true
false
false
1607950742446
true
Process finished with exit code 0
**/
4、操作目录
创建目录
public class TestMain3{
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\aj\\Desktop\\IO", "1");
if (!file.exists()) {
//boolean mkdirs = file.mkdirs();//创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录。
// 请注意,如果此操作失败,它可能已成功创建一些必需的父目录。
System.out.println("创建目录成功吗?" + file.mkdir());//仅创建当前目录
}
}
}
/**
创建目录成功吗?true
Process finished with exit code 0
**/
列出所有文件
public class TestMain3{
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\aj\\Desktop\\IO");
String[] list = file.list();
for (String s : list) {
System.out.println(s);//可以看到,返回的是文件或目录的名字,不含路径,如果配合路径操作,可以利用file对象
}
System.out.println("-------");
File[] files = file.listFiles();
for (File file1 : files) {
System.out.println(file1);//可以看到得到的是绝对路径的File对象
}
}
}
/**
Notepad++.lnk
文件夹1
腾讯QQ.lnk
阿里巴巴Java开发手册(详尽版).pdf
-------
C:\Users\aj\Desktop\IO\Notepad++.lnk
C:\Users\aj\Desktop\IO\文件夹1
C:\Users\aj\Desktop\IO\腾讯QQ.lnk
C:\Users\aj\Desktop\IO\阿里巴巴Java开发手册(详尽版).pdf
Process finished with exit code 0
**/
列出已过滤的文件
public class TestMain3{
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\aj\\Desktop\\IO");
System.out.println("该目录下所有文件及目录如下");
String[] allFiles = file.list();
for (String file2 : allFiles) {
System.out.println(file2);
}
System.out.println("--------------");
System.out.println("过滤了.lnk后");
String[] list = file.list((dir, name) -> {
//dir是一个File类型的参数,只是这里没用到而已,并不代表它没有用
return name.endsWith(".lnk");
});
for (String s : list) {
System.out.println(s);
}
System.out.println("------------");
System.out.println("过滤了.html后");
fileNameFilterImp fileNameFilterImp = new fileNameFilterImp();
fileNameFilterImp.setSuffix("html");
File[] files = file.listFiles(fileNameFilterImp);
for (File file1 : files) {
System.out.println(file1);
}
}
}
class fileNameFilterImp implements FilenameFilter {
//如果实现了FileFilter,则只对File对象(会把所有文件都转为File对象)进行过滤判断,因此没有参数name
private String suffix = null;//设置要过滤的后缀
@Override
public boolean accept(File dir, String name) {//参数dir即为调用这个过滤器时的目录File,name为目录下的文件名(包括目录)
return name.endsWith("." + suffix);//如果文件名以name
}
public String getSuffix() {
return suffix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
}
/**
该目录下所有文件及目录如下
CodeBlocks.lnk
Notepad++.lnk
showLikeDetails.html
songDetails.html
userLogin.html
userRegister.html
文件夹1(故意加上.lnk后缀).lnk
文件夹2
腾讯QQ.lnk
阿里巴巴Java开发手册(详尽版).pdf
--------------
过滤了.lnk后
CodeBlocks.lnk
Notepad++.lnk
文件夹1(故意加上.lnk后缀).lnk
腾讯QQ.lnk
------------
过滤了.html后
C:\Users\aj\Desktop\IO\showLikeDetails.html
C:\Users\aj\Desktop\IO\songDetails.html
C:\Users\aj\Desktop\IO\userLogin.html
C:\Users\aj\Desktop\IO\userRegister.html
Process finished with exit code 0
**/
三、文件字节流
1、FileInputStream
Modifier and Type | Method and Description |
---|---|
int | available() 返回从此输入流中可以读取(或跳过)的剩余字节数的估计值,而不会被下一次调用此输入流的方法阻塞。 |
void | close() 关闭此文件输入流并释放与流相关联的任何系统资源。 |
protected void | finalize() 确保当这个文件输入流的 close 方法没有更多的引用时被调用。 |
FileChannel | getChannel() 返回与此文件输入流相关联的唯一的FileChannel 对象。 |
FileDescriptor | getFD() 返回表示与此 FileInputStream 正在使用的文件系统中实际文件的连接的 FileDescriptor 对象。 |
int | read() 从该输入流读取一个字节的数据。 |
int | read(byte[] b) 从该输入流读取最多 b.length 个字节的数据为字节数组。 |
int | ==read(byte[] b, int off, int len)==从该输入流读取最多 len 字节的数据为字节数组。 |
long | skip(long n) 跳过并从输入流中丢弃 n 字节的数据。 |
public class TestMain3{
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\aj\\Desktop\\IO", "testFileInputStream.txt");
FileInputStream fileInputStream = new FileInputStream(file);//也可以用String来构造
byte[] result = new byte[5];
int n;
int readCount = 0;
while ((n = fileInputStream.read(result)) != -1) {//把成功读取的字节数赋给n
String string = new String(result, 0, n);//把从0到n的字节数组转为String对象
System.out.print(string);
++readCount;
}
System.out.println();
System.out.println("文件大小=" + file.length() + "个字节");
System.out.println("一次读取" + result.length + "个字节");
System.out.println("共读取了" + readCount + "次");
fileInputStream.close();//如果不关闭,其它程序会访问不到。程序结束后虽然会自动关闭
}
}
/**文件内容:
hello my name is Piggg.
i am from the moon.
中文来了!怕不怕
**/
/注意:因为是按字节读的,所以中文很有可能会乱码!!
/**输出:
hello my name is Piggg.
i am from the moon.
中���来了��怕���怕
文件大小=70个字节
一次读取5个字节
共读取了14次
**/
2、FileOutputStream
注意:构造的时候,如果没有append参数,则会创建新文件,把原来的文件覆盖掉。
构造方法
Constructor and Description |
---|
FileOutputStream(File file) 创建文件输出流以写入由指定的 File 对象表示的文件。 |
FileOutputStream(File file, boolean append) 创建文件输出流以写入由指定的 File 对象表示的文件。 |
FileOutputStream(FileDescriptor fdObj) 创建文件输出流以写入指定的文件描述符,表示与文件系统中实际文件的现有连接。 |
FileOutputStream(String name) 创建文件输出流以指定的名称写入文件。 |
FileOutputStream(String name, boolean append) 创建文件输出流以指定的名称写入文件。 |
方法
Modifier and Type | Method and Description |
---|---|
void | close() 关闭此文件输出流并释放与此流相关联的任何系统资源。 |
protected void | finalize() 清理与文件的连接,并确保当没有更多的引用此流时,将调用此文件输出流的 close 方法。 |
FileChannel | getChannel() 返回与此文件输出流相关联的唯一的FileChannel 对象。 |
FileDescriptor | getFD() 返回与此流相关联的文件描述符。 |
void | write(byte[] b) 将 b.length 个字节从指定的字节数组写入此文件输出流。 |
void | write(byte[] b, int off, int len) 将 len 字节从位于偏移量 off 的指定字节数组写入此文件输出流。 |
void | write(int b) 将指定的字节写入此文件输出流。 //一个字节表示的整数,可以从0到127,即ASCILL码,如传入65,会写入A |
public class TestMain3{
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\aj\\Desktop\\IO", "testFileInputStream.txt");
FileOutputStream fileOutputStream = new FileOutputStream(file);
byte[] bytes = "i love apples and phones.".getBytes();
fileOutputStream.write(bytes);//写入完整数组
fileOutputStream.write(65);//写入字母 A
fileOutputStream.write(bytes, 0 , 3);//标准英文中,1个字符占一个字节,所以写入i l
//注意,只要不关闭输出流,那么会一直拼接在文件,而不会像创建输出流对象时会出现覆盖文件的情况
fileOutputStream.close();
}
}
/**
输出到文件的内容:
i love apples and phones.Ai l
**/
四、文件字符流
基本方法与文件字节流差不多,只是把对byte类型的操作更换为char类型的操作。
(感觉最后一个类就是为了名称好看,因为它没有具体方法,全部都从父类继承过来。。。就构造方法有更新)
(额可能是我现在太肤浅,没理解这样设计要干啥🐷。。。)
(哦,现在知道了,从构造方法中可以看出,FileReader/FileWriter是面向File类的,而InputStreamReader/OutPutStreamWriter是面向流的)
用到的:Reader – InputStreamReader – FileReader
Writer – OutPutStreamWriter – FileWriter
/**
* 从A.txt中以字符形式读取文件,并送到B.txt中,并在最后加上一句表扬的话
*/
public class TestMain3{
public static void main(String[] args) throws IOException {
File fileA = new File("C:\\Users\\aj\\Desktop\\IO\\A.txt");
File fileB = new File("C:\\Users\\aj\\Desktop\\IO\\B.txt");
if (!fileA.exists()) {
System.out.println(fileA.createNewFile());
}
if (!fileB.exists()) {
System.out.println(fileB.createNewFile());
}
FileReader fileReader = new FileReader(fileA);
FileWriter fileWriter = new FileWriter(fileB, false);
char[] result = new char[5];
int n;
int count = 0;
while ((n = fileReader.read(result)) != -1) {//如果到文件尾,则返回-1
fileWriter.write(result, 0 , n);//一次要设置一个n,因为result数组可能填不满
count++;
}
System.out.println("-------");
System.out.println("一次读取5个字符");
System.out.println("共读了" + count + "次");
fileWriter.write("\n说得对,说得好,说得呱呱叫");//可以写入String类型哦
fileReader.close();
fileWriter.close();
}
}
/**
A.txt:
Java是一门面向对象编程语言,
不仅吸收了C++语言的各种优点,
还摒弃了C++里难以理解的多继承、指针等概念,
因此Java语言具有功能强大和简单易用两个特征。
Java语言作为静态面向对象编程语言的代表,
极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程。
**/
/********************************************************************
B.txt:
Java是一门面向对象编程语言,
不仅吸收了C++语言的各种优点,
还摒弃了C++里难以理解的多继承、指针等概念,
因此Java语言具有功能强大和简单易用两个特征。
Java语言作为静态面向对象编程语言的代表,
极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程。
说得对,说得好,说得呱呱叫
**/
/********************************************************************
输出:
-------
一次读取5个字符
共读了29次
Process finished with exit code 0
**/
五、缓冲流
- 从字符输入(出)流读取(写入)文本,缓冲字符,以提供字符,数组和行的高效读取(写入)
- BufferedReader和BufferedWriter称为上层流,而它们指向的字符流称为底层流
- 关闭输出流时,首先关闭缓冲流,再关闭缓冲流指向的流,即先关闭底层流再关闭上层流。写代码时,只要关闭上层流即可,底层流会自动关闭。
/**
功能与上面那个程序一样
**/
public class TestMain3{
public static void main(String[] args) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new FileReader("C:\\Users\\aj\\Desktop\\IO\\A.txt"));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("C:\\Users\\aj\\Desktop\\IO\\B.txt"));
String s;
while ((s = bufferedReader.readLine()) != null) {
bufferedWriter.write(s);
bufferedWriter.newLine();//新建一个空行
}
bufferedWriter.write("说得对,说得好,说得呱呱叫");
bufferedWriter.flush();
bufferedReader.close();
bufferedWriter.close();
}
}
六、随机流与数据流
- 数据流和随机流都涉及到了非常复杂的编码知识,可以参看文章:https://blog.csdn.net/qq_47234534/article/details/111349801
- 实现了DataInput接口和DataOutput接口即数据流,所以可以对文件同时进行输入输出
Constructor and Description |
---|
RandomAccessFile(File file, String mode) 创建一个随机访问文件流从File 参数指定的文件中读取,并可选地写入文件。 |
RandomAccessFile(String name, String mode) 创建随机访问文件流,以从中指定名称的文件读取,并可选择写入文件。 |
mode:r(只读)、rw(可读写)(更多看官方文档)
注意:输出时,它不会刷新文件。
Modifier and Type | Method and Description |
---|---|
void | close() 关闭此随机访问文件流并释放与流相关联的任何系统资源。 |
long | getFilePointer() 返回此文件中的当前偏移量。 |
long | length() 返回此文件的长度。 |
int | read() 从该文件读取一个字节的数据。 |
int | read(byte[] b) 从该文件读取最多 b.length 字节的数据到字节数组。 |
int | read(byte[] b, int off, int len) 从该文件读取最多 len 个字节的数据到字节数组。 |
String | readLine() 从此文件中读取下一行文本。 |
void | seek(long pos) 设置文件指针偏移,从该文件的开头测量,发生下一次读取或写入。 |
void | setLength(long newLength) 设置此文件的长度。 |
int | skipBytes(int n) 尝试跳过 n 字节的输入丢弃跳过的字节。 |
void | write(byte[] b) 从指定的字节数组写入 b.length 个字节到该文件,从当前文件指针开始。 |
void | write(byte[] b, int off, int len) 从指定的字节数组写入 len 个字节,从偏移量 off 开始写入此文件。 |
public class TestMain3{
public static void main(String[] args) throws IOException {
File file = new File("C:\\Users\\aj\\Desktop\\IO\\A.txt");
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
double[] data = {1.2, 2, 3, 4, 5, 6.6, 7};
for (int i = 0; i < 7; i++) {
randomAccessFile.writeDouble(data[i]);
}
for (int i = 6; i >= 0; i--) {
randomAccessFile.seek(i * 8);
System.out.println(randomAccessFile.readDouble());
System.out.println();
}
}
}
七、数组流
- 流的源和目的地除了可以是文件外,还可以是计算机内存
- 字节数组流和字符数组流分别是InputStream家庭和Reader家庭的
public class TestMain3{
public static void main(String[] args) throws IOException {
byte[] bytes = "You are a pig".getBytes();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();//不像文件流要指定文件
byteArrayOutputStream.write(bytes);//向内存写
System.out.println(new String(byteArrayOutputStream.toByteArray()));//把缓冲中的字节数组打印出来
byte[] bytes2 = new byte[byteArrayOutputStream.toByteArray().length];//新建一个内存,因为要从内存中读东西进来
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());//构建读取的时候,要指定从哪个数组流读取
byteArrayInputStream.read(bytes2);//读
System.out.println(new String(bytes2));
}
}
八、对象流
- 写入文件后,再读出来,可以得到两个完全不同的对象,因此可以用于对象克隆
- 如果对象内的数据域含有另一个对象,那么另一个对象也是要实现Serializable标记接口
public class TestMain4 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
TV tv = new TV();
tv.name = "国家级电视";
tv.price = 3.0;
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("C:\\Users\\aj\\Desktop\\IO\\A.txt"));
objectOutputStream.writeObject(tv);
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("C:\\Users\\aj\\Desktop\\IO\\A.txt"));
TV tv2 = (TV)objectInputStream.readObject();
System.out.println(tv == tv2);//从这里的结果false可以看到,这两个对象是完全不同的两个对象,因此可以应用于对象的克隆
System.out.println(tv2.name);
System.out.println(tv2.price);
}
}
class TV implements Serializable {//要实现Serializable接口,这是一个标记接口
String name;
double price;
}
//输出如下:
false
国家级电视
3.0