Java—— IO流的应用

带权重的点名系统

案例要求

文件中有学生的信息,每个学生的信息独占一行。包括学生的姓名,性别,权重

要求每次被抽中的学生,再次被抽中的概率在原先的基础上降低一半。

本题的核心就是带权重的随机

分析

权重,权重和,权重占比

如何将概率降低为原先的一半

假设总共10个人,一开始设定每个人的权重都是1,权重和是10,权重占比=权重/权重和,那么每个人的权重占比是0.1,说明每个人一开始被抽中的概率都是0.1,抽中后再次被抽中的概率在原先的基础上降低一半,需要将其权重降低为原来的一半。修改完权重要将其写回文件,下次再抽人时权重就是0.5了。

细节:

权重降低为原来的一半,权重和也会改变,但是改变很小(由10变为9.5),人数越多改变越小(假如一共100人时,权重和为100,抽到一人,这人权重降低为原来的一半,权重和由100变为99.5),可以视作不变,而这个人权重降低为原来的一半,权重和又视作不变,则权重占比就是原来的一半。被抽中的概率就是原先的一半

权重占比的区间范围

如何确定抽到哪个人

一开始每个人的权重占比是0.1,说明可以将长度为1的数轴分为10等份,每个人占其中一份

这就是权重占比的区间范围,例如:

于沧浪-男-1 (0.0~0.1]

双文楠-男-1 (0.1~0.2]

鞠弘杰-男-1 (0.2~0.3]

乐弘雪-男-1 (0.3~0.4]

贝泽瀚-男-1 (0.4~0.5]

孙秀逸-女-1 (0.5~0.6]

蔺文思-女-1 (0.6~0.7]

燕芷灵-女-1 (0.7~0.8]

酆若环-女-1 (0.8~0.9]

广飞瑶-女-1 (0.9~1.0]

随机一个0~1之间的数,其在哪个范围就表示抽取到了哪个同学

代码实现

Student类用于封装文件中的数据

java 复制代码
public class Student {
    private String name;
    private String sex;
    private double weight;
    
    public Student() {
    }

    public Student(String name, String sex, double weight) {
        this.name = name;
        this.sex = sex;
        this.weight = weight;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public double getWeight() {
        return weight;
    }

    public void setWeight(double weight) {
        this.weight = weight;
    }

    //toString方法数据格式与文件中相同
    //这样直接传递学生对象的toString方法就能写出相同格式的数据,
    public String toString() {
        return name + "-" + sex + "-" + weight;
    }
}

测试类

java 复制代码
import java.io.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;

public class Test2 {
    public static void main(String[] args) throws IOException {

        //定义集合存储学生对象
        ArrayList<Student> list = new ArrayList<>();

        //将文件中的数据读出,交给Student对象进行封装
        BufferedReader br = new BufferedReader(new FileReader("day05\\names.txt"));
        String line;
        while ((line = br.readLine()) != null) {
            String[] arr = line.split("-");
            Student stu = new Student(arr[0], arr[1], Double.parseDouble(arr[2]));
            //将该Student添加到集合中
            list.add(stu);
        }
        br.close();

        //获取总权重
        double weightSum = 0;
        for (Student stu : list) {
            weightSum = weightSum + stu.getWeight();
        }

        //计算每个人的权重占比,保存到一个数组中
        double[] arr = new double[list.size()];
        int index = 0;
        for (Student stu : list) {
            arr[index] = stu.getWeight() / weightSum;
            index++;
        }
        System.out.println(Arrays.toString(arr));

        //计算每个人权重占比的区间范围
        //例如第一个人权重占比是0.1,那么就规定0~0.1是他的权重占比的区间范围,记为0.1
        //0~0.1是第一个人的,那么第二个人的权重占比的区间范围就从0.1开始
        //所以第二个人的权重占比的区间范围是 第一个人权重占比~第一个人权重占比+自己的权重占比
        //依次往后
        //随机一个0~1之间的数,若该数处于0~0.1,就表示随机到了第一个人

        for (int i = 1; i < arr.length; i++) {
            //利用BigDecimal精确运算
            BigDecimal bd1 = BigDecimal.valueOf(arr[i - 1]);
            BigDecimal bd2 = BigDecimal.valueOf(arr[i]);
            arr[i] = bd1.add(bd2).doubleValue();
        }
        System.out.println(Arrays.toString(arr));

        //随机0~1之间的数
        double random = Math.random();
        System.out.println(random);
        //判断random在数组中的位置
        //数组中元素是有序的利用二分查找法
        //如果要查找的元素在数组中,返回该元素的索引
        //如果要查找的元素不在数组中,返回值 = -索引 -1
        //通过加法交换率得           索引= -(返回值 +1)
        int i = Arrays.binarySearch(arr, random);
        if (i >= 0) {
            System.out.println(i);
        } else {
            i = -i - 1;
            System.out.println(i);
        }

        //索引i表示抽到的人
        Student stu = list.get(i);
        System.out.println(stu);
        //修改这个人的权重为原来的一半
        stu.setWeight(stu.getWeight() / 2);

        //将修改后的数据写回文件
        BufferedWriter bw = new BufferedWriter(new FileWriter("day05\\names.txt"));
        for (Student s : list) {
            bw.write(s.toString());
            bw.newLine();
        }
        bw.close();
    }
}

一开始文件中的数据 第一次抽取 第二次抽取 第三次抽取

游戏注册功能以及存档功能

可以将用户信息和游戏进度写到一个文件中永久保存,这样结束程序重新启动时,数据不会丢失,借助IO将前面介绍的拼图游戏完善

程序启动类

java 复制代码
import java.io.IOException;

public class App {
    public static void main(String[] args) throws IOException {

        //创建登录界面,在登录界面中成功登录后创建游戏界面
        new LoginJFrame();
        
    }
}

登录界面

java 复制代码
import javax.swing.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;

public class LoginJFrame extends JFrame implements MouseListener {

    //不止一个方法使用的变量定义在成员变量位置
    JButton login;
    JButton register;
    JTextField username;
    JTextField password;
    JTextField code;
    String codeStr;
    JLabel showCode;
    JDialog jDialog;

    //记录所有用户
    ArrayList<User> list = new ArrayList();

    //空参构造初始化登录
    public LoginJFrame() throws IOException {

        //调用方法获取文件中记录的用户信息,存储在集合中
        UserInfo();

        //调用方法初始化登录界面
        initFrame();

        //调用方法加载登录界面图式
        loadImages();

        //添加事件监听,鼠标监听事件MouseListener
        login.addMouseListener(this);
        register.addMouseListener(this);
        showCode.addMouseListener(this);

        //设置界面可视,建议放在最后
        this.setVisible(true);
    }

    //定义方法获取文件中记录的用户信息,存储在集合中
    private void UserInfo() throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("PuzzleGame\\UserInfo.txt"));
        String line;
        while ((line = br.readLine()) != null) {
            String[] arr = line.split("&");
            String username = arr[0].split("=")[1];
            String password = arr[1].split("=")[1];
            User u = new User(username, password);
            list.add(u);
        }
        br.close();
    }

    //定义方法加载登录界面图式
    private void loadImages() {
        //1. 添加用户名文字
        JLabel usernameText = new JLabel(new ImageIcon("puzzlegame\\image\\login\\用户名.png"));
        usernameText.setBounds(116, 135, 47, 17);
        this.getContentPane().add(usernameText);

        //2.添加用户名输入框
        username = new JTextField();
        username.setBounds(195, 134, 200, 30);
        this.getContentPane().add(username);

        //3.添加密码文字
        JLabel passwordText = new JLabel(new ImageIcon("puzzlegame\\image\\login\\密码.png"));
        passwordText.setBounds(130, 195, 32, 16);
        this.getContentPane().add(passwordText);

        //4.密码输入框
        password = new JTextField();
        password.setBounds(195, 195, 200, 30);
        this.getContentPane().add(password);

        //验证码提示
        JLabel codeText = new JLabel(new ImageIcon("puzzlegame\\image\\login\\验证码.png"));
        codeText.setBounds(133, 256, 50, 30);
        this.getContentPane().add(codeText);

        //验证码的输入框
        code = new JTextField();
        code.setBounds(195, 256, 100, 30);
        this.getContentPane().add(code);

        //调用工具类生成验证码
        codeStr = CodeUtil.getCode();
        //显示生成的验证码
        showCode = new JLabel();
        //设置内容
        showCode.setText(codeStr);
        //位置和宽高
        showCode.setBounds(300, 256, 50, 30);
        //添加到界面
        this.getContentPane().add(showCode);

        //5.添加登录按钮
        login = new JButton();
        login.setBounds(123, 310, 128, 47);
        //设置按钮背景图
        login.setIcon(new ImageIcon("puzzlegame\\image\\login\\登录按钮.png"));
        //去除按钮的默认边框
        login.setBorderPainted(false);
        //去除按钮的默认背景
        login.setContentAreaFilled(false);
        this.getContentPane().add(login);

        //6.添加注册按钮
        register = new JButton();
        register.setBounds(256, 310, 128, 47);
        //设置按钮背景图
        register.setIcon(new ImageIcon("puzzlegame\\image\\login\\注册按钮.png"));
        //去除按钮的默认边框
        register.setBorderPainted(false);
        //去除按钮的默认背景
        register.setContentAreaFilled(false);
        this.getContentPane().add(register);

        //7.添加界面背景图片
        JLabel background = new JLabel(new ImageIcon("puzzlegame\\image\\login\\background.png"));
        background.setBounds(0, 0, 470, 390);
        this.getContentPane().add(background);
    }

    //定义方法初始化登录界面
    private void initFrame() {
        this.setSize(488, 430);
        this.setTitle("拼图游戏登录");
        this.setAlwaysOnTop(true);
        this.setLocationRelativeTo(null);
        this.setDefaultCloseOperation(3);
        this.setLayout(null);
    }

    //定义方法判断用户名密码是否正确
    private boolean checkUser(ArrayList<User> list, User u) {
        //查询用户名是否在集合中存在
        int index = checkUserName(list, u);
        if (index == -1) {
            //System.out.println("用户名和密码错误");
            return false;
        }

        //到这步,证明用户名存在,判断对应的密码是否相同
        if (list.get(index).getPassWord().equals(u.getPassWord())) {
            //相同返回true
            return true;
        }
        //不同返回false
        return false;
    }

    //定义方法查询用户名是否在集合中存在,存在返回索引,不存在返回-1
    private int checkUserName(ArrayList<User> list, User u) {
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).getUserName().equals(u.getUserName())) {
                return i;
            }
        }
        return -1;
    }

    //定义方法展示弹窗
    private void showDialog(String s) {

        jDialog = new JDialog();

        //设置弹框的宽和高:100,100
        jDialog.setSize(100, 100);
        //设置弹框居中
        jDialog.setLocationRelativeTo(null);
        //设置弹框置顶
        jDialog.setAlwaysOnTop(true);
        //设置关闭后进行其他操作
        jDialog.setModal(true);
        //创建一个JLabel去编写文本内容
        JLabel textJlabel = new JLabel(s);
        textJlabel.setBounds(20, 50, 120, 60);
        //把文本JLabel添加到弹框当中
        jDialog.getContentPane().add(textJlabel);
        //把弹框展示出来
        jDialog.setVisible(true);
    }


    //鼠标监听事件MouseListener
    //鼠标单击调用该方法
    @Override
    public void mouseClicked(MouseEvent e) {
        Object source = e.getSource();
        if (source == login) {
            //如果是登录按钮,获取输入框中的用户名,密码,验证码
            String myUserName = username.getText();
            String myPassWord = password.getText();
            String myCode = code.getText();

            //判断是否为空,如果为空,提示:用户名或密码为空
            if (myUserName.equals("") || myPassWord.equals("")) {
                //展示弹框:用户名或密码为空
                //调用方法展示弹框,参数为需要展示的文字
                showDialog("用户名或密码为空");
                //换一个验证码
                codeStr = CodeUtil.getCode();
                showCode.setText(codeStr);
                return;
            }

            //判断验证码是否正确
            if (!(myCode.equalsIgnoreCase(codeStr))) {
                //展示弹框:验证码错误
                showDialog("验证码错误");
                System.out.println("验证码错误");
                //换一个验证码
                codeStr = CodeUtil.getCode();
                showCode.setText(codeStr);
                return;
            }

            //判断用户名和密码是否为正确,如果正确隐藏登录界面,进入游戏界面。
            //调用方法判断
            User myUser = new User(myUserName, myPassWord);
            boolean flag = checkUser(list, myUser);
            if (!flag) {
                //展示弹框:用户名或密码错误
                showDialog("用户名或密码错误");
                System.out.println("用户名或密码错误");
                //换一个验证码
                codeStr = CodeUtil.getCode();
                showCode.setText(codeStr);
                return;
            }

            //到这步,证明用户名和密码正确
            //隐藏登录界面,进入游戏界面
            this.setVisible(false);
            new GameJFrame();

        } else if (source == showCode) {
            //重新生成验证码
            codeStr = CodeUtil.getCode();
            showCode.setText(codeStr);

        } else if (source == register) {
            //进行注册
            //关闭登录界面
            this.setVisible(false);
            //创建注册界面
            //把存储用户信息的集合传递过去,注册时用户名不能与集合中的重复
            new RegisterJFrame(list);
        }
    }

    //鼠标按下调用该方法
    @Override
    public void mousePressed(MouseEvent e) {
        Object source = e.getSource();

        if (source == login) {
            //登录按钮
            //按下不松的时候利用setIcon方法,修改登录按钮的背景色
            login.setIcon(new ImageIcon("PuzzleGame\\image\\login\\登录按下.png"));
        } else if (source == register) {
            //注册按钮
            //按下不松的时候利用setIcon方法,修改注册按钮的背景色
            register.setIcon(new ImageIcon("PuzzleGame\\image\\login\\注册按下.png"));
        }
    }

    //鼠标释放调用该方法
    @Override
    public void mouseReleased(MouseEvent e) {
        Object source = e.getSource();
        //登录按钮
        //释放的时候利用setIcon方法,恢复登录按钮的背景色
        if (source == login) {
            login.setIcon(new ImageIcon("PuzzleGame\\image\\login\\登录按钮.png"));
        } else if (source == register) {
            //注册按钮
            //释放的时候利用setIcon方法,恢复注册按钮的背景色
            register.setIcon(new ImageIcon("PuzzleGame\\image\\login\\注册按钮.png"));
        }

    }

    //鼠标划入调用该方法
    @Override
    public void mouseEntered(MouseEvent e) {

    }

    //鼠标划出调用该方法
    @Override
    public void mouseExited(MouseEvent e) {

    }


}

注册界面

java 复制代码
import javax.swing.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

public class RegisterJFrame extends JFrame implements MouseListener {

    //存储用户集合
    ArrayList<User> list;

    //提升三个输入框的变量的作用范围,让这三个变量可以在本类中所有方法里面可以使用。
    JTextField username = new JTextField();
    JTextField password = new JTextField();
    JTextField rePassword = new JTextField();

    //提升两个按钮变量的作用范围,让这两个变量可以在本类中所有方法里面可以使用。
    JButton submit = new JButton();
    JButton reset = new JButton();

    //弹窗
    JDialog jDialog;

    public RegisterJFrame(ArrayList<User> list) {

        //接收传递过来的用户集合
        this.list = list;

        //初始化界面
        initFrame();

        //调用方法加载登录界面图式
        initView();

        //添加事件监听,鼠标监听事件MouseListener
        submit.addMouseListener(this);
        reset.addMouseListener(this);

        //设置界面可视,建议放在最后
        this.setVisible(true);
    }

    //初始化界面
    private void initFrame() {
        //对自己的界面做一些设置。
        //设置宽高
        this.setSize(488, 430);
        //设置标题
        this.setTitle("拼图游戏 V1.0注册");
        //取消内部默认布局
        this.setLayout(null);
        //设置关闭模式
        this.setDefaultCloseOperation(3);
        //设置居中
        this.setLocationRelativeTo(null);
        //设置置顶
        this.setAlwaysOnTop(true);
    }

    //加载登录界面图式
    private void initView() {
        //添加注册用户名的文本
        JLabel usernameText = new JLabel(new ImageIcon("puzzlegame\\image\\register\\注册用户名.png"));
        usernameText.setBounds(85, 135, 80, 20);

        //添加注册用户名的输入框
        username.setBounds(195, 134, 200, 30);

        //添加注册密码的文本
        JLabel passwordText = new JLabel(new ImageIcon("puzzlegame\\image\\register\\注册密码.png"));
        passwordText.setBounds(97, 193, 70, 20);

        //添加密码输入框
        password.setBounds(195, 195, 200, 30);

        //添加再次输入密码的文本
        JLabel rePasswordText = new JLabel(new ImageIcon("puzzlegame\\image\\register\\再次输入密码.png"));
        rePasswordText.setBounds(64, 255, 95, 20);

        //添加再次输入密码的输入框
        rePassword.setBounds(195, 255, 200, 30);

        //注册的按钮
        submit.setIcon(new ImageIcon("puzzlegame\\image\\register\\注册按钮.png"));
        submit.setBounds(123, 310, 128, 47);
        submit.setBorderPainted(false);
        submit.setContentAreaFilled(false);

        //重置的按钮
        reset.setIcon(new ImageIcon("puzzlegame\\image\\register\\重置按钮.png"));
        reset.setBounds(256, 310, 128, 47);
        reset.setBorderPainted(false);
        reset.setContentAreaFilled(false);

        //背景图片
        JLabel background = new JLabel(new ImageIcon("puzzlegame\\image\\register\\background.png"));
        background.setBounds(0, 0, 470, 390);

        this.getContentPane().add(usernameText);
        this.getContentPane().add(passwordText);
        this.getContentPane().add(rePasswordText);
        this.getContentPane().add(username);
        this.getContentPane().add(password);
        this.getContentPane().add(rePassword);
        this.getContentPane().add(submit);
        this.getContentPane().add(reset);
        this.getContentPane().add(background);
    }

    //定义方法展示弹窗
    private void showDialog(String s) {

        jDialog = new JDialog();

        //设置弹框的宽和高:100,100
        jDialog.setSize(100, 100);
        //设置弹框居中
        jDialog.setLocationRelativeTo(null);
        //设置弹框置顶
        jDialog.setAlwaysOnTop(true);
        //设置关闭后进行其他操作
        jDialog.setModal(true);
        //创建一个JLabel去编写文本内容
        JLabel textJlabel = new JLabel(s);
        textJlabel.setBounds(20, 50, 120, 60);
        //把文本JLabel添加到弹框当中
        jDialog.getContentPane().add(textJlabel);
        //把弹框展示出来
        jDialog.setVisible(true);
    }

    //鼠标单击事件
    @Override
    public void mouseClicked(MouseEvent e) {
        Object source = e.getSource();
        if (source == submit) {
            //注册按钮
            //得到三个输入框中的数据
            String myUsername = username.getText();
            String myPassword = password.getText();
            String myRePassword = rePassword.getText();

            //三个输入框中的数据不能为空
            if (myUsername.length() == 0 || myPassword.length() == 0 || myRePassword.length() == 0) {
                //弹窗提醒
                showDialog("输入框不能为空");
                //结束方法
                return;
            }

            //密码和再次输入的密码要相同
            if (!myPassword.equals(myRePassword)) {
                //弹窗提醒
                showDialog("两次输入的密码不同");
                //结束方法
                return;
            }

            //用户名要符合规定,4-16位大小写字母和数字
            if (!myUsername.matches("[a-zA-Z0-9]{4,16}")) {
                //弹窗提醒
                showDialog("用户名要4-16位大小写字母和数字");
                //结束方法
                return;
            }

            //密码要符合规定,至少6位大小写字母和数字
            if (!myPassword.matches("[a-zA-Z0-9]{6,}")) {
                //弹窗提醒
                showDialog("密码要至少6位大小写字母和数字");
                //结束方法
                return;
            }

            //用户名不能与集合中的重复
            //调用方法判断
            if (containsUsername(myUsername)) {
                //弹窗提醒
                showDialog("用户名已存在");
                //结束方法
                return;
            }

            //到这一步,说明用户名和密码符合条件
            //将其添加到用户集合中,并写到文件中永久存储
            User u = new User(myUsername, myPassword);
            list.add(u);

            try {

                BufferedWriter bw = new BufferedWriter(new FileWriter("PuzzleGame\\UserInfo.txt"));
                for (User user : list) {
                    bw.write(user.toString());
                    bw.newLine();
                }
                bw.close();

                //开启登录界面
                this.setVisible(false);
                new LoginJFrame();

            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }

        } else if (source == reset) {
            //重置按钮
            //清空三个输入框中的内容
            username.setText("");
            password.setText("");
            rePassword.setText("");

        }
    }

    //判断自定义用户名是否与集合中的用户名重复
    private boolean containsUsername(String myUsername) {
        for (User u : list) {
            if (u.getUserName().equals(myUsername)) {
                return true;
            }
        }
        return false;
    }

    //鼠标按下事件
    @Override
    public void mousePressed(MouseEvent e) {
        Object source = e.getSource();
        //切换按钮深色背景
        if (source == submit) {
            submit.setIcon(new ImageIcon("PuzzleGame\\image\\register\\注册按下.png"));

        } else if (source == reset) {
            reset.setIcon(new ImageIcon("PuzzleGame\\image\\register\\重置按下.png"));

        }
    }

    //鼠标释放事件
    @Override
    public void mouseReleased(MouseEvent e) {
        Object source = e.getSource();
        //切换按钮正常背景
        if (source == submit) {
            submit.setIcon(new ImageIcon("PuzzleGame\\image\\register\\注册按钮.png"));

        } else if (source == reset) {
            reset.setIcon(new ImageIcon("PuzzleGame\\image\\register\\重置按钮.png"));

        }

    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }
}

游戏界面

java 复制代码
import javax.swing.*;
import javax.swing.border.BevelBorder;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.*;
import java.util.Random;

//游戏界面类继承界面类,实现键盘监听和动作监听
public class GameJFrame extends JFrame implements KeyListener, ActionListener {

    //多个方法需要用到的变量,记录在成员变量的位置
    //定义二维数组记录打乱后的0~15,每一个数字对应一张拼图
    int[][] data = new int[4][4];

    //定义胜利时的数据数组
    int[][] winArr = {
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 10, 11, 12},
            {13, 14, 15, 0}
    };

    //定义坐标记录0的位置
    int x = 0;
    int y = 0;

    //定义计数器记录移动步数
    int stepCount = 0;

    //创建菜单选项里下面的条目
    JMenuItem restartItem = new JMenuItem("重新开始");
    JMenuItem reLoginItem = new JMenuItem("重新登录");
    JMenuItem exitItem = new JMenuItem("退出游戏");

    JMenuItem animalItem = new JMenuItem("动物");
    JMenuItem girllItem = new JMenuItem("美女");
    JMenuItem sportItem = new JMenuItem("运动");

    JMenuItem AccountItem = new JMenuItem("公众号");

    JMenu saveJmenu = new JMenu("存档");

    JMenuItem saveItem0 = new JMenuItem("存档0(空)");
    JMenuItem saveItem1 = new JMenuItem("存档1(空)");
    JMenuItem saveItem2 = new JMenuItem("存档2(空)");

    JMenu loadJmenu = new JMenu("读档");

    JMenuItem loadItem0 = new JMenuItem("读档0(空)");
    JMenuItem loadItem1 = new JMenuItem("读档1(空)");
    JMenuItem loadItem2 = new JMenuItem("读档2(空)");

    //定义图片路径,方便后面修改
    String path = "PuzzleGame\\image\\animal\\animal1\\";

    Random r = new Random();

    //空参构造初始化游戏
    public GameJFrame() {

        //调用方法初始化界面
        initFrame();

        //给界面添加事件监听,键盘监听KeyListener
        this.addKeyListener(this);

        //调用方法添加菜单
        initMenu();

        //给菜单里的条目添加事件监听,动作监听ActionListener
        restartItem.addActionListener(this);
        reLoginItem.addActionListener(this);
        exitItem.addActionListener(this);
        AccountItem.addActionListener(this);

        animalItem.addActionListener(this);
        girllItem.addActionListener(this);
        sportItem.addActionListener(this);

        saveItem0.addActionListener(this);
        saveItem1.addActionListener(this);
        saveItem2.addActionListener(this);
        loadItem0.addActionListener(this);
        loadItem1.addActionListener(this);
        loadItem2.addActionListener(this);

        //调用方法初始化数据
        initData();

        //调用方法根据初始化后的数据加载图片
        loadImages();

        //设置界面可视,建议放在最后
        this.setVisible(true);
    }

    //定义方法判断游戏是否胜利
    private boolean victoty() {
        for (int i = 0; i < data.length; i++) {
            for (int j = 0; j < data[i].length; j++) {
                if (data[i][j] != winArr[i][j]) {
                    //data中的数据有一个与胜利数组中的数据不同,就说明没有胜利
                    return false;
                }
            }
        }
        return true;
    }

    //定义方法根据初始化后的数据加载图片
    private void loadImages() {

        //清空所有图片
        this.getContentPane().removeAll();

        //如果胜利,加载胜利图标
        if (victoty()) {
            JLabel vicJLabel = new JLabel(new ImageIcon("PuzzleGame\\image\\win.png"));
            vicJLabel.setBounds(203, 283, 197, 73);
            this.getContentPane().add(vicJLabel);
        }

        //加载计数器
        JLabel countJLabel = new JLabel("步数:" + stepCount);
        countJLabel.setBounds(50, 30, 100, 20);
        this.getContentPane().add(countJLabel);

        //利用循环加载拼图图片
        for (int i = 0; i < data.length; i++) {
            for (int j = 0; j < data[i].length; j++) {
                //获得data数组里的数据
                int number = data[i][j];
                //根据数据创建图片对象
                ImageIcon icon = new ImageIcon(path + number + ".jpg");
                //创建管理容器,将图片交给管理容器
                JLabel jLabel = new JLabel(icon);
                //设置管理容器位置,大小
                jLabel.setBounds(105 * j + 83, 105 * i + 134, 105, 105);
                //设置边框
                jLabel.setBorder(new BevelBorder(0));
                //将管理容器添加到界面中
                this.getContentPane().add(jLabel);
            }
        }

        //添加背景图片
        JLabel bgJLabel = new JLabel(new ImageIcon("PuzzleGame\\image\\background.png"));
        bgJLabel.setBounds(40, 40, 508, 560);
        this.getContentPane().add(bgJLabel);

        //刷新一下
        this.getContentPane().repaint();
    }

    //定义方法初始化数据
    private void initData() {

        int[] tempArr = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};

        //打乱数据,根据打乱后的数据就能实现打乱拼图
        for (int i = 0; i < tempArr.length; i++) {
            int index = r.nextInt(tempArr.length);
            int temp = tempArr[i];
            tempArr[i] = tempArr[index];
            tempArr[index] = temp;
        }

        //将打乱后的数据记录在二维数组data中
        for (int i = 0; i < tempArr.length; i++) {
            //如果是0,记录0的位置
            if (tempArr[i] == 0) {
                x = i / 4;
                y = i % 4;
            }
            data[i / 4][i % 4] = tempArr[i];
        }

    }

    //定义方法添加菜单
    private void initMenu() {

        //创建菜单
        JMenuBar jMenuBar = new JMenuBar();

        //创建菜单的选项
        JMenu functionJmenu = new JMenu("功能");
        JMenu aboutJmenu = new JMenu("关于我们");


        //嵌套二级菜单,JMenu里面是可以再次添加其他的JMenu
        JMenu updateJmenu = new JMenu("更换图片");
        updateJmenu.add(animalItem);
        updateJmenu.add(girllItem);
        updateJmenu.add(sportItem);

        //存档
        saveJmenu.add(saveItem0);
        saveJmenu.add(saveItem1);
        saveJmenu.add(saveItem2);

        //读档
        loadJmenu.add(loadItem0);
        loadJmenu.add(loadItem1);
        loadJmenu.add(loadItem2);

        //将条目添加到选项中
        functionJmenu.add(updateJmenu);
        functionJmenu.add(restartItem);
        functionJmenu.add(reLoginItem);
        functionJmenu.add(exitItem);
        functionJmenu.add(saveJmenu);
        functionJmenu.add(loadJmenu);

        aboutJmenu.add(AccountItem);

        //将选项添加到菜单中
        jMenuBar.add(functionJmenu);
        jMenuBar.add(aboutJmenu);


        //将菜单添加到界面中
        this.setJMenuBar(jMenuBar);

        //游戏开始时获取游戏数据信息修改菜单提示
        getGameInfo();


    }

    //游戏开始时获取游戏数据信息修改菜单提示
    private void getGameInfo() {
        //遍历存档文件夹
        File f = new File("PuzzleGame\\save");
        File[] files = f.listFiles();
        if (files == null) {
            return;
        }
        for (File file : files) {
            //读取每一个文件信息
            GameInfo gi = null;
            try {
                ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
                gi = (GameInfo) ois.readObject();
                ois.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
            //获取要体现在菜单上的步数信息
            int step = gi.getStepCount();
            //获取要修改的条目索引
            String name = file.getName();//save0/1/2.data
            int index = name.charAt(4) - '0';
            //进行修改
            JMenuItem item1 = saveJmenu.getItem(index);
            item1.setText("存档" + index + "(" + step + "步)");
            JMenuItem item2 = loadJmenu.getItem(index);
            item2.setText("读档" + index + "(" + step + "步)");
        }

    }

    //定义方法初始化界面
    private void initFrame() {
        //设置大小
        this.setSize(603, 680);
        //设置标题
        this.setTitle("拼图游戏");
        //设置居中
        this.setLocationRelativeTo(null);
        //设置置顶
        this.setAlwaysOnTop(true);
        //设置关闭模式
        this.setDefaultCloseOperation(3);

        //设置解除默认居中放置,只有解除了,才能根据xy轴的方式添加主件
        this.setLayout(null);


    }

    //动作监听ActionListener,鼠标单击或按空格调用该方法
    @Override
    public void actionPerformed(ActionEvent e) {
        Object source = e.getSource();
        if (source == restartItem) {
            //重新开始
            //重新初始化数据
            initData();
            //计数器归0
            stepCount = 0;
            //重新根据初始化后的数据加载图片
            loadImages();

        } else if (source == reLoginItem) {
            //重新登录
            //关闭本类(游戏类)
            this.setVisible(false);
            //创建登录界面
            try {
                new LoginJFrame();
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }

        } else if (source == exitItem) {
            //退出游戏
            System.exit(0);

        } else if (source == AccountItem) {
            //显示公众号
            //创建弹窗
            JDialog accJDialog = new JDialog();
            //加载弹窗中的图片
            JLabel aboutJLabel = new JLabel(new ImageIcon("PuzzleGame\\image\\about.png"));
            aboutJLabel.setBounds(0, 0, 258, 258);
            //将图片添加到弹窗中
            accJDialog.getContentPane().add(aboutJLabel);
            //设置弹窗的大小
            accJDialog.setSize(344, 344);
            //设置弹窗置顶
            accJDialog.setAlwaysOnTop(true);
            //设置弹窗居中放置
            accJDialog.setLocationRelativeTo(null);
            //设置关闭后进行其他操作
            accJDialog.setModal(true);
            //设置弹窗可视
            accJDialog.setVisible(true);

        } else if (source == animalItem) {
            //随机更换动物图片
            int rAnimalNum = r.nextInt(8) + 1;
            path = "PuzzleGame\\image\\animal\\animal" + rAnimalNum + "\\";
            //重新初始化数据
            initData();
            //计数器归0
            stepCount = 0;
            //重新根据初始化后的数据加载图片
            loadImages();

        } else if (source == girllItem) {
            //随机更换美女图片
            int rGirlNum = r.nextInt(13) + 1;
            path = "PuzzleGame\\image\\girl\\girl" + rGirlNum + "\\";
            //重新初始化数据
            initData();
            //计数器归0
            stepCount = 0;
            //重新根据初始化后的数据加载图片
            loadImages();

        } else if (source == sportItem) {
            //随机更换运动图片
            int rSportlNum = r.nextInt(10) + 1;
            path = "PuzzleGame\\image\\sport\\sport" + rSportlNum + "\\";
            //重新初始化数据
            initData();
            //计数器归0
            stepCount = 0;
            //重新根据初始化后的数据加载图片
            loadImages();

        } else if (source == saveItem0 || source == saveItem1 || source == saveItem2) {
            //进行存档
            //得到点击的存档序号
            JMenuItem item = (JMenuItem) source;
            String text = item.getText();//存档0/1/2(空)
            int index = text.charAt(2) - '0';

            //封装要保存的游戏数据
            //包括:对应拼图的data数组,记录0位置的坐标x,y,图片路径path,步数stepCount
            GameInfo gi = new GameInfo(data, x, y, stepCount, path);

            //将游戏信息对象以序列化流写入对应文件中,防止篡改数据
            try {
                ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("PuzzleGame\\save\\save" + index + ".data"));
                oos.writeObject(gi);
                oos.close();
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }

            //修改菜单上的提示信息
            item.setText("存档" + index + "(" + stepCount + "步)");
            //获取与存档对应的读档菜单
            JMenuItem item1 = loadJmenu.getItem(index);
            item1.setText("读档" + index + "(" + stepCount + "步)");

        } else if (source == loadItem0 || source == loadItem1 || source == loadItem2) {
            //进行读档
            //得到点击的读档序号
            JMenuItem item = (JMenuItem) source;
            String text = item.getText();//读档0/1/2(空)
            int index = text.charAt(2) - '0';

            //利用反序列化流读取文件中的游戏数据
            GameInfo gi = null;
            try {
                ObjectInputStream ois = new ObjectInputStream(new FileInputStream("PuzzleGame\\save\\save" + index + ".data"));
                gi = (GameInfo) ois.readObject();
                ois.close();
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            } catch (ClassNotFoundException ex) {
                throw new RuntimeException(ex);
            }

            //将游戏数据更新为存档的游戏数据
            data = gi.getData();
            x = gi.getX();
            y = gi.getY();
            path = gi.getPath();
            stepCount = gi.getStepCount();
            //重新加载游戏
            loadImages();
        }


    }

    //键盘监听KeyListener
    //该方法基本不使用
    @Override
    public void keyTyped(KeyEvent e) {

    }

    //长按键盘时调用该方法
    @Override
    public void keyPressed(KeyEvent e) {

        //判断是否胜利
        if (victoty()) {
            //如果胜利,禁止进行显示全图操作,直接结束方法
            return;
        }

        //长按w显示全图
        int keyCode = e.getKeyCode();
        if (keyCode == 87) {
            //清空所有图片
            this.getContentPane().removeAll();

            //加载计数器
            JLabel countJLabel = new JLabel("步数:" + stepCount);
            countJLabel.setBounds(50, 30, 100, 20);
            this.getContentPane().add(countJLabel);

            //加载全图
            JLabel picJLabel = new JLabel(new ImageIcon(path + "all.jpg"));
            picJLabel.setBounds(83, 134, 420, 420);
            this.getContentPane().add(picJLabel);

            //添加背景图片
            JLabel bgJLabel = new JLabel(new ImageIcon("PuzzleGame\\image\\background.png"));
            bgJLabel.setBounds(40, 40, 508, 560);
            this.getContentPane().add(bgJLabel);

            //刷新一下
            this.getContentPane().repaint();
        }

    }

    //按下并松开键盘时调用该方法
    @Override
    public void keyReleased(KeyEvent e) {
        //判断是否胜利
        if (victoty()) {
            //如果胜利,禁止进行移动操作,直接结束方法
            return;
        }
        int keyCode = e.getKeyCode();
        if (keyCode == 37) {
            //判断是否到了最左边
            if (y == 0) {
                //如果到了最左边,不能进行向左,直接结束方法
                return;
            }
            //没到最左边,进行下面的操作
            System.out.println("向左");
            //更改data数组里的数据
            data[x][y] = data[x][y - 1];
            data[x][y - 1] = 0;
            //更改0的坐标
            y--;
            //步数加1
            stepCount++;
            //按照此数据重新加载图片
            loadImages();

        } else if (keyCode == 38) {
            //判断是否到了最上边
            if (x == 0) {
                //如果到了最上边,不能进行向上,直接结束方法
                return;
            }
            //没到最上边,进行下面的操作
            System.out.println("向上");
            //更改data数组里的数据
            data[x][y] = data[x - 1][y];
            data[x - 1][y] = 0;
            //更改0的坐标
            x--;
            //步数加1
            stepCount++;
            //按照此数据重新加载图片
            loadImages();

        } else if (keyCode == 39) {
            //判断是否到了最右边
            if (y == 3) {
                //如果到了最右边,不能进行向右,直接结束方法
                return;
            }
            //没到最右边,进行下面的操作
            System.out.println("向右");
            //更改data数组里的数据
            data[x][y] = data[x][y + 1];
            data[x][y + 1] = 0;
            //更改0的坐标
            y++;
            //步数加1
            stepCount++;
            //按照此数据重新加载图片
            loadImages();

        } else if (keyCode == 40) {
            //判断是否到了最下边
            if (x == 3) {
                //如果到了最下边,不能进行向下,直接结束方法
                return;
            }
            //没到最下边,进行下面的操作
            System.out.println("向下");
            //更改data数组里的数据
            data[x][y] = data[x + 1][y];
            data[x + 1][y] = 0;
            //更改0的坐标
            x++;
            //步数加1
            stepCount++;
            //按照此数据重新加载图片
            loadImages();

        } else if (keyCode == 87) {
            //松开w恢复原状
            loadImages();

        } else if (keyCode == 65) {
            //一键通过
            for (int i = 0; i < data.length; i++) {
                for (int j = 0; j < data[i].length; j++) {
                    data[i][j] = winArr[i][j];
                }
            }
            loadImages();
        }

    }

}

封装用户信息

java 复制代码
public class User {
    private String userName;
    private String passWord;

    public User() {
    }

    public User(String userName, String passWord) {
        this.userName = userName;
        this.passWord = passWord;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassWord() {
        return passWord;
    }

    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }

    @Override
    public String toString() {
        return "uesrname="+userName+"&password="+passWord;
    }
}

封装游戏信息

java 复制代码
import java.io.Serial;
import java.io.Serializable;

public class GameInfo implements Serializable {
    @Serial
    private static final long serialVersionUID = 3545018877843048289L;

    private int[][] data;
    private int x;
    private int y;
    private int stepCount;
    private String path;

    public GameInfo() {
    }

    public GameInfo(int[][] data, int x, int y, int stepCount, String path) {
        this.data = data;
        this.x = x;
        this.y = y;
        this.stepCount = stepCount;
        this.path = path;
    }

    public int[][] getData() {
        return data;
    }

    public void setData(int[][] data) {
        this.data = data;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public int getStepCount() {
        return stepCount;
    }

    public void setStepCount(int stepCount) {
        this.stepCount = stepCount;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public String toString() {
        return "GameInfo{data = " + data + ", x = " + x + ", y = " + y + ", stepCount = " + stepCount + ", path = " + path + "}";
    }
}

生成验证码工具类

java 复制代码
import java.util.Random;

public class CodeUtil {

    private CodeUtil(){}

    public static String getCode(){

        //定义StringBuilder方便拼接字符串
        StringBuilder sb = new StringBuilder();

        //记录a~z,A~Z
        char[] ch = new char[52];
        for (int i = 0; i < ch.length; i++) {
            if (i < 26) {
                ch[i] = (char) ('a' + i);
            } else {
                ch[i] = (char) ('A' + i - 26);
            }
        }

        Random r = new Random();

        //随机抽取4个字母
        for (int i = 0; i < 4; i++) {
            int index = r.nextInt(ch.length);
            char randomCh = ch[index];
            //拼接
            sb.append(randomCh);
        }

        //随机抽取1个字母
        int randomNum = r.nextInt(10);
        //拼接
        sb.append(randomNum);

        //数字可以位于随机位置,因此最后一位需要与随机位置交换
        char[] chars = sb.toString().toCharArray();
        int index = r.nextInt(chars.length);
        char temp = chars[chars.length-1];
        chars[chars.length-1] = chars[index];
        chars[index] = temp;

        return new String(chars);

    }

}
相关推荐
设计师小聂!4 分钟前
spring cloud alibaba Sentinel详解
java·spring cloud·sentinel
CrazyClaz6 分钟前
JVM(Java虚拟机)
java·jvm
总是难免34 分钟前
设计模式 - 模板方法模式
java·设计模式·模板方法模式
感谢地心引力1 小时前
【Matlab】雷达图/蛛网图
开发语言·matlab
逾非时1 小时前
python网络爬虫的基本使用
开发语言·爬虫·python
ppdkx1 小时前
python训练营第33天
开发语言·python
攻城狮7号1 小时前
Java三十而立:Java 的30岁与Spring AI 1.0正式发布
java·人工智能·深度学习·ai·spring ai
Su米苏1 小时前
Spring Boot 中修改 HTTP 响应状态码(即 `response.status`)可以通过以下几种方式实现
java
玉笥寻珍2 小时前
从零开始:Python语言进阶之异常处理
开发语言·python
Java永无止境2 小时前
JavaSE常用API之Runtime类:掌控JVM运行时环境
java·开发语言·jvm