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

单链表的详解实现

单链表

结构

单链表结构中有两个数据,一个是存储数据的,还有一个指针指向下一个节点。

在这里插入图片描述
该图就是一个简单单链表的结构图。

接口实现

SLNode* CreateNode(SLNDataType x);//申请节点
void SLTprint(SLNode* head);//打印链表
void SLTPushBack(SLNode** pphead, SLNDataType x);//尾插
void SLTPushFront(SLNode** pphead, SLNDataType x);//头插
void SLTPopBack(SLNode** pphead);//尾删
void SLTPopFront(SLNode** pphead);//头删
SLNode* SLTFind(SLNode* phead, SLNDataType x);//查找
SLNode* SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x);//任意位置插入
void SLTErase(SLNode** pphead, SLNode* pos);//任意位置删除
void SLTDestroy(SLNode** pphead);//销毁链表

结构体的定义

typedef int SLNDataType;
typedef struct SListNode
{SLNDataType val;struct SList* next;
}SLNode;

首先,我们需要定义这样一个结构体,用来实现链表。本章禁用数字来实现,想用链表写一些学生信息的朋友们可以参考一下。

申请节点

SLNode* CreateNode(SLNDataType x)
{SLNode* newnode=(SLNode*)malloc(sizeof(SLNode));//动态申请内存SLNode大小即可,存放到SLNode* 的指针中去if (newnode == NULL)//如果申请失败,则提醒一下{perror("malloc fail");exit("-1");}newnode->val = x;//申请成功后,将该数据存放到该指针指向的val中去newnode->next = NULL;//将下一个节点设置为空指针return newnode;//返回值为该节点
}

首先,我们插入一个数据的时候,必须要开辟一个空间,也就是申请一个节点用来存放数据,所以单独封装这个函数来实现这个功能。

链表的打印

void SLTprint(SLNode* head)
{assert(head);//断言保证传入指针非空SLNode* cur = head;while (cur!=NULL)//打印循环{printf("%d->", cur->val);//打印当前指针指向的val值cur = cur->next;//将该指针指向的下一个指针的地址赋给该指针,实现链表的遍历}printf("NULL\n");
}

我们接受该指针,之后检查是否为空,把该指针重新赋给一个SLNode* 类型的cur进入打印循环,循环结束条件为尾指针指向的指针不为空指针即可。

尾插

我们插入删除数据时候,需要注意的一点是,我们需要遍历修改的时一级指针,那么我们就要用二级指针去接受这个参数了。

void SLTPushBack(SLNode** pphead, SLNDataType x)
{assert(pphead);//断言非空SLNode* newnode = CreateNode(x);//将新开辟的节点赋给newnodeif (*pphead == NULL)//链表为空{*pphead = newnode;//直接将newnode赋给头节点}else{SLNode* tmp = *pphead;//将头节点给一个临时变量while (tmp->next != NULL)//遍历循环{tmp = tmp->next;//可实现链表移动从而遍历}tmp->next = newnode;//让最后一个节点指向newnode即可}
}

我们实现尾部插入,有两种情况,首先就是如果该链表为空,也就是头节点指向的是空指针,那么我们直接将新开辟的节点赋给头节点即可,其他的情况,首先需要遍历链表到最后一个节点,然后让最后一个节点指向新开辟的节点即可实现尾部插入。

头插

void SLTPushFront(SLNode** pphead, SLNDataType x)
{assert(pphead);//断言非空SLNode* newnode = CreateNode(x);//将新开辟的节点赋给newnode节点newnode->next = *pphead;//新开辟的节点指向的是pphead节点*pphead = newnode;//newnode成为新的头节点
}

实现头插是十分简单的,直接看代码注释即可

尾删

void SLTPopBack(SLNode** pphead)
{assert(*pphead);//保证头节点不为空指针if ((*pphead)->next == NULL)//仅有一个节点的情况{free(*pphead);//释放掉头节点*pphead = NULL;//并将其赋值为空指针即可}else//多个节点的情况{SLNode* prev = NULL;//双指针移动之实际指针SLNode* tail = *pphead;//双指针移动之遍历指针while (tail->next!= NULL)//遍历循环,结束条件为tail的下一个节点不为空指针{prev = tail;//将遍历指针赋值给实际指针tail = tail->next;//遍历指针向后移动}
//循环结束,此时实际指针指向倒数第二个节点,而遍历指针指向最后一个节点free(tail);//释放掉此时指向最后一个节点的遍历节点tail = NULL;//将释放后的遍历指针赋值为空指针prev->next = NULL;//实际指针变成最后一个指针,最后一个指针指向的是空指针}
}

尾删,我们需要分为两种情况,首先是该链表只有一个头节点,这种情况直接将头节点释放掉在赋值为空指针即可,另外的就是该链表有多个节点,那么此时我们选哟做到的就是,遍历到最后一个节点,之后将该节点释放掉即可,这里我们用双指针的移动来实现释放节点。

头删

void SLTPopFront(SLNode** pphead)
{assert(*pphead);//保证头节点不为空SLNode* tmp = *pphead;//将头节点赋给临时节点(*pphead) = tmp->next;//临时节点也就是头节点指向的下一个节点赋给头节点成为新的头节点free(tmp);//释放掉已经不是头节点的节点tmp = NULL;//将释放掉的节点赋值为空指针
}

头删十分简单,直接看代码注释即可。

查找

SLNode* SLTFind(SLNode* phead, SLNDataType x)
{if (phead == NULL)//头节点为空指针,也就是链表为空无需打印{printf("链表为空无需查找");return NULL;//返回空指针}else//链表不为空{SLNode* cur = phead;//将头节点赋给临时变量curwhile (cur)//遍历循环{if (cur->val== x)//如果遍历的数据是所需要的数据{return cur;//返沪该指针}cur = cur->next;//如果不是,则移动临时变量cur实现链表的遍历}}return NULL;//找完没有,则返回空指针
}

查找的实现也很简单,如果链表为空,那么我们就无需查找,返回空指针即可,不为空,我们遍历找到需要数据存放的节点,之后返回这个节点就可以了,如果遍历完没有找到,就说明不存在该节点返回空指针即可。

任意位置的插入

SLNode* SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x)
{assert(pphead);//断言非空if (*pphead == pos)//头插情况{SLTPushFront(pphead,x);}else//非头部插入情况{SLNode* newnode = CreateNode(x);//新开辟的节点赋给newnode指针SLNode* cur = *pphead;//头节点给临时变量curwhile (cur->next != pos)//遍历到pos前一个位置{cur = cur->next;//遍历移动}cur->next = newnode;//此时让pos位置前一个节点指向要插入的节点newnode->next = pos;//要插入的节点指向之后的节点,实现链表的完整}
}

首先,如果插入的位置是头节点,那么也就是头插了,如果是其他位置,那么首先需要遍历到该位置的前一个节点,让该节点指向新插入的节点,新插入的节点在指向原来该位置的节点就实现了该位置的数据插入。

任意位置的删除

void SLTErase(SLNode** pphead, SLNode* pos)
{assert(pphead);//断言非空if (*pphead == pos)//头删情况{SLTPopFront(pphead);}else//非头删情况{SLNode* cur = *pphead;//头节点赋给临时变量curwhile (cur->next != pos)//遍历到pos位置的前一个节点循环{cur = cur->next;//临时变量移动,链表的遍历}cur->next = pos->next;//pos位置的前一个节点直线pos的下一个节点free(pos);//释放pospos = NULL;//pos赋值为空指针}
}

首先,也是如果删除的节点是头节点,直接头删即可,不是则遍历到该位置的前一个位置,跳过该位置指向下一个节点,再将该位置节点释放掉即可。

链表的销毁

void SLTDestroy(SLNode** pphead)
{assert(pphead);SLNode* cur = *pphead;while (cur){SLNode* pur = cur;cur = cur->next;free(pur);}printf("链表已经成功销毁!");
}

链表的销毁十分简单,遍历释放即可。

代码参考

鄙人不才,大家可以参考一下链表代码的实现

SList.h

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>typedef int SLNDataType;
typedef struct SListNode
{SLNDataType val;struct SList* next;
}SLNode;
SLNode* CreateNode(SLNDataType x);//申请节点
void SLTprint(SLNode* head);//打印链表
void SLTPushBack(SLNode** pphead, SLNDataType x);//尾插
void SLTPushFront(SLNode** pphead, SLNDataType x);//头插
void SLTPopBack(SLNode** pphead);//尾删
void SLTPopFront(SLNode** pphead);//头删
SLNode* SLTFind(SLNode* phead, SLNDataType x);//查找
SLNode* SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x);//任意位置插入
void SLTErase(SLNode** pphead, SLNode* pos);//任意位置删除
void SLTDestroy(SLNode** pphead);//销毁链表

SList.c

#define  _CRT_SECURE_NO_WARNINGS
#include"SList.h"
SLNode* CreateNode(SLNDataType x)
{SLNode* newnode=(SLNode*)malloc(sizeof(SLNode));if (newnode == NULL){perror("malloc fail");exit("-1");}newnode->val = x;newnode->next = NULL;return newnode;
}
void SLTprint(SLNode* head)
{assert(head);SLNode* cur = head;while (cur!=NULL){printf("%d->", cur->val);cur = cur->next;}printf("NULL\n");
}
void SLTPushBack(SLNode** pphead, SLNDataType x)
{SLNode* newnode = CreateNode(x);if (*pphead == NULL){*pphead = newnode;}else{SLNode* tmp = *pphead;while (tmp->next != NULL){tmp = tmp->next;}tmp->next = newnode;}
}
void SLTPushFront(SLNode** pphead, SLNDataType x)
{SLNode* newnode = CreateNode(x);newnode->next = *pphead;*pphead = newnode;
}
void SLTPopBack(SLNode** pphead)
{assert(*pphead);if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{SLNode* prev = NULL;SLNode* tail = *pphead;while (tail->next!= NULL){prev = tail;tail = tail->next;}free(tail);tail = NULL;prev->next = NULL;}
}
void SLTPopFront(SLNode** pphead)
{assert(*pphead);SLNode* tmp = *pphead;(*pphead) = tmp->next;free(tmp);tmp = NULL;
}
SLNode* SLTFind(SLNode* phead, SLNDataType x)
{if (phead == NULL){printf("链表为空无需查找");return NULL;}else{SLNode* cur = phead;while (cur){if (cur->val== x){return cur;}cur = cur->next;}}return NULL;
}
SLNode* SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x)
{assert(pphead);if (*pphead == pos){SLTPushFront(pphead,x);}else{SLNode* newnode = CreateNode(x);SLNode* cur = *pphead;while (cur->next != pos){cur = cur->next;}cur->next = newnode;newnode->next = pos;}
}
void SLTErase(SLNode** pphead, SLNode* pos)
{assert(pphead);if (*pphead == pos){SLTPopFront(pphead);}else{SLNode* cur = *pphead;while (cur->next != pos){cur = cur->next;}cur->next = pos->next;free(pos);pos = NULL;}
}
void SLTDestroy(SLNode** pphead)
{assert(pphead);SLNode* cur = *pphead;while (cur){SLNode* pur = cur;cur = cur->next;free(pur);}printf("链表已经成功销毁!");
}

test.c

这个文件主要是为了检测链表代码是否有误,用于调试链表。

#define  _CRT_SECURE_NO_WARNINGS
#include"SList.h"
void test1()
{SLNode* S1=NULL;SLTPushBack(&S1, 1);SLTPushBack(&S1, 2);SLTPushBack(&S1, 3);SLTPushBack(&S1, 4);SLTprint(S1);
}
void test2()
{SLNode* S1 = NULL;SLTPushBack(&S1, 1);SLTPushBack(&S1, 2);SLTPushBack(&S1, 3);SLTPushBack(&S1, 4);SLTPushFront(&S1, 4);SLTprint(S1);
}
void test3()
{SLNode* S1 = NULL;SLTPushBack(&S1, 1);SLTPushBack(&S1, 2);SLTPushBack(&S1, 3);SLTPushBack(&S1, 4);SLTPushFront(&S1, 4);SLTPopBack(&S1);SLTprint(S1);
}
void test4()
{SLNode* S1 = NULL;SLTPushBack(&S1, 1);SLTPushBack(&S1, 2);SLTPushBack(&S1, 3);SLTPushBack(&S1, 4);SLTPushFront(&S1, 4);SLTPopBack(&S1);SLTPopFront(&S1);SLTprint(S1);
}
void test5()
{SLNode* S1 = NULL;SLTPushBack(&S1, 1);SLTPushBack(&S1, 2);SLTPushBack(&S1, 3);SLTPushBack(&S1, 4);SLTPushFront(&S1, 5);SLNode* x =SLTFind(S1,3);SLTprint(S1);printf("%d", x->val);}
void test6()
{SLNode* S1 = NULL;SLTPushBack(&S1, 1);SLTPushBack(&S1, 2);SLTPushBack(&S1, 3);SLTPushBack(&S1, 4);SLTPushFront(&S1, 4);SLTInsert(&S1, S1, 5);SLNode* S2 = S1->next;SLTInsert(&S1, S2, 8);SLTprint(S1);
}
void test7()
{SLNode* S1 = NULL;SLTPushBack(&S1, 1);SLTPushBack(&S1, 2);SLTPushBack(&S1, 3);SLTPushBack(&S1, 4);SLTPushFront(&S1, 4);SLTInsert(&S1, S1, 5);SLNode* S2 = S1->next;SLTInsert(&S1, S2, 8);SLTErase(&S1, S1);SLTErase(&S1, S2);SLTprint(S1);
}
void test8()
{SLNode* S1 = NULL;SLTPushBack(&S1, 1);SLTPushBack(&S1, 2);SLTPushBack(&S1, 3);SLTPushBack(&S1, 4);SLTPushFront(&S1, 4);SLTInsert(&S1, S1, 5);SLNode* S2 = S1->next;SLTInsert(&S1, S2, 8);SLTErase(&S1, S1);SLTErase(&S1, S2);SLTprint(S1);SLTDestroy(&S1);
}
int main()
{/*test1();*//*test2();*//*test3();*//*test4();*//*test5();*//*test6();*//*test7();*/test8();return 0;
}

最后谢谢大家观看,以后会奉上更多内容,大家共同进步!

相关文章:

单链表的详解实现

单链表 结构 单链表结构中有两个数据&#xff0c;一个是存储数据的&#xff0c;还有一个指针指向下一个节点。 该图就是一个简单单链表的结构图。 接口实现 SLNode* CreateNode(SLNDataType x);//申请节点 void SLTprint(SLNode* head);//打印链表 void SLTPushBack(SLNode*…...

抛弃 scp 改用 rsync,让 Linux 下文件传输高效无比

我们都使用过 scp 来传输文件。当传输在中途或甚至在 99% 时被中断时&#xff0c;&#xff08;每当我想起99%的中断传输时&#xff0c;我的心都很痛&#xff09;&#xff1b;让我们看看如何使用 rsync 来替代 scp&#xff0c;避免这样的不幸。 什么是rsync&#xff1f; Rsync…...

Leetcode 2919. Minimum Increment Operations to Make Array Beautiful

Leetcode 2919. Minimum Increment Operations to Make Array Beautiful 1. 解题思路2. 代码实现 题目链接&#xff1a;2919. Minimum Increment Operations to Make Array Beautiful 1. 解题思路 这一题就是一个动态规划的题目。 思路上来说&#xff0c;就是考察每一个没到…...

关键词搜索亚马逊商品数据接口(标题|主图|SKU|价格|优惠价|掌柜昵称|店铺链接|店铺所在地)

亚马逊提供了API接口来获取商品数据。其中&#xff0c;关键词搜索亚马逊商品接口&#xff08;item_search-按关键字搜索亚马逊商品接口&#xff09;可以用于获取按关键字搜索到的商品数据。 通过该接口&#xff0c;您可以使用API Key和API Secret来认证身份&#xff0c;并使用…...

[计算机提升] Windows系统软件:娱乐类

3.3 系统软件&#xff1a;娱乐类 3.3.1 Windows Media Player&#xff1a;dvdplay Windows Media Player是Windows操作系统自带的多媒体播放软件&#xff0c;用于播放和管理电脑中的音频和视频文件。它提供了以下功能&#xff1a; 播放音频和视频文件&#xff1a;Windows Med…...

【Git企业开发】第五节.远程操作

文章目录 前言一、理解分布式版本控制系统二、远程仓库 2.1 新建远程仓库 2.2 克隆远程仓库 2.3 向远程仓库推送 2.4 拉取远程仓库总结 前言 一、理解分布式版本控制系统 我们目前所说的所有内容(工作区&#xff0c;暂存区&#xff0c;版本库等等)&#x…...

idea 配置checkstyle全过程

checkstyle是提高代码质量,检查代码规范的很好用的一款工具&#xff0c;本文简单介绍一下集成的步骤&#xff0c;并提供一份完整的checkstyle的代码规范格式文件&#xff0c;以及常见的格式问题的解决方法。 一&#xff0c;安装 打开idea的文件选项&#xff0c;选择设置&…...

小程序如何设置自动使用物流账号发货

小程序支持自动使用物流账号发货并生成运单号。商家需要与物流公司合作&#xff0c;获取物流账号&#xff0c;支持快递物流和同城外卖配送平台。具体方法请参考公众号之前发布的文章&#xff0c;例如可以搜索“快递账号”。 导入物流账号后&#xff0c;在小程序管理员后台->…...

高性能渲染——详解Html Canvas的优势与性能

本文由葡萄城技术团队原创并首发。转载请注明出处&#xff1a;葡萄城官网&#xff0c;葡萄城为开发者提供专业的开发工具、解决方案和服务&#xff0c;赋能开发者。 一、什么是Canvas 想必学习前端的同学们对Canvas 都不陌生&#xff0c;它是 HTML5 新增的“画布”元素&#x…...

2023.10 各个编程语言 受欢迎指数 排行

目录 一、前言: 二、排行: 三、趋势: 四、历史排名: 五、名人堂: 一、前言: 来自tiobe...

『PyQt5-基本控件』| 15 如何设置主窗口居中?退出应用程序如何操作?

15 如何设置主窗口居中?退出应用程序如何操作? 1 如何实现主窗口居中显示?1.1 获取屏幕坐标1.2 获取窗口坐标1.3 居中计算1.4 移动位置1.5 完整代码1.6 效果演示2 如何退出应用程序?2.1 使用按钮退出2.2 信号与槽绑定2.3 布局和增加按钮2.4 定义一个按钮事件2.5 完整代码2.…...

scrapy+selenium框架模拟登录

目录 一、cookie和session实现登录原理 二、模拟登录方法-Requests模块Cookie实现登录 三、cookiesession实现登录并获取数据 四、selenium使用基本代码 五、scrapyselenium实现登录 一、cookie和session实现登录原理 cookie:1.网站持久保存在浏览器中的数据2.可以是长期…...

【实验五】题解

T1&#xff1a;缺失的数字 题目描述; 我是敦立坤的爹&#xff01;&#xff01;&#xff01; 一个整数集合中含有n个数字&#xff0c;每个数字都在0n之间。假设0n的n1个数字中有且仅有一个数字不在该集合中&#xff0c;请找出这个数字。 分析&#xff1a; 这里引用一个桶的思…...

Android开发知识学习——Kotlin基础

函数声明 声明函数要用用 fun 关键字&#xff0c;就像声明类要用 class 关键字一样 「函数参数」的「参数类型」是在「参数名」的右边 函数的「返回值」在「函数参数」右边使用 : 分隔&#xff0c;没有返回值时可以省略 声明没有返回值的函数&#xff1a; fun main(){println…...

C++——定义一个 Book(图书)类

完整代码&#xff1a; /*定义一个 Book(图书)类&#xff0c;在该类定义中包括数据成员和成员函数 数据成员&#xff1a;book_name &#xff08;书名&#xff09;、price(价格)和 number(存书数量)&#xff1b; 成员函数&#xff1a;display()显示图书的 情况&#xff1b;borro…...

深度学习之基于YoloV5的道路地面缺陷检测系统(UI界面)

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、道路地面缺陷检测系统四. 总结 一项目简介 基于YoloV5的道路地面缺陷检测系统利用深度学习中的目标检测算法&#xff0c;特别是YoloV5算法&am…...

AcWing94. 递归实现排列型枚举:输出1~n的全排列

题目 把 1∼ n n n 这 n n n 个整数排成一行后随机打乱顺序&#xff0c;输出所有可能的次序。 输入格式 一个整数 n n n。 输出格式 按照从小到大的顺序输出所有方案&#xff0c;每行 1 个。 首先&#xff0c;同一行相邻两个数用一个空格隔开。 其次&#xff0c;对于两…...

神经网络多种注意力机制原理和代码讲解

多种注意力表格&#xff1a; 大神参考仓库链接&#xff1a; 魔鬼面具 对应 name 就是目录&#xff0c;点击即可跳转到对应学习。 nameneed_chaneelpaper SE (2017) Truehttps://arxiv.org/abs/1709.01507 BAM (2018) Truehttps://arxiv.org/pdf/1807.06514.pdf CBAM (2018) Tr…...

前端HTML

文章目录 一、什么是前端前端后端 前端三剑客1.什么是HTML2.编写前端的步骤1.编写服务端2.浏览器充当客户端访问服务端​ 3.浏览器无法正常展示服务端内容(因为服务端的数据没有遵循标准)4.HTTP协议>>>:最主要的内容就是规定了浏览器与服务端之间数据交互的格式 3. 前…...

Jenkins安装(Jenkins 2.429)及安装失败解决(Jenkins 2.222.4)

敏捷开发与持续集成 敏捷开发 敏捷开发以用户的需求进化为核心&#xff0c;采用迭代、循序渐进的方法进行软件开发。在敏捷开发中&#xff0c;软件项目在构建初期被切分成多个子项目&#xff0c;各个子项目的成果都经过测试&#xff0c;具备可视、可集成和可运行使用的特征。…...

Objective-C常用命名规范总结

【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名&#xff08;Class Name)2.协议名&#xff08;Protocol Name)3.方法名&#xff08;Method Name)4.属性名&#xff08;Property Name&#xff09;5.局部变量/实例变量&#xff08;Local / Instance Variables&…...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

蓝桥杯3498 01串的熵

问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798&#xff0c; 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.

ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #&#xff1a…...

Vite中定义@软链接

在webpack中可以直接通过符号表示src路径&#xff0c;但是vite中默认不可以。 如何实现&#xff1a; vite中提供了resolve.alias&#xff1a;通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...

抽象类和接口(全)

一、抽象类 1.概念&#xff1a;如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象&#xff0c;这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法&#xff0c;包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中&#xff0c;⼀个类如果被 abs…...

Spring Security 认证流程——补充

一、认证流程概述 Spring Security 的认证流程基于 过滤器链&#xff08;Filter Chain&#xff09;&#xff0c;核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤&#xff1a; 用户提交登录请求拦…...

区块链技术概述

区块链技术是一种去中心化、分布式账本技术&#xff0c;通过密码学、共识机制和智能合约等核心组件&#xff0c;实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点&#xff1a;数据存储在网络中的多个节点&#xff08;计算机&#xff09;&#xff0c;而非…...

Android写一个捕获全局异常的工具类

项目开发和实际运行过程中难免会遇到异常发生&#xff0c;系统提供了一个可以捕获全局异常的工具Uncaughtexceptionhandler&#xff0c;它是Thread的子类&#xff08;就是package java.lang;里线程的Thread&#xff09;。本文将利用它将设备信息、报错信息以及错误的发生时间都…...