【从零开始学习JAVA | 第三十六篇】IO流下的高级流

news2024/12/24 0:18:51

目录

前言:

1.缓冲流:

2.转换流:

 3.序列化流:

4.打印流:

5.压缩流:

总结:


前言:

        在前面我们从IO流体系出发,分别介绍了字节流和字符流,并且详细讲解了其下沿的各种基本流,而在今天我们就要学习一下IO流中的几个高级流。

1.缓冲流:

        缓冲流其实就是可以让我们自己手动提供缓冲区,以此来增加读写速度,我们知道字节流是没有缓冲区的,因此创建一个缓冲区对于字节流的读写速度提高是很明显的。

        缓冲区是计算机科学中常用的一个概念,用于在不同速度的系统或设备之间传输数据时解决速度不匹配的问题。它是用来临时存储数据的一段内存区域,可用于暂时保存输入数据、输出数据或处理中间数据。

        缓冲区通常用于处理输入/输出(I/O)操作,例如在文件读写、网络通信、图像处理等过程中。它能够提高数据传输的效率,减少因速度不匹配而导致的延迟和性能瓶颈。

        总结来说,缓冲区是一种临时存储数据的内存区域,可以解决不同速度设备之间数据传输的速度不匹配问题,提高系统的效率。它在输入/输出操作和数据处理过程中起到关键的作用。 

  1. 字节缓冲流(BufferedInputStream和BufferedOutputStream):
    • BufferedInputStream:将字节输入流包装起来,提供了在内存中创建缓冲区的功能,这样可以减少磁盘IO次数。
    • BufferedOutputStream:将字节输出流包装起来,同样提供了缓冲区,可以减少磁盘IO次数。

示例代码:

import java.io.*;

public class ByteStreamExample {
    public static void main(String[] args) {
        try {
            // 创建字节输入流
            FileInputStream fileInputStream = new FileInputStream("input.txt");
            // 使用字节缓冲流包装字节输入流
            BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);

            // 创建字节输出流
            FileOutputStream fileOutputStream = new FileOutputStream("output.txt");
            // 使用字节缓冲流包装字节输出流
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);

            // 读取文件内容并写入到目标文件
            int data;
            while ((data = bufferedInputStream.read()) != -1) {
                bufferedOutputStream.write(data);
            }

            // 关闭流
            bufferedInputStream.close();
            bufferedOutputStream.close();

            System.out.println("文件复制成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

字节缓冲流的底层默认就自带了一个长度为8192的缓冲区,这在我们一开始学习到的基本字节流中是没有的。但其实字节缓冲流本质上只是对基本字节流的一个包装,在读取数据的时候依然是基本字节流在读取数据,并不是什么新的方法。

我们可以通过调试这段代码看到缓冲区:

  1. 字符缓冲流(BufferedReader和BufferedWriter):
    • BufferedReader:将字符输入流包装起来,提供了在内存中创建缓冲区的功能,可以一次读取一行字符数据。
    • BufferedWriter:将字符输出流包装起来,同样提供了缓冲区,可以一次写入一行字符数据。

示例代码:

import java.io.*;

public class CharacterStreamExample {
    public static void main(String[] args) {
        try {
            // 创建字符输入流
            FileReader fileReader = new FileReader("input.txt");
            // 使用字符缓冲流包装字符输入流
            BufferedReader bufferedReader = new BufferedReader(fileReader);

            // 创建字符输出流
            FileWriter fileWriter = new FileWriter("output.txt");
            // 使用字符缓冲流包装字符输出流
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);

            // 读取文件内容并写入到目标文件
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                bufferedWriter.write(line);
                bufferedWriter.newLine();
            }

            // 关闭流
            bufferedReader.close();
            bufferedWriter.close();

            System.out.println("文件复制成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 虽然基本的字符流方法就自带缓冲区,但是我们缓冲字符流也提供了一些很好的方法来便捷我们的使用。

 readLine在读取方法时候,一次读一整行,遇到回车换行就结束

但是他不会把回车换行读到内存中

 缓冲流的优点:

1. 提高读写性能:缓冲流通过在内存中创建缓冲区,减少IO操作的次数,从而提高读写性能。它们可以一次读取或写入多个字节或字符,减少了与磁盘或网络的交互次数。

2. 减少系统开销:使用缓冲流可以减少系统调用次数和CPU的使用率。通过将数据保存在内存中的缓冲区中,在进行大规模数据读写时,可以显著降低系统开销和资源消耗。

3. 支持高效的字符处理:字符缓冲流(BufferedReader和BufferedWriter)提供了方便的方法,比如一次读取一行字符数据或一次写入一行字符数据。这对于处理文本文件非常有用,可以简化代码并提高代码的可读性。

4. 缓冲区自动管理:缓冲流会自动管理缓冲区的大小和数据的填充和清空,无需手动处理。这样可以简化代码编写,并提高代码的可维护性。

5. 可以与其他IO流结合使用:缓冲流可以与其他各种字节流或字符流结合使用,如文件流、网络流等。这使得在不同场景下进行读写操作更加灵活和高效。

2.转换流:

转换流(InputStreamReader和OutputStreamWriter)是Java IO库中的字符流,用于在字节流和字符流之间进行转换。它们提供了将字节流转换为字符流的能力,以便更方便地处理文本数据。

转换流的主要作用是解决字节流和字符流之间的编码转换问题。在读取或写入文本数据时,需要将字节数据转换为字符数据或将字符数据转换为字节数据。转换流在内部实现了字符集的转换,可以根据指定的字符集将字节流转换为字符流,或将字符流转换为字节流。

转换流的构造函数接受两个参数:字节流和字符集。其中,字节流可以是字节输入流或字节输出流,而字符集可以是标准字符集名称或特定字符集名称。

主要的转换流类及其作用如下:

  1. InputStreamReader:
    • 作用:将字节输入流转换为字符输入流。
    • 构造函数:
      • InputStreamReader(InputStream in):创建一个默认字符集的InputStreamReader对象。
      • InputStreamReader(InputStream in, Charset cs):创建一个使用指定字符集的InputStreamReader对象。
    • 示例代码:
import java.io.*;

public class InputStreamReaderExample {
    public static void main(String[] args) {
        try {
            FileInputStream fileInputStream = new FileInputStream("input.txt");
            InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8");

            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

            String line;
            while ((line = bufferedReader.readLine()) != null) {
                System.out.println(line);
            }

            bufferedReader.close();
            inputStreamReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  1. OutputStreamWriter:
    • 作用:将字节输出流转换为字符输出流。
    • 构造函数:
      • OutputStreamWriter(OutputStream out):创建一个默认字符集的OutputStreamWriter对象。
      • OutputStreamWriter(OutputStream out, Charset cs):创建一个使用指定字符集的OutputStreamWriter对象。
    • 示例代码:
import java.io.*;

public class OutputStreamWriterExample {
    public static void main(String[] args) {
        try {
            FileOutputStream fileOutputStream = new FileOutputStream("output.txt");
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF-8");

            BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);

            bufferedWriter.write("Hello, World!");
            bufferedWriter.newLine();
            bufferedWriter.write("你好,世界!");

            bufferedWriter.close();
            outputStreamWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

通过使用转换流,我们可以在字节流和字符流之间进行方便的转换,使得处理文本数据更加简便和灵活。转换流还支持指定字符集,以便进行特定编码的读写操作。

转换流的存在主要是为了:让字节流使用字符流中的方法。

其实在以前,我们还可以利用转换流来使得读取文件的时候按照指定的字符编码读取。

 3.序列化流:

序列化流是Java IO库中的一种特殊流,用于将对象转换为字节序列,以便存储到文件、数据库或通过网络进行传输。它们提供了对象的序列化和反序列化功能,使得可以方便地将对象持久化或在不同系统之间传输对象数据。

java中的序列化和反序列化是通过ObjectInputStream和ObjectOutputStream类来实现的,它们是字节流的子类,用于处理对象的读写操作。

序列化流的主要作用有以下几点:

  1. 对象的持久化:通过将对象转换为字节序列,可以将对象永久保存到磁盘上。这样即使程序结束,对象数据也可以被重新读取和使用。对象的持久化在很多场景中非常有用,比如保存用户信息、游戏进度等。

  2. 对象的传输:通过序列化流,可以将对象转换为字节序列,然后发送到网络上或通过进程间通信进行传输。接收方可以使用反序列化将字节序列转换回对象,以便获取传输的对象数据。这在分布式系统、远程方法调用等场景中非常常见。

序列化流的用法包括以下几个步骤:

  1. 对象的序列化:
    • 创建一个输出流,如FileOutputStream或Socket.getOutputStream()。
    • 创建一个ObjectOutputStream对象,将输出流传递给它。
    • 使用ObjectOutputStream的writeObject()方法将对象写入输出流。该方法会将对象转换为字节序列并写入输出流。
try {
    FileOutputStream fileOut = new FileOutputStream("object.ser");
    ObjectOutputStream out = new ObjectOutputStream(fileOut);
    out.writeObject(object);
    out.close();
    fileOut.close();
} catch (IOException e) {
    e.printStackTrace();
}
  1. 对象的反序列化:
    • 创建一个输入流,如FileInputStream或Socket.getInputStream()。
    • 创建一个ObjectInputStream对象,将输入流传递给它。
    • 使用ObjectInputStream的readObject()方法从输入流中读取对象。该方法会将字节序列转换回对象。
try {
    FileInputStream fileIn = new FileInputStream("object.ser");
    ObjectInputStream in = new ObjectInputStream(fileIn);
    Object object = in.readObject();
    in.close();
    fileIn.close();
} catch (IOException e) {
    e.printStackTrace();
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

在进行对象的序列化和反序列化时,需要注意以下事项:

  • 要求被序列化的类必须实现Serializable接口,该接口没有任何方法,只是起到一个标识作用,表明该类可以进行序列化。
  • 被序列化的类中的字段也需要是可序列化的,即要求它们也是实现了Serializable接口的。
  • 序列化和反序列化的顺序必须一致,否则会导致错误。
  • 如果我们不想给对象中的某一个成员变量序列化,那么就使用transient关键字修饰,该关键字标记的成员变量不参与序列化过程。

使用序列化的注意点:

1. 类的版本控制:当你使用序列化流将对象写入文件时,类的定义可能会发生变化。为了确保在反序列化过程中能够正确地读取对象,你需要仔细控制类的版本你可以使用 `serialVersionUID` 字段来指定类的版本号,并在类的变化过程中进行更新。

不过我们可以手动设置让idea能对此进行提示,方便我们快捷生成serialVersionUID

2. 数据完整性:序列化流允许你将整个对象图写入文件,包括对象之间的引用。然而,这也意味着你需要确保所有相关的对象都被正确地序列化和反序列化,以保持数据的完整性。如果某个对象没有被序列化或者反序列化,那么它在反序列化后可能会变成 `null` 或者其他未初始化的值。

3. 安全性考虑:序列化流允许你将对象写入文件并从文件中读取。这可能引发安全问题,因为恶意用户可能会修改序列化的数据。为了确保安全性,你应该对序列化的数据进行验证和过滤,以防止不可预测的行为。

4. 跨语言兼容性:序列化是一种用于对象持久化和传输的通用机制。然而,不同的编程语言可能会使用不同的序列化格式和机制。如果你计划在不同的编程语言之间进行对象的序列化和反序列化,你需要确保选择一个与目标编程语言兼容的序列化方式。

5. 性能问题:序列化和反序列化过程可能会占用大量的时间和资源。如果你需要频繁地将大量对象进行序列化和反序列化操作,你可能需要考虑性能优化方面的问题,例如使用更快速的序列化库或者改变序列化策略。、

总结来说,序列化流在Java IO库中是一种特殊的流,用于对象的序列化和反序列化操作,实现了对象的持久化和传输功能。通过序列化和反序列化,我们可以方便地将对象转换为字节序列,进行持久化存储或跨系统传输。

4.打印流:

打印流是Java IO库中的一种流,用于将数据以文本形式输出到控制台或文件。它提供了简洁易用的打印方法,方便用户进行输出操作。

Java中的打印流有两种主要的实现类:PrintStream和PrintWriter。

  1. PrintStream:
    PrintStream是字节打印流,它继承自OutputStream类,并且提供了一系列打印方法,例如print、println等。PrintStream可以接收字节数据,并将其转换为字符输出,它还具备自动刷新和在操作完成后关闭的能力。

  2. PrintWriter:
    PrintWriter是字符打印流,它继承自Writer类,也提供了一系列打印方法。PrintWriter可以接收字符数据,并将其直接输出到目标,它也具备自动刷新和在操作完成后关闭的能力。

打印流的主要优点在于它们的简化和便利性,使用打印流可以更方便地进行输出操作,而不需要手动进行转换和拼接字符串。打印流的常见用法包括以下几点:

  1. 创建打印流实例:
    • PrintStream可以通过System.out获取标准输出流,也可以通过构造函数创建包装其他输出流的实例。
    • PrintWriter可以通过System.out获取标准输出流,也可以通过构造函数创建包装其他Writer的实例。
PrintStream ps = System.out;
PrintWriter pw = new PrintWriter(System.out);
  1. 打印数据:
    • 使用PrintStream的print和println方法可以打印各种数据类型的值,并通过toString方法将其转换为字符串。
    • 使用PrintWriter的print和println方法同样可以打印各种数据类型的值,它们会自动调用值的toString方法。
ps.print("Hello");
pw.println(12345);
ps.println(3.14);
pw.print(true);
  1. 自动刷新:
    • 打印流可以设置自动刷新功能,即每次写入后都会自动刷新缓冲区,将内容立即输出。
    • 可以通过setAutoFlush方法设置自动刷新的条件,当写入的数据超过一定大小或使用println方法时,缓冲区会被自动刷新。
pw = new PrintWriter(System.out, true); // 设置自动刷新
pw.println("Auto flush");

自动刷新这个函数虽然PrintStream也有,但是我们不会使用,因为字节流底层就没有缓冲区,开不开自动刷新都一样。

  1. 关闭打印流:
    • 使用打印流进行输出后,应该及时关闭它们。关闭打印流会自动关闭底层的输出流。
    • 可以通过调用close方法来关闭打印流。
ps.close();
pw.close();

总结来说,打印流是Java IO库中方便的输出流,它提供了打印各种数据类型的方法,并具备自动刷新和关闭的功能。使用打印流可以简化输出操作,减少手动转换和拼接字符串的工作量。

5.压缩流:

压缩流和解压缩流是Java IO库中用于对数据进行压缩和解压缩的特殊流,它们可以用于减小文件大小、提高网络传输效率以及节省存储空间。压缩流用于将数据压缩成较小的字节序列,而解压缩流则用于将压缩后的字节序列还原成原始的数据。

在Java中,压缩流和解压缩流是通过GZIP压缩算法实现的,主要包括以下两个类:

  1. GZIPOutputStream(压缩流):
    GZIPOutputStream是OutputStream的子类,它将数据压缩成GZIP格式的字节序列。可以将GZIPOutputStream连接到其他输出流(如FileOutputStream或Socket.getOutputStream()),将数据写入GZIPOutputStream,它会自动进行压缩并将结果写入连接的输出流。

压缩的代码示例:

try {
    FileOutputStream fileOut = new FileOutputStream("compressed.gz");
    GZIPOutputStream gzipOut = new GZIPOutputStream(fileOut);
    gzipOut.write(data);
    gzipOut.close();
    fileOut.close();
} catch (IOException e) {
    e.printStackTrace();
}
  1. GZIPInputStream(解压缩流):
    GZIPInputStream是InputStream的子类,它从GZIP格式的字节序列中读取数据并解压缩成原始数据。可以将GZIPInputStream连接到其他输入流(如FileInputStream或Socket.getInputStream()),通过读取GZIPInputStream获取解压缩后的数据。

解压缩的代码示例:

try {
    FileInputStream fileIn = new FileInputStream("compressed.gz");
    GZIPInputStream gzipIn = new GZIPInputStream(fileIn);
    byte[] buffer = new byte[1024];
    int length;
    while ((length = gzipIn.read(buffer)) != -1) {
        // 处理解压缩后的数据
    }
    gzipIn.close();
    fileIn.close();
} catch (IOException e) {
    e.printStackTrace();
}

压缩流和解压缩流的使用需要注意以下几点:

  • 压缩流和解压缩流只能处理单个文件或数据流,无法直接压缩多个文件或整个文件夹。如果需要压缩多个文件,可以将文件逐个写入压缩流,并标记文件名和内容长度,以便解压缩时进行还原。
  • 在使用压缩流和解压缩流时,应注意关闭流以释放资源,可以使用try-with-resources语句来自动关闭这些流。

总结来说,压缩流和解压缩流是Java IO库中用于对数据进行压缩和解压缩的特殊流。通过GZIPOutputStream可以将数据压缩成GZIP格式的字节序列,而通过GZIPInputStream可以将压缩后的字节序列解压缩成原始数据。在使用压缩流和解压缩流时,需要将其连接到其他流,并注意关闭流以释放资源。

总结:

        本文我们为大家介绍了这些IO流中的高级类,他们各自都有各自的最佳使用范围,我们可以根据自己的需求来选择到底使用哪一个高级类完成需求,而这些其实在日常中不是很普遍的使用,因此我们只需要知道这些东西,需要用的时候即用即查就好了。

如果我的内容对你有帮助,请点赞,评论,收藏创作不易,大家的支持就是我坚持下去的动力!

 

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

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

相关文章

推荐两款github敏感信息搜集工具(gsil、gshark)

推荐两款github敏感信息搜集工具(gsil、gshark) - 云社区 - 腾讯云 (tencent.com) github敏感信息泄露是很多企业时常忽视的一个问题,国外有一份研究报告显示,在超过24,000份的GitHub公开数据中,发现有数千个文件中可能…

python开发实战——ip池

前言 代理IP池是一组可用的代理IP地址,用于访问网站或执行其他网络请求。它可以帮助我们在网络请求时隐藏我们的真实IP地址,从而提高网络安全性、匿名性和稳定性。同时,代理IP池还可以通过定时更新和测试代理IP,保证代理IP的有效…

C#生成dll给c++调用 方法二COM方式 vs2022 NO Make Assembly COM-Visible选错了 不需要clr

有些C项目中也用了C语言.c,用方法一就无法使用【不能使用 /clr 选项编译 C 文件】。就用方法2。 方法二:COM方式 参考: https://www.5axxw.com/questions/content/2ozion 1.C# 生成dll using System; using System.Collections.Generic; using System.Linq; usin…

【Linux多线程】基于生产消费模型写的一个实例(附源代码+讲解)

生产消费模型 生产消费模型为何要使用生产者消费者模型生产者消费者模型优点 基于BlockingQueue的生产者消费者模型BlockQueue.cc代码解释 BlockQueue.hpp代码解释 Makefile代码解释 Task.hpp代码解释 生产消费模型 为何要使用生产者消费者模型 生产者消费者模式就是通过一个…

tdengine入门详解

TDengine是什么? TDengine 是一款开源、高性能、云原生的时序数据库(Time Series Database, TSDB), 它专为物联网、车联网、工业互联网、金融、IT 运维等场景优化设计,基于C语言开发。 什么是时序数据库?时序数据产生…

三元运算符引发的自动拆装箱问题

文章目录 问题背景问题排查排查过程问题扩展总结 问题背景 生产环境上出现空指针异常,追踪报错位置得知以下代码报错 if (isNull(aiGroup)) {return null;}aiGroup.setNum(isNull(param.getNum()) ? aiGroup.getNum() : param.getNum().doubleValue());问题排查 …

1500-2000元预算性价比吉他推荐,雅马哈FG800和VEAZEN费森VZ90怎么选?评测对比哪一款更适合初学者入门选购!

在2000元价位入门进阶吉他圈里,可谓是群雄角逐,Yamaha 雅马哈入门级FG800系列和VEAZEN 费森VZ90系列是一直都很热销的面单吉他型号,初学者想要在其中挑选出一把合适自己的吉他还是有点难度的。 那么,今天就以它们为本期的评测主角…

企业级高负载web服务器-Tomcat小项目

目录 web静态动态页面区别安装java环境安装Tomcat安装Tomcat包到目录查看Tomcat主目录结构查看Tomcat配置目录结构Tomcat管理Tomcat web管理功能 部署jpress应用 web静态动态页面区别 静态页面: 在网站设计中,纯粹HTML格式的网页(可以包含图…

后端整理(JVM、Redis、反射)

1. JVM 文章仅为自身笔记 详情查看一篇文章掌握整个JVM,JVM超详细解析!!! 1.1 什么是JVM jvm是Java虚拟机 1.2 Java文件的编译过程 程序员编写代码形成.java文件经过javac编译成.class文件再通过JVM的类加载器进入运行时数据…

MFC、Qt、WPF?该用哪个?

MFC、Qt和WPF都是流行的框架和工具,用于开发图形用户界面(GUI)应用程序。选择哪个框架取决于你的具体需求和偏好。MFC(Microsoft Foundation Class)是微软提供的框架,使用C编写,主要用于Windows…

蔚小理新势力互联网造车在CAN FD硬件主框架及后装控制方案开发

在国内,新势力造车影响已经非常之大,整个造车大潮中,新整车企业蔚来汽车、小鹏汽车、理想汽车无一例外选择了CAN FD作为主要的车载通信总线,特斯拉推出了引领汽车EE架构集中化的趋势,即使在车载以太网EE架构快速发展的…

BES 平台 SDK之充电盒与耳塞串口单工通信

本文章是基于BES2700 芯片,其他BESxxx 芯片可做参考,如有不当之处,欢迎评论区留言指出。仅供参考学习用! BES 平台 SDK之主从耳组队_谢文浩的博客-CSDN博客 关于系统主从耳组队流程可参考上一篇文章。链接如上所示! …

初识集合和背后的数据结构

目录 集合 Java集合框架 数据结构 算法 集合 集合,是用来存放数据的容器。其主要表现为将多个元素置于一个单元中,用于对这些元素进行增删查改。例如,一副扑克牌(一组牌的集合)、一个邮箱(一组邮件的集合)。 Java中有很多种集…

捷码低代码|Modal模态框组件详解

知识补充: 模态组件是一种在用户界面中显示的特殊类型的组件。它们被设计为在应用程序的其他部分被屏蔽或暂停的情况下引导用户完成一个特定的任务或交互。 常见的模态组件包括: 1、弹出窗口(Popup):弹出窗口是一种常见…

Unity中UGUI的 OnPopulateMesh函数与VertexHelper类

Graphics类 当一个UGUI的UI元素生成顶点数据时会调用Graphics类中的 OnPopulateMesh(VertexHelper vh) 函数,我们可以在这个函数中修改顶点的数据或者获取顶点的数据。 UGUI中与显示相关的控件,例如Image、Text、RawImage等都继承自MaskableGraphic类&a…

正则替换windows文件名禁用的特殊字符

背景&#xff1a; windows文件名中不能出现以下提示的特殊字符&#xff0c;因此需要提前替换处理。 解决&#xff1a; // 替换\/:*?"<>|为空 fileName.replaceAll("[\\\\/:*?\"<>|]", "");

Boost开发指南-3.9object_pool

object_pool object_pool是用于类实例&#xff08;对象&#xff09;的内存池&#xff0c;它的功能与pool类似&#xff0c;但会在析构时对所有已经分配的内存块调用析构函数&#xff0c;从而正确地释放资源。 object_pool位于名字空间boost&#xff0c;为了使用object_pool组件…

千元级入门单板吉他推荐,SAGA萨伽SF700、VEAZEN费森VZ200、布鲁克V12、恩雅X1PRO全方面评测对比,哪一款更值得购买!

很多吉他初学者的预算不多&#xff0c;就想要选购平价又好用的吉他&#xff0c;这个想法是很正确的。初学者要注意的是这种平价且高性价比的吉他需要仔细挑选&#xff0c;太便宜的合板吉他保证不了原材料的品质和制作工艺要求&#xff0c;音准手感都无法保证&#xff0c;那么这…

云主机秘钥泄露及利用

前言&#xff1a; 云平台作为降低企业资源成本的工具&#xff0c;在当今各大公司系统部署场景内已经成为不可或缺的重要组成部分&#xff0c;并且由于各类应用程序需要与其他内外部服务或程序进行通讯而大量使用凭证或密钥&#xff0c;因此在漏洞挖掘过程中经常会遇到一类漏洞&…

时间复杂度、空间复杂度实践练习(力扣OJ)

目录 文章目录 前言 题目一&#xff1a;轮转数组 思路一&#xff1a; 思路二&#xff1a; 思路三&#xff1a; 题目二&#xff1a;消失的数字 思路一&#xff1a; 思路二&#xff1a; 思路三&#xff1a; 题目三&#xff1a;移除元素 思路&#xff1a; 总结 前言 想要编写高效的…