问题:java.sql.SQLException: The url cannot be null,涉及静态变量与静态代码块执行顺序,实例化对象执行顺序

4,686 阅读3分钟

针对使用JDBC工具类出现“java.sql.SQLException: The url cannot be null”异常解决方式及原因分析,涉及静态变量与静态代码块执行顺序、类加载实例化对象执行顺序

The url cannot be null.jpg

1. 解决

1.1 贴上源代码

JDBCUtils.java

package com.jdbc.day0511.word;

import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.*;
import java.util.Properties;

//JDBC工具类
public class JDBCUtils {
    //定义连接数据库属性
    private static String url = null;
    private static String user = null;
    private static String password = null;
    private static String driver = null;

    //读取配置文件,static,文件的读取,只需要一次即可拿到连接所需属性
    static{
        try {
            //使用类加载器获取当前类路径
            //获取类加载器对象
            ClassLoader classLoader = JDBCUtils.class.getClassLoader();
            //通过类加载器的getResource()找到指定名称的资源
            URL resource = classLoader.getResource("jdbc.properties");
            String path = resource.getPath();
            //加载文件
            Properties pro = new Properties();
            pro.load(new FileReader(path));
            //根据键获取值
            String url = pro.getProperty("url");
            String user = pro.getProperty("user");
            String password = pro.getProperty("password");
            String driver = pro.getProperty("driver");
            //注册驱动,类加载就会执行
            Class.forName(driver);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    //定义获取连接方法,获取数据库连接对象Connection
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url,user,password);
    }

    //释放资源
    public static void close(ResultSet rs, Statement stat, Connection conn){
        if (rs != null){
            try {
                rs.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (stat != null){
            try {
                stat.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (conn != null){
            try {
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
    public static void close(Statement stat, Connection conn){
        if (stat != null){
            try {
                stat.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (conn != null){
            try {
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}

Test.java

package com.jdbc.day0511.word;

import java.sql.*;
import java.util.Scanner;

/*
    需求:(创建表user(id,username,password))
        1. 通过键盘录入用户名和密码
        2. 判断用户是否登录成功
        select * from user where username = "" and password = "";
        如果这个sql有查询结果,则成功,反之,则失败
 */
public class Test {
    public static void main(String[] args) {
        while(true) {
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入用户名:");
            String username = sc.nextLine();
            System.out.println("请输入密码:");
            String password = sc.nextLine();

            boolean flag = login(username, password);
            if (flag) {
                System.out.println("登录成功!");
                break;
            } else {
                System.out.println("登录失败!请仔细检查");
            }
        }
    }

    private static Boolean login(String username,String password) {
        Boolean flag = null;
        Connection conn = null;
        PreparedStatement pstat = null;
        ResultSet rs = null;
        try {
            //获取连接数据库对象Connection
            conn = JDBCUtils.getConnection();
            //定义sql
            String sql = "select * from user where username = ? and password = ?";
            //获取执行sql语句对象Statement
            //预防sql注入,使用prepareStatment
            pstat = conn.prepareStatement(sql);
            pstat.setString(1,username);
            pstat.setString(2,password);
            //执行sql,并接收返回结果
            rs = pstat.executeQuery();
            //查询结果是否有值
            flag = rs.next();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            JDBCUtils.close(rs,pstat,conn);
        }
        return flag;
    }
}

1.2 解决方式

将静态代码块中定义变量的String删除,删除如下,它与静态成员变量中定义的变量冲突

The url cannot be null2.jpg

2. 原因分析

  • 静态变量和静态代码块也是有执行顺序的,与代码书写的顺序一致。

    • 在静态块中可以使用静态变量,但是被使用的静态变量必须在静态代码块前面声明。
  • Java中的静态变量和静态代码块是在类加载的时候就执行的,实例化对象时,先声明并实例化变量再执行构造函数。如果子类继承父类,则先执行父类的静态变量和静态代码块(先声明的先执行),再执行子类的静态变量和静态代码块。同样,接着依次执行父类和子类变量(或非静态代码块)和构造函数。

  • 实例化对象执行顺序:

    • 父类静态变量/静态代码块(先声明的先执行)>子类静态变量/静态代码块(先声明的先执行)>父类变量/代码块((先声明的先执行)>父类构造函数>子类变量/代码块((先声明的先执行)>子类构造函数>