API,全称Application Programming Interface,即应用程序编程接口。
API是一些预先定义函数,目的是用来提供应用程序与开发人员基于某软件或者某硬件得以访问一组例程的能力,并且无需访问源码或无需理解内部工作机制细节
Java API是JDK所提供的使用类,这些类将底层的代码给封装起来了。Object类是java语言中的根类,它所描述的所有方法子类都可以使用,所有类在创建对象的时候,最终找的父类就是Object。在Objec类中,最常见的就是euqals方法和toString方法。equals方法用于比较两个对象是否相同,实质是比较两个对象的内存地址。在复写Object中的equals方法时,一定要注意public boolean equals(Object obj)的参数是Object类型。在调用对象的属性时,一定要进行类型转换,转换之前一定要进行类型判断。toString方法返回该对象的字符串表示。其实该字符串内容就是对象的类型+@+内存地址值。
API就是操作系统给应用程序的调用接口,应用程序通过调用操作系统的 API而使操作系统去执行应用程序的命令(动作)。在 Windows 中,系统API是以函数调用的方式提供的。
SDA和API
可以认为API是包含在SDK中的:SDK是一系列文件的组合,包括lib、dll、.h、文档、示例等;API是对程序而言的,提供用户编程时的接口,即一系列模块化的类和函数。
API与GUI
两者都属于直接用户接口,不同的是API接口属于一种操作系统或程序接口,但是GUI接口属于一种图形操作系统。
扩展资料:
API分类
1.Windows API:Windows application programming interface(WIndows应用程序接口)
Windows安装完,系统就有了一套简单的SDK,包含了一套完整的基础Windows API,如果安装VC或下载新的SDK后,就可以使用更加丰富的Windows API了。
凡是在 Windows工作环境底下执行的应用程序,都可以调用Windows API。
2.linux API:linux application programming interface(linux应用程序接口)
在linux中,用户编程接口API遵循了UNIX中最流行的应用编程界面标准——POSIX标准。
Java 常用API的运用,效率及技巧
Java面向对象基本概念
System
String, StringBuffer
数值,字符,布尔对象与简单类型的操作
Class, ClassLoader
Java IO系统
Java集合类
ResourceBundle, Properties
Exceptions
JDBC类库
常用设计模式
Java面向对象基本概念
Java基本上是面向对象的程序设计语言, 除了一些简单类型(primitive)的变量以外,一切都是对象, 程序是对象的组合, 每个对象都有自己的空间, 并且每个对象都有一种类型, 同一类所有对象都能接受相同的消息。 下面只对Java中对象的结构作简单的说明:
Ø 类(class): class是定义类的关键字, 类中包含类变量, 方法, 内部类, 内部接口等。由class可以生成类的实例, 即一个个对象。 如果一个类的成员被定义成static的,则这个成员不专属于任何对象, 而是属于这个类, 所有的对象共享这个成员。
Ø 抽象类(abstract class): 抽象类不能直接生成一个实例, 抽象类中必需有方法是abstract的,抽象类的意思就是它实现了一部分的方法, 而定义为abstract的方法则需要在它的字类中去实现。
Ø 接口(interface): 接口可以理解为纯抽象的类, 它的每个方法都是未实现的, 它可以有成员变量, 但必须是static的。 一个类如果从这个接口继承(implements)则它必须实现这个接口的所有方法。
继承类用关键字:extends,继承接口用关键字:implements。 一个类只能从一个类继承下来, 但可以从多个接口继承(类似于C++的多重继承)。 字类可以覆盖父类的方法(method), 但不能覆盖父类的成员变量(field)。 如果父类的方法为final或static的则不能被覆盖。类的初始化顺序是, 如果有父类, 则先初始化父类的field,然后执行父类的构造函数, 如果子类没有显式的去调父类的构造函数则缺省的会去调父类的无参数构造函数。 然后是子类的field与构造函数的初始化。
public interface SuperInterface {
public staitc String SOME_FLAG = “1”;
public void someMethod();
}
public Class SuperClass {
{ System.out.println(“init SuperClass field”);}
public SuperClass() {System.out.println(“init SuperClass Constructor”); }
public void runMethod() { System.out.println(“run SuperClass runMethod()”); }
}
public Class SubClass extends SuperClass implements SuperInterface {
{ System.out.println(“init SubClass field”); }
public SubClass() {System.out.println(“init SubClass Constructor”); }
public void someMethod() {System.out.println(“run SubClass someMethod()”); }
public void runMethod() {System.out.println(“run SubClass runMethod()”); }
}
有以下test代码:
public class Test {
public void main(String[] args) {
SubClass sub = new SubClass();
sub. runMethod();
}
}
则会输出:
init SuperClass field
init SuperClass Constructor
init SubClass field
init SubClass Constructor
run SubClass runMethod()
以下章节所讲述到的常用的Java API就是一些Java自带的一些Class或Interface的用法。
2.System
System类位于package java.lang下面, 凡是此package下面的类我们可以直接引用无需先import进来, 因为JVM缺省就load了这下面的所有class。
System包含了一些我们常用的方法与成员变量。 System不能被实例化, 所有的方法都可以直接引用。 主要作用大致有:
Ø 输入输出流:
(PrintStream) System.out (标准终端输出流),
(PrintStream) System.err(标准错误输出流),
(InputStream) System.in(标准输入流)。
我们还可以重定向这些流, 比如将所有的System.out的输出全部重定向至一文件中去。
System.setOut(PrintStream) 标准输出重定向
System.setErr(PrintStream) 标准错误输出重定向
System.setIn(InputStream) 标准输入重定向
Ø 取当前时间:
System.currentTimeMillis() 所取到的时间是从1970/01/01以来1/1000秒计算的long型值。这个值可以转换至Date或Timestamp值。 它一般还可以用来计算程序执行的时间。例:
long beginTime = System. currentTimeMillis();
…
…
System.out.println(“run time = ” + (System. currentTimeMillis() – beginTime));
Ø 数组拷贝:
System.arraycopy(Object src, int src_position, Object dst, int dst_position, int length)
src: 源数组。
src_position: 源数组拷贝的起始位置。
dst: 目标数组
dst_position: 拷贝至目标数组的起始位置
length: 拷贝元素的长度
利用System.arraycopy进行数组的拷贝效率是最高的, 一般情况下我们自己很少直接用到这个方法,但在集合类的内部中都大量使用了这个方法。
例:
int[] array1 = {1, 2, 3, 4, 5};
int[] array2 = {4, 5, 6, 7, 8};
int array3 = new int[8];
System.arraycopy(array1, 0, array3, 0, 5);
System.arraycopy(array2, 2, array3, 5, 3);
此时array3 = {1, 2, 3, 4, 5, 6, 7, 8}
这比用for循环来进行赋值效率要高。
Ø 存取系统的Properties:
System.getProperties():取得当前所有的Properties, Properties将在后面的集合一节进行详细的论述。
System.setProperties(Properties props):设置系统的Properties。
System.getProperty(String key): 根据一个键值来取得一个Property。
System.setProperty(String key, String value): 设置系统的一个Property。
JVM起动的时候将会有一些缺省的Properties值, 例如:
java.version Java运行环境版本
java.home Java主目录 installation directory
java.class.path Java 的class path
java.ext.dirs Java的扩展目录路径
file.separator 文件分隔符("/" on UNIX)
path.separator 路径分隔符(":" on UNIX)
line.separator 行分隔符 ("\n" on UNIX)
user.name 用户名
user.home 用户主目录
user.dir 用户当前工作目录
更详细的信息请参照Java API。 另外在起动一个java程序的时候可以通过-D来设置系统的Property, 比如 java –Dejb.file=ejb_Test PrintTest 在PrintTest里面就可以通过System.getProperty(“ejb.file”)来取得值ejb_Test。
Ø 其它
System. loadLibrary(String libname): 加载native的动态库。 可以用C写JNI的库, 然后在java中通过native方法来调用。
System.setSecurityManager(SecurityManager s)
System.getSecurityManager(): 设置与取得系统的security class。
3.String, StringBuffer
3.1 基本用法
String可以说是我们最常用的一个类, 熟练掌握它的一些基本用法是很有用的。
String是由一组字符组成的字符串, 下标由0开始。 一旦有必要改变原来的内容, 每个String方法都有返回了一个新的String对象。
Ø char charAt(int index) 返回指定位置的字符。
Ø int compareTo(Object o)
int compareTo(String anotherString)
与另外一个对象进行比较。
Ø int compareToIgnoreCase(String str) 与另一个String进行比较, 不区分大小写
Ø String concat(String str) 连接两字符串, 可以直接用+, 因为Java给String覆盖了+
Ø static String copyValueOf(char[] data)
static String copyValueOf(char[] data, int offset, int count)
将data数组转换至String
Ø boolean endsWith(String suffix) 测试此String是否以suffix结尾。
boolean startsWith(String prefix) 测试此String是否以prefix开头。
Ø boolean equals(Object anObject)
boolean equalsIgnoreCase(String anotherString)
比较两字符串的值。 不相等则返回false
Ø byte[] getBytes() 根据缺省的字符编码将String转换成字节数组。
byte[] getBytes(String enc) 根据指定的编码将String转换万字节数组。
Ø void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) 拷贝字符至一数组中
Ø int indexOf(int ch) 从字串的起始位置查找字符ch第一次出现的位置
int indexOf(int ch, int fromIndex) 从指定的fromIndex位置向后查找第一次出现ch的位置,
int indexOf(String str)
int indexOf(String str, int fromIndex)
如果不存在ch或str都返回-1
Ø int lastIndexOf(int ch) 从字串的最终位置往前查找第一次出现ch的位置
int lastIndexOf(int ch, int fromIndex) 从指定的位置往前查找第一次出现ch的位置,
int lastIndexOf(String str)
int lastIndexOf(String str, int fromIndex)
如果不存在则返回-1
Ø int length() 该字符串的字符长度(一个全角的汉字长度为1)
Ø String replace(char oldChar, char newChar) 将字符oldChar全部替换为newChar, 返回一个新的字符串。
Ø String substring(int beginIndex) 返回从beginIndex开始的字符串子集
String substring(int beginIndex, int endIndex) 返回从beginIndex至endIndex结束的字符串的子集。 其中endIndex – beginIndex等于子集的字符串长度
Ø char[] toCharArray() 返回该字符串的内部字符数组
Ø String toLowerCase() 转换至小写字母的字符串
String toLowerCase(Locale locale)
String toUpperCase() 转换至大写字母的字符串
String toUpperCase(Locale locale)
Ø String toString() 覆盖了Object的toString方法, 返回本身。
Ø String trim() 将字符串两边的半角空白字符去掉, 如果需要去掉全角的空白字符得要自己写。
Ø static String valueOf(primitive p) 将其它的简单类型的值转换为一个String
StingBuffer是一个可变的字符串,它可以被更改。同时StringBuffer是Thread safe的, 你可以放心的使用, 常用的方法如下:
Ø StringBuffer append(param) 在StringBuffer对象之后追加param(可以为所有的简单类型和Object) 返回追加后的StringBuffer, 与原来的对象是同一份。
Ø char charAt(int index) 返回指定位置index的字符。
Ø StringBuffer delete(int start, int end) 删除指定区域start~end的字符。
Ø StringBuffer deleteCharAt(int index) 删除指定位置index的字符。
Ø void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) 同String的getChars方法
Ø StringBuffer insert(int offset, boolean b) 在指定位置offset插入param(为所有的简单类型与Object)
Ø int length() 同String的length()
Ø StringBuffer replace(int start, int end, String str) 将指定区域start~end的字符串替换为str
Ø StringBuffer reverse() 反转字符的顺序
Ø void setCharAt(int index, char ch) 设置字符ch至index位置。
Ø String substring(int start)
Ø String substring(int start, int end) 同String的subString
Ø String toString() 返回一个String
大家可能已经注意到很多方法都返回了一个StringBuffer对象, 但返回的这个对象与String的方法返回的String不一样, 返回的StringBuffer对象与被操作的StringBuffer对象是同一份, 而String的方法返回的String则肯定是重新生成的一个String。
3.2性能对比
因为String被设计成一种安全的字符串, 避免了C/C++中的尴尬。因此在内部操作的时候会频繁的进行对象的交换, 因此它的效率不如StringBuffer。 如果需要频繁的进行字符串的增删操作的话最好用StringBuffer。 比如拼SQL文, 写共函。 另: 编绎器对String的+操作进行了一定的优化。
x = “a” + 4 + “c”
会被编绎成
x = new StringBuffer().append(“a”).append(4).append(“c”).toString()
但:
x = “a”;
x = x + 4;
x = x + “c”;
则不会被优化。 可以看出如果在一个表达式里面进行String的多次+操作会被优化, 而多个表达式的+操作不会被优化。
3.3技巧
在Servlet2.3与JSP1.1以前画面post到后台的数据是通过ISO88591格式进行编码的, 则当遇到全角日文字的时候, 在后台request得到的数据可能就是乱码, 这个时候就得自己进行编码转换, 通过String.getBytes(String enc)方法得到一个字节流, 然后通过String(byte[] bytes, String enc)这个构造函数得到一个用新的编码生成的字符串. 例如将ISO88591的字符串转换成Shift_JIS的字符串, 方法如下:
public static String convertString(String str) {
if (str == null) { return null; }
try {
byte[] buf = str.getBytes("ISO8859_1");
return new String(buf, "Shift_JIS");
} catch (Exception ex) {ex.printStackTrace();return null; }
}
不过在最新的Servlet2.3与Jsp1.2中可以通过request.setCharacterEncoding来进行设置取值的码制, 不需要自己再做转换。
2.因为Java在计算String的长度是以字符为单位的, 因此一个全角与半角的字符长度是一样的, 但是DB中往往是根据字节来计算长度的, 因此我们在做Check的时候得要判断String的字节长, 可以用以下的方法:
public static String length(String str) {
if (str == null) { return 0; }
return str.getBytes().length;
}
4.数值,字符,布尔对象与简单类型的操作
简单的对照表如下:
与C等其它语言不同的是数值的范围不随平台的改变而改变, 这就保证了平台之间的统一性,提高了可移植性。
Number: Number本身是个抽象类, 不能直接使用, 所有直接从Number继承下来的子类都有以下几种方法:
Ø byte byteValue() 返回字节值
Ø double doubleValue() 返回double值
Ø float floatValue() 返回float值
Ø int intValue() 返回float值
Ø long longValue() 返回long值
Ø short shortValue() 返回short值
在需要通过Object来取得简单数据类型的值的时候就得用到以上的方法, 不过我不推荐不同类型之间的取值, 比如Long型的Object不要直接去调用intValue(),精度可能会丢失。
如果想通过String来得到一个数值类型的简单类型值, 一般在每个Number的类里面都有一个parseXXX(String)的静态方法, 如下:
Ø byte Byte.parseByte(String s)
Ø double Double.parseDouble(String s)
Ø float Float.parseFloat(String s)
Ø int Integer.parseInt(String s)
Ø long Long.parseLong(String s)
Ø short Short.parseShort(String s)
如果想直接从String得到一个Number型的Object,则每个Number类里面都有valueOf(String s) 这个静态方法。如:
Ø Byte Byte.valueOf(String s)
Ø Double Double.valueOf(String s)
Ø Float Float.valueOf(String s)
Ø Integer Integer.valueOf(String s)
Ø Long Long.valueOf(String s)
Ø Short Short.valueOf(String s)
一般的在构造一个Number的时候都可以通过一个String来完成, 比如:
Long longObject = new Long(“1234567890”);
等价于
Long longObject = Long.valueOf(“1234567890”);
因为每个Number的子类都实现了Object的toString()方法, 所以, 如果想得到一个String型的数值, 直接调用XXX.toString()就可以了。 如果想得到一个简单类型的String, 方法很多总结如下:
Ø 首先生成对应的Number Object类型, 然后调用toString()
Ø 调用Number子类 .toString(type t) 其中t就是简单类型的数据。
Ø 调用String.valueOf(type t) (推荐使用这种方法)
大家可以看出, 往往一种结果可以用多种方法实现, 总的原则就是深度最少优先。比如由一个String得到一个简单类型的值可以有以下两种方法:
Integer.parseInt(“12345”);
或
(new Integer(s)).intValue(“12345”);
当然应该使用第一种方法。
Character: Character对应着char类型, Character类里面有很多静态的方法来对char进行判断操作, 详细的操作请参照JDK API。 Java对字符的判断操作基本都是以Unicode进行的, 比如Character.isDigit(char ch)这个方法, 不光半角的0-9符合要求, 全角的日文0-9也是符合要求的。
Boolean: Boolean对应着boolean类型, boolean只有true和false两个值, 不能与其它数值类型互换, 可以通过字符串”true”以及”false”来得到Object的Boolean, 也可以通过简单类型的boolean得到Boolean, 常用方法如下:
Ø Boolean(boolean value) 通过简单类型的boolean构造Boolean
Ø Boolean(String s) 通过String(“true”, “false”)构造Boolean
Ø boolean booleanValue() 由Object的Boolean得到简单类型的boolean值
Ø boolean equals(Object obj) 覆盖了Object的.equals方法, Object值比较
Ø static Boolean valueOf(String s) 功能与构造函数Boolean(String s)一样
5.Class, ClassLoader
Java是一种介于解释与编绎之间的语言, Java代码首先编绎成字节码, 在运行的时候再翻译成机器码。 这样在运行的时候我们就可以通过Java提供的反射方法(reflect)来得到一个Object的Class的额外信息, 灵活性很大,可以简化很多操作。
Class: 任何一个Object都能通过getClass()这个方法得到它在运行期间的Class。 得到这个Class之后可做的事情就多了, 比如动态得到它的构造函数, 成员变量, 方法等等。 还可以再生成一份新的实例, 下面只给出几个我们常用的方法, 更详细的用法参照Java API
Ø Class Class.forName(String className) throws ClassNotFoundException: 这是个静态方法, 通过一个Class的全称来得到这个Class。
Ø String getName() 取得这个Class的全称, 包括package名。
Ø Object newInstance() 得到一个实例, 调用缺省的构造函数。
例如我们有一个类: com.some.util.MyClass 如果得到它的一个实例呢? 可能有以下两种方法:
MyClass myClass = new MyClass(), 直接通过操作符new生成;
或者:
MyClass myClass = (MyClass) Class.forName(“com.some.util.MyClass”).newInstance();
也许有人就会怀疑第二种方法实际意义, 能够直接new出来干嘛绕弯。 但实际上它的用处却很大, 举个例子: 用过struts的人都知道, 在action-config.xml当中定义了一系列的formBean与actionBean, 当然每个form与action都具有同类型, 这样在一个request过来的时候我可以动态的生成一个form与action的实例进行具体的操作, 但在编码的时候我并不知道具体是何种的form与action, 我只调用它们父类的方法。 你如果要用第一种方法的话, 你得在编码的时候通过一个标志来判断每一次request需要具体生成的form与action, 代码的灵活性大大降低。 总的来说在面向接口的编程当中经常使用这种方法, 比如不同数据库厂家的JDBC Driver都是从标准的JDBC接口继承下去的, 我们在写程序的时候用不着管最终是何种的Driver, 只有在运行的时候确定。 还有XML的Parser也是, 我们使用的只是标准的接口, 最后到底是谁来实现它的, 我们用不着去管。
ClassLoader: ClassLoader是一个抽象类,一般的系统有一个缺省的ClassLoader用来装载Class, 用ClassLoader.getSystemClassLoader()可以得到。不过有时候为了安全或有其它的特殊需要我们可以自定义自己的ClassLoader来进行loader一些我们需要的Class, 比如有的产品它用了自己的ClassLoader可以指定Class只从它指定的特定的JAR文件里面来loader,如果你想通过覆盖ClassPath方法来想让它用你的Class是行不通的。 有兴趣的可以参照Java API 的更详细的用法说
InputStream
ByteArrayInputStream
FileInputStream
FilterInputStream
ObjectInputStream
PipedInputStream
SequenceInputStream
BufferedInputStream
DataInputStream
PushbackInputStream
LineNumberInputStream
JDK1.1输入流
Reader
BufferedReader
FilterReader
PipedReader
StringReader
InputStreamReader
CharArrayReader
FileReader
PushbackReader
LineNumberReader
JDK1.0输出流
OutputStream
BufferedOutputStream
DataOutputStream
PrintStream
ByteArrayOutputStream
FileOutputStream
FilterOutputStream
ObjectOutputStream
PipedOutputStream
JDK1.1输出流
Writer
BufferedWriter
FilterWriter
PipedWriter
StringWriter
FileWriter
PrintWriter
OutputStreamWriter
CharArrayWriter
如果你刚刚接触Java的IO部分, 你可能会感觉无从入手, 确实Java提供了过多的类,反而让人感到很乱。
可将Java库的IO类分为输入与输出两个部分, 在1.0版本中提供了两个抽象基类, 所有输入的类都从InputStream继承, 所有输出的类都从OutputStream继承, 1.1提供了两个新的基类, 负责输入的Reader与输出的Writer, 但它们并不是用来替换原来老的InputStream与OutputStream, 它们主要是让Java能更好的支持国际化的需求。 原来老的IO流层只支持8位字节流, 不能很好地控制16位Unicode字符。 Java内含的char是16位的Unicode, 所以添加了Reader和Writer层次以提供对所有IO操作中的Unicode的支持。 除此之外新库也对速度进行了优化, 可比旧库更快地运行。
InputStream的类型:
1. 字节数组
2. String对象
3. 文件
4. 管道, 可以从另外一个输出流得到一个输入流
5. 一系列的其他流, 可以将这些流统一收集到单独的一个流内。
6. 其他起源(如socket流等)
还有一个是File类, Java中一个目录也是一个文件,可以用file.isFile()和file.isDirectory()来进行判断是文件还是目录。 File 对象可能作为参数转换为文件流进行操作。 具体操作参照Java IO API。
常用方法:
/**
拼文件名, 在windows()跟unix(/)上的文件分割符是不一样的
可以通过File类的静态成员变量separator取得
*/
public static String concatFileName(String dir, String fileName) {
String fullFileName = “”;
if (dir.endsWith(File.separator)) {fullFileName = dir + fileName;
} else {fullFileName = dir + File.separator + fileName;}
return fullFileName; }
/**
从控制台读取输入的数据, System.in (InputStream) 先转换至InputStreamReader再用
BufferedReader进行读取.
*/
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String str = “”;
while (str != null) {str = in.readLine();// process(str); }
/**
*/
BufferedReader in = new BufferedReader(new FileReader(“infilename”));
String str;
while ((str = in.readLine()) != null) {// process(str); }
in.close();
/**
*/
BufferedWriter out = new BufferedWriter(new FileWriter(“outfilename”));
out.write(“a String”);
out.close();
/**
追加新的数据到一个文件中去, 如果原文件不存在
则新建这个文件.
*/
BufferedWriter out = new BufferedWriter(new FileWriter(“filename”, true));
out.write(“aString”);
out.close();
/**
*/
ObjectOutput out = new ObjectOutputStream(new FileOutputStream(“filename.ser”));
out.writeObject(object);
out.close();
/**
*/
ObjectInputStream in = new ObjectInputStream(new FileInputStream(“filename.ser”));
Object object = (Object) in.readObject();
in.close();
/**
*/
BufferedReader in = new BufferedReader(new InputStreamReader(
new FileInputStream(“infilename”), “UTF8”));
String str = in.readLine();
/**
*/
Writer out = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(“outfilename”), “UTF8”));
out.write(“a String”);
out.close();
由上面的例子可以看出, 流之间可以互相转换, 如果想对流进行字符操作最好将之转换成BufferedReader与BufferedWriter这样可以提高读写的效率。
需要注意的是一般来说流的大小是得不到的, 虽然一般的InputStream都有一个available()的方法可以返回这个流可以读到的字节数, 不过这个方法有时候不会很准确, 比如在读取网络传输的流的时候, 取到的长度并不一定是真实有效的长度。
大家在写程序的时候可能已经注意到一些类都继承了java.io.Serializable这个接口, 其实继承了这个接口之后这个类的本身并不做任何事情, 只是这个类可以被序列化并通过流来进行传输, 并可被还原成原Object。 这个流可以通过网络传输(EJB 中进行传递参数和返回值的Data Class), 也可以保存到文件中去(ObjectOutputStream)。
Java的集合类只能容纳对象句柄, 对于简单类型的数据存放, 只能通过数据来存放, 数组可以存放简单类型的数据也能存放对象。
Java提供了四种类型的集合类: Vector(矢量), BitSet(位集), Stack(堆栈), Hashtable(散列表)。
矢量: 一组有序的元素, 可以通过index进行访问。
位集: 其实就是由二进制位构成的Vector, 用来保存大量”开-关”信息, 它所占的空间比较小, 但是效率不是很高, 如果想高效率访问, 还不如用固定长度的数组。
堆栈: 先入后出(LIFO)集合, java.util.Stack类其实就是从Vector继承下来的, 实现了pop, push方法。
散列表: 由一组组“键--值”组成, 这里的键必须是Object类型。 通过Object的hashCode进行高效率的访问。
对于这些集合之间的关联关系见下图, 其中标色的部分为我们常用的类。
由上图可以看出, 基本接口有两个:
Collection: 所有的矢量集合类都从它继承下去的, 但并不直接从它继承下去的。 List与Set这两个接口直接继承了Collection, 他们的区别是List里面可以保存相同的对象句柄, 而Set里面的值是不重复的。 我们经常用的Vector与ArrayList就是从List继承下去的, 而HashSet是从Set继承的。
Map:散列表的接口, Hashtable与HashMap继承了这个接口。
下面给出常用集合类的常用方法。
/**
Vector 与 ArrayList的操作几乎是一样的
常用的追加元素用add(), 删除元素用remove()
取元素用get(), 遍历它可以循环用get()取. 或者
先得到一个Iterator, 然后通过遍历Iterator的方法
遍历Vector或ArrayList
*/
// 生成一个空的Vector
Vector vector = new Vector();
// 在最后追加一个元素。
vector.add(“one”);
vector.add(“two”);
// 在指定的地方设置一个值
vector.set(0, “new one”);
// 移走一个元素或移走指定位置的元素
vector.remove(0);
// 用for循环遍历这个Vector
for (int i = 0; i < vector.size(); i++) {
String element = (String) vector.get(i); }
// 用枚举器(Enumeration)遍历它(只有Vector有,ArrayList没有)
Enumeration enu = vector.elements();
while (enu.hasMoreElements()) {
enu.nextElement();}
// 用反复器(Iterator)遍历它
Iterator it = vector.iterator();
while (it.hasNext()) {it.next();}
/**
Hashtable与HashMap的操作, 追加元素用put(不是add)
删除元素用remove, 遍历可以用Iterator 既可以遍历
它的key, 也可以是value
*/
// 生成一个空的Hashtable或HashMap
Hashtable hashtable = new Hashtable();
// 追加一个元素
hashtable.put(“one”, “one object value”);
// 删除一个元素
hashtable.remove(“one”);
// 用Iterator遍历
Iterator keyIt = hashtable.keySet().iterator();
while (keyIt.hasNext()) {
Object keyName = keyIt.next();
String value = (String) hashtable.get(keyName); }
Iterator valueIt = hashtable.values().iterator();
while (valueIt.hasNext()) {
valueIt.next();}
// 用Enumeration遍历, 只有Hashtable有, HashMap没有.
Enumeration enu = hashtable.elements();
while (enu.hasMoreElements()) {
enu.nextElement();}
说明: Enumeration是老集合库中的接口, 而Iterator是新集合(1.2)中出现的, 而Vector与Hashtable也都是老集合中的类, 所以只有Vector与Hashtable可以用Enumeration。
Vector与ArrayList对比:
虽然在使用的时候好象这两个类没什么区别, 它们都是从List继承下来的, 拥有相同的方法, 但它们的内部还是有些不同的,
Ø 首先Vector在内部的一些方法作了线程同步(synchronized)。 同步的代价就是降低了执行效率, 但提高了安全性。而ArrayList则是线程不同步的, 可以多线程并发读写它。
Ø 内部数据增长率。 所有的这些矢量集合在内部都是用Object的数组进行存储和操作的。 所以也就明白了为什么它可以接受任何类型的Object, 但取出来的时候需要进行类型再造。 Vector与ArrayList具有自动伸缩的功能, 我们不用管它size多大, 我们都可以在它的后面追加元素。 Vector与ArrayList内部的数组增长率是不一样的, 当内部的数组不能容纳更多元素的时候, Vector会自动增长到原两倍大小, ArrayList会变为原一倍半大小, 而不是我们所想象的一个元素一个元素的增长。
Hashtable与HashMap对比:
Hashtable与HashMap都是从Map继承下来的, 方法几乎都一样, 它们内部有两个不同点:
Ø 与Vector和ArrayList一样, 它们在线程同步是不同的, Hashtable在内部做了线程同步, 而HashMap是线程不同步的。
Ø HashMap的键与值都可以为null, 而Hashtable不可以, 如果你试图将一个null值放到Hashtable里面去, 会抛一个NullPointException的。
性能对比:
抛开不常用的集合不讲, 每种集合都应该有一个我们常用的集合类, 而在不同的场合下应该使用效率最高的一个。 一般来说我推荐尽量使用新的集合类, 除非不得已, 比如说需要用用了老集合类写的产品的程序。 也就是说尽量使用ArrayList与HashMap, 而少使用Vector与Hashtable。
Ø 在单线程中使用ArrayList与HashMap, 而在多线程中如果需要进行线程同步可以使用Vector与Hashtable, 但也可以用synchronized对ArrayList与HashMap进行同步, 不过同步后的ArrayList与HashMap是比Vector与Hashtable慢的。 不过我认为需要进行线程同步的地方并不多。 如果一个变量定义在方法内部同时只可能有一个线程对之进行操作, 就不必要进行同步, 如果定义在类的内部并且不是静态的, 属于实例变量, 而这个类并没有被多线程使用也就不必要同步。
一般自己写的程序很少会自己去另开线程的, 但在Web开发的时候, 如果用了Servlet, 则每个request都是一个线程, 也就是说每个Servlet都是在多线程环境下运行的, 如果Servlet中使用了全局静态的成员变量就得小心点儿, 如果需要同步就得在方法上加上synchronized修饰符, 如果允许多个线程操作它, 并且你知道不会有什么冲突问题就可以大胆的使用ArrayList与HashMap。 另外如果在多线程中有线程在对ArrayList或HashMap进行修改(结构上的修改), 而有一个线程在用Iterator进行读取操作, 这个时候就有可能会抛ConcurrentModificationException, 因为用Iterator的时候, 不允许原List的结构改变。但可以用get方法来取。
常用技巧:
采用面向接口的编程技巧, 比如现在需要写一个共通函数,对矢量集合类诸如Vector,ArrayList,HashSet等等进行操作, 但我并不知道最终用户会具体传给我什么类型的类, 这个时候我们可以使用Collection接口, 从而使代码具有很大的灵活性。 代码示例如下:
/**
将list里面的所有元素用sep连接起来,
list可以为Vector, ArrayList, HashSet等。
*/
public static String join(String sep, Collection list) {
StringBuffer sb = new StringBuffer();
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
sb.append(iterator.next());
if (iterator.hasNext()) { sb.append(sep); }
}
return sb.toString();
}
利用Set进行Unique, 比如有一组对象(其中有对象是重复的), 但我们只对不同的对象感兴趣, 这个时候可以使用HashSet这个集合类, 然后可以通过覆盖Object的equals方法来选择自定义判断相等的rule。 缺省的是地址判断。 例:
class DataClass {
private String code = null;
private String name = null;
public void setCode(String code) {this.code = code; }
public String getCode() {return this.code; }
public void setName(String name) {this.name = name; }
public String getName() {return this.name; }
public boolean equals(DataClass otherData) {
if (otherData != null) {
if (this.getCode() != null&& this.getCode().equals(otherData.getCode()) {
return true;
}
}
return false;
}}
DataClass data1 = new DataClass();
DataClass data2 = new DataClass();
data1.setCode(“1”);
data2.setCode(“1”);
HashSet singleSet = new HashSet();
singleSet.add(data1);
singleSet.add(data2);
结果singleSet里面只有data1, 因为data2.equals(data1), 所以data2并没有加进去。
灵活的设计集合的存储方式, 以获得较高效的处理。 集合里面可以再嵌套集合, 例:在ArrayList里面存放HashMap, HashMap里面再嵌套HashMap。
8.ResourceBundle, Properties
ResourceBundle:开发一个项目, 配置文件是少不了的, 一些需要根据环境进行修改的参数, 都有得放到配置文件中去, 在Java中一般是通过一个properties文件来实现的, 这个文件以properties结尾。 内部结构是二维的, 以key=value的形式存在。 如下:
options.column.name.case=1
options.column.bean.serializable=1
options.column.bean.defaultconstructor=1
options.column.method.setter=1
options.general.user.version=1.0
database.connection[0]=csc/csc@localhost_oci8
database.connection[1]=cscweb/cscweb@localhost_thin
ResourceBundle用来解析这样的文件, 它的功能是可以根据你的Locale来进行解析配置文件, 如果一个产品需要进行多语言支持, 比如在不同语种的系统上, 会显示根据它的语言显示相应的界面语言, 就可以定义多份的properties文件, 每个文件的key是一样的, 只是value不一样, 然后在application起动的时候, 可以判别本机的Locale来解析相应的properties文件。 Properties文件里面的数据得要是Unicode。 在jdk下面可以用native2ascii这个命令进行转换。 例: native2ascii Message.txt Message.properties 会生成一个Unicode的文件。
Properties: Properties这个类其实就是从Hashtable继承下来的, 也就是说它是一个散列表, 区别在于它的key与value都是String型的, 另外也加了几个常用的方法:
Ø String getProperty(String key) 取得一个property
Ø String getProperty(String key, String defaultValue) 取property, 如果不存在则返回defaultValue。
Ø void list(PrintStream out) 向out输出所有的properties
Ø void list(PrintWriter out)
Ø Enumeration propertyNames() 将所有的property key名以Enumeration形式返回。
Ø Object setProperty(String key, String value) 设置一个property。
ResourceBundle与Properties一般结合起来使用。 它们的用法很简单, 由ResourceBundle解析出来的key与value然后放至到一个静态的Properties成员变量里面去, 然后就可以通过访问Properties的方法进行读取Property。 下面给个简单的例子:
public class PropertyManager implements Serializable {
/** 定义一个静态的Properties变量 */
private static Properties properties = new Properties();
/**
通过一个类似于类名的参数进行Property文件的初期化
比如现在有一个文件叫Message.properties, 它存放在
ejb/util下面并且, 这个目录在运行的classpath下面
则in就为ejb.util.Message
*/
public static void init(String in) throws MissingResourceException {
ResourceBundle bundle = ResourceBundle.getBundle(in);
Enumeration enum = bundle.getKeys();
Object key = null;
Object value = null;
while (enum.hasMoreElements()) {
key = enum.nextElement();
value = bundle.getString(key.toString());
properties.put(key, value);
}}
/**
*/
public static String getProperty(String key) {return properties.get(key); }
/**
*/
public static void setProperty(String key, String value) {properties.put(key, value); }
}
不过现在的Java产品中,越来越倾向于用XML替换Properties文件来进行配置。 XML配置具有层次结构清楚的优点。
Exception
Error
RuntimeException
ClassNotFoundException
IOException
SQLException
IndexOutOfBoundsException
ClassCastException
NullPointerException
OutOfMemoryError
StackOverflowError
NoClassDefFoundError
Java采用违例(Exception)处理机制来进行错误处理。 违例机制的一个好处就是能够简化错误控制代码, 我们再也不用检查一个特定的错误, 然后在程序的多处地方对其进行控制。 此外, 也不需要在方法调用的时候检查错误(因为保证有人能够捕获这里的错误)。 我们只需要在一个地方处理问题:”违例控制模块”或者”违例控制器”。 这样可有效减少代码量, 并将那些用于描述具体操作的代码与专门纠正错误的代码分隔开。
一个完整的违例例子:
public void throwTest() throws MyException {
try {
...
} catch (SQLException se) {
cat.error("", se);
throw new MyException(se.getMessage());
} catch (Exception e) {cat.error("", e);
} finally {
...
}
}
如果一段代码有可能会抛出违例可以用try {} catch {}来处理。 被catch到的违例可以再抛出, 也可以转换为其它类型的Exception抛出。 finally块里面的代码总会被执行到的, 不管前面是否已经throw或return了。
Throwable是所有违例的基类, 它有两种常规类型。 其中, Error代表编绎期和系统错误, 我们一般不必特意捕获它们。 Exception是可以从任何标准Java库的类方法中掷出的基本类型。
看上面的图, 如果是Error的子类或是RuntimeException的子类这种违例有一定的特殊性, 可以说我们可以当它们不存在, 当这种违例抛出的时候, 我们可以不catch它, 也可以不在方法上throws它。 RuntimeException一般代表的是一个编程错误, 是完全可以避免的。
性能注意点: 因为使用了Exception之后是要影响一些效率的, 所以Exception不能滥用。一般的不要用Exception来控制业务流程, 其次不要循环体内使用。
技巧:我们可以从Exception或直接从Throwable继承写我们自己的Exception, 然后根据业务需要抛不同种类的Exception。
下面为常用的处理流程:
DriverManager
Connection
Statement
PreparedStatement
CallableStatement
ResultSet
简单地说,JDBC 可做三件事:
与数据库建立连接
发送 SQL 语句
处理结果
下列代码段给出了以上三步的基本示例:
Class.forName(“oracle.jdbc.driver.OracleDriver”).newInstance();
Connection conn = DriverManager.getConnection (
“jdbc:oracle:thin:@eai-sol:1521:eai_db”, “csc2”, “csc2”);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(“SELECT CONTACTID FROM CONTACTINFO”);
while (rs.next()) {long contactID = rs.getLong(“CONTACTID”);}
下面对常用的几个类和接口做些简单的说明。
DriverManager:
DriverManager类是 JDBC 的管理层,作用于用户和驱动程序之间。它跟踪可用的驱动程序,并在数据库和相应驱动程序之间建立连接。另外,DriverManager 类也处理诸如驱动程序登录时间限制及登录和跟踪消息的显示等事务。对于简单的应用程序,一般程序员需要在此类中直接使用的唯一方法是 DriverManager.getConnection。正如名称所示,该方法将建立与数据库的连接。
Class.forName(“oracle.jdbc.driver.OracleDriver”).newInstance();
Connection conn = DriverManager.getConnection (
“jdbc:oracle:thin:@eai-sol:1521:eai_db”, “csc2”, “csc2”);
其中第一句话的作用是在当前的环境中load一个DB Driver, 有人可能觉得奇怪, 这句话执行完之后, 后面怎么知道去用这个Driver呢? 其实DriverManager可以从load的classes里面找到注册过的driver,然后使用它所找到的第一个可以成功连接到给定 URL 的驱动程序。 第二句话的三个参数分别是URL, User, Password。Driver不一样, URL可能也不一样。
Statement:
Statement 对象用于将 SQL 语句发送到数据库中。实际上有三种 Statement 对象,它们都为在给定连接上执行 SQL 语句的包容器:Statement、PreparedStatement(它从 Statement 承而来)和 CallableStatement(它从 PreparedStatement 继承而来)。它们都专用于发送定类型的 SQL 语句: Statement 对象用于执行不带参数的简单 SQL 语句;PreparedStatement 对象用于执行带或不带 IN 参数的预编译 SQL 语句;CallableStatement 对象用于执行对数据库已存储过程的调用。
Statement 接口提供了执行语句和获取结果的基本方法。PreparedStatement 接口添加了处理 IN 参数的方法;而 CallableStatement 添加了处理 OUT 参数的方法。
Ø 创建 Statement 对象
建立了到特定数据库的连接之后,就可用该连接发送 SQL 语句。Statement 对象用 Connection 的方法 createStatement 创建,如下列代码段中所示:
Statement stmt = conn.createStatement();
为了执行 Statement 对象,被发送到数据库的 SQL 语句将被作为参数提供给 Statement 的方法:
ResultSet rs = stmt.executeQuery(“SELECT a, b, c FROM table1”);
Ø 使用 Statement 对象执行语句
Statement 接口提供了三种执行 SQL 语句的方法:executeQuery、executeUpdate 和execute。使用哪一个方法由 SQL 语句所产生的内容决定。
方法 executeQuery 用于产生单个结果集的语句,例如 SELECT 语句。
方法 executeUpdate 用于执行 INSERT、UPDATE 或 DELETE 语句以及 SQL DDL(数据定义语言)语句,例如 CREATE TABLE 和 DROP TABLE。INSERT、UPDATE 或 DELETE 语句的效果是修改表中零行或多行中的一列或多列。executeUpdate 的返回值是一个整数,指示受影响的行数(即更新计数)。对于 CREATE TABLE 或 DROP TABLE 等不操作行的语句,executeUpdate 的返回值总为零。
方法 execute 用于执行返回多个结果集、多个更新计数或二者组合的语句。
Ø 语句完成
当连接处于自动提交模式时,其中所执行的语句在完成时将自动提交或还原。语句在已执行且所有结果返回时,即认为已完成。对于返回一个结果集的 executeQuery 方法,在检索完 ResultSet 对象的所有行时该语句完成。对于方法 executeUpdate,当它执行时语句即完成。但在少数调用方法 execute 的情况中,在检索所有结果集或它生成的更新计数之后语句才完成。
Ø 关闭 Statement 对象
Statement 对象将由 Java 垃圾收集程序自动关闭。而作为一种好的编程风格,应在不需要 Statement 对象时显式地关闭它们。这将立即释放 DBMS 资源,有助于避免潜在的内存问题。 关闭Statement用 stmt.close() 方法。
ResultSet:
ResultSet 包含符合 SQL 语句中条件的所有行,并且它通过一套 get 方法(这些 get 方法可以访问当前行中的不同列)提供了对这些行中数据的访问。ResultSet.next 方法用于移动到 ResultSet 中的下一行,使下一行成为当前行。
结果集一般是一个表,其中有查询所返回的列标题及相应的值。例如,如果查询为 SELECT a, b, c FROM Table1,则结果集将具有如下形式:
a b c
12345 Cupertino CA
83472 Redmond WA
83492 Boston MA
下面的代码段是执行 SQL 语句的示例。该 SQL 语句将返回行集合,其中列 1 为 int,列 2 为 String,而列 3 则为日期型:
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(“SELECT a, b, c FROM Table1”);
while (rs.next()) {
int i = rs.getInt(“a”);
String s = rs.getString(“b”);
Timestamp t = rs.getTimestamp(“c”);
}
Ø 行和光标
ResultSet 维护指向其当前数据行的光标。每调用一次 next 方法,光标向下移动一行。最初它位于第一行之前,因此第一次调用 next 将把光标置于第一行上,使它成为当前行。随着每次调用 next 导致光标向下移动一行,按照从上至下的次序获取 ResultSet 行。在 ResultSet 对象或其父辈 Statement 对象关闭之前,光标一直保持有效。
Ø 列
方法 getXXX 提供了获取当前行中某列值的途径。在每一行内,可按任何次序获取列值。但为了保证可移植性,应该从左至右获取列值,并且一次性地读取列值。列名或列号可用于标识要从中获取数据的列。例如,如果 ResultSet 对象 rs 的第二列名为“title”,并将值存储为字符串,则下列任一代码将获取存储在该列中的值:
String s = rs.getString(“title”);
String s = rs.getString(2);
注意列是从左至右编号的,并且从列 1 开始。同时,用作 getXXX 方法的输入的列名不区分大小写。 为了代码的可维护性与可读性, 应该禁止用index的方法来取值, 要用读列名的方法, 如上面的第一行取值方法。
Ø 数据类型和转换
对于 getXXX 方法,JDBC 驱动程序试图将基本数据转换成指定 Java 类型,然后返回适合的 Java 值。例如,如果 getXXX 方法为 getString,而基本数据库中数据类型为 VARCHAR,则 JDBC 驱动程序将把 VARCHAR 转换成 Java String。getString 的返回值将为 Java String 对象。
Ø NULL 结果值
要确定给定结果值是否是 JDBC NULL,必须先读取该列,然后使用 ResultSet.wasNull 方法检查该次读取是否返回 JDBC NULL。
PreparedStatement:
该 PreparedStatement 接口继承 Statement,并与之在两方面有所不同:
PreparedStatement 实例包含已编译的 SQL 语句。这就是使语句“准备好”。 包含于 PreparedStatement 对象中的 SQL 语句可具有一个或多个 IN 参数。IN 参数的值在 SQL 语句创建时未被指定。相反的,该语句为每个 IN 参数保留一个问号(“?”)作为占位符。每个问号的值必须在该语句执行之前,通过适当的 setXXX 方法来提供。
由于 PreparedStatement 对象已预编译过,所以其执行速度要快于 Statement 对象。因此,多次执行的 SQL 语句经常创建为 PreparedStatement 对象,以提高效率。
作为 Statement 的子类,PreparedStatement 继承了 Statement 的所有功能。另外它还添加了一整套方法,用于设置发送给数据库以取代 IN 参数占位符的值。同时,三种方法 execute、 executeQuery 和 executeUpdate 已被更改以使之不再需要参数。
Ø 创建 PreparedStatement 对象
以下的代码段(其中 conn 是 Connection 对象)创建包含带两个 IN 参数占位符的 SQL 语句的 PreparedStatement 对象:
PreparedStatement pstmt=conn.prepareStatement(“UPDATE table1 SET a = ? WHERE b = ?”);
pstmt 对象包含语句 " UPDATE table1 SET a = ? WHERE b = ?",它已发送给 DBMS,并为执行作好了准备。
Ø 传递 IN 参数
在执行 PreparedStatement 对象之前,必须设置每个 ? 参数的值。这可通过调用 setXXX 方法来完成,其中 XXX 是与该参数相应的类型。例如,如果参数具有 Java 类型 long,则使用的方法就是 setLong。setXXX 方法的第一个参数是要设置的参数的序数位置,第二个参数是设置给该参数的值。例如,以下代码将第一个参数设为 123456789,第二个参数设为 100000000:
pstmt.setLong(1, 123456789);
pstmt.setLong(2, 100000000);
CallableStatement:
CallableStatement 对象为所有的 DBMS 提供了一种以标准形式调用已储存过程(也就是SP)的方法。已储存过程储存在数据库中。对已储存过程的调用是 CallableStatement 对象所含的内容。有两种形式:一种形式带结果参数,另一种形式不带结果参数。结果参数是一种输出 (OUT) 参数,是已储存过程的返回值。两种形式都可带有数量可变的输入(IN 参数)、输出(OUT 参数)或输入和输出(INOUT 参数)的参数。问号将用作参数的占位符。
在 JDBC 中调用已储存过程的语法如下所示。注意,方括号表示其间的内容是可选项;方括号本身并不是语法的组成部份。
{call 过程名[(?, ?, …)]}
返回结果参数的过程的语法为:
{? = call 过程名[(?, ?, …)]}
不带参数的已储存过程的语法类似:
{call 过程名}
通常,创建 CallableStatement 对象的人应当知道所用的 DBMS 是支持已储存过程的,并且知道这些过程都是些什么。然而,如果需要检查,多种 DatabaseMetaData 方法都可以提供这样的信息。例如,如果 DBMS 支持已储存过程的调用,则 supportsStoredProcedures 方法将返回 true,而 getProcedures 方法将返回对已储存过程的描述。
CallableStatement 继承 Statement 的方法(它们用于处理一般的 SQL 语句),还继承了 PreparedStatement 的方法(它们用于处理 IN 参数)。CallableStatement 中定义的所有方法都用于处理 OUT 参数或 INOUT 参数的输出部分:注册 OUT 参数的 JDBC 类型(一般 SQL 类型)、从这些参数中检索结果,或者检查所返回的值是否为 JDBC NULL。
Ø 创建 CallableStatement 对象
CallableStatement 对象是用 Connection 方法 prepareCall 创建的。下例创建 CallableStatement 的实例,其中含有对已储存过程 Csc_ GetCustomId调用。该过程有两个变量,但不含结果参数:
CallableStatement cstmt = con.prepareCall("{call CSC_GetCustomId (?, ?, ?)}");
其中 ? 占位符为 IN、 OUT 还是 INOUT 参数,取决于已储存过程 Csc_ GetCustomId。
Ø IN 和 OUT 参数
将IN 参数传给 CallableStatement 对象是通过 setXXX 方法完成的。该方法继承自 PreparedStatement。所传入参数的类型决定了所用的 setXXX 方法(例如,用 setFloat 来传入 float 值等)。
如果已储存过程返回 OUT 参数,则在执行 CallableStatement 对象以前必须先注册每个 OUT 参数的 JDBC 类型(这是必需的,因为某些 DBMS 要求 JDBC 类型)。注册 JDBC 类型是用 registerOutParameter 方法来完成的。语句执行完后,CallableStatement 的 getXXX 方法将取回参数值。正确的 getXXX 方法是为各参数所注册的 JDBC 类型所对应的 Java 类型也就是说, registerOutParameter 使用的是 JDBC 类型(因此它与数据库返回的 JDBC 类型匹配),而 getXXX 将之转换为 Java 类型。下面给出CSC中的一个例子:
String sqlSp = “{call CSC_GetCustomId(?, ?, ?)}”;
cstmt = conn.prepareCall(sqlSp.toString());
cstmt.registerOutParameter(1, Types.NUMERIC);
cstmt.registerOutParameter(2, Types.NUMERIC);
cstmt.registerOutParameter(3, Types.VARCHAR);
cstmt.execute();
long customerID = cstmt.getLong(1);
long lRet = cstmt.getLong(2);
String sErr = cstmt.getString(3);
Ø INOUT 参数
既支持输入又接受输出的参数(INOUT 参数)除了调用 registerOutParameter 方法外,还要求调用适当的 setXXX 方法(该方法是从 PreparedStatement 继承来的)。setXXX 方法将参数值设置为输入参数,而 registerOutParameter 方法将它的 JDBC 类型注册为输出参数。setXXX 方法提供一个 Java 值,而驱动程序先把这个值转换为 JDBC 值,然后将它送到数据库中。这种 IN 值的 JDBC 类型和提供给 registerOutParameter 方法的 JDBC 类型应该相同。然后,要检索输出值,就要用对应的 getXXX 方法。例如,Java 类型为 byte 的参数应该使用方法 setByte 来赋输入值。应该给 registerOutParameter 提供类型为 TINYINT 的 JDBC 类型,同时应使用 getByte 来检索输出值。下例假设有一个已储存过程 reviseTotal,其唯一参数是 INOUT 参数。方法 setByte 把此参数设为 25,驱动程序将把它作为 JDBC TINYINT 类型送到数据库中。接着,registerOutParameter 将该参数注册为 JDBC TINYINT。执行完该已储存过程后,将返回一个新的 JDBC TINYINT 值。方法 getByte 将把这个新值作为 Java byte 类型检索。
CallableStatement cstmt = con.prepareCall("{call reviseTotal(?)}");
cstmt.setByte(1, 25);
cstmt.registerOutParameter(1, java.sql.Types.TINYINT);
cstmt.executeUpdate();
byte x = cstmt.getByte(1);
Singleton模式主要作用是保证在Java应用程序中,一个Class只有一个实例存在。一般有两种方法:
Ø 定义一个类,它的构造函数为private的,所有方法为static的。其他类对它的引用全部是通过类名直接引用。例如:
private SingleClass() {}
public static String getMethod1() {}
public static ArrayList getMethod2() {}
Ø 定义一个类,它的构造函数为private的,它有一个static的private的该类变量,通过一个public的getInstance方法获取对它的引用,继而调用其中的方法。例如:
private staitc SingleClass _instance = null;
private SingleClass() {}
public static SingleClass getInstance() {
if (_instance == null) {_instance = new SingleClass();}
return _instance; }
public String getMethod1() {}
public ArrayList getMethod2() {}
2.Prototype模式
Prototype模式用于创建对象,尤其是当创建对象需要许多时间和资源时。在Java中,Prototype模式的实现是通过方法clone(),该方法定义在Java的根对象Object中, 因此,Java中的其他对象只要覆盖它就行了。通过clone(),我们可以从一个对象获得更多的对象,
并请可以按照我们的需要修改他们的属性。
public class Prototype implements Cloneable {
private String Name;
public rototype(String Name) {this.Name = Name; }
public void setName(String Name) {this.Name = Name; }
public String getName() {return Name; }
public Object clone() {
try{return super.clone();
}catch(CloneNotSupportedException cnse){
cnse.printStackTrace();
return null;
}
}}
Prototype p = new Prototype(“My First Name”);
Prototype p1 = p.clone();
p.setName(“My Second Name”);
Prototype p2 = p.clone();
Ø Factory模式
利用给Factory对象传递不同的参数,以返回具有相同基类或实现了同一接口的对象。
Ø Abstract Factory模式
先利用Factory模式返回Factory对象,在通过Factory对象返回不同的对象!
下面给出Sun XML Parser中的例子:
// 1. Abstract Factory模式
SAXParserFactory spf = SAXParserFactory.newInstance();
String validation = System.getProperty (“javax.xml.parsers.validation”, “false”);
if (validation.equalsIgnoreCase(“true”)) {spf.setValidating (true); }
// 2. Factory模式
SAXParser sp = spf.newSAXParser();
parser = sp.getParser();
parser.setDocumentHandler(this);
parser.parse (uri);
注意:
SAXParserFactory 的定义为:
public abstract class SAXParserFactory extends java.lang.Object
SAXParserFactoryImpl 的定义为:
public class SAXParserFactoryImpl extends javax.xml.parsers.SAXParserFactory
public static SAXParserFactory newInstance() {
String factoryImplName = null;
try {
factoryImplName =System.getProperty("javax.xml.parsers.SAXParserFactory",
“com.sun.xml.parser.SAXParserFactoryImpl”);
} catch (SecurityException se) {
factoryImplName = “com.sun.xml.parser.SAXParserFactoryImpl”;
}
SAXParserFactory factoryImpl;
try {
Class clazz = Class.forName(factoryImplName);
factoryImpl = (SAXParserFactory) clazz.newInstance();
} catch (ClassNotFoundException cnfe) {
throw new FactoryConfigurationError(cnfe);
} catch (IllegalAccessException iae) {
throw new FactoryConfigurationError(iae);
} catch (InstantiationException ie) {
throw new FactoryConfigurationError(ie);
}
return factoryImpl;
}
SAXParserFactoryImpl继承了SAXParserFactory,它实现了方法newSAXParser():
public SAXParser newSAXParser()
throws SAXException, ParserConfigurationException {
SAXParserImpl saxParserImpl = new SAXParserImpl(this);
return saxParserImpl;
}
注意:
SAXParserImpl的定义为:
public class SAXParserImpl extends javax.xml.parsers.SAXParser
SAXParserImpl的构造函数定义为:
public SAXParserImpl(SAXParserFactory spf)
throws SAXException, ParserConfigurationException {
super();
this.spf = spf;
if (spf.isValidating ()) {
parser = new ValidatingParser();
validating = true;
} else { parser = new Parser();}
if (spf.isNamespaceAware ()) {
namespaceAware = true;
throw new ParserConfigurationException(
“Namespace not supported by SAXParser”);
}
}
java.lang.Math
提供sin, cos, tan, exp, log, log10 等类方法,PI和E等类字段
java.lang.String(StringBuilder线程不安全,StringBuffer线程安全)
assert 条件;
assert 条件:表达式;
二、JAVA集合框架
Java集合类库将接口和实现分离。当程序使用集合时,一旦构建了集合就不需要知道究竟使用了哪种实现。因此,只有在构建集合对象时,使用具体的类才有意义。可以使用接口类型存放集合的引用。利用这种方法,一旦改变想法,可以轻松使用另外一种不同的实现,只需在对象创建处修改即可。
List接口扩展自Collection,它可以定义一个允许重复的有序集合,从List接口中的方法来看,List接口主要是增加了面向位置的操作,允许在指定位置上操作元素,同时增加了一个能够双向遍历线性表的新列表迭代器ListIterator。List接口有动态数组(ArrayList类)和双端链表(LinkedList类)两种实现方式。
Set接口扩展自Collection,它与List的不同之处在于,规定Set的实例不包含重复的元素。在一个规则集内,一定不存在两个相等的元素。AbstractSet是一个实现Set接口的抽象类,Set接口有三个具体实现类,分别是散列集HashSet、链式散列集LinkedHashSet和树形集TreeSet。
java.util.Deque
接口Deque,是一个扩展自Queue的双端队列,它支持在两端插入和删除元素,Deque接口由ArrayDeque和LinkedList这两个类实现,所以通常我们可以使用LinkedList来创建一个队列。PriorityQueue类实现了一个优先队列,优先队列中元素被赋予优先级,拥有高优先级的先被删除。
java.util.ProrityQueue
优先级队列中的元素可以按任意顺序插入,却总是按照排序的顺序进行检索。优先级队列由堆实现。堆是一个可以自我调整的二叉树,对树执行添加和删除操作,可以让最小元素移动到根(最小堆),而不必花费时间对元素进行排序
4、Map接口
Map,图,是一种存储键值对映射的容器类,在Map中键可以是任意类型的对象,但不能有重复的键,每个键都对应一个值,真正存储在图中的是键值构成的条目。
java.util.Map
java.util.HashMap
java.util.LinkedHashMap
LinkedHashMap继承自HashMap,它主要是用链表实现来扩展HashMap类,HashMap中条目是没有顺序的,但是在LinkedHashMap中元素既可以按照它们插入图的顺序排序,也可以按它们最后一次被访问的顺序排序。
java.util.TreeHashMap
TreeMap基于红黑树数据结构的实现,键值可以使用Comparable或Comparator接口来排序。TreeMap继承自AbstractMap,同时实现了接口NavigableMap,而接口NavigableMap则继承自SortedMap。SortedMap是Map的子接口,使用它可以确保图中的条目是排好序的。在实际使用中,如果更新图时不需要保持图中元素的顺序,就使用HashMap,如果需要保持图中元素的插入顺序或者访问顺序,就使用LinkedHashMap,如果需要使图按照键值排序,就使用TreeMap。
5、其他集合类
下面主要介绍一下其它几个特殊的集合类,Vector、Stack、HashTable、ConcurrentHashMap以及CopyOnWriteArrayList。
java.util.Vector
用法上,Vector与ArrayList基本一致,不同之处在于Vector使用了关键字synchronized将访问和修改向量的方法都变成同步的了,所以对于不需要同步的应用程序来说,类ArrayList比类Vector更高效。
java.util.Stack
Stack,栈类,是Java2之前引入的,继承自类Vector。
java.util.HashTable
HashTable和前面介绍的HashMap很类似,它也是一个散列表,存储的内容是键值对映射,不同之处在于,HashTable是继承自Dictionary的,HashTable中的函数都是同步的,这意味着它也是线程安全的,另外,HashTable中key和value都不可以为null。
java.util.ConcurrentHashMap
ConcurrentHashMap是HashMap的线程安全版。同HashMap相比,ConcurrentHashMap不仅保证了访问的线程安全性,而且在效率上与HashTable相比,也有较大的提高。
java.util.CopyOnWriteArrayList
CopyOnWriteArrayList,是一个线程安全的List接口的实现,它使用了ReentrantLock锁来保证在并发情况下提供高性能的并发读取。
java.util.CopyOnWriteArraySet
CopyOnWriteArraySet,是一个线程安全的set接口的实现,它使用了ReentrantLock锁来保证在并发情况下提供高性能的并发读取。
ConcurrentLinkedQuerue是一个先进先出的队列。它是非阻塞队列。
ConcurrentSkipListMap可以在高效并发中替代SoredMap(例如用Collections.synchronzedMap包装的TreeMap)。
ConcurrentSkipListSet可以在高效并发中替代SoredSet(例如用Collections.synchronzedSet包装的TreeMap)。
Executor类执行器
1)调用Executors类(工厂类)中静态工厂方法newCacherThreadPool
2)调用submit提交Runnable或Callable对象
3)当不再提交任何任务时,调用shutdown