一、File类
- File类的一个对象代表一个文件或文件夹。
- File能新建、删除、重命名文件和目录,但File不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流。
- 想要在Java程序中表示一个真实存在的文件或目录,那么必须有一个File对象,但是Java程序的一个File对象可能没有一个真实存在的文件或目录。
- File对象可以作为参数传递给流的构造器。
1、构造器
- 构造器1:
public File(String pathname)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
File file1 = new File("hello.txt");
File file2 = new File("D:\\File\\file.txt");
File file3 = new File("D:" + File.separator + "File" + File.separator + "file.txt"); System.out.println(file1); System.out.println(file2); System.out.println(file3);
|
- 构造器2:
public File(String parent,String child)
1 2 3 4 5
|
File file4 = new File("D:\\upload","picture"); System.out.println(file4);
|
- 构造器3:
public File(File parent,String child)
1 2 3 4 5
|
File file5 = new File(file4, "hello.png"); System.out.println(file5);
|
2、常用方法
String getAbsolutePath()
:获取文件绝对路径
String getPath()
:获取路径
String getName()
:获取文件名
String getParent()
:获取上层文件目录路径。若无,返回null
long length()
:获取文件长度(即字节数)
long lastModified()
:获取最后一次修改时间,毫秒值
String[] list()
:获取指定目录下的所有文件或者文件目录的名称数组
File[] listFiles()
:获取指定目录下的所有文件或者文件目录的File数组
boolean renameTo(File dest)
:把文件重命名为指定的文件路径,例如:file1.renameTo(file2)
,要想返回true,file1必须在硬盘存在,且file2不能在硬盘中存在。
boolean isDirectory()
:判断是否是文件夹
boolean isFile()
:判断是否是文件
boolean exists()
:判断是否存在
boolean canRead()
:判断是否可读
boolean canWrite()
:判断是否可写
boolean isHidden()
:判断是否隐藏
boolean createNewFile()
:创建文件。若此文件存在,则不创建,返回false
boolean mkdir()
:创建文件夹。若此文件夹存在,不创建。若此文件夹的上层目录不存在,也不创建。
boolean mkdirs()
:创建文件夹。若上层文件夹不存在,则一并创建。
boolean delete()
:删除文件或文件夹。注意:删除不会放到回收站,要删除一个文件夹,请注意该文件夹下不可包含文件或文件夹。
二、IO流
- I/O是Input/Output的缩写,用于处理设备间的数据传输。如读写文件,网络通信等。
- Java程序中,对于数据的输入/输出操作以“流(Stream)“的方式进行。
- java.io包下提供了各种流的类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。
1、何为输入/输出
- 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
- 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中。
- 因此:输入/输出是站在程序(内存)的角度进行理解的。数据读进内存叫做输入,从内存中写出到外存叫做输出。
2、流的分类
3、IO流的体系
分类 |
字节输入流 |
字节输出流 |
字符输入流 |
字符输出流 |
抽象基类 |
InputStream |
OutputStream |
Reader |
Writer |
访问文件 |
FileInputStream |
FileOutputStream |
FileReader |
FileWriter |
访问数组 |
ByteArrayInputStream |
ByteArrayOutputStream |
CharArrayReader |
CharArrayWriter |
访问管道 |
PipedInputStream |
PipedOutputStream |
PipedReader |
PipedWriter |
访问字符串 |
|
|
StringReader |
StringWriter |
缓冲流 |
BufferedInputStream |
BufferedOutputStream |
BufferedReader |
BufferedWriter |
转换流 |
|
|
InputStreamReader |
OutputStreamWriter |
对象流 |
ObjectInputStream |
ObjectOutputStream |
|
|
|
FilterInputStream |
FilterOutputStream |
FilterReader |
FilterWriter |
打印流 |
|
PrintOutputStream |
|
PrintWriter |
推回输入流 |
PushbackInputStream |
|
PushbackReader |
|
特殊流 |
DataInputStream |
DataOutputStream |
|
|
- 其中FileInputStream 、FileOutputStream 、FileReader、FileWriter这四个流是节点流,即直接操作对文件本身进行操作的流。
- BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter四个缓冲流是比较经典的处理流。
- 处理流在节点流基础上进行加工,作用于节点流之上,可以提高流的读写速度。
4、节点流
4.1、FileReader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
@Test public void test1() throws Exception { File file = new File("hello.txt"); FileReader fileReader = new FileReader(file); int data; while ((data = fileReader.read()) != -1) { System.out.print((char) data); } fileReader.close(); }
|
处理异常(必须处理,否则流可能无法关闭):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @Test public void test1(){ FileReader fileReader = null; try { File file = new File("hello.txt"); fileReader = new FileReader(file); int data; while ((data = fileReader.read()) != -1) { System.out.print((char) data); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (fileReader != null) fileReader.close(); } catch (IOException e) { e.printStackTrace(); } } }
|
对read()的操作升级,即使用read的重载方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| @Test public void test2() { FileReader fileReader = null; try { File file = new File("hello.txt"); fileReader = new FileReader(file); char[] cbuf = new char[5]; int len; while ((len = fileReader.read(cbuf)) != -1) { for (int i = 0; i < len; i++) { System.out.print(cbuf[i]); } } } catch (IOException e) { e.printStackTrace(); } finally { try { if (fileReader != null) fileReader.close(); } catch (IOException e) { e.printStackTrace(); } } }
|
4.2、FileWriter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @Test public void test1() { FileWriter fileWriter = null; try { File file = new File("test1.txt"); fileWriter = new FileWriter(file); fileWriter.write("这是写出的数据"); } catch (IOException e) { e.printStackTrace(); } finally { try { if (fileWriter != null) fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } }
|
注意:输出操作,对应的输出文件可以不存在,会自动创建。如果存在,默认情况下会对原有文件进行覆盖操作,不是追加操作。
如果想要进行追加操作,则添加构造器第二个参数,true则表示在末尾追加:
1 2
| fileWriter = new FileWriter(file, true);
|
FileReader+FileWriter实现对文本文件复制:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| @Test public void test1() { FileReader fileReader = null; FileWriter fileWriter = null; try { File srcFile = new File("hello.txt"); File desFile = new File("test.txt"); fileReader = new FileReader(srcFile); fileWriter = new FileWriter(desFile); char[] cbuf = new char[5]; int len; while ((len = fileReader.read(cbuf)) != -1) { fileWriter.write(cbuf, 0, len); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (fileReader != null) fileReader.close(); } catch (IOException e) { e.printStackTrace(); } try { if (fileWriter != null) fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } }
|
注意:不可用像上面的字符流来处理图片视频等格式,可以运行,但是复制后的文件无法正常打开。
首先,先用字节流来尝试打印出文本文件内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @Test public void test1() { FileInputStream fis = null; try { File file = new File("hello.txt"); fis = new FileInputStream(file); byte[] buffer = new byte[5]; int len; while ((len=fis.read(buffer))!=-1){ String str = new String(buffer,0,len); System.out.print(str); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (fis!=null) fis.close(); } catch (IOException e) { e.printStackTrace(); } } }
|
从上面的运行结果可以看出:使用字节流输出文本内容,当文本中有中文时,可能会出现乱码情况。
出现这种情况的原因:在UTF8中,一个汉字3字节,而上面定义的缓冲区长度是5字节,byte[5],此时就有可能将一个汉字分隔开搬运,因此输出结果就会显示乱码。
因此:
1.对于文本文件(.txt,.java,.c,.cpp),应使用字符流处理。
2.对于非文本文件(.doc,.png,.jpg,.mp4,.ppt,….),使用字节流处理
4.4、FileOutputStream
使用FileInputStream+FileOutputStream实现对非文本文件复制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| @Test public void test1() { FileInputStream fis = null; FileOutputStream fos = null; try { File srcFile = new File("122.jpg"); File desFile = new File("1.jpg"); fis = new FileInputStream(srcFile); fos = new FileOutputStream(desFile); byte[] buffer = new byte[5]; int len; while ((len = fis.read(buffer)) != -1) { fos.write(buffer, 0, len); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (fos != null) fos.close(); } catch (IOException e) { e.printStackTrace(); } try { if (fis != null) fis.close(); } catch (IOException e) { e.printStackTrace(); } } }
|
5、缓冲流
BufferedInputStream、BufferedOutputStream实现对非文本文件的复制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| @Test public void test1() { BufferedInputStream bis = null; BufferedOutputStream bos = null; try { File srcFile = new File("122.jpg"); File desFile = new File("1.jpg"); FileInputStream fis = new FileInputStream(srcFile); FileOutputStream fos = new FileOutputStream(desFile); bis = new BufferedInputStream(fis); bos = new BufferedOutputStream(fos); byte[] buffer = new byte[10]; int len; while ((len = bis.read(buffer)) != -1) { bos.write(buffer, 0, len); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (bos != null) bos.close(); } catch (IOException e) { e.printStackTrace(); } try { if (bis != null) bis.close(); } catch (IOException e) { e.printStackTrace(); } } }
|
BufferedReader和BufferedWriter和上面的字节流使用方法一致,都是在FileReader和FileWriter外再套一层即可,用于提高对文本文件的处理速率。
6、转换流
- 转换流也属于处理流的一种,它提供了字符流和字节流之间的转换
- Java中提供了两个转换流:
- InputStreamReader:将InputStream转换为Reader(byte转换成char,解码)
- OutputStreamWriter:将Writer转换为OutputStream(char转换成byte,编码)
- 字节流中的数据都是字符时,转换成字符流操作更高效。
- 很多时候我们使用转换流来处理文件的乱码问题。实现编码和解码的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @Test public void test1() { InputStreamReader isr = null; try { FileInputStream fis = new FileInputStream("hello.txt"); isr = new InputStreamReader(fis, "UTF-8"); char[] cbuf = new char[1024]; int len; while ((len = isr.read(cbuf)) != -1) { String str = new String(cbuf, 0, len); System.out.println(str); } } catch (IOException e) { e.printStackTrace(); } finally { if (isr!=null) { try { isr.close(); } catch (IOException e) { e.printStackTrace(); } } } }
|
综合使用InputStreamReader和OutputStreamWriter(先用UTF-8读入,再用GBK写出)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| @Test public void test2() { InputStreamReader isr = null; OutputStreamWriter osw = null; try { File file1 = new File("hello.txt"); File file2 = new File("hello2.txt"); FileInputStream fis = new FileInputStream(file1); FileOutputStream fos = new FileOutputStream(file2); isr = new InputStreamReader(fis, "UTF-8"); osw = new OutputStreamWriter(fos, "GBK"); char[] buffer = new char[1024]; int len; while ((len = isr.read(buffer)) != -1) { osw.write(buffer, 0, len); } } catch (IOException e) { e.printStackTrace(); } finally { if (isr!=null) { try { isr.close(); } catch (IOException e) { e.printStackTrace(); } } if (osw!=null) { try { osw.close(); } catch (IOException e) { e.printStackTrace(); } } } }
|
7、标准输入、输出流
- System.in:标准的输入流,默认从键盘输入
- System.out:标准的输出流,默认从控制台输出
- 可以通过System类的
setIn(InputStream is)
/setOut(PrintStream ps)
方法重新指定输入和输出的流。
练习:
从键盘输入字符串,要求将读取到的整行字符串转换成大写输出,然后继续进行输入操作,直到输入‘e’或者‘exit’时,退出程序。
方法一:使用Scanner,调用next()返回一个字符串(省略)
方法二:使用System.in,System.in——>转换流——>BufferedReader的readline()(如下)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public static void main(String[] args) { BufferedReader br = null; try { InputStreamReader isr = new InputStreamReader(System.in); br = new BufferedReader(isr); while (true) { System.out.print("请输入字符串:"); String data = br.readLine(); if ("e".equalsIgnoreCase(data) || "exit".equalsIgnoreCase(data)) { System.out.println("程序结束"); break; } String upperCase = data.toUpperCase(); System.out.println("转换结果:" + upperCase); } } catch (IOException e) { e.printStackTrace(); } finally { if (br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } }
|
8、对象流
- ObjectInputStream和ObjectOutputStream用于存储和读取基本数据类型数据或者对象的处理流。它的强大之处就是可以将Java的对象写入数据源中,也能把对象从数据源中还原回来。
- 序列化:用ObjectOutputStream类保存基本类型数据或对象的机制。
- 反序列化:用ObjectInputStream类读取基本数据类型数据或对象的机制。
- ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量。
- 对象的序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久的保存在磁盘上,或通过网络这种二进制流来传输到另一个网络节点。当其他程序获取这种二进制流,就可以恢复成原来的Java对象
- 序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原。
- 如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一,否则,会抛出NotSerializableException异常。
- 凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:
- private static final long serialVersionUID;
- serialVersionUID用来表明类的不同版本的兼容性。简言之,其目的是以序列化对象进行版本控制,有关个版本反序列化时是否兼容
- 如果类中没有显式定义该常量,它的值是Java运行环境根据类的内部细节自动生成。若类的实例变量做了修改,serialVersionUID就可能发生变化。故建议显式声明。
- 简单来说,Java的序列化机制是通过在运行时判断serialVersionUID来验证版本一致性的。进行反序列化时,JVM会把过来的字节流中的serialVersionUID与本地相应实体类serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常(InvalidCastException)。
练习:
将String对象序列化和反序列化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| @Test public void test1() { ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(new FileOutputStream("obj.dat")); oos.writeObject(new String("这是一个String对象")); oos.flush(); } catch (IOException e) { e.printStackTrace(); } finally { if (oos != null) { try { oos.close(); } catch (IOException e) { e.printStackTrace(); } } } }
@Test public void test2() { ObjectInputStream ois = null; try { ois = new ObjectInputStream(new FileInputStream("obj.dat")); Object obj = ois.readObject(); String str = (String) obj; System.out.println(str); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } finally { if (ois != null) { try { ois.close(); } catch (IOException e) { e.printStackTrace(); } } } }
|
注意:被序列化的对象必须实现Serializable接口!!而且其内部的所有属性也必须是可序列化的!否则都会报错。