File、IO流

序列化

对象持久化

在 Java 程序中所创建的对象都保存在内存中,一旦 JVM 停止运行,这些对象都将会消失。因此以下两种情况必须通过序列化实现:

  1. 需要把对象持久化保存在文件中,在 JVM 重启后能够继续使用。
  2. 通过网络传送对象信息,在多个 JVM 间共享对象。

Serializable 接口

在类中声明实现 Serializable 接口,表示允许 Java 程序对这个类的对象序列化:JVM 会将对象的成员变量保存为一组字节,这些字节可以再被 JVM 组装成对象。对象序列化只保存的对象的成员变量,且不会关注类中的静态变量。

  1. transient 字段:默认序列化机制就会被忽略。
  2. private 字段:序列化后不会被保护,任何 JVM 都可读取。
  3. serialVersionUID 字段:申明序列化的版本号码,序列化的版本号与反序列化的版本号必须一致才不会出错!(private static final long serialVersionUID = 1;)
//person类的读入读出
//对于 class Person implements Serializable

        ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file));
        Person person = new Person("John", 101, Gender.MALE);
        oout.writeObject(person);             // 序列化
        oout.close();

        ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
        Object newPerson = oin.readObject();  // 反序列化 (没有强制转换到Person类型)
        oin.close();

File

// 创建对象
File f = new File("D:\\resources\\xueshan.jpeg");  // 创建File对象(指定了文件的路径)
File f = new File("D:" + File.separator+"resources"+ File.separator +"xueshan.jpeg"); // File.separator 分隔符创建文件对象
File f = new File("file-io-app/src/data.txt");    // 相对路径:一般定位模块中的文件的。 相对到工程下!!
File f = new File("D:\\resources");                // File创建对象 ,可以是文件也可以是文件夹

// 判断文件类型、获取文件信息功能
f.length()  // 文件大小(字节)

f.exists(); // 判断这个路径是否存在,这个文件夹存在否

f.getAbsolutePath(); // 获取绝对路径

f.getPath();   // 获取文件定义的时候使用的路径

f.getName();  //获取文件的名称:带后缀

f.lastModified();  // 获取文件的最后修改时间

f.isFile();    // 判断文件是文件还是文件夹

f.isDirectory();  // 判断文件是文件还是文件夹

// 创建文件
f.createNewFile();  // 创建新文件,创建成功返回true

f.mkdir(); // 创建一级目录

f.mkdirs();  // 支持多级创建

// 删除文件
f.delete();  // 删除文件或者空文件夹,占用一样可以删除

// 遍历文件夹
f.list();  // 获取当前目录下所有的"一级文件名称"到一个字符串数组

f.listFiles(); // 获取当前目录下所有的"一级文件对象"到一个文件对象数组

listFiles方法注意事项:
- 当调用者不存在时,返回null
- 当调用者是一个文件时,返回null
- 当调用者是一个空文件夹时,返回一个长度为0的数组
- 当调用者是一个有内容的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回
- 当调用者是一个有隐藏文件的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏内容
- 当调用者是一个需要权限才能进入的文件夹时,返回null 

递归实现文件搜索

package com.itheima.d2_recusion;

import java.io.File;
import java.io.IOException;

/**
    目标:去D判断搜索 eDiary.exe文件
 */
public class RecursionDemo05 {
    public static void main(String[] args) {
        // 2、传入目录 和  文件名称
        searchFile(new File("D:/") , "eDiary.exe");
    }

    /**
     * 1、搜索某个目录下的全部文件,找到我们想要的文件。
     * @param dir  被搜索的源目录
     * @param fileName 被搜索的文件名称
     */
    public static void searchFile(File dir,String fileName){
        // 3、判断dir是否是目录
        if(dir != null && dir.isDirectory()){
            // 可以找了
            // 4、提取当前目录下的一级文件对象
            File[] files = dir.listFiles(); // null  []
            // 5、判断是否存在一级文件对象,存在才可以遍历
            if(files != null && files.length > 0) {
                for (File file : files) {
                    // 6、判断当前遍历的一级文件对象是文件 还是 目录
                    if(file.isFile()){
                        // 7、是不是咱们要找的,是把其路径输出即可
                        if(file.getName().contains(fileName)){
                            System.out.println("找到了:" + file.getAbsolutePath());
                            // 启动它。
                            try {
                                Runtime r = Runtime.getRuntime();
                                r.exec(file.getAbsolutePath());
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }else {
                        // 8、是文件夹,需要继续递归寻找
                        searchFile(file, fileName);
                    }
                }
            }
        }else {
            System.out.println("对不起,当前搜索的位置不是文件夹!");
        }
    }
}

字符集

String编码

// 编码:把文字转换成字节(使用指定的编码)
String name = "abc我爱你中国";
// byte[] bytes = name.getBytes(); // 以当前代码默认字符集进行编码 (UTF-8)
byte[] bytes = name.getBytes("GBK"); // 指定编码
System.out.println(bytes.length);
System.out.println(Arrays.toString(bytes));

String解码

// 解码:把字节转换成对应的中文形式(编码前 和 编码后的字符集必须一致,否则乱码 )
// String rs = new String(bytes); // 默认的UTF-8
String rs = new String(bytes, "GBK"); // 指定GBK解码
System.out.println(rs);

IO

字节流

FileInputStream

// 创建一个文件字节输入流管道与源文件接通
InputStream is = new FileInputStream("file-io-app\\src\\data.txt");

int b1 = is.read();  // 读取一个字节返回  每次读取一个字节,性能较慢,读取中文字符输出无法避免乱码问题。

int b4 = is.read();  // 读取完毕返回-1

// 定义一个字节数组,用于读取字节数组
byte[] buffer = new byte[3];

int len = is.read(buffer);   // 返回读取的字节个数 每次读取一个字节数组,读取的性能得到了提升,读取中文字符输出无法避免乱码问题。

int len3 = is.read(buffer);  // 读取完毕返回-1

String rs = new String(buffer,0 ,len2);  // 将读出的字节数组转换为字符串(读取多少倒出多少) 


// 如何使用字节输入流读取中文内容输出不乱码呢?

// 方式一: 定义一个与文件一样大的字节数组,一次性读取完文件的全部字节。
File f = new File("file-io-app/src/data03.txt");
byte[] buffer = new byte[(int) f.length()];  // 定义一个字节数组与文件的大小刚刚一样大。
int len = is.read(buffer);
System.out.println(new String(buffer));

// 方式二: 官方为字节输入流InputStream提供了如下API可以直接把文件的全部数据读取到一个字节数组中
byte[] buffer = is.readAllBytes();  // 读取全部字节数组
System.out.println(new String(buffer));

FileOutputStream

OutputStream os = new FileOutputStream("file-io-app/src/out04.txt" , true);   // 追加数据管道
OutputStream os = new FileOutputStream("file-io-app/src/out04.txt");          // 先清空之前的数据,写新数据进入

os.write('a');               // 写一个字节

byte[] buffer = {'a' , 97, 98, 99};
byte[] buffer2 = "我是中国人".getBytes();

os.write(buffer);            // 写一个字节数组

os.write("\r\n".getBytes());// 换行

os.write(buffer, 0 , 3);    // 写一个字节数组的一部分

os.flush();                 // 写数据必须,刷新数据 可以继续使用流

os.close();                 // 释放资源,包含了刷新的!关闭后流不可以使用了

字符流

FileReader

// 创建一个字符输入流管道与源文件接通
Reader fr = new FileReader("file-io-app\\src\\data06.txt");  

int code = fr.read();             // 读取一个字符返回,没有可读的字符了返回-1,每次读取一个字符性能较慢
System.out.print((char)code);

// 2、用循环,每次读取一个字符数组的数据
char[] buffer = new char[1024]; // 1K字符
int len;
while ((len = fr.read(buffer)) != -1) {
    String rs = new String(buffer, 0, len);    // 每次读取一个字符数组,读取的性能得到了提升,读取中文字符输出不会乱码
    System.out.print(rs);
}

FileWriter

// 创建一个字符输出流管道与目标文件接通
Writer fw = new FileWriter("file-io-app/src/out08.txt"); // 覆盖管道,每次启动都会清空文件之前的数据
Writer fw = new FileWriter("file-io-app/src/out08.txt", true); // 追加管道

fw.write('a');                   // 写一个字符

fw.write("abc我是中国人");        // 写一个字符串

fw.write("abc我是中国人", 0, 5);  // 写字符串的一部分

har[] chars = "abc我是中国人".toCharArray();

fw.write(chars);                 // 写一个字符数

fw.write(chars, 3, 5);           // 写字符数组的一部分

fw.write("\r\n");                // 换行

fw.flush();                      // 刷新后流可以继续使用
 
fw.close();                      // 关闭包含刷线,关闭后流不能使用

缓冲流

BufferedInputStream/BufferedOutputStream


InputStream is = new FileInputStream("D:\\resources\\newmeinv.jpeg");        // 创建一个字节输入流管道与原视频接通

InputStream bis = new BufferedInputStream(is);                               // 把原始的字节输入流包装成高级的缓冲字节输入流

OutputStream os = new FileOutputStream("D:\\resources\\newmeinv222.jpeg");   // 创建一个字节输出流管道与目标文件接通

OutputStream bos = new BufferedOutputStream(os);                             // 把字节输出流管道包装成高级的缓冲字节输出流管道

BufferedReader/BufferedWriter

try (
  // 创建一个文件字符输入流与源文件接通。
  Reader fr = new FileReader("io-app2/src/data01.txt");

  // 把低级的字符输入流包装成高级的缓冲字符输入流。
  BufferedReader br = new BufferedReader(fr);
  ){

  String line;
  while ((line = br.readLine()) != null){  // 读取一行数据返回,如果读取没有完毕,无行可读返回null
      System.out.println(line);
    }
} catch (IOException e) {
  e.printStackTrace();
}
// 创建一个字符输出流管道与目标文件接通
Writer fw = new FileWriter("io-app2/src/out02.txt");         // 覆盖管道,每次启动都会清空文件之前的数据
//Writer fw = new FileWriter("io-app2/src/out02.txt", true); // 追加数据
BufferedWriter bw = new BufferedWriter(fw);                  // 包装成高级字符输出流, API与文件字符输出流FileWriter保持一致

bw.newLine();                                                // 换行(新增功能)                                          

转换流

InputStreamReader

// 字符输入转换流InputStreamReader作用:解决字符流读取不同编码乱码的问题,可以把原始的字节流按照指定编码转换成字符输入流。

InputStream is = new FileInputStream("D:\\resources\\data.txt");

// 把原始字节流转换成字符输入流
Reader isr = new InputStreamReader(is , "GBK"); // 以指定的GBK编码转换成字符输入流  完美的解决了乱码问题

OutputStreamWriter

// 字符输出转换流OutputStreamWriter 可以指定编码把字节输出流转换成字符输出流,从而可以指定写出去的字符编码!

OutputStream os = new FileOutputStream("io-app2/src/out03.txt");    // 定义一个字节输出流

// 把原始的字节输出流转换成字符输出流
Writer osw = new OutputStreamWriter(os , "GBK");     // 指定GBK的方式写字符出去

BufferedWriter bw = new BufferedWriter(osw);         // 把低级的字符输出流包装成高级的缓冲字符输出流。

bw.write("我爱中国~~");
bw.close();

打印流

PrintStream/PrintWriter

// 创建一个打印流对象
// PrintStream ps = new PrintStream(new FileOutputStream("io-app2/src/ps.txt"));
// PrintStream ps = new PrintStream(new FileOutputStream("io-app2/src/ps.txt" , true)); // 追加数据,在低级管道后面加True
// PrintStream ps = new PrintStream("io-app2/src/ps.txt" );

new PrintWriter(new File("io-app2/src/ps.txt"));
PrintWriter ps = new PrintWriter("io-app2/src/ps.txt"); // 打印功能上与PrintStream的使用没有区别

ps.println(97);
ps.println('a');
ps.println(23.3);
ps.println(true);
ps.println("我是打印流输出的,我是啥就打印啥");

ps.close();
// 改变输出语句的位置(重定向)
PrintStream ps = new PrintStream("io-app2/src/log.txt");
System.setOut(ps); // 把系统打印流改成我们自己的打印流

资源释放

try-catch-finally

public class TryCatchFinallyDemo1 {
    public static void main(String[] args) {
        InputStream is = null;
        OutputStream os = null;
        try {

            // System.out.println(10/ 0);

            // 1、创建一个字节输入流管道与原视频接通
             is = new FileInputStream("file-io-app/src/out04.txt");

            // 2、创建一个字节输出流管道与目标文件接通
             os = new FileOutputStream("file-io-app/src/out05.txt");

            // 3、定义一个字节数组转移数据
            byte[] buffer = new byte[1024];
            int len; // 记录每次读取的字节数。
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0 , len);
            }
            System.out.println("复制完成了!");

         //   System.out.println( 10 / 0);

        } catch (Exception e){
            e.printStackTrace();
        } finally {
            // 无论代码是正常结束,还是出现异常都要最后执行这里
            System.out.println("========finally=========");
            try {
                // 4、关闭流。
                if(os!=null)os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(is != null) is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        System.out.println(test(10, 2));
    }

    public static int test(int a , int b){
        try {
            int c = a / b;
            return c;
        }catch (Exception e){
            e.printStackTrace();
            return -111111; // 计算出现bug.
        }finally {
            System.out.println("--finally--");
            // 哪怕上面有return语句执行,也必须先执行完这里才可以!
            // 开发中不建议在这里加return ,如果加了,返回的永远是这里的数据了,这样会出问题!
            return 100;
        }
    }
}

try-with-resource

public class TryCatchResouceDemo2 {
    public static void main(String[] args) {

        try (
                // 这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源(即使出现异常也会做关闭操作)
                // 1、创建一个字节输入流管道与原视频接通
               InputStream is = new FileInputStream("file-io-app/src/out04.txt");
                // 2、创建一个字节输出流管道与目标文件接通
               OutputStream os = new FileOutputStream("file-io-app/src/out05.txt");

               // int age = 23; // 这里只能放资源(资源都是实现了Closeable/AutoCloseable接口的类对象)
                MyConnection connection = new MyConnection(); // 最终会自动调用资源的close方法
        )
        {
            // 3、定义一个字节数组转移数据
            byte[] buffer = new byte[1024];
            int len; // 记录每次读取的字节数。
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0 , len);
            }
            System.out.println("复制完成了!");

        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

class MyConnection implements AutoCloseable{
    @Override
    public void close() throws IOException {
        System.out.println("连接资源被成功释放了!");
    }
}

// JDK 9释放资源的方式
public class TryCatchResouceDemo3 {
    public static void main(String[] args) throws Exception {

        // 这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源(即使出现异常也会做关闭操作)
        // 1、创建一个字节输入流管道与原视频接通
        InputStream is = new FileInputStream("file-io-app/src/out04.txt");
        // 2、创建一个字节输出流管道与目标文件接通
        OutputStream os = new FileOutputStream("file-io-app/src/out05.txt");
        try ( is ; os ) {
            // 3、定义一个字节数组转移数据
            byte[] buffer = new byte[1024];
            int len; // 记录每次读取的字节数。
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0 , len);
            }
            System.out.println("复制完成了!");

        } catch (Exception e){
            e.printStackTrace();
        }
    }
}
posted @ 2023-03-19 15:39  晚点心动。  阅读(24)  评论(0)    收藏  举报