java——IO流基础

目录

  • IO流
    • IO流的四大分类:
    • IO流的体系:
    • FileinputStream(文件字节输入流)
    • FileOutputStream(文件字节输出流)
    • 文件复制
    • 资源释放
    • FileReader(文件字符输入流)
    • FileWriter(文件字符输出流)
    • 缓冲流
      • 字节缓冲流
      • 字符缓冲流
    • 原始流、缓冲流的性能分析
      • 测试代码:
      • 测试一:
      • 测试二:
      • 测试三:
      • 测试四:
    • 转换流
      • InputStreamReader (字符输入转换流)
      • OutputStreamWriter(字符输出转换流)
    • 打印流(PrintStream/PrintWriter)
      • 字节打印流
      • 字符打印流
      • PrintStream和Printwriter的区别
      • 重定向输出语句
    • 数据流
      • DataOutputStream(数据输出流)
      • DatalnputStream(数据输入流)
    • 序列化流
      • ObjectOutputStream(对象字节输出流)
      • ObjectinputStream(对象字节输入流)
      • 如果要一次序列化多个对象,怎么做?
    • IO框架

IO流

用于读写数据的数据流 (可以读写磁盘文件,或网络中的数据…)

IO流的四大分类:

  • 字节输入流:以内存为基准,来自磁盘文件/网络中的数据以字节的形式读入到內存中去的流
  • 字节输出流:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流。
  • 字符输入流:以内存为基准,来自磁盘文件/网络中的数据以字符的形式读入到内存中去的流。
  • 字符输出流:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络介质中去的流。

IO流的体系:

20240222-041004-Bf.png

FileinputStream(文件字节输入流)

作用:以内存为基准,可以把磁盘文件中的数据以字节的形式读入到内存中去。
20240222-041225-tI.png

FileOutputStream(文件字节输出流)

作用:以内存为基准,把内存中的数据以字节的形式写出到文件中去。20240222-050152-3K.png

文件写入换行符:os.write("/r/n”.getBytes());

文件复制

20240222-143944-i8.png

代码实现:

package cn.kt.FileAndIO;

import java.io.*;

public class FileCopy {
    public static void main(String[] args) {
        InputStream is = null;
        OutputStream os = null;
        try {
            // 需求:复制照片。
            //1、创建一个字节输入流管道与源文件接通
            is = new FileInputStream("/Users/mac/Downloads/hzw.jpg");
            //2、创建一个字节输出流管道与目标文件接通。
            os = new FileOutputStream("/Users/mac/tao/hzw.jpg");
            //3、创建一个字节数组,负责转移字节数据。
            byte[] buffer = new byte[1024];// 1KB.
            //4、从字节输入流中读取宁节数据,写出去到宁节输出流中。读多少写出去多少。
            int len;    // 记住每次读取了多少个字节。
            while ((len = is.read(buffer)) != -1) {
                os.write(buffer, 0, len);
            }
            System.out.println("复制完成!!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (os != null) os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (is != null) is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

注意:字节流非常话合做一切文件的复制操作
任何文件的底层都是字节,字节流做复制,是一字不漏的转移完全部字节,只要复制后的文件格式一致就没问题!

资源释放

JDK 7开始提供了更简单的资源释放方案:try-with-resource
20240222-152716-Os.png

代码示例:

package cn.kt.FileAndIO;

import java.io.*;

/**
 * 描述: 复制照片。
 */
public class FileCopy {
    public static void main(String[] args) {
        try (
                //1、创建一个字节输入流管道与源文件接通
                InputStream is = new FileInputStream("/Users/mac/Downloads/hzw.jpg");
                //2、创建一个字节输出流管道与目标文件接通。
                OutputStream os = new FileOutputStream("/Users/mac/tao/hzw.jpg");
                // 注意:这里只能放置资源对象。(流对象)
                // 什么是资源呢?资源都是会实现AutoCloseable接口
        ) {

            //3、创建一个字节数组,负责转移字节数据。
            byte[] buffer = new byte[1024];// 1KB.
            //4、从字节输入流中读取宁节数据,写出去到宁节输出流中。读多少写出去多少。
            int len;    // 记住每次读取了多少个字节。
            while ((len = is.read(buffer)) != -1) {
                os.write(buffer, 0, len);
            }
            System.out.println("复制完成!!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

代码相对简洁很多

FileReader(文件字符输入流)

作用:以内存为基准,可以把文件中的数据以字符的形式读入到内存中去。

20240222-153237-sE.png

FileWriter(文件字符输出流)

作用:以内存为基准,把内存中的数据以字符的形式写出到文件中去。
20240222-154058-wI.png

字符输出流使用时的注意事项
字符输出流写出数据后,必须刷新流,或者关闭流,写出去的数据才能生效
20240222-154945-pw.png

字节流、字符流的使用场景小结:
字节流适合做一切文件数据的拷贝(音视频,文本);字节流不适合读取中文内容输出。
字符流适合做文本文件的操作(读,写)。

缓冲流

20240222-160144-0G.png

字节缓冲流

提高字节流读写数据的性能
原理:字节缓冲输入流自带了8KB缓冲池;字节缓冲输出流也自带了8KB缓冲池。

20240222-155544-Bg.png
20240222-155840-nI.png

字符缓冲流

  1. BufferedReader(字符缓冲输入流)
    作用:自带8K(8192)的字符缓冲池,可以提高字符输入流读取字符数据的性能。

20240222-160545-Id.png

  1. BufferedWriter(字符缓冲输出流)
    作用:自带8K的字符缓冲池,可以提高字符输出流写字符数据的性能。

20240222-162236-0q.png

原始流、缓冲流的性能分析

测试代码:

package cn.kt.FileAndIO;

import java.io.*;

/**
 * Created by tao.
 * Date: 2024/2/22 16:44
 * 描述: 观察原始流和缓冲流的性能。
 */

public class BufferStreamTimeTest {
    // 复制的视频路径
    private static final String SRC_FILE = "D:\\resource\\线程池.avi";
    // 复制到哪个目的地
    private static final String DEST_FILE = "D:\\";

    public static void main(String[] args) {
        // copy01(); // 低级字节流一个一个字节的赋值,慢的简直让人无法忍受,直接淘汰!
        copy02();// 低级的字节流流按照一个一个字节数组的形式复制,速度较慢!
        // copy03(); // 缓冲流按照一个一个字节的形式复制,速度较慢,直接淘汰!
        copy04(); // 缓冲流按照一个一个字节数组的形式复制,速度极快,推荐使用!
    }

    private static void copy01() {
        long startTime = System.currentTimeMillis();
        try (
                InputStream is = new FileInputStream(SRC_FILE);
                OutputStream os = new FileOutputStream(DEST_FILE + "1.avi");
        ) {

            int b;
            while ((b = is.read()) != -1) {
                os.write(b);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("低级字节流一个一个字节复制耗时:" + (endTime - startTime) / 1000.0 + "s");
    }

    private static void copy02() {
        long startTime = System.currentTimeMillis();
        try (
                InputStream is = new FileInputStream(SRC_FILE);
                OutputStream os = new FileOutputStream(DEST_FILE + "2.avi");
        ) {
            byte[] buffer = new byte[1024 * 64];
            int len;
            while ((len = is.read(buffer)) != -1) {
                os.write(buffer, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("低级字节流使用字节数组复制耗时:" + (endTime - startTime) / 1000.0 + "s");
    }

    private static void copy03() {
        long startTime = System.currentTimeMillis();
        try (
                InputStream is = new FileInputStream(SRC_FILE);
                BufferedInputStream bis = new BufferedInputStream(is);
                OutputStream os = new FileOutputStream(DEST_FILE + "3.avi");
                BufferedOutputStream bos = new BufferedOutputStream(os);
        ) {

            int b;
            while ((b = bis.read()) != -1) {
                bos.write(b);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("缓冲流一个一个字节复制耗时:" + (endTime - startTime) / 1000.0 + "s");
    }

    private static void copy04() {
        long startTime = System.currentTimeMillis();
        try (
                InputStream is = new FileInputStream(SRC_FILE);
                BufferedInputStream bis = new BufferedInputStream(is, 64 * 1024);
                OutputStream os = new FileOutputStream(DEST_FILE + "4.avi");
                BufferedOutputStream bos = new BufferedOutputStream(os, 64 * 1024);
        ) {
            byte[] buffer = new byte[1024 * 64]; // 32KB
            int len;
            while ((len = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("缓冲流使用字节数组复制耗时:" + (endTime - startTime) / 1000.0 + "s");
    }
}

测试一:

分别使用原始的字节流,以及字节缓冲流复制一个很大视频(889MB)。
测试步骤:

  1. 使用低级的字节流按照一个一个字节的形式复制文件。
  2. 使用低级的字节流按照字节数组的形式复制文件。
  3. 使用高级的缓冲字节流按照一个一个字节的形式复制文件。
  4. 使用高级的缓冲字节流按照字节数组的形式复制文件。

默认情况测试结果:

  1. 低级流一个字节复制: 慢得简直让人无法忍受
  2. 低级流按照字节数组复制(数组长度1024): 12.117s
  3. 缓冲流一个字节复制: 11.058s
  4. 缓冲流按照字节数组复制(数组长度1024): 2.163s

经过上面的测试,我们可以得出一个结论:默认情况下,采用一次复制1024个字节,缓冲流完胜。

测试二:

但是,缓冲流就一定性能高吗?我们采用一次复制8192个字节试

1. 低级流按照字节数组复制(数组长度8192): 2.535s
2. 缓冲流按照字节数组复制(数组长度8192): 2.088s

经过上面的测试,我们可以得出一个结论:一次读取8192个字节时,低级流和缓冲流性能相当。相差的那几毫秒可以忽略不计。

测试三:

继续把数组变大,看一看缓冲流就一定性能高吗?现在采用一次读取1024*32个字节数据试试(缓冲流底层的字节数组也设成32k)

1. 低级流按照字节数组复制(数组长度32k): 1.128s
2. 缓冲流按照字节数组复制(数组长度32k): 1.133s

测试四:

继续把数组变大,看一看缓冲流就一定性能高吗?现在采用一次读取1024*64个字节数据试试

1. 低级流按照字节数组复制(数组长度64k): 1.039s
2. 缓冲流按照字节数组复制(数组长度64k): 1.151s

此时你会发现,当数组大到一定程度,性能已经提高不了多少了,甚至缓冲流的性能还没有低级流高。
在实际开发中,想提升读写性能就扩大数组大小,大小取决于经验,并且缓冲流的性能不一定就比低级流好。

转换流

解决的问题:不同编码读取出现乱码的问题

  1. 如果代码编码和被读取的文本文件的编码是一致的,使用字符流读取文本文件时不会出现乱码!
  2. 如果代码编码和被读取的文本文件的编码是不一致的,使用字符流读取文本文件时就会出现乱码!
    20240222-174932-ZD.png

InputStreamReader (字符输入转换流)

解决不同编码时,字符流读取文本内容乱码的问题。
解决思路:先获取文件的原始字节流,再将其按真实的字符集编码转成字符输入流,这样字符输入流中的字符就不乱码了。

20240222-175337-6U.png
InputStreamReader也是不能单独使用的,它内部需要封装一个InputStream的子类对象,再指定一个编码表,如果不指定编码表,默认会按照UTF-8形式进行转换。我们可以先准备一个GBK格式的文件,然后使用下面的代码进行读取是不会有乱码的。
代码示例:

public class InputStreamReaderTest {
    public static void main(String[] args) {
        try (
                // 1、得到文件的原始字节流(GBK的字节流形式)
                // 2、把原始的字节输入流按照指定的字符集编码转换成字符输入流
                Reader isr = new InputStreamReader(new FileInputStream("io-app2/src/06.txt"), "GBK");//多态写法
                // 3、把字符输入流包装成缓冲字符输入流
                BufferedReader br = new BufferedReader(isr);
                ){
            String line;
            while ((line = br.readLine()) != null){
                System.out.println(line);
            }


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

OutputStreamWriter(字符输出转换流)

作用:可以控制写出去的字符使用什么字符集编码。
解决思路:获取字节输出流,再按照指定的字符集编码将其转换成字符输出流,以后写出去的字符就会用该字符集编码了。

20240222-180125-u2.png

OutputStreamReader也是不能单独使用的,它内部需要封装一个OutputStream的子类对象,再指定一个编码表,如果不指定编码表,默认会按照UTF-8形式进行转换。我们可以先准备一个GBK格式的文件,使用下面代码往文件中写字符数据。
代码示例:

public class OutputStreamWriterTest2 {
    public static void main(String[] args) {
        // 指定写出去的字符编码。
        try (
                // 1、创建一个文件字节输出流
                OutputStream os = new FileOutputStream("io-app2/src/out.txt");
                // 2、把原始的字节输出流,按照指定的字符集编码转换成字符输出转换流。
                Writer osw = new OutputStreamWriter(os, "GBK");
                // 3、把字符输出流包装成缓冲字符输出流
                BufferedWriter bw = new BufferedWriter(osw);
                ){
            bw.write("我是中国人abc");
            bw.write("我爱你中国123");

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

打印流(PrintStream/PrintWriter)

20240222-180711-Y6.png

作用:打印流可以实现更方便、更高效的打印数据出去,能实现打印啥出去就是啥出去。

字节打印流

20240222-181147-0W.png

字符打印流

20240222-181749-DR.png

代码示例:

public class PrintTest1 {
    public static void main(String[] args) {
        try (
                // 1、创建一个打印流管道
//                PrintStream ps =
//                        new PrintStream("io-app2/src/itheima08.txt", Charset.forName("GBK"));
//                PrintStream ps =
//                        new PrintStream("io-app2/src/itheima08.txt");
                PrintWriter ps =
                        new PrintWriter(new FileOutputStream("io-app2/src/itheima08.txt", true));
                        //注意 高级流不支持追加参数 所以想要追加的方式来写 就得先包装一个低级流并声明是追加型写入
                ){
                ps.print(97);	//文件中显示的就是:97而不是a
                ps.print('a'); //文件中显示的就是:a
                ps.println("我爱你中国abc");	//文件中显示的就是:我爱你中国abc
                ps.println(true);//文件中显示的就是:true
                ps.println(99.5);//文件中显示的就是99.5

                ps.write(97); //文件中显示a,发现和前面println方法的区别了吗?println自带换行

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

其实打印流我们一直在使用,只是没有感受到而已。打印流可以实现更加方便,更加高效的写数据的方式。
这里所说的打印其实就是写数据的意思,它和普通的write方法写数据还不太一样,一般会使用打印流特有的方法叫print(数据)或者println(数据),它的特点是打印啥就输出啥,并且内部底层自己封装了缓冲流,所以性能也不差。

PrintStream和Printwriter的区别

  1. 打印数据的功能上是一模一样的:都是使用方便,性能高效(核心优势)
  2. PrintStream继承自字节输出流OutputStream,因此支持写字节数据的方法。
  3. PrintWriter继承自字符输出流Writer,因此支持写字符数据出去。

重定向输出语句

System.out.println()这句话表示打印输出,但是至于为什么能够输出,其实我们一直不清楚。
其实是因为System里面有一个静态变量叫out,out的数据类型就是PrintStream,它就是一个打印流,而且这个打印流的默认输出目的地是控制台,所以我们调用System.out.pirnln()就可以往控制台打印输出任意类型的数据,而且打印啥就输出啥。

而且System还提供了一个方法,可以修改底层的打印流,这样我们就可以重定向打印语句的输出目的地了。示例代码如下:

public class PrintTest2 {
    public static void main(String[] args) {
        System.out.println("老骥伏枥");
        System.out.println("志在千里");

        try ( PrintStream ps = new PrintStream("io-app2/src/09.txt"); ){
            // 把系统默认的打印流对象改成自己设置的打印流
            System.setOut(ps);

            System.out.println("烈士暮年");	
            System.out.println("壮心不已");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

此时打印语句,将往文件中打印数据,而不在控制台。

数据流

20240222-184547-3S.png

我们想把数据和数据的类型一并写到文件中去,读取的时候也将数据和数据类型一并读出来。这就可以用到数据流,有两个DataInputStream和DataOutputStream.

DataOutputStream(数据输出流)

DataOutputStream类,它也是一种包装流,创建DataOutputStream对象时,底层需要依赖于一个原始的OutputStream流对象。然后调用它的wirteXxx方法,写的是特定类型的数据。
20240222-185020-T4.png
示例代码如下:往文件中写整数、小数、布尔类型数据、字符串数据

public class DataOutputStreamTest1 {
    public static void main(String[] args) {
        try (
                // 1、创建一个数据输出流包装低级的字节输出流
                DataOutputStream dos =
                        new DataOutputStream(new FileOutputStream("io-app2/src/10out.txt"));
                ){
            dos.writeInt(97);
            dos.writeDouble(99.5);
            dos.writeBoolean(true);
            dos.writeUTF("666");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
//运行完之后666前面显示的是乱码,但其实不是发生了编码错误,而是一种特定的数据存储方式
//因为本来就不是存给人看的,而是方便下次读取的

DatalnputStream(数据输入流)

DataIntputStream类,它也是一种包装流,用于读取数据输出流输出的数据。创建DataInputStream对象时,底层需要依赖于一个原始的InputStream流对象。然后调用它的readXxx()方法就可以读取特定类型的数据。
20240222-185309-Rg.png

示例代码如下:读取文件中特定类型的数据(整数、小数、字符串等)

public class DataInputStreamTest2 {
    public static void main(String[] args) {
        try (
                DataInputStream dis =
                        new DataInputStream(new FileInputStream("io-app2/src/10out.txt"));//文件是上面数据输出流输出的文件
                ){
            int i = dis.readInt();
            System.out.println(i);//97

            double d = dis.readDouble();
            System.out.println(d);//99.5

            boolean b = dis.readBoolean();
            System.out.println(b);//true

            String rs = dis.readUTF();
            System.out.println(rs);//"666"
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意输出的时候读取文件的数据类型要和写入文件的数据类型相同,否则会出问题。

序列化流

20240222-190021-7s.png

序列化流是干什么用的呢? 我们知道字节流是以字节为单位来读写数据、字符流是按照字符为单位来读写数据、而对象流是以对象为单位来读写数据。也就是把对象当做一个整体,可以写一个对象到文件,也可以从文件中把对象读取出来。这里有一个新词 序列化
序列化:意思就是把对象写到文件或者网络中去。(简单记:写对象)
反序列化:意思就是把对象从文件或者网络中读取出来。(简单记:读对象)

ObjectOutputStream(对象字节输出流)

ObjectOutputStream流也是一个包装流,不能单独使用,需要结合原始的字节输出流使用,可以把Java对象进行序列化:把Java对象存入到文件中去。

20240222-190353-Xy.png

代码如下:将一个User对象写到文件中去
第一步:先准备一个User类,必须让其实现Serializable接口。

// 注意:对象如果需要序列化,必须实现序列化接口。
public class User implements Serializable {
    private String loginName;
    private String userName;
    private int age;
    // transient 这个成员变量将不参与序列化,即某个对象被写到文件后再读取,这个字段就是null了。
    private transient String passWord;

    public User() {
    }

    public User(String loginName, String userName, int age, String passWord) {
        this.loginName = loginName;
        this.userName = userName;
        this.age = age;
        this.passWord = passWord;
    }

    @Override
    public String toString() {
        return "User{" +
                "loginName='" + loginName + '\'' +
                ", userName='" + userName + '\'' +
                ", age=" + age +
                ", passWord='" + passWord + '\'' +
                '}';
    }
}

注意:transient 这个成员变量将不参与序列化,即某个对象被写到文件后再读取,这个字段就是null了。

第二步:再创建ObjectOutputStream流对象,调用writeObject方法对象到文件。

public class Test1ObjectOutputStream {
    public static void main(String[] args) {
        try (
                // 2、创建一个对象字节输出流包装原始的字节 输出流。
                ObjectOutputStream oos =
                        new ObjectOutputStream(new FileOutputStream("io-app2/src/11out.txt"));
                ){
            // 1、创建一个Java对象。
            User u = new User("admin", "张三", 32, "666888xyz");

            // 3、序列化对象到文件中去
            oos.writeObject(u);
            System.out.println("序列化对象成功!!");

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

注意:写到文件中的对象,是不能用记事本打开看的。因为对象本身就不是文本数据,打开是乱码,这里必须用反序列化,自己写代码读。
20240222-190814-WV.png

ObjectinputStream(对象字节输入流)

ObjectInputStream流,它也是一个包装流,不能单独使用,需要结合原始的字节输入流使用,可以读任意的java对象。

20240222-190932-0h.png

接着前面的案例,文件中已经有一个User对象,现在要使用ObjectInputStream读取出来。称之为反序列化。

public class Test2ObjectInputStream {
    public static void main(String[] args) {
        try (
            // 1、创建一个对象字节输入流管道,包装 低级的字节输入流与源文件接通
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("io-app2/src/itheima11out.txt"));
        ){
            User u = (User) ois.readObject();
            System.out.println(u);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

如果要一次序列化多个对象,怎么做?

用一个ArrayList集合存储多个对象,然后直接对集合进行序列化即可
注意:ArrayList集合已经实现了序列化接口!

IO框架

为了简化对IO操作,很多技术大牛或者组织提供了一些有关IO流小框架,可以提高IO流的开发效率。

如:

  1. 常用的hutool:https://hutool.cn/docs/#/core/IO/%E6%A6%82%E8%BF%B0
  2. apache开源基金组织提供了一组有关IO流小框架:commons-io

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[rospack] Error: package ‘moveit_setup_assistant‘ not found解决方法

执行:rosrun moveit_setup_assistant moveit_setup_assistant 显示报错:[rospack] Error: package ‘moveit_setup_assistant’ not found 这是由于没有安装moveit的包,所以找不到。 解决方法就是安装moveit包: sudo apt-get in…

【XR806开发板试用】踩坑deepin20,回归ubuntu,开发环境

首先,感谢给予XR806的试用机会。由于之前使用过deepin进行过开发,使用了很长的一段时间,印象还是不错的,所以,这次就想用deepin进行xr806的开发,但过于折腾。 sudo 安装 1. sudo apt-get install build-e…

128 Linux 系统编程6 ,C++程序在linux 上的调试,GDB调试

今天来整理 GDB 调试。 在windows 上我们使用vs2017开发,可以手动的加断点,debug。 那么在linux上怎么加断点,debug呢?这就是今天要整理的GDB调试工具了。 那么有些同学可能会想到:我们在windows上开发,…

Threejs 实现3D影像地图,Json地图,地图下钻

1.使用threejs实现3D影像地图效果,整体效果看起来还可以,底层抽象了基类,实现了通用,对任意省份,城市都可以只替换数据,即可轻松实现效果。 效果如下: 链接https://www.bilibili.com/video/BV1…

nginx 模块 常见内置变量 location

一、nginx 模块 ngx_http_core_module 核心模块 ngx_http_access_module 访问控制模块 deny allow ngx_http_auth_basic_module 身份验证 小红小名(虚拟用户) ftp也有虚拟用户 ngx_http_gzip_module 压缩模块 ngx_http_gzip_static_modul…

LeetCode二叉树中的第 K 大层和

题目描述 给你一棵二叉树的根节点 root 和一个正整数 k 。 树中的 层和 是指 同一层 上节点值的总和。 返回树中第 k 大的层和(不一定不同)。如果树少于 k 层,则返回 -1 。 注意,如果两个节点与根节点的距离相同,则…

互联网广告投放与IP地理位置定位

随着互联网的发展和普及,互联网广告投放成为各行业推广营销的重要方式之一。而结合IP地理位置定位技术,可以实现精准定向,提高广告投放的效果和精准度。IP数据云将探讨互联网广告投放与IP地理位置定位的关系,分析其优势和应用场景…

Android LinearLayout 如何让子元素靠下居中对齐 center bottom

Android LinearLayout 如何让子元素靠下居中对齐 center bottom 首先你需要知道两个知识点: android:layout_gravity 指定的是当前元素在父元素中的位置android:gravity 指定的是当前元素子元素的排布位置 比如: 有这么一个布局,我需要让…

【Java程序设计】【C00279】基于Springboot的智慧外贸平台(有论文)

基于Springboot的智慧外贸平台(有论文) 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的智慧外贸平台 本系统分为系统功能模块、管理员功能模块、买家功能模块以及商家功能模块。 系统功能模块:在平台首页可以…

snowflake雪花算法

snowflake雪花算法 雪花算法是由Twitter开源的由64位整数组成分布式ID,性能较高,并且在单机上递增。 为什么叫分布式ID生成器? 因为10bit-工作机器id。 雪花算法(Snowflake Algorithm)之所以适用于分布式系统&#…

opengl 学习纹理

一.纹理是什么? 纹理是一个2D图片(甚至也有1D和3D的纹理),它可以用来添加物体的细节;类似于图像一样,纹理也可以被用来储存大量的数据,这些数据可以发送到着色器上。 采样是指用纹理坐标来获取纹…

代码随想录算法训练营第50天|123.买卖股票的最佳时机III、188.买卖股票的最佳时机IV

文章目录 123.买卖股票的最佳时机III思路代码 188.买卖股票的最佳时机IV思路代码 123.买卖股票的最佳时机III 题目链接:123.买卖股票的最佳时机III 文章讲解:代码随想录|123.买卖股票的最佳时机III 视频讲解:123.买卖股票的最佳时机III 思路 …

7分钟0基础彻底理解常用数据压缩原理---哈夫曼编码

前言 如果你之前没有做过数据压缩,或者想要了解数据压缩的原理,那么这编文章将会帮到你。这编文章将会带你彻底了解哈夫曼编码原理,这种编码方式常用作的图片无损压缩,和ZIP的等压缩存储。 思考,计算机的存储与解析获…

消息中间件篇之RabbitMQ-延时队列

一、延时队列 延迟队列:进入队列的消息会被延迟消费的队列。 场景:超时订单、限时优惠、定时发布。 延迟队列死信交换机TTL(生存时间)。 二、死信交换机 当一个队列中的消息满足下列情况之一时,可以成为死信&#xf…

LVS的工作模式及其原理

1、LVS 介绍 (1)LVS 是Linux Virtual Server的简称,也就是 Linux 虚拟服务器, 是一个由章文嵩博士发起的自由软件项目,它的官方站点是www.linuxvirtualserver.org。现在LVS已经是 Linux标准内核的一部分,因此性能较高…

注册中心 Service Discovery --- Intro

注册中心 Service Discovery --- Intro 为什么需要注册中心注册中心的原理常用的注册中心注册中心的高可用 为什么需要注册中心 在微服务架构中,系统被拆分成了若干个独立的服务,因此服务之间需要进行通信和协作。为了实现服务的发现和调用,需…

一周学会Django5 Python Web开发-Django5二进制文件下载响应

锋哥原创的Python Web开发 Django5视频教程: 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计25条视频,包括:2024版 Django5 Python we…

CrossOver 24.0.0 for mac 震撼发布 2024最新下载安装详细图文教程

2024年2 月 23 日消息,CodeWeavers 近日发布了 CrossOver 24 版本更新,基于近期发布的 Wine 9.0,不仅兼容更多应用和游戏,还初步支持运行 32 位应用程序。 苹果在 macOS Catalina 系统中移除对 32 位软件的支持之后,在…

08 Redis之集群的搭建和复制原理+哨兵机制+CAP定理+Raft算法

5 Redis 集群 2.8版本之前, Redis采用主从集群模式. 实现了数据备份和读写分离 2.8版本之后, Redis采用Sentinel哨兵集群模式 , 实现了集群的高可用 5.1 主从集群搭建 首先, 基本所有系统 , “读” 的压力都大于 “写” 的压力 Redis 的主从集群是一个“一主多从”的读写分…

K线实战分析系列之三:吞没形态

K线实战分析系列之三:吞没形态 一、吞没形态二、看涨吞没形态三、看跌吞没形态四、吞没形态判别标准 一、吞没形态 两根或两根以上的K线形成的组合形态,吞没形态就是一种主要的反转形态。 这个形态由两根K线组成,前短后长,一阴一…