C++ 模板保姆级详解——template<class T>(什么是模板?模板分哪几类?模板如何应用?)
目录
一、前言
二、 什么是C++模板
💦泛型编程的思想
💦C++模板的分类
三、函数模板
💦函数模板概念
💦函数模板格式
💦函数模板的原理
💦函数模板的实例化
🍎隐式实例化
🍉显式实例化
💦模板支持多个模板参数
💦模板参数的匹配原则
四、类模板
💦类模板的概念
💦类模板格式
💦类模板的实例化
💦类模板的分离编译
五、总结
六、共勉
一、前言
在我们学习C++时,常会用到函数重载。而函数重载,通常会需要我们编写较为重复的代码,这就显得臃肿,且效率低下。重载的函数仅仅只是类型不同,代码的复用率比较低,只要有新类型出现时,就需要增加对应的函数。此外,代码的可维护性比较低,一个出错可能会导致所有的重载均出错。
那么,模板的出现,就让这些问题有了解决方案,所以本次博客将为大家详细的讲解C++的模板!!
二、 什么是C++模板
程序设计中经常会用到一些程序实体:它们的实现和所完成的功能基本相同,不同的仅 仅是所涉及的数据类型不同。而模板正是一种专门处理不同数据类型的机制。
模板------是泛型程序设计的基础(泛型generic type——通用类型之意)。
函数、类以及类继承为程序的代码复用提供了基本手段,还有一种代码复用途径——类属类型(泛型),利用它可以给一段代码设置一些取值为类型的参数(注意:这些参数 的值是类型,而不是某类型的数据),通过给这些参数提供一些类型来得到针对不同类 型的代码。
💦泛型编程的思想
首先我们来看一下下面这三个函数,如果学习过了C++函数重载 和 C++引用 的话,就可以知道下面这三个函数是可以共存的,而且传值会很方便
void Swap(int& left, int& right) {int temp = left;left = right;right = temp; } void Swap(double& left, double& right) {double temp = left;left = right;right = temp; } void Swap(char& left, char& right) {char temp = left;left = right;right = temp; }
但是真的很方便吗?这里只有三种类型的数据需要交换,若是我们需要增加交换的数据呢?再CV然后写一个函数吗?
这肯定是不现实的,所以很明显函数重载虽然可以实现,但是有一下几个不好的地方:
- 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数
- 代码的可维护性比较低,一个出错可能所有的重载均出错
那是否能做到这么一点,告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码
⭐总结:
所以,总结上面的这么一个技术,C++的祖师爷呢就想到了【模版】这个东西,告诉编译器一个模子,然后其余的工作交给它来完成,根据不同的需求生成不同的代码
这就是👉泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础
💦C++模板的分类
1️⃣: 函数模板(function tempalte):使用泛型参数的函数(function with generic parameters)
2️⃣:类模板(class template):使用泛型参数的类(class with generic parameters)
三、函数模板
知晓了模版的基本概念后,首先我们要来看的就是【函数模版】
💦函数模板概念
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本
通过函数模板,可以编写一种通用的函数定义,使其能够适用于多种数据类型,从而提高代码的复用性和灵活性。
💦函数模板格式
template<typename T1, typename T2,......,typename Tn> 返回值类型 函数名(参数列表) {//…… } 注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)
因此,交换函数就可以这样套用模板:
template<typename T> void Swap(T& left, T& right) {T temp = left;left = right;right = temp; }int main() {int x = 2, y = 3;cout << x << " " << y << endl << endl;Swap(x, y);cout << x << " " << y << endl << endl;cout << "-------------------------------" << endl;char a = 'a', b = 'b';cout << a << " " << b << endl << endl;Swap(a, b);cout << a << " " << b << endl << endl;cout << "-------------------------------" << endl;double c = 2.1, d = 3.1;cout << c << " " << d << endl << endl;Swap(c, d);cout << c << " " << d << endl << endl;cout << "-------------------------------" << endl;return 0; }
我们通过这个函数模版,分别传入不同数据类型的参数,通过结果的观察可以发现这个函数模版可以根据不同的类型去做一个自动推导,继而去起到一个交换的功能。
💦函数模板的原理
💬 那我现在想问一个问题,请问它们调用的真的是同一个函数吗?
当然不是,这里我们三次Swap不是调用同一个函数,其实Swap的时候根据不同的类型通过模板定制出专属你的类型的函数,然后再调用,这里可以通过反汇编观察到:可以发现,在进行汇编代码查看的时候,被调用的函数模版生成了两个不同的函数,它们有着不同的函数地址,因此可以回答上一小节所提出的问题了,两次所调用的函数是不一样的,是根据函数模版所生成的
函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。
所以其实模板就是将本来应该我们做的重复的事情交给了编译器。
在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。
- 补充:
其实库里面有一个swap函数,因此我们也不需要自己写模板了:
直接套用swap即可:int main() {int x = 2, y = 3;cout << x << " " << y << endl << endl;swap(x, y);cout << x << " " << y << endl << endl;cout << "-------------------------------" << endl;char a = 'a', b = 'b';cout << a << " " << b << endl << endl;swap(a, b);cout << a << " " << b << endl << endl;cout << "-------------------------------" << endl;double c = 2.1, d = 3.1;cout << c << " " << d << endl << endl;swap(c, d);cout << c << " " << d << endl << endl;cout << "-------------------------------" << endl;return 0; }
💦函数模板的实例化
用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:
1️⃣: 隐式实例化 2️⃣: 显式实例化
🍎隐式实例化
隐式实例化 :让编译器根据实参推演模板参数的实际类型
template<class T> T Add(const T& left, const T& right) {return left + right; } int main() {int a1 = 10, a2 = 20;double d1 = 10.0, d2 = 20.0;Add(a1, a2); //编译器推出T是intAdd(d1, d2); //编译器推出T是doublereturn 0; }
⚠ 注意:但是我调用的时候如若这样就会出错:
int main() {int a1 = 10, a2 = 20;double d1 = 10.0, d2 = 20.0;Add(a1, d1); //err 编译器推不出来/* 该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型 通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有 一个T,编译器无法确定此处到底该将T确定为int 或者 double类型而报错 注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅*/ }
此时我们发现在模板函数里面,形参不匹配,就会出现报错
编译器无法确定这里的T到底是int还是double。此时有两种处理方式:
法一:用户自己来强制转化int main() {int a1 = 10, a2 = 20;double d1 = 10.0, d2 = 20.0;Add(a1, (int)d1); //强制类型转换。或者Add((double)a1, d1); }
法二:使用显式实例化
接下来进行讲解。
🍉显式实例化
显式实例化:在函数名后的<>中指定模板参数的实际类型
继刚才的例子,法二:使用显式实例化template<class T> T Add(const T& left, const T& right) {return left + right; } int main() {int a1 = 10, a2 = 20;double d1 = 10.0, d2 = 20.0;//显示实例化Add<int>(a1, d1); //double隐式类型转换成int Add<double>(a1, d2); return 0; }
💦模板支持多个模板参数
template<class K, class V> //两个模板参数 void Func(const K& key, const V& value) {cout << key << ":" << value << endl; } int main() {Func(1, 1); //K和V均intFunc(1, 1.1);//K是int,V是doubleFunc<int, char>(1, 'A'); //多个模板参数也可指定显示实例化不同类型 }
💦模板参数的匹配原则
- 原则1: 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
//专门处理int的加法函数 int Add(int left, int right) {return left + right; } //通用加法函数 template<class T> T Add(T left, T right) {return left + right; } int main() {Add(1, 2); //会调用哪个Add函数? }
首先,这俩Add可以同时存在,关键是我调用Add时调的是模板函数Add,还是专门的Add?
通过反汇编得知,调用的是专属Add函数。得出结论:编译器在调用时,有现成的就调用现成的,没有就套用模板。当然,我们也有办法强制让编译器走模板函数,如下:
void Test() {Add(1, 2); // 与非模板函数匹配,编译器不需要特化Add<int>(1, 2); // 调用编译器特化的Add版本 }
- 原则2:对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板
// 专门处理int的加法函数 int Add(int left, int right) {return left + right; } // 通用加法函数 template<class T1, class T2> T1 Add(T1 left, T2 right) {return left + right; } void Test() {Add(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化Add(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数 }
四、类模板
💦类模板的概念
类模板是对成员数据类型不同的类的抽象,它说明了类的定义规则,一个类模板可以生成多种具体的类。与函数模板的定义形式类似, 类模板也是使用template关键字和尖括号“<>”中的模板形参进行说明,类的定义形式与普通类相同。
💦类模板格式
- 首先来看到的就是其定义格式,函数模版加在函数上,那对于类模版的话就是加在类上
template<class T1, class T2, ..., class Tn> class 类模板名 {// 类内成员定义 };
我们以下面这个Stack类为例来进行讲解
- 如果你学习了模版的相关知识后,一定会觉得这个类的限制性太大了,只能初始化一个具有整型数据的栈,如果此时我想要放一些浮点型的数据进来的话也做不到,代码如下:
// C++ 正常情况下,如果需要不同类型的类(int char double 等) typedef int DataType; class Stack { public:// 构造函数Stack(int capacity = 3) //初始化列表:_array(new DataType[capacity]) // 开辟一个DateType的动态数组,并进行初始化, _capacity(capacity),_size(0){}void Push(DataType data){// CheckCapacity();_array[_size] = data;_size++;}// 其他方法...~Stack(){delete[]_array;_array = nullptr;_size = _capacity = 0;} private:DataType* _array;int _capacity;int _size; }; int main() {Stack s1;return 0; }
💬 如果没有模版技术的话你会如何去解决这个问题呢?很简单那就是定义多个类
这是我们同学最擅长的事,CV一下两个栈就有了,StackInt
存放整型数据,StackDouble
存放浮点型数据class StackInt class StackDouble
但是本文我们重点要讲解的就是【模版技术】,技术界有一句话说得好 “不要重复造轮子”
- 下面就是使用模版去定义的一个类,简称【模板类】,不限制死数据类型,将所有的DataType都改为【T】,代码如下:
template<class T1> class Stack { public:// 构造函数Stack(int capacity = 4):_a(new T1[capacity]),_capacity(capacity),_size(0){}void Push(T1 data){_a[_size] = data;_size++;}// ...其他方法// 析构函数~Stack(){delete[]_a;_a = nullptr;_capacity = _size = 0;} private:T1* _a;int _capacity;int _size; };int main() {Stack<int> s1;Stack<double> s2;return 0; }
💦类模板的实例化
👉 类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟
<>
,然后将实例化的类型放在<>
中即可,类模板名字不是真正的类,而实例化的结果才是真正的类
- 可以看到因为我们将这个类定义为了类模版,此时便可以去初始化不同数据类型的栈了,上面说到过
Stack
是类名,但是像Stack<int>
、Stack<double>
这些都是它的类型int main() {Stack<int> s1; // intStack<double> s2; // doubleStack<char> s3; // charreturn 0; }
💦类模板的分离编译
上面这样写的栈代码,其实并不是最规范的写法,还记得我们在学习C++类和对象讲到过一个类要声明和定义分离,那对于模板类也同样适用,我们马上来看看
首先进行栈 类的模板声明:#include <iostream> #include <stdio.h> #include <stdlib.h> using namespace std;template<class T1> class Stack { public:// 构造函数Stack(int capacity = 4);//插入函数void Push(T1 data);// 其他方法....// 析构函数~Stack(); private:T1* _a;int _capacity;int _size; };
其次进行栈 类的模板定义:先看一下构造函数的定义
// 构造函数 Stack::Stack(int capacity):_a(new T1[capacity]),_capacity(capacity),_size(0) {}
- 不过呢可以看到直接像我们之前那样去进行类外定义似乎行不通,说
缺少类模版“Stack”的参数列表
,因为这个成员函数内部也使用到了模版参数T,那么这个函数也要变为函数模版才行
- 这里要强调一点的是对于普通类来说类名和类型是一样的, 像构造函数,它的函数名就是类名;可是对于模板类来说是不一样,类名和类型不一样,这里
Stack
只是这个模版类的类名罢了,但我们现在需要的是类型,此处就想到了我们在上面所学的【显式实例化】,这个模板类的类型即为Stack<T>
所以正确的写法为:
#include "Stack.h"template<class T1> // 构造函数 Stack<T1>::Stack(int capacity):_a(new T1[capacity]),_capacity(capacity),_size(0) {} template<class T1> // 插入函数 void Stack<T1>::Push(T1 data) {_a[_size] = data;_size++; } // 析构函数 template<class T1> Stack<T1>::~Stack() {delete[]_a;_a = nullptr;_capacity = _size = 0; }
⚠ 注意:类模板,是不支持,声明,定义,测试分开写的,会出现链接编译错误,如下:
解决方法:将测试和定义写在一起
这样就不会报错啦!
五、总结
- 首先呢我们介绍了什么是【函数模版】,新学习了一个关键字叫做template,用它再配合模版参数就可以去定义出一个函数模版,有了它,我们在写一些相同类型函数的时候就无需去进行重复的CV操作了,在通过汇编观察函数模版的原理后,清楚了我们只需要传入不同的类型,此时模版参数就会去进行一个自动类型推导,从而产生不同的函数。函数模版定义好后还要对其实例化才能继续使用,但此时要注意的一点是如果传递进去的类型个数与模版参数的个数不匹配的话,其就无法完成自动类型推导,因为这会产生一个歧义。所以想要真正学好模版,这点是一定要搞清楚的!!!
- 接下去呢我们又学习了【类模版】,没想到吧,类也可以变成一个模版,以Stack类为例,对于类模版而言,其类名和类型与普通类是不一样的,这点要注意了,尤其体现在类的成员函数放在类外进行定义的时候,也要将其定义为函数模版,函数名前面指明其类型,这才不会出问题。有了类模版之后,我们去显式实例化不同的数据类型后也可以让模版参数去做一个自动类型推导从而得到不同数据类型的栈
六、共勉
以下就是我对C++ 模板的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新对C++STL库的理解,请持续关注我哦!!!
相关文章:

C++ 模板保姆级详解——template<class T>(什么是模板?模板分哪几类?模板如何应用?)
目录 一、前言 二、 什么是C模板 💦泛型编程的思想 💦C模板的分类 三、函数模板 💦函数模板概念 💦函数模板格式 💦函数模板的原理 💦函数模板的实例化 🍎隐式实例化 🍉显式实…...

uni.getLocation() 微信小程序 线上获取失败
开发版,体验版,用此方法都可以正确获取定位,但是在小程序的线上,总是获取失败 参考:uni-app微信小程序uni.getLocation获取位置;authorize scope.userLocation需要在app.json中声明permission;小程序用户拒绝授权后重新授权-CSDN博客 uniapp 中的 uni.…...

Pytorch损失函数、反向传播和优化器、Sequential使用
Pytorch_Sequential使用、损失函数、反向传播和优化器 文章目录 nn.Sequential搭建小实战损失函数与反向传播优化器 nn.Sequential nn.Sequential是一个有序的容器,用于搭建神经网络的模块被按照被传入构造器的顺序添加到nn.Sequential()容器中。 import torch.nn …...

css:两个行内块元素和图片垂直居中对齐
目录 两个行内块元素垂直居中对齐图片垂直居中问题图片和文字垂直居中对齐参考文章 两个行内块元素垂直居中对齐 先看一段代码: <style> .box {width: 200px;height: 200px;line-height: 200px;font-size: 20px;text-align: center;display: inline-block;b…...

从0开始python学习-34.pytest常用插件
目录 1. pytest-html:生成HTML测试报告 2.pytest-xdist:并发执行用例 3. pytest-order:自定义用例的执行顺序 4. pytest-rerunfailures:用例失败时自动重试 5. pytest-result-log:用例执行结果记录到日志文件 1. pytest-html…...

初始MySQL(二)(表的增删查改)
目录 修改表 CRUD(增删改查) insert语句(表中增加数据) update语句(修改表中的数据) delete删除语句 select语句 修改表 添加列 ALTER TABLE tablename ADD (column datatype [DEFAULT expr] [, column datatype] ...); 修改列 ALTER TABLE tablename MODIFY (column …...

SLAM从入门到精通(SLAM落地的难点)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 在所有的slam算法中,基于反光柱的激光slam和基于二维码的视觉slam是落地最彻底的两种slam方法。和磁条、色带等传统导航方式相比较&…...

通过内网穿透快速搭建公网可访问的Spring Boot接口调试环境
文章目录 前言1. 本地环境搭建1.1 环境参数1.2 搭建springboot服务项目 2. 内网穿透2.1 安装配置cpolar内网穿透2.1.1 windows系统2.1.2 linux系统 2.2 创建隧道映射本地端口2.3 测试公网地址 3. 固定公网地址3.1 保留一个二级子域名3.2 配置二级子域名3.2 测试使用固定公网地址…...

职业迷茫,我该如何做好职业规划
案例25岁男,入职2月,感觉自己在混日子,怕能力没有提升,怕以后薪资也提不起来。完全不知道应该往哪个方向进修,感觉也没有自己特别喜欢的。感觉自己特别容易多想,想多年的以后一事无成的样子。 我觉得这个案…...

数据结构----顺序栈的操作
1.顺序栈的存储结构 typedef int SElemType; typedef int Status; typedef struct{SElemType *top,*base;//定义栈顶和栈底指针int stacksize;//定义栈的容量 }SqStack; 2.初始化栈 Status InitStack(SqStack &S){//初始化一个空栈S.basenew SElemType[MAXSIZE];//为顺序…...

web3 React Dapp书写订单 买入/取消操作
好 上文web3 前端dapp从redux过滤出 (我创建与别人创建)正在执行的订单 并展示在Table上中 我们过滤出了 我创建的 与 别人创建的 且 未完成 未取消的订单数据 这边 我们起一下 ganache 环境 ganache -d然后 我们项目 发布一下智能合约 truffle migrate --reset然…...

C++学习---信号处理机制、中断、异步环境
文章目录 前言信号处理signal()函数关于异步环境 信号处理函数示例raise()函数 前言 信号处理 关于信号,信号是一种进程间通信的机制,用于在程序执行过程中通知进程发生了一些事件。在Unix和类Unix系统中,信号是一种异步通知机制,…...

机器学习——奇异值分解案例(图片压缩-代码简洁版)
本想大迈步进入前馈神经网络 但是…唉…瞅了几眼,头晕 然后想到之前梳理的奇异值分解、主成分分析、CBOW都没有实战 如果没有实际操作,会有一种浮在云端的虚无感 但是如果要实际操作,我又不想直接调用库包 可是…如果不直接调包,感…...

【Go入门】面向对象
【Go入门】面向对象 前面两章我们介绍了函数和struct,那你是否想过函数当作struct的字段一样来处理呢?今天我们就讲解一下函数的另一种形态,带有接收者的函数,我们称为method method 现在假设有这么一个场景,你定义…...
Asp.Net Core 中使用配置文件
本文参考微软文档:ASP.NET Core 中的配置 ASP.NET Core 中的应用程序配置是使用一个或多个配置程序提供程序执行的。 配置提供程序使用各种配置源从键值对读取配置数据: 设置文件,例如 appsettings.json环境变量Azure Key VaultAzure 应用配…...

深入理解JVM虚拟机第二十四篇:详解JVM当中的动态链接和常量池的作用
大神链接:作者有幸结识技术大神孙哥为好友,获益匪浅。现在把孙哥视频分享给大家。 孙哥链接:孙哥个人主页 作者简介:一个颜值99分,只比孙哥差一点的程序员 本专栏简介:话不多说,让我们一起干翻J…...
QGridLayout
QGridLayout QGridLayout 是 Qt 框架中的一个布局管理器类,用于在窗口或其他容器中创建基于网格的布局。 QGridLayout 将窗口或容器划分为行和列的网格,并将小部件放置在相应的单元格中。可以通过调整行、列和单元格的大小来控制布局的样式和结构。 以…...

万能在线预约小程序系统源码 适合任何行业在线预约小程序+预约到店模式 带完整的搭建教程
大家好啊,源码小编又来给大家分享啦!随着互联网的发展和普及,越来越多的服务行业开始使用在线预约系统以方便客户和服务管理。例如,美发店、健身房、餐厅等都可以通过在线预约系统提高服务效率,减少等待时间࿰…...
Leetcode 2935. Maximum Strong Pair XOR II
Leetcode 2935. Maximum Strong Pair XOR II 1. 解题思路2. 代码实现 题目链接:2935. Maximum Strong Pair XOR II 1. 解题思路 这一题又是一个限制条件下找“最大值”的问题,不过这里的最大值是XOR之后的最大值。 而要求XOR之后结果的最大值&#x…...

[直播自学]-[汇川easy320]搞起来(4)看文档 查找设备(续)
2023.11.12 周六 19:05 补充一下关于以太网查找设备,如果设置如下: 然后点击测试: 点击ping 如果设置如下: 测试和ping和上图一样。 这就设计的有点不大好了! 另…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...

遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...