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

【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);}
}); //只展示一个按钮的点击事件,其他的你也肯定会写了

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

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

在这里插入图片描述

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

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

文章到这里就结束了!

相关文章:

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

引言 在Android开发中&#xff0c;数据持久化是一个不可或缺的部分。随着应用的复杂度增加&#xff0c;选择合适的数据存储方式变得尤为重要。Room数据库作为Android Jetpack架构组件之一&#xff0c;提供了一种抽象层&#xff0c;使得开发者能够以更简洁、更安全的方式操作SQ…...

「数组」堆排序 / 大根堆优化(C++)

目录 概述 核心概念&#xff1a;堆 堆结构 数组存堆 思路 算法过程 up() down() Code 优化方案 大根堆优化 Code(pro) 复杂度 总结 概述 在「数组」快速排序 / 随机值优化|小区间插入优化&#xff08;C&#xff09;中&#xff0c;我们介绍了三种基本排序中的冒泡…...

Edegex Foundry docker和源码安装

edgex文档下载 https://github.com/edgexfoundry/edgex-docs/branches/all 在线文档查看 首先要安装python3环境 然后后安装 打开超级终端 #pip3 install mkdocs #mkdocs serve 在浏览器中输入 http://127.0.0.1:8000/edgex-docs/2.3/ 即可打开在线文档 edgex入门可以参考…...

阿里P8和P9级别有何要求

阿里巴巴的P8和P9级别&#xff0c;代表着公司的资深技术专家或管理者岗位&#xff0c;要求候选人具有丰富的职业经历、深厚的技术能力以及出色的领导力。以下是对P8和P9级别的要求、考察点以及准备建议的详细分析。 P8 级别要求 1. 职业经历&#xff1a; 8年以上的工作经验&a…...

【目标检测数据集】锯子数据集1107张VOC+YOLO格式

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1107 标注数量(xml文件个数)&#xff1a;1107 标注数量(txt文件个数)&#xff1a;1107 标注…...

移动产业处理器接口(MIPI)协议是什么?

未来汽车的宏伟愿景备受瞩目&#xff0c;特别是驱动这些汽车的技术更是成为焦点。如今&#xff0c;传感器对于汽车视觉和安全技术的下一阶段至关重要&#xff0c;因为驾驶员和乘客都依赖于它们。这些传感器能够支持众多应用&#xff0c;这些应用往往基于人工智能&#xff08;AI…...

OpenAI o1:隐含在训练与推理间的动态泛化与流形分布

随着OpenAI o1发布&#xff0c;进一步激发了产业与学术各界对AGI的期待以及new scaling law下的探索热情&#xff0c;也看到来自社区和专业机构对o1的阐释&#xff0c;但总感觉还差点什么&#xff0c;因此决定以自己的角度分篇幅梳理下&#xff0c;并分享给大伙&#xff1a; O…...

沉浸式体验和评测Meta最新超级大语言模型405B

2024年7月23日&#xff0c; 亚马逊云科技的AI模型托管平台Amazon Bedrock正式上线了Meta推出的超级参数量大语言模型 - Llama 3.1模型&#xff0c;小李哥也迫不及待去体验和试用了该模型&#xff0c;那这么多参数量的AI模型究竟强在哪里呢&#xff1f;Llama 3.1模型是Meta&…...

Python 课程10-单元测试

前言 在现代软件开发中&#xff0c;单元测试 已成为一种必不可少的实践。通过测试&#xff0c;我们可以确保每个功能模块在开发和修改过程中按预期工作&#xff0c;从而减少软件缺陷&#xff0c;提高代码质量。而测试驱动开发&#xff08;TDD&#xff09; 则进一步将测试作为开…...

【嵌入式硬件开发基础】Arduino板常用外设及应用:MPU6050空间运动传感器(简介,类库函数,卡尔曼滤波),继电器(原理介绍,含应用实例/代码)

当一个人不能拥有的时候,他唯一能做的便是不要忘记。 🎯作者主页: 追光者♂🔥 🌸个人简介: 📝[1] CSDN 博客专家📝 🏆[2] 人工智能领域优质创作者🏆 🌟[3] 2022年度博客之星人工智能领域TOP4🌟 🌿[4] 2023年城市之星领跑者TOP1(哈尔滨…...

Pandas Series对象创建,属性,索引及运算详解

目录 Series对象创建 实例化参数 index参数 选用array-like创建Series对象 list ndarray 显示索引与隐式索引 选用dict创建Series对象 不指定索引 指定索引 选用标量创建Series对象 使用标量创建的广播机制 Series属性 name size shape index values Series索…...

优化算法(一)—遗传算法(Genetic Algorithm)附MATLAB程序

遗传算法&#xff08;Genetic Algorithm, GA&#xff09;是一种启发式搜索算法&#xff0c;用于寻找复杂优化问题的近似解。它模拟了自然选择和遗传学中的进化过程&#xff0c;主要用于解决那些传统算法难以处理的问题。 遗传算法的基本步骤&#xff1a; 初始化种群&#xff0…...

高等数学 2.3 高阶导数

一般地&#xff0c;函数 y f ( x ) y f(x) yf(x) 的导数 y ′ f ′ ( x ) y\ f\ (x) y ′f ′(x) 仍然是 x x x 的函数。我们把 y ′ f ′ ( x ) y\ f\ (x) y ′f ′(x) 的导数叫做函数 y f ( x ) y f(x) yf(x) 的二阶导数&#xff0c;记作 y ′ ′ y\ y ′…...

app抓包 chrome://inspect/#devices

一、前言&#xff1a; 1.首先不支持flutter框架&#xff0c;可支持ionic、taro 2.初次需要翻墙 3.app为debug包&#xff0c;非release 二、具体步骤 1.谷歌浏览器地址&#xff1a;chrome://inspect/#devices qq浏览器地址&#xff1a;qqbrowser://inspect/#devi…...

SAP自动化-ME12批量更新某行价格

Python源码 #-Begin-----------------------------------------------------------------#-Includes-------------------------------------------------------------- import sys, win32com.client import os#-Sub Main----------------------------------------------------…...

数据库系统 第58节 概述源码示例

深入探讨数据库技术&#xff0c;我们将通过具体的源代码示例来进一步解释数据库分区、复制、集群和镜像等高级特性。 数据库分区的源代码示例 哈希分区 在PostgreSQL中&#xff0c;可以使用哈希分区来创建一个分区表&#xff1a; CREATE TABLE measurements (city_id …...

软件设计师——程序设计语言

目录 低级语言和高级语言 编译程序和解释程序 正规式&#xff0c;词法分析的一个工具 有限自动机 ​编辑 上下文无关法 ​编辑 中后缀表示法 杂题 ​编辑 低级语言和高级语言 编译程序和解释程序 计算机只能理解由0、1序列构成的机器语言&#xff0c;因此高级程序设计…...

【在Linux世界中追寻伟大的One Piece】五种IO模型和阻塞IO

目录 1 -> 五种IO模型 1.1 -> 阻塞IO(Blocking IO) 1.2 -> 非阻塞IO(Non-blocking IO) 1.3 -> 信号驱动IO(Signal-Driven IO) 1.4 -> IO多路转接(IO Multiplexing) 1.5 -> 异步IO(Asynchronous IO) 2 -> 高级IO概念 2.1 -> 同步通信VS异步通信…...

nginx实现权重机制(nginx基础配置二)

在上一篇文章中我们已经完成了对轮询机制的测试&#xff0c;详情请看轮询机制。 接下来我们进行权重机制的测试 一、conf配置 upstream backServer{ server 127.0.0.1:8080 weight2; server 127.0.0.1:8081 weight1; } server { listen 80; server_name upstream.boyatop.cn…...

华为的仓颉和ArkTS这两门语言有什么区别

先贴下官网&#xff1a; ArkTs官网 仓颉官网 ArkTS的官网介绍说&#xff0c;ArkTS是TypeScript的进一步强化版本&#xff0c;简单来说就是包含了TS的风格&#xff0c;但是做了一些改进。 了解TypeScript的朋友都应该知道&#xff0c;其实TypeScript就是JavaScript的改进版本&…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

算法笔记2

1.字符串拼接最好用StringBuilder&#xff0c;不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

基于IDIG-GAN的小样本电机轴承故障诊断

目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) ​梯度归一化(Gradient Normalization)​​ (2) ​判别器梯度间隙正则化(Discriminator Gradient Gap Regularization)​​ (3) ​自注意力机制(Self-Attention)​​ 3. 完整损失函数 二…...

STM32HAL库USART源代码解析及应用

STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...

STM32---外部32.768K晶振(LSE)无法起振问题

晶振是否起振主要就检查两个1、晶振与MCU是否兼容&#xff1b;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容&#xff08;CL&#xff09;与匹配电容&#xff08;CL1、CL2&#xff09;的关系 2. 如何选择 CL1 和 CL…...

深入解析光敏传感技术:嵌入式仿真平台如何重塑电子工程教学

一、光敏传感技术的物理本质与系统级实现挑战 光敏电阻作为经典的光电传感器件&#xff0c;其工作原理根植于半导体材料的光电导效应。当入射光子能量超过材料带隙宽度时&#xff0c;价带电子受激发跃迁至导带&#xff0c;形成电子-空穴对&#xff0c;导致材料电导率显著提升。…...

基于stm32F10x 系列微控制器的智能电子琴(附完整项目源码、详细接线及讲解视频)

注&#xff1a;文章末尾网盘链接中自取成品使用演示视频、项目源码、项目文档 所用硬件&#xff1a;STM32F103C8T6、无源蜂鸣器、44矩阵键盘、flash存储模块、OLED显示屏、RGB三色灯、面包板、杜邦线、usb转ttl串口 stm32f103c8t6 面包板 …...

SQL注入篇-sqlmap的配置和使用

在之前的皮卡丘靶场第五期SQL注入的内容中我们谈到了sqlmap&#xff0c;但是由于很多朋友看不了解命令行格式&#xff0c;所以是纯手动获取数据库信息的 接下来我们就用sqlmap来进行皮卡丘靶场的sql注入学习&#xff0c;链接&#xff1a;https://wwhc.lanzoue.com/ifJY32ybh6vc…...