string 类以及模拟实现

𝙉𝙞𝙘𝙚!!👏🏻‧✧̣̥̇‧✦👏🏻‧✧̣̥̇‧✦ 👏🏻‧✧̣̥̇:Solitary_walk

      ⸝⋆   ━━━┓
     - 个性标签 - :来于“云”的“羽球人”。 Talk is cheap. Show me the code
┗━━━━━━━  ➴ ⷯ

本人座右铭 :   欲达高峰,必忍其痛;欲戴王冠,必承其重。

👑💎💎👑💎💎👑 
💎💎💎自💎💎💎
💎💎💎信💎💎💎
👑💎💎 💎💎👑    希望在看完我的此篇博客后可以对你有帮助哟

👑👑💎💎💎👑👑   此外,希望各位大佬们在看完后,可以互相支持,蟹蟹!
👑👑👑💎👑👑👑 

目录:

一:C语言里面的字符串

二:标准库里面的string

三:string常用的接口

四:对string 这个类的模拟实现


1:C语言里面的字符串
C 语言中,字符串是以 '\0'结尾 的一些字符的集合,为了操作方便, C 标准库中提供了一些 str 系列的库函数, 但是这些库函数与字符串是分离开的,不太符合OOP(面向对象编程,核心:封装,继承,多态) 的思想。
但是有很多局限:很容易出现越界访问
2:标准库里面的string
1.) string 是表示字符串的字符串类
2. ) 该类的接口与常规容器的接口基本相同,无非就是添加了一些专门用来操作 string 的常规操作。

3. )string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>  string;

4. ) 不能操作多字节或者变长字符的序列。
在使用string类时,必须包含#include头文件以及using namespace std;
3:string常用的接口
3.1对string 对象常用到的构造函数

常用到的构造函数:多为这4个

 使用示范:

3.2string 对象的容量的操作

1)size () 使用

 简言之就是:size () 这个函数只统计字符串的有效个数(\0之前的字符)

2)resize() 和 reserve () 使用以及对比

 通俗的理解就是:resize ( )这个函数可以进行扩容(并默认支持指定 /0 来进行初始化初始化)也可以进行缩容

 运行结果:

reserve () :这个函数也可以进行空间的开辟,但是不支持初始化(一般当提前大概知道当前对象的大小的时候可以进行空间开辟,避免底层扩容的消耗,代价过大) 

3)capacity()

 capacity():这个函数是以字节作为单位返回当前指向对象被开辟的空间大小,注意capacity有效空间的大小并不受当前size()的影响

4)clear () 

简言之就是:把当前的有效的字符进行删除

 

3.3 对string 对象的访问以及遍历操作
1) operator [ ]

operator [ ] : 返回下标为pos 对应位置的字符

 

2) begin ()+ end( )

注意:begin 指向当前字符的第一个位置,end 指向最后一个有效字符的下一个位置,范围是左闭右开

 使用示范:

3) rebegin( ) reend( )

使用示范:

4) 范围for

3.4 对string  对象的修改操作
3.4.1) push_back()  append( )

push_back: 在原来字符串的后面追加一个字符

 使用示范:

3.4.2) operator +=

对于以上追加一个字符和一个字符串的过于复杂了,有没有一个函数就可以搞定的???

3.4.3) find() 和  pos() substr()   getline( )

find () 这个函数 的返回值:若是存在返回第一个字符出现的对应下标位置否则返回npos

使用示范:

 对于getline( )这个函数结合具体的例题更有助于理解

对于这个题其实并不难理解,关键就是如何处理输入中的空格或者是 \0 ,这个时候使用getline () 即可,默认遇到缓冲区里面的空格或者是 \0并不结束读取

#include <iostream>
#include <string>
using namespace std;

int main()
{
	string str;
	//cin >> str;
	getline(cin, str);
	//对于scnaf ()和cin 进行输入的时候:当有多个数据进行输入的时候,编译器会自动把空格或者是换行作为数据分割开的依据
	int len = 0;
	size_t pos = str.rfind(' ');
	if (pos != string::npos)//已经找到
    {  
		len = str.size() - (pos + 1);
        cout << len << endl;
    }
	else
	 cout<< str.size()<<endl;//就是只有整个单词
	
	
}
 3.4.5) vs下的string深度理解

在VS下:一个string 类的对象 大小是28个字节,包括默认一个16个字节的数组,一个维护_size 的变量,一个维护_capacity的变量,和维护堆上的一个指针变量(默认32平台)总大小 = 16 + 3*4 = 28

注意: 对于VS而言,\0不算做有效字符,所以_capacity的大小是15个字节

3.5对string 非类的成员函数
4:对string 这个类的模拟实现
4.1 有参以及无参的构造函数模拟

首先需要自己定义一个类:string (我为了避免与库里面的string 冲突,把自己定义的这个string 放在 Y 这个命名空间里面)

string 这个类的定义

namespace Y
{
	class string
	{
	public:
		
	private:
		size_t _size;
		size_t _capacity;
		const char* _str;
	};
}

 构造函数的实现

namespace Y
{
	class string
	{
	public:
		//无参的构造函数
		string()
			:_size(0)
			, _capacity(_size)
			, _str(new char[1]{ '\0' })  // 开一个空间并进行初始化
		{}
		//有参的构造函数
		string(const char* s = "")
			:_size(strlen(s))
			,_capacity(_size)
		{
			_str = new char[_size+1];
			strcpy(_str, s);
		}
	private:
		size_t _size;
		size_t _capacity;
		 char* _str;
	};
}

注意有参构造函数 的参数类型必须的const char * 类型,所以说是不能传 '\0'

在对_str开空间的时候要多开一个是为了存放 \0

4.2 析构函数模拟
        ~string()//析构函数
		{
			delete[] _str;
			_size = _capacity = 0;
		}
4.3 push_back()  append()  operator+=() 模拟

push_back() append()  operator+=()  这三个函数都是在原来字符串的基础上进行尾插,只不过是尾插一个字符还是尾插一个字符串的区别(对这三个函数的具体理解见前面的分析)

push_back() 模拟

进行尾插的时候:需要考虑是否进行扩容以及对_size要进行改变

void reserve(size_t n)
		{
			if (n > _capacity) // 扩容条件:扩容之后的空间必须是大于当前的空间容量
			{
				char* tmp = new char[n + 1];
				//对原来数据进行拷贝
				strcpy(tmp, _str);
				delete []_str;//_str 是一个数组
				_str = tmp;
				_capacity = n;
			}
		}
		void push_back(const char ch = '\0')
		{
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}
append() 模拟
	void append(const char* str)
		{
			size_t len = strlen(str);
			if (len + _size > _capacity)
			{
				reserve(len + _size);//注意这里不能单纯的以2倍扩容(当size= 4,len= 80,有问题)
			}
			strcpy(_str + _size, str);
			_size += len;
			//_str[_size] = '\0'; //err 因为strcpy拷贝的时候会自动拷贝\0过去
		}
operator +=()模拟

其实就是对push_back() 以及 append() 函数的封装

string& operator+=(const char ch)
		{
			push_back(ch);
			return *this;
		}
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}
4.4 insert ()模拟

错误使用:当对字符串下标为0 的位置插入字符或者是字符串的时候,程序就有问题了:

分析:这是因为发生了整形提升,因为end 类型是size_t 当end = -1是进入死循环导致end 下标的越界最终程序出现问题

 解决:

1) 对pos 进行类型强转

2)改变 end 所指向的位置

 

4.5 erase 模拟

erase介绍见前序

指定删除序列的n个字符从pos位置开始,(注意默认pos是从下标为0的时候,len默认是整个字符串)

 代码:
void erase(size_t pos , size_t len = npos)
		{
			//1:特殊情况处理 (直接对_size,以及\0改变)2:正常情况(数据覆盖)
			if (len >= _size || pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			//正常删除
			else
			{
				size_t end = pos + len;
				while (end <= _size)
				{
					_str[pos] = _str[end];
					end++;
					pos++;
				}
				_size -= len;
			}
		}
4.6  流插入的重载(<<)

为了更好的对类成员的访问(有的类成员可能设置成private,protected)这里把流插入函数写成友元函数:注意并不是所有 的 流插入函数都写成友元函数,写成友元函数只是更好的对类成员的访问(这个小小的知识点可能是面试官的灵魂拷问哟)

 

ostream& operator<<(ostream& out, const string& s)
	{
		for (size_t i = 0; i < s._size; i++)// i< _size 错误:
		{
			cout << s._str[i];
		}
		return out;
	}
4.7 流提取的重载(>>)
istream& operator>>(istream& in, string& s)
	 {
		 s.reserve(128);//避免频繁开空间
		 char ch = in.get();
		 //char buff[129];//多开一个为\0存储
		 size_t i = 0;

		 while (ch !='\n' && ch!= ' ')//从缓冲区读取数据的时候,默认遇到\n 或者空格读取结束
		 {
			
			 s += ch;
			 ch = in.get();
		 }
		 
		 cout << s.size() << endl;
		 cout << s.capacity() << endl;
		 //问题:当输入字符个数过少的时候,会造成空间 空间的浪费,而且是不可回收的
		 //解决:提前整个数组,这样即使输入字符个数很少程序结束的时候也会归还系统
		 return in;
	 }

运行结果:

 

自习分析:发现我们的代码有很大的优化空间,当我们输入的字符个数过少 的时候会造成所开空间的浪费,而且是不可回收的,那岂不是有待优化对于咱们的代码?

代码:

 istream& operator>>(istream& in, string& s)
	 {
		 //s.reserve(128);//避免频繁开空间
		 char ch = in.get();
		 char buff[129];//多开一个为\0存储
		 size_t i = 0;

		 while (ch !='\n' && ch!= ' ')//从缓冲区读取数据的时候,默认遇到\n 或者空格读取结束
		 {
			 //s += ch;
			 if (i == 128)
			 {
				 buff[i] = '\0';//这一段读取结束,开始下一段读取
				 s += buff;
				 i = 0;
			 }
			 buff[i++] = ch;//读取的每一个字符放到buff这个数组,并让下标++
			 //s += ch;
			 ch = in.get();
		 }
		 if (i != 0)  //可能存在当前buff里面只有一部分的数据
		 {
			 buff[i] = '\0';//一定要置空,否则出现随机值
			 s += buff;
		 }
		 cout << s.size() << endl;
		 cout << s.capacity() << endl;
		 //问题:当输入字符个数过少的时候,会造成空间 空间的浪费,而且是不可回收的
		 //解决:提前整个数组,这样即使输入字符个数很少程序结束的时候也会归还系统
		 return in;
	 }

运行结果:

4.8 find ()模拟
对一个字符的查找

		size_t find(char ch, size_t pos = 0)const
		{
			size_t find = 0;
			while (find <_size)
			{
				if (_str[find] == ch)
				{
					return find;
				}
				find++;
			}
			return npos;
		}
对一个字符串的查找
size_t find(const char* str, size_t pos = 0)const
		{
			//找一个子字符串,借助strstr函数
			 char* find = strstr(_str + pos, str);
			if (find != nullptr)
			{
				return find - _str;// 返回找到的第一个字符所对应下标(指针相减 = 个数)
			}
			else
				return  npos;//未找到
		}

 在这里有一个关键的知识点:就是如何把指针转换成对应 的元素个数

指针与指针相减即可

4.9 substr() 模拟

 代码:
string substr(size_t pos = 0, size_t len = 0)
		{
			size_t end = len+pos;//结束的下标
			if (pos + len >= _size || pos == end) //特殊情况处理:要取的字符串可能超出当前的有效字符串 的长度,需要进行修改
			{
				len = _size - pos;//实际取到的有效字符
				end = _size;
			}
			string sub("hello");
			sub.reserve(15);//提前进行空间的开辟
			while (pos < end)
			{
				sub += _str[pos];
				pos++;
			}
			return sub;
		}
4.10  resize()模拟

分析:

resize() 这个函数有双重功能:一个是缩容;另一个是扩容(默认\0进行初始化)

void resize(size_t n,char ch = '\0')
		{
			if (n < _size)//缩容
			{
				_str[n] = ch;
				_size = n;
			}
			else //扩容,默认\0
			{
				reserve(n);
				_size += n;
				size_t i = 0;
				while (i < n)
				{
					_str[i++] = ch;
				}
			}
			//不要忘了最后置空
			_str[_size] = '\0';
		}
4.11 reserve()模拟
void reserve(size_t n)
		{
			if (n > _capacity) // 扩容条件:扩容之后的空间必须是大于当前的空间容量
			{
				char* tmp = new char[n + 1];
				//对原来数据进行拷贝
				strcpy(tmp, _str);
				delete []_str;//_str 是一个数组
				_str = tmp;
				_capacity = n;
			}
		}

 本质上讲:用realloc()函数和用new进行扩容其实没有质的区别,只不过用new,delete等操作符可以支持自定义类型

注意这里是进行异地扩容,不要忘了进行数据 对比以及旧空间的释放

4.12 size() 模拟
size_t size() const
		{
			return _size;
		}
4.13capacity() 模拟
ize_t capacity()
		{
			return _capacity;
		}
4.14  迭代器相关的模拟

4.15  比较关系的运算符的重载

在这里简单的实现以下几个比较关系操作符的书写,其余进行复用,还是挺香的

bool operator>(const string& s2) const
		{
			//借助strcmp()函数一次进行比较返回int 类型的

			//return strcmp(*this, s2._str) > 0; //err  指代不明确
			return strcmp(_str, s2._str) > 0;//只要是在类里面都可以进行对成员变量的访问,不分私有还是公有
		}
		bool operator==(const string& s2) const
		{
			return strcmp(_str, s2._str) == 0;
		}

CV一下

//接下来比较关系运算符的虫重载直接进行复用即可
		bool operator>=(const string& s2) const
		{
			//return (operator>(s2) || operator==(s2));
			return (*this > s2 || *this == s2);
		}
		bool operator<(const string& s2) const
		{
			return (!operator>=(s2));
		}
		bool operator<=(const string& s2) const
		{
			return (!operator>(s2));
		}
		bool operator !=(const string& s2)
		{
			return !(operator==(s2));
		}
 4.16赋值运算符的重载
void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);

		}
//传统写法
		string operator=(const string& s)
		{
			if (this != &s)
			{
				char* tmp = new char[s._size + 1];
				delete _str;
				_str = tmp;
				_size = s._size;
				_capacity = s._capacity;
			}
			return *this;
		}

 现代写法:

void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);

		}
		
		//现代写法
		//  s2 = s1
		string operator=(const string& s) 
		{
			if (this != &s)
			{
				string tmp(s);
				swap(tmp);
			}
			return *this;
		}

 其实本质上没有什么不同,最终编译器还是进行传统写法的调用只不过用现代的写法我们自己是省去了不少事(简言之,就是自己不用搬太多的砖了,有个小弟帮自己干点,就可以减轻自己的负担)

4.17拷贝构造函数的重载

传统写法:

 //传统拷贝构造函数的写法
		 //s2(s1)
		 string(const string& s)
		 {
			 _str = new char[_capacity + 1];
			 //数据拷贝
			 strcpy(_str, s._str);
			 _size = s._size;
			 _capacity = s._capacity;
		 }
		 

现代写法:

// 现代写法的拷贝构造函数
		 // s2(s1)
		 string(const string& s) 
			 : _size(0)
			 ,_str(nullptr)//为什么要对_str置空(不置空的话,此时_str指向的数据是随机值,也就是说此时的_str是野指针):方便后面交换之后,tmp指向的空间为空,这时在调用析构函数的时候不会崩溃,因为只有动态开辟出来的空间才可以进行释放
			 , _capacity(0)
		 {
			 //我在实现的过程中面临的问题:程序出现野指针(没有对tmp 进行初始化)
			 //string tmp(s);//err
			 string tmp(s._str);//将s1里面的字符串初始化tmp 
			 swap(tmp);
		 }
结语:

以上就是关于string相关函数应用以及模拟实现,相信不少老铁们看到这里,已经实属不易了,毕竟大大浪淘沙,最后终能淘的自己的“金银珠宝”。关于前期阶段的理解是很重要的,只要是前期自己掌握了七八成,相信到这里你也是可以看懂滴,希望各位老铁们看到这里都能有所收获,当然我的此篇博客也存在多多的不足之处,欢迎各位大佬们随时指正,当然后期也会不断更新,希望各位大佬们阔以多多支持!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/570855.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Flutter 中优雅切换应用主题的组件

Flutter 中优雅切换应用主题的组件 视频 https://youtu.be/L–XLpc452I https://www.bilibili.com/video/BV1wD421n75p/ 前言 原文 https://ducafecat.com/blog/flutter-app-theme-switch Adaptive Theme 这个组件通过包裹 MaterialApp 的方式整体管理 theme 主题&#xff0…

Java冲突

本身 父类 接口(多) 如果出现同样名字的方法,就会出现冲突 * 情况描述1: * 当一个类,继承了父类,实现了某接口,父类中的成员方法和接口中的方法重名 * 解决方法: * 子类就近选择父类成员方法 亲爹优先原则 * *使用格式: * 父类:super.方法名 * 父接口:父接口名.super.方…

QT——其他方式实现HelloWrold

QT——其他方式实现HelloWrold 使用输入框实现使用代码实现 通过按钮实现信号槽代码方式实现 我们之前对QT实现HelloWorld有了一些基本的了解&#xff0c;用了一些简单的方法实现了HelloWorld&#xff0c;如果对QT还不怎么了解的&#xff0c;可以点击这里&#xff1a; https://…

算法提高 第一期 KMP扩展算法

1## 具体思路&#xff1a; 和KMP算法的是想类似&#xff0c;充分利用已经比较字符性质来减少冗余的字符比较次数。KMP的思想是充分的利用模式串中所有前缀字串&#xff08;以模式串为开头的字串&#xff09;的真前缀和真后缀&#xff08;指子串的开始字符与子串的最后字符相等的…

【C 数据结构】二叉树

文章目录 【 1. 基本原理 】1.1 二叉树的性质1.2 满二叉树1.3 完全二叉树 【 2. 二叉树的顺序存储结构 】2.1 完全二叉树的顺序存储2.2 普通二叉树的顺序存储2.3 完全二叉树的还原 【 3. 二叉树的链式存储结构 】【 4. 二叉树的先序遍历 】4.1 递归实现4.2 非递归实现 【 5. 二…

MongoDB磁盘空间占满,导致数据库被锁定,如何清理数据和磁盘空间

一、问题 1、我在实际项目中&#xff0c;遇到一个问题&#xff0c;随着数据每天的不断增加&#xff0c;导致mongodb的磁盘空间站满了&#xff0c;数据库被锁了&#xff0c;无法使用。 2、故障表现 部署的应用程序突然无法将数据写入数据库&#xff0c;但是可以正常读取数据。…

栈和队列详解

目录 栈栈的概念及结构栈的实现数组栈的实现数组栈功能的实现栈的初始化void STInit(ST* pst)初始化情况一初始化情况二 代码栈的插入void STPush(ST* pst, STDataType x)代码 栈的删除void STPop(ST* pst)代码 栈获取数据STDataType STTop(ST* pst)代码 判断栈是否为空bool ST…

裸金属服务器是什么

自推出裸金属服务器以来&#xff0c;它一直断断续续地出现在我们面前。最近&#xff0c;关于裸金属服务器、什么是裸金属服务器、裸金属服务器可以做什么、数据托架共享的讨论越来越多&#xff1a; 裸金属服务器&#xff08;bare metal server&#xff0c;BMS&#xff09;的官…

如何在OpenWRT上配置SFTP远程文件传输

如何在OpenWRT上配置SFTP远程文件传输 OpenWRT 是一款广泛使用的开源路由器固件&#xff0c;它能够让普通的家用路由器具备高级路由功能&#xff0c;提供更多自定义和优化选项。本文将介绍如何在OpenWRT上配置SFTP&#xff08;SSH文件传输协议&#xff09;服务&#xff0c;以便…

js生成不同的阅读数分配到每一篇上面,不会因为刷新而变动

js生成不同的阅读数分配到每一篇上面,不会因为刷新而变动 {%- for article in blog.articles -%}<div class"blog-articles__article article">{%- render article-card,article: article,media_height: section.settings.image_height,media_aspect_ratio: a…

面试遇到算法题:实现LRU缓存

请你设计并实现一个满足 LRU (最近最少使用) 缓存约束的数据结构。 这是一道大厂面试高频出现的算法题&#xff0c;难度为⭐️⭐️⭐️&#xff0c;属于中等&#xff0c;老铁们来一起看看这个题该怎么解&#xff1f; 1. 原题再现 没有废话&#xff0c;翠花&#xff0c;上酸菜&…

CountDownLatch使用错误+未最终断开连接导致线程池资源耗尽

错误描述&#xff1a; 我设置了CountDownLatch对线程的协作做出了一些限制&#xff0c;但是我发现运行一段时间以后便发现定时任务不运行了。 具体代码&#xff1a; public void sendToCertainWeb() throws IOException, InterruptedException {List<String> urlList …

HTML的学习-通过创建相册WEB学习HTML-第二部分

文章目录 二、学习开始3.6、form元素示例&#xff1a;添加form元素示例&#xff1a;action属性添加到form属性中 3.7、input元素示例&#xff1a;在input属性中添加参数 3.8、button元素示例&#xff1a;在button中添加type元素示例&#xff1a;定义单选按钮radio 3.9、id属性示…

交换式网络捕获网络流量的方法

交换式网络捕获网络流量的方法 参考资料&#xff1a; https://blog.csdn.net/weixin_44143678/article/details/107559329 # 一.端口镜像 端口镜像&#xff0c;又称为“端口监视”或“端口抄送”&#xff0c;是一种网络管理技术&#xff0c;旨在将网络设备上的特定端口的流…

伙伴匹配(后端)-- 数据库表设计

文章目录 用户表标签表队伍表用户队伍表sql语言分类&#xff08;题外话&#xff09;待更新... 在后端开发中&#xff0c;数据库表设计真的是非常重要的一环了&#xff0c;进入公司熟悉业务第一个要看的也是数据库的表,接下来就让我们看看本项目的数据库表有哪些吧&#xff08;暂…

LoRA: 大模型的低秩适配

笔记整理&#xff1a;陈一林&#xff0c;东南大学硕士&#xff0c;研究方向为不确定知识图谱规则学习 链接&#xff1a;https://arxiv.org/abs/2106.09685 1、动机 自然语言处理的一个重要范式包括在通用领域数据上进行大规模预训练&#xff0c;然后对特定任务或领域进行适应性…

Go语言中通过数据对齐降低内存消耗和提升性能

数据对齐是一种安排数据分配方式以加速 CPU 访问内存的方法。 不了解这个概念会导致额外的内存消耗甚至性能下降。 要了解数据对齐的工作原理&#xff0c;让我们首先讨论没有它会发生什么。假设我们分配两个变量&#xff0c;一个 int32 类型的 &#xff08;32 B&#xff09; 和…

【上海大学计算机组成原理实验报告】四、指令系统实验

一、实验目的 了解指令结构、PC寄存器的功能和指令系统的基本工作原理。 学习设计指令的方法。 二、实验原理 根据实验指导书的相关内容&#xff0c;对于部分使用频率很高&#xff0c;且只用几条微指令即可完成的简单操作&#xff0c;可以把这部分简单操作的微指令序列固定下…

安装esxi 7 对硬件资源的需求

安装VMware vSphere ESXi 7.0 虚拟化平台对硬件资源的基本需求如下&#xff1a; 处理器&#xff1a; 必须是64位x86架构的CPU。至少需要两个物理核心&#xff08;不过对于生产环境&#xff0c;建议更多的核心数以支持更多虚拟机并保证性能&#xff09;。支持并启用硬件辅助虚拟…

SpringMvc(2)RequestMapping注解

RequestMapping注解 1 、RequestMapping的作用2、RequestMapping的出现位置3、类上与方法上结合使用4、RequestMapping注解的value属性4.1 value属性的使用4.2 Ant风格的value4.3 value中的占位符&#xff08;重点&#xff09; 5、RequestMapping注解的method属性5.2衍生Mappin…
最新文章