多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,就是同一种事物表现出的多种形态。编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。
比如有一个函数是叫某个人来吃饭,函数要求传递的参数是人的对象,可是来了一个美国人,你看到的可能是用刀和叉子在吃饭,而来了一个中国人你看到的可能是用筷子在吃饭,这就体现出了同样是一个方法,可以却产生了不同的形态,这就是多态!
如果父类与子类有同名的属性---------执行父类的属性 如果父类与子类有同名的方法(重写)----执行子类重写之后的方法 若想要调用子类中独有的成员 (强制类型转化) 造型 铸型 (向上/向下转型)
建议:
Animal a = new Cat(); Animal a = new Dog(); //当你定义其他函数,比如feed函数:可以定义feed(Animal a){} //此时,只要你传入的参数是Animal以及子类都可以, //传入参数Cat,就调用Cat的对象的方法属性 //传入参数Dog,就调用Dog的对象的方法属性 //这就是面向抽象编程,面向一个抽象的Animal,而不是面向具体的Dog或者Cat, //这样,当有一天Dog和Cat删除了,也不影响我们的eat函数,因为Animal父类还在 public void feed(Animal a){ //面向父类类型编程 }不建议:
Dog d = new Dog(); Cat c = new Cat(); 为什么SUN制定一套JDBC接口呢? 因为每一个数据的底层实现原理都不一样。Oracle数据库有自己的原理。MySQL数据库也有自己的原理。每一个数据库产品都有自己独特的实现原理。程序员不可能挨个调用使用,所以设计JDBC接口。这样以后程序员面向JDBC接口写代码,调用JDBC提供的几个类。 JDBC其实就是一堆的class类文件。 这些接口的实现由各大数据库厂家自己进行编写。去实现接口中的各个类class。
Sun公司:制定接口 数据库厂家:实现接口,子类数据库厂家 继承 Sun公司的接口 程序员:通过多态,调用父类----接口中方法,实际调用 子类----数据库厂家 中 重写 接口的方法
Sun公司:
/* SUN公司负责制定这套SUN接口 */ public interface JDBC{ //数据库连接方法 void getConnection(); }数据库厂家:
/* Oracle的数据库厂家负责编写JDBC接口的实现方法 */ public class Oracle implements JDBC{ //数据库连接方法 void getConnection(){ System.out.println("连接Oracle数据库成功"); } } /* SqlServer的数据库厂家负责编写JDBC接口的实现方法 */ public class SqlServer implements JDBC{ //数据库连接方法 void getConnection(){ System.out.println("连接SqlServer数据库成功"); } }程序员:new方法创建对象
/* Java程序员角色: 不需要关心具体是哪个品牌的数据库,只需要面向JDBC接口写代码 面向接口编程,面向抽象编程,不要面向具体编程。 */ public class JavaProgrammer{ public static void main(String[] args){ JDBC jdbc = new SqlServer(); //使用哪一个数据库,就new哪一个数据库 //JDBC类型,调用接口中的方法就可以了 jdbc.getConnection(); //多态:调用父类的方法即可 // 子类重写了父类方法,默认调用子类重写的方法。 // 不需要关注继承接口的具体数据库类是如何实现的,不用程序员自己去实现, // 数据库厂家自己进行实现 //最终,我们调用接口中的抽象方法,但是却可以调用各个子类的堆抽象方法的实现。 } }通过多态:JDBC jdbc = new SqlServer();,可以实现调用父类的方法名字,实际上调用的是子类重写父类的方法。 这样以后想要换一家数据库也只需要,修改一个代码JDBC jdbc = new Oracle();即可,后面的代码调用方法也都是调用父类的JDBC的方法(虽然实际调用的是子类的方法),不需要修改其他。 但是如果你就面向具体编程了,比如SqlServer jdbc = new SqlServer();那么不同厂家有不同的函数名,就比如连接这个函数,SqlServer厂家的方法是conn,Oracle厂家的方法是connection。 之前数据库使用SqlServer ,调用连接方法是jdbc.conn, 那么当你把数据库变换Oracle以后,首先类一定要修改Oracle jdbc = new Oracle();,那么调用方法就该为jdbc.connection。 修改的地方太多了,这样对程序员来说太难了,所以规定了一个JDBC接口,制定规则,各大厂家去实现这个接口,程序员只需要调用父类JDBC中的方法,就可以通过多态实际上调用的是子类重写的方法。 程序员:反射方法创建对象
public class JavaProgrammer{ public static void main(String[] args){ //除了new的方法创建对象 //还可以通过反射的方法创建对象 Class c = Class.forName("Oracle"); JDBC jdbc = (JDBC)c.newInstance(); jdbc.getConnection(); } }如果让客户在程序中修改使用数据库类型,不大现实,所以经常使用配置文件,来帮助用户随时修改使用哪种数据库。 首先定义一个配置文件:jdbc.properties文件:里面只有一行: className = Oracle 然后程序员的代码变为:
public class JavaProgrammer{ public static void main(String[] args) throws Exception { ResourceBundle bundle = ResourceBundle.getBundle("jdbc"); String className = bundle.getString("className");//里面输入key值,即等号左边 Class c = Class.forName(className); JDBC jdbc = (JDBC)c.newInstance(); jdbc.getConnection(); } } //此时,如果想要修改使用的数据库类型,只需要在配置文件中修改就可以了。 //改配置文件,就可以,以后都不用改代码了 所以,如果需要使用数据库的时候,需要自己下载对应数据库的jar包,然后配置到环境中,jar包实际上就是各种实现接口的类,即是一堆class文件。实际开发中不建议把连接数据库的信息写死到java程序中,所以考虑写到配置文件中。
rs = stmt.executeQuery("select empno,ename,sal from emp");
每次调用next方法,向下一行,有数据就返回true
主要如何处理查询结果: 方式一:
//通过下标获取查询结果,1就是第一列结果,2是第二列结果 String empno = rs.getString(1); String ename = rs.getString(2); String sal = rs.getString(3); System.out.println(empno + "," + ename + "," + sal);方式二:
// 按下标取出,程序不健壮 String empno = rs.getString("empno"); String ename = rs.getString("ename"); String sal = rs.getString("sal"); System.out.println(empno + "," + ename + "," + sal);方式三:
// 以指定的格式取出 int empno = rs.getInt(1); String ename = rs.getString(2); double sal = rs.getDouble(3); System.out.println(empno + "," + ename + "," + (sal + 100));整体查询代码:
import java.sql.*; import java.util.*; public class JDBCTest05 { public static void main(String[] args) { Connection conn = null; Statement stmt = null; ResultSet rs = null; try{ ResourceBundle rb = ResourceBundle.getBundle("jdbc"); String driver = rb.getString("driver"); String url = rb.getString("url"); String user = rb.getString("user"); String password = rb.getString("password"); // 1、注册驱动 Class.forName(driver); // 2、建立连接 conn = DriverManager.getConnection(url,user,password); // 3、获取数据库操作对象 stmt = conn.createStatement(); // 4、执行sql语句 rs = stmt.executeQuery("select empno,ename,sal from emp"); // 5、获取查询结果集 while(rs.next()){ /* String empno = rs.getString(1); String ename = rs.getString(2); String sal = rs.getString(3); System.out.println(empno + "," + ename + "," + sal); */ /* // 按下标取出,程序不健壮 String empno = rs.getString("empno"); String ename = rs.getString("ename"); String sal = rs.getString("sal"); System.out.println(empno + "," + ename + "," + sal); */ /* // 以指定的格式取出 int empno = rs.getInt(1); String ename = rs.getString(2); double sal = rs.getDouble(3); System.out.println(empno + "," + ename + "," + (sal + 100)); */ int empno = rs.getInt("empno"); String ename = rs.getString("ename"); double sal = rs.getDouble("sal"); System.out.println(empno + "," + ename + "," + (sal + 200)); } } catch(Exception e){ e.printStackTrace(); }finally{ // 6、释放资源 if(rs != null){ try{ rs.close(); } catch (Exception e){ e.printStackTrace(); } } if(stmt != null){ try{ stmt.close(); } catch (Exception e){ e.printStackTrace(); } } if(conn != null){ try{ conn.close(); } catch (Exception e){ e.printStackTrace(); } } } } }实现功能:
需求: 模拟用户登录功能的实现业务描述: 程序运行的时候,提供一个输入的入口,可以让用户输入用户名和密码。 用户输入用户名和密码以后,提交信息,java程序收集到用户信息。 java程序连接数据库验证用户和密码是否合法。 合法:显示登录成功。 不合法:显示登陆失败。所以,代码实现:
String sql = "select * from t_user where loginName = '"+loginName + "'and loginPwd = '" + loginPwd + "'"; rs = stmt.executeQuery(sql); if(rs.next()){ loginSucess = true; }此时如果输入: 用户名:fdsa 密码:fdsa’ or ‘1’='1 ‘1’ = '1’是对的,所以or后面是对的,就会导致整个表都被输出。所以rs.next()一定会输出。 问题出在用户输入的内容有sql的关键字导致SQL注入。 导致SQL注入的根本原因是什么:用户输入信息中含有sql语句的关键字,而这些关键字参与sql语句的编译过程。导致sql语句的原意被扭曲,进而达到sql注入。
只要不让他参加编译就可以了啊,但是上面的代码不行,正好完成了sql语句的拼接编译进去了。
//Statement stmt = null; 变为下面那句 PreparedStatement ps = null; //然后后面的3变化 // 3、获取预编译的数据库操作 String sql = "select * from t_user where loginName = ? and loginPwd= ? "; ps = conn.prepareStatement(sql); //给占位符?传值(第一个问号下标是1,第二个问号下标是2) ps.setString(1,loginNamme); ps.setString(2,loginPwd); // 4、执行sql语句 rs = ps.executeQuery(); if(rs.next()){ loginSucess = true; }Statement和PreparedStatement的区别:
Statement存在sql注入问题,PreparedStatement解决sql注入问题。Statement是编译一次执行一次,PreparedStatement效率较高一些。PreparedStatement会在编译阶段做类型的安全检查。JDBC中的事务是自动提交的,什么是自动提交? 只要执行任意一条DML语句,则自动提交一次。这是JDBC默认的事务行为。 但是在实际业务当中,通常都是N条DML语句共同联合才能完成的,必须保证他们这些DML语句在同一事务中同时成功或者同时失败。
假设一个银行转账系统,数据库中有用户名actno int类型和银行余额balance double类型。 账户111:余额20000 账户222:余额0 现在手写代码,实现账户111给账户222转账10000元。按理来说,转账成功后: 账户111:余额10000 账户222:余额10000 正常情况下代码:
//设置账户1: // 3、获取预编译的数据库操作 String sql = "update t_act set balance = ? where actno = ?"; ps = conn.prepareStatement(sql); //给占位符?传值(第一个问号下标是1,第二个问号下标是2) ps.setDouble(1,10000); ps.setInt(2,111); // 4、执行sql语句 int count = ps.executeUpdate(); //设置账户2: ps.setDouble(1,10000); ps.setInt(2,222); // 4、执行sql语句 count += ps.executeUpdate(); System.out.println(count == 2?"转账成功":"转账失败"); //正常情况下:输出:转账成功 //账户111余额为10000 //账户222余额10000制造异常: 验证JDBC事务是否是默认提交的。
//设置账户1: // 3、获取预编译的数据库操作 String sql = "update t_act set balance = ? where actno = ?"; ps = conn.prepareStatement(sql); //给占位符?传值(第一个问号下标是1,第二个问号下标是2) ps.setDouble(1,10000); ps.setInt(2,111); // 4、执行sql语句 int count = ps.executeUpdate(); //##############制造异常########### String s = null; s.toString(); //异常会导致程序从这里直接退出,导致账户2未收到转账信息 //设置账户2: ps.setDouble(1,10000); ps.setInt(2,222); // 4、执行sql语句 count += ps.executeUpdate(); System.out.println(count == 2?"转账成功":"转账失败"); //账户111余额为10000 //账户222余额0 //因为异常导致,直接不执行后面的语句,直接执行finally了。 //账户2没能执行到,凭空丢了10000修改默认提交: conn.setAutoCommit(false); 在第2步,链接后面,加上,将自动提交机制修改为手动提交 conn.commit();当多个数据库语句成功执行完毕,说明没有异常,事务结束,手动提交。
if(conn != null){ conn.rollback(); }如果有异常,判断conn是否为空,不为null,则应当进行回滚操作。
import java.sql.*; public class JDBCTest01 { public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; try{ //1、注册驱动 Class.forName("com.mysql.jdbc.Driver()"); // 2、获取连接 String url = "jdbc:mysql://127.0.0.1:3306/mydatabase"; //url: 协议;IP;端口;资源名 String user = "root"; // 数据库使用者 String password = "146"; //密码 conn = DriverManager.getConnection(url,user,password); //**************将自动提交机制修改为手动提交************** conn.setAutoCommit(false); //设置账户1: // 3、获取预编译的数据库操作 String sql = "update t_act set balance = ? where actno = ?"; ps = conn.prepareStatement(sql); //给占位符?传值(第一个问号下标是1,第二个问号下标是2) ps.setDouble(1,10000); ps.setInt(2,111); // 4、执行sql语句 int count = ps.executeUpdate(); //##############制造异常########### String s = null; s.toString(); //如果有异常,就进行回滚操作,没有异常就正常手动提交。 //设置账户2: ps.setDouble(1,10000); ps.setInt(2,222); // 4、执行sql语句 count += ps.executeUpdate(); System.out.println(count == 2?"转账成功":"转账失败"); //能够执行到这,说明程序没有异常,事务结束,手动提交 conn.commit(); // 5、处理查询结果集 由于不是select,所以不用处理 } catch(SQLException e) { if(conn != null){ conn.rollback(); } e.printStackTrace(); } finally { // 6、释放资源 // 从小到大依次关闭,每个资源都需要捕获异常 if(ps != null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }将刚才的代码封装成工具类,防止每天写重复的代码。
import java.sql.*; public class DBUtil { /* 工具类中的构造方法都是私有的:https://blog.csdn.net/static_void_james/article/details/78254465 因为工具类当中方法都是静态的,不需要new对象,直接采样类名调用。 和类绑定,这样每次调用就不需要new对象。 */ private DBUtil(){}; //静态块,加载类的时候自动执行,使用数据库一定要先注册驱动,所以讲这一部分写在静态块中 static { try { Class.forName("jdbc:mysql://127.0.0.1:3306/mydatabase"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection( "jdbc:mysql://127.0.0.1:3306/mydatabase", "root", "146"); } public static void close(Connection conn,Statement ps, ResultSet rs){//面向父类编程,Statement p而不是PreparesStatement,更具有一般性。 if(ps != null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }使用工具类进行模糊查询:
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 e) { e.printStackTrace(); }finally { DBUtil.close(conn, ps, rs); } } }