Java之IO流
1.File类
1.1定义
是文件和目录路径名的抽象表示形式,Java中把文件或者目录都封装成File对象。创建File时不会在文件系统创建文件。
1.2构造函数
1)File(String pathname)
将一个字符串路径封装成File对象。
File file=new File("D:\\Java\\1.txt");
2)File(String parent,String child)
传入一个父级路径和子级路径。
File flie2=new File("D:\\Java", "1.txt");
3)File(File parent,String child)
传入一个File类型的父级路径和子级路径。
File file3=new File(new File("D:\\Java"), "1.txt");
1.3File创建
File的创建包含创建文件和创建文件夹。
1)createNewFile()
当指定文件不存在时创建此文件并返回true,当文件已经存在时直接返回false。
File file = new File("E:/temp/test.txt"); System.out.println(file.createNewFile());
2)mkdir()
当指定一级文件夹不存在时创建此文件夹并返回true,当文件夹已经存在时直接返回false。即指定的多级目录不存在时,只会创建一级的目录,子级目录不会被创建。因此推荐使用mkdirs来创建文件夹。
3)mkdirs()
当指定多级文件夹中某一级不存在时创建此文件夹并返回true,当文件夹已经存在时直接返回false。
File file = new File("E:/temp/aaa/bbb/ccc"); System.out.println(file.mkdirs());
1.4File删除
1)delete()
当指定的文件或文件夹存在时删除文件或单级空文件夹,删除成功返回true,文件/文件夹不存在或删除失败时返回false。
File file = new File("E:/temp/aaa/bbb/ccc"); System.out.println(file.delete());
1.5File获取和修改名字
1)getAbsolutePath()
获取文件/目录的绝对路径,返回路径的字符串。
File file = new File("E:/tempFile/1.txt"); System.out.println(file.getAbsolutePath());//E:\tempFile\1.txt
2)getParent()
获取当前路径的父级目录,返回字符串
File file = new File("E:/tempFile/aaa/1.txt"); System.out.println(file.getParent());//E:\tempFile\aaa
3)getName()
获取最后一层文件或文件夹的名称,返回字符串
//获取文件名 File file = new File("E:/tempFile/1.txt"); System.out.println(file.getName());//1.txt //获取目录 File file2 = new File("E:/tempFile/bbb"); System.out.println(file2.getName());//bbb
4)lastModified()
以毫秒值返回最后修改时间
5)length()
返回文件内容的字节数
File file = new File("E:/tempFile/1.txt"); System.out.println(file.length());
6)listFiles()
以File对象的形式返回当前路径下所有的文件和文件夹的名称(绝对路径)
File file = new File("E:/tempFile"); System.out.println(file.listFiles())
7)File.listRoots()
获取计算机中所有的盘符
8)renameTo(File dest)
将当前File对象所指向的路径修改为指定File所指向的路径(移动文件)
File file = new File("E:/tempFile/1.txt"); File file2= new File("E:/tempFile/bbb/2.txt"); //移动文件 file.renameTo(file2);
1.6File判断
当文件或文件夹不存在时都返回false,否则返回true。
1)exists()
判断指定路径的文件或文件夹是否存在,存在返回true,否则返回false。多用于当文件或文件夹不存在时去再去创建。
第一种:判断文件夹是否存在,不存在就创建:
File file = new File("E:/temp/aaa/bbb/ccc"); if (!file.exists()) { file.mkdirs(); }
第二种:判断文件是否存在,不存在就创建:
String filePath = "E:/tempFile/1.txt"; File file2 = new File(filePath); //获取文件名 String fileName = file2.getName(); //获取文件的目录 String dirPath = filePath.substring(0, filePath.lastIndexOf(fileName)); File dirFile = new File(dirPath); //判断目录是否存在,若目录不存在直接创建文件会报错 if (!dirFile.exists()) { dirFile.mkdirs(); } if (!file2.exists()) { file2.createNewFile(); }
2)isAbsolute()
判断指定的路径是否是绝对路径,是返回true,否则返回false
File file = new File("tempFile/1.txt"); System.out.println(file.isAbsolute());//false File file2 = new File("E:/tempFile/1.txt"); System.out.println(file2.isAbsolute());//true
3)isDirectory()
判断是否是目录,是返回true,否则返回false
File file = new File("E:/tempFile/bbb"); System.out.println(file.isDirectory());
4)isFile()
判断是否是文件,是返回true,否则返回false
File file = new File("E:/tempFile/1.txt"); System.out.println(file.isFile());
5)isHidden()
判断是否是隐藏文件/目录,是返回true,否则返回false
1.7案例演示
1)删除指定目录。要删除一个目录,必须先删除其子文件和子目录!
public static void delete(File file) { if (file.exists()) { File[] listFiles = file.listFiles(); for (File f : listFiles) { if (f.isFile()) { f.delete();// 删除文件 } else if (f.isDirectory()) { delete(f); } } file.delete();// 删除目录 } }
2)查询指定目录下以.txt结尾的文件
public static void query(File file) { if(file.exists()) { File[] listFiles = file.listFiles(); for (File f : listFiles) { if(f.isFile()&& f.getName().endsWith(".txt")) { System.out.println(f); }else if(f.isDirectory()) { query(f); } } } }
2. IO流概述
2.1概述
Java中I/O操作主要是指使用java.io
包下的内容,进行输入、输出操作。输入也叫做读取数据,输出也叫做作写出数据。
2.2分类
详细的分类见下图,具体说明见后续章节。
3.字符流
只能读取纯文本文件内容。
3.1输入流(Reader)
输入流的顶层类,是一个抽象类。使用实现类FileReader来创建此输入流对象。
示例:读取txt的内容
File file = new File("E:/tempFile/1.txt"); FileReader reader = new FileReader(file); char b[] = new char[(int) file.length()]; reader.read(b); reader.close(); System.out.println(new String(b));
3.2输出流(Writer)
输出流的顶层类,是一个抽象类。使用实现类FileWriter来创建此输出流对象。
3.3案例演示
复制文本文件:
Reader reader = null; Writer writer = null; try { reader = new FileReader("E://tempFile/1.txt"); writer = new FileWriter("E://tempFile/copy.txt"); int len; char[] chars = new char[1024]; while ((len = reader.read(chars)) != -1) { writer.write(chars, 0, len); } writer.flush(); if (len == -1) System.out.println("文本文件复制成功"); } catch (Exception e) { e.printStackTrace(); } finally { //关闭资源 try { reader.close(); writer.close(); } catch (Exception e) { e.printStackTrace(); } }
4.字节流
读取任意文件,纯文本文件可能会乱码。
4.1输入流InputStream
读取。输入流的顶层类,是一个抽象类。使用实现类FileInputStream来创建此输入流对象。
对输入流,最重要的方法是read(byte[] b),将输入流读取到指定长度的字节数组中,返回读取的长度,当读到文件尾部时返回-1。
4.2输出流OutputStream
写入。使用实现类FileOutputStream来创建此输入流对象,创建对象时自动创建不存在的单级文件不同的流每次写都会覆盖之前的内容。
4.3案例演示
1)图片的复制
OutputStream os = null; InputStream is = null; try { //指定目标文件的路径,文件不存在时会创建一个 os = new FileOutputStream("E:/tempFile/aaa/new.jpg"); //指定读取的文件 is = new FileInputStream("E:/tempFile/3.jpg"); int len; byte[] bytes = new byte[1024]; while ((len = is.read(bytes)) != -1) {//边读边写 os.write(bytes, 0, len); } if (len == -1) System.out.println("复制图片成功"); } catch (Exception e) { e.printStackTrace(); } finally { try {//关闭资源,防止内存泄漏 os.close(); is.close(); } catch (Exception e) { e.printStackTrace(); } }
上述代码是图片的复制,对普通文件也适用。
2)浏览器浏览图片
@GetMapping("/lookImg") public void lookImg( HttpServletResponse response) throws IOException { File file = new File("E:/tempFile/下载图片模板.jpg"); if (!file.exists()) { response.setContentType("text/html;charset=UTF-8"); response.getWriter().write("error"); return; } FileInputStream in = new FileInputStream(file); int len ; byte bytes[] = new byte[1024]; OutputStream out = response.getOutputStream(); while ((len = in.read(bytes)) != -1) { out.write(bytes, 0, len); } in.close(); }
在浏览器地址栏访问对应的接口就可以看到图片。上述代码就是把图片通过输入流的方式转成浏览器可识别的输出流,交给浏览器去处理。
3)下载文件
@GetMapping("/download") public void download( HttpServletResponse response) throws IOException { File file = new File("E:/tempFile/下载图片模板.jpg"); if (!file.exists()) { response.setContentType("text/html;charset=UTF-8"); response.getWriter().write("error"); return; } FileInputStream in = new FileInputStream(file); //设置浏览器直接下载文件,不打开,并设置文件名的编码 response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8")); int len ; byte bytes[] = new byte[1024]; OutputStream out = response.getOutputStream(); while ((len = in.read(bytes)) != -1) { out.write(bytes, 0, len); } in.close(); }
在浏览器地址栏访问对应的接口即可下载图片,普通文件同上。示例2和示例3就只差了一行代码。实际上,对于输出流,浏览器打开部分文件,比如图片。能打开就直接打开,不能打开就下载。当指定不打开直接下载时,浏览器就会直接下载。
5.高效流
在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。由于字符流的局限性,故以字节流进行说明。
5.1高效字符流
1)BufferedReader:高效输入流
2)BufferedWriter:高效输出流
5.2高效字节流
1)BufferedInputStream:高效输入流
2)BufferedOutputStream:高效输出流
本例复制了wps的安装程序,大小是145MB。先看不使用高效流来复制一个大文件的耗时:
OutputStream os = null; InputStream is = null; long start = System.currentTimeMillis(); try { //指定读取的文件 is = new FileInputStream("E:/tempFile/aaa/wps.exe"); //指定目标文件的路径,文件不存在时会创建一个 os = new FileOutputStream("E:/tempFile/test.exe"); int len; byte[] bytes = new byte[1024]; while ((len = is.read(bytes)) != -1) {//边读边写 os.write(bytes, 0, len); } } catch (Exception e) { e.printStackTrace(); } finally { //关闭资源,防止内存泄漏 os.close(); is.close(); long end = System.currentTimeMillis(); System.out.println("复制成功,使用时间:" + (end - start) + " 毫秒");//复制成功,使用时间:1170 毫秒 }
使用高效流的耗时:
BufferedOutputStream os = null; BufferedInputStream is = null; long start = System.currentTimeMillis(); try { //指定读取的文件 is = new BufferedInputStream(new FileInputStream("E:/tempFile/aaa/wps.exe")); //指定目标文件的路径,文件不存在时会创建一个 os = new BufferedOutputStream(new FileOutputStream("E:/tempFile/test.exe")); int len; byte[] bytes = new byte[1024]; while ((len = is.read(bytes)) != -1) {//边读边写 os.write(bytes, 0, len); } } catch (Exception e) { e.printStackTrace(); } finally { //关闭资源,防止内存泄漏 os.close(); is.close(); long end = System.currentTimeMillis(); System.out.println("复制成功,使用时间:" + (end - start) + " 毫秒");//复制成功,使用时间:250 毫秒 }
从打印的结果来看,高效流确实比普通流更节省时间。
把上述的下载文件的代码进行修改,代码如下:
@GetMapping("/download") public void download( HttpServletResponse response) throws IOException { File file = new File("E:/tempFile/下载图片模板.jpg"); if (!file.exists()) { response.setContentType("text/html;charset=UTF-8"); response.getWriter().write("error"); return; } BufferedInputStream in = new BufferedInputStream(new FileInputStream(file)); //设置浏览器直接下载文件,不打开,并设置文件名的编码 response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8")); int len ; byte bytes[] = new byte[1024]; OutputStream out = response.getOutputStream(); while ((len = in.read(bytes)) != -1) { out.write(bytes, 0, len); } in.close(); }
为了提高代码的效率,推荐使用高效流操作文件。
对于高效输出流,当需要在写入文件时输入换行符时,可使用以下方式:
out.write((char) 13);
6.准字输入输出流
标准输入输出流说起来很熟悉。
1)System.in:标准输入流,从键盘读取内容
2)System.out:标准输出流,输出到控制台
7.打印流
平时在控制台打印输出,是调用print()
和println()
方法完成的,不过这两个方法都来自于java.io.PrintStream
类。以字节打印流和PrintStream
说明。它属于输出流的一种,多用于文本文件的复制,看下例:
try { BufferedReader br = new BufferedReader(new FileReader("E:/tempFile/user.txt")); PrintStream ps = new PrintStream("E:/tempFile/user1.txt"); String line; while ((line = br.readLine()) != null) { ps.println(line); } br.close(); ps.close(); } catch (Exception e) { e.printStackTrace(); }
在上述代码中用到了高效字符流,它特有的方法就是readLine,一次读取一行。
8.序列化流
Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型、对象的属性。字节序列写出到文件后,相当于文件中持久保存了一个对象的信息。反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。
8.1 ObjectOutputStream类
将Java对象的原始数据类型写出到文件,实现对象的持久存储。但一个对象要想序列化,必须满足两个条件,一是该类必须实现java.io.Serializable
接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
;二是该类的所有属性必须是可序列化的。当有属性被transient
修饰时,是不会进行序列化的。
把一个对象的数据写到user.txt文件中,代码如下,其中addr是瞬时的,不会被序列化:
User类:
public class User implements Serializable { private String name; private Integer age; private String password; private transient String addr; //get,set方法略 }
写入的方法:
User user = new User(); user.setAddr("湖北武汉"); user.setName("张三"); user.setAge(20); user.setPassword("123456"); ObjectOutputStream oos = null; try { oos = new ObjectOutputStream(new FileOutputStream("E:/tempFile/user.txt")); //将指定的对象写出 oos.writeObject(user); oos.close(); } catch (IOException e) { e.printStackTrace(); }
写完后,打开文件,发现是字节码,并不需要关心是什么意思,只要在解码时数据是正确的即可。
8.2 ObjectInputStream类
反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。
User user = null; ObjectInputStream ois = null; try { ois = new ObjectInputStream(new FileInputStream("E:/tempFile/user.txt")); //将指定的数据写入到对象 user = (User) ois.readObject(); ois.close(); System.out.println("转换的数据:" + user.toString());//User(name=张三, age=20, password=123456, addr=null) } catch (Exception e) { e.printStackTrace(); }
前面进行序列化的文件内容进行反序列化后仍能正常的打印对象的值,这就是序列化流。