IO流学习笔记
1.File类
文件和目录路径名的抽象表示形式。
4种构造方法
File(File parent, String child)
File(File parent, String child)
File(String parent, String child)
创建一个文件
案例【1】
1 import java.io.File;
2 import java.io.IOException;
3
4 public class FileDemo {
5
6 /**
7 * 创建一个文件
8 */
9 public static void main(String[] args) {
10 // TODO 自动生成的方法存根
11 File f = new File("F:\\file.txt");
12 System.out.println(f.exists());
13 try {
14 f.createNewFile();
15 } catch (IOException e) {
16 // TODO 自动生成的 catch 块
17 e.printStackTrace();
18 }
19 System.out.println(f.exists());
20 }
21
22 }
运行结果:
false
true
打开F盘会发现存在一个0字节的file.txt文件
路径分隔符
在不同的系统中路径分隔符是不同的,所以想要代码有更好的移植性,我们采用pathSeparator
和separator
公共静态字段。
案例【2】
1 import java.io.File;
2 import java.io.IOException;
3
4 public class FileDemo {
5
6 /**
7 * 创建一个文件
8 */
9 public static void main(String[] args) {
10 // TODO 自动生成的方法存根
11 String filename ="F:"+File.separator+"file.text2";
12 File f = new File(filename);
13 File f2 = new File("1.txt");
14
15 try {
16 f.createNewFile();
17 f2.createNewFile();
18 } catch (IOException e) {
19 // TODO 自动生成的 catch 块
20 e.printStackTrace();
21 }
22 System.out.println(f.getName());
23 System.out.println(f.getAbsolutePath());
24 System.out.println(f.getPath());
25 System.out.println(f2.getName());
26 System.out.println(f2.getAbsolutePath());
27 System.out.println(f2.getPath());
28 }
29
30 }
输出结果:
file.text2
F:\file.text2
F:\file.text2
1.txt
F:\ec\HelloWorld\1.txt
1.txt
注意体会AbsolutePath和Path(我的项目写在F:\ec\HelloWorld文件夹中)
创建多级文件夹
File类中的mkdir()和mkdirs()可以用来创建目录,区别是mkdirs()可以创建多层目录,即同时创建这个目录的父目录和父目录的目录,而mkdir()
要求创建目录的父目录已经存在。
案例【3】
1 import java.io.File;
2
3 public class FileDemo {
4
5 /**
6 * 创建一个多级文件夹
7 */
8 public static void main(String[] args) {
9 String filename ="F:"+File.separator+"a"+File.separator+"b";
10 File f = new File(filename);
11 f.mkdirs();
12
13 }
14
15 }
运行完打开F盘发现多了文件夹a,打开文件夹a。里面有空文件夹b。
列出指定目录的全部文件(不包括子目录)
File类中存在list和Filelist方法,返回的分别是String类型的数组和File类型的数组
案例【4】
1 import java.io.File;
2
3 public class FileDemo {
4
5 /**
6 * 列出指定目录文件(不包括子目录)
7 */
8 public static void main(String[] args) {
9 String filename ="F:"+File.separator+"src";
10 File f = new File(filename);
11 File[] f2 = f.listFiles();
12 for(int i=0;i<f2.length;i++)
13 {
14 System.out.println(f2[i]);
15 }
16 }
17
18 }
输出:
F:\src\A.class
F:\src\abc.java
F:\src\B$1.class
F:\src\B$C.class
F:\src\B.class
F:\src\back
F:\src\Car.class
F:\src\day01.class
F:\src\pack
列出指定目录的全部文件(包括子目录)
对于这个问题,我们需要用到isDirectory()方法和递归。
对于isDirectory()方法,当且仅当此抽象路径名表示的文件存在且是一个目录时,返回 true
;否则返回 false
。
因此我们可以对文件夹列出使用递归,如果文件夹下还有文件夹,则对后者接着使用列出方法。
案例【5】
1 import java.io.File; 2 3 public class FileDemo { 4 5 /** 6 * 列出指定目录文件(包括子目录) 7 */ 8 public static void main(String[] args) { 9 String filename ="F:"+File.separator+"src"; 10 File f = new File(filename); 11 listAll(f); 12 } 13 14 public static void listAll(File f) 15 { 16 File[] f2 =f.listFiles(); 17 for(int i=0;i<f2.length;i++) 18 { 19 if(f2[i].isDirectory()) 20 { 21 System.out.println(f2[i]); 22 listAll(f2[i]); 23 } 24 else 25 { 26 System.out.println(f2[i]); 27 } 28 } 29 } 30 }
输出结果:
F:\src\A.class
F:\src\abc.java
F:\src\B$1.class
F:\src\B$C.class
F:\src\B.class
F:\src\back
F:\src\back\packageDemo.class
F:\src\Car.class
F:\src\day01.class
F:\src\pack
F:\src\pack\abc.class
我们可以发现虽然列出了所有的文件夹和文件夹下的文件,但是我们直观上并不好看出哪些是一级目录下的,哪些是子目录下的文件,
所以我们对此进行改进。
案例【6】
1 import java.io.File; 2 3 public class FileDemo { 4 5 /** 6 * 列出指定目录文件(包括子目录) 7 */ 8 public static int level =0; 9 public static void main(String[] args) { 10 String filename ="F:"+File.separator+"src"; 11 File f = new File(filename); 12 listAll(f); 13 } 14 15 public static void listAll(File f) 16 { 17 File[] f2 =f.listFiles(); 18 for(int i=0;i<f2.length;i++) 19 { 20 if(f2[i].isDirectory()) 21 { 22 print(level); 23 System.out.println(f2[i]); 24 level++; 25 listAll(f2[i]); 26 } 27 else 28 { 29 print(level); 30 System.out.println(f2[i]); 31 } 32 } 33 level--; 34 } 35 36 private static void print(int level) { 37 for(int i=0;i<level;i++) 38 System.out.print("——"); 39 } 40 }
输出:
F:\src\A.class
F:\src\B$1.class
F:\src\B$C.class
F:\src\B.class
F:\src\back
+F:\src\back\abc.java
+F:\src\back\packageDemo.class
F:\src\Car.class
F:\src\day01.class
F:\src\pack
+F:\src\pack\abc.class
我们可以清楚的看到一级目录下的文件和文件夹是没有+号的,二级目录有个+号,如果有三级目录的话,会出现两个+号。
文件过滤
当我们需要在一堆文件中找到我们需要的特定类型文件时我们可以根据后缀名过滤。
public String[] list(FilenameFilter filter)
public File[] listFiles(FilenameFilter filter)
这两个方法需要FilenameFilter对象参数。
FilenameFilter是一个接口,其中声明了一个方法
boolean accept(File dir ,String name)
我在F盘src文件夹中添加了2个txt文本文件,并将其过滤去。
案例【7】
1 import java.io.*; 2 3 //后缀名文件过滤器 4 public class SuffixFilter implements FilenameFilter { 5 6 public boolean accept(File dir, String name) { 7 8 return name.endsWith(".txt"); 9 } 10 11 } 12 13 14 import java.io.File; 15 16 public class FileDemo { 17 18 /** 19 * 列出指定目录文件(不包括子目录)下的txt格式文件 20 */ 21 public static void main(String[] args) { 22 String filename ="F:"+File.separator+"src"; 23 File f = new File(filename); 24 File[] f2 = f.listFiles(new SuffixFilter()); 25 for(int i=0;i<f2.length;i++) 26 { 27 System.out.println(f2[i]); 28 } 29 } 30 31 }
运行结果:
F:\src\新建文本文档 (2).txt
F:\src\新建文本文档.txt
我们可以看到成功筛选出txt文档文件。
2字符流
Reader和Writer是字符流的超类,直接继承自Object类的抽象类。
以字符为单位进行读写。
这些体系的子类都以父类名作为后缀。
而且子类名的前缀就是该对象的功能。
以子类FileReader和FileWriter为例作为说明
案例【8】
1 import java.io.*; 2 3 public class FileDemo { 4 5 /** 6 * 字符流读写 7 * @throws IOException 8 */ 9 public static void main(String[] args) throws IOException { 10 String filename ="F:"+File.separator+"字符流.txt"; 11 12 FileWriter fw =new FileWriter(filename); 13 fw.write("zerocoin你好"); 14 fw.close(); 15 FileReader fr =new FileReader(filename); 16 System.out.println((char)fr.read()); 17 int ch=0; 18 while((ch=fr.read())!=-1) 19 { 20 System.out.print((char)ch); 21 } 22 fr.close(); 23 24 }
输出结果:
z
erocoin你好
F盘中出现字符流.txt,内容是zerocoin你好。
不难发现read()方法返回的是int型,当所有数据读完时,返回—1。
read方法中也可以添加参数。
案例【9】
1 import java.io.*; 2 3 public class FileDemo { 4 5 /** 6 * 字符流读写 7 * @throws IOException 8 */ 9 public static void main(String[] args) throws IOException { 10 String filename ="F:"+File.separator+"字符流.txt"; 11 12 FileWriter fw =new FileWriter(filename); 13 fw.write("zerocoin你好"); 14 fw.close(); 15 FileReader fr =new FileReader(filename); 16 //System.out.println((char)fr.read()); 17 char[] ch=new char[1024]; 18 int len=0; 19 while((len=fr.read(ch))!=-1)//数组作为参数,读取到数组中 20 { 21 System.out.print(new String(ch,0,len)); 22 } 23 fr.close(); 24 25 } 26 27 }
zerocoin你好
我们在往已存在内容的文件中用Write方法的话,会从头开始写,文件内容会被覆盖,
我们可以使用FileWriter另一个构造器生成对象进行续写。
即FileWriter(String fileName, true)。
3字节流
字节流按字节读写
案例【9】
1 import java.io.*; 2 3 public class FileDemo { 4 5 /** 6 * 字节流读写 7 * @throws IOException 8 */ 9 public static void main(String[] args) throws IOException { 10 String filename ="F:"+File.separator+"字节流.txt"; 11 FileOutputStream fo =new FileOutputStream(filename); 12 FileInputStream fi = new FileInputStream(filename); 13 //fo.write("zerocoin你好");错误,字节流没有write(string s)方法 14 fo.write("zerocoin你好".getBytes()); 15 fo.close(); 16 byte [] ch=new byte[1024]; 17 int len=0; 18 while((len=fi.read(ch))!=-1)//数组作为参数,读取到数组中 19 { 20 System.out.print(new String(ch,0,len)); 21 } 22 fi.close(); 23 24 } 25 26 }
输出
zerocoin你好
F盘中出现字节流.txt
关于字节流和字符流的区别(参考于Rollen博客)
实际上字节流在操作的时候本身是不会用到缓冲区的,是文件本身的直接操作的,但是字符流在操作的 时候下后是会用到缓冲区的,是通过缓冲区来操作文件的。
读者可以试着将上面的字节流和字符流的程序的最后一行关闭文件的代码注释掉,然后运行程序看看。你就会发现使用字节流的话,文件中已经存在内容,但是使用字符流的时候,文件中还是没有内容的,这个时候就要刷新缓冲区。
使用字节流好还是字符流好呢?
答案是字节流。首先因为硬盘上的所有文件都是以字节的形式进行传输或者保存的,包括图片等内容。但是字符只是在内存中才会形成的,所以在开发中,字节流使用广泛。
4. BufferedReader和BufferedWriter流
从字符输入流中读写文本,缓冲各个字符,从而实现字符、数组和行的高效读写。
存在newLine()
,readLine(),reset(),skip()等新方法
案例【10】
1 import java.io.*; 2 3 public class FileDemo { 4 5 /** 6 * 7 * @throws IOException 8 */ 9 public static void main(String[] args) throws IOException { 10 String filename ="F:"+File.separator+"字符流.txt"; 11 12 FileWriter fw =new FileWriter(filename); 13 BufferedWriter bfw = new BufferedWriter(fw); 14 bfw.write("第一行"); 15 bfw.newLine();//分行符 16 bfw.write("012345", 0, 3);//从0读到2位 17 bfw.close(); 18 FileReader fr =new FileReader(filename); 19 BufferedReader bfr =new BufferedReader(fr); 20 21 String str=null; 22 //readLine()返回String类型,读完返回null 23 while((str=bfr.readLine())!=null) 24 { 25 System.out.println(str); 26 } 27 bfr.close(); 28 29 } 30 31 }
输出结果:
第一行
012
F盘中字符流.txt也被写入这输出。
BufferedInputStream和BufferOutputStream类似。
缓冲区中无非就是封装了一个数组,
并对外提供了更多的方法对数组进行访问。
其实这些方法最终操作的都是数组的角标。
缓冲的原理:
其实就是从源中获取一批数据装进缓冲区中。
在从缓冲区中不断的取出一个一个数据。
好处:效率更高。
5转换流
InputStreamReader和OutputStreamWriter 字节流转字符流
转换流的出现方便了对文件的读写,她在字符流与字节流之间架起了一座桥梁,使原本毫无关联的两种流操作能够进行转化,提高了程序的灵活性
1 import java.io.*; 2 3 public class FileDemo { 4 5 6 public static void main(String[] args) throws IOException { 7 String filename ="F:"+File.separator+"转换流.txt"; 8 File f =new File(filename); 9 FileOutputStream out =new FileOutputStream(f); 10 OutputStreamWriter osw =new OutputStreamWriter(out); 11 BufferedWriter bufw = new BufferedWriter(osw); 12 bufw.write("hello zerocoin"); 13 bufw.close(); 14 15 } 16 17 }
F盘下出现了转换流.txt.
InputStreamReader也是同样的用法。
下面主要讲下InputStreamReader(InputStream in,String charsetName)
和 OutputStreamWriter(OutputStream out,String charsetName)两种构造器。
1 import java.io.*; 2 3 public class FileDemo { 4 /** 5 * 用UTF编码写,默认编码GBK读 6 */ 7 8 public static void main(String[] args) throws IOException { 9 String filename ="F:"+File.separator+"转换流.txt"; 10 File f =new File(filename); 11 OutputStreamWriter osw =new OutputStreamWriter(new FileOutputStream(f),"UTF-8"); 12 osw.write("hello你好吗zerocoin"); 13 osw.close(); 14 InputStreamReader isr=new InputStreamReader(new FileInputStream(f)); 15 char []ch = new char[1024]; 16 int len; 17 while((len=isr.read(ch))!=-1) 18 { 19 System.out.println(new String(ch,0,len)); 20 } 21 22 } 23 }
结果:hello浣犲ソ鍚梲erocoin
为什么会出现乱码呢?因为当我们InputStreamReader(InputStream in)和OutputStreamWriter(OutputStream out)的默认
都是用的系统的字符集GBK。所以当我们用UTF—8码进行写入,GBK进行读取时就会出现乱码。我们可以设置用UTF—8码读,这样
就可以解决问题。
1 import java.io.*; 2 3 public class FileDemo { 4 /** 5 * 用UTF-8编码写,UTF-8读 6 */ 7 8 public static void main(String[] args) throws IOException { 9 String filename ="F:"+File.separator+"转换流.txt"; 10 File f =new File(filename); 11 OutputStreamWriter osw =new OutputStreamWriter(new FileOutputStream(f),"UTF-8"); 12 osw.write("hello你好吗zerocoin"); 13 osw.close(); 14 InputStreamReader isr=new InputStreamReader(new FileInputStream(f),"UTF-8"); 15 char []ch = new char[1024]; 16 int len; 17 while((len=isr.read(ch))!=-1) 18 { 19 System.out.println(new String(ch,0,len)); 20 } 21 22 } 23 }
输出:hello你好吗zerocoin
6.标准流
JAVA语言包中有一个类System,这个类没有共有的构建器,因此我们并不能创建这个类的实例对象。
它提供了三个静态类字段。
public final static InputStream in;
public final static PrintStream out;
public final static PrintStream err;
1 import java.io.*; 2 3 public class FileDemo { 4 /** 5 * 控制台写入文件 6 */ 7 8 public static void main(String[] args) throws IOException { 9 String filename ="F:"+File.separator+"text.txt"; 10 File f =new File(filename); 11 BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); 12 BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new 13 FileOutputStream(f))); 14 String line = null; 15 while((line=bufr.readLine())!=null){ 16 if("over".equals(line)) 17 break; 18 bufw.write(line); 19 bufw.newLine(); 20 bufw.flush(); 21 } 22 23 24 25 } 26 }
输入:
你好
zerocoin
over
当输入over时,程序结束。text中写入
你好
zerocoin
7 Properties集合
* Map
* |--Hashtable
* |--Properties
* 特点:
* 1,该集合中的键和值都是字符串类型。
* 2,集合中的数据可以保存到流中,或者从流获取。
* 通常该集合用于操作以键值对形式存在的配置文件。
Properties
类表示了一个持久的属性集。Properties
可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。
1 import java.io.FileOutputStream; 2 import java.util.Properties; 3 4 public class PropertiesDemo{ 5 public static void main(String[] args) throws Exception { 6 propertiesDemo(); 7 } 8 9 public static void propertiesDemo() throws Exception { 10 Properties prop = new Properties(); 11 12 prop.setProperty( "zhangsan","10" ); 13 prop.setProperty( "lisi","20" ); 14 prop.setProperty( "wangwu","30" ); 15 prop.setProperty( "zhaoliu","40" ); 16 17 //想要将这些集合中的字符串键值信息持久化存储到文件中 18 //需要关联输出流 19 FileOutputStream fos = new FileOutputStream("info.txt" ); 20 21 //将集合中数据存储到文件中 22 prop.store(fos, "name+age"); 23 24 fos.close(); 25 } 26 }