Bootstrap

JavaSE项目——嗖嗖移动大厅(详细注释,java基础知识基本上都包含了)

Java面向对象综合实战——嗖嗖移动大厅

                   主页上传文件有源码

1.项目需求

  1. 嗖嗖移动是一个假定的通信运营商,提供了话痨套餐、网虫套餐、超人套餐,各种套餐所包含服务内容及资费如表所示:

​ 嗖嗖移动套餐服务内容及资费

品牌套餐话痨套餐网虫套餐超人套餐
通话时长(分钟)500/200
上网流量(GB)/31
短信条数(条)30/50
资费(元/月)586878
  1. 计费规则:如实际使用中超出套餐内包含的通话时长、短信条数或上网流量,按以下规则计费。
    1. 超出的通话:0.2元/分钟
    2. 超出的短信:0.1元/条
    3. 超出的上网流量:0.1元/MB
  2. 功能介绍:
菜单级别功能描述
主菜单用户登录输入正确的手机卡号和密码可进入二级菜单列表
主菜单用户注册录入信息并开卡,用户输入的信息包括选择卡号、选择套餐类型、输入用户的用户名和密码、预存话费金额(预存话费金额必须足以支付所选套餐一个月的资费)
主菜单使用嗖嗖输入正确的手机卡号和密码后,随即进入本卡号所属套餐可支持的一个场景、消费套餐余量或话费余额,并记录消费信息。当话费余额不足,
主菜单话费充值输入正确的用户名和密码后,可谓该卡号充值(单次充值最低50元)
主菜单资费说明提供个品牌套餐所包含的通话时长、上网流量、短信条数、月资费等信息
主菜单退出系统推出本系统
二级菜单本月账单查询可查询该卡号的套餐资费、实际消费金额、账户余额
套餐余量查询可查询该卡号的套餐余量
二级菜单打印消费详单输入正确的卡号和密码,可打印当前卡号用户的消费详单
二级菜单套餐变更可变更其他套餐,变更后话费余额须减去变更后的套餐资费,余额不足时须给出信息提示,套餐变更后重新统计卡中实际消费数据以及当月消费金额
二级菜单办理退网输入正确的卡号和密码后,可从已注册卡号列表中删除本卡号,并退出系统
  1. 环境准备:我的运行环境为:
    • jdk11.0.8
    • IntelliJ IDEA 2020

2.项目实现

为实现程序的可维护性和可拓展性,采用面向对象的细想进行整体构架的设计

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BeeKH5D5-1647423765522)(…/images/1647411722927.png)]

1)创建类

(省略Getter/Setter和不需要特殊赋值的构造方法,idea快捷键ait+insert一键生成没有不知道的吧)

  1. 手机卡类(MobileCard):

    package entity;
    
    /**
     * 嗖嗖移动卡
     */
    public class MobileCard {
        private String cardNumber; //卡号
        private String userName; //用户名
        private String passWord; //密码
        private ServicePackage serPackage; //所属套餐
        private double consumAmount; //当月消费金额
        private double money;  //账户余额
        private int realTalkTime; //当月实际通话时长
        private int realSMSCount; //当月实际发送短信条数
        private int realFlow; //当月实际上网流量
        //展示卡号信息
        public void showMeg(){
            System.out.println("卡号:"+this.cardNumber+",用户名:"+this.userName+",当前余额:"+this.money+"元");
            this.serPackage.showInfo();
    
        }
    
  2. 业务套餐类(ServicePackage):

    package entity;
    
    /**
     * 业务套餐 抽象类 包含网虫套餐,话痨套餐,超人套餐
     */
    public abstract class ServicePackage {
        private double price;  //套餐月资费
    
        //展示套餐信息
        public abstract void showInfo();
    
    }
    
    
  3. 消费信息类(ConsumInfo):

    package entity;
    
    /**
     * 消费记录
     */
    public class ConsumInfo {
        private String cardNumber;  //卡号
        private String type;        //消费类型
        private int consumData;     //消费数据
    }
    
    
  4. 使用场景类(Scene):

    package entity;
    
    /**
     * 使用场景
     */
    public class Scene {
        private String type;      //场景类型
        private int data;        //场景消费数据
        private String description; //场景描述
    
    }
    
    
2)创建接口:
  1. 通话接口(CallService):

    package service;
    
    import entity.MobileCard;
    
    //通话服务
    public interface CallService {
        /**
         * 通话
         * @param minCount 通话分钟数
         * @param card     超出套餐内的通话时长时需消费哪张卡的余额
         */
        int call(int minCount, MobileCard card)throws Exception;
    
    }
    
    
  2. 短信接口(SendService):

    package service;
    
    import entity.MobileCard;
    
    //短信服务
    public interface SendService {
        /**
         * 发短信
         * @param count 短信数
         * @param card  超过套餐范围短信需要使用哪张卡付费
         */
        int send(int count, MobileCard card)throws Exception;
    }
    
    
  3. 上网接口(NetService):

    package service;
    
    import entity.MobileCard;
    
    //上网服务
    public interface NetService {
        /**
         * 上网
         * @param flow 上网流量
         * @param card 超出套餐流量部分需要使用哪张卡余额
         */
        int netPlay(int flow, MobileCard card)throws Exception;
    }
    
    
3)创建业务套餐实体类的子类
  1. 话痨套餐类(TalkPackage):

    package entity;
    
    import service.CallService;
    import service.SendService;
    
    /**
     * 话痨套餐
     */
    public class TalkPackage extends ServicePackage implements CallService, SendService {
        private int talkTime; //通话时长
        private int smsCount; //可发送短信条数
        //构造方法设置卡套餐内容
        public TalkPackage() {
            this.talkTime=200;
            this.smsCount=50;
            super.setPrice(58);
        }
    
        @Override
        public void showInfo() {
            System.out.println("话痨套餐: 通话时长为"+this.talkTime+"分钟/月,短信条数为"+this.smsCount+"条/月,上网流量为"+0+"GB/月");
        }
    
        @Override
        public int call(int minCount, MobileCard card) throws Exception {
            // 重写通话接口功能 获得套餐使用详情
            int temp = 0;// 实际消耗分钟数
            // 循环判断使用详情
            for (int i = 0; i < minCount; i++) {
                // 第一种情况 套餐余额充足还可支持1分钟通话
                if (this.getTalkTime() - card.getRealTalkTime() >= 1) {
                    card.setRealTalkTime(card.getRealTalkTime() + 1);// 实际通话数据+1
                    temp++;
                } else if (card.getMoney() >= 0.2) {
                    // 情况二:套餐通话时长已经用完,但是账户余额还可以支持1分钟通话,直接使用账户余额支付
                    card.setRealTalkTime(card.getRealTalkTime() + 1);// 实际使用通话时长分钟+1
                    temp++;
                    // 剩余金额减少0.2元
                    card.setMoney(card.getMoney() - 0.2);
                    // 总消费增加0.2元
                    card.setConsumAmount(card.getConsumAmount() + 0.2);
                } else {
                    try {
                        throw new Exception("本次已通话" + temp + "分钟,您的余额已不足,请充值后在使用!");
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } finally {
                        // 报错结束返回一个实际通话时长
                        return temp;
                    }
                }
            }
            return temp;// 返回一个实际通话时长s
    
        }
    
        @Override
        public int send(int count, MobileCard card) throws Exception {
            // 重写短信接口功能 获得套餐使用详情
            int temp = 0;// 实际消耗短信次数
            // 循环判断使用详情
            for (int i = 0; i < count; i++) {
                // 第一种情况 套餐余额充足还可支持发送1次短信
                if (this.getSmsCount() - card.getRealSMSCount() >= 1) {
                    card.setRealSMSCount(card.getRealSMSCount() + 1);// 实际短信数据+1
                    temp++;
                } else if (card.getMoney() >= 0.1) {
                    // 情况二:套餐短信次数已经用完,但是账户余额还可以支持发一次短信,直接使用账户余额支付
                    card.setRealSMSCount(card.getRealSMSCount() + 1);// 实际短信数据+1
                    temp++;
                    // 剩余金额减少0.1元
                    card.setMoney(card.getMoney() - 0.1);
                    // 总消费增加0.1元
                    card.setConsumAmount(card.getConsumAmount() + 0.1);
                } else {
                    try {
                        throw new Exception("本次已发送" + temp + "次短信,您的余额已不足,请充值后在使用!");
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        // 报错结束返回一个实际短信次数
                        return temp;
                    }
                }
            }
            return temp;// 返回一个实际短信次数
    
        }
    }
    
    
  2. 网虫套餐类(NetPackage):

    package entity;
    
    import service.NetService;
    
    
    /**
     * 网虫套餐
     */
    public class NetPackage extends ServicePackage implements NetService{
        private int flow; //上网流量
        public NetPackage() {
            this.flow=5*1024;
            super.setPrice(68);
        }
    
    
        @Override
        public void showInfo() {
            System.out.println("话痨套餐: 通话时长为"+0+"分钟/月,短信条数为"+0+"条/月,上网流量为"+(this.flow/1024.0)+"GB/月");
    
        }
          @Override
        public int netPlay(int flow, MobileCard card) throws Exception {
            // 重写上网接口功能 获得套餐使用详情
            int temp = 0;// 实际消耗流量数据
            // 循环判断使用详情
            for (int i = 0; i < flow; i++) {
                // 第一种情况 套餐余额充足还可以使用1MB的流量
                if (this.getFlow() - card.getRealFlow() >= 1) {
                    card.setRealFlow(card.getRealFlow() + 1);
                    ;// 实际流量数据+1
                    temp++;
                } else if (card.getMoney() >= 0.1) {
                    // 情况二:套餐剩余流量已经用完,但是账户余额还可以支持使用1MB流量,直接使用账户余额支付
                    card.setRealSMSCount(card.getRealSMSCount() + 1);// 实际流量数据+1
                    temp++;
                    // 剩余金额减少0.1元
                    card.setMoney(card.getMoney() - 0.1);
                    // 总消费增加0.1元
                    card.setConsumAmount(card.getConsumAmount() + 0.1);
                } else {
                    try {
                        throw new Exception("本次已使用" + temp + "MB的流量,您的余额已不足,请充值后在使用!");
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        // 报错结束返回一个实际流量数据
                        return temp;
                    }
                }
            }
            return temp;// 返回一个实际流量数据
        }
    }
    
  3. 超人套餐类(SuperPackage):

    package entity;
    
    import service.CallService;
    import service.NetService;
    import service.SendService;
    
    /**
     * 超人套餐
     */
    public class SuperPackage extends ServicePackage implements CallService, SendService , NetService {
        private int talkTime; //通话时长
        private int smsCount; //可发送短信条数
        private int flow; //上网流量
        public SuperPackage() {
        this.talkTime=200;
        this.smsCount=100;
        this.flow=1*1024;
        super.setPrice(78);
        }
    
        @Override
        public void showInfo() {
            System.out.println("超人套餐:通话时长为"+this.talkTime+"分钟/月,短信条数为:"+this.smsCount+"条/月,上网流量为"+(this.flow/1024)+"GB/月");
        }
         @Override
        public int call(int minCount, MobileCard card) throws Exception {
            // 重写通话接口功能 获得套餐使用详情
            int temp = 0;// 实际消耗分钟数
            // 循环判断使用详情
            for (int i = 0; i < minCount; i++) {
                // 第一种情况 套餐余额充足还可支持1分钟通话
                if (this.getTalkTime() - card.getRealTalkTime() >= 1) {
                    card.setRealTalkTime(card.getRealTalkTime() + 1);// 实际通话数据+1
                    temp++;
                } else if (card.getMoney() >= 0.2) {
                    // 情况二:套餐通话时长已经用完,但是账户余额还可以支持1分钟通话,直接使用账户余额支付
                    card.setRealTalkTime(card.getRealTalkTime() + 1);// 实际使用通话时长分钟+1
                    temp++;
                    // 剩余金额减少0.2元
                    card.setMoney(card.getMoney() - 0.2);
                    // 总消费增加0.2元
                    card.setConsumAmount(card.getConsumAmount() + 0.2);
                } else {
                    try {
                        throw new Exception("本次已通话" + temp + "分钟,您的余额已不足,请充值后在使用!");
                    } catch (Exception e) 
                        e.printStackTrace();
                    } finally {
                        // 报错结束返回一个实际通话时长
                        return temp;
                    }
                }
            }
            return temp;// 返回一个实际通话时长
        }
    
        @Override
        public int netPlay(int flow, MobileCard card) throws Exception {
            // 重写上网接口功能 获得套餐使用详情
            int temp = 0;// 实际消耗流量数据
            // 循环判断使用详情
            for (int i = 0; i < flow; i++) {
                // 第一种情况 套餐余额充足还可以使用1MB的流量
                if (this.getFlow() - card.getRealFlow() >= 1) {
                    card.setRealFlow(card.getRealFlow() + 1);
                    ;// 实际流量数据+1
                    temp++;
                } else if (card.getMoney() >= 0.1) {
                    // 情况二:套餐剩余流量已经用完,但是账户余额还可以支持使用1MB流量,直接使用账户余额支付
                    card.setRealSMSCount(card.getRealSMSCount() + 1);// 实际流量数据+1
                    temp++;
                    // 剩余金额减少0.1元
                    card.setMoney(card.getMoney() - 0.1);
                    // 总消费增加0.1元
                    card.setConsumAmount(card.getConsumAmount() + 0.1);
                } else {
                    try {
                        throw new Exception("本次已使用" + temp + "MB的流量,您的余额已不足,请充值后在使用!");
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        // 报错结束返回一个实际流量数据
                        return temp;
                    }
                }
            }
            return temp;// 返回一个实际流量数据
        }
    
        @Override
        public int send(int count, MobileCard card) throws Exception {
            // 重写短信接口功能 获得套餐使用详情
            int temp = 0;// 实际消耗短信次数
            // 循环判断使用详情
            for (int i = 0; i < count; i++) {
                // 第一种情况 套餐余额充足还可支持发送1次短信
                if (this.getSmsCount() - card.getRealSMSCount() >= 1) {
                    card.setRealSMSCount(card.getRealSMSCount() + 1);// 实际短信数据+1
                    temp++;
                } else if (card.getMoney() >= 0.1) {
                    // 情况二:套餐短信次数已经用完,但是账户余额还可以支持发一次短信,直接使用账户余额支付
                    card.setRealSMSCount(card.getRealSMSCount() + 1);// 实际短信数据+1
                    temp++;
                    // 剩余金额减少0.1元
                    card.setMoney(card.getMoney() - 0.1);
                    // 总消费增加0.1元
                    card.setConsumAmount(card.getConsumAmount() + 0.1);
                } else {
                    try {
                        throw new Exception("本次已发送" + temp + "次短信,您的余额已不足,请充值后在使用!");
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        // 报错结束返回一个实际短信次数
                        return temp;
                    }
                }
            }
            return temp;// 返回一个实际短信次数
        }
    
    }
    
    
4)创建工具类

工具类主要包含数据存储列表和功能实现:

package util;

import entity.*;
import jdk.swing.interop.SwingInterOpUtils;
import service.CallService;
import service.NetService;
import service.SendService;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;

/**
 * 工具类
 */
public class CardUtil {
    //已注册用户列表
    private  Map<String, MobileCard> cards=new HashMap<>();
    //所有卡号消费记录列表
    private  Map<String, List<ConsumInfo>> consumInfos=new HashMap<>();
    //使用场景列表
    private List<Scene> scenes = new ArrayList<>();

    Scanner sc=new Scanner(System.in);
    /**
     * 初始化场景
     * */
    public void initScene(){
        //初始化场景
        scenes.add(new Scene("通话", 90, "问候客户,谁知其如此难缠,通话90分钟"));
        scenes.add(new Scene("通话", 30, "询问妈妈身体状况,本地通话30分钟"));
        scenes.add(new Scene("短信", 5, "参与环境保护实施方案问卷调查,发送短信5条"));
        scenes.add(new Scene("短信", 50, "通知朋友手机换号,发送短信50条"));
        scenes.add(new Scene("上网", 1024, "和女朋友用微信视频聊天,使用流量1GB"));
        scenes.add(new Scene("上网", 2 * 1024, "晚上手机在线看韩剧,不留神睡着啦!使用2GB"));
    }

    /**
     * 初始化
     * 电话卡  使用记录
     */
    public void init(){
        //话痨
        MobileCard card1=new MobileCard("13901234567", "小陈", "123456", new TalkPackage(), 58,30,600,30,0 );
        //网虫
        MobileCard card2=new MobileCard("13908765431", "小许", "987654", new NetPackage(), 68,200,0,0,0);
        //超级
        MobileCard card3=new MobileCard("13092322791", "小薛", "123123", new SuperPackage(), 78,300,0,0,0);
        cards.put(card1.getCardNumber(),card1);
        cards.put(card2.getCardNumber(),card2);
        cards.put(card3.getCardNumber(),card3);

        //初始化消费记录列表

       //卡1消费记录
        List<ConsumInfo> c1= new ArrayList<>();
        c1.add(new ConsumInfo(card1.getCardNumber(),"通话",100));
        card1.setRealTalkTime(card1.getRealTalkTime()+100);
        consumInfos.put(card1.getCardNumber(),c1);
        //卡2消费记录
        List<ConsumInfo> c2= new ArrayList<>();
        c2.add(new ConsumInfo(card2.getCardNumber(),"上网",1024));
        card2.setRealFlow(card2.getRealFlow()+1024);
        consumInfos.put(card2.getCardNumber(),c2);
        //消费记录卡3
        List<ConsumInfo> c3= new ArrayList<>();
        c3.add(new ConsumInfo(card3.getCardNumber(),"通话",100));
        c3.add(new ConsumInfo(card3.getCardNumber(),"上网",1024));
        c3.add(new ConsumInfo(card3.getCardNumber(),"发短信",20));
        card3.setRealTalkTime(card3.getRealTalkTime()+100);
        card3.setRealFlow(card3.getRealFlow()+1024);
        card3.setRealSMSCount(card3.getRealSMSCount()+20);
        consumInfos.put(card3.getCardNumber(),c3);





    }


    /**
     * 注册新卡
     * @param card  注册卡号
     */
    public void addCard(MobileCard card){
        //创建选择业务对象 抽象类不能new
        ServicePackage sp;
        //创造数组接收随机生成的手机号
        String counts[]=getNewNumbers(9);
        System.out.println("************可选择的卡号**************");
        //打印随机手机号
        for (int i = 0; i < counts.length; i++) {
            System.out.print((i+1)+"."+counts[i]+"\t\t");
            //输出三次换行
            if((i+1)%3==0){
                System.out.println();
            }
        }
        System.out.print("请选择卡号:");
        int cardNum=sc.nextInt();
        //判断选择正确性
        while ((cardNum-1)>=counts.length||(cardNum-1)<0){
            System.out.print("请输入正确的序号:");
            cardNum=sc.nextInt();
        }
        System.out.println("请选择套餐:1.话痨套餐(58元/月) 2.网虫套餐(68元/月) 3.超人套餐(78元/月)(请输入序号):");
        int choice=sc.nextInt();
        //判断选择正确性
        while (choice<=0||choice>3){
            System.out.println("请输入正确的套餐序号");
            choice=sc.nextInt();
        }
        //获取指定套餐 1.话痨套餐 2.网虫套餐 3.超人套餐
        switch (choice){
            case 1:
                sp=new TalkPackage();
                break;
            case 2:
                sp=new NetPackage();
                break;
            case 3:
                sp=new SuperPackage();
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + choice);
        }
        System.out.print("请输入姓名:");
        String name=sc.next();
        System.out.print("请输入密码:");
        String password=sc.next();
        System.out.print("请输入预存花费金额:");
        double money=sc.nextDouble();
        //将基本信息添加到手机卡对象中    //卡号 用户名 密码 所属套餐  账户余额
        card=new MobileCard(counts[cardNum - 1], name, password, sp, ( money-sp.getPrice()));
        //循环判断预存金额是否充足 不足则重新赋值
        while(card.getMoney()<0){
            System.out.print("您预存的花费金额不足以支付本月固定套餐资费,请重新充值:");
            money=sc.nextDouble();
            card.setMoney(money-sp.getPrice());
        }
        System.out.println("注册成功!");
        //展示信息
        card.showMeg();

        //添加到集合中
        cards.put(card.getCardNumber(),card);
    }

    /**
     * 话费充值
     * @param number  卡号
     * @param money   充值金额
     */
    public void chargeMoney(String number,double money){
        //判断卡号是否存在
        if(cards.get(number)!=null){
            //最低充值50
            if(money>=50){
                cards.get(number).setMoney(cards.get(number).getMoney()+money);
                System.out.println("充值成功!当前的话费余额为:"+cards.get(number).getMoney()+"元");
            }else{
                System.out.println("最低消费50!");
            }
        }else {
            System.out.println("该手机号不存在");
        }

    }

    //使用嗖嗖(模拟手机消费)
    public void userSoso(String number)throws Exception{
        //通过手机号获取手机卡对象
        MobileCard card=cards.get(number);
        //随机产生场景
        Random rand=new Random();
        int temp=0; //各场景的实际消费数据
        //遍历判断手机卡所有场景的功能类型
        while(true){
            //获取0-5的随机数
            int randNum=rand.nextInt(6);
            //根据随机数创建场景(场景在上面已初始化)
            Scene scene=scenes.get(randNum);
            switch (randNum){
                case 0:
                case 1:
                    //通话 判断该手机卡是否包含语音套餐(网虫套餐无)
                    if(card.getSerPackage() instanceof CallService){
                        //展示场景
                        System.out.println(scene.getDescription()+"\t");
                        //获得实际消费数据  scene中data消费数据即为call方法的通话分钟数
                        temp=((CallService) card.getSerPackage()).call(scene.getData(),card);
                        //添加消费记录
                        //创建消费记录对象,数据是实际消费数据(不是场景中消费数据)
                        ConsumInfo info=new ConsumInfo(number,scene.getType(),temp);
                        //修改电话卡当月实际通话数据
                        card.setRealTalkTime(card.getRealTalkTime()+temp);
                        //添加消费记录
                        addConsumInfo(number,info);
                        break;
                    }
                    continue;
                case 2:
                case 3:
                    //短信场景 判断手机卡对象的套餐是否包含短信业务
                    if(card.getSerPackage() instanceof SendService){
                        //展示场景
                        System.out.println(scene.getDescription()+"\t");
                        //获得实际消费记录 scene中data为send中短信条数
                        temp=((SendService) card.getSerPackage()).send(scene.getData(),card);
                        //添加消费记录
                        //创建消费记录对象,数据是实际消费数据(不是场景中消费数据)
                        ConsumInfo info=new ConsumInfo(number,scene.getType(),temp);
                        //修改电话卡当月实际短信条数
                        card.setRealSMSCount(card.getRealSMSCount()+temp);
                        addConsumInfo(number,info);
                        break;
                    }
                    continue;
                case 4:
                case 5:
                    //上网  判断手机卡对象的套餐是否包含上网业务
                    if(card.getSerPackage() instanceof NetService){
                        //展示场景
                        System.out.println(scene.getDescription()+"\t");
                        //获得实际消费数据 此时scene中data为netplay的流量
                        temp=((NetService)card.getSerPackage()).netPlay(scene.getData(),card);
                        //添加消费记录
                        ConsumInfo info=new ConsumInfo(number,scene.getType(),temp);
                        //修改电话卡当月实际上午数据
                        card.setRealFlow(card.getRealFlow()+temp);
                        addConsumInfo(number,info);
                        break;
                    }
                    continue;
            }
            //退出while循环
            break;
        }
    }


    //资费说明  创建套餐资费说明文件
    public void showDescription()throws IOException {
        //创建FileWriter对象
        FileWriter fw=new FileWriter("src/serviceInfo.txt");
        //创建StringBuffer接收需要写入的信息
        StringBuffer sb=new StringBuffer();
        sb.append("套餐类型:话唠套餐\r\n" + "通话时长:500分钟 \r\n" + "短信条数:30条\r\n" + "月资费:58元\r\n" + "——————————————\r\n"
                + "套餐类型:网虫套餐\r\n" + "上网流量:3GB\r\n" + "月资费:68元\r\n" + "——————————————\r\n" + "套餐类型:超人套餐\r\n"
                + "通话时长:200分钟 \r\n" + "短信条数:50条\r\n" + "上网流量:1GB\r\n" + "月资费:78元\r\n" + "——————————————\r\n"
                + "超出套餐计费:\r\n" + "通话时长:0.2元/分钟\r\n" + "短信条数:0.1元/分钟\r\n" + "上网流量:0.1元/分钟\r\n"
                + "————————————————\r\n");
        fw.write(sb.toString());
        fw.flush();
        //创建FileReader对象
        FileReader fr=new FileReader("src/serviceInfo.txt");
        //创建BufferReader对象接收字符串
        BufferedReader br=new BufferedReader(fr);
        //循环打印到控制台
        String line=br.readLine();
        while(line!=null){
            System.out.println(line);
            line=br.readLine();
        }
        try {
            br.close();
            fr.close();
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
    //本月账单查询
    public void showAmountDetail(String number){
        //根据手机号查找
        MobileCard card=cards.get(number);
        //判断是否存在
        if(card!=null){
            //创建StringBuffer类 便于字符串拼接
            StringBuffer sb = new StringBuffer("您的卡号:" + number + "的当月账单:\n");
            sb.append("套餐资费:"+card.getSerPackage().getPrice()+"元\n");
            sb.append("合计:"+card.getConsumAmount()+"元\n");
            sb.append("账户余额:"+card.getMoney()+"元。");
            System.out.println(sb);
        }else{
            System.out.println("该手机卡号不存在!");
        }
    }
    //套餐余量查询
    public void showRemainDetail(String number){
        //根据手机号查找
        MobileCard card=cards.get(number);
        //判断是否存在
        if(card!=null) {
            //创建StringBuffer类 便于字符串拼接
            StringBuffer sb = new StringBuffer("您的卡号是:" + number + "\n套餐内剩余:");
            // 获取卡中的真实消费数据和业务数据比较,判定
            ServicePackage serPackage = card.getSerPackage();
            // 判断是哪种套餐
            if (serPackage instanceof SuperPackage) {
                //向下转型获取超人套餐信息和功能
                SuperPackage pack = (SuperPackage) serPackage;
                //获取套餐余量
                int minusTalkData = pack.getTalkTime() - card.getRealTalkTime();
                int minusSMSData = pack.getSmsCount() - card.getRealSMSCount();
                int minusFlowData = pack.getFlow() - card.getRealFlow();
                sb.append("\n通话时长:" + (minusTalkData > 0 ? minusTalkData + "分钟" : "0分钟"));
                sb.append("\n短信条数:" + (minusSMSData > 0 ? minusSMSData + "条" : "0条"));
                sb.append("\n上网流量:" + (minusFlowData > 0 ? minusFlowData/1024 + "GB" : "0GB"));
            } else if (serPackage instanceof TalkPackage) {
                // 向下转型获取话痨套餐信息和功能
                TalkPackage pack = (TalkPackage) serPackage;
                // 获取套餐余量
                int minusTalkData = pack.getTalkTime() - card.getRealTalkTime();
                int minusSMSData = pack.getSmsCount() - card.getRealSMSCount();
                sb.append("\n通话时长:" + (minusTalkData > 0 ? minusTalkData + "分钟" :  "0分钟"));
                sb.append("\n短信条数:" + (minusSMSData > 0 ? minusSMSData + "条" : "0条"));
            } else if (serPackage instanceof NetPackage) {
                // 向下转型获取话痨套餐信息和功能
                NetPackage pack = (NetPackage) serPackage;
                /// 获取套餐余量
                int minusFlowData = pack.getFlow() - card.getRealFlow();
                sb.append("\n上网流量:" + (minusFlowData > 0 ? minusFlowData / 1024 + "GB" :"0GB"));
            }
            System.out.println(sb);
        } else {
            System.out.println("该手机卡号不存在!");
        }
    }
    //打印消费账单
    public void printAmountDetail(String number){
        //判断手机号是否存在
        if(cards.get(number)!=null){
            //打印消费记录到文本文件中 文件名:手机号+消费记录
            try {
                FileWriter fw=new FileWriter(number+"消费记录.txt");

                //创建StringBuffer写入消费记录
                StringBuffer sb=new StringBuffer("***********************"+number+"的消费记录***********************");
                sb.append("\n序号\t类型\t数据(通话(分钟)/上网(MB)/短信(条))\n");
                // 获取消费记录集合
                List<ConsumInfo> infoList=consumInfos.get(number);
                //判断集合是否为空
                if(infoList!=null){
                    //遍历消费记录
                    for (int i = 0; i < infoList.size(); i++) {
                        sb.append((i+1)+".\t"+infoList.get(i).getType()+"\t"+infoList.get(i).getConsumData()+"\n");
                    }
                    System.out.println("打印完成");
                }else{
                    System.out.println("该卡暂无消费记录");
                }
                // 写入文件
                fw.write(sb.toString());
                fw.flush();
                fw.close();
            }catch (IOException e) {
                e.printStackTrace();
            }
        }else {
            System.out.println("改手机号不存在");
        }
    }



    /**
     * 套餐变更
     * @param number  卡号
     */
    public void changingPack(String number){
        //创建一个业务套餐对象 抽象类无法实例化(判断卡号类型向下转型)
        ServicePackage sp;
        //判断卡号是否存在
        if(cards.get(number)!=null){
            System.out.println("**********************");
            System.out.println("1、话痨套餐\t2、网虫套餐\t3、超人套餐  请选择(序号):");
            int choice=sc.nextInt();
            //判断输入正误
            while (choice<=0||choice>3){
                System.out.print("请输入正确的序号");
                choice=sc.nextInt();
            }
            //判断项目类型
            switch (choice){
                case 1:
                    //话痨套餐
                    sp=new TalkPackage();
                    //判断现有套餐是否与要更改套餐相同
                    if(cards.get(number).getSerPackage() instanceof TalkPackage){
                        System.out.println("对不起,您已经是改套餐用户,无需更改");
                    }else{
                        //判断剩余金额是否足以支付套餐费用
                        if(cards.get(number).getMoney()>sp.getPrice()){
                            //更改套餐
                            cards.get(number).setSerPackage(sp);
                            //余额减去套餐费用
                            cards.get(number).setMoney(cards.get(number).getMoney()-sp.getPrice());
                            //当月消费金额加上套餐费
                            cards.get(number).setConsumAmount(cards.get(number).getConsumAmount() + sp.getPrice());
                            System.out.println("更改套餐成功!");
                            //展示套餐信息
                            sp.showInfo();
                        }else {
                            System.out.println("余额不足,无法更改套餐");
                        }
                    }
                    break;
                case 2:
                    //网虫套餐
                    sp=new NetPackage();
                    //判断现有套餐和要更改套餐是否相同
                    if(cards.get(number).getSerPackage() instanceof NetService){
                        System.out.println("您已经是改套餐用户,无需更改");
                    }else{
                        //判断余额要大于套餐费用
                        if(cards.get(number).getMoney()>sp.getPrice()){
                            //更改套餐
                            cards.get(number).setSerPackage(sp);
                            //余额减去套餐费
                            cards.get(number).setMoney(cards.get(number).getMoney()-sp.getPrice());
                            //月消费加上套餐费
                            cards.get(number).setConsumAmount(cards.get(number).getConsumAmount()+sp.getPrice());
                            System.out.println("套餐修改成功");
                        }else{
                            System.out.println("余额不足,无法更改套餐");
                        }
                    }
                    break;
                case 3:
                    //超人套餐
                    sp=new SuperPackage();
                    //判断已有套餐和要修改套餐是否相同
                    if(cards.get(number).getSerPackage() instanceof SuperPackage){
                        System.out.println("您已经是该套餐用户,无法修改");
                    }else {
                        //判断余额是否足以修改套餐
                        if(cards.get(number).getMoney()>sp.getPrice()){
                            //更改套餐
                            cards.get(number).setSerPackage(sp);
                            //更改余额
                            cards.get(number).setMoney(cards.get(number).getMoney()-sp.getPrice());
                            //更改月消费
                            cards.get(number).setConsumAmount(cards.get(number).getConsumAmount()+sp.getPrice());
                        }else{
                            System.out.println("余额不足,无法更改套餐");
                        }
                    }
                    break;
            }
        }else{
            System.out.println("该手机号不存在");
        }
    }

    /**
     * 办理退网
     * @param number 卡号
     */
    public void delCard(String number){
        //判断集合中卡号是否存在
        if(cards.get(number)!=null){
            System.out.println("*********办理退网**********");
            //二次确定
            System.out.print("您确定注销此手机号吗?(Y/N):");
            String choice=sc.next();
            if(choice.equalsIgnoreCase("Y")){
                //通过 key手机号删除
                cards.remove(number);
                System.out.println("卡号:"+number+"退网成功!\n感谢使用");
            }
        }else{
            System.out.println("该手机卡号不存在!");
        }
    }
    //根据卡密验证该卡是否已注册(登录验证)
    public boolean isExistCard(String number,String passWord){
        Set<String> numbers = cards.keySet();
        Iterator<String> it = numbers.iterator();
        boolean flag=false;
        while (it.hasNext()){
            String searchNum = it.next();
            if(searchNum.equals(number) && (cards.get(searchNum)).getPassWord().equals(passWord)){
                flag=true;
            }

        }
        return flag;

    }
    //根据卡号验证卡号是否注册
    public boolean isExistCard(String number){
        Set<String> numbers = cards.keySet();
        Iterator<String> it = numbers.iterator();
        boolean flag=false;
        while (it.hasNext()){
            String searchNum = it.next();
            if(searchNum.equals(number)){
                flag=true;
            }
        }
        return flag;
    }
    //生成随机卡号
    public String createNumber(){
        String fixedNum="130";
        String cardNumber="";
        Random rand=new Random();
        String randNumber="";
        //随机生成八位数字
        for (int i = 0; i < 8; i++) {
            randNumber+=rand.nextInt(10);
        }
        cardNumber=fixedNum+randNumber;
        return cardNumber;
    }

    /**
     * 生成指定个数的卡号列表
     * @param count  返回手机号码数量
     * @return
     */
    public String[] getNewNumbers(int count){
        //创建一个数组接收随机生成卡号
        String[] numbers=new String[count];
        //遍历赋值
        for (int i = 0; i < numbers.length; i++) {
            numbers[i]=createNumber();
            //判断是否注册
            if(isExistCard(numbers[i])){
                //如果被注册就重新赋值
                i--;
            }
            //判断生成是否有重复
            for (int j = 0; j < i; j++) {
                //判断新生成的号码与之前生成的号码是否有重复,如果有重新生成
                if(numbers[i].equals(numbers[j])){
                   //有重复重新赋值
                    i--;
                    break;
                }
            }
        }
        return numbers;
    }

    /**
     * 添加指定卡号的消费记录
     * @param number 手机卡号
     * @param info   一条消费记录
     */
    public void addConsumInfo(String number,ConsumInfo info){
        //创建消费对象集合
        List<ConsumInfo> consumList=null;

        //有消费记录(集合已存在)
        if(consumInfos.containsKey(number)){
            consumList=consumInfos.get(number);
            consumList.add(info);
            System.out.println("添加消费记录成功");
        }else {
            //没有消费记录(该卡号不存在集合中)
            consumList=new ArrayList<>();
            consumList.add(info);
            //添加到map中
            consumInfos.put(number,consumList);
            System.out.println("该卡还未进行消费,新增一条消费记录");
        }

    }


}

5)创建菜单类

功能菜单的实现

package manager;

import entity.MobileCard;
import util.CardUtil;

import java.io.IOException;
import java.util.Scanner;

public class SosoMgr {
    private Scanner sc=new Scanner(System.in);
    private CardUtil cardUtil=new CardUtil();
    /**
     * 启动
     */
    public void start(){
        //初始化
        cardUtil.init();
        cardUtil.initScene();
        int choose; //用户选择
        boolean isExit=false;// 标记用户是否退出系统,true为退出
        String cardNum=null; //手机卡号
        //遍历菜单
        do {
            mainMenu();
            choose=sc.nextInt();
            switch (choose){
                case 1:
                    System.out.println("***用户登录***");
                    System.out.print("请输入手机号:");
                    cardNum = sc.next();
                    System.out.print("请输入密码:");
                    String passWord=sc.next();
                    //判断注册状态
                    if (cardUtil.isExistCard(cardNum,passWord)){
                        cardMenu(cardNum);
                    }else{
                        System.out.println("账号或密码输入错误");
                    }
                    break;
                case 2:
                    System.out.println("***用户注册***");
                    MobileCard card=null;
                    cardUtil.addCard(card);
                    break;
                case 3:
                    System.out.println("***使用嗖嗖***");
                    System.out.print("请输入手机卡号");
                    cardNum=sc.next();
                    if(cardUtil.isExistCard(cardNum)){
                        try {
                            cardUtil.userSoso(cardNum);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }else {
                        System.out.println("该手机号不存在");
                    }
                    break;
                case 4:
                    System.out.println("***话费充值***");
                    System.out.print("请输入要充值的手机号:");
                    cardNum=sc.next();
                    System.out.print("请输入充值金额:");
                    double money=sc.nextDouble();
                    cardUtil.chargeMoney(cardNum,money);
                    break;
                case 5:
                    System.out.println("***资费说明***");
                    try {
                        cardUtil.showDescription();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    break;
                case 6:
                    System.out.println("退出系统");
                    isExit=true;
                    break;
                default:
                    System.out.println("业务开发中...");
                    continue;
            }
            if(!isExit){
                System.out.println("按任意数字键返回");
                try {
                    choose=sc.nextInt();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }else{
                break;
            }
        } while (true);
        System.out.println("欢迎下次光临嗖嗖移动服务大厅");

    }
    //主菜单
    public static void mainMenu(){
        System.out.println("******欢迎使用嗖嗖移动业务大厅******");
        System.out.println("1.用户登录\t2.用户注册\t3.使用嗖嗖\t4.话费充值\t5.资费说明\t6.退出系统");
        System.out.println("请选择(输入1-6选择功能,其他键返回主菜单):");

    }
    //二级菜单
    public  void cardMenu(String number){
        do {
            int choice;
            System.out.println("******嗖嗖移动用户菜单******");
            System.out.println("1.本月账单查询\t2.套餐余量查询\t3.打印消费详单\t4.套餐变更\t5.办理退网");
            System.out.println("请选择(输入1-5选择功能,其他键返回上一级):");
            choice=sc.nextInt();
            switch (choice) {
                case 1:
                    System.out.println("1、本月账单查询");
                    cardUtil.showAmountDetail(number);
                    break;
                case 2:
                    System.out.println("2、套餐余量查询");
                    cardUtil.showRemainDetail(number);
                    break;
                case 3:
                    System.out.println("3、打印消费祥单");
                    cardUtil.printAmountDetail(number);
                    break;
                case 4:
                    System.out.println("4、套餐变更");
                    cardUtil.changingPack(number);
                    break;
                case 5:
                    System.out.println("5、办理退网");
                    cardUtil.delCard(number);
                    break;
                default:
                    System.out.println("返回主菜单");
                    return;
            }
        }while (true);


    }

    
}

5)创建测试类
package manager;

public class Test {
    public static void main(String[] args) {
        SosoMgr soso=new SosoMgr();
        soso.start();
        //可使用可以多进行几遍使用嗖嗖来测试话费不足提醒

    }
}

3.使用技能

实现嗖嗖移动大厅项目主要运用到以下Java技能:

  1. 面向对象思想进行程序设计
  2. 使用异常处理机制抛出并处理异常
  3. 使用集合存储和操作数据
  4. 使用I/O读写文本文件
  5. 使用Random类、String类等实用类
;