《Java从入门到精通》第十二章学习笔记
第12章 输入和输出
一、流概述
流是指一组有序的数据序列,数据源和输出目标可以是文件,网络,压缩包或其他。
二、输入/输出流
所有输入流都是抽象类InputStream(字节输入流)或抽象类Reader(字符输入流)的子类;所有输出流都是抽象类OutputStream(字节输出流)和抽象类Writer(字符输出流)的子类。
1、输入流
InputStream是所有字节输入流的父类,该类主要方法如下,方法出现错误时候会引发IOException异常。
(1)read():从输入流中读取数据的下一个字节,返回0~255之间的int字节值,如果已到末尾,返回-1.
(2)read(byte[] b):从输入流中读入一定长度的字节,并以整数形式返回字节数。
(3)close():关闭此输入流并释放与该数据流关联的所有系统资源。
Java中的字符是Unicode编码,为双字节,而InputStream类处理的是字节流,一般在Java中用Reader类处理字符文本。
2、输出流
OutputStream是所有字节输出流的父类,主要方法如下:
(1)write(int b):将指定的字节写入此输出流。
(2)write(byte[] b):将b.length个字节从指定的byte数组写入流
(3)close():关闭输出流。
三、File类
1、文件的创建与删除
(1)File(String pathname):参数作为路径名创建一个新File实例。
(2)File(String parent,String child):参数分别作为父路径和子路径,创建一个新File实例。
(3)File(File f,String child):f作为父路径对象,child作为子路径字符串。
1 //项目中创建FileTest类,判断D盘的mywork文件夹是否存在work.txt 2 //如果存在则删除,如果不存在则创建 3 import java.io.File; 4 public class FileTest { 5 6 public static void main(String[] args) { 7 File file=new File("D:/myword","test.txt");//创建文件对象 8 if(file.exists()){ 9 file.delete(); 10 System.out.println("文件已删除"); 11 }else{ 12 try{ 13 file.createNewFile(); 14 System.out.println("文件已创建"); 15 }catch (Exception e){ 16 e.printStackTrace(); 17 } 18 } 19 } 20 }
2、获取文件信息
File类获取文件本身信息的方法主要包括:
(1)getName():获取文件的名称。
(2)canRead():判断文件是否可读
(3)canWrite():判断文件是否可写入
(4)exists():判断文件是否存在
(5)length():获取文件以字节为单位的长度
(6)getAbsolutePath():获取文件的绝对路径
(7)getParent():获取文件的父路径
(8)isFile():判断文件是否存在(判断对象是否是一个文件?)
(9)isDirectory():判断对象是否是一个目录
(10)isHidden():判断对象是否是隐藏文件
(11)lastMOdified():获取文件最后修改时间
import java.io.File; public class FileTest { public static void main(String[] args) { File file=new File("D:/myword","test.txt");//创建文件对象 if(file.exists()){ String name=file.getName(); //获取文件名称 long length=file.length(); //获取文件长度 boolean hidden=file.isHidden(); //判断文件是否隐藏 System.out.println("文件名称为:"+name); System.out.println("文件长度为:"+length); System.out.println("文件是隐藏文件吗?"+hidden); }else{ System.out.println("文件不存在!"); }
} }
//通过renameTo()重命名文件 import java.io.File; public class FileTest { public static void main(String[] args) { File file=new File("D:/myword","test.txt");//创建文件对象 if(file.exists()){ System.out.println("文件名称为:"+file.getName());//获取文件名称 System.out.println("文件长度为:"+file.length());//获取文件长度 System.out.println("文件是隐藏文件吗?"+file.isHidden());//判断文件是否隐藏 }else{ System.out.println("文件不存在!"); } File file1=new File("D:/myword","testhaha.txt"); if(file1.exists()){ System.out.println("已存在"+file1.getName()+"文件,无法重命名!!"); }else{ file.renameTo(file1); System.out.println(file.getName()+"文件名称已修改为"+file1.getName()); } } }
四、文件输入、输出流
1、如果需要将数据永久保存,可使用FileInputStream和FileOutputStream类与文件建立连接,将需要的数据永久保存到文件里。
FileInputStream是InputStream抽象类的实现类,FileOutputStream是OutputStream抽象类的实现类。
1 //使用FileOutputStream类向文件work写入信息 2 //然后通过FileinputStream类将work文件中的数据读取到控制台中 3 import java.io.*; 4 public class FileTest { 5 public static void main(String[] args){ 6 File file=new File("F:\\...\\STUDY\\lab\\java","work.txt"); //创建文件对象 7 try{ 8 FileOutputStream out=new FileOutputStream(file); //创建FileOutputStream对象 9 byte bx[]=" tslvcj ".getBytes(); //创建字节型数组 10 out.write(bx); //将数组中信息写入文件 11 out.close(); //关闭输出流 12 }catch(Exception e){ 13 e.printStackTrace(); 14 } 15 try{ 16 FileInputStream in=new FileInputStream(file); 17 byte by[]=new byte[1024]; 18 int len=in.read(by); //将文件中字节流数据读取入数组 19 System.out.println("文件中信息是:"+new String(by,0,len)); 20 in.close(); 21 }catch(Exception e){ 22 e.printStackTrace(); 23 } 24 25 } 26 27 }
2、FileInputStream和FileOutputStream是针对字节流文件的类,如果对于非单字节编码的文件,可能会出现乱码,一般对字符流文件采用FileReader和FileWriter类。
1 import java.io.*; 2 public class FileRwTest { 3 4 public static void main(String[] args) { 5 File file=new File("F:\\霜\\STUDY\\lab\\java","work.txt"); 6 try{ 7 FileWriter fwr=new FileWriter(file); 8 fwr.write("ilc!"); 9 fwr.close(); 10 }catch(Exception e){ 11 e.printStackTrace(); 12 } 13 try{ 14 FileReader fr=new FileReader(file); 15 char mystr[]=new char[1024]; 16 int len=fr.read(mystr); 17 System.out.println("文件中信息为:"+new String(mystr,0,len)); 18 fr.close(); 19 }catch(Exception e){ 20 e.printStackTrace(); 21 } 22 System.out.println("文件名称为:"+file.getName()); 23 System.out.println("文件长度为:"+file.length()+"Bytes"); 24 System.out.println("文件是否隐藏:"+file.isHidden()); 25 } 26 }
1 import java.io.*; 2 public class FileTest { 3 //显示某一文件类下所有文件的名称 4 public void showFileName(File file){ 5 if(!file.exists()){ 6 System.out.println("文件不存在!"); 7 }else{ 8 if(file.isFile()){ 9 System.out.println(file.getName()); 10 }else{ 11 File[] fd=file.listFiles(); 12 for(File fx:fd){ 13 showFileName(fx); 14 } 15 } 16 } 17 } 18 public static void main(String[] args){ 19 FileTest ft=new FileTest(); 20 File file=new File("E:\\lab"); 21 ft.showFileName(file); 22 } 23 }
1 //删除指定文件或文件夹 2 import java.io.*; 3 public class DelFile { 4 public void delF(File file){ 5 if(!file.exists()){ 6 System.out.println("文件不存在!"); 7 }else{ 8 if(file.isFile()){ 9 System.out.println("文件"+file.getName()+"已删除!"); 10 file.delete(); 11 }else{ 12 File[] fa=file.listFiles(); 13 for(File fx:fa){ 14 delF(fx); 15 } 16 17 } 18 } 19 } 20 public static void main(String[] args){ 21 DelFile delTest=new DelFile(); 22 File file=new File("E:\\lab"); 23 delTest.delF(file); 24 } 25 }
五、带缓存的输入、输出流
1、BufferedInputStream类可以对任意InputStream类进行带缓存区的包装以达到性能优化,有两种构造函数:
(1)BufferedInputStream(InputStream in):创建了32个字节的缓存流;
(2)BufferedInputStream(InputStream in,int size):按指定大小创建缓存流。
2、BufferedOutputStream类输出信息和向OutputStream类输入信息完全一样,只不过BufferedOutputStream通过flush()方法强制将缓存区的数据输出结束。其有两种构造方法:
(1)BufferedOutputStream(OutputStream in):创建一个32个字节的缓存区;
(2)BufferedOutputStream(OutputStream in,int size):按指定大小创建缓存流。
3、BufferedReader类与BufferedWriter类分别继承Reader类和Writer类
*BufferedReader类常用方法如下:
(1)read():读取单个字符;
(2)readLine():读取一个文本行,并将其返回为字符串;若无数据,返回null;
(3)writ(String s,int off,int len):写入字符串的某一部分;
(4)flush():刷新该流的缓存。
补充:
一、RandomAccessFile
是java提供的对内容的访问类,可读写操作,顾名思义也可以随机访问文件(即访问文件的任意位置)。
1、Java文件模型:对于Java而言,在硬盘上的文件是byte-byte-byte存储的,是字节数据的集合
2、访问有两种模式:"rw"(读写)、"r"(只读)
RandomAccessFile raf=new RandomAccessFile(file,"rw");
因为随机访问,所以RandomAccessFile类内部含有一个文件指针,第一次打开文件时候指针指向文件开头即pointer=0;随着操作指针会后移。
3、写方法
raf.write(int):只写一个字节(int的后8位),同时指针指向下一个字节位置
4、读取文件
int b=raf.read():读取指针所指的一个字节
5、文件操作完成后一定要关闭
raf.close();
1 import java.io.File; 2 import java.io.RandomAccessFile; 3 import java.util.Arrays; 4 5 6 public class RafDemo { 7 public static void main(String[] args) throws Exception{ 8 //创建文件所在目录对应的文件对象 9 File demo=new File("demo"); 10 if(!demo.exists()){ 11 demo.mkdir(); 12 } 13 //创建文件对象 14 File file=new File(demo,"raf.dat"); 15 if(!file.exists()){ 16 file.createNewFile(); 17 } 18 //创建针对上面文件的文件访问类 19 RandomAccessFile raf=new RandomAccessFile(file,"rw"); 20 //看一下指针的位置 21 System.out.println(raf.getFilePointer()); 22 23 raf.write('A');//只写一个字节,A的后8位(低8位) 24 System.out.println(raf.getFilePointer()); 25 raf.write('B'); 26 27 //定义一个整数,写入文件 28 //但每次只能写一个字节 29 //只能通过移位,分别写4词 30 int i=0x7fffffff; 31 raf.write(i>>>24);//先写高8位,因为write写的是低8位,所以先右移24位, 32 raf.write(i>>>16); 33 raf.write(i>>>8); 34 raf.write(i); 35 //前面写了6个字节,此时指针指向6 36 System.out.println(raf.getFilePointer()); 37 38 //可以直接写int型数据 39 raf.writeInt(i); 40 41 System.out.println(raf.getFilePointer()); 42 43 String s="中"; 44 byte[] gbk=s.getBytes("gbk"); 45 raf.write(gbk); 46 System.out.println(raf.length()); 47 48 //读文件必须先把指针移动到头部 49 raf.seek(0); 50 //一次性读取到字符数组 51 byte[] buf=new byte[(int)raf.length()]; 52 raf.read(buf); 53 54 System.out.println(Arrays.toString(buf)); 55 for (byte b : buf) { 56 System.out.print(Integer.toHexString(b&0xff)+" "); 57 } 58 //字符数组构造成字符串 59 String s1=new String(buf); 60 System.out.println(s1); 61 raf.close(); 62 } 63 64 }
二、IO流
分为字节流和字符流
1、字节流
(1)InputStream\OutputStream
InputStream抽象了应用程序读取数据的方式;OutputStream抽象了应用程序写出数据的方式;
(2)达到文件结尾,即EOF=End 读到-1就是读到结尾
(3)输入流基本方法(一般即读出的操作)
int b=in.read();读取一个字节无符号填充到整型b的低8位.读到-1即结尾
in.read(byte[] buf):读取数据填充到字节数组buf中
in.read(byte[] buf,int start,int size):从buf的start位置开始读取size长度的数据到字节数组buf中
(4)输出流基本方法(一般即写入的操作)
out.write(int b):只写一个byte(b的低八位)到out流
out.write(byte[] buf):将buf字节数组写入流
out.write(byte[] buf,int start,int size)
(5)InputStream\OutputStream的子类:FileInputStream和FileOutputStream
FileInputStream具体实现了在文件上读取数据
FileOutputStream具体实现了在文件上写数据
1 import java.io.File; 2 import java.io.IOException; 3 import java.io.*; 4 5 6 public class IOUtil { 7 /* 8 * 读取指定文件内容,按照16进制输出到控制台 9 * 并且每输出10个byte换行 10 */ 11 12 public static void printHex(String fileName) throws Exception{ 13 //把文件作为字节流进行读操作 14 FileInputStream in=new FileInputStream(fileName); 15 int b; 16 int i=1; 17 while((b=in.read())!=-1){ 18 if(b<=0xf){ 19 System.out.print("0"); 20 } 21 System.out.print(Integer.toHexString(b)+" "); 22 if(i++%10==0){ 23 System.out.println(); 24 } 25 } 26 System.out.println(); 27 System.out.println("文件一共有"+i+"个字节!"); 28 in.close(); 29 } 30 31 } 32 33 public class IOUtilTest1 { 34 35 public static void main(String[] args) { 36 try { 37 IOUtil.printHex("F:\\IOUtil.java"); 38 } catch (Exception e) { 39 e.printStackTrace(); 40 } 41 } 42 43 }
上面程序中用in.read()通过单字节读取文件,对于较大的文件,一般用in.read(byte[] buf,int start,int size)进行批量读取以节约时间。
针对上面的程序,优化如下:
1 public static void printHexByByteArray(String fileName)throws Exception{ 2 FileInputStream in=new FileInputStream(fileName); 3 byte[] buf=new byte[20*1024]; 4 //从in中批量读取字节,放入到buf字节数组中,从第0个位置开始放置,最多放buf.length个字节;返回为读取的个数 5 int bytes=in.read(buf,0,buf.length); 6 int j=1; 7 for (int i=0;i<bytes;i++) { 8 if(buf[i]<0xf){ 9 System.out.print("0"); 10 } 11 System.out.print(Integer.toHexString(buf[i])+" "); 12 if(j++%10==0){ 13 System.out.println(); 14 } 15 } 16 in.close(); 17 18 }
利用FileOutputStream写文件,并完成文件的复制功能。
1 import java.io.FileInputStream; 2 import java.io.FileOutputStream; 3 import java.io.IOException; 4 import java.io.File; 5 public class FileOutDemo1 { 6 public static void copyFile(File srcFile,File destFile)throws IOException{ 7 if(!srcFile.exists()){ 8 throw new IllegalArgumentException("文件不存在!"); 9 } 10 if(!srcFile.isFile()){ 11 throw new IllegalAccessError(srcFile+"不是文件"); 12 } 13 FileInputStream in=new FileInputStream(srcFile); 14 FileOutputStream out=new FileOutputStream(destFile); 15 byte[] buf=new byte[8*1024]; 16 int b; 17 while((b=in.read(buf,0,buf.length))!=-1){ 18 out.write(buf,0,b); 19 out.flush();//刷新缓冲区 20 } 21 in.close(); 22 out.close(); 23 24 } 25 public static void main(String[] args)throws IOException{ 26 File demo=new File("demo"); 27 if(!demo.exists()){ 28 demo.mkdir(); 29 } 30 //如果该文件不存在,则直接创建,如果存在删除后创建 31 FileOutputStream out=new FileOutputStream("demo/out.txt"); 32 out.write('a'); 33 out.write('b'); 34 byte[] buf="中国".getBytes("gbk"); 35 out.write(buf); 36 File srcFile=new File("demo/out.txt"); 37 File outCopy=new File("demo/outCopy.txt"); 38 copyFile(srcFile,outCopy); 39 40 41 } 42 43 }
三、DataOutputStream/DataInputStream是对“流”功能的扩展,可以更方便的读取int,long,字符等类型数据
1 import java.io.*; 2 public class DosDemo { 3 public static void main(String[] args)throws Exception{ 4 String file="demo/dos.txt"; 5 File demo=new File("demo"); 6 if(!demo.exists()){ 7 demo.mkdir(); 8 } 9 //DataOutputStream类的构造函数的参数是FileOutputStream类对象 10 DataOutputStream dos=new DataOutputStream( 11 new FileOutputStream(file)); 12 dos.writeInt(10); 13 dos.writeInt(-10); 14 dos.writeLong(10); 15 dos.writeDouble(10.5); 16 dos.writeUTF("中国"); 17 dos.writeChars("中国"); 18 dos.close(); 19 DataInputStream dis=new DataInputStream( 20 new FileInputStream(file)); 21 int i=dis.readInt(); 22 System.out.println(i); 23 i=dis.readInt(); 24 System.out.println(i); 25 long l=dis.readLong(); 26 System.out.println(l); 27 double d=dis.readDouble(); 28 System.out.println(d); 29 String s=dis.readUTF(); 30 System.out.println(s); 31 dis.close(); 32 } 33 }
4、BufferedInputStream/BufferedOutputStream:为IO提供了带缓冲区的操作,一般打开文件进行写入或读取操作时,都会加上缓冲,这种流模式提高了IO的性能。
比喻:从应用程序中把数据输出到文件,相当于把一缸水倒入外面的一个水箱
那么:
FileOutputStream类的write()方法相当于一滴一滴倒入;
DataOutputStream类的writeXxx()方法(包括writeInt,writeLong,writeUTF等)相当于一瓢一瓢地倒入;
而BufferOutputStream类的write()方法相当于先一瓢一瓢倒入桶中,再从桶中倒入水箱。
下面是通过不同IO流类的复制文件方法所耗时的对比:
/*利用带缓冲的字节流类 * 进行文件的复制 */ import java.io.*; public class CopyFile { //Buffered流操作 public static void copyFileByBuffer(File srcFile,File destFile)throws Exception{ if(!srcFile.exists()){ throw new IllegalArgumentException("文件不存在!"); } if(!srcFile.isFile()){ throw new IllegalAccessError(srcFile+"不是文件"); } BufferedInputStream bis=new BufferedInputStream( new FileInputStream(srcFile)); BufferedOutputStream bos=new BufferedOutputStream( new FileOutputStream(destFile)); int c; while((c=bis.read())!=-1){ bos.write(c); bos.flush(); } bis.close(); bos.close(); } //单字节流操作 public static void copyFileByByte(File srcFile,File destFile)throws Exception{ if(!srcFile.exists()){ throw new IllegalArgumentException("文件不存在!"); } if(!srcFile.isFile()){ throw new IllegalAccessError(srcFile+"不是文件"); } FileInputStream bis=new FileInputStream(srcFile); FileOutputStream bos=new FileOutputStream(destFile); int c; while((c=bis.read())!=-1){ bos.write(c); } bis.close(); bos.close(); } //字节批量操作 public static void copyFileByArray(File srcFile,File destFile)throws Exception{ if(!srcFile.exists()){ throw new IllegalArgumentException("文件不存在!"); } if(!srcFile.isFile()){ throw new IllegalAccessError(srcFile+"不是文件"); } FileInputStream bis=new FileInputStream(srcFile); FileOutputStream bos=new FileOutputStream(destFile); int c; byte[] ba=new byte[2024*1024]; while((c=bis.read(ba,0,ba.length))!=-1){ bos.write(ba,0,ba.length); } bis.close(); bos.close(); } //字节流操作 public static void copyFileByData(File srcFile,File destFile)throws Exception{ if(!srcFile.exists()){ throw new IllegalArgumentException("文件不存在!"); } if(!srcFile.isFile()){ throw new IllegalAccessError(srcFile+"不是文件"); } DataInputStream bis=new DataInputStream( new FileInputStream(srcFile)); DataOutputStream bos=new DataOutputStream( new FileOutputStream(destFile)); int c; while(bis.available()>0&&(c=bis.read())!=-1){ bos.write(c); } bis.close(); bos.close(); } } import java.io.File; public class copyTest { public static void main(String[] args)throws Exception { File demo=new File("demo"); if(!demo.exists()){ demo.mkdir(); } File srcFile=new File("demo/but.wma"); File destFile=new File("demo/c1.wma"); File destFile1=new File("demo/c2.wma"); File destFile2=new File("demo/c3.wma"); File destFile3=new File("demo/c4.wma"); long start=System.currentTimeMillis(); CopyFile.copyFileByBuffer(srcFile, destFile); long end=System.currentTimeMillis(); System.out.println("BufferedCopy用了"+(double)(end-start)/1000+"秒!"); start=System.currentTimeMillis(); CopyFile.copyFileByData(srcFile, destFile1); end=System.currentTimeMillis(); System.out.println("DataCopy用了"+(double)(end-start)/1000+"秒!"); start=System.currentTimeMillis(); CopyFile.copyFileByByte(srcFile, destFile2); end=System.currentTimeMillis(); System.out.println("ByteCopy用了"+(double)(end-start)/1000+"秒!"); start=System.currentTimeMillis(); CopyFile.copyFileByArray(srcFile, destFile3); end=System.currentTimeMillis(); System.out.println("ArrayCopy用了"+(double)(end-start)/1000+"秒!"); } } /*可以看出批量操作的复制时间最短 * 其次是带缓冲流 * Data流包装类和单字节读取时间最慢 * 但也可以看出批量操作的时间比带缓冲流节约时间效果非常明显 */
5、字符流:一次处理一个字符,字符的底层仍然是基本的字节序列。一般操作的都是文本和文本文件。
1、InputStreamRead:完成byte流解析为char流,按照编码解析。
OutputStreamWriter:提供char流到byte流,按照编码解析。
1 import java.io.*; 2 3 4 public class IsrAndOswDemo { 5 public static void main(String[] args)throws IOException { 6 //先创建FileInputStream对象in,用做创建InputStreamReader类对象的构造函数的参数 7 FileInputStream in=new FileInputStream("H:\\javalab\\1.txt"); 8 InputStreamReader isr=new InputStreamReader(in);//参数不加编码类型则采用项目默认的编码 9 10 FileOutputStream out=new FileOutputStream("H:\\javalab\\2.txt"); 11 OutputStreamWriter osw=new OutputStreamWriter(out); 12 //单字符读取写入 13 int c; 14 while((c=isr.read())!=-1){ 15 System.out.print((char)c); 16 osw.write((char)c); 17 osw.flush(); 18 } 19 //批量读取,放入buff字符数组,从0开始放,最多放buff.length个字符,返回的是读到的字符个数 20 /*char[] buff=new char[8*1024]; 21 int c; 22 while((c=isr.read(buff,0,buff.length))!=-1){ 23 String s=new String(buff,0,c); 24 System.out.println(s); 25 osw.write(buff,0,c); 26 osw.flush(); 27 }*/ 28 in.close(); 29 isr.close(); 30 } 31 }
2、FileReader/FileWriter:比InputStreamReader/OutputStreamWriter操作要方便;
可以直接对文件进行字符流构造
1 import java.io.*; 2 public class FrAndFwDemo { 3 4 public static void main(String[] args)throws IOException { 5 FileReader fr=new FileReader("H:\\javalab\\3.txt"); 6 FileWriter fw=new FileWriter("H:\\javalab\\4.txt",true); 7 char[] buff=new char[2*1024]; 8 int c; 9 while((c=fr.read(buff,0,buff.length))!=-1){ 10 String s=new String(buff,0,c); 11 System.out.println(s); 12 fw.write(buff,0,c); 13 fw.flush(); 14 } 15 fr.close(); 16 fw.close(); 17 } 18 }
3、字符流的过滤器:BufferedReader最强大的功能是readLine方法,一次读一行;
BufferedWriter/PrintWriter:写操作。
1 import java.io.*; 2 public class BrAndBwAndPwDemo { 3 public static void main(String[] args)throws IOException{ 4 //对文件进行读写操作 5 //Buffered过滤文件流构造比较方法 6 BufferedReader br=new BufferedReader( 7 new InputStreamReader( 8 new FileInputStream("H:\\javalab\\4.txt"))); 9 /*BufferedWriter bw=new BufferedWriter( 10 new OutputStreamWriter( 11 new FileOutputStream("H:\\javalab\\5.txt"))); 12 String line; 13 while((line=br.readLine())!=null){ 14 System.out.println(line);//一次读一行并不能识别换行 15 bw.write(line);//没法单独写一行 16 bw.newLine();//单独写换行操作 17 bw.flush(); 18 } 19 */ 20 //PrintWriter的构造比较简单 21 PrintWriter pw=new PrintWriter("H:\\javalab\\5.txt"); 22 //PrintWriter pw1=new PrintWriter("H:\\javalab\\5.txt",true);//自动带清空缓冲区功能 23 String line; 24 while((line=br.readLine())!=null){ 25 //pw.print(line); 26 pw.println(line);//换行用println() 27 pw.flush(); 28 } 29 br.close(); 30 pw.close(); 31 32 33 34 } 35 36 }