繼MySQL之后的技術-JDBC-從淺到深-02

目錄

概念

編程六部曲

SQL注入和statement

工具類的封裝

JDBC事務

模糊查詢

批處理

數據庫連接池

Apache-DBUtils

BasicDao


概念

JDBC為訪問不同的數據庫提供了統一的接口,為使用者屏蔽了細節問題。

Java程序員使用JDBC,可以連接任何提供了JDBC驅動程序得到數據庫系統,從而完成對數據庫的各種操作。

解耦合:降低程序的耦合度,提高程序的擴展力。

編程六部曲

第一步,注冊驅動

第二步,獲取連接

第三步,獲取數據庫操作對象

第四步,執行sql語句

第五步,處理查詢結果集

第六步,釋放資源

public static void main(String[] args) throws Exception{//類加載Class.forName("com.mysql.jdbc.Driver");//獲取連接String url = "jdbc:mysql://localhost:3306/mydata";Connection connection = DriverManager.getConnection(url, "root", "123456");//獲取數據庫對象Statement statement = connection.createStatement();//執行sql語句String sql = "select no,name from student";ResultSet resultSet = statement.executeQuery(sql);//處理結果集while(resultSet.next()){int no = resultSet.getInt(1);String name = resultSet.getString(2);System.out.println(name + ":\t" + no);}//關閉流resultSet.close();statement.close();connection.close();
}

SQL注入和statement

目前存在的問題:用戶輸入的信息中患有sql語句的關鍵字,并且這些關鍵字參與sql語句的編譯過程導致sql語句的愿意被扭曲,進而達到sql注入。

如何解決sql注入問題?

1、主要用戶提供的信息不參與sql語句的編譯過程,問題就解決了。

2、即使用戶提供的信息中含有sql語句的關鍵字,但是沒有參與編譯,不起作用。

3、想要用戶信息不參與sql語句的編譯,那么必須使用java.sql.PreparedStatement

4、PreparedStatement接口繼承了java.sql.Statement

5、PreparedStatement是屬于預編譯的數據庫操作對象

6、PreparedStatement的原理是:預先對SQL語句的框架進行編譯,然后再給SQL語句傳值

解決SQL注入的關鍵是什么?

用戶提供的信息中即使含有sql語句的關鍵字,但是這些關鍵字并沒有參與編譯,不起作用。

?對比Statement和PreparedStatement

Statement存在sql注入問題,PreparedStatement解決了SQL注入問題。

Statement是編譯一次執行一次。

PreparedStatement是編譯一次,可執行N次,PreparedStatement效率較高。

PreparedStatement使用較多,凡是業務方面要求是需要進行sql語句拼接的,必須使用Statement。

工具類的封裝

public class JDBCUtil{private static String url;private static String user;private static String password;private static String driver;static{try{Properties properties = new Properties();properties.load(new FileInputStream("mysql.properties"));driver = properties.getProperty("driver");url = properties.getProperty("url");user = properties.getProperty("user");password = properties.getProperty("password");//注冊驅動Class.forName(driver);}catch (Exception e){throw new RuntimeException(e);}}//創建連接方法public static Connection getConnection(){try{return DriverManager.getConnection(url,user,password);} catch(SQLException throwables) {throw new RuntimeException(throwables);}}//關閉流public static void close(ResultSet rs, Statement ps,Connection c){if(rs != null){try{rs.close();} catch(SQLException throwables){throw new RuntimeException(throwables);}}if(ps != null){try{ps.close();} catch(SQLException throwables){throw new RuntimeException(throwables);}}if(c != null){try{c.close();} catch(SQLException throwables){throw new RuntimeException(throwables);}}}public static void main(String[] args){Connection connection = JDBCUtil.getConnection();PreparedStatement preparedStatement = null;String sql = "insert into t1 values('李四',2)";try{preparedStatement = connection.prepareStatement(sql);//執行sql語句preparedStatement.executeUpdate();} catch(SQLException throwables){throwables.printStackTrace();} finally{JDBCUtil.close(null,preparedStatement,connection);}}}

JDBC事務

JDBC中的事務是自動提交的,什么時候自動提交?

只要執行任意一條DML預計,則自動提交一次,這是JDBC默認的事務行為。但是在實際的業務當中,通常都是N條DML語句共同聯合才能完成的,必須保證他們這些DML語句在同一個事務中同時成功或者同時失敗。

//重點三行代碼
connection.setAutoCommit(false);
connection.commit();
connection.rollback();
public static void main(String[] args){Connection connection = JDBCUtil.getConnection();PreparedStatement preparedStatement = null;try {//設置為不自動提交,即開啟事務connection.setAutoCommit(false);String sql = "update t1 set no = no - 100 where name = '馬云'" ;preparedStatement = connection.prepareStatement(sql);preparedStatement.executeUpdate();String sql2 = "update t1 set no = no + 100 where name = '馬化騰'";PreparedStatement preparedStatement1 = connection.prepareStatement(sql2);preparedStatement1.executeUpdate();//提交事務connection.commit();} catch (SQLException throwables){try{//程序執行到此處,說明程序報錯了connection.rollback();} catch (SQLException e){e.printStackTrace();}throwables.printStackTrace();} finally{JDBCUtil.close(null, preparedStatement, connection);}
}

模糊查詢

public static void main(String[] args){Connection connection = JDBCUtil.getConnection();PreparedStatement preparedStatement = null;ResultSet resultSet = null;try{String sql = "select name fron t1 where name like ?";preparedStatement = connection.prepareStatement(sql);//模糊查詢preparedStatement.setString(1,"馬%");resultSet = preparedStatement.executeQuery();while(resultSet.next()){String name = resultSet.getString("name");System.out.println(name);}} catch(SQLException throwables){throwables.printStackTrace();} finally{JDBCUtil.close(resultSet, preparedStatement, connection);}
}

批處理

1、當需要成批插入或者更新記錄時,可以采用java的批量更新機制,這一機制允許多條語句一次性提交給數據庫批量處理。

2、JDBC的批量處理語句包括以下方法:

? ? ? ? addBatch(); //添加需要批量處理的SQL語句或參數

? ? ? ? executeBatch(); //執行批量處理語句

? ? ? ? clearBatch(); // 清空批處理包下的語句

3、JDBC連接MySQL時,如果要使用批處理功能,請在url后添加:

? ? ? ? ?rewriteBatchedStatements=true

4、批處理往往和PreparedStatement一起搭配使用,減少變異次數,減少運行次數。

//以下是傳統方法批量傳入大量數據
public static void main(String[] args){Connection connection = JDBCUtil.getConnection();PreparedStatement preparedStatement = null;try{String sql = "insert into t1 values(?,?)";preparedStatement = connection.prepareStatement(sql);long start = System.currentTimeMillis();//批量插入數據for(int i = 0;i < 5000;i++){prepreadStatement.setString(1,"凍梨");preparedStatement.setInt(2,i);preparedStatement.executeUpdate();}long end = System.currentTimeMillis();} catch(SQLException throwables){throwables.printStackTrace();} finally{JDBCUtil.close(null, preparedStatement, connection);}
}
//以下是批處理方法
public static void main(String[] args){Connection connection = JDBCUtil.getConnection();PreparedStatement preparedStatement = null;try {String sql = "insert into t1 values(?,?)";preparedStatement = connection.preparedStatement(sql);long start = System.currentTimeMillis();//批量插入數據for(int i = 15000; i< 20000;i++){preparedStatement.setString(1,"凍梨");preparedStatement.setInt(2,i);//將sql語句加入批處理包內preparedStatement.addBatch();//達到一千條,在進行處理if((i + 1) % 1000 == 0){preparedStatement.executeBatch();preparedStatement.clearBatch();}}long end = System.currentTimeMillis();} catch (SQLException throwables){throwables.printStackTrace();} finally{JDBCUtil.close(null,preparedStatement, connection);}
}

數據庫連接池

1、傳統的JDBC數據庫連接使用DriverManager來獲取,每次向數據庫連接的時候都要將Connection加載到內容中,再驗證IP地址,用戶名和密碼需要數據庫連接,會占用很多系統資源。

2、每一次數據庫連接,使用完后都得斷開,如果程序出現異常而未能關閉,將導致數據庫內存泄漏,最終將導致重啟數據庫。

3、傳統獲取連接的方式,不能控制創建的連接數量,如連接過多,也可能導致內存泄漏,MySQL崩潰。

4、解決傳統開發中的數據庫連接問題,可以采用數據庫連接池技術。

數據庫連接池基本介紹:

????????1、預先在緩沖池放入一定數據的連接,當需要建立數據庫連接時,只需從緩沖池中取出一個,使用完畢之后再放回去。

? ? ? ? 2、數據庫連接池負責分配,管理和釋放數據庫連接,他允許應用程序重復使用一個現有的數據庫連接,而不是重新建立一個。

? ? ? ? 3、當應用程序向連接池請求的連接數超過最大連接數據時,需要等待。

JDBC的數據庫連接池使用javax.sql.DataSource來表示,DataSource只是一個接口。

數據庫連接池種類:

? ? ? ? 1、C3P0:速度慢,穩定

? ? ? ? 2、DBCP:較快,不穩定

? ? ? ? 3、Proxool:有監控連接池窗臺的功能,不穩定

? ? ? ? 4、BonCP:數據快

? ? ? ? 5、Druid:集以上優點于一身

//傳統方式
public static void main(String[] args) throws Exception{long start = System.currentTimeMillis();for(int i = 0;i < 5000;i++){Connection connection = JDBCUtil.getConnection();connection.close();}long end = System.currentTimeMillis();}
//連接池,需要加入對應的jar和配置文件
public static void main(String[] args) throws Exception{//獲取數據源對象ComboPooledDataSource comboPoolDataSource = new ComboPooledDataSource();//根據配置文件獲取信息Properties properties = new Properties();properties.load(new FileInputStream("src//mysql.properties"));String driver = properties.getProperty('driver');String url = properties.getProperty('url');String user = properties.getProperty('user');String password = properties.getProperty('password');//給數據源設置參數comboPooledDataSource.setDriverClass(driver);comboPooledDataSource.setJdbcUrl(url);comboPooledDataSource.setUser(user);comboPooledDataSource.setPassword(password);//初始化連接數comboPooledDataSource.setInitialPoolSize(10);//設置最大連接數comboPooledDataSource.setMaxPoolSize(50);//獲取連接for(int i = 0;i < 5000;i++){Connection connection = comboPooledDataSource.getConnection();connection.close();}}
//比如50w,Druid要快
public static void main(String[] args) throws Exception{Properties properties = new Properties();properties.load(new FileInputStream("src//druid.properties"));//創建一個Druid連接DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);for(int i = 0;i < 5000;i++){Connection connection = dataSource.getConnection();connection.close();}}
//關于Druid連接池的工具類
public class JDBCUtilsByDruid{private static DataSource ds;static {try{Properties properties = new Properties();properties.load(new FileInputStream("src//druid.properties"));ds = DruidDataSourceFactory.createDataSource(properties);} catch (Exception e){throw new RuntimeException(e);}}//創建連接public static Connection getConnection(){try{return ds.getConnection();} catch(SQLException throwables){throw new RuntimeException(throwables);}}//不是真正的關閉,而是放回連接池public static void close(ResultSet rs, Statement ps, Connection c){if(rs != null){try{rs.close();} catch(SQLException throwables){throw new RuntimeException(throwables);}}if(ps != null){try{ps.close();} catch(SQLException throwables){throw new RuntimeException(throwables);}}if(c!= null){try{c.close();} catch(SQLException throwables){throw new RuntimeException(throwables);}}}
}

Apache-DBUtils

//以下是土方法把resultSet集合里的數據,封裝到list集合中
public class T2{private Integer no;private String name;public T2(){}public T2(Integer no, String name){this.no = no;this.name = name;}public Integer getNo(){return no;}public static void main(String[] args){Connection connection = null;PreparedStatement preparedStatement = null;ResultSet resultSet = null;String sql = "select * from t2";//存儲結果ArrayList<T2> list = new ArrayList<>();try{connection = JDBCUtilsByDruid.getConnection();preparedStatement = connection.prepareStatement(sql);resultSet = preparedStatement.executeQuery();while(resultSet.next()){int no = resultSet.getInt(1);String name = resultSet.getString(2);list.add(new T2(no,name));}} catch(SQLExceotion throwables){throwables.printStackTrace();} finally{JDBCUtilByDruid.close(resultSet, preparedStatement, connection);}}
}

Apache-DBUtils:是Apache組織提供的一個開源JDBC工具類庫,是對JDBC的封裝,極大簡化JDBC工作量。

Dbutils類:

1、QueryRunner類:該類封裝了SQL的執行,是線程安全的。

2、ResultSetHandler接口:該接口用于處理java.sql.ResultSet,將數據按要求轉換為另一種形式。

ArrayHandler:把結果集中的第一行數據轉成對象數組

ArrayListJamdler:把結果集中的每一行數據都換成一個數組,存放到List中

BeanHandler:將結果集中的第一行數據封裝到一個對應的JavaBean實例中

BeanListHandler:將結果集中的第一行數據都封裝到一個對應得到JavaBean實例中,存放到List

ColumnListHandler:將結果集中某一列的數據存放到List中

KeyedHandler:將結果集中的每行數據都封裝到Map里,再把這些map再存放到一個map里,其key為指定的key

MapHandler:將結果集中的第一行數據封裝到一個Map里,key是列名,value就是對應的值

MapListHandler:將結果集中的每一行數據都封裝到一個Map里,然后再存放到List

public static void main(String[] args) throws SQLException{Connection connection = JDBCUtilsByDruid.getConnection();QueryRunner queryRunner = new QueryRunner();String sql = "select * from t2";/*第一個參數:一個連接對象第二個參數:sql語句第三個參數:new BeanListHandler<>(T2.class)在將resultSet封裝成T2對象,然后加入list集合中底層使用反射機制,去獲取T2類的屬性,進行封裝*/List<T2> list = queryRunner.query(connection, sql, new BeanListHandler<>(T2.class));for(T2 t: list){System.out.println(t);}JDBCUtilsByDruid.close(null,null,connection);
}
public static void main(String[] args) throws SQLException{Connection connection = JDBCUtilsByDruid.getConnection();QueryRunner queryRunner = new QueryRunner();String sql = "delete from t2 where no = 3";int update = queryRunner.update(connection, sql);JDBCUtilsByDruid.close(null,null,connection);
}

BasicDao

DAO和增上改查通用方法——BasicDao

1、DAO:data access object 數據訪問對象

2、這樣的通用類,成為BasicDao,是專門和數據庫交互的,即完成對數據表的crud操作

3、在BasicDao的基礎上,實現一張表對應一個Dao,更好的完成功能

Customemer表——Customer.java——CustomerDao,java

Apache-dbutils+Druid簡化了JDBC開發,但還有不足:

1、SQL語句是固定的,不能通過參數傳入,通用性不好,需要進行改進,更方便執行CRUD

2、對于select操作,如果有返回值,返回類型不能固定,需要使用泛型

3、將來的表很多,業務需求復雜,不可能只靠一個java類完成

public class BasicDAO<T> {private QueryRunner qr = new QueryRunner();//dml操作語句public int update(String sql, Object... parameters){Connection connection = null;try {connection = JDBCUtilsByDruid.getConnection();return qr.update(connection, sql, parameters);} catch (SQLException e){throw new RuntimeException(e);} finally {JDBCUtilsByDruid.close(null,null,connection);}}//根據傳入的Class對象,返回對應泛型的集合public List<T> query(String ssql, Class<T> clazz, Object... parameters){Connection connection = null;try {connection = JDBCUtilsByDruid.getConnection();return qr.query(connection, sql, new BeanListHandler<T>(clazz),parameters);} catch (SQLException e){throw new RuntimeException(e);} finally{JDBCUtilsByDruid.close(null,null,connection);}}//查詢單行結果public T querySingle(String sql, Class<T> clazz,Object... parameters){Connection connection = null;try{conenction = JDBCUtilsByDruid.getConnection();return qr.query(connection, sql, new BeanHandler<T>(clazz), parameters);} catch (SQLException e){throw new RuntimeException(e);} finally {JDBCUtilsByDruid.close(null,null,connection);}}//查詢單行單列結果public Object queryScalar(String sql, Object... parameters){Connection connection = null;try{conenction = JDBCUtilsByDruid.getConnection();return qr.query(connection, sql, new ScalarHandler(), parameters);} catch (SQLException e){throw new RuntimeException(e);} finally {JDBCUtilsByDruid.close(null,null,connection);}}}
public class T2DAO extends BasicDAO<T2>{}public class T2{private Integer no;private String name;public T2(){}public T2(Integer no, String name){this.no = no;this.name = name;}public Integer getNo(){ return no;}public void setNo(Integer no) {this.no = no;}public String getName() {return name;}public void setName(String name){this.name = name;}@Overridepublic String toString(){}
}public class TestDao{public static void main(String[] args){T2DAO t2DAO = new T2DAO();//查詢多行String sql = "select * from t2";List<T2> query = t2DAO.query(sql, T2.class);for(T2 t: query){System.out.println(t);}//查詢單行    String sql3 = "select * from t2 where no = 2";T2 t2 = t2DAO.querySingle(sql3, T2.class);//查詢單行單列String sql4 = "select name fron t2 where no = 2";Object o = t2DAO.queryScalar(sql4);//dml操作String sql2 = "insert into t2 values(4, '擦原配')";int update = t2DAO.update(sql2);}
}
public class JDBCUtilsByDruid{private static DataSource ds;static{try{Properties properties = new Properties();properties.load(new FileInputStream("src//druid.properties"));ds = DruidDataSourceFactory.createDataSource(Properties);} catch (Exception e){throw new RuntimeException(e);}}public static Connection getConnection(){try{return ds.getConnection();} catch (SQLException throwables){throw new RuntimeException(throwables);}}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/908416.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/908416.shtml
英文地址,請注明出處:http://en.pswp.cn/news/908416.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【配置 YOLOX 用于按目錄分類的圖片數據集】

現在的圖標點選越來越多&#xff0c;如何一步解決&#xff0c;采用 YOLOX 目標檢測模式則可以輕松解決 要在 YOLOX 中使用按目錄分類的圖片數據集&#xff08;每個目錄代表一個類別&#xff0c;目錄下是該類別的所有圖片&#xff09;&#xff0c;你需要進行以下配置步驟&#x…

淺談python如何做接口自動化

工具與環境準備 開發工具 PyCharm專業版&#xff1a;支持項目視圖、代碼導航、調試功能和主流框架開發官方資源&#xff1a;JetBrains PyCharm 數據庫操作 使用mysqlclient庫操作MySQL&#xff08;Django官方推薦&#xff09;安裝命令&#xff1a;pip install mysqlclient1.3.…

知識圖譜技術概述

一、概述 知識圖譜&#xff08;Knowledge Graph&#xff09; 是一種基于圖結構的語義網絡&#xff0c;用于表示實體及其之間的關系&#xff0c;旨在實現更智能的知識表示和推理。它通過將現實世界中的各類信息抽象為 “實體-關系-實體” 的三元組結構&#xff0c;構建出復雜的知…

NodeJS Koa 后端用戶會話管理,JWT, Session,長短Token,本文一次性講明白

前言 前幾天&#xff0c;我寫了一篇文章&#xff0c;《我設計的一個安全的 web 系統用戶密碼管理流程》。其中著重點是講的如何利用非對稱加密進行安全的設計&#xff0c;并在講述了原理之后&#xff0c;又寫了 《node 后端和瀏覽器前端&#xff0c;有關 RSA 非對稱加密的完整…

0.5S 級精度背后:DJSF1352-RN-6 如何讓儲能電站的每 1kWh 都「有跡可循」?

1、背景 在能源轉型的時代洪流里&#xff0c;大型儲能電站作為保障電網穩定運行、平衡能源供需的核心基礎設施&#xff0c;其戰略價值愈發凸顯。而儲能電站的高效運轉&#xff0c;始終離不開精準的電能計量體系支撐。今日為您重點推介一款針對 1500V 儲能系統研發的專業電能表…

Linux運維筆記:服務器安全加固

文章目錄 背景加固措施1. 修改用戶密碼2. 使用公鑰認證替代密碼登錄3. 強化系統安全4. 掃描與清理殘留威脅5. 規范軟件管理&#xff08;重點&#xff09; 注意事項總結 提示&#xff1a;本文總結了大學實驗室 Linux 電腦感染挖礦病毒后的安全加固措施&#xff0c;重點介紹用戶密…

Pycharm 配置解釋器

今天更新了一版pycharm&#xff0c;因為很久沒有配置解釋器了&#xff0c;發現一直失敗。經過來回試了幾次終于成功了&#xff0c;記錄一下過程。 Step 1 Step 2 這里第二步一定要注意類型要選擇python 而不是conda。 雖然我的解釋器是conda 里面建立的一個環境。挺有意思的

【Linux】awk 命令詳解及使用示例:結構化文本數據處理工具

【Linux】awk 命令詳解及使用示例&#xff1a;結構化文本數據處理工具 引言 awk 是一種強大的文本處理工具和編程語言&#xff0c;專為處理結構化文本數據而設計。它的名稱來源于其三位創始人的姓氏首字母&#xff1a;Alfred Aho、Peter Weinberger 和 Brian Kernighan。 基…

MS1023/MS1224——10MHz 到 80MHz、10:1 LVDS 并串轉換器(串化器)/串并轉換器(解串器)

產品簡述 MS1023 串化器和 MS1224 解串器是一對 10bit 并串 / 串并轉 換芯片&#xff0c;用于在 LVDS 差分底板上傳輸和接收 10MHz 至 80MHz 的并行字速率的串行數據。起始 / 停止位加載后&#xff0c;轉換為負載編 碼輸出&#xff0c;串行數據速率介于 120Mbps…

跟我學c++中級篇——理解類型推導和C++不同版本的支持

一、類型推導 在前面反復分析過類型推導&#xff08;包括前面提到的類模板參數推導CTAD&#xff09;&#xff0c;類型推導其實就是滿足C語言這種強類型語言的要求即編譯期必須確定對象的數據類型。換一句話說&#xff0c;理論上如果編譯器中能夠自動推導所有的相關數據類型&am…

vue3+TS+eslint9配置

記錄eslint升級到9.x的版本之后遇到的坑 在 ESLint 9 中&#xff0c;配置方式發生了變化。Flat Config 格式&#xff08;eslint.config.js 或 .ts&#xff09;不再支持 extensions 選項。所以vscode編輯器中的 extensions 需要注釋掉&#xff0c;要不然保存的時候不會格式化。…

書籍推薦 --- 《篳路維艱:中國經濟社會主義路徑的五次選擇》

蕭冬連.篳路維艱:中國社會主義路徑的五次選擇[M]. 前不久看完的這本書&#xff0c;還是蠻受震撼的。 這本書比較細致地(引用了很多的史料)、從中央高層的視角講解了從新中國成立一直到改革開放初期這30多年里(1949---1980年代)發生在我國的幾次重大事件(三大改造、第一個五年計…

C++課設:簡易日歷程序(支持傳統節假日 + 二十四節氣 + 個人紀念日管理)

名人說&#xff1a;路漫漫其修遠兮&#xff0c;吾將上下而求索。—— 屈原《離騷》 創作者&#xff1a;Code_流蘇(CSDN)&#xff08;一個喜歡古詩詞和編程的Coder&#x1f60a;&#xff09; 專欄介紹&#xff1a;《編程項目實戰》 目錄 一、為什么要開發一個日歷程序&#xff…

(三)動手學線性神經網絡:從數學原理到代碼實現

1 線性回歸 線性回歸是一種基本的預測模型&#xff0c;用于根據輸入特征預測連續的輸出值。它是機器學習和深度學習中最簡單的模型之一&#xff0c;但卻是理解更復雜模型的基礎。 1.1 線性回歸的基本元素 概念理解&#xff1a; 線性回歸假設輸入特征和輸出之間存在線性關系。…

二十五、面向對象底層邏輯-SpringMVC九大組件之HandlerMapping接口設計

一、引言&#xff1a;MVC架構的交通樞紐 在Spring MVC框架中&#xff0c;HandlerMapping接口扮演著"請求導航儀"的關鍵角色&#xff0c;它決定了HTTP請求如何被路由到對應的Controller處理器。作為MVC模式的核心組件之一&#xff0c;HandlerMapping在請求處理的生命…

凌晨四點的星光

凌晨四點的城市像臺停止運轉的老舊機器&#xff0c;陳明裹緊外套踩著路燈的殘影往家走。鍵盤敲擊聲仿佛還在耳邊回響&#xff0c;他揉了揉酸澀的眼睛&#xff0c;手機屏幕突然亮起&#xff0c;是妻子發來的消息&#xff1a;“孩子又發燒了&#xff0c;我帶他去醫院。” 這是他…

Kyosan K5BMC ELECTRONIC INTERLOCKING MANUAL 電子聯鎖

Kyosan K5BMC ELECTRONIC INTERLOCKING MANUAL 電子聯鎖

LeetCode 熱題 100 74. 搜索二維矩陣

LeetCode 熱題 100 | 74. 搜索二維矩陣 大家好&#xff0c;今天我們來解決一道經典的算法題——搜索二維矩陣。這道題在 LeetCode 上被標記為中等難度&#xff0c;要求我們在一個滿足特定條件的二維矩陣中查找一個目標值。如果目標值在矩陣中&#xff0c;返回 true&#xff1b…

如何在 HTML 中添加按鈕

原文&#xff1a;如何在 HTML 中添加按鈕 | w3cschool筆記 &#xff08;請勿將文章標記為付費&#xff01;&#xff01;&#xff01;&#xff01;&#xff09; 在網頁開發中&#xff0c;按鈕是用戶界面中不可或缺的元素之一。無論是用于提交表單、觸發動作還是導航&#xff0…

一篇文章實現Android圖片拼接并保存至相冊

系列文章目錄 一篇文章實現Android圖片拼接并保存至相冊 文章目錄 系列文章目錄前言實現功能類定義和成員變量onCreate方法權限檢查和圖片選擇處理選擇的圖片圖片拼接功能圖片保存功能 使用ImageStitcher類拼接圖片代碼解釋&#xff1a;ImageStitcher.java類定義和方法計算拼接…