Java IO

基本介绍

流的概念

在Java程序中,对于数据的输入/输出操作以“流”(stream)方式进行。流只能单方向流动,输入流用来读取数据,输出流用来写出。
在这里插入图片描述

输入/输出流的分类

  • 按照数据流的方向不同可以分为输入流和输出流;
  • 按照处理数据单位不同可以分为字节流和字符流
  • 按照功能不同可以分为节点流和处理流

节点流和处理流

节点流可以从一个特定的节点读写数据。
处理流是“连接”在已存在的流之上,通过对数据的处理为程序提供更为强大的读写功能。
在这里插入图片描述

InputStream类

用于向程序输入数据,数据单位为字节。
InputStream相关子类
在这里插入图片描述
注:上图深色为节点流,浅色为处理流。
InputStream基本方法

abstract int read() 
从输入流读取数据的下一个字节。  
int read(byte[] b) 
从输入流读取一些字节数,并将它们存储到缓冲区 b 。  
int read(byte[] b, int off, int len) 
从输入流读取最多 len字节的数据到一个字节数组。  
void close() 
关闭此输入流并释放与流相关联的任何系统资源。  
long skip(long n) 
跳过并丢弃来自此输入流的 n字节数据。 

OutputStream类

用于向程序输出数据,数据单位为字节。
OutStream相关子类
在这里插入图片描述
OutStream基本方法

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

Reader类

用于向程序输入数据,数据单位为字符(16 bit)。
Reader相关子类
在这里插入图片描述

Reader基本方法

int read() 
读一个字符  
int read(char[] cbuf) 
将字符读入数组。  
abstract int read(char[] cbuf, int off, int len) 
将字符读入数组的一部分。  
int read(CharBuffer target) 
尝试将字符读入指定的字符缓冲区。  
long skip(long n) 
跳过字符  
abstract void close() 
关闭流并释放与之相关联的任何系统资源 

Writer

用于向程序输出数据,数据单位为字符。
Writer相关子类
在这里插入图片描述
Writer基本方法

abstract void close() 
关闭流,先刷新。  
abstract void flush() 
刷新流。  
void write(char[] cbuf) 
写入一个字符数组。  
abstract void write(char[] cbuf, int off, int len) 
写入字符数组的一部分。  
void write(int c) 
写一个字符  
void write(String str) 
写一个字符串  
void write(String str, int off, int len) 
写一个字符串的一部分。 

以上关于基本方法的介绍部分摘自JDK API文档

节点流

通俗地讲,节点流就像一根管子,程序就像一个水桶,管子直接插入水桶并通过管子进行数据的输入/输出。

节点流类型

在这里插入图片描述

节点流的实际应用

下面给出FileInputStream,FileOutputStream,FileReader,FileWriter的小例子。
InputStream

import java.io.*;
public class TestFileInputStream {
    public static void main(String args[]){
        int b = 0;
        FileInputStream in = null;
        try{
            in = new FileInputStream("C:\\Users\\86198\\IdeaProjects\\zhoulr\\src\\com\\company\\TestFileInputStream.java");
            //此处文件路径根据你要读取的文件进行书写
        }catch(FileNotFoundException e){
            System.out.println("找不到指定文件");
            System.exit(-1);//结束程序
        }
        try {
            long num = 0;
            while ((b = in.read()) != -1)//检测是否输到文件尾
            {
                System.out.print((char)b);
                num++;
            }
            in.close();
            System.out.println();
            System.out.println("共读取了"+num+"个字节");
        }catch(IOException e1){
            System.out.println("文件读取错误");System.exit(-1);
        }
    }
}

结果
我只截取了后面的一部分,其实就是把这个代码打印出来了(因为我的文件路径就是这个代码的路径)。其中出现乱码的原因是InputStream是字节流,单位是一个字节,但中文需要两个字节所以会出现乱码。
在这里插入图片描述
OutputStream

import java.io.*;
public class TestOutputStream {
    public static void main(String args[]){
        int b = 0;
        FileInputStream in = null;
        FileOutputStream out = null;
        try{
            in = new FileInputStream("D:/HelloWorld.java");
            //写需要复制的文件路径
            out = new FileOutputStream("D:/JAVA/test.txt");
			//写被复制的文件路径
            while((b=in.read())!=-1)
                out.write(b);
            in.close();//关闭输入流
            out.close();//关闭输出流
        }catch(FileNotFoundException e1){
            System.out.println("找不到指定文件");
            System.exit(-1);
        }catch(IOException e2){
            System.out.println("文件复制错误");
            System.exit(-1);
        }
        System.out.println("文件已成功复制");
    }
}

结果
这个就只有一句文件成功复制…当然如果路径不正确的话应该会显示错误。然后在文件里就可以查看相关信息了。

FileReader

import java.io.*;
public class TestFileReader {
    public static void main(String args[]) {
        FileReader fr = null;
        int a = 0;
        try {
            fr = new FileReader("C:\\Users\\86198\\IdeaProjects\\zhoulr\\src\\com\\company\\TestFileReader.java");
            int l = 0;
            while ((a = fr.read()) != -1) {
                System.out.print((char) a);
            }
            fr.close();
        } catch (FileNotFoundException e1) {
            System.out.println("找不到指定文件");
        } catch (IOException e2) {
            System.out.println("文件读取错误");
        }
    }
}

结果
其实这个程序跟上一个InputStream是有很多相似之处的,但它的结果不会出现乱码,因为Reader本身是字符流,单位是字符,可以输出中文。
在这里插入图片描述
FileWriter

import java.io.*;
public class TestFileWriter {
    public static void main(String args[]){
        FileWriter fw = null;
        try{
            fw = new FileWriter("D:\\JAVA\\test.txt");
            for(int i=0;i<=50000;i++){
                fw.write(i);
            }
            fw.close();
        }catch(IOException e){
            e.printStackTrace();
            System.out.println("文件写入错误");
            System.exit(-1);
        }
    }
}

结果
这个程序和OutputStream那个其实还是有很大区别的,这个程序利用Writer也是字符输出的形式,将0~50000的字符都读入了文件中(最大应该是65535个字符),Java采用的是Unicode码,65535个可以包含差不多所有国家的文字了。接下来看一下这个文件里的内容。有一些是乱码,应该是因为在我的电脑上不支持查看…但条件允许的话应该都是可以显示的。
在这里插入图片描述

处理流

处理流相当于是另一根管道包在节点流或处理流的外面(人生无常大肠包小肠hhhh),通过处理流能让数据的处理更简洁高效,为程序提供更强大的读写功能。

处理流的类型

在这里插入图片描述

缓冲流

缓冲流套接在相应节点流上,对输入输出的数据提供缓冲的功能,提高了读写的效率,同时增加了一些新方法。
缓冲流相关构造方法

1.BufferdReader(Reader in)
2.BufferedReader(Reader in,int sz)//sz为自定义缓存区大小
3.BufferedWriter(Writer out)
4.BufferedWriter(Writer out,int sz)
5.BufferedInputStream(InputStream in)
6.BufferedInputStream(InputStream in,int sz)
7.BufferedWriter(OutputStream out)
8.BufferedWriter(OutputStream out,int sz)

注意事项

  • 缓冲流支持mark和reset方法。
  • BufferedReader提供了readLine方法用于读取一行字符串。
  • BufferedWriter提供了newLine用于写入一个行分隔符。
  • 对于缓冲流,使用flush方法会将内存中的数据立刻写出。

缓冲流相关应用

Example1

import java.io.*;
public class TestBufferedStream1 {
    public static void main(String args[]){
        try{
            FileInputStream fis=new FileInputStream("D://HelloWorld.java");
            BufferedInputStream bis =new BufferedInputStream(fis);
            int c=0;
            System.out.println(bis.read());
            System.out.println(bis.read());
            //输入fis的前两个字符
            bis.mark(100);
            for(int i=0;i<=10&&(c= bis.read())!=-1;i++){
                System.out.println(c+" ");
            }
            //依此从第三个字符开始输出十一个字符
            System.out.println(" ");
            bis.reset();
            for(int i=0;i<=10&&(c= bis.read())!=-1;i++) {
                System.out.println(c + " ");
            }
            //再输出十一个字符
            bis.close();
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}

Example2

import java.io.*;

public class TestBufferedStream2 {
    public static void main(String args[]){
        try{
            BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\JAVA\\test.txt"));
            BufferedReader br = new BufferedReader(new FileReader("D:\\HelloWorld.java"));
            String a = null;
            for(int i=1;i<=100;i++){
                a = String.valueOf(Math.random());
                bw.write(a);
                bw.newLine();
            }
            //此段代码的作用是a生成100个随机数输入到test.txt文件中
            bw.flush();
            while((a=br.readLine())!=null){
                System.out.println(a);
            }
            //将HelloWorld.java内的内容输出
            bw.close();
            br.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

结果
小黑板上的结果:
在这里插入图片描述
文件里的结果:
在这里插入图片描述

转换流

个人理解转换流是Reader和InputStream的转换,Writer和OutputStream的转换(本质上说就是字符流和字节流的转换吧)。
这块内容会更好理解一点点吧,现在就举一个小例子。
Example1

import java.io.*;

public class TestTransForm {
    public static void main(String args[]){
        try{
            OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\\JAVA\\test.txt"));
            osw.write("Javahaonan!!");
            System.out.println(osw.getEncoding());
            osw.close();
            osw = new OutputStreamWriter(new FileOutputStream(("D:\\JAVA\\test.txt"),true),"ISO8859_1");
            //ISO8859_1包含所有的西欧语言,别名latin_1。
            //这个true表示Bluemsun great是加在前面的数据后面的,如果不写true就会覆盖掉前面的字符串。
            osw.write("Bluemsun great");
            System.out.println(osw.getEncoding());
            osw.close();
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}

数据流

DataInputStream和DataOutputStream分别继承自InputStream和OutputStream,它属于处理流,需要分别套接在InputStream和OutputStream类型的节点流上。
数据流提供了可以存取与机器无关的Java原始类型数据(如int,double等)的方法。
下面举一个简单的例子来阐述一下数据流的构造方法和使用。

import java.io.*;
public class TestDataStream {
    public static void main(String args[]){
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(baos);
        //构造方法:DataOutputStream (OutputStream out);
        try{
            dos.writeDouble(Math.random());
            dos.writeBoolean(true);
            //内存里double类型的一个数据,Boolean类型一个数据
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            System.out.println(bais.available());
            //输出内存所占空间
            DataInputStream dis = new DataInputStream(bais);
            System.out.println(dis.readDouble());
            //输出double型数据
            System.out.println(dis.readBoolean());
            //输出boolean型数据
            dis.close();dos.close();
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}

结果
在这里插入图片描述

Print流

顾名思义,Print流是用来输出的处理流。
注意事项

  • PrintWriter和PrintStream都是输出流,分别针对字符和字节。
  • PrintWriter和PrintStream提供了重载的print,println方法用于多种数据类型的输出。
  • PrintWriter和PrintStream的输出操作不会抛出异常,用户通过检测错误状态获取信息
  • PrintWriter和PrintStream有自动flush的功能。(也可以手动)

关于Print流的相关构造方法在这里不再赘述,需要学习的话可以自行打开API文档查看。
下面举一个关于Print流的应用实例。
Example

import java.io.*;
import java.util.*;
import java.util.Locale;

public class TestPrintStream3 {
    public static void main(String args[]){
        String s = null;
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        try{
            FileWriter fw = new FileWriter("D:\\JAVA\\test.txt",true);
            PrintWriter pw = new PrintWriter(fw);
            while((s=br.readLine())!=null){
                if(s.equalsIgnoreCase("exit"))
                    break;
                System.out.println(s.toUpperCase());
                pw.println("-----");
                pw.println(s.toUpperCase());
                pw.flush();
                //虽然可以自动flush但是还是养成习惯flush比较好
            }
            pw.println("==="+new Date()+"===");
            pw.flush();
            pw.close();
        }catch(IOException e)
        {
            e.printStackTrace();
        }
    }
}

这个代码可以算是一个简单的写日志的代码叭。每次编辑都会显示日期,然后只有你输入才会在文件里输出。还挺有意思的。
结果看下图。
绿色是我的输入,白色是输出。在这里插入图片描述
这是记事本内的内容。在这里插入图片描述

Object流

Object流可以直接将Object整个写入或读出。
关于Object流先以一个例子来说明。
Example

import java.io.*;
public class TestObjectIO {
    public static void main(String args[]) throws Exception{
        T t = new T();
        t.k=8;
        FileOutputStream fos = new FileOutputStream("D:\\JAVA\\test.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(t);
        oos.flush();
        oos.close();
        FileInputStream fis = new FileInputStream("D:\\JAVA\\test.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);
        T tread = (T)ois.readObject();
        System.out.println(tread.i+"  "+tread.j+" "+ tread.k+" "+ tread.n);

    }
}
class T
    implements Serializable
{
    int i = 7;
    int j = 9;
    double k = 21.0;
    transient int n = 13;
}

结果如下图:
在这里插入图片描述
通过以上程序 Object有几个知识点需要掌握。

transient关键字
transient是流动的,暂时的的意思。它是屏蔽了在此关键词后所定义的内容,故要输出的话,会输出它的初始值,也就是0.

serializable接口
上面的程序可以看到在class T的定义中后面写了implements serializable说明该方法时可序列化的(给编译器看的),如果没有写这句话,Object整体被输入输出的话是会报错的。

externalizable接口
由serializable接口我们可以知道是可以声明一个类是可序列化的,但序列化的这个过程是由jdk来完成。而externalizable接口则可以控制序列化的过程。(但最好还是jdk给你序列化)

总结

终于写完了…这篇博客真的有够长的。上一节学异常的时候其实很懵都是在网上东拼西凑的看课预习,这节其实好很多,就看了学长学姐推荐的网课,我感觉条理很清晰。然后但是其中还是有很多细节的方法我不知道的,就一直在看API文档,比如printStackTrace,equalsIgnoreCase等。
这一节的内容其实很好分类,主要分成输出和输入两个大类,然后是节点流,处理流。节点流相对简单一点吧,File****这样的格式。处理流包括很多类,有不同功能,Buffered缓冲,转换流,Data数据流,然后是Print输出流和Object流。
还有很多方法没有写出来,需要用到的话,拥抱API叭~~~~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值