04- IO流

IO流基础

硬盘 内存

一台电脑启动过程

把硬盘中的操作系统 读取到内存中

关机 把内存中的东西清空 , 保存到硬盘中

把电脑的内存当成 程序执行的位置

硬盘中的文件读取到内存中 为输入流

内存中的文件写出到硬盘中 为输出流

File

文件或目录路径名的抽象表示

常用属性

separator

// 与系统相关的默认名称 - 分隔符字符,以方便的方式表示为字符串
windows  "\"  
linux "/" 
在Windows 中 可以使用  / 代替 \

pathSeparator

回顾 配置java的 环境变量 , 多个路径之间使用 ; 分隔
windows 为 分号
linux 为 冒号

构造方法

  • new File(文件或目录的路径);
  • new File(文件或目录的父级路径(字符串), 文件或目录的子级路径);
  • new File(文件或目录的父级路径(文件对象), 文件或目录的子级路径);

绝对路径和相对路径

绝对路径

1- 盘符 D:/a.txt
2- 以 "/" 开头 表示当前项目所在的磁盘的根目录

相对路径

直接写文件名或目录名 相当于表示文件本身所在的项目位置

常用方法

查看

getAbsolutePath()  返回此抽象路径名的绝对形式。
getPath()  将此抽象路径名转换为路径名字符串。
getName()  返回由此抽象路径名表示的文件或目录的名称。
getParent()  返回此抽象路径名的父 null的路径名字符串,
		如果此路径名未命名为父目录,则返回null。
length()  返回由此抽象路径名表示的文件的长度。
File f1 = new File("D:\\a.txt");
System.out.println(f1.getPath());  // D:\a.txt
System.out.println(f1.getAbsolutePath());  //D:\a.txt
// 使用相对路径
File f2 = new File("a.txt");
System.out.println(f2.getPath());  // a.txt
System.out.println(f2.getAbsolutePath());  //D:\IDEA\hello_plus\a.txt

判断

exists()  测试此抽象路径名表示的文件或目录是否存在。 
isAbsolute()  测试这个抽象路径名是否是绝对的。
isDirectory()  测试此抽象路径名表示的文件是否为目录。 
isFile()  测试此抽象路径名表示的文件是否为普通文件。 

新增删除

mkdir() 创建由此抽象路径名命名的目录。
mkdirs() 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录。
createNewFile() 
当且仅当具有该名称的文件尚不存在时,原子地创建一个由该抽象路径名命名的新的空文件。 
delete() 
删除由此抽象路径名表示的文件或目录。 

获取子级目录

String[] list() 
返回一个字符串数组,命名由此抽象路径名表示的目录中的文件和目录。 
File[] listFiles() 
返回一个抽象路径名数组,表示由该抽象路径名表示的目录中的文件。 
File dir = new File("D:\\601- 壁纸");
String[] list = dir.list();
System.out.println(Arrays.toString(list));
File[] files = dir.listFiles();
for(File file : files ){
    System.out.println(file.getName());
}

遍历文件夹的方法(包括子文件夹)

// 遍历文件夹中的所有子文件
public static void showDir(File dir){
    //如果 该文件是一个文件夹, 才可以有子文件夹
    if(dir.isDirectory()){
        // 获取子目录
        File[] files = dir.listFiles();
        for(File file : files){
            showDir(file);
        }
    }else{
        System.out.println(dir.getName());
    }
}

递归

在方法中调用 方法本身

计算 n的阶乘

public static int jc(int n){  // 5
    int jc = 1;
    for(int i = 1; i <= n ; i++){
        jc *= i;
    }
    return jc;
}
// 第二种方案
// jc(n) 就是 n的阶乘
// jc(5) = 5 * jc(4)
// 3! = 3 * 2 * 1
// 4! = 4 * 3!
public static int jc(int n){
    if(n == 1) {return 1;}
    return n * jc(n-1);
}

递归练习题

/* 
	一球从100米高度自由落下,每次落地后反跳回原高度的一半;再落下,求它在 第10次落地时,共经过多少米?第10次反弹多高?
*/ 
public class Demo6 {
    public static void main(String[] args) {
        System.out.println(ft(10));
        // 100 + 每一次弹起的高度 * 2
        int sum = 100;
        for(int i = 1 ; i < 2;i++){
            sum += ft(i)*2;
        }

    }
    // 小球第n 次反弹高度为 多少
    public static double ft(int n){
        if(n == 0){return 100;}
        return ft(n-1)/2;
    }
}
把一个数组里的数进行组合全部列出,比如1和2列出来为1,2,12,21
水仙花数在数学上的定义是:指一个三位数,如果它的各位数字的立方和等于其本身,则这个三位数为水仙花数, 计算1000 之内的所有水仙花数

IO流

流是一组有序的数据序列
以先进先出方式发送信息的通道

输入入流和输出流 站在程序的角度上

从数据源 到程序 输入流

从程序到数据源 输出流

分类

流的方向

  • 输入流 InputStream 和 Reader
  • 输出流 OutputStream 和 Writer

按照处理数据单元划分

  • 字节流 ( 8位通用字节流)
    • 字节输入流 InputStream
    • 字节输出流 OutputStream
  • 字符流 (16位unicode字符流)
    • 字符输入流 Reader
    • 字符输出流 Writer

字节流输入流 InputStream

这个抽象类是表示输入字节流的所有类的超类。
public abstract class InputStream{}
public class FileInputStream extends InputStream{}

FileInputStream

构造方法

FileInputStream(File file) 
通过打开与实际文件的连接创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。   
FileInputStream(String name) 
通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。  

方法

int read() 
从该输入流读取一个字节的数据。  
int read(byte[] b) 
从该输入流读取最多 b.length个字节的数据为字节数组。  
int read(byte[] b, int off, int len) 
从该输入流读取最多 len字节的数据为字节数组。  
close() 
关闭此文件输入流并释放与流相关联的任何系统资源。 
  • read() 每次读取一个字节, 读取完成之后将指针向后移动一位

当我们在查看某个电话本的时候, 将手指当做一个 防止看错行的工具

看完一行, 手指向下一行移动

  • 当指针指向数据的时, 返回数据对应的字节值

数据读取完毕, 没有数据指向, 返回 -1

案例

/*
使用程序 读取D:\\a.txt 文件中的内容
1- 创建该文件的对象
2- 使用流 建立起与文件之间的通道
3- 读取文件
4- 关闭通道
 */

原始写法

File file = new File("D:\\a.txt");
FileInputStream fis = new FileInputStream(file);
// InputStream 字节输入流 , read() 每次读取一个字节
  int i = 0;
  // 这种写法 会出现多打印一次 -1 的情况
  while(i != -1){
      i  = fis.read();  // 没有数据 返回 -1
      System.out.println(i);
  }
  fis.close();

优化

FileInputStream fis = new FileInputStream("D:\\a.txt");
int i ;
while( (i = fis.read()) != -1){
    System.out.print((char)i);
}
fis.close();

使用数组优化

// fis.read() // 每次读取一个字节, 效率非常低, 想要一次读取多个字节
FileInputStream fis = new FileInputStream("D:\\a.txt");
// 使用工具
byte [] bys = new byte[1024];
int len;  // 数组中真实数据的长度
while( (len = fis.read(bys)) != -1){
    for(int i = 0 ; i< len; i++){
        System.out.print((char)bys[i]);
    }
}

字节输出流

构造方法

FileOutputStream(String name) 
创建文件输出流以指定的名称写入文件。  
FileOutputStream(String name, boolean append) 
创建文件输出流以指定的名称写入文件。  
FileOutputStream(File file) 
创建文件输出流以写入由指定的 File对象表示的文件。  
FileOutputStream(File file, boolean append) 
创建文件输出流以写入由指定的 File对象表示的文件。 

注意点

1.两种一个参数的构造方法在向文件写数据时将覆盖文件中原有的内容
	带有boolean值得参数, true 表示 不会覆盖, 在原有内容的基础之上添加
2.创建FileOutputStream实例时,如果相应的文件并不存在,则会自动创建一个空的文件

方法

void write(byte[] b) 
将 b.length个字节从指定的字节数组写入此文件输出流。  
void write(byte[] b, int off, int len) 
将 len字节从位于偏移量 off的指定字节数组写入此文件输出流。  
void write(int b) 
将指定的字节写入此文件输出流。 
void close() 
关闭此文件输出流并释放与此流相关联的任何系统资源。  
public void flush()throws IOException
刷新此输出流并强制任何缓冲的输出字节被写出。

案例

// 把程序中的一个字符串 输出到 硬盘中
// 1- 建立程序到硬盘文件的通道
// 2- 调用方法 写出数据
// 3- 关闭资源
String s = "Hello outputStream";
FileOutputStream fos = new FileOutputStream("D:\\out.txt");
// 把该字符串输出到硬盘
// write 一次只能输出一个字节
byte[] bytes = s.getBytes();
// 遍历数组并输出
for(byte by : bytes){
    fos.write(by);
}
fos.close();

程序优化

String s = "Hello outputStream";
FileOutputStream fos = new FileOutputStream("D:\\out.txt");
// 把该字符串输出到硬盘
// write 一次只能输出一个字节
byte[] bytes = s.getBytes();
fos.write(bytes);
fos.close();
String s = "Hello outputStream";
FileOutputStream fos = new FileOutputStream("D:\\out.txt");
// 把该字符串输出到硬盘
// write 一次只能输出一个字节
byte[] bytes = s.getBytes();
fos.write(bytes,0,5);
fos.close();

综合案例

文件复制

// 将D:\\a.txt 文件 复制到 C:\\a.txt 中

初级版本

// 把 source 源文件 复制到 target 目标文件
public static void copy(String source , String target) throws IOException {
    // 1- 建立输入和输出流
    // 2- 通过输入流把文件内容读取到 程序
    // 3- 通过程序写出到目标文件
    // 4- 关闭资源
    FileInputStream fis = new FileInputStream(source);
    FileOutputStream fos = new FileOutputStream(target);
    // 使用单个字节的方式读取
    int i;
    while( (i = fis.read()) != -1){
        // i 是字节输入流 读取到的 字节
        fos.write(i);
    }
    fos.close();
    fis.close();
}

查看该程序的运行时间

// 把 source 源文件 复制到 target 目标文件
public static void copy1(String source , String target) throws IOException {
    FileInputStream fis = new FileInputStream(source);
    FileOutputStream fos = new FileOutputStream(target);
    // 使用单个字节的方式读取
    int i;
    long oldTime = System.currentTimeMillis();
    while( (i = fis.read()) != -1){
        // i 是字节输入流 读取到的 字节
        fos.write(i);
    }
    long newTime = System.currentTimeMillis();
    System.out.println("该程序运行所用时间为: "+(newTime-oldTime)+"毫秒");
    fos.close();
    fis.close();
}
// 一个5.64M的图片 所花时间为 46454毫秒

使用数组优化程序

// 把 source 源文件 复制到 target 目标文件
public static void copy2(String source , String target) throws IOException {
    FileInputStream fis = new FileInputStream(source);
    FileOutputStream fos = new FileOutputStream(target);
    // 使用单个字节的方式读取
    // fis.read()  返回值 是 输入流读取的 一个字节
    // fis.read(数组) 返回值 是输入流读取到的字节数组的长度
    byte [] bys = new byte[1024];
    int len;
    long oldTime = System.currentTimeMillis();
    while( (len = fis.read(bys)) != -1){
        // i 是字节输入流 读取到的 字节
        fos.write(bys,0,len);
    }
    long newTime = System.currentTimeMillis();
    System.out.println("该程序运行所用时间为: "+(newTime-oldTime)+"毫秒");
    fos.close();
    fis.close();
}

内部处理异常

public static void copy2(String source , String target){
    FileInputStream fis = null;
    FileOutputStream fos = null;
    try{
       fis = new FileInputStream(source);
       fos = new FileOutputStream(target);
        // 使用单个字节的方式读取
        // fis.read()  返回值 是 输入流读取的 一个字节
        // fis.read(数组) 返回值 是输入流读取到的字节数组的长度
        byte [] bys = new byte[1024];
        int len;
        long oldTime = System.currentTimeMillis();
        while( (len = fis.read(bys)) != -1){
            // i 是字节输入流 读取到的 字节
            fos.write(bys,0,len);
        }
        long newTime = System.currentTimeMillis();
        System.out.println("该程序运行所用时间为: "+(newTime-oldTime)+"毫秒");
    }catch(Exception e){
        System.out.println(e.getMessage());
    }finally{
        if(fos != null){
            try {
                fos.close();// 垃圾回收机制
            } catch (IOException e) {
                fos = null;
            }
        }
        if(fis != null){
            try {
                fis.close();
            } catch (IOException e) {
                fis = null;
            }
        }
    }
}

多文件复制案例

可变参数

// 可变参数优化
public static void add(int ...arr){
    int sum = 0;
    for(int i : arr){
        sum += i;
    }
    System.out.println(sum);
}

多文件复制

public static void copy3(String targetDir, String ...list){
    FileInputStream fis = null;
    FileOutputStream fos = null;
    for(String sfile : list){
        try{
            // 根据集合中所保存的 文件的路径创建文件对象
            File source = new File(sfile);
            String fileName = source.getName();
            File tarDir = new File(targetDir);
            if(!tarDir.exists()){
                tarDir.mkdirs();
            }
            File target = new File(tarDir,fileName);
            fis = new FileInputStream(source);
            fos = new FileOutputStream(target);
            // 使用单个字节的方式读取
            // fis.read()  返回值 是 输入流读取的 一个字节
            // fis.read(数组) 返回值 是输入流读取到的字节数组的长度
            byte [] bys = new byte[1024];
            int len;
            long oldTime = System.currentTimeMillis();
            while( (len = fis.read(bys)) != -1){
                // i 是字节输入流 读取到的 字节
                fos.write(bys,0,len);
            }
            long newTime = System.currentTimeMillis();
            System.out.println("该程序运行所用时间为: "+(newTime-oldTime)+"毫秒");
        }catch(Exception e){
            System.out.println(e.getMessage());
        }finally{
            if(fos != null){
                try {
                    fos.close();// 垃圾回收机制
                } catch (IOException e) {
                    fos = null;
                }
            }
            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    fis = null;
                }
            }
        }
    }
}

使用字节流读取汉字

字符编码

  • ASCII 128 个字节值 表示 控制字符和可打印字符
  • ISO-8859-1 西欧编码
  • GB2312 / GBK / GB18030 中文编码字符集
  • UTF-8 支持在全球范围内使用
public static void hz() throws IOException {
    FileInputStream fis = new FileInputStream("D:\\a.txt");
    byte [] bys = new byte[100];
    int len ;
    while( (len = fis.read(bys)) != -1){
        // 字节数组 转换为 String字符串
        String s = new String(bys,"GBK");
        System.out.println(s);
    }
    fis.close();
}

转换流

InputStreamReader

语法 可以在构建对象的时候, 通过传入 字节输入流的参数, 把字节流转换为字符流

还可以在构建对象的时候, 传入编码字符集

字节流和字符流的对比

字节类似于 玩具的每一个模块

字符 类似于 一个小玩具, 是一个整体

public static void testGBK1()throws Exception{
    // 读取GBK.txt 文件 把把内容显示到控制台
    FileInputStream fis = new FileInputStream("D:\\GBK.txt");
    // 把字节流 转换为字符流 , 依赖于一个字节流  ,参数二 为指定格式的字符集
    InputStreamReader isr = new InputStreamReader(fis,"GBK");
    char [] chs = new char[1024];
    int len;
    while(  (len = isr.read(chs)) != -1){
        String s = new String(chs, 0, len);
        System.out.println(s);
    }
    fis.close();
}

字符流

字符输入流

Reader

InputStreamReader 是 Reader 的一个子类

FileReader

构造方法

FileReader(File file) 
创建一个新的 FileReader ,给出 File读取。 
FileReader(String fileName) 
创建一个新的 FileReader ,给定要读取的文件的名称。 

成员方法

int read() 
读一个字符  
int read(char[] cbuf) 
将字符读入数组。  
abstract int read(char[] cbuf, int off, int len) 
将字符读入数组的一部分。  

案例

读取文档内容

public static void read1() throws Exception{
    FileReader fr = new FileReader("D:\\a.txt");
    int ch;
    while( (ch = fr.read()) != -1){
        System.out.print((char)ch);
    }
    fr.close();
}

优化

public static void read() throws Exception{
    FileReader fr = new FileReader("C:\\盗墓笔记.txt");
    char [] chs = new char[1024];
    int len;
    while( (len = fr.read(chs)) != -1){
        System.out.print(new String(chs,0,len));
    }
    fr.close();
}

字符输出流

Writer

案例

// 文件复制案例
public static void copy() throws Exception{
    FileReader fr = new FileReader("C:\\盗墓笔记.txt");
    FileWriter fw = new FileWriter("D:\\盗墓.txt");
    char [] chs = new char[1024];
    int len;
    while( (len = fr.read(chs)) != -1){
        fw.write(chs,0,len);
    }
    fr.close();
    fw.close();
}

缓冲区增强流

BufferedReader

BufferedWriter

因为 缓冲区功能是一个增强流, 需要依赖于原始流

通过构造方法, 把原始流进行增强

使用BufferedReader 读取文件

public static void m1() throws  Exception{
    // 普通流
    FileReader fr = new FileReader("C:\\盗墓笔记.txt");
    // 把原始流进行增强
    BufferedReader br = new BufferedReader(fr);
    int ch;
    while( (ch = br.read()) != -1){
        System.out.print((char)ch);
    }
    // 只需要关闭外层流
    br.close();
}

特有方法

String readLine() 读一行文字。 

点名程序案例

​ 数组

​ 集合

​ IO流 继续优化

public static void main(String[] args) throws Exception {
    // 1- 读取 文本中的所有内容
    // 2- 每一行作为集合的一个元素
    // 3- 使用集合 书写点名程序
    FileReader fis = new FileReader("C:\\Users\\Administrator\\四班.txt");
    BufferedReader br = new BufferedReader(fis);
    // 定义存放所有 姓名的集合
    List<String> list = new ArrayList<String>();
    String s = null;
    while( (s = br.readLine()) != null ){
        list.add(s);
    }
    br.close();
    System.out.println(list);
    Random random = new Random();
    Scanner input = new Scanner(System.in);
  do{
      int no = random.nextInt(list.size());
      System.out.println("恭喜-=-"+list.get(no));
      list.remove(no);
      System.out.println("是否继续? 按0 继续");
      int i = input.nextInt();
      // 集合中没有数据了
      if(i != 0  || list.isEmpty()){
          System.out.println("点名完毕!");
          break;
      }
  }while(true);
}

四个文件拷贝案例

// 使用普通字符流
public static void copy() throws Exception{
    FileReader fr = new FileReader("C:\\盗墓笔记.txt");
    FileWriter fw = new FileWriter("D:\\盗墓.txt");
    int ch;
    long old = System.currentTimeMillis();
    while( (ch = fr.read()) != -1){
        fw.write(ch);
    }
    System.out.println("普通字符 所用时间: "+ (System.currentTimeMillis
    fr.close();
    fw.close();
}
//使用普通字符流+ 数组复制案例
public static void copy1() throws Exception{
    FileReader fr = new FileReader("C:\\盗墓笔记.txt");
    FileWriter fw = new FileWriter("D:\\盗墓.txt");
    char [] chs = new char[1024];
    int len;
    long old = System.currentTimeMillis();
    while( (len = fr.read(chs)) != -1){
        fw.write(chs,0,len);
    }
    System.out.println("普通字符数组所用时间: "+ (System.currentTimeMilli
    fr.close();
    fw.close();
}
// 使用缓冲流字符
public static void copy2() throws Exception{
    FileReader fr = new FileReader("C:\\盗墓笔记.txt");
    FileWriter fw = new FileWriter("D:\\盗墓.txt");
    BufferedReader br = new BufferedReader(fr);
    BufferedWriter bw = new BufferedWriter(fw);
    int ch;
    long old = System.currentTimeMillis();
    while( (ch = br.read()) != -1){
        bw.write(ch);
    }
    System.out.println("缓冲流 所用时间: "+ (System.currentTimeMillis(
    br.close();
    bw.close();
}
// 使用缓冲流 + 数组
public static void copy3() throws Exception{
    FileReader fr = new FileReader("C:\\盗墓笔记.txt");
    FileWriter fw = new FileWriter("D:\\盗墓.txt");
    BufferedReader br = new BufferedReader(fr);
    BufferedWriter bw = new BufferedWriter(fw);
    char [] chs = new char[1024];
    int len;
    long old = System.currentTimeMillis();
    while( (len = br.read(chs)) != -1){
        bw.write(chs,0,len);
    }
    System.out.println("缓冲数组流 所用时间: "+ (System.currentTimeMilli
    br.close();
    bw.close();
}

读写二进制文件

.java 经过编译过程 得到的 .class 字节码文件 (二进制文件)(可执行文件)

DataInputStream

DataoutputStream

FileOutputStream fos = new FileOutputStream("D:\\b.txt");
DataOutputStream dos = new DataOutputStream(fos);
FileInputStream fis = new FileInputStream("D:\\b.txt");
DataInputStream dis = new DataInputStream(fis);
dos.writeBoolean(true);
dos.writeByte(120);
dos.writeChar('丞');
// 要求读的顺序和写出的顺序一致, 才不会有读取异常的问题
boolean b = dis.readBoolean();
System.out.println(b);
byte b1 = dis.readByte();
System.out.println(b1);
char c = dis.readChar();
System.out.println(c);
dis.close();
dos.close();

打印流

PrintStream

// 重定向IO 
FileOutputStream fos = new FileOutputStream("D:\\c.txt",true);
PrintStream ps = new PrintStream(fos);
System.setOut(ps);
System.out.println("456789");

序列化流

序列化 ObjectOutputStream

将对象的状态写入到特定的流中的过程
(将内存中的对象 持久化到硬盘中的过程)

反序列化 ObjectInputStream

从特定的流中获取数据重新构建对象的过程
(把已经持久化到硬盘中的对象, 读取到内存中的过程)

案例

public class Student implements Serializable {
    String name;
    int age;
    String address;
    public Student() {
    }
    public Student(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
}

自定义类完成序列化功能

Student stu = new Student("张清华", 23, "青岛");
// stu 保存到硬盘中
// 序列化
FileOutputStream fos = new FileOutputStream("D:\\d.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
// java.io.NotSerializableException: s0729.Student
// TreeSet 排序 一个类想要具有排序的功能 必须实现 排序的接口
// 一个类可以序列化到 硬盘中 也是一个功能, 需要实现一个接口
oos.writeObject(stu);
oos.close();

类的序列化由实现java.io.Serializable接口的类启用。 不实现此接口的类将不会使任何状态序列化或反序列化。 可序列化类的所有子类型都是可序列化的。 序列化接口没有方法或字段,仅用于标识可串行化的语义。

  • 要求 被序列化的类 必须实现 Serializable接口
  • 使用ObjectOutputStream

反序列化

 public static void inobj() throws Exception{
     FileInputStream fis = new FileInputStream("D:\\d.txt");
     ObjectInputStream ois = new ObjectInputStream(fis);
     
     Object obj = ois.readObject();
     Student stu = (Student) obj;
     System.out.println(stu.name);
 }

问题1

student 对象 先序列化到 磁盘 , 修改Student 中的内容 , 再次进行反序列化

// java.io.InvalidClassException: s0729.Student;
// local class incompatible: stream classdesc serialVersionUID = -8030828354293478697,
// local class serialVersionUID = 4591992182167877374

每一个实现了 Serializable 接口的类, 都有一个 默认的 serialVersionUID

​ 当程序员改变了该类的中的内容时, 该ID 也会发生变化, 进行反序列化时 就会发生 UID 不一致的情况, 导致反序列化失败

针对该问题发生的原因, 当类发生变化的时候, 不希望serialVersionUID 发生变化, 在类中自定义 serialVersionUID 变量 , 改为 public static final 类型的

public static final long serialVersionUID = 1L;

问题2

如果希望一些保密字段不序列化到磁盘中

transient int age; // 声明不需要序列化的成员域

问题3

写出了多个不同对象, 需要按照写出的顺序读取

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章