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

这一文,关于Java泛型的点点滴滴 一

作为一个 Java 程序员,用到泛型最多的,我估计应该就是这一行代码:

List<String> list = new ArrayList<>();

这也是所有 Java 程序员的泛型之路开始的地方啊。

不过本文讲泛型,先不从这里开始讲,而是再往前看一下,看一看没有泛型的时候,Java 代码是怎么写的,然后我们才会知道为什么要加入泛型,泛型代码该怎么写。

这里插播一下我的微信公众号,希望大家能够多多关注,我会不定期更新优秀的技术文章:

接下来,开始我们的正文。

为什么要设计泛型

提高代码重用性

没有泛型之前,我们写一个两数相加的函数:

public static int add(int a, int b) {return a + b;
}

看似没问题,对吧。不过这个时候我们想计算 float 类型的加法,那这个函数就不行了,因为他只能计算 int 值。此时就只能再加入一个相同的函数了:

public static float add(float a, float b) {return a + b;
}

现在我们有两个方法能够计算 int 和 float 类型的加法。那现在如果要计算 String 类型的加法呢,这两个方法就又不够用了。面对这样的需求,在没有泛型的支持下,我们只能不断地增加逻辑基本相同的方法,代码重用性极低。
这就是泛型要解决的第一个问题:提高代码重用性。
那在泛型的加持下,我们如何编写这个函数呢?

public static <T extends Number> double add(T a, T b) {return a.doubleValue() + b.doubleValue();
}

这个方法使用了泛型,它能够处理任何类型的数字相加,不需要针对每个类型编写各自的加法方法。这就大大提高了代码的重用性,有了这个方法,那些固定类型的方法就都可以删了。
特别是一些逻辑相同的代码,使用泛型不仅能够提高代码重用性,还能够提高可读性。比如说下面这段代码,真是的是非常好用:

public static <T> void printArray(T[] array) {for (T element : array) {System.out.println(element);}
}

泛型的这个特性虽然很牛了,但是这还不是 Java 要设计泛型的全部原因。因为泛型还有一个作用,那就是保证类型安全。

保证类型安全

在说泛型的这个作用之前,先问大家一个问题,咱们常用的集合 ArrayList 是 Java 哪个版本加入的呢?泛型又是 Java 哪个版本加入的呢?

答案:ArrayList 是 Java 1.2 版本加入的,而泛型是 Java 1.5 加入的。

也就是说,有一段时时间,ArrayList 不是大家普遍认识的带泛型的 ArrayList<T> 这种形式,而是一个只能存放 Object 的列表。

在那一段泛型之光没有照耀到 Java 的日子里,保证类型安全成为了 Java 程序员在使用集合时不得不考虑的事情,考虑下面这一段代码:

ArrayList list = new ArrayList();
list.add("123");
// do some work......
Integer num = (Integer) list.get(0);

这段代码没有使用泛型来使用 ArrayList,我们加入了字符串 "123",但是在使用时,我们假定程序员忘记了加入的类型,他只记得好像应该是数字,于是在获取时就直接使用了 Integer 类强转。

这样的代码是能通过编译的,但是在运行的时候,会崩溃:

 Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer

这是一个典型的未使用泛型,而导致的类型安全无法保证,引发的崩溃。让程序员去保证类型安全,本身是不靠谱的做法,特别是在这种都是 Object 对象的列表中,鬼都不会知道存着的是个什么鬼。

这个时候就需要泛型出场了,泛型能够在编译时保证类型安全。例如上面的代码,我们加入泛型:

ArrayList<String> list = new ArrayList<>();
list.add("123");
Integer num = (Integer) list.get(0);

首先,ArrayList 加入泛型后,我们就知道这个列表是只能存入 String 类型的,也就不会将其转换为 Integer。那如果我非要转换呢,javac 编译器就会报错:

错误: 不兼容的类型: String无法转换为IntegerInteger num = (Integer) list.get(0);

这样类型安全就可以在编译时得到保证,不会出现在运行时的崩溃。

例子的代码很简单,大家可能看不到这一点对于软件开发有多重要,在大型复杂的项目中,这种类型安全的保证,是能减少很多运行时的崩溃的。特别是,一般像这种类型不一致的崩溃很多都是偶现的,偶现的 BUG 是最恶心的,因此使用泛型保证类型安全是十分必要的。

消除强制类型转换

泛型的这个作用其实就是上面保证类型安全这一点带来的。没有用泛型时,需要我们使用强制类型转化,但是加入泛型后,编译器已经能够知道我们存入的是什么类型,因此也就不需要我们进行强制类型转换了。

既然泛型有那么大的作用,那我们就赶紧把泛型用起来吧。

使用泛型

这一节,我们来看看如何使用系统提供的泛型类,以及其中需要注意的事项。

最常用到泛型的地方便是集合了,使用这些泛型集合类时,只需要把具体泛型参数 <T> 替换为需要的类型即可,例如 ArrayList<String>ArrayList<Number>Map<String, Integer> 等。

如果在使用泛型类时不指定类型参数,编译器会给出警告,且只能将 <T> 视为 Object 类型。这个时候就需要程序员自己去保证类型安全了,因此强烈不建议这么做,因为这样容易将类型转换异常带到运行时中去。

使用泛型基本就需要注意以上两点,下面介绍一下在使用泛型时的注意事项,这也是大家很少关注到的向上转型的问题。

在 Java 中,ArrayList<T> 是实现了 List<T> 接口,也就是说它可以向上转型为 List<T>

public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable

那么问题就来了,当泛型参数不同时,还能向上转型么,说具体一点,ArrayList<String> 能转型为 List<Number> 么?

答案是不行的:

ArrayList List = new ArrayList<String>();    //Raw use of parameterized class 'ArrayList'
List<Integer> list = new ArrayList<String>();    //直接报错

为什么 Java 不允许这么转型呢?因为运行转型的话,那么对于一个 ArrayList<String> 的容器,我将其转型为 ArrayList<Integer> 就可以往里面加入 Integer 对象了,这明显会造成 ClassCastException。泛型的存在用于限定类型的,这么一搞,泛型就失去了其作用。

这里,大家可以简单理解为,当泛型参数不一样时,两个类就没有太大关系了。例如 ArrayList<Integer>List<Number> 两者完全没有继承关系。

编写泛型

知道怎么使用系统的泛型之后,我们现在就来看看如何编写自己的泛型类。

泛型作为对类型进行限制的一种方式,我们编写泛型代码,也就是对使用我们代码的人进行一种限制。在这种情况下,我们是作为其他程序员的底层,向上提供某种框架代码,让其他程序员能够在我们设定的框架中更容易地编写代码实现功能。这有点类似于库的开发者,或是框架开发者,作为这种角色,写好泛型代码就更显得尤为重要了。毕竟,你也不想让别人说,这代码写得就跟一坨屎一样吧。

编写泛型类

编写泛型类,是比普通类要复杂的。这里我们就用 Pair<F, S> 这个类作为目标,一步一步编写出一个合格的泛型类。Pair 类是 Android 开发中一个简单的使用工具类,用于存储一对相关联的对象。

我们的第一版 Pair 只能使用没有使用泛型:

public class Pair {public final String first;public final String second;
}

那这肯定是不行的,因为这个 Pair 只能存放 String 类型的 first 和 second,那了能够存放所有类型,我们就使用泛型 <T>

public class Pair<T> {public final T first;public final T second;
}

我们把 firstsecondT 来修饰,表示其这两个成员变量是 T 类型的。而这个 T 类型,Java 是不知道的,我们必须声明告诉 Java 这是一个类型,因此类名从 Pair 变成了 Pair<T>,后面的 <T> 就是我们的泛型类型声明。

上面的代码看上去没问题,但是这个 Pair<T> 只能存放的 firstsecond 必须是相同的类型 T,那不同类型的怎么办呢?这时候我们再加一个泛型不就行了:

public class Pair<F, S> {public final F first;public final S second;
}

在加入两个泛型之后,firstsecond 的类型对应不同的泛型,这样就可以表示不同的类型了,注意 FS 这两个不同的泛型都需要在类上进行声明。

我们在为 Pair<F, S> 添加个构造方法:

public class Pair<F, S> {public final F first;public final S second;public Pair(F first, S second) {this.first = first;this.second = second;}
}

这算是一个简单的泛型类,那接下来,我们再为它编写一个泛型方法。

编写泛型方法

此处的泛型方法是指静态方法,而不是成员方法。这两种方法在使用泛型时是有一些区别的,其中最重要的一点就是,静态方法是不能使用类上声明的泛型类型,必须得自己声明泛型类型。例如,下面的代码将编译错误:

public static class Pair<F, S> {public final F first;public final S second;//编译错误,F、S 类型不能在 static 方法上使用public static Pair<F, S> create(F a, S b) {return new Pair<F, S>(a, b);}
}

可以想一想,为什么静态方法不能使用类上已经声明的泛型类型呢?

在回答这个问题之前,我们可以先想一下,类上的泛型类型,是在什么时候确定下来的呢?是在类创建的时候,我们在 new 的时候是需要提供具体类型的,这个时候泛型就被具体化为某个特定类型。不同的对象可能被创建为不同的类型,而静态方法只跟类相关,跟具体对象无关,而这些泛型又是跟具体对象相关的。所以静态对象不能使用类上声明的泛型也就变得合理了。

那要想使静态方法使用泛型,那就必须这个静态方法自己声明泛型:

public static <F, S> Pair <F, S> create(F a, S b) {return new Pair<F, S>(a, b);
}

这个静态方法在函数名前使用 <F, S> 来声明了两个泛型,那么后续这两个泛型就可以在这个函数中使用了。此时注意,这里的 FS 虽然与 Pair 上的 FS 泛型看似相同,实际上是没有任何关系的。所以为了避免产生误会,一般都会使用不同的泛型名,例如将这个方法的 <F, S> 变成 <A, B>

public static class Pair<F, S> {public final F first;public final S second;public static <A, B> Pair <A, B> create(A a, B b) {return new Pair<A, B>(a, b);}
}

这样才能够清楚地将静态方法的泛型类型和实例类型的泛型类型区分开。

在使用时,我们可以使用如下代码创建一个 Pair<F, S> 实例:

Pair<String, Integer> pair = Pair.create("123", 123);

这里总结一下编写泛型需要注意的几点:

  • 编写泛型时,需要定义泛型类型 <T>
  • 静态方法不能引用类上的泛型类型 <T>,必须定义自己方法特有的泛型类型;
  • 泛型可以同时定义多个,例如 <F, S><F、S、T>

在这里我们需要注意泛型的一个限制,那就是不能使用泛型类型直接创建对象。这一点也好理解,T 是什么类型只有在使用时,指定了泛型的具体类型才能确定。T 类型是一个抽象的类型,它是无法直接 new 出来的,就像你无法直接 new 一个 interface 一样。例如下面的代码是错误的:

public static class Pair<F, S> {public final F first;public final S second;public Pair(F first, S second) {this.first = new F();        //错误this.second = new S();       //错误}
}

这里使用 F 类型的默认构造,设想一下假如这个类型被确定为一个没有默认构造方法的类型呢。所以使用泛型类型创建对象是不行的。

Java 的泛型实现方式:类型擦除

上面的几节介绍了泛型的好处,泛型的使用,那这一节我们就来看看 Java 是如何实现泛型技术的。

首先,泛型编程并非 Java 特有的,在其他语言 C++、C# 上都有类似的技术,只不过名称不同而已,例如 C++ 上叫模版。在这些技术的加持下,程序员可以编写与具体类型无关的代码,只需要在使用时指定具体类型,从而提高代码的复用性;并且在编译时进行类型检查,减少运行时错误。

Java 的泛型是通过类型擦除(Type Erasure)来实现的。也就是说在编译时将泛型类型擦除,替换为其上限类型(通常为 Object),并在必要时插入类型转换。这种机制在编译时处理泛型类型,而在运行时移除了所有的泛型信息,因此叫做类型擦除。

这也就意味着,Java 的泛型是由编译器实现的,在编译成 class 文件时类型信息已经被擦除了,因此运行时,Java 虚拟机是没有任何泛型信息的。

例如上面我们编写的 Pair 的这个类,在我们看来,它是这样的,在源代码阶段,里面是包含泛型信息的:

public static class Pair<F, S> {public final F first;public final S second;public Pair(F first, S second) {this.first = first;this.second = second;}public static <A, B> Pair<A, B> create(A a, B b) {return new Pair<A, B>(a, b);}
}

那么在虚拟机的视角,它是这样的:

public class Pair {private Object first;private Object last;public Pair(Object first, Object last) {this.first = first;this.last = last;}
}

从这里就能看到,这个 Pair 在运行时已经没有泛型信息了,所有的泛型类型都被替换为了 Object

那么既然我们定义的泛型类型最终都变成了 Object,那我们就知道了 Java 泛型的一个局限:泛型类型 <T> 不能是基本类型。
因为像 intfloat 这些基本类型不是 Object 的子类,所以我们必须使用包装类:

Pair<float, int> pair = Pair.create(3.15, 123);    //编译错误
Pair<Float, Integer> pair = Pair.create(3.15F, 123);    //编译通过

尽管 Java 的泛型在编译时通过类型擦除机制移除了泛型类型信息,但 Java 编译器会在 class 文件中保留一些泛型信息,以便工具和开发人员能够利用这些信息进行反射和调试。所以如果大家把这个类编译为 class 文件之后,再查看它的反编译的内容,会发现它是有一些泛型信息的。但这并不意味着 JVM 在运行时会携带这些类型信息,既然是类型擦除,也就是说泛型类型参数被擦除并替换为其边界类型,如果没有指定边界,则默认为 Object

这里又引入了边界类型这个概念,在下一篇文章中,我们就来详细聊聊这个边界类型,这也是泛型中比较重要和难的点。

相关文章:

这一文,关于Java泛型的点点滴滴 一

作为一个 Java 程序员&#xff0c;用到泛型最多的&#xff0c;我估计应该就是这一行代码&#xff1a; List<String> list new ArrayList<>();这也是所有 Java 程序员的泛型之路开始的地方啊。 不过本文讲泛型&#xff0c;先不从这里开始讲&#xff0c;而是再往前…...

微信小程序之调查问卷

一、设计思路 1、界面 调查问卷又称调查表&#xff0c;是以问题的形式系统地记载调查内容的一种形式。微信小程序制作的调查问卷&#xff0c;可以在短时间内快速收集反馈信息。具体效果如下所示&#xff1a; 2、思路 此调查问卷采用服务器客户端的方式进行设计&#xff0c;服…...

基于Qt的视频剪辑

在Qt中进行视频剪辑可以通过多种方式实现&#xff0c;但通常需要使用一些额外的库来处理视频数据。以下是一些常见的方法和步骤&#xff1a; 使用FFmpeg FFmpeg是一个非常强大的多媒体框架&#xff0c;可以用来处理视频和音频数据。你可以使用FFmpeg的命令行工具或者其库来实现…...

electron 网页TodoList工具打包成win桌面应用exe

参考&#xff1a; electron安装&#xff08;支持win、mac、linux桌面应用&#xff09; https://blog.csdn.net/weixin_42357472/article/details/140643624 TodoList工具 https://blog.csdn.net/weixin_42357472/article/details/140618446 electron打包过程&#xff1a; 要将…...

数据结构之判断二叉树是否为搜索树(C/C++实现)

文章目录 判断二叉树是否为搜索树方法一&#xff1a;递归法方法二&#xff1a;中序遍历法总结 二叉树是一种非常常见的数据结构&#xff0c;它在计算机科学中有着广泛的应用。二叉搜索树&#xff08;Binary Search Tree&#xff0c;简称BST&#xff09;是二叉树的一种特殊形式&…...

golang长连接的误用

误用一&#xff1a;忘记读取响应的body 由于忘记读取响应的body导致创建大量处于TIME_WAIT状态的连接&#xff08;同时产生大量处于transport.go的readLoop和writeLoop的协程&#xff09; 在linux下运行下面的代码: package mainimport ("fmt""html"&qu…...

Springboot @Validate @Valid 基于复杂嵌套对象的参数校验示例

Springboot Validate Valid 基于复杂嵌套对象的参数校验示例 复杂对象 Data public class Object1 {Length(max 50,message "长度不能超过50位字符")NotBlank(message "名称不能为空")private String name;NotNull(message "不能为空")pri…...

算力共享下的,分级路由转发报文协议与通告

目录 网络双 SLA 约束 一、双SLA约束的定义与背景 二、双SLA约束的应用场景 三、双SLA约束的管理与实施 四、双SLA约束的优势与挑战 算力共享下的,分级路由转发报文协议与通告 基础设施即服务(IaaS)类 型算力资源 函数即服务(FaaS)类型算力服务 软件即服务(SaaS…...

滚动数组详解

滚动数组详解 何为滚动数组&#xff1f;滚动数组是如何优化空间的&#xff1f;交替滚动例题&#xff1a;来自某某轮廓线DP的题目 自我滚动(~~不如交替~~ 完结&#xff01;&#xff01;&#xff01; ( 宇宙免责任书&#xff1a;我用的是C) 何为滚动数组&#xff1f; 什么是滚动…...

C 语言动态链表

线性结构->顺序存储->动态链表 一、理论部分 从起源中理解事物&#xff0c;就是从本质上理解事物。 -杜勒鲁奇 动态链表是通过结点&#xff08;Node&#xff09;的集合来非连续地存储数据&#xff0c;结点之间通过指针相互连接。 动态链表本身就是一种动态分配内存的…...

【Leetcode】二十、记忆化搜索:零钱兑换

文章目录 1、记忆化搜索2、leetcode509&#xff1a;斐波那契数列3、leetcode322&#xff1a;零钱兑换 1、记忆化搜索 也叫备忘录&#xff0c;即把已经计算过的结果存下来&#xff0c;下次再遇到&#xff0c;就直接取&#xff0c;不用重新计算。目的是以减少重复计算。 以前面提…...

json数据格式 继续学习

1.定义 轻量级的数据交互格式&#xff0c;可以按照json数据格式去组织和封装数据。 本质是一个带有特定格式的字符串。 2.功能 负责不同编程语言中的数据传递和交互。 3.json数据格式转化 """ 演示json数据和python字典之间的转换 """ impor…...

gradle 构建项目添加版本信息

gradle 构建项目添加版本信息&#xff0c;打包使用 spring boot 的打包插件 build.gradle 配置文件 bootJar {manifest {attributes(Project-Name: project.name,Project-Version: project.version,"project-Vendor": "XXX Corp","Built-By": &…...

vue3 学习笔记17 -- 基于el-menu封装菜单

vue3 学习笔记17 – 基于el-menu封装菜单 前提条件&#xff1a;组件创建完成 配置路由 // src/router/index.ts import { createRouter, createWebHashHistory } from vue-router import type { RouteRecordRaw } from vue-router export const Layout () > import(/lay…...

使用 Redis 实现验证码、token 的存储,用自定义拦截器完成用户认证、并使用双重拦截器解决 token 刷新的问题

可以看一下我以前做过的笔记&#xff1a;黑马点评 短信登录部分 基于session实现登录流程 1.发送验证码 用户在提交手机号后&#xff0c;会校验手机号是否合法&#xff0c;如果不合法&#xff0c;则要求用户重新输入手机号 如果手机号合法&#xff0c;后台此时生成对应的验…...

反转链表 - 力扣(LeetCode)C语言

206. 反转链表 - 力扣&#xff08;LeetCode&#xff09;( 点击前面链接即可查看题目) /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ struct ListNode* reverseList(struct ListNode* head) {if(head NULL)…...

【Linux】进程间通信(1):进程通信概念与匿名管道

人与人之间是如何通信的&#xff1f;举个简单的例子&#xff0c;假如我是月老&#xff0c;我要为素不相识的但又渴望爱情的男女两方牵红线。我需要收集男方的信息告诉女方&#xff0c;收集女方的信息告诉男方&#xff0c;然后由男女双方来决定是否继续。对于他们而言&#xff0…...

Spring从入门到精通 01

文章目录 1. 依赖注入 (Dependency Injection, DI)2. 面向切面编程 (Aspect-Oriented Programming, AOP)3. 事务管理4. 简化 JDBC 开发5. 集成各种框架和技术6. 模块化和扩展性&#xff1a;主要的 Spring 模块&#xff1a;Core Container&#xff1a;AOP 模块&#xff1a;Data …...

C语言经典习题25

冒泡排序 对一维数组进行升序排序&#xff0c;然后在数组中输入20个数&#xff0c;将排序后的结果打印输出。 #include<stdio.h> #define N 20 int main() {int a[N];int i;for(i0;i<N;i) //初始化数组的数 {scanf("%d",&a);}for(i0;…...

2-47 基于matlab的时域有限差分法(FDTD法)拉夫等效原理进行时谐场外推

基于matlab的时域有限差分法(FDTD法)拉夫等效原理进行时谐场外推。外推边界距离吸收边界的距离、电磁场循环、傅立叶变换提起幅值和相位、各远区剖分点电场、方向系数计算等操作&#xff0c;得出可视化结果。程序已调通&#xff0c;可直接运行。 2-47 时域有限差分法(FDTD法) 拉…...

JupyterNotebook快捷键 自用

COMMAND MODE —————————————————————————————— Up Down cells的上下选择 A B 在上/下方插入cell C V X 复制/粘贴/剪切cell 双击D 删除所选cell Z 恢复被删除的cell 双击I Interrupt中断内核 Shift Enter 运行cell并选择下方 EDIT MODE ———…...

【我的OpenGL学习进阶之旅】讲一讲GL_TEXTURE_2D和GL_TEXTURE_EXTERNAL_OES的区别

在使用OpenGL ES进行图形图像开发时,我们常使用GL_TEXTURE_2D纹理类型,它提供了对标准2D图像的处理能力。这种纹理类型适用于大多数场景,可以用于展示静态贴图、渲染2D图形和进行图像处理等操作。 另外,有时我们需要从Camera或外部视频源读取数据帧并进行处理。这时,我们…...

Makefile 如何将生成的 .o 文件放到指定文件夹

研究了不少文章&#xff0c;我行通了一个&#xff0c;但是也不全&#xff0c;目前只能适用当前文件夹&#xff0c;如果源文件有子文件夹处理不了&#xff0c;还得继续研究。很多人说编译完把O文件移动走或者直接删掉。我想说的是不符合我的要求&#xff0c;移走或者删除O文件&a…...

聊一聊知识图谱结合RAG

因为最近在做一些关于提高公司内部使用的聊天机器人的回答准确率&#xff0c;并且最近微软官方也是开源了一下graphrag的源码&#xff0c;所以想聊一聊这个知识图谱结合rag。 rag在利用私有数据增强大模型回答的领域是一种比较典型的技术&#xff0c;也就是我们提出问题的时候&…...

Java面试锦集 之 一、Java基础(1)

一、Java基础&#xff08;1&#xff09; 1.final 关键字的作用&#xff1f; 修饰变量&#xff1a; 一旦被赋值&#xff0c;就不能再被修改&#xff0c;保证了变量值的稳定性。 例&#xff1a; final int NUMBER 10; //之后就不能再改变 NUMBER 的值了。修饰方法&#xff1a;…...

【leetcode】排列序列

给出集合 [1,2,3,...,n]&#xff0c;其所有元素共有 n! 种排列。 按大小顺序列出所有排列情况&#xff0c;并一一标记&#xff0c;当 n 3 时, 所有排列如下&#xff1a; "123""132""213""231""312""321" 给定…...

【Cesium开发实战】视频融合功能的实现,可自定义位置和视频路径

Cesium有很多很强大的功能,可以在地球上实现很多炫酷的3D效果。今天给大家分享一个视频融合功能。 1.话不多说,先展示 视频融合 2.设计思路 点击绘制开始在地图上绘制视频融合的点位,形成视频播放的区域,双击弹框输入名称和要播放视频的路径,即可对应区域播放对应视频,…...

【秋招笔试题】小明的美食

解析&#xff1a;思维题。由于需要互不相同&#xff0c;每次操作取重复的值与最大值相加即可&#xff0c;这样即可保证相加后不会新增重复的值。因此统计重复值即可。 #include <iostream> #include <algorithm>using namespace std; const int maxn 1e5 5; int…...

基于OpenLCA、GREET、R语言的生命周期评价方法、模型构建及典型案例应用

生命周期分析 (Life Cycle Analysis, LCA) 是评价一个产品系统生命周期整个阶段——从原材料的提取和加工&#xff0c;到产品生产、包装、市场营销、使用、再使用和产品维护&#xff0c;直至再循环和最终废物处置——的环境影响的工具。这种方法被认为是一种“从摇篮到坟墓”的…...

Linux操作系统 -socket网络通信

同一台主机之间的进程 1.古老的通信方式 无名管道 有名管道 信号 2、IPC对象通信 system v 消息队列 共享内存 信号量集 由于不同主机间进程通信 3.socket网络通信 国际网络体系结构&#xff1a; 七层OSI模型(理论…...

【苍穹】完美解决由于nginx更换端口号导致无法使用Websocket

一、报错信息 进行到websocket开发的过程中&#xff0c;遇到了前端报错&#xff0c;无法连接的提示&#xff1a; 经过F12排查很明显是服务端和客户端并没有连接成功。这里就涉及到之前的坑&#xff0c;现在需要填上了。 二、报错原因和推导 应该还记得刚开苍穹的第一天配置前…...

Qt中在pro中实现一些宏定义

在pro文件中利用 DEFINES 定义一些宏定义供工程整体使用。&#xff08;和在cpp/h文件文件中定义使用有点类似&#xff09;可以利用pro的中的宏定义实现一些全局的判断 pro中实现 #自定义一个变量 DEFINES "PI\"3.1415926\"" #自定义宏 DEFINES "T…...

bash XXX.sh文件和直接运行XXX.sh的区别

区别&#xff1a; bash XXX.sh 明确说明使用bash作为脚本的解释器不需要文件有执行权限 XXX.sh 需要指定相关解释器。如果第一行是#!/bin/bash则使用bash&#xff0c;如果是#!/bin/sh&#xff0c;则使用sh作为解释器需要有执行权限:通过chmod x 文件名指定 注意: #!是特殊标…...

【Python机器学习】k-近邻算法简单实践——改进约会网站的配对效果

需求背景&#xff1a; XX一直使用约会网站寻找适合自己的约会对象&#xff0c;ta会把人分为3种类型&#xff1a; 不喜欢、魅力一般、非常有魅力 对人分类轴&#xff0c;发现了对象样本的以下3种特征&#xff1a; 1、每年获得的飞行里程数 2、玩视频游戏所耗时间百分比 3、…...

vue3前端开发-小兔鲜项目-登录组件的开发表单验证

vue3前端开发-小兔鲜项目-登录组件的开发表单验证&#xff01;现在开始写登录页面的内容。首先这一次完成基础的首页按钮点击跳转&#xff0c;以及初始化一些简单的表单的输入验证。后期还会继续完善内容。 1&#xff1a;首先还是准备好login页面的组件代码内容。 <script …...

Winform上位机TCP客户端/服务端、串口通信

Winform上位机TCP客户端/服务端、串口通信 背景 日常练习&#xff0c;着急换工作&#xff0c;心态都快乱了。 工具 串口调试助手 网络调试助手 代码 客户端 using Microsoft.VisualBasic.Logging; using System.Net.Sockets; using System.Text;namespace TcpClientDem…...

Linux基础复习(二)

前言 本文介绍了一下Linux命令行基本操作及网络配置 一、 命令行提示含义 [当前用户主机名 工作目录]$ 若当前用户是root&#xff0c;则最后一个字符为# 否则&#xff0c;最后一个字符为$ 二、常用Linux命令及其解释 修改主机名 一般在创建一台主机后会使用hostname相关命…...

nginx漏洞修复 ngx_http_mp4_module漏洞(CVE-2022-41742)【低可信】 nginx版本升级

风险描述&#xff1a; Nginx 是一款轻量级的Web服务器、反向代理服务器。 Nginx 的受影响版本中的ngx _http_mp4_module模块存在内存越界写入漏洞&#xff0c;当在配置中使用 mp4 directive时&#xff0c;攻击者可利用此漏洞使用使用ngx_http_mp4_module模块处理特制的音频或视…...

网格布局 HTML CSS grid layout demo

文章目录 页面效果代码 (HTML CSS)参考 页面效果 代码 (HTML CSS) <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"…...

Java算法之递归算法-如何计算阶乘的值

上一篇学了递归之后&#xff0c;练习一下递归算法。 题目&#xff1a;使用递归算法计算阶乘的值&#xff0c;也就是5&#xff01;5*4*3*2*1&#xff0c;直接使用循环是非常简单的&#xff0c;这边练习一下递归算法。 先写一下两个条件 基线条件&#xff1a;等于1的时候返回1…...

python爬虫入门小案例

python爬虫 以下内容仅供学习交流,请勿用作其他用途,若涉及隐私和版权问题,请及时联系我删除 闲来无事,学了学爬虫小知识,适合入门,文笔拙劣,还望见谅 爬虫是什么: 爬取网页上的文字,图片,视频,音频 自动化操作浏览器,比如填写表单,打卡,提高工作效率爬虫的注意事项: 爬虫…...

【昇腾AI创新大赛集训营南京站学习笔记】-Ascend算子开发课程

昇腾AI创新大赛训练营 14:00-14:30 基础知识-理论课 一、CANN 、达芬奇架构和算子 1.AI Core逻辑架构 达芬奇架构包含三部分&#xff1a; 1&#xff09;计算类&#xff1a;矩阵计算单元&#xff08;两个矩阵扔进去相乘&#xff09;、向量计算单元、标量计算单元 2&#xff09;控…...

系统架构设计师教程 第4章 信息安全技术基础知识-4.5 密钥管理技术4.6 访问控制及数字签名技术-解读

系统架构设计师教程 第4章 信息安全技术基础知识-4.5 密钥管理技术&4.6 访问控制及数字签名技术 4.5 密钥管理技术4.5.1 对称密钥的分配与管理4.5.1.1 密钥的使用控制4.5.1.1.1 密钥标签4.5.1.1.2 控制矢量4.5.1.2 密钥的分配4.5.1.2.1物理方式14.5.1.2.2 物理方式24.5.1…...

C语言日常练习Day13

目录 一、设半径r1.5&#xff0c;圆柱高h3&#xff0c;求圆周长、圆面积、圆球表面积、圆球体积、圆柱体积 二、编写程序&#xff0c;用getchar函数读入两个字符给c1,c2&#xff0c;然后分别用putchar函数和printf函数输出这两个字符 三、输入4个整数&#xff0c;要求按由小…...

map、foreach、filter这些方法你还不知道什么时候该用哪个吗?那就看过来

forEach&#xff1a;‌主要用于遍历数组并对每个元素执行某种操作&#xff0c;‌通常用于改变当前数组里的值。‌它不会返回新数组&#xff0c;‌而是直接在原数组上进行操作。‌forEach方法不支持return、‌break、‌continue等语句&#xff0c;‌因为这些语句在forEach中不会…...

6.3 面向对象技术-设计模式

设计模式 创建型模式 结构型模式...

Mac 中安装内网穿透工具ngrok

ngrok 是什么&#xff1f; Ngrok 是一个网络工具&#xff0c;主要用于在网络中创建从公共互联网到私有或本地网络中运行的web服务的安全隧道。它充当了一个反向代理&#xff0c;允许外部用户通过公共可访问的URL访问位于防火墙或私有网络中的web应用程序或服务。Ngrok 特别适用…...

python count返回什么

描述 count() 方法用于统计字符串中某个子字符串出现的次数&#xff0c;可选参数为开始搜索与结束搜索的位置索引。 语法 count() 方法语法&#xff1a; S.count(sub[,start0[,endlen(S)]]) 参数 sub -- 搜索的子字符串。 S -- 父字符串。 start -- 可选参数&#xff0c;…...

mac清理软件哪个好用免费 MacBook电脑清理软件推荐 怎么清理mac

随着使用时间的增长&#xff0c;mac电脑会积累一些不必要的垃圾文件&#xff0c;这些文件会占用宝贵的存储空间&#xff0c;影响电脑的运行速度和稳定性。因此&#xff0c;定期清理mac电脑的垃圾文件是非常有必要的。市场上有许多优秀的Mac清理软件&#xff0c;包括一些出色的国…...

学生党百元蓝牙耳机哪个性价比高?精选四款超强性价比耳机型号

现阶段&#xff0c;蓝牙耳机技术逐渐成熟&#xff0c;蓝牙耳机在我们的学习和娱乐中承担着很重要的角色&#xff0c;那么在面对众多品牌和型号中&#xff0c;学生党们在选择蓝牙耳机上纠结不已&#xff0c;到底学生党百元蓝牙耳机哪个性价比高&#xff1f;作为一个蓝牙耳机重度…...