File类:
在整个io包中,唯一表示与文件有关的类就是File类。使用File类可以进行创建或者删除文件等常用的操作。要想使用File类,则首先要观察File类的构造方法,此类的常用构造方法如下:
public File(String pathname)——根据路径找到文件
public static final String pathSeparator明明是一个常量,但是为什么是小写?实际上这就是java的历史遗留问题。
实例一:创建一个新文件
public boolean createNewFile() throws IOException——如果指定的文件不存在并成功地创建,则返回
true
;如果指定的文件已经存在,则返回
false。
- import java.io.File;
- import java.io.IOException;
-
- public class CreateFile {
-
- public static void main(String[] args) {
- File file = new File("E:/tmp/test.txt");
- try {
- file.createNewFile();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- }
将在E:\tmp中创建一个test.txt的文件(该文件大小为0byte)。注意:在传入路径的时候要么使用“/”,要么使用"\\"表示一个斜杠。
以上代码已经完成了文件的创建功能,但是如果在开发时按照以上的格式编写,则肯定会出现错误。因为在各个操作系统中,路径的分隔符是不一样的。例如:windows中使用“\”表示文件分隔符;而Linux中使用“/”表示文件分隔符。
如果java程序的可移植性继续保持,则最好是根据所在的操作系统来自动使用文件分隔符。使用File中的两个常量:
public static final String pathSeparator
public static final String separator
- System.out.println("separator:"+File.separator);
- System.out.println("pathSeparator:"+File.pathSeparator);
运行结果:
separator:\
pathSeparator:;
如果想让java程序继续保持可移植性,那么在编写路径的时候就应该使用File类中的常量。
修改以上程序:
- File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");
- try {
- file.createNewFile();
- } catch (IOException e) {
- e.printStackTrace();
- }
在进行路径传入的时候应该使用路径分隔符:File.separator
删除文件:
public boolean delete()
- File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");
- file.delete();
执行以上的程序,文件确实已经被删除了,但是在删除文件的时候应该先判断一下文件是否存在。
判断文件是否存在:
public boolean exists()
- File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");
- if (file.exists()) {
- file.delete();
- } else {
- System.out.println("文件不存在!");
- }
综合创建、删除文件的操作:
如果文件存在则删除之,如果文件不存在,则创建之。
- import java.io.File;
- import java.io.IOException;
-
- public class CreateAndDeleteFile {
-
- public static void main(String[] args) {
- File file = new File("E:" + File.separator + "tmp" + File.separator
- + "test.txt");
- if (file.exists()) {
- if (file.delete()) {
- System.out.println("文件删除成功!");
- }
- } else {
- try {
- if (file.createNewFile()) {
- System.out.println("文件创建成功!");
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
-
- }
从程序的运行结果可以发现,所有的操作并不会立即执行,因为java是通过JVM与底层进行交互的,所以所有的操作都必须经过JVM,所以有可能产生延迟。
什么是流?在程序中所有的数据都是以流的方式进行传输或者保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就需要使用输出流来完成。
程序中的输入和输出都是以流的形式进行保存的,流中保存的实际上全部都是字节文件。
javaIO中的步骤:
1.使用File类打开一个文件;
2.使用字节流或者字符流的子类指定输出装置;
3.进行读写操作;
4.关闭输入、输出流。
InputStream、OutputStream、Writer和Reader都是抽象类。IO操作属于资源操作,操作完成之后必须关闭,否则容易出现未知错误。
字节流:
字节流操作的是比特类型的数据,以byte数组为准,主要操作类就是OutputStream(字节输出流)、InputStream(字节输出流)
OutputStream:
整个IO包中字节输出流的最大父类,该类是一个抽象类,如果想要使用此类,则必须通过子类实例化对象。
- public class FileOutputStreamDemo01 {
-
- public static void main(String[] args) throws IOException {
-
- File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");
-
- FileOutputStream fos = new FileOutputStream(file);
-
- String info = "不悔梦归处,只恨太匆匆!";
- byte[] b = info.getBytes();
- fos.write(b);
-
- fos.close();
- }
-
- }
运行结果:
也可以一个一个字节地向文件中写入数据:
- String info = "不悔梦归处,只恨太匆匆!";
- byte[] b = info.getBytes();
- for (int i = 0; i < b.length; i++) {
- fos.write(b[i]);
- }
如果文件不存在则创建之,如果文件存在则覆盖之。如果想要进行追加的话,需要使用FileOutPutStream的另一个构造方法:public FileOutputStream(File file, boolean append)
如果想要字符串换行将字符串改变成如下:
- String info = "\r\n不悔梦归处,只恨太匆匆!";
-
- File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");
-
- FileInputStream fis = new FileInputStream(file);
-
- byte[]b = new byte[1024];
- fis.read(b);
-
- fis.close();
- System.out.println("内容是:"+new String(b));
文件没有几行,但是输出的结果中却有大量的空格,问题就在于我们开辟了一个1024大小的区域。
修改以上程序为以下:
-
- File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");
-
- FileInputStream fis = new FileInputStream(file);
-
- byte[]b = new byte[1024];
- int len = fis.read(b);
-
- fis.close();
- System.out.println("长度:"+len);
- System.out.println("内容是:"+new String(b,0,len));
运行结果:
改正程序后一切正常了。但是以上的代码还存在问题:文件明明很小,但是却开辟了1024字节肯定很浪费,能不能根据文件的大小开辟数组空间呢?
如果想要知道文件的大小直接使用File即可。public long length()
-
- File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");
-
- FileInputStream fis = new FileInputStream(file);
-
- byte[]b = new byte[(int) file.length()];
- int len = fis.read(b);
-
- fis.close();
- System.out.println("长度:"+len);
- System.out.println("内容是:"+new String(b));
-
- File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");
-
- FileInputStream fis = new FileInputStream(file);
-
- byte[]b = new byte[(int) file.length()];
- for (int i = 0; i < b.length; i++) {
- b[i] = (byte) fis.read();
- }
-
- fis.close();
- System.out.println("内容是:"+new String(b));
以上的代码只适用于知道输入流大小的时候,如果我不知道大小呢?
-
- File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");
-
- FileInputStream fis = new FileInputStream(file);
-
- byte[]b = new byte[1024];
- int len = 0;
- int temp = 0;
- while ((temp = fis.read())!=-1) {
- b[len++] = (byte) temp;
- }
-
- fis.close();
- System.out.println("内容是:"+new String(b,0,len));
字符流:
字符流比字节流好在一点:就是可以直接输出字符串了,不用再将字符串转化为字节数组了。
字符输出流:
-
- File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");
-
- FileWriter fw = new FileWriter(file, true);
-
- String str = "\r\n不悔梦归处,只恨太匆匆!";
-
- fw.write(str);
-
-
- fw.close();
字符输入流:
以字符数组的形式读取数据:
-
- File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");
-
- FileReader fr = new FileReader(file);
-
- char[] c = new char[1024];
-
- int len = fr.read(c);
-
- System.out.println(new String(c,0,len));
-
- fr.close();
通过文件是否读到底的形式进行读取:
-
- File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");
-
- FileReader fr = new FileReader(file);
-
- char[] c = new char[1024];
-
- int temp = 0;
- int len = 0;
- while ((temp = fr.read())!=-1) {
- c[len++] = (char) temp;
- }
-
- System.out.println("内容是:"+new String(c,0,len));
- fr.close();
字符流与字节流的区别:
通过一个代码来验证字符流使用了缓存:
- File file = new File("E:\\tmp\\hello.txt");
- FileOutputStream fos = new FileOutputStream(file);
- String str = "我前年买了个登山包!";
- byte[]b = str.getBytes();
- fos.write(b);
-
以上的代码并没有关闭流,文件已经存在于磁盘上,并且已经有了内容。
- File file = new File("E:\\tmp\\hello.txt");
- FileWriter fr = new FileWriter(file);
- String str = "我前年买了个登山包!";
- fr.write(str);
-
执行以上代码由于没有关闭流,所以硬盘上虽然生成了文件,但是文件的内容是空的。也就字符流中的数据保存在了内存的缓冲区中,流关闭的时候刷新缓冲区后内容才显示出来。基于这个原理:如果没有关闭流,可以使用flush()方法强制刷新缓冲区。
硬盘上的文件以字节的方式进行保存并且也是以字节的形式进行网络的传输,而字符只是在内存中才会形成的,所以字节流的使用很广泛。
实例讲解:文件拷贝
copy [源文件] [目标文件]
肯定要使用字节流,因为万一进行copy的东西是图片呢?
实现一:将源文件的内容全部读取,再一次性写入
实现二:边读边写【Better】(可以处理大文件)
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.IOException;
-
- public class Copy {
-
- public static void main(String[] args) {
- if (args.length != 2) {
- System.out.println("输入参数不正确。");
- System.out.println("例:java Copy 源文件路径 目标文件路径");
- System.exit(1);
- } else {
- copyFile(args[0], args[1]);
- }
- }
-
- private static void copyFile(String s, String d) {
- File sFile = new File(s);
- File dFile = new File(d);
-
- if (!sFile.exists()) {
- System.out.println("源文件不存在!");
- System.exit(1);
- } else {
- try {
- FileInputStream fis = new FileInputStream(sFile);
- FileOutputStream fos = new FileOutputStream(dFile);
-
- if (fis != null && fos != null) {
- int temp = 0;
- while ((temp = fis.read()) != -1) {
- fos.write(temp);
- }
- System.out.println("复制完成!");
- }
-
- fis.close();
- fos.close();
- } catch (IOException e) {
- System.out.println("复制失败!");
- e.printStackTrace();
- }
- }
- }
-
- }
字节-字符装换流
在整个IO包中,实际上就是分为字节流和字符流,但是除了这两个流之外,还存在一组字节流-字符流的装换类。
OutputStreamWriter:Writer(字符输出流)的子类,将一个字符流的输出对象变为字节流的输出对象。
InputStreamReader:Reader(字符输入流)的子类,将一个字节流的输入对象变为字符流的输入对象。
其构造方法如下:public OutputStreamWriter(OutputStream out,String charsetName) throws UnsupportedEncodingException
public InputStreamReader(InputStream in,String charsetName)throws UnsupportedEncodingException
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.OutputStreamWriter;
- import java.io.Writer;
-
- public class OutputStreamWriterDemo {
-
- public static void main(String[] args) throws IOException {
- File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");
- Writer out = new OutputStreamWriter(new FileOutputStream(file));
- out.write("Hello World!");
- out.close();
- }
-
- }
以上是写入文件,读入文件的方式正好相反:
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.io.Reader;
-
- public class InputStreamReaderDemo {
-
- public static void main(String[] args) throws IOException {
- File file = new File("E:"+File.separator+"tmp"+File.separator+"test.txt");
- Reader reader = new InputStreamReader(new FileInputStream(file));
-
- char[]c = new char[1024];
- int len = reader.read(c);
- reader.close();
-
- System.out.println(new String(c, 0, len));
- }
-
- }
以上只是以文件为例,因为OutputStreamWriter中接收的类型是OutputStream,只要是字节输出流都可以使用字符的形式输出,而InputStreamReader中接收的类型是InputStream,只要是字节输入流都可以使用字符输出流操作。
FileWriter和FileReader的说明:
FileOutputStream是OutputStream的子类,FileInputStream是InputStream的子类,但是在字符流文件的两个操作类却有一些特殊,FileWriter并不是Writer的子类,而是OutputStreamWriter的子类,而FileReader也不直接是Reader的子类而是InputStreamReader的子类。从这两个类的关系就可以清楚的发现:不管是使用字节流还是字符流实际上最终都是以字节的形式操作输入输出流的。
内存操作流:
之前所讲解的程序中,输出和输入都是从文件中来的,当然,也可以将输出的位置设置在内存上。此时就需要使用ByteArrayInputStream【将内容写入内存】和ByteArrayOutputStream【从内存中取出数据】将输入和输出的位置设置在内存上。
利用他们完成一个大小写装换的程序:
- import java.io.ByteArrayInputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
-
- public class ByteArrayDemo {
-
- public static void main(String[] args) {
- String str = "HELLO";
- ByteArrayInputStream bis = new ByteArrayInputStream(str.getBytes());
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
-
- int temp = 0;
- while ((temp = bis.read()) != -1) {
- char c = (char) temp;
- bos.write(Character.toLowerCase(c));
- }
-
-
- String newString = bos.toString();
- System.out.println(newString);
-
- try {
- bis.close();
- bos.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- }
-
- }
管道流:
管道流的主要作用是可以进行两个线程之间的通信,分为管道输出流PipedOutputStream和管道输入流(PipedInputStream),如果要想进行管道的输出,则必须把输出流连在输入流上,在PipedInputStream中有如下的一个方法用于连接管道:
public void connect(PipedInputStream snk)throws IOException
如果要连接输入和输出,则必须使用以上方法。
- import java.io.IOException;
- import java.io.PipedInputStream;
- import java.io.PipedOutputStream;
-
-
- class Send implements Runnable {
- private PipedOutputStream pos = null;
-
- public Send() {
- this.pos = new PipedOutputStream();
- }
-
- @Override
- public void run() {
- String str = "Hello World!";
-
- try {
- this.pos.write(str.getBytes());
- catch (IOException e) {
- e.printStackTrace();
- try {
- pos.close();
- catch (IOException e) {
- e.printStackTrace();
- System.out.println("发送方发送信息:"+str);
- }
-
- public PipedOutputStream getPipedOutputStream() {
- return this.pos;
- }
- }
-
-
- class Receive implements Runnable {
- PipedInputStream pis = null;
-
- public Receive() {
- this.pis = new PipedInputStream();
- }
-
- @Override
- public void run() {
- byte[] b = new byte[1024];
- int len = 0;
- try {
- len = this.pis.read(b);
- catch (IOException e) {
- e.printStackTrace();
- pis.close();
- catch (IOException e) {
- System.out.println("接收方接收信息:" + new String(b, 0, len));
- }
-
- public PipedInputStream getPipedInputStream() {
- return this.pis;
- }
- }
-
-
- public class PipeDemo {
-
- public static void main(String[] args) {
- Send s = new Send();
- Receive r = new Receive();
- try {
- s.getPipedOutputStream().connect(r.getPipedInputStream());
- catch (IOException e) {
- e.printStackTrace();
-
- new Thread(s, "发送方").start();
- new Thread(r, "接收方").start();
-
- }
- }
- <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类重新包装了一下,使输出更加方便。
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.PrintStream;
-
- public class PrintStreamDemo01 {
-
- public static void main(String[] args) {
- try {
- PrintStream ps = new PrintStream(new FileOutputStream(
- "E:\\tmp\\test.txt"));
- ps.print("Hello ");
- ps.println("World!");
- ps.print("1 + 1 = " + 2);
- ps.close();
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- }
-
- }
-
- }
运行结果:

也就是说此时将FileOutputStream类包装了一下。实际上这样的思路叫做装饰者设计模式。
在JDK1.5之后,java又对PrintStream类进行了扩充,增加了格式化的输出方式,直接使用printf()方法就可以完成操作,但是在格式化输出的时候需要指定其输出的数据类型。
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.PrintStream;
-
- public class PrintStreamDemo02 {
-
- public static void main(String[] args) {
- try {
- PrintStream ps = new PrintStream(new FileOutputStream(
- "E:\\tmp\\test.txt"));
-
- String name = "汤姆·杰克逊";
- int age = 30;
- float score = 96.3f;
- char sex = 'M';
- ps.printf("姓名:%s,年龄:%d,分数:%f,性别:%c", name,age,score,sex);
-
- ps.close();
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- }
-
- }
-
- }
运行结果:
由运行结果可以看出写入浮点数好像有一点问题,不过无关紧要(和系统有关)。
如果觉得%d、%c不好记忆的话也可以直接使用%s,因为任何数据类型碰到String都向String靠拢。
将以上的代码中的格式输出变成以下:
- ps.printf("姓名:%s,年龄:%s,分数:%s,性别:%s", name,age,score,sex);
运行结果:
一点要明白一点:打印流属于装饰者设计模式。
System类对IO的支持
System类中有三个常量对IO有支持:
- public static final InputStream in;
- public static final PrintStream out;
- public static final PrintStream err;
- <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>
- import java.io.IOException;
- import java.io.OutputStream;
-
- public class SystemDemo01 {
-
- public static void main(String[] args) {
- OutputStream os = System.out;
-
- try {
- os.write("hello world".getBytes());
- } catch (IOException e) {
- e.printStackTrace();
- }
- try {
- os.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- }
谁为我实例化,我就具备了谁的能力!根据对象的多态性,如果实现的子类不同,那么完成的功能也不同。
System.err
如果程序中出现了错误可以使用System.err进行输出。
- public class SystemDemo02 {
-
- public static void main(String[] args) {
- String str = "Hello World!";
- try {
- System.out.println(Integer.parseInt(str));
- catch (NumberFormatException e) {
- System.err.println(e);
- }
- <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类类型的对象,那么此时就可以使用此方式完成从键盘读取数据的功能。
- import java.io.IOException;
- import java.io.InputStream;
-
- public class SystemDemo03 {
-
- public static void main(String[] args) {
- InputStream input = System.in;
- byte[]b = new byte[1024];
- System.out.print("请输入内容:");
- try {
- int len = input.read(b);
- System.out.println("你的输入:"+new String(b,0,len));
- input.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- }
运行结果:
请输入内容:niasn3543#$%^
你的输入:niasn3543#$%^
所有的数据都已经输入进去了,但是以上的代码是否存在问题?
问题1:指定了输入数据的长度,如果输入的数据的长度超过了指定的长度,则只能输入部分数据
将以上 的比特数组的空间改为3byte,运行结果:
请输入内容:123456
你的输入:123
字符串被截短了。
问题2:如果开辟的比特数组为奇数的话可能出现中文乱码问题:
请输入内容:简体中文
你的输入:简?
因为一个中文使用2个字节来保存,一个汉字被劈成两半。
如果不指定比特数组的长度,则只能通过设置标志位的方式来完成。
判断标志位的方式:
- import java.io.IOException;
- import java.io.InputStream;
-
- public class SystemDemo04 {
-
- public static void main(String[] args) {
- InputStream input = System.in;
- System.out.println("输入内容:");
- StringBuffer sb = new StringBuffer();
- int temp = 0;
- try {
- while ((temp = input.read())!= -1) {
- char c = (char) temp;
- if ('\n' == c) {
- break;
- }
- sb.append(c);
- }
- System.out.println("你的输入:\n"+sb);
- input.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- }
运行结果:
这时输入的长度没有限制了,但是输入中文一样会乱码(每个汉字是拆分成两次读入的)。
最好的解决方案是将全部的输入数据放到一块内存之中,之后一次性从内存中读取数据,这样所有的数据都只读了一次,不会有乱码,也不会有长度的限制。
如果要实现以上功能就需要使用
BufferedReader。
输入、输出重定向
为System.out重定向-setOut()方法:
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.PrintStream;
-
-
- public class SystemDemo05 {
-
- public static void main(String[] args) throws FileNotFoundException {
- System.setOut(new PrintStream(new FileOutputStream("E:\\tmp\\red.txt")) );
- System.out.println("你好");
- System.out.println(1223);
- }
-
- }
输出重定向,所有的内容向文件中打印。
利用以上思想可以完成错误的重定向,System.out是用户希望看到的信息,一旦有错误最好写入文件。
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.PrintStream;
-
- public class SystemDemo06 {
-
- public static void main(String[] args) throws FileNotFoundException {
- String str = "Hello World!";
- try {
- System.out.println(Integer.parseInt(str));
- } catch (NumberFormatException e) {
- System.setOut(new PrintStream(new FileOutputStream(
- "E:\\tmp\\err.log")));
- System.out.println(e);
- } catch (Exception e) {
- }
- }
-
- }
错误信息不会在控制台打印,而是重定向到了错误日志文件中。
System.err重定向:
利用System.err向控制台输出信息,为了方便起见使用内存操作流。
- import java.io.ByteArrayOutputStream;
- import java.io.FileNotFoundException;
- import java.io.PrintStream;
-
- public class SystemDemo07 {
-
- public static void main(String[] args) throws FileNotFoundException {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- System.setErr(new PrintStream(bos));
- System.err.print("错误的的");
- System.err.println("Hello!");
- }
-
- }
System.err的信息写入了内存。
为System.in重定向
例如:从文件中读取。例如有以下文件:
用System.in从文件中读取:
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.io.InputStream;
-
- public class SystemDemo08 {
-
- public static void main(String[] args) throws IOException {
- System.setIn(new FileInputStream("E:\\tmp\\demo.txt"));
- InputStream input = System.in;
- byte[]b = new byte[1024];
- int len = input.read(b);
- System.out.println("输入的内容是:"+new String(b, 0, len));
- input.close();
- }
-
- }
运行结果:
对于输入输出重定向基本上都是在System.out上使用,用来把信息显示给用户。
Scanner不能直接接受日期型的数据,如果要想接收Date类型的数据,只能通过字符串转型,但是也是可以使用Scanner中的方法进行验证的:
验证:public boolean hasNext(Pattern pattern)
接收:public String next(Pattern pattern)
- import java.text.ParseException;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.Scanner;
-
- public class ScannerDemo04 {
-
- public static void main(String[] args) {
- Scanner scanner = new Scanner(System.in);
- String str = null;
- Date date = null;
- System.out.println("输入日期(yyyy-MM-dd):");
- if (scanner.hasNext("^\\d{4}-\\d{2}-\\d{2}$")) {
- str = scanner.next("^\\d{4}-\\d{2}-\\d{2}$");
- try {
- date = new SimpleDateFormat("yyyy-MM-dd").parse(str);
- } catch (ParseException e) {
- e.printStackTrace();
- }
- } else {
- System.out.println("输入的日期格式错误!");
- }
- System.out.println(date);
- }
-
- }
Scanner还可以直接从文件中读取数据:
- import java.io.File;
- import java.io.FileNotFoundException;
- import java.util.Scanner;
-
- public class ScannerDemo05 {
-
- public static void main(String[] args) {
- StringBuffer str = new StringBuffer();
- try {
- Scanner scanner = new Scanner(new File("E:/tmp/tmp.txt"));
-
- while (scanner.hasNext()) {
- str.append(scanner.next()).append("\n");
- }
- System.out.println("文件中的内容是:" + str);
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- }
-
- }
- }
在使用Scanner类来读取文件的时候要考虑到换行的功能。
数据操作流(DataInputStream和DataOutputStream)
在整个IO包中提供了两个与平台无关的数据操作流:
DataOutputStream:数据输出流
DataInputStream:数据输入流
通常数据输出流会按照一定的格式将数据输出,再通过数据输入流按照一定的格式将数据读入。
例如有以下订单数据:
DataOutputStream类的定义如下:
public class DataOutputStream extends FilterOutputStream implements DataOutput,其中FilterOutputStream 是OutputStream的子类,DataOutput接口中有一系列的写操作(writeXXX),可以写入各种数据类型。此接口的定义格式应该大致了解,以便于后面的学习。
要想使用DataOutputStream进行数据的输出操作的话,必须指明数据的输出格式。
- import java.io.DataOutputStream;
- import java.io.FileOutputStream;
- import java.io.IOException;
-
- public class DataOutputStreamDemo {
-
- public static void main(String[] args) throws IOException {
-
- DataOutputStream dos = new DataOutputStream(new FileOutputStream(
- "E:/tmp/data.txt"));
-
- String[] names = { "衬衣", "手套", "围巾" };
- float[] price = { 98.3f, 30.3f, 50.5f };
- int[] nums = { 3, 2, 1 };
- for (int i = 0; i < names.length; i++) {
- dos.writeChars(names[i]);
- dos.writeChar('\t');
- dos.writeFloat(price[i]);
- dos.writeChar('\t');
- dos.writeInt(nums[i]);
- dos.writeChar('\n');
- }
- dos.close();
- }
-
- }
执行以上程序将会生成文件E:\tmp\data.txt,但是用记事本打开会出现乱码,因为使用DataOutputStream写入的数据需要使用DataInputStream才能够进行读取!
- import java.io.DataInputStream;
- import java.io.FileInputStream;
- import java.io.IOException;
-
- public class DataInputStreamDemo {
-
- public static void main(String[] args) throws IOException{
- DataInputStream dis = new DataInputStream(new FileInputStream(
- "E:/tmp/data.txt"));
-
- String name = null;
- float price = 0.0f;
- int nums = 0;
-
- char[] temp = null;
- int len = 0;
- char c = 0;
- while (true) {
- temp = new char[200];
- len = 0;
-
- while ((c = dis.readChar()) != '\t') {
- temp[len] = c;
- len++;
- }
- name = new String(temp, 0, len);
- price = dis.readFloat();
- dis.readChar();
- nums = dis.readInt();
- dis.readChar();
-
- System.out.printf("名称:%s 价格:%.2f 数量:%d\n", name, price, nums);
- }
- }
-
- }
这个和随机访问很相似,DataInput接口和DataOutput接口,这两个接口彼此对应。
合并流(SequenceInputStream)
合并流从概念上讲:就是将内容合并到一起了。例如在:E:\tmp目录下存在两个文件a.txt和b.txt
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.io.SequenceInputStream;
-
- public class SequenceInputStreamDemo {
-
- public static void main(String[] args) {
- try {
- InputStream is1 = new FileInputStream("E:/tmp/a.txt");
- InputStream is2 = new FileInputStream("E:/tmp/b.txt");
- OutputStream os = new FileOutputStream("E:/tmp/ab.txt");
- SequenceInputStream sis = new SequenceInputStream(is1, is2);
-
- int temp = 0;
- while ((temp = sis.read()) != -1) {
- os.write(temp);
- }
-
- sis.close();
- is1.close();
- is2.close();
- os.close();
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- }
-
- }
运行结果:
两个文件的内容被合并了!
压缩流(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:
- java.lang.Object
- java.io.OutputStream
- java.io.FilterOutputStream
- java.util.zip.DeflaterOutputStream
- java.util.zip.ZipOutputStream
从继承关系可以看出它是一个字节输出流
- public void putNextEntry(ZipEntry e)
- throws IOException
在压缩文件中,每一个压缩的实体都用一个ZipEntry表示,所以在进行压缩之前必须通过putNextEntry设置一个zipEntry即可。
- public void setComment(String comment)
以上方法用来设置注释
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.zip.ZipEntry;
- import java.util.zip.ZipOutputStream;
-
- public class ZipOutputStreamDemo01 {
-
- public static void main(String[] args) throws IOException {
- File file = new File("E:/tmp/info.txt");
- File zipFile = new File("E:/tmp/info.zip");
-
- InputStream is = new FileInputStream(file);
- ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile));
- zos.putNextEntry(new ZipEntry(file.getName()));
- zos.setComment("Windows SystemFile");
-
- int temp = 0;
- while ((temp = is.read()) != -1) {
- zos.write(temp);
- }
- is.close();
- zos.close();
- }
-
- }
运行结果:
以上的代码仅仅是完成文件的压缩,如果要压缩一个文件夹怎么办?
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.zip.ZipEntry;
- import java.util.zip.ZipOutputStream;
-
- public class ZipOutputStreamDemo02 {
-
- public static void main(String[] args) throws IOException {
- File file = new File("E:/tmp/test");
- File zipFile = new File("E:/tmp/testdir.zip");
-
- InputStream is = null;
- ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile));
-
- if (file.isDirectory()) {
- File[] listFiles = file.listFiles();
- for (int i = 0; i < listFiles.length; i++) {
- is = new FileInputStream(listFiles[i]);
- zos.putNextEntry(new ZipEntry(file.getName() + File.separator
- + listFiles[i].getName()));
- zos.setComment("E:/tmp/test Files");
- int temp = 0;
- while ((temp = is.read()) != -1) {
- zos.write(temp);
- }
-
- }
-
- }
-
- is.close();
- zos.close();
- }
-
- }
运行结果:
ZipFile类:
··
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.util.zip.ZipEntry;
- import java.util.zip.ZipException;
- import java.util.zip.ZipFile;
-
- public class ZipFileDemo01 {
-
- public static void main(String[] args) throws ZipException, IOException {
- File file = new File("E:/tmp/info.zip");
- File outFile = new File("E:/tmp/info_unzip.txt");
- ZipFile zipFile = new ZipFile(file);
- ZipEntry entry = zipFile.getEntry("info.txt");
- OutputStream os = new FileOutputStream(outFile);
- InputStream is = zipFile.getInputStream(entry);
- int temp = 0;
- while ((temp = is.read()) != -1) {
- os.write(temp);
- }
- is.close();
- os.close();
- }
-
- }
以上程序对一个压缩文件进行了解压缩。以上程序中有一个问题:必须知道每一个压缩实体的名称才可以进行解压缩操作,如果是一个文件夹呢?
ZipInputStream:
该类的定义如下:
- java.lang.Object
- java.io.InputStream
- java.io.FilterInputStream
- java.util.zip.InflaterInputStream
- java.util.zip.ZipInputStream
它是InputStream的子类。
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.util.zip.ZipEntry;
- import java.util.zip.ZipFile;
- import java.util.zip.ZipInputStream;
-
- public class ZipInputStreamDemo01 {
-
- public static void main(String[] args) throws IOException {
- File file = new File("E:/tmp/testdir.zip");
- File outFile = null;
-
- ZipFile zipFile = new ZipFile(file);
- ZipEntry zipEntry = null;
- ZipInputStream zis = new ZipInputStream(new FileInputStream(file));
-
- OutputStream os = null;
- InputStream is = null;
-
- while ((zipEntry = zis.getNextEntry()) != null) {
- System.out.println("解压缩:" + zipEntry.getName() + "文件!");
- outFile = new File("E:/tmp/" + zipEntry.getName());
-
- if (!outFile.getParentFile().exists()) {
- outFile.getParentFile().mkdir();
- }
-
- is = zipFile.getInputStream(zipEntry);
- os = new FileOutputStream(outFile);
-
- int temp = 0;
- while ((temp = is.read()) != -1) {
- os.write(temp);
- }
- is.close();
- os.close();
- }
- }
-
- }
压缩文件中每一个压缩实体使用一个ZipEntry,一个zip文件中可能有多个ZipEntry。
回退流(PushbackInputStream):
在java的IO中所有的数据都是采用顺序的方式进行读取的,即:对于一个输入流来讲都是采用从头到尾顺序读取的。如果在输入流中某个不需要的内容被读取进来,则只能通过程序将这些不需要的内容处理掉。为了解决这样的问题。在java中提供了一种回退流【分为字节流和字符流】(PushbackInputStream、PushBackReader),可以把读取进来的某些数据重新退回到输入流的缓冲区中。
对于PushbackInputStream来说,它提供了3个unread()方法,这3个方法与InputStream类中的3个read()方法是一一对应的。
操作回退流:
以下面一个简单的程序为例,进行回退流的讲解,现在内存中有一个"www.imooc.com"的字符串,只要输入的内容是“.”就进行回退操作,即:不读取"."内存中使用ByteArrayInputStream:
- import java.io.ByteArrayInputStream;
- import java.io.IOException;
- import java.io.PushbackInputStream;
-
- public class PushbackInputStreamDemo {
-
- public static void main(String[] args) throws IOException {
- String str = "www.imooc.com";
- ByteArrayInputStream bais = new ByteArrayInputStream(str.getBytes());
- PushbackInputStream pushbackInputStream = new PushbackInputStream(bais);
- System.out.println("读取之后的数据为:");
- int temp = 0;
- while ((temp = pushbackInputStream.read()) != -1) {
- if (temp == '.') {
- pushbackInputStream.unread(temp);
- temp = pushbackInputStream.read();
-
- } else {
- System.out.print((char) temp);
- }
- }
- }
-
- }
通过回退流可以给用户第二次读取的机会,仅仅只有这一个优点而已。
字符编码:
在计算机世界中,任何的文字都是以指定的编码方式存在的,在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类可以取得系统的编码:
- System.getProperty("file.encoding")
系统默认的编码时GBK,如果在程序中使用了iso8859-1编码,则肯定会出现乱码:
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.OutputStream;
-
- public class CharsetDemo02 {
-
- public static void main(String[] args) throws IOException {
- File file = new File("E:/tmp/info.txt");
- OutputStream os = new FileOutputStream(file);
-
- byte[] bytes = "你好,中国".getBytes("iso8859-1");
- os.write(bytes);
- os.close();
- }
-
- }
运行结果:
因为字符编码的不同产生了乱码。
对象的序列化及其反序列化:
什么叫做对象的序列化?
一个对象产生之后实际上就在内存中开辟了一个存储空间,方便存储信息。

对象的序列化就是将一个对象变成二进制的数据流的一种方法,通过对象的序列化可以方便的实现对象的存储和传输。如果一个类的对象需要被序列化,则该类必须实现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
- package io.demo;
-
- 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 object) throws Exception {
- ObjectOutputStream oos = null;
- boolean flag = false;
- try {
- oos = new ObjectOutputStream(new FileOutputStream(file));
- oos.writeObject(object);
- flag = true;
- } catch (Exception e) {
- throw e;
- } finally {
- if (oos != null) {
- oos.close();
- }
- }
- return flag;
- }
-
-
- public Object load() throws Exception {
- Object object = null;
- ObjectInputStream ois = null;
- try {
- ois = new ObjectInputStream(new FileInputStream(file));
- object = ois.readObject();
- } catch (Exception e) {
- throw e;
- } finally {
- if (ois != null) {
- ois.close();
- }
- }
- return object;
- }
- }
学生类Student:
- package io.demo;
-
- import java.io.Serializable;
-
- public class Student implements Serializable {
- private String name;
- private int age;
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public int getAge() {
- return age;
- }
-
- public void setAge(int age) {
- this.age = age;
- }
-
- public Student(String name, int age) {
- super();
- this.name = name;
- this.age = age;
- }
-
- @Override
- public String toString() {
- return "Student [name=" + name + ", age=" + age + "]";
- }
-
- }
实际操作类Operation:
- package io.demo;
-
- public class Operation {
- public static void add() {
- System.out.println("你选择了【增加】");
-
- InputData inputData = new InputData();
- FileOperate fileOperate = new FileOperate("E:/tmp/test.info");
- String name = inputData.getString("请输入姓名:");
- int age = inputData.getInt("请输入年龄:", "年龄必须是数字");
- Student student = new Student(name, age);
- try {
- fileOperate.save(student);
- System.out.println("信息添加成功!");
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- public static void delete() {
- System.out.println("你选择了【删除】");
-
- InputData inputData = new InputData();
- FileOperate fileOperate = new FileOperate("E:/tmp/test.info");
-
- Student student = null;
- try {
- fileOperate.save(student);
- System.out.println("信息删除成功!");
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- public static void update() {
- System.out.println("你选择了【更新】");
-
- FileOperate fileOperate = new FileOperate("E:/tmp/test.info");
- try {
- Student student = (Student) fileOperate.load();
- InputData inputData = new InputData();
- String name = inputData.getString("请输入姓名(原来的姓名:"
- + student.getName() + "):");
- int age = inputData.getInt(
- "请输入年龄(原来的年龄:" + student.getAge() + "):", "年龄必须是数字");
- student = new Student(name, age);
- fileOperate.save(student);
- System.out.println("信息更新成功!");
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- }
-
- public static void find() {
- System.out.println("你选择了【查找】");
-
- FileOperate fileOperate = new FileOperate("E:/tmp/test.info");
- try {
- Student student = (Student) fileOperate.load();
- System.out.println(student);
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- }
-
- public static void exit() {
- System.out.println("你选择了【退出】\n系统退出。。。");
- System.exit(0);
- }
- }
菜单显示类Menu:
- package io.demo;
-
- public class Menu {
- public Menu() {
- while (true) {
- this.show();
- }
- }
-
- public void show() {
- System.out.println("======= XXX系统 ========");
- System.out.println("\t[1].增加数据");
- System.out.println("\t[2].删除数据");
- System.out.println("\t[3].更新数据");
- System.out.println("\t[4].查找数据");
- System.out.println("\t[0].退出系统");
- InputData inputData = new InputData();
- int i = inputData.getInt("请选择:", "请输入正确的选项!");
- switch (i) {
- case 1:
- Operation.add();
- break;
- case 2:
- Operation.delete();
- break;
- case 3:
- Operation.update();
- break;
- case 4:
- Operation.find();
- break;
- case 0:
- Operation.exit();
- break;
- default:
- System.out.println("请选择正确的操作!");
- break;
- }
- }
- }
处理输入类InputData
- package io.demo;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.text.ParseException;
- import java.text.SimpleDateFormat;
- import java.util.Date;
-
- public class InputData {
- private BufferedReader buf = null;
-
- public InputData() {
- super();
- this.buf = new BufferedReader(new InputStreamReader(System.in));
- }
-
- public String getString(String info) {
- String temp = null;
- System.out.println(info);
- try {
- temp = 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.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.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) throws ParseException{
- Date temp = null;
- String str = null;
- boolean flag = true;
- while (flag) {
- str = getString(info);
- if (str.matches("^\\d{4}-\\d{2}-\\d{2}$")) {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
- temp = sdf.parse(str);
- flag = false;
- }else {
- System.out.println(getString(err));
- }
- }
- return temp;
- }
- }
测试类:
- package io.demo;
-
- public class ExecTest {
-
- public static void main(String[] args) {
- new Menu();
-
- }
-
- }
JavaIO范例:投票程序
有一个班采用明主投票的方法推选班长,班长候选人一共有4位,每个人的姓名和代号分别是:张三,1;李四,2;王五,3;赵六,4.程序操作员将每张选票上所选的代号(1、2、3、4)循环输入电脑,输入数字0表示输入结束,然后将所有候选人的得票情况显示出来:
学生类Student:
- package io.demo2;
-
- public class Student implements Comparable<Student> {
- private int stuNo;
- private String name;
- private int vote;
-
- public Student(int stuNo, String name, int vote) {
- super();
- this.stuNo = stuNo;
- this.name = name;
- this.vote = vote;
- }
-
- @Override
- public String toString() {
- return stuNo + "\t" + name + "\t【" + vote + "】";
- }
-
- public int getStuNo() {
- return stuNo;
- }
-
- public void setStuNo(int stuNo) {
- this.stuNo = stuNo;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public int getVote() {
- return vote;
- }
-
- public void setVote(int vote) {
- this.vote = vote;
- }
-
- @Override
- public int compareTo(Student o) {
- if (this.vote < o.vote) {
- return 1;
- } else if (this.vote > o.vote) {
- return -1;
- } else {
- return 0;
- }
- }
-
- }
数据输入类:InputData
- package io.demo2;
-
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
-
- public class InputData {
- private BufferedReader bufferedReader = null;
-
- public InputData() {
- this.bufferedReader = new BufferedReader(new InputStreamReader(
- System.in));
- }
-
- public String getString(String info) {
- String temp = null;
- System.out.println(info);
- try {
- temp = this.bufferedReader.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.getString(info);
- if (str.matches("\\d+")) {
- temp = Integer.parseInt(str);
- flag = false;
- } else {
- System.out.println(err);
- }
- }
- return temp;
- }
- }
具体操作类:
- package io.demo2;
-
- import java.util.Arrays;
-
- public class Operate {
- Student[] students = { new Student(1, "张三", 0), new Student(2, "李四", 0),
- new Student(3, "王五", 0), new Student(4, "赵六", 0) };
- private boolean flag = true;
-
- public Operate(){
- this.printInfo();
- while (flag) {
- this.vote();
- }
- this.getResult();
- this.printInfo();
- System.out.println("系统退出!");
- System.exit(0);
- }
-
- public void getResult(){
- Arrays.sort(this.students);
- System.out.println("投票最终结果:"+this.students[0].getName()+"同学以"+this.students[0].getVote()+"票领先!详细信息如下:");
- }
-
- public void vote(){
- InputData inputData = new InputData();
- int num = inputData.getInt("请输入班长候选人代号(数字0结束)", "此选票无效,请输入正确的候选人编号!");
-
- switch (num) {
- case 0:
- this.flag = false;
- break;
- case 1:
- this.students[0].setVote(this.students[0].getVote()+1);
- break;
- case 2:
- this.students[1].setVote(this.students[1].getVote()+1);
- break;
- case 3:
- this.students[2].setVote(this.students[2].getVote()+1);
- break;
- case 4:
- this.students[3].setVote(this.students[3].getVote()+1);
- break;
- default:
- System.out.println("此选票无效,请输入正确的候选人代号!");
- break;
- }
- }
- public void printInfo() {
- System.out.println("编号\t姓名\t票数");
- for (Student student : students) {
- System.out.println(student);
- }
- }
- }
测试类:
- package io.demo2;
-
- public class Exec {
-
- public static void main(String[] args) {
- new Operate();
- }
-
- }
java中的输入输出重定向
下面的一个程序从键盘(stdin)中读取整数并计算它们的和
- import java.util.Scanner;
-
- public class InputRedirectDemo {
-
- public static void main(String[] args) {
- System.out.println("输入整数:");
- Scanner scanner = new Scanner(System.in);
- int sum = 0;
- while (scanner.hasNextInt()) {
- sum += scanner.nextInt();
- }
- System.out.println("sum = " + sum);
- }
-
- }
也可以从文件中读取整数计算它们的和。使用重定向从文件中读取整数:java InputRedirectDemo< integers.txt
见下图:
类似的还可以使用输出重定向,也可以同时使用两种重定向: