java IO学习心得

File类:

在整个io包中,唯一表示与文件有关的类就是File类。使用File类可以进行创建或者删除文件等常用的操作。要想使用File类,则首先要观察File类的构造方法,此类的常用构造方法如下:
public File(String pathname)——根据路径找到文件

public static final String pathSeparator明明是一个常量,但是为什么是小写?实际上这就是java的历史遗留问题。

实例一:创建一个新文件

public boolean createNewFile() throws IOException——如果指定的文件不存在并成功地创建,则返回 true;如果指定的文件已经存在,则返回 false。
[java]  view plain  copy
  1. import java.io.File;  
  2. import java.io.IOException;  
  3.   
  4. public class CreateFile {  
  5.   
  6.     public static void main(String[] args) {  
  7.         File file = new File("E:/tmp/test.txt"); // 传入路径,实例化file对象  
  8.         try {  
  9.             file.createNewFile(); // 创建新文件  
  10.         } catch (IOException e) {  
  11.             e.printStackTrace();  
  12.         }  
  13.     }  
  14.   
  15. }  
将在E:\tmp中创建一个test.txt的文件(该文件大小为0byte)。注意:在传入路径的时候要么使用“/”,要么使用"\\"表示一个斜杠。

以上代码已经完成了文件的创建功能,但是如果在开发时按照以上的格式编写,则肯定会出现错误。因为在各个操作系统中,路径的分隔符是不一样的。例如:windows中使用“\”表示文件分隔符;而Linux中使用“/”表示文件分隔符。
如果java程序的可移植性继续保持,则最好是根据所在的操作系统来自动使用文件分隔符。使用File中的两个常量:
public static final String pathSeparator
public static final String separator
[java]  view plain  copy
  1. System.out.println("separator:"+File.separator);      
  2.     System.out.println("pathSeparator:"+File.pathSeparator);  
运行结果:
separator:\
pathSeparator:;

如果想让java程序继续保持可移植性,那么在编写路径的时候就应该使用File类中的常量。
修改以上程序:
[java]  view plain  copy
  1. File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt"); // 传入路径,实例化file对象  
  2.         try {  
  3.             file.createNewFile(); // 创建新文件  
  4.         } catch (IOException e) {  
  5.             e.printStackTrace();  
  6.         }  
在进行路径传入的时候应该使用路径分隔符:File.separator

删除文件:

public boolean delete()
[java]  view plain  copy
  1. File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");  
  2.         file.delete();  
执行以上的程序,文件确实已经被删除了,但是在删除文件的时候应该先判断一下文件是否存在。

判断文件是否存在:

public boolean exists()
[java]  view plain  copy
  1. File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");  
  2.         if (file.exists()) {  
  3.             file.delete();//文件存在则删除之  
  4.         } else {  
  5.             System.out.println("文件不存在!");  
  6.         }  
综合创建、删除文件的操作:
如果文件存在则删除之,如果文件不存在,则创建之。
[java]  view plain  copy
  1. import java.io.File;  
  2. import java.io.IOException;  
  3.   
  4. public class CreateAndDeleteFile {  
  5.   
  6.     public static void main(String[] args) {  
  7.         File file = new File("E:" + File.separator + "tmp" + File.separator  
  8.                 + "test.txt");  
  9.         if (file.exists()) {  
  10.             if (file.delete()) {  
  11.                 System.out.println("文件删除成功!");  
  12.             }  
  13.         } else {  
  14.             try {  
  15.                 if (file.createNewFile()) {  
  16.                     System.out.println("文件创建成功!");  
  17.                 }  
  18.             } catch (IOException e) {  
  19.                 e.printStackTrace();  
  20.             }  
  21.         }  
  22.     }  
  23.   
  24. }  
从程序的运行结果可以发现,所有的操作并不会立即执行,因为java是通过JVM与底层进行交互的,所以所有的操作都必须经过JVM,所以有可能产生延迟。

创建一个文件夹:

public boolean mkdir()
[java]  view plain  copy
  1. File file = new File("E:"+File.separator+"tmp"+File.separator+"test.tt");  
  2.         file.mkdir();  

列出指定目录下的所有文件(包含目录):

public String[] list()——以字符串数组的形式返回
public File[] listFiles()——以File对象数组的形式返回
操作一:使用String数组
[java]  view plain  copy
  1. File file = new File("E:"+File.separator);  
  2.         String[] fileString = file.list();  
  3.         for (String string : fileString) {  
  4.             System.out.println(string);  
  5.         }  
操作二:使用File对象数组:
[java]  view plain  copy
  1. File file = new File("E:"+File.separator);  
  2.         File[] files = file.listFiles();  
  3.         for (File file2 : files) {  
  4.             System.out.println(file2);  
  5.         }  
操作二打印的是文件的绝对路径。因此使用操作二较好。

判断指定路径是否目录:

public boolean isDirectory()
[java]  view plain  copy
  1. File file = new File("E:"+File.separator);  
  2.         if (file.isDirectory()) {  
  3.             System.out.println(file.getPath()+"是目录!");  
  4.         }else {  
  5.             System.out.println(file.getAbsolutePath()+"不是目录!");  
  6.         }  

采用递归的形式列出指定目录下的所有文件(不含目录):

采用递归的形式:
[java]  view plain  copy
  1. import java.io.File;  
  2.   
  3. public class ListFile {  
  4.   
  5.     public static void main(String[] args) {  
  6.         File file = new File("E:" + File.separator);  
  7.         print(file);  
  8.     }  
  9.   
  10.     public static void print(File file) { // 该方法被递归调用  
  11.         if (file != null) { // 判断对象是否为空  
  12.             if (file.isDirectory()) {  
  13.                 File[] files = file.listFiles(); // 列出全部的文件  
  14.                 if (files != null) {    //因为有一些文件是操作系统不允许访问的  
  15.                     for (File file2 : files) {  
  16.                         print(file2);   //递归调用  
  17.                     }  
  18.                 }  
  19.             } else {  
  20.                 System.out.println(file);  
  21.             }  
  22.         }  
  23.     }  
  24. }  

RandomAccessFile类:

该类的功能是完成随机读取,可以读取指定位置的内容:

其构造方法如下:
public RandomAccessFile(String name,String mode)throws FileNotFoundException
public RandomAccessFile(File file,String mode)throws FileNotFoundException

mode 参数指定用以打开文件的访问模式。允许的值及其含意为:
"r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。  
"rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。  
"rws" 打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。  
"rwd"   打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。

写文件:
[java]  view plain  copy
  1. import java.io.File;  
  2. import java.io.IOException;  
  3. import java.io.RandomAccessFile;  
  4.   
  5. public class RandomAccessFileDemo01 {  
  6.   
  7.     public static void main(String[] args) throws IOException {  
  8.         File file = new File("E:" + File.separator + "tmp" + File.separator  
  9.                 + "test.txt");  
  10.   
  11.         RandomAccessFile rdf = new RandomAccessFile(file, "rw");// 读写模式,文件不存在则创建之  
  12.   
  13.         String name = null;  
  14.         int age = 0;  
  15.         name = "zhangsan"// 字符串长度为8字节  
  16.         age = 30// 整数的长度为4字节  
  17.   
  18.         rdf.writeBytes(name);  
  19.         rdf.writeInt(age);  
  20.   
  21.         name = "lisi    ";  
  22.         age = 31;  
  23.         rdf.writeBytes(name);  
  24.         rdf.writeInt(age);  
  25.   
  26.         name = "wangwu  ";  
  27.         age = 32;  
  28.         rdf.writeBytes(name);  
  29.         rdf.writeInt(age);  
  30.   
  31.         rdf.close();// 关闭  
  32.     }  
  33. }  
以上完成了文件的写入,每条数据的长度都是12位
读文件:
[java]  view plain  copy
  1. import java.io.File;  
  2. import java.io.IOException;  
  3. import java.io.RandomAccessFile;  
  4.   
  5. public class RandomAccessFileDemo02 {  
  6.   
  7.     public static void main(String[] args) throws IOException {  
  8.         File file = new File("E:" + File.separator + "tmp" + File.separator  
  9.                 + "test.txt");  
  10.   
  11.         RandomAccessFile rdf = new RandomAccessFile(file, "r"); // 只读模式  
  12.   
  13.         String name = null;  
  14.         int age = 0;  
  15.   
  16.         byte[] b = new byte[8]; // 开辟比特数组  
  17.         // 读取第二个人的信息  
  18.         rdf.skipBytes(12); // 跳过第一个人的信息  
  19.         for (int i = 0; i < b.length; i++) {  
  20.             b[i] = rdf.readByte(); // 读取一个字节  
  21.         }  
  22.         name = new String(b); // 将读取出来的比特数组变为字符串  
  23.         age = rdf.readInt();  
  24.         System.out.println("第二个人的信息-"+"姓名:" + name + "年龄:" + age);  
  25.           
  26.         //读取第一个人的信息  
  27.         rdf.seek(0);    //指针回到文件的开头  
  28.         for (int i = 0; i < b.length; i++) {  
  29.             b[i] = rdf.readByte();   
  30.         }  
  31.         name = new String(b);   
  32.         age = rdf.readInt();  
  33.         System.out.println("第一个人的信息-"+"姓名:" + name + "年龄:" + age);  
  34.           
  35.         //读取第三个人的信息  
  36.         rdf.skipBytes(12);  //此时文件指针位于第一个人的末尾  
  37.         for (int i = 0; i < b.length; i++) {  
  38.             b[i] = rdf.readByte(); // 读取一个字节  
  39.         }  
  40.         name = new String(b);   
  41.         age = rdf.readInt();  
  42.         System.out.println("第三个人的信息-"+"姓名:" + name + "年龄:" + age);  
  43.           
  44.         rdf.close(); // 关闭  
  45.     }  
  46.   
  47. }  
以上的代码操作起来比较复杂,可以通过javaio中的流来操作使其简便。

字节流和字符流

什么是流?在程序中所有的数据都是以流的方式进行传输或者保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就需要使用输出流来完成。
程序中的输入和输出都是以流的形式进行保存的,流中保存的实际上全部都是字节文件。

javaIO中的步骤:
1.使用File类打开一个文件;
2.使用字节流或者字符流的子类指定输出装置;
3.进行读写操作;
4.关闭输入、输出流。

InputStream、OutputStream、Writer和Reader都是抽象类。IO操作属于资源操作,操作完成之后必须关闭,否则容易出现未知错误。

字节流:

字节流操作的是比特类型的数据,以byte数组为准,主要操作类就是OutputStream(字节输出流)、InputStream(字节输出流)

OutputStream:
整个IO包中字节输出流的最大父类,该类是一个抽象类,如果想要使用此类,则必须通过子类实例化对象。
[java]  view plain  copy
  1. public class FileOutputStreamDemo01 {  
  2.   
  3.     public static void main(String[] args) throws IOException {  
  4.         //1.使用File类找到一个文件  
  5.         File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");  
  6.         //2.通过父类实例化对象  
  7.         FileOutputStream fos = new FileOutputStream(file);  
  8.         //3.进行写操作(byte数组)  
  9.         String info = "不悔梦归处,只恨太匆匆!";  
  10.         byte[] b = info.getBytes(); //将String转化为byte数组  
  11.         fos.write(b);  
  12.         //4.关闭输出流  
  13.         fos.close();  
  14.     }  
  15.   
  16. }  
运行结果:

也可以一个一个字节地向文件中写入数据:
[java]  view plain  copy
  1. String info = "不悔梦归处,只恨太匆匆!";  
  2.     byte[] b = info.getBytes(); //将String转化为byte数组  
  3.     for (int i = 0; i < b.length; i++) {  
  4.         fos.write(b[i]);    //每次只写入一个字节  
  5.     }  
如果文件不存在则创建之,如果文件存在则覆盖之。如果想要进行追加的话,需要使用FileOutPutStream的另一个构造方法:public FileOutputStream(File file, boolean append)
如果想要字符串换行将字符串改变成如下:
[java]  view plain  copy
  1. String info = "\r\n不悔梦归处,只恨太匆匆!";  
字节输入流(InputStream)
读取数据:
[java]  view plain  copy
  1. //1.使用File类找到一个文件  
  2.         File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");  
  3.         //2.通过父类实例化对象  
  4.         FileInputStream fis = new FileInputStream(file);  
  5.         //3.进行读操作  
  6.         byte[]b = new byte[1024];  
  7.         fis.read(b);  
  8.         //4.关闭输出流  
  9.         fis.close();  
  10.         System.out.println("内容是:"+new String(b));  
文件没有几行,但是输出的结果中却有大量的空格,问题就在于我们开辟了一个1024大小的区域。
修改以上程序为以下:
[java]  view plain  copy
  1. //1.使用File类找到一个文件  
  2.         File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");  
  3.         //2.通过父类实例化对象  
  4.         FileInputStream fis = new FileInputStream(file);  
  5.         //3.进行读操作  
  6.         byte[]b = new byte[1024];  
  7.         int len = fis.read(b);  
  8.         //4.关闭输出流  
  9.         fis.close();  
  10.         System.out.println("长度:"+len);  
  11.         System.out.println("内容是:"+new String(b,0,len));  
运行结果:


改正程序后一切正常了。但是以上的代码还存在问题:文件明明很小,但是却开辟了1024字节肯定很浪费,能不能根据文件的大小开辟数组空间呢?
如果想要知道文件的大小直接使用File即可。public long length()
[java]  view plain  copy
  1. //1.使用File类找到一个文件  
  2.         File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");  
  3.         //2.通过父类实例化对象  
  4.         FileInputStream fis = new FileInputStream(file);  
  5.         //3.进行读操作  
  6.         byte[]b = new byte[(int) file.length()];//数组的大小由文件大小决定  
  7.         int len = fis.read(b);  
  8.         //4.关闭输出流  
  9.         fis.close();  
  10.         System.out.println("长度:"+len);  
  11.         System.out.println("内容是:"+new String(b));  
[java]  view plain  copy
  1. //1.使用File类找到一个文件  
  2.         File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");  
  3.         //2.通过父类实例化对象  
  4.         FileInputStream fis = new FileInputStream(file);  
  5.         //3.进行读操作  
  6.         byte[]b = new byte[(int) file.length()];//数组的大小由文件大小决定  
  7.         for (int i = 0; i < b.length; i++) {  
  8.             b[i] = (byte) fis.read();  
  9.         }  
  10.         //4.关闭输出流  
  11.         fis.close();  
  12.         System.out.println("内容是:"+new String(b));  
以上的代码只适用于知道输入流大小的时候,如果我不知道大小呢?
[java]  view plain  copy
  1. //1.使用File类找到一个文件  
  2.     File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");  
  3.     //2.通过父类实例化对象  
  4.     FileInputStream fis = new FileInputStream(file);  
  5.     //3.进行读操作  
  6.     byte[]b = new byte[1024];  
  7.     int len = 0;  
  8.     int temp = 0;   //接收每一个读取进来的数据  
  9.     while ((temp = fis.read())!=-1) {  
  10.         b[len++] = (byte) temp;  
  11.     }  
  12.     //4.关闭输出流  
  13.     fis.close();  
  14.     System.out.println("内容是:"+new String(b,0,len));  


字符流:

字符流比字节流好在一点:就是可以直接输出字符串了,不用再将字符串转化为字节数组了。
字符输出流:
[java]  view plain  copy
  1. //1.使用File类找到一个文件  
  2.         File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");  
  3.         //2.通过父类实例化对象  
  4.         FileWriter fw = new FileWriter(file, true);//追加模式  
  5.           
  6.         String str = "\r\n不悔梦归处,只恨太匆匆!";  
  7.         //3.写入内容  
  8.         fw.write(str);  
  9.           
  10.         //4.关闭  
  11.         fw.close();  
字符输入流:
以字符数组的形式读取数据:

[java]  view plain  copy
  1. //1.使用File类找到一个文件  
  2.         File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");  
  3.         //2.通过父类实例化对象  
  4.         FileReader fr = new FileReader(file);  
  5.           
  6.         char[] c = new char[1024];  
  7.         //3.读取内容  
  8.         int len = fr.read(c);  
  9.           
  10.         System.out.println(new String(c,0,len));  
  11.         //4.关闭  
  12.         fr.close();  
通过文件是否读到底的形式进行读取:
[java]  view plain  copy
  1. //1.使用File类找到一个文件  
  2.         File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");  
  3.         //2.通过父类实例化对象  
  4.         FileReader fr = new FileReader(file);  
  5.           
  6.         char[] c = new char[1024];  
  7.         //3.读取内容  
  8.         int temp = 0;   //接收每个内容  
  9.         int len = 0;  
  10.         while ((temp = fr.read())!=-1) {  
  11.             c[len++] = (char) temp;  
  12.         }  
  13.         //4.关闭  
  14.         System.out.println("内容是:"+new String(c,0,len));  
  15.         fr.close();  
字符流与字节流的区别:

通过一个代码来验证字符流使用了缓存:
[java]  view plain  copy
  1. File file = new File("E:\\tmp\\hello.txt");  
  2.         FileOutputStream fos = new FileOutputStream(file);  
  3.         String str = "我前年买了个登山包!";  
  4.         byte[]b = str.getBytes();  
  5.         fos.write(b);   //写入数据  
  6. //      fos.close();  
以上的代码并没有关闭流,文件已经存在于磁盘上,并且已经有了内容。
[java]  view plain  copy
  1.     File file = new File("E:\\tmp\\hello.txt");  
  2.         FileWriter fr = new FileWriter(file);  
  3.         String str = "我前年买了个登山包!";  
  4.         fr.write(str);  //写入数据  
  5. //      fr.close();  
执行以上代码由于没有关闭流,所以硬盘上虽然生成了文件,但是文件的内容是空的。也就字符流中的数据保存在了内存的缓冲区中,流关闭的时候刷新缓冲区后内容才显示出来。基于这个原理:如果没有关闭流,可以使用flush()方法强制刷新缓冲区。

硬盘上的文件以字节的方式进行保存并且也是以字节的形式进行网络的传输,而字符只是在内存中才会形成的,所以字节流的使用很广泛。


实例讲解:文件拷贝

copy [源文件] [目标文件]
肯定要使用字节流,因为万一进行copy的东西是图片呢?
实现一:将源文件的内容全部读取,再一次性写入
实现二:边读边写【Better】(可以处理大文件)
[java]  view plain  copy
  1. import java.io.File;  
  2. import java.io.FileInputStream;  
  3. import java.io.FileOutputStream;  
  4. import java.io.IOException;  
  5.   
  6. public class Copy {  
  7.   
  8.     public static void main(String[] args) {  
  9.         if (args.length != 2) {  
  10.             System.out.println("输入参数不正确。");  
  11.             System.out.println("例:java Copy 源文件路径  目标文件路径");  
  12.             System.exit(1);  
  13.         } else {  
  14.             copyFile(args[0], args[1]);  
  15.         }  
  16.     }  
  17.   
  18.     private static void copyFile(String s, String d) {  
  19.         File sFile = new File(s);  
  20.         File dFile = new File(d);  
  21.   
  22.         if (!sFile.exists()) {  
  23.             System.out.println("源文件不存在!");  
  24.             System.exit(1);  
  25.         } else {  
  26.             try {  
  27.                 FileInputStream fis = new FileInputStream(sFile);  
  28.                 FileOutputStream fos = new FileOutputStream(dFile);  
  29.   
  30.                 if (fis != null && fos != null) {  
  31.                     int temp = 0;  
  32.                     while ((temp = fis.read()) != -1) {  
  33.                         fos.write(temp); // 边读边写  
  34.                     }  
  35.                     System.out.println("复制完成!");  
  36.                 }  
  37.   
  38.                 fis.close();  
  39.                 fos.close();  
  40.             } catch (IOException e) {  
  41.                 System.out.println("复制失败!");  
  42.                 e.printStackTrace();  
  43.             }  
  44.         }  
  45.     }  
  46.   
  47. }  

字节-字符装换流

在整个IO包中,实际上就是分为字节流和字符流,但是除了这两个流之外,还存在一组字节流-字符流的装换类。
OutputStreamWriter:Writer(字符输出流)的子类,将一个字符流的输出对象变为字节流的输出对象。
InputStreamReader:Reader(字符输入流)的子类,将一个字节流的输入对象变为字符流的输入对象。


其构造方法如下:public OutputStreamWriter(OutputStream out,String charsetName) throws UnsupportedEncodingException
public InputStreamReader(InputStream in,String charsetName)throws UnsupportedEncodingException

[java]  view plain  copy
  1. import java.io.File;  
  2. import java.io.FileOutputStream;  
  3. import java.io.IOException;  
  4. import java.io.OutputStreamWriter;  
  5. import java.io.Writer;  
  6.   
  7. public class OutputStreamWriterDemo {  
  8.   
  9.     public static void main(String[] args) throws IOException {  
  10.         File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");  
  11.         Writer out = new OutputStreamWriter(new FileOutputStream(file));    //将字节流变为字符流  
  12.         out.write("Hello World!");  //使用字符流输出  
  13.         out.close();  
  14.     }  
  15.   
  16. }  
以上是写入文件,读入文件的方式正好相反:
[java]  view plain  copy
  1. import java.io.File;  
  2. import java.io.FileInputStream;  
  3. import java.io.IOException;  
  4. import java.io.InputStreamReader;  
  5. import java.io.Reader;  
  6.   
  7. public class InputStreamReaderDemo {  
  8.   
  9.     public static void main(String[] args) throws IOException {  
  10.         File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");  
  11.         Reader reader = new InputStreamReader(new FileInputStream(file));  
  12.           
  13.         char[]c = new char[1024];  
  14.         int len = reader.read(c);  
  15.         reader.close();  
  16.           
  17.         System.out.println(new String(c, 0, len));  
  18.     }  
  19.   
  20. }  
以上只是以文件为例,因为OutputStreamWriter中接收的类型是OutputStream,只要是字节输出流都可以使用字符的形式输出,而InputStreamReader中接收的类型是InputStream,只要是字节输入流都可以使用字符输出流操作。

FileWriter和FileReader的说明:
FileOutputStream是OutputStream的子类,FileInputStream是InputStream的子类,但是在字符流文件的两个操作类却有一些特殊,FileWriter并不是Writer的子类,而是OutputStreamWriter的子类,而FileReader也不直接是Reader的子类而是InputStreamReader的子类。从这两个类的关系就可以清楚的发现:不管是使用字节流还是字符流实际上最终都是以字节的形式操作输入输出流的。


内存操作流:

之前所讲解的程序中,输出和输入都是从文件中来的,当然,也可以将输出的位置设置在内存上。此时就需要使用ByteArrayInputStream【将内容写入内存】和ByteArrayOutputStream【从内存中取出数据】将输入和输出的位置设置在内存上。

利用他们完成一个大小写装换的程序:

[java]  view plain  copy
  1. import java.io.ByteArrayInputStream;  
  2. import java.io.ByteArrayOutputStream;  
  3. import java.io.IOException;  
  4.   
  5. public class ByteArrayDemo {  
  6.   
  7.     public static void main(String[] args) {  
  8.         String str = "HELLO"// 定义一个字符串,全部由大写字母组成  
  9.         ByteArrayInputStream bis = new ByteArrayInputStream(str.getBytes());// 向内存中写入  
  10.         ByteArrayOutputStream bos = new ByteArrayOutputStream(); // 从内存中取出内容  
  11.   
  12.         int temp = 0;  
  13.         while ((temp = bis.read()) != -1) {  
  14.             char c = (char) temp; // 将读取的数字变为字符  
  15.             bos.write(Character.toLowerCase(c)); // 将字符变为小写  
  16.         }  
  17.   
  18.         // 所有的数据都在ByteArrayOutputStream中了  
  19.         String newString = bos.toString(); // 取出内容  
  20.         System.out.println(newString);  
  21.   
  22.         try {  
  23.             bis.close();  
  24.             bos.close();  
  25.         } catch (IOException e) {  
  26.             e.printStackTrace();  
  27.         }  
  28.   
  29.     }  
  30.   
  31. }  

管道流:

管道流的主要作用是可以进行两个线程之间的通信,分为管道输出流PipedOutputStream和管道输入流(PipedInputStream),如果要想进行管道的输出,则必须把输出流连在输入流上,在PipedInputStream中有如下的一个方法用于连接管道:
public void connect(PipedInputStream snk)throws IOException

如果要连接输入和输出,则必须使用以上方法。
[java]  view plain  copy
  1. import java.io.IOException;    
  2. import java.io.PipedInputStream;    
  3. import java.io.PipedOutputStream;    
  4.     
  5.     
  6. class Send implements Runnable {    
  7.     private PipedOutputStream pos = null// 管道输出流    
  8.     
  9.     public Send() {    
  10.         this.pos = new PipedOutputStream(); // 实例化输出流    
  11.     }    
  12.     
  13.     @Override    
  14.     public void run() {    
  15.         String str = "Hello World!";    
  16.     
  17.        try {    
  18.             this.pos.write(str.getBytes());    
  19.        catch (IOException e) {    
  20.             e.printStackTrace();    
  21.        try {    
  22.             pos.close();    
  23.        catch (IOException e) {    
  24.             e.printStackTrace();    
  25.             System.out.println("发送方发送信息:"+str);    
  26.     }    
  27.     
  28.     public PipedOutputStream getPipedOutputStream() {    
  29.         return this.pos;    
  30.     }    
  31. }    
  32.     
  33.     
  34. class Receive implements Runnable {    
  35.     PipedInputStream pis = null// 管道输入流    
  36.     
  37.     public Receive() {    
  38.         this.pis = new PipedInputStream(); // 实例化管道输入流    
  39.     }    
  40.     
  41.     @Override    
  42.     public void run() {    
  43.         byte[] b = new byte[1024];    
  44.         int len = 0;    
  45.         try {    
  46.             len = this.pis.read(b);// 读取内容    
  47.         catch (IOException e) {    
  48.             e.printStackTrace();    
  49.             pis.close();    
  50.         catch (IOException e) {    
  51.             System.out.println("接收方接收信息:" + new String(b, 0, len));    
  52.     }    
  53.     
  54.     public PipedInputStream getPipedInputStream() {    
  55.         return this.pis;    
  56.     }    
  57. }    
  58.     
  59.     
  60. public class PipeDemo {    
  61.     
  62.     public static void main(String[] args) {    
  63.         Send s = new Send();    
  64.         Receive r = new Receive();    
  65.         try {    
  66.             s.getPipedOutputStream().connect(r.getPipedInputStream()); // 连接管道    
  67.         catch (IOException e) {    
  68.             e.printStackTrace();    
  69.             
  70.         new Thread(s, "发送方").start();    
  71.         new Thread(r, "接收方").start();    
  72.     
  73.     }    
  74. }    
[java]  view plain  copy
  1. <span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">运行结果:</span>  
发送方发送信息:Hello World!
接收方接收信息:Hello World!

打印流:

在整个IO包中,打印流式输出信息最方便的类,主要包含字节打印流(PrintStream)和字符打印流(PrintWriter)。打印流提供了非常方便的打印功能,可以打印任何的数据类型,例如:小数、整数、字符串等等。打印流可以非常方便地进行输出。在PrintStream中重载了很多print()和println()方法。


在PrintStream中定义的构造方法中可以清楚的发现有一个构造方法可以直接接收OutputStream类的实例,这是因为与OutputStream相比,PrintStream可以更加方便进行输出数据,这就好比OutputStream类重新包装了一下,使输出更加方便。
[java]  view plain  copy
  1. import java.io.FileNotFoundException;  
  2. import java.io.FileOutputStream;  
  3. import java.io.PrintStream;  
  4.   
  5. public class PrintStreamDemo01 {  
  6.   
  7.     public static void main(String[] args) {  
  8.         try {  
  9.             PrintStream ps = new PrintStream(new FileOutputStream(  
  10.                     "E:\\tmp\\test.txt"));  
  11.             ps.print("Hello ");  
  12.             ps.println("World!");  
  13.             ps.print("1 + 1 = " + 2);  
  14.             ps.close();  
  15.         } catch (FileNotFoundException e) {  
  16.             e.printStackTrace();  
  17.         }  
  18.   
  19.     }  
  20.   
  21. }  
运行结果:
也就是说此时将FileOutputStream类包装了一下。实际上这样的思路叫做装饰者设计模式。
使用打印流明显方便很多。

格式化输出:

在JDK1.5之后,java又对PrintStream类进行了扩充,增加了格式化的输出方式,直接使用printf()方法就可以完成操作,但是在格式化输出的时候需要指定其输出的数据类型。
[java]  view plain  copy
  1. import java.io.FileNotFoundException;  
  2. import java.io.FileOutputStream;  
  3. import java.io.PrintStream;  
  4.   
  5. public class PrintStreamDemo02 {  
  6.   
  7.     public static void main(String[] args) {  
  8.         try {  
  9.             PrintStream ps = new PrintStream(new FileOutputStream(  
  10.                     "E:\\tmp\\test.txt"));  
  11.               
  12.             String name = "汤姆·杰克逊";  
  13.             int age = 30;  
  14.             float score = 96.3f;  
  15.             char sex = 'M';  
  16.             ps.printf("姓名:%s,年龄:%d,分数:%f,性别:%c", name,age,score,sex);  
  17.               
  18.             ps.close();  
  19.         } catch (FileNotFoundException e) {  
  20.             e.printStackTrace();  
  21.         }  
  22.   
  23.     }  
  24.   
  25. }  
运行结果:
由运行结果可以看出写入浮点数好像有一点问题,不过无关紧要(和系统有关)。

如果觉得%d、%c不好记忆的话也可以直接使用%s,因为任何数据类型碰到String都向String靠拢。

将以上的代码中的格式输出变成以下:
[java]  view plain  copy
  1. ps.printf("姓名:%s,年龄:%s,分数:%s,性别:%s", name,age,score,sex);  
运行结果:

一点要明白一点:打印流属于装饰者设计模式。


System类对IO的支持

System类中有三个常量对IO有支持:
[java]  view plain  copy
  1. public static final InputStream in; //标准输入  
  2. public static final PrintStream out;//标准输出  
  3. public static final PrintStream err;//标准错误输出  
[java]  view plain  copy
  1. <span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">观察以上的3个常量都使用了public static final关键字进行修饰,但是所有的单词的字母不是大写的,这属于Java的历史遗留问题。</span>  

System.out

[java]  view plain  copy
  1. import java.io.IOException;  
  2. import java.io.OutputStream;  
  3.   
  4. public class SystemDemo01 {  
  5.   
  6.     public static void main(String[] args) {  
  7.         OutputStream os = System.out;   //此时的输出流是向屏幕上输出  
  8.           
  9.         try {  
  10.             os.write("hello world".getBytes()); //向屏幕输出  
  11.         } catch (IOException e) {  
  12.             e.printStackTrace();  
  13.         }  
  14.         try {  
  15.             os.close();  
  16.         } catch (IOException e) {  
  17.             e.printStackTrace();  
  18.         }  
  19.     }  
  20.   
  21. }  

谁为我实例化,我就具备了谁的能力!根据对象的多态性,如果实现的子类不同,那么完成的功能也不同。

System.err

如果程序中出现了错误可以使用System.err进行输出。
[java]  view plain  copy
  1. public class SystemDemo02 {  
  2.   
  3.     public static void main(String[] args) {  
  4.         String str = "Hello World!";  
  5.     try {  
  6.         System.out.println(Integer.parseInt(str));//抛出的是运行时异常  
  7.     catch (NumberFormatException e) {  
  8.         System.err.println(e);  
  9. }  


[java]  view plain  copy
  1. <span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">运行结果,控制台输出:</span>  
java.lang.NumberFormatException: For input string: "Hello World!"

如果将上述代码的catch块中的System.err变成System.out则发现结果完全一样。以上的代码肯定可以使用System.out进行输出。

System.out和System.err的区别:

两者都是PrintStream的实例化对象,并且通过示例代码可以发现:两者都是输出错误信息,但是一般来讲System.out是将信息显示给用户看,是正常的信息显示;而System.err的信息正好相反不不希望用户看到的,会直接在后天打印,是专门显示错误的。
一般来讲,如果要输出错误信息最好使用System.err,这一点只能从概念上进行划分。

System.in:

实际上是一个键盘的输入流,其本身是InputStream类类型的对象,那么此时就可以使用此方式完成从键盘读取数据的功能。
[java]  view plain  copy
  1. import java.io.IOException;  
  2. import java.io.InputStream;  
  3.   
  4. public class SystemDemo03 {  
  5.   
  6.     public static void main(String[] args) {  
  7.         InputStream input = System.in;  //从键盘读取数据  
  8.         byte[]b = new byte[1024];   //开辟byte数组空间,读取数据  
  9.         System.out.print("请输入内容:");  
  10.         try {  
  11.             int len = input.read(b);//接收数据  
  12.             System.out.println("你的输入:"+new String(b,0,len));  
  13.             input.close();  
  14.         } catch (IOException e) {  
  15.             e.printStackTrace();  
  16.         }     
  17.     }  
  18.   
  19. }  
运行结果:
请输入内容:niasn3543#$%^
你的输入:niasn3543#$%^
所有的数据都已经输入进去了,但是以上的代码是否存在问题?
问题1:指定了输入数据的长度,如果输入的数据的长度超过了指定的长度,则只能输入部分数据
将以上 的比特数组的空间改为3byte,运行结果:
请输入内容:123456
你的输入:123
字符串被截短了。
问题2:如果开辟的比特数组为奇数的话可能出现中文乱码问题:
请输入内容:简体中文
你的输入:简?
因为一个中文使用2个字节来保存,一个汉字被劈成两半。

如果不指定比特数组的长度,则只能通过设置标志位的方式来完成。

判断标志位的方式:

[java]  view plain  copy
  1. import java.io.IOException;  
  2. import java.io.InputStream;  
  3.   
  4. public class SystemDemo04 {  
  5.   
  6.     public static void main(String[] args) {  
  7.         InputStream input = System.in;  //从键盘读取数据  
  8.         System.out.println("输入内容:");  
  9.         StringBuffer sb = new StringBuffer();  
  10.         int temp = 0;   //接收数据  
  11.         try {  
  12.             while ((temp = input.read())!= -1) {  
  13.                 char c = (char) temp;  
  14.                 if ('\n' == c) {    //输入回车跳出循环  
  15.                     break;  
  16.                 }  
  17.                 sb.append(c);     
  18.             }  
  19.             System.out.println("你的输入:\n"+sb);  
  20.             input.close();  
  21.         } catch (IOException e) {  
  22.             e.printStackTrace();  
  23.         }  
  24.     }  
  25.   
  26. }  
运行结果:


这时输入的长度没有限制了,但是输入中文一样会乱码(每个汉字是拆分成两次读入的)。

最好的解决方案是将全部的输入数据放到一块内存之中,之后一次性从内存中读取数据,这样所有的数据都只读了一次,不会有乱码,也不会有长度的限制。


如果要实现以上功能就需要使用 BufferedReader

输入、输出重定向

为System.out重定向-setOut()方法:
[java]  view plain  copy
  1. import java.io.FileNotFoundException;  
  2. import java.io.FileOutputStream;  
  3. import java.io.PrintStream;  
  4.   
  5.   
  6. public class SystemDemo05 {  
  7.   
  8.     public static void main(String[] args) throws FileNotFoundException {  
  9.         System.setOut(new PrintStream(new FileOutputStream("E:\\tmp\\red.txt")) );  //System.out输出重定向  
  10.         System.out.println("你好"); //控制台看不到了,在文件中  
  11.         System.out.println(1223);  
  12.     }  
  13.   
  14. }  
输出重定向,所有的内容向文件中打印。

利用以上思想可以完成错误的重定向,System.out是用户希望看到的信息,一旦有错误最好写入文件。

[java]  view plain  copy
  1. import java.io.FileNotFoundException;  
  2. import java.io.FileOutputStream;  
  3. import java.io.PrintStream;  
  4.   
  5. public class SystemDemo06 {  
  6.   
  7.     public static void main(String[] args) throws FileNotFoundException {  
  8.         String str = "Hello World!";  
  9.         try {  
  10.             System.out.println(Integer.parseInt(str));  
  11.         } catch (NumberFormatException e) {  
  12.             System.setOut(new PrintStream(new FileOutputStream(  
  13.                     "E:\\tmp\\err.log")));  
  14.             System.out.println(e);  
  15.         } catch (Exception e) {  
  16.         }  
  17.     }  
  18.   
  19. }  
错误信息不会在控制台打印,而是重定向到了错误日志文件中。


System.err重定向:
利用System.err向控制台输出信息,为了方便起见使用内存操作流。
[java]  view plain  copy
  1. import java.io.ByteArrayOutputStream;  
  2. import java.io.FileNotFoundException;  
  3. import java.io.PrintStream;  
  4.   
  5. public class SystemDemo07 {  
  6.   
  7.     public static void main(String[] args) throws FileNotFoundException {  
  8.         ByteArrayOutputStream bos = new ByteArrayOutputStream();  
  9.         System.setErr(new PrintStream(bos));    //输出重定向  
  10.         System.err.print("错误的的");   //向内存中输出  
  11.         System.err.println("Hello!");  
  12.     }  
  13.   
  14. }  
System.err的信息写入了内存。

为System.in重定向

例如:从文件中读取。例如有以下文件:

用System.in从文件中读取:
[java]  view plain  copy
  1. import java.io.FileInputStream;  
  2. import java.io.IOException;  
  3. import java.io.InputStream;  
  4.   
  5. public class SystemDemo08 {  
  6.   
  7.     public static void main(String[] args) throws IOException {  
  8.         System.setIn(new FileInputStream("E:\\tmp\\demo.txt")); //输入重定向  
  9.         InputStream input = System.in;  //从文件中接收数据  
  10.         byte[]b = new byte[1024];  
  11.         int len = input.read(b);    //接收  
  12.         System.out.println("输入的内容是:"+new String(b, 0, len));  
  13.         input.close();  
  14.     }  
  15.   
  16. }  
运行结果:
对于输入输出重定向基本上都是在System.out上使用,用来把信息显示给用户。

BufferedReader类:

如果想要接收任意长度的数据并且避免乱码,就可以使用BufferedReader(该类是Reader的子类)。
构造方法如下:public BufferedReader(Reader in)
常用方法:public String readLine() throws IOException

此时,可以给出从键盘输入数据的标准格式:
[java]  view plain  copy
  1. import java.io.BufferedReader;  
  2. import java.io.IOException;  
  3. import java.io.InputStreamReader;  
  4.   
  5. public class BufferedReaderDemo {  
  6.   
  7.     public static void main(String[] args) {  
  8.         BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // 将字节流变为字符流  
  9.         String str = null// 接受内容  
  10.         System.out.println("请输入内容:");  
  11.         try {  
  12.             str = br.readLine();// 读取一行  
  13.         } catch (IOException e) {  
  14.             e.printStackTrace();  
  15.         }  
  16.         System.out.println("输入的内容:\n" + str);  
  17.     }  
  18.   
  19. }  
运行结果:
请输入内容:
ndisnfisdn中国人5456dfds
输入的内容:
ndisnfisdn中国人5456dfds

以上程序完整地输出了内容,并且没有任何乱码产生。

IO操作实例:

实例一:加法操作
从键盘读入两个数字,之后完成整数的加法操作。因为从键盘接收过来的内容全部是采用字符串的形式存放的,所以此时直接将字符串通过包装类Integer将字符串变为基本数据类型:
[java]  view plain  copy
  1. import java.io.BufferedReader;  
  2. import java.io.IOException;  
  3. import java.io.InputStreamReader;  
  4.   
  5. public class Exec {  
  6.   
  7.     public static void main(String[] args) throws IOException {  
  8.         int i = 0, j = 0;  
  9.         BufferedReader bufferedReader = new BufferedReader(  
  10.                 new InputStreamReader(System.in));  
  11.   
  12.         String str = null// 接收数据  
  13.   
  14.         System.out.print("请输入第一个数据:");  
  15.         str = bufferedReader.readLine();  
  16.         i = Integer.parseInt(str);  
  17.         System.out.print("请输入第二个数据:");  
  18.         j = Integer.parseInt(bufferedReader.readLine());  
  19.         System.out.println(i + " + " + j + " = " + (i + j));  
  20.     }  
  21.   
  22. }  
以上确实完成了基本功能,但是有以下问题:
1.  假如用户输入的数据不是数字?
2.  用户如果输入小数怎么办?
3.  代码重复(一使用数据肯定要使用BufferedReader)

需要对类进行合理的划分:
专门设计一个类用于处理输入,并且在该类中完成输入数据的检查。
下面完成一个处理输入的类,但是只能得到整数和字符串:
[java]  view plain  copy
  1. import java.io.BufferedReader;  
  2. import java.io.IOException;  
  3. import java.io.InputStreamReader;  
  4.   
  5. public class InputData {  
  6.     private BufferedReader buf = null;  
  7.   
  8.     public InputData() {  
  9.         super();  
  10.         this.buf = new BufferedReader(new InputStreamReader(System.in));  
  11.     }  
  12.   
  13.     public String getString(String info) {  
  14.         String temp = null;  
  15.         System.out.println(info); // 打印提示信息  
  16.         try {  
  17.             temp = buf.readLine();  
  18.         } catch (IOException e) {  
  19.             e.printStackTrace();  
  20.         }  
  21.         return temp;  
  22.     }  
  23.   
  24.     public int getInt(String info, String err) {  
  25.         int temp = 0;  
  26.         String str = null;  
  27.         boolean flag = true;  
  28.           
  29.         while (flag) {  
  30.             str = this.getString(info);  
  31.             if (str.matches("^\\d+$")) { // 判断是否由数字组成  
  32.                 temp = Integer.parseInt(str);  
  33.                 flag = false// 结束循环  
  34.             } else {  
  35.                 System.out.println(err); // 打印错误信息  
  36.             }  
  37.         }  
  38.   
  39.         return temp;  
  40.     }  
  41.   
  42. }  
[java]  view plain  copy
  1. import java.io.IOException;  
  2.   
  3. public class Exec {  
  4.   
  5.     public static void main(String[] args) throws IOException {  
  6.         int i = 0, j = 0;  
  7.         InputData inputData = new InputData();  
  8.         i = inputData.getInt("请输入第一个数字""输入的不是数字,请重新输入!");  
  9.         j = inputData.getInt("请输入第二个数字""输入的不是数字,请重新输入!");  
  10.   
  11.         System.out.println(i + " + " + j + " = " + (i + j));  
  12.     }  
  13.   
  14. }  
对InputData进行进一步扩充(整数、小数、字符串、日期),得到以下的代码:
[java]  view plain  copy
  1. import java.io.BufferedReader;  
  2. import java.io.IOException;  
  3. import java.io.InputStreamReader;  
  4. import java.text.ParseException;  
  5. import java.text.SimpleDateFormat;  
  6. import java.util.Date;  
  7.   
  8. public class InputData {  
  9.     private BufferedReader buf = null;  
  10.   
  11.     public InputData() {  
  12.         super();  
  13.         this.buf = new BufferedReader(new InputStreamReader(System.in));  
  14.     }  
  15.   
  16.     public String getString(String info) {  
  17.         String temp = null;  
  18.         System.out.println(info); // 打印提示信息  
  19.         try {  
  20.             temp = buf.readLine();  
  21.         } catch (IOException e) {  
  22.             e.printStackTrace();  
  23.         }  
  24.         return temp;  
  25.     }  
  26.   
  27.     public int getInt(String info, String err) {  
  28.         int temp = 0;  
  29.         String str = null;  
  30.         boolean flag = true;  
  31.           
  32.         while (flag) {  
  33.             str = this.getString(info);  
  34.             if (str.matches("^\\d+$")) { // 判断是否由数字组成  
  35.                 temp = Integer.parseInt(str);  
  36.                 flag = false// 结束循环  
  37.             } else {  
  38.                 System.out.println(err); // 打印错误信息  
  39.             }  
  40.         }  
  41.   
  42.         return temp;  
  43.     }  
  44.       
  45.     public float getFloat(String info, String err) {  
  46.         float temp = 0;  
  47.         String str = null;  
  48.         boolean flag = true;  
  49.           
  50.         while (flag) {  
  51.             str = this.getString(info);  
  52.             if (str.matches("^\\d+.?\\d+$")) { // 判断小数  
  53.                 temp = Float.parseFloat(str);  
  54.                 flag = false// 结束循环  
  55.             } else {  
  56.                 System.out.println(err); // 打印错误信息  
  57.             }  
  58.         }  
  59.   
  60.         return temp;  
  61.     }  
  62.   
  63.     public Date getDate(String info, String err) throws ParseException{  
  64.         Date temp = null;  
  65.         String str = null;  
  66.         boolean flag = true;  
  67.         while (flag) {  
  68.             str = getString(info);  
  69.             if (str.matches("^\\d{4}-\\d{2}-\\d{2}$")) { //^和$分别表示正则表达式的开头和结尾  
  70.                 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");  
  71.                 temp = sdf.parse(str);  
  72.                 flag = false;  
  73.             }else {  
  74.                 System.out.println(getString(err));  
  75.             }  
  76.         }  
  77.         return temp;  
  78.     }  
  79. }  




实例二:菜单显示
使用IO操作完成一个简单的菜单应用程序,如果用户输入的编号不正确,则给出错误提示,并等待用户重新选择:

将菜单的显示与菜单的功能进行分离(MVC设计模式)
菜单操作类:
[java]  view plain  copy
  1. public class Operation {  
  2.     public static void add(){  
  3.         System.out.println("你选择了【增加】");  
  4.     }  
  5.     public static void delete(){  
  6.         System.out.println("你选择了【删除】");  
  7.     }  
  8.     public static void update(){  
  9.         System.out.println("你选择了【更新】");  
  10.     }  
  11.     public static void find(){  
  12.         System.out.println("你选择了【查找】");  
  13.     }  
  14.     public static void exit(){  
  15.         System.out.println("你选择了【退出】\n系统退出。。。");  
  16.         System.exit(0);  
  17.     }  
  18. }  
菜单显示类:
[java]  view plain  copy
  1. public class Menu {  
  2.     public Menu() {  
  3.         while (true) {  
  4.             this.show();  
  5.         }  
  6.     }  
  7.   
  8.     public void show() {  
  9.         System.out.println("======= XXX系统 ========");  
  10.         System.out.println("     [1].增加数据");  
  11.         System.out.println("     [2].删除数据");  
  12.         System.out.println("     [3].更新数据");  
  13.         System.out.println("     [4].查找数据");  
  14.         System.out.println("     [0].退出系统");  
  15.         InputData inputData = new InputData();  
  16.         int i = inputData.getInt("请选择:""请输入正确的选项!");  
  17.         switch (i) {  
  18.         case 1:  
  19.             Operation.add();  
  20.             break;  
  21.         case 2:  
  22.             Operation.delete();  
  23.             break;  
  24.         case 3:  
  25.             Operation.update();  
  26.             break;  
  27.         case 4:  
  28.             Operation.find();  
  29.             break;  
  30.         case 0:  
  31.             Operation.exit();  
  32.             break;  
  33.         default:  
  34.             System.out.println("请选择正确的操作!");  
  35.             break;  
  36.         }  
  37.     }  
  38. }  
测试类:
[java]  view plain  copy
  1. public class ExecTest {  
  2.   
  3.     public static void main(String[] args) {  
  4.         new Menu();  
  5.   
  6.     }  
  7.   
  8. }  

Scanner类:

在JDK1.5之后提供了专门的输入数据类,此类可以完成BufferReader类的功能。也可以方便的对输入数据进行验证,此类放在java.util中。


使用Scanner接收键盘的输入:

[java]  view plain  copy
  1. import java.util.Scanner;  
  2.   
  3. public class ScannerDemo01 {  
  4.   
  5.     public static void main(String[] args) {  
  6.         Scanner scanner = new Scanner(System.in); // Scanner接收一个InputStream  
  7.         System.out.println("输入数据:");  
  8.         String str = scanner.next(); // 接收数据  
  9.         System.out.println("输入的数据是:" + str);  
  10.     }  
  11.   
  12. }  
比使用BufferedReader更加容易,但是以上的程序存在问题:
如果输入hello world,则运行结果:

它只能读取空格之前的内容,如果想要输入空格就必须修改分隔符,将分隔符变成“\n”【回车表示】
public Scanner useDelimiter(String pattern)
	修改分隔符:
     
[java] view plain copy
  1. import java.util.Scanner;  
  2.   
  3. public class ScannerDemo02 {  
  4.   
  5.     public static void main(String[] args) {  
  6.         Scanner scanner = new Scanner(System.in); // Scanner接收一个InputStream  
  7.         scanner.useDelimiter("\n"); //设置分隔符为回车  
  8.         System.out.println("输入数据:");  
  9.         String str = scanner.next(); // 接收数据  
  10.         System.out.println("输入的数据是:" + str);  
  11.     }  
  12.   
  13. }  
[java] view plain copy
  1. <span style="white-space:pre">    </span>运行以上程序没有任何问题,使用Scanner在进行操作的时候最好指定分隔符。  

使用Scanner类可以进行方便地进行数字的输入:

XXX nextXXX()方法,该方法需要与boolean hasNextXXX()方法进行配合使用,例如:
[java] view plain copy
  1. import java.util.Scanner;  
  2.   
  3. public class ScannerDemo03 {  
  4.   
  5.     public static void main(String[] args) {  
  6.         Scanner scanner = new Scanner(System.in); // Scanner接收一个InputStream  
  7.           
  8.         System.out.println("输入数据:");  
  9.         if (scanner.hasNextInt()) {  
  10.             int i = scanner.nextInt();  
  11.             System.out.println("整数:"+i);  
  12.         } else {  
  13.             System.out.println("不是整数!");  
  14.         }  
  15.           
  16.     }  
  17.   
  18. }  
Scanner不能直接接受日期型的数据,如果要想接收Date类型的数据,只能通过字符串转型,但是也是可以使用Scanner中的方法进行验证的:
验证:public boolean hasNext(Pattern pattern)
接收:public String next(Pattern pattern)
[java]  view plain  copy
  1. import java.text.ParseException;  
  2. import java.text.SimpleDateFormat;  
  3. import java.util.Date;  
  4. import java.util.Scanner;  
  5.   
  6. public class ScannerDemo04 {  
  7.   
  8.     public static void main(String[] args) {  
  9.         Scanner scanner = new Scanner(System.in); // Scanner接收一个InputStream  
  10.         String str = null;  
  11.         Date date = null;  
  12.         System.out.println("输入日期(yyyy-MM-dd):");  
  13.         if (scanner.hasNext("^\\d{4}-\\d{2}-\\d{2}$")) { // 判断  
  14.             str = scanner.next("^\\d{4}-\\d{2}-\\d{2}$"); // 接收  
  15.             try {  
  16.                 date = new SimpleDateFormat("yyyy-MM-dd").parse(str);  
  17.             } catch (ParseException e) {  
  18.                 e.printStackTrace();  
  19.             }  
  20.         } else {  
  21.             System.out.println("输入的日期格式错误!");  
  22.         }  
  23.         System.out.println(date);  
  24.     }  
  25.   
  26. }  

Scanner还可以直接从文件中读取数据:

[java]  view plain  copy
  1. import java.io.File;  
  2. import java.io.FileNotFoundException;  
  3. import java.util.Scanner;  
  4.   
  5. public class ScannerDemo05 {  
  6.   
  7.     public static void main(String[] args) {  
  8.         StringBuffer str = new StringBuffer();  
  9.         try {  
  10.             Scanner scanner = new Scanner(new File("E:/tmp/tmp.txt"));  
  11.               
  12.             while (scanner.hasNext()) {  
  13.                 str.append(scanner.next()).append("\n");  
  14.             }  
  15.             System.out.println("文件中的内容是:" + str);  
  16.         } catch (FileNotFoundException e) {  
  17.             e.printStackTrace();  
  18.         }  
  19.   
  20.     }  
  21. }  
在使用Scanner类来读取文件的时候要考虑到换行的功能。

数据操作流(DataInputStream和DataOutputStream)

在整个IO包中提供了两个与平台无关的数据操作流:
DataOutputStream:数据输出流
DataInputStream:数据输入流
通常数据输出流会按照一定的格式将数据输出,再通过数据输入流按照一定的格式将数据读入。
例如有以下订单数据:


DataOutputStream类的定义如下:
public class DataOutputStream extends FilterOutputStream implements DataOutput,其中FilterOutputStream 是OutputStream的子类,DataOutput接口中有一系列的写操作(writeXXX),可以写入各种数据类型。此接口的定义格式应该大致了解,以便于后面的学习。
要想使用DataOutputStream进行数据的输出操作的话,必须指明数据的输出格式。
[java]  view plain  copy
  1. import java.io.DataOutputStream;  
  2. import java.io.FileOutputStream;  
  3. import java.io.IOException;  
  4.   
  5. public class DataOutputStreamDemo {  
  6.   
  7.     public static void main(String[] args) throws IOException {  
  8.         // 声明并实例化DataOutputStream对象  
  9.         DataOutputStream dos = new DataOutputStream(new FileOutputStream(  
  10.                 "E:/tmp/data.txt"));  
  11.   
  12.         String[] names = { "衬衣""手套""围巾" }; // 商品名称数组  
  13.         float[] price = { 98.3f, 30.3f, 50.5f }; // 商品价格数组  
  14.         int[] nums = { 321 }; // 数量  
  15.         for (int i = 0; i < names.length; i++) {  
  16.             dos.writeChars(names[i]); // 写入名称  
  17.             dos.writeChar('\t'); // 写入分隔符  
  18.             dos.writeFloat(price[i]); // 写入价格  
  19.             dos.writeChar('\t');  
  20.             dos.writeInt(nums[i]);// 写入数量  
  21.             dos.writeChar('\n');  
  22.         }  
  23.         dos.close();// 关闭输出流  
  24.     }  
  25.   
  26. }  
执行以上程序将会生成文件E:\tmp\data.txt,但是用记事本打开会出现乱码,因为使用DataOutputStream写入的数据需要使用DataInputStream才能够进行读取!
[java]  view plain  copy
  1. import java.io.DataInputStream;  
  2. import java.io.FileInputStream;  
  3. import java.io.IOException;  
  4.   
  5. public class DataInputStreamDemo {  
  6.   
  7.     public static void main(String[] args) throws IOException{  
  8.         DataInputStream dis = new DataInputStream(new FileInputStream(  
  9.                 "E:/tmp/data.txt"));  
  10.   
  11.         String name = null// 接收名称  
  12.         float price = 0.0f; // 接收价格  
  13.         int nums = 0// 接收数量  
  14.   
  15.         char[] temp = null// 接收商品名称的数组  
  16.         int len = 0// 保存读取数据的个数  
  17.         char c = 0// \u0000  
  18.         while (true) {  
  19.             temp = new char[200]; // 开辟空间  
  20.             len = 0;  
  21.   
  22.             while ((c = dis.readChar()) != '\t') {  
  23.                 temp[len] = c;  
  24.                 len++;  
  25.             }  
  26.             name = new String(temp, 0, len);  
  27.             price = dis.readFloat();  
  28.             dis.readChar(); // 读取\t  
  29.             nums = dis.readInt();  
  30.             dis.readChar(); // 读取\n  
  31.   
  32.             System.out.printf("名称:%s 价格:%.2f 数量:%d\n", name, price, nums);  
  33.         }  
  34.     }  
  35.   
  36. }  
这个和随机访问很相似,DataInput接口和DataOutput接口,这两个接口彼此对应。

合并流(SequenceInputStream)

合并流从概念上讲:就是将内容合并到一起了。例如在:E:\tmp目录下存在两个文件a.txt和b.txt

[java]  view plain  copy
  1. import java.io.FileInputStream;  
  2. import java.io.FileNotFoundException;  
  3. import java.io.FileOutputStream;  
  4. import java.io.IOException;  
  5. import java.io.InputStream;  
  6. import java.io.OutputStream;  
  7. import java.io.SequenceInputStream;  
  8.   
  9. public class SequenceInputStreamDemo {  
  10.   
  11.     public static void main(String[] args) {  
  12.         try {  
  13.             InputStream is1 = new FileInputStream("E:/tmp/a.txt");// 输入流1  
  14.             InputStream is2 = new FileInputStream("E:/tmp/b.txt"); // 输入流2  
  15.             OutputStream os = new FileOutputStream("E:/tmp/ab.txt");// 输出流  
  16.             SequenceInputStream sis = new SequenceInputStream(is1, is2);// 实例化合并流  
  17.   
  18.             int temp = 0// 接收内容  
  19.             while ((temp = sis.read()) != -1) {  
  20.                 os.write(temp);  
  21.             }  
  22.   
  23.             sis.close();  
  24.             is1.close();  
  25.             is2.close();  
  26.             os.close();  
  27.         } catch (FileNotFoundException e) {  
  28.             e.printStackTrace();  
  29.         } catch (IOException e) {  
  30.             e.printStackTrace();  
  31.         }  
  32.   
  33.     }  
  34.   
  35. }  
运行结果:

两个文件的内容被合并了!

压缩流(ZipOutputStream、ZipFile、ZipInputStream)


压缩流的实现:

1.zip是一种较为常见的压缩形式,在Java中想要实现zip的压缩需要导入java.util.zip包,可以使用此包中的ZipFile、ZipOutputStream、ZipInputStream、ZipEntry几个类完成操作:
2.jar及gzip文件格式的压缩输入、输出流:
jar压缩的支持类保存在java.util.jar包中,常用的类有以下几个:
jar压缩输出流:JarOutputStream
jar压缩输入流:JarInputStream
jar文件:JarFile
jar实体:JarEntry
gzip是用于Nnix系统的文件压缩,在Linux中经常会使用到*.gz的文件,就是gzip格式,gzip压缩的支持类保存在java.util.zip包中,常用的有以下几个类:
gzip压缩输出流:gzipOutputStream
gzip压缩输入流:gzipInputStream

在实例化ZipEntry的时候,要设置名称,此名称实际上就是压缩文件中每一个元素的名称。

ZipOutputStream:

[html]  view plain  copy
  1. java.lang.Object  
  2.  java.io.OutputStream  
  3.       java.io.FilterOutputStream  
  4.        java.util.zip.DeflaterOutputStream  
  5.             java.util.zip.ZipOutputStream  
从继承关系可以看出它是一个字节输出流
[java]  view plain  copy
  1. public void putNextEntry(ZipEntry e)  
  2.                   throws IOException  
在压缩文件中,每一个压缩的实体都用一个ZipEntry表示,所以在进行压缩之前必须通过putNextEntry设置一个zipEntry即可。
[java]  view plain  copy
  1. public void setComment(String comment)  
以上方法用来设置注释
[java]  view plain  copy
  1. import java.io.File;  
  2. import java.io.FileInputStream;  
  3. import java.io.FileOutputStream;  
  4. import java.io.IOException;  
  5. import java.io.InputStream;  
  6. import java.util.zip.ZipEntry;  
  7. import java.util.zip.ZipOutputStream;  
  8.   
  9. public class ZipOutputStreamDemo01 {  
  10.   
  11.     public static void main(String[] args) throws IOException {  
  12.         File file = new File("E:/tmp/info.txt"); // 需要压缩的文件  
  13.         File zipFile = new File("E:/tmp/info.zip"); // 压缩文件  
  14.   
  15.         InputStream is = new FileInputStream(file);  
  16.         ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile));  
  17.         zos.putNextEntry(new ZipEntry(file.getName())); // 设置zipEntry对象  
  18.         zos.setComment("Windows SystemFile");  
  19.   
  20.         int temp = 0;  
  21.         while ((temp = is.read()) != -1) {  
  22.             zos.write(temp); // 压缩输出  
  23.         }  
  24.         is.close();  
  25.         zos.close();  
  26.     }  
  27.   
  28. }  
运行结果:
以上的代码仅仅是完成文件的压缩,如果要压缩一个文件夹怎么办?
[java]  view plain  copy
  1. import java.io.File;  
  2. import java.io.FileInputStream;  
  3. import java.io.FileOutputStream;  
  4. import java.io.IOException;  
  5. import java.io.InputStream;  
  6. import java.util.zip.ZipEntry;  
  7. import java.util.zip.ZipOutputStream;  
  8.   
  9. public class ZipOutputStreamDemo02 {  
  10.   
  11.     public static void main(String[] args) throws IOException {  
  12.         File file = new File("E:/tmp/test"); // 需要压缩的目录  
  13.         File zipFile = new File("E:/tmp/testdir.zip"); // 压缩文件  
  14.   
  15.         InputStream is = null;  
  16.         ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile));  
  17.   
  18.         if (file.isDirectory()) {  
  19.             File[] listFiles = file.listFiles(); // 列出全部文件  
  20.             for (int i = 0; i < listFiles.length; i++) {  
  21.                 is = new FileInputStream(listFiles[i]);  
  22.                 zos.putNextEntry(new ZipEntry(file.getName() + File.separator  
  23.                         + listFiles[i].getName())); // 设置zipEntry对象  
  24.                 zos.setComment("E:/tmp/test Files");  
  25.                 int temp = 0;  
  26.                 while ((temp = is.read()) != -1) {  
  27.                     zos.write(temp); // 压缩输出  
  28.                 }  
  29.   
  30.             }  
  31.   
  32.         }  
  33.   
  34.         is.close();  
  35.         zos.close();  
  36.     }  
  37.   
  38. }  
运行结果:

ZipFile类:

··

[java]  view plain  copy
  1. import java.io.File;  
  2. import java.io.FileOutputStream;  
  3. import java.io.IOException;  
  4. import java.io.InputStream;  
  5. import java.io.OutputStream;  
  6. import java.util.zip.ZipEntry;  
  7. import java.util.zip.ZipException;  
  8. import java.util.zip.ZipFile;  
  9.   
  10. public class ZipFileDemo01 {  
  11.   
  12.     public static void main(String[] args) throws ZipException, IOException {  
  13.         File file = new File("E:/tmp/info.zip"); // 压缩文件  
  14.         File outFile = new File("E:/tmp/info_unzip.txt");  
  15.         ZipFile zipFile = new ZipFile(file); // 实例化ZipFile对象  
  16.         ZipEntry entry = zipFile.getEntry("info.txt");// 得到压缩实体  
  17.         OutputStream os = new FileOutputStream(outFile);  
  18.         InputStream is = zipFile.getInputStream(entry);  
  19.         int temp = 0;  
  20.         while ((temp = is.read()) != -1) {  
  21.             os.write(temp);  
  22.         }  
  23.         is.close();  
  24.         os.close();  
  25.     }  
  26.   
  27. }  
以上程序对一个压缩文件进行了解压缩。以上程序中有一个问题:必须知道每一个压缩实体的名称才可以进行解压缩操作,如果是一个文件夹呢?

ZipInputStream:

该类的定义如下:
[html]  view plain  copy
  1. java.lang.Object  
  2.   java.io.InputStream  
  3.       java.io.FilterInputStream  
  4.            java.util.zip.InflaterInputStream  
  5.               java.util.zip.ZipInputStream  

它是InputStream的子类。
[java]  view plain  copy
  1. import java.io.File;  
  2. import java.io.FileInputStream;  
  3. import java.io.FileOutputStream;  
  4. import java.io.IOException;  
  5. import java.io.InputStream;  
  6. import java.io.OutputStream;  
  7. import java.util.zip.ZipEntry;  
  8. import java.util.zip.ZipFile;  
  9. import java.util.zip.ZipInputStream;  
  10.   
  11. public class ZipInputStreamDemo01 {  
  12.   
  13.     public static void main(String[] args) throws IOException {  
  14.         File file = new File("E:/tmp/testdir.zip");  
  15.         File outFile = null// 输出文件的时候需要有文件夹的操作  
  16.   
  17.         ZipFile zipFile = new ZipFile(file); // 实例化zipFile对象  
  18.         ZipEntry zipEntry = null// 每一个压缩实体  
  19.         ZipInputStream zis = new ZipInputStream(new FileInputStream(file));  
  20.   
  21.         OutputStream os = null;// 定义输出流,用于输出每一个实体  
  22.         InputStream is = null// 定义输入流,读取每一个ZipEntry  
  23.   
  24.         while ((zipEntry = zis.getNextEntry()) != null) {  
  25.             System.out.println("解压缩:" + zipEntry.getName() + "文件!");  
  26.             outFile = new File("E:/tmp/" + zipEntry.getName());  
  27.   
  28.             if (!outFile.getParentFile().exists()) { // 如果输出文件夹不存在  
  29.                 outFile.getParentFile().mkdir();  
  30.             }  
  31.   
  32.             is = zipFile.getInputStream(zipEntry); // 得到实体的输入流  
  33.             os = new FileOutputStream(outFile); // 实例化文件输出流  
  34.   
  35.             int temp = 0;  
  36.             while ((temp = is.read()) != -1) {  
  37.                 os.write(temp);  
  38.             }  
  39.             is.close();  
  40.             os.close();  
  41.         }  
  42.     }  
  43.   
  44. }  

压缩文件中每一个压缩实体使用一个ZipEntry,一个zip文件中可能有多个ZipEntry。

回退流(PushbackInputStream):

在java的IO中所有的数据都是采用顺序的方式进行读取的,即:对于一个输入流来讲都是采用从头到尾顺序读取的。如果在输入流中某个不需要的内容被读取进来,则只能通过程序将这些不需要的内容处理掉。为了解决这样的问题。在java中提供了一种回退流【分为字节流和字符流】(PushbackInputStream、PushBackReader),可以把读取进来的某些数据重新退回到输入流的缓冲区中。
对于PushbackInputStream来说,它提供了3个unread()方法,这3个方法与InputStream类中的3个read()方法是一一对应的。

操作回退流:

以下面一个简单的程序为例,进行回退流的讲解,现在内存中有一个"www.imooc.com"的字符串,只要输入的内容是“.”就进行回退操作,即:不读取"."内存中使用ByteArrayInputStream:
[java]  view plain  copy
  1. import java.io.ByteArrayInputStream;  
  2. import java.io.IOException;  
  3. import java.io.PushbackInputStream;  
  4.   
  5. public class PushbackInputStreamDemo {  
  6.   
  7.     public static void main(String[] args) throws IOException {  
  8.         String str = "www.imooc.com";  
  9.         ByteArrayInputStream bais = new ByteArrayInputStream(str.getBytes());  
  10.         PushbackInputStream pushbackInputStream = new PushbackInputStream(bais);  
  11.         System.out.println("读取之后的数据为:");  
  12.         int temp = 0;  
  13.         while ((temp = pushbackInputStream.read()) != -1) {  
  14.             if (temp == '.') {  
  15.                 pushbackInputStream.unread(temp); // 放回到缓冲区  
  16.                 temp = pushbackInputStream.read(); // 再读一遍  
  17. //              System.out.print("退回" + (char) temp);  
  18.             } else {  
  19.                 System.out.print((char) temp);  
  20.             }  
  21.         }  
  22.     }  
  23.   
  24. }  
通过回退流可以给用户第二次读取的机会,仅仅只有这一个优点而已。

字符编码:

在计算机世界中,任何的文字都是以指定的编码方式存在的,在java程序的开发中常见的是以下几种编码:ISO8859-1、GBK/GB2312、unicode、UTF
iso8859-1:单字节编码,最多只能表示0~255的字符范围,主要在英文上应用;
GBK/GB2312:中文的国际编码,专门用来表示汉字,双字节编码;
unicode:java中就是采用此种编码方式,也是最标准的一种编码,是使用16进制表示的编码,但是此编码不兼容iso8859-1.
UTF:由于unicode不支持iso8859-1编码,而且容易占用更多的空间。而且对于英文字母也需要使用2个字节进行编码,这样使用unicode不便于存储和传输,因而产生了UTF编码。UTF编码兼容了iso8859-1编码,可以用来表示所有的语言字符。不过UTF编码是不定长编码,每一个字符的长度从1~6个字节不等,一般在中文网页中使用此编码,因为这样可以节省空间。
System类可以取得系统的编码:
[java]  view plain  copy
  1. System.getProperty("file.encoding")  
系统默认的编码时GBK,如果在程序中使用了iso8859-1编码,则肯定会出现乱码:
[java]  view plain  copy
  1. import java.io.File;  
  2. import java.io.FileOutputStream;  
  3. import java.io.IOException;  
  4. import java.io.OutputStream;  
  5.   
  6. public class CharsetDemo02 {  
  7.   
  8.     public static void main(String[] args) throws IOException {  
  9.         File file = new File("E:/tmp/info.txt");  
  10.         OutputStream os = new FileOutputStream(file);  
  11.   
  12.         byte[] bytes = "你好,中国".getBytes("iso8859-1"); // 将字符编码强制改为iso8859-1  
  13.         os.write(bytes); // 保存  
  14.         os.close();  
  15.     }  
  16.   
  17. }  
运行结果:
因为字符编码的不同产生了乱码。

对象的序列化及其反序列化:

什么叫做对象的序列化?

  一个对象产生之后实际上就在内存中开辟了一个存储空间,方便存储信息。

  对象的序列化就是将一个对象变成二进制的数据流的一种方法,通过对象的序列化可以方便的实现对象的存储和传输。如果一个类的对象需要被序列化,则该类必须实现Serializable接口,该接口的定义如下:

1 public interface Serializable {
2     
3 }

  Serializable接口中没有任何方法,只表示该类有这样具备这样一种能力,属于标识接口。下面的一个Student因为实现了Serializable接口,所以它就是可序列化的了:

复制代码
 1 package io.Serializable;
 2 
 3 import java.io.Serializable;
 4 
 5 public class Student implements Serializable {
 6     private String name;
 7     private int age;
 8 
 9     public Student(String name, int age) {
10         super();
11         this.name = name;
12         this.age = age;
13     }
14 
15     public String getName() {
16         return name;
17     }
18 
19     public void setName(String name) {
20         this.name = name;
21     }
22 
23     public int getAge() {
24         return age;
25     }
26 
27     public void setAge(int age) {
28         this.age = age;
29     }
30 
31     @Override
32     public String toString() {
33         return "Student [name=" + name + ", age=" + age + "]";
34     }
35 
36 }
复制代码

  以上的代码仅仅是实现了Serializable接口,其他部分并没有任何改变,以上的类产生的对象就是可序列化(二进制比特流)的了。

如果要进行对象的序列化必须依靠两个类:

ObjectInputStream和ObjectOutputStream

 serialVersionUID

  在对对象进行序列化和反序列化的时候要考虑到JDK版本的问题,如果序列化的JDK版本和反序列化的JDK版本不统一就会抛出异常。所以在对象序列化的操作中引入了一个serialVersionUID的常量。可以通过此常量验证版本的一致性。在进行反序列化的操作时,JVM会把传来的字节流中的serialVersionUID与本地响应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化的操作,否则就会抛出序列化版本不一致的异常。

  如果使用Eclipse进行编写,如果没有指定serialVersionUID则会出现一些警告信息,如图:

  按照Eclipse的提示,我们加上这个常量,这样这个类的设计就完成了。

对象的序列化和反序列化:

复制代码
 1 package io.Serializable;
 2 
 3 import java.io.File;
 4 import java.io.FileInputStream;
 5 import java.io.FileNotFoundException;
 6 import java.io.FileOutputStream;
 7 import java.io.IOException;
 8 import java.io.ObjectInputStream;
 9 import java.io.ObjectOutputStream;
10 
11 public class SerializableDemo {
12 
13     public static void main(String[] args) {
14 
15         File file = new File("E:" + File.separator + "tmp" + File.separator
16                 + "stu.obj"); // 保存路径
17         try {
18             ObjectOutputStream oos = new ObjectOutputStream(
19                     new FileOutputStream(file));
20 
21             Student[] students = { new Student("张三", 12),
22                     new Student("李四", 15), new Student("王五", 18) };
23             oos.writeObject(students); // 直接写入一个对象数组,因为数组也是对象
24             oos.close();
25 
26             ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
27                     file));
28             Student[] stus = (Student[]) ois.readObject();
29             ois.close();
30 
31             for (Student student : stus) {
32                 System.out.println(student);
33             }
34 
35         } catch (FileNotFoundException e) {
36             e.printStackTrace();
37         } catch (IOException e) {
38             e.printStackTrace();
39         } catch (ClassNotFoundException e) {
40             e.printStackTrace();
41         }
42 
43     }
44 
45 }
复制代码

  运行结果:

 Externalizable接口

  被Serializable接口声明的类的对象的内容都能够被序列化,如果现在用户希望可以自己制定序列化的内容,则可以让一个类实现Externalizable接口,该接口的定义如下:

1 public interface Externalizable extends Serializable {
2     public void readExternal(ObjectInput in)throws IOException,ClassNotFoundException;
3     public void writeExternal(ObjectOutput out)throws IOException;                  
4 }

 利用此接口修改之前的程序:

复制代码
 1 package io.Serializable;
 2 
 3 import java.io.Externalizable;
 4 import java.io.IOException;
 5 import java.io.ObjectInput;
 6 import java.io.ObjectOutput;
 7 
 8 public class Student implements Externalizable {
 9     private String name;
10     private int age;
11 
12     public Student(String name, int age) {
13         super();
14         this.name = name;
15         this.age = age;
16     }
17 
18     public String getName() {
19         return name;
20     }
21 
22     public void setName(String name) {
23         this.name = name;
24     }
25 
26     public int getAge() {
27         return age;
28     }
29 
30     public void setAge(int age) {
31         this.age = age;
32     }
33 
34     @Override
35     public String toString() {
36         return "Student [name=" + name + ", age=" + age + "]";
37     }
38 
39     @Override
40     public void readExternal(ObjectInput in) throws IOException,
41             ClassNotFoundException {
42         this.name = (String) in.readObject();    //读取属性
43         this.age = in.readInt();
44     }
45 
46     @Override
47     public void writeExternal(ObjectOutput out) throws IOException {
48         out.writeObject(this.name);    //保存属性
49         out.writeInt(this.age);
50     }
51 
52 }
复制代码
复制代码
 1 package io.Serializable;
 2 
 3 import java.io.File;
 4 import java.io.FileInputStream;
 5 import java.io.FileNotFoundException;
 6 import java.io.FileOutputStream;
 7 import java.io.IOException;
 8 import java.io.ObjectInputStream;
 9 import java.io.ObjectOutputStream;
10 
11 public class SerializableDemo {
12 
13     public static void main(String[] args) {
14 
15         File file = new File("E:" + File.separator + "tmp" + File.separator
16                 + "stu.obj"); // 保存路径
17         try {
18             ObjectOutputStream oos = new ObjectOutputStream(
19                     new FileOutputStream(file));
20 
21             Student[] students = { new Student("张三", 12),
22                     new Student("李四", 15), new Student("王五", 18) };
23             oos.writeObject(students); // 直接写入一个对象数组,因为数组也是对象
24             oos.close();
25 
26             
27             ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
28                     file));
29             Student[] stus = (Student[]) ois.readObject();
30             ois.close();
31 
32             for (Student student : stus) {
33                 System.out.println(student);
34             }
35 
36         } catch (FileNotFoundException e) {
37             e.printStackTrace();
38         } catch (IOException e) {
39             e.printStackTrace();
40         } catch (ClassNotFoundException e) {
41             e.printStackTrace();
42         }
43 
44     }
45 
46 }
复制代码

  运行结果:

  在使用Externalizable接口的时候需要原有的类中有无参构造,在Student类中加入无参构造后一切正常了!此外在Externalizable接口中也可以自定义哪些属性需要序列化,见以下代码:

复制代码
 1     @Override
 2     public void readExternal(ObjectInput in) throws IOException,
 3             ClassNotFoundException {
 4         this.name = (String) in.readObject();    //读取属性
 5 //        this.age = in.readInt();
 6     }
 7 
 8     @Override
 9     public void writeExternal(ObjectOutput out) throws IOException {
10         out.writeObject(this.name);    //保存属性
11 //        out.writeInt(this.age);
12     }
复制代码

  以上代码在运行的时候age属性将不会序列化!

transient关键字:

  在序列化对象的时候如果不需要某个属性被序列化可以使用transient关键字进行修饰。如此一来Externalizable接口就变得毫无用途。

1 private  transient int age;    //age属性不会被序列化

  运行结果:

  由此可见Externalizable接口的功能完全可以由Serializable接口和transient关键字的组合来取代!

JavaIO实例讲解:

将之前的菜单程序进行扩充,要求:对人的信息进行增删改查
Tips:使用对象的序列化进行保存

文件操作类:FileOperate
[java]  view plain  copy
  1. package io.demo;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileInputStream;  
  5. import java.io.FileOutputStream;  
  6. import java.io.ObjectInputStream;  
  7. import java.io.ObjectOutputStream;  
  8.   
  9. /*此类专门用于保存和读取*/  
  10. public class FileOperate {  
  11.     private File file = null;  
  12.   
  13.     public FileOperate(String pathName) {  
  14.         this.file = new File(pathName);  
  15.     }  
  16.   
  17.     /* 保存对象 */  
  18.     public boolean save(Object object) throws Exception {  
  19.         ObjectOutputStream oos = null;  
  20.         boolean flag = false;  
  21.         try {  
  22.             oos = new ObjectOutputStream(new FileOutputStream(file));  
  23.             oos.writeObject(object);// 对象写入  
  24.             flag = true;  
  25.         } catch (Exception e) {  
  26.             throw e; // 将异常交给调用处处理  
  27.         } finally {  
  28.             if (oos != null) {  
  29.                 oos.close();  
  30.             }  
  31.         }  
  32.         return flag;  
  33.     }  
  34.   
  35.     /* 读取对象 */  
  36.     public Object load() throws Exception {  
  37.         Object object = null;  
  38.         ObjectInputStream ois = null;  
  39.         try {  
  40.             ois = new ObjectInputStream(new FileInputStream(file));  
  41.             object = ois.readObject();  
  42.         } catch (Exception e) {  
  43.             throw e;  
  44.         } finally {  
  45.             if (ois != null) {  
  46.                 ois.close();  
  47.             }  
  48.         }  
  49.         return object;  
  50.     }  
  51. }  
学生类Student:
[java]  view plain  copy
  1. package io.demo;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. public class Student implements Serializable {  
  6.     private String name;  
  7.     private int age;  
  8.   
  9.     public String getName() {  
  10.         return name;  
  11.     }  
  12.   
  13.     public void setName(String name) {  
  14.         this.name = name;  
  15.     }  
  16.   
  17.     public int getAge() {  
  18.         return age;  
  19.     }  
  20.   
  21.     public void setAge(int age) {  
  22.         this.age = age;  
  23.     }  
  24.   
  25.     public Student(String name, int age) {  
  26.         super();  
  27.         this.name = name;  
  28.         this.age = age;  
  29.     }  
  30.   
  31.     @Override  
  32.     public String toString() {  
  33.         return "Student [name=" + name + ", age=" + age + "]";  
  34.     }  
  35.   
  36. }  
实际操作类Operation:
[java]  view plain  copy
  1. package io.demo;  
  2.   
  3. public class Operation {  
  4.     public static void add() {  
  5.         System.out.println("你选择了【增加】");  
  6.   
  7.         InputData inputData = new InputData();  
  8.         FileOperate fileOperate = new FileOperate("E:/tmp/test.info");  
  9.         String name = inputData.getString("请输入姓名:");  
  10.         int age = inputData.getInt("请输入年龄:""年龄必须是数字");  
  11.         Student student = new Student(name, age);  
  12.         try {  
  13.             fileOperate.save(student); // 保存对象  
  14.             System.out.println("信息添加成功!");  
  15.         } catch (Exception e) {  
  16.             e.printStackTrace();  
  17.         }  
  18.     }  
  19.   
  20.     public static void delete() {  
  21.         System.out.println("你选择了【删除】");  
  22.   
  23.         InputData inputData = new InputData();  
  24.         FileOperate fileOperate = new FileOperate("E:/tmp/test.info");  
  25.   
  26.         Student student = null;  
  27.         try {  
  28.             fileOperate.save(student); // 保存空对象  
  29.             System.out.println("信息删除成功!");  
  30.         } catch (Exception e) {  
  31.             e.printStackTrace();  
  32.         }  
  33.     }  
  34.   
  35.     public static void update() {  
  36.         System.out.println("你选择了【更新】");  
  37.   
  38.         FileOperate fileOperate = new FileOperate("E:/tmp/test.info");  
  39.         try {  
  40.             Student student = (Student) fileOperate.load();  
  41.             InputData inputData = new InputData();  
  42.             String name = inputData.getString("请输入姓名(原来的姓名:"  
  43.                     + student.getName() + "):");  
  44.             int age = inputData.getInt(  
  45.                     "请输入年龄(原来的年龄:" + student.getAge() + "):""年龄必须是数字");  
  46.             student = new Student(name, age);  
  47.             fileOperate.save(student);  
  48.             System.out.println("信息更新成功!");  
  49.         } catch (Exception e) {  
  50.             e.printStackTrace();  
  51.         }  
  52.   
  53.     }  
  54.   
  55.     public static void find() {  
  56.         System.out.println("你选择了【查找】");  
  57.   
  58.         FileOperate fileOperate = new FileOperate("E:/tmp/test.info");  
  59.         try {  
  60.             Student student = (Student) fileOperate.load();  
  61.             System.out.println(student);  
  62.         } catch (Exception e) {  
  63.             e.printStackTrace();  
  64.         }  
  65.   
  66.     }  
  67.   
  68.     public static void exit() {  
  69.         System.out.println("你选择了【退出】\n系统退出。。。");  
  70.         System.exit(0);  
  71.     }  
  72. }  
菜单显示类Menu:
[java]  view plain  copy
  1. package io.demo;  
  2.   
  3. public class Menu {  
  4.     public Menu() {  
  5.         while (true) {  
  6.             this.show();  
  7.         }  
  8.     }  
  9.   
  10.     public void show() {  
  11.         System.out.println("======= XXX系统 ========");  
  12.         System.out.println("\t[1].增加数据");  
  13.         System.out.println("\t[2].删除数据");  
  14.         System.out.println("\t[3].更新数据");  
  15.         System.out.println("\t[4].查找数据");  
  16.         System.out.println("\t[0].退出系统");  
  17.         InputData inputData = new InputData();  
  18.         int i = inputData.getInt("请选择:""请输入正确的选项!");  
  19.         switch (i) {  
  20.         case 1:  
  21.             Operation.add();  
  22.             break;  
  23.         case 2:  
  24.             Operation.delete();  
  25.             break;  
  26.         case 3:  
  27.             Operation.update();  
  28.             break;  
  29.         case 4:  
  30.             Operation.find();  
  31.             break;  
  32.         case 0:  
  33.             Operation.exit();  
  34.             break;  
  35.         default:  
  36.             System.out.println("请选择正确的操作!");  
  37.             break;  
  38.         }  
  39.     }  
  40. }  
处理输入类InputData
[java]  view plain  copy
  1. package io.demo;  
  2. import java.io.BufferedReader;  
  3. import java.io.IOException;  
  4. import java.io.InputStreamReader;  
  5. import java.text.ParseException;  
  6. import java.text.SimpleDateFormat;  
  7. import java.util.Date;  
  8.   
  9. public class InputData {  
  10.     private BufferedReader buf = null;  
  11.   
  12.     public InputData() {  
  13.         super();  
  14.         this.buf = new BufferedReader(new InputStreamReader(System.in));  
  15.     }  
  16.   
  17.     public String getString(String info) {  
  18.         String temp = null;  
  19.         System.out.println(info); // 打印提示信息  
  20.         try {  
  21.             temp = buf.readLine();  
  22.         } catch (IOException e) {  
  23.             e.printStackTrace();  
  24.         }  
  25.         return temp;  
  26.     }  
  27.   
  28.     public int getInt(String info, String err) {  
  29.         int temp = 0;  
  30.         String str = null;  
  31.         boolean flag = true;  
  32.           
  33.         while (flag) {  
  34.             str = this.getString(info);  
  35.             if (str.matches("^\\d+$")) { // 判断是否由数字组成  
  36.                 temp = Integer.parseInt(str);  
  37.                 flag = false// 结束循环  
  38.             } else {  
  39.                 System.out.println(err); // 打印错误信息  
  40.             }  
  41.         }  
  42.   
  43.         return temp;  
  44.     }  
  45.       
  46.     public float getFloat(String info, String err) {  
  47.         float temp = 0;  
  48.         String str = null;  
  49.         boolean flag = true;  
  50.           
  51.         while (flag) {  
  52.             str = this.getString(info);  
  53.             if (str.matches("^\\d+.?\\d+$")) { // 判断小数  
  54.                 temp = Float.parseFloat(str);  
  55.                 flag = false// 结束循环  
  56.             } else {  
  57.                 System.out.println(err); // 打印错误信息  
  58.             }  
  59.         }  
  60.   
  61.         return temp;  
  62.     }  
  63.   
  64.     public Date getDate(String info, String err) throws ParseException{  
  65.         Date temp = null;  
  66.         String str = null;  
  67.         boolean flag = true;  
  68.         while (flag) {  
  69.             str = getString(info);  
  70.             if (str.matches("^\\d{4}-\\d{2}-\\d{2}$")) { //^和$分别表示正则表达式的开头和结尾  
  71.                 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");  
  72.                 temp = sdf.parse(str);  
  73.                 flag = false;  
  74.             }else {  
  75.                 System.out.println(getString(err));  
  76.             }  
  77.         }  
  78.         return temp;  
  79.     }  
  80. }  
测试类:
[java]  view plain  copy
  1. package io.demo;  
  2.   
  3. public class ExecTest {  
  4.   
  5.     public static void main(String[] args) {  
  6.         new Menu();  
  7.   
  8.     }  
  9.   
  10. }  

JavaIO范例:投票程序

有一个班采用明主投票的方法推选班长,班长候选人一共有4位,每个人的姓名和代号分别是:张三,1;李四,2;王五,3;赵六,4.程序操作员将每张选票上所选的代号(1、2、3、4)循环输入电脑,输入数字0表示输入结束,然后将所有候选人的得票情况显示出来:

学生类Student:
[java]  view plain  copy
  1. package io.demo2;  
  2.   
  3. public class Student implements Comparable<Student> {  
  4.     private int stuNo;  
  5.     private String name;  
  6.     private int vote;  
  7.   
  8.     public Student(int stuNo, String name, int vote) {  
  9.         super();  
  10.         this.stuNo = stuNo;  
  11.         this.name = name;  
  12.         this.vote = vote;  
  13.     }  
  14.   
  15.     @Override  
  16.     public String toString() {  
  17.         return stuNo + "\t" + name + "\t【" + vote + "】";  
  18.     }  
  19.   
  20.     public int getStuNo() {  
  21.         return stuNo;  
  22.     }  
  23.   
  24.     public void setStuNo(int stuNo) {  
  25.         this.stuNo = stuNo;  
  26.     }  
  27.   
  28.     public String getName() {  
  29.         return name;  
  30.     }  
  31.   
  32.     public void setName(String name) {  
  33.         this.name = name;  
  34.     }  
  35.   
  36.     public int getVote() {  
  37.         return vote;  
  38.     }  
  39.   
  40.     public void setVote(int vote) {  
  41.         this.vote = vote;  
  42.     }  
  43.   
  44.     @Override  
  45.     public int compareTo(Student o) {  
  46.         if (this.vote < o.vote) {  
  47.             return 1;  
  48.         } else if (this.vote > o.vote) {  
  49.             return -1;  
  50.         } else {  
  51.             return 0;  
  52.         }  
  53.     }  
  54.   
  55. }  
数据输入类:InputData
[java]  view plain  copy
  1. package io.demo2;  
  2.   
  3. import java.io.BufferedReader;  
  4. import java.io.IOException;  
  5. import java.io.InputStreamReader;  
  6.   
  7. public class InputData {  
  8.     private BufferedReader bufferedReader = null;  
  9.   
  10.     public InputData() {  
  11.         this.bufferedReader = new BufferedReader(new InputStreamReader(  
  12.                 System.in));  
  13.     }  
  14.   
  15.     public String getString(String info) {  
  16.         String temp = null;  
  17.         System.out.println(info);  
  18.         try {  
  19.             temp = this.bufferedReader.readLine();  
  20.         } catch (IOException e) {  
  21.             e.printStackTrace();  
  22.         }  
  23.         return temp;  
  24.     }  
  25.   
  26.     public int getInt(String info, String err) {  
  27.         int temp = 0;  
  28.         String str = null;  
  29.         boolean flag = true;  
  30.         while (flag) {  
  31.             str = this.getString(info);  
  32.             if (str.matches("\\d+")) {  
  33.                 temp = Integer.parseInt(str);  
  34.                 flag = false;  
  35.             } else {  
  36.                 System.out.println(err);  
  37.             }  
  38.         }  
  39.         return temp;  
  40.     }  
  41. }  
具体操作类:
[java]  view plain  copy
  1. package io.demo2;  
  2.   
  3. import java.util.Arrays;  
  4.   
  5. public class Operate {  
  6.     Student[] students = { new Student(1"张三"0), new Student(2"李四"0),  
  7.             new Student(3"王五"0), new Student(4"赵六"0) };  
  8.     private boolean flag = true;  
  9.       
  10.     public Operate(){  
  11.         this.printInfo();   //投票之前  
  12.         while (flag) {  
  13.             this.vote();  
  14.         }  
  15.         this.getResult();  
  16.         this.printInfo();   //投票之后  
  17.         System.out.println("系统退出!");  
  18.         System.exit(0);  
  19.     }  
  20.       
  21.     public void getResult(){  
  22.         Arrays.sort(this.students);  
  23.         System.out.println("投票最终结果:"+this.students[0].getName()+"同学以"+this.students[0].getVote()+"票领先!详细信息如下:");  
  24.     }  
  25.       
  26.     public void vote(){  
  27.         InputData inputData = new InputData();  
  28.         int num = inputData.getInt("请输入班长候选人代号(数字0结束)""此选票无效,请输入正确的候选人编号!");  
  29.           
  30.         switch (num) {  
  31.         case 0:  
  32.             this.flag = false;  
  33.             break;  
  34.         case 1:  
  35.             this.students[0].setVote(this.students[0].getVote()+1);  
  36.             break;  
  37.         case 2:  
  38.             this.students[1].setVote(this.students[1].getVote()+1);  
  39.             break;  
  40.         case 3:  
  41.             this.students[2].setVote(this.students[2].getVote()+1);  
  42.             break;  
  43.         case 4:  
  44.             this.students[3].setVote(this.students[3].getVote()+1);  
  45.             break;  
  46.         default:  
  47.             System.out.println("此选票无效,请输入正确的候选人代号!");  
  48.             break;  
  49.         }  
  50.     }  
  51.     public void printInfo() {  
  52.         System.out.println("编号\t姓名\t票数");  
  53.         for (Student student : students) {  
  54.             System.out.println(student);  
  55.         }  
  56.     }  
  57. }  
测试类:
[java]  view plain  copy
  1. package io.demo2;  
  2.   
  3. public class Exec {  
  4.   
  5.     public static void main(String[] args) {  
  6.         new Operate();  
  7.     }  
  8.   
  9. }  

java中的输入输出重定向

下面的一个程序从键盘(stdin)中读取整数并计算它们的和
[java]  view plain  copy
  1. import java.util.Scanner;  
  2.   
  3. public class InputRedirectDemo {  
  4.   
  5.     public static void main(String[] args) {  
  6.     System.out.println("输入整数:");  
  7.     Scanner scanner = new Scanner(System.in);  
  8.     int sum = 0;  
  9.     while (scanner.hasNextInt()) {  
  10.         sum += scanner.nextInt();  
  11.     }  
  12.     System.out.println("sum = " + sum);  
  13.     }  
  14.   
  15. }  
也可以从文件中读取整数计算它们的和。使用重定向从文件中读取整数:java InputRedirectDemo< integers.txt
见下图:



类似的还可以使用输出重定向,也可以同时使用两种重定向:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值