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类的常用方法有:

       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类的主要操作方法如下:

  下面通过对象输入流将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接口实现序列化要麻烦很多。两者的实现方式还有以下区别:


一般的开发中,因为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类,为其增加具体的操作。


(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个数字之一,或者输入的是一串字母,应显示出错误提示信息“此选票无效,请输入正确的候选人代号!”  并继续等待输入
  • 输入结束后,显示所有候选人的得票情况。
  • 输出最终当选者的相关信息
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值