IO(上)

 

IO

IO(Input Output)流

    IO流用来处理设备之间的数据传输,java对数据的操作是通过流的方式,java用于操作流的对象都在IO包中.

流的分类:

1)       流按操作数据分为两种:字节流和字符流

2)       流按流向分为:输入流,输出流

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

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

注意:流的操作只有两种:读和写。

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

IO流常用基类

    字节流的抽象基类: InputStream, OutputStream

    字符流的抽象基类:Reader ,Writer

    注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。即子类名后缀都是父类名,前缀名都是这个子类的功能名称。

    如:InputStream的子类FileInputStream

    如:Reader的子类FileReader

字符流和字节流

字节流两个基类:InputStream OutputStream

字符流两个基类:Reader Writer

先学习一下字符流的特点:

既然IO流是用于操作数据的,那么数据的最常见体现形式是:文件

那么先以操作文件为主来演示,

需求:在硬盘上,创建一个文件并写入一些文件数据

找到一个专门用于操作文件的writer子类对象,filewriter,后缀名是父类名,前缀名是该流对象的功能。

import java.io.*;
public class filewritedemo {
public static void main(String[] args)  throws IOException //读、写都会发生IO异常
	{
		//创建一个filewriter对象,该对象一被初始化就必须要明确被操作的文件。
		//而且该文件会被创建到指定目录下, 如果该目录下已有同名文件,将被覆盖
		//其实该步就是在明确数据要存放的目的地
		FileWriter fw=new FileWriter("demo.txt");
		//调用writer方法,将字符串写入到流中
		fw.write("agwewetg");
		//刷新流对象中的缓冲中的数据,将数据刷到目的地中
		fw.flush();
		//关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据
		//将数据刷到目的地中,
		//和flush区别,flush刷新后,流可以继续使用,close刷新后,会将流关闭。
		fw.close();// 关闭流,其实关闭的就是java调用的系统底层资源。在关闭前,会先刷新该流。
	}
}

close()flush()的区别:

flush():将缓冲区的数据刷到目的地中后,流可以使用。

close():将缓冲区的数据刷到目的地中后,流就关闭了,该方法主要用于结束调用的底层资源。这个动作一定做。

io异常的处理方式:io一定要写finally

FileWriter写入数据的细节:

1window中的换行符:\r\n两个符号组成。 linux\n

2:续写数据,只要在构造函数中传入新的参数true

3:目录分割符:window \\  /

import java.io.*;
public class filewriterdemo2 {
	public static void main(String[] args) {
		FileWriter fw =null;//在外边建立引用
               try {
	        fw = new FileWriter("demo.txt");//在try内进行初始化。
	         fw.write("sdgsgdsfd");
                } catch (IOException e) {
	       System.out.println("catch:"+e.toString());
              }
              finally
              {
	      try {  if(fw!=null)
	              fw.close();
                 } catch (IOException e)
                           {
	                                System.out.println("catch:"+e.toString());
                            }
}
}
}

演示对已有文件的数据续写

import java.io.FileWriter;
import java.io.IOException;
public class filewriterdemo3 {
	public static void main(String[] args) {
		FileWriter fw =null;
		try {//传递一个true参数,代表补覆盖已有的文件,并在已有文件的末尾处进行数据续写
			 fw = new FileWriter("demo.txt",true);
			fw.write("sdgs\r\ngdsfd");//\r\n表示换行。
		} catch (IOException e) {
			System.out.println("catch:"+e.toString());
		}
		finally
		{
			      try {  if(fw!=null)
			              fw.close();
		                 } catch (IOException e)
		                           {
          System.out.println("catch:"+e.toString());
		                          }
		}
	}
}

FileReader使用Reader体系,读取一个文本文件中的数据。返回 -1 ,标志读到结尾。

文件的读取

import java.io.*;
public class filereaderdemo {
	public static void main(String[] args) throws IOException
	{
		// 创建一个文件读取流对象,和指定名称的文件相关联
                     //要保证该文件是已经存在的,如果补存在,会发生异常fileNotFoundException
                  FileReader fr=new FileReader("demo.txt");
                  //调用读取流对象的read方法
                    //read():一次读一个字符,而且会自动往下读。
                    //读取方式的第一种。
                      int ch=0;
                 while((ch=fr.read())!=-1)//条件是没有读到结尾。
                    {
                      System.out.println((char)ch);//调用读取流的read方法,读取一个字符。
                    }
                /*while(true)
                {
                   int ch1=fr.read();
                   if(ch1==-1)
                  {
	           break;
                   }
                    System.out.println("ch="+(char)ch);
              }*/
	}
}

读取数据的第二种方式:第二种方式较为高效,自定义缓冲区。

第二种方式,通过字符数组进行读取

import java.io.FileReader;
import java.io.IOException;
public class FileReaderDemo2
{
	public static void main(String[] args) throws IOException
	{
	   FileReader fr=new FileReader("demo.txt");//创建读取流对象和指定文件关联。
                 //定义一个字符数组,用于存储读到字符。
                //该read(char[])返回的是读到字符个数。
             //因为要使用read(char[])方法,将读取到字符存入数组。所以要创建一个字符数组,一般数组的长度都是1024的整数倍。
               char[]buf=new char[1024];
             int num=0;
             while((num=fr.read(buf))!=-1)
              { System.out.println(new String(buf,0,num));
              }
               fr.close();
	}
}
 读取一个.java文件,并打印在控制台上
import java.io.*;
public class filereaderTest {
      public static void main(String[] args)throws IOException
     {
       FileReader fr=new FileReader("DateDemo.java");
       char[] buf=new char[1024];
       int num=0;
       while((num=fr.read(buf))!=-1)
       {
         System.out.print(new String(buf,0,num));
       }
     fr.close();
}
}

将c盘一个文本文件复制到d盘

复制的原理:

其实就是将C盘下的文件数据存储到D盘的一个文件中

步骤1,在D盘创建一个文件,用于存储C盘文件中的数据

2,定义读取流和C盘文件关联

3,通过不断的读写完成数据存储

4,关闭资源

import java.io.*;
public class CopyTest {
	public static void main(String[] args) throws IOException {
	      copy_1();
	}
	public static void copy_2()
	{
		FileWriter fw=null;
		FileReader fr=null;
	try {
		fw = new FileWriter("demo_copy.txt");
		fr = new FileReader("demo.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){}
	}
	}
   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();
   }
}

字符流:

Reader用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) close()

     |---BufferedReader从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。

        |---LineNumberReader跟踪行号的缓冲字符输入流。此类定义了方法setLineNumber(int)getLineNumber(),它们可分别用于设置和获取当前行号。

     |---InputStreamReader是字节流通向字符流的桥梁:它使用指定的charset读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。

        |---FileReader用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在 FileInputStream 上构造一个 InputStreamReader

     |---CharArrayReader

     |---StringReader

-------------------------------------------------

Writer写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)flush() close()

     |---BufferedWriter将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。

     |---OutputStreamWriter是字符流通向字节流的桥梁:可使用指定的charset将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。

        |---FileWriter用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在 FileOutputStream 上构造一个 OutputStreamWriter

     |---PrintWriter

     |---CharArrayWriter

     |---StringWriter

---------------------------------

字节流:

InputStream是表示字节输入流的所有类的超类。

     |--- FileInputStream从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。FileInputStream用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader

     |---FilterInputStream包含其他一些输入流,它将这些流用作其基本数据源,它可以直接传输数据或提供一些额外的功能。

        |--- BufferedInputStream该类实现缓冲的输入流。

        |--- Stream

     |---ObjectInputStream

     |---PipedInputStream

-----------------------------------------------

OutputStream此抽象类是表示输出字节流的所有类的超类。

     |---FileOutputStream文件输出流是用于将数据写入FileFileDescriptor的输出流。

     |---FilterOutputStream此类是过滤输出流的所有类的超类。

        |---BufferedOutputStream该类实现缓冲的输出流。

        |--- PrintStream

        |--- DataOutputStream

     |---ObjectOutputStream

     |---PipedOutputStream

--------------------------------

缓冲区是提高效率用的,给谁提高呢?

字符流的缓冲区

a)       缓冲区的出现提高了对数据的读写效率,对应类:BufferedWriter BufferdeReader

b)       缓冲区要结合流才可以使用,在流的基础上对流的功能进行了增强

c)       缓冲区的出现是为了提高流的操作效率而出现的,所以在创建缓冲区之前,必须要先有流对象

d)       该缓冲区中提供了一个跨平台的换行符:newLine();

BufferedWriter是给字符输出流提高效率用的,那就意味着,缓冲区对象建立时,必须要先有流对象。明确要提高具体的流对象的效率。

import java.io.*;	
public class BufferedWriterDemo {
	public static void main(String[] args) throws IOException
	{
		//创建一个字符写入流对象
		FileWriter fw=new FileWriter("buf.txt");
		//为了提高字符写入流效率,加入了缓冲技术
		//只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可
		BufferedWriter bufw=new BufferedWriter(fw);
		for(int x=1;x<5;x++)
		{
			bufw.write("abcd"+x);
			bufw.newLine();//写入一个行分隔符。跨平台,\n\r
			bufw.flush();
		}
		//记住,只要用到缓冲区,就要记得刷新。
		//bufw.flush();
		//其实关闭缓冲区,就是在关闭缓冲区中的流对象。
		bufw.close();
	}
}

字符读取流缓冲区

该缓冲区提供了一个一次读一行的方法readline。方便于对文本数据的获取,当返回null时,表示读到文件末尾

readline方法返回的时候只返回回车符之前的数据内容,并不返回回车符。

BufferedReader

import java.io.*;
public class BufferedReaderDemo 
{
	public static void main(String[] args) throws IOException
	{
		//创建一个读取流对象和文件相关联
		FileReader fr=new FileReader("buf.txt");
	         //为了提高效率,加入缓冲技术,将字符读取流对象作为参数传递给缓冲对象的构造函数
		BufferedReader bufr=new BufferedReader(fr);
		String line=null;
		while((line=bufr.readLine())!=null)
		{
			System.out.println(line);
		}
		/*String s1=bufr.readLine();//readLine方法返回的时候是不带换行符的。
		System.out.println("s1:"+s1);*/
       bufr.close();
	}
}

//记住,只要一读取键盘录入,就用这句话。

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

BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));//输出到控制台

String line = null;

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

    if("over".equals(line))

        break;

    bufw.write(line.toUpperCase());//将输入的字符转成大写字符输出

    bufw.newLine();

    bufw.flush();

    }

bufw.close();

bufr.close();

———————————————

通过缓冲区复制一个.java文件

readline方法的原理,无论是读一行,获取读取多个字符,其实最终都是在硬盘上一个一个读取,

所以最终使用的还是read方法一次读一个的方法

import java.io.*;
public class copyTextByBuf {
	public static void main(String[] args)
	{
		BufferedReader br=null;
		BufferedWriter bw=null;
   try {
     br=new BufferedReader(new FileReader("bufferedWritedemo.java"));
     bw=new BufferedWriter(new FileWriter("buffered_copy.txt"));
     String line=null;
     while((line=br.readLine())!=null)
     {
    	 bw.write(line);
    	 bw.newLine();
    	 bw.flush();
      }
	} catch (IOException e) {
		throw new RuntimeException("读写失败");
	} finally{
	     try{ if(br!=null)
	    	   br.close();
	       }
	     catch(IOException e)
	     {
	    	 throw new RuntimeException("读取关闭失败");
	     }
	     try{ if(bw!=null)
	    	   bw.close();
	       }
	     catch(IOException e)
	     {
	    	 throw new RuntimeException("写入关闭失败");
	     }
   }
}
}


内存图解:


 

 

来模拟一下Bufferedreader

明白了bufferedReader类中特有方法readline的原理后,可以自定义一个类中包含一个功能和readline一致的方法。

import java.io.*;
public class MyBufferedReaderDemo 
{
	public static void main(String[] args) throws IOException
	{        //创建一个读取流对象和文件相关联
		FileReader fr=new FileReader("buf.txt");
		MyBufferedReader mybuf=new MyBufferedReader(fr);
                  String line=null;
       while((line=mybuf.myReadLine())!=null)
       {
    	   System.out.println(line);
       }
       mybuf.myclose();
	}
}
class MyBufferedReader extends Reader
{
	private /* FileReader */Reader  r;
	MyBufferedReader(Reader/*FileReader*/ r)
	{
		this.r=r;
	}
               //可以一次读一行数据的方法
	public String myReadLine() throws IOException
	{
		//定义一个临时容器,原bufferedreader封装的是字符数组
	           //为了演示方便,定义一个stringbuilder容器,因为最终还是要将数据变成字符串
		StringBuilder sb=new StringBuilder();
		int ch=0;
		while((ch=r.read())!=-1)
		{
			if(ch=='\r')
				continue;
			if(ch=='\n')
				return sb.toString();
			else
				sb.append((char)ch);
		}
		return null;
	}
	/*
	 * 覆盖reader类中的抽象方法*/
	public int read(char[] cbuf,int off,int len)throws IOException
	{
		return r.read(cbuf,off,len);
	}
	public void close()throws IOException
	{
		r.close();
	}
	public void myclose() throws IOException
	{
		r.close();
	}
}

—————————————————

IO中的使用到了一个设计模式:装饰设计模式。

装饰设计模式解决:对一组类进行功能的增强。

当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能,那么自定义的该类称为装饰类。装饰类通常会通过构造方法接受被装饰的对象

并基于被装饰的对象的功能,提供更强的功能。

包装:写一个类(包装类)对被包装对象进行包装;

 1、包装类和被包装对象要实现同样的接口;

 2、包装类要持有一个被包装对象;

 3、包装类在实现接口时,大部分方法是靠调用被包装对象来实现的,对于需要修改的方法我们自己实现; 

public class personDemo {
	public static void main(String[] args) {
               person p=new person();
               p.chifan();
               superperson p1=new superperson(p);
               p1.superchifan();
	}
}
class person
{
        public void chifan()
        {
	 System.out.println("吃饭");
       }
}
class superperson
{    private person p;
     superperson(person p)
    {
	  this.p=p;
    }
    public void superchifan()
    {
	  System.out.println("开胃酒");
	  p.chifan();
	  System.out.println("甜点");
   }
}

————————————————

      MyReader//专门用于读取数据的类

         |--MyTextReader

           |---MyBufferTextReader

         |--MyMediaReader

           -- MyBufferMediaReader

         |---MyDataReader

            |--MyBufferDataReader

       class MyBufferReader

       {

        MyBufferReader(MyTextReader text)

        {}

        MyBufferReader(MyMediaReader media)

        {}

       }   

       上面这个类扩展性很差

       找到其参数的共同类型,通过多态的形式,可以提高扩展性。

       class MyBufferReader extends MyReader

       {private MyReader r;继承结构变成组合结构。

        MyBufferReader(MyReader r)

        {}

       }  装饰设计模式。

       MyReader//专门用于读取数据的类 、、、装饰

         |--MyTextReader

         |--MyMediaReader

         |---MyDataReader

         |--MyBufferReader

          装饰模式比继承要灵活,避免了继承体系臃肿。而且降低了类与类之间的关系

          装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能

          所以装饰类和被装饰类通常是都属于一个体系中的。

————————————————————————————

LineNumberReader跟踪行号的缓冲字符输入流。此类定义了方法setLineNumber(int) getLineNumber(),它们可分别用于设置和获取当前行号。

 

import java.io.*;
public class LineNumberReaderDemo {
	public static void main(String[] args)throws IOException
	{
	   FileReader fr=new FileReader("persondemo.java");
            LineNumberReader lnr=new LineNumberReader(fr);
            String line=null;
            lnr.setLineNumber(100);//设置行号。
            while((line=lnr.readLine())!=null)
          {
                System.out.println(lnr.getLineNumber()+":::"+line);	
          }
         lnr.close();
}
}

——————————————————————

练习:模拟一个带行号的缓冲区对象。

import java.io.*;
public class MyLineNumberReaderDemo {
	public static void main(String[] args)throws IOException
	{
	FileReader fr=new FileReader("copytextBybuf.java");
	MyLineNumberReader mylnr=new MyLineNumberReader(fr);
	String line=null;
	mylnr.setLineNumber(100);
	while((line=mylnr.myReadLine())!=null)
	{
		System.out.println(mylnr.getLineNumber()+"::"+line);
	}
	mylnr.close();
	}
}
class MyLineNumberReader  extends MyBufferedReader
{
         private int lineNumber;
         MyLineNumberReader(Reader r)
        {
            super(r);
        }
public String myReadLine()throws IOException
{       lineNumber++;
        return super.myReadLine();
}
public void setLineNumber(int lineNumber)
{
        this.lineNumber=lineNumber;	
}
public int getLineNumber()
{
        return lineNumber;	
}
}
/*class MyLineNumberReader 
{
       private Reader r;
       private int lineNumber;
       MyLineNumberReader(Reader r)
      {
        this.r=r;	
       }
      public String myReadLine()throws IOException
      { lineNumber++;
        StringBuilder sb=new StringBuilder();
        int ch=0;
        while((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 void setLineNumber(int lineNumber)
     {
       this.lineNumber=lineNumber;	
     }
     public int getLineNumber()
     {
       return lineNumber;	
     }
    public void myClose()throws IOException
    {
       r.close();	
    }
}*/

字节流

inputstream  outputstream

需求 想要操作图片数据,这时就要用到字节流

import java.io.*;
public class FileStream {
	public static void main(String[] args)throws IOException
	{
	readFile_1();	
	}
	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("num="+num);
	  System.out.println(new String(buf));
	  fis.close();
	}
	public static void readFile_2()throws IOException
	{
	  FileInputStream fis=new FileInputStream("fos.txt");
	byte[]buf=new byte[1024];
	int len=0;
	while((len=fis.read(buf))!=-1)
	{
		System.out.println(new String(buf,0,len));
	}
	fis.close();
	}
	
         public static void readFile_1()throws IOException
         {
          FileInputStream fis=new FileInputStream("fos.txt");
          int ch=0;
          while((ch=fis.read())!=-1)
          {
 	  System.out.println((char)ch);
           }
          fis.close();
        }
        public static void writeFile()throws IOException
        {
	FileOutputStream fos=new FileOutputStream("fos.txt");
	fos.write("abcd".getBytes());
	fos.close();
        }
}

————————————————

复制一个图片

思路:

     1,用字节读取流对象和图片关联

     2,用字节写入流对象创建一个图片文件,用于存储获取到的图片数据

     3,通过循环读写,完成数据的存储

     4,关闭资源

import java.io.*;
public class copyPic {
	public static void main(String[] args) {
		FileOutputStream fos=null;
		FileInputStream fis=null;
		try {
			fos=new FileOutputStream("c:\\2. jpg");
		      fis=new FileInputStream("c:\\1. jpg");//源
		      byte[] buf=new byte[1024];//定义缓冲区
		      int len=0;
		      while((len=fis.read(buf))!=-1)
		      {
		    	  fos.write(buf,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("写入文件失败");
			}
		}
	}
}

用字符流可以复制,但打开时看不了,因为图片数据读完一段以后去查看编码表,到编码表里面找到对应的数字给查出来了。如果找到对应数据编码没变,如果没找到对应的数据编码就找编码表的未知区域,最后编码变了。所以生成新图片的编码和老图片的编码不一样的。

——————————————————————————————

演示MP3的复制,通过缓冲区

BufferedOutputStream:创建一个新的换冲输出流,以将数据写入指定的底层输出流

BufferedInputStream:创建一个新的缓冲输入流,

writer:将指定的字节写入此缓冲的输出流。

flush:刷新此缓冲的输出流。

import java.io.*;
public class CopyMp3 {
	public static void main(String[] args) throws IOException
	{
		long start=System.currentTimeMillis();
		copy_1();
		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:\\0.mp3"));
	         int by=0;
                  //System.out.println("第一个字节:"+bufis.myRead());//获取第一个字节。是-1,没进行复制。
	          while((by=bufis.myRead())!=-1)
	         {
		  bufos.write(by);
	          }
	         bufos.close();
	         bufis.myClose();
	}
} 
/*1,定义数组
2,定义指针
3,定义计数器*/
/*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
       {
	 //通过in 对象读取硬盘上数据,并存储buf中
	if(count==0)
	{ count=in.read(buf);
	  if(count<0)
		   return -1;
	 pos=0; 
	 byte b=buf[pos];
	 count--;
	 pos++;
	 return b;
	}
	else if(count>0)
	{
		 byte b=buf[pos];
		 count--;
		 pos++;
		 return b;
	}		
	return -1;
 }
 public void myClose() throws IOException
 {
	 in.close();
 }
}*/

定义数组,定义指针,定义计数器。

import java.io.*;
public class MyBufferedInputStream {
	/**
	 * 定义数组,定义指针,定义计数器。
	 * reader方法在提升,writer方法在做强转。
            * writer:将指定的字节写入此缓冲的输出流。
	 * reader方法在提升,writer方法在做强转。
	 * writer方法是八位写出去,保证原数据的不变化,将最低的数据写出去,保证了原数据的原样性。
	 * 为什么reader返回来的不是byte类型而是int类型的,避免了-1的发生。
	 * @param args
	 */
	private InputStream in;
	 private byte[] buf=new byte[1024*4];
	 private int pos=0,count=0;
	 MyBufferedInputStream(InputStream in)
	 {
		 this.in=in;
	 }
	 //一次读一个字节,从缓冲区(字节数组)获取
	 public int myRead() throws IOException
	 {
		 //通过in 对象读取硬盘上数据,并存储buf中
		if(count==0)
		{ count=in.read(buf);
		   if(count<0)
			   return -1;
		 pos=0; 
		byte b=buf[pos];
		 count--;
		 pos++;
		 return b&255;
		}
		else if(count>0)
		{
			byte b=buf[pos];
			 count--;
			 pos++;
			 return b&0xff;
		}		
		return -1;
	 }
	 public void myClose() throws IOException
	 {
		 in.close();
	 }
}

b&255原理图解:



需求:通过键盘录入数据

import java.io.*;
public class ReadIn {
	/**读取键盘录入
	 * system.out:对应的是标准输出设备,控制台
	 * system.in:对应的标准输入设备,键盘
	 * 需求:通过键盘录入数据
	 * 当录入一行数据后,就将该行数据进行打印,如果录入的数据是over,那么停止录入
	 * @param args
	 */
	public static void main(String[] args)throws IOException
	{
	  InputStream in=System.in;
           StringBuilder sb=new StringBuilder();
           while(true)
           {
             int ch=in.read();
             if(ch=='\r')
     	       continue;
             if(ch=='\n')
           {
	     String s=sb.toString();
	     if("over".equals(s))
	      break;
	     System.out.println(s.toUpperCase());
	     sb.delete(0, sb.length());
            }
             else 
             sb.append((char)ch);
          }
          /*int ch=0;
          while((ch=in.read())!=-1)
          {
              System.out.println(ch);	
          }
         in.close();*/
         /*int by=in.read();
         System.out.println(by);
         System.out.println('\r'+0);*/
	}
}

字符流

FileReader   FileWriter

BufferedReader BufferedWriter

字节流

InputStream  OutputStream

BufferdeInputStream BufferedOutStream

通过刚才的键盘录入一行数据并打印其大写,发现其实就是读一行数据的原理,也就是readline方法

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

readLine方法是字符流BufferedReader类中的方法而键盘录入的read方法是字节流InputStream的方法

那么能不能将字节流转成字符流再使用字符流缓冲区的readLine方法呢?

InputStreamReader是字节流通向字符流的桥梁,是将字节流转换成字节流。

InputStreamReader(InputStream in)要接受一个指定的字节流。

创建一个使用默认字符串的InputStreamReader。

OutputStreamWriter 是字符流通向字节流的桥梁。可使用指定的charset将要写入流中的字符编码成字节,它使用的字符集可以由名称指定或显示给定,否则将接受平台默认的字符集。

为了提高效率,可考虑将OutputStreamWriter包装到BufferedWriter中,以避免频繁调用转换器。

OutputStreamWriter(OutputStream out)它在构造的时候要接收一个输出流。

import java.io.*;
public 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);
		 *OutputStreamWriter不具有newLine方法。newLine是BufferedWriter的方法。
		 **/
		BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(System.out));
		String line=null;
		while((line=bufr.readLine())!=null)
		{
			if("over".equals(line))
			  break;
			bufw.write(line.toUpperCase());//可以不用写system.out.println();
			bufw.newLine();
			bufw.flush();
		}
		bufr.close();
	}
}

InputStreamReader(InputStream in,String charsetName)

创建使用指定字符集的InputStreamReader

下面有一儿子FileReader操作字符文件。固定的编码表GBK,指定编码表时要看爹InputStreamReader。

如果是GBK的可以用FileReader,如果是utf-8则用InputStreamReader来读。

字符流的好处是可以直接返回来一个中文。

    /**1,源:键盘录入

     * 目的:控制台

     * 2,需求:想把键盘录入的数据存储到一个文件中

     * 源:键盘

     * 目的:文件

     * 3,需求:想要将一个文件的数据打印在控制台上

     * 源:文件

     * 目的:控制台

 

     * 流操作的基本规律

     * 最痛苦的就是流对象有很多,不知道该用哪一个,

     * 通过两个明确来完成:

     * 1,明确源和目的

     * 源:输入流。Inputstream reader

     * 目的:输出流:outputstream writer

     * 2,操作的数据是否是纯文本

     * 是,字符流,不是,字节流

 

     * 通过三个明确来完成

     * 1,明确源和目的

     * 源:输入流。Inputstream reader

     * 目的:输出流:outputstream writer

     * 2,操作的数据是否是纯文本

     * 是,字符流,不是,字节流

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

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

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

     *需要: 1,将一个文本文件中数据存储到另一个文件中,复制文件

     * 源:因为是源,所以使用读取流,InputStream Reader

     * 是不是操作文本文件

     * 是,这时就可以选择Reader  这样体系就是明确了

     * 接下来明确要使用该系统中的哪个对象,明确设备:硬盘,上一个文件

     * Reader 体系中可以操作文件的对象是FileReader

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

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

     *目的outputstream writer

     * 是不是纯文件,是 Writer

     * 设备:硬盘,一个文件

     *Writer 系统中可以操作文件的对象FileWriter

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

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

     * BufferedWriter bufw=new BufferedWriter(fw);

     * 练习:将一个图片文件中数据存储到另一个文件中,复制文件 ,要按照以上格式自己完成三个明确。

     * 2,需求:将键盘录入的数据保存到一个文件中

     * 这个需求中有源头和目的都存在

     * 那么分别分析

     *InputStream Reader 是不是纯文本?是,Reader

     * 设备:键盘,对应的对象是system.in

     * 不是选择Reader吗?system.in对应的不是字节流吗?

     * 为了操作键盘的文本数据方便,转成字符流按照字符串操作是最方便的,

     * 所以既然明确了Reader ,那么就将system.in转换成Reader

     * 用了Reader体系中转换流,InputStreamReader

     * InputStreamReader isr=new InputStreamReader(System.in);

     * 需要提高效率吗?是,BufferedReader

     * BufferedReader bufr=new BufferedReader(isr);

      如果是读取键盘中的前两个字节,然后打印,就不需要转换了。

     *目的:OutputStream Writer

     * 是不是存文本?是,Writer

     * 设备:硬盘,一个文件,使用FileWriter 默认的编码

     * FileWriter fw=new FileWriter("c.txt");

     * 需要提高效率吗?需要

     * BufferedWriter bufw=new BufferedWriter(fw);

____________________________

扩展一下,想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中。指定编码用的是转换流。

目的:OutputStream Writer

是不是存文本?是,用字符流。Writer

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

      所以要使用的对象是OutputStreamWriter

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

 Outputstreamwriter osw =new Outputstreamwriter(new FileoutputStream("d.txt"),"utf-8")

需要高效吗?需要 BufferedWriter bufw=new BufferedWriter(osw);

扩展一下,想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中。所以,记住,转换流什么使用,字符和字节之间的桥梁,通常,涉及到字符编码转换时,需要用到转换流。

练习:将一个文本数据打印在控制台上,要按照以上格式自己完成三个明确,

import java.io.*;
public class TransStreamDemo2 {
		public static void main(String[] args) throws IOException
	         {        System.setIn(new FileInputStream("person.java"));//目的
	                     System.setOut(new PrintStream("zz.txt"));//源是键盘录入,源是一个文件,目的也是一个文件,形成复制的效果。
		//键盘的最常见写法。
                     //1,2,BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
                  //3,BufferedReader bufr=new BufferedReader(new InputStreamReader(new FileInputStream("copypic.java")));
		//1,BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(System.out));
                  //2,BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("out.txt")));//FileOutputStream操作文件字节流
                     //3,BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(System.out));
		    //键盘的最常见写法。
	        BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
	       BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("out.txt"),"UTF-8"));// UTF-8八个字节,GBK六个字节。
		String line=null;
		while((line=bufr.readLine())!=null)
		{
			if("over".equals(line))
			  break;
			bufw.write(line.toUpperCase());
			bufw.newLine();
			bufw.flush();
		}
		bufr.close();
	}
}

日志文件信息

import java.io.*;
import java.util.*;
import java.text.*;
public class ExceptionInfo {

	/**printStackTrace(PrintStream p)将此throwable及其追踪输出到指定的输出流
	 * *printStackTrace(PrintWriter p)将此throwable及其追踪输出到指定的PrintWriter。
	 * @param args
	 */
	public static void main(String[] args) throws IOException
	{
            try {
	         int[] arr = new int[2];
	         System.out.println(arr[3]);
            } catch (Exception e) {
              try {
		 Date d = new Date();
		 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
	          String s=sdf.format(d);
	          PrintStream ps=new PrintStream("exeception.log");
	          ps.println(s);
	         System.setOut(ps);//setOut改变目的。
	     } catch (IOException e2) {
		throw new RuntimeException("日志文件创建失败");
	}
	e.printStackTrace(System.out);
         }//log4j,日志文件信息的建立工具包,
	}
}

列表输出到指定的输出流

import java.util.*;
import java.io.*;
public class SystemInfo {

	/** list(PrintStream out)将属性列表输出到指定的输出流。
	 * @param args
	 */
	public static void main(String[] args) throws IOException
	{
		Properties prop=System.getProperties();//确定当前的系统属性。可以和流相结合。
                     //System.out.println(prop);
                  //   prop.list(System.out);
                 prop.list(new PrintStream("sysinfo.txt"));	
	}
}
<span style="font-size:14px;">------------------------------</span>

流对象:其实很简单,就是读取和写入。但是因为功能的不同,流的体系中提供N多的对象。那么开始时,到底该用哪个对象更为合适呢?这就需要明确流的操作规律。

流的操作规律:

1,明确源和目的。

       数据源:就是需要读取,可以使用两个体系:InputStreamReader

       数据汇:就是需要写入,可以使用两个体系:OutputStreamWriter

2,操作的数据是否是纯文本数据?

       如果是:数据源:Reader

                  数据汇:Writer

       如果不是:数据源:InputStream

                    数据汇:OutputStream

3,虽然确定了一个体系,但是该体系中有太多的对象,到底用哪个呢?

       明确操作的数据设备。

       数据源对应的设备:硬盘(File),内存(数组),键盘(System.in)

       数据汇对应的设备:硬盘(File),内存(数组),控制台(System.out)

4,需要在基本操作上附加其他功能吗?比如缓冲。

       如果需要就进行装饰。

转换流特有功能:转换流可以将字节转成字符,原因在于,将获取到的字节通过查编码表获取到指定对应字符。

转换流的最强功能就是基于字节流 +编码表。没有转换,没有字符流。

发现转换流有一个子类就是操作文件的字符流对象:

InputStreamReader

       |--FileReader

OutputStreamWriter

       |--FileWrier

想要操作文本文件,必须要进行编码转换,而编码转换动作转换流都完成了。所以操作文件的流对象只要继承自转换流就可以读取一个字符了。

但是子类有一个局限性,就是子类中使用的编码是固定的,是本机默认的编码表,对于简体中文版的系统默认码表是GBK

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

InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"gbk");

以上两句代码功能一致,

如果仅仅使用平台默认码表,就使用FileReader fr = new FileReader("a.txt"); //因为简化。

如果需要制定码表,必须用转换流。

转换流 = 字节流+编码表。

转换流的子类File =字节流 + 默认编码表。

凡是操作设备上的文本数据,涉及编码转换,必须使用转换流。

———寄语:我们都是好孩子,濛濛加油中。。。希望你也和濛濛一样努力中。。。为了自己的梦想,我们努力吧。不管我们以前是怎么的人,只要我们现在在为梦想努力中,这就足够啦!和濛濛一起加油。。。我在成长中。。。你呢?可爱的童鞋们——— 濛濛 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值