如果现在要想保存多个对象,肯定使用对象数组完成,但是对象数组本身有一个最大的问题在于其数据的长度,所以后来使用了链表完成了动态对象数组的开发,可是链表的开发难度实在是很大,而且如果一个链表要想真正去使用,只依靠之前所编写的还不够,还需要进行一些代码的调优。
而在JDK 1.2之后正式引入了类集的概念,类集是一种动态的对象数组,属于各个数据结构的实现类,在整个类集之中主要的组成是一些核心的操作接口:Collection、List、Set、Map、Iterator、Enumeration。
所谓的单值保存指的是每一次操作只会保存一个对象,就好像之前的链表程序一样,每一次只保存了一个对象,在Collection接口之中定义了如下的一些操作方法。
No. |
方法名称 |
类型 |
描述 |
1 |
public boolean add(E e) |
普通 |
数据增加 |
2 |
public void clear() |
普通 |
清除数据 |
3 |
public boolean contains(Object o) |
普通 |
查找数据是否存在 |
4 |
public boolean isEmpty() |
普通 |
判断是否为空集合 |
5 |
public Iterator |
普通 |
为Iterator接口实例化 |
6 |
public boolean remove(Object o) |
普通 |
删除数据 |
7 |
public int size() |
普通 |
取得集合的个数 |
8 |
public Object[] toArray() |
普通 |
将集合变为对象数组 |
在Collection接口之中一共定义了15个方法,在所有的方法之中,只有两个方法最为常用:add()、iterator()。不过从开发上讲,很少会去直接使用Collection,都会使用Collection的两个子接口:List、Set。
List是Collection的一个最为常用的子接口,首先这个接口的定义如下:
public interface List |
但是List接口对Collection接口进行了大量的扩充,但是扩充之后的主要方法:
No. |
方法名称 |
类型 |
描述 |
1 |
public E get(int index) |
普通 |
取得指定索引位置上的数据 |
2 |
public E set(int index, E element) |
普通 |
修改指定索引位置上的数据 |
3 |
public ListIterator |
普通 |
为ListIterator接口实例化 |
但是以上的三个方法,只是针对于List接口起作用,而List接口有两个常用子类:ArrayList、Vector。
ArrayList是List子接口使用最多的一个子类,而这个类的定义如下:
public class ArrayList extends AbstractList implements List |
按照面向对象的概念来讲,现在使用ArrayList主要的目的是为List接口实例化,所有的操作方法都以List接口为主。
范例:使用ArrayList进行List接口的功能验证
package cn.mldn.demo; import java.util.ArrayList; import java.util.List; public class TestDemo { public static void main(String[] args) throws Exception { List all.add("Hello") ; all.add("Hello") ; // 内容重复了 all.add("World") ; for (int x = 0; x < all.size(); x++) { String str = all.get(x) ; // get()方法只有List接口有 System.out.print(str + "、"); } } } |
在使用代码的时候可以发现,List集合之中即使存在了重复数据,也可以正常的保存,而且数据保存的顺序就是存入数据的顺序。
范例:使用List集合修改之前的程序
package cn.mldn.demo; import java.util.ArrayList; import java.util.List; interface Animal { // 动物 public String getName() ; public int getAge() ; } class Zoo { private List public void add(Animal ani) { // 增加动物 this.animals.add(ani) ; // 增加动物 } public void delete(Animal ani) { this.animals.remove(ani) ; // 需要equals() } public List List for (int x = 0 ; x < this.animals.size() ; x ++) { Animal ani = this.animals.get(x) ; if (ani.getName().contains(keyWord)) { // 满足 result.add(ani) ; } } return result ; } } class Dog implements Animal { private String name ; private int age ; public Dog(String name,int age) { this.name = name ; this.age = age ; } public String getName() { return this.name ; } public int getAge() { return this.age ; } public boolean equals(Object obj) { if (this == obj) { return true ; } if (obj == null) { return false ; } if (!(obj instanceof Dog)) { return false ; } Dog dog = (Dog) obj ; if (this.name.equals(dog.name) && this.age == dog.age) { return true ; } return false ; } public String toString() { return "〖狗的信息〗名字:" + this.name + ",年龄:" + this.age ; } } class Tiger implements Animal { private String name ; private int age ; public Tiger(String name,int age) { this.name = name ; this.age = age ; } public String getName() { return this.name ; } public int getAge() { return this.age ; } public boolean equals(Object obj) { if (this == obj) { return true ; } if (obj == null) { return false ; } if (!(obj instanceof Tiger)) { return false ; } Tiger t = (Tiger) obj ; if (this.name.equals(t.name) && this.age == t.age) { return true ; } return false ; } public String toString() { return "〖老虎的信息〗名字:" + this.name + ",年龄:" + this.age ; } } public class TestDemo { public static void main(String args[]) { Zoo zoo = new Zoo() ; // 动物园 zoo.add(new Dog("花狗",1)) ; zoo.add(new Dog("黄狗",1)) ; zoo.add(new Dog("黑狗",1)) ; zoo.add(new Dog("斑点狗",1)) ; zoo.add(new Tiger("斑点虎",2)) ; zoo.add(new Tiger("黑虎",2)) ; zoo.add(new Tiger("花虎",2)) ; zoo.delete(new Dog("斑点狗",1)) ; // 删除 List for (int x = 0 ; x < result.size() ; x ++) { System.out.println(result.get(x)) ; } } } |
至少此时的程序不再需要自己去开发链表了,所有的链表的实现类都有了。
Vector类是在JDK 1.0的时候就推出的一个最早的实现动态数组的操作类,实际上对于今天而言,有许多的类上依然还是在使用着Vector,不过从实际的开发来讲,现在设计的一些程序都是针对于接口的操作了。
package cn.mldn.demo; import java.util.List; import java.util.Vector; public class TestDemo { public static void main(String[] args) throws Exception { List all.add("Hello") ; all.add("Hello") ; // 内容重复了 all.add("World") ; for (int x = 0; x < all.size(); x++) { String str = all.get(x) ; // get()方法只有List接口有 System.out.print(str + "、"); } } } |
因为所有的操作都是针对于接口完成的,接口定义的方法不变,子类随便变。
面试题:请解释ArrayList和Vector的区别?
No. |
区别 |
ArrayList |
Vector |
1 |
推出时间 |
JDK 1.2 |
JDK 1.0 |
2 |
性能 |
采用异步处理方式,性能更高 |
采用同步处理方式,性能相对较低 |
3 |
安全性 |
非线程安全 |
线程安全 |
4 |
输出 |
Iterator、ListIterator、foreach |
Iterator、ListIterator、foreach、Enumeration |
从实际开发而言,几乎都是开发异步程序,所以首选的肯定是ArrayList子类。
Set也是一个Collection较为常用的子接口,这个接口的定义如下:
public interface Set |
在Collection接口定义了15个方法,但是Set子接口并不像List子接口那样对Collection接口进行了大量的扩充,而是完整的继承了下来,那么就证明了在Set子接口之中是肯定无法使用get()方法的。
那么在Set子接口之中常用的两个子类:HashSet、TreeSet,下面分别说明。
Hash(哈希)属于一种算法,这种算法的核心意义指的是找空保存算法,所以只要一看见hash第一反应就是说没有顺序的保存。
范例:观察Set接口使用
package cn.mldn.demo; import java.util.HashSet; import java.util.Set; public class TestDemo { public static void main(String[] args) throws Exception { Set all.add("Hello") ; all.add("Hello") ; // 内容重复了 all.add("World") ; System.out.println(all); } } |
保存数据再输出之后可以发现,重复的数据没有了,并且其本身的保存也是没有任何顺序的。
如果现在希望Set集合之中保存的数据有顺序,那么就通过TreeSet进行Set接口的实例化。
范例:使用TreeSet
package cn.mldn.demo; import java.util.Set; import java.util.TreeSet; public class TestDemo { public static void main(String[] args) throws Exception { Set all.add("D") ; all.add("A") ; // 内容重复了 all.add("B") ; all.add("B") ; all.add("C") ; System.out.println(all); } } |
现在发现所有保存的数据没有重复且有序排列。
通过之前的程序可以发现,使用TreeSet实例化Set接口之中,所有保存的数据都是有序的,那么在这种情况下,那么如果说使用的是一个自定义的类呢?
那么这个时候如果这个类对象要进行排序的话,则这个类必须实现Comparable接口,设置比较规则。但是在这种情况下有一点必须注意:一旦使用了Comparable之后,类之中的所有属性都必须写进排序规则。
范例:自定义类排序
package cn.mldn.demo; import java.util.Set; import java.util.TreeSet; class Person implements Comparable private String name ; private int age ; public Person(String name,int age) { this.name = name ; this.age = age ; } @Override public String toString() { return "姓名:" + this.name + ",年龄:" + this.age + "\n" ; } @Override public int compareTo(Person o) { if (this.age > o.age) { return 1 ; } else if (this.age < o.age) { return -1 ; } else { return this.name.compareTo(o.name); } } } public class TestDemo { public static void main(String[] args) throws Exception { Set all.add(new Person("张三",20)) ; all.add(new Person("张三",20)) ; // 全部重复 all.add(new Person("李四",20)) ; // 年龄重复 all.add(new Person("王五",19)) ; all.add(new Person("赵六",21)) ; System.out.println(all); }} |
TreeSet子类依靠Comparable中compareTo()方法的返回值是否为0来判断是否为重复元素。
那么TreeSet依靠Comparable进行重复元素判断,那么HashSet可以吗?发现以上的程序换为了HashSet之后,该有的重复还是有,因为从真正的意义上来讲,判断重复元素依靠的不是Comparable(只有排序的时候才依靠Comparable),所有的重复元素的判断依靠于Object类的两个方法:
· hash码:public int hashCode();
· 对象比较:public boolean equals(Object obj)。
在进行对象比较的过程之中,首先会先使用hashCode()与已保存在集合之中的对象的hashCode()进行比较,如果代码相同,则再使用equals()方法进行属性的依次判断,如果全部相同,则为相同元素。
那么为了保证每一个对象的hashCode()不一样,需要设计一组数学公式才可以,会吗?
范例:重复元素
package cn.mldn.demo; import java.util.HashSet; import java.util.Set; class Person { private String name ; private int age ; public Person(String name,int age) { this.name = name ; this.age = age ; } @Override public String toString() { return "姓名:" + this.name + ",年龄:" + this.age + "\n" ; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } } public class TestDemo { public static void main(String[] args) throws Exception { Set all.add(new Person("张三",20)) ; all.add(new Person("张三",20)) ; // 全部重复 all.add(new Person("李四",20)) ; // 年龄重复 all.add(new Person("王五",19)) ; all.add(new Person("赵六",21)) ; System.out.println(all); } } |
至此,Object类之中的全部方法就讲解完成了。
在之前所介绍的都属于单值集合的基本操作,可是对于集合有一个最为重要的问题就是如何进行集合内容的输出操作,而这个问题在Java的类集框架之中给出了四种输出方式:Iterator、ListIterator、Enumeration、foreach。
Iterator是最为常用的集合输出接口,在这个接口中一共定义了三个方法,但只有两个有真正用处:
· 判断是否有下一个元素:public boolean hasNext();
· 取得下一个元素:public E next()。
在Iterator接口之中存在了一个remove()方法,但是这个方法真没用。而且在之前学习的Scanner也是Iterator的子类。
但是如何取得Iterator接口的实例化对象呢?这一操作在Collection接口就已经明确定义了,因为Collection继承了一个Iterable接口,在这个接口下定义了一个方法:public Iterator
范例:使用Iterator输出集合数据
package cn.mldn.demo; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class TestDemo { public static void main(String[] args) throws Exception { List all.add("Hello"); all.add("Hello"); // 内容重复了 all.add("World"); Iterator while (iter.hasNext()) { // 判断是否有下一个元素 String str = iter.next() ; System.out.print(str + "、"); } } } |
以后只要是见到了集合的输出操作,永远都使用Iterator接口完成。
Iterator可以完成的是由前向后的单向输出操作,如果现在希望可以完成由前向后,和由后向前输出的话,那么就可以利用ListIterator接口完成,此接口是Iterator的子接口,在ListIterator接口主要使用以下两个扩充方法:
· 判断是否有前一个元素:public boolean hasPrevious();
· 取出前一个元素:public E previous()。
但是如果要想取得ListIterator接口的实例化对象,Collection没有这样的方法支持,这个方法在List接口之中存在:public ListIterator
范例:执行双向迭代
package cn.mldn.demo; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; public class TestDemo { public static void main(String[] args) throws Exception { List all.add("Hello"); all.add("Hello"); // 内容重复了 all.add("World"); ListIterator System.out.print("由前向后输出:"); while (iter.hasNext()) { // 判断是否有下一个元素 String str = iter.next() ; System.out.print(str + "、"); } System.out.print("\n由后向前输出:"); while (iter.hasPrevious()) { String str = iter.previous() ; System.out.print(str + "、"); } } } |
但是对于由后向前的输出操作,在进行之前一定要首先发生由前向后的输出。由于此输出接口只有List可以使用,所以在开发之中几乎不会出现。
Enumeration是一个最早的输出接口,最早称为枚举输出,在JDK 1.0的时候就已经推出了,并且在JDK 1.5的时候将其功能进行了扩充,主要就是增加了泛型,在Enumeration接口里面只定义了两个方法:
· 判断是否有下一个元素:public boolean hasMoreElements();
· 取得当前元素:public E nextElement();
不过要想取得Enumeration的实例化对象,不能依靠Collection接口了,只能够依靠Vector类完成,在Vector子类之中定义了如下一个方法:public Enumeration
范例:使用Enumeration进行输出
package cn.mldn.demo; import java.util.Enumeration; import java.util.Vector; public class TestDemo { public static void main(String[] args) throws Exception { Vector all.add("Hello"); all.add("Hello"); // 内容重复了 all.add("World"); Enumeration while (enu.hasMoreElements()) { String str = enu.nextElement(); System.out.print(str + "、"); } } } |
从开发而言,首先考虑绝对不是Enumeration,考虑的肯定是Iterator,只有在必须使用的时候才用它。
对于foreach输出除了可以进行数组内容的输出之外,也可以针对于集合完成输出。
范例:使用foreach
package cn.mldn.demo; import java.util.ArrayList; import java.util.List; public class TestDemo { public static void main(String[] args) throws Exception { List all.add("Hello"); all.add("Hello"); // 内容重复了 all.add("World"); for (String str : all) { System.out.print(str + "、"); } } } |
使用foreach并不是一个被广泛认可的操作代码形式。
偶对象指的是一对对象,即:两个对象要同时保存。这两个对象是按照了“key =value”的形式进行定义的,即:可以通过key找到对应的value数据,就好象电话号码本一样,例如,电话号码本之中保存了如下的信息:
· key = 张三,value = 123456;
· key = 李四,value = 234567;
现在如果要想找到张三的电话,那么肯定根据张三的key,取得对应的value,,而如果现在要想找王五的电话,由于没有王五这个key,所以返回的结果就是null。
Map就是实现这样一种操作的数据结构,这个接口之中定义的主要操作方法如下。
No. |
方法名称 |
类型 |
描述 |
1 |
public V put(K key, V value) |
普通 |
向集合之中保存数据 |
2 |
public V get(Object key) |
普通 |
通过指定的key取得对应的value |
3 |
public Set |
普通 |
将Map中的所有key以Set集合的方式返回 |
4 |
public Set |
普通 |
将Map集合变为Set集合 |
在Map接口之中有两个常用的子类:HashMap、Hashtable。
HashMap是Map接口之中使用最多的一个子类,这个子类的定义如下:
public class HashMap extends AbstractMap implements Map |
下面就直接通过HashMap演示Map接口中各个主要方法的作用。
范例:验证Map方法
package cn.mldn.demo; import java.util.HashMap; import java.util.Map; public class TestDemo { public static void main(String[] args) throws Exception { Map map.put(3, "张三"); map.put(null, "无名氏"); map.put(3, "李四"); // key重复,value会被新内容覆盖 map.put(1, "王五"); map.put(0, "赵六"); System.out.println(map.get(3)); System.out.println(map.get(null)); } } |
通过这一代码可以发现,Map和Collection在操作上的不同:
· Collection接口设置完的内容目的是为了输出;
· Map接口设置完内容的目的是为了查找。
范例:取得全部的key,全部的key通过Set集合返回
package cn.mldn.demo; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class TestDemo { public static void main(String[] args) throws Exception { Map map.put(3, "张三"); map.put(null, "无名氏"); map.put(3, "李四"); // key重复,value会被新内容覆盖 map.put(1, "王五"); map.put(0, "赵六"); Set Iterator while (iter.hasNext()) { Integer key = iter.next() ; System.out.println(key + " --> " + map.get(key)); } } } |
Hashtable是在JDK 1.0的时候推出的一个数据结构类,在JDK 1.2之后,让Hashtable实现了一个Map接口,所以用户在使用的时候依然采用子类为接口实例化的方法进行,那么只要接口的方法不变,实际上不管使用那一个子类都一样。
package cn.mldn.demo; import java.util.Hashtable; import java.util.Map; public class TestDemo { public static void main(String[] args) throws Exception { Map map.put(3, "张三"); map.put(3, "李四"); // key重复,value会被新内容覆盖 map.put(1, "王五"); map.put(0, "赵六"); System.out.println(map.get(3)); System.out.println(map.get(10)); } } |
这个时候在使用Hashtable子类的时候,里面的数据不能有null。
面试题:请解释HashMap和Hashtable的区别?
No. |
区别 |
HashMap |
Hashtable |
1 |
推出时间 |
JDK 1.2 |
JDK 1.0 |
2 |
性能 |
采用异步处理方式,性能更高 |
采用同步处理方式,性能相对较低 |
3 |
安全性 |
非线程安全 |
线程安全 |
4 |
设置null |
允许将key或value设置为null |
不允许出现null,否则出现空指向异常 |
对于集合操作,在之前就一直强调:只要是集合的输出都使用Iterator完成,但是对于现在的Map集合就麻烦了,因为Map接口之中并没有提供像Collection接口那样的iterator()方法,所以如何使用Iterator输出Map集合呢?
如果要想真正的去思考Map接口通过Iterator输出,那么首先需要来观察一个Map.Entry接口,此接口定义如下:
public static interface Map.Entry |
很明显,这是一个在Map接口之中使用static定义的一个内部接口。而且通过Map接口的定义也可以发现此内部接口的存在。
而在Map.Entry这个内部接口之中还存在有以下的两个常用方法:
· 取得当前的key:public K getKey();
· 取得当前的value:public V getValue()。
下面通过一个图形来对比一下Collection和Map接口保存的数据形式。
通过以上的对比可以发现,在Map集合和Collection集合之中保存的最大区别:Collection直接保存的是要操作对象,而Map集合是将保存的key和value变成了一个Map.Entry对象,通过这个对象包装了key和value后保存的,所以根据这一特征,就可以给出Map使用Iterator输出的操作步骤:
· 使用Map接口中的entrySet()方法,将Map集合变为Set集合;
· 取得了Set接口实例之后就可以利用iterator()方法取得Iterator的实例化对象;
· 使用Iterator迭代找到每一个Map.Entry对象,并进行key和value的分离。
范例:使用Iterator输出Map集合
package cn.mldn.demo; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import java.util.Set; public class TestDemo { public static void main(String[] args) throws Exception { Map map.put(3, "张三"); map.put(3, "李四"); // key重复,value会被新内容覆盖 map.put(1, "王五"); map.put(0, "赵六"); Set Iterator while (iter.hasNext()) { Map.Entry System.out.println(me.getKey() + "," + me.getValue()); } } } |
这种代码在日后的所有开发之中一定会出现,所以必须会。
面试题:现在在一个List集合之中保存了多个String对象,要求将这个List集合变为Set集合,而后再将这个Set集合之中的全部数据保存在Map集合的value里面,而Map集合的key使用UUID生成,最后将Map中的数据进行迭代输出。
相关说明:
1、 在Collection接口之中存在一个增加一组集合的方法:public boolean addAll(Collection extends E> c);
2、 UUID是一种算法,在Java中有指定的类,这个类可以根据时间戳生成一个几乎不会重复的字符串;
package cn.mldn.demo; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; public class TestDemo { public static void main(String[] args) throws Exception { List list.add("Hello"); list.add("Hello"); list.add("World"); Set set.addAll(list); // 将List集合加到Set之中 Map Iterator while (iter.hasNext()) { map.put(UUID.randomUUID(), iter.next()); } // 数据保存到Map集合 Iterator while (iterMap.hasNext()) { Map.Entry System.out.println(me.getKey() + " --> " + me.getValue()); } } } |
就是把几个集合互相折腾了一番。
通过程序可以发现,之前的Map集合之中都是使用了系统类作为了Map的key,那么实际上用户也可以使用自定义的类作为key出现,可是如果要想作为key的类必须注意一点:因为key属于查找操作,所以要想找到符合的key,那么作为key所在的类就必须覆写Object类之中的两个方法:hashCode()、equals()。
范例:自定义类作为key
package cn.mldn.demo; import java.util.HashMap; import java.util.Map; class Person { private String name; public Person(String name) { this.name = name; } @Override public String toString() { return "姓名:" + this.name; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } } public class TestDemo { public static void main(String[] args) throws Exception { Map map.put(new Person("张三"), new String("zs")); System.out.println(map.get(new Person("张三"))); } } |
但是这种程序也只是作为学习之中的概念出现,而在实际的开发之中,永远都是String作为key,因为最方便。
Stack是栈,栈是一种先进后出的数据结构,Stack类的定义如下:
public class Stack |
可以发现Stack类属于Vector的子类,但是使用的时候却不使用Vector类定义的方法,而使用Stack类自己的方法:
· 入栈操作:public E push(E item);
· 出栈操作:public E pop();
范例:观察栈的基本操作
package cn.mldn.demo; import java.util.Stack; public class TestDemo { public static void main(String[] args) throws Exception { Stack all.add("A"); all.add("B"); all.add("C"); System.out.println(all.pop()); System.out.println(all.pop()); System.out.println(all.pop()); System.out.println(all.pop()); // 没数据了,出现EmptyStackException } } |
对于栈这一概念在自己编写的代码之中使用不多,不过以后的学习都会出现栈的概念,例如:在Android开发之中,多个Activity之间的互相调用和返回就是利用了栈。
Collections是专门提供的一个集合的工具类,并没有实现Collection接口,但是在这个类之中,有许多的操作方法,可以方便的进行集合的操作(根本没用)。
package cn.mldn.demo; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class TestDemo { public static void main(String[] args) throws Exception { List Collections.addAll(all, "A", "B", "C"); System.out.println(all); Collections.reverse(all) ; System.out.println(all); } } |
面试题:请解释Collection和Collections的区别?
· Collection是一个接口,用于定义集合操作的标准;
· Collections是一个工具类,可以操作任意的集合对象。
属性一般都是指的是针对于字符串数据,并且所有的字符串数据都会按照“key = value”的形式保存,属性操作类主要是针对于属性文件完成的。Properties类本身是Hashtable的子类:
public class Properties extends Hashtable |
但是在使用方法上操作的并不是由Map接口定义的方法,使用Properties自己的方法:
· 设置属性:public Object setProperty(String key, String value);
· 取得属性:public String getProperty(String key),如果没有指定的key返回null;
· 取得属性:public String getProperty(String key, String defaultValue),如果没有指定的key,返回默认值。
范例:观察属性的设置和取得
package cn.mldn.demo; import java.util.Properties; public class TestDemo { public static void main(String[] args) throws Exception { Properties pros = new Properties(); pros.setProperty("BJ", " BeiJing "); pros.setProperty("SH", "上海"); System.out.println(pros.getProperty("BJ")); System.out.println(pros.getProperty("TJ")); System.out.println(pros.getProperty("TJ", "没有发现")); } } |
但是使用Properties类最方便的特点是可以直接将这些属性以OutputStream的方式或InputStream的方式输出或读取:
· 向输出流中输出属性:public void store(OutputStream out, String comments) throws IOException;
· 从输入流中读取属性:public void load(InputStream inStream) throws IOException。
范例:将属性保存到文件之中,一般保存属性文件的后缀都是“*.properties”
package cn.mldn.demo; import java.io.File; import java.io.FileOutputStream; import java.util.Properties; public class TestDemo { public static void main(String[] args) throws Exception { Properties pros = new Properties(); pros.setProperty("BJ", " BeiJing "); pros.setProperty("SH", "上海"); pros.store(new FileOutputStream(new File("D:" + File.separator + "area.properties")), "Area Info"); } } |
范例:通过属性文件读取内容
package cn.mldn.demo; import java.io.File; import java.io.FileInputStream; import java.util.Properties; public class TestDemo { public static void main(String[] args) throws Exception { Properties pros = new Properties(); pros.load(new FileInputStream(new File("D:" + File.separator + "area.properties"))); System.out.println(pros.getProperty("BJ")); System.out.println(pros.getProperty("TJ")); System.out.println(pros.getProperty("TJ", "没有发现")); } } |
在日后的开发之中,一些框架会帮助用户自动的编写读取属性的操作,所以用户以后最关心的只是修改属性文件的工作,这一点在日后的Struts、Spring中都会见到。
1、 Collection负责输出、Map负责查找;
2、 集合的输出就使用Iterator完成。
JDBC是Java数据库连接服务,主要是一个由Java程序操作数据库的执行标准,所以在这个JDBC的开发包java.sql包之中,所有的组成几乎都是接口,那么不同的数据库如果要想使用java开发,就必须实现这些接口的标准。不过JDBC不是技术,而是一种服务:所有的操作完全固定。
在JDBC之中的核心组成部分:DriverManager类、Connection接口、Statement接口、PreparedStatement接口、ResultSet接口,即:五个接口一个类,就构成了整个JDBC的组成。
但是如果要进行JDBC的开发,程序必须首先要连接到数据库,而在Java之中,对于程序连接数据库的操作,有以下的三种方式支持:
· JDBC-ODBC:使用微软的ODBC技术进行数据库的连接;
|- 流程:程序 è JDBC è ODBC è 数据库,性能很差,数据的实时性特别差,没人用,这个操作由SUN自己提供,所有的JDBC技术都是最新版本;
· JDBC连接:由各个数据库生产商提供数据库驱动程序的实现,用户需要单独配置开发包(*.jar);
|- 流程:程序 è JDBC è 数据库,直接操作,性能高;
· JDBC网络连接:由程序根据网络协议连接到指定的数据库上,这个驱动程序依然由数据库生产商提供;
在以后的开发之中主要使用JDBC网络连接。
在Java之中,由于JDBC属于固定的服务,所以其代码的操作流程完全一样:
· 第一步:加载数据库驱动程序;
· 第二步:通过DriverManager类根据指定的属性内容连接数据库;
· 第三步:进行数据表的CRUD操作,执行SQL;
· 第四步:关闭数据库连接。
如果现在要使用Oracle进行连接,那么对于Oracle数据库就必须启动以下两个服务:
· 监听服务:OracleOraDb 10g _home1TNSListener;
· 数据库实例服务:OracleServiceMLDN。
如果监听服务有问题,那么程序根本就无法连接数据库。当这两个服务已经正常启动之后,下面就需要配置Oracle的驱动程序了。对于Oracle或DB2这样的大型数据库,一般都会在安装目录下存在了数据库的驱动程序开发包,Oracle的驱动程序路径:D:\oracle\product\ 10.1.0 \db_1\jdbc\lib\classes12.jar。
对于这个jar包的配置需要分两种情况:
情况一:如果现在使用命令行方式开发(不用开发工具),则这个jar包要在CLASSPATH属性之中进行配置。
情况二:如果现在使用的是Eclipse进行开发,这个时候CLASSPATH是不起作用的,必须在项目属性之中配置指定库文件的开发包。
配置流程:项目点右键 è 属性 è Java Build Path è Add External Jars。
如果要想进行连接,那么首先需要知道如下的几个信息(背):
· 数据库的驱动程序路径(配置的*.jar):oracle.jdbc.driver.OracleDriver;
· 数据库的连接地址:jdbc:oracle:thin:@主机名称:端口号:数据库的实例名称;
|- 要连接本机的mldn数据库:jdbc:oracle:thin:@localhost:1521:mldn;
· 数据库的用户名:scott;
· 数据库的密码:tiger。
不同的数据库以上的信息是不一样的,但是至少背一个,应付面试去。清楚了以上的操作之后,下面来逐步分析如何实现之前所说的JDBC的每一步操作:
第一步:加载数据库驱动程序;
数据库驱动程序给出的是“包.类”,只要看见写出了完整的类名称都应该想到反射,所以驱动程序的加载是通过Class类完成的:Class.forName(驱动程序);
第二步:通过DriverManager类根据指定的属性内容连接数据库;
java.sql.DriverManager是一个负责取得数据库连接接口(Connection)对象的操作类,在这个类之中定义了如下一个可以取得连接的方法:public static Connection getConnection(String url, String user, String password) throws SQLException;
第四步:关闭数据库连接。
在Connection接口中定义了close()方法:public void close() throws SQLException。
范例:连接mldn数据库
package cn.mldn.demo; import java.sql.Connection; import java.sql.DriverManager; public class TestDemo { public static final String DBDRIVER = "oracle.jdbc.driver.OracleDriver"; public static final String DBURL = "jdbc:oracle:thin:@localhost:1521:mldn"; public static final String DBUSER = "scott"; public static final String PASSWORD = "tiger"; public static void main(String[] args) throws Exception { Connection conn = null; // 每一个Connection对象表示一个数据库连接 Class.forName(DBDRIVER); // 加载驱动程序 conn = DriverManager.getConnection(DBURL, DBUSER, PASSWORD); System.out.println(conn); // 如果可以输出对象则表示连接成功 conn.close() ; // 关闭连接 } } |
对于日后不同的数据而言,除了驱动程序、连接地址、用户名和密码不同之外,其余的所有代码都一样。
JDBC的设计就属于一个工厂设计模式,其中的DriverManager类负责取得Connection接口的实例化对象。
在正常情况下以上的程序肯定是可以连接数据库,但是也会有一些个别份子由于自己电脑上经常安装一些不正当的软件会造成连接失败,主要的常见问题有二:
问题一:监听问题无法启动
那么这种情况下,如果要想解决此问题,首先要确定一下监听程序的路径是否正确。
现在已经配置的路径:D:\oracle\product\ 10.1.0 \db_1\BIN\TNSLSNR,如果执行程序的路径正确,那么下面可以使用一个Oracle的工具通过命令行来控制监听启动:lsnrctl,现在就有可能是网络的配置环境出现了问题。
为了解决这个问题,现在进入到网络配置路径:D:\oracle\product\ 10.1.0 \db_1\NETWORK\ADMIN路径下有两个文件:tnsnames.ora、listener.ora,如果这两个文件的网络配置出错,那么基本上监听服务就Over了。将网络名称修改为主机名称一样的数值。
问题二:缺少SID,从而导致无法连接
Exception in thread "main" java.sql.SQLException: Io 异常: The Network Adapter could not establish the connection |
这个是因为有些电脑安装了Oracle之后,有可能安装过程之中出现了一些问题,导致SID没有配置成功。
这个时候,可以启动Net Manager程序,通过这个程序注册一个与当前数据库实例名称相同的SID,保存后即可连接。
如果现在要进行数据库的操作,那么一定需要数据表,所以首先编写一个如下的数据库创建脚本:
DROP SEQUENCE myseq ; DROP TABLE member PURGE ; CREATE SEQUENCE myseq ; CREATE TABLE member( mid NUMBER , name VARCHAR2(20) NOT NULL , age NUMBER(3) , birthday DATE , note CLOB , CONSTRAINT pk_mid PRIMARY KEY (mid) , CONSTRAINT ck_age CHECK (age BETWEEN 0 AND 200) ) ; |
如果要想执行数据表操作在Java之中可以使用Statement接口完成,但是如果要想取得Statement接口对象的话,必须依靠Connection接口完成,在Connection接口中定义了如下的一个操作方法:
· 取得Statement接口对象:public Statement createStatement() throws SQLException;
当取得了Statement接口对象之后就可以进行数据表的CRUD操作了,而操作的方法:
· 执行数据更新操作:public int executeUpdate(String sql) throws SQLException,返回更新的行数;
· 执行数据查询操作:public ResultSet executeQuery(String sql) throws SQLException;
· 关闭操作:public void close() throws SQLException。
数据更新就是三种操作语法:增加、修改、删除。
范例:执行数据的增加操作
· 增加的SQL语法:INSERT INTO 表名称 (字段,字段,..) VALUES (值,值,…);
package cn.mldn.demo; import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; public class TestDemo { public static final String DBDRIVER = "oracle.jdbc.driver.OracleDriver"; public static final String DBURL = "jdbc:oracle:thin:@localhost:1521:mldn"; public static final String DBUSER = "scott"; public static final String PASSWORD = "tiger"; public static void main(String[] args) throws Exception { Connection conn = null; // 每一个Connection对象表示一个数据库连接 Statement stmt = null; // 数据库操作对象 Class.forName(DBDRIVER); // 加载驱动程序 conn = DriverManager.getConnection(DBURL, DBUSER, PASSWORD); stmt = conn.createStatement(); // 找到Statement接口对象 String sql = "INSERT INTO member (mid,name,age,birthday,note) VALUES (myseq.nextval,'张三',20,SYSDATE,'是世界末日的一个人')"; int len = stmt.executeUpdate(sql); System.out.println("更新行数:" + len); stmt.close(); // 关闭操作,如果不关闭操作,关闭连接也是一样的 conn.close(); // 关闭连接 } } |
范例:执行修改操作
· 修改的SQL语法:UPDATE 表名称 SET 字段=值,字段=值,.. [WHERE 更新条件(s)];
package cn.mldn.demo; import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; public class TestDemo { public static final String DBDRIVER = "oracle.jdbc.driver.OracleDriver"; public static final String DBURL = "jdbc:oracle:thin:@localhost:1521:mldn"; public static final String DBUSER = "scott"; public static final String PASSWORD = "tiger"; public static void main(String[] args) throws Exception { Connection conn = null; // 每一个Connection对象表示一个数据库连接 Statement stmt = null; // 数据库操作对象 Class.forName(DBDRIVER); // 加载驱动程序 conn = DriverManager.getConnection(DBURL, DBUSER, PASSWORD); stmt = conn.createStatement(); // 找到Statement接口对象 String sql = "UPDATE member SET name='李四',age=19,note='扫除达人' WHERE mid BETWEEN 10 AND 20"; int len = stmt.executeUpdate(sql); System.out.println("更新行数:" + len); stmt.close(); // 关闭操作,如果不关闭操作,关闭连接也是一样的 conn.close(); // 关闭连接 } } |
范例:执行删除操作
· 删除的SQL语法:DELETE FROM 表名称 WHERE 删除条件 ;
package cn.mldn.demo; import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; public class TestDemo { public static final String DBDRIVER = "oracle.jdbc.driver.OracleDriver"; public static final String DBURL = "jdbc:oracle:thin:@localhost:1521:mldn"; public static final String DBUSER = "scott"; public static final String PASSWORD = "tiger"; public static void main(String[] args) throws Exception { Connection conn = null; // 每一个Connection对象表示一个数据库连接 Statement stmt = null; // 数据库操作对象 Class.forName(DBDRIVER); // 加载驱动程序 conn = DriverManager.getConnection(DBURL, DBUSER, PASSWORD); stmt = conn.createStatement(); // 找到Statement接口对象 String sql = "DELETE FROM member WHERE mid IN (1,11,21)"; int len = stmt.executeUpdate(sql); System.out.println("更新行数:" + len); stmt.close(); // 关闭操作,如果不关闭操作,关闭连接也是一样的 conn.close(); // 关闭连接 } } |
在数据的更新操作之中,如果已经成功的执行了数据的更新,一定会返回更新的行数的。
更新操作会返回给用户更新的行数,那么查询更应该返回数据给用户浏览,所以在整个进行查询的操作之中,就必须有一种结构,可以装下整个的查询结果,而这个结构就使用ResultSet表示。
当所有的记录返回到ResultSet的时候,所有的内容都是按照数据类型存放的,所以用户只需要按照数据类型一行行的取出数据即可,在ResultSet接口之中定义了这样的几个方法:
· 移动指针并判断是否有数据:public boolean next() throws SQLException;
· 取得指定类型的数据:public 数据 getXxx(列的标记) throws SQLException
· 关闭:public void close() throws SQLException。
范例:使用ResultSet取出数据
package cn.mldn.demo; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import java.util.Date; public class TestDemo { public static final String DBDRIVER = "oracle.jdbc.driver.OracleDriver"; public static final String DBURL = "jdbc:oracle:thin:@localhost:1521:mldn"; public static final String DBUSER = "scott"; public static final String PASSWORD = "tiger"; public static void main(String[] args) throws Exception { Connection conn = null; // 每一个Connection对象表示一个数据库连接 Statement stmt = null; // 数据库操作对象 Class.forName(DBDRIVER); // 加载驱动程序 conn = DriverManager.getConnection(DBURL, DBUSER, PASSWORD); stmt = conn.createStatement(); // 找到Statement接口对象 String sql = "SELECT mid,name,age,birthday,note FROM member"; ResultSet rs = stmt.executeQuery(sql) ; // 查询 while (rs.next()) { int mid = rs.getInt("mid") ; String name = rs.getString("name") ; int age = rs.getInt("age") ; Date birthday = rs.getDate("birthday") ; String note = rs.getString("note") ; System.out.println(mid + "," + name + "," + age + "," + birthday + "," + note); } rs.close() ; stmt.close(); // 关闭操作,如果不关闭操作,关闭连接也是一样的 conn.close(); // 关闭连接 } } |
从今天开始,所有的SQL语句的查询之中不允许再出现“SELECT *…”,必须明确的写出要显示字段名称。但是,在编写SQL语句的时候,既然已经明确的知道了要显示列的顺序,所以也可以采用序号的形式在ResultSet中使用。
while (rs.next()) { int mid = rs.getInt(1) ; String name = rs.getString(2) ; int age = rs.getInt(3) ; Date birthday = rs.getDate(4) ; String note = rs.getString(5) ; System.out.println(mid + "," + name + "," + age + "," + birthday + "," + note); } |
这种方式更加的简单,所以以后都采用此方式完成。
之前所讲解的Statement基本上不用,即:可以不会,下面首先来解释一下之所以不去使用Statement操作的原因,假设:现在要增加的数据为用户自己输入的。
package cn.mldn.demo; import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; public class TestDemo { public static final String DBDRIVER = "oracle.jdbc.driver.OracleDriver"; public static final String DBURL = "jdbc:oracle:thin:@localhost:1521:mldn"; public static final String DBUSER = "scott"; public static final String PASSWORD = "tiger"; public static void main(String[] args) throws Exception { String name = "Mr'Smith" ; String birthday = " 1989-01-02 " ; int age = 30 ; String note = "终于来了个人" ; Connection conn = null; // 每一个Connection对象表示一个数据库连接 Statement stmt = null; // 数据库操作对象 Class.forName(DBDRIVER); // 加载驱动程序 conn = DriverManager.getConnection(DBURL, DBUSER, PASSWORD); stmt = conn.createStatement(); // 找到Statement接口对象 String sql = "INSERT INTO member (mid,name,age,birthday,note) VALUES (myseq.nextval,'" + name + "'," + age + ",TO_DATE('" + birthday + "','yyyy-mm-dd'),'" + note + "')"; System.out.println(sql); int len = stmt.executeUpdate(sql); System.out.println("更新行数:" + len); stmt.close(); // 关闭操作,如果不关闭操作,关闭连接也是一样的 conn.close(); // 关闭连接 } } |
本程序一执行之后,会出现如下的错误提示信息:
INSERT INTO member (mid,name,age,birthday,note) VALUES (myseq.nextval,'Mr'Smith',30,TO_DATE(' 1989-01-02 ','yyyy-mm-dd'),'终于来了个人') Exception in thread "main" java.sql.SQLException: ORA-00917: missing comma |
在日后的所有开发之中,如果出现了SQLException不用去检查代码,就检查SQL语句是不是有问题,在SQL语句之中所有的字符串要求使用“'”定义,但是现在名字上有了“'”,那么自然SQL语句就出现了错误,所以导致程序无法进行数据的保存。所以通过这一代码就可以发现,在开发之中,这种拼凑SQL语句的形式根本就不可能被使用,即:Statement不应该被使用,所有的开发都会使用Statement的子接口:PreparedStatement完成。
PreparedStatement采用的是一种预处理的方式来进行程序编写的,所谓的预处理指的是先在数据库之中执行好要操作的SQL语句,但是其对应的内容暂时不插入,通过程序再依次设置。
如果要想取得PreparedStatement接口的对象依然需要通过Connection接口完成,在Connection接口定义的方法:
· 取得PreparedStatement接口对象:public PreparedStatement prepareStatement(String sql) throws SQLException
当取得了PreparedStatement接口对象之后,就可以通过PreparedStatement定义的方法进行数据库操作:
· 更新操作:public int executeUpdate() throws SQLException;
· 查询操作:public ResultSet executeQuery() throws SQLException。
如果现在要为操作的SQL设置内容,使用的方法:
· 根据索引设置各种类型:public void setBigDecimal(int parameterIndex, 数据类型 x) throws SQLException
在使用PreparedStatement接口中的setDate()方法的时候,其对应的类型是java.sql.Date,而不是平常使用的java.util.Date,那么就必须执行转换,在java.sql.Date类中有一个构造:public Date(long date)。
范例:通过PreparedStatement执行增加操作
package cn.mldn.demo; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.util.Date; public class TestDemo { public static final String DBDRIVER = "oracle.jdbc.driver.OracleDriver"; public static final String DBURL = "jdbc:oracle:thin:@localhost:1521:mldn"; public static final String DBUSER = "scott"; public static final String PASSWORD = "tiger"; public static void main(String[] args) throws Exception { String name = "Mr'Smith" ; Date birthday = new Date() ; // 表示的是日期 int age = 30 ; String note = "终于来了个人" ; String sql = "INSERT INTO member (mid,name,age,birthday,note) VALUES (myseq.nextval,?,?,?,?)"; Connection conn = null; // 每一个Connection对象表示一个数据库连接 PreparedStatement pstmt = null; // 数据库操作对象 Class.forName(DBDRIVER); // 加载驱动程序 conn = DriverManager.getConnection(DBURL, DBUSER, PASSWORD); pstmt = conn.prepareStatement(sql) ; // 执行了SQL pstmt.setString(1, name) ; pstmt.setInt(2,age) ; pstmt.setDate(3,new java.sql.Date(birthday.getTime())) ; pstmt.setString(4, note) ; int len = pstmt.executeUpdate() ; // 执行更新操作 System.out.println("更新行数:" + len); pstmt.close(); // 关闭操作,如果不关闭操作,关闭连接也是一样的 conn.close(); // 关闭连接 } } |
正因为PreparedStatement使用起来要比Statement方便,所以在日后的开发之中,全部都使用PreparedStatement,而不要使用Statement接口操作。
更新的操作都和增加操作类似,而最麻烦的就在于数据的查询操作,下面编写几个程序,这几个程序是必须会的操作,直接和后续的代码开发有联系。
范例:查询全部操作
package cn.mldn.demo; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Date; public class TestDemo { public static final String DBDRIVER = "oracle.jdbc.driver.OracleDriver"; public static final String DBURL = "jdbc:oracle:thin:@localhost:1521:mldn"; public static final String DBUSER = "scott"; public static final String PASSWORD = "tiger"; public static void main(String[] args) throws Exception { String sql = "SELECT mid,name,age,birthday,note FROM member"; Connection conn = null; // 每一个Connection对象表示一个数据库连接 PreparedStatement pstmt = null; // 数据库操作对象 Class.forName(DBDRIVER); // 加载驱动程序 conn = DriverManager.getConnection(DBURL, DBUSER, PASSWORD); pstmt = conn.prepareStatement(sql) ; // 执行了SQL ResultSet rs = pstmt.executeQuery() ; // 查询操作 while (rs.next()) { int mid = rs.getInt(1) ; String name = rs.getString(2) ; int age = rs.getInt(3) ; Date birthday = rs.getDate(4) ; String note = rs.getString(5) ; System.out.println(mid + "," + name + "," + age + "," + birthday + "," + note); } pstmt.close(); // 关闭操作,如果不关闭操作,关闭连接也是一样的 conn.close(); // 关闭连接 } } |
范例:设置按id查询,如果按照id查询只会返回一条数据,要么就不返回。
package cn.mldn.demo; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Date; public class TestDemo { public static final String DBDRIVER = "oracle.jdbc.driver.OracleDriver"; public static final String DBURL = "jdbc:oracle:thin:@localhost:1521:mldn"; public static final String DBUSER = "scott"; public static final String PASSWORD = "tiger"; public static void main(String[] args) throws Exception { String sql = "SELECT mid,name,age,birthday,note FROM member WHERE mid=?"; Connection conn = null; // 每一个Connection对象表示一个数据库连接 PreparedStatement pstmt = null; // 数据库操作对象 Class.forName(DBDRIVER); // 加载驱动程序 conn = DriverManager.getConnection(DBURL, DBUSER, PASSWORD); pstmt = conn.prepareStatement(sql) ; // 执行了SQL pstmt.setInt(1, 300) ; // 在执行之前设置内容 ResultSet rs = pstmt.executeQuery() ; // 查询操作 if (rs.next()) { // 查询出来就显示 int mid = rs.getInt(1) ; String name = rs.getString(2) ; int age = rs.getInt(3) ; Date birthday = rs.getDate(4) ; String note = rs.getString(5) ; System.out.println(mid + "," + name + "," + age + "," + birthday + "," + note); } else { System.out.println("没有查询结果!"); } pstmt.close(); // 关闭操作,如果不关闭操作,关闭连接也是一样的 conn.close(); // 关闭连接 } } |
在使用ResultSet接口操作的时候,如果有内容则next()才可以执行,如果没有查询结果,next()直接返回false。
范例:设置模糊查询
package cn.mldn.demo; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Date; public class TestDemo { public static final String DBDRIVER = "oracle.jdbc.driver.OracleDriver"; public static final String DBURL = "jdbc:oracle:thin:@localhost:1521:mldn"; public static final String DBUSER = "scott"; public static final String PASSWORD = "tiger"; public static void main(String[] args) throws Exception { String column = "name" ; String keyWord = "三" ; String sql = "SELECT mid,name,age,birthday,note FROM member WHERE " + column + " LIKE ?"; Connection conn = null; // 每一个Connection对象表示一个数据库连接 PreparedStatement pstmt = null; // 数据库操作对象 Class.forName(DBDRIVER); // 加载驱动程序 conn = DriverManager.getConnection(DBURL, DBUSER, PASSWORD); pstmt = conn.prepareStatement(sql) ; // 执行了SQL pstmt.setString(1, "%" + keyWord + "%"); // 在执行之前设置内容 ResultSet rs = pstmt.executeQuery() ; // 查询操作 while (rs.next()) { // 查询出来就显示 int mid = rs.getInt(1) ; String name = rs.getString(2) ; int age = rs.getInt(3) ; Date birthday = rs.getDate(4) ; String note = rs.getString(5) ; System.out.println(mid + "," + name + "," + age + "," + birthday + "," + note); } pstmt.close(); // 关闭操作,如果不关闭操作,关闭连接也是一样的 conn.close(); // 关闭连接 } } |
范例:分页显示,Oracle使用ROWNUM完成
package cn.mldn.demo; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Date; public class TestDemo { public static final String DBDRIVER = "oracle.jdbc.driver.OracleDriver"; public static final String DBURL = "jdbc:oracle:thin:@localhost:1521:mldn"; public static final String DBUSER = "scott"; public static final String PASSWORD = "tiger"; public static void main(String[] args) throws Exception { String column = "name" ; String keyWord = "三" ; int currentPage = 2 ; // 当前所在页 int lineSize = 5 ; // 每页显示的长度 String sql = " SELECT * FROM ( " + " SELECT mid,name,age,birthday,note,ROWNUM rn FROM member WHERE " + column + " LIKE ? AND ROWNUM<=?) temp " + " WHERE temp.rn>? "; Connection conn = null; // 每一个Connection对象表示一个数据库连接 PreparedStatement pstmt = null; // 数据库操作对象 Class.forName(DBDRIVER); // 加载驱动程序 conn = DriverManager.getConnection(DBURL, DBUSER, PASSWORD); pstmt = conn.prepareStatement(sql) ; // 执行了SQL pstmt.setString(1, "%" + keyWord + "%"); // 在执行之前设置内容 pstmt.setInt(2, currentPage * lineSize) ; pstmt.setInt(3, (currentPage - 1) * lineSize); ResultSet rs = pstmt.executeQuery() ; // 查询操作 while (rs.next()) { // 查询出来就显示 int mid = rs.getInt(1) ; String name = rs.getString(2) ; int age = rs.getInt(3) ; Date birthday = rs.getDate(4) ; String note = rs.getString(5) ; System.out.println(mid + "," + name + "," + age + "," + birthday + "," + note); } pstmt.close(); // 关闭操作,如果不关闭操作,关闭连接也是一样的 conn.close(); // 关闭连接 } } |
如果这个时候设置的关键字为空字符串(String keyWord = "" ;)的话呢,则表示查询全部。
范例:统计查询,统计数据量,统计函数使用COUNT()完成。
package cn.mldn.demo; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; public class TestDemo { public static final String DBDRIVER = "oracle.jdbc.driver.OracleDriver"; public static final String DBURL = "jdbc:oracle:thin:@localhost:1521:mldn"; public static final String DBUSER = "scott"; public static final String PASSWORD = "tiger"; public static void main(String[] args) throws Exception { String column = "name" ; String keyWord = "" ; String sql = " SELECT COUNT(mid) FROM member WHERE " + column + " LIKE ? "; Connection conn = null; // 每一个Connection对象表示一个数据库连接 PreparedStatement pstmt = null; // 数据库操作对象 Class.forName(DBDRIVER); // 加载驱动程序 conn = DriverManager.getConnection(DBURL, DBUSER, PASSWORD); pstmt = conn.prepareStatement(sql) ; // 执行了SQL pstmt.setString(1, "%" + keyWord + "%"); // 在执行之前设置内容 ResultSet rs = pstmt.executeQuery() ; // 查询操作 if (rs.next()) { // 一定会有内容返回 int count = rs.getInt(1) ; System.out.println(count); } pstmt.close(); // 关闭操作,如果不关闭操作,关闭连接也是一样的 conn.close(); // 关闭连接 } } |
以上的几种就是JDBC在实际开发之中的基本操作,也是必须掌握的操作。
以上的一些代码是属于JDBC 1.0的时候就推出的功能,但是这些功能被新版本在一些bug进行了纠正。而最新的JDBC版本为4.0(没人使),因为从JDBC 2.0之后增加了一些新功能:可滚动结果集、使用ResultSet更新数据、批处理,这之中批处理操作是唯一有点用的技术:所谓的批处理指的是数据库可以一次性的执行多条SQL语句。所以在JDBC 2.0之后,对于Statement和PreparedStatement接口都有了一些新的方法:
· Statement接口增加了两个方法:
|- 增加一个执行的SQL:public void addBatch(String sql) throws SQLException,没有执行;
|- 一次性执行多条SQL:public int[] executeBatch() throws SQLException;
· PreparedStatement接口增加了一个方法:
|- 增加执行的SQL:public void addBatch() throws SQLException。
范例:为了方便起见,使用Statement执行一次批处理
package cn.mldn.demo; import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; import java.util.Arrays; public class TestDemo { public static final String DBDRIVER = "oracle.jdbc.driver.OracleDriver"; public static final String DBURL = "jdbc:oracle:thin:@localhost:1521:mldn"; public static final String DBUSER = "scott"; public static final String PASSWORD = "tiger"; public static void main(String[] args) throws Exception { Connection conn = null; // 每一个Connection对象表示一个数据库连接 Statement stmt = null ; Class.forName(DBDRIVER); // 加载驱动程序 conn = DriverManager.getConnection(DBURL, DBUSER, PASSWORD); stmt = conn.createStatement() ; stmt.addBatch("INSERT INTO member(mid,name) VALUES (myseq.nextval,'张三A')") ; stmt.addBatch("INSERT INTO member(mid,name) VALUES (myseq.nextval,'张三B')") ; stmt.addBatch("INSERT INTO member(mid,name) VALUES (myseq.nextval,'张三C')") ; stmt.addBatch("INSERT INTO member(mid,name) VALUES (myseq.nextval,'张三D')") ; stmt.addBatch("INSERT INTO member(mid,name) VALUES (myseq.nextval,'张三E')") ; int data [] = stmt.executeBatch() ; // 执行批处理 System.out.println(Arrays.toString(data)); conn.close(); // 关闭连接 } } |
但是在这里面有一个小问题了,如果假设以上的五条更新SQL都属于同一个业务:
· A、从罗先生帐户上转出5000W;
· B、在我的帐户上增加5000W; è 本步出错了
· C、交付转帐手续费50;
· D、交付个人所得税:10。
在JDBC下,所有的事务处理都是自动提交的,这就意味着只要执行了SQL语句,那么都会自动的提交数据库的事务,而如果要想解决之前的问题,就必须手工处理事务,所有的事务处理操作命令都在Connection接口之中定义了,有如下的三个支持事务操作的方法:
· 设置事务是否自动提交:public void setAutoCommit(boolean autoCommit) throws SQLException;
· 提交事务:public void commit() throws SQLException;
· 回滚事务:public void rollback() throws SQLException。
范例:手工控制事务
package cn.mldn.demo; import java.sql.Connection; import java.sql.DriverManager; import java.sql.Statement; import java.util.Arrays; public class TestDemo { public static final String DBDRIVER = "oracle.jdbc.driver.OracleDriver"; public static final String DBURL = "jdbc:oracle:thin:@localhost:1521:mldn"; public static final String DBUSER = "scott"; public static final String PASSWORD = "tiger"; public static void main(String[] args) throws Exception { Connection conn = null; // 每一个Connection对象表示一个数据库连接 Statement stmt = null ; Class.forName(DBDRIVER); // 加载驱动程序 conn = DriverManager.getConnection(DBURL, DBUSER, PASSWORD); conn.setAutoCommit(false) ; // 取消自动提交 stmt = conn.createStatement() ; try { stmt.addBatch("INSERT INTO member(mid,name) VALUES (myseq.nextval,'张三A')") ; stmt.addBatch("INSERT INTO member(mid,name) VALUES (myseq.nextval,'张三B')") ; stmt.addBatch("INSERT INTO member(mid,name) VALUES (myseq.nextval,'张三'C')") ; stmt.addBatch("INSERT INTO member(mid,name) VALUES (myseq.nextval,'张三D')") ; stmt.addBatch("INSERT INTO member(mid,name) VALUES (myseq.nextval,'张三E')") ; int data [] = stmt.executeBatch() ; // 执行批处理 System.out.println(Arrays.toString(data)); conn.commit() ; // 不出错提交 } catch (Exception e) { e.printStackTrace() ; conn.rollback() ;// 回滚事务 } conn.close(); // 关闭连接 } } |
但是这些手工的事务处理,日后实际上也不需要用户自己完成,因为有容器帮助。
1、 JDBC连接Oracle数据库;
2、 PreparedStatement接口实现数据的更新和查询操作。
1、 巩固一下Java的基本概念,尤其是面向对象;
2、 代码:
· 简单Java类和数据表映射;
· 动物园程序,换成List接口;
· 各个设计模式的组成:单例、工厂、代理;
· 异常处理的标准格式;
· 日期、基本类型、String的互相转换;
· 正则表达式;
· 文件拷贝、打印流、扫描流;
· JDBC的PreparedStatement操作。