表关系图及建表

from django.db import models
# Create your models here.
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
phone = models.BigIntegerField(null=True,blank=True) # 告诉django后台这个字段可以不填
# avatar 存的是用户的头像文件路径,用户上传的头像会自动保存到avatar文件下
avatar = models.FileField(upload_to='avatar/', default='media/avatar/default.jpg')
create_time = models.DateField(auto_now_add=True)
blog = models.OneToOneField(to='Blog', null=True)
# class Meta:
# verbose_name_plural = '用户表'
# verbose_name = '用户表'
def __str__(self):
return self.username class Blog(models.Model):
site_title = models.CharField(max_length=32)
site_name = models.CharField(max_length=32)
site_theme = models.CharField(max_length=255)
# def __str__(self):
# return self.site_name class Category(models.Model):
name = models.CharField(max_length=32)
blog = models.ForeignKey(to='Blog')
def __str__(self):
return self.name class Tag(models.Model):
name = models.CharField(max_length=32)
blog = models.ForeignKey(to='Blog')
def __str__(self):
return self.name class Article(models.Model):
title = models.CharField(max_length=255)
desc = models.CharField(max_length=255)
content = models.TextField()
create_time = models.DateField(auto_now_add=True) # 数据库优化字段
comment_num = models.IntegerField(default=0)
up_num = models.IntegerField(default=0)
down_num = models.IntegerField(default=0)
# 外键字段
blog = models.ForeignKey(to='Blog', null=True)
category = models.ForeignKey(to='Category', null=True)
tag = models.ManyToManyField(to='Tag', through='Article2Tag', through_fields=('article', 'tag'), ) def __str__(self):
return self.title class Article2Tag(models.Model):
article = models.ForeignKey(to='Article')
tag = models.ForeignKey(to='Tag') class UpAndDown(models.Model):
user = models.ForeignKey(to='UserInfo')
article = models.ForeignKey(to='Article')
is_up = models.BooleanField() class Comment(models.Model):
user = models.ForeignKey(to='UserInfo')
article = models.ForeignKey(to='Article')
content = models.CharField(max_length=255)
create_time = models.DateField(auto_now_add=True)
parent = models.ForeignKey(to='self', null=True)

注册页面搭建

校验用户名、密码、邮箱、头像等格式是否正确

# coding:utf8
# myforms.py
from django import forms
from django.forms import widgets
from app01 import models class MyRegForm(forms.Form):
username = forms.CharField(max_length=8, min_length=3, label='用户名',
error_messages={
'max_length': '用户名最大八位',
'min_length': '用户名最小三位',
'required': '用户名不能为空'
}, widget=widgets.TextInput(attrs={'class': 'form-control'})
) password = forms.CharField(max_length=8, min_length=3, label='密码',
error_messages={
'max_length': '密码最大八位',
'min_length': '密码最小三位',
'required': '密码不能为空'
}, widget=widgets.PasswordInput(attrs={'class': 'form-control'})
) confirm_password = forms.CharField(max_length=8, min_length=3, label='确认密码',
error_messages={
'max_length': '确认密码最大八位',
'min_length': '确认密码最小三位',
'required': '确认密码不能为空'
}, widget=widgets.PasswordInput(attrs={'class': 'form-control'})
)
email = forms.EmailField(label='邮箱', error_messages={
'required': '邮箱不能为空',
'invalid': '邮箱格式错误',
}, widget=widgets.EmailInput(attrs={'class': 'form-control'})) # 局部钩子,校验用户名是否已存在
def clean_username(self):
username = self.cleaned_data.get('username')
is_exist = models.UserInfo.objects.filter(username=username)
if is_exist:
self.add_error('username', '用户名已存在!')
return username # 全局钩子,校验密码是否一致
def clean(self):
password = self.cleaned_data.get('password')
confirm_password = self.cleaned_data.get('confirm_password')
if password != confirm_password:
self.add_error('confirm_password', '两次密码不一致!') return self.cleaned_data

注册后端代码

from app01 import myforms
def register(request):
form_obj = myforms.MyRegForm()
back_dic = {'code': 100, 'msg': ''}
# 校验用户信息是否合法
if request.method == 'POST':
# print(request.POST)
form_obj = myforms.MyRegForm(request.POST)
if form_obj.is_valid():
clean_data = form_obj.cleaned_data
clean_data.pop('confirm_password')
# 手动获取用户头像
user_avatar = request.FILES.get('myfile')
if user_avatar: # 判断用户是否传头像了,
clean_data['avatar'] = user_avatar
# 创建数据
models.UserInfo.objects.create_user(**clean_data)
back_dic['msg'] = '注册成功!'
back_dic['url'] = '/login/'
else:
back_dic['code'] = 101
back_dic['msg'] = form_obj.errors
return JsonResponse(back_dic)
return render(request, 'register.html', locals())

注册前端代码

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7/css/bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'font-awesome-4.7.0/css/font-awesome.min.css' %}">
<script src="{% static 'bootstrap-3.3.7/js/bootstrap.min.js' %}"></script> </head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<h2 class="text-center">注册</h2>
<form action="" id="myform" novalidate>
{% csrf_token %}
{% for foo in form_obj %}
<div class="form-group">
{#foo.auto_id获取foo渲染的input框的id值,点击input框提示信息,自动跳到input框内#}
<label for="{{ foo.auto_id }}">{{ foo.label }}</label>
{# 不同的input框#}
{{ foo }}
<span class="errors pull-right" style="color:red;"></span>
</div>
{% endfor %}
<div class="form-group">
{# for指定id=myfile(获取头像的input框),点击头像,就能跳进选择框,#}
<label for="myfile">头像
<img src="/static/img/default.jpg" alt="" id ="img" style="height: 60px;margin-left: 5px">
</label>
<input type="file" name="avatar" id="myfile" style="display: none">
</div>
<input type="button" class="btn btn-primary pull-right" value="注册" id="submit">
</form>
</div>
</div>
</div>
<script>
$('#myfile').change(function () {
// 获取用户上传的文件对象
var fileobj = $(this)[0].files[0];
// 利用内置对象FileReader
var fileReader = new FileReader();
// 将文件对象交由文件阅读器读取,文件内容
fileReader.readAsDataURL(fileobj);
// 找到img标签,修改src属性
// 等待文件阅读器完全读取完文件数据之后,才做下面的操作,使用onload。不然插入的图片在浏览器页面渲染不出来
fileReader.onload = function() {$('#img').attr('src',fileReader.result)};
});
$('#submit').click(function () {
// 产生内置对象formdata
var formData = new FormData();
// 循环添加form组件的普通键值对 username,password,confirm_password,email,还有一个是自动添加到formdata对象中的csrf组件
//console.log($('#myform').serializeArray()) [{…}, {…}, {…}, {…}, {…}]
$.each($("#myform").serializeArray(),function (index,obj) {
formData.append(obj.name,obj.value)
});
// 手动添加文件
formData.append('myfile',$('#myfile')[0].files[0]);
$.ajax({
url:'',
type:'post',
data:formData,
// 传文件需要指定两个参数
contentType:false,
processData:false,
success:function (data) {
if (data.code==100) {
location.href = data.url
}else{
// 如果没有成功,说明用户输入的数据不合法,需要展示错诶信息
{#console.log(data.msg)#}
// 能够将对应错误信息准确无误的渲染到对应的input框下面的span标签内
// 手动拼接input的id值
$.each(data.msg,function(index,obj){
{#console.log(index,obj)#}
var targetId = '#id_'+ index;
// 将父标签div添加一个has-error属性,该属性能将对应的input框动态渲染成红色,与错误信息展示相一致
$(targetId).next().text(obj[0]).parent().addClass('has-error')
})
}
}
})
});
$('input').focus(function () {
$(this).next().text('').parent().removeClass('has-error') // 清除格式错误信息,并去除父标签div的has-error属性
})
</script>
</body>
</html>

admin后台管理

是一个能够帮助你快速的实现注册了的模型表数据的增删改查,

使用:

在应用中找到admin.py文件,然后注册想要操作的默认表即可,使用超级管理员账户,即可登录后台进行数据的管理。

1.创建超级管理员用户;
在Django console内输入createsuperuser,并根据提示输入用户名、密码等,
2.注册要操作的表;
在应用app中的admin.py文件夹下,导入models,并注册;
from django.contrib import admin(自带的)
from app01 import models
admin.site.register(models.UserInfo)
admin.site.register(models.Blog)
admin.site.register(models.Tag)
admin.site.register(models.Category) 等等
3.用创建的超级管理员用户登录django后台,进行表操作;
http://127.0.0.1:8000/admin/
登入后台注册表表名都自动加上了's',
如果想以自己定义的中文展示对应
的表名,可以在models.py内的各
个类内部加上如下代码:
class Meta:
# verbose_name_plural = '用户表' # 修改成中文表名
verbose_name = '用户表' # 修改成中文并加上一个字母's'

admin 内部源码


http://127.0.0.1:8000/admin/app01/userinfo/ # 展示数据
http://127.0.0.1:8000/admin/app01/userinfo/add/ # 添加数据
http://127.0.0.1:8000/admin/app01/userinfo/3/change/ # 编辑数据
http://127.0.0.1:8000/admin/app01/userinfo/3/delete/ # 删除数据 http://127.0.0.1:8000/admin/app01/article/ # 展示数据
http://127.0.0.1:8000/admin/app01/article/add/ # 添加数据
http://127.0.0.1:8000/admin/app01/article/3/change/ # 编辑数据
http://127.0.0.1:8000/admin/app01/article/3/delete/ # 删除数据

用户头像展示(media配置)

博客园每个文章前都有用户的头像,通过模板语法{{article.blog.userinfo.avatar}},并不能将头像渲染到博客园页面上,这时候需要将我们avatar文件夹暴露给用户,才能添加上头像;然而django固用的暴露给外界资源的方式都是用文件夹media,通过media配置,暴露给用户任意的后端资源;

在settings文件夹下配置如下代码:
# 规定用户上传的所有的静态文件全部放到media文件夹下
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
## 暴露任意文件夹资源
MEDIA_ROOT1 = os.path.join(BASE_DIR,'app01')
之后用户在注册的时候会在项目文件夹下自动创建一个media文件夹,内部套一个我们类中指定的upload_to='avatar'文件夹,并把用户注册的头像自动添加该文件中;
然后再在urls.py文件中配置路由:
from django.views.static import serve
from BBS import settings
# 手动暴露后端的文件夹资源
urlpatterns = [url(r'^media/(?P<path>.*)',serve,{'document_root':settings.MEDIA_ROOT})]
# 暴露任意文件夹资源
urlpatterns = [url(r'^app01/(?P<path>.*)',serve,{'document_root':settings.MEDIA_ROOT1})]

个人主页搭建

404页面 直接拷贝博客园404页面即可
但是会发现图片加载不出来 是因为 人家设置了 图片防盗链
什么是图片防盗链?
实现原理:校验当前请求的url是否是我本网站的
如果是我正常给资源,如果不是 直接拒绝
Referer: http://127.0.0.1:8000/fsdafasfd/
Referer请求头 表示了你这个网页上一次是从哪里来的
爬虫
解决方式
1.爬虫直接爬取所有图片 保存到自己的数据库
2.如果图片需求量不大的情况下 你可以采用人工智能方式 手动下载到本地

侧边栏渲染

	inclusion_tag

	日期归档
id content create_time month
1 111 2019-1-1 2019-1
2 322 2019-2-11 2019-2
3 222 2019-1-21 2019-1
4 555 2019-1-22 2019-1

		-官方提供
from django.db.models.functions import TruncMonth
models.Article.objects
.annotate(month=TruncMonth('create_time')) # Truncate to month and add to select list
.values('month') # Group By month
.annotate(c=Count('id')) # Select the count of the grouping
.values('month', 'c') # (might be redundant, haven't tested) select month and count

点赞点踩

1.前端页面点赞点踩样式拷贝
1.html和css代码 两者都需要拷贝
2.点赞点踩的图片设置了防盗链的 要想永久建议下载到本地
3.前端页面如何区分用户是点赞还是点踩
给赞和踩的图标加了一个公共的样式类
给这个样式类绑定了一个点击事件
再利用this指代的就是当前被操作对象
只需要通过判断当前被点击对象是否有某个独立的样式类
4.发送ajax请求
参数只需要两个 文章的主键 点赞或点踩
5.后端逻辑
1.后端接收到的用户点赞点踩参数是一个前端js的布尔值字符串
你可以直接利用json.loads转成后端的布尔值类型
2.验证当前请求是否是ajax请求
request.is_ajax()
3.验证当前用户是否登录
request.user.is_authenticated()
4.验证当前文章是否是当前用户自己写的
根据前端传过来的文章主键查询出文章对象
根据文章对象查询出作者与request.user当前登录用户做比对
5.验证当前文章是否已经被当前用户点过赞或踩了
根据文章主键和当前登录用户对象去点赞点踩表中筛选数据
一旦有数据 说明该用户已经给当前文章点过赞或者踩
6.操作数据库
一旦要注意在操作点赞点踩表的时候 一旦要去文章表中将
点赞或点踩普通字段同步修改
6.前端展示提示信息
点赞或点踩成功需要将前端页面对应的数字在原来的基础上加一
注意在加的时候一定要转换成数值类型相加
Number() 类似后端 int()

文章的评论

1.前端获取用户评论样式搭建
应该做到只有登录的用户才能看到评论框
2.前端利用ajax发送评论请求
当前文章的主键
评论的内容
3.后端
1.校验当前请求是否是ajax请求
获取主键 评论内容
2.操作数据库
利用事务完成两个地方数据的同步
1.文章表里面的评论普通字段
2.评论表记录
3.前端展示评论楼信息
后端获取当前文章所有的评论传递到前端
4.评论渲染
当用户点击提交按钮 除了渲染之外 你还应该将textarea框内容清空
1.DOM临时渲染
1.获取当前评论人的用户名和评论内容
2.利用es6新语法 模板字符串 完成字符串的替换
3.查找ul标签 将创建的li标签添加到ul标签内
2.render永久渲染
在渲染页面的时候 for循环文章所有的评论一一渲染即可
5.子评论的功能
子评论和根评论的唯一区别仅仅在于parent_id字段是否有值
突破口:点击回复按钮发送了几件事
1.获取当前登录用户想评论的那条评论人的用户名 拼接成 @用户名\r\n
利用标签可以支持任意多个自定义属性 给回复按钮标签 添加username={{comment.user.username}}
2.将拼接完成的内容的添加到textarea框中
3.textarea框自动聚焦
4.无论是提交根评论还是子评论都是点击的一个按钮
1.提交子评论和父评论唯一的差距就在于你是否传了parent_id
2.数据库中parent_id是可以为空的 也就意味你在创建数据的时候 如果传空也不会有影响
5.无论是根评论 子评论我都可以提交一个parent_id无论它是否有值
后端只需要接收parent_id然后在create方法中直接添加即可 至此后端代码一行都不需要再修改了
6.在点击回复按钮的时候 除了获取评论人的用户名之外 还应该回去当前评论数据的主键值
还需要给回复按钮 加一个自定义数据 pk = {{comment.pk}}
点击回复按钮能够获取到评论主键值 什么时候用的?
当你在点击提交评论按钮的时候才会用到评论主键值(一个函数需要引用林外一个函数中的某个名字)
应该在全局设置一个专门存储评论主键值的字段parent_id = null;默认等于None
点击完回复按钮之后才会对全局的parent_id进行修改
7.提交子评论内容中含有@用户名\r\n 这一段内容并不是用户自己写的 应该去除
前端处理
获取\n所在的索引值 然后你自己思考了一下 切片取值 是顾头不顾尾 所以应该给索引加1
利用slice切片操作(从0开始到索引结束的内容全部去除 保留剩下部分)
8.你会发现当你提交了一次子评论之后 页面不刷新的情况 永远无法提交根评论了 因为全局的parent_id字段一直有值
你应该在每一次提交完成后 清空parent_id字段
9.评论渲染的时候
如何渲染出子评论
利用orm表查询
{{ comment.parent.user.username }}

后台页面搭建

添加文章的前端页面渲染

类似于博客园添加随笔时的界面,在以下链接下载并解压,复制到BBS的static文件夹中

编辑页面下载官网:kindeditor编辑器

处理xss攻击

文章简介的获取

1.文章简介的获取

​ 截取150个中文字符

2.防止用户写script脚本

1.获取用户输入的所有的script标签直接删除

2.给script转义

3.

处理xss攻击可以使用beautifulsoup4模块

beautifulsoup4 简称bs4

pip3 install beautifulsoup4

from bs4 import beautifulsoup

# 先生成一个beautifulsoup对象
soup =BeautifulSoup(content,'html.parser')
for tag in soup.find_all():
# print(tag.name) # 获取当前html页面所有的标签
# print(soup.text) # 获取当前html页面所有的文本
if tag.name == 'script': # 对于script标签,直接删除
tag.decompose() # 将符合条件的标签删除
desc = soup.text[0:150] # 给文章简介获取150个
article_obj = models.Article.objects.create(title=title,desc =desc,content=str(soup),category_id=category,blog=request.user.blog)

上传图片操作

在kindeditor编辑器官网上找到以下配置:

》上传文件
uploadJson : '../jsp/upload_json.jsp', # 匹配一个路由与视图函数的对应关系 仅仅只配置这个参数,上传图片还会报错(CSRF verification failed. Request aborted.),还需要额外配置以下参数: 》编辑器初始化参数》extraFileUploadParams:
extraFileUploadParams : {
'csrfmiddleware':'{{csrf_token}}',
}

所以文本编辑器加上传文件这一块一共应该配置的参数为:

<textarea name="content" cols="30" rows="10" id="id_content"></textarea>

 <script charset="utf-8" src="/static/kindeditor/kindeditor-all-min.js"></script>
<script>
KindEditor.ready(function (K) {
window.editor = K.create('#id_content', {
width: '100%',
height: '450px',
resizeType: 1,
uploadJson : '/upload_img/',
extraFileUploadParams : { // 支持传额外的图片参数
'csrfmiddlewaretoken':'{{csrf_token}}',
}
});
});
</script>

图片传到后端操作:

def upload_img(request):
# 接受用户写文章上传的所有的图片资源
if request.method == "POST":
# print(request.FILES) # <MultiValueDict: {'imgFile': [<InMemoryUploadedFile: 44.jpg (image/jpeg)>]}> # 编辑文章上传的图片传到后端全部都放在了request.FILES中了,且字典的键是它指定的imgFile
file_obj = request.FILES.get('imgFile')
#将文章图片存储在media文件下一个专门用来放文章图片的文件夹 # 手动拼接出图片所在的文件夹路径
base_path = os.path.join(settings.BASE_DIR,'media','article_img')
if not os.path.exists(base_path):
os.mkdir(base_path) # 手动拼接文件的具体路径
file_path = os.path.join(base_path,file_obj)
# 将图片存放在该文件夹下
with open(file_path,'wb') as f:
for line in file_obj:
f.write(line)
back_dic = {"error": 0, "url": "/media/article_img/%s"%file_obj.name}
return JsonResponse(back_dic)
# 图片上传成功之后,返回的数据格式(JSON)
'''
》上传文件》返回格式(JSON)
//成功时
{
"error" : 0,
"url" : "http://www.example.com/path/to/file.ext"
}
//失败时
{
"error" : 1,
"message" : "错误信息"
}
'''

用户头像修改

BBS 功能汇总

1.数据库的设计  (7张表 一对一对一 一对多对多 其余的都是一对多)
2.forms组件完成注册功能
注册功能前端错误信息渲染 需要你自己找出每一个input id值的规律
3.登陆功能
图片验证码 4.首页展示
django admin后台管理
用户头像展示
media配置 5.个人站点
侧边栏展示
侧边栏筛选功能
inclusion_tag 6.文章详情页
点赞点踩 评论 7.后台管理

BBS 页面搭建知识点整理的更多相关文章

  1. JSP页面开发知识点整理

    刚学JSP页面开发,把知识点整理一下. ----------------------------------------------------------------------- JSP语法htt ...

  2. 扩展auth_user字段、BBS需求分析、创建BBS数据库、注册页面搭建与用户头像展示及Ajax提交数据

    昨日内容回顾 csrf跨站请求 1. SQL注入 2. xss攻击 3. csrf跨站请求 4. 密码加密(加盐) '''django中默认有一个中间件来验证csrf''' # 只针对post请求才验 ...

  3. springmvc 项目完整示例08 前台页面以及知识点总结

    至此已经基本测试成功了,我们稍作完善,让它成为一个更加完整的项目 我们现在重新规划下逻辑 两个页面 一个登录页面 一个欢迎页面 登陆页面输入账号密码,登陆成功的话,跳转登陆成功 欢迎页面 并且,更新用 ...

  4. vue前端面试题知识点整理

    vue前端面试题知识点整理 1. 说一下Vue的双向绑定数据的原理 vue 实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty() 来劫 ...

  5. stark组件之显示页面搭建(四)

    页面搭建包括第一如何获取前端传过来的数据,第二如何在前端渲染出对应标签. 一.后台获取数据并进行处理 在路由系统中,每一个路由都对应着一个处理函数,如下所示: def wrapper(self, fu ...

  6. web前端面试知识点整理

    一.HTML5新特性 本地存储 webStorage websocket webworkers新增地理位置等API对css3的支持canvas多媒体标签新增表单元素类型结构标签:header nav ...

  7. HTML&&CSS基础知识点整理

    HTML&&CSS基础知识点整理 一.WEB标准:一系列标准的集合 1. 结构(Structure):html 语言:XHTML[可扩展超文本标识语言]和XML[可扩展标记语言] 2. ...

  8. Python基础知识点整理(详细)

    Python知识点整理(详细) 输出函数 print()可以向屏幕打印内容,或者在打开指定文件后,向文件中输入内容 输入函数 input([prompt])[prompt] 为输入的提示字符.该函数返 ...

  9. js页面跳转整理

    js页面跳转整理 js方式的页面跳转1.window.location.href方式    <script language="javascript" type=" ...

随机推荐

  1. 推荐:【视频教程】ASP.NET Core 3.0 入门

    墙裂推荐了,免费,通俗易懂,唯一可惜的就是不是我录的,更可惜的是人家录制完了快半年了我还没看完... 版权归原作者所有,建议新手还是边看边实践吧,要不然过完一遍发现自己啥也没学会,不要眼高手低 [视频 ...

  2. NoSQL数据库一MongoDB基本使用

    如今的网站对数据存储要求越来越灵活,在这种需求下 NoSQL 也就是非关系数据库越来越流行.所谓非关系数据库,是指不使用 SQL 语言进行数据操作的数据库的统称.这类数据库存储数据时没有固定的模式,不 ...

  3. oracle中row_number()的用法

    公司系统升级的时候需要数据迁移,遇到一个问题:新表的数据结构和旧表异构,旧表是流水号,新表是联合主键(业务号码+业务号码序号) 最后发现用窗口函数 row_number() + partition b ...

  4. Python基础 第7章 再谈抽象

    1. 1 多态 多态,即便不知道变量指向的是哪种对象,也能对其执行操作,且操作的行为将随对象所属的类型(类)而异. 1.2 多态与方法 当无需知道对象是什么样的就能对其执行操作时,都是多态在起作用. ...

  5. SAS学习笔记12 SAS数据清洗和加工

    set语句纵向合并 我们把a1和b1进行合并,并区分是来自哪个数据集,会用到in=选项 in=a是产生临时变量a,由于它是a1的选项,所以a的值=1(来自a1)或者=0(不来自a1) in=b是产生临 ...

  6. SAS学习笔记8 循环语句(do函数)

    do-end函数

  7. 监听lsnrctl status查询状态报错linux error 111:connection refused

    报错现象 今天给客户一个单实例环境配置监听,创建正常,查询状态异常报错 tns tns tns linux error :connection refused 匹配MOS Starting TNS L ...

  8. restTemplate源码解析(三)创建ClientHttpRequest请求对象

    所有文章 https://www.cnblogs.com/lay2017/p/11740855.html 正文 上一篇文章中,我们大体看了一下restTemplate的核心逻辑.再回顾一下核心代码 p ...

  9. canvas验证码实现

    1 <!DOCTYPE html> <html> <!-- head --> <head> <meta charset="utf-8&q ...

  10. Java 之 Collections 工具类

    一.Collections 概述 java.utils.Collections 是集合工具类,用来对集合进行操作. 二.常用方法 public static <T> boolean add ...