java某新手的项目的一些细节以及多线程2

文章讲述了在Java项目中实现用户和管理者登录功能,使用FileOutputStream将数据保存至文件。管理者需输入创建码才能创建账户,以确保安全性。同时,讨论了多线程的概念,包括进程和线程的区别,并展示了两种实现多线程的方法,强调了线程安全问题,如使用`synchronized`关键字处理并发访问资源的情况。最后,提到了程序的优化点,如避免冗余文件操作和增加商品库存管理。
摘要由CSDN通过智能技术生成

一级标题

二级标题

三级标题

四级标题
五级标题
六级标题

本次要讲的是java项目中的一些小细节,以及多线程

那么我们在做一个简易的商品管理系统的时候,我们需要实现使用者和管理者的功能不同,但是登录基本相同

他们大体相同的地方是在登录上

为什么说大体相同,等会揭晓答案

已经注册过的可以直接登录,未注册过的先注册,这是基本功能

但是在如何保存注册账号上我用FileOutputStream写到文件上去的,但是注册管理者账号和使用者有一点不同,就是管理者需要输入一个创建码来创建

如果人人都可以创建管理者账户岂不是很危险,所以我特地设置了一个创建码,只有创建码输入正确才能创建管理者账户,不然风险太大了

请看大致代码和效果

public static void addcontrol(ArrayList<Sell> array) {
        String id;
        String select;
        String name;
        String protect;
        Sell s = new Sell();
        String src = "D:/zhanghao.txt";
        File file = new File(src);
        FileOutputStream fis = null;
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入您的昵称");
        name = sc.nextLine();
        System.out.println("请输入您的手机号");
        id = sc.nextLine();
        System.out.println("请输入您的密码");
        select = sc.nextLine();
        while (true) {
            System.out.println("请输入您的创建码");
            protect = sc.nextLine();
            if (s.getControl().equals(protect)) {
                s.setUsername(name);
                s.setId(id);
                s.setSelect(select);
                array.add(s);
                System.out.println("创建成功!");
                try {
                    fis = new FileOutputStream(file);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
                try {
                    fis.write((s.getUsername()+","+ s.getId()+","+s.getSelect()).getBytes());
                } catch (IOException e) {
                    e.printStackTrace();
                }
                break;
            } else {
                System.out.println("创建失败!请重试,创建码错误");
            }
        }
    }

让我们来康康效果

在这里插入图片描述

保存成功,所以说保存方法可行

那么,如果说你早就注册好了账号,如何直接使用呢?

我先把文件里面所有账号全都读取了出来放在集合里面,然后我再输入我的账号密码比对就可以直接登录,可以看看演示

这是我在文件中已经加上去的账号

在这里插入图片描述

在这里插入图片描述

可以看到,成功

(该账号仅做演示使用,并非实用账号)

这里我也终于实现增删改查都可以存进文件里了,所有功能都可以使用了

在以前,其实顾客的查询购买是一直不能使用的,因为没有存商品进去,实在太好惹!

现在终于可以实现了,而且我发现,其实可以用Scanner

Scanner是一个扫描程序,我们是不是一般用它来new对象扫描输入?让我们看看它的源码

所以说,我们指定文件让它扫描,也和指定扫描从键盘输入的字符是一样的操作,然后将扫描结果保存在集合里是可行的

public Scanner(InputStream source) {
        this(new InputStreamReader(source), WHITESPACE_PATTERN);
    }
Constructs a new Scanner that produces values scanned from the specified input stream. Bytes from the stream are converted into characters using the underlying platform's default charset.
构造一个新的扫描程序,该扫描程序生成从指定的输入流扫描的值。流中的字节使用基础平台的默认字符集转换为字符。

这里是这个操作里面最值得讲的地方,真的很好用
这样一来,我们便不用再学习很多看似高大上实则让人忘记本来的操作,很容易就可以读取文件了。

自我感受:把自己的项目搞的好不容易不那么像一坨答辩,昨天改的时候我都嘲讽自己在吃屎,起码是功能都能用了,但是还是有很多安全问题,比如说,你的账号密码如果像演示的那样都是1,1,那不是非常容易被盗号,还有就是我有一个地方,其实没做处理,如果说我已经有一个1账号了,我再注册一个,就会重复,但是我没做重复处理,这样就很糟糕,因为大概率会埋下bug的火种,因为程序弄不清你是哪个账号,很可能会出现错误登录

错误登录是一件非常糟糕的事情,就像你登你QQ突然登到别人QQ上去了的感觉

缺陷

1.我其实感觉我的程序里面有些地方,比如说改和删,都要先把文件的东西保存下来,在集合里面改了之后我再把集合里面的东西再传一遍文件,感觉很耗时间,但是我目前没有想到能快速解决的办法,唉QWQ

2.然后我感觉我的程序里面没有商品的库存个数,这样不好,我估计得加个属性了

3.没有商品个数的话就不好加多线程,所以说,必须要搞商品的库存个数了,我要把这坨屎舔干净了之后,这坨屎还没舔干净

好的,现在我们来稍微讲一下多线程

因为大体说了多线程,所以说我们讲一下上次没有详细讲的

进程:
电脑中时会有很多单独运行的程序,每个程序有一个独立的进程,而进程之间是相互独立存在的。
线程:
进程中的最小执行单位就是线程,并且一个进程中至少有一个线程。
(java中的main方法也叫作主线程)

这里来讲讲两种实现多线程的方法

编写一个线程类,直接 继承 java.lang.Thread,重写 run 方法。
用 strat() 启动线程。

看看代码

package org.example;

public class Test {
    public static void main(String[] args) {
        Tickets t = new Tickets(100);
        t.start();
        while(t.getNum()> 0){
            t.setName("分支线程");
            t.sale();

        }
    }
}


package org.example;

public class Tickets extends Thread{
    private int num;
    private String name;


    public Tickets(int num) {
        this.num = num;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public void sale(){
        try{
            Thread.sleep(100);
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"正在出售第 "+this.num +"张票,"+"剩余票数:"+--num);
    }
    public void run(){
        if(this.num > 0){
            sale();
        }
    }
}

但是吧,我们一般不用这种方法,因为java里只能单继承
所以我们一般用第二种方法实现多线程
编写一个类,实现 java.lang.Runnable 接口,实现run方法

先来看看它的源码

public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}
当使用实现接口 Runnable 的对象创建线程时,启动线程会导致在该单独执行的线程中调用对象的 run 方法。
方法运行的一般约定是它可以采取任何操作。

我们来看看代码

package org.example;

public class Test {
    public static void main(String[] args) {
        Tickets tickets = new Tickets(100);
        Thread t1 = new Thread(new Service(tickets),"窗口1");
        t1.start();
        Thread t2 = new Thread(new Service(tickets),"窗口2");
        t2.start();
        Thread t3 = new Thread(new Service(tickets),"窗口3");
        t3.start();
    }
}
package org.example;

public class Service implements Runnable {
    private Tickets tickets;

    public Service(Tickets tickets) {
        this.tickets = tickets;
    }

    public void run() {
        while (tickets.getNum() > 0) {
            sale();
        }
    }

    public void sale() {
        synchronized (tickets) {
            int num = tickets.getNum();
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (num > 0) {
                System.out.println(Thread.currentThread().getName() + "正在卖第 " + tickets.getNum() + "张票" + --num);
                tickets.setNum(num);
            }
        }
    }
}

package org.example;

public class Tickets extends Thread{
    private int num;
    private String name;


    public Tickets(int num) {
        this.num = num;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }
}

窗口1正在卖第 100张票99
窗口1正在卖第 99张票98
窗口1正在卖第 98张票97
窗口1正在卖第 97张票96
窗口1正在卖第 96张票95
窗口1正在卖第 95张票94
窗口1正在卖第 94张票93
窗口1正在卖第 93张票92
窗口1正在卖第 92张票91
窗口1正在卖第 91张票90
窗口1正在卖第 90张票89
窗口1正在卖第 89张票88
窗口1正在卖第 88张票87
窗口1正在卖第 87张票86
窗口2正在卖第 86张票85
窗口2正在卖第 85张票84
窗口2正在卖第 84张票83
窗口2正在卖第 83张票82
窗口2正在卖第 82张票81
窗口2正在卖第 81张票80
窗口2正在卖第 80张票79
窗口2正在卖第 79张票78
窗口2正在卖第 78张票77
窗口2正在卖第 77张票76
窗口2正在卖第 76张票75
窗口2正在卖第 75张票74
窗口2正在卖第 74张票73
窗口2正在卖第 73张票72
窗口2正在卖第 72张票71
窗口2正在卖第 71张票70
窗口2正在卖第 70张票69
窗口2正在卖第 69张票68
窗口2正在卖第 68张票67
窗口2正在卖第 67张票66
窗口2正在卖第 66张票65
窗口2正在卖第 65张票64
窗口2正在卖第 64张票63
窗口2正在卖第 63张票62
窗口2正在卖第 62张票61
窗口2正在卖第 61张票60
窗口2正在卖第 60张票59
窗口2正在卖第 59张票58
窗口2正在卖第 58张票57
窗口2正在卖第 57张票56
窗口2正在卖第 56张票55
窗口2正在卖第 55张票54
窗口2正在卖第 54张票53
窗口2正在卖第 53张票52
窗口2正在卖第 52张票51
窗口2正在卖第 51张票50
窗口2正在卖第 50张票49
窗口2正在卖第 49张票48
窗口2正在卖第 48张票47
窗口2正在卖第 47张票46
窗口3正在卖第 46张票45
窗口3正在卖第 45张票44
窗口3正在卖第 44张票43
窗口3正在卖第 43张票42
窗口3正在卖第 42张票41
窗口3正在卖第 41张票40
窗口3正在卖第 40张票39
窗口3正在卖第 39张票38
窗口3正在卖第 38张票37
窗口3正在卖第 37张票36
窗口3正在卖第 36张票35
窗口3正在卖第 35张票34
窗口3正在卖第 34张票33
窗口3正在卖第 33张票32
窗口3正在卖第 32张票31
窗口3正在卖第 31张票30
窗口3正在卖第 30张票29
窗口3正在卖第 29张票28
窗口3正在卖第 28张票27
窗口3正在卖第 27张票26
窗口3正在卖第 26张票25
窗口3正在卖第 25张票24
窗口3正在卖第 24张票23
窗口3正在卖第 23张票22
窗口3正在卖第 22张票21
窗口3正在卖第 21张票20
窗口3正在卖第 20张票19
窗口3正在卖第 19张票18
窗口3正在卖第 18张票17
窗口3正在卖第 17张票16
窗口3正在卖第 16张票15
窗口3正在卖第 15张票14
窗口3正在卖第 14张票13
窗口3正在卖第 13张票12
窗口3正在卖第 12张票11
窗口3正在卖第 11张票10
窗口3正在卖第 10张票9
窗口3正在卖第 9张票8
窗口3正在卖第 8张票7
窗口3正在卖第 7张票6
窗口3正在卖第 6张票5
窗口3正在卖第 5张票4
窗口3正在卖第 4张票3
窗口3正在卖第 3张票2
窗口3正在卖第 2张票1
窗口3正在卖第 1张票0

然后还有一个中止线程的方法,不建议用stop容易丢失运行的数据,可以弄一个信号,类似flag的东西,所以说我们可以先设置一个boolean类型的数,等到要停止的时候,改变它的值,它就知道在那里要停下了


好的,这里有个线程锁比较重要

`synchronized `关键字:

代表这个方法加锁,相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法(或者该类的其他同步方法),有的话要等正在使用synchronized方法的线程B(或者C 、D)运行完这个方法后再运行此线程A,没有的话,锁定调用者,然后直接运行。



然后还有一个中止线程的方法,不建议用`stop`容易丢失运行的数据,可以弄一个信号,类似`flag`的东西,所以说我们可以先设置一个`boolean`类型的数,等到要停止的时候,改变它的值,它就知道在那里要停下了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值