【Android】Room—数据库的基本操作

引言

在Android开发中,数据持久化是一个不可或缺的部分。随着应用的复杂度增加,选择合适的数据存储方式变得尤为重要。Room数据库作为Android Jetpack架构组件之一,提供了一种抽象层,使得开发者能够以更简洁、更安全的方式操作SQLite数据库。本文将带你深入了解Room数据库的基本概念、使用方式以及最佳实践。

核心组件

Room是一个持久化库,它提供了一个抽象层,用于在SQLite数据库中存储和查询数据。它通过注解处理器和编译时检查,确保数据库操作的类型安全,减少了运行时错误的可能性。

Entity(实体):

  • Entity 是一个注解,用于标记一个类作为数据库中的一个表。
  • 每个 Entity 都映射到数据库中的一个表。
  • 你可以使用注解来定义表的名称(@PrimaryKey 用于定义主键,@Entity(tableName = "name")),以及定义列(@Column)。
  • 一个 Entity 类通常包含数据字段和适当的 getter 和 setter 方法。

Dao(数据访问对象):

  • Dao 是一个接口,它定义了对数据库的操作,如插入、查询、更新和删除。
  • 每个 Dao 都与一个 Entity 相关联,并且定义了与该 Entity 相关的数据库操作。
  • 你可以在 Dao 接口中使用注解来定义 SQL 语句,如 @Query@Insert@Update@Delete 等。
  • Room 会根据 Dao 接口自动生成实现代码。

Database(数据库):

  • Database 是一个抽象类,它定义了整个数据库的结构,包括所有的 Dao。
  • 它使用 @Database 注解来标记,并且可以定义版本号和包含的实体。
  • 你可以使用 Room.databaseBuilder() 方法来创建数据库实例。
  • 通常,一个应用中只有一个数据库类,它包含了所有的 Dao。

在这里插入图片描述

示例

只说概念可能会觉得抽象还是没有理解,接下来就通过一个示例来看看吧!

在使用room之前一定不要忘记它是要添加依赖

implementation("androidx.room:room-runtime:2.4.3")
annotationProcessor("androidx.room:room-compiler:2.4.3")

我们就设置一个学生示例吧,进行学生信息的增删改查操作,我们将学生的信息展示在页面上,设置增删改查操作的按钮,这部分的代码就不做说明了,你一定非常熟悉了。

  1. 定义Entity:我们要存放的是学生信息,先创建一个学生类:
@Entity(tableName = "student")
public class student {@PrimaryKey(autoGenerate = true)@ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER)int id;@ColumnInfo(name = "name", typeAffinity = ColumnInfo.TEXT)String name;@ColumnInfo(name = "age", typeAffinity = ColumnInfo.INTEGER)int age;public student(int id, String name, int age) {this.id = id;this.name = name;this.age = age;}@Ignore //告诉Room不要用这个构造方法,这是我们所用的方法public student(String name, int age) {this.name = name;this.age = age;}@Ignorepublic student(int id) {this.id = id;}public int getAge() {return age;}
}

里面的方法大家已经写了很多遍了,接下来就给大家解释里面的注解:

  • @Entity(tableName = "student")
    • 这个注解标记了这个类是一个数据库表的映射。tableName属性指定了数据库中表的名称,这里是"student"
  • @PrimaryKey(autoGenerate = true)
    • 这个注解标记了一个字段作为表的主键。autoGenerate = true表示主键值是自动生成的,通常是自增的。
  • @ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER)
    • 这个注解提供了字段的额外信息。name属性指定了数据库中列的名称,这里是"id"typeAffinity属性指定了列的数据类型,这里是ColumnInfo.INTEGER,表示这个字段在数据库中是整型。
  1. ColumnInfo.INTEGER:表示列的数据类型为整型。
  2. ColumnInfo.TEXT:表示列的数据类型为文本。
  3. ColumnInfo.REAL:表示列的数据类型为浮点数。
  4. ColumnInfo.BLOB:表示列的数据类型为二进制数据(例如图片或文件)。
  5. ColumnInfo.FLOAT:表示列的数据类型为浮点数,与REAL类似,但更明确表示为浮点数。
  6. ColumnInfo.LONG:表示列的数据类型为长整型。
  7. ColumnInfo.SHORT:表示列的数据类型为短整型。
  8. ColumnInfo.BOOLEAN:表示列的数据类型为布尔值。
  • @Ignore
    • 这个注解用于告诉Room忽略接下来的构造函数,不将其作为数据库操作的一部分。这通常用于那些仅用于应用逻辑,而不是数据库操作的构造函数。
  1. 定义DAO:创建一个接口来定义数据访问对象(DAO),使用@Dao注解标记。在这个接口中,定义方法来执行数据库操作,如插入、查询、更新和删除。
@Dao
public interface StudentDao {@Insertvoid insertStudent(student... students);@Deletevoid deleteStudent(student... students);@Updatevoid updateStudent(student... students);@Query("SELECT * FROM student")List<student> getAllStudent();@Query("SELECT * FROM student WHERE id = :id")List<student> getStudentById(int id);
}

都是接口当中定义的方法名,这里的注解大家也都见过,在学习SQLite的时候对这四个方法用了很多次了,这里是直接使用注解,来标记这个方法。看最后一个注解就是为了告诉它根据表的id来寻找你所要查找的学生信息。

  1. 定义Database:创建一个抽象类来继承RoomDatabase,并使用@Database注解标记。在这个类中,定义数据库的版本和包含的实体和DAO。
@Database(entities = {student.class}, version = 1, exportSchema = false)
public abstract class MyDataBase extends RoomDatabase {private static MyDataBase mInstance;private static final String DATABASE_NAME = "my_db.db";public static synchronized MyDataBase getInstance(Context context) {if (mInstance == null) {mInstance = Room.databaseBuilder(context.getApplicationContext(), MyDataBase.class, DATABASE_NAME).build();}return mInstance;}public abstract StudentDao getStudentDao(); //Room会帮我们自动实现
}
  • @Database(entities = {student.class}, version = 1, exportSchema = false)
    • @Database注解用于定义数据库的配置。它告诉Room这个数据库包含哪些实体(entities),数据库的版本(version),以及其他一些配置项。
    • entities = {student.class}:指定了这个数据库包含的实体类。在这个例子中,它包含student实体。
    • version = 1:指定了数据库的版本号。当数据库结构发生变化时(例如添加、删除或修改实体),需要增加这个版本号。
    • exportSchema = false:指定是否允许Room导出数据库的schema文件。如果设置为true,Room会在编译时生成一个包含数据库schema的文件,这有助于调试和测试。在这个例子中,它被设置为false,意味着不导出schema文件。
  • public abstract class MyDataBase extends RoomDatabase
    • 定义了一个名为MyDataBase的抽象类,它继承自RoomDatabase。这个类将作为数据库的顶层接口,用于创建和管理数据库实例。
  • private static MyDataBase mInstance;
    • 定义了一个静态的MyDataBase实例,用于实现数据库的单例模式。这样可以确保整个应用程序中只有一个数据库实例。
  • private static final String DATABASE_NAME = "my_db.db";
    • 定义了一个常量,指定了数据库文件的名称。在这个例子中,数据库文件将被命名为my_db.db
  • public static synchronized MyDataBase getInstance(Context context)
    • 定义了一个静态方法,用于获取数据库的单例实例。这个方法使用了synchronized关键字来确保线程安全,避免在多线程环境下创建多个数据库实例。
    • context.getApplicationContext():获取应用程序级别的上下文,用于创建数据库实例。
  • if (mInstance == null) { mInstance = Room.databaseBuilder(...).build(); }
    • 如果mInstancenull,则使用Room.databaseBuilder()方法创建一个新的数据库实例。这个方法链式调用了多个配置方法,包括指定数据库类、数据库名称、以及数据库构建的其他配置。
  • public abstract StudentDao getStudentDao();
    • 定义了一个抽象方法,用于获取StudentDao的实例。Room在编译时会为这个方法生成实现代码,这样你就可以在应用程序中通过调用getStudentDao()来获取StudentDao的实例,进而执行数据库操作。
  1. 接下来就可以为按钮注册点击事件,运行程序看数据的更改了
public class MainActivity extends AppCompatActivity {StudentDao studentDao;@Overrideprotected void onCreate(Bundle savedInstanceState) {//......获取按钮,页面的滚动控件buttonAdd.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {student s1 = new student("Jack", 20);student s2 = new student("Rose", 30);new InsertStudentTask(studentDao).execute(s1, s2);}});buttonResearch.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {new GetAllStudentTask(studentDao).execute();}});buttonUpdata.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {student s1 = new student(3,"Tason", 21);new UpdataStudentTask(studentDao).execute(s1);}});buttonDelete.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {student s1 = new student(2);new DeleteStudentTask(studentDao).execute(s1);}});}class DeleteStudentTask extends AsyncTask<student, Void, Void> {private StudentDao studentDao;public DeleteStudentTask(StudentDao studentDao) {this.studentDao = studentDao;}@Overrideprotected Void doInBackground(student... students) {studentDao.deleteStudent(students);return null;}}class UpdataStudentTask extends AsyncTask<student, Void, Void> {private StudentDao studentDao;public UpdataStudentTask(StudentDao studentDao) {this.studentDao = studentDao;}@Overrideprotected Void doInBackground(student... students) {studentDao.updateStudent(students);return null;}}class InsertStudentTask extends AsyncTask<student, Void, Void> {private StudentDao studentDao;public InsertStudentTask(StudentDao studentDao) {this.studentDao = studentDao;}@Overrideprotected Void doInBackground(student... students) {studentDao.insertStudent(students);return null;}}class GetAllStudentTask extends AsyncTask<Void, Void, List<student>> {private StudentDao studentDao;public GetAllStudentTask(StudentDao studentDao) {this.studentDao = studentDao;}@Overrideprotected List<student> doInBackground(Void... voids) {return studentDao.getAllStudent();}@Overrideprotected void onPostExecute(List<student> students) {super.onPostExecute(students);studentRecyclerViewAdapter.setstudent(students);studentRecyclerViewAdapter.notifyDataSetChanged();}}
}

注意我们不在主线程进行数据库的相关操作,

接下来就运行一下:

在这里插入图片描述

当我们进行增加操作,页面没有发生变化,我们点击查询操作,我们在上面的代码当中知道没当按下查询操作就会获取所有的学生信息,并将其展示在页面的滚动控件当中:

在这里插入图片描述

再看看删除和修改操作吧,上面的代码我们将2号学生删除,并修改3号学生的信息,操作后按下查询按钮:
在这里插入图片描述

优化

我们看到数据库的信息已经修改了。每次修改都要进行查询才能看到更新的信息就很麻烦,而且你也不知道到底有没有跟新就要进行查询操作,在之前我们学习了LiveData每当数据变化,就会自动告诉View,使其自动更新,那不就大大优化了程序吗

在这里插入图片描述

官方给出的架构指南,Model当中使用Room访问SQLite的内容。接下来就看看如何使用吧!

1、2、3步是一样的,这里就不多说了,只是对于数据库的操作有稍微的修改,第一次学习写的就不修改了,大家看看吧

@Dao
public interface StudentDao {@Insertvoid insertStudent(student... students);@Deletevoid deleteStudent(student... students);@Updatevoid updateStudent(student... students);@Query("DELETE FROM student")void deleteAllAtudent();@Query("SELECT * FROM student")LiveData<List<student>> getAllStudentsLive();
}
  1. 创建Repository:Repository作为数据层的抽象,封装了数据来源,它可以是一个类,包含了一系列方法来执行数据库操作。这些方法通常会调用DAO中定义的操作,并将结果包装成LiveData或Flow对象,以便ViewModel可以观察数据变化。
public class StudentRepository {private StudentDao studentDao;public StudentRepository(Context context) {MyDataBase dataBase = MyDataBase.getInstance(context);this.studentDao = dataBase.getStudentDao();}//对数据进行添加public void insertStudent(student... students) {new InsertStudentTask(studentDao).execute(students);}class InsertStudentTask extends AsyncTask<student, Void, Void> {private StudentDao studentDao;public InsertStudentTask(StudentDao studentDao) {this.studentDao = studentDao;}@Overrideprotected Void doInBackground(student... students) {studentDao.insertStudent(students);return null;}}//对数据进行修改public void updateStudent(student... students) {new UpdataStudentTask(studentDao).execute(students);}class UpdataStudentTask extends AsyncTask<student, Void, Void> {private StudentDao studentDao;public UpdataStudentTask(StudentDao studentDao) {this.studentDao = studentDao;}@Overrideprotected Void doInBackground(student... students) {studentDao.updateStudent(students);return null;}}//对数据进行删除public void deleteStudent(student... students) {new DeleteStudentTask(studentDao).execute(students);}class DeleteStudentTask extends AsyncTask<student, Void, Void> {private StudentDao studentDao;public DeleteStudentTask(StudentDao studentDao) {this.studentDao = studentDao;}@Overrideprotected Void doInBackground(student... students) {studentDao.deleteStudent(students);return null;}}//对数据进行查找public LiveData<List<student>> getAllStudentsLive() {return studentDao.getAllStudentsLive();}//将所有数据都删除public void deleteAllStudent() {new DeleteAllStudentTask(studentDao).execute();}class DeleteAllStudentTask extends AsyncTask<Void, Void, Void> {private StudentDao studentDao;public DeleteAllStudentTask(StudentDao studentDao) {this.studentDao = studentDao;}@Overrideprotected Void doInBackground(Void... voids) {studentDao.deleteAllAtudent();return null;}}
}
  • 构造函数
    • StudentRepository的构造函数接收一个Context对象,用它来获取数据库的实例。MyDataBase.getInstance(context)是一个单例模式的数据库实例获取方法,确保整个应用中只有一个数据库实例。
  • 异步任务(AsyncTask)
    • InsertStudentTaskUpdataStudentTaskDeleteStudentTaskDeleteAllStudentTask是内部类,它们继承自AsyncTask。这些类用于在后台线程上执行数据库操作,以避免阻塞主线程。
    • AsyncTask已经被标记为过时,推荐使用java.util.concurrent包中的类、Kotlin Coroutines或者其他现代的并发解决方案。
  • doInBackground方法
    • AsyncTaskdoInBackground方法中执行实际的数据库操作。这个方法在后台线程上运行,接收的参数是传递给execute方法的参数。
  • LiveData
    • getAllStudentsLive方法返回一个LiveData对象,它包含了数据库中所有学生数据的列表。LiveData是一个可观察的数据存储器,它具有生命周期感知能力,只会在观察者处于活跃状态时发送数据更新。
  1. 使用ViewModel:ViewModel负责管理UI相关的数据,它可以调用Repository中的方法来获取数据,并根据数据变化更新UI。ViewModel使用LiveData或Flow作为数据的载体,这样当数据发生变化时,UI可以自动更新。
public class StudentViewModel extends AndroidViewModel {//当要使用上下文的时候使用AndroidViewModelprivate StudentRepository repository;public StudentViewModel(@NonNull Application application) {super(application);this.repository = new StudentRepository(application);}public void insertStudent(student... students) {repository.insertStudent(students);}public void deleteStudent(student... students) {repository.deleteStudent(students);}public void deleteAllStudent() {repository.deleteAllStudent();}public void updateStudent(student... students) {repository.updateStudent(students);}public LiveData<List<student>> research() {return repository.getAllStudentsLive();}
}
  1. 观察数据变化:在UI层(如Activity或Fragment),观察ViewModel中的LiveData或Flow对象。当数据发生变化时,UI层会收到通知并更新界面。
studentViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(StudentViewModel.class);
studentViewModel.research().observe(this, new Observer<List<student>>() {@Overridepublic void onChanged(List<student> students) {studentRecyclerViewAdapter.setstudent(students);studentRecyclerViewAdapter.notifyDataSetChanged();}
}); //为其注册监听,到获取到的数据库内容进行变化,就更新UI
buttonAdd.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {student s1 = new student("Jack", 20);student s2 = new student("Rose", 30);studentViewModel.insertStudent(s1,s2);}
}); //只展示一个按钮的点击事件,其他的你也肯定会写了

接下来就运行程序看看吧!

我们进行了增加删除修改操作,页面直接自己更新了,带来了很大的便利

在这里插入图片描述

按下清空页面就没有了,数据库就被清空了!

本篇内容介绍了数据库的基本操作,感谢你的阅读!

文章到这里就结束了!

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

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

相关文章

快速搭建最简单的前端项目vue+View UI Plus

1 引言 ‌‌Vue是一套用于构建Web前端界面的渐进式JavaScript框架。‌‌它以其易学易用、性能出色、灵活多变而深受开发者喜爱&#xff0c;并且与其他前端框架&#xff08;如‌React和‌Angular&#xff09;相比&#xff0c;在国内市场上受到了广泛的认可和使用。点击进入官方…

AI问答-HTTP:理解 Content-Disposition

一、简介 Content-Disposition是HTTP协议中的一个响应头字段&#xff0c;它主要用于指示如何处理响应的内容以及如何呈现给用户。这个字段是MIME协议类型的扩展&#xff0c;用于指导MIME用户代理&#xff08;如浏览器&#xff09;如何显示附加的文件。Content-Disposition的主…

yolo自动化项目实例解析(二)ui页面整理

我们在上一章整理main.py 的if __name__ __main__: 内容还留下面这一段&#xff0c; app QApplication(sys.argv) # 初始化Qt应用ratio screen_width / 2560 # 分辨率比例# 设置全局字体大小# 计算字体大小base_font_size 13# 基准字体大小&#xff0c;适合1920*1080分辨…

【数据结构与算法 | 灵神题单 | 合并链表篇】力扣2, 21, 445, 2816

1. 力扣2&#xff1a;两数相加 1.1 题目&#xff1a; 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储 一位 数字。 请你将两个数相加&#xff0c;并以相同形式返回一个表示和的链表。 你可…

vue3 +百度地图 实现 地点检索,输入联想,经纬度,逆地理编码,创建标记,label等

由于百度地图文档确实有点欠缺&#xff0c;在这里记录一下 vue3 百度地图&#xff08;js api 3.0&#xff09;实现效果如下实现方式注意事项 vue3 百度地图&#xff08;js api 3.0&#xff09; 需求&#xff1a; 地图弹框组件&#xff0c;可以搜索地图点&#xff0c;输入联想…

尚品汇-订单拆单、支付宝关闭交易、关闭过期订单整合(五十)

目录&#xff1a; &#xff08;1&#xff09;拆单接口 &#xff08;2&#xff09;取消订单业务补充关闭支付记录 &#xff08;3&#xff09;支付宝关闭交易 &#xff08;4&#xff09;查询支付交易记录 &#xff08;5&#xff09;PaymentFeignClient 远程接口 &#xff08…

JMeter脚本开发

环境部署 Ubuntu系统 切换到root用户 sudo su 安装上传下载的命令 apt install lrzsz 切换文件目录 cd / 创建文件目录 mkdir java 切换到Java文件夹下 cd java 输入rz回车 选择jdk Linux文件上传 解压安装包 tar -zxvf jdktab键 新建数据库 运行sql文件 选择sql文件即…

基于51单片机的电饭锅控制系统proteus仿真

地址&#xff1a; https://pan.baidu.com/s/1CGyg6uPhFI0MeaBWwe_HAg 提取码&#xff1a;1234 仿真图&#xff1a; 芯片/模块的特点&#xff1a; AT89C52/AT89C51简介&#xff1a; AT89C52/AT89C51是一款经典的8位单片机&#xff0c;是意法半导体&#xff08;STMicroelectro…

RedisTemplate操作ZSet的API

文章目录 ⛄概述⛄常见命令有⛄RedisTemplate API❄️❄️ 向集合中插入元素&#xff0c;并设置分数❄️❄️向集合中插入多个元素,并设置分数❄️❄️按照排名先后(从小到大)打印指定区间内的元素, -1为打印全部❄️❄️获得指定元素的分数❄️❄️返回集合内的成员个数❄️❄…

LineageOS刷机教程

版权归作者所有&#xff0c;如有转发&#xff0c;请注明文章出处&#xff1a;https://cyrus-studio.github.io/blog/ LineageOS 是一个基于 Android 开源项目&#xff08;AOSP&#xff09;的开源操作系统&#xff0c;主要由社区开发者维护。它起源于 CyanogenMod 项目&#xff…

10年Python程序员教你多平台采集10万+电商数据【附实例】

10万级电商数据采集需要注意什么&#xff1f; 在进行10万级电商数据采集时&#xff0c;有许多关键因素需要注意&#xff1a; 1. 采集平台覆盖&#xff1a;确保可以覆盖主流的电商平台&#xff0c;如淘宝、天猫、京东、拼多多等。 2. 数据字段覆盖&#xff1a;检查是否可以对平…

go 笔记

数据结构与 方法&#xff08;增删改查&#xff09; 安装goland,注意版本是2024.1.1&#xff0c;不是2024.2.1&#xff0c;软件下载地址也在链接中提供了 ‘go’ 不是内部或外部命令&#xff0c;也不是可运行的程序 或批处理文件。 在 Windows 搜索栏中输入“环境变量”&#…

架构理论碰撞:对比TOGAF、Zachman、DODAF和FEAF等主流架构框架

信息架构框架对比分析&#xff1a;选择适合企业的最佳方案 在企业数字化转型过程中&#xff0c;信息架构的设计与实施至关重要。成功的信息架构能够有效地支持业务流程优化&#xff0c;提升数据管理效率&#xff0c;推动技术创新。然而&#xff0c;不同的信息架构框架各有其独…

COMTRADE binary数据文件解析

一、COMTRADE 二进制文件的解析需要用到cfg文件中的配置信息&#xff0c;以及dat文件中的数据。 二、cfg文件 1、cfg文件整体配置 2、cfg文件实例 厂站名&#xff0c;记录装置&#xff0c;COMTRADE标准版本年号 SMARTSTATION,IED123,2013 总通道数&#xff0c;模拟通道编号&…

记录word转xml文件踩坑

word文件另存为xml文件后&#xff0c;xml文件乱码 解决方法&#xff1a; 1.用word打开.docx文件 2.另存为xml文件 3.点击工具 -> Web选项 -> 编码&#xff0c;选择UTF-8 4.点击确定 5.使用notpad打开xml文件 6.使用xml tool进行xml格式化即可。

uniapp小程序,使用腾讯地图获取定位

本篇文章分享一下在实际开发小程序时遇到的需要获取用户当前位置的问题&#xff0c;在小程序开发过程中经常使用到获取定位功能。uniapp官方也提供了相应的API供我们使用。 官网地址&#xff1a;uni.getLocation(OBJECT)) 官网获取位置的详细介绍这里就不再讲述了&#xff0c;大…

安宝特方案 | 医疗AR眼镜,重新定义远程会诊体验

【AR眼镜&#xff1a;重新定义远程会诊体验】 在快速发展的医疗领域&#xff0c;安宝特医疗AR眼镜以其尖端技术和创新功能&#xff0c;引领远程会诊的未来&#xff0c;致力于为为医生和患者带来更高效、精准和无缝的医疗体验。 探索安宝特医疗AR眼镜如何在医疗行业中引领新风潮…

视频推拉流/直播点播EasyDSS平台安装失败并报错“install mediaserver error”是什么原因?

TSINGSEE青犀视频推拉流/直播点播EasyDSS平台支持音视频采集、视频推拉流、播放H.265编码视频、存储、分发等视频能力服务&#xff0c;在应用场景中可实现视频直播、点播、转码、管理、录像、检索、时移回看等。此外&#xff0c;平台还支持用户自行上传视频文件&#xff0c;也可…

MongoDB日志级别

日志 查看当前的日志级别 根据你提供的 MongoDB 命令结果&#xff0c;命令 db.adminCommand({ getParameter: "logComponentVerbosity" }) 返回了 "ok" : 0&#xff0c;这意味着命令执行失败&#xff0c;没有成功获取到日志级别的配置信息。错误信息 &quo…

【JAVA入门】Day45 - 压缩流 / 解压缩流

【JAVA入门】Day45 - 压缩流 / 解压缩流 文章目录 【JAVA入门】Day45 - 压缩流 / 解压缩流一、解压缩流二、压缩流 在文件传输过程中&#xff0c;文件体积比较大&#xff0c;传输较慢&#xff0c;因此我们发明了一种方法&#xff0c;把文件里的数据压缩到一种压缩文件中&#x…