Java - I/O 流基类以及常用处理流

本文介绍了Java中File文件类的基本操作,详细解析了Java的流分类,包括输入流、输出流、字节流、字符流、节点流和处理流。重点讨论了处理流如BufferedReader、BufferedInputStream及其工作原理,以及InputStreamReader、OutputStreamWriter的作用,强调了字符编码在处理文本文件时的重要性。
摘要由CSDN通过智能技术生成

File文件类基本操作

首先我们在了解Java流之前,我们先了解 File 文件类
文件类用于写入和写出,也就是我们的输入和输出流。我们首先要知道关于文件的基本操作。例如创建,删除,添加,修改等等。

    public static void main(String[] args) {
        //创建File对象
        //创建File对象时,要给定URL,可以是相对路径也可以是绝对路径
        //绝对路径:
        File file1 = new File("D:\\TulunJava\\test\\a.txt");
        //相对路径:
        File file2 = new File(".\\test\\b.txt");

        //如果在这个路径下不存在此文件,那么我们就创建文件
        try {
        if(!file1.exists()){
            //创建了a.txt
            file1.createNewFile();
        }
        if(!file2.exists()) {
            //创建了b.txt
            file2.createNewFile();
        }
        } catch (IOException e) {
            e.printStackTrace();
        }

        //创建单级文件夹
        File file3 = new File("D:\\TulunJava\\test\\folder");
        file3.mkdir();

        //如果要创建多级文件夹,
        // 也就是在folder中创建一个文件夹叫folder1,在foldder1中创建了folder2
        File file4 = new File("D:\\TulunJava\\test\\folder\\folder1\\folder2");
        file4.mkdirs();

        //获取文件的大小,名字,绝对路径
        System.out.println(file1.length());
        System.out.println(file1.getName());
        System.out.println(file1.getAbsolutePath());

        //判断一个文件对象是 文件夹 还是 文件
        if(file1.isFile()){
            System.out.println("file1文件对象是一个文件");
        }

        if(file4.isDirectory()){
            System.out.println("file4文件对象是一个文件夹");
        }

        //获取一个目录下的所有文件名
        File file = new File(".\\test\\");
            //把file文件目录下所有的文件的名字返回到String数组中
        String[] strs = file.list();
        for(String  s1: strs){
            System.out.println(s1);
        }
           //把file文件目录下所有的文件返回到File数组中
        File[] files = file.listFiles();
        System.out.println(Arrays.toString(files));
        
         //文件的删除
        //文件可以直接删除
        file1.delete();
      
        
    }
      //但是文件夹必须为空才可以直接删除它,
        // 所以在这里我们要先进入文件夹将文件夹的文件全部删除,我们在将文件夹删除
        //这里我们运用递归
     public static  boolean delete(File file1){
        if(file1 == null || !file1.exists()){
            return false;
        }
        //如果这个文件对象是文件直接删除即可
        if(file1.isFile()){
            file1.delete();
            return  true;
        }
        //如果这个对象是文件夹
        if(file1.isDirectory()){
            File[] files = file1.listFiles();
            //如果文件夹不为空遍历删除
            if(files != null){
                for(File f1 : files){
                    //遍历到文件夹调用删除文件方法递归
                    if(f1.isDirectory()){
                        delete(f1);
                        //删除完文件夹内容后,再删除
                      	f1.delect();
                      }
                    //遍历到文件直接删除
                    if(f1.isFile())
                        f1.delete();
                }
            }else {
                //如果文件夹为空直接删除即可
                file1.delete();
                return true;
            }

        }
       //如果是文件夹并且出了循环,代表文件夹内容删除干净了,删除自己即可
      file.delete();
      return true;
    }

以上就是File文件类的常用操作方法


Java 中有那些流?

Java 中按流向分为 输入流输出流
输入流:InputStreamReader 作为基类
输出流:OutputStreamWriter 作为基类
(tips:输入和输出是相对j计算机内存而言,而不是源和目标。)

处理数据单元功能划分为 字节流字符流
字节流:字节输入流基类:InputStream、字节输出流基类: OutputStream
字符流:字符输入流基类:Reader、字符输出流基类:Writer
(tips:字节流是通用8位字节流,字符流是16位 Unicode 字符流,亦或者其他字符流。)

按照功能划分,分为 节点流处理流
节点流:可以直接从数据源或目的地读写数据
处理流(包装流):不直接连接到数据源或目的地,是其他流进行封装。目的是简化操作,提升性能。
(tips:节点流和处理流的关系:节点流处于I/O操作的第一线,所有操作b必须通过它执行;处理流可以对其他流进行处理(提高效率或保证操作灵活性))


输入流 和 输出流

输入流: 我们可以理解为读取文件,把文件的内容输入给内存;
输出流:我们可以理解为写入文件,把内存中的数据输出给文件。

我们实现把 a.txt 文件的内容复制到 b.txt。
(这里我们已经存在 a.txt 和 b.txt 文件,如果不存在,那么就创建。)

  public static void main(String[] args) {
        //创建源文件对象,目的文件对象,以及输入输出流资源,
        File src = new File("./a.txt");
        File dest = new File("./b.txt");
        InputStream  inputStream = null;
        OutputStream outputStram = null;

        try {
            inputStream = new FileInputStream(src);
            outputStram  = new FileOutputStream(dest);

            //方法1:边读取,边写入
            //将读取的内容(字节)先存储到 temp 中
//            int temp = 0;
//            //inputStream的read()方法是按字节读取,当字节读取为-1时,证明读取结束
//            while( (temp = inputStream.read()) != -1){
//                outputStram.write(temp);
//            }

            //方法2:将数据读取到缓存再去写入,优点:减少了磁盘
            //读取到的内容长度
            int length = 0;
            //创建了一个字节数组,先将
            byte[] buffer = new byte[1024];
            //把内容读取到缓存数组buffer中
            if((length = inputStream.read(buffer)) != -1){
                //把缓存中的内容进行写入
                outputStram.write(buffer);
            }
  			//flush方法强制将缓冲区中的数据刷新到目标文件中
            outputStram.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                //无论如何,关闭资源
                inputStream.close();
                outputStram.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }


    }

(tips: 读取 和 写入都是在文件的最开始的位置读取和写入的,所以当程序重复执行时,会发现目的文件并没有任何变化,这就是原因)

额外知识:为什么read()在读取的时候是按照字节读取的,但是方法返回的却是int类型??

因为我们在处理输入流时,我们最终会在read方法返回 -1 的时候中止读取,如果read方法返回的是byte类型,-1也就对应的是8个字节全部为1。这个时候有很大的概率读取到中间读取到了8个字节全部为1的情况,但是此时并没有将源文件读取完,所以就会产生错误。但是当我们用 int 类型去返回时,读取到-1时,也只是8位为1,避免了中途中断读取的错误。但是读取到32位全部为1这个概率就非常小了,所以这避免了读取到中间就中止读取的错误。

额外知识:什么时候需要用 flush 方法?

1.再输出流关闭之前,每一次都 flush 一下(最保险的方法,推荐使用)
2.当某一个输出流对象中有缓冲区,就需要flush。


字符流和字节流

字符流,我们对源文件和目的文件按照指定字符去读取和写入。
字节流:我们对源文件和目的文件按照字节去读取和写入。
从这里我们就可以发现,字符流的效率一定是比字节低的!因为计算机的磁盘的存储本质上就是字节存储,如果按照字符去读取和写入,那么我们必须先按照字节读取,然后再进行指定编码读取,再写入到目的文件。这个效率比字节读取和写入要低下。
并且字符流实际上只能处理文本文件,当时字节流可以处理一切文件。

但是!既然如此为什么还会存在字符流?大家可以去试试读取中文文本,每当按照字节读取时,每读取一次就输出一次,我们会发现输出的是乱码,所以这就是问题(中文字符占1-5个字节),但是当我们使用字节流时就不需要担心了,我们按照了指定的字符编码去读取和写入,所以每一次读取的都是一个字符并且经过Unicode编码,所以就不会出现乱码了。
字节流

    public static void main(String[] args) {
        //创建源文件和目标文件对象,以及输入输出资源
        File src = new File("./a.txt");
        File dest = new File("./b.txt");
        InputStream  inputStream = null;
        OutputStream outputStream = null;

        try {
            inputStream = new FileInputStream(src);
            outputStream = new FileOutputStream(dest);
            
            //利用缓冲区提高效率
            int length = 0; 
            byte[] buffer = new byte[1024];
            while( (length= inputStream.read(buffer)) != -1){
                outputStream.write(buffer);
            }
            
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            try {
                inputStream.close();
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

字符流

public static void main(String[] args) {
       //创建源文件、目标文件对象,以及输入输出资源
        File src = new File("./a.txt");
        File dest = new File("./b.txt");
        Reader reader = null;
        Writer writer = null;
  try {
            reader = new FileReader(src);
            writer = new FileWriter(dest);

            //创建字符缓冲区提高效率
            int length = 0;
            char[] buffer = new char[1024];
            while((length = reader.read(buffer)) != -1 ){
                writer.write(buffer);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                reader.close();
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }

节点流和处理流

节点流:源文件和目的文件直接连接,并没有中间流进行包装。
处理流:源文件和目的文件不直接连接,中间有字节流或者字符流对源文件的流进行包装,然后再输出给目的文件,这样做能提高我们的效率,并且简化我们的操作。

节点流

    public static void main(String[] args) {
        //创建源文件和目标文件对象,以及输入输出资源
        File src = new File("./a.txt");
        File dest = new File("./b.txt");
        InputStream  inputStream = null;
        OutputStream outputStream = null;

        try {
            inputStream = new FileInputStream(src);
            outputStream = new FileOutputStream(dest);
            
            //利用缓冲区提高效率
            int length = 0; 
            byte[] buffer = new byte[1024];
            //源文件和目的文件全部直接连接,并没有用包装类或者其他类进行操作
            while( (length= inputStream.read(buffer)) != -1){
                outputStream.write(buffer);
            }
            
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            try {
                inputStream.close();
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

处理流:

再演示处理流之前,我们要对流的Java类的结构进行梳理和查看。
在这里插入图片描述
再此之前,我们对基类都进行了基本的实现。基类也就是InputStream、OutputStream、Reader、Writer。还有FileInputStream、
FileOutputStream进行实现。
这里我们对常用的子类进行梳理。


常用处理流

BufferedReader / BufferedWriter 和 BufferedInputStream / BufferedOutputStream

BufferedReader / BufferedWriter 和 BufferedInputStream / BufferedOutputStream 这些处理流都是内置一个缓冲区(大小为8kb)。
前者处理字符流,后者处理字节流。
我们来做个演示。

public static void main(String[] args) {
        File src = new File("./a.txt");
        File dest = new File("./b.txt");
        BufferedReader  bufferedReader = null;
        BufferedWriter  bufferedWriter = null;
        
//			BufferdeInputStream 和 BufferedReader使用几乎一致
//        BufferedInputStream bufferedInputStream = null;
//        BufferedOutputStream bufferedOutputStream = null;

        try { 
        //我们也可以指定缓冲区的大小。
//          bufferedReader = new BufferedReader(new FileReader(src),8192);

            bufferedReader = new BufferedReader(new FileReader(src));
            bufferedWriter = new BufferedWriter(new FileWriter(dest));
        
        	//buffered特殊方法skip
//            bufferedReader.skip(4);
            //如果使用这种方法,那么就会进行跳跃式的读取
            //例如 abcdefghijklmnopq 
            //skip读取:a f k p 间隔4个读取一次
            
            int length = 0 ;
            while((length = bufferedReader.read()) != -1){
                bufferedWriter.write(length);
            }
            //BufferReader特有方法按行读取的方式
//            String line = null;
//            //注意:readLine读取到末尾时返回的是null
//            while((line = bufferedReader.readLine()) != null){
//                bufferedWriter.write(line);
 					//换行
//                 bufferedWriter.newLine();
//            }

            //强制刷新到目标数组中
            bufferedWriter.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
            	//关闭资源
                bufferedWriter.close();
                bufferedReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

我们讲解一下处理流 Buffered输入流 的 read() 方法,read()方法,直接从源文件读取8192字节大小(或者字符大小)的数据,每当我们进行读取时,就直接从 buf 也就是缓存中去读写,如果读取的内容的大小已经大于了buf的长度,那么 buf 直接返回内存中指定需要的数据(内存大小8192,如果4096的内容已经都去了,这一次要读取8192,那么直接将剩余的4096字节的内容读取,再对磁盘进行交互),然后再读取源文件8192大小,返回给buf缓存,再返回给read(),直到满足需求。
write()方法,同样,先把数据写入缓存,当调用write()方法时,直接从缓存中去写入,如果写入的大小大于了 buf 的大小,那么直接flush(),再去将内存更新,再写入。

额外知识:这些类自带的缓冲区,与我们自己创建的缓冲区有什么不同??

在这里插入图片描述
read 方法和 write 方法一样。本质上提升了效率,减少了磁盘的交互。


InputStreamReader / OutputStreamWriter

InputStreamReader / OutputStreamWriter都是字符流的子类,对文本文件的处理是按照1个字符一个字符去输入和读写的。
当我们处理文本文件时,我们按照字符去读写,效率会比字节流相对要高很多

实现:

  public static void main(String[] args) {
        //源文件、目的文件、输入流、输出流资源创建
        File src = new  File("./a.txt");
        File dest = new File("./b.txt");
//        FileInputStream fileInputStream = null;
//        FileOutputStream fileOutputStream = null;
        InputStreamReader inputStreamReader = null;
        OutputStreamWriter outputStreamWriter = null;

        try {
            //我们可以把FileInputStream定义成临时变量节省资源
//            fileInputStream = new FileInputStream(src);
//            fileOutputStream = new FileOutputStream(dest);

            inputStreamReader = new InputStreamReader( new FileInputStream(src));
            outputStreamWriter = new OutputStreamWriter(new FileOutputStream(dest));
            //InputStreamReader 和 OutputStreamWriter在构造器中可以指定字符集
//            inputStreamReader = new InputStreamReader(new FileInputStream(src),"gbk");
//            outputStreamWriter = new OutputStreamWriter(new FileOutputStream(dest),"gbk");
            int length = 0;
            char[] chars = new char[1024];

//            //介绍一些Reader和Writer的重载方法
//            //重载方法Reader
//            //按字符读取
//            inputStreamReader.read();
//            //按字符缓存进行读取(父类Reader方法)
//            inputStreamReader.read(chars);
//            //按字符缓冲数组的大小进行读取(这个大小是参数,可以自定义)
//            //成员变量:缓冲字符数组,从那个位置开始读取,读取多长。
//            inputStreamReader.read(chars,0,chars.length);

//            //重载方法Writer
//            //写入整数
//            outputStreamWriter.write(1);
//            //写入字符串
//            outputStreamWriter.write("asdfg");
//            //写入字符串,从那个位置开始写入,写入多长
//            outputStreamWriter.write("abcdefg",0,4);
//            //写入缓冲字符数组中的数据
//            outputStreamWriter.write(chars);
//            //写入缓冲字符数组,从那里开始写入,写入多长
//            outputStreamWriter.write(chars,0,chars.length);


            while((length = inputStreamReader.read(chars,0,chars.length ))!= -1){
                    outputStreamWriter.write(chars);
            }

            //强制刷新
            outputStreamWriter.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
            //注意,如果我们还创建了FileInputSream,按照创建顺序的相反顺序关闭
                inputStreamReader.close();
                outputStreamWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

为什么已经有了字符流Reader / Writer,要这个处理流 InputStreamReader / OutputStreamReader 干嘛?就为了给定一个字符编码?

现在处理的都是文件处理,但是在开发过程中,更多的是接收网络端源的数据,不是字节就是字符,可能是纯文本。使用传输更多的就是字节流。那么如果我们知道传输的数据是纯文本,我们是不是就可以使用到这个处理流来提高效率了!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值