【Android】数据存储方案——文件存储、SharedPreferences、SQLite数据库用法总结

news2024/11/16 19:43:36

文章目录

  • 文件存储
    • 存储到文件
    • 读取文件
  • SharedPreferences存储
    • 存储
      • 获取SharedPreferences对象
      • Context 类的 getSharedPreferences() 方法
        • Activity 类的 getPreferences() 方法
        • PreferenceManager 类中的 getDefaultSharedPreferences() 方法
      • 示例
    • 读取
    • 记住密码的功能
  • SQLite数据库存储
    • 创建数据库
    • 升级数据库
    • 添加数据
    • 更新数据
    • 删除数据
    • 查询数据

今天来介绍一下Android的数据持久化技术,提供了三种存储方式,还可以存储到SD卡中。

主要介绍这三种:

文件存储

适用于存储较大或复杂的数据文件,比如图像、视频、文档等。

也适合存储简单的文本文件。

SharedPreferences存储

适用于存储简单的键值对数据,比如用户设置和应用配置。

数据量通常较小,数据结构简单。

数据库存储

适用于存储结构化数据,支持复杂的查询和数据管理。

文件存储

使用Context提供的openFileOutputopenFileInput方法,他们返回

存储到文件

openFileOutput()

public class MainActivity extends AppCompatActivity {

    private EditText edit; 

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        edit = findViewById(R.id.editText); 
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        String inputText = edit.getText().toString();  // 获取EditText中的文本
        save(inputText);  
    }

    // 将输入的文本保存到文件的方法
    private void save(String inputText) {
        FileOutputStream out = null;  // 声明FileOutputStream变量
        BufferedWriter writer = null;  // 声明BufferedWriter变量
        try {
            // 打开名为 "data" 的文件
            out = openFileOutput("data", Context.MODE_PRIVATE);
            writer = new BufferedWriter(new OutputStreamWriter(out));  // 创建BufferedWriter对象
            writer.write(inputText);  // 将输入的文本写入文件
        } catch (IOException e) {
            throw new RuntimeException(e); 
        } finally {
            try {
                if (writer != null) {
                    writer.close();
                }
            } catch (IOException e) {
                throw new RuntimeException(e); 
            }
        }
    }
}

读取文件

openFileInput

public class MainActivity extends AppCompatActivity {

    private EditText edit; 

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main); 
        
        edit = findViewById(R.id.editText);
        
        String inputText = load(); 
        // 如果加载到的文本内容不为空
        if (!TextUtils.isEmpty(inputText)) { 
            // 如果加载到的文本内容不为空
            edit.setText(inputText); 
            // 将光标移动到文本末尾
            edit.setSelection(inputText.length()); 
            Toast.makeText(this, "加载成功", Toast.LENGTH_SHORT).show(); 
           
        }
    }

    // 加载之前保存的文本内容
    private String load() {
        FileInputStream input = null; 
        BufferedReader reader = null;
        // 创建 StringBuilder 对象,用于存储加载的文本内容
        StringBuilder content = new StringBuilder(); 
        try {
            // 打开名为 "data" 的文件
            input = openFileInput("data"); 
            // 创建 BufferedReader 对象
            reader = new BufferedReader(new InputStreamReader(input)); 
            String line;
            while ((line = reader.readLine()) != null) { 
                content.append(line);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (reader != null) {
                    reader.close(); 
                }
            } catch (IOException e) {
                throw new RuntimeException(e); 
            }
        }
        return content.toString(); 
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        String inputText = edit.getText().toString();
        save(inputText); 
    }

  
    private void save(String inputText) {
        //省略
    }
}

SharedPreferences存储

存储

获取SharedPreferences对象

利用SharedPreferences来存储数据,有三种获取SharedPreferences对象的方法

Context 类的 getSharedPreferences() 方法

  • 第一个参数指定SharedPreferences文件名
  • 第二个参数指定操作模式,MODE_PRIVATE:只有当前应用程序可以对SharedPreferences文件读写
SharedPreferences sharedPreferences = getSharedPreferences("data", MODE_PRIVATE);
Activity 类的 getPreferences() 方法
  • 只接受一个操作模式

  • 自动使用当前应用程序包名作为前缀命名SharedPreferences文件

SharedPreferences preferences = getPreferences(MODE_PRIVATE);
PreferenceManager 类中的 getDefaultSharedPreferences() 方法

步骤:

  1. 获取 SharedPreferences.Editor 编辑器对象

通过SharedPreferences对象的edit()方法

  1. SharedPreferences 中存储数据

  2. 提交数据

SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = sharedPreferences.edit();

或者

SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();

示例

public void StoringData(View view) {
    // 获取 SharedPreferences 编辑器对象
    SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();

    // 向 SharedPreferences 中存储数据
    editor.putString("name", "feng"); // 存储一个字符串值
    editor.putInt("age", 20); // 存储一个整数值
    editor.putBoolean("married", false); // 存储一个布尔值

    // 提交数据
    editor.apply();
}
  1. 获取 SharedPreferences 编辑器对象

    SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();
    
    • getSharedPreferences("data", MODE_PRIVATE):获取名为 dataSharedPreferences 文件。
    • MODE_PRIVATE:操作模式,表示只有本应用可以访问此文件。
    • edit():获取 SharedPreferences.Editor 对象,用于修改 SharedPreferences
  2. SharedPreferences 中存储数据

    editor.putString("name", "feng");
    editor.putInt("age", 20);
    editor.putBoolean("married", false);
    
    • putString("name", "feng"):存储一个字符串键值对。
    • putInt("age", 20):存储一个整数键值对。
    • putBoolean("married", false):存储一个布尔键值对。
  3. 提交数据

    editor.apply();
    
    • apply():异步提交数据,不会返回任何结果,更加高效。可以使用 commit() 同步提交数据,但会返回一个布尔值表示提交是否成功。

读取

get方法:允许你根据键(key)从 SharedPreferences 文件中获取相应的值

getStringgetIntgetBooleangetFloatgetLonggetStringSet

getStringSet(String key, Set<String> defValues)
  • 获取一个字符串集合
  • 第一个参数:键
  • 第二个参数:键不存在,返回的默认值
public void readData(View view) {
    // 获取 SharedPreferences 对象
    SharedPreferences sharedPreferences = getSharedPreferences("data", MODE_PRIVATE);
    
    // 读取数据
    String name = sharedPreferences.getString("name", ""); 
    int age = sharedPreferences.getInt("age", 0); 
    boolean married = sharedPreferences.getBoolean("married", false); 
    
    // 打印读取到的数据
    Log.d("SecondActivity", "name: " + name + " age: " + age + " married: " + married);
}

记住密码的功能

展示登陆界面

image-20240725160811071

记住密码:

public class MainActivity extends BaseActivity {

    private EditText editText1;  // 用于输入账号的 EditText
    private EditText editText2;  // 用于输入密码的 EditText
    private Button btn;          // 用于触发登录的按钮
    private CheckBox rememberPass; // 用于是否记住密码的复选框
    private SharedPreferences sharedPreferences; // 用于存储和读取用户的登录信息
    private SharedPreferences.Editor editor; // 用于编辑 SharedPreferences 的数据

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main); 

        editText1 = findViewById(R.id.editText1);
        editText2 = findViewById(R.id.editText2);
        btn = findViewById(R.id.button);
        rememberPass = findViewById(R.id.remeber_pass);
        
        // 获取 SharedPreferences 对象,用于存储和读取数据
        sharedPreferences = getSharedPreferences("data", MODE_PRIVATE);

        // 检查 SharedPreferences 中是否保存了记住密码的设置
        Boolean isRemember = sharedPreferences.getBoolean("remember_password", false);
        if (isRemember) {
            // 如果记住密码被勾选,填充账号和密码的 EditText,并且勾选复选框
            editText1.setText(sharedPreferences.getString("account", ""));
            editText2.setText(sharedPreferences.getString("password", ""));
            rememberPass.setChecked(true);
        }

        // 登陆按钮点击事件监听器
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 获取用户输入的账号和密码
                String count = editText1.getText().toString();
                String password = editText2.getText().toString();
                
                // 验证账号和密码
                if (count.equals("feng") && password.equals("123")) {

                    // 获取 SharedPreferences 的编辑器对象,注意是在判断语句的外面的大
                    editor = sharedPreferences.edit();
                    
                    // 根据复选框的状态决定是否保存账号和密码
                    if (rememberPass.isChecked()) {
                        // 如果勾选了复选框,保存账号和密码
                        editor.putBoolean("remember_password", true);
                        editor.putString("account", editText1.getText().toString());
                        editor.putString("password", editText2.getText().toString());
                    } else {
                        // 如果未勾选复选框,清除保存的账号和密码
                        editor.clear();
                    }
  
                    // 应用编辑器的更改
                    editor.apply();

                    // 启动新的 Activity 并结束当前 Activity
                    Intent intent = new Intent(MainActivity.this, FirstActivity.class);
                    startActivity(intent);
                    finish();
                } else {
                    // 如果账号或密码错误,显示错误提示
                    Toast.makeText(MainActivity.this, "账号或密码错误", Toast.LENGTH_SHORT).show();
                }
            }
        });
    }
}

SQLite数据库存储

创建数据库

SQLiteOpenHelper抽象类,需要重写onCreate() onUpgrade()两个方法

自定义一个帮助类继承SQLiteOpenHelper

  • getReadableDatabase()getWritableDatabase()

    都可以创建或打开一个现有的数据库(没有就新创建一个)

    返回一个可以对新数据库进行读写操作的对象

  • SQLiteOpenHelper构造方法

     public MyDatabaseHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
            super(context, name, factory, version);
            mContext = context;
        }
    
    1. Context:在构造函数中,这个参数用于初始化mContext,以便在数据库创建时(例如在onCreate方法中)显示Toast消息或访问其他资源

    2. String:指定你想要创建或打开的数据库文件的名称。数据库文件会保存在应用的默认数据库目录中(通常是/data/data/<package_name>/databases/

    3. SQLiteDatabase.CursorFactory

      作用: 这是一个SQLiteDatabase.CursorFactory对象,用于创建Cursor对象。这个工厂对象可以用于自定义Cursor的创建方式,但通常不需要使用它,默认值为null

      使用: 你可以传递一个自定义的CursorFactory对象,以便自定义Cursor的行为。如果不需要自定义,可以传递null,这时SQLite会使用默认的CursorFactory

    4. int version

      数据库的版本号。每次对数据库结构进行更改时,都需要增加这个版本号,以便触发onUpgrade方法。

    数据库创建代码示例:

public class MyDatabaseHelper extends SQLiteOpenHelper {

    // SQL语句用于创建Book表
    public static final String CREATE_BOOK = "create table Book("
            + "id integer primary key autoincrement,"  // id字段,自增主键
            + "author text,"  // author字段,作者名称
            + "price real,"  // price字段,书的价格
            + "pages integer,"  // pages字段,书的页数
            + "name text)";  // name字段,书名

    private Context mContext;  // 用于显示Toast消息的Context对象

    // 构造函数,初始化数据库助手对象
    public MyDatabaseHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);  // 调用父类构造函数
        mContext = context;  // 保存Context对象
    }

    // 创建数据库时调用,执行SQL语句创建表
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);  // 执行创建Book表的SQL语句
        db.execSQL(CREATE_CATEGORY);  // 执行创建Category表的SQL语句(注意CREATE_CATEGORY未定义)
        Toast.makeText(mContext, "创建成功", Toast.LENGTH_SHORT).show();  // 显示表创建成功的消息
    }

    // 升级数据库时调用,处理数据库版本升级
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}

public class MainActivity extends AppCompatActivity {

    private MyDatabaseHelper databaseHelper;  // 数据库帮助类的实例
    private Button btn;  // 按钮控件的实例

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        btn = findViewById(R.id.btn1);  

        // 初始化数据库帮助类
        databaseHelper = new MyDatabaseHelper(this, "BookStore", null, 1);

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 获取可写的数据库实例
                databaseHelper.getWritableDatabase();
            }
        });
    }
}

升级数据库

因为oncreate方法只执行一次,当我们要更新数据库是无法成功

public class MyDatabaseHelper extends SQLiteOpenHelper {

    // 定义创建 Book 表的 SQL 语句
    public static final String CREATE_BOOK = "create table Book("
            + "id integer primary key autoincrement,"  // 自增主键 id
            + "author text,"  // 作者
            + "price real,"  // 价格
            + "pages integer,"  // 页数
            + "name text)";  // 书名

    // 定义创建 Category 表的 SQL 语句
    public static final String CREATE_CATEGORY = "create table Category("
            + "id integer primary key autoincrement,"  // 自增主键 id
            + "category_name text,"  // 类别名称
            + "category_code integer)";  // 类别代码

    private Context mContext;  // 上下文对象

    // 构造函数,初始化数据库帮助类
    public MyDatabaseHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        mContext = context;  // 初始化上下文
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // 创建 Book 表
        db.execSQL(CREATE_BOOK);
        // 创建 Category 表
        db.execSQL(CREATE_CATEGORY);
        // 显示提示信息
        Toast.makeText(mContext, "创建成功", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // 如果表存在,删除 Book 表
        db.execSQL("drop table if exists Book");
        // 如果表存在,删除 Category 表
        db.execSQL("drop table if exists Category");
        // 重新创建数据库
        onCreate(db);
    }
}

更新版本号,比刚才大,onUpgrade()就可以执行了

        databaseHelper = new MyDatabaseHelper(this, "BookStore", null, 2);

添加数据

CRUD:创建(Create)、读取(Read)、更新(Update)和删除(Delete)

getReadableDatabase()getWritableDatabase()返回一个可以对新数据库进行读写操作的对象

调用该对象insert方法

insert

long insert (String table, String nullColumnHack, ContentValues values)
  • table:要插入数据的表名,例如 "Book"

  • nullColumnHack

    未指定添加数据情况下给某些可为空的列自动赋值NULL,一般不使用该功能,直接传入null

  • values: 一个 ContentValues 对象,包含列名和相应的值。

    使用它的put方法添加数据

    ContentValues values = new ContentValues();
    values.put("name", "The Da Vinci Code");
    values.put("author", "Dan Brown");
    values.put("pages", 454);
    values.put("price", 16.96);
    

    添加数据示例:

btn2.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // 获取可写的数据库实例
        SQLiteDatabase db = databaseHelper.getWritableDatabase();
        
        // 创建 ContentValues 实例并存储第一条记录的数据
        ContentValues values = new ContentValues();
        values.put("name", "The Da Vinci Code"); // 书名
        values.put("author", "Dan Brown"); // 作者
        values.put("pages", 454); // 页数
        values.put("price", 16.96); // 价格
        
        // 插入第一条记录到数据库的 "data" 表
        db.insert("Book", null, values);
        
        // 清空 ContentValues 实例并存储第二条记录的数据
        values.clear();
        values.put("name", "The Lost Symbol"); // 书名
        values.put("author", "Dan Brown"); // 作者
        values.put("pages", 510); // 页数
        values.put("price", 19.95); // 价格
        
        // 插入第二条记录到数据库的 "data" 表
        db.insert("Book", null, values);
        
        // 清空 ContentValues 实例以备将来使用
        values.clear();
    }
});

更新数据

与插入数据类似,最后使用update方法更新

  • 第一个参数:表名 “Book”
  • 第二个参数:ContentValues 对象,包含要更新的列及其新值
  • 第三个参数:WHERE 子句,指定哪些行需要更新,这里使用占位符 ‘?’
  • 第四个参数:占位符的实际值,这里是 “The Da Vinci Code”
btn3.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // 获取可写的数据库实例
        SQLiteDatabase db = databaseHelper.getWritableDatabase();
        
        // 创建 ContentValues 实例并存储要更新的列及其新值
        ContentValues values = new ContentValues();
        values.put("price", 150); // 更新书籍的价格为 150
        
        // 执行更新操作
        db.update("Book", values, "name = ?", new String[]{"The Da Vinci Code"});
    }
});

删除数据

delete方法

  • 第一个参数:表名 “Book”
  • 第二个参数:WHERE 子句,指定哪些行需要删除,这里使用占位符 ‘?’
  • 第三个参数:占位符的实际值,这里是 “500”
btn4.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // 获取可写的数据库实例
        SQLiteDatabase db = databaseHelper.getWritableDatabase();
        
        // 执行删除操作
        // 删除五百页以上的书
        db.delete("Book", "pages > ?", new String[]{"500"});
    }
});

查询数据

  • Cursor cursor = db.query("Book", null, null, null, null, null, null);
    
    • 第一个参数 "Book" 是要查询的表名。
    • 第二个参数 null 表示查询所有列。
    • 第三个到第六个参数 null 分别表示 WHERE 子句、WHERE 子句参数、GROUP BY 子句和 HAVING 子句,这里都没有使用。
    • 第七个参数 null 表示排序顺序,这里没有指定。

1721912823839

  • 处理查询结果:

    if (cursor.moveToFirst()) 检查游标是否包含数据,如果包含数据,则移动到第一行。

    • do {...} while (cursor.moveToNext());
      

      循环遍历游标中的所有行,读取每一行的数据,在这里使用 Log.d 方法将读取到的数据输出到日志中,方便调试和查看。

btn5.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // 获取可写的数据库实例
        SQLiteDatabase db = databaseHelper.getWritableDatabase();
        
        // 查询 "Book" 表中的所有数据
        Cursor cursor = db.query("Book", null, null, null, null, null, null);
        
        // 检查游标是否至少包含一行数据
        if (cursor.moveToFirst()) {
            do {
                // 读取当前行的数据
                String name = cursor.getString(cursor.getColumnIndex("name")); 
                // 读取 "name" 列的数据
                String author = cursor.getString(cursor.getColumnIndex("author")); 
                // 读取 "author" 列的数据
                int pages = cursor.getInt(cursor.getColumnIndex("pages")); 
                // 读取 "pages" 列的数据
                double price = cursor.getDouble(cursor.getColumnIndex("price")); 
                // 读取 "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. 《第一行代码》

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

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

相关文章

【解决方案】华普微汽车智能钥匙解决方案

一、方案概述 1.什么是被动式无钥匙进入 "被动式无钥匙进入"&#xff08;Passive Keyless Entry&#xff09;是一种用于车辆、建筑物或其他设施的访问控制系统。它利用无线射频技术自动判断用户是否接近&#xff0c;并进行身份识别以执行开锁或落锁动作&#xff0c…

【Unity2D 2022:UI】TextMeshPro组件无法显示中文

在Unity中创建了一个预制体Card&#xff0c;上面挂载了一些Text Mesh Pro组件用来显示卡牌信息。但是在输入文字后&#xff0c;发现无法显示中文&#xff1a; 解决方法如下&#xff1a; 一、导入字体文件&#xff08;ttf格式&#xff09;和常用字字集&#xff08;txt格式&…

leetcode日记(51)不同路径Ⅱ

和上一道题&#xff08;无障碍物的最短路径&#xff09;很像&#xff0c;但事实上比上一题多了优化方法 根据上一题改的代码如下&#xff0c;添加了对障碍物的判定&#xff0c;如果有障碍物则将数组值设为0。 class Solution { public:int uniquePathsWithObstacles(vector&l…

理发店收银管理系统 python、sqlite3、pyqt5

给姐姐家店写的一个&#xff0c;功能比较简单&#xff0c;结合gpt 功能包含&#xff1a;次卡和充值卡&#xff0c;可以查剩余次数、以及查找消费记录 后期会把sqlite3转到mysql&#xff0c;换成springboot的一个项目 1.使用技术&#xff1a; Python、sqlite3、PyQt5 2.页面 …

快手可灵视频生成大模型全方位测评

快手视频生成大模型“可灵”&#xff08;Kling&#xff09;&#xff0c;是全球首个真正用户可用的视频生成大模型&#xff0c;自面世以来&#xff0c;凭借其无与伦比的视频生成效果&#xff0c;在全球范围内赢得了用户的热烈追捧与高度评价。截至目前&#xff0c;申请体验其内测…

如何使用C#自制一个Windows安装包

原文链接&#xff1a;https://www.cnblogs.com/zhaotianff/p/17387496.html 以前都在用InstallShield制作安装包&#xff0c;基本需求是能满足的&#xff0c;但也有一些缺点&#xff1a; 1、界面不能完全定制 2、不能直接调用代码里的功能 平常使用一些其它软件&#xff0c;…

ETL数据集成丨将PostgreSQL数据库数据实时同步至PostgreSQL

前言 我们在进行数据集成、实时数据同步中&#xff0c;经常会出现在同一个数据库中做数据同步和复制、实时分析和报告、负载均衡和高可用性等场景&#xff0c;这次我们以PostgreSQL为例&#xff0c;通过ETLCloud工具&#xff0c;进行同数据库中数据实时同步的步骤应该如何设置…

Anconda 快速常用命令简洁版

目的&#xff1a;简单清楚的使用基本的conda 命令 可能需求 查看项目中的虚拟环境及依赖是否满足需求操作新环境来满足项目或者论文的实现 Anconda 常用命令 conda 查看基础命令1. 进入Anaconda 环境2. 查看版本3.查看有哪些虚拟环境4.激活虚拟环境5. 进入虚拟环境查看6. 退出…

shopee虾皮 java后端 一面面经 整体感觉不难

面试总结&#xff1a;总体不难&#xff0c;算法题脑抽了只过了一半&#xff0c;面试官点出了问题说时间到了&#xff0c;反问一点点&#xff0c;感觉五五开&#xff0c;许愿一个二面 1.Java中的锁机制&#xff0c;什么是可重入锁 Java中的机制主要包括 synchronized关键字 Loc…

微信小程序之计算器

在日常生活中&#xff0c;计算器是人们广泛使用的工具&#xff0c;可以帮助我们快速且方便地计算金额、成本、利润等。下面将会讲解如何开发一个“计算器”微信小程序。 一、开发思路 1、界面和功能 “计算器”微信小程序的页面效果如图所示 在计算器中可以进行整数和小数的…

NET8部署Kestrel服务HTTPS深入解读TLS协议之Certificate证书

Certificate证书 Certificate称为数字证书。数字证书是一种证明身份的电子凭证&#xff0c;它包含一个公钥和一些身份信息&#xff0c;用于验证数字签名和加密通信。数字证书在网络通信、电子签名、认证授权等场景中都有广泛应用。其特征如下&#xff1a; 由权威机构颁发&…

Minos 多主机分布式 docker-compose 集群部署

参考 docker-compose搭建多主机分布式minio - 会bk的鱼 - 博客园 (cnblogs.com) Minio 是个基于 Golang 编写的开源对象存储套件&#xff0c;虽然轻量&#xff0c;却拥有着不错的性能 中文地址&#xff1a;MinIO | 用于AI的S3 & Kubernetes原生对象存储 官网地址&#xf…

数字看板:跨行业需求下的创新与升级

在当今这个数据驱动的时代&#xff0c;数字看板作为信息展示与决策支持的重要工具&#xff0c;正逐步渗透到各行各业之中。从智慧城市到智能制造&#xff0c;从金融分析到医疗健康&#xff0c;数字看板以其直观、动态、高效的特点&#xff0c;成为了连接数据与决策者的桥梁。本…

C# 将字符串数组以树型结构化

例如字符串数组&#xff1a; string[] arr { "1","3-4-5-6-7", "2","3-4","3-4-5","3-4-5-6", "3", "6", "4", "6-1", "6-2", "5", "6-1-1&…

c++如何理解多态与虚函数

目录 **前言****1. 何为多态**1.1 **编译时多态**1.1.1 函数重载1.1.2 模板 **1.2 运行时多态****1.2.1 虚函数****1.2.2 为什么要用父类指针去调用子类函数** **2. 注意****2.1 基类的析构函数应写为虚函数****2.2 构造函数不能设为虚函数** **本文参考** 前言 在学习 c 的虚…

Tableau入门|数据可视化与仪表盘搭建

原视频链接&#xff08;up:戴戴戴师兄&#xff09;&#xff0c;文章为笔者的自学笔记&#xff0c;用于复习回顾&#xff0c;原视频下方有原up整理的笔记&#xff0c;更加直观便捷。因为视频中间涉及的细节较多&#xff0c;建议一边操作&#xff0c;一边学习。 整体介绍 可视化…

生成式AI:对话系统(Chat)与自主代理(Agent)的和谐共舞

生成式AI&#xff1a;对话与行动的和谐共舞 我们正站在一个令人激动的时代门槛上——生成式AI技术飞速发展&#xff0c;带来了无限的可能性。一个关键问题浮现&#xff1a;AI的未来是对话系统&#xff08;Chat&#xff09;的天下&#xff0c;还是自主代理&#xff08;Agent&am…

非凸T0算法,如何获取超额收益?

什么是非凸 T0 算法&#xff1f; 非凸 T0 算法基于投资者持有的股票持仓&#xff0c;利用机器学习等技术&#xff0c;短周期预测&#xff0c;全自动操作&#xff0c;抓取行情波动价差&#xff0c;增厚产品收益。通过开仓金额限制、持仓时长控制等&#xff0c;把控盈亏风险&…

【Ant Design Pro】快速上手

初始化 初始化脚手架&#xff1a;快速开始 官方默认使用 umi4&#xff0c;这里文档还没有及时更新&#xff08;不能像文档一样选择 umi 的版本&#xff09;&#xff0c;之后我选择 simple。 然后安装依赖。 在 package.json 中&#xff1a; "start": "cross-e…

java-数据结构与算法-02-数据结构-05-栈

文章目录 1. 栈1. 概述2. 链表实现3. 数组实现4. 应用 2. 习题E01. 有效的括号-Leetcode 20E02. 后缀表达式求值-Leetcode 120E03. 中缀表达式转后缀E04. 双栈模拟队列-Leetcode 232E05. 单队列模拟栈-Leetcode 225 1. 栈 1. 概述 计算机科学中&#xff0c;stack 是一种线性的…