IO流、多线程学习

系列文章目录

JavaSE
基础知识、数据类型学习万年历项目代码逻辑训练习题
代码逻辑训练习题方法、数组学习图书管理系统项目
面向对象编程:封装、继承、多态学习封装继承多态习题常用类、包装类、异常处理机制学习
集合学习IO流、多线程学习仓库管理系统JavaSE项目
员工管理系统、多表查询、反射实现DBHelper学习DML、DDL、数据库对象学习
JavaWeb
网络编程、各种标签、CSS学习ECMAScript、BOM学习DOM、jQuery学习
Servlet、JSP、Cookie、Ajax学习融资管理系统JavaWeb项目
框架
MyBatis框架学习逆向工程、Spring框架IOC、AOP学习SpringMVC框架学习
SpringBoot框架学习招聘网站框架项目Vue介绍、窗体内操作、窗体间操作学习
Vue路由配置、网络请求访问框架项目、element组件介绍学习标准管理系统Vue项目
微服务
Linux安装、Nginx反向代理、负载均衡学习Docker学习Jenkins学习
Nexus学习Spring Security学习RabbitMQ学习
Redis学习MongoDB学习MongoDB学习
Nacos学习Spring Session学习Spring Gateway学习
JSR 303学习OpenFeign学习Hystrix学习


前言

本文我们将介绍IO流和多线程,IO流用对文件内容进行操作或对文件属性进行操作,多线程用于多个线程同时对数据进行处理。在下攸攸太上,背靠人山人海,享受海阔天空。


一、IO流

1. IO流介绍

I: input输入(从程序外输出到到程序里)
O: output输出(从程序里输出到到程序外)
流(通道): 输送方法

2. File工具类

1、分析参与者,涉及到文档对象。提取一个类,确定类名:File(文档类File类)
2、分析参与者的属性
3、分析参与者的行为(我们不太关注File的行为)

Windows系统的地址分隔符为\\,Linix系统的地址分隔符为/
File.separator:自动获得适合当前系统的分隔符

2.1 操作文件

常用方法代码:

File file = new File("D:" + File.separator + "for_codes\\for_java\\JiRuanJava\\240729_Demo1\\240729.txt");
        System.out.println("获得文件名:" + file.getName());
        System.out.println("判断文件是否存在:" + file.exists());
        System.out.println("判断文件是否是目录(文件夹)(返回true是文件夹):" + file.isDirectory());
        System.out.println("判断是否是文件:" + file.isFile());
        System.out.println("获得文件大小:" + file.length());
        //System.out.println("删除文件+判断:" + file.delete() + ":" + file.exists());
        System.out.println("判断上一级目录(文件夹):" + file.getParent());
        System.out.println("创建(需要throws IOException):" + file.createNewFile());
        System.out.println("是否隐藏:" + file.isHidden());
        System.out.println("文件的大小:" + file.length());
        System.out.println("是否存在:" + file.exists());
        System.out.println("文件是否可读:" + file.canRead());
        System.out.println("文件是否可写:" + file.canWrite());

运行结果:
在这里插入图片描述
相对路径:
相对路径:相对于当前代码文件位置的路径".\\“代表包含本文件的文件夹下的”..\\"代表包含包含本文件的文件夹的文件夹。
绝对路径:
从C盘开始往下捋,显示途中所有的文件夹

2.2 操作目录(文件夹)

2.2.1 创建删除目录

代码:

    public static void main(String[] args) throws IOException {
        new Test().getFileInfo("D:\\Program Files");
    }
    public void getFileInfo(String file){
        File file2 = new File(file);
        File[] files = file2.listFiles();
        for (File file3: files){
            if (file3.isDirectory()){
                getFileInfo(file3.getPath());
            }
            System.out.println(file3);
        }
    }

运行结果:
在这里插入图片描述
在这里插入图片描述
只删除了Three,多层删除需要循环

2.2.2 模拟杀毒软件(读取所有文件)

代码:

 new Test().getFileInfo("D:\\Program Files");
    }
    public void getFileInfo(String file){
        File file2 = new File(file);
        File[] files = file2.listFiles();
        for (File file3: files){
            if (file3.isDirectory()){
                getFileInfo(file3.getPath());
            }
            System.out.println(file3);
        }
    }

在这里插入图片描述
提示:递归占用内存,可能造成死循环,不好理解,一般不使用
File类提供的方法,都是用来获得文件/目录的基本信息,没有提供存储信息等操作文件内部信息的方法。
下面IO流可以操作文件内部信息。

3. IO流如何读写文件

IO流的分类:
根据流的方向:输入流;输出流
根据流承载信息量的大小:字节流(8位:处理不了汉字);字符流(16位)
根据流的功能:节点流(无其它功能,只管运送);处理流(有其他功能)
在这里插入图片描述

4. 使用IO流进行读写文件

4.1 挨个读取数据

代码:

	public static void main(String[] args) throws IOException {
        //1:获得信息来源对象(获得水龙头)
        File file = new File("D:\\for_codes\\for_java\\JiRuanJava\\240729_Demo1\\240729.txt");
        //2:创建IO流对象(拿出水管,连接水龙头)
        FileInputStream fileInputStream = new FileInputStream(file);
        //3:读取信息(洒水)
        int i = fileInputStream.read();
        while (i != -1){//没有下一个了read到的就是-1
            System.out.print((char)i);
            i = fileInputStream.read();//read每次只读一个字节,每次read自动跳到下一个
        }
        //4:关闭流资源对象
        fileInputStream.close();
    }

运行结果:
在这里插入图片描述

如果不进行赋值操作,那么内侧循环为:

        while (fileInputStream.read() != -1){//没有下一个了read到的就是-1
            System.out.print((char)fileInputStream.read());//read每次只读一个字节,每次read自动跳到下一个
        }

运行结果:
在这里插入图片描述
结果会跳一个读一个(因为每次read操作IO流都跳到下一个,我们循环条件里那个read也跳)

4.2 读取所有数据

因为硬盘不能多次读取,会更快的到寿命,为了不那么多次读取,我们使用一次读取多个的read()方法
也称为缓冲区
代码:

    public static void main(String[] args) throws IOException {
        File file = new File("D:\\for_codes\\for_java\\JiRuanJava\\240729_Demo1\\240729.txt");
        FileInputStream fileInputStream = new FileInputStream(file);
        byte[] bytes = new byte[1024];
        int i = fileInputStream.read(bytes);
        while (i != -1){
            a:for (byte b:bytes
                 ) {
                System.out.print((char)b);
                if ((char)b == 0)
                    break a;
            }
            i = fileInputStream.read();
            System.out.println(i);
        }
        fileInputStream.close();
    }

运行结果:
在这里插入图片描述

4.1 输入输出【复制】数据(图片)

代码:

    public static void main(String[] args) throws IOException {
        //图片视频音频都是用字节流
        //获得数据源对象
        File fileIn = new File("D:\\for_codes\\for_java\\JiRuanJava\\240729_Demo1\\PP.jpg");
        File fileOut = new File("D:\\for_codes\\for_java\\JiRuanJava\\240729_Demo1\\One\\Two\\PP.jpg");
        //获得流对象
        FileInputStream fileIS = new FileInputStream(fileIn);
        FileOutputStream fileOS = new FileOutputStream(fileOut);
        //读写信息
        byte[] bytes = new byte[1024];
        int i = fileIS.read(bytes);
        while (i != -1){
            fileOS.write(bytes);
            i = fileIS.read(bytes);
        }
        //关闭资源(后开先关)
        fileOS.close();
        fileIS.close();
        System.out.println("🆗");
    }

运行结果:
在这里插入图片描述
复制成功

5. 字节流字符流区别

5.1 字节流

代码:

    public static void main(String[] args) throws IOException {
        //字节输入流
        //获得数据源对象
        File fileIn = new File("D:\\for_codes\\for_java\\JiRuanJava\\240729_Demo1\\240729.txt");
        //获得流对象
        FileInputStream fileIS = new FileInputStream(fileIn);
        //读写信息
        byte[] bytes = new byte[1024];
        int i = fileIS.read(bytes);
        while (i != -1){
            a:for (byte b:bytes
            ) {
                System.out.print((char)b);
                if ((char)b == 0)
                    break a;
            }
            i = fileIS.read(bytes);
        }
        //关闭资源(后开先关)
        fileIS.close();
        System.out.println("🆗");
    }

运行结果:
在这里插入图片描述
原因: 汉字是16位的,读取八字节相当于只读取一半,所以出现乱码

5.2 字符流

5.2.1 字符流输入

代码:

    public static void main(String[] args) throws IOException {
        //字符输入流
        //获得数据源对象
        File fileIn = new File("D:\\for_codes\\for_java\\JiRuanJava\\240729_Demo1\\240729.txt");
        //获得流对象
        FileReader fileIS = new FileReader(fileIn);
        //读写信息
        char[] chars = new char[1024];//每次读取1024个字符
        int i = fileIS.read(chars);
        while (i != -1){
            a:for (char b:chars
            ) {
                System.out.print(b);
                if (b == 0)
                    break a;
            }
            i = fileIS.read(chars);
        }
        //关闭资源(后开先关)
        fileIS.close();
        System.out.println("ok");
    }

输出结果:
在这里插入图片描述

5.2.2 字符流输出

代码:

public static void main(String[] args) throws IOException {
        //获得数据源对象
        //模拟微信聊天信息存储功能
        File fileIn = new File("D:\\for_codes\\for_java\\JiRuanJava\\240729_Demo1\\240729.txt");
        FileWriter fileWriter = new FileWriter(fileIn, true);
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入想输入的信息,输入exit退出:");
        String str = scanner.next();
        while (!"exit".equals(str)){
            System.out.println("请输入想输入的信息,输入exit退出:");
            fileWriter.write("\n" + str);
            str = scanner.next();
        }
        fileWriter.close();
    }

输入:
在这里插入图片描述
输出结果:
在这里插入图片描述
以上介绍的所有流都是节点流

7. 处理流

7.1 处理流与节点流对比

处理流:有特殊功能;连接节点流对象
节点流:无特殊功能;直接连接信息源对象

7.2 处理流分类

缓冲流:自带缓冲区
转换流:字节流转换位字符流
对象流:内存对象存到文件中
数据流:在存取信息时,可以保留信息的类型
打印流:System.out.*下的对象

7.3 对象流(序列化和反序列化)

输入输出代码:

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //使用对象流:将内存信息读取到外部文件里称为序列化
        //外部文件内对象信息读取到内存中称为反序列化
        File file = new File("D:\\for_codes\\for_java\\JiRuanJava\\240729_Demo1\\obj.txt");
        //必须用字节
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        Student student = new Student(1, "李四",12,  "男");
        objectOutputStream.writeObject(student);
        //当有(同方向)多层流对象时,关闭最里层对象就可以
        objectOutputStream.close();

        FileInputStream fileInputStream = new FileInputStream(file);
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);

        Student student1 = (Student)objectInputStream.readObject();

        System.out.println(student1);
        objectInputStream.close();
    }

输出:
文件内:
在这里插入图片描述
idea输出:
在这里插入图片描述
为什么以字节流输出文件,为什么字节流能够成功获取汉字数据?

二、多线程

1. 线程介绍

程序:是为了完成特定任务,使用编程语言编写的一组指令的集合,是一段静态的代码。
进程:程序的执行过程。正在运行的程序,进程作为资源分配的单位。
线程:进程的细化,是一个程序内部的一条执行路径。

举例
单线程:一个工人造完车轮造车身,造完车身造车窗
多线程:一个工人多条流水线同时造车轮、车身、车窗,完成之后组装在一起

2. 并行、并发介绍

并行:多个CPU同时执行多个任务(一般电脑不支持)
并发:一个CPU“同时”执行多个任务(采用时间片切换,本文中主要使用并发)

3. 创建多线程的三种方式

3.1 第一种:继承Thread父类

线程类:只有线程类能开辟新的线程
main方法所在的线程为主线程
1、创建线程类
2、创建线程对象【新生状态】
3、线程对象.start()【就绪状态】
4、CPU给资源【运行状态】
如果调用run(),程序会以:一个线程结束后,开始另一个线程,所以跟单线程没差别
启动线程效率高,占用了父类的位置,资源需要加静态

售票例子:
有是张车票,三个窗口,同时售票。
售票窗口类:

public class SellerThread extends Thread {
    static int number = 10;


    @Override
    public void run() {
        while (number >= 1)
            System.out.println(getName() + "的窗口\t卖出第" + (11 - number--) + "票");
    }
}

主函数类:

    public static void main(String[] args) {
        SellerThread seller1 = new SellerThread();
        seller1.setName("麻辣张婆");
        seller1.start();
        SellerThread seller2 = new SellerThread();
        seller2.setName("沉默王爷");
        seller2.start();
        SellerThread seller3 = new SellerThread();
        seller3.setName("大嗓门张姨");
        seller3.start();
    }

运行结果:
在这里插入图片描述
可以看出所有的票都是乱七八糟的重复的, 这是因为我们一个线程还没做输出语句,执行流就被切换到下一个了

3.2 第二种:实现Runnable接口

启动线程对象效率低,没有占用父类位置,共享资源的能力强,不用加静态
Seller类:

public class Seller implements Runnable {
    int number = 10;
    @Override
    public void run() {
        while (number >= 1){
            System.out.println(Thread.currentThread().getName() + "的窗口\t卖出第" + (11 - number--) + "票");
        }
    }
}

主函数:

    public static void main(String[] args) {
        Seller seller1 = new Seller();
        Thread tSeller1 = new Thread(seller1, "麻辣张婆");
        tSeller1.start();
        Thread tSeller2 = new Thread(seller1, "沉默王爷");
        tSeller2.start();
        Thread tSeller3 = new Thread(seller1, "大嗓门张姨");
        tSeller3.start();
    }

运行结果:
在这里插入图片描述

3.3 第三种:实现Callable接口

run方法返回值为void,不能抛出异常,Callable接口能解决
Sell类:

public class Sell implements Callable {
    static int number = 100;
    @Override
    public Object call() throws Exception {
        while (number >= 1)
            System.out.println(Thread.currentThread().getName() + "的窗口\t卖出第" + (101 - number--) + "票");

        return 0;
    }
}

主函数:

    public static void main(String[] args) {
        Sell sell = new Sell();
        FutureTask futureTask = new FutureTask(sell);
        FutureTask futureTask1 = new FutureTask(sell);
        Thread thread = new Thread(futureTask);
        Thread thread1 = new Thread(futureTask1);
        thread.start();
        thread1.start();
    }

运行结果:
在这里插入图片描述

4. 常用方法

4.1 start()

启用当前线程,表面上叫start,其实不过是披着start皮的run

4.3 run()

run()方法里面是线程要执行的内容

4.3 getName()

对象.getName()获得当前线程对象的名字
如果开发者调用了无参构造器,程序自动设置线程名:Thread-0/1/2(主线程固定为main)
开发者使用有参构造器,参数的值就是线程的名字

4.4 setName()

开发者使用setName()方法对线程名赋值

4.5 Thread.currentThread()

获得当前正在运行的线程对象

4.6 setPriority()

改变该线程对象的优先级
代码:

    public static void main(String[] args) {
        Seller seller1 = new Seller();
        Thread tSeller1 = new Thread(seller1, "麻辣张婆");
        tSeller1.start();
        Thread tSeller2 = new Thread(seller1, "沉默王爷");
        tSeller2.setPriority(1);
        tSeller2.start();
        Thread tSeller3 = new Thread(seller1, "销售王张姨");
        tSeller3.setPriority(10);
        tSeller3.start();
    }

结果:
在这里插入图片描述

4.7 join()

一个线程调用了join()方法,这个线程会被执行,结束后才会去执行其他线程
start()方法后,join()才有效

4.8 sleep()

在被其他资源占用时,sleep的时间继续向下算吗?继续往下算,是实际时间的毫秒数,不是该线程在执行的时间片段相加

4.8 setDaemon()

主线程死亡,伴随线程也会跟着一起死亡(会有一些延迟)
NumberThread类

public class NumberThread extends Thread {
    @Override
    public void run() {//第二条执行路线的内容
        super.run();
        for (int i = 1; i < 1001; i++) {
            System.out.println(super.getName() + "\t\ti = " + i);
        }
    }
}

主函数:

    public static void main(String[] args) throws InterruptedException {
        NumberThread num = new NumberThread();
        num.setDaemon(true);//在start()方法前
        num.start();
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName() + "\t\ti = " + i);
        }
    }

结果:
在这里插入图片描述
之后就结束了

5. 多线程的生命周期

以方法作为临界点
在这里插入图片描述

1、使用线程构造器>创建线程对象>新生状态
2、创建线程对象.start()>进入就绪状态>有资格没资源
3、线程对象.run()>进入运行状态>有资格有资源
4、分三种情况
——情况1、时间片段内执行完,死亡状态
——情况2、时间片段内没执行完,重返就绪状态
——情况3、时间片段内出现突发事件,阻塞状态,突发事件处理完返回就绪状态

6. 同步

锁的范围要尽量小,不然可能出现死锁

6.1 同步代码块

在代码体里,把所有客官想同步的代码放到同步代码块里
Seller类:

public class Seller implements Runnable {
    int number = 1000;
    @Override
    public void run() {
        while (true){
            synchronized (Seller.class){//必须是多个线程用的是同一把锁(同步监视器)引用数据类型
                if (number >= 1)
                    System.out.println(Thread.currentThread().getName() + "的窗口\t卖出第" + (1001 - number--) + "票");
            }
            if (number == 0){
                break;
            }
        }
    }
}

6.2 同步方法

在代码体里,把所有客官想同步的代码提取出来放到同步方法里
如果用继承Thread类的方式,则同步方法和资源都需要加上static关键字
Seller类:

public class Seller implements Runnable {
    int number = 1000;
    @Override
    public void run() {
        while (true){
            if (number == 0)
                break;
            else
                sellT();
        }
    }
    public synchronized void sellT() {
        if (number != 0)
            System.out.println(Thread.currentThread().getName() + "的窗口\t卖出第" + (1001 - number--) + "票");
    }
}

6.3 Lock锁

如果用继承Thread类的方式,则Lock锁和资源都需要加上static关键字
Seller类:

public class Seller implements Runnable {
    int number = 1000;
    Lock lock = new ReentrantLock();//创建一个锁对象

    @Override
    public void run() {

        while (true) {
            lock.lock();
            if (number >= 1)
                System.out.println(Thread.currentThread().getName() + "的窗口\t卖出第" + (1001 - number--) + "票");
            lock.unlock();
            if (number == 0) {
                break;
            }
        }
    }
}

7.线程通信

线程通信是使两个线程之间能知道下一步该谁做,做完下一步有该谁做下下一步。
重要的有两个方法和一个变量
wait方法使此线程等待,等到其他线程完成他该完成的,
notify方法通知所有线程我运行完了,你们谁用资源啊
变量,作为类似红绿灯的作用,从两个线程的角度都能看到,红灯线程1走,绿灯线程2走。
我描述的可能不太清晰,直接上例子吧!
题目:
在这里插入图片描述
Printer类:

public class Printer {
    private int index = 1;

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }

    public synchronized void print(int i) throws InterruptedException {
        if (index % 3 == 0){
            wait();
        }
        System.out.print(i);
        index++;
        notifyAll();//唤醒所有等待的线程
    }

    public synchronized void print(char c) throws InterruptedException {
        if (index % 3 != 0){
            wait();
        }
        System.out.println(c);
        index++;
        notifyAll();
    }
}

NumberPrint类:

public class NumberPrint extends Thread{
    private Printer p;//成员变量,属性
    public NumberPrint(Printer p){
        this.p = p;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 52; i++) {
            //p.print(i);
            try {
                p.print(i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

类:

public class LetterPrinter extends Thread {
    private Printer p;
    public LetterPrinter(Printer p){
        this.p = p;
    }

    @Override
    public void run() {//65 91
        for (char c = 'A'; c <= 'Z'; c++) {
            try {
                p.print(c);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Test类:

public class Test {
    public static void main(String[] args) {
        Printer p = new Printer();
        NumberPrint num = new NumberPrint(p);
        LetterPrinter l = new LetterPrinter(p);
        num.start();
        l.start();
    }
}

结果:
在这里插入图片描述


总结

本文讲述了Java中如何对文件或目录进行操作,使用了IO流技术。讲述了多线程如何进行,请各位客官哇,总结好难辩。
也不能怪我啦,毕竟我是外星人的嘞。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

攸攸太上

感谢支持

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

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

打赏作者

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

抵扣说明:

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

余额充值