IO流(一)

news2024/11/23 9:48:36

IO流的思维导图如下所示:

  

我们下来对文件、IO流原理及流的分类,节点流(访问文件的、访问数组的、访问管道的)和处理流(缓冲流、对象流、管道流),输入流(InputStream和Reader)和输出流(OutputStream和Writer),properties类进行学习。

一、文件

什么是文件:文件,对我们并不陌生,文件是保存数据的地方,比如大家经常使用的word文档,txt文件,excel文件....都是文件,它既可以保存一张图片,也可以保持视频,声音...

文件流:文件在程序中是以流的形式来操作的

流:数据在数据源(文件)和程序(内存)之间经历的路径

输入流:数据从数据源(文件)到程序(内存)的路径

输出流:数据从程序(内存)到数据源(文件)的路径

二、常用的文件操作:

1.创建文件对象相关构造器和方法

相关方法

new File(String pathname) //根据路径创建一个File对象

new File(File parent,String child) //根据父目录文件+子路径创建

new File(String parent,String child) //根据父目录+子路径构建

createNewFile 创建新文件

我们采用方式一进行创建:

new File(String pathname) //根据路径创建一个File对象

我们的代码如下所示:

 @Test
    //方式1 new File(String pathname) //根据路径创建一个File对象
    public void create01(){
        String filePath ="e:\\news1.txt";
        File file = new File(filePath);

        try {
            file.createNewFile();
            System.out.println("文件创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }

我们运行之后如下:

 我们在E盘里进行查看是否有该文件:

 我们发现成功创建

我们采用方式二进行创建如下:

new File(File parent,String child) //根据父目录文件+子路径创建

我们创建代码如下所示:

 @Test
    //方式2  new File(File parent,String child) //根据父目录文件+子路径创建
    //e:\\news2.txt
    public void create02(){
        File parentFile = new File("e:\\");
        String filename="news.txt";
        //这里的file对象,在java程序中,只是一个java对象
        //只有执行了createNewFile方法,才会真正的在磁盘创建该文件
        File file = new File(parentFile, filename);
        try {
            file.createNewFile();
            System.out.println("创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

我们运行之后如下所示:

 我们打开之后如下所示:

 我们进行查看方式三:

new File(String parent,String child) //根据父目录+子路径构建

我们编写代码如下所示:

 @Test
    //方式3  new File(String parent,String child) //根据父目录+子路径构建
    //e:\\news3.txt
    public void create03(){
       String parentPath="e:\\";
       String fileName="new3.txt";
        File file = new File(parentPath, fileName);
        try {
            file.createNewFile();
            System.out.println("文件创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

我们运行之后如下所示:

我们找到如下所示:

 我们发现当我们的写法如下所示:文件写为/,也可以创建成功,但是我们常用的是\\。

 @Test
    //方式4  new File(String parent,String child) //根据父目录+子路径构建
    //e:\\news4.txt
    public void create03(){
       String parentPath="e:/";
       String fileName="new4.txt";
        File file = new File(parentPath, fileName);
        try {
            file.createNewFile();
            System.out.println("文件创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

运行如下所示:

我们找到如下所示:

 2.获取文件信息

getName(获取名字)、getAbsolutePath(获取绝对路径)、getParent(获取父集目录)、length(大小)、exists(是否存在这个文件)、isFile(是否是文件)、isDirectory(是否是目录)。

我们发现File里面有好多方法。 

我们进行演示如下所示:

我们编写的代码如下所示:

//获取文件的信息
    @Test
    public  void info(){
        //先创建文件对象
        File file = new File("e:\\news1.txt");
//调用相应的方法得到对应信息即可。
        System.out.println("文件名字="+file.getName());
        System.out.println("文件绝对路径="+file.getAbsoluteFile());
        System.out.println("文件父集目录="+file.getParent());
        System.out.println("文件大小(字节)="+file.length());
        System.out.println("文件是否是文件:"+file.isFile());//T
        System.out.println("文件是否存在:"+file.exists());//T
        System.out.println("文件是否是目录:"+file.isDirectory());//F
    }
}

我们的文件如下所示: 

我们的文件内容如下所示: 

 

我们发现该编码为UTF-8编码,UTF-8编码一个英文字母对应一个字节,汉字对应三个字节。

我们运行之后的结果如下所示:
 

3.目录的操作和文件删除

 mkdir创建一级目录,mkdirs创建多级目录,delete删除空目录或文件

(1)判断:e:\\news1.txt 是否存在,如果存在就删除

我们创建代码如下所示:

import org.junit.Test;

import java.io.File;
import java.io.IOException;

public class Directory_ {
    public static void main(String[] args) {
        String filepath="e:\\news1.txt";
        File file = new File(filepath);
        try {
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
   // 判断:e:\\news1.txt 是否存在,如果存在就删除
    @Test
    public void m1(){
        String filepath="e:\\news1.txt";
        File file = new File(filepath);
        if (file.exists()){
            if(file.delete()){
                System.out.println(filepath+"删除成功");
            }else {
                System.out.println(filepath+"删除失败");
            }

        }else {
            System.out.println("该文件不存在...");
        }


    }
}

我们运行之后如下所示:

我们查看之后如下所示:

我们发现删除成功。

(2) 判断:d:\\demo02是否存在,如果存在就删除,否则提示不存在

我们编写代码如下所示:

// 2.判断:d:\\demo02是否存在,如果存在就删除,否则提示不存在
    //这里我们需要体会到,在java编程中,目录也被当作文件
    @Test
    public void m2() {
        String filepath = "d:\\demo02";
        File file = new File(filepath);
        if (file.exists()) {
            if (file.delete()) {
                System.out.println(filepath+"删除成功");
            } else {
                System.out.println(filepath+"删除失败");
            }
        } else {
            System.out.println("该目录不存在");
        }
    }
}

我们运行之后如下所示:

 我们在D盘里面进行创建该目录:

 我们继续运行之后如下所示:

我们进入D盘里面发现该目录已经被删除了。 

(3)判断 D:\\demo\\a\\b\\c 目录是否存在,如果存在就提示已经存在,否则就创建

我们创建多级目录的时候采用mkdirs进行创建,而采用mkdir进行创建的时候则会创建失败。

我们创建的代码如下所示:

@Test
    public void m3(){
        String directorypath="D:\\demo\\a\\b\\c";
        File file = new File(directorypath);
        if(file.exists()){
            System.out.println("该目录已经存在");
        }else{
           if(file.mkdirs()){//创建一级目录使用mkdir(),创建多级目录使用mkdirs()
               System.out.println(directorypath+"创建成功");
           }else {
               System.out.println(directorypath+"创建失败");
           }
        }
    }

运行之后如下所示:

 

我们进入D盘进行查看:

 我们用mkdir进行创建如下所示:

 我们利用mkdir创建一级目录如下所示:‘

4.IO流原理及流的分类 

java IO流原理:

(1)I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理数据传输。如读/写文件,网络通讯等。

(2)java程序中,对于数据的输入/输出操作以“流(Stream)"的方式进行。

(3)java.io包下提供了各种”流“类和接口,用以获取不同种类的数据,并通过方法输入或输出数据

(4)输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。

(5)输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中

流的分类:

按操作数据单位不同分为:字节流(8bit。效率低一点,但是对于二进制文件(声音文件,视频文件,word文件)可以保证操作为无损操作),字符流(按字符。文本文件)

按数据流的流向不同分为:输入流,输出流

按流的角色的不同分为:节点流,处理流/包装流

(抽象基类)字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter

(1)java的IO流共涉及40多个类,实际上非常规则,都是从如上4个抽象基类派生的。

(2)由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。

 

 

他们为抽象类,不能够直接实例化,它对应的子类对象才能操作。 

以下四个都是抽象类:

InputStream、OutputStream、Writer(字符输出流)、Reader(字符输入流)

三、字节流

InputStream:字节输入流

InputStream抽象类是所有类字节输入流的超类

InputStrem常用的子类

1.FileInputStream:文件输入流

2.BufferedInputStream:缓冲字节输入流

3.ObjectInputStream:对象字节输入流

我们的关系图如下所示:

 1.FileInputStream:文件输入流

要求使用FileInputStream读取hello.txt文件,并将文件内容显示到控制台。

我们写入如下文件:

我们编写的代码如下所示:

 /**
     * 演示读取hello.txt文件
     * 单个字节的读取,效率比较低
     * @throws IOException
     */
    @Test
    public void  readFile01() throws IOException {
        int readData=0;
        String filePath="e:\\hello.txt";
        //创建FileInputStream对象,用于读取文件
        FileInputStream fileInputStream = new FileInputStream(filePath);
        //从该输入流读取一个字节的数据,如果没有输入可用,此方法将阻止。
        //如果返回-1,表示读取完毕。
        while(( readData=fileInputStream.read()) !=-1){
            System.out.print((char)readData);//转成char显示
        }
            //关闭文件流,释放资源
            fileInputStream.close();
        }

    }

我们运行之后如下所示:

 如果我们用此方法读取中文的时候,会出现乱码。

我们使用read(byte [ ] b )来进行读取。

我们将文件改为如下所示:

 我们编写的代码如下所示:

 /**
     * 演示读取hello.txt文件
     * 使用read(byte[] b)读取文件,提高效率
     * @throws IOException
     */
    @Test
    public void  readFile01() throws IOException {
        //字节数组
        byte[] buf = new byte[24];//一次读取24个字符
        int readLen=0;
        String filePath="e:\\hello.txt";
        //创建FileInputStream对象,用于读取文件
        FileInputStream fileInputStream = new FileInputStream(filePath);
        //从该输入流读取最多b.length字节的数据到字节数组,此方法将阻塞,直到某些输入可用
        //如果返回-1,表示读取完毕。
        //如果读取正常,返回实际读取的字节数
        while ((readLen=fileInputStream.read(buf))!=-1){
            System.out.print((new String(buf,0,readLen)));//显示
        }
            //关闭文件流,释放资源
            fileInputStream.close();
        }

    }

运行之后如下所示:

2.FileOutputStream:文件输出流 

请使用FileOutputStream在a.txt文件中写入"hello,world".如果文件不存在,会创建文件(注意:前提是目录已经存在)。

我们的代码设计如下所示:

 /**
     * 演示使用FileOutputStream,将数据写到文件中,如果该文件不存在,则创建
     */
    @Test
    public void writeFile(){
//请使用FileOutputStream在a.txt文件中写入"hello,world".如果文件不存在,会创建文件(注意:前提是目录已经存在)。
        //创建FileOutputStream对象
        String filePath="e:\\a.txt";
        FileOutputStream fileOutputStream= null;

        try {
            //得到FileOutputStream对象
            fileOutputStream = new FileOutputStream(filePath);
            //写入一个字节
            fileOutputStream.write('a');

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }


    }

 我们运行之后如下所示:

以上代码为写入一个字符,我们下来看写入字符串的实现

我们写入字符串的代码设计如下所示:

 @Test
    public void writeFile(){
//请使用FileOutputStream在a.txt文件中写入"hello,world".如果文件不存在,会创建文件(注意:前提是目录已经存在)。
        //创建FileOutputStream对象
        String filePath="e:\\a.txt";
        FileOutputStream fileOutputStream= null;

        try {
            //得到FileOutputStream对象
            fileOutputStream = new FileOutputStream(filePath);
            //写入一个字节
            fileOutputStream.write('a');
            //写入字符串
            String str="hello,world!";
            //str.getBytes()可以把 字符串转变为字节数组
            fileOutputStream.write(str.getBytes());
            /*
            write(byte[] b,int off,int len) 将len字节从位于偏移量off的指定字节数组写入此文件输出流。
            */
            String str1="rgf,ypl!";
            fileOutputStream.write(str1.getBytes(),0,str1.length());
            //写入前三个字节
            fileOutputStream.write(str1.getBytes(),0,3);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

我们运行之后如下所示:

 当我们在前一个写入的基础上再写入新内容的时候,我们会发现会覆盖之前的内容。

代码如下所示:

 /**
     * 演示使用FileOutputStream,将数据写到文件中,如果该文件不存在,则创建
     */
    @Test
    public void writeFile(){
//请使用FileOutputStream在a.txt文件中写入"hello,world".如果文件不存在,会创建文件(注意:前提是目录已经存在)。
        //创建FileOutputStream对象
        String filePath="e:\\a.txt";
        FileOutputStream fileOutputStream= null;

        try {
            //得到FileOutputStream对象
            fileOutputStream = new FileOutputStream(filePath);
            //写入一个字节
            //fileOutputStream.write('a');
            //写入字符串
            String str="hello,world!";
            //str.getBytes()可以把 字符串转变为字节数组
           // fileOutputStream.write(str.getBytes());
            /*
            write(byte[] b,int off,int len) 将len字节从位于偏移量off的指定字节数组写入此文件输出流。
            */
            String str1="rgf,ypl!";
           // fileOutputStream.write(str1.getBytes(),0,str1.length());
            //写入前三个字节
            fileOutputStream.write(str1.getBytes(),0,3);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }


    }
}

我们运行之后如下所示:

我们会发现此运行结果覆盖掉了之前的内容。

我们除了重新创建str之外,我们也可以采用其他方式进行创建。

2.new FileOutputStream(filePath,true),当写入内容时,是追加到文件后面

我们设计的代码如下所示:

 @Test
    public void writeFile(){
//请使用FileOutputStream在a.txt文件中写入"hello,world".如果文件不存在,会创建文件(注意:前提是目录已经存在)。
        //创建FileOutputStream对象
        String filePath="e:\\a.txt";
        FileOutputStream fileOutputStream= null;

        try {
            //得到FileOutputStream对象
            //说明:
            //1.new FileOutputStream(filePath)创建方式,当写入内容时,会覆盖原来的内容
            //2.new FileOutputStream(filePath,true),当写入内容时,是追加到文件后面
            fileOutputStream = new FileOutputStream(filePath,true);
            //写入一个字节
            fileOutputStream.write('a');
            //写入字符串
            String str="hello,world!";
            //str.getBytes()可以把 字符串转变为字节数组
           fileOutputStream.write(str.getBytes());
            /*
            write(byte[] b,int off,int len) 将len字节从位于偏移量off的指定字节数组写入此文件输出流。
            */
            String str1="rgf,ypl!";
           fileOutputStream.write(str1.getBytes(),0,str1.length());
            //写入前三个字节
            fileOutputStream.write(str1.getBytes(),0,3);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

我们运行之后如下所示:

 我们发现是在之前的文件内容的基础上进行输出的。

3.文件拷贝

我们在D盘里面存储如下照片:

我们设计的原理如下所示:

 之后我们将其拷贝到E盘里面,我们进行设计代码:

package com.rgf.outputStream_;

import java.io.*;

public class FileCopy {
    public static void main(String[] args) {
        //完成文件拷贝,将D盘里面的微信图片D:\\微信图片.png拷贝到 E:\\
        //思路分析
        //1.创建文件的输入流,将文件读入到程序
        //2.创建文件的输出流,将读取到的文件数据,写入到指定的文件。
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        String srcFilePath="D:\\微信图片.png";
        String destFilePath="E:\\微信图片.png";
        try {
             fileInputStream = new FileInputStream(srcFilePath);
             fileOutputStream = new FileOutputStream(destFilePath);
            //定义一个字节数组,提高读取效果
            byte[] buf=new byte[1024];
            int readLen=0;
            while ((readLen=fileInputStream.read(buf))!=-1){
           //读取到后,就写入到文件,通过FileOutputStream
                //即,是一边读一边写
                fileOutputStream.write(buf,0,readLen); //一定要使用这个方法
            }
            System.out.println("拷贝成功");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //关闭输入流和输出流,释放资源
            if(fileInputStream!=null){
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                   e.printStackTrace();
                }
            }
            if(fileOutputStream!=null){
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

我们运行之后如下所示:

 我们打开E盘进行查看:

我们发现成功拷贝进去。 

四、字符流

1.文件字符流说明

FileReader和FileWriter是字符流,即按照字符来操作io

FileReader的关系如下所示:

FileReader相关方法:

(1)new FileReader(File/String)

(2)read:每次读取单个字符,返回该字符,如果到文件末尾返回-1。

(3)read(char[]):批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾返回-1

相关API:

(1)new String(char[] ):将char[]转换成String

(2)new String(char [],off,len):将char[]的指定部分转换成String

FileWriter的关系如下所示:

FileWriter常用方法:

(1)new FileWriter(File/String):覆盖模式,相当于流的指针在首端

(2)new FileWriter(File/String,true):追加模式,相当于流的指针在尾端

(3)writer(int):写入单个字符

(4)writer(char[]):写入指定数组

(5)writer(char[],off,len):写入指定数组的指定部分

(6)writer(string):写入整个字符串

(7)write(string,off,len):写入字符串的指定部分

相关API:String类:toCharArray:将String转换成char[]

注意:

FileWriter使用后,必须要关闭(close)或刷新(flush),否则写入不到指定的文件!

2.FileReader

使用FileReader从story.txt读取内容,并显示。

我们的代码设计如下所示:

单个字符进行读取

package com.rgf.reader_;

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

public class FileReader_ {
    public static void main(String[] args) {
        FileReader fileReader=null;
        int data=' ';
        String filePath="E:\\story.txt";
//1.创建FileReader对象

        try {
            fileReader = new FileReader(filePath);
            //循环读取 使用read,单个字符读取
            while((data=fileReader.read())!=-1){
                System.out.print((char) data);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(fileReader!=null){
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }

}

 我们运行之后如下所示:

 以上方法读取速度慢,我们进行尝试如下方法:

我们使用字符数组来进行读取文件:

我们设计的代码如下所示:

//字符数组读取文件
package com.rgf.reader_;

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

public class FileReader_ {
    public static void main(String[] args) {
        FileReader fileReader=null;
        int readLen=0;
        char[] buf=new char[8];
        String filePath="E:\\story.txt";
//1.创建FileReader对象

        try {
            fileReader = new FileReader(filePath);
            //循环读取 使用read(buf),返回的是实际读取到的字符数
            //如果返会回-1,说明到文件结束
            while((readLen=fileReader.read(buf))!=-1){
                System.out.print(new String(buf,0,readLen));
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(fileReader!=null){
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }

}

我们运行之后如下所示:

3.FileWriter

使用FileWriter将“风雨之后,定见彩虹”写入到note.txt文件中,注意细节。

(1)writer(string):写入整个字符串

我们的代码如下所示:

package com.rgf.writer;

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

public class FileWriter_ {
    public static void main(String[] args) {
        String filePath="E:\\note.txt";
        //创建FileWriter对象
        FileWriter fileWriter=null;
        try {
             fileWriter = new FileWriter(filePath);
           fileWriter.write("风雨之后,定见彩虹");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            
        }

    }
}

我们发现在我们编写完毕之后没有关闭之后进入如下所示:

 之后我们在finally加入如下所示:

package com.rgf.writer;

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

public class FileWriter_ {
    public static void main(String[] args) {
        String filePath="E:\\note.txt";
        //创建FileWriter对象
        FileWriter fileWriter=null;
        try {
             fileWriter = new FileWriter(filePath);
           fileWriter.write("风雨之后,定见彩虹");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //对应FileWriter,一定要关闭流,或者flush才能真正的把数据写入到文件
            if(fileWriter!=null){
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

运行之后如下所示:

 (2)writer(int):写入单个字符

我们设计代码如下所示:

package com.rgf.writer;

import java.io.FileWriter;
import java.io.IOException;
/*
(1)writer(int):写入单个字符

(2)writer(char[]):写入指定数组

(3)writer(char[],off,len):写入指定数组的指定部分

(4)writer(string):写入整个字符串

(5)write(string,off,len):写入字符串的指定部分
 */
public class FileWriter_ {
    public static void main(String[] args) {
        String filePath="E:\\note.txt";
        //创建FileWriter对象
        FileWriter fileWriter=null;
        try {
             fileWriter = new FileWriter(filePath);
           fileWriter.write('H');
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //对应FileWriter,一定要关闭流,或者flush才能真正的把数据写入到文件
            if(fileWriter!=null){
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

我们运行之后如下所示:

(3)writer(char[]):写入指定数组

package com.rgf.writer;

import java.io.FileWriter;
import java.io.IOException;
/*
(1)writer(int):写入单个字符

(2)writer(char[]):写入指定数组

(3)writer(char[],off,len):写入指定数组的指定部分

(4)writer(string):写入整个字符串

(5)write(string,off,len):写入字符串的指定部分
 */
public class FileWriter_ {
    public static void main(String[] args) {
        String filePath="E:\\note.txt";
        //创建FileWriter对象
        FileWriter fileWriter=null;
        char[] chars={'a','b','c'};
        try {
             fileWriter = new FileWriter(filePath);//默认是覆盖.
           fileWriter.write(chars);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //对应FileWriter,一定要关闭流,或者flush才能真正的把数据写入到文件
            if(fileWriter!=null){
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

 我们运行之后如下所示:

 (4)writer(char[],off,len):写入指定数组的指定部分

我们设计的代码如下所示:

package com.rgf.writer;

import java.io.FileWriter;
import java.io.IOException;
/*
(1)writer(int):写入单个字符

(2)writer(char[]):写入指定数组

(3)writer(char[],off,len):写入指定数组的指定部分

(4)writer(string):写入整个字符串

(5)write(string,off,len):写入字符串的指定部分
 */
public class FileWriter_ {
    public static void main(String[] args) {
        String filePath="E:\\note.txt";
        //创建FileWriter对象
        FileWriter fileWriter=null;
        char[] chars={'a','b','c'};
        try {
             fileWriter = new FileWriter(filePath);
           fileWriter.write(chars);
           fileWriter.write("争做最棒的程序员".toCharArray(),0,3);
//toCharArray(字符数组)
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //对应FileWriter,一定要关闭流,或者flush才能真正的把数据写入到文件
            if(fileWriter!=null){
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

我们运行之后如下所示:

 (5)write(string,off,len):写入字符串的指定部分

package com.rgf.writer;

import java.io.FileWriter;
import java.io.IOException;
/*
(1)writer(int):写入单个字符

(2)writer(char[]):写入指定数组

(3)writer(char[],off,len):写入指定数组的指定部分

(4)writer(string):写入整个字符串

(5)write(string,off,len):写入字符串的指定部分
 */
public class FileWriter_ {
    public static void main(String[] args) {
        String filePath="E:\\note.txt";
        //创建FileWriter对象
        FileWriter fileWriter=null;
        char[] chars={'a','b','c'};
        try {
             fileWriter = new FileWriter(filePath);
       fileWriter.write("争做最棒的程序员",0,3);//覆盖
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //对应FileWriter,一定要关闭流,或者flush才能真正的把数据写入到文件
            if(fileWriter!=null){
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

我们运行之后如下所示:

 在数据量大的情况下,可以使用循环操作。

我们可以通过断点的形式来查看close的重要性。

 我们继续追进去,我们发现进入了他的父类:OutputStreamWriter

 我们进入如下所示:

我们进入如下最重要的地方。

我们发现这才是我们真正添加文件的地方。 

我们发现底层用的是FileOutputStream,调用的是底层的方法。 

我们继续进入即可发现我们所输出的一系列数据 

我们即可看到返回的东西。

我们的flush也可以用同样的方法。

我们继续往进追:

此为判断是否流关闭掉了。 

 

 我们发现底层都是按照WriteBytes进行操作的。

我们进入了this.out.write,我们发现底层仍然是FileOutputStream。

我们发现close相当于等价于flush()+一个关闭的行为。

五、节点流和处理流

1.字节流和处理流的原理机制

1.节点流可以从一个特定的数据源(存放数据的地方)读写数据,如FileReader、FileWriter(对文件进行读和对文件进行写的对象

 2.处理流(也叫包装流)是“连接”在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能,也更加灵活,如BufferedReader、BufferWriter.

 BufferedReader类中,有属性Reader,即可以封装一个节点流(该节点流可以是任意的,只要是Reader的一个子类就行)

我们进入他们的源码如下所示:

 我们的BufferedReader类继承自Reader,我们的BufferReader可以用Reader的子类。BufferedWriter也是一样的。

 

 我们查看构造器里面也有writer。

 节点流和处理流的区别和联系:

1.节点流是底层流/低级流,直接跟数据源相接。

2.处理流(包装流)包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出。

3.处理流(也叫包装流)对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连(模拟修饰器设计模式)

我们下来演示修饰器的效果。

我们利用代码进行演示:

我们新建Reader_:

package com.rgf.writer;

public abstract class Reader_ { //抽象类  Reader
    public   void readFile()  {

    };
    public void readString()  {

    };

}

新建FileReader_:

package com.rgf.writer;
//节点流
public class FileReader_ extends  Reader_{

    public  void readFile(){
        System.out.println("对文件进行读取..");
    }
}

新建StringFile_:

package com.rgf.writer;

/**
 * 节点流
 */
public class StringReader_ extends Reader_{
    public  void readString() {
        System.out.println("读取字符串");
    }
}

我们新建BufferedReader_:

package com.rgf.writer;

/**
 * 做成处理流/包装流
 */
public class BufferedReader_ extends  Reader_ {
    private Reader_ reader_; //属性是Reader_类型,即我们可以调用里面的方法

//接收Reader_ 子类对象
    public BufferedReader_(Reader_ reader_) {
        this.reader_ = reader_;
    }

    //让方法更加灵活,多次读取文件,扩展了readFile()
    public  void  readFiles(int num){
        for(int i=0;i<num;i++){
            reader_.readFile();
        }
    }
    //扩展readString,批量处理字符串数据
    public  void  readStrings(int num){
        for (int i=0;i<num;i++){
            reader_.readString();
        }
    }

}

我们所编写的BufferedReader_里面包含了FileReader_类里面的readFile方法,StringReader_类里面的readStrings方法。

我们下来进行测试:

package com.rgf.writer;

public class Test_ {
    public static void main(String[] args) {
        BufferedReader_ bufferedReader_ = new BufferedReader_(new FileReader_());
        bufferedReader_.readFiles(10);

        //这次希望通过BufferedReader_ 多次读取字符串
        BufferedReader_ bufferedReader_1 = new BufferedReader_((new StringReader_()));
        bufferedReader_1.readStrings(10);

    }
}

我们运行之后如下所示:

以上的BufferedReader_是我们扩展的方法,我们也可以直接调用我们需要的方法(字节流)

FileReader_ fileReader_ = new FileReader_();
        fileReader_.readFile();

我们运行之后如下所示:
 

我们整个的运行机制就是上述的原理所示,即可通过BufferedReader_更加方便,可以使用更多的单一类的方法,更加高效。

处理流的功能主要体现在以下两个方面:

1.性能的提高:主要以增加缓冲的方式来提高输入输出的效率

2.操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便。

以上演示的过程中,我们采用了多个方法,我们也可以采用统一的read方法进行管理:

 public abstract void read();

之后的方法我们设定为:

 public  void  read(){
        
    }

我们利用多态的动态绑定机制来绑定到他对应的子类。

我们后面在调用时,利用对象动态绑定机制,绑定到对应的实现子类即可。

2.处理流BufferedReader

BufferedReader和BufferedWriter属于字符流,是按照字符来读取数据的。关闭处理流,只需要关闭外层流即可。尽量来操作文本文件,而不是二进制文件。二进制文件(图片,声音,视频)在组织的时候是按字节来组织的。文本文件用的是字符,用字符来操作比较高效。

如果用二进制文件来按字符读取,有可能会造成字符的损毁。

我们设置代码如下所示:

package com.rgf.reader_;

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

/**
 * 演示BufferedReader使用
 */
public class BufferedReader_ {
    public static void main(String[] args) throws Exception{
        String filePath="E:\\a.txt";
        //创建bufferedReader对象
        BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
        //读取,bufferedReader读取的时候,底层其实用的是FileReader.
        String line; //按行读取,效率高
        //说明
        //1.bufferedReader.readLine()是按行读取文件
        //2.当返回null时,表示文件读取完毕
        while((line=bufferedReader.readLine())!=null){
            System.out.println(line);
        }
        //关闭流,这里注意,只需要关闭BufferedReader,因为底层会自动的去关闭节点流
        
        bufferedReader.close();
    }
}

我们运行之后如下所示:

 我们通过断点来查看关闭BufferedReader的底层机制:

我们点红色箭头进入里面。

我们发现此时我们的in已经显示的是FileReader 。

3.处理流BufferedWriter 

使用BufferedWriter将“沃尔总冠军”写入到文件中。

我们设计的代码如下所示:

package com.rgf.reader_;

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

/**
 * 演示BufferedWriter的使用
 */
public class BufferedWriter_ {
    public static void main(String[] args) throws IOException {
        String filePath="E:\\a.txt";
        //创建BufferedWriter
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
        bufferedWriter.write("沃尔总冠军1");
        bufferedWriter.write("沃尔总冠军2");
        bufferedWriter.write("沃尔总冠军3");

        //说明:关闭外层流即可,传入的new FileWriter(filePath)会在底层关闭。
        bufferedWriter.close();


    }

}

我们运行之后如下所示:

 我们发现此输入没有换行,我们将代码修改如下所示:

package com.rgf.reader_;

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

/**
 * 演示BufferedWriter的使用
 */
public class BufferedWriter_ {
    public static void main(String[] args) throws IOException {
        String filePath="E:\\a.txt";
        //创建BufferedWriter
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
        bufferedWriter.write("沃尔总冠军1");
        bufferedWriter.newLine(); //插入一个和系统相关的换行
        bufferedWriter.write("沃尔总冠军2");
        bufferedWriter.newLine(); //插入一个和系统相关的换行
        bufferedWriter.write("沃尔总冠军3");
        bufferedWriter.newLine(); //插入一个和系统相关的换行
        //说明:关闭外层流即可,传入的new FileWriter(filePath)会在底层关闭。
        bufferedWriter.close();


    }

}

我们运行之后如下所示:

 我们查看BufferedWriter是否有追加的方法,我们通过构造器查看其方法:

我们发现没有boolean,即没有追加的方法

我们在FileWriter可以发现,即可以进行追加。

 

 我们设计代码如下所示:

package com.rgf.reader_;

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

/**
 * 演示BufferedWriter的使用
 */
public class BufferedWriter_ {
    public static void main(String[] args) throws IOException {
        String filePath="E:\\a.txt";
        //创建BufferedWriter
        //说明:
        //1.new FileWriter(filePath,true)表示以追加的方式写入
        //2.new FileWriter(filePath),表示以覆盖的方式写入
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath,true));
        bufferedWriter.write("沃尔总冠军1");
        bufferedWriter.newLine(); //插入一个和系统相关的换行
        bufferedWriter.write("沃尔总冠军2");
        bufferedWriter.newLine(); //插入一个和系统相关的换行
        bufferedWriter.write("沃尔总冠军3");
        bufferedWriter.newLine(); //插入一个和系统相关的换行
        //说明:关闭外层流即可,传入的new FileWriter(filePath)会在底层关闭。
        bufferedWriter.close();


    }

}

运行之后如下所示:

 

4.BufferedReader和BufferedWriter完成文本文件拷贝。

我们设计的代码如下所示:

package com.rgf.writer;

import java.io.*;

public class BufferedCopy_ {
    public static void main(String[] args)  {
        //说明:
        //1.BufferedReader和BufferedWriter是安装字符操作
        //2.不要去操作二进制文件,可能造成文件损坏
        //首先确定要拷贝的文件
        String srcFilePath="E:\\a.txt";
        //确定要拷贝到的地址
        String destFilePath="E:\\b.txt";
        BufferedReader br=null;
        BufferedWriter bw=null;
        String line;
        try {
            br=new BufferedReader(new FileReader(srcFilePath));
            bw= new BufferedWriter(new FileWriter(destFilePath));
            //然后进行读取
            //说明:readLine读取一行内容,但是没有换行
            while((line=br.readLine())!=null){
                //每读取一行,就写入
                bw.write(line);
                //插入一个换行
                bw.newLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //关闭流
            //不关闭无法写入文件,写文件肯定是不为null的时候,所以要在不为null时关闭,这样才能写入(不为null说明有文件,在已经有文件的情况下要写入的话需关闭),如果为null的话,是已经关闭了,不需要手动关闭了。
            if(br!=null){
                try {
                    br.close();

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(bw!=null){
                try {
                    bw.close();
                    System.out.println("拷贝完毕..");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

    }
}

我们运行之后如下所示:

 我们进行拷贝文件的过程为:先确定拷贝的文件,确定拷贝的地址,然后我们new进行读和写的对象,然后进行循环的从被拷贝文件里面读,写到拷贝文件里。

我们拷贝二进制文件如下所示:

package com.rgf.writer;

import java.io.*;

public class BufferedCopy_ {
    public static void main(String[] args)  {

        //说明:
        //1.BufferedReader和BufferedWriter是安装字符操作
        //2.不要去操作二进制文件[声音,视频,doc,pdf等等),可能造成文件损坏
        //首先确定要拷贝的文件
        String srcFilePath="E:\\微信图片.png";
        //确定要拷贝到的地址
        String destFilePath="E:\\微信.png";
        BufferedReader br=null;
        BufferedWriter bw=null;
        String line;
        try {
            br=new BufferedReader(new FileReader(srcFilePath));
            bw= new BufferedWriter(new FileWriter(destFilePath));
            //然后进行读取
            //说明:readLine读取一行内容,但是没有换行
            while((line=br.readLine())!=null){
                //每读取一行,就写入
                bw.write(line);
                //插入一个换行
                bw.newLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //关闭流
            //不关闭无法写入文件,写文件肯定是不为null的时候,所以要在不为null时关闭,这样才能写入(不为null说明有文件,在已经有文件的情况下要写入的话需关闭)
            if(br!=null){
                try {
                    br.close();

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(bw!=null){
                try {
                    bw.close();
                    System.out.println("拷贝完毕..");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

    }
}

运行之后如下所示:

 

我们发现拷贝完之后,图片大小也不一样。

 

我们发现图片已经损坏了。

 

5.BufferedInputStream和BufferedOutputStream

我们进入源码如下所示:

我们发现BufferedInputStream封装了一个in,但是是在父类FilterInputStream里面,而InputStream是一个抽象类。我们使用BufferedInputStream时,只要使用InputStream子类就可以了。

 我们可以发现只要实现了InputStream这种节点流,就可以接收。

BufferedInputStream这种处理流,他的底层用的是从他的父类FilterInputStream继承下来的in这个属性,而且类型是InputStream。而InputStream很多个子类,就可以接收它下面的任意多个子类。

 BufferedOutputStream

 BufferedOutputStream是字节流,实现缓冲的输出流,可以将多个字节写入底层输出流中,而不必对每次字节写入调用底层系统。它的父类FilterOutputStream也继承了一个属性,out,他的属性是OutoutStream(抽象类),可以接受他的多个实现子类。

6.编程完成图片/音乐的拷贝(要求使用BufferedOutputStream和BufferedInputStream)

我们设计的代码如下所示:

package com.rgf.outputStream_;
/**
 * 演示使用BufferedOutputStream和BufferedInputStream
 * 使用他们,可以完成二进制文件拷贝
 * 思考:字节流可以操作二进制文件,可以操作文本文件。一个中文字符占三个字节,一个英文字符占一个字节。
 */

import java.io.*;

public class BufferedCopy02 {
    public static void main(String[] args)  {
        //首先确定原文件的路径
        String srcFilePath="E:\\微信图片.png";
        //确定拷贝到的路径
        String destFilePath="E:\\复制微信图片.png";

        //创建BufferedOutputStream对象和BufferedInputStream对象
        BufferedInputStream bis=null;
        BufferedOutputStream bos=null;
        try {
            //因为FileInputStream是InputStream的子类
            bis=new BufferedInputStream(new FileInputStream(srcFilePath));
            //因为BufferedOutputStream是OutputStream的子类
            bos=new BufferedOutputStream(new FileOutputStream(destFilePath));
            //循环的读取文件,并写入到destFilePath
            byte[] buff =new byte[1024];
            int readLen =0;
            //当返回-1时,就表示文件读取完毕
            while ((readLen=bis.read(buff))!=-1){
                bos.write(buff,0,readLen);
            }

            System.out.println("文件拷贝成功");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //关闭流,关闭外层的处理流即可,底层会去关闭节点流
            if(bis!=null){
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
           if (bos!=null){
               try {
                   bos.close();
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
        }




    }
}

我们运行之后如下所示:


 我们发现复制过来的大小一致,同时也可以打开,复制成功了。

 

  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/99900.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[附源码]Nodejs计算机毕业设计基于协同过滤技术的旅游景点购票系统Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

Python+Selenium自动化测试详细教程

前言 PythonSelenium 来实现的web端自动化, 以下演示会用到禅道、百度和自己编写的html. 一、准备工作 1、安装 安装Python 3安装selenium库&#xff0c;命令&#xff1a;pip install selenium搭建禅道环境 2、导入浏览器驱动 selenium操作不同的浏览器&#xff0c;需要下…

Windows10 系统下载网址推荐(二)

Windows10 系统下载网址推荐&#xff08;二&#xff09;1. 计算机操作系统概述2.HelloWindows3. 山己几子木4. xitongku5. TechBench结束语1. 计算机操作系统概述 操作系统&#xff08;Operating System&#xff0c;OS&#xff09;是一组主管并控制计算机操作、运用和运行硬件…

设计一个接口务必考虑好这14个基本点

目录&#xff1a;导读 前言 1、入参合法性校验 2、接口的版本控制 3、接口考虑幂等性 4、接口考虑防止重复请求 5、提高接口的响应时间 6、接口限流控制 7、黑白IP白名单 8、敏感数据脱敏 9、请求接口的先决条件-token 10、记录接口请求日志 11、调用第三方接口要考…

分解优化react对redux的基础使用

之前写了 react18 通过redux 做一个简单的状态管理基站 但代码确实相对比较乱 这次进行一些小封装和分解 优化一下管理质量 首先 我们创建一个 react项目 然后在项目中引入 npm install redux --save在src目录下创建 reducers 文件夹 下面创建 counter.js counter.js 参考代码…

有什么免费python安装包?

前言 Python的安装涉及到&#xff1a;Python编辑器、Python解释器、Python包管理工具&#xff08;pip&#xff09;。因此&#xff0c;首先我们要搞清楚这三个东西都是啥。 Python编辑器 正如在电脑上编辑文档需要用Word、处理数据需要用Excel、做演示文稿需要用PPT、修图需要…

git add 命令详解

1. 前言 2. git add 基本操作 3. git add 命令参数 4. git add 背后做了什么 1. 前言 众所周知&#xff0c;git 中有工作区、暂存区、版本库三大组成部分 工作区: 电脑中能看到的目录&#xff0c;也就是写代码的地方 暂存区: 英文叫 stage 或 index。一般存放在 .git 目录下…

【图像分割】灰狼算法最小交叉熵多阈值图像分割【含Matlab源码 903期】

⛄一、最小交叉熵多阈值图像分割简介 1 单阈值分割 设有两个概率分布P{p1, p2, …, pN}和Q{q1, q2, …, qN}, 交叉熵度量它们之间的信息量差异。其对称形式为 交叉熵既可看成是采用P取代Q作为单个系统概率分布时系统信息量变化的期望值, 也可看成是两个概率系统P和Q之间的信息…

CSAPP-Lab5 CacheLab解析

Review Cache Struct A cache is a set of 2s2^s2s cache setsA cache set is a set of E cache lines if E1, it is called “direct-mapped” Each cache line stores a blockTotal Capacity S * B * E 由此&#xff0c;我们可以写出cache line和cache的结构&#xff1a; …

微服务框架 SpringCloud微服务架构 服务异步通讯 50 消息可靠性 50.4 失败重试机制 50.4.1 消费者失败重试

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 服务异步通讯 文章目录微服务框架服务异步通讯50 消息可靠性50.4 失败重试机制50.4.1 消费者失败重试50 消息可靠性 50.4 失败重试机制 50…

可路由计算引擎实现前置数据库

❤️作者主页&#xff1a;小虚竹 ❤️作者简介&#xff1a;大家好,我是小虚竹。Java领域优质创作者&#x1f3c6;&#xff0c;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;掘金年度人气作者&#x1f3c6;&#xff0c;阿里云专家博主&#x1f3…

非零基础自学Golang 第9章 结构体 9.1 理解结构体 9.2 定义结构体 9.3 实例化结构体

非零基础自学Golang 文章目录非零基础自学Golang第9章 结构体9.1 理解结构体9.2 定义结构体9.3 实例化结构体9.3.1 标准实例化9.3.2 new函数实例化9.3.3 取地址实例化9.3.4 访问成员第9章 结构体 在Go语言官网的常见问题解答一栏中&#xff0c;有这样一个问题&#xff1a;“Is…

Fiddler(7):fiddler设置弱网测试

弱网测试 概念&#xff1a;弱网看字面意思就是网络比较弱&#xff0c;我们通称为信号差&#xff0c;网速慢。 意义&#xff1a;模拟在地铁、隧道、电梯和车库等场景下使用APP &#xff0c;网络会出现延时、中断和超时等情况。 Fiddler弱网测试流程&#xff1a; 一、限速操作…

基于云开发的微信小程序、QQ小程序详细教程(更新中)

云开发解决方案 小程序云开发解决方案 为企业和开发者提供一站式后端云服务&#xff0c;无需管理基础架构&#xff0c;一次开发多端运行&#xff0c;腾讯云和微信联合出品。 云开发&#xff08;Tencent CloudBase&#xff0c;TCB&#xff09;是腾讯云提供的云原生一体化开发环…

数据结构---大整数相加

大整数相加列竖式运算第一步第二步第三步第四步JAVA实现给出两个很大的整数&#xff0c;要求实现程序求出两个整数之和。 记得这个题目我大二ACM课程老师讲过&#xff0c;但是忘记了。。。。。。。。。。 列竖式运算 程序不可能通过一条指令计算出两个大整数之和&#xff0c;…

【学习打卡04】可解释机器学习笔记之Grad-CAM

可解释机器学习笔记之Grad-CAM 文章目录可解释机器学习笔记之Grad-CAMGrad-CAM介绍Grad-CAM是CAM的泛化形式Gard-CAM可视化结果Grad-CAM算法的优点Grad-CAM算法的缺点Grad-CAM变种Grad-CAMScore-CAMLayer-CAM思考与总结参考阅读首先非常感谢同济子豪兄拍摄的可解释机器学习公开…

SAP S4 FICO 固定资产模块后台配置详解

1. 概述 资产会计&#xff08;FI-AA&#xff09;作为总帐模块&#xff08;FI-GL&#xff09;的子分类帐&#xff0c;对企业的固定资产、无形资产、在 建工程、低值易耗品、长期待摊、从购置、资本化、折旧、调拨到出售/报废的整个生命周期 进行全过程的管理&#xff0c;并和…

java实现给微信群中定时推送消息

大家好&#xff0c;我是雄雄。 前言 上一篇&#xff0c;我们介绍了如何通过调用接口的方式&#xff0c;将每日新闻发送到自己的博客中。我们会发现&#xff0c;将新闻以文章的形式发布&#xff0c;并且相关内容按照markdown的形式进行格式调整&#xff0c;有需要的可以点击这里…

获取小程序生产、开发、体验等环境、版本信息、appid等信息

if (typeof __wxConfig "object"){const version __wxConfig.envVersion;console.log("当前环境:" version)if (version "develop"){cosole.log(测试 开发环境)}else if (version "trial"){cosole.log(测试 体验版)}else if (versio…

【电力系统】基于YALMIP+CPLEX求解带储能的微电网优化调度问题附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …