关系图
分类
按用途划分(处理流 包装流):
缓冲流(Buffer)
数据流(DataInputStream)
打印流:PrintWriter 字符输出流,PrintStream 字节输出流
对象流:ObjectOutputStream
按方向:输入流 输出流
按数据单位:字节流byte 字符流char
RandomAccessFile随机读写流
随机读写流,是一个字节流,可以对文件进行任意位置读写
- 随机:可以定位到文件的任意位置进行读写操作,通过移动指针(Pointer)来实现
- 读写:使用该流既能读取文件,也能写入文件
try(
/*
* 当文件不存在时:
* 如果模式为r,会报异常FileNotFoundException
* 如果模式为rw,会自动创建文件
* 模式:r只读、rw读写
*/
RandomAccessFile raf=new RandomAccessFile("c:\\test\\w.txt", "rw");
){
System.out.println(raf.getFilePointer()); // 获取当前指针的位置,从0开始
raf.write("张三".getBytes()); //对于utf-8,一个汉字占3个字节
raf.write("hello".getBytes());
System.out.println(raf.getFilePointer()); // 11
System.out.println("写入成功");
raf.seek(8); // 将指针移动到指定的位置
raf.write("李四".getBytes());
System.out.println(raf.getFilePointer()); // 14
raf.seek(6);
byte[] buffer = new byte[2];
raf.read(buffer);
System.out.println(new String(buffer));
System.out.println(raf.getFilePointer()); // 8
raf.skipBytes(3); // 将指针向后跳过指定的字节,只能往前,不能倒退 ——>
buffer = new byte[1024*1024];
int num = -1;
while((num = raf.read(buffer)) != -1){
System.out.println(new String(buffer,0,num));
}
// 修改数据
raf.seek(8);
raf.write("赵".getBytes());
System.out.println("修改成功");
}catch(IOException e){
e.printStackTrace();
}
ObjectInputStream对象输入输出流
如果希望将Java对象写入到IO流中,或从IO流中读取Java对象,则要使用对象输入输出流,称为对象的序列化和反序列化
4.1 序列化和反序列化
序列化:将Java对象写入IO流中,实现将对象保存在磁盘上或在网络中传递对象
反序列化:从IO流中读取Java对象,实现从磁盘上或网络中恢复对象
要求:
对象必须实现Serializable接口,才能被序列化,转换为二进制流,通过网络进行传输
通过 serialVersionUID 判断对象的序列化版本的一致性:
在反序列时,会将流中的serialVersionUID与本地相应实体对象/类的serialVersionUID进行比较
如果相同就认为版本一致,则可以进行反序列化
如果不相同,则会出现序列化版本不一致的异常InvalidClassException
4.2 ObjectInputStream
对象输入流:用来读取对象,即反序列化
ObjectInputStream 和 ObjectOutputStream 属于包装流(用于对节点流进行功能扩展 / 包装)
在创建包装流,需要传入要操作的节点流对象
当关闭流时,只需要关闭包装流,被包装的节点流也会被关闭
4.3 ObjectOutputStream
对象输出流:用来写入对象,即序列化
User.java
public class User implements Serializable {
// static final long serialVersionUID
// 根据类名、接口名、成员方法和属性等生成的一个64位的哈希值
// 表示对象类型的的唯一标识
private static final long serialVersionUID = 705661196096434175L;
private Integer id; // 都使用包装类型,默认为null
private String name;
private transient Integer age; // transient修饰的属性不会被序列化(static变量也不会被序列化)
private Address address; // 对象属性也必须实现Serializable接口
// ...
}
Address.java
public class Address implements Serializable {
private String province;
private String city;
// ...
}
Test.java
public class Test01 {
public static void main(String[] args) {
test01();
test02();
}
// 序列化,写入对象
public static void test01() {
User u1 = new User(1001, "tom", 18);
u1.setAddress(new Address("江苏", "南京"));
User u2 = new User(1002, "jack", 21);
u1.setAddress(new Address("江苏", "扬州"));
List<User> users = Arrays.asList(u1, u2);
ObjectOutputStream oos = null;
try {
// FileOutputStream fos = new FileOutputStream("user.data");
// oos = new ObjectOutputStream(fos);
// 输出到user.data文件中,是二进制文件,文本编辑器打开不能正常显示
oos = new ObjectOutputStream(new FileOutputStream("user.data"));
// 写入对象
// oos.writeObject(u1);
// oos.writeObject(u2);
oos.writeObject(users);
oos.flush(); // 这里不flush也行,后面有close
System.out.println("写入对象成功");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (oos != null) {
try {
oos.close(); // 只需要关闭包装流
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 反序列化,读取对象
public static void test02() {
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("user.data"));
// 读取顺序和写入顺序一致
// User u1 = (User) ois.readObject();
// User u2 = (User) ois.readObject();
// System.out.println(u1);
// System.out.println(u2);
List<User> list = (List<User>) ois.readObject();
System.out.println(list);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
IO案例
递归获取指定目录下所有文件
public class Test01 {
public static void main(String[] args) throws URISyntaxException, IOException {
File file = new File("c:/idea_pro");
display(file);
}
public static void display(File file) {
if (!file.exists()) {
return;
}
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
if(f.isFile()){
System.out.println(f.getAbsolutePath());
}else{
display(f);
}
}
}else{
System.out.println(file.getAbsolutePath());
}
}
}
递归删除全部空目录
public class Test01 {
public static void main(String[] args) throws URISyntaxException, IOException {
File file = new File("d:/resource");
remove(file);
}
public static void remove(File file) {
// 判断是否为空目录
if (file.isDirectory() && file.listFiles().length == 0) {
file.delete();
System.out.println("删除空目录:" + file.getAbsolutePath());
// 删除当前目录后可能导致父目录也为空,所以需要对父目录进行处理
remove(file.getParentFile());
} else if (file.isDirectory()) {
// 如果当前目录不为空目录,则获取目录下所有文件进行再处理
File[] files = file.listFiles();
for (File f : files) {
remove(f); // 递归删除
}
}
}
}
文件的加密
实现文件的加密 a.txt ——> a.txt.sec
思路:读取文件中的每个字节,与pwd进行异或的操作
public class Test01 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.print("请输入文件路径:");
String filePath=input.nextLine();
System.out.print("请输入密码:");
int pwd = input.nextInt();
secret(filePath, pwd);
System.out.println("加密成功");
}
public static void secret(String filePath, int pwd){
try(
FileInputStream fis=new FileInputStream(filePath);
FileOutputStream fos = new FileOutputStream(filePath+".sec");
){
int data=-1;
while((data=fis.read())!=-1){
fos.write(data ^ pwd);
}
}catch(IOException e){
e.printStackTrace();
}
}
}
单个文件复制
public static void test01() throws IOException {
InputStream is = null;
OutputStream os = null;
try {
// 如果读取文件不存在,FileNotFoundException
is = new FileInputStream("c:\\test\\oss2.zip");
// 如果输出文件不存在,会自动创建文件,如果存在,会覆盖
os = new FileOutputStream("c:\\test\\oss.zip");
int data=-1;
// 每次read()方法默认读取一个字节 可设置读取单位为[1024*1024]
byte[] buffer = new byte[1024*1024];
while((data=is.read(buffer))!=-1){
System.out.println(data);
os.write(buffer,0, data);
}
System.out.println("文件复制成功");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
os.close();
is.close();
}
}
递归复制目录下所有文件到目标目录
public class Test01 {
public static void main(String[] args) {
copyFile("C:/software","c:/backup");
}
public static void copyFile(String srcPath, String destPath) {
File srcFile = new File(srcPath);
File destFile = new File(destPath);
if(srcFile.isDirectory()){
destFile=new File(destPath+File.separator+srcFile.getName());
destFile.mkdirs();
File[] files = srcFile.listFiles();
for (File file : files) {
copyFile(file.getAbsolutePath(), destFile.getAbsolutePath());
}
}else if(srcFile.isFile()){
try(
InputStream is=new FileInputStream(srcFile);
OutputStream os=new FileOutputStream(destPath+File.separator+srcFile.getName());
){
byte[] buffer=new byte[1024*1024];
int num=-1;
while((num=is.read(buffer))!=-1){
os.write(buffer, 0, num);
}
System.out.println("复制"+srcPath);
}catch(IOException e){
e.printStackTrace();
}
}
}
}
文件的拆分和合并
public class Test01 {
public static void main(String[] args) {
// 路径不包含bak的话报错,不知为何,总之不能在根目录下操作
splitFile("C:\\bak\\oss.zip");
mergeFile("C:\\bak\\oss.zip_1");
}
// 拆分文件:一个输入流,多个输出流
public static void splitFile(String filePath) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(filePath);
byte[] buffer = new byte[1024 * 1024 * 10]; // 每次读取10M
int num = -1;
int index = 0;
while ((num = fis.read(buffer)) != -1) {
fos = new FileOutputStream(filePath + "_" + (++index));
fos.write(buffer, 0, num);
fos.flush();
fos.close();
}
System.out.println("拆分成功,共拆分为:" + index + "个");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 合并文件:一个输出流,多个输入流
public static void mergeFile(String filePath) {
//filePath只需要一个文件名,通过截取即可
String basePath = filePath.substring(0, filePath.lastIndexOf("_"));
FileOutputStream fos = null;
FileInputStream fis = null;
try {
fos = new FileOutputStream(basePath);
int index = 1;
File f = null;
while ((f = new File(basePath + "_" + index++)).exists()) {
fis = new FileInputStream(f);
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fos.write(buffer);
fos.flush();
fis.close();
}
System.out.println("合并成功:"+basePath);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}