目录
- Java面向对象基本概念
- System
- String, StringBuffer
- 数值,字符,布尔对象与简单类型的操作
- Class, ClassLoader
- Java IO系统
- Java集合类
- ResourceBu( b i bndle, Properties
- Exceptions
- JDBC类库
- 常用设计模式
- 读者福利
一、Java面向对象基本概念
Java基本上是面向对象的程序设计语言, 除了一些简单类型(primitive)的变量以外,一切都是对象, 程序是对象的组合, 每个对象都有自己的空间, 并且每个对象都有一种类型, 同一类所有对象都能接受相同的消息。 下面只对Java中对象的结构作简单的说明:
- 类(class): class是定义类的关键字, 类中包含类变量, 方法, 内部类, 内部接口等。由class可以生成类的实例, 即一个个对象。 如果一个类的成员被定义成static的,则这个成员不专属于任何对象, 而是属于这个类, 所有的对象共享这个成员。
- 抽象类(abstract class): 抽象类不能直接生成一个实例, 抽象类中必需有* z { k S 4 0方法是abstract的,抽象类的意思就是它实现了一l K x a W N - X !部分的方法, 而定义为abstract的方法则需要在它的字类中去实现。
- 接| ; M % Z口(interface): 接口可以理解为纯抽象的类, 它的每个方法都是未实现的, 它可以有成员变量, 但必须是sta$ H $ @tic的。 一个类如果从这个接口继承(implements)则它必须实现这个接口的所有方法。
继承类用关键字:extends,继承接口用关键字:implements。 一个类只能从w G } & V一个类继承下来, 但可以从多个接口继承(j ^ A类似于C++的多重继承)S . E S。 字类可以覆盖父类的方法(method), 但不能覆盖父类的成员变J w s ` T n H s量(field)。 如果父类的方法为final或static的则不能被覆盖。类的初始化顺序是, 如果有父类, 则先初始化父类的field,然后执行父类的构造函数, 如果子类没有显式的去调父类的构造函数则缺省的会去调父类的无参数构造函数。 然后是子类的field与构造函数的初始化。
public inw $ D & [terface SuperInterface {
public staitc St$ a p ) Ering SOME_FLAG = “1”;
public void someMethod();
}
public Class SuperClass {
{
System.out.println(“init SuperClass field”);
}
public SuperClass() {# y ^ 3 X #
System.out.p; a f S v ! o 7rintln(“init SuperClass Constructor”);
}
public void runMethod() {
System.out.pri4 T C d entln(“run SuperCR k 9 _ X r w `lass runMethod()”);
}
}
public Class S/ x U n 3 : # 4 dubClass extends SuperClass implements SuperInterface {
{
Sy~ 0 , ` ( - Fstem.out.println(“init SubClass field”);
}
public SubClass()? : # n S 8 x X @ {
System.out.println(“init SubClass Constructor”);
}
publi0 p 5 ` g - Uc void som; Q Y 4 HeM# 4 0 B M y %ethod() {
System.out.println(“run SubCl( F Dass someMethod()”);
}
pub# [ ^ ; Xlic void runMethod() {
System.out.println(“run SubClass runMethod()”);
}
}
有以下test代码:
public class Test {
public void main(String[] args) {
SubClass sub = new SubClass();
sub. runMethod();
}
}
则9 f q会输出:
init SuperClass field
init SuperClass Constructor
init SubClass field
init SubClass Cona x !structor
run SubClass runMethod(T p 0 i v S ] $)
以下章节所 b - $ @ { k Q .讲述到的常用的Java API就是一些Java自带的一些Class或Interface的用法。
二、System
System类位于package java.lang下面, 凡是此paN } D & W T Uckage下面的类我们可以直接引用无需先import进来, 因s j ; v 5 5 m U 1为JVM缺省就load了这下面的所有class。
System包含了一些我们常用的方法与成员变量。 System不能被实例化, 所有的方法都可以直接引用。 主要作用大致有:
2.1 输入输出流:
- (PrintStream) Syt J [ p A K S @ Dstem.out (标准终端输出流),
- (Prx 3 | C K B v O -intStream) System.err(标准错误输出流)% X V f T e r H Y,
- (InputStream) System.in(标准输入流)。
我们还可以重定向这些流, 比如将所有的System.out的输出全部重定向至一文件中去。
- System.setOut(PrintStream) 标准输出重_ D Q u J a定向
- System.setErr(PrintStream) 标准错误输出= ` 3 +重定向
- System.setIn(InputStream) 标准输入重定向
2.2 取当前时间:
System.currentTimeMillis() 所取O 3 = ; * X R到的时间是从1970/01/01以来1/1000秒计算的l6 I ] u q F @ong型值。这个值可以转换至Date或Timestamp值。 它一般还可以用来计算程序执行的时间。例:
long beginTime = Syst c ` 7 X w U $ 2em. currentTimeMillis();
…
…
System.out.pri5 x = u d =ntln(“run time = ” + (System. currec O c e F * = BntTimeMillis() – b3 = 5eginTime));
2^ E T.3 数组拷贝:
System.arraycopy(Object src, int src_position, Object dst, int dsE o : B N ` +t_pos^ _ hition, int l8 + t 8 F } Qength)
- srcb T ^ v 7 ( N n G: 源数组。
- src_position: 源数组拷贝的起始位置。
- dst: 目标数组
- dst_position: 拷贝至目标数组的起始位置
- lengtl w : z *h: 拷贝元素的长度
利用System.arraycopy进行数组的拷贝效率是最高的, 一般情况下我们自己很少直接用到这个方法,Q B +但在集合类的内部中都大量使用了这个方法。
例:
int[] array1 = {1, 2, 3, 4, 5};
int[( b t y K [ X r 6] array2 = {4, 5, 6, 7, 8};
int array3 = new int[8];
System.arraycopy(aC j t Mrray1, 0, array3, 0, 5);
System.arraycopy(array2, 2, array3, 5, 3);
此时array3Y ) _ P = {1, 2, 3, 4, 5, 6, 7, 8}
这比用for循环来进行赋值效率要高。
2.4v a M 8 ; w S 存取系统的Properties:
System.getPropertiesE * ` a ] 2 w j():取得当前所有的Pro8 z / Kperties, Properties将在后面的集合一节进行详细的论述。
- System.setPro[ * z k s u _ Uperties(Properties pro@ M @ps):设置系统_ : ^ z的PropeW J , W @ Z ^ 7rties。
- System.getPropeW m d x ! & h u Lrty(String key): 根据一个键值来取得一个Property。
- System.setProperty(Strm e Y y w ^ Ting key, String value): 设置系统的一个Property。
JVM起动的时候将会有一些缺省的Properties值, 例如:
- java.version Java运行环境版本
- java9 C ! u | (.home Java主目录 inste % Xallation directory
- java.class.path Java 的class path
- ja% a | B _ 7 ) e Yva.ext.dirs Java的扩展目录路径
- file.separ~ V C r u 0 f P eator6 - W W 文件分隔符(\"/\" of R U L in UNIXH $ $ O H)
- path.separator 路径分隔符(Z L ] U 9 R\":\" oV 1 c 6 ) } en UNIX)
- line.separator 行e w w V ` ( X | 2分隔符 (\"\\n\" on UNIX)
- user.name 用户名
- user.home 用户主目录
- user.dir 用户当前工作目录
更详细的信息请a i % 3参照Java API。 另外在起动} ? z 3 F `一个java程序的时候可以通过-D来设置系统的Prope m T # | f Q yrty, 比如 java –Dejb.file=ejb_Test Pri6 8 R [ 9 S ntTest 在PrintTesP L it里面就可以通过System.getProperty(“ejb.fil. Q O n d Z Fe”)来取得值ejb_Test。
2.5 其它
- System. loadLibrary(String libname): 加载native的动态库。 可以用C写JNI的库, 然后在java中通过nativ; 5 $ V J { ~ X je方法来调用。
- Sy5 6 ~ 2stem.setSecurityManager(SecurityManager s)
- System.ge+ ^ 5 4 $tSecurityManager(): 设置与t ; f Q ! o | o取得系统的sef X - U 0 3 zcurity class。
三、String, StringBuffer
3.1 基本用法
String可以说是我们最常用的一个类, 熟练掌握它的一些基本用法是很有用的。
String是由一组字符组成的字符串, 下标由0开始。 一旦有必要改变原来的内容, 每个String方法都有返回了一个新的String对象。
- char charAt(int index) 返回指定位置的字符。
- int compare& & L ATo(Object o)
- int compareTo(String anotherString)
与另外一个对象进行比较。
- int compareToIgnoreCase(String str) 与另一个String进行比较, 不区分大小写
- Stringw _ z ( concat(String str) 连接两字符串, 可以直接用+, 因为Ja_ Q l { : p Dva给String覆盖了+
- static String copyVaj K r * o e ] CluL u 5 GeOf(char[] data)
- static String copyValueOf^ | K , R 9 # ) v(char[] data, in/ N ,t offs^ } z L e et, int count)
将data数组转换至String
- boolean endsWith(String suffix) 测试此String是否以suffix结尾。
- boolean startsWith(String prefix) 测试此Str ` W $ 1 qing是否以prefV L . W & H { i @ix开头。
- boolean equals(Object anObject)
- boolean equaO D 2 k i }lsIgnoreCase(String anotherStrin5 u } z t Bg)
比较两字符串的值。 不相等则返回falsea H ? ( H ;
- byte[] getBytes() 根据缺省的字符编码将Str4 Z eing转换成字节数组。
- byte[] getBytes(String enc) 根据指定的编码将SR k ` x 2 + L E jtringf I k Y 8 ^转换万字节数组。
- void getCha7 5 K l l wrs(int srcBegin, int srcEI E 7 rnd, char[] dst, int dstBegin) 拷贝字符至一数组中
- int indexOf(int cs z Qh) 从字串的起始位置查找字符ch第一次出现的位置
- int indexOf(int ch, int fromIndex) 从指定的fromIndex位置向后查找第一次出现ch的位置,
- int indexOf(StQ $ = ! 5 Wring str)
- int indexOf(String str, int fromIndex)R 1 g F d { u
如果不存在ch或str都返回-1
- int lastIndexOf(int ch) 从字串的最终位置往前查找第一次出现ch的位置
- int lastI? ^ S l $ A M 6 Nndeh m .xOf(int ch, int fro5 v E # HmIndex) 从指定的位置往前查找第一次出现ch的位置,
- int lastIndexOf(String str)
- int lastIndexOf(String str, int fromIndex)
如果H 0 M . e a R 4 J不存在则返回-1
- int length() 该字t I 7 2符串的字符长度(一个全角的汉字长度为1)
- String replace(char oldChar, cB ) ! ehar newChar)o + 4 = j ( 将字符oldChar全部替换为newChar, 返回一个新的字符串。
- String substring(int beginIndex) 返回从beginIndex开始的字符串子集
- String subsP 8 X X O u 0 Y ftring(int beginIndex, int endIndex) 返回从beginIndex至endIndex结束的字符串的子集。 其中endIndex – beginIndex等于子f B $ Y Z |集的字符串长度
- char[] toCharArraE o ? t d py() 返回该字符串的内部字符数组
- String toLu . h a W HowerCase() 转换至小写字母的字符串
- Strin3 U R & u W I Zg toLowerCase(Locale locale)
- String toUpperCase() 转换至大写字母的字符串
- String toUpperCase(Locale locale)
- String toString() 覆盖了Object的toString方法, 返回本身。
- StriA L K b U qng trim() 将字符串两边的半角空白字符去掉, 如果需要去掉全角的空白字符得要a 7 D自己写。
- static String v{ d C h # = 7 ualV } y iueOf(primitive p) 将其它的简单类型的值转换为一个String
StingBuffer是一个可变的字符串,它可以被更改。同时S^ * c w , U %tringBuR o @ j 7 c r Lffer是Thread safe的, 你可以放心* F T f的使用, 常用的方法如下:
- StringBuffer append(parE Z = ( iam) a 8 C H e 在StringBuffer对象之后追加param(可以为所有的简单类型和Object) 返回追加后的StringBuffer, 与原来的对象是同一份。
- char charAt(int index) 返回指定位置index的字符。
- StringBuffer delete(int start, int end) 删除: { g 指定区域start~enf 0 } L pd的字符。
- StringBuffer deleteCharAt(inV V G e r d %t index) 删除指定位置index的: [ ^ h A ^ 3字符。
- void getChars(int srcBegin, int s ] : t W P b :rcEnd, char[] dst, int dstBegin)F v G u q y ! A 同StI _ , f { Lring的getChars方法
- StringBufferv _ P b ine D -ser2 d t ; K nt(int offset, boolean b) 在指定位置offset插入param(为所有的简单类型与Object)
- int length() 同String的length()
- StringBuffer replace(int start, int end, String str) 将指定区域start~end的字符串替换为str
- StringBuffer reve{ D . ) & t Srse() 反转字符的顺序
- void setCharAt(int in0 - w W Y Pdex, char ch) 设置字_ D # f 4 ^ %符ch至index位置。
- String substring(int start)B t b 0
- String substring(int start, int end) 同String的subString
- String toString() 返回一个Strin4 j K Xg
大家可能已经注意到很多方法都返回了一个StringBuffer对象, 但返回的这个对象与String的方法返回的String不一样, 返回的StringBuffer对象与被操作的StringBuffer对象是同一份, 而String的方法返回的String则肯定是重新生成的一个String。
3.2 性能对比
因为String被设计成一种安全的字符串, 避免了C| w h R + H ; w/C++中的尴尬。因Y V } c ~此在内部操作的时候会频繁的进行对象的交换3 ) m | j R R 8, 因此它的效率不如StringBuffer。 如果需要频繁的进行字符串的增删操作的话最好用StringBuffer。 比如拼SQL文, 写共函。 另: 编9 & E q % Z绎器对String的+操作进行了一定的优化。
x = D : i\"a\" + 4 + C . 3 _ u\"c\"
会被编绎成
x = new StringBuffer().append(\"a\").append(4).append(\"c\").toStrin. G ` u u [ s U ~g()
但:
x = “a”;
x = x + 4;
x = x + “c”;
则不会被优化。 可以看出如果在一个表达式里面进行String的多次+操作会被优化, 而多q 6 A p r F G个表达式的+操作不会被优化。
3.3 技巧
①. 在Servlet2.3与JSP1.1以前画面post到后台的数据是通过IS; v } A 9 ]O88591格式进行编码的, 则当遇到全角日文字的时候, 在后台request得到的数据可能就是乱码, 这个时候就得自己进行编码转换, 通过String.getBytes(String enc)方法得到一个字节流, 然后通过String(byte[] bytes, String enc)这个构造函数得到一个用新的编码生成的字符串. 例如将ISO88591的字符串转换成Shift_JIS的字符串, 方法如下:
public static String cC 7 F F c : ; V wonvertString(String str) {
if (str == null) {
retu@ 2 J n = h ! @rn null;
}
try {
byte[] buf = str.getBytes(\"ISO8859_1\");
retur! O ? Q / Bn new/ B i e String(buf, \"Shift_JIS\");
}
catch (Excepti& k ~ { v ! 9on ex) {
ex.printStackG M S iTrace();
return nullj E . J j w % ^ X;
}
}
不过在最新的Servlet2.3与Jsp1.2中可以通过H s l ` $request.setCharacterEncoding来进行设置取值的V S v j ) M ~ ^ }码制, 不需要自己再做转换。
②. 因为Java在计算String的E q W M w 6长度是以字符为单位的, 因此一个全角与半角的字符长度是一样的, 但是DB中往往是H ) U O根据字节来计算长度- E } W O ;的, 因此我们在做Check的时候得要判断String的字节长, 可以用以下的方法:
public statiz T I F sc String length(String str) {
if ([ v { xstr == null) {
return 0;
}
return str.getBytes().length;
}
四= 5 % n N ( ) x、数值,字符,布尔对象与简单类型的操作
简单的对照V q ^ l ] n `表如下:
与C等其它语言不同的是数值o r b的范围不随平台的改变而改变, 这就保证了平台之间的统一性,提高了可移植性。
Number: Number本身是个抽象类, 不能直接使用, 所有直接从Number继承下来的子类都有以下几种方法:
- byte byteValue() 返回字节值
- double doubleValuF J !e() 返回double值
- float floatValue() 返回float值
- int intValue; S _(I z , T u) 返回float值
- long longValue() 返回long值
- s_ K [ F Ahort shortValue() 返回short值
在需要通过Object来取得简单数据类型的值的时候就得用到5 y ] o以上的方法, 不过我不推荐不同类型之间的取值, 比如Long型的Object不要直接去调用intVal6 l = C Nue(),精度可能会丢失。
如果想通过Strh R k & 4ing来得到一个数值类型的简单类型值, 一般在每个Numc A - ber的类里面都有一个parseXXX(StI i * ( 9 % {ring)的静态方法, 如下:
- byte ByK H u 7 b Yte.parseByte(String s)
- double Double.parseDouble(String s)
- float Float.parseFloat(String s)
- int Integ% F Y [er.parseInt(StriP z y _ M 2 | l +ng s)
- long Long.* q e d x kparseLong(String s)
- short Short.parseShort(String s)
如果想直接从String得到一个N* $ x K ? a 7 ] Zumber型的Ob^ M 2 Kject,则每个Number类里面都有valueOf(String s) 这个静态方法。如:
- Byte Byte.valueOf(String s)
- Double Double.valueOf(Stri# X 8 7ng s)
- Float Float.va& + 1 p $ 5 Q m +lueOf(String s)
- Integer Integer.valueOf(String s)
- Long Long.valueOf(String s)
- Short Short.valueOf(String s)
一般的在构造一个Number的时候都可以通过一个String来完成, 比如:
Long longObject = new LonK 1 @ r b u ~g(“123456, n x = ( X7890”);
等价于
Long lo5 O h w ^ p #ngObject = Long.valueOf(“1234567890”)E % h;
因为每个Number的子类都实现了Object的toString()方法, 所以v O L, 如果想得到一个Str} . Fing型的数值, 直接调用XXX.toString()就可以了。 如果想得到一个简单类型的String, 方法很多总结如下:
- 首先生成对应的Number Object类型, 然后调用toString()
- 调用Number子类 .toString(type t) 其N L s ] N 1 X中t就是简单类型的数据。
- 调用Strit @ t m V ~ c l $ng.valueOf(type t) (推荐使用这种方法)
大家可以看出, 往往一种结果可以用多种方法实现, 总的原则就是深度最少优先。比如由一个String得到一个简单类型的值可以有以下两种方法:
Inz v I XtegeN : k ^ . ]r.parseInt(“7 a b ] L Y12345”);V T l
或
(new Integer(s)).intValue(“12345K 4 w m”);
当然应该使用第一种方法。
Chs ~ 0 } u T waracter: Character对应着char类型, Character类里面有很多静态的方法来对char进行判断操作, 详细的操作请参照JDK API。 Java对字符的判断操作基本都o 4 o {是以Unicode进行的, 比如Character.isDigit(char c$ u v l !h)这个J c / N L l z P方法, 不光半角的0-9符合要求, 全角的日文0-9也是符合要求的。
BT $ = 9 V _ b voov Z n J 9 _ : `lean: Boolean对应着boolean类型, boolean只有true和false两个值, 不能与其它数值类型互换, 可以通过字符串”true”以及”fal- 2 n j x M s kse”来得到Object的Boolean, 也可以通过简单类! * f (型的boolean得到Boolean, 常用方法如下:
- Boolean(boolean value) 通过简单类型的bool{ $ w D a M T {eanw 4 B y A构造Boolean
- Boolean(String s) 通过String(“te 1 arue”, “* } bfalse”)构造Boolean
- boolean booleanValue() 由Object的B6 d B x } ( k !oolean得到简单类型的boolean值
- boolean eK x { | & , gquals(Object obj) 覆盖了Object的.g m X X , xequals方法, Object值比较
- static Boolean valueOf(String s) 功能与构造函数Bo^ ` B j n z $ [ aolean(String s)一样
五、Class, ClassLoader
Java是一种介于解释与编绎之间的语言, Java代码首先编绎成字节码, 在运行的时候再翻译成机器码。 这样在运行的时候我们就= ~ s [ # j可以通过Java提供的反射方法(reflect)来得到一个Object的Class的额外信息, 灵活性很大,可以简化很多操作。
Class: 任何一个Object都能通过getClass()这个方法U o ; M a e - J y得到它在运行期间的Class。 得I @ 6 K b y `到这个Class之后可做的事情就x z 2 : / I多了, 比如动态得到它的- m ;构造函数, 成员变量Y @ l n, 方法等等。 还可以再生成一份新的实例, 下面只给出几个我们常W b ) u T !用的方法g e A u p ? S, 更详细的用法参照Java API
- Class Class.forName(String className) throws ClassNotFoundException: 这是个静态方法, 通过一个Class的全称来得到这个Class。
- Stri; # L 5ng getName() 取得这个Class的全称, 包括package名。
- Object newInstance() 得到一个实例, 调用缺省的R D {构造函数。
例如我们有一个类: com.some.util.MyClas$ W c H D ]s 如果得到它的一个实例呢? 可能有以下两种方法:
MyClass myClass = new MyClass(), 直接通过操作符new生成;
或者:
MyClass myClass = (MyClass) Class.forName(“com.some.util.MyClass”).newInstance();
也许有人就会怀疑第二种方法实际意义, 能够直接new出来干嘛绕弯。 但实际上它的用处却很大, 举个例子: 用过struts的人都知道, 在action-config.x2 C $ @ Y N xml当中定义了一系列的formBean与actionBean, 当然每个form与action都具有同类型, 这样在一个request过来的时候我可以动态的生成一个form与action的实例进行具体的操作, 但在编码的时候我并不知道具体是何种的form与action, 我只2 T X调用它们父类的方法。 你如果要用第一种方法的话, 你得在编码的时候通过一个标志来判断每一次requeg L - 1 b qst需要具体生成的form与action, 代码的灵活性大大降低。 总的来说在面向接口的编程当中经常使用这种方法, 比如不同数据库厂家的JDBC Driver都是从标准的JDBC接口继承下去的, 我们在写程序的时候用不着管最终是何种的Driver, 只有在运行的时候确定。 还有XMLi ! n g ^ = ` e 9的Parser也是, 我们使用的只是标准f ? x 4 V的接口, 最后到底是谁来 k E实现它的, 我们用不着去管。
ClassLoader: ClassLoader是一个抽象类,一般的系统有一个缺省的ClassLoader用来装载Class, 用ClassLoader.7 ` s # Y ) R lgetSystemClassLoader()可以得到。不过有时候为了安全或有其它的特殊需要我们可以自定义自己的ClassLoader来进行loader一些我们需要的Class, 比如有的产品它用了自己的ClassLoaderY X F H u A可以指定Class只从它指定的特定的JAR文件里面来loader,如果你想通过覆盖CK = k s plassPath方法来想让它用你的Clas2 K ~ q b B S Fs是行x L a + h T | h不通的。 有兴趣的可以参照Java API 的更详细的用法说
六、Java IO系统
6.1 JDK1.0输入流
InputStream
ByteArrayInputA @ i / . p 1Stream
FileInputStream
FilterInputStream
ObjectInputStream
PiY c s ? g q L |pedIn& k l . putStream
SequenceInputStream
BufferedInputStream
DataInputStrez P 0 * sam
PushbackInputStream
LineNumbery ~ % S , G ` Z 4InputStream
6.2 JDK1.1输入流
Reader
Be l U kufferedReader
FilterReadc # jer
PipedReader
StringReader
InputStreamReader8 Z )
CharArra| ! ( s syReader
FileReader
PusC c : } e / 2 5hb3 ; . 6 ~ G V ( oackReader
LineNumberReader
6.3 JDK1.0输出流
OutputStream
BufferedOutputStream
DataOutputStream
PrintStream
ByteArrayOutputSt} _ Q | 5ream
FileOutputStream
FilterOutputStream
Ox I e q wbjectOu4 x & u h o ; ytputStream
PipedOutputStream
6.4 JDK1.1输出流
Writer
BufferedWriter
Fi~ Y * zlterWriter
PipedW& 8 C 9 G Griter
StringWriter
FileWriter
PrintWriter
OutputStreamWriter5 } )
CharArra1 ? :yWriter
如果你刚刚接触Je 8 na9 ~ t W - A sva的IO部分, 你可能会感觉无从入手, 确实Java提供了过多的类,反而让人感到很乱。
可将Java库的IO类分为输入与u w W输出两个部分, 在1.0版本中提供了两个抽象基类, 所有输入的类都从InputStream继承, 所有输出的类都从OutputStN e C Wream继承, 1.1提供了两个新的基类, 负责输入的, | Q 0 3Reader与输出X P l ; %的Writer, 但它们并不是用来替换原来老的InputStream与OutputStream, 它们主要是让Java能更好的支持国际化的需求。 原来老的IO流层只支持8位字节流, 不能很好地控制16位Unicode字符。 Java内含的char是16位的Unicode, 所以添4 ] o D加了Reader和Writer层次以提供对所有IO操作中的Unicode的支持。 除此之外新库也对速度进行了优化, 可比旧库更快地运行。
6.5^ g } ( I } 8 InputStream的类型:
- 字节数组
- String对象
- 文件
- 管道, 可以从另外一个输出流得到一个输入流
- 一系列的其他流, 可以将这些流统一收集到单独的一个流内。
- 其他起源(如socket流F s !等)
还有一个是File类, Java中一个目录也是一个文件,可以用file.it ] g B [ ^ ZsFile()和file.isDirectory()来进行判断是文件还是目录。 File 对象可o ! |能作为参数转换为文件流进行操作。 具体操作参照Java IO API。
6.6 常用方法:
/**
* 拼文件名, 在windows()跟unix(/)上的文件分割符u / F / 7是不一样的
* 可以通过File类的静态成员变量separator取得
*/
public sg Z }tatic String concatFileName(Strin^ r i J V @ W : $g dir, Ss E { Atring fileName) {
String fullFileName = \"\";
if (dir.endsWith(File.separator)) {
fullFileNameD r 2 V 5 / D = dir + fO % @ q k - bileName;
} else {
fullFileN k T $ ) ^ ] Qame = dir + File.separator + fileName;
}
return fullFileName;
}
/**
* 从控制台读取输入的数据, System.in (InputStream) 先转换至InpL m | e B % W butStreamReader再用
* BufferedReader进行读取.
*
*/
BufferedR/ 3 #eader in = new B- O i F X )uffe9 j EredReaded : N Sr(new InputStreamReader(System.in));
St2 o z tring str = \"\";
whi7 T 7 { G m i ele (str != null) {
str = in.readLine();
// process(str); }
/**
* 从文件中按行进行读取数据处理
*/
BufferedReader in = new BufferedReader(new FileReader(\"infilename\"));
String str;
while ((str = in.readLine()) != null) {
// process(str); }
inE ~ o 5 l $ . B.close();
/**
* 写数据至一个新的文件中去.
*/
BufferedWriterO y Q 6 l n N out = new BufferedWritez { } $ 2 l S lr(new FileWriter(\"outfilenaI v i sme\"));
out.write(\"a String\");
o3 y A 0 dut.close();
/**
* 追加新的数据到一个文件中去, 如果原文件不存在
* 则新建这个文件.
*/
BufferedWriter out = newk U = ^ b L l 9 BufferedWru ; * 5 Siter(new FileWriter(\"filename@ k x d c\", true));
oD O ) Aut.write(\"aSB B 1 Z 3 {tring\");
out.close();
/**
* 将一个可序列化的Java的object以流方式写到一个文件中去中.
*/
ObjectOut8 , qput out = new O Z D L d m m QbQ { d M x jectOutputStream(new FileOutputSz # i z Itream(\"filename.ser\"));
o: 5 [ q D o L Rut.w# W W . = 4 W DriteObject(object);
out.cloB F y f ! P 0 % ^se();
/**
* 从文件中恢复序列化过的Java Obj+ n y J Dect
*/} R u s ^ /
ObjectInputStream in = new Obje? # qctInputStream(new FilG l 9 : J } R oeInputStream(\"fF g 0 ! ,ilename.ser\")); * s 0
Object objec5 s T 0 H ) f 3 St = (Object) ix ) % % Fn.readObject();
in.close();
/**
* 以指定的编码方式从文件中读取数据
*/
BufferedReader in = new BufferedReader(new InputStreamReader(
new FileInputS{ Y s h Strt ; R 7 ! ,eam(U s - G o | r\"infilenamel B 5 [\"), \"UTF8\2 ^ - . 2 U k 3"));
String8 + V R U { | str =. A G U o ; J ( in.readLine();
/**
* 以指定的编码方式写数v ) p ` Z ! F 5 r据到文件中
*/
Writer out = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(\"outfilename\"), \"UTF8\"));
out.write(\"a String\");
out.cJ _ ~ I B Hlose();
由上面的例子可以看出, 流之间可以互相转换, 如果想对流进行字符操作最好将之转换成BufferedReader与BufferedWriter这样可以提高读写的效率。
需要注意的是一般来说流的大小是得不到的, 虽然一般的InputStream都有一个available()的方法可以返回这个流可以读到的字节数, 不过这个方法有时候不会很准确, 比如在读取网络传输的流的时候, 取到的长度并不一定是真实有效Z N 1的长度。
大家在写程D l M 3 w P #序的时候可能已经注意到一些类都继承了java.io.Serializable这个接口, 其实继承了这个接口之后这个类的本身并不做任何事情, 只是这个类可以被序列化并通过流来进行传输, 并可被还原成原Objec3 9 c A j Ot。 这个流可以通过网络传输(EJB 中进行传递参数和返回值的Data ClF # M h F K d 6 }ass)* u E i H j [ k, 也可以保存到文件中去(ObjectOutputStream)。
七、Java集合类R h : c 0
在写程序的时候并不是每次A U i g m S只使用一个对象, 更多的是对一组对象进行操作, 就需要知道如何组合这些对象, 还有在编码的时候我c G U v + ? u &们有时并不知道到底有多少对象,它们需要进行动态的分配存放。
Java的集合类只能容纳对象句柄, 对于简单类型的数据存放, 只能通过数据来存放, 数组可以存放简单类型的数据也能存放对象。
Java提供了四种类型1 _ M . ) x = b J的集合类: Vector(矢量), BitSet(位集), Stack(堆栈), Hashtable(散列表)。
- 矢量: 一组有% * )序F ? ` 8 e u F p的元素, 可以通过index进行访问。
- 位集: 其实就是由二进制位构成的Vector, 用来保存大量”开-关”信息, 它所占的空间比较小, 但是效率不是很高, 如果想高效率访问, 还不如用固定长度的数组。
- 堆栈: 先入后出(LIFO)集合, java.util.Stack类其实就是从Vector继承下来的, 实现了pop, push方法。
- 散列表: 由一组组“键--值”组成, 这里的键必须x W | 6是Object类型。 通过Object的hashCode进行高效率的访问。
对于S a j . u m ]这些集合I y / g 3之间的关联关系见下图, 其中标色的部分为我们常用的类。
由上图可以看出, 基本接口有两个:
Collection: 所有的矢量集合类都从它继承下去的, 但并不直接从它继承下去的。 List与Set这两个接@ | i B M Z W 4口直接继承了Collectiob ; Y k p un, 他们的区别是Lis( 5 * `t里面可以保存相同的对象句柄, 而Set里面的值是不重复的。 我们经常用~ Z y * M的Vector与ArrayList就是从Listc x J h h e继承下去的, 而HashSet是从Set继承的。
Map:散列表的M x 5 1 K X = W `接口, Hashtable与HashMap继= % | a w承了这个接口。 下面给出常用集合类的常用方法。
/**
* Vector 与 ArrayList的操作几乎是z K /一样的
* 常用的追加元素用add(), 删除元素用remove()
* 取元素用get(), 遍历它可以循环用get()取. 或者
* 先得到一个Iterator,7 [ Q 4 8 l | 然后通过遍历Iterator的方法
* 遍历VectG } Q d $ b 8 6 -or或ArrayList
*/
// 生成一个空的Vector
Vd m d o V rector vector = new Vector();r G = @ S j
// 在最后追加一个元素。
vector.add(\"one\"3 % U 8 x % : ^ D);
vector.add(\"two\");
// 在0 6 D ]指定的地方设置一个值
vector.set(0, \"new one\");
// 移走一个元素或移走指定位置的元素
vector.remove(0);
// 用for循环遍历这个Vector
for (int it v 8 k % r = 0; i < vector.size(); i++) {
String elemen/ / Ft = (String) vector.get(i);
}
// 用枚举器(Enumeration)遍历它(只有Vect7 ] T 6or有,ArrayList没有)
Enumeratio2 K C c } E ? ; ln enu = vector.elements();
while (enu.hasMoreElements()) {
enu.nextElemev ~ I n S jnt();
}
// 用反复器(Iterator)遍历它
Iterator it = vector.iterator();
while (it.hasNext()) {
it.nb 4ext();
}
/**
* Hashtable与HashMap的操作, 追加元素用put(不是add)
* 删除元素用rs t ;emove, 遍历可以用I u O 8 A 9 )teraq 2 i # ntor 既可以遍历
* 它的key, 也可以是value
*/
// 生成一个空的Hashtable或HashMap
Hashtable hashtable = new Hashtable();
// 追加一个D X o元素
hashtable.put(\"one\", \"one object value\");
// 删除一个元素
hashtable.remove(\"one\9 _ E G q M Z");
// 用It* { e , Terator遍e a N . i U ~ y H历
Iterator ke} y ^ U .ya J L c ( l N W )It = hashtable.keySet().iterator();
while (keyIt.ha, Q K r 6 9 N VsNext()) {
Object keyName = keyIt.next();
String value = (String) hashtable.get(keyNameI ` B);
}
Iterator vI , v I . M Z LalueIt = hashtable.values().iterator();
while (valueIt.hasNexta F =()) {
valueIt.next();
}
// 用Enumeration遍历, 只有Hashtable有, HashMap没有.
Ens V wumeration enu = hashtable.elements();
while (enu.h4 R H BasMoreElements()) {
enu.nextElement();
}
说明: Enumeration是老集合库中的接口, 而Iterator是新集合(1.2)中出现的, 而Vector与Hashtable也都是老集合中的类, 所以只有Vector与Hashtabl( a j ae可以用Enumerati| ] r X +on。
Vector与ArrayList对比:
虽然在使用的时候好象这两个类没什么区别, 它们都是从List继承下来的, 拥有相同的方法, 但它们的内部还是有些不同的,
- 首先Vector在内部的一些方法作了线a B V L程同步(synchronized)。 同步的代价就是降低了执行效率, 但提高了安全性。而ArrayList则是线程不同步的, 可以多线程并发! I &读写它。
- 内部数据增长率。 所有的这些矢量集合在内部都是用Object的数组进行存储和操作的。 所以R , o x k也就明白了为什么它可以接受任何类型的OW 9 G [bjecw [ m ] c ) t ot, 但取出来的时候需要进行类型再造。 Vector与ArrayList具有自动伸缩的功能, 我们不用管它size多大, 我们都可以在它的后面追! x ; + n !加元素。 Vector与ArrayList内部的数组增长率是不一样的, 当内部的数组不能容纳更多元素的时候,R v H 4 Vector会自动增长到原两倍大小, ArrayList会变为原一倍半大小, 而不/ + z n !是我们所想象的一个元素一个元素的o _ d W A z n /增长。
Hashtable与HashMap对比:
Hashtaa f 2 i c mble与HashMap都是从6 } _ | d u Y kMap继承下来的, 方法几乎都一样, 它们内部有两个不同点:
- 与Vector和ArrayList一样, 它们在线程同步是不同的, Hashtable在内部做了线程同步, 而HashMap是线程不同步的。
- HashMap的键与值都可以为null, 而Hashtable不可以, 如果你试图将一个null值放到Hashtable里面去, 会抛一个NullPointException的。
性能对比:
抛开不常用的集合不讲, 每种集合都应该有一个V 4 m v ] S我们常用的集合类, 而在不同的场合下应该使用+ $ l [效率最高的一个。 一般来说我推荐尽量使用新的集合类, 除非不得已, 比如说需要用用了老集合类写的产品的程序。 也就是说尽量使用ArrayList与HashMap, 而少使用Vector与Hashtable。
- 在单线程中使用ArrayList与HashMap, 而在多线程中如果需要进行线程同步可以使用Vector与Hashtable, 但也可以用synchronized对ArrayList与HashMap进行同步, 不过同步后的ArrayList与HashMap是比Vector与Hashtable慢的。 不过我认为需要进行线程同步的地方并不多。 如果一个变量定义在方法内部同时只可能有一个线程对之进行操作, 就不必要进行同步, 如果定义在类的内部并且不是静态的, 属于实例变量, 而这个类并没有被多线程使用也就不必要同步。
- 一般自己写的程序很少会自己去另K u ] 3 ^ 6开线程的, 但在Web开发的时候, 如果用了Se/ o 8 x b X ] qrvlet, 则每个requ= F * 8 Lest都是一个线程, 也就是说每个Servlet都是在多线程环境下运行的, 如果Servlet中使用了全局静态的成员变量就得小心点儿, 如果需要同步就得在方法上加上synchronized修饰符,4 W T m I 如果允许多个线程操作它, 并且你知道不会有什么冲突问题就可以大胆的使用ArrayList$ E g # o p 3 n !与HashMap。 另外如果在多线程中有线程在对ArrayList或HashMap进行修改(结构上的修改), 而有一个线程在用Iterator进行读取操作, 这个时候就有可能会抛ConcurrentModificationException, 因为用Iterator的时候, 不允许原List的结构改变。但可以用get方法来取。
常用技巧:
①. 采用面向接口的编程技巧, 比如现在需要写一个共通函数,对矢量集合类诸如Vector,ArrayList,HashSet等等进行操作, 但我并不知道最终用户会具体传给我什a ~ z 6么类型的类, 这个时候我们可以使用Collection接口, 从而使代码具有很大的灵活性。 代码示例如下:
/**
* 将list里面的所有元素用sep连接起来,
* list可以为Vector, ArrayList, HashSet等。
*/
public static String join(String sep, Collection list) {
StringBuffer sbq t k % ] j ~ 3 Q = new StringBuffer();
Iterator iterator = list.iterator();
while (iterator.hasNext()) {} - } y y : Q I -
sb.append(iterator.next());
if (iterator.hasNext()) {
sb.append(sep);
}
}
return sb.toString();
}
②. 利用Set进行Unique] 1 x j l Z z 9, 比如有一组对象(其中有对象是u I ] $ * f w重复的)- a . ? l 4 P _ W, 但我们只对不同的对象感兴趣, 这个时3 0 k ^候可以使用HasM w / I .hSet这个集合类, 然后可以通过覆盖Object的equals方法来选择自定义判断相等的rule。 缺省的是地址判断。 例:
class DataClass {
private String code = null;
private String name = null;
public void sV ; ] j w [etCode(String code)5 _ I q $ l Z t ( {
this.code = code;
}
public Strp k m ying getCode() {
return this.code;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
pu. ; ,blic Boolean equals(DataClass otherData) {
if (otherData != null) {& 7 e + U -
if (this.getCode() != null&& this.getCode().equals(otherData.getCode()) {
return true;
}
}
return false;
}
}
DataClass data1 = new DD ) a 6 E %ataCl@ Z / A A [ Cass();
DataClass dat@ + _ D ] e Ba2 = new DataClass();
data1.setCode(\"1\");
data2.setCode(\"1\");
HashSet singleSet = new HashSet();
singleSet.add(data1);
singleSet.add(data2);
结果singleSet里面只有data1, 因i ~ ) V # r +为data2.equals(data1), 所以data2并没有加进去。
③. 灵活的设Y ; { ~计集合的存储方式, 以获得较高效的处理。 集合里面可以再嵌套集合, 例:在ArrayList里面存放HashMap, HashMap里面再嵌套Hl , S v h w bashMap。
八、ResourceBundle, Properties
ResourceBundle:开发一个项目1 c o, 配置文件是少不了的, 一些需要根据环境进行修改的参数, 都有得+ w p F & W放到配置文件中去, 在Java中一般是通过一个properties文件来实现的, 这K } B o ) 0 _个文件以properties结尾。J J n ) 2 e 内部结构是二维的, 以key=value的形式存N ] U 5 f ?在。 如下:
options.column.naJ - f lme.case=1
options.column.bean.serializable=1
options.column.bean.defaultconstructog i @ X _ E m yr=1
options.column.method+ / U : m c |.setter=1
options.general.usF X x 6 ! Ler.version=1.0
database.connection[0]=5 7 u { g @ b Vcsc/csc@localhost_oci8
database.conne% a C ~ction[1]=cscweb/cscweb@localhost_thin
ResourceBuni ! * R ] 7 Kdle用来解析这样的文件, 它的功能是可以根据你的Locale来$ [ w y J ] a进行解析配置文件, 如果一个产品需要进行多语言支持, 比如在不同语种的系统上, 会显示根据它的语言显示相应的界面语言, 就可以定义多份的properties文件, 每个文件的key1 c x 4 ? i 6 x是一样的, 只是value不一样, 然后在application起动的时候, 可以判别本机的~ G t P r . , _ 4Locale来解析相应的properties文件。 Properties文件里面的数据得要是Unicode。 在jdk下面可以用native2asciiL + 5 1 G - o t这个命令进行转换。 例: natj } H Bive2ascii Message.txt Message.properties 会生成一个Unicode的文件。
Properties: Properties这个类其实就是从Hashtable继承下来的, 也就是说它是一个散列表, 区别在于D c它的key与value都是String型的, 另外也加了几个常用的方法:
- String getProperty(String key; V I /) 取得一个property
- String getProperty(String key, St! 6 ` b ] C H o uring defaultValue) 取pr# W 5 Y #opertyl x 8, 如果不存在则返回defaultValue。
- void list(PrintStream out) 向out输出所有的properties
- void list(PrintWriter out)
- Enumeration propert) P y m Y Q ) X WyNames() 将所有的proper* I A j ; . 9ty key名以Enumeration形式返回。
- Object setProperty(String key, String value) 设置一个property。
ResourceBundle与Properties一般结合起来使用。 它们的用法很简单, 由ResourceBundle解析出来的key与value然后放至到一个静态的Propertip u [ 7 jes成员变量里面去, 然后就可以通过访问Properties的方法进行读取Property。 下面给个简单的例子:
public class PropertyManager implements SerD / x D 3 ? T nializ[ e F Z o = 1able {
/** 定义一个静态的Proper& c w 8 ) Kties变量 */
private static Properties properties = new Properties();
/**
* 通过一个类似于类名的参数进行Property文件的初期化
* 比如现在有一个文件叫Message.properties, 它存放在
* ejb/util下面并且, 这个目录在运行的classpath下面
*8 + F x v & W 1 N 则in就为ejb.util.Message
*
*/
public static void init(String in) throws Missio ) 3 x dngResourcn P 6 a 6eException {
ResourceBundle bundle = Resourn Z ; X a | L j KceBundle.getBundle(in);
Enumeration enum^ f o Y t b v = bundle.getKeys();
ObjecW % jt key = null;
ObjeP X O V Hct val1 ! ~ w g h f 0 Hue = null;
while (enum.hasMoreElements()) {
key = enum.nextElement();
valu^ v . Ne = bundle.getStringE G U ( S ` & T(key.toString());
properties.put(key, value);
}
}
/**
* 取得一个Property值
*/
public[ [ . M C 1 z static String getProperty(String key) {
return properties.getL B z h Z b C f u(key);
}
/**
* 设置一个Property值
*/
public static void setProperty(String key, String value) {R j p } T d 8 ~ r
properties.put(key,7 D 4 4 Z value);
}
}
不过现在的Java产品中,越来越倾向0 X @ i Z K s `于用XML替换Properties文件来进行配置。 XML配置具有层次结构清楚的优点。
九、Exceptions
Throwable
Exception
Error
RuntimeException
CG + q q wlassNotFoundException
IOException
SQLException
IndexOutOfBoundsException
ClassCastException
NullPo P ! T x 3 : KointerException
OutOfMemoryError
StackOverflowError
NoClassDD t 0 0 v w r ;efFoundError
Java采用违例(Exception)处理机制来进行错误处理。 违例机制的一个好处就是能够简化错误控制代码, 我们) 7 N { y R M再也不用检查一个特定+ E i 0 ?的错误, 然后在程序的多处地方对其进行控制。 此外, 也不需要在方法调用的时候检查错误(因为保证有人能够捕获这里的错误)。W r m 我们只需要在一个地} u - 6 f &方处理问题:”违例控制模块”或者”违例控制器”。 这样可有效减少代码量, 并将那些用于描述K W l具体操作的代码与专门纠正错误的代码分隔开。
一个完整] o 6 b u ` y的违例例子:
public void thj M N c h x VrowTest() thrG , } vows MyException {
try {D H , b k y P
...
}
catch (SQLException se)W s c i q - ) U {
cat.error(\"\", se);
throw new MyException(se.getMessage())? W A;
}
catq W q I | ? ~ hch (Exception e) {
cat.error(\"\", e);
}
finally {
...
}
}
如果一段代码有可能会抛出w O 2 i违例可以用try {} catch {}来处理。 被catch到的违例可以再抛出, 也 R f B @ k O X可以转换为其它类型的Exception抛出。 finally块里面的J P R r W + L s Y代码总会被执行g 5 o ~ e到的, 不管前面是否已经throw或return了。
Throwable是所有违例的基类, 它有两种常规类型。 其中, Error代表编绎期和系统错误, 我们一般不必特意捕获它们。 Exception是可以从任何标准Java库的类方法中掷出的基x 5 * m N /本类型。
看4 j y ^ % p U 5上面的图, 如果是Error的子类或是Runtih ( w m y X A % $meException的子类这种b X W W违例有一定的特殊性, 可以说我们可以当它们不存在, 当这种违例抛出的时候, 我们= s o w T j S [可以不catch它, 也可以不在方法上throws它。 Ruu u e %ntimeException一般代表的是一个编程错误, 是完全可以避免的。
性能注意点: 因为使用了Exception之后是要影响一些效率的, 所以ExceptionH ? 9 6 I t Y v ^不能滥用。一般的不要用ExcepI j c 9 o 3 mtion来控制业; _ ) q _ j 9 C务流程, 其次不要循环体内使用。
技巧:我们可以从Exception或直接q S q从Throwable继承写我们自己的Exception, 然后根据业务需s a n ] t J要抛不同种类的Exception。
十、JDBC类库
有了 JDBC,向各种关系数据P ) V r库发送 SQL 语句就是一件很容易的事。换言之,有了 JDBC API,就不必为访问 Sybase 数据库专门写一个程序R X o E K,为访问 Oracle 数据库又专门写一个程序,为访问 Informix 数据库又写另一个程序,等等。您只需用 JDBC API 写一个程序就够了,它可向相应数据库发送 SQL 语句。而且,使用 Java 编程语言编写的应用程序,` } } -就无须去忧虑要为不同的平台编写不同的应用程序。将 Java 和 JDBC 结合起来将使程序员只须写一遍程序就可让它在任何平台上运行。
下面为常用的处理流程:
DriverManager
Connection
Statement
PreparT ! 8 $ f 2 CedStatement
CallableStatement_ ( v J [ 8
ResultSet
简单地说,JD: 0 ZBC 可做三件事:
- 与数据库建立连接
- 发送 SQL 语句
- 处理结果
下列代码段给出了以上三步的基本示例:
Class.forName(\"oracle.jdbc.driver.OracleDriver\").newInstance();
Connection conn = DriverManager.getConnection (
\"jdbc:oracle: k )thin:@K 2 { 7 j P F Leai-sol:1521:eai_db\", \"csc2\", \"csc2\")@ % z d : 8 @ C E;
Sta! 5 7 g = @ y e atement stmt = co4 @ n $ 8 7 R nnn.createStatement();
ResultSet rs = stmt.executeQuery(\"SELECT CW T gONTACTID FROM CONTACTINFOf T ] ( s\");
while (rs.next()) {long conk y 2 A j R ]tactID = rs.getLong(\"CONTACTIx ~ ; f QD\");}
下面对常用的几个类和接口做些简单的说明。
DriverManager:
DriverManager类是 JDBC 的管理t z 3 Z @层,作用于用户和驱动程序之间。它跟踪可用的驱动程序,并在数据库和相应驱动程序之间建立连接。另外,DriverManager 类也处理诸如驱动程序登录时间限制及登录* U Z ^ Y 8 s和跟踪消息的显示等事务。对于简单的应 r | 9 s E ? e S用程序,一般程序& N 3 b S j员需要在此类中直接使用的唯一方法是 DriverM{ C g J # ~ * oanager.getConnection。M E @ ! j ]正如名称所示Z Y ; p : P,该方M P U l v p法将建立与数据库的连接。
Class.forName(\"oracle.jdbc.driver.OraclQ , 2 V n ; u TeDriver\")a ` ~ O { q # R.newInstance();
Connection conn = DriveT ; N [ { U O /rMX O 7anager.getD K m lConnection (
\"jdbc:oracle:thin:@eai-sol:1521:eai_db\", \"csc2\", \"cscW C 2 v V f / u r2\");
其中第一句话的作用是在当前的环境中load一个DB Driver, 有人可能觉得奇怪,9 J y M 这句话执行完之后, 后面怎么知道去用这个Driver呢? 其实DriverManageG J I u T L M or可以从load的classes里面找到注册过的driver,然后使用它所找到的第一个可以成功连接到给定 URLz ~ M ? x 的驱动程序。 第二句话的三个参数分别是URL, UseX n Rr, Password。Driver不一样, U= 1 VRL可能也不一样。
S$ O o f $tatement:
Statement 对象用于将 SQL 语句发送到数据库中。实际上有三种 Statement 对象,它们都为在给定连接上执行 SQL 语句的包容器:Statement、Prepv ~ W ? s garedStatement(它从 Statement 承而来)和 Caln o w L P e k ?lableStatement(它从 PreparedStatement 继承而来)。它们都专用于发送定类型的 SQL 语句: Statement 对象用于执行不带参数的简单 SQL 语句;Pr_ e $ j zeparedStatement 对象用于执行带或不带 IN 参数的预编译 SQL 语句;CallableStatement 对象用于执行K L 8 v T l f对数据库已存储过程的调用。
Statement 接口提供了执行语句和获取结果的基本方法。PreparedStatement 接口添加了处理 IN 参数的方法;而 CallableStatement 添加了处理 OP ] ^ R P & 8 r !UT 参数的方法。
①. 创建 Statement 对象
建立了到特定数据库的连接之后,就可用该 7 S # 6连接发送 SQL 语句。Statement 对象用 Connection 的方法 createStatement 创建,如下列代码段中所示:
Statement stmt = conn.createStatement();
为了执行 Statement 对象,被发送到数据库的 SQL 语句将被作为参数提供给 Statement 的方法:
ResultSet rs = stmt.executeQuery(\"SELECT a, b, c FROM table1\");
②. 使用 Statement 对象执行语句
Statement 接口提供了三种执行6 ; 8 SQL 语句的方法:executeQuery、ex6 3 V ^ 0 -ecuteUpdate 和execute。使用哪一个方法由 SQL 语句所产生的内容决定。
方法 executeQuery 用于产生单个结果集的语句,例如 SELECT 语句。b Q , 0 ( !
方法 executeUpdate 用于执行 INSERT、UPDATE 或 DELETE 语句以及v v 0 SQL DDL(数据定义语言)语句,例如 CREATE TAX r RBLE 和 DROP TABLE。INSERT、UPDATE 或 DELETE 语句的效果是修改表中零T Y K行或多行中的一列或多列。executeUpdate 的返回值是一个整数,指示受影响的行数(即更新计数)。对于 CREATE TABLE 或 DROP TABLE 等不操A r a ] =作行的语句,executeUpdate 的返回值总为零。
方法 execute 用于执行返回多个结果集、F b 8 w } q t多个更新计数或二者组合的语句。
③. 语O o h句完 B Q = y ^成
当连接处于自动提交模式时,其中所执行的语P $ h w y L t ( F句在完成时将自动提交或还原。语句在已执行且所有结果返回时,即m D | M ;认为已完成。对于返回一个结果v ] f E e Y e集的 executeQuery 方法,在检索完 ResultSet 对象的所有行时该语句完成。对于方法 executL - reUpdate,当它执行时语句s t . E n即完成。但在少数调用方法 execute 的情况中,在检索所有结果集或它生成的更新计数之后语句才完成。
④. 关闭 Statement 对象
Statement 对象将由 Java 垃圾收集程序自动关闭。而作为一种好的编程风格,应5 { U ; T在不需要 Statement 对象时显式d d S d X x (地关8 z S + o闭它们。这将立即释放 DBMS 资源,有助于避免潜在的内存问题。 关闭Statemm [ | *ent用 stmt.close() 方法。
ResultSet:
ResultSet 包含符合 SQL 语句中条件的所有行,并且它通过一套 get 方法(这些 get 方法可以访问当前行中的不同列)提供了对这些行中数) . 6 + .据的访问。ResultSn ] Uet.next 方法用于移动到 ResultSek M M 4 Pt 中的下一行,使下一行成为当前行。
结果集一般是一个表,其中有查询所返回的列标题及相应的值。例如,如果查询为 SELECT a, b, c FROM Table1,则结果集将具有如下形式:
a b c
-------- --------- --------
12345 Cupertino CA
83472 Redmond{ [ U A 2 WA
83492 Boston MA
下面的代码段是执行 SQL 语句的示例。该 SQL 语句将返回行集合,其中列r 6 ] / 1 为 int,列 2 为 String,而列 3 则为日期型:
Statement stmt = conn.creatn . O A ` 8 w k geStatement();
ResultSet rs = stmt.executeQuery(\"Sm + m pELD f A X : ` (ECT a, b, c FROM Table1j T Z b\");
while (rs.next()) {
int i = rs. y I : & = 4getint(\"a\");
Strin8 ) I Wg s = rs.getString(\"b\");
Time= A { ! S % $ Qstamp t = rs.getTimestamp(\"c\");
}
①. 行和光标
ResultSet 维护指