Java之IO流
File类:
File类的构造方法:
- File(File parent, String child)
通过父级路径file对象+子路径String创建file对象 - File(String pathname)
通过路径String创建file对象 - File(String parent, String child)
通过父级路径String+子路径String创建file对象 - File(URI uri)
通过file:uri创建file对象
uri:统一资源标识符,用来唯一标识某个资源
url:统一资源定位符,uri的一个子类,实现定位的uri
常用方法:
- boolean createNewFile()
当文件不存在时,创建该文件 - boolean mkdir()
创建目录,该方法在父级路径存在时才会创建成功 - boolean mkdirs()
创建目录,该方法在父级目录不存在时,也会创建父级目录 - boolean delete()
删除文件或目录 - boolean exists()
判断file是否存在 - boolean isAbsolute()
判断是否是绝对路径 - boolean isDirectory()
判断是否是目录 - boolean isFile()
判断是否是文件 - File getAbsoluteFile()
获得file的绝对路径,以File对象形式返回 - String getAbsolutePath()
获得file的绝对路径,以字符串形式返回 - String getName()
获得文件或目录名 - String getParent()
获得父级目录的路径 - File getParentFile()
获得父级目录的路径 - String getPath()
file路径转换为字符串形式 - String[] list()
以字符串形式返回file路径下的文件和目录 - File[] listFiles()
以file形式返回file路径下的文件和目录 - boolean renameTo(File dest)
重命名
遍历文件
/**
* 获得该目录下的所有文件及子文件
*
* @param path
* @return
*/
public static void getAllFile(String path) {
File root = new File(path);
if(!root.exists()){
return;
}
File[] files = root.listFiles();
for (File file : files){
if(file.isDirectory()){//如果是目录,则继续遍历
getAllFile(file.getAbsolutePath());
}else {
System.out.println(file.getAbsolutePath());
}
}
}
Java的IO架构
I即Input,外部程序向Java程序传递数据
O即Output,Java程序向外部程序传递数据
分类
- 按照传输方向:输入流和输出流
- 按照传输内容:字节流和字符流
字节流和字符流有啥区别呢?
来个单位换算:
1字符(char)= 2字节(byte)=16位(bit)
字节流可以处理任意类型的数据,当使用字节流读取中文文件时,每次读的可能不是一个完整的字符,这时候,程序就不认识那半个字符,所以就容易出现乱码。而字符流则是按照字符读取。但是当读取像图片、视频等等的文件,字符流就用不上了。
字节流
InputStream:字节输入流的父类接口
OutputStream:字节输出流的父类接口
常用方法
OutputStream:
- void write(byte[] b)
将数组写入到输出流 - void write(byte[] b, int off, int len)
将数组从off位置,写入len个长度到输出流 - void write(int b)
将指定字节写入输出流,b会强转成byte类型 - void close()
关闭流
InputStream:
- int read()
从输入流读取数据的下一个字节 - int read(byte[] b)
从输入流读取字节,并存储到b - int read(byte[] b, int off, int len)
从输入流off位置,读取len个字节,并存储到b - void close()
关闭流
对文件写入和读取
public static void main(String[] args) throws IOException {
InputStream is = null;
OutputStream os = null;
File file = new File("H://a.txt");
is = new FileInputStream(file);
os = new FileOutputStream(file, true);//追加模式,默认为覆盖
byte[] bytesWrite = "我是Java界的小学生".getBytes();
os.write(bytesWrite);
os.close();//用完就关闭
int len = 0;
byte[] bytesRead = new byte[1024];
while ((len = is.read(bytesRead)) != -1) {//读完返回-1
System.out.println(new String(bytesRead,0,len));
}
is.close();
}
字符流
Reader:字符输入流父类接口
Writer:字符输出流父类接口
常用方法
Writer
- Writer append(char c)
链式编程,将字符写入该字符输出流,并返回该输出流 - void write(int c)
将字符写入该字符输出流 - void write(char[] cbuf)
将字符数组写入该字符输出流 - void write(String str)
将字符串写入该字符输出流 - void write(String str, int off, int len)
将该字符串,从off开始,写len个长度到该字符输出流 - void flush()
刷新流,将缓冲区的字符写入文件,当close()时会调用,也可以通过该方法强制刷新
Reader
- int read()
读取一个字符 - int read(char[] cbuf)
将字符读入数组 - int read(char[] cbuf, int off, int len)
将字符从off读取len个长度到数组
使用字符流重写上面的程序
public static void main(String[] args) throws IOException {
Reader is = null;
Writer os = null;
File file = new File("H://a.txt");
is = new FileReader(file);
os = new FileWriter(file, true);//追加模式,默认为覆盖
String w = "我是Java界的小学生";
os.write(w);
os.flush();//不关闭时,必须刷新
os.close();//关闭自动刷新
int len = 0;
char[] r = new char[1024];
while ((len = is.read(r)) != -1) {//读完返回-1
System.out.println(new String(r,0,len));
}
is.close();
}
转换流
用于将字节流转换为字符流
InputStreamReader:将字节输入流转换为字符输入流
OutputStreamWriter:将字节输出流转换为字符输出流
将字节流转换为字符流后写入读取文件
public static void main(String[] args) throws IOException {
InputStream is = null;
OutputStream os = null;
File file = new File("H://a.txt");
is = new FileInputStream(file);
os = new FileOutputStream(file, true);//追加模式,默认为覆盖
//字节转字符
InputStreamReader inputStreamReader = new InputStreamReader(is, "utf-8");//指定字符集
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(os, "utf-8");
String w = "我是Java界的小学生";
outputStreamWriter.write(w);
outputStreamWriter.flush();//不关闭时,必须刷新
outputStreamWriter.close();//关闭自动刷新
int len = 0;
char[] r = new char[1024];
while ((len = inputStreamReader.read(r)) != -1) {//读完返回-1
System.out.println(new String(r,0,len));
}
is.close();
}
缓冲流
当对文件频繁操作时,性能和效率低。缓冲流就是将数据先缓存起来,然后一起写入或读取,可以提高文件操作的效率。
缓冲流分为字节缓冲流和字符缓冲流
字节缓冲流
public static void main(String[] args) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try{
bis = new BufferedInputStream(new FileInputStream("H://a.txt"));
bos = new BufferedOutputStream(new FileOutputStream("H://a.txt"));
bos.write("我是Java界的小学生".getBytes());
bos.flush();
byte[] c = new byte[1024];
int len = 0;
while ((len = bis.read(c))!= -1){
System.out.println(new String(c,0,len));
}
}catch (Exception e){
e.printStackTrace();
} finally {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
字符缓冲流
public static void main(String[] args) {
BufferedReader bis = null;
BufferedWriter bos = null;
try{
bis = new BufferedReader(new FileReader("H://a.txt"));
bos = new BufferedWriter(new FileWriter("H://a.txt"));
bos.write("我是Java界的小学生");
bos.flush();
char[] c = new char[1024];
int len = 0;
while ((len = bis.read(c))!= -1){
System.out.println(new String(c,0,len));
}
}catch (Exception e){
e.printStackTrace();
} finally {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
序列化
ObjectInputStream&ObjectOutputStream
使用DataOutputStream可以将对象存入文件
使用DataInputStream可以取出该对象
使用他们的前提是,该对象及其属性都实现了序列化
首先构建一个实现了序列化的类
class Stu implements Serializable{
String name;
String clazz;
int age;
@Override
public String toString() {
return "Stu{" +
"name='" + name + '\'' +
", clazz='" + clazz + '\'' +
", age=" + age +
'}';
}
}
然后将该对象存入文件
public static void main(String[] args) {
ObjectInputStream is = null;
ObjectOutputStream os = null;
FileOutputStream fileOutputStream = null;
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream("H://a.txt");
fileOutputStream = new FileOutputStream("H://a.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
os = new ObjectOutputStream(fileOutputStream);
is = new ObjectInputStream(fileInputStream);
Stu s = new Stu();
s.age = 18;
s.clazz = "1年级1班";
s.name = "荼蘼";//超级留级生
os.writeObject(s);
os.flush();
Stu o = (Stu) is.readObject();
System.out.println(o);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Stu{name='荼蘼', clazz='1年级1班', age=18}
容易产生的问题
java.io.EOFException
at java.io.ObjectInputStream$PeekInputStream.readFully(ObjectInputStream.java:2638)
at java.io.ObjectInputStream$BlockDataInputStream.readShort(ObjectInputStream.java:3113)
at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:853)
at java.io.ObjectInputStream.<init>(ObjectInputStream.java:349)
at com.dmlll.objects.Test.main(Test.java:19)
当新建一个文件存储ObjectOutputStream文件时,如果把ObjectInputStream放在ObjectOutputStream之前声明,则会报这个错误
读源码得知:
局部序列化
- transient
修改Stu
class Stu implements Serializable {
transient String name;
String clazz;
int age;
@Override
public String toString() {
return "Stu{" +
"name='" + name + '\'' +
", clazz='" + clazz + '\'' +
", age=" + age +
'}';
}
}
main方法不变
输出结果
Stu{name='null', clazz='1年级1班', age=18}
- static
修改Stu
class Stu implements Serializable {
static String name;
String clazz;
int age;
@Override
public String toString() {
return "Stu{" +
"name='" + name + '\'' +
", clazz='" + clazz + '\'' +
", age=" + age +
'}';
}
}
读取前将name设置为“小明”
Stu{name='小明', clazz='1年级1班', age=18}
在没有拿到序列化后的对象时,值可以被改变,说明name没有被序列化
3.实现Externalizable,重写writeExternal和readExternal
该类实际就是继承了Serializable接口,抽象出了这两个方法。
实现Serializable接口,重写这两个方法也可以
Java会调用ObjectOutputStream类检查Stu是否有私有的、无返回值的writeObject方法,如果有,Stu会委托该方法进行对象序列化。
修改Stu
class Stu implements Externalizable {
String name;
String clazz;
int age;
public Stu() {
}
@Override
public String toString() {
return "Stu{" +
"name='" + name + '\'' +
", clazz='" + clazz + '\'' +
", age=" + age +
'}';
}
public Stu(String name, String clazz, int age) {//必须要有构造器
this.name = name;
this.clazz = clazz;
this.age = age;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(clazz);
out.writeObject(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
clazz = (String) in.readObject();
age = (int)in.readObject();
}
}
结果
Stu{name='null', clazz='1年级1班', age=18}
异常处理中流的关闭
1.7可以这么关
public static void main(String[] args) {
try (FileOutputStream fileOutputStream = new FileOutputStream("H://a.txt");
FileInputStream fileInputStream = new FileInputStream("H://a.txt");
ObjectOutputStream os = new ObjectOutputStream(fileOutputStream);
ObjectInputStream is = new ObjectInputStream(fileInputStream)){
Stu s = new Stu();
s.age = 18;
s.clazz = "1年级1班";
s.name = "荼蘼";//超级留级生
os.writeObject(s);
os.flush();
s.name = "小明";
Stu o = (Stu) is.readObject();
System.out.println(o);
} catch (Exception e) {
e.printStackTrace();
}
}
但是如果有多个try块也挺麻烦的
java9可以这么搞
public static void main(String[] args) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("H://a.txt");
FileInputStream fileInputStream = new FileInputStream("H://a.txt");
ObjectOutputStream os = new ObjectOutputStream(fileOutputStream);
ObjectInputStream is = new ObjectInputStream(fileInputStream);
try (fileOutputStream;fileInputStream;os;is){
Stu s = new Stu();
s.age = 18;
s.clazz = "1年级1班";
s.name = "荼蘼";//超级留级生
os.writeObject(s);
os.flush();
s.name = "小明";
Stu o = (Stu) is.readObject();
System.out.println(o);
} catch (Exception e) {
e.printStackTrace();
}
}