从“追求尽量不出错”,到正视“出错是必然”的转变,才是|

如此而已~~~

园龄:3年3个月粉丝:0关注:12

22_Java中的IO流

Java中的IO流

一、File

注意:File对象可能是文件抽象路径字符串,也可能是目录抽象路径字符串,主要取决于使用的创建方法

构造函数相当于保存了字符串形式的路径名称,根据创建方法创建文件

1、File类的概述和构造方法:

File:它是文件和路劲名的抽象表示(即存储的是文件的路径名)

​ 文件和目录是可以通过File封装成对象的

​ 对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径而已。它可以是存在的,也可以是不存在的。

​ 将来是要通过具体操作把这个路劲的内容转换为具体存在的

方法名 说明
File(String pathname) 通过将给定的路径名 字符串 转换为抽象路径来创建新的File实例
File(String parent, String child) 从父路径名字和子路径名字 字符串 创建新的File实例
File(File parent, String child) 从父抽象路径名和子路径名 字符串 创建新的File实例
package com.itheima;
/*
File:它是文件和路劲名的抽象表示
文件和目录是可以通过File封装成对象的
对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径而已。它可以是存在的,也可以是不存在的。
将来是要通过具体操作把这个路劲的内容转换为具体存在的
File(String pathname) 通过将给定的路径名 字符串 转换为抽象路径来创建新的File实例
File(String parent, String child) 从父路径名字和子路径名字 字符串 创建新的File实例
File(File parent, String child) 从父抽象路径名和子路径名 字符串 创建新的File实例
*/
//导包
import java.io.File;
public class FileDemo01 {
public static void main(String[] args){
//File(String pathname) 通过将给定的路径名 字符串 转换为抽象路径来创建新的File实例
File f1 = new File("D:\\itcast\\java.txt"); //在Java中"//"字符表示"/"
System.out.println(f1); //可见此类重写了toString()方法
//File(String parent, String child) 从父路径名字和子路径名字 字符串 创建新的File实例
File f2 = new File("D:\\itcast", "java.txt");
System.out.println(f2);
//File(File parent, String child) 从父抽象路径名和子路径名 字符串 创建新的File实例
File f3 = new File("D:\\itcast");
File f4 = new File(f3, "java.txt");
System.out.println(f4);
}
}
2、File类的创建功能:

注意:不能根据抽象文件名来确定最后创建的是文件还是文件夹,需要看方法:createNewFile()创建的是文件,mkdir()和mkdirs()创建的是文件夹,且文件与文件夹不要重名

方法名 说明
public boolean createNewFile() 当具有该名称的文件不存在时,创建一个由该抽象路径名命名的新空文件
public boolean mkdir() 创建由此抽象路径名命名的目录
public boolean mkdirs() 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录(上级目录)
package com.itheima;
/*
File类"创建"功能
public boolean createNewFile() 当具有该名称的文件不存在时,创建一个由该抽象路径名命名的新空文件
如果文件不存在,就创建文件,并返回true
如果文件存在,就不创建文件,并返回false
public boolean mkdir() 创建由此抽象路径名命名的目录
如果文件夹不存在,就创建文件夹,并返回true
如果文件夹存在,就不创建文件夹,并返回false
public boolean mkdirs() 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录
用于当我们需要指定的文件夹,但是中间连接路径不存在时 将所到的(包括本身)的不存在文件夹都一起创建了
*/
//导包
import java.io.File;
import java.io.IOException;
public class FileDemo02 {
public static void main(String[] args) throws IOException{ //使用throws抛出了IOException异常
//需求一:我需要在D:\\itcast目录下创建一个文件java.txt
File f1 = new File("D:\\itcast\\java.txt");
System.out.println(f1.createNewFile());
//需求二:我需要在D:\\itcast目录下创建目录JavaSE
File f2 = new File("D:\\itcast\\JavaSE");
System.out.println(f2.mkdir());
//需求一:我需要在D:\\itcast目录下创建一个多级目录JavaWEB\\HTML
File f3 = new File("D:\\itcast\\JavaWEB\\HTML");
System.out.println(f3.mkdirs());
//需求一:我需要在D:\\itcast目录下创建一个文件javase.txt
File f4 = new File("D:\\itcast\\javase.txt");
System.out.println(f4.createNewFile());
//测试mkdirs()方法是否能创建文件
// File f5 = new File("D:\\itcast\\JavaEE\\java.txt");
// System.out.println(f5.mkdirs());
}
}
3、File类判断和获取功能:

使用list与listFile方法时注意,如果对象是一个具体的文件,那么它下面就不会有文件或目录所以获得的数组就是null,注意空指针异常

方法名 说明
public boolean isDirectory() 判断此抽象路径名表示的 File是否为目录
public boolean isFile() 判断此抽象路径名表示的 File是否为文件
public boolean exists() 判断此抽象路径名表示的 文件或目录是否存在
public String getAbsolutePath() 返回此抽象路径名的 绝对路径名 字符串
public String getPath() 将定义此文件对象时的 抽象路径名 以字符串形式返回
public String getName() 返回由此抽象路径名表示的文件或目录的 名称(中间路径不在显示)
public String[] list() 获取此抽象路径名下的 所有文件和目录名称 并以字符串数组的形式返回
public File[] listFiles() 获取此抽象路径名下的 所有文件和目录 的File对象数组并返回
package com.itheima;
/*
public boolean isDirectory() 测试此抽象路径名表示的File是否为目录
public boolean isFile() 测试此抽象路径名表示的File是否为文件
public boolean exists() 测试此抽象路径名表示的文件或目录是否存在
public String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串
public String getPath() 将此抽象路径名转换为路径名称字符串
public String getName() 返回由此抽象路径名表示的文件或目录的名称
public String[] list() 返回此抽象路径名表示的目录中的文件和目录的名称的字符串数组
public File[] listFiles() 返回此抽象路径名表示的目录中的文件和目录的File对象数组
*/
//导包
import java.io.File;
import java.io.IOException;
public class FileDemo03 {
public static void main(String[] args) throws IOException{
//创建一个File对象
//本模块目录下创建() 主要用于区分方法的展示
//如果本身使用绝对路径来创建 getPath() 与 getAbsolutePath()返回值就是一样的
File f = new File("myFile\\java.txt");
//先创建这个文件夹
System.out.println(f.createNewFile());
// public boolean isDirectory() 测试此抽象路径名表示的File是否为目录
// public boolean isFile() 测试此抽象路径名表示的File是否为文件
// public boolean exists() 测试此抽象路径名表示的File是否存在
System.out.println(f.isDirectory());
System.out.println(f.isFile());
System.out.println(f.exists());
System.out.println("------------");
// public String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串
// public String getPath() 将此抽象路径名转换为路径名称字符串
// public String getName() 返回由此抽象路径名表示的文件或目录的名称
System.out.println(f.getAbsolutePath()); //返回绝对路径
System.out.println(f.getPath()); //返回这个文件对象对应的抽象路径名
System.out.println(f.getName()); //返回这个文件对象最终表示的文件或目录的名称
System.out.println("------------");
// public String[] list() 返回此抽象路径名 表示的目录中 所含的文件和目录的名称的字符串数组
// public File[] listFiles() 返回此抽象路径名 表示的目录中 所含的文件和目录的File对象数组
File f2 = new File("D:\\itcast");
String[] strArray = f2.list();
for(String s : strArray){
System.out.println(s);
}
System.out.println("------------");
File[] fileArray = f2.listFiles();
for(File file : fileArray){
// System.out.println(file);
// System.out.println(file.getName());
// System.out.println(file.getPath());
// System.out.println(file.getAbsolutePath());
//返回文件对象 可以使用方法来判断是否为文件
if(file.isFile()){
System.out.println(file.getName());
}
}
}
}
4、File类删除功能:
方法名 说明
public boolean delete() 删除由此路径名表示的文件或目录

1、绝对路径和相对路径的区别:

​ 绝对路径:完整的路径名,不需要任何其它信息就可以定位文件

"D:\\itcast\\java.txt"

​ 相对路径:必须使用取自其它路径名的信息并进行解释

"myFile\\java.txt"

2、删除目录时的注意事项:

​ 如果一个目录中又内容(目录、文件),不能直接删除,应该先删除目录中的内容,最后才能删除目录

删除功能参考代码:

package com.itheima;
/*
File类的删除功能:
public boolean delete() 删除由此路径名表示的文件或目录
*/
//导包
import java.io.File;
import java.io.IOException;
public class FileDemo04 {
public static void main(String[] args) throws IOException{
//一、使用几种抽象文件名创建文件方式
//创建一个File类对象
// File f1 = new File("D:itcast\\java.txt");
//直接使用文件名创建文件 会在本模块同级目录下创建文件
// File f1 = new File("java.txt");
// if(!f1.exists()){
// System.out.println(f1.createNewFile());
// }
/*//在本模块下创建文件
File f1 = new File("myFile\\java.txt");
// if(!f1.exists()){
// System.out.println(f1.createNewFile());
// }
//二、删除当前模块目录下的java.txt文件
System.out.println(f1.delete());
System.out.println("------------");*/
/*//三、在当前模块下创建一个目录itcast
File f2 = new File("myFile\\itcast");
if(!f2.exists()){
System.out.println(f2.mkdir());
}
//四、删除当前模块下的itcast目录
System.out.println(f2.delete());*/
//五、在当前模块下创建一个目录itcast,然后在该目录下创建一个文件夹java.txt
//注意在创建文件之前 需要创建其上级目录
File f3 = new File("myFile\\itcast");
if(!f3.exists()){
System.out.println(f3.mkdir());
}
File f4 = new File("myFile\\itcast\\java.txt");
if(!f4.exists()){
System.out.println(f4.createNewFile());
}
//六、删除当前目录下的itcast (注意当目录下还存在文件时是不能使用delete方法直接删除的)
System.out.println(f4.delete());
System.out.println(f3.delete());
}
}
5、递归:

1、递归概述:

​ 以编程的角度来看,递归指的是方法定义中调用方法本身的现象

2、递归解决问题的思路:

​ 把一个复杂问题层层转化为一个与原问题相似的规模较小的问题来求解

​ 递归策略只需要少量的程序就可以描述出解题过程所需要的多次重复计算

3、递归解决问题要找到两个内容:

​ 递归出口:否则程序会出现内存溢出

​ 递归规则:与原问题相似的规模较小的问题

参考代码:

package com.itheima_01;
/*
递归概述:
以编程的角度来看,递归指的是方法定义中调用方法本身的现象
*/
public class DiGuiDemo {
public static void main(String[] args){
//回顾不死神兔问题,求第20个月兔子的对数
//每月的兔子对数:1, 1, 2, 3, 5...
// int[] arr = new int[20];
//
// arr[0] = 1;
// arr[1] = 1;
//
// for(int i = 2; i < arr.length; i++){
// arr[i] = arr[i - 1] + arr[i - 2];
// }
// System.out.println(arr[19]);
System.out.println(f(20));
}
/*
使用递归解决,首先定义一个方法:
定义一个方法f(n),表示n个月的兔子对数
那么,第n-1个月的兔子对数该如何表示呢?f(n-1)
同理,第n-2个月的兔子对数该如何表示呢?f(n-2)
StackOverflowError:当堆栈溢出发生时抛出一个应用程序递归太深
*/
public static int f(int n){
//第一个月与第二个月 数量都是1
if(n == 1 || n == 2){
return 1;
}
else{
//从第三位数开始如此
return f(n - 1) + f(n - 2);
}
}
}

4、案例1:使用递归求阶乘:

package com.itheima_01;
/*
使用递归求取阶乘
*/
//导包
import java.util.Scanner;
public class DiGuiTest {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个整型数据:");
int number = sc.nextInt();
System.out.println("这个数的阶乘为:" + factorial(number));
}
public static int factorial(int n){
//当n为1时 返回值为1
if(n == 1){
return 1;
}
//当n > 1时 n! = n * (n-1)
else{
return n * factorial(n-1);
}
}
}

5、案例2:遍历目录:

需求:给定一个路径,请通过递归完成遍历该目录下的所有内容,并把所有文件的绝对路径输出在控制台

package com.itheima_02;
/*
需求:
给定一个路径,请通过递归完成遍历该目录下的所有内容,并把所有文件的绝对路径输出在控制台
思路:
1、根据给定的路径创建一个File对象
2、定义一个方法,用于获取给定目录下的所有内容,参数为第一步所创建的File对象
3、获取给定的File目录下所有的文件或者目录的File数组
4、遍历该File数组,得到每一个File对象
5、判断该File对象是否是目录
是,递归调用
不是,获取绝对路径输出在控制台
6、调用方法
*/
//导包
import java.io.File;
public class DiGuiDemo {
public static void main(String[] args){
//根据一个给定路径创建一个File对象
File srcFile = new File("D:\\itcast");
//方法的调用
getAllFilePath(srcFile);
}
public static void getAllFilePath(File srcFile){
//通过listFiles方法获取当前文件对象下的所有目录和文件,并以文件类数组的形式返回
File[] files = srcFile.listFiles();
//依次获取对象并判断
if(files != null){ //防止数组为空 或 集合为空
for(File file : files){
//是文件就输出绝对路径
if(file.isFile()){ //出口
System.out.println(file.getAbsolutePath());
}
//是目录就继续调用方法
else if(file.isDirectory()){ //规则
getAllFilePath(file);
}
}
}
}
}

二、 字节流

1、IO流概述和分类:

1、IO流概述:

​ IO:输入输出(Input/Output)

​ 流:是一种抽象概念,是对数据传输的总称。也就是说数据在设备键的传输称为流,流的本质是数据传输

​ IO流就是用来处理设备间数据传输问题的

​ 常见的应用:文件复制;文件上传;文件下载

2、IO流的分类(这里的输入输出是针对内存而言):

​ 按照数据的流向

​ 输入流:读文件(硬盘读数据到内存)

​ 输出流:写文件(内存在硬盘上写数据)

​ 按数据类型来分

​ 字节流:字节输入流;字节输出流

​ 字符流:字符输入流;字符输出流

一般来说,我们说IO流的分类是按照数据类型来分的

那么这两种流都在什么情况下使用呢?

​ 如果数据通过Window自带的记事本软件打开,我们还可以读懂里面的内容,就使用字符流,

​ 否则使用字节流。如果不知道该使用哪种类型的流,就使用字节流

2、字节流写数据:

1、字节流抽象基类:

​ InputStream:这个抽象类是表示字节输入流的所有类的超类

​ OutputStream:这个抽象类是表示字节输出流的所有类的超类

​ 子类名称特点:子类名称都是以其父类名作为子类的后缀

2、FileOutputStream:文件输出流用于将数据写入File

​ FileOutputStream(String name):创建文件输出流已指定的名称写入文件

3、使用字节输出流写数据的步骤:

​ 创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流对象指向文件)

​ 调用字节输出流对象写数据方法

​ 释放资源("关闭此文件输出流"并"释放与此流相关联的任何系统资源")

参考代码:

package com.itheima_01;
/*
FileOutputStream:文件输出流用于将数据写入File
FileOutputStream(String name):创建文件输出流已指定的名称写入文件
*/
//导包
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutStreamDemo01{
public static void main(String[] args) throws IOException{//构造函数所需是其子类
//创建字节输出流对象
//FileOutputStream(String name):创建文件输出流已指定的名称写入文件
FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt");
/*
这个创建字节流对象的构造方法做了:
1、调用系统功能创建了文件
2、创建了字节输出流对象
3、让字节输出流对象指向创建好的文件
*/
//void write(int b) 将指定的字节写入此文件输出流
fos.write(97); //转换为对应的ASCLL码值
//通过ASCLL码写出字符97
// fos.write(57);
// fos.write(55);
//最后都要释放资源
//void close() "关闭此文件输出流"并"释放与此流相关联的任何系统资源"
fos.close(); //如果不进行此操作,程序结束前文件将会被占用
}
}
3、字节流写数据的三种方式:
方法名 说明
void write(int b) 将指定的字节写入此文件输出流,一次写一字节数据
void write(byte[] b) 将b.length字节从指定的字节数组写入此文件输出流,一次写一个字节数组数据
void write(byte[] b, int off, int len) 将len字节从指定的字节数组开始,从偏移量off开始写入此文件输入流,一次写一字节数组的部分数据
package com.itheima_01;
/*
构造方法(这两个方法其实一样的,只是一个帮我们写了字符到File对象的转换而已):
FileOutputStream(String name) 创建文件输出流以指定的名称写入文件
FileOutputStream(File file) 创建文件输出流以写入由指定的 File对象表示的文件
写数据的三种方式:
void write(int b)
将指定的字节写入此文件输出流,一次写一字节数据
void write(byte[] b)
将b.length字节从指定的字节数组写入此文件输出流,一次写一个字节数组数据
void write(byte[] b, int off, int len)
将len字节从指定的字节数组开始,从偏移量off开始写入此文件输入流,一次写一字节数组的部分数据
*/
//导包
import java.io.FileOutputStream;
import java.io.File;
import java.io.IOException;
public class FileOutputStreamDemo02 {
public static void main(String[] args) throws IOException {
//一、构造方法
//FileOutputStream(String name) 创建文件输出流以指定的名称写入文件
FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt");
//底层做了以下操作
/* //new File(name)
// FileOutputStream fos = new FileOutputStream(new File("myByteStream\\fos.txt"));*/
// //FileOutputStream(File file) 创建文件输出流以写入由指定的 File对象表示的文件
//// File file = new File("myByteStream\\fos.txt");
//// FileOutputStream fos2 = new FileOutputStream(file);
// FileOutputStream fos2 = new FileOutputStream(new File("myByteStream\\fos.txt"));
//二、三种写数据的方式
//void write(int b) 将指定的字节写入此文件输出流,一次写一字节数据
// fos.write(97);
// fos.write(98);
// fos.write(99);
// fos.write(100);
// fos.write(101);
//void write(byte[] b) 将b.length字节从指定的字节数组写入此文件输出流,一次写一个字节数组数据
//byte[] bytes = {97, 98, 99, 100, 101, 102};
//byte[] getBytes​() 使用平台的默认字符集将该 String编码为一系列字节,将结果存储到新的字节数组中
byte[] bytes = "abcde".getBytes();
// fos.write(bytes);
//void write(byte[] b, int off, int len) 将len字节从指定的字节数组开始,从偏移量off开始写入此文件输入流,一次写一字节数组的部分数据
// fos.write(bytes, 0, bytes.length);
fos.write(bytes, 1, 3);
//!!!释放资源
fos.close();
}
}
4、字节流写数据的两个小问题:

1、字节流写数据如何实现换行呢?

​ 写完数据后,加换行符

​ window: \r\n
​ linux: \n
​ mac: \r

2、字节流写数据如何实现追加写入呢?

public FileOutputStream(String name,boolean append)
//创建文件输出流以指定的名称写入文件
//如果第二个参数为true ,则字节将写入文件的末尾而不是开头

参考代码:

package com.itheima_01;
/*
字节流写数据的两个小问题:
1、字节流写数据如何实现换行呢?
window: \r\n
linux: \n
mac: \r
2、字节流写数据如何实现追加写入呢?
public FileOutputStream(String name,boolean append)
创建文件输出流以指定的名称写入文件
如果第二个参数为true ,则字节将写入文件的末尾而不是开头
*/
//导包
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamDemo03 {
public static void main(String[] args) throws IOException {
//创建字节流对象
// FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt");
FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt", true);
//写数据 //hellohellohellohellohellohellohellohellohellohello
for(int i = 0; i < 10; i++){
fos.write("hello".getBytes());
//window换行
fos.write("\r\n".getBytes());
}
//释放资源
fos.close();
}
}
5、字节流写数据加异常处理:

finally:在异常处理是提供finally块来执行所有清除操作。比如说IO流中的释放资源

特点:被finally控制的语句一定会执行,除非JVM退出

//finall配合try...catch语句使用
try{
//可能出现异常的代码
}
catch(异常类名 变量名){
//异常处理的代码
}
finally{
//执行所有清除操作
}

参考代码:

package com.itheima_01;
/*
字节流写数据加入异常处理
*/
//导包
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamDemo04 {
public static void main(String[] args){
// try{
// FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt");
// fos.write("hello".getBytes());
// fos.close();
// }
// catch(IOException e){
// e.printStackTrace();
// }
FileOutputStream fos = null; //在外部对变量进行声明,使得所有语句块可以访问,赋初值防止为赋值操作
try{
// fos = new FileOutputStream("F:\\myByteStream\\fos.txt");
fos = new FileOutputStream("myByteStream\\fos.txt");
fos.write("hello".getBytes());
}
catch(IOException e){
e.printStackTrace();
}
finally{ //使用finallyl来保证 fos.close()语句得以执行(释放文件资源)
if(fos != null){ //防止初始化异常时(路径不存在)fos为空值时空指针访问异常
try{
fos.close();
}
catch(IOException e){
e.printStackTrace();
}
}
}
}
}
6、字节流读数据(一次读一个字节数据):

1、需求:把文件fos.txt中的内容读取出来在控制台输出

2、FileInputStream:从文件系统中的文件获取输入字节

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

3、使用字节流输入流读数据的步骤:

​ 创建字节流输入流对象

​ 调用字节流对象的读数据方法

​ 释放资源

参考代码:

package com.itheima_02;
/*
1、需求:
把文件fos.txt中的内容读取出来在控制台输出
2、FileInputStream:从文件系统中的文件获取输入字节
FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名
3、使用字节流输入流读数据的步骤:
创建字节流输入流对象
调用字节流对象的读数据方法
释放资源
*/
//导包
import java.io.FileInputStream;
import java.io.IOException;
public class FileInputStreamDemo01 {
public static void main(String[] args) throws IOException {
//创建字节流输入流对象
FileInputStream fis = new FileInputStream("myByteStream\\fos.txt");
//调用字节流对象的读数据方法
// //int read() 从该输入流读取一个字节的数据
// int b1 = fis.read();
// int b2 = fis.read();
// int b3 = fis.read();
// int b4 = fis.read();
// int b5 = fis.read();
// int b6 = fis.read();
// int b7 = fis.read();
//
// System.out.println((char) b1); //强制转换一下看到清楚些
// System.out.println(b2);
// System.out.println(b3);
// System.out.println(b4);
// System.out.println((char) b5);
// System.out.println(b6); //到达文件末尾为-1
// System.out.println(b7);
//for循环
// for(int b = fis.read(); b != -1; b = fis.read()){
// System.out.print((char) b);
// }
//while循环
// int b = fis.read();
// while(b != -1){
// System.out.print((char) b);
// b = fis.read();
// }
//优化while循环
/*
fis.read(); 读数据
b = fis.read(); 把读取到的数据赋值给b
b != -1; 判断读取到的数据是否是-1
*/
int b;
while((b = fis.read())!= -1){
//而且无需人为处理换行
System.out.print((char)b);
}
//释放资源
fis.close();
}
}
7、案例:复制文本文件:

需求:把D盘下的txt文件内容复制到模块目录下的txt文件中

分析:

​ 复制文本文件,其实就是把文本文件的内容从一个文件中读取出来(数据源),然后写入到另一个文件中(目的地)

​ 数据源:盘符中一个目录下的文件

​ 目的地:本模块下的文件

​ 释放资源

package com.itheima_02;
/*
需求:把D盘下的txt文件内容复制到模块目录下的txt文件中
分析:
复制文本文件,其实就是把文本文件的内容从一个文件中读取出来(数据源),然后写入到另一个文件中(目的地)
数据源:盘符中一个目录下的文件
目的地:本模块下的文件
释放资源
*/
//导包
import java.io.FileInputStream;
import java.io.IOException;
import java.io.FileOutputStream;
public class CopyTxtDemo {
public static void main(String[] args) throws IOException {
//根据数据源创建字节输入流对象
FileInputStream fis = new FileInputStream("D:\\itcast\\美文.txt");
//根据目的地创建字节输出流对象
FileOutputStream fos = new FileOutputStream("myByteStream\\美文.txt");
//读写数据,复制文本文件(一次读取一个字节,一次写入一个字节)
int by;
while((by = fis.read())!= -1){
fos.write(by);
}
//释放资源
fos.close();
fis.close();
}
}
8、字节流读数据(一次读一个字节数组数据):

1、需求:把文件fos.txt中的内容读取出来在控制台输出

2、使用字节输入流读数据的步骤:

​ 创建字节输入流对象

​ 调用字节流输入对象的读取数据方法

​ 释放资源

3、int read(byte[] b)方法:

​ 参数是传入一个字节数组用于接收数据

​ 返回值是读取到这个字节数组中的大小

参考代码:

package com.itheima_02;
/*
需求:把文件fos.txt中的内容读取出来在控制台输出
使用字节输入流读数据的步骤:
创建字节输入流对象
调用字节流输入对象的读取数据方法
释放资源
*/
//导包
import java.io.FileInputStream;
import java.io.IOException;
public class FileInputStreamDemo02 {
public static void main(String[] args) throws IOException {
//创建字节输入流对象
FileInputStream fis = new FileInputStream("myByteStream\\fos.txt");
//调用字节流输入对象的读取数据的方法
//int read(byte[] b) 从该输入流读取最多 b.length个字节的数据到一个字节数组
// byte[] bys = new byte[5];
/*//第一次读取数据
int len = fis.read(bys);
System.out.println(len);
//String(byte[] bytes)
//System.out.println(new String(bys)); //使用匿名对象来创建一个String对象
System.out.println(new String(bys, 0, len));
//第二次读取数据
len = fis.read(bys);
System.out.println(len);
//System.out.println(new String(bys));
System.out.println(new String(bys, 0, len));
*//*
hello\r\n
world\r\n
第一次读取:hello
第二次读取:\r\nwor
第三次读取:ld\r\nr
*//*
//第三次读取数据
//注意:容易在world后面不加换行而导致结果不同
len = fis.read(bys);
System.out.println(len);
//String(byte[] bytes, int offset, int length) 通过使用平台的默认字符集解码指定的字节子阵列来构造新的 String
System.out.println(new String(bys, 0 , len));
//再多读取两次
len = fis.read(bys);
System.out.println(len);
len = fis.read(bys);
System.out.println(len);*/
//循环改进
byte[] bys = new byte[1024]; //1024及其整数倍
int len;
while((len = fis.read(bys)) != -1){
System.out.println(new String(bys, 0, len));
System.out.println("-----");
}
//释放资源
fis.close();
}
}
9、案例:复制图片:

需求:把D盘下的图片复制到模块目录下

package com.itheima_02;
/*
需求:把盘符下的文件复制到模块下
思路:
1、根据数据源创建字节流输入流对象
2、根据目的地创建字节流输出流对象
3、读写数据,复制图片(一次读取一个字节数组,一次写入一个字节数组)
4、释放对象
*/
//导包
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyJpgDemo {
public static void main(String[] args) throws IOException {
//1、根据数据源创建字节输入流对象
FileInputStream fis = new FileInputStream("D:\\itcast\\图片.jpg");
//2、根据目的地创建字节流输出流对象
FileOutputStream fos = new FileOutputStream("myByteStream\\图片.jpg");
//3、读写文件,复制图片
byte[] bys = new byte[1024];
int len;
while((len = fis.read(bys)) != -1){
fos.write(bys, 0, len);
}
//4、释放对象
fis.close();
fos.close();
}
}
10、字节缓冲流(目的是缓冲一定数量的字节,再一次性的输入输出。提高读写数据效率):

字节缓冲流:

​ BufferedOutputStream:该类实现缓冲输出流。通过设置这样的输入流,应用程序可以向底层输出流写入字节,而不必为写入的每一个字节导致底层系统的调用

​ BufferedInputStream:创建BufferedInputStream将创建一个内部缓冲区数组。当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流重新填充,一次多个字节

构造方法:

​ 字节缓冲输出流:BufferedOutputStream(OutputStream out)

​ 字节缓冲输入流:BufferedInputStream(InputStream in)

为什么构造放方法中所需要的参数是字节流,而不是具体的文件或者路径呢?

​ 字节流缓冲区仅仅提供缓冲区,而真正的读写数据还得依靠基本的字节流对象进行操作

参考代码:

package com.itheima_03;
/*
测试字节缓冲流
构造方法:
字节缓冲输出流:BufferedOutputStream(OutputStream out)
字节缓冲输入流:BufferedInputStream(InputStream in)
*/
//导包
import java.io.BufferedOutputStream;
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class BufferedStreamDemo {
public static void main(String[] args) throws IOException{
//字节缓冲输出流:BufferedOutputStream(OutputStream out)
// FileOutputStream fos = new FileOutputStream("myByteStream\\bos.txt");
// BufferedOutputStream bos = new BufferedOutputStream(fos);
/*BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("myByteStream\\bos.txt"));
//写数据
bos.write("hello\r\n".getBytes());
bos.write("world\r\n".getBytes());
//释放资源
bos.close();*/
//字节缓冲输入流:BufferedInputStream(InputStream in)
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("myByteStream\\bos.txt"));
// //一次读取一个字节
// int b;
// while((b=bis.read()) != -1){
// System.out.print((char)b);
// }
//一次读一个字节数组
byte[] bys = new byte[1024];
int len;
while((len=bis.read(bys)) != -1){
System.out.println(new String(bys, 0, len));
}
//释放资源
bis.close();
}
}
11、案例:复制视频:

需求把盘符下的视频复制到模块目录下:

package com.itheima_03;
/*
使用字节流复制视频
比较四种方式的速度:
1、使用基本字节流进行复制(一次一字节):136360毫秒
2、使用基本字节流进行复制(一次一字节数组):204毫秒
3、使用字节缓冲流进行复制(一次一字节):203毫秒
4、使用字节缓冲流进行复制(一次一字节数组):47毫秒
*/
//导包
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyAviDemo {
public static void main(String[] args) throws IOException{
//记录开始时间
long startTime = System.currentTimeMillis();
/*//一、使用基本字节流进行复制
FileInputStream fis = new FileInputStream("D:\\itcast\\视频.avi");
FileOutputStream fos = new FileOutputStream("myByteStream\\视频.avi");
// //一次一字节 共耗时:136360毫秒
// int b;
// while((b = fis.read()) != -1){
// fos.write(b);
// }
//一次一个字节数组 共耗时:204毫秒
byte[] bys = new byte[1024];
int len;
while((len=fis.read(bys)) != -1){
fos.write(bys, 0, len);
}
//释放文件资源
fis.close();
fos.close();
*/
//二、使用字节缓冲流进行复制
//创建字节缓冲流输入流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\itcast\\视频.avi"));
//创建字节缓冲流输出流对象
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("myByteStream\\视频.avi"));
// //一次一字节 共耗时:203毫秒
// int b;
// while((b=bis.read()) != -1){
// bos.write(b);
// }
//一次一字节数组 共耗时:47毫秒
byte[] bys = new byte[1024];
int len;
while((len=bis.read(bys)) != -1){
bos.write(bys, 0, len);
}
//释放文件
bis.close();
bos.close();
//记录结束时间
long endTime = System.currentTimeMillis();
System.out.println("共耗时:" + (endTime - startTime) + "毫秒");
}
}

三、字符流

1、为什么会出现字符流:

由于字节流操作中文不是特别方便,所以Java就提供字符流

​ 字符流 = 字节流 + 编码表

用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,如何识别中文?

​ 汉字在存储时,无论先择哪种编码存储,第一个字节都是负数

参考代码:

package com.itheima_01;
/*
需求:字节流读文本文件数据
一个汉字编码:
如果是GBK编码,占用两个字节(一下使用的是GBK)
如果是UTF-8编码,占用三个字节
*/
//导包
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
public class FileInputStreamDemo {
public static void main(String[] args) throws IOException {
// FileInputStream fis = new FileInputStream("myCharStream\\abc.txt");
//
// int by;
// while((by=fis.read()) != -1){
// System.out.print((char) by);
// }
//
// fis.close();
//使用字节流复制中文文本文件为什么没有问题? 汉字字节编码带负数(再根据对应的编码形式进行拼接)
// String s = "abc"; //[97, 98, 99]
String s = "中国"; //(GBK):[-42, -48, -71, -6] (UTF-8):[-28, -72, -83, -27, -101, -67]
byte[] bys = s.getBytes("UTF-8");
System.out.println(Arrays.toString(bys));
}
}
2、编码表:

基础知识:

​ 计算机中存储的信息都是用二进制数表示的;我们屏幕上看到的英文、汉字等字符是二进制数转换之后的结果

按照某种规则,将字符存储到计算机中,称之为编码。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码。这里强调一下:按照某种编码格式存储就需要用此格式进行解码,这样才能显示正确文本字符。否则就会导致乱码现象

​ 字节编码:就是一套自然语言的字符与二进制数之间的对应规则(A, 65)

字符集:

​ 是系统支持的所有字符的集合,包括各国文字、标点符号、图形符号、数字等

​ 计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。

​ 常见的字符集有ASCLL字符集、GBXXX字符集、Unicode字符集等

ASCLL字符集:

​ ASCLL(American Standard Code for Information Interchange美国信息交换标准代码):是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符,阿拉伯数字和西文符号)

​ 基本的ASCLL字符集,使用7位表示一个字符,共128字符。ASCLL的扩展字符集使用8位标识一个字符,共256字符,方便支持欧洲常用字符,是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等

GBXXX字符集:

GB2312:简体中文码表。一个小于127的字符的意义与原来相同,但是两个大于127的字符连在一起时,就表示一个汉字,这样的大约可以组合了包含7000多个简体汉字,此外数学符号、罗马希腊的字母、日文的假名等都编进去了,连在ASCLL里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的”全角字符“,而原来的127号以下的那些就叫”半角“字符了

GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等

GB18030:最新的中文码表。收录汉字70244个,采用多字节编码,每个字可以由1个、2个或4个字节组成。支持中国国内少数民族的文字,同时支持中国国内少数名族的文字,同时支持繁体汉字以及日韩汉字等

Unicode字符集:

Unicode字符集:为了表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国码。它最多使用4字节的数字来表达每个字母、符号,或者文字。由三种编码方案,UTF-8、UTF-16和UTF32。最常用的UTF-8编码

UTF-8编码:可以用来表示Unicode标准中的任意字符,它是电子邮件、网页及其它存储或传送文字的应用中,优先采用的编码。互联网工程小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四字节为每个字符编码

编码规则:

​ 128US-ASCLL字符,只需一个字节编码

​ 拉丁文等字符,需要两个字节编码

​ 大部分常用字(含中文),使用三个字节编码

​ 其它极少使用的Unicode辅助字符,使用四字节编码

小结:按照某种编码格式存储就需要用此格式进行解码,这样才能显示正确文本字符。否则就会导致乱码现象

package com.itheima_01;
/*
需求:字节流读文本文件数据
一个汉字编码:
如果是GBK编码,占用两个字节(一下使用的是GBK)
如果是UTF-8编码,占用三个字节
*/
//导包
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
public class FileInputStreamDemo {
public static void main(String[] args) throws IOException {
// FileInputStream fis = new FileInputStream("myCharStream\\abc.txt");
//
// int by;
// while((by=fis.read()) != -1){
// System.out.print((char) by);
// }
//
// fis.close();
//使用字节流复制中文文本文件为什么没有问题? 汉字字节编码带负数(再根据对应的编码形式进行拼接)
// String s = "abc"; //[97, 98, 99]
String s = "中国"; //(GBK):[-42, -48, -71, -6] (UTF-8):[-28, -72, -83, -27, -101, -67]
byte[] bys = s.getBytes("UTF-8");
System.out.println(Arrays.toString(bys));
}
}
3、字符串中的编码解码问题:

1、编码:

​ byte[] getBytes():使用平台默认的字符集将该String编码为一系列字节,将结果存储到新的字节数组中

​ byte[] getBytes(String charsetName):使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中

2、解码:

​ String(byte[] bytes):通过使用平台道德默认字符集解码指定的字节数组来构造新的String

​ String(byte[] bytes, String charsetName):通过指定的字符集解码指定的字节数组来构造新的String

参考代码:

package com.itheima_01;
/*
1、编码:
byte[] getBytes():使用平台默认的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
byte[] getBytes(String charsetName):使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
2、解码:
String(byte[] bytes):通过使用平台的默认字符集解码指定的字节数组来构造新的String
String(byte[] bytes, String charsetName):通过指定的字符集解码指定的字节数组来构造新的String
*/
//导包
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class StringDemo {
public static void main(String[] args) throws UnsupportedEncodingException {
//定义一个中文字符
String s = "中国";
//编码
//byte[] getBytes():使用平台默认的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
// byte[] bys = s.getBytes();
// System.out.println(Arrays.toString(bys)); //[-42, -48, -71, -6]
//byte[] getBytes(String charsetName):使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
byte[] bys2 = s.getBytes("UTF-8");
System.out.println(Arrays.toString(bys2)); //[-28, -72, -83, -27, -101, -67]
//解码:
//String(byte[] bytes):通过使用平台的默认字符集解码指定的字节数组来构造新的String
// String ss = new String(bys);
//String(byte[] bytes, String charsetName):通过指定的字符集解码指定的字节数组来构造新的String
// String ss = new String(bys, "UTF-8"); //解码与编码选择的字符集不同
String ss = new String(bys2, "UTF-8");
System.out.println(ss);
}
}
3、字符流中的编码解码问题:

1、字符流抽象基类:

Reader:字符输入流的抽象类

Writer:字符输出流的抽象类

2、字符流中和编码解码问题相关的两个类(转换流是字节流与字符流间的桥梁):

InputStreamReader

OutputStreamWriter

参考代码:

package com.itheima_02;
/*
以下使用的都是默认GBK编码形式
1、字符流抽象基类:
Reader:字符输入流的抽象类
Writer:字符输出流的抽象类
2、字符流中和编码解码问题相关的两个类:
InputStreamReader
OutputStreamWriter
*/
//导包
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.FileInputStream;
public class ConversionStreamDemo {
public static void main(String[] args) throws IOException {
// OutputStreamWriter(OutputStream out) 创建一个使用默认字符编码的OutputStreamWriter
// OutputStreamWriter(OutputStream out, String charsetName) 创建一个使用命名字符集的OutputStreamWriter
// //使用默认字符集(GBK)
// OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("myCharStream\\osw.txt"));
//使用自定义字符集
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("myCharStream\\osw.txt"), "UTF-8");
// OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("myCharStream\\osw.txt"), "GBK");
osw.write("中国");
//释放文件
osw.close();
// InputStreamReader(InputStream in) 创建一个使用默认字符集的InputStreamReader
// InputStreamReader(InputStream in, String charsetName) 创建一个使用命名字符集的InputStreamReader
InputStreamReader isr = new InputStreamReader(new FileInputStream("myCharStream\\osw.txt"), "UTF-8");
//一次读取一个字符流数据
int by;
while((by=isr.read()) != -1) {
System.out.print((char) by);
}
//释放文件
isr.close();
}
}
4、字符流些数据的5种方式:
方法名 说明
void write(int c) 写一个字符
void write(char[] cbuf) 写一个字符数组
void write(char[] cbuf, int off, int len) 写入字符数组的一部分
void write(String str) 写入一个字符串
void write(String str, int off, int len) 写一个字符串的一部分

字符流写数据会先放在缓存区,不会立即写入,需要通过刷新流或者关闭流来写入。

方法名 说明
flush() 刷新流,还可以继续写数据
close() 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭就不能再写数据

参考代码:

package com.itheima_02;
/*
void write(int c) 写一个字符
void write(char[] cbuf) 写一个字符数组
void write(char[] cbuf, int off, int len) 写入字符数组的一部分
void write(String str) 写入一个字符串
void write(String str, int off, int len) 写一个字符串的一部分
*/
//导包
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.FileOutputStream;
public class OutputStreamWriterDemo{
public static void main(String[] args) throws IOException {
//使用默认字符集创建一个字符编码为GBK的字符输出流对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("myCharStream\\osw.txt"));
//一、void write(int c) 写一个字符
// osw.write(97);
//void flush() 刷新流 把缓存区的数据输入到文件中
// osw.flush();
//
// osw.write(98);
// osw.flush(); //仅仅刷新流的缓冲,并不会关闭文件
//
// osw.write(99);
//二、void write(char[] cbuf) 写一个字符数组
char[] chs = {'a', 'b', 'c', 'd', 'e'};
// osw.write(chs);
// osw.flush();
//三、void write(char[] cbuf, int off, int len) 写入字符数组的一部分
// osw.write(chs, 0, 5);
// osw.write(chs, 1, 3);
//四、void write(String str) 写入一个字符串
// osw.write("abcde");
//五、void write(String str, int off, int len) 写一个字符串的一部分
// osw.write("abcde", 0, "abcde".length());
osw.write("abcde", 1, 3);
//释放资源 void close() 关闭流,先刷新
osw.close();
//Exception in thread "main" java.io.IOException: Stream closed
// osw.write(100);
}
}
5、字符流读数据的2种方式:
方法名 说明
int read() 一次读一个字符数据
int read(char[] cbuf) 一次读一个字符数组数据

参考代码:

package com.itheima_02;
/*
测试字符流读取数据
int read() 一次读一个字符数据
int read(char[] cbuf) 一次读一个字符数组数据
*/
//导包
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.FileInputStream;
public class InputStreamReaderDemo {
public static void main(String[] args) throws IOException {
//使用默认编码形式创建字符流输入流对象
InputStreamReader isr = new InputStreamReader(new FileInputStream("myCharStream\\ConversionStream.txt"));
//int read() 一次读一个字符数据
// int ch;
// while((b=isr.read()) != -1){
// System.out.print((char) ch);
// }
//int read(char[] cbuf) 一次读一个字符数组数据
char[] chars = new char[1024];
int len;
while((len=isr.read(chars)) != -1){
System.out.print(new String(chars, 0, len));
}
//释放文件
isr.close();
}
}
6、案例:复制Java文件:

需求:包模块目录下的Java文件复制到模块下另一个Java文件中

package com.itheima_02;
/*
将模块下的java文件复制到模块下另一个文件
*/
//导包
import java.io.InputStreamReader;
import java.io.FileInputStream;
import java.io.OutputStreamWriter;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyJavaDemo {
public static void main(String[] args) throws IOException{
//创建字符流输入流对象
InputStreamReader isr = new InputStreamReader(new FileInputStream("myCharStream//ConversionStreamDemo.java"));
//创建字符流输入对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("myCharStream//Copy.java"));
//读写文件,复制java文件
//一次读写一个字符数据
// int ch;
// while((ch=isr.read()) != -1){
// osw.write(ch);
// }
//一次读写一个字符数组
char[] chars = new char[1024];
int len;
while((len=isr.read(chars)) != -1){
osw.write(chars, 0, len);
}
//释放文件
isr.close();
osw.close();
}
}
7、案例:复制Java文件(改进版):

转换流的名字太长,但是我们常见的操作都是按照本地默认的编码实现的,所以,为了简化书写,转换流提供了对应的子类

​ FileReader:用于读取字符文件的便捷类

​ FileReader(String fileName)

​ FileWriter:用于写入文件的便捷类

​ FileWriter(String fileName)

数据源和目的地的分析:

​ 数据源:模块下的java文件---读数据---Reader---InputStreamReader---FileReader

​ 目的地:模块下的另一个java数据---写数据---Writer---OutputStreamWriter---FileWriter

参考代码:

package com.itheima_02;
/*
前提条件:为涉及到编码解码问题
*/
//导包
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyJavaDemo02 {
public static void main(String[] args) throws IOException {
//定义字符便捷流输入流对象
FileReader fr = new FileReader("myCharStream\\ConversionStreamDemo.java");
//定义字符便捷流输出流对象
FileWriter fw = new FileWriter("myCharStream\\Copy2.java");
//读写文件,复制java文件
//一次读取一个字节
// int by;
// while((by=fr.read()) != -1){
// fw.write(by);
// }
//一次读取一个字符数组
char[] chars = new char[1024];
int len;
while((len=fr.read(chars)) != -1){
fw.write(chars, 0, len);
}
//释放文件
fr.close();
fw.close();
}
}
8、字符缓冲流:

1、字符缓冲流:

​ BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接收默认大小。默认值足够大,可以用于大多数用途

​ BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小,默认值足够大,可用于大多数用途

2、构造方法:

BufferedWriter(Writer out)

BufferedReader(Reader in)

参考代码:

package com.itheima_03;
/*
1、字符缓冲流:
BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接收默认大小。默认值足够大,可以用于大多数用途
BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小,默认值足够大,可用于大多数用途
2、构造方法:
BufferedWriter(Writer out)
BufferedReader(Reader in)
*/
//导包
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class BufferedStreamDemo {
public static void main(String[] args) throws IOException{
//一、写数据
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("myCharStream\\bw.txt")));
// bw.write("hello");
// bw.write("\n");
// bw.write("world");
// bw.flush();
// //释放文件
// bw.close();
//二、读数据
//创建字符缓冲流输入流对象用于读取数据 (也可以使用简化流对象)
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("myCharStream\\Copy.java")));
//使用单个字节读取对象
// int by;
// while((by=br.read()) != -1){
// System.out.print((char) by);
// }
//使用字节数组读取对象
char[] chars = new char[1024];
int len;
while((len=br.read(chars)) != -1){
System.out.print(new String(chars, 0, len));
}
//释放文件
br.close();
}
}
9、案例:复制Java文件(字符缓冲流改进版):
package com.itheima_03;
/*
使用字符缓冲流复制java文件
*/
//导包
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class CopyJavaDemo {
public static void main(String[] args) throws IOException{
//创建字符缓冲流输入流对象
BufferedReader br = new BufferedReader(new FileReader("myCharStream\\ConversionStreamDemo.java"));
//创建字符缓冲流输出流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\Copy.java"));
//文件输出输入,文件复制
//一次读取一个字符
// int ch;
// while((ch=br.read()) != -1){
// bw.write((char) ch);
// }
//一次读取一个字符数组
char[] chars = new char[1024];
int len;
while((len=br.read(chars)) != -1){
bw.write(chars, 0, len);
}
//释放文件
br.close();
bw.close();
}
}
10、字符缓冲流特有功能:

BufferedWrite:

​ void newLine():写一行行的分隔符,行分隔符字符串系统属性自定义

BufferedReader:

​ public String readLine():读一行文字,结果包含行的内容的字符串,不包含任何行终止字符,如果流的结尾已经达到,则为null

参考代码:

package com.itheima_03;
/*
BufferedWrite:
void newLine():写一行行的分隔符,行分隔符字符串系统属性自定义
BufferedReader:
public String readLine():读一行文字,结果包含行的内容的字符串,不包含任何行终止字符,如果流的结尾已经达到,则为null
*/
//导包
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedStreamDemo02{
public static void main(String[] args) throws IOException{
/*//创建字符缓冲输入流
BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\bw.txt"));
//写数据
for(int i = 0; i < 10; i++){
//写数据
bw.write("hello" + i);
// bw.write("\r\n"); //只适用于Window操作系统
//写换行符号
//void newLine():写一行行的分隔符,行分隔符字符串系统属性自定义
bw.newLine();
//刷新
bw.flush();
}
//释放资源
bw.close();*/
//创建字符缓冲输入流对象
BufferedReader br = new BufferedReader(new FileReader("myCharStream\\bw.txt"));
//public String readLine():读一行文字,结果包含行的内容的字符串,不包含任何行终止字符,如果流的结尾已经达到,则为null
//注意:此处换行是因为使用的println()
// //第一次读取数据
// String line = br.readLine();
// System.out.println(line);
//
// //第二次读取数据
// line = br.readLine();
// System.out.println(line);
//
// //再多读两次
// line = br.readLine();
// System.out.println(line);
//
// line = br.readLine();
// System.out.println(line);
String line;
while((line=br.readLine()) != null){
System.out.println(line);
}
//释放资源
br.close();
}
}
11、案例:复制java文件(字符缓冲流特有功能改进版):
package com.itheima_03;
/*
复制java文件
*/
//导包
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class CopyJavaDemo02 {
public static void main(String[] args) throws IOException{
//创建字符缓冲输入流对象
BufferedReader br = new BufferedReader(new FileReader("myCharStream\\ConversionStreamDemo.java"));
//创建字符缓冲输出流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\Copy.java"));
//文件读写,文件复制
//使用BufferedReader 和 BufferedWriter中特有的方法
String line;
while((line=br.readLine()) != null){ //依次获取每一个行字符
//使用字符串形式写入
bw.write(line);
//换行
bw.newLine();
//刷新
bw.flush();
}
//释放文件
br.close();
bw.close();
}
}
12、IO流小结:

字节流

字节输入流:InputStream

字节输出流:OutputStream

FileInputStream

BufferedInputStream

FileOutputStream

BufferedOutputStream

​ 小结:字节流可以复制任意文件数据,有四种方式一般采用字节缓冲流一次读写一个字节数组的方式

字符流

字符输入流Reader

字符输出流Writer

InputStreamReader

BufferedReader

OutputStreamWriter

BufferedWriter

FileReader

FileWriter

​ 小结:字符流只能复制文本数据,有五种方式,一般采用字符缓冲流的特有功能

13、案例:集合到文件:

需求:把ArrayList集合中的字符串数据写入到文本文件中。要求:每一个字符串元素作为文件中的一行数据

参考代码(也可以使用BufferedWriter + 简化的字符输出流子类FileWriter):

package com.itheima_04;
/*
需求:把ArrayList集合中的字符串数据写入到文本文件中。要求:每一个字符串元素作为文件中的一行数据
*/
//导包
import java.util.ArrayList;
import java.io.OutputStreamWriter;
import java.io.FileOutputStream;
import java.io.IOException;
public class ArrayListToTxtDemo {
public static void main(String[] args) throws IOException{
//创建ArrayList集合对象
ArrayList<String> arrayList = new ArrayList<String>();
//添加数据
arrayList.add("hello");
arrayList.add("world");
arrayList.add("java");
//创建字符转换输出流对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("myCharStream\\ArrayList.txt"));
//遍历集合,并将字符元素写入文件中
for(int i = 0; i < arrayList.size(); i++){
osw.write(arrayList.get(i));
//添加对应系统的换行符
osw.write("\r\n");
}
//释放文件资源
osw.close();
}
}
14、案例:文件到集合:

需求:把文本文件中的数据读取到集合中,并遍历集合,要求:文件中每一行数据是一个集合元素

参考代码(可以使用字符转换输入流 [使用一次读一个字符数组,再用字符串类型构造方法来转换]+ 文件字节输入流来实现):

package com.itheima_04;
/*
需求:把文本文件中的数据读取到集合中,并遍历集合,要求:文件中每一行数据是一个集合元素
*/
//导包
import java.util.ArrayList;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class TxtToArrayListDemo {
public static void main(String[] args) throws IOException{
//创建字符缓冲输出流对象
BufferedReader br = new BufferedReader(new FileReader("myCharStream\\ArrayList.txt"));
//创建ArrayList集合对象
ArrayList<String> arrayList = new ArrayList<String>();
//读取文件中的数据并存入集合中
String line;
while((line=br.readLine()) != null){
arrayList.add(line);
}
//释放文件
br.close();
//遍历集合查看数据
for(String s : arrayList){
System.out.println(s);
}
}
}

使用字符转换流的参考代码(强行写的,有待考证):

package com.itheima_04;
import java.io.InputStreamReader;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.io.IOException;
public class TxtToArrayListDemo02 {
public static void main(String[] args) throws IOException{
InputStreamReader isr = new InputStreamReader(new FileInputStream("myCharStream\\ArrayList.txt"));
ArrayList<String> a = new ArrayList<String>();
char[] chars = new char[1024];
int len;
int left = 0; //用一个整型变量来记录左边界
while((len=isr.read(chars)) != -1){
for(int i = 0; i < chars.length; i++){//判断是否为一行
if(chars[i] == '\r'){
String line = new String(chars, left, i + 2 - left);//第三个参数表示取几个
a.add(line); //将数据插入
left = i + 2; //刷新左边界
i++;
}
else if(chars[i] == 0){ //判断是否在文末
String line = new String(chars, left, i-1);
a.add(line);
break; //直接结束
}
}
// for(char c : chars){
// System.out.print(c);
// }
}
isr.close();
for(String s : a){
//此处无需println() 在读取过来的过程中就已经把换行字符存进去了
System.out.print(s);
}
System.out.println();
System.out.println(a.size());
}
}
15、案例:点名器:

需求:我有一个文件里面存储了班级同学的姓名,每一个姓名占一行,要求通过程序实现随机点名器:

package com.itheima_04;
/*
需求:我有一个文件里面存储了班级同学的姓名,每一个姓名占一行,要求通过程序实现随机点名器:
*/
//导包
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
public class RollBookDemo {
public static void main(String[] args) throws IOException{
//创建一个字符缓冲流输入对象
BufferedReader br = new BufferedReader(new FileReader("myCharStream\\RollBook.txt"));
//创建一个ArrayList集合对象
ArrayList<String> arrayList = new ArrayList<String>();
//读取文件,将其中每一行的数据读取到集合元素中
String line;
while((line=br.readLine()) != null){
arrayList.add(line);
}
//释放文件
br.close();
Random random = new Random();
int index = random.nextInt(arrayList.size()); //索引0~n-1
System.out.println("抽中的同学是:" + arrayList.get(index));
}
}
16、案例:集合到文件(改进版)

需求:把ArrayList集合中的学生数据写入到文本文件中。要求:每一个学生对象的数据作为文件中的一行数据:

格式:学号,姓名,年龄,居住地 举例:001,张三,林青霞,30,西安

package com.itheima_05;
/*
定义一个简单的学生类
*/
public class Student {
private int sid;
private String name;
private int age;
private String address;
public Student(){}
public Student(int sid, String name, int age, String address){
this.sid = sid;
this.name = name;
this.age = age;
this.address = address;
}
public void setSid(int sid){
this.sid = sid;
}
public int getSid(){
return this.sid;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return this.age;
}
public void setAddress(String address){
this.address = address;
}
public String getAddress(){
return this.address;
}
//重写toString()方法
@Override
public String toString(){
return this.sid + "," +
this.name + "," +
this.age + "," +
this.address;
}
}
package com.itheima_05;
/*
需求:把ArrayList集合中的学生数据写入到文本文件中。要求:每一个学生对象的数据作为文件中的一行数据:
格式:学号,姓名,年龄,居住地 举例:001,张三,林青霞,30,西安
*/
//导包
import java.util.ArrayList;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class ArrayListToTxtDemo {
public static void main(String[] args) throws IOException{
//创建ArrayList集合对象
ArrayList<Student> arrayList = new ArrayList<Student>();
//创建学生对象
Student s1 = new Student(100, "张三", 18, "重庆");
Student s2 = new Student(101, "李四", 19, "四川");
Student s3 = new Student(102, "王五", 20, "山东");
Student s4 = new Student(103, "赵六", 21, "上海");
Student s5 = new Student(104, "小明", 20, "北京");
Student s6 = new Student(105, "小蓝", 20, "云南");
Student s7 = new Student(106, "小红", 20, "新疆");
Student s8 = new Student(107, "小白", 20, "天津");
//把学生数据放入到 ArrayList集合中
arrayList.add(s1);
arrayList.add(s2);
arrayList.add(s3);
arrayList.add(s4);
arrayList.add(s5);
arrayList.add(s6);
arrayList.add(s7);
arrayList.add(s8);
//创建文件缓冲输出流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\Student.txt"));
//将集合中的元素以字符串形式读取到 文件中
for(Student s : arrayList){
//写数据
// 使用重写的toString
// bw.write(s.toString()); //重写toString()方法来,写入文件
//使用StringBuilder
StringBuilder sb = new StringBuilder();
sb.append(s.getSid()).append(",").append(s.getName()).append(",").append(s.getAge()).append(",").append(s.getAddress());
//通过toString方法将 StringBuilder转换为String类型
bw.write(sb.toString());
//换行
bw.newLine();
//刷新
bw.flush();
}
//释放文件
bw.close();
}
}
17、案例:文件到集合(改进版):

需求:把文本文件中的数据读取到集合中,并遍历集合。要求:文件中每一行数据是一个学生对象的成员变量值

举例:001,张三,18,重庆

package com.itheima_05;
/*
需求:把文本文件中的数据读取到集合中,并遍历集合。要求:文件中每一行数据是一个学生对象的成员变量值
举例:001,张三,18,重庆
*/
//导包
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;
import java.io.IOException;
public class TxtToArrayListDemo {
public static void main(String[] args) throws IOException{
//创建文件缓冲输入流对象
BufferedReader br = new BufferedReader(new FileReader("myCharStream\\Student.txt"));
//创建ArrayList集合对象
ArrayList<Student> arrayList = new ArrayList<Student>();
//读取文件并存入集合中
String line; //用于接收读取的字符串
String[] strings; //定义一个字符串数组来,接收split()方法分割后的字符串数组
while((line=br.readLine()) != null){
Student stu = new Student();
strings = line.split(","); //以“,”字符分隔字符串
// for(int i = 0; i < strings.length; i++){
// System.out.println(strings[i]);
// }
stu.setSid(Integer.parseInt(strings[0])); //这里使用Integer包装类中的方法将String -> int
stu.setName(strings[1]);
stu.setAge(Integer.parseInt(strings[2]));
stu.setAddress(strings[3]);
arrayList.add(stu);
}
//释放文件
br.close();
//遍历集合
for(Student s : arrayList){
System.out.println(s.getSid() + "," + s.getName() + "," + s.getAge() + "," + s.getAddress());
}
}
}
18、案例:集合到文件(数据排序改进版):

需求:键盘录入5个学生信息(姓名、语文成绩、数学成绩、英语成绩),要求按照成绩总分从高到低写入文本文件

格式:姓名,语文成绩,数学成绩,英语成绩

package com.itheima_06;
/*
定义一个学生类
*/
public class Student implements Comparable<Student>{
//成员属性
private String name;
private int chScore;
private int mathScore;
private int EnglishScore;
//构造方法
public Student(){}
public Student(String name, int chScore, int mathScore, int EnglishScore){
this.name = name;
this.chScore = chScore;
this.mathScore = mathScore;
this.EnglishScore = EnglishScore;
}
//set/get
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getChScore() {
return chScore;
}
public void setChScore(int chScore) {
this.chScore = chScore;
}
public int getMathScore() {
return mathScore;
}
public void setMathScore(int mathScore) {
this.mathScore = mathScore;
}
public int getEnglishScore() {
return EnglishScore;
}
public void setEnglishScore(int englishScore) {
EnglishScore = englishScore;
}
//重写toString()
@Override
public String toString(){
return "Student{" +
"name='" + this.name + '\'' +
"chScore='" + this.chScore + '\'' +
"mathScore='" + this.mathScore + '\'' +
"EnglishScore='" + this.EnglishScore + '\'' +
"}";
}
//重写equals()
@Override
public boolean equals(Object o){
if(this == o){
return true;
}
if(o == null){
return false;
}
Student stu = (Student) o; //向下转型,进行比较
if((stu.chScore + stu.mathScore + stu.EnglishScore) == (this.chScore + this.mathScore + this.EnglishScore));{
return this.name != null? this.name.equals(stu.name) : stu.name == null;
}
}
//重写compareTo()
@Override
public int compareTo(Student stu){
//从大到小排序
int num = (stu.chScore + stu.mathScore + stu.EnglishScore) - (this.chScore + this.mathScore + this.EnglishScore);
//成绩相同时
int result = num == 0? this.name.compareTo(stu.name) : num;
return result;
}
}
package com.itheima_06;
/*
需求:键盘录入4个学生信息(姓名、语文成绩、数学成绩、英语成绩),要求按照成绩总分从高到低写入文本文件
格式:姓名,语文成绩,数学成绩,英语成绩
*/
//导包
import java.util.TreeSet;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class TreeSetToTxt {
public static void main(String[] args) throws IOException{
//创建TreeSet集合对象 (使用无参构造函数即自然排序)
TreeSet<Student> treeSet = new TreeSet<Student>();
//创建Student对象
Student s1 = new Student("张三", 99, 98, 99);
Student s2 = new Student("李四", 88, 78, 77);
Student s3 = new Student("王五", 69, 93, 96);
Student s4 = new Student("赵六", 80, 88, 86);
//将学生数据放到集合中
treeSet.add(s1);
treeSet.add(s2);
treeSet.add(s3);
treeSet.add(s4);
// //遍历测试查看
// for(Student stu : treeSet){
// System.out.println(stu.toString());
// }
//创建字符文件缓冲输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\TreeSet.txt"));
//写文件
for(Student stu : treeSet){
//定义一个StringBuilder对象
StringBuilder sb = new StringBuilder();
//按照格式拼接字符串
sb.append(stu.getName()).append(",").append(stu.getChScore()).append(",").append(stu.getMathScore()).append(",").append(stu.getEnglishScore());
//写入字符
bw.write(sb.toString());
//换行
bw.newLine();
//刷新
bw.flush();
}
//释放文件
bw.close();
}
}
19、案例:复制单级文件夹(内部只有文件的文件夹):

需求:把盘符中的文件夹复制到模块目录下:

package com.itheima_08;
/*
需求:把盘符单级文件夹中的文件复制到模块中
*/
//导包
import java.io.File;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyFolderDemo {
public static void main(String[] args) throws IOException {
//创建数据源目录File对象
File file = new File("D:\\test");
//获取数据源目录File对象名称(test)
String folderName = file.getName();
//创建目的地File对象
File mdFolderName = new File("myCharStream", folderName);
//判断目的地是否存在
if(!mdFolderName.exists()){
mdFolderName.mkdir();
}
//获取数据源目录下的所有File数组
File[] listFiles = file.listFiles();
//遍历File数组
for(File f : listFiles){
//获取数据源文件File对象的名称
String fileName = f.getName();
//创建目的地文件File对象
File destFile = new File(mdFolderName,fileName);
//复制文件
copyFile(f, destFile);
}
}
private static void copyFile(File f, File destFile) throws IOException{
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
byte[] bys = new byte[1024];
int len;
while((len=bis.read(bys)) != -1){
bos.write(bys, 0, len);
}
bis.close();
bos.close();
}
}
20、复制二级文件:
package com.itheima_08;
/*
从盘符复制一个二级文件到模块下
*/
//导包
import java.io.File;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyFolderDemo02 {
public static void main(String[] args) throws IOException{
//创建目标File对象
File sourceFolder = new File("D:\\itcast");
//获取来源文件名
String sourceName = sourceFolder.getName();
//创建目的地文件名
File destinationFolder = new File("myCharStream", sourceName);
//判断文件是否存在 并创建
if(!destinationFolder.exists()){
destinationFolder.mkdir();
}
//获取文件集合
File[] listFiles = sourceFolder.listFiles();
if(listFiles != null){
//遍历文件数组
for(File file : listFiles){
//获取文件名
String fileName = file.getName();
//System.out.println(fileName);
//判断是否为文件
if(file.isFile()){
//创建目的地文件
//第一个参数是一级目录前面已经创建,后面一个参数是这里面的文件抽象名
File destinationFile = new File(destinationFolder, fileName);
copyFile(file, destinationFile);
}
//判断是否为目录
if(file.isDirectory()){
//创建目的地文件夹
File destinationFolder2 = new File(destinationFolder, fileName);
if(!destinationFolder2.exists()){
destinationFolder2.mkdir();
}
//获取目录中的文件数组
File[] listFiles2 = file.listFiles();
if(listFiles2 != null){
//继续遍历
for(File file2 : listFiles2){
//获取文件名
String fileName2 = file2.getName();
//创建目的地文件
File destinationFile2 = new File(destinationFolder2, fileName2);
copyFile(file2, destinationFile2);
}
}
}
}
}
}
private static void copyFile(File file, File destinationFile) throws IOException{
//创建字节缓冲输入流对象 读取来源文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
//创建字节缓冲输出流对象 用来创建和写文件
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destinationFile));
//文件读写,复制文件
byte[] bys = new byte[1024];
int len;
while((len=bis.read(bys)) != -1){
bos.write(bys, 0, len);
}
//释放文件资源
bis.close();
bos.close();
}
}

推广:递归实现多级文件

package com.itheima_08;
/*
从盘符复制多级文件到模块下
*/
//导包
import java.io.File;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class CopyFolderDemo03 {
public static void main(String[] args) throws IOException{
//创建目标File对象
File sourceFolder = new File("D:\\itcast");
//创建目的地文件路径
File destinationFolder = new File("myCharStream\\");
copyFolder(sourceFolder, destinationFolder);
}
//定义一个方法来复制多级目录
private static void copyFolder(File sourceFolder, File destinationFolder) throws IOException{
//判断文件是否为目录
if(sourceFolder.isDirectory()) {
//拿到文件夹名
String folderName = sourceFolder.getName();
//创建目的地文件对象
File newFolder = new File(destinationFolder, folderName); //myCharStream\\itcast
if (!newFolder.exists()) {
newFolder.mkdir();
}
//获取目录下的文件数组
File[] listFiles = sourceFolder.listFiles();
if (listFiles != null) {
//遍历文件数组
for (File file : listFiles) {
//传递新的目标对象 和 目的地对象
copyFolder(file, newFolder);
}
}
}
else{ //是文件时
//获取文件名
String fileName = sourceFolder.getName();
//创建目的地文件对象
File destFile = new File(destinationFolder, fileName);
copyFile(sourceFolder, destFile);
}
}
//定义一个方法来复制文件
private static void copyFile(File sourceFolder, File destinationFile) throws IOException{
//创建字节缓冲输入流对象 读取来源文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceFolder));
//创建字节缓冲输出流对象 用来创建和写文件
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destinationFile));
//文件读写,复制文件
byte[] bys = new byte[1024];
int len;
while((len=bis.read(bys)) != -1){
bos.write(bys, 0, len);
}
//释放文件资源
bis.close();
bos.close();
}
}
21、复制文件的异常处理:

1、try...catch...finally的做法:

try{
//可能出现异常的代码
}catch(异常类名 变量名){
//异常的处理代码
}finally{
//执行所有清除操作
}

2、JDK7改进方案:

try(定义流对象){
//可能出现异常的代码
}catch(异常类名 变量名){
//异常的处理代码
}
//自动释放资源

3、JDK9改进方案

//定义输入流对象
//定义输出流对象
try(输入流对象;输出流对象){
//可能出现异常的代码
}catch(异常类名 变量名){
//异常的处理代码
}
//自动释放资源

参考代码:

package com.itheima_09;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/*
复制文件加入异常处理
*/
public class CopyFileDemo {
public static void main(String[] args){
}
//JDK9的改进方案
private static void method4() throws IOException{
FileReader fr = new FileReader("fr.txt");
FileWriter fw = new FileWriter("fw.txt");
try(fr;fw){
char[] chs = new char[1024];
int len;
while((len=fr.read(chs))!= -1){
fw.write(chs, 0, len);
}
}
}
//JDK7的改进方案
private static void method3(){
try(FileReader fr = new FileReader("fr.txt");
FileWriter fw = new FileWriter("fw.txt");){
char[] chs = new char[1024];
int len;
while((len=fr.read(chs))!= -1){
fw.write(chs, 0, len);
}
}
catch(IOException e){
e.printStackTrace();
}
}
//try...catch...finally
private static void method2(){
//声明并进行初始化
FileReader fr = null;
FileWriter fw = null;
try{
fr = new FileReader("fr.txt");
fw = new FileWriter("fw.txt");
char[] chs = new char[1024];
int len;
while((len=fr.read(chs))!= -1){
fw.write(chs, 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();
}
}
}
}
//抛出处理
private static void method1() throws IOException {
FileReader fr = new FileReader("fr.txt");
FileWriter fw = new FileWriter("fw.txt");
char[] chs = new char[1024];
int len;
while((len=fr.read(chs))!= -1){
fw.write(chs, 0, len);
}
fw.close();
fr.close();
}
}

四、特殊操作流

1、标准输入输出流:

1、System类中有两个静态的成员变量(InputStream与PrintStream是这两个流的类型):

​ public static final InputStream in:标准输入流。通常该流对于键盘输入或有主机环境或用户指定的另一个输入源

​ public static final PrintStream out:标准输出流。通常该流对应于显示输出或由主机环境或用户指定的另一个输出目标

2、标准输入流

自己实现键盘录入数据:

​ BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

Java中提供的工具类:

​ Scanner sc = new Scanner(System.in);

输入流参考代码:

package com.itheima_01;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.util.Scanner;
/*
public static final InputStream in:标准输入流。通常该流对于键盘输入或有主机环境或用户指定的另一个输入源
*/
public class SystemInDemo {
public static void main(String[] args) throws IOException {
// //public static final InputStream in
// InputStream is = System.in; //InputStream是字节输入流的抽象基类
//读取数据
//一次读取一个字节
// int by;
// while((by=is.read())!= -1){
// System.out.print((char) by);
// }
// //如何把字节流转换为字符流 用字符转换流来实现
// InputStreamReader isr = new InputStreamReader(is);
// //使用字符流一次读一行数据 使用字符缓冲输入流对象
// BufferedReader br = new BufferedReader(isr);
//优化对象创建
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//提示语句
System.out.println("请输入一个字符串:");
String line;
line = br.readLine();
System.out.println("你输入的数据是:" + line);
System.out.println("请输入一个整数:");
int i = Integer.parseInt(br.readLine()); //通过包装类Integer中的parseInt方法来实现String->int
System.out.println("你输入的整型数是:" + i);
//自己提供键盘录入数据,需要注意类型之间的转换,Java中有一个类可以提供帮助
//就是前面提供的Scanner类
Scanner sc = new Scanner(System.in);
}
}

3、标准输出流:

输出语句的本质:是标准的输出流

PrintStream ps = System.out

PrintStream类有的方法,System.out都可以使用

输出流参考代码:

package com.itheima_01;
import java.io.PrintStream;
/*
public static final PrintStream out:标准输出流。通常该流对应于显示输出或由主机环境或用户指定的另一个输出目标
*/
public class SystemOutDemo {
public static void main(String[] args){
//public static final PrintStream out
PrintStream ps = System.out;
//A PrintStream将功能添加到另一个输出流,即能够方便地打印各种数据值的表示
// ps.print("hello");
// ps.print(100);
//
// ps.println("hello");
// ps.println(100);
//System.out的本质是一个字节输出流
// System.out.print("hello");
// System.out.print(100);
System.out.println("hello");
System.out.println(100);
System.out.println(); //可以不带参数
// System.out.print() //需要有参数
}
}
2、打印流(只负责写数据):

1、打印流分类:

​ 字节打印流:PrintStream

​ 字符打印流:PrintWriter

2、打印流特点:

​ 只负责输出数据,不负责读取数据

​ 有自己特有的方法

3、字节打印流

​ PrintStream(String fileName):使用指定的文件名创建新的打印流

​ 使用继承父类的方法写数据时,查看的时候会转码;使用自己特有方法写数据,查看的数据照原样输出

参考代码:

package com.itheima_02;
/*
2、打印流特点:
只负责输出数据,不负责读取数据
有自己特有的方法
3、字节打印流
PrintStream(String fileName):使用指定的文件名创建新的打印流
*/
//导包
import java.io.IOException;
import java.io.PrintStream; //继承于FileOutputStream文件输出流,所以这里面的打印方法是往文件里面打印
public class PrintStreamDemo {
public static void main(String[] args) throws IOException {
//PrintStream(String fileName):使用指定的文件名创建新的打印流
PrintStream ps = new PrintStream("myOtherStream\\ps.txt");
//写数据
//字节输出流有的方法
// ps.write(97);
//使用特有方法来写数据
// ps.print(97);
// ps.println();
// ps.print(98);
ps.println(97);
ps.println(98);
//释放资源
ps.close();
}
}

4、字符打印流构造函数:

方法名 说明
PrintWriter(String fileName) 使用指定的文件名创建一个新的PrintWriter,而不需要自动执行刷新
PrintWriter(Writer out, boolean autoFlush) 创建一个新的PrintWriter:out:字符输出流,autoFlush:是否将println、print、format方法刷新输出缓冲区
package com.itheima_02;
/*
字符打印流的构造方法:
PrintWriter(String fileName) 使用指定的文件名创建一个新的PrintWriter 需要使用flush方法刷新缓冲区
PrintWriter(Writer out, boolean autoFlush) 创建一个新的PrintWriter:
out:字符输出流 autoFlush:是否将println、print、format方法刷新输出缓冲区
*/
//导包
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class PrintWriterDemo {
public static void main(String[] args) throws IOException {
// //一、PrintWriter(String fileName) 使用指定的文件名创建一个新的PrintWriter,而不需要自动执行刷新
// PrintWriter pw = new PrintWriter("myOtherStream\\pw.txt");
// pw.write("hello");
// pw.write("\r\n");
// pw.flush();
// pw.write("world");
// pw.write("\r\n");
// pw.flush();
// pw.println("hello");
// /*
// 对于write方法而言,相当于进行了写入加换行
// */
// pw.flush();
// pw.println("world");
// pw.flush();
//二、PrintWriter(Writer out, boolean autoFlush) 创建一个新的PrintWriter
PrintWriter pw = new PrintWriter(new FileWriter("myOtherStream\\pw.txt"), false);
//true时
// pw.println("hello");
// pw.println("world");
//false时
pw.println("java");
//释放资源
pw.close();
}
}

5、案例:复制Java文件(打印流改进版):

需求:把模块目录下的java文件复制到模块目录下的另一个java文件中

package com.itheima_02;
/*
需求:把模块目录下的java文件复制到模块目录下的另一个java文件中
使用打印流改进(注意打印流只能够写数据)
*/
//导包
import java.io.*;
public class CopyJavaDemo {
public static void main(String[] args) throws IOException{
//
// //根据数据源创建字符输入流对象
// BufferedReader br = new BufferedReader(new FileReader("myOtherStream\\PrintStreamDemo.java"));
//
// //创建目的地创建字符输出流对象
// BufferedWriter bw = new BufferedWriter(new FileWriter("myOtherStream\\Copy.java"));
//
// //读写数据,复制文件
// String line;
// while((line=br.readLine()) != null){
// bw.write(line);
// bw.newLine();
// bw.flush();
// }
//
// //释放资源
// bw.close();
// br.close();
//根据数据源创建字符输入流对象
BufferedReader br = new BufferedReader(new FileReader("myOtherStream\\\\PrintStreamDemo.java"));
//根据目的地创建字符输出流对象
PrintWriter pw = new PrintWriter(new FileWriter("myOtherStream\\Copy.java"));
String line;
while((line=br.readLine()) != null){
//直接实现了 使用字符流的写入和换行 (而是否能够实现flush就看打印流的构造函数参数)
pw.println(line);
}
//释放资源 释放资源前会刷新缓冲区所以即使未刷新也能够写入
br.close();
pw.close();
}
}
3、对象序列化流(将对象的信息写到文件中):

1、概述:

对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象

这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型,对象的数据和对象中存储的属性等信息字节序列写到文件之后,相当于文件中持久保存了一个对象的信息

反之,该字节序列还可以从文件中读取回来,重构对象,对他进行反序列化

2、内容:

要实现序列和反序列化就要使用对象序列化流和对象反序列化流:

​ 对象序列化流:ObjectOutputStream

​ 对象反序列化流:ObjectInputStream

3、对象序列化流(ObjectOutputStream)

将Java对象的原始数据类型和图形写入OutputStream。可以使用ObjectInputStream读取(重构)对象。可以通过使用流的文件来实现对象的持久存储。如果流是网络套接字流,则可以在另一个主机或另一个进程中重构对象

构造方法:

​ ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream

序列化对象的方法:

​ void writeObject(Object obj):将指定的对象写入ObjectOutputStream

注意:一个对象要想被序列化,该对象所属的类必须实现Serializable接口

Serializable是一个标记接口,实现该接口,不需要重写任何方法

参考代码:

package com.itheima_03;
/*
对象序列化流
构造方法:
ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream
序列化对象的方法:
void writeObject(Object obj):将指定的对象写入ObjectOutputStream
NotSerializableException
抛出一个实例需要一个Serializable接口。 序列化运行时或实例的类可能会抛出此异常。 参数应该是类的名称。
类的序列化由实现java.io.Serializable接口的类启用。 不实现此接口的类将不会使任何状态序列化或反序列化。
*/
//导包
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
public class ObjectOutputStreamDemo {
public static void main(String[] args) throws IOException{
//ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("myOtherStream\\oos.txt"));
//创建对象
Student s = new Student("张三", 20);
//void writeObject(Object obj):将指定的对象写入ObjectOutputStream
oos.writeObject(s);
//释放资源
oos.close();
}
}

4、对象的反序列化流(ObjectInputStream)

反序列化先前使用序列化流编写的原始数据和对象

构造方法:

​ ObjectInputStream(InputStream in):创建从指定的InputStream读取的ObjectInputStream

反序列化的方法:

​ Object readObject():从ObjectInputStream读取一个对象

参考代码:

package com.itheima_03;
/*
对象反序列化流
构造方法:
ObjectInputStream(InputStream in):创建从指定的InputStream读取的ObjectInputStream
反序列化的方法:
Object readObject():从ObjectInputStream读取一个对象
*/
//导包
import java.io.ObjectInputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class ObjectInputStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException{
//创建对象反序列化流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myOtherStream\\oos.txt"));
Object obj = ois.readObject(); //使用这个方法会存在一个异常
Student s = (Student) obj;
System.out.println(s.getName() + "," + s.getAge());
//释放资源
ois.close();
}
}

5、对象序列化流的问题?

用对象序列化流序列化一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出现问题呢?

​ 会出问题,抛出InvalidClassException异常

如果出问题了,如何解决?

​ 给对象所属的类一个serialVersionUID

​ private static final long serialVersionUID = 42L;

如果一个对象中的某个成员变量的值不想被序列化,又该如何事项呢?

​ 使用关键字transient关键字修饰该成员变量

参考代码:

package com.itheima_03;
/*
用对象序列化流序列化一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出现问题呢?
java.io.InvalidClassException: com.itheima_03.Student; local class incompatible:
stream classdesc serialVersionUID = -3743788623620386195,
local class serialVersionUID = -247282590948908173
当序列化运行时检测到类中的以下问题之一时抛出。
类的串行版本与从流中读取的类描述符的类型不匹配
该类包含未知的数据类型
该类没有可访问的无参数构造函数
如果出问题了,如何解决?
//直接给定了一个序列化ID值,使得类在被修改后也不会,再次计算出新的序列化ID值
private static final long serialVersionUID = 42L;
如果一个对象中的某个成员变量的值不想被序列化,又该如何事项呢?
使用transient关键字来修饰成员变量
*/
//导包
import java.io.*;
public class ObjectStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException{
// write();
read();
}
//反序列化
private static void read() throws IOException, ClassNotFoundException{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myOtherStream\\oos.txt"));
Object obj = ois.readObject();
Student s = (Student) obj;
System.out.println(s.getName() + "," + s.getAge());
ois.close();
}
//序列化
private static void write() throws IOException, ClassNotFoundException{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("myOtherStream\\oos.txt"));
Student s = new Student("李四", 19);
oos.writeObject(s);
oos.close();
}
}

四、Properties

1、Properties概述:

​ 是一个Map体系的集合类

​ Properties可以保存到流中或从流中加载

练习:Properties作为Map集合的使用

参考代码:

package com.itheima_04;
/*
Properties作为Map集合的使用
*/
//导包
import java.util.Properties;
import java.util.Set;
public class PropertiesDemo01 {
public static void main(String[] args){
//创建集合对象 不能使用泛型
// Properties<String,String> prop = new Properties<String, String>();
Properties properties = new Properties();
//存储元素
properties.put("001", "张三");
properties.put("002", "李四");
properties.put("003", "王五");
properties.put("004", "赵六");
//遍历集合
Set<Object> set = properties.keySet(); //获取键值集合
for(Object key : set){ //通过键值集合获取对应的值
Object value = properties.get(key);
System.out.println(key + "," + value);
}
}
}

2、Properties作为集合的特有方法:

方法名 说明
Object setProperty(String key,String value) 设置集合的键和值,都是String类型,底层调用Hashtable方法put[即添加数据]
String getProperty(String key) 使用此属性列表中指定的键值搜索属性
Set stringPropertyNames() 从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串
package com.itheima_04;
/*
Object setProperty(String key,String value) 设置集合的键和值,都是String类型,底层调用Hashtable方法put[即添加数据]
String getProperty(String key) 使用此属性列表中指定的键值搜索属性
Set<String> stringPropertyNames() 从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串
*/
//导包
import java.util.Properties;
import java.util.Set;
public class PropertiesDemo02 {
public static void main(String[] args) {
//创建集合对象
Properties properties = new Properties();
//Object setProperty(String key,String value) 设置集合的键和值,都是String类型,底层调用Hashtable方法put[即添加数据]
properties.setProperty("001", "张三");
/*
public synchronized Object setProperty(String key, String value) {
return put(key, value);
}
public synchronized Object put(Object key, Object value) {
return map.put(key, value);
}
*/
properties.setProperty("002", "李四");
properties.setProperty("003", "王五");
properties.setProperty("004", "赵六");
// //String getProperty(String key) 使用此属性列表中指定的键值搜索属性
// System.out.println(properties.getProperty("002"));
// System.out.println(properties.getProperty("007"));
//
// System.out.println(properties);
//Set<String> stringPropertyNames() 从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串
Set<String> names = properties.stringPropertyNames();
for(String key : names){
// System.out.println(key);
String value = properties.getProperty(key);
System.out.println(key + "," + value);
}
}
}

3、Properties和IO流结合的方法:

方法名 说明
void load(InputStream inStream) 从输入字节流读取属性列表(键和元素对)
void load(Reader reader) 从输入字符流读取属性列表(键和元素对)
void store(OutputStream out, String comments) 将此属性列表(键和元素对)写入此Properties表中,以适合于使用load(InputStream)方法的格式写入输出字节流
void store(Writer writer, String comments) 将此属性列表(键和元素对)写入此Proper表中,以适合使用load(Reader)方法的格式写入输出字符流

示例只涉及了与字符输入输出流的结合

package com.itheima_04;
/*
void load(InputStream inStream) 从输入字节流读取属性列表(键和元素对)
void load(Reader reader) 从输入字符流读取属性列表(键和元素对)
void store(OutputStream out, String comments) 将此属性列表(键和元素对)写入此Properties表中,以适合于使用load(InputStream)方法的格式写入输出字节流
void store(Writer writer, String comments) 将此属性列表(键和元素对)写入此Proper表中,以适合使用load(Reader)方法的格式写入输出字符流
第二个参数是描述信息
*/
//导包
import java.io.IOException;
import java.util.Properties;
import java.io.FileWriter;
import java.io.FileReader;
public class PropertiesDemo03 {
public static void main(String[] args) throws IOException{
//把集合中的数据保存到文件
// myStroe();
//把文件中的数据加载到集合
myLoad();
}
private static void myLoad() throws IOException{
Properties properties = new Properties();
//void load(Reader reader) 从输入字符流读取属性列表(键和元素对)
FileReader fr = new FileReader("myOtherStream\\fw.txt");
properties.load(fr);
fr.close();
System.out.println(properties);
}
private static void myStroe() throws IOException {
Properties properties = new Properties();
properties.setProperty("001","张三");
properties.setProperty("002","李四");
properties.setProperty("003","王五");
properties.setProperty("004","赵六");
//void store(Writer writer, String comments) 将此属性列表(键和元素对)写入此Proper表中,以适合使用load(Reader)方法
FileWriter fw = new FileWriter("myOtherStream\\fw.txt");
properties.store(fw, null);
fw.close();
}
}

4、案例:游戏次数

需求:请写程序实现猜数字小游戏只能试玩3次,如果还想玩,提示:游戏试玩已结束,想玩请冲值(www.itcast.cn)

思路:

​ 写一个游戏测试类,里面有一个猜数字的小游戏

​ 写一个测试类,测试类中有main()方法,mian()方法中按照下面步骤完成:

​ 从文件中读取数据到Properties集合,用load()方法实现

​ 文件已存在:game.txt

​ 里面有一个数据值:count=0

​ 通过Properties集合获取到玩游戏的次数

​ 判断次数是否到3次了

​ 如果到了,给出提示:游戏试玩以结束,想玩请充值(www.itcast.cn)

​ 如果不到3次:

​ 玩游戏

​ 次数+1,重新写回文件,用Properties的store()方法实现

package com.itheima_04;
/*
猜数字小游戏
*/
//导包
import java.util.Random;
import java.util.Scanner;
public class GuessNumber {
public GuessNumber(){}
public static void start(){
//生成一个随机数,使用一个变量来接收
Random r = new Random();
int number = r.nextInt(100) + 1;
while(true){
//定义一个Scanner对象用于输入数据
Scanner sc = new Scanner(System.in);
System.out.println("请输入你要猜的数字:");
int guessNumber = sc.nextInt();
//对比数值并进行提示
if(guessNumber > number){
System.out.println("你猜的数字" + guessNumber + "大了");
}
else if(guessNumber < number){
System.out.println("你猜的数字" + guessNumber + "小了");
}
else{
System.out.println("恭喜你猜中了");
break;
}
}
}
}
package com.itheima_04;
/*
需求:请写程序实现猜数字小游戏只能试玩3次,如果还想玩,提示:游戏试玩已结束,想玩请冲值(www.itcast.cn)
思路:
写一个游戏测试类,里面有一个猜数字的小游戏
写一个测试类,测试类中有main()方法,mian()方法中按照下面步骤完成:
从文件中读取数据到Properties集合,用load()方法实现
文件已存在:game.txt
里面有一个数据值:count=0
通过Properties集合获取到玩游戏的次数
判断次数是否到3次了
如果到了,给出提示:游戏试玩以结束,想玩请充值(www.itcast.cn)
如果不到3次:
玩游戏
次数+1,重新写回文件,用Properties的store()方法实现
*/
//导包
import java.io.IOException;
import java.util.Properties;
import java.io.FileReader;
import java.io.FileWriter;
public class PropertiesTest {
public static void main(String[] args) throws IOException {
//1、从文件中读取数据到Properties集合,用load()方法实现
Properties prop = new Properties();
FileReader fr = new FileReader("myOtherStream\\game.txt");
prop.load(fr);
fr.close();
//2、通过Properties集合获取到玩游戏的次数
String count = prop.getProperty("count");
int number = Integer.parseInt(count);
if(number >= 3){
//如果到了,给出提示:游戏试玩以结束,想玩请充值(www.itcast.cn)
System.out.println("游戏试玩以结束,想玩请充值(www.itcast.cn)");
}
else{
//玩游戏
GuessNumber.start();
//次数+1,重新写回文件,用Properties的store()方法实现
number++;
prop.setProperty("count", String.valueOf(number));
FileWriter fw = new FileWriter("myOtherStream\\game.txt");
prop.store(fw, null);
fw.close();
}
}
}

本文作者:编程初学者求大佬指点

本文链接:https://www.cnblogs.com/fragmentary/p/16823182.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   如此而已~~~  阅读(26)  评论(0编辑  收藏  举报
//雪花飘落效果
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起