Java基础课的中下基础课05

目录

二十五、IO之File类及常用方法

25.1 什么是I/O

25.2 什么是文件

(1)为什么用文件存储

25.3 File类

25.4 什么叫流,做什么?

二十六、IO之文件夹遍历删除(递归)

26.1递归的理解

26.2 文件夹遍历删除

二十七、字节型文件流

27.1 流

27.2 字节型文件流(1字节)

(1)字节型文件输入流FileInputStream

(2)字节型文件输出流FileOutputStream

(3)小总

27.3 小任务文件夹复制

(1)文件的复制

(2)文件夹的复制

二十八、字符型文件流

27.2 字符型文件流

(1)FileReader输入流

(2)FileWriter输出流

(3)字符集

二十九、缓冲流

29.1 缓存流

29.2 字节型缓存流

(1)BufferedInputStream

(2)BufferedOutputStream

29.3 字符型缓冲流

(1)BufferedReader

(3)用BufferedReader和BufferedWriter做登录

三十、小总文件流

三十一、数组流(了解)

三十二、对象流

32.1 对象的序列化和反序列化

(1)ObjectInputStream / ObjectOutputStream

三十三、银行系统(网上银行)练习

33.1 业务说明

33.2 登录模块

(1)基本登录思想

(2)整体优化后:

33.2 查询余额模块

33.3 存款模块

33.4 整体代码

33.4 如何分类


二十五、IO之File类及常用方法

25.1 什么是I/O

I/O相关(输入/输出)流(数据流动)

数据流动的方向,读数据(输入Input) 写数据(输出output)

25.2 什么是文件

文件:一种电脑的存储形式,文件有不同的格式:.txt .doc .ppt .mp4 .jpg .zip等...

文件夹:是目录或路径

(1)为什么用文件存储

1、变量 只能存一份

2、数组 存储好多个 数据类型统一

3、集合 存储好多个 存储后个数还能改变 泛型----数据类型统一

如上三个都是Java中的类型(对象)-----------都存储在内存里,那么程序执行完毕,虚拟机jvm停止的时候,内存空间就回收了,数据都是临时性存储的。

4、文件 存储好多信息 文件是存储在硬盘上的-------都是永久性保存,数据虽然安全,但是文件毕竟不在内存中,需要通过I/O操作文件

25.3 File类

File:与电脑上的文件或文件夹产生一一对应的映射关系

File:是个类,在java.io中,可以表示文件或目录路径名(文件夹)的抽象形式,File与真实硬盘中的文件或文件夹,不是一个东西

File是在内存中的一个对象<---映射--->硬盘上的文件或文件夹,产生一个映射关系,互相映射

canRead() canWrite() isHidden() isFile()

isDirectory() 判断当前的file是否是一个目录(文件)

length() 获取文件中字节的个数

lastMondified() 获取文件最后的修改时间----毫秒值

String path = getAbsolutePath() 获取文件的绝对路径,绝对路径可以通过完整的字符串,定位盘符、文件夹、文件;

相对路径没有盘符的写法,当前工程(项目)所在的位置找寻

String name = getName() 获取文件的名字

boolean = createNeswFIle() 创建新的文件

boolean = mkdir() 创建新的文件夹,外层没有,不能创建

boolean = mkdirs() 创建新的文件夹,外层没有,可以自动创建

String name = getParent() 获取当前file的父亲file名字

File file = getParentFile() 获取当前file的父亲file对象

String[] names = list() 获取当前file的所有儿子的名字(只有文件夹可以调用,文件调用就是空的,只有文件夹才会有儿子)

File[] files = listFiles() 获取当前file的所有儿子的对象

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
​
/**
 * @Author: Insight
 * @Description: TODO 操作File类
 * @Date: 2023/11/3 20:25
 * @Version: 1.0
 */
public class TestFile {
    public static void main(String[] args) {
​
        //File类没有无参构造函数,也就是创建对象时参数不能为空
        File file = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile//Test.txt");
        //file对象 是真实的文件吗? 不是
        //file对象在堆内存中,创建出来的一个对象空间
        //路径是看创建的对象,是否能与硬盘中的一个真实文件产生对应映射关系
        //通过文件流去读取文件的内容
        //系统内硬盘上的文件的名字,是不区分大小写的,内存中File对象,变量名字区分大小写
​
        //文件本身的一些属性 --->
        //如果test.txt是否能打开,
        System.out.println(file.canExecute());//true
        //如果test.txt可写就true,否则false
        System.out.println(file.canRead());//true
        System.out.println(file.canWrite());//true
        //文件是否隐藏
        System.out.println(file.isHidden());//false
        //判断当前的file是否是一个文件
        System.out.println(file.isFile());//true
        //判断当前的file是否是一个目录(文件)
        System.out.println(file.isDirectory());//false
System.out.println("==============================================================");
​
        //文件的大小是多少
        //Array.length List.size() String.length() File.length()
        long l = file.length();
        System.out.println(l);//0
System.out.println("==============================================================");
​
        //获取文件最后修改时间
        long time = file.lastModified();//毫秒的 看不懂可以格式化一下
        Date date = new Date(time);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd KK:mm:ss");
        System.out.println(sdf.format(date));//2023-11-04 09:57:39
System.out.println("==============================================================");
​
        //修改文件的时间
        file.setLastModified(time);
System.out.println("==============================================================");
​
        //获取文件的名字
        String name = file.getName();//Test.txt
System.out.println("==============================================================");
​
        //获取文件的绝对路径
        String path = file.getAbsolutePath();
        System.out.println(path);//D:\IDEA_JavaCode\Duyi\JavaBasics05\TestFile\Test.txt
System.out.println("==============================================================");
​
        //获取文件的相对路径,通常是用相对路径,自动找到跟目录
        File file2 = new File("TestFile\\Test.txt");
        String path2 = file2.getAbsolutePath();
        System.out.println(path2);//D:\IDEA_JavaCode\Duyi\JavaBasics05\TestFile\TestFil\Test.txt
System.out.println("==============================================================");
​
        //创建一个file对象
        File file1 = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile//Test02.txt");
        //文件夹是不占空间的,占内存的是文件,是文件夹中的文件,所以文件夹是没有异常的
        //通过这个对象,回头在硬盘上,创建文件,如果文件盘符写错了,他就真写不了了
        try{
            boolean value = file1.createNewFile();//编译时异常
            System.out.println(value);//true
        } catch (IOException e) {
            e.printStackTrace();
        }
System.out.println("==============================================================");
​
        //创建file对象
        File file3 = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile//Test03");
        //在硬盘上,创建一个新的文件夹,文件夹的创建是没有什么异常的
        //文件夹是不占空间的,占内存的是文件,是文件夹中的文件,所以文件夹是没有异常的
        boolean value = file3.mkdir();
        System.out.println(value);//true
​
        File file4 = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile//Test04//inter.txt");
        boolean b2 = file4.mkdir();//外层(父元素)需要真实存在才能创建(Test04)
        System.out.println(b2);//false
        boolean b = file4.mkdirs();//可以创建文件夹,如果外层没有,也会同时创建文件夹
        System.out.println(b);//true
System.out.println("==============================================================");
​
        File file5 = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile//inter");
        String pname = file5.getParent();//当前file的父亲名字 只是名字 TestFile
        //这个用的多一点开发
        File pfile = file5.getParentFile();//当前file的父亲对象 file----表示D盘IDEA_JavaCode//Duyi//JavaBasics05//TestFile//文件夹下的对象
        System.out.println(pname);
        System.out.println(pfile.getAbsolutePath());
System.out.println("==============================================================");
​
        //遍历当前file的所有父目录
        File files = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile//Test.txt");
        //想通过循环的方式看到这个文件往上的每一层关系
        File pfiles = files.getParentFile();//获取它的父亲
        while (pfiles != null) {//等于null说明是盘符
            System.out.println(pfiles.getAbsolutePath());//先输出自己的父亲是谁
            pfiles = pfiles.getParentFile();//再找一遍,找父亲的父亲
        }
System.out.println("==============================================================");
​
        File filess = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile");
        //数组对象为空,证明当前的file是一个文件
        //数组对象不文空,证明当前的file是一个文件夹
        //如果数组对象的长度不为0,证明当前的file是一个不为空的文件夹,文件夹内有元素
        File[] file0 = filess.listFiles();
        System.out.println(file0);
        System.out.println(file0.length);
System.out.println("==============================================================");
​
        //遍历当前file的子元素需要通过while循环,那么如果当前file的子元素中还有子元素又需要while循环,所以遍历子元素不能和父元素一样
        File file00 = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile//Test02.txt");
        boolean bb = file.delete();//不能瞎玩啊
        System.out.println(bb);
System.out.println("==============================================================");
​
        
    }
}
​
如果把下面的属性改了,上面的代码就会跟随你文件属性进行改变

25.4 什么叫流,做什么?

流:就是操作文件中的内容

输入流:读取文件中的内容

输出流:向文件中写内容

比如:文件的复制和文件的加密

二十六、IO之文件夹遍历删除(递归)

26.1递归的理解

1、文件夹的遍历----需要一个递归来完成

public class TestMethod {
    public void testOne() {
        this.testTwo();
        System.out.println("我是testOne方法");
    }
    public void testTwo() {
        this.testThree();
        System.out.println("我是testOne方法");
    }
    public void testThree() {
        this.testOne();
        System.out.println("我是testOne方法");
    }
    public static void main(String[] args) {
        TestMethod tm = new TestMethod();
        //调用One,One等Two做完在做事,Two等Three做完在做,所以Three最先做,然后在Two、One
    /*
        临时执行testOne(1.调用TestTwo 2.自己的执行;未销毁因为还未执行完)
        临时执行testTwo(1.调用TestThree 2.自己的执行;未销毁因为还未执行完)
        临时执行testThree(1.自己的执行;执行完销毁)
        本质上就是递归
    */
        tm.testOne();
    }
}

理解递归:

public class TestRecursion {
    //建房子
    //递归思想:如果你想让我做事(让我盖第五层),只盖第5层,所以我要求别人先把前四层盖好了我才盖(5等4,4等3)
    //调用顺序是从上到下的,执行顺序是从下到上的;1、让别人先做事;2、我自己做事
    //5  等4做完 ---执行
    //4  等3做完 ---执行
    //3  等2做完 ---执行
    //2  等1做完 ---执行
    //1  不等 ---直接执行
    public void buildHouse(int floor) {//5层
        //判断当前floor是否为1,若不是找一个别人先盖之前的层
        if (floor > 1) {
            this.buildHouse(floor-1);
        }
        //2、我自己做事
        System.out.println("盖到第" + floor + "层了");
    }
​
    public static void main(String[] args) {
        TestRecursion testRecursion = new TestRecursion();
        testRecursion.buildHouse(5);
    }
}

26.2 文件夹遍历删除

递归不是一层一层找的,而是一个分支一直找到底

public class NewTestFile {
    //方法:用来展示(遍历)文件夹;参数-file(代表文件或文件夹)
    //递归不是一层一层找的,而是一个分支一直找到底
    public void showFile(File file) {
        
        //1.判断file是否一个文件夹,文件夹内如果有元素,找一个人先做
        //获取file的子元素,files==nul是个文件,files!=null是个文件夹,files.length!=是一个带元素的文件夹
        File[] files = file.listFiles();//src文件夹所有子元素
        if (files != null && files.length != 0) {
            for(File f:files) {//将每一个子元素都找人遍历一遍
                this.showFile(f);//循环第一次 src文件夹中的test 第二次 test文件夹中的test.html
            }
        }
        
        //2.做自己的显示(file是文件或file是一个空文件夹),放前面运行和放后面运行不一样
        System.out.println(file.getAbsolutePath());
    }
​
    public static void main(String[] args) {
        NewTestFile newTestFile = new NewTestFile();
        File file = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile02//Test");
        newTestFile.showFile(file);//找到src中的所有子
    }
}

递归删除文件夹中的子元素,别瞎玩

public static void main(String[] args) {
    NewTestFile newTestFile = new NewTestFile();
    File file = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile02//Test");
    newTestFile.deleteFile(file);//删除之后回收站也没有。别瞎玩
}
​
public void deleteFile(File file) {
    //判断file不是空文件夹,找人先做事
    File[] files = file.listFiles();
    if (file != null && files.length != 0) {
        for (File f:files) {
            this.deleteFile(f);
        }
    }
    //删除file(file是个文件或file是一个空文件夹)
    file.delete();
}

1、文件夹的遍历、文件夹的删除----需要一个递归

2、文件夹的路径---循环

二十七、字节型文件流

27.1 流

file对象不能操作文件中的内容--------需要通过I/O的方式来完成

流 按照方向(功能)来区分:in(读取) out(写入)

操作的目标来区分:文件流、数组流、字符串流、数据流、对象流、网络流....

学习文件流:读取文件中的信息in,将信息写入文件中out;

文件流按照读取或写入的单位(字节数)大小来区分:

(1)字节型文件流(1字节) FileInputStream / FileOutputStream

(2)字符型文件流(2字节~1字符) FileReader / FileWriter

27.2 字节型文件流(1字节)

(1)字节型文件输入流FileInputStream

FileInputStream

(1)java.io

(2)了解继承:InputStream类,字节型输入流的父类

(3)创建对象:调用一个带file类型的构造方法、调用一个带String类型的构造方法

(4)常用方法:

int code = read(); 每次从流管道中读取一个字节,返回的是字节的code码

int count = read( byte() ); 每次从n流管道中读取若干个字节,存入数组内,返回有效元素个数

public static void main(String[] args){
        //理解为:文件是一个仓库,fis对象搬运工,推一个平板车
        try{
            //创建一个字节型的文件输入流,读取一个文件中内容
            File file = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile02"
                                   + "//Test//aaa//02.txt");
​
            FileInputStream fis = new FileInputStream(file);//真实去读文件,如果没有此文件就会报错,所以我们可以try
​
            int i = fis.read(); //读取一个字节 -1,i表示这一个字节的code码,每次只能读一个字节太慢了
            System.out.println(i);//97
            //有可能读不到第一个字节
            while (i != -1) {//知道最后读完
                System.out.print((char)i);//读取的字节的UniCode码 0~65535
                i = fis.read(); //读取下一行的一个字节 -1
            }
​
        } catch (FileNotFoundException e){
            e.printStackTrace();
        } catch (IOException e){
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        try {
​
            FileInputStream fis = new FileInputStream("D://IDEA_JavaCode//Duyi//JavaBasics05"
                                                      + "//TestFile02//Test//aaa//02.txt");
            //创建一个空的数组(不是去数组里读东西,目的是去文件里读东西,然后把东西装入数组内)
            //利用一个数组从文件中读东西,装数组里,再拿这个数组给他显示出来
            byte[] b = new byte[5];//每次存储5个字节
            int count = fis.read(b);//去文件里读东西(读5个),装入数组b内,返回int型count表示读取到的有效字节个数
            System.out.println("有效字节个数count = " + count);//5
            while (count != -1) {
                //把数组构建成一个String在展示出来
                //String value = new String(b);
                //第一次   a b  c  d e
                //第二次   f g  \r \n h count=5将第一次的元素覆盖
                //3       i j  k  l m  .....
                //4       n \r \n o p  .....
                //5       q \r \n o p count=1,此时的数组内还有第4次的元素,但是第5次就一个元素只能覆盖第4次的第一个元素
                //就是构建String出来问题,每次都构建5给元素的位置,最后一行没有5个元素,我们应该有几个元素就构建几个空间
​
                //把数组构建成一个String在展示出来
                String value02 = new String(b,0,count);//从索引0构建count个空间
                System.out.print(value02);
                //在读一遍,读下一行
                count = fis.read(b);
            }
        }catch (IOException e) {
            e.printStackTrace();
        }
    }

int count = available(); 返回流管道中还有多少缓冲的字节数(就是fis对象与硬盘之间有一个流管道,这个管道进行一一映射,其中这个管道中有流动的数据(元素),available()用于获取流动的元素)

long l = skip( long n ); 跳过几个字节在读取,多线程----利用几个线程同时读取文件(1000字节,5个人同时读取;第一个人读1~200;第二个人2001~400....)

close(); 将流管道关闭,必须要做的事,最好放在finally里,注意代码的健壮性,判断严谨

    public static void main(String[] args) {
        //先定义为空,因为new File有可能是空文件夹或没有此文件,会报异常(new的过程才会产生异常)
        //,所以真正的文件或文件夹还是要在try中
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile02"
                                               + "//Test//aaa//02.txt"));
            long l = fis.skip(5);
            System.out.println(l);
            int code = fis.read();
            System.out.println(code);
​
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭的是流通道,不是file对象,关闭这件事情必须要做(如果不关闭就像你优盘不能退出一样
            //,当你点击退出时,显示您的设备正在打开)
            //所以关闭不能在try中如果前面的代码出现问题,这个close就不能执行了,try体系中finally
            //是必须执行的,所以放在finally
            try {
                //fis在执行try后就不存在了,所以finally中变量fis就失效了(相当于未定义fis),所以要在try体系外定义fis,
                //但是你在外面给fis=null,这里的null空无法调用close方法,所以还要在这里定义一个try体系
                if (fis != null) {
                    fis.close();//如果不为空,我最后就要关闭文件
                }
                //如果是空,就没必要关闭了
            } catch (IOException e) {
                e.printStackTrace();
            }
​
        }
​
    }

(2)字节型文件输出流FileOutputStream

FileOutputStream 将数据写入文件中

(1)java.io

(2)继承OutputStream,所有字节型输出流的父类

(3)创建对象:1、调用一个带FIle参数,还有FIle,Boolean重载

2、调用一个带String参数,还有String,boolean重载

(4)常用方法

write(int code); 将给定code对应的字符写入文件

write( byte[] ); 将数组中的全部字节写入文件,可以用此方法getByte()将写好的字符串转化为byte数组

String str = "1+1=2";
byte[] b = str.getBytes();

close(); 注意在finally中关闭

flush(); 将管道内的字节推入文件中(刷新)

public static void main(String[] args) {
    FileOutputStream fos = null;
    try {
        //创建一个字节型文件输出流
        File file = new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile02"
                              + "//Test//test00//test.txt");
        //如果创建的是文件输入流,若文件路径有问题,则抛出异常 FileNotFoundException(输入读)
        //若创建是输出流,他是输出写入,若文件路径有问题,则直接帮我们创建一个新的文件(文件夹路径写错就不行)
        fos = new FileOutputStream(file);
        //我们写的a是写在了流管道里面了(第一次run执行a写入了txt文件中)
        //fos.write(97);
        //这时我们在写一个b,但是打开text文件中发现上次写入的a被这次的b覆盖了(第二次run),其实是构建的事FileOutputStream(file)
        //它每次都创建一个新的file对象,然后覆盖了你之前的txt文件,如果想追加内容需要FileOutputStream(file,true)利用构造方法
        fos.write(98);
        //刷新,将管道中的字节,推入文件中
        fos.flush();
        System.out.println("写入完毕");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (fos != null) {
                fos.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
​
}
public static void main(String[] args) {
    FileOutputStream fos = null;
    try {
        //创建一个字节型文件输出流
        fos = new FileOutputStream("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile02"
                                   + "//Test//test00//test.txt",true);
        //创建一个数组(带信息的)
        //byte[] b = new byte[]{97,98,99};
        String str = "1+1=2";
        byte[] b = str.getBytes();
        fos.write(b);//将信息写入
        fos.flush();//刷新
        System.out.println("写入完毕");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (fos != null) {
                fos.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

(3)小总

字节型文件流:FileInputStream、FileOutputStream

(1)所在的包java.io

(2)各自继承关系InputStream / OutStream

(3)构造方法

利用file对象构造 new FileInputStream( file ); new OutputStream( file, true );

利用String对象构造 new FileInputStream( " " ); new OutputStream( " " ,true);

27.3 小任务文件夹复制

(1)文件的复制

public class OperateFile {    
	public void copyFile(File file, String path) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try{
            //创建输入流
            fis = new FileInputStream(file);
            //创建新的file对象,用于操作复制
            File newFile = new File(path + "//" + file.getName());
            //给新file创建输出流
            fos = new FileOutputStream(newFile);
            //读取文件,不可能一次读完,所以循环
            byte[] b = new byte[1024];//每次读取 1kb~8kb之间
            int count = fis.read(b);//每次1024个字节,最后一行没有1024,那么默认值是0,所以
            //循环读取
            while (count != -1) {
                //可以在这里做点手脚,比如加密
                //要将读取到的有效字节 写入,利用构造方法
                fos.write(b,0,count);
                fos.flush();
                count = fis.read(b);//在读
            }
            System.out.println("复制完毕");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭输入流
            try{
                if (fis != null) {
                    //输入流和输出流 各关各个,如果出现一次就会导致下一个未关闭
                    fis.close();
                }
            }catch (IOException e) {
                e.printStackTrace();
            }
            //关闭输出流
            try{
                if (fos != null) {
                    //输入流和输出流 各关各个,如果出现一次就会导致下一个未关闭
                    fos.close();
                }
            }catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
}
public static void main(String[] args) {
    OperateFile of = new OperateFile();
    of.copyFile(new File("D://IDEA_JavaCode//Duyi//JavaBasics05//TestFile02" +
            "//Test//test.txt"), "D://IDEA_JavaCode//Duyi//JavaBasics05//" +
            "TestFile02//Test02");
}

可以做点加密

如果想把加密完成的文件解密回去,首先将加密后的文件剪切到原路径下(把原路径下最开始的文件删除),然后在加密一次,也即是将元素在互换回去。

//循环读取
while (count != -1) {
    //可以在这里做点手脚,比如加密,每一次数组的前两个元素位置互换 1024
    byte temp = b[10];
    b[0] = b[1];
    b[1] = temp;
    //要将读取到的有效字节 写入,利用构造方法
    fos.write(b,0,count);
    fos.flush();
    count = fis.read(b);//在读
}

(2)文件夹的复制

//文件夹的复制 ,file可以表示文件  或者 文件夹
    public void superCopyFile(File file, String newPath) {
        //获取file(原文件)的绝对路径,拼串的方式获取新文件的名字
        String oldFilePath = file.getAbsolutePath();
        //按照冒号进行拆分(冒号没有了),只剩C和Test//aaa,[1]表示Test//aaa这一堆
        //将目标路径放在想复制的路径前面D://Test//bb复制到
        String newFilePath = newPath + oldFilePath.split(":")[1];
        //创建新的file对象
        File newFile = new File(newFilePath);
        //判断当前传递进来的file是个文件还是文件夹 isFile  isDirectory  listFiles直接找寻当前文件夹的子元素
        //获取当前传递进来的file对象的所以子元素
        File[] files = file.listFiles();
        //如果是文件夹,先把外层的文件夹复制过去,才能往里面写
        if (files != null) {//file不空说明是一个文件夹,才有数组对象
            //通过新的file对象操作,在硬盘上创建一个文件夹
            newFile.mkdir();//不用mkdirs因为这个file就一个,先把一个文件夹创建过去,下一次递归还是一个文件
            System.out.println(newFile.getName() + "文件夹复制完毕");
            //如果里面还有元素怎么办?递归
            //发现这个if什么时候会执行?是不是只有是文件夹的时候才会执行,文件是走的if 文件夹走下面else
            if (files != null && files.length != 0) {//证明里面还有东西
                //里面有子文件或文件夹,就在调用
                for (File f:files) {
                    this.superCopyFile(f,newPath);
                }
            }
        } else {//file是一个文件,没有子元素,不需要数组对象
            //创建两个文件流,分别读取旧的file和写入新的newFile
            FileInputStream fis = null;
            FileOutputStream fos = null;
            try {
                fis = new FileInputStream(file);
                fos = new FileOutputStream(newFile);
                byte[] b = new byte[1024];
                int count = fis.read(b);
                //循环读取
                while (count != -1) {
                    //将有效字节读到就写入
                    fos.write(b,0,count);
                    fos.flush();
                    //别忘记在读一边,将读来的信息进行覆盖
                    count = fis.read(b);
                }
                System.out.println(newFile.getName() + "文件复制完毕");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                //关闭输入流
                try {
                    if (fis != null) {
                        fis.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                //关闭输出流
                try {
                    if (fos != null) {
                        fos.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
​
    }
//剪切思路也是差不多
public void 剪切(){
    this.superCopyFile();
    this.deleteFile()
}

二十八、字符型文件流

27.2 字符型文件流

(1)和上面的字节型文件流使用方法一样,就是byte数组变成char数字,字符型是为了识别汉字的

(2)只能操作纯文本的文件.txt

(3)创建字符流对象的时候,会产生编译时异常 FileNotFoundException

(1)FileReader输入流

(1)java.io包

(2)继承 InputStreamReader

(3)常用:int code = read(); int count = read(char[])

(2)FileWriter输出流

(1)java.io包

(2)继承 OutputStreamWriter

(3)常用:write(int); write(char[]); write(String); flush close

(4)构造方法:

带file参数; 带file,boolean参数; 带String参数; 带String,boolean参数

(3)字符集

字符集

字符:文字和符号总成(Character)

不同国家的数字和符号是一样的,字母

不同国家的文字(中文、日文、韩文)

计算机最早产生是按照英文单词,单个字符设计的;字母、数字、符号--------1字节、8bit、256

如果计算机想要处理上述字母符号以外的其他字符--比如中文2字节,需要将中文进行字符编码----拆分和组合

拆分组合的规则------所谓的字符编码

平台(操作系统)默认字符集GBK,Linux(MacOs)默认字符集UTF-8

编译使用的开发环境 Idea---UTF-8 Eclipse---GBK

所以我们怎么才能让中文不出现乱码,我们改平台的字符集是比较麻烦的,所以我们改编译器的字符集

设置默认字符集为UTF-8

try{
    //创建一个字符型文件输入流----一个字符
    FileReader fr = new FileReader(new File("D://IDEA_JavaCode//Duyi//JavaBasics05" +
            "//TestFile02//Test//test.txt"));
    int code = fr.read();
    System.out.println(code);//25105读出来的时候是以GBK的形式读取的
    System.out.println((char)code);//我
} catch (IOException e) {
    e.printStackTrace();
}

二十九、缓冲流

29.1 缓存流

文件流:FileInputStream/FileOutputStream、FileReader/FileWriter

缓冲流:为了在流管道内增加缓存的数据,让我们使用流读取的文字更加的流程

为了保证数据是源源不断的流出,而不是断断续续的,可以在管道的硬盘端变粗(增加缓存数据),让主干的数据远远大于流出的数据(这里就是将高级流去包装低级流,缓冲流也叫高级流,它的创建通过低级流来完成的),让我们使用流读取的文字更加的流程

缓冲流也叫高级流,它的创建通过低级流来完成的,缓冲流的创建它里面放的是FileInputStream、FileOutputStream、FileReader、FileWriter是这四个低级流,就是高级流BufferedInputStream/BufferedOutputStream、BufferedReader/BufferedWriter将前面四个包装起来了,但是后面四个的性能和效率更高,但是使用方法差不多

缓冲流:BufferedInputStream/BufferedOutputStream、BufferedReader/BufferedWriter

29.2 字节型缓存流

public static void main(String[] args) {
    try {
        /*TODO 高级流的构建方式需要使用低级流构建*/
        FileInputStream fis = new FileInputStream(new File("D://IDEA_JavaCode//Duyi//JavaBasics05//" +
                "TestFile02//Test//test.txt"));
        BufferedInputStream bis = new BufferedInputStream(fis);//装入低级流

        FileOutputStream fos = new FileOutputStream(new File("D://IDEA_JavaCode//Duyi//JavaBasics05//" +
                "TestFile02//Test//test.txt"));
        BufferedOutputStream bos = new BufferedOutputStream(fos);//装入低级流

    } catch (IOException e) {
        e.printStackTrace();
    }

}

(1)BufferedInputStream

(1) 构建方式,使用低级流构建,需要将低级流装入高级流

(2)基本使用与低级流的方式完全一致

read(); skip(); available(); close();

bis.read();
bis.read(byte[]);
bis.available();
bis.skip(long);
bis.close();

(2)BufferedOutputStream

(1) 构建方式,使用低级流构建,需要将低级流装入高级流(注意:缓存流构建的时候没有boolean类型的参数)

(2)基本使用与低级流方法完全一致

write(); flush(); close();

bos.write(int);
bos.write(byte[]);
bos.flush();
bos.close();

29.3 字符型缓冲流

(1)BufferedReader

       try{
            FileReader fr = new FileReader(new File("D://IDEA_JavaCode//Duyi//JavaBasics05//" +
                    "TestFile02//Test//test.txt"));
            BufferedReader br = new BufferedReader(fr);
            //读取文件中一行的信息
            String value = br.readLine();
            while (value != null) {
                System.out.println(value);
                value = br.readLine();
            }
​
        } catch (IOException e) {
            e.printStackTrace();
        }

(3)用BufferedReader和BufferedWriter做登录

既然BufferedReader可以通过String value = br.readLine();方法读取一行信息,那么我们可不可以用它做登录?

以前数组、box、集合(List Set Map)用这些来做真实数据的存储,但是他们都是临时性的存储,因为他们都在内存中

所以用文件更好,永久性的存储,存储在硬盘上

public class TestBufferedReaderAndWriter {
    /**
     * @Description: TODO 用来做登录用户认证
     * @Author: 曹宇希
     * @Date: 2023/11/7 12:24
     * @Param: [username, password]
     * @Return: java.lang.String
     */
    public String login(String username, String password) {
        //真实的名字和密码,永久的存在数据库中-----数据持久化
        //文件我们采用.txt形式的纯文本(方便),文本中的内容,以行为单位的,每一行记录一个人的信息
        try {
            BufferedReader br = new BufferedReader(new FileReader("D://IDEA_JavaCode//Duyi//JavaBasics05//" +
                    "TestFile02//Test//login.txt"));
            //user表示一行的记录信息,记录着账号和密码
            String user = br.readLine();
            while (user != null) {
                //如果读到以后,将user信息,按照-拆分,分别于方法的参数进行比较 v[0]账号 v[1]密码
                String[] value = user.split("-");
                if (value[0].equals(username)) {
                    if (value[1].equals(password)) {
                        return "登录成功!";
                    }
                }
                //在读一边,进行下一行信息
                user = br.readLine();
            }
        } catch (Exception e) {
​
        }
        return "用户名或密码错误!";
    }
​
    public static void main(String[] args) {
        TestBufferedReaderAndWriter test = new TestBufferedReaderAndWriter();
        String result = test.login("xxx","123");
        System.out.println(result);
​
        try{
            //也可以向txt中写入信息,append追加信息
            BufferedWriter bw = new BufferedWriter(new FileWriter("D://IDEA_JavaCode//Duyi//JavaBasics05//" +
                    "TestFile02//Test//login.txt",true));
            //写入
            bw.newLine();
            bw.write("sada-12434");
            bw.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
​
    }
​
}

三十、小总文件流

文件流:

低级: 字节型 FileInputStream / FileOutputStream

字符型 FileReader / FileWriter

缓存(高级): 字节型 BufferedInputStream / BufferedOutputStream(操作文件/文件夹本身的加密复制)

字符型 BufferedReader / BufferedWriter(开发的时候经常用,通常操作纯文本拿它来做数据库来用)

三十一、数组流(了解)

数组流: byte数组 ByteArrayInputStream / ByteArrayOutputStream

char数组 CharArrayReader / CharArrayWriter

三十二、对象流

(1)为什么有要用文件:文件永久性保存信息,将很多的数据直接存入为念----数据持久化

(2)如果按照以行为单位写信息:好处在于每一行记录的信息都是相关的,信息的我们可以读取出来,可以直接看懂文件;

第一:不安全,你能看懂别人也行

第二:如果对象中有一些方法(动作)它是无法记录的,只能记录一些String信息,属性也行

所以才会出现对象流。

对象流:ObjectInputStream / ObjectOutputStream

(1)将对象直接存入文件中,将对象拆分成字节码,然后直接写入文件

32.1 对象的序列化和反序列化

(1)ObjectInputStream / ObjectOutputStream

对象的序列化:将一个完整的对象,拆分成字节碎片,记录在文件中(将对象写入文件)

对象的反序列化:将文件记录的对象碎片,反过来组合成一个完整的对象(从文件中读取对象)

如果想要将对象序列化到文件中:

(1)需要让对象Person实现Serializable接口下 ,是一个示意性的接口(里面没有实质性的动作)

public static void main(String[] args) {
    try {
        //将对象直接记录在文件中,永久保存,对象的序列化(持久化)
        Person p1 = new Person("cyx",18);
        //对象输出流
        FileOutputStream fos = new FileOutputStream("D://IDEA_JavaCode//Duyi//JavaBasics05//" +
                "TestFile02//Test//test.text");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        //将对象拆分成碎片,序列化到文件里,不能随便的序列化,必须要有一定规则的才行---去对象Person中实现Serializable接口
        oos.writeObject(p1);
        oos.flush();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

(2)同时为了让对象可以反序列化,我们需要让对象多存在一个属性private long serialVersionUID = 任意L,然后就可以将对象写进文件里了

如果想要将对象反序列化(就是从文件中读出来),需要给对象提供一个序列化的版本号,jdk有很多版本,如果我用1.7版本写一个String,然后读String是在一个1.8版本的运行环境中读的,写的版本和读的版本不一致,可能就导致方法里面的东西不一样(比如String在1.8版本进行了更新,更新了一些其他功能)

就是拿出来的对象需要和你现在的运行版本进行比对一下,如果版本没错我就给你反序列化读出来,否则无法反序列化)

然后输入移入User对象

package testbuffered;

import java.io.Serializable;

/**
 * @Description: TODO 人类
 * @Author: 曹宇希
 * @Date: 2023/11/7 13:40
 * @Version: 1.0
 * @Company: 版权所有
 */
public class Person implements Serializable {
    /**
     * @Description: TODO 反序列化需要添加版本号,添加一个serialVersionUID然后给初始值,这个值可以随意
     * @Author: 曹宇希
     * @Date: 2023/11/7 19:20
     */
    private long serialVersionUID = 5291531515806950297L;
    private String name;
    private int age;

    public Person(){}

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public void eat() {
        System.out.println("Person的eat方法");
    }
    /**
     * @Description: TODO 重写toString方法,就可以直接打印对象里面的东西了
     * @Author: 曹宇希
     * @Date: 2023/11/7 19:13
     * @Param: []
     * @Return: java.lang.String
     */
    @Override
    public String toString() {
        return this.name + "," + this.age;
    }

}
public static void main(String[] args) {
    try{
        //需要一个对象输入流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D://IDEA_JavaCode//Duyi" +
                "//JavaBasics05//" +
                "TestFile02//Test//test.text"));
        //readObject返回值是Object
        Person p = (Person) ois.readObject();
        System.out.println(p);
        p.eat();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

三十三、银行系统(网上银行)练习

33.1 业务说明

1、实现一个银行业务系统

2、要求有如下的业务功能

(1)登录

(2)查询余额

(3)存款

(4)取款

(5)转账

(6)开户

(7)销户

3、记录银行的用户信息

账户-密码-余额

33.2 登录模块

我能拿到项目的时候第一件事先做什么?

我们所有的功能业务是不是需要底层数据的支持啊?

1、底层数据如何存储:先采用文件形式.txt,然后每一行记录一个人的所有信息(账户-密码-余额),以后是需要数据库的引入

那这个.txt数据存储在哪里合适?

(1)文件存储在固定的位置,这样可能会造成客户不存在这个数据,比如你存储在E盘,但是客户没有E盘,还有就是你写的项目和你的数据不在一个地方,也不方便查看(不建议)

(2)文件存储在当前项目的内部,这样项目和代码都在一起,你把项目部署在客户上,这个数据就跟着项目一起走了

(1)基本登录思想

(1)先在项目中创建一个数据User.txt(暂时当作数据库)

(2)创建Test类(因为一开始不知道后面还有什么类出现)

(3)设计login方法,参数String用户给的名字和密码,返回String一串登录是否成功的信息

1.要比对数据信息是否正确,所以采用字符型输入流进行读取信息(BufferedReader),采用相对路径File file = new File("src\atmsystem\User.txt");,这个高级流要装入低级流

2.创建输入流后采用读取一行的形式readLine()

3.如果数据为空读取的第一行也为空,所以加循环判断 value != null,数据库不为空就一直读取到空为止;然后将读取到信息进行拆分(user[0]用户名,user[1]密码),拆分后进行用户名和密码判断,如果对就直接返回String信息就结束了;如果不对在while里添加读取下一行readLine()进行读取下一行;不对之后还有return错误信息(这个不能写try中,因为如果输入流有误,就无法执行return了)

4.login方法写完进行测试,创建TestMain类进行测试各个方法是否有误Test t = new Test(); t.login("曹宇希","123456");

package atmsystem;
​
import java.io.*;
​
/**
 * @Description: TODO 模拟网络银行的业务
 * @Author: 曹宇希
 * @Date: 2023/11/7 20:07
 * @Version: 1.0
 * @Company: 版权所有
 */
public class Test {
    /**
     * @Description: TODO 登录
     * @Author: 曹宇希
     * @Date: 2023/11/7 20:34
     * @Param: [username, password] 用户给的名字, 用户给的密码
     * @Return: java.lang.String 返回登录成功与否
     */
​
    public String login(String username, String password) {
        try{
            //创建一个字符型输入流,读取真实文件的记录
            //file采取的是相对路径,会自动找到当前项目的跟目录,src前面是当前工程的跟目录D:\IDEA.....\src...
            File file = new File("src\\atmsystem\\User.txt");
            FileReader fileReader = new FileReader(file);
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            //读取一行数据信息,value表示一行人的信息
            String value = bufferedReader.readLine();
            //如何数据库不为空,就一致读到为空为止
            while (value != null) {
                String[] user = value.split("-");
                if (user[0].equals(username)) {
                    if (user[1].equals(password)) {
                        return "登录成功!";
                    }
                }
                //总要读取下一行
                value = bufferedReader.readLine(); 
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "密码或用户名错误!";
    }
}

(4)对登录的疑问?我们做完之后会有很多客户去用,每一次只有登录都要让对象读一边,那么读文件就需要一个管道,频繁读文件特别慢,那能不能一次性都读到计算机里,留着它,以后登录就不需要读文件了,而是读取你留好的那个地方,这就是缓存机制性能高

发现登录的方法,每一次调用需要创建流管道,读取文件的信息,内存中操作登录的业务,文件不在一个地方,认为读取过程很慢,

(5)解决登录性能的优化。创建一个Map集合充当缓存,将文件中的数据全部都读出来,就读一次;创建Map集合充当缓存,将文件中的数据全部读取出来

一行记录,创建一个对象存起来,一个人,一个对象,三个属性,HashMap<String, User>第二个参数无论是什么,它的目的都是为了存储人的所有信息的,所以拿对象当第二个参数来提高阅读性,因为对象的名字可以随便写(自己创建),人的信息是可变的数组不太好,Map阅读性差,对象中的属性可以自己定义

private HashMap<String, User> userBox = new HashMap<String, User>();

(2)整体优化后:

  1. 创建UserBox缓存,用Map集合充当缓存,存储用户数据信息,key为用户账户,value为User对象用于存储用户数据信息,提供阅读性

  2. 创建User对象,实现Serializable接口,对象中定义属性(版本号、String账户、String密码、Float余额),设定构造方法用于创建对象,创建get和set方法用于获取和设置用户数据信息

  3. 缓存创建完成后,要给Map集合存入用户数据信息,创建程序块{}用于将数据存入map集合

  4. 程序块中采用try{ } catch( ){ } finally{ }结构,在此结构中创建字符型输入流FileReader和字符型缓存输入流BufferedReader,用于读取,采用读取一行信息的形式String value = bufferedReader.readLine();,由于读取信息不是一次读完,所以while (value != null) {}循环遍历

  5. 在while中将读取到的信息value进行拆分成数组String[] userValue = value.split("-");,将这些信息存入对象中 new User(String,String,Float);,然后再将对象存入Map集合中hashMap<String账户,User对象>,然后再去读取一行信息value = bufferedReader.readLine();

  6. 最后将字符型输入流FielReader和字符型缓存输入流BufferedReader关闭

  7. 设计login方法,登录 参数:用户给的名字, 用户给的密码,返回值:返回登录成功与否

  8. 在login中获取缓存中的用户名User user = userBox.get(username),也就是获取key然后找value,value是User对象;然后if判断对象中存数据信息与输入的是否一致

package atmsystem;
​
import java.io.*;
import java.util.HashMap;
​
//模拟网络银行的业务
public class Test {
    // Map集合充当缓存(方便查找),存储人当中的所有数据,参数:String每个人的用户名不同(key) User对象存用户数据(value)    
    // 登录方法,每一次调用都创建流管道,读取文件的信息很慢,利用Map集合充当缓存,将文件中的数据全部读取出来放入缓存中
    
    //一行记录,创建一个对象存起来,一个人,一个对象,三个属性,HashMap<String, User>第二个参数无论是什么,它的目的都是
    //为了存储人的所有信息的,所以拿对象当第二个参数来提高阅读性,因为对象的名字可以随便写(自己创建),人的信息是可变的数组
    //不太好,Map阅读性差,对象中的属性可以自己定义
    private HashMap<String, User> userBox = new HashMap<String, User>();
    // 程序块给Map存用户数据程序块目的在对象创建之前,给Map集合进行赋值操作,将数据存入缓存中
    {
        FileReader fileReader = null;
        BufferedReader bufferedReader = null;
        try{
            //创建一个字符型输入流,读取真实文件的记录
            //file采取的是相对路径,会自动找到当前项目的跟目录,src前面是当前工程的跟目录D:\IDEA.....\src...
            File file = new File("src\\atmsystem\\User.txt");
            fileReader = new FileReader(file);
            bufferedReader = new BufferedReader(fileReader);
            //读取一行数据信息,value表示一行人的信息
            String value = bufferedReader.readLine();
            //如何数据库不为空,就一致读到为空为止
            while (value != null) {
                //将value的信息拆分成三段
                String[] userValue = value.split("-");
                //构建一个User对象,存储三段信息(三个属性刚好存储), User构造方法中第三个参数(第三个属性)是Float类型的
                User user =  new User(userValue[0], userValue[1], Float.parseFloat(userValue[2]));
                //将对象存入Map集合<账户,User对象>
                userBox.put(userValue[0],user);
                //再次读取
                value = bufferedReader.readLine();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //fileReader关闭字符型输入流
            try {
                if (fileReader != null) {
                    fileReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            //bufferedReader关闭字符型缓存流
            try{
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
​
    //登录 参数:用户给的名字, 用户给的密码,返回值:返回登录成功与否
    public String login(String username, String password) {
        User user = userBox.get(username);
        if (user != null && user.getPassword().equals(password)) {
            return "登录成功!";
        }
        return "密码或用户名错误!";
    }
}
package atmsystem;
​
import java.io.Serializable;
//User 记录数据库中的一行信息,把账户-密码-余额以对象的形式包起来,继承Serializable接口
public class User implements Serializable {
​
    //版本号
    private static final long serialVersionUID = -322964954562611312L;
  
    private String username;
​
    private String password;
​
    private Float userBalance;
    //构造器
    public User(){}
    public User(String username, String password, Float userBalance) {
        this.username = username;
        this.password = password;
        this.userBalance = userBalance;
    }
    //get,set方法
    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;
    }
​
    public Float getUserBalance() {
        return userBalance;
    }
​
    public void setUserBalance(Float userBalance) {
        this.userBalance = userBalance;
    }
}

33.2 查询余额模块

package atmsystem;
​
import java.io.*;
import java.util.HashMap;
import java.util.Iterator;
​
/**
 * @Description: TODO 模拟网络银行的业务
 * @Author: 曹宇希
 * @Date: 2023/11/7 20:07
 * @Version: 1.0
 * @Company: 版权所有
 */
public class Test {
    /**
     * @Description: TODO Map集合充当缓存,存储人当中的所有数据
     *                  String每个人的用户名不同(key) User对象存用户数据(value)
     *登录方法,每一次调用都创建流管道,读取文件的信息很慢,利用Map集合充当缓存,将文件中的数据全部读取出来
     * @Author: 曹宇希
     * @Date: 2023/11/7 20:34
     */
    private HashMap<String, User> userBox = new HashMap<String, User>();
    /**
     * @Description: TODO 程序块给Map存用户数据
     * 程序块目的在对象创建之前,给Map集合进行赋值操作,将数据存入缓存中
     * @Author: 曹宇希
     * @Date: 2023/11/8 14:59
     */
    {
        FileReader fileReader = null;
        BufferedReader bufferedReader = null;
        try{
            //创建一个字符型输入流,读取真实文件的记录
            //file采取的是相对路径,会自动找到当前项目的跟目录,src前面是当前工程的跟目录D:\IDEA.....\src...
            File file = new File("src\\atmsystem\\User.txt");
            fileReader = new FileReader(file);
            bufferedReader = new BufferedReader(fileReader);
            //读取一行数据信息,value表示一行人的信息
            String value = bufferedReader.readLine();
            //如何数据库不为空,就一致读到为空为止
            while (value != null) {
                //将value的信息拆分成三段
                String[] userValue = value.split("-");
                //构建一个User对象,存储三段信息(三个属性刚好存储), User构造方法中第三个参数(第三个属性)是Float类型的
                User user =  new User(userValue[0], userValue[1], Float.parseFloat(userValue[2]));
                //将对象存入Map集合<账户,User对象>
                userBox.put(userValue[0],user);
                //再次读取
                value = bufferedReader.readLine();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //fileReader关闭字符型输入流
            try {
                if (fileReader != null) {
                    fileReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            //bufferedReader关闭字符型缓存流
            try{
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
​
/*网络银行的业务,所有的业务方法,按照我们之前的优化结构设计,以下只有业务逻辑,判断、比较、
计算等等,看不到任何数据的操作(从哪查,从哪存都看不见),横线以上都是对数据的真实操作,所以
这两个应该是放在不同的类中*/
    /**
     * @Description: TODO 登录
     * @Author: 曹宇希
     * @Date: 2023/11/7 20:34
     * @Param: [username, password] 用户给的名字, 用户给的密码
     * @Return: java.lang.String 返回登录成功与否
     */
    public String login(String username, String password) {
        User user = this.selectOne(username);
        if (user != null && user.getPassword().equals(password)) {
            return "登录成功!";
        }
        return "密码或用户名错误!";
    }
​
    /**
     * @Description: TODO 查询余额
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:17
     * @Param: [username] 账户
     * @Return: java.lang.Float
     */
    public Float queryBalance(String username) {
        User user = this.selectOne(username);
        return user.getUserBalance();
    }
​
    /**
     * @Description: TODO 存款
     * 我们只做业务,至于怎么查出来的,怎么存入的不用管
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:20
     * @Param: [username, depositMoney] 账户 存入的金额
     * @Return: void
     */
    public void deposit(String username, Float depositMoney) {
        //找到User对象,修改对象中的balance属性
        User user = this.selectOne(username);
        //将原有的金额 加上 存入的金额
        user.setUserBalance(user.getUserBalance() + depositMoney);
        //将修改后的对象数据,在存入Map集合中
        this.update(user);
        //当修改以后就可以将修改后的对象数据全部写入文件中
        //将临时的数据永久的写入文件
        this.commit();
    }
​
    /**
     * @Description: TODO   查询缓存中的对象信息
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:41
     * @Param: [username] 账户
     * @Return: atmsystem.User 对象
     */
    public User selectOne(String username) {
        //这里是一行,以后如果不是一行的时候,只需要改变这个方法就可以了
        return userBox.get(username);
    }
    /**
     * @Description: TODO 将一个修改完毕的对象在存入集合
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:51
     * @Param: [user]
     * @Return: void
     */
    public void update(User user) {
        //集合做了修改
        userBox.put(user.getUsername(), user);
​
    }
​
    /**
     * @Description: TODO 将修改后的对象数据全部写入文件中
     * HashMap<String, User>
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:57
     * @Param: []
     * @Return: void
     */
    public void commit() {
        FileWriter fileWriter = null;
        BufferedWriter bufferedWriter = null;
        try {
            //创建一个字符型文件输出流
            File file = new File("");
            fileWriter = new FileWriter(file);
            bufferedWriter = new BufferedWriter(fileWriter);
            //遍历集合,因为Map的集合key是不一样的,userBox.keySet()返回值是Set集合所以用迭代器
            Iterator<String> names = userBox.keySet().iterator();
            while (names.hasNext()) {
                //Set集合内获取的某一个人名,用人名确定对象
                String name = names.next();
                //获取对象,记录一个人的真实数据
                User user = userBox.get(name);
                //将user的真实数据拼凑成一行字符串
                StringBuilder builder = new StringBuilder(user.getUsername());
                builder.append("-");
                builder.append(user.getPassword());
                builder.append("-");
                builder.append(user.getUserBalance());
                //将字符串在重写写入文件里,字符型文件输出流将拼接好的builder写入文件
                //write参数是String的,builder转为字符串
                bufferedWriter.write(builder.toString());
                //换行
                bufferedWriter.newLine();
                bufferedWriter.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭字符型输出流
            try {
                if (fileWriter != null) {
                    fileWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            //关闭字符型输出缓存流
            try {
                if (bufferedWriter != null) {
                    bufferedWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
​
    }
​
    /**
     * @Description: TODO 取款
     * 与存款一样就是加减的问题
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:20
     * @Param: [username, withdrawalMoney] 账户 取出的金额
     * @Return: void
     */
    public void withdrawal(String username, Float withdrawalMoney) {
        //找到User对象,修改对象中的balance属性
        User user = this.selectOne(username);
        //如果账户的金额大于取出的金额就可以取
        if (user.getUserBalance() > withdrawalMoney) {
            //将原有的金额 减去 取出的金额
            user.setUserBalance(user.getUserBalance() - withdrawalMoney);
            //将修改后的对象数据,在存入Map集合中
            this.update(user);
            //当修改以后就可以将修改后的对象数据全部写入文件中
            //将临时的数据永久的写入文件
            this.commit();
        } else {
            System.out.println("对不起," + username + "您的账户余额不足!");
        }
​
    }
​
    /**
     * @Description: TODO 转账
     * 转账就是先存后取
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:20
     * @Param: [outName, inName, transferMoney] 转出账户,转入账户,转账金额
     * @Return: void
     */
    public void transfer(String outName, String inName, Float transferMoney) {
        //找到User对象,修改对象中的balance属性
        User outUser = this.selectOne(outName);
        //如果转出用户的金额大于转账金额就可以转
        if (outUser.getUserBalance() > transferMoney) {
            //查询收账用户,查询到底转账的给哪个用户
            User inUser = this.selectOne(inName);
            //收账的用户不为空,说明收账用户存在
            if (inUser != null) {
                //转出用户的余额 - 传出的钱
                outUser.setUserBalance(outUser.getUserBalance() - transferMoney);
                //收账用户的余额 + 上面转出的钱
                inUser.setUserBalance(inUser.getUserBalance() + transferMoney);
                //将修改后的对象数据,在存入Map集合中
                this.update(outUser);
                this.update(inUser);
                //当修改以后就可以将修改后的对象数据全部写入文件中
                //将临时的数据永久的写入文件
                this.commit();
            } else {
                System.out.println("对不起,您输入的" + inName + "账户不存在!");
            }
​
        } else {
            System.out.println("对不起," + outName + "您的账户余额不足!");
        }
​
    }
}

你会发现登录需要先查询集合中的对象key,通过key去找数据value信息;然后余额也需要这一步也是先查。那么以后在添加方法也会执行此代码过程,太繁琐了,所以我们设计一个查询方法selectOne(String username),目的为了我的所有业务服务,参数账户名,返回值User对象

1、设计selectOne方法用于查询缓存(Map集合)中的对象,怎么进行查Map集合,通过Key查询相应Value(userBox.get(账户名)),key表示账户,value表示一个人的数据信息,参数:String账户,返回值一个User对象

33.3 存款模块

存款就是在数据库中做一个对金额的修改,数据库中的用户名不变,密码也不变,数据库中的行数也没变;他只是某一行当中的某个信息发生改变。

在没有集合前,我们修改数据库.txt,是修改某一行的记录,我们读取一行可以,写一行新的也可以,但是在.txt中修改金额根本做不到

所以,我们想要修改数据,需要将原数据存到一个临时的地方,然后在临时的地方进行修改,然后再把修改后的临时数据在存入到原数据的文件下。

在有了集合后,集合除了可以充当缓存,增强执行性能以外,还能用来做数据的修改。(先将集合内的数据做修改,找到某一个user对象,将对象中的balance属性修改,然后在将集合内所有最终的数据全部写入文件中,文件内的所有内容替换掉)

33.4 整体代码

package atmsystem;
​
import java.io.*;
import java.util.HashMap;
import java.util.Iterator;
​
/**
 * @Description: TODO 模拟网络银行的业务
 * @Author: 曹宇希
 * @Date: 2023/11/7 20:07
 * @Version: 1.0
 * @Company: 版权所有
 */
public class Test {
    /**
     * @Description: TODO Map集合充当缓存,存储人当中的所有数据
     *                  String每个人的用户名不同(key) User对象存用户数据(value)
     *登录方法,每一次调用都创建流管道,读取文件的信息很慢,利用Map集合充当缓存,将文件中的数据全部读取出来
     * @Author: 曹宇希
     * @Date: 2023/11/7 20:34
     */
    private HashMap<String, User> userBox = new HashMap<String, User>();
    /**
     * @Description: TODO 程序块给Map存用户数据
     * 程序块目的在对象创建之前,给Map集合进行赋值操作,将数据存入缓存中
     * @Author: 曹宇希
     * @Date: 2023/11/8 14:59
     */
    {
        FileReader fileReader = null;
        BufferedReader bufferedReader = null;
        try{
            //创建一个字符型输入流,读取真实文件的记录
            //file采取的是相对路径,会自动找到当前项目的跟目录,src前面是当前工程的跟目录D:\IDEA.....\src...
            File file = new File("src\\atmsystem\\User.txt");
            fileReader = new FileReader(file);
            bufferedReader = new BufferedReader(fileReader);
            //读取一行数据信息,value表示一行人的信息
            String value = bufferedReader.readLine();
            //如何数据库不为空,就一致读到为空为止
            while (value != null) {
                //将value的信息拆分成三段
                String[] userValue = value.split("-");
                //构建一个User对象,存储三段信息(三个属性刚好存储), User构造方法中第三个参数(第三个属性)是Float类型的
                User user =  new User(userValue[0], userValue[1], Float.parseFloat(userValue[2]));
                //将对象存入Map集合<账户,User对象>
                userBox.put(userValue[0],user);
                //再次读取
                value = bufferedReader.readLine();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //fileReader关闭字符型输入流
            try {
                if (fileReader != null) {
                    fileReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            //bufferedReader关闭字符型缓存流
            try{
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
​
/*网络银行的业务,所有的业务方法,按照我们之前的优化结构设计,以下只有业务逻辑,判断、比较、
计算等等,看不到任何数据的操作(从哪查,从哪存都看不见),横线以上都是对数据的真实操作,所以
这两个应该是放在不同的类中*/
    /**
     * @Description: TODO 登录
     * @Author: 曹宇希
     * @Date: 2023/11/7 20:34
     * @Param: [username, password] 用户给的名字, 用户给的密码
     * @Return: java.lang.String 返回登录成功与否
     */
    public String login(String username, String password) {
        User user = this.selectOne(username);
        if (user != null && user.getPassword().equals(password)) {
            return "登录成功!";
        }
        return "密码或用户名错误!";
    }
​
    /**
     * @Description: TODO 查询余额
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:17
     * @Param: [username] 账户
     * @Return: java.lang.Float
     */
    public Float queryBalance(String username) {
        User user = this.selectOne(username);
        return user.getUserBalance();
    }
​
    /**
     * @Description: TODO 存款
     * 我们只做业务,至于怎么查出来的,怎么存入的不用管
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:20
     * @Param: [username, depositMoney] 账户 存入的金额
     * @Return: void
     */
    public void deposit(String username, Float depositMoney) {
        //找到User对象,修改对象中的balance属性
        User user = this.selectOne(username);
        //将原有的金额 加上 存入的金额
        user.setUserBalance(user.getUserBalance() + depositMoney);
        //将修改后的对象数据,在存入Map集合中
        this.update(user);
        //当修改以后就可以将修改后的对象数据全部写入文件中
        //将临时的数据永久的写入文件
        this.commit();
    }
​
    /**
     * @Description: TODO   查询缓存中的对象信息
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:41
     * @Param: [username] 账户
     * @Return: atmsystem.User 对象
     */
    public User selectOne(String username) {
        //这里是一行,以后如果不是一行的时候,只需要改变这个方法就可以了
        return userBox.get(username);
    }
    /**
     * @Description: TODO 将一个修改完毕的对象在存入集合
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:51
     * @Param: [user]
     * @Return: void
     */
    public void update(User user) {
        //集合做了修改
        userBox.put(user.getUsername(), user);
​
    }
​
    /**
     * @Description: TODO 将修改后的对象数据全部写入文件中
     * HashMap<String, User>
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:57
     * @Param: []
     * @Return: void
     */
    public void commit() {
        FileWriter fileWriter = null;
        BufferedWriter bufferedWriter = null;
        try {
            //创建一个字符型文件输出流
            File file = new File("");
            fileWriter = new FileWriter(file);
            bufferedWriter = new BufferedWriter(fileWriter);
            //遍历集合,因为Map的集合key是不一样的,userBox.keySet()返回值是Set集合所以用迭代器
            Iterator<String> names = userBox.keySet().iterator();
            while (names.hasNext()) {
                //Set集合内获取的某一个人名,用人名确定对象
                String name = names.next();
                //获取对象,记录一个人的真实数据
                User user = userBox.get(name);
                //将user的真实数据拼凑成一行字符串
                StringBuilder builder = new StringBuilder(user.getUsername());
                builder.append("-");
                builder.append(user.getPassword());
                builder.append("-");
                builder.append(user.getUserBalance());
                //将字符串在重写写入文件里,字符型文件输出流将拼接好的builder写入文件
                //write参数是String的,builder转为字符串
                bufferedWriter.write(builder.toString());
                //换行
                bufferedWriter.newLine();
                bufferedWriter.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭字符型输出流
            try {
                if (fileWriter != null) {
                    fileWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            //关闭字符型输出缓存流
            try {
                if (bufferedWriter != null) {
                    bufferedWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
​
    }
​
    /**
     * @Description: TODO 取款
     * 与存款一样就是加减的问题
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:20
     * @Param: [username, withdrawalMoney] 账户 取出的金额
     * @Return: void
     */
    public void withdrawal(String username, Float withdrawalMoney) {
        //找到User对象,修改对象中的balance属性
        User user = this.selectOne(username);
        //如果账户的金额大于取出的金额就可以取
        if (user.getUserBalance() > withdrawalMoney) {
            //将原有的金额 减去 取出的金额
            user.setUserBalance(user.getUserBalance() - withdrawalMoney);
            //将修改后的对象数据,在存入Map集合中
            this.update(user);
            //当修改以后就可以将修改后的对象数据全部写入文件中
            //将临时的数据永久的写入文件
            this.commit();
        } else {
            System.out.println("对不起," + username + "您的账户余额不足!");
        }
​
    }
​
    /**
     * @Description: TODO 转账
     * 转账就是先存后取
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:20
     * @Param: [outName, inName, transferMoney] 转出账户,转入账户,转账金额
     * @Return: void
     */
    public void transfer(String outName, String inName, Float transferMoney) {
        //找到User对象,修改对象中的balance属性
        User outUser = this.selectOne(outName);
        //如果转出用户的金额大于转账金额就可以转
        if (outUser.getUserBalance() > transferMoney) {
            //查询收账用户,查询到底转账的给哪个用户
            User inUser = this.selectOne(inName);
            //收账的用户不为空,说明收账用户存在
            if (inUser != null) {
                //转出用户的余额 - 传出的钱
                outUser.setUserBalance(outUser.getUserBalance() - transferMoney);
                //收账用户的余额 + 上面转出的钱
                inUser.setUserBalance(inUser.getUserBalance() + transferMoney);
                //将修改后的对象数据,在存入Map集合中
                this.update(outUser);
                this.update(inUser);
                //当修改以后就可以将修改后的对象数据全部写入文件中
                //将临时的数据永久的写入文件
                this.commit();
            } else {
                System.out.println("对不起,您输入的" + inName + "账户不存在!");
            }
​
        } else {
            System.out.println("对不起," + outName + "您的账户余额不足!");
        }
​
    }
}
package atmsystem;
​
import java.io.Serializable;
​
/**
 * @Description: TODO User 记录数据库中的一行信息
 *                  把账户-密码-余额以对象的形式包起来
 *                  继承Serializable接口
 * @Author: 曹宇希
 * @Date: 2023/11/8 13:51
 * @Version: 1.0
 * @Company: 版权所有
 */
public class User implements Serializable {
    /**
     * @Description: TODO 版本号
     * @Author: 曹宇希
     * @Date: 2023/11/8 14:35
     */
    private static final long serialVersionUID = -322964954562611312L;
    /**
     * @Descript
     * @Author: 曹宇希
     * @Date: 2023/11/8 14:03 
     */  
    private String username;
    /**
     * @Description: TODO 用户密码  
     * @Author: 曹宇希
     * @Date: 2023/11/8 14:03 
     */
    private String password;
    /**
     * @Description: TODO 用户余额  
     * @Author: 曹宇希
     * @Date: 2023/11/8 14:04 
     */
    private Float userBalance;
​
    /**
     * @Description: TODO 构造方法
     * @Author: 曹宇希
     * @Date: 2023/11/8 14:10
     * @Param: [username, password, userBalance]
     * @Return:
     */
    public User(){}
    public User(String username, String password, Float userBalance) {
        this.username = username;
        this.password = password;
        this.userBalance = userBalance;
    }
    /**
     * @Description: TODO  以下是get,set方法
     * @Author: 曹宇希
     * @Date: 2023/11/8 15:24
     * @Param: []
     * @Return: java.lang.String
     */
​
    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;
    }
​
    public Float getUserBalance() {
        return userBalance;
    }
​
    public void setUserBalance(Float userBalance) {
        this.userBalance = userBalance;
    }
}

33.4 如何分类

1、我们写Test类,这个类中是银行的整体操作,包含数据的操作,业务逻辑的判断

2、我们发现这个数据的操作 和 业务逻辑是两个不同的模块,我们要实现每一个类只干一件事,这样才能体现出面向对象的思想

3、创建AtmDao类,用于存放数据的所有操作,首先Test类中:程序块缓存、selectOne方法查询缓存中的对象、update将一个修改完毕的对象在存入集合、commit方法将修改后的对象数据全部写入文件中;都是操作数据的,所以将这些都移入AtmDao类

这个DAO层属于---持久层,专门操作数据的层

4、这样Test类只负责业务上的逻辑,而DAO层AtmDao类只负责操作数据

网络银行的业务,所有的业务方法,按照我们之前的优化结构设计,以下只有业务逻辑,判断、比较、
计算等等,看不到任何数据的操作(从哪查,从哪存都看不见),横线以上都是对数据的真实操作,所以
这两个应该是放在不同的类中
package atmsystem;
​
import java.io.*;
import java.util.HashMap;
import java.util.Iterator;
​
/**
 * @Description: TODO 操作数据
 * DAO作为层次--持久层,为了操作数据,只负责读写数据
 * @Author: 曹宇希
 * @Date: 2023/11/9 10:44
 * @Version: 1.0
 * @Company: 版权所有
 */
public class AtmDao {
    /**
     * @Description: TODO Map集合充当缓存,存储人当中的所有数据
     *                  String每个人的用户名不同(key) User对象存用户数据(value)
     *登录方法,每一次调用都创建流管道,读取文件的信息很慢,利用Map集合充当缓存,将文件中的数据全部读取出来
     * @Author: 曹宇希
     * @Date: 2023/11/7 20:34
     */
    private HashMap<String, User> userBox = new HashMap<String, User>();
​
    /**
     * @Description: TODO 程序块给Map存用户数据
     * 程序块目的在对象创建之前,给Map集合进行赋值操作,将数据存入缓存中
     * @Author: 曹宇希
     * @Date: 2023/11/8 14:59
     */
    {
        FileReader fileReader = null;
        BufferedReader bufferedReader = null;
        try{
            //创建一个字符型输入流,读取真实文件的记录
            //file采取的是相对路径,会自动找到当前项目的跟目录,src前面是当前工程的跟目录D:\IDEA.....\src...
            File file = new File("src\\atmsystem\\User.txt");
            fileReader = new FileReader(file);
            bufferedReader = new BufferedReader(fileReader);
            //读取一行数据信息,value表示一行人的信息
            String value = bufferedReader.readLine();
            //如何数据库不为空,就一致读到为空为止
            while (value != null) {
                //将value的信息拆分成三段
                String[] userValue = value.split("-");
                //构建一个User对象,存储三段信息(三个属性刚好存储), User构造方法中第三个参数(第三个属性)是Float类型的
                User user =  new User(userValue[0], userValue[1], Float.parseFloat(userValue[2]));
                //将对象存入Map集合<账户,User对象>
                userBox.put(userValue[0],user);
                //再次读取
                value = bufferedReader.readLine();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //fileReader关闭字符型输入流
            try {
                if (fileReader != null) {
                    fileReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            //bufferedReader关闭字符型缓存流
            try{
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
​
    /**
     * @Description: TODO   查询缓存中的对象信息
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:41
     * @Param: [username] 账户
     * @Return: atmsystem.User 对象
     */
    public User selectOne(String username) {
        //这里是一行,以后如果不是一行的时候,只需要改变这个方法就可以了
        return userBox.get(username);
    }
​
    /**
     * @Description: TODO 将一个修改完毕的对象在存入集合
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:51
     * @Param: [user]
     * @Return: void
     */
    public void update(User user) {
        //集合做了修改
        userBox.put(user.getUsername(), user);
    }
​
    /**
     * @Description: TODO 将修改后的对象数据全部写入文件中
     * HashMap<String, User>
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:57
     * @Param: []
     * @Return: void
     */
    public void commit() {
        FileWriter fileWriter = null;
        BufferedWriter bufferedWriter = null;
        try {
            //创建一个字符型文件输出流
            File file = new File("");
            fileWriter = new FileWriter(file);
            bufferedWriter = new BufferedWriter(fileWriter);
            //遍历集合,因为Map的集合key是不一样的,userBox.keySet()返回值是Set集合所以用迭代器
            Iterator<String> names = userBox.keySet().iterator();
            while (names.hasNext()) {
                //Set集合内获取的某一个人名,用人名确定对象
                String name = names.next();
                //获取对象,记录一个人的真实数据
                User user = userBox.get(name);
                //将user的真实数据拼凑成一行字符串
                StringBuilder builder = new StringBuilder(user.getUsername());
                builder.append("-");
                builder.append(user.getPassword());
                builder.append("-");
                builder.append(user.getUserBalance());
                //将字符串在重写写入文件里,字符型文件输出流将拼接好的builder写入文件
                //write参数是String的,builder转为字符串
                bufferedWriter.write(builder.toString());
                //换行
                bufferedWriter.newLine();
                bufferedWriter.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭字符型输出流
            try {
                if (fileWriter != null) {
                    fileWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            //关闭字符型输出缓存流
            try {
                if (bufferedWriter != null) {
                    bufferedWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
​
    }
}

1、我们发现这个DAO层中,虽然都是操作数据的,但是它也分操作集合,和操作File流

2、所以我们还可以将其分类,创建FileLoaderAndCommit类用于操作File流,将跟流相关的东西全部移入此类中

3、将程序块输入流将信息写入Map集合,commit方法将修改后的数据对象在次装入文件中,将这两个存入此类

package atmsystem;
​
import java.util.HashMap;
​
/**
 * @Description: TODO 操作数据
 * DAO作为层次--持久层,为了操作数据,只负责读写数据
 * @Author: 曹宇希
 * @Date: 2023/11/9 10:44
 * @Version: 1.0
 * @Company: 版权所有
 */
public class AtmDao {
    /**
     * @Description: TODO Map集合充当缓存,存储人当中的所有数据
     *                  String每个人的用户名不同(key) User对象存用户数据(value)
     *登录方法,每一次调用都创建流管道,读取文件的信息很慢,利用Map集合充当缓存,将文件中的数据全部读取出来
     * @Author: 曹宇希
     * @Date: 2023/11/7 20:34
     */
    private HashMap<String, User> userBox = new HashMap<String, User>();
​
    /**
     * @Description: TODO   查询缓存中的对象信息
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:41
     * @Param: [username] 账户
     * @Return: atmsystem.User 对象
     */
    public User selectOne(String username) {
        //这里是一行,以后如果不是一行的时候,只需要改变这个方法就可以了
        return userBox.get(username);
    }
​
    /**
     * @Description: TODO 将一个修改完毕的对象在存入集合
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:51
     * @Param: [user]
     * @Return: void
     */
    public void update(User user) {
        //集合做了修改
        userBox.put(user.getUsername(), user);
    }
​
​
}

1、此时FileLoaderAndCommit类中有程序块,将程序块写出方法,程序块目的在对象创建之前,给Map集合进行赋值操作,将数据存入缓存中,所以设计loadFile方法,参数:void 返回值HashMap<String, User>集合

2、commit提交修改后的对象数据全部写入文件中,也设计成含参的,参数HashMap<String ,User> userBox

package atmsystem;
​
import java.io.*;
import java.util.HashMap;
import java.util.Iterator;
​
/**
 * @Description: TODO 操作File流
 * @Author: 曹宇希
 * @Date: 2023/11/9 10:49
 * @Version: 1.0
 * @Company: 版权所有
 */
public class FileLoaderAndCommit {
    /**
     * @Description: TODO 利用输入流程序块给Map存用户数据
     * 程序块目的在对象创建之前,给Map集合进行赋值操作,将数据存入缓存中
     * @Author: 曹宇希
     * @Date: 2023/11/8 14:59
     */
    public HashMap<String, User> loadFile(){
        HashMap<String, User> userBox = new HashMap<String, User>();
        FileReader fileReader = null;
        BufferedReader bufferedReader = null;
        try{
            //创建一个字符型输入流,读取真实文件的记录
            //file采取的是相对路径,会自动找到当前项目的跟目录,src前面是当前工程的跟目录D:\IDEA.....\src...
            File file = new File("src\\atmsystem\\User.txt");
            fileReader = new FileReader(file);
            bufferedReader = new BufferedReader(fileReader);
            //读取一行数据信息,value表示一行人的信息
            String value = bufferedReader.readLine();
            //如何数据库不为空,就一致读到为空为止
            while (value != null) {
                //将value的信息拆分成三段
                String[] userValue = value.split("-");
                //构建一个User对象,存储三段信息(三个属性刚好存储), User构造方法中第三个参数(第三个属性)是Float类型的
                User user =  new User(userValue[0], userValue[1], Float.parseFloat(userValue[2]));
                //将对象存入Map集合<账户,User对象>
                userBox.put(userValue[0],user);
                //再次读取
                value = bufferedReader.readLine();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //fileReader关闭字符型输入流
            try {
                if (fileReader != null) {
                    fileReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            //bufferedReader关闭字符型缓存流
            try{
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return userBox;
    }
​
    /**
     * @Description: TODO 将修改后的对象数据全部写入文件中
     * HashMap<String, User>
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:57
     * @Param: []
     * @Return: void
     */
    public void commit(HashMap<String, User> userBox) {
        FileWriter fileWriter = null;
        BufferedWriter bufferedWriter = null;
        try {
            //创建一个字符型文件输出流
            File file = new File("");
            fileWriter = new FileWriter(file);
            bufferedWriter = new BufferedWriter(fileWriter);
            //遍历集合,因为Map的集合key是不一样的,userBox.keySet()返回值是Set集合所以用迭代器
            Iterator<String> names = userBox.keySet().iterator();
            while (names.hasNext()) {
                //Set集合内获取的某一个人名,用人名确定对象
                String name = names.next();
                //获取对象,记录一个人的真实数据
                User user = userBox.get(name);
                //将user的真实数据拼凑成一行字符串
                StringBuilder builder = new StringBuilder(user.getUsername());
                builder.append("-");
                builder.append(user.getPassword());
                builder.append("-");
                builder.append(user.getUserBalance());
                //将字符串在重写写入文件里,字符型文件输出流将拼接好的builder写入文件
                //write参数是String的,builder转为字符串
                bufferedWriter.write(builder.toString());
                //换行
                bufferedWriter.newLine();
                bufferedWriter.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭字符型输出流
            try {
                if (fileWriter != null) {
                    fileWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            //关闭字符型输出缓存流
            try {
                if (bufferedWriter != null) {
                    bufferedWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
​
    }
}

1、此时Test类中只有,负责处理业务逻辑的方法,它需要数据结构的支持,所以可以添加一个属性private AtmDao dao = new AtmDao();,这个dao就是数据,然后Test类中的this就可以替换为dao.用它来调用关于数据操作的事情,

2、你会发现取款,转账,存款这些方法最后都会调用commit来进行提交修改后的数据写入到文件中,其实我们可以在dao中的Update中实现,就是在更新完数据就立马提交

3、回到dao层AtmDao类中,要添加一个负责加载文件,更新文件数据的属性,因为你File操作和数据操作分开了;private FileLoaderAndCommit fileLoaderAndCommit = new FileLoaderAndCommit("src\atmsystem\User.txt"); 这个输入流要传递路径,因为路径不是写死的,所以在FileLoaderAndCommit中添加路径属性,还有设置构造方法fileName;

4、回到AtmDao类中,属性流创建完成后,还要有一个userBox存读取出来的信息private HashMap<String, User> userBox = fileLoaderAndCommit.loadFile();

5、最后都整理完发现Test类不能叫这个名字了,改为AtmService

package atmsystem;
​
/**
 * @Description: TODO 模拟网络银行的业务
 * 这个类都是负责处理业务逻辑
 * @Author: 曹宇希
 * @Date: 2023/11/7 20:07
 * @Version: 1.0
 * @Company: 版权所有
 */
public class AtmService {
/*网络银行的业务,所有的业务方法,按照我们之前的优化结构设计,以下只有业务逻辑,判断、比较、
计算等等,看不到任何数据的操作(从哪查,从哪存都看不见),横线以上都是对数据的真实操作,所以
这两个应该是放在不同的类中*/
    /**
     * @Description: TODO 数据结构
     * @Author: 曹宇希
     * @Date: 2023/11/9 18:06 
     */
    private AtmDao dao = new AtmDao();
    /**
     * @Description: TODO 登录
     * @Author: 曹宇希
     * @Date: 2023/11/7 20:34
     * @Param: [username, password] 用户给的名字, 用户给的密码
     * @Return: java.lang.String 返回登录成功与否
     */
    public String login(String username, String password) {
        User user = dao.selectOne(username);
        if (user != null && user.getPassword().equals(password)) {
            return "登录成功!";
        }
        return "密码或用户名错误!";
    }
​
    /**
     * @Description: TODO 查询余额
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:17
     * @Param: [username] 账户
     * @Return: java.lang.Float
     */
    public Float queryBalance(String username) {
        User user = dao.selectOne(username);
        return user.getUserBalance();
    }
​
    /**
     * @Description: TODO 存款
     * 我们只做业务,至于怎么查出来的,怎么存入的不用管
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:20
     * @Param: [username, depositMoney] 账户 存入的金额
     * @Return: void
     */
    public void deposit(String username, Float depositMoney) {
        //找到User对象,修改对象中的balance属性
        User user = dao.selectOne(username);
        //将原有的金额 加上 存入的金额
        user.setUserBalance(user.getUserBalance() + depositMoney);
        //将修改后的对象数据,在存入Map集合中
        dao.update(user);
        //当修改以后就可以将修改后的对象数据全部写入文件中
        //将临时的数据永久的写入文件
        //dao.commit();
        //commit在流类,所以我们给提交设置为自动提交,也就是说在AtmDao类中的update中提交
    }
​
    /**
     * @Description: TODO 取款
     * 与存款一样就是加减的问题
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:20
     * @Param: [username, withdrawalMoney] 账户 取出的金额
     * @Return: void
     */
    public void withdrawal(String username, Float withdrawalMoney) {
        //找到User对象,修改对象中的balance属性
        User user = dao.selectOne(username);
        //如果账户的金额大于取出的金额就可以取
        if (user.getUserBalance() > withdrawalMoney) {
            //将原有的金额 减去 取出的金额
            user.setUserBalance(user.getUserBalance() - withdrawalMoney);
            //将修改后的对象数据,在存入Map集合中
            dao.update(user);
            //当修改以后就可以将修改后的对象数据全部写入文件中
            //将临时的数据永久的写入文件
        } else {
            System.out.println("对不起," + username + "您的账户余额不足!");
        }
​
    }
​
    /**
     * @Description: TODO 转账
     * 转账就是先存后取
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:20
     * @Param: [outName, inName, transferMoney] 转出账户,转入账户,转账金额
     * @Return: void
     */
    public void transfer(String outName, String inName, Float transferMoney) {
        //找到User对象,修改对象中的balance属性
        User outUser = dao.selectOne(outName);
        //如果转出用户的金额大于转账金额就可以转
        if (outUser.getUserBalance() > transferMoney) {
            //查询收账用户,查询到底转账的给哪个用户
            User inUser = dao.selectOne(inName);
            //收账的用户不为空,说明收账用户存在
            if (inUser != null) {
                //转出用户的余额 - 传出的钱
                outUser.setUserBalance(outUser.getUserBalance() - transferMoney);
                //收账用户的余额 + 上面转出的钱
                inUser.setUserBalance(inUser.getUserBalance() + transferMoney);
                //将修改后的对象数据,在存入Map集合中
                dao.update(outUser);
                dao.update(inUser);
                //当修改以后就可以将修改后的对象数据全部写入文件中
                //将临时的数据永久的写入文件
            } else {
                System.out.println("对不起,您输入的" + inName + "账户不存在!");
            }
​
        } else {
            System.out.println("对不起," + outName + "您的账户余额不足!");
        }
​
    }
}
package atmsystem;
​
import java.util.HashMap;
​
/**
 * @Description: TODO 操作数据
 * DAO作为层次--持久层,为了操作数据,只负责读写数据
 * @Author: 曹宇希
 * @Date: 2023/11/9 10:44
 * @Version: 1.0
 * @Company: 版权所有
 */
public class AtmDao {
    /**
     * @Description: TODO 负责加载文件,更新文件数据
     * @Author: 曹宇希
     * @Date: 2023/11/9 18:11
     */
    private FileLoaderAndCommit fileLoaderAndCommit = new FileLoaderAndCommit("src\\atmsystem\\User.txt");
    /**
     * @Description: TODO Map集合充当缓存,存储人当中的所有数据
     *                  String每个人的用户名不同(key) User对象存用户数据(value)
     *登录方法,每一次调用都创建流管道,读取文件的信息很慢,利用Map集合充当缓存,将文件中的数据全部读取出来
     * @Author: 曹宇希
     * @Date: 2023/11/7 20:34
     */
    private HashMap<String, User> userBox = fileLoaderAndCommit.loadFile();
​
​
    /**
     * @Description: TODO   查询缓存中的对象信息
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:41
     * @Param: [username] 账户
     * @Return: atmsystem.User 对象
     */
    public User selectOne(String username) {
        //这里是一行,以后如果不是一行的时候,只需要改变这个方法就可以了
        return userBox.get(username);
    }
​
    /**
     * @Description: TODO 将一个修改完毕的对象在存入集合
     * @Author: 曹宇希
     * @Date: 2023/11/8 16:51
     * @Param: [user]
     * @Return: void
     */
    public void update(User user) {
        //集合做了修改
        userBox.put(user.getUsername(), user);
        //自动提交修改后的数据
        fileLoaderAndCommit.commit(userBox);
    }
​
​
}
package atmsystem;
​
import java.util.Scanner;
​
/**
 * @Description: TODO 测试写的方法是否有问题
 * @Author: 曹宇希
 * @Date: 2023/11/7 21:15
 * @Version: 1.0
 * @Company: 版权所有
 */
public class TestMain {
    public static void main(String[] args) {
        AtmService service = new AtmService();
        Scanner input = new Scanner(System.in);
        System.out.println("欢迎您使用银行自助服务程序,请输入账户:\n");
        String username = input.nextLine();
        System.out.println("请输入密码:");
        String password = input.nextLine();
        String loginResult = service.login(username, password);
        System.out.println(loginResult);
        if ("登录成功!".equals(loginResult)) {
            System.out.println("恭喜您登录成功,请输入操作项");
            System.out.println("查询输入1\n存款输入2\n取款输入3\n转账输入4\n退出输入5\n");
            String option = input.nextLine();
            switch (option) {
                case "1":
                    Float userBalance = service.queryBalance(username);
                    System.out.println("尊敬的" + username + "用户,您的余额为:" + userBalance);
                break;
                default:
                    System.out.println("您是输入的数字不匹配!");
            }
        } else {
            System.out.println("对不起," + loginResult);
        }
    }
​
}
package atmsystem;
​
import java.util.Scanner;
​
/**
 * @Description: TODO 测试写的方法是否有问题
 * @Author: 曹宇希
 * @Date: 2023/11/7 21:15
 * @Version: 1.0
 * @Company: 版权所有
 */
public class TestMain {
    public static void main(String[] args) {
        AtmService service = new AtmService();
        Scanner input = new Scanner(System.in);
        System.out.println("欢迎您使用银行自助服务程序,请输入账户:\n");
        String username = input.nextLine();
        System.out.println("请输入密码:");
        String password = input.nextLine();
        String loginResult = service.login(username, password);
        System.out.println(loginResult);
        if ("登录成功!".equals(loginResult)) {
            System.out.println("恭喜您登录成功,请输入操作项");
            System.out.println("查询输入1\n存款输入2\n取款输入3\n转账输入4\n退出输入5\n");
            String option = input.nextLine();
            switch (option) {
                case "1":
                    Float userBalance = service.queryBalance(username);
                    System.out.println("尊敬的" + username + "用户,您的余额为:" + userBalance);
                break;
                default:
                    System.out.println("您是输入的数字不匹配!");
            }
        } else {
            System.out.println("对不起," + loginResult);
        }
    }
​
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

A五花肉~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值