JavaSE學習總結(十六)—— 泛型與泛型應用


一、泛型概要

泛型(Generic)的本質是類型參數化,通俗的說就是用一個占位符來表示類型,這個類型可以是String,Integer等不確定的類型,表明可接受的類型。

泛型是Java中一個非常重要的知識點,在Java集合類框架中泛型被廣泛應用。

1.1、為什么需要泛型

在數據結構中有一種結構叫:,它的特點是:先進后出,后進先出

如:放衣服的箱子,糖葫蘆

現在來模擬一個棧的數據結構

1.1.1、版本一(強類型)

package com.nf.math;

public class ObjUtil {
    public static void main(String[] args) {
        Stack stack=new Stack();
        stack.push(11);
        stack.push(22);
        stack.push(55);
        int data=stack.pop();
        System.out.println(data);
        System.out.println(stack.pop());
        System.out.println(stack.pop());
    }
}

class Stack{
    //用於存放數據的數組
    private int[] data=new int[10];
    //當前下標
    private int i=0;
    //進棧
    public void push(int obj){
        data[i++]=obj;
    }
    //出棧
    public int pop(){
        return data[--i];
    }
}

結果:

55
22
11

缺點是不通用

1.1.2、版本二(Object弱類型)

版本一有明顯的缺點,只允許存放int類型的數據,如果需要其它類型的數據怎么辦法?

有人提議重新創建不同類型的棧,這樣不好,因為如果需要10種不同類型的棧,則需定義10個,維護也麻煩。

使用Object也許可以解決問題,代碼如下:

package com.nf.math;

public class ObjUtil {
    public static void main(String[] args) {
        Stack stack=new Stack();
        stack.push(1.1);
        stack.push(2.2);
        stack.push(5.5);
        double data=(double)stack.pop();
        System.out.println(data);
        System.out.println(stack.pop());
        System.out.println(stack.pop());
    }
}

class Stack{
    //用於存放數據的數組
    private Object[] data=new Object[10];
    //當前下標
    private int i=0;
    //進棧
    public void push(Object obj){
        data[i++]=obj;
    }
    //出棧
    public Object pop(){
        return data[--i];
    }
}

結果:

5.5
2.2
1.1

缺點是安全隱患(類型轉換)

1.1.3、版本三(泛型)

版本二中存在類型的強制轉換,如果轉換的類型不匹配則會引起運行時異常,存在安全隱患,使用泛型可以解決該問題:

package com.nf.math;

public class ObjUtil {
    public static void main(String[] args) {
        Stack<Double> stack=new Stack<Double>();
        stack.push(1.1);
        stack.push(2.2);
        stack.push(5.5);
        double data=stack.pop();  //不需要拆箱,沒有類型轉換
        System.out.println(data);
        System.out.println(stack.pop());
        System.out.println(stack.pop());
    }
}

class Stack<T>{
    //用於存放數據的數組
    private T[] data=(T[])(new Object[10]);
    //當前下標
    private int i=0;
    //進棧
    public void push(T obj){
        data[i++]=obj;
    }
    //出棧
    public T pop(){
        return data[--i];
    }
}

結果:

5.5
2.2
1.1

因為使用了泛型,兼具了版本一與版本二的優點,沒有類型轉換,沒有安全隱患,可以適用多種不同的數據類型。

java不支持泛型數組,List或ArrayList具有泛型數組的功能

1.2、泛型的優點

沒有泛型的情況的下,通過對類型Object的引用來實現參數的“任意化”,“任意化”帶來的缺點是要做顯式的強制類型轉換,而這種轉換是要求開發者對實際參數類型可以預知的情況下進行的。對於強制類型轉換錯誤的情況,編譯器可能不提示錯誤,在運行的時候才出現異常,這是一個安全隱患。

泛型的好處是在編譯的時候檢查類型安全,並且所有的強制轉換都是自動和隱式的,以提高代碼的重用率。

1、提高程序的安全性和可靠性
使用類型更安全,指定具體類型后,Java編譯器會對錯誤的類型在編譯時被捕獲,而不是在運行時當作ClassCastException展示出來,從而提高程序的安全性和可靠性

2、消除強制類型轉換
例如在集合里使用泛型后,從集合里取出對象后就不需要再進行強制類型轉換了,這樣使編寫程序變得更簡單,更不容易出錯

3、提高代碼重用率
在一個類里要對不同結構類型的對象進行操作時,有的對象成員和方法的邏輯都是一樣的,就是類型不一樣,就有可能會造成不必要的代碼重復,通過使用泛型,只需要一個Java類就可以表示不同類型的對象,從而可以大大提高代碼的重用率

1.3、泛型規則

1、泛型的類型參數只能是類類型(包括自定義類),不能是簡單類型。

        Stack<Double> stack=new Stack<Double>();

2、同一種泛型可以對應多個版本(因為參數類型是不確定的),不同版本的泛型類實例是不兼容的

        Stack<Integer> stack2=stack;  //錯誤

3、泛型的類型參數可以有多個。

class Stack<T,E,K,M,X>{
}

4、泛型的參數類型可以使用extends語句,例如<T extends superclass>。習慣上稱為“有界類型”。

泛型約束,約束T的類型只能是superclass的子類型

5、泛型的參數類型還可以是通配符類型。例如Class<?> classType = Class.forName("java.lang.String");

1.4、Java中方法的參數

java中方法參數傳遞的都是值,與C#區別很大,沒有ref與out。

java中數據類型分為基本數據類型和引用數據類型。

  • 基本數據類型 
    • 整型:byte,short,int,long
    • 浮點型:float,double
    • 字符型:char
    • 布爾型:boolean
  • 引用數據類型 
    • 數組
    • 接口

方法的參數分為實際參數,和形式參數。

  • 形式參數:定義方法時寫的參數。
  • 實際參數:調用方法時寫的具體數值。

1.4.1、基本數據類型

package com.nf.math;

public class ObjUtil {
    public static void main(String[] args) {
        Util util=new Util();
        int n1=100,n2=200;
        util.Swap(n1, n2);  //副本
        System.out.println("n1="+n1+",n2="+n2);
    }
}

class Util
{
    public void Swap(int n1, int n2) {
        int temp = n1;
        n1 = n2;
        n2 = temp;
    }
}

結果:

n1=100,n2=200

從上面的結果可以看出n1與n2在調用交換方法后並沒有實質交換,是因為形參是n1與n2的副本。

1.4.2、引用類型

package com.nf.math;

public class ObjUtil {
    public static void main(String[] args) {
        Util util=new Util();
        String n1="100",n2="200";
        util.Swap(n1, n2);  //形參是n1的引用副本
        System.out.println("n1="+n1+",n2="+n2);
    }
}

class Util
{
    public void Swap(String n1, String n2) {
        String temp = n1;
        n1 = n2;
        n2 = temp;
    }
}

結果:

n1=100,n2=200

依然沒有交互

String對象做為參數傳遞時,走的依然是引用傳遞,只不過String這個類比較特殊。

String對象一旦創建,內容不可更改。每一次內容的更改都是重現創建出來的新對象。

1.4.3、結論

  • 值傳遞的時候,將實參的,copy一份給形參。
  • 引用傳遞的時候,將實參的地址值,copy一份給形參。

也就是說,不管是值傳遞還是引用傳遞,形參拿到的僅僅是實參的副本,而不是實參本身。

二、自定義泛型類

 示例:

package com.nf.math;

public class BoxTest {
    public static void main(String[] args) {
        Box<Integer> boxInt = new Box<Integer>();
        boxInt.setAttr(5);
        System.out.println(boxInt.getAttr() + 1);
    }
}

class Box<T> {
    private T attr;

    public T getAttr() {
        return this.attr;
    }

    public void setAttr(T attr) {
        this.attr = attr;
    }
}

結果:

6

三、自定義泛型方法

在自定義泛型類中,整個類都可以使用類型占位T,有時候只需要局部用到則可以定義泛型方法

定義泛型方法,語法如下:

[訪問修飾符] <泛型列表> 返回值 方法名(參數列表) {
    //方法體;
}

示例:

package com.nf.math;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class BoxTest {
    public static void main(String[] args) {
        Box<Integer> boxInt = new Box<Integer>();
        boxInt.setAttr(5);
        System.out.println(boxInt.getAttr() + 1);
        
        ReflectUtil.getMethods(Box.class);
        
    }
}

class Box<T> {
    private T attr;

    public T getAttr() {
        return this.attr;
    }

    public void setAttr(T attr) {
        this.attr = attr;
    }
}

class ReflectUtil
{
    public static <T> void getMethods(Class<T> type){
        Method[] methods=type.getMethods();
        System.out.println(type.getName()+":");
        for (Method method : methods) {
            System.out.println(Modifier.toString(method.getModifiers())+" "+method.getReturnType()+"  "+method.getName()+"()");
        }
    }
}

結果:

說明:

1)在泛型列表中聲明的泛型,可用於該方法的返回值類型聲明、參數類型聲明和方法代碼中的局部變量的類型聲明

2)類中其他方法不能使用當前方法聲明的泛型

3)使用泛型方法時,不必指明參數類型,編譯器會自己找出具體的類型;泛型方法除了定義不同,調用就像普通方法一樣。

注意:是否擁有泛型方法,與其所在的類是否泛型沒有關系。要定義泛型方法,只需將泛型參數列表置於返回值前。

四、通配符與泛型約束

4.1、類型通配符

類型通配符一般是使用 ? 代替具體的類型實參。

package com.nf.math;

public class BeanTest {
    public static void main(String[] args) {
        // 同一種泛型可以對應多個版本(因為參數類型是不確定的)
        // 不同版本的泛型類實例是不兼容的。
        Bean<String> bean1 = new Bean<String>();
        Bean<Integer> bean2 = new Bean<Integer>();
        System.out.println(bean1.getClass().getName()); // 類型
        System.out.println(bean2.getClass().getName());
        Bean<Number> bean3 = bean1;
    }
}

class Bean<T> {
    private T var;

    public T getVar() {
        return var;
    }

    public void setVar(T var) {
        this.var = var;
    }

    @Override
    public String toString() {
        return var.toString();
    }
}

運行結果:

示例

package com.nf.math;

public class BeanTest {
    public static void main(String[] args) {
        // 同一種泛型可以對應多個版本(因為參數類型是不確定的)
        // 不同版本的泛型類實例是不兼容的。
        Bean<String> bean1 = new Bean<String>();
        bean1.setVar("one");
        Bean<Integer> bean2 = new Bean<Integer>();
        bean2.setVar(2);
        System.out.println(bean1.getClass().getName()); // 類型
        System.out.println(bean2.getClass().getName());
        Bean<?> bean3 = bean1;
        
        Show(bean1);
        //Show(bean2);錯誤
        Display(bean1);
        Display(bean2);
    }
    
    public static void Show(Bean<String> p){
        System.out.println("內容是:"+p.getVar());
    }
    public static void Display(Bean<?> p){
        System.out.println("內容是:"+p);
    }
}

class Bean<T> {
    private T var;

    public T getVar() {
        return var;
    }

    public void setVar(T var) {
        this.var = var;
    }

    @Override
    public String toString() {
        return var.toString();
    }
}

結果:

com.nf.math.Bean
com.nf.math.Bean
內容是:one
內容是:one
內容是:2

 示例: 

class Info<T>{  
    private T var ;     // 定義泛型變量  
    public void setVar(T var){  
        this.var = var ;  
    }  
    public T getVar(){  
        return this.var ;  
    }  
    public String toString(){   // 直接打印  
        return this.var.toString() ;  
    }  
};  
public class GenericsDemo{  
    public static void main(String args[]){  
        Info<String> i = new Info<String>() ;       // 使用String為泛型類型  
        i.setVar("it") ;                            // 設置內容  
        fun(i) ;  
    }  
    public static void fun(Info<?> temp){     // 可以接收任意的泛型對象  
        System.out.println("內容:" + temp) ;  
    }  
};  

4.2、 上界

4.2.1、通配符上界

類型通配符上限通過形如Stack<? extends Number>形式定義

class Info<T>{  
    private T var ;     // 定義泛型變量  
    public void setVar(T var){  
        this.var = var ;  
    }  
    public T getVar(){  
        return this.var ;  
    }  
    public String toString(){   // 直接打印  
        return this.var.toString() ;  
    }  
};  
public class GenericsDemo17{  
    public static void main(String args[]){  
        Info<Integer> i1 = new Info<Integer>() ;        // 聲明Integer的泛型對象  
        Info<Float> i2 = new Info<Float>() ;            // 聲明Float的泛型對象  
        i1.setVar(30) ;                                 // 設置整數,自動裝箱  
        i2.setVar(30.1f) ;                              // 設置小數,自動裝箱  
        fun(i1) ;  
        fun(i2) ;  
    }  
    public static void fun(Info<? extends Number> temp){  // 只能接收Number及其Number的子類  
        System.out.print(temp + "、") ;  
    }  
};  

 示例

package com.nf.math;

public class BeanTest {
    public static void main(String[] args) {
        Bean<String> bean1 = new Bean<String>();
        bean1.setVar("one");
        //public final class Integer extends Number implements Comparable<Integer> {
        Bean<Integer> bean2 = new Bean<Integer>();
        bean2.setVar(2);
        Bean<Number> bean3 = new Bean<Number>();
        bean3.setVar(3);
        
        //Display(bean1);  錯誤,原因是T必須繼承Number或就是Number類型
        Display(bean2);
        Display(bean3);
    }
    
    public static void Display(Bean<? extends Number> p){
        System.out.println("內容是:"+p);
    }
}

class Bean<T> {
    private T var;

    public T getVar() {
        return var;
    }

    public void setVar(T var) {
        this.var = var;
    }

    @Override
    public String toString() {
        return var.toString();
    }
}

結果

內容是:2
內容是:3

4.2.2、占位符上界

package com.nf.math;

public class BeanTest {
    public static void main(String[] args) {
        Bean<String> bean1 = new Bean<String>();  //錯誤,因為T有上界,要求是Number或Number的子類
        //public final class Integer extends Number implements Comparable<Integer> 
        Bean<Integer> bean2 = new Bean<Integer>();
        Bean<Number> bean3 = new Bean<Number>();
    }
}

class Bean<T extends Number> {  //T必須繼承Number或就是Number
    private T var;

    public T getVar() {
        return var;
    }

    public void setVar(T var) {
        this.var = var;
    }

    @Override
    public String toString() {
        return var.toString();
    }
}

4.2.3、多種限制

class C<T extends Comparable<? super T> & Serializable> 

我們來分析以下這句,T extends Comparable這個是對上限的限制,Comparable< super T>這個是下限的限制,Serializable是第2個上限。一個指定的類型參數可以具有一個或多個上限。具有多重限制的類型參數可以用於訪問它的每個限制的方法和域。 

class Bean<T extends Number & Serializable> 

4.3、下界

4.3.1、通配符下界

類型通配符下限為Stack<? super Number>形式,其含義與類型通配符上限正好相反

class Info<T>{  
    private T var ;     // 定義泛型變量  
    public void setVar(T var){  
        this.var = var ;  
    }  
    public T getVar(){  
        return this.var ;  
    }  
    public String toString(){   // 直接打印  
        return this.var.toString() ;  
    }  
};  
public class GenericsDemo21{  
    public static void main(String args[]){  
        Info<String> i1 = new Info<String>() ;      // 聲明String的泛型對象  
        Info<Object> i2 = new Info<Object>() ;      // 聲明Object的泛型對象  
        i1.setVar("hello") ;  
        i2.setVar(new Object()) ;  
        fun(i1) ;  
        fun(i2) ;  
    }  
    public static void fun(Info<? super String> temp){    // 只能接收String或Object類型的泛型  
        System.out.print(temp + "、") ;  
    }  
};  

 示例:

package com.nf.math;

import java.io.Serializable;

public class BeanTest {
    public static void main(String[] args) {
        Bean<String> bean1 = new Bean<String>();
        bean1.setVar("one");
        //public abstract class Number implements java.io.Serializable
        Bean<Integer> bean2 = new Bean<Integer>();
        bean2.setVar(2);
        Bean<Number> bean3 = new Bean<Number>();
        bean3.setVar(3);
        Bean<Object> bean4 = new Bean<Object>();
        bean4.setVar(4);
        Bean<Serializable> bean5 = new Bean<Serializable>();
        bean5.setVar("5");
        
        //Display(bean1);  //錯誤
        //Display(bean2); //錯誤
        Display(bean3);
        Display(bean4);
        Display(bean5);
    }
    
    //?必是Number或Number的父類,Object,Serializable,Number
    public static void Display(Bean<? super Number> p){
        System.out.println("內容是:"+p);
    }
}

class Bean<T> {
    private T var;

    public T getVar() {
        return var;
    }

    public void setVar(T var) {
        this.var = var;
    }

    @Override
    public String toString() {
        return var.toString();
    }
}

結果:

內容是:3
內容是:4
內容是:5

4.3.2、占位符下界

沒有,不存在...

五、類型擦除

5.1、類型擦除

Java中的泛型基本上都是在編譯器這個層次來實現的。在生成的Java字節代碼中是不包含泛型中的類型信息的。使用泛型的時候加上的類型參數,會被編譯器在編譯的時候去掉。這個過程就稱為類型擦除。如在代碼中定義的List<Object>和List<String>等類型,在編譯之后都會變成List。JVM看到的只是List,而由泛型附加的類型信息對JVM來說是不可見的。Java編譯器會在編譯時盡可能的發現可能出錯的地方,但是仍然無法避免在運行時刻出現類型轉換異常的情況。類型擦除也是Java的泛型實現方式與C++模板機制實現方式之間的重要區別。

Java 的泛型在編譯器有效,在運行期被刪除,也就是說所有泛型參數類型在編譯后都會被清除掉。

Java在泛型設計上是一種“偽泛型”,存在着泛型擦除。

類型擦除是Java中泛型的實現方式。泛型是在編譯器這個層次來實現的。在Java 源代碼中聲明的泛型類型信息,在編譯過程中會被擦除,只保留不帶類型參數的形式。被擦除的類型信息包括泛型類型和泛型方法聲明時的形式類型參數,以及參數化類型中的實際類型信息。經過類型擦除之后,包含泛型類型的代碼被轉換成不包含泛型類型的代碼,相當於回到了泛型被引入之前的形式,Java虛擬機在運行字節代碼時並不知道泛型類型的存在。 

定義好的泛型類:

public class ObjectHolder<T> {
    private T obj;
    public T getObject() { 
        return obj;
    }
    public void setObject(T obj) {
        this.obj = obj;
    }
}

被轉譯后:

以上面代碼中的ObjectHolder泛型類為例,經過類型擦除后,由於形式類型參數T沒有上界,T的所有出現將被替換成Object類型

public class ObjectHolder {
    private Object obj;
    public Object getObject() { 
        return obj;
    }
    public void setObject(Object obj) {
        this.obj = obj;
    }
}

另外使用ObjectHolder類的代碼也要進行處理,如下列代碼所示:

ObjectHolder<String> holder = new ObjectHolder<String>();
holder.setObject("Hello");
String str = holder.getObject();

在類型擦除后,ObjectHolder類中的getObject方法的返冋值類型實際上是 Object類型,因此需要添加強制類型轉換把getObject方法的返回值轉換成String類型。 這些類型轉換操作由編譯器自動添加。由於編譯器已經確保不允許使用除String類的對象之外的其他對象調用setObject方法,因此這個強制類型轉換操作始終是合法的,如下列代碼所示:

ObjectHolder holder = new ObjectHolder();
holder.setObject("Hello");
String str = (String)holder.getObject();

泛型信息只存在於代碼編譯階段,在進入 JVM 之前,與泛型相關的信息會被擦除掉,專業術語叫做類型擦除

示例:

package com.nf.math;

public class BeanTest {
    public static void main(String[] args) {
        Bean<Integer> bean1=new Bean<Integer>();
        Bean<String> bean2=new Bean<String>();
        
        System.out.println(bean1.getClass().getName());
        System.out.println(bean2.getClass().getName());
        
    }
}

class Bean<T> { 
    private T var;

    public T getVar() {
        return var;
    }

    public void setVar(T var) {
        this.var = var;
    }

    @Override
    public String toString() {
        return var.toString();
    }
}

結果:

com.nf.math.Bean
com.nf.math.Bean 

 在JVM中的 Class 都是com.nf.math.Bean,泛型信息被擦除了。

5.2、泛型轉譯

示例:

package com.nf.math;

import java.lang.reflect.Field;

public class BeanTest {
    public static void main(String[] args) throws Exception {
        Bean<Integer> bean1=new Bean<Integer>();
        Bean<String> bean2=new Bean<String>();
    
        //獲得Bean中的字段var
        Field clazz=bean1.getClass().getDeclaredField("var");
        clazz.setAccessible(true);
        clazz.set(bean1, "abc");
        //取字段var的類型
       System.out.println(clazz.getType().getSimpleName());
       System.out.println(bean1.getVar());
        
    }
}

class Bean<T> { 
    private T var;

    public T getVar() {
        return var;
    }

    public void setVar(T var) {
        this.var = var;
    }

    @Override
    public String toString() {
        return var.toString();
    }
}

 因為Bean沒有上界,T被轉譯成Object,生成如下代碼:

class BeanCopy { 
    private Object var;

    public Object getVar() {
        return var;
    }

    public void setVar(Object var) {
        this.var = var;
    }

    @Override
    public String toString() {
        return var.toString();
    }
} 

運行結果:

Object
abc

泛型類被類型擦除后,相應的類型就被替換成 Object 類型呢?,不一定,如果有上界,則被替換成上界:

package com.nf.math;

import java.lang.reflect.Field;

public class BeanTest {
    public static void main(String[] args) throws Exception {
        Bean<Integer> bean1=new Bean<Integer>();
        Bean<Number> bean2=new Bean<Number>();
    
        //獲得Bean中的字段var
        Field clazz=bean1.getClass().getDeclaredField("var");
        //查看運行時字段var的類型
       System.out.println(clazz.getType().getSimpleName());
    }
}

class Bean<T extends Number> {   //設置上界
    private T var;

    public T getVar() {
        return var;
    }

    public void setVar(T var) {
        this.var = var;
    }

    @Override
    public String toString() {
        return var.toString();
    }
}

運行結果:

Number

類型被轉譯成:

class Bean{ 
    private Number var;

    public Number getVar() {
        return var;
    }

    public void setVar(Number var) {
        this.var = var;
    }

    @Override
    public String toString() {
        return var.toString();
    }
}

六、泛型應用

6.1、泛型與反射簡化JDBCUtils工具類示例

參考:https://commons.apache.org/proper/commons-dbutils/

已經寫好的工具類:

package com.zhangguo.utils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class JDBCUtils {

    public static String DRIVER = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
    public static String URL = "jdbc:sqlserver://localhost:1433;databasename=pubs";
    public static String USER_NAME = "sa";
    public static String PASSWORD = "sa";

    static {
        try {
            Class.forName(DRIVER);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    private JDBCUtils() {

    }

    /**
     * Get connection
     * 
     * @return
     */
    public static Connection getconnnection() {
        Connection con = null;
        try {
            con = DriverManager.getConnection(URL, USER_NAME, PASSWORD);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return con;
    }

    /**
     * Close connection
     * 
     * @param rs
     * @param st
     * @param con
     */
    public static void close(ResultSet rs, Statement st, Connection con) {
        try {
            try {
                if (rs != null) {
                    rs.close();
                }
            } finally {
                try {
                    if (st != null) {
                        st.close();
                    }
                } finally {
                    if (con != null)
                        con.close();
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * Close connection
     * 
     * @param rs
     */
    public static void close(ResultSet rs) {
        Statement st = null;
        Connection con = null;
        try {
            try {
                if (rs != null) {
                    st = rs.getStatement();
                    rs.close();
                }
            } finally {
                try {
                    if (st != null) {
                        con = st.getConnection();
                        st.close();
                    }
                } finally {
                    if (con != null) {
                        con.close();
                    }
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * Close connection
     * 
     * @param st
     * @param con
     */
    public static void close(Statement st, Connection con) {
        try {
            try {
                if (st != null) {
                    st.close();
                }
            } finally {
                if (con != null)
                    con.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * insert/update/delete
     * 
     * @param sql
     * @param args
     * @return
     */
    public static int update(String sql, Object... args) {
        int result = 0;
        Connection con = getconnnection();
        PreparedStatement ps = null;
        try {
            ps = con.prepareStatement(sql);
            if (args != null) {
                for (int i = 0; i < args.length; i++) {
                    ps.setObject((i + 1), args[i]);
                }
            }
            result = ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(ps, con);
        }

        return result;
    }

    /**
     * query, because need to manually close the resource, so not recommended
     * for use it
     * 
     * @param sql
     * @param args
     * @return ResultSet
     */
    @Deprecated
    public static ResultSet query(String sql, Object... args) {
        ResultSet result = null;
        Connection con = getconnnection();
        PreparedStatement ps = null;
        try {
            ps = con.prepareStatement(sql);
            if (args != null) {
                for (int i = 0; i < args.length; i++) {
                    ps.setObject((i + 1), args[i]);
                }
            }
            result = ps.executeQuery();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * Query a single record
     * 
     * @param sql
     * @param args
     * @return Map<String,Object>
     */
    public static Map<String, Object> queryForMap(String sql, Object... args) {
        Map<String, Object> result = new HashMap<String, Object>();
        List<Map<String, Object>> list = queryForList(sql, args);
        if (list.size() > 0) {
            result = list.get(0);
        }
        return result;
    }

    /**
     * Query a single record
     * 
     * @param sql
     * @param args
     * @return <T>
     */
    public static <T> T queryForObject(String sql, Class<T> clz, Object... args) {
        T result = null;
        List<T> list = queryForList(sql, clz, args);
        if (list.size() > 0) {
            result = list.get(0);
        }
        return result;
    }

    /**
     * Query a single record
     * 
     * @param sql
     * @param args
     * @return List<Map<String,Object>>
     */
    public static List<Map<String, Object>> queryForList(String sql, Object... args) {
        List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
        Connection con = null;
        ResultSet rs = null;
        PreparedStatement ps = null;
        try {
            con = getconnnection();
            ps = con.prepareStatement(sql);
            if (args != null) {
                for (int i = 0; i < args.length; i++) {
                    ps.setObject((i + 1), args[i]);
                }
            }
            rs = ps.executeQuery();
            ResultSetMetaData rsmd = rs.getMetaData();
            int columnCount = rsmd.getColumnCount();
            while (rs.next()) {
                Map<String, Object> map = new HashMap<String, Object>();
                for (int i = 1; i <= columnCount; i++) {
                    map.put(rsmd.getColumnLabel(i), rs.getObject(i));
                }
                result.add(map);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close(rs, ps, con);
        }
        return result;
    }

    /**
     * Query a single record
     * 
     * @param sql
     * @param args
     * @return List<T>
     */
    public static <T> List<T> queryForList(String sql, Class<T> clz, Object... args) {
        List<T> result = new ArrayList<T>();
        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            con = getconnnection();
            ps = con.prepareStatement(sql);
            if (args != null) {
                for (int i = 0; i < args.length; i++) {
                    ps.setObject((i + 1), args[i]);
                }
            }
            rs = ps.executeQuery();
            ResultSetMetaData rsmd = rs.getMetaData();
            int columnCount = rsmd.getColumnCount();
            while (rs.next()) {
                T obj = clz.newInstance();
                for (int i = 1; i <= columnCount; i++) {
                    String columnName = rsmd.getColumnName(i);
                    String methodName = "set" + columnName.substring(0, 1).toUpperCase()
                            + columnName.substring(1, columnName.length());
                    Method method[] = clz.getMethods();
                    for (Method meth : method) {
                        if (methodName.equals(meth.getName())) {
                            meth.invoke(obj, rs.getObject(i));
                        }
                    }
                }
                result.add(obj);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } finally {
            close(rs, ps, con);
        }
        return result;
    }
}
View Code

數據庫:

CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '編號',
  `name` varchar(50) DEFAULT NULL COMMENT '姓名',
  `sex` varchar(20) DEFAULT NULL,
  `cno` varchar(50) DEFAULT NULL COMMENT '班級',
  `addr` varchar(50) DEFAULT NULL COMMENT '籍貫',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;

示例:

package com.zhangguo.utils;

import java.util.List;

public class JDBCUtilsTest {
    public static void main(String[] args) {
        List<Student> students = JDBCUtils.queryForList("select * from student where id<=?", Student.class, 10);
        for (Student student : students) {
            System.out.println(student);
        }
    }
}

/** 學生 */
class Student {
    private int id;
    private String name;
    private String sex;
    private String cno;
    private String addr;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getCno() {
        return cno;
    }

    public void setCno(String cno) {
        this.cno = cno;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

    @Override
    public String toString() {
        return "Student [id=" + id + ", name=" + name + ", sex=" + sex + ", cno=" + cno + ", addr=" + addr + "]";
    }
}

結果:

6.2、實現泛型與反射簡化JDBCUtils工具類

數據庫與表的元數據就是用於描述數據庫或表的信息,如表的字段(長度,類型,注釋),主鍵,外鍵,約束等信息,表中的數據本來是描述客觀事物的。

6.2.1、獲得表的元信息方法一

示例:

    /**
     * 根據數據庫的連接參數,獲取指定表的基本信息:字段名、字段類型、字段注釋
     *            表名
     * @return Map集合
     */
    public static List getTableInfo(String table) {
        List result = new ArrayList();

        Connection conn = null;
        DatabaseMetaData dbmd = null;

        try {
            conn = DriverManager.getConnection(URL, USER_NAME, PASSWORD);

            dbmd = conn.getMetaData();
            ResultSet resultSet = dbmd.getTables(null, "%", table, new String[] { "TABLE" });

            while (resultSet.next()) {
                String tableName = resultSet.getString("TABLE_NAME");
                System.out.println(tableName);

                if (tableName.equals(table)) {
                    ResultSet rs = conn.getMetaData().getColumns(null, getSchema(conn), tableName.toUpperCase(), "%");

                    while (rs.next()) {
                        // System.out.println("字段名:"+rs.getString("COLUMN_NAME")+"--字段注釋:"+rs.getString("REMARKS")+"--字段數據類型:"+rs.getString("TYPE_NAME"));
                        Map map = new HashMap();
                        String colName = rs.getString("COLUMN_NAME");
                        map.put("code", colName);

                        String remarks = rs.getString("REMARKS");
                        if (remarks == null || remarks.equals("")) {
                            remarks = colName;
                        }
                        map.put("name", remarks);

                        String dbType = rs.getString("TYPE_NAME");
                        map.put("dbType", dbType);

                        map.put("valueType", changeDbType(dbType));
                        result.add(map);
                    }
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        return result;
    }

    private static String changeDbType(String dbType) {
        dbType = dbType.toUpperCase();
        switch (dbType) {
        case "VARCHAR":
        case "VARCHAR2":
        case "CHAR":
            return "1";
        case "NUMBER":
        case "DECIMAL":
            return "4";
        case "INT":
        case "SMALLINT":
        case "INTEGER":
            return "2";
        case "BIGINT":
            return "6";
        case "DATETIME":
        case "TIMESTAMP":
        case "DATE":
            return "7";
        default:
            return "1";
        }
    }

    // 其他數據庫不需要這個方法 oracle和db2需要
    private static String getSchema(Connection conn) throws Exception {
        String schema;
        schema = conn.getMetaData().getUserName();
        if ((schema == null) || (schema.length() == 0)) {
            throw new Exception("ORACLE數據庫模式不允許為空");
        }
        return schema.toUpperCase().toString();
    }

結果:

persons
student
[{code=id, valueType=2, name=編號, dbType=INT}, {code=name, valueType=1, name=姓名, dbType=VARCHAR}, {code=sex, valueType=1, name=sex, dbType=VARCHAR}, {code=cno, valueType=1, name=班級, dbType=VARCHAR}, {code=addr, valueType=1, name=籍貫, dbType=VARCHAR}]
View Code

6.2.2、獲得表的元信息方法二

代碼:

package com.zhangguo.utils;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class DbUtils {
    public static String DRIVER = "com.mysql.jdbc.Driver";
    public static String URL = "jdbc:mysql://localhost:3306/studentmis?useUnicode=true&characterEncoding=utf8";
    public static String USER_NAME = "mysqluser";
    public static String PASSWORD = "root";

    static {
        try {
            Class.forName(DRIVER);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static List<Student> getStudents(String sql, Object... params) throws Exception {
        List<Student> result = new ArrayList<>();

        Connection conn = null;
        PreparedStatement pstm = null;

        conn = DriverManager.getConnection(URL, USER_NAME, PASSWORD);
        pstm = conn.prepareStatement(sql);
        // 添加參數
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                pstm.setObject(i + 1, params[i]);
            }
        }
        // 執行查詢
        ResultSet rs = pstm.executeQuery();
        
        //獲得表的元數據
        ResultSetMetaData rsmd=pstm.getMetaData();
        //表的列數
        int metaSize=rsmd.getColumnCount();
        for (int i =1; i <=metaSize; i++) {
            System.out.println("列名:"+rsmd.getColumnName(i));
            System.out.println("長度:"+rsmd.getColumnDisplaySize(i));
            System.out.println("類型:"+rsmd.getColumnType(i));
            System.out.println("類型:"+rsmd.getColumnTypeName(i));
            System.out.println("----------");
        }
        
        while (rs.next()) {
            Student student=new Student();
            
            student.setAddr(rs.getString("addr"));
            student.setCno(rs.getString("cno"));
            student.setId(rs.getInt("id"));
            student.setName(rs.getString("name"));
            student.setSex(rs.getString("sex"));
            
            result.add(student);
        }
        
        CloseConnection(conn, pstm, rs);
        return result;
    }

    public static void CloseConnection(Connection conn, PreparedStatement pstm, ResultSet rs) throws SQLException {
        if (rs != null) {
            rs.close();
        }
        if (pstm != null) {
            pstm.close();
        }
        if (conn != null) {
            conn.close();
        }
    }

}

測試類:

package com.zhangguo.utils;

import java.util.List;

public class JDBCUtilsTest {
    public static void main(String[] args) throws Exception {
        List<Student> students = JDBCUtils.queryForList("select * from student where id<=?", Student.class, 10);
        for (Student student : students) {
            System.out.println(student);
        }
        System.out.println("-------------------------------------------------------------------");
        List<Student> stus =DbUtils.getStudents("select * from student where id<=?", 5);
        for (Student student : stus) {
            System.out.println(student);
        }
        System.out.println("-------------------------------------------------------------------");
        System.out.println(stus);
    }
}

/** 學生 */
class Student {
    private int id;
    private String name;
    private String sex;
    private String cno;
    private String addr;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getCno() {
        return cno;
    }

    public void setCno(String cno) {
        this.cno = cno;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

    @Override
    public String toString() {
        return "Student [id=" + id + ", name=" + name + ", sex=" + sex + ", cno=" + cno + ", addr=" + addr + "]";
    }
}

結果:

Student [id=1, name=張學友, sex=男, cno=S1SJ90, addr=中國香港]
Student [id=2, name=黎明, sex=男, cno=S1SJ18, addr=中國上海]
Student [id=3, name=張慧妹, sex=女, cno=S2SJ19, addr=中國北京]
Student [id=4, name=張國立, sex=男, cno=S3J37, addr=中國廣東]
Student [id=5, name=張哪啦, sex=女, cno=S3SR96, addr=中國杭州]
Student [id=6, name=張鐵林, sex=男, cno=S2SJ140, addr=中國珠海]
Student [id=7, name=張國榮, sex=男, cno=S1SJ111, addr=中國香港]
Student [id=8, name=張果果, sex=女, cno=S3SU198, addr=中國深圳]
Student [id=10, name=張小軍, sex=女, cno=S1SN196, addr=中國斗門]
-------------------------------------------------------------------
列名:id
長度:11
類型:4
類型:INT
----------
列名:name
長度:50
類型:12
類型:VARCHAR
----------
列名:sex
長度:20
類型:12
類型:VARCHAR
----------
列名:cno
長度:50
類型:12
類型:VARCHAR
----------
列名:addr
長度:50
類型:12
類型:VARCHAR
----------
Student [id=1, name=張學友, sex=男, cno=S1SJ90, addr=中國香港]
Student [id=2, name=黎明, sex=男, cno=S1SJ18, addr=中國上海]
Student [id=3, name=張慧妹, sex=女, cno=S2SJ19, addr=中國北京]
Student [id=4, name=張國立, sex=男, cno=S3J37, addr=中國廣東]
Student [id=5, name=張哪啦, sex=女, cno=S3SR96, addr=中國杭州]
-------------------------------------------------------------------
[Student [id=1, name=張學友, sex=男, cno=S1SJ90, addr=中國香港], Student [id=2, name=黎明, sex=男, cno=S1SJ18, addr=中國上海], Student [id=3, name=張慧妹, sex=女, cno=S2SJ19, addr=中國北京], Student [id=4, name=張國立, sex=男, cno=S3J37, addr=中國廣東], Student [id=5, name=張哪啦, sex=女, cno=S3SR96, addr=中國杭州]]

6.3、封裝DbUtilis工具類

6.3.1.、查詢功能

package com.zhangguo.utilities;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class DbUtils {
    public static String DRIVER = "com.mysql.jdbc.Driver";
    public static String URL = "jdbc:mysql://localhost:3306/studentmis?useUnicode=true&characterEncoding=utf8";
    public static String USER_NAME = "mysqluser";
    public static String PASSWORD = "root";

    static {
        try {
            Class.forName(DRIVER);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static <T> List<T> queryList(String sql,Class<T> clazz,Object... params) throws Exception {
        List<T> result = new ArrayList<>();

        Connection conn = null;
        PreparedStatement pstm = null;

        conn = DriverManager.getConnection(URL, USER_NAME, PASSWORD);
        pstm = conn.prepareStatement(sql);
        // 添加參數
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                pstm.setObject(i + 1, params[i]);
            }
        }
        // 執行查詢
        ResultSet rs = pstm.executeQuery();

        // 獲得表的元數據
        ResultSetMetaData rsmd = pstm.getMetaData();
        // 表的列數
        int metaSize = rsmd.getColumnCount();

        while (rs.next()) {
            T entity = clazz.newInstance();
            
            for (int i = 1; i <= metaSize; i++) {
                //獲得當前列的列名
                String columnName=rsmd.getColumnName(i);
                //根據字段名獲得實體類中的字段
                Field field=clazz.getDeclaredField(columnName);
                //設置字段可以被訪問
                field.setAccessible(true);
                //給實體中的字段賦值
                field.set(entity, rs.getObject(columnName));
            }
            result.add(entity);
        }

        CloseConnection(conn, pstm, rs);
        return result;
    }

    public static void CloseConnection(Connection conn, PreparedStatement pstm, ResultSet rs) throws SQLException {
        if (rs != null) {
            rs.close();
        }
        if (pstm != null) {
            pstm.close();
        }
        if (conn != null) {
            conn.close();
        }
    }
}

測試:

package com.zhangguo.utilities;

import java.util.List;

public class DbUtilsTest {

    public static void main(String[] args) throws Exception {
        List<Animal> result = DbUtils.queryList("select * from animal where id<?", Animal.class, 5);
        System.out.println(result);
        List<Student> students = DbUtils.queryList("select * from student where id<?", Student.class, 5);
        System.out.println(students);
    }

}

/** 動物 */
class Animal {
    private int id;
    private String name;
    private String color;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Animal [id=" + id + ", name=" + name + ", color=" + color + "]";
    }
}


/** 學生 */
class Student {  //T只允許是String或String的子類
    private int id;
    private String name;
    private String sex;
    private String cno;
    private String addr;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getCno() {
        return cno;
    }

    public void setCno(String cno) {
        this.cno = cno;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

    @Override
    public String toString() {
        return "Student [id=" + id + ", name=" + name + ", sex=" + sex + ", cno=" + cno + ", addr=" + addr + "]";
    }
}

結果:

[Animal [id=1, name=Dog, color=yellow], Animal [id=2, name=Cat, color=White], Animal [id=3, name=Duck, color=Black]]
[Student [id=1, name=張學友, sex=男, cno=S1SJ90, addr=中國香港], Student [id=2, name=黎明, sex=男, cno=S1SJ18, addr=中國上海], Student [id=3, name=張慧妹, sex=女, cno=S2SJ19, addr=中國北京], Student [id=4, name=張國立, sex=男, cno=S3J37, addr=中國廣東]]
View Code

6.3.2、增刪改功能

package com.zhangguo.utilities;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class DbUtils {
    public static String DRIVER = "com.mysql.jdbc.Driver";
    public static String URL = "jdbc:mysql://localhost:3306/studentmis?useUnicode=true&characterEncoding=utf8";
    public static String USER_NAME = "mysqluser";
    public static String PASSWORD = "root";

    static {
        try {
            Class.forName(DRIVER);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static <T> List<T> queryList(String sql, Class<T> clazz, Object... params) throws Exception {
        List<T> result = new ArrayList<>();

        Connection conn = null;
        PreparedStatement pstm = null;

        conn = DriverManager.getConnection(URL, USER_NAME, PASSWORD);
        pstm = conn.prepareStatement(sql);
        // 添加參數
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                pstm.setObject(i + 1, params[i]);
            }
        }
        // 執行查詢
        ResultSet rs = pstm.executeQuery();

        // 獲得表的元數據
        ResultSetMetaData rsmd = pstm.getMetaData();
        // 表的列數
        int metaSize = rsmd.getColumnCount();

        while (rs.next()) {
            T entity = clazz.newInstance();

            for (int i = 1; i <= metaSize; i++) {
                // 獲得當前列的列名
                String columnName = rsmd.getColumnName(i);
                // 根據字段名獲得實體類中的字段
                Field field = clazz.getDeclaredField(columnName);
                // 設置字段可以被訪問
                field.setAccessible(true);
                // 給實體中的字段賦值
                field.set(entity, rs.getObject(columnName));
            }
            result.add(entity);
        }

        CloseConnection(conn, pstm, rs);
        return result;
    }

    public static <T> T queryObject(String sql, Class<T> clazz, Object... params) throws Exception {
        List<T> result = queryList(sql, clazz, params);
        if (result != null && !result.isEmpty()) {
            return result.get(0);
        }
        return null;
    }

    public static int execute(String sql, Object... params) throws Exception {
        int rows = 0;
        Connection conn = null;
        PreparedStatement pstm = null;

        conn = DriverManager.getConnection(URL, USER_NAME, PASSWORD);
        pstm = conn.prepareStatement(sql);
        // 添加參數
        if (params != null) {
            for (int i = 0; i < params.length; i++) {
                pstm.setObject(i + 1, params[i]);
            }
        }
        // 執行
        rows = pstm.executeUpdate();
        CloseConnection(conn, pstm, null);
        return rows;
    }

    public static void CloseConnection(Connection conn, PreparedStatement pstm, ResultSet rs) throws SQLException {
        if (rs != null) {
            rs.close();
        }
        if (pstm != null) {
            pstm.close();
        }
        if (conn != null) {
            conn.close();
        }
    }
}
View Code

6.4、JSON返回類型封裝

package com.zhangguo.utils;

import java.util.HashMap;
import java.util.Map;

/**
 * 返回類型封裝
 */
public class R extends HashMap<String, Object> {
    private static final long serialVersionUID = 1L;
    
    public R() {
        put("code", 0);
        put("msg", "success");
    }

    //錯誤時
    public static R error() {
        return error(500, "未知異常,請聯系管理員");
    }
    
    public static R error(String msg) {
        return error(500, msg);
    }
    
    public static R error(int code, String msg) {
        R r = new R();
        r.put("code", code);
        r.put("msg", msg);
        return r;
    }

    //成功時
    public static R ok(String msg) {
        R r = new R();
        r.put("msg", msg);
        return r;
    }
    
    public static R ok(Map<String, Object> map) {
        R r = new R();
        r.putAll(map);
        return r;
    }
    
    public static R ok() {
        return new R();
    }

    @Override
    public R put(String key, Object value) {
        super.put(key, value);
        return this;
    }
}

 

測試:

package com.zhangguo.controller;

import java.util.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.zhangguo.utils.DbUtils;
import com.zhangguo.utils.JsonUtils;
import com.zhangguo.utils.R;

@WebServlet("/StudentController")
public class StudentController extends BaseServlet {
    private static final long serialVersionUID = 1L;

    public void getAllStudent(HttpServletRequest request, HttpServletResponse response) {
        List<Student> students=null;
        try {
            students = DbUtils.queryList("select * from student", Student.class);
            String json = JsonUtils.toJson(students);
            response.getWriter().append(json);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void getAllStus(HttpServletRequest request, HttpServletResponse response) {
        List<Student> students=null;
        try {
            students = DbUtils.queryList("select * from student", Student.class);
            R r=R.ok().put("data", students).put("time",new Date());
            String json = JsonUtils.toJson(r);
            response.getWriter().append(json);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

運行結果:

jsonUtils:

package com.zhangguo.utils;

import java.text.SimpleDateFormat;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonUtils {
    /**
     * 序列化成json
     * */
    public static String toJson(Object obj) {
        // 對象映射器
        ObjectMapper mapper = new ObjectMapper();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd HH:mm:ss");
        mapper.setDateFormat(sdf);
        
        String result = null;
        // 序列化user對象為json字符串
        try {
            result = mapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return result;
    }
    
    /**
     * 反序列化成對象
     * */
    public static <T> T toObject(String json,Class<T> valueType) {
        //對象映射器
        ObjectMapper mapper=new ObjectMapper();
        T result=null;
        try {
            result=mapper.readValue(json,valueType);

        }catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}
View Code

baseServlet:

package com.zhangguo.controller;

import java.io.IOException;
import java.lang.reflect.Method;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class BaseServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public void init(ServletConfig config) throws ServletException {
        
    }
    
    public String commonObject;
   //Write once only once!
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String action = request.getParameter("act");
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        
        if (action != null) {
            try {
                // 在當前Servlet實例中根據action找到方法信息
                Method method = getClass().getDeclaredMethod(action, HttpServletRequest.class,
                        HttpServletResponse.class);
                if (method != null) {
                    // 在當前實例上調用方法method,指定參數request,response
                    method.invoke(this, request, response);
                } else {
                    response.getWriter().write("您請求的action不存在");
                }
            } catch (Exception e) {
                response.getWriter().write("調用發生了錯誤,錯誤:" + e.getMessage());
                e.printStackTrace();
            }

        } else {
            try {
                response.getWriter().write("請指定參數act");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
View Code

七、視頻與示例

https://www.bilibili.com/video/av9219224/

示例下載

八、作業

7.1、請定義一個泛型類,實現簡單的List功能,可變長度的數組,可以實現foreach功能。

        MyList<Integer> list1=new MyList<Integer>();
        list1.add(1);
        list1.add(2);
        list1.add(3);
        foreach(Integer i : list){
            System.out.println(i);
        }

7.2、請定義一個泛型方法,根據指定的類型獲得方法中所有的字段、方法信息。

7.3、請使用JDBC+反射+泛型實現一個可返回強類型的方法,如:

指定SQL語句、參數與類型返回一個強類型對象

 List<Student> students = JDBCUtils.queryForList("select * from student where id<=?", Student.class, 10);
        for (Student student : students) {
            System.out.println(student);
        }
View Code

7.4、指定一個數據庫,生成數據庫下所有的表的實體類文件

7.5、JDBC+反射+泛型實現一個簡單的ORM(選作)

package model;
 
import java.util.Date;
 
import annotation.Column;
import annotation.Entity;
import annotation.Id;
 
/**
 * 圖書
 */
@Entity("t_book")    //表名
public class Book {
 
    /**
     * 圖書編號
     */
    @Id("t_isbn")
    private String isbn;
 
    /**
     * 書名
     */
    @Column("t_name")
    private String name;
 
    /**
     * 作者
     */
    @Column("t_author")
    private String author;
 
    /**
     * 出版社
     */
    @Column("t_publishing")
    private String publishing;
 
    /**
     * 出版時間
     */
    @Column(value = "t_pubdate")
    private Date pubdate;
 
    /**
     * 價格
     */
    @Column(value = "t_price")
    private double price;
 
    public String getIsbn() {
        return isbn;
    }
 
    public void setIsbn(String isbn) {
        this.isbn = isbn;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getAuthor() {
        return author;
    }
 
    public void setAuthor(String author) {
        this.author = author;
    }
 
    public String getPublishing() {
        return publishing;
    }
 
    public void setPublishing(String publishing) {
        this.publishing = publishing;
    }
 
    public Date getPubdate() {
        return pubdate;
    }
 
    public void setPubdate(Date pubdate) {
        this.pubdate = pubdate;
    }
 
    public double getPrice() {
        return price;
    }
 
    public void setPrice(double price) {
        this.price = price;
    }
 
    @Override
    public String toString() {
        return "書名: " + name + " 圖書編號: " + isbn + " 作者: " + author
                + " 出版社: " + publishing + " 出版時間: " + pubdate
                + " 價格: " + price;
    }
}
View Code

調用:

package xml;
 
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import model.Book;
 
import org.junit.BeforeClass;
import org.junit.Test;
 
import util.DateUtils;
import dao.GenericDao;
import dao.JdbcGenericDaoImpl;
 
/**
 * 測試泛型DAO的CRUD操作
 */
public class GenericDaoTest {
    
    private GenericDao<Book> bookDao = new JdbcGenericDaoImpl<Book>();
    
    private static InputStream is;
 
    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        is = XmlParserTest.class.getResourceAsStream("/books.xml");
    }
    
    @Test
    public void testSave() throws Exception {
        List<Book> books = SaxHelper.saxReader(is);
        for (Book book : books) {
            bookDao.save(book);
        }
    }
    
    @Test
    public void testStudentFindAll1() throws Exception {
        System.out.println("\n-------------更新、刪除前,測試查詢所有記錄--------------------");
        List<Book> books = bookDao.findAllByConditions(null, Book.class);
        for (Book book : books) {
            System.out.println(book);
        }
    } 
    
    @Test
    public void testDelete() throws Exception {
        System.out.println("\n-------------測試刪除一條記錄--------------------");
        bookDao.delete("9787111349662",Book.class);
    }
    
    @Test
    public void testGet() throws Exception {
        System.out.println("\n-------------測試查詢一條記錄--------------------");
        Book book = bookDao.get("9787121025389", Book.class);
        System.out.println(book);
    }
    
    @Test
    public void testUpdate() throws Exception {
        System.out.println("\n-------------測試修改一條記錄--------------------");
        Book book = new Book();
        book.setIsbn("9787121025389");
        book.setName("JAVA面向對象編程");
        book.setAuthor("孫衛琴");
        book.setPublishing("電子工業出版社");
        book.setPubdate(DateUtils.string2Date("yyyy-MM-dd", "2006-07-01"));
        book.setPrice(50.6);
        bookDao.update(book);
    }
    
    @Test
    public void testStudentFindAll2() throws Exception {
        System.out.println("\n-------------更新、刪除前,測試根據條件查詢所有記錄--------------------");
        Map<String,Object> sqlWhereMap = new HashMap<String, Object>();
        //sqlWhereMap.put("t_isbn", "9787111213826");
        //sqlWhereMap.put("t_name", "Java");
        sqlWhereMap.put("t_publishing", "機械工業出版社");
        //sqlWhereMap.put("t_pubdate", new Date(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2007-01-01 12:06:00").getTime()));
        List<Book> books = bookDao.findAllByConditions(null, Book.class);
        for (Book book : books) {
            System.out.println(book);
        }
    } 
 
}
View Code  

參考:

https://www.cnblogs.com/sunwei2012/archive/2010/10/08/1845938.html

https://www.cnblogs.com/lwbqqyumidi/p/3837629.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM