I/O是Input/Output的缩写, I/O技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等。Java程序中,对于数据的输入/输出操作以“流(stream)” 的方式进行。java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。
输入input:读取外部数据(磁 盘、光盘等存储设备的数据)到 程序(内存)中。
输出output:将程序(内存) 数据输出到磁盘、光盘等存储设 备中。
按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)
按数据流的流向不同分为:输入流,输出流
按流的角色的不同分为:节点流,处理流
节点流:直接从数据源或目的地读写数据
处理流:不直接连接到数据源或目的地,而是“连接”在已存 在的流(节点流或处理流)之上,通过对数据的处理为程序提 供更为强大的读写功能。
InputStream 和 Reader 是所有输入流的基类。
程序中打开的文件 IO 资源不属于内存里的资源,垃圾回收机制无法回收该资 源,所以应该显式关闭文件 IO 资源。
FileInputStream 从文件系统中的某个文件中获得输入字节。FileInputStream 用于读取非文本数据之类的原始字节流。要读取字符流,需要使用 FileReader
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 关闭此输入流并释放与该流关联的所有系统资源。
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 和 writer 是所有输入流的基类。
因为字符流直接以字符作为操作单位,所以 Writer 可以用字符串来替换字符数组, 即以 String 对象作为参数 :void write(String str); / void write(String str, int off, int len);
FileOutputStream 从文件系统中的某个文件中获得输出字节。FileOutputStream 用于写出非文本数据之类的原始字节流。要写出字符流,需要使用 FileWriter
void write(int b) 将指定的字节写入此输出流。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 关闭此输出流并释放与该流关联的所有系统资源。
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 关闭此输出流并释放与该流关联的所有系统资源。
注:以下代码演示均为字符流 (FileReader、FileWriter)的操作,处理字节流文件请更换为字节流(FileInputStream、FileOutputStream)
- //1.建立一个流对象,将已存在的一个文件加载进流。
- FileReader fr = new FileReader(new File("E:\\JAVA\\java2\\src\\day8\\hello.png"));
-
- //2.创建一个临时存放数据的数组。
- char[] c = new char[1024];
-
- //3.调用流对象的读取方法将流中的数据读入到数组中。
- fr.read(c);
-
- //4.关闭流的资源
- fr.close();
读取的时候使用:int read()方法
- FileReader fr = null;
- try {
- //1.建立一个流对象,将已存在的一个文件加载进流。
- fr = new FileReader(new File("E:\\JAVA\\java2\\src\\day8\\hello.png"));
-
- //2.调用流对象的读取方法将流中的数据读入到数组中。
- int len;
- while ((len = fr.read()) != -1){
- System.out.print((char) len);
- }
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- //3.关闭流的资源
- try {
- if (fr != null)
- fr.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
读取的时候使用:int read(char[] cbuf)方法
- FileReader fr = null;
- try {
- //1.建立一个流对象,将已存在的一个文件加载进流。
- fr = new FileReader(new File("E:\\JAVA\\java2\\src\\day8\\hello.png"));
-
- //2.创建一个临时存放数据的数组。
- char[] cfr = new char[1024];
-
- //3.调用流对象的读取方法将流中的数据读入到数组中。
- int len;
- while ((len = fr.read(cfr)) != -1){
- //方式1:
- for (int i = 0; i < len; i++) {
- System.out.print(cfr[i]);
- }
-
- //方式2:
- String str = new String(cfr,0,len);
- System.out.print(str);
- }
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- //4.关闭流的资源
- try {
- if (fr != null)
- fr.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- //1.创建流对象,建立数据存放文件
- FileWriter fw = new FileWriter(new File("E:\\JAVA\\java2\\src\\day8\\hello.png"));
- //2.调用流对象的写入方法,将数据写入流
- fw.write("写入的数据。。。");
- //3.关闭流资源,并将流中的数据清空到文件中。
- fw.close();
- FileWriter fw = null;
- try {
- //1.创建流对象,建立数据存放文件
- fw = new FileWriter(new File("E:\\JAVA\\java2\\src\\day8\\helloCopy.png"));
-
- //2.调用流对象的写入方法,将数据写入流
- fw.write("I am a superman\n");
- fw.write("you are a superman too");
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- //3.关闭流资源,并将流中的数据清空到文件中。
- if (fw != null){
- try {
- fw.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- FileReader srcFile = null;
- FileWriter destFile = null;
- try {
- //1.创建输入输出流的对象,指明读入和写出的文件
- srcFile = new FileReader(new File("E:\\JAVA\\java2\\src\\day8\\hello.png"));
- destFile = new FileWriter(new File("E:\\JAVA\\java2\\src\\day8\\hello1.png"));
-
- //2.数据的读入和写出操作
- char[] cbuf = new char[1024];
- //记录每次读入到cbuf数组中的字符的个数
- int len;
- while ((len = srcFile.read(cbuf)) != -1){
- //每次写出len个字符
- destFile.write(cbuf,0,len);
- }
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- //3.关闭流资源
- /*//方式1:
- try {
- if (destFile != null)
- destFile.close();
- } catch (IOException e) {
- e.printStackTrace();
- }finally {
- try {
- if (srcFile != null)
- srcFile.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }*/
-
- //方式2:
- try {
- if (destFile != null)
- destFile.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- try {
- if (srcFile != null)
- srcFile.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
1.定义文件路径时,注意:可以用“/”或者“\\”。
2.在写入一个文件时,如果使用构造器FileOutputStream(file),则目录下有同名文件将被覆盖。
3.如果使用构造器FileOutputStream(file,true),则目录下的同名文件不会被覆盖,在文件内容末尾追加内容。
4.在读取文件时,必须保证该文件已存在,否则报异常。
5.字节流操作字节,比如:.mp3,.avi,.rmvb,mp4,.jpg,.doc,.ppt
6.字符流操作字符,只能操作普通文本文件。最常见的文本文件:.txt,.java,.c,.cpp 等语言的源代码。尤其注意.doc,excel,ppt这些不是文本文件。
1.缓冲流是处理流的一种,内部提供了一个缓冲区,提供流的读取、写入的速度。
2.为了提高数据读写的速度,Java API提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组,缺省使用8192个字节(8Kb)的缓冲区。
3.缓冲流要“套接”在相应的节点流之上,根据数据操作单位可以把缓冲流分为:
操作字节数据类型的:BufferedInputStream 和 BufferedOutputStream
操作字符数据类型的:BufferedReader 和 BufferedWriter
4.当使用BufferedInputStream读取字节文件时,BufferedInputStream会一次性从文件中读取8192个(8Kb),存在缓冲区中,直到缓冲区装满了,才重新从文件中读取下一个8192个字节数组。
5.向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满,BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法flush()可以强制将缓冲区的内容全部写入输出流
6.flush()方法的使用:手动将buffer中内容写入文件
7.关闭流的顺序和打开流的顺序相反。只要关闭最外层流即可,关闭最外层流也会相应关闭内层节点流
8.如果是带缓冲区的流对象的close()方法,不但会关闭流,还会在关闭流之前刷新缓冲区,关闭后不能再写出
9.当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区
- /**
- * @description 使用缓冲流复制字节类型的数据
- * @author Alvin
- * @param srcFile:需要复制的字节文件地址
- * @param destFile:复制到的文件地址
- * @date 2022年8月17日15:19:39
- * @version
- */
- private static void copyFileWithBuffered(String srcFile,String destFile) {
- BufferedInputStream bis = null;
- BufferedOutputStream bos = null;
- try {
- //1.创建文件和相应的节点流
- FileInputStream fis = new FileInputStream(new File(srcFile));
- FileOutputStream fos = new FileOutputStream(new File(destFile));
-
- //2.创建缓冲流
- bis = new BufferedInputStream(fis);
- bos = new BufferedOutputStream(fos);
-
- //3.读取、写入
- byte[] bbuf = new byte[1024];
- int len;
- while ((len = bis.read(bbuf)) != -1){
- bos.write(bbuf,0,len);
- bos.flush();
- }
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- //4.流资源关闭(要求:先关闭外层的流,再关闭内层的流)
- try {
- if (bos != null)
- bos.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- if (bis != null) {
- try {
- bis.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- //说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略.
- // fos.close();
- // fis.close();
- }
- }
- /**
- * @description 使用缓冲流复制字符类型的数据
- * @author Alvin
- * @param srcFile:需要复制的字符文件地址
- * @param destFile:复制到的文件地址
- * @date 2022年8月17日16:05:04
- * @version
- */
- public static void copyCharFileWithBuffered(String srcFile,String destFile){
- BufferedReader bd = null;
- BufferedWriter bw = null;
- try {
- //1.创建文件和相应的节点流+缓冲流
- bd = new BufferedReader(new FileReader(new File(srcFile)));
- bw = new BufferedWriter(new FileWriter(new File(destFile)));
-
- //2.读写操作
- /*//方式1:使用char[]数组
- char[] cbuf = new char[1024];
- int len;
- while ((len = bd.read(cbuf)) != -1){
- bw.write(cbuf,0,len);
- }*/
-
- //方式2:使用String
- String data;
- while ((data = bd.readLine()) != null){
- //方法1:
- bw.write(data + "\n");//data中不包含换行符
- //方法2:
- bw.write(data);
- bw.newLine();//提供换行的操作
- }
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- //3.关闭流资源
- if (bw != null) {
- try {
- bw.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- try {
- if (bd != null)
- bd.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- }
1.转换流:属于字符流。
2.转换流提供了在字节流和字符流之间的转换
3.Java API提供了两个转换流:
InputStreamReader:将InputStream转换为Reader(解码:字节、字节数组 --->字符数组、字符串)
OutputStreamWriter:将Writer转换为OutputStream(编码:字符数组、字符串 ---> 字节、字节数组)
4.字节流中的数据都是字符时,转成字符流操作更高效。
5.很多时候我们使用转换流来处理文件乱码问题。实现编码和解码的功能。
- InputStreamReader isr = null;
- OutputStreamWriter osr = null;
- try {
- FileInputStream fis = new FileInputStream(new File("E:\\JAVA\\java2\\src\\day8\\hello.txt"));
- FileOutputStream fos = new FileOutputStream(new File("E:\\JAVA\\java2\\src\\day8\\hello1.txt"));
-
- isr = new InputStreamReader(fis,"utf-8");//该“utf-8“参数指明了字符集,具体使用哪个字符集,取决于文件保存时使用的字符集
- osr = new OutputStreamWriter(fos,"GBK");//再次编码的时候,可以指定新的字符集
-
- char[] cbuf = new char[1024];
- int len;
- while ((len = isr.read(cbuf)) != -1){
- osr.write(cbuf,0,len);
- }
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- if (osr != null) {
- try {
- osr.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- try {
- if (isr != null)
- isr.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
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”时,退出程序。*/
- public static void main(String[] args) {
- BufferedReader br = null;
- try {
- // 把"标准"输入流(键盘输入)这个字节流包装成字符流,再包装成缓冲流
- InputStreamReader isr = new InputStreamReader(System.in);
- br = new BufferedReader(isr);
-
- while (true){
- System.out.println("请输入字符串:");
- String data = br.readLine(); // 读取用户输入的一行数据 --> 阻塞程序
- if ("e".equalsIgnoreCase(data) || "exit".equalsIgnoreCase(data)){
- System.out.println("程序结束!");
- break;
- }
- String upperCase = data.toUpperCase();
- System.out.println(upperCase);
- }
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- if (br != null) {
- try {
- br.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
-
- }
public static void setIn(InputStream in)
- public static void main(String[] args) {
- OutputStreamWriter osw = null;
- BufferedReader br = null;
- try {
- //创建新的文件输入流
- InputStream ps = new FileInputStream("java2\\\\src\\\\dayExercise\\\\Files\\\\log.txt");
- //创建标准输出流
- osw = new OutputStreamWriter(System.out);
- //将标准输入流替换成新的文件输入流
- System.setIn(ps);
- br = new BufferedReader(new InputStreamReader(System.in));
- char[] cbuf = new char[1024];
- //此时读入内存的为新的文件输入流内容,非控制台输入的内容
- int i = br.read(cbuf);
- //输出在控制台的为文件内容如
- osw.write(cbuf,0,i);
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- if (br != null)
- br.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- try {
- if (osw != null)
- osw.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
public static void setOut(PrintStream out)
- public static void main(String[] args) {
- try {
- //创建输出流,保存键盘输出的内容
- PrintStream ps = new PrintStream("java2\\src\\dayExercise\\Files\\log.txt");
- //创建标准程序输出流,用来输出提示语
- PrintStream out = System.out;
- //键盘录入数据
- Scanner scanner = new Scanner(System.in);
- String line = "";
- System.out.println("请输入:");
- while (scanner.hasNextLine()) {
- line = scanner.nextLine();
- if (line.equalsIgnoreCase("exit")){
- System.out.println("程序结束!输入的信息已存储到对应文件中。");
- break;
- }
- //创新指定输出流
- System.setOut(ps);
- System.out.println(line);
- //还原为标准输出流,输入提示语句
- System.setOut(out);
- System.out.println("输入的信息已存储到对应文件中。请继续输入(输入exit结束程序):");
- }
- } catch (FileNotFoundException e) {
- // TODO: handle exception
- e.printStackTrace();
- }
- }
实现将基本数据类型的数据格式转化为字符串输出
打印流:PrintStream和PrintWriter
1.提供了一系列重载的print()和println()方法,用于多种数据类型的输出
2.PrintStream和PrintWriter的输出不会抛出IOException异常
3.PrintStream和PrintWriter有自动flush功能
4.PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用 PrintWriter 类。
5.System.out返回的是PrintStream的实例
- //打印流可以使用System.setOut()方法将本来通过[System.out.print()]方法输入在控制台的文件给储存起来
- PrintStream ps = null;
- try {
- FileOutputStream fos = new FileOutputStream(new File("E:\\JAVA\\java2\\src\\day8\\hello1.txt"));
-
- // 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区)
- ps = new PrintStream(fos, true);
- if (ps != null) {// 把标准输出流(控制台输出)改成文件存储
- System.setOut(ps);
- }
- //打印ASCII的字符
- for (int i = 0; i < 255; i++) {
- System.out.print((char) i);
- if (i % 50 == 0) {// 每50个数据一行
- System.out.println();//换行
- }
- }
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } finally {
- try {
- if (ps != null)
- ps.close();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
为了方便地操作Java语言的基本数据类型和String的数据,可以使用数据流。
数据流有两个类:(用于读取和写出基本数据类型、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即可。
- //将内存中的字符串、基本数据类型的变量写出到文件中。
- DataOutputStream dos = null;
- try {
- dos = new DataOutputStream(new FileOutputStream("E:\\JAVA\\java2\\src\\day8\\data.dat"));
- dos.writeUTF("我是中国人!");
- dos.flush();//刷新操作,将内存中的数据写入文件
- dos.writeBoolean(true);
- dos.flush();
- dos.writeLong(12121212L);
- dos.flush();
- System.out.println("写入成功");
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- if (dos != null)
- dos.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
注意点:读取不同类型的数据的顺序要与当初写入文件时,保存的数据的顺序一致!
- //将文件中存储的基本数据类型变量和字符串读取到内存中,保存在变量中。
- //注意点:读取不同类型的数据的顺序要与当初写入文件时,保存的数据的顺序一致!
- DataInputStream dis = null;
- try {
- dis = new DataInputStream(new FileInputStream("E:\\JAVA\\java2\\src\\day8\\data.dat"));
-
- String str = dis.readUTF();
- boolean b = dis.readBoolean();
- Long l = dis.readLong();
- System.out.println(str);
- System.out.println(b);
- System.out.println(l);
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- if (dis != null)
- dis.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
1.ObjectInputStream 和 ObjectOutputStream
2.作用:用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
3.ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
4.序列化:用ObjectOutputStream类保存基本类型数据或对象的机制
反序列化:用ObjectInputStream类读取基本类型数据或对象的机制
5.序列化机制:
5.1对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。 当其它程序获取了这种二进制流,就可以恢复成原来的Java对象。
5.2序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原
5.3序列化是 RMI(Remote Method Invoke – 远程方法调用)过程的参数和返回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是JavaEE 平台的基础
6.要想一个java对象是可序列化的,需要满足相应的要求。
6.1如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一(Serializable、Externalizable )。否则,会抛出NotSerializableException异常
6.2凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:private static final long serialVersionUID; serialVersionUID用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象进行版本控制,有关各版本反序列化时是否兼容。如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。故建议,显式声明。
6.3简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)
6.4如果某个类的属性不是基本数据类型或 String 类型,而是另一个引用类型,那么这个引用类型必须是可序列化的,否则拥有该类型的Field 的类也不能序列化
序列化过程:将内存中的java对象保存到磁盘中或通过网络传输出去,使用ObjectOutputStream实现
- ObjectOutputStream oos = null;
- try {
- //创建一个 ObjectOutputStream
- oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
-
- //调用 ObjectOutputStream 对象的 writeObject(对象) 方法输出可序列化对象
- oos.writeObject(new String("我爱我得祖国"));
- //注意写出一次,操作flush()一次
- oos.flush();
- oos.writeObject(new Person("张三",23,001));
- oos.flush();
- oos.writeObject(new Person("李四",24,002,new Account(1000)));
- oos.flush();
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- if (oos != null)
- oos.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
反序列化:将磁盘文件中的对象还原为内存中的一个java对象,使用ObjectInputStream来实现
- ObjectInputStream ois = null;
- try {
- //创建一个 ObjectInputStream
- ois = new ObjectInputStream(new FileInputStream("object.dat"));
-
- //调用 readObject() 方法读取流中的对象
- Object o = ois.readObject();
- String str = (String) o;
-
- Person p1 = (Person) ois.readObject();
- Person p2 = (Person) ois.readObject();
- Person p3 = (Person) ois.readObject();
-
- System.out.println(str);
- System.out.println(p1);
- System.out.println(p2);
- System.out.println(p3);
- } catch (IOException e) {
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- } finally {
- try {
- if (ois != null)
- ois.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
1.我们知道它用于序列化,是空方法接口
2.实现了Serializable接口的对象,可将它们转换成一系列字节,并可在以后完全恢复回原来的样子。这一过程亦可通过网络进行。这意味着序列化机制能自动补偿操作系统间的差异。换句话说,可以先在Windows机器上创建一个对象,对其序列化,然后通过网络发给一台Unix机器,然后在那里准确无误地重新“装配”。不必关心数据在不同机器上如何表示,也不必关心字节的顺序或者其他任何细节。
3.由于大部分作为参数的类如String、Integer等都实现了java.io.Serializable的接口,也可以利用多态的性质,作为参数使接口更灵活。
RandomAccessFile的使用
1. RandomAccessFile直接继承于java.lang.Object类,实现了DataInput和DataOutput接口
2. RandomAccessFile既可以作为一个输入流,又可以作为一个输出流
3. 如果RandomAccessFile作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建。如果写出到的文件存在,则会对原有文件内容进行覆盖。(默认情况下,从头覆盖)
4. RandomAccessFile 对象包含一个记录指针,用以标示当前读写处的位置。RandomAccessFile 类对象可以自由移动记录指针:
long getFilePointer():获取文件记录指针的当前位置
void seek(long pos):将文件记录指针定位到 pos 位置
5. 可以通过相关的操作,实现RandomAccessFile“插入”数据的效果
构造器
public RandomAccessFile(File file, String mode)
public RandomAccessFile(String name, String mode)
< 创建 RandomAccessFile 类实例需要指定一个 mode 参数,该参数指定 RandomAccessFile 的访问模式:
r: 以只读方式打开
rw:打开以便读取和写入
rwd:打开以便读取和写入;同步文件内容的更新
rws:打开以便读取和写入;同步文件内容和元数据的更新
< 如果模式为只读r。则不会创建文件,而是会去读取一个已经存在的文件,如果读取的文件不存在则会出现异常。 如果模式为rw读写。如果文件不存在则会去创建文件,如果存在则不会创建。
< JDK1.6上面写的每次write数据时,“rw”模式,数据不会立即写到硬盘中;而“rwd”,数据会别立即写入硬盘,如果写数据过程发生异常,“rwd”模式中已被write的数据被保存在硬盘中,而“rw”则全部丢失。
- RandomAccessFile raf1 = null;
- RandomAccessFile raf2 = null;
- try {
- //创建读入文件流
- raf1 = new RandomAccessFile(new File("E:\\JAVA\\java2\\正能量.png"), "r");
- //创建写出文件流
- raf2 = new RandomAccessFile(new File("E:\\JAVA\\java2\\正能量1.png"), "rw");
-
- byte[] bbuf = new byte[1024];
- int len;
- while ((len = raf1.read(bbuf)) != -1){
- raf2.write(bbuf,0,len);
- }
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- if (raf1 != null)
- raf1.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- try {
- if (raf2 != null)
- raf2.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
使用RandomAccessFile实现数据的插入效果
使用StringBuffer存储插入位置后边的内容:
- RandomAccessFile raf = null;
- try {
- //需求:在hello.txt文件角标为3的位置插入”abc”
- raf = new RandomAccessFile("E:\\JAVA\\java2\\src\\day8\\hello.txt","rw");
-
- //将指针调到角标为3的位置
- raf.seek(3);
- //保存指针3后面的所有数据放到StringBuilder中
- StringBuffer StrBuf = new StringBuffer((int) new File("E:\\JAVA\\java2\\src\\day8\\hello.txt").length());
- byte[] bbuf = new byte[1024];
- int len;
- while ((len = raf.read(bbuf)) != -1){
- StrBuf.append(new String(bbuf,0,len));
- }
- //调回指针,写入“abc”
- raf.seek(3);
- raf.write("abc".getBytes());
- //将StringBuilder中的数据写入到文件中
- raf.write(StrBuf.toString().getBytes());
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- if (raf != null){
- try {
- raf.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
使用ByteArrayOutputStream存储插入位置后边的内容:
- @Test
- public void test1() throws Exception {
- RandomAccessFile raf = new RandomAccessFile("E:\\JAVA\\java2\\src\\day8\\hello.txt","rw");
- //将指针调到角标为3的位置
- raf.seek(3);
- //保存指针3后面的所有数据放到ByteArrayOutputStream对象info中
- String info = readStringFromInputStream(raf);
- //调回指针,写入“hhhhh”
- raf.seek(3);
- raf.write("hhhhh".getBytes());
- //将info中的数据写入到文件中
- raf.write(info.getBytes());
- }
-
- /**
- * @description 使用ByteArrayOutputStream存储插入位置后边的内容
- * @param raf 随机存取流对象
- * @return 返回存储数据的ByteArrayOutputStream对象
- * @throws IOException
- */
- private String readStringFromInputStream(RandomAccessFile raf) throws IOException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- byte[] buffer = new byte[10];
- int len;
- while ((len = raf.read(buffer)) != -1) {
- baos.write(buffer, 0, len);
- }
-
- return baos.toString();
- }
1. Java NIO (New IO,Non-Blocking IO)是从Java 1.4版本开始引入的一套新的IO API,可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的(IO是面向流的)、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。
2. Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套就是网络编程NIO。
3. 随着 JDK 7 的发布,Java对NIO进行了极大的扩展,增强了对文件处理和文件系统特性的支持,以至于我们称他们为 NIO.2。因为 NIO 提供的一些功能,NIO已经成为文件处理中越来越重要的部分。
4. 早期的Java只提供了一个File类来访问文件系统,但File类的功能比较有限,所提供的方法性能也不高。而且,大多数方法在出错时仅返回失败,并不会提供异常信息。
5. NIO. 2为了弥补这种不足,引入了Path接口,代表一个平台无关的平台路径,描述了目录结构中文件的位置。Path可以看做是java.io.File类的升级版本。也可以表示文件或文件目录,与平台无关
6. 同时,NIO.2在java.nio.file包下还提供了Files、Paths工具类,Files包含了大量静态的工具方法来操作文件;Paths则包含了两个返回Path的静态工厂方法。
7. Paths 类提供的静态 get() 方法用来获取 Path 对象(如何实例化Path:使用Paths):
static Path get(String first, String … more) : 用于将多个字符串串连成路径
static Path get(URI uri): 返回指定uri对应的Path路径
- Path path1 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello1.txt");//相当于:new File(String filepath)
- Path path2 = Paths.get("E:\\JAVA\\java2\\src\\day8", "hello1.txt");//相当于new File(String parent,String filename);
一下代码演示用到的实例化对象path和path1
- Path path = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello1.txt");
- Path path1 = Paths.get("hello1.txt");
Path toAbsolutePath() : 作为绝对路径返回调用 Path 对象
- Path absolutePath = path.toAbsolutePath();
- System.out.println(absolutePath);//E:\JAVA\java2\src\day8\hello1.txt
- Path absolutePath1 = path1.toAbsolutePath();
- System.out.println(absolutePath1);//E:\JAVA\java2\src\day8\hello1.txt
File toFile(): 将Path转化为File类的对象 《------》Path toPath(): 将Flie转化为Path类的对象
- File file = path.toFile();//Path--->File的转换
- Path path2 = file.toPath();//File--->Path的转换
Path resolve(Path p) :合并两个路径,返回合并后的路径对应的Path对象
- Path resolve = path.resolve("day9\\hello2.txt");
- System.out.println(resolve);//E:\JAVA\java2\src\day8\hello1.txt\day9\hello2.txt
- Path resolve1 = path1.resolve("day9\\hello2.txt");
- System.out.println(resolve1);// hello1.txt\day9\hello2.txt
Path getName(int idx) : 返回指定索引位置 idx 的路径名称
- Path name = path.getName(2);
- System.out.println(name);//src
- //Path name1 = path1.getName(2);
- //System.out.println(name1);//java.lang.IllegalArgumentException
int getNameCount() : 返回Path 根目录后面元素的数量
- int nameCount = path.getNameCount();
- System.out.println(nameCount);//5
- int nameCount1 = path1.getNameCount();
- System.out.println(nameCount1);//1
Path getFileName() : 返回与调用 Path 对象关联的文件名
- Path fileName = path.getFileName();
- System.out.println(fileName);//hello1.txt
- Path fileName1 = path1.getFileName();
- System.out.println(fileName1);//hello1.txt
Path getRoot() :返回调用 Path 对象的根路径
- Path root = path.getRoot();
- System.out.println(root);//E:\
- Path root1 = path1.getRoot();
- System.out.println(root1);//null
Path getParent() :返回Path对象包含整个路径,不包含 Path 对象指定的文件路径
- Path parent = path.getParent();
- System.out.println(parent);//E:\JAVA\java2\src\day8
- Path parent1 = path1.getParent();
- System.out.println(parent1);//null
boolean isAbsolute() : 判断是否是绝对路径
- boolean absolute = path.isAbsolute();
- System.out.println(absolute);//true
- boolean absolute1 = path1.isAbsolute();
- System.out.println(absolute1);//false
boolean endsWith(String path) : 判断是否以 path 路径结束
- boolean endsWith = path.endsWith("hello1.txt");
- System.out.println(endsWith);//true
boolean startsWith(String path) : 判断是否以 path 路径开始
- boolean startsWith = path.startsWith("E:\\JAVA");
- System.out.println(startsWith);//true
String toString() : 返回调用 Path 对象的字符串表示形式
System.out.println(path.toString());//toString可是省略 //E:\JAVA\java2\src\day8\hello1.txt
java.nio.file.Files 用于操作文件或目录的工具类。
Path copy(Path src, Path dest, CopyOption … how) : 文件的复制
- Path path1 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello1.txt");
- Path path2 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello2.txt");
- //要想复制成功,要求path1对应的物理上的文件存在。path2对应的文件没有要求。
- Path pathCopy = Files.copy(path1, path2, StandardCopyOption.REPLACE_EXISTING);
Path createDirectory(Path path, FileAttribute … attr) : 创建一个目录
- Path path3 = Paths.get("E:\\JAVA\\java2\\src\\day8");//该文件磁盘中存在
- Path path4 = Paths.get("E:\\JAVA\\java2\\src\\day9");//该文件磁盘中不存在
- //要想执行成功,要求path对应的物理上的文件目录不存在。一旦存在,抛出异常。
- //Path directory = Files.createDirectory(path3);//java.nio.file.FileAlreadyExistsException
- Path directory1 = Files.createDirectory(path4);
Path createFile(Path path, FileAttribute … arr) : 创建一个文件
- Path path5 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello1.txt");//该文件磁盘中存在
- Path path6 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello3.txt");//该文件磁盘中不存在
- //要想执行成功,要求path对应的物理上的文件不存在。一旦存在,抛出异常。
- //Path file = Files.createFile(path5);//java.nio.file.FileAlreadyExistsException
- Path file1 = Files.createFile(path6);
void delete(Path path) : 删除一个文件/目录,如果不存在,执行报错
- Path path7 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello3.txt");//该文件磁盘中存在
- Path path8 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello4.txt");//该文件磁盘中不存在
- //删除一个文件/目录,如果不存在,执行报错
- Files.delete(path7);
- //Files.delete(path8);//java.nio.file.NoSuchFileException
void deleteIfExists(Path path) : Path对应的文件/目录如果存在,执行删除.如果不存在,正常执行结束
- Path path9 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello3.txt");//该文件磁盘中存在
- Path path10 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello4.txt");//该文件磁盘中不存在
- //Path对应的文件/目录如果存在,执行删除.如果不存在,正常执行结束
- Files.deleteIfExists(path9);
- Files.deleteIfExists(path10);//文件不存在时不会报错
Path move(Path src, Path dest, CopyOption…how) : 将 src 移动到 dest 位置
- Path path11 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello1.txt");//该文件磁盘中存在
- Path path12 = Paths.get("E:\\JAVA\\java2\\src\\day9\\hello1.txt");//该文件磁盘中不存在
- //要想执行成功,src对应的物理上的文件需要存在,dest对应的文件没有要求。
- Path move = Files.move(path11, path12, StandardCopyOption.ATOMIC_MOVE);
long size(Path path) : 返回 path 指定文件的大小
- Path path13 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello1.txt");//该文件磁盘中存在
- Path path14 = Paths.get("E:\\JAVA\\java2\\src\\day9\\hello1.txt");//该文件磁盘中不存在
-
- long size = Files.size(path13);
- System.out.println(size);
- //long size1 = Files.size(path14);//java.nio.file.NoSuchFileException
用于判断的方法
boolean exists(Path path, LinkOption … opts) : 判断文件是否存在
- Path path1 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello1.txt");//该文件磁盘中存在
- Path path2 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello3.txt");//该文件磁盘中不存在
- boolean exists1 = Files.exists(path1,LinkOption.NOFOLLOW_LINKS);
- System.out.println(exists1);//true
- boolean exists2 = Files.exists(path2,LinkOption.NOFOLLOW_LINKS);
- System.out.println(exists2);//false
boolean notExists(Path path, LinkOption … opts) : 判断文件是否不存在
- Path path11 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello2.txt");//文件存在
- Path path12 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello3.txt");//文件不存在
- boolean notExists1 = Files.notExists(path11,LinkOption.NOFOLLOW_LINKS);
- System.out.println(notExists1);//false
- boolean notExists2 = Files.notExists(path12,LinkOption.NOFOLLOW_LINKS);
- System.out.println(notExists2);//true
boolean isDirectory(Path path, LinkOption … opts) : 判断是否是目录
- Path path3 = Paths.get("E:\\JAVA\\java2\\src\\day8");//目录
- Path path4 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello2.txt");//文件
- boolean directory1 = Files.isDirectory(path3, LinkOption.NOFOLLOW_LINKS);
- System.out.println(directory1);//true
- boolean directory2 = Files.isDirectory(path4,LinkOption.NOFOLLOW_LINKS);
- System.out.println(directory2);//false
boolean isRegularFile(Path path, LinkOption … opts) : 判断是否是文件
- Path path5 = Paths.get("E:\\JAVA\\java2\\src\\day8");//目录
- Path path6 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello2.txt");//文件
- boolean regularFile1 = Files.isRegularFile(path5,LinkOption.NOFOLLOW_LINKS);
- System.out.println(regularFile1);//false
- boolean regularFile2 = Files.isRegularFile(path6,LinkOption.NOFOLLOW_LINKS);
- System.out.println(regularFile2);//true
boolean isHidden(Path path) : 判断是否是隐藏文件
- Path path7 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello2.txt");//文件存在
- Path path8 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello3.txt");//文件不存在
- //要求此path对应的物理上的文件需要存在。才可判断是否隐藏。否则,抛异常。
- boolean hidden = Files.isHidden(path7);
- System.out.println(hidden);//false
- //boolean hidden1 = Files.isHidden(path8);//java.nio.file.NoSuchFileException
boolean isReadable(Path path) : 判断文件是否可读
- Path path9 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello2.txt");//文件存在
- boolean readable = Files.isReadable(path9);
- System.out.println(readable);//true
boolean isWritable(Path path) : 判断文件是否可写
- Path path10 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello2.txt");//文件存在
- boolean writable = Files.isWritable(path10);
- System.out.println(writable);//true
用于操作内容的方法
SeekableByteChannel newByteChannel(Path path, OpenOption…how) : 获取与指定文件的连 接,how 指定打开方式。
- /*StandardOpenOption.READ:表示对应的Channel是可读的。
- StandardOpenOption.WRITE:表示对应的Channel是可写的。
- StandardOpenOption.CREATE:如果要写出的文件不存在,则创建。如果存在,忽略
- StandardOpenOption.CREATE_NEW:如果要写出的文件不存在,则创建。如果存在,抛异常*/
- Path path1 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello2.txt");
- SeekableByteChannel Channel = Files.newByteChannel(path1, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
DirectoryStream newDirectoryStream(Path path) : 打开 path 指定的目录
- Path path2 = Paths.get("E:\\JAVA\\java2\\src");
- DirectoryStream<Path> paths = Files.newDirectoryStream(path2);
- Iterator<Path> iterator = paths.iterator();
- while (iterator.hasNext()){
- System.out.println(iterator.next());
- }
InputStream newInputStream(Path path, OpenOption…how):获取 InputStream 对象
- Path path3 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello2.txt");
- InputStream inputStream = Files.newInputStream(path3,StandardOpenOption.READ);
OutputStream newOutputStream(Path path, OpenOption…how) : 获取 OutputStream 对象
- Path path4 = Paths.get("E:\\JAVA\\java2\\src\\day8\\hello2.txt");
- OutputStream outputStream = Files.newOutputStream(path4,StandardOpenOption.WRITE,StandardOpenOption.CREATE);