Java学习——复习 第七天多线程、网络通信、JDBC操作数据库
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();
}
}
}