绪:
- 多线程与并发
- 集合Map 、List
- GUI(ecplise插件WindowBuilder)
DEMO——购票项目
项目要求
- 文本文件提供票的信息,车次、票价、始发站终点站、票数
- 启动程序,首先读取文件,放入TextArea中,如果车次较多,那么需要加滚动条(上述两步骤,用到的是文件读写IO)
- 输入车次,查找该车次,并显示信息
- 如何在文本域中找出相关信息,并且读出这一条信息——字符串操作
- 找到车次了,输入购买张数,点击购买,弹出新的窗口——新的窗口是启动了一个线程——多线程
- 在购买界面,显示购买车次的信息,和顾客进行确认。并且提供身份证信息
- 如果买多张,那么这些身份证信息,先保存到集合中(map),遍历集合,给每个身份证购买一张票
- 输出购买信息
实现步骤
需要创建一个车票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; } publicStringtoString() { 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; } publicStringtoString() { 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】
控件拖动到界面就行了,进行调整一下就行了
界面创建好之后,就需要添加对应的事件监听器了
2、事件监听
1.窗口加载就显示出信息
窗口加载就显示出车票信息
/*** 窗口加载事件*/addWindowListener(newWindowAdapter() { publicvoidwindowOpened(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() { publicvoidmouseClicked(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() { publicvoidmouseClicked(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() { publicvoidmouseClicked(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() { publicvoidmouseClicked(MouseEventarg0) { //创建新的线程PayWindowpw=newPayWindow(ticketMap); newThread(pw).start(); } });
三、购买窗口【PayWindow类】
1、界面
和主窗口【Window类】一样,首先用【WindowBuilder】进行布局
布好局之后就可以加事件监听了
2、实现线程接口【Runnable】
publicclassPayWindowextendsJFrameimplementsRunnable { publicvoidrun() { PayWindowframe=newPayWindow(ticketMap); frame.setVisible(true); } }
注:
这里为什么不用继承【Thread类】来实现线程类,大家可以知道,【PayWindow类】已经继承了【JFrame类】就不能再继承了,但是我们可以通过实现接口【Runnable】来实现线程类,这就是【Thread类】和【Runnable接口】在应用时的区别
3、事件监听
1.提交信息
- 每提交一次有效信息则添加一次信息
- 因为每个人的身份证号是唯一的,姓名可以重复,因此Map集合采用身份证号为Key,如果身份证号有重复则覆盖
- textArea显示的信息中身份证号部分采用字符串截取
/*** 提交按钮*/btnNewButton_subbmit.addMouseListener(newMouseAdapter() { publicvoidmouseClicked(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.下单
- 判断顾客选择的车次存不存在
- 判断是否添加顾客的信息
- 判断车票数量是否小于购买数量
- 支付
- 购票
- 修改票的信息
- 如果车票卖完,但是还有顾客没买到票,则退钱
- 输出所有顾客信息
- 清空集合,以便下次提交信息
/*** 下单按钮*/btnNewButton.addMouseListener(newMouseAdapter() { publicvoidmouseClicked(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() { publicvoidwindowClosing(WindowEvente) { dispose(); } });
源代码
导入之后,更改导入包以及【车次.txt】文件路径