• 卷妹带你学jdbc---2天冲刺Day2


    卷妹带你学jdbc—2天冲刺Day2

    在这里插入图片描述

    👩‍💻博客主页:京与旧铺的博客主页

    ✨欢迎关注🖱点赞🎀收藏⭐留言✒

    🔮本文由京与旧铺原创,csdn首发!

    😘系列专栏:java学习

    👕参考网课:动力节点

    💻首发时间:🎞2022年6月21日🎠

    🎨你做三四月的事,八九月就会有答案,一起加油吧

    🀄如果觉得博主的文章还不错的话,请三连支持一下博主哦

    🎧最后的话,作者是一个新人,在很多方面还做的不好,欢迎大佬指正,一起学习哦,冲冲冲
    💬推荐一款模拟面试、刷题神器👉点击进入网站

    该问题存在BUG

    用户名:fdsa

    密码:fdsa’ or ‘1’='1登录成功

    这种现象被称为SQL注入(安全隐患)。(黑客经常使用)5、导致SQL注入的根本原因是什么?

    用户输入的信息中含有sql语句的关键字,并且这些关键字参与sql语句的编译过程,导致sql语句的原意被扭曲,进而达到SQL注入。


    package com.bjpowernode.jdbc;
    
    import java.sql.*;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Scanner;
    
    /*
    实现功能:
        1、需求:
            模拟用户登录功能的实现。
        2、业务描述:
            程序运行的时候,提供一个输入的入口,可以让用户输入用户名和密码
            用户输入用户名和密码之后,提交信息,java程序收集到用户信息
            Java程序连接数据库验证用户名和密码是否合法
            合法:显示登录成功
            不合法:显示登录失败
        3、数据的准备:
            在实际开发中,表的设计会使用专业的建模工具,我们这里安装一个建模工具:PowerDesigner
            使用PD工具来进行数据库表的设计。(参见user-login.sql脚本)
        4、当前程序存在的问题:
            用户名:fdsa
            密码:fdsa' or '1'='1
            登录成功
            这种现象被称为SQL注入(安全隐患)。(黑客经常使用)
        5、导致SQL注入的根本原因是什么?
            用户输入的信息中含有sql语句的关键字,并且这些关键字参与sql语句的编译过程,
            导致sql语句的原意被扭曲,进而达到sql注入。
     */
    public class JDBCTest06 {
        public static void main(String[] args) {
            // 初始化一个界面
            Map<String,String> userLoginInfo = initUI();
            // 验证用户名和密码
            boolean loginSuccess = login(userLoginInfo);
            // 最后输出结果
            System.out.println(loginSuccess ? "登录成功" : "登录失败");
        }/**
    
       * 用户登录
          @param userLoginInfo 用户登录信息
    
            * @return false表示失败,true表示成功
              /
                  private static boolean login(Map<String, String> userLoginInfo) {
              // 打标记的意识
              boolean loginSuccess = false;
              // 单独定义变量
              String loginName = userLoginInfo.get("loginName");
              String loginPwd = userLoginInfo.get("loginPwd");
    
         // JDBC代码
         Connection conn = null;
         Statement stmt = null;
         ResultSet rs = null;
    
         try {
             // 1、注册驱动
             Class.forName("com.mysql.jdbc.Driver");
             // 2、获取连接
             conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root", "333");
             // 3、获取数据库操作对象
             stmt = conn.createStatement();
             // 4、执行sql
             String sql = "select * from t_user where loginName = '"+loginName+"' and loginPwd = '"+loginPwd+"'";
             // 以上正好完成了sql语句的拼接,以下代码的含义是,发送sql语句给DBMS,DBMS进行sql编译。
             // 正好将用户提供的“非法信息”编译进去。导致了原sql语句的含义被扭曲了。
             rs = stmt.executeQuery(sql);
             // 5、处理结果集
             if(rs.next()){
                 // 登录成功
                 loginSuccess = true;
             }
         } catch (Exception e) {
             e.printStackTrace();
         } finally {
             // 6、释放资源
             if (rs != null) {
                 try {
                     rs.close();
                 } catch (SQLException e) {
                     e.printStackTrace();
                 }
             }
             if (stmt != null) {
                 try {
                     stmt.close();
                 } catch (SQLException e) {
                     e.printStackTrace();
                 }
             }
             if (conn != null) {
                 try {
                     conn.close();
                 } catch (SQLException e) {
                     e.printStackTrace();
                 }
             }
         }
         return loginSuccess;
             }
    
    ​    /**
    
       * 初始化用户界面
          @return 用户输入的用户名和密码等登录信息
              */
             private static Map<String, String> initUI() {
         Scanner s = new Scanner(System.in);
    
         System.out.print("用户名:");
         String loginName = s.nextLine();
    
         System.out.print("密码:");
         String loginPwd = s.nextLine();
    
         Map<String,String> userLoginInfo = new HashMap<>();
         userLoginInfo.put("loginName", loginName);
         userLoginInfo.put("loginPwd", loginPwd);
    
         return userLoginInfo;
             }
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127

    解决SQL注入问题

    3、解决SQL注入的关键是什么?

    用户提供的信息中即使含有sql语句的关键字,但是这些关键字并没有参与编译。不起作用。4、对比一下statement和Preparedstatement?

    Statement存在sql注入问题,Preparedstatement解决了SQL注入问题。

    Statement是编译一次执行一次。PreparedStatement是编译一次,可执行多次。Preparedstatement效率较高一些。Preparedstatement会在编译阶段做类型的安全检查。

    package com.bjpowernode.jdbc;
    
    import java.sql.*;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Scanner;
    
    /**
     * 作者:杜聚宾
     *
     * 1、解决SQL注入问题?
     *      只要用户提供的信息不参与SQL语句的编译过程,问题就解决了。
     *      即使用户提供的信息中含有SQL语句的关键字,但是没有参与编译,不起作用。
     *      要想用户信息不参与SQL语句的编译,那么必须使用java.sql.PreparedStatement
     *      PreparedStatement接口继承了java.sql.Statement
     *      PreparedStatement是属于预编译的数据库操作对象。
     *      PreparedStatement的原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传“值”。
     * 2、测试结果:
     *      用户名:fdas
     *      密码:fdsa' or '1'='1
     *      登录失败
     * 3、解决SQL注入的关键是什么?
     *      用户提供的信息中即使含有sql语句的关键字,但是这些关键字并没有参与编译。不起作用。
     * 4、对比一下Statement和PreparedStatement?
     *      - Statement存在sql注入问题,PreparedStatement解决了SQL注入问题。
     *      - Statement是编译一次执行一次。PreparedStatement是编译一次,可执行N次。PreparedStatement效率较高一些。
     *      - PreparedStatement会在编译阶段做类型的安全检查。
     *
     *      综上所述:PreparedStatement使用较多。只有极少数的情况下需要使用Statement
     * 5、什么情况下必须使用Statement呢?
     *      业务方面要求必须支持SQL注入的时候。
     *      Statement支持SQL注入,凡是业务方面要求是需要进行sql语句拼接的,必须使用Statement。
     */
    public class JDBCTest07 {
        public static void main(String[] args) {
            // 初始化一个界面
            Map<String,String> userLoginInfo = initUI();
            // 验证用户名和密码
            boolean loginSuccess = login(userLoginInfo);
            // 最后输出结果
            System.out.println(loginSuccess ? "登录成功" : "登录失败");
        }
    
        /**
         * 用户登录
         * @param userLoginInfo 用户登录信息
         * @return false表示失败,true表示成功
         */
        private static boolean login(Map<String, String> userLoginInfo) {
            // 打标记的意识
            boolean loginSuccess = false;
            // 单独定义变量
            String loginName = userLoginInfo.get("loginName");
            String loginPwd = userLoginInfo.get("loginPwd");
    
            // JDBC代码
            Connection conn = null;
            PreparedStatement ps = null; // 这里使用PreparedStatement(预编译的数据库操作对象)
            ResultSet rs = null;
    
            try {
                // 1、注册驱动
                Class.forName("com.mysql.jdbc.Driver");
                // 2、获取连接
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root", "333");
                // 3、获取预编译的数据库操作对象
                // SQL语句的框子。其中一个?,表示一个占位符,一个?将来接收一个“值”,注意:占位符不能使用单引号括起来。
                String sql = "select * from t_user where loginName = ? and loginPwd = ?";
                // 程序执行到此处,会发送sql语句框子给DBMS,然后DBMS进行sql语句的预先编译。
                ps = conn.prepareStatement(sql);
                // 给占位符?传值(第1个问号下标是1,第2个问号下标是2,JDBC中所有下标从1开始。)
                ps.setString(1, loginName);
                ps.setString(2, loginPwd);
                // 4、执行sql
                rs = ps.executeQuery();
                // 5、处理结果集
                if(rs.next()){
                    // 登录成功
                    loginSuccess = true;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 6、释放资源
                if (rs != null) {
                    try {
                        rs.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (ps != null) {
                    try {
                        ps.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
            return loginSuccess;
        }
    
        /**
         * 初始化用户界面
         * @return 用户输入的用户名和密码等登录信息
         */
        private static Map<String, String> initUI() {
            Scanner s = new Scanner(System.in);
    
            System.out.print("用户名:");
            String loginName = s.nextLine();
    
            System.out.print("密码:");
            String loginPwd = s.nextLine();
    
            Map<String,String> userLoginInfo = new HashMap<>();
            userLoginInfo.put("loginName", loginName);
            userLoginInfo.put("loginPwd", loginPwd);
    
            return userLoginInfo;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130

    演示statement的用途

    即什么时候使用statement

    综上所述: Preparedstatement使用较多。只有极少数的情况下需要使用statement5、什么情况下必须使用statement呢?

    业务方面要求必须支持sql注入的时候。

    statement支持sql注入,凡是业务方面要求是需要进行sql语句拼接的,必须使用statement。

    package ustc.java.jdbc;
    
    import java.sql.*;
    import java.util.Scanner;
    
    public class JDBCTest08 {
        public static void main(String[] args) {
            //用户在控制台输入desc就是降序,输入asc就是升序
            Scanner s = new Scanner(System.in);
            System.out.println("请输入desc或者asc");
            String keyWords = s.nextLine();
            //执行SQL
            Connection conn = null;
            Statement stmt = null;
            ResultSet rs = null;
    
            try {
                Class.forName("com.mysql.jdbc.Driver");
    
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");
    
                stmt = conn.createStatement();
    
                String sql = "select ename from emp order by ename " + keyWords;
                rs = stmt.executeQuery(sql);
                //遍历结果集
                while(rs.next()){
                    System.out.println(rs.getString("ename"));
                }
    
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            } finally {
                if (rs != null) {
                    try {
                        rs.close();
                    } catch (SQLException throwables) {
                        throwables.printStackTrace();
                    }
                }
                if (stmt != null) {
                    try {
                        rs.close();
                    } catch (SQLException throwables) {
                        throwables.printStackTrace();
                    }
                }
                if (conn != null) {
                    try {
                        rs.close();
                    } catch (SQLException throwables) {
                        throwables.printStackTrace();
                    }
                }
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    三、JDBC事务机制

    JDBC事务机制:

    1、JDBc中的事务是自动提交的,什么是自动提交?

    只要执行任意一条DML语句,则自动提交—次。这是JDBC默认的事务行为。但是在实际的业务当中,通常都是N条DML语句共同联合才能完成的,必须保证他们这些DML语句在同一个事务中同时成功或者同时失败。

    文件名:t_act.sql

    用途:bjpowernode;

    source …

    drop table if exists t_act;
    create table t_act(
    	actno int,
    	balance double(7,2)//注意7表示有效数字的个数,2表示小数位的个数。
    );
    insert into t_act(actno,balance) values(111,20000);
    insert into t_act(actno,balance) values(222,0);
    commit;
    select * from t_act;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    /*重点三行代码:
           conn.setAutoCommit(false);
           conn.commit();
           conn.rollback();
     */
    package ustc.java.jdbc;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    
    public class JDBCTest10 {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps = null;
            try {
                // 注册驱动
                Class.forName("com.mysql.jdbc.Driver");
    
                // 获取连接
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");
    
                // 将自动提交改为手动提交
                conn.setAutoCommit(false);
    
                // 获取预编译的数据库操作对象
                String sql = "update t_act set balance = ? where actno = ? ";
                ps = conn.prepareStatement(sql);
                ps.setInt(1,10000);
                ps.setDouble(2,111);
    
                // 执行sql语句
                int count = ps.executeUpdate();
    
                /*String s = null;
                s.toString();*/
    
                ps.setInt(1,10000);
                ps.setDouble(2,222);
                count += ps.executeUpdate();
    
                System.out.println(count == 2 ? "转账成功" : "转账失败");
    
                // 程序能执行到此处,说明没有异常,事务结束,手动提交数据
                conn.commit();
            } catch (Exception e) {
                // 遇到异常,回滚
                if (conn != null) {
                    try {
                        conn.rollback();
                    } catch (SQLException throwables) {
                        throwables.printStackTrace();
                    }
                }
                e.printStackTrace();
            }  finally {
                // 释放资源
                if (ps != null) {
                    try {
                        ps.close();
                    } catch (SQLException throwables) {
                        throwables.printStackTrace();
                    }
                }
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException throwables) {
                        throwables.printStackTrace();
                    }
                }
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    四、JDBC工具类的封装

    img

    jdbc.properti

    driver=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://localhost:3306/bjpowernode?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    user=root
    password=123456
    
    • 1
    • 2
    • 3
    • 4
    package com.bjpowernode.oa.utils;
    
    import java.sql.*;
    import java.util.ResourceBundle;
    
    /**
     * JDBC的工具类
     */
    public class DBUtil {
    
        // 静态变量:在类加载时执行。
        // 并且是有顺序的。自上而下的顺序。
        // 属性资源文件绑定
        private static ResourceBundle bundle = ResourceBundle.getBundle("resources.jdbc");
        // 根据属性配置文件key获取value
        private static String driver = bundle.getString("driver");
        private static String url = bundle.getString("url");
        private static String user = bundle.getString("user");
        private static String password = bundle.getString("password");
    
        static {
            // 注册驱动(注册驱动只需要注册一次,放在静态代码块当中。DBUtil类加载的时候执行。)
            try {
                // "com.mysql.jdbc.Driver" 是连接数据库的驱动,不能写死。因为以后可能还会连接Oracle数据库。
                // 如果连接oracle数据库的时候,还需要修改java代码,显然违背了OCP开闭原则。
                // OCP开闭原则:对扩展开放,对修改关闭。(什么是符合OCP呢?在进行功能扩展的时候,不需要修改java源代码。)
                //Class.forName("com.mysql.jdbc.Driver");
    
                Class.forName(driver);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 获取数据库连接对象
         * @return conn 连接对象
         * @throws SQLException
         */
        public static Connection getConnection() throws SQLException {
            // 获取连接
            Connection conn = DriverManager.getConnection(url, user, password);
            return conn;
        }
    
        /**
         * 释放资源
         * @param conn 连接对象
         * @param ps 数据库操作对象
         * @param rs 结果集对象
         */
        public static void close(Connection conn, Statement ps, ResultSet rs){
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77

    测试DBUtil工具类和模糊查询

    package ustc.java.jdbc;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    /*
        1、测试DBUtil工具类
        2、模糊查询
     */
    public class JDBCTest {
        public static void main(String[] args) {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
    
            try {
                conn =  DBUtil.getConnection();
    
                String sql = "select ename from emp where ename like ?";
                ps = conn.prepareStatement(sql);
                ps.setString(1,"_A%");
    
                rs = ps.executeQuery();
    
                while(rs.next()){
                    System.out.println(rs.getString("ename"));
                }
            } catch (SQLException throwables) {
                e.printStackTrace();
            }finally{
                DBUtil.close(conn,ps,rs);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
    
        try {
            conn =  DBUtil.getConnection();
    
            String sql = "select ename from emp where ename like ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1,"_A%");
    
            rs = ps.executeQuery();
    
            while(rs.next()){
                System.out.println(rs.getString("ename"));
            }
        } catch (SQLException throwables) {
            e.printStackTrace();
        }finally{
            DBUtil.close(conn,ps,rs);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    }

    结束语🏆🏆🏆
    🔥推荐一款模拟面试、刷题神器网站
    点击跳转进入网站[点击进入](https://www.nowcoder.com/exam/oj?page=1&tab=%E8%AF%AD%E6%B3%95%E7%AF%87&topicId=220&fromPut=pc_csdncpt_jyjp_java)
    1、算法篇(398题):面试必刷100题、算法入门、面试高频榜单
    2、SQL篇(82题):快速入门、SQL必知必会、SQL进阶挑战、面试真题
    3、大厂笔试真题:字节跳动、美团、百度、腾讯…
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 相关阅读:
    安装Docker(Linux:CentOS)
    二叉树的前序、中序和后序非递归
    学习JDBC总结
    Linux Kernel入门到精通系列讲解(RV-Kernel 篇) 5.5 RTC设备编写与实现
    独立游戏笔记-002 unity -01
    潮玩宇宙大逃杀游戏开发源码说明
    操作系统笔记(王道考研) 第四章:文件管理
    大学生静态HTML网页源码 我的校园网页设计成品 学校班级网页制作模板 web课程设计 dreamweaver网页作业
    面试官:你说说一条查询 SQL 的执行过程
    50从零开始用Rust编写nginx,原来TLS证书还可以这么申请
  • 原文地址:https://blog.csdn.net/qq_46272491/article/details/125464413