常用Java IO 上
位置:Java操作流的对象都在IO(java.io)包中。
分类:
按操作数据:可分为字节流和字符流。
按流向:可分为输入流和输出流。
字节流的抽象基类:InputStream、OutputStream.
字符流的抽象基类:Reader、Writer.
命名规则:由以上四个类派生出来的子类名称都是以其父类名称作为子类名的后缀。
如:FileInputStream.
首先从字符流开始介绍。
FileWriter类:此类用来写入字符文件的类。比如在记事本中存入一些文字。示例代码如下:
public class FileWriterDemo {
public static void main(String[] args) throws IOException {
FileWriterfw = new FileWriter("demo.txt"); fw.write("hello"); // 刷新缓冲区 fw.append("world"); fw.close(); } } |
以上代码执行过程是这样的:
首先是在项目所以文件路径新建一个名为“demo.txt”的文件,如果这个文件已经存在则会替换掉。接着向缓冲区(内存中的某个地方)写入字符串“hello”。接着刷新缓冲区,也就是把“hello”字符串写入到“demo.txt”中(底层依然是一个字符接一个来写的)。再接着就是把“world”字符串写到缓冲区中,接着调用关闭函数把流关闭,但是,在关闭函数内部,是先调用了刷新函数来刷新缓冲区的,这样第二条数据在没有手动刷新的情况下也能写入到“demo.txt”当中。当然它是写在“hello”后面的,并且没有换行。
正规写法应该try-catch一下:
public static void main(String[] args) {
FileWriter fw = null;
try { fw = new FileWriter("demo.txt"); fw.write("hello"); fw.append("world"); fw.flush(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (fw != null) fw.close(); } catch (IOException e) { e.printStackTrace(); } } } |
FileReader类:用来读取字符文件的类。比如读记事本文件中已经存在的数据。示例代码如下:
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("demo.txt");
int num = 0; while ((num = fr.read()) != -1) {
System.out.println((char)num);
}
fr.close();
} |
以上代码含义如下:
首先实例化一个FileReader对象并让它与“demo.txt”文件相关联。接着定义一个整型变量来接受读取到的字符所对应的数字,一次读取一个,read()方法是阻塞式的,会一直读到末尾返回-1,然后输入每个字符(需要强制转换)。最后关闭流。
利用FileWriter类和FileReader类简单实现字符文件复制。
示例代码如下:
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("Compare_copy.java"); FileReader fr = new FileReader("Compare.java");
int ch = 0; while ((ch = fr.read()) != -1) { fw.write((char)ch); }
fw.close(); fr.close(); } |
以上过程与前面介绍FileWriter类和FileReader类时唯一的区别就是在输出的时候改变了输出的指向,这里是输出到另一个文件中。如果想提高效率可以自定义一个字符数组来缓存数据,然后一次性的将数据写入文件。当然这里规范写法依然是需要手动try-catch的。
BufferedWriter类:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入的类。包裹需要提高效率的输出流。示例代码如下:
public class BufferedWriterDemo {
public static void main(String[] args) {
FileWriter fw = null; BufferedWriter bw = null;
try { fw = new FileWriter("demo.txt"); bw = new BufferedWriter(fw);
for (int i = 0; i < 5; i ++) { bw.write("abcde" + i); bw.newLine(); bw.flush(); }
} catch (IOException e) { System.out.println(e); } finally { if (bw != null) try { bw.close(); } catch (IOException e) { System.out.println(e); } } }
} |
用法相当简单,和前面介绍的类差不多。
BufferedReader类:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取的类。示例代码如下:
public class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("demo.txt"); BufferedReader br = new BufferedReader(fr);
String str = null;
while ((str = br.readLine()) != null) { System.out.println(str); }
}
} |
用readLine()方法可以实现一次读取一行,当读到末尾后没数据时就会返回null。
利用BufferedWriter类和BufferedReader类实现字符文件复制。示例代码如下:
public class BufferedReaderWriterDemo {
public static void main(String[] args) {
BufferedWriter bw = null; BufferedReader br = null;
try { br = new BufferedReader(new FileReader("Compare.java")); bw = new BufferedWriter(new FileWriter("BufferedCopy.txt"));
String line = null; while ((line = br.readLine()) != null) { bw.write(line); bw.newLine(); bw.flush(); } } catch (IOException e) { System.out.println(e); } finally { if (bw != null) { try { bw.close(); } catch (IOException e) { throw new RuntimeException("写入缓冲流关闭失败"); } } if (br != null) { try { br.close(); } catch (IOException e) { throw new RuntimeException("读取流关闭失败"); } } } }
} |
LineNumberReader类:跟踪行号的缓冲字符输入流。此类定义了方法setLineNumber(int)
和getLineNumber()
,它们可分别用于设置和获取当前行号。示例代码如下:
// 带行号的读取器 public class LineNumberReaderDemo {
public static void main(String[] args) throws IOException { FileReader fr = new FileReader("compare.java"); LineNumberReader lnr = new LineNumberReader(fr);
String line = null; lnr.setLineNumber(100); while ((line = lnr.readLine()) != null) { System.out.println(lnr.getLineNumber() + ":" + line); } } } |
用这个类输出的东西可以带上行号,除了这个以后,没别的特殊了,只是增加了这个功能,利用装饰设计模式,将功能不那么多或者强的类包装一下,增加一些,仅此而已。如果不设置它的行号,则从1开始,如代码中的设置了行号为100,则从101开始。
自定义缓冲字符流MyBufferedReader类:
public class MyBufferedReader {
private Reader br;
public MyBufferedReader(Reader br) { this.br = br; }
public String myReadLine() throws IOException { StringBuilder sb = new StringBuilder(); int ch = 0; while ((ch = br.read()) != -1) { if (ch == '\r') { continue; } else if (ch == '\n') { return sb.toString(); } else { sb.append((char)ch); } } if (sb.length() != 0) { return sb.toString(); } return null; }
public void myClose() throws IOException { br.close(); }
public static void main(String[] args) throws IOException { FileReader fr = new FileReader("demo.txt"); MyBufferedReader mbr = new MyBufferedReader(fr);
String ch = null; while ((ch = mbr.myReadLine()) != null) { System.out.println(ch); }
mbr.myClose(); } } |
真正的不是用StringBuilder来存储,而是用数组,这里只是模拟一下。
接下来讲解字节输入输出流。
示例代码如下:
// 字节流操作 public class FileStreamDemo {
public static void readStream2() throws IOException { FileInputStream fis = new FileInputStream("fileout.txt"); byte[] buf = new byte[fis.available()]; fis.read(buf); System.out.println(new String(buf)); }
// 文件输入字节流读取文件内容 public static void readStream1() throws IOException { FileInputStream fis = new FileInputStream("fileout.txt"); int ch = 0; while ((ch = fis.read()) != -1) { System.out.print((char)ch); } }
// 文件输入字节流读取文件内容 public static void readStream() throws IOException { FileInputStream fis = new FileInputStream("fileout.txt"); int len = fis.read(buf); System.out.println(new String(buf, 0, len)); }
// 输出 public static void outPutStreamTest() throws IOException { FileOutputStream fos = new FileOutputStream("fileout.txt"); fos.write("abcde".getBytes()); fos.close(); }
public static void main(String[] args) throws IOException { //outPutStreamTest(); //readStream(); //readStream1(); readStream2(); } } |
字节流和字符的区别不大,顾名思义就知道他们操作字符和字节,但是如果你要操作图片类型或其他类型的数据就不能使用字符流,这时就需要使用字节流来处理了。
这句byte[] buf = new byte[fis.available()];的意思是定义刚刚数据大小的空间。这方法虽然好用,但是不适合大数据,你想想如果数据太大你的内存才多大。所以大数据一般自定义一个缓冲byte[] buf = new byte[1024];来接受数据。
利用字节流复制图片,代码如下所示:
// 复制图片 public class CopyPhoto {
public static void main(String[] args) { FileInputStream fis = null; FileOutputStream fos = null;
try { fis = new FileInputStream("c:\\learnPath.jpg"); fos = new FileOutputStream("c:\\1.jpg");
byte[] buf = new byte[1024]; int len = 0;
while ((len = fis.read(buf)) != -1) { fos.write(buf, 0, len); } } catch(IOException e) { e.printStackTrace(); } finally { try { if (fis != null) fis.close(); } catch (IOException e){ e.printStackTrace(); }
try { if (fos != null) { fos.close(); } } catch (IOException e) { e.printStackTrace(); } } } } |
自定义缓冲字节输入流MyBufferedInputStream类:
public class MyBufferedInputStream {
private InputStream in; private byte[] buf = new byte[1024*4]; private int pos, count;
public MyBufferedInputStream(InputStream in) { this.in = in; }
// 一次读一个字节,从缓冲区(字节数组)获取 public int myRead() throws IOException { // 通过in对象读取数据到buf中 if (count == 0) {
count = in.read(buf); if (count < 0) { return -1; } pos = 0; byte b = buf[pos]; count--; pos++; return b&255; } else if (count > 0) { //大于0说明里面还有数据,取就行了,不用存 byte b = buf[pos]; count--; pos++; return b&0xff; } return -1;
}
public void myClose() throws IOException { in.close(); } } |
利用上面自定义的缓冲字节输入流实现复制一个MP3文件。代码如下:
public class CopyMp3 {
public static void copy1() throws IOException { //BufferedInputStream fis = new BufferedInputStream(new FileInputStream("c:\\0.mp3")); MyBufferedInputStream fis = new MyBufferedInputStream(new FileInputStream("c:\\0.mp3")); BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream("c:\\1.mp3"));
int len = 0; while ((len = fis.myRead()) != -1) { fos.write(len); }
fis.myClose(); fos.close(); }
public static void main(String[] args) throws IOException { long start = System.currentTimeMillis(); copy1(); long end = System.currentTimeMillis(); System.out.println((end - start) + "毫秒"); } } |
MyBufferedInputStream类中之所以让b&255就是为了避免无法复制因为开头读到连续8个1的二进制数就是-1,然后就结束了。
从键盘读取数据并输出:
public class SysteminDemo {
public static void main(String[] args) throws IOException { InputStream in = System.in;
int i = 0; StringBuilder sb = new StringBuilder(); while (true) { i = in.read();
if (i == '\r') continue; if (i == '\n') { if ("over".equals(sb.toString())) { break; } System.out.println(sb.toString()); sb.delete(0, sb.length()); } else { sb.append((char)i); }
}
} } |
转换流:
// 字节转换为字符流 public class InputStreamReaderDemo {
public static void main(String[] args) throws IOException { InputStreamReader isr = new InputStreamReader(System.in); // 提高效率用缓冲字符流包装 BufferedReader br = new BufferedReader(isr);
String str = null; while ((str = br.readLine()) != null) { if ("over".equals(str)) break; System.out.println(str); }
} } |
字符流转换成字节流:
// 将字符流转换成字节流 public class OutputStreamWriterDemo {
public static void main(String[] args) throws InterruptedException, IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
OutputStream os = System.out; OutputStreamWriter osw = new OutputStreamWriter(os); BufferedWriter bw = new BufferedWriter(osw);
String line = null;
while ((line = br.readLine()) != null) { if ("over".equals(line)) break; bw.write(line); bw.newLine(); //里面有缓冲区,所以要刷新 bw.flush(); }
br.close(); bw.close(); } } |
字符流都有它默认的编码,可以手动指定。
但凡键盘录入,最常见的写法:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
流操作的基本规律:
通过两个明确来知道该选用哪个流对象:
1、 明确来源和目的
源:输入流用 InputStream、Reader
目的:输出流用 OutputStream、Writer
2、 操作的数据是否是纯文本
是:字符流
不是:字节流
3、 当体系明确后,再明确要使用哪个具体的对象。
通过设备来进行区分:
源设备:内存、硬盘、键盘。
目的设备:内存、硬盘、控制台。
利用以上操作的基本规律,示例如下:
1、 要求:将一个文本文件中数据存储到另一个文件中。就是复制。
分析如下:
源:因为是源,所以用读取流。InputStream、Reader
是不是操作文本文件?
是!这时就可以选择 Reader
这样体系就明确了。
接下来明确要使用流体系中的哪个对象。
明确设备:硬盘。上面的一个文件。
Reader体系中可以操作文件的对象:FileReader
FileReader fr = new FileReader(“a.txt”);
是否需要提高效率:是!加入缓冲区。
BufferedReader br = new BufferedReader(fr);
明确目的:OutputStream、Writer
是否纯文本?
是!用Writer
设备:硬盘。上的一个文件。
Writer体系中可以操作文件的对象:FileWriter
FileWriter fw = new FileWriter(“b.txt”);
是否需要提高效率?是!加入缓冲区。
BufferedWriter bw = new BufferedWriter(fw);
利用以上所说规律和实例分析,可以很快确定自己应该使用哪些流对象。
未完待续。。。。。。