【Java IO 源码详解】: InputStream

本文主要从JDK 11 源码角度分析InputStream。

Java IO - 源码: InputStream

  • InputStream 类实现关系
  • InputStream 抽象类
  • 源码实现
    • InputStream
    • FilterInputStream
    • ByteArrayInputStream
    • BufferedInputStream
  • 参考文章

InputStream 类实现关系

InputStream是输入字节流,具体的实现类层次结构如下:

在这里插入图片描述

InputStream 抽象类

InputStream 类重要方法设计如下:

// 读取下一个字节,如果没有则返回-1
public abstract int read() // 将读取到的数据放在 byte 数组中,该方法实际上调用read(byte b[], int off, int len)方法
public int read(byte b[]) // 从第 off 位置读取<b>最多(实际可能小于)</b> len 长度字节的数据放到 byte 数组中,流是以 -1 来判断是否读取结束的; 此方法会一直阻止,直到输入数据可用、检测到stream结尾或引发异常为止。
public int read(byte b[], int off, int len) // JDK9新增:读取 InputStream 中的所有剩余字节,调用readNBytes(Integer.MAX_VALUE)方法
public byte[] readAllBytes()// JDK11更新:读取 InputStream 中的剩余字节的指定上限大小的字节内容;此方法会一直阻塞,直到读取了请求的字节数、检测到流结束或引发异常为止。此方法不会关闭输入流。
public byte[] readNBytes(int len)// JDK9新增:从输入流读取请求的字节数并保存在byte数组中; 此方法会一直阻塞,直到读取了请求的字节数、检测到流结束或引发异常为止。此方法不会关闭输入流。
public int readNBytes(byte[] b, int off, int len)// 跳过指定个数的字节不读取
public long skip(long n) // 返回可读的字节数量
public int available() // 读取完,关闭流,释放资源
public void close() // 标记读取位置,下次还可以从这里开始读取,使用前要看当前流是否支持,可以使用 markSupport() 方法判断
public synchronized void mark(int readlimit) // 重置读取位置为上次 mark 标记的位置
public synchronized void reset() // 判断当前流是否支持标记流,和上面两个方法配套使用
public boolean markSupported() // JDK9新增:读取 InputStream 中的全部字节并写入到指定的 OutputStream 中
public long transferTo(OutputStream out

源码实现

梳理部分InputStream及其实现类的源码分析。

InputStream

InputStream抽象类源码如下:

public abstract class InputStream implements Closeable {// 当使用skip方法时,最大的buffer size大小private static final int MAX_SKIP_BUFFER_SIZE = 2048;// 默认的buffer sizeprivate static final int DEFAULT_BUFFER_SIZE = 8192;// JDK11中增加了一个nullInputStream,即空模式实现,以便可以直接调用而不用判空(可以看如下的补充说明)public static InputStream nullInputStream() {return new InputStream() {private volatile boolean closed;private void ensureOpen() throws IOException {if (closed) {throw new IOException("Stream closed");}}@Overridepublic int available () throws IOException {ensureOpen();return 0;}@Overridepublic int read() throws IOException {ensureOpen();return -1;}@Overridepublic int read(byte[] b, int off, int len) throws IOException {Objects.checkFromIndexSize(off, len, b.length);if (len == 0) {return 0;}ensureOpen();return -1;}@Overridepublic byte[] readAllBytes() throws IOException {ensureOpen();return new byte[0];}@Overridepublic int readNBytes(byte[] b, int off, int len)throws IOException {Objects.checkFromIndexSize(off, len, b.length);ensureOpen();return 0;}@Overridepublic byte[] readNBytes(int len) throws IOException {if (len < 0) {throw new IllegalArgumentException("len < 0");}ensureOpen();return new byte[0];}@Overridepublic long skip(long n) throws IOException {ensureOpen();return 0L;}@Overridepublic long transferTo(OutputStream out) throws IOException {Objects.requireNonNull(out);ensureOpen();return 0L;}@Overridepublic void close() throws IOException {closed = true;}};}// 读取下一个字节的数据,如果没有则返回-1public abstract int read() throws IOException;// 将读取到的数据放在 byte 数组中,该方法实际上调用read(byte b[], int off, int len)方法public int read(byte b[]) throws IOException {return read(b, 0, b.length);}// 从第 off 位置读取<b>最多(实际可能小于)</b> len 长度字节的数据放到 byte 数组中,流是以 -1 来判断是否读取结束的; 此方法会一直阻止,直到输入数据可用、检测到stream结尾或引发异常为止。public int read(byte b[], int off, int len) throws IOException {// 检查边界Objects.checkFromIndexSize(off, len, b.length);if (len == 0) {return 0;}// 读取下一个字节int c = read();if (c == -1) { // 读到stream末尾,则返回读取的字节数量为-1return -1;}b[off] = (byte)c;// i用来记录取了多少个字节int i = 1;try {// 循环读取for (; i < len ; i++) {c = read();if (c == -1) {// 读到stream末尾,则breakbreak;}b[off + i] = (byte)c;}} catch (IOException ee) {}// 返回读取到的字节个数return i;}// 分配的最大数组大小。// 由于一些VM在数组中保留一些头字,所以尝试分配较大的阵列可能会导致OutOfMemoryError(请求的阵列大小超过VM限制)private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;// JDK9新增:读取 InputStream 中的所有剩余字节,调用readNBytes(Integer.MAX_VALUE)方法public byte[] readAllBytes() throws IOException {return readNBytes(Integer.MAX_VALUE);}// JDK11更新:读取 InputStream 中的剩余字节的指定上限大小的字节内容;此方法会一直阻塞,直到读取了请求的字节数、检测到流结束或引发异常为止。此方法不会关闭输入流。public byte[] readNBytes(int len) throws IOException {// 边界检查if (len < 0) {throw new IllegalArgumentException("len < 0");}List<byte[]> bufs = null; // 缓存每次读取到的内容放到bufs,最后组装成resultbyte[] result = null; // 最后读取到的内容int total = 0;int remaining = len; // 剩余字节长度int n;do {byte[] buf = new byte[Math.min(remaining, DEFAULT_BUFFER_SIZE)];int nread = 0;// 读取到结束为止,读取大小n可能大于或小于缓冲区大小while ((n = read(buf, nread,Math.min(buf.length - nread, remaining))) > 0) {nread += n; remaining -= n;}if (nread > 0) {if (MAX_BUFFER_SIZE - total < nread) {throw new OutOfMemoryError("Required array size too large");}total += nread;if (result == null) {result = buf;} else {if (bufs == null) {bufs = new ArrayList<>();bufs.add(result);}bufs.add(buf);}}// 如果读不到内容(返回-1)或者没有剩余的字节,则跳出循环} while (n >= 0 && remaining > 0);if (bufs == null) {if (result == null) {return new byte[0];}return result.length == total ?result : Arrays.copyOf(result, total);}// 组装最后的resultresult = new byte[total];int offset = 0;remaining = total;for (byte[] b : bufs) {int count = Math.min(b.length, remaining);System.arraycopy(b, 0, result, offset, count);offset += count;remaining -= count;}return result;}// JDK9新增:从输入流读取请求的字节数并保存在byte数组中; 此方法会一直阻塞,直到读取了请求的字节数、检测到流结束或引发异常为止。此方法不会关闭输入流。public int readNBytes(byte[] b, int off, int len) throws IOException {Objects.checkFromIndexSize(off, len, b.length);int n = 0;while (n < len) {int count = read(b, off + n, len - n);if (count < 0)break;n += count;}return n;}// 跳过指定个数的字节不读取public long skip(long n) throws IOException {long remaining = n;int nr;if (n <= 0) {return 0;}int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);byte[] skipBuffer = new byte[size];while (remaining > 0) {nr = read(skipBuffer, 0, (int)Math.min(size, remaining));if (nr < 0) {break;}remaining -= nr;}return n - remaining;}// 返回可读的字节数量public int available() throws IOException {return 0;}// 读取完,关闭流,释放资源public void close() throws IOException {}// 标记读取位置,下次还可以从这里开始读取,使用前要看当前流是否支持,可以使用 markSupport() 方法判断public synchronized void mark(int readlimit) {}// 重置读取位置为上次 mark 标记的位置public synchronized void reset() throws IOException {throw new IOException("mark/reset not supported");}// 判断当前流是否支持标记流,和上面两个方法配套使用。默认是false,由子类方法重写public boolean markSupported() {return false;}// JDK9新增:读取 InputStream 中的全部字节并写入到指定的 OutputStream 中public long transferTo(OutputStream out) throws IOException {Objects.requireNonNull(out, "out");long transferred = 0;byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];int read;while ((read = this.read(buffer, 0, DEFAULT_BUFFER_SIZE)) >= 0) {out.write(buffer, 0, read);transferred += read;}return transferred;}

总结下JDK9的更新点

类 java.io.InputStream 中增加了新的方法来读取和复制 InputStream 中包含的数据。

  • readAllBytes:读取 InputStream 中的所有剩余字节。
  • readNBytes: 从 InputStream 中读取指定数量的字节到数组中。
  • transferTo:读取 InputStream 中的全部字节并写入到指定的 OutputStream 中 。
public class TestInputStream {private InputStream inputStream;private static final String CONTENT = "Hello World";@Beforepublic void setUp() throws Exception {this.inputStream =TestInputStream.class.getResourceAsStream("/input.txt");}@Testpublic void testReadAllBytes() throws Exception {final String content = new String(this.inputStream.readAllBytes());assertEquals(CONTENT, content);}@Testpublic void testReadNBytes() throws Exception {final byte[] data = new byte[5];this.inputStream.readNBytes(data, 0, 5);assertEquals("Hello", new String(data));}@Testpublic void testTransferTo() throws Exception {final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();this.inputStream.transferTo(outputStream);assertEquals(CONTENT, outputStream.toString());}
}
  • read(byte[], int, int)readNBytes(byte[], int, int)看似是实现的相同功能,为何会设计readNBytes方法呢?
  1. read(byte[], int, int)是尝试读到最多len个bytes,但是读取到的内容长度可能是小于len的。
  2. readNBytes(byte[], int, int) 会一直(while循环)查找直到stream尾为止

举个例子:如果文本内容是12345<end>, read(s,0,10)是允许返回123的, 而readNbytes(s,0,10)会一直(while循环)查找直到stream尾为止,并返回12345.

补充下JDK11为什么会增加nullInputStream方法的设计?即空对象模式

  • 空对象模式
    举个例子:
public class MyParser implements Parser {private static Action NO_ACTION = new Action() {public void doSomething() { /* do nothing */ }};public Action findAction(String userInput) {// ...if ( /* we can't find any actions */ ) {return NO_ACTION;}}
}

然后便可以始终可以这么调用,而不用再判断空了

ParserFactory.getParser().findAction(someInput).doSomething();

FilterInputStream

FilterInputStream 源码如下

public class FilterInputStream extends InputStream {// 被装饰的inputStreamprotected volatile InputStream in;// 构造函数,注入被装饰的inputStreamprotected FilterInputStream(InputStream in) {this.in = in;}// 本质是调用被装饰的inputStream的方法public int read() throws IOException {return in.read();}public int read(byte b[]) throws IOException {return read(b, 0, b.length);}public int read(byte b[], int off, int len) throws IOException {return in.read(b, off, len);}public long skip(long n) throws IOException {return in.skip(n);}public int available() throws IOException {return in.available();}public void close() throws IOException {in.close();}public synchronized void mark(int readlimit) {in.mark(readlimit);}public synchronized void reset() throws IOException {in.reset();}public boolean markSupported() {return in.markSupported();}
}

为什么被装饰的inputStream是volatile类型的?

请参看: 关键字: volatile详解

ByteArrayInputStream

ByteArrayInputStream源码如下

public class ByteArrayInputStream extends InputStream {// 内部保存的byte 数组protected byte buf[];// 读取下一个字节的数组下标,byte[pos]就是read获取的下个字节protected int pos;// mark的数组下标位置protected int mark = 0;// 保存的有效byte的个数protected int count;// 构造方法public ByteArrayInputStream(byte buf[]) {this.buf = buf;              this.pos = 0;this.count = buf.length;}// 构造方法,带offset的public ByteArrayInputStream(byte buf[], int offset, int length) {                this.buf = buf;this.pos = offset;this.count = Math.min(offset + length, buf.length);this.mark = offset;}// 从流中读取下一个字节,没有读取到返回 -1public synchronized int read() {return (pos < count) ? (buf[pos++] & 0xff) : -1;}// 从第 off 位置读取<b>最多(实际可能小于)</b> len 长度字节的数据放到 byte 数组中,流是以 -1 来判断是否读取结束的public synchronized int read(byte b[], int off, int len) {// 边界检查if (b == null) {throw new NullPointerException();} else if (off < 0 || len < 0 || len > b.length - off) {throw new IndexOutOfBoundsException();}if (pos >= count) {return -1;}int avail = count - pos;if (len > avail) {len = avail;}if (len <= 0) {return 0;}// 从buf拷贝到byte 数组b中System.arraycopy(buf, pos, b, off, len);pos += len;return len;}// 跳过指定个数的字节不读取public synchronized long skip(long n) {long k = count - pos;if (n < k) {k = n < 0 ? 0 : n;}pos += k;return k;}// 还有稍稍byte在buffer中未读取,即总的count 减去 当前byte位置public synchronized int available() {return count - pos;}// 支持mark所以返回truepublic boolean markSupported() { return true;}  // 在流中当前位置mark, readAheadLimit参数未使用    public void mark(int readAheadLimit) {            mark = pos;}// 重置流,即回到mark的位置public synchronized void reset() {pos = mark;}// 关闭ByteArrayInputStream不会产生任何动作public void close() throws IOException { }
}

BufferedInputStream

BufferedInputStream源码如下

public class BufferedInputStream extends FilterInputStream {// 默认的buffer大小private static int DEFAULT_BUFFER_SIZE = 8192;// 分配的最大数组大小。// 由于一些VM在数组中保留一些头字,所以尝试分配较大的阵列可能会导致OutOfMemoryError(请求的阵列大小超过VM限制)private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;// 内部保存在byte 数组中protected volatile byte buf[];// 关闭流的方法可能是异步的,所以使用原子AtomicReferenceFieldUpdater提供CAS无锁方式(可以解决CAS的ABA问题)来保证private static final AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =AtomicReferenceFieldUpdater.newUpdater(BufferedInputStream.class,  byte[].class, "buf");// 有效byte的大小protected int count;// 当前位置protected int pos;// 最后一次,调用mark方法,标记的位置protected int markpos = -1;/*** 该变量惟一入口就是mark(int readLimit),好比调用方法mark(1024),那么后面读取的数据若是* 超过了1024字节,那么这次mark就为无效标记,子类能够选择抛弃该mark标记,从头开始。不过具体实现* 跟具体的子类有关,在BufferedInputStream中,会抛弃mark标记,从新将markpos赋值为-1*/protected int marklimit;// 获取被装饰的streamprivate InputStream getInIfOpen() throws IOException {InputStream input = in;if (input == null)throw new IOException("Stream closed");return input;}// 获取实际内部的buffer数组private byte[] getBufIfOpen() throws IOException {byte[] buffer = buf;if (buffer == null)throw new IOException("Stream closed");return buffer;}// 构造函数,buffer是8kbpublic BufferedInputStream(InputStream in) {this(in, DEFAULT_BUFFER_SIZE);}// 构造函数,指定buffer大小public BufferedInputStream(InputStream in, int size) {super(in);if (size <= 0) {throw new IllegalArgumentException("Buffer size <= 0");}buf = new byte[size];}/*** 用更多的数据填充缓冲区,考虑到shuffling和其他处理标记的技巧,* 假设它是由同步方法调用的。该方法还假设所有数据已经被读入,因此pos >count。*/private void fill() throws IOException {// 得到内部缓冲区bufferbyte[] buffer = getBufIfOpen();// 没有mark的情况下, pos为0if (markpos < 0)pos = 0;            /* no mark: throw away the buffer */// pos >= buffer.length  buffer已经被读取完了 else if (pos >= buffer.length)  /* no room left in buffer */// markpos > 0  有标记,标记处在缓存中间if (markpos > 0) {  /* can throw away early part of the buffer */// 把buffer中,markpos到pos的部分移动到0-sz处,pos设置为sz,markpos为0int sz = pos - markpos;System.arraycopy(buffer, markpos, buffer, 0, sz);pos = sz;markpos = 0;// markpos已经为0了,marklimit比buffer.length小,再读取buffer已经没有地方了} else if (buffer.length >= marklimit) {// 清空缓存,清空标记,markpos为-1,pos为0markpos = -1;   /* buffer got too big, invalidate mark */pos = 0;        /* drop buffer contents */// markpos已经为0了,marklimit比buffer.length大,而buffer.length已经最大了,不能扩容} else if (buffer.length >= MAX_BUFFER_SIZE) {throw new OutOfMemoryError("Required array size too large");// markpos已经为0了,marklimit比buffer.length大} else {            /* grow buffer */// 建立一个长度为min(2*pos,marklimit,MAX_BUFFER_SIZE),的缓存数组,然后把原来0-pos移动到新数组的0-pos处int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?pos * 2 : MAX_BUFFER_SIZE;if (nsz > marklimit)nsz = marklimit;byte nbuf[] = new byte[nsz];System.arraycopy(buffer, 0, nbuf, 0, pos);// 用bufUpdater替换bufferif (!bufUpdater.compareAndSet(this, buffer, nbuf)) {// Can't replace buf if there was an async close.// Note: This would need to be changed if fill()// is ever made accessible to multiple threads.// But for now, the only way CAS can fail is via close.// assert buf == null;throw new IOException("Stream closed");}buffer = nbuf;}// 当前读取上限count为poscount = pos;// 从内部的输入流,读取pos到buffer.length部分,读取的字节数加到countint n = getInIfOpen().read(buffer, pos, buffer.length - pos);if (n > 0)count = n + pos;}// 读取bytepublic synchronized int read() throws IOException {// 说明当前buf[]数组大小不够了,须要fill()if (pos >= count) {fill();// 说明没有读取到任何数据if (pos >= count)return -1;}return getBufIfOpen()[pos++] & 0xff;}/*** Read characters into a portion of an array, reading from the underlying* stream at most once if necessary.*/private int read1(byte[] b, int off, int len) throws IOException {int avail = count - pos;if (avail <= 0) {// 当写入指定数组b的长度大小超过BufferedInputStream中核心缓存数组buf[]的大小而且 markpos < 0,那么就直接从数据流中读取数据给b数组,而不经过buf[]缓存数组,避免buf[]数组急剧增大if (len >= getBufIfOpen().length && markpos < 0) {return getInIfOpen().read(b, off, len);}fill();avail = count - pos;if (avail <= 0) return -1;}int cnt = (avail < len) ? avail : len;System.arraycopy(getBufIfOpen(), pos, b, off, cnt);pos += cnt;return cnt;}// 读取到byte数组b中public synchronized int read(byte b[], int off, int len)throws IOException{getBufIfOpen(); // Check for closed streamif ((off | len | (off + len) | (b.length - (off + len))) < 0) {throw new IndexOutOfBoundsException();} else if (len == 0) {return 0;}int n = 0;for (;;) {int nread = read1(b, off + n, len - n);if (nread <= 0)return (n == 0) ? nread : n;n += nread;if (n >= len)return n;// if not closed but no bytes available, returnInputStream input = in;if (input != null && input.available() <= 0)return n;}}// 跳过n个public synchronized long skip(long n) throws IOException {getBufIfOpen(); // Check for closed streamif (n <= 0) {return 0;}long avail = count - pos;if (avail <= 0) {// If no mark position set then don't keep in bufferif (markpos <0)return getInIfOpen().skip(n);// Fill in buffer to save bytes for resetfill();avail = count - pos;if (avail <= 0)return 0;}long skipped = (avail < n) ? avail : n;pos += skipped;return skipped;}// buf[]数组剩余字节数+输入流中剩余字节数public synchronized int available() throws IOException {int n = count - pos;int avail = getInIfOpen().available();return n > (Integer.MAX_VALUE - avail)? Integer.MAX_VALUE: n + avail;}// 标记位置,marklimit只有在这里才可以被赋值,readlimit表示mark()方法执行后,最多可以从流中读取的数据// 若是超过该字节大小,那么在fill()的时候,就会认为此mark()标记无效,从新将 markpos = -1,pos = 0public synchronized void mark(int readlimit) {marklimit = readlimit;markpos = pos;}// 重置位置public synchronized void reset() throws IOException {getBufIfOpen(); // 如果已经close, 则直接报错if (markpos < 0)throw new IOException("Resetting to invalid mark");pos = markpos;}// 支持mark, 所以返回truepublic boolean markSupported() {return true;}// 通过AtomicReferenceFieldUpdater的CAS无锁方式closepublic void close() throws IOException {byte[] buffer;while ( (buffer = buf) != null) {if (bufUpdater.compareAndSet(this, buffer, null)) {InputStream input = in;in = null;if (input != null)input.close();return;}// Else retry in case a new buf was CASed in fill()}}
}

AtomicReferenceFieldUpdater具体可以参考:JUC原子类: CAS, Unsafe和原子类详解

参考文章

JDK 11 源码

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

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

相关文章

LabVIEW机械臂轨迹跟踪控制

介绍了一个使用LabVIEW开发的机械臂轨迹跟踪控制系统。该系统的主要目标是实现对机械臂运动轨迹的精确控制&#xff0c;使其能够按照预定路径进行精确移动。此系统特别适用于需要高精度位置控制的场合&#xff0c;如自动化装配、精密操作等。 为了实现LabVIEW环境下的机械臂轨迹…

【SpringBoot3】集成Knife4j、springdoc-openapi作为接口文档

一、什么是springdoc-openapi Springdoc-openapi 是一个用于生成 OpenAPI&#xff08;之前称为 Swagger&#xff09;文档的库&#xff0c;专为 Spring Boot 应用程序设计。它可以根据你的 Spring MVC 控制器、REST 控制器和其他 Spring Bean 自动生成 OpenAPI 文档&#xff0c…

ElasticSearch重建/创建/删除索引操作 - 第501篇

历史文章&#xff08;文章累计500&#xff09; 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 《国内最全的Spring Boot系列之六》 E…

解决InputStream流无法重复使用的问题

一.需求 现在有个需求&#xff0c;要通过InputStream流先去判断文件类型&#xff0c;然后再上传文件&#xff0c;这样就会用到两次InputStream。 二.问题 这个功能之前的同事已经做了一版&#xff0c;一直以为是正常的&#xff0c;毕竟都很久了&#xff0c;但是我用的时候发…

自然语言处理 TF-IDF

✅作者简介&#xff1a;人工智能专业本科在读&#xff0c;喜欢计算机与编程&#xff0c;写博客记录自己的学习历程。 &#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&…

Cesium 问题:遇到加载Cesium时各组件飞出

致敬爱的读者&#xff1a;该问题出现后暂时未找到最优的解决方案&#xff0c;而是将所有组件状态均进行隐藏&#xff0c;大家如果有解决方案可以留言、评论大家一起探讨解决&#xff0c;欢迎大家踊跃说出自己的想法 文章目录 问题分析 问题 在加载 Cesium 时出现各组件的位置不…

论文笔记:多任务学习模型:渐进式分层提取(PLE)含pytorch实现

整理了RecSys2020 Progressive Layered Extraction : A Novel Multi-Task Learning Model for Personalized Recommendations&#xff09;论文的阅读笔记 背景模型代码 论文地址&#xff1a;PLE 背景 多任务学习&#xff08;multi-task learning&#xff0c;MTL&#xff09;&a…

防火墙路由

目录 1. 防火墙的智能选路 2. 策略路由 -- PBR 3. 智能选路 --- 全局路由策略 3.1 基于链路带宽的负载分担: 3.2 基于链路质量进行负载分担 3.3 基于链路权重进行负载分担 3.4 基于链路优先级的主备备份 1. 防火墙的智能选路 就近选路 --- 我们希望在访问不同运营商的服…

Vue2 通过.sync修饰符实现数据双向绑定

App.vue <template><div class"app"><buttonv-on:clickisShowtrue>退出按钮</button><BaseDialog:visible.syncisShow></BaseDialog></div> </template><script> import BaseDialog from "./components…

多符号表达式的共同子表达式提取教程

生成的符号表达式&#xff0c;可能会存在过于冗长的问题&#xff0c;且多个符号表达式中&#xff0c;有可能存在相同的计算部分&#xff0c;如果不进行处理&#xff0c;计算过程中会导致某些算式计算多次&#xff0c;从而影响计算效率。 那么多个符号表达式生成函数时&#xf…

[机器学习]KNN——K邻近算法实现

一.K邻近算法概念 二.代码实现 # 0. 引入依赖 import numpy as np import pandas as pd# 这里直接引入sklearn里的数据集&#xff0c;iris鸢尾花 from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split # 切分数据集为训练集和测试…

2024年数学建模美赛 分析与编程

2024年数学建模美赛 分析与编程 1、本专栏将在2024年美赛题目公布后&#xff0c;进行深入分析&#xff0c;建议收藏&#xff1b; 2、本专栏对2023年赛题&#xff0c;其它题目分析详见专题讨论&#xff1b; 2023年数学建模美赛A题&#xff08;A drought stricken plant communi…

JavaSE——运算符、运算符优先级、API、Scanner

目录 基本的算术运算符 自增自减运算符 赋值运算符 关系运算符 逻辑运算符 三目运算符 运算符优先级 API Scanner 基本的算术运算符 符号作用加-减*乘/除%取余 基本与C语言的基本算术运算符一致 注意&#xff1a;两个整数相除结果还是整数 public static void main…

C++PythonC# 三语言OpenCV从零开发(7):图像的阈值

文章目录 相关链接前言阈值阈值使用代码PythonCCsharpcsharp代码问题 总结 相关链接 C&Python&Csharp in OpenCV 专栏 【2022B站最好的OpenCV课程推荐】OpenCV从入门到实战 全套课程&#xff08;附带课程课件资料课件笔记&#xff09; OpenCV一个窗口同时显示多张图片 …

C 变量

目录 1. C变量 2. C变量定义 2.1 变量初始化 2.2 C中的变量声明 3. C中的左值&#xff08;Lvalues&#xff09;和右值&#xff08;Rvalues&#xff09; 1. C变量 在C语言中&#xff0c;变量可以根据其类型分为以下几种基本类型&#xff1a; 整型变量&#xff1a;用…

自然语言nlp学习 三

4-8 Prompt-Learning--应用_哔哩哔哩_bilibili Prompt Learning&#xff08;提示学习&#xff09;是近年来在自然语言处理领域中&#xff0c;特别是在预训练-微调范式下的一个热门研究方向。它主要与大规模预训练模型如GPT系列、BERT等的应用密切相关。 在传统的微调过程中&a…

将vite项目(vue/react)使用vite-plugin-pwa配置为pwa应用,只需要3分钟即可

将项目配置为pwa模式&#xff0c;就可以在浏览器里面看到安装应用的选项&#xff0c;并且可以将web网页像app一样添加到手机桌面或者pad桌面上&#xff0c;或者是电脑桌面上&#xff0c;这样带来的体验就像真的在一个app上运行一样。为了实现这个目的&#xff0c;我们可以为vue…

算法设计与分析实验:滑动窗口与二分查找

目录 一、寻找两个正序数组的中位数 1.1 具体思路 1.2 流程展示 1.3 代码实现 1.4 代码复杂度分析 1.5 运行结果 二、X的平方根 2.1 具体思路 2.2 流程展示 2.3 代码实现 2.4 代码复杂度分析 2.5 运行结果 三、两数之和 II-输入有序数组 3.1 采用二分查找的思想 …

LeetCode —— 43. 字符串相乘

&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️Take your time ! &#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️&#x1f636;‍&#x1f32b;️…

Bio-Rad(Abd serotec)独特性抗体

当一种抗体与另一种抗体的独特型结合时&#xff0c;它被称为抗独特型抗体。抗体的可变部分包括独特的抗原结合位点&#xff0c;称为独特型。独特型(即独特型)内表位的组合对于每种抗体都是独特的。 如今开发的大多数治疗性单克隆抗体是人的或人源化的&#xff0c;用于诱导抗药…