IO流全面解析(含NIO部分说明)
IO流
- File类的使用
- 概述
- 说明
- 案例
- 常用方法
- File 类的获取功能
- File类的重命名功能
- File类的判断功能
- File类的创建功能(创建硬盘中对应的文件或文件目录)
- File类的删除功能
- 案例
- 练习
- 判断指定目录下是否有后缀名为.jpg的文件,如果有,就输出该文件名称
- 遍历指定目录所有文件名称,包括子文件目录中的文件
- IO流原理及流的分类
- Java IO 流原理
- Java IO原理
- 流的分类
- InputStream 方法说明
- Reader 方法说明
- OutputStream 方法说明
- Writer 方法说明
- 说明
- IO 流体系
- 节点流 与 处理流的理解
- 节点流 (也可称为:文件流) 的使用
- 流对象
- 注意点
- 相对路径中单元测试与 main() 方法的不同
- 案例
- 节点流的基本步骤
- 字符流案例
- 输入流 - FileReader
- 对 read() 方法的操作升级:使用 read() 的重载方法
- 输出流 - FileWriter
- 输入流和输出流的结合使用
- 字符流不能处理图片文件的测试
- 字节流案例
- 输入流 - FileInputStream
- 输入流与输出流(FileOutputStream)的结合使用
- 指定路径下文件的复制 - 视频 & 文档
- 处理流一:缓存流的使用
- 说明
- 流对象
- 案例
- BufferedInputStream 和 BufferedOutputStream 的使用
- BufferedReader 和 BufferedWriter 的使用
- 小练习
- 练习一
- 练习二
- 练习三
- 处理流二:转换流的使用
- 说明
- 流对象
- InputStreamReader 说明
- OutputStreamWriter 说明
- 案例
- InputStreamReader 的使用
- 综合使用 InputStreamReader 和 OutputStreamWriter
- 扩展:字符编码
- 转换流的编码应用
- 其它处理流的使用
- 标准输入、输出流的使用
- 说明
- 案例
- 练习
- 打印流的使用
- 说明
- 流对象
- 案例
- PrintStream 的使用
- PrintWriter 的使用
- 数据流的使用
- 说明
- 案例
- 对象流的使用
- 说明
- 对象的序列化
- 使用对象流序列化对象
- 序列化对象步骤
- 反序列化对象步骤
- 注意
- 案例
- 使用自定义类实现序列化与反序列化操作
- 随机存取文件流
- RandomAccessFile 类
- 说明
- 使用
- 其它
- 案例
- 说明
- RandomAccessFile 类的使用
- 关于 RandomAccessFile 只作为输出流时的文件覆盖操作
- RandomAccessFile 实现数据的插入效果
- getFilePointer() 方法的使用
- 思考:将 StringBuilder 替换为 ByteArrayOutputStream 实现 RandomAccessFile 的插入操作
- NIO.2 中 Path、Paths、Files 类的使用
- Java NIO 概述
- NIO.2
- Path、Paths 和 Files 核心 API
- Path 常用方法
- Files 常用方法
- 常用方法
- 常用方法 · 用于判断
- 常用方法 · 用于操作内容
- 案例
- Path 的使用
- Files 工具类的使用
- 在 IDEA 中手动导入第三方 jar 包
- 第三方 jar 包的使用
File类的使用
概述
-
File 类的一个对象,代表一个文件或一个文件目录(俗称:文件夹)
-
File 类声明在 java.io 包下
-
File 类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法
并没有涉及到写入或读取文件内容的操作,如果需要读取或写入文件内容,则必须使用 IO 流来完成
-
后续 File 类的对象,常会作为参数传递到流的构造器中,指明读取或写入的 “终点”(可以简单理解成:位置)
说明
-
如何创建 File 类的实例
方式一:File(String filePath)
方式二:File(String parentPath, String childPath)
方式三:File(File parentFile, String childPath)
-
路径
相对路径:相较于某个路径下,指明的路径
绝对路径:包含盘符在内的文件或文件目录的路径
-
路径分隔符: 路径中的每级目录之间用一个路径分隔符隔开
windows:\\ unix:/
-
Java 程序支持跨平台运行,因此路径分隔符要慎用。
-
为了解决这个隐患,File类提供了一个常量:public static final String separator。根据操作系统,动态的提供分隔符。
-
举例:
File file1 = new File("d:\\IO\\info.txt"); File file2 = new File("d:" + File.separator + "IO" + File.separator + "info.txt"); File file3 = new File("d:/IO");
-
案例
package com.laoyang.test.day1;import org.junit.Test;
import java.io.File;/*** @ClassName Test* @Description: File 类的使用* @Author Laoyang* @Date 2021/10/21 9:42*/
public class FileTest {@Testpublic void testOne() {// 构造器1File fileA = new File("hello.txt"); // 相对于当前模块(Demo_15)下/** 路径中的每级目录之间用一个路径分隔符隔开。* 路径分隔符和系统有关:> windows 和 DOS 系统默认使用 “\\” 来表示> UNIX 和 URL 使用 “/” 来表示* Java 程序支持跨平台运行,因此路径分隔符要慎用。*为了解决这个隐患,File类提供了一个常量:> public static final String separator。根据操作系统,动态的提供分隔符。*/File file1 = new File("d:\\info.txt");File file2 = new File("d:" + File.separator + "info.txt");File fileB = new File("D:\\Java\\项目\\JavaDemo\\Demo_15\\hi.txt"); // 绝对路径System.out.println(fileA);System.out.println(fileB);// 构造器2File fileC = new File("D:\\Java\\项目", "JavaDemo");System.out.println(fileC);// 构造器3:在指定路径下找到对应的文件File fileD = new File(fileC, "hi.txt");System.out.println(fileD);}
}
常用方法
File 类的获取功能
方法 | 作用 |
---|---|
public String getAbsolutePath() | 获取绝对路径 |
public String getPath() | 获取路径 |
public String getName() | 获取名称 |
public String getParent() | 获取上层文件目录路径。若无,返回null |
public long length() | 获取文件长度(即:字节数)。不能获取目录的长度 |
public long lastModified() | 获取最后一次的修改时间,毫秒值 |
public String[] list() | 获取指定目录下的所有文件或者文件目录的名称数组 |
public File[] listFiles() | 获取指定目录下的所有文件或者文件目录的名称数组 |
list() 和 listFiles() 适用于文件目录
File类的重命名功能
方法 | 作用 |
---|---|
public boolean renameTo(File dest) | 把文件重命名为指定的文件路径 |
功能简单理解:将以存在硬盘中的文件进行重命名并放入指定的文件目录下
要求:要想保证返回 true(执行成功),需要 fileA 在硬盘中是存在的,并且 fileB 不能再硬盘中存在
File类的判断功能
方法 | 作用 |
---|---|
public boolean isDirectory() | 判断是否是文件目录 |
public boolean isFile() | 判断是否是文件 |
public boolean exists() | 判断是否存在 |
public boolean canRead() | 判断是否可读 |
public boolean canWrite() | 判断是否可写 |
public boolean isHidden() | 判断是否隐藏 |
File类的创建功能(创建硬盘中对应的文件或文件目录)
方法 | 作用 |
---|---|
public boolean createNewFile() | 创建文件。若文件存在,则不创建,返回false |
public boolean mkdir() | 创建文件目录。如果此文件目录存在,就不创建了。 如果此文件目录的上层目录不存在,也不创建。 |
public boolean mkdirs() | 创建文件目录。如果上层文件目录不存在,一并创建 |
注意事项:如果你创建文件或者文件目录没有写盘符路径,那么,默认在项目路径下。
File类的删除功能
方法 | 作用 |
---|---|
public boolean delete() | 删除文件或者文件夹 |
注意事项:
- Java中的删除不会进入回收站。
- 要删除一个文件目录时,该文件目录内不能包含文件或者文件目录,如果包含文件或文件目录就无法进行删除
案例
package com.laoyang.test.day1;import org.junit.Test;
import java.io.File;
import java.io.IOException;/*** @ClassName FileCommonMethod* @Description: File 类的常用方法* @Author Laoyang* @Date 2021/10/21 10:27*/
public class FileCommonMethod {/** File类的获取功能 public String getAbsolutePath():获取绝对路径 public String getPath() :获取路径 public String getName() :获取名称 public String getParent():获取上层文件目录路径。若无,返回null public long length() :获取文件长度(即:字节数)。不能获取目录的长度。 public long lastModified() :获取最后一次的修改时间,毫秒值 如下的两个方法适用于文件目录 public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组 public File[] listFiles() :获取指定目录下的所有文件或者文件目录的名称数组*/@Testpublic void testOne() {File fileA = new File("hello.txt");File fileB = new File("F:\\IO\\hi.txt");// 获取绝对路径:D:\Java\项目\JavaDemo\Demo_15\hello.txtSystem.out.println(fileA.getAbsolutePath());// 获取路径:hello.txtSystem.out.println(fileA.getPath());// 获取名称:hello.txtSystem.out.println(fileA.getName());/*获取上层文件目录路径。若无,返回null:null(根据参数中的路径来进行获取,像这里的相对路径,只有文件名(hello.txt),没有上一层目录信息,所以不管创不创建该文件都是返回 null)*/System.out.println(fileA.getParent());/*获取文件长度(即:字节数)。不能获取目录的长度:0(没创建文件前返回 0,创建文件后根据里面内容的字节数返回对应长度)*/System.out.println(fileA.length());/*获取最后一次的修改时间,毫秒值:0(没创建文件前返回 0,创建文件后根据最后一次修改的时间返回毫秒数(可以使用时间类型转换为标准时间格式进行查看))*/System.out.println(fileA.lastModified());System.out.println("---------------------------------------");// 获取绝对路径:F:\IO\hi.txtSystem.out.println(fileB.getAbsolutePath());// 获取路径:F:\IO\hi.txtSystem.out.println(fileB.getPath());// 获取名称:hi.txtSystem.out.println(fileB.getName());/*获取上层文件目录路径。若无,返回null:F:\IO(因为这里使用的是绝对路径,所以在参数路径中有上一层的文件目录信息(F:\\IO),所以不管最后有没有找到对应的文件,都可以得到上一层的目录信息)*/System.out.println(fileB.getParent());/*获取文件长度(即:字节数)。不能获取目录的长度:0(没创建文件前返回 0,创建文件后根据里面内容的字节数返回对应长度)*/System.out.println(fileB.length());/*获取最后一次的修改时间,毫秒值:0(没创建文件前返回 0,创建文件后根据最后一次修改的时间返回毫秒数(可以使用时间类型转换为标准时间格式进行查看))*/System.out.println(fileB.lastModified());}/** 如下的两个方法适用于文件目录 public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组 public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组*/@Testpublic void testOneB() {File fileA = new File("D:\\Java\\项目\\JavaDemo");/*使用这两个方法的时候,参数目录必须是实际存在的,否则就会报错:NullPointerException*/String[] list = fileA.list();for (String s : list) {System.out.println(s); // 获取指定目录下的所有文件或者文件目录的名称}File[] files = fileA.listFiles();for (File file : files) {System.out.println(file); // 获取指定目录下的所有文件或者文件目录}}/**File类的重命名功能 public boolean renameTo(File dest):把文件重命名为指定的文件路径(该方法的功能简单理解:将以存在硬盘中的文件进行重命名并放入指定的文件目录下)要求:要想保证返回 true(执行成功),需要 fileA 在硬盘中是存在的,并且 fileB 不能再硬盘中存在*/@Testpublic void testTwo() {File fileA = new File("hello.txt");File fileB = new File("F:\\IO\\hi.txt");boolean b = fileB.renameTo(fileA);System.out.println(b);}/** File类的判断功能 public boolean isDirectory():判断是否是文件目录 public boolean isFile() :判断是否是文件 public boolean exists() :判断是否存在 public boolean canRead() :判断是否可读 public boolean canWrite() :判断是否可写 public boolean isHidden() :判断是否隐藏*/@Testpublic void testThree() {/*文件测试*/File fileA = new File("hello.txt");System.out.println(fileA.isDirectory()); // 是否是文件目录System.out.println(fileA.isFile()); //是否是文件System.out.println(fileA.exists()); // 是否存在System.out.println(fileA.canRead()); // 是否可读System.out.println(fileA.canWrite()); // 是否可写System.out.println(fileA.isHidden()); // 是否隐藏System.out.println("------------------------------------");/*文件目录测试,这里我写了两个文件目录,一个是存在的,一个是不存在的,大家可以都测试一下*/File fileB = new File("F:\\IO"); // 目录存在
// fileB = new File("F:\\ioi"); // 目录不存在System.out.println(fileB.isDirectory()); // 是否是文件目录System.out.println(fileB.isFile()); //是否是文件System.out.println(fileB.exists()); // 是否存在System.out.println(fileB.canRead()); // 是否可读System.out.println(fileB.canWrite()); // 是否可写System.out.println(fileB.isHidden()); // 是否隐藏}/** File类的创建功能(创建硬盘中对应的文件或文件目录) public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建。 public boolean mkdirs() :创建文件目录。如果上层文件目录不存在,一并创建注意事项:如果你创建文件或者文件目录没有写盘符路径,那么,默认在项目路径下。*/@Testpublic void testFour() throws IOException {// 文件的创建File fileA = new File("hi.txt");// 如果文件不存在,在进行创建,反之则不创建if (!fileA.exists()) {boolean newFile = fileA.createNewFile();if (newFile) {System.out.println("创建成功!");}}}@Testpublic void testFourB() {// 文件目录的创建File fileA = new File("F:\\IO\\day1\\java1"); // 因为上层目录(day1)不存在,所以会导致创建失败boolean mkdir = fileA.mkdir();if (mkdir) {System.out.println("创建成功A!");}File fileB = new File("F:\\IO\\day1\\java2");boolean mkdirs = fileB.mkdirs();if (mkdirs) {System.out.println("创建成功B!");}}/** File类的删除功能 public boolean delete():删除文件或者文件夹删除注意事项:1. Java中的删除不会进入回收站。2. 要删除一个文件目录时,该文件目录内不能包含文件或者文件目录,如果包含文件或文件目录就无法进行删除*/@Testpublic void testFive() {// 因为它里面还有一个 java2 的文件夹,所以这里是不能进行删除的File fileA = new File("F:\\IO\\day1");if (fileA.exists()) {boolean delete = fileA.delete();if (delete)System.out.println("删除成功!");} else {System.out.println("文件不存在!");}}
}
练习
判断指定目录下是否有后缀名为.jpg的文件,如果有,就输出该文件名称
package com.laoyang.test.day2;import org.junit.Test;
import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;/*** @ClassName FindJPGFileTest* @Description: 判断指定目录下是否有后缀名为.jpg的文件,如果有,就输出该文件名称* @Author Laoyang* @Date 2021/10/21 17:10*/
public class FindJPGFileTest {/*** 方式一:使用 list() 方法*/@Testpublic void test1(){File srcFile = new File("F:\\IO\\day1\\java1");String[] fileNames = srcFile.list(); // 获取到当前目录下的所有文件名for(String fileName : fileNames){if(fileName.endsWith(".jpg")){ // 根据当前获取到的文件名进行判断System.out.println(fileName);}}}/*** 方式二:使用 listFiles() 方法* Ps:其实跟第一种方式差不多,也是要使用 String 类中的 endsWith() 方法进行判断*/@Testpublic void test2(){File srcFile = new File("F:\\IO\\day1\\java1");File[] listFiles = srcFile.listFiles();for(File file : listFiles){if(file.getName().endsWith(".jpg")){System.out.println(file.getAbsolutePath());}}}/** 方式三:* File 类提供了两个文件过滤器方法* public String[] list(FilenameFilter filter)* public File[] listFiles(FileFilter filter)*/@Testpublic void test3() {File srcFile = new File("F:\\IO\\day1\\java1");// FilenameFilterFile[] subFiles = srcFile.listFiles(new FilenameFilter() {@Overridepublic boolean accept(File dir, String name) {return name.endsWith(".jpg");}});for (File file : subFiles) {System.out.println(file.getAbsolutePath());}// FileFilterFile file = new File("F:\\IO\\day1\\java1");File[] files = file.listFiles(new FileFilter() {@Overridepublic boolean accept(File pathname) {return pathname.getName().endsWith(".jpg");}});for (File fileA : files) {System.out.println(fileA.getAbsolutePath());}}
}
这里有三种实现方式,大家可自行测试
遍历指定目录所有文件名称,包括子文件目录中的文件
拓展1:并计算指定目录占用空间的大小
拓展2:删除指定文件目录及其下的所有文件
package com.laoyang.test.day2;import org.junit.Test;
import java.io.File;/*** @ClassName ListFilesTest* @Description: 遍历指定目录所有文件名称,包括子文件目录中的文件。* @Author Laoyang* @Date 2021/10/21 17:10*/
public class ListFilesTest {/*** 方式一:递归获取*/public static void main(String[] args) {// 递归:文件目录// 1.创建目录对象File dir = new File("F:\\IO");// 2.打印目录的子文件printSubFile(dir);}public static void printSubFile(File dir) {// 打印目录的子文件File[] subfiles = dir.listFiles();for (File f : subfiles) {// 判断是不是文件目录if (f.isDirectory()) {// 如果是文件目录,则使用递归的方式在次进行判断,直到没有文件目录为止printSubFile(f);} else {// 如果不是文件目录则直接打印该文件的绝对路径System.out.println(f.getAbsolutePath());}}}/*** 方式二:循环实现* 列出 file 目录的下级内容,仅列出一级的话* 使用 File 类的 String[] list() 比较简单* > 如果只是打印下一层的文件夹信息的话可以采用 String[] list 的方式*/@Testpublic void testOne() {// 1.创建目录对象File dir = new File("F:\\IO\\day1");// 2.打印目录的子文件名和文件夹名listSubFiles(dir);}public void listSubFiles(File file) {if (file.isDirectory()) {String[] all = file.list();for (String s : all) {System.out.println(s);}} else {System.out.println(file + "是文件!");}}/*** 方式三*/@Testpublic void testThree() {// 1.创建目录对象File dir = new File("F:\\IO");// 2.打印目录的子文件listAllSubFiles(dir);}/**列出 file 目录的下级,如果它的下级还是目录,接着列出下级的下级,依次类推建议使用 File 类的 File[] listFiles()*/public void listAllSubFiles(File file) {if (file.isFile()) {System.out.println(file);} else {File[] all = file.listFiles();/*如果 all[i] 是文件,直接打印如果 all[i] 是目录,接着再获取它的下一级注意:大家这里不要被搞混了1. 这个递归是在循环中调用的,所以递归方法执行结束的条件是:没有下一层就结束(所以大家不用困惑为什么找到文件后不会直接结束该方法)2. 循环中的递归方法参数,除了第一次是 file,其它的都是使用 f,所以可以直接找到下一层然后继续进行查找假设:第一次执行,发现不是文件,则使用循环遍历该文件目录下的所有文件(D:\\IO 下的所有文件信息)第二次执行,发现不是文件,则获取子目录中的所有信息(文件与文件夹)(D:\\IO\\day1 下的所有文件信息)第三次执行,发现是文件,则打印完该文件信息后,再次执行 else 中没有执行完的循环,遍历下一个文件/或文件夹再次进行判断......以此类推,直到没有下一层,或没有文件为止最后一点:当一个目录从头到尾找完以后,就会开始在第二个目录> 比如:D:\\IO\\day1 和 D:\\IO\\day1,此时 IO 目录下有两个文件夹,这个时候会先遍历完 day1,然后在去遍历 day2,> 所以并不会出现进入一个子目录深处之后不能再遍历主目录(IO)下的其他文件夹的问题*/for (File f : all) {System.out.println("------------" + f);listAllSubFiles(f); // 递归调用:自己调用自己就叫递归}}}
}
上面也采用了三种实现方式
- 方式一使用了递归的方式实现
- 方式二只会获取到子级的信息,不会获取到子级的子级,比如:A/B/C,从A开始获取,那么最后只能获取到B的文件信息,而不能获取到C
- 方式三也是使用了递归,但是跟第一种有一丢丢不一样,大家可以结合注释进行理解
扩展
package com.laoyang.test.day2;import org.junit.Test;
import java.io.File;/*** @ClassName ListFilesTest* @Description: 遍历指定目录所有文件名称,包括子文件目录中的文件。* @Author Laoyang* @Date 2021/10/21 17:10*/
public class ListFilesTest {/*** 扩展1:求指定目录所在空间的大小(求任意一个目录的总大小)*/@Testpublic void testFour() {File file = new File("F:\\IO");long directorySize = getDirectorySize(file);System.out.println(directorySize);}/**求任意一个目录的总大小*/public long getDirectorySize(File file) {// file是文件,那么直接返回file.length()// file是目录,把它的下一级的所有大小加起来就是它的总大小long size = 0;if (file.isFile()) {size += file.length();} else {File[] all = file.listFiles();// 获取file的下一级// 累加all[i]的大小for (File f : all) {/*f 的大小; 这个地方不要被迷惑了,因为 size 加上的是 getDirectorySize() 方法的返回值而且这个 size 一直存在循环中,所以并不会被外面 size = 0 给影响*/size += getDirectorySize(f);}}return size;}/*** 扩展2:删除指定的目录*/@Testpublic void testFive() {File file = new File("F:\\IO");deleteDirectory(file);}public void deleteDirectory(File file) {/*如果file是文件,直接delete如果file是目录,先把它的下一级干掉,然后删除自己*/if (file.isDirectory()) {File[] all = file.listFiles();/*循环删除的是 file 的下一层;f代表file的每一个下级逻辑:先把子目录下的文件删除完,在把子目录给删除,最后把当前的目录给删除*/for (File f : all) {deleteDirectory(f);}}// 删除自己boolean delete = file.delete();if (delete) {System.out.println("删除成功!");}}
}
IO流原理及流的分类
Java IO 流原理
- I/O 是 Input/Output 的缩写, I/O 技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等。
- Java 程序中,对于数据的输入/输出操作以 “流(stream)” 的方式进行。
- java.io 包下提供了各种 “流” 类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。
Java IO原理
- 输入 input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
- 输出 output:将程序(内存)数据输出到磁盘、光盘等存储设备中。
流的分类
- 操作数据单位:字节流、字符流
- 数据的流向:输入流、输出流
- 流的角色:节点流、处理流
Java 的 IO 流共涉及 40 多个类,实际上非常规则,都是从如下4个抽象基类派生的。
抽象基类 | 字节流 | 字符流 |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
InputStream 方法说明
- int read() :从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。
- int read(byte[] b):从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。否则以整数形式返回实际读取的字节数。
- int read(byte[] b, int off,int len):将输入流中最多 len 个数据字节读入 byte 数组。尝试读取 len 个字节,但读取的字节也可能小于该值。以整数形式返回实际读取的字节数。如果因为流位于文件末尾而没有可用的字节,则返回值 -1。
- public void close() throws IOException:关闭此输入流并释放与该流关联的所有系统资源。
Reader 方法说明
- int read():读取单个字符。作为整数读取的字符,范围在 0 到 65535 之间 (0x00-0xffff)(2个 字节的Unicode码),如果已到达流的末尾,则返回 -1
- int read(char[] cbuf):将字符读入数组。如果已到达流的末尾,则返回 -1。否则返回本次读取的字符数。
- int read(char[] cbuf,int off,int len):将字符读入数组的某一部分。存到数组 cbuf 中,从 off 处开始存储,最多读 len 个字 符。如果已到达流的末尾,则返回 -1。否则返回本次读取的字符数。
- public void close() throws IOException:关闭此输入流并释放与该流关联的所有系统资源。
OutputStream 方法说明
- void write(int b) OutputStream:将指定的字节写入此输出流。write 的常规协定是:向输出流写入一个字节。要写入的字节是参数 b 的八个低位。b 的 24 个高位将被忽略。 即写入0~255 范围的。
- void write(byte[] b):将 b.length 个字节从指定的 byte 数组写入此输出流。write(b) 的常规协定是:应该与调用 write(b, 0, b.length) 的效果完全相同。
- void write(byte[] b,int off,int len):将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
- public void flush()throws IOException:刷新此输出流并强制写出所有缓冲的输出字节,调用此方法指示应将这些字节立即写入它们预期的目标。
- public void close() throws IOException:关闭此输出流并释放与该流关联的所有系统资源。
Writer 方法说明
- void write(int c):写入单个字符。要写入的字符包含在给定整数值的 16 个低位中,16 高位被忽略。 即 写入0 到 65535 之间的Unicode码。
- void write(char[] cbuf):写入字符数组。
- void write(char[] cbuf,int off,int len):写入字符数组的某一部分。从 off 开始,写入 len 个字符
- void write(String str):写入字符串。
- void write(String str,int off,int len):写入字符串的某一部分。
- void flush():刷新该流的缓冲,则立即将它们写入预期目标。
- public void close() throws IOException:关闭此输出流并释放与该流关联的所有系统资源。
因为字符流直接以字符作为操作单位,所以 Writer 可以用字符串来替换字符数组, 即以 String 对象作为参数。
说明
- 输入流和输出流方法基本都是通用的,使用的套路也都差不多,后面的案例中大家就可以发现
- 这里是比较官方的解释,所以看起来部分解释看起来比较模糊或者官方,后面的使用中有比较接地气的解释,供大家理解
IO 流体系
节点流 与 处理流的理解
节点流:直接从数据源或目的地读写数据
处理流:不直接连接到数据源或目的地,而是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。
节点流 (也可称为:文件流) 的使用
流对象
节点流 | 字节流 | 字符流 |
---|---|---|
输入流 | FileInputStream | FileReader |
输出流 | FileOutputStream | FileWriter |
字节流 read() 重载方法
int read()
int read(byte[] b)
int read(byte[] b, int off, int len)
字符流 read() 重载方法
int read()
int read(char [] c)
int read(char [] c, int off, int len)
注意:程序中打开的文件 IO 资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以需要显式关闭文件 IO 资源。
注意点
- 定义文件路径时,注意:可以用 “/” 或者 “\”。
- 在写入一个文件时,如果使用构造器 FileOutputStream(file),则目录下有同名文件将被覆盖。
- 如果使用构造器 FileOutputStream(file, true),则目录下的同名文件不会被覆盖, 会在文件内容末尾追加内容。
- 在读取文件时,必须保证该文件已存在,否则报异常。
- 字节流操作字节,比如:.mp3,.avi,.rmvb,mp4,.jpg,.doc,.ppt
- 字符流操作字符,只能操作普通文本文件。最常见的文本文 件:.txt,.java,.c,.cpp 等语言的源代码。尤其注意 .doc, excel, ppt 这些不是文本文件。
相对路径中单元测试与 main() 方法的不同
-
如果使用单元测试方法进行测试,默认找的是当前模块下对应的文件(只会在当前模块/项目中去找对应的文件)
-
如果使用 main() 方法测试,默认找的就是当前工程下对应的文件(如果使用的是父子工程的时候,就会默认去父工程中找对应的文件)
如果是使用 Eclipse,那么不管是在单元测试中还是在 main() 方法中,相对路径默认找的都是当前工程下的对应文件
案例
package com.laoyang.test.day3;import org.junit.Test;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;/*** @ClassName IOTest* @Description: IO 流的使用* @Author Laoyang* @Date 2021/10/23 10:32*/
public class IOTest {/*** 单元测试打印结果* D:\Java\项目\JavaDemo\Demo_15\hello.txt* D:\Java\项目\JavaDemo\Demo_15\Demo_15\hello.txt** main 方法打印结果* D:\Java\项目\JavaDemo\hello.txt* D:\Java\项目\JavaDemo\Demo_15\hello.txt*/public static void main(String[] args) {File file = new File("hello.txt"); // 相较于当前工程(JavaDemo)System.out.println(file.getAbsoluteFile());File fileB = new File("Demo_15\\hello.txt");System.out.println(fileB.getAbsolutePath());}
}
这里只演示了 mian() 方法的,大家有兴趣可以在创建一个单元测试方法进行对比
节点流的基本步骤
- 实例化 File 类的对象,指明要操作的文件
- 提供具体的流
- 数据的输入、输出操作
- 流的关闭
字符流案例
输入流 - FileReader
说明点
- read() 方法理解:返回读取的一个字符,如果达到文件末尾,返回 -1
- 异常的处理:为了保证资源一定可以执行关闭操作,需要使用 try-catch-finally 进行处理
- 读取的文件一定要存在,否则就会报 FileNotFoundException 异常
package com.laoyang.test.day3;import org.junit.Test;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;/*** @ClassName IOTest* @Description: IO 流的使用* @Author Laoyang* @Date 2021/10/23 10:32*/
public class IOTest {/*** 节点流的使用* FileReader* FileWriter* 将 Demo_15(当前模块)下的 hello.txt 文件内容读入程序中,并输出到控制台*/@Testpublic void testOne() {FileReader reader = null;try {// 1. 实例化 File 类的对象,指明要操作的文件File file = new File("hello.txt"); // 相较于当前 Module(Demo_15)// 2. 提供具体的流reader = new FileReader(file);/*3. 数据的读取read():返回读取到的一个字符,如果达到文件末尾,返回 -1*/// 方式一
// int read = reader.read();
// while (read != -1) {
// System.out.print((char) read);
// read = reader.read();
// }// 方式二,语法上针对于方式一的修改,性能方面没有什么区别int read;while ((read = reader.read()) != -1) {System.out.print((char) read);}} catch (IOException e) {e.printStackTrace();} finally {// 这里的 try{}catch(){} 写在 if 里面或者外面都是可以的try {/*这里进行判断是为了防止 reader 还没有被实例化的时候就出现异常的问题如果 reader 没有被实例化,然后直接执行了 close,那么是会导致报错的(就好比你在一个没有水的河里还想游泳)*/if (reader != null) {// 4. 数据的关闭操作(一定要加上!!!)reader.close();}} catch (IOException e) {e.printStackTrace();}}}
}
对 read() 方法的操作升级:使用 read() 的重载方法
-
read(char[] cbuf):返回每次读取 cbuf 数组中的字符的个数,如果达到文件末尾,返回 -1
根据 cbuf 数组设置的长度来分次读取对应文件中的字符,比如这里设置为 5,那么每一次都会从文件中读取五个字符
-
read(char[] cbuf, int off, int len):cbuf(获取字符的数组大小),off(从什么位置开始存储),len(每一次获取字符的长度)
根据 len 设置的长度来分次读取文件中的字符,即使数组长度为 100,len 设置为 5,那么每一次最多只会从文件中读取 5 个字符存储在 cbuf 数组中;off 表示把读取的字符从数组哪一个位置开始存储,比如我不想从头开始存放就可以设置为 0 以外的下标值
这个重载方法在开发中并不推荐使用!!!
package com.laoyang.test.day3;import org.junit.Test;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;/*** @ClassName IOTest* @Description: IO 流的使用* @Author Laoyang* @Date 2021/10/23 10:32*/
public class IOTest {/*** 对 read() 方法的操作升级:使用 read() 的重载方法*/@Testpublic void testTwo() {FileReader reader = null;try {// 1. File 类的实例化File file = new File("hello.txt");// 2. FileReader 流的实例化reader = new FileReader(file);/*3. 读取的操作read(char[] cbuf):返回每次读取 cbuf 数组中的字符的个数,如果达到文件末尾,返回 -1> 根据 cbuf 数组设置的长度来分次读取对应文件中的字符,比如这里设置为 5,那么每一次都会从文件中读取五个字符read(char[] cbuf, int off, int len):cbuf(获取字符的数组大小),off(从什么位置开始存储),len(每一次获取字符的长度)> 根据 len 设置的长度来分次读取文件中的字符,即使数组长度为 100,len 设置为 5,那么每一次最多只会从文件中读取 5 个字符存储在 cbuf 数组中> off 表示把读取的字符从数组哪一个位置开始存储,比如我不想从头开始存放就可以设置为 0 以外的下标值> 这个重载方法在开发中并不推荐使用!!!*/char[] cbuf = new char[5];int read = reader.read(cbuf);while (read != -1) {// 方式一// 错误写法
// for (int i = 0; i < cbuf.length; i++) {
// System.out.print(cbuf[i]);
// }// 正确写法
// for (int i = 0; i < read; i++) {
// System.out.print(cbuf[i]);
// }// 方式二// 错误写法
// String str = new String(cbuf);
// System.out.println(str);// 正确写法String str = new String(cbuf, 0, read);System.out.print(str);read = reader.read(cbuf);}// 4. 输出的操作,因为这里只是演示read()重载方法的使用,就不写读取操作了} catch (IOException e) {e.printStackTrace();} finally {// 5. 资源的关闭try {if (reader != null) {reader.close();}} catch (IOException e){e.printStackTrace();}}}
}
输出流 - FileWriter
说明点
-
输出操作,对应的 File 是可以不存在的,并不会出现异常。
-
File 对应的硬盘中的文件如果不存在,在输出的过程中会自动创建此文件。
File 对应的硬盘中的文件如果存在:
如果流使用的构造器是:FileWriter(file, false) / FileWriter(file):则会对原有文件的覆盖
如果流使用的构造器是:FileWriter(file, true) :则不会对原有文件进行覆盖,而是在原文件的内容末尾进行新增
package com.laoyang.test.day3;import org.junit.Test;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;/*** @ClassName IOTest* @Description: IO 流的使用* @Author Laoyang* @Date 2021/10/23 10:32*/
public class IOTest {/*** 从内存中写出数据到硬盘的文件里*/@Testpublic void testThree() {FileWriter writer = null;try {// 1. 提供 File 类的对象,指明写入到的文件File file = new File("helloA.txt");/*2. 提供 FileWriter 对象,用于数据的写入第二个参数表示新增或覆盖,为 true 表示往文件后面新增,为 false 表示对文件进行覆盖*/writer = new FileWriter(file, true);/*3. 写入的操作write(String str):案例:writer.write("Java!");write(char cbuf[]):案例:writer.write("Java!".toCharArray());*/writer.write("java is the best language in the world!\n");writer.write("none of them!");} catch (IOException e) {e.printStackTrace();} finally {// 4. 流资源的关闭try {if (writer != null) {writer.close();}} catch (IOException e) {e.printStackTrace();}}}
}
输入流和输出流的结合使用
package com.laoyang.test.day3;import org.junit.Test;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;/*** @ClassName IOTest* @Description: IO 流的使用* @Author Laoyang* @Date 2021/10/23 10:32*/
public class IOTest {/*** 输入流和输出流的结合使用*/@Testpublic void testFour() {FileReader reader = null;FileWriter writer = null;try {// 1. 创建 File 类的对象,指明读取和写入的文件File read = new File("hello.txt");File write = new File("helloA.txt");// 2. 创建输入流和输出流的对象reader = new FileReader(read);writer = new FileWriter(write);// 3. 输入和输出操作char[] cbuf = new char[5];// 记录每次读取到 cbuf 数组中的字符的个数int len = reader.read(cbuf);while (len != -1) {// 每次写入 len 个字符writer.write(cbuf, 0, len);len = reader.read(cbuf);}} catch (IOException e) {e.printStackTrace();} finally {// 4. 关闭流资源,此时的关闭顺序还没什么规定// 方式一
// try {
// if (writer != null) {
// writer.close();
// }
// } catch (IOException e) {
// e.printStackTrace();
// } finally {
// try {
// if (reader != null) {
// reader.close();
// }
// } catch (IOException e) {
// e.printStackTrace();
// }
// }// 方式二:建议使用try {if (writer != null) {writer.close();}} catch (IOException e) {e.printStackTrace();}try {if (reader != null) {reader.close();}} catch (IOException e) {e.printStackTrace();}}}
}
流程都是差不多的
字符流不能处理图片文件的测试
package com.laoyang.test.day3;import org.junit.Test;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;/*** @ClassName FilePictureTest* @Description: 字符流不能处理图片文件的测试* @Author Laoyang* @Date 2021/10/23 16:08*/
public class FilePictureTest {/**说明:不能使用字符流来处理图片等字节数据,字符流只能处理字符数据*/@Testpublic void testOne() {FileReader reader = null;FileWriter writer = null;try {// 1. 创建 File 对象,用于指明读取和写入的文件File fileA = new File("2.jpg");File fileB = new File("3.jpg");// 2. 实例化输入流和输出流的对象reader = new FileReader(fileA);writer = new FileWriter(fileB);// 3. 进行读取和写入操作char[] cbuf = new char[5];int read = reader.read(cbuf);while (read != -1) {// 可以写入成功,但是写入的文件无法正常使用,因为只能处理字符文件,图片属于字节文件writer.write(cbuf, 0, read);read = reader.read(cbuf);}} catch (IOException e) {e.printStackTrace();} finally{// 4. 关闭流操作try {if (writer != null)writer.close();} catch (IOException e) {e.printStackTrace();}try {if (reader != null)reader.close();} catch (IOException e) {e.printStackTrace();}}}
}
字节流案例
说明点
-
对于文本文件(.txt、.java、.c、.cpp等),使用字符流处理
源文档 - 内存读取(如果在内存中读取的话就可能会出现乱码,例如:testOne())
源文档 - 复制(在复制的过程中不会在内存中进行读取,而是在复制完以后直接提供给我们查看,所以就不会出现乱码,例如:testFour())
-
对于非文本文件(.jpg、.mp3、.mp4、.avi、.ppt等),使用字节流处理
输入流 - FileInputStream
package com.laoyang.test.day3;import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;/*** @ClassName FileByteStreamTest* @Description: 字节流的使用(FileInputStream 和 FileOutputStream 的使用)* @Author Laoyang* @Date 2021/10/23 16:21*/
public class FileByteStreamTest {/*** 字节流 - FileInputStream 的使用* 使用字节流 FileInputStream 处理文本文件,可能会出现乱码*/@Testpublic void testOne() {FileInputStream inputStream = null;try {// 1. 创建 File 对象,指明要读取的文件File file = new File("hello.txt");// 2. 实例化字节流对象inputStream = new FileInputStream(file);// 3. 读取操作byte[] buffer = new byte[5];int read = inputStream.read(buffer);while (read != -1) {// 一个汉字占三个字节String str = new String(buffer, 0, read);System.out.print(str); // 出现乱码read = inputStream.read(buffer);}} catch (IOException e) {e.printStackTrace();} finally {// 4. 关闭流操作try {if (inputStream != null) {inputStream.close();}} catch (IOException e) {e.printStackTrace();}}}
}
输入流与输出流(FileOutputStream)的结合使用
package com.laoyang.test.day3;import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;/*** @ClassName FileByteStreamTest* @Description: 字节流的使用(FileInputStream 和 FileOutputStream 的使用)* @Author Laoyang* @Date 2021/10/23 16:21*/
public class FileByteStreamTest {/*** 实现对图片的复制操作*/@Testpublic void testTwo() {FileInputStream inputStream = null;FileOutputStream outputStream = null;try {// 指明要复制的文件File fileA = new File("2.jpg");File fileB = new File("3.jpg");// 实例化字节对象inputStream = new FileInputStream(fileA);outputStream = new FileOutputStream(fileB);// 复制的过程byte[] buffer = new byte[5];int read = inputStream.read(buffer);while (read != -1) {outputStream.write(buffer, 0, read);read = inputStream.read(buffer);}System.out.println("复制成功!");} catch (IOException e) {e.printStackTrace();} finally {// 关闭流if (outputStream != null) {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}if (inputStream != null) {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}}}
}
指定路径下文件的复制 - 视频 & 文档
package com.laoyang.test.day3;import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;/*** @ClassName FileByteStreamTest* @Description: 字节流的使用(FileInputStream 和 FileOutputStream 的使用)* @Author Laoyang* @Date 2021/10/23 16:21*/
public class FileByteStreamTest {/*** 指定路径下文件的复制 - 视频*/@Testpublic void testThree() {long currentTimeMillis = System.currentTimeMillis();String strPath = "C:\\Users\\Laoyang\\Videos\\B站\\其它\\呆呆王的一天-英文\\呆呆王的一天.mp4";String destPath = "C:\\Users\\Laoyang\\Videos\\B站\\其它\\呆呆王的一天-英文\\呆王的一天.mp4";copyFile(strPath, destPath);long currentTimeMillis1 = System.currentTimeMillis();System.out.println("总耗时:" + (currentTimeMillis1 - currentTimeMillis));}/*** 指定路径下文件的复制 - 文档*/@Testpublic void testFour() {long currentTimeMillis = System.currentTimeMillis();String strPath = "hello.txt";String destPath = "helloA.txt";copyFile(strPath, destPath);long currentTimeMillis1 = System.currentTimeMillis();System.out.println("总耗时:" + (currentTimeMillis1 - currentTimeMillis));}/*** 通用方法*/public void copyFile(String strPath, String destPath) {FileInputStream inputStream = null;FileOutputStream outputStream = null;try {// 指明要复制的文件File fileA = new File(strPath);File fileB = new File(destPath);// 实例化字节对象inputStream = new FileInputStream(fileA);outputStream = new FileOutputStream(fileB);// 复制的过程byte[] buffer = new byte[1024];int read = inputStream.read(buffer);while (read != -1) {outputStream.write(buffer, 0, read);read = inputStream.read(buffer);}} catch (IOException e) {e.printStackTrace();} finally {// 关闭流if (outputStream != null) {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}if (inputStream != null) {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}}}
}
因为逻辑代码都差不多,所以这里为了方便就直接使用通用方法减少代码量了
处理流一:缓存流的使用
说明
-
为了提高数据读写的速度,Java API 提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组,缺省使用 8192 个字节 (8Kb) 的缓冲区。
BufferedInputStream 部分源码
public class BufferedInputStream extends FilterInputStream {// 缓冲区,先把读取到的数据存储到 8192 大小的缓冲区内,然后在一次性获取到里面的数据private static int DEFAULT_BUFFER_SIZE = 8192; }比如:现在有一个10L的水杯和一个5L的水杯,要求倒满100L的水缸(期间两个水杯同时可以装满),由此可见,最后先倒满的肯定是 10L 的水杯
-
处理流,就是 “套接” 在已有的流的基础上
-
当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区
-
当使用 BufferedInputStream 读取字节文件时,BufferedInputStream 会一次性从文件中读取 8192个(8Kb),存在缓冲区中,直到缓冲区装满了,才重新从文件中读取下一个 8192 个字节数组。
-
向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满, BufferedOutputStream 才会把缓冲区中的数据一次性写到文件里。使用方法 flush() 可以强制将缓冲区的内容全部写入输出流
-
关闭流的顺序和打开流的顺序相反。只要关闭最外层流即可,关闭最外层流也会相应关闭内层节点流
-
flush() 方法的使用:手动将buffer中内容写入文件
-
如果是带缓冲区的流对象的 close() 方法,不但会关闭流,还会在关闭流之前刷新缓冲区,关闭后不能再写出
流对象
缓冲流 | 字节流 | 字符流 |
---|---|---|
输入流 | BufferedInputStream | BufferedReader |
输出流 | BufferedOutputStream | BufferedWriter |
案例
-
缓冲流
BufferedInputStream:处理字节输入
BufferedOutputStream:处理字节输出
BufferedReader:处理字符输入
BufferedWriter处理字符输出 -
作用:提高流的读取和写入速度
提高读写速度的原因:内部提供了一个缓冲区
BufferedInputStream 和 BufferedOutputStream 的使用
package com.laoyang.test.day3;import org.junit.Test;
import java.io.*;/*** @ClassName BufferedTest* @Description: 处理流之一:缓冲流的使用* @Author Laoyang* @Date 2021/10/23 17:04*/
public class BufferedTest {/*** 使用 BufferedInputStream 和 BufferedOutputStream 实现非文本文件的复制*/@Testpublic void testOne() {FileInputStream inputStream = null;FileOutputStream outputStream = null;BufferedInputStream bis = null;BufferedOutputStream bos = null;try {// 1. 创建 File 对象,指明要复制的文件File fileA = new File("2.jpg");File fileB = new File("4.jpg");// 2. 实例化流对象// 2.1:实例化节点流对象inputStream = new FileInputStream(fileA);outputStream = new FileOutputStream(fileB);// 2.2:实例化缓冲流对象bis = new BufferedInputStream(inputStream);bos = new BufferedOutputStream(outputStream);// 3. 复制的细节操作,读取、写入byte[] buffer = new byte[10];int read = bis.read(buffer);while (read != -1) {bos.write(buffer, 0, read);read = bis.read(buffer);}} catch (IOException e) {e.printStackTrace();} finally {/*4. 资源关闭要求:先关闭外层的,在关闭内层的说明:在关闭外层流的同时,内层流也会自动的进行关闭,关于内层流的关闭,我们可以省略*/if (bos != null) {try {bos.close();} catch (IOException e) {e.printStackTrace();}}if (bis != null) {try {bis.close();} catch (IOException e) {e.printStackTrace();}}// 内层流的关闭可以省略
// inputStream.close();
// outputStream.close();}}/*** 实现文件复制的方法 - 视频* 比较缓冲流和字节流的读写速度*/@Testpublic void testTwo() {long currentTimeMillis = System.currentTimeMillis();String srcPath = "C:\\Users\\Laoyang\\Videos\\B站\\其它\\呆呆王的一天-英文\\呆呆王的一天.mp4";String destPath = "C:\\Users\\Laoyang\\Videos\\B站\\其它\\呆呆王的一天-英文\\呆河马的一天.mp4";copyFileWithBuffered(srcPath, destPath);long currentTimeMillis1 = System.currentTimeMillis();System.out.println("总耗时:" + (currentTimeMillis1 - currentTimeMillis));}/*** 通用方法*/public void copyFileWithBuffered(String srcPath, String destPath) {FileInputStream inputStream = null;FileOutputStream outputStream = null;BufferedInputStream bis = null;BufferedOutputStream bos = null;try {// 1. 创建 File 对象,指明要复制的文件File fileA = new File(srcPath);File fileB = new File(destPath);// 2. 实例化流对象// 2.1:实例化节点流对象inputStream = new FileInputStream(fileA);outputStream = new FileOutputStream(fileB);// 2.2:实例化缓冲流对象bis = new BufferedInputStream(inputStream);bos = new BufferedOutputStream(outputStream);// 3. 复制的细节操作,读取、写入byte[] buffer = new byte[1024];int read = bis.read(buffer);while (read != -1) {bos.write(buffer, 0, read);read = bis.read(buffer);}} catch (IOException e) {e.printStackTrace();} finally {// 4. 资源关闭if (bos != null) {try {bos.close();} catch (IOException e) {e.printStackTrace();}}if (bis != null) {try {bis.close();} catch (IOException e) {e.printStackTrace();}}}}
}
BufferedReader 和 BufferedWriter 的使用
package com.laoyang.test.day3;import org.junit.Test;
import java.io.*;/*** @ClassName BufferedTest* @Description: 处理流之一:缓冲流的使用* @Author Laoyang* @Date 2021/10/23 17:04*/
public class BufferedTest {/*** 使用 BufferedReader 和 BufferedWriter 实现文本文件的复制*/@Testpublic void testThree() {BufferedReader reader = null;BufferedWriter writer = null;try {// 1. 创建 File 对象,指明要复制的文件File fileA = new File("hello.txt");File fileB = new File("hi.txt");// 2. 实例化缓冲流对象(此处为了方便就直接使用匿名对象的方式传参了)reader = new BufferedReader(new FileReader(fileA));writer = new BufferedWriter(new FileWriter(fileB));// 3. 读写(复制)操作// 方式一:使用 char[],read() 每次读取指定大小的字符
// char[] cbuf = new char[5];
// int read = reader.read(cbuf);
// while (read != -1) {
// writer.write(cbuf, 0, read);
// read = reader.read(cbuf);
// }// 方式二:使用 String,readLine() 每次读取一行字符String data = reader.readLine();while (data != null) {// data 中是不包含换行符的
// writer.write(data); // 不换行// 换行方式一:\n
// writer.write(data + "\n");//换行方式二:newLine()writer.write(data);writer.newLine();data = reader.readLine();}} catch (IOException e) {e.printStackTrace();} finally {// 关闭流if (reader != null) {try {reader.close();} catch (IOException e) {e.printStackTrace();}}if (writer != null) {try {writer.close();} catch (IOException e) {e.printStackTrace();}}}}
}
小练习
练习一
分别使用节点流:FileInputStream、FileOutputStream和缓冲流:BufferedInputStream、BufferedOutputStream实现文本文件/图片/视频文件的 复制。并比较二者在数据复制方面的效率
package com.laoyang.test.day4;import org.junit.Test;
import java.io.*;/*** @ClassName CopyExerciseTest* @Description: 练习一:分别使用节点流:FileInputStream、FileOutputStream和缓冲流:BufferedInputStream、BufferedOutputStream实现文本文件/图片/视频文件的 复制。并比较二者在数据复制方面的效率* @Author Laoyang* @Date 2021/10/24 15:58*/
public class CopyExerciseTest {/*** 节点流*/@Testpublic void testFile() {long start = System.currentTimeMillis();// 复制文件(1毫秒,文件太小了)nodeFlow("hello.txt", "新文件.txt");// 复制图片(13-14毫秒)
// nodeFlow("2.jpg", "新图片.jpg");// 复制视频(83-86毫秒)
// nodeFlow("C:\\Users\\Laoyang\\Videos\\B站\\其它\\呆呆王的一天-英文\\呆呆王的一天.mp4", "新视频.mp4");long finish = System.currentTimeMillis();System.out.println("总耗时:" + (finish - start));}/*** FileInputStream、FileOutputStream 方法*/public void nodeFlow(String srcPath, String destPath) {FileInputStream inputStream = null;FileOutputStream outputStream = null;try {inputStream = new FileInputStream(srcPath);outputStream = new FileOutputStream(destPath);byte[] buffer = new byte[1024];int read = inputStream.read(buffer);while (read != -1) {outputStream.write(buffer, 0, read);read = inputStream.read(buffer);}} catch (IOException e) {e.printStackTrace();} finally {if (outputStream != null) {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}if (inputStream != null) {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 缓冲流*/@Testpublic void testBuffered() {long start = System.currentTimeMillis();// 复制文件(0毫秒,文件太小了)bufferStream("hello.txt", "新文件.txt");// 复制图片(3-4毫秒)
// bufferStream("2.jpg", "新图片.jpg");// 复制视频(18-19毫秒)
// bufferStream("C:\\Users\\Laoyang\\Videos\\B站\\其它\\呆呆王的一天-英文\\呆呆王的一天.mp4", "新视频.mp4");long finish = System.currentTimeMillis();System.out.println("总耗时:" + (finish - start));}public void bufferStream(String srcPath, String descPath) {BufferedInputStream input = null;BufferedOutputStream output = null;try {input = new BufferedInputStream(new FileInputStream(srcPath));output = new BufferedOutputStream(new FileOutputStream(descPath));byte[] buffer = new byte[1024];int read = input.read(buffer);while (read != -1) {output.write(buffer, 0, read);read = input.read(buffer);}} catch (IOException e) {e.printStackTrace();} finally {if (output != null) {try {output.close();} catch (IOException e) {e.printStackTrace();}}if (input != null) {try {input.close();} catch (IOException e) {e.printStackTrace();}}}}
}
练习二
实现图片加密解密操作。
package com.laoyang.test.day4;import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;/*** @ClassName PicTest* @Description: 练习二:实现图片加密解密操作。* @Author Laoyang* @Date 2021/10/24 15:13*/
public class PicTest {/*** 加密操作*/@Testpublic void testOne() {FileInputStream inputStream = null;FileOutputStream outputStream = null;try {File fileA = new File("2.jpg");File fileB = new File("222.jpg");inputStream = new FileInputStream(fileA);outputStream = new FileOutputStream(fileB);byte[] buffer = new byte[10];int read = inputStream.read(buffer);while (read != -1) {// 字符数组进行修改// 错误写法:此时改的只是循环中的局部变量// for (byte b : buffer) {// b = (byte) (b ^ 5);// }// 正确写法for (int i = 0; i < read; i++) {buffer[i] = (byte) (buffer[i] ^ 5); // 加密操作}outputStream.write(buffer, 0, read);read = inputStream.read(buffer);}} catch (IOException e) {e.printStackTrace();} finally {if (outputStream != null) {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}if (inputStream != null) {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 解密操作*/@Testpublic void testTwo() {FileInputStream inputStream = null;FileOutputStream outputStream = null;try {File fileA = new File("222.jpg"); // 要解密的文件File fileB = new File("333.jpg"); // 解密后的文件inputStream = new FileInputStream(fileA);outputStream = new FileOutputStream(fileB);byte[] buffer = new byte[10];int read = inputStream.read(buffer);while (read != -1) {// 字符数组进行修改// 错误写法:此时改的只是循环中的局部变量// for (byte b : buffer) {// b = (byte) (b ^ 5);// }// 正确写法for (int i = 0; i < read; i++) {buffer[i] = (byte) (buffer[i] ^ 5); // 加密操作}outputStream.write(buffer, 0, read);read = inputStream.read(buffer);}} catch (IOException e) {e.printStackTrace();} finally {if (outputStream != null) {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}if (inputStream != null) {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}}}
}
练习三
获取文本上字符出现的次数,把数据写入文件。
思路:
-
遍历文本每一个字符
-
字符出现的次数存在 Map 中
Map<Character,Integer> map = new HashMap<Character,Integer>();
map.put(‘a’,18);
map.put(‘你’,2);
-
把 map 中的数据写入文件
package com.laoyang.test.day4;import org.junit.Test;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;/*** @ClassName PicTest* @Description: 练习三:获取文本上字符出现的次数,把数据写入文件。* @Author Laoyang* @Date 2021/10/24 15:13*/
public class WordCount {/*说明:如果使用单元测试,文件相对路径为当前module如果使用main()测试,文件相对路径为当前工程*/@Testpublic void testWordCount() {FileReader fr = null;BufferedWriter bw = null;try {//1.创建Map集合,Character:char 的包装类Map<Character, Integer> map = new HashMap<Character, Integer>();//2.遍历每一个字符,每一个字符出现的次数放到map中,这里直接使用文件路径,就可以不用提前创建 File 对象了fr = new FileReader("hello.txt");// 使用 read() 空参方法的时候,默认是一个一个字符进行查找int c = 0;while ((c = fr.read()) != -1) {//int 还原 charchar ch = (char) c;// 判断char是否在map中第一次出现if (map.get(ch) == null) {map.put(ch, 1);} else {// map.get(ch) + 1:表示该字符出现的次数 + 1,当key值相同的时候,此操作为修改操作map.put(ch, map.get(ch) + 1);}}//3.把map中数据存在文件 wordcount.txt//3.1 创建Writerbw = new BufferedWriter(new FileWriter("wordcount.txt"));//3.2 遍历map,再写入数据Set<Map.Entry<Character, Integer>> entrySet = map.entrySet();for (Map.Entry<Character, Integer> entry : entrySet) {switch (entry.getKey()) {case ' ':bw.write("空格=" + entry.getValue());break;case '\t'://\t表示tab 键字符bw.write("tab键=" + entry.getValue());break;case '\r'://bw.write("回车=" + entry.getValue());break;case '\n'://bw.write("换行=" + entry.getValue());break;default:bw.write(entry.getKey() + "=" + entry.getValue());break;}//换行bw.newLine();}} catch (IOException e) {e.printStackTrace();} finally {//4.关流if (fr != null) {try {fr.close();} catch (IOException e) {e.printStackTrace();}}if (bw != null) {try {bw.close();} catch (IOException e) {e.printStackTrace();}}}}
}
处理流二:转换流的使用
说明
-
转换流(根据最后的关键词辨别是字符流还是字节流,Reader 和 Writer 表示字符流):属于字符流
-
作用:提供字节流与字符流之间的转换
-
字节流中的数据都是字符时,转成字符流操作更高效。
-
很多时候我们使用转换流来处理文件乱码问题。实现编码和 解码的功能。
解码:字节、字节数组 转 字符数组、字符串
编码:字符数组、字符串 转 字节、字节数组
流对象
转换流 | 字符流 |
---|---|
输入流 | InputStreamReader |
输出流 | OutputStreamWriter |
InputStreamReader:将一个字节的输入流转换为字符的输入流
OutputStreamWriter:将一个字符的输出流转换为字节的输出流
InputStreamReader 说明
-
实现将字节的输入流按指定字符集转换为字符的输入流。
-
需要和InputStream“套接”。
-
构造器
public InputStreamReader(InputStream in) public InputSreamReader(InputStream in,String charsetName) 如: Reader isr = new InputStreamReader(System.in,”gbk”); > gbk 表示字符集
OutputStreamWriter 说明
-
实现将字符的输出流按指定字符集转换为字节的输出流。
-
需要和OutputStream“套接”。
-
构造器
public OutputStreamWriter(OutputStream out) public OutputSreamWriter(OutputStream out,String charsetName)
案例
说明
public InputSreamReader(InputStream in,String charsetName)
public OutputSreamWriter(OutputStream out,String charsetName)
-
charsetName 表示字符集,具体使用哪个字符集,取决于指定文件(这里指 hello.txt)保存时使用的字符集。
-
如果设置的字符编码与保存时的字符编码不同,则可能会导致出现乱码,比如:保存时用 utf-8,读取时用 gbk,就会导致乱码。
-
如果不设置 charsetName,则默认使用当前编译器字符集格式,如果设置,则使用设置的字符集格式。
InputStreamReader 的使用
package com.laoyang.test.day5;import org.junit.Test;
import java.io.*;/*** @ClassName InputStreamReaderTest* @Description: 处理流二:转换流的使用* @Author Laoyang* @Date 2021/10/24 16:25*/
public class InputStreamReaderTest {/*** 此时处理异常的时候依然是使用 try-catch-finally* InputStreamReader 的使用:实现字节的输入流到字符的输入流的转换*/@Testpublic void testOne() {FileInputStream inputStream = null;InputStreamReader streamReader = null;try {inputStream = new FileInputStream("hello.txt");/*第二个参数表示字符集:具体使用哪个字符集,取决于指定文件(这里指 hello.txt)保存时使用的字符集如果设置的字符编码与保存时的字符编码不同,则可能会导致出现乱码,比如:保存时用 utf-8,读取时用 gbk,就会导致乱码如果不设置参数二,则使用当前编译器默认的字符集格式,如果设置,则使用设置的字符集格式*/
// streamReader = new InputStreamReader(inputStream, "GBK");streamReader = new InputStreamReader(inputStream, "UTF-8");char[] cbuf = new char[10];int read = streamReader.read(cbuf);while (read != -1) {String str = new String(cbuf, 0, read);System.out.print(str);read = streamReader.read(cbuf);}} catch (IOException e) {e.printStackTrace();} finally {if (streamReader != null) {try {streamReader.close();} catch (IOException e) {e.printStackTrace();}}}}
}
综合使用 InputStreamReader 和 OutputStreamWriter
说明:
如果将复制的文件字符集修改为 GBK,那么在 IDEA 中打开会出现乱码的情况,因为 IDEA 我现在设置的是 UTF-8,但是用记事本、notepad++ 之类的软件打开就可以正常显示,因为它们可以自动使用对应的字符集进行显示
package com.laoyang.test.day5;import org.junit.Test;
import java.io.*;/*** @ClassName InputStreamReaderTest* @Description: 处理流二:转换流的使用* @Author Laoyang* @Date 2021/10/24 16:25*/
public class InputStreamReaderTest {/*** 综合使用 InputStreamReader 和 OutputStreamWriter*/@Testpublic void testTwo() {FileInputStream inputStream = null;FileOutputStream outputStream = null;InputStreamReader streamReader = null;OutputStreamWriter streamWriter = null;try {// 1. 创建 File 对象,指明要操作的文件File fileA = new File("hello.txt");File fileB = new File("hello_gbk.txt");// 2. 实例化流inputStream = new FileInputStream(fileA);outputStream = new FileOutputStream(fileB);streamReader = new InputStreamReader(inputStream, "UTF-8");streamWriter = new OutputStreamWriter(outputStream, "GBK");// 3. 读写过程char[] cbuf = new char[10];int read = streamReader.read(cbuf);while (read != -1) {streamWriter.write(cbuf, 0, read);read = streamReader.read(cbuf);}} catch (IOException e) {e.printStackTrace();} finally {// 4.关闭流if (streamWriter != null) {try {streamWriter.close();} catch (IOException e) {e.printStackTrace();}}if (streamReader != null) {try {streamReader.close();} catch (IOException e) {e.printStackTrace();}}}}
}
扩展:字符编码
-
编码表的由来
计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识 别各个国家的文字。就将各个国家的文字用数字来表示,并一一对应,形成一张表。 这就是编码表。
-
常见的编码表(字符集)
ASCII:美国标准信息交换码。
用一个字节的 7 位可以表示。
ISO8859-1:拉丁码表。欧洲码表
用一个字节的 8 位表示。
GB2312:中国的中文编码表。最多两个字节编码所有字符
GBK:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码
Unicode:国际标准码,融合了目前人类使用的所有字符。为每个字符分配唯一的 字符码。所有的文字都用两个字节来表示。
UTF-8:变长的编码方式,可用 1-4 个字节来表示一个字符。
-
在 Unicode 出现之前,所有的字符集都是和具体编码方案绑定在一起的(即字 符集≈编码方式),都是直接将字符和最终字节流绑定死了。
-
GBK 等双字节编码方式,用最高位是 1 或 0 表示两个字节和一个字节。
-
Unicode 不完美,这里就有三个问题
第一:我们已经知道,英文字母只用 一个字节表示就够了
第二:如何才能区别 Unicode 和 ASCII ?计算机怎么知道两个字节表示一个符号,而不是分别表示两个符号呢?
第三:如果和 GBK 等双字节编码方式一样,用最高位是 1 或 0 表示两个字节和一个字节, 就少了很多值无法用于表示字符,不够表示所有字符。
Unicode 在很长一段时间内无法推广,直到互联网的出现。
-
向传输的众多 UTF(UCS Transfer Format)标准出现了,顾名思义,UTF-8 就是每次 8 个位传输数据,而 UTF-16 就是每次 16 个位。这是为传输而设计的编码,并使编码无国界,这样就可以显示全世界上所有文化的字符了。
-
Unicode 只是定义了一个庞大的、全球通用的字符集,并为每个字符规定了唯一确定的编号,具体存储成什么样的字节流,取决于字符编码方案。推荐的 Unicode 编码是 UTF-8 和 UTF-16。
转换流的编码应用
- 可以将字符按指定编码格式存储
- 可以对文本数据按指定编码格式来解读
- 指定编码表的动作由构造器完成
其它处理流的使用
标准输入、输出流的使用
说明
-
System.in 和 System.out 分别代表了系统标准的输入和输出设备
-
默认输入设备:键盘
输出设备是:显示器
-
System.in 的类型是 InputStream
-
System.out 的类型是 PrintStream,其是 OutputStream 的子类 FilterOutputStream 的子类
-
重定向:通过 System 类的 setIn,setOut 方法对默认设备进行改变。
public static void setIn(InputStream in)
public static void setOut(PrintStream out)
案例
从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续进行输入操作,直至当输入“e”或者“exit”时,退出程序。
方式一:使用 Scanner 实现,调用 next() 返回一个字符串
方式二:使用 System.in 实现,System.in 转 转换流 转 BufferedReader 的 readLine() 方法
package com.laoyang.test.day6;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;/*** @ClassName OtherStreamTest* @Description: 标准的输入、输出流的使用* @Author Laoyang* @Date 2021/10/24 17:21*/
public class StandardInputAndOutputTest {public static void main(String[] args) {BufferedReader bufferedReader = null;try {InputStreamReader streamReader = new InputStreamReader(System.in);bufferedReader = new BufferedReader(streamReader);while (true) {System.out.print("请输入字符串:");String data = bufferedReader.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 (bufferedReader != null) {try {bufferedReader.close();} catch (IOException e) {e.printStackTrace();}}}}
}
练习
创建一个名为 MyInput.java 的程序:包含读取 int 的方法,来自键盘的 double、float、boolean、short、byte 和 String 值
package com.laoyang.test.day6;import java.io.*;/*** @ClassName OtherStreamTest* @Description: 练习:创建一个名为 MyInput.java 的程序:包含读取 int 的方法,来自键盘的 double、float、boolean、short、byte 和 String 值* @Author Laoyang* @Date 2021/10/24 17:21*/
public class MyInput {/*** 读取从键盘输入的值*/public static String readString() {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));// 声明并初始化字符串String string = "";// 从键盘获取字符串try {string = br.readLine();} catch (IOException ex) {System.out.println(ex);}// 返回从键盘获取的字符串return string;}/*** 从键盘读取一个 int 值*/public static int readInt() {return Integer.parseInt(readString());}/*** 从键盘读取一个 double 值*/public static double readDouble() {return Double.parseDouble(readString());}/*** 从键盘读取一个 byte 值*/public static double readByte() {return Byte.parseByte(readString());}/*** 从键盘读取一个 short 值*/public static double readShort() {return Short.parseShort(readString());}/*** 从键盘读取一个 long 值*/public static double readLong() {return Long.parseLong(readString());}/*** 从键盘读取一个 float 值*/public static double readFloat() {return Float.parseFloat(readString());}
}
注意:该类虽然可以接收从键盘输入的不同类型的值,但是使用的接收类型和输入的数据类型不一致的话,还是会报错的!!!
打印流的使用
说明
- 实现将基本数据类型的数据格式转化为字符串输出
- 打印流:PrintStream 和 PrintWriter
- 提供了一系列重载的 print() 和 println() 方法,用于多种数据类型的输出
- PrintStream 和 PrintWriter 的输出不会抛出 IOException 异常
- PrintStream 和 PrintWriter 有自动 flush 功能
- PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。 在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。
- System.out 返回的是 PrintStream 的实例
流对象
打印流 | 字符流 | 字节流 |
---|---|---|
输入流 | 无 | 无 |
输出流 | PrintStream | PrintWriter |
注意:打印流只有输出流,没有输入流!!!
案例
PrintStream 的使用
package com.laoyang.test.day6;import org.junit.Test;
import java.io.*;/*** @ClassName PrintStreamTest* @Description: 打印流的使用* @Author Laoyang* @Date 2021/10/24 17:51*/
public class PrintStreamTest {/*** 打印流:PrintStream 的使用*/@Testpublic void testOne() {PrintStream ps = null;try {// 文件不存在是可以的,但是存放文件的目录必须要存在!FileOutputStream fos = new FileOutputStream(new File("F:\\IO\\day1\\hello.txt"));// 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区),如果不想要自动刷新,改为false或者不写即可ps = new PrintStream(fos, true);// 把标准输出流(控制台输出)改成文件if (ps != null) {System.setOut(ps);}// 输出ASCII字符for (int i = 0; i <= 255; i++) {System.out.print((char) i);// 每50个数据一行if (i % 50 == 0) {System.out.println(); // 换行}}} catch (FileNotFoundException e) {e.printStackTrace();} finally {if (ps != null) {ps.close();}}}
}
将 System.out.print(); 中的值存入指定的文件中
PrintWriter 的使用
package com.laoyang.test.day6;import org.junit.Test;
import java.io.*;/*** @ClassName PrintStreamTest* @Description: 打印流的使用* @Author Laoyang* @Date 2021/10/24 17:51*/
public class PrintStreamTest {/*** 打印流:PrintWriter 的使用*/@Testpublic void testTwo() {PrintWriter printWriter = null;try {FileOutputStream streamWriter = new FileOutputStream(new File("F:\\IO\\day1\\hello.txt"));printWriter = new PrintWriter(streamWriter, true);// 将 ASCII 字符存储到指定文件中for (int i = 0; i <= 255; i++) {printWriter.print((char) i);// 每50个数据一行if (i % 50 == 0) {printWriter.println(); // 换行}}} catch (IOException e) {e.printStackTrace();} finally {if (printWriter != null)printWriter.close();}}
}
将 printWriter.print(); 中的值存入指定的文件中
数据流的使用
说明
-
为了方便地操作 Java 语言的基本数据类型和 String 的数据,可以使用数据流。
-
数据流有两个类:(作用:用于读取或写入基本数据类型的变量或字符串)
DataInputStream 和 DataOutputStream
分别 “套接” 在 InputStream 和 OutputStream 子类的流上
-
DataInputStream中的方法
boolean readBoolean() byte readByte() char readChar() float readFloat() double readDouble() short readShort() long readLong() int readInt() String readUTF() void readFully(byte[] b) -
DataOutputStream 中的方法:将上述的方法的 read 改为相应的 write 即可。
案例
package com.laoyang.test.day6;import org.junit.Test;
import java.io.*;/*** @ClassName DataFlowTest* @Description: 处理流五:数据流的使用* @Author Laoyang* @Date 2021/10/25 14:58*/
public class DataFlowTest {/*** 写入的过程,DataOutputStream 的使用*/@Testpublic void testOne(){DataOutputStream dos = null;try {dos = new DataOutputStream(new FileOutputStream("data.txt"));// flush():刷新操作,将内存中的数据写入文件dos.writeUTF("小白");dos.flush();dos.writeInt(18);dos.flush();dos.writeBoolean(true);} catch (IOException e) {e.printStackTrace();} finally {if (dos != null) {try {dos.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 读取:DataInputStream 的使用* 练习:将文件中存储的基本数据类型变量和字符串读取到内存中,保存在变量中* 注意点:读取不同类型的数据的顺序要与当初写入文件时的顺序保持一致*/@Testpublic void testTwo() {DataInputStream dis = null;try {// 1. 实例化流的对象(因为流程都差不多,这里就把前面的操作合在一起了)dis = new DataInputStream(new FileInputStream("data.txt"));/*2. 读取操作,此处有规定规定:要根据写入时的顺序进行读取,否则就会报 java.io.EOFException*/String name = dis.readUTF();int age = dis.readInt();boolean isMale = dis.readBoolean();System.out.println(name);System.out.println(age);System.out.println(isMale);} catch (IOException e) {e.printStackTrace();} finally {// 3. 关闭流if (dis != null) {try {dis.close();} catch (IOException e) {e.printStackTrace();}}}}
}
在指定文件中写入数据后是不能直接打开查看的,因为会导致乱码;如果想要查看正确的数据,可以使用 DataInputStream 进行读取
对象流的使用
说明
- 对象流:ObjectInputStream 和 OjbectOutputSteam
- 作用:用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把 Java 中的对象写入到数据源中,也能把对象从数据源中还原回来。
- 序列化:用 ObjectOutputStream 类保存基本类型数据或对象的机制
- 反序列化:用 ObjectInputStream 类读取基本类型数据或对象的机制
- ObjectOutputStream 和 ObjectInputStream 不能序列化 static 和 transient 修饰的成员变量
对象的序列化
-
对象序列化机制允许把内存中的 java 对象转换成平台无关的二进制流,从而允许把这种二进制流持久的保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点;当其它程序获取了这种二进制流,就可以恢复成原来的 java 对象
-
序列化的好处在于可将任何实现了 Serializable 接口的对象转化为字节数据, 使其在保存和传输时可被还原
-
序列化是 RMI(Remote Method Invoke – 远程方法调用)过程的参数和返回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是 JavaEE 平台的基础
-
如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一。 否则,会抛出NotSerializableException异常
Serializable 和 Externalizable
-
凡是实现 Serializable 接口的类都有一个表示序列化版本标识符的静态变量:private static final long serialVersionUID;
-
serialVersionUID 用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象进行版本控制,有关各版本反序列化时是否兼容。
-
如果类没有显示定义这个静态常量,它的值是 Java 运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。所以建议显式声明。
比如当前类没有定义 serialVersionUID 常量,属性暂时只有 name 和 age,这个时候使用 OjbectOutputSteam 写入;
然后在当前类中新加入一个属性 id,以及它的 get/set 等方法,这个时候在使用 ObjectInputStream 去读取,就会报错;
因为这个时候的版本以及不一样了,假设第一次版本为 1,然后加入 id 属性后,版本变成了 2,由于 1 版本已经升为了 2,这个时候再去找 1 版本的数据就会报错。
-
-
简单来说,Java 的序列化机制是通过在运行时判断类的 serialVersionUID 来验证版本一致性的。在进行反序列化时,JVM 会把传来的字节流中的 serialVersionUID 与本地相应实体类的 serialVersionUID 进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)
使用对象流序列化对象
序列化对象步骤
-
创建 ObjectOutputStream 流对象
-
调用 ObjectOutputStream 对象的 writeObject(对象) 方法输出可序列化对象
注意写入一次,操作 flush() 一次
-
关闭流
反序列化对象步骤
-
创建 ObjectInputStream 流对象
-
调用 readObject() 方法读取流中的对象
注意:如果添加了不同类型的对象,则读取的对象类型必须和写入的对象类型的顺序保持一致,比如第一次写入的是 String 类型对象,第二次写入的是 User 类型对象,那么读取的时候第一次必须先读取 String 对象,再来读取 User 对象,否则就会报错!
-
关闭流
注意
如果某个类的属性不是基本数据类型或 String 类型,而是另一个引用类型,那么这个引用类型必须是可序列化的,否则拥有该类型的类也不能序列化
详情请看 “使用自定义类实现序列化与反序列化操作”
案例
序列化过程:将内存中的 java 对象保存到磁盘中或通过网络传输出去
反序列化过程:将磁盘文件中的对象还原为内存中的一个 java 对象
package com.laoyang.test.day6;import org.junit.Test;
import java.io.*;/*** @ClassName ObjectStreamTest* @Description: 处理流六:对象流的使用* @Author Laoyang* @Date 2021/10/25 16:57*/
public class ObjectStreamTest {/**序列化过程:将内存中的 java 对象保存到磁盘中或通过网络传输出去使用 ObjectOutputStream 实现输出的文件是不能直接打开的,如果想要查看里面的内容,则需要使用 ObjectInputStream 实现*/@Testpublic void testOne() {ObjectOutputStream oos = null;try {oos = new ObjectOutputStream(new FileOutputStream("object.dat"));oos.writeObject(new String("java是世界上最好的语言"));// 刷新操作oos.flush();} catch (IOException e) {e.printStackTrace();} finally {if (oos != null) {try {oos.close();} catch (IOException e) {e.printStackTrace();}}}}/**反序列化过程:将磁盘文件中的对象还原为内存中的一个 java 对象使用 ObjectInputStream 实现*/@Testpublic void testTwo() {ObjectInputStream ois = null;try {ois = new ObjectInputStream(new FileInputStream("object.dat"));Object object = ois.readObject();String str = (String) object;System.out.println(str);} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} finally {if (ois != null) {try {ois.close();} catch (IOException e) {e.printStackTrace();}}}}
}
使用自定义类实现序列化与反序列化操作
自定义类需要满足如下的要求,才可进行序列化操作
- 实现 Serializable 接口(Serializable 是一个标识接口,里面没有任何需要实现的方法)
- 当前类提供一个全局常量:serialVersionUID
注意:
- 读取的时候需要和写入时的顺序保持一致(如果都是同一种类型,则可以直接进行读取),否则会报异常
- ObjectOutputStream 和 ObjectInputStream 不能序列化 static 和 transient 修饰的成员变量
- 如果自定义类中的子类属性不是可序列化的,那么也会报错:NotSerializableException;如果想要正常执行,那么需要把子类也声明为可序列化的
package com.laoyang.test.day6;import org.junit.Test;
import java.io.*;/*** @ClassName ObjectStreamTest* @Description: 处理流六:对象流的使用* @Author Laoyang* @Date 2021/10/25 16:57*/
public class ObjectStreamTest {/*** 使用自定义类实现序列化与反序列化操作* 序列化操作*/@Testpublic void testThree() {ObjectOutputStream oos = null;
// ObjectInputStream ois = null;try {// 写入oos = new ObjectOutputStream(new FileOutputStream("object.dat"));oos.writeObject(new String("java是世界上最好的语言"));// 刷新操作oos.flush();oos.writeObject(new Person("小白", 18));oos.flush();oos.writeObject(new Person("小黑", 23, new Account(5000)));oos.flush();} catch (Exception e) {e.printStackTrace();} finally {if (oos != null) {try {oos.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 反序列化操作*/@Testpublic void testFour() {ObjectInputStream ois = null;try {// 读取,读取的时候需要和写入时的顺序保持一致(如果都是同一种类型,则可以直接进行读取),否则会报异常ois = new ObjectInputStream(new FileInputStream("object.dat"));String str = (String) ois.readObject();Person person = (Person) ois.readObject();System.out.println(str);System.out.println(person);/*如果 Person 类中的 Account 属性不是可序列化的,那么就会报错:NotSerializableException如果想要正常执行,那么需要把 Account 类也声明为可序列化的*/Person account = (Person) ois.readObject();System.out.println(account);} catch (Exception e) {e.printStackTrace();} finally {if (ois != null) {try {ois.close();} catch (IOException e) {e.printStackTrace();}}}}
}/*** 自定义类实现序列化与反序列化操作* Person 类需要满足如下的要求,才可进行序列化操作* 1. 实现 Serializable 接口(Serializable 是一个标识接口,里面没有任何需要实现的方法)* 2. 当前类提供一个全局常量:serialVersionUID*/
class Person implements Serializable {// 可测试定义 serialVersionUID 与不定义 serialVersionUID 常量的区别private static final long serialVersionUID = -1806851998099103327L;// ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
// private static String name;
// private transient int age;private String name;private int age;private Account account;public Person(String name, int age, Account account) {this.name = name;this.age = age;this.account = account;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +", account=" + account +'}';}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Person(String name, int age) {this.name = name;this.age = age;}public Person() {}
}/*** 自定义类,用于当做 Person 类的参数进行测试* 可以将 Serializable 去掉或加上进行测试,看不同的测试效果*/
// class Account {
class Account implements Serializable {private static final long serialVersionUID = 1928731172189653323L;private double balance;@Overridepublic String toString() {return "Account{" +"balance=" + balance +'}';}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}public Account(double balance) {this.balance = balance;}public Account() {}
}
idea设置快捷生成:File > Editor > Inspections > Serialization issues > 把 Serializable class without ‘serialVersionUID’ 勾上即可
设置好后将鼠标点击对应的类,然后按 ALT + 回车即可
随机存取文件流
RandomAccessFile 类
说明
-
RandomAccessFile 声明在 java.io 包下,但直接继承于 java.lang.Object类。并 且它实现了 DataInput、DataOutput 这两个接口,也就意味着这个类既可以读也可以写。
-
RandomAccessFile 类支持 “随机访问” 的方式,程序可以直接跳到文件的任意地方来读、写文件
支持只访问文件的部分内容
可以向已存在的文件后追加内容 -
RandomAccessFile 对象包含一个记录指针,用以标示当前读写处的位置。
RandomAccessFile 类对象可以自由移动记录指针:
long getFilePointer():获取文件记录指针的当前位置
void seek(long pos):将文件记录指针定位到 pos 位置
使用
-
构造器
public RandomAccessFile(File file, String mode)
public RandomAccessFile(String name, String mode)
-
创建 RandomAccessFile 类实例需要指定一个 mode 参数,该参数指定 RandomAccessFile 的访问模式:
r: 以只读方式打开 rw:打开以便读取和写入 rwd:打开以便读取和写入;同步文件内容的更新 rws:打开以便读取和写入;同步文件内容和元数据的更新
-
如果模式为只读 r。则不会创建文件,而是会去读取一个已经存在的文件, 如果读取的文件不存在则会出现异常。 如果模式为 rw 读写。如果文件不存在则会去创建文件,如果存在则不会创建。
其它
我们可以用 RandomAccessFile 这个类,来实现一个多线程断点下载的功能, 用过下载工具的朋友们都知道,下载前都会建立两个临时文件,一个是与被下载文件大小相同的空文件,另一个是记录文件指针的位置文件,每次暂停的时候,都会保存上一次的指针,然后断点下载的时候,会继续从上一次的地方下载,从而实现断点下载或上传的功能,有兴趣的朋友们可以自己实现下。
比如:使用百度云或迅雷下载文件的时候,在还没有下载完成的时候我们打开对应的保存位置是可以已经有一个文件存在的,这个文件就属于临时文件,如果这个时候突然没有网络导致暂停了下载,那么我们下一次下载的时候就是接着从上一次暂停的位置开始下载,而不需要从头下载,这就属于一个多线程断点下载。
案例
说明
- RandomAccessFile 直接继承了 java.lang.Object 类,实现了 DataInput 和 DataOutput 接口
- RandomAccessFile 既可以作为一个输入流,又可以作为一个输出流
- 如果 RandomAccessFile 作为输出流时,写入到的文件如果不存在,则在执行过程中自动创建
如果写入到的文件存在,则会对原有文件内容进行覆盖(默认情况下,是从头开始覆盖) - 可以通过相关的操作,实现 RandomAccessFile “插入” 数据的操作
RandomAccessFile 类的使用
构造器
public RandomAccessFile(File file, String mode)
public RandomAccessFile(String name, String mode)
创建 RandomAccessFile 类实例需要指定一个 mode 参数,该参数指定 RandomAccessFile 的访问模式:
r:以只读方式打开
rw:打开以便读取和写入
rwd:打开以便读取和写入;同步文件内容的更新
rws:打开以便读取和写入;同步文件内容和元数据的更新
一般我们使用 r 和 rw 就够用了
package com.laoyang.test.day7;import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;/*** @ClassName RandomStorageFileStreamTest* @Description: 随机存取文件流* @Author Laoyang* @Date 2021/10/26 9:32*/
public class RandomAccessFileStreamTest {@Testpublic void testOne() {RandomAccessFile rafDataInput = null;RandomAccessFile rafDataOutput = null;try {// 1. 实例化流的对象rafDataInput = new RandomAccessFile(new File("2.jpg"), "r");rafDataOutput = new RandomAccessFile(new File("234.jpg"), "rw");// 2. 读写操作byte[] buffer = new byte[10];int read = rafDataInput.read(buffer);while (read != -1) {rafDataOutput.write(buffer, 0, read);read = rafDataInput.read(buffer);}} catch (IOException e) {e.printStackTrace();} finally {// 3. 关闭流if (rafDataOutput != null) {try {rafDataOutput.close();} catch (IOException e) {e.printStackTrace();}}if (rafDataInput != null) {try {rafDataInput.close();} catch (IOException e) {e.printStackTrace();}}}}
}
关于 RandomAccessFile 只作为输出流时的文件覆盖操作
指定文件不存在的时候会创建一个文件然后在将数据写入到这个新文件中;如果文件存在,则会从头开始覆盖,比如 helloA.txt 中的数据为 Hello,当写入 AAA后,数据变为 AAAlo
package com.laoyang.test.day7;import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;/*** @ClassName RandomStorageFileStreamTest* @Description: 随机存取文件流* @Author Laoyang* @Date 2021/10/26 9:32*/
public class RandomAccessFileStreamTest {/*** 关于 RandomAccessFile 只作为输出流时的文件覆盖操作*/@Testpublic void testTwo() {RandomAccessFile raf = null;try {File file = new File("helloA.txt");/*指定文件不存在的时候会创建一个文件然后将数据写入到这个新文件中如果存在,则会从头开始覆盖,比如 helloA.txt 中的数据为 Hello,当写入 AAA后,数据变为 AAAlo*/
// raf = new RandomAccessFile("helloAAA.txt", "rw");raf = new RandomAccessFile(file, "rw");// 将指针调到角标为 3 的位置
// raf.seek(3);// 如果想要从文件末尾开始追加数据,则可以指针指向文件最后一个字符的角标位置raf.seek(file.length());// 如果指定的文件存在,那么 write() 就相当于一个覆盖操作raf.write("AAA".getBytes());} catch (IOException e) {e.printStackTrace();} finally {try {if (raf != null)raf.close();} catch (IOException e) {e.printStackTrace();}}}
}
RandomAccessFile 实现数据的插入效果
大致逻辑:
-
从对应指针位置开始读取,获取到插入位置后面的所有数据,然后用 StringBuilder 存储起来
-
从对于指针位置写入对应的数据
-
将要插入的数据先执行一次写入操作,然后在把 StringBuilder 中的数据写入进去
StringBuilder 写入的数据是接在上一次写入的数据后面的,基本上也可以理解为覆盖原本后面的数据。
比如:原数据为:Hello World,第一次插入 AAA,结果变为:HelAAAWorld,第二次插入时,变为 HelAAAlo World
就相当于 “lo World” 是存放在 StringBuilder 中的,第二次插入只是为了将插入时覆盖的数据复原。
package com.laoyang.test.day7;import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;/*** @ClassName RandomStorageFileStreamTest* @Description: 随机存取文件流* @Author Laoyang* @Date 2021/10/26 9:32*/
public class RandomAccessFileStreamTest {/*** 使用 RandomAccessFile 实现数据的插入效果*/@Testpublic void testThree() {RandomAccessFile raf = null;try {File file = new File("helloA.txt");raf = new RandomAccessFile(file, "rw");// 从指针 3 的位置开始进行读取raf.seek(3);// 保存指针 3 后面的所有数据到 StringBuilder 中StringBuilder builder = new StringBuilder((int) file.length());byte[] buffer = new byte[10];int read = raf.read(buffer);while (read != -1) {builder.append(new String(buffer, 0, read));read = raf.read(buffer);}System.out.println(builder.toString());// 调回指针,写入 ABCraf.seek(3);raf.write("ABC".getBytes());// 将 StringBuilder 中的数据写入到文件中raf.write(builder.toString().getBytes());} catch (IOException e) {e.printStackTrace();} finally {if (raf != null) {try {raf.close();} catch (IOException e) {e.printStackTrace();}}}}
}
getFilePointer() 方法的使用
package com.laoyang.test.day7;import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;/*** @ClassName RandomStorageFileStreamTest* @Description: 随机存取文件流* @Author Laoyang* @Date 2021/10/26 9:32*/
public class RandomAccessFileStreamTest {/*** getFilePointer() 方法的使用* 作用:获取文件记录指针的当前位置* @throws IOException*/@Testpublic void test() {RandomAccessFile file = null;try {file = new RandomAccessFile(new File("helloA.txt"), "rw");System.out.println(file.getFilePointer()); // 0file.seek(3);System.out.println(file.getFilePointer()); // 3} catch (IOException e) {e.printStackTrace();} finally {if (file != null) {try {file.close();} catch (IOException e) {e.printStackTrace();}}}}
}
思考:将 StringBuilder 替换为 ByteArrayOutputStream 实现 RandomAccessFile 的插入操作
package com.laoyang.test.day7;import org.junit.Test;
import java.io.*;/*** @ClassName RandomStorageFileStreamTest* @Description: 思考:将 StringBuilder 替换为 ByteArrayOutputStream 实现 RandomAccessFile 的插入操作* @Author Laoyang* @Date 2021/10/26 11:04*/
public class ByteArrayOutputStreamTest {@Testpublic void test1() {FileInputStream fis = null;try {fis = new FileInputStream("helloA.txt");String info = readStringFromInputStream(fis);System.out.println(info);} catch (IOException e) {e.printStackTrace();} finally {if (fis != null) {try {fis.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 读取操作* @param fis* @return* @throws IOException*/private String readStringFromInputStream(FileInputStream fis) throws IOException {// 方式一:可能出现乱码
// String content = "";
// byte[] buffer = new byte[1024];
// int len;
// while((len = fis.read(buffer)) != -1){
// content += new String(buffer);
// }
// return content;// 方式二:BufferedReader
// BufferedReader reader = new BufferedReader(new InputStreamReader(fis));
// char[] buf = new char[10];
// int len;
// String str = "";
// while ((len = reader.read(buf)) != -1) {
// str += new String(buf, 0, len);
// }
// return str;// 方式三:避免出现乱码ByteArrayOutputStream baos = new ByteArrayOutputStream();byte[] buffer = new byte[10];int len = fis.read(buffer);while (len != -1) {baos.write(buffer, 0, len);len = fis.read(buffer);}return baos.toString();}/*** 插入操作*/@Testpublic void testByteArrayOutputStream() {RandomAccessFile raf = null;try {File file = new File("helloA.txt");raf = new RandomAccessFile(file, "rw");raf.seek(3);ByteArrayOutputStream baos = new ByteArrayOutputStream();byte[] buffer = new byte[10];int read = raf.read(buffer);while (read != -1) {baos.write(buffer, 0, read);read = raf.read(buffer);}raf.seek(3);raf.write("QQ".getBytes());raf.write(baos.toString().getBytes());} catch (IOException e) {e.printStackTrace();} finally {if (raf != null) {try {raf.close();} catch (IOException e) {e.printStackTrace();}}}}
}
这里演示了使用 ByteArrayOutputStream 实现读写操作
NIO.2 中 Path、Paths、Files 类的使用
Java NIO 概述
- Java NIO (New IO,Non-Blocking IO) 是从 Java 1.4 版本开始引入的一套 的 IO API,可以替代标准的 Java IO API。NIO 与原来的 IO有同样的作用和目的,但是使用的方式完全不同,NIO 支持面向缓冲区的 ( IO 是面向流的)、基于 通道的 IO 操作。NIO 将以更加高效的方式进行文件的读写操作。
- Java API 中提供了两套 NIO,一套是针对标准输入输出 NIO,另一套就是网络编程 NIO。
|-----java.nio.channels.Channel|-----FileChannel:处理本地文件|-----SocketChannel:TCP网络编程的客户端的Channel|-----ServerSocketChannel:TCP网络编程的服务器端的Channel|-----DatagramChannel:UDP网络编程中发送端和接收端的Channel
NIO.2
随着 JDK 7 的发布,Java 对 NIO 进行了极大的扩展,增强了对文件处理和文件系统特性的支持,以至于我们称他们为 NIO.2。 因为 NIO 提供的一些功能,NIO 已经成为文件处理中越来越重要的部分。
Path、Paths 和 Files 核心 API
-
早期的 Java 只提供了一个 File 类来访问文件系统,但 File 类的功能比较有限,所提供的方法性能也不高。而且,大多数方法在出错时仅返回失败,并不会提供异常信息。
-
NIO. 2 为了弥补这种不足,引入了 Path 接口,代表一个平台无关的平台路径,描述了目录结构中文件的位置。Path 可以看成是 File类的升级版本,实际引用的资源也可以不存在。
-
在以前 IO 操作都是这样写的:
import java.io.File; File file = new File("index.html");
-
但在Java7 中,我们可以这样写:
import java.nio.file.Path; import java.nio.file.Paths; Path path = Paths.get("index.html")
-
同时,NIO.2 在 java.nio.file 包下还提供了 Files、Paths 工具类,Files 包含了大量静态的工具方法来操作文件;Paths 则包含了两个返回 Path 的静态工厂方法。
-
Paths 类提供的静态 get() 方法用来获取 Path 对象:
static Path get(String first, String … more):用于将多个字符串串连成路径 static Path get(URI uri):返回指定 uri 对应的 Path 路径
Path 常用方法
方法 | 作用 |
---|---|
String toString() | 返回调用 Path 对象的字符串表示形式 |
boolean startsWith(String path) | 判断是否以 path 路径开始 |
boolean endsWith(String path) | 判断是否以 path 路径结束 |
boolean isAbsolute() | 判断是否是绝对路径 |
Path getParent() | 返回 Path 对象包含整个路径,不包含 Path 对象指定的文件路径 |
Path getRoot() | 返回调用 Path 对象的根路径 |
Path getFileName() | 返回与调用 Path 对象关联的文件名 |
int getNameCount() | 返回 Path 根目录后面元素的数量 |
Path getName(int idx) | 返回指定索引位置 idx 的路径名称 |
Path toAbsolutePath() | 作为绝对路径返回调用 Path 对象 |
Path resolve(Path p) | 合并两个路径,返回合并后的路径对应的Path对象 |
File toFile() | 将 Path 转化为 File 类的对象 |
Files 常用方法
说明:java.nio.file.Files 用于操作文件或目录的工具类。
常用方法
方法 | 作用 |
---|---|
Path copy(Path src, Path dest, CopyOption … how) | 文件的复制 |
Path createDirectory(Path path, FileAttribute<?> … attr) | 创建一个目录 |
Path createFile(Path path, FileAttribute<?> … arr) | 创建一个文件 |
void delete(Path path) | 删除一个文件/目录,如果不存在,执行报错 |
void deleteIfExists(Path path) | Path 对应的文件/目录如果存在,执行删除 |
Path move(Path src, Path dest, CopyOption…how) | 将 src 移动到 dest 位置 |
long size(Path path) | 返回 path 指定文件的大小 |
常用方法 · 用于判断
方法 | 作用 |
---|---|
boolean exists(Path path, LinkOption … opts) | 判断文件是否存在 |
boolean isDirectory(Path path, LinkOption … opts) | 判断是否是目录 |
boolean isRegularFile(Path path, LinkOption … opts) | 判断是否是文件 |
boolean isHidden(Path path) | 判断是否是隐藏文件 |
boolean isReadable(Path path) | 判断文件是否可读 |
boolean isWritable(Path path) | 判断文件是否可写 |
boolean notExists(Path path, LinkOption … opts) | 判断文件是否不存在 |
常用方法 · 用于操作内容
方法 | 作用 |
---|---|
SeekableByteChannel newByteChannel(Path path, OpenOption…how) | 获取与指定文件的连接,how 指定打开方式。 |
DirectoryStream | 打开 path 指定的目录 |
InputStream newInputStream(Path path, OpenOption…how) | 获取 InputStream 对象 |
OutputStream newOutputStream(Path path, OpenOption…how) | 获取 OutputStream 对象 |
案例
Path 的使用
-
jdk 7.0 时,引入了 Path、Paths、Files 三个类。
-
此三个类声明在:java.nio.file 包下。
-
Path可以看做是java.io.File类的升级版本。也可以表示文件或文件目录,与平台无关
-
如何实例化Path:使用Paths
static Path get(String first, String … more):用于将多个字符串串连成路径 static Path get(URI uri):返回指定uri对应的Path路径
package com.laoyang.test.day8;import org.junit.Test;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;/*** @ClassName RandomStorageFileStreamTest* @Description: Path 类的使用* @Author Laoyang* @Date 2021/10/26 11:17*/
public class PathTest {/*** 如何使用Paths实例化Path*/@Testpublic void test1() {Path path1 = Paths.get("F:\\nio\\hello.txt"); // new File(String filepath)Path path2 = Paths.get("F:\\", "nio\\hello.txt"); // new File(String parent,String filename);System.out.println(path1);System.out.println(path2);Path path3 = Paths.get("F:\\", "nio");System.out.println(path3);}/*** Path中的常用方法*/@Testpublic void test2() {Path path1 = Paths.get("F:\\", "nio\\nio1\\nio2\\hello.txt");Path path2 = Paths.get("hello.txt");// String toString() : 返回调用 Path 对象的字符串表示形式System.out.println(path1);// boolean startsWith(String path) : 判断是否以 path 路径开始System.out.println(path1.startsWith("F:\\nio"));// boolean endsWith(String path) : 判断是否以 path 路径结束System.out.println(path1.endsWith("hello.txt"));// boolean isAbsolute() : 判断是否是绝对路径System.out.println(path1.isAbsolute() + "~");System.out.println(path2.isAbsolute() + "~");// Path getParent() :返回Path对象包含整个路径,不包含 Path 对象指定的文件路径System.out.println(path1.getParent());System.out.println(path2.getParent());// Path getRoot() :返回调用 Path 对象的根路径System.out.println(path1.getRoot());System.out.println(path2.getRoot());// Path getFileName() : 返回与调用 Path 对象关联的文件名System.out.println(path1.getFileName() + "~");System.out.println(path2.getFileName() + "~");// int getNameCount() : 返回Path 根目录后面元素的数量
// Path getName(int idx) : 返回指定索引位置 idx 的路径名称for (int i = 0; i < path1.getNameCount(); i++) {System.out.println(path1.getName(i) + "*****");}// Path toAbsolutePath():作为绝对路径返回调用 Path 对象System.out.println(path1.toAbsolutePath());System.out.println(path2.toAbsolutePath());// Path resolve(Path p):合并两个路径,返回合并后的路径对应的Path对象Path path3 = Paths.get("F:\\", "nio");Path path4 = Paths.get("nioo\\hi.txt");path3 = path3.resolve(path4);System.out.println(path3);// File toFile(): 将Path转化为File类的对象File file = path1.toFile();//Path--->File的转换Path newPath = file.toPath();//File--->Path的转换}
}
File 与 Path 之间的转换:
Path —> File 的转换:使用 toFile() 方法
Fil e—> Path 的转换:使用 toPath() 方法
Files 工具类的使用
package com.laoyang.test.day8;import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.*;
import java.util.Iterator;/*** @ClassName RandomStorageFileStreamTest* @Description: Files工具类的使用:操作文件或目录的工具类* @Author Laoyang* @Date 2021/10/26 11:23*/
public class FilesTest {@Testpublic void test1() throws IOException{Path path1 = Paths.get("F:\\nio", "hello.txt");Path path2 = Paths.get("heihei.txt");// Path copy(Path src, Path dest, CopyOption … how) : 文件的复制//要想复制成功,要求path1对应的物理上的文件存在。path1对应的文件没有要求。
// Files.copy(path1, path2, StandardCopyOption.REPLACE_EXISTING);// Path createDirectory(Path path, FileAttribute<?> … attr) : 创建一个目录//要想执行成功,要求path对应的物理上的文件目录不存在。一旦存在,抛出异常。Path path3 = Paths.get("F:\\nio\\nio1");
// Files.createDirectory(path3);// Path createFile(Path path, FileAttribute<?> … arr) : 创建一个文件//要想执行成功,要求path对应的物理上的文件不存在。一旦存在,抛出异常。Path path4 = Paths.get("F:\\nio\\hi.txt");
// Files.createFile(path4);// void delete(Path path) : 删除一个文件/目录,如果不存在,执行报错
// Files.delete(path4);// void deleteIfExists(Path path) : Path对应的文件/目录如果存在,执行删除.如果不存在,正常执行结束Files.deleteIfExists(path3);// Path move(Path src, Path dest, CopyOption…how) : 将 src 移动到 dest 位置//要想执行成功,src对应的物理上的文件需要存在,dest对应的文件没有要求。
// Files.move(path1, path2, StandardCopyOption.ATOMIC_MOVE);// long size(Path path) : 返回 path 指定文件的大小long size = Files.size(path2);System.out.println(size);}@Testpublic void test2() throws IOException{Path path1 = Paths.get("F:\\nio", "hello.txt");Path path2 = Paths.get("heihei.txt");// boolean exists(Path path, LinkOption … opts) : 判断文件是否存在System.out.println(Files.exists(path2, LinkOption.NOFOLLOW_LINKS));// boolean isDirectory(Path path, LinkOption … opts) : 判断是否是目录//不要求此path对应的物理文件存在。System.out.println(Files.isDirectory(path1, LinkOption.NOFOLLOW_LINKS));// boolean isRegularFile(Path path, LinkOption … opts) : 判断是否是文件// boolean isHidden(Path path) : 判断是否是隐藏文件//要求此path对应的物理上的文件需要存在。才可判断是否隐藏。否则,抛异常。
// System.out.println(Files.isHidden(path1));// boolean isReadable(Path path) : 判断文件是否可读System.out.println(Files.isReadable(path1));// boolean isWritable(Path path) : 判断文件是否可写System.out.println(Files.isWritable(path1));// boolean notExists(Path path, LinkOption … opts) : 判断文件是否不存在System.out.println(Files.notExists(path1, LinkOption.NOFOLLOW_LINKS));}/*** StandardOpenOption.READ:表示对应的Channel是可读的。* StandardOpenOption.WRITE:表示对应的Channel是可写的。* StandardOpenOption.CREATE:如果要写出的文件不存在,则创建。如果存在,忽略* StandardOpenOption.CREATE_NEW:如果要写出的文件不存在,则创建。如果存在,抛异常*/@Testpublic void test3() throws IOException{Path path1 = Paths.get("F:\\nio", "hello.txt");// InputStream newInputStream(Path path, OpenOption…how):获取 InputStream 对象InputStream inputStream = Files.newInputStream(path1, StandardOpenOption.READ);// OutputStream newOutputStream(Path path, OpenOption…how) : 获取 OutputStream 对象OutputStream outputStream = Files.newOutputStream(path1, StandardOpenOption.WRITE,StandardOpenOption.CREATE);// SeekableByteChannel newByteChannel(Path path, OpenOption…how) : 获取与指定文件的连接,how 指定打开方式。SeekableByteChannel channel = Files.newByteChannel(path1, StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);// DirectoryStream<Path> newDirectoryStream(Path path) : 打开 path 指定的目录Path path2 = Paths.get("F:\\teach");DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path2);Iterator<Path> iterator = directoryStream.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}}
}
在 IDEA 中手动导入第三方 jar 包
- 右键对应的模块,点击 New,然后创建一个普通文件夹,用于存放第三方 jar 包(文件夹名一般为:lib)
- 将对应的 jar 包复制到刚创建好的文件夹下,这个时候 jar 包还是不能用的
- 右键对应的 jar 包,点击 Add as Library,然后在选择要添加到的模块,选好以后点 OK 即可
第三方 jar 包的使用
package com.laoyang.test.day9;import org.apache.commons.io.FileUtils;
import org.junit.Test;
import java.io.File;
import java.io.IOException;/*** @ClassName FileUtilsTest* @Description: 第三方 jar 包的使用* @Author Laoyang* @Date 2021/10/26 11:25*/
public class FileUtilsTest {@Testpublic void testOne() {File fileA = new File("2.jpg");File fileB = new File("666.jpg");try {FileUtils.copyFile(fileA, fileB);} catch (IOException e) {e.printStackTrace();}}
}
import org.apache.commons.io.FileUtils; 就是使用了我们刚才导入进去的 jar 包
好处:更加方便快捷
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
相关文章
- Cadence Orcad 中批量修改off-page connector 标签或者net名字方法
最近做一项目需要批量修改off-page connector 和net网络标号,折腾了好半天总算完成了,现在抽空做一下总结:第一步:打开原理图文件后选择要修改的页,如果修改整个原理图就选中整个原理图第二步:点击Edit-->Browes->off-page connect 然后按shift 选中要修改的信号第…...
2024/5/4 17:04:26 - 关于 android 虚拟机显示在了界面里面,怎么把模拟器放到到android studio窗口外面这件事
关于 android 虚拟机显示在了界面里面,怎么把模拟器放到到android studio窗口外面这件事 如图所示: 解决方法: 再次重启 AndroidStudio 即可...
2024/5/2 21:29:23 - 什么说 Golang 是 DevOps 专业人士的第一首选?
Golang 是当今最受欢迎的编程语言之一,现在就让我们来看看它在 DevOps 空间中能够做什么? Golang,也称为 “Go”,是一种具备快速和高性能的编译型语言,这是被设计成为易于阅读和理解的原因。Go 是由 Rob Pike…...
2024/5/4 23:18:17 - 水仙花数(入门)
春天是鲜花的季节,水仙花就是其中最迷人的代表,数学上有个水仙花数,他是这样定义的: “水仙花数”是指一个三位数,它的各位数字的立方和等于其本身,比如:15313533^3。 现在要求输出所有在m和n范…...
2024/5/2 21:29:16 - LeetCode 260. 只出现一次的数字 III
题目链接 方法一: 思路:先排序,然后对相邻两个元素进行比较。该方法不满足线性时间复杂度。 代码: class Solution {public int[] singleNumber(int[] nums) {int[] r new int[2];int t0;Arrays.sort(nums);for(int i0;i<…...
2024/5/2 5:37:32 - 3d游戏成套模型素材网站合集看过来
整理了自己常用的游戏成套的模型网站,包含各种类型,赶紧来码住,接下来小编会为大家详细介绍一下! 1、爱给网(高品质 全品类 最推荐) 爱给网的“游戏成套”素材的都还挺不错的,感兴趣的朋友可以…...
2024/5/2 19:33:11 - 计算机网络 --- 网络层IP数据报
IP数据报格式 首部 版本:IPv4/IPv6首部长度:单位是4B,最小为5。也就是说如果首部长度的四个bit的出来的数是8,那么首部长度就是8 * 4B 32B也就是32字节区分服务:指示期望获得哪种类型的服务总长度:首部数据…...
2024/5/2 21:29:08 - c语言中的汉诺塔问题详解
汉诺塔问题是一个古典的数学问题,也是c语言学习中一个用递归方法解题的典型实例,我们先看一下原题。 相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自…...
2024/5/2 21:29:04 - 【预测模型】基于BP神经网络预测股票matlab代码
1 简介 BP神经网络模型是目前应用最为广泛神经网络之一。它的本质是通过对历史数据的学习找出数据变化趋势之间的非线性关系,并通过输出量与预期值之间的误差不断调整网络中各个单元的权重,使整个网络的误差最小。因此࿰…...
2024/5/2 21:28:59 - 《这就是软件工程师》读书笔记
第一部分 行业地图 选择:一线和次一线城市机会巨大 1.北京 2.上海、深圳、杭州 3.成都、广州、南京、厦门 4.福州、苏州 软件工程师的四大台阶: 阶段 能力 1 新手阶段 执行力 2 进阶阶段 设计能力 3 高手阶段 融会贯通的能力 4 行业大…...
2024/5/2 21:28:55 - java名字的由来
名字来源 Java是印度尼西亚爪哇岛的英文名称,因盛产咖啡而闻名。Java语言中的许多库类名称,多与咖啡有关:如JavaBeans(咖啡豆)、NetBeans(网络豆)以及ObjectBeans(对象豆)等等。SUN和JAVA的标识也正是一杯正冒着热气的咖啡。[2]语言起源 Java平台和语言最开始只是SUN公…...
2024/5/2 21:28:51 - 筑基_C_10_泛型的stack结构
泛型的stack结构1 以int型为基础设计1个栈1.1 定义栈结构体1.2 打印栈元素1.3 栈操作的实现1.4 测试用例1.4.1 测试int型1.4.2 测试float型2 增加对指针数组的支持2.1 改进StackDispose函数2.2 改进StackNew函数2.3 创建string型栈的打印函数1个小失误2.4 测试用例1 以int型为基…...
2024/5/2 21:28:48 - MyBatis框架缓存
目录 MyBatis框架缓存分类 1、一级缓存 2、二级缓存 使用二级缓存的方法 一、在MyBatis框架核心配置文件中设置全局开启二级缓存 二、开启全局二级缓存之后。默认SQL映射文件是不使用的,还需要在SQL映射文件中配置缓存。 三、配置好之后可对个别查询语句进行调整 M…...
2024/5/2 21:28:44 - autojs-造雾者-脚本云平台(源码)
作者:造雾者 微信:LGD-Lang QQ:130468168 autojs/JavaScript 有偿收徒 有偿解答 欢迎打扰~ 话不多说直接上代码 "ui";const baseUrl"https://www.biqingju.com/"; const marketUrl"http://www.biqingju.com:8888/autojs/api/v3/market/ap…...
2024/5/2 21:28:39 - 添加定时任务:创建定时任务类,使用cron表达式
注意点:在类中的cron表达式只能写6位,不能写7位,否则会报错,因为它的年是默认为当前年份。...
2024/5/2 21:28:35 - 数据结构--栈和队列(下)
栈和队列(下)--队列一丶队列(1)队列的概念(2)队列的分类1>顺序队列第一种:front不动第二种:rear不动2>链式队列二丶循环队列关于返回队列尾三丶双端队列四丶总结一丶队列 什么…...
2024/5/2 21:28:31 - 动态规划初见
最近上课讲了动态规划,写这篇当作自己的笔记罢了,希望能温故而知新。 动态规划 动态规划(Dynamic Programming)与分治方法类似,将问题划分成为若干个子问题,求解完子问题后把解组合起来成为原问题的解。但…...
2024/5/2 21:28:28 - ubuntu下使用virtualbox安装MacOS踩坑日记
ubuntu下使用virtualbox安装MacOS踩坑日记使用环境部署开始安装Mac 使用环境部署 virtualbox + mac.cdr(cdr格式镜像文件) virtualbox原生Mac OS的安装故不需要破解 1.virtualbox——随意什么版本建议5.0以上建议使用命令安装,安装可能出现一些问题请百度自己解决。2.virtualb…...
2024/5/2 17:06:52 - Hero因子分解和枚举l
因子分解和枚举 题一:n的第k个因子; 思路: 1.创建一个数组用于存储整数n的所有因子,然后返回k-1位的因子,如果没有k-1位则返回-1; 2.对整数n进行遍历,为了减少遍历量,只遍历到根号…...
2024/5/2 21:28:19 - ssm浏览器乱码问题
ssm浏览器乱码问题 在当前你运行页面对应的controller里面加上 RequestMapping(value "/save",produces "text/html;charsetUTF-8")当前运行的是save.jsp...
2024/5/2 21:28:15
最新文章
- 正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-12-蜂鸣器
前言: 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM(MX6U)裸机篇”视频的学习笔记,在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…...
2024/5/5 0:24:57 - 梯度消失和梯度爆炸的一些处理方法
在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言,在此感激不尽。 权重和梯度的更新公式如下: w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...
2024/3/20 10:50:27 - composer常见错误解决
在Java中,常见的问题和解决方法包括: 内存不足错误:Java应用程序在运行时可能会遇到内存不足的错误。可以通过增加JVM的堆内存大小来解决,可以通过设置-Xms和-Xmx参数来指定初始堆大小和最大堆大小。 java -Xms2G -Xmx4G YourAppl…...
2024/4/30 3:27:03 - 算法四十天-删除排序链表中的重复元素
删除排序链表中的重复元素 题目要求 解题思路 一次遍历 由于给定的链表是排好序的,因此重复的元素在链表中的出现的位置是连续的,因此我们只需要对链表进行一次遍历,就可以删除重复的元素。 具体地,我们从指针cur指向链表的头节…...
2024/5/1 13:07:13 - 【外汇早评】美通胀数据走低,美元调整
原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...
2024/5/4 23:54:56 - 【原油贵金属周评】原油多头拥挤,价格调整
原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...
2024/5/4 23:54:56 - 【外汇周评】靓丽非农不及疲软通胀影响
原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...
2024/5/4 23:54:56 - 【原油贵金属早评】库存继续增加,油价收跌
原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...
2024/5/4 23:55:17 - 【外汇早评】日本央行会议纪要不改日元强势
原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...
2024/5/4 23:54:56 - 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响
原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...
2024/5/4 23:55:05 - 【外汇早评】美欲与伊朗重谈协议
原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...
2024/5/4 23:54:56 - 【原油贵金属早评】波动率飙升,市场情绪动荡
原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...
2024/5/4 23:55:16 - 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试
原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...
2024/5/4 23:54:56 - 【原油贵金属早评】市场情绪继续恶化,黄金上破
原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...
2024/5/4 18:20:48 - 【外汇早评】美伊僵持,风险情绪继续升温
原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...
2024/5/4 23:54:56 - 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势
原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...
2024/5/4 23:55:17 - 氧生福地 玩美北湖(上)——为时光守候两千年
原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...
2024/5/4 23:55:06 - 氧生福地 玩美北湖(中)——永春梯田里的美与鲜
原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...
2024/5/4 23:54:56 - 氧生福地 玩美北湖(下)——奔跑吧骚年!
原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...
2024/5/4 23:55:06 - 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!
原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...
2024/5/4 2:59:34 - 「发现」铁皮石斛仙草之神奇功效用于医用面膜
原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...
2024/5/4 23:55:16 - 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者
原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...
2024/5/4 23:54:58 - 广州械字号面膜生产厂家OEM/ODM4项须知!
原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...
2024/5/4 23:55:01 - 械字号医用眼膜缓解用眼过度到底有无作用?
原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...
2024/5/4 23:54:56 - 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...
解析如下:1、长按电脑电源键直至关机,然后再按一次电源健重启电脑,按F8健进入安全模式2、安全模式下进入Windows系统桌面后,按住“winR”打开运行窗口,输入“services.msc”打开服务设置3、在服务界面,选中…...
2022/11/19 21:17:18 - 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。
%读入6幅图像(每一幅图像的大小是564*564) f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...
2022/11/19 21:17:16 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...
win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面,在等待界面中我们需要等待操作结束才能关机,虽然这比较麻烦,但是对系统进行配置和升级…...
2022/11/19 21:17:15 - 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...
有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows,请勿关闭计算机”的提示,要过很久才能进入系统,有的用户甚至几个小时也无法进入,下面就教大家这个问题的解决方法。第一种方法:我们首先在左下角的“开始…...
2022/11/19 21:17:14 - win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...
置信有很多用户都跟小编一样遇到过这样的问题,电脑时发现开机屏幕显现“正在配置Windows Update,请勿关机”(如下图所示),而且还需求等大约5分钟才干进入系统。这是怎样回事呢?一切都是正常操作的,为什么开时机呈现“正…...
2022/11/19 21:17:13 - 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...
Win7系统开机启动时总是出现“配置Windows请勿关机”的提示,没过几秒后电脑自动重启,每次开机都这样无法进入系统,此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一:开机按下F8,在出现的Windows高级启动选…...
2022/11/19 21:17:12 - 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...
有不少windows10系统用户反映说碰到这样一个情况,就是电脑提示正在准备windows请勿关闭计算机,碰到这样的问题该怎么解决呢,现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法:1、2、依次…...
2022/11/19 21:17:11 - 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...
今天和大家分享一下win7系统重装了Win7旗舰版系统后,每次关机的时候桌面上都会显示一个“配置Windows Update的界面,提示请勿关闭计算机”,每次停留好几分钟才能正常关机,导致什么情况引起的呢?出现配置Windows Update…...
2022/11/19 21:17:10 - 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...
只能是等着,别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚,只能是考虑备份数据后重装系统了。解决来方案一:管理员运行cmd:net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...
2022/11/19 21:17:09 - 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?
原标题:电脑提示“配置Windows Update请勿关闭计算机”怎么办?win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢?一般的方…...
2022/11/19 21:17:08 - 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...
关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!关机提示 windows7 正在配…...
2022/11/19 21:17:05 - 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...
钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...
2022/11/19 21:17:05 - 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...
前几天班里有位学生电脑(windows 7系统)出问题了,具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面,长时间没反应,无法进入系统。这个问题原来帮其他同学也解决过,网上搜了不少资料&#x…...
2022/11/19 21:17:04 - 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...
本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法,并在最后教给你1种保护系统安全的好方法,一起来看看!电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中,添加了1个新功能在“磁…...
2022/11/19 21:17:03 - 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...
许多用户在长期不使用电脑的时候,开启电脑发现电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机。。.这要怎么办呢?下面小编就带着大家一起看看吧!如果能够正常进入系统,建议您暂时移…...
2022/11/19 21:17:02 - 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...
配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!配置windows update失败 还原更改 请勿关闭计算机&#x…...
2022/11/19 21:17:01 - 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...
不知道大家有没有遇到过这样的一个问题,就是我们的win7系统在关机的时候,总是喜欢显示“准备配置windows,请勿关机”这样的一个页面,没有什么大碍,但是如果一直等着的话就要两个小时甚至更久都关不了机,非常…...
2022/11/19 21:17:00 - 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...
当电脑出现正在准备配置windows请勿关闭计算机时,一般是您正对windows进行升级,但是这个要是长时间没有反应,我们不能再傻等下去了。可能是电脑出了别的问题了,来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...
2022/11/19 21:16:59 - 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...
我们使用电脑的过程中有时会遇到这种情况,当我们打开电脑之后,发现一直停留在一个界面:“配置Windows Update失败,还原更改请勿关闭计算机”,等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢࿰…...
2022/11/19 21:16:58 - 如何在iPhone上关闭“请勿打扰”
Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...
2022/11/19 21:16:57