java序列化和文件的输入和输出

news2024/11/25 15:57:26

文章目录

  • 一、JAVA的序列化
    • 1. 简介
    • 2. 对象序列化的步骤
    • 3. 小结
    • 4. 序列化
    • 4. 解序列化
  • 二、对象的序列化
    • 1. 简介
    • 2. Java.io.File.class
    • 3. 缓冲区
  • 三、序列化版本控制

一、JAVA的序列化

1. 简介

如何将我们的java对象存储起来,这里介绍两种思路:

  • 如果我们只有自己写的java程序会用到这些数据:此时我们就可以用序列化,将被序列化的对象写到文件中,然后就可以让你的程序去文件中读取序列化的对象并把它们展开回到活生生的状态。
  • 如果数据需要被其他程序引用:写一个纯文本文件,用其他程序可以解析的特殊字符写到文件中。

Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化,也就是说,对象的类型信息、对象的数据,还有对象中的数据类型可以用来在内存中新建对象。整个过程都是 Java 虚拟机(JVM)独立的,也就是说,在一个平台上序列化的对象可以在另一个完全不同的平台上反序列化该对象。类 ObjectInputStream 和 ObjectOutputStream 是高层次的数据流,它们包含反序列化和序列化对象的方法。

2. 对象序列化的步骤

  • 创建出FileOutPutStream
FileOutputStream fileStream = new FileOutputSream("MyGame.ser")
  • 创建ObjectOutputStream
ObjectOutputStream os=new ObjectOutputStream(fileStream);
  • 写入对象
os.writeObject(characterOne)
  • 关闭ObjectOutputStream
os.close()

将串流(Stream)连接起来代表源与目的地(文件或网络端口)的连接。串流必须要连接到某处才能算是个串流,一般来说,串流要两两连接才能做出有意义的事,其中一个代表连接,另一个则是要被调用方法,因为连接的串流通常都是很底层的。以FileOutStream为例,它有可以写入字节的方法。但我们通常不会直接去写字节,而是以对象层次的观点来写入,所以需要高层的连接串流。那为什么不以单一的串流来执行?这就要考虑到良好的面向对象设计了,每个类只要做好一件事。FileOutputStream把字节写入文件,ObjectOutputStream把对象转换成可以写入串流的数据。当我们调用ObjectOutputStream的writeObject方法时,对象就会被打成串流送到FileOutputStream中来写入文件中。

3. 小结

Java序列化是将Java对象转换为字节流的过程,以便在网络上传输或保存到磁盘中。序列化的主要目的是实现对象的持久化存储和传输,即使在不同的操作系统和编程语言之间也能保持对象的一致性。以下是Java序列化的几个重要意义:

  • 持久化存储:通过序列化,可以将对象保存到磁盘上或数据库中,以便在程序重新启动或在不同的应用程序之间共享数据。这对于需要长期存储和检索数据的应用程序非常有用,例如文件系统、缓存系统和持久化框架。

  • 网络传输:在网络编程中,通过序列化,可以将Java对象转换为字节流,然后通过网络传输到远程机器上。这种方式在分布式系统、客户端-服务器应用程序和远程方法调用(RPC)中非常常见。通过序列化,可以轻松地在不同的计算机之间传递对象,并在远程机器上进行反序列化以还原对象。

  • 对象复制:通过序列化,可以实现对象的深层复制。当需要创建一个已有对象的独立副本时,序列化提供了一种简单而有效的方法。通过将对象序列化到字节流中,然后再从字节流中反序列化,可以获得一个与原始对象相同的副本。

  • 缓存和缓存共享:序列化可以用于实现缓存系统,将经过计算的对象序列化并保存在内存中,以便下次需要时可以快速检索。此外,序列化还可以实现将缓存数据共享到不同的应用程序实例或服务器之间,以提高系统性能和减少重复计算。

尽管Java序列化具有上述优点,但它也有一些限制。例如,序列化的性能相对较低,并且对于频繁变化的对象结构可能不稳定。此外,Java序列化还存在安全性和版本兼容性的考虑因素。因此,在使用Java序列化时,需要谨慎考虑这些因素,并评估是否有更好的替代方案,例如JSON、XML或Protocol Buffers等。

4. 序列化

当一个对象被序列化时,被该对象引用的实例变量也会被序列化。且所有被引用的对象也会被序列化,且这些操作都是自动进行的。如果要让类能够被序列化,该类就需要实现Serializable接口。

Serializable接口又被称为marker或tag类标记用接口,因为此接口并没有任何方法需要实现,它的唯一目的就是声明有实现它的类是可以被序列化的。也就是说,此类型的对象可以通过序列化的机制来存储。如果某类是可序列化的,则它的子类也是可以序列化的(类的继承关系可以知道)

//Serializable接口源码
public interface Serializable {
}
public class Main {
    public static void main(String[] args) throws InterruptedException {
        Box mybox=new Box();
        mybox.setHeight(50);
        mybox.setWidth(20);
        try {
            //定义文件输出流
            FileOutputStream fs=new FileOutputStream("foo.ser");
            ObjectOutputStream os=new ObjectOutputStream(fs);
            os.writeObject(mybox);
            os.close();
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }
}
class Box implements Serializable{
    private int width;
    private int height;

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }
}

在这里插入图片描述

如果某类的某实例变量不能或不应该被实例化,我们可以使用 transient关键字对其标记,序列化程序会自动跳过

4. 解序列化

将对象序列化整个事情的重点在于你可以事后,在不同Java虚拟机执行期,把对象恢复到存储时的状态。解序列化的步骤如下:

  • 创建FileInputStream
FileInputStream fileStream =new FileInputStream("mygame.ser")
  • 创建ObjectInputStream
ObjectInputStream os=new ObjectInputStream(fileStream);
  • 读取对象
//该方法会从Stream中读出下一个对象,读取顺序会和写入顺序相同,次数超过会抛出异常
Object one=os.readObject();
  • 转换对象类型
myObject mot=(myObject) one;
  • 关闭流

当对象被解序列化时,Java虚拟机会通过尝试在堆上创建新的对象,让它维持与被序列化时有相同的状态来恢复对象的原状(说明对象反序列化是多例的,它会创建新的对象)。所以解序列化底层的过程其实是下面这样:

  1. 对象从stream中读出来
  2. Jvm通过存储的信息判断出对象的class类型
  3. Jvm虚拟机尝试寻找和加载对象的类。如果Java虚拟机找不到或无法加载该类,则Java虚拟机会抛出异常
  4. 新的对象会被配置在堆上,但构造函数不会执行!很明显的,这样会把对象的状态抹去又变成全新的,而这不是我们想要的结果,我们需要对象回到序列化之前的状态
  5. 如果对象在继承树上有个不可序列化的祖先类,则该不可序列化类以及在它之上的类的构造函数(就算是可序列化也一样)就会执行。一旦构造函数连锁启动后就无法停止。也就是说,从第一个不可序列化的父类开始,全部都会重新初始状态
  6. 对象的实例变量会被还原成序列化时的状态值,transient变量会被赋值为null的对象引用或primitive主类型的默认为0、false等值
public class Main {
    public static void main(String[] args) throws InterruptedException {
        GameCharacter one=new GameCharacter(50,"Elf",new String[]{"bow","sword","dust"});
        GameCharacter two=new GameCharacter(200,"Troll",new String[]{"bare hands","big ax"});
        GameCharacter three=new GameCharacter(120,"Magician",new String[]{"spelld","invisibility"});
        System.out.println("序列化之前的one的hash值:"+one.hashCode());
        try{
            ObjectOutputStream os=new ObjectOutputStream(new FileOutputStream("Game.ser"));
            os.writeObject(one);
            os.writeObject(two);
            os.writeObject(three);
            os.close();

        }catch (Exception e){
            e.printStackTrace();
        }
        //设置成null,因此无法获取堆上的这些对象
        one=null;
        two=null;
        three=null;
        try{
            ObjectInputStream  is=new ObjectInputStream(new FileInputStream("Game.ser"));
            GameCharacter oneRestore=(GameCharacter) is.readObject();
            GameCharacter twoRestore=(GameCharacter) is.readObject();
            GameCharacter threeRestore=(GameCharacter) is.readObject();
            System.out.println("One`s type:"+oneRestore.getType());
            System.out.println("反序列化之后的one的hash值:"+oneRestore.hashCode());

        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
class GameCharacter implements  Serializable{
    int power;
    String type;
    String[] weapons;
    public GameCharacter(int p,String t, String[] w){
        power=p;
        type=t;
        weapons=w;
    }
    public int getPower(){
        return power;
    }
    public String getType(){
        return type;
    }
    public String getWeapons(){
        String weaponList="";
        for (int i = 0; i < weapons.length; i++) {
            weaponList+=weapons[i]+"";
        }
        return weaponList;
    }
}

在这里插入图片描述

从结果来看,序列化之前和反序列化之后的对象并不是同一个,但状态是一样的

二、对象的序列化

1. 简介

通过序列化来存储对象是Java程序在来回执行间存储和恢复数据最简单的方法。但有时你还得把数据存储到单纯的文本文件中。假设你的Java程序必须把数据写到文本文件中以让其他可能是非java的程序读取。假设你的Servlet(在web服务器上执行的Java程序)会读取用户在网页上输入的数据,并将它写入文本文件以让网站管理入能够用电子表格来分析数据。

2. Java.io.File.class

File这个类代表磁盘上的文件,但并不是文件中的内容。你可以把File对象想象成文件的路径,而不是文件本身。例如File并没有读写文件的方法。关于File有个很有用的功能是它提供一种比使用字符串文件名来表示文件名的类也可以用File对象来代替该参数,以便检查路径是否合法,然后再把对象传给FileWriter或FileInputStream,下面是File的常用使用:

  • 创建出代表现存盘文件的File对象
File f=new File("MyCode.txt")
  • 建立新的目录
File dir=new File("Chapter7")
  • 列出目录下的内容
if(dir.isDirectory()){
{
  String[] dirContents=dir.list();
  for(int i=0;i<dirContents.length;i++){
    System.out.println(dirContents[i]);
    }
}
  • 取得文件或目录的绝对路径
System.out.println(dir.getAbsolutePath());
  • 删除文件或目录
boolean isDeleted=f.delete();

3. 缓冲区

缓冲区的奥妙之处在于使用缓冲区比没有缓冲区的效率更好。你也可以直接使用FileWriter,调用它的write()来写文件,但它每次都会直接写下去。而通过BufferedWriterFileWriter的链接,BufferedWriter可以暂存一堆数据,然后到满的时候再实际写入磁盘,这样就可以减少对磁盘的操作的次数。如果你想要强制缓冲区立即写入,只要调用writer.flush()这个方法就可以要求缓冲区马上把内容写下去。BufferedReader是Java IO包中用于读取字符数据的缓冲输入流。它提供了对底层输入流的缓冲读取,以提高读取性能。下面是BufferedReader缓存原理的简要说明:

  • 缓冲区:BufferedReader内部维护了一个字符缓冲区(char数组),用于临时存储从底层输入流读取的字符数据。缓冲区的大小可以根据需要进行配置,默认为8192个字符。

  • 填充缓冲区:当我们调用BufferedReader的读取方法(如read()、readLine())时,它会尝试从缓冲区中读取字符数据。如果缓冲区已经为空,它会从底层输入流中读取一定数量的字符数据,并将其存储到缓冲区中。

  • 读取缓冲区:一旦缓冲区中有数据,BufferedReader会直接从缓冲区返回所需的字符数据,而无需每次都直接访问底层输入流。这样可以减少对底层输入流的频繁读取操作,提高读取效率。

  • 缓冲区管理:当缓冲区中的数据被读取完毕后,BufferedReader会再次尝试从底层输入流中读取新的字符数据,并填充到缓冲区中,以供后续读取操作使用。这个过程在需要的时候会自动进行,我们不需要手动管理缓冲区。

通过使用缓冲区,BufferedReader实现了批量读取字符数据,减少了对底层输入流的频繁读取操作,从而提高了读取的效率。缓冲区的大小可以根据需要进行调整,以平衡内存消耗和读取性能。需要注意的是,在使用BufferedReader读取字符数据时,我们应该使用其提供的读取方法(如read()、readLine()),而不是直接操作底层输入流。这样可以确保数据按照缓冲区的方式进行读取,以获得更好的性能和可靠性。总结而言,BufferedReader通过内部的字符缓冲区实现了对底层输入流的缓冲读取,减少了IO操作次数,提高了读取性能。BufferedWriter是Java IO包中用于写入字符数据的缓冲输出流。它提供了对底层输出流的缓冲写入,以提高写入性能。下面是BufferedWriter的工作原理的简要说明:

  • 缓冲区:BufferedWriter内部维护了一个字符缓冲区(char数组),用于临时存储待写入底层输出流的字符数据。缓冲区的大小可以根据需要进行配置,默认为8192个字符。

  • 写入缓冲区:当我们调用BufferedWriter的写入方法(如write()、append())时,它会将待写入的字符数据暂时存储到缓冲区中。

  • 刷新缓冲区:当缓冲区被填满或我们显式调用flush()方法时,BufferedWriter会将缓冲区中的数据一次性写入底层输出流。这样可以减少对底层输出流的频繁写入操作,提高写入效率。

  • 关闭流:当我们调用BufferedWriter的close()方法关闭流时,它会先自动调用flush()方法将缓冲区中的剩余数据写入底层输出流,然后关闭底层输出流。

通过使用缓冲区,BufferedWriter实现了批量写入字符数据,减少了对底层输出流的频繁写入操作,从而提高了写入的效率。缓冲区的大小可以根据需要进行调整,以平衡内存消耗和写入性能。需要注意的是,在使用BufferedWriter写入字符数据时,我们应该使用其提供的写入方法(如write()、append()),而不是直接操作底层输出流。这样可以确保数据按照缓冲区的方式进行写入,以获得更好的性能和可靠性。总结而言,BufferedWriter通过内部的字符缓冲区实现了对底层输出流的缓冲写入,减少了IO操作次数,提高了写入性能。

  • 准备一个txt文件
    在这里插入图片描述

  • 读取文件文件

public class QuizCardBuilder {

    public static void main(String[] args) {
        try {
            File myfile=new File("MyText.txt");
            FileReader fileReader=new FileReader(myfile);
            //将FileReader链接到BufferdReader以获取更高的效率,它只会在缓冲区空的时候才会去磁盘取
            BufferedReader reader=new BufferedReader(fileReader);
            String line=null;
            while((line=reader.readLine())!=null ){
                System.out.println(line);
            }
           reader.close();
        }catch (Exception e){
            e.printStackTrace();
        }

   }
}

在这里插入图片描述

三、序列化版本控制

serialVersionUID是Java中用于序列化和反序列化对象的一个特殊属性,它是一个静态常量,用于标识类的版本。在Java的序列化机制中,每个可序列化的类都被赋予一个唯一的标识符,即serialVersionUID。它用于判断序列化的对象与反序列化时使用的类定义是否兼容。以下是serialVersionUID的一些特点和使用方式:

  • 版本控制:serialVersionUID用于控制类的版本。当一个类进行了更改,如添加或删除字段、修改方法等,serialVersionUID的值也应相应更改。通过版本控制,可以确保序列化和反序列化的兼容性,防止因类定义的更改而导致的版本冲突。

  • 默认值生成:如果在类定义中未显式声明serialVersionUID,Java序列化机制会自动生成一个值。生成规则基于类的结构,如类名、字段和方法等。这种自动生成的serialVersionUID对于不涉及版本控制的简单场景可能是足够的。

  • 显式声明:可以在类定义中显式声明serialVersionUID,通过指定一个固定的值来控制版本。在进行序列化和反序列化时,Java会比较序列化对象的serialVersionUID与反序列化时使用的类定义的serialVersionUID,如果两者不匹配,则会抛出InvalidClassException。

  • 序列化兼容性:当对一个已序列化的对象进行反序列化时,Java会通过比较对象的serialVersionUID与当前类定义的serialVersionUID来判断兼容性。如果两者匹配,则可以成功反序列化;如果不匹配,则会抛出InvalidClassException。

注意事项:序列化和反序列化的兼容性要求类的结构和逻辑保持一致,即不能改变类的实例变量和方法的签名。
如果类定义中未显式声明serialVersionUID,并且进行了类结构的更改,如添加或删除字段等,反序列化时可能会导致InvalidClassException。为避免此问题,建议在进行类结构更改时显式声明serialVersionUID并进行版本控制。
总结而言,serialVersionUID是用于版本控制和兼容性验证的一个特殊属性,在Java的序列化和反序列化过程中起着重要的作用。通过正确管理和使用serialVersionUID,可以确保序列化和反序列化的正确性和兼容性。

  • 首先进行序列化
public class QuizCardBuilder {

    public static void main(String[] args) {
        dog mydog=new dog();
        try {
            //定义文件输出流
            FileOutputStream fs=new FileOutputStream("foo2.ser");
            ObjectOutputStream os=new ObjectOutputStream(fs);
            os.writeObject(mydog);
            os.close();
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }
}
class dog implements Serializable{
    static final long serialVersionUID=21321312L;
    private String name;
    private int size;
}
  • 查看序列化版本
serialver dog

在这里插入图片描述

  • 更改代码结构查看会不会出问题
class dog implements Serializable{
    static final long serialVersionUID=21321313L;
    private String name;
    private int size;
    private int age;
}
  • 反序列化
public class QuizCardBuilder {

    public static void main(String[] args) {
        dog mydog=null;
        try{
            ObjectInputStream  is=new ObjectInputStream(new FileInputStream("foo2.ser"));
            dog oneRestore=(dog) is.readObject();

        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

出现版本不一致问题
在这里插入图片描述

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

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

相关文章

【JavaEE进阶】——第八节.SpringBoot统一功能处理

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;JavaEE进阶 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01; 文章目录 前…

智能公厕引导系统为未来共享公厕发展新趋势打造基础

智慧公厕管理系统可以提高公厕的服务质量和管理效率&#xff0c;实现公厕的自动化管理和数据分析。该系统可以广泛应用于各类公共场所&#xff0c;如商场、公园、机场、医院等。 一、智慧公厕引导系统案例展示 智慧公厕管理系统还可以实现公厕的人口统计和安全监控等功能。服务…

【今天聊聊AI】AI歌手会取代流行歌手吗

一、聊聊AI歌手发展现状 近日&#xff0c;“AI孙燕姿”火遍全网&#xff0c;AI孙燕姿翻唱林俊杰的《她说》、周董的《爱在西元前》、赵雷的《成都》等等歌曲让网友听了直呼&#xff1a;“听了一晚上&#xff0c;出不去了。” 现在AI歌手技术到底已经发展到了怎样的地步&#xf…

MySQL千万级数据优化方案

简介 ↓↓↓处理千万级数据的MySQL数据库&#xff0c;可以采取以下优化措施↓↓↓ 使用索引&#xff1a;确保对经常用于查询和排序的字段添加索引。不要在查询中使用SELECT *&#xff0c;而是明确指定需要的字段。分区表&#xff1a;如果表中的数据按照时间或其他维度进行划分&…

26岁机电专业,零基础转行学云计算,求推荐靠谱机构!

26岁机电专业&#xff0c;零基础转行学云计算&#xff0c;求推荐靠谱机构&#xff01; 关于26岁零基础转行云计算&#xff0c;求机构推荐这类问题。建议可以先把自己的具体需求整理清楚&#xff0c;例如自身的学习能力情况、需要学习的专业的技术内容、你想要通过培训学习达到什…

承接vue2->vue3的一些变动

1.Vue3简介 2020年9月18日&#xff0c;Vue.js发布3.0版本&#xff0c;代号&#xff1a;One Piece&#xff08;海贼王&#xff09;耗时2年多、2600次提交、30个RFC、600次PR、99位贡献者github上的tags地址&#xff1a;https://github.com/vuejs/vue-next/releases/tag/v3.0.0 …

NetApp FAS 混合闪存阵列协助您建构简单易用、聪明智慧、值得信赖的储存基础架构

NetApp FAS 混合闪存阵列 主要优势 1、简单易用&#xff1a;节省您宝贵的时间、金钱和人力 •几分钟内完成储存资源配置。 •以获证实的效率技术降低成本。 •可在单一系统上管理档案与区块资料。 2、聪明智慧&#xff1a;灵活因应瞬息万变的业务需求 •以不中断营运的方式扩…

javascript中的错误类型

javascript 中的错误类型&#xff1a; SyntaxErrorTypeErrorReferenceErrorRangeErrorURLErrorError SyntaxError 语法错误 // 当您在编写一个函数时忘记了括号 ,)来括起您的代码&#xff0c;您将收到一个SyntaxError错误 function say(text) {return text; }say(shark;// …

[论文阅读72]Parameter-Efficient Transfer Learning for NLP

1. 基本信息 题目论文作者与单位来源年份Parameter-Efficient Transfer Learning for NLPNeil Houlsby等Google Research&#xff0c;雅盖隆大学-波兰PMLR2019 Houlsby N, Giurgiu A, Jastrzebski S, et al. Parameter-efficient transfer learning for NLP[C]//Internationa…

代码随想录训练营Day49| 121. 买卖股票的最佳时机 122.买卖股票的最佳时机II

目录 学习目标 学习内容 121. 买卖股票的最佳时机 122.买卖股票的最佳时机II 学习目标 121. 买卖股票的最佳时机 122.买卖股票的最佳时机II 学习内容 121. 买卖股票的最佳时机 121. 买卖股票的最佳时机 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/prob…

chatgpt赋能Python-python_ip地址转换

Python IP地址转换&#xff1a;原理、常用函数和示例 在网络编程中&#xff0c;IP地址是一个很重要的概念。Python提供了很多IP地址转换函数&#xff0c;这篇文章将讲解其原理、常用函数和示例。 IP地址转换原理 IP地址是由32位表示的二进制数&#xff0c;它是计算机在网络上…

socket概述 python中如何使用TCP/UDP协议实现通信-教程

很多编程语言中&#xff0c;都使用scoket套接字实现网络通信。 Socket是对TCP/IP协议的封装&#xff0c;Socket本身就是一个调用接口&#xff08;API&#xff09;&#xff0c;方便程序员用Socket使用TCP/IP协议簇&#xff0c;实现网络通信。 不同编程语言&#xff0c;shiyong…

基于ROS2的costmap中Obstacle Layer中对障碍物信息的增加与删除机制的方案调研。

文章目录 1.背景2.目标3. 障碍物信息添加方式发送数据的数据结构与接收数据的数据结构 4. 障碍物清理机制4.1 可调参数4.2 优化光追算法4.3 障碍物跟踪 1.背景 基于costmap地图&#xff0c;使用navigation导航时&#xff0c;会出现由于激光雷达/图像测距的局限性&#xff0c; …

RocketMQ 在小米的多场景灾备实践案例

作者&#xff1a;邓志文、王帆 01 为什么要容灾&#xff1f; 在小米内部&#xff0c;我们使用 RocketMQ 来为各种在线业务提供消息队列服务&#xff0c;比如商城订单、短信通知甚至用来收集 IoT 设备的上报数据&#xff0c;可以说 RocketMQ 的可用性就是这些在线服务的生命线…

我写了个操作系统,却被别人给骂了!

前言 哈喽&#xff0c;小伙伴们好&#xff0c;我是子牙。是一个擅长深入研究Windows内核、Linux内核、Hotspot源码的资深程序员&#xff0c;之前是JVM专家&#xff0c;手写过JVM。所以做老师后&#xff0c;第一个打造的课程便是《手写JVM》小班&#xff0c; 一经推出就受到了学…

我的苹果手机的越狱之旅

最近因为业务需要&#xff0c;需要一台越狱手机&#xff1b;就把测试机6plus拿来做越狱使用&#xff0c;在此之前先大致说明一下越狱的原理、应用、流程以及可能存在的问题&#xff1a; 越狱是指通过一些技术手段&#xff0c;使iOS设备可以访问到iOS系统的全部控制权&#xff0…

抛弃Vuex,使用Pinia

Pinia 符合直觉的 Vue.js 状态管理库 文章目录 Pinia 符合直觉的 Vue.js 状态管理库1.简介2.为什么要使用Pinia3.安装3.1 挂载pinia 4.创建一个store容器4.1 Option 参数4.2 Setup 参数 5.三个重要概念5.1 State5.2 Getter**5.3 Action** 6.购物车实例6.1 商品列表组件 1.简介 …

【Python-Django】如何在一个项目中创建多个app模块

django开发案例&#xff1a;a​​​​​【Django】开发日报_1_Day&#xff1a;用户管理系统案例-创建项目_django 开发用户管理系统_代码骑士的博客-CSDN博客 前面做过的管理系统项目功能比较单一&#xff0c;只用一个模块就能解决问题。如果想创建多个不同的模块的话&#xf…

Nodejs之HTTP模块

目录 前言一&#xff0c;创建HTTP模块1.1 基本使用1.2 注意事项 二&#xff0c;查看报文2.1 浏览器查看HTTP报文2.2 利用request获取HTTP请求报文2.2.1 获取请求行和请求头 2.2.2 获取请求路径2.2.3 获取查询字符串 三&#xff0c;跟请求报文相关的练习四&#xff0c;设置响应报…