zoukankan      html  css  js  c++  java
  • java_IO

    1 ,IO流概述

    2,输出流,输入流方法的应用

    3,缓冲区的应用

    4,转换流的应用

    5,流操作规律

    java把所有传统的流类型都放在java.io包中,用以实现输入/输出功能。
    1.1,输出流和输入
    ->输入流:只能从中读取数据,而不能向其写入数据
    ->输出流:只能向其写入数据,而不能从中读取数据
    java的输入流主要由InputStream和Reader作为基类,而输出流则主要由OutputStream和Writer作为基类,他们都是抽象基类,无法直接创建对象。
    1.2,字节流和字符流
    字节流和字符流的用法几乎一样,区别在于字节流和字符流所操作的数据单元不同。
    字节流主要由InputStream和OutputStream作为基类,而字符流则主要由Reader和Writer作为基类。
    字符流两个基类:融合了编码表,可以指定编码表。

    2,输出流,输入流方法的应用
    2.1在Reader里主要有读的3个方法。
    public int read() 读取单个字符 
    返回:如果已到达流的末尾,则返回 -1
    public int read(char[] cbuf)  将字符读入数组
    返回:读取的字符数,如果已到达流的末尾,则返回 -1
    public abstract int read(char[] cbuf,int off,int len) 将字符读入数组的某一部分
    返回:读取的字符数,如果已到达流的末尾,则返回 -1 

    InputStream类里还有一个 available()方法,具体用法如下

    public static void readFile_3()throws IOException
    	{
    		FileInputStream fis = new FileInputStream("fos.txt");
    		
    		//int num = fis.available();//字节个数
    		byte[] buf = new byte[fis.available()];//定义一个刚刚好的缓冲区不用再循环了
    
    		fis.read(buf);
    		System.out.println(new String(buf));
    
    		fis.close();
    	}



    下面代码是对上述方法的简单使用:

    class FileReadDemo 
    {
    	public static void main(String[] args) 
    	{
    
    	}
    		
    	public static void method_1()
    	{
    		//创建一个文件读取流对象,和指定名称的文件相关联。
    		//要保证该文件是已经存在的,如果不存在,会发生异常,FileNotFoundException
    		FileReader fr = new FileReader("demo.txt");
    		int ch = 0;
    		while((ch=fr.read())!=-1)//已到末尾返回-1
    		{
    			System.out.println("ch="+(char)ch);
    		}
    		fr.close();
    	}
    	
    	public static void method_2()
    	{
    		FileReader fr = new FileReader("demo.txt");
    		char[] buf = new char[1024];//定义一个字符数组,用于存储读到的字符
    		int num = 0;
    		while((num=fr.read(buf))!=-1)
    		{											//无论buf数组里多少字符,长度都是1024
    			System.out.print(new String(buf,0,num));//new String(buf,0,num) String类的构造器,可将数组封装成字符串
    		}
    		fr.close();
    	}
    }

    注意:程序里打开的文件IO资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件IO资源。


    2.2OutputStream和Writer也非常相似,两个流都提供了如下3个方法
    ->void write(int c) 写入单个字符
    ->public void write(byte[]/char[] buf) 将字节数组 /字符数组中的数据输出到指定流中
    ->public void write(byte[]/char[] buf,int off,int len)  将字节数组/字符数组中从off位置开始,长度为len的字节/字符输出到输出流中。
    因为字符流直接以字符作为操作单位,所以Writer可以用字符串来代替字符数组,即以String对象作为参数。Writer里还包含如下两个方法。
    void write(String str);将str字符串里包含的字符输出到指定输出流中。
    void write(String str,int off,int len): 将str字符串里从off位置开始,长度为len的字符输出到指定输出流中。


    下面用代码来简单说明输出流的用法:

    class FileWriterDemo
    {
    	public static void main(String[] args) 
    	{
    		
    		//创建一个FileWriter对象,该对象一被初始化,就必须要明确被操作的文件
    		//而且该文件会被创建到指定的目录下。如果该目录下已有同名文件,将被覆盖。
    		//其实该步就是在明确数据要存放的目的地
    		FileWriter fw = new FileWriter("demo.txt");
    		
    		//传递一个true参数。代表不覆盖已有的文件,并在已有文件的末尾处进行数据续写。
    		//FileWriter fw = new FileWriter("demo.txt",true);
    		
    		//调用writer方法,将字符串写入到流中。
    		fw.write("asdasd");
    		
    		//刷新流对象中的缓冲中的数据。
    		//将数据刷到目的地中、
    		//fw.flush();
    		
    		
    		//关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据。
    		//将数据刷到目的地中
    		//和flush区别;flush刷新后,流可以继续使用,close刷新后,会将流关闭。
    		fw.close();
    	}
    }
    class FileWriterDemo2
    {
    	public static void main(String[] args) 
    	{
    		FileWriter fw = null;//在代码块外创建引用才能在所有代码块中使用引用。
    		try
    		{
    			fw = new FileWriter("demo.txt");
    		
    			fw.write("ddsffdhgjf");
    		}
    		catch (IOException e)
    		{	
    			System.out.println("catch:"+e.toString());
    		}
    		finally
    		{	
    			try
    			{
    				if(fw!=null)//如果没有判断,可能产生空指针异常
    				fw.close();//也会抛出异常
    			}
    			catch (IOException e)
    			{
    				System.out.println(e.toString());
    			}
    		}
    	}
    }

    输出流和输入流结合的简单应用:

    将一个文本文件里的内容复制到另一个文件里。

    class CopyTest
    {
    	public static void main(String[] args) throws IOException
    	{
    		
    	}
    	//从才盘读一个字符就往d盘写一个字符
    	public static void copy_1()throws IOException
    	{
    		//创建目的地
    		FileWriter fw = new FileWriter("RuntimeDemo_copy.txt");
    
    		//与已有文件关联
    		FileReader fr = new FileReader("RuntimeDemo.java");
    
    		int ch = 0;
    
    		while((ch=fr.read())!=-1)
    		{
    			fw.write(ch);
    		}
    		fw.close();
    		fr.close();		
    	}
    	
    	public static void copy_2()
    	{
    		FileWriter fw = null;
    		FileReader fr = null;
    		try
    		{
    			fw = new FileWriter("SystemDemo_copy.txt");
    			fr = new FileReader("SystemDemo.java");
    
    			char[] buf = new char[1024];
    
    			int len = 0;
    
    			while((len=fr.read(buf))!=-1)
    			{
    				fw.write(buf,0,len);
    			}
    		}
    		catch (IOException e)
    		{
    			throw new RuntimeException("读写失败");
    		}
    		finally
    		{
    			if(fr!=null)
    				try
    				{
    					fr.close();
    				}
    				catch (IOException e)
    				{
    				}
    			if(fw!=null)
    				try
    				{
    					fw.close();
    				}
    				catch (IOException e)
    				{
    		
    				}
    		}
    	}
    }
    
    </pre><p><span style="font-size:18px">3,缓冲区的应用</span></p><p><span style="color:#993300">缓冲区的出现是为了提高流的操作效率而出现的。</span></p><span style="color:#993300">所以在创建缓冲区之前,必须要先有流对象。</span>BufferedWriter该缓冲区中提供了跨平台的换行符。<span style="color:#ff0000">newLine();</span><p>BufferedReader该缓冲区提供了更为高效率的读方法</p><p><span style="color:rgb(255,0,0)">readLine()</span></p><p><span style="color:#ff0000">注意:</span>当返回null时。表示读到文件末尾。readLine方法返回的时候只返回回车符之前的数据内容。并不返回回车符。</p><p></p><p><span style="font-size:14px; color:#ff0000">readLine的原理:</span></p><p><span style="white-space:pre"></span>无论是读一行,读取多个字符,其实最终都是在硬盘上一个一个读取。所以最终使用的还是read方法一次读一个的方法。</p><p><span style="white-space:pre"></span>在readLine方法里封装了一个存放字符的容器,当读到一行的时候,返回这个容器的字符。</p><p>代码实现如下:</p><p></p><p></p><p><span style="white-space:pre"></span></p><pre name="code" class="java">class MyBufferedReader extends Reader
    {
    	private Reader r;
    	MyBufferedReader(Reader r)
    	{
    		this.r = r;
    	}
    
    	// 可以一次读一行数据的方法。
    	public String myReadLine()throws IOException
    	{
    		//定义一个临时容器,原BufferReader封装的是字符数组。
    		//为了方便,定义一个StringBuilder容器,因为最终还是要将数据变成字符串。
    		StringBuilder sb = new StringBuilder();
    		int ch = 0;
    		while((ch=r.read())!=-1)
    		{
    			if(ch=='
    ')
    				continue;
    			if(ch=='
    ')
    				return sb.toString();
    			else
    			sb.append((char)ch);
    		}
    		if(sb.length()!=0)//防止有的行没有换行符,则在缓冲区里的数据就不能被返回。
    			return sb.toString();
    		return null;
    	}

    通过缓冲区来进行文本文件的复制,可以提高效率。

    class CopyTextByBuf
    {
    	public static void main(String[] args) 
    	{
    		BufferedReader bufr = null;
    		BufferedWriter bufw = null;
    
    		try
    		{
    			bufr = new BufferedReader(new FileReader("BufferedWriterDemo.java"));
    			bufw = new BufferedWriter(new FileWriter("bufwriter_Copy.txt"));
    
    			String line = null;
    
    			while((line=bufr.readLine())!=null)
    			{
    				bufw.write(line);
    				bufw.newLine();
    				bufw.flush();
    			}
    		}
    		catch (IOException e)
    		{
    			throw new RuntimeException("读写失败");
    		}
    		finally
    		{
    			try
    			{
    				if(bufr!=null)
    					bufr.close();
    			}
    			catch (IOException e)
    			{
    				throw new RuntimeException("读取关闭失败");
    			}
    				
    			try
    			{
    				if(bufw!=null)
    					bufw.close();
    			}
    			catch (IOException e)
    			{
    				throw new RuntimeException("写入关闭失败");
    			}
    			
    		}
    	}
    }


    BufferedReader有一个子类为LineNumeberReader  表示跟踪行号的缓冲字符输入流,

    里面定义了setLineNumber(int)和getLineNumber(),他们分别可用于设置和获取当前行号。


    通过字节流缓冲区复制一段MP3

    <pre name="code" class="java">class CopyMp3
    {
    	public static void main(String[] args) throws IOException
    	{
    		long start = System.currentTimeMillis();
    		copy_2();
    		long end = System.currentTimeMillis();
    		System.out.println(end-start+"毫秒");
    	}
    
    	//通过字节流的缓冲区完成复制。
    	public static void copy_1()throws IOException
    	{
    		BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("c:\0.mp3"));
    		BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\1.mp3"));
    
    		int by = 0;
    		while((by=bufis.read())!=-1)//一个一个往外取
    		{
    			bufos.write(by);
    		}
    
    		bufos.close();
    		bufis.close();
    	}
    	public static void copy_2()throws IOException
    	{
    		MyBufferedInputStream bufis = new MyBufferedInputStream(new FileInputStream("c:\0.mp3"));
    		BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\2.mp3"));
    
    		int by = 0;
    		while((by=bufis.myRead())!=-1)//一个一个往外取 
    		{
    			bufos.write(by);//<span style="font-family: Arial, Helvetica, sans-serif;">InputStream的write是将指定字节写到输出流从缓冲区里面读,</span>只写最低8位,不然最后复制的文件大于原文件4倍
    		}
    
    		bufos.close();
    		bufis.myClose();
    	}
    }
    //自定义字节缓冲区
    class MyBufferedInputStream
    {
    	private InputStream in;
    	private byte[] buf = new byte[1024];
    
    	private int pos = 0,count = 0;
    	MyBufferedInputStream(InputStream in)
    	{
    		this.in = in;
    	}
    	//一次读一个字节,从缓冲区(字节数组)获取、
    	public int myRead()throws IOException//为什么返回的不是byte,被提升。由4个字节存放
    			//在读到8个1时为了避免和判断结束标记-1相同,可以在保留8个1的情况下,在前面补0,那就不是-1是255.在判断的时候就不会跳出while循环
    	{
    		//通过in对象读取硬盘上的数据,并存储 buf中。
    		if(count==0)
    		{
    			count = in.read(buf);//count为0的时候才读取数据到缓冲区
    			if(count<0)
    				return -1;
    			pos = 0;
    			byte b = buf[pos];
    	
    			count--;
    			pos++;
    			return b&255;//在前面补0,保留最低8位
    		}
    		else if(count>0)//count大于0的时候,说明缓冲区还有数据,不需要读,只需要取
    		{
    			byte b = buf[pos];
    	
    			count--;
    			pos++;
    			return b&255;
    		}
    		return -1;
    	}
    	public void myClose()throws IOException
    	{
    		in.close();
    	}
    }
    


    
     为什么是0kb?因为读的第一个字节是-1, MP3是2进制数据,读1个字节,读了8个2进制位,连续读了8个1,是-1. 1111-1111 -->提升为一个int类型,为4个字节。那还不是-1吗?是-1的原因是因为在8个1前面补的是1导致的 那么我只要在前面补0,既可以保留原字节数据不变,又可以避免-1的出现 怎么补0呢。  1111-1111&  00000000 00000000 00000000 1111-1111  --------------------------------------read方法在做提升,write在做强转
    

    4,转换流的应用

    转换流什么时候使用,字符和字节之间的

    4.1读取键盘录入System.out:对应的是标准的输出设备,控制台。System.in:对应的是标准的输入设备,键盘。

    class ReadIn
     {
    	public static void main(String[] args) throws IOException
    	{
    		InputStream in = System.in;
    		int ch = 0;
    		StringBuilder sb = new StringBuilder();//可变长度
    		while(true)
    		{		
    			int ch = in.read();
    			if(ch=='
    ')
    				continue;
    			if(ch=='
    ')
    			{
    				String s = sb.toString();
    				if("over".equals(s))
    					break;
    				System.out.println(s.toUpperCase());//变成大写
    				sb.delete(0,sb.length());//打印完一次要清空缓冲区
    			}
    			else
    				sb.append((char)ch);
    
    		}
    		in.close();
    	}
    }
    

    4.2转换流

    以上键盘录入一行数据并打印其大写,发现其实就是一行数据的原理。也就是readLine方法

    能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?

    readLine方法是字符流BufferedReader类中方法。
    而键盘录入的read方法是字节流InputStream的方法。
    那么能不能将字节流转成字符流在使用字符流缓冲区的readLine方法呢?通过转换流可以实现

    具体用法见以下代码

    import java.io.*;
    class TransStreamDemo 
    {
    	public static void main(String[] args) throws IOException
    	{
    		//获取键盘录入对象,字节流
    		InputStream in = System.in;
    		
    		//将字节流对象转成字符流对象,使用转换流。InputStreamReader
    		InputStreamReader isr = new InputStreamReader(in);
    
    		//为了提高效率,将字符流进行缓冲技术高效操作。使用BufferedReader
    
    		BufferedReader bufr =  new BufferedReader(isr);
    
    		//BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
    		//键盘录入就输以上语句,
    		
    		//字符流通向字节流的桥梁
    		OutputStream out = System.out;
    		OutputStreamWriter osw = new OutputStreamWriter(out);
    		BufferedWriter bufw = new BufferedWriter(osw);
    
    		String line = null;
    
    		while((line=bufr.readLine())!=null)
    		{
    			if("over".equals(line))
    				break;	
    			//System.out.println(line.toUpperCase());
    			bufw.write(line.toUpperCase()+);//要刷新,数据才能出来。
    			bufw.newLine();//换行。
    
    			bufw.flush();
    		}
    		bufr.close();
    	}
    }

    5,流操作规律

    5.1通过两个明确来完成。

    1,明确源和目的。
    源:输入流。
    目的:输出流。
    2,明确操作的数据是否是纯文本。
    是:用字符流。
    不是:字节流
    3,当体系明确后,在明确要使用哪个具体的对象。
    通过设备进行区分:
    源设备:内存,硬盘,键盘。
    目的设备:内存,硬盘,控制台。

    例子:

    1,将一个文本文件中数据存储到另一个文件中,复制文件。
    源:因为是源所以选择读取流,InputStream Reader
    操作文本文件,所以选择Reader
    设备是硬盘上的文件,所以选择FileReader

    BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
    目的:OutputStream Writer
      操作文本文件,所以选择Writer
      设备是硬盘上的文件,所以选择FileWriter

    BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
    2,将键盘录入的数据保存到一个文件中。
    源:InputStream Reader
    操作纯文本 Reader
    设备:键盘,对应对象时System.in
    System.in对应的是字节流,为了操作键盘的文本数据方便,
    转成字符流按照字符串操作是最方便的。
    所以既然明确了Reader。那么将System.in转换成Reader。
    用了Reader体系中转换流,InputStreamReader


    InputStreamReader isr = new InputStreamReader(System.in);
    加入缓冲区提高效率。
    BufferedReader bufr = new BufferedReader(isr);


    目的:OutputSteam Writer
    操作存文本 Writer
    设备:硬盘(一个文件)FileWriter


    FileWriter fw = new FileWriter("w.txt");
    加入缓冲区提高效率
    BufferedWriter bufw = new BufferedWriter(fw);

    扩展,想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中
    目的:OutputSteam Writer
    操作存文本 Writer
    设备:硬盘(一个文件)FileWriter

    但是存储时,需要加入指定编码表,而指定的编码表只有转换流可以指定。
    所以要使用的对象时OutputStreamWriter.
    而该转换流对象要接收一个字节输出流,而且还可以操作的文件的字节输出流,FileOutputStream
    OutputStreamWriter osw = new OutputStream(new FileOutputStream("d.txt"),"utf-8");

    5.2改变标准输入输出设备

    System.in默认的输入设备是键盘

    System .out默认的输出设备是控制台

    通过System类的setIn(InputStream)setOut(OutputStream)和改变标准输入输出设备,

    比如:

    System.setIn(new FileInputStream("PersonDemo.java")); 

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

    这样标准输入设备就是一个文件。

  • 相关阅读:
    利用java自带的base64实现加密、解密
    CentOS 7下源码安装MySQL 5.7
    阿里巴巴Json工具-Fastjson讲解
    微信企业号开发—发送消息
    微信企业号开发-如何建立连接
    如何注册、使用微信企业号及安装第三方应用详解
    Jsp四个作用域page、request、session和application的区别
    根据两点经纬度计算距离
    Linux云服务器安装tomcat
    linux下svn服务器搭建步骤
  • 原文地址:https://www.cnblogs.com/grkbeyond/p/4147266.html
Copyright © 2011-2022 走看看