天天看點

12. JAVA IO Part 4 (對象的序列化) ----- 學習筆記

12.16 對象序列化

       12.16.1 基本概念與Serializable接口

       對象序列化就是把一個對象變為二進制的資料流的一種方法。通過對象序列化可以友善地實作對象的傳輸或存儲。

*****如果一個類得對象想被序列化,則對象所在的類必須實作java.io.Serializable接口。 此接口定義格式如下:*****

public interface Serializable{}           

接口Serializable中,并沒有定義任何方法。是以此接口隻是一個辨別接口。表示一個類具備了被序列化的能力!!!

範例:定義可序列化的類

package org.forfan06.serializabledemo;
import java.io.Serializable;
public class Person implements Serializable{
    private String name;
    private int age;
    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }
    public String toString(){
        return "姓名:" + this.name + "; 年齡:" + this.age;
    }
}           

        上面的Person類實作了序列化接口Serializable, 是以此類的對象是可以經過二進制資料流進行傳輸的。而如果要完成對象的輸入或輸出,還必須依靠對象輸出流(ObjectOutputStream類)和對象輸入流(ObjectInputStream類)。

        使用對象輸出流(ObjectOutputStream類)輸出序列化對象的過程稱為序列化,而使用對象輸入流(ObjectInputStream)讀入對象的過程稱為反序列化。

  • 對象序列化和對象反序列化操作時的版本相容型問題

       在對象進行序列化或反序列化操作時,要考慮到JDK版本的問題。如果序列化的JDK版本和反序列化的JDK版本不統一則有可能造成異常, 是以在序列化操作中引入了一個serialVersionUID常量, 可以通過此常量來驗證版本的一緻性。

       在進行反序列化時,JVM會把傳來的位元組流中的serialVersionUID與本地相應實體(類)的serialVersionUID進行比較。如果相同就認為是一緻的,可以進行反序列化;否則就會出現序列化版本不一緻的異常。

       當實作java.io.Serializable接口的實體(類)沒有顯式地定義一個名為serialVersionUID、類型為long的變量時,Java序列化機制在編譯時會自動生成一個此版本的serialVersionUID。

       當然,如果不希望通過編譯來自動生成,也可以直接顯式地定義一個名為serialVersionUID、類型為long的變量,隻要不修改這個變量值的序列化實體,都可以互相進行串行化和反串行化。

===可以在上面程式Person類中加入以下的常量即可:

private static final long serialVersionUID = 1L;           

serialVersionUID具體内容由使用者指定!!!

       12.16.2 對象輸出流 ObjectOutputStream

         一個對象如果要進行輸出,則必須使用ObjectOutputStream類(對象輸出流)。 ObjectOutputStream類得定義如下:

public class ObjectOutputStream extends OutputStream implement ObjectOutput, ObjectStreamConstants           

      ObjectOutputStream類屬于OutputStream類的子類;ObjectOutputStream類的常用方法有:

12. JAVA IO Part 4 (對象的序列化) ----- 學習筆記

       ObjectOutputStream類的使用形式和PrintStream類非常相似,在執行個體化時也需要傳入一個OutputStream的子類對象,然後根據傳入的OutputStream子類對象的不同,輸出的位置也不同!!!

範例:将Person類的對象儲存在檔案中

package org.forfan06.serializabledemo;
import java.io.Serializable;
public class Person implements Serializable{
    private String name;
    private int age;
    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }
    public String toString(){
        return "姓名:" + this.name + "; 年齡:" + this.age;
    }
}

package org.forfan06.serializabledemo;
import java.io.File;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class SerializableDemo01{
    public static void main(String args[]) throws Exception{
        File f = new File("E:" + File.separator + "test.txt");
        ObjectOutputStream oos = null;
        OutputStream out = new FileOutputStream(f);
        oos = new ObjectOutputStream(out);
        oo.writeObject(new Person("forfan06", 28));
        oos.close();
    }
}           

上面demo将内容儲存到檔案中,但是儲存的内容全是二進制資料。儲存的檔案本身不可以直接修改,因為會破壞其儲存格式!!!!!

Q:到底序列化了哪些内容呢??? 一個對象被序列化後,到底哪些内容被儲存下來,是屬性還是方法???

  • 隻有屬性被序列化了!!!在面對對象基礎部分曾經提到,每個對象都具備相同的方法,但是每個對象的屬性不一定相同,也就是說, 對象儲存的隻有屬性資訊,那麼在序列化操作時也同樣的是這個道理,隻有屬性被序列化了!!!!!!!!!!

       12.16.3 對象輸入流 ObjectInputStream

         使用ObjectInputStream類可以直接把被序列化好的對象反序列化。

ObjectInputStream類得定義格式如下:

public class ObjectInputStream extend InputStream implements ObjectInput, ObjectStreamConstants           

ObjectInputStream類是InputStream類的一個子類, 與PrintStream類得使用類似。ObjectInputStream類同樣需要接收InputStream類的執行個體對象才可以執行個體化。

ObjectInputStream類的主要操作方法如下:

12. JAVA IO Part 4 (對象的序列化) ----- 學習筆記

  下面通過對象輸入流将SerializableDemo01中儲存在檔案中的對象讀取出來,此過程也稱為反序列化。

範例:對象的反序列化操作, 從檔案中把Person對象反序列化

package org.forfan06.serializabledemo;
import java.io.File;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class SerializableDemo02{
    public static void main(String args[]) throws Exception{
        File f = new File("E:" + File.separator + "test.txt");
        ObjectInputStream ois = null;
        InputStream input = new FileInputStream(f);  //檔案輸入流
        ois = new ObjectInputStream(input);
        Object obj = ois.readObject();   //讀取對象
        ois.close();
        System.out.println(obj);
    }
}           

運作結果:

姓名: forfan06; 年齡:28

解析:從程式結果可以發現,實作了Serializable接口的類,對象中的所有屬性都被序列化。

*****如果使用者想根據自己的需要選擇被序列化的屬性,則可以使用另外一種序列化接口 ---> Externalizable接口*****

問題: 可不可以讓所有的類都實作Serializable接口?? 一個類如果實作了Serializable接口後可以直接序列化,而且此接口中沒有任何的方法,也不會讓實作此接口的類增加不必要的操作,那麼所有的類都實作此接口的話不是更好嗎???這樣也可以增加類得一個功能。

Answer:

  • 肯定不可以讓所有的淚都實作Serializable接口。 在目前已知的JDK版本中,java.io.Serializable接口中都沒有定義任何方法。如果所有的類都實作此接口在文法上并沒有任何的問題,但是,如果在以後的JDK版本中修改了此接口,而且有增加了許多方法呢??那麼以往的系統中所有的類就都被修改,這樣肯定會很麻煩的。。。是以最好在隻有需要被序列化對象的類實作Serializable接口。

       12.16.4 Externalizable接口

        被Serializable接口聲明的類的對象,其内容都将被序列化;但是,如果使用者希望自己指定被序列化的内容,則可以讓一個類實作Externalizable接口。

Externalizable接口的定義如下:

public interface Externalizable extend Serializable{
     public void writeExternal(ObjectOutput out) throws IOException;
     public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}           

Externalizable接口繼承了Serializable接口。是Serializable接口的子接口!! 在此接口中定義了兩個方法,這兩個方法的作用如下:

  • writeExternal(ObjectOutput out):  在此方法中指定要儲存的屬性資訊,對象序列化時調用!
  • readExternal(ObjectInput in): 在此方法中讀取被儲存的資訊,對象反序列化時調用。

writeExternal(ObjectOutput out)和readExternal(ObjectInput in)方法的參數類型是ObjectOutput和ObjectInput。 這兩個接口的定義如下:

--------->   ObjectOutput接口定義:

public interface ObjectOutput extends DataOutput           

--------->   ObjectInput接口定義:

public interface ObjectInput extends DataInput           

上面兩個接口分别繼承DataOutput和DataInput, 這樣在這兩個方法中就可以像DataOutputStream和DataInputStream那樣直接輸出和讀取各種類型的資料!!!

  如果一個類要使用 Externalizable接口實作序列化時, 在此類中必須存在一個無參構造方法, 因為在反序列化時會預設調用無參構造執行個體化對象!!如果沒有此無參構造方法,則運作時将會出現異常, 這點的實作機制與Serializable接口是不同的!!

範例: 修改Person類并實作Externalizable接口

package org.forfan06.serializabledemo;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectOutput;
import java.io.ObjectInput;
public class Person implements Externalizable{
    private String name;
    private int age;
    public Person(){  //必須定義無參構造方法
        
    }
    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }
    public String toString(){
        return "姓名:" + this.name + "; 年齡:" + this.age;
    }
    //覆寫接口Externalizable中定義的兩個抽象方法
    public void writeExternal(ObjectOutput out) throws IOException{
        out.writeObject(this.name);
        out.writeInt(this.age);
    }
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException{
        this.name = (String) in.readObject();
        this.age = in.readInt();
    }
}           

以上程式,Person類實作了Externalizable接口,這樣使用者在類中有選擇地儲存需要的屬性或其他的具體資料。 在這裡,我們将全部屬性都儲存下來了!

注意:  out.writeObject(this.name);  --> 是因為ObjectOutput類中定義了writeObject(Object obj), 其子接口ObjectOutputStream類中writeObject(Object obj)是實作了此方法

ObjectOutput類中自己定義了以下方法:

      close()、flush()、write(byte[] b)、write(byte[] b, int off, int len)、write(int b)、writeObject(Object obj)。他們都是抽象方法!!!!!!

另外,ObjectOutput類也從java.io.DataOutput中繼承了以下方法:

Methods inherited from interface java.io.DataOutput

writeBoolean,writeByte,writeBytes, writeChar, writeChars, writeDouble, writeFloat, writeInt, writeLong, writeShort, writeUTF

範例: 序列化和反序列化Person對象。

(1)Person類的定義:

package org.forfan06.serializabledemo;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectOutput;
import java.io.ObjectInput;
public class Person implements Externalizable{
    private String name;
    private int age;
    public Person(){  //必須定義無參構造方法
        
    }
    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }
    public String toString(){
        return "姓名:" + this.name + "; 年齡:" + this.age;
    }
    //覆寫接口Externalizable中定義的兩個抽象方法
    public void writeExternal(ObjectOutput out) throws IOException{
        out.writeObject(this.name);
        out.writeInt(this.age);
    }
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException{
        this.name = (String) in.readObject();
        this.age = in.readInt();
    }
}           

(2)序列化和反序列化Person類對象

/*
 *對象的序列化、反序列化操作!!!!
 */
package org.forfan06.serializabledemo;
import java.io.File;
import java.io.OutputStream;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SerializableDemo03{
    public static void main(String args[]) throws Exception{
        ser();     //序列化
        dser();    //反序列化
    }
    
    //對象的序列化
    public static void ser() throws IOException{
        File f = new File("E:" + File.separator + "test.txt");
        ObjectOutputStream oos = null;
        OutputStream out = new FileOutputStream(f);  //檔案輸入流
        oos = new ObjectOutputStream(out);   //執行個體化對象輸出流
        oos.writeObject(new Person("forfan", 28));   //儲存對象到檔案
        oos.close();  //關閉輸出流
    }
    //對象的反序列化
    public static void dser() throws IOException, ClassNotFoundException{
        File f = new File("E:" + File.separator + "test.txt");
        ObjectInputStream ois = null;
        InputStream input = new FileInputStream(f);  //檔案輸入流
        ois = new ObjectInputStream(input);   //為對象輸入流執行個體化
        Object obj = ois.readObject();  //讀取對象
        ois.close();  //關閉輸入流
        System.out.println(obj);  
    }
}           

使用Externalizable接口實作序列化明顯要比使用Serializable接口實作序列化要麻煩很多。兩者的實作方式還有以下差別:

12. JAVA IO Part 4 (對象的序列化) ----- 學習筆記

一般的開發中,因為Serializable接口的使用較為友善,是以出現比較多。

       12.16.5 transient關鍵字

Serializable接口實作的操作實際上是将一個對象中的全部屬性進行序列化;當然,也可以使用Externalizable接口以實作部分屬性的序列化,但是這樣操作比較複雜。

當使用Serializable接口實作序列化操作時,如果一個對象中的某個/些屬性不希望被序列化時,可以使用關鍵字transient來修飾(進行聲明), 例如:

範例:Person類中的name屬性不希望被序列化

package org.forfan06.serializabledemo;
import java.io.Serializable;
public class Person implements Serializable{
	private transient String name;
	private int age;
	public Person(String name, int age){
		this.name = name;
		this.age = age;
	}
	public String toString(){
		return "姓名: " + this.name + ";年齡 : " + this.age;
	}
}           

範例:重新儲存、再讀取對象

package org.forfan06.serializabledemo;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
public class SerializableDemo04{
	public static void main(String args[]) throws Exception{
		ser();
		dser();
	}
	public static void ser() throws Exception{
		File f = new File("E:" + File.separator + "test.txt");
		ObjectOutputStream oos = null;
		OutputStream out = new FileOutputStream(f);
		oos = new ObjectOutputStream(out);
		oos.writeObject(new Person("forfan06", 28));
		oos.close();
	}
	public static void dser() throws Exception{
		File f = new File("E:" + File.separator + "test.txt");
		ObjectInputStream ois = null;
		InputStream input = new FileInputStream(f);
		ois = new ObjectInputStream(input);
		Object obj = ois.readObject();
		ois.close();
		System.out.println(obj);
	}
}           

運作結果:

姓名:null; 年齡:28           

 以上程式中的姓名為null,表示内容沒有被序列化儲存下來。這樣在對象反序列化後,輸出時姓名就是預設值null。

       12.16.6 序列化一組對象

        對象輸出時,隻提供了一個對象的輸出操作(writeObject(Object obj)),并沒有提供多個對象的輸出。

        如果要同時序列化多個對象,可以使用對象數組進行操作。因為數組也是屬于引用資料類型,是以可以直接使用Object類型進行接收的!!!

範例:序列化多個Person對象

(1)Person類的定義:

package org.forfan06.serializabledemo;
import java.io.Serializable;
public class Person implements Serializable{
    private String name;
    private int age;
    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }
    public String toString(){
        return "姓名:" + this.name + "; 年齡:" + this.age;
    }
}           

(2)測試類

package org.forfan06.serializabledemo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
public class SerializableDemo05{
	public static void main(String args[]) throws Exception{
		Person per[] = {new Person("forfan06", 28), new Person("eric", 31), new Person("linda", 17)};
		ser(per);
		Object obj[] = dser();
		for(int i = 0; i < obj.length; i++){
			Person p = (Person) obj[i];
			System.out.println(p);
		}
	}
	public static void ser(Object obj[]) throws Exception{
		File f = new File("E:" + File.separator + "test.txt");
		ObjectOutputStream oos = null;
		OutputStream out = new FileOutputStream(f);
		oos = new ObjectOutputStream(out);
		oos.writeObject(obj);
		oos.close();
	}
	public static Object[] dser() throws Exception{
		File f = new File("E:" + File.separator + "test.txt");
		ObjectInputStream ois = null;
		InputStream input = new FileInputStream(f);
		ois = new ObjectInputStream(input);
		Object obj[] = (Object[]) ois.readObject();
		ois.close();
		return obj;
	}
}           

       以上程式中,使用對象數組可以儲存多個對象;但是,數組本身存在長度的限制,為了解決數組中的長度問題,可以使用動态對象數組(類集)完成。

12.17 執行個體操作 -- 單人資訊關系程式

          将前面的菜單程式進行擴充,要求增加時可以增加一個人的完整資訊,人的資訊包括姓名和年齡。儲存後也可以修改、删除、查詢此資訊

          可以使用對象序列化儲存。此時程式可以使用前面講解過的InputData、Person、Operate、Menu幾個類。 需要增加檔案操作類,專門負責儲存和讀取檔案的内容,并修改Operate類,為其增加具體的操作。

12. JAVA IO Part 4 (對象的序列化) ----- 學習筆記

(1)增加檔案操作類FileOperate

package org.forfan06.execdemo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class FileOperate{
	private File file = null;
	public FileOperate(String pathName){
		this.file = new File(pathName);
	}
	public boolean save(Object obj) throws Exception{
		ObjectOutputStream oos = null;
		boolean flag = false;
		try{
			oos = new ObjectOutputStream(new FileOutputStream(this.file));
			oos.writeObject(obj);
			flag = true;
		}catch (Exception e){
			e.printStackTrace();
		}finally{
			if(oos != null){
				oos.close();
			}
		}
		return flag;
	}
	public Object load() throws Exception{
		Object obj = null;
		ObjectInputStream ois = null;
		try
		{
			ois = new ObjectInputStream(new FileInputStream(this.file));
			obj = ois.readObject();
		}catch (Exception e){
			throw e;
		}finally{
			if (ois != null){
				ois.close();
			}
		}
		return obj;
	}
}           

以上程式中的FileOperate類的功能就是向程式中寫入對象和 讀取對象,在操作時隻需要傳入一個路徑即可!

(2)修改Person類,增加setter和 getter方法

package org.forfan06.execdemo;
import java.io.Serializable;
public class Person implements Serializable{
	private String name;
	private int age;
	public Person(String name, int age){
		this.name = name;
		this.age = age;
	}
	public String toString(){
		return "姓名:" + this.name + ";年齡:" + this.age;
	}
	public String getName(){
		return name;
	}
	public int getAge(){
		return age;
	}
	public void setName(String name){
		this.name = name;
	}
	public void setAge(int age){
		this.age = age;
	}
}           

需要在增加、修改、删除、顯示的地方編寫具體的代碼,但是在修改時應該先查詢出來,顯示已有的内容。

(3)修改操作類

package org.forfan06.execdemo;
import java.io.File;
public class Operate{
	public static void add(){
		InputData input = new InputData();
		FileOperate fo = new FileOperate("E:" + File.separator + "test.per");
		String name = input.getString("請輸入姓名: ");
		int age = input.getInt("請輸入年齡: ", "輸入錯誤!年齡必須是數字!");
		Person per = new Person(name, age);
		try{
			fo.save(per);
		}catch(Exception e){
			e.printStackTrace();
		}
		System.out.println("資訊增加成功");
	}
	public static void delete(){
		FileOperate fo = new FileOperate("E:" + File.separator + "test.per");
		try{
			fo.save(null);
		}catch(Exception e){
			e.printStackTrace();
		}
		System.out.println("資訊删除成功 !");
	}
	public static void update(){
		InputData input = new InputData();
		FileOperate fo = new FileOperate("E:" + File.separator + "test.per");
		Person per = null;
		try{
			per = (Person) fo.load();
		}catch(Exception e){
			e.printStackTrace();
		}
		String name = input.getString("請輸入新的姓名 (原姓名: " + per.getName() + "):");
		int age = input.getInt("請輸入新的年齡( 原 年齡:" + per.getAge() + "):", "年齡必須是數字");
		per = new Person(name, age);
		try{
			fo.save(per);
		}catch(Exception e){
			e.printStackTrace();
		}
		System.out.println("資訊更新成功!");
	}
	public static void find(){
		FileOperate fo = new FileOperate("E:" + File.separator + "test.per");
		Person per = null;
		try{
			per = (Person)fo.load();
		}catch(Exception e){
			e.printStackTrace();
		}
		System.out.println(per);
	}
}           

(4)專門處理輸入資料的類 InputData類!

package org.forfan06.execdemo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.util.Date;
import java.text.SimpleDateFormat;
public class InputData{
	private BufferedReader buf = null;
	public InputData(){
		this.buf = new BufferedReader(new InputStreamReader(System.in));
	}
	public String getString(String info){
		String temp = null;
		System.out.println(info);
		try{
			temp = this.buf.readLine();
		}catch(IOException e){
			e.printStackTrace();
		}
		return temp;
	}
	public int getInt(String info, String err){
		int temp = 0;
		String str = null;
		boolean flag = true;
		while(flag){
			//str = this.buf.readLine();  should add a try...catch if use this statement, 
			str = this.getString(info);
			if(str.matches("^\\d+$")){
				temp = Integer.parseInt(str);
				flag = false;
			}else{
				System.out.println(err);
			}
		}
		return temp;
	}
	public float getFloat(String info, String err){
		float temp = 0;
		String str = null;
		boolean flag = true;
		while(flag){
			//str = this.buf.readLine();  should add a try...catch if use this statement, 
			str = this.getString(info);
			if(str.matches("^\\d+.?\\d+$")){
				temp = Float.parseFloat(str);
				flag = false;
			}else{
				System.out.println(err);
			}
		}
		return temp;
	}
	public Date getDate(String info, String err){
		Date d = null;
		String str = null;
		boolean flag = true;
		while(flag){
			//str = this.buf.readLine();  should add a try...catch if use this statement, 
			str = this.getString(info);
			if(str.matches("^\\d{4}-\\d{2}-\\d{2}$")){
				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
				try{
					d = sdf.parse(str);
				}catch(ParseException e){
					e.printStackTrace();
				}
				flag = false;
			}else{
				System.out.println(err);
			}
		}
		return d;
	}
}           

(5)界面類Menu

package org.forfan06.execdemo;
public class MyMenu{
	public MyMenu(){
		while(true){
			this.show();
		}
	}
	public void show(){
		System.out.println("=====Xxx系統=====");
		System.out.println("[1]、增加資料");
		System.out.println("[2]、删除資料");
		System.out.println("[3]、修改資料");
		System.out.println("[4]、檢視資料");
		System.out.println("[0]、系統退出\n");
		InputData input = new InputData();
		int i = input.getInt("請選擇: ", "請輸入正确的選項!");
		switch (i){
			case 1:{
				Operate.add();
				break;
			}
			case 2:{
				Operate.delete();
				break;
			}
			case 3:{
				Operate.update();
				break;
			}
			case 4:{
				Operate.find();
				break;
			}
			case 0:{
				System.exit(1);
				break;
			}
			default:{
				System.out.println("請輸入正确的操作!");
			}
		}
	}
}           

(6)測試類ExecDemo05

package org.forfan06.execdemo;
public class ExecDemo05{
	public static void main(String args[]) throws Exception{
		new MyMenu();
	}
}           

12.18 本章要點

  1. 本章所講解到的操作類。它們之間的繼承關系
  2. 在Java中使用File類表示檔案本身,可以直接使用此類完成檔案的各種操作。如建立、删除等
  3. RandomAccessFile類可以從指定的位置開始讀取資訊,但是要求檔案愛你中各個資料的儲存長度必須固定。
  4. 輸入/輸出流主要分為位元組流(OutputStream類、InputStream類)和字元流(Writer類、Reader類)兩種。但是在傳輸中以位元組流操作較多; 字元流在操作時使用到緩沖區,而位元組流沒有用到緩沖區。
  5. 位元組流或字元流都是以抽象類的形式定義的,根據其使用的子類不同,輸入/輸出的位置也不同。
  6. 在IO包中可以使用OutputStreamWriter類和InputStreamReader類,來完成字元流與位元組流之間的轉換操作!!!!
  7. 使用ByteArrayInputStream類和ByteArrayOutputStream類可以對記憶體進行輸入/輸出操作。
  8. 線上程之間進行輸入/輸出通信,主要使用PipedOutputStream類和PipedInputStream類
  9. 在IO中輸出時最好使用列印流(PrintStream類、PrintWrite類),這樣可以友善地輸出各種類型的資料
  10. System類提供了3個支援IO操作的常量: out、err、in。 (1)System.out 對應顯示器的标準輸出  (2)System.err  對應錯誤列印,一般此資訊不希望被使用者看到 (3)System.in 對應标準的鍵盤輸入。  *****在程式操作中,根據setOut()方法可以修改System.out的輸出位置; 使用setErr()方法修改System.err的輸出位置; 使用setIn()方法修改System.in的輸入位置*****
  11. BufferedReader可以直接從緩沖區中讀取資料。    标準的鍵盤輸入!!!!!!!!!!!!!!!!!!!!!!!!!
  12. 使用Scanner類可以友善地進行輸入流操作
  13. 資料操作流提供了與平台無關的資料操作,主要使用DataOutputStream類和DataInputStream類
  14. 使用合并流(SequenceInputStream類)可以将兩個檔案的内容進行合并處理
  15. 如果資料量過大,則可以使用壓縮流壓縮資料。在Java中支援ZIP、JAR、GZIP3種壓縮格式
  16. 使用回退流可以将不需要的資料回退到資料緩沖區中以待重新讀取
  17. 造成字元亂碼的根本原因在于程式編碼與本地編碼的不統一
  18. 對象序列化可以将記憶體中的對象轉化為二進制資料。 但是,對象所在的類必須實作Serializable接口。一個類中的屬性如果使用transient關鍵字聲明(修飾),表明此屬性的内容将不會被序列化
  19. 對象的輸入/輸出主要使用ObjectInputStream類、ObjectOutputStream類來完成

12.19 習題

  1. 編寫Java程式,輸入3個整數,并求出3個整數的最大值和最小值
  2. 從鍵盤輸入檔案的内容和要儲存的檔案名稱,然後根據輸入的名稱建立檔案,并将内容儲存到檔案中
  3. 從鍵盤傳入多個字元串到程式中,并将它們按照逆序輸出到螢幕上
  4. 從鍵盤輸入以下的資料: “TOM:89 | JERRY: 90 | TONY:95”, 資料格式為“姓名:成績 | 姓名:成績 | 姓名:成績” ,對輸入的内容按年齡進行排序,并将排序結果按照成績由高到低排序
  5. 将第4題中的内容進行擴充,可以将全部輸入的資訊儲存在檔案中,還可以添加資訊,并可以顯示全部的資料
  6. 編寫程式,程式運作後,根據螢幕提示輸入一個數字字元串,輸入後統計有多少個偶數數字和奇數數字
  7. 完成系統登入程式,從指令行輸入使用者名和密碼,如果沒有輸入使用者名和密碼,則提示輸入使用者名和密碼;如果輸入了使用者名但是沒有輸入密碼,則提示使用者輸入密碼;然後判斷使用者名是否是csdn,密碼是否是hello;如果正确,則提示登入成功;如果錯誤,則顯示登入失敗的資訊,使用者再次輸入使用者名和密碼,連續3次輸入錯誤後系統退出
  8. 完成檔案複制操作,在程式運作後提示輸入源檔案路徑,然後再輸入目标檔案路徑
  9. 編寫程式,程式運作時輸入目錄名稱,并把該目錄下的所有檔案名字尾改為.txt
  10. 編寫一個投票程式,具體如下:

           (1)功能描述

  • 有一個班采用民主投票方式推選班長,班長候選人共4位,每個人姓名及代号分别為: “張三 1; 李四 2; 王五 3; 趙六 4”。 程式操作員将每張選票上鎖天的代号(1、2、3或4)循環輸入電腦,輸入數字0結束輸入,然後将所有候選人的得票情況顯示出來,并顯示最終當選者的資訊

           (2)具體要求

  • 要求面對對象方法,編寫學生類Student,将候選人姓名、代号和票數儲存到類Student中,并實作相應的getXXX和setXXX方法
  • 輸入資料前,顯示出各位候選人的代碼及姓名(提示,建立一個候選人類型數組)
  • 循環執行接收鍵盤輸入的班長候選人代号,知道輸入的數字為0,結束選票的輸入工作
  • 在接收每次輸入的選票後,要求驗證該選票是否有效!也就是,如果輸入的數不是0、1、2、3、4這5個數字之一,或者輸入的是一串字母,應顯示出錯誤提示資訊“此選票無效,請輸入正确的候選人代号!”  并繼續等待輸入
  • 輸入結束後,顯示所有候選人的得票情況。
  • 輸出最終當選者的相關資訊

繼續閱讀