当前位置: 首页 > news >正文

List操作的一些常见问题

1. Arrays.asList转换基本类型数组

在实际的业务开发中,我们通常会进行数组转List的操作,通常我们会使用Arrays.asList来进行转换,但是在转换基本类型的数组的时候,却出现转换的结果和我们想象的不一致。

import java.util.Arrays;
import java.util.List;/*** Arrays.asList数组常见问题* @author 百里*/
public class BaiLiTestDemo {public static void main(String[] args) {int[] arr = {1, 2, 3};List list = Arrays.asList(arr);System.out.println("list.size:" + list.size());for (int i = 0; i < list.size(); i++) {System.out.println("循环打印:" + list.get(i));}}
}

观察下asList的实现,可以看到是入参是使用的是泛型,所以会将{1, 2, 3}三个整数放入一个泛型列表中返回。

public static List asList(T... a) {return new ArrayList<>(a); 
}


那我们该如何解决呢?只需要在声明数组的时候,声明类型改为包装类型。

import java.util.Arrays;
import java.util.List;/*** Arrays.asList数组常见问题* @author 百里*/
public class BaiLiTestDemo {public static void main(String[] args) {Integer[] arr = {1, 2, 3};List list = Arrays.asList(arr);System.out.println("list.size:" + list.size());//size = 3for (int i = 0; i < list.size(); i++) {System.out.println("循环打印:" + list.get(i));}}
}

这就是第一个坑了,然而Arrays.asList不止这一个需要注意的问题,我们继续往下看:

2. Arrays.asList返回的List不支持增删操作

我们接着上面的demo,增加list加减的逻辑,运行demo会提示UnsupportedOperationException:

import java.util.Arrays;
import java.util.List;/*** Arrays.asList数组常见问题* @author 百里*/
public class BaiLiTestDemo {public static void main(String[] args) {Integer[] arr = {1, 2, 3};List list = Arrays.asList(arr);System.out.println("list.size:" + list.size());list.add(4);}
}

为什么会这样?我们看下asList的实现,它返回的ArrayList是Arrays的内部类,而不是我们通常使用的java.util.ArrayList:


可以看到内部类中的ArrayList没有add()与remove(),那我们怎么可以使用增减方法呢,继续往下看:


可以看到ArrayList继承了AbstractList类,我们观察AbstractList类的add()与remove():


现在是不是就理解Arrays.asList返回的List不支持增删操作了。

3. 对原始数组的修改会影响到我们获得的那个List

基于第一个demo我们继续改造,修改原arr[0]=10,这个时候打印Arrays.asList返回的list值也发生了改变:

import java.util.Arrays;
import java.util.List;/*** Arrays.asList数组常见问题* @author 百里*/
public class BaiLiTestDemo {public static void main(String[] args) {Integer[] arr = {1, 2, 3};List list = Arrays.asList(arr);System.out.println("list.size:" + list.size());arr[0] = 10;//修改原数组for (int i = 0; i < list.size(); i++) {System.out.println("循环打印:" + list.get(i));}}
}

为什么呢?观察ArrayList的实现,可以知道asList创建了 ArrayList,但它直接引用原本的数据组对象。所以只要原本的数组对象一发生变化,List也跟着变化。
 


解决方案:new一个新的ArrayList装Arrays.asList返回数据。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;/*** Arrays.asList数组常见问题* @author 百里*/
public class BaiLiTestDemo {public static void main(String[] args) {Integer[] arr = {1, 2, 3};List list = new ArrayList<>(Arrays.asList(arr));arr[0] = 10;for (int i = 0; i < list.size(); i++) {System.out.println("循环打印:" + list.get(i));}}
}

4. ArrayList.subList强转ArrayList导致异常

当使用ArrayList.subList的返回list强转ArrayList时,会出现java.lang.ClassCastException,看以下代码:

import java.util.ArrayList;
import java.util.List;/*** ArrayList.subList常见问题* @author 百里*/
public class BaiLiArrayListDemo {public static void main(String[] args) {List<String> names = new ArrayList<String>() {{add("one");add("two");add("three");}};ArrayList strings = (ArrayList) names.subList(0, 1);System.out.println(strings);}
}
Exception in thread "main" java.lang.ClassCastException: java.util.ArrayList$SubList cannot be cast to java.util.ArrayListat BaiLiArrayListDemo.main(BaiLiArrayListDemo.java:15)

同样的,我们看下sublist的实现:
 


可以看到SubList()实际上没有创建一个新的List,而是直接引用了原来的List,指定了元素的范围。并且返回的是一个内部类实现的SubList对象,该对象只是原始ArrayList的一个引用,而不是一个全新的ArrayList,因此无法直接将其强制转换为ArrayList类型。
由于是引用的原List,因此也会存在asList的问题,也就是针对subList进行增减数据,会影响原List的值。

import java.util.ArrayList;
import java.util.List;/*** ArrayList.subList常见问题* @author 百里*/
public class BaiLiArrayListDemo {public static void main(String[] args) {List<String> names = new ArrayList<String>() {{add("one");add("two");add("three");}};List  strings = names.subList(0, 1);strings.add(0,"four");System.out.println(strings);//[four, one]System.out.println(names);//[four, one, two, three]}
}

需要注意修改原List-names的值会出导致strings的遍历、增加、删除产生ConcurrentModificationException异常。

import java.util.ArrayList;
import java.util.List;/*** ArrayList.subList常见问题* @author 百里*/
public class BaiLiArrayListDemo {public static void main(String[] args) {List<String> names = new ArrayList<String>() {{add("one");add("two");add("three");}};List strings = names.subList(0, 1);names.add("four");System.out.println(strings);System.out.println(names);}
}
Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1231)at java.util.ArrayList$SubList.listIterator(ArrayList.java:1091)at java.util.AbstractList.listIterator(AbstractList.java:299)at java.util.ArrayList$SubList.iterator(ArrayList.java:1087)at java.util.AbstractCollection.toString(AbstractCollection.java:454)at java.lang.String.valueOf(String.java:2994)at java.io.PrintStream.println(PrintStream.java:821)at BaiLiArrayListDemo.main(BaiLiArrayListDemo.java:17)

上面问题的解决方案跟asList同样,直接new一个新的ArrayList装Arrays.subList返回数据就可以了。

import java.util.ArrayList;
import java.util.List;/*** ArrayList.subList常见问题* @author 百里*/
public class BaiLiArrayListDemo {public static void main(String[] args) {List<String> names = new ArrayList<String>() {{add("one");add("two");add("three");}};List strings = new ArrayList<>(names.subList(0, 1));strings.add("four");System.out.println(strings);//[one, four]System.out.println(names);//[one, two, three]}
}

5. ArrayList中的subList切片造成OOM

subList所产生的List,其实是对原来List对象的引用,这个产生的List只是原来List对象的视图,也就是说虽然值切片获取了一小段数据,但是原来的List对象却得不到回收,如果这个原来的对象很大,就会出现OOM的情况。我们将VM参数调小:-Xms20m -Xmx40m

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;/*** ArrayList.subList常见问题* @author 百里*/
public class BaiLiArrayListDemo {public static void main(String[] args) {List data = new ArrayList<>();IntStream.range(0, 1000).forEach(i ->{List<Integer> collect = IntStream.range(0, 100000).boxed().collect(Collectors.toList());data.add(collect.subList(0, 1));});}
}

出现OOM的原因:原数组无法被回收,会一直在内存中。
解决方案:new一个新的ArrayList接收subList返回。

6.Copy-On-Write 是什么?

Copy-On-Write它是一种在计算机科学中常见的优化技术,主要应用于需要频繁读取但很少修改的数据结构上。
简单的说就是在计算机中就是当你想要对一块内存进行修改时,我们不在原有内存块中进行写操作,而是将内存拷贝一份,在新的内存中进行写操作,写完之后呢,就将指向原来内存指针指向新的内存,原来的内存就可以被回收掉了!
既然是一种优化策略,我们看一段代码:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;/**
* @author 百里
*/
public class BaiLiIteratorTest {private static List<String> list = new ArrayList<>();public static void main(String[] args) {list.add("1");list.add("2");list.add("3");Iterator<String> iter = list.iterator();while (iter.hasNext()) {System.err.println(iter.next());}System.err.println(Arrays.toString(list.toArray()));}
}

上面的Demo在单线程下执行时没什么毛病,但是在多线程的环境中,就可能出异常,为什么呢?
因为多线程迭代时如果有其他线程对这个集合list进行增减元素,会抛出java.util.ConcurrentModificationException的异常。
我们以增加元素为例子,运行下面这Demo:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 并发迭代器问题示例代码* @author 百里*/
public class BaiLiConcurrentIteratorTest {// 创建一个ArrayList对象private static List<String> list = new ArrayList<>();public static void main(String[] args) throws InterruptedException {// 给ArrayList添加三个元素:"1"、"2"和"3"list.add("1");list.add("2");list.add("3");// 开启线程池,提交10个线程用于在list尾部添加5个元素"121"ExecutorService service = Executors.newFixedThreadPool(10);for (int i = 0; i < 10; i++) {service.execute(() -> {for (int j = 0; j < 5; j++) {list.add("121");}});}// 使用Iterator迭代器遍历list并输出元素值Iterator<String> iter = list.iterator();for (int i = 0; i < 10; i++) {service.execute(() -> {while (iter.hasNext()) {System.err.println(iter.next());try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}});}service.shutdown();}
}

这里暴露的问题是什么呢?

  • 多线程场景下迭代器遍历集合的读取操作和其他线程对集合进行写入操作会导致出现并发修改异常

解决方案:

  • CopyOnWriteArrayList避免了多线程操作List线程不安全的问题

7.CopyOnWriteArrayList介绍

从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。CopyOnWrite容器非常有用,可以在非常多的并发场景中使用到。
CopyOnWriteArrayList原理:
在写操作(add、remove等)时,不直接对原数据进行修改,而是先将原数据复制一份,然后在新复制的数据上执行写操作,最后将原数据引用指向新数据。这样做的好处是读操作(get、iterator等)可以不加锁,因为读取的数据始终是不变的。
接下来我们就看下源码怎么实现的。

8.CopyOnWriteArrayList简单源码解读

add()方法源码:

/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {final ReentrantLock lock = this.lock;//重入锁lock.lock();//加锁啦try {Object[] elements = getArray();int len = elements.length;Object[] newElements = Arrays.copyOf(elements, len + 1);//拷贝新数组newElements[len] = e;setArray(newElements);//将引用指向新数组  1return true;} finally {lock.unlock();//解锁啦}
}

可以看到,CopyOnWriteArrayList中的写操作都需要先获取锁,然后再将当前的元素数组复制一份,并在新复制的元素数组上执行写操作,最后将数组引用指向新数组。

@SuppressWarnings("unchecked")
public E next() {if (! hasNext()) //是否存在下一个元素throw new NoSuchElementException(); //没有下一个元素,则会抛出NoSuchElementException异常//snapshot是一个类成员变量,它是在创建迭代器时通过复制集合内容而获得的一个数组。//cursor是另一个类成员变量,初始值为0,并在每次调用next()时自增1,表示当前返回元素的位置。return (E) snapshot[cursor++];
}

而读操作不需要加锁,直接返回当前的元素数组即可。
这种写时复制的机制保证了读操作的线程安全性,但是会牺牲一些写操作的性能,因为每次修改都需要复制一份数组。因此,适合读远多于写的场合。
所以我们将多线程Demo中的ArrayList改为CopyOnWriteArrayList,执行就不会报错啦!

import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/**
* 并发迭代器问题示例代码
* @author 百里
*/
public class BaiLiConcurrentIteratorTest {// 创建一个ArrayList对象private static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();public static void main(String[] args) throws InterruptedException {// 给ArrayList添加三个元素:"1"、"2"和"3"list.add("1");list.add("2");list.add("3");// 开启线程池,提交10个线程用于在list尾部添加5个元素"121"ExecutorService service = Executors.newFixedThreadPool(10);for (int i = 0; i < 10; i++) {service.execute(() -> {for (int j = 0; j < 5; j++) {list.add("121");}});}// 使用Iterator迭代器遍历list并输出元素值Iterator<String> iter = list.iterator();for (int i = 0; i < 10; i++) {service.execute(() -> {while (iter.hasNext()) {System.err.println(iter.next());try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}});}service.shutdown();}
}

9.CopyOnWriteArrayList优缺点

优点:

  1. 线程安全。CopyOnWriteArrayList是线程安全的,由于写操作对原数据进行复制,因此写操作不会影响读操作,读操作可以不加锁,降低了并发冲突的概率。
  1. 不会抛出ConcurrentModificationException异常。由于读操作遍历的是不变的数组副本,因此不会抛出ConcurrentModificationException异常。

缺点:

  1. 写操作性能较低。由于每一次写操作都需要将元素复制一份,因此写操作的性能较低。
  1. 内存占用增加。由于每次写操作都需要创建一个新的数组副本,因此内存占用会增加,特别是当集合中有大量数据时,内存占用较高。
  1. 数据一致性问题。由于读操作遍历的是不变的数组副本,因此在对数组执行写操作期间,读操作可能读取到旧的数组数据,这就涉及到数据一致性问题。

10.CopyOnWriteArrayList使用场景

  • 读多写少。为什么?因为写的时候会复制新集合
  • 集合不大。为什么?因为写的时候会复制新集合
  • 实时性要求不高。为什么,因为有可能会读取到旧的集合数据

相关文章:

List操作的一些常见问题

1. Arrays.asList转换基本类型数组 在实际的业务开发中&#xff0c;我们通常会进行数组转List的操作&#xff0c;通常我们会使用Arrays.asList来进行转换&#xff0c;但是在转换基本类型的数组的时候&#xff0c;却出现转换的结果和我们想象的不一致。 import java.util.Arra…...

如何使用Java和RabbitMQ实现延迟队列?

前言 今天我们使用Java和RabbitMQ实现消息队列的延迟功能。 前期准备&#xff0c;需要安装好docker、docker-compose的运行环境。 需要安装RabbitMQ的可以看下面这篇文章。 如何使用PHP和RabbitMQ实现消息队列&#xff1f;-CSDN博客 今天讲的是依赖RabbitMQ的延迟插件实现…...

AI论文速读 | TF-LLM:基于大语言模型可解释性的交通预测

论文标题&#xff1a; Explainable Traffic Flow Prediction with Large Language Models 作者&#xff1a;Xusen Guo, Qiming Zhang, Mingxing Peng, Meixin Zhu(朱美新)*, Hao (Frank)Yang(杨昊) 机构&#xff1a;香港科技大学&#xff08;广州&#xff09;&#xff0c;约翰…...

智慧矿山视频智能监控与安全监管方案

一、行业背景 随着全球能源需求的日益增长&#xff0c;矿业行业作为国民经济的重要支柱&#xff0c;其发展日益受到广泛关注。然而&#xff0c;传统矿山管理模式的局限性逐渐显现&#xff0c;如生产安全、人员监管、风险预警等方面的问题日益突出。因此&#xff0c;智慧矿山智…...

2024春算法训练4——函数与递归题解

一、前言 感觉这次的题目都很好&#xff0c;但是E题....&#xff08;我太菜了想不到&#xff09;&#xff0c;别人的题解都上百行了&#xff0c;晕&#xff1b; 二、题解 A-[NOIP2010]数字统计_2024春算法训练4——函数与递归 (nowcoder.com) 这种题目有两种做法&#xff1a;…...

【C++】C++知识点复习

牛客cpp&#xff1a;牛客网在线编程 2024年4月10日:BC1—>BC8 BC4&#xff1a;浮点数精度保留 问题&#xff1a;不加入fixed输入0.359813&#xff0c;最后得到0.36&#xff0c;并不是强制保留0.360。这种写法会保留小数点后三位精度&#xff0c;但是最后输出会省略掉最后…...

SpringBoot+Vue,轻松实现网页版人脸登录与精准识别

目录 1、技术介绍 2、技术原理 2.1、人脸检测 ①参考模板法 ②人脸规则法 2.2、人脸跟踪 2.3、人脸比对 ①特征向量法 ②面纹模板法 识别过程 案例 一、springboot后端项目 1&#xff0c;拉取项目后&#xff0c;导入相关依赖jar包 2&#xff0c;执行sql文件夹下面…...

深入浅出 -- 系统架构之垂直架构

当业务复杂度增加、访问量逐渐增大出现高并发时&#xff0c;单体架构无法满足需求&#xff0c;可以根据业务功能对系统进行拆分&#xff0c;以提高访问效率。 垂直架构介绍 1.垂直架构一般是因为单体架构太过于庞大而进行的拆分&#xff0c;拆分后各个系统应满足独立运行互相不…...

深入浅出 -- 系统架构之微服务架构选型参考图

技术选型架构图 是一个用于展示项目中所采用的各种技术和组件之间关系的图表。 它通常包括以下几个部分&#xff1a; 1. 项目名称和描述&#xff1a;简要介绍项目的背景和目标。 2. 技术栈&#xff1a;列出项目中使用的主要技术和工具&#xff0c;如编程语言、框架、数据库…...

Java 使用 ant.jar 执行 SQL 脚本文件

Java 使用 ant.jar 执行 SQL 脚本文件&#xff0c;很简单。 在 pom.xml 中导入 ant 依赖 <dependency><groupId>org.apache.ant</groupId><artifactId>ant</artifactId><version>1.10.11</version> </dependency>sql 脚本文件…...

【随笔】Git 高级篇 -- 快速定位分支 ^|~(二十三)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…...

git环境切换

文章目录 一. 操作步骤&#xff1a;1.查看全局设置3.Git 切换本地git设置4.切换仓库并推送 一. 操作步骤&#xff1a; 1.查看全局设置 $ Git config --global --list credential.https://codeup.aliyun.com.providergeneric user.namebiejiahao user.emailxxxxxxxxqq.com3.Gi…...

hyperf websocket

composer require hyperf/websocket-server 配置 Server 修改 config/autoload/server.php&#xff0c;增加以下配置。 <?phpreturn [servers > [[name > ws,type > Server::SERVER_WEBSOCKET,host > 0.0.0.0,port > 9502,sock_type > SWOOLE_SOCK_TCP…...

用Echarts词云数据可视化热词表白​​

目录 1、使用前准备 2、准备工作 3、盒子搭建 4、整体展现 1、使用前准备 找到表白对象&#xff08;重中之重&#xff01;&#xff09;&#xff0c;不要一见钟情&#xff08;个人觉得&#xff1a;一见钟情属于见色起意&#xff01;&#xff09;&#xff0c;因为数据可视化需…...

VUE 实现路由的基本原理

路由 基本概念 在前端技术早期&#xff0c;所有页面的跳转通过更改url,浏览器页面刷新获取新的页面内容&#xff0c;这种粗糙的交互方式&#xff0c;一直等待优化。 后来&#xff0c;改变发生了——Ajax 出现了&#xff0c;它允许人们在不刷新页面的情况下发起请求&#xff0…...

Android 11 添加系统属性

在初识Android 属性一文中提到&#xff0c;系统会默认加载以下文件 /system/etc/prop.default /system/build.prop /system_ext/build.prop /vendor/default.prop /vendor/build.prop /odm/etc/build.prop /product/build.prop /factory/factory.prop要弄清楚我们应该在哪里添…...

docker 创建容器过程

结合下图&#xff0c;本文讨论docker 创建容器过程&#xff1a; START└── [用户通过Docker Client发出指令]└── (1) docker run 或 docker create 命令├── (2) Docker Client与Docker Daemon建立通信连接└── (3) Docker Daemon接收到创建容器请求├── (4) 检查…...

OSI七层网络攻击行为及防范手段

2020年3月3日&#xff0c;360安全大脑披露美国中央情报局攻击组织&#xff08;APT-C-39&#xff09;对我国大型互联网公司、政府部门及相关企业进行长达11年的网络攻击渗透&#xff0c;该组织所使用的网络武器和CIA“Vault7”项目中的网络武器完全吻合。如今随着互联网技术的蓬…...

第100+5步 ChatGPT文献复现:ARIMAX预测肺结核 vol. 5

基于WIN10的64位系统演示 一、写在前面 我们继续往下看&#xff0c;首先例行回顾文章&#xff1a; 《PLoS One》杂志的2023年一篇题目为《A comparative study of three models to analyze the impact of air pollutants on the number of pulmonary tuberculosis cases in …...

论文| Convolutional Neural Network-based Place Recognition - 2014

2014-Convolutional Neural Network-based Place Recognition...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

vscode(仍待补充)

写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh&#xff1f; debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用

1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

蓝桥杯 冶炼金属

原题目链接 &#x1f527; 冶炼金属转换率推测题解 &#x1f4dc; 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V&#xff0c;是一个正整数&#xff0c;表示每 V V V 个普通金属 O O O 可以冶炼出 …...

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…...

动态 Web 开发技术入门篇

一、HTTP 协议核心 1.1 HTTP 基础 协议全称 &#xff1a;HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09; 默认端口 &#xff1a;HTTP 使用 80 端口&#xff0c;HTTPS 使用 443 端口。 请求方法 &#xff1a; GET &#xff1a;用于获取资源&#xff0c;…...

群晖NAS如何在虚拟机创建飞牛NAS

套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...