Java---IO流

IO流概述

IO流:用于读写文件中的数据。其中I表示inputO表示output

IO流的分类

  1. 字节流:操作所有类型的文件
  2. 字符流:只能操作纯文本文件

IO流体系

IO流
字节流
字符流
InputStream
OutptStream
Reader
Writer

具体实现步骤

  1. 创建对象
  2. 写出数据
  3. 释放资源

字节流


字节输入流(InputStream)基本

具体实现

  1. 创建对象(使用FileOutputStream类)
 FileOutputStream fop = new FileOutputStream("io\\a.txt");
  1. 写出数据
fop.write(97);
  1. 释放资源
fop.close();

字节输出流的细节

  1. 创建对象
    • 参数字符串或者File对象均可
    • 如果文件不存在会重新创建一个新的文件,但是只要保证父类文件存在即可
    • 如果文件已经存在,则会清空原本文件
  2. 写数据
    • write方法参数是整数,但是写到对应路径上的文件是ASCII上的字符
  3. 释放资源
    • 每次使用完之后都要进行资源的释放

FileOutputStream的三种写数据方法

方法名称说明
void write ( int b )一次写一字节数据
void write ( byte[] b )一次写一字节数组数据
void write ( byte[] b ,int off ,int len)一次写一部分字节数组数据

具体实现

 FileOutputStream fop = new FileOutputStream("io\\a.txt");
 String str = "lalalalawoshimaibaodexiaohangjia";
 //将字符串转换为byte类型的数组
 byte[] bytes = str.getBytes(str);
 fop.write(bytes);
 fop.close();

关于换行

不同的系统换行操作是不同的

  • Windows系统:\r\n
  • Linux系统:\n
  • 苹果系统:\r

我们在实现换行操作时直接读入换行符即可

String str1 = "\r\n";
byte[] bytes1 = str1.getBytes(str1)
fop.write(bytes1);

字节输出流(OutputStream)基本

具体实现

  1. 创建对象(FileInputStream类)
        FileInputStream fis = new FileInputStream("io\\a.txt");

  1. 读取数据
        int b1 = fis.read();
        System.out.println((char)b1);
  1. 释放资源
        fis.close();

字节输入流的细节

  1. 创建对象
    • 如果文件不存在,会报错
  2. 写数据
    • 一次读入一个字节,读出来是对应ASCII的数字.。
    • 读到文件末了,read方法返回-1。
  3. 释放资源
    • 每次使用完之后都要进行资源的释放

字节输入流的循环格式

通过read方法返回-1来循环

FileInputStream fis = new FileInputStream("io\\a.txt");
        int b1;
        //while循环进行判断
        while((b1 = fis.read())!=-1){
            System.out.print((char)b1);
        }
        fis.close();

还可以用byte []来接收

FileInputStream fis = new FileInputStream("io\\a.txt");
int b1;
 byte[] bytes = new byte[5];
 while((b1 = fis.read(bytes))!=-1){
            System.out.print(new String(bytes,0,b1));
        }
fis.close();

关于字节流的拷贝问题

对于小文件

我们可以采取边读边录入的方式进行

//创建对象
FileInputStream inputStream = new FileInputStream("io\\a.txt");
FileOutputStream outputStream = new FileOutputStream("io\\b.txt");
//边读入边录入
int b;
while((b = inputStream.read())!=-1){
    outputStream.write(b);
}
//先开后关 后开先关   
outputStream.close();
inputStream.close();

对于大文件

我们采用一个字节一个字节的读入会使得速度缓慢,这是我们需要一个字符数组一个字符数组的读入

        FileInputStream inputStream = new FileInputStream("io\\a.txt");
        FileOutputStream outputStream = new FileOutputStream("io\\b.txt");
        int b;
        //创建你想要传输的字节数组大小
        byte[] bytes = new byte[1024*1024*5];
        //传输
        while((b = inputStream.read(bytes))!=-1){
            outputStream.write(bytes,0,b);
        }
        释放
        outputStream.close();
        inputStream.close();
细节

此时我们使用write(byte[],int off,int len)方法是防止数据可能最后读入时无法取整的情况


对于字节流的异常处理

基本做法

手搓异常处理

try(){

	//可能出现的异常
	
}catch(异常类名 变量名){

	//异常的处理代码

}finally{

	//执行所以资源的释放
	
}

对于之前的数据拷贝进行异常处理

	//将实例创建放在外面,是的finally中的变量能够使用
	//对于成员变量要实现 空 的初始化,不然可能会警示成员变量未初始化。
		FileInputStream inputStream = null;
        FileOutputStream outputStream = null;
        
       try{
            inputStream = new FileInputStream("io\\a.txt");
            outputStream = new FileOutputStream("io\\b.txt");
           int b;
           byte[] bytes = new byte[1024*1024*5];
           while((b = inputStream.read(bytes))!=-1){
               outputStream.write(bytes,0,b);
           }
                      
          }catch(IOException e){
           System.out.println(e.getMessage());
           
          }finally{
          //如果为空,则没有关闭流的必要
           if(outputStream!=null){
           		//对于close还要进行异常处理
               try{
                   outputStream.close();
               }catch (IOException e){
                   e.getMessage();
               }
           }
           if(inputStream!=null){
               try{
                   inputStream.close();
               }catch (IOException e){
                   System.out.println(e.getMessage());
               }
           }
          
       }

JDK8

将对象的创建直接放入try中,之间用分号隔开

 	   try(FileInputStream inputStream = new FileInputStream("io\\a.txt");
            FileOutputStream outputStream = new FileOutputStream("io\\b.txt");){
            int b;
            byte[] bytes = new byte[1024*1024*5];
            while((b = inputStream.read(bytes))!=-1){
                outputStream.write(bytes,0,b);
            }
        }catch (IOException e){
            e.getMessage();
        }

JDK9

将对象创建放在外面,直接由函数抛出对象创建的异常。try中直接放入变量名。

 public static void main(String[] args) throws FileNotFoundExpection{	
	FileInputStream inputStream = new FileInputStream("io\\a.txt");
    FileOutputStream outputStream = new FileOutputStream("io\\b.txt");
 	try(inputStream;outputStream){
            int b;
            byte[] bytes = new byte[1024*1024*5];
            while((b = inputStream.read(bytes))!=-1){
                outputStream.write(bytes,0,b);
            }
        }catch (IOException e){
            e.getMessage();
        }
 }

字符集


关于字符集

了解字符集之前,我们要知道以下两点

  1. 计算机中,任意数据都是二进制存储
  2. 计算机中,最小的存储单元是一个字节(8位二进制)

ASCII字符集

对于欧美国家已经受用且一个英文占用一个字节

GDK字符集

我国所定义的含义中文日文韩文等的字符集

  1. 简体中文版的Windows默认GDK字符集
  2. GDK字符集兼容ASCII字符集
    • 一个英文占一个字节,二进制首位为0
    • 一个中文站两个字节,高进制首位为1

Unicode字符集

国际上定义的字符集,几乎含有所有语言,最常用UTF-8的编码形式

  • 一个英文占用一个字节,二进制首位为0,转为十进制为正数
  • 一个中文占用三个字节,二进制首位为1,第一个二进制转为十进制为负数

关于乱码

为什么会产生乱码

  1. 读取数据时没有读完完整的汉字
  2. 编码解码时没有使用同一种方式

如何规避

  1. 不用字节流读取文本文件
  2. 编码解码时使用同一种方式

Java中关于编码与解码

编码

String类里面的方法说明
public byte[] getBytes()使用默认方式编码
public byte[] getBytes(String charsetName)使用指定方式编码

解码

String类里面的方法说明
String (byte[] bytes)默认方式解码
String (byte[] bytes ,String charsetName)指定方式解码

具体实现

		String str0 = "喜欢你!";
        byte[] byte1 = str0.getBytes();
        byte[] byte2 = str0.getBytes("GBK");

        //解码
        String str1 = new String(byte1);
        String str2 = new String(byte2,"GBK");

        System.out.println(str1);
        System.out.println(str2);

字符流

字符流的底层就是字节流

字符流 = 字节流 + 字符集

特点

  • 输入流:一次读入一个字节,遇到中文时候读入多个字节
  • 输出流:底层会把指定的编码方式进行编译,变成字节在写到文件之中

字符输入流(Reader)的基本

具体实现

  1. 创建对象
 FileReader fr = new FileReader("io\\reader.txt");
  1. 读取数据
//单个数据
int cha1 = fr.read();
//多个数据
int cha2 = fre.read(char[] buffer);
  1. 释放资源
fr.close();

空参read

		FileReader fr = new FileReader("io\\reader.txt");
		//读取数据
        int ch;
        while((ch = fr.read()) != -1){
            System.out.print((char)ch);
        }
        //资源释放
        fr.close();

有参read

有参数的read方法相当于将数据读取,解码,强制三部并和了

        FileReader fr = new FileReader("io\\reader.txt");

        int ch;
        //用字符串数组去接收
        char[] chars = new char[25];
        while((ch = fr.read(chars)) != -1){
            System.out.print(new String(chars,0,ch));
        }

        fr.close();

字符输出流(Writer)基本

具体实现

  1. 创建对象
        FileWriter fw = new FileWriter("io\\writers");
  1. 读取数据
		fw.write("我爱你");
		//也可读入一个字符数组write(char[] cha);
  1. 释放资源
		fw.close();

原理解析

  1. 创建对象
    • 关联文件,创建缓冲区(长度为8192字节的数组)
  2. 读取数据
    • 判断缓冲区是否有数据可以读取
    • 缓冲区没有数据时:从文件中获取数据,尽可能填满缓冲区;如果文件中没有数据,则返回-1
    • 缓冲区有数据:则从缓冲区读取数据

扩展

方法名称说明
public void flush()将缓冲区的数据刷新到本地文件中
public void close释放资源/关流

File类

  1. File表示什么?
    • File对象表示路径,可以是文件,可以是文件夹,可存在也可不存在
  2. 绝对路径相对路径
    • 绝对路径带有盘符
    • 相对路径不带盘符,默认当前项目下寻找

File类的构造方法

方法名称说明
public File (String pathname)用字符串表示路径
public File (String parent , String child)把父级路径和子级路径拼接到一起
public File (File parent , String child)同上

常用成员方法

方法名称说明
public boolean createNewFile()创建一个新的文件
public boolean mkdir() / mkdirs()创建单极/多级文件
public boolean delete()删除文件,空的文件夹
public boolean delete()获取当前路径下的所有内容

public boolean createNewFile() :创建一个新的文件

  1. 如果当前路径表示的文件不存在,则创建成功并且返回true,反之则返回false
  2. 如果父级路径不存在,则会抛出IOException异常
  3. createNewFile方法创建的一定是文件,如果没有后缀名,则会创建没有后缀的文件

public boolean mkdir() / mkdirs() :创建单极/多级文件

  1. Windows系统中文件名是唯一的,如果存在,则创建失败返回false
  2. **mkdirs()**方法既可创建单极也可创建多级,经常使用

public boolean delete() : 删除文件,空的文件夹

  1. 如果删除的是文件或空的文件夹,则直接删除且不走回收站
  2. 如果删除的是有内容的文件夹,则删除失败,返回false

public File[] listFiles() :获取当前路径下的所有内容

  • 当调用者File表示的路径不存在或者为文件时,返回null
  • 当为空文件时,返回长度为0的数组
  • 当文件夹有内容时,返回所有内容的路径
  • 当有隐藏文件时,照样会返回路径
  • 当有权限修饰时,会返回null

一些具体的实现

文件夹的拷贝

基本原理

  1. 写一个文件拷贝的函数,传递数据源路径和目标路径
  2. 遇到文件则进行拷贝,遇到文件夹则进行递归

先写copy函数

public static void copy(File parent,File child) throws IOException {
		//防止为空
        child.mkdirs();
        //阅览目标文件下的内容
        File[] files = parent.listFiles();
        //增强for语句遍历
        for(File file : files){
        	//如果是文件,直接拷贝
            if(file.isFile()){
                FileInputStream fis = new FileInputStream(file);
                //拷贝地址为父类文件加上子类文件名
                FileOutputStream fos = new FileOutputStream(new File(child,file.getName()));
                byte[] bytes = new byte[1024*1024];
                int len;
                while((len = fis.read(bytes)) != -1){
                    fos.write(bytes,0,len);
                }
                fos.close();
                fis.close();
                //如果为文件,则进行递归操作
            }else{
                copy(file,new File(child,file.getName()));
            }
        }

再写上主函数

		File str = new File("F:\\kaobei\\Java");
        File md = new File("F:\\kaobei\\Javadekaobei");
        try{
            copy(str,md);
        }catch (IOException e){
            System.out.println(e.getMessage());
        }catch(Exception e){
            System.out.println(e.getMessage());
        }

缓冲流

缓冲流
缓冲字节流
缓冲字符流
BufferedInputStream
BufferedOutptStream
BufferedReader
BufferedWriter

作用

  1. 对于字符流:在内存中创建一个最大长度为8192的缓冲区来接受,传递数据,提高传输效率。
  2. 对于字节流
    • 由于自带缓冲区,所以对于传输速度影响不大
    • 有许多好用的方法比如readLine(),newLine等。

缓冲字节流

  1. 创建对象
方法名称说明
public BufferedInputStream (InputStream is)输入流创建
public BufferedOutputStream (OutputStream is)输出流创建

具体实现

		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("io\\a.txt"));
		
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("io\\b.txt"));
  1. 读取数据
    与之前的字节输入输出流用法一致
  2. 释放资源
		bis.close();
		bos.close();

注解:这里的close()方法只需要关闭缓冲流的即可,底层代码会自动关闭字节流。

缓冲字符流

创建对象,读取数据,释放资源与上类似,来讲讲他的好用的方法

public String readLine():缓冲字符输入流 特有 方法

public String readLine()方法会读取一整行,遇到回车换行会返回null,所以我们在用readLine()方法时判断条件应改为 :

String len;
while((len = br.readLine()) != null){.....}

public void newLine():缓冲字符输出流 特有 方法

因为windows,Linux,苹果系统之间的换行符不一样,所以使用newLine()方法能统一换行,更加严谨。

转换流

转换输入流
InputStreamReader
转换输出流
OutputStreamWriter

作用

  1. 指定字符集的读写 (已经被淘汰!)
  2. 字节流想要使用字符流中的方法。

具体实现

  1. 创建对象
	InputStreamReader isr = new InputStreamReader(new FileInputStream());
	//若想使用BufferedInputStream的方法,则如下创立对象
	InputStreamReader isr = new InputStreamReader(new BufferedInputStream(new FileInputStream()));
  1. 读取数据
  2. 释放资源

序列化流

反序列化
ObjectInputStream
序列化
ObjectOutputStream

作用:将成员变量存储到文件当中

具体实现

  1. 构造
构造方法说明
public ObjectOutputStream (OutputStream is)把基本输出流包装为高级流
public ObjectInputStream (InputStream is)把基本输入流包装为高级流
	ObjectOutputStream oos = new ObjectOutputStream( new OutputStream());
  1. 读取数据
方法说明
public final void writeObject (Object obj)把对象序列化,并且读入到文件中
public final void readObject (Objetct obj)把文件中的对象反序列化并且读出
	oos.writeObject(new Student());
  1. 释放资源

细节

  1. 使用序列化流时,要让javabean实现Serializable接口
public class Student implements Serializable{...}
  1. 序列化流写道文件中无法被改变了,改变会出现异常
  2. 序列化对象后,如果我们改变javabean,在进行反序列化
    • 会出现异常,因为每一次改变都会更变javabean的“版本号”
    • 要规避这个异常,我们就要自己固定版本号serialVersionUID,并且使用private static final修饰。
	private static final long serialVersionUID = -7160264594827633452L;
  1. 如果一个成员变量不想被序列化,则用transient进行修饰
    private transient int age;

多对象读取

思路

  1. 多对象的读取到文件末尾不会返回-1或者null
  2. 我们想要定义一个**ArrayList< E >**容器来装取多个对象,只需要读取一次即可

对于对象的录入

		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("io\\xulie1"));
        ArrayList<Student> list = new ArrayList<>();
		//多个对象的定义
        Student stu1 = new Student("zhangsan",17);
        Student stu2 = new Student("lisi",18);
        Student stu3 = new Student("wangwu",19);
		//将对象装入容器中
        list.add(stu1);
        list.add(stu2);
        list.add(stu3);
		//写入文件当中
        oos.writeObject(list);
		//释放资源
        oos.close();;

对于对象的录出

		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("io\\xulie1"));
		//读取数据
        ArrayList<Student> o = (ArrayList<Student>)ois.readObject();
        //增强for来阅览容器
        for (Student student : o) {
            System.out.println(student);
        }
		资源释放
        ois.close();

打印流

字节打印流
PrintStream
字符打印流
PrintWriter

特点

  1. 打印流只有输出流,无输入流
  2. 特有的方法可以实现,可以保证数据的原始输出
  3. 特有的写出方法,可以实现自动刷新自动换行

字节打印流

构造方法

构造方法说明
public PrintStream(OutputStream/File/String)关联字节输出流
public PrintStream(String fileName, Charest charset)指定字符编码
public PrintStream(OutputStream out, boolean autoFlush)自动刷新
public PrintStream((OutputStream out, boolean autoFlush, String encoding)指定字符编码并且自动刷新

成员方法

方法名称说明
public void write()常规方法:与之前的写出规则一样
public void println()特点方法:打印任意数据,自动换行,自动刷新
public void print()特点方法:打印任意数据,不换行
public void printf()特定方法“带有占位符的打印语句,不换行

说明

  • 由于字节流的底部没有缓冲区,所以自动刷新功能没有用处。
	 PrintStream ps = new PrintStream(new FileOutputStream("io\\reader"),true, StandardCharsets.UTF_8);
     //写出数据
     ps.println(97);
     ps.print(true);
     ps.printf("%dis%d",1,2);
     //释放资源
     ps.close();

字符打印流

字节打印流差不多,记得自动刷新要手动开启。

关于System.out.println()

System类中有一个静态的out是这么定义的

	public static final PrintfStream out = null;
  • 此时我们获取打印流对象,在虚拟机启动时默认创建,指向控制台
  • 特殊的打印流,是唯一的,不能进行关闭操作

针对以上,我们可以如下写出代码

	PrintStream ps = System.out;
	ps.println("你好你好");
  • 24
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值