java语法基础 - 第八部分 - IO流


2. IO流 - ( input/output流 )

2.1 分类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ucb1Z7QN-1574619667537)(en-resource://database/10217:1)]

本图来自于视频截图

流向( 针对内存 )
输入流:流入内存
输出流:流出内存到文件、硬盘上等
数据流类型
字符流 - 常用于文本文件
字节流 - 常用于非文本文件
数据流的功能
节点流:直接跟数据源相接
处理流:将某种流转为其他流进行处理

2.2 具体的流类
2.2.1 字节输入流、输出流( InputStream、OutputStream ) – 永远是根基

不管怎么优化:底层上字节输入、输出流依然还是一个一个字节上进行读、写操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lXn7ak78-1574619667540)(en-resource://database/10027:1)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IULUEiym-1574619667544)(en-resource://database/10029:1)]

FileInputStream、FIleOutputStream

针对文件的字节输入、输出流
字节输出流,没有自带的输出缓存

FileOutputStream构造函数参数
( String ):写入文件的路径
( File ):写入文件的File对象
( File, boolean ):写入文件的File对象,追加还是重写
( FileDescriptor ):不会用

  简单使用

public static void copyFile(String sourceFilePath, String targetFilePath) {

    InputStream fis = null;
    OutputStream fos = null;
    try {
         // 1. 构建字节输入、输出流对象
        fis = new FileInputStream( sourceFilePath );
        fos = new FileOutputStream( targetFilePath );
        
        // 2. 用来获取每次从字节流读取的字节 -- 自动转为int型
        int readChar = -1;
        /*
            // 2. 用来获取每次从字节流读取1024个字节,len标识实际读取到该数组的字节长度
           char[] buffer = new buffer[1024];
           int len = -1;
        */
        
        // 3. 将从字节输入流中读取的数据一个一个字节写入到目标文件上 -- 性能差
        while((readChar = fis.read()) != -1) {
            fos.write(readChar);
        }
        /*
         // 3. 将从字节输入流中读取的数据以len数组长度字节写入到目标文件上 -- 性能好
        while((len = fis.read(buffer)) != -1) {
            fos.write(buffer, 0 , len);
        }
        */
        
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 4. 关闭字节输入、输出流防止内存泄漏
        if(fos != null) {
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(fos != null) {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

}

//应用实例 - 运行下列的代码:
copyFile("C:/Users/lrc/Desktop/字符流.png", "C:/Users/lrc/Desktop/copyImg.png")

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zVqEC79A-1574619667552)(en-resource://database/10031:1)]

2.2.2 字符输入流、输出流( InputReader、OutputWriter )

内部实现依然是字节流

FileReader、FileWriter

1. 针对文件的字符输入、输出流
2. 文字字符操作流自动缓存 - Write只是写在一个输出缓存中,还没放入文件、硬盘上,需要缓存满、flush()、或者 close() 才会从内存输出到持久化文件上
3. 不管怎么优化:底层上字节输入、输出流实现依然还是按字节上进行读、写操作
4. 默认自带缓存是8k
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I5QEuBWx-1574619667556)(en-resource://database/10035:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-niWOljvc-1574619667561)(en-resource://database/10037:1)]

  简单使用 - 只能是文本文件

public static void copyFile(String sourcePath, String targetPath) {
    FileReader fr = null;
    FileWriter fw = null;
    try {
        fr = new FileReader(sourcePath);
        fw = new FileWriter(targetPath);

        char[] buffer = new char[1024];
        int len = -1;

        while((len = fr.read(buffer)) != -1) {
            fw.write(buffer, 0, len);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (fw != null) {
            try {
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (fr != null) {
            try {
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

//应用实例 - 运行下列的代码:
copyFile("C:/Users/lrc/Desktop/项目测试SQL.sql", "C:/Users/lrc/Desktop/a.txt");

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sceLAyRM-1574619667565)(en-resource://database/10033:2)]

2.2.3 转换流

解决到时别人给的流是不好处理尴尬情况,需要装换为其他的流进行处理

OutputStreamWriter,InputStreamReader

1. OutputStreamWriter: 字节流转为字符流的方式进行处理
2. InputStreamWriter: 字节流转为字符流


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hKSJIaSv-1574619667569)(en-resource://database/10039:1)]

  简单示例 - 字节流转字符流进行复制文件

public static void newFile(InputStream is, String targetPath) {
    Reader reader = null;
    FileWriter fos = null;
    try {
        reader = new InputStreamReader(is);
        fos = new FileWriter(targetPath);

        char[] buffer = new char[1024];
        int len = -1;
        while((len = reader.read(buffer)) != -1) {
            fos.write(buffer, 0, len);
        }

    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (fos != null) {
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            } 
        }
    }
}

//应用实例 - 运行下列的代码:
newFile("C:/Users/lrc/Desktop/项目测试SQL.sql", "C:/Users/lrc/Desktop/a.txt");

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GdZkMqOG-1574619667574)(en-resource://database/10033:2)]

  简单示例 - 输入、输出流的处理

public static void addContent(OutputStream os, InputStream is) {
    OutputStreamWriter osw = null;
    InputStreamReader isr = null;
    try {
        osw = new OutputStreamWriter(os, "utf-8");

        isr = new InputStreamReader(is);

        char[] buffer = new char[1024];
        int len = -1;
        while ((len = isr.read(buffer)) != -1) {
            osw.write(buffer, 0, len);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (osw != null) {
            try {
                osw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (isr != null) {
            try {
                isr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

//应用实例 - 运行下列的代码: --  运行两次
InputStream is = new FileInputStream("C:\\Users\\lrc\\Desktop\\项目测试SQL.sql");
OutputStream os = new FileOutputStream("C:\\Users\\lrc\\Desktop\\a.txt", true);
addContent(os,is);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-67Qwb6ah-1574619667583)(en-resource://database/10043:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EN0FXp2L-1574619667584)(en-resource://database/10045:1)]

2.2.4 缓冲流

1. 解决痛点:硬盘、内存的读写速率相差非常的大,频繁的从硬盘进行读写效率低、性能差

2. 可将缓冲流类比成购物车,而不是一个一个用手拿

BufferedInputStream,BufferedOutputStream

1. BufferedInputStream: 为另一个字节流添加内部缓存区数组,用于缓冲数据
2. BufferedOutputStream 将字节写入底层输出流中,而不是每个字节写入调用底层系统

3. 字节流没有自带的缓冲,可通过此留提升读写效率

4. 默认是缓冲数组是8KB,一旦数组满,自动刷新

  简单示例:复制文件 - 字节缓冲流

public static void newFile2(InputStream is, OutputStream os) {

    BufferedInputStream bis = null;
    BufferedOutputStream bos = null;
    try {
        bis = new BufferedInputStream(is);

        bos = new BufferedOutputStream(os);

        byte[] buffer = new byte[1024];
        int len = -1;
        while((len = bis.read(buffer)) != -1) {
            bos.write(buffer, 0, len);
        }

    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 关闭缓冲流,会自动的将字节流刷新读入、写出
        if (bos != null) {
            try {
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (bis != null) {
            try {
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

//应用实例 - 运行下列的代码: 
InputStream is = new FileInputStream("C:\\Users\\lrc\\Desktop\\字节流.png");
OutputStream os = new FileOutputStream("C:\\Users\\lrc\\Desktop\\a.png", true);
newFile2(is, oss);

BufferedReader,BufferedWriter

1. 缓冲各个字符,从而实现高效的读、写字符、数组、行

3. 字符流自带缓冲,通过该缓冲流可让其效率更加的高 - 并且可以指定缓存大小


  简单示例:复制文本文件 - 字符缓冲流

public static void newFile3(Reader r, Writer w) {
    BufferedReader br = null;
    BufferedWriter bw = null;
    try {
        br = new BufferedReader(r);
        bw = new BufferedWriter(w);

        char[] buffer = new char[1024];
        int len = -1;
        while((len = br.read(buffer)) != -1) {
            bw.write(buffer, 0, len);
        }

    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (bw != null) {
            try {
                bw.close();
            } catch (IOException e) {
                e.printStackTrace();
            } 
        }
        if (br != null) {
            try {
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            } 
        }
    }

}

2.2.5 打印流

增强输出流的功能

PrintStream( 字节 ),PrintWriter( 字符 )


  PrintStream - 往某个文件增添内容

public static void bytePrint(String targetPath, String addContent) {
    PrintStream ps = null;
    try {
        OutputStream os = new FileOutputStream(targetPath, true);
        BufferedOutputStream bs = new BufferedOutputStream(os);
        ps = new PrintStream(bs, true);
        ps.println(addContent);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } finally {
        if (ps != null) {
            ps.close();
        }
    }
}

  PrintWriter – 往某个文件增添内容

public static void charPrint(String targetPath, String addContent) {
    PrintWriter pw = null;
    try {
        // 节点流
        Writer w = new FileWriter(targetPath, true);
        
        // 处理流:硬盘写入的效率更加好
        BufferedWriter bw = new BufferedWriter(w);
        
        // 处理流:增加多种打印的内容方法
        pw = new PrintWriter(bw, true);
        pw.println(addContent);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (pw != null) {
            pw.close();
        }
    }
}
2.2.6 序列化、反序列化

1. 需要序列化的类必须实现 Serializable接口

2. 序列化: 把对象属性值存入硬盘上、反序列化: 文件上的内容还原成一个对象

3. 如果一个类有transient修饰的成员属性,则该属性的属性值不会被序列化文件上

序列化的情况
1. 需要将对象保存成为文件
2. 对象需要网络进行传输
ObjectInputStream( 反序列化 ),ObjectOutputStream( 序列化 )

  一个关于Emp类的JavaBean,用来下面序列化、反序列的测试

public class Emp implements Serializable{
	Integer empno;
	String ename;
	transient Integer year;
	public Integer getEmpno() {
		return empno;
	}
	public Integer getYear() {
		return year;
	}
	public void setYear(Integer year) {
		this.year = year;
	}
	public void setEmpno(Integer empno) {
		this.empno = empno;
	}
	public String getEname() {
		return ename;
	}
	public void setEname(String ename) {
		this.ename = ename;
	}
	@Override
	public String toString() {
		return "Emp [empno=" + empno + ", ename=" + ename + ", year=" + year + "]";
	}
	
}



  简单测试 - 序列化单个对象

public static void writeObject(String targetPath, Object targetObject) {
        ObjectOutputStream oos = null ;
        try {
            OutputStream os = new FileOutputStream(targetPath);
            oos = new ObjectOutputStream(os); 
            oos.writeObject(targetObject);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if( oos!=null ) {
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
}


// 测试代码
Emp emp = new Emp();
emp.setEmpno(1111);
emp.setEname("lrc");
emp.setYear(21);
writeObject("C:\\Users\\lrc\\Desktop\\emp.txt", emp);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c4wzJwSW-1574619667592)(en-resource://database/10047:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MXRCC9lm-1574619667596)(en-resource://database/10049:1)]



  简单测试 - 反序列化对象

public static Object readObject(String targetPath) {
    ObjectInputStream ois = null;
    try {
        InputStream is = new FileInputStream(targetPath);
        ois = new ObjectInputStream(is);
        Object object = ois.readObject();
        return object;
    } 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();
            }
        }
    }
    return null;
}


// 测试代码
// 将序列化的文件的反序列化为一个对象
Object o = readObject("C:\\Users\\lrc\\Desktop\\emp.txt");
System.out.println( o );

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QtINQnkP-1574619667600)(en-resource://database/10051:1)]



  简单测试 - 一个文件上存储多个序列化对象

一个序列化文件只能存储一个对象,故想要存储多个对象,可将其存在数组、容器中,然后序列化这个数组或者容器对象

       Emp emp1 = new Emp();
       emp1.setEmpno(1111);
       emp1.setEname("lrc");
       emp1.setYear(21);

       Emp emp2 = new Emp();
       emp2.setEmpno(1112);
       emp2.setEname("lrd");
       emp2.setYear(22);

       Emp[] emps = {emp1, emp2};
        
        // 序列化数组对象成为一个文件
       writeObject("C:\\Users\\lrc\\Desktop\\emp.txt", emps);
        
        // 反序列化数组文件
       Object o = readObject("C:/Users/lrc/Desktop/emp.txt");
       Object[] os = (Object[])o;
       System.out.println(Arrays.toString(os));

  运行结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GQSk8XOZ-1574619667607)(en-resource://database/10053:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b0uvplwK-1574619667611)(en-resource://database/10055:1)]

2.2.6 字节字符数组流 – 与文件无关

1. 拥有内部缓冲区 – 只针对内存操作
2. 关闭此流无效,此对象流的方法仍然有效
3. 可以不用关闭此流,本质内部就是个数组

ByteArrayInputStream,ByteArrayOutputStream

跟字符数组流差不多

CharArrayInputStream,CharArrayOutputStream

  简单示例 - 用上述两者字符数组流进行筛选字符

public static char[] getLetter(String str) {
    try {
        CharArrayReader car = new CharArrayReader(str.toCharArray());
        CharArrayWriter caw = new CharArrayWriter();
        int currentChar = -1;
        while ((currentChar = car.read()) != -1) {
            System.out.println((char)currentChar);
            if ((65 <= currentChar && currentChar <= 90) || (97 <= currentChar && currentChar <= 122)) {
                caw.write(currentChar);
            }
        }
        return caw.toCharArray();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

//测试代码
char[] a = getLetter("凡事都疯了似的432fsdafsdfSFDDSfsdf凡事都浪费");
System.out.println(new String(a));

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3O0echJh-1574619667618)(en-resource://database/10057:1)]

2.2.6 数据流
DataInputStream、DataOutputStream

1. DataInputStream:使用底层输入流读取操纵Java基本数据类型,由数据输出流写入,数据输入流读取数据 - 输入字节大小完全根据Java决定

2. DataOutputStream:应用程序将基本数据类型写入输出流中,使用输入流将数据读入

3. 线程安全可选

4. 写读顺序一致,要不然读取会乱码


  简单示例 - 写入基本数据类型数据到文件

public static void writeData(String sourcePath, String content) {
    DataOutputStream dos = null;
    try {
        OutputStream os = new FileOutputStream(sourcePath);
        dos = new DataOutputStream(os);
        dos.writeUTF(content);

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if( dos != null) {
            try {
                dos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

//测试代码
writeData("C:\\Users\\lrc\\Desktop\\emp.txt", "fsdfsdafsdr放松放松地方1111");

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zIidw9Hr-1574619667623)(en-resource://database/10061:1)]


  简单示例 - 从文件中读取由DataOutStream写入的数据

public static void readData(String sourcePath) {
    try {
        InputStream is = new FileInputStream(sourcePath);
        DataInputStream dis = new DataInputStream(is);
        String readStr = dis.readUTF();
        System.out.println(readStr);
        dis.close();

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

//测试代码
readData( "C:\\Users\\lrc\\Desktop\\emp.txt" );

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kUsHil0F-1574619667624)(en-resource://database/10063:1)]

2.2.6 其他流
StringReader,StringWriter - 字符串流( 字符流 )

可将字符串转成字符流

作用:到时可将字符串数据转成字符流进行解析数据

  简单示例 - 计算字符串的单词个数

public static Integer getWordCount(String target) {

    StringReader sr = new StringReader(target);

    StreamTokenizer st = new StreamTokenizer(sr);

    int count = 0;
    while(st.ttype != StreamTokenizer.TT_EOF) {
        try {
            if( st.nextToken() ==  StreamTokenizer.TT_WORD ) {
                count++;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    return count;
}


// 测试代码
int count = getWordCount("23 happy new 465 year 分公司的但是 1245fds");
System.out.println( count );


  运行结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t7jPxcJj-1574619667632)(en-resource://database/10075:1)]

PipedInputStream,PipedOutputStream - 管道流(字节流)

特点:

  • 1. 管道输入流 提供数据给 管道输出流
  • 2. 管道输入流自带缓冲区
  • 3. 通常:1个线程 管理 管道输入流,其他线程 管理 管道输出流
  • 4. 管道输入流的线程不存在,则管道已被破坏

作用: 线程之间的通讯
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6HgrqY5y-1574619667634)(en-resource://database/10081:1)]

  1. PipeOutput线程

class PipeOutput implements Runnable {

	String sourcePath;
	PipedOutputStream pos;
	
	PipeOutput(PipedOutputStream pos, String sourcePath) {
		this.pos = pos;
		this.sourcePath = sourcePath;
	}

	@Override
	public void run() {
		InputStream is = null;
		try {
			is = new FileInputStream(sourcePath);

			byte[] buffer = new byte[2048];
			int len = -1;
			
			// 将外部文件的内容写入到内存上 
			while ((len = is.read(buffer)) != -1) {
				pos.write(buffer, 0, len);
			}

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {

			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (pos != null) {
				try {
					pos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

		}
	}
}

  2, PipeInput线程

class PipeInput implements Runnable {

	PipedInputStream pis;
	String receive;

	PipeInput(PipedInputStream pis) {
		this.pis = pis;
	}

	public String getReceive() {
		return receive;
	}

	@Override
	public void run() {
		StringBuilder sb = new StringBuilder();
		try {
			byte[] buffer = new byte[2048];
			int len = -1;
			// 将输出管道流写进内存的内容进行读取,并把读取的内容用 StringBuilder保存,最后存到receiver
			while ((len = pis.read(buffer)) != -1) {
				String a = new String(buffer, 0, len);
				sb.append(a);
			}
			receive = sb.toString();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				pis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

	}
}


  3. 测试上述两个管道流线程 - 在另一个类中定义的

@Test
public void test1() throws IOException, InterruptedException {

    // 1. 创建输入、输出管道流
    PipedInputStream pis = new PipedInputStream();
    PipedOutputStream pos = new PipedOutputStream();

    // 2. 将两个管道进行连接
    pis.connect(pos);

    // 3. 输入、输出管道流在各自的线程上进行运行
    PipeInput pi = new PipeInput(pis);
    PipeOutput po = new PipeOutput(pos, "C:\\Users\\lrc\\Desktop\\项目测试SQL.sql");

    Thread tRead = new Thread(pi);
    Thread tWrite = new Thread(po);

    // 4. 启动线程,当前有三个线程进行运行, tRead、tWrite、以及调用test1的线程
    tRead.start();
    tWrite.start();

    // 5. 休眠test1函数的线程,因为 tRead、tWrite线程会阻塞,导致提前调用test1的线程,从而致使下面的receive为空
    Thread.currentThread().sleep(1000);

    // 6. 获取tRend线程的 receive成员变量的值,打印输出到控制台
    String receive = pi.getReceive();
    if (receive != null) {
        System.out.println(receive.length());
        System.out.println(receive);
    } else {
        System.out.println("没有接收到东西");
    }

}



  运行结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DbcUNcqg-1574619667642)(en-resource://database/10083:1)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BMfkQA64-1574619667643)(en-resource://database/10085:1)]

RandomAccessFile – 以字节流的方式对文件进行读写

只可以对文件进行操作 - (文件的读取、写入)
强大的文件输写功能 – 内部是 byte[] 数组
劣势:已经被NIO取代

RandomAccessFile模式
r( 读模式 ):文件不存在则报错
rw( 读写模式 ):文件不存在,则创建新文件

  简单示例 – 文件的合并

public static void writeContent(String sourcePath, String targetPath) {
    RandomAccessFile source = null;
    RandomAccessFile target = null;
    try {
        // 1. 定义 文件流
        source = new RandomAccessFile(sourcePath, "r");
        target = new RandomAccessFile(targetPath, "rw");

        // 2. 设置文件流的输入指针在文件末,而不是默认的文件头
        target.seek(target.length());

        // 3. 填充内容到目标文件末尾
        byte[] buffer = new byte[2048];
        int len = -1;
        while( (len = source.read(buffer) ) != -1) {
            target.write(buffer, 0, len);
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            source.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            target.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}


// 测试代码
writeContent("C:\\Users\\lrc\\Desktop\\测试\\常见面试题.txt", "C:\\Users\\lrc\\Desktop\\测试\\项目测试SQL.sql");  


  运行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JFzRrWga-1574619667649)(en-resource://database/10087:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ofsefhgi-1574619667656)(en-resource://database/10089:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i7Cwr3co-1574619667659)(en-resource://database/10091:1)]

2.2.7 ZipOutputStream( 压缩 ), ZipInputStream( 解压 ) – 文件压缩

  应用示例 - 压缩文件

// 传入你将需要压缩文件的绝对路径
public static void compress(String sourcePath) {

    try {
        // 1. 创建一个压缩流、并且如果没有关联的压缩文件,自动创建  --  压缩后的文件在桌面上
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("C:\\Users\\lrc\\Desktop\\压缩.zip"));

        // 2. 将即将压缩的文件名转为 File对象
        File f = new File(sourcePath);

        // 3. 调用递归函数
        zipFile(zos, f, f.getName());

        // 4. 压缩输出流关闭
        zos.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

}

public static void zipFile(ZipOutputStream zos, File f, String prefix) throws IOException {
    // 1. 获取当前File对象  是文件 则返回null 是目录则返回File数组 
    File[] files = f.listFiles();
    // 2. 文件压缩
    if (files == null) {
            zos.putNextEntry(new ZipEntry(prefix));
            InputStream is = new FileInputStream(f);
            BufferedInputStream bis = new BufferedInputStream(is);
            byte[] buffer = new byte[1024];
            int len = -1;
            while ((len = bis.read(buffer)) != -1) {
                zos.write(buffer, 0, len);
            }
            bis.close();
    // 3. 目录压缩
    } else {
        if(files.length == 0) 
            zos.putNextEntry(new ZipEntry(prefix + "/"));
        for (File file : files) {
             zipFile(zos, file, prefix + "/" + file.getName());
        }
    }
}

// 测试代码
compress("C:\\Users\\lrc\\Desktop\\Jaav-Web页面");

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2V92viZf-1574619667667)(en-resource://database/10213:1)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zr2nugCK-1574619667668)(en-resource://database/10215:1)]


  应用示例 - 解压缩文件

/**
 * @Description 传入需要解压的文件的绝对路径,以解压文件名为目录在桌面上显示解压结果
 */
public static void decompress(String zipFileName) {

    // 1. 获取zipFileName压缩文件的文件名,并以这个文件名为 压缩后文件的目录
    int zipNameEnd = zipFileName.lastIndexOf(".");
    int zipNameStart = zipFileName.lastIndexOf("/") != -1? zipFileName.lastIndexOf("/") : zipFileName.lastIndexOf("\\");
    String directory = zipFileName.substring(zipNameStart+1, zipNameEnd);
    String path = "C:\\Users\\lrc\\Desktop\\" + directory + "\\";

    try {
        // 2. 获取一个解压流对象
        InputStream is = new FileInputStream(zipFileName);
        ZipInputStream zis = new ZipInputStream(is);
        ZipEntry entry = null;
        File file = null;

        // 3. 迭代进行遍历获取解压流的 zipEntry条目
        while((entry = zis.getNextEntry())!=null && entry.isDirectory() == false) {
            file = new File(path + entry.getName());
            System.out.println(file.getPath());

            // 3.1 重复的创建目录,不会覆盖目录中已存在的文件
            file.getParentFile().mkdirs();

            FileOutputStream fos = new FileOutputStream(file);

            byte[] buffer = new byte[1024];
            int len = -1;
            while((len = zis.read(buffer)) != -1)  {
                fos.write(buffer, 0, len);
            }
            fos.close();
        }
        zis.close();

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

2.3 应用
2.3.1 文件分割
public static void splitFile(String filePath, long splitLength) {
    File sourceFile = new File(filePath);
    // 1. 分割的文件数
    int remainder = (int) (sourceFile.length() % splitLength);
    int fileCount = (int) (sourceFile.length() / splitLength);
    int num_file = remainder == 0 ? fileCount : fileCount + 1;

    // 声明一个字节输入缓冲流
    BufferedInputStream bis = null;
    try {
        InputStream is = new FileInputStream(sourceFile);
        bis = new BufferedInputStream(is);

        // 2. 生成num_file个切割文件
        for (int i = 0; i < num_file; i++) {

            // 3.  打印分割文件的绝对路径
            System.out.println(sourceFile.getParent() + "\\s" + (i + 1) + " - " + sourceFile.getName());
            OutputStream os = new FileOutputStream(
                    sourceFile.getParent() + "\\s" + (i + 1) + " - " + sourceFile.getName());
            BufferedOutputStream bos = new BufferedOutputStream(os);
            byte[] buffer = null;
            int len = -1;

            // 如果切割文件的字节数小于 2KB 则直接以一个切割文件字节数来缓冲数组
            if (splitLength <= 2048) {
                buffer = new byte[(int)splitLength];
                if ((len = bis.read(buffer)) != -1)
                    bos.write(buffer, 0, len);
                bos.close();
                continue;
            // 否则每次以2kB来进行切割文件的写操作
            } else {
                buffer = new byte[2048];
                // 计算每个切割文件需要缓冲数组写操作的次数
                int count = splitLength % 2048 == 0 ? (int) (splitLength / 2048) : (int) (splitLength / 2048) + 1;
                for (int j = 0; j < count; j++) {
                    if ((len = bis.read(buffer)) != -1)
                        bos.write(buffer, 0, len);
                }
                bos.close();
            }
        }

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (bis != null) {
                bis.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}


// 测试代码
// -- 每个文件分割成为8M
splitFile("D:\\谷歌\\下载的文件\\张国荣 - 童年时.flv", 1024*1024*8); 


  运行效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tkGNlhwY-1574619667677)(en-resource://database/10069:1)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i85QWZDd-1574619667680)(en-resource://database/10067:1)]

2.3.3 文件合并 - sequenceInputStream

各种输入流的逻辑串联,依次读取流集合中每个流的信息,直至到最后一个流才结束

public static void mergeFile(String... sourcePaths) {
        ArrayList<InputStream> lists = new ArrayList<InputStream>();
        BufferedOutputStream bis = null;
        SequenceInputStream sis = null;
        try {
            // 1. 将需要合并的文件放入list容器中
            for (String sourcePath : sourcePaths) {
                lists.add(new FileInputStream(sourcePath));
            }

            // 2. 序列输入流接受的参数是枚举类似的,故需要转型
            Enumeration<InputStream> enums = Collections.enumeration(lists);
            sis = new SequenceInputStream(enums);

            // 3, 以第一个文件类型为标准,确定合并后的文件类型
            String fileType = sourcePaths[0].substring((sourcePaths[0].lastIndexOf(".")));

            // 4. 将合并后的文件输出到桌面上,并且 文件名 以      合并文件.fileType
            bis = new BufferedOutputStream(
                    new FileOutputStream("C:\\Users\\lrc\\Desktop\\合并文件" + fileType));

            byte[] buffer = new byte[2048];
            int len = -1;

            // 5. 将序列输入流的流文件依次写入到 合并文件输出流中
            while((len = sis.read(buffer)) != -1) {
                bis.write(buffer, 0, len);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if( sis != null) {
                try {
                    sis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if( bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
}


// 测试代码
String[] sourcePaths = {"D:\\谷歌\\下载的文件\\s1 - 张国荣 - 童年时.flv", "D:\\谷歌\\下载的文件\\s2 - 张国荣 - 童年时.flv", "D:\\谷歌\\下载的文件\\s3 - 张国荣 - 童年时.flv"};
mergeFile(sourcePaths);


运行效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NM4DXvIP-1574619667683)(en-resource://database/10071:1)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ep9784kB-1574619667690)(en-resource://database/10073:1)]

3. NIO流 - ( New input/output流 )

3.1 概念

NIO优势:

  • 1. 传统流I/O是以单字节流进行传输,而新I/O是以数据块的形式进行传输

  • 2. 所有数据都是用缓冲区进行的 — 重点

  • 3. 缓冲区提供对数据的结构化访问 - 对每种基本数据类型都有对应的缓冲区

缓冲区类型
ByteBuffer
ShortBuffer
IntBuffer
LongBuffer
CharBuffer
FloatBuffer
DoubleBuffer

3.2 缓冲区方法
缓冲区方法
position(), limit(), capacity():获取指针位置,含有元素,数组容量
flip():计算limit为当前的position--limit(),并将指针移到0-position
clear():清空数组中的元素,指针变为0,limit变为数组容量
hasRemaining(), remaining():是否填充有元素,含有的填充元素个数
get( int ),put(元素):获取某个序号的元素、往数组放入元素并且指针position往前移动
public static void main(String[] args) throws IOException {

    IntBuffer ib = IntBuffer.allocate(10);
    System.out.println(ib + "\n");

    ib.put(1);
    ib.put(2);
    System.out.println(ib + "\n");

    ib.flip();
    System.out.println(ib + "\n");

    if(ib.hasRemaining()) {
        for(int i = 0; i < ib.remaining(); i++) {
            System.out.println(ib.get(i));
        }
    }

    System.out.println("");
    ib.put(15);
    ib.flip();
    System.out.println(ib);
}


  运行结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xzj5UhaE-1574619667692)(en-resource://database/10329:1)]

3.3 Channel通道

通过Channel通道数据往缓冲区进行读写 - 我们不会在通道进行获取、读取数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qfZyOnVN-1574619667701)(en-resource://database/10327:1)]


  简单示例 - 文件复制 - 方式1

public static void copyFile(String sourcePath) {
    
    File sourceFile = new File(sourcePath);
    String copiedFile = "C:\\Users\\lrc\\Desktop\\" + sourceFile.getName();
    FileInputStream fis = null;
    FileOutputStream fos = null;
    FileChannel fc1 = null;
    FileChannel fc2 = null;
    try {
        // 1.新建文件输入输出流
         fis = new FileInputStream(sourceFile);
         fos = new FileOutputStream(copiedFile);  //将文件复制到桌面

         // 2. 获取与文件相连的通道
        fc1 = fis.getChannel();
        fc2 = fos.getChannel();

        // 3. 新建一个2048字节的 字节缓冲区  --  我们只操作这部分
        ByteBuffer bb = ByteBuffer.allocate(2048);

        // 4. 将 字节缓冲区的数据通过 Channel通道 填充文件
        while(fc1.read(bb)!= -1) {
            // 4.1 确定当前字节缓冲区limit已经填充元素的个数
            bb.flip();
            // 4.2 将字节缓冲区的数据填充入文件
            fc2.write(bb);

            // 4.3 将字节缓冲区的所有数据清空,恢复初始的状态
            bb.clear();
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();

    // 5. 关闭流、以及通道
    } finally {
        try {
            fc2.close();
            fc1.close();
            fis.close();
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}



  源码分析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4UL6YBlM-1574619667702)(en-resource://database/10331:1)]


  简单示例 - 文件复制 - 方式2

public static void randomCopyFile(String sourcePath) {
    File sourceFile = new File(sourcePath);
    String copiedFile = "C:\\Users\\lrc\\Desktop\\" + sourceFile.getName();
    RandomAccessFile raf_R = null;
    RandomAccessFile raf_W = null;

    FileChannel readC = null;
    FileChannel writeC = null;
    try {
        // 1. 获取对应文件输入 随机存取输入输出流
        raf_R = new RandomAccessFile(sourceFile, "r");
        raf_W = new RandomAccessFile(copiedFile, "rw");

        // 2. 根据1的输入输出流获取 字节通道
        readC = raf_R.getChannel();
        writeC = raf_W.getChannel();

        long length = sourceFile.length();

        // 3. 根据2的道道,获取对应的内存 字节缓冲区 -- 输入缓冲区已经是有内容的
        MappedByteBuffer map_R = readC.map(MapMode.READ_ONLY, 0, length);

        // 4. 注意输出缓冲区映射需要设置的跟复制文件的大小一样
        MappedByteBuffer map_W = writeC.map(MapMode.READ_WRITE, 0, length);

        // 5. 将映射文件的输入缓冲区内容,增添到输出缓冲区
        if (map_R.hasRemaining()) {
            for (int i = 0; i < map_R.remaining(); i++) {
                map_W.put(map_R.get(i));
            }
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            // 6. 通道关闭时,会将输出映射缓冲区数据块写入文件
            writeC.close();
            readC.close();
            raf_W.close();
            raf_R.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

4 I/O性能

  性能越往下越低

通道内存映射 - Channel.Map()
自己定义内存映射缓存xxxBuffer,写入到通道 - Channel.write( xxxBuffer );
经过Buffered修饰的I/O流 - BufferedInputStream( InputStream )
无Buffered修饰的流 - FIleInputStream
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值