文章说明
我一开始看书是《Java核心技术》,越看越晕乎。去看了一些视频后,决定写一篇学习总结,便于以后的复习。如有不足,还请指出。👍
一、File
作用
Java
使用 File
类来直接处理文件和文件系统,是文件和目录路径名的抽象表示。如果想要访问文件的属性或者目录结构,比如文件的大小、名称或者子目录结构等,可以选择File
对象,但是File
类没有指定信息怎样从文件读取或向文件存储。
1.常用构造方法
File(File parent, String child)
从父抽象路径名和子路径名字符串创建新的 File实例。
File(String pathname)
通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
File(String parent, String child)
从父路径名字符串和子路径名字符串创建新的 File实例。
比如File file=new File("E:"+File.separator+"java");
,表示E盘下名为java
的文件夹,这里要说明一下路径的表示方法(Windows系统
):
- 用
\\
作为分隔符,比如“E:\\java”
,用两个\
的原因是反斜杠字符在Java
字符串中是转义字符; - 用
/
作为分隔符,比如E:/java
; - 用
File.separator
作为分隔符,这样做的好处就是使程序具有可移植性,平常练习用前两种方便一些。
2.常用类库方法
方法 | 解释 |
---|---|
boolean createNewFile() | 创建由此抽象路径名命名的文件 |
boolean exists() | 测试此抽象路径名表示的文件或目录是否存在 |
String getPath() | 将此抽象路径名转换为路径名字符串 |
long length() | 返回由此抽象路径名表示的文件的大小 |
String[] list() | 返回一个字符串数组,命名由此抽象路径名表示的目录中的文件和目录 |
File[] listFiles() | 返回一个抽象路径名数组,表示由该抽象路径名表示的目录中的文件 |
boolean mkdir() | 创建由此抽象路径名命名的目录 |
备注
createNewFile()
和mkdir()
虽然都是创建,但二者却有天壤之别,前者创建文件,后者创建文件夹。
3.示例
遍历一个目录下的所有一级子目录和文件
public void listDirectory(File file) throws IOException {
//异常判断
if(!file.exists())
throw new IOException("目录"+file+"不存在");
if(!file.isDirectory())
throw new IOException(file+"不是目录");
//list()方法的实际应用
String[] fileNames=file.list();
for(String each:fileNames)
System.out.println(each);
}
如果在一级子目录的基础上还想要遍历所有子目录,要运用listFiles()
,得到所有File
对象,再挨个儿判断,如果是目录,就递归调用本方法。
二、文件字节流
作用
字节流可以用来读写任何类型的文件,比如图片、视频、文本文件等,操作单位是byte
。
1.FileInputStream
1.1 常用构造方法
FileInputStream(File file)
通过打开与实际文件的连接创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
FileInputStream(String name)
通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。
1.2 常用类库方法
方法 | 解释 |
---|---|
int available() | 返回从此输入流中剩余可以读取(或跳过)的剩余字节数的估计值 |
int read() | 从该输入流读取一个字节的数据 |
int read(byte[] b) | 从该输入流读取最多 b.length个字节的数据为字节数组 |
void close() | 关闭此文件输入流并释放与流相关联的任何系统资源 |
备注
- 用
read()
读取数据时,其实有一个指向字节的指针,当读到最后时,返回-1
,缺点是一次只能读取一个字节,效率低下; read(byte[] b)
返回的是一次读取字节的长度,读到最后时同样返回-1
。需要注意的是每次调用该方法,如果参数是同一个数组,都是从b[0]
开始读入到b
中;- 每次创建一个流对象之后,如果非空,用完之后都要调用
close()
,便于释放有限的操作系统资源。
1.3 示例
读取文件中的全部字节
//假设目录下存在文件
public static void main(String[] args) {
FileInputStream fileIn = null;
byte[] data=new byte[10];
try {
fileIn = new FileInputStream("E:/IO练习/字节输入流.txt");
//适合读取小文件,因为byte数组不能过大
byte[] data=new byte[fileIn.available()];
fileIn.read(data);
System.out.print(new String(data));
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileIn != null) {
try {
fileIn.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2.FileOutputStream
2.1 常用构造方法
FileOutputStream(File file)
创建文件输出流以写入由指定的 File对象表示的文件。
FileOutputStream(File file, boolean append)
创建文件输出流以写入由指定的 File对象表示的文件。
FileOutputStream(String name)
创建文件输出流以指定的名称写入文件。
FileOutputStream(String name, boolean append)
创建文件输出流以指定的名称写入文件。
备注
boolean append
决定了是否以追加的方式对文件进行写入。如果没有这个参数,相当于第一次创建输出流进行写入时,先对原来文件的内容进行清空再写入。- 创建
FileOutputStream
对象时,如果对应的目录文件不存在,会自动创建。
2.2 常用类库方法
方法 | 解释 |
---|---|
void write(byte[] b) | 将 b.length个字节从指定的字节数组写入此文件输出流 |
void write(byte[] b, int off, int len) | 将 len字节从位于偏移量 off的指定字节数组写入此文件输出流 |
void write(int b) | 将指定的字节写入此文件输出流 |
应用
向文件写入的关键代码如下:
fileOut = new FileOutputStream("E:/IO练习/字节输入流.txt",true);
byte[] data = {'x', 'z', 'y'};
fileOut.write(data);
fileOut.flush();
2.3 示例
复制文件
public static void main(String[] args) {
FileInputStream fileIn = null;
FileOutputStream fileOut = null;
try {
fileIn = new FileInputStream("E:/IO练习/字节输入流.txt");
//不用追加的原因在于每次写入都是同一个输出流,不会清空重写
fileOut = new FileOutputStream("E:/IO练习/字节输出流.txt");
byte[] data=new byte[2];
int count=0;
while((count=fileIn.read(data))!=-1){
fileOut.write(data,0,count);
}
fileOut.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
//分开捕获异常,使得二者相互独立
if (fileIn != null) {
try {
fileIn.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fileOut != null) {
try {
fileOut.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
容易产生疑问的地方
以.txt
文件为例,如果文件中有汉字,发现复制过去的文件可能出现乱码问题。原因在于编码方式,比如源文件是GBK
编码,Java
工程的编码方式是UTF-8
,就会产生这种问题,改正的话让二者编码方式一致就好。
三、文件字符流
作用
操作文件,只对普通文本进行读写,如.txt、.java
文件,注意word不是普通文件,平时判断的依据可以是能否用记事本打开。
1.FileReader
备注
是InputStreamReader
的子类。
1.1 常用构造方法
FileReader(File file)
创建一个新的 FileReader ,给出 File读取。
FileReader(String fileName)
创建一个新的 FileReader ,给定要读取的文件的名称。
1.2 常用类库方法
方法 | 解释 |
---|---|
void close() | 关闭流并释放与之相关联的任何系统资源 |
String getEncoding() | 返回此流使用的字符编码的名称 |
int read() | 读一个字符 |
int read(char[] cbuf, int offset, int length) | 将字符读入数组的一部分 |
备注
方法的返回值可以参考FileInputStream
~
1.3 示例
读取文件中的全部字节
public static void main(String[] args) {
FileReader fRead = null;
try {
//假设文件存在
fRead = new FileReader("E:/IO练习/字符输入流.txt");
char[] data = new char[16];
int count = 0;
while ((count = fRead.read(data)) != -1)
System.out.print(new String(data, 0, count));
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fRead != null) {
fRead.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.FileWriter
备注
是OutputStreamWriter
的子类。
2.1 常用构造方法
FileWriter(File file)
给一个File对象构造一个FileWriter对象。
FileWriter(File file, boolean append)
给一个File对象构造一个FileWriter对象。
FileWriter(String fileName)
构造一个给定文件名的FileWriter对象。
FileWriter(String fileName, boolean append)
构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据。
2.2 常用类库方法
方法 | 解释 |
---|---|
void close() | 关闭流,先刷新 |
void flush() | 刷新流 |
String getEncoding() | 返回此流使用的字符编码的名称 |
void write(char[] cbuf, int off, int len) | 写入字符数组的一部分 |
void write(int c) | 写一个字符 |
void write(String str, int off, int len) | 写一个字符串的一部分 |
应用
向文本文件写入字符的关键代码如下:
FileWriter fWrite=new FileWriter("E:/IO练习/字符输出流.txt");
char[] data = {'字','符','流'};
fWrite.write(data);
fWrite.flush();
2.3 示例
复制普通文本文件的关键代码
FileReader fRead=new FileReader("E:/IO练习/字符输入流.txt");
FileWriter fWrite=new FileWriter("E:/IO练习/字符输出流.txt");
char[] data=new char[1024*512];
int count=0;
while((count=fRead.read(data))!=-1)
fWrite.write(data,0,count);
fWrite.flush();
四、缓冲流
作用
前面介绍的几种IO流
都是直接进行内存和硬盘间的交互,读取效率较低,而缓冲流内部实现了缓冲机制—
- 进行读取时,先储存到缓冲区,利用时直接从缓冲区读到内存。当缓冲区为空时,会从硬盘中入一个新的数据块;
- 进行写入时,先储存到缓冲区中,直到缓冲区满才会将数据到送到硬盘。当然可以运用
flush()
人为地清空缓冲区。
1.BufferedReader
作用
从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取。
1.1 常用构造方法
BufferedReader(Reader in)
创建使用默认大小的输入缓冲区的缓冲字符输入流。
BufferedReader(Reader in, int sz)
创建使用指定大小的输入缓冲区的缓冲字符输入流。
备注
Reader
是抽象类,无法被实例化,所以我们传入的时候为了方便,可以传入Reader
的子类对象,比如FileReader
类的对象。
1.2 常用类库方法
方法 | 解释 |
---|---|
void close() | 关闭流并释放与之相关联的任何系统资源 |
String readLine() | 读一行文字 |
备注
- 调用
close()
时,不仅会关闭该缓冲流(包装流),而且会关闭构造时作为参数的流(节点流); readLine()
不会读取每行的换行符,返回的是读取到的字符串,没有字符可读时,会返回NULL
。
1.3示例
读取文件的全部字符
public static void main(String[] args) {
BufferedReader bRead = null;
try {
FileReader fRead = new FileReader("E:/IO练习/缓冲输入流.txt");
bRead = new BufferedReader(fRead);
String lineData;
while ((lineData = bRead.readLine()) != null)
System.out.println(lineData);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bRead != null) {
bRead.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.BufferedWriter
作用
将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入。
2.1 常用构造方法
BufferedWriter(Writer out)
创建使用默认大小的输出缓冲区的缓冲字符输出流。
BufferedWriter(Writer out, int sz)
创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区。
备注
Writer
是抽象类,无法被实例化,所以我们传入的时候为了方便,可以传入Writer
的子类对象,比如FileWriter
类的对象。
2.2 常用类库方法
方法 | 解释 |
---|---|
void close() | 关闭流并释放与之相关联的任何系统资源 |
void flush() | 刷新流,即清空缓冲区 |
void newLine() | 写入行分隔符 |
备注
- 调用
close()
时,不仅会关闭该缓冲流(包装流),而且会关闭构造时作为参数的流(节点流); newLine()
向输出流中写入一个行结束标志,它不是简单地换行符\n
或回车符\r
,而是系统定义的行隔离标志(line separator)
。
3.小结
以上两种缓冲流都是以字符为单位进行操作的,还有两种字节缓冲流—BufferedInputStream
和BufferedOutputStream
(可通过后缀进行区分),最好参照文件字节流进行学习,不会有太大差别,这里就不过多介绍~
五、数据流
作用
数据流除了可处理字节和字节数组外,还可以处理 int、float、boolean
等基本数据类型,也就是对二进制数据进行读写。
1.DataOutputStream
1.1 常用构造方法
DataOutputStream(OutputStream out)
创建一个新的数据输出流,以将数据写入指定的底层输出流。
1.2 常用类库方法
方法 | 解释 |
---|---|
int size() | 返回计数器的当前值 written ,到目前为止写入此数据输出流的字节数 |
void writeInt(int v) | 将底层输出流写入 int 作为四字节 |
void writeUTF(String str) | 使用 modified UTF-8 编码以机器无关的方式将字符串写入基础输出流 |
备注
DataOutputStream
有一个实例域written
,记录了到目前为止写入数据输出流的字节数;- 可以按顺序将数据和数据类型一并写入,但数据是对外不可见的,读取时也要按照写入的顺序依据数据类型读,否则得不到想要的结果,就好像加密文件一样。
应用
//仅展示关键代码
FileOutputStream fOut=new FileOutputStream("E:/IO练习/数据输出流.txt");
DataOutputStream dOut=new DataOutputStream(fOut);
dOut.writeUTF("你好");
dOut.writeByte(1);
dOut.writeBoolean(true);
结果我打开E:/IO练习/数据输出流.txt
是这样的:你好
。
2.DataInputStream
2.1 常用构造方法
DataInputStream(InputStream in)
创建使用指定的底层InputStream的DataInputStream。
2.2 常用类库方法
方法 | 解释 |
---|---|
boolean readBoolean() | 读取一个输入字节,并返回 true如果该字节不为零, false如果该字节是零 |
byte readByte() | 读取并返回一个输入字节 |
String readUTF() | 读取已使用 modified UTF-8 格式编码的字符串 |
备注
最好是从利用DataOutputStream
进行写入的文件中进行读取。
应用
FileInputStream fIn=new FileInputStream("E:/IO练习/数据输出流.txt");
DataInputStream dIn=new DataInputStream(fIn);
System.out.print(dIn.readUTF()+dIn.readByte()+dIn.readBoolean());
控制台输出结果是这样的:你好1true
,与存入一致。
六、标准输出流
1.PrintStream
作用
我们常用的System.out
就属于PrintStream
,能够方便地打印各种数据值的表示。
1.1 常用构造方法
PrintStream(File file)
使用指定的文件创建一个新的打印流,而不需要自动换行。
PrintStream(OutputStream out)
创建一个新的打印流。
PrintStream(String fileName)
使用指定的文件名创建新的打印流,无需自动换行。
1.2 常用类库方法
我们最开始学习Java运用的print()、printf()、println()
方法就源于这里,由于方法过多,这里就不展开叙述了,需要注意的是我们不需要手动关闭close()
方法。
1.3 示例
修改System.out输出方向
//仅展示关键代码
PrintStream pStream=new PrintStream("E:/IO练习/标准输出流.txt");
System.setOut(pStream);
System.out.println(12);
这样一来,12不会再输出到控制台,而是打印到了文件中,至于怎么修改回来,目前我的方法就是删除System.setOut(pStream);
这一代码…
七、对象流
作用
用来操作Java
中的对象,完成对象在内存和硬盘间的传递。
1、ObjectOutputStream
注释
将Java
对象的原始数据类型和图形写入OutputStream
,过程是把一个对象分成若干个序列,再依次写入,称为序列化(Serialize)。
- 只有支持
java.io.Serializable
接口的对象才能写入流中; java.io.Serializable
是标志性接口,不含方法,用来给虚拟机提供参考,当虚拟机看到此接口时,会为该类自动生成一个序列化版本号。- 序列化版本号是用来区分类的,如果之后修改了类的源代码,导致序列号版本号不同,反序列化失败。为了避免这种错误,可以手动生成,例如
private static final long serialVersionUID = 1336136411266510208L;
。
1.1 常用构造方法
ObjectOutputStream(OutputStream out)
创建一个写入指定的OutputStream的ObjectOutputStream。
1.2 常用类库方法
方法 | 解释 |
---|---|
void writeObject(Object obj) | 将指定的对象写入ObjectOutputStream |
1.3 示例
//创建一个商品类
public class Item implements Serializable {
private String name;
private int price;
Item(String name,int price){
this.name=name;
this.price=price;
}
//重载toString()方法,便于验证
@Override
public String toString() {
return "Item:"+'\n'+"name-"+name+'\n'+"price-"+price;
}
}
//添加类对象到文件中
public class TestItem {
public static void main(String[] args) {
try {
FileOutputStream fOut=new FileOutputStream("E:/IO练习/对象输出流.txt");
ObjectOutputStream oOUt=new ObjectOutputStream(fOut);
Item aItem=new Item("菠萝",10);
oOUt.writeObject(aItem);
oOUt.flush();
oOUt.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
打开文件之后是一堆乱码: sr From0To100.Item?e樨k焞 I priceL namet Ljava/lang/String;xp t 鑿犺悵
2、ObjectInputStream
注释
过程是把若干个序列重组成对象再进行读取,称为反序列化(Deserialize)。
- 只能从流中读取支持
java.io.Serializable
或java.io.Externalizable
接口的对象
2.1常用构造方法
ObjectInputStream(InputStream in)
创建从指定的InputStream读取的ObjectInputStream。
2.2常用类库方法
方法 | 解释 |
---|---|
Object readObject() | 从ObjectInputStream 读取一个对象 |
2.3 示例
//建立在上一个程序上的基础上,展示关键代码
FileInputStream fIn=new FileInputStream("E:/IO练习/对象输出流.txt");
ObjectInputStream oIn=new ObjectInputStream(fIn);
Object goods=oIn.readObject();
System.out.println(goods.toString());
输出结果为:
Item:
name-菠萝
price-10
备注
- 要想对多个对象序列化,可将对象放在同一个集合中,因为集合也是一个对象,成功的前提是集合和集合中对象对应的类都要实现
java.io.Serializable
接口; - 如果不想让某一实例域序列化,可利用关键词
transient
(游离的),比如private transient int price
,输出的结果中price=0
,也就是默认值。
八、IO与Properties联合使用
Properties类继承了Hashtable<Object,Object>
,可以利用输入流获取文件的信息。这里的文件比较特殊,格式都是key=value
,称为配置文件。以.properties
作为后缀的称为属性配置文件,通过Properties类可以获取其中的键值对,直接修改配置文件就起到了修改程序的作用。
src/class.properties文件信息如下:
classname=java.lang.String
public static void main(String[] args) throws IOException {
//新建文件输入流对象
FileReader reader=new FileReader("src/class.properties");
//新建Map集合
Properties ppt=new Properties();
//加载配置信息到Map集合中
ppt.load(reader);
//关闭流
reader.close();
//通过key获取value
String s=ppt.getProperty("classname");
}
总结
其实我几个月前已经学习过一次IO了,但是当时只是看了书没记笔记,现在回想起来发现全给忘记了,思绪比较混乱。现在通过这篇博客记录学习过程,以后复习时直接拿来看就好,顺便还能给自己增加访问量,一举两得😏
(欢迎评论区交流~)