文章说明
我一开始看书是《Java核心技术》,越看越晕乎。去看了一些视频后,决定写一篇学习总结,便于以后的复习。如有不足,还请指出。
作用
Java
使用 File
类来直接处理文件和文件系统,是文件和目录路径名的抽象表示。如果想要访问文件的属性或者目录结构,比如文件的大小、名称或者子目录结构等,可以选择File
对象,但是File
类没有指定信息怎样从文件读取或向文件存储。
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
作为分隔符,这样做的好处就是使程序具有可移植性,平常练习用前两种方便一些。方法 | 解释 |
---|---|
boolean createNewFile() |
创建由此抽象路径名命名的文件 |
boolean exists() |
测试此抽象路径名表示的文件或目录是否存在 |
String getPath() |
将此抽象路径名转换为路径名字符串 |
long length() |
返回由此抽象路径名表示的文件的大小 |
String[] list() |
返回一个字符串数组,命名由此抽象路径名表示的目录中的文件和目录 |
File[] listFiles() |
返回一个抽象路径名数组,表示由该抽象路径名表示的目录中的文件 |
boolean mkdir() |
创建由此抽象路径名命名的目录 |
备注
createNewFile()
和mkdir()
虽然都是创建,但二者却有天壤之别,前者创建文件,后者创建文件夹。
遍历一个目录下的所有一级子目录和文件
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
FileInputStream(File file)
通过打开与实际文件的连接创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
FileInputStream(String name)
通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。
方法 | 解释 |
---|---|
int available() |
返回从此输入流中剩余可以读取(或跳过)的剩余字节数的估计值 |
int read() |
从该输入流读取一个字节的数据 |
int read(byte[] b) |
从该输入流读取最多 b.length个字节的数据为字节数组 |
void close() |
关闭此文件输入流并释放与流相关联的任何系统资源 |
备注
read()
读取数据时,其实有一个指向字节的指针,当读到最后时,返回-1
,缺点是一次只能读取一个字节,效率低下;read(byte[] b)
返回的是一次读取字节的长度,读到最后时同样返回-1
。需要注意的是每次调用该方法,如果参数是同一个数组,都是从b[0]
开始读入到b
中;close()
,便于释放有限的操作系统资源。读取文件中的全部字节
//假设目录下存在文件
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
FileOutputStream(File file)
创建文件输出流以写入由指定的 File对象表示的文件。
FileOutputStream(File file, boolean append)
创建文件输出流以写入由指定的 File对象表示的文件。
FileOutputStream(String name)
创建文件输出流以指定的名称写入文件。
FileOutputStream(String name, boolean append)
创建文件输出流以指定的名称写入文件。
备注
boolean append
决定了是否以追加的方式对文件进行写入。如果没有这个参数,相当于第一次创建输出流进行写入时,先对原来文件的内容进行清空再写入。FileOutputStream
对象时,如果对应的目录文件不存在,会自动创建。方法 | 解释 |
---|---|
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();
复制文件
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
的子类。
FileReader(File file)
创建一个新的 FileReader ,给出 File读取。
FileReader(String fileName)
创建一个新的 FileReader ,给定要读取的文件的名称。
方法 | 解释 |
---|---|
void close() |
关闭流并释放与之相关联的任何系统资源 |
String getEncoding() |
返回此流使用的字符编码的名称 |
int read() |
读一个字符 |
int read(char[] cbuf, int offset, int length) |
将字符读入数组的一部分 |
备注
方法的返回值可以参考FileInputStream
~
读取文件中的全部字节
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
的子类。
FileWriter(File file)
给一个File对象构造一个FileWriter对象。
FileWriter(File file, boolean append)
给一个File对象构造一个FileWriter对象。
FileWriter(String fileName)
构造一个给定文件名的FileWriter对象。
FileWriter(String fileName, boolean append)
构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据。
方法 | 解释 |
---|---|
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();
复制普通文本文件的关键代码
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
作用
从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取。
BufferedReader(Reader in)
创建使用默认大小的输入缓冲区的缓冲字符输入流。
BufferedReader(Reader in, int sz)
创建使用指定大小的输入缓冲区的缓冲字符输入流。
备注
Reader
是抽象类,无法被实例化,所以我们传入的时候为了方便,可以传入Reader
的子类对象,比如FileReader
类的对象。
方法 | 解释 |
---|---|
void close() |
关闭流并释放与之相关联的任何系统资源 |
String readLine() |
读一行文字 |
备注
close()
时,不仅会关闭该缓冲流(包装流),而且会关闭构造时作为参数的流(节点流);readLine()
不会读取每行的换行符,返回的是读取到的字符串,没有字符可读时,会返回NULL
。读取文件的全部字符
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
作用
将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入。
BufferedWriter(Writer out)
创建使用默认大小的输出缓冲区的缓冲字符输出流。
BufferedWriter(Writer out, int sz)
创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区。
备注
Writer
是抽象类,无法被实例化,所以我们传入的时候为了方便,可以传入Writer
的子类对象,比如FileWriter
类的对象。
方法 | 解释 |
---|---|
void close() |
关闭流并释放与之相关联的任何系统资源 |
void flush() |
刷新流,即清空缓冲区 |
void newLine() |
写入行分隔符 |
备注
close()
时,不仅会关闭该缓冲流(包装流),而且会关闭构造时作为参数的流(节点流);newLine()
向输出流中写入一个行结束标志,它不是简单地换行符 \n
或回车符\r
,而是系统定义的行隔离标志(line separator)
。以上两种缓冲流都是以字符为单位进行操作的,还有两种字节缓冲流—BufferedInputStream
和BufferedOutputStream
(可通过后缀进行区分),最好参照文件字节流进行学习,不会有太大差别,这里就不过多介绍~
作用
数据流除了可处理字节和字节数组外,还可以处理 int、float、boolean
等基本数据类型,也就是对二进制数据进行读写。
1.DataOutputStream
DataOutputStream(OutputStream out)
创建一个新的数据输出流,以将数据写入指定的底层输出流。
方法 | 解释 |
---|---|
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
DataInputStream(InputStream in)
创建使用指定的底层InputStream的DataInputStream。
方法 | 解释 |
---|---|
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
,能够方便地打印各种数据值的表示。
PrintStream(File file)
使用指定的文件创建一个新的打印流,而不需要自动换行。
PrintStream(OutputStream out)
创建一个新的打印流。
PrintStream(String fileName)
使用指定的文件名创建新的打印流,无需自动换行。
我们最开始学习Java运用的print()、printf()、println()
方法就源于这里,由于方法过多,这里就不展开叙述了,需要注意的是我们不需要手动关闭close()
方法。
修改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;
。ObjectOutputStream(OutputStream out)
创建一个写入指定的OutputStream的ObjectOutputStream。
方法 | 解释 |
---|---|
void writeObject(Object obj) |
将指定的对象写入ObjectOutputStream |
//创建一个商品类
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
接口的对象ObjectInputStream(InputStream in)
创建从指定的InputStream读取的ObjectInputStream。
方法 | 解释 |
---|---|
Object readObject() |
从ObjectInputStream 读取一个对象 |
//建立在上一个程序上的基础上,展示关键代码
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
,也就是默认值。 Properties类继承了Hashtable
,可以利用输入流获取文件的信息。这里的文件比较特殊,格式都是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了,但是当时只是看了书没记笔记,现在回想起来发现全给忘记了,思绪比较混乱。现在通过这篇博客记录学习过程,以后复习时直接拿来看就好,顺便还能给自己增加访问量,一举两得
(欢迎评论区交流~)