Java開(kāi)發(fā)綜合實(shí)戰(zhàn) 教案全套 趙健 項(xiàng)目1-10 Java開(kāi)發(fā)環(huán)境和工具-訪問(wèn)數(shù)據(jù)庫(kù)_第1頁(yè)
Java開(kāi)發(fā)綜合實(shí)戰(zhàn) 教案全套 趙健 項(xiàng)目1-10 Java開(kāi)發(fā)環(huán)境和工具-訪問(wèn)數(shù)據(jù)庫(kù)_第2頁(yè)
Java開(kāi)發(fā)綜合實(shí)戰(zhàn) 教案全套 趙健 項(xiàng)目1-10 Java開(kāi)發(fā)環(huán)境和工具-訪問(wèn)數(shù)據(jù)庫(kù)_第3頁(yè)
Java開(kāi)發(fā)綜合實(shí)戰(zhàn) 教案全套 趙健 項(xiàng)目1-10 Java開(kāi)發(fā)環(huán)境和工具-訪問(wèn)數(shù)據(jù)庫(kù)_第4頁(yè)
Java開(kāi)發(fā)綜合實(shí)戰(zhàn) 教案全套 趙健 項(xiàng)目1-10 Java開(kāi)發(fā)環(huán)境和工具-訪問(wèn)數(shù)據(jù)庫(kù)_第5頁(yè)
已閱讀5頁(yè),還剩254頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

Java開(kāi)發(fā)綜合實(shí)戰(zhàn)STYLEREFbt1a項(xiàng)目一STYLEREFbt1bJava開(kāi)發(fā)環(huán)境和工具訪問(wèn)數(shù)據(jù)庫(kù)任務(wù)1實(shí)現(xiàn)Java多線程任務(wù)引入既然開(kāi)發(fā)了網(wǎng)絡(luò)版的應(yīng)用程序,就有可能涉及多人同時(shí)操作管理系統(tǒng)的問(wèn)題,這就需要用到多線程技術(shù)。小白知道多線程是Java的重要特性之一,什么是線程呢?在Java中如何實(shí)現(xiàn)多線程呢?知識(shí)準(zhǔn)備在Java中,線程是一種繼承了Thread類或者實(shí)現(xiàn)了Runnable接口的對(duì)象。對(duì)應(yīng)地,創(chuàng)建線程有兩種方式:一種是繼承Thread類,另一種是實(shí)現(xiàn)Runnable接口。一、進(jìn)程與線程在Windows中,每一個(gè)在操作系統(tǒng)中運(yùn)行的應(yīng)用程序都是一個(gè)進(jìn)程(process)。進(jìn)程是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位。每一個(gè)進(jìn)程都有獨(dú)立的內(nèi)存空間和系統(tǒng)資源,其內(nèi)部數(shù)據(jù)和狀態(tài)也是完全獨(dú)立的。程序從加載、執(zhí)行到執(zhí)行完畢的過(guò)程,就是進(jìn)程從產(chǎn)生、發(fā)展到消亡的過(guò)程。CPU同一時(shí)刻只能運(yùn)行一個(gè)進(jìn)程,利用CPU的分時(shí)機(jī)制,每個(gè)進(jìn)程都能循環(huán)獲得自己的CPU時(shí)間片。由于CPU的輪換速度非???,以至于用戶感覺(jué)不到進(jìn)程的中斷,產(chǎn)生所有程序“同時(shí)”運(yùn)行的錯(cuò)覺(jué)。線程是進(jìn)程的組成部分,也稱為輕量級(jí)進(jìn)程(LWP),一個(gè)線程是進(jìn)程中的一個(gè)執(zhí)行流程。一個(gè)進(jìn)程中可以同時(shí)包括多個(gè)線程,每個(gè)線程本身不擁有系統(tǒng)資源,只擁有少量在運(yùn)行中必不可少的資源,但它可與同屬一個(gè)進(jìn)程的其他線程共享進(jìn)程所擁有的全部資源。每個(gè)線程都可以得到一小段程序的執(zhí)行時(shí)間,這樣一個(gè)進(jìn)程就可以具有多個(gè)并發(fā)執(zhí)行的線程。每個(gè)Java應(yīng)用程序都有一個(gè)缺省的主線程(main線程)。這個(gè)主線程就是JVM加載代碼發(fā)現(xiàn)main()方法后啟動(dòng)的線程,負(fù)責(zé)執(zhí)行main()方法。在單線程中,程序代碼按調(diào)用順序依次往下執(zhí)行,執(zhí)行完最后一個(gè)語(yǔ)句,JVM就會(huì)結(jié)束Java應(yīng)用程序。如果需要一個(gè)進(jìn)程同時(shí)完成多段代碼的操作,就需要在main()方法的執(zhí)行中再創(chuàng)建其他線程,也就是產(chǎn)生多線程。JVM在主線程和其他線程之間輪流切換,每個(gè)線程都有機(jī)會(huì)獲得CPU資源。這種情況下,如果main線程結(jié)束,JVM也不會(huì)結(jié)束應(yīng)用程序,而是等到應(yīng)用程序中的所有線程都結(jié)束之后,才結(jié)束應(yīng)用程序。二、線程的狀態(tài)為方便操作系統(tǒng)管理線程,線程要經(jīng)歷不同的生命階段。線程被創(chuàng)建以后,CPU需要在多條線程之間切換,因此線程既不是一啟動(dòng)就進(jìn)入執(zhí)行狀態(tài),也不是一直處于執(zhí)行狀態(tài)。在線程的生命周期中,它要經(jīng)過(guò)新建(New)、就緒(Runnable)、運(yùn)行(Running)、阻塞(Blocked)和消亡(Dead)五種狀態(tài)。1.新建狀態(tài)一個(gè)線程對(duì)象被聲明并創(chuàng)建時(shí),該線程就處于新建狀態(tài),此時(shí)僅由JVM為其分配內(nèi)存空間,并初始化其成員變量的值。2.就緒狀態(tài)線程對(duì)象調(diào)用start()方法之后,該線程就處于就緒狀態(tài),進(jìn)入線程隊(duì)列排列,等待調(diào)度運(yùn)行。3.運(yùn)行狀態(tài)如果處于就緒狀態(tài)的線程獲得了CPU資源,就開(kāi)始執(zhí)行run()方法中的線程執(zhí)行體,此時(shí)該線程處于運(yùn)行狀態(tài)。run()方法規(guī)定了線程的具體使命。在線程的run()方法結(jié)束之前,不能再調(diào)用該線程的start()方法,否則會(huì)發(fā)生IllegalThreadStateException異常。4.阻塞狀態(tài)當(dāng)處于運(yùn)行狀態(tài)的線程失去所占用資源之后,便進(jìn)入阻塞狀態(tài)。在Java中,線程進(jìn)入阻塞狀態(tài)可能有以下4種原因。(1)JVM將CPU資源切換給其他線程。(2)線程執(zhí)行了sleep(intmillsecond)方法進(jìn)入休眠狀態(tài)。經(jīng)過(guò)指定的時(shí)間millsecond之后,該線程將重新進(jìn)入線程隊(duì)列等候CPU資源,以便從中斷處繼續(xù)運(yùn)行。(3)線程執(zhí)行了wait()方法進(jìn)入等待狀態(tài)。這種情況下,必須由其他線程調(diào)用notify()方法通知它重新進(jìn)入線程隊(duì)列等候CPU資源,以便從中斷處繼續(xù)運(yùn)行。(4)線程執(zhí)行某個(gè)操作(例如運(yùn)行耗時(shí)的I/O操作)進(jìn)入阻塞狀態(tài)。這種情況下,只有當(dāng)引起阻塞的原因消除時(shí),該線程才會(huì)重新進(jìn)入線程隊(duì)列等候CPU資源,以便從中斷處繼續(xù)運(yùn)行。5.消亡狀態(tài)線程因異常被強(qiáng)制結(jié)束或執(zhí)行完run()方法,線程結(jié)束生命周期就會(huì)處于消亡狀態(tài)。此時(shí)的線程已釋放分配給它的內(nèi)存,不再具有繼續(xù)運(yùn)行的能力。三、繼承Thread類實(shí)現(xiàn)多線程Thread類是java.lang包中的一個(gè)類,在使用時(shí)不需要引入java.lang包,系統(tǒng)會(huì)自動(dòng)加載。使用Thread類實(shí)現(xiàn)線程的步驟如下:(1)繼承Thread類,并重寫(xiě)run()方法。Thread類中的run()方法沒(méi)有具體內(nèi)容,需要在子類中重寫(xiě)run()方法規(guī)定線程要完成的具體任務(wù)。run()方法通常也稱為線程執(zhí)行體。(2)創(chuàng)建Thread子類的實(shí)例,即創(chuàng)建線程對(duì)象。Thread類創(chuàng)建線程有以下兩個(gè)常用的構(gòu)造方法:publicThread():創(chuàng)建一個(gè)線程對(duì)象。publicThread(StringthreadName):創(chuàng)建一個(gè)有指定名稱的線程對(duì)象。(3)調(diào)用線程對(duì)象的start()方法啟動(dòng)線程。線程對(duì)象創(chuàng)建之后,不會(huì)自動(dòng)進(jìn)入線程隊(duì)列,JVM也不知道它的存在。此時(shí)需要調(diào)用start()方法啟動(dòng)線程,進(jìn)入線程隊(duì)列等候執(zhí)行。當(dāng)線程獲得CPU資源時(shí),run()方法就會(huì)即刻執(zhí)行,進(jìn)入運(yùn)行狀態(tài)。案例——模擬喂養(yǎng)寵物本案例通過(guò)繼承Thread類創(chuàng)建兩個(gè)線程,輸出寵物吃東西、喝水,使用主線程輸出寵物玩耍,演示多線程的運(yùn)行效果。(1)在Eclipse中新建一個(gè)名為ThreadDemo的Java項(xiàng)目。然后在項(xiàng)目中添加一個(gè)名為EatFood的類,該類繼承Thread類,并重寫(xiě)run()方法。具體代碼如下:publicclassEatFoodextendsThread{ privateintfoodamount=5; //食物總量 publicvoidrun(){ //重寫(xiě)run() while(foodamount>0){ for(inti=1;i<=5;i++){ System.out.print("第"+i+"次吃東西\t"); foodamount-=1; } System.out.println("食物吃完啦!"); } }}(2)在項(xiàng)目中添加一個(gè)名為Drink的類,繼承Thread類,并重寫(xiě)run()方法。具體代碼如下:publicclassDrinkextendsThread{ privateintwateramount=3; //水的總量 publicvoidrun(){ //重寫(xiě)run()方法 while(wateramount>0){ for(inti=1;i<=3;i++){ System.out.print("第"+i+"次喝水\t"); wateramount-=1; } System.out.println("水喝完啦!"); } }}(3)在項(xiàng)目中添加一個(gè)名為TestThread的類,編寫(xiě)主方法實(shí)例化線程對(duì)象,然后啟動(dòng)線程。具體代碼如下:publicclassTestThread{ publicstaticvoidmain(String[]args){ //調(diào)用無(wú)參構(gòu)造方法創(chuàng)建兩個(gè)線程 EatFoodeat=newEatFood(); Drinkdrink=newDrink(); //啟動(dòng)線程 eat.start(); drink.start(); //主線程的循環(huán)語(yǔ)句 for(inti=1;i<=4;i++) System.out.print("第"+i+"次玩耍\t"); }}(4)運(yùn)行程序TestThread.java,在控制臺(tái)窗格中可以看到多線程的運(yùn)行結(jié)果,如圖9-1所示。圖9-1運(yùn)行結(jié)果提示:上述程序的運(yùn)行結(jié)果取決于當(dāng)前CPU資源的使用情況,因此每次運(yùn)行的結(jié)果都可能不同。在運(yùn)行程序TestThread.java時(shí),JVM首先進(jìn)入main()方法啟動(dòng)主線程,主線程在使用CPU資源時(shí)實(shí)例化并啟動(dòng)兩個(gè)線程,然后執(zhí)行for循環(huán)。在本例的這次運(yùn)行中,主線程執(zhí)行1次for循環(huán)語(yǔ)句后,CPU資源切換給線程drink。線程drink運(yùn)行run()方法,執(zhí)行for循環(huán)3次后,CPU資源切換給線程eat,該線程運(yùn)行run()方法,執(zhí)行1次for循環(huán)后,CPU資源又切換給線程drink,輸出“水喝完啦!”,至此,線程drink的run()方法執(zhí)行完成,進(jìn)入消亡狀態(tài)。CPU資源切換給主線程執(zhí)行一次for循環(huán),然后切換給線程eat執(zhí)行3次for循環(huán)。接下來(lái)又切換給主線程執(zhí)行兩次for循環(huán),至此,主線程的執(zhí)行體運(yùn)行完成進(jìn)入消亡狀態(tài)。但JVM并沒(méi)有結(jié)束應(yīng)用程序,而是將CPU資源切換給線程eat從中斷處繼續(xù)執(zhí)行,直到該線程的run()方法執(zhí)行完成,所有線程都進(jìn)入消亡狀態(tài),應(yīng)用程序才結(jié)束運(yùn)行。四、使用Runnable接口實(shí)現(xiàn)多線程線程對(duì)象除了可以繼承Thread類創(chuàng)建,還可以通過(guò)實(shí)現(xiàn)Runnable接口創(chuàng)建。與前一種方式相比,使用Runnable接口方式可以避免單繼承的局限,在繼承非Thread類時(shí)實(shí)現(xiàn)多線程。使用Runnable接口實(shí)現(xiàn)線程的步驟如下:(1)定義Runnable接口的實(shí)現(xiàn)類,并重寫(xiě)該接口的run()方法。Runnable接口中只定義了一個(gè)抽象方法run()方法,事實(shí)上Thread類也是Runnable接口的一個(gè)實(shí)現(xiàn)類,Thread類的run()方法是Runnable接口的run()方法的重寫(xiě)。也就是說(shuō),上一節(jié)中繼承Thread類重寫(xiě)的run()方法實(shí)際上重寫(xiě)的是Runnable接口的run()方法。(2)創(chuàng)建Runnable接口實(shí)現(xiàn)類的實(shí)例作為線程對(duì)象的運(yùn)行對(duì)象,傳遞給構(gòu)造方法創(chuàng)建線程對(duì)象。Runnable接口的實(shí)現(xiàn)類采用以下兩種構(gòu)造方法創(chuàng)建Thread對(duì)象。publicThread(Runnabletarget):使用實(shí)現(xiàn)了Runnable接口的類對(duì)象target作為運(yùn)行對(duì)象,創(chuàng)建一個(gè)線程對(duì)象。publicThread(Runnabletarget,Stringname):使用一個(gè)有指定名稱的對(duì)象target作為運(yùn)行對(duì)象,創(chuàng)建線程對(duì)象。(3)調(diào)用線程對(duì)象的start()方法啟動(dòng)線程。案例——模擬外賣訂單本案例通過(guò)實(shí)現(xiàn)Runnable接口創(chuàng)建一個(gè)線程,輸出一個(gè)外賣訂單,使用主線程輸出一個(gè)訂單,演示多線程的運(yùn)行效果。(1)在Java項(xiàng)目ThreadDemo中添加一個(gè)名為OrderThread的類,該類實(shí)現(xiàn)Runnable接口,并重寫(xiě)run()方法。具體代碼如下://定義接口Runnable的實(shí)現(xiàn)類publicclassOrderThreadimplementsRunnable{ publicvoidrun(){ //重寫(xiě)run()方法 for(inti=1;i<=3;i++) System.out.println("套餐B"+i+"一份"); }}(2)在項(xiàng)目中添加一個(gè)名為TestRunnable的類,編寫(xiě)主方法實(shí)例化線程對(duì)象,然后啟動(dòng)線程。具體代碼如下:publicclassTestRunnable{ publicstaticvoidmain(String[]args){ OrderThreadtarget=newOrderThread(); //創(chuàng)建target Threadtread=newThread(target);//創(chuàng)建Thread對(duì)象 tread.start(); //啟動(dòng)線程 for(inti=1;i<=3;i++) System.out.println("套餐A"+i+"一份"); }}(3)運(yùn)行程序TestRunnable.java,在控制臺(tái)窗格中可以看到多線程的運(yùn)行結(jié)果,如圖9-2所示。圖9-2運(yùn)行結(jié)果任務(wù)2應(yīng)用多線程任務(wù)引入通過(guò)上一個(gè)任務(wù)的學(xué)習(xí),小白掌握了創(chuàng)建線程的方法,他明白,要想掌握多線程編程,就必須了解線程的生命周期,以及線程各種狀態(tài)的切換。此外,他還想知道如何將線程的特性應(yīng)用于解決各類實(shí)際問(wèn)題。知識(shí)準(zhǔn)備一、線程的常用方法前面提到過(guò),線程創(chuàng)建后,調(diào)用start()方法進(jìn)入就緒狀態(tài),在線程隊(duì)列中排隊(duì)等待執(zhí)行;當(dāng)線程獲取CPU資源時(shí)就自動(dòng)執(zhí)行run()方法,進(jìn)入運(yùn)行狀態(tài)。在實(shí)際應(yīng)用中,經(jīng)常需要根據(jù)程序流程操作線程,強(qiáng)制使線程從某一種狀態(tài)轉(zhuǎn)換到另一種狀態(tài)。下面簡(jiǎn)要介紹操作線程常用的幾個(gè)方法。1.start()方法該方法用于啟動(dòng)線程,使新建狀態(tài)的線程轉(zhuǎn)換為就緒狀態(tài),進(jìn)入線程隊(duì)列。讀者需要注意的是,只有處于新建狀態(tài)的線程才可以調(diào)用start()方法,調(diào)用之后不能再次調(diào)用該方法,否則會(huì)引起IllegalThreadStateException異常。2.run()方法該方法用于指定線程要執(zhí)行的具體操作,當(dāng)線程獲得CPU資源后由系統(tǒng)自動(dòng)調(diào)用該方法,使就緒狀態(tài)的線程進(jìn)入運(yùn)行狀態(tài)。該方法執(zhí)行完成之后,線程進(jìn)入消亡狀態(tài)。3.sleep()方法默認(rèn)情況下,線程按照優(yōu)先級(jí)從高到低的順序調(diào)度執(zhí)行,如果優(yōu)先級(jí)高的線程未消亡,優(yōu)先級(jí)低的線程就沒(méi)有機(jī)會(huì)獲得CPU資源。通過(guò)在高級(jí)別線程的run()方法中調(diào)用sleep()方法,可以使高級(jí)別的線程暫時(shí)進(jìn)入休眠,從而低級(jí)別的線程有機(jī)會(huì)執(zhí)行。線程休眠的時(shí)間由sleep()方法的參數(shù)指定,單位為毫秒。休眠結(jié)束后,線程進(jìn)入就緒狀態(tài)。sleep()是靜態(tài)方法,只對(duì)當(dāng)前對(duì)象有效,讓當(dāng)前線程進(jìn)入休眠。注意:線程在休眠時(shí)如果被中斷,JVM會(huì)拋出InterruptedException異常。因此,必須在try-catch語(yǔ)句塊中調(diào)用sleep()。4.isAlive()方法該方法用于判斷線程是否啟動(dòng)。處于新建狀態(tài)和消亡狀態(tài)的線程可調(diào)用isAlive()方法,返回false。如果線程進(jìn)入運(yùn)行狀態(tài),且run()方法沒(méi)有執(zhí)行完成,此時(shí)調(diào)用isAlive()方法返回true。5.currentThread()方法該方法是Thread類的一個(gè)靜態(tài)方法,返回當(dāng)前正在使用CPU資源的線程。此時(shí),可以通過(guò)getName()方法和setName()方法分別獲取、設(shè)置線程的名稱。如果沒(méi)有在線程啟動(dòng)前設(shè)置名稱,系統(tǒng)會(huì)在使用時(shí)自動(dòng)為線程分配一個(gè)形如“Thread-N”的名稱。6.interrupt()方法一個(gè)占有CPU資源的線程可以使用該方法“吵醒”(中斷)正在休眠的線程。此時(shí)休眠的線程會(huì)拋出InterruptedException異常,結(jié)束休眠,重新進(jìn)入就緒狀態(tài)等候CPU資源。7.join()方法該方法可以強(qiáng)制某個(gè)線程運(yùn)行,在此期間,其他線程進(jìn)入阻塞狀態(tài),必須等待此線程消亡之后才可以繼續(xù)執(zhí)行。8.setPriority()方法在多線程環(huán)境下,系統(tǒng)根據(jù)線程的優(yōu)先級(jí)決定就緒狀態(tài)下哪個(gè)線程首先進(jìn)入運(yùn)行狀態(tài)。使用setPriority()方法可以設(shè)置線程的優(yōu)先級(jí)(1~10的正整數(shù))。Thread類使用常量表示線程的優(yōu)先級(jí),例如MIN_PRIORITY代表數(shù)字1;MAX_PRIORITY代表數(shù)字10;NORM_PRIORITY代表數(shù)字5;如果沒(méi)有設(shè)置優(yōu)先級(jí),則為默認(rèn)值5。注意:線程的優(yōu)先級(jí)大小并不能決定線程的執(zhí)行順序,具體執(zhí)行順序由CPU的調(diào)度決定。9.yield()方法該方法可以暫停當(dāng)前正在執(zhí)行的線程,使線程進(jìn)入阻塞狀態(tài),其他具有相同優(yōu)先級(jí)的線程有進(jìn)入運(yùn)行狀態(tài)的機(jī)會(huì)。案例——模擬VIP插隊(duì)排號(hào)本案例使用join()方法阻塞當(dāng)前線程,模擬銀行柜臺(tái)VIP插隊(duì)排號(hào)。(1)在Java項(xiàng)目ThreadDemo中新建一個(gè)名為JoinDemo的類,該類實(shí)現(xiàn)Runnable接口,重寫(xiě)run()方法。然后編寫(xiě)主方法,創(chuàng)建線程對(duì)象,使用join()方法合并線程。具體代碼如下://定義Runnable接口實(shí)現(xiàn)類publicclassJoinDemoimplementsRunnable{ //重寫(xiě)run()方法 publicvoidrun(){ for(inti=0;i<2;i++){ //獲取線程名稱 System.out.println(Thread.currentThread().getName()+(i+1)+"號(hào)請(qǐng)到窗口辦理"); try{ Thread.sleep(2000); //休眠2秒 }catch(InterruptedExceptione){ e.printStackTrace(); } } } publicstaticvoidmain(String[]args){ //使用Runnable接口實(shí)現(xiàn)類的實(shí)例創(chuàng)建線程對(duì)象vip JoinDemotarget=newJoinDemo(); Threadvip=newThread(target); vip.setName("VIP客戶"); //設(shè)置線程名稱 vip.start(); for(inti=15;i<20;i++){ System.out.println((i+1)+"號(hào)客戶請(qǐng)到窗口辦理"); try{ vip.join(); //合并線程,強(qiáng)制運(yùn)行線程vip,其他線程進(jìn)入阻塞狀態(tài) }catch(InterruptedExceptione){ e.printStackTrace(); } } }}上面的代碼在重寫(xiě)run()方法時(shí),在for循環(huán)中調(diào)用currentThread()方法和getName()方法獲取當(dāng)前占用CPU資源的線程名稱;使用sleep()方法使線程休眠2秒。在main()方法中創(chuàng)建線程對(duì)象vip后,調(diào)用setName()方法設(shè)置線程的名稱。然后啟動(dòng)線程vip,在for循環(huán)中合并vip線程強(qiáng)制運(yùn)行。(2)運(yùn)行程序,在控制臺(tái)窗格中可以看到運(yùn)行結(jié)果,如圖9-3所示。圖9-3運(yùn)行結(jié)果從運(yùn)行結(jié)果可以看到,線程vip調(diào)用join()方法之后,主線程進(jìn)入阻塞狀態(tài),vip線程被強(qiáng)制執(zhí)行直到結(jié)束,然后繼續(xù)執(zhí)行主線程。二、實(shí)現(xiàn)線程同步在多線程程序中,由于多個(gè)線程搶占資源,可能會(huì)發(fā)生資源訪問(wèn)的沖突,例如同時(shí)訪問(wèn)同一個(gè)變量,且某個(gè)線程需要修改這個(gè)變量的值。為了避免這種沖突造成的數(shù)據(jù)不安全,Java提供了線程同步機(jī)制。所謂線程同步,是指一個(gè)線程調(diào)用使用關(guān)鍵字synchronized修飾的方法或代碼塊時(shí),其他線程要使用這個(gè)代碼塊或方法就必須等待,直到上一個(gè)線程使用完該代碼塊或方法。實(shí)現(xiàn)線程同步有兩種方式:同步塊和同步方法。1.同步塊使用關(guān)鍵字synchronized修飾的代碼塊稱為同步塊,也稱為臨界區(qū),語(yǔ)法格式如下:synchronized(同步對(duì)象){//需要同步的代碼}從上面的語(yǔ)法格式可以看到,使用同步塊時(shí)必須指定一個(gè)需要同步的對(duì)象,通常將當(dāng)前對(duì)象this設(shè)置為同步對(duì)象。每個(gè)對(duì)象都存在一個(gè)標(biāo)志位,具有0和1兩個(gè)值。一個(gè)線程運(yùn)行到同步塊時(shí)首先檢查該對(duì)象的標(biāo)志位,如果為0,表明有其他線程在運(yùn)行同步塊,此時(shí)該線程處于就緒狀態(tài)。運(yùn)行同步塊的線程結(jié)束,同步對(duì)象的標(biāo)志位被置為1,就緒狀態(tài)的線程開(kāi)始執(zhí)行同步塊,并將同步對(duì)象的標(biāo)志位設(shè)置為0。簡(jiǎn)單來(lái)說(shuō),同步就是指一個(gè)線程必須等待另一個(gè)線程操作完再繼續(xù)的情況。需要同步的代碼通常為對(duì)共享資源的操作。2.同步方法所謂同步方法,就是使用關(guān)鍵字synchronized修飾多個(gè)線程都要使用的方法,語(yǔ)法格式如下:Synchronized返回值類型方法名(參數(shù)列表){//需要同步的代碼}某個(gè)對(duì)象調(diào)用同步方法時(shí),該對(duì)象上的其他同步方法必須等到該同步方法執(zhí)行完畢后才能被執(zhí)行。案例——模擬景區(qū)售票因疫情防控,某景區(qū)開(kāi)放了3個(gè)售票窗口實(shí)行限流售票。本案例使用同步代碼塊模擬景區(qū)售票。(1)在Java項(xiàng)目ThreadDemo中新建一個(gè)名為Tickets的類,該類實(shí)現(xiàn)Runnable接口,在重寫(xiě)run()方法時(shí)定義同步代碼塊。具體代碼如下:publicclassTicketsimplementsRunnable{ intnum=10; //初始票數(shù) publicvoidrun(){ //重寫(xiě)run()方法 while(num>0){ //定義同步塊,修改余票數(shù)量 synchronized(this){ if(num>0){ try{ Thread.sleep(500); //休眠0.5秒 }catch(Exceptione){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+ "售出一張\t余票:"+(--num)); //輸出售票信息和余票數(shù) } } } }}上面的代碼中,訪問(wèn)和修改余票是各個(gè)線程的共享資源操作,因此放置在同步塊中。一個(gè)線程在處理余票數(shù)據(jù)時(shí),其他線程暫時(shí)處于就緒狀態(tài),等前一個(gè)線程處理完成,余票數(shù)據(jù)減1,其他線程才能進(jìn)入同步塊并處理余票數(shù)據(jù)。(2)在項(xiàng)目ThreadDemo中添加一個(gè)名為SaleTickets的類。編寫(xiě)main()方法,創(chuàng)建3個(gè)線程并啟動(dòng)線程。具體代碼如下:publicclassSaleTickets{ publicstaticvoidmain(Stringargs[]){ Ticketstickets=newTickets(); //創(chuàng)建Runnable實(shí)例tickets Thread[]td=newThread[3]; //使用數(shù)組存放3個(gè)線程 //創(chuàng)建線程,指定線程名稱,并啟動(dòng)線程 for(inti=0;i<3;i++){ td[i]=newThread(tickets); td[i].setName((i+1)+"號(hào)窗口"); td[i].start(); } }}(3)運(yùn)行SaleTickets.java,在控制臺(tái)窗格中可以看到輸出的售票信息以及余票信息,如圖9-4所示。圖9-4運(yùn)行結(jié)果三、協(xié)調(diào)同步的線程對(duì)于同步,有時(shí)會(huì)涉及一些特殊情況。比如旅客排隊(duì)進(jìn)站時(shí),要求出示身份證明相關(guān)證件。如果有人一時(shí)找不到身份證件,相關(guān)人員就會(huì)要求他先在一旁查找,并允許他后面的人先驗(yàn)證通過(guò)。如果找到了,他就可以從等待的位置開(kāi)始驗(yàn)證通行。在程序中,如果一個(gè)線程使用的同步方法要用到某個(gè)變量,而該變量又需要其他線程修改后才能使用,此時(shí)可以在同步方法中使用wait()方法中斷線程,使其等待,并允許其他線程執(zhí)行這個(gè)同步方法。其他線程執(zhí)行完這個(gè)同步方法時(shí),使用notify()方法或notifyAll()方法通知等待執(zhí)行這個(gè)同步方法的線程進(jìn)入就緒隊(duì)列,等待分配系統(tǒng)資源,以從中斷處開(kāi)始執(zhí)行。如果有多個(gè)處于等待的線程,則遵循“先中斷先繼續(xù)”原則進(jìn)入運(yùn)行狀態(tài)。注意:wait()、notify()和notifyAll()方法只能用在同步塊或同步方法中,且不允許重寫(xiě)。1.wait()方法該方法與sleep()方法的功能類似,在非多線程運(yùn)行條件下的情況都是當(dāng)前線程讓出執(zhí)行機(jī)會(huì),進(jìn)入休眠/等待。不同的是,線程調(diào)用wait()方法會(huì)釋放其占有的資源,從運(yùn)行狀態(tài)轉(zhuǎn)換為等待狀態(tài),使得其他線程可以進(jìn)入synchronized代碼塊執(zhí)行,且不需要捕獲異常。而調(diào)用sleep()方法不會(huì)釋放資源,其他線程只能等待synchronized代碼塊中的線程結(jié)束休眠并執(zhí)行完畢才能競(jìng)爭(zhēng),且必須要捕獲異常。提示:在實(shí)際應(yīng)用中,wait()方法通常放在一個(gè)while(等待條件){}循環(huán)結(jié)構(gòu)中。2.notify()方法該方法用于通知等待隊(duì)列中的第一個(gè)線程從等待狀態(tài)進(jìn)入就緒狀態(tài)。3.notifyAll()方法該方法用于通知等待隊(duì)列中的所有線程從等待狀態(tài)進(jìn)入就緒狀態(tài)。案例——排隊(duì)購(gòu)買蛋糕顧客在蛋糕店排隊(duì)購(gòu)買蛋糕,但現(xiàn)在只剩芝士蛋糕了,如果需要其他蛋糕,則需要等待。本案例利用wait()方法和notifyAll()方法模擬排隊(duì)買蛋糕的場(chǎng)景。(1)在Java項(xiàng)目ThreadDemo中新建一個(gè)名為Customers的類,該類實(shí)現(xiàn)Runnable接口,并定義一個(gè)同步方法處理購(gòu)買蛋糕的事務(wù)。具體代碼如下:publicclassCustomersimplementsRunnable{ publicvoidrun(){ //重寫(xiě)run()方法 //不同顧客調(diào)用同步方法byCake(),參數(shù)為要購(gòu)買的蛋糕種類 if(Thread.currentThread().getName().equals("Lily")){ byCake("Bestingcake"); } elseif(Thread.currentThread().getName().equals("Alex")){ byCake("cheesecake"); } } //定義同步方法 privatesynchronizedvoidbyCake(Stringcake){ //cheesecake可以直接購(gòu)買 if(cake=="cheesecake"){ System.out.println(Thread.currentThread().getName()+"要買"+cake); System.out.println(Thread.currentThread().getName()+"買到了"+cake); }else{ //如果要購(gòu)買其他種類的蛋糕,輸出提示信息 try{ System.out.println(Thread.currentThread().getName()+"要買"+cake); System.out.println(cake+"已售完"); System.out.println(Thread.currentThread().getName()+"等待糕點(diǎn)師制作蛋糕……"); System.out.println("下一位顧客購(gòu)買蛋糕……"); wait();//中斷當(dāng)前線程,進(jìn)入等待狀態(tài) Thread.sleep(2000);//休眠2秒,輸出提示信息 System.out.println(cake+"制作完成"); System.out.println(Thread.currentThread().getName()+"買到了"+cake); }catch(InterruptedExceptione){} } notifyAll(); //喚醒等待中的線程,從中斷處繼續(xù)執(zhí)行 }}(2)在項(xiàng)目中添加一個(gè)名為Cakes的類,該類創(chuàng)建兩個(gè)線程并啟動(dòng)線程。具體代碼如下:publicclassCakes{ publicstaticvoidmain(String[]args){ Customerstarget=newCustomers(); //實(shí)例化Runnable實(shí)現(xiàn)類對(duì)象 Thread[]customers=newThread[2]; //使用數(shù)組存儲(chǔ)2個(gè)線程 String[]name=newString[]{"Lily","Alex"}; //指定線程名稱 //創(chuàng)建2個(gè)線程對(duì)象,設(shè)置名稱并啟動(dòng) for(inti=0;i<2;i++){ customers[i]=newThread(target); customers[i].setName(name[i]); customers[i].start(); } }}(3)運(yùn)行程序,在控制臺(tái)窗格中可以看到運(yùn)行結(jié)果,如圖9-5所示。圖9-5運(yùn)行結(jié)果四、GUI線程在運(yùn)行包含圖形用戶界面的Java應(yīng)用程序時(shí),JVM會(huì)自動(dòng)啟動(dòng)一些專門用來(lái)監(jiān)聽(tīng)和響應(yīng)用戶在圖形用戶界面上的操作的GUI線程。GUI線程負(fù)責(zé)建造窗體以及處理GUI事件,任何一個(gè)特定窗體的消息總是被產(chǎn)生這一窗體的線程捕獲,然后派發(fā)給不同的窗體函數(shù)處理。GUI線程中有兩個(gè)重要的線程:AWT-EventQuecue和AWT-Windows。AWT-EventQuecue線程負(fù)責(zé)處理GUI事件,而AWT-Windows線程負(fù)責(zé)將窗體或組件繪制到桌面。在創(chuàng)建有GUI的Java多線程應(yīng)用程序時(shí),通常會(huì)繼承JFrame類,并實(shí)現(xiàn)Runnable接口和需要的事件監(jiān)聽(tīng)接口。案例——字母游戲本案例利用Swing組件、GUI事件和多線程,制作一個(gè)字母游戲。(1)在Java項(xiàng)目ThreadDemo中新建一個(gè)名為RandomLetter的類,該類繼承JFrame類,并實(shí)現(xiàn)接口ActionListener和Runnable。具體代碼如下:importjava.awt.Color;importjava.awt.FlowLayout;importjava.awt.Font;importjava.awt.event.ActionEvent;importjava.awt.event.ActionListener;importjavax.swing.JFrame;importjavax.swing.JLabel;importjavax.swing.JTextField;//繼承JFrame,并實(shí)現(xiàn)接口ActionListener和RunnablepublicclassRandomLetterextendsJFrameimplementsActionListener,Runnable{ JLabelinfo,letter,score,msg; //標(biāo)簽組件 JTextFieldinput; //文本域 ThreadsetLetter; //用于產(chǎn)生字母 intsleepTime,scores; //休眠時(shí)間和得分 RandomLetter(){ setLayout(newFlowLayout()); //設(shè)置流式布局 setLetter=newThread(this); //創(chuàng)建線程 letter=newJLabel(""); //顯示生成的字母,初始為空 letter.setFont(newFont("Arial",Font.BOLD,26));//設(shè)置產(chǎn)生的字母的字體和顏色 letter.setForeground(Color.RED); add(letter); //添加到窗體 info=newJLabel("請(qǐng)輸入左側(cè)的字母(回車確認(rèn))"); add(info); input=newJTextField(6); //輸入的字母 add(input); score=newJLabel("得分:"); score.setForeground(Color.BLUE); add(score); msg=newJLabel(""); //錯(cuò)誤提示信息 msg.setForeground(Color.RED); //默認(rèn)字呈,顯示紅色 msg.setAlignmentY(CENTER_ALIGNMENT); //居中對(duì)齊 add(msg); input.addActionListener(this); //監(jiān)聽(tīng)文本域的動(dòng)作事件 setBounds(100,200,420,120); //窗體的位置和大小 setResizable(false); //窗體不可改變大小 setVisible(true); //窗體可見(jiàn) setDefaultCloseOperation(EXIT_ON_CLOSE); //關(guān)閉窗體的方式 setLetter.start(); //在AWT-Windows線程中啟動(dòng)產(chǎn)生字母的線程 } publicvoidsetSleepTime(intms){ //設(shè)置休眠時(shí)間 sleepTime=ms; } publicvoidrun(){ //重寫(xiě)Runnable接口的run() while(true){ charc=(char)(int)(Math.random()*26+97);//生成隨機(jī)字母 letter.setText(""+c+""); //顯示字母 try{ Thread.sleep(sleepTime); //休眠 }catch(InterruptedExceptione){} } } //重寫(xiě)ActionListener接口的actionPerformed() publicvoidactionPerformed(ActionEvente){ //獲取產(chǎn)生的字母和輸入的字母 Strings=letter.getText().trim(); Stringin=input.getText().trim(); if(s.equals(in)){ //如果輸入的字母正確 msg.setText(null); //清空錯(cuò)誤提示 scores++; //計(jì)算得分 score.setText("得分:"+scores); //輸出得分 input.setText(null); //清空輸入文本框 setLerrupt(); //喚醒休眠的線程,產(chǎn)生新的字母 } else //如果輸入不正確,輸出錯(cuò)誤提示 msg.setText("錯(cuò)誤!請(qǐng)重新輸入!"); }}(2)在項(xiàng)目中添加一個(gè)名為L(zhǎng)ettersGame的類,編寫(xiě)main()方法實(shí)例化類對(duì)象,具體代碼如下:publicclassLettersGame{ publicstaticvoidmain(String[]args){ RandomLetterwin=newRandomLetter(); win.setTitle("字母游戲"); //設(shè)置窗體標(biāo)題 win.setSleepTime(5000); //設(shè)置休眠時(shí)間 }}(3)運(yùn)行程序,打開(kāi)如圖9-6所示的窗口。在文本框中輸入一個(gè)字母,如果與給定的字母相同(包括大小寫(xiě)),則得分加1;如果輸入的字母與給定字母不同,則輸出一條錯(cuò)誤提示信息,如圖9-7所示。圖9-6運(yùn)行結(jié)果1圖9-7運(yùn)行結(jié)果2本例中由于指定了產(chǎn)生字母的線程生成字母后休眠5秒,因此如果輸入錯(cuò)誤或沒(méi)有輸入,則間隔5秒后顯示的字母會(huì)被新產(chǎn)生的字母替換。如果在5秒內(nèi)輸入了正確的字母,則運(yùn)行語(yǔ)句setLerrupt();,喚醒休眠的線程,立即產(chǎn)生新的字母。項(xiàng)目總結(jié)項(xiàng)目實(shí)戰(zhàn)本章將在上一章完成的項(xiàng)目基礎(chǔ)上進(jìn)行修改,分別創(chuàng)建服務(wù)器端線程和客戶端線程。當(dāng)一個(gè)客戶端訪問(wèn)服務(wù)器時(shí),就會(huì)新建一個(gè)線程處理這個(gè)客戶端的事務(wù),從而可以處理多個(gè)用戶的請(qǐng)求。(1)復(fù)制并粘貼“進(jìn)銷存管理系統(tǒng)V8.0”,在CopyProject對(duì)話框中修改項(xiàng)目名稱為“進(jìn)銷存管理系統(tǒng)V9.0”,然后單擊Copy按鈕關(guān)閉對(duì)話框。(2)在net包中新建一個(gè)名為ServerThread.java的類,繼承Thread類,用于創(chuàng)建服務(wù)器端線程。具體代碼如下:packagenet;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;import.Socket;importcontroller.ServerControllers;importmodel.Goods;importmodel.Results;publicclassServerThreadextendsThread{ Socketconn; byte[]buf=newbyte[256]; InputStreamsin; OutputStreamsout; publicServerThread(Socketconn){ //構(gòu)造方法 this.conn=conn; } publicvoidrun(){ //重寫(xiě)run()方法 try{ System.out.println("已連接客戶端"+conn.getInetAddress() +",端口為"+conn.getPort()); sin=conn.getInputStream(); sout=conn.getOutputStream(); //處理請(qǐng)求 while(true){……}篇幅所限,省略了處理客戶端事務(wù)請(qǐng)求的代碼,具體代碼請(qǐng)參見(jiàn)上一章項(xiàng)目實(shí)戰(zhàn)中Server類的構(gòu)造方法。(3)打開(kāi)Server.java,修改Server類的構(gòu)造方法如下: publicServer(){ ServerSocketss; Socketconn; //接收并處理客戶端請(qǐng)求 try{ ss=newServerSocket(2022); while(true){ System.out.println("服務(wù)器準(zhǔn)備就緒,等待連接請(qǐng)求……"); conn=ss.accept(); //阻塞線程 ServerThreadst=newServerThread(conn); //創(chuàng)建線程 st.start(); //啟動(dòng)線程 } }catch(IOExceptione){ e.printStackTrace(); } }(4)在net包中新建一個(gè)名為ClientThread的類,繼承Thread類,用于創(chuàng)建服務(wù)器端線程。具體代碼如下:packagenet;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;import.Socket;publicclassClientThreadextendsThread{ Socketconn; //連接套接字 InputStreamcin; //輸入流 OutputStreamcout; //輸出流 byte[]buf=newbyte[20000]; publicClientThread(Socketconn){ //構(gòu)造方法 this.conn=conn; } publicvoidrun(){ //重寫(xiě)run()方法 try{ System.out.println("已連接到服務(wù)器"); //創(chuàng)建輸入流和輸出流 cin=conn.getInputStream(); cout=conn.getOutputStream(); }catch(IOExceptione){ e.printStackTrace(); } } }(5)打開(kāi)Client.java,修改靜態(tài)代碼塊,并添加構(gòu)造方法,代碼如下:…… //靜態(tài)代碼塊創(chuàng)建客戶端套接字,只在類第一次加載時(shí)執(zhí)行 static{ try{ conn=newSocket("",2022); //套接字綁定到指定的服務(wù)器和端口 //創(chuàng)建輸入流和輸出流 in=conn.getInputStream(); out=conn.getOutputStream(); }catch(UnknownHostExceptione){ e.printStackTrace(); }catch(IOExceptione){ e.printStackTrace(); } } publicClient(){ //構(gòu)造方法 ClientThreadct=newClientThread(conn);//創(chuàng)建客戶端線程 ct.start(); //啟動(dòng)線程 }……(6)打開(kāi)MainFrame.java,修改main()方法的代碼如下: publicstaticvoidmain(String[]args){ //使事件派發(fā)線程(按鈕單擊事件)上的運(yùn)行對(duì)象排隊(duì),調(diào)用run()方法更新窗體組件 EventQueue.invokeLater(newRunnable(){ publicvoidrun(){ try{ MainFramewindow=newMainFrame(); window.setVisible(true); }catch(Exceptione){ e.printStackTrace(); } } }); }由于Java中Swing是單線程的設(shè)計(jì),只能從事件派發(fā)線程(例如按鈕的單擊事件)訪問(wèn)將要在屏幕上繪制的Swing組件。因此,事件監(jiān)聽(tīng)器接口ActionListener中定義的事件處理方法actionPerformed()要在事件派發(fā)線程中調(diào)用。如果要從事件派發(fā)線程以外的線程中更新Swing組件,可以使用SwingUtilities類提供的方法invokeLater(),使事件派發(fā)線程上的可運(yùn)行對(duì)象排隊(duì)。當(dāng)可運(yùn)行對(duì)象排在事件派發(fā)隊(duì)列的隊(duì)首時(shí),就調(diào)用其run()方法。其效果是允許事件派發(fā)線程調(diào)用另一個(gè)線程中的任意一個(gè)代碼塊。事件派發(fā)線程是一個(gè)隊(duì)列,特點(diǎn)是只有執(zhí)行完上一個(gè)事件的處理程序后,才會(huì)處理下一個(gè)事件。(7)按照上一步同樣的方法修改InFrame.java、OutFrame.java和SearchFrame.java的main()方法,使對(duì)應(yīng)的窗體可見(jiàn)。(8)依次運(yùn)行Server.java和MainFrame.java,在打開(kāi)的圖形用戶界面中入庫(kù)和出庫(kù)商品。運(yùn)行結(jié)果與上一章的項(xiàng)目實(shí)戰(zhàn)運(yùn)行結(jié)果相同。項(xiàng)目十訪問(wèn)數(shù)據(jù)庫(kù)思政目標(biāo)找對(duì)學(xué)習(xí)方法,注重前后知識(shí)的遷移,能舉一反三。鼓勵(lì)應(yīng)用創(chuàng)新,引導(dǎo)學(xué)生融會(huì)貫通,增強(qiáng)實(shí)踐能力。技能目標(biāo)能夠利用常用的SQL語(yǔ)句查詢、更新、添加和刪除記錄。能夠使用JDBC操作數(shù)據(jù)庫(kù)中的數(shù)據(jù)。項(xiàng)目導(dǎo)讀在開(kāi)發(fā)應(yīng)用程序的過(guò)程中,數(shù)據(jù)庫(kù)扮演著十分重要的角色,絕大多數(shù)的應(yīng)用都需要使用數(shù)據(jù)庫(kù)來(lái)存儲(chǔ)和管理數(shù)據(jù)。JAVA提供了專門用于操作數(shù)據(jù)庫(kù)的API,即JDBC(JavaDatabaseConnectivity,Java數(shù)據(jù)庫(kù)連接),可以很容易地訪問(wèn)不同的數(shù)據(jù)庫(kù),對(duì)數(shù)據(jù)庫(kù)中的記錄實(shí)現(xiàn)查詢、修改、刪除和添加等操作。任務(wù)1SQL語(yǔ)法基礎(chǔ)任務(wù)引入小白創(chuàng)建的進(jìn)銷存管理系統(tǒng)使用數(shù)組存儲(chǔ)商品信息,為便于后期的數(shù)據(jù)擴(kuò)容和管理,他決定利用數(shù)據(jù)庫(kù)存儲(chǔ)、管理商品數(shù)據(jù)。為熟悉數(shù)據(jù)查詢操作,他使用SQLServer創(chuàng)建了一個(gè)存儲(chǔ)學(xué)生成績(jī)的數(shù)據(jù)庫(kù)performance,并創(chuàng)建了一個(gè)成績(jī)表score、錄入了數(shù)據(jù)。如果他要查詢、更新、添加或刪除數(shù)據(jù)庫(kù)中的數(shù)據(jù),可以使用什么語(yǔ)句呢?知識(shí)準(zhǔn)備訪問(wèn)數(shù)據(jù)庫(kù)要使用SQL語(yǔ)句。SQL(StructuredQueryLanguage,結(jié)構(gòu)查詢語(yǔ)言)是一個(gè)用于與數(shù)據(jù)庫(kù)通信的數(shù)據(jù)庫(kù)語(yǔ)言,是關(guān)系數(shù)據(jù)庫(kù)管理系統(tǒng)的標(biāo)準(zhǔn)語(yǔ)言。SQL語(yǔ)句主要有以下3大類,每一類語(yǔ)言包含或多或少的語(yǔ)句,使用在不同的應(yīng)用程序中。數(shù)據(jù)定義語(yǔ)言(DDL):用于定義數(shù)據(jù)的結(jié)構(gòu),例如創(chuàng)建、修改或刪除數(shù)據(jù)庫(kù)或數(shù)據(jù)庫(kù)中的對(duì)象(如表、視圖、存儲(chǔ)過(guò)程、觸發(fā)器等)。數(shù)據(jù)操縱語(yǔ)言(DML):用于操作數(shù)據(jù)表中的數(shù)據(jù),主要包括數(shù)據(jù)的插入、刪除、更新、查找、過(guò)濾和排序等,是最常用的核心SQL語(yǔ)言。數(shù)據(jù)控制語(yǔ)言(DCL):用于分配數(shù)據(jù)庫(kù)用戶的權(quán)限。在一般的應(yīng)用程序中使用較多的是數(shù)據(jù)操縱語(yǔ)言,可以使用CRUD概括地表示對(duì)數(shù)據(jù)庫(kù)的常見(jiàn)操作,即表的創(chuàng)建(Create)、數(shù)據(jù)檢索(Retrieve)、數(shù)據(jù)更新(Update)和刪除(Delete)操作。SQL語(yǔ)言很復(fù)雜也很龐大,因篇幅所限,本節(jié)僅簡(jiǎn)要介紹常用的幾個(gè)數(shù)據(jù)操縱語(yǔ)句,更多的SQL語(yǔ)句讀者可以查閱相關(guān)資料。一、select語(yǔ)句該語(yǔ)句用于在數(shù)據(jù)表中檢索符合查詢條件的數(shù)據(jù)行,僅包含指定的字段。其語(yǔ)法格式如下:SELECT所選字段列表FROM數(shù)據(jù)表名[WHERE查詢條件表達(dá)式][ORDERBY字段名[ASC|DESC]如果要檢索數(shù)據(jù)表中的所有列,可以使用通配符(*)替代字段列表。ORDER子句用于將查詢結(jié)果集按照某個(gè)字段值進(jìn)行排序,關(guān)鍵字ASC表示升序,DESC表示降序。案例——查詢成績(jī)表SQLServer數(shù)據(jù)庫(kù)performance中的score表由一群學(xué)生的成績(jī)數(shù)據(jù)組成,每一行包含四個(gè)字段,即學(xué)生姓名、數(shù)學(xué)成績(jī)、語(yǔ)文成績(jī)和外語(yǔ)成績(jī),如圖10-1所示。圖10-1score表如果要查詢?cè)摫碇袛?shù)學(xué)成績(jī)?cè)?0及以上的學(xué)生名單及對(duì)應(yīng)的數(shù)學(xué)成績(jī),并將結(jié)果按降序排列,可以利用如下SQL語(yǔ)句:SELECTname,MathFROMscoreWHERE(Math>=90)ORDERBYMathDESC查詢的結(jié)果如圖10-2所示。圖10-2select命令查詢結(jié)果二、insert語(yǔ)句INSERT語(yǔ)句用于在一個(gè)表中插入單行或多行數(shù)據(jù),同時(shí)賦給每個(gè)列相應(yīng)的值,如果這個(gè)值支持它們定義的物理順序中的所有的值,則不需要字段名。其語(yǔ)法格式如下:INSERT[INTO]表名或視圖名[(字段列表)]VALUES(字段值列表)例如,下面的語(yǔ)句在表score中插入了學(xué)生Alex的成績(jī)記錄:INSERTINTOscore(name,Math,Chinese,English)VALUES('Alex',87,93,92)其中,字段列表可以省略。執(zhí)行上面的語(yǔ)句后,表score如圖10-3示。圖10-3插入記錄后的score表三、update語(yǔ)句UPDATE語(yǔ)句用于根據(jù)查詢條件更新數(shù)據(jù)表中的某些字段值。其語(yǔ)法格式如下:UPDATE數(shù)據(jù)表名SET字段名1=字段值1,字段名2=字段值2……WHERE條件表達(dá)式例如,使用以下語(yǔ)句,可以在表score中修改olivia的數(shù)學(xué)和英語(yǔ)成績(jī):UPDATEscoreSETMath=89,English=92WHERE(name='Olivia')注意:SQL語(yǔ)句中的字符串應(yīng)包含在單引號(hào)中。該語(yǔ)句執(zhí)行之后,表score如圖10-4所示。圖10-4更新記錄后的score表四、delete語(yǔ)句該語(yǔ)句用于在數(shù)據(jù)庫(kù)中刪除符合指定條件的數(shù)據(jù)行,其語(yǔ)法格式如下:DELETEFROM數(shù)據(jù)表名[WHERE條件表達(dá)式]如果有查詢條件,則刪除與查詢條件相符的數(shù)據(jù)行;如果沒(méi)有查詢條件,將刪除所有的記錄。例如,下面的語(yǔ)句刪除表score中語(yǔ)文成績(jī)小于90分的成績(jī)記錄:DELETEFROMscoreWHERE(Chinese<90)執(zhí)行上面的語(yǔ)句后,score表如圖10-5所示。圖10-5刪除記錄后的score表任務(wù)2使用JDBC訪問(wèn)數(shù)據(jù)庫(kù)任務(wù)引入小白掌握了常用的查詢語(yǔ)句后,想利用圖形用戶界面修改數(shù)據(jù)庫(kù)中的數(shù)據(jù),但是怎樣將數(shù)據(jù)庫(kù)與Java應(yīng)用程序關(guān)聯(lián)起來(lái)呢?他查看相關(guān)資料得知,JDBC為連接數(shù)據(jù)庫(kù)提供了統(tǒng)一的規(guī)范,決定采用JDBC訪問(wèn)數(shù)據(jù)庫(kù)。接下來(lái)該如何在系統(tǒng)中部署JDBC,連接數(shù)據(jù)庫(kù)呢?查詢數(shù)據(jù)后怎樣輸出滿足條件的數(shù)據(jù)記錄呢?知識(shí)準(zhǔn)備一、JDBC概述JDBC是一套用于執(zhí)行SQL語(yǔ)句的JavaAPI,提供了一套數(shù)據(jù)庫(kù)操作標(biāo)準(zhǔn),可以采用相同的API實(shí)現(xiàn)對(duì)多種關(guān)系型數(shù)據(jù)庫(kù)的統(tǒng)一操作,從而提高Java程序多數(shù)據(jù)庫(kù)的可移植性。簡(jiǎn)單來(lái)說(shuō),JDBC能完成下列三種功能:與一個(gè)數(shù)據(jù)庫(kù)建立連接向數(shù)據(jù)庫(kù)發(fā)送SQL語(yǔ)句處理數(shù)據(jù)庫(kù)返回的結(jié)果JDBC由兩層構(gòu)成:上層是JDBCAPI,負(fù)責(zé)在Java應(yīng)用程序和JDBC驅(qū)動(dòng)程序管理器之間進(jìn)行通信,發(fā)送程序中的SQL語(yǔ);下層是JDBC驅(qū)動(dòng)程序API,負(fù)責(zé)JDBC驅(qū)動(dòng)程序管理器與實(shí)際連接的數(shù)據(jù)庫(kù)的廠商驅(qū)動(dòng)程序和第三方驅(qū)動(dòng)程序之間進(jìn)行通信,返回查詢信息或者執(zhí)行規(guī)定的操作。如果要使用JDBC訪問(wèn)某種數(shù)據(jù)庫(kù)中的數(shù)據(jù),計(jì)算機(jī)上必須安裝有JDBC驅(qū)動(dòng)程序。二、部署JDBC驅(qū)動(dòng)程序JDBC針對(duì)每一個(gè)數(shù)據(jù)庫(kù)廠商都提供了一個(gè)JDBC驅(qū)動(dòng)程序。在連接到數(shù)據(jù)庫(kù)之前,必須首先在本地計(jì)算機(jī)上安裝數(shù)據(jù)庫(kù)和JDBC驅(qū)動(dòng)程序。不同版本的JDBC驅(qū)動(dòng)程序?qū)RE的要求也不相同,因此部署JDBC驅(qū)動(dòng)程序之前,要先選擇正確的JAR類庫(kù)文件。例如MicrosoftJDBCDriver10.2安裝包中包含三個(gè)JAR類庫(kù):mssql-jdbc-10.2.0.jre8.jar、mssql-jdbc-10.2.0.jre11.jar和mssql-jdbc-10.2.0.jre17.jar。其中,mssql-jdbc-10.2.0.jre11.jar需要Java運(yùn)行時(shí)環(huán)境(JRE)11.0,使用JRE10.0或更低版本會(huì)引發(fā)異常。mssql-jdbc-10.2.0.jre17.jar需要JavaRuntimeEnvironment(JRE)17.0,使用較低版本會(huì)引發(fā)異常。JDBC驅(qū)動(dòng)程序的具體系統(tǒng)要求可參見(jiàn)對(duì)應(yīng)數(shù)據(jù)庫(kù)的官網(wǎng)說(shuō)明。由于JDBC類庫(kù)文件不是JavaSDK的一部分,因此,在下載合適的類庫(kù)文件后,應(yīng)將jar類庫(kù)文件包含在用戶應(yīng)用程序的環(huán)境變量classpath中。如果使用JDBCDriver10.2,應(yīng)在classpath中包括mssql-jdbc-10.2.0.jre8.jar、mssql-jdbc-10.2.0.jre11.jar或mssql-jdbc-10.2.0.jre17.jar,如圖10-6所示。圖10-6設(shè)置變量CLASSPATH如果在IDE中運(yùn)行訪問(wèn)數(shù)據(jù)庫(kù)的Java項(xiàng)目,需要將JDBC數(shù)據(jù)庫(kù)驅(qū)動(dòng)包添加到當(dāng)前項(xiàng)目的構(gòu)建路徑中,步驟如下。(1)在Eclipse中右擊項(xiàng)目名,從彈出的快捷菜單中選擇BuildPath→ConfigureBuildPath...命令。(2)在打開(kāi)的對(duì)話框左側(cè)窗格中選中“JavaBuildPath”節(jié)點(diǎn),然后在Libraries選項(xiàng)卡中選中Classpath,單擊AddLibrary...按鈕,如圖10-7所示。(3)在打開(kāi)的AddLibrary對(duì)話框的庫(kù)列表框中選擇“UserLibrary”,然后單擊Next按鈕,在彈出的對(duì)話框中,依次單擊UserLibraries按鈕,New按鈕,新建一個(gè)用戶庫(kù)的名稱。(4)依次單擊OK按鈕和ApplyandClose按鈕關(guān)閉對(duì)話框。然后單擊AddExternalJARs按鈕,在打開(kāi)的對(duì)話框中選擇與JRE匹配的JDBCJAR包文件(比如與JRE17匹配的mssql-jdbc-10.2.0.jre17.jar)。單擊“打開(kāi)”按鈕關(guān)閉對(duì)話框,此時(shí)可以看到新建的用戶庫(kù)如圖10-8所示。圖10-7項(xiàng)目屬性對(duì)話框圖10-8創(chuàng)建的用戶庫(kù)(5)依次單擊ApplyandClose按鈕和Finish按鈕關(guān)閉對(duì)話框。此時(shí)在項(xiàng)目的屬性對(duì)話框中可以看到添加的類庫(kù)路徑,如圖10-9所示。圖10-9添加的類庫(kù)路徑(6)添加完成后,單擊ApplyandClose按鈕關(guān)閉對(duì)話框。三、連接數(shù)據(jù)庫(kù)使用JDBC數(shù)據(jù)庫(kù)驅(qū)動(dòng)方式和數(shù)據(jù)庫(kù)建立連接需要經(jīng)過(guò)兩個(gè)步驟:(1)注冊(cè)JDBC驅(qū)動(dòng)程序;(2)與指定數(shù)據(jù)庫(kù)建立連接。這些操作使用JDBC中的Driver接口、DriverManager類和Connection接口實(shí)現(xiàn)。1.注冊(cè)驅(qū)動(dòng)程序注冊(cè)驅(qū)動(dòng)程序就是將特定數(shù)據(jù)庫(kù)的驅(qū)動(dòng)程序類裝載到JVM。每種數(shù)據(jù)庫(kù)的驅(qū)動(dòng)程序都提供一個(gè)實(shí)現(xiàn)Driver接口的類,簡(jiǎn)稱Driver類,是應(yīng)用程序必須首先加載的類,用于向驅(qū)動(dòng)程序管理器(java.sql.DriverManager類)注冊(cè)該類的實(shí)例,以便驅(qū)動(dòng)程序管理器管理數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序。在JDBCAPI4.0之前,通常使用java.lang.Class類的靜態(tài)方法forName(className)加載要連接的數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序類,并將加載的類自動(dòng)向DriverManager類注冊(cè),參數(shù)為要加載的數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序的完整類名。如果加載失敗,則拋出ClassNotFoundException異常。例如,下面的程序段可用于檢測(cè)SQLServer數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序類是否成功加載。try{//加載JDBC驅(qū)動(dòng)器 Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");out.println("驅(qū)動(dòng)器類加載成功");}catch(ClassNotFoundExceptione){ out.println("加載驅(qū)動(dòng)器類時(shí)出現(xiàn)異常");}提示:在加載驅(qū)動(dòng)程序之前,應(yīng)確保驅(qū)動(dòng)程序已經(jīng)在Java編譯器的類路徑中,否則會(huì)拋出找不到相關(guān)類的異常信息。要在項(xiàng)目中添加數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序,可以將下載的JDBC驅(qū)動(dòng)程序直接存放在Web服務(wù)目錄的WEB-INF/lib/目錄下。從JDBCAPI4.0開(kāi)始,DriverManager.getConnection()方法得到了增強(qiáng),可自動(dòng)加載JDBC驅(qū)動(dòng)程序。因此,使用驅(qū)動(dòng)程序jar庫(kù)時(shí),應(yīng)用程序無(wú)需調(diào)用Class.forName方法注冊(cè)驅(qū)動(dòng)程序。當(dāng)前通過(guò)使用Class.forName方法加載驅(qū)動(dòng)程序的現(xiàn)有應(yīng)用程序仍可繼續(xù)工作,無(wú)需進(jìn)行修改。2.建立連接注冊(cè)數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序后,JVM和數(shù)據(jù)庫(kù)之間還沒(méi)有直接聯(lián)系,需要調(diào)用DriverManager類的靜態(tài)方法getConnection()方法獲得一個(gè)數(shù)據(jù)庫(kù)連接對(duì)象,建立Java應(yīng)用程序與指定數(shù)據(jù)庫(kù)之間的聯(lián)系。建立數(shù)據(jù)庫(kù)連接對(duì)象的過(guò)程涉及兩個(gè)主要API:java.sql.DriverManager類和java.sql.Connection接口。DriverManager是JDBC用于管理驅(qū)動(dòng)程序的類,主要用于管理用戶程序與特定數(shù)據(jù)庫(kù)的連接。Connection接口類對(duì)象是應(yīng)用程序連接數(shù)據(jù)庫(kù)的連接對(duì)象,主要作用是調(diào)用createStatement()方法創(chuàng)建語(yǔ)句對(duì)象。DriverManager類有如下兩個(gè)用于建立連接的靜態(tài)方法:Connectionconn=DriverManager.getConnection(URL,user,password);Connectionconn=DriverManager.getConnection(URL);URL參數(shù)是每個(gè)JDBC驅(qū)動(dòng)程序?qū)S玫腏DBCURL,語(yǔ)法格式如下:jdbc:子協(xié)議:數(shù)據(jù)庫(kù)定位器子協(xié)議與JDBC驅(qū)動(dòng)程序有關(guān),根據(jù)實(shí)際的JDBC驅(qū)動(dòng)程序廠商而不同。提示:不同版本的SQLServer數(shù)據(jù)庫(kù)的子協(xié)議有所不同。SQLServer2005之前的版本為microsoft:sqlserver;而SQLServer2005及之后的版本子協(xié)議為sqlserver。數(shù)據(jù)庫(kù)定位器用于指定要與應(yīng)用程序進(jìn)行交互的數(shù)據(jù)庫(kù),根據(jù)驅(qū)動(dòng)程序的類型,可能包括主機(jī)名、端口和數(shù)據(jù)庫(kù)系統(tǒng)名。例如:連接SQLServer數(shù)據(jù)庫(kù)sample的URL為"jdbc:sqlserver://localhost:1433;DatabaseName=sample"。localhost(或)表示本機(jī)地址,1433是SQLServer的默認(rèn)端口號(hào),sample是數(shù)據(jù)庫(kù)名稱。很多驅(qū)動(dòng)程序還接受在URL末尾附加參數(shù),如數(shù)據(jù)庫(kù)賬號(hào)的用戶名和密碼,此時(shí)采用getConnection()方法的第二種格式。例如:以下語(yǔ)句連接MySQL數(shù)據(jù)庫(kù)sample:Stringurl="jdbc:mysql://localhost:3306/sample?user=root&password=123";Connectionconn=DriverManager.getConnection(url);參數(shù)user和password分別為用戶登錄數(shù)據(jù)庫(kù)管理系統(tǒng)的用戶名及密碼。如果沒(méi)有設(shè)置用戶名和密碼,參數(shù)設(shè)置為空即可。由于getConnection()方法可能拋出SQLException異常,因此在程序中應(yīng)捕獲異常。例如,與SQLServer數(shù)據(jù)源student建立連接的語(yǔ)句如下:try{Stringurl="jdbc:sqlserver://localhost:1433;DatabaseName=student";Connectioncon=DriverManager.getConnection(url,"sa","123456");}catch(SQLExceptione){ //處理異常}注意:如果數(shù)據(jù)庫(kù)表的記錄包含漢字,在建立連接時(shí)應(yīng)在URL參數(shù)中附加一個(gè)characterEncoding參數(shù),值為utf-8或gb2312。四、操作數(shù)據(jù)庫(kù)與數(shù)據(jù)庫(kù)建立連接后,就可以使用JDBC提供的API與數(shù)據(jù)庫(kù)進(jìn)行交互。交互的主要方式是使用SQL語(yǔ)句,JDBC將標(biāo)準(zhǔn)的SQL語(yǔ)句發(fā)送給數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)執(zhí)行指令并處理查詢結(jié)果,返回結(jié)果集。1.發(fā)送、執(zhí)行SQL指令向數(shù)據(jù)庫(kù)發(fā)送SQL指令需要使用Statement接口類對(duì)象聲明一個(gè)SQL語(yǔ)句,然后通過(guò)創(chuàng)建的數(shù)據(jù)庫(kù)連接對(duì)象調(diào)用方法createStatement()創(chuàng)建這個(gè)SQL語(yǔ)句對(duì)象,語(yǔ)法格式如下:try{ Statementsql=con.createStatement();}catch(SQLExceptione){//處理異常}Statement接口定義了執(zhí)行語(yǔ)句和獲取結(jié)果的基本方法,用于將不帶參數(shù)的簡(jiǎn)單SQL語(yǔ)句發(fā)送到數(shù)據(jù)庫(kù),并獲取指定SQL語(yǔ)句的執(zhí)行結(jié)果。Statement接口包含以下3種執(zhí)行SQL語(yǔ)句的方法。execute(Stringsql):可執(zhí)行任何SQL語(yǔ)句,返回結(jié)果為布爾值。如果值為true,表明有結(jié)果集,通常是執(zhí)行了select查詢語(yǔ)句;如果值為false,表明沒(méi)有結(jié)果集,通常是執(zhí)行了insert、delete、update等增刪改語(yǔ)句。executeQuery(Stringsql):通常執(zhí)行select查詢語(yǔ)句,返回單個(gè)ResultSet結(jié)果集。executeUpdate(Stringsql):常用于執(zhí)行DML和DDL語(yǔ)句,返回值為int型。執(zhí)行DML語(yǔ)句時(shí)返回受SQL語(yǔ)句影響的數(shù)據(jù)行數(shù);執(zhí)行DDL語(yǔ)句返回0。當(dāng)Connection對(duì)象處于默認(rèn)狀態(tài)時(shí),所有Statement對(duì)象都是自動(dòng)執(zhí)行的,也就是說(shuō),當(dāng)Statement語(yǔ)句對(duì)象執(zhí)行SQL語(yǔ)句時(shí),該SQL語(yǔ)句馬上提交數(shù)據(jù)庫(kù)并返回結(jié)果。如果將連接修改為手動(dòng)提交的事務(wù)模式,則只有執(zhí)行commit()方法時(shí),才會(huì)提交相應(yīng)的數(shù)據(jù)庫(kù)操作。Statement對(duì)象使用完畢后,最好使用close()方法將其關(guān)閉。如果要執(zhí)行動(dòng)態(tài)SQL語(yǔ)句,可使用PreparedStatement接口類對(duì)象,該接口繼承自Statement接口,具有Statement接口的所有方法。由于PreparedStatement實(shí)例對(duì)象保存有已被預(yù)編譯的SQL語(yǔ)句,因此執(zhí)行速度比Statement對(duì)象快。通過(guò)調(diào)用Connection接口對(duì)象的preparedStatement()方法可獲得PreparedStatement對(duì)象,語(yǔ)法格式如下:Connectionconn=DriverManager.getConnection(url,"user","password");PreparedStatementpstmt=conn.preparedStatement(Stringsql);從上面的語(yǔ)法格式可以看到,使用preparedStatement()方法創(chuàng)建PreparedStatement對(duì)象時(shí),需要SQL命令字符串作為參數(shù),以實(shí)現(xiàn)SQL命令預(yù)編譯。在SQL命令中可以包含一個(gè)或多個(gè)IN參數(shù),也可以使用“?”作為占位符。在調(diào)用executeQuery()或executeUpdate()方法之前,使用如表10-1所示的setXxx()方法為占位符賦值。表10-1PreparedStatement接口常用的方法方法說(shuō)明setInt(intindex,intk)將SQL命令中出現(xiàn)次序?yàn)閕ndex的參數(shù)設(shè)置為int值ksetFloat(intindex,floatk)將SQL命令中出現(xiàn)次序?yàn)閕ndex的參數(shù)設(shè)置為float值ksetLong(intindex,longk)將SQL命令中出現(xiàn)次序?yàn)閕ndex的參數(shù)設(shè)置為long值ksetDouble(intindex,doublek)將SQL命令中出現(xiàn)次序?yàn)閕ndex的參數(shù)設(shè)置為double值ksetDate(intindex,datek)將SQL命令中出現(xiàn)次序?yàn)閕ndex的參數(shù)設(shè)置為date值k續(xù)表方法說(shuō)明setString(intindex,Strings)將SQL命令中出現(xiàn)次序?yàn)閕ndex的參數(shù)設(shè)置為String值ssetNull(intindex,intsqlType)將SQL命令中出現(xiàn)次序?yàn)閕ndex的參數(shù)設(shè)置為SQLNULL例如,下面的程序段利用PreparedStatement對(duì)象在score表中插入一條記錄:try{//包含4個(gè)占位符的SQL命令Stringsql="insertintoscore(name,Math,Chinese,English)values(?,?,?,?)";//創(chuàng)建PreparedStatement對(duì)象pstmtPreparedStatementpstmt=conn.preparedStatement(sql); //為占位符賦值pstmt.setString(1,"Jeson"); //為name字段賦值pstmt.setInt(2,85); //為Math字段賦值pstmt.setInt(3,92); //為Chinese字段賦值pstmt.setInt(4,94); //為English字段賦值pstmt.executeUpdate(); //執(zhí)行插入操作}catch(IOExceptione){}2.返回結(jié)果集如果執(zhí)行的SQL語(yǔ)句是查詢語(yǔ)句,將返回一個(gè)ResultSet對(duì)象存放查詢結(jié)果。ResultSet對(duì)象由按字段組織的數(shù)據(jù)行構(gòu)成,并具有指向當(dāng)前數(shù)據(jù)行的游標(biāo)。當(dāng)獲得一個(gè)ResultSet時(shí),游標(biāo)指向第一行記錄之前的位置,一次只能看到一個(gè)數(shù)據(jù)行。通過(guò)next()方法可移動(dòng)到下一個(gè)數(shù)據(jù)行,沒(méi)有下一行時(shí)返回false。獲得一行數(shù)據(jù)后,ResultSet對(duì)象可以使用如表10-2所示的getXxx()方法獲得字段值,方法的參數(shù)可以是字段的位置索引或者是字段的名稱。表10-2獲取結(jié)果集字段值的常用方法方法說(shuō)明getInt(intcolumnIndex)或getInt(StringcolumnLabel)以int形式返回ResultSet對(duì)象當(dāng)前數(shù)據(jù)行指定列的值。如果列值為NULL,返回0getFloat(intcolumnIndex)或getFloat(StringcolumnLabel)以float形式返回ResultSet對(duì)象當(dāng)前數(shù)據(jù)行指定列的值。如果列值為NULL,返回0getDate(intcolumnIndex)或getDate(StringcolumnLabel)以date形式返回ResultSet對(duì)象當(dāng)前數(shù)據(jù)行指定列的值。如果列值為NULL,返回null

續(xù)表方法說(shuō)明getBoolean(intcolumnIndex)或getBoolean(StringcolumnLabel)以Boolean形式返回ResultSet對(duì)象當(dāng)前數(shù)據(jù)行指定列的值。如果列值為NULL,返回nullgetString(intcolumnIndex)或getString(StringcolumnLabel)以String形式返回ResultSet對(duì)象當(dāng)前數(shù)據(jù)行指定列的值。如果列值為NULL,返回null默認(rèn)情況下,ResultSet的游標(biāo)只能向下一行單向移動(dòng),如果要在結(jié)果集中向前移動(dòng)、或顯示結(jié)果集指定的某條記錄,則需要調(diào)用數(shù)據(jù)庫(kù)連接對(duì)象的createStatement(inttype,intconcurrency)方法創(chuàng)建一個(gè)Statement對(duì)象,然后執(zhí)行SQL語(yǔ)句返回一個(gè)可以滾動(dòng)的結(jié)果集。語(yǔ)法格式如下:Statementst=conn.createStatement(inttype,intconcurrency);ResultSetrs=st.executeQuery(Stringsql);其中,type的取值(如表10-3所示)決定滾動(dòng)方式;concurrency取值(如表10-4所示)決定是否可以用結(jié)果集更新數(shù)據(jù)庫(kù)。表10-3參數(shù)type的取值值含義ResultSet.TYPE_FORWORD_ONLY游標(biāo)只能向下移動(dòng)ResultSet.TYPE_SCROLL_INSENSITIV

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論