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

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


                                                                                                       知識點十      對象序列化
操作對象:
ObjectOutputStram和ObjectInputStream
被操作的對象要實現Serializable(標記接口);
一、概述:

將堆內存中的對象存入硬盤,保留對象中的數據,稱之爲對象的持久化(或序列化)

二、特有方法:

1、write(int val)   --->  寫入一個字節(最低八位)

2、writeInt(int vale)  --->   吸入一個32爲int值

三、使用步驟:

說明:serialVersion

a、給類一個可被編譯器識別的的序列號,在編譯類時,會分配一個long型UID,通過序列號,將類存入硬盤中,並序列化,即持久化。序列號根據成員算出的。靜態不能被序列化。如果非靜態成員也無需序列化,可以用transien修飾。

b、接口Serializable中沒有方法,稱之爲標記接口

1、寫入流對象:

1)創建對象寫入流,與文件關聯,即傳入目的

2)通過寫入writeObject()方法,將對象作爲參數傳入,即可寫入文件

2、讀取流對象

1)創建對象讀取流,與文件關聯,即傳入源

2)通過writeObject()方法,讀取文件中的對象,並返回這個對象

示例:

/*
對象的序列化
*/

import java.io.*;
//對象序列化測試 
class ObjectStreamDemo 
{
	public static void main(String[] args) throws Exception
	{    
	    //對象寫入流
		writeObj();
		//對象讀取流  
		readObj();
	}
	//定義對象讀取流 
	public static void readObj()throws Exception
	{
	    //ObjectInputStream對細節對象進行操作 
		//創建對象讀取流  
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
        //通過讀取文件數據,返回對象  
		Person p = (Person)ois.readObject();

		System.out.println(p);
		//最終關閉流對象  
		ois.close();
	}
    //定義對象寫入流  
	public static void writeObj()throws IOException
	{
      	//創建對象寫入流  
	    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
         //寫入對象數據  
		oos.writeObject(new Person("lisi0",399,"kr"));
        //關閉流資源  
		oos.close();
	}
}

/*
沒有方法的接口通常稱爲標記
*/

import java.io.*;
//創建Person類,實現序列化
class Person implements Serializable
{
	//定義自身的序列化方式  
	public static final long serialVersionUID = 42L;
    //定義私有屬性  
	private String name;
	//age被transient修飾後就不能被序列化了,保證其值只在堆內存中存在,而不再文本文件中存在
	transient int age;
	//靜態成員變量不能被序列化
	static String country = "cn";
	//構造Person類  
	Person(String name,int age,String country)
	{
		this.name = name;
		this.age = age;
		this.country = country;
	}
	//覆寫toString方法  
	public String toString()
	{
		return name+":"+age+":"+country;
	}
}

                                          知識點十一      管道流

一、概述:

1、管道流:PipedInputStream和PipedOutputStream

2、管道流輸入輸出可以直接進行連接,通過結合線程使用。

3、PipedInputStream和PipedOutputStream是涉及到多線程技術的IO流對象。

二、使用步驟:

1、要先創建一個讀和寫的兩個類,實現Runnable接口,因爲是兩個不同的線程,覆蓋run方法,注意,需要在內部拋異常

2、創建兩個管道流,並用connect()方法將兩個流連接

3、創建讀寫對象,並傳入兩個線程內,並start執行

示例:

/*
管道流技術,涉及多線程技術
*/

import java.io.*;
//創建Read類,實現run方法  
class Read implements Runnable
{
	private PipedInputStream in;
	
	Read(PipedInputStream in)
	{
		this.in = in;
	}
	//實現run方法  
	public void run()
	{
		try
		{
			byte[] buf = new byte[1024];
             //讀取寫入的數據  
			System.out.println("讀取前。。沒有數據阻塞");
			int len = in.read(buf);
			System.out.println("讀到數據。。阻塞結束");



			String s= new String(buf,0,len);

			System.out.println(s);

			in.close();

		}
		catch (IOException e)
		{
			throw new RuntimeException("管道讀取流失敗");
		}
	}
}
//創建Write類  
class Write implements Runnable
{
	private PipedOutputStream out;
	//Write構造函數
	Write(PipedOutputStream out)
	{
		this.out = out;
	}
	//實現run方法 
	public void run()
	{
		try
		{
		    //寫入數據  
			System.out.println("開始寫入數據,等待6秒後。");
			Thread.sleep(6000);
			out.write("piped lai la".getBytes());
			out.close();
		}
		catch (Exception e)
		{
			throw new RuntimeException("管道輸出流失敗");
		}
	}
}

class  PipedStreamDemo
{
	public static void main(String[] args) throws IOException
	{
        //創建管道流對象  
		PipedInputStream in = new PipedInputStream();
		PipedOutputStream out = new PipedOutputStream();
		//將讀取流(輸入流)和寫入流(輸出流)關聯起來  
		in.connect(out);

		Read r = new Read(in);
		Write w = new Write(out);
		//創建讀寫對象,並創建線程對象  
		new Thread(r).start();
		new Thread(w).start();


	}
}
                                    

                                         知識點十二     RandomAccessFile 類

一、概述:

1、RandomAccessFile稱之爲隨機訪問文件的類,自身具備讀寫方法。

2、該類不算是IO體系中的子類,而是直接繼承Object,但是它是IO包成員,因爲它具備讀寫功能,內部封裝了一個數組,且通過指針對數組的元素進行操作,同時可通過seek改變指針的位置。

3、可以完成讀寫的原理:內部封裝了字節輸入流

4、構造函數:RandomAccessFile(File file,String mode),可已從它的構造函數中看出,該類只能操作文件(也有字符串),而且操作文件還有模式。

模式傳入值:”r“:以只讀方式打開;”rw“:打開以便讀寫

如果模式爲只讀,則不會創建文件,會去讀一個已存在的文件,若文件不存在,則會出現異常,如果模式爲rw,且該對象的構造函數要操作的文件不存在,會自動創建,如果存在,則不會覆蓋,也可通過seek方法修改。

5.RandomAccessFile的最大的作用是實現多線程的下載

二、特有方法:

1、seek(int n):設置指針,可以將指針設置到前面或後面

2、skipBytes(int n):跳過指定字節數,不可往前跳

三、使用步驟:

1、創建RandomAccessFile對象

2、將數據寫入到指定文件中

3、讀取數據,讀入到指定文件中

注意:要想取得後面的數據,需要調用數組指針,通過改變角標位置,取出相應的數據

a.調整對象的指針:seek()

b.跳過指定字節數

示例:

class RandomAccessFileDemo 
{
	public static void main(String[] args) throws IOException
	{
		//writeFile_2();
		//readFile();

		//System.out.println(Integer.toBinaryString(258));

	}

	public static void readFile()throws IOException
	{
	    //"r"代表模式,只讀
		RandomAccessFile raf = new RandomAccessFile("ran.txt","r");
		
		//seek和skipBytes的區別是:skipBytes不能往回跳,seek可以前後的跳,可以隨意改變指針。
		
		//調整對象中指針。
		//raf.seek(8*1);

		//跳過指定的字節數
		raf.skipBytes(8);

		byte[] buf = new byte[4];

		raf.read(buf);

		String name = new String(buf);

		int age = raf.readInt();


		System.out.println("name="+name);
		System.out.println("age="+age);

		raf.close();


	}

	public static void writeFile_2()throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
		raf.seek(8*0);
		raf.write("週期".getBytes());
		raf.writeInt(103);

		raf.close();
	}

	public static void writeFile()throws IOException
	{
		RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");

		raf.write("李四".getBytes());
		raf.writeInt(97);
		raf.write("王五".getBytes());
		raf.writeInt(99);

		raf.close();
	}
}

                                          知識點十三    操作基本數據類型的流對象

一、概述:

1、操作基本數據類型的流對象:DataInputStream和DataOutputStream

2、這兩個讀寫對象,可用於操作基本數據類型的流對象,包含讀寫各種基本數據類型的方法

二、特有方法:

返回值類型                   讀                                                    寫

int型                 writeInt(int n)                               int readInt()

boolean型      writeBoolean(boolean b)         boolean readBoolean()

double型        writeDouble(double d)              double readDouble()

示例:

import java.io.*;
class DataStreamDemo 
{
	public static void main(String[] args) throws IOException
	{
		//writeData();
		//readData();

		//writeUTFDemo();

//		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk");
//
//		osw.write("你好");
//		osw.close();

//		readUTFDemo();

	}
	public static void readUTFDemo()throws IOException
	{
		DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));

		String s = dis.readUTF();

		System.out.println(s);
		dis.close();
	}



	public static void writeUTFDemo()throws IOException
	{
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdate.txt"));

		dos.writeUTF("你好");

		dos.close();
	}

	public static void readData()throws IOException
	{
		DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));

		int num = dis.readInt();
		boolean b = dis.readBoolean();
		double d = dis.readDouble();

		System.out.println("num="+num);
		System.out.println("b="+b);
		System.out.println("d="+d);

		dis.close();
	}
	public static void writeData()throws IOException
	{
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));

		dos.writeInt(234);
		dos.writeBoolean(true);
		dos.writeDouble(9887.543);

		dos.close();

		ObjectOutputStream oos = null;
		oos.writeObject(new O());

		
	}
}

                                                               知識點十四 操作數組和字符串的流對象   

一、操作字節數組的對象:ByteArrayInputStream和ByteArrayOutputStream

1、這個對象並沒有調用底層資源,所以不用關閉流資源

2、存入的是緩衝區,並未用到鍵盤和硬盤燈,所以不需要拋任何IO異常

3、對象中封裝了數組

4、構造函數:

1)ByteArrayInputStream:在構造函數的時候,需要接受數據源,而且數據源是一個字節數據。

2)ByteArrayOutputStream:在構造函數的時候,不用定義數據目的,因爲該對象中已經在內部封裝了可變長度的字節數組,這就是數據的目的地

4、因爲兩個流對象都是操作的是數據,並沒有使用系統資源,所以不用進行close關閉。

6、其實就是用流的思想操作數組

7、特有方法:writeTo(OutputStream out) 這個方法用到了字節輸出流,有異常存在,需要拋IO異常

二、對應的字符數組和字符串:

字符數組流對象:CharArrayReader和CharArrayWriter

字符串流對象:   StringReader和StringWriter

示例:

/*
用於操作字節數組的流對象。

ByteArrayInputStream :在構造的時候,需要接收數據源,。而且數據源是一個字節數組。

ByteArrayOutputStream: 在構造的時候,不用定義數據目的,因爲該對象中已經內部封裝了可變長度的字節數組。
這就是數據目的地。

因爲這兩個流對象都操作的數組,並沒有使用系統資源。
所以,不用進行close關閉。
在流操作規律講解時:
源設備,
	鍵盤 System.in,硬盤 FileStream,內存 ArrayStream。
目的設備:
	控制檯 System.out,硬盤FileStream,內存 ArrayStream。
用流的讀寫思想來操作數據。

toString()方法是將字節緩衝區的數據轉化成字符串

*/
import java.io.*;
class ByteArrayStream 
{
	public static void main(String[] args) 
	{
		//數據源。在內存當中
		ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFD".getBytes());

		//數據目的 也在內存當中
		ByteArrayOutputStream bos = new ByteArrayOutputStream();

		int by = 0;
        //讀取和寫入數據  
		while((by=bis.read())!=-1)
		{
			bos.write(by);
		}

		System.out.println(bos.size());
		System.out.println(bos.toString());

	 try  
        {  
            //方法,此處拋異常,所以上面需要拋出去  
            baos.writeTo(new FileOutputStream("a.txt"));  
        }  
        catch (IOException e)  
        {  
            throw new RuntimeException("寫入文件失敗");  
        }  

	}
}

                                                                             知識點十五  字符編碼

一、概述:

1、字符流的出現爲了方便操作字符,更重要的是加入了編碼的轉換,即轉換流。

2、通過子類進行轉換

3、在兩個對象進行構造時,可加入編碼表

4、可傳入編碼表的有:

1)轉換流:InuputStreamReader和OutputStreamWriter

2)打印流:PrintStream和PrintWriter,只有輸出流

5.編碼表的由來

1)計算機只能識別二進制數據,早期由來是電信號。
2)爲了方便應用計算機,讓它可以識別各個國家的文字。
3)就將各個國家的文字用數字來表示,並一一對應,形成一張表。
這就是編碼表

6、常見的編碼表:

1)ASCII:美國標準信息交換碼錶。用一個字節的7位表示

2)IOS8859-1:拉丁碼錶;歐洲碼錶。用一個字節的8位表示

3)GB2312:中國的中文編碼表

4)GBK:中國的中文編碼表升級,融合了更多的中文文字字符。打頭的是兩個高位爲1的兩個字節編碼。爲負數

5)Unicode:國際標準碼,融合了多種文字

6)UTF-8:最多用三個字節表示一個字符的編碼表,包括:一位、兩位、三位表示的字符

      UTF-8有自己的字節碼:

      一個字節:0開頭

      兩個字節:字節一  ---> 110     位數:10 ~ 6

                    字節二  --->  10      位數:5 ~ 0

      三個字節:字節一  ---> 110     位數:15 ~ 12

                    字節二  --->  10      位數:11 ~ 6

                    字節三 --->  10       位數:5 ~ 0

示例:

import java.io.*;
class EncodeStream 
{
	public static void main(String[] args) throws IOException 
	{
			//writeText();
			readText();
	}

	public static void readText()throws IOException 
	{
		InputStreamReader isr = new InputStreamReader(new FileInputStream("utf.txt"),"gbk");
         
		 //定義數組長度
		char[] buf = new char[10];
		int len = isr.read(buf);

		String str = new String(buf,0,len);

		System.out.println(str);

		isr.close();
	}
	public static void writeText()throws IOException 
	{
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8");

		osw.write("你好");

		osw.close();
	}

}
二、編碼和解碼:

1、編碼:字符串變成字節數組

      解碼:字節數組變成字符串

2、轉換:

1)默認字符集:

      String  --->  byte[]   :srt.getBytes()

      byte[]   --->  String  :new String(byte[])

2)指定字符集:

      String  --->  byte[]   :srt.getBytes(charsetName)

      byte[]   --->  String  :new String(byte[],charsetName)

示例:

import java.util.*;
class  EncodeDemo
{
	public static void main(String[] args)throws Exception 
	{
		String s = "哈哈";
         
	   //對s進行編碼
		byte[] b1 = s.getBytes("GBK");
        //把數組變成字符串
		System.out.println(Arrays.toString(b1));
		String s1 = new String(b1,"utf-8");
		System.out.println("s1="+s1);

		//對s1進行iso8859-1編碼。
		byte[] b2 = s1.getBytes("utf-8");
		System.out.println(Arrays.toString(b2));

		String s2 = new String(b2,"gbk");

		System.out.println("s2="+s2);

		

	}
}
三、對於編碼和解碼的字符集轉換

1、如果編碼失敗,解碼就沒意義了。

2、如果編碼成功,解碼出來的是亂碼,,則需對亂碼通過再次編碼(用解錯碼的編碼表),然後再通過正確的編碼表解碼。針對於IOS8859-1是通用的。

3、如果用的是GBK編碼,UTF-8解碼,那麼再通過2的方式,就不能成功了,因爲UTF-8也支持中文,在UTF-8解的時候,會將對應的字節數改變,所以不會成功。


四、'' 聯通產生的亂碼問題":

1)關於聯通兩個字在記事本爲什麼會顯示亂碼問題的總結
聯通按照gbk的編碼進行存儲,但是這兩個字的二進制是11000001  10101010
正好符合utf-8的編碼規範進行解碼,直接去查UTF-8的碼錶進行解碼

2)“聯通”二字這樣的情況,還有別的,比如“透支”二字。

假設你保存文檔時選擇另存爲,編碼格式選擇UTF-8,那麼你再打開那個文本文檔時就不會發生亂碼了。

示例:

class EncodeDemo2 
{
	public static void main(String[] args) throws Exception
	{
	        String s = "聯通";
                byte[] by = s.getBytes("gbk");

		for(byte b : by)
		{
			System.out.println(Integer.toBinaryString(b&255));
		}


		System.out.println("Hello World!");
	}
}

                                                                      知識點十六   練習

有五個學生,每個學生有3門課的成績,
從鍵盤輸入以上數據(包括姓名,三門課成績),
輸入的格式:如:zhagnsan,30,40,60計算出總成績,
並把學生的信息和計算出的總分數高低順序存放在磁盤文件"stud.txt"中。
步驟:

1,描述學生對象。
2,定義一個可操作學生對象的工具類。
思想:
1,通過獲取鍵盤錄入一行數據,並將該行中的信息取出封裝成學生對象。
2,因爲學生有很多,那麼就需要存儲,使用到集合。因爲要對學生的總分排序。
所以可以使用TreeSet。
3,將集合的信息寫入到一個文件中。

下面給出詳細的代碼和註釋:

import java.io.*;
import java.util.*;
//定義學生類 
class Student implements Comparable<Student>
{
    //定義私有屬性 
	private String name;
	private int ma,cn,en;
	private int sum;
    //構造Student函數,初始化
	Student(String name,int ma,int cn,int en)
	{
		this.name = name;
		this.ma = ma;
		this.cn = cn;
		this.en = en;
		sum = ma + cn + en;
	}
    //覆寫compareTo方法,按學生總成績排序
	public int compareTo(Student s)
	{
		int num = new Integer(this.sum).compareTo(new Integer(s.sum));
		if(num==0)
			return this.name.compareTo(s.name);
		return num;
	}
     //獲取學生信息  
	public String getName()
	{
		return name;
	}
	public int getSum()
	{
		return sum;
	}
    //覆寫hasdCode()和equals()方法,排除相同的兩個學生
	public int hashCode()
	{
		return name.hashCode()+sum*78;

	}
	public boolean equals(Object obj)
	{
		if(!(obj instanceof Student))
			throw new ClassCastException("類型不匹配");
		Student s = (Student)obj;

		return this.name.equals(s.name) && this.sum==s.sum;
	} 
	//定義學生信息顯示格式  
     public String toString()
	{
		return "student["+name+", "+ma+", "+cn+", "+en+"]";
	}
}
//工具類,將鍵盤錄入的輸入存入集合,並將集合的元素寫入文件中
class StudentInfoTool
{
    //按照默認的比較方式
	//無比較器的學生集合  
	public static Set<Student> getStudents()throws IOException
	{
		return getStudents(null);
	}
     //按照指定比較器的比較方式
	 //具備比較器的學生集合 
	public static Set<Student> getStudents(Comparator<Student> cmp)throws IOException
	{
     	//讀取鍵盤的錄入,記牢固
		BufferedReader bufr = 
			new BufferedReader(new InputStreamReader(System.in));

		String line = null;
		
		Set<Student> stus  = null;
		//選擇集合是否有比較器  
		if(cmp==null)
			stus = new TreeSet<Student>();
		else
			stus = new TreeSet<Student>(cmp);
	    //循環讀取鍵盤錄入的數據  
		while((line=bufr.readLine())!=null)
		{
			if("over".equals(line))
				break;
			 //對讀取的數據進行分割並存入集合  
			String[] info = line.split(",");
			
			//Integer.parseInt是將字符轉換成整形數據
			Student stu = new Student(info[0],Integer.parseInt(info[1]),
										Integer.parseInt(info[2]),
										Integer.parseInt(info[3]));

			
			stus.add(stu);
		}

		bufr.close();

		return stus;
	}
    
	//將集合中的數據寫入到文件中去
	public static void write2File(Set<Student> stus)throws IOException
	{
	    // 創建寫入流對象,向文件中系寫入數據
		BufferedWriter bufw = new BufferedWriter(new FileWriter("stuinfo.txt"));
         //循環寫入數據  
		for(Student stu : stus)
		{
		    //bufw.write(stu.toString()+"\t");是爲什麼???
			//\t爲製表符
			bufw.write(stu.toString()+"\t");
			bufw.write(stu.getSum()+"");
			bufw.newLine();
			bufw.flush();
		}

		bufw.close();

	}
}

class StudentInfoTest 
{
	public static void main(String[] args) throws IOException
	{
         //強行逆轉排序比較器 ,反轉比較器,將成績從大到小排
		Comparator<Student> cmp = Collections.reverseOrder();
        //將錄入的學生信息存入集合  
		Set<Student> stus = StudentInfoTool.getStudents(cmp);
        //將信息寫入指定文件中 
		StudentInfoTool.write2File(stus);
	}
}

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

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


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