1、http常用请求头
- host
- User-Agent
- path
- content-type
- accept-encoding
- accept-language
- cookie
- referen
- method
- accept
2、常见请求方式
- post
- get
- delete
- options
- put
- trace
- head
- patch
3、常见的状态码
- 200 ok
- 301 永久重定向
- 302 临时重定向
- 403 服务器禁止请求
- 404 请求的信息不存在或已转移
- 500 服务器错误
- 503 服务器超时
4、websocket协议与实现原理
- Websocket是一种在单个TCP连接上进行全双工通讯的协议
- WebSocket 首先发起一个 HTTP 请求,在请求头加上 `Upgrade` 字段,该字段用于改变 HTTP 协议版本或者是换用其他协议,这里我们把 `Upgrade` 的值设为 `websocket` ,将它升级为 WebSocket 协议
5、django、flask、tornado框架的比较?
- 对于django,大而全的框架它的内部组件比较多,内部提供:ORM、Admin、中间件、Form、ModelForm、Session、
缓存、信号、CSRF;功能也都挺完善的
- flask,微型框架,内部组件就比较少了,但是有很多第三方组件来扩展它,
比如说有那个wtform(与django的modelform类似,表单验证)、flask-sqlalchemy(操作数据库的)、
flask-session、flask-migrate、flask-script、blinker可扩展强,第三方组件丰富。所以对他本身来说有那种短小精悍的感觉
- tornado,异步非阻塞。
- django和flask的共同点就是,他们2个框架都没有写socket,所以他们都是利用第三方模块wsgi。
但是内部使用的wsgi也是有些不同的:django本身运行起来使用wsgiref,而flask使用werkzeug wsgi
还有一个区别就是他们的请求管理不太一样:django是通过将请求封装成request对象,再通过参数传递,而flask是通过上下文管理机制
6、什么是wsgi?
7、django请求的生命周期?
用户请求进来先走到 wsgi 然后将请求交给 jango的中间件 穿过django中间件(方法是process_request)
接着就是 路由匹配 路由匹配成功之后就执行相应的 视图函数
在视图函数中可以调用orm做数据库操作 再从模板路径 将模板拿到 然后在后台进行模板渲染
模板渲染完成之后就变成一个字符串 再把这个字符串经过所有中间件(方法:process_response) 和wsgi 返回给用户
8、列举django的内置组件
- form 组件
- 信号组件
- csrf组件
- contentype
- 中间件
- session组件
9、FBV与CBV的区别
本质上是没什么区别的,因为他们的本质都是函数。CBV的.as_view()返回的view函数,view函数中调用类的dispatch方法,在dispatch方法中通过反射执行get/post/delete/put等方法;FBV则直接根据请求的方式进行逻辑处理。
非要说区别的话:
- CBV比较简洁,GET/POST等业务功能分别放在不同get/post函数中。FBV需自己做请求判断进行区分,对代码的简洁性不太友好,所以根据路由的请求方式做出不同选择,来增加代码的可读性和可维护性。
10、websocket是如何进行建立连接与通信的(简单理解)?
握手过程:
websocket-client端通过ws协议向websocket-server端发起连接请求前,首先在自己的请求头中添加Sec-Websocket-Key键值对,值为根据自己账号通过一定的方式生成的字符串,client端发送自己的key后,server端取出并保存该字符串后,将该字符串与魔法字符串拼接后先后经过sha1、base64加密后获得与此client端的通信密钥保存并发送给client端,client端在本地保存此通信密钥,此时server端与client端完成握手。
通信过程(解密):
server端或client端在收到加密信息(字节类型)后,将加密信息的第二个字节与127进行与位运算,根据位运算的结果分为=127、=126、<=125三种情况,根据不同的位运算结果分别取出其掩码(mask)部分和数据部分,将掩码部分与数据进行^(异或)运算即得到真实的数据(字节类型),根据编码类型进行解码后即得到可读性的数据。
通信过程(加密):
import struct
msg_bytes = "hello".encode("utf8")
token = b"\x81"
length = len(msg_bytes)
if length < 126:
token += struct.pack("B", length)
elif length == 126:
token += struct.pack("!BH", 126, length)
else:
token += struct.pack("!BQ", 127, length)
msg = token + msg_bytes
print(msg)
- 作用:
- 对用户请求数据格式进行校验
- 自动生成HTML标签
- 区别:
- Form,字段需要自己手写。
class Form(Form):
xx = fields.CharField(.)
xx = fields.CharField(.)
xx = fields.CharField(.)
xx = fields.CharField(.)
- ModelForm,可以通过Meta进行定义
class MForm(ModelForm):
class Meta:
fields = "__all__"
model = UserInfo
- 应用:只要是客户端向服务端发送表单数据时,都可以进行使用,如:用户登录注册
12、django的Model中的ForeignKey字段中的on_delete参数有什么作用
on_delete参数是当外键数据被删除时,本表中所有关联此数据的数据都会被删除时会触发一个事件即级联删除。可选参数有:
CASCADE:此值设置,是级联删除。
PROTECT:此值设置,是会报完整性错误。
SET_NULL:此值设置,会把外键设置为null,前提是允许为null。
SET_DEFAULT:此值设置,会把设置为外键的默认值。
SET():此值设置,会调用外面的值,可以是一个函数。
一般情况下使用CASCADE就可以了。
13、面向对象的理解
1.封装
封装是指将同一类的方法和属性封装到一个类中,实例化后的对象均有此属性及方法
2.继承
如果多个类中均有相同的方法,就可以选择继承以提高代码的复用性
3.多态
不同的对象但含有相同的方法,尽管实现功能不一样,但我们就认为它是多态的体现
14、django中csrf的实现机制
目的:防止用户直接向服务端发起POST请求
- 用户先发送GET获取csrf token: Form表单中一个隐藏的标签 + token
- 发起POST请求时,需要携带之前发送给用户的csrf token;
- 在中间件的process_view方法中进行校验。
在html中添加{%csrf_token%}标签
15、基于django使用ajax发送post请求时,都可以使用哪种方法携带csrf token?
1.在请求头中携带:
$.ajax({
url:"/index/",
data:{id:1},
type:'POST',
headers:{
'X-CSRFToken':$.cookie('csrftoken') // 去cookie中获取
},
success:function(data){
console.log(data);
}
})
2.在form表单中携带:
$.ajax({
url:"/index/",
type:'POST',
data:{csrfmiddlewaretoken:'{{ csrf_token }}',name:'alex'}
success:function(data){
console.log(data);
}
})
16、django中如何实现orm表中添加数据时创建一条日志记录
# 使用django中的信号机制,用法如下:
from django.db.models.signals import post_save
from django.dispatch import receiver
from app01 import models
@reciever(post_save)
def book_create(request):
models.Book.objects.create(name='三体')
return redirect('/book_list')
17、django中如何使用redis缓存?
1.pip3 install django-redis
2.在django项目中的settings.py中做如下配置:
CASHES = {
'default':{
'BACKEND':'django_redis.cache.RedisCache',
'LOCATIONG':'ip:port', # 或"redis://127.0.0.1:6379/1"
'OPTIONS':{
'CLIENT_CLASS':'django_redis.client.DefaultClient',
'CONNECTION_POOL_KWARGS':{
'max_connections':100
}
}
}
}
3.使用
(1) 局部视图使用
from django.views.decorators.cache import cache_page
@cache_page(60) # 括号内为缓存时间
def index(request):
bar = Foo.objects.all()
return render(request, 'cache/index.html', {'bar': bar})
(2) template模板中使用
{% load cache %}
{% cache 60 缓存的key %}
要缓存的内容
{% endcache %}
(3) 在全站中进行缓存,在settings.py中进行设置
MIDDLEWARE = [
# 站点缓存 , 注意必须在第一个位置
'django.middleware.cache.UpdateCacheMiddleware',
...
# 站点缓存, 注意必须在最后一个位置
'django.middleware.cache.FetchFromCacheMiddleware',
]
18、django的模板中filter和simple_tag的区别
filter:
类似管道,只能接受两个参数第一个参数是|前的数据
simple_tag:
类似函数,可以传多个参数
在开发过程中可以查看网页请求的性能参数,包括SQL查询、请求时间等,特别是在Mysql访问等的分析上大有用处
20、解释orm中 db first 和 code first的含义
db first:是先有数据表,再创建django-orm中的models-class
code first:是先创建django-orm中的model-class,再创建数据库表
21、django中如何根据数据库表生成model中的类
1、修改setting.py文件,在settings.py里面设置要连接的数据库类型和名称、地址
2、运行下面代码可以自动生成models模型文件
- python manage.py inspectdb
3、创建一个app执行下下面代码:
- python manage.py inspectdb > app/models.py
22、谈谈你对restfull 规范的认识
1.url中要体现版本号
2.url中要要用名词
3.url中要有api标识
4.返回值要有状态码和错误信息或超链接
5.根据请求方式的不同做出不同的操作
6.根据请求方式的不同,返回值要有相应的信息
7.url中要有过滤条件或分页
8.尽量使用https
23、为什么要使用django rest framework框架?
1.DRF中拥有丰富的组件,如版本组件、认证组件、权限组件、频率组件、分页组件、路由组件等
2.DRF拥有独立的序列化组件,可以序列化较多的数据类型
3.DRF有自己的解析器,根据不同的数据类型做出不同的解析操作
24、DRF中CBV继承GenericViewSet类时,路由中要做什么操作?
url(r'book', views.BookView.as_view({'get': 'list','post':'create'})),
url(r'book/<?P(\d+)>', views.BookView.as_view({'get': 'retrieve','put':'update','delete':'destroy'}))
要根据不同的请求方式寻找对应的CBV函数
25、列举常用的Flask第三方组件
flask-session、flask-sqlalchemy、flask-script、flask-migrate、wtforms、gevent-websocket
26、如何在python中使用redis的事务
from redis import Redis
db = Redis()
pipe = db.pipeline(transaction=True)
pipe.multi() # 开启事务
# 执行redis中的方法
pipe.set('name', 'alex')
pipe.execute() # 结束事务
27、Flask中上下文管理主要涉及到了那些相关的类?并描述类主要作用
RequestContext: 封装请求中的原始信息、session
LocalStack: 将local对象中的数据维持成一个列表
Local:保存请求上下文和应用上下文中的对象
AppContext: 封装应用信息
28、为什么Flask把Local对象中的的值stack维护成一个列表?
1.因为通过维护成列表,可以实现一个栈的数据结构,进栈出栈时只取一个数据,巧妙的简化了问题。
2.在多app应用时,可以实现数据隔离;列表里不会加数据,而是会生成一个新的列表
3.local是一个字典,字典里key(stack)是唯一标识,value是一个列表
29、在CRM项目中如何进行单字段搜索或组合字段搜索?
1.在前端搜索form表单中添加一个多选select框,默认值设置为name字段
2.在后端获取根据字段及搜索参数
column_list = request.GET.getlist('columns') # 获取要查询的根据字段
query_param = request.GET.get('query_param') # 获取要查询的内容
from django.db.models import Q
q = Q() # 实例化母Q对象
q.connector = 'OR' # 设置母Q对象的连接符
for column in column_list:
# 依次将查询的字段添加进母Q对象中
q.children.append(Q(('{}__contains'.format(column), query_param)))
query_set = models.User.objects.filter(q) # 对母Q对象进行查询
30、Django中的ORM与原生SQL的区别
ORM:
优点:摒弃了复杂的SQL语句,只关注业务逻辑,开发效率高
缺点:可能没有原生的SQL执行效率高
原生SQL:
优点:经过专业设计的SQL语句查询效率要比ORM高
缺点:需要较高的SQL能力,不容易掌握
31、scrapy框架中都有哪些组件
1.爬虫文件
- 爬虫中间件
2.引擎
3.调度器
4.下载器
- 下载器中间件
5.管道
32、scrapy框架执行流程
首先爬虫文件先对起始URL发起请求,将该请求发送给引擎,引擎将该请求发送给调度器,调度器将该请求记录后重新发给调度器,调度器将该请求发到下载中间件中的process_request()后再传送到下载器,下载器从互联网中下载完数据后经过下载中间件的process_response()方法再将response对象发送给引擎,引擎将response对象经过爬虫中间件的process_spider_input()方法处理,处理完后走到爬虫文件对response对象进行解析生成并返回item对象,如果有其他URL请求则继续发起异步请求,之后将该item对象经process_spider_output()方法处理,处理完后请该item返回给引擎,引擎将item对象发送到管道中进行持久化操作。
33、在scrapy框架中如何设置代理?
方式一:在爬虫文件中设置代理
import os
import scrapy
from scrapy.http import Request
def start_requests(self):
os.environ['HTTP_PROXY'] = "http://192.168.11.11"
for url in self.start_urls:
yield Request(url=url,callback=self.parse)
方法二:在下载中间件中设置代理
def process_request(self, request, spider):
if request.url.split(':')[0] == 'http':
request.meta['proxy'] = 'http://209.34.29.9:8181'
else:
request.meta['proxy'] = 'https://119.59.84.58:8080'
34、简述scrapy中爬虫中间件和下载中间件的作用
爬虫中间件(Spider Middlewares):
介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出。
下载器中间件(Downloader Middlewares):
位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应。
35、什么是事务?事务在MySQL及在Django中如何使用?
事务是多个SQL语句一起执行,一旦其中的一条出现异常,整个操作都将失败并回滚到原来的状态,保证数据库数据的完整性。
MySQL:
start transaction;
insert into user(name,age) values('alex',200);
commit;
Django:
from django.db import transaction
with transaction.atomic():
models.User.object.create(name='alex',age=200)
36、如何使用vitualenv?
1.下载安装
pip3 install vitualenv
2.创建虚拟环境
vitualenv myenv
3.使用方式
widows:
cd myenv/Script
active # 激活虚拟环境
deactive # 退出虚拟环境
Linux:
source myenv/bin/active # 激活虚拟环境
deactive # 退出虚拟环境
37、解释 PV、UV 的含义
PV:Page View,即页面访问量,每打开一次页面PV计数+1,刷新页面也是。
UV:Unique Visitor指独立访客访问数,一台电脑终端为一个访客。
38、解释 QPS的含义
query per second,即每秒请求量,一般指并发量,此参数代表了服务器的并发能力
39、uwsgi、wsgi、uWSGI的区别
wsgi:一种通用的接口标准或者接口协议,实现了python web程序与服务器之间交互的通用性。
uwsgi:同WSGI一样是一种通信协议,并且是一个'uWSGI服务器'自有的协议,它用于定义传输信息的类型
uWSGI:实现了uwsgi和WSGI两种协议的Web服务器,负责响应python的web请求
40、*arg和**kwarg作用
*arg:代表位置参数,它会接收任意多个参数并把这些参数作为元组传递给函数
**kwarg:代表的关键字参数,返回的是字典,位置参数一定要放在关键字前面
41、什么是粘包?
在TCP协议中,发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。
42、redis的过期策略都有哪些?
1.volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
2.volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
3.volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
4.allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
5.allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
6.no-enviction(驱逐):禁止驱逐数据
43、redis中的watch的命令的作用
当watch某些个key后,在执行事务之前,若这些个key的value发生变化,则事务的后续操作都将被放弃并返回Null multi-bulk应答以通知调用者事务执行失败。
44、Django中如何防止SQL注入?
pymysql
import pymysql
conn = pymysql.connection()
cursor = conn.cursor()
sql = 'select * from user where name=%s'
cursor.execute(sql,['alex'])
Django
name = request.POST.get('name')
sql = 'select * from user where name=%s'
obj = models.User.objects.raw(sql,[name])
45、断言的作用是什么?
断言asert是判断当前行代码的结果是True or False,如果为True,则继续执行下面代码,否则则抛出异常。
46、jsonp是什么?实现原理是什么?
jsonp是hack浏览器同源策略的一种技术手段。
实现原理是浏览器不拦截src请求,通过Script标签来执行回调函数对数据进行接收处理。
47、什么是跨域(CORS)?
CORS,cross origin resource sharing,
如果是简单请求的话通过在服务器的响应头上添加response[Access-Control-Allow-Origin] = '*'就可以,
如果是复杂请求的话还需在响应头上添加response[Access-Control-Allow-Headers] = 'Content-Type',response['Access-Control-Allow-Methods'] = 'DELETE, PUT, POST'
48、什么是反射?应用场景都什么?
反射就是以字符串的形式去操作一个对象的方法或属性。
应用场景如在Djano中的CBV中的dispatch()方法,根据请求的方式去执行对应的方法。
还有就是在Django项目中的settings.py中配置项的使用就是以反射的方式去寻找对应的类或配置信息。
49、json序列化时,默认遇到中文会转换成unicode,如果想要保留中文怎么办?
在序列化时,中文汉字总是被转换为unicode码,在dumps函数中添加参数ensure_ascii=False即可解决。
50、with语句在进行上下文管理的机制是什么?
with后面加对象时候,首先这个对象的类中得有__enter__()及__exit__()方法,这两种方法里可以进行相应得操作,实现上下文管理。
51、select、poll、epoll 等IO多路复用模型的区别?
select:只支持在Windows上运行,采用轮询的方式,时间复杂度高,且最大连接数仅为2048
poll:解决了select的最大连接数问题,理论上可以是无限,但它同样是采用轮询方式,随着连接数越来越多时间复杂度就更高。
epoll:同时解决了select和poll的问题,满足较多的连接数,只需遍历被IO唤醒的连接即可。
52、什么是并发和并行?
并发:并发是在同一时间段内交替执行不同的任务
并行:并行是在同一时间点上同时执行各自的任务
53、python如何使用redis连接池
from redis import ConnectionPool, Redis
pool = ConnectionPool(host='localhost', port=6379, db=0, decode_responses=True)
rdb = Redis(connection_pool=pool)
rdb.set('name2', 'rooter')
name2 = rdb.get('name2')
print(name2)
54、主键和外键的区别?
主键:能确定一条记录的唯一标示
外键:用于与另一张表的关联,是能确定另一张表记录的字段,用于保持数据的一致性
55.mysql索引种类
1.普通索引
2.唯一索引
3.主键索引
4.联合索引
56、简述一下OSI七层模型及所对应的协议
应用层:http,https,ftp,smtp,ws
表示层
会话层
传输层:tcp/udp
网络层:ip协议
数据链路层:arp
物理层:
57、innodb引擎和MyIsam引擎的区别是什么?
innodb:存取速度快,支持事务、表锁和行级锁,支持外键,不支持全文索引
myisam:存取速度也快,不支持事务,不支持行级锁,不支持外键,支持全文索引
58、你的项目中为什么要使用flask框架呢?
1.相较于Django框架,flask框架比较轻量,中间涉及代码较少,做为资源服务器比较合适而无需像Django那样经过层层代码
2.第三方组件也是比较轻巧,按需所取即可。
59、列举出常见的关系型数据库和非关系型数据库
SQL数据库:mysql,sqlserver,oracle,db2,sqllite
NoSQL数据库:redis,mongodb,memcached
60、redis相比memcached有哪些优势?
redis:存储方式较多,支持数据类型多,可以持久化及主从复制
memcached:存储方式单一,支持数据类型只有string,不可以持久化,系统断电数据就丢失。
61、单例模式的优缺点
优点:
1、提供了对唯一实例的受控访问。
2、由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可 以提高系统的性能。
3、允许可变数目的实例。
缺点:
1、由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
2、单例类的职责过重,在一定程度上违背了“单一职责原则”。
3、滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象 的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态 的丢失。
62、jwt听说过吗?实现原理及用法
jwt组成:
- header base64后的头部信息(声明加密方式)
- payload base64后的载荷(数据部分)
- signature 签名(经加密方式加密头和载荷的信息)
伪代码:
header = base64encode('jwt','HMACSHA256')
payload = base64encode({'name':'alex'})
string = header + '.' + payload
signature = HMACSHA256(string, 'mysecret')
python中使用jtw:
pip install pyjwt
import jwt
token = jwt.encode({'name': 'alex'}, 'mysecret')
print(token)
my_token = b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiYWxleCJ9.zYeNPg9oJSbULugVm3-4qMOFQjIRgSkPvrcKSUwVRmo'
print(jwt.decode(my_token, key='mysecret'))
作用:
1.能有效地解决单点登录问题
2.在负载均衡中应用更好
63、sqlalchemy中session是什么、commit和flush的区别是什么?
Session:具有一个缓存, 位于缓存中的对象称为持久化对象, 它和数据库中的相关记录对应
commit和flush区别:flush 执行一系列 sql 语句,但不提交事务;commit 方法先调用flush() 方法,然后提交事务. 意味着提交事务意味着对数据库操作永久保存下来。
64、如何使用数据库连接池?
from DButils.PoolDB import PoolDB
import pymysql
pool = PoolDB(
creator=pymysql, # 使用链接数据库的模块
maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数
mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
maxcached=5, # 链接池中最多闲置的链接,0和None不限制
maxshared=3, # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
maxusage=None, # 一个链接最多被重复使用的次数,None表示无限制
setsession=[], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
ping=0,
# ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
host='127.0.0.1',
port=3306,
user='root',
password='123',
database='pooldb',
charset='utf8'
)
65、数据库优化都有哪些?
1、创建数据表时把固定长度的放在前面
2、将固定数据放入内存: 例如:choice字段 (django中有用到,数字1、2、3…… 对应相应内容)
3、char 和 varchar 的区别(char可变, varchar不可变 )
4、联合索引遵循最左前缀(从最左侧开始检索)
5、避免使用 select *
6、读写分离
- 实现:两台服务器同步数据
- 利用数据库的主从分离:主,用于删除、修改、更新;从,用于查;
读写分离:利用数据库的主从进行分离:主,用于删除、修改更新;从,用于查
7、分库
- 当数据库中的表太多,将某些表分到不同的数据库,例如:1W张表时
- 代价:连表查询
8、分表
- 水平分表:将某些列拆分到另外一张表,例如:博客+博客详情
- 垂直分表:讲些历史信息分到另外一张表中,例如:支付宝账单
9、加缓存
- 利用redis、memcache (常用数据放到缓存里,提高取数据速度)
10、join代替子查询
11、尽量把字段设置为not null 将来查询时,数据库不用比较null值
12、使用union来代替手动创建的临时表
13、不采用全文索引
66、如何查看SQL语句的效率?
执行计划:explain SQL语句,其中的type表明该语句是否命中索引,rows表示查询的行数
67、django项目中如何设置数据库读写分离
1.在settings.py中设置DATABASE_ROUTER = ['myutils.db.DbRouter']
2.在myutils文件夹下的db文件中创建DbRouter类
class DbRouter(object):
def db_for_read(self):
return 'default'
def db_for_write(self):
return 'write_db'
1、reduce
2、wraps
3、partial
4、singledispatch
from functools import singledispatch
@singledispatch
def show(obj):
print(obj, type(obj), "obj")
@show.register(str)
def _(text):
print(text, type(text), "str")
@show.register(int)
def _(n):
print(n, type(n), "int")
@show.register(list)
def _(n):
print(n, type(n), "list")
show(1)
show("xx")
show([1])
show({123})
结果分别是:
1 <class 'int'> int
xx <class 'str'> str
[1] <class 'list'> list
{123} <class 'set'> obj
69、反爬策略都有什么?
1、UA检测 => UA伪装
2、IP封禁 => IP代理
3、数据加密 => 数据解密
4、动态加载 => selenium 抓包
5、访问频率限制 => IP代理|降低请求速度
6、验证码 -> 云打码
70、git常见命令作用
# git init
初始化,当前所在的文件夹可以被管理且以后版本相关的数据都会存储到.git文件中
# git status
查看当前文件夹以及子目录中文件是否发生变化:
内容修改/新增文件/删除,已经变化的文件会变成红色,已经add的文件会变成绿色
# git add .
给发生变化的文件(贴上一个标签)或 将发生变化的文件放到某个地方,
只写一个句点符就代表把git status中红色的文件全部打上标签
# git commit -m
新增用户登录认证功能以及xxx功能将“绿色”文件添加到版本中
# git log
查看所有版本提交记录,可以获取版本号
# git reset --hard 版本号
将最新的版本回退到更早的版本
# git reflog
回退到之前版本后悔了,再更新到最新或者最新之前的版本
# git branch -a
查看所有的分支
# git remote -v
查看当前项目所在的git源及对源进行操作的
# git pull
拉取当前分支最新的git代码
# git pull origine 分支名
拉取远程某分支的最新代码
# git push
将当前分支的本地仓库代码上传至远程当前的分支
# git push origine 分支名
将当前分支的本地仓库代码上传至远程某分支上
# git remote add upstream xxx.git
git fetch upstream
git merge upstream/master
git push
将fork项目添加母项目的源,获取母项目的代码,然后将本地仓库代码与母项目某一分支的仓库代码进行合并,
合并完后将本地分支的本地仓库代码上传至自己远程分支的仓库,此时远程仓库与本地仓库均与母项目的代码保持一致
# git remote update origin -p
若远程新增或更新分支,为了保持本地的分支与远程分支一致,故更新本地的分支列表
# git checkout -b 分支名
新建并切换到新分支
# git checkout 分支名
切换分支
71、django的contenttype组件的作用?
contenttype是django的一个组件(app),它可以将django下所有app下的表记录下来
可以使用他再加上表中的两个字段,实现一张表和N张表动态创建FK关系。
- 字段:表名称
- 字段:数据行ID
72、提升scrapy爬取数据的效率
- 降低日志等级
- 禁止cookie
- 禁止重试
- 减少下载超时
- 增加并发
73、接口的幂等性是什么意思?
一个接口通过1次相同的访问,再对该接口进行N次相同的访问时,对资源不造影响就认为接口具有幂等性。
get,put,delete为幂等
74、垃圾回收机制
1.引用计数
2.标记清楚
3.分代回收
75、Django中间件
中间件是Django中用来处理请求与响应的类,在视图函数执行前后通过5个方法进行自定义操作。
76、Django中间件应用场景
1、权限验证
2、csrfmiddleware
3、访问频率控制
77、Https加密传输过程
一:浏览器请求:
发送支持的TLS等协议,列举支持的加密算法,压缩算法。
二:服务器确认:
发送服务器证书,带着服务器的公钥,确认使用的加密算法,确认安全协议。
三:浏览器核实:
浏览器首先验证证书真假,然后从证书中获取公钥,用公钥加密随机数,再用随机数加密消息。并发送信息。和 hash验证值
四:服务器再确认:
服务器使用私钥解密随机数,在用随机数解密握手消息,并验证Hash是否与浏览器发送的一致。使用这个随机数加密一段握手消息发送给浏览器。告知可以传输数据了。
五:传输数据。
双发使用浏览器之前生成的随机数密码进行加密传输。
78、守护线程和守护进程的区别
守护进程:守护进程的结束是随着主进程代码的结束而结束
守护线程:是随着主线程的执行完毕而完毕
79、简述一下GIL
GIL本质就是一把互斥锁,既然是互斥锁,所有互斥锁的本质都一样,都是将并发运行变成串行,以此来控制同一时间内共享数据只能被一个任务所修改,进而保证数据安全。
80、多线程和多进程的应用场景
多进程:多适用于计算密集型
多线程:多适用于IO密集型
81、DNS解析的过程
输入域名后,操作系统优先在DNS缓存中查找是否有对应的IP,若未查到就去操作系统的host文件中查找,仍未找到的话就去操作系统配置的DNS解析服务器上去查找,找到的话就把IP地址返回给操作系统,操作系统就对该IP发起请求。
82、列举 创建索引但是无法命中索引的8种情况。
1.- like '%xx'
select * from tb1 where name like '%cn';
2.- 使用函数
select * from tb1 where reverse(name) = 'wupeiqi';
3.- or
select * from tb1 where nid = 1 or email = 'seven@live.com';
特别的:当or条件中有未建立索引的列才失效,以下会走索引
select * from tb1 where nid = 1 or name = 'seven';
select * from tb1 where nid = 1 or email = 'seven@live.com' and name = 'alex'
4.- 类型不一致
如果列是字符串类型,传入条件是必须用引号引起来,不然...
select * from tb1 where name = 999;
5.- !=
select * from tb1 where name != 'alex'
特别的:如果是主键,则还是会走索引
select * from tb1 where nid != 123
6.- >
select * from tb1 where name > 'alex'
特别的:如果是主键或索引是整数类型,则还是会走索引
select * from tb1 where nid > 123
select * from tb1 where num > 123
7.- order by
select email from tb1 order by name desc;
当根据索引排序时候,选择的映射如果不是索引,则不走索引
特别的:如果对主键排序,则还是走索引:
select * from tb1 order by nid desc;
8.- 组合索引最左前缀
如果组合索引为:(name,email)
name and email -- 使用索引
name -- 使用索引
email -- 不使用索引
82、sqlalchemy如何执行原生SQL语句
from sqlalchemy import create_engine, Column,Integer,Char
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base() # 创建基类
class User(Base):
id = Column(Integer, primary=True, auto_increment=True)
name = Column(Char(22), index=True)
engine = create_engine('mysql+mysqlconnector://root:@127.0.0.1:3306/sqlalchemydb?charset=utf8') # 创建引擎
Base.metadata.create_all(engine) # 将所有继承Base的类都会用此引擎
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(engine)
db_session = Session() # 创建session对象
db_session.query(User).filter(User.name='alex')
db_session.add(User(name='wuse'))
db_session.flush() # 将添加的内容加载到db_session缓存中,并没有执行生成结果到数据库
db_session.commit() # 将执行结果到数据库
sql = 'select * from user where name=%s'
db_session.query(User).from_statement(sql, ['alex']) # 执行原生SQL
83、数据库分页怎么做?
1.记录当前页的ID最大值和最小值
2.上一页:
select * from userinfo where id<min_id order by id desc limit 10;
3.下一页:
select * from userinfo where id>max_id limit 10;
84、Django ORM中如何执行原生SQL语句?
from app01 import models
models.User.objects.raw(SQL语句)
1.form将表单提交变得更加简单和安全,包括重构数据结构后在页面件传递,
2.创建前端的html页面,以及接收和处理客户端传来的数据
3.ModelForm是在Form表单的基础上实现的,节省了大量的代码
86、flask中g的作用
g: global的意思
g对象可以保存用户的数据的,在同一次请求中全局通用,g的生命周期只有一个请求的周期。
87、flask中默认处理session处理机制
flask默认的session利用了Werkzeug的SecureCookie,把信息做序列化后经base64加密后放在cookie中
88、jwt在DRF中如何应用?
1.pip install djangorestframework-jwt
2.在配置settings文件的认证选择
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
# 配置默认的认证方式 base:账号密码验证
#session:session_id认证
'DEFAULT_AUTHENTICATION_CLASSES': (
# drf的这一阶段主要是做验证,middleware的auth主要是设置session和user到request对象
# 默认的验证是按照验证列表从上到下的验证
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
"rest_framework_jwt.authentication.JSONWebTokenAuthentication",
)}
3.
89、索引的种类都有哪些?
1.普通索引
2.主键索引
3.唯一索引
4.联合索引
- 唯一联合所以
- 主键联合索引
- 普通联合索引
90、scrapy-redis的调度器如何实现任务的深度优先和广度优先
# 在settings.py设置
from scrapy_redis.queue import PriorityQueue,FifoQueue,LifoQueue
# 先进先出:广度优先
SCHEDULER_QUEUE_CLASS='scrapy_redis.queue.FifoQueue'
# 后进先出:深度优先
SCHEDULER_QUEUE_CLASS='scrapy_redis.queue.LifoQueue'
91、数据库数据导入导出命令
1.导出
mysqldump -uroot -p 库名.* > /data/db.dump
2.导入
mysqldump -uroot -p < /data/db.dump
或者进入数据库内
source /data/db.dump
92、IO多路复用的作用及优点
1.作用:
多个客户端连接,单线程下实现并发效果,就叫多路复用
2.优点:
与多进程和多线程技术相比,I/O多路复用技术的最大优势是系统开销小,系统不必创建进程/线程,也不必维护这些进程/线程,从而大大减小了系统的开销。
93、Nginx负载均衡的策略都有哪些?
1.ip哈希
2.url哈希
3.权重分配
4.位置轮询(默认)
94、docker常用命令
- # 列出所有的image文件
docker images
- # 删除image文件
docker rmi 镜像id
- # 删除容器
docker rm 容器id
- # 在dockerhub中搜索docker
docker search 镜像名
- # 启动一个已经停止的容器
docker start 容器id
- # 从dockerhub中下载镜像
docker pull 镜像名
- # 进入带交互式的镜像
docker run -it /bin/bash
- # 查看容器
docker container ls -a
- # 进入带交互式的容器
docker exec -it 容器id /bin/bash
- # 查看某个容器的端口映射
docker port 容器id
- # 查看所有的容器(包括已停止的)
docker ps -a
- # 查看容器日志
docker logs -f <容器名orID>
- # 停止、启动、杀死一个容器
docker stop <容器名orID>
docker start <容器名orID>
docker kill <容器名orID>
- # 构建自己的镜像
docker build -t <镜像名> <Dockerfile路径>
- # 运行一个新容器,文件夹映射
docker run -d -v /var/log/docker/id1:/var/log
- # 导出一个镜像
docker save centos7(镜像名) > /tmp/centos.tar
- # 导入一个镜像
docker load < /tmp/centos.tar
95、git的常用命令
# 新建一个目录,将其初始化为Git代码库
git init [project-name]
# 下载一个项目和它的整个代码历史
git clone [url]
# 设置提交代码时的用户信息
git config [--global] user.name "[name]"
git config [--global] user.email "[email address]"
# 添加指定文件到暂存区
git add [file1] [file2] ...
# 添加指定目录到暂存区,包括子目录
git add [dir]
# 添加当前目录的所有文件到暂存区
git add .
# 提交暂存区到仓库区
git commit -m [message]
# 提交暂存区的指定文件到仓库区
git commit [file1] [file2] ... -m [message]
# 显示有变更的文件
git status
# 显示当前分支的版本历史
git log
# 显示commit历史,以及每次commit发生变更的文件
git log --stat
# 显示某次提交的元数据和内容变化
git show [commit]
# 更新远程仓储
git remote update
# 取回远程仓库的变化,并与本地分支合并
git pull [remote] [branch]
# 上传本地指定分支到远程仓库
git push [remote] [branch]
# 重置暂存区与工作区,与上一次commit保持一致
git reset --hard commit记录id
# 暂时将未提交的变化移除,稍后再移入
git stash
git stash pop
96、celery简单用法
1.创建任务文件夹Celery_Task
2.在Celery_Task下分别创建celery.py、task_a.py、task_b.py
3.在celery.py中创建app对象:
from celery import Celery
app = Celery('tasks', broker='redis://127.0.0.1:6379',
backend='redis://127.0.0.1:6379',
include=['Celery_Task.task_a', 'Celery_Task.task_b'])
4.在task_a.py中:
from Celery_Task.celery import my_celery
import time
@app.task
def one(x, y):
time.sleep(5)
return f'task_a {x + y}'
5.在task_b.py中:
from Celery_Task.celery import my_celery
import time
@app.task
def one(x, y):
time.sleep(10)
return f'task_b {x + y}'
6.在和Celery_Task文件夹同级目录下创建一个启动文件celery_excute.py:
from Celery_Task.task_a import one
from Celery_Task.task_b import two
one.delay(10, 10)
two.delay(20, 20)
7.在shell窗口中运行
celery worker -A Celery_task -l INFO -P eventlet
开启celery项目等待任务发布
8.运行celery_excute.py 发布任务
97、请尽量用简洁的方法将二维数组转换为一维数组
import numpy as np
li = [[1, 2, 4], [4, 5, 6, 9]]
array = np.array(li)
array = list(array.reshape(-1))
98、请手写一个单例类
import threading
class A(object):
_instance = None
# 使用递归锁来处理多线程时可能造成单例失败
_lock = threading.RLock()
def __new__(cls, *args, **kwargs):
if cls._instance:
return cls._instance
with cls._lock:
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance
99、为何基于tcp协议的通信比基于udp协议的通信更可靠?
tcp:可靠,因为只要对方回了确认收到信息,才发下一个,如果没收到确认信息就重发
UDP:不可靠,它是一直发数据,不需要对方回应
流式协议: TCP协议,可靠传输
数据报协议: UDP协议,不可传输
100、简述三次握手、四次挥手的流程
三次握手:
第一次握手:客户端先向服务端发起一次询问建立连接的请求,并随机生成一个值作为标识
第二次握手:服务端向客户端先回应第一个标识,再重新发一个确认标识
第三次握手:客户端确认标识,建立连接,开始传输数据
四次挥手 ---> 断开连接
第一次挥手:客户端向服务端发起请求断开连接的请求
第二次挥手:服务端向客户端确认请求
第三次挥手:服务端向客户端发起断开连接请求
第四次挥手:客户端向服务端确认断开请求
101、where和having的区别
Where:是一个约束声明,使用Where来约束来自数据库的数据,Where是在结果返回之前起作用的,且Where中不能使用聚合函数。
Having:是一个过滤声明,是在查询返回结果集以后对查询结果进行的过滤操作,在Having中可以使用聚合函数。
102、事务的四大特性
1.原子性:整个事务过程是完整且不可分割的,要么同时成功要么同时失败回滚
2.持久性:持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
3.隔离性:多个事务时,每个事务不能被别的事务所干扰
4.一致性:在事务开始以前,被操作的数据的完整性处于一致性的状态,事务结束后,被操作的数据的完整性也必须处于一致性状态。
103、drop、delete、truncate有什么区别
drop:执行后会删除表结构、表数据、索引,不能回滚
delete:执行后会删除表数据并将删除操作进行记录,可以进行回滚,在执行时若无where约束则将所有数据删除但不会删除表结构和索引
truncate:执行后会删除所有数据,不能回滚,假若该表存在索引,则truncate无法删除任何数据
104、数据库五大约束
1.primary KEY:设置主键约束;
2.UNIQUE:设置唯一性约束,不能有重复值;
3.DEFAULT 默认值约束
4.NOT NULL:设置非空约束,该字段不能为空;
5.FOREIGN key :设置外键约束。
105、SQLAlchemy中的 session和scoped_session 的区别
Session:由于无法提供线程共享功能,开发时要给每个线程都创建自己的session
scoped_session:线程安全,基于本地线程实现每个线程用同一个session
106、ansible的简单使用
1.先在宿主机及从机分别安装ansible
yum install ansible -y
2.宿主机生成自己的公私钥
ssh-keygen
3.宿主机将自己的公钥分别分配给从机
ssh-copy-id 从机ip
4.在宿主机的/etc/ansible/hosts文件内添加要管理的主机
192.168.21.42
192.168.21.39
192.168.21.41
192.168.21.40
[web] # 分组web
192.168.21.42
192.168.21.39
[db] # 分组db
192.168.21.41
192.168.21.40
5.执行第一条命令
ansible all -m ping
6.命令执行格式为
ansible [host-pattern] -m 模块名 -a(参数) 要执行的参数
7.模块帮助文档查询
ansible-doc -s 模块名
8.各模块的用法
shell:ansible web -m shell -a 'ls- l' # 执行Linux命令
script:ansible web -m script -a 'a.sh' # 执行本地脚本
copy:ansible web -m copy -a 'dest=/tmp src=/tmp/log' # 将宿主机的log文件夹复制到从机的/tmp文件夹下
file:ansible web -m file -a 'dest=/tmp/log state=(absent,directory,touch) # 将从机的某个文件或文件夹进行操作
fetch:ansible web -m fetch -a 'src=/data/test.txt dest=/data' # 将从机的/data/test.txt文件下载到宿主机的/data目录下
pip:
ansible web -m pip -a 'name=django==1.11.11' # 安装django1.11.11版本
ansible web -m pip -a 'name=requests state=absnt' # 卸载requests模块
yum:ansible web -m yum -a 'name=nginx state=latest'
servie: ansible web -m service -a 'name=nginx state=started/stopped/restarted'
cron:ansible web -m cron -a 'minute=0 hour=0 name=ntp job=ntpdate'
setup:ansible web -m setup # 列出各主机的机器信息
user:ansible web -m user -a 'name=alex' # 创建一个叫alex的用户
group:ansible web -m group -a 'name=actual'
9.剧本的用法:格式遵循yaml语法,test.yml
- hosts: web
tasks:
- name: installnginx
yum:
name: nginx
state: latest
- name: installmysql
yum:
name: mariadb
state: latest
- name: pipdjango
pip:
name: django==1.11.11
1.检测yaml文件的语法是否正确命令为:ansible-playbook --syntax-check test.yml
2.执行yaml文件的命令:ansible-playbook test.yml
107、nginx三大模块都有哪些?
1.核心模块:user,worker_processes,error_log,pid_file
2.事件模块:worker_connections,IO model
3.HTTP模块:server
108、Linux中释放内存的命令
echo 0/1/2/3 > /proc/sys/vm/drop_caches
drop_caches的值可以是0-3之间的数字,代表不同的含义:
0:不释放(系统默认值)
1:释放页缓存
2:释放dentries和inodes
3:释放所有缓存
109、Redis发布订阅模式用做消息队列和RabbitMQ的区别
1.可靠性
redis:没有相应的机制保证消息的可靠消费,如果发布者发布一条消息,而没有对应的订阅者的话,这条消息将丢失,不会存在内存中;
rabbitmq:具有消息消费确认机制,如果发布一条消息,还没有消费者消费该队列,那么这条消息将一直存放在队列中,直到有消费者消费了该条消息,以此可以保证消息的可靠消费;
2.实时性
redis:实时性高,redis作为高效的缓存服务器,所有数据都存在在服务器中,所以它具有更高的实时性
3.消费者负载均衡
rabbitmq队列可以被多个消费者同时监控消费,但是每一条消息只能被消费一次,由于rabbitmq的消费确认机制,因此它能够根据消费者的消费能力而调整它的负载;
redis发布订阅模式,一个队列可以被多个消费者同时订阅,当有消息到达时,会将该消息依次发送给每个订阅者;
4.持久性
redis:redis的持久化是针对于整个redis缓存的内容,它有RDB和AOF两种持久化方式(redis持久化方式,后续更新),可以将整个redis实例持久化到磁盘,以此来做数据备份,防止异常情况下导致数据丢失。
rabbitmq:队列,消息都可以选择性持久化,持久化粒度更小,更灵活;
5.队列监控
rabbitmq:实现了后台监控平台,可以在该平台上看到所有创建的队列的详细情况,良好的后台管理平台可以方面我们更好的使用;
redis:没有所谓的监控平台。
6.流量控制
Redis:不提供,需自行实现
RabbitMQ:服务器过载的情况,对生产者速率会进行限制,保证服务可靠性
7.出入队性能
对于RabbitMQ和Redis的入队和出队操作,各执行100万次,每10万次记录一次执行时间。
测试数据分为128Bytes、512Bytes、1K和10K四个不同大小的数据。
实验表明:
入队时,当数据比较小时Redis的性能要高于RabbitMQ,而如果数据大小超过了10K,Redis则慢的无法忍受;
出队时,无论数据大小,Redis都表现出非常好的性能,而RabbitMQ的出队性能则远低于Redis。
8.总结
redis:轻量级,低延迟,高并发,低可靠性;
rabbitmq:重量级,高可靠,异步,不保证实时;
rabbitmq是一个专门的AMQP协议队列,他的优势就在于提供可靠的队列服务,并且可做到异步,而redis主要是用于缓存的,redis的发布订阅模块,可用于实现及时性,且可靠性低的功能。
110、gensim和jieba是如何使用的?
1.将数据库内容进行jieba分词生成二维数组
content = [i.get('title') for i in MONGO_DB.content.find({})]
all_doc_list = [[word for word in jieba.cut_for_search(doc)] for doc in content]
2.将二维数组做成词袋
dictionary = corpora.Dictionary(all_doc_list)
3.将二维数组中每个列表中每个词语与词袋中的每个key进行匹配绑定,生成语料库corpus
corpus = [dictionary.doc2bow(doc) for doc in all_doc_list]
匹配后的结果,例如['你', '今年', '几岁', '了']
得到 [(1, 1), (5, 1), (6, 1), (7, 1)]
4.用LsiModel训练语料库,得到训练模型
lsi = models.LsiModel(corpus)
5.将语料库用训练模型得到训练结果
res = lsi[corpus]
6.用稀疏矩阵相似度类将特征数与训练结果生成稀疏矩阵模型
index = similarities.SparseMatrixSimilarity(res,
num_features=len(dictionary.keys()))
7.用jieba将被测语句分词为一个数组,并用词袋的key与被测语句数组进行匹配绑定得到被测语料
doc_test_list = [word for word in jieba.cut(a)]
doc_test_vec = dictionary.doc2bow(doc_test_list)
8.用训练模型将被测语料进行计算得到测试结果
gongyinshi = lsi[doc_test_vec]
9.用稀疏矩阵模型计算测试结果的相似度
sim = index[gongyinshi]
10.对下标和相似度结果进行一个排序,拿出相似度最高的结果
cc = sorted(enumerate(sim), key=lambda item: -item[1])[0][0]
cc为content的索引
11.取出与被测语句最相似的语句
text = content[cc]
111、mongodb的$all和$in有什么区别
$all:精确查找,所查内容为一个整体,包含此数据的会被查到
$in:模糊查找,若所查内容中任一元素在该key中均会被查到
112、什么是设计模式及都有哪些设计模式?
什么是设计模式
设计模式其实就是是写了很多代码的人总结出来的经验!
设计模式是关于一些问题的最佳解决方案;
也就是说,每一个典型的问题,关于一个典型的问题,他都会总结出来一个最佳的解决方案;
就比方说医生问诊:
你挂不同的科室,也就是不同的医生,每个医生都有自己擅长并拿手的治疗方案,关于某一个疾病都有自己独特的解决方案;
设计模式其实也一样,就是通过大量的代码总结出来的一些问题的最佳解决办法;
Java的设计模式一共有23种设计模式;
在这23种设计模式中,你仅掌握3~4种设计模式即可,并且要求能够手写出来;
23种设计模式并分为三大类:
1.创建型模式【五种】
工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
2.结构型模式【七种】
适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式
3.行为型模式【十一种】
策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
设计模式 -- 六大设计原则
总原则:开闭原则(Open Close Princciple)
对修改关闭,对扩展开放,所有的设计模式都必须遵循这个原则!
也就是说,所有的设计模式,你可以扩展它的功能,但是不可以修改它原始的功能;
六大小原则:
1.单一职责原则
不要存在多于一个导致类变更的原因,也就是说每个类应该实现单一的职责,如若不然,就应该把类拆分;
2.里氏替换原则
里氏替换原则中,子类对父类的方法尽量不要重写和重载,因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它;
3.依赖倒转原则
这个原则的意思四,每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用多张个隔离的接口,比使用单个接口(多个几口方法结合到一个的接口)更好;
4.迪米特法则(最少知道原则)
就是说,一个类对自己的依赖的类知道的越少越好。也就是说无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部,这样当被依赖的类变化时,才能量最小的影响该类;
6.复合重用原则
尽量首先使用合成/聚合的方式,而不是使用继承的方式;
单例设计模式
单例设计模式是最简单的设计模式
目的:创建全局唯一的实例对象;
1.饿汉模式
在类加载的时候,就创建一个对象,因为就比较饿嘛,上来就创建对象;
2.懒汉模式
需要的时候才创建,因为比较懒,你不拿鞭子抽它,他就不动;