【黑马】程序员————IO(一)流概述、字符流、字节流

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流!-----

一、流概述和图表。

 

1、IO(Input/Output)概述:

 

    流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。 JAVA通过流的方式,对数据进行操作。操作流的对象 在java.io.*包中。

   IO流的分类:  A.  字符流、字节流

            B.  输入流、输出流。

 

2、 IO知识框图:

 

 

注释
File 文件类
RandomAccessFile 随机存取文件类
InputStream 字节输入流
OutputStream 字节输出流
Reader 字符输入流
Writer 字符输出流

 

【黑马】程序员————IO(一)流概述、字符流、字节流_第1张图片

注:此图系转载。用以参考。

 

二、字符流。

 

1、简介:

  Java 流在处理上分为字符流和字节流。字符流处理的单元为 2 个字节的 Unicode 字符,分别操作字符、字符数组或字符串,而字节流处理单元为 1 个字节,操作字节和字节数组。Java 内用 Unicode 编码存储字符,字符流处理类负责将外部的其他编码的字符流和 java 内 Unicode 字符流之间的转换。而类 InputStreamReader 和 OutputStreamWriter 处理字符流和字节流的转换。字符流(一次可以处理一个缓冲区)一次操作比字节流(一次一个字节)效率高。

 

2.字符流的读取写入

 

2.1字符流的写入步骤:

 创建FileWriter对象,明确操作的文件。

   调用对象.write();方法将“字符串”写入文件。

   调用.close();方法刷新流,并且关闭流。

 

   Tips:如果指定位置不存在会触发IOException需要进行异常处理。

 

<示例1>向文件“abc.txt”中续写内容:

import java.io.*;
public class IoTest1 {
    
    public static void main(String[] args)  {
        
        //创建字符流对象fw,“abc.txt”为所要操作的文件
        FileWriter fw = null;
        try {
            //写入字符
            fw = new FileWriter("D:\\abc.txt",true);
            fw.write("从前有座山\r\n山里有座庙");
        }
        catch(IOException e) {
            System.out.println(e.toString());
        }
        
        finally {
            try{
                if(fw != null)
                    //刷新流
                    fw.flush();
            }
            catch(IOException e) {
                System.out.println(e.toString());
            }
        }    
    }

}

 2.2字符流的读取步骤:

  两种方式:单个读取,利用数组存取。

  创建FileReader对象并指定要操作的文件,(文件不存在则会触发FileNotFoundException异常);

  调用FileReader对象的.read();方法,读取单个字符,可结合循环操作

  .close();关闭资源。

  <示例2> 

import java.io.*;
public class IoTest1 {
    
    public static void main(String[] args) {
        
        
        FileReader fr = null;
        try {
            //创建字符流读取对象,指定所要操作的文件
            fr = new FileReader("d:\\abc.txt");
            int i = 0;
            //循环读取字符的编码值并赋予变量i
            while(( i = fr.read())!= -1) {
                //强制转换i为char型并打印
                System.out.println((char)fr.read());
            }
            
            
        } catch(IOException e) {
            
            e.printStackTrace();
        }
        
    finally  {
        try {
            fr.close();
        } catch(IOException e) {
            e.printStackTrace();
        }
    }

 

//字符数组    读取
import java.io.*;
public class IoTest1 {
    
    public static void main(String[] args) {
        
        
        FileReader fr = null;
        char[] ch = new char [12];
        try {
            //创建字符流读取对象,指定所要操作的文件
            fr = new FileReader("d:\\abc.txt");
            int i = 0;
            //循环读取字符的编码值并赋予变量i
            while(( i = fr.read(ch))!= -1) {
                //强制转换i为char型并打印
                System.out.println("个数="+i+new String(ch));
            }
            
            
        } catch(IOException e) {
            
            e.printStackTrace();
        }
        
    finally  {
        try {
            fr.close();
        } catch(IOException e) {
            e.printStackTrace();
        }
    }
        
            
    
        
    
    }
}

2.3 小练习:

字符流输入输出综合运用:文件拷贝。

/*在C盘创建一个文件,用于D存储盘文件中的数据。
 * 定义读取流和C盘文件关联。
 * 通过不端读写完成数据存取。
 * 关闭资源
 */
import java.io.*;
public class IoTest1 {
    
    public static void main(String[] args) {
        
        copyFile();
    }
    
    public static void copyFile() {
        //创建字符输入输出流对象
        FileReader fr = null;
        FileWriter fw = null;
        
        try{
            fr = new FileReader("d:\\abc.txt");//关联文件
            fw = new FileWriter("c:\\abc_copy.txt");//关联文件
            
            int i = 0;
            char[] temp = new char[1024]; //定义存放读取文件的字符数组
            while((i = fr.read(temp))!= -1) {
            
                fw.write(temp,0,i);
                
            }
            
            
            
        }catch(IOException e) {
            throw new RuntimeException("读写失败");
        }
        
        finally {
            try { 
                if(fr!=null)
                fr.close();
                
            } catch(IOException e) {
                e.printStackTrace();
            }
            finally {
                try{
                    if(fw!=null) 
                        fw.close();
                }catch(IOException e){}
            }
            
        }
    }
        
        
    
}

 3、字符流的缓冲区


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

对应类:BufferedReaderBufferedWriter。

在流的基础上对流的功能进行了增强。

缓冲区需要结合流才可以使用。

步骤:以BufferedWriter为例:1、创建字符写入流对象。2、创建缓冲区对象,并将字符流对象作为缓冲区构造函数的参数。3、bw.newLine();缓冲区的newLine()方法可以提供         换行功能。4、bw.flush.只要用到缓冲区就要刷新。5、bw.close();关闭缓冲区(同时关闭了流对象)。

 

个人理解:如果把字符流理解为 水流,则缓冲区可以理解位一个装水的容器。 因此缓冲区操作的数据类型是 String.

对比:  FileWriter fw = new FileWriter();

     BufferedWriter bwfw = new BufferedWriter(fw);

     fw.read();

     bwfw.readLine();

       

              int i = 0;    while((i = fw.read())! = -1))

     String s = null;  while((s = bwfw.readLine()) != null ) //缓冲区一次可以读一行。

 

<示例3>通过缓冲区复制文件,对比字符流复制文件。

/*
 * 通过缓冲区复制文件。
 */
import java.io.*;
public class IoTest1 {
    
    public static void main(String[] args)throws IOException {
        
        BufferedReader br = null;
        BufferedWriter bw = null;
        String s = null;
        
        try {
            br = new BufferedReader(new FileReader("D:\\abcd.txt"));//创建缓冲区读取对象,关联文件
            bw = new BufferedWriter(new FileWriter("C:\\abcd_copy.txt"));//创建缓冲区写入对象,关联文件
            
            while((s = br.readLine())!= null) { //判断文件是否还留有文本
                
                bw.write(s);                    //将读取的字符串写入目标文件
                bw.flush();                        //刷入文件
            }
            
        } catch(IOException e) {
            throw new RuntimeException("erro");
        }
        
        finally {
            
            try {
                if(br != null) 
                    br.close();
                }  catch(IOException e) {
                    throw new RuntimeException("erro");
                
            }
            finally {
                
                try {
                    if(bw != null) 
                        bw.close();
                    }  catch(IOException e) {
                        throw new RuntimeException("erro");
                    
                }
            }
        }
        
    
    }
}
        

 3.1自定义Buffered

  如果把字符流理解为 水流,则缓冲区可以理解位一个装水的容器。 在此理解上可以自定义Buffered。

<示例4>

/*
 * 自定义Buffered
 */
import java.io.*;
public class IoTest1 {
    

    public static void main(String[] args) {
        
        
    }    
}

class MyBufferedReader {
    
    
    private FileReader fr; 
    MyBufferedReader(FileReader fr) { //构造函数(参数类型:FileReader)
        this.fr = fr;
    }
    
    public String myReadLine () throws IOException {  //  自定义readLine方法,返回一个字符串
        //利用StringBuilder容器存储读取的字符串
        StringBuilder sb = new StringBuilder();
        
        int i = 0;
        while((i = fr.read()) != -1) {
            if(i == '\r')    
                continue;
            if(i == '\n')   //循环到换行符时返回String字符串。
                return sb.toString();
            else
                sb.append((char)i);
            
        }
        return null;    
    }
    
    public void myClose() throws IOException {
        fr.close();
    }
}

3.2 装饰设计模式:

以上的例子中,MyBufferedReader是FileReader的一个增强类,这种设计模式被称作是装饰设计模式。

当相要对已有的对象进行功能增强时,基于已有的功能,提供扩展功能,这个自定义的类成为装饰类。

装饰类通常通过构造方法接受被装饰的类。

例如

class Person {

    public void chifan() {

      sop("吃馒头");

    }

  }

class SuperPerson {

  SuperPerson(Person p) {

      this.p = p;

  }

 

  扩展......

  public void superChifan() {

    p.chifan();

    sop("吃东坡肉");

    sop("吃烧鸡");

  } 

}

 

装饰类和继承的区别:

 

BufferedReader bf = new BufferReader(FileReader fr);//这就是一个典型的装饰类例子。可以传递(各种Reader参数)。

装饰类通过多态的形式极大的提高了扩展性。优化了体系结构。

  1)比继承灵活,避免了继承体系结构的臃肿(不必包含被装饰类的所有成员属性和方法)。

  2)装饰类具备的功能和已有的功能是相同的,提供了更强的功能。所以装饰类和被装饰类一般都属于一个体系。

  3)继承结构转为组合结构。

 

4.LineNumberReader extends BufferedReader:

LineNumberReader是BufferedReader的一个子类,扩展了功能,提供了以下扩展方法:

  1)getLine();//获取行号

  2)setLine();//设置行号(从N行开始)

 <示例5> MyLineNumberReader

class MyLineNumberReader {
    
    
    private FileReader fr; 
    private int lineNumber;
    MyLineNumberReader(FileReader fr) { //构造函数(参数类型:FileReader)
        this.fr = fr;
    }
    
    public String myReadLine () throws IOException {  //  自定义readLine方法,返回一个字符串
        
        lineNumber++;
        
        //利用StringBuilder容器存储读取的字符串
        StringBuilder sb = new StringBuilder();
        
        int i = 0;
        while((i = fr.read()) != -1) {
            if(i == '\r')    
                continue;
            if(i == '\n')   //循环到换行符时返回String字符串。
                return sb.toString();
            else
                sb.append((char)i);
            }
        
        if(sb.length() != 0)   //如果最后一个字符不是换行符的情况
            return sb.toString();
        else    
            return null;    
    }
    
    public void myClose() throws IOException {
        fr.close();
    }
    
    public int getLineNumber() {   //获取行号方法
        return lineNumber;
    }
    public void setLineNumber(int n) {  //设置行号方法
        lineNumber = n;
        
    }
}

 

三、字节流。

 

1、字节流的特点 

  1)相关类:InputStream/OutputStream

  2)字节流操作的是字节Byte型数据,写入和读取的都是字节,所以在写入时候,要以字节的形式写入。例如:FileOutputStream fos = new.....; fos.write("abcd".getBytes();)

  3)int available方法:可以理解为获取文件的字节总数(文件大小)

   FileInputStream fis = new FileInputStream("abc.txt");

   byte[] temp = new byte[fis.available()];

   fis.read(buf); //把abc.txt中的所有字节写入temp数组中,大小刚好为文件的字节总数。可以不再写循环。

   注意,这个方法容易引起数据溢出,一般还是用byte[] temp = byte[1024];  (1024bytes = 1KB ;1024*1024bytes = 1MB)  

 

<示例6> 拷贝一个图片,太6了!

/*
 * COPY图片!
 */
import java.io.*;
public class IoTest1 {
    

    public static void main(String[] args) {
        
        FileInputStream fis = null;
        
        FileOutputStream fos = null;
        
        byte[] temp = null;
        try{
            
            fis = new FileInputStream("F:\\a.jpg"); //建立字节输入流对象,关联文件
            fos = new FileOutputStream("c:\\b.jpg"); //字节输出流对象,关联文件
            temp = new byte[fis.available()];   //定义字节数组,大小为a.jpg文件的字节数
            fis.read(temp);
            fos.write(temp);
            
        } catch(IOException e) {
            
            new RuntimeException("erro");
        }
        
        finally {
            try {
                    fis.close();
            }catch (IOException e) {
                new RuntimeException("erro");
            }
            finally {
                try {
                        fos.close();
                }catch (IOException e) {
                    new RuntimeException("erro");
                }
            }
        
        }    
    }
}

 

2、字节流的缓冲区   :   BufferedInputStream/BufferedOutputStream

  用法示例:BufferedInputStream bis = new BuffferedInputStream(new FileInputStrem(“C:\\爱要克制.mp3”))

     字节流的缓冲区的特点:

  read()方法:虽然字节流操作的数据是Byte(8bits),但是BufferedInputStream的read方法返回的是int型的数据,32bits

  原因,在“拷贝MP3文件”的例子中,如果遇到字节数据是11111111时,即使转换为int型数据,高位补1后返回值仍为-1,因此需要&255

  (&0000000 000000000 00000000 11111111),高位补0

  write()方法则将转换后的int型数据 保留低8位写入。

 

  对缓冲区的几点个人理解:

  缓冲区相比普通的流存取,由于在内存中划分了缓冲的区域,所以使得存取的效率提高。

  read(byte[] b)我认为也是一种缓冲方式。

  Buffered中的read()方法,应该也是以数组的方式在内存中划分了区域,

  因此,是否BufferedInputStream中的read()方法 的原理 其实是  FileInputStream类的read(Byte[] b)方法呢?

<示例7> 自定义字节流缓冲区  MyBufferedInputStream 

 

 

/*
自定义字节流读取缓冲区
思路:

 
*/
import java.io.*;
class MyBufferedInputStream
{
       privateInputStream in;
       privatebyte[] by=new byte[2048];
       privateint count=0,pos=0;
       MyBufferedInputStream(InputStreamin) {
       
              this.in=in;
       }

 
      
       public int myRead()throws IOException
       {
              //通过in对象读取硬盘上数据,并存储by中。
              //存储在数组中的数据被读取完,再通过in对象从硬盘上读取数据
              if(count==0)
              {
                     count=in.read(by);
                     if(count<0)
                            return -1;

 
                     pos=0;//初始化指针
                     byteb=by[pos];

                     count--;//每被读一个字节,表示数组中的字节数少一个
                     pos++;//指针加1
                     returnb&255;//返回的byte类型提升为int类型,字节数增加,且高24位被补1,原字节数据改变。
                                          //通过与上255,主动将byte类型提升为int类型,将高24位补0,原字节数据不变。
                                          //而在输出字节流写入数据时,只写该int类型数据的最低8位。
              }
              elseif(count>0)//如果数组中的数据没被读取完,则继续读取
              {
                     byteb=by[pos];
 
                     count--;
                     pos++;
                     returnb&0xff;
              }
              return-1;
       }
       
       //自定义关闭资源方法
       publicvoid close()throws IOException
       {
              in.close();
       }
}

 
//测试自定义输入字节流缓冲区
class MyBufferedCopyMp3
{
       publicstatic void main(String[] args) 
       {
              longstart=System.currentTimeMillis();
              //利用字节流的缓冲区进行复制
              copy_2();
              longend=System.currentTimeMillis();
              System.out.println("复制共用时:"+(end-start)+"毫秒");
       }
       //使用字节流的缓冲区进行复制
       publicstatic void copy_2()
       {
              BufferedOutputStreambout=null;
              MyBufferedInputStreambin=null;
              try
              {
                     //关联复制文件输入流对象到缓冲区
                     bin=newMyBufferedInputStream(new FileInputStream("C:/Users/asus/Desktop/一样的月光.mp3"));
                     //指定文件粘贴位置的输出流对象到缓冲区
                     bout=newBufferedOutputStream(new FileOutputStream("C:/Users/asus/Desktop/一样的月光2.mp3"));
                     intby=0;
 

                     while((by=bin.myRead())!=-1)
                     {
                            bout.write(by);//将缓冲区中的数据写入指定文件中
                     }
              }
              catch(IOException e)
              {
                     thrownew RuntimeException("MP3复制失败");
              }
              finally
              {
                     try
                     {
                            if(bin!=null)
                                   bin.close();//关闭输入字节流
                     }
                     catch(IOException e)
                     {
                            thrownew RuntimeException("读取字节流关闭失败");
                     }
                     try
                     {
                            if(bout!=null)
                                   bout.close();//关闭输出字节流
                     }
                     catch(IOException e)
                     {
                            thrownew RuntimeException("写入字节流关闭失败");
                     }
              }
       }
}

 

 

你可能感兴趣的:(【黑马】程序员————IO(一)流概述、字符流、字节流)