总结之JDBC

一、JDBC概述

1.1、数据的持久化:

Java DataBase Connectivity Java 数据库连接, Java语言操作数据库

持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用。

快速入门:

​ * 步骤:

​ 1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar

​ 1.复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下

​ 2.右键–>Add As Library

​ 2. 注册驱动

​ 3. 获取数据库连接对象 Connection

​ 4. 定义sql

​ 5. 获取执行sql语句的对象 Statement

​ 6. 执行sql,接受返回结果

​ 7. 处理结果

​ 8. 释放资源

1.2、 JDBC的理解:

JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口(一组API)

简单理解为:JDBC,是SUN提供的一套 API,使用这套API可以实现对具体数据库的操作(获取连接、关闭连接、DML、DDL、DCL)

3.图示理解:

在这里插入图片描述

4.数据库的驱动:
数据库厂商针对于JDBC这套接口,提供的具体实现类的集合。

5.面向接口编程的思想:
JDBC是sun公司提供一套用于数据库操作的接口,java程序员只需要面向这套接口编程即可。
不同的数据库厂商,需要针对这套接口,提供不同实现。不同的实现的集合,即为不同数据库的驱动。

二、数据库的连接

	public static Connection getConnection() throws Exception {
    
    
		// 1.读取配置文件中的4个基本信息
		InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");	
	Properties pros = new Properties();
	pros.load(is);

	String user = pros.getProperty("user");
	String password = pros.getProperty("password");
	String url = pros.getProperty("url");
	String driverClass = pros.getProperty("driverClass");

	// 2.加载驱动
	Class.forName(driverClass);

	// 3.获取连接
	Connection conn = DriverManager.getConnection(url, user, password);
	return conn;
}

2.1、JDBCUtils.java(操作数据库的工具类)

/** 
 *  操作数据库的工具类
 */
public class JDBCUtils {
    
    
	
	/**
	 *  获取数据库的连接
	 * @throws Exception
	 */
	public static Connection getConnection() throws Exception {
    
    
		// 1.读取配置文件中的4个基本信息
		InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");

		Properties pros = new Properties();
		pros.load(is);

		String user = pros.getProperty("user");
		String password = pros.getProperty("password");
		String url = pros.getProperty("url");
		String driverClass = pros.getProperty("driverClass");

		// 2.加载驱动
		Class.forName(driverClass);

		// 3.获取连接
		Connection conn = DriverManager.getConnection(url, user, password);
		return conn;
	}
	/**
	 * 
	 * @param conn
	 * @param ps
	 */
	public static void close(Connection conn,Statement ps){
    
    
		try {
    
    
			if(ps != null)
				ps.close();
		} catch (SQLException e) {
    
    
			e.printStackTrace();
		}
		try {
    
    
			if(conn != null)
				conn.close();
		} catch (SQLException e) {
    
    
			e.printStackTrace();
		}
	}
	/**
	 * 
	 * @Description 关闭资源操作
	 * @param conn
	 * @param ps
	 * @param rs
	 */
	public static void close(Connection conn,Statement ps,ResultSet rs){
    
    
		try {
    
    
			if(ps != null)
				ps.close();
		} catch (SQLException e) {
    
    
			e.printStackTrace();
		}
		try {
    
    
			if(conn != null)
				conn.close();
		} catch (SQLException e) {
    
    
			e.printStackTrace();
		}
		try {
    
    
			if(rs != null)
				rs.close();
		} catch (SQLException e) {
    
    
			e.printStackTrace();
		}
	}
}

2.2、详解各个对象:

1.DriverManager:驱动管理对象

  • 功能:
    1. 注册驱动:告诉程序该使用哪一个数据库驱动jar
    static void registerDriver(Driver driver) :注册与给定的驱动程序 DriverManager 。
    写代码使用: Class.forName(“com.mysql.jdbc.Driver”);//可以省略
    通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块

      ```java
      	 static {
      		        try {
      		            java.sql.DriverManager.registerDriver(new Driver());
      		        } catch (SQLException E) {
      		            throw new RuntimeException("Can't register driver!");
      		        }
      				}
      ```
      
      ​		注意:mysql5之后的驱动jar包可以省略注册驱动的步骤。
      
      	2. 获取数据库连接:
      		* 方法:static Connection getConnection(String url, String user, String password) 
      		* 参数:
      			* url:指定连接的路径
      				* 语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
      				* 例子:jdbc:mysql://localhost:3306/db3
      				* 细节:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称
      			* user:用户名
      			* password:密码 
    

2.Connection:数据库连接对象

  1. 功能:
    1. 获取执行sql 的对象
    * Statement createStatement()
    * PreparedStatement prepareStatement(String sql)
    2. 管理事务:
    * 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务

     		* 提交事务:commit() 
     		
     		* 回滚事务:rollback() 
    

3.Statement:执行sql的对象

执行sql

  1. boolean execute(String sql) :可以执行任意的sql 了解
    2. int executeUpdate(String sql) :执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句
    int * 返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功 返回值>0的则执行成功,反之,则失败。

     3. ResultSet executeQuery(String sql)  :执行DQL(select)语句
    

4.ResultSet:结果集对象,封装查询结果

  • boolean next(): 游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回true
    * getXxx(参数):获取数据
    * Xxx:代表数据类型 如: int getInt() , String getString()
    * 参数:
    1. int:代表列的编号,从1开始 如: getString(1)
    2. String:代表列名称。 如: getDouble(“balance”)

      * 注意:
      	* 使用步骤:
      		1. 游标向下移动一行
      		2. 判断是否有数据
      		3. 获取数据
    
      	   //循环判断游标是否是最后一行末尾。
              while(rs.next()){
                  //获取数据
                  int id = rs.getInt(1);
                  String name = rs.getString("name");
                  double balance = rs.getDouble(3);
    
         ​         System.out.println(id + "---" + name + "---" + balance);
         ​     }
    

5.PreparedStatement:执行sql的对象

  1. SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题
    1. 输入用户随便,输入密码:a’ or ‘a’ = 'a
    2. sql:select * from user where username = ‘wanghui’ and password = ‘a’ or ‘a’ = ‘a’

     2. 解决sql注入问题:使用PreparedStatement对象来解决
     3. 预编译的SQL:参数使用?作为占位符
     4. 步骤:
     	1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
     	2. 注册驱动
     	3. 获取数据库连接对象 Connection
     	4. 定义sql
     		* 注意:sql的参数使用?作为占位符。 如:select * from user where username = ? and password = ?;
     	5. 获取执行sql语句的对象 PreparedStatement  Connection.prepareStatement(String sql) 
     	6. 给?赋值:
     		* 方法: setXxx(参数1,参数2)
     			* 参数1:?的位置编号 从1 开始
     			* 参数2:?的值
     	7. 执行sql,接受返回结果,不需要传递sql语句
     	8. 处理结果
     	9. 释放资源
    

注意:后期都会使用PreparedStatement来完成增删改查的所有操作

			1. 可以防止SQL注入
			2. 效率更高

练习

insert:
	@Test
	public void testInsert() throws Exception{
    
    
		Connection conn = JDBCUtils.getConnection();
		String sql = "insert into customers(name,email,birth,photo)values(?,?,?,?)";
		
		PreparedStatement ps = conn.prepareStatement(sql);
		
		ps.setObject(1,"王晖");
		ps.setObject(2, "[email protected]");
		ps.setObject(3,"1996-10-01");
		FileInputStream is = new FileInputStream(new File("wh.jpg"));
		ps.setBlob(4, is);
		
		ps.execute();
		
		JDBCUtils.closeResource(conn, ps);
		
	}
query:
	@Test
	public void testQuery(){
    
    
		Connection conn = null;
		PreparedStatement ps = null;
		InputStream is = null;
		FileOutputStream fos = null;
		ResultSet rs = null;
		try {
    
    
			conn = JDBCUtils.getConnection();
			String sql = "select id,name,email,birth,photo from customers where id = ?";
			ps = conn.prepareStatement(sql);
			ps.setInt(1, 18);
			rs = ps.executeQuery();
			if(rs.next()){
    
    
	//			方式一:
	//			int id = rs.getInt(1);
	//			String name = rs.getString(2);
	//			String email = rs.getString(3);
	//			Date birth = rs.getDate(4);
				//方式二:
				int id = rs.getInt("id");
				String name = rs.getString("name");
				String email = rs.getString("email");
				Date birth = rs.getDate("birth");
				
				Customer cust = new Customer(id, name, email, birth);
				System.out.println(cust);
				
				//将Blob类型的字段下载下来,以文件的方式保存在本地
				Blob photo = rs.getBlob("photo");
				is = photo.getBinaryStream();
				fos = new FileOutputStream("wh.jpg");
				byte[] buffer = new byte[1024];
				int len;
				while((len = is.read(buffer)) != -1){
    
    
					fos.write(buffer, 0, len);
				}
				
			}
		} catch (Exception e) {
    
    
			e.printStackTrace();
		}finally{
    
    
			
			try {
    
    
				if(is != null)
					is.close();
			} catch (IOException e) {
    
    
				e.printStackTrace();
			}
			
			try {
    
    
				if(fos != null)
					fos.close();
			} catch (IOException e) {
    
    
				e.printStackTrace();
			}
			
			JDBCUtils.close(conn, ps, rs);
		}		
	}

三、数据库的事务

3.1、JDBC控制事务:

​ 1. 事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。

​ 2. 操作:

​ 1. 开启事务

​ 2. 提交事务

​ 3. 回滚事务

​ 3. 使用Connection对象来管理事务

​ * 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务

​ * 在执行sql之前开启事务

​ * 提交事务:commit()

​ * 当所有sql都执行完提交事务

​ * 回滚事务:rollback()

​ * 在catch中回滚事务

说明:
1.数据一旦提交,就不可回滚

2.哪些操作会导致数据的自动提交?

DDL操作一旦执行,都会自动提交。

set autocommit = false 对DDL操作失效

DML默认情况下,一旦执行,就会自动提交。

我们可以通过set autocommit = false的方式取消DML操作的自动提交。

默认在关闭连接时,会自动的提交数据

public class JDBCTest {
    
    

		    public static void main(String[] args) {
    
    
		        Connection conn = null;
		        PreparedStatement pstmt1 = null;
		        PreparedStatement pstmt2 = null;
		
		        try {
    
    
		            //1.获取连接
		            conn = JDBCUtils.getConnection();
		            //开启事务  取消数据的自动提交
		            conn.setAutoCommit(false);
		
		            //2.定义sql
		            //2.1 张三 - 500
		            String sql1 = "update account set balance = balance - ? where id = ?";
		            //2.2 李四 + 500
		            String sql2 = "update account set balance = balance + ? where id = ?";
		            //3.获取执行sql对象
		            pstmt1 = conn.prepareStatement(sql1);
		            pstmt2 = conn.prepareStatement(sql2);
		            //4. 设置参数
		            pstmt1.setDouble(1,500);
		            pstmt1.setInt(2,1);
		
		            pstmt2.setDouble(1,500);
		            pstmt2.setInt(2,2);
		            //5.执行sql
		            pstmt1.executeUpdate();
		            // 手动制造异常
		            int i = 3/0;
		
		            pstmt2.executeUpdate();
		            //提交事务
		            conn.commit();
		        } catch (Exception e) {
    
    
		            //事务回滚
		            try {
    
    
		                if(conn != null) {
    
    
		                    conn.rollback();
		                }
		            } catch (SQLException e1) {
    
    
		                e1.printStackTrace();
		            }
		            e.printStackTrace();
		        }finally {
    
    
		            JDBCUtils.close(pstmt1,conn);
		            JDBCUtils.close(pstmt2,null);
		        }
	
		    }
		}

四、数据库连接池

存在的问题:
在这里插入图片描述

4.1、数据库连接池概念

​ 1. 概念:其实就是一个容器(集合),存放数据库连接的容器。

​ 当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。

​ 2. 好处:

​ 1. 节约资源

​ 2. 用户访问高效

​ 3. 实现:

​ 1. 标准接口:DataSource javax.sql包下 的

​ 1. 方法:

​ * 获取连接:getConnection()

​ * 归还连接:Connection.close()。如果连接对象Connection是从连接池中获取的,那么调用Connection.close()方法,则不会再关闭连接了。而是归还连接

​ 2.一般我们不去实现它,有数据库厂商来实现

在这里插入图片描述

​ 1.C3P0:数据库连接池技术

​ 2.Druid:数据库连接池实现技术,由阿里巴巴提供的

4.2、C3P0:数据库连接池技术

​ * 步骤:

​ 1. 导入jar包 (两个) c3p0-0.9.5.2.jar mchange-commons-java-0.2.12.jar ,

​ * 不要忘记导入数据库驱动jar包

​ 2. 定义配置文件:

​ * 名称: c3p0.properties 或者 c3p0-config.xml

​ * 路径:直接将文件放在src目录下即可。

​ 3. 创建核心对象 数据库连接池对象 ComboPooledDataSource

​ 4. 获取连接: getConnection

代码:
	 //1.创建数据库连接池对象
	      DataSource ds  = new ComboPooledDataSource();
	 //2. 获取连接对象
	      Connection conn = ds.getConnection();

其中,配置文件定义在src下。名为:c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>

	<named-config name="hellc3p0">
		<!-- 提供获取连接的4个基本信息 -->
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="jdbcUrl">jdbc:mysql:///test</property>
		<property name="user">root</property>
		<property name="password">root</property>
		
		<!-- 进行数据库连接池管理的基本信息 -->
		<!-- 当数据库连接池中的连接数不够时,c3p0一次性向数据库服务器申请的连接数 -->
		<property name="acquireIncrement">5</property>
		<!-- c3p0数据库连接池中初始化时的连接数 -->
		<property name="initialPoolSize">10</property>
		<!-- c3p0数据库连接池维护的最少连接数 -->
		<property name="minPoolSize">10</property>
		<!-- c3p0数据库连接池维护的最多的连接数 -->
		<property name="maxPoolSize">100</property>
		<!-- c3p0数据库连接池最多维护的Statement的个数 -->
		<property name="maxStatements">50</property>
		<!-- 每个连接中可以最多使用的Statement的个数 -->
		<property name="maxStatementsPerConnection">2</property>

	</named-config>
</c3p0-config>

4.3、Druid:数据库连接池实现技术,由阿里巴巴提供的

​ 1. 步骤:

​ 1. 导入jar包 druid-1.0.9.jar

​ 2. 定义配置文件:

​ * 是properties形式的

​ * 可以叫任意名称,可以放在任意目录下

​ 3. 加载配置文件。Properties

​ 4. 获取数据库连接池对象:通过工厂来来获取 DruidDataSourceFactory

​ 5. 获取连接:getConnection

	* 代码:
		    //1.加载配置文件
	        Properties pro = new Properties();
	        InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
	        pro.load(is);
	        //2.获取连接池对象
	        DataSource ds = DruidDataSourceFactory.createDataSource(pro);
	        //3.获取连接
	        Connection conn = ds.getConnection();
		2. 定义工具类
			1. 定义一个类 JDBCUtils
			2. 提供静态代码块加载配置文件,初始化连接池对象
			3. 提供方法
				1. 获取连接方法:通过数据库连接池获取连接
				2. 释放资源
				3. 获取连接池的方法
代码:
			public class JDBCUtils {
    
    

			    //1.定义成员变量 DataSource
			    private static DataSource ds ;
			    static{
    
    
			        try {
    
    
			            //1.加载配置文件
			            Properties pro = new Properties();
			            pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
			            //2.获取DataSource
			            ds = DruidDataSourceFactory.createDataSource(pro);
			        } catch (IOException e) {
    
    
			            e.printStackTrace();
			        } catch (Exception e) {
    
    
			            e.printStackTrace();
			        }
			    }
			
			    /**
			     * 获取连接
			     */
			    public static Connection getConnection() throws SQLException {
    
    
			        return ds.getConnection();
			    }
			
			    /**
			     * 释放资源
			     */
			    public static void close(Statement stmt,Connection conn){
    
    
			       /* if(stmt != null){
			            try {
			                stmt.close();
			            } catch (SQLException e) {
			                e.printStackTrace();
			            }
			        }
			
			        if(conn != null){
			            try {
			                conn.close();//归还连接
			            } catch (SQLException e) {
			                e.printStackTrace();
			            }
			        }*/
			
			       close(null,stmt,conn);
			    }
			
			
			    public static void close(ResultSet rs , Statement stmt, Connection conn){
    
    
			
			
			        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();
			            }
			        }
			    }
			
			    /**
			     * 获取连接池方法
			     */
			
			    public static DataSource getDataSource(){
    
    
			        return  ds;
			    }
			
			}
其中,配置文件定义在src下:druid.properties
url=jdbc:mysql:///wh
username=root
password=root
driverClassName=com.mysql.jdbc.Driver

initialSize=10
maxActive=10

五、Spring JDBC

* Spring框架对JDBC的简单封装。提供了一个JDBCTemplate对象简化JDBC的开发

​ * 步骤:

​ 1. 导入jar包

​ 2. 创建JdbcTemplate对象。依赖于数据源DataSource

* JdbcTemplate template = new JdbcTemplate(ds);

​ 3. 调用JdbcTemplate的方法来完成CRUD的操作

​ * update():执行DML语句。增、删、改语句

​ * queryForMap():查询结果将结果集封装为map集合,将列名作为key,将值作为value 将这条记录封装为一个map集合

​ * 注意:这个方法查询的结果集长度只能是1

​ * queryForList():查询结果将结果集封装为list集合

​ * 注意:将每一条记录封装为一个Map集合,再将Map集合装载到List集合中

​ * query():查询结果,将结果封装为JavaBean对象

​ * query的参数:RowMapper

​ * 一般我们使用BeanPropertyRowMapper实现类。可以完成数据到JavaBean的自动封装

* new BeanPropertyRowMapper<类型>(类型.class)

​ * queryForObjec t:查询结果,将结果封装为对象

​ * 一般用于聚合函数的查询

练习:* 需求:

​ 1. 修改1号数据的 salary 为 10000

​ 2. 添加一条记录

​ 3. 删除刚才添加的记录

​ 4. 查询id为1的记录,将其封装为Map集合

​ 5. 查询所有记录,将其封装为List

​ 6. 查询所有记录,将其封装为Emp对象的List集合

​ 7. 查询总记录数

* 代码:
				
				import cn.wh.domain.Emp;
				import cn.wh.utils.JDBCUtils;
				import org.junit.Test;
				import org.springframework.jdbc.core.BeanPropertyRowMapper;
				import org.springframework.jdbc.core.JdbcTemplate;
				import org.springframework.jdbc.core.RowMapper;
				
				import java.sql.Date;
				import java.sql.ResultSet;
				import java.sql.SQLException;
				import java.util.List;
				import java.util.Map;
				
				public class JdbcTemplateDemo2 {
    
    	
				    //Junit单元测试,可以让方法独立执行	
				    //1. 获取JDBCTemplate对象
				    private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
				   
                    /**
				     * 1. 修改1号数据的 salary 为 10000
				     */
				    @Test
				    public void test1(){
    
    
				
				        //2. 定义sql
				        String sql = "update emp set salary = 10000 where id = 1001";
				        //3. 执行sql
				        int count = template.update(sql);
				        System.out.println(count);
				    }
				
				    /**
				     * 2. 添加一条记录
				     */
				    @Test
				    public void test2(){
    
    
				        String sql = "insert into emp(id,ename,dept_id) values(?,?,?)";
				        int count = template.update(sql, 1015, "郭靖", 10);
				        System.out.println(count);
				
				    }
				
				    /**
				     * 3.删除刚才添加的记录
				     */
				    @Test
				    public void test3(){
    
    
				        String sql = "delete from emp where id = ?";
				        int count = template.update(sql, 1015);
				        System.out.println(count);
				    }
				
				    /**
				     * 4.查询id为1001的记录,将其封装为Map集合
				     * 注意:这个方法查询的结果集长度只能是1
				     */
				    @Test
				    public void test4(){
    
    
				        String sql = "select * from emp where id = ? or id = ?";
				        Map<String, Object> map = template.queryForMap(sql, 1001,1002);
				        System.out.println(map);
				        //{id=1001, ename=孙悟空, job_id=4, mgr=1004, joindate=2000-12-17, salary=10000.00, bonus=null, dept_id=20}
				
				    }
				
				    /**
				     * 5. 查询所有记录,将其封装为List
				     */
				    @Test
				    public void test5(){
    
    
				        String sql = "select * from emp";
				        List<Map<String, Object>> list = template.queryForList(sql);
				
				        for (Map<String, Object> stringObjectMap : list) {
    
    
				            System.out.println(stringObjectMap);
				        }
				    }
				
				    /**
				     * 6. 查询所有记录,将其封装为Emp对象的List集合
				     */
				
				    @Test
				    public void test6(){
    
    
				        String sql = "select * from emp";
				        List<Emp> list = template.query(sql, new RowMapper<Emp>() {
    
    
				
				            @Override
				            public Emp mapRow(ResultSet rs, int i) throws SQLException {
    
    
				                Emp emp = new Emp();
				                int id = rs.getInt("id");
				                String ename = rs.getString("ename");
				                int job_id = rs.getInt("job_id");
				                int mgr = rs.getInt("mgr");
				                Date joindate = rs.getDate("joindate");
				                double salary = rs.getDouble("salary");
				                double bonus = rs.getDouble("bonus");
				                int dept_id = rs.getInt("dept_id");
				
				                emp.setId(id);
				                emp.setEname(ename);
				                emp.setJob_id(job_id);
				                emp.setMgr(mgr);
				                emp.setJoindate(joindate);
				                emp.setSalary(salary);
				                emp.setBonus(bonus);
				                emp.setDept_id(dept_id);
				
				                return emp;
				            }
				        });
				
				
				        for (Emp emp : list) {
    
    
				            System.out.println(emp);
				        }
				    }
				
				    /**
				     * 6. 查询所有记录,将其封装为Emp对象的List集合
				     */
				
				    @Test
				    public void test6_2(){
    
    
				        String sql = "select * from emp";
				        List<Emp> list = template.query(sql, new BeanPropertyRowMapper<Emp>(Emp.class));
				        for (Emp emp : list) {
    
    
				            System.out.println(emp);
				        }
				    }
				
				    /**
				     * 7. 查询总记录数
				     */
				
				    @Test
				    public void test7(){
    
    
				        String sql = "select count(id) from emp";
				        Long total = template.queryForObject(sql, Long.class);
				        System.out.println(total);
				    }
				
				}

猜你喜欢

转载自blog.csdn.net/sss153/article/details/108580760