Android数据库之SharedPreferences、SQLite、Room

news2024/12/23 6:10:50

文章目录

    • 一、SharedPreferences
    • 二、SQLite
    • 三、Room
      • 使用Room进行增删改查
      • Room数据库升级

一、SharedPreferences

要想使用SharePreferences来存储数据,首先需要获取到SharedPreferences对象。Android中提供了三种方法用于得到SharedPreferences对象
1.Context类中的getSharedPreferences()方法
第一个参数用于指定SharedPreferences文件名称,如果指定的文件不存在则会创建一个。第二个参数用于指定操作模式
MODE_PRIVATE表示只有当前的应用程序才可以对这个SharedPreferences文件进行读写。
例如:

//获取对象
SharePreferences.Editor editor=getSharedPreferences("data",MODE_PRIVATE).edit();
//进行存储
editor.putString("name","Tom);
editor.putInt("age",28);
editor.apply();
//读取数据
SharedPreference pref=getSharedPreferences("data",MODE_PRIVATE);
/*
通过getSharedPreferences()方法得到了SharedPreferences对象,然后再调用getString(),getInt()获取前面所存储的姓名和年龄,如果没有找到相应的值,就会使用方法中传入的默认值来代替
*/
String name=pref.getString("name","");
int age=pref.getInt("age",0);

如果存储的key值一样,后存储的会覆盖前存储的,比如:
pref.getString(“sex”,“女”);
pref.getString(“sex”,“男”);
最后shared_prefs文件夹文件只会显示

 <map>
    <string name="sex"></string>
</map>

2.Activity类中的getPreferences()方法
这个方法和Context类中的getSharedPreferences()方法很相似,不过它只接受一个操作模式,这个方法会自动将当前活动的类名作为SharedPreferences的文件名
3.PreferenceManager类中的getDefaultSharedPreferences()方法
这是一个静态方法,他接收一个Context参数,并自动使用当前应用程序的包作为前缀来命名SharedPreferences文件
例如:

//获取对象
         SharedPreferences.Editor editor = PreferenceManager.
                                     getDefaultSharedPreferences(WeatherActivity.this)
                                    .edit();
                            //添加数据
                            editor.putString("weather", responseText);
                            //将添加的数据提交,从而完成数据存储操作
                            editor.apply(); 
  //得到SharedPreferences对象
        SharedPreferences prefs = PreferenceManager.
                getDefaultSharedPreferences(this);
        //读取数据
        String weatherString = prefs.getString("weather", null);
        String bingPic=prefs.getString("bing_pic",null);

向SharedPreferences文件存储数据分为三步:
1.调用SharedPreferences对象的edit()方法来获取一个SharedPreferences.Editor对象
2.向SharedPreferences.Editor对象添加数据,比如添加一个布尔型数据就使用putBoolean()方法,添加一个字符串则使用putString()方法,以此类推
3.调用apply()方法将添加的数据提交,从而完成数据存储操作

二、SQLite

1.建立数据库
Android提供了一个SQLiteOpenHelper帮助类,借助这个类我们可以对数据库进行创建和升级。SQLiteOpenHelper是一个抽象类,我们要想使用他的话就需要创建一个自己的帮助类去继承他。SQLiteOpenHelper中有两个抽象方法,分别是onCreate和onUpgrade,我们必须在自己的帮助类中重写这两个方法,分别在这两个方法去实现创建、升级数据库。
SQLiteOpenHelper中有两个构造方法重写,我们一般使用参数少的那个,这个构造方法中接受4个参数,第一个是Context,第二个是数据库名,第三个是查询数据库时返回的一个自定义的Cursor,一般都是传入null。第四个参数表示当前数据库的版本号,可用于对数据库进行升级操作。构件出SQLiteOpenHelper的实例之后,再调用它的getReadableDatabase()或getWrittableDatabase()方法就能创建数据库了
SQLiteDatabase的execSQL()方法去执行建表语句

public class MyDatabaseHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK = "create table Book(" + "id integer primary key autoincrement,"
            + "author text, " + "price real, " + "pages integer, " + "name text)";
 private Context mContext;

    public MyDatabaseHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        mContext = context;
    }
 //表如果已经存在就不会在执行onCreate()方法
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);//这是创建表的语句,又因为在onCreate方法中,所以会
        //在创建数据库链接getWritableDatabase的时候调用,这样的话就能创建表
        Toast.makeText(mContext, "Create succeeded", Toast.LENGTH_LONG).show();
}
 @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
    
}
public class MainActivity extends AppCompatActivity {
        //implements View.OnClickListener{
    private MyDatabaseHelper dbHelper;
    private Button createDatabase,addData,updateData,delete,query;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //创建数据库
        dbHelper=new MyDatabaseHelper(this,"BookStore.db",null,1);
        //当版本从1变到2的时候就会让onUpgrade()方法执行
      // init();
        createDatabase=(Button) findViewById(R.id.ceate_database);
        addData=(Button) findViewById(R.id.Add_data);
        updateData=(Button) findViewById(R.id.update_data);
        delete=(Button) findViewById(R.id.delete_data);
        query=(Button) findViewById(R.id.query_data);
        createDatabase.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dbHelper.getWritableDatabase();
            }
        });
        }

2.升级数据库
onUpgrade()方法是用于对数据库进行升级的。目前就已经有一张Book表用于存放书的各种详细数据,如果我们想再添加一张Category表用于记录图书的分类,该怎么做呢?在之前的代码中添加以下语句

 public static final String CREATE_CATEGORY = "create table Category(" + "id integer primary key autoincrement,"
            + "category_name text, " + "category_code integer)";
 //表如果已经存在就不会在执行onCreate()方法
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);//这是创建表的语句,又因为在onCreate方法中,所以会
        //在创建数据库的时候调用,这样的话创建的数据库的同时也能创建表
        db.execSQL(CREATE_CATEGORY);
        Toast.makeText(mContext, "Create succeeded", Toast.LENGTH_LONG).show();
    }
 @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        //如果存在两张表中的一个就都删除掉,,然后再调用onCreate方法重新创建
        db.execSQL("drop table if exists Book");//如果存在Book表删掉
        db.execSQL("drop table if exists Category");//如果存在Category表删掉
        onCreate(db);
    }

因为之前的BookStore.db数据库已经存在了,之后不管怎么点击createDatabase按钮,MyDatabaseHelper的onCreate()方法都不会再执行,因此填加的新表也无法创建了。
此时我们可以在onUpgrade方法里执行两条DROP语句,如果发现创建了Book表或者Category表。就将这张表删除,然后再调用onCreate方法重新创建。
==如何让onUpgrade方法得到执行?==直接将SQLiteOpenHelper的构造方法里接受的第四个参数。他表示当前数据库的版本号,之前我们传入的是1,现在传入一个比1大的数(一般要比原来的大),就可以让onUpgrade()方法得到执行。如dbHelper=new MyDatabaseHelper(this,“BookStore.db”,null,2);
3.添加数据库
SQLiteDatabase中提供了一个insert()方法,这个方法就是专门用于添加数据。他接收3个参数,第一个参数是表名,第二个参数是用于未指定添加数据的情况下给某些可为空的列自动赋值为NULL,一般用不到,直接传入null即可。。第三个参数是一个ContentValues对象,它提供了一系列的put()方法重载,用于向contentValues中添加数据。

  addData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db1= dbHelper.getWritableDatabase();
                ContentValues values=new ContentValues();
                //开始组装第一天数据
                values.put("name","The Da Vinci Code");
                values.put("author","Dan Brown");
                values.put("pages",454);
                values.put("price",16.69);
                db1.insert("Book",null,values);
                values.clear();
                //开始组装第二条数据
                values.put("name","The Lost Symbol");
                values.put("author","Dan Brown");
                values.put("pages",510);
                values.put("price",19.95);
                db1.insert("Book",null,values);
            }
        });

4.更新数据库
update()方法用于対数据进行更新。这个方法接收4个参数,第一个是表名,在这里是指定去更新哪张表里的数据。第二个参数是ContentValues对象,用于把更新数据在这里组装进去。第三、第四个参数用于约束更新某一行或某几行中的数据,不知道的话默认是更新所有行

 updateData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase  db2 = dbHelper.getWritableDatabase();
             ContentValues   values = new ContentValues();
                values.put("price",10.99);
                db2.update("Book",values,"name = ?",new String[]{"The Da Vinci Code"});
            }
        });

5.删除数据库
delete()方法专门用来删除数据,这个方法接收三个参数,第一个参数是表名,第二,第三个参数用于约束删除某一行或某几行数据,不指定的话就是默认删除所有行。

 delete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase  db3=dbHelper.getWritableDatabase();
                db3.delete("Book","pages>?",new String[]{"500"});
                db3.delete("Book","author=?",new String[]{"Dan Brown"});
            }
        });

6.查询数据库
query()方法用于对数据库进行查询。最短的一个方法重载需要传入7个参数。第一个参数表名,第二个参数指定去查询哪几列,如果不指定则默认查询所有列。第三,四个参数用于约束查询某一行或某几行的数据,不指定则默认查询所有行,第五个参数用于指定需要去group by的列,不指定则表示不对查询结果进行group by操作。第六个参数用于对group by之后的数据进行进一步的过滤,不指定则表示不进行过滤。第七个参数用于对查询结果的排序方式,不指定则表示使用默认的排序方式。
cursor.moveToFirst()将指针指向第一个数据的第一行数据。然后进入循环去遍历

 query.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase  db=dbHelper.getWritableDatabase();
                //查询表中所有的数据
                Cursor cursor= db.query("Book", null, null, null, null, null, null);
                if(cursor.moveToFirst())
                {
                    do {
                        //遍历Cursor对象,取出数据并打印
                        String name=cursor.getString(cursor.getColumnIndex("name"));
                        String author=cursor.getString(cursor.getColumnIndex("author"));
                        int pages=cursor.getInt(cursor.getColumnIndex("pages"));
                        double price=cursor.getDouble(cursor.getColumnIndex("price"));
                        Log.d("MainActivity","book name is"+name);
                        Log.d("MainActivity","book author is"+author);
                        Log.d("MainActivity","book pages is"+pages);
                        Log.d("MainActivity","book price is"+price);
                    }while (cursor.moveToNext());
                }
                cursor.close();

            }
        });

//遍历方法1:
if (cursor.moveToFirst()) {
do{
//取出数据,调用cursor.getInt/getString等方法
}while(cusor.moveToNext());
}

//遍历方法2:
if(cusor != null){
while(cursor.moveToNext()) {
//读取数据
}
}

三、Room

使用Room进行增删改查

通过下面这三个就可以完成对数据库的基本操作:
Room的整体结构,它主要由Entity、Dao和Database这3部分组成。

  • Entity。用于定义封装实际数据的实体类,每个实体类都会在数据库中有一张对应的表,并且表中的列是根据实体类中的字段自动生成的。
  • Dao。Dao是数据访问对象的意思,通常会在这里对数据库的各项操作进行封装,在实际编程的时候,逻辑层就不需要和底层数据库打交道了,直接和Dao层进行交互。
  • Database。用户定义数据库中的关键信息,包括数据库的版本号,包括哪些实体类以及提供Dao层的访问实例。

在这里插入图片描述
新建一个User实体类

@Entity
data class User(var firstName:String,var lastName:String,var age:Int){
    @PrimaryKey(autoGenerate = true)
    var id:Long=0
}

这里我们在User的类名上使用@Entity注解,将它声明成了一个实体类,然后在User类中添加了一个id字段,并使用@PrimaryKey注解将它设为了主键,再把autoGenerate参数指定为true,使得主键的值是自动生成的。
接下来定义Dao,因为所有访问数据库的操作都在这里封装的。
访问数据库的操作无非就是增删改查这4种,但是业务需求千变万化。而Dao要做的事情就是覆盖所有的业务需求,使得业务永远只需要与Dao层进行交互,而不必和底层的数据库打交道。
新建一个UserDao接口

@Dao
interface UserDao {

    @Insert
    fun insertUser(user: User):Long

    @Update
    fun updateUser(newUser: User)

    @Query("select * from User")
    fun loadAllUsers():List<User>

    @Query("select * from User where age > :age")
    fun loadUsersOlderThan(age:Int):List<User>

    @Delete
    fun  deleteUser(user:User)

    @Query("delete from User where lastName=:lastName")
    fun deleteUserByLastName(lastName:String):Int
}

UserDao 接口的上面使用了一个@Dao注解,这样Room才能将它识别成一个Dao。UserDao的内部就是根据业务需求对各种数据库操作进行的封装。数据库操作通常有增删改查4种,因此Room也提供了@Insert,@Delete, @Update ,@Query这4种相应的注解
insertUser()方法上面使用了@Insert注解,表示会将参数中传入的User对象插入数据库中,插入完成后还会将自动生成的主键id值返回。updateUser()方法上面使用了@Update注解,表示会将参数中传入的User对象更新到数据库当中。deleteUser()方法上面使用了@Delete注解,表示会将参数传入的User对象从数据库中删除。以上几种数据库操作都是直接使用注解标识,不需要编写SQL语句。
如果想要从数据库查询数据,或者使用非实体类参数来增删改查数据,就必须编写SQL语句了。比如loadAllUsers()方法,如果只使用一个@Query注解,Room将无法知道我们想要查询哪些数据。我们还可以将方法中传入的参数指定到SQL语句当中,比如loadUsersOlderThan()方法就可以查询所有年龄大于指定参数的用户。而如果使用非实体类的参数来增删改数据,也必须编写SQL语句,只能使用@Query注解,参考deleteUserByLastName()方法的写法。
定义Database。只需定义好3个部分的内容:数据库版本号、包含哪些实体类,以及提供Dao层的访问实例。新建一个AppDatabase.kt文件,代码如下:

@Database(version = 1,entities = [User::class])
abstract class AppDatabase: RoomDatabase() {
    abstract fun userDao():UserDao
    
    companion object{
        private var instance:AppDatabase?=null
        
        fun  getDatabase(context: Context):AppDatabase{
        //instance不为空直接执行let函数返回instance,为空则不执行let函数
            instance?.let { 
                return it
            }
            return Room.databaseBuilder(context.applicationContext,
            AppDatabase::class.java,"app_database"
                ).build().apply { instance=this }
                //apply函数中是构建AppDatabase的实例的上下文,会自动返回调用对象本身
        }
    }
}

我们在AppDatabase类的头部使用了@Database注解,并在注解中声明了数据库的版本号以及包含哪些实体类,多个实体类之间用逗号隔开。
AppDatabase类必须继承自RoomDatabase类,并且一定要使用abstract关键字将它声明成抽象类,然后提供相应的抽象方法,用于获取之前编写的Dao的实例,比如这里提供的userDao()方法。这里只需声明,具体的实现由Room在底层自动完成的(不用自己去实现这个抽象方法)。
接着在companion object结构体中编写了一个单例模式,因为原则上全局应该只存在一份AppDatabase实例。这里使用了instance变量来缓存AppDatabase的实例,然后再getDatabase()方法中判断:如果instance变量不为空就直接返回,否则就调用Room.databaseBuilder()方法来构建一个AppDatabase实例。databaseBuilder()方法接收3个参数,第一个参数是applicationContext,避免出现内存泄露。第二个参数是AppDatabase的Class类型,第三个参数是数据库名。最后调用build()方法完成构建,并将创建出来的实例赋值给instance变量,然后返回当前实例。
activity_main.xml代码:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/addDataBtn"
        android:layout_gravity="center_horizontal"
        android:text="Add Data"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/updateDataBtn"
        android:layout_gravity="center_horizontal"
        android:text="Update Data"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/deleteDataBtn"
        android:layout_gravity="center_horizontal"
        android:text="Delete Data"
        />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/queryDataBtn"
        android:layout_gravity="center_horizontal"
        android:text="Query Data"
        />
</LinearLayout>

MainActivity代码:

class MainActivity : AppCompatActivity() {
  private val TAG:String="MainActivity"
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        var userDao=AppDatabase.getDatabase(this).userDao()
        val user1=User("Tom","Brandy",40)
        val user2=User("Tom","Hanks",63)
        addDataBtn.setOnClickListener {
            thread {
                user1.id=userDao.insertUser(user1)
                user2.id=userDao.insertUser(user2)
            }
        }
        updateDataBtn.setOnClickListener {
            thread {
                user1.age=2
                userDao.updateUser(user1)
            }
        }
        deleteDataBtn.setOnClickListener {
            thread {
                userDao.deleteUserByLastName("Hanks")
            }
        }
        queryDataBtn.setOnClickListener {
            thread {
                for(user in userDao.loadAllUsers()){
                    Log.d(TAG, user.toString())
                }
            }
        }
     }
 }

点击"Add Data"按钮,再点击"Query Data"按钮
在这里插入图片描述
可以看到两条用户数据添加进来了
点击"Update Data"按钮,再重新点击"Query Data"按钮
在这里插入图片描述
可以看到,第一条数据中用户的年龄被成功修改成了2岁。
点击"Delete Data"按钮,再重新点击"Query Data"按钮
在这里插入图片描述
只剩下一条数据了

Room数据库升级

如果你处于开发测试阶段,Room提供了一个简单粗暴的方法,如下所示:

Room.databaseBuilder(context.applicationContext,
            AppDatabase::class.java,"app_database"
 ).fallbackToDestructiveMigration().build()

在构建AppDatabase实例的时候,加入一个fallbackToDestructiveMigration()方法。这样只要数据库进行了升级,Room就会将之前的数据库销毁,然后再重新创建,随之而来的副作用就是将之前数据库中的所有数据就全部丢失了。
接下来是Room数据库升级的正规写法:
随着业务升级,现在我们打算在数据库添加一张Book表,那么首先就是创建一个Book的实体类,如下所示:

@Entity
data class Book (var name:String,var pages:Int){
    @PrimaryKey(autoGenerate = true)
    var id:Long=0
}

然后创建一个BookDao接口

@Dao
interface BookDao {
     
    @Insert
    fun insertBook(book:Book):Long
    
    @Query("select * from Book")
    fun loadAllBooks():List<Book>
    
}

接下来修改AppDatabase中的代码,在里面编写数据库升级的逻辑

@Database(version = 2,entities = [User::class,Book::class])
abstract class AppDatabase: RoomDatabase() {
    abstract fun userDao():UserDao
    
    abstract fun bookDao():BookDao

    companion object{
        
        val MIGRATION_1_2=object :Migration(1,2){
            override fun migrate(database: SupportSQLiteDatabase) {
                database.execSQL("create table Book(id integer primary key autoincrement not null,name text not null,pages integer not null)")
            }
        }   
        private var instance:AppDatabase?=null

        fun  getDatabase(context: Context):AppDatabase{
            instance?.let {
                return it
            }
            return Room.databaseBuilder(context.applicationContext,
            AppDatabase::class.java,"app_database"
                ).addMigrations(MIGRATION_1_2).build().apply { instance=this }//apply函数中是构建AppDatabase的实例的上下问,
        // 会自动返回调用对象本身
        }
    }
}

首先在@Database注解中,我们将版本号升级成了2,并将Book类添加到了实体类声明中,然后又提供了一个bookDao()方法用于获取BookDao的实例。
在 companion object结构体中,我们实现了一个Migration的匿名类,并传入了1和2这两个参数,表示当数据库从1升级到2的时候就执行这个匿名类的时候就执行这个匿名类的升级逻辑。由于我们新增一张Book表,所以需要在migrate()方法中编写相应的建表语句。Book表的建表语句和Book实体类中声明的结构要完全一致。
最后在构建AppDatabase实例的时候,加入一个addMigrations()方法,并把MIGRATION_1_2传入
现在当我们进行任何数据库操作时,Room就会自动根据当前数据库的版本号执行这些升级逻辑,从而让数据库始终保证是最新的版本。
每次数据库升级并不一定都要新增一张表,也有可能是向现有的表中添加新的列。这个时候只需要使用alter语句修改表结构就可以了。
比如添加一个作者字段

@Entity
data class Book (var name:String,var pages:Int,var author:String){

    @PrimaryKey(autoGenerate = true)
    var id:Long=0
}

修改AppDatabase中的代码

@Database(version = 3,entities = [User::class,Book::class])
abstract class AppDatabase: RoomDatabase() {
    abstract fun userDao():UserDao

    abstract fun bookDao():BookDao

    companion object{

        val MIGRATION_1_2=object :Migration(1,2){
            override fun migrate(database: SupportSQLiteDatabase) {
                database.execSQL("create table Book(id integer primary key autoincrement not null,name text not null,pages integer not null)")
            }

        }


        val MIGRATION_2_3=object :Migration(2,3){
            override fun migrate(database: SupportSQLiteDatabase) {
                database.execSQL("alter table Book add column author text not null default 'unknown'")
            }

        }

        private var instance:AppDatabase?=null

        fun  getDatabase(context: Context):AppDatabase{
            instance?.let {
                return it
            }
            return Room.databaseBuilder(context.applicationContext,
            AppDatabase::class.java,"app_database"
                ).addMigrations(MIGRATION_1_2, MIGRATION_2_3).build().apply { instance=this }//apply函数中是构建AppDatabase的实例的上下问,
        // 会自动返回调用对象本身
        }
    }

}

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

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

相关文章

20230119英语学习

Back to the Future 在故宫修钟表是种什么样的体验&#xff1f; After a year of complex restoration, specialists from the Palace Museum in Beijing have given a pair of antique pagoda clock automata a new lease on life. In the form of a nine-tiered pagoda, th…

单片机寄存器

单片机寄存器简述 1、单片机寄存器就是单片机片内存储器&#xff08;片内RAM)一部分&#xff0c;每一个都有地址。只不过这几个寄存器有特殊的作用&#xff0c;比如指令&#xff1a;MUL AB,这条指令用到两个寄存器A,B进行乘法&#xff0c;结果存到BA里面&#xff0c;这条指令必…

Linux基本功系列之type命令实战

文章目录一. type命令介绍二. 语法格式及常用选项三. 参考案例3.1 查看别名3.2 查看是否是内建命令3.3 查看是否为关键字3.4 显示所有命令的位置3.5 判断当前命令是否为alias或者keyword等总结前言&#x1f680;&#x1f680;&#x1f680; 想要学好Linux&#xff0c;命令是基本…

4-2指令系统-指令的寻址方式

文章目录一.指令寻址1.顺序寻址2.跳跃寻址二.数据寻址1.隐含寻址2.立即&#xff08;数&#xff09;寻址3.直接寻址4.间接寻址5.寄存器寻址6.寄存器间接寻址7.相对寻址&#xff08;程序浮动、转移指令&#xff09;8.基址寻址&#xff08;多道程序&#xff09;9.变址寻址&#xf…

移动web字体图标

字体图标下载字体图标使用字体图标使用类名引入字体图标使用unicode编码(了解)在线字体图标使用伪元素字体图标小结下载字体图标 具体的步骤&#xff1a; 使用字体图标 引入相关文件 复制相关的文件&#xff0c;到 fonts文件夹里面。 引入 css <link rel"styleshe…

回溯法复习(总结篇)

根据课本上的学习要点梳理&#xff0c;“通用解题法”&#xff0c;可以系统的搜索一个问题的所有解、任一解&#xff0c;他是一个既带有系统性&#xff08;暴力遍历&#xff09;又带有跳跃性&#xff08;剪枝&#xff09;的搜索算法。 理解回溯法和深度优先搜索策略 回溯的本质…

Kafka入门与核心概念

前言在我们开发过程中&#xff0c;有一些业务功能比较耗时&#xff0c;但是又不是很重要的核心功能&#xff0c;最典型的场景就是注册用户以后发送激活邮件分为两步1&#xff1a;向数据库插入一条数据2&#xff1a;向注册用户发送邮件第2步其实并不是核心功能&#xff0c;但是发…

SpringMVC-拦截器

1&#xff0c;pringMVC-拦截器 对于拦截器这节的知识&#xff0c;我们需要学习如下内容: 拦截器概念入门案例拦截器参数拦截器工作流程分析 1.1 拦截器概念 讲解拦截器的概念之前&#xff0c;我们先看一张图: (1)浏览器发送一个请求会先到Tomcat的web服务器 (2)Tomcat服务…

字节青训前端笔记 | 响应式系统与 React

本节课为前端框架 React 的基础课程讲解 React的设计思路 UI编程的特点 状态更新的时候&#xff0c;UI不会自动更新&#xff0c;需要手动调用DOM接口进行更新欠缺基本的代码层面的封装和隔离&#xff0c;代码层面没有组件化UI之间的数据依赖关系&#xff0c;需要手动维护&am…

【程序人生 | 价值扳机】你的寒假自律第一步(建议收藏)

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学会计专业大二本科在读&#xff0c;阿里云社区专家博主&#xff0c;华为云社区云享专家&#xff0c;CSDN SAP应用技术领域新兴创作者。   在学习工…

小技巧:Excel顽固的名称、引用冲突的解决

共享编辑、跨文档引用的不便 Excel 的共享文档多人编辑、跨文档引用等功能似乎很美好&#xff0c;实际用下来很成问题。 首先共享文档虽然用约定按标注责任人的方式由不同的人编辑不同的行避免的多人同时编辑一行的冲突&#xff0c;但是这通常是个大文件&#xff0c;经常(大概…

函数 C语言】

函数的声明和定义 函数间调用关系是&#xff0c;由于函数调用其他函数&#xff0c;替他函数也可以互相调用&#xff0c;同一个函数可以被一个或多个函数调用任意次。 先声明&#xff0c;后调用。 #include <stdio.h> //去标准库下找文件 #include "stdio.h"…

分件操作和IO

1.文件的路径如图:当我们打开此电脑后(windows系统),上面会显示我们当前的位置&#xff0c;点击之后会出现如图片中的一段字符&#xff0c;这段字符代表着当前你所处位置的路径。最开头的D&#xff1a;/d&#xff1a;是盘符&#xff0c;后面会用斜杠‘/’或者反斜杠‘\’分开,该…

MFC的使用

1.初使用的简单代码该程序包含两个代码 头文件mfc.h和mfc.cpp文件头文件mfc.h#pragma once #include<afxwin.h>class MyApp:public CWinApp//CWinApp应用程序类 { public://程序入口virtual BOOL InitInstance(); }; class MyFrame : public CFrameWnd//继承窗口框架类a …

Java_Git:2. 使用git管理文件版本

目录 1 创建版本库 1.1 使用GitBash 1.2 使用TortoiseGit 2 添加文件 2.1 添加文件过程 2.2 工作区和暂存区 3 修改文件 3.1 提交修改 3.2 查看修改历史 3.3 差异比较 3.4 还原修改 4 删除文件 5 案例&#xff1a;将java工程提交到版本库 5.1 复制文件到工作目录 …

论文阅读:Boosting 3D Object Detection by Simulating Multimodality on Point Clouds

Boosting 3D Object Detection by Simulating Multimodality on Point CloudsResponse DistillationSparse-Voxel DistillationVoxel-to-Point DistillationInstance DistillationlossExperiments稠密&#xff08;多模态、多帧&#xff09;信息->稀疏&#xff08;单模态点云…

【华为上机真题 2023】数组去重和排序 (华为机考真题)

&#x1f388; 作者&#xff1a;Linux猿 &#x1f388; 简介&#xff1a;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我&#xff0c;关注我&#xff0c;有问题私聊&#xff01; &…

2023/1/20 ES6基本语法学习

1 let 命令的解读 1 let 声明变量&#xff0c;没有变量提升 <script>// 不存在变量提升console.log(variable)let variable zhaoshuai-lc </script>2 作用域 全局作用域函数作用域&#xff1a;function() {}块级作用域&#xff1a;{} let 是一个块作用域 <…

Spring中涉及的设计模式总结

Spring中涉及的设计模式总结 1.简单工厂(非23种设计模式中的一种) 实例化阶段主要是通过反射或者CGLIB对bean进行实例化&#xff0c;在这个阶段Spring又给我们暴露了很多的扩展点&#xff1a; 实现方式&#xff1a;BeanFactory。Spring中的BeanFactory就是简单工厂模式的体现…

【设计模式】结构型模式·装饰者模式

学习汇总入口【23种设计模式】学习汇总(数万字讲解体系思维导图) 写作不易&#xff0c;如果您觉得写的不错&#xff0c;欢迎给博主来一波点赞、收藏~让博主更有动力吧&#xff01; 一.概述 在不改变现有对象结构的情况下&#xff0c;动态地给该对象增加一些职责&#xff08;即增…