RAII - 安卓中的智能指针
RAII - 安卓中的智能指针
概念 sp wp RefBase
是什么
system/core/libutils/RefBase.cpp
system/core/libutils/include/utils/RefBase.hsystem/core/libutils/StrongPointer.cpp
system/core/libutils/include/utils/StrongPointer.h
Android在标准库之外,自定义了以下两种智能指针类型:
-
强指针
sp
(strong pointer),弱指针wp
(weak pointer)都是模板类 -
sp 和
share_ptr
概念相同。wp 和weak_ptr
概念相同。sp
和wp
has-aRefBase
-
弱指针仅仅记录对象的地址,不能通过弱指针来访问该对象。要想访问弱指针所指向的对象,需首先通过wp类所提供的promote()方法将弱指针升级为强指针。
-
弱指针所指向的对象是有可能在其它地方被销毁的。如果对象已经被销毁,wp的promote()方法将返回空指针,这样就能避免出现地址访问错的情况。
-
支持使用 sp
和 wp
的对象,必须继承RefBase类。观察Android 中使用sp
和 wp
的地方,传进来的模板参数都是继承自RefBase
,例如:
// MediaPlayer是支持使用强指针和弱指针的对象
class MediaPlayer : public BnMediaPlayerClient,public virtual IMediaDeathNotifier
{
public:MediaPlayer();~MediaPlayer();
// 父类是 BnMediaPlayerClient 和 IMediaDeathNotifier,
// 多继承可能出现菱形继承,因此 虚继承自 RefBase
class IMediaDeathNotifier: virtual public RefBase
{
public:IMediaDeathNotifier() { addObitRecipient(this); }virtual ~IMediaDeathNotifier() { removeObitRecipient(this); }//随便找个,可以看到是继承RefBase,注意的是虚拟继承。
怎么用
// 使用智能指针的类必须继承自 RefBase
class Ressource : public RefBase { Ressource(int){}void test() {}
}{ // 作用域开始// 强引用 sp 构造函数有很多,这里简单列举两个Ressource* p = new Ressource(1);sp<Ressource> sp1(p); // 1sp<Ressource> sp1 = new Ressource(1); // 2// sp::make 提供内存连贯性&缓存命中: 构建一个 sp 对象,其内部new一个资源对象,在将成员变量 m_ptr 指针指向它 static sp<Ressource> sp1 = sp<Ressource>::make(1); // 3// 通过强引用,操作指针指向的对象sp1->test();// 不能直接通过弱引用使用对象,升级为强指针再使用wp<Ressource> wp1(p); // 弱引用sp<Ressource> sp2 = wp1.promote();if(sp2 != NULL) { // sp重载了 != 原本spRessource2与 NULL 不是同一类型sp2->test();}
} // 作用域结束,调用强引用对象的析构函数,释放掉 p 指针指向的内存
对比标准库
为什么不直接用标准库?
为什么不用标准库?
1、历史原因!C++11推出 shared_ptr 和 weak_ptr之前,Android就已经需要存在了。
2、特殊的功能:有些功能是针对特定使用场景定制的(显然不符合 C++ 标准)。
例如,特殊的接口:
template<typename T>
class sp {//! Special optimization for use by ProcessState (and nobody else).void force_set(T* other);
}
template<typename T>
void sp<T>::force_set(T* other) {other->forceIncStrong(this);m_ptr = other;
}
例如,智能指针有两种模式
- 强引用控制
OBJECT_LIFETIME_STRONG
(默认,且绝大多数情况下都是强引用控制) - 弱引用控制
OBJECT_LIFETIME_WEAK
可通过 void RefBase::extendObjectLifetime(int32_t mode)
函数切换。强/弱引用控制的切换,通常在被管理的目标类的构造函数中调用:
// system/core/libutils/include/utils/RefBase.h
enum {OBJECT_LIFETIME_STRONG = 0x0000, //强引用控制OBJECT_LIFETIME_WEAK = 0x0001, //弱引用控制// 掩码值,可使用 flag&OBJECT_LIFETIME_MASK 的形式来获取 flag 的最低位数值OBJECT_LIFETIME_MASK = 0x0001
};// frameworks/native/libs/binder/BpBinder.cpp
BpBinder::BpBinder(Handle&& handle) {// 调用函数修改智能指针模式extendObjectLifetime(OBJECT_LIFETIME_WEAK);
}
例如,神奇的功能:
static void check_not_on_stack(const void* ptr) {
实现上的区别
从被管理的资源的角度来看:
标准库的 shared_ptr,”管理它所持有的 裸指针“。
- 被管理的资源——裸指针所指向的对象,可以是任意类型。
- 处于"被管理状态",不会对资源本身造成什么影响,没有负担。
Android 实现的 sp,wp ”操作它所持有的 RefBase类的计数“。
- 被管理的资源——必须是 RefBase类,即被管理要求原有类型必须继承 RefBase类。
- 处于"被管理状态",意味着
- 资源类RefBase 自身保存计数,保存的强引用计数归零后自己执行
delete this
- sp wp 会去操作,让资源类去管理自身保存的计数
- 资源类RefBase 自身保存计数,保存的强引用计数归零后自己执行
优缺点
Android这样不使用 STD 智能指针的做法,
优点:总结来说就是,客制化的美~
-
允许进行根据需求自定义的修改/优化。
前文介绍过的,特殊接口,强/弱引用控制
-
安卓fwk中普遍存在跨进程“引用资源”的的情况,RefBase 自行计数方便IPC情境下的管理。
-
方便且臃肿的 debug 函数
缺点:总结来说就是,增大对象本身的开销
-
对象占用内存增高—— 作为操作系统必要,但是其它场景不需要的开销
诸如函数
RefBase::getStrongCount()
,RefBase::weakref_type::trackMe()
-
资源类有时候需要虚继承 RefBase —— 为了解决菱形继承而不可避免的选择。因此相较于使用标准库的智能指针,每次更改计数都不得不承担虚函数表带来的开销。
-
不同于标准库这件事情本身就会影响可读性,引入新的复杂度。
你自己看看复杂不复杂,臃肿不臃肿
void RefBase::weakref_type::decWeak(const void* id)
{weakref_impl* const impl = static_cast<weakref_impl*>(this);impl->removeWeakRef(id); //删除弱引用//减少对象的弱引用计数,并且返回减少之前的值,存储在c中const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release); //计数减1ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);/*如果c不等于1则不用进行处理,如果c=1,说明弱引用计数为0,则强引用计数必定也为0,则考虑如何释放该对象*/if (c != 1) return; //还有其他引用在用这个指针,直接返回/*释放方式取决于当前对象的生命周期控制标志mFlags的值0,只要强引用计数为0,系统则自动释放这个对象OBJECT_LIFETIME_WEAK,只有强引用计数和弱引用计数都为0,系统才会自动释放这个对象OBJECT_LIFETIME_FOREVER,系统永远不会释放这个对象,需要开发者手动释放*/ atomic_thread_fence(std::memory_order_acquire);// ...
RefBase类
首先,这屌东西里面有什么。它能干啥?
内部类 weakref_type
RefBase
类 有两个内部类:定义 & 实现 引用计数维护接口
class weakref_type
:定义接口class weakref_impl
:具体实现(weakref_type
继承weakref_impl
)
成员变量RefBase::mRefs
,类型就是刚刚提到的内部类 weakref_impl* const
——描述对象的引用计数。
内部类weakref_type
提供有类方法:
- 增加/减少 弱引用计数:
weakref_type::incWeak
,weakref_type::decWeak
- 维护对象的强/弱引用计数:
weakref_type::attemptIncStrong
,weakref_type::attemptIncWeak
class RefBase {friend class weakref_type;class weakref_type {/*...*/};class weakref_impl;//使用一个weakref_impl对象来描述对象的引用计数weakref_impl* const mRefs;
class RefBase::weakref_impl : public RefBase::weakref_type
成员函数
RefBase
类 有一系列 成员函数
-
关于强引用:
incStrong
,incStrongRequireStrong
,decStrong
,forceIncStrong
-
关于弱引用:
createWeak
,getWeakRefs
,
createWeak
类方法,返回weakref_type
类型的指针。
sp wp
sp
-
sp::m_ptr
的类型是T*
即RefBase *
——sp has-a RefBase -
控制计数,最终调用:
RefBase::incStrong
和RefBase::decStrong
方法
template <typename T>
class sp {// T 类型就是 RefBase 的子类T* m_ptr;
};
// decStrong 和incStrong的具体实现,在RefBase类中。
void RefBase::incStrong(const void* id) const
void RefBase::decStrong(const void* id) const
wp
wp::m_refs
的类型是RefBase::weakref_type *
- 控制计数,最终调用:
RefBase::incStrong
和RefBase::decStrong
方法
// RefBase::weakref_type中实现
RefBase::weakref_type* RefBase::createWeak(const void* id) const
void RefBase::weakref_type::incWeak(const void* id)
void RefBase::weakref_type::decWeak(const void* id)
强引用 sp
通过 初始化/传入参数 设置m_ptr
,m_ptr
必须是RefBase
类的派生类(虚继承),从而使用 RefBase::incStrong和RefBase::decStrong修改RefBase本身的引用计数。
sp构造时:初始化sp中RefBase类型的指针 m_ptr,并调用指针incStrong()进行计数加1
sp析构时:指针T *m_ptr需要将计数减1
template<typename T>
sp<T>::sp(T* other): m_ptr(other) {if (other)other->incStrong(this);
}template<typename T>
sp<T>::~sp()
{if (m_ptr) m_ptr->decStrong(this);
}
弱引用 wp
成员变量 wp::m_ptr
,wp::m_refs
wp::m_ptr
类型是RefBase
类型的指针,指向资源RefBase
本身。wp::m_refs
类型是RefBase::weakref_type
,即资源RefBase
的成员变量RefBase::mRefs
——用于指向管理资源的内部类。
构造/移动&拷贝/赋值/清空时,以上两个成员变量被同时修改。
通过成员变量wp::m_refs
(类型为RefBase::weakref_type
)的成员函数去修改资源RefBase计数。
template <typename T>
class wp
{typedef typename RefBase::weakref_type weakref_type;T* m_ptr;weakref_type* m_refs;
};template<typename T>
wp<T>::~wp()
{if (m_ptr) m_refs->decWeak(this);
}
源码注释 RefBase
接口
class RefBase
{
public://增加强引用计数void incStrong(const void* id) const;//减少强引用计数void decStrong(const void* id) const;// (wp中的m_refs调用)weakref_type* createWeak(const void* id) const;weakref_type* getWeakRefs() const;void forceIncStrong(const void* id) const;int32_t getStrongCount() const;// 定义了内部类:weakref_type weakref_implclass weakref_type{public:RefBase* refBase() const;//增加弱引用计数 (wp中的m_refs调用)void incWeak(const void* id);//减少弱引用计数 (wp中的m_refs调用)void decWeak(const void* id);//维护对象的强引用计数bool attemptIncStrong(const void* id);//维护对象的弱引用计数bool attemptIncWeak(const void* id);// debug用函数int32_t getWeakCount() const;void printRefs() const;void trackMe(bool enable, bool retain);};// Debug 函数...// 允许自定义的Destroyerclass Destroyer {friend class RefBase;public:virtual ~Destroyer();private:virtual void destroy(RefBase const* base) = 0;};void setDestroyer(Destroyer* destroyer);
protected:RefBase();virtual ~RefBase();// 生命周期有关的flag,函数...// 一堆其它功能的虚函数...
private:friend class weakref_type;class weakref_impl;RefBase(const RefBase& o);RefBase& operator=(const RefBase& o);//使用一个weakref_impl对象来描述对象的引用计数weakref_impl* const mRefs;
};
强引用
decStrong
RefBase类的成员函数decStrong减少对象的引用计数
实现如下:
void RefBase::decStrong(const void* id) const
{weakref_impl* const refs = mRefs;refs->removeStrongRef(id);//移除强引用链表中的引用idconst int32_t c = refs->mStrong.fetch_sub(1, std::memory_order_release);//计数减1
#if PRINT_REFSALOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);
#endifALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);if (c == 1) {//如果是最后一个了,那需要释放该指针了std::atomic_thread_fence(std::memory_order_acquire);refs->mBase->onLastStrongRef(id);int32_t flags = refs->mFlags.load(std::memory_order_relaxed);if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {delete this;}}refs->decWeak(id);//如果不是最后一个,需要减少弱引用链表
}
incStrong
RefBase类的成员函数incStrong 增加对象的引用计数
实现如下:
frameworks/base/libs/utils/RefBase.cpp
//RefBase类的成员函数incStrong来增加对象的引用计数
void RefBase::incStrong(const void* id) const
{weakref_impl* const refs = mRefs;refs->addWeakRef(id);refs->incWeak(id);//增加对象的引用计数refs->addStrongRef(id);//增加对象的强引用计数const int32_t c = android_atomic_inc(&refs->mStrong);LOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);#if PRINT_REFSLOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);
#endifif (c != INITIAL_STRONG_VALUE) {return;}android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
//如果对象第一次被强指针引用,进行通知const_cast<RefBase*>(this)->onFirstRef()
}
弱引用
incWeak
只有RefBase::weakref_type
的对象才能调用 incWeak
和decWeak
。
frameworks/base/libs/utils/RefBase.cpp
void RefBase::weakref_type::incWeak(const void* id)
{weakref_impl* const impl = static_cast<weakref_impl*>(this);impl->addWeakRef(id);const int32_t c = android_atomic_inc(&impl->mWeak);LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}
decWeak
只有RefBase::weakref_type
的对象才能调用 incWeak
和decWeak
。
void RefBase::weakref_type::decWeak(const void* id)
{weakref_impl* const impl = static_cast<weakref_impl*>(this);impl->removeWeakRef(id); //删除弱引用//减少对象的弱引用计数,并且返回减少之前的值,存储在c中const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release); //计数减1ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);/*如果c不等于1则不用进行处理,如果c=1,说明弱引用计数为0,则强引用计数必定也为0,则考虑如何释放该对象*/if (c != 1) return; //还有其他引用在用这个指针,直接返回/*释放方式取决于当前对象的生命周期控制标志mFlags的值0,只要强引用计数为0,系统则自动释放这个对象OBJECT_LIFETIME_WEAK,只有强引用计数和弱引用计数都为0,系统才会自动释放这个对象OBJECT_LIFETIME_FOREVER,系统永远不会释放这个对象,需要开发者手动释放*/ atomic_thread_fence(std::memory_order_acquire);int32_t flags = impl->mFlags.load(std::memory_order_relaxed);if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {// This is the regular lifetime case. The object is destroyed// when the last strong reference goes away. Since weakref_impl// outlive the object, it is not destroyed in the dtor, and// we'll have to do it here.if (impl->mStrong.load(std::memory_order_relaxed)== INITIAL_STRONG_VALUE) {// Special case: we never had a strong reference, so we need to// destroy the object now.delete impl->mBase; //如果没有其他的引用了,需要释放当前指针} else {// ALOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);delete impl;}} else {// This is the OBJECT_LIFETIME_WEAK case. The last weak-reference// is gone, we can destroy the object.impl->mBase->onLastWeakRef(id);if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {if (impl->mBase) {if (impl->mDestroyer) {impl->mDestroyer->destroy(impl->mBase);} else {delete impl->mBase;//如果没有其他引用了,就释放当前指针}}}}
}
weakref_type 具体实现
weakref_type类
定义了引用计数维护接口,具体的实现由weakref_impl类
实现。
frameworks/base/libs/utils/RefBase.cpp
class RefBase::weakref_impl : public RefBase::weakref_type
{
public:volatile int32_t mStrong; //描述强引用计数volatile int32_t mWeak; //描述弱引用计数RefBase* const mBase; //指向它所引用的对象的地址/*mFlags取值范围0,表示对象的生命周期只收强引用计数影响OBJECT_LIFETIME_WEAK, 表示对象的生命周期同时受强引用计数和弱引用计数影响OBJECT_LIFETIME_FOREVER,表示对象的生命周期完全不受强引用指针和弱引用指针影响*/volatile int32_t mFlags;//描述对象生命周期控制方式Destroyer* mDestroyer;#if !DEBUG_REFSweakref_impl(RefBase* base): mStrong(INITIAL_STRONG_VALUE), mWeak(0), mBase(base), mFlags(0), mDestroyer(0){}void addStrongRef(const void* /*id*/) { }void removeStrongRef(const void* /*id*/) { }void addWeakRef(const void* /*id*/) { }void removeWeakRef(const void* /*id*/) { }void printRefs() const { }void trackMe(bool, bool) { }#else
....//省略部分不重要内容
#endif
};
析构函数
RefBase类
中成员变量mRefs
的解析函数的实现。
frameworks/base/libs/utils/RefBase.cpp
RefBase::~RefBase()
{/*如果发现对象的弱引用计数为0,则在释放RefBase对象的同时也将引用计数对象mRefs一起释放掉。mRefs对象在RefBase的构造函数中被创建,指向weakref_impl,如果RefBase对象都不存在了,并且mWeak也为0,mRefs也没有什么存在的必要了。强引用计数一定是小于弱引用计数的,所以强引用计数为0时弱引用计数仍有可能大于0。所以在弱引用计数大于0的情况下我们只释放RefBase对象,留下mRefs对象继续使用。*/if ((mRefs->mFlags & OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK) {if (mRefs->mWeak == 0) {delete mRefs;}...}
}
相关文章:

RAII - 安卓中的智能指针
RAII - 安卓中的智能指针 概念 sp wp RefBase 是什么 system/core/libutils/RefBase.cpp system/core/libutils/include/utils/RefBase.hsystem/core/libutils/StrongPointer.cpp system/core/libutils/include/utils/StrongPointer.hAndroid在标准库之外,自定义…...

linux--库指令
ldd ldd 可执行文件路径 显示依赖的库的查找路径以及是否查找到了。...

展讯方案-内置多张开机logo
1. 开机图片的资源存放在logo分区中,这个分区中可以存放一个xx.bmp文件,也可以存放一个bin文件(1logo.bin,包含多张压缩的图片集合) 2.平台代码中logo.bin是由mk_1ogo_img.py脚本打包,具体如下(…...

Stable Diffusion模型资源合集(附整合包)
(模型资源在ComfyUI、WebUI以及ForgeUI中都通用) 之前的Stable Diffusion笔记受到了不少小伙伴的关注,很感谢大家的建议和支持。有很多小伙伴私信我问我一些AI绘画的模型资源在哪来下载,一般来说有两个网站比较常用,分…...

机器学习|Pytorch实现天气预测
机器学习|Pytorch实现天气预测 🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 电脑系统:Windows11 显卡型号:NVIDIA Quadro P620 语言环境:python 3.9.7 编译器&#x…...

【Kuberntes】k8s权限管理
文章目录 权限管理概述核心概念配置RBAC创建Role和ClusterRole创建RoleBinding和ClusterRoleBinding 默认角色和角色绑定权限的实现注意事项 如何在 Kubernetes 中实现 RBAC 的细粒度权限控制?1. Role和ClusterRole2. RoleBinding和ClusterRoleBinding3. 配置RBAC4.…...

C++,STL 033(24.10.15)
内容 queue容器(队列)的常用接口。 代码 #include <iostream> #include <string> #include <queue> // 注意包含queue容器(队列)的头文件using namespace std;class Person { public:string m_Name;int m_Age…...

AdmX_new
0x00前言 因为环境问题,此次靶场都放在vm上。都为NAT模式。 靶机地址: https://download.vulnhub.com/admx/AdmX_new.7z 需要找到两个flag文件。 0x01信息搜集 搜集IP 确认目标IP为172.16.8.131,进一步信息搜集 获取端口开放情况,版本信…...

【python3】函数注解
Python 函数注解 (Function Annotations) Python 函数注解 (Function Annotations)函数注解的基本语法基本语法格式示例 特殊类型注解注解信息的存储与访问函数注解的实际用途注意事项小结 函数注解是 Python 的一种特性,用于为函数的参数和返回值添加 元数据。注解…...

leetcode hot100 之【LeetCode 42. 接雨水】 java实现
LeetCode 42. 接雨水 题目描述 给定一个非负整数数组 height 表示柱状图中每个柱子的高度,请你计算按此排列的柱状图能接多少雨水。 示例 1: 输入:height [0,1,0,2,1,0,1,3,2,1,2,1] 输出:6 解释:上面的柱状图可以…...

10月18日,每日信息差
第一、现代汽车集团在上海举办了中国前瞻技术研发中心的发布及启新庆典,宣布成立其全资法人公司 —— 现代前瞻汽车技术开发(上海)有限公司。该中心是集团在海外建立的首个前瞻技术研发中心,专注于自动驾驶、智能座舱、共享出行等…...

Axure科技感元件:打造可视化大屏设计的得力助手
Axure,作为一款专业的原型设计工具,凭借其强大的设计功能、丰富的组件库和灵活的交互能力,成为了许多设计师打造科技感设计的首选工具。其中,Axure科技感元件更是以其独特的魅力和实用性,在数据可视化大屏、登录界面、…...

【模板】最近公共祖先(LCA)倍增
P3379 P3379 【模板】最近公共祖先(LCA) # 【模板】最近公共祖先(LCA) ## 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。 ## 输入格式 第一行包含三个正整数 $N,M,S$&#…...

我的JAVA项目构建
1.Maven maven就是pip 设置maven下载的的jar包位置 换源 下载插件maven-search 配置dependency 2.Tomcat 设置环境变量JAVA_HOME 设置编码方式 方框就是路径的前缀 3.Servlet 新建项目 写一个类继承HttpServlet,复写doGet(应对Get请求),doPost(应对…...

应用层协议 序列化
自定义应用层协议 例子:网络版本计算器 序列化反序列化 序列化:将消息,昵称,日期整合成消息-昵称-日期 反序列化:消息-昵称-日期->消息,昵称,日期 在序列化中,定义一个结构体…...

【HAD】Half-Truth: A Partially Fake Audio Detection Dataset
文章目录 Half-Truth: A Partially Fake Audio Detection Dataset背景key points研究数据集设计评价指标实验基线:utterance-level分类(话语级)基线:segment-level分类(片段级)Half-Truth: A Partially Fake Audio Detection Dataset 会议/期刊:Interspeech 2021 CCF-C…...

OpenAI Prompt generation - 生成和优化Prompt的Prompt
OpenAI Prompt generation - 生成和优化Prompt的Prompt 从头开始创建 Prompt 可能很耗时,所以快速生成 Prompt 可以帮助我们提高效率。 下面是 OpenAI 提供的协助生成 Prompt 的 Prompt。 from openai import OpenAIclient OpenAI()META_PROMPT ""&qu…...

Android技术探索:深入解析Android组件
Android系统以其开放性和多样性,成为了众多开发者的首选平台。在Android应用的开发中,组件(Components)是构建应用的基础元素。深入了解Android组件,对于开发者来说至关重要。本文将详细探讨Android的四大核心组件&…...

使用R-GCN处理异质图ACM的demo
加载和处理数据集 import torch from torch_geometric.datasets import HGBDataset from torch_geometric.transforms import RandomLinkSplit# 加载ACM数据集,这是一个包含论文(paper)、主题(subject)以及它们之间关…...

征程 6E DISPLAY 功能介绍及上手实践
01 功能概述 本文将带大家一起实现单路、多路 MIPI CSI TX 输出、IDU 回写、IDU oneshot 模式、绑定输出 VPS 数据等功能,此处主要介绍各 sample 的实现与使用方法。 02 软件架构说明 本文中绑定 VPS 输出功能基于 libvio API 实现,调用 libvio 提供的…...

安卓窗口wms/input小知识NO_INPUT_CHANNEL剖析
背景: 经常在学员的vip技术群里经常有很多学员会提问一些不太常见的窗口和input的相关的问题,虽然不太常见,但确实是工作中会遇到的一些问题,所以马哥有必要进行一下记录这些窗口技术知识点。 具体分享技术点: input中…...

【2024最新版】Win10下 Java环境变量配置----适合入门小白
首先,你应该已经安装了 Java 的 JDK 了(如果没有安装JDK,请跳转到此网址:http://www.oracle.com/technetwork/java/javase/downloads/index-jsp-138363.html) 笔者安装的是 jdk-8u91-windows-x64 接下来主要讲怎么配…...

Servlet 生命周期详解及案例演示(SpringMVC底层实现)
文章目录 什么是Servlet?Servlet生命周期简介1. 初始化阶段:init()方法示例代码: 2. 请求处理阶段:service() 和 doGet()、doPost()方法示例代码: 3. 销毁阶段:destroy()方法示例代码: Servlet生…...

2024 kali系统2024版本,可视化界面汉化教程(需要命令行更改),英文版切换为中文版,基于Debian创建的kali虚拟机
我的界面如下所示 1. 安装 locales sudo apt install locales 2. 生成中文语言环境 sudo locale-gen zh_CN.UTF-8 如果你希望安装繁体中文,可以加入: sudo locale-gen zh_TW.UTF-8 3. 修改 /etc/default/locale 文件 确保有以下内容 LANGzh_CN.UT…...

深入理解 CMake 中的 INCLUDE_DIRECTORIES 与 target_include_directories
在使用 CMake 构建系统时,指定头文件的包含路径是非常常见的一步。对于这个任务,CMake 提供了两种主要的命令:INCLUDE_DIRECTORIES 和 target_include_directories。虽然它们看似类似,但它们的作用范围、应用方式以及适用场景却有…...

【不知道原因的问题】java.lang.AbstractMethodError
项目场景: 提示:这里简述项目相关背景: 遇到了一个问题: java.lang.AbstractMethodError 问题描述 提示:这里描述项目中遇到的问题: 在Java开发中,java.lang.AbstractMethodError是一个常见…...

分布式篇(分布式事务)(持续更新迭代)
一、事务 1. 什么是事务 2. 事务目的 3. 事务的流程 4. 事务四大特性 原子性(Atomicity) 一致性(Consistency) 持久性(Durability) 隔离性(Isolation) 5. MySQL VS Oracle …...

[Linux] 逐层深入理解文件系统 (2)—— 文件重定向
标题:[Linux] 逐层深入理解文件系统 (2)—— 文件重定向 个人主页水墨不写bug (图片来源于网络) 目录 一、文件的读取和写入 二、文件重定向的本质 1.手动模拟重定向的过程——把标准输出重定向到redir.txt 2.重定向…...

html+css+js实现Badge 标记
实现效果: 代码实现: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Badge…...

纯css 轮播图片,鼠标移入暂停 移除继续
核心 滚动: animation: 动画名称 20s linear infinite normal;停止: animation: 动画名称 20s linear infinite paused; 完整例子: html: <div class"carousel-wrapper"><div class"carousel"><div cl…...