一.File类
1.File类是io包中唯一代表磁盘文件本身的对象。
构造方法创建文件对象:
File file = new File(String pathname);pathname:指路径名称(包含文件名)。
例:File file = new File("f:/abc/abcd/123.txt");/也可以写成\;
2.File类常用的方法:
- exists(): 判断这个文件是否存在
- mkdir(): 创建文件夹路径(只能建一层)
- mkdirs(): 创建文件夹路径
- createNewFile(): 创建文件
- delete(): 删除文件
- renameTo(): 对文件进行更名操作(也可以移动文件)
- geName(): 获取文件名称
- getPath(): 获取文件路径
- length(): 获取文件大小(以字节为单位)
- isDirectory(): 判断这个路径是不是一个目录
- isFile(): 判断这个路径是不是一个文件
- listFiles(): 获取当前这个路径下面所有的文件和文件夹,返回的是一个文件类型的数组;
二.流(stream)
1.概述
流是一组有序的数据序列,可以看成是一个通道,用来读取文件的数据,原始的数据都是通过二进制的形式进行数据的传输。
2.流的分类
按照数据流的方向分类:
- 输入流:Input 从文件到程序进行数据传输;
- 输出流:output 从程序到文件进行数据传输;
按照处理数据的单位分类:
- 字节流:一个字节占8位, 以一个字节为单位处理数据;
- 字符流:一个字符占两个字节,以一个字符为单位处理数据;
按照功能分类:
- 节点流:只有一个通道在文件上进行传输;
- 处理流:将节点流处理一下,增强通道的功能;
3.流的基本方法
JDK中提供了四种基本抽象类流InputStream(字节输入流),Reader(字符输入流),OutputStream(字节输出流),Writer(字符输出流)。所有的输入输出流都是他们的子类。
InputStream的基本方法:
- int read();每此调用这个方法,就读取一个字节,并以整数(0~255)的形式返回;返回-1时到达输入流末尾;
- int read(byte[] buffer);把读取到的数据填满这个byte[]类型的数组buffer(buffer是内存里面的一块缓冲区),然后再处理数组里面的数据。
- int read(byte[] buffer, int offset, int length);读取length长度的字节数, 存到buffer的缓冲区里, 从buffer的offset位置开始存, 返回值是实际读了多少字节数。
- close();关闭此输入流并释放与该流关联的所有系统资源。
- mark();在输入流的当前位置放置一个标记。
- reset();返回当前所做的标记处。
OutputStream的基本方法:
- void write();每次调用这个方法,就写入一个字节;
- void write(byte[] buffer);将buffer数组长度的字节从byte数组写入输出流;
- void write(byte[] buffer, int offset, int length);从bufer数组的offset位置开始写入length长度的字节到输出流;
- flush();彻底输出并清空缓冲区;
- close();关闭输出流;
Reader的基本方法:
- int read();每此调用这个方法,就读取一个字符,并以整数(0~255)的形式返回;返回-1时到达输入流末尾;
- int read(char[] buffer);把读取到的数据填满这个char[]类型的数组buffer(buffer是内存里面的一块缓冲区),然后再处理数组里面的数据。
- int read(char[] buffer, int offset, int length);读取length长度的字符数, 存到buffer的缓冲区里, 从buffer的offset位置开始存, 返回值是实际读了多少字符数。
- close();关闭此输入流并释放与该流关联的所有系统资源。
Wrriter的基本方法:
- void write();每次调用这个方法,就写入一个字节;
- void write(char[] buffer);将buffer数组长度的字节从byte数组写入输出流;
- void write(char[] buffer, int offset, int length);从buffer数组的offset位置开始写入length长度的字节到输出流;
- void write(String s);将一个字符串中的字符写入到输出流;
- void write(String s, int offset, int length);将一个字符串从offset开始的length个字符写入到输出流;
- flush();彻底输出并清空缓冲区;
- close();关闭输出流;
4.节点流范例
FileInputStream 文件输入字节流:读取文件中内容打印到控制台上。
public class Test {
public static void main(String args[]) {
int b = 0; // 使用变量b来装调用read()方法时返回的整数
FileInputStream in = null;
// 使用FileInputStream流来读取有中文的内容时,读出来的是乱码,因为使用InputStream流里面的read()方法读取内容时是一个字节一个字节地读取的,而一个汉字是占用两个字节的,所以读取出来的汉字无法正确显示。
// FileReader in = null;//使用FileReader流来读取内容时,中英文都可以正确显示,因为Reader流里面的read()方法是一个字符一个字符地读取的,这样每次读取出来的都是一个完整的汉字,这样就可以正确显示了。
try {
in = new FileInputStream("F:\abcdf\123/测试.txt");
// in = new FileReader("F:\abcdf\123/测试.txt");
} catch (FileNotFoundException e) {
System.out.println("系统找不到指定文件!");
System.exit(-1); // 系统非正常退出
}
long num = 0; // 使用变量num来记录读取到的字符数
try { // 调用read()方法时会抛异常,所以需要捕获异常
while ((b = in.read()) != -1) {
// 调用int read() throws Exception方法时,返回的是一个int类型的整数
// 循环结束的条件就是返回一个值-1,表示此时已经读取到文件的末尾了。
// System.out.print(b+" "); //如果没有使用“(char)b”进行转换,那么直接打印出来的b就是数字,而不是英文和中文了
System.out.print((char) b);
// “char(b)”把使用数字表示的汉字和英文字母转换成字符输入
num++;
}
in.close(); // 关闭输入流
System.out.println();
System.out.println("总共读取了" + num + "个字节的文件");
} catch (IOException e1) {
System.out.println("文件读取错误!");
}
}
}
FileOutputStream 文本输出字节流:读取一个文件的内容并将其写入另一个文件中;
public class Test{
public static void main(String args[]) {
int b = 0;
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("f:/abcdf/123/测试.txt");
out = new FileOutputStream("f:/abcdf/123/测试字节输出.txt");
// 指明要写入数据的文件,如果指定的路径中不存在测试字节输出.txt这样的文件,则系统会自动创建一个
while ((b = in.read()) != -1) {
out.write(b);
// 调用write(int c)方法把读取到的字符全部写入到指定文件中去
}
in.close();
out.close();
} catch (FileNotFoundException e) {
System.out.println("文件读取失败");
System.exit(-1); // 非正常退出
} catch (IOException e1) {
System.out.println("文件复制失败!");
System.exit(-1);
}
System.out.println("TestFileInputStream.java文件里面的内容已经成功复制到文件测试字节输出.txt里面");
}
}
FileInputStream和FileOutputStream这两个流都是字节流,都是以一个字节为单位进行输入和输出的。所以对于占用2个字节存储空间的字符来说读取出来时就会显示成乱码。
FileReader 文本输入字符流和 FileWriter 文本输出字符流;向文件中写入编码表并读取此文件内容打印到控制台上;
public class Test {
public static void main(String args[]){
/*使用FileWriter输出流从程序把数据写入到Uicode.dat文件中
使用FileWriter流向文件写入数据时是一个字符一个字符写入的*/
FileWriter fw = null;
try{
fw = new FileWriter("f:/abcdf/123/编码表.txt");
//字符的本质是一个无符号的16位整数
//字符在计算机内部占用2个字节
//这里使用for循环把0~60000里面的所有整数都输出
//这里相当于是把全世界各个国家的文字都0~60000内的整数的形式来表示
for(int c=0;c<=60000;c++){
fw.write(c);
//使用write(int c)把0~60000内的整数写入到指定文件内
//调用write()方法时,我认为在执行的过程中应该使用了“(char)c”进行强制转换,即把整数转换成字符来显示
//因为打开写入数据的文件可以看到,里面显示的数据并不是0~60000内的整数,而是不同国家的文字的表示方式
}
fw.close();
int b = 0;
long num = 0;
FileReader fr = null;
fr = new FileReader("f:/abcdf/123/编码表.txt");
while((b = fr.read())!= -1){
if(num % 100 ==0 ) {
System.out.println();
}
System.out.print((char)b);
num++;
}
fr.close();
System.out.println();
System.out.println("总共读取了"+num+"个字符");
}catch(Exception e){
e.printStackTrace();
}
}
}
5.处理流
1.缓冲流
带有缓冲区的,缓冲区(Buffer)就是内存里面的一小块区域,读写数据时都是先把数据放到这块缓冲区域里面,减少io对硬盘的访问次数,保护我们的硬盘。可以把缓冲区想象成一个小桶,把要读写的数据想象成水,每次读取数据或者是写入数据之前,都是先把数据装到这个桶里面,装满了以后再做处理。这就是所谓的缓冲。先把数据放置到缓冲区上,等到缓冲区满了以后,再一次把缓冲区里面的数据写入到硬盘上或者读取出来,这样可以有效地减少对硬盘的访问次数,有利于保护我们的硬盘。
BufferedInputSream 带缓存的输入字节流
例:将文件中的内容用缓冲流打印到控制台上
public class Test {
public static void main(String args[]){
FileInputStream fis = null;
try {
fis = new FileInputStream("F:\abcdf\123/测试.txt");
BufferedInputStream bis = new BufferedInputStream(fis); // 在FileInputStream节点流的外面套接一层处理流BufferedInputStream
int c = 0;
System.out.println((char)bis.read());
System.out.println((char)bis.read());
bis.mark(30); // 在当前位置做一个标记
for (int i = 0; i < 10 && (c = bis.read()) != -1;i++) {
System.out.print((char)c);
}
System.out.println();
bis.reset(); // 重新回到原来标记的地方
for (int i = 0; i < 10 && (c = bis.read()) != -1;i++) {
System.out.print((char)c); //打印结果是相同的;
}
bis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.转换流
转换流非常的有用,它可以把一个字节流转换成一个字符流,转换流有两种,一种叫InputStreamReader,另一种叫OutputStreamWriter。InputStream是字节流,Reader是字符流,InputStreamReader就是把InputStream转换成Reader。OutputStream是字节流,Writer是字符流,OutputStreamWriter就是把OutputStream转换成Writer。把OutputStream转换成Writer之后就可以一个字符一个字符地通过管道写入数据了,而且还可以写入字符串。我们如果用一个FileOutputStream流往文件里面写东西,得要一个字节一个字节地写进去,但是如果我们在FileOutputStream流上面套上一个字符转换流,那我们就可以一个字符串一个字符串地写进去。
例:将文本输出字节流转换成输出字符流;
public class Test {
public static void main(String[] args) {
OutputStreamWriter osw = null;
try {
osw = new OutputStreamWriter(new FileOutputStream("f:/abcdf/123/测试.txt"));//将文本输出字节流转换为输出字符流;
osw.write("今天天气很好");//转换后可以一个字符串一个字符串的写入文件中;
System.out.println(osw.getEncoding());//在控制台上打印字符的默认的编码格式;打印结果:GBK
osw.close();
osw = new OutputStreamWriter(new FileOutputStream("f:/abcdf/123/测试.txt",true),"utf-8");//如果不写true,前面写入的数据将会被替换掉,写上true后,会写在前面的数据后面;
osw.write("明天有雨");
System.out.println(osw.getEncoding());//打印结果:UTF-8;
osw.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
例:将输入到控制台的字符变为大写并打印到控制台上;
public class Test {
public static void main(String[] args) {
InputStreamReader isr = new InputStreamReader(System.in);//System.in为控制台的标准输入流;
BufferedReader br = new BufferedReader(isr);//将输入字符流转换成带缓冲的输入字符流;
String s = null;
try {
s = br.readLine(); // 阻塞式方法;
while (s != null) {
if (s.equalsIgnoreCase("exit")) {
break;
}
System.out.println(s.toUpperCase());
s = br.readLine();
}
br.close();//处理流关闭后,会自动关闭其中内部所有的流的对象;
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.打印流
PrintSream 和PrintWriter都属于打印流,分别针对字节和字符;且都有自动flush功能;
例:设置默认打印窗口为指定文件并打印编码表;
public class Test {
public static void main(String[] args) {
PrintStream ps = null;
try {
FileOutputStream fos = new FileOutputStream("f:/abcdf/123/编码表.txt");
ps = new PrintStream(fos);// 在输出流的外面套接一层打印流,用来控制打印输出
if (ps != null) {
System.setOut(ps);// 这里调用setOut()方法改变了输出窗口,以前写System.out.print()默认的输出窗口就是命令行窗口.但现在使用System.setOut(ps)将打印输出窗口改成了由ps指定的文件里面,通过这样设置以后,打印输出时都会在指定的文件内打印输出
}
for (char i = 0; i < 60000; i++) {
System.out.print(i);
if (i % 100 == 0) {
System.out.println();
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
例:调用方法将文件中的内容打印到控制台上;
public class Test {
public static void main(String[] args) {
String filename = "f:/abcdf/123/编码表.txt";
list(filename, System.out);
}
public static void list(String filename, PrintStream ps) {
BufferedReader br;
try {
br = new BufferedReader(new FileReader(filename)); //将文件字符输入流外套接一层缓冲流;
String s = null;
while ((s = br.readLine()) != null) { //缓冲流成行的读取文件内容
ps.print(s);
}
br.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
例:使用打印流做一个日志
public class Test {
public static void main(String[] args) {
String s = null;
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in)); //将系统的输入流套接转换流,再套接缓冲流,使其可以成行的读取数据;
PrintWriter pw; //定义一个打印流;
try {
FileWriter fw = new FileWriter("f:/abcdf/123/测试字符输出.txt",true); //创建一个文件字符输出流;
pw = new PrintWriter(fw); //将文件字符输出流外套接这个打印流;
while ((s = bf.readLine()) != null) { //按行读取控制台上的字符串;
if (s.equalsIgnoreCase("exit")) { //如果控制台上输入exit字符,退出循环;
break;
}
System.out.println(s.toUpperCase()); //将输入的字符转换成大写打印到控制台上;
pw.println("----------------------");
pw.println(s); //将读取到字符串内容通过打印流打印到文件中;
pw.flush();
}
pw.println("===============" + new Date() + "==============="); //退出循环后打印日期分隔符;
pw.flush();
pw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.数据流
例:通过数据输出流输出基础类型数据并通过输入流读取数据打印到控制台上;
public class Test {
public static void main(String[] args) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); //创建一个Byte型数组输出字节流;
DataOutputStream dos = new DataOutputStream(baos); //在流外面套接一个数据流输出字节流;用来处理int,double类型的数;
//数据流一般用来处理基础数据类型;
try {
dos.writeDouble(Math.random()); //写入一个Double型0到1之间的随机数;
dos.writeBoolean(true); //写入一个布尔值true;
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); //将写入Byte型输出字节流的数组转换成数组再放入到另一个Byte型输入字节流中;
System.out.println(bais.available()); //打印这个输入字节流的字节长度;打印结果为9。
DataInputStream dis = new DataInputStream(bais); //将这个输入字节流外面套接一个数据流输入字节流;
System.out.println(dis.readDouble()); //读取这个数据流里的数据
System.out.println(dis.readBoolean());
//数据流的写入与读取遵循先入先出的原则;如果先打印布尔型数据则会显示错误;
//栈中数据传输遵循先入后出的原则;
dos.close();
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
通过bais这个流往外读取数据的时候,是一个字节一个字节地往外读取的,因此读出来的数据无法判断是字符串还是bool类型的值,因此要在它的外面再套一个流,通过dataInputStream把读出来的数据转换就可以判断了。注意了:读取数据的时候是先写进去的就先读出来,因此读ByteArray字节数组数据的顺序应该是先把占8个字节的double类型的数读出来,然后再读那个只占一个字节的boolean类型的数,因为double类型的数是先写进数组里面的,读的时候也要先读它。这就是所谓的先写的要先读。如果先读Boolean类型的那个数,那么读出来的情况可能就是把double类型数的8个字节里面的一个字节读了出来。
5.对象流
1 public class Test {
2 public static void main(String[] args) {
3 T t =new T();
4 t.i += 4;
5 try {
6 FileOutputStream fos = new FileOutputStream("f:/abcdf/123/输出测试.txt");
7 ObjectOutputStream oos = new ObjectOutputStream(fos); //ObjectOutputStream流专门用来处理Object的,在fos流的外面套接ObjectOutputStream流就可以直接把一个Object写进去
8 oos.writeObject(t);
9 oos.flush();
10 oos.close();
11 FileInputStream fis = new FileInputStream("f:/abcdf/123/输出测试.txt");
12 ObjectInputStream ois = new ObjectInputStream(fis); // ObjectInputStream专门用来读一个Object的
13 T tRead = (T) ois.readObject(); // 直接把文件里面的内容全部读取出来然后分解成一个Object对象,并使用强制转换成指定类型T
14 System.out.println(tRead); //打印结果是重写的对象方法;
15 ois.close();
16 } catch (FileNotFoundException e) {
17 e.printStackTrace();
18 } catch (IOException e) {
19 e.printStackTrace();
20 } catch (ClassNotFoundException e) {
21 e.printStackTrace();
22 }
23 }
24 }
25 /*
26 * 凡是要将一个类的对象序列化成一个字节流就必须实现Serializable接口
27 * Serializable接口中没有定义方法,Serializable接口是一个标记性接口,用来给类作标记,只是起到一个标记作用。
28 * 这个标记是给编译器看的,编译器看到这个标记之后就可以知道这个类可以被序列化 如果想把某个类的对象序列化,就必须得实现Serializable接口
29 */
30 class T implements Serializable{ // Serializable的意思是可以被序列化的
31 String name = "duixiang";
32 int i = 3;
33 int j = 6;
34 transient double k = 3.14; //在声明变量时如果加上transient关键字,那么这个变量就会被当作是透明的,即不存在。
35 @Override
36 public String toString() {
37 return "T [name=" + name + ", i=" + i + ", j=" + j + ", k=" + k + "]";
38 }