Java的IO流
IO流
主要内容
-
java.io.File类的使用:计算机操作系统中的文件和文件夹
-
IO原理及流的分类
文件流(数据流的读写都是基于文件的操作)FileInputStream/FileOutputStream/FileReader/FileWriter
缓存流(数据流的读写都是基于内存的操作)
BufferedInputStream/BufferedOutputStream/BufferedReader/BufferedWriter
通过程序把一个图放到某一个文件夹,把图片转化为一个数据集(例如二进制),把这些数据一点一点传到文件夹,这个传递的过程就很类似于水的流动,我们就可以称这个整体的数据集是一个数据流
-
转换流
InputStreamReader/OutputStreamWriter
-
标准输入/输出
-
打印流(了解)
PrintStream/PrintWriter
-
数据流(了解)
DataInputStream/DataOutputStream
-
对象流===涉及序列化、反序列化(把一个对象转化为一个数据流进行读写)
ObjectInputStream/ObjectOutputStream
-
随机存取文件流(RandomAccessFile)例如一个TXT文件,其中有100行数据,可以直 接读取第50行的数据,也可以在第89行插入数据
File类
java.io.File类:文件和目录路径名的抽象表示形式,与平台无关
File能新建、删除、重命名文件和目录,但File不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流
File对象可以作为参数传递给流的构造函数
File类的常见构造方法:
-
public File(String pathname)
以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储
-
public File(String parent,String child)
以parent为父路径,child为子路径创建File对象
File的静态属性String separator存储了当前系统的路径分隔符
在UNIX中,此字段为'/',在Windows中,为'\\'
-
import java.io.File;
import java.io.IOException;
public class Demo01 {
public static void main(String[] args) {
File f = new File("D:\\test\\abc\\tt.txt");//这个时候对象f就是tt.txt文件
File f1 = new File("D:\\test", "abc\\tt.txt");//这个时候对象f1也是tt.txt文件,用的少
File f2 = new File("D:/test/abc/tt.txt");//这个时候对象f2就是tt.txt文件
File f3 = new File("D:" + File.separator + "test/abc/tt.txt");//这个时候对象f3就是tt.txt文件
//注意,\在文件中是路径的分隔符,但是在java编程中一个\的意思是转义符,在java中\\或者/才是文件的分隔符
System.out.println(f2.getName());//获取文件名
System.out.println(f.getPath());//获取文件或者文件夹的路径
File f5 = new File("src/com/aggregate/io");//使用相对路径创建File对象
System.out.println(f5.getPath());//获取文件或者文件夹的路径,就是new file时候写的路径
System.out.println(f5.getAbsolutePath());//获取当前文件的绝对路径
System.out.println(f5);
System.out.println(f5.getAbsoluteFile());//返回一个用当前文件的路径构建的File对象
System.out.println(f5.getParent());//返回当前或者文件夹的父级路径
f.renameTo(new File("D:/test/abc/tt1.txt"));//给文件或者文件夹重命名
File f6 = new File("D:/test/abc/tt1.txt");
System.out.println(f6.exists());//判断文件或者文件夹是否存在
System.out.println(f6.canWrite());//判断文件是否可写
System.out.println(f6.canRead());//判断文件是否可读
System.out.println(f6.isFile());//判断当前的file对象是不是文件
System.out.println(f6.isDirectory());//判断当前的file对象是不是文件夹或者目录
System.out.println(f6.lastModified());//获取文件的最后修改时间,返回的是一个毫秒数
System.out.println(f6.length());//返回文件的长度,单位是字节数
File f7 = new File("D:/test/abc/tt2.txt");
System.out.println(f7.exists());//判断文件是否存在
if (!f7.exists()) {
try {
f7.createNewFile();//创建新的文件
} catch (IOException e) {
e.printStackTrace();
}
}
f7.delete();//删除文件
File f8 = new File("D:/test/abc/cc");
f8.mkdir();//创建单层目录,如果使用这样的方法来创建多层目录,就得一层一层的执行mkdir()
File f9 = new File("D:/test/abc/a/b/c");
f9.mkdirs();//这个方法是直接用来创建多层目录
File f10 = new File("D:/test");
String[] list = f10.list();//返回的是当前文件夹的子集的名称,包括目录和文件
for (String s : list) {
System.out.println(s);
}
File[] files = f10.listFiles();//返回的是当前文件夹的子集的file对象,包括目录和文件
for (File file : files) {
System.out.println(file);
}
File t = new File("D:/test");
new Demo01().test(t);
}
//遍历d盘下的test文件,把test文件夹下所有的目录与文件全部遍历出来,不论层级有多深,要全部遍历出来
//这个使用递归的方式来实现
/**
* 递归遍历文件
*
* @param file
*/
public void test(File file) {
if (file.isFile()) {
System.out.println(file.getAbsoluteFile() + "是文件");
} else {
System.out.println(file.getAbsoluteFile() + "是文件夹");
//如果是文件夹,这个文件夹里就可能有子文件或者文件
File[] files = file.listFiles();
if (files != null && files.length > 0) {
for (File file1 : files) {
test(file1);//递归
}
}
}
}
}
Java IO原理
IO流用来处理设备之间的数据传输
Java程序中,对于数据的输入/输出操作以“流(stream)”的方式进行
java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据
输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中
流的分类
按操作数据单位不同分为:字节流(8bit),字符流(16bit)
按数据流的流向不同分为:输入流、输出流
按流的角色的不同分为:节点流,处理流
Java的IO流共涉及40多个类,实际上非常规则,都是从如下4个抽象基类派生的。由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。
IO流体系
文件字节输入流
文件字节输出流
package com.aggregate.io;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class Demo02 {
public static void main(String[] args) {
Demo02.testFileInputStream();
Demo02.testFileOutputStream();
Demo02.copyFile("D:\\test\\abc\\tt1.txt","D:\\test\\abc\\a\\tt4.txt");
Demo02.copyFile("D:\\test\\abc\\img.png","D:\\test\\abc\\a\\img.png");
}
/**
* 文件字节输入流FileInputStream
*/
public static void testFileInputStream() {
try {
FileInputStream fileInputStream = new FileInputStream("D:\\test\\abc\\tt1.txt");
byte[] b = new byte[1024];//设置有关byte数组接收读取文件的内容
int len = 0;//设置一个读取数据的长度
//fileInputStream.read(b);//将txt文件中的内容读取到b中
//fileInputStream.read()方法有一个返回值,返回值是读取的数据的长度,如果读取到最后一个数据,还会向后读一个
//也就意味着当fileInputStream.read()的返回值是-1的时候整个文件就读取完毕了
while ((len = fileInputStream.read(b)) != -1) {
//参数1:缓存数据的数组;参数2:是从数组的哪个位置开始转化字符串;参数3:是总共转化几个字节
System.out.println(new String(b, 0, len));//将流byte转换为字符串输出
}
fileInputStream.close();//注意。流在使用完毕之后一定要关闭
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 文件字节输出流FileOutputStream
*/
public static void testFileOutputStream() {
try {
//指定向tt4输出数据
FileOutputStream fileOutputStream = new FileOutputStream("D:\\test\\abc\\tt4.txt");
String str = "advantages";
fileOutputStream.write(str.getBytes());//把数据写到内存中
fileOutputStream.flush();//把内存中的数据刷写到硬盘
fileOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 复制文件到指定位置
**/
public static void copyFile(String inPath, String outPath) {
try {
//读取的源文件
FileInputStream fileInputStream = new FileInputStream(inPath);
//复制到哪里
FileOutputStream fileOutputStream = new FileOutputStream(outPath);
byte[] b = new byte[1024];
int len = 0;
while ((len = fileInputStream.read(b)) != -1) {
fileOutputStream.write(b, 0, len);
}
fileOutputStream.flush();
fileOutputStream.close();
fileOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意
文件字节流非常通用,可以用来操作字符的文档,还可以操作任何的其他类型文件(图片,压缩包等等),引用字节流直接使用二进制
文件字符输入流
文件字符输出流
package com.aggregate.io;
import com.sun.org.apache.xalan.internal.xsltc.dom.StepIterator;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Demo03 {
public static void main(String[] args) {
Demo03.testFileReader("D:\\test\\abc\\tt1.txt");
Demo03.testFileWriter("hello world", "D:\\test\\abc\\tt5.txt");
Demo03.copyFile("D:\\test\\abc\\tt5.txt", "D:\\test\\abc\\cc\\tt5.txt");
}
/**
* 文件字符输入流FileReader
*/
public static void testFileReader(String inPath) {
try {
//创建文件字符输入流的对象
FileReader fileReader = new FileReader(inPath);
char[] c = new char[1024];//创建临时存数据的字符数组
int len = 0;
while ((len = fileReader.read(c)) != -1) {
System.out.println(new String(c, 0, len));
}
fileReader.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 文件字符输出流FileWriter
*/
public static void testFileWriter(String text, String outPath) {
try {
FileWriter fileWriter = new FileWriter(outPath);
fileWriter.write(text);//写到内存中
fileWriter.flush();//把内存的数据刷到硬盘
fileWriter.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 字符流完成拷贝文件
* 字符流自适合内容是字符的文件
*
* @param inPath
* @param outPath
*/
public static void copyFile(String inPath, String outPath) {
try {
FileReader fileReader = new FileReader(inPath);
FileWriter fileWriter = new FileWriter(outPath);
char[] c = new char[1024];
int len = 0;
while ((len = fileReader.read()) != -1) {//读取数据
fileWriter.write(c, 0, len);//写入数据到内存
}
fileWriter.flush();
fileWriter.close();
fileReader.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意
- 定义文件路径时,注意:可以用“/”或者“\”
- 在写入一个文件时,如果目录下有同名文件将被覆盖
- 在读取文件时,必须保证该文件已存在,否则出异常
处理流之一:缓冲流
为了提高数据读写的速度,Java API提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组
根据数据操作单位可以把缓冲流分为
BufferedInputStream和BufferedOutputStream
BufferedReader和BudderedWriter
缓冲流要“套接”在相应的节点流之上,对读写的数据提供了缓冲的功能,提高了读写的效率,同时增加了一些新的方法
对于输出的缓冲流,写出的数据会先在内存中缓冲,使用flush()将会使内存中的数据立刻写出
注意
缓冲流是把数据缓冲到内存中
package com.aggregate.io;
import java.io.*;
public class Demo05 {
public static void main(String[] args) {
try {
Demo05.testBufferedReader();
Demo05.testBufferedWriter();
Demo05.copyFile();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 缓冲字符输入流
*/
public static void testBufferedReader() throws Exception {
FileReader fileReader = new FileReader("F:\\code\\Java基础语法\\src\\com\\aggregate\\io\\tt.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);
char[] c = new char[1024];
int len = 0;
while ((len = bufferedReader.read(c)) != -1) {
System.out.println(new String(c, 0, len));
}
bufferedReader.close();
fileReader.close();
}
/**
* 缓冲字符输出流
*/
public static void testBufferedWriter() throws Exception {
FileWriter fileWriter = new FileWriter("F:\\code\\Java基础语法\\src\\com\\aggregate\\io\\tt3.txt");
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
String s = "hello world!!!!!";
bufferedWriter.write(s);
bufferedWriter.flush();
bufferedWriter.close();
fileWriter.close();
}
/**
* 缓冲字符流复制文件
*/
public static void copyFile() throws Exception {
BufferedReader bufferedReader = new BufferedReader(new FileReader("F:\\code\\Java基础语法\\src\\com\\aggregate\\io\\tt3.txt"));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("F:\\code\\Java基础语法\\src\\com\\aggregate\\io\\tt4.txt"));
char[] c = new char[1024];
int len = 0;
while ((len = bufferedReader.read(c)) != -1) {
bufferedWriter.write(c, 0, len);
}
bufferedWriter.flush();
bufferedWriter.close();
bufferedReader.close();
}
}
处理流之二:转换流
转换流提供了在字节流和字符流之间的转换
Java API提供了两个转换流:
InputStreamReader和OutputStreamWriter
字节流中的数据都是字符时,转成字符流操作更高效
InputStreamReader
用于将字节流中读取到的字节按指定字符集解码成字符。需要和InputStream"套接"
构造方法:
public InputStreamReader(InputStream in)
public InputStreamReader(InputStream in,String charsetName)
如:Reader isr = new InputStreamReader(System.in,"ISO5334_1");
package com.aggregate.io;
import java.io.*;
/**
* 转换流
* 可以把字节流转换为字符流
* 当字节流中的数据都是字符的时候,使用转换流转为字符流处理效率更高
*/
public class Demo06 {
public static void main(String[] args) {
//所有的文件都是有编码格式
//对于我们来说,txt和java文件一般来讲有三种编码
//ISO8859-1,西欧编码,是纯粹英文编码,不适应汉字
//GBK和UTF-8,这两编码是适用于中文和英文
//一般使用UTF-8编码
try {
Demo06.testInputStreamReader();
Demo06.testOutputStreamWriter();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 转换字节输入流为字符输入流
* 注意,在转换字符流的时候,设置的字符集编码要与读取的文件的数据的编码一致
* 不然就会出现乱码
*
* @throws FileNotFoundException
*/
public static void testInputStreamReader() throws Exception {
FileInputStream fileInputStream = new FileInputStream("F:\\code\\Java基础语法\\src\\com\\aggregate\\io\\tt5.txt");
//把字节流转换为字符流
//参数1是字节流,参数2是编码
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8");
char[] c = new char[1024];
int len = 0;
while ((len = inputStreamReader.read(c)) != -1) {
System.out.println(new String(c, 0, len));
}
inputStreamReader.close();
fileInputStream.close();
}
/**
* 转换字节输出流为字符输出流
*/
public static void testOutputStreamWriter() throws Exception {
FileOutputStream fileOutputStream = new FileOutputStream("F:\\code\\Java基础语法\\src\\com\\aggregate\\io\\tt6.txt");
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF-8");
outputStreamWriter.write("你好,世界!");
outputStreamWriter.flush();
outputStreamWriter.close();
outputStreamWriter.close();
}
}
处理流之三:标准输入输出流
System.in和System.out分别代表了系统标准的输入和输出设备
默认输入设备是键盘,输出设备是显示器
System.in的类型是InputStream
System.out的类型是PrintStream,其是OutputStream的子类FileOutputStream的子类
练习:把控制台输入的内容写到指定的TXT文件中,当接收到字符串over,就结束程序的运行。
处理流之四:打印流
在整个IO包中,打印流是输出信息最方便的类
PrintStream(字符打印流)和PrintWrite(字符打印流)提供了一系列重载的print和println方法,用于多种数据类型的输出
- PrintStream和PrintWriter的输出不会抛出异常
- PrintStream和PrintWriter有自动flush功能
- Syetem.out返回的是PrintStream的实例
处理流之五:数据流
为了方便地操作Java语言的基本数据类型的数据,可以使用数据流
数据流有两大类:(用于读取和写出基本数据类型的数据)
-
DataInputStream和DataOutputStream分别“套接”在InputStream和OutputStream节点流上
-
DataInputStream中的方法
boolean readBoolean()
char readChar()
double readBoolean()
long readLong()
String readUTF()
byte readByte()
float readFloat()
short readShort()
int readInt()
void readFully(byte[] b)
-
DataOutputStream中的方法
将上述的方法的read改为相应的write即可
处理流之六:对象流
ObjectInputStream和ObjectOutputStream
用于存储和读取对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来
序列化:用ObjectInputStream类将一个Java对象写入IO流中
反序列化:用ObjectInputStream类从IO流中恢复该Java对象
ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
例如:
person对象
-
把这个对象存到电脑的硬盘上,硬盘存储的基础是什么?是二进制
那就需要把对象转化为一个二进制的字节流,把这个流保存到电脑上。要使用这个对象,得把流转化为对象再使用
-
把这个对象通过网络传到另一台机器上,网络的通信基础是什么?是二进制
也就是需要把一个对象转化为二进制的数据流,把这个流通过网络进行传输,在接收者如果要使用接收的对象得先把对象的流转化为对象
正是因为保存对象到硬盘(对象的持久化)和对象的网络传输,需要做这两件事,就产生了对象的输入与输出流
序列化与反序列化都针对的是对象的各种属性,不包括类的属性
如果需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口:
- Serializable
- Externalizable
对象序列化
凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量private static final long serialVersionUID;
serialVersionUID用来表明类的不同版本间的兼容性
如果类没有显示定义这个静态变量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的源代码做了修改,serialVersionUID可能发生变化。故建议,显示声明
显示定义serialVersionUID的用途
- 希望类的不同版本对序列化兼容,因此需确保类的不同版本具有相同的serialVersionUID
- 不希望类的不同版本对序列化兼容,因此需确保类的不同版本具有不同的serialVersionUID
使用对象流序列化对象
若某个类实现了Seralizable接口,该类的对象就是可序列化的:
- 创建一个ObjectOutputStream
- 调用ObjectOutputStream对象的writeObject(对象)方法输出可序列化对象。注意写出一次,操作flush()
反序列化
- 创建一个ObjectInputStream
- 调用readObject()方法读取流中的对象
强调:如果某个类的字段不是基本数据类型或String类型,而是另一个引用类型,那么这个引用类型必须是可序列化的,否则拥有该类型的Field的类也不能序列化
package com.aggregate.io;
import java.io.Serializable;
/**
* 可以序列化与反序列化的对象
*/
public class Person implements Serializable {
private static final long IL = 0;
/**
* 一个表示序列化版本标识符的静态变量
* 用来表明类的不同版本间的兼容性
*/
private static final long serialVersionUID = IL;
String name;
int age;
}
package com.aggregate.io;
import java.io.*;
/**
* 序列化与反序列化
* 注意:对象的序列化与反序列化使用的类要严格一致,包名,类名,类结构等等所有都要一致
*/
public class TestPerson {
public static void main(String[] args) {
try {
TestPerson.testSerialize();
TestPerson.testDeserialize();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 对象的序列化
*
* @throws Exception
*/
public static void testSerialize() throws Exception {
//定义对象的输出流,把对象的序列化之后的流放到指定的文件中
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("F:\\code\\Java基础语法\\src\\com\\aggregate\\io\\tt9.txt"));
Person p = new Person();
p.name = "张三";
p.age = 11;
//将p序列化
out.writeObject(p);
out.flush();
out.close();
}
public static void testDeserialize() throws Exception {
//创建对象输入流对象,从指定的文件中把对象序列化后的流读取出来
ObjectInputStream in = new ObjectInputStream(new FileInputStream("F:\\code\\Java基础语法\\src\\com\\aggregate\\io\\tt9.txt"));
Object obj = in.readObject();
Person p = (Person) obj;
System.out.println(p.name);
System.out.println(p.age);
in.close();
}
}
RandomAccessFile类
RandomAccessFile类支持“随机访问”的方式,程序可以直接跳到文件的任意地方来读、写文件
- 支持只访问文件的部分内容
- 可以向已存在的文件后追加内容
RandomAccessFile对象包含一个记录指针,用以标示当前读写处的位置
RandomAccessFile类对象可以自由移动记录指针
- long getFilePointer():获取文件记录指针的当前位置
- void seet(long pos):将文件记录指针定位到pos位置
构造器
- public RandomAccessFile(File file,String mode)
- public RandomAccessFile(String name,String mode)
创建RandomAccessFile类实例需要指定一个mode参数,该参数指定RandomAccessFile的访问模式:
- r:以只读方式打开
- rw:打开以便读取和写入
- rwd:打开以便读取和写入;同步文件内容的更新
- rws:打开以便读取和写入;同步文件内容和元数据的更新
package com.aggregate.io;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
/**
* 文件的随机读写
* 程序可以直接跳到文件的任意地方来读、写文件
*/
public class Demo09 {
public static void main(String[] args) {
try {
// Demo09.testRandomAccessFileRead();
Demo09.testRandomAccessFileWrite();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 随机读文件
*
* @throws Exception
*/
public static void testRandomAccessFileRead() throws Exception {
// RandomAccessFile的构造有两个参数,
// 参数1是读写的文件的路径
// 参数2是指定RandomAccessFile的访问模式
// - r:以只读方式打开
// - rw:打开以便读取和写入
// - rwd:打开以便读取和写入;同步文件内容的更新
// - rws:打开以便读取和写入;同步文件内容和元数据的更新
//最常用的是r和rw
RandomAccessFile ra = new RandomAccessFile("F:\\code\\Java基础语法\\src\\com\\aggregate\\io\\tt10.txt", "r");
ra.seek(0);//设置读取文件内容的起始点,来达到从文件的任意位置读取
byte[] b = new byte[1024];
int len = 0;
while ((len = ra.read(b)) != -1) {
System.out.println(new String(b, 0, len));
}
ra.close();
}
/**
* 随机写文件
*/
public static void testRandomAccessFileWrite() throws Exception {
RandomAccessFile ra = new RandomAccessFile("F:\\code\\Java基础语法\\src\\com\\aggregate\\io\\tt10.txt", "rw");
// ra.seek(0);//设置写的起始点,0代表从开头写
//注意:如果是在文件开头或者中间的某个位置开始写的话,就会用写的内容覆盖掉等长度的原内容
ra.seek(ra.length());//设置写的起始点,ra.length()代表从文件的最后结尾写,也就是文件的追加
ra.write("你好,世界".getBytes());
ra.close();
}
}
流的基本应用小节
流是用来处理数据的
处理数据时,一定要先明确数据源,与数据目的地
数据源可以是文件,也可以是键盘
数据目的地可以是文件、显示器或者其他设备
而流只是在帮助数据进行传输,并对传输的数据进行处理,比如过滤处理、转换处理等
重点
-
字节流-缓冲流
输入流:InputStream-FileInputStream-BufferedInputStream
输出流:OutputStream-FileOutputStream-BufferedOutputStream
-
字符流-缓冲流
输入流:Reader-FileReader-BufferedReader
输出流:Writer-FileWriter-BufferedWriter
-
转换流
InputStreamReader和OutputStreamWriter
-
对象流
ObjectInputStream和ObjectOutputStream
序列化
反序列化
-
随机存取流
RandomAccessFile

浙公网安备 33010602011771号