java io流
对于java使用者来说熟练的掌握io流是非常重要的,在java.io包下面包含大量的io流的实现类平时使用时对这一部分也十分模糊所以这次对io流进行一个简单的梳理。
下面是io包下的一些实现类:
从图中可以清晰的看出io流可以分为两类字节流和字符流,两者之间的主要区别就是操作数据单元不同,字节流的操作单元是字节而字符流的操作单元是字符。
另外如果根据数据的流向不同还可以把io流划分为输入流和输出流,图中的reader、inputStream 就属于输入流他们负责把程序之外的数据流读到程序当中,图中的writer、outputStream属于输出流他们的作用是把程序中的数据输出到程序之外。
在使用io流之前必须先要介绍file类,file类是java.io下对文件进行操作的类,下面是一些file当中的常用方法,根据这些方法可以处理路径和目录的问题:
import java.io.File;
import java.io.IOException;
public class io {
public static void main(String arg[]) throws IOException{
File file=new File(".");
//获得文件名
System.out.print(file.getName());
//文件上一级目录
System.out.print(file.getParent());
//文件相对路径
System.out.print(file.getPath());
//文件绝对路径
System.out.print(file.getAbsoluteFile());
//文件绝对路径的上一级目录
System.out.print(file.getAbsoluteFile().getParent());
//建立临时文件
File file2=File.createTempFile("one", ".txt", file);
//jvm退出时删除
file2.deleteOnExit();
//以当前时间作为文件名
File file3=new File(System.currentTimeMillis()+"");
System.out.print("file3 is exists?"+file.exists());
//在该目录下创建文件
file3.createNewFile();
//建立file3对应的目录,但是file文件已创建所以该方法无效
file3.mkdir();
//获得当前目录下的所有文件夹和路径
String[] filelist=file.list();
System.out.print("当前目录下的所有文件和路径"+"\n");
for(String filename :filelist){
System.out.print(filename+"\n");
}
//获得系统磁盘所有根目录
File[] root =File.listRoots();
System.out.print("系统所有根路径:"+"\n");
for(File rootname :root){
System.out.print(rootname+"\n");
}
}
}
看完file类接下来就开始分析io流的使用方法,需要注意一点上图中的最基础的四个类都是抽象类不能创建实例进行操作。
1.fileinputStream 和 filereader,先看一段代码
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
public class file {
public static void main(String arg[]) throws IOException{
/*FileInputStream fil=new FileInputStream("F:\\work\\io\\src\\one\\file.java");
byte[] buff=new byte[1024];
int hasRead=0;
while((hasRead=fil.read(buff))>0){
System.out.print(new String(buff,0,hasRead));
}
fil.close();*/
//
try (FileReader readfile=new FileReader("F:\\work\\io\\src\\one\\file.java");){
char[] cbuffer=new char[30];
int hasReads=0;
while((hasReads=readfile.read(cbuffer))>0){
System.out.print(new String (cbuffer,0,hasReads));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
被注掉的代码是字符输入流,需要注意的是fileinputStream 和 filereader当中的read方法,根据read内部参数不同可以读取单个字节/字符或者把读出的字节/字符放到数组中去。
2.fileoutputstream 和filewrite,这两个类可以与上面两个类进行对照其作用正好与上两个类的作用相反,下面通过一段代码来进行查看:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
public class filewriter {
public static void main(String arg[]){
/* try (FileWriter writer=new FileWriter("wtiter.txt");){
writer.write("第一句话。\r\n");
writer.write("第二句话。\r\n");
writer.write("第三句话。\r\n");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
try(
FileInputStream input=new FileInputStream("F:\\work\\io\\src\\one\\filewriter.java");
FileOutputStream output=new FileOutputStream("output.txt");
){
byte[] buff=new byte[1024];
int hasRead=0;
while((hasRead=input.read(buff))>0){
output.write(buff, 0, hasRead);
}
}catch(IOException e){
e.printStackTrace();
}
}
}
这段代码是把当前的java文件通过输入流放到程序中,然后通过输出流把部分代码输出到output.txt文件,这里需要注意的是write方法。
3.处理流PrintStream,处理流的目的是包装outputStream然后通过PrintStream对数据进行输出,下面是具体用法:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
public class print {
public static void main(String arg[]){
try(
FileOutputStream output=new FileOutputStream("print.txt");
PrintStream print=new PrintStream(output);
){
//print 通过print()方法向输出流中定义的文件输出
print.print("输出的语句");
print.print(new print());
}catch(IOException e){
e.printStackTrace();
}
}
}
这段代码通过print方法把数据输出到了FileOutputStream 指定的文件中。
4.StringReader和StringWriter,如果流中的数据都是字符串的形式的话就可以用到StringReader和StringWriter,其具体用法与filereader和filewriter类似,这里就不贴代码进行描述了。
5.推回输入流PushbackReader,推回输入流可以把输入流中的unread方法可以把数据推回到缓冲区,具体使用如下:
import java.io.FileReader;
import java.io.IOException;
import java.io.PushbackReader;
public class pushback {
public static void main(String arg[]){
try(//用pushback文件创建退回输入流对象,指定其长度为64
PushbackReader reader=new PushbackReader(new FileReader("F:\\work\\io\\src\\one\\pushback.java"), 64)
)
{
char[] buff=new char[32];
String lastCount="";
int hasRead=0;
while((hasRead=reader.read(buff))>0){
String content =new String(buff,0,hasRead);
int targetIndex=0;
//if中为true时说明已经读到new PushbackReader
if((targetIndex=(lastCount+content).indexOf("new PushbackReader"))>0){
//上次读出和这次读出的内容一起退回缓冲区,unread()方法中要放char[]数组
//不要理解为把之前读出的全部推回去
reader.unread((lastCount+content).toCharArray());
if(targetIndex>32){
buff=new char[targetIndex];
}
reader.read(buff,0,targetIndex);
System.out.print(new String(buff,0,targetIndex)+"\n"+targetIndex);
System.exit(0);
}
else{
System.out.print(lastCount);
lastCount=content;
}
}
}catch(IOException e){e.printStackTrace();}
}
}
这段代码的作用是把文件中new PushbackReader字符串之前的字符都输出来,需要注意的是在PushbackReader初始化的时候需要用FileReader的对象来作为他的参数。
6.ObjectOutputStream和ObjectInputStream,这两个流的作用是把对象放入流中进行处理,这里需要注意到是一般的对象放入流中会产生错误所以这里要对这些特殊的对象进行处理,具体的处理方式就是对象序列化,对象序列化就是实现Serializable类或继承Externalizable类,以第一种方法为例下面来进行类的序列化:
import java.io.Serializable;
public class serializable implements Serializable{
private String sername;
private int sernum;
public serializable(String sername,int sernum){
this.setSername(sername);
this.setSernum(sernum);
System.out.print("serializable 参数构造器");
}
public String getSername() {
return sername;
}
public void setSername(String sername) {
this.sername = sername;
}
public int getSernum() {
return sernum;
}
public void setSernum(int sernum) {
this.sernum = sernum;
}
}
经过这次序列化之后该类的对象就可以放入ObjectOutputStream和ObjectInputStream当中进行操作:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class test {
public static void main(String arg[]){
try(
ObjectOutputStream out=new ObjectOutputStream
(new FileOutputStream("serializabletest.txt"));
ObjectInputStream in =new ObjectInputStream
(new FileInputStream("F:\\work\\io\\serializabletest.txt"));
){
serializable ser=new serializable("hewenkai", 22);
out.writeObject(ser);
serializable ser2 = (serializable)in.readObject();
System.out.print(ser2.getSername()+"\n"+ser2.getSernum()+"\n"+(ser2==ser));
}catch(IOException e){
e.printStackTrace();
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
这里我把一个对象那个放入到输出流输出然后又通过该输入流把该对象读出来,最后的结果是读出的结果对象与写入对象的成员变量都相等,这就说明了对象读写的操作成功。
这里有一个问题需要注意就是,如果序列化类中用其他类的对象作为其成员变量那么这个类也必须实现序列化。
7.重定向输入输出及虚拟机读取其他进程数据:
import java.io.IOException;
import java.io.PrintStream;
public class redirectout {
public static void main(String arg[]){
try(
PrintStream pa=new PrintStream("redirect.txt");
){
//标准输出重定向到pa输出流
System.setOut(pa);
System.out.print("重定向输出");
}catch(IOException e){
e.printStackTrace();
}
}
}
这段代码是对标准输出的重定向System.setOut后执行System.out.print可以发现控制台中没有输出相应的字符,这是因为System.setOut把标准输出重定向到了PrintStream对象所指定的文件,这样再执行System.out.print就可以把字符输出到redirect.txt文件中。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
//读取其他进程的数据
public class readfrom {
public static void main(String arg[]) throws IOException{
//执行javac命令返回命令子进程
Process pro=Runtime.getRuntime().exec("javac");
BufferedReader read=new BufferedReader(new InputStreamReader(pro.getErrorStream()));
String buff=null;
//读取错误输出
while((buff=read.readLine())!=null){
System.out.print(buff);
}
read.close();
}
}
这段代码使用虚拟机读取其他进程的流Process 的对象启动了javac命令并获取到了该命令的错误输出
8.RandomAccessFile,RandomAccessFile类对象可以读取文件当中的任意位置,只需要通过seek方法移动指针就可以了
import java.io.IOException;
import java.io.RandomAccessFile;
public class randomaccessfile {
public static void main(String arg[]){
try(
RandomAccessFile reader=new RandomAccessFile("randomaccessfile.txt", "rw");
){
reader.seek(reader.length());
reader.write("追加内容".getBytes());
}catch(IOException e){e.printStackTrace();}
}
}
这段代码直接把指针移动到了文件的最后,然后在文件的最后输出自己需要添加的内容。
另外还有一部分类没有用代码进行说明,但是可以从类名中来分析其主要作用例如StringReader和StringWriter明显就是对String字符串进行操作的输入流输出流,其他类也是如此,这些类的具体方法与filereader和filewrite的用法大体相似所以不进行赘述。