《Java核心技术(卷2):高级特性(原书第9版)》章节试读

出版社:机械工业出版社
出版日期:2014-3-1
ISBN:9787111442509
作者:[美] Cay S. Horstmann,[美] Gary Cornell
页数:856页

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第160页 - 3.2.1 为多个客户端服务

每当程序建立一个新的套接字连接,也就是说当调用accept 时,将启动一个新的线程来处理服务器和该客户端之间的连接,而主程序将立即返回并等待下一个连接。为了实现这个机制,服务器应该具有类似以下代码的循环操作:
while (true)
{
Socket incoming = s.accept();
Runnable r = new ThreadedEchoHandler(incoming);
Thread t = new Thread(r);
t.start();
}
ThreadedEchoHandler 类实现了Runnable 接口,而且在它的run 方法中包含了与客户端循环通信的代码。
class ThreadedEchoHandler implements Runnable
{
......
public void run()
{
try
{
InputStream inStream = incoming.getInputStream();
OutputStream outStream = incoming.getOutputStream();

Process input and send response
}catch (IOException e) {
e.printStackTrace();
}finally {
incoming.close();
}
由于每一个连接都会启动一个新的线程,因而多个客户端就可以同时连接到服务器了。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第11页 - 1.2.1 如何写出文本输出

PrintWriter out = new PrintWriter("employee.txt");
等同于:
PrintWriter out = new PrintWriter(new FileWriter("employee.txt"));
为了输出到打印写出器,需要使用与使用System.out 时相同的print、println 和
printf 方法。你可以用这些方法来打印数字(int、short、long、float、double)、字符、
boolean 值、字符串和对象。
例如,考虑下面的代码:
String name = "Harry Hacker";
double salary = 75000;
out.print(name);
out.print(' ');
out.println(salary);
它将把下面的字符:
Harry Hacker 75000.0
输出到写出器out,之后这些字符将会被转换成字节并最终写入employee.txt 中。
如果写出器设置为自动冲刷模式,那么只要println 被调用,缓冲区中的所有字符都会被发送到它们的目的地(打印写出器总是带缓冲区的)。默认情况下,自动冲刷机制是禁用的,可以通过使用PrintWriter(Writer out, Boolean autoFlush) 来启用或禁用自动冲刷机制:
PrintWriter out = new PrintWriter(new FileWriter("employee.txt"),true); //autoflush

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第766页 - 11.1 客户与服务器的角色

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第164页 - 3.3 可中断套接字

当连接到一个套接字时,当前线程将会被阻塞直到建立连接或产生超时为止。同样地,当通过套接字读写数据时,当前线程也会被阻塞直到操作成功或产生超时为止。在交互式的应用中,也许会考虑为用户提供一个选项,用以取消那些看似不会产生结果的连接。但是,当线程因套接字无法响应而发生阻塞时,则无法通过调用interrupt 来解除阻塞。
为了中断套接字操作,可以使用java.nio 包提供的一个特性—SocketChannel 类。可以使用如下方法打开SocketChannel:
SocketChannel channel = SocketChannel.open(new InetSocketAddress(host,port));
如果不想处理缓冲区, 可以使用Scanner 类从SocketChannel 中读取信息, 因为Scanner 有一个带ReadableByteChannel 参数的构造器:
Scanner in = new Scanner(channel);
通过调用静态方法Channels.newOutputStream,可以将通道转换成输出流。
OutputStream outStream = Channels.newOutputStream(channel);
上述操作都是必须做的。假设线程正在执行打开、读取或写入操作,此时如果线程发生中断,那么这些操作将不会陷入阻塞,而是以抛出异常的方式结束。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第241页 - 4.9.1 保存点

在使用某些驱动程序时,使用保存点(save point)可以更细粒度地控制回滚操作。创建一个保存点意味着稍后只需返回到这个点,而非事务的开头。例如,
Statement stat = conn.CreateStatement(); //start transaction ; rollback() goes here
stat.executeUpdate(command1);
Savepoint svpt = conn.setSavepoint(); //set savepoint ; rollback(svpt) goes here
stat.executeUpdate(command2);
if (...) conn.rollback(svpt); //undo effect of command2
...
conn.commit();
当不再需要保存点时,必须释放它:
conn.releaseSavepoint(svpt);

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第771页 - 10.3.2 RMI注册表

服务器程序使用自举注册服务来注册至少一个远程对象。要注册一个远程对象,需要一个RMI URL和一个对实现对象的引用。
RMI的URL以rmi:开头,后接服务器以及一个可选的端口号,接着是远程对象的名字。例如:
rmi://regserver.mycompany.com:99/central_warehouse
默认情况下,主机名是localhost,端口为1099。服务器告诉注册表在给定位置将这个名字关联或"绑定"到该对象。
构造并注册了一个WarehouseImpl对象:
public class WarehouseServer {
public static void main(String[] args) throws RemoteException, NamingException {
System.out.println("Constructing server implementation...");
WarehouseImpl centralWarehouse = new WarehouseImpl();
System.out.println("Binding server implementation to registry...");
Context namingContext = new InitialContext();
namingContext.bind("rmi:central_warehouse", centralWarehouse);
System.out.println("Waiting for invocations from clients...");
}
}
客户端可以通过下面的方式,来指定服务器和远程对象的名字,以此获得访问远程对象所需的存根:
public class WarehouseClient{
public static void main(String[] args) throws NamingException, RemoteException {
String url = "rmi://localhost/central_warehouse";
Warehouse centralWarehouse = (Warehouse) namingContext.lookup(url);
String descr = "Blackwell Toaster";
double price = centralWarehouse.getPrice(descr);
System.out.println(descr + ": " + price);
}
}
11-4展示了客户端如何获得远程仓库对象的存根,并调用远程的getPrice方法。图11-4展示了其控制流。客户端获得了一个Warehouse存根,并在其上调用了getPrice方法。在后台,存根与服务器联系,并在WarehouseImpl对象上调用getPrice方法。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第175页

最后我们再介绍一个总览全局的方法:setRequestProperty,它可以用来设置对特定协议起作用的任何“名- 值(name/value)对”。关于HTTP 请求头的格式,请参见RFC 2616,其中的某些参数没有很好地建档,它们通常在程序员之间口头传授。例如,如果你想访问一个有密码保护的Web 页,那么就必须按如下步骤操作:
1)将用户名、冒号和密码以字符串形式连接在一起。
String input = username + ":" + password;
2)计算上一步骤所得字符串的Base64 编码。(Base64 编码用于将字节序列编码成可打印的ASCII 字符序列。)
String encoding = base64Encode(input);
3)调用setRequestProperty 方法,设置name 参数的值为“ Authorization”、value参数的值为“Basic”+encoding:
connection.setRequestProperty("Authorization","Basic" + encoding);
提示:我们上面介绍的是如何访问一个有密码保护的Web 页。如果想要通过FTP 访问一个
有密码保护的文件时,要采用一种完全不同的方法。可以直接构建一个如下格式的URL:
ftp://username:password@ftp.yourserver.com/pub/file.txt

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第114页 - 2.4 使用XPath来定位信息

先从XPathFactory 创建一个XPath 对象:
XPathFactory xpfactory = XPathFactory.newInstance();
path = xpfactory.newXPath();
然后,调用evaluate方法来计算XPath 表达式,来获取username节点中的文本:
String username = path.evaluate("/configuration/database/username",doc);
这种形式的evaluate 方法将返回一个字符串。这很适合用来获取文本。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第767页 - 11.2 远程方法调用

存根与参数编组
当客户代码要在远程对象上调用一个远程方法时,实际上调用的是代理对象上的一个普通的方法,我们称此代理对象为存根(stub)。例如,
Warehouse centralWarehouse = get stub object;
double price = centralWarehouse.getPrice("Blackwell Toaster");
存根位于客户端机器上,而非服务器上。它知道如何通过网络与服务器联系。存根将远程方法所需的参数打包成一组字节。对参数编码的过程称作参数编组(parameter marshalling),参数编组的目的是将参数转换成适合在虚拟机之间进行传递的格式。在RMI协议中,对象是使用序列化机制进行编码的,第1章描述了这种机制。在SOAP协议中,对象被编码为XML。
总的来说,客户端的存根方法构造了一个信息块,它由以下几部分组成:
●被使用的远程对象的标识符;
●被调用的方法的描述;
●编组后的参数。
然后,存根将此信息发送给服务器。在服务器一端,接收对象执行以下动作:
1)定位要调用的远程对象;
2)调用所需的方法,并传递客户端提供的参数;
3)捕获返回值或该调用产生的异常;
4)将返回值编组,打包送回给客户端存根。
客户端存根对来自服务器端的返回值或异常进行反编组,就成为了调用存根的返回值。如果远程方法抛出了一个异常,那么存根就在客户端发起调用的处理空间中重新抛出该异常。图10-3展示了一次远程方法调用的信息流。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第716页 - 10.1 Java平台的脚本

直接通过名字、MIME类型或文件扩展来请求它,例如:
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
一旦拥有了引擎,就可以通过下面的调用来直接调用脚本:
Object result = engine.eval(scriptString);
如果脚本存储在文件中,那么需要先打开一个Reader,然后调用:
Object result = engine.eval(reader);
可以在同一个引擎上调用多个脚本。如果一个脚本定义了变量、函数或类,那么大多数引擎都会保留这些定义,以供将来使用。例如:
engine.eval("n=1728");
Object result = engine.eval("n +1");
将返回1729。
我们经常希望能够向引擎中添加新的变量绑定。绑定由名字及其关联的Java对象构成。例如:
engine.put(k,1728);
Object result = engine.eval("k + 1");

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第228页 - 4.7.1 构建行集

在Java7中,有一种获取行集的标准方式:
RowSetFactory factory = RowSetProvider.newFactory();
CachedRowSet crs = factory.createCachedRowSet();
在Java7之前,创建行集的方法都是与供应商相关的。如果你无法使用RowSetProvider,那么可以使用下面的类取而代之:
CachedRowSet crs = new com.sun.rowset.CachedRowSetImpl();

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第30页 - 1.5 对象流与序列化

为了保存对象数据,首先需要打开一个ObjectOutputStream 对象:
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("employee.dat"));
现在,为了保存对象,可以直接使用ObjectOutputStream 的writeObject 方法,如下所示:
Employee harry = new Employee("Harry Hacker", 50000, 1989, 10, 1);
Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);
out.writeObject(harry);
out.writeObject(boss);
为了将这些对象读回,首先需要获得一个ObjectInputStream 对象:
ObjectInputStream in = new ObjectInputStream(new FileInputStream("employee.dat"));
然后,用readObject 方法以这些对象被写出时的顺序获得它们:
Employee e1= (Employee) in.readObject();
Employee e2= (Employee) in.readObject();
但是,对希望在对象流中存储或恢复的所有类都应进行一下修改,这些类必须实现Serializable接口:
class Employee implements Serializable{ ... }
Serializable 接口没有任何方法,因此你不需要对这些类做任何改动。在这一点上,
它与在第Ⅰ卷第6 章中讨论过的Cloneable 接口很相似。但是,为了使类可克隆,你仍旧
需要覆盖Object 类中的clone 方法,而为了使类可序列化,你不需要做任何事。
注意:你只有在写出对象时才能用writeObject/readObject 方法,对于基本类型值,
你需要使用诸如writeInt/readInt 或writeDouble/readDouble 这样的方法。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第201页 - 4.3.5 连接到数据库

private Statement stat;
private Connection con;
private ResultSet rs;
public void connectMySQL() {
String url = "jdbc:mysql://localhost:3306/drivingschool";
String user = "root";
String password = "1";
try {
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection(url, user, password);
System.out.println("数据库连接成功");
stat = con.createStatement();
} catch (Exception e) {
e.printStackTrace();
}
}


public ResultSet query(String sql)throws SQLException{
if(sql==null || sql.equals("")){
return null;
}
rs=stat.executeQuery(sql);
return rs;
}

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第69页 - 1.8 正则表达式

正则表达式的最简单用法就是测试某个特定的字符串是否与它匹配。下面展示了如何用Java 来编写这种测试,首先用表示正则表达式的字符串构建一个Pattern 对象。然后从这个模式中获得一个Matcher,并调用它的matches 方法:
Pattern pattern = Pattern.compile(patternString);
Matcher matcher = pattern.matcher(input);
if(matcher.matches())...
这个匹配器的输入可以是任何实现了CharSequence 接口的类的对象,例如String、StringBuilder 和CharBuffer。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第47页 - Path

Path 类有许多有用的方法用来将路径断开以及和其他路径进行组合。下面的代码示例展示了其中部分最有用的方法:
Path p =Paths.get("/home","cay","myprop.properties");
Path parent = p.getParent(); //the path /home/cay
Path file = p.getFileName(); //the path myprop.properties
Path root = p.getRoot(); //the path /
注意:偶尔,你可能需要与遗留系统的API交互,它们使用的是File类而不是Path类。Path类有一个toFile方法,而File类页也有一个toPath方法。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第214页 - 4.5.1 预备语句

许多程序员都不喜欢使用如此复杂的SQL语句。比较常见的方法是使用大量的Java代码来迭代多个结果集,但是这种方法效率非常低。通常,使用数据库的查询代码要比使用Java程序好得多——这是数据库的一个重要优点。一般而言,可以使用SQL解决的问题,就不要使用Java程序。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第48页 - 读写文件

Files类可以使得普通文件操作变得快捷。例如,可以用下面的方式很容易地读取文件的所有内容:
byte[] bytes = Files.readAllBytes(path);
如果想将文件当作字符串读入,那么可以在调用readAllBytes之后执行下面的代码:
String content = new String(bytes,charset);
但是如果希望将文件当作行序列读入,那么可以调用:
List<String> lines = Files.readAllLines(path, charset);
相反地,如果希望写出一个字符串到文件中,可以调用:
Files.write(path, content.getBytes(charset));
向指定文件追加内容,可以调用:
Files.write(path, content.getBytes(charset),StandardOpenOption.APPEND);
还可以用下面的语句将一个行的集合写出到文件中:
Files.write(path, lines);
这些简便方法适用于处理中等长度的文本文件,如果要处理的文件长度比较大,或者是二进制文件,那么还是应该使用所熟知的流或者读入器/写出器:
InputStream in = Files.newInputStream(path);
OutputStream out = Files.newOutputStream(path);
Reader in = Files.newBufferedReader(path, charset);
Writer out = Files.newBufferedWriter(path, charset);
这些便捷方法可以将你从处理FileInputStream、FileOutputStream、BufferedReader和BufferedWriter的繁复操作中解脱出来。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第241页 - 4.9.2 批量更新

假设有一个程序需要执行许多INSERT语句,以便将数据填入数据库表中,此时可以使用批量更新的方法来提高程序性能。在使用批量更新(batch update)时,一个语句序列作为一批操作将同时被收集和提交。
处于同一批中的语句可以是INSERT、UPDATE和DELETE等操作,也可以是数据库定义语句,如CREATE TABLE和DROP TABLE。但是,在批量处理中添加SELETE语句会抛出异常。
为了执行批量处理,首先必须使用通常的办法创建一个Statement对象:
Statement stat = conn.CreateStatement();
现在,应该调用addBatch方法,而非executeUpdate方法:
String command = “CREATE TABLE..."
stat.addBatch(command);
while (...){
command = "INSERT INTO ... VALUES (" + ... + ")";
stat.addBatch(command);
}
最后,提交整个批量更新语句:
int[] count = stat.executeBatch();
调用executeBatch方法将为所有已提交的语句返回一个记录数的数组。
为了在批量处理模式下正确地处理错误,必须将批量执行的操作视为单个事务。如果批量更新在执行过程中失败,那么必须将它回滚到批量操作开始之前的状态。
首先,关闭自动提交模式,然后收集批量操作,执行并提交该操作,最后恢复最初的自动提交模式:
boolean autoCommit = conn.getAutoCommit();
conn.setAutoCommit(false);
Statement stat = conn.getStatement();
...
//keep calling stat.addBatch(...);
...
stat.executeBatch();
conn.commit();
conn.setAutoCommit(autoCommit);

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第770页 - 10.3.1 接口与实现

public class WarehouseImpl extends UnicastRemoteObject implements Warehouse {
public WarehouseImpl() throws RemoteException {
prices = new HashMap<String, Double>();
prices.put("Blackwell Toaster", 24.95);
prices.put("ZapXpress Microwave Oven", 49.95);
}
public double getPrice(String description) throws RemoteException {
Double price = prices.get(description);
return price == null ? 0 : price;
}
private Map<String, Double> prices;
}
注意:WarehouseImpl的构造器声明了会抛出RemoteException异常,因为其超类的构造器会抛出这个异常,这发生在无法连接到跟踪远程对象的网络服务时。
这个类是远程方法调用的目标,因为它继承自UnicastRemoteObject,这个类的构造器使得它的对象可供远程访问。"最小阻抗路径"是从UnicastRemoteObject中导出的,本章中的所有服务实现类也都是如此。
有时候可能不希望服务器类继承UnicastRemoteObject,也许是因为实现类类已经继承了其他的类。在这种情况下,读者需要亲自初始化远程对象,并将它们传给静态的exportObject方法。如果不继承UnicastRemoteObject,可以在远程对象的构造器中像下面这样调用exportObject方法。
UnicastRemoteObject.exportObject(this, 0);
第二个参数是0,表明任意合适的端口都可用来监听客户连接。
注意:Unicast这个术语是指我们是通过产生对单一的IP地址和端口的调用来定位远程对象的这一事实。这是Java SE中唯一支持的机制。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第206页 - 4.4.1 管理连接、语句和结果集

在使用JavaSE 7时,可以在Statement上调用closeOnCompletion方法,在其所有结果集都被关闭后,该语句会立即被自动关闭。
如果所用连接都是短时的,那么无需考虑关闭语句和结果集。只需将close语句放在带资源的try语句中,以便确保最终连接对象不可能继续保持打开状态。
try(Connection conn= ...){
Statement stat = conn.createStatement();
ResultSet result = stat.executeQuery(queryString);
Process query result
}
提示:应该使用带资源的try语句块来关闭连接,并使用一个单独的try/catch块处理异常。分离try程序块可以提高代码的可读性和可维护性。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第21页 - 1.3 读写二进制数据

DataInputStream 类实现了DataInput 接口,为了从文件中读入二进制数据,可以将DataInputStream 与某个字节源相组合,例如FileInputStream:
DataInputStream in = new DataInputStream(new FileInputStream("employee.dat"));
与此类似, 要想写出二进制数据, 你可以使用实现了DataOutput 接口的DataOutput- Stream 类:
DataOutputStream out = new DataOutputStream(new FileOutputStream("employee.dat"));

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第727页 - 10.2.1 编译便捷之法

调用编译器非常简单,下面是一个示范调用:
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); .
OutputStream outStrεam=...;
OutputStream err5tream =...;
int result = compiler.run(null,outStream,"-sourcepath","src","Test.java");
返回值为0表示编译成功。
编译器会向提供给它的流发送输出和错误消息。如果将这些参数设置为nu11,就会使用System.out和System.err。run方法的第一个参数是输入流,由于编译器不会接受任何控制台输入,因此总是应该让其保持为null。 (run方法是从泛化的Tool接口继承而来的,它考虑到某些工具需要读取输入。)如果在命令行调用Javac,那么run方法其余的参数就会作为变量传递给Javac。这些变量是一些选项或文件名。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第797页 - 12.1 从Java程序中调用C函数

遵循下面的步骤就可以将一个本地方法链接到Java程序中:
1)在Java类中声明一个本地方法。
2)运行javah以获得包含该方法的C声明的头文件。
3)用C实现该本地方法。
4)将代码置于共享类库中。
5)在Java程序中加载该类库。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第183页 - 3.4.3 提交表单数据

在向Web 服务器发送信息时,通常有两个命令会被用到:GET 和POST。
在使用GET 命令时,只需将参数附在URL 的结尾处即可。这种URL 的格式如下:
http://host/script?parameters
其中,每个参数都具有“名字= 值”的形式,而这些参数之间用& 字符分隔开。参数的值将遵循下面的规则,使用URL 编码模式进行编码:
● 保留字符 A-Z、a-z、0-9 以及 . - * _。
● 用 + 字符替换所有的空格。
● 将其他所有字符编码为 UTF-8,并将每个字节都编码为 % 后面紧跟一个两位的十六进制数字。
例如,若要发送街道名New York,NY,可以使用New+York%2C+NY,因为十六进制数2c(即十进制数44)是“,”的ASCII 码值。
这种编码方式使得在任何中间程序中都不会混入空格,并且也不需要对其他特殊字符进
行转换。
例如,就在编写本书的时候,Yahoo 的Web 站点在主机maps.yahoo.com 上放置了一
个脚本程序:py/maps.py。运行该脚本需要两个参数:addr 和csz。
为了得到1 Market Strect, San Francisco, CA 的地图,只需访问下面的URL 即可:
http://maps.yahoo.com/py/maps.py?addr=1+Market+Strect&csz=San+Francisco+CA
GET 命令很简单,但是它有一个重要的局限性,正是由于这个局限性使得它并不是非常
受欢迎:大多数浏览器都对GET 请求中可以包含的字符数作了限制。
在使用POST 命令时,并不需要在URL 中添加任何参数,而是从URLConnection 中获
取输出流,并将名- 值对写入该流中。当然,仍然需要对这些值进行URL 编码,并用& 字
符将它们隔开。
下面, 我们将详细介绍这个过程。在提交数据给脚本之前, 首先需要创建一个URLConnection 对象。
URL url = new URL("http://host/script");
URLConnection connection = url.openConnection();
然后,调用setDoOutput 方法建立一个用于输出的连接。
connection .setDoOutput(true);
接着,调用getOutputStream 方法获得一个流,可以通过这个流向服务器发送数据。如果要向服务器发送文本信息,那么可以非常方便地将流包装在PrintWriter 对象中。
PrintWriter out = new PrintWriter(connection.getOutputStream());
现在,可以向服务器发送数据了。
out.print(name1 + "=" + URLEncoder.encode(value1,"UTF-8") + "&");
out.print(name2 + "=" + URLEncoder.encode(value2,"UTF-8"));
之后,关闭输出流。
out.close();
最后,调用getInputStream 方法读取服务器的响应。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第158页 - 3.2 实现服务器

每一个服务器程序,比如一个HTTP Web 服务器,都不间断地执行下面这个循环:
1)通过输入数据流从客户端接收一个命令(“get me this information”)。
2)解码这个客户端命令。
3)收集客户端所请求的信息。
4)通过输出数据流发送信息给客户端。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第229页 - 4.7.2 被缓存的行集

一个被缓存的行集包含了一个结果集中所有的数据。CachedRowSet是ResultSet接口的子接口,所以可以使用ResultSet中的API。被缓存的行集的优点:断开数据库连接后仍然可以使用行集。
这种做法大大简化了交互式应用的实现。在执行每个用户命令时,我们只需打开数据库连接、执行查询操作、将查询结果放入被缓存的行集,然后关闭数据库连接即可。
我们甚至可以修改被缓存的行集中的数据。当然,这些修改不会立即反馈到数据库中。相反,必须发起一个显式的请求,以便让数据库真正接受所有修改。此时CachedRowSet类会重新连接到数据库,并通过执行SQL语句向数据库中写入所有修改后的数据。
可以使用一个结果集来填充CachedRowSet对象:
ResultSet result = ...;
CachedRowSet crs = new com.sun.rowset.CachedRowSetImpl();
//or use an implementation from your database vendor
crs.populate(result);
conn.close(); // now OK to close the database connection
或者,也可以让CachedRowSet对象自动创建一个数据库连接。首先,设置数据库参数:
crs.setUrl("jdbc:derby://localhost:1527/COREJAVA");
crs.setUsername("dbuser");
crs.setPassword("12345678");
然后,设置查询语句和所有参数
crs.setCommand("SELECT * FROM Books WHERE PUBLISHER = ?");
crs.setString(1, publisherName);
最后,将查询结果填充到行集:
crs.execute();
这个方法调用会建立数据库连接、执行查询操作、填充行集,最后断开连接。
如果查询结果非常大,那我们肯定不想将其全部放入行集中。毕竟,用户可能只是想浏览其中的几行而已。在这种情况下,可以指定每一页的尺寸:
CachedRowSet crs = ...;
crs.setCommand("SELECT * FROM Books WHERE PUBLISHER = ?");
crs.setPageSize(20);
...
crs.execute();
现在就能只获取20行了。要获取下一批数据,可以调用:
crs.nextPage();
可以使用与结果集中相同的方法来查看和修改行集中的数据。如果修改了行集中的内容,那么必须调用以下方法将修改写回到数据库中:
crs.acceptChanges(conn);

crs.acceptChanges();
//只有设置了连接数据库所需的信息(如URL、用户名和密码)时,此方法调用才会有效。
注意:
1)如果一个行集包含的是复杂查询的查询结果,那么就无法将对行集数据的修改写回到数据库中。不过,如果行集上的数据都来自同一张数据库表,那么就可以安全的写回数据。
2)如果是使用结果集来填充行集,那么行集就无从获知需要更新数据的数据库表名。此时,必须调用setTable方法来设置表名。
3)提交时行集会检查行集中的原始值(即修改前的值)是否与数据库中的当前值一致。如果一致,那么修改后的值将覆盖数据库中的当前值,否则抛出SyncProviderException异常,且不向数据库写回任何值。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第786页 - 10.4.5 远程对象与equals、hashCode和clone方法

可以使用集与散列表中的远程引用,但是必须记住,对它们进行等价测试以及散列计算并不会考虑远程对象的内容。不能直接克隆远程引用。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第10页 - 1.2 文本输入与输出

OutputStreamWriter 类将使用选定的字符编码方式,把Unicode 字符流转换为字节流。而InputStreamReader 类将包含字节(用某种字符编码方式表示的字符)的输入流转换为可以产生Unicode 码元的读入器。
例如,下面的代码就展示了如何让一个输入读入器可以从控制台读入键盘敲击信息,并将其转换为Unicode:
InputStreamReader in = new InputStreamReader(System.in);
这个输入流读入器会假定使用主机系统所使用的默认字符编码方式,例如西欧采用ISO
8859-1。你可以通过在InputStreamReader 的构造器中进行指定的方式来选择不同的编码
方式。例如,
InputStreamReader in = new InputStreamReader(new FileInputStream("kremlin.dat"),"ISO8859_5");

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第81页 - 2.2 解析XML文档

要读入一个XML 文档,首先需要一个DocumentBuilder 对象,可以从DocumentBuilderFactory 中得到这个对象,例如:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
现在,可以从文件中读入某个文档:
File f =...
Document doc = builder.parse(f);
或者,可以用一个URL:
URL u =...
Document doc = builder.parse(u);
甚至可以指定一个任意的输入流:
InputStream in = ...
Document doc = builder.parse(in);

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第49页 - 1.6.3 复制、移动和删除文件

将文件从一个位置复制到另一个位置可以直接调用
Files.copy(fromPath,toPath);
移动文件(即复制并删除原文件)可以调用
Files.move(fromPath,toPath);
如果目标路径已经存在,那么复制或移动将失败。如果想要覆盖已有的目标路径,可以使用REPLACE_EXISTING 选项。如果想要复制所有的文件属性, 可以使用COPY_ATTRIBUTES 选项。也可以像下面这样同时选择这两个选项:
Files.copy(fromPath,toPath,StandardCopyOption.REPLACE_EXISTING,StandardCopyOption.COPY_ATTRIBUTES);
你可以将移动操作定义为原子性的,这样就可以保证要么移动操作成功完成,要么源文件继续保持在原来位置。具体可以使用ATOMIC_MOVE 选项来实现:
Files.move(fromPath,toPath,StandardCopyOption.ATOMIC_MOVE);
最后,删除文件可以调用:
Files.delete(path);
如果要删除的文件不存在,这个方法就会抛出异常。因此,可转而使用下面的方法:
boolean deleted = Files.deleteIfExists(path);
该删除方法还可以用来移除空目录。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第221页 - 4.5.5 获取自动生成键

当我们向数据表中插入一个新行,且其键自动生成时,可以实现下面的代码来获取这个键:
stmt.executeUpdate(insertStatement,Statement.RETURN_GENERATED_KEYS);
ResultSet rs = stmt.getGeneratedKeys();
if ( rs.next() ){
int key = rs.getInt(1);
...
}

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第7页 - 1.1.3 组合流过滤器

FileInputStream 和FileOutputStream 可以提供附着在一个磁盘文件上的输入流和输出流,而你只需向其构造器提供文件名或文件的完整路径名。例如:
FileInputStream fin = new FileInputStream("employee.dat");
这行代码可以查看在用户目录下名为“employee.dat”的文件。
提示:所有在java.io 中的类都将相对路径名解释为以用户工作目录开始,你可以通过调用System.getProperty("user.dir") 来获得这个信息。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第785页 - 10.4.4 具有多重接口的远程引用

一个远程类可以实现多个接口。考虑远程接口ServiceCenter。
public interface ServiceCenter extends Remote{
int getReturnAuthorization(Product prod) throws RemoteException;
}
现在假设WarehouseImpl类实现了这个接口与Warehouse接口。当一个对ServiceCenter的引用被传递到另一个虚拟机时,接收者会获得一个可以访问在ServiceCenter与Warehouse接口中的所有远程方法的存根。可以使用instanceof操作符来查看一个特定的远程对象是否实现了某个接口。假设我们通过一个类型为Warehouse的变量得到了一个远程对象:
Warehouse location = product.getLocation();
这个远程对象可能是一个ServiceCenter,但也可能不是。要确定到底是不是,可以使用下面的测试:
if (location instanceof ServiceCenter)
如果测试通过,就可以将location转型为ServiceCenter,并调用getReturnAuthorization方法。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第9页 - 1.1 流

● FileOutputStream(String name)
● FileOutputStream(String name, boolean append)
● FileOutputStream(File file)
● FileOutputStream(File file, boolean append)
使用由name 字符串或file 对象指定路径名的文件创建一个新的文件输出流。如果append 参数为true,那么数据将被添加到文件尾,而具有相同名字的已有文件不会被删除;否则,这个方法会删除所有具有相同名字的已有文件。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第1页

RMI,JDBC是需要重点关注的章节。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第779页 - 10.4.2 传递非远程对象

总结一下,在虚拟机之间传递值有两种机制:
●实现了Remote接口的类的对象将作为远程引用传递。
●实现了Serializable接口,但是没有实现Remote接口的类的对象将使用序列化进行复制。
这两种机制都是自动化的,而且不需要任何程序员的干预。请记住,序列化对于大型对象来说会比较慢,而且远程方法不能改变被序列化的参数。当然,你可以通过传递远程引用来避免这些问题。但是这么做代价太大:在远程引用上调用方法与调用本地方法相比,代价高得多。意识到这些开销有助于在设计远程服务时进行选择。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第156页 - 3.1.2 因特网地址

一个因特网地址由4 个字节组成(在IPv6 中是16 个字节),比如132.163.4.102。但是,如果需要在主机名和因特网地址之间进行转换,那么就可以使用InetAddress 类。
只要主机操作系统支持IPv6 格式的因特网地址,java.net 包也将支持它。
静态的getByName 方法可以返回代表某个主机的InetAddress 对象。例如,
InetAddress address = InetAddress.getByName("time-A.timefreq.bldrdoc.gov");
System.out.println(address);
将返回一个InetAddress 对象,该对象封装了一个4 字节的序列:132.163.4.104。然后,可以使用getAddress 方法来访问这些字节:
byte[] addressBytes = address.getAddress();
一些访问量较大的主机名通常会对应于多个因特网地址,以实现负载均衡。例如,在撰写本书时,主机名google.com 就对应着12 个不同的因特网地址。当访问主机时,会随机选取其中的一个。可以通过调用getAllByName 方法来获得所有主机:
InetAddress[] addresses = InetAddress.getAllByName("baidu.com");
System.out.println(Arrays.toString(addresses));
最后需要说明的是,有时可能需要本地主机的地址。如果只是要求得到localhost 的地址,那总会得到地址127.0.0.1,但是其他程序无法用这个地址来连接到这台机器上。此时,可以使用静态的getLocalHost 方法来得到本地主机的地址:
InetAddress localAddress = InetAddress.getLocalHost();
System.out.println(localAddress);

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第241页 - 4.9 事物

默认情况下,数据库连接处于自动提交模式(autocommit mode)。每个SQL语句一旦被执行便被提交给数据库。一旦命令被提交,就无法对它进行回滚操作。在使用事务时,需要关闭这个默认值:
conn.setAutoCommit(false);
现在可以使用通常的方法创建一个语句对象:
Statement stat = conn.CreateStatement();
然后任意多次地调用executeUpdate方法:
stat.executeUpdate(command1);
stat.executeUpdate(command2);
stat.executeUpdate(command3);
···
如果执行了所有命令之后没有出错,则调用commit方法:
conn.commit();
如果出现错误,则回滚:
conn.rollback();
此时,程序将自动撤销自上次提交以来的所有语句。当事务被SQLException异常中断时,典型的办法就是发起回滚操作。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第690页 - 9.5.1 消息摘要

Java编程语言已经实现了SHA1和MD5。MessageDigest类是用于创建封装了指纹算法的对象的“工厂”,它的静态方法getInstance返回继承了MessageDigest类的某个类的对象。这意味着MessageDigest类能够承担下面的双重职责:
(1)作为一个工厂类。
(2)作为所有消息摘要算法的超类。
例如,下面是如何获取一个能够计算SHA指纹的对象的方法:
MessageDigest alg = MessageDigest.getInstance(“SHA-1”);
(如果要获取计算MD5的对象,请使用字符串“MD5”作为getInstance的参数。)
当你已经获取MessageDigest对象之后,通过反复调用update方法,将信息中的所有字节提供给该对象。例如,下面的代码将文件中的所有字节传给上面建立的alg对象,以执行指纹算法:
InputStream in=….
int ch;
while((ch=in.read())!=-1)
alg.updat((byte) ch);
另外,如果这些字节存放在一个数组中,那就可以一次完成整个数组的更新:
byte[] bytes =...;
alg.update(bytes);
当完成上述操作后,调用digest方法。该方法填充输入信息—指纹算法需要的—并且进行相应的计算,然后以字节数组的形式返回消息摘要。
byte[] hash=alg.digest();

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第204页 - 4.4 执行SQL语句

executeUpdate方法将返回受SQL命令影响的行数,或者对于不返回行数的语句返回0。
executeUpdate方法既可以执行诸如INSERT、UPDATE和DELETE之类的操作,也可以执行诸如CREATE TABLE和DROP TABLE之类的数据定义语句。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第155页 - 3.1.1 套接字超时

从套接字读取信息时,在有数据可供访问之前,读操作将会被阻塞。如果此时主机不可达,那么应用将要等待很长的时间,并且因为受底层操作系统的限制而最终会导致超时。
对于不同的应用,应该确定合理的超时值。然后调用setSoTimeout 方法设置这个超时值(单位:毫秒)。
Socket s = new Socket(...);
s.setSoTimeout(10000); //time out after 10 seconds
如果已经为套接字设置了超时值,并且之后的读操作和写操作在没有完成之前就超过了
时间限制,那么这些操作就会抛出SocketTimeoutException 异常。你可以捕获这个异常,并对超时做出反应。
try{
InputStream in = s.getInputStream(); // read from in
...
}catch(InterruptedIOException exception){
react to timeout
}
另外还有一个超时问题是必须解决的。下面这个构造器:
Socket(String host ,int port)
会一直无限期地阻塞下去,直到建立了到达主机的初始连接为止。
可以通过先构建一个无连接的套接字,然后再使用一个超时来进行连接的方法解决这个问题。
Socket s = new Socket();
s.connect(new InetSocketAddress(host,port), timeout);

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第13页 - 1.2.2 如何读入文本输入

提示:所有在java.io 中的类都将相对路径名解释为以用户工作目录开始,你可以通过调用System.getProperty("user.dir") 来获得这个信息。
BufferedReader 没有任何用于读入数字的方法,我们建议你使用Scanner 来读入文本输入。
try {
System.out.println(System.getProperty("user.dir"));
Scanner sc = new Scanner(new FileInputStream("src\\scanner\\scan.txt"));
String line=null;
while( sc.hasNextLine())
{
line=sc.nextLine();
System.out.println(line);
}
} catch (FileNotFoundException e) {

e.printStackTrace();
}

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第121页 - 2.6 流机制解析器

DOM 解析器完整地读入XML 文档,然后将其转换成一个树形的数据结构。对于大多数应用,DOM 都运行得很好。但是,如果文档很大,并且处理算法又非常简单,可以在运行时解析节点,而不必看到完整的树形结构,那么DOM 可能就会显得效率低下了。在这种情况下,我们应该使用流机制解析器(streaming parser)。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第173页 - 3.4.2 使用URLConnection 获取信息

当操作一个URLConnection 对象时,必须像下面这样非常小心地安排操作步骤:
1)调用URL 类中的openConnection 方法获得URLConnection 对象:
2)使用以下方法来设置任意的请求属性:
3)调用connect 方法连接远程资源:
除了与服务器建立套接字连接外, 该方法还可用于向服务器查询头信息(header information)。
4)与服务器建立连接后,你可以查询头信息。getHeaderFieldKey 和getHeaderField
两个方法枚举了消息头的所有字段。getHeaderFields 方法返回一个包含了消息头中所有字段的标准Map 对象。
5)最后,访问资源数据。使用getInputStream 方法获取一个输入流用以读取信息(这个输入流与URL 类中的openStream 方法所返回的流相同)。

《Java核心技术(卷2):高级特性(原书第9版)》的笔记-第218页 - 4.5.2 读写LOB

如果你有一张保存图书封面图像的表,那么就可以像下面这样获取一张图像:
PreparedStatement stat = conn.prepareStatement("SELECT Cover FROM BookCovers WHERE ISBN=?");
Stat.set(1,isbn);
ResultSet result = stat.executeQuery();
if( result.next() ){
Blob coverBlob = result.getBlob(1);
Image coverImage = ImageIO.read( coverBlob.getBinaryStream() );
}
存储一张图像:
Blob coverBlob = connection.createBlob();
int offset = 0;
OutputStream out = coverBlob.setBinaryStream(offset);
ImageIO.write(coverImage,"PNG",out);
PreparedStatement stat = conn.prepareStatement("INSERT INTO Cover VALUES(?,?)");
stat.set(1,isbn);
stat.set(2,coverBlob);
stat.executeUpdate();


 Java核心技术(卷2):高级特性(原书第9版)下载 更多精彩书评


 

外国儿童文学,篆刻,百科,生物科学,科普,初中通用,育儿亲子,美容护肤PDF图书下载,。 零度图书网 

零度图书网 @ 2024