Python Flask高级编程之RESTFul API前后端分离(学习笔记)
Flask-RESTful是一个强大的Python库,用于构建RESTful APIs。它建立在Flask框架之上,提供了一套简单易用的工具,可以帮助你快速地创建API接口。Flask-RESTful遵循REST原则,支持常见的HTTP请求方法,如GET、POST、PUT和DELETE,并提供了验证、授权、分页等功能。
- 理解API
- 理解Restful API
- 理解装饰器
- 理解Flask框架
- 使用Python Flask 实现Restful API
API的理解
API(application programming interfaces),即应用程序编程接口。API由服务器(Server)提供(服务器有各种各样的类型,一般我们浏览网页用到的是web server,即网络服务器),通过API,计算机可以读取、编辑网站数据,就像人类可以加载网页、提交信息等。通俗地,API可以理解为家用电器的插头,用户只提供插座,并执行将插头插入插座的行为,不需要考虑电器内部如何运作。从另外一个角度上讲API是一套协议,规定了与外界的沟通方式:如何发送请求和接受响应。
理解 RESTful API
RESTful API即满足RESTful风格设计的API,RESTful表示的是一种互联网软件架构(以网络为基础的应用软件的架构设计),如果一个架构符合REST原则,就称它为RESTful架构。RESTful架构的特点:
- 每一个URI代表一种资源;
- 客户端和服务器之间,传递这种资源的某种表现层;把"资源"具体呈现出来的形式,叫做它的"表现层"(Representation)。比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现。
- 客户端通过四个HTTP动词,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。
后端的RESTful API指的是在服务器端实现的符合RESTful架构风格的API,它用于提供数据和服务,并且通常被前端或其他服务端所调用。后端的RESTful API包括了资源的定义、URI的设计、HTTP方法的使用以及数据格式等内容。
而前端的RESTful API通常是指在客户端(比如浏览器端、移动端应用)中调用后端RESTful API的方式和规范。前端的RESTful API通常是通过Ajax、Fetch API或者一些库(比如axios)来发起HTTP请求,按照RESTful规范与后端的API进行交互,包括发送GET、POST、PUT、DELETE等请求,处理返回的数据等。
设计 RESTful API
设计 RESTful API 需要遵循一定的规范和原则。下面是一些常见的设计原则:
- • 使用名词来表示资源,比如
/users
、/articles
等。 - • 使用 HTTP 方法来表示对资源的操作,比如 GET、POST、PUT、DELETE 等。
- • 使用 URL 参数来传递附加信息,比如查询条件、分页信息等。
- • 使用 HTTP 状态码来表示操作结果,比如 200 表示成功、201 表示创建成功、400 表示请求错误、404 表示资源不存在等。
- • 使用 JSON 或 XML 格式来传输数据。
下面是一个简单的 RESTful API 设计示例:
GET /users # 获取所有用户信息
GET /users/1 # 获取指定用户信息
POST /users # 添加用户
PUT /users/1 # 修改指定用户信息
DELETE /users/1 # 删除指定用户
理解装饰器
为了理解装饰器,应该先做好三个方面的准备:
1. 理解对象
通过变量和对象的关系理解对象,在python中,如果要使用一个变量,不需要提前进行声明,只需要在用的时候,给这个变量赋值即可。如 a=1,整数1 为一个对象,a 是一个引用,利用赋值语句,引用a指向了对象1;这边形象比喻一下:这个过程就相当于“放风筝”,变量a就是你手里面的“线”,python就跟那根“线”一样,通过引用来接触和拴住天空中的风筝——对象。由于Python一切皆是对象的理念,所以函数也是一个对象。
比如,写一个函数,也可以是说创造了一个函数对象。
def cal(x,y):result=x+yreturn result
这时便是创造了一个叫做cal的函数对象。然后就可以使用了
cal(1,2) #直接使用了cal这个函数对象
...........或者.............
calculate=cal #把一个名为calculate的变量指向cal这个函数对象
calculate(1,2)
2.理解函数带括弧和不带括弧时分别代表的意思
如果只写一个cal(不带扩号),此时的cal仅仅是代表一个函数对象;当写成cal(1,2)时,就是在告诉编辑器说“执行cal这个cal函数”
3.理解可变参数和关键字参数
- 可变参数
定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个*号。如下,在函数内部,参数numbers接收到的是一个tuple,并且,调用该函数时,可以传入任意个参数,包括0个参数:
def calc(*numbers):sum = 0for n in numbers:sum = sum + n * nreturn sum
calc(1,2) # 5
- 关键字参数
可变参数允许传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。请看示例:
def person(name, age, **kw):print('name:', name, 'age:', age, 'other:', kw)>>> person('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
现在正式开始理解装饰器了。首先理解为什么需要装饰器呢?
主要原因是装饰器可以起到一个“偷懒”的作用,比如写了很多个简单的函数,现在想知道在运行的时候是哪些函数在执行,并且你又觉得这个没有必要写测试,只是想要很简单的在执行完毕之前给它打印上一句“Start”,那该怎么办呢?你可以这样:
def func_name(arg):print 'Start func_name'sentence
想想看给每一个函数后面都加上那一句话将是非常的麻烦的。但是有了装饰器就不一样了。
def log(func):def wrapper(*arg, **kw):print 'Start%s'%funcreturn func(*arg,**kw)return wrapper@log
def func_a(arg):pass@log
def func_b(arg):pass@log
def func_c(arg):pass
这段代码是一个简单的装饰器示例,使用了 Python 的装饰器语法。
首先,定义了一个装饰器函数 log
,该函数接受一个函数 func
作为参数。装饰器的作用是在函数执行前后打印日志。
-
def log(func):
定义了装饰器函数log
,接受一个函数作为参数,这个函数将被装饰。 -
def wrapper(*arg, **kw):
在装饰器内部定义了一个内部函数wrapper
,它接受任意数量的位置参数arg
和关键字参数kw
。这里使用*arg
和**kw
来接收任意数量的参数。 -
print('Start%s' % func):
在函数执行前打印日志信息,使用了%
格式化字符串来将func
的字符串表示插入日志中。 -
return func(*arg, **kw):
调用被装饰的原始函数func
,并将接收到的参数传递给它。返回原始函数的结果。 -
这里使用
@log
语法将装饰器应用到了三个函数func_a
、func_b
和func_c
上。这相当于执行了以下操作:
func_a = log(func_a)
func_b = log(func_b)
func_c = log(func_c)
这样,每当调用这些函数时,实际上是调用了被装饰后的版本,这些版本在执行前后会打印日志信息。
理解Flask框架:一个小的Flask应用
from flask import Flask
app = Flask(__name__)@app.route('/')
def hello_world():return 'Hello World!'if __name__ == '__main__':app.run()
运行上面代码,在浏览器上输入http://127.0.0.1:5000/,便会看到 Hello World! 字样。
那么,这段代码做了什么?
1.首先导入了Flask类。这个类的实例是WSGI应用程序
2.接下来,我们创建一个该类的实例,第一个参数是应用模块或者包的名称,如果使用单一的模块,应该使用name,因为模块的名称将会因其作为单独应用启动还是作为模块导入而有不同
3.然后,我们使用route()装饰器告诉Flask什么样的URL能够触发我们的函数。
4.这个函数的名字也在生成 URL 时被特定的函数采用,这个函数返回我们想要显示在用户浏览器中的信息。
5.最后使用run()函数来让应用运行在本地服务器上。其中if__name__=='main':确保服务器只会在该脚本被Python解释器直接执行的时候才会运行,而不是作为模块导入的时候。
如果启用了调试支持,服务器会在代码修改后自动重新载入,并在发生错误时提供一个相当有用的调试器。启动调试,app.run(debug=True);并且route()装饰器把一个函数绑定到对应的URL上。如下例子
@app.route('/')
def index():return 'Index Page'@app.route('/hello')
def hello():return 'Hello World'
其中还是可以构造含有动态部分的URL,要给 URL 添加变量部分,可以把这些特殊的字段标记为 <variable_name> , 这个部分将会作为命名参数传递到函数。规则可以用 <converter:variable_name> 指定一个可选的转换器,如下例子。
@app.route('/user/<username>')
def show_user_profile(username):# show the user profile for that userreturn 'User %s' % username@app.route('/post/<int:post_id>')
def show_post(post_id):# show the post with the given id, the id is an integerreturn 'Post %d' % post_id
如果Flask能匹配URL,也是可以生成URL的。用url_for()来给指定的函数构造URL。它接受函数名作为第一个参数,也接受对应 URL 规则的变量部分的命名参数。未知变量部分会添加到 URL 末尾作为查询参数。这里有一些例子:
from flask import Flask, url_for
>>> app = Flask(__name__)
>>> @app.route('/')
... def index(): pass
...
>>> @app.route('/login')
... def login(): pass
...
>>> @app.route('/user/<username>')
... def profile(username): pass
...
>>> with app.test_request_context():
... print url_for('index') #/
... print url_for('login') #/login
... print url_for('login', next='/') #/login?next=/
... print url_for('profile', username='John Doe')
... #/user/John%20Doe
HTTP(与web应用会话的协议)有许多不同的访问URL方法。默认情况下,路由只回应GET请求,但是通过route()装饰器传递methods参数可以改变这个行为。
@app.route('/login', methods=['GET', 'POST'])
def login():if request.method == 'POST':do_the_login()else:show_the_login_form()
使用Python Flask 实现Restful API
Flask 是一个轻量级Web框架,可以实现快速的 Web 开发,并且提供了良好的扩展性。另外,Flask 还为实现 Restful API 提供了很多方便的工具,如Flask Restful 和 Flask Restplus 等。Flask 的代码结构简单,上手容易,比 Django 等其他框架更为轻量级,更适合快速迭代。
安装 Flask
首先,我们需要安装 Flask。可以使用 pip 命令来安装:
pip install flask
创建 Flask 应用
首先,我们需要导入 Flask 模块,并创建一个 Flask 应用实例:
from flask import Flaskapp = Flask(__name__)
这里的 __name__
参数表示当前模块的名字,Flask 根据这个参数来确定应用的根目录。
定义 API 路由
接下来,我们需要定义 API 的路由和处理函数。在 Flask 中,可以使用 @app.route
装饰器来定义路由:
@app.route('/users', methods=['GET'])
def get_users():return jsonify(users)
这里的 /users
表示路由的路径,methods=['GET']
表示这个路由只支持 GET 方法。get_users
函数是这个路由的处理函数,它返回一个 JSON 格式的用户列表。
同样的,我们还可以定义其他路由和处理函数,比如获取指定用户信息、添加用户、修改用户信息和删除用户等。
处理请求参数
在 RESTful API 中,客户端通常需要向服务器传递一些参数,比如查询条件、请求体等。在 Flask 中,可以使用 request
对象来访问这些参数:
user = {'id': request.json['id'], 'name': request.json['name'], 'age': request.json['age']}
这里的 request.json
表示请求体中的 JSON 数据。如果请求体不是 JSON 格式,可以使用 request.form
或 request.args
来获取表单数据或查询参数。
返回 JSON 数据
在 RESTful API 中,通常需要返回 JSON 格式的数据。在 Flask 中,可以使用 jsonify
函数来将 Python 对象转换为 JSON 格式的数据:
return jsonify(users)
这里的 users
是一个 Python 列表对象,jsonify(users)
将它转换为 JSON 格式的数据,并返回给客户端。
启动应用
最后,我们需要在应用中启动 Flask 服务器:
if __name__ == '__main__':app.run()
这里的 __name__
参数表示当前模块的名字,app.run()
表示启动 Flask 服务器,默认监听在本地的 5000 端口上。如果需要修改监听地址或端口,可以使用 host
和 port
参数来指定。
编写代码
下面是一个简单的 Flask 应用,它实现了一个简单的 RESTful API,用于获取和修改用户信息:
from flask import Flask, jsonify, requestapp = Flask(__name__)# 用户数据(模拟数据库)
users = [{"id": 1, "name": "Alice", "age": 20},{"id": 2, "name": "Bob", "age": 25},{"id": 3, "name": "Charlie", "age": 30},
]# 获取所有用户信息
@app.route('/users', methods=['GET'])
def get_users():return jsonify(users)# 获取指定用户信息
@app.route('/users/<int:user_id>', methods=['GET'])
def get_user(user_id):user = next((user for user in users if user['id'] == user_id), None)if user:return jsonify(user)else:return jsonify({'error': 'User not found'})# 添加用户
@app.route('/users', methods=['POST'])
def add_user():user = {'id': request.json['id'], 'name': request.json['name'], 'age': request.json['age']}users.append(user)return jsonify(user)# 修改用户信息
@app.route('/users/<int:user_id>', methods=['PUT'])
def update_user(user_id):user = next((user for user in users if user['id'] == user_id), None)if user:user['name'] = request.json.get('name', user['name'])user['age'] = request.json.get('age', user['age'])return jsonify(user)else:return jsonify({'error': 'User not found'})# 删除用户
@app.route('/users/<int:user_id>', methods=['DELETE'])
def delete_user(user_id):user = next((user for user in users if user['id'] == user_id), None)if user:users.remove(user)return jsonify({'result': True})else:return jsonify({'error': 'User not found'})if __name__ == '__main__':app.run()
测试 API
启动应用后,我们可以使用 curl 命令或其他工具来测试 API:
# 获取所有用户信息
curl http://localhost:5000/users# 获取指定用户信息
curl http://localhost:5000/users/1# 添加用户
curl -H "Content-Type: application/json" -X POST -d "{\"id\": 4, \"name\": \"David\", \"age\": 35}" http://localhost:5000/users# 修改用户信息
curl -H "Content-Type: application/json" -X PUT -d "{\"name\": \"Alice Smith\", \"age\": 22}" http://localhost:5000/users/1# 删除用户
curl -X DELETE http://localhost:5000/users/4
在 Windows 的命令提示符中,需要使用双引号 "
对 JSON 数据进行引用,并且在内部的双引号前面加上反斜杠 \
进行转义。
参考:
用Python 的Flask实现 RESTful API(学习篇)
通过Flask框架创建灵活的、可扩展的Web Restful API服务 - 知乎 (zhihu.com)
相关文章:

Python Flask高级编程之RESTFul API前后端分离(学习笔记)
Flask-RESTful是一个强大的Python库,用于构建RESTful APIs。它建立在Flask框架之上,提供了一套简单易用的工具,可以帮助你快速地创建API接口。Flask-RESTful遵循REST原则,支持常见的HTTP请求方法,如GET、POST、PUT和DE…...

Windows如何打开投影到此电脑
1.首先点开设置 找到系统 点击投影到此电脑,如果这3行都显示灰色说明没有开启。 2.如何开启投影到此电脑 ①回到设置,点击应用 ②点击可选应用 ③ 安装无线显示器 投影设置可以和我一样...

【BUG】段错误
1. 问题 8核工程,核4在运行了20分钟以上,发生了段错误。 [C66xx_4] A00x53 A10x53 A20x4 A30x167e A40x1600 A50x850e2e A60x845097 A70xbad9f5e0 A80x0 A90x33 A100x53535353 A110x0 A120x0 A130x0 A140x0 A150x0 A160x36312e35 A170x20 A180x844df0 …...

深入理解指针(3)
目录 一、 字符指针变量二、 数组指针变量1.数组指针变量是什么?2.数组指针变量怎么初始化? 三、 二维数组传参的本质四、 函数指针变量1. 函数指针变量的创建2.函数指针变量的使用3.typedef关键字 五、 函数指针数组六、 转移表 一、 字符指针变量 在指针的类型中…...

ssm在线学习平台-计算机毕业设计源码09650
目 录 摘要 1 绪论 1.1 选题背景及意义 1.2国内外现状分析 1.3论文结构与章节安排 2 在线学习平台系统分析 2.1 可行性分析 2.2 系统业务流程分析 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系统用例分析 2.5本章小结 3 在线学习平台总体设计 …...

【Linux 内核源码分析】内存映射(mmap)机制原理
内存映射(mmap)是 Linux 内核的一个重要机制,它为程序提供了一种将文件内容直接映射到进程虚拟地址空间的方式。同时内存映射也是虚拟内存管理和文件 IO 的重要组成部分。 在 Linux 中,虚拟内存管理是基于内存映射来实现的。在调用 mmap 函数时…...

贪心算法之合并区间
“任世界多宽广,停泊在这港口~” 区间问题,涉及到最多的就是 取交集 和 并集的概念。我们使用C排序算法后,其默认规则就是按照 “左排序”进行的。因而,我们实质上注意的是每一个区间的 右端点,根据题目要求ÿ…...

Eclipse - Colors and Fonts
Eclipse - Colors and Fonts References 编码最好使用等宽字体,Ubuntu 下自带的 Ubuntu Mono 可以使用。更换字体时看到名字里面带有 Mono 的基本都是等宽字体。 Window -> Preferences -> General -> Appearance -> Colors and Fonts -> C/C ->…...

java 数据结构LinkedList类
目录 什么是LinkedList 链表的概念及结构 链表的结构 无头单向非循环链表 addFirst方法(头插法) addLast方法(尾插法) addIndex方法 contains方法 removeAllKey方法 size和clear方法 链表oj题 无头双向非循环链表 ad…...

第五次作业(防御安全)
需求: 1.办公区设备可以通过电信链路和移动链路上网(多对多的NAT,并且需要保留一个公网IP 不能用来转换) 2.分公司设备可以通过总公司的移动链路和电信链路访问到DMZ区的http服务器 3.分公司内部的客户端可以通过公网地址访问到内部的服务…...

阿里云香港轻量应用服务器是什么线路?
阿里云香港轻量应用服务器是什么线路?不是cn2。 阿里云香港轻量服务器是cn2吗?香港轻量服务器不是cn2。阿腾云atengyun.com正好有一台阿里云轻量应用服务器,通过mtr traceroute测试了一下,最后一跳是202.97开头的ip,1…...

C# Winform .net6自绘的圆形进度条
using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms;namespace Net6_GeneralUiWinFrm {public class CircularProgressBar : Control{private int progress 0;private int borderWidth 20; // 增加的边框宽度public int Progr…...

Git基本操作(超详细)
文章目录 创建Git本地仓库配置Git配置命令查看是否配置成功重置配置 工作区、暂存区、版本库添加文件--场景一概述实例操作 查看.git文件添加文件--场景二修改文件版本回退撤销修改情况⼀:对于工作区的代码,还没有 add情况⼆:已经 add &#…...

【AGI视频】Sora的奇幻之旅:未来影视创作的无限可能
在五年后的未来,科技的发展为影视创作带来了翻天覆地的变化。其中,Sora视频生成软件成为了行业的翘楚,引领着全新的创作潮流。Sora基于先进的Transformer架构,将AI与人类的创造力完美结合,为观众带来了前所未有的视听盛…...

Docker部署nginx
搜索镜像 docker search nginx 下载拉取nginx镜像 docker pull nginx 查看镜像 docker images 启动容器 docker run -d --name nginx01 -p 3344:80 nginx 外部端口需要在服务器安全组中设置,使用docker镜像nginx以后台模式启动一个容器,并将容器…...

C++Qt——自定义信号与槽
自定义信号与槽 自定义信号与槽是实现对象间通信的一种机制,比如按钮和窗口间的通信。 一、定义信号 Signal关键字声明的类成员函数。不需要实现,只需要声明。 signals:void mySignals();//定义信号,不用实现二、定义槽 可以使任何普通成员函数&…...

提高项目的性能和响应速度的方法
目录 引言 一、代码优化 二、数据库优化 三、缓存技术: 四、异步处理 1. 将耗时的操作改为异步处理 1.1 文件上传 1.2 邮件发送 2. 使用消息队列实现异步处理 2.1 配置消息队列 2.2 发送消息 2.3 接收消息并处理 五、负载均衡和集群 1. 负载均衡 1.1 …...

QT学习事件
一、事件处理过程 众所周知 Qt 是一个基于 C 的框架,主要用来开发带窗口的应用程序(不带窗口的也行,但不是主流)。 我们使用的基于窗口的应用程序都是基于事件,其目的主要是用来实现回调(因为只有这样程序…...

第13章 网络 Page818 UDP(和TCP的比较)
TCP核心类 asio::ip::tcp::socket;//网络套接字 asio::ip::tcp::endpoint;//边接端地址 asio::ip::tcp::resolver;//地址解析器 asio::ip::tcp::acceptor;//连接接受器 UPD核心类 asio::ip::udp::socket;//网络套接字 asio::ip::udp::endpoint;//边接端地址 asio::ip::udp::…...

EMQX Enterprise 5.4 发布:OpenTelemetry 分布式追踪、OCPP 网关、Confluent 集成支持
EMQX Enterprise 5.4.0 版本已正式发布! 新版本提供 OpenTelemetry 分布式追踪与日志集成功能,新增了开放充电协议 OCPP 协议接入能力,并为数据集成添加了 Confluent 支持。此外,新版本还进行了多项改进以及 BUG 修复,…...

记录 | C++ cout.setf(ios::fixed)
cout.setf(ios::fixed); 是在 C 中使用的一个标准库函数,用于将流的输出格式设置为"fixed" "fixed"格式指定输出浮点数时,小数点后的位数是固定的。这意味着,无论输出的数字有多少位小数,小数点后都会保留相…...

Eclipse 创建 Hello World 工程
Eclipse 创建 Hello World 工程 1. Hello WorldReferences Download and install the Eclipse IDE. 1. Hello World Eclipse -> double click -> Launch 单击蓝色方框 (右上角) 最大化 IDE File -> New -> C Project -> Finish Project name:工程名…...

【前端工程化面试题】vite热更新原理
vite 在开发阶段,运行 vite 命令,会启动一个开发服务器,vite 在开发阶段是一个服务器 依赖 esm: vite 在开发阶段使用 esm 作为开发时的模块系统。esm 具有动态导入的能力,这使得在代码中引入模块时可以动态地加载新的…...

【leetcode】判断二叉树是否完全二叉树
递归方式判断二叉树是否完全二叉树 bool TreeComplete(TreeNode* root) {if (root ! NULL) {if (root->left NULL && root->right ! NULL) {return false; // 左子树空}else if (root->left NULL && root->right NULL) {return true; // 左右子…...

Java多线程系列——内存模型JMM
目录 核心思想 关键概念 1. 可见性 2. 原子性 3. 有序性 工作原理 并发工具类 对并发编程的影响 同步策略 JMM的实践意义 结语 Java内存模型(Java Memory Model, JMM)是Java并发编程中的核心概念,其定义了Java虚拟机(JV…...

深入理解 Vue3 中的 setup 函数
💗💗💗欢迎来到我的博客,你将找到有关如何使用技术解决问题的文章,也会找到某个技术的学习路线。无论你是何种职业,我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章,也欢…...

【QT+QGIS跨平台编译】之三十六:【RasterLite2+Qt跨平台编译】(一套代码、一套框架,跨平台编译)
文章目录 一、RasterLite2介绍二、文件下载三、文件分析四、pro文件五、编译实践一、RasterLite2介绍 RasterLite2是一个开源的轻量级栅格数据库,可以用于存储和管理各种类型的栅格数据,包括卫星遥感图像、数字高程模型等。 与传统的GIS数据存储方式不同,RasterLite2采用基…...

java面试题:分布式和微服务的区别
1 分布式和微服务概念不同 微服务架构是架构设计方式,是设计层面的东西,一般考虑如何将系统从逻辑上进行拆分,也就是垂直拆分。 分布式系统是部署层面的东西,即强调物理层面的组成,即系统的各子系统部署在不同计算机…...

GO语言的变量与常量
1.变量 go是一个静态语言 变量必须先定义后使用变量必须要有类型 定义变量的方式: var 名称 类型 var 名称 值 名称 :值 例如: var num int 这样就存了一个num类型为int的变量 var num 1 上面使用简化的定义通过num自动判断后面的类型为int并…...

java面试多线程篇
文章说明 在文章中对所有的面试题都进行了难易程度和出现频率的等级说明 星数越多代表权重越大,最多五颗星(☆☆☆☆☆) 最少一颗星(☆) 1.线程的基础知识 1.1 线程和进程的区别? 难易程度:☆☆…...