Java io流

发布于:2025-05-01 ⋅ 阅读:(44) ⋅ 点赞:(0)

io流

io流的概述

io流的体系



字节输出流基本用法 

package mybytestream;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class ByteStreamDemo1 {
    public static void main(String[] args) throws IOException {
        /*
        演示:宁节输出流FileOutputStream
        实现需求:写出一段文字到本地文件中。(暂时不写中文)
        实现步骤:
        创建对象
        写出数据
        释放资源
         */

        //1.创建对象
        //1.创建对象
        //写出 输出流 OutputStream
        //本地文件
        FileOutputStream fos=new FileOutputStream("..\\myio\\a.txt");
        //2.写出数据
        fos.write(97);
        //3.释放数据
        fos.close();
    }
}

 FileOutputStream写数据的3种方式

package mybytestream;

import java.io.FileOutputStream;
import java.io.IOException;

public class ByteStreamDemo2 {
    public static void main(String[] args) throws IOException {
        /*
        void write(int b)一次写一个字节数据
        void write(byte[] b)一次写一个字节数组数据
        void write(byte[]b, int off, int len)一次写一个字节数组的部分数据
        参数一:
        数组
        参数二:
        起始索引
        参数三:
        个数
         */

        //1.创建对象
        FileOutputStream fos=new FileOutputStream("..\\myio\\a.txt");
        //2.写出数据
        //fos.write(97);//a
        //fos.write(98);//b
        byte[] bytes={97,98,99,100,101};
        //fos.write(bytes);//abcde

        fos.write(bytes,1,2);//bc
        //3.释放资源
        fos.close();;
    }
}

换行和续写
package mybytestream;

import java.io.FileOutputStream;
import java.io.IOException;

public class ByteStreamDem03 {
    public static void main(String[] args) throws IOException {
        /*
        换行写:
        再次写出一个换行符就可以了
        windows:\r\n
        Linux:\n
        Mac:\r
        细节:
        在windows操作系统当中,java对回车换行进行了优化。
        虽然完整的是\r\n,但是我们写其中一个\r或者\n,
        java也可以实现换行,因为java在底层会补全。
        建议:
        不要省略,还是写全了。
        续写:
        如果想要续写,打开续写开关即可
        开关位置:创建对象的第二个参数
        默认false:表示关闭续写,此时创建对象会清空文件
        手动传递true:表示打开续写,此时创建对象不会清空文件
         */

        //1.创建对象
        FileOutputStream fos=new FileOutputStream("..\\myio\\a.txt",true);
        //2.写出数据
        String str1="阳雨婵";
        byte[] bytes1=str1.getBytes();
        fos.write(bytes1);

        //再次写出一个换行符就可以了
        String wrap="\r\n";
        byte[] bytes2=wrap.getBytes();
        fos.write(bytes2);

        String str3="666";
        byte[] bytes3=str3.getBytes();
        fos.write(bytes3);

        //3.释放资源
        fos.close();
    }
}

FileInputStream字节输入流的基本用法 

package mybytestream;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ByteStreamDemo4 {
    public static void main(String[] args) throws IOException {
        /*
        演示:字节输入流FileInputStream
        实现需求:读取文件中的数据。(暂时不写中文)
        实现步骤:
        创建对象
        读取数据
        释放资源
         */
        
        //1.创建对象
        FileInputStream fis=new FileInputStream("..\\myio\\a.txt");
        //2.读取数据
        for (int i = 0; i < 5; i++) {
            int b=fis.read();
            System.out.println((char)b);
        }
        //3.释放资源
        fis.close();
    }
}

所需要注意的细节

循环读取
package mybytestream;

import java.io.FileInputStream;
import java.io.IOException;

public class ByteStreamDemo5 {
    public static void main(String[] args) throws IOException {
        /*
        字节输入流循环读取
         */

        //1.创建对象
        FileInputStream fis=new FileInputStream("..\\myio\\a.txt");
        //2.循环读取
        int b;
        while((b=fis.read())!=-1){
            System.out.println((char) b);
        }
        //3.释放资源
        fis.close();

        /*
        read:表示读取数据,而且是读取一个数据就移动一次指针

        FileInputStream fis=new FileInputStream("..\\myio\\a.txt");
        //2.循环读取
        while((fis.read())!=-1){
            System.out.println(fis.read());
        }
        //3.释放资源
        fis.close();
        */
    }
}

文件拷贝基本代码

package mybytestream;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

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

        /*
        练习:
        文件拷贝
        注意:
        选择一个比较小的文件,不要太大
         */

        //q.创建对象
        FileInputStream fis=new FileInputStream("D:\\cd");
        FileOutputStream fos=new FileOutputStream("..\\myio\\copy");
        //2.拷贝
        //核心思想:边读边想
        int b;
        while ((b=fis.read())!=-1){
            fos.write(b);
        }
        //释放资源
        //规则:最先开的最后关闭
        fos.close();;
        fis.close();;

    }
}

字节流一次读取多个字节

package mybytestream;

import java.io.FileInputStream;
import java.io.IOException;

public class ByteStreamDemo7 {
    public static void main(String[] args) throws IOException {
        /*
        public int read(byte[] buffer)  一次读一个字节数组数据
         */

        //1.创建对象
        FileInputStream fis=new FileInputStream("..\\myio\\a.txt");
        //2.读取数据
        byte[] bytes=new byte[2];
        //一次读取多个字节数据,具体读多少,跟数组的长度有关
        //返回值:本次读取到了多少个字节数据
        int len1=fis.read(bytes);
        System.out.println(len1);//2
        String str1=new String(bytes,0,len1);
        System.out.println(str1);//ab


        int len2=fis.read(bytes);
        System.out.println(len2);//2
        String str2=new String(bytes,0,len2);
        System.out.println(str2);//cd


        int len3=fis.read(bytes);
        System.out.println(len3);//1
        String str3=new String(bytes,0,len3);
        System.out.println(str3);//e
        //3.释放数据
        fis.close();
    }
}
 用此方法进行文件拷贝
package mybytestream;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

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

        /*
        练习:文件拷贝(27.0MB)
         */

        long start=System.currentTimeMillis();

        //1.创建对象
        FileInputStream fis=new FileInputStream("D:\\腾讯会议\\WeMeet\\3.31.2.441\\wemeet_app_components.dll");
        FileOutputStream fos=new FileOutputStream("..\\myio\\copy.dll");
        //2.拷贝
        int len;
        byte[] bytes=new byte[1024*1024*5];
        while((len=fis.read(bytes))!=-1){
            fos.write(bytes,0,len);
        }
        //3.释放资源
        fos.close();
        fis.close();

        long end=System.currentTimeMillis();

        System.out.println(end-start);//40ms
    }
}

io流中的异常处理

finally中的语句一定会执行,所有可以将释放资源的代码用finally控制,确保资源能正常释放。

字符集详解

(GBK)

在计算机中,任意数据都是以二进制的形式来存储的

存储英文,一个字节足以

 核心1:GBK中,一个英文字母一个字节,二进制第一位是0

核心2:GBK中,一个中文汉字两个字节,二进制第一位是1

(Unicode)

UTF-8不是字符集,是Unicode字符集的一种编码方式。

为什么会有乱码

原因1:读取数据时未读完整个汉字

原因2:编码和解码的方式不统一

拷贝时是一个字节一个字节进行拷贝的,所有使用字节流拷贝也不会出现乱码

java中的编码和解码

package mycharset;

import java.io.UnsupportedEncodingException;
import java.sql.SQLOutput;
import java.util.Arrays;

public class CharSetDemo1 {
    public static void main(String[] args) throws UnsupportedEncodingException {

         /*
    Java中编码的方法
    public byte[] getBytes()使用默认方式进行编码
    public byte[] getBytes(string charsetName) 使用指定方式进行编码

    Java中解码的方法
    String(byte[] bytes)使用默认方式进行解码
    String(byte[l bytes, string charsetName)使用指定方式进行解码

     */

        //1.编码
        String str="ai你哟";
        byte[] bytes1=str.getBytes();
        System.out.println(Arrays.toString(bytes1));

        byte[] bytes2=str.getBytes("GBK");
        System.out.println(Arrays.toString(bytes2));

        //2.解码
        String str2=new String(bytes1);
        System.out.println(str2);

        String str3=new String(bytes1,"GBK");
        System.out.println(str3);
    }
}

字符流

FIleReader字符输入流 
空参read方法详解

package mycharstream1;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class CharStreamDemo1 {
    public static void main(String[] args) throws IOException {
        /*
        第一步:创建对象
        public FileReader(File file)创建字符输入流关联本地文件
        public FileReader(string pathname)创建字符输入流关联本地文件
        第二步:读取数据
        public int read()读取数据,读到末尾返回-1
        public int read(char[] buffer)读取多个数据,读到末尾返回-1
        第三步:释放资源
        public void close()释放资源/关流
         */

        //1创建对象并关联本地文件
        FileReader fr=new FileReader(("..\\myio\\a.txt"));
        //2.读取数据 read()
        //字符流的底层也是字节流,默认也是一个字节一个字节的读取的。
        //如果遇到中文就会一次读取多个,GBK一次读两个字节,UTF-8一次读三个字节

        //read()细节:
        //1.read():默认也是一个字节一个字节的读取的,如果遇到中文就会一次读取多个
        //2.在读取之后,方法的底层还会进行解码并转成十进制。
        //最终把这个十进制作为返回值
        //这个十进制的数据也表示在字符集上的数字
        //英文:文件里面二进制数据 0118 0001
        //read方法进行读取,解码并转成十进制97
        //中义:文件里面的二进制数据 11100110 10110801 10001001
        //read方法进行读取,解码并转成十进制27721

        //我想看到中文汉字,就是把这些十进制数据,再进行强转就可以了
        int ch;
        while((ch=fr.read())!=-1){
            System.out.print((char) ch);
        }
    }
}
有参read方法详解
package mycharstream1;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

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

         /*
        第一步:创建对象
        public FileReader(File file)创建字符输入流关联本地文件
        public FileReader(string pathname)创建字符输入流关联本地文件
        第二步:读取数据
        public int read()读取数据,读到末尾返回-1
        public int read(char[] buffer)读取多个数据,读到末尾返回-1
        第三步:释放资源
        public void close()释放资源/关流
         */
        
        //1.创建对象
        FileReader fr = new FileReader("..\\myio\\a.txt");
        //2.读取数据
        char[] chars = new char[2];
        int len;
        //read(chars):读取数据,解码,强转三步合并了,把强转之后的字符放到数组当中
        //空参的read + 强转类型转换
        while ((len = fr.read(chars)) != -1) {
            //把数组中的数据变成字符串再进行打印
            System.out.print(new String(chars, 0, len));
        }
        //3.释放资源
        fr.close();
    }
}

FileWriter字符输出流

 

package mycharstream1;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CharStreamDemo3 {
    public static void main(String[] args) throws IOException {
        /*
        第一步:创建对象
        public FileWriter(File file)创建字符输出流关联本地文件
        public FileWriter(string pathname)创建字符输出流关联本地文件
        public FileWriter(File file, boolean append)创建字符输出流关联本地文件,续写
        public FileWriter(string pathname, boolean append)创建字符输出流关联本地文件,续写

        第二步:读取数据
        void write(int c)写出一个字符
        void write(string str)写出一个字符串
        void write(string str, int off, int len)写出一个字符串的一部分
        void write(char[] cbuf)写出一个字符数组
        void write(char[] cbuf, int off, int len)写出字符数组的一部分

        第三步:释放资源
        public void close()释放资源/关流

        '我'    25105

         */

        FileWriter fw=new FileWriter("..\\myio\\a.txt",true);

        //fw.write(25105);
        //fw.write("你好威啊???");
        char[] chars={'a','b','c','我'};
        fw.write(chars);

        fw.close();
    }
}

字符流原理解析

练习

1.拷贝

package Test;

import java.io.*;
import java.lang.reflect.Field;

public class Test1 {
    public static void main(String[] args) throws IOException {
        File src=new File("D:\\应用\\Inno Setup 5\\Languages");
        File dest=new File("D:\\aaa\\dest");
        copydir(src,dest);

    }
    public static void copydir(File src,File dest) throws IOException {
        dest.mkdirs();
        File[] files=src.listFiles();
        for(File file:files){
            if(file.isFile()){
                FileInputStream fis=new FileInputStream(file);
                FileOutputStream fos=new FileOutputStream(new File(dest,file.getName()));
                byte[] bytes=new byte[1024];
                int len;
                while((len=fis.read(bytes))!=-1){
                    fos.write(bytes,0,len);
                }
                fos.close();
                fis.close();
            }else{
                copydir(file,new File(dest,file.getName()));
            }
        }
    }
}

2.文件加密

package Test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

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

        /*
        异或
        两边相同:false
        两边不同:true
        一个数异或两次同一个数会得到他本身
         */
        //创建对象关联原始文件
        FileInputStream fis=new FileInputStream("..\\myio\\ency.jpg");
        //创建对象管理加密文件
        FileOutputStream fos=new FileOutputStream("..\\myio\\redu.jpg");
        //3。加密处理
        int b;
        while((b=fis.read())!=-1){
            fos.write(b^2);
        }
        //4.释放资源
        fos.close();
        fis.close();
    }
}

 3.修改文件中的数据

package Test;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;

public class Test3 {
    public static void main(String[] args) throws IOException {
        /*
        文本文件中有以下的数据:
        2-1-9-4-7-8
        将文件中的数据进行排序,变成以下的数据:
        1-2-4-7-8-9
         */

        //1.读取数据
        FileReader fr=new FileReader("..\\myio\\a.txt");
        StringBuilder sb=new StringBuilder();
        int ch;
        while((ch=fr.read())!=-1){
            sb.append((char)ch);
        }
        fr.close();
        //2.排序
        Integer[] arr = Arrays.stream(sb.toString()
                        .split("-"))
                .map(Integer::parseInt)
                .sorted()
                .toArray(Integer[]::new);
        //3.写出
        FileWriter fw=new FileWriter("..\\myio\\a.txt");
        String s=Arrays.toString(arr).replace(", ","-");
        String result=s.substring(1,s.length()-1);
        fw.write(result);
        fw.close();
    }
}

缓冲流

BUfferedInputStream字节缓冲流

一次读取一个字节

package mybufferedstream1;

import java.io.*;

public class BufferedStreamDemo1 {
    public static void main(String[] args) throws IOException {
        /*
        需求:
        利用字节缓冲流拷贝文件
        字节缓冲输入流的构造方法:
        public BufferedInputStream(Inputstream is)
        字节缓冲输出流的构造方法:
        public Bufferedoutputstream(outputstream os)
         */

        //1.创建缓冲流的对象
        BufferedInputStream bis=new BufferedInputStream(new FileInputStream("..\\myio\\a.txt"));
        BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("..\\myio\\copy.txt"));
        //2.循环读取并写到目的地
        int b;
        while((b=bis.read())!=-1){
            bos.write(b);
        }
        //3.释放资源
        bos.close();
        bis.close();

    }
}

一次读取多个字节
package mybufferedstream1;

import java.io.*;

public class BufferedStreamDemo2 {
    public static void main(String[] args) throws IOException {
        /*
        需求:
        利用字节缓冲流拷贝文件
        字节缓冲输入流的构造方法:
        public BufferedInputStream(Inputstream is)
        字节缓冲输出流的构造方法:
        public Bufferedoutputstream(outputstream os)
         */

        //1.创建缓冲流的对象
        BufferedInputStream bis=new BufferedInputStream(new FileInputStream("..\\myio\\a.txt"));
        BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("..\\myio\\copy2.txt"));
        //2.循环读取并写到目的地
        byte[] bytes=new byte[1024];
        int len;
        while((len=bis.read(bytes))!=-1){
            bos.write(bytes,0,len);
        
        //3.释放资源
        bos.close();
        bis.close();

    }
}

BufferedReader字符缓冲流

输入
package mybufferedstream1;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class BufferedStreamDemo3 {
    public static void main(String[] args) throws IOException {
        /*
        字符缓冲输入流:
        构造方法:
        public BufferedReader(Reader r)
        特有方法:
        public string readLine()
        谈一整行
         */

        //1.创建字符缓冲输入流的对象
        BufferedReader br=new BufferedReader(new FileReader("..\\myio\\a.txt"));
        //细节:
        //readLine方法在读取的时候,一次读一整行,遇到回车换行结束
        //但是他不会把回车换行读到内存当中
        //2.读取数据
        String line1=br.readLine();
        System.out.println(line1);

        String line2=br.readLine();
        System.out.println(line2);
        //3.释放资源
        br.close();
    }
}

输出
package mybufferedstream1;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class BufferedStreamDemo4 {
    public static void main(String[] args) throws IOException {
        /*
    字符缓冲输出流
    构造方法:
    public BufferedWriter(Writer r)
    特有方法:
    public void newLine()
    跨平台的换行
     */
        //1.创建字符缓冲输出流的对象
        BufferedWriter bw=new BufferedWriter(new FileWriter("b.txt",true));
        //2.写出数据
        bw.write("123");
        bw.newLine();
        bw.write("456");
        bw.newLine();
        //3.释放资源
        bw.close();
    }
}

练习

1.修改文本顺序

package Test;

import java.io.*;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class Test4 {
    public static void main(String[] args) throws IOException {
        
        //1.读取数据
        BufferedReader br=new BufferedReader(new FileReader("..\\myio\\a.txt"));
        String line;
        TreeMap<Integer,String> tm=new TreeMap<>();
        while((line=br.readLine())!=null){
            String[] arr=line.split("\\.");
            tm.put(Integer.parseInt(arr[0]),arr[1]);
        }
        br.close();
        
        //2.写出数据
        BufferedWriter bw=new BufferedWriter(new FileWriter("..\\myio\\result.txt"));
        Set<Map.Entry<Integer,String>> entries=tm.entrySet();
        for (Map.Entry<Integer, String> entry : entries) {
            String value=entry.getValue();
            bw.write(value);
            bw.newLine();
        }
        bw.close();
    }
}

2.软件运行次数
package Test;

import java.io.*;

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

        /*
        实现
        验证在序运行实数的小程序,
        要求如下
        1.当程序运行超过3次时给出提示:本软件只能免费使用3次,欢迎您注册会员后继续使用~
        2.程序运行演示如下:
        第一次运行控制台输出:欢迎使用本软件,第1次使用免费~
        第二次运行控制台输出:欢迎使用本软件,第2次使用免费~
        第三次运行控制台输出:欢迎使用本软件,第3次使用免费~
        第四次及之后运行控制台输出:本软件只能免费使用3次,欢迎您注册会员后继续使用~
        次数 计数器
        int count =0;
         */

        //1.把文件中的数字读取到内存中
        //原则:
        //IO:
        //随用随创建
        //什么时候不用什么时候关闭
        BufferedReader br=new BufferedReader(new FileReader("..\\myio\\count.txt"));
        String line=br.readLine();
        br.close();
        int count=Integer.parseInt(line);
        count++;
        //2.判断
        if(count<=3){
            System.out.println("欢迎使用本软件,第"+count+"次使用免费~");
        }else{
            System.out.println("本软件只能免费使用3次,欢迎您注册会员后继续使用~");
        }
        BufferedWriter bw=new BufferedWriter(new FileWriter("..\\myio\\count.txt"));
        bw.write(count+"");
        bw.close();
    }
}

InputStreamReader/OutputStreamWriter转换流

1.转换流的基本用法

JDK11以前可以使用转换流按照指定字符编码读取和写入

JDK11以后字符流中添加了一个新歌构造指定字符编码

练习:读取文件中的数据

package myconvertstream;

import java.io.*;

public class ConvertStreamDemo1 {
    public static void main(String[] args) throws IOException {
        /*
        利用字节流读取文件中的数据,每次读一整行,而且不能出现乱码
        //1.字节流在读取中文的时候,是会出现乱码的,但是字符流可以搞定
        //2.字节流里面是没有读一整行的方法的,只有字符缓冲流才能搞定
         */
        BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream("..\\myio\\a.txt")));
        String line;
        while((line=br.readLine())!=null){
            System.out.println(line);
        }
        br.close();
    }
}

总结

序列化流和反序列化流

ObjectOutputStream序列化流

package myobjectstream;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class ObjectStreamDemo1 {
    public static void main(String[] args) throws IOException {
        /*
        需求:
        利用序列化流/对象操作输出流,把一个对象写到本地文件中
        构造方法:
        public objectoutputstream(outputstream out)
        成员方法:
        public final void writeobject(object obj)
        把基本流变成高级流
        把对象序列化(写出)到文件中去
         */

        //1.创建对象
        Student stu=new Student("zhangsan",23);

        //2.创建序列化流的对象/对线操作输出流
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("..\\myio\\b.txt"));

        //3.写出数据
        oos.writeObject(stu);

        //4.释放数据
        oos.close();
    }
}
package myobjectstream;

import java.io.Serializable;

/*
Serializable接口里面是没有抽象方法,标记型接口
一旦实现了这个接口,那么就表示当前的student类可以被序列化
理解:
-个物品的合格证
 */
public class Student implements Serializable {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}

 ObjectInputStream反序列化流

package myobjectstream;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class ObjectStreamDemo2 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        /*
        求:
        利用反序列化流/对象操作输入流,把文件中中的对象读到程序当中
        构造方法:
        public objectInputstream(Inputstream out)
        成员方法:
        把基本流变成高级流
        public obiect readobiect()
        把序列化到本地文件中的对象,读取到程序中来
         */

        //1.创建反序列化流的对象
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("..\\myio\\b.txt"));

        //2.读取数据
        Student stu=(Student) ois.readObject();
        
        //3.打印对象
        System.out.println(stu);

        //4.释放资源
        ois.close();
    }
}

细节

package myobjectstream;

import java.io.Serial;
import java.io.Serializable;

/*
Serializable接口里面是没有抽象方法,标记型接口
一旦实现了这个接口,那么就表示当前的student类可以被序列化
理解:
-个物品的合格证
 */
public class Student implements Serializable {

    @Serial
    private static final long serialVersionUID = 3340324615881368858L;
    private String name;
    private int age;
    //transient:瞬态关键字
    //作用:不会把当前属性序列化到本地文件当中
    private transient String address;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}

练习

package Test;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collections;

public class Test6 {
    public static void main(String[] args) throws IOException {
        /*
        需求:
        将多个自定义对象序列化到文件中,但是对象的个数不确定,该如何操作呢
         */

        //1.序列化多个对象
        Student s1=new Student("zhangsan",25,"邵阳");
        Student s2=new Student("lisi",26,"衡阳");
        Student s3=new Student("wangwu",27,"隆回");

        ArrayList<Student> list=new ArrayList<>();
        Collections.addAll(list,s1,s2,s3);

        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("..\\myio\\b.txt"));
        oos.writeObject(list);

        oos.close();
    }
}

打印流

打印流不能读只能写

PrintStream字节打印流

构造方法

成员方法

练习

package myprintstream;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;

public class PrintStreamDemo1 {
    public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
        /*
        字节打印流:
        构造方法
        public Printstream(outputstream/File/string)关联字节输出流/文件/文件路径
        public Printstream(string fileName,Charset charset)指定字符编码
        public Printstream(outputstreamout,boolean autoFlush)自动刷新
        public Printstream(outputstream out, boolean autoFlush, string encoding)指定字符编码且自动刷新

        成员方法:
        public void write(int b)常规方法:规则跟之前一样,将指定的字节写出
        public void println(Xxx xx)特有方法:打印任意数据,自动刷新,自动换行
        public void print(Xxx xx)特有方法:打印任意数据,不换行
        public void printf(string format,object... args) 特有方法:带有占位符的打印语句,不换行
         */

        //1.创建字节打印流的对象
        PrintStream ps=new PrintStream(new FileOutputStream("..\\myio\\a.txt"),true,"UTF-8");
        //2.写出数据
        ps.println(97);
        ps.print(true);
        ps.println();
        ps.printf("%s爱上了%s","阿珍","阿强");
        //3.释放资源
        ps.close();
    }
}

PrintWriter字符打印流

构造方法

成员方法

打印流的应用场景 

package myprintstream;

import java.io.PrintStream;

public class PrintStreamDemo3 {
    public static void main(String[] args) {
        /*
        打印流的应用场景
         */

        //获取打印流的对象,此打印流在虚拟机启动的时候,由虚拟机创建,默认指向控制台
        //特殊的打印流,系统中的标准输出流,是不能关闭,在系统中是唯一的。
        PrintStream ps=System.out;

        //调用打印流中的方法println
        //写出数据,自动换行,自动刷新
        ps.println("123");

        //ps.close();;

        ps.println("你好你好");

        System.out.println("456");
    }
}

总结

解压缩流/压缩流 

解压缩流

压缩包中的每一个文件或者文件夹都是一个ZipEntry对象。 

package myzipstream;

import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

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

        //1.创建一个File表示要解压的压缩包
        File src=new File("D:\\aaa.zip");
        //2.创建一个File表示解压的目的地
        File dest=new File("D:\\");

        //调用方法
        unzip(src,dest);
    }

    //定义一个方法来解压
    public static void unzip(File src,File dest) throws IOException {
        //解压的本质:把压缩包里面的每一个文件或者文件夹读取出来,按照层级拷贝到目的地当中

        //创建一个解压缩流用来读取压缩包中的数据
        ZipInputStream zip=new ZipInputStream(new FileInputStream(src));
        //要先获取到压缩包里面的每一个zipentry对象

        //表示当前在压缩包中获取到的文件或者文件夹
        ZipEntry entry;
        while((entry=zip.getNextEntry())!=null){
            System.out.println(entry);
            if(entry.isDirectory()){
                //文件夹:需要在目的地dest处创建一个同样的文件夹
                File file=new File(dest,entry.toString());
                file.mkdirs();
            }else{
                //文件:需要读取到压缩包中的文件,并把他存放到目的地dest文件夹中(按照层级目录进行存放)
                FileOutputStream fos=new FileOutputStream(new File(dest,entry.toString()));
                int b;
                while((b=zip.read())!=-1){
                    //写到目的地
                    fos.write(b);
                }
                fos.close();
                //表示在压缩包中的一个文件处理完毕了
                zip.closeEntry();
            }
        }
        zip.close();
    }
}

压缩流

package myzipstream;

import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

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

        /*
        压缩流
        需求:
        把D:\a.txt打包成一个压缩包
         */

        //1.创建File对象表示要压缩的文件
        File src=new File("D:\\a.txt");
        //2.创建File对象表示压缩包的位置
        File dest=new File("D:\\");
        //3.调用方法用来压缩
        toZip(src,dest);
    }

    /*
    作用:压缩
    参数一:表示要压缩的文件
    参数二:表示压缩包的位置
     */
    public static void toZip(File src,File dest) throws IOException {
        //1.创建压缩流关联压缩包
        ZipOutputStream zos=new ZipOutputStream(new FileOutputStream(new File(dest,"a.zip")));
        //2.创建ZipEntry对象,表示压缩包里面的每一个文件和文件夹
        ZipEntry entry=new ZipEntry("a.txt");
        //3.把ZipEntry对象放到压缩包当中
        zos.putNextEntry(entry);
        //4.把src文件中的数据写到压缩包当中
        FileInputStream fis=new FileInputStream(src);
        int b;
        while((b=fis.read())!=-1){
            zos.write(b);
        }
        zos.closeEntry();
        zos.close();
    }
}

常用工具包

Commons-io

 经典白雪,那我之前敲的代码算什么,算孩子有劲吗

Hutool工具包

练习

1.爬虫获取姓名

package myiotest1;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Test1 {
    public static void main(String[] args) throws IOException {
        /*
        制造假数据:
        获取男生名字:http://www.haoming8.cn/baobao/10881.htm1
        获取姓氏:https://hanyu.baidu.com/shici/detail?pid=0b2f26d4ceddb3ee693fdb1137ee1bed&from=kg0
        获取女生名字:http://www.haoming8.cn/baobao/7641.html
     */

        //1.定义变量记录网址
        String familyNameNet="https://hanyu.baidu.com/shici/detail?pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d&from=kg0";
        String boyNameNet="http://www.haoming8.cn/baobao/10881.htm1";
        String girlNameNet="http://www.haoming8.cn/baobao/7641.html";

        //2.爬取数据,把网址上所有数据拼接成一个字符串
        String familyNameStr= webCrawler(familyNameNet);
        String boyNameStr= webCrawler(familyNameNet);
        String girlNameStr= webCrawler(familyNameNet);


        //3.通过正则表达式,把其中符合要求的数据获取出来
        ArrayList<String> familyNameTempList=getData(familyNameStr,"(\\W{4})(。|,)",1);
        ArrayList<String> boyNameTempList=getData(boyNameStr,"([\\u4E00-\\u9FA5]{2})(。|、)",1);
        ArrayList<String> girlNameTempList=getData(girlNameStr,"(.. ){4}..",0);

        //4.处理数据
        //familyNameTempList(姓氏)
        //处理方案:把每一个姓氏拆开并添加到一个新的集合当中
        ArrayList<String> familyNameList=new ArrayList<>();
        for(String str:familyNameTempList){
            //str 赵钱孙李 周吴郑王 冯陈褚卫 蒋沈韩杨
            for (int i = 0; i < str.length(); i++) {
                char c=str.charAt(i);
               familyNameList.add(c+"");
            }
        }
        //boyNameTempList(男生的名字)
        //处理方案:去除其中的重复元素
        ArrayList<String> boyNameList=new ArrayList<>();
        for (String str : boyNameList) {
            if(!boyNameList.contains(str)){
                boyNameList.add(str);
            }
        }
        //girlNameTempList(女生的名字)
        //处理方案:把里面的每一个元素用空格进行切割,得到每一个女生的名字
        ArrayList<String> girlNameList=new ArrayList<>();
        for (String str : girlNameList) {
            String[] arr=str.split(" ");
            for (int i = 0; i < arr.length; i++) {
                girlNameList.add(arr[i]);
            }
        }
        //5.生成数据
        //姓名(唯一)-性別-年龄
        ArrayList<String> list=getInfos(familyNameList,boyNameList,girlNameList,70,80);
        System.out.println(list);

        //写出数据
        BufferedWriter bw=new BufferedWriter(new FileWriter("..\\myio\\names.txt"));
        for (String str : list) {
            bw.write(str);
            bw.newLine();
        }
        bw.close();
    }
    /*
    作用:
        获取男生和女生的信息:张三-男-23
        涨
        形参:
        参数一:装着姓氏的集合
        参数二:装着男生名字的集合
        参数三:装着女生名字的集合
        参数四:男生的个数
        参数五:女生的个数
     */
    public static ArrayList<String> getInfos(ArrayList<String> familyNameList,ArrayList<String> boyNameList,ArrayList<String> girlNameList,int boyCount,int girlCount){
        //1.生成不重复的名字
        HashSet<String> boyhs=new HashSet<>();
        while(true){
            if(boyhs.size()==boyCount){
                break;
            }
            //随机
            Collections.shuffle(familyNameList);
            Collections.shuffle(boyNameList);
            boyhs.add(familyNameList.get(0)+boyNameList.get(0));
        }
        //2.生成女生不重复的名字
        HashSet<String> girlhs=new HashSet<>();
        while(true){
            if(girlhs.size()==girlCount){
                break;
            }
            //随机
            Collections.shuffle(familyNameList);
            Collections.shuffle(girlNameList);
            boyhs.add(familyNameList.get(0)+girlNameList.get(0));
        }
        //3.生成男生的信息并添加到集合当中
        ArrayList<String> list=new ArrayList<>();
        Random r=new Random();
        //[18~27]
        for (String boyName : boyhs) {
            int age=r.nextInt(10)+18;
            list.add(boyName+"男"+age);
        }
        //3.生成女生的信息并添加到集合当中
        //[18~25]
        for (String girlName : girlhs) {
            int age=r.nextInt(8)+18;
            list.add(girlName+"女"+age);
        }
        return list;
    }

    /*
    作用:根据正则表达式获取字符串中的数据
    参数一:
    完整的字符串
    参数二:
    正则表达式
    参数三:
    ???
    返回值:
    真正想要的数据
     */
    private static ArrayList<String> getData(String str,String regex,int index){
        //1.创建集合存放数据
        ArrayList<String> list=new ArrayList<>();
        //2.按照正则表达式的规则,去获取数据
        Pattern pattern=Pattern.compile(regex);
        //按照Pattern的规则,到str中去获取数据
        Matcher matcher=pattern.matcher(str);
        while(matcher.find()){
            list.add(matcher.group(index));
        }
        return list;
    }


    /*
    作用:
    从网络中爬取数据,把数据拼接成字符串返回
    形参:
    网址
    返回值:
    爬取到的所冇数据
     */
    public static String webCrawler(String net) throws IOException {
        //1.定义StringBuilder拼接爬取到的数据
        StringBuilder sb=new StringBuilder();
        //2.创建一个URL对象
        URL url=new URL(net);
        //3.链接上这个网址
        //细节:保证网络是畅通的,而且这个网址是可以链接上的。
        URLConnection conn=url.openConnection();
        //4.读取数据
        InputStreamReader isr=new InputStreamReader(conn.getInputStream());
        int ch;
        while((ch= isr.read())!=-1){
            sb.append((char)ch);
        }
        //5.释放资源
        isr.close();;
        return sb.toString();
    }
}

2.随机点名器

1.

package myiotest2;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;

public class Test1 {
    public static void main(String[] args) throws IOException {
        /*需求:
            需求:
                有一个文件里面存储了班级同学的信息,每一个信息占一行。
                格式为:张三-男-23
                要求通过程序实现随机点名器。

            运行效果:
                第一次运行程序:随机同学姓名1(只显示名字)
                第二次运行程序:随机同学姓名2(只显示名字)
                第三次运行程序:随机同学姓名3(只显示名字)
                …
         */
        //1.读取文件中学生的姓名
        ArrayList<String> list=new ArrayList<>();
        BufferedReader br=new BufferedReader(new FileReader("..\\myiotest\\allnames.txt"));
        String line;
        while((line=br.readLine())!=null){
            list.add(line);
        }
        br.close();

        //2.随机抽取
        Collections.shuffle(list);
        String randomName=list.get(0);
        String[] arr2=randomName.split("-");
        System.out.println(arr2[0]);
    }
}

2.

package myiotest2;

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

public class Test2 {
    public static void main(String[] args) throws IOException {
        /*
        需求:
        一个文件里面存储了班级同学的信息,每一个学生信息占一行。
        格式为:张三-男-23。
        要求通过程序实现随机点名器
        运行效果:
        70%的概率随机到男生
        30%的概率随机到女生
        总共随机100万次,统计结果。
        注意观察:看生成男生和女生的比例是不是接近于7:3
         */

        //1.读取文件中的姓名,按性别存放到数组中
        ArrayList<String> boylist=new ArrayList<>();
        ArrayList<String> girllist=new ArrayList<>();
        BufferedReader br=new BufferedReader(new FileReader("..\\myiotest\\allnames.txt"));
        String line;
        while((line=br.readLine())!=null){
            if(line.split("-")[1].equals("男")){
                boylist.add(line);
            }else{
                girllist.add(line);
            }
        }
        br.close();

        //按概率抽取
        Random r=new Random();
        int a=0,b=0;
        for(int i=1;i<=100000;i++){
            int x= r.nextInt(10)+1;
            Collections.shuffle(boylist);
            Collections.shuffle(girllist);
            if(x<=7){
                a++;
            }else{
                b++;
            }
        }
        System.out.println(a);
        System.out.println(b);
    }
}

3.

package myiotest2;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

public class Test3 {
    public static void main(String[] args) throws IOException {
        /*
        需求:
        一个文件里面存储了班级同学的姓名,每一个姓名占一行。
        要求通过程序实现随机点名器,
        第三次必定是张三同学
        运行效果:
        第一次运行程序
        随机同学姓名1
        第二次运行程序:
        随机同学姓名2
        第三次运行程序:张三
         */

        //读取文件中学生的姓名
        ArrayList<String> list=new ArrayList<>();
        BufferedReader br1=new BufferedReader(new FileReader("..\\myiotest\\allnames.txt"));
        String line;
        while((line=br1.readLine())!=null){
            list.add(line);
        }
        br1.close();
        //读取程序运行的次数
        BufferedReader br2=new BufferedReader(new FileReader("..\\myiotest\\count.txt"));
        String strcount=br2.readLine();
        int count=Integer.parseInt(strcount);
        br2.close();
        count++;
        if(count!=3){
            Collections.shuffle(list);
            Arrays.stream(list.get(0).split("-")).limit(1)
                    .forEach(System.out::println);
        }else{
            System.out.println("张三");
        }
        //将程序运行次数存入文件
        BufferedWriter bw=new BufferedWriter(new FileWriter("..\\myiotest\\count.txt"));
        bw.write(count+"");
        bw.close();
    }
}

4.

package myiotest2;

import java.io.*;
import java.util.ArrayList;
import java.util.Collections;

public class Test5{
    public static void main(String[] args) throws IOException {
        /*需求:
            一个文件里面存储了班级同学的姓名,每一个姓名占一行。
            要求通过程序实现随机点名器。

          运行结果要求:
            被点到的学生不会再被点到。
            但是如果班级中所有的学生都点完了, 需要重新开启第二轮点名。

          核心思想:
               点一个删一个,把删除的备份,全部点完时还原数据。

        */

        //1.定义变量,表示初始文件路径,文件中存储所有的学生信息
        String src = "..\\myiotest\\names.txt";
        //2.定义变量,表示备份文件,一开始文件为空
        String backups = "..\\myiotest\\backups.txt";
        //3.读取初始文件中的数据,并把学生信息添加到集合当中
        ArrayList<String> list = readFile(src);
        //4.判断集合中是否有数据
        if (list.size() == 0) {
            //5.如果没有数据,表示所有学生已经点完,从backups.txt中还原数据即可
            //还原数据需要以下步骤:
            //5.1 读取备份文件中所有的数据
            list = readFile(backups);
            //5.2 把所有的数据写到初始文件中
            writeFile(src, list, false);
            //5.3 删除备份文件
            new File(backups).delete();
        }
        //5.集合中有数据,表示还没有点完,点一个删一个,把删除的备份到backups.txt当中
        //打乱集合
        Collections.shuffle(list);
        //获取0索引的学生信息并删除
        String stuInfo = list.remove(0);
        //打印随机到的学生信息
        System.out.println("当前被点到的学生为:" + stuInfo);
        //把删除之后的所有学生信息,写到初始文件中
        writeFile(src, list, false);
        //把删除的学生信息备份(追加写入)
        writeFile(backups, stuInfo, true);


    }

    private static void writeFile(String pathFile, ArrayList<String> list, boolean isAppend) throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter(pathFile, isAppend));
        for (String str : list) {
            bw.write(str);
            bw.newLine();
        }
        bw.close();
    }

    private static void writeFile(String pathFile, String str, boolean isAppend) throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter(pathFile, isAppend));
        bw.write(str);
        bw.newLine();
        bw.close();
    }


    private static ArrayList<String> readFile(String pathFile) throws IOException {
        ArrayList<String> list = new ArrayList<>();
        BufferedReader br = new BufferedReader(new FileReader(pathFile));
        String line;
        while ((line = br.readLine()) != null) {
            list.add(line);
        }
        br.close();
        return list;
    }
}

5.

package myiotest3;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;

public class Test1 {
    public static void main(String[] args) throws IOException {
        //1.把文件中的所以学生信息读取到内存中
        ArrayList<Student> list=new ArrayList<>();
        BufferedReader br=new BufferedReader(new FileReader("..\\myiotest\\names.txt"));
        String line;
        while((line=br.readLine())!=null){
            String[] arr=line.split("-");
            list.add(new Student(arr[0],arr[1],Integer.parseInt(arr[2]),Double.parseDouble(arr[3])));
        }
        br.close();

        //2.计算权重的总和
        double weight=0;
        for (Student stu : list) {
            weight+= stu.getWeight();
        }

        //3.计算每一个人的实际占比
        double[] arr=new double[list.size()];
        int index=0;
        for (Student stu : list) {
            arr[index++]= stu.getWeight()/weight;
        }

        //4.计算每一个人的权重占比范围
        for (int i = 1; i < arr.length; i++) {
            arr[i]+=arr[i-1];
        }

        //5.随机抽取
        //获取一个0.0~1.0之间的随机数
        double number=Math.random();
        //判断number在arr中的位置
        //二分查找法
        //方法回返回:-插入点 -1
        int result=-Arrays.binarySearch(arr,number)-1;
        Student stu=list.get(result);
        System.out.println(stu);
        
        //6.修改当前学生的权重
        double w= stu.getWeight()/2;
        stu.setWeight(w);
        
        //7.把集合中的数据再次写到文件中
        BufferedWriter bw=new BufferedWriter(new FileWriter("..\\myiotest\\names.txt"));
        for (Student s : list) {
            bw.write(s.toString());
            bw.newLine();
        }
        bw.close();
    }
}

登录注册

package myiotesy4;

import java.io.*;
import java.util.Scanner;

public class Test1 {
    public static void main(String[] args) throws IOException {
        /*
        需求:写一个登陆小案例
        添加锁定账号功能)
        步骤:
        将正确的用户名和密码手动保存在本地的
        自userinfo.txt文件中
        保存格式为:username=zhangsan&password=123&count=0
        让用户键盘录入用户名和密码
        比较用户录入的和正确的用户名密码是否一致
        如果一致则打印登陆成功
        如果不一致则打印登陆失败
        输错三次被锁定
         */

        //1.读取正确用户名密码和登陆失败次数
        BufferedReader br=new BufferedReader(new FileReader("..\\myiotest\\src\\myiotesy4\\userinfo.txt"));
        String line=br.readLine();
        br.close();
        String[] useInfo=line.split("&");
        String[] arr1=useInfo[0].split("=");
        String[] arr2=useInfo[1].split("=");
        String[] arr3=useInfo[2].split("=");

        String rightUsername=arr1[1];
        String rightPassword=arr2[1];
        //count:表示用户连续输错的次数
        int count=Integer.parseInt(arr3[1]);

        //2.用户键盘录入用户名和密码
        if(count==3){
            System.out.println("用户账户锁定");
        }else {
            Scanner sc=new Scanner(System.in);
            System.out.println("请输入用户名");
            String username=sc.nextLine();
            System.out.println("请输入密码");
            String password=sc.nextLine();

            //3.比较
            if(rightUsername.equals(username)&&rightPassword.equals(password)){
                System.out.println("登录成功");
                count=0;
            }else {
                count++;
                System.out.println("登录失败,还剩下"+(3-count)+"次机会");

            }
            //4.把登录失败次数打印到文件当中
            BufferedWriter bw=new BufferedWriter(new FileWriter("..\\myiotest\\src\\myiotesy4\\userinfo.txt"));
            bw.write("username=" + rightUsername + "&password=" + rightPassword + "&count="+String.valueOf(count));
            bw.close();
        }
    }
}

properties

properties是配置文件的一种

Properties作为双列集合的方法
package myiotest5;

import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;

public class Test1 {
    public static void main(String[] args) {
        /*
        Properties作为Map集合的操作
         */

        //1.添加集合的对象
        Properties prop=new Properties();

        //2.添加数据
        //细节:虽然我们可以往Properties当中添加任意的数据类型,但是一般只会往里面添加字符串类型的数据
        prop.put("aaa","111");
        prop.put("bbb","222");
        prop.put("ccc","333");
        prop.put("ddd","444");

        //3.遍历集合
        /*Set<Object> keys=prop.keySet();
        for (Object key : keys) {
            Object value=prop.get(key);
            System.out.println(key+"="+value);
        }*/

        Set<Map.Entry<Object,Object>> entries=prop.entrySet();
        for (Map.Entry<Object, Object> entry : entries) {
            Object key=entry.getKey();
            Object value=entry.getValue();
            System.out.println(key+"="+value);
        }
    }
}

Properties中特有的读写方法
package myiotest5;

import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class Test2 {
    public static void main(String[] args) throws IOException {
        /*
        Properties跟IO流结合的操作
         */

        //1.创建集合
        Properties prop=new Properties();

        //2.添加数据
        prop.put("aaa","111");
        prop.put("bbb","222");
        prop.put("ccc","333");
        prop.put("ddd","444");

        //3.把集合中的数据以键值对的形式写到本地文件当中
        FileOutputStream fos=new FileOutputStream("..\\myiotest\\a.properties");
        prop.store(fos,"test");
        fos.close();


        /*BufferedWriter bw=new BufferedWriter(new FileWriter("..\\myiotest\\a.properties"));
        Set<Map.Entry<Object,Object>> entries=prop.entrySet();
        for (Map.Entry<Object, Object> entry : entries) {
            Object key=entry.getKey();
            Object value=entry.getValue();
            bw.write(key+"="+value);
            bw.newLine();
        }
        bw.close();*/
    }
}

package myiotest5;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class Test3 {
    public static void main(String[] args) throws IOException {
        //1.创建集合
        Properties prop=new Properties();
        //2.读取本地Properties文件里面的数据
        FileInputStream fis=new FileInputStream("..\\myiotest\\a.properties");
        prop.load(fis);
        fis.close();

        //3.打印集合
        System.out.println(prop);
    }
}

拼图游戏

游戏界面
package ui;

import domain.GameInfo;

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.Properties;
import java.util.Random;

public class GameJFrame extends JFrame implements KeyListener, ActionListener {
    //表示游戏的主界面

    //彩蛋
    int flag;

    //创建一个二维数组
    //目的:用来管理数据
    //加载图片的时候,根据二维数组中的数据,加载对应的图片
    int[][] data = new int[4][4];

    //记录空白方块在二维数组中的索引
    int x = 0;
    int y = 0;
    //定义一个变量,记录当前展示图片的路径
    String path = "..\\puzzlegame\\image\\animal\\animal3\\";
    //定义一个二维数组,用来判断胜利
    int[][] win = {
            {1,2,3,4},
            {5,6,7,8},
            {9,10,11,12},
            {13,14,15,0}
    };

    //定义一个变量,用来统计步数
    int step = 0;

    //创建选项下面的条目对象
    JMenuItem girlJMenuItem = new JMenuItem("美女");
    JMenuItem animalJMenuItem = new JMenuItem("动物");
    JMenuItem sportJMenuItem = new JMenuItem("运动");
    JMenuItem replayJMenuItem = new JMenuItem("重新游戏");
    JMenuItem reLoginJMenuItem = new JMenuItem("重新登录");
    JMenuItem closeJMenuItem = new JMenuItem("关闭游戏");

    JMenu saveJMenu = new JMenu("存档");
    JMenu loadJMenu = new JMenu("读档");

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

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

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

    public GameJFrame(int flag){

        //彩蛋
        this.flag=flag;

        System.out.println(this.flag);
        //初始化界面
        initJFrame();

        //初始化菜单
        initJMenuBar();

        //初始化数据(打乱图片的顺序)
        initData();
        
        //初始化图片(根据打乱后的顺序加载图片)
        initImage();

        //让界面显示出来
        this.setVisible(true);
    }


    //初始化数据(打乱图片的顺序)
    private void initData() {
        //1.定义一个一维数组
        int[] tempArr = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
        //2.打乱数组中数据的顺序
        //遍历数组,得到数组中的每一个元素,拿着每一个元素跟随机索引上的元素进行交换
        Random r = new Random();
        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;
        }

        //3.遍历一维数组,把一维数组中的元素放到二维数组中
        for (int i = 0; i < tempArr.length; i++) {
            if(tempArr[i] == 0){
                //记录0的索引
                x = i/4;
                y = i%4;
            }
                //i/4 表示二维数组的行索引
                //i%4 表示二维数组的列索引
                data[i/4][i%4] = tempArr[i];
        }

    }

    //初始化图片
    //添加图片的时候,按照二维数组中的数据,来添加图片
    private void initImage() {
        //清空原本已经出现的所有图片
        this.getContentPane().removeAll();

        if(victory()){
            //添加胜利的图片
            JLabel winJLabel = new JLabel(new ImageIcon("..\\puzzlegame\\image\\win.png"));
            winJLabel.setBounds(203,283,197,73);
            //把图片添加到界面中
            this.getContentPane().add(winJLabel);
        }

        //添加步数
        JLabel stepJLabel = new JLabel("步数:"+step);
        stepJLabel.setBounds(50,30,100,20);
        //把步数添加到界面中
        this.getContentPane().add(stepJLabel);

        //先添加的图片在上方,后添加的图片在下方
        //外循环---把内循环的代码重复执行4次
        for (int i = 0; i < 4; i++) {
            //内循环---表示在一行添加四张图片
            for (int j = 0; j < 4; j++) {
                //获取当前要加载图片的序号
                int num = data[i][j];
                //创建一个JLabel的对象(管理容器)
                JLabel jLabel = new JLabel(new ImageIcon(path + num+ ".jpg"));
                //指定图片的位置
                jLabel.setBounds(105*j+83,105*i+134,105,105);
                //给图片添加边框
                jLabel.setBorder(new BevelBorder(BevelBorder.LOWERED));
                //把管理容器添加到界面中
                this.getContentPane().add(jLabel);
                //添加一次之和number需要自增,表示下一次加载后面一张图片
            }
        }

        //添加背景图片
        JLabel background = new JLabel(new ImageIcon("..\\puzzlegame\\image\\background.png"));
        background.setBounds(40,40,520,600);
        //把背景图片添加到界面中
        this.getContentPane().add(background);


        //刷新界面
        this.getContentPane().repaint();


    }

    //创建菜单
    private void initJMenuBar() {
        //创建整个的菜单对象
        JMenuBar jMenuBar = new JMenuBar();

        //创建菜单上面的两个选项的对象(功能 关于我们)
        JMenu functionJMenu = new JMenu("功能");
        JMenu aboutJMenu = new JMenu("关于我们");
        JMenu changeJMenu = new JMenu("更换图片");

        //将每一个选项下面的条目添加到选项当中
        //把5个存档,添加到saveJMenu中
        saveJMenu.add(saveItem0);
        saveJMenu.add(saveItem1);
        saveJMenu.add(saveItem2);
        saveJMenu.add(saveItem3);
        saveJMenu.add(saveItem4);

        //把5个读档,添加到loadJMenu中
        loadJMenu.add(loadItem0);
        loadJMenu.add(loadItem1);
        loadJMenu.add(loadItem2);
        loadJMenu.add(loadItem3);
        loadJMenu.add(loadItem4);

        //把美女,动物,运动添加到更换图片当中
        changeJMenu.add(girlJMenuItem);
        changeJMenu.add(animalJMenuItem);
        changeJMenu.add(sportJMenuItem);

        //将更换图片,重新游戏,重新登录,关闭游戏,存档,读档添加到“功能”选项当中
        functionJMenu.add(changeJMenu);
        functionJMenu.add(replayJMenuItem);
        functionJMenu.add(reLoginJMenuItem);
        functionJMenu.add(closeJMenuItem);
        functionJMenu.add(saveJMenu);
        functionJMenu.add(loadJMenu);

        //将公众号添加到关于我们当中
        aboutJMenu.add(accountJMenuItem);

        //给选项下面的条目添加监听
        girlJMenuItem.addActionListener(this);
        animalJMenuItem.addActionListener(this);
        sportJMenuItem.addActionListener(this);
        replayJMenuItem.addActionListener(this);
        reLoginJMenuItem.addActionListener(this);
        closeJMenuItem.addActionListener(this);
        accountJMenuItem.addActionListener(this);
        saveItem0.addActionListener(this);
        saveItem1.addActionListener(this);
        saveItem2.addActionListener(this);
        saveItem3.addActionListener(this);
        saveItem4.addActionListener(this);
        loadItem0.addActionListener(this);
        loadItem1.addActionListener(this);
        loadItem2.addActionListener(this);
        loadItem3.addActionListener(this);
        loadItem4.addActionListener(this);

        //将菜单里的两个选项添加到菜单当中
        jMenuBar.add(functionJMenu);
        jMenuBar.add(aboutJMenu);

        //读取存档信息,修改菜单上表示的内容
        getGameInfo();

        //给整个界面设置菜单
        this.setJMenuBar(jMenuBar);
    }

    public void getGameInfo(){
        //1.创建File对象表示所有存档所在的文件夹
        File file=new File("..\\puzzlegame\\save");
        //2.进入文件夹所获取到里面所有的存档文件
        File[] files=file.listFiles();
        //3.获取里面的部署,修改菜单
        for (File f : files) {
            //f :依次表示每一个存档文件
            //获取每一个存档文件中的步数
            GameInfo gi = null;
            try {
                ObjectInputStream ois=new ObjectInputStream(new FileInputStream(f));
                gi = (GameInfo) ois.readObject();
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }

            //获取到了步数
            int step=gi.getStep();

            //把存档的步数同步到菜单当中
            //save0 --->0
            //save1 ---> 1
            //...
            //获取存档的文件名 save0.data
            String name= f.getName();
            //获取当前存档的序号(索引)
            int index=name.charAt(4)-'0';
            //修改菜单上所表示的文字信息
            saveJMenu.getItem(index).setText("存档"+index+"("+step+")步");
            loadJMenu.getItem(index).setText("存档"+index+"("+step+")步");
        }

    }
    //初始化界面
    private void initJFrame() {
        //设置界面的宽高
        this.setSize(603,680);
        //设置界面的标题
        this.setTitle("拼图单机版 v1.0");
        //设置界面置顶
        this.setAlwaysOnTop(true);
        //设置界面居中显示
        this.setLocationRelativeTo(null);
        //设置关闭模式
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        //取消默认的居中放置,而是采用绝对定位的方式
        this.setLayout(null);
        //给整个界面添加键盘的监听
        this.addKeyListener(this);
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    //按下不松时,会一直触发该方法
    @Override
    public void keyPressed(KeyEvent e) {

        int code = e.getKeyCode();

        if(code == 65){
            //把界面中的图片全部删除
            this.getContentPane().removeAll();
            //加载第一张完整的图片
            JLabel jLabel = new JLabel(new ImageIcon(path+"all.jpg"));
            jLabel.setBounds(83,134,420,420);
            //把图片添加到界面中
            this.getContentPane().add(jLabel);
            //加载背景图片
            JLabel background = new JLabel(new ImageIcon("..\\puzzlegame\\image\\background.png"));
            background.setBounds(40,40,520,600);
            //把背景图片添加到界面中
            this.getContentPane().add(background);
            //刷新界面
            this.getContentPane().repaint();


        }
    }

    //松开按键时,触发该方法
    @Override
    public void keyReleased(KeyEvent e) {
        //判断游戏是否胜利,如果胜利了,就不再接收键盘的输入了
        if(victory()){
            return;
        }

        //对上,下,左,右四个方向键进行判断
        //左:37 上:38 右:39 下:40
        int code = e.getKeyCode();
        if(code == 37){
            //左
            if(y == 3){
                //如果空白方块已经在最左边了,就不能再往左移动了
                return;
            }
            System.out.println("向左移动");
            data[x][y] = data[x][y+1];
            data[x][y+1] = 0;
            y++;
            step++;
            //重新加载图片
            initImage();
        }else if(code == 38){
            //上
            if(x == 3){
                //如果空白方块已经在最上面了,就不能再往上移动了
                return;
            }
            System.out.println("向上移动");
            data[x][y] = data[x+1][y];
            data[x+1][y] = 0;
            x++;
            step++;
            //重新加载图片
            initImage();
        }else if(code == 39){
            //右
            if(y == 0){
                //如果空白方块已经在最右边了,就不能再往右移动了
                return;
            }
            System.out.println("向右移动");
            data[x][y] = data[x][y-1];
            data[x][y-1] = 0;
            y--;
            step++;
            //重新加载图片
            initImage();
        }else if(code == 40){
            //下
            if(x == 0){
                //如果空白方块已经在最下面了,就不能再往下移动了
                return;
            }
            System.out.println("向下移动");
            data[x][y] = data[x-1][y];
            data[x-1][y] = 0;
            x--;
            step++;
            //重新加载图片
            initImage();
        }else if(code == 65){
            initImage();
        }else if(code == 87){
            data=new int [][]{
                    {1,2,3,4},
                    {5,6,7,8},
                    {9,10,11,12},
                    {13,14,15,0}
            };
            initImage();
        }
    }

    //判断是否胜利
    public boolean victory(){
        for (int i = 0; i < data.length; i++) {
            for (int j = 0; j < data[i].length; j++) {
                if(data[i][j] != win[i][j]){
                    return false;
                }
            }
        }
        return true;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        //获取到当前点击的条目对象
        Object obj = e.getSource();
        if(obj == replayJMenuItem){
            System.out.println("重新游戏");

            //计步器清零
            step = 0;
            //重新打乱图片的顺序
            initData();
            //重新加载图片
            initImage();

        }else if(obj == reLoginJMenuItem){
            System.out.println("重新登录");
            //关闭当前的游戏界面
            this.setVisible(false);
            //打开登录界面
            new LoginJFrame();
        }else if(obj == closeJMenuItem){
            System.out.println("关闭游戏");
            System.exit(0);
        }else if(obj == accountJMenuItem){
            System.out.println("公众号");
            //1.创建集合
            Properties prop=new Properties();
            //2.读取数据
            try {
                FileInputStream fis=new FileInputStream("..\\puzzlegame\\game.properties");
                prop.load(fis);
                fis.close();
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }

            //3.获取图片的路径
            String path=(String)prop.get("account");

            //调用下面的showIDialog方法,展示弹框
            //showJDialog方法的参数,就是图片的路径
            //比如:showJDialog("puzzlegame\\image\\about1.png");
            showJDialog(path);

        }else if(obj == girlJMenuItem){
            System.out.println("美女");
            Random r=new Random();

            int i;
            if(flag==0){
                i=r.nextInt(13)+1;
            }else{
                i=r.nextInt(3)+14;
            }
            path="..\\puzzlegame\\image\\girl\\girl"+i+"\\";
            step=0;
            initData();
            initImage();
        }else if(obj == animalJMenuItem){
            System.out.println("动物");
            Random r=new Random();
            int i=r.nextInt(8)+1;
            path="..\\puzzlegame\\image\\animal\\animal"+i+"\\";
            step=0;
            initData();
            initImage();
        }else if(obj == sportJMenuItem){
            System.out.println("运动");
            Random r=new Random();
            int i=r.nextInt(10)+1;
            path="..\\puzzlegame\\image\\sport\\sport"+i+"\\";
            //计步器清零
            step=0;
            //再次打乱二维数组中的数据
            initData();
            //重新加载图片
            initImage();
        }else if (obj==saveItem0||obj==saveItem1||obj==saveItem2||obj==saveItem3||obj==saveItem4){
            //获取当前是哪个存档被点击了,获取其中的序号
            JMenuItem item=(JMenuItem) obj;
            String str=item.getText();
            int index=str.charAt(2)-'0';

            //之间把游戏的数据写到本地文件中
            try {
                ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("..\\puzzlegame\\save\\save"+index+".data"));
                GameInfo gi=new GameInfo(data,x,y,path,step);
                oos.writeObject(gi);
                oos.close();
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
            //修改一下存档item上的展示信息
            //存档0(xx步)
            item.setText("存档"+index+"("+step+"步)");
            //修改一下读档item上的展示信息
            loadJMenu.getItem(index).setText("存档"+index+"("+step+"步)");

        }else if (obj==loadItem0||obj==loadItem1||obj==loadItem2||obj==loadItem3||obj==loadItem4){
            System.out.println("读档");
            //获取当前是哪个读档被点击了,获取其中的序号
            JMenuItem item=(JMenuItem) obj;
            String str=item.getText();
            int index=str.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 ioException) {
                ioException.printStackTrace();
            } catch (ClassNotFoundException classNotFoundException) {
                classNotFoundException.printStackTrace();
            }

            data=gi.getData();
            path=gi.getPath();
            step=gi.getStep();
            x=gi.getX();
            y=gi.getY();

            //重新刷新界面并加载游戏
            initImage();;
        }
    }

    //创建一个弹框对象
    JDialog jDialog = new JDialog();
    //展示弹框
    public void showJDialog(String filepath) {
        if (!jDialog.isVisible()) {
            JLabel jLabel = null;
            //判断传递的字符串是否是一个路径,且必须以jpg或者png结尾
            //如果满足,则当做图片处理
            //如果不满足,则当做普通字符串处理
            if (new File(filepath).exists() && (filepath.endsWith(".png") || filepath.endsWith(".jpg"))) {
                jLabel = new JLabel(new ImageIcon(filepath));
            } else {
                jLabel = new JLabel(filepath);
            }
            //设置位置和宽高
            jLabel.setBounds(0, 0, 258, 258);
            //把图片添加到弹框当中
            jDialog.getContentPane().add(jLabel);
            //给弹框设置大小
            jDialog.setSize(344, 344);
            //让弹框置顶
            jDialog.setAlwaysOnTop(true);
            //让弹框居中
            jDialog.setLocationRelativeTo(null);
            //弹框不关闭则无法操作下面的界面
            jDialog.setModal(true);
            //让弹框显示出来
            jDialog.setVisible(true);
        }
    }
}

登录界面
package ui;

import cn.hutool.core.io.FileUtil;
import domain.User;
import util.CodeUtil;

import javax.swing.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class LoginJFrame extends JFrame implements MouseListener
{
    //表示登录界面
    //创建一个集合存储正确的用户名和密码
    ArrayList<User> allUsers=new ArrayList<>();

    //登录和注册
    JButton login = new JButton();
    JButton register = new JButton();

    //创建文本框对象
    JTextField username = new JTextField();
    JPasswordField password = new JPasswordField();
    JTextField code = new JTextField();

    //正确的验证码
    JLabel rightCode=new JLabel();

    public LoginJFrame(){
        //读取本地文件中的用户信息
        readUserInfo();

        //初始化界面
        initJFramed();

        //初始化页面
        initView();

        //让界面显示出来
        this.setVisible(true);
    }

    //读取本地文件中的用户信息
    private void readUserInfo() {
        //1.读取数据
        List<String> userInfoStrList = FileUtil.readUtf8Lines("D:\\idea代码\\untitled1\\puzzlegame\\userinfo.txt");
        //2.遍历集合并获取用户信息并创建User对象
        for (String str : userInfoStrList) {
            String[] userInfoArr=str.split("&");

            System.out.println(Arrays.toString(userInfoArr));
            String[] arr1 = userInfoArr[0].split("=");
            String[] arr2 = userInfoArr[1].split("=");

            User u=new User(arr1[1],arr2[1]);
            allUsers.add(u);
        }
        System.out.println(allUsers);
    }

    //初始化页面
    private void initView() {
        //1.添加用户名文字
        JLabel usernameText = new JLabel(new ImageIcon("..\\puzzlegame\\image\\login\\用户名.png"));
        usernameText.setBounds(116,135,47,17);
        this.getContentPane().add(usernameText);

        //2.添加用户名输入框
        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.setBounds(195,194,200,30);
        this.getContentPane().add(password);

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

        //6.添加验证码输入框
        code.setBounds(195,256,100,30);
        this.getContentPane().add(code);

        //随机生成一个验证码
        String codeStr= CodeUtil.getCode();
        System.out.println(codeStr);
        //设置内容
        rightCode.setText(codeStr);
        //位置和宽高
        rightCode.setBounds(300,256,50,30);
        //添加鼠标监听事件
        rightCode.addMouseListener(this);
        //添加到桌面
        this.getContentPane().add(rightCode);

        //7.添加登录按钮
        login.setBounds(123,310,128,47);
        login.setIcon(new ImageIcon("..\\puzzlegame\\image\\login\\登录按钮.png"));
        //去除按钮的边框
        login.setBorderPainted(false);
        //去除按钮的背景
        login.setContentAreaFilled(false);
        //添加鼠标监听事件
        login.addMouseListener(this);
        this.getContentPane().add(login);

        //8.添加注册按钮
        register.setBounds(256,310,128,47);
        register.setIcon(new ImageIcon("..\\puzzlegame\\image\\login\\注册按钮.png"));
        //去除按钮的边框
        register.setBorderPainted(false);
        //去除按钮的背景
        register.setContentAreaFilled(false);
        //添加鼠标监听事件
        register.addMouseListener(this);
        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 initJFramed() {
        //在创建登录界面的时候,同时给这个界面去设置一些信息
        this.setSize(488,430);
        //设置界面的标题
        this.setTitle("拼图 登录");
        //设置界面置顶
        this.setAlwaysOnTop(true);
        //设置界面居中显示
        this.setLocationRelativeTo(null);
        //设置关闭模式
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        //取消默认的居中放置,而是采用绝对定位的方式
        this.setLayout(null);
    }

    //设置弹窗
    public void showJDialog(String content){
        //创建一个弹框对象
        JDialog jDialog=new JDialog();
        //给弹框设置大小
        jDialog.setSize(200,150);
        //设置弹框置顶
        jDialog.setAlwaysOnTop(true);
        //设置弹框居中显示
        jDialog.setLocationRelativeTo(null);
        //设置弹框不关闭永远无法操作下面的界面
        jDialog.setModal(true);

        //创建JLabel对象管理文字并添加到弹框当中
        JLabel warning=new JLabel(content);
        warning.setBounds(0,0,200,150);
        jDialog.getContentPane().add(warning);

        //让弹框显示出来
        jDialog.setVisible(true);
    }

    //鼠标按下并释放事件
    @Override
    public void mouseClicked(MouseEvent e) {
        if (e.getSource() == login) {
            System.out.println("点击了登录按钮");
            //获取两个文本输入框中的内容
            String usernameInput = username.getText();
            String passwordInput = password.getText();
            //获取用户输入的验证码
            String codeInput = code.getText();

            //创建一个User对象
            User userInfo = new User(usernameInput, passwordInput);
            System.out.println("用户输入的用户名为" + usernameInput);
            System.out.println("用户输入的密码为" + passwordInput);

            if (codeInput.length() == 0) {
                showJDialog("验证码不能为空");
            } else if (usernameInput.length() == 0 || passwordInput.length() == 0) {
                //校验用户名和密码是否为空
                System.out.println("用户名或者密码为空");

                //调用showJDialog方法并展示弹框
                showJDialog("用户名或者密码为空");


            } else if (!codeInput.equalsIgnoreCase(rightCode.getText())) {
                showJDialog("验证码输入错误");
            } else if (contains(userInfo)) {
                System.out.println("用户名和密码正确可以开始玩游戏了");
                //关闭当前登录界面
                this.setVisible(false);
                //打开游戏的主界面
                //需要把当前登录的用户名传递给游戏界面
                if(usernameInput.equals("yangyuchan")){
                    new GameJFrame(1);
                }else {
                    new GameJFrame(0);
                }
            } else {
                System.out.println("用户名或密码错误");
                showJDialog("用户名或密码错误");
            }
            login.setBounds(123,310,128,47);
            login.setIcon(new ImageIcon("..\\puzzlegame\\image\\login\\登录按钮.png"));
        } else if (e.getSource() == register) {
            System.out.println("点击了注册按钮");
            //关闭当前的登录界面
            this.setVisible(false);
            //打开注册界面
            new RegisterJFrame(allUsers);
        } else if (e.getSource() == rightCode) {
            System.out.println("更换验证码");
            //获取一个新的验证码
            String code = CodeUtil.getCode();
            rightCode.setText(code);
        }
    }

    //鼠标按下事件
    @Override
    public void mousePressed(MouseEvent e) {
        if(e.getSource()==login){
            System.out.println("登录按钮被按下了");
            login.setIcon(new ImageIcon("puzzlegame\\image\\login\\登录按下.png"));
        } else if (e.getSource()==register) {
            System.out.println("注册按钮被按下了");
            register.setIcon(new ImageIcon("puzzlegame\\image\\login\\注册按下.png"));
        }
    }

    //鼠标释放事件
    @Override
    public void mouseReleased(MouseEvent e) {
        if (e.getSource() == login) {
            login.setIcon(new ImageIcon("puzzlegame\\image\\login\\登录按钮.png"));
        } else if (e.getSource() == register) {
            register.setIcon(new ImageIcon("puzzlegame\\image\\login\\注册按钮.png"));
        }
    }

    //鼠标进入事件
    @Override
    public void mouseEntered(MouseEvent e) {

    }

    //鼠标离开事件
    @Override
    public void mouseExited(MouseEvent e) {

    }

    //判断用户在集合中是否存在
    public boolean contains(User userInput){
        for (int i = 0; i < allUsers.size(); i++) {
            User rightUser=allUsers.get(i);
            if(userInput.getUsername().equals(rightUser.getUsername())&&userInput.getPassword().equals(rightUser.getPassword())){
                //有相同的代表存在,返回true,后面的不需要再比了
                return true;
            }
        }
        //混合结束之后还没有找到就表示不存在
        return false;
    }
}

注册界面
package ui;

import cn.hutool.core.io.FileUtil;
import domain.User;

import javax.swing.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;

public class RegisterJFrame extends JFrame implements MouseListener{
    //表示注册的界面

    ArrayList<User> allUsers;

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

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

    public RegisterJFrame(ArrayList<User> allUsers){
        this.allUsers=allUsers;
        //初始化界面
        initFrame();
        initView();
        //让界面显示出来
        this.setVisible(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);
        submit.addMouseListener(this);

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

        //背景图片
        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 initFrame() {
        //对自己的界面做一些设置。
        //设置宽高
        setSize(488, 430);
        //设置标题
        setTitle("拼图游戏 V1.0注册");
        //取消内部默认布局
        setLayout(null);
        //设置关闭模式
        setDefaultCloseOperation(3);
        //设置居中
        setLocationRelativeTo(null);
        //设置置顶
        setAlwaysOnTop(true);
    }

    //只创建一个弹框对象
    JDialog jDialog = new JDialog();
    //因为展示弹框的代码,会被运行多次
    //所以,我们把展示弹框的代码,抽取到一个方法中。以后用到的时候,就不需要写了
    //直接调用就可以了。
    public void showDialog(String content) {
        if (!jDialog.isVisible()) {
            //把弹框中原来的文字给清空掉。
            jDialog.getContentPane().removeAll();
            JLabel jLabel = new JLabel(content);
            jLabel.setBounds(0, 0, 200, 150);
            jDialog.add(jLabel);
            //给弹框设置大小
            jDialog.setSize(200, 150);
            //要把弹框在设置为顶层 -- 置顶效果
            jDialog.setAlwaysOnTop(true);
            //要让jDialog居中
            jDialog.setLocationRelativeTo(null);
            //让弹框
            jDialog.setModal(true);
            //让jDialog显示出来
            jDialog.setVisible(true);
        }
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        if(e.getSource()==submit){
            //点击了注册按钮
            //1.用户名,密码不能为空
            if(username.getText().length()==0||password.getText().length()==0||rePassword.getText().length()==0){
                showDialog("用户名和密码不能为空");
                return;
            }
            //2.判断两次密码输入是否一致
            if(!password.getText().equals(rePassword.getText())){
                showDialog("两次密码输入不一致");
                return;
            }
            //3.判断用户名和密码1的格式是否正确
            if(!username.getText().matches("[a-zA-Z0-9]{4,16}")){
                showDialog("用户名必须是4~16位的数字或字母");
                return;
            }
            if(!password.getText().matches("\\S*(?=\\S{6,})(?=\\S*\\d)(?=\\S*[a-z])\\S*")){
                showDialog("密码不符合规则,至少包含1个小写字母,1个数字,长度至少6位");
                return;
            }
            //4.判断用户名是否已经重复
            if(containsUsername(username.getText())){
                showDialog("用户名已经存在,请重新输入");
                return;
            }
            //5.添加用户
            allUsers.add(new User(username.getText(),password.getText()));
            //6.写入文件
            FileUtil.writeLines(allUsers,"D:\\idea代码\\untitled1\\puzzlegame\\userinfo.txt","UTF-8");
            //7.提示注册成功
            showDialog("注册成功");
            //关闭注册界面,打开登录界面
            this.setVisible(false);
            new LoginJFrame();
        }else if(e.getSource()==reset){
            //点击了重置按钮
            //清空了三个输入框
            username.setText("");
            password.setText("");
            rePassword.setText("");
        }
    }

    /*
    作用:
    参数:
    判断username在集合中是否存在
    用户名
    返回值:
    true:存在
    false:不存在
     */
    public boolean containsUsername(String username){
        for (User u : allUsers) {
            if (u.getUsername().equals(username)){
                return true;
            }
        }
        return false;
    }
    @Override
    public void mousePressed(MouseEvent e) {

    }

    @Override
    public void mouseReleased(MouseEvent e) {

    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }

}

验证码生成
package util;

import java.util.Random;

public final class CodeUtil {
    private CodeUtil(){

    }

    public static String getCode(){
        //a-97 A-65
        char []str=new char[52];
        for (int i = 0; i < str.length; i++) {
            if(i<=25){
                str[i]=(char)(i+65);
            }else{
                str[i]=(char)(i+71);
            }
        }
        StringBuilder sb=new StringBuilder();
        Random random=new Random();
        String code="";
        for (int i = 0; i < 4; i++) {
            int index = random.nextInt(str.length);
            code+=str[index];
        }
        code+=(random.nextInt(10));
        char[] chars = code.toCharArray();
        int index = random.nextInt(chars.length);
        char temp = chars[4];
        chars[4] = chars[index];
        chars[index] = temp;
        String result = new String(chars);
        return result;
    }
}

用户类
package domain;

public class User {
    private String username;
    private String password;


    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    /**
     * 获取
     *
     * @return username
     */
    public String getUsername() {
        return username;
    }

    /**
     * 设置
     *
     * @param username
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * 获取
     *
     * @return password
     */
    public String getPassword() {
        return password;
    }

    /**
     * 设置
     *
     * @param password
     */
    public void setPassword(String password) {
        this.password = password;
    }

    public String toString() {
        return "username=" + username + "&password=" + password;
    }
}

游戏信息类

package domain;

import java.io.Serial;
import java.io.Serializable;

public class GameInfo implements Serializable {


    @Serial
    private static final long serialVersionUID = -5313750424022850690L;
    private int[][] data;
    private int x=0;
    private int y=0;
    private String path;
    private int step;

    public GameInfo() {
    }

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

    /**
     * 获取
     * @return data
     */
    public int[][] getData() {
        return data;
    }

    /**
     * 设置
     * @param data
     */
    public void setData(int[][] data) {
        this.data = data;
    }

    /**
     * 获取
     * @return x
     */
    public int getX() {
        return x;
    }

    /**
     * 设置
     * @param x
     */
    public void setX(int x) {
        this.x = x;
    }

    /**
     * 获取
     * @return y
     */
    public int getY() {
        return y;
    }

    /**
     * 设置
     * @param y
     */
    public void setY(int y) {
        this.y = y;
    }

    /**
     * 获取
     * @return path
     */
    public String getPath() {
        return path;
    }

    /**
     * 设置
     * @param path
     */
    public void setPath(String path) {
        this.path = path;
    }

    /**
     * 获取
     * @return step
     */
    public int getStep() {
        return step;
    }

    /**
     * 设置
     * @param step
     */
    public void setStep(int step) {
        this.step = step;
    }

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