黑馬程序員 java基礎--IO流(2)

                                                                                 -------android培訓java培訓java學習型技術博客、期待與您交流! ----------


                                              知識點二   字節流

一、概述:

1、字節流的操作原理和字符流類是相似的,只不過字節流可以對圖片、音頻和視頻媒體進行操作。

2、由於媒體數據中都是以字節存儲的,所以,字節流對象可直接對媒體進行操作,可以不用再進行刷新流的動作。

3、讀寫字節流:InputStream     --->  輸入流(讀)

                             OutputStream  --->  輸出流(寫)

4、不用進行刷新流的動作的原因:

      因爲字節流操作的是字節,即數據的最小單位,不需要像字符流一樣要進行轉換爲字節。可直接將字節寫入到指定文件中,但是需要在寫代碼的時候,如果有字符串,要將字符串轉爲字節數組再進行操作。

5、字節流所特有方法:

int available() --->  放回數據字節的長度,包含終止符

在定義字節數組長度的時候,可以用到這個方法:byte[] = new byte[fos.available()]   (fos爲字節流對象)

但是,對於這個方法要慎用,如果字節過大,比如一部電影(幾個G),那麼如此大的數組就會損壞內存,超過jvm所承受的大小(指定內存爲64M)。

舉例:

/*
字符流:
FileReader 
FileWriter

BufferedReader
BufferedWriter

字節流:
兩個基類:
InputStream(讀) OutputStream(寫)

需求:想要操作圖片數據,這時候就要用到字節流
*/
import java.io.*;
class FileStream{
    public static void main(String []args) throws IOException
   { 
      // writeFile();
	  // readFile_1();
	  // readFile_2();
	  readFile_3();
   }
   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 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_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 writeFile() throws IOException
   {
      FileOutputStream fos=new FileOutputStream("fos.txt");
	  
	  //將字符串變爲字節數組
	  fos.write("abcde".getBytes());
	  
	  fos.close();
	  
   }
}

二、複製媒體文件(一張圖片):

1、思路:

1)用字節流讀取流對象和媒體文件相關聯

2)用字節寫入流對象,創建一個媒體文件,用於存儲獲取到的媒體文件數據

3)通過循環讀寫,完成數據的存儲

4)關閉資源

注意:不要用字符流操作媒體文件,因爲圖片數據讀取一段後去查編碼表,如果找到對應的數字編碼未變話,如果沒找到去找編碼表的未知區域,所以編碼會發生變化,這樣生成新圖片的編碼和老圖片的編碼就不一樣了,字節碼發生變化,因此字符流只用於處理文字數據。

示例:

import java.io.*;
class  CopyPic
{
	public static void main(String[] args) 
	{
		FileOutputStream fos = null;
		FileInputStream fis = null;
		try
		{
			fos = new FileOutputStream("c:\\2.bmp");
			fis = new FileInputStream("c:\\1.bmp");

			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("寫入關閉失敗");
			}
		}
	}
}
三、字節流緩衝區:

1、讀寫特點:

read():會將字節byte型值提升爲int型值

write():會將int型強轉爲byte型,即保留二進制數的最後八位。

2、原理:將數據拷貝一部分,讀取一部分,循環,直到數據全部讀取完畢。

1)先從數據中抓取固定數組長度的字節,存入定義的數組中,再通過然後再通過read()方法讀取數組中的元素,存入緩衝區

2)循環這個動作,知道最後取出一組數據存入數組,可能數組並未填滿,同樣也取出包含的元素

3)每次取出的時候,都有一個指針在移動,取到數組結尾就自動回到數組頭部,這樣指針在自增

4)取出的時候,數組中的元素再減少,取出一個,就減少一個,直到減到0即數組取完

5)到了文件的結尾處,存入最後一組數據,當取完數組中的元素,就會減少到0,這是全部數據就取完了

示例:

/*
通過緩衝區,演示Mp3的複製

BufferedOutputStream
BufferedInputStream
*/
import java.io.*;
class CopyMp3{
    public static void main(String []args) throws IOException
   { 
      long start=System.currentTimeMillis();
      //copy_1();
	  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;
	   
	   //讀了一次沒有寫出去
	  // System.out.println("第一個字節:"+bufis.myRead());
	   
	   while((by=bufis.myRead())!=-1)
	   {
	     bufos.write(by);
	   }
	   bufos.close();
	   bufis.myClose();
   }
}

4、自定義字節流緩衝區:

思路:
1、定義一個固定長度的數組
2、定義一個指針和計數器用於讀取數組長度,和計數數組元素是否取完爲0
3、每次將字節數據存入元素要先將數組中的元素取完

注:取出的是byte型,返回的是int型,這裏存在提升的動作,
當byte中的八位全爲1的時候是byte的-1,提升爲int類型,就變爲int型的-1,,read循環條件就結束了
變爲-1的原因是由於在提升時,將byte的八位前都補的是1,即32位的數都是1,即爲int型的-1了。
如何保證提升後的最後八位仍爲1呢?就需要將前24位補0,就可以保留原字節數據不變,又可以避免轉爲int型出現-1的情況;
那麼要如何做呢?

這就需要將提升爲int的數據和前24位爲0,後八位仍爲原字節數據的這個值做與運算。即和255做與運算即可

示例:

/*
11111111 -->提升了一個int類型,那不還是-1,嗎?是-1的原因是因爲在8個1的前面補得的是1導致的
那麼我只要在前面補0,既可以保留原字節數據不變,又可以避免-1的出現,那麼怎,補0呢
                           
 11111111 11111111 11111111 11111111
&00000000 00000000 00000000 11111111
-------------------------------------
 00000000 00000000 00000000 11111111
 
 這樣就可以避免-1的發生
 byte:-1  ---->int:-1
 read方法在提升,write方法將指定的字節寫入此緩衝的輸出流(強制轉換,取最低的8位)
*/

import java.io.*;
class MyBufferedInputStream{
   private InputStream in;
   
   private byte[] buf=new byte[1024*4];
   
   private int pos=0;
   
   private int 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;
	  //取數組第一個元素pos=0;
	  byte b=buf[pos];
	  
	  count--;
	  pos++;
	 //b與255進行與操作,用十六進制表示就是0xff
	  return b&255;
	  }else if(count>0)
	  {
	  byte b=buf[pos];
	  
	  count--;
	  pos++;
	  //b與255進行與操作,用十六進制表示就是0xff
	  return b&255;
	  }
	  
	  return -1;
   }
   public void myClose()throws IOException
   {
      in.close();
   }
 
}
5.讀取鍵盤錄入

System.out:對應的是標準的輸出設備,控制檯。
System.in:對應的標準輸入設備,鍵盤

/*
需求:
通過鍵盤錄入數據,當錄入一行數據後就進行打印。
如果當錄入的數據時over,那麼停止錄入

Dos下鍵盤錄入操作終止可以使用Ctrl+C操作
*/

import java.io.*;

class  ReadIn
{
  public static void main(String[] args) throws IOException
  {
    InputStream in=System.in;
	StringBuilder sb=new StringBuilder();
    /*
	int ch=0;
    while((ch=in.read())!=-1)
    {
	 System.out.println(ch);
	}	
	*/
	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=new StringBuilder();
		   
		   sb.delete(0,sb.length());
		}
		else
		sb.append((char)ch);
	}
	
	
	in.close();
	//數據類型轉換
	System.out.println('\r'+0);
	System.out.println('\n'+0);
	/*
	int by=in.read();
	int by1=in.read();
	int by2=in.read();
	
	
	System.out.println(by);
	System.out.println(by1);
	System.out.println(by2);
	*/
  }
}

                                             知識點三   轉換流


一、概述:

轉換流的由來:內部可以指定編碼表

1.通過上面的示例,通過鍵盤錄入一行數據並打印其大寫,發現其實就是讀一行數據的原理

能不能直接使用readLine方法來完成鍵盤錄入的一行數據的讀取呢?
readLine方法是字符流BufferedReader類中的方法
而鍵盤錄入的read方法字節流InputStream的方法

那麼能不能將字節流轉成字符流在使用字符流緩衝區的readLine方法呢?

2.兩個轉換流:讀取轉換流InputStreamReader和寫入轉換流OutputStreamWriter,這兩個類分別屬於字符流Reade和Writer的子類

二、讀取轉換流:

InputStreamReader是字節流通向字符流的橋樑,它使用指定的charset讀取字節並將其解碼爲字符。

InputStreamReader:讀取轉換流

a.獲取鍵盤錄入對象: ---> InputStream in = System.in;

b.將字節流對象轉換成字符流對象,使用轉換流InputStreamReader: ---> InputStreamReader isr = new InputStreamReader(in);

c.爲提高效率,將字符串進行緩衝區技術操作,使用BufferedReader: ---> BufferedReader bufw = new BufferedReader(isr);

d.之後就可以使用readLine()方法讀取錄入的一行數據了。


示例:

class TransStreamDemo{
    public static void main(String []args) throws IOException
   { 
           //獲取鍵盤錄入對象
	  // InputStream in=System.in;
	   
	   //將字節流對象裝換成字符流對象,InputStreamReader
	  //InputStreamReader isr=new  InputStreamReader(in);
	   
	   //爲了提高效率,講將字符流進行緩衝區技術的高效操作
	   
	  // BufferedReader bufr=new BufferedReader(isr);
	  
	   BufferedReader bufr=new BufferedReader(new InputStreamReader(new FileInputStream("CopyMp3.java")));
	   
	   
	   BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(System.out));
	   
	   
	   String line=null;
	   
	   while((line=bufr.readLine())!=null)
	   {
	     if("over".equals(line))
		 break;
	     System.out.println(line.toUpperCase());
	
           }
	   bufr.close();
   }
}


二、寫入轉換流

OutputStreamWriter是字符流通向字節流的橋樑,可使用指定的charset將要寫入流中的字符編碼成字節。

OutputStreamWriter:寫入轉換流 ---> 和讀取轉換流同理,即使用對應的Writer的子類

示例:

class TransStreamDemo{
    public static void main(String []args) throws IOException
   { 
      
	  //*************畢老師說這句換一定要記住,鍵盤錄入,非常的重要********************
	  //鍵盤錄入最常見寫法
	   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();
		 
		
	   }
	   
	   bufr.close();
   }
}

                                                                                                   知識點四  流操作規律

一、分析清楚源和目的以及需求:

1.源:鍵盤錄入
目的:控制檯
2.需求:想把鍵盤錄入的數據存儲到一個文件中
源:鍵盤 
目的:文件
3.需求:想要將一個文件的數據打印在控制檯上。
源:文件
目的:控制檯
二:
流操作的基本規律:
最痛苦的就是流對象有很多,不知道該用哪一個

通過三個來明確
1.明確源和目的
  源:輸入流  InputStream Reader
  目的:輸出流 OutputStream Writer
  

2.明確操作的數據是否是純文本
  是:字符流
  不是:字節流

3.當體系明確後,在明確要使用哪個具體的對象
  通過設備來進行區分;
  源設備:內存(ArrayStream),硬盤(FileStream),鍵盤(System.in)
  目的設備:內存(ArrayStream),硬盤(FileStream),控制檯(System.out)
4、需求體現:

4.1需求:將一個文本文件中的數據存儲到另一個文件中,複製文件。
  源:因爲是源,所以使用讀取流 InputStream Reader
  是不是操作文本文件?
  是!這時就可以選擇Reader
  這樣體系就明確了
  
  接下來明確對象該體系中的哪個對象。
  明確設備:硬盤上的一個文件。
  Reader體系中可以操作文件的對象是FileReader
  
  是否需要提高效率:是!加入Reader體系中的緩衝區,BufferedReader
  
  FileReader fr=new FileReader("a.txt");
  
  BufferReader bufr=new BufferedReader(fr);
  
  目的:OutputStream Writer
  是否是純文本文件?
  是!Writer。
  設備:硬盤。一個文件。
  Writer體系中可以操作文件的對象是FileWriter
  
  是否需要提高效率:是!加入Writer體系中的緩衝區,BufferedWriter
  
  FileWriter fw=new FileWriter("b.txt");
  
  BufferedWriter  bufw=new BufferedWriter(fw);
  
練習:將一個圖片文件中數據存儲到另一個文件中,複製文件。要按照以上格式完成三個明確。

分析:

1)源:InputStream和Reader

      圖片:字節流 ---> InputStream

      設備:硬盤上的文件 --->  FileInputStream

2)目的:OutoutStream和Writer

      圖片:字節流  --->  OutputStream

      設備:硬盤上的文件 --->  FileOutputStream

4.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);

     目的:OutputputStream Writer
     是否是純文本?是!Writer
     設備:硬盤。一個文件。使用FileWriter
     FileWriter fw=new FileWriter("c.txt");
     需要提高效率嗎?需要
     BufferedWriter bufw=new BufferedWriter(fw);

5.擴展一下,想要把錄入的數據按照指定的編碼表(utf-8),將數據存到文件中

   目的:OutputputStream Writer
是否是純文本?是!Writer
設備:硬盤。一個文件。使用FileWriter
但是FileWriter是使用默認的編碼表,GBK
但是存儲時需要加入指定的編碼表utf-8,只有轉換流才能指定。
所以要使用的對象是OutputStreamReader
而該轉換流對象要接受一個字節輸出流,而且還可以操作文件的字節輸出流。FileOutputStream
  OutputStreamWriter osw=new  OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");
需要提高效率嗎?需要
BufferedWriter bufw=new BufferedWriter(osw);
所以記住,裝換流什麼時候使用,字符和字節之間的橋樑,通常,涉及到字符編碼裝換時需要用到轉換流
示例:
class TransStreamDemo2{
    public static void main(String []args) throws IOException
   { 
          //獲取鍵盤錄入對象
	  // InputStream in=System.in;
	
	  //鍵盤錄入最常見寫法
	   BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
	   
	   BufferedWriter bufw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("d2.txt"),"UTF-8"));
	   
	   
	   String line=null;
	   
	   while((line=bufr.readLine())!=null)
	   {
	     if("over".equals(line))
		 break;
	  
	     bufw.write(line.toUpperCase());
		 bufw.newLine();
		 bufw.flush();
           }
	   bufr.close();
   }
}
6.改變標準輸入輸出設備
System.setIn(new FileInputStream("PersonDemo.java"));
System.setOut(new PrintStream("zz.txt"));
                                                                   
                                             知識點五  信息

一、異常的日誌信息:

當程序在執行的時候,出現的問題是不希望直接打印給用戶看的,是需要作爲文件存儲起來,方便程序員查看,並及時調整的。

示例:
/*
說明:Log4j的jar包可以完成日誌信息建立
*/
import java.io.*;
import java.util.*;
//格式化要用的包
import java.text.*;

class ExceptionInfo
{
  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將時間格式化
	   SimpleDateFormat sdf=new  SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//h是12小時制,H是24小時制
	   String s=sdf.format(d);
	   
	   PrintStream ps=new PrintStream("exception.log");
	   //ps是字節流,要將字符串變成字節數組寫出去
	   //ps.write(d.toString().getBytes);
	   //ps.println(d.toString());
	   ps.println(s);
	    //改變標準輸出
	   System.setOut(ps);
	   //e.printStackTrace(new PrintStream("a.txt"));
	}catch(IOException ex)
	{
	  throw new RuntimeException("日誌文件創建失敗");
	}
	
	   e.printStackTrace(System.out);
	}
	
  
  }
  
}

二、系統信息:

獲取系統信息:
Properties getProperties()
將信息輸出到指定輸出流中
void  list(PrintStream out)
將輸出流中數據存入指定文件中
new PrintStream("systeminfo.txt")
示例:

import java.io.*;
import java.util.*;
class SystemInfo
{
  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"));
  }
}

最新最全的的java學習視頻教程:http://pro.net.itcast.cn/View-22-1458.aspx       
                                              -------android培訓java培訓java學習型技術博客、期待與您交流! ----------

詳細請查看:http://edu.csdn.net/heima


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章