------
Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
IO流
一、概述
IO(Input Output)流
1、IO流用来处理设备之间的数据传输
Java对数据的操作是通过流的方式,Java用于操作流的对象都在IO包中。
流按操作数据分为两种:字节流和字符流。
流按流向分为:输入流,输出流。
2、IO流常用基类
字节流两个基类:
InputStream OutputStream
字符流两个基类:
Reader Writer
注:由这四个类派生出来的子类名称都是以其父类作为子类名的后缀。
如:InputStream的子类FileInputStream
Reader的子类FileReader
二、字符流
既然IO流是用于操作数据的,那么数据的最常见体现形式是:文件。
那么先以操作文件为主要演示。通过演示来了解字符流的基本操作。
示例:
/*
需求:在硬盘上,创建一个文件并写入一些文字数据。 找到一个专门用于操作文件的Writer子类对象,FileWriter。后缀名是父类名。前缀名是该流对象的功能。
*/ import java.io.*; class FileWriterDemo { public static void main(String[] args) throws IOException { //创建一个FileWriter对象。该对象一被初始化就必须要明确被操作的文件。而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。 //其实该步就是在明确数据要存放的目的地。 FileWriter fw = new FileWriter("Demo.txt"); //调用write方法将字符串写入到流中 fw.write("abcde"); //刷新流对象中的缓冲中的数据。将数据刷到目的地中。 //fw.flush(); //close关闭流资源,但关闭之前会刷新一次内部的缓冲数据。将数据刷到目的地中。 <span style="white-space:pre"> </span>//**>和flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。 fw.close(); //此次已经不能再写入数据,流已关闭 //fw.write("hahah"); //fw.flush(); } }
1、IO异常的处理方式
import java.io.*; class FileWriterDemo2 { public static void main(String[] args) { <span style="white-space:pre"> </span>//在外部建立引用,在try内建立对象 FileWriter fw =null; try { fw = new FileWriter("Demo.txt"); fw.write("abcdef"); } catch(IOException e) { System.out.println("catch:"+e.toString()); } finally {
<span style="white-space:pre"> </span>//关闭流也会出现异常,也要处理 try { <span style="white-space:pre"> </span>//如果对象创建失败,fw为空,则close没有意义,抛出NullPointExcepton空指针异常 if(fw!=null)//必须做判断 fw.close(); } catch(IOException e) { System.out.println(e.toString()); } } } }
2、对文件的续写
/* 演示对已有文件的数据续写。 */ import java.io.*; class FileWriterDemo3 { public static void main(String[] args) throws IOException { //传递一个true参数,代表不覆盖已有的文件,并在已有文件的末尾处进行数据续写 FileWriter fw = new FileWriter("Demo.txt",true); fw.write("nihao\r\nxiexie"); fw.close(); /* FileWriter fw =null; try { fw = new FileWriter("Demo.txt",true); fw.write("hahah"); } catch(IOException e) { System.out.println("catch:"+e.toString()); } finally { try { if(fw!=null) fw.close(); } catch(IOException e) { System.out.println("catch:"+e.toString()); } } */ } }
3、读取文件
/* 第一种方式 */ import java.io.*; class FileReaderDemo { public static void main(String[] args) throws IOException { //创建一个文件读取流对象,和指定名称的文件相关联。 //要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException FileReader fr = new FileReader("Demo.txt"); //调用读取流对象的read方法。 //read():一次读一个字符,而且会自动往下读。如果已到达流的末尾,则返回 -1 int ch = 0; while((ch=fr.read())!=-1) { System.out.println("ch:"+(char)ch); } /* while(true) { int ch = fr.read(); if(ch==-1) break; System.out.println("ch:"+(char)ch); } */ /* int ch1 = fr.read(); System.out.println("ch1:"+(char)ch1); int ch2 = fr.read(); System.out.println("ch2:"+(char)ch2); */ fr.close(); } }
/* 第二种方式:通过字符数组进行读取。 */ import java.io.*; class FileReaderDemo2 { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("Demo.txt"); //定义一个字符数组,用于存储读到的字符。 //该read方法返回的是读到字符个数。 char[] buf = new char[1024]; int num =0; while((num=fr.read(buf))!=-1) { System.out.println("num="+num+"..."+new String(buf,0,num)); } /* 如果定义的数组长度小于所要读取数据的字符长度read方法会继续向下读取,并覆盖原有数组中的数据,读到结尾时,返回-1 System.out.println("num="+num+"..."+new String(buf)); int num1 = fr.read(buf); System.out.println("num1="+num1+"..."+new String(buf)); int num2 = fr.read(buf); System.out.println("num2="+num2+"..."+new String(buf)); */ fr.close(); } }
练习://读取一个.java 文件 ,并打印在控制台上。
import java.io.*; class FileReaderTest { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("DateDemo.java"); char[] buf = new char[1024]; int num = 0; while((num=fr.read(buf))!=-1) { <span style="white-space:pre"> </span>//如果加上ln,数据大于1024时,打印的结果就不连贯 System.out.print(new String(buf,0,num)); } fr.close(); } }
练习://将C盘一个文本文件复制到D盘
/* 复制原理: 其实就是将C盘下的文件数据存储到D盘的一个文件中 步骤; 1.在D盘创建一个文件,用于存储C盘文件中的数据。 2.定义读取流和C盘文件关联 3.通过不断的读写完成数据存储。 4.关闭资源。 */ import java.io.*; class CopyTest { public static void main(String[] args) throws IOException { copy_2(); } public static void copy_2() { FileWriter fw = null; FileReader fr = null; try { fw = new FileWriter("DateDemo_copy.txt"); fr = new FileReader("DateDemo.java"); char[] buf = new char[1024]; int len = 0; while((len=fr.read(buf))!=-1) { fw.write(buf,0,len); } } catch(IOException e) { throw new RuntimeException("读取失败"); } finally { if(fw!=null) try { fw.close(); } catch(IOException e) { System.out.println(e.toString()); } if(fr!=null) try { fr.close(); } catch(IOException e) { System.out.println(e.toString()); } } } //存入到数组中,然后将数组的内容全部写入到写入流中 public static void copy_1() throws IOException { //创建目的地 FileWriter fw = new FileWriter("Runtime_copy.txt"); //与已有文件关联 FileReader fr = new FileReader("RuntimeDemo.java"); char[] ch2 = new char[1024];
int ch = 0;while((ch=fr.read())!=-1){fw.write(ch);}*/fw.close();fr.close();}}<span style="white-space:pre"> </span>int num = 0; while((ch=fr.read())!=-1) { fw.write(ch2); } /*<pre name="code" class="java" style="font-size:18px;">//从C盘读取一个字符,就往D盘写一个字符。
三、字符流的缓冲区
缓冲区的出现提高了对数据的读写效率。
对应类:
BufferedReader
BufferedWriter
缓冲区要结合流才可以使用。在流的基础上对流的功能进行增强。所以在创建缓冲区之前,必须要先有流对象。
缓冲区中提供了一个跨平台的换行符。newLine();
1、字符流写入流缓冲区
import java.io.*; class BufferedWriterDemo { public static void main(String[] args) throws IOException { //创建一个字符写入流对象 FileWriter fw = new FileWriter("buf.txt"); <span style="white-space:pre"> </span>//为了提高字符写入流效率,加入了缓冲技术。 <span style="white-space:pre"> </span>//只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可 BufferedWriter bufw = new BufferedWriter(fw); for(int x=0;x<5;x++) { bufw.write("abcde"+x); bufw.newLine(); bufw.flush(); } //记住:只要用到缓冲区,就要记得刷新。 //bufw.flush(); //其实关闭缓冲区,就是在关闭缓冲区中的流对象。 bufw.close(); } }
2、字符读取流缓存区:
该缓冲区提供一个一次读一行的方法readLine,方便于对文本数据的获取。
当返回null时,读到文件的末尾。
readLine方法返回的时候只返回回车符之前的数据内容,并不返回回车符。
import java.io.*; class BufferedReaderDemo { public static void main(String[] args) throws IOException { <span style="white-space:pre"> </span>//创建一个读取流对象和文件相关联。 FileReader fr = new FileReader("buf.txt"); <span style="white-space:pre"> </span>//为了提高效率,加入缓冲技术,将字符读取流对象作为参数传递给缓冲对象的构造函数 BufferedReader bufr = new BufferedReader(fr); String line = null; while((line=bufr.readLine())!=null) { System.out.println("line:"+line); } /* String s1 = bufr.readLine(); System.out.println("s1:"+s1); String s1 = bufr.readLine(); System.out.println("s1:"+s1); */ bufr.close(); } }
练习:
/* 通过缓冲区复制一个.java 文件。 */ import java.io.*; class CopyTextByBuf { public static void main(String[] args) { BufferedReader bufr = null; BufferedWriter bufw = null; try { bufr = new BufferedReader(new FileReader("BufferedWriterDemo.java")); bufw = new BufferedWriter(new FileWriter("bufWriter_Copy.txt")); String line = null; while((line=bufr.readLine())!=null) { bufw.write(line); bufw.newLine(); bufw.flush(); } } catch(IOException e) { throw new RuntimeException("读取失败"); } finally {
<span style="white-space:pre"> </span>//两个缓冲区要分别进行try关闭 try { if(bufr!=null) bufr.close(); } catch(IOException e) { throw new RuntimeException("读取关闭失败"); } try { if(bufw!=null) bufw.close(); } catch(IOException e) { throw new RuntimeException("写入关闭失败"); } } } }
3、readLine方法的原理
无论是读一行,还是获取读取多个字符,其实最终都是在硬盘上一个一个读取。所以最终使用的还是read方法一次读一个的方法。其实在每一行的末尾还存在两个字符就是:\r\n
当readLine读到这两个字符时,就会返回一个字符串,但是不返回\r\n。之后在调用read方法往下读。。。
练习:自定义一个与readLine功能一致的方法。
/* 明白了BufferedReader类中特有方法readLine的原理后, 可以自定义一个类包含一个功能和readLine一致的方法, 来模拟一下BufferedReader */ import java.io.*; class MyBufferedReader { private FileReader r; MyBufferedReader(FileReader r) { this.r = r; } //可以一次读一行数据的方法 public String myReadLine() throws IOException { <span style="white-space:pre"> </span>//定义一个临时容器。原BufferedReader封装的是字符数组。 <span style="white-space:pre"> </span>//为了演示方便,定义一个StringBuilder容器,因为最终还是要将数据变成字符串。 StringBuilder sb = new StringBuilder(); int ch = 0; while((ch=r.read())!=-1) { if(ch=='\r') continue; if(ch=='\n') return sb.toString(); else sb.append((char)ch); }
<span style="white-space:pre"> </span>//当最后一个字符没有\r\n时,我发做出判断就读不出来数据,所以要判断存储的字符串是否为null。 if(sb.length()!=0) return sb.toString(); return null; } /* 覆盖Reader类中的抽象方法。在Reader功能的基础上进行增强。 */ public int read(char[] cbuf,int off,int len) throws IOException { return r.read(cbuf,off,len); } public void close() throws IOException { r.close(); } public void myClose() throws IOException { r.close(); } } class MyBufferedReaderDemo { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("buf.txt"); MyBufferedReader myBuf = new MyBufferedReader(fr); String line = null; while((line=myBuf.myReadLine())!=null) { System.out.println(line); } } }
4、装饰设计模式
当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。那么自定义的该类称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象。并基于被装饰的对象的功能,提供更强的功能。
示例:
class Person { public void chifan() { System.out.println("吃饭"); } } class SuperPerson { private Person p; SuperPerson(Person p) { this.p = p ; } public void superChifan() { System.out.println("开胃酒"); p.chifan(); System.out.println("甜点"); System.out.println("来一根"); } } class PersonDemo { public static void main(String[] args) { Person p = new Person(); SuperPerson sp = new SuperPerson(p); sp.superChifan(); } }
5、理解装饰与继承
继承模式:
MyReader//专门用于读取数据的类。通过对父类的加强,来产生每一个功能增强的子类
|--MyTextReader
|--MyBufferedTextReader
|--MyMediaReader
|--MyBufferedMediaReader
|--MyDataReader
|--MyBufferedDataReader
class MyBufferedReader
{
MyBufferedReader(MyTextReader text)
{}
MyBufferedReader(MyMediaReader media)
{}
}
上面这个类扩展性很差。
找到其参数的共同类型,通过多态的形式,可以提高扩展性。
装饰模式
class MyBufferedReader extends MyReader
{
MyBufferedReader(MyReader r)
{}
}
MyReader//专门用于读取数据的类。MyReader的子类也可以传入该子类,并分别进行增强
|--MyTextReader
|--MyMediaReader
|--MyDataReader
|--MyBufferReader
装饰模式比继承要灵活,避免了继承体系臃肿。而且降低了类与类之间的关系。
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。
所以装饰类和被装饰类通常是都属于一个体系中的。
6、LineNumberReader类
跟踪行号的缓冲字符输入流。
此类定义了方法 setLineNumber(int) 和 getLineNumber(),它们可分别用于设置和获取当前行号。
默认情况下,行编号从 0 开始。该行号随数据读取在每个行结束符处递增,并且可以通过调用 setLineNumber(int) 更改行号。但要注意的是,setLineNumber(int) 不会实际更改流中的当前位置;它只更改将由 getLineNumber() 返回的值。
import java.io.*; class LineNumberReaderDemo { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("PersonDemo.java"); LineNumberReader lnr = new LineNumberReader(fr); String line = null; <span style="white-space:pre"> </span>//自定义初始行号 lnr.setLineNumber(100); while((line=lnr.readLine())!=null) { System.out.println(lnr.getLineNumber()+":"+line); } lnr.close(); /*异常处理try LineNumberReader lnr = null; try { lnr = new LineNumberReader(new FileReader("PersonDemo.java")); String line = null; while((line=lnr.readLine())!=null) { System.out.println(line); } } catch(IOException e) { throw new RuntimeException(); } finally { try { <span style="white-space:pre"> </span>if(lnr!=null) <span style="white-space:pre"> </span>lnr.close(); } catch(IOException e) { throw new RuntimeException(); } } */ } }
//练习:模拟一个带行号的缓冲对象。
import java.io.*; class MyLineNumberReader { private Reader r; private int lineNumber; MyLineNumberReader(Reader r) { this.r = r; } public String myReadLine()throws IOException { lineNumber++; StringBuilder sb = new StringBuilder(); int ch = 0; while((ch=r.read())!=-1) { if(ch=='\r') continue; if(ch=='\n') return sb.toString(); else sb.append((char)ch); } if(sb.length()!=0) return sb.toString(); return null; } public void setLineNumber(int lineNumber) { this.lineNumber = lineNumber; } public int getLineNumber() { return lineNumber; } public void myClose()throws IOException { r.close(); } } class MyLineNumberReaderDemo { public static void main(String[] args)throws IOException { FileReader fr = new FileReader("PersonDemo.java"); MyLineNumberReader mylnr = new MyLineNumberReader(fr); String line = null; mylnr.setLineNumber(100); while((line=mylnr.myReadLine())!=null) { System.out.println(mylnr.getLineNumber()+"::"+line); } mylnr.myClose(); } }
四、字节流
1、字节流:InputStream(读) OutputStream(写)
字节流常操作媒体文件如图片、音视频文件。
演示:
需求:想要操作图片数据,这时就要用到字节流。先了解字节流的读写操作:
import java.io.*; class FileStreamDemo { public static void main(String[] args)throws IOException { writeFile(); readFile_1(); readFile_2(); readFile_3(); } public static void readFile_3()throws IOException { FileInputStream fis = new FileInputStream("fos.txt"); // int num = fis.available();
//<code><strong><a target=_blank href="">available</a></strong>()</code> 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。 byte[] buf = new byte[fis.available()]; //定义一个刚刚好的缓冲区,不用再循环了 fis.read(buf); System.out.println(new String(buf)); fis.close(); } public static void readFile_2()throws IOException { FileInputStream fis = new FileInputStream("fos.txt"); byte[] buf = new byte[1024]; int len=0; while((len=fis.read(buf))!=-1) { System.out.println(new String(buf,0,len)); } fis.close(); } public static void readFile_1()throws IOException { FileInputStream fis = new FileInputStream("fos.txt"); int ch = 0; while((ch=fis.read())!=-1) { System.out.println((char)ch); } fis.close(); } public static void writeFile()throws IOException { FileOutputStream fos = new FileOutputStream("fos.txt"); fos.write("abcde".getBytes());//转换成字节数组 fos.close(); } }
练习:
/* 复制一个图片 思路: 1.用字节读取流对象和图片关联。 2.用字节写入流创建一个图片文件,用于存储获取到的图片数据。 3.通过循环读写,完成数据的存储。 4.关闭资源。 */ import java.io.*; class CopyPic { public static void main(String[] args) { FileOutputStream fos = null; FileInputStream fis = null; try { fos = new FileOutputStream("2.png"); fis = new FileInputStream("1.png"); byte[] buf = new byte[1024]; int len = 0; while((len=fis.read())!=-1) { fos.write(buf,0,len); } } catch(IOException e) { throw new RuntimeException("复制文件失败"); } finally { try { if(fis!=null) fis.close(); } catch(IOException e) { throw new RuntimeException("关闭读文件失败"); } try { if(fos!=null) fos.close(); } catch(IOException e) { throw new RuntimeException("关闭写文件失败"); } } } }
2、自制缓冲区:
import java.io.*; class MyBufferedInputStream { private InputStream in; private byte[] buf = new byte[1024*4]; private int pos = 0,count = 0; MyBufferedInputStream(InputStream in) { this.in = in; } //一次读一个字节,从缓冲区(字节数组)获取。 public int myRead() throws IOException { //通过in对象读取硬盘上的数据,并存储在buf中; if(count==0) { <span style="white-space:pre"> </span>//count=0开始读取文件 count =in.read(buf);
<span style="white-space:pre"> </span>//判断是否读取完 if(count<0) return -1; pos=0; byte b = buf[pos]; count--; pos++; //11111111-->提升为一个int类型,那不还是-1吗?是-1 的原因是因为在8个1牵连补的还是1导致的,那么我们只要在前面补0,既可以保留原字节数据不变,又可以避免-1的出现。所以可以让b&0xff; return b&0xff; } else if(count>0) { byte b = buf[pos]; count--; pos++; return b&0xff; } return -1; } public void myClose() throws IOException { in.close(); } }
演示mp3的复制,通过缓冲区。
BufferedOutputStream
BufferedInputStream
import java.io.*; class CopyMp3 { public static void main(String[] args) throws IOException {
<span style="white-space:pre"> </span>//返回系统时间 long start = System.currentTimeMillis(); copy_2(); long end = System.currentTimeMillis(); System.out.println((end-start)+"毫秒"); } public static void copy_2() throws IOException { MyBufferedInputStream bufis = new MyBufferedInputStream(new FileInputStream("0.mp3")); BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("1.mp3")); int by = 0; while((by=bufis.myRead())!=-1) { bufos.write(by); } bufis.myClose(); bufos.close(); } //通过字节流的缓冲区完成复制。 public static void copy_1() throws IOException { BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("0.mp3")); BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("1.mp3")); int by = 0; while((by=bufis.read())!=-1) { bufos.write(by); } bufis.close(); bufos.close(); } }
4、读取键盘录入
System.out:对应的是标准输出设备,控制台。
System.in:对应的标准输入设备,键盘。
/*
需求: 通过键盘录入数据。 当录入一行数据后,就将该行数据进行打印。 如果录入的数据是over,那么停止录入。 */ import java.io.*; class ReadIn { public static void main(String[] args) throws IOException { InputStream in = System.in; StringBuilder sb = new StringBuilder(); while(true) { int ch = in.read(); if(ch=='\r') continue; if(ch=='\n') { String s = sb.toString(); if("over".equals(s)) break; System.out.println(s.toUpperCase()); <span style="white-space:pre"> </span>//清空缓冲区 sb.delete(0,sb.length()); } else sb.append((char)ch); } } }
通过刚才的键盘录入一行数据并打印大写,发现其实就是读一行数据的原理。也就是readLine方法。
能不能直接使用readLine方法来完成键盘录入的一行数据读取呢?
readLine方法是字符流BufferedReader类中的方法。而键盘录入的read方法是字节流InputStream的方法。
那么能不能将字节流转成字符流再使用字符流缓冲区的readLine方法呢?
import java.io.*; class TransStreamDemo { public static void main(String[] args) throws IOException { /* <span style="white-space:pre"> </span>//获取键盘录入对象 InputStream in = System.in; <span style="white-space:pre"> </span>//将字节流对象转换成字符流对象,使用转换流。InputStreamReader InputStreamReader isr = new InputStreamReader(in); <span style="white-space:pre"> </span>//为了提高效率,将字符串进行缓冲区技术高校操作。使用BufferedReader BufferedReader bufr = new BufferedReader(isr); */ //键盘录入最常见写法 BufferedReader bufr= new BufferedReader(new InputStreamReader(System.in)); /* OutputStream out = System.out; OutputStreamWriter osw = new OutputStreamWriter(out); BufferedWriter bufw = new BufferedWriter(osw); */ BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out)); String line = null; while((line=bufr.readLine())!=null) { if("over".equals(line)) break; bufw.write(line.toUpperCase()); bufw.newLine(); bufw.flush(); } bufr.close(); } }
5、学了IO流对象这么多该如何使用?下面就开始分析:
a.源:键盘录入。
目的:控制台。
b.需求:想把键盘录入的数据存储到一个文件中。
源:键盘。
目的:文件。
c.需求:想要将一个文件的数据打印在控制的台上。
源:文件
目的:控制台。
流操作的基本规律:
最痛苦的就是流对象有很多,不知道该用哪一个。
通过三个明确来完成。
a.明确源和目的。
源:输入流:InputStream Reader
目的:输出流:OutputStream Writer
b.明确操作的数据是否是纯文本。
是:字符流
否:字节流
c.当体系明确后,在明确要使用那个具体的对象。
通过设备来进行区分:
源设备:内存、硬盘、键盘
目的设备:内存、硬盘、控制台。
a.将一个文本文件中的数据存储到另一个文件中。复制文件。
源:因为是源,所以用读取流。InputStream Reader
是不是操作文本文件》
1):是:z这是就可以选择Reader
这样体系就明确了。
接下来明确要使用该体系中的那个对象。
明确设备:硬盘、上的一个文件。
Reader体系中可以操做文件的对象是 FileReader
FileReader fr = new FileReader("a.txt");
是否需要提高效率:是,加入Reader体系中缓冲区:BufferedReader。
BufferedReader bufr = new BufferedReader(fr);
目的:OutputStream Writer。
是否纯文本。
1):是:Writer
设备:硬盘,一个文件。
Writer体系中可以操作文件的对象 FileWriter。
FileWriter fw = new FileWriter("b.txt");
是否需要提高效率:是,加入Writer体系中缓冲区:BufferedWriter。
BufferedWriter bufw = new BufferedWriter(fw);
b.需求:将键盘录入的数据保存到一个文件中。
这个需求中有源和目的都存在。
那么分别分析
源:InputStream Reader
是不是纯文本?是!Reader
设备:键盘:对应的对象是System.in
不是选择Reader吗?System.in对应的不是字节流吗??
为了操作键盘的文本数据方便。可以转成字符流,按照字符串操作是最方便的。
所以既然明确对了Reader,就将System.in转换成Reader。
用了Reader体系中的转换流,InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
需要提高效率吗?需要:BufferedReader
BufferedReader bufr = new BufferedReader(isr);
目的:OutputStream Writer
是否是纯文本?是!Writer
设备:硬盘,一个文件。使用FileWriter。
FileWriter fw = new FileWriter("c.txt");
需要提高效率吗?需要。
BufferedWriter bufw = new BufferedWriter(fw);
*****************************************************************************
扩展:想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中。
目的:OutputStream Writer
是否是纯文本?是!Writer
设备:硬盘,一个文件。使用FileWriter。
但是FileWriter是使用的默认编码表。GBK。
但是存储时,需要加入指定的编码表utf-8,而指定的编码表只有转换流可以指定。
所以要使用的对象是OutputStreamWriter.
而该转换流对象要接受一个字节输出流,而且还可以操作文件的字节输出流。FileOutputStream
OutputStreamWriter osw = new OutptStreamWriter(new FileOutputStream("d.txt"),"UTF-8");
需要高效吗?需要。
BufferedWriter bufw = new BufferedWriter(osw);
所以记住,转换流什么时候使用。字符和字节之间的桥梁。
通常,涉及到字符编码转换时需要用到转换流。
练习:
class TransStreamDemo2 { public static void main(String[] args)throws IOException { //改变输入源 //System.setIn(new FileInputStream("CopyMp3.java")); //改变输出源 System.setOut(new PrintStream("zz.txt")); //键盘录入最常见写法 BufferedReader bufr= new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out)); String line = null; while((line=bufr.readLine())!=null) { if("over".equals(line)) break; bufw.write(line.toUpperCase()); bufw.newLine(); bufw.flush(); } bufr.close(); } }
练习:将异常信息传入到文本中
import java.io.*; import java.util.*; import java.text.*; class ExceptionInfo { public static void main(String[] args) { try { int[] arr = new int[2]; System.out.println(arr[3]); } catch(Exception e) { try { Date d = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); String s = sdf.format(d); PrintStream ps = new PrintStream("exception.log"); ps.println(s); System.setOut(ps); } catch(IOException ex) { throw new RuntimeException("日志文件创建失败"); } e.printStackTrace(System.out); } } } //log4j包工具完成日志信息的建立
五、File类
用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作。
File对象可以作为参数传递给流的构造函数。
1、File类常见方法:
a.创建
boolean createNewFile():在指定位置创建文件,如果文件已经存在,则不创建,返回false;
和输出流不一样,输出流对象一建立创建文件,而且文件已经存在,会覆盖。
boolean mkdir();创建文件夹
boolean mkdirs();创建多级目录
b.删除
boolean delete();删除失败返回false
void deleteOnExit();在程序退出时删除指定文件。
c.判断
boolean exists();文件是否存在
isFile();
isDirectory();
isHidden();
isAbsolute();是否是绝对路径
d.获取信息
String getName();
getPath();
getParent();
getAbsolute();
long lastModified();返回最后一次修改的时间
long length();
import java.io.*; class FileDemo { public static void main(String[] args)throws IOException { method_5(); } public static void method_5()throws IOException { File f1 = new File("a.txt"); File f2 = new File("b.txt"); // 重新命名此抽象路径名表示的文件。 sop("rename:"+f1.renameTo(f2));//重命名 } public static void method_4() { File f = new File("file.txt"); //封装的什么路径返回什么路径 sop("path:"+f.getPath()); //返回绝对路径 sop("abspath:"+f.getAbsolutePath()); sop("parent:"+f.getParent()); //该方法返回的是绝对路径中的父目录,如果获取的是相对路径,返回的NULL //如果相对路径中有上一层目录,那么该目录就是返回结果。 } public static void method_3() { File f = new File("File.txt"); //记住:在判断文件对象是否是文件或目录时,必须要判断该文件对象封装的内容是否存在。通过exists判断。 sop("dir:"+f.isDirectory()); sop("file:"+f.isFile()); } public static void method_2() { File f = new File("file.txt"); // sop("exists:"+f.exists()); // sop("execute:"+f.canExecute());//是否能执行 //创建文件夹 File dir = new File("abc\\kk"); sop("mkdir:"+dir.mkdir());//只能创建一级目录 } public static void method_1()throws IOException { File f = new File("file.txt"); //在虚拟机终止时,请求删除此抽象路径名表示的文件或目录。 f.deleteOnExit(); //判断创建文件是否成功 // sop("create:"+f.createNewFile()); // sop("delete:"+f.delete()); } //创建File对象 public static void consMethod() { //将a.txt封装成file对象。可以将已有的和未出现的文件或文件夹封装成对象 File f1 = new File("c:\\abc\\a.txt"); //将目录和文件分开,好处是对目录下的文件可以随意操作 File f2 = new File("c:\\abc","b.txt"); <span style="white-space:pre"> </span>/*<span style="white-space:pre"> </span>目的和上面是一样的 File d = new File("c:\\abc"); File f3 = new File(d,"c.txt"); <span style="white-space:pre"> </span>*/ sop("f1:"+f1); sop("f2:"+f2); sop("f3:"+f3); //跨平台分隔符separator :与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。 File f4 = new File("c:"+File.separator+"abc"+File.separator+"d.txt"); sop("f4:"+f4);//c:\abc\d.txt } public static void sop(Object obj) { System.out.println(obj); } }
import java.io.*; class FileDemo2 { public static void main(String[] args) { File dir = new File("c:\\"); //返回该目录下所有文件与文件夹,返回的对象,可以进行操作 File[] files = dir.listFiles(); for(File f : files) { System.out.println(f.getName()+"::"+f.length()); } } public static void listDemo_2() { File dir = new File("E:\\Java\\java2015\\io");
<span style="white-space:pre"> </span>//返回根据指定过滤器格式的文件活文件夹 <span style="white-space:pre"> </span> String[] arr = dir.list(new FilenameFilter() { public boolean accept(File dir,String name) { // System.out.println("dir:"+dir+"..name::"+name); /* if(name.endsWith(".bmp")) return true else return false; */ return name.endsWith(".png"); } }); for(String name : arr) { System.out.println(name); } } public static void listDemo() { File f = new File("c:\\"); <span style="white-space:pre"> </span>//累出当前目录下的所有文件,包含隐藏文件 String[] names = f.list(); //调用list方法file对象必须是封装了一个目录,该目录还必须存在。 for(String name : names) { System.out.println(name); } } public static void listRootsDemo() { <span style="white-space:pre"> </span>//static File[] listRoots() 列出可用的文件系统根。 File[] files = File.listRoots(); for(File f : files) { System.out.println(f); } } }
/* 列出指定目录下文件或者文件夹,包含子目录中的内容。 也就是列出指定目录下所有内容 因为目录中还有目录,只要使用同一个列出目录功能的函数完成即可。 在列出过程中出现的还是目录的话,还可以再次调用本功能。 也就是函数自身调用自身。 这种表现形式,或者编程手法,称为递归。 递归要注意: 1.限定条件 2.要注意递归的次数,尽量避免内存益处。 */ import java.io.*; class FileDemo3 { public static void main(String[] args) { File dir = new File("E:\\Java\\java2015"); showDir(dir,-1); //toBin(6); //int n = getSum(3); //System.out.println(n); } public static String getLevel(int level) { StringBuilder sb = new StringBuilder(); sb.append("|--"); for(int x=0;x<level;x++) { sb.insert(0,"| "); } return sb.toString(); } public static void showDir(File dir,int level) { System.out.println(getLevel(level)+dir.getName()); File[] files = dir.listFiles(); level++; for(int x=0;x<files.length;x++) { if(files[x].isDirectory()) showDir(files[x],level); else System.out.println(getLevel(level)+files[x]); } } //递归思想 public static int getSum(int n) { if(n==1) return 1; return n+getSum(n-1); } public static void toBin(int num) { if(num>0) { toBin(num/2); System.out.println(num%2); } /* while(num>0) { System.out.println(num%2); num = num/2; } */ } }
/* 删除一个带内容的目录。 删除原理: 在window中,删除目录从里面往外面删除的。 既然是从里往外删除。就需要用到递归。 */ import java.io.*; class RemoveDir { public static void main(String[] args) { File dir = new File("E:\\Java\\java2015\\test"); removeDir(dir); } public static void removeDir(File dir) { File[] files = dir.listFiles(); for(int x=0;x<files.length;x++) { if(files[x].isDirectory()) removeDir(files[x]); else System.out.println(files.toString()+":-file-:"+files[x].delete()); } System.out.println(dir+"::dir::"+dir.delete()); } }
练习:
/* 将一个指定目录下的java文件的绝对路径,存储到一个文本文件中。 建立一个java文件列表文件。 思路: 1.对指定的目录进行递归 2.获取递归过程中所有的java文件的路径。 3.讲这些路径存储到集合中。 4.将集合中的数据写入到一个文件中 */ import java.util.*; import java.io.*; class JavaFileList { public static void main(String[] args) { File dir = new File("E:\\Java\\java2015"); List<File> list = new ArrayList<File>(); fileToList(dir,list); //System.out.println(list.size()); File file = new File(dir,"javalist.txt"); writeToFile(list,file.toString()); } public static void fileToList(File dir,List<File> list) { File[] files = dir.listFiles(); for(File file : files) { if(file.isDirectory()) fileToList(file,list); else { if(file.getName().endsWith(".java")) list.add(file); } } } public static void writeToFile(List<File> list,String javaListFile) { BufferedWriter bufw = null; try { bufw = new BufferedWriter(new FileWriter(javaListFile)); for(File f : list) { String path = f.getAbsolutePath(); bufw.write(path); bufw.newLine(); bufw.flush(); } } catch(IOException e) { throw new RuntimeException("异常"); } finally { try { if(bufw!=null) bufw.close(); } catch(IOException e) { throw new RuntimeException("关闭异常"); } } } }