黑马程序员-java 基础之IO基本类

本文深入探讨Java中IO流的使用,包括字符流与字节流的基本概念、操作流程及应用实例,如文本文件的读写、缓冲区技术、复制文件等。详细解释了流的体系结构、操作步骤与注意事项。

--------------------- android培训java培训、java学习型技术博客、期待与您交流! -------------------

IO

概述:IO用于处理设备间的数据的传输,在java中对于数据的操作时通过流的方式,java用于操作流的对象都存放在IO包中,流按照操作数据分为两种:字节流和字符流,按流向分为2种输出和输入,在IO包中几乎所有类都是一一对应,输出对应输入

流的体系因为功能不同,但是有共性内容,不断抽取,形成继承体系。该体系一共有四个基类,而且都是抽象类。

四大抽象基类

字符抽象基类read writer

字节抽象基类inputstream outputstream

IO继承关系图解

在四大基类中,他们的子类都有一个共性特点,子类后缀名都是父类名,前缀名都是这个子类的功能名称。

字节流:处理字节数据的流对象。设备上的数据无论是图片或者dvd,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。

字符流:因为字符每个国家都不一样,所以涉及到了字符编码问题,那么GBK编码的中文用unicode编码解析是有问题的,所以需要获取中文字节数据的同时+ 指定的编码表才可以解析正确数据。为了方便于文字的解析,所以将字节流和编码表封装成对象,这个对象就是字符流。只要操作字符数据,优先考虑使用字符流体系,字符流基于字节流。

字符流写入数据

步骤

       1因为Writer是抽象类因此需要寻找其子类来进行操作,因为最常见操作是操作文件,因此使用Writer子类中的FileWriter

       2创建FileWriter对象,该对象初始化时必须要有与之相关联的文件,需要传入一个文件作为参数,将该文件路径以字符串形式传入。该文件不存在时,将会创建该文件,该文件存在时将会覆盖,在构造时可以传入一个boolean参数,当参数为true时,不会覆盖文件,而是在文件的末尾,进行数据的书写

       3调用对象的write()方法,将数据写入流中,

       4调用对象的flush()方法,刷新流中的缓存,将数据刷到目的地。之所以需要刷新,是因为字符流对象操作时,操作的是字符,而文件存储的是字节,一个字符对应多个字节,所以需要先把字节缓冲。通过刷新再写入到文件中。

       5掉用对象的close()方法,关闭流资源,因为java自身是不能完成数据的书写,其底层是在调用系统内部资源,进行数据的书写,所以使用完后一定要关闭系统资源,close在关闭资源前,会先调用fiush刷新流,关闭资源后,流不能再使用。flush刷新后流还能继续使用

写入数据代码示例

 

class WriterDemo

{

       public static void main(String[] args)

{

 

              FileWriter fw = null;

              try {

 

                     fw = new FileWriter(d:\\demo.txt");

 

                     fw.write("我要写数据进去了");

 

              }

catch (IOException e)

{

                     System.out.println(e.toString());

              }

finally

{

                     if (fw != null)

{

                     try {

                                   fw.close();

                            }

 catch (IOException e)

{

                                   ..

                                   throw new RuntimeException("关闭失败");

                            }

                     }

              }

 

       }

 

}

字符流读取数据

步骤

       1同写入一样创建一个与文件相关的子类对象FileReader,同时也要相关联一个文件,当该文件不存在时,会报FileNotFoundException异常

       2调用read()方法读取,read方法一次只读取一个字符,当读到文件结尾处时会换回-1

       3调用close()方法读取完成后需要关闭资源。读取操作不需要刷新流。

代码示例

class ReaderDemo

{

       public static void main(String[] args)

{

 

              FileReader fr = null;

              try {

 

                     fr = new FileReader ("d:\\ demo.txt");

 

                     int len=0

                     while((len=fr.read())!=-1)

{

       System.out.println(len);

}

              }

catch (IOException e)

{

                     System.out.println(e.toString());

              }

finally

{

                     if (fr!= null)

{

                     try {

                                   fr.close();

                            }

 catch (IOException e)

{

                                   ..

                                   throw new RuntimeException("关闭失败");

                            }

                     }

              }

 

       }

 

}

注意:路径中的\需要写成\\,不然会被转义。

小练习

文本文件的拷贝

代码示例

class CopyTextTest

{

       public static void main(String[] args)

{

 

              FileReader fr = null;

              FileWriter fw = null;

              try {

                     fr = new FileReader("IO_2.txt");

                     fw = new FileWriter("copytest_2.txt");

                    

                     //创建一个临时容器,用于缓存读取到的字符。

                     char[] buf = new char[1024];//这就是缓冲区。

                    

                     //定义一个变量记录读取到的字符数,(其实就是往数组里装的字符个数 )

                     int len = 0;

                     while((len=fr.read(buf))!=-1){

                            fw.write(buf, 0, len);

                     }

                    

              } catch (Exception e) {

                    

                     throw new RuntimeException("读写失败");

              }finally{

                     if(fw!=null)

                            try {

                                   fw.close();

                            } catch (IOException e) {

                                  

                                   e.printStackTrace();

                            }

                     if(fr!=null)

                            try {

                                   fr.close();

                            } catch (IOException e) {

                                  

                                   e.printStackTrace();

                            }

              }

       }

 

}

字符流缓冲区

缓存区的出现提高了对数据的读写效率

对应类

BufferedWriter

特有方法:

       newline()跨平台的换行符,(推测其出现是因为readLine读取的数据不包含换行)

BufferedReader

特有方法

       readLine()一次读一行,当读到文件末尾时,返回null

缓存区要结合流才可以使用,创建缓存区对象时需要传入对应的流对象,对原有流对象的功能进行了增强

使用缓冲区复制文本文件

代码示例

public class CopyBuf

{

 

      

       public static void main(String[] args) throws IOException

{

 

              FileReader fr = new FileReader("buf.txt");          

              BufferedReader bufr = new BufferedReader(fr);

             

              FileWriter fw = new FileWriter("buf_copy.txt");

              BufferedWriter bufw = new BufferedWriter(fw);

             

              String line = null;

              while((line=bufr.readLine())!=null){

                     bufw.write(line);

                     //因为readLine()读取的字符中不含有换行符

                     //通过BufferedWriternewLine()方法写入一个跨平台的换行符

                     bufw.newLine();

                     bufw.flush();

              }

             

             

              bufw.close();

              bufr.close();

       }

}

BufferReader中通过一个一次读一行的readLine方法对FileReader进行了加强,

我们也可以自定义一个类继承ReaderFileReader进行加强

原理

       定义一个临时容器作为缓冲区,将文件的一行数据写入容器中后,再将容器中的数据取出

原理图解

代码示例

import java.io.*;    

class MyBufferedReader extends Reader 

    private Reader r;//定义接收的流对象 

     MyBufferedReader(Reader r) 

    { 

        this.r=r; 

    } 

    //自定义ReadLine 

    public String myReadLine()throws IOException 

    { 

        //创建一个容器,用来存储一行的字符 

        StringBuilder sb =new StringBuilder(); 

        //一个字符一个字符读取 

        for (int ch=0;(ch=r.read())!=-1 ; ) 

        { 

            if(ch=='\r')//如果遇到换行符,则继续 

                continue; 

            if(ch=='\n')//如果遇到回车符,表示该行读取完毕 

                return sb.toString(); 

            else 

                sb.append((char)ch);//将该行的字符添加到容器 

        } 

       if(sb.length()!=0)//如果读取结束,容器中还有字符,则返回元素 

            return sb.toString(); 

        return null;     } 

      //复写父类中的抽象方法 

    public int read(char[] cbuf, int off, int len) throws IOException 

    { 

        return r.read(cbuf,off,len); 

    } 

 

    //复写父类的close方法 

    public void close()throws IOException 

    {  

        r.close(); 

    } 

 

class  MyBufferedReaderDemo 

    public static void main(String[] args)  

    { 

        MyBufferedReader mbr=null; 

              try 

        { 

            mbr=new MyBufferedReader(new FileReader("BufferedCopyDemo.java")); 

            for (String line=null;(line=mbr.myReadLine())!=null ; ) 

            { 

                System.out.println(line);//显示效果 

            } 

        } 

        catch (IOException e) 

        { 

            throw new RuntimeException("读取数据失败"); 

        } 

        finally 

        { 

            try 

            { 

                if(mbr!=null) 

                    mbr.close(); 

            } 

            catch (IOException e) 

            { 

                throw new RuntimeException("读取流关闭失败"); 

            } 

        }    

    } 

ioBufferedReader有一个子类LineNumberReader,该类有特有的方法,可以获取行号

代码示例

class LineNumberReaderDemo

{

       public static void main(String[] args) throws IOException

{

 

              FileReader fr = new FileReader("IO_2.txt");

              LineNumberReader lnr = new LineNumberReader(fr);

             

              String line = null;

              lnr.setLineNumber(100);

              while((line=lnr.readLine())!=null){

                     System.out.println(lnr.getLineNumber()+":"+line);

              }

             

              lnr.close();

       }

}

装饰设计模式

概述:当想要对已有的对象进行功能增加时,可以自定义类将已有对象传入,在内部基于已有的功能对其进行功能的加强

装饰和继承的区别

     装饰模式比继承要灵活,如果一个类的功能需要加强就定义一个子类,那么当需要加强的功能很多时,体系就会变得十分臃肿,而装饰只需要继承根类,然后将其他子类传入即可。                                       

注意:在定义类时尽量不要使用继承。

字节流

概述:字节流可以操作任何数据,字节流操作时和字符流基本相同,因为字节流操作的是字节,文件中存储也是以字节形式存在,所以字节流对象不需要刷新就可以直接将文件写入文件中,不用再进行刷新动作

小知识点:InputStream中的available()方法可以直接返回文件中字节个数,可以用此方法来确定定义的缓冲数组的大小,但文件较大时,容易造成内存溢出,所以请慎用。

使用字节流对象复制图片

代码示例

import java.io.*; 

class  InputCop 

    public static void main(String[] args)  

    { 

       

        FileInputStream fis=null; 

        FileOutputStream fos=null; 

        try 

        { 

            //关联要复制的文件 

            fis=new FileInputStream("C:/Users/asus/Desktop/1.jpg"); 

            //指定复制的路径 

            fos=new FileOutputStream("C:/Users/asus/Desktop/2.jpg"); 

            //利用数组的读取方式 

            byte[] b=new byte[1024];

              int len=0;

            while ((len=fis.read(b))!=-1)//复制文件的全部数据 

            { 

                fos.write(b,0,len);//粘贴到指定路径 

            } 

        } 

        catch (IOException e) 

        { 

            throw new RuntimeException("图片复制失败"); 

        } 

        finally 

        { 

            try 

            { 

                if(fis!=null) 

                    fis.close();//关闭输入字节流 

            } 

            catch (IOException e) 

            { 

                throw new RuntimeException("读取字节流关闭失败"); 

            } 

            try 

            { 

                if(fos!=null) 

                    fos.close();//关闭输出字节流 

            } 

            catch (IOException e) 

            { 

                throw new RuntimeException("写入字节流关闭失败"); 

            } 

        }  

    } 

字节缓冲区

概述,与字符缓冲区作用相同,都是为了提高读写效率。

特点:

       read():会将字节byte类型提升为int型,因为read方法读到末尾时会返回-1,这样有可能当连续读到8个二进制1时,返回-1,结束了读取动作。为了避免这种情况将byte提升为int,将读到的byte&255就可以避免这种情况了。

       write():会将int型强转为byte

转换流:

概述:是字节流和字符流之间的桥梁,该流对象中可以对读取到的字节数据进行指定编码表的编码转换,当字节与字符有转换动作时,流操作的数据需要进行编码表的指定时使用。

对应对象

       InputStreamReader

       OutputStreamWriter

标准输入输出

System.out:对应的是标准输出设备,也就是控制台

System.in:对应的是标准输入设备,也就是键盘

读取键盘录入代码示例

import java.io.*; 

class TransStreamDemo  

    public static void main(String[] args)throws IOException 

   {       //获取键盘录入对象。 

        //InputStream in=System.in; 

        //将字节流对象转成字符流对象,使用转换流。 

        //InputStreamReader isr=new InputStreamReader(in); 

        //为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader 

        //BufferedReader br=new BufferedReader(isr); 

 

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

       

         BufferedWriter bw =new BufferedWriter(new OutputStreamWriter(System.out)); 

 

        String s=null; 

        while((s=in.readLine())!=null) 

        { 

            if("over".equals(s)) 

                break; 

            bw.write(s.toUpperCase());//写入数据 

            bw.newLine();//换行 

            bw.flush();//刷新 

             

        } 

        bw.close();//关闭流资源 

        in.close(); 

    } 

流操作规律

通过三个明确来完成:

4.1 明确源和目的。

源:输入流。InputStream Reader

目的:输出流。OutputStream Writer

4.2 操作的数据是否是纯文本。

是:字符流

否:字节流

4.3 当体系明确后,再明确要使用哪个具体的对象。通过设备来进行区分:

源设备:内存,硬盘,键盘

目的设备:内存,硬盘,控制台

举例体现

需求将一个文本文件中数据存储到另一个文件中。复制文件。

1)源:因为是源,所以使用读取流:InputStreamReader

明确体系:是否操作文本:是,Reader

明确设备:明确要使用该体系中的哪个对象:硬盘上的一个文件。Reader体系中可以操作文件的对象是FileReader

是否需要提高效率:是,加入Reader体系中缓冲区 BufferedReader.

FileReader fr = new FileReader("a.txt");

BufferedReader bufr = new BufferedReader(fr);

2)目的:输出流:OutputStreamWriter

明确体系:是否操作文本:是,Writer

明确设备:明确要使用该体系中的哪个对象:硬盘上的一个文件。Writer体系中可以操作文件的对象FileWriter

是否需要提高效率:是,加入Writer体系中缓冲区 BufferedWriter

FileWriter fw = new FileWriter("b.txt");

BufferedWriter bufw = new BufferedWriter(fw);

需求:想要把录入的数据按照指定的编码表(UTF-8)(默认编码表是GBK),将数据存到文件中。

目的:OutputStream Writer

是否是存文本?是!Writer

设备:硬盘上的一个文件。使用 FileWriter。——但是FileWriter是使用的默认编码表:GBK。而存储时,需要加入指定编码表utf-8。而指定的编码表只有转换流可以指定。所以要使用的对象是OutputStreamWriter

该转换流对象要接收一个字节输出流,而且还可以操作的文件的字节输出流:FileOutputStream

OutputStreamWriter osw =new OutputStreamWriter(newFileOutputStream("d.txt"),"UTF-8");

需要高效吗?需要,BufferedWriter

BufferedWriter bufw = new BufferedWriter(osw);

 --------------------- android培训java培训、java学习型技术博客、期待与您交流! -------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值