在认识io流之前需要先认识FIle类,因为file类用来表示文件或文件夹的。
通过File类能对文件或文件夹进行新建、删除、重命名操作
public File(String pathname):
以pathname为路径创建File对象,可以是绝对路径或者相对路径
public File(String parent,String child)
以parent为父路径,child为子路径创建File对象。
public File(File parent,String child)
根据一个父File对象和子文件路径创建File对象
public boolean createNewFile()
创建文件。若文件存在,则不创建,返回false
public boolean mkdir() :
创建文件目录。如果此文件目录存在,就不创建了。
如果此文件目录的上层目录不存在,也不创建。
public boolean mkdirs() :
创建文件目录。如果上层文件目录不存在,一并创建
而IO流则是对文件或文件夹进行读写操作的。
public boolean delete():
删除文件或者文件夹
public String getAbsolutePath():获取绝对路径
public String getPath() :获取路径
public String getName() :获取名称
public String getParent():获取上层文件目录路径。若无,返回null
public long length() :获取文件长度(即:字节数)。不能获取目录的长度。
public long lastModified() :获取最后一次的修改时间,毫秒值
public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组
public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组
public boolean isDirectory():判断是否是文件目录
public boolean isFile() :判断是否是文件
public boolean exists() :判断是否存在
public boolean canRead() :判断是否可读
public boolean canWrite() :判断是否可写
public boolean isHidden() :判断是否隐藏
根据流向分:
输入流和输出流
根据数据单位分:
字节流和字符流
一个字符等于两个字节
根据角色分:
节点流和处理流
如果操作的对象是文件,则该流是节点流;
如果操作的对象是流,则该流是处理流
抽象基类
InputStream
OutputStream
Reader
Writer
节点流:
FileInputStream
FileOuptStream
FileReader
FileWriter
缓存流:
BufferedInputStream
BufferedOutputStream
BufferedReader
BufferedWriter
字符输入流
从文件中的数据写入到
可以通过以下两种方式读取,两种方式都是读取到最后一个字符为-1时,则到达文件末尾
while ((read=fileReader.read()) != -1) {
System.out.print((char) read);
}
@Test
public void test1() {
File file = new File("src/file/hello.txt");
FileReader fileReader = null;
try {
fileReader = new FileReader(file);
System.out.println(file.getAbsolutePath());
int read;
while ((read=fileReader.read()) != -1) {
System.out.print((char) read);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
char[] chars=new char[5];
int len;
while ((len=fileReader.read(chars))!=-1){
for (int i=0;i<len;i++){
System.out.print(chars[i]);
}
System.out.println();
}
@Test
public void test2(){
File File2=new File("src/file/hello.txt");
FileReader fileReader = null;
try {
fileReader=new FileReader(File2);
char[] chars=new char[5];
int len;
while ((len=fileReader.read(chars))!=-1){
for (int i=0;i<len;i++){
System.out.print(chars[i]);
}
System.out.println();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (fileReader!=null){
fileReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
字符输出流
将字符从内存中写到文件(硬盘)
1、如果文件不存在,则进行创建
2、如果文件存在:
使用FIleWriter(file,true),则将字符追加到文件中
使用FileWriter(file,false)或FileWriter(file),则写入的内容覆盖原有的内容
fileWriter=new FileWriter(file2,true);
char[] chars=new char[10];
int len;
while ((len=fileReader.read(chars))!=-1){
fileWriter.write(chars);
}
字节输入流,可以将字节数据读取到内存中
使用该流可以读取图片,视频,音频等
File file1=new File("E:\\temp\\aof1.png");
fileInputStream=new FileInputStream(file1);
byte[] bytes=new byte[10];
int len;
while ((len=fileInputStream.read(bytes))!=-1){
System.out.println(new String(bytes,0,len));
}
int read()
从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值。如果因
为已经到达流末尾而没有可用的字节,则返回值 -1。
int read(byte[] b)
从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。如果因为已
经到达流末尾而没有可用的字节,则返回值 -1。否则以整数形式返回实际读取
的字节数。
int read(byte[] b, int off,int len)
将输入流中最多 len 个数据字节读入 byte 数组。尝试读取 len 个字节,但读取
的字节也可能小于该值。以整数形式返回实际读取的字节数。如果因为流位于
文件末尾而没有可用的字节,则返回值 -1。
File write=new File("src/file/hello.gif");
FileOutputStream fileOutputStream=new FileOutputStream(write,true);
byte[] bytes=new byte[10];
int len;
while ((len=fileInputStream.read(bytes))!=-1){
//System.out.println(new String(bytes,0,len));
fileOutputStream.write(bytes);
}
public void flush()throws IOException:
刷新此输出流并强制写出所有缓冲的输出字节,调用此方法指示应将这些字节立
即写入它们预期的目标。
void write(int b)
将指定的字节写入此输出流。write 的常规协定是:向输出流写入一个字节。要写
入的字节是参数 b 的八个低位。b 的 24 个高位将被忽略。 即写入0~255范围的。
void write(byte[] b)
将 b.length 个字节从指定的 byte 数组写入此输出流。write(b) 的常规协定是:应该
与调用 write(b, 0, b.length) 的效果完全相同。
void write(byte[] b,int off,int len)
将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流
缓冲流要“套接”在相应的节点流之上,根据数据操作单位可以把缓冲流分为:
BufferedInputStream 和 BufferedOutputStream
BufferedReader 和 BufferedWriter
当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区
读文件
当使用BufferedInputStream读取字节文件时,BufferedInputStream会一次性从
文件中读取8192个(8Kb),存在缓冲区中,直到缓冲区装满了,才重新从文件中
读取下一个8192个字节数组。
写文件
向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满,
BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法
flush()可以强制将缓冲区的内容全部写入输出流
关闭流
关闭流的顺序和打开流的顺序相反。只要关闭最外层流即可,关闭最外层流也
会相应关闭内层节点流
举个例子:
FileReader fileReader = null;
FileWriter fileWriter = null;
BufferedReader bufferedReader = null;
BufferedWriter bufferedWriter = null;
fileReader = new FileReader(source);
fileWriter = new FileWriter(target);
bufferedReader = new BufferedReader(fileReader);
bufferedWriter = new BufferedWriter(fileWriter);
String s = null;
while ((s = bufferedReader.readLine()) != null) {
bufferedWriter.write(s);
}
转换流提供了在字节流和字符流之间的转换
Java API提供了两个转换流:
InputStreamReader:将InputStream转换为Reader
OutputStreamWriter:将Writer转换为OutputStream
将字节输入流转换为字符输入流
将字节输出流转为字符输出流
编码:将字符转为字节(看得懂的转成看不懂的)
解码:将字节转为字符(看不懂的转成看得懂的)
转换流复制文件
@Test
public void test1() {
File read = null;
File write = null;
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
OutputStreamWriter outputStreamWriter=null;
InputStreamReader inputStreamReader=null;
try {
read = new File("hell01.txt");
write = new File("temp.txt");
fileInputStream = new FileInputStream(read);
fileOutputStream = new FileOutputStream(write);
inputStreamReader=new InputStreamReader(fileInputStream,"utf-8");
outputStreamWriter=new OutputStreamWriter(fileOutputStream,"utf-8");
char[] chars=new char[10];
int len = 0;
while ((len = inputStreamReader.read(chars)) != -1) {
System.out.println();
outputStreamWriter.write(String.valueOf(chars));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStreamReader != null) {
try {
inputStreamReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outputStreamWriter != null) {
try {
outputStreamWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
ObjectInputStream和OjbectOutputSteam
用于存储和读取基本数据类型数据或对象的处理流。
序列化:ObjectOutputStrem保存基本数据类型或对象
反序列化:ObjectInputStream读取基本数据类型或对象
注意:
ObjectOutputStream和ObjectInputStream不能序列化 static和transient修
饰的成员变量
特点:
1、可以将基本数据类型或java对象转换成二进制流;
2、任何实现serializeable接口的对象转换成的字节数据都可以在保存和传输时被还原
3、如果要进行网络传输,就必须进行序列化
4、如果要让对象支持序列化就必须实现Serializable或Externalizable接口
5、凡是实现Serializable接口的类 必须 有一个表示序列化标识符的静态变量
private static final long serialVersionUID
6、除了当前Person类需要实现Serializable接口之外,还必须保证其内部所有属性也必须是可序列化的。(默认情况下,基本数据类型可序列化)
为什么凡是实现Serializable接口的类 必须 有一个表示序列化标识符的静态变量?
如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自
动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。
举个例子:(为了让代码看起来少一点,这里没有在finally中关闭资源)
演示不加入serialVersionUID
public class Person implements Serializable {
// public static final long serialVersionUID=1L;
private String name;
private String sex;
public Person(String name, String sex) {
this.name = name;
this.sex = sex;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
@Test
public void test1(){
serializableString(); // 序列化
unSerializableString(); // 反序列化
}
private void serializableString() {
try {
ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream("data.txt"));
Person person=new Person("张三","男");
objectOutputStream.writeObject(person);
objectOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void unSerializableString() {
try {
ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream("data.txt"));
Person person= (Person) objectInputStream.readObject();
System.out.println(person.toString());
objectInputStream.close();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
在序列化之后没有对类进行更改,发现反序列化之后可以恢复
如果在·序列化之后,修改了类
public class Person implements Serializable {
// public static final long serialVersionUID=1L;
private String name;
private String sex;
pricate int age;
public Person(String name, String sex) {
this.name = name;
this.sex = sex;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
执行反序列化操作
@Test
public void test1(){
unSerializableString(); // 反序列化
}
这是因为通过serialVersionUID可以识别出是哪一个类,即使改变了也可以反序列化处理,但是 如果没有serialVersionUID,就不知道是哪个类,所有就无法反序列化。