Java使用多线程和GUI实现购买火车票<集合>

简介: Java使用多线程和GUI实现购买火车票<集合>

绪:

  • 多线程与并发
  • 集合Map 、List
  • GUI(ecplise插件WindowBuilder)


DEMO——购票项目


项目要求

  1. 文本文件提供票的信息,车次、票价、始发站终点站、票数
  2. 启动程序,首先读取文件,放入TextArea中,如果车次较多,那么需要加滚动条(上述两步骤,用到的是文件读写IO)
  3. 输入车次,查找该车次,并显示信息
  1. 如何在文本域中找出相关信息,并且读出这一条信息——字符串操作
  1. 找到车次了,输入购买张数,点击购买,弹出新的窗口——新的窗口是启动了一个线程——多线程
  2. 在购买界面,显示购买车次的信息,和顾客进行确认。并且提供身份证信息
  1. 如果买多张,那么这些身份证信息,先保存到集合中(map),遍历集合,给每个身份证购买一张票
  1. 输出购买信息

实现步骤

需要创建一个车票javaBean类,和顾客javaBean类

来存储对应的车票信息和顾客信息


一、【车次.txt】【Person类】【Ticket类】

  • 【车次.txt】用来存储车票的信息
  • 【Person类】用来根据顾客的信息生成顾客的对象
  • 【Ticket类】生成车票的对象


【Person类】【Ticket类】都是两个JavaBean类


1.【车次.txt】

存储车票信息的.txt文件

K1528      石家庄      北  京      24.2      1
K1527      北  京      石家庄      24.2      2
K1526      石家庄      北  京      24.2      100
K1525      北  京      石家庄      24.2      200
K1524      石家庄      北  京      24.2      100
K1523      北  京      石家庄      24.2      200
K1522      石家庄      北  京      24.2      100
K1521      北  京      石家庄      24.2      200

2.【Person类】

用来根据顾客的信息生成顾客的对象

publicclassPerson {
privateStringname; // 姓名privateStringIDCard; // 身份证号privateStringticketname; // 车票名privateStringstart; // 起始站privateStringend; // 终点站privatedoubleprice; // 车票的价钱publicPerson() {
super();
    }
publicPerson(Stringname, StringiDCard) {
super();
this.name=name;
IDCard=iDCard;
    }
publicPerson(Stringname, StringiDCard, Stringticketname, Stringstart, Stringend, doubleprice) {
super();
this.name=name;
IDCard=iDCard;
this.ticketname=ticketname;
this.start=start;
this.end=end;
this.price=price;
    }
publicStringgetName() {
returnname;
    }
publicvoidsetName(Stringname) {
this.name=name;
    }
publicStringgetIDCard() {
returnIDCard;
    }
publicvoidsetIDCard(StringiDCard) {
IDCard=iDCard;
    }
publicStringgetTicketname() {
returnticketname;
    }
publicvoidsetTicketname(Stringticketname) {
this.ticketname=ticketname;
    }
publicStringgetStart() {
returnstart;
    }
publicvoidsetStart(Stringstart) {
this.start=start;
    }
publicStringgetEnd() {
returnend;
    }
publicvoidsetEnd(Stringend) {
this.end=end;
    }
publicdoublegetPrice() {
returnprice;
    }
publicvoidsetPrice(doubleprice) {
this.price=price;
    }
@OverridepublicStringtoString() {
return"Person [name="+name+", IDCard="+IDCard+", ticketname="+ticketname+", start="+start+", end="+end+", price="+price+"]";
    }
}

3.【Ticket类】

生成车票的对象

publicclassTicket {
privateStringcheci; // 车票名privateStringstart; // 起始站privateStringend; // 终点站privatedoubleprice; // 价格privateintnumber; // 数量publicTicket(Stringcheci, Stringstart, Stringend, doubleprice, intnumber) {
super();
this.checi=checi;
this.start=start;
this.end=end;
this.number=number;
this.price=price;
    }
publicTicket(Stringcheci, doubleprice, intnumber) {
super();
this.checi=checi;
this.price=price;
this.number=number;
    }
@OverridepublicStringtoString() {
return"Ticket [checi="+checi+", start="+start+", end="+end+", price="+price+", number="+number+"]\n";
    }
publicStringgetCheci() {
returncheci;
    }
publicvoidsetCheci(Stringcheci) {
this.checi=checi;
    }
publicStringgetStart() {
returnstart;
    }
publicvoidsetStart(Stringstart) {
this.start=start;
    }
publicStringgetEnd() {
returnend;
    }
publicvoidsetEnd(Stringend) {
this.end=end;
    }
publicdoublegetPrice() {
returnprice;
    }
publicvoidsetPrice(doubleprice) {
this.price=price;
    }
publicintgetNumber() {
returnnumber;
    }
publicvoidsetNumber(intnumber) {
this.number=number;
    }
}

二、主窗口 【Window类】


1、界面

还需要弄个窗口显示信息,这里GUI用的Ecplise的【WindowsBuilder】

窗口的布局用的是【ABsolute layout】

控件拖动到界面就行了,进行调整一下就行了

55aa3ec346eaad55fae882fbec23b3e2.png

界面创建好之后,就需要添加对应的事件监听器了


2、事件监听

1.窗口加载就显示出信息

窗口加载就显示出车票信息

/*** 窗口加载事件*/addWindowListener(newWindowAdapter() {
@OverridepublicvoidwindowOpened(WindowEvente) {
FileReaderfr=null;
BufferedReaderbr=null;
try {
// 1.用IO流读取【车次.txt】文件的信息fr=newFileReader(newFile("src\\buyticket\\车次.txt"));
br=newBufferedReader(fr);
Stringcheci="    车 次 起始站 终点站 单价  票数\n";
Stringstr=null;
while ((str=br.readLine()) !=null) {
// 2.拼接字符串,用来在textArea中展示信息checi+="  "+str+"\n";
// 3.拆分字符串得到信息,创建 Ticket 对象String[] split=str.split("\t");
Tickettic=newTicket(split[0], split[1], split[2], Double.parseDouble(split[3]),
Integer.parseInt(split[4]));
// 4.将生成的车票对象添加到 Map 集合里存储,Key-车次,Value-Ticket对象ticketMap.put(tic.getCheci(), tic);
                    }
// 5.textArea显示步骤 2得到的拼接字符串textArea.append(checi);
                } catch (Exceptione1) {
e1.printStackTrace();
                } finally {
try {
br.close();
                    } catch (IOExceptione2) {
e2.printStackTrace();
                    }
try {
fr.close();
                    } catch (IOExceptione2) {
e2.printStackTrace();
                    }
                }
            }
        });

2.查找按钮

根据文本框输入的信息查找相关车次

/*** 查找按钮监听事件*/button_search.addMouseListener(newMouseAdapter() {
@OverridepublicvoidmouseClicked(MouseEventarg0) {
// 1.清空textArea_1显示的信息textArea_1.setText("");
// 2.获取文本框输入的信息Stringsearch=searchText.getText();
Stringcheci="    车 次 起始站 终点站 单价  票数\n";
// 3.遍历Map集合,比对信息Set<Map.Entry<String, Ticket>>entrySet=ticketMap.entrySet();
for (Map.Entry<String, Ticket>entryset : entrySet) {
// 3.1根据Map的键组成的集合比对从文本框得到的信息,如果Key包含有文本框的信息则获取对应的Value值,Ticket的对象if (entryset.getKey().contains(search)) {
Stringkey=entryset.getKey();
Tickettic= (Ticket) ticketMap.get(key);
// 4.字符串拼接获取车票的信息checi+="  "+tic.getCheci() +"  "+tic.getStart() +"  "+tic.getEnd() +"    "+tic.getPrice() +"    "+tic.getNumber() +"\n";
                    }
                }
// 5.textArea_1显示拼接的字符串textArea_1.append(checi);
            }
        });

2.查找按钮

根据文本框输入的信息查找相关车次

/*** 查找按钮监听事件*/button_search.addMouseListener(newMouseAdapter() {
@OverridepublicvoidmouseClicked(MouseEventarg0) {
// 1.清空textArea_1显示的信息textArea_1.setText("");
// 2.获取文本框输入的信息Stringsearch=searchText.getText();
Stringcheci="    车 次 起始站 终点站 单价  票数\n";
// 3.遍历Map集合,比对信息Set<Map.Entry<String, Ticket>>entrySet=ticketMap.entrySet();
for (Map.Entry<String, Ticket>entryset : entrySet) {
// 3.1根据Map的键组成的集合比对从文本框得到的信息,如果Key包含有文本框的信息则获取对应的Value值,Ticket的对象if (entryset.getKey().contains(search)) {
Stringkey=entryset.getKey();
Tickettic= (Ticket) ticketMap.get(key);
// 4.字符串拼接获取车票的信息checi+="  "+tic.getCheci() +"  "+tic.getStart() +"  "+tic.getEnd() +"    "+tic.getPrice() +"    "+tic.getNumber() +"\n";
                    }
                }
// 5.textArea_1显示拼接的字符串textArea_1.append(checi);
            }
        });

3. 刷新

刷新车票信息的变化

/*** 刷新*/shuaxin.addMouseListener(newMouseAdapter() {
@OverridepublicvoidmouseClicked(MouseEventarg0) {
//1.将textArea显示的信息清空textArea.setText("");
Stringcheci="    车 次 起始站 终点站 单价  票数\n";
//2.遍历Map集合,获取Map中存储的车票信息Set<Map.Entry<String, Ticket>>entrySet=ticketMap.entrySet();
for (Map.Entry<String, Ticket>set : entrySet) {
Tickettic= (Ticket) ticketMap.get(set.getKey());
//3.字符串拼接checi+="  "+tic.getCheci() +"  "+tic.getStart() +"  "+tic.getEnd() +"    "+tic.getPrice() +"    "+tic.getNumber() +"\n";
                }
//4.textArea显示拼接之后的字符串textArea.append(checi);
            }
        });

注:这里可以优化下,自动定时刷新


4.购买

点击购买则弹出一个新的窗口【购买窗口】,这里就需要用到多线程,实现多个窗口同时运行

/*** 点击购买*/button_buy.addMouseListener(newMouseAdapter() {
@OverridepublicvoidmouseClicked(MouseEventarg0) {
//创建新的线程PayWindowpw=newPayWindow(ticketMap);
newThread(pw).start();
            }
        });

三、购买窗口【PayWindow类】


1、界面

和主窗口【Window类】一样,首先用【WindowBuilder】进行布局

53ed42bde0e7a3e0710580dbd794adc6.png

布好局之后就可以加事件监听了


2、实现线程接口【Runnable】

publicclassPayWindowextendsJFrameimplementsRunnable {
@Overridepublicvoidrun() {
PayWindowframe=newPayWindow(ticketMap);
frame.setVisible(true);
    }
}

注:

这里为什么不用继承【Thread类】来实现线程类,大家可以知道,【PayWindow类】已经继承了【JFrame类】就不能再继承了,但是我们可以通过实现接口【Runnable】来实现线程类,这就是【Thread类】和【Runnable接口】在应用时的区别


3、事件监听


1.提交信息
  • 每提交一次有效信息则添加一次信息
  • 因为每个人的身份证号是唯一的,姓名可以重复,因此Map集合采用身份证号为Key,如果身份证号有重复则覆盖
  • textArea显示的信息中身份证号部分采用字符串截取
/*** 提交按钮*/btnNewButton_subbmit.addMouseListener(newMouseAdapter() {
@OverridepublicvoidmouseClicked(MouseEventarg0) {
// private String personlist1 = " 已提交信息名单\n 姓名 身份证号\n";// 1.设置textArea显示 personlist1 字符串,因为频繁使用,所以设置成了全局变量textArea.setText(personlist1);
// 2.获取输入的姓名和身份证号Stringname=textField_name.getText();
StringIDCard=textField_IDCard.getText();
Stringli="";
// 如果两个文本框不为空则添加信息到队列if (!"".equals(name) &&!"".equals(IDCard)) {
// 3.1用获取到的姓名和身份证号生成对象,添加到存储顾客对象的Map集合中 if(name.length()<=2) {
name=name.charAt(0)+"  "+name.charAt(1);
                    }
Personperson=newPerson(name, IDCard);
personMap.put(IDCard, person);
                } else {
JOptionPane.showMessageDialog(null, name+IDCard+"信息不完整", name+IDCard, 0);
                }
// 4.遍历Map集合,获取集合中顾客的所有信息Set<Map.Entry<String, Person>>set=personMap.entrySet();
Iterator<Entry<String, Person>>setItor=set.iterator();
while (setItor.hasNext()) {
Personperson=setItor.next().getValue();
// 4.1将顾客的所有的信息拼接成字符串li+="  "+person.getName() +"    "+person.getIDCard().substring(0, 6) +"******"+person.getIDCard().substring(14) +"\n";
                }
// 5.textArea显示拼接的字符串textArea.append(li);
            }
        });

2.下单
  1. 判断顾客选择的车次存不存在
  2. 判断是否添加顾客的信息
  3. 判断车票数量是否小于购买数量
  4. 支付
  5. 购票
  1. 修改票的信息
  2. 如果车票卖完,但是还有顾客没买到票,则退钱
  1. 输出所有顾客信息
  2. 清空集合,以便下次提交信息
/*** 下单按钮*/btnNewButton.addMouseListener(newMouseAdapter() {
@OverridepublicvoidmouseClicked(MouseEventarg0) {
// 获取文本框输入的车次Stringcheci=textField.getText();
// 1.判断顾客选择的车次存不存在Ticketticket= (Ticket) ticketMap.get(checi);
if (ticket!=null) {
// 2. 判断是否添加顾客的信息intnumber=personMap.size();
if (number>0) {
JOptionPane.showMessageDialog(null, "已提交信息名单为"+number, "已提交信息", 1);
// 获取车次的信息Stringname=ticket.getCheci();
Stringend=ticket.getEnd();
Stringstart=ticket.getStart();
doubleprice=ticket.getPrice();
intticketNumber=ticket.getNumber();
// 用来接收对话框的返回值intn=0;
// 3.判断车票数量是否小于购买数量if (ticketNumber<number) {
n=JOptionPane.showConfirmDialog(null,
"车票剩余不够购买数量,目前剩余数量:"+ticketNumber+",如果继续购买则先买先得", "",
JOptionPane.YES_NO_OPTION);
                        }
if (n==0) {
// 4.支付inta=JOptionPane.showConfirmDialog(null,
"按照已提交信息名单收取金额(若未购买到票则退还相应的金额),应支付金额:"+personMap.size() *price+",是否确认支付",
"支付窗口", JOptionPane.YES_NO_OPTION);
if (a==0) {
JOptionPane.showMessageDialog(null, "支付成功", checi, 1);
textArea.setText(personlist2);
Stringjine="";
// 5.购票 同步购票代码块,synchronized (PayWindow.class) {
booleanbool=true;
if (ticketNumber==0) {
bool=false;
                                    }
// 统计购买了多少张票intcount=0;
// 根据输入的信息循环购买多张票,如果票不够,则买完为止Set<Map.Entry<String, Person>>set=personMap.entrySet();
Iterator<Entry<String, Person>>setItor=set.iterator();
for (inti=1; i<=personMap.size(); i++) {
if (bool) {
// 5.1修改票的信息Tickettic=newTicket(name, start, end, price, --ticketNumber);
count++;
if (setItor.hasNext()) {
Personperson=setItor.next().getValue();
person.setTicketname(name);
person.setPrice(price);
                                            }
Window.ticketMap.put(name, tic);
                                        }
jine=count*price+"";
// 5.2如果车票卖完,但是还有顾客没买到票,则退钱if (ticketNumber==0) {
bool=false;
ticketMap.remove(ticket.getCheci());
JOptionPane.showMessageDialog(null, "票已售完,返还"+ (personMap.size() -count) *price+"总共花费"+count*price,
"支付窗口", 1);
break;
                                        }
                                    }
                                }
// 6.输出所有顾客信息Set<Map.Entry<String, Person>>set=personMap.entrySet();
Iterator<Entry<String, Person>>setItor=set.iterator();
Stringli="";
while (setItor.hasNext()) {
Personperson=setItor.next().getValue();
// 截取字符串显示********li+="  "+person.getName() +"   "+person.getIDCard().substring(0, 6)
+"*******"+person.getIDCard().substring(14) +"  "+person.getTicketname() +"    "+person.getPrice() +"\n";
                                }
textArea.append(li);
textArea.append("               总共花费:"+jine);
// 7.清空集合,以便下次提交信息personMap.clear();
                            } else {
JOptionPane.showMessageDialog(null, "支付失败", checi, 0);
                            }
                        } else {
JOptionPane.showMessageDialog(null, checi+"票不够", checi, 0);
                        }
                    } else {
JOptionPane.showMessageDialog(null, "未添加信息", checi, 0);
                    }
                } else {
JOptionPane.showMessageDialog(null, "未找到"+checi, checi, 0);
                }
            }
        });

3.窗口销毁

关闭购票窗口而不关闭整个程序

// 设置窗口销毁,而不关闭程序addWindowListener(newWindowAdapter() {
@OverridepublicvoidwindowClosing(WindowEvente) {
dispose();
            }
        });

源代码

Java用多线程GUI实现买车票【ticket.zip】

导入之后,更改导入包以及【车次.txt】文件路径

目录
相关文章
|
23小时前
|
消息中间件 缓存 NoSQL
Java多线程实战-CompletableFuture异步编程优化查询接口响应速度
Java多线程实战-CompletableFuture异步编程优化查询接口响应速度
|
1天前
|
数据采集 存储 Java
高德地图爬虫实践:Java多线程并发处理策略
高德地图爬虫实践:Java多线程并发处理策略
|
1天前
|
缓存 Java
【Java基础】简说多线程(上)
【Java基础】简说多线程(上)
5 0
|
2天前
|
并行计算 算法 安全
Java从入门到精通:2.1.3深入学习Java核心技术——掌握Java多线程编程
Java从入门到精通:2.1.3深入学习Java核心技术——掌握Java多线程编程
|
2天前
|
安全 Java 编译器
是时候来唠一唠synchronized关键字了,Java多线程的必问考点!
本文简要介绍了Java中的`synchronized`关键字,它是用于保证多线程环境下的同步,解决原子性、可见性和顺序性问题。从JDK1.6开始,synchronized进行了优化,性能得到提升,现在仍可在项目中使用。synchronized有三种用法:修饰实例方法、静态方法和代码块。文章还讨论了synchronized修饰代码块的锁对象、静态与非静态方法调用的互斥性,以及构造方法不能被同步修饰。此外,通过反汇编展示了`synchronized`在方法和代码块上的底层实现,涉及ObjectMonitor和monitorenter/monitorexit指令。
15 0
|
2天前
|
监控 安全 Java
在Java中如何优雅的停止一个线程?可别再用Thread.stop()了!
在Java中如何优雅的停止一个线程?可别再用Thread.stop()了!
10 2
|
2天前
|
Java 调度
Java面试必考题之线程的生命周期,结合源码,透彻讲解!
Java面试必考题之线程的生命周期,结合源码,透彻讲解!
28 1
|
2天前
|
安全 Java
Java基础教程(15)-多线程基础
【4月更文挑战第15天】Java内置多线程支持,通过Thread类或Runnable接口实现。线程状态包括New、Runnable、Blocked、Waiting、Timed Waiting和Terminated。启动线程调用start(),中断线程用interrupt(),同步用synchronized关键字。线程安全包如java.util.concurrent提供并发集合和原子操作。线程池如ExecutorService简化任务管理,Callable接口允许返回值,Future配合获取异步结果。Java 8引入CompletableFuture支持回调。
|
1月前
|
存储 安全 Java
java集合框架及其特点(List、Set、Queue、Map)
java集合框架及其特点(List、Set、Queue、Map)
|
5月前
|
Java
Java集合框架“List“
Java集合框架“List“
47 1

热门文章

最新文章