Java系列(十八)__Java IO(2)
1、System类对IO支持
对于System.out.println()实际上是一个非常熟悉的语法了,但是下面来看一下,在System类之中定义的三个常量:
· 错误输出:public static final PrintStream err;
· 系统输出:public static final PrintStream out;
· 系统输入:public static final InputStream in。
实际上之前一直使用的系统打印输出,都是调用的IO操作完成的,利用PrintStream类完成。
1、 错误输出:System.err
package cn.mldn.demo; public class TestDemo { public static void main(String[] args) throws Exception { try { Integer.parseInt("abc") ; } catch (Exception e) { System.err.println(e) ; System.out.println(e) ; } } } |
最早设计两个输出的目的在于:err是输出不希望用户看见的错误信息,而out是输出用户可以看见的信息,但是对于err意义不大,因为所有的异常打印都使用printStackTrace()方法完成。
2、 系统输出:System.out
System.out对应的是标准输出设备 —— 显示器,其默认就是表示向显示器屏幕上显示数据。
范例:做一个荒唐的功能,利用OutputStream实现屏幕输出
package cn.mldn.demo; import java.io.OutputStream; public class TestDemo { public static void main(String[] args) throws Exception { OutputStream output = System.out ; // 系统输出 output.write("Hello World.".getBytes()); // 向屏幕上输出 } } |
现在的OutputStream的输出位置完全由实例化它的对象来决定。
3、 系统输入:System.in
在很多语言里面都支持有键盘输入功能,但非常遗憾的是Java没有直接提供,而且要想实现键盘的输入数据,则只能够利用System.in完成,System.in对应着标准输入设备 —— 键盘。
范例:实现键盘输入
package cn.mldn.demo; import java.io.InputStream; public class TestDemo { public static void main(String[] args) throws Exception { InputStream input = System.in; // 由键盘输入而来 System.out.print("请输入数据:"); byte data[] = new byte[1024]; // 保存输入数据 int len = input.read(data); // 接收键盘数据 System.out.println(new String(data, 0, len)); input.close(); } } |
但是这个时候的键盘输入是存在有问题的,因为有长度限制。发现超过长度外的数据就不接收了,这个问题如何解决呢?
范例:不设置读取长度
package cn.mldn.demo; import java.io.InputStream; public class TestDemo { public static void main(String[] args) throws Exception { InputStream input = System.in; // 由键盘输入而来 System.out.print("请输入数据:"); StringBuffer buf = new StringBuffer(); int temp = 0 ; // 保存每次读取的数据 while ((temp = input.read()) != -1) { if (temp == '\n') { break ; } buf.append((char) temp); } System.out.println(buf); input.close(); } } |
此时已经解决了长度的限制,但是如果输入中文发现是不可能正常完成的。
2、BufferedReader
如果要想进行中文的处理肯定使用字符流操作是最方便的,那么在输入数据的时候不能按照字节输入,应该将所有输入的内容保存在一个缓冲区之中,而后一次性的从该缓冲区里面读取数据,这样才能更好的避免掉中文输入问题,所以就必须使用BufferedReader类来实现键盘输入。那么首先来观察一下此类的继承结构。
java.lang.Object java.io.Reader java.io.BufferedReader |
此类的构造方法:public BufferedReader(Reader in);
在此类之中还定义了一个读取一行数据的方法:public String readLine() throws IOException,此方法返回的是String型数据,那么就意味着可以向任意的数据类型转换,可以使用正则进行验证。
范例:实现键盘输入
package cn.mldn.demo; import java.io.BufferedReader; import java.io.InputStreamReader; public class TestDemo { public static void main(String[] args) throws Exception { // System.in是InputStream类对象,而BufferedReader需要接收的是Reader类对象; // InputStreamReader是Reader的子类,此类构造可以接收InputStream类对象 BufferedReader buf = new BufferedReader( new InputStreamReader(System.in)); System.out.print("请输入数据:"); String msg = buf.readLine() ; // 读取数据 System.out.println(msg); buf.close() ; } } |
以上的代码就形成了一个键盘输入数据的操作,可以感觉到实在是很麻烦。
范例:现在由键盘输入一个生日数据
package cn.mldn.demo; import java.io.BufferedReader; import java.io.InputStreamReader; import java.text.SimpleDateFormat; public class TestDemo { public static void main(String[] args) throws Exception { // System.in是InputStream类对象,而BufferedReader需要接收的是Reader类对象; // InputStreamReader是Reader的子类,此类构造可以接收InputStream类对象 BufferedReader buf = new BufferedReader( new InputStreamReader(System.in)); System.out.print("请输入生日(yyyy-mm-dd):"); String msg = buf.readLine() ; // 读取数据 if (msg.matches("\\d{4}-\\d{2}-\\d{2}")) { System.out.println(new SimpleDateFormat("yyyy-MM-dd") .parse(msg)); } else { System.out.println("输入错误!"); } buf.close() ; } } |
如果键盘接收的数据返回的是String一定要使用。
3、Scanner
在JDK 1.5之后增加了一个新的类:java.util.Scanner类,此类专门负责处理输入数据的操作,此类的处理要比BufferedReader更加简单,那么在这个类之中主要关心如下几个方法:
· 构造方法:public Scanner(InputStream source);
· 设置读取分割符:public Scanner useDelimiter(String pattern);
· 判断是否有数据:public boolean hasNextXxx();
· 取数据:public 数据 nextXxx()。
范例:利用Scanner实现键盘输入
package cn.mldn.demo; import java.util.Scanner; public class TestDemo { public static void main(String[] args) throws Exception { Scanner scan = new Scanner(System.in) ; scan.useDelimiter("\n") ; System.out.print("请输入信息:"); if (scan.hasNext()) { // 有数据 String data = scan.next() ; System.out.println(data); } scan.close() ; } } |
范例:输入整型数据,直接提供有数据判断
package cn.mldn.demo; import java.util.Scanner; public class TestDemo { public static void main(String[] args) throws Exception { Scanner scan = new Scanner(System.in) ; System.out.print("请输入年龄:"); if (scan.hasNextInt()) { // 输入的是int型数据 System.out.println(scan.next()); } else { System.out.println("输入的数据有错误!"); } scan.close() ; } } |
范例:输入日期,只能够自己编写正则验证,自己转型
package cn.mldn.demo; import java.text.SimpleDateFormat; import java.util.Scanner; public class TestDemo { public static void main(String[] args) throws Exception { Scanner scan = new Scanner(System.in) ; System.out.print("请输入生日:"); if (scan.hasNext("\\d{4}-\\d{2}-\\d{2}")) { // 输入的是int型数据 System.out.println(new SimpleDateFormat("yyyy-mm-dd").parse(scan .next())); } else { System.out.println("输入的数据有错误!"); } scan.close() ; } } |
以上的操作除了中间的判断和接收数据有用之外,其它的代码意义不大,那么下面使用Scanner实现一个文件数据的读取操作。
范例:读取文件
package cn.mldn.demo; import java.io.File; import java.io.FileInputStream; import java.util.Scanner; public class TestDemo { public static void main(String[] args) throws Exception { Scanner scan = new Scanner(new FileInputStream(new File("D:" + File.separator + "my.txt"))); scan.useDelimiter("\n") ; while (scan.hasNext()) { System.out.println(scan.next()); } scan.close() ; } } |
结论:第八个代码模型
· 如果由程序输出内容那么使用打印流(PrintStream或PrintWriter);
· 如果程序输入数据使用Scanner(如果有时候Scanner不好使的时候使用BufferedReader)。
4、对象序列化
对象序列化指的是将保存在内存中的对象转化为二进制数据的形式,这样就可以将对象保存在文件之中或者是进行网络传输。但是如果要想实现对象序列化的操作,有一个要求:对象所在的类一定要实现java.io.Serializable接口,此接口没有任何的方法,所以是一个标识接口。
范例:定义可以被序列化的对象
@SuppressWarnings("serial") class Person implements Serializable { // 此类对象可以被序列化 } |
如果在开发之中,用户只关心这个接口的实现,而具体如何实现序列化和反序列化可以暂时不理会,那么下面将通过代码演示序列化和反序列化的操作。
这两个操作要分别使用两个类完成:ObjectOutputStream、ObjectInputStream,来观察这两个类的继承结构和构造方法
对象序列化:ObjectOutputStream | 对象反序列化:ObjectInputStream |
java.lang.Object java.io.OutputStream java.io.ObjectOutputStream | java.lang.Object java.io.InputStream java.io.ObjectInputStream |
public ObjectOutputStream(OutputStream out) throws IOException | public ObjectInputStream(InputStream in) throws IOException |
public final void writeObject(Object obj) throws IOException | public final Object readObject() throws IOException, ClassNotFoundException |
范例:对象序列化
@SuppressWarnings("serial") class Person implements Serializable { // 此类对象可以被序列化 private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "姓名:" + this.name + ",年龄:" + this.age; } } |
public class TestDemo { public static void main(String[] args) throws Exception { File file = new File("D:" + File.separator + "person.ser"); ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream(file)) ; output.writeObject(new Person("张三",20)); output.close(); } } |
范例:读取对象 —— 反序列化
public class TestDemo { public static void main(String[] args) throws Exception { File file = new File("D:" + File.separator + "person.ser"); ObjectInputStream input = new ObjectInputStream(new FileInputStream(file)) ; System.out.println(input.readObject()); input.close(); } } |
但是在默认情况下,一个对象之中的所有属性都被序列化了,如果现在希望某些属性不被序列化,则在属性声明时可以加上transient关键字。
private transient String name; |
而你们需要放心的时候,对于序列化和反序列化日后会有机制帮助用户自动完成。