#输入是运行的程序从某个外部源读取信息或数据,输出是指运行的程序将信息或数据写到某个外部目标中。
#流与相关类
当程序需要读取数据时,就会开启一个同向数据源的流,这个数据源可以是文件,内存或是网络链接。
当程序需要输出数据是,就会开启一个通向目的地的流。
可以想象数据好像在流动一样。
下图是java一些I/O流的示例。java输入/输出工作可以由单一种类的I/O流实现,也可以由多种输入/输出流共同完成。
Java中的I/O流分别继承自下表中的抽象流类:
一、字节流及其方法
字节流是以字节为处理单位的流,字符流是以字符为处理单位的流。java中两个字节组成一个字符。字节流是最基本的流,所有的InputStream和OutputStream以及他们的子类都是字节流。字节流主要处理二进制数据,他是按字节来处理数据的。InputStream和OutputStream是两个抽象类,其他以字节为单位的流都扩展了这两个基类。
1.InputStream类
表示从不同数据源产生的输入类。数据源包括:①文件②管道,一个线程通过管道输出流发送数据,另一个线程通过管道输入流读取数据,这样就可以实现两个线程通信。3.字节数组 ;4.其他种类流组成的序列;5.字符串对象;6.对象序列。每一种字节流的输入数据源都有对应的InputStream的子类,其中深色为节点流,浅色为处理流,将在后面介绍。下表列出常用的InputStream的子类及其功能。
FilterInputStream提供以各种方式过滤输入数据所需的方法。下图是常用的FilterInputStream的子类和功能概述:
下图是InputStream的子类的构造方法:
2.OutputStream类
决定输出所要指向的目标包括1.文件;2,管道;3.字节数组;4,对象序列。每一种字节流的输出数据类型都有对应的OutputStream子类。下图为常用子类:
FilterOutputStream类是OutputStream类的一个子类,提供了以各种方式过滤输出数据的方法;下图是FilterOutputStream的子类和功能描述以及OutputStream的子类的构造方法:
3.InputStream的方法
该类及其子类种常用的方法如下:
read()方法和其重载方法说明;
①public int read()
从输入流读取下一个字节数据。返回0-255范围内的int字节值。如果因已达到流末尾而没有可用的字节,放回-1;
②public int read(byte[]b)
从输入流读取一定数量字节并将其存储在缓冲区数组b种。以整数形式返回实际读取的字节数。如果因为流位于文件末尾而没有可用的字节,则返回-1;否则至少可以读取一个字节并将其存储在b中。此方法等同于read(b,0,b.length)
③public int read(byte[]b,int off,int len)
将输入流中最多len个字节数据读入字节数组。尝试读取多达len字节,但可能读取较少数量的字符。以整数形式返回实际读取字节数。如果由于已达流末尾而并没有可用的字节,则返回-1。参数b:读入数据的缓冲区;参数off:写入数据的数组b的初始偏移量;参数len:要写入的最大字节数。
4.OutputStream的方法
该类及其子类常用方法如下:
write()方法和其重载方法以及flush()方法说明:
①public void write(int b)
将指定的字节写入此输出流,要写入的字节是是参数b的低八位的内容,b的前24位内容将被忽略。
②public void write(byte []b)
将b.length个字节从指定的字节数组写入此输出流。write(b)的常规用法与write(b,0,b.length)的效果完全相同。
③public void write (byte[] b,int off,int len)
将指定字节数组从偏移量off开始的len个字节写入此输出流。
④public void flush()
刷新此输出流并强制写出所有缓冲的输出字节。flush()的常规用法是:如果此输出流的实现已经缓冲了以前写入的任何字节,则调用此方法指示应将这些字节立即写入预期的目标。
#字符流及其方法
字符流是以Unicode字符位基础的I/O流,表示以Unicode字符为单位从I/O流中读取或往I/O中写入信息。
一、Reader可以在多种数据源上按照字符进行输入。包括文件,管道,字符数组和字符串对象等。每一种字符流的输入数据源都有对应的Reader的子类。如下图:
二、Writer类
Writer类可以将多种类型的数据按照字符进行输出。输出目标包括:文件,管道,字符数组等。每一种字符流的输出数据类型都有对应的Writer的子类。
三、Reader的方法:
read()方法说明:
①public int read()
从输入流读取下一个字符数据。返回0到65535范围的int字符值,如果因为已经达到文件末尾而没有可用的字符,则返回值-1。
②pulic int read(char [] cbuf)
从输入流中读取一定数量的字符并将其存储在缓存区数组cbuf中。以整数形式返回实际读取的字符数。如果因为流位于文件末尾而没有可用的字符,则返回-1,否则,至少可以读取一个字符并将其存储在数组cbuf中。
③public int read(char []cbuf,int off,int len)
将输入流中最多len个字符从偏移量off开始读入字符数组cbuf中。
四、Writer的方法:
write方法和flush()方法说明:
①public void write(int c)
将指定的字符写入此输出流。
②public void write(char []cbuf)
将cbuf.length个字符数组写入此输出流。
③public void write(char [] cbuf,int off,int len)
将指定字符数组从偏移量off开始的len个字符写入此输出流。
④public void flush()
刷新此输出流并强制写出所有缓冲的输出字符。flush()的常规用法是:如果此输出流的实现已经缓冲了以前写入的任何字符,则调用此方法将这些字符立即写入他们预期的目标。
#节点流和处理流
Java I/O按照数据处理的功能不同,将流分为节点流和处理流。
一、节点流
节点流可以从一个特定的数据源(节点)读写数据,这些特定的数据源包括:文件,内存,管道等。主要的节点流包括文件I/O流,内存I/O和管道I/O。下图是文件为数据源的节点流的示意图:
常用节点流如下图所示:
FileInputStream和FileOutputStream用于从文件中读取或向文件中写入字节流。如果在构造FileOutputStream时,文件已经存在,则覆盖这个文件。FileReader和FileWriter,用于从文件中读取或向文件中写入字符流。ByteArrayInputStream、ByteArrayOutputStream与CharArrayReader,CharArrayWriter分别以字节和字符的方式从内存中读写数据。StringReader、和StringWriter分别以字符的方式从字符串中读数据和像字符串中写数据。PipedInputStream和PipedOutputStream与PipedsReader和PipedWriter分别以字节和字符的方式读写管道数据。
二、处理流
处理流(过滤流)是对一个已存在的流的连接和封装,通过调用所封装流的功能来实现数据读写,修改或管理流中数据,并提供额外功能。处理流的构造方法总要带一个其他的流对象作为参数。一个流对象经过其他流的多次包装,称为流的链接。下图表示一个以文件为数据源的处理流的示意图。
常用的处理流类如下表所示。主要包括缓冲处理流,过滤处理流,数据处理流,对象处理流,打印处理流以及字节/字符转换处理流等
①缓冲流
没有缓冲的I/O,直接读写效率低,对硬盘损坏大,为了解决这些缺点,java提供了基于缓冲的I/O流。带缓冲的输入流从一个类似缓冲区的内存区域中读取数据,当缓冲区为空时,调用基本的输入API完成输入操作;同样地,带缓冲的输出流首先向缓冲区写数据,在缓冲区已满时调用基本的输出API完成输出操作。缓冲流链接在其他节点流之上,对读写数据提供缓冲功能,提高读写效率,并增加了一些新方法。
Java提供四种缓冲流如下图所示:
②打印流
为其他输出流添加了打印的功能。可以打印floa,int,String等多种数据类型。
分别针对字节和字符,提供了重载的print()和println()方法用于多种数据类型的输出,不抛出IOException。
③数据流(DataInputStream/DataOutputStream
提供了可以存取与机器无关的Java的基本类型数据的方法。以支持基本数据类型的输入/输出,包括:boolean,char,byte,short,int,long,float和double类型。所有的数据流分别继承自InputStream和OutputStream,实现了DataInput和DataOutput接口,数据流的构造方法常以InputStream和OutputStream类型的流对象为参数。
import java.io.*;
public class TextDataStream{
public static void main(String []args){
ByteArrayOutputStream boas=new ByteArrayOutputStream();
DataOutputStream dos=new DataOutputStream(baos);
try{
dos.writeDouble(Math.random());
dos.wrietBoolean(true);
ByteArrayInputStream bais=new ByteArrayInputStream(baos.toByteArray());
System.out.println(bais.available());
DataInputStream dis=new DataInputStream(bais);
System.out.println(dis.readDouble());
System.out.println(dis.readBoolean());
dos.close();
dis.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
//输出结果
9
0.922050670572964
true
程序说明:第四行代码创建了一个字节数组输出流baos,第五行再封装成数据输出流dos,第七,八行往此流中写入一个double和一个boolean数据。第九行用baos的字节数组创建一个字节数组输入流bais,第11行再封装成数据输入流dis,第12,13行从此流中度出一个double和一个boolean数据;第14,15行关闭相关流。
④对象流
对象流支持Object的输入/输出,使任意一个对象能够像数据流一样进行输入/输出。在对象流中进行输入/输出的对象需要是可序列化的类型。对象流包括ObjectInputStream和ObjectOutputStream,这些类实现了DataInput和DataOutput的子接口ObjectInput和ObjectOutput接口。在使用的时候,一个对象流可以包含基本数据类型,对象类型数据,以及两种数据类型的混合。
对象流实例:
import java.util.concurrent.*;
import java.io.*;
import java.util.*;
public class Logon implements Serializable{
private Date date=new Date();
private String username;
private String password;
public Logon(String name,String pwd){
username=name;
password=pwd;
}
public String toString(){
return "Logon info: \username: "+username+"\n date: "+date+"\n password:"+password;
}
public static void main(String []args)throws Exception{
Logon a=new Logon("Hulk","myLittlePony");
System.out.println("logon a="+a);
ObjectOutputStream o=new ObjectOutputStream(new FileOutputStream("Logon.out"));
o.writeObject(a);
o.close();
TimeUnit.SECONDS.sleep(1); //延时
ObjectInputStream in=new ObjectInputStream(new FileInputStream("Logon.out"));
System.out.println("Recovering object at "+new Date());
a=(Logon)in.readObject():
System.out.println("long a="+a);
}
}
程序说明:第17行先创建一个Logon的对象实例a,第20行将该对象写入文件Logon.out中,然后第26.27行从该文件中读出对象实例a,并显示出来。
#标准I/O流
System.in是InputStream类的对象实例in作为标准输入流对象,对应于键盘输入;System.out是PrintStream类的对象实例out作为标准输出流对象,对应于显示器输出;System.err为PrintStream类的对象实例err作为标准错误输出流对象,对应于显示器输出。
标准I/O操作实现键盘输入字符串并在屏幕上显示:
import java.io.*;
public class Echo{
public static void main(String []args){
throws IOException{
BufferReader stdin=new BufferedReader(new InputStreamReader(System.in));
String s;
while((s=stdin.readLine())!=null&&s.length()!=0)
System.out.println(s);
}
}
程序说明:第5,6行代码将键盘输入System.in封装成缓冲字符输入流stdin,第8.9行按行显示在屏幕上,当输入为空行或者输入Ctrl+Z时退出循环,结束输入。
标准I/O重定向实例:
import java.io.*;
public class Redirection{
public static void main(String []args){
throws{
PrintStream consoleout=System.out;
InputStream consolein=System.in;
BufferedInputStream in=new BufferedOutputStream(new FileInputStream("Redicrecting.java)):
PringStream out=new PrintStream(new BufferedOutputStream(new FileOutputStream("test.out"))):
System.setIn(in);
System.setOut(out);
System.setOut(out);
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
String s;
while((s=br.readLine())!=null)
Sysetm.out.println(s);
out.close;
System.setOut(consoleout):
in.close();
System.setIn(consolein);
}
}
程序说明:第五六行代码用变量consoleout和consolein引用屏幕输入System.in和System.out,第七八行将Redirecting.java文件输入流封装成缓冲字节输入流in,第八~十行将test.out文件输入流封装成缓冲字节输出流,进一步封装成打印流out;第11~13行将标准输入重定向为in,标准输出流和错误流重定向为out;第14~18行实现新定向标准I/O的读写,即文件复制;第19~21行关闭相关流并设回标准I/O.
另外,Java5添加了java.util.Scanner类,这是一个用于扫描输入文本的新的实用程序。它是原有的StringTokenizer和Matcher类的某种结合,除了能使用正则表达式之外,Scanner类还可以对字符串和基本类型的数据进行分析;
可以使用Scanner类创建一个对象:Scanner reader=new Scanner(System.in);
然后reader对象可以调用方法nextByte,nextDouble,nextFloat,nextInt,nextLine,nextLong,next Short,读取用户在命令行输入的各种数据类型。上述方法在执行十都会造成阻塞,等待用户在命令行输入数据回车确认。
Scanner读入键盘数据示例:
import java.util.*;
public class Example{
public static void main(String args[]){
System.out.println("请输入若干个数,每输入一个数用回车确认");
System.out.println("最后输入一个非数字结束输入操作");
Scanner reader=new Scanner(System.in);
double sum=0;
int m=0;
while(reader.hasNextDouble()){
double x=reader.nextDoubl();
m=m+1;
sum=sum+x;
}
System.out.println(m+"个数的和为"+sum);
System.out.println(m+"个数的平均值是"+sum/m);}
}
#文件I/O流
一、获取文件数据源和目的源信息的File类:
File类位于java.io.file包中,表示系统文件名,包括某个特定的路径和文件名,或者一系列路径和文件名。File类的构造方法和常用方法如下:
读取文件的路径与长度示例:
import java.io.*;
public class Test{
public static void main(String[] args){
String filename="myfile.txt";
String directory="mydirl"+"/"+"mydir2";
File f=new File(directory,filename);
if(f.exists()){
System.out.println("File Name:"+f.getAbsolutePath());
System.out.println("File Length:"+f.length);
}else{
f.getParentFile().mkdirs();
try{
f.createNewFile();
}catch(IOException e){
e.printStackTrace();
}
}
}
}
程序说明:第六行代码根据以directory为路径名,filename为文件名创建一个新File对象f。第七行判断文件对象f是否存在,如果文件存在获取文件名称和长度,如果文件不存在,则创建文件。
二、基于字节的文件流
①FileInputStream
文件是否可用取决于主机环境。还能用于读取诸如图像文件之类的原始字节流。
使用FileInputStream类访问文件,显示文件内容和长度:
import java.io.*;
public class TestFileInputStream{
public static void main(String []args){
int b=0;
FileInputStream in=null;
try{
in=new FileInputStream("./TestFileInputStream.java");
}catch(FileNotFoundException e){
System.out.println("Can't find file");
System.exit(-1);
}
try{
long num=0;
while((b=in.read()!=-1){
System.out.print((char)b);
num++;
}
in.close();
System.out.println("read"+num+"Byte");
}catch(IOException e1){
System.out.println("File read error");
System.out.exit(-1);
}
}
}
②FileOutputSteam
文件是否可用取决于主机环境。还能用于写入诸如图像文件之类的原始字节流。若某些平台一次只允许一个FileOutputStream(或其他文件写入对象)打开文件进行写入,这时若文件已经打开,此类的构造方法将失败。
使用FileOutputStream类访问文件,实现文件的复制:
import java.io.*;
public class TestFileOutputStream{
public static void main(String[]args){
int b=0;
FileInputStream in=null;
FileOutputStream out=null;
try{
in=new FileInputStream("./HelloWorld.java");
out=new FileOutputStram("./HW.java");
while((b=in.read())!=-1){
out.write(b);
}
in.close();
out.close();
}catch(FileNotFoundException e2){
System.out.println("Can't find file");
System.exit(-1);
}catch(IOException e1){
System.out.println("File copy error");
System.exit(-1);
}
System.out.println("File copy finished");
}
}
二、基于字符的文件流
①FileReader
从文件系统中的某个文件获得输入字符。文件是否可用取决于主机环境。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在FileInputStream上构造一个InputStreamReader。
使用FileReader类访问文件,读出文件长度:
import java.io.*;
public class TestFileReader{
public static void main(String[]args){
FileReader fr=null;
int c=0;
int num=0;
try{
fr=new FileReader("./TestFileReader.java");
int ln=0;
while((c=fr.read())!=-1){
System.out.print((char)c);
num++;
}
fr.close();
System.out.println(num);
}catch(FileNotFoundException e){
System.out.println("Can't find file");
}catch(IOException e){
System.out.println("File read error");
}
}
}
②FileWriter
将数据写入File或FileDescriptor的输出流。文件是否可用取决于主机环境。若某些平台一次只允许一个FileWriter(或其他文件写入对象)打开文件进行写入,这时若文件已经打开,此类的构造方法将失败。
使用FileWriter类访问文件:
import java.io.*;
public class TestFileWriter{
public static void main(String[]args){
FileWriter fw=null;
int num=0;
try{
fw=new FileWriter("./unicode.dat");
for(int c=0;c<50000;c++){
fw.write(c);
num++;
}
fw.close();
System.out.println(num);
}catch(IOException e){
e.printStackTrace();
System.out.println("File write error");
System.exit(-1);
}
}
}
注:最后说一下System.exit()这个方法,接收一个参数status,0表示正常退出,非零参数表示非正常退出。不管status为何值都会退出程序。和return 相比,return是回到上一层,而System.exit(status)是回到最上层。
String[] args是main函数的形式参数,可以用来获取命令行用户输入进去的参数。
比方说,java主类名为Test,执行
java Test A1 A2, 就会使得在代码中,args[0] = "A1",args[1] = "A2"。args[0]不是伪代码,是args这个数组的第一个元素。
如果执行
java Test,即没有参数的情况,args数组长度就为0。
高级语言编写的代码大都有获取命令行输入参数的能力,比如c语言中的main(int argc,char *argv[]) 等等。