Java學習——複習 第七天多線程、網絡通信、JDBC操作數據庫
13 多線程
(1)線程簡介—Java語言提供了併發機制,程序員可以在程序中執行多個線程,每一個線程完成一個功能,並與其它線程併發執行,這種機制被稱爲多線程。一個線程中可以同時包含多個線程,系統會分配給每個進程一點CPU時間片。
(2)實現線程的兩種方式
2.1繼承Thread類—通過繼承Thread類進行創建線程步驟
1)創建一個繼承自Thread類的子類
2)覆寫Thread類的run方法。
3)創建線程類的一個對象
4)通過線程類的對象調用start方法啓動線程(啓動後會自動調用覆寫的run方法執行線程)
Thread類常用構造方法:
publicThread();創建一個新的線程對象
publicThread(String threadName):創建一個名稱爲threadName的線程對象。
對線程進行操作使用以下方法
Thread類常用方法 | |
方法 | 說明 |
interrupt() | 中斷線程 |
join() | 等待該線程終止 |
join(long millis) | 等待該線程終止的時間最長爲millis毫秒 |
run() | 如果該線程是使用獨立的Runnable運行對象構造的,則調用該Runnable對象的run方法;否則,該方法不執行任何操作並返回 |
setPrinrity(int newPriority) | 更改線程的優先級 |
sleep(long millis) | 在制定的毫秒數內讓當前正在執行的線程休眠(暫停執行) |
start() | 使該線程開始執行:Java虛擬機調用該線程的run方法 |
yield() | 暫停當前正在執行的線程對象,並執行其他線程 |
代碼實現:
public classThreadTest extends Thread { // 指定類繼承Thread類
privateint count = 10;
publicvoid run() { // 重寫run()方法
while(true) {
System.out.print(count+ " "); // 打印count變量
if(--count == 0) { // 使count變量自減,當自減爲0時,退出循環
return;
}
}
}
publicstatic void main(String[] args) {
ThreadTesttest = new ThreadTest();// 創建線程對象
test.start();//啓動線程
}
}
運行結果:
10 9 8 7 6 5 43 2 1
2.2實現Runnable接口—如果繼承其他類可以通過Runnable接口實現接口
實現Runnable接口需要創建一個Thread對象,並將Runnable對象與Thread對象相關聯
1)public Thread(Runnabletarget):分配新的Thread對象,以便將target作爲其運行對象
2)publicThrend(Runnable target,String name):分配新的Thread對象,以便將target作爲其運行對象,將指定的name作爲其名稱
使用Runnable接口啓動新的線程的步驟
1)建立Runnable對象
2)使用參數爲Runnable對象的構造方法創建Thread實例
3)調用start()方法啓動線程
代碼實現:
public classSwingAndThread extends JFrame {
privateJLabel jl = new JLabel(); // 聲明JLabel對象
privatestatic Thread t; // 聲明線程對象
privateint count = 0; // 聲明計數變量
privateContainer container = getContentPane(); // 聲明容器
publicSwingAndThread() {
setBounds(300,200, 250, 100); // 絕對定位窗體大小與位置
container.setLayout(null);// 使窗體不使用任何佈局管理器
try{
URLurl = SwingAndThread.class.getResource("1.gif"); // 獲取圖片的URL
Iconicon = new ImageIcon(url);// 實例化一個Icon
jl.setIcon(icon);// 將圖標放置在標籤中
}catch (NullPointerException ex) {
System.out.println("圖片不存在,請將1.gif拷貝到當前目錄下!");
return;
}
//設置圖片在標籤的最左方
jl.setHorizontalAlignment(SwingConstants.LEFT);
jl.setBounds(10,10, 200, 50); // 設置標籤的位置與大小
jl.setOpaque(true);
t= new Thread(new Roll());
t.start();// 啓動線程
container.add(jl);// 將標籤添加到容器中
setVisible(true);// 使窗體可見
//設置窗體的關閉方式
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
}
classRoll implements Runnable {// 定義內部類,實現Runnable接口
@Override
publicvoid run() {
while (count <= 200) { // 設置循環條件
//將標籤的橫座標用變量表示
jl.setBounds(count,10, 200, 50);
try{
Thread.sleep(1000);// 使線程休眠1000毫秒
}catch (Exception e) {
e.printStackTrace();
}
count+= 4; // 使橫座標每次增加4
if(count == 200) {
// 當圖標到達標籤的最右邊時,使其回到標籤最左邊
count= 10;
}
}
}
}
publicstatic void main(String[] args) {
newSwingAndThread(); // 實例化一個SwingAndThread對象
}
}
運行結果:這是一個swing窗口,做的圖標的循環滾動,注意要把一個圖片放在當前目錄下
(3)線程的生命週期
線程的整個生命週期,包括五種狀態,分別是出生狀態、就緒狀態、運行狀態、暫停狀態(包括休眠、等待和阻塞等)和死亡狀態
(4)操作線程的方法
4.1線程的休眠—sleep()方法可以使線程進入休眠狀態,但需要一個以毫秒爲單位的參數,通常在run()方法中循環使用。
代碼實現:
public classSleepMethodTest extends JFrame {
/**
*
*/
privatestatic final long serialVersionUID = 1L;
privateThread t;
//定義顏色數組
privatestatic Color[] color = { Color.BLACK, Color.BLUE,Color.CYAN, Color.GREEN, Color.ORANGE,Color.YELLOW,
Color.RED,Color.PINK, Color.LIGHT_GRAY };
privatestatic final Random rand = new Random();// 創建隨機對象
privatestatic Color getC() {// 獲取隨機顏色值的方法
//隨機產生一個color數組長度範圍內的數字,以此爲索引獲取顏色
returncolor[rand.nextInt(color.length)];
}
publicSleepMethodTest() {
t= new Thread(new Draw());// 創建匿名線程對象
t.start();//啓動線程
}
classDraw implements Runnable {//定義內部類,用來在窗體中繪製線條
intx = 30;// 定義初始座標
inty = 50;
publicvoid run() {// 覆蓋線程接口方法
while(true) {// 無限循環
try{
Thread.sleep(100);//線程休眠0.1秒
}catch (InterruptedException e) {
e.printStackTrace();
}
//獲取組件繪圖上下文對象
Graphicsgraphics = getGraphics();
graphics.setColor(getC(1));//設置繪圖顏色
//繪製直線並遞增垂直座標
graphics.drawLine(x,y, 100, y++);
if(y >= 80) {
y= 50;
}
}
}
privateColor getC(int i) {
//TODO 自動生成的方法存根
returnnull;
}
}
publicstatic void main(String[] args) {
init(newSleepMethodTest(), 100, 100);
}
//初始化程序界面的方法
publicstatic void init(JFrame frame, int width, intheight) {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(width,height);
frame.setVisible(true);
}
}
4.2線程的加入—當某個線程使用join()方法加入另一個線程時,另一個線程會等該線程執行完畢後再繼續執行。
代碼實現:
import java.awt.*;
import javax.swing.*;
public class JoinTest extends JFrame {
/**
*
*/
privatestatic final long serialVersionUID = 1L;
privateThread threadA; // 定義兩個線程
privateThread threadB;
finalJProgressBar progressBar = new JProgressBar(); // 定義兩個進度條組件
finalJProgressBar progressBar2 = new JProgressBar();
intcount = 0;
publicstatic void main(String[] args) {
init(newJoinTest(), 100, 100);
}
publicJoinTest() {
super();
//將進度條設置在窗體最北面
getContentPane().add(progressBar,BorderLayout.NORTH);
//將進度條設置在窗體最南面
getContentPane().add(progressBar2,BorderLayout.SOUTH);
progressBar.setStringPainted(true);// 設置進度條顯示數字字符
progressBar2.setStringPainted(true);
//使用匿名內部類形式初始化Thread實例子
threadA= new Thread(new Runnable() {
intcount = 0;
publicvoid run() { // 重寫run()方法
while(true) {
progressBar.setValue(++count);// 設置進度條的當前值
try{
Thread.sleep(100);// 使線程A休眠100毫秒
threadB.join();// 使線程B調用join()方法
}catch (Exception e) {
e.printStackTrace();
}
}
}
});
threadA.start();// 啓動線程A
threadB= new Thread(new Runnable() {
intcount = 0;
publicvoid run() {
while(true) {
progressBar2.setValue(++count);// 設置進度條的當前值
try{
Thread.sleep(100);// 使線程B休眠100毫秒
}catch (Exception e) {
e.printStackTrace();
}
if(count == 100) // 當count變量增長爲100時
break;// 跳出循環
}
}
});
threadB.start();// 啓動線程B
}
//設置窗體各種屬性方法
publicstatic void init(JFrame frame, int width, int height) {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(width,height);
frame.setVisible(true);
}
}
運行結果:
4.3線程的中斷—在run()方法中使用無限循環形式,然後使用一個布爾型標記控制循環的停止
代碼實現:
import java.awt.*;
import javax.swing.*;
public class InterruptedSwing extendsJFrame {
Threadthread;
publicstatic void main(String[] args) {
newInterruptedSwing();
}
publicInterruptedSwing() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//關閉窗體後停止程序
setSize(100,100);// 設定窗體寬高
setVisible(true);//窗體可見
finalJProgressBar progressBar = new JProgressBar(); // 創建進度條
//將進度條放置在窗體合適位置
getContentPane().add(progressBar,BorderLayout.NORTH);
progressBar.setStringPainted(true);// 設置進度條上顯示數字
thread= new Thread() {// 使用匿名內部類方式創建線程對象
intcount = 0;
publicvoid run() {
while(true) {
progressBar.setValue(++count);// 設置進度條的當前值
try{
if(count == 50) {
interrupt();//執行線程停止方法
}
Thread.sleep(100);// 使線程休眠100豪秒
}catch (InterruptedException e) {// 捕捉InterruptedException異常
System.out.println("當前線程被中斷");
break;
}
}
}
};
thread.start();// 啓動線程
}
}
運行結果:
當進度條走到設定值後終止進程
(5)線程的優先級
在Thread類中包含成員變量代表線程的優先級,如Thread.MIN_PRIORITY(常數1),Thread.MAX_PRIORITY(常數10),Thread.NORM_PRIORITY(常數5)。其中每個線程的優先級都在Thread.MIN_PRIORITY~Thread.MAX_PRIORITY之間,在默認情況下優先級都是Thread.NORM_PRIORITY。每個新產生的線程都繼承父線程的優先級。可以使用setPriority()方法調整,且該方法設置的優先級必須在1~10之間,不然則產生IllegalArgumentException異常。
代碼實現:
classPriority extends Thread {
privateString threadName; // 線程的名稱
privateString output; // 控制檯輸出的信息
publicPriority(String threadName, String output) { // 以線程名、控制檯輸出的信息爲參數的構造方法,利用構造方法初始化變量
this.threadName= threadName;
this.output= output;
}
@Override
publicvoid run() { // 線程要執行的任務
System.out.print(threadName+ ":" + output + " ");
}
}
public classPriorityTest {
publicstatic void main(String[] args) {
for(int i = 0; i < 5; i++) { // 通過循環控制啓動線程的次數
/**
* 創建4個以線程名、輸出信息爲參數的線程類子類的對象 並分別設置這4個線程的優先級
*/
Prioritytest1 = new Priority("加", "+");
test1.setPriority(Thread.MIN_PRIORITY);//設置優先級最低
Prioritytest2 = new Priority("減", "-");
test2.setPriority(3);//以數字設置優先級
Prioritytest3 = new Priority("乘","×");
test3.setPriority(8);//以數字設置優先級
Prioritytest4 = new Priority("除","÷");
test4.setPriority(Thread.MAX_PRIORITY);//設置優先級最高
//啓動線程
test1.start();
test2.start();
test3.start();
test4.start();
System.out.println();//換行
}
}
}
運行結果:
加:+ 減:- 除:÷ 乘:×
乘:× 除:÷ 減:- 加:+
乘:× 除:÷ 減:- 乘:×
除:÷ 加:+ 減:- 加:+ 加:+ 減:- 除:÷ 乘:×
從運行結果可以看出設置線程的優先級只是增加或減少了線程優先執行的概率,並不能保證線程一定會優先或者延後處理。
(6)線程的同步—java中提供了線程同步機制來防止資源訪問的衝突。
6.1線程的安全—來源於兩個線程同時操作存取單一對象數據。
public classThreadSafeTest implements Runnable {// 實現Runnable接口
intnum = 10; // 設置當前總票數
publicvoid run() {
while(true) {// 設置無限循環
if(num > 0) {// 判斷當前票數是否大於0
try{
Thread.sleep(100);//使當前線程休眠100毫秒
}catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "----票數" + num--);// 票數減1
}
}
}
publicstatic void main(String[] args) {
ThreadSafeTestt = new ThreadSafeTest(); // 實例化類對象
ThreadtA = new Thread(t, "線程一"); // 以該類對象分別實例化4個線程
ThreadtB = new Thread(t, "線程二");
ThreadtC = new Thread(t, "線程三");
ThreadtD = new Thread(t, "線程四");
tA.start();// 分別啓動線程
tB.start();
tC.start();
tD.start();
}
}
運行結果:
線程一----票數9
線程二----票數10
線程三----票數8
線程四----票數7
線程一----票數6
線程三----票數5
線程二----票數4
線程四----票數5
線程一----票數3
線程三----票數2
線程四----票數0
線程二----票數1
線程一----票數-1
6.2線程同步機制
1)同步塊—Java中提供了同步機制,可以有效地防止資源衝突。同步機制使用synchronize關鍵字,使用該關鍵字包含的代碼塊稱之爲同步塊,也稱臨界區。
代碼實現:
public classSynchronizedTest implements Runnable {
intnum = 10; // 設置當前總票數
publicvoid run() {
while(true) {// 設置無限循環
synchronized(this) {// 設置同步代碼塊
if(num > 0) {// 判斷當前票數是否大於0
try{
Thread.sleep(1000);//使當前線程休眠100毫秒
}catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"----票數"+num--);// 票數減1
}
}
}
}
publicstatic void main(String[] args) {
SynchronizedTestt = new SynchronizedTest();// 實例化類對象
ThreadtA = new Thread(t,"線程一");// 以該類對象分別實例化4個線程
ThreadtB = new Thread(t,"線程二");
ThreadtC = new Thread(t,"線程三");
ThreadtD = new Thread(t,"線程四");
tA.start();//分別啓動線程
tB.start();
tC.start();
tD.start();
}
}
運行結果:
線程一----票數10
線程一----票數9
線程四----票數8
線程三----票數7
線程二----票數6
線程二----票數5
線程二----票數4
線程二----票數3
線程三----票數2
線程三----票數1
2)同步方法—就是在方法前面使用synchronized關鍵字修飾的方法
代碼:將上一個案例的代碼稍作如下修改
public synchronizedvoid doit() {
//TODO 自動生成的方法存根
if(num>0){
try{
Thread.sleep(100);//使當前線程休眠10毫秒
}catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"----票數"+num--);// 票數減1
}
}
publicvoid run() {
while(true) {// 設置無限循環
doit();
}
}
運行結果和用同步塊的結果是一樣的。
(7)線程的暫停與恢復—是通過頂級父類的Object提供的wait()方法和notify()方法實現,wait表示暫停線程,notify表示恢復線程
代碼實現:
public classThreadSuspendFrame extends JFrame {
privateJLabel label;// 顯示數字的標籤
privateThreadSuspend t;// 自定義線程類
publicThreadSuspendFrame() {
setTitle("手機號碼抽獎");//窗口標題
setDefaultCloseOperation(EXIT_ON_CLOSE);//窗口關閉規則:窗口關閉則停止程序
setBounds(200,200, 300, 150);// 設置窗口座標和大小
label= new JLabel("0");// 實例化標籤,初始值爲0
label.setHorizontalAlignment(SwingConstants.CENTER);//標籤文字居中
label.setFont(newFont("宋體", Font.PLAIN, 42));// 標籤使用99號字
getContentPane().add(label,BorderLayout.CENTER);// 將標籤放入窗口容器的中間區域
JButtonbtn = new JButton("暫停");// 創建暫停按鈕
getContentPane().add(btn,BorderLayout.SOUTH);// 將按鈕放入窗口容器的南部區域
t= new ThreadSuspend();// 實例化自定義線程類
t.start();//啓動線程
btn.addActionListener(newActionListener() {// 按鈕添加事件監聽
publicvoid actionPerformed(ActionEvent e) {
StringbtnText = btn.getText();// 獲取按鈕文本
if(btnText.equals("暫停")) {// 如果按鈕的文本爲“暫停”
t.toSuspend();//自定義線程暫停
btn.setText("繼續");// 將按鈕文本改爲“繼續”
}else {
t.toRun();//自定義線程繼續運行
btn.setText("暫停");// 將按鈕文本改爲“暫停”
}
}
});
setVisible(true);//設置窗口可見
}
/**
* 在主類中創建內部類:自定義線程類,繼承Thread線程類
*/
classThreadSuspend extends Thread {
/**
* 線程掛起狀態,若suspend爲false,線程會正常運行;若suspend爲true,則線程會處於掛起狀態
*/
privateboolean suspend = false;
/**
* (線程安全的)線程暫停方法
*/
publicsynchronized void toSuspend() {
suspend= true;
}
/**
* (線程安全的)線程恢復運行方法,除了將suspend變爲false,同時使用超級父類Object類提供的notify()方法喚醒線程
*/
publicsynchronized void toRun() {
suspend= false;
notify();
}
@Override
publicvoid run() {
//定義中獎池號碼
String[]phoneNums={"13610780204","13847928544","18457839454","18423098757","17947928544","19867534533"};
while(true) {// run方法中的代碼無限運行
synchronized(this) {// 創建線程掛起區,線程加鎖對象爲this
while(suspend) {// 判斷線程是否要暫停
try{
wait();//超級父類Object類提供的等待方法
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
label.setText(phoneNums[(newRandom()).nextInt(phoneNums.length)]);// 修改標籤中的值
}
}
}
publicstatic void main(String[] args) {
newThreadSuspendFrame();
}
}
運行結果:
14網絡通信
(1)網絡程序設計基礎—所謂網絡程序設計就是指編寫與其他計算機進行通信的程序
1.1局域網與因特網
1.2網絡協議
1.3端口和套接字
(2)IP地址封裝—java中提供了IP地址的封裝類InetAddress,它位於java.net包中,主要封裝了IP地址,並提供相關方法,獲取IP地址,主機地址等
代碼實現:
public classIpToName {
publicstatic void main(String args[]) {
StringIP = null;
InetAddresshost;// 創建InetAddress對象
try{
host= InetAddress.getLocalHost();// 實例化InetAddress對象,用來獲取本節的IP地址相關信息
Stringlocalname = host.getHostName(); // 獲取本機名
Stringlocalip = host.getHostAddress(); // 獲取本機IP地址
System.out.println("本機名:" +localname + " 本機IP地址:" +localip); // 將本機名和IP地址輸出
}catch (UnknownHostException e) {// 捕獲未知主機異常
e.printStackTrace();
}
for(int i = 50; i <= 70; i++) {
IP= "192.168.1." + i; // 生成IP字符串
try{
host= InetAddress.getByName(IP); // 獲取IP封裝對象
if(host.isReachable(2000)) { // 用2秒的時間測試IP是否可達
StringhostName = host.getHostName();// 獲取指定IP地址的主機名
System.out.println("IP地址 " +IP + " 的主機名稱是:" + hostName);
}
}catch (UnknownHostException e) { // 捕獲未知主機異常
e.printStackTrace();
}catch (IOException e) { // 捕獲輸入輸出異常
e.printStackTrace();
}
}
System.out.println("搜索完畢。");
}
}
結果:
本機名:DESKTOP-GBPN9VK 本機IP地址:192.168.1.104
搜索完畢。
因爲在同一局域網內只有我這一臺機器所以沒有查找到其他機器。·
(3)TCP程序設計
3.1ServerSocket服務器端—ServerSocket類用於表示服務器的套接字,服務器套接字一次可以與一個套接字連接。
3.2Socket客戶端—調用ServerSocket類的accpt()方法會返回一個與客戶端Socket對象
java.net包中的Socket類用於表示客戶端套接字,它採用TCP建立計算機之間的連接幷包含了java語言中所有對TCP有關的操作方法。
Socket常用構造方法如下
3.3TCP網絡程序實例
先新建一個服務器端類:
public classServer {
publicstatic void main(String[] args) throws IOException {
ServerSocketserver = new ServerSocket(1100);// 創建服務器端對象,監聽1100
System.out.println("服務器啓動成功,等待用戶接入…");
//等待用戶接入,直到有用戶接入爲止,Socket對象表示客戶端
Socketclient = server.accept();
//得到接入客戶端的IP地址
System.out.println("有客戶端接入,客戶IP:" +client.getInetAddress());
InputStreamin = client.getInputStream();// 從客戶端生成網絡輸入流,用於接收來自網絡的數據
OutputStreamout = client.getOutputStream();// 從客戶端生成網絡輸出流,用來把數據發送到網絡上
byte[]bt = new byte[1024];// 定義一個字節數組,用來存儲網絡數據
intlen = in.read(bt);// 將網絡數據寫入字節數組
Stringdata = new String(bt, 0, len);// 將網絡數據轉換爲字符串數據
System.out.println("來自客戶端的消息:"+ data);
out.write("我是服務器,歡迎光臨".getBytes());//服務器端數據發送(以字節數組形勢)
client.close();//關閉套接字
}
}
在創建一個客戶端類:
public classClient {
publicstatic void main(String[] args) throwsUnknownHostException, IOException {
Socketclient = new Socket("127.0.0.1", 1100);
System.out.println("連接服務器成功");
InputStreamin = client.getInputStream();// 從客戶端生成網絡輸入流,用於接收來自網絡的數據
OutputStreamout = client.getOutputStream();// 從客戶端生成網絡輸出流,用來把數據發送到網絡上
out.write("我是客戶端,我來了".getBytes());//客戶端數據發送(以字節數組形勢)
byte[]bt = new byte[1024];// 定義一個字節數組,用來存儲網絡數據
intlen = in.read(bt);// 將網絡數據寫入字節數組
Stringdata = new String(bt, 0, len);// 將網絡數據轉換爲字符串數據
System.out.println("來自服務器的消息:"+ data);
client.close();//關閉套接字
}
}
運行結果:
服務器端顯示:
服務器啓動成功,等待用戶接入…
有客戶端接入,客戶IP:/127.0.0.1
來自客戶端的消息:我是客戶端,我來了
客戶端顯示:
連接服務器成功
來自服務器的消息:我是服務器,歡迎光臨
(4)UDP程序設計
——是用戶數據報協議,它是網絡信息傳輸的另一種形式,UDP與TCP不同在於UDP信息傳輸更快但不提供可靠保障。
UDP通信的基本模式如下:
1)將數據打包(稱爲數據包)然後將數據發往目的地
2)接收到別人發來的數據包,然後查看數據包
4.1使用Java進行UDP程序設計
4.2DatagramPacket類
DategramPacket類常用方法:
4.3DatagramSocket類
DatagramSocket常用方法:
4.4UDP網絡程序實例
創建一個廣播主機類,無限播放廣播:
public classBroadCast extends Thread { // 創建類。該類爲多線程執行程序
Stringbroadcast = "節目預報:八點有大型晚會,請收聽";
intport = 9898; // 定義端口,通過該端口進行數據的發送和接收
InetAddressiaddress = null; // 創建InetAddress對象,用來指定主機所在多播組
MulticastSocketsocket = null; // 聲明多點廣播套接字
BroadCast(){ // 構造方法
try{
//實例化InetAddress,指定主機所在的組,組的範圍爲:224.0.0.0~224.255.255.255
iaddress= InetAddress.getByName("224.255.10.0");
socket= new MulticastSocket(port); // 實例化多點廣播套接字
socket.setTimeToLive(1);// 指定發送範圍是本地網絡
socket.joinGroup(iaddress);// 加入廣播組
}catch (Exception e) {
e.printStackTrace();// 輸出異常信息
}
}
publicvoid run() { // run()方法
while(true) {
DatagramPacketpacket = null; // 聲明DatagramPacket對象,作爲要發送的數據包
bytedata[] = broadcast.getBytes(); // 聲明字節數組,存儲要發送的內容
//生成要發送的數據包
packet= new DatagramPacket(data, data.length, iaddress, port);
System.out.println(newString(data)); // 將廣播信息輸出
try{
socket.send(packet);// 發送數據
sleep(3000);// 線程休眠
}catch (Exception e) {
e.printStackTrace();// 輸出異常信息
}
}
}
publicstatic void main(String[] args) { // 主方法
BroadCastbCast = new BroadCast(); // 創建本類對象
bCast.start();// 啓動線程
}
}
創建一個接收廣播程序:
publicReceive() { // 構造方法
super("廣播數據報");// 設置窗體標題
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);//設置窗體關閉方式
thread= new Thread(this);// 實例化線程對象
ince.addActionListener(this);// 綁定"開始接收"按鈕的單擊事件
stop.addActionListener(this);// 綁定 "停止接收"按鈕的單擊事件
inceAr.setForeground(Color.blue);// 指定提示文本域中文字顏色
JPanelnorth = new JPanel(); // 創建Jpane對象
north.add(ince);// 將按鈕添加到面板north上
north.add(stop);
add(north,BorderLayout.NORTH); // 將north放置在窗體的上部
JPanelcenter = new JPanel(); // 創建面板對象center
center.setLayout(newGridLayout(1, 2)); // 設置面板佈局
center.add(inceAr);// 將文本域添加到面板上
finalJScrollPane scrollPane = new JScrollPane();
center.add(scrollPane);
scrollPane.setViewportView(inced);
add(center,BorderLayout.CENTER); // 設置面板佈局
validate();// 刷新
port= 9898; // 設置端口號
try{
group= InetAddress.getByName("224.255.10.0"); // 指定接收地址
socket= new MulticastSocket(port); // 綁定多點廣播套接字
socket.joinGroup(group);// 加入廣播組
}catch (Exception e) {
e.printStackTrace();// 輸出異常信息
}
setBounds(100,50, 360, 380); // 設置佈局
setVisible(true);// 將窗體設置爲顯示狀態
}
publicvoid run() { // run()方法
while(true) {
bytedata[] = new byte[1024]; // 創建byte數組,用來存儲接收到的數據
DatagramPacketpacket = null; // 創建DatagramPacket對象
//待接收的數據包
packet= new DatagramPacket(data, data.length, group, port);
try{
socket.receive(packet);// 接收數據包
Stringmessage = new String(packet.getData(), 0, packet.getLength()); // 獲取數據包中內容,轉換爲字符串
//將接收內容顯示在文本域中
inceAr.setText("正在接收的內容:\n"+ message);
inced.append(message+ "\n"); // 每條信息爲一行
}catch (Exception e) {
e.printStackTrace();// 輸出異常信息
}
if(b == true) { // 當變量等於true時,退出循環
break;
}
}
}
publicvoid actionPerformed(ActionEvent e) { // 按鈕的單擊事件
if(e.getSource() == ince) { // 如果是"開始接收"按鈕
ince.setBackground(Color.red);// 設置按鈕顏色
stop.setBackground(Color.yellow);
if(!(thread.isAlive())) { // 如線程不處於“新建狀態”
thread= new Thread(this); // 實例化Thread對象
}
thread.start();// 啓動線程
b= false; // 設置變量值爲false,表示接收數據
}
if(e.getSource() == stop) { // 如果是"停止接收"按鈕
ince.setBackground(Color.yellow);// 設置按鈕顏色
stop.setBackground(Color.red);
b= true; // 設置變量值爲true,表示停止接收數據
}
}
publicstatic void main(String[] args) { // 主方法
Receiverec = new Receive(); // 創建本類對象
rec.setSize(460,200); // 設置窗體大小
}
}
運行結果:
廣播端:
接收端:
15使用JDBC操作數據庫
(1)JDBC概述—MySQL、SqlServer、Oracle等多種適用於不同場景的數據庫
1.1數據庫基礎—數據庫是一種存儲結構,他允許使用各種格式輸入、處理和檢索數據。而使用JDBC操作數據庫,SQL語句是必不可少的,下面簡單的回顧下SQL語法:
1.2JDBC簡介
(2)JDBC中常用的類和接口
2.1DriverManager類—用來管理數據庫中的所有驅動程序,他是JDBC的管理層,作用於用戶和驅動程序之間,跟蹤可用的驅動程序,並在數據庫的驅動程序之間建立連接。在java操縱數據庫之間須加載數據庫
2.2 Connection接口--用於與特定的數據庫的連接
2.3 Statement接口—用於在已經建立連接的基礎上向數據庫發送SQL語句
2.4 PreparedStatement接口—繼承自Statement接口,用來執行動態的SQL語句。
2.5 CallableStatement接口—繼承並擴展PreparedStatement接口,用來執行對數據庫存儲過程的調用
2.6 ResultSet接口
(3)數據庫操作
3.1連接數據庫—要訪問數據庫首先要加載數據的驅動程序(僅在第一次訪問時加載),然後每次訪問數據時創建一個Connection對象,接着執行操作數據庫的SQL語句,最後完成數據庫操作後銷燬前面創建的Connection對象,釋放與數據庫的連接
代碼實現
public classConn { // 創建類Conn
Connectioncon; // 聲明Connection對象
publicConnection getConnection() { // 建立返回值爲Connection的方法
try{ // 加載數據庫驅動類
Class.forName("com.mysql.jdbc.Driver");
System.out.println("數據庫驅動加載成功");
}catch (ClassNotFoundException e) {
e.printStackTrace();
}
try{ // 通過訪問數據庫的URL獲取數據庫連接對象
con= DriverManager.getConnection("jdbc:mysql:"
+"//127.0.0.1:3306/test", "root", "123456");
System.out.println("數據庫連接成功");
}catch (SQLException e) {
e.printStackTrace();
}
returncon; // 按方法要求返回一個Connection對象
}
publicstatic void main(String[] args) { // 主方法
Connc = new Conn(); // 創建本類對象
c.getConnection();// 調用連接數據庫的方法
}
}
3.2數據查詢—通過Statement接口和ResultSet接口實現,Statement接口用於執行SQL語句,ResultSet接口用於存儲查詢結果
代碼實現
public classGradation { // 創建類
staticConnection con; // 聲明Connection對象
staticStatement sql; // 聲明Statement對象
staticResultSet res; // 聲明ResultSet對象
publicConnection getConnection() { // 連接數據庫方法
try{
Class.forName("com.mysql.jdbc.Driver");
con= DriverManager.getConnection("jdbc:mysql:"
+"//127.0.0.1:3306/test", "root", "root");
}catch (Exception e) {
e.printStackTrace();
}
returncon; // 返回Connection對象
}
publicstatic void main(String[] args) { // 主方法
Gradationc = new Gradation(); // 創建本類對象
con= c.getConnection(); // 與數據庫建立連接
try{
sql= con.createStatement(); // 實例化Statement對象
//執行SQL語句,返回結果集
res= sql.executeQuery("select * from tb_stu");
while(res.next()) { // 如果當前語句不是最後一條則進入循環
Stringid = res.getString("id"); // 獲取列名是"id"的字段值
//獲取列名是"name"的字段值
Stringname = res.getString("name");
//獲取列名是"sex"的字段值
Stringsex = res.getString("sex");
//獲取列名是"birthday"的字段值
Stringbirthday = res.getString("birthday");
System.out.print("編號:" + id);// 將列值輸出
System.out.print("姓名:" + name);
System.out.print("性別:" + sex);
System.out.println("生日:" + birthday);
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
3.3動態查詢—PreparedStatement對象執行動態查詢
代碼實現
public classPrep { // 創建類Perp
staticConnection con; // 聲明Connection對象
staticPreparedStatement sql; // 聲明預處理對象
staticResultSet res; // 聲明結果集對象
publicConnection getConnection() { // 與數據庫連接方法
try{
Class.forName("com.mysql.jdbc.Driver");
con= DriverManager.getConnection("jdbc:mysql:"
+"//127.0.0.1:3306/test", "root", "root");
}catch (Exception e) {
e.printStackTrace();
}
returncon; // 返回Connection對象
}
publicstatic void main(String[] args) { // 主方法
Prepc = new Prep(); // 創建本類對象
con= c.getConnection(); // 獲取與數據庫的連接
try{
//實例化預處理對象
sql= con.prepareStatement("select * from tb_stu"
+" where id = ?");
sql.setInt(1,4); // 設置參數
res= sql.executeQuery(); // 執行預處理語句
//如果當前記錄不是結果集中最後一行,則進入循環體
while(res.next()) {
Stringid = res.getString(1); // 獲取結果集中第一列的值
Stringname = res.getString("name"); // 獲取name列的列值
Stringsex = res.getString("sex"); // 獲取sex列的列值
//獲取birthday列的列值
Stringbirthday = res.getString("birthday");
System.out.print("編號:" + id);// 輸出信息
System.out.print("姓名:" + name);
System.out.print("性別:" + sex);
System.out.println("生日:" + birthday);
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
3.4添加、修改、刪除記錄
1.單條記錄操作--使用PreparedStatement對象進行操作處理
代碼實現
public classRenewal { // 創建類
staticConnection con; // 聲明Connection對象
staticPreparedStatement sql; // 聲明PreparedStatement對象
staticResultSet res; // 聲明ResultSet對象
publicConnection getConnection() {
try{
Class.forName("com.mysql.jdbc.Driver");
con= DriverManager.getConnection("jdbc:mysql:"
+"//127.0.0.1:3306/test", "root", "root");
}catch (Exception e) {
e.printStackTrace();
}
returncon;
}
publicstatic void main(String[] args) {
Renewalc = new Renewal(); // 創建本類對象
con= c.getConnection(); // 調用連接數據庫方法
try{
sql= con.prepareStatement("select * from tb_stu"); // 查詢數據庫
res= sql.executeQuery(); // 執行SQL語句
System.out.println("執行增加、修改、刪除前數據:");
while(res.next()) {
Stringid = res.getString(1);
Stringname = res.getString("name");
Stringsex = res.getString("sex");
Stringbirthday = res.getString("birthday"); // 遍歷查詢結果集
System.out.print("編號:" + id);
System.out.print("姓名:" + name);
System.out.print("性別:" + sex);
System.out.println("生日:" + birthday);
}
sql= con.prepareStatement("insert into tb_stu(name,sex,birthday)values(?,?,?)");
sql.setString(1,"張一"); // 預處理添加數據
sql.setString(2,"女");
sql.setString(3,"2012-12-1");
sql.executeUpdate();
sql= con.prepareStatement("update tb_stu set birthday "
+"= ? where id = ? ");
sql.setString(1,"2012-12-02"); // 更新數據
sql.setInt(2,1); // 更新數據
sql.executeUpdate();
Statementstmt = con.createStatement();
stmt.executeUpdate("deletefrom tb_stu where id = 1");
//查詢修改數據後的tb_stu表中數據
sql= con.prepareStatement("select * from tb_stu");
res= sql.executeQuery(); // 執行SQL語句
System.out.println("執行增加、修改、刪除後的數據:");
while(res.next()) {
Stringid = res.getString(1);
Stringname = res.getString("name");
Stringsex = res.getString("sex");
Stringbirthday = res.getString("birthday");
System.out.print("編號:" + id);
System.out.print("姓名:" + name);
System.out.print("性別:" + sex);
System.out.println("生日:" + birthday);
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
2.批量操作—使用PreparedStatement對象的批處理方法
代碼實現
public classBatchTest { // 創建類
staticConnection con; // 聲明Connection對象
staticPreparedStatement sql; // 聲明PreparedStatement對象
staticResultSet res; // 聲明ResultSet對象
publicConnection getConnection() {
try{
Class.forName("com.mysql.jdbc.Driver");
con= DriverManager.getConnection("jdbc:mysql:" + "//127.0.0.1:3306/test","root", "root");
}catch (Exception e) {
e.printStackTrace();
}
returncon;
}
publicstatic void main(String[] args) {
BatchTestc = new BatchTest(); // 創建本類對象
con= c.getConnection(); // 調用連接數據庫方法
try{
sql= con.prepareStatement("select * from tb_stu"); // 查詢數據庫
res= sql.executeQuery(); // 執行SQL語句
System.out.println("執行批量添加前數據:");
while(res.next()) {
Stringid = res.getString(1);
Stringname = res.getString("name");
Stringsex = res.getString("sex");
Stringbirthday = res.getString("birthday"); // 遍歷查詢結果集
System.out.print("編號:" + id);
System.out.print("姓名:" + name);
System.out.print("性別:" + sex);
System.out.println("生日:" + birthday);
}
String[][]records = { { "明日", "男", "2004-01-01" }, { "小科", "男", "1976-10-01"},
{"小申", "男", "1980-05-01" } };
sql= con.prepareStatement("insert into tb_stu(name,sex,birthday)values(?,?,?)");
sql.clearBatch();// 清空批處理命令
for(int i = 0; i < records.length; i++) {
sql.setString(1,records[i][0]);
sql.setString(2,records[i][1]);
sql.setString(3,records[i][2]);
sql.addBatch();// 將添加語句添加到批處理中
}
sql.executeBatch();// 批量執行批處理命令
//查詢修改數據後的tb_stu表中數據
sql= con.prepareStatement("select * from tb_stu");
res= sql.executeQuery(); // 執行SQL語句
System.out.println("執行批量添加後的數據:");
while(res.next()) {
Stringid = res.getString(1);
Stringname = res.getString("name");
Stringsex = res.getString("sex");
Stringbirthday = res.getString("birthday");
System.out.print("編號:" + id);
System.out.print("姓名:" + name);
System.out.print("性別:" + sex);
System.out.println("生日:" + birthday);
}
}catch (SQLException e) {
e.printStackTrace();
}finally {
try{
sql.close();//關閉數據庫
}catch (SQLException e) {
e.printStackTrace();
}
}
}
}
3.5調用存儲過程—通過使用存儲過程,不僅可以提高執行效率,而且安全性更高
代碼實現
public classCallableStatementTest { // 創建類
staticConnection con; // 聲明Connection對象
staticResultSet res; // 聲明ResultSet對象
publicConnection getConnection() {
try{
Class.forName("com.mysql.jdbc.Driver");
con= DriverManager.getConnection("jdbc:mysql:" + "//127.0.0.1:3306/test","root", "root");
}catch (Exception e) {
e.printStackTrace();
}
returncon;
}
publicstatic void main(String[] args) {
CallableStatementTestc = new CallableStatementTest(); // 創建本類對象
con= c.getConnection(); // 調用連接數據庫方法
try{
CallableStatementcallStatement = con.prepareCall("{call proc_GetInfo(?)}"); // 調用存儲過程
callStatement.setString(1,"張%"); // 爲存儲過程設置參數
res= callStatement.executeQuery(); // 執行存儲過程
System.out.println("調用存儲過程的結果:");
while(res.next()) {// 遍歷查詢結果
Stringid = res.getString("id");// 獲取編號
Stringname = res.getString("name");// 獲取姓名
Stringsex = res.getString("sex");// 獲取性別
Stringbirthday = res.getString("birthday");// 獲取生日
System.out.print("編號:" + id);
System.out.print("姓名:" + name);
System.out.print("性別:" + sex);
System.out.println("生日:" + birthday);
}
}catch (SQLException e) {
e.printStackTrace();
}
}
}