当前位置: 首页 > news >正文

IO、NIO解读和不同点,以及常用的文件流操作方法

java高级——IO、NIO解读和不同点,以及常用的文件流操作方法

  • 前情提要
  • 文章介绍
  • 1. 什么是IO
    • 1.1 节点的分类
    • 1.2 传输方式
  • 2. 七大传输方式解读
    • 2.1 File类解读
      • 2.1.1 创建文件的三种方式
      • 2.2.2 File的常用方法
      • 2.2.3 如何正确认识FileUtils
    • 2.2 字节流(核心)
      • 2.1.1 FileOutputStream
      • 2.1.2 FileInputStream
    • 2.3 字符流
      • 2.2.1 FileReader
      • 2.2.2 FileWriter
    • 2.4 缓冲流
      • 2.4.1 字节缓冲流(重点)
      • 2.4.2 字符缓冲流
    • 2.5 转换流(不常用,但是得知道原理)
    • 2.6 序列化流(了解)
    • 2.7 打印流(了解)
  • 号外
    • RandomAccessFile
  • 总结

前情提要

   上一篇文章我们探索了Exception类,一个我们经常用却很容易忽略的知识点,看完发现自己又专业了一点,主要还是针对异常的分类和如何有效的规避异常进行了讲解,还是建议大家看一下。

java高级——Exception异常类基本解读

文章介绍

   这一篇文章主要讲一下java中的文件流,也就是我们常说的IO流,虽然在开发中对普通的Excel、PDF、图片等文件经常读写,但毕竟只是一些基本的文件,而且相对都是一些导入导出操作。近期公司开发了在线学习的模块,里面涉及了大部分的文件读写,而且都是比较大的文件,在保证数据完整的同时还要兼具效率,所以采用了阿里的ssm服务器进行传输,虽然没有参与开发,但是在过程意识到了这部分的不足,所以进行系统学习,方便后期遇到这部分需求能快速有效的开发。

   除了对基本知识的讲解外,同时也会糅杂当前最为流行的NIO,本文将不再对源码进行过多的分析,主要还是针对实际开发和实用的知识点

本文参考自Java IO——二哥的Java进阶之路,在此基础上进行部分扩展。

1. 什么是IO

   首先对IO进行拆分,I表示Input(输入),O表示Output(输出),那么研究IO我们要从哪几个方面入手呢?首先要知道输入和输出的起点终点,专业名词就是节点,之后就是传输方式了。而节点分为很多种,举个例子,你要跨越1000公里去旅游,很难说直接从家到目的地吧,中途肯定要从A——B,B——C,依此类推,才能到达最终目的地,这这每个地方我们都可以称作节点,不一定说只有起点和终点才能称之为节点。

1.1 节点的分类

  1. 文件
       文件是我们最常见的节点,开发中我们经常需要将数据导出到Excel或者PDF文件中进行存储,对应的类有FileInputStream 、FileOutputStream 、FileReader 、FileWriter 等。
  2. 字节数组
       ByteArrayInputStream ByteArrayOutputStream 大多数用于对字节进行流形式的存储和传输,一般用于操作图片、视频等。
  3. 字符串
       很难想到字符串也算节点,其实这相当于中转站,我们读取的数据大多数都是字符串,包括从文件中读取的数据,所以这也是一个节点,StringReader 和StringWriter 用流的形式操作字符串。
  4. 控制台
       这里的控制台不仅仅是代表我们开发中的控制台,键盘也算做控制台,都是数据的来源,Java中通常使用System.in和System.out对数据进行控制台的交换。
  5. 内存映射文件
       这个概念可能有些生僻,这是一种高效的文件读取方式,非常适合读取大型文件和需要频繁进行随机读写的文件,通过直接将文件映射到内存的方式,我们可以直接访问内存,效率很高,同时也可以多线程共享(包括加锁),因为普通的文件读写都是一个线程使用的,而且都需要请求全部数据(当然可以分片)。这是一个非常高效的手段,因为其按需读取的原因,也不用担心内存被占满,可谓香的一批。Java中对应的是FileChannel
    6. 网络套接字
       就是读取和写入数据,只不过对象是网络传输,也可以理解成网线,通常我们前后端发的post和get请求啊这就是从网络中读写数据,这个概念可能大一些,名词就是Socket
  6. 数据结构
       这个也比较抽象,一般我们可以看到Java中有些类是需要实现Serializable接口的,这样的接口一般都可以将从文件啊或者网络读取的数据存储到这个类中,也可以将这个类存储的数据回显出去,代表性的类就是Collection类下的子类和String类。
  7. 管道
       这是一种为线程和进程之间通信而出现的概念,Java和Linux中不太一样,Linux中不同的进程可以通过管道通信,而Java中进程之间是不可以相同通信的,Java中的进程就是一个jvm,这和你new一个线程是完全不同的,而Java中不同的线程可以通过管道通信

  8.    这可谓是Java中最流行的概念了,这是一个抽象的概念,就是指一连串的数据在一个通道内进行传输,而这个通道就可以称之为流。一般有以下几个特点:
  • 先进先出:类似于隧道。
  • 只能顺序存取,不能随机存取。
  • 一个流不能兼具输入和输出两个功能,也就是单车道。
  1. 对象序列化
       上面说过网络套接字发送请求吧,那这个请求是如何进行获取数据的呢?对象序列化就上场了,严格来说这数据数据结构,只不过更加直观一些,就是实现了Serializable的类,也叫对象序列化。

1.2 传输方式

   我们习惯将传输方式分为两大类,一类是字节流,一类是字符流。

字节流:

   字节流(byte)是所有数据的终极形态,在网络中或者内存、硬盘等都是用byte来表示的,一个字节通常有8位(bit),而bit就是0和1,这是计算机能识别的最小单位,那么我们常说的二进制或者十进制就是通过bit表示的,如下图。
在这里插入图片描述
   一般情况下数据传输用的都是字节流,因为所有数据类型都可以通过字节流传输,最常见的就是图片和视频,在Java中对应的就是InputStreamOutputStream两大类。

字符流:

   字符流就是我们所能识别的char,通常看到的字母、汉字等就是用字符流表示的,一个字母就是一个字符,一个汉字可能由于字符编码的方式不同而体现的个数不同,UTF-8编码下一个汉字是三个字符。一般我们传输文本类型数据的时候首选字符流,Java中对应ReaderWriter

   当然这是大的分类,如果细分的话可以分为8大类,本文就将围绕这8大类进行详细解读,先看图:

在这里插入图片描述

2. 七大传输方式解读

   声明一下,严格来说文件流可以归类到字节流和字符流中,所以我们先对Java中File类进行解读,方便我们进行代码编写,之后的七大传输方式都会掺杂文件流的相关内容。

2.1 File类解读

2.1.1 创建文件的三种方式

	public static void main(String[] args) {String pathName = "D:/test";// 直接通过完整的文件路径创建文件File file = new File(pathName + "/test.txt");// 通过文件夹的路径和创建文件的文件名创建文件,简称父子创建方式File file1 = new File(pathName, "test.txt");// 通过父文件和子文件的名称创建文件,也是父子创建方式的一种,只不过父参数是一个文件File parent = new File(pathName);File file2 = new File(parent, "test.txt");}

   这是Java中创建文件的三种方式,一般我们用的都是直接通过路径创建的,有懂得同学可能会有疑问,说这里可以直接更改文件的,为啥不算IO呢?请看下面的例子:

		File test1 = new File("D:/test/test1.txt");File test3 = new File("D:/test/test3.txt");		// 无法创建test3.txtSystem.out.println("test3.exists() = " + test3.exists());	// falseSystem.out.println("test1.exists() = " + test1.exists());	// truetest1.renameTo(test3);	// 可以重命名test1.delete();		// 可以删除文件

   上面的例子能进行的操作是删除重命名文件,但是无法创建文件,更无法修改文件的内容(如果不涉及IO操作的话),严格来说上面的操作只是指令没有进行数据的传输,也就不涉及IO的操作,想想,我给你一个指令,你自己去执行一些操作,这不算IO操作哦。

   还有一点就是文件路径拼接的时候,很少我们会直接向电脑硬盘直接操作文件,一般的网站都是从浏览器读取一个文件解析上传到Linux服务器,但是我们要知道,Window和Linux的文件路径拼接符号不一样,Window是\Linux是/,Java中可以通过File.separator获取,当然正常我们在进行文件操作的时候,都直接用/,也可以读取到window系统的文件。

2.2.2 File的常用方法

常用的获取文件信息的方法

		// window下使用相对路径创建文件,文件一般在你的idea工作目录下File file = new File("/test111/t1/hello.txt");  // 文件不存在// 推荐使用绝对路径创建文件,当然开发中文件都是在Linux服务器,不存在分盘File file1 = new File("D:/test/test2.txt");// 获取文件的绝对和相对路径System.out.println("file.getAbsolutePath() = " + file.getAbsolutePath());System.out.println("file.getPath() = " + file.getPath());// 获取文件或者文件夹的名称(文件名)System.out.println("file.getName() = " + file.getName());// 获取文件的大小System.out.println("file1.Length = " + file1.length());System.out.println("file.Length = " + file.length());// 判断文件是否存在System.out.println("file.exists() = " + file.exists());System.out.println("file1.exists() = " + file1.exists());// 判断是文件还是文件夹System.out.println("file1.isFile() = " + file1.isFile());System.out.println("file1.isDirectory() = " + file1.isDirectory());

在这里插入图片描述

常用的操作文件的几种方法

		File file = new File("D:/test/test2.txt");// 文件重命名file.renameTo(new File("D:/test/test3.txt"));// 创建文件,但只能创建一级,如果有多个层级则无法创建file.mkdir();// 如果文件不存在,则使用这个方法,确保文件正确创建,没有层级限制file.mkdirs();// 删除文件,会有删除成功和失败的返回值boolean delete = file.delete();// 返回目录中所有的子文件或目录路径(file必须是一个文件夹)String[] list = file.list();// 返回目录中所有的子文件或目录(file必须是一个文件夹)File[] listFiles = file.listFiles();

2.2.3 如何正确认识FileUtils

   在了解了文件的基本使用的,后续我们对文件的大部分操作都在创建文件删除文件复制文件移动文件下载文件中,下载文件都是从服务器传输到浏览器中,大多数伙伴对于下载文件还是搞不太清楚,同样对应的还有上传文件,如何将文件快速的读取并传输的服务器当中(提一下:对于普通的office文件和图片文件一般存储在公司的服务器中,但是大型文件都会选择专业的文件服务器,类似阿里的文件服务器)

   后续我们在开发当中都会使用工具类,常用的是Apache FileUtils 类Hutool FileUtil 类,一般公司会有架构师进行再次封装,实际的核心代码还是使用上面的工具包进行,封装的意义就是为了统一管理,常见的手段就是增加日志限流或者说切割,保证代码的流畅运行。

   这里对两个工具类不进行过多阐述,需要的伙伴可以打开官网自行查看或自己在idea中增加依赖查看代码,正确使用工具类能大大提高编码效率(二次封装的代码后续会放在例子中)。

需要注意的几点

  • 开发中操作文件时一定要记得判断文件是否存在,不然会报错。
  • 传递文件路径的时候,使用绝对路径,虽然Linux中不存在window的分盘。
  • 获取文件大小返回的单位是KB,如果返回给前台是需要根据需求进行单位转换的。
  • 开发中创建文件时尽量使用mkdirs方法
  • 删除文件最好根据返回值判断是否删除成功,因为可能会因为文件占用而导致删除失败。
  • 遍历文件夹时最好使用exist方法判断文件是否存在,并且使用isDirectory判断是一个目录。

2.2 字节流(核心)

   最开始说过,字节是计算机中最基础也是最重要的单位,同理,字节流也是所有IO流中最基本最重要的,万物皆可用字节流传输

   首先我们要知道,字节流中OutputStreamInputStream两个超类,下面有N多个实现类,包括缓冲流(Buffered Streams)、转换流(Filter Streams)等等,具有代表性的就是FileOutputStreamFileInputStream,这一小节主要围绕这两个进行研究。

2.1.1 FileOutputStream

public static void main(String[] args) {File file = new File("/test/test111.txt");FileOutputStream fileOutputStream = null;try {fileOutputStream = new FileOutputStream(file);String content = "这是一段测试内容";fileOutputStream.write(content.getBytes());} catch (Exception e) {e.printStackTrace();} finally {if (fileOutputStream != null) {try {fileOutputStream.close();} catch (Exception e) {e.printStackTrace();}}}}

在这里插入图片描述

   上面这段代码很简单,创建了一个txt文件,写入了一段测试内容并将其输出到了硬盘,创建输出流FileOutputStream我使用的构造方法是传入一个文件,当然还可以直接给一个文件名,但是这种基本不使用。

   上面使用的方法只有一个writeclose方法,记住,一定要再使用完流后将其关闭,不然会导致IO异常。而write方法则是我们常用的写数据方法,你可以理解为new 一个FileOutputStream是内存与硬盘之间构建了一个通道,但是你要传输数据,而传输数据的方法就是write

   write常用的有两个重载方法:

  • write(byte[] b):将 b.length 个字节从指定的字节数组写入此输出流。

  • write(byte[] b, int off, int len) :从指定的字节数组写入 len 字节到此输出流,相当于可以指定范围进行输出输出,一般用于给文档开头还是尾部写数据。

   开发中怎样才能高效的写数据呢?如果我这个文件有几个G,这么直接写入会不会出现阻塞或其它情况呢?那么正常情况下我们肯定是不能直接这么写的,要么使用循环写入,要么使用缓冲流,之后会说到。

   上面的输出有一个问题,如果需求是往一个文件中追加一部分内容,那上面的写法就失效了,因为你直接write写数据的话,会将原有的内容直接清空,如果要实现追加的内容,则需要在构造方法中加一个参数append

	public static void main(String[] args) {File file = new File("/test/test111.txt");FileOutputStream fileOutputStream = null;try {fileOutputStream = new FileOutputStream(file, true);// 注意:window的换行符是\r\n,Linux的换行符是\nString content = "\r\n这是一段追加内容";fileOutputStream.write(content.getBytes());} catch (Exception e) {e.printStackTrace();} finally {if (fileOutputStream != null) {try {fileOutputStream.close();} catch (Exception e) {e.printStackTrace();}}}}

   这里在提一下往浏览器中输出一个文件,因为我们现在是直接给硬盘或服务器输出文件的,这时候的outputStream是直接可以new的,本身的流指向就是硬盘或服务器,如果想改变指向,在web开发中前端发送的请求都是有一个HttpServletResponse参数的,我们可以直接从这个参数中拿到outputStream,并设置相关的响应类型,然后写数据,这时候自然就实现了向浏览器输出文件。总的来说,就是创建通道确认通道的起点和终点,给通道中输送数据

2.1.2 FileInputStream

   字节输入流代表性的类就是FileInputStream,功能就是从服务器或者硬盘读取数据,使用思路是和输出流一致的,只不过在使用方法上有一些区别。

	public static void main(String[] args) {File file = new File("/test/test111.txt");FileInputStream fileInputStream = null;try {fileInputStream = new FileInputStream(file);// read方法一次只读取一个字节,注意,是一个字节// 返回的是ASCII码,所以要转换为char字符类型// 如果返回的是-1,则代表没有数据了,已经到最后了while (fileInputStream.read() != -1) {System.out.println("fileInputStream.read() = " + (char)fileInputStream.read());}} catch (Exception e) {e.printStackTrace();} finally {if (fileInputStream != null) {try {fileInputStream.close();} catch (Exception e) {e.printStackTrace();}}}}

   上面是一个很简单的从文件中读取内容的例子,读取内容的方法变成了read,很直观,只不过需要注意的是,read方法一次性只会读取一个字符,而且返回的是ASCII码,如果需要查看,还需要转换为char类型。

   上面的实现稍微有一个缺陷,就是在while循环中不建议使用read的返回值进行判断,相当于我们对同一个字符进行了两次读取,没有必要,可以修改为fileInputStream.available() != 0判断,较为标准。

在这里插入图片描述

   看一下程序的输出结果,发现中文乱码了,原因我们在一开始就提到过,中文在UTF-8编码下是3个字节,因为read一次性读取的只有一个字节,所以转为char类型就是乱码。那就没有办法了吗,肯定有,稍微改造一下代码即可,请看。

	public static void main(String[] args) {File file = new File("/test/test111.txt");FileInputStream fileInputStream = null;try {fileInputStream = new FileInputStream(file);// 定义一个指定大小的byte数组,一般都是1MB,也就是1024KB(字节)byte[] bytes = new byte[1024];while (fileInputStream.available() != 0) {// 返回数据的长度int len = fileInputStream.read(bytes);System.out.println(new String(bytes, 0, len));}} catch (Exception e) {e.printStackTrace();} finally {if (fileInputStream != null) {try {fileInputStream.close();} catch (Exception e) {e.printStackTrace();}}}}

在这里插入图片描述

   能看到上面的输出和文件中内容是一致的,包含换行符,当然肯定也有指定范围读取的参数,除了利用while循环读取外,还有一个readAllBytes()方法,读取所有的字节,但一般不建议使用,除非你明确知道这个文件的内容比较小,比如这是一个配置的Properties文件

   正常情况下,读取这种文件使用的都是字符流,这里只是举例来说明字节输入流的用法,毕竟这是最基础的内容。

   简单提一下浏览器中的图片是怎么展示的,一般我们需要再浏览器中展示图片,给一个图片的路径即可,浏览器会自己加载,那加载的原理和过程是什么呢?其实就是浏览器发送了一个get请求,设置了请求类型是图片,而且一般都有Connection: Keep-Alive属性,这是HTTP协议中的一个缓存属性,你的请求数据是会被缓存的,下次加载页面就会快很多。发送请求后服务器返回的数据除了正常的响应头外,图片的数据就是使用字节流返回的二进制串,浏览器收到后进行解码,就变成了我们能看到的图片啦。

浏览器请求图片原理

2.3 字符流

2.2.1 FileReader

   字符流在上面已经简单讲过了,和字节流相比受限比较大,这是一种专为操作文本而设计的,其实操作和字节流大差不差,主要区别还是在flush方法上和读取的方式上,字符流每次读取的是一个字符,所以相对比较简单。

	 public static void main(String[] args) {File file = new File("/test/test111.txt");FileReader fileReader = null;try {fileReader = new FileReader(file);int ch = -1;while ((ch = fileReader.read()) != -1) {System.out.println((char) ch);}} catch (Exception e) {e.printStackTrace();} finally {if (fileReader != null) {try {fileReader.close();} catch (Exception e) {e.printStackTrace();}}}}

在这里插入图片描述

   上面的运行结果很清晰,将文件中的每个字符依次输出,包含换行符和空格等,那些都属于字符,但一般我们都不会这么读取的,因为read方法每次默认只读取一个字符,程序修改如下。

	public static void main(String[] args) {File file = new File("/test/test111.txt");FileReader fileReader = null;try {fileReader = new FileReader(file);char[] chars = new char[1024];int len = -1;while ((len = fileReader.read(chars)) != -1) {System.out.print(new String(chars, 0, len));}} catch (Exception e) {e.printStackTrace();} finally {if (fileReader != null) {try {fileReader.close();} catch (Exception e) {e.printStackTrace();}}}}

在这里插入图片描述

   相对于字节流差别不大,如果是操作文本文件(例如一些配置文件和日志文件)推荐使用字符流,当然字符流也同样拥有指定范围读取的功能,这里就不做演示了,重点还是放在FileWriter上。

2.2.2 FileWriter

  • write(int c): 写入单个字符。
  • write(char[] cbuf) :写入字符数组。
  • write(char[] cbuf, int off, int len) :写入字符数组的一部分,off为开始索引,len为字符个数。
  • write(String str) :写入字符串。
  • write(String str, int off, int len): 写入字符串的某一部分,off 指定要写入的子串在 str 中的起始位置,len 指定要写入的子串的长度。

   上面是五个常用的写入数据的方法,有直接通过char字符写入的,也有通过String字符串写入的,个人比较喜欢String,操作起来相对方便一些。同样的,FileWriter有覆盖追加两种方式写入,和字节流的append参数一样。

	public static void main(String[] args) {File file = new File("/test/test222.txt");FileWriter fileWriter = null;try {fileWriter = new FileWriter(file);fileWriter.write("字符流写入数据。\r\n 这是另一段测试内容");} catch (Exception e) {e.printStackTrace();} finally {if (fileWriter != null) {try {fileWriter.close();} catch (Exception e) {e.printStackTrace();}}}}

   这里我们就演示常用的一种写入数据的方式,其余方式都是一样的,上面的例子比较简单,也是很常用的一种,需要明白的是,不一定要创建一个存在的文件,如果文件不存在,FileWriter会自动创建,而且在最后一定要关闭流,不然会导致数据无法写入的情况。

	public static void main(String[] args) {// 此例子只是为了研究flush的作用,开发中不能这么使用File file = new File("/test/test333.txt");FileWriter fileWriter = null;try {fileWriter = new FileWriter(file);fileWriter.write("字符流写入数据。\r 测试不关闭流只刷新,数据是否正常写入");fileWriter.flush();} catch (Exception e) {e.printStackTrace();}}

   上面这个例子很有趣,我没有关闭流,但是文件正常创建了,数据也正常写入了,这是为什么?

   答案就在我们的flush方法上,因为字符流在设计上是有一个缓冲区的,你的数据优先会写入缓冲区,需要我们手动将数据刷出去。一般开发中如果数据比较大,在一定大小是需要调用flush方法将数据刷出去的,清空缓冲区。而close方法里面是会调用flush方法的,所以数据可以正常写入。

   为什么要知道这个,一方面是了解字符流的设计原理,同时开发中有时候我们不需要立即关闭这个流,可以先将数据刷出去,在执行一些逻辑后继续写入数据。要知道,频繁创建输入输出流是很耗费资源的,就和不断创建数据库连接一样,为啥不能再for循环中写sql,知道为啥了吧。

2.4 缓冲流

   缓冲流从字面意思理解稍微抽象一些,不过核心就5个字:空间换时间!!!

   上面5个字的意思就是缓冲流将每次读写的数据放在一块内存中,当达到预定的大小后再将数据写入到磁盘中,大大减少了磁盘和内存的交互次数,因为交互过程相对来说是很浪费时间的,这就是用空间换时间的概念,具体我们根据下面的例子来说明。

2.4.1 字节缓冲流(重点)

   万物皆可使用字节流,当然字节流我们着重说一下,围绕复制一个比较大的文件来举例。

    public static void main(String[] args) {// 记录开始时间long start = System.currentTimeMillis();// 创建流对象// 准备一个100mb以下的就可以,太大使用普通流复制需要时间比较长try (FileInputStream fis = new FileInputStream("D:/test/file1.txt");FileOutputStream fos = new FileOutputStream("D:/test/file1Copy.txt")){// 读写数据int b;while ((b = fis.read()) != -1) {fos.write(b);}} catch (Exception e) {e.printStackTrace();}// 记录结束时间long end = System.currentTimeMillis();System.out.println("普通流复制时间:"+(end - start)+" 毫秒");}

   上面使用了最基本的字节流进行文件复制,文件大小为48.7Mb,1Mb = 1048576 Byte(字节),那这个文件就是51,065,651.2字节,而此次交互次数为文件大小的2倍(从磁盘读写到内存在复制到磁盘),那交互次数最终为 102,131,302.4 字节,简单点那就算1亿,虽然计算机读写速度也很快,单页扛不住这么造,上面程序最终的运行时间如下:231229毫秒,接近4分钟(取决于电脑运行速度)

在这里插入图片描述

   接下来使用字节缓冲流进行复制,看一下效果。

    public static void main(String[] args) {// 记录开始时间long start = System.currentTimeMillis();// 创建流对象// 准备一个100mb以下的就可以,太大使用普通流复制需要时间比较长try (BufferedInputStream fis = new BufferedInputStream(new FileInputStream("D:/test/file1.txt"));BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream("D:/test/file1Copy.txt"))){// 读写数据int b;while ((b = fis.read()) != -1) {fos.write(b);}} catch (Exception e) {e.printStackTrace();}// 记录结束时间long end = System.currentTimeMillis();System.out.println("普通流复制时间:"+(end - start)+" 毫秒");}

   稍微替换了一个类速度得到了明显提升,最终花费1089毫秒,也就是1秒。既然缓冲流减少了IO的交互次数,从代码中具体是在哪里体现的呢?

在这里插入图片描述

   上面这张图是缓冲输入流的构造方法(输出流同理),可以看到构造时创建了一个8192字节大小的byte数组,这就是用空间换时间所指的空间

在这里插入图片描述

   接下来这张图是缓冲流的Write方法,真正写出数据的方法是flushBuffer(),只有当读取大小达到指定空间大小后才会写出(read同理),从创建到操作都减少了IO的读取次数。

   既然知道了原理不难发现,其实这就是对普通的流进行了二次封装,那我们自己是不是也可以搞一个类似的代码,毕竟47Mb的文件复制执行了1秒还是太慢了,注意看,上面每次我们的读写都还是每次1个字节1个字读取的,那是不是可以用数组呢。来吧,try it

public static void main(String[] args) {// 记录开始时间long start = System.currentTimeMillis();// 创建流对象try (BufferedInputStream fis = new BufferedInputStream(new FileInputStream("D:/test/file1.txt"));BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream("D:/test/file1Copy.txt"))){// 读写数据int len;byte[] bytes = new byte[8 * 1024];while ((len = fis.read(bytes)) != -1) {fos.write(bytes, 0, len);}} catch (Exception e) {e.printStackTrace();}// 记录结束时间long end = System.currentTimeMillis();System.out.println("复制时间:"+(end - start)+" 毫秒");}

在这里插入图片描述

   表现很优异,只花费了63毫秒,这里我们创建的数组大小刚好是和Java中默认的读取大小一致,如果将这个大小继续调整,变成16*1024或者更大呢?经过测试发现只有扩大一倍后速度有显著提升,达到44毫秒,继续扩大速度提升都在几毫秒,而为了这几毫秒浪费那么大的空间倒是有些没必要了。当然,也可以修改构造方法,改变默认IO读写的空间大小,但很遗憾,经过不断测试,速度每次减少也在几毫秒,可能跟内存大小有关,一般按照规范的大小其实就可以了。

2.4.2 字符缓冲流

   字符缓冲流这里只简单进行介绍,和字节缓冲流使用方法基本一致,最大的不同应该就是多了一个readLine和newLine方法比较常用,如果有兴趣的可以在idea中写例子进行探索。

2.5 转换流(不常用,但是得知道原理)

   为什么会有转换流的存在,是因为编码格式在作祟,因为一个汉字、字母、数字在计算机中存储的格式是不一样的,用二进制存储,和我们看到的肯定不一样,比如字母A在计算机中存储1000001,而计算机之所以知道这是A,就是因为编码格式记录好了,1000001表示A。

   那上面说的和转换流有什么关系,为什么说转换流不常用,就是因为数据在传输的时候用的是二进制,我们需要使用转换流才能识别成我们能看懂的文字,其实总结来说转换流就是字节流和字符流转换的工具
在这里插入图片描述

   说到这和大姐在普及一个知识点,不同编码方式的区别在哪里,其实在最开始没有汉字的编码方式,搞得中国很难受,最后还是有牛人做了中文编码集GBK才解决了,所以,吾辈要自强啊。下面是常见编码格式的区别:

  1. ASCII(美国信息交换标准代码)
  • 定义:ASCII 是最早的字符编码标准之一,用于表示英文字符和一些控制字符。
  • 范围:
    ASCII 使用 7 位二进制数(即 1 字节,最高位为 0)表示字符,共有 128 个字符,包括:
    26 个大写英文字母(A-Z)
    26 个小写英文字母(a-z)
    10 个数字(0-9)
    34 个标点符号和控制字符(如空格、换行符等)
  • 特点:
    简单高效,适合英文字符。
    在早期计算机系统中广泛使用。
    不支持非英文字符(如中文、日文等)。
    应用场景:主要用于英文文本处理,如早期的计算机通信、操作系统和编程语言。
  1. ISO-8859-1(Latin-1)
  • 定义:ISO-8859-1 是一种扩展的 ASCII 编码,用于支持西欧语言(如法语、德语、西班牙语等)。
  • 范围:
    使用 8 位二进制数(1 字节),共 256 个字符。其中:
    前 128 个字符与 ASCII 相同。
    后 128 个字符用于表示西欧语言中的特殊字符(如 é、ü、ö 等)。
  • 特点:
    支持多种西欧语言。
    仍然无法支持其他语言(如中文、日文、阿拉伯语等)。
    应用场景:主要用于西欧语言的文本处理,如早期的网页、电子邮件等。
  1. GBK 和 GB2312(中文编码)
  • 定义:GBK 和 GB2312 是用于表示中文字符的编码标准。
  • 范围:
    GB2312:使用双字节表示字符,支持 6763 个汉字和符号,主要用于简体中文。
    GBK:是 GB2312 的扩展,支持更多汉字(包括繁体字)和符号,共约 21003 个字符。
  • 特点:
    专门用于中文字符,兼容 ASCII(单字节表示英文字符)。
    不支持其他语言(如日文、韩文等)。
    应用场景:主要用于中文操作系统(如 Windows 简体中文版)、中文网页等。
  1. Shift-JIS(日文编码)
  • 定义:Shift-JIS 是一种用于表示日文字符的编码标准。
  • 范围:使用单字节和双字节混合编码,支持日文假名、汉字(Kanji)和 ASCII 字符。
  • 特点:
    支持日文字符,兼容 ASCII。
    不支持其他语言。
    应用场景:主要用于日文操作系统、日文网页等。
  1. UTF-8(Unicode 转换格式)
  • 定义:UTF-8 是一种可变长度的 Unicode 编码,用于表示 Unicode 字符集。
  • 范围:
    根据字符的不同,使用 1 到 4 个字节表示:
    ASCII 字符(0-127)使用 1 个字节。
    拉丁语系、希腊语、西里尔语等字符使用 2 个字节。
    中文、日文、韩文等字符使用 3 个字节。
    特殊字符(如表情符号)使用 4 个字节。
  • 特点:
    兼容 ASCII(单字节表示英文字符)。
    支持全球所有语言。
    可变长度编码,节省存储空间。
    应用场景:广泛应用于现代互联网(如 HTML、JSON)、操作系统(如 Linux、macOS)、编程语言(如 Python、Java)等。

   当然现在大多数语言和编码工具都解决了乱码的问题,但这不代表说我们之后不会遇到,大家接触最多的可能就是GBK和UTF-8了,当然首选肯定是UTF-8,因为这是最全的。

2.6 序列化流(了解)

   序列化流和转换流有相同点,都是将某个东西转为字节传输和存储,而序列化流是对象和字节相互转换的途径,这里主要是也会将对象的映射关系等其它信息也会序列化,信息比较完整。(图片引自“二哥的Java进阶之路”)
在这里插入图片描述

   因为序列化流使用次数不多,这里仅做简单的了解,我们只需要知道怎么回事就可以了。

   Java中使用ObjectInputStream 和ObjectOutputStream进行序列化,需要注意的是序列化和反序列的对象必须要继承 Serializable 接口,这个没说的,创建完对象之后使用writeObject和readObject方法进行序列化和反序列化,但是因为Java自带的这个序列化实际弊端很大,容易破解还占用大,所以一般我们使用第三方库进行,主要弊端如下:

  1. 安全漏洞
    Java 序列化对象容易受到安全风险的影响,例如反序列化漏洞可能允许攻击者执行任意代码,从而对系统造成严重的安全威胁。
  2. 性能限制
    序列化和反序列化过程相对较慢,尤其是对于大型或复杂的对象。
    序列化后的字节体积较大,增加了存储和传输成本。
  3. 版本控制问题:
    当类结构发生变化时,反序列化旧的序列化对象可能导致兼容性问题和潜在的运行时异常。
  4. 处理复杂对象图的限制
    Java 序列化可能难以处理具有循环引用或瞬态字段的复杂对象图,导致意外行为或异常。
  5. 可移植性差
    Java 序列化是 Java 特有的,无法跨语言进行序列化和反序列化。

Java 序列化的替代产品
为了解决上述痛点,以下是一些流行的替代方案:

  1. Kryo
  • 优势:
    高性能、高效率,生成的序列化数据体积小。
    易于使用和扩展,支持多种数据类型。
  1. Protocol Buffers(Protobuf)
  • 优势:
    高效紧凑,生成的二进制数据体积小,传输速度快。
    跨平台兼容,支持多种编程语言。
    强类型支持,减少运行时错误。
  • 劣势:
    需要定义 .proto 文件来描述数据结构,增加了开发复杂性。
    二进制格式难以直接调试。
  1. Apache Avro
  • 优势:
    支持模式演变,即使数据结构发生变化,也能处理旧数据。
    高效性能,以二进制格式存储数据。
    轻量运行,模式信息嵌入序列化文件中。
  • 劣势:
    模式定义复杂,学习曲线较陡。
  1. JSON
  • 优势:
    简单易读,适合调试和开发。
    跨语言支持,广泛应用于 Web 开发。
  • 劣势:
    数据体积相对较大,性能不如二进制格式。

   在上述几种常用的替代方案中我们用的最多的可能就是JSON了,这是在网络传输(尤其是不涉及文件或图片视频的情况)几乎是首选,而在之前的版本中fastjson是阿里的开源产品最受欢迎,不过由于后面发现了重大的漏洞则改变为fastjson2

2.7 打印流(了解)

   打印流这个概念可能大多数伙伴第一次听说,打印两个字不知道是不是你们小时候的噩梦(去,把这份卷子打印一下发给全班同学),真的很惨。其实Java的打印流我们在没有接触框架前经常使用,就是向控制台打印信息,而具体的类就是PrintStream ,获取方式就是:System.out。

PrintStream 类的常用方法包括:

  • print():输出一个对象的字符串表示形式。
  • println():输出一个对象的字符串表示形式,并在末尾添加一个换行符。
  • printf():使用指定的格式字符串和参数输出格式化的字符串。
        PrintStream ps = System.out;ps.printf("姓名:%s,年龄:%d,成绩:%f", "沉默的程序猴", 18, 99.9);

   为什么说这一部分是了解,虽然我们在学习的时候都是通过打印流向控制台打印信息的,但终归只是学习,在真正的开发中很少能见到上面这种打印日志的方式,都是通过各种的日志框架实现的,虽然底层都是打印流,常见的就是log4j。

   这里稍微多说两句,一个好的程序猴对于日志的处理是非常优秀的,往往通过日志的查看就能快速定位问题,在实际开发中非常的节省时间,这样就不用测试人员再复现问题,然后你的本地进行debug找出问题。分享一个打印日志的好习惯

  1. 开发中打印日志不要一下输出整个对象,尤其是那种字段很多的对象,你很难在一堆字母中找到你想要的属性,建议的做法是只打印出你需要的属性即可。
  2. 一个方法中的日志最好在开头带上方法名和一段“===============”,这样日志非常清晰,在实际开发中非常有用,毕竟日志是相当多的。
  3. 实际开发中一个方法会存在很深层次的调用,通常会涉及多角度逻辑处理,打印日志时有一个总体方法的开始和结束标识,日后在日志分割时也能快速定位。同时尽量将每一个业务的日志也打上开始和结束标识,标明这一段业务逻辑结束了。

号外

RandomAccessFile

   RandomAccessFile 是 Java 中一个非常强大的类,位于 java.io 包中。它允许对文件进行随机访问,即可以随机读取或写入文件的任意位置,而不仅仅是顺序读取或写入。这使得它在处理大型文件或需要频繁访问文件特定位置的场景中非常有用。

  1. 特点
    随机访问:可以自由地定位到文件的任意位置进行读写操作。
    支持读写:既可以读取文件内容,也可以写入文件内容。
    文件指针:通过维护一个文件指针(file pointer),记录当前读写的位置。
    灵活性:支持多种数据类型(如 int、double、String 等)的读写。
  2. 构造方法
    RandomAccessFile 的构造方法需要两个参数:
    文件路径:可以是文件名或 File 对象。
    访问模式:指定文件的打开模式,常见的模式有:
    “r”:只读模式。
    “rw”:读写模式。
    “rws”:读写模式,同时要求对文件内容或元数据的更新同步写入到底层存储设备。
    “rwd”:读写模式,要求对文件内容的更新同步写入到底层存储设备。
  3. 常用方法
    (1)定位方法
    seek(long pos):将文件指针移动到指定位置(从文件开头开始计数,单位为字节)。
    getFilePointer():获取当前文件指针的位置。
    length():获取文件的长度(单位为字节)。
    (2)读取方法
    read():读取单个字节。
    read(byte[] b):读取字节到数组。
    readLine():按行读取(已废弃,建议使用其他方式替代)。
    readInt()、readDouble()、readUTF() 等:读取特定数据类型。
    (3)写入方法
    write(int b):写入单个字节。
    write(byte[] b):写入字节数组。
    writeInt(int v)、writeDouble(double v)、writeUTF(String s) 等:写入特定数据类型。
    (4)其他方法
    close():关闭文件,释放资源。
  4. 使用场景
    RandomAccessFile 适用于以下场景:
    随机读写文件:例如,对文件的某一部分进行修改,而不需要重新写入整个文件。
    处理大型文件:通过随机访问,可以高效地读取文件的特定部分,而不需要加载整个文件。
    模拟数据库:在文件中存储结构化数据,并通过随机访问实现快速查询和更新。
  5. 示例代码
    示例 1:随机读取文件内容
    java复制
public class RandomAccessFileExample {public static void main(String[] args) {try (RandomAccessFile raf = new RandomAccessFile("example.txt", "r")) {// 跳到文件的第10个字节raf.seek(10);byte[] buffer = new byte[10];int bytesRead = raf.read(buffer);System.out.println("Read: " + new String(buffer, 0, bytesRead));} catch (IOException e) {e.printStackTrace();}}
}

示例 2:随机写入文件内容
java复制

public class RandomAccessFileExample {public static void main(String[] args) {try (RandomAccessFile raf = new RandomAccessFile("example.txt", "rw")) {// 跳到文件的第20个字节raf.seek(20);// 写入字符串raf.write("Hello, RandomAccessFile!".getBytes());} catch (IOException e) {e.printStackTrace();}}
}

示例 3:读写特定数据类型
java复制

public class RandomAccessFileExample {public static void main(String[] args) {try (RandomAccessFile raf = new RandomAccessFile("data.dat", "rw")) {// 写入数据raf.writeInt(123);raf.writeDouble(45.67);raf.writeUTF("Hello");// 重新定位到文件开头raf.seek(0);// 读取数据int intValue = raf.readInt();double doubleValue = raf.readDouble();String stringValue = raf.readUTF();System.out.println("Int: " + intValue);System.out.println("Double: " + doubleValue);System.out.println("String: " + stringValue);} catch (IOException e) {e.printStackTrace();}}
}
  1. 注意事项
    文件指针:seek() 方法会移动文件指针,因此需要小心管理指针位置。
    文件长度:如果写入的位置超过了文件当前长度,文件会自动扩展。
    线程安全:RandomAccessFile 不是线程安全的,如果需要多线程访问,需要手动同步。
    性能:随机访问文件可能会导致磁盘寻道次数增加,影响性能,因此需要合理使用。
  2. 总结
    RandomAccessFile 是 Java 中一个非常灵活的文件操作类,支持随机读写操作,适合处理大型文件或需要高效访问文件特定位置的场景。通过合理使用其方法,可以实现复杂的文件操作需求。

   关于RandomAccessFile的介绍我就先说到这,因为我对其本身也仅限于文字了解和基本的使用,我在公司负责的业务板块并不是这里,可能之后有空闲时间会和其他组进行交流和实践后会发布一篇文章,具体的业务是在线学习和直播,这里对于流的操作非常专业,同时也涉及和阿里对接(oss服务器和转码)。

总结

   好了,这篇文章到这里就告一段落,关于IO流这篇文章只是想完善一下Java的学习体系,也是想让大家对于Java的IO流不那么陌生,同时也满足基本的开发和使用,如果说业务方向是视频、图片和直播等那这篇文章就显得不专业了,希望大家理解,下一篇我们将讲解一下NIO。

相关文章:

IO、NIO解读和不同点,以及常用的文件流操作方法

java高级——IO、NIO解读和不同点,以及常用的文件流操作方法 前情提要文章介绍1. 什么是IO1.1 节点的分类1.2 传输方式 2. 七大传输方式解读2.1 File类解读2.1.1 创建文件的三种方式2.2.2 File的常用方法2.2.3 如何正确认识FileUtils 2.2 字节流(核心&am…...

51单片机-按键

1、独立按键 1.1、按键介绍 轻触开关是一种电子开关,使用时,轻轻按开关按钮就可使开关接通,当松开手时,开关断开。 1.2、独立按键原理 按键在闭合和断开时,触点会存在抖动现象。P2\P3\P1都是准双向IO口,…...

Spring Boot Actuator 监控✨

Spring Boot Actuator 是 Spring Boot 提供的一个强大的监控和管理工具,它可以帮助你深入了解和监控你的应用程序的运行状态。通过 Actuator,你可以获取应用程序的健康状况、内存使用情况、线程信息、HTTP 请求跟踪等。🚀 核心知识点 &#…...

SpringBoot论坛网站 – 功能详解与部署教程

项目概述 《SpringBoot的论坛网站》是一个基于SpringBoot框架开发的现代化论坛平台,旨在为用户提供一个便捷的交流空间。该项目不仅功能丰富,还具备良好的扩展性和易用性,适合用于学习、分享和讨论各类话题。以下是项目的核心功能模块和部署…...

俄罗斯方块游戏完整代码示例

以下是一个基于Cocos Creator引擎开发的俄罗斯方块游戏的完整代码示例。该游戏实现了俄罗斯方块的基本功能,并且代码整合在单个文件中,无需任何外部依赖,可以直接在浏览器中运行。 1. 创建Cocos Creator项目 首先,确保你已经安装了…...

【设计模式】【结构型模式】组合模式(Composite)

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD 🔥 2025本人正在沉淀中… 博客更新速度 👍 欢迎点赞、收藏、关注,跟上我的更新节奏 🎵 当你的天空突…...

【设计模式】03-理解常见设计模式-行为型模式(专栏完结)

前言 前面我们介绍完创建型模式和创建型模式,这篇介绍最后的行为型模式,也是【设计模式】专栏的最后一篇。 一、概述 行为型模式主要用于处理对象之间的交互和职责分配,以实现更灵活的行为和更好的协作。 二、常见的行为型模式 1、观察者模…...

Qt 6.8版本 自制windows下运行软件<一>——串口调试助手

自制串口调试助手 哔哩哔哩效果展示 一、 说明 本人在读学生,跟随哔哩哔哩网站北京迅为公司的教学视频,进行学习qt,由于视频中的实现过程是利用ui界面的实现,本人在学习过程中,通过完全敲代码的形式,实现同…...

Qt——静态函数中发送信号方法总结(不需要通过类内部信号与槽实现,关键是清楚你发送的信号源自哪个对象)

【系列专栏】:博主结合工作实践输出的,解决实际问题的专栏,朋友们看过来! 《项目案例分享》 《极客DIY开源分享》 《嵌入式通用开发实战》 《C++语言开发基础总结》 《从0到1学习嵌入式Linux开发》 《QT开发实战》 《Android开发实战》...

深入解析 vLLM:高性能 LLM 服务框架的架构之美(一)原理与解析

修改内容时间2.4.1处理请求的流程,引用更好的流程图2025.02.11首发2025.02.08 深入解析 vLLM:高性能 LLM 服务框架的架构之美(一)原理与解析 深入解析 vLLM:高性能 LLM 服务框架的架构之美(二)…...

关于视频去水印的一点尝试

一. 视频去水印的几种方法 1. 使用ffmpeg delogo滤镜 delogo 滤镜的原理是通过插值算法,用水印周围的像素填充水印的位置。 示例: ffmpeg -i input.mp4 -filter_complex "[0:v]delogox420:y920:w1070:h60" output.mp4 该命令表示通过滤镜…...

前端常见面试题-2025

vue4.0 Vue.js 4.0 是在 2021 年 9 月发布。Vue.js 4.0 是 Vue.js 的一个重要版本,引入了许多新特性和改进,旨在提升开发者的体验和性能。以下是一些关键的更新和新特性: Composition API 重构:Vue 3 引入了 Composition API 作为…...

JavaEE-SpringBoot快速入门

文章目录 本节目标Maven什么是Maven创建一个Maven项目maven项目功能maven的依赖管理全球仓库, 私服, 本地服务器, 配置国内镜像 第一个SpringBoot项目创建项目运行SpringBoot程序 SpringBoot原理初步Web服务器 总结 本节目标 了解什么是maven, 配置国内源使用Springboot创建项…...

盛铂科技 SMF106 低相位噪声贴片式频率综合器模块

在现代通信和电子设备领域,频率综合器作为关键组件,其性能优劣直接影响系统的整体表现。盛铂科技的 SMF106 低相位噪声贴片式频率综合器,以其卓越的性能和独特设计,成为众多高性能系统的选择。 一、频率覆盖范围广,步进…...

前端【技术方案】重构项目

1. 明确重构目标 优化性能 减少页面加载时间降低资源占用 提升代码可维护性 更规范的代码风格更清晰的代码结构更明确的模块设计 扩展功能 为项目添加新功能改进现有功能 2. 评估项目现状 审查代码 全面检查现有代码,找出代码中的问题,如代码冗余、耦合…...

第十六天 HarmonyOS WebView开发实战:从加载网页到与JavaScript交互

HarmonyOS WebView开发实战:从加载网页到与JavaScript交互 一、WebView基础与HarmonyOS特性解析 在移动应用开发中,WebView作为内嵌浏览器组件,在HarmonyOS(鸿蒙系统)中扮演着重要角色。它不仅能够加载本地和远程网页…...

Unity学习part2

为bilibili教程【【Unity教程】零基础带你从小白到超神】 https://www.bilibili.com/video/BV1gQ4y1e7SS/?p50&share_sourcecopy_web&vd_source6e7a3cbb802eb986578ad26fae1eeaab的笔记 1、灯光的使用 定向光模拟太阳,是平行光。旋转定向光,光…...

贪吃蛇游戏

贪吃蛇 一、html <div class"container" id"app"></div><script src"./js/index.js"></script>二、css * {margin: 0;top: 0;} .set {margin: 15px auto;width: 600px; } .container {width: 600px;height: 600px;bac…...

docker修改镜像默认存储路径(基于 WSL2 的迁移方法)

打开powershell窗口 任意地方shift右键 1、停止 WSL wsl --shutdown2、导出数据 wsl --export docker-desktop-data E:\docker\DockerDesktopdata\docker-desktop-data.tar wsl --export docker-desktop E:\docker\DockerDesktop\docker-desktop.tar3、取消注册 wsl --un…...

C#+SqlSugar实现主从库读写分离

在使用 **SqlSugar** 进行分库操作时&#xff0c;可以通过配置多个数据库连接&#xff0c;并根据业务逻辑动态切换数据库。以下是一个完整的分库示例&#xff0c;展示如何实现分库功能。 --- ### **1. 安装 NuGet 包** 安装 SqlSugarCore&#xff1a; bash dotnet add packag…...

从无序到有序:上北智信通过深度数据分析改善会议室资源配置

当前企业普遍面临会议室资源管理难题&#xff0c;预约机制不完善和临时会议多导致资源调度不合理&#xff0c;既有空置又有过度拥挤现象。 针对上述问题&#xff0c;上北智信采用了专业数据分析手段&#xff0c;巧妙融合楼层平面图、环形图、折线图和柱形图等多种可视化工具&a…...

以太网详解(八)传输层协议:TCP/UDP 协议

文章目录 传输层协议概述为什么需要传输层&#xff1f;传输层功能网络层与传输层在实现 “端到端” 传输的异同两类服务:面向连接/无连接服务 传输控制协议 TCPTCP 协议数据单元格式TCP 的重传机制快重传和快恢复快重传举例快恢复算法 用户数据报协议 UDPUDP 概述UDP 基本工作过…...

CentOS 8 配置bond

CentOS 8 网络配置的详细步骤和对应的配置文件内容。 1. 配置聚合网卡(Bonding) 配置intranet聚合网卡 在/etc/sysconfig/network-scripts/目录下创建ifcfg-intranet文件,内容如下: TYPE=Bond NAME=intranet DEVICE=intranet ONBOOT=yes BOOTPROTO=none IPADDR=10.2.1.22…...

C语言基础16:二维数组、字符数组

二维数组 定义 二维数组本质上是一个行列式的组合&#xff0c;也就是说二维数组由行和列两部分组成。属于多维数组&#xff0c;二维数组数据是通过行列进行解读。 二维数组可被视为一个特殊的一维数组&#xff0c;相当于二维数组又是一个一维数组&#xff0c;只不过它的元素…...

Java 同步锁性能的最佳实践:从理论到实践的完整指南

目录 一、同步锁性能分析 &#xff08;一&#xff09;性能验证说明 1. 使用同步锁的代码示例 2. 不使用同步锁的代码示例 3. 结果与讨论 &#xff08;二&#xff09;案例初步优化分析说明 1. 使用AtomicInteger原子类尝试优化分析 2. 对AtomicInteger原子类进一步优化 …...

思科、华为、H3C常用命令对照表

取消/关闭 思科no华为undo华三undo 查看 思科show华为display华三display 退出 思科exit华为quit华三quit 设备命名 思科hostname华为sysname华三sysname 进入全局模式 思科enable、config terminal华为system-view华三system-view 删除文件 思科delete华为delete华…...

[qt5学习笔记]Application Example示例程序源码解析

开发环境问题 vs2022下直接打开ui、ts文件失败 解决办法如下图&#xff0c; 设置designer独立运行。估计是嵌入运行存在些许bug。 同理&#xff0c;ts编辑工具linguist也存在这个问题。 qrc rc的编辑嵌入编辑都正常&#xff0c;但分离式更稳定可靠。 qt creator编译失败 原…...

华为交换机堆叠技术简介配置

目录 一、华为堆叠技术简介&#xff08;一&#xff09;提高可靠性&#xff08;二&#xff09;扩展端口数量&#xff08;三&#xff09;增大带宽&#xff08;四&#xff09;简化组网&#xff08;五&#xff09;长距离堆叠 二、华为交换机堆叠技术的案例及命令配置&#xff08;一…...

腿足机器人之四- 卡尔曼滤波

腿足机器人之四卡尔曼滤波 概率学基础贝叶斯准则熵 卡尔曼滤波扩展卡尔曼滤波信息滤波器 在机器人&#xff08;四足、人形&#xff09;领域&#xff0c;感知和行动的不确定性可能由多种因素引起&#xff0c;如传感器噪声、外部环境的变化、非精确控制以及实时性算力限制等。 和…...

nginx 部署前端vue项目

&#x1f468;‍⚕ 主页&#xff1a; gis分享者 &#x1f468;‍⚕ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕ 收录于专栏&#xff1a;前端工程师 文章目录 一、&#x1f353;什么是nginx&#xff1f;二、&#x1f353;nginx 部署…...

【第1章:深度学习概览——1.6 深度学习框架简介与选择建议】

嘿,各位老铁们,今天咱们来一场深度学习框架的深度探索之旅。在这个充满无限可能的深度学习时代,深度学习框架就像是连接理论与实践的桥梁,帮助我们从算法设计走向实际应用。随着技术的飞速发展,深度学习框架的选择变得越来越多样化,每一种框架都有其独特的优势和适用场景…...

NLLB 与 ChatGPT 双向优化:探索翻译模型与语言模型在小语种应用的融合策略

作者&#xff1a;来自 vivo 互联网算法团队- Huang Minghui 本文探讨了 NLLB 翻译模型与 ChatGPT 在小语种应用中的双向优化策略。首先介绍了 NLLB-200 的背景、数据、分词器和模型&#xff0c;以及其与 LLM&#xff08;Large Language Model&#xff09;的异同和协同关系。接着…...

C#的委托delegate与事件event

在C#中&#xff0c;delegate&#xff08;委托&#xff09;和 event&#xff08;事件&#xff09;是两个非常重要的概念&#xff0c;它们主要用于实现回调机制和事件驱动编程。下面详细介绍它们的原理和使用场景。 1. Delegate&#xff08;委托&#xff09; 1.1 委托的原理 委托…...

Spring Boot 集成MyBatis-Plus

文章目录 一、背景说明二、集成过程 2.1 引入 maven 依赖2.2 增加属性配置2.3 自动配置类 三、验证集成 3.1 控制器3.2 服务类3.3 Mapper接口类3.4 实体类3.4 不要忘记XML文件3.5 发起请求 四、技巧拓展 4.1 如何打印sql语句&#xff1f;4.2 如何对参数增加非空验证&#xff1f…...

javacv将视频切分为m3u8视频并播放

学习链接 ffmpeg-demo 当前对应的 gitee代码 Spring boot视频播放(解决MP4大文件无法播放)&#xff0c;整合ffmpeg,用m3u8切片播放。 springboot 通过javaCV 实现mp4转m3u8 上传oss 如何保护会员或付费视频&#xff1f;优酷是怎么做的&#xff1f; - HLS 流媒体加密 ffmpe…...

Docker 入门与实战:从安装到容器管理的完整指南

&#x1f680; Docker 入门与实战&#xff1a;从安装到容器管理的完整指南 &#x1f31f; &#x1f4d6; 简介 在现代软件开发中&#xff0c;容器化技术已经成为不可或缺的一部分。而 Docker 作为容器化领域的领头羊&#xff0c;以其轻量级、高效和跨平台的特性&#xff0c;深…...

计算机视觉:卷积神经网络(CNN)基本概念(二)

第一章&#xff1a;计算机视觉中图像的基础认知 第二章&#xff1a;计算机视觉&#xff1a;卷积神经网络(CNN)基本概念(一) 第三章&#xff1a;计算机视觉&#xff1a;卷积神经网络(CNN)基本概念(二) 第四章&#xff1a;搭建一个经典的LeNet5神经网络 接上一篇《计算机视觉&am…...

SQL SERVER的PARTITION BY应用场景

SQL SERVER的PARTITION BY关键字说明介绍 PARTITION BY关键字介绍具体使用场景排名计算累计求和分组求最值分组内百分比计算分组内移动平均计算分组内数据分布统计分组内数据偏移计算 总结 PARTITION BY关键字介绍 在SQL SERVER中&#xff0c;关键字PARTITION BY主要用于窗口函…...

【ISO 14229-1:2023 UDS诊断全量测试用例清单系列:第十二节】

ISO 14229-1:2023 UDS诊断服务测试用例全解析&#xff08;TesterPresent_0x3E服务&#xff09; 作者&#xff1a;车端域控测试工程师 更新日期&#xff1a;2025年02月14日 关键词&#xff1a;UDS协议、0x3E服务、会话保持、ISO 14229-1:2023、ECU测试 一、服务功能概述 0x3E服…...

gsoap实现webservice服务

gsoap实现webservice服务 在实现Web服务时&#xff0c;使用gSOAP是一个很好的选择&#xff0c;因为它提供了强大的工具和库来创建SOAP和RESTful服务。gSOAP是一个C和C语言开发的库&#xff0c;它支持SOAP协议的各种版本&#xff0c;包括SOAP 1.1和SOAP 1.2。下面是如何使用gSO…...

达梦:dmserver占用io高排查

目录标题 1. 使用达梦数据库的性能视图查询当前活动会话查询执行时间较长的 SQL 2. 使用 DM 性能监视工具3. 使用操作系统工具监控 I/Oiostat 工具dstat 工具 4. 优化查询和索引审查 SQL 执行计划优化索引 5. 调整数据库参数6. 分析数据库日志7. 硬件和存储检查总结 针对达梦数…...

MoE架构中的专家选择门控机制:稀疏激活如何实现百倍效率突破?

技术原理&#xff08;数学公式与核心逻辑&#xff09; 核心公式 门控网络输出&#xff1a; G ( x ) Softmax ( W g ⋅ x b g ) G(x) \text{Softmax}(W_g \cdot x b_g) G(x)Softmax(Wg​⋅xbg​) 最终输出&#xff1a; y ∑ i 1 n G i ( x ) ⋅ E i ( x ) (仅保留Top-…...

用python写一个聊天室程序

下面是一个简单的基于Socket的Python聊天室程序示例&#xff0c;包括服务器端和客户端&#xff1a; 服务器端代码&#xff1a; import socket import threadingdef handle_client(client, address):print(f"New connection from {address}")while True:msg client…...

七星棋牌全开源修复版源码解析:6端兼容,200种玩法全面支持

本篇文章将详细讲解 七星棋牌修复版源码 的 技术架构、功能实现、二次开发思路、搭建教程 等内容&#xff0c;助您快速掌握该棋牌系统的开发技巧。 1. 七星棋牌源码概述 七星棋牌修复版源码是一款高度自由的 开源棋牌项目&#xff0c;该版本修复了原版中的多个 系统漏洞&#…...

Vulhub靶机 ActiveMQ任意 文件写入(CVE-2016-3088)(渗透测试详解)

一、开启vulhub环境 docker-compose up -d 启动 docker ps 查看开放的端口 漏洞版本&#xff1a;ActiveMQ在5.14.0之前的版本&#xff08;不包括5.14.0&#xff09; 二、访问靶机IP 8161端口 默认账户密码都是admin 1、利用bp抓包&#xff0c;修改为PUT方法并在fileserver…...

Cloud: aws:network: limit 含有pps这种限制

https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/troubleshooting-ena.html#statistics-ena 这个是调查网络问题的一个网页; 在里面,竟然含有pps这种限制:ethtool -S;其实是比较苛刻的安全相关的策略? [ec2-user ~]$ ethtool -S ethN NIC statistics:tx_timeout: …...

28、深度学习-自学之路-NLP自然语言处理-做一个完形填空,让机器学习更多的内容程序展示

import sys,random,math from collections import Counter import numpy as npnp.random.seed(1) random.seed(1) f open(reviews.txt) raw_reviews f.readlines() f.close()tokens list(map(lambda x:(x.split(" ")),raw_reviews))#wordcnt Counter() 这行代码的…...

观察者模式说明(C语言版本)

观察者模式主要是为了实现一种一对多的依赖关系&#xff0c;让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时&#xff0c;会通知所有观察者对象&#xff0c;使它们能够自动更新自己。下面使用C语言实现了一个具体的应用示例&#xff0c;有需要的可以参考…...

LC-搜索二维矩阵II、相交链表、反转链表、回文链表、环形链表、环形链表ll

搜索二维矩阵II 方法&#xff1a;从右上角开始搜索 我们可以从矩阵的右上角开始进行搜索。如果当前元素 matrix[i][j] 等于 target&#xff0c;我们直接返回 true。如果 matrix[i][j] 大于 target&#xff0c;说明 target 只能出现在左边的列&#xff0c;所以我们将列指针向左…...

如何查看 Linux 服务器的 MAC 地址:深入解析与实践指南

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...