4:File类与IO流

news2024/12/28 11:42:37

文章目录

  • 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 +
                '}';
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

易趋受邀出席CIAS 2023中国数智汽车峰会

2023年5月18日-19日&#xff0c;由中国电子商会、中国汽车信息化推进产业联盟、信息侠联合主办&#xff0c;上海市交通工程协会、 浙江省数字经济联合会联合支持的CIAS 2023第二届中国数智汽车峰会在杭州千岛湖顺利召开。易趋受邀出席此次会议&#xff0c;并发表了主题演讲。 &…

软件架构: 一切皆有代价

软件架构必须随着业务发展而演进&#xff0c;否则就会成为业务的阻碍。但架构本身在发展过程中很容易逐渐腐化&#xff0c;堆积大量技术债务&#xff0c;因此在软件发展过程中始终保持架构愿景非常重要。原文: Software architecture — Paying the Price for Neglecting it 在…

数组的玩法比我以为的要多

数组是最基本的数据结构&#xff0c;关于数组的面试题也屡见不鲜&#xff0c;本文罗列了一些常见的面试题&#xff0c;仅供参考。目前有以下18道题目。 数组求和 求数组的最大值和最小值 求数组的最大值和次大值 求数组中出现次数超过一半的元素 求数组中元素的最短距离 求…

2023.05.28 homework

求三角形最长的边的长度 1&#xff09;任意两边之和大于第三边&#xff1b; 2&#xff09;任意两边之差小于第三边&#xff1b; 已知最短边13cm&#xff0c;其他两边肯定都等于大于13cm&#xff0c;两边只差肯定要小于13cm&#xff1b;简单推导就是枚举呗。 13cm 14cm 33cm&a…

web --- javascript(01)-- 介绍、变量和数据类型

JavaScript w3c&#xff1a;三层分离 结构层&#xff1a;HTML 表示层&#xff1a;CSS 行为层&#xff1a;JavaScript 介绍 &#xff08;1&#xff09;作用&#xff1a; 数据校验网页特效数据交互服务器端编程&#xff08;NodeJS&#xff09; &#xff08;2&#xff09;javas…

【PS小贴士】PS项目交货(CNS0/VL02N)-和销售订单交货(VL01N/VL02N)的区别点

目前一家公司在进行企业信息化建设&#xff0c;作为PS顾问有幸参与到其中。真是做的项目越来越多&#xff0c;经历的人越来越多&#xff0c;每个项目的感触也不同。不论是方案&#xff0c;还是项目上一起努力的小组成员、以及项目组顾问。哈哈哈哈哈哈哈哈...... 同样&#xf…

Linux 系统上的库文件生成与使用

1.库文件 库是一组预先编译好的方法的集合。Linux系统存储的库的位置一般在&#xff1a;/lib 和 /usr/lib。 在 64 位的系统上有些库也可能被存储在/usr/lib64 下。库的头文件一般会被存储在 /usr/include 下或其子目录下。 库有两种&#xff0c;一种是静态库&#xff0c;其…

分享几个国内免费可用的ChatGPT镜像【无需梯子】

文章目录 1.什么是ChatGPT2.ChatGPT的基础技术3.ChatGPT工作原理4.ChatGPT应用场景5.ChatGPT局限性6.ChatGPT的未来发展7.国内免费ChatGPT镜像写在最后 ChatGPT国内能用吗&#xff1a;ChatGPT在国内是无法使用的。你肯定要问我怎样才能体验到ChatGPT的神奇魔力呢&#xff1f;文…

【Spring】— 映射文件

映射文件 映射文件是MyBatis框架中十分重要的文件。在映射文件中&#xff0c;<mapper>元素是映射文件的根元素&#xff0c;其他元素都是它的子元素。映射文件中的主要元素如下所示。 <mapper><!--映射查询语句&#xff0c;可自定义参数&#xff0c;返回结果集…

Python日期带时区转换工具类总结

文章目录 1.背景2. 遇到的坑3. 一些小案例3.1 当前日期、日期时间、UTC日期时间3.2 昨天、昨天UTC日期、昨天现在这个时间点的时间戳3.3 日期转时间戳3.4 时间戳转日期3.5 日期加减、小时的加减 4. 总结5. 完整的编码 1.背景 最近项目是国际项目&#xff0c;所以需要经常需要用…

docker 解析DNS失败

问题现象&#xff1a; 我之前在docker里部署的容器&#xff0c;今天突然访问不了了&#xff0c;一开始我以为是容器的问题&#xff0c;将容器restart&#xff0c;销毁重建&#xff0c;都没有解决。 最后准备重启docker service&#xff0c;在重启之前&#xff0c;看了一眼dock…

接口自动化测试,从常规到动态再到全局断言,Postman让你居家必备!

目录 引言&#xff1a; 常规断言 动态参数断言 全局断言 总结 引言&#xff1a; Postman是一款非常流行的API接口测试工具&#xff0c;它以其易用性和强大的功能得到了广泛的应用。其中Postman断言功能是我们进行接口自动化测试时非常关键的一个环节。在接口测试中&#…

【正则入门】

语法 . &#xff08;点&#xff09; 匹配除换行符&#xff08;\n、\r&#xff09;之外的任何单个字符&#xff0c;相等于 [^\n\r]。 [\s\S] 匹配所有。\s 是匹配所有空白符&#xff0c;包括换行&#xff0c;\S 非空白符&#xff0c;不包括换行。 \d [0-9] \D 非数字&#xff1…

CISCN2023-Crypto

CISCN2023-Crypto 基于国密SM2算法的密钥分发可信度量Sign_in_passwdBB84badkey1 基于国密SM2算法的密钥分发 非预期&#xff0c;直接search能找到明文 curl -d "nameha&schoolhznu&phone110" http://123.56.244.196:30817/api/logincurl -d "id984ae…

【线程池】实现多线程并发定时任务

文章目录 一、为什么需要配置多线程定时任务二、单线程定时任务三、多线程定时任务 一、为什么需要配置多线程定时任务 springboot中通过注解 Scheduled 注解的方法都是一个定时执行的任务, 默认都是单线程的,就算是多个定时任务也是在同一个单线程(scheduled-1)中运行, 如果其…

离线版Gerber查看器+PCB/PCBA检测神器新功能!

一、CAM离线版——华秋CAM│专业Gerber查看器 公司电脑不能连接网络&#xff1f;不慌&#xff0c;我们完全离线且无需账号登录&#xff01; 磁盘空间不够拒绝下载&#xff1f;别怕 &#xff0c;我们安装包仅20M可随便分享&#xff01; 不会使用CAM350导入Gerber&#xff1f;没事…

APP开发死亡潮来临 小程序是否会取而代之?

移动互联网的发展&#xff0c; APP开发行业也迎来了它的大时代。据有关数据显示&#xff0c;2017年上半年国内新增的 App数量达到了创纪录的449万款&#xff0c;用户使用时长超过了200亿分钟。移动互联网已成为名副其实的“流量”产业&#xff0c;也因此诞生出一大批 APP开发公…

PostgreSQL 基础(一)-- 新手教程

PostgreSQL 12 官网 github 世界上最先进的开源关系数据库 1、新手入门 1.1 安装 当然&#xff0c;在使用PostgreSQL之前&#xff0c;你需要安装它。PostgreSQL可能已经安装在您的站点上&#xff0c;或者因为它包含在您的操作系统发行版中&#xff0c;或者因为系统管理员已…

【了不起的芯片 - 读书笔记】CPU 的制作流程 ( 晶圆制作 | 光刻机光刻流程 | 蚀刻过程 | 涂层过程 | 重复上述步骤若干次 | 芯片封装 )

文章目录 一、晶圆制作二、光刻机光刻流程三、蚀刻过程四、涂层过程五、重复上述步骤若干次六、芯片封装 一、晶圆制作 晶圆制作是半导体芯片制造的关键过程&#xff0c;它涉及将硅晶片&#xff08;或其他半导体材料&#xff09;转化为可以用于集成电路制造的基础材料。下面是晶…

ERP的需求分析

目录 什么是需求分析? 开发背景 01、系统模块 02、基础资料 供应商信息</