JAVA程序设计基础-第6版陈国君2006-学习笔记5

JAVA程序设计基础-第6版陈国君2006-学习笔记5

[TOC]

JAVA程序设计基础-第6版陈国君2006-学习笔记5

第十七章 Java数据库程序设计

关系数据库系统

数据库是按照一定的数据结构来组织、存储和管理数据的仓库;
数据库管理系统(Data BaseManagement System, DBMS)是一种操纵和管理数据库的大型软件,用于建立、使用和维护数据库;
数据库系统(database system)由数据库、数据库管理系统以及应用程序组成。

应用程序视为用户与数据库之间的接口。

大多数数据库系统都是关系数据库系统。它们都是基于关系数据模型的,这种模型有三个要素:结构完整性语言

结构定义了数据的表示;
完整性是一些对数据的约束,所谓约束就是当向数据库中输入数据时所必须遵守的规则,所以约束也称为限制条件;
语言则提供了访问和操纵数据的手段。

数据库与数据库表

一个关系型数据库通常是由一个或多个二维数据库表组成,数据库中的二维数据库表简称表。

数据库中的所有数据和信息都被保存在这些表中。

数据库中的每个表都具有唯一的表名称,表中的行称为记录,列称为字段

完整性约束

完整性约束是对表强加了一个限制条件,表中的所有合法值都必须满足该条件。

完整性约束有三种类型:域约束、主码约束和外码约束。
域约束和主码约束只涉及一个表,而外码约束则涉及多个表。

域约束

域就是字段的取值范围,域约束就是规定一个表的字段的允许取值。

主码约束

主码也称为主键,是表中用于唯一确定一条记录的一个字段或最小的字段组。
主码可以由一个字段组成,也可以是由多个字段共同组成,由多个字段共同组成的主码称为复合主码。
若一个表中存在多个可以作为主码的字段,则称这些字段为候选码或候选键。

外码约束

若一个表的某个字段(或字段组合)不是该表的主码,却是另一个表的主码,则称这样的字段为该表的外码或外键。外码是表与表之间的纽带。

所有关系数据库系统都支持主码约束和外码约束。但不是所有数据库系统都支持域约束。

SQL

结构化查询语言(Structured Query Language, SQL)是用来定义表和完整性约束以及访问和操纵数据库的语言,它是访问关系数据库的通用语言。

[]:表示可选项,即方括号中的内容可以根据需要进行选择;不选用时,则使用系统的默认值。方括号本身不是SQL语句的一部分,所以输入时不要输入方括号本身。
{}:表示必选项,即大括号中的内容必须要提供。在实际操作时也不要输入大括号本身。
< >:表示尖括号中的内容是用户必须提供的参数。输入时不要输入尖括号本身。
|:表示只能选一项,竖线分隔多个选择项,用户必须选择其中之一。
[,...n]:表示前面的项可重复n次,相互之间以逗号隔开。

SQL的关键字不区分大小写。
SQL中不区分字符型和字符串型量,而统一定义为字符串型量,字符串型常量的定界符既可使用单引号也可使用双引号。

创建数据库

1
CREATE DATABASE <数据库名>;
1
CREATE DATABASE StudentScore;

表操作

创建表

1
CREATE TABLE <表名>(<字段名><数据类型>[<字段级完整性约束>][,<字段名><数据类型>[<字段级完整性约束>]]...[,<表级完整性约束>]);

<表名>:要创建的表的名字,表名在同一数据库中不允许重名。
<字段名>:字段名字。
<数据类型>:指定字段的数据类型,对有些数据类型还需同时给出其长度、小数位数。
<字段级完整性约束>:字段完整性约束条件,主要有如下几种:NULL和NOT NULL:限制字段可以为NULL(空),或者不能为NULL;PRIMARY KEY:设置字段为主码;UNIQUE:设置字段值具有唯一性。
<表级完整性约束>:表级完整性约束条件所使用的关键字与字段级完整性约束相似。

1
2
3
4
5
6
CREATE TABLE Student(sNo CHAR(9) NOT NULL PRIMARY KEY, sName CHAR(12) NOT NULL, sex CHAR(2), age INT, dept CHAR(50)); //创建表Student, sNo为主码

CREATE TABLE Course(cNo CHAR(9) NOT NULL PRIMARY KEY, cName CHAR(30)
NOT NULL, credit INT);//创建表Course, cNo为主码

CREATE TABLE Score(sNo CHAR(9) NOT NULL, cNo CHAR(6) NOT NULL, grade FLOAT, PRIMARY KEY(sNo, cNo));//创建表Score,字段组sNo和cNo为复合主码

删除表

1
DROP TABLE <表名>;
1
DROP TABLE Student;

修改表结构

1
2
3
4
5
ALTER TABLE <表名>
[ALTER COLUDMN <字段名> <数据类型>]|
[ADD COLUMN <字段名> <数据类型> [<字段级完整性约束>]|
[DROP COLUMN <字段名>]|
[DROP CONSTRAINT <完整性约束>];

ALTER COLUMN子句:修改表中已有字段的定义。
ADD COLUMN子句:增加新字段及相应的完整性约束条件。
DROP COLUMN子句:在该表中删除该子句中给出的字段。
DROP CONSTRAINT子句:删除指定的完整性约束条件。

1
2
//利用SQL语句给Student表添加一个字符型的电话字段phone,长度为11个字符。
ALTER TABLE Student ADD COLUMN phone CHAR(11);

表数据操作

SQL的数据操作语言(Data Manipulation Language, DML)的功能,包括向表中插入数据、修改数据、删除数据和查询数据等,对应操作所使用的命令为INSERT( 插入) 、UPDATE(修改)、DELETE(删除)和SELECT(查询)等。

插入数据-INSERT

1
INSERT INTO <表名>[(<字段名[,<字段名>]...>)]VALUES(<>[,<>]...);

<表名>:要添加新记录的表。
<字段名>:可选项,指定待添加数据的字段。
VALUES子句:指定待添加数据的具体值。当指定字段名时,VALUES子句中值的排列顺序必须和字段名的排列顺序一致;若不指定字段,则VALUES子句中值的排列顺序必须与创建表字段时的排列顺序一致。

1
2
//在学生表Student中插入一条学生记录,学号:201201009,姓名:王毅,性别:男,年龄:18,系别:外语。
INSERT INTO Student(sNo, sName, sex, age, dept)VALUES('201201009', '王', '男', 18, '外语');

修改数据-UPDATE

1
UPDATE <表名>SET <字段名>=<表达式>[, <字段名>=<表达式>[WHERE <条件>];

SET子句:给出要修改的字段及其修改后的值。
WHERE子句:指定待修改的记录应当满足的条件。WHERE子句省略时,则修改表中所有记录。

1
UPDATE Student SET dept='金融' WHERE sNo='201201009';

删除数据-DELETE

1
DELETE FROM<表名>[WHERE <条件>];

WHERE子句:指定待删除的记录应当满足的条件。WHERE子句省略时,则删除表中所有记录。

1
2
//在学生表Student中删除学号为201201009的学生记录。
DELETE FROM Student WHERE sNo='201201009';

数据查询-SELECT

1
2
3
4
5
SELECT [ALL|DISTINCT][TOP n [PERCENT]]{ * |{<字段名>|<表达式>|}
[[AS]<别名>]|<字段名>[[AS]<别名>]}[...n]}
FROM <表名>[WHERE <查询条件表达式>]
[GROUP BY <字段名表>[HAVING <分组条件>]]
[ ORDER BY <次序表达式>[ASCI DESC]];

ALL:指定在结果集中显示所有记录,包括重复行。ALL是默认设置。
DISTINCT:指定在结果集中显示所有记录,但不包括重复行。
TOP n[PERCENT]:指定从结果集中输出前n行,如果指定了PERCENT,表示从结果集中输出
前百分之n行。
:指定返回查询表中的所有字段。
<字段名>:指定要返回的字段。
<表达式>:返回由字段名、常量、函数以及运算符连接起来的表达式的值。
<别名>:指定在结果集中用”别名”来替换字段名或表达式进行显示。
FROM子句:用于指定查询的表或视图。
WHERE子句:用于设置查询条件。
GROUP BY子句:指明按照<字段名表>中的值进行分组,该字段的值相同的记录为一个组。分组后每个组只返回一行结果。如果GROUP子句带HAVING子句,则只有满足HAVING指定条件的组才予以输出。如果GROUP BY后有多个字段名,则先按第一个字段分组,再按第二个字段分组,依次类推。
HAVING子句:用来指定每一个分组内应该满足的条件,即对每个分组内的记录进行再筛选,它通常与GROUP BY子句一起使用。HAVING子句中的分组条件格式与WHERE子句中的条件格式类似。
ORDER BY子句:将查询结果按指定的次序表达式的值升序或降序排列。次序表达式可以是字段名、字段的别名或表达式。ASC指定升序排列,DESC指定降序排列,默认排序方式为ASC。

WHERE子句是对整个表中的数据筛选出满足条件的记录;
HAVING子句是对GROUP BY分组查询后产生的组设置的条件,所以是筛选出满足条件的组。
在HAVING子句中可以使用统计函数,而在WHERE子句则不能。
ORDER BY子句需放在SQL命令中的最后。

简单查询

1
2
//在学生表Student中只查询学生的学号sNo和姓名sName两个字段,并且字段名分别以别名“学号”和“姓名”进行显示。
SELECT sNo AS 学号, sName AS 姓名 FROM Student; //"学号"和"姓名"为别名

条件查询

  • WHERE常用的运算符及功能
运算符 功能说明
= 、> 、< 、>=、 <=、 !=、 < > 比较大小
BETWEEN AND、NOT BETWEEN AND 确定范围
IN、NOT IN 确定集合
LIKE、NOT LIKE 字符匹配
IS NULL、IS NOT NULL 判断空值
AND、OR、NOT 逻辑运算(多重条件查询)
1
2
3
//确定范围运算符使用格式
v BETWEEN v1 AND v2 //v>=v1 AND v<=v2
v NOT BETWEEN v1 AND v2 //v<v1 OR v>v2
1
2
//在学生表Student中查找计算机系的所有同学
SELECTFROM Student WHERE dept='计算机';

多重条件查询

逻辑运算符的优先级由高到低为:NOT,AND,OR,可以使用括号改变其优先级。

1
2
//在学生表Student中查找计算机系所有男同学。
SELECTFROM Student WHERE dept='计算机' AND sex='男';

模糊查询

当查询条件不知道完全精确的值时,还可以使用LIKE或NOT LIKE进行模糊查询,模糊查询也称为部分匹配查询。

1
<字段名>[NOT]LIKE <匹配串>

<字段名>必须是字符型的字段;
<匹配串>可以是一个完整的字符串,也可以是包含通配符的字符串。

  • 模糊查询时字符串中的通配符及其功能
通配符 功能说明 实例
% 代表0个或多个字符 ‘ab%表示’ab’后可接任意字符串
_ (下画线) 代表一个字符 ‘a _ b’表示’a’与’b’之间可为任意单个字符
[] 表示在某一范围内的字符 [0-9]表示0~9的字符
[ ^ ] 表示不在某一范围内的字符 [ ^ 0-9]表示不在0~9的字符
1
SELECT ∗ FROM Student WHERE sName LIKE '李%';

常用的统计函数及统计汇总查询

在SQL中除了可以使用算术运算符+(加法)、—(减法)、∗(乘法)和 / (除法)外,SQL还提供了一系列统计函数。

  • 常用的统计函数及功能
函数名称 功能说明
AVG(<字段名>) 求字段名所在列的平均值(必须是数值型列)
SUM(<字段名>) 求字段名所在列的总和(必领是数值型列)
MAX(<字段名>) 求字段名所在列的最大值
MIN(<字段名>) 求字段名所在列的最小值
COUNT(+) 统计表中记录的个数
COUNT([DISTINCT] <字段名>) 统计字段名所在列非空值的个数,DISTINCT表示不包括字段的重复值

上述函数中除COUNT(*)外,其他函数在计算过程中均忽略NULL值。

1
2
//在成绩表Score中统计所有成绩grade的平均值。
SELECT AVG(grade) AS平均成绩 FROM Score; //"平均成绩"是表达式AVG(grade)的别名

ORDER BY 字句

ORDER BY是一个可选的子句,它允许根据指定字段的值按照升序或者降序的顺序显示查询结
果。其中默认为升序排列,用ASC表示,降序排列用DESC表示。

1
2
//在成绩表Score中查询课程号cNo为c001的学生的学号sNo和成绩grade,并按成绩降序排列。
SELECT sNo, grade FROM Score WHERE cNo='c001' ORDER BY grade DESC

分组数据

统计函数只能产生单一的汇总数据,使用GROUP BY子句,则可以生成分组的汇总数据。

GROUP BY子句可以按关键字段的值来组织数据,关键字段值相同的为一组。

1
2
//在成绩表Score中查询每门课程的课程号cNo和学生人数。
SELECT cNo, COUNT(∗) AS 人数 FROM Score GROUP BY cNo; //"人数"是别名

JDBC

JDBC是为在Java程序中访问数据库而设计的一组Java API,是Java数据库应用程序开发中的一项核心技术。

JDBC概述

JDBC的含义是Java Database Connectivity,它是Java程序中访问数据库的标准API。

一般来说,JDBC做三件事:与数据库建立连接;发送SQL语句;处理SQL语句执行的结果。

JDBC类型

JDBC不能直接访问数据库,必须依赖于数据库厂商或第三方提供的JDBC驱动程序。

类型1:JDBC-ODBC桥加ODBC驱动程序

JDBC-ODBC桥由Sun公司开发,是JDK提供的标准API。

于JDBC-ODBC桥先调用ODBC再由ODBC去调用本地数据库接口访问数据库,所以执行效率比较低。

需要客户端预装对应的ODBC驱动程序,所以不适合Internet/Intranet应用。

类型2:本地API部分用Java编写的驱动程序

是部分使用Java语言编写和部分使用本机代码编写的驱动程序,它将JDBC的调用直接翻译成对特定DBMS(如MySQL、SQL Server、Oracle等)客户端API的调用后再去访问数据库。

是用特定的DBMS客户端取代JDBC-ODBC桥和ODBC,因此也具有与JDBC-ODBC桥相类似的局限性。

类型3:JDBC网络协议纯Java驱动程序√

用纯Java语言编写。

它将JDBC的调用转换成与DBMS无关的网络协议命令,之后发送给一个网络服务器中的数据库中间件,该中间件进一步将网络协议命令转换成某种DBMS所能理解的操作命令。

网络协议是平台无关的。

种驱动程序不调用任何本地代码。

在服务器上配置有数据库驱动程序,并且由于多了一个中间件传递数据,它的执行效率还不是最好。

类型4:本地协议纯Java驱动程序√

将JDBC调用直接转换成特定DBMS所使用的网络协议,这将允许从客户机上直接调用DBMS服务器,访问速度快。

完全由Java语言实现,实现了平台的独立性。

但对于不同的数据库需要下载不同的驱动程序。

使用JDBC开发数据库应用程序

JDBC API主要位于Java的java.sql包与javax.sql

  • JDBC中主要的类与接口
类与接口 功能说明
DriverManager 负责加载各种不同驱动程序(driver)并根据不同的请求。向调用者返回相应的数据库连接(conneetion)
Connection 数据库连接,负责与数据库间进行通信,SQL执行以及事务处理都是在某个特定连接环境中进行的,并可以产生用以执行SQL的Statement对象
Statement 用以执行不含参数的静态SQL查询和更新,并返回执行结果
PreparedStatement 用以执行包含参数的动态SQL查询和更新(在服务器端编译,允许重复执行以提高效率
CallableStatement 用以调用数据库中的存储过程
ResultSet 用以获得SQL查询结果
SQLException 代表在数据库连接的建立。关闭或SQL语句的执行过程中发生了异常

JDBC驱动程序开发商已提供了对这些接口的实现类,所以在使用时实际上是调用这些接口实现类中的方法。

建立与数据库的链接

数据库连接的建立包括两个步骤:
一是加载相应数据库的JDBC驱动程序;
二是创建数据库连接。

加载JDBC驱动程序
1
java.lang.Class.forName(JDBCDriverClass);

该方法是Class类的静态方法,参数JDBCDriverClass是要加载的JDBC驱动程序类的名称,它是以字符串形式表达的类名。

  • 数据库的驱动程序类
数据库 驱动程序类 所在包
Access sun.jdbc.odbe.JdbcOdbeDriver 捆绑JDK
SQL Server com.microsoft.sqlserver.jdbc.SQLServerDriver sqljdbc42.jar
MySQL com.mysql.jdbc.Driver mysql-connector-java-5.1.45-bin.jar
Oracle oracle.jdbc.driver.OracleDriver ojdbc6.jar
创建数据库连接

由于JDBC驱动程序与数据库的连接是以对象的形式表示的,所以创建数据库连接也称创建数据库连接对象。

  • DriverManager类的常用方法
常用方法 功能说明
public static Conection getConnection(String url, String user, String password) 建立JDBC驱动程序到指定数据库URL的连接。其中url提供了一种标识数据库的方法,user为用户名,password为密码
public static Driver getDriver(Stringurl) 返回url所指定的数据库连接的驱动程序
1
Connection conn = DriverManager.getConnection(String url, String user, String password);
1
jdbc: < subprotocol > : < subname >

< subprotocol >是子协议,指数据库连接的方式;
< subname >是子名称,是一种标识数据库的方法。

  • 数据库的URL
数据库 URL
Access jdbc:odbe:dataSource
SQL Server jdbc:sqLserver://hostname:port#;DatabaseName-dbname
MySQL jdbc:mysl://hostname/dbname
Oracle jdbc:oracle:thin:@hostname:port#:oracleDBSID

1)Access数据库的URL是jdbc:odbc:dataSource。ODBC数据源可以使用Windows下的
ODBC数据源管理器(data source administrator)来创建。

2)SQL Server数据库的URL指定包含数据库的主机名(hostname)、数据库监听输入连接
请求的端口号(port#)和数据库名(dbname)

1
Connection conn = DriverManager.getConnection ("jdbc:sqlserver://1ocalhost:1433;DatabaseName=StudentScore","sa","123456");

3)MySQL数据库的URL指定包含数据库的主机名(hostname)和数据库名(dbname)。

1
Connection conn = DriverManager.getConection("jdbe:mysql://localhost/StudentScore", "root", "123456");

4)Oracle数据库的URL指定主机名(hostname)、数据库监听输入连接请求的端口号
( port# ) 以及数据库名( oracleDBSID ) 。

1
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:StudentScore", "root", "123456");
  • Connection接口的常用方法
常用方法 功能说明
public Statement createStatement() 创建一个Statement对象用束将SQL语句发送到数据库
public Statement createStatement(int resultSetType, int resultSetConcurreney) 功能同上,参数resultSetType指定结果集类型,有三个取值:
TYPE_FORWORD_ONLY表示只可向前移动记录指针;
TYPE_SCROLL_INSENSITIVE表示可双向移动记录指针,但不及时更新,也就是如果数据库中的数据修改过,并不在ResultSet中反映出来;
TYPE_SCROLL_SENSITIVE表示可双向移动记录指针,并及时跟踪据库的更新,以便更改ResultSet中的数据。
参数resultSetConcurreney指定结果集的并发模式,有两个取值:
CONCUR_READ_ONLY表示不能用结果集更新数据库中的表;
CONCUR_UPDATABLE表示能用结果集更新数据库中的表
public PreparedStatement prepareStatement(String sql) 创建一个PreparedStatement对象来将具有参数的动态SQL语句发送到数据库
public CallableStatement prepareCall(String sql) 创建一个CallableStatement对象来调用数据库的存储过程
public void close() 断开连接,释放此Conneetion对象的数据库和JDBC资源
public boolean isClosed() 用于判断Connetion对象是否已经被关闭
public void setAutoCommit(boolean autoCommit) 设置是否关闭自动提交模式
public void commit() 提交SQL语句,使从上一次提交/回滚以来进行的所有更改生效
public void rollback() 取消SQL语句的执行,撤销在当前事务中进行的所有更改

执行SQL语句

执行SQL语句包括两个步骤:
一是创建Statement对象;
二是通过调用该对象的相应方法将SQL语句发送到所连接的数据库去执行。

创建Statement对象

创建一个Statement接口对象,该对象将各种SQL语句发送到所连接的数据库中执行。

1
Statement stmt = conn.createStatement();
调用Statement对象的相应方法将SQL语句发送到所连接的数据库

如果SQL语句运行后产生结果集,Statement对象会将结果集封装成ResultSet对象并返回。

  • Statement接口的常用方法
常用方法 功能说明
public ResultSet executeQuery(String sql) 执行给定的SQL语句,并将结果封装在结果集ResultSet对象中返回
public int executeUpdate(String sql) 执行给定的SQL语句,该语句可能是INSERT、UPDATE或DELETE或是不返回任何内容的SQL语句(如DDL语句)。该语句的返回值是一个整数,表示受影响的行数(即更新计数)
public boolean execute(String sql) 执行给定的SQL语句。如果执行的是SELECT语句,则返回true,调用getResultSet()方法获得执行SQL语句的返回结果;如果执行的是INSERT、UPDATE或DELETE,或者不返回任何内容的SQL语旬,则返回false,调用getUpdateCount()方法获得执行SQL语句的返回结果
public ResultSet getResultSet() 以ResultSet对象的形式返回当前结果。如果结果是更新计数(即执行executeUpdate()方法)或没有结果,则返回null
以更新计数的形式返回当前结果;如果结果为ResultSet 对象或
public int getUpdateCount() 没有更多结果,则返回-1。每个结果只应调用一次该方法
public void close() 释放此Statement对象的数据库和JDBC资源

在executeQuery()与executeUpdate()方法中的字符串参数,如果超出一行将出现编译错误,所以在构造SQL参数时,需要将表达多行的字符串加上双引号并将各行用加号“+”连接起来。

1
2
3
4
5
6
String sqlStr = "SELECT sNo,sName,sex,age FROM Student WHERE dept='计算机'";
ResultSet rs = stmt.executeQuery(sqlStr);//执行查询操作并将查询结果存放到ResultSet对象rs中

String sqlStr = "INSERT INTO Student(sNo,sName,sex,age,dept)"+
"VALUES('201201009','王毅','男',18,'外语')";
stmt.executeUpdate(sqlStr);

处理返回结果

结果集是包含SQL的SELECT语句中符合条件的所有行,这些行的全体称为结果集,返回的结果集是一个表,而这个表就是ResultSet接口的对象。

在结果集中通过记录指针(也称为游标)控制具体记录的访问,记录指针指向结果集的当前记录。在结果集中可以使用getXXX()方法从当前行获取值。

  • ResultSet接口的常用方法
常用方法 功能说明
public boolean absolute(int row) 将记录指针移动到结果集的第row条记录
public boolean relative(int row) 按相对行数(或正或负)移动记录指针
public void beforFirst() 将记录指针移动到结果集的头(第一条记录之前)
public boolean first() 将记录指针移动到结果集的第一条记录
public boolean previous() 将记录指针从结果集的当前位置移动到上一条记录
public boolean next() 将记录指针从结果集的当前位置移动到下一条记录
public boolean last() 将记录指针移动到结果集的最后一条记录
public void afterLast() 将记录指针移动到结果集的尾(最后一条记录之后)
public boolean isAfterlLast() 判断记录指针是否位于结果集的尾(最后一条记录之后)
public boolean isBeforeFirst() 判断记录指针是否位于结果集的头(第一条记录之前)
public boolean isFirst() 判断记录指针是否位于结果集的第一条记录
public boolean isLast() 判断记录指针是否位于结果集的最后一条记录
public int getRow() 返回当前记录的行号
public String getString(String columnLabel) 返回当前记录字段名为columnLabel的值
public String getString(int columnIndex) 返回当前行第columnIndex列的值,类型为String
public int getInt(int columnIndex) 返回当前行第columnIndex列的值,类型为int
public Statement getStatement() 返回生成结果集的Statement对象
public void close() 释放此ResultSet对象的数据库和JDBC资源
public ResdtSetMetaData getMetaData() 返回结果集的列的编号、类型和属性

记录指针的最初始位置位于第一条记录之前,即结果集的头。

第一次调用next()方法使记录指针移到第一条记录,当记录指针移动到结果集的尾时其返回false。

在使用ResultSet对象的getXXX()方法对结果集中的数据进行访问时,一定要使数据库中字段的数据类型与Java的数据类型相匹配。

使用Statement stmt = conn.createStatement();语句,虽然可以得到Statement
类的对象stmt,通过语句ResultSet rs = stmt.executeQuert("SELECT∗FROM Student");也可以得到相应的结果集rs,但这种类型的结果集rs不能来回移动记录指针读取记录。如果需要来回移动记录指针读取结果集,创建Statement语句的时候需要使用如下带参数的方法定义:

1
2
3
Statement createStatement(int resultSetType, int resultSetConcurrency);

conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
  • 常用的SQL数据类型与Java数据类型之间的对应关系
SQL数据类型 Java数据类型 结果集中对应的方法
integer/ int int getInt()
smallint short getShort()
float double getDouble()
double double getDouble()
real float getFloat()
varchar/char/varchar2 java.lang.String getString()
boolean boolean getBoolean()
date java.sql.Date getDate()
time java.sql.Time getTime()
blob java.sql.Blob getBlob()
clob java.sql.Clob getClob()
1
2
3
4
5
6
//在使用getXXX()方法进行取值时,可以通过字段名或列号来标识要获取数据的列。
//下面两条语句的作用是一样的,都是读取当前行中sNo字段的内容。
String no = rs.getString("sNo");

//在ResultSet中,字段是从左至右编号的,并且从1开始。
String no = rs.getString(1);

关闭创建的各种对象

关闭的次序是:
①关闭结果集对象;
②关闭Statement对象;
③关闭连接对象。

在任一时间内,一个给定的Statement对象只能打开一个结果集。当重新使用同一个Statement对象时,将会关闭先前生成的任何结果集。
在重新执行Statement对象之前,需要完成对当前ResultSet对象的处理。

1
2
3
4
5
6
7
8
9
try {
if(rs!=null) rs.close(); //关闭结果集对象
if(stmt!=null) stmt.close(); //关闭Statement对象
if(conn!=null) conn.close(); //关闭JDBC与数据库的连接
}

catch(Exception e){
e.printStackTrace();
}

数据库的进一步操作

JDBC中执行SQL对表的查询有三种方式:
不含参数的静态查询(静态SQL语句):Statement;
含有参数的动态查询(动态SQL语句):PreparedStatement;
存储过程调用:CallableStatement。

Statement接口

谓静态SQL语句是指在执行executeQuery()、executeUpdate()等方法时,作为参数的SQL语句的内容固定不变,也就是SQL语句中没有参数。

PreparedStatement接口

就是可以在SQL语句中提供参数,这使得可以对相同的SQL语句替换参数从而多次使用。
预编译语句:先让数据库管理系统在内部通过预先编译,形成带参数的内部指令,并保存在PreparedStatement接口的对象中。提高程序的执行效率。

PreparedStatement对象也可用于执行不带参数的预编译的SQL语句。

  • PreparedStatement接口的常用方法
常用方法 功能说明
public boolean execute() 执行任何种类的SQL的语句,可能会产生多个结果集
public ResultSet executeQuery() 执行SQL查询命令SELECT并返回结果集
public int executeUpdate() 执行修改的SQL指令如INSERT、DELETE、UPDATE等
public ResultSetMetaData getMetaData() 返回结果集ResultSet的有关字段的信息
public void clearParameters() 清除当前所有参数的值
public void setBoolean(int parameterIndex, boolean x) 给第parameterIndex个参数设置boolean型值x
public void setInt(int parameterIndex, int x) 给第parameterIndex个参数设置int型值x
public void setDouble (int parameterIndex, double x) 给第parameterIndex个参数设置double型值x
public void setString (int parameterlndex, String x) 给第parameterIndex个参数设置String型值x
public void setDate(int parameterIndex, Date x) 给第parameterIndex个参数设置Date型值x
public void setObject (int parameterIndex, Object x) 给第parameterIndex个参数设置Object型值x

可通过Connection的prepareStatement()方法创建PreparedStatement对象。在创建用于PreparedStatement对象的动态SQL语句时,可使用?作为动态参数的占位符。

1
2
String insertSql = "INSERT INTO Student(sNo,sName,sex,age,dept)VALUES(?,?,?,?,?);";
PreparedStatement ps = conn.prepareStatement(insertSql);

在执行带参数的SQL语句前,必须对?进行赋值。这可以通过使用setXXX()方法,通过占位符的下标完成对输入参数的赋值(下标是从1开始的),XXX根据不同的数据类型进行选择。

CallableStatement 接口

存储过程是一组SQL语句,它们形成一个相对独立的逻辑单元,能完成特定的任务。

该接口可以处理一般的SQL语句,也可以处理IN(输入)参数,同时它还定义了OUT(输出)参数及INOUT(输入输出)参数的处理方法

IN(输入):接收传递给存储过程的值;
OUT(输出):用于存储过程执行结束后接收一个返回值,所以在调用存储过程时,不需要向OUT参数传递任何值;
INOUT(输入输出):当存储过程被调用时,需要向该参数传递一个值,当存储过程执行完后该参数还将接收一个返回值;

  • CallableStatement接口的常用方法
常用方法 功能说明
public void setInt( String parameterName,int x) 将名为parameterName的参数设置为int型值x
public void setFloat(String parameterName,float x) 将名为parameterName的参数设置为float 型值x
public void setDouble(String parameterName, double x) 将名为parameterName的参数设置为double型值x
public void set Boolean(String parameterName, boolean x) 将名为parameterName的参数设置为boolean型值x
public void setString(String parameterName, String x) 将名为parameterName的参数设置为String 型值x
public void setDate(String parameterName, Date x) 将名为parameterName的参数设置为Date型值x
public void setObject (String parameterName, Object x) 将名为parameterName的参数设置为Object型值x
public int getInt(int parameterIndex) 返回第parameterIndex个参数int型值
public int getInt(String parameterName) 返回参数名为parameterNam的int 型值
public float getFloat(int parameterIndex) 返回第parameterIndex个参数float型值
public float getFloat(String parameterName) 返回参数名为parameterNam的float型值
public double getDouble(int parameterIndex) 返回第parameterIndex个参数double型值
public double getDouble(String parameterName) 返回参数名为parameterNam的double型值
public String getString(int parameterIndex) 返回第parameterIndex个参数String 型值
public String getString(String parameterName) 返回参数名为parameterNam的String 型值
public Object getObject(String parameterName) 返回参数名为parameterNam的Object型值

创建一个CallableStatement对象可以使用Connection接口的prepareCall()方法,调用格式有三种:

1
2
3
4
5
6
7
8
//不带参数的存储过程调用
CallableStatement cs = conn.prepareCall("{call存储过程名}");

//带若干个参数的存储过程调用
CallableStatement cs = conn.prepareCall("{call存储过程名(?,?,…)}"); //?是占位符

//带若干个参数并且有返回值参数的存储过程调用(实际是函数调用)
CallableStatement cs = conn.prepareCall("{?=call存储过程名(?,?,…)}"); //?是占位符

在执行带参数的SQL语句前,必须对IN和INOUT参数的占位符”?”进行赋值

1
2
3
cs.registerOutParamenter(int index,int sqlType);

cs.registerOutParamenter(1, java.sql.Types.INTEGER);

利用CallableStatement执行存储过程时,其执行结果可能是多个ResultSet、多次修改记录或两者都有的情况。所以对CallableStatement一般调用execute()方法执行SQL语句。

1
cs.execute();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//使用CallableStatement实现对数据库StudentScore中Student表的各种存储过程的调用。

//第一个存储过程addStudent
CREATE PROCEDURE addStudent(no CHAR(9), name CHAR(12), sex CHAR(2), age int, dept CHAR(50))
BEGIN
INSERT INTO Student(sNo, sName, sex, age, dept) VALUES(no, name, sex, age, dept);
END

//第二个存储过程getCount
CREATE PROCEDURE getCount(OUT total int)
BEGIN
SELECT COUNT(*) INTO total FROM Student;
END

//第三个存储过程addSub
CREATE PROCEDURE addSub(INOUT numl int, INOUT num2 int)
BEGIN
SET numl = numl + num2;
SET num2 = numl - num2 - num2;
END
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
//使用CallableStatement按口,实现对存储过程的调用

import java.sql.*;

public class App17_18 {
private static String driver = "com.myaql.jdbc.Driver";
private static String url = "jdbc:mysql://localhost/StudentScore?useSSL=false";
private static String user = "root";
private static String password = "";

public static void main(String[] args) {
Connection conn = null;
CallableStatenent cs = null;
ResultSet rs = null;
String callsql1 = "{call addStudent(?,?,?,?,?)}"; //"?"为存储过程IN型参数的占位符
String callSql2 = "{call getCount(?)}"; //"?"为存储过程our型参数的占位符
String callSql3 = "{call addSub(?, ?}"; //"?"为存储过程INOUT型参数的占位符
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, user, password);

cs = conn.prepareCall(callSql1); //创建用于执行存储过程的对象cs
cs.setstring(1, "201201009"); //对第14行中的第1个IN参数赋值
es.setString(2, "王毅");
cs.setString(3, "男");
ca.setInt(4, 18);
cs.setStrsing(5, "外语”);
cs.execute(); //执行sQL, 的存储过程adStudent

ca = conn.prepareCall(callSql2); //创建用于执行存储过程的对象ca
//下面语句注册getCount存鍺过程OUT参数的类型
cs.registerOutParameter(1, java.sql.Types.INTEGER);
c8.execute(); //执行SQL的存储过程getCount
int total = es.getInt(1); //返回存储过程getCount的第1个out参数
System.out.printIn("总人数为: " + total);
int a=5;
int b=3;

cs = conn. prepareCall(cal1Sq13); //创建用于执行存储过程的对象cs
ca.setInt(1, a); //对第16行中的第1个INOUT型参数赋值
cs. setInt(2, b); //对第16行中的第2个INOUT型参数赋值

//下面两条语句分别注册addSub第1.2个INOUT参数的类型
c8.regiaterOutParameter(1, java.sq1.Types.INTEGER);
c8.registerOutParameter(2, java.sql.Types.INTEGER);
c8.execute();

//执行SQL的存储过程adSub
int sum = c8.getInt(1);
//返回存储过程addSub的第1个INOUT型参数的值
int sub = es.getInt(2);
//返回存储过程adSub的第2个INOUT型参数的值
System.out.println(a + "" + b + "的和: " + sun + ",差: " + sub);

} catch(Exception e) {
e.printStackTrace();
} finally {
try {
if(cs != mu11) cs.close();
if(conn != mul1) conn.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}
}

获取元数据

谓元数据(meta data)就是有关数据库和表结构的信息,如数据库中的表、表的字段、表的索引、数据类型、对SQL的支持程度等信息。

JDBC提供DatabaseMetaData接口用来获取数据库范围的信息,提供ResultSetMetaData接口用来获取特定结果集ResultSet的信息,如字段名和字段个数等。

DatabasedMetaData接口-获取数据库信息

DatabaseMetaData接口主要是用来得到关于数据库的信息,如数据库中所有表名、系统函数、关键字、数据库产品名和数据库支持的JDBC 驱动程序名等。

1
DatabaseMetaData dmd = conn.getMetaData();
  • DatabaseMetaData接口的常用方法
方法名称 功能说明
public boolean supportsOuterJoins() 判断数据库是否支持外部连接
public boolean supportsStoredProcedures() 判断数据库是否支持存储过程
public String getURL() 返回用于连接数据库的URL地址
public String getUserName() 返回当前用户名
public String getDatabaseProductName() 返回使用的数据库产品名
public String getDatabaseProductVersion() 返回使用的数据库版本号
public String getDriverName() 返回用以连接的驱动程序名称
public String getDriverVersion() 返回用以连接的驱动程序版本号
public ResultSet getTypeInfo() 返回当前数据库中支持的所有数据类型的描述

ResultSetMetaData接口-获取结果集信息

ResultSetMetaData接口主要用来获取结果集的结构。例如,结果集字段的数量、字段的名字等。

1
ResultSetMetaData rsMetaData = rs.getMetaData();
  • ResultSetMetaData接口的常用方法
常用方法 功能说明
public int getColumnCount() 返回此ResultSet对象中的字段数
public String getColumnName(int column) 返回指定列的名称
public int getColumnType(int column) 返回指定列的SQL类型
public int getColumnDisplaySize(int column) 以字符为单位返回指定字段的最大宽度
public boolean isAutoIncrement(int column) 判断是否自动为指定字段进行编号,判断给定字段是否可以为null,返回值是columnNoNulls
public int isNullable(int column) columnNullable或columnNullableUnknown之一
public boolean isSearchable(int column) 判断是否可以在WHERE子句中使用指定的字段
public boolean isReadOnly(int column) 判断指定的字段是否为只读

事物操作

事务是保证数据库中数据完整性与一致性的重要机制。事务由一组SQL语句组成,这组语句要么都执行,要么都不执行,因此事务具有原子性。

已提交事务是指成功执行完毕的事务,未能成功执行完成的事务称为中止事务,对中止事务造成的变更需要进行撤销处理,称为事务回滚

事物操作基于connection

setAutoCommit()

事务操作默认是自动提交。

即每一条SQL语句都被看作是一个事务,对数据库的更新操作成功后,系统将自动调用commit()方法提交。若把多个SQL语句作为一个事务就要关闭这种自动提交模式,这是通过调用当前连接的setAutoCommit(flase)方法来实现的。

commit()

当连接的自动提交模式被关闭后,SQL语句的执行结果将不被提交。

rollback()

当调用ommit()法进行事务处理时,只要事务中的任何一条SQL语句没有生效,就会抛出SQLException异常。

这个方法将取消事务,并将该事务已执行部分对数据的修改恢复到事务执行前的值。

  • 回滚保存点
1
2
Savepoint s1 = conn.setSavepoint();
conn.rollback(s1);

在窗口中访问数据库

本章小结

  • 数据库、数据库管理系统和数据库系统是三个不同的概念。
  • 一个关系型数据库是由一个或多个二维表构成的。表的列称为字段,行称为记录。
  • 数据库中的表有三种约束:域约束、主码约束和外码约束。
  • SQL是结构化查询语言(Structured Query Language)的英文缩写,是用来定义数据库表和完
    整性约束以及访问和操纵数据的语言。
  • JDBC是为在Java程序中访问数据库而设计的一组Java API,包含有一组类与接口,用于与数据库的连接、把SQL语句发送到数据库、处理SQL语句的结果以及获取数据库的元数据等。
  • 使用Java开发任何数据库应用程序都需要四个接口:Driver、Connection、Statement和ResultSet。这些接口定义了使用SQL语句访问数据库的方法。JDBC驱动程序开发商或第三方已实现了这些接口中的方法。
  • 使用JDBC访问数据库的一般步骤为:加载驱动程序、建立与数据库的连接、创建执行方式语句、执行SQL语句、处理返回结果和关闭创建的各种对象。
  • JDBC中有三种SQL查询方式:不含参数的静态查询、含有参数的动态查询和存储过程调用。这三种方式分别对应Statement、PreparedStatement和CallableStatement接口。
  • JDBC通过Statement接口实现静态SQL查询,通过PreparedStatement接口实现动态SQL查询,通过CallableStatement接口实现存储过程的调用。
  • JDBC通过ResultSet返回查询结果集,并提供记录指针对其记录进行定位。
  • JDBC通过DatabaseMetaData接口获得关于数据库的信息,通过ResultSetMetaData接口获取结果集的结构。
  • JDBC默认的事务提交方式是自动提交,可以通过setAutoCommit()方法控制事务提交方式,使用rollback()方法可实现事务回滚。

课后习题

  • 写出在数据库StudentScore的Student表中查找所有年龄大于或等于19的同学的SQL语句。
  • 写出姓名为“刘韵”的学生所学课程名称及成绩的SQL语句。
  • 描述JDBC中Driver、Connection、Statement和ResultSet接口的功能。
  • 使用Statement接口和PreparedStatement接口有什么区别?
  • 归纳一下使用JDBC进行数据库访问的完整过程。
  • 如何在结果集中返回字段的数目?如何在结果集中返回字段名?
  • 编写一个应用程序,使其可以从StudentScore数据库的某个表中查询一个字段的所有信息。
  • 创建一个名为Books的数据库,并在其中建立一个名为Book的表,字段包括书名、作者、出版社、出版时间和ISBN。编写一个应用程序,运用JDBC在该数据库中实现增加、删除和修改数据的功能。
  • 假设在StudentScore数据库的Student表中,存在多个姓氏相同的人,根据这种情况建立查询,要求提供一个合适的图形界面,用户可以滚动查看查询记录。

第十八章 Java网络编程

网络基础

TCP/IP

网络通信协议是计算机间进行通信所遵守的各种规则的集合。

Internet的主要协议有:网络层的IP协议;传输层的TCP和UDP协议;应用层的FTP、HTTP、SMTP等协议。其中传输控制协议(TransportControl Protocol, TCP)和网际互联协议(Internet Protocol,IP)是Internet的主要协议。

TCP/IP网络参考模型包括四个层次:应用层、传输层、网络层、链路层。

链路层

链路层也称为数据链路层或网络接口层。通常包括操作系统中的设备驱动程序和计算机中对应的网络接口卡。它们一起处理与电缆(或其他任何传输媒介)有关的物理接口细节。

网络层

网络层对TCP/IP网络中的硬件资源进行标识。

传输层

在TCP/IP网络中,不同的机器之间进行通信时,数据的传输是由传输层控制的,这包括数据要发往的目的主机及应用程序、数据的质量控制等。

用于通信的端点是由Socket来定义的,而Socket是由IP地址和端口号组成的。

TCP是通过在端点与端点之间建立持续的连接而进行通信的。整个字节流到达接收端时完好无缺。

UDP是一种无连接的传输协议,传输方式是无序的,也不能确保绝对安全可靠,但它非常简单,也具有比较高的效率。

当对所传输的数据有时序性和可靠性等要求时,应使用TCP;
当传输的数据比较简单、对时序等无要求时,UDP能发挥更好的作用。

应用层

大多数基于Internet的应用程序。
FTP、HTTP、SMTP、POP3、Telnet等。

通信端口

一台机器只能通过一条链路连接到网络上,网络端口号(port)就是用于区分一台主机中的不同应用程序。

个标记计算机逻辑通信信道的正整数。

其范围为0~65 535,其中,0~1023被系统保留,专门用于那些通用的服务(well-known service)

HTTP服务的端口号为80,Telnet服务的端口号为21,FTP服务的端口号为23。

IP地址和端口号组成了所谓的Socket。

Socket是网络上运行的程序之间双向通信链路的最后终结点,是TCP和UDP的基础。

URL概念

URL是统一资源定位器(Uniform Resource Locator)的英文缩写。

URL的基本结构由五部分组成,其格式如下:

1
传输协议://主机名:端口号/文件名#引用

(1)传输协议(protocol):指所使用的协议名,如HTTP、FTP等。
(2)主机名(hostname):指资源所在的计算机。可以是IP地址,也可以是计算机的名称或域名。
(3)端口号(portnumber):一个计算机中可能有多种服务,如Web服务、FTP服务或自己建立的服务等。为了区分这些服务,就需要使用端口号,每一种服务用一个端口号。
(4)文件名(filename):包括该文件的完整路径。在HTTP中,有一个默认的文件名是index.html,因此,下列两个地址是等价的。

1
2
http://java.sun.com
http://java.sun.com/index.html

( 5 ) 引用( reference ) : 就是资源内部的某个参考点, 如
1
http://java.sun.com/index.html#chapter1。

Java语言的网络编程

Java语言的网络编程分为三个层次。

  • 从网络上下载小程序
  • 通过URL类的对象指明文件所在位置
  • 利用java.net包中提供的类直接在程序中实现网络通信

针对不同层次的网络通信,Java语言提供的网络功能有四大类:URL、InetAddress、Socket、Datagram。

  • URL:面向应用层,通过URL, Java程序可以直接输出或读取网络上的数据。
  • InetAddress:面向的是IP层,用于标识网络上的硬件资源。
  • Socket和Datagram:面向的是传输层。Socket使用TCP,这是传统网络程序最常用的方式,可以想象为两个不同的程序通过网络的通信信道进行通信;Datagram则使用UDP,是另一种网络传输方式,它把数据的目的地址记录在数据包中,然后直接放在网络上。

Java语言网络编程中主要使用的java.net包中的类如下。

  • 面向IP层的类:InetAddress;
  • 面向应用层的类:URL、URLConnection;
  • TCP相关类:Socket、ServerSocket;
  • UDP 相关类: DatagramPacket 、DatagramSocket 、MulticastSocket。

可能产生的异常包括BindExceptio 、ConnectException 、MalformedURLException 、NoRouteToHostException 、ProtocolException 、SocketException 、Unknown-HostException、UnknownServiceException。

URL编程

创建URL对象

  • 创建URL对象的构造方法
构造方法 功能说明
public URL(String spec) 使用URL形式的字符串spec创建一个URL对象
public URL(String protocol, String host, int port, String file) 创建一个协议为protocol、主机名为host、端口号为port、待访问的文件名为file的URL对象.
public URL(String protocol, String host, String file) 创建一个URL对象,参数的含义同上、但使用默认端口号
public URL(String protocol, String host, int port, String file, URLStream Handler handler) 创建一个协议为protocol、主机名为host、端口号为port、待访问的文件名为file、URL流句柄为handler的URL对象
public URL(URL context,String spec) 使用已有的URL对象context和URL形式的字符串spec创建URL对象
public URL(URL context, String spee, URI StreamHandlerhandler) 参数同上,但创建的URL对象包含流句柄handler

在创建URL对象时,若发生错误,系统会产生MalformedURLException异常,这是非运行时异常,必须在程序中捕获处理。

  • URL类的常用方法
常用方法 功能说明
public boolean equals(Object obj) 判断两个URL是否相同
public final Object getContent() 获取URL连接的内容
public String getProtocol() 返回URL对象的协议名称
public String getHost() 返回URL对象访问的计算机名称
public int getPort() 返回URL对象访问的端口号
public String getFile() 返回URL指向的文件名
public String getPath() 返回URL对象所使用的文件路径
public String getRef() 返回URL对象的引用字符串,即获取参考点
public URLConnction openConnection() 打开URL指向的连接
public final InputStream openStream() 打开输人流
protected void set(String protocol,String host, int port, String file, String ref) 用给定参数设置URL中各字段的内容
public String toString() 返回整个URL字符串

使用URL类访问网络资源

用Java语言实现底层网络通信

InetAdress程序设计

Internet上主机的地址有两种表示方式,即域名和IP地址。

在已知一个InetAddress对象时,就可以通过一定的方法从中获取Internet上主机的地址(域名或IP地址)。

InetAddress类没有构造方法, 因此不能用new运算符来创建InetAddress对象,通常是用它提供的静态方法来获取。

  • InetAddress类的常用方法
常用方法 功能说明
public static InetAddress getByName(String host) 通过给定的主机名host, 获取InetAddress 对象的IP地址
public static InetAddress getByAddress( byte[] addr) 通过存放在字节数组中的IP地址,返回一个InetAddress对象
public static InetAddress getLocalHost() 获取本地主机的IP地址
public byte[] getAddress() 获取本对象的IP地址,并存放在字节数组中
public String getHostAddress() 利用InetAddress对象,获取该对象的IP地址
public String getHostName() 利用InetAddress对象,获取该对象的主机名
public String toString() 将IP地址转换成字符串形式的域名

该表中给的static方法通常会产生UnknownHostException异常,应在程序中捕获处理。

1
2
InetAddress myIPAddress = InetAddress.getLocalHost();
InetAddress myServer = InetAddress.getByName("www.tom.com");

基于连接的Socket通信程序设计

Socket通信属于网络底层通信,它是网络上运行的两个程序间双向通信的一端,它既可以接收请求,也可以发送请求,利用它可以较方便地进行网络上的数据传输。

Socket 是实现客户与服务器(Client/Server, C/S)模式的通信方式。

Socket通信机制的基本概念

1)建立连接
2)连接地址
3)TCP/IP Socket通信

服务器端套接字使用的是ServerSocket类对象,客户端套接字使用的是Socket类对象,由此区分服务器端和客户端。

Socket类

java.net.Socket继承自java.lang.Object类。

Socket类用在客户端,用户通过创建一个Socket对象来建立与服务器的连接。

通信开始之前先由通信双方确认身份并建立一条专用的虚拟连接通道;
然后它们通过这条通道传送数据信息进行通信;
当通信结束时再将原先所建立的连接拆除。

  • Socket类的构造方法
构造方法 功能说明
public Socket(String host ,int port) 在客户端以指定的服务器地址host和端口号port,创建一个Socket对象,并向服务器端发出连接请求
public Socket(InetAddress address,int port) 同上,但IP地址由address指定
public Socket(String host, int port, boolean stream) 同上,但若stream为真,则创建流Socket对象,否则创建
public Socket(InetAddress host, int port, boolean stream) 数据报Socket对象同上,但IP地址由host指定
  • Socket类的常用方法
常用方法 功能说明
public InetAddress getInetAddress() 获取创建Socket对象时指定的服务器的IP地址
public InetAddress getLocalAddress() 获取创建Socket对象时客户计算机的IP地址
public InputStream getInputStream() 为当前的Socket 对象创建输人流
public OutputStream getOutputStream() 为当前的Socket对象创建输出流
public int getPort() 获取创建Socket时指定远程主机的端口号
public void setReceiveBufferSize(int size) 设置接收缓冲区的大小
public int getReceiveBufferSize() 返回接收缓冲区的大小
public void setSendBufferSize(int size) 设置发送缓冲区的大小
public int getSendBufferSize() 返回发送缓冲区的大小
public void close() 关闭建立的Socket连接

ServerSocket类

java.net.ServerSocket继承自java.lang.Object

  • ServerSocket类的构造方法
构造方法 功能说明
public ServerSocket(int port) 以指定的端口port创建ServerSocket对象,并等候客户端的连接请求。端口号必须与客户端呼叫用的端口号相同
public ServerSocket(intport, int backlog) 同上,但以backlog指定最大的连接数,即可同时连接的客户端数量
  • ServerSocket类的常用方法
常用方法 功能说明
public Socket accept() 在服务器端的指定端口监听客户端发来的连接请求,并返回一个与客户端Socket对象相连接的Socket对象
public InetAddress getInetAddress() 返回服务器的IP地址
public int getLocalPort() 返回服务器的端口号
public void close() 关闭服务器端建立的套接字

Socket通信模式

1)客户建立到服务器的套接字对象

1
2
3
4
try{
Socket mySocket=new Socket(http://www.gduf.edu.cn,1880);
}
catch(IOException e){}

getInputStream()方法获得输入流,getOutputStream()方法获得输出流。

2)建立接收客户套接字的服务器套接字

1
2
3
4
5
6
7
8
9
10
11
12
13
try{
ServerSocket serSocket = new ServerSocket(1880);
}
catch(IOException e){

}

try{
Socket sc = serSocket.accept();
}
catch(IOException e){

}

Socket通信的步骤如下:
(1)在服务器端创建一个ServerSocket对象,并指定端口号;
(2)运行ServerSocket的accept()方法,等候客户端请求;
(3)客户端创建一个Socket对象,指定服务器的IP地址和端口号,向服务器端发出连接请求;
(4)服务器端接收到客户端请求后,创建Socket对象与客户端建立连接;
(5)服务器端和客户端分别建立输入输出数据流,进行数据传输;
(6)通信结束后,服务器端和客户端分别关闭相应的Socket连接;
(7) 服务器端程序运行结束后, 调用ServerSocket 对象的close()方法停止等候客户端请求。

为了能实现服务器端同时对多个客户进行服务,需要用多线程,在服务器端创建客户请求的监听线程,一旦客户发起连接请求,则在服务器端创建用于服务的Socket,利用该Socket完成与客户的通信,即每个线程针对一个客户进行服务。

无连接的数据报通信程序设计

数据报通信是基于用户数据报协议( User Datagram Protocol,UDP)的网络信息传输方式。数据报(datagram)是网络层数据单元在介质上传输信息的一种逻辑分组形式。
数据报是无连接的远程通信服务,它是一种在网络中传输的、独立的、自身包含地址信息的数据单位,不保证传送顺序和内容的准确性。数据报Socket又称为UDP套接字,它无须建立、拆除连接,直接将信息打包传向指定的目的地,使用起来比流式Socket要简单一些。但由于该种通信方式不能保证将所有数据都传送到目的地,所以一般用于传送非关键性的数据。

首先将数据打包,形成数据包,这类似于将信件装入信封,然后将数据包发往目的地;其次是接收端收到别人发来的数据包,然后查看数据包中的内容,这类似于从信封中取出信件。

DatagramPacket类在发送端用于将待发送的数据打包,在接收端则用于将收到的数据拆包;
DatagramSocket类用于实现数据报通信的过程中数据报的发送与接收。

DatagramPacket类

需要传输的数据、数据报的长度、IP 地址和端口号等信息。

  • DatagramPacket类的构造方法
构造方法 功能说明
public DatagramPacket(byte[]buf, int length) 创建一个用于接收数据报的对象,buf数组用于接收数据报中的数据,接收长度为length
public DatagramPacket ( byte[] buf,int length, InetAddress address, int port) 创建一个用于发送给远程系统的数据报对象。并将数组buf中长度为length的数据发送到地址为address、端口号为port的主机上
  • DatagramPacket类的常用方法
常用方法 功能说明
public byte[] getData() 返回一个字节数组,包含收到或要发送的数据报中的数据
public int getLength() 返回发送或接收到的数据的长度
public InetAddress getAddress() 返回目标数据包的IP地址或发送该数据包主机的IP地址
public int getPort() 返回目标数据包的端口号或发送该数据包主机的端口号

DatagramSocket类

于在发送主机中建立数据报通信方式,提出发送请求,实现数据报的发送与接收。

  • DatagramSocket类的构造方法
构造方法 功能说明
public DatagramSocket() 创建一个以当前计算机的任一个可用端口为发送端口的数据报连接
public DatagramSocket(int port) 创建一个以当前计算机的指定端口为接收端口的数据报连接
public DatagramSocket(int port, InetAddress laddr) 用于在有多个IP地址的当前主机上,创建一一个以laddr 为指定IP地址、以port为指定端口的数据报连接

SocketException异常

  • DatagramSocket类的常用方法
常用方法 功能说明
public void receive(DatagramPacket p) 从建立的数据报连接中接收数据,并保存到p中
public void send(DatagramPacket p) 将数据报对象p中包含的报文发送到所指定的IP地址主机的指定端口
public void setSoTimeout(int timeout) 设置传输超时为timeout
public void close() 关闭数据报连接

由于数据报是不可靠的通信方式,所以receive()方法不一定能接收到数据,为防止线程死掉,应该利用setSoTimeout()。

数据报通信的发送与接收过程

发送

(1)创建一个用于发送数据的DatagramPacket对象,使其包含如下信息:
要发送的数据;
数据报分组的长度;
发送目的地的主机IP地址和目的端口号。
(2)在指定的或可用的本机端口创建DatagramSocket对象。
(3)调用DatagramSocket对象的send()方法,以DatagramPacke对象为参数发送数据报。

接收

(1)创建一个用于接收数据报的DatagramPacket对象,其中包含空白数据缓冲区和指定数据报分组的长度。
(2)在指定的或可用的本机端口创建DatagramSocket对象。
(3) 调用DatagramSocket 对象的receive()方法, 以DatagramPacket对象为参数接收数据报,接收到的信息有:
收到的数据报分组的内容;
发送端主机的IP地址;
发送端主机的发送端口号。

在数据报通信中,由于通信双方之间并不需要建立连接,所以服务器端应用程序通信过程与客户端应用程序的通信过程是非常相似的,客户端与服务器端双方均可以发送与接收数据报分组。所不同的是服务器应用程序要面向网络中的所有计算机,所以服务器应用程序收到一个数据报分组后要分析它,得到数据报的源地址信息,这样才能创建正确的返回结果分组给客户机。

本章小结

  • 通信端口是一个标记计算机逻辑通信信道的正整数,用于区分一台主机中的不同应用程序,端口号不是物理实体。
  • IP地址和端口号组成了所谓的Socket。Socket是实现客户与服务器(Client/Server, C/S)模式的通信方式,Socket原意为“插座”,在通信领域中译为“套接字”,在网络通信里的含义就是建立一个连接。
  • URL是统一资源定位器(Uniform Resource Locator)的简称,它表示Internet上某一资源的地址。URL的基本结构由五部分组成。
  • Java的网络编程分为三个层次。最高一级的网络通信就是从网络上下载小程序;次一级的通信就是通过URL类的对象指明文件所在位置,并从网络上下载音频、视频或图像文件,然后播放音频、视频或显示图像;最低一级的通信是利用java.net包中提供的类直接在程序中实现网络通信。
  • 针对不同层次的网络通信,Java语言提供的网络功能有四大类:URL、InetAddress、Socket、Datagram。
    (1)URL:面向应用层,通过URL,Java程序可以直接输出或读取网络上的数据。
    (2)InetAddress:面向的是IP层,用于标识网络上的硬件资源。
    (3)Socket和Datagram:面向的是传输层。Socket使用TCP,这是传统网络程序最常用的方式,可以想象为两个不同的程序通过网络的通信信道进行通信;Datagram则使用UDP,是另一种网络传输方式,它把数据的目的地址记录在数据包中,然后直接放在网络上。

课后习题

  • 什么是URL?URL地址由哪几部分组成?
  • 什么是Socket?它与TCP/IP有何关系?
  • 简述流式Socket的通信机制。它的最大特点是什么?为什么可以实现无差错通信?
  • 什么是端口号?服务器端和客户端分别如何使用端口号?
  • 什么是套接字?其作用是什么?
  • 编写Java程序,使用InetAddress类实现根据域名自动到DNS(域名服务器)上查找IP地址的功能。
  • 用Java程序实现流式Socket通信,需要使用哪两个类?它们是如何定义的?应怎样使用?
  • 与流式Socket相比,数据报通信有何特点?
文章作者: HibisciDai
文章链接: http://hibiscidai.com/2022/01/10/JAVA程序设计基础-第6版陈国君2006-学习笔记5/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 HibisciDai
好用、实惠、稳定的梯子,点击这里