4:File类与IO流

文章目录

  • File类
    • 1:引入:
    • 2:对文件进行操作
    • 3:对目录/文件夹进行操作
  • IO流
    • 1:引入:
    • 2:字符输入 / 出流FileReader 与 FileWriter
    • 3:用try - catch - finally 处理异常
    • 4:几个常见的问题
    • 5:字节输入 / 出流FileInputStream 与 FileOutputStream
    • 6:缓冲字节流 BufferedInputStream 与 BufferedOutputStream
      • 引入:
      • 实践
      • **总结:**
    • 7:缓冲字符流
    • 8:转换流:inputstreamreader , outputstreamwriter
    • 9:System.in与System.out
    • 10:练习:键盘录入内容输出到文件中
    • 11:对象流 ObjectInputStream 与 ObjectOutputStream

File类

1:疑问一:

	public static void main(String[] args) {
        //File f = new File("A:" + File.separator + "TEXT" + File.separator + "11.docx");
        //File f1 = new File("A:" + File.separator + "TEXT" + File.separator + "11.docx");
        File f = new File("A:\\TEXT\\11.docx");
        File f1 = new File("A:\\TEXT\\11.docx");
        System.out.println(f.exists());
        System.out.println(f1.exists());
        System.out.println(f == f1);
        System.out.println(f.equals(f1)); // 比较的是两个文件的路径是否一致
    }
/*
true
true
false
true
*/
File f = new File("A:\\TEXT\\11.docx");// 创建一个引用通过传进来的路径指向磁盘中的文件。

1:引入:

文件:

内存中存放的数据在计算机关机后就会消失。要长久保存数据,就要使用硬盘、光盘、U 盘等设备。为了便于数据的管理和检索,引入了“文件”的概念。一篇文章、一段视频、一个可执行程序,都可以被保存为一个文件,并赋予一个文件名。操作系统以文件为单位管理磁盘中的数据。 一般来说,文件可分为文本文件、视频文件、音频文件、图像文件、可执行文件等多种类别,这是从文件的功能进行分类的。从数据存储的角度来说,所有的文件本质上都是一样的,都是由一个个字节组成的,归根到底都是 0、1 比特串。不同的文件呈现出不同的形态(有的是文本,有的是视频等等)

文件夹/目录

成千上万个文件如果不加分类放在一起,用户使用起来显然非常不便,因此又引入了树形目录(目录也叫文件夹)的机制,可以把文件放在不同的文件夹中,文件夹中还可以嵌套文件夹,这就便于用户对文件进行管理和使用

盘符上的文件—》封装为对象—》对象属于File类的对象–》有了这个对象,我们程序就可以直接操纵这个对象,通过这个对象获取文件的各种信息,还可以对文件进行创建 ,删除。

2:对文件进行操作

	public static void main(String[] args) throws IOException {
     // File.separator:获取当前操作系统的拼接符。
     // File f = new File("A:" + File.separator + "TEXT" + File.separator + "11.docx");
     // File f1 = new File("A:" + File.separator + "TEXT" + File.separator + "11.docx");
     File f = new File("A:\\TEXT\\11.docx");
     File f1 = new File("A:\\TEXT\\11.docx");
     File f2 = new File("A:\\TEXT\\12.docx"); // 随便写的一个目录
     // 文件是否可读/文件是否可写
     System.out.println(f.canRead()); // true
     System.out.println(f.canWrite()); // true
     System.out.println(f2.canWrite()); // false
     System.out.println(f2.canRead()); // false

     // 获取文件的名字
     System.out.println(f.getName()); // 11.docx
     System.out.println(f1.getName()); // 11.docx
     System.out.println(f2.getName()); // 12.docx

     // 获取当前文件的上级目录
     System.out.println(f.getParent()); // A:\TEXT
     System.out.println(f2.getParent()); // A:\TEXT

     // 判断当前目录下的东西是否是一个文件
     System.out.println(f.isFile()); // true
     System.out.println(f2.isFile()); // false

     // 判断当前目录下的东西是否是一个目录
     System.out.println(f.isDirectory()); // false
     System.out.println(f2.isDirectory()); // false

     // 判断当前文件是否隐藏
     System.out.println(f.isHidden()); // F
     System.out.println(f2.isHidden()); // F

     // 当前文件的大小
     System.out.println(f.length()); // 14246
     System.out.println(f2.length()); // 0

     // 当前文件是否存在
     System.out.println(f.exists()); // true
     System.out.println(f2.exists()); // false

     if (f2.exists()) {
         f2.delete();
     } else {
         f2.createNewFile();
     }

     // 比较两个对象的地址是否一致
     System.out.println(f == f1); // false -> 堆中的两个不同对象

     // 比较两个对象对应的路径
     System.out.println(f.equals(f1)); // true : 两个对象的路径是一样的。

     // 根路径相关的
     System.out.println("绝对路径: " + f.getAbsolutePath()); // 真实的一个精准的,完整的路径
     System.out.println("相对路径: " + f.getPath()); // 有一个参照物,相对这个参照物的路径。
     System.out.println("tostring: " + f.toString()); // tostring跟相对目录一致

     //
     File f5 = new File("demo.txt");
     File f6 = new File("a/b/c/demo.txt");
     if (!f5.exists()) {
         f5.createNewFile();
     }
     System.out.println("绝对路径: " + f6.getAbsolutePath()); // 绝对路径: D:\java\javacode\src\MyJavaSE\a\b\c\demo.txt
     System.out.println("相对路径: " + f6.getPath()); // 相对路径: a\b\c\demo.txt
     System.out.println("tostring: " + f6.toString()); // tostring: a\b\c\demo.txt
     System.out.println("f5的绝对路径" + f5.getAbsolutePath()); // f5的绝对路径D:\java\javacode\src\MyJavaSE\demo.txt
 }

3:对目录/文件夹进行操作

对目录进行操作跟对文件进行操作差不多。

public static void main(String[] args) throws IOException {
     // File.separator:获取当前操作系统的拼接符。
     // File f = new File("A:" + File.separator + "TEXT" + File.separator + "11.docx");
     // File f1 = new File("A:" + File.separator + "TEXT" + File.separator + "11.docx");
     File f = new File("A:\\TEXT\\145");
     File f1 = new File("A:\\TEXT\\145");
     File f2 = new File("A:\\TEXT\\146"); // 随便写的一个目录
     // 文件是否可读/文件是否可写
     System.out.println(f.canRead()); // true
     System.out.println(f.canWrite()); // true
     System.out.println(f2.canWrite()); // false
     System.out.println(f2.canRead()); // false

     // 获取文件的名字
     System.out.println(f.getName()); // 145
     System.out.println(f1.getName()); // 145
     System.out.println(f2.getName()); // 146

     // 获取当前文件的上级目录
     System.out.println(f.getParent()); // A:\TEXT
     System.out.println(f2.getParent()); // A:\TEXT

     // 判断当前目录下的东西是否是一个文件
     System.out.println(f.isFile()); // f
     System.out.println(f2.isFile()); // false

     // 判断当前目录下的东西是否是一个目录
     System.out.println(f.isDirectory()); // t
     System.out.println(f2.isDirectory()); // false

     // 判断当前文件是否隐藏
     System.out.println(f.isHidden()); // F
     System.out.println(f2.isHidden()); // F

     // 当前文件的大小
     System.out.println(f.length()); // 0
     System.out.println(f2.length()); // 0

     // 当前文件是否存在
     System.out.println(f.exists()); // true
     System.out.println(f2.exists()); // false

     if (f2.exists()) {
         f2.delete();
     } else {
         f2.createNewFile();
     }

     // 比较两个对象的地址是否一致
     System.out.println(f == f1); // false -> 堆中的两个不同对象

     // 比较两个对象对应的路径
     System.out.println(f.equals(f1)); // true : 两个对象的路径是一样的。

     // 根路径相关的
     System.out.println("绝对路径: " + f.getAbsolutePath()); // 真实的一个精准的,完整的路径
     System.out.println("相对路径: " + f.getPath()); // 有一个参照物,相对这个参照物的路径。
     System.out.println("tostring: " + f.toString()); // tostring跟相对目录一致

     //
     File f5 = new File("demo");
     File f6 = new File("a/b/c/");
     if (!f5.exists()) {
         f5.createNewFile();
     }
     System.out.println("绝对路径: " + f6.getAbsolutePath()); // 绝对路径: D:\java\javacode\src\MyJavaSE\a\b\c
     System.out.println("相对路径: " + f6.getPath()); // 相对路径: \a\b\c
     System.out.println("tostring: " + f6.toString()); // tostring: \a\b\c
     System.out.println("f5的绝对路径" + f5.getAbsolutePath()); // f5的绝对路径D:\java\javacode\src\MyJavaSE\demo

 // 与目录有关的操作-------------------------------------------------------------------------------
     File file = new File("A:\\a\\b\\c");

     // 创建单层目录 -------------
     file.mkdir();

     // 创建多层目录
     file.mkdirs();

     // 目录的删除只会删除一层,并且这一层必须是空的才会被删除。
     //file.delete();

     // 当前目录下所有的文件和目录的名字放到String数组中。
     String[] str = file.list();
     System.out.println(Arrays.toString(str)); // [1.txt, 145.docx, 2]

     // 功能更加的强大:将当前目录下所有的文件和目录的放到File数组中。
     File[] files = file.listFiles();
     for (File F : files) {
         System.out.println(F.getName() + " ->  " + F.getAbsolutePath());
     }
     /*
     1.txt ->  A:\a\b\c\1.txt
     145.docx ->  A:\a\b\c\145.docx
     2 ->  A:\a\b\c\2`
      */
 }

IO流

1:引入:

形象理解:把IO流当作一根管子。

在这里插入图片描述

注意:按照功能又分为节点流与处理流

**节点流:**FileInputStream FileOutputStream FileReader FileWriter

**处理流:**BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter InputStreamReader OutputStreamWriter

读取文件数据 是以io流的方式,但是关于网络中的数据,同样也是io流,但是并不是file了,而是专门处理网络数据的socket和serversocket,以及之后nio中的channel

在这里插入图片描述

2:字符输入 / 出流FileReader 与 FileWriter

一次读取1个字符

	public static void main(String[] args) throws IOException {
     // 1: 有一个文件 -> 创建一个File文件
     File file = new File("A:\\test.txt");
     // 2: 有一根管子,将这个管子怼到文件中 -> 创建一个FileReader对象,
     FileReader fileReader = new FileReader(file);
     // 3:有一个”吸“的操作 --> 进行读取操作
     //    一次只会读一个字符。当读到文件的结尾处,那读到是-1.
     int n = fileReader.read();
     while (n != -1) {
         System.out.print((char) n);
         n = fileReader.read();
     }
     // 4:管子不用了,要关闭管子 --> 关闭流
     // 流,数据库,网络资源,靠jvm本身没有办法帮我们关闭,此时必须程序员手动关闭:
     fileReader.close();
     System.out.println();
     method();
 }

一次读取5个字符

	public static void method() throws IOException {
     // 1:创建一个File文件
     File file = new File("A:\\test.txt");
     // 2:创建一个FileReader对象
     FileReader fileReader = new FileReader(file);
     // 3: 进行读取操作
     char[] c = new char[5]; // 缓冲数组
     // 一次读取5个返回值是有效长度
     int len = fileReader.read(c);
     while(len != -1) {
         // 错误方式
         for (int i = 0; i < c.length; i++) {
             System.out.print(c[i]); // 肝死你们,草泥马们,
         }
         // 正确方式1:
         for (int i = 0; i < len; i++) {
             System.out.print(c[i]); // 肝死你们,草泥马
         }
         // 正确方式2:
         String str = new String(c);
         System.out.print(str);

         len = fileReader.read(c);
     }
     // 4:关闭流
     fileReader.close();
 }

写操作

	public static void method2() throws IOException {
     // 1:创建一个文件
     File file = new File("A:\\T.txt");
     // 2:创建一个FileWriter怼到文件中去
     FileWriter fileWriter = new FileWriter(file);
     // 3:写动作
     String str = "abcdefghijklmn";
     /*
     char[] c = str.toCharArray(); // 缓冲数组
     fileWriter.write(c);
      */
     fileWriter.write(str);
     // 4:关闭流
     fileWriter.close();
 }

文件的复制

	// 文件的复制
 public static void main(String[] args) throws IOException {
     // 一个源文件
     File f1 = new File("A:\\test.txt");
     // 一个目标文件
     File f2 = new File("A:\\T.txt");
     // 创建一个输入管和一个输出管
     FileReader fileReader = new FileReader(f1);
     FileWriter fileWriter = new FileWriter(f2);
     // 开始动作:读动作与写动作
     char[] c = new char[5]; // 缓冲数组
     int len = fileReader.read(c); // len:有效长度
     while (len != -1) {
         fileWriter.write(c, 0, len); //注意下标范围是[0,len]
         len = fileReader.read(c);
     }
     // 关闭流
     fileWriter.close();
     fileReader.close();
 }

文件流为什么要关闭

文件流在使用完毕后需要进行关闭操作,以释放系统资源保证数据的完整性。在某些编程语言中,文件流必须在使用完毕后手动进行关闭操作,否则文件流将一直占用系统资源,可能会导致程序出现异常或无法继续进行操作不释放 其他的线程就获取不到.

3:用try - catch - finally 处理异常

public static void main(String[] args) {
        // 源文件
        File f1 = new File("A:\\test.txt");
        // 目标文件
        File f2 = new File("A:\\T.txt");
        // 输入管和一个输出管
        FileReader fileReader = null;
        FileWriter fileWriter = null;
        try {
            fileReader = new FileReader(f1);
            fileWriter = new FileWriter(f2);
            // 开始动作:读动作与写动作
            char[] c = new char[5]; // 缓冲数组
            int len = fileReader.read(c); // len:有效长度
            while (len != -1) {
                fileWriter.write(c, 0, len);
                len = fileReader.read(c);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭流
            // 注意两个流关闭的try-catch是分开写的。
            try {
                if(fileWriter != null) { // 防止空指针异常。
                    fileWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(fileReader != null) {
                    fileReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

4:几个常见的问题

(1):

文本文件:.txt .java .c .cpp —》建议使用字符流操作

非文本文件:.jpg, .mp3 , .mp4 , .docx , .ppt —》建议使用字节流操作

(2):

Q:警告:不要用字符流去操作非文本文件

A:字符流是用于处理文本数据的流,他们将数据视为字符序列,并通过读取与写入字符来操作数据。相比之下,非文本文件包含的是二进制数据,他们不是用字符表示的。因此如果你使用字符流去处理非文本文件,可能会带来如下几个问题。(1):数据的损坏:字符流会将数据读入内存,然后将其转化为字符序列,在这个过程中,如果数据中包含着不可转换为字符的字节,这些字节将被丢弃或被转换为其他的字符,这样就会导致数据的损坏。(2):效率低下:由于字符流会将二进制数据转化为字符序列,因此会产生额外的开销,这可能会导致处理非常大的二进制文件时效率降低。因此建议使用使用字节流来处理非文本文件,字节流直接读取与写入字节数据,不需要进行任何的转化,因此可以确保数据的完整性与准确性,并提高准确度。

(3):

Q:那字节流可以处理文本文件吗??

**A:字节流可以用于处理文本文件,虽然文本文件是由字符组成,但是在计算机内部,他们是用二进制数据表示的。字节流可以直接的读取与写入二进制数据,因此也可以用于处理文本文件。**当然如果你只需要处理文本文件,使用字符流会更加的方便与简单。因为字符流会自动的处理字符编码和换行符等文本特有的问题,**而字节流需要手动的处理这些问题。**然是如果你需要同时处理字符以及包含二进制数据的文件,或是需要处理非常大的文件,那么字节流是最合适的。

5:字节输入 / 出流FileInputStream 与 FileOutputStream

复制一份非文本文件

	public static void main(String[] args) throws IOException {
     // 1:创建一个源文件与一个目标文件
     File f1 = new File("A:\\TEXT\\1.1.jpg");
     File f2 = new File("A:\\TEXT\\1.2.jpg");
     // 2:创建字节流,读取字节流与写入字节流
     FileInputStream fileInputStream = new FileInputStream(f1);
     FileOutputStream fileOutputStream = new FileOutputStream(f2);
     // 3:进行传输动作
     // (1):文件是UTF-8进行存储的,所以英文字符占1个字节,中文字符占3个字节
     // (2):如果文件是文本文件,建议不要使用字节流进行读取,建议使用字符流。
     byte[] bytes = new byte[1024 * 6]; // 缓冲数组
     int len = fileInputStream.read(bytes); // 返回的是有效长度
     while(len != -1) {
         fileOutputStream.write(bytes,0 , len); // 注意这个地方的范围
         len = fileInputStream.read(bytes);
     }
     // 4:关闭流
     fileOutputStream.close();
     fileInputStream.close();
 }

6:缓冲字节流 BufferedInputStream 与 BufferedOutputStream

引入:

【1】读入一个字节,写出一个字节:

在这里插入图片描述

【2】利用缓冲字节数组:

在这里插入图片描述

【3】利用缓冲区:

在这里插入图片描述

实践

	public static void main(String[] args) throws IOException {
     // 1:创建一个源文件与一个目标文件
     File f1 = new File("A:\\TEXT\\1.1.jpg");
     File f2 = new File("A:\\TEXT\\1.2.jpg");
     // 2:创建字节流,读取字节流与写入字节流
     FileInputStream fileInputStream = new FileInputStream(f1);
     FileOutputStream fileOutputStream = new FileOutputStream(f2);
     // 3:功能加强:
     // FileInputStream外面套一个管子:BufferedInputStream
     // fileOutputStream外面套一个管子:BufferedOutputStream
     BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
     BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
     // 4:进行传输动作
     byte[] bytes = new byte[1024 * 20]; // 缓冲数组
     int len = bufferedInputStream.read(bytes); // 返回的是有效长度
     while(len != -1) {
         System.out.println(len);
         bufferedOutputStream.write(bytes,0,len);
         len = bufferedInputStream.read(bytes);
     }
     // 4:关闭流
     bufferedOutputStream.close();
     bufferedInputStream.close();
 }

总结:

		byte[] bytes = new byte[1024 * 6]; // 缓冲数组
		int len = fileInputStream.read(bytes); // 返回的是有效长度
		while(len != -1) {
         fileOutputStream.write(bytes,0 , len);
         len = fileInputStream.read(bytes);
      }

**这里 fileInputStream.read(bytes) 意思是:先从底层硬盘中一个字节一个字节的读到 bytes 数组中,当bytes数组满了或是文件读取完毕之后,再将bytes数组中的数据写入到目标文件中。读取与写入硬盘的总次数 == (文件字节的长度)/ bytes .length **

	    // 1:创建一个源文件与一个目标文件
     File f1 = new File("A:\\TEXT\\1.1.jpg");
     File f2 = new File("A:\\TEXT\\1.2.jpg");
     // 2:创建字节流,读取字节流与写入字节流
     FileInputStream fileInputStream = new FileInputStream(f1);
     FileOutputStream fileOutputStream = new FileOutputStream(f2);
     // 3:功能加强
     // FileInputStream外面套一个管子
     // fileOutputStream外面套一个管子
     BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
     BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
     // 4:进行传输动作
     byte[] bytes = new byte[1024 * 20]; // 缓冲数组
     int len = bufferedInputStream.read(bytes); // 返回的是有效长度
     while(len != -1) {
         bufferedOutputStream.write(bytes,0,len);
         len = bufferedInputStream.read(bytes);
     }
     // 4:关闭流
	   // 如果处理流包裹着节点流的话,那么其实只要关闭高级流(处理流),那么里面的字节流也会随之被关闭。
     bufferedOutputStream.close();
     bufferedInputStream.close();

这段代码中的缓冲数组可以理解为一个中转站,具体细节是这样的:读缓冲区的大小是8kb,每次从底层硬件文件中一次读8kb的字节到读缓冲区中。当缓冲区满了,然后将缓冲区中的数据放到缓冲数组中。当缓冲数组满了,就将缓冲数组中的数据放到写缓冲区中。当写缓冲区满了,就将写缓冲区的数据放到底层硬件的目标文件中。

字节流与缓冲流对比:

A:字节流:

数据是以字节为单位进行读写操作程序直接从硬件中读物或写入数据,一切文件在系统中都是以字节的形式保存的,无论你是文档文件、视频文件、音频文件…,需要读取这些文件都可以用FileInputStream去读取其保存在存储介质(磁盘等)上的字节序列,文件字节输入流的读取时,是直接同字节流中读取的。由于字节流是与硬件(存储介质)进行的读取,所以速度较慢。而CPU需要使用数据时通过read()、read(byte[])读取数据时就要受到硬件IO的慢速度限制。

在这里插入图片描述

B:缓冲流:

将一个一个的字节先存入到缓冲区中在JVM中会开辟一块缓冲区的内存空间,然后将文件中的数据读取到缓冲区中,直到读满这个缓冲,才会将缓冲区中的数据获取到程序中。
在JVM中会开辟一块缓冲区的内存空间,然后将程序中的数据写入到缓冲区中,直到写满这个缓冲,才会将缓冲区中的数据写入到文件中。

在这里插入图片描述

C:原理总结:

带缓冲的字节输入流:上面我们知道文件字节输入流的读取时,是直接同字节流中读取的。由于字节流是与硬件(存储介质)进行的读取,所以速度较慢。而CPU需要使用数据时通过read()、read(byte[])读取数据时就要受到硬件IO的慢速度限制。我们又知道,CPU与内存发生的读写速度比硬件IO快一个数量级,所以优化读写的思路就有了:在内存中建立缓存区,先把存储介质中的字节读取到缓存区中。CPU需要数据时直接从缓冲区读就行了,缓冲区要足够大,在被读完后又触发fill()函数自动从存储介质的文件字节内容中读取字节存储到缓冲区数组。
BufferedInputStream 内部有一个缓冲区,默认大小为8KB**,每次调用read方法的时候,它首先尝试从缓冲区里读取数据,若读取失败(缓冲区无可读数据),则选择从物理数据源 (譬如文件)读取新数据(这里会尝试尽可能读取多的字节)放入到缓冲区中,最后再将缓冲区中的内容返回给用户.由于从缓冲区里读取数据远比直接从存储介质读取速度快,所以BufferedInputStream的效率很高。**

7:缓冲字符流

	public static void main(String[] args) throws IOException {
     File f1 = new File("A:\\TEXT\\16.txt");
     File f2 = new File("A:\\TEXT\\15.txt");

     FileReader fileReader = new FileReader(f1);
     FileWriter fileWriter = new FileWriter(f2);
     BufferedReader bufferedReader = new BufferedReader(fileReader);
     BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);

     /*
     // 方式一:缓冲数组
     char[] c = new char[1024];
     int len = bufferedReader.read(c);
     while (len != -1) {
         bufferedWriter.write(c, 0, len);
         len = bufferedReader.read(c);
     }
      */

     /*
     // 方式二:一个一个的读写
     int n = bufferedReader.read();
     while(n != -1) {
         bufferedWriter.write(n);
         n = bufferedReader.read();
     }
      */
     // 方式三:
     String str = bufferedReader.readLine();
     while(str != null) {
         bufferedWriter.write(str);
         // 另起一行,注意系统是不会给我们自动换行的,我们需要自己换行。
         bufferedWriter.newLine();
         str = bufferedReader.readLine();
     }
     bufferedWriter.close();
     bufferedReader.close();
 }

注意:read与readline方法的区别

read:读取单个字符/字节,是一个通用方法。 返回:作为一个整数(其范围从 0 到 65535 (0x00-0xffff))读入的字符,如果已到达流末尾,则返回 -1。

readLine:读取一个文本行,主要是一个用于读取文本的方法。通过下列字符之一即可认为某行已终止:换行 (‘\n’)、回车 (‘\r’) 或回车后直接跟着换行。 返回:包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null

8:转换流:inputstreamreader , outputstreamwriter

【1】转换流:作用:将字节流和字符流进行转换。

【2】转换流 属于 字节流还是字符流**?属于字符流**

InputStreamReader :字节输入流 —》字符的输入流

OutputStreamWriter : 字符输出流 --》字节的输出流

在这里插入图片描述

	public static void main(String[] args) throws IOException {
     File f1 = new File("A:\\TEXT\\16.txt");
     File f2 = new File("A:\\TEXT\\15.txt");

     FileInputStream fileInputStream = new FileInputStream(f1); //字节输入流
     // 转化为UTF-8类型的字符
     InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream , "utf-8"); // 字符转换流

     FileOutputStream fileOutputStream = new FileOutputStream(f2); // 字节输出流
     // 输出为UTF-8类型的字符
     OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream , "utf-8"); // 字符转换流

     char[] c = new char[1024 * 10];
     int len = inputStreamReader.read(c);
     while (len != -1) {
         outputStreamWriter.write(c , 0 , len);
         len = inputStreamReader.read(c);
     }

     outputStreamWriter.close();
     inputStreamReader.close();
 }

输入转换流:将源文件中的二进制内容转化成对应编码字符集的内容。

输出转换流:按照目标编码字符集,将字符转化成二进制内容。

假如源文件是UTF-8编码,目标文件是GBK编码。

(1):源文件按照字节的形式输入,然后按照UTF-8字符集的格式将二进制内容解析成对应的字符。

(2):将解析出来的字符按照GBK字符集的格式转化成对应的二进制形式进行输出。

9:System.in与System.out

System.in : “标准”输入流。—》默认情况下 从键盘输入

System.out :“标准”输出流。 —》默认情况下,输出到控制台。

package P1.package4;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;

public class Test7 {
 public static void main(String[] args) throws IOException {
     // 标准的输入流
     InputStream in = System.in; // 注意这是一个字节流
     // read方法等待键盘的录入,这是一个阻塞方法。
     // system.in这个管怼到键盘上,所以你从键盘上录入的化,就会从这个管到程序中。
     int n = in.read(); // a
     System.out.println(n); // 97
     // Scanner就是一个扫描器,扫描从这个管出来的数据。
     // Scanner scanner = new Scanner(System.in);
     File file = new File("A:\\TEXT\\16.txt");
     // 既然Scanner是扫描的作用,不一定非得扫 System.in进来的东西,还可以扫描其他管的内容:
     Scanner sc = new Scanner(new FileInputStream(file));// 自己输入流
     while(sc.hasNext()) {
         System.out.println(sc.nextLine()); // 读取下一个单词并返回这一单词。
     }
 }
}
/*
a
97
肝死你们,Fuck you !
111111111111111111111111。1111111111111111111111111111111111111。
0000000000。


111111111,11111111111,
000000。
*/
package P1.package4;

import java.io.PrintStream;

public class Test8 {
 public static void main(String[] args) {
     // 直接在控制台写出
     // printstream:打印流,一个管子插在控制台上
     PrintStream o = System.out;
     o.print("1");
     o.print("2");
     o.print("3");
     o.print("4");
     o.println("1");
     o.println("1");
     o.println("1");

     // 就相当于
     System.out.println("1");
     System.out.print("1");
 }
}

10:练习:键盘录入内容输出到文件中

在这里插入图片描述

	public static void main(String[] args) throws IOException {
     // 1: 先准备输入方向
     // 键盘录入
     InputStream in = System.in; // 属于字节流
     // 字节流 --> 字符流
     InputStreamReader inputStreamReader = new InputStreamReader(in); // 转换流
     // 字符流 --> 字符缓冲流
     BufferedReader bufferedReader = new BufferedReader(inputStreamReader); // 字符缓冲流

     // 2:再准备输出方向
     File file = new File("A:\\TEXT\\15.txt");
     FileWriter fileWriter = new FileWriter(file); // 字符写入流
     BufferedWriter bufferedWriter = new BufferedWriter(fileWriter); // 字符缓冲流

     // 3:准备操作

     String s = bufferedReader.readLine();
     while(!s.equals("exist")) {
         bufferedWriter.write(s);
         bufferedWriter.newLine();
         s = bufferedReader.readLine();
     }

     // 4:关闭流
     bufferedWriter.close();
     bufferedReader.close();
 }

课上老师使用的是:键盘输入 / 字节流 --> 字符流 --> 字符缓冲流 --> 程序 --> 字符缓冲流 --> 字符流 --> 目标文件的写法。

我想试一下:键盘输入 / 字节流 --》字节缓冲流 --> 程序 --> 字节缓冲流 --> 字节流 --》目标文件的写法。

package P1.package4;

import java.io.*;

// 键盘录入内容输出到文件中
public class Test9 {

 public static void main(String[] args) throws IOException, InterruptedException {
     // 1: 先准备输入方向
     // 键盘录入
     InputStream in = System.in; // 属于字节流
     //File file1 = new File("A:\\TEXT\\188.txt");
     //FileInputStream fileInputStream = new FileInputStream(file1);
     // 字节流 --> 字节缓冲流
     BufferedInputStream bufferedInputStream = new BufferedInputStream(in);

     // 再准备输出方向
     File file = new File("A:\\TEXT\\122.txt");
     System.out.println(file.exists());
     //字节缓冲流
     BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file));

     // 进行操作
     byte[] bytes = new byte[1024];
     int len = bufferedInputStream.read(bytes); // 阻塞
     // System.out.println(len);
     int count = 0;
     while (len != -1) {
         bufferedOutputStream.write(bytes, 0, len); // 新线程阻塞
         // System.out.println(new String(bytes, 0, len));
         len = bufferedInputStream.read(bytes);
         System.out.println(len);
         count++;
         if (count == 3) {
             break;
         }
     }
     // 关闭流
     bufferedOutputStream.close();
     bufferedInputStream.close();
 }
}

11:对象流 ObjectInputStream 与 ObjectOutputStream

【1】:对象流

用于存储和读取基本数据类型数据或对象的处理流。

它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。

【2】序列化和反序列化:

ObjectOutputStream 类 : 把内存中的Java对象转换成平台无关的二进制数据,从而允许把这种二进制数据持久地保存在磁盘上,或通过网络将这种二进制数据传输到另一个网络节点。----》序列化

用ObjectInputStream类 : 当其它程序获取了这种二进制数据,就可以恢复成原来的Java对象。----》反序列化

【3】代码:操作字符串对象:

进行序列化操作。

package P1.package4;

import java.io.*;

public class Test14 {
 public static void main(String[] args) throws IOException {
     ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File("A:\\TEXT\\122.txt")));
     objectOutputStream.writeObject("肝死你们,草泥马");
     objectOutputStream.close();
 }
}

在这里插入图片描述

这玩意不是给人看的,是给程序看的。

进行反序列化操作。

package P1.package4;

import java.io.*;

public class Test14 {
 public static void main(String[] args) throws IOException, ClassNotFoundException {
     /*
     ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File("A:\\TEXT\\122.txt")));
     objectOutputStream.writeObject("肝死你们,草泥马");
     objectOutputStream.close();
      */
     ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(new File("A:\\TEXT\\122.txt")));
     System.out.println(objectInputStream.readObject());
     objectInputStream.close();
 }
}
// 肝死你们,草泥马

【4】代码:操作自定义类的对象:

package P1.package4;

import java.io.*;

public class Test14 {
 public static void main(String[] args) throws IOException, ClassNotFoundException {
     // 对象流
     ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File("A:\\TEXT\\122.txt")));
     Person person = new Person("asfsdf" , 13 , 188.3);
     objectOutputStream.writeObject(person);
     objectOutputStream.close();

     /*
     ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(new File("A:\\TEXT\\122.txt")));
     System.out.println(objectInputStream.readObject());
     objectInputStream.close();
      */
 }
}

class Person {
 // 属性
 private String name;
 private int age;
 private double height;

 // 构造器
 public Person(String name, int age, double height) {
     this.name = name;
     this.age = age;
     this.height = height;
 }

 // 方法
 @Override
 public String toString() {
     return "Student{" +
             "name='" + name + '\'' +
             ", age=" + age +
             ", height=" + height +
             '}';
 }
}

在这里插入图片描述

会报错误,显示的是没有序列化异常。

解决方法:你要序列化的那个对象所对应的类应该实现一个Serializable接口

在这里插入图片描述

接口内部,什么都没有,这种接口叫 标识接口。 起到标识作用,标识什么呢?只要实现这个接口的类的对象才能序列化,否则不可以。

【5】serialVersionUID:

假设刚开始的时候Person类里面我们没有toString方法,我们将person对象通过对象流写入到文件中,然后再通过反序列化读入到内存中,此时对Person对象进行修改重新加上toString方法,并打印该对象,会发现出现异常。

写入的person类

class Person implements Serializable{
 // 属性
 private String name;
 private int age;
 private double height;

 // 构造器
 public Person(String name, int age, double height) {
     this.name = name;
     this.age = age;
     this.height = height;
 }


}

重写反序列化

package P1.package4;

import java.io.*;

public class Test14 {
 public static void main(String[] args) throws IOException, ClassNotFoundException {


     /*
     ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File("A:\\TEXT\\122.txt")));
     Person person = new Person("asfsdf" , 13 , 188.3);
     objectOutputStream.writeObject(person);
     objectOutputStream.close();
      */




     ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(new File("A:\\TEXT\\122.txt")));
     System.out.println(objectInputStream.readObject().toString());
     objectInputStream.close();
 }
}
// 反序列化后修改后的person类
class Person implements Serializable{
 // 属性
 private String name;
 private int age;
 private double height;

 // 构造器
 public Person(String name, int age, double height) {
     this.name = name;
     this.age = age;
     this.height = height;
 }

 // 方法

 @Override
 public String toString() {
     return "Student{" +
             "name='" + name + '\'' +
             ", age=" + age +
             ", height=" + height +
             '}';
 }

}

在这里插入图片描述

会出现InvalidClassException异常。

出现异常的原因:

在这里插入图片描述

解决:给这个类 加入一个 序列号:serialVersionUID

在这里插入图片描述

凡是实现Serializable接口(标识接口)的类都有一个表示序列化版本标识符的静态常量:

➢private static final long serialVersionUID;

➢serialVersionUID用来表明类的不同版本间的兼容性简言之,其目的是以序列化对象进行版本控制,有关各版本反序列化时是否兼容。

➢如果类没有显示定义这个静态变量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。故建议,显式声明。

➢简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)

【6】序列化细节:

(1)被序列化的类的内部的所有属性,必须是可序列化的基本数据类型都是可序列化的

在这里插入图片描述

(2)static,transient修饰的属性 不可以被序列化。

package P1.package4;

import java.io.*;

public class Test14 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        /*
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File("A:\\TEXT\\122.txt")));
        Person person = new Person("asfsdf" , 13 , 188.3);
        System.out.println(person.toString()); // Student{name='asfsdf', age=13, height=188.3}
        objectOutputStream.writeObject(person);
        objectOutputStream.close();
         */
        // 因为static与transient修饰的属性不可以被序列化,所以这两个属性是写不进去的。

        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(new File("A:\\TEXT\\122.txt")));
        System.out.println(objectInputStream.readObject().toString());
        // Student{name='null', age=0, height=188.3}
        objectInputStream.close();

    }
}

class Person implements Serializable{
    private static final long serialVersionUID = -7774721154046748661L;
    // 属性
    private static String name;
    private transient int age;
    private double height;

    // 构造器
    public Person(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }

    // 方法
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", height=" + height +
                '}';
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/25910.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Android-JetpackCompose】13、实战在线课程 App

文章目录 一、BottomNavigation 底部导航1.1 底部导航栏的布局、点击1.2 设置 bottomBar 的颜色1.3 设置顶部 actionBar 的颜色 二、主页 StudyScreen2.1 顶部状态栏2.2 一、BottomNavigation 底部导航 1.1 底部导航栏的布局、点击 首先&#xff0c;构造 NavigationItem 的 d…

c++—断言、异常

一、 断言&#xff0c;主要用于在函数入口处进行参数检查&#xff0c;是否符合参数设置要求&#xff1b; &#xff08;1&#xff09;true&#xff1a;继续执行&#xff1b;false&#xff1a;终止运行&#xff1b; &#xff08;2&#xff09;特点&#xff1a;在程序运行时才能起…

【20】SCI易中期刊推荐——计算机信息系统工程电子与电气(中科院3区)

💖💖>>>加勒比海带,QQ2479200884<<<💖💖 🍀🍀>>>【YOLO魔法搭配&论文投稿咨询】<<<🍀🍀 ✨✨>>>学习交流 | 温澜潮生 | 合作共赢 | 共同进步<<<✨✨ 📚📚>>>人工智能 | 计算机视觉…

zookeeper学习笔记

zookeeper Zookeeper 入门概述Zookeeper工作机制特点数据结构应用场景统一命名服务统一配置管理统一集群管理服务器动态上下线软负载均衡 zookeeper安装本地模式安装配置参数解读 Zookeeper 集群操作集群操作集群安装 选举机制节点类型客户端命令行操作命令语法znode 节点数据信…

mysql 是否包含 返回索引 截取字符串

是否包含返回索引 原文链接&#xff1a;https://www.cnblogs.com/shoshana-kong/p/16474175.html 方法1&#xff1a;使用通配符%。 通配符也就是模糊匹配&#xff0c;可以分为前导模糊查询、后导模糊查询和全导匹配查询&#xff0c;适用于查询某个字符串中是否包含另一个模糊…

Redis的全局命令及相关误区

Redis中所说的数据结构是针对key-value中的value而言的。主要的结构包括String、哈希表、列表、集合等等在redis中存在16个库&#xff0c;涉及到后期的集群搭建只能使用0号库最为方便 查看所有键&#xff08;支持通配符&#xff09; keys * keys S*返回当前数据库中的键总数 …

智能出行更安全,亚马逊云科技携手木卫四助汽车客户安全合规出海

木卫四&#xff08;北京&#xff09;科技有限公司在汽车网络安全领域拥有独特专业知识&#xff0c;其融合人工智能算法的安全检测引擎可以不依赖车辆中安装的代理软件&#xff0c;只需几周即可快速部署实施&#xff0c;是汽车网络安全领域的技术领先者。 在亚马逊云科技初创团…

ChatGPT国内镜像站

免费国内镜像推荐&#xff08;超稳定&#xff09; 下面为大家收集了目前国内最稳定流畅的ChatGPT镜像网站 目录 机器人 博弈ai 泰cool辣 道合顺 二狗问答 核桃 WOChat GPT中文站 TomChat 利用ChatGPTMindShow三分钟生成PPT ChatGPT国内镜像是啥 ChatGPT 镜像是指…

大语言模型技术原理

在今天这个时代&#xff0c;人们的工作和生活已经离不开数据访问&#xff0c;而几乎所有平台背后的数据存储和查询都离不开数据库。SQL作为一种数据库的查询和处理语言历史悠久&#xff0c;最早由IBM于上世纪70年代初研究关系数据模型时提出&#xff0c;后续发展为一种广泛使用…

帕累托改进和帕累托最优、卡尔多-希克斯改进

根据目标个数&#xff0c;分为单目标规划&#xff0c;以及多目标规划。多目标的规划是去找折中的解集合&#xff0c;既pareto最优解集合。对优化目标超过3个以上的&#xff0c;称之为超多目标优化问题。 帕累托改进描述的就是在没有人变得不好的前提下让有些人更好的过程。帕累…

C#简单数据结构类和常用泛型结构类

文章目录 1.简单数据结构类&#xff08;1&#xff09;动态数组Arraylist&#xff08;2&#xff09;栈Stack&#xff08;3&#xff09;队列Queue&#xff08;4&#xff09;哈希表Hashtable 2.泛型3.常用泛型数据结构类&#xff08;1&#xff09;列表List&#xff08;2&#xff0…

Linux之基础IO

文章目录 前言一、再谈文件二、再谈文件操作二、如何理解文件1.文件操作的本质2.管理被打开的文件 三、进程和被打开的文件如何关联四、文件描述符fd1.引入2.理解3.分配规则 五、重定向1.引入重定向2.接口3.追加重定向4.输入重定向 总结 前言 本文介绍了系统IO、fd、重定向等内…

【Linux】在Ubuntu中卸载、下载mysql以及如何检查mysql是否卸载成功

介绍 这里是小编成长之路的历程&#xff0c;也是小编的学习之路。希望和各位大佬们一起成长&#xff01; 以下为小编最喜欢的两句话&#xff1a; 要有最朴素的生活和最遥远的梦想&#xff0c;即使明天天寒地冻&#xff0c;山高水远&#xff0c;路远马亡。 一个人为什么要努力&a…

micropython固件编译——把自己的py库添加进固件

目录 0. 前言1. 编写自己库的代码2. 移植库3. 验证 0. 前言 本节编译自己写的py库&#xff0c;增强移植性&#xff0c;往后烧录自己的固件即可轻易移植代码 没装好环境或者没有基础可以先看看这个&#xff1a; Ubuntu下ESP-IDF的环境搭建 Ubuntu下编译esp32micropython固件编…

windows下上架iOS应用到appstore

windows下上架iOS应用到appstore 背景步骤申请苹果开发者账号创建唯一标示符App IDs申请发布证书申请发布描述文件创建App并填写信息选择证书编译打包上传IPA到App Store提交审核 尾巴 背景 现在由于跨平台技术的兴起&#xff0c;不使用原生技术就能开发出Android和iOS应用。A…

一些关于c++的琐碎知识点

目录 bool强转 const构成重载:const修饰*p 移动构造 new int (10)所做的四件事 this指针---为什么函数里面需要this指针&#xff1f; .和->的区别 new创建对象 仿函数 new和malloc的区别 c系统自动给出的函数有 delete和delete[ ]区别何在 检查有没有析构函数 e…

BTC API:如何在比特币网络上创建应用程序?

比特币是一种去中心化的数字货币&#xff0c;可以通过比特币API与比特币网络进行交互。比特币API是一组允许开发人员与比特币网络进行交互的编程接口&#xff0c;可以帮助开发者构建各种比特币应用程序。 比特币API可以用于创建区块浏览器、钱包和比特币支付。其中利用比特币A…

Android-Activity生命周期

文章参考&#xff1a;添加链接描述 文章参考&#xff1a;添加链接描述 五大状态 StartingRunningStoppedPausedDestroyed 借用一张已经包浆的图 PS&#xff1a;Running和Paused是可视阶段&#xff0c;其余都是不可视 几大函数 onCreate&#xff1a;通过setContentLayout初始…

Markdown笔记应用程序Note Mark

什么是 Note Mark Note Mark 是一种轻量、快速、简约&#xff0c;基于网络的 Markdown 笔记应用程序。具有时尚且响应迅速的网络用户界面。 安装 在群晖上以 Docker 方式安装。 ghcr.io 镜像下载 官方的镜像没有发布在 docker hub&#xff0c;而是在 ghcr.io&#xff0c;所以…

iptables和防火墙

文章目录 1.防火墙2.Iptables基本介绍2.1 什么是iptables2.2 什么是包过滤防火墙2.3 包过滤防火墙如何实现 1.防火墙 Linux防火墙主要工作在网络层&#xff0c;针对 TCP/IP 数据包实施过滤和限制&#xff0c;典型的包过滤防火墙&#xff0c;基于内核编码实现&#xff0c;具有非…