IO体系简表:
IO流常用基类:
字节流:InputStream、OutputStream
字符流:Reader、Writer
这四个基类派生的子类都是以父类名作为子类类名的后缀。
IO流中,输入流和输出流基本上是成对出现的。
字符流也是字节流的一种,只是字符流要查找编码表。
一:字符流基类—Writer 和 Reader
Writer:
用于写入字符流的抽象类。
常用方法:void flush() //刷新流缓冲,它是抽象的。 void close() //刷新并关闭流,是抽象方法。 void write(int c) //写入单个字符 void write(String s) //写入字符串 void write(char[] arr) //写入字符数组 void write(String s,offset,len) //写入字符串的一部分 void write(char[] arr,offset,len) //写入字符数组的一部分,它是抽象的 Writer append(CharSequence c) //将指定字符序列续写到此Writer。
Reader :
用于读取字符流的抽象类。常用方法:
void close() //关闭流资源 int read() //读取一个字符,返回字符的ASCII码表示的整数。 int read(char[] arr)//将字符读入数组,返回读入的字符数。
二:FileWriter 和 FileReader
FileWriter :
用于写入字符文件的类。
FileWriter 对象一被初始化就必须指定被操作文件,否则无法写入数据。常用构造方法:
FileWriter(File file) //根据指定的File对象构造一个FileWriter对象。 FileWriter(String name) //根据指定的文件名构造一个FileWriter对象。 FileWriter(String name,boolean add) //在文件末尾处续写数据,不覆盖文件。
该类无方法,只有继承自父类的方法。
举例:
import java.io.*; class Demo { public static void main(String [] args) { FileWriter fw=null; try { fw=new FileWriter("Demo.txt"); //此句要抛异常 fw.write("zheshiyigewenjian"); } catch(IOException e) { throw new RuntimeException("写入失败 !"); } finally { if (fw!=null) try { fw.close(); } catch(IOException e) { throw new RuntimeException("写入关闭失败 !"); } } } }
注意:
(1) new FileWriter 对象时,会在当前目录下生成文件名为传入的参数的文件,
如果目录下包含该文件,会覆盖。
(2) 如果FileWriter调用了close方法,就不可以再写入数据了。否则报IO异常。
(3) 因为try代码块中的代码是局部代码,try代码块执行完毕就在内存中自动释放了,
所以要在try代码块外部先建立 FileWriter 对象的引用。否则finally代码块中找不到引用对象:fw。
(4) java创建文件时,底层其实是调用系统的内容建立文件,调用完毕要关闭资源,
所以调用close方法是必须的。一般放在异常体系的finally中。FileReader :
用于读取字符文件的类。常用构造方法:
FileReader(File file) //构造一个读取指定文件对象的读取流。 FileReader(String name) //构造一个读取指定文件的读取流。
该类无方法,只有继承自父类的方法。
举例: FileReader对象一次读取一个字符。
import java.io.*; class Demo { public static void main(String [] args) { FileReader fr=null; //先在try代码块外部建立对象引用 try { fr=new FileReader("Demo.txt"); int ch=0; while ((ch=fr.read())!=-1) { System.out.println("-> "+(char)ch); } } catch(IOException e) { throw new RuntimeException("没有这个文件!"); } finally { if (fw!=null) try { fw.close(); } catch(IOException e) { throw new RuntimeException("关闭失败 !"); } } } }
注意:
如果要读取的文件找不到,会报IO异常的子类异常: FileNotFoundException 。
read方法一次读取一个字符,可以迭代读取。
读取文件到末尾时,会有一个结束标记,就是-1,所以读取到-1 时就不再继续读。举例: FileReader对象一次读取多个字符存入一个字节数组。
import java.io.*; class Demo { public static void main(String [] args) { FileReader fr=null; try { fr=new FileReader("Demo.txt"); char [] arr=new char[1024]; //定义一个数组,用于缓冲读取的多个字符 int num=0; while ((num=fr.read(arr))!=-1) { System.out.print(new String(arr,0,num)); //把数组的一部分转成字符串打印 } } catch(IOException e) { throw new RuntimeException("没有这个文件!"); } finally { if (fw!=null) try { fw.close(); } catch(IOException e) { throw new RuntimeException("关闭失败 !"); } } } }
注意:内存中始终只有一个字符数组,字符数组不需要全部打印,读取多少字符就打印多少字符。
练习 : 复制一个文件,从C盘到D盘。
import java.io.*; class Demo { public static void main(String [] args) throws IOException { FileReader fr=new FileReader("LianXi.java"); FileWriter fw=new FileWriter("D:\\1.txt"); char [] arr=new char[1024]; int num=0; while ((num=fr.read(arr))!=-1) { fw.write(new String(arr,0,num)); } fr.close(); fw.close(); } }
注意:一定要有关流操作。
三:BufferedReader 和 BufferedWriter
读写流的缓冲区,它俩直接继承自Reader和Writer,可以实现高效读写。
缓冲区要结合流使用,可以在流的基础上对流功能进行增强。所以要先有流对象,才有缓冲区。
BufferedReader:
BufferedReader 在构造时需要指定输入流,常用的构造方法:BufferedReader(Reader r) //创建默认大小的缓冲字符读取流。
常用方法:
String readLine() //读取一个文本行 boolean ready() //判断此流是否已经准备好被读取 int read() //读取单个字符 void reset() //重置此流 void colse() //关闭流资源 int read(char[] arr,offset,len) //将字符读入数组的某一部分
BufferedReader 缓冲原理其实是定义方法包装了文件读取流的read方法,
对read方法的功能“一次读一个字符”进行了增强,一次可以读取一行。
这种基于已有功能提供增强后的功能的类称为装饰类,这种模式就是装饰设计模式。拓展:装饰和继承的区别
对于某一个具体的功能类,想要增强其功能,可以定义一个子类继承并复写类中方法。
但是如果这样的类比较多,就需要定义很多子类去增强它的功能,这样的继承体系比较臃肿。
所以可以定义一个专门增强功能的子类,可以对这个继承体系的所有类进行功能增强。
装饰设计模式一般作为一个类的子类存在,它没有自己特有的功能,只是让其他类通过该装饰类增强自身功能。装饰类通常会通过构造方法接收要被装饰的对象,并提供更强的功能。
装饰模式比继承要灵活,而且装饰类和被装饰类通常是同一个体系中的类。BufferedWriter:
BufferedWriter 在构造时也要指定输入流,常用构造方法:
BufferedWriter(Writer w) //创建默认大小的缓冲字符写入流
常用方法:
void flush() //刷新流的缓冲 void close() //刷新并关闭流 void newLine() //写入跨平台的换行符 void write(int ch) //写入字符的整数表示形式 void write(String s,offset,len) //写入字符串的一部分 void write(char[] arr,offset,len) //写入字符数组的一部分
举例:通过缓冲区复制文件
import java.io.*; class Demo { public static void main(String [] args) { BufferedReader br=null; BufferedWriter bw=null; try { br=new BufferedReader(new FileReader("LianXi.java")); bw=new BufferedWriter(new FileWriter("D.java")); copy(br,bw); } catch(IOException e) { throw new RuntimeException("读写失败"); } finally { if (br!=null) { try { br.close(); } catch(IOException e) { throw new RuntimeException("读取关闭失败"); } } if (bw!=null) { try { bw.close(); } catch(IOException e) { throw new RuntimeException("写入关闭失败"); } } } } public static void copy(BufferedReader br,BufferedWriter bw) throws IOException { String s=null; while ((s=br.readLine())!=null) { bw.write(s); //写入一行数据 bw.newLine(); //写入换行符 bw.flush(); //刷新缓冲区 } } }
LineNumberReader :
跟踪行号的缓冲字符输入流,它是BufferedReader的子类。
编号默认从0 开始,可以获取和修改行号。构造方法:
LineNumberReader(Reader r)
常用方法基本和BufferedReader相同, LineNumberReader 特有方法:
getLineNumber() //获取当前行号 setLineNumber() //设置当前行号
举例:
import java.io.*; class Demo { public static void main(String [] args) { LineNumberReader lnr=null; try { lnr=new LineNumberReader(new FileReader("LianXi.java")); char [] arr=new char[1024]; String line=null; lnr.setLineNumber(100); while((line=lnr.readLine())!=null) { System.out.println(lnr.getLineNumber()+": "+line); } } catch (IOException e) { throw new RuntimeException("读取失败"); } finally { if (lnr!=null) try { lnr.close(); } catch (IOException e) { throw new RuntimeException("读取关闭失败"); } } } }
注意: LineNumberReader 也是缓冲区,
四:字节流基类—OutputStream 和 InputStream
OutputStream:
字节输出流,它是抽象的,是所有字节输出流的根类。常用方法:
void flush() //刷新流缓冲 void close() //关闭流资源 void write(int b) //写入一个字节,该方法是抽象的 void write(byte[] b) //写入一个字节数组 void write(byte[] b,offset,len) //写入字节数组的一部分
InputStream:
字节输入流,它是抽象的,是所有字节输入流的根类。常用方法:
void close() //关闭此流 void read() //读取下一个字节,它是抽象的 void read(byte[] b) //读取一个字节数组 void read(byte[] b,offset,len) //读取字节数组的一部分 int available() //返回剩余未读取字节数
五:FileOutputStream 和 FileInputStream
FileOutputStream :
字节文件输出流, OutputStream 的直接子类。常用构造方法:
FileOutputStream(File f) //创建一个向指定文件对象写入数据的字节文件输出流 FileOutputStream(File f,boolean b) //创建一个向指定文件对象续写数据的字节文件输出流 FileOutputStream(String name) //创建一个字节输出流,向指定名称的文件写入数据 FileOutputStream(String name,boolean b) //创建一个字节输出流,向指定名称的文件续写数据
常用方法:
void close() //关闭流资源 void write(int b) //写入一个字节 void write(byte[] b) //写入一个字节数组 void write(byte[] b,offset,len) //写入字节数组的一部分
FileOutputStream 写入字节后,不需要刷新。
字符流读取的字符因为是双字节的,需要临时存储配对并查表,
字节流读取的字节是最小的单位,不需要转换,所以直接刷到目录去。FileInputStream :
字节文件输入流, InputStream 的直接子类。常用构造方法:
FileInputStream(File f) //创建一个文件对象的字节文件输入流 FileInputStream(String name) //创建一个指定名称的文件的字节输入流
常用方法:
void close() //关闭流资源 void read() //读取下一个字节 void read(byte[] b) //读取一个字节数组 void read(byte[] b,offset,len) //读取一个字节数组的一部分 int available() //返回剩余未读取字节数。换行符占两个字节。
举例:用字节流复制图片
import java.io.*; class Demo { public static void main(String [] args) { FileInputStream fin=null; FileOutputStream fout=null; try { fin=new FileInputStream("1.jpg"); fout=new FileOutputStream("A.jpg"); byte [] b=new byte[fin.available()]; //新建一个容量恰好的字节数组 fin.read(b); //按指定的长度读取源文件的字节 fout.write(b); //将读取后的数组写入输出流。 } catch(IOException e) { throw new RuntimeException("读写失败"); } finally { if (fin!=null) try { fin.close(); } catch (IOException e) { throw new RuntimeException("读取关闭失败"); } if (fout!=null) try { fout.close(); } catch (IOException e) { throw new RuntimeException("写入关闭失败"); } } } }
**注意:**available方法当要操作的文件特别大时可能会内存溢出。
比较高效的方法是建立临时数组缓冲一下存取次数。
六:BufferedInputStream 和 BufferedOutputStream
字节流的缓冲区。
BufferedInputStream:
字节输入流的缓冲区。构造方法:
BufferedInputStream(InputStream in) //创建一个输入流对象的缓冲区
常用方法:
void close() //关闭流资源 void read() //读取下一个字节 void read(byte[] b,offset,len) //读取一个字节数组的一部分 int available() //返回剩余未读取字节数。
BufferedOutputStream :
字节输出流的缓冲区。构造方法:
BufferedOutputStream(OutputStream out) //创建一个输出流对象的缓冲区
常用方法:
void flush() //刷新缓冲输出流 void write(int b) //写入整数转成字节后的部分 void write(byte[] b,offset,len) //将字节数组的一部分写入缓冲输出流
举例: 字节流缓冲区复制一个图片
import java.io.*; class Demo { public static void main(String [] args) { BufferedInputStream bin=null; BufferedOutputStream bout=null; try { bin=new BufferedInputStream(new FileInputStream("1.jpg")); bout=new BufferedOutputStream(new FileOutputStream("copy.jpg")); int b=0; while ((b=bin.read())!=-1) { bout.write(b); } } catch(IOException e) { throw new RuntimeException("读写失败"); } finally { if (bin!=null) try { bin.close(); } catch (IOException e) { throw new RuntimeException("读取关闭失败"); } if (bout!=null) try { bout.close(); } catch (IOException e) {throw new RuntimeException("写入关闭失败"); } } } }
注意:
此处的缓冲区,默认大小是1024*8 字节。练习: 自定义一个字节流读取缓冲区。
import java.io.*; class MyBuffer { private InputStream in; private byte [] arr=new byte[1024*8]; private int len=0,pos=0; MyBuffer(InputStream in) { this.in=in; } public int myRead() throws IOException { if (len==0) { len=in.read(arr); pos=0; if (len<0) return -1; } len--; return arr[pos++] &0xff; } public void myClose() throws IOException { in.close(); } } class Demo { public static void main(String [] args) throws IOException { MyBuffer mb=new MyBuffer(new FileInputStream("1.jpg")); BufferedOutputStream bout=new BufferedOutputStream(new FileOutputStream("MY.jpg")); int n=0; while ((n=mb.myRead())!=-1) { bout.write(n); } mb.myClose(); bout.close(); } }
注意:
(1)字节流在读取字节时,如果读取字节是负数,那么返回的int值会在二进制位上补1,返回的仍是一个负数。
如果返回的是-1,那写入循环就会终止,为了避免这种情况的发生,就必须让返回的int值在二进制位上补0。
可以在写入时手动强转,最简单的还是在返回值时通过与运算让整数的前24位变成1。
(2)缓冲区在写入时,依然是一次写入一个字节,这是比较慢的,可以自己改进,让写入时也写入数组。
七:InputStreamReader 和 OutputStreamWriter
读写转换流,可以实现字节流和字符流的相互转换。
创建对象时需要指定输入输出流。
这两个方法一般是将字节流先转成字符流,然后按字符流的方法进行操作,然后把字符流再转成字节流存储。
InputStreamReader :
读取转换流,可以将读取的字节流转成字符流的形式。
读取键盘录入时,获取的键盘录入是字节流形式,可以通过将字节流转成字符流来利用字符流的一次读一行的方法进行操作。
被该流转换后的流是字符流。构造方法:
InputStreamReader(InputStream in) //指定要转成字符流的字节输入流 InputStreamReader(InputStream in,String charSetName) //按指定字符集将字节流转成字符流
常用方法:
void close() //关闭流资源 int read() //读取单个字符 int read(byte[] b,offset,len) //读取字符数组的一部分
OutputStreamWriter :
写入转换流,可以将要写入的字符流转成字节流写入硬盘。
被该流转换后的流是字节流。构造方法:
OutputStreamWriter(OutputStream out) //指定要转成字节流的字符流。 OutputStreamWriter(OutputStream out,String charSetName) //按指定字符集将字符流转成字节流
常用方法:
void close() //关闭流资源 void flush() //刷新流缓冲 void write(int c) //写入单个字符 void write(char[] ch,offset,len) //写入字符数组的一部分 void write(String s,offset,len) //写入字符串的一部分
流的操作规律:
1 :明确是源还是目的。
如果是源,就选择InputStream或者Reader中的一个,如果是目的,就选择OutputStream或者Writer中的一个。
2 :明确操作的数据是否是纯文本。
如果是纯文本,就选择Reader或者Writer中的一个,如果不是纯文本,就选择InputStream或者OutputStream中的一个。
3 :以上两步可以确定使用哪一个类,然后根据设备来选择要使用哪个具体的对象。
4 :如果需要提高效率,就再加上相应的缓冲区。举例:读取转换流读取键盘录入,写入转换流把录入的数据输出。
import java.io.*; class Demo { public static void main(String [] args) { BufferedReader br=null; BufferedWriter bw=null; try { br=new BufferedReader(new InputStreamReader(System.in)); //一次读取一行字节并转成字符 bw=new BufferedWriter(new OutputStreamWriter(System.out)); //字符转成字节并一次打印一行 String line=null; while ((line=br.readLine())!=null) { if ("over".equals(line)) //退出条件 break; bw.write(line.toUpperCase()); //写入字符的大写 bw.newLine(); //字符流缓冲区特有的换行操作 bw.flush(); //刷新流缓冲,每一行都要刷新一次 } } catch (IOException e) { throw new RuntimeException("读取失败"); } finally { if (br!=null) try {br.close(); } catch (IOException e) { throw new RuntimeException("读取关闭失败"); } if (bw!=null) try {bw.close(); } catch (IOException e) { throw new RuntimeException("读取关闭失败"); } } } }
注意:
(1)如果需要将输出流输出到文件中,需要改动字节输出流 System.out ,改为 FileOutputStream(“out.txt”)。
(2)如果需要将输入流改成文件输入流,要改动字节输入流 System.out ,改为 FileInputStream(“in.txt”)。
(3)转换流的格式基本不动,使用时只需要改动源(输入流)和目的(输出流)。转换流编码的问题:
如果写入转换流指定了不是默认GBK的字符集编码,
BufferedWriter bw=
new BufferedWriter(new OutputStreamWriter(new FileOutputStream("1.txt"),"UTF-8"));
那么读取转换流在读取同一个文件时,也必须指定同一个编码才可以正常查看写入的内容,
BufferedReader br=
new BufferedReader(new InputStreamReader(new FileInputStream("1.txt"),"UTF-8"));
如果不指定编码或者指定其他编码,就会读取出错。FileReader 和 FileWriter 是 InputStreamReader 和 OutputStreamWriter 的子类,
但是 FileReader 和 FileWriter 不可以再指定其他编码,只能使用默认GBK编码,
如果需要指定其他编码,就要用他们的父类 InputStreamReader 和 OutputStreamWriter 。
八:PrintStream 和 PrintWriter
PrintStream:
字节打印流。PrintStream使其他流能够方便的打印各种数据值表示形式。构造方法:
PrintStream(File f) //创建指定文件对象的打印流 PrintStream(File f,String encode) //创建指定文件对象和编码的打印流 PrintStream(String fileName) //创建指定文件名称的打印流 PrintStream(String fileName,encode) //创建指定文件名称和编码的打印流 PrintStream(OutputStream out) //创建指定输出流的打印流 PrintStream(OutputStream out,autoFlush) //创建指定输出流的自动刷新的打印流 PrintStream(OutputStream out,autoFlush,encode) //创建指定输出流和编码的自动刷新的打印流
常用方法:
void flush() //刷新流缓冲 void close() //关闭流资源 void print(Basic) //打印基础数据类型 void println(Basic) //打印基础数据类型并换行 void write(int b) //将指定整数转成byte类型写入该流 void write(byte[] b,offset,len) //将字节数组一部分写入此流 PrintStream append(CharSequence cs) //将指定字符序列添加到此输出流
举例:改变标准输入输出设备,让系统日志信息输出到文件。
import java.io.*; import java.util.*; import java.text.*; class Demo { public static void main(String [] args) { try { int x=1/0; //发生异常 System.out.println("x="+x); } 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("log.txt"); //创建打印流对象并指向文件。 ps.println(s); System.setOut(ps); //设置输出流指向打印流。 } catch (IOException ex) { throw new RuntimeException("日志文件创建失败"); } e.printStackTrace(System.out); } } }
注意: System.out 是属于 PrintStream 流。
System.setIn(InputStream in) 方法和 System.setOut(PrintStream pout)方法可以改变标准输入输出设备。PrintWriter:
字符打印流。 PrintWriter 可以接收PrintStream可以接收的参数,而且可以接收字符输出流对象构造方法:
PrintWriter(File f) //创建指定文件对象的打印流 PrintWriter(File f,String encode) //创建指定文件对象和编码的打印流 PrintWriter(String fileName) //创建指定文件名称的打印流 PrintWriter(String fileName,encode) //创建指定文件名称和编码的打印流 PrintWriter(OutputStream out) //创建指定输出流的打印流 PrintWriter(OutputStream out,autoFlush) //创建指定输出流的自动刷新的打印流 PrintWriter(Writer w) //创建指定字符输出流的打印流 PrintWriter(Writer w,autoFlush) //创建指定字符输出流的自动刷新的打印流
常用方法:
void flush() //刷新流缓冲 void close() //关闭流资源 void print(Basic) //打印基础数据类型 void println(Basic) //打印基础数据类型并换行 void write(int c) //将指定整数转成byte类型写入该流 void write(char [] arr) //写入一个字符数组 void write(char[] arr,offset,len) //将字节数组一部分写入此流 void write(String s) //写入一个字符串 void write(String s,offset,len) //写入字符串的一部分 PrintWriter append(CharSequence cs) //将指定字符序列添加到此输出流
举例: PrintWriter 方法输出键盘录入到文件
import java.io.*; class Demo { public static void main(String [] args) throws IOException { BufferedReader bw=new BufferedReader(new InputStreamReader(System.in)); PrintWriter pw=new PrintWriter(new FileWriter("out.txt"),true); //设置输出流的自动刷新 String line=null; while ((line=bw.readLine())!=null) { if ("over".equals(line)) break; pw.println(line); } bw.close(); pw.close(); } }
注意: PrintWriter只有接受输出流才可以设置自动刷新。
输出时,直接用println方法,比write方法省了刷新动作。
九:File
用于将文件或文件夹封装成对象,方便对文件或文件夹进行操作。
File 对象可以作为参数传递给流的构造函数。
流只能操作文件的数据, File可以对文件或文件夹的属性信息进行操作。
构造方法:
File(String name) //创建一个文件对象
File(File dir,String name) //创建指定目录对象和文件名的文件对象
File(String dir,String name) //创建指定目录和文件名的文件对象
常用方法:
boolean exists() //判断是否存在该文件。
boolean isFile() //判断文件对象是否是文件
boolean isDirectory() //判断文件对象是否是目录
boolean mkdir() //创建文件夹,返回是否创建成功
boolean mkdirs() //创建多级文件夹
boolean createNewFile() //创建文件,如果文件已存在则返回false。
boolean canExecute() //判断文件是否能执行
boolean isHidden() //判断文件是否是隐藏的,有的隐藏文件没有权限访问
boolean isAbsolute() //判断是否是绝对路径
boolean canRead() //判断是否可以读取此文件
boolean canWrite() //判断是否可以修改此文件
boolean delete() //删除文件,返回是否删除成功
void deleteOnExit() //系统退出时删除文件,以防正在运行的文件删除不成功
int compareTo(File name) //按字母顺序比较两个文件对象
long length() //获取文件长度
long lastModified() //最后一次修改的时间
String getPath() //获取文件对象的字符串信息
String getName() //只获取文件对象的文件名
String getParent() //之获取文件对象的目录
String getAbsolutePath() //获取绝对路径
static File[] listRoots() //返回文件对象数组,包括所有可用的文件系统根盘符。
String[] list() //将文件对象表示的目录中的文件名和目录返回到一个字符串数组。
String[] list(FilenameFilter nf) //返回目录下过滤后的文件名数组
File[] listFiles() //返回目录下的文件和文件夹的对象的数组
File[] listFiles(FileFilter ff) //返回目录下过滤后的文件对象数组
举例:常用方法示例
import java.io.*;
class Demo {
public static void main(String [] args) throws IOException {
File dir=new File("d:\\Directory"); //创建目录对象
System.out.println(dir.mkdir()); //创建一级目录,返回是否创建成功,
File file=new File("d:\\Directory\\file.java"); //创建文件对象,也可以是目录对象
File file2=new File(dir,"file2.java"); //创建指定目录名和指定文件名的文件对象
System.out.println(file.exists()); //打印文件夹下文件是否存在
System.out.println(file); //打印的是传入的文件对象
System.out.println(file2); //打印的是文件的路径和文件名
System.out.println(file.isFile()); //打印文件对象是否是文件,该文件必须先存在才能判断
System.out.println(file2.isDirectory());//打印文件对象是否是目录,该目录必须先存在才能判断
//文件对象不代表文件y一定存在,只有调用创建文件的方法后,才会在硬盘创建文件,如果文件已存在,也不会覆盖。
//注意指定的目录必须存在,否则报错。
System.out.println(file.createNewFile()); //在硬盘创建文件
System.out.println(file2.createNewFile()); //在硬盘创建文件
System.out.println(file2.delete()); //这里只会删除指定目录下文件,而不会删除文件夹。
System.out.println(file.getName()); //只获取文件对象的文件名
System.out.println(file.getParent()); //之获取文件对象的目录部分,如果没有就返回null
System.out.println(file.getPath()); //获取文件对象的字符串路径
System.out.println(file.getAbsolutePath()); //获取文件绝对路径
System.out.println(file.length()); //返回文件大小
System.out.println(file.lastModified()); //返回文件上次被修改时间
}
}
注意:判断文件对象是文件还是目录,需要先判断文件或文件夹存在,否则直接返回false。
带目录的文件对象必须要保证存在该目录,创建新文件时只能创建文件。
练习:列出目录下所有子目录中的文件名
import java.io.*;
class Demo {
public static void main(String [] args) {
File dir=new File("f:\\共享站");
if (dir.isDirectory())
listFileName(dir);
else
System.out.println("目录错误");
}
public static void listFileName(File dir) {
File [] file=dir.listFiles();
for (File f : file) {
if (f.isDirectory()) {
System.out.println();
listFileName(f);
}
System.out.println(f.getParent()+"::::"+f.getName());
}
}
}
十:IO中的其他类
SequenceInputStream:
合并流。
表示其他输入流的逻辑串联,可以对多个输入流进行合并。
它从多个输入流的有序集合中的第一个开始读取,读完后接着读取下一个,直到读取完所有输入流。构造函数:
SequenceInputStream(Enumeration<? extends InputStream> e) //创建一个输入流的枚举合并流 SequenceInputStream(InputStream in1,InputStream in2) //创建一个指定两个输入流的合并流
常用方法:
void close() //关闭流资源 int read() //读取一个字节 int available() //可读取的长度 int read(byte[] b,offset,len) //读取字节数组一部分
举例:
import java.io.*; import java.util.*; class Demo { public static void main(String [] args) throws IOException { FileInputStream fin=new FileInputStream("A.jpg"); FileOutputStream fout=null; byte[] arr=new byte[1024*1024]; int len=0,count=0; while ((len=fin.read(arr))!=-1) { fout=new FileOutputStream("part"+(++count)+".part"); fout.write(arr,0,len); fout.close(); } fin.close(); Vector<InputStream> v=new Vector<InputStream>(); v.add(new FileInputStream("part1.part")); v.add(new FileInputStream("part2.part")); Enumeration<InputStream> en=v.elements(); SequenceInputStream sin=new SequenceInputStream(en); BufferedOutputStream bout=new BufferedOutputStream(new FileOutputStream("All.jpg")); int n=0; while ((n=sin.read())!=-1) { bout.write(n); } sin.close(); bout.close(); } }
注意:使用枚举的方法是通用的方法,但是比较麻烦,需要用到枚举。
也可以将要合并的文件传入文件输入流数组集合,建立ArrayList的迭代器,
然后建立Enumeration对象,通过枚举的迭代方式,取出ArrayList的元素。
文件输出流每个循环都要新建并关闭,因为每次生成的文件名都有差别。ObjectInputStream 和 ObjectOutputStream:
操作对象的流。(1)ObjectOutputStream :
对象写入流。
对象是存在于对内存中的,程序执行完后,内存中的对象就不存在了,
想要对之前的对象再次调用,可将对象通过此流输出到硬盘上,即对象的持久化存储。
要存储的对象必须实现 Serializable 接口,以启用序列化功能。否则无法序列化。构造方法:
ObjectOutputStream(OutputStream out) //将对象输出到指定输出流中。常用方法:
void close() //关闭流资源 void flush() //刷新流缓冲 void write(int b) //写入一个整数的字节部分 void write(byte[] arr) //写入字节数组 void write(byte[] b,offset,len) //写入字节数组的一部分 void writeInt() //写入一个整数的32位 void writeChar(int c) //写入一个字符的16位,还有其他的写入基本数据的方法 void writeChars(String s) //以字符序列形式写入一个字符串 void writeObject(Object oo) //将指定对象写入ObjectOutputStream
(2)ObjectInputStream :
对象读取流。
对已存储的对象进行读取。构造方法:
ObjectInputStream(InputStream in) //从指定文件中读取对象。
常用方法:
void close() //关闭流资源。 int readInt() //读取一个32位的int值。 char readChar() //读取一个16位的字符,还有其他的读取基本数据的方法 Object readObject() //读取对象。 int read() //读取数据字节。 int read(byte[] b,offset,len) //读取字节数组的一部分。
举例:
import java.io.*; class Demo { public static void main(String [] args) { ObjectOutputStream oout=null; ObjectInputStream oin=null; try { oout=new ObjectOutputStream(new FileOutputStream("W.txt")); oout.writeObject(new Person("wang",19)); oin=new ObjectInputStream(new FileInputStream("W.txt")); Person p=(Person)oin.readObject(); System.out.println(p); } catch (IOException e) { throw new RuntimeException("读写失败"); } catch (ClassNotFoundException e) { throw new RuntimeException("找不到该类"); } finally { if (oout!=null) try { oout.close(); } catch (IOException e) { throw new RuntimeException("写入关闭失败"); } if (oin!=null) try { oin.close(); } catch (IOException e) { throw new RuntimeException("读取关闭失败"); } } } } class Person implements Serializable { //实现Serializable接口才能进行序列化。 private String name; private int age; Person(String name,int age) { this.name=name; this.age=age; } public String toString() { return name+":"+age; } }
注意:
(1) Serializable 接口没有方法,不需要子类实现。没有接口的方法称为标记接口。
(2) 实现了 Serializable 接口的类会拥有一个UID,这个ID是编译器使用的。
如果类被再次修改,类拥有的ID就会变化,对于以存储到硬盘上的对象的ID还是原来的ID。
这样就会不匹配,导致运行失败。
(3) 如果在类中自己定义标记, static final long serialVersionUID=42L;
即使类被修改,UID也是一样的。
(4) 类中静态成员不可以被序列化。
如果非静态成员也不想被序列化,就在不想被序列化的成员前加 transient 。PipedInputStream 和 PipedOutputStream :
管道流.(1)PipedInputStream:
管道输入流,一般结合管道输出流使用。
它是管道的接收端,构造方法:
PipedInputStream() //创建尚未连接到管道输出流的管道输入流,需要再通过connect方与管道输出流连接 PipedInputStream(PipedOutputStream pout) //创建已连接到管道输出流的管道输入流。
常用方法:
void close() //关闭流资源 int read() //读取一个字节 int read(byte[] b,offset,len) //读取字节到一个字节数组的一部分 void connect(PipedOutputStream pout) //连接到管道输出流
(2)PipedOutputStream:
管道输出流,管道的发送端。构造方法:
PipedOutputStream() //创建尚未连接到管道输入流的管道输出流,需要通过connect方法与管道输入流连接 PipedOutputStream(PipedInputStream pin) //创建已连接到管道输入流的管道输出流
常用方法:
void close() //关闭流资源 void flush() //刷新流缓冲 void write(int b) //写入一个字节 void write(byte[] b,offset,len) //写入字节数组一部分 void connect(PipedInputStream pin) //将此管道输出流连接到管道输入流。
举例:
import java.io.*; class Read implements Runnable { //读取线程 private PipedInputStream pin; Read(PipedInputStream pin) { this.pin=pin; } public void run() { try { byte[] arr=new byte[1024]; int len=pin.read(arr); System.out.println(new String(arr,0,len)); pin.close(); } catch (IOException e) { throw new RuntimeException("管道流读取失败"); } } } class Write implements Runnable { //写入线程 private PipedOutputStream pout; Write(PipedOutputStream pout) { this.pout=pout; } public void run() { try { pout.write("xue xi java".getBytes()); pout.close(); } catch (IOException e) { throw new RuntimeException("管道流写入失败"); } } } class Demo { public static void main(String [] args) { PipedInputStream pin=null; PipedOutputStream pout=null; try { pin=new PipedInputStream(); pout=new PipedOutputStream(); pin.connect(pout); //管道流建立关系 Read r=new Read(pin); Write w=new Write(pout); new Thread(r).start(); new Thread(w).start(); } catch (IOException e) { throw new RuntimeException("管道流建立失败"); } } }
RandomAccessFile :
随机访问文件,自身具备读写的方法。该类虽然属于IO包中的成员,但它不是IO体系中的子类,而是直接继承自 Object 。
它内部封装了字节输入输出流,所以它具备读写功能。
它可以通过指针对数组的元素进行操作,可以改变和获取指针的位置。该类只能操作文件。
构造方法:
RandomAccessFile(File file,String mode) //创建指定文件对象的随机访问文件流。不覆盖。 RandomAccessFile(String s,String mode) //创建指定文件名称的随机访问文件流。不覆盖。
访问模式:
“r” 代表只读模式。要读取的文件不存在会报异常。
“rw” 代表读写模式。要读取的文件已存在不会覆盖。RandomAccessFile 类中有读取流、写入流常用的read、write方法,还有读写基本数据的方法。
其他的常用方法还有:long length() //返回文件长度 void seek(long pos) //设置指针 int skipBytes(int n) //跳过指定字节
举例:
import java.io.*; class Demo { public static void main(String [] args) throws IOException { RandomAccessFile rafw=new RandomAccessFile("raf.txt","rw"); rafw.write("张三".getBytes()); rafw.writeInt(257); rafw.write("李四".getBytes()); rafw.writeInt(258); rafw.write("王五".getBytes()); rafw.writeInt(259); rafw.close(); RandomAccessFile rafr=new RandomAccessFile("raf.txt","r"); rafr.seek(8); //调整指针到制定角标位。参数可以理解为角标。 rafr.skipBytes(8); //跳过指定字节数。 byte[] b=new byte[4]; rafr.read(b); String name=new String(b); int age=rafr.readInt(); System.out.println(name+" : "+age); rafr.close(); } }
注意:
(1)写入时,最好有规范,比如姓名固定占16字节,年龄固定占4字节。
(2)取出时,一个信息占20字节,可以调整要取出的是第几个信息。
(3)skipBytes方法只能往后跳,不抛异常,返回跳过的实际字节数。
(4)seek方法指定角标从哪开始,可以跳过空位读取字节或者写入字节,也可以通过重复写入达到修改的目的。DataInputStream 和 DataOutputStream :
操作基本数据类型的类。(1)DataInputStream :
基本数据输入流
构造方法:DataInputStream(InputStream in) //用指定的字节输入流创建一个基本数据输入流对象。
此类中有读取流常用的read方法,还有读取基本数据的方法。
其他常用方法还有:int skipBytes(int n) //跳过指定字节 String readUTF(); //以指定的编码读取字节
(2)DataOutputStream :
基本数据输出流
构造方法:DataOutputStream(OutputStream out) //创建指定输出流对象。
此类中有写入流常用的方法,还有写入基本数据的方法,
其他常用方法还有:int size() //已写入输出流的字节数。 void writeUTF(String s) //使用UTF-8编码将字符串写入基本数据输出流。读取时需要对应编码。
举例:
import java.io.*; class Demo { public static void main(String [] args) throws IOException { DataOutputStream dout=new DataOutputStream(new FileOutputStream("d.txt")); dout.writeInt(1234); dout.writeBoolean(false); dout.writeDouble(123.45); dout.writeUTF("你好"); dout.close(); DataInputStream din=new DataInputStream(new FileInputStream("d.txt")); System.out.println("int : "+din.readInt()); System.out.println("boolean : "+din.readBoolean()); System.out.println("double : "+din.readDouble()); System.out.println("UTF汉字 : "+din.readUTF()); din.close(); } }
注意:
读取字节需要有顺序,否则显示错误。
如果写入时指定了编码,读取时,也要按照对应编码读取。ByteArrayInputStream 和 ByteArrayOutputStream :
字节数组流。(1)ByteArrayInputStream :
字节数组输入流。
该流包含内部缓冲区,缓冲区包含从流中读取的字节。
该类不产生IO异常,不用关闭资源,因为没有调用系统底层资源。构造方法:(构造是需要定义数据源)
ByteArrayInputStream(byte[] arr) //指定缓冲区数组的字节数组输入流 ByteArrayInputStream(byte[] arr,offset,len) //指定缓冲区数组的字节数组输入流
常用方法:
long skip(long L) //从此输入流中跳过L个字节 void close() //关闭流,但是无效 int read() //读取下一个字节 int read(byte[] b,offset,len) //读取字节进数组
(2)ByteArrayOutputStream :
字节数组输出流。
该流中的数据被写入一个字节数组缓冲区,缓冲区容量会自动增长。
该类不产生IO异常,不需要关闭资源。该类构造时不需要定义数据目的。
常用方法:
int size() //返回缓冲区当前大小 void close() //关闭流,但是无效 byte[] toByteArray() //把缓冲区变成数组 void writeTo(OutputStream out) //将字节数组的内容写入到指定输出流中。
举例:
import java.io.*; class Demo { public static void main(String [] args) { ByteArrayInputStream bain=new ByteArrayInputStream("ABCD".getBytes()); ByteArrayOutputStream baout=new ByteArrayOutputStream(); int b=0; while ((b=bain.read())!=-1) { baout.write(b); } System.out.println(baout.toString()); try { baout.writeTo(new FileOutputStream("out.txt")); } catch (IOException e) { throw new RuntimeException("写入失败"); } } }
注意:
操作字符数组的 CharArrayReader 和 CharArrayWriter ,是专门针对字符的输入输出流,
还有专门操作字符串的输入输出流 StringReader 和 StringWriter 。