摘要
Java数据在输入/输出时会做连续传递,这点有点像水流,所以Java把数据流称为流(Stream)。最常见的流是在文本模式下,用户输入资料以后。经过处理再将结果打印在屏幕上。另一种流是发生在文件存取:先将文件内容读取到内存,然后再将结果写回文件。输入和输出时相对内存而言:
如图:
由图可知,数据流分为输入输出流,当然,Java函数库内相应的处理类也分为输入输出两种。除此之外,若以数据处理方式来分类,数据流还可以分为字节流(byte)和字符流(char)两种。
1.输入类:
(1)字节型:类名称大部分带有Stream,例如:FileInputStream
(2)字符型:类名称大部分带有Reader,例如:FileReader
2.输出类:
(1)字节型:类名称大部分带有Stream,例如:FileOutputStream
(2)字符型:类名称大部分带有Reader,例如:FileWriter
(提示:InputStreamReader类之所以同时带有Stream和Reader,是因为它可将位型流转换成字符型,所以可以所示这两种传输型的桥柳,OutputStreamWriter同理)
Java流的主要结构:
既然提到了流,属于Java.io包,这里就顺便把IO包下面的File类也顺便说了吧。File类专门提供文件管理功能,例如文件的查询,增加,修改和删除等功能。文件查询仅限于查询文件的基本信息,如文件名,文件大小,修改日期等。File类不能用来直接存取文件内容,若要直接存取内容,就必须用到流的相关函数。
下面是File类的相关函数:
1.构造函数
public File(String pathname)
(依据传入的pathname(路径名称)建立File对象,虽然建立了File对象, 但还未真正在硬盘上建立相应的文件或目录,必须调用createNewFile()或mkdirs()方法才能真正建立文件或目录)
public File(File parent,String child)
(依据传入的parent对象与child路径建立File对象。虽然建立了File对象, 但还未真正在硬盘上建立相应的文件或目录,必须调用createNewFile()或mkdirs()方法才能真正建立文件或目录)
2.方法
public boolean createNewFile()
(建立一个新的文件,成功返回true,失败传回false 异常事件—>当IO发生问题,会产生IOException)
public Boolean delete()
(可以删除文件或者目录(条件是目录必须为空)删除成功返回true,失败返回false)
public void deleteOnExit()
调用此方法的文件会在程序执行结束时自动删除,删除成功返回true,失败传回false
public boolean exists()
检查文件或者目录是否存在,存在返回true,不存在返回false
public String getName()
取得路径最后的名称,可能是文件或目录名称,例如:[E:/Books/book.txt],调用此方法会取得[book.txt]
public String getParent()
取得上一层的路径,例如:[E:/Books/book.txt],调用此方法会取得[E:/Books]
public String getPath()
取得完整路径的名称,例如:[E:/Books/book.txt],调用此方法会取得[E:/Books/book.txt]
public boolean isFile()
判断是否为文件,是的话返回true,否则返回false
public long lastModified()
传回最后修改时间
public long length()
传回文件的大小(单位为byte),如果是目录,则传回0
public boolean renameTo(File dest)
将原名称改为新名称(新名称就是dest对象代表的名称),成功返回true,失败返回false
public String[] list()
如Flile对象为目录,则返回该目录内的所有文件和目录的名称,如果是对象,则返回null
public File[] listFiles()
如果File对象为目录,传回该目录内的所有File对象,所以为File数组。如果File是我文件,则传回null
public boolean mkdir()
建立目录,如果上一层路径不存在,则无法建立。例如:路径为【E:/Books/book.txt】,如果【E:/Books】不存在,则无法建立book.txt目录
public boolean mkdirs()
建立目录,即使上一层路径不存在,也会自动建立所需目录。成功返回true,失败返回false
public boolean setReadOnly()
设定只读模式,成功返回true,失败返回false
代码举例:在E盘建立Book目录,然后在该目录下建立book1.txt,book2.txt
public class Test {
public static void main(String[] args) throws IOException {
File file=new File("E:/Books");
if(!file.exists()){//假设目录不存在
file.mkdirs(); //在E盘下面建立Books目录
}
File book1_file=new File(file,"book1.txt");
if(!book1_file.exists()){
book1_file.createNewFile(); //在Books目录下建立book1.txt文件
}
File book2_file=new File(file,"book2.txt");
if(!book2_file.exists()){
book2_file.createNewFile(); //在Books目录下建立book2.txt文件
}
String total_fileName[]=file.list(); //取得Books目录下的所有文件名字
File total_file[]=file.listFiles();
for(String s:total_fileName){
System.out.println(s+" ");
}
for(File f:total_file){
Date date=new Date(f.lastModified());
System.out.println(date+" "+f.length()+"字节"); //打印文件最后修改时间和字节大小
}
}
}
2.字节流(FileInputStream, FileOutputStream, BufferedInputStream, BufferedOutputStream)
举例:给上述的book1.txt写入数据
代码如下:
public class Test {
public static void main(String[] args) throws IOException {
File file=new File("E:/Books/book1.txt");
OutputStream os=new FileOutputStream(file);//以字节流的方式写入文件
//温馨提示:如果想要在文件内容后面追加的话,函数为OutputStream os=new FileOutputStream(file,true)
String s="田浩兵是大帅哥,呸,真不要脸...";
BufferedOutputStream bos=new BufferedOutputStream(os);//提供一个缓冲区,以免一直与文件建立链接,效率低
bos.write(s.getBytes());
bos.close();//关闭流的顺序很重要!!!,先关闭缓冲区,然后再关闭输出流
os.close();
}
}
接下里,我们从book1.txt中按照字节流的方式读出来试试:
代码如下:
public class Test {
public static void main(String[] args) throws IOException {
File file=new File("E:/Books/book1.txt");
InputStream is=new FileInputStream(file);
BufferedInputStream bis=new BufferedInputStream(is);//缓冲区
int len;
while((len=bis.read())!=-1){
System.out.print((char)len);
}
bis.close(); // 关闭流,还是那句话,顺序很重要!!!
is.close();
}
}
输出结果:
(Fuck!!!,怎么是乱码?田浩兵大帅哥去哪里了呢?(害羞….))
原因解释:在文本中,中文占用两个字节,字符占用一个字节,因为我们是一个字节一个字节的读出来的,所以读到一个中文时,比如读到田,只能读田的一半,当转换为字符时,当然是乱码了。那怎么解决呢?
代码如下:
public class Test {
public static void main(String[] args) throws IOException {
File file=new File("E:/Books/book1.txt");
InputStream is=new FileInputStream(file);
BufferedInputStream bis=new BufferedInputStream(is);//缓冲区
int len1,len2;
while((len1=bis.read())!=-1){
if(!(len1>=0&&len1<128)){ //通过ASCII来判断是否为中文
len2=bis.read();
System.out.print(new String(new byte[]{(byte)len1,(byte)len2})); //将len1和len2拼接起来,组成一个byte数组,因为分别读了一半
}else{
System.out.print((char)len1); //转换为字符输出
}
}
bis.close(); //关闭流,还是那句话,顺序很重要!!!
is.close();
}
}
输出结果:
(这不,大帅哥又出来了…)
3.字符流(InputStreamReader , BufferReader, OutputStreamWriter , BufferedWriter)
我们往book1.txt追求一句话(田浩兵真的是大帅哥 )
代码如下:
public class Test {
public static void main(String[] args) throws IOException {
File file=new File("E:/Books/book1.txt");
FileWriter fw=new FileWriter(file,true);//表示往文本后面追加,而不是覆盖
BufferedWriter bw=new BufferedWriter(fw);//以字符流的方式写入
String name="田浩兵真的是大帅哥";
bw.write(name);
bw.close();
fw.close();
}
}
执行结果:
(田浩兵真的是大帅哥就出来了)
接下来,我们从这个文本以字符流的方式读出来试试:
代码如下:
public class Test {
public static void main(String[] args) throws IOException {
File file=new File("E:/Books/book1.txt");
FileReader fw=new FileReader(file);
BufferedReader bw=new BufferedReader(fw);
String s;
while((s=bw.readLine())!=null){//以字符流的方式读文本,一次不仅仅读取一个字节,可以一行一行的读取
System.out.print(s);
}
bw.close();
fw.close();
}
}
执行结果:
4.最后,还有两个函数,分别为InputStreamReader和OutputStreamWriterr,这两个函数是字节流转换为字符流的桥梁。我们下面来用代码讲解:
public class Test {
public static void main(String[] args) throws IOException {
File file=new File("E:/Books/book1.txt");
OutputStream is=new FileOutputStream(file,true);
String name =" 字节流转换为字符流";
is.write(name.getBytes());
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(is));//字节流转为字符流,然后增加一个缓冲区
bw.close();
is.close();
}
}
运行结果: