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 |
---|
|
范例: 序列化和反序列化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 本章要点
- 本章所讲解到的操作类。它们之间的继承关系
- 在Java中使用File类表示文件本身,可以直接使用此类完成文件的各种操作。如创建、删除等
- RandomAccessFile类可以从指定的位置开始读取信息,但是要求文件爱你中各个数据的保存长度必须固定。
- 输入/输出流主要分为字节流(OutputStream类、InputStream类)和字符流(Writer类、Reader类)两种。但是在传输中以字节流操作较多; 字符流在操作时使用到缓冲区,而字节流没有用到缓冲区。
- 字节流或字符流都是以抽象类的形式定义的,根据其使用的子类不同,输入/输出的位置也不同。
- 在IO包中可以使用OutputStreamWriter类和InputStreamReader类,来完成字符流与字节流之间的转换操作!!!!
- 使用ByteArrayInputStream类和ByteArrayOutputStream类可以对内存进行输入/输出操作。
- 在线程之间进行输入/输出通信,主要使用PipedOutputStream类和PipedInputStream类
- 在IO中输出时最好使用打印流(PrintStream类、PrintWrite类),这样可以方便地输出各种类型的数据
- System类提供了3个支持IO操作的常量: out、err、in。 (1)System.out 对应显示器的标准输出 (2)System.err 对应错误打印,一般此信息不希望被用户看到 (3)System.in 对应标准的键盘输入。 *****在程序操作中,根据setOut()方法可以修改System.out的输出位置; 使用setErr()方法修改System.err的输出位置; 使用setIn()方法修改System.in的输入位置*****
- BufferedReader可以直接从缓冲区中读取数据。 标准的键盘输入!!!!!!!!!!!!!!!!!!!!!!!!!
- 使用Scanner类可以方便地进行输入流操作
- 数据操作流提供了与平台无关的数据操作,主要使用DataOutputStream类和DataInputStream类
- 使用合并流(SequenceInputStream类)可以将两个文件的内容进行合并处理
- 如果数据量过大,则可以使用压缩流压缩数据。在Java中支持ZIP、JAR、GZIP3种压缩格式
- 使用回退流可以将不需要的数据回退到数据缓冲区中以待重新读取
- 造成字符乱码的根本原因在于程序编码与本地编码的不统一
- 对象序列化可以将内存中的对象转化为二进制数据。 但是,对象所在的类必须实现Serializable接口。一个类中的属性如果使用transient关键字声明(修饰),表明此属性的内容将不会被序列化
- 对象的输入/输出主要使用ObjectInputStream类、ObjectOutputStream类来完成
12.19 习题
- 编写Java程序,输入3个整数,并求出3个整数的最大值和最小值
- 从键盘输入文件的内容和要保存的文件名称,然后根据输入的名称创建文件,并将内容保存到文件中
- 从键盘传入多个字符串到程序中,并将它们按照逆序输出到屏幕上
- 从键盘输入以下的数据: “TOM:89 | JERRY: 90 | TONY:95”, 数据格式为“姓名:成绩 | 姓名:成绩 | 姓名:成绩” ,对输入的内容按年龄进行排序,并将排序结果按照成绩由高到低排序
- 将第4题中的内容进行扩展,可以将全部输入的信息保存在文件中,还可以添加信息,并可以显示全部的数据
- 编写程序,程序运行后,根据屏幕提示输入一个数字字符串,输入后统计有多少个偶数数字和奇数数字
- 完成系统登录程序,从命令行输入用户名和密码,如果没有输入用户名和密码,则提示输入用户名和密码;如果输入了用户名但是没有输入密码,则提示用户输入密码;然后判断用户名是否是csdn,密码是否是hello;如果正确,则提示登录成功;如果错误,则显示登录失败的信息,用户再次输入用户名和密码,连续3次输入错误后系统退出
- 完成文件复制操作,在程序运行后提示输入源文件路径,然后再输入目标文件路径
- 编写程序,程序运行时输入目录名称,并把该目录下的所有文件名后缀改为.txt
- 编写一个投票程序,具体如下:
(1)功能描述
- 有一个班采用民主投票方式推选班长,班长候选人共4位,每个人姓名及代号分别为: “张三 1; 李四 2; 王五 3; 赵六 4”。 程序操作员将每张选票上锁天的代号(1、2、3或4)循环输入电脑,输入数字0结束输入,然后将所有候选人的得票情况显示出来,并显示最终当选者的信息
(2)具体要求
- 要求面对对象方法,编写学生类Student,将候选人姓名、代号和票数保存到类Student中,并实现相应的getXXX和setXXX方法
- 输入数据前,显示出各位候选人的代码及姓名(提示,建立一个候选人类型数组)
- 循环执行接收键盘输入的班长候选人代号,知道输入的数字为0,结束选票的输入工作
- 在接收每次输入的选票后,要求验证该选票是否有效!也就是,如果输入的数不是0、1、2、3、4这5个数字之一,或者输入的是一串字母,应显示出错误提示信息“此选票无效,请输入正确的候选人代号!” 并继续等待输入
- 输入结束后,显示所有候选人的得票情况。
- 输出最终当选者的相关信息