一些笔记

前端

(https://www.cnblogs.com/yuan-liu/p/15252207.html)

HTML + CSS

初始HTML

HTML基本结构

  • 分为头部和主体部分
  • <body></body>等成对出现的标签叫开放标签 和闭合标签
  • 单独呈现的标签(空标签),如<hr/>;意为用/来关闭空标签
  • <!DOCTYPE html> 代表html的版本
  • <html lang="en"> 网页默认语言,中文 lang="ch-CN"lang="zh"
  • <meta charset="UTF-8"> 默认的编码规则
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title> 我的第一个网页 </title>
    </head>
    <body> 
        我的第一个网页
    </body>
</html>

网页的基本标签

块级标签:独占一行;行级标签:不会独占一行

标题标签
  • <h1> 一级标题 </h1>……<h6> 六级标题 </h6>
  • 标题标签共分六级:<h1>最大,<h6>最小
  • 标题标签独占一行
段落标签
  • <p></p>段落标签独占一行
换行标签
  • <br/>标签后面的内容自动换行
  • / 写在后面表示自关闭
水平线标签
  • <hr/>标签在网页中转换成一条分割线
字体样式标签
  • <strong></strong>字体加粗标签
  • <em></em>斜体标签
注释和特殊符号(转义)
  • HTML注释 <!-- 注释内容 -->
  • 空格符号 &nbsp;
  • 大于符号 > &gt; 小于符号 < &lt;
  • 引号 “” &quot;
  • 版权符号 @&copy;
图像标签
  • <img src="path" alt="text" title="text" width="x" height="y"/>
  • src中传入图片地址,alt表示图片的替代文字,title是图片悬停的提示文字
链接标签
  • <a href="path" target="目标窗口位置"> 显示内容 </a>
  • href内存放链接地址;target值:_self、_blank

常用的超链接

  • 使用cursor: pointer;模拟链接;鼠标放上变小手
页面链接
  • 从一个页面跳转到另外一个页面
  • <a href="跳转链接地址" target="网页打开方式"> 内容 </a>
锚链接
  • 从从本页面特定位置跳转到本页面下一位置
  • 从本页面特定位置跳转到其他页面特定位置
  • 创建跳转标记 <a name="aign">A位置</a>
  • 创建跳转链接 <a href="#sign">B位置</a>
功能性链接
  • 电子邮件、QQ、MSN
  • <a href="mailto:2452791011@qq.com">联系我们</a>

列表的分类

无序列表
<ul>
    <li>列表项1</li>
    <li>列表项2</li>
    <li>列表项3</li>
</ul>
  • 列表项可以包括图片、文本、还可以嵌套列表和其他标签
  • 使用 ul 声明无序列表
  • ul{list-style: none;} 去除列表前的小黑点
有序列表
<ol>
    <li>列表项1</li>
    <li>列表项2</li>
    <li>列表项3</li>
</ol>
  • 有序列表默认以数字序号排列显示
  • 使用 ol 声明有序列表
定义列表
<dl>
    <dt>声明列表项</dt>
    <dd>列表项1</dd>
    <dt>声明列表项2</dt>
    <dd>列表项2</dd>
</dl>
  • 在以后网页制作过程中经常会用到定义列表,特别是图文混淆的情况
  • 定义列表用 dl 声明, 使用 dt 声明列表项, 使用 dd 定义列表项内容

表格

简单通用,结构稳定

表格的基本语法
<table>
    <tr>
    	<th>标题1</th>
        <th>标题2</th>
        <th>标题3</th>
    </tr>
    <tr>
         <td>内容1</td>
         <td>内容2</td>
         <td>内容3</td>
     </tr>
     <tr>
         <td>内容4</td>
         <td>内容5</td>
         <td>内容6</td>
     </tr>
</table>
  • 创建表格标签 <table></table>;行标签 <tr></tr>;单元格标签 <td></td>;标题标签 <th></th>

  • <table border="1" height="100%" width="100%">设置表格宽高

  • td 设置边框,边框之间会有空隙。给table 设置 boder-collapse

    table{
    	text-align: center;
    	border-collapse: collapse;
    }
    table td{
    	border: 1px solid #00000080;
    }
    
  • 表格行(rowspan)、列合并(colspan)

表单语法

<form method="post" action="result.html">
        <p>名字:<input name="name" type="text" /> </p>
        <p> 密码:<input name="password" type="text"> </p>
        <p>
            <input type="submit" name="Button" value="提交">
            <input type="reset" name="Reset" value="重填">
        </p>
</form>
  • method规定如何发送表单数据 get | post, action 表示向何处发送表单数据
  • post提交数据安全性要高于get
表单元素格式
  • <input type="text" name="fname" value="text"/>
  • type 指定元素的类型。text、password、checkbox、radio、submit、reset、file、hidden、image和button,默认为text
  • name 指定表单元素的名称,同一组radio标签name值必须相同;checkbox同理
  • value 元素的初始值。type为radio时必须指定一个值
  • size 指定表单元素的初始宽度。当type为text或password时,表单元素的大小以字符为单位。对于其他类型,宽度以像素为单位
  • maxlength type为text或password时,输入的最大字符
  • checked type为radio或checkbox时,指定按钮是否是被选中
input元素类型
  • text 文本框;password 密码框;radio 单选按钮;checkbox 复选框
  • select、option 列表框;reset、submit、button按钮;
  • textarea 多行文本域;可设置宽:clos=""高: rows=""
  • file 文件域
  • label 扩大按钮点击区域
  • 按钮设置为图片 <input type="image" src=""/>
  • input{outline:none} 选中输入框没有提示框
  • outline: none; 点击输入框输入文字不加载边框
  • input type="text" 标签设置 required 表示必须填写
<p>
	性别:
	<label for="gen1">
	<input name="gen" type="radio" class="input" valur="男" id="gen1" 		            checked/>男
	</label>
    
	<label for="gen2">
	<input name="gen" type="radio" calss="input" value="女" id="gen2"/>女
	</label>
</p>
表单的高级应用
隐藏域
  • <input type="hidden" value="666" name="userid">
  • type="hidden" 隐藏域,只是把type的值设置为hidden
只读
  • <input name="name" type="text" value="张三" readonly>
禁用
  • <input type="submit" disabled value="保存"

HTML新增元素及属性

HTML5

  • HTML5是用于取代HTML和XHTML的标准版本
  • 新的语义化标签,比如 header、footer、nav
  • 新的表单控件,比如 email、url、search
  • 用于绘画的 canvas 元素
  • 用于媒介回放的 video 和 audio 元素

HTML5新增结构元素

header 页面或页面中某一个区块的页眉,通常是一些引导和导航信息
nav 可以作为页面导航的链接组
section 页面中的一个内容区块,通常由内容及其标题组成
article 代表一个独立的、完整的相关内容块,可独立于页面其它内容使用
aside 非正文的内容,与页面的主要内容是分开的,被删除而不会影响到网页的内容
footer 页面或页面中某一个区块的脚注
<div class="box">        <header class="header">头部</header>        <main class="main">            <nav class="nav">导航</nav>            <div class="container">                <aside class="aside">侧边栏</aside>                <article class="article">正文</article>            </div>        </main>        <footer class="footer">底部</footer>    </div>

HTML5新增网页元素

标签 说明
audio 定义音频,如音乐或其他音频流
video 定义视频,如电影片段或其他视频流
canvas 定义图形
datalist 定义可选数据的列表
time 标签定义日期或时间
mark 在视觉上向用户呈现那些需要突出的文字

音频播放

<audio src="路径"> </audio>

属性 说明
controls controls 如果出现该属性,则向用户显示控件,比如播放按钮
autoplay autoplay 如果出现该属性,则音频在就绪后马上播放
loop loop 如果出现该属性,则当音频结束时重新开始播放
preload auto/meta/none 如果出现该属性,则音频在页面加载时进行加载,并预备播放
<h2>在html5中播放音频:</h2> <audio controls="controls">       <source src="vedio/song.ogg"/>       <source src="vedio/song.mp3"/>         您的浏览器不支持audio元素播放的音频</audio>

视频播放

  • <video src="路径"> </video>
属性 说明
controls controls 如果出现该属性,则向用户显示控件,比如播放按钮
autoplay autoplay 如果出现该属性,则视频在就绪后马上播放
loop loop 如果出现该属性,则当视频结束时重新开始播放
preload auto 如果出现该属性,则视频在页面加载时进行加载,并预备播放
width/height length(px) 设置视频播放器的宽度/高度
<h2>在html5中播放音频:</h2> <video controls="controls">      <source src="video/video.mp4"/>      <source src="video/video.ogg"/>      <source src="video/video.webm"/>     您的浏览器不支持video播放的视频</video>

source元素

  • audio、video元素允许多个source元素
  • source可链接不同的音频、视频文件

Canvas概念

  • HTML5的Canvas元素使用JavaScript在网页上绘制图像
  • 画布是一个矩形区域,可以控制其每一个像素
  • Canvas拥有多种绘制矩形、路径、圆形、字符以及添加图像的方法

HTML5新增全局属性

标签 说明
contentEditable 规定是否允许用户编辑内容
designMode 规定整个页面是否可编辑
hidden 规定对元素进行隐藏
spellcheck 规定是否必须对元素进行拼写或语法检查
time 标签定义日期或时间
tabindex 规定元素的tab键迭制次序

HTML5新增input类型

类型 说明
email 电子邮件地址文本框,提交表单时会自动验证email的值
url 网页的URL,提交表单时会自动验证url的值
color 主要用于选取颜色
search 用于搜索引擎(搜索框)
number 只包含数值的字段,能够设定对所接受的数字的限定
range 滑动条,特定值范围内的数值选择器
Date pickers 拥有多个可供选取日期的新输入类型

表单初级验证方法

  • placeholder 字段提示
  • required 必须填写字段
  • pattern 正则表达式,输入内容必须符合正则规则

CSS点击展开折叠效果

<label class="drop" for="_1">Collapse 1 </label><input id="_1" type="checkbox"> <div>Content 1</div>
.drop {  cursor: pointer;  display: block;  background: #090;}.drop + input{ display: none; /* hide the checkboxes */}.drop + input + div{  display:none;}.drop + input:checked + div{  display:block;}

CSS( less 动态设置样式 )

  • 包括对字体、颜色、边距、高度、宽度、背景图片、网页定位等设定
  • 由选择器和声明构成一条完整的css语句

HTML中引入CSS样式

行内样式
  • html 代码标签中直接写上 style 属性
<h1 style="color:red; font-size: large;">一级标题</h1>
内部样式
  • CSS 代码写在 <header></header> 中的 <style></style> 里面
外部样式

​ css 代码保存在外部通过代码导入到 html 代码中

  • 链接式 <link href="style.css" rel="stylesheet" type="text/css" />"
  • 导入式 @import url("style.css");

CSS样式优先级

  • 行内样式 > 内部样式表 > 外部样式表
  • 就近原则:后面写的属性会覆盖前面设置的属性

CSS基本选择器

标签选择器
  • 设置一个标签属性所有同名标签的属性值都会随之改变
  • 所有html标签都能作为标签选择器被选中
类选择器
  • 每个标签都可以有多个class,可以通过给不同类名设置属性赋给同一个标签
  • 引用class一定要加.
  • class命名:只能由字符、数字、下划线组成,且不能以数字开头,更不能是html关键字如p,a,img等
ID选择器
  • 在企业开发中如果仅仅只是为了设置样式,通常不会使用id,在前端开发中id通常是留给js使用的
  • id选择器命名规范与类选择器一致
  • 每个标签仅可设置唯一一个id
基本选择器的优先级
  • ID选择器 > 类选择器 > 标签选择器

层次选择器

并集选择器
  • .one, .two{color:red;} 类名用逗号隔开
后代选择器
  • 通过父代限定范围;先查找父类标签再查后代
  • body div{background: red;} 父代与后代之间用空格隔开
子选择器
  • body>div{background: red;} 父代与子代之间用大于号连接
  • 只限定子代的标签,不限定更多后代
相邻兄弟选择器
  • .active+p{background: green;} 选择相邻弟辈标签进行对应属性赋值
通用兄弟选择器
  • .active~p{background: green;} 改变所有弟辈的属性值

属性选择器

  • a[id]{background: yellow;} 带 id 属性的 a 标签改变样式
  • a[id=first]{ background: red;} id 属性值为 first 的 a 标签改变样式
  • a[href^=http] { background: green;} 属性 href 里以 http 开头的元素改变样式
  • a[href$=pdf] { background: red;} 属性 href 里以 pdf 结尾的元素改变样式
  • a[href*=pdf] { background: red;} 属性 href 里包含 pdf 的元素改变样式
  • body p:nth-of-type(2){background: yellow;} 伪类选择器,body父元素里第二个类型为p的孩子元素

span标签

  • 让某几个文字或者某个词语凸显出来
  • span 标签 title 属性:鼠标悬停在span标签上会显示title值

字体、文本样式

字体样式
属性 含义 举例
font-family 设置字体类型 font-family:"隶书";
font-size 设置字体大小 font-size:12px;
font-style 设置字体风格 font-style:italic;
font-weight 设置字体的粗细 font-weight:bold;
font 在一个声明中设置所有字体属性 font:italic bold 36px "宋体";
  • em 相对单位,以父亲字体的大小为参照
  • rem 相对单位,以根元素字体大小为参照
文本样式
属性 含义 举例
color 设置文本颜色 color:#00C;
text-align 设置元素水平对齐方式 text-align:right;
text-indent 设置首行文本的缩进 text-indent:20px;
line-height 设置文本的行高 line-height:25px;
text-decoration 设置文本的装饰 text-decoration:underline;
文本颜色
  • 十六进制表示颜色:前两位表示红色分量,中间两位表示绿色分量,最后两位表示蓝色分量
  • rgb(r,g,b):正整数的取值为0~255
  • RGBA:在RGB基础上增加了控制alpha透明度的参数,取值范围为0~1
文本段落设置
  • 水平对齐方式:text-align:
  • 首行缩进:text-indent:em 或 px
  • 行高:line-height:px
水平对齐方式 text-align
说明
left 把文本排列到左边。默认值:由浏览器决定
right 把文本排列到右边
center 把文本排列到中间
justify 实现两端对齐文本效果
文本装饰
  • text-decoration属性
说明
none 默认值,定义的标准文本
underline 设置文本的下划线
overline 设置文本的上划线
line-through 设置文本的删除线
垂直对齐
  • vertical-align属性:middle、top、bottom 图片与文本垂直居中对齐

超链接伪类

  • 伪类样式
a:hover{    color:#B46210;    text-decoration:underline;}
  • 使用css设置伪类
伪类名称 含义 示例
a:link 未单击访问时超链接样式 a:link{color:#9ef5f9;}
a:visited 单击访问后超链接样式 a:visited {color:#333;}
a:hover 鼠标悬浮其上的超链接样式 a:hover{color:#ff7300;}
a:active 鼠标单击未释放的超链接样式 a:active {color:#999;}

列表样式

  • 清除无无序列表前面的小黑点:list-style:none;

盒子模型

  • 用于封装HTML元素
  • 它包括:外边距(margin),边框(border),填充(padding)和实际内容(contet)
  • 默认没有填充物,盒子里面的填充物不会影响盒子呈现的尺寸
  • 边框线占实际尺寸
  • 内边距会影响盒子里面放标签的数量
  • 内外看的角度不同,内:从父往子看;外:从子往父看。
  • 外边距会占空间,标签实际呈现的时候的尺寸
边框
  • 边框颜色:border-color

  • 边框粗细:border-width

  • 边框样式:border-style

    • dotted:点状;solid:实线;double:双线;dashed:虚线
  • border简写: border:width style color;

外边距
  • margin是指从自身边bai框到另一du个容器zhi边框之间的距离,dao就是容器外距离
  • margin有四个值,分别表示上右下左的值;三个值的时候分别表示上,左右,下
  • margin也可以分别给 margin-top、margin-left、margin-bottom、margin-right 赋值
  • css中经常使用 *{margin:0; padding:0;}清除所有标签的边距
  • 也可以使用 margin:0px auto;来使得网页居中对齐
内边距
  • padding是指自身边bai框到自身内部另一个容器边框之间的距离,就是容器内距离
  • padding取值和 margin 取法一致

display

  • 行级标签设置高宽不起作用
  • sblock 可以把行级元素变成块级元素
  • inline 可以把块级元素变成行级元素
  • inline-block 使标签具有行级和块级的特征,把块的独占一行特征取消了

浮动-float属性

属性值 说明
left 元素向左浮动
right 元素向右浮动
none 默认值,元素不浮动
  • 浮动元素遇到障碍物则不浮动
  • 浮动元素的位置会被其他元素占用
  • 浮动的元素脱离文档流的标准布局格式
  • 如果后面有不浮动的元素,那么这些不浮动的元素还按照标准文档流的格式布局
  • 非浮动块级元素跟在浮动元素后边且在定位后产生重叠时,块级元素边框和背景在浮动元素之下显示,只有内容在浮动元素“之上”显示,如果内容显示不出来,找个其他的地方显示
  • 浮动元素后边的元素若是非浮动行内元素且因为定位产生重叠时,行内元素边框、背景和内容都在该浮动元素“之上”显示。
清除浮动
  • 不清除浮动会导致边框塌陷
  • 使用 clear:both; 清除浮动

定位(position)

属性值 说明
static 默认值,没有定位
relative 相对定位
absolute 绝对定位
fixed 固定定位
相对定位(relative)
  • 相对自身原来位置进行偏移
  • 设置了相对定位的元素初始位置会被保留不会被其他元素占用
  • 偏移位置设置:top、left、right、bottom
  • 优先级:top > bottom ; left > right
  • 可以把标准流和浮动元素覆盖在下面
绝对定位(absolute)
  • 以父类元素为参照,否则以浏览器窗口为参照
  • 绝对定位的元素设置参照父级需要设置相对定位,但是不能设置偏移
  • 设置绝对定位的元素位置会被其他元素占用
  • 当参照对象是浏览器时,以整个页面做定位
固定定位(fixed)
  • 偏移设置:top、right、bottom、left
  • 类似于绝对定位,不过区别在于定位的基准不是祖先元素,而是浏览器窗口
  • 以当前浏览器视口做参照

CSS3基础及动画

下拉菜单实现

<!DOCTYPE html><html><head><title>下拉菜单实例|菜鸟教程(runoob.com)</title><meta charset="utf-8"><style>.dropdown {  position: relative;  display: inline-block;}.dropdown-content {  display: none;  position: absolute;  background-color: #f9f9f9;  min-width: 160px;  box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);  padding: 12px 16px;}.dropdown:hover .dropdown-content {  display: block;}</style></head><body><h2>鼠标移动后出现下拉菜单</h2><p>将鼠标移动到指定元素上就能看到下拉菜单。</p><div class="dropdown">  <span>鼠标移动到我这!</span>  <div class="dropdown-content">    <p>菜鸟教程</p>    <p>www.runoob.com</p>  </div></div></body></html>

css边框

border-radius 圆角边框
  • border-radius: 20px 10px 50px 30px; 分别设置四个值,左上右上右下左下
  • 圆角值也能设置百分比大小,当值为50%时会呈现一个圆形,需要正方形
box-shadow 盒子阴影
box-shadow:(	h-shadow  /* 水平阴影的位置 */	v-shadow  /* 垂直阴影的位置 */	blur 	  /* 模糊距离 */	spread 	  /* 阴影的大小 */	color 	  /* 阴影的颜色 */	inset;	  /* 阴影类型内阴影 */);

背景

background-size 背景图片的尺寸
  • length 设置图片高度和宽度,第一个值是宽度,第二个是高度;如果只有一个值,第二个值默认为 auto
  • percentage 计算相对于背景定位区域的百分比,第一个值是宽度,第二个是高度;如果只有一个值,第二个值默认为 auto
  • cover 保持图像的纵横比并将图片缩放成完全覆盖背景定位区域的最小大小
  • contain 保持图像的纵横比并将图像缩放成将适合背景定位区域的最大大小
background-origin 背景图片的区域
background-clip 背景图片的绘制区域
background-origin值说明 background-clip值说明
padding-box 背景图像相对于内边距框来定位 背景被裁剪到内边距框
border-box 背景图像相对于边框盒来定位 背景被裁剪到边框盒
content-box 背景图像相对于内容框来定位 背景被裁剪到内容框

z-index

z-index:num; num值越大,优先级越高,就会显示在最上面

不透明度设置

  • opacity: num; num值在0~1之间,默认为1即完全不透明 filter: alpha(opacity=50); 兼容IE
  • color: #00000050; 直接在16进制颜色后面加数值,数值取0~100之间的十位整数

渐变

线性渐变—— Linear Gradients
  • 颜色沿直线过度:从上至下、从左至右等

    linear-gradient(    position, /* 渐变的方向;默认从上至下;to right就是从左至右 */    color1,/* 第一种颜色 */    color2,/* 第二种颜色 */    ……);
    
径向渐变—— Radial Gradients
  • 圆形或椭圆形渐变,颜色从一个起点朝所有方向混合

    radial-gradient(    start-color, …… last-color);
    

文本效果

text-shadow 属性
说明
h-shadow 必需,水平阴影的位置,允许负值
v-shadow 必需,垂直阴影的位置,允许负值
blur 可选,模糊距离
color 可选,阴影的颜色
text-overflow 属性
  • white-space:nowrap 文本不会换行,在同一行继续
  • overflow:hidden 溢出隐藏
  • text-overflow:ellipsis 用省略号来代表被修剪的文本

艺术字体

@font-face {          font-family: 必需。规定字体的名称          src: 必需。定义字体文件的 URL          font-weight: 可选。定义字体的粗细。默认是 "normal"          font-style: 可选。定义字体的样式。默认是 "normal"}
<style>		@font-face{			font-family: myfont;			src: url(fonts/shimesone_personal-webfont.eot);            src: url(fonts/shimesone_personal-webfont.svg);            src: url(fonts/shimesone_personal-webfont.ttf);            src: url(fonts/shimesone_personal-webfont.woff);            font-weight: normal;            font-style: normal;		}		h1{			font-family: myfont;			font-size: 80px;		}</style><body>	<h1>This is a long text</h1></body>

CSS3变形

  • 变形是一些效果的集合,如:平移、旋转、缩放、倾斜等

    transform:	transform-function;  /* 变形函数 可以是多个 中间以空格隔开 */
    
变形函数
  • translate():平移函数,基于X,Y坐标重新定位元素的位置
  • scale():缩放函数,可以使任意元素对象尺寸发生变化
  • rotate():旋转函数,取值是一个度数值(deg)
  • skew():倾斜函数,取值是度数值
2D位移(translate)
translate(tx,  /* X轴,横坐标的位移长度 */	ty); /* 纵坐标的位移长度 */ 
  • 单方向位移 translate(0, 100px) 只移动Y轴坐标
2D缩放(scale)
  • scale(sx, sy);
  • sanle 函数可以只接收一个值,只有一个值时第二个值默认与第一个值相等
2D倾斜(skew)
  • skew(sx, sy);
  • 可以仅设置一个值,表示只有 X或者Y 倾斜
2D旋转
  • rotate(a) 参数 a 单位使用 deg 表示
  • 参数 a 取正值时,元素相对原位置顺时针旋转
  • div:hover{transform:rotate(20deg) ;}
  • animation语法
  • img

CSS3过渡

transition 呈现的时一种动画转换过程,如渐现、渐弱、动画快慢等。 transition 通过一些CSS的简单动作触发样式 平滑过渡

transition: 	transition-property /* 过渡或动态模拟的css属性 */	transition-duration /* 完成过渡需要的时间 */	transition-timing-function /* 指定过度函数 */	transition-delay /* 过渡开始出现的延迟时间 */
/* 示例 */div{	background-color: red;	width: 200px;	height: 200px;	transition: background-color 3s ease-in-out;}div:hover{	background-color: yellow;}
过渡属性(transition-property)
  • 定义转换动画的CSS属性名称
  • 指定的CSS属性:width、height、background-color 等属性
  • 一般为了方便都会使用 all
过渡所需的时间(transition-duration)
  • 定义转换动画的时间长度,即从旧属性转换到新属性需要的时间
  • 单位为秒 (S)
过渡动画函数(transition-timing-function)
  • 指定浏览器的过渡速度,以及过渡周期的操作进展情况

  • 通过给过渡添加一个函数来指定动画的快慢方式

说明
linear 规定以相同速度开始至结束的过渡效果
ease 规定慢速开始,然后变快,然后慢速结束的过渡效果(默认值)
ease-in 规定以慢速开始的过渡效果
ease-out 规定以慢速结束的过渡效果
ease-in-out 规定以慢速开始和结束的过渡效果
过渡延迟时间(transition-delay)
  • 指定一个动画开始执行的时间,当改变元素属性值后多长时间去执行过渡效果
  • 正值:元素过渡效果不会立即触发,当过了设置事件后才触发
  • 负值:元素过渡效果从该时间点开始显示,之前的动作被截断
  • 0:默认值,元素过渡效果立即执行
过渡的触发机制
  • 伪类触发::hover,    :active,    :focus,    :checked

  • 媒体查询:通过 @media 属性判断设备尺寸、方向等

  • JavaScript触发:通过 JavaScript 脚本触发

CSS3动画


JavaScript(typescript)

JavaScript基础

JavaScript 是一种基于对象和事件驱动的、具有安全性能的脚本语言
JavaScript 的特点

向HTML页面添加交互行为
脚本语言,语法和Java类似
解释性语言,边执行边解释

JavaScript组成:ECMAScript + DOM + BOM

JavaScript基本结构

<script type="text/javascript"/>    执行语句;</script>

JavaScript的应用

<script type="text/javascript">	document.write("hello, world");    </script>
  • document.whrite() 起到输出数据的作用
  • <script>...</script> 可以包含在文档的任何地方,只要保证这些代码在被使用前已读取并加载到内存即可

网页中引用JavaScript文件的方式

  • 使用 <script>标签引入,放在网页标签后,等待网页标签加载完成再加载JS
  • 外部 JS 文件 <script src="export.js type="text/javascript></script>;外部文件不能包含<script>标签
  • 直接在 HTML 标签中写
<input name="btn" type="button" value="清除缓存"  onclick="javascript:alter("清除成功");"/>

JavaScript 核心语法

变量

变量就是一个助记符号,帮助我们记忆计算机内存地址

js的数据类型由变量当前值进行判断(弱类型)

  • 变量名区分大小写

先声明变量在赋值 var width; width = 5;

var - 用于声明变量的关键字;width - 变量名

同时声明和赋值变量

var catName="皮皮"; var x,y,z = 10;

不声明直接赋值

不声明直接赋值容易出错,且不易排查,不推荐使用

JavaScript 区分大小写,特别是变量的命名、语句关键字等

数据类型
  • undefined var width; 变量width没有初始值,将被赋予值undefined
  • null 表示一个空值,与undefined值相等
  • number var iNum = 23;整数 var iNum="23.0";浮点数
  • boolean 布尔值,true和false
  • string 一组被引号(单、双引号)括起来的文本, var string1="this is a string";
typeof运算符

typeof 运算符返回值如下

undefined:变量被声明后,但未被赋值

string:用单引号或双引号来声明的字符串

Boolean:true 或false

number:整数或浮点数

object:JavaScript 中的对象、数组和null

运算符
  • += 、-=、*=、/= 赋值运算符

  • == 和 === 双等只要求相等,三等号需要

  • 用于一般比较,=用于严格比较,在比较的时候可以转换数据类型,
    =严格比较,只要类型不匹配就返回false。
    举例说明:
    "1" == true
    类型不同,"
    "将先做类型转换,把true转换为1,即为 "1" == 1;
    此时,类型仍不同,继续进行类型转换,把"1"转换为1,即为 1 == 1;
    此时,"
    " 左右两边的类型都为数值型,比较成功!
    如果比较:"1" === true 左侧为字符型,右侧为bool布尔型,
    左右两侧类型不同,结果为false;
    如果比较:"1" === 1 左侧为字符型,右侧为int数值型,
    左右两侧类型不同,结果为false;
    如果比较: 1 === 1 左侧为int数值型,右侧为int数值型,
    左右两侧类型相同,数值大小也相同,结果为true;
    如果比较: 1 === 2 左侧为int数值型,右侧为int数值型,
    左右两侧类型相同,但数值大小不同,结果为false;
    简而言之就是 "" 只要求值相等; "=" 要求值和类型都相等

数值交换
  • 位运算

    var num1 = 12, num2 = 13;num1 = num1 ^ num2;num2 = num1 ^ num2;num1 = num1 ^ num2;alert(num1 +"_"+ num2);
    
string 对象

字符串对象.方法名();

lenght 属性:返回字符串的长度;

var str="hello"; document.write(str.lenght); 返回 5

charAt(index):返回在指定位置的字符

var str="hello"; document.write(str.charAT(2)); 返回 e

indexOf(str, index):自 index 后查找 str 首次出现的位置;如果字符串不存在则返回 -1

var str="hello"; document.write(str.indexOf("e")); 返回1

substring(index1, index2):返回位于index1和index2之间的字符串,包含index1,不包含index2

var str="hello"; document.write(1,4); 返回 ell

split(str):将字符串分割为字符串数组

var string1="hello"; document.write(string1.split("")); 返回 h,e,l,l,o

str.substring()方法:提取字符串介于指定下标之间的字符

let str="0123";str = str.substring(str.length,1); 输出 123

数组
  • 数组具有地址连续性的特征
  • 数组下标不存在越界的问题,数组本身会动态扩容,值为undefined
  • 同一个数组里面的元素类型可以是不相同的
创建数组
  • var Aname = new Array(size);
  • Array 表示数组的关键字; size 表示数组中可存放的元素总数
  • var arr1= [1,2,3,4];
为数据元素赋值
  • 直接赋值 var fruit= new Array("apple", "orange", "peach","banana");

  • <script>	var fruit = new Array(4);	fruit [0] = " apple ";	fruit [1] = " orange ";	fruit [2] = " peach ";	fruit [3] = " banana ";   </script>
    
访问数组
  • 数组名[下标]
  • document.write(fruit[0]) 返回 apple
数组操作
  • 修改数组内容 var arr= [1,2,3]; arr[1]= 5;

  • 删除数组元素 splice(index, num, "替换值")

    var arr = ['a','b','a','b','c','a','d','d'];	var j = 0;	while (j < arr.length){		for(var i = j + 1; i < arr.length; i++){			if(arr[j] === arr[i]){				arr.splice(i,1);			}		}		j++;	}
    
数组的常用属性和方法
  • length 属性 设置或返回数组中元素的数量

方法

join():把数组的所有元素放入一个字符串,通过一个分隔符进行分隔

<script>  var arr1=[1,2,3,4];  alert(arr1.join("·"));</script>

sort():对数组排序

pop():删除数组末尾的元素

push():向数组末尾添加一个或多个元素,并返回新的长度

<script>      var arr1=[5,9,3,4];  alert(arr1.push("0")); //返回 5  alert(arr1) //返回5,9,3,4,0</script>
逻辑控制语句
if条件语句
  • 第一次遇到条件成立,就立即执行
  • 前面条件成立,后面的条件就不会继续判断,直接终止选择
switch多分支语句
var i= 1;        switch(i){            case 1:                alert("1");                break;            case 10:                alert("10");                break;            default:                alert("None");}
switch (operator){	case "-":		var sum = (num0 - num1);		break;	case "+":		var sum = parseInt(num0) + parseInt(num1);        //强制类型转换,在获取到值后乘一再计算也可以做到强制类型转换		break;	case "*":		var sum = (num0 * num1);		break;	case "/":		var sum = (num0 / num1);		break;	default:  //可以不带后续内容		break;}
  • 等值判断
  • 每条语句结束都要加上 break终止循环 ,不加就会依次执行
  • default语句 在所有条件都不满足的时候默认执行
for循环
  • for(初始化; 条件; 增量) 三者可以随机组合,若不带则是死循环
while循环语句
var i = 0;        while(i < 5){        alert(i);        i++;}
for-in
var fruit = ["apple", "orange", "peach", "banana"];		for(var i in fruit){			document.write(fruit[i] + "<br/>");		}
循环中断
break
  • 完全终止循环
continue
  • 终止本次循环,继续下一轮循环
注释
  • 单行注释 //以双斜线开始,行末自动结束
  • 多行注释 /* 里面的内容不被解析 */
常用的输入/输出

alert():在浏览器页面弹窗输出

console.log():浏览器控制台输出

prompt();弹窗显示输入框

prompt("提示信息", "输入框默认展示信息")

var year= prompt("请输入年份");alert(year);

程序调试

  • 错误代码定位,在正常运行的代码后加上分割代码
  • 浏览器设置断点

函数

  • 函数的含义:是完成特定任务的代码语句块
  • 使用简单:不用定义属于某个类,直接使用
  • 函数分类:系统函数和自定义函数
常用系统函数
parselnt(“字符串”)
  • 将字符串转换为整型数字 parseInt("99");
  • var str="88"; var num = parseInt(str); num的类型为number
parseFloat("字符串")
  • 将字符串转换为浮点型数字
isNaN()
  • 用于检查其参数是否为数字
自定义函数
  • 定义函数parameter:传入参数,相当于前提,可以有多个也可以不带

    function name(parameter.) //不带parameter是无参函数 {    //javascript 语句    [return返回值]  //可以不要}
    
  • 调用函数

    函数调用一般和表单元素事件一起使用

    调用格式 event_name= "function_name()";

调用无参函数
<form>	    <input type="button" value="点击就做俯卧撑" onclick="study()"/></form><script type="text/javascript">	    function study(){			for (var i= 0; i< 9999;i++){		    	alert(i +"个");			}	    }</script>
调用有参函数
<form>    <input type="button" value="输出"onclick="study(prompt('输入次数:'))"/></form><script>    function study(count){        for(i=0; i<count;i++){            alert(i);        }    }</script>

变量的作用域

var i = 20; //全局变量    function first(){      var i = 5;  //局部变量      for(var j = 0; j < i; j++){          document.write("&nbsp;&nbsp;&nbsp;&nbsp;" + j);      }    }     function second(){        var t = prompt("请输入一个数:", '');        if(t > i)             document.write(t);        else             document.write(i);        	 first();      }
全局变量
局部变量
  • 如果出现全局和局部变量同名,在函数里局部变量优先使用
  • 局部变量只能在本函数内使用

事件

  • 事件就是用户对网页或浏览器做出的动作
名称 说明
onload 一个页面或一幅图像完成加载
onlick 鼠标单击某个对象
onmouseover 鼠标指导移到某元素上
onkeydown 某个键盘按键被按下
onchange 域的内容被改变
onmouseout 鼠标从某元素上移开
<input type="button" value="按钮0" onclick="alert('onclick')"/><br/><input type="button" value="按钮1" onmouseover="alert('onmouseover')"/><br/> <input type="button" value="按钮2" onmouseout="alert('onmouseout')"/><br/><input type="button" value="按钮3" onmousedown="alert('onmousedown')"/><br/>
  • 键盘按键事件,输出可打印字符。
<input type="text" onkeydown='fn()'/><br/><input type="text" onkeyup='fn()'/><br/><input type="text" onkeypress='fn()'/><br/><script>   function fn(){       alert(event.key);       alert(event.keyCode);   }</script>
  • 焦点事件
<input type="text" value="change" onchange="fn1()"/> //内容发生改变并丢失焦点时触发<input type="text" value="foucus" onfocus="fn2()"/> //获取到焦点时触发<input type="text" value="blur" onblur="fn3()"> //焦点丢失后触发<script>    function fn1(){        alert('change');    }    function fn2(){        alert('focus');    }    function fn3(){        alert('blur');    }</script>

JavaScript操作BOM对象

BOM模型

image-20210323132817777

浏览器对象模型
BOM可实现功能
  • 弹出新的浏览器窗口
  • 移动、关闭浏览器窗口以及调整窗口大小
  • 页面的前进、后退

window对象

控制窗口,通过操作对象上的属性和方法,达到控制窗口对象的目的

window.属性名 = "属性值";

  • 常用属性

history: 有关客户访问过的URL

window.history.back(); //返回上一级页面
history常用方法

back() 加载history对象前一个URL链接地址

forward() 加载history对象下一个URL链接地址

go() 加载history对象中的某个具体的URL链接地址

history.back() === history.go(-1);

history.forward() === history.go(1);

location:有关当前URL的信息

window.location = "http://www.baidu.com",'_self';//'_self'  在当前页面打开,不带则是跳转到一个新窗口
location常用属性

host 设置或返回主机名和当前URL的端口号

hostname 设置或返回当前URL的主机名

href 设置或返回完整的URL

var href = window.location.href;
location常用方法

reload() 重新加载当前文档

replace() 用新的文档替换当前文档

location和history对象的应用
<a href="javascript:location.href='flower.html'">查看鲜花详情</a>  <a href="javascript:location.reload()">刷新本页</a><a href="javascript:history.back()">返回主页面</a>//方法前面带上 javascript:提示浏览器
  • 常用方法
方法名称 说 明
prompt( ) 显示可提示用户输入的对话框
alert( ) 显示带有一个提示信息和一个确定按钮的警示框
confirm( ) **显示一个带有提示信息、确定和取消按钮的对话框 **
close( ) 关闭浏览器窗口
open( ) 打开一个新的浏览器窗口,加载给定 URL 所指定的文档
setTimeout( ) 在指定的毫秒数后调用函数或计算表达式
setInterval( ) 按照指定的周期(以毫秒计)来调用函数或表达式

confirm()方法弹出一个确认对话框

confirm() 与 alert()、prompt() 的区别

alert():一个参数,仅显示警告对话框的消息,无返回值,不能对脚本产生任何改变

prompt():两个参数,输入对话框,用来提示用户输入一些信息,单击 取消 按钮则返回null,单击 确定 按钮则返回用户输入的值,常用于收集用户关于特定问题而反馈的信息。

confirm():一个参数,确认对话框,显示提示对话框的消息、确定 按钮和 取消 按钮,单击 确定 按钮返回 true,单击 取消 按钮返回 false,因此与 if-else 语句搭配使用

setTimeout()方法 在规定时间后执行某个操作就停止

window.setTimeout('alert("你等了两秒")',2000); //输出一次:你等了两秒

setInterval()方法 会一直按照时间周期循环

time = window.setTimeout('alert("你等了两秒")',2000); //每两秒输出一次:你等了两秒clearInterval(time); //停止循环

Document对象

常用属性
名称 说 明
referrer (document.referrer) 返回载入当前文档的URL
URL (document.URL) **返回当前文档的URL **
Document对象的常用方法
名称 说 明
getElementById() 返回对拥有指定id的第一个对象的引用 (对象的ID唯一性)
getElementsByName() 返回带有指定名称的对象的集合 (相同name属性)
getElementsByTagName() 返回带有指定标签名的对象的集合 (相同的元素)
write() 向文档写文本、HTML表达式或JavaScript代码
动态改变层、标签中的内容
document.getElementById("book").innerHTML="现象级全球畅销书";// innerHTML 替换原标签中的内容
document.querySelector()方法
  • queryselector()方法只会返回匹配选择器的第一个元素
  • 获取文档中 id = "demo" 的元素:document.querySelector("#demo");
document.querySelectorAll()方法
  • 返回文档中匹配指定的所有元素,返回NodeList对象
  • 返回的数组可以通过下标访问,通过获取下标实现元素调用

JavaScript内置对象

  • Array:用于在单独的变量名中存储一系列的值
  • String:用于支持对字符串的处理
  • Math:用于执行常用的数字任务,它包含了若干个数字常量和函数
  • Date:用于操作日期和时间
Date对象
  • var日期对象 = new Date();

    • var  today=new Date();   //返回当前日期和时间var tdate=new Date("september 1,2013,14:58:12");
      
常用方法
方法 说 明
getDate() 返回 Date 对象的一个月中的每一天,其值介于1~31之间
getDay() 返回 Date 对象的星期中的每一天,其值介于0~6之间
getHours() 返回 Date 对象的小时数,其值介于0~23之间
getMinutes() 返回 Date 对象的分钟数,其值介于0~59之间
getSeconds() 返回 Date 对象的秒数,其值介于0~59之间
getMonth() 返回 Date 对象的月份,其值介于0~11之间
getFullYear() 返回 Date对象的年份,其值为4位数
getTime() 返回自某一时刻(1970年1月1日)以来的毫秒数
Math对象
常用方法
  • 四舍五入方法:fiexed(2) 取小数点后两位
  • 生成一个 x-y的随机数: Math.round(Math.random()*(y-x)+x);
方法 说 明 示例
ceil() 对数进行上舍入 Math.ceil(25.5);返回26 Math.ceil(-25.5);返回-25
floor() 对数进行下舍入 Math.floor(25.5);返回25 Math.floor(-25.5);返回-26
round() 把数四舍五入为最接近的数 Math.round(25.5);返回26 Math.round(-25.5);返回-26
random() 返回0~1之间的随机数 Math.random();例如:0.6273608814137365
  • 实现整数返回范围为 2~99

  • var iNum=Math.floor(Math.random()*98+2);
    
定时函数

setTimeout()

setTimeout("调用的函数",等待的毫秒数);var myTime=setTimeout("disptime() ", 1000 ); //1秒,1000ms后执行

setInterval()

setInterval("调用的函数",间隔的毫秒数)var  myTime=setInterval("disptime() ", 1000 );//如果要多次调用,使用setInterval()或者让disptime()自身再次调用setTimeout()

清除函数

clearTimeout()

//clearTimeout(setTimeOut()返回的ID值)var  myTime=setTimeout("disptime() ", 1000 );clearTimeout(myTime);

clearInterval()

//clearInterval(setInterval()返回的ID值)var  myTime=setInterval("disptime() ", 1000 );clearInterval(myTime)

总结

image-20210328224523181

JavaScript操作DOM对象

访问节点

  • 使用getElementBy系列方法访问
    • ngetElementById()、getElementsByName()、getElementsByTagName()
  • 根据层次关系访问节点
  • 根据querySelector || querySelectorAll访问
根据层次关系访问节点
节点属性
parentNode 返回节点的父节点
childNodes 返回子节点集合,childNodes[i]
firstChild 返回节点的第一个子节点,最普遍的用法是访问该元素的文本节点
lastChild 返回节点的最后一个子节点
nextSibling 下一个节点
previousSibling 上一个节点
Element属性
属性名称 描述
firstElementChild 返回节点的第一个子节点,最普遍的用法是访问该元素的文本节点
lastElementChild 返回节点的最后一个子节点
nextElementSibling 下一个节点
previousElementSibling 上一个节点
oNext = oParent.nextElementSibling || oParent.nextSibling   oPre = oParent.previousElementSibling || oParent.previousSibling  oFirst = oParent. firstElementChild  ||  oParent.firstChild   oLast = oParent.lastElementChild || oParent.lastChild 
节点信息
  • nodeName:节点名称
  • nodeValue:节点值
  • nodeType:节点类型

操作节点

javascript事件
名称 描述
onclick 当用户单击某个对象时调用事件
onmouseover 鼠标移到某元素之上
onmouseout 鼠标从某元素移开
onmousedown 鼠标按钮被按下
操作节点属性
  • getAttribute("属性名");

  • setAttribute("属性名", "属性值");

    • img.setAttribute("src", "images/dog.jpg");img.src= "images/mai.jpg";  //不使用 setAttribute 操作属性;
      
  • 附加属性: 添加标签中不存在的属性,方便编程调用

    • img.aaa = "aaa";img.setAttribute("src", "images/dog.jpg");alert(document.getElementById("image").aaa); //访问添加的属性
      
  • 复制节点:cloneNode 的值默认为 false,只复制子节点;为true时复制内部所有节点以及属性值

    • function copyNode(){	var bName = document.getElementsByTagName("div")[0];	var copy = bName.lastChild.cloneNode(false);	bName.insertBefore(copy, bName.firstChild);}
      
创建和插入节点
名称 描述
createElement( tagName) 创建一个标签名为tagName的新元素节点
A.appendChild( B) 把B节点追加至A节点的末尾
insertBefore( A,B ) 把A节点插入到B节点之前
cloneNode(deep) 复制某个指定的节点
var img = document.createElement("img");img.setAttribute("src", "images/dog.jpg"); //可以直接写:img.src="";img.setAttribute("alt", "狗和我一起活下来");//information.appendChild(infor);//informations.insertBefore(information, informations.childNodes[0]);
删除和替换节点
名称 描述
removeChild( node) 删除指定的节点
replaceChild( newNode, oldNode)属性attr 用其他的节点替换指定的节点
var delNode=document.getElementById("first");delNode.parentNode.removeChild(delNode);var oldNode=document.getElementById("second");var newNode=document.createElement("img");newNode.setAttribute("src","images/f03.jpg");oldNode.parentNode.replaceChild(newNode,oldNode);
操作节点样式
style属性
  • HTML元素.style.样式属性="值"
document.getElementById("titles").style.color="#ff0000"; document.getElementById("titles").style.fontSize="25px ";//在设置样式时,需要中线拼接的单词改为小驼峰写法即可
className属性
  • HTML元素.className="样式名称"
function over(){    document.getElementById("cart").className="cartOver";    document.getElementById("cartList").className="cartListOver";}function out(){    document.getElementById("cart").className="cartOut";    document.getElementById("cartList").className="cartListOut";}
获取元素样式
  • HTML元素.style.样式属性;
alert(document.getElementById("cartList").display);document.defaultView.getComputedStyle(元素,null).属性;var cartList=document.getElementById("cartList");alert(document.defaultView.getComputedStyle(cartList,null).display);//HTML元素. currentStyle.样式属性;alert(document.getElementById("cartList").currentStyle.display);//getComputedStyle()不支持IE;currentStyle支持IE

获取元素位置

HTML中元素属性
属性 描述
offsetLeft 返回当前元素左边界到它上级元素的左边界的距离,只读属性
offsetTop 返回当前元素上边界到它上级元素的上边界的距离,只读属性
offsetHeight 返回元素的高度
offsetWidth 返回元素的宽度
offsetParent 返回元素的偏移容器,即对最近的动态定位的包含元素的引用
scrollTop 返回匹配元素的滚动条的垂直位置
scrollLeft 返回匹配元素的滚动条的水平位置
clientWidth 返回元素的可见宽度
clientHeight 返回元素的可见高度
//标准浏览器document.documentElement.scrollTop;document.documentElement.scrollLeft;//Chromedocument.body.scrollTop;document.body.scrollLeft;var sTop=document.documentElement.scrollTop||document.body.scrollTop;//兼容

总结

image-20210328231239548

ES6

strict 语法

  • 如果一个变量不使用var申明就使用,那么该变量就自动申明为全局变量
  • 启用strict模式:在JavaScript部分头部加上 'use strict'

字符串操作

拼接
var name = "小明";var age = 18;var message0 = "你好" + name +",你今年" + age + "岁了!"//使用 + 号拼接var message1 = `你好,${name}, 你今年${age}岁了!`;// 使用反单引号
  • 字符串可以通过下标获取对应位置的元素,可以通过 length()方法获取字符串长度

  • 字符串内部内容不可以更改

  • 字符串全部大写或小写:str.toUpperCase()str.toLowerCase()

返回指定索引区间单独子串 substring()
var s = 'hello, world's.substring(0, 5); // 从索引0开始到5(不包括5),返回'hello's.substring(7); // 从索引7开始到结束,返回'world'
搜索指定字符出现的位置 indexof()
var s = 'hello, world';s.indexOf('world'); // 返回7s.indexOf('World'); // 没有找到指定的子串,返回-1
全大写 && 全小写
 // toUpperCase() 把一个字符串全部变为大写var s = 'Hello';s.toUpperCase(); // 返回'HELLO' // toLowerCase()把一个字符串全部变为小写:var s = 'Hello';var lower = s.toLowerCase(); // 返回'hello'并赋值给变量lowerlower; // 'hello'

数组

  • 获取元素在数组内的下标;如果数组内没有该元素则返回 -1

  • var arr = [1,2,3,4,5,90]; alert(arr.indexOf(3));

  • 截取 slice()

    • var arr0 = ['a', 'b', 'c', 'd', 'e'];var newArr1 = arr0.slice(0, 3);console.log(newArr1); // 输出第零位到第三位的元素,左闭右开var newArr2 = arr0.slice(3);console.log(newArr2); // 输出第三位开始后面的所有元素
      
  • push()pop()方法

    • push()Array的末尾添加若干元素,pop()则把Array的最后一个元素删除掉:

    • var arr = [1, 2];arr.push('A', 'B'); // 返回Array新的长度: 4arr; // [1, 2, 'A', 'B']arr.pop(); // pop()返回'B'arr; // [1, 2, 'A']arr.pop(); arr.pop(); arr.pop(); // 连续pop 3次arr; // []arr.pop(); // 空数组继续pop不会报错,而是返回undefinedarr; // []
      
  • unshift()shift

    • 如果要往Array的头部添加若干元素,使用unshift()方法,shift()方法则把Array的第一个元素删掉:

    • var arr = [1, 2];arr.unshift('A', 'B'); // 返回Array新的长度: 4arr; // ['A', 'B', 1, 2]arr.shift(); // 'A'arr; // ['B', 1, 2]arr.shift(); arr.shift(); arr.shift(); // 连续shift 3次arr; // []arr.shift(); // 空数组继续shift不会报错,而是返回undefinedarr; // []
      
  • splice()

    • splice()方法是修改Array的“万能方法”,它可以从指定的索引开始删除若干元素,然后再从该位置添加若干元素:

    • var arr = ['Microsoft', 'Apple', 'Yahoo', 'AOL', 'Excite', 'Oracle'];// 从索引2开始删除3个元素,然后再添加两个元素:arr.splice(2, 3, 'Google', 'Facebook'); // 返回删除的元素 ['Yahoo', 'AOL', 'Excite']arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']// 只删除,不添加:arr.splice(2, 2); // ['Google', 'Facebook']arr; // ['Microsoft', 'Apple', 'Oracle']// 只添加,不删除:arr.splice(2, 0, 'Google', 'Facebook'); // 返回[],因为没有删除任何元素arr; // ['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']
      
  • concat()

    • concat()方法把当前的Array和另一个Array连接起来,并返回一个新的Array

    • var arr = ['A', 'B', 'C'];var added = arr.concat([1, 2, 3]);added; // ['A', 'B', 'C', 1, 2, 3]arr; // ['A', 'B', 'C']
      
    • concat()方法并没有修改当前Array,而是返回了一个新的Array

    • 实际上,concat()方法可以接收任意个元素和Array,并且自动把Array拆开,然后全部添加到新的Array里:

    • var arr = ['A', 'B', 'C'];arr.concat(1, 2, [3, 4]); // ['A', 'B', 'C', 1, 2, 3, 4]
      

面向对象

  • JavaScript 的对象是一种无序的几何数据类型

    • var xiaoming = {    name: '小明',    birth: 1990,    school: 'No.1 Middle School',    height: 1.70,    weight: 65,    score: null};// 获取对象的属性xiaoming.name; // '小明'xiaoming.birth; // 1990
      
    • 访问属性是通过.操作符完成的,但这要求属性名必须是一个有效的变量名。如果属性名包含特殊字符,就必须用''括起来;访问这个属性也无法使用.操作符,必须用['xxx']来访问;

    • var xiaohong = {    name: '小红',    'middle-school': 'No.1 Middle School'}; // 访问属性xiaohong['middle-school']; // 'No.1 Middle School'xiaohong['name']; // '小红'xiaohong.name; // '小红'
      
    • 如果访问不存在的值,JavaScript并不会报错,而是返回undefined

给对象添加和删除属性

var xiaoming = {    name: '小明'};xiaoming.age; // undefinedxiaoming.age = 18; // 新增一个age属性xiaoming.age; // 18delete xiaoming.age; // 删除age属性xiaoming.age; // undefineddelete xiaoming['name']; // 删除name属性xiaoming.name; // undefineddelete xiaoming.school; // 删除一个不存在的school属性也不会报错

检测对象是否拥有某一属性,用 in 操作符

var xiaoming = {    name: '小明',    birth: 1990,    school: 'No.1 Middle School',    height: 1.70,    weight: 65,    score: null};'name' in xiaoming; // true'grade' in xiaoming; // false
  • 如果in判断一个属性存在,这个属性不一定是对象自身的,它可能是继承得到的:

  • 因为toString定义在object对象中,而所有对象最终都会在原型链上指向object,所以xiaoming也拥有toString属性。

  • 要判断一个属性是否是自身拥有的,而不是继承得到的,可以用hasOwnProperty()方法:

  • var xiaoming = {    name: '小明'};xiaoming.hasOwnProperty('name'); // truexiaoming.hasOwnProperty('toString'); // false
    

构造函数

function Student(name) {    this.name = name;    this.hello = function () {        alert('Hello, ' + this.name + '!');    }}var xiaoming = new Student('小明');xiaoming.name; // '小明'xiaoming.hello(); // Hello, 小明!
  • 如果不写new,这就是一个普通函数,它返回undefined。但是,如果写了new,它就变成了一个构造函数,它绑定的this指向新创建的对象,并默认返回this,也就是说,不需要在最后写return this;

原型链

image-20210402084911291

原型继承
  • JavaScript由于采用原型继承,我们无法直接扩展一个Class,因为根本不存在Class这种类型。

函数

函数的定义和调用

  • 函数体内部的语句在执行时,一旦执行到return语句时就结束,并返回结果,如果没有return语句就返回undefined

  • function abs(x) {    if (x >= 0) {        return x;    } else {        return -x;    }}// abs() 函数是一个函数对象,函数名 abs可以认作是指向函数的变量// 匿名函数var abs = function (x) {    if (x >= 0) {        return x;    } else {        return -x;    }}; // 需要在函数结束带上分号表示赋值结束// 上述两种写法是等价的
    
  • 匿名函数将函数赋值给了变量 abs,可以通过 abs 调用该函数

  • 函数在传值的时候个数不受影响,函数内部不会接受多余的函数,但是传值过少则会返回错误的值

arguments 方法
  • 永远指向当前函数的调用者传入的所有参数
  • arguments 类似一个数组,但不是数组
  • arguments 可以获得调用者传入的所有参数,如果函数不定义任何参数则获取到0
for in && for of
  • for of 可以直接取出对应下标的值

  • for in 则取出对应下标

  • function foo(a, b, ...rest){    var result = 0;    for(var i of  arguments){        result += i;    }    return result;}a = foo(9, 2, 3, 4, 5, 6);alert(a);
    
rest 参数
  • function foo(a, b, ...rest) {    console.log('a = ' + a);    console.log('b = ' + b);    console.log(rest);}// 只获取a,b两个参数,后面的参数自动生成为一个数组
    
  • rest 参数只能写在最后,前面用 ... 标识

  • 如果前面的参数未传满,则前面的参数为 undefined,rest则接收一个空数组

变量作用域与解构赋值

  • 变量在函数体内部声明则作用域为整个函数体,在函数体外不可调用
  • 不同函数体内部声明的相同名称的变量相互独立,互不影响
  • JavaScript的函数可以嵌套,内部函数可以访问外部定义的变量,反之则不行
  • 如果内部函数变量名与外部相同,函数会使用内部的变量
变量提升
  • JavaScript的函数定义会先扫描整个函数体语句,叭所有声明的变量提到函数顶部,但是只会提升定义,不会提升赋值

  • function foo() {    var x = 'Hello, ' + y;    console.log(x);    var y = 'Bob';} // 输出 Hello undefined;
    
全局作用域
  • 未在任何函数体内定义的变量就具有全局作用域,JavaScript默认的全局对象是 window,全局作用域的变量会被绑定到window的属性上

  • var course = 'Learn JavaScript';alert(course); // 'Learn JavaScript'alert(window.course); // 'Learn JavaScript'
    
  • 因此,以变量方式 var foo = function(){}l; 定义的函数实际也是一个全局变量,直接访问 foo() 和访问 window.foo()是一样的

  • 减少名字冲突的一个办法是把自己所有的变量和函数全部绑定到一个全局变量中

  • // 唯一的全局变量MYAPP:
    var MYAPP = {};
    
    // 其他变量:
    MYAPP.name = 'myapp';
    MYAPP.version = 1.0;
    
    // 其他函数:
    MYAPP.foo = function () {
        return 'foo';
    };
    
局部作用域
  • JavaScript的变量作用域是在函数体内部,所以无法在循环语句块中定义具有局部作用的变量;因此需要使用 let关键字 来替代 var
  • 由于 var 和 let 声明的是变量,所以ES6引入了新的关键字 const 来定义常量。const 和 let 都具有块级作用域
解构赋值
  • 可以实现同时对多个变量赋值

  • 使用解构赋值时如果对应的属性不存在变量将赋值为 undefined

  • var array = ['hello', 'JavaScript', 'ES6'];
    var x = array[0];
    var y = array[1];
    var z = array[2];
    
    var [x, y, z] = ['hello', 'JavaScript', 'ES6'];
    
    // 嵌套赋值
    let [x, [y, z]] = ['hello', ['JavaScript', 'ES6']];
    
    // 忽略某些元素进行赋值
    let [, , z] = ['hello', 'JavaScript', 'ES6'];
    
    // 从对象里面取出嵌套元素
    var person = {
        name: '小明',
        age: 20,
        gender: 'male',
        passport: 'G-12345678',
        school: 'No.4 middle school',
        address: {
            city: 'Beijing',
            street: 'No.1 Road',
            zipcode: '100001'
        }
    };
    var {name, address: {city, zip}} = person;
    // 如果使用的变量与属性名不一致可以给将属性值赋给变量
    let {name, passport:id} = person;
    // 也可以修改他的默认值
    var {name, single=true} = person;
    
    // 如果变量已经被声明再赋值会报错
    var x, y;
    {x, y} = { name: '小明', x: 100, y: 200};
    // 因为JavaScript把以大括号开头当作了块处理,=就不合法,用小括号括起来就ok
    ({x, y} = { name: '小明', x: 100, y: 200});
    
  • 使用解构赋值来交换变量的值,不需要临时变量

    var x=1, y=2;[x, y] = [y, x]// 快速获取当前页面的域名和路径var {hostname:domain, pathname:path} = location;
    

sort函数

  • 直接使用sort()排序会按照ASCII码进行排序,如果是数字的话会自动识别成字符串按照首位数字的ASCII码进行排序

  • // 使用 sort() 方法对数字进行排序arr.sort(function (x, y) {    if (x < y) {        return -1;    }    if (x > y) {        return 1;    }    return 0;});
    
  • // 将所有字符串首字母大写再排序var arr = ['Google', 'apple', 'Microsoft'];arr.sort(function (s1, s2) {    x1 = s1.toUpperCase();    x2 = s2.toUpperCase();    if (x1 < x2) {        return -1;    }    if (x1 > x2) {        return 1;    }    return 0;});
    

异常处理

  • 如果一个函数内部出现了错误,但是它本身没有try…catch 那错误就会被抛到外部函数上去如果一直没有被捕获就会一直向外抛出直至被JavaScript引擎获取,代码终止

try……catch……finally

  • var r1, r2, s = null;try {    r1 = s.length; // 此处应产生错误    r2 = 100; // 该语句不会执行} catch (e) {    console.log('出错了:' + e);} finally {    console.log('finally');}console.log('r1 = ' + r1); // r1应为undefinedconsole.log('r2 = ' + r2); // r2应为undefined
    
  • 可以不带finally直接catch,也可以catch不带finally

抛出错误

  • 主动抛出一个错误,让执行流程直接跳转到catch块。抛出错误使用throw语句

  • if(typeof x !== 'number'){	throw 'Not a number!'; // 手动产生一个错误,阻止程序继续执行}
    

闭包

  • 返回函数不要引用任何循环变量或者后续会发生变化的变量

  • function count() {    var arr = [];    for (var i=1; i<=3; i++) {        arr.push(function () {            return i * i;        });    }    return arr;}var results = count();var f1 = results[0];var f2 = results[1];var f3 = results[2];f1(); // 16f2(); // 16f3(); // 16
    
  • 返回的是一个函数体需要再进行调用才能获得最后的值

箭头函数

  • x => x * x// 等价于function (x) {    return x * x;}
    
  • 箭头函数相当于匿名函数,并且简化了函数定义,包含多行语句时就不能省略花括号以及里面的return语句

  • // 包含多个表达式x => {    if (x > 0) {        return x * x;    }    else {        return - x * x;    }}
    
  • 如果返回的是一个对象,需要注意写法

  • // 错误写法x => { foo: x }// 正确写法x => ({ foo: x })
    

Less

作用域

  • less 变量的作用域与js一致,都是优先使用本身的作用域内寻找对应的变量
  • 如果有同名的变量,后面的变量会覆盖前一个变量

嵌套

  • 与 css 的后代选择器同理,在父元素内添加子元素的样式

  • @hcolor: red;.header{    font-size: 20px;    .nav{ // nav是header的子元素        float: left;        background-color: @hcolor;    }    .logo{        float: left;        width: 300px;    }}
    

运算

  • @width: 200px;@height: @width + 10px;.header{    width: @width;    height: @height;    background-color: red;}
    
  • 支持加减乘除运算,不支持取余

&号的使用

  • 使用 & 符号选择父标签并设置伪类,如果设置伪类不带 & 则无效

  • .header{    color: black;    .nav {        font-size: 20px;    }    .logo {        width: 300px;        &:hover{            text-decoration: line-through;        }    }}
    

混合

  • 混合即是JavaScript内的函数,有带参和不带参两种形式

  • 给混合名称带上小括号混合体就不会在css中出现,不带则会原样解析在css中

  • // 不带参.less-mixins(){ // 命名带上() less 的混合就不在 css 内不编译    float: left;    width: 200px;    height: 200px;}.div1{    .less-mixins;    background-color: green;}.div2{    .less-mixins;    background-color: red;}
    
  • // 带参数.border-radius(@radius){    border-radius: @radius;    -moz-border-radius: @radius;    -webkit-border-radius: @radius;    border: 3px solid red;    height: 100px;    width: 100px;}#header{    .border-radius(6px)}.border-radius2(@radius: 5px){    border-radius:  @radius;    -moz-border-radius: @radius;    -webkit-border-radius: @radius;    border: 3px solid red;    height: 100px;    width: 100px;}#header2{    .border-radius2;}
    

继承

  • 关键字: extend。直接指定对应的混合体

  • .less-mixins{    width: 200px;    height: 200px;}#div1{    &:extend(.less-mixins);    background-color: green;}#div2{    &:extend(.less-mixins);    background-color: red;}
    

移动端开发

移动端基础

  • 手机上默认的布局视口宽度:主要缩放,适应PC端的页面宽度
  • window.onresize = function(){}; 当浏览器变化大小时执行方法
  • vh 单位:1vh表示浏览器高度的 1%。
  • user-select: none; 可以让网页中的文字不能被选中

js获取当前屏幕宽高

Javascript:网页可见区域宽: document.body.clientWidth网页可见区域高: document.body.clientHeight网页可见区域宽: document.body.offsetWidth (包括边线的宽)网页可见区域高: document.body.offsetHeight (包括边线的高)网页正文全文宽: document.body.scrollWidth网页正文全文高: document.body.scrollHeight网页被卷去的高: document.body.scrollTop网页被卷去的左: document.body.scrollLeft网页正文部分上: window.screenTop网页正文部分左: window.screenLeft屏幕分辨率的高: window.screen.height屏幕分辨率的宽: window.screen.width屏幕可用工作区高度: window.screen.availHeight屏幕可用工作区宽度: window.screen.availWidthJQuery:$(document).ready(function(){alert($(window).height()); //浏览器当前窗口可视区域高度alert($(document).height()); //浏览器当前窗口文档的高度alert($(document.body).height());//浏览器当前窗口文档body的高度alert($(document.body).outerHeight(true));//浏览器当前窗口文档body的总高度 包括border padding marginalert($(window).width()); //浏览器当前窗口可视区域宽度alert($(document).width());//浏览器当前窗口文档对象宽度alert($(document.body).width());//浏览器当前窗口文档body的宽度alert($(document.body).outerWidth(true));//浏览器当前窗口文档body的总宽度 包括border padding margin})

视口

  • 浏览器显示页面内容的屏幕区域

  • 设置视口可以缩小网页的尺寸,保证网页在手机上正常显示

  • image-20210416112242961

  • meta视口标签存在的目的是让布局视口的尺寸和理想视口的尺寸匹配。它是Apple发明的,其他手机和平板复制了它的大部分内容。桌面浏览器不支持它,也不需要它,因为它们没有理想视口的概念。meta视口标签应该被放在文档的head标签中

  • width:主要目的是把布局视口的尺寸设为一个理想的值。那个这个特定的值可以设置为一个具体的像素值。不过默认值为“device-width”,即让布局视口的宽度等于设备的宽度。这样可以灵活的变通,而不用考虑设备的尺寸变化。initial-scale:设置了页面的初始缩放程度。1代表100%,2表示200%,以此类推。minimum-scale和maximum-scale:设置缩放程度的最小值和最大值。容易出现的问题是修改了initial-scale的值,发现没有任何效果,原因是没修改maximum-scale的默认值。如果最大缩放程度为1,无论把initial-scale改为几都没用。

布局视口 layout viewport
  • document.documentElement.clientWidth/Height 返回的是布局视口的尺寸
  • 在PC端上,布局视口等于浏览器窗口的宽度。而在移动端上,由于要使为PC端浏览器设计的网站能够完全显示在移动端的小屏幕里,此时的布局视口会远大于移动设备的屏幕,就会出现滚动条。
  • image-20210416111900196
视觉视口 visual viewport
  • js获取视觉视口:window.innerWidth
  • 指用户看到的网页区域。用户可以缩放屏幕,操作的是视觉视口,但是不管用户怎么缩放都不会影响到布局视口的宽度
  • image-20210416111942596
理想视口 ideal viewport
  • js获取理想视口:window.screen.width

  • <meta name="viewport" content="width=device-width, initial-scale=1.0 "/>; // 理想视口实现
    
  • 指布局视口的一个理想状态,只有当布局视口等于设备宽度时才是理想视口

  • image-20210416112002107

em、rem使用场景

  • em
    • 通常不使用 em 单位控制字体大小
    • 首行缩进:text-indent: 2em;
  • rem
    • 一切可扩展都应该使用 rem 单位
    • 响应式网站可以使用 rem 去做适配
  • 不要使用 em 或 rem
    • 多列元素
    • 当元素应该是严格不可缩放的时候

总结

自适应和响应式布局

  • 响应式覆盖了自适应
  • 响应式布局不仅是自适应,同时会改变排列方式
  • 判断浏览器终端类型

Swiper 插件

响应式网页设计

  • 写一套网页可以适应不同终端
  • 响应式网页设计(RWD,Responsive Web Design)
  • 有三种开发技术:弹性布局、弹性图片、媒体和媒体查询
媒体查询
  • 媒体类型
设备类型
All 所有设备
Braille 盲人用点字法触觉回馈设备
Embossed 盲人打印机
Handheld 便携设备
Print 打印用纸或打印预览视图
Projection 各种投影设备
Screen 电脑显示器
Speech 语音或音频合成器
Tv 电视机类型设备
Tty 使用固定密度字母栅格的媒介,比如电传打字机和终端
  • 媒体类型引入方式

    • @media方式 @media 媒体类型 { 选择器{ /样式代码写在这里…/ } }

    • link 方法 <link rel="stylesheet" href ="style.css" media="媒体类型" />

    • @import中引入

      <style type="text/css" media="screen and (min-width:600px) and (max-width:900px)">      @import url("css/style.css");  </style> 
      
  • 媒体特性:“媒体类型(判断条件)+ CSS(符合条件的样式规则)”

  • 媒体特征语法:@media 媒体类型 and (媒体特性)

  • 关键字

    • and:同时满足才生效

      @media screen and (max-width:1200px)

    • only:指定某种特定的媒体类型,可以用来排除不支持媒体查询的浏览器

      <link href = "style.css" media = " only screen and (max-width**:500px) " />

    • not:排除某种指定的媒体类型,即排除符合表达式的设备

      @media not print and (max-width:1200px){样式代码…}

属 性 Min/Max 描 述
device-width Length Yes 设置屏幕的输出宽度
device-height Length Yes 设置屏幕的输出高度
width Length Yes 渲染界面的宽度
height Length Yes 渲染界面的高度
Orientation Portrait/landscape No 横屏或竖屏
Resolution 分辨率(dpi/dpcm) Yes 分辨率
Color 整数 Yes 每种色彩的字节数
color-index 整数 Yes 色彩表中的色彩数

响应式布局的优缺点

  • 优点
    • 不同分辨率设备灵活性强
    • 多终端视觉和操作体验好
    • 响应式web设计中的大部分技术都可以在WebApp中开发
    • 节约开发成本,维护成本也轻松很多
  • 缺点
    • 兼容各种设备工作量大,效率低下
    • 代码累赘,会出现隐藏无用元素,加载时间长

与自适应的区别

  • 响应式的概念覆盖了自适应,但是包括的东西更多。响应式布局可以根据屏幕的大小自动的调整页面的展现方式,以及布局
  • 自适应还是暴露出一个问题,如果屏幕太小,即使网页能够根据屏幕大小进行适配,也会感觉在小屏幕上查看,内容过于拥挤
  • 响应式解决了自适应布局的问题。它可以自动识别屏幕宽度、并做出相应调整,布局和展示的内容可能会有所变动

rem 响应式布局

  • 如果页面只在移动端访问,可以使用 rem实现响应式布局
  • 实现原理: 动态改变 html的font-size值的大小,来完成rem实现响应式布局

Flex弹性布局

  • 对界面宽容度要求较高

  • 采用 Flex 布局的元素

    移动端点透问题

    事件冒泡

jQuery

jQuery基础

$(document).ready(function(){}); 为页面加载时间绑定方法

vue

  • vue是mvvm模型,自底向上逐层应用,用于构建用户界面的渐进式框架。
  • attribute 是元素标签的属性,property 是元素对象的属性

MVVM框架

  • 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变
  • 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑
  • 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计

begin

<div id="box">  //确定一个范围,表示此范围内部都是 vue解析出来的      <h1> {{ msg }} </h1></div><script>	var vm = new Vue({		el: '#box',  // el:‘选择器’		data: {		     msg: 'hello, world!'		},		// 实例中,可以设置很多配置项	});</script>

vue实例

// 创建一个vue实例var vm = new Vue({  // 选项})

数据方法

  • 当一个 Vue 实例被创建时,它将 data 对象中的所有的 property 加入到 Vue 的响应式系统中。当这些 property 的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
// 我们的数据对象var data = { a: 1 }// 对象被加入到一个 Vue 实例中var vm = new Vue({  data: data})
  • 使用 Object.freeze(),这会阻止修改现有的 property。
// 我们的数据对象	var data = { a: 1 }	Object.freeze(data)	// 该对象被加入到一个 Vue 实例中	var vm = new Vue({		el: '#app',		data : data})

差值表达式

  • 使用双大括号包裹起来的js代码就是差值表达式
  • 差值表达式会解析大括号内的内容转换成对应属性值进行展示
  • 差值表达式内可以进行运算和简单js代码解析,例如三目运算。但是不建议写入复杂的js语句

插入原始 html 代码不能使用双大括号;需要使用 v-html 指令

<p>Using v-html directive: <span v-html="rawHtml"></span></p>

vue指令

  • 指令(Directive)是特殊的带有 v- 前缀的特性
  • 指令的作用:当表达式的值改变时,将某些行为应用到DOM上
  • 指令可以写在任意HTML元素的开始标签内
  • 一个开始标签内可以写入多个指令,多个指令之间使用空格分隔

v-if指令

  • 条件渲染指令,他根据表达式的真假来插入和删除元素

  • v-if='表达式' : 根据表达式结果的真假,缺点是否显示当前元素;true显示,false隐藏

v-else指令

  • v-else元素必须立即跟在v-if元素后,否则不能被识别
  • v-else后不需要表达式
  • v-if为true,不执行v-else;v-if为false,执行

v-show指令

  • 控制切换一个元素的显示和隐藏
  • v-show = ’表达式‘;根据表达式结果的真假确定是否显示该元素

v-if 与 v-show 区别

  • v-show也是条件渲染指令,和v-if指令不同的是,使用v-show指令的元素始终会被渲染到HTML,它只是简单地为元素设置CSS的style属性。不满足条件的元素被设置了style="display:none"样式,如果是v-if不满足条件的情况下,是不会渲染到html中的;
  • v-if 指令有更高的切换消耗,v-if当条件成立时就会将元素加上,不成立就会移除,内部指令不执行
  • v-show指令有更高的初始渲染消耗。他只是简单是隐藏和显示
  • 如果需要频繁切换使用 v-show 较好,在运行时条件不大可能改变使用 v-if 较好

v-on 指令

<div id="app-5">  <p>{{ message }}</p>  <button v-on:click="reverseMessage">反转消息</button></div><script>  var app5 = new Vue({  el: '#app-5',  data: {    message: 'Hello Vue.js!'  },  methods: {    reverseMessage: function () {      this.message = this.message.split('').reverse().join('')    }  }})</script>
  • 为 HTML 元素绑定事件监听
  • v-on:事件名称='函数名称()' 表达式可以是一个方法的名字或一个内联语句
  • 简写语法:@事件名称 = '函数名称()' 函数定义在 methods 配置项中

简写 <button @click='fn()'>toggle

v-on 后添加修饰符防止事件冒泡
  • .stop 调用event.stopPropagation();方法只在自身使用,不向上传递
  • .prevent 调用event.preventDefault()
  • .self 只当元素自身触发时才触发回调,限制到自己身上去触发事件,不由冒泡触发
  • .{keycode} 只在指定键上触发回调

v-model指令

<div id="app-6">  <p>{{ message }}</p>  <input v-model="message"></div><script>  var app6 = new Vue({  el: '#app-6',  data: {    message: 'Hello Vue!'  }})</script>
  • 实现表单输入和应用状态之间的双向绑定,将用户的输入同步到视图
  • 双向绑定指的是vue实例中的data与其渲染的dom元素上内容保持一致,不论谁改变另一方也随之更新
  • v-model = '变量' v-model指令只能用在表单元素上

v-bind指令

  • v-bind可以在其名称后面带一个参数,参数通常是HTML元素的特征(attribute),v-bind是动态绑定指令,默认情况下自带属性值是固定的,为了动态的给属性添加值可以使用v-bind指令
  • v-bind:属性名 = '表达式'
  • 简写: v-bind可以省略,直接书写 :属性名 = '表达式'
  • <img v-bind:src="imageSrc"> 等价于 <img :src="imageSrc"> 绑定一个属性
  • <img v-bind:class="{'textColor':isColor, 'textSize':isSize}"> 多个样式绑定

v-for指令

<div id="app-4">  <ol>    <li v-for="todo in todos">      {{ todo.text }}    </li>  </ol></div><script>  var app4 = new Vue({  el: '#app-4',  data: {    todos: [      { text: '学习 JavaScript' },      { text: '学习 Vue' },      { text: '整个牛项目' }    ]  }})</script>
  • 遍历data中的数据,并进行展示
  • v-for = '(item, index) in items'
    • item 表示每次遍历得到的元素
    • index 表示item 的索引,可选参数
    • items 表示数组或者对象

交互与实例的生命周期

Axios

基于promise用于浏览器和node.js的http客户端

Axios的特点

  • 支持浏览器和 node.js
  • 支持 promise
  • 能拦截请求和响应
  • 能转换请求和响应数据
  • 能取消请求
  • 自动转换json数据
  • 浏览器端支持防止 CSRF(跨站请求伪造)

Axios发送请求(Get方法)

<script>    var vm = new Vue({        el: "#app",        data: {            msg: "hello"        },        methods: {            get() {                axios.defaults.baseURL = 'http://127.0.0.1:8080' // tomcat                axios.defaults.withCredentials = true // 开启跨域                axios.get("/demo00_war/news/CorsServlet", {                    // 携带参数写法                    params: {                        username: 'name',                        password: 'password'                    },                    // 请求头中添加参数                    // Headers: {                    //     token: "123"                    // }                }).then( // 支持promise语法,代表成功获得数据之后的操作                    res => { // res就是response的意思,代表返回值                        // 这里我们把返回值赋值给msg显示到页面                        // 判断状态是否为200, 200才是正确的响应信息                        if (res && res.status === 200) {                            this.msg = res.data.name;                        } else {                            alert("出错了")                        }                    }).catch(err => {                    // 没有成功获得数据,打印错误                    console.log(err)                })            }        }    })</script>

全局拦截器

axios.interceptors.request.use(function (config) {    // 请求之前可以处理一些操作    return config;  }, function (error) {    // 请求错误之前可以处理的操作    return Promise.reject(error);  });axios.interceptors.response.use(function (response) {    //返回响应数据做一些操作   return response;  }, function (error) {    //响应错误的时候做一些操作    return Promise.reject(error);  });

实例的生命周期函数(钩子函数)

img
  • 是指实例对象从构造函数开始执行(被创建)到被GC(Garbage Collection)回收垃圾回收销毁的过程就是他的生命周期
  • 在生命周期中被自动调用的函数叫做生命周期函数,也称钩子函数
生命周期函数 含义
beforeCreate(创建前) 组件实例刚被创建,组件属性计算之前,比如data属性等等
created(创建后) 组件实例刚创建完成,属性已经绑定,当时DOM还未生成,$le属性还不存在
beforeMount(载入前) 模板编译、挂载之前
mounted(载入后) 模板编译、挂载之后
beforeUpdate(更新前) 组件更新之前
updated(更新后) 组件更新之后
beforeDestroy(销毁前) 组件销毁前调用
destroyed(销毁后) 组件销毁后调用

钩子函数适用场景

  • beforeCreate:加载等待事件
  • created:结束等待事件,做初始化。实现函数自执行
  • mounted:发起后端请求取回数据、接收页面之间传递的参数、子组件向父组件传递参数

vue计算属性

  • 关键字:computed
  • 计算属性就是当期依赖属性的值发生变化时,这个属性的值会自动更新,与之关联的DOM部分也会同步更新
  • vue 中computed的属性可以看成和data一样,可以读取和设值,所以计算属性可以分作geter和setter。一般情况下没有setter,计算属性就是只读属性。getter是默认省略的
  • component计算属性的结果会被缓存,触发依赖的响应式属性变化才会重新计算;主要当作属性来用(返回一个新的数据)

计算属性与methods对比

  • computed 是属性调用,而methods是函数调用、
  • computed依赖于data中的数据,只有在它相关依赖数据发生改变时才会重新求值
  • 计算属性有缓存结果的特点
  • methods方法表示一个具体的操作。主要书写业务逻辑(方法调用)

计算属性与watch的对比

  • computed对于简单的逻辑,以及操作处理与watch一样,为了防止对于watch的滥用,建议使用computed,但是逻辑关系复杂的computed也要自定义一个侦听器,用于异步处理和复杂的处理
  • watch为一个对象,键是需要观察的表达式,值是对应的回调函数;主要用来监听某些特定的数据变化,从而进行某些具体的业务逻辑操作,可看作是component 和 methods 的结合体

vue侦听器

  • vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时使用侦听器
  • 深度侦听:可以监听到数组和对象的变化
  • 侦听器可以监听数组、对象;但是过多使用侦听器会造成性能下降
  • 允许异步操作,限制我们执行操作的频率,并在得到最终结果前设置中间状态
<div id="app">        <input type="text" v-model="message">        <h2>state: {{ state}}</h2></div><script>    var vm = new Vue({        el: "#app",        data: {            message: "111",            state: "原始数据",        },        watch:{            message: function(e){                this.state = "修改后"            }        }    })</script>

vue过滤器

  • vue支持在 {{}} 插值的尾部添加一个管道符 | 对数据进行过滤,经常用于格式化文本,比如字母的大写、货币的千位使用逗号分割等
  • 过滤器发规则是自定义的,通过vue示例添加选项filters来设置
  • 过滤器是 JavaScript 函数,可以接收参数
  • 在没有冲突的前提下,过滤器可以串联
  • 过滤器的本质就是一个有参数有返回值的方法
  • 过滤器应当用于处理简单的文本转换,如果要实现更为复杂的数据变化,应该使用计算属性来做

组件

  • 组件就是自定义控件
  • 组件能够封装可重用代码,扩展HTML标签功能
  • 组件是可复用的 Vue 实例, 如果网页中的某一个部分需要在多个场景中使用,那么我们可以将其抽出为一个组件进行复用。组件大大提高了代码的复用率。

全局组件:创建实例前定义全局组件

  • 组件调用方法:<组件名></组件名>
  • 全局范围内可以调用;不同的作用域均可以使用
// 定义名为 runoob 的新组件<div id="app">        <runoob></runoob></div><script>    // 注册组件    Vue.component('runoob', {        template: '<div style="border: 1px black solid;">11</div>'    })    // 创建根实例    new Vue({        el: '#app'    })</script>

局部组件:只在定义该组件的作用域内可以使用

  • 局部组件定义位置:实例配置项中定义
  • 组件的作用域:定义该组件的作用域内可调用

父子组件

components:{	‘parent’: {		template:’<div></div>’,		components:{			‘child’: {				template:’<div></div>’			}		}}  }
  • 当前组件内部仍然存在 components选项,当前组件的子组件就写到components选项中
  • 父子组件的作用域
    • 组件相当于完整的vue实例
    • 组件与vue实例间作用域独立
    • 父子组件作用域间相互独立
      • 子组件调用只能在父组件的模块中进行调用
组件通信
props选项
  • 组件之间不仅仅是要把模板的内容进行复用,更重要的是组件之间的通信,由父组件向子组件正向传递数据或参数,就是通过props实现
  • 作用:props选项用来声明他期待获得的数据
  • props本质:props为元素属性
  • 组件不仅仅要把模板的内容进行复用,更重要的是组件之间进行通信,通常是父组件的模板中包含子组件,父组件要向子组件进行传递数据或者参数,子组件接收后根据参数的不同来渲染不同的内容或者执行操作,这个正向传递数据的过程就是通过 props来实现的;
props声明
  • props值:可以是两种:一种是字符串数组,一种是对象
<script>	components: { '组件名称': {            template: '#模板ID',            props: ['message1', 'message2',...]        }    }</script><组件 message=‘val’></组件>
props使用
  • 与data一样,props 可以在模板中
  • 可以在 vm 实例中像 this.message 使用
  • 与组件data函数 return 的数据没有区别
    • props 的数据来源于父级
    • data 中的数据是自己的数据
  • 不管是props的数据还是组件data函数return的数据都可以在模板template以及计算属性computed和方法methods中使用
父组件传值给子组件
  • 子组件有时候需要接收来自父组件的动态数据,这时候就需要使用v-bind指令来动态绑定props的值

vue脚手架

vue项目

  • 一个组件下下只能由一个并列的 div
  • 数据要写在 return里面,不能按照 vue实例写

vue项目文档介绍

img

1、build:构建脚本目录

    1)build.js ==> 生产环境构建脚本;``

    2)check-versions.js ==> 检查npm,node.js版本;

    3)utils.js ==> 构建相关工具方法;

    4)vue-loader.conf.js ==> 配置了css加载器以及编译css之后自动添加前缀;

    5)webpack.base.conf.js ==> webpack基本配置;

    6)webpack.dev.conf.js ==> webpack开发环境配置;

    7)webpack.prod.conf.js ==> webpack生产环境配置;

 2、config:项目配置

    1)dev.env.js ==> 开发环境变量;

    2)index.js ==> 项目配置文件;

    3)prod.env.js ==> 生产环境变量;

 3、node_modules:npm 加载的项目依赖模块

​ 4、src:这里是我们要开发的目录,基本上要做的事情都在这个目录里。里面包含了几个目录及文件:

    1)assets:资源目录,放置一些图片或者公共js、公共css。这里的资源会被webpack构建;

    2)components:组件目录,我们写的组件就放在这个目录里面;

    3)router:前端路由,我们需要配置的路由路径写在index.js里面;

    4)App.vue:根组件;

    5)main.js:入口js文件;

 5、static:静态资源目录,如图片、字体等。不会被webpack构建

 6、index.html:首页入口文件,可以添加一些 meta 信息等

 7、package.json:npm包配置文件,定义了项目的npm脚本,依赖包等信息``

 8、README.md:项目的说明文档,markdown 格式

 9、.xxxx文件:这些是一些配置文件,包括语法配置,git配置等

vue打包为静态网页

java

(https://www.cnblogs.com/yuan-liu/p/15252299.html)

java基础

命令行修改路径

  • 改变目标磁盘:C:\Users\YuanLiu> d: 直接在 > 后面输入目标磁盘+:
  • 进入目标地址:cd F:\my\AllCode\codes\begin 直接 cd + 路径

命令行执行 java 程序

  1. 在路径中打开命令行窗口
  2. 命令行输入 javac TheName.java ;在文件夹中会生成一个 .class文件
  3. 命令行输入 java TheName ; 程序开始执行
  4. 命令行输入过程大小写严格
  5. 已经生成 .class 文件,下次可以直接执行第三步调用

初始java

Java程序解构

  • 类名与文件名完全一致,首字母大写
  • main() 程序入口,四要素必不可少
  • public static void main(String[] args){}
  • System.out.println() 从控制台输出信息,System注意大写
  • 花括号必须成对出现,缺一不可
  • 语句结束必须带分号

输出

  • System.out.println(); :换行输出
  • System.out.print(); :输出不换行
  • \n:转义输出,换行;\t :制表符

注释

  • 单行注释://
  • 多行注释:/* 注释内容 */
  • JavaDoc注释:/** 注释内容 */
  • javadoc注释:为了最终可以生成 html 帮助文件

变量和数据类型

变量

  • 变量的概念:内存根据数据的不同类型开辟的空间
  • 变量存储的是内存地址,我们通过变量名能获取到对应地址保存的数据
  • 变量命名规范:
    • 只能使用英文字母、_ 、$三个开头,后面的可以使用数字、字母和_ 、美元符号 ;
    • 不能使用 java 关键字
    • 采用见文识意的方式命名
    • 多个字母命名时,采用小驼峰方式命名
变量的声明以及使用
  1. 声明变量,根据数据类型申请变量:int money;
  2. 变量赋值,将数据存储到对应位置:money = 200;
  3. 两步合并:int money = 200;

数据类型

数据类型 大小 取值范围
byte 1字节8位 -128 ~ +127
int 4字节32位 -2147483648(-231) ~ + 2147483647(231-1)
short 2字节16位 -32768 (-215) ~ + 32767 (+215-1)
long 8字节64位 -263 ~ + 263-1
float 4字节32位浮点数 1.4E-45 ~ 3.4E+38 , -1.4E-45 ~ -3.4E+38
double 8字节64位浮点数 4.9E-324 ~ 1.7E+308, -4.9E-324 ~ -1.7E+308

常量

  • 常量需要在定义时加上final:final int NUM = 10;
  • 常量名通常全大写
  • 不同字符使用下划线分割
  • 只能被赋值一次,通常定义时即初始化

scanner

scanner输入

  1. 导入 scanner 类:import java.util.*;
  2. 创建 scanner 对象:Scanner input = new Scanner(System.in)
  3. 获取键盘输入的数据:int now = input.nextInt();
  4. 整数:nextInt(); 小数:nextDouble(); 字符串:next();

自动类型转换

  • 如果一个操作数为 double 型,则整个表达式可提升为 double 型
  • 数据类型兼容可转换例:整数和浮点数兼容
  • 目标类型大于源类型可转换例:double 型大于 int 型
  • 整型数,字节少的可以往多的转反之不行
  • double不能自动转float,float可以转double
  • char可以做数学运算,使用本身的 Ascii 码

强制类型转换规则

int b = (int)10.2;double a = 10;int c = (int)a;

选择结构

循环条件,优先级

  • 最高优先级:()
  • 最低优先级:=
  • 优先级:!> 算术运算符 > 关系运算符 > && > ||
  • 复杂条件使用括号提高可读性

swith 与多重 if

image-20210420142508283

总结

image-20210420142545491

循环结构

  • while循环:先判断循环条件再执行循环操作
  • do-while循环:先执行,再判断条件
  • for循环:比较简洁,循环次数固定
  • while 和 do-while 的区别
    • 执行次序不同
    • 初始情况不满足的话,do-while会执行一次,而while一次不执行

for循环

for循环的语法和执行顺序

image-20210420143225395

总结

  • 需要多次重复执行一个或者多个任务的问题考虑使用循环来解决
  • 无论哪种循环结构,都有四个必不可少的部分:初始部分、循环条件、循环体、更新循环变量

image-20210420143530334

数组

  • 数组是一个变量,存储相同数据类型的一组数据
  • 数组组成要素
    • 标识符(即数组的名字)
    • 数组元素(必须是同一类型的元素)
    • 元素下标(下标都是从0开始)
    • 元素类型
  • 数组的长度是不可变的
  • 声明数组
    • 数据类型[] 数组名 = new 数据类型[数组长度];
    • 例:int[] scores = new int[10];
冒泡排序
  • 降序排列数组元素
public class Main{    public static void main(String[] args){        int arr[] = new int[]{35, 42, 21, 66, 12};        for(int i = 0; i < arr.length-1; i++){            for(int j = 0; j < arr.length-1-i; j++){                if(arr[j] < arr[j+1]){                    int temp = arr[j];                    arr[j] = arr[j+1];                    arr[j+1] = temp;                }            }        }        for(int x = 0; x < arr.length; x++){            System.out.println(arr[x]);        }    }}
Arrays类
  • 使用 Java.util.Arrays类

  • java.util包提供的工具类

  • Arrays类提供操作数组的方法,如:排序、查询

  • Arrays类的 sort() 方法:对数组进行升序排列

  • 方法名称 说明
    boolean equals(array1,array2) 比较array1和array2两个数组是否相等
    sort(array) 对数组array的元素进行升序排列
    String toString(array) 将一个数组array转换成一个字符串
    void fill(array,val) 把数组array所有元素都赋值为val
    copyOf(array,length) 把数组array复制成一个长度为length的新数组,返回类型与复制的数组一致
    int binarySearch(array, val) 查询元素值val在数组array中的下标(要求数组中元素已经按升序排列)

多维数组

  • 三维以上的数组很少使用
  • 主要使用二维数组
  • 从语法上 java 支持多维数组
  • 从内存分配原理的角度讲,只有一位数组
二维数组
  • 二维数组实际上是一个以一维数组做元素的一维数组
  • 二维数组定义与赋值
    • int scores[][]=new int{{90, 85, 92, 78, 54}, {76, 63, 80}, {87}};
    • int scores[][] = {{90, 85, 92, 78, 54}, {76, 63,80}, {87}};
  • image-20210425141917492

面向对象

开发方法

  • 结构化开发
    • 面向功能划分软件结构
    • 自顶而下
    • 最小的子系统是方法
    • 制约了软件的可维护性和可扩展性
  • 面向对象开发
    • 把软件系统看成各种对象的集合
    • 系统结构较稳定
    • 子系统相对独立
    • 软件可重用性、可维护性和可扩展性强

对象的特征

  • 属性:对象具有的各种特征(对象的静态特征)
    • 每个对象的每个属性都拥有特定值
  • 方法:对象执行的操作(对象的动态特征)
    • 对象:用来描述客观事物的一个实体,由一组属性和方法构成
  • 类:具有相同属性和方法的一组对象的集合类是对象的抽象,对象是类的具体
  • 类是抽象的概念,仅仅是模板
  • 对象是一个看得到,摸得着的具体实体
  • image-20210425151015345

创建和使用对象

  • 创建对象
    • 类名 对象名 = new 类名();
    • School center = new School();
  • 引用对象成员:使用 . 进行操作
    • 引用类的属性:对象名.属性
    • 引用类的方法:对象名.方法名()

类的方法

image-20210425151411805

方法的返回值

  • 如果方法具有返回值,方法中必须使用关键字 return 返回该值,返回值类型为该值的类型
  • 返回值只能有一个
  • 如果方法没有返回值,返回类型为 void

方法调用

  • 方法之间允许相互调用,不需要知道方法的具体实现,实现重用,提高效率

  • 情况 举例
    Student类的方法a( )调用Student类的方法b( ),直接调用 public void a( )
    Student类的方法a( )调用Teacher类的方法b( ),先创建类对象,然后使用“.”调用 public void a( )

Java数据类型

  • 基本数据类型: int short byte long double float boolen char
  • 引用数据类型:String
  • 基本数据类型:传值;
  • 应用数据类型:传入地址;

方法与方法重载

带参数的方法

image-20210426141945058

  • 在定义参数时需要指定参数的类型,调用时则不需要再指定类型
  • 形参和实参的数据类型必须一致,数量也必须一致
  • 基本数据类型,操作传递的是变量的值,改变一个变量的值不会影响另一个变量的值。引用数据类型(类、数组和接口),赋值是把原对象的引用(可理解为内存地址)传递给另一个引用

toString方法

  • 返回该对象的字符串表达
  • 一般会被重写,如不重写就会返回对应地址值

构造方法

  • 构造方法没有返回参数,也不能写 void
  • 构造方法必须和类名一致
  • 如果不写构造方法,java会默认提供一个无参的构造方法
  • 靠参数数量来区别
  • 如果参数数量相同,靠参数数据类型来区别
  • 重载和返回参数没有关系
  • 构造方法间可以互相调用
  • 构造方法调用构造方法的时候:使用 this()
  • this 有两种含义:如果是构造方法调用,代表构造方法,否则代表自己
  • 普通方法调用构造方法,new 构造方法,不能写 this()
  • 构造方法调用普通方法:方法名(参数);
  • 在使用属性或者普通方法的时候,前面可加 this 也可以不加(不是静态方法和静态属性)
  • 如果局部变量和成员变量重名,若不带 this 优先使用局部变量
  • 构造方法调用构造方法, this() 必须放在第一行
方法重载
  • 如果两个方法方法名相同,但参数不一致,则一个方法是另一个方法的重载:
    • 方法名相同
    • 同一个类中
    • 方法的参数类型,参数个数不一样
    • 方法的返回值可以不相同
    • 方法的修饰符可以不相同
    • main 方法也可以被重载

image-20210426142617786

this 的用法
  • 调用属性:this.health = 100;
  • 调用方法:this.print();
  • 调用构造方法:this(); 如果使用,必须是构造方法中的第一句

成员变量和局部变量

  • 变量声明的位置决定变量的作用域
  • 变量作用域确定可在程序中按变量名访问该变量的区域
  • 局部变量超出其作用域后无法使用
成员变量和局部变量的区别
  • 作用域不同
    • 局部变量的作用域仅限于定义他的方法
    • 成员变量的作用域在整个类内部都是可见的
  • 初始值不同
    • java会给成员变量一个初始值
    • java不会给局部变量赋予初始值
  • 在同一个方法中,不允许有同名局部变量;在不同的方法中,可以有同名局部变量
  • 在同一个类中,成员变量和局部变量同名时,局部变量具有更高的优先级

继承 封装

如果类名相同,需要引用时

package com.kgc// 如果类名重复,那么另外一个类直接带全包名 + 类名来区分public class Test{    public static void main(String[] args){        com.kgc.child.Test test = new  com.kgc.child.Test();    }}
  • Java虚拟机默认规则:位于java.lang包下面的类不需要引用,java会自动引入,直接使用

封装

  • 概念:将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的访问和操作。
  • 把尽可能多的东西藏起来,对外提供便捷的接口
  • 只提供给用户用到的接口,封装得越多越安全。
  • 属性无法判断错误,用户输入什么就接受什么不安全
封装的实现
  1. 修改属性的可见性;设为 private ,防止错误的修改
  2. 创建公有的 get/set 方法,用于属性的读写
  3. 在 get/set方法中加入属性控制语句,对属性值的合法性进行判断
封装的好处
  • 便于使用者正确使用系统,防止错误修改属性
    • 提高了系统的安全性
  • 有助于系统之间的松耦合,提高系统的独立性
    • 当某一个系统的实现发生变化时,只要他的接口不变,就不会影响其他的系统
  • 提高软件的可重用性
    • 每个系统都是一个相对独立的整体,可以在多种环境中得到重用
  • 降低了构建大型系统的风险
    • 即便整个系统不成功,个别对立子系统可能任然存在价值
包的作用

image-20210512160859582

类成员的访问修饰

image-20210512161430874

继承

  1. 子类创建对象的时候,首先执行父类的构造方法,然后执行自己的构造方法
  2. 子类不指定调用哪个父类的构造方法,那么 java 默认是使用父类的无参构造方法
  3. 如果父类里面没有无参构造方法,那么需要编写子类的人使用 super() 方法去指定使用父类的构造方法
  4. 如果不写 super() ,java会默认在子类的构造方法上自动生成,调用无参的父类构造方法,且 super() 必须写在第一行
  5. 重写的基本语法规则:方法名称一样、传入参数一样、传出参数一样、修饰符号一样
  6. 一旦重写了父类的方法,子类再调用这个方法,那么调用的就是自己重写的方法
  7. 如果重写了父类的方法,子类还想使用父类的方法,就使用 super.方法名(),使用这种方法没有第一行的限制
  8. 父类的 private 方法不能被子类重写
  9. 子类重写父类的方法,子类的修饰符要大于等于父类的修饰符
  10. 重载和返回参数没有关系,重写要求返回参数一致
  11. 子类重写父类的方法,传入参数要一致,否则不不能重写
  12. 属性没有重写的概念
  • 同包的情况
    • 重写除了private 不能重写,只要子类的修饰符大于等于父类就可以重写
  • 不同包的情况
    • public 可以重写
    • 父类不加修饰符的方法不能被重写到
    • protected 修饰的父类方法,子类可以重写
    • private 修饰的父类方法,子类不能重写
  • 子类能不能重写父类的方法,和子类能不能直接访问父类的方法有关
  • 不同包的情况,子类是不能访问被 private 或不带修饰符的父类方法
  • 不显性写继承,java 默认继承 Object 类型
  • Object 是所有类的起源
  • 继承条件下的构造方法
    • 如果子类的构造方法中没有通过super显式调用父类的有参构造方法,也没有通过this显式调用自身的其他构造方法,则系统会默认先调用父类的无参构造方法。在这种情况下,写不写“super();”语句,效果是一样的。
    • 如果子类的构造方法中通过super显式调用父类的有参构造方法,那将执行父类相应构造方法,而不执行父类无参构造方法。
    • 如果子类的构造方法中通过this显式调用自身的其他构造方法,在相应构造方法中应用以上两条规则。
    • 如果存在多级继承关系,在创建一个子类对象时,以上规则会多次向更高一级父类应用,一直到执行顶级父类Object类的无参构造方法为止。

final关键字

  • 被 final 修饰的类不能被继承
  • 通常将 final 和 static 写在一起表示共享的常量
  • 被 final 修饰的参数在方法中不允许被修改

static修饰符

  • 被 static 修饰过过的属性和方法都是类级,他们是被共享的。访问的时候,我们可以直接通过 类名.方法 来访问
  • 在实例方法里面不可以定义 static 变量
  • 在静态的方法里面访问非静态的属性是不能被访问的,是因为对象本身还没有被创建,非静态属性是属于对象的,不属于类
  • 静态方法可以直接访问静态属性
  • 类加载的时候就执行静态代码块
  • 加载的时候:静态的属性先初始化,然后是静态代码块
  • 静态方法调用普通方法(不允许直接调用)
    • 创建对象进行调用
static变量的作用
  1. 能被类的所有实例共享,可作为实例之间进行交流的共享数据
  2. 如果类的所有实例都包含一个相同的常量属性,可把这个属性定义为静态常量变量类型,从而节省内存空间
类的成员变量包括
  • 类变量(静态变量)
    • 被static修饰的变量
    • 在内存中只有一个拷贝
    • 类内部,可在任何方法内直接访问静态变量
    • 其他类中,可直接通过类名访问
  • 实例变量
    • 没有被static修饰的变量
    • 每创建一个实例,就会为实例变量分配一次内存,实例变量可在内存中有多个拷贝,互不影响
静态方法:可直接通过类名访问
  • 静态方法中不能使用 this 和 super
  • 不能直接访问所属类的实例变量和实例方法
  • 可直接访问类的静态方法和静态变量
实例方法:通过实例访问
  • 可直接访问所属的静态变量、静态方法、实例变量和实例方法
静态方法必须被实现main()就是最常用的静态方法

接口 抽象类

抽象类

  • 抽象类不能被实例化,但可以创建一个引用变量,其类型是一个抽象类,指向非抽象的子类实例

  • 抽象类中可以没有抽象方法,但包含了抽象方法的类必须被定义为抽象类

  • 如果子类没有实现父类的所有抽象方法,子类必须被定义为抽象类

  • 没有抽象构造方法,也没有抽象静态方法

  • 抽象类中可以有非抽象的构造方法,创建子类的实例时能调用

接口

  • 实现者完全按照接口指定者的方法去实现
  • 方法名要一致,传入参数要一致(数据类型一致),返回值也要一致
  • 修饰符只能写public,如果不写修饰符,系统会自己生成一个 public
  • abstract int get1(); 等效于 int get1(); 不带方法体的都默认为抽象方法
  • 接口之间的继承使用 extends 关键字
  • 抽象类的实现需要使用 extends 进行继承
  • 接口不可以被实例化
  • 实现类必须实现接口的所有方法
  • 实现类可以实现多个接口
    • implements、多个接口使用逗号隔开
  • 接口中的变量都是静态变量(public static final)
接口使用
  • 接口中的成员变量
    • 默认都是 public static final的,必须显示初始化
  • 接口中的方法
    • 默认都是 public abstract的
  • 接口没有构造方法,不能被实例化
  • 一个接口不能实现另一个接口,但可以继承多个其他接口
  • 一个类必须实现接口抽象方法(implements),除非这个类也是抽象类

抽象类 接口

相同点
  • 代表系统的抽象层
  • 都不能被实例化
  • 都能包含抽象方法
    • 用于描述系统提供的服务,不必提供具体实现
不同点
  • 在抽象类中可以为部分方法提供默认实现,而接口中只能包含抽象方法
    • 抽象类便于复用,接口便于维护
  • 一个类只能继承一个直接的父类,但可以实现多个接口
使用原则
  • 接口做系统与外界交互的窗口
    • 接口提供服务
  • 接口本身一旦制定,就不允许随意修改
  • 抽象类可完成部分功能实现,还有部分功能可作为系统的扩展点

总结

  • java中的接口
    • 属性全部都是全局静态变量
    • 方法都是全局抽象方法
    • 无构造方法
  • 一个类可以实现多个接口,非抽象类实现接口时必须实现接口中的全部方法
  • 抽象类利于代码复用,接口便于代码维护

多态

  • 接口的多种不同实现方式即为多态

  • 多态性是允许你将父对象设置成为一个或更多的他的子对象相等的技术

  • 我们在程序中定义的引用变量所指向的具体类型和通过该引用变量的方法调用在编程的时候并不确定,当处于运行期间才确定。就是这个引用变量究竟指向哪一个实例对象,在编译期间是不确定的,只有运行期才能确定,这样不用修改源码就可以把变量绑定到不同的类实例上,让程序拥有了多个运行状态,这就是多态。

  • instanceof 检测对象是否属于类

  • 对象的身份是由创建的类来确定的

  • 在创建对象时,如果使用父类的类型,Person tang = new TangDance(); 那么java会自动向上转型,如果要使用子类方法,需要手动向下转型 ((TangDance)tang).dance();

异常处理

  • try-catch-finally 结构中 try 必须存在,catch和finally是可选的,但是二者必须存在其中一个

  • System.exit(1) || System.exit(0) 0是正常退出,1是非正常退出

  • finally 语句块唯一不执行的情况是在其之前使用 System.exit(1) 退出

  • printStackTrace()的堆栈跟踪功能显示出程序运行到当前类的执行流程,调用该方法输出错误信息 e.printStackTrace();

  • String getMessage();返回异常信息描述字符串,是printStackTrace()输出信息的一部分

  • Java的异常处理是通过5个关键字来实现的:try、catch、 finally、throw、throws

  • image-20210512172359597

  • 在 try-catch 后加上final 表示是否异常都执行final里的语句块

常见的异常类型

image-20210512172910453

多重 catch 块

  • 引发多种类型的异常
    • 排列 catch 语句的顺序:先子类后父类
    • 发生异常时按照顺序逐个匹配
    • 只执行第一个与异常类型匹配的 catch 语句

声明异常

image-20210512174810957

抛出异常
public void setSex(String sex) throws Exception {        if ("男".equals(sex) || "女".equals(sex)) {            this.sex = sex;        } else {            throw new Exception("性别必须是“男”或者“女”");        }    }// 处理异常Person person = new Person();        try{            person.setSex("男");            person.print();        }catch (Exception e){            e.printStackTrace();        }

Java常用类

枚举

  • public enum  Sex {    MALE("男"), FEMALE("女");    private String value;    Sex(String value){        this.value = value;    }    public String getValue() {        return value;    }    public void setValue(String value){        this.value = value;    }    public static void main(String[] args) {        Sex.MALE.setValue(" ");        System.out.println(Sex.MALE.getValue());    }}
    
  • enum 在 Java 中就是一个语法特殊一点的类

  • MALE , FEMALE 写法就是使用默认的无参构造方法建立两个对象,而且是静态的

  • 可以直接使用 enum.MALE直接访问

  • 构造方法都是 private 的,不准使用者在外部使用构造方法再建立对象

  • 枚举创建后就应当禁止变更,只能重新创建

  • 用法

    • 常量:枚举可以将相关常量分组放到一个枚举类型里,且枚举提供了比常量更多的方法
    • switch:使用枚举,可以让代码可读性增强

Math 类

  • java.lang.Math类提供了常用的数学运算方法和两个静态常量E(自然对数的底数) 和PI(圆周率)

  • int random = (int) (Math.random() * 10); //生成一个0-9之间的随机数

  • Random rand=new Random(); //创建一个Random对象for(int i=0;i<20;i++){//随机生成20个随机整数,并显示         int num=rand.nextInt(10);//返回下一个伪随机数,整型的    	 System.out.println("第"+(i+1)+"个随机数是:"+num);} 
    
要生成在[min,max]之间的随机整数,可使用Random类进行相关运算:Random random = new Random();int s = random.nextInt(max)%(max-min+1) + min;random.nextInt(max)表示生成[0,max]之间的随机数,然后对(max-min+1)取模。以生成[1000,10000]随机数为例,首先生成0-10000的随机数,然后对(10000-1000+1)取模得到[0-1000]之间的随机数,然后加上min=1000,最后生成的是1000-10000的随机数另外也可random.nextInt(max - min + 1) + min;

数字转换字符串左边补零

public class TestStringFormat {        public static void main(String[] args) {          int youNumber = 1;          // 0 代表前面补充0          // 4 代表长度为4          // d 代表参数为正数型          String str = String.format("%04d", youNumber);          System.out.println(str); // 0001        }      }

字符串

  • 字符串常量池:在创建对象赋值时会优先在常量池中查找有无相同的值进行直接赋值,使用相同的地址空间
  • equalsIgnoreCase() :不区分大小写比较字符串
  • toLowerCase() 全部转小写比较;toUpperCase() 全部转大写比较
  • charAt(int index); 找到字符串对应下标的元素
  • startWith(); 字符串开头的字符

字符串连接

  • 使用 "+" 号连接符
  • 使用 concat() 方法

字符串常用提取方法

image-20210514171512437

包装类

  • 包装类不是用来取代基本数据类型的,而是在基本数据类型需要用对象表示时使用

包装类把基本类型数据转换为对象

  • 每个基本类型在 java.lang包中都有一个相应的包装类

包装类的作用

  • 提供了一系列实用的方法
  • 集合不允许存放基本数据类型,存放数字时要用包装类型
  • image-20210514165255889

包装类的构造方法

所有包装类都可将与之对应的基本数据类型作为参数,来构造他们的实例
  • 除Character类外,其他包装类可将一个字符串作为参数构造它们的实例

  • 	Integer i = new Integer(34);       Double d = new Double(98.7);       Boolean b = new Boolean(true);       Character c = new Character('a');        Integer i1 = new Integer("2");       Double d1 = new Double("98.7");       Boolean b1 = new Boolean("true");
    
  • Boolean类构造方法参数为String类型时,若该字符串内容为true(不考虑大小写),则该Boolean对象表示true,否则表示false

  • 当Number包装类构造方法参数为String 类型时,字符串不能为null,且该字符串必须可解析为相应的基本数据类型的数据,否则编译不通过,运行时会抛NumberFormatException异常

包装类的常用方法

  • 包装类转换为基本类型:XXXValue()

    • Integer integerId=new Integer(25);int intId = integerId.intValue();
      
  • toString() 以字符串形式返回包装对象表示的基本类型数据(基本类型->字符串)

    • String sex = Character.toString('男');String id = Integer.toString(25);
      
  • parseXXX():把字符串转换为相应的基本数据类型数据(Character除外)(字符串->基本类型)

    • int num = Integer.parseInt("36");boolean bool = Boolean.parseBoolean("false");
      

装箱和拆箱

  • 基本类型和包装类型的转换

  • 装箱:基本类型转换为包装类的对象

    • int a = 3;Integer b = a; // 装箱,等同于Integer b=Integer.valueOf(a) System.out.println(b);
      
  • 拆箱:包装类对象转换为基本类型的值

    • Integer b = new Integer(3);int a = b; // 拆箱,等同于int a=b.intValue();System.out.println(a);
      
  • Integer a = new Integet(123);Integer b = new Integet(123);a != b; // 因为a和b属于对象,指向堆里不同的两块区域Integer c = 123;Integer d = 123;c == d; // 他们都调用了Integer.valueOf(int i) 方法,属于自动装箱,没有创建新的对象Integer e = 129;Integer f = 129;e != f; // 因为只有-128 ~ 127 的以内的数值才会添加缓存指向堆内同一地址int g = 59;Integer h = new Integer(59);g == h; // 因为在比较的时候 h 会自动拆箱,就只会比较数值
    

操作日期时间

  • 获取当前时间

    • java.util.Date类:表示日期和时间

    • 格式化时间:java.text.SimpleDateFormat类

    • //创建日期对象Date date = new Date(); //定制日期格式SimpleDateFormat formater = new SimpleDateFormat("yyyy- MM-dd HH:mm:ss");String now = formater.format(date);System.out.println(now);
      
  • Calendar 类

    • 抽象类,java.util.Calendar
    • 用于设置和获取日期/时间数据的特定部分
    • Calendar类提供一些方法和静态字段来操作日历

集合框架与泛型

List

  • List<String> list = new ArrayList();
  • 编译后的 class文件 不会包含任何泛型信息
  • 插入单个元素: List.add(int index, E element) 此方法没有返回值
  • 数组拼接:List.add(int index, List list2) 此方法返回值为 Boolean
  • 返回指定下标位置的元素: list.get(int index);
  • 返回元素首次出现的位置: list.indexOf(Object o);
  • remove 方法
    • list.remove(Object o); 删除列表中元素 o,返回值为 Boolean
    • list.remove(int index); 删除列表对应下标的元素,返回值为元素本身
  • 替换方法:list.set(int index, Object o); 将对应下标位置元素替换为元素 o
  • list.subList(int fromIndex, int toIndex); 返回from到to的元素集合,左闭右开

HashMap

  • 在 map 中,键(key)具有唯一性
  • Map<String, String> countries = new HashMap(); HashMap的创建
  • 允许出现键为null的情况
  • HashMap 数据存储的顺序与放置的顺序无关
  • 清空 HashMap 内所有数据:mapName.clear();
  • 输出所有键:mapName.kaySet();
  • 输出所有值:mapName.values();
  • 直接输出 HashMap 名字,输出所有对值
  • 通过键查找对应的值:mapName.get( keyName );
  • 判断是否存在键:mapName.containsKey( keyName )

Map 的 put() 插入方法

  1. 对 key 进行 hash 运算,得到一个数字值

  2. putValue() , 把键值对对象封装成 Node 对象 Map.Entery

  3. 调整一维数组的长度,上面创建的 Node 对象就存放在这个数组里面

  4. (n - 1) & hash 通过 hash 值与数组的长度与运算计算数组下标

    当前的键值对对象存放在数组的位置

Hashtable

HashSet

  • Set<String> set = new HashSet(); set集合创建
  • set集合允许插入空值 set.add(null);
  • LinkedHashSet<String> set = new LinkedHashSet(); 有序set集合创建

collection

  • Collection<String> values = countries.values(); 创建collection

LinkedList

  • 内部是链表结构,在插入数据时速度较快

  • 需要在列表头尾部添加删除元素时适用此方法

I \ O流

  • 输入流:从介质到内存,通过一个虚拟的通道,把信息从介质输入到内存
  • 输出流:从内存到介质,通过一个虚拟的通道,把信息从内存输出到介质

文件操作

public static void main(String[] args) {        File fl = new File("src\\test.txt");        if(fl.exists()){            System.out.println("文件已经存在");        }else{            // 创建一个文件            try{                fl.createNewFile(); // 创建文件的方法                System.out.println("文件创建成功");            }catch (Exception e){                e.printStackTrace();            }        }        System.out.println(fl.exists()); // 判断文件是否存在        System.out.println(fl.getName()); // 获取文件的名字        System.out.println(fl.getParent()); // 获取父路径        System.out.println(fl.getPath()); // 相对路径        System.out.println(fl.getAbsolutePath()); // 绝对路径        System.out.println(fl.isDirectory()); // 判断是否为文件夹        System.out.println(fl.length()); // 文件大小        System.out.println(fl.lastModified()); // 文件最后修改时间        System.out.println(fl.canRead()); // 可读        System.out.println(fl.canWrite()); // 可写        	fl.renameTo(new File()); // 传入参数为 file 对象    	fl.delete(); // 删除文件    }

字节流

  • 以字节为基本单位 , 在 java.io包中,大部分操作继承InputStream(输入字节流)类和OutputStream(输出字节流)类

FileInputStream

  • 继承与InputStream 类,这是一个文件输入流,进行文件读操作的最基本的类

  • 作用是将文件中的数据输入到内存中,用其进行读文件的操作

  • 由于字节流的原因,无法读取中文字符

    public static void main(String[] args) {        try{            File fl = new File("src\\12.txt");            FileInputStream fi = new FileInputStream(fl);            for(int i = 0; i < fl.length(); i++){                char ch = (char) fi.read();                System.out.print(ch);            }            fi.close();        } catch (Exception e) {            e.printStackTrace();        }    }
    

FileOutputStream

  • 文件输出流,继承于OutputStream类
  • 将数据存储到文件中,用来执行写文件的操作
public static void main(String[] args) throws IOException {        File file = new File("src\\12.txt");        FileOutputStream fl = new FileOutputStream(file, true); // true 表示不覆盖当前文本内容        String str = "Hello, world!";        byte[] buff = str.getBytes(); // 将字符串转换为字节数组        try{            fl.write(buff);        } catch (Exception e) {            e.printStackTrace();        }finally {            fl.close();        }    }

字符流

  • 两个字节为基本单位,专门处理字符串和文本,对于字符流进行操作的类主要是Reader(读取流)类和 Writer(写入流)类。

FileWriter

  • 可以直接写入字符串,不用将字符串转换为字节数组
public static void main(String[] args) throws IOException {        String[] str = {"123", "456", "789"};        File file = new File("src\\test.txt");        FileWriter f = null; // 创建文件写入对象        BufferedWriter fl = null; // 创建字符流写入对象        try{            // 分开写写入对象            f = new FileWriter(file);            fl = new BufferedWriter(f);            // 循环获取需要写入的元素            for(int i = 0; i < str.length; i++){                fl.write(str[i]);                fl.newLine();            }        } catch (Exception e) {            e.printStackTrace();        }finally {            fl.close();            f.close();        }    }

FileReader

public static void main(String[] args) throws IOException {        File file = new File("src\\test.txt");        FileReader f = null; // 文件读取对象        BufferedReader bf = null; // 字符流对象		        try{            f = new FileReader(file);            bf = new BufferedReader(f);			// 循环打印文件中的每行数据            String str = null;            while ((str = bf.readLine()) != null){                 System.out.println(str);            }        } catch (Exception e) {            e.printStackTrace();        }finally {            bf.close();            f.close();        }    }

关闭文件代码

public static void close(Closeable closeable){ // 传入后代类
        if(closeable != null){
            try{
                closeable.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }

反射机制

  • 反射就是将类别的各个组成部分进行剖析,得到每个组成部分,然后对每一部分进行操作
  • 使用场景:在不方便创建对象时使用
  • img
    • Class 类:代表一个类
    • Field类:代表类的成员变量
    • Method类:代表类的方法
    • Constructor类:代表类的构造函数

使用反射创建对象

  • // 必须保证默认的构造方法正常存在,且不是私有
    Class<Person> p = Person.class;
    Person s = p.getConstructor().newInstance();
    
  • // 此方法忽略访问权限Class<Person> p = Person.class;Person s = p.newInstance();
    

实例

// 实体对象package reflex.begin;public class SimpleReflectObject {    private String name;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}// 反射实现package reflex.begin;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class ReflexDemo {    public static void main(String[] args) {        try{            // 加载类,class 是描述类的结构信息的一个类            Class clz = Class.forName("reflex.begin.SimpleReflectObject");            Object obj = clz.newInstance(); // 实例化对象            // 根据方法名称和参数类型,在类中获取方法对象            Method m = clz.getDeclaredMethod("setName", String.class);            Method[] methods = clz.getMethods();            m.invoke(obj, "反射"); // 执行具体对象的 setName 方法            // 第一个参数是对象本身, 后面的是方法传入参数值            System.out.println(((SimpleReflectObject)obj).getName());            Method ml = clz.getDeclaredMethod("getName");            System.out.println("返回名字:" + ml.invoke(obj));            // 获取实体内所有方法            for(Method method : methods){                System.out.println(method.getName());            }            System.out.println(obj.getClass().getName());        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {            e.printStackTrace();        }    }}

java.lang.Class.getDeclaredMethod()

  • 此方法返回一个Method对象,它反映此Class对象所表示的类或接口的指定已声明方法。

多线程

  • Java执行程序的过程:进程(JVM) -> 产生一个线程 -> 线程执行main()方法

  • 一个进程中同时运行多个线程用来完成不同的工作

    • 进程是操作系统进行资源分配的基本单位,线程是操作系统进行调度的基本单位
      • 因为线程不能单独存在,线程是依附于进程
    • 进程时应用程序的实例,有独立的内存空间和系统资源
    • 线程是CPU调度和分派的基本单位,是进程中执行运算的最小单位,可完成一个独立的顺序控制流程
  • Java虚拟机本身不能创建线程,实际是由操作系统建立线程

  • 每次创建线程,都是由操作系统来做,从用户态到核心态的一个转换,很消耗资源

  • 线程之间的切换(因为它的时间片到期,等某项资源等不到),消耗资源很大的地方

  • 线程资源共享也会带来性能问题

  • 执行多线程时需要使用 start() 方法,直接调用则未实现多线程

  • 并发 并行
    • 并发:多个cpu实例或者多台机器同时执行一段处理逻辑,真正的同时
    • 并行:通过cpu调度算法,让用户看上是同时运行,在cpu层面并不是同时。并发在场景中有公用的资源,我们会用 TPS或者 QPS来反应这个系统的处理能力
      • TPS(Transactions Per Second):每秒事务数,
      • QPS(Queries Per Second):单个进程每秒请求服务器成功的次数
      • 事务表示客户端发起请求到收到服务器端最终响应的整个过程,这是一个TPS,为了处理第一次请求可能会引起后续多次对服务端的访问才能完成这次工作,每次访问都算一个QPS,所以 一个TPS可能包含多个QPS
  • 分时
    • 操作系统把每个可运行的时间单位分片
  • 同步
    • Java中的同步是指通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。
  • 时间片
    • CPU会在具体的时间点,执行某个进程的某个线程
    • 没有得到CPU时间片的程序(进程)不可能运行,处于一种等待状态,等待CPU重新分配时间片
    • 线程什么时候获得CPU的时间片,程序是无法控制的
    • 时间片的分配是由操作系统进行控制

主线程

  • Thread 类支持多线程编程
  • 一个程序的main()方法就是主线程入口
  • 主线程是产生其他子线程的线程
  • 主线程必须最后完成执行,因为他执行关闭动作

线程的创建和启动

  • Thread.currentThread() 返回当前线程对象

  • 继承 java.lang.Thread 类;编写简单,可直接操作线程,适用于单继承

    • 定义MyThread类继承Thread类

    • 重写Run()方法,编写线程执行体

    • 创建线程对象,调用 start() 方法启动线程

    • public class MyThread extends Thread {    // 重写 run() 方法    public void run(){        for(int i = 0; i < 10; i++){            System.out.println(Thread.currentThread().getName() + ":" + i);        }    }    public static void main(String[] args) {        MyThread l = new MyThread();        l.start(); // 启动线程        // 多个线程交替执行,并不是真正的并行        // 线程每次执行时长由时间片决定      MyThread l1 = new MyThread();        MyThread l2 = new MyThread();        l1.start()        l2.start()    }}
      
    • 启动线程直接调用run()方法就不属于多线程,而是只有主线程一个线程在执行

      • image-20210602185052808
  • 实现 java.lang.Runnable 接口;避免单继承局限性

    • 定义 MyRunnable 类实现 Runnable 接口

    • 实现 Run() 方法,编写线程执行体

    • 创建线程对象,调用 start() 方法启动线程

    • public class MyRunnable implements Runnable {    public void run() {        for(int i = 0; i < 10; i++){            System.out.println(Thread.currentThread().getName() + ":" + i);        }    }    public static void main(String[] args) {        MyRunnable myRunnable = new MyRunnable();        Thread thread = new Thread(myRunnable);        Thread thread1 = new Thread(myRunnable);        thread.start();        thread1.start();    }}
      

线程的状态

  • image-20210602192121889
  • 就绪状态在源码中不存在,就绪状态与运行状态在一起
  • 线程终止的状态时,打断标志为 false
  • 要查询一个线程的状态可以使用 getState() 方法

线程调度

  • 线程调度指按照特定机制为多个线程分配CPU的使用权

  • setPriority(int newPriority) 线程的优先级
    • 更改线程优先级:可以提高线程获取 cpu时间片 的概率
      • 优先级由 1~10 表示,1最低,默认优先级为10
      • MAX_PRIORITY 最高优先级
      • MIN_PRIORITY 最低优先级
  • jion() 方法 线程强制执行
    • 使当前线程暂停执行,等待其他线程结束后再执行本线程
    • 当下一个线程的输入依赖于上一步的输出时就要使用 jion方法 来阻塞线程
    • jion(); 传入时间参数,规定大概让出时间片的时间,在规定时间附近归还时间片
  • sleep(long millis) 线程休眠
    • 线程休眠时处于阻塞状态
    • 在指定毫秒数内让当前正在执行的线程休眠
  • yield() 线程礼让
    • 只是提供一种可能,不能保证一定会礼让
    • 暂停当前线程,允许其他具有相同优先级的线程获得运行机会
    • 该线程处于就绪状态,不转为阻塞状态
  • interrupt()
    • 中断线程
  • isAlive()
    • 测试线程是否处于活动状态

synchronized

  • 使用锁修饰的方法控制对类成员变量的访问

  • synchronized Java中最基本的互斥同步手段

  • 尽量缩小锁的范围,提高效率

  • 对象线程身上带有一个计数器,当计数器为0是未被上锁,为1是被上锁

  • Object类在 jdk 设计时就已经把这个计数器设计了,写的任何类都能直接继承

  • 当对象线程执行一个带synchronized 方法时,计数器被置为1,其他线程执行方法时必须等他执行结束,他也必须等该方法执行完毕才能执行其他synchronized 方法

  • 死锁
    • 多个线程同时被阻塞,他们中的一个或者全部都在等待某个资源被释放,由于线程被无限制阻塞,,因此程序无法正常终止

    • 死锁产生的条件:
      1. 互斥使用,即当资源被一个线程使用(占用)时,别的线程不能使用
      2. 不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由占有者主动释放
      3. 请求和保持,即当资源请求者在请求其他资源的同时保持对原资源的占有
      4. 循环等待,即存在一个等待队列:P1占用P2的资源,P2占用P3的资源,P3占用P1的资源,这样就形成了一个等待环路
    • A锁 B锁;线程1 线程2

      • 线程1现先获得A锁后获得B锁;线程2先获得B锁再获得A锁
多个并发线程访问同一资源的同步代码块时
  • 同一时刻只能有一个线程进入 synchronized (this) 同步代码块
  • 当一个线程访问一个 synchronized (this) 同步代码块是,其他 synchronized (this) 同步代码块同样被锁定
  • 当一个线程访问一个 synchronized (this) 同步代码块时,其他线程可以访问该资源的非 synchronized (this) 同步代码块

notify() 和 notifyAll()

  • 单唤醒和多唤醒
  • 单唤醒效率高,但容易死锁
  • notifyAll() 全唤醒,效率较低

生产者消费者实例

// 生产方法public class Producer extends Thread {    private int needNum;    private WareHouse wareHouse;    public Producer(int needNum, WareHouse wareHouse){        this.needNum = needNum;        this.wareHouse = wareHouse;    }    public void run(){        this.wareHouse.produce(needNum);    }}// 消费方法public class Consumer extends Thread {    private int needNum; // 每次消费的产品数量    private WareHouse wareHouse; // 仓库    public Consumer(int needNum, WareHouse wareHouse){        this.needNum = needNum;        this.wareHouse = wareHouse;    }    public void run(){        this.wareHouse.consume(needNum);    }}// 仓库方法public class WareHouse {    private final int MAX_SIZE = 100; // 仓库最大容量    private int currentNum; // 现有库存    public WareHouse(int currentNum){        this.currentNum = currentNum;    }    public synchronized void produce(int needNum){        while (true){            while (this.currentNum + needNum > this.MAX_SIZE){                // if(this.currentNum + needNum > this.MAX_SIZE){                System.out.println(Thread.currentThread().getName() + "要生产的数量" + needNum + "已经超过剩余库存容量"                + (this.MAX_SIZE - this.currentNum) + ",暂时不能进行生产任务");                try{                    this.wait(200); // 不需要释放锁,进入等待序列                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            this.currentNum += needNum; // 满足生产条件,进行产品生产            System.out.println(Thread.currentThread().getName() + "已经生产了" + needNum + "现有库存容量时" + this.currentNum);            this.notifyAll(); // 唤醒在此对象监视器上等待的所有线程        }    }    public synchronized void consume(int needNum){        while (true){            while (this.currentNum < needNum){                System.out.println(Thread.currentThread().getName() + "要消费的产品数量" + needNum + "已经超过剩余库存数量"                + this.currentNum + "暂时不能进行消费任务");                try{                    this.wait(200);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            this.currentNum -= needNum; // 满足消费条件,进行产品消费            System.out.println(Thread.currentThread().getName() + "已经消费了" + needNum + "现在库存容量是" + this.currentNum);            this.notifyAll();        }    }}// 测试public class TestWareHouse {    public static void main(String[] args) {        WareHouse wareHouse = new WareHouse(20);        Consumer c1 = new Consumer(20, wareHouse);        Consumer c2 = new Consumer(30, wareHouse);        Consumer c3 = new Consumer(10, wareHouse);        c1.setName("c1");        c2.setName("c2");        c3.setName("c3");        // 生产者初始化        Producer p1 = new Producer(10, wareHouse);        Producer p2 = new Producer(10, wareHouse);        Producer p3 = new Producer(10, wareHouse);        Producer p4 = new Producer(10, wareHouse);        p1.setName("p1");        p2.setName("p2");        p3.setName("p3");        p4.setName("p4");        c1.start();        c2.start();        c3.start();        p1.start();        p2.start();        p3.start();        p4.start();    }}
  • 内部使用while循环是因为调用了notifyAll() 唤醒全部对象时如果唤醒了不应该执行的对象时需要继续等待

volatie

  • 我们使用程序建立对象的时候, jvm 会给这个对象分配内存空间
  • jvm 会自动回收对象不使用的内存(gc 垃圾回收机制)
  • 正在被程序使用的对象不能被内存回收
  • jvm 怎么判断对象是否在使用
  • jvm 没办法回收内存,导致内存不够用就叫作内存泄漏

并发编程三大概念

  • 原子性
    • 一个操作或多个操作要么全部执行并且执行的过程中不会被任何因素打断,要么就都不执行。
    • 在Java中,对 基本数据类型 的变量读取与赋值操作是原子性操作
  • 有序性
  • 可见性

线程池

线程池创建线程数量

  • 业务场景
    1. 单个任务的处理时长:CPU核数 或者 CPU核数 * 2
    2. 单个任务的处理时间短,并发处理高:((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目
    3. 事件驱动:一个线程负责处理多个任务 CPU核数 或者 CPU核数 * 2
    4. NIO:事件驱动

StringBuilder 和 StringBuffer

  • StringBuilder sb2 = new StringBuilder(); 不带锁,效率高
  • StringBuffer sb3 = new StringBuffer(); 带锁
  • 字符串的拼接会频繁创建对象

Java 设计模式、设计原则

策略模式 Strategy Pattern

  • 理解:一个行为多种实现,基于一种行为,不同对象进行管理。比如第三方支付可以选择银行卡、微信、支付宝进行支付,最终都能支付成功达到相同的目的。
  • 意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
  • 主要解决:在多种算法相似的情况,使用 if…else 难以维护,且策略模式满足高内聚低耦合要求
  • 优点
    • 算法可以自由切换
    • 避免使用多种添加判断,方便维护
    • 扩展性良好便于业务拓展与封装,代码扩展不需要在源代码基础上改动,不违背开闭原则
  • 缺点
    • 策略类比较多
    • 所有策略类都需要对外暴露
  • 策略模式属于对象的行为模式
  • 针对一组算法,将每一个算法封装到具有共同接口的独立类中

网络编程

  • 操作系统提供用来支撑网络通讯的方法:协议栈
  • 编程语言对这些操作系统网络命令(方法)进行封装

数据结构

数据结构 优点 缺点
数组 插入快,如果知道下标可以快速进行存放 查找慢,删除慢,大小是固定的
有序数组 比无序数组查找快 删除和插入慢,固定大小
提供后进先出的存取 存取其他项慢
队列 提供先进先出的存取 存取其他项慢
链表 插入、删除快 查找慢
二叉树 插入、查找、删除、删除都快(如果树保持平衡) 删除算法复杂
红-黑树 插入、查找、删除都很快、树总数平衡的 算法复制
2-3-4树 查找、插入、删除都快、树总是平衡的、类似的树对磁盘存储有用 算法复杂
哈希表 如果关键字已知则存取快,插入快 删除慢,如果不知道关键字则存取都慢,对存储空间使用不充分
插入、删除快,对最大数据项的存取很快 对其他数据项存取慢
对现实世界建模 有些算法慢且复杂

算法

基础

// 最大公约数public static int gcd(int i, int j){    if(j == 0) return i;    int x = i % j;    return gcd(j, x);}

排序

  • 快速排序 >> 归并排序 >>>>> 插入排序 >> 选择排序 >> 冒泡排序

MySQL

(https://www.cnblogs.com/yuan-liu/p/15255452.html)

  • 如果起名与数据库关键字重复,就使用 `` 包起来
  • IFNULL(NULL, 1);
  • 截取字符串 substring(studentName, 1,1) from student;
  • [ IF EXISTS ] 此关键字用于判断表或数据库等是否存在
  • FIELD1 CHAR(4), // 最多四个字符,固定长度,不够会自动设置空格四位自动补齐
    FIELD2 VARCHAR(10), // 最多10个字符,不够10位实际长度去调整
  • auto_increment 设置字段值为自增长类型
  • distinct 查询时去重
  • COLLATE utf8_general_ci mysql 的排序规则,ci Case Insensitive的缩写,即是大小写无关,不区分大小写

常见的关系型与非关系型数据库及其区别

关系型数据库

MySQL、SQLServer、SQLite、ORACLE、MariaDB...

​ 关系型数据库最典型的数据结构是表,由二维表及其之间的联系所组成的一个数据组织

优点:

  1. 易于维护:都是表结构,格式统一
  2. 使用方便:SQL语句通用,可用于复杂查询
  3. 复杂操作:支持SQL,可用于一个表以及多个表之间非常复制的查询

缺点:

  1. 读写性能比较差,尤其是数据量大的高效率读写
  2. 固定的表结构,灵活度稍欠
  3. 高并发读写需求,传统关系数据库来说,硬盘 I/O是一个很大的麻烦从

非关系型数据库

redis、mongoDB、Neo4j...

​ 非关系型数据库严格来说不是一种数据库,应该是一种结构化存储方法集合,可以是文档或者键值对等

优点:

  1. 格式灵活:存储数据的格式可以是 key,value 形式、文档形式等,使用场景广泛,而关系数据库则只支持基础类型
  2. 速度快:nosql 可以使用硬盘或者随机存储器作为载体,而关系数据库只能使用硬盘
  3. 高扩展性
  4. 成本低: nosql 数据库部署简单,基本都是开源软件

缺点;

  1. 不提供 sql 支持,学习和使用成本较高
  2. 无事务处理
  3. 数据结构相对复杂,复制查询方面欠缺

nosql

相关概念

sql语句分类

名称 解释 命令
DDL (数据定义语言) 定义和管理数据对象, 如数据库,数据表等 CREATE、DROP、ALTER
DML (数据操作语言) 用于操作数据库对象中所包含的数据 INSERT、UPDATE、DELETE
DQL (数据查询语言) 用于查询数据库数据 SELECT
DCL (数据控制语言) 用来管理数据库的语言,包括管理权限及数据更改 GRANT、COMMIT、ROLLBACK

数据库引擎(表类型)

名称 MyISAM InnoDB
事务处理 不支持 支持
数据行锁定 不支持 支持
外键约束 不支持 支持
全文索引 支持 不支持
表空间大小 较小 较大,约2倍

命令行操作数据库

> net start user_name; #启动数据库> net stop user_name; #关闭数据库> mysql -u[user_name] -p[password] #命令行运行数据库CREATE DATABASE if not exists database_name DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci # 创建数据库并规定字符集SELECT user FROM mysql.user; # 查看数据库所有用户show databases; #查看所有的数据库use database_name #进入数据库show tables # 查看当前数据库里的所有表desc table_name; #查看表结构,等价于describe table_name;show full columns from table_name; #查看当前表的所有列信息show create table table_name; #显示表创建语句show processlist;  #检查数据库进程信息

where条件限制

  • 若不设置条件限制则会影响额外数据

  • 数值类型的值才能做运算

  • 相同数据类型的值才能作比较

  • 运算符 含义 范例 结果
    = 等于 5=6 false
    <> != 不等于 5!=6 true
    > 大于 5>6 false
    < 小于 5<6 true
    >= 大于等于 5>=6 false
    <= 小于等于 5<=6 true
    BETWEEN 在某个范围之间 BETWEEN 5 AND 10 -
    AND 并且 5>1 AND 1>2 false
    OR 5>1 OR 1>2 true
操作符名称 语法 描述
AND或&& a AND b 或 a && b 逻辑与,同时为真,结果才为真
OR或|| a OR b 或 a||b 逻辑或,只要一个为真,则结果为真
NOT或! NOT a 或 !a 逻辑非,若操作数为假,结果则为真
操作符名称 语法 描述
IS NULL a IS NULL 若操作符为NULL,则结果为真
IS NOT NULL a IS NOT NULL 若操作符不为NULL,则结果为真
BETWEEN a BETWEEN b AND c 若a范围在b与c之间则结果为真
LIKE a LIKE b SQL模式匹配,若a匹配b,则结果为真
IN a IN (a1,a2,a3,….) 若a等于a1,a2…中的某一个,则结果为真

数据库操作

  • 数据库基本操作主要就是 增删改查 四个操作,其中查询较困难

创建(create)

  • 属于 DDL 的一种
  • TIMESTAMP DEFAULT CURRENT_TIMESTAMP 数据类型为 timestamp 默认值为 current_timestamp 是字段默认获取当前系统时间插入

数据表创建

 CREATE   TABLE [ IF NOT EXISTS ]    `表名`   (  # ``反引号用于引入关键字        `字段名1`    列类型 [ 属性 ]  [ 索引 ] [注释] ,        `字段名2`   列类型 [ 属性 ]  [ 索引 ] [注释] ,          … …             `字段名n`   列类型 [ 属性 ]  [ 索引 ] [注释] )  [  表类型 ] [ 表字符集 ] [注释] ;
列类型
  • 规定数据库中该列存放的数据类型

  • 其中分为以下几种类型:

  • 数值类型
    • 类型 说明 取值范围 存储需求
      tinyint 非常小的数据 有符值: -27 ~ 27-1 无符号值:0 ~ 28-1 1字节
      smallint 较小的数据 有符值: -215 ~ 215-1 无符号值: 0 ~ 216-1 2字节
      mediumint 中等大小的数据 有符值: -223 ~ 223-1 无符号值: 0 ~ 224-1 3字节
      int 标准整数 有符值: -231 ~ 231-1 无符号值:0 ~ 232-1 4字节
      bigint 较大的整数 有符值: -263 ~263-1 无符号值:0 ~264-1 8字节
      float 单精度浮点数 ±1.1754351e -38 4字节
      double 双精度浮点数 ±2.2250738585072014e -308 8字节
      decimal 字符串形式的浮点数 decimal(m, d) m个字节
  • 字符串类型
    • 类型 说明 最大长度
      char[(M)] 固定长字符串,检索快但费空间, 0 <= M <= 255 M字符
      varchar[(M)] 可变字符串 0 <= M <= 65535 变长度
      tinytext 微型文本串 28–1字节
      text 文本串 216–1字节
  • 日期和时间型数值类型
    • 类型 说明 取值范围
      DATE YYYY-MM-DD,日期格式 1000-01-01~ 9999-12-31
      TIME Hh:mm:ss ,时间格式 -838:59:59~838:59:59
      DATETIME YY-MM-DD hh:mm:ss 1000-01-01 00:00:00 至 9999-12-31 23:59:59
      TIMESTAMP YYYYMMDDhhmmss 格式表示的时间戳 197010101000000 ~2037年的某个时刻
      YEAR YYYY 格式的年份值 1901~2155
  • NULL类型
    • 理解为 “没有值” 或 “未知值”
    • 不能用 NULL 进行算术运算,结果是NULL
  • 选择数据类型
    • 整数和浮点数
    • 日期类型
    • char 和 varchar
  • 复合类型
    • ENUM 类型
      • ENUM 类型因为只允许在集合中取得一个值,有点类似于单选项。在处理相互排拆的数据时容易让人理解,比如人类的性别。ENUM 类型字段可以从集合中取得一个值或使用 null 值,除此之外的输入将会使 MySQL 在这个字段中插入一个空字符串。另外如果插入值的大小写与集合中值的大小写不匹配,MySQL 会自动使用插入值的大小写转换成与集合中大小写一致的值。
      • ENUM 类型在系统内部可以存储为数字,并且从 1 开始用数字做索引。一个 ENUM 类型最多可以包含 65536 个元素,其中一个元素被 MySQL 保留,用来存储错误信息,这个错误值用索引 0 或者一个空字符串表示。
      • MySQL 认为 ENUM 类型集合中出现的值是合法输入,除此之外其它任何输入都将失败。这说明通过搜索包含空字符串或对应数字索引为 0 的行就可以很容易地找到错误记录的位置。
    • SET 类型
      • SET 类型与 ENUM 类型相似但不相同。SET 类型可以从预定义的集合中取得任意数量的值。并且与 ENUM 类型相同的是任何试图在 SET 类型字段中插入非预定义的值都会使 MySQL 插入一个空字符串。如果插入一个即有合法的元素又有非法的元素的记录,MySQL 将会保留合法的元素,除去非法的元素。
      • 一个 SET 类型最多可以包含 64 项元素。在 SET 元素中值被存储为一个分离的“位”序列,这些“位”表示与它相对应的元素。“位”是创建有序元素集合的一种简单而有效的方式。并且它还去除了重复的元素,所以 SET 类型中不可能包含两个相同的元素。
      • 希望从 SET 类型字段中找出非法的记录只需查找包含空字符串或二进制值为 0 的行。
数据字段类型
  • unsigned
    • 无符号的
    • 申明该列数据不允许负数
  • zerofill
    • 0填充的
    • 不足位数的用0来填充,如 int(3) 值为5时,插入表内为 005
  • auto_increment
    • 自动增长的,每添加一条数据,自动在上一个记录数上加 1
    • 通常用于设置主键,且为整数类型
    • 可以定义起始值得=和步长
  • NULL 和 NOT NULL

    • 数据值默认为 NULL ,即没有插入该列的值
    • 如果设置为 NOT NULL , 则该列必须有值
  • default
    • 默认的,用于设置默认值
    • 例如:性别默认为男,若插入值不特别指定则性别都为男
  • comment
    • 注释,此关键字加上注释内容,内容用引号引起来
    • 例: create table student (id int(6) comment "学号" ) comment "学生表";
表类型
  • 表类型设置CREATE TABLE 表名(省略一些代码) ENGINE = InnoDB;

  • 常见表类型:MyISAM、InnoDB 、HEAP、BOB、CSV等

    • 使用MyISAM: 节约空间及相应速度

    • 使用InnoDB: 安全性,事务处理及多用户操作数据表

    • InnoDB类型数据表只有一个*.frm文件,数据文件为上一级目录的 ibdata1 文件

    • MyISAM类型数据表对应三个文件:

      *.frm -- 表结构定义文件

      *.MYD -- 数据文件

      *.MYI -- 索引文件

表字符集
  • 设置方法与表类型设置一致,例:CHARSET= utf8;

复制表

CREATE TABLE student_demo AS SELECT * FROM student

复制 student 表数据到 student_demo 表,只会复制数据,不会带主键以及自动增长等属性

alert

# 修改表名ALTER TABLE demo2 RENAME AS demo3;# 修改字段ALTER TABLE demo3 CHANGE NAME username VARCHAR(32) NOT NULL; # 添加字段ALTER TABLE demo3 ADD PASSWORD VARCHAR(32) NOT NULL;# 删除字段ALTER TABLE demo3 DROP PASSWORD;

增(insert)

  • INSERT INTO 表名 [ ( 字段1, 字段2, 字段3, … ) ] VALUES ( '值1', '值2', '值3', …)
    • 插入单行使用 values 效率高,插入多行时使用 value 效率更好
  • 字段部分可省略,但是顺序与类型必须一致
  • 插入多行数据只需要在values后面加括号传值
  • 其他方式:INSERT INTO 表名 SET 字段名1=值1[,字段名2=值2,…]
  • 插入列是自动增长或又默认值可以使用 'default' 占位,系统会自动生成对应的值

插入分层行

​ 插入母表数据同时插入子表

通过 LAST_INSERT_ID() 函数来获取前面一次插入数据的 id 来作为子表的 id 插入

-- 案例INSERT INTO orders (customer_id, order_date, status)VALUES (1, '2019-01-01', 1);INSERT INTO order_itemsVALUES(LAST_INSERT_ID(), 1, 1, 2.95);

使用子查询插入

​ 从其他表查询数据插入到目标表

INSERT INTO order_demoSELECT * FROM ordersWHERE order_date < '2021-01-01'

删(delete)

  • DELETE FROM 表名 [ WHERE condition ];

  • delete 只是在数据文件中吧删除字段进行标记

  • truncate 在物理文件上操作,彻底删除

    • TRUNCATE [TABLE] table_name
    • 完全清空表数据,表结构、索引、约束不变。且速度比 delete 快
# 删除数据表;if exists为可选,会判断是否存在该表,如不存在则抛出错误 DROP  TABLE  [ IF  EXISTS ]   表名

改(update)

  • UPDATE 表名 SET column_name = value [ , column_name2 = value2, …. ] [ WHERE condition ];
  • ncolumn_name 为要更改的数据列
  • values 内的数据是更新后的值
  • 若不加 where 限制会改变整个表的数据

批量修改表内数据

使用 case 语法,对符合要求的 id 数量进行运算

UPDATE batch_update SET amount = 	CASE id		WHEN 1 THEN amount + 10        WHEN 3 THEN amount + 20	ENDWHERE id in (1, 3);

查(select)

查询合并行并求对应值

SELECT moneyType AS '操作裂类型', SUM(transactionBalance) AS '总金额' FROM transtable GROUP BY moneyType;

跨数据库查询

SELECT id, name FROM school.student 只需要在表名前加上 '数据库名.' 即可切换到对应数据库

select 语法

  • {} 里面的内容是必须有的;[] 部分内容则是可选的
SELECT   [ALL | DISTINCT] {  * |  table.* | [ table.field1 [ as  alias1] [, table.field2 [as  alias2]][, …]] }FROM  table_name  [ as  table_ alias  ]    [ left|out|inner  join  table_name2 ]    #联合查询	[ WHERE  … ]   	#指定结果需满足的条件	[ GROUP BY …]	#指定结果按照哪几个字段来分组	[ HAVING …]	#过滤分组的记录必须满足的次要条件	[ ORDER BY… ]	#指定查询记录按一个或者多个条件排序	[ LIMIT  {   [ offset,] row_count    |   row_count OFFSET offset   }] ;  #指定查询的记录从哪条至哪条
  • 查询数据表中的所有内容:select * from table_name; 此方法效率低,不推荐
  • 指定列查询: select studentName, studentNo from student; 只查询表中对应列的数据
  • 查询多个表中有相同字段时:select student.studentNo, from student; 使用 表名.列名 来指定
AS 子句
  • 可以给数据列取一个别名
    • select studentNo as '学号' from student;
  • 也可以给表起一个别名来进行调用
    • select s.studentNo from student as s;
  • 可以把计算或者总结的结果用另外一个名称来代替
    • select num + 1 AS nums from student;
  • AS 可以省略即:select name '名字' from student;
DISTINCT 去重关键字
  • 去掉 select 查询返回的记录结果内重复的记录(重复的数据只返回一条)
  • select distinct 字段名1, 字段名2... FROM 表名
  • ALL 是默认的关键字,返回所有的记录
  • 可以写在函数的括号内去掉重复项
ALL() 关键字
  • all 后面可以接上多个数值,mysql 会使用前面的值对其进行逐个比较

  • -- 取出所以满足条件的值逐个进行比较SELECT invoice_total FROM invoicesWHERE invoice_total > ALL(	SELECT invoice_total	FROM invoices	WHERE client_id = 3)--  等价于--  取出最大值,直接比较SELECT invoice_total FROM invoicesWHERE invoice_total > (	SELECT MAX(invoice_total)	FROM invoices	WHERE client_id = 3)
    
ANY 关键字
  • ANY 需要搭配等号使用 = ANY
  • 等价于 IN 关键字
EXISTS 关键字
  • 判断子查询条件是否满足查询要求

  • 可以使用 NOT EXTST 来判断不存在

  • -- 查询发票表里面所有的用户-- exists 接收到的是布尔值SELECT * FROM clients cWHERE EXISTS(	SELECT client_id    FROM invoices    WHERE c.client_id = client_id)-- 等价于-- IN 接受到的是一个结果集SELECT * FROM clientsWHERE client_id IN(	SELECT DISTINCT client_id    FROM invoices)-- 等价于SELECT 	DISTINCT c.*FROM invoicesLEFT JOIN clients c	USING(client_id)
    
IF 关键字
  • 判断查询项是否满足对应条件,传对应的值
SELECT	id,	name,    if(is_spices = 0, '否', '是') is_spicesFROM tb_material_type
between and 范围查询
  • 根据一个范围值进行查询
  • SELECT 字段列1,字段2 ,…FROM 表名 WHERE 字段x BETWEEN 值1 AND 值2
  • 相当于 >= 和 <= 一起使用
  • 使用 between and 他的两个边界值也是被包含的
like 模糊查询
  • 与 "%" 一起使用,表示匹配0或任意多个字符
  • 与 "_" 一起使用,表示匹配单个字符;可以写多个 ‘-’ ,每个‘-’表示一个占位符
regexp 正则表达式查询(可以使用别名 RLIKE)
  • WHERE name REGEXP 'a' 表示名字中包含 ‘a' 的人;等同于 ’%a%‘
  • WHERE name REGEXP '^a' 使用 ^ 符号开头,表示名字必须以 ’a‘ 开头,等价于 'a%'
  • WHERE name REGEXP 'a$' 等价于 '%a'
  • WHERE name REGEXP 'a|b' '|' 符号有 or 的作用,表示名字中含有 'a' 或者 'b' 的用户
    • 注意:| 符号两侧不能加空格
    • 可以使用多个 | 进行多次判断,每个判断项可以加上 '^' or '$'
  • WHERE name REGEXP '[abc]o' 表示名字中含有 'ao' or 'bo' or 'co' 的用户;[]也可以写后面,查询结果顺序同理
    • [a-g] 表示 a~f 的字母都能匹配,不包括 g;- 代表一个范围
    • [][]a 前面使用多个小括号,可以匹配多位
使用 in 进行范围查询
  • SELECT 字段列1,字段2 ,…FROM 表名 WHERE 字段x IN ( 值1,值2,值3…)
  • 查询的字段x的值,至少与括号中的一个值相等
  • 多个值之间用英文逗号隔开
NULL
  • null 代表 ”无值“,区别于 0 或者 ”“
  • null只能出现在允许为null的字段,必须使用 is null 或者 is not null 去判断
SELECT CONCAT("姓名:", StudentName) AS 新姓名 FROM student;# 去重复 distinctSELECT DISTINCT StudentNo FROM result;SELECT * FROM subject WHERE subjectname LIKE "%数学%";# 下划线占位,后面必须有两个的才能被查出来SELECT studentno, studentname FROM student WHERE studentname LIKE "李__";SELECT * FROM subject WHERE classhour = 100 OR classhour = 110 OR classhour = 120;# 使用in查询方式,更为简洁效率也更高SELECT * FROM subject WHERE classhour IN (100, 110, 120);SELECT  *  FROM  subject  where    ClassHour = 100  OR ClassHour =110 OR ClassHour  = 120;  #普通处理方式SELECT  *  FROM  subject  where ClassHour  IN ( 100, 110,120 );#使用IN进行查询方式,更为简洁,效率更高

union查询

​ 合并多个查询结果

​ union 前面的查询语句决定列名的呈现

SELECT 	order_id,    order_date,    'active' statusFROM ordersWHERE order_date >= '2019-01-01'UNIONSELECT 	order_id,    order_date,    'archived' statusFROM ordersWHERE order_date < '2019-01-01'

连接查询

  • 需要多张数据表的数据进行查询,可以通过连接运算符进行查询
  • [inner/outter] JOIN ON 如果连接两个表的列名一致可以使用 using (column_name) 来替代 on cloumn_name = column_name
    • 当有多个条件相同时,使用 using (cloumn_name, clounmn_name) 来简化代码
  • 分类为
    • 内连接(inner join)
      • 等值和非等值的连接查询
      • 自身连接查询
      • 可以有多个 join on 连接多个表进行查询
    • 外连接(out join)
      • 左外连接 (left join)
      • 右外连接 (right join)
内连接查询
  • inner join 内连接;在表中至少有一个匹配时,则返回数据;on 后面跟条件

    SELECT   字段1,字段2,…  FROM  table_1 INNER  JOIN   table_2    ON  table_1.字段x   =  table_2.字段y;# INNER JOIN  与 JOIN 是相同的;#  如table_1中的行在table_2中没有匹配,则不返回;#要求:从subject和grade数据表查询课程名称和所属年级名称SELECT SubjectName,GradeName FROM   subject   INNER   JOIN  grade   ON   subject.GradeID= grade.GradeID;  
    
  • 等值和非等值的连接查询

#要求:从subject和grade数据表查询课程名称和所属年级名称#非等值连接查询 (返回记录为两表记录数的乘积)SELECT  SubjectName, GradeName  FROM  subject,  grade;  #等值查询 (等效于内连接)SELECT  SubjectName, GradeName  FROM  subject,  grade WHERE  subject.GradeID = grade.GradeID;  
自连接
  • 需要使用不同的别名来输出对应的列数据
  • 选取对应列时需要加上对应的别名
  • join 后面可以跟多个条件,条件之间使用 and 连接
SELECT * FROM sql_hr.employees eJOIN sql_hr.employees m	ON e.reports_to = m.employee_id
外连接(left/right outer join outer可以省略)
左外连接(left join)
  • 从左表(table_1)中返回所有的记录,即便在右(table_2)中没有匹配的行
  • SELECT 字段1,字段2,… FROM table_1 LEFT [ OUTER ] JOIN table_2 ON table_1.字段x = table_2.字段y;
右外连接(right join)
  • 从右表(table_2)中返回所有的记录,即便在左(table_1)中没有匹配的行
  • SELECT 字段1,字段2,… FROM table_1 RIGHT [ OUTER ] JOIN table_2 ON table_1.字段x = table_2.字段y;
自然连接(natural join);
  • 不带连接条件,数据库引擎会自动连接;不建议使用
交叉连接(cross join)
  • 不带连接条件,返回笛卡尔积
  • 在查询时查询多个表不加限制条件同样能实现交叉查询,带join属于显式查询,from 多个表属于隐式
各个连接的对比
操作符名称 描述
INNER JOIN ( JOIN ) 如果表中有至少一个匹配,则返回行
LEFT JOIN 不论右表是否有匹配,都会返回左表的所有行
RIGHT JOIN 不论左表是否有匹配,都会返回右表的所有行

image-20210621192327125

自连接查询
  • 数据表与自身进行连接
SELECT  c1.categoryName AS "父栏目名称",c2.categoryName AS  "子栏目名称" FROM category AS c1,category AS c2 WHERE c1.categoryId = c2.pid; SELECT  c1.categoryName AS "父栏目名称",c2.categoryName AS  "子栏目名称" FROM category AS c1 LEFT JOIN category AS c2 ON c1.categoryId = c2.pid WHERE c1.pid = 1; 
子查询
  • 嵌套查询即是由多个子查询组成的,求解方式是由内向外执行

  • 相关子查询(普通子查询子查询代码只会执行一次)

    • 主查询和子查询使用了同一张表,给主查询赋别名与子查询对应列进行关联

    • 相关子查询内存占用高,但是实际使用情况较多

    • 不能使用 ANY 关键字来替代相关子查询的部分操作

    • SELECT * FROM employees eWHERE salary >  (	SELECT AVG(salary)    FROM employees    WHERE office_id = e.office_id)-- 不等价, > any 会逐个比较,不是比较互相对应的SELECT * FROM employees eWHERE salary > ANY  (	SELECT AVG(salary)    FROM employees    GROUP BY office_id)
      
  • 在选择语句中使用子查询(子查询可以直接调用新定义的列)

    • SELECT 	invoice_id,    invoice_total,    (SELECT AVG(invoice_total) 		FROM invoices)  AS invoice_average,	invoice_total - (SELECT invoice_average) AS difference  -- 不能直接减别名,使用 SELECT 查询FROM invoices;
      
    • -- demoSELECT 	c.client_id,    c.name,    SUM(i.invoice_total) AS total_sales,    (SELECT AVG(invoice_total) FROM invoices) AS invoice_average,    SUM(i.invoice_total) - (SELECT invoice_average) AS differenceFROM clients cLEFT JOIN invoices i	USING(client_id)GROUP BY c.client_id-- 等价于SELECT 	c.client_id,    c.name,    (SELECT SUM(invoice_total)      FROM invoices i     WHERE client_id = c.client_id) AS total_sales,     (SELECT AVG(invoice_total)      FROM invoices i) AS average,	 (SELECT total_sales - average) AS differenceFROM clients c
      
  • 在 from 中也可以使用子查询;需要给 from 语句设置一个别名,否则会报错

    • 在 from子句使用子查询仅限于简单查询

      SELECT *FROM(	SELECT 		c.client_id,		c.name,		(SELECT SUM(invoice_total) 		 FROM invoices i		 WHERE client_id = c.client_id) AS total_sales,		 (SELECT AVG(invoice_total)		  FROM invoices i) AS average,		 (SELECT total_sales - average) AS difference	FROM clients c) AS sales_summaryWHERE total_sales IS NOT NULL
      

ORDER BY排序

  • order by 是放在语句的最后

  • 与 select 语句查询得到结果,按照指定字段进行排序

  • 与 DESC(降序) 或 ASC 搭配使用,默认为 ASC

  • order by colum_name1, column_namw2 可以接多个列名,逐个排序

  • select id, name from student order by 1, 2 这里的1,2指的是 id, name

Limit 限制

  • limit [m, ] n 或 limit n offset m
    • 限制select 返回结果的行数
    • m 限制第一个返回记录行的偏移量
    • n 限制返回记录行的最大数目
    • m 不指定默认为0
  • limit 属于分页显示
  • limit 子句永远放在最后
  • select * from student limit 5; 返回前5条数据
  • select * from student limit 5, 10; 返回 6-15条数据

group by 分组

  • 使用GROUP BY关键字对查询结果分组
    • 在from 和 where 语句的后面,在order by 的前面
    • 对所有的数据进行分组统计
    • 分组的依据可以有多个,并依次分组 GROUP BY date, payment_method 直接在后面写多个,用逗号隔开
    • 与HAVING结合使用,进行分组后的数据筛选,where语句的顺序在group之前,所以只能用HAVING进行筛选;HAVING 后面可以接多个条件,用AND连接;但是条件必须是 select 语句里面有的字段
# 分组查询,having作为条件限制,group进行分组SELECT 	s.subjectName AS "课程名",	MAX(studentResult) AS "最高分",	MIN(studentResult) AS "最低分",	AVG(studentResult) AS "平均分",	SUM(studentResult) AS "分数和",	COUNT(1) AS "人数"FROM result AS rLEFT JOIN	subject AS s	ON s.subjectNo = r.subjectNoGROUP BY r.subjectNoHAVING AVG(studentResult) >= 60;

MYSQL函数

  • 数学函数、字符串函数、日期时间函数、系统统计函数
MYSQL统计函数
函数名称 描述
COUNT( ) 返回满足SELECT条件的记录总和数,COUNT()只会返回非空的数据列,如果要返回所有的数据列可以使用count(*)
SUM( ) 返回数字字段或表达式列作统计,返回一列的总和
AVG( ) 通常为数值字段或表达列作统计,返回一列的平均值
MAX( ) 可以为数值字段、字符字段或表达式列作统计,返回最大的值
MIN( ) 可以为数值字段、字符字段或表达式列作统计,返回最小的值
  • WITH ROLLUP 运算符,自动在列最后生成一个存储聚合函数的和值,只能适用于聚合函数;且跟在 group by 条件后可以实现分类汇总,

    SELECT 	IFNULL(city, '合计') city,    IFNULL(state, '小计') state,    SUM(invoice_total)FROM invoicesJOIN clients USING(client_id)GROUP BY city, state WITH ROLLUP
    
数值函数
函数名称 描述
ROUND() 四舍五入;SELECT ROUND(5,745, 1) 括号里第一项是要处理的数字,第二项是要保留的位数,不写就取整数
TRUNCATE() 截断数字;SELECT TRUNCATE(5.7563, 2) 第一项待处理,第二项是保留的位数
CEILING() 向上取整,只有一个参数
FLOOR() 向下取整
ABS() 取参数的绝对值
RAND() 返回 0-1 的随机浮点数
字符串函数
函数名称 描述
LENGTH() 返回字符串的长度;SELECT LENGTH('sky')
UPPER() 字符串转大写
LOWER() 转小写
LTRIM() 去除传入参数左侧的空格;RTRIM()会去除右侧;TRIM() 会同时去除两侧的空格
LEFT() LEFT('Hello,World', 5) 返回前五个字符 ;RIGHT() 同理反之
SUBSTRING() SUBSTRING('Hello,World', 2, 5) 从第二位开始,返回五个字符,第三参数不传就会取到最后
LOCATE() LOCATE('e', 'hello') 第一个参数是要查找的字符,后面是查找的对象;返回值是最先发现的下标,如果不存在返回 0 ,不区分大小写
CONCAT() 传入多个字符串参数,函数会将他们进行拼接

MYSQL事务处理

  • 事务就是一组SQL语句放在同一批次内去执行
  • 如果一个SQL语句出错,则该批次的所有语句都将被取消执行
  • mysql 事务处理只支持 innoDB 和 BDB 数据表类型

事务 ACID 属性

  • 事务必须具备以下四个属性
  • 一个事务一般是指多个操作的集合,比如插入数据库分为两段插入,第二次插入错误,第一次插入操作也需要回退
  • 事务:一个最小的不可再分的工作单元;通常一个事务对应一个完整的业务
  • 一个完整的业务需要批量的DML(insert,update,delete) 语句共同联合完成
  • 事务只和 DML 语句有关,或者说只有 DML 语句才有事务

原子性(Atomicity)

  • 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生
  • 指的是整个事务是一个独立的单元,要么操作成功要么不成功

一致性(Consistency

  • 事务必须使数据库从一个一致状态转换到另外一个一致性状态,执行事务前后状态都必须一致。不一致包含三点:脏读、不可重复读、幻读
  • 事务必须要保持和系统处于一致的状态

隔离性(Isolation

  • 事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰
  • 事务是并发控制机制,他们交错也需要一致性,隐藏隔离,一般通过悲观或乐观锁实现

持久性(Durability

  • 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响
  • 一个成功的事务将永久改变系统的状态,在他结束之前所有导致状态变化的记录都在一个持久的事务日志中

mysql事务实现

  • 使用 set 语句来改变自动提交模式
    • set autocommit = 0; 关闭自动提交模式
    • set autocommit = 1; 开启自动提交模式
  • MySQL中默认是自动提交
  • 使用事务时应该先关闭自动提交

事务实现方法

  • start transaction : 开始一个事务,标记事务的起始点
  • commit : 提交一个事务给数据库
  • rollback : 将事务回滚,数据回到本次事务的起始状态
  • set autocommit = 1 : 开启 MySQL 数据库的自动提交

事务处理步骤

image-20210624192943835

四种隔离级别

  • 越往上性能损失越大
  • 开启了事务,修改数据时不提交或者回滚其他线程对数据的可见性
SELECT @@tx_isolation; # 查看当前会话的隔离级别SELECT @@global.tx_isolation; # 查看系统当前的隔离级别SET SESSION TRANSACTION isolation LEVEL REPEATABLE READ; # 设置当前会话当前隔离级别set global transaction isolation level repeatable read; # 设置系统当前隔离级别set autocommit = off 或者 start transaction; # 命令行,开始事务时

脏读 可以看到未提交的数据(read uncommit)

  • 一个线程读到另一个线程未提交的数据,这个数据可能是存在问题的
  • 如果程序处理的时候,有问题需要回滚,不应该被别人看到

不可重复读(read )(Oracle 默认级别)

  • 解决了脏读问题,但是在一个事务里面,两次查询看到的结果是不一致的
  • 修改后没有提交的数据是看不到的

可重复读(mysql 默认级别)

  • 解决了不可重复读的问题,但是可能会产生幻象。他认为不存在的记录可能在他操作期间被别人插入了。在他插入的时候就可能会报错

串行事务、序列化事务

  • 解决了上面的所有问题,但是性能较差。因为他要独占所有资源
  • 其他人要操作只能等他提交或回滚释放掉资源才能操作

MySQL锁

  • update delete 这种操作,MySQL 内置了隐藏的独占锁,具体某个时刻只能有一个线程修改,这隐性独占锁的效率是最高的

行锁

  • 我们在写update delete insert 时,会执行一个内部隐含的独占锁

行级共享锁

  • 出现死锁的概率较高,一般不建议使用

行级独占锁

  • 使用的时候被线程独占,回滚或提交时释放

表级锁

  • 使用表级锁在退出的时候最好释放,不然容易造成死锁

表级写锁

表级读锁

悲观锁

  • 容易造成性能问题,频繁加锁影响并发

乐观锁

约束

作用:是为了保证数据的完整性而实现的摘自一套机制,它具体的根据各个不同的数据库的实现而有不同的工具(约束);

外键约束

  • ALTER TABLE tradeInfo ADD CONSTRAINT FK_cardID FOREIGN KEY(cardID) REFERENCES cardInfo(cardID);
  • 设置外键:CONSTRAINT t_id_fk FOREIGN KEY (t_id) REFERENCES t1 (id)

索引

  • 有索引的字段尽量写在前面
  • mysql使用指定索引语法,避免mysql自行优化使用其他索引
    • select * from table_name force index(index_name) where conditions;
  • 作用
    • 提高查询速度
    • 确保数据的唯一性
    • 可以加速表与表之间的连接,实现表与表之间的参照完整性
    • 使用分组和排序子句进行数据检索时,可以显著减少分组和排序的时间
    • 全文检索字段进行搜索优化

主键索引(primary key)

  • 主键是最常见的索引类型

  • 确保数据记录的唯一性

  • 确定数据记录的唯一性

  • 主键的值必须是唯一的且主键的值不能为空

  • 默认情况下,mysql会自动尝试使用索引,不需要我们手动干预

  • 主键只能声明一次,可以一次声明多个字段为主键

主键的声明方法

create table student(	studentNo int(11) primary key, # 第一种声明方式,直接在字段后面声明    StudentName varchar(5),    StudentPhone int(11),    primary key(studentNo, studentPhone) # 第二种声明方式,可以同时设置多个字段为主键); 

唯一索引(unique)

  • 作用:避免同一个表中某数据列中的值重复

  • 被修饰的字段具有唯一性

  • 字段值为空是不被检查,可以插入多个 NULL

  • unique 可以设置给多个字段,也可以一次给多个字段声明

  • 可以同时设置对各字段为唯一索引

唯一索引声明方法

create table student(	studentNo int(11) unique, # 第一种设置方式,为单个字段设置索引    studentName varchar(6),    studentPhone int(11) unique,    unique key (studentNo, studentName) # 第二种创建方式,同时设置多个字段);

常规索引 ( index )

  • 作用:快速定位特点数据

  • index 和 key 关键字都可设置常规索引

  • 应加在查找字段

  • 不宜添加过多的常规索引,影响数据的插入、删除和修改操作

  • 删除index约束 drop index index_table_index on index_table;

创建常规索引

create table student(    studentNo int(11),     StudentName varchar(5),    StudentPhone int(11),	index/key (studentNo, studentPhone)  # 创建表时添加);alter table student add index (studentNo, studentPhone); # 创建后追加

全文检索(fulltext)

  • 作用:快速定位特定数据
  • 只能用于 MyISAM 类型的数据表
  • 只能用于 char、varchar、text 数据列类型
  • 适合大型数据集

创建全文索引

create table student(	fulltext (studentName)  # 创建表时添加);alter table student add fulltext (studentName);  # 创建后添加

管理索引

创建索引

  • 床表时添加:直接在相应字段添加即可
  • 创表后追加:alter table table_name add index_name (column_name)

删除索引

  • drop index index_name on table_name
  • alter table table_name drop index index_name
  • alter table table_name drop primary key 删除主键

查看索引

  • show index/key from table_name;

数据库备份与恢复

备份

  • D:\Program Files\MySQL\MySQL Server 5.5\bin>mysqldump -u root -p myschool > D:/temp/myschool.sql
  • 数据库 bin 文件路径 > mysqldump -u 用户名 -p 数据库名 > 备份路径
  • 备份数据库内的表:数据库bin文件路径 > mysqldump -u 用户名 -p 数据库名 表名1 表名2 > 备份路径

数据库调优

JDBC、JNDI

  • SQL 注入的安全漏洞
  • 效率比较低,不利于服务器缓存sql

Java连接数据库

static String url =            "jdbc:mysql://127.0.0.1:3306/goods?useUnicode=true&characterEncoding=UTF-8";static String user = "root"; // 账号static String password = "000000"; // 密码

image-20210623105118472

Java框架

netty

​ Java里性能最高的网络编程框架

Mybatis

​ 简化数据层代码

​ 通过 xml 文档或者注解来配置和映射原始类型接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录

安装mybatis

​ 使用 MyBatis,就只需要将 mybatis 的jar文件放置到类路径

​ 如果使用 Maven 构建项目,添加依赖到 pom.xml 文件:

<dependency>  <groupId>org.mybatis</groupId>  <artifactId>mybatis</artifactId>  <version>x.x.x</version></dependency>

Sping

(https://www.cnblogs.com/yuan-liu/p/15324780.html)

​ 作用于系统的服务层,Spring是面向 Bean(即 Java 对象) 的编程,spring 把相互协作的关系称作是依赖关系

spring框架架构

​ Spring 总共大约有20个模块,由1300 多个文件构成,这些组件被分别整合在 核心容器(Core Container)、AOP(Aspect Oriented Programming)和设备支持(Instrmentation)、数据访问及集成(Data Access、Integeration)、Web、报文发送(Messaging)、测试6个模块集合中。

  1. 核心容器:Spring-beans 和 Spring-core 模块是Spring 框架的核心模块,包含控制反转(Inversion of Control IoC)和依赖注入(Dependency Injection DI),核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,工厂模式的实现。BeanFactory 使用控制反转(IoC)思想将应用程序的配置和依赖性规范与实际的应用程序代码分开

    Spring 上下文 Spring Context:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能

    Spring-Expression 模块是统一表达式语言(unified EL)的扩展模块,可以查询、管理运行中的对象,同时也方便的可以调用对象方法,如操作数组、集合等。他的语法类似与传统的 EL,但提供了额外的功能,最出色的就是函数调用和简单字符串的模板函数

  2. Spring-AOP:Spring-AOP 是 Spring 的另外一个核心模块,在Spring中,他是以 JVM 的动态代理技术为基础,然后设计出了一系列的 AOP 横切实现,比如前置通知、返回通知、异常通知。通过其配置管理特性,Spring AOP模块直接将切面的编程功能集成到了 Spring 框架中。所以,可以很容易的使 Spring 框架的任何对象执行 AOP

  3. Spring Data Access(数据访问):由Spring-idbc、spring-tx、spring-orm、spring-jms 和 spring-oxm 5个模块组成 。spring-jdbc 模块是 Spring 提供的 JDBC 抽象框架的主要实现模块。用于简化 Spring JDBC

    Spring-tx 模块是spring JDBC 事务控制实现模块,使用 spring框架,他对事务做了很好的封装,通过他的 AOP 配置,可以灵活的配置在任何一层

    spring-Orm 模块是 ORM框架的支持模块,主要集成 hibernate、Java Persistence API(JPA) 和 Java Data Objects(JDO)用于资源管理、数据访问对象(DAO)的实现和事务策略

    Spring-Jms 模块 (Java Messaging Service) 能够发送和接受信息

    Spring-Oxm 模块主要提供一个抽象层以支撑 OXM (OXM 是 Object-to-XML-Mapping 的缩写,他是一个 O/M-mapper,将Java对象映射成 XML 数据,或者将 XML 数据映射成Java对象),例如:JAXB、Castor、XMLBeans、JiBX和 XStream 等

  4. Web 模块:由Spring-web、Spring-webmvc、Spring-webscoket 和 Spring-webmvc-portlet 4个模块构成,Web上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作

  5. 报文发送:即 Spring-messaging 模块

    Spring-messaging 是 spring4 新加入的一个模块,主要职责是为 Spring 框架集成一些基础的保温传送应用

  6. 单元测试:即 Spring-test 模块,Spring-test 模块主要是为测试提供支持

Spring 环境搭建

​ 需要 maven 项目进行环境搭建

Spring 依赖添加

Maven 仓库:https://mvnrepository.com/

<dependency>   <groupId>org.springframework</groupId>   <artifactId>spring-context</artifactId>   <version>5.2.4.RELEASE</version></dependency>

编写 Bean 对象

public class UserService{    public void test(){        System.out.println("Hello");    }}

添加 spring 配置文件

  1. 创建 resource 文件夹,与 java 文件夹同级,并设置其为资源目录

  2. 新建 xml 文件(spring.xml)

    <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xsi:schemaLocation="http://www.springframework.org/schema/beans           http://www.springframework.org/schema/beans/spring-beans.xsd">    <!--		xmlns 即 xml namespace xml 使用的命名空间		xmlns:xsi 即 xml schema instance xml 遵守的具体规范		xsi:schemaLocation 本文档 xml 遵守的规范(官方规定)	-->            <!--  		xml 中配置 bean 对象		id:bean对象的唯一标识,一般是 对象的名称首字母小写		class:bean 对象的路径	-->    <bean id="userService" class="com.xxx.service.UserService"></bean></bean>
    

加载配置文件,获取实例化对象

public class App{    public static void main(String[] args){        // 获取 Spring 上下文环境        ApplicationContext context = new ClassPathXmlApplicationContext("apring.xml");        // 通过 getBean 方法得到 spring 容器中实例化好的 Bean 对象        // userService 代表的是配置文件中 bean 标签的 id 值        UserService service = (UserService) service.getBean("userService");        // 调用方法        service.test();    }}

Spring IoC 配置文件加载

配置文件加载

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.3c.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://www.springframework.org/schema/beans           http://www.springframework.org/schema/beans/spring-beans.xsd">    <bean id="userService" class="com.package.service.UserService"></bean>    </beans>

相对路径加载

Application ac = new ClassPathXmlApplicationContext("spring.xml");

多个配置文件加载

相对路径加载(创建多个配置文件,分别加载)

Application ac = new ClassPathXmlApplicationContext("service.xml", "dao.xml");

总配置文件加载引入其他配置文件,再通过相对路径加载

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.3c.org/2001/XMLSchema-instance"       xsi:schemaLocation="http://www.springframework.org/schema/beans           http://www.springframework.org/schema/beans/spring-beans.xsd">        <import resource="dao.xml"/>    <import resource="service.xml"/></beans>

两大技术

​ spring 的核心概念就是 IoC 和 AOP

控制反转 / 依赖注入(IoC / DI)

​ 不管是控制反转还是依赖注入,他们都可以这样理解:当某个Java实例(调用者)需要另一个Java实例(被调用者)时,在传统的程序设计过程中,通常有调用者来创建被调用者的实例。但是在依赖注入/控制反转模式下,创建被调用者的工作不再是有调用者来完成,而是由Spring容器来完成,然后注入调用者。

要实现 控制反转与依赖注入需要 Bean对象满足 JavaBean 的创建规范

Spring IOC 容器 Bean对象实例化模拟
  1. 定义 Bean 工厂接口,提供获取 Bean 方法
  2. 定义 Bean 工厂接口实现类,解析配置文件,实例化 Bean 对象
  3. 实现获取 Bean 方法
定义 Bean 属性对象

引入 dom4j 、XPath 依赖包
<!-- https://mvnrepository.com/artifact/org.dom4j/dom4j --><dependency>    <groupId>dom4j</groupId>    <artifactId>dom4j</artifactId>    <version>1.6.1</version></dependency><dependency>    <groupId>jaxen</groupId>    <artifactId>jaxen</artifactId>    <version>1.1.6</version></dependency>
创建配置文件

​ 如果没有 resources 资源目录需要手动创建

<?xml version="1.0" encoding="UTF-8" ?><beans>    <!--  设置 javaBean 对应的标签  -->    <bean></bean>    <bean></bean>    </beans>
控制反转

​ 即控制权的转移,将创建对象的方式反转 ,使用 spring 完成创建以及注入就是我们将控制权反转给程序

​ 原始写法 UserDao dao = new UserImpl() -> spring 控制 UserDao dao = ctx.getBean('dao', userDao.class)

依赖注入

​ 在依赖注入模式下,创建被调用者的工作不由调用者来完成,因此称作依赖注入

​ 依赖注入通常有三种方式:

​ 1. 设置注入:IoC 容器使用属性的 setter 方法来注入被依赖的实例

​ 2. 构造注入:IoC 容器使用构造器来注入被依赖的实例

​ 3. 注解注入

// 设置注入 <property>标签<bean id="hello" class="demo.Hello">    <!-- 写法1 -->    <property name="name" value="world"></property>    <!-- 写法2 -->    <property name="name">    	<value>world</value>    	<!-- 若value的值是特殊字符串,避免被转移可以使用如下格式 -->    	<value><![CDATA[ string ]]></value>    </property></bean>    <!-- 写法3,直接p:带属性名赋值 --><bean id="person5" class="spingtest.collection.Person"p:age="30" p:name="Queen" p:car-ref="car"></bean>
// 构造注入 <constructor-arg>标签;可以给属性赋值为 null<bean id="car" class="spingtest.collection.Car">    // index 指定构造器属性顺序    <constructor-arg value="Audi" index="0"></constructor-arg>    <constructor-arg value="Shanghai" index="1"></constructor-arg>    // 如果有多个构造方法,相同位置的属性不同,可以使用 type 来区分对应的构造方法    <constructor-arg value="300000" type="double"></constructor-arg></bean>

Bean 对象的引用

// 内部引用,内部 Bean 只能被内部使用,<bean id="car" class="com.panlei.demo.Car">    <property name="name" value="奥迪"></property>    <property name="price" value="400000"></property>    <property name="tires" >    <!-- 可以在里面用对应的数据类型装配数据 -->    <!-- 如果属性是map,需要用 <entry>标签存数据 -->    	<class="com.panlei.demo.Tire">    		<property name="name" value="朝阳"></property>    		<property name="price" value="3000"></property>		</bean>    </property></bean>    // 外部引用,可以被多个 Bean 调用;使用 ref 标签引用<bean id="tire" class="com.panlei.demo.Tire">    <property name="name" value="朝阳"></property>    <property name="price" value="3000"></property></bean><bean id="car" class="com.panlei.demo.Car">    <property name="name" value="奥迪"></property>    <property name="price" value="400000"></property>    <property name="tires" ref="tire"></property>    // 外部引用并赋值,需要先初始化对应的对象并且该对象具有这个属性才能进行其他属性的赋值    <property name="tire.number" value="4"></property></bean>
Bean 注解注入

声明 Bean 注解

@Component  // 组件 没有明确规定其角色,作用在类级别上声明当前类作为一个业务租还,被 Spring IoC 容器维护@Service  // 在业务逻辑层(service)对类进行声明@Repository // 在数据访问层(dao)对类进行声明@Controller  // 在展现层(MVC)使用,标注当前类为一个控制器

注入Bean注解

@Autowired  // Spring 官方提供注解@Inject@Resource

​ 使用注解注入就不需要在 xml 文件配置 bean 文件,需要添加 <context:component-scan base-package="annspring"/> 标签,base-package 里面就是需要扫描注解的包文件

@Test 加在实现类上面,可以不用写main函数,直接执行该方法

@Component("name") 在不明确 Bean 属于三层架构的那一层时就使用此注解;带来名字可以直接通过名字获取 Bean,若不带名字就使用类名获取 Bean;还可以写成 @Component(value = "name")

@Repository("name") 位于持久层,用于标注数据访问组件,即 Dao 层组件;可以带参数,代表 Bean 的名称。若不带参数 Bean 的名称就是类名 (首字母小写) ,服务层和控制层注解也能不带参数

@Service("name") 服务层的注解

@Controller("name") 控制层注解

@Autowired + @Qualifier 自动装配

// 注解的参数注入@Autowired // 按照属性类型去找要注入的bean,可以单独使用,不需要setter方法@Qualifier(value = "name") // 按照 id 查找,不能单独使用// 写法1,在方法上进行参数构造@Autowired@Qualifier("serviceImpl")public void service(ServiceImpl service){    this.service = service;}// 写法2,在构造器上注入参数@Autowiredpublic void setter(@Qualifier("ServliceImpl")ServiceImpl service){    this.service = service;}

spring 创建对象

​ @Scope("singleton") 单例模式,只创建一个对象在 IoC 容器中,默认这种方式

​ @Scope("prototype") 多例模式,使用一次 spring就会创建一个对象

@Resource(name = "name") 注解参数

​ @Resource 按照名称去查找参数,@Autowired 按照参数类型查找参数。前者精确度更高

@Lazy(true/false) 懒加载

​ 当值为true时,spring初始化时不会创建这个对象,只有使用时才会创建

@PostConstruct

​ 构造方法被执行完后,开始执行这个注解方法,加载初始化资源

@PreDestroy

​ 在容器被销毁时执行这个方法,主要是为了正确释放资源

@Configuration 用来定义配置类,可以替换配置文件

​ 被此注解注解的类中应该有一个或多个被 @Bean 注解的方法

三层架构注解之间的关系

img
Spring 工厂类

ApplicationContext :新版本的工厂类,加载配置文件的时候,就会将Spring管理的类都实例化。

​ ApplicationContext有两个实现类

    ClassPathXmlApplicationContext :加载类路径下的配置文件

    FileSystemXmlApplicationContext :加载文件系统下的配置文件

spring 框架部署
  1. ClassPathXmlApplicationContext("applicationContext.xml") spring框架读取配置文件,创建一个容器(hashMap)

  2. 往容器里装东西(Java 对象)

    1. 明确需要装载 Java 对象
    2. 创建对应的 Java 对象 => 内部使用反射创建对象
    3. spring 先检查 bean 之间的依赖关系,给对应的属性进行赋值
    4. 成功创建出对象,将对象放在容器里面去 hashMap.put("Class_name", "created_Bean")
  3. 开发者使用容器里已经被创建好的Java对象(Bean)

  4. 配置 Java Bean,生命周期和 IoC容器生命周期一样长;默认情况(即单例模式)

  5. 如果说 JavaBean 在被多线程共享时,产生线程安全问题,那么需要使用多例模式。我们使用一次JavaBean,spring框架就为我们创建一个新的对象,这个对象不会在IoC容器中存放,在spring启动的时候,也不会创建这个JavaBean对象,调用者自己管理这个JavaBean的生命周期

  6. 单例模式(单例模式可以不用加 scope 标签)

    多线程共享这个对象,在IoC容器中只有一份

  7. 懒加载(单例模式下使用)

    lazy-init="true" 直接加在 JavaBean 标签上;局部懒加载标签优先

    default-laze-init="true" 全局懒加载,加在最外层的 Bean 标签上;

    在sping启动的时候,不会马上创建对象,第一次使用的时候再去创建对象,创建好的对象同样放在IoC容器,供后面的使用者共享,加快系统启动

注解注入 spring运行流程
  1. spring 启动的时候,创建 IoC 容器
  2. 扫描带注解的类 (component、controller、service、repository);IoC看到这四个注解就会创建 JavaBean,放在IoC容器里面,提供给调用者使用
  3. @Autowaird 这种类似的注解,加在属性上面,IoC 在启动时会扫描这种类似的注解,完成值的注入
  4. 静态属性的不能直接在属性上面注入,必须使用方法。静态的属性不属于 IoC 容器
  5. 单例懒加载注入 @Lazy(true) ;多例只需要再加上注解 @Scope("prototype")

面向切面编程 (AOP)

非核心业务就叫切面

​ Aop是一种编程思想,是面向对象编程(OOP) 的一种补充,面向对象将程序抽象成各个层次的对象,而面向切片编程是将程序抽象成各个切面

​ AOP要达到的效果是,保证开发者不修改代码的前提下,去为系统中的业务添加某种通用功能。AOP 的本质是由AOP框架修改业务组件的多个方法的源代码。AOP 其实就是代理模式的典型应用(代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的访问)

AOP是另一种编程思想

按照AOP框架修改源代码的时机可以将其分为两类:

静态 AOP 实现:
  • AOP 框架在编译阶段对程序源代码进行修改,生成了静态的 AOP 代理类
    生成的 *.class 文件已经被改掉了,需要使用特定的编译器,比如 AspectJ
  • 以数据库操作层dao为场景,将dao层中事务的开启和提交进行分离
  • 存在的问题:
    • 静态代理模式并没有做到事务的重用
    • 假设dao层有100个类,100个proxy,接口中有多少方法,在proxy层就得实现多少方法,有多少方法就要开启和提交多少事务
    • 如果一个proxy实现了多个接口,如果其中的一个接口发生变化(添加了一个方法),那么proxy也要做相应的改变
动态 AOP 实现:
  • 没有写代理类的源代码,代理类出自字节码层

  • AOP 框架在运行阶段对动态代理生成的代理对象
    在内存中以 JDK 动态代理,或 cGlib 动态地生成 AOP 代理类,比如 SpringAOP

  • 动态代理模式就是让jvm动态的生成的代理类;动态代理需要设置拦截器

    • 目标类导入进来 personDao实现类

    • 事务导入进来 : Transaction类

    • invoke完成 :执行

      1、开启事务

      2、调用目标对象的方法

      3、事务的提交

常用AOP之间的比较关系

image-20210805173358122

拦截器

1、拦截器的invoke方法是在什么时候执行的?

​ 当在客户端,代理对象调用方法的时候,进入到了拦截器的invoke方法

2、代理对象的方法体的内容是什么?

​ 拦截器的invoke方法的内容就是代理对象的方法的内容

3、拦截器中的invoke方法中的参数method是谁在什么时候传递过来的?

​ 代理对象调用方法的时候,进入了拦截器中的invoke方法,所以invoke方法中的参数method就是代理对象调用的方法

​ 存在问题

​ 1、在拦截器中除了能调用目标对象的目标方法以外,功能是比较单一的,在这个例子中只能处理事务

​ 2、拦截器中的invoke方法的if判断语句在真实的开发环境下是不靠谱的,因为一旦方法很多if语句需要写很多。

AOP相关术语
  • 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。切面里的具体业务方法,这些方法要组合到核心业务上执行
    • 通知分为五种类型:前置通知、后置通知、异常通知、最终通知、环绕通知,
  • 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。激活点,告诉spring 切面要工作了
  • 切点(PointCut): 可以插入增强处理的连接点。告诉 spring 那些核心业务方法被执行时需要被动态拦截
  • 切面(Aspect): 切面是通知和切点的结合。非核心业务,比如日志切面或事务切面等
  • 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。给类增加新功能,在不修改原类的基础上,把另一个实现新功能的类直接引入进来。这样原类就具有了新类的功能
  • 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。

Spring + Mybatis + TomCat

  1. 把 spring 核心配置文件放在 web.xml

    通过 contextListener 监听器的初始化方法,完成 IoC 容器的初始化

  2. spring 框架依赖我们写的配置

    1. 初始化数据库连接
    2. 初始化 mybatis 的配置
    3. 初始化 javaBenan,初始化数据层和业务层的 javaBean
    4. 初始化事务管理

Spring 核心配置文件

事务注解
  1. 可以加在类上,也可以加在方法上面。

    ​ 加在类上面表示所有数据操作的方法全开事务,力度较大影响并发。建议在需要开始事务的方法上加

  2. 依赖异常决定是否提交还是回滚 @Transactional(rollbackFor = {Exception.class})

    ​ 默认回滚 RuntimeException

Spring 事务

Spring mvc

  1. tomcat 会接受我们输入的地址进行访问

  2. 对应 jsp 页面与对应的方法互相匹配

  3. tomcat 启动单独时候会建立 servlet 对象,servlet 对象会去读取 mvc 的配置文件,配置文件我们设置了扫包

    1. 首先扫描 controller 注解类,创建对象放到 ioc 容器 (mvc)
    2. 处理 RequestMappring 类似的注解 hashmap.put("mvc/hello.do", hello的反射方法)

SpringBoot

常用注解

@SpringBootApplication

​ boot 项目的启动点

​ 自动扫包,默认扫包规则,从启动程序开始的包作为依据开始扫描(包括子包)

@ServletComponentScan({"springbootlession.springboot.servlet"}) 规定报名,不使用则默认规则扫描

​ 完成自动配置,读取配置文件

@EnableAutoConfiguration

​ 允许 Spring Boot 自动配置注解,开启注解后,Spring Boot 就能根据当前路径下的包或者类来配置 Spring Bean

@ServletComponentScan

​ 在SpringBootApplication上使用@ServletComponentScan注解后,Servlet、Filter、Listener可以直接通过@WebServlet、@WebFilter、@WebListener注解自动注册,无需其他代码。

@Autowired

​ 可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作

@Component

​ 作用就是实现 Bean 的注入

springBoot 项目搭建

springBoot 依赖坐标

    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>2.5.3</version>        <relativePath/>     </parent>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>

​ Spring 项目必须要将 parent 设置为 SpringBoot 的parent,他包含了大量默认配置

导入 SpringBoot 的 web 坐标与相关插件

            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>

springBoot 配置文件

​ springBoot 配置文件有两种格式,放置于 resources 资源目录下

application.properties 配置文件配置

# 项目启动端口配置server.port=8080# 项目访问上下文路径server.servlet.context-path=/mvc# 数据源配置spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.url=dbc:mysql://127.0.0.1:3306/database_name?userSSL=false&useUnicode=true&characterEncoding=UTF-8spring.datasource.username=rootspring.datasource.password=root

application.yml 配置文件配置

server:  port: 80  servlet:  	context-path: /mvcspring:  datasource:    url: jdbc:mysql://127.0.0.1:3306/t1958?userSSL=false&useUnicode=true&characterEncoding=UTF-8    username: root    password: '000000'    driver-class-name: com.mysql.jdbc.Drivermybatis:  mapper-locations: 'classpath*:mapper/**/*Mapper.xml'pagehelper:  params: count=countSql  auto-dialect: mysql  reasonable: true  support-methods-arguments: true

JavaWeb

tomcat

image-20210705142100469

  • tomcat默认首页是 index.jsp;默认访问首页,tomcat执行的是 index.class 字节码文件

  • tomcat运行会首先找到 index.class 文件,如果文件不存在会翻译 index.jsp 为 index.java 再翻译为 index.class运行

  • index.class 有负责输出的代码,把html标签和css、js输出到浏览器

  • 浏览器收到响应结果后,会断开于tomcat的网络连接

  • 浏览器发送请求到收到结果的中间时间是由tomcat服务器在处理

  • 修改后的程序需要在编译器重新运行代码才会生效

  • 浏览器会缓存执行结果

  • 404:地址有误;500:Java代码出现异常

  • 浏览器发送请求会带上所有的请求参数

  • tomcat接收到请求会创建一个request(请求对象)的Java对象,浏览器发送的所有请求被封装在该对象中,在对象里内置了一个类似map的对象

  • request会被tomcat传入jsp中

  • request的生命周期很短,浏览器在接收到服务器响应后,这个对象就被当作垃圾回收

  • 浏览器每发送一次请求都会建立一个全新的request对象,他是线程安全的

  • 浏览器默认使用get方式提交请求,除非我们自行限制使用post

  • tomcat连接mysql:Tomcat8以上的数据库配置要使用maxTotal而不是maxActive,要使用maxWaitMillis而不是maxWait

  • 比较项 Get post
    参数出现在URL中
    长度限制
    安全性
    URL可传播

web服务器

web服务器向浏览器输出响应信息

response对象

  • 浏览器把请求发送到服务器时,服务器同时创建request和response对象
  • response也是线程安全的
  • response的生命周期和request一致、

session会话

  • 保存登录用户的账户和密码
  • session对象并不是每次请求都创建,在第一次发送请求时会话就被建立并存在类似hashmap的结构中,cookie值为key,session为value
  • session的存货时间可以进行配置,默认为30分钟
  • session生命周期较长,最好只存放重要信息
  • session靠cookie维持与浏览器之间的关联,浏览器后续发的请求都会带上cookie信息
  • 不同浏览器使用了不同的cookie以及session
  • session不是线程安全的,使用不同账户在相同浏览器登录会产生信息污染,因为浏览器带带cookie信息就会使用相同的session对象

jsp

  • 运行在服务器端的 java 页面
  • 使用 html 嵌套的 java 代码实现
  • 在jsp中写Java代码,需要写在脚本段中: <% code here... %>
  1. <%page%> 用来设置一个JSP页面的属性:<%@ page contentType="text/html;charset=UTF-8" language="java" %>
  2. <% out.print();%> 实现页面的输出
  3. jsp 添加注释
    • HTML注释:
    • jsp注释: <%-- jsp注释 --%>
    • jsp脚本注释: <%// 单行注释%> <%/* 多行注释 */%>

jsp内置对象

  • 请求对象:request
  • 输出对象:out
  • 响应对象:response
  • 应用程序对象:application
  • 会话对象:session
  • 页面上下文对象:pageContentext
  • 页面对象:page
  • 配置对象:config
  • 异常对象:Exception

获取表单数据

  • 使用 request.getParameter() 获取参数

    • String email = request.getParameter("email");
  • request 对象常用方法

    • 方法名称 说明
      String getParameter(String name) 根据表单组件名称获取提交数据
      String[ ] getParameterValues(String name) 获取表单组件对应多个值时的请求数据
      void setCharacterEncoding(String charset) 指定每个请求的编码
      RequestDispatcher getRequestDispatcher(String path) 返回一个RequestDispatcher对象,该对象的forward( )方法用于转发请求

中文乱码

request.setCharacterEncoding("utf-8"); // 请求乱码

response.setCharacterEncoding("utf-8"); // 响应乱码

<%@ page language="java" contentType="text/html; charset=utf-8"%>

表单get请求乱码解决

治标的方法:new String( s.getBytes("iso-8859-1"), "utf-8" );

治本的方法:配置tomcat\conf\server.xml文件

转发和重定向

  • 都是页面跳转的方式

转发

  • RequestDispatcher对象forward()方法
    • request.getRequestDispatcher("url").forward(request, response)
    • <jsp:forward page="url" />

重定向

  • response.sendRedirect("url") 将用户重新定位到一个url
    • response.sendRedirect("/demo00_war/jsp/fail.jsp");
转发与重定向的区别
比较项 转发 重定向
URL变化
重新发出请求 不会
是否携带请求
目标URL要求 仅本Web应用 任意URL

servlet

(https://www.cnblogs.com/yuan-liu/p/15255326.html)

HTTP协议

HTTP协议的特点

  1. 支持客户 / 服务器模式
  2. 简单快速
  3. 灵活
  4. 无连接,HTTP1.1 版本后支持可持续连接
  5. 无状态

HTTP URL

 http://host[:port]/[abs_path] http://IP(主机名/域名):端口/访问的资源路径
  • http 表示通过HTTP协议来定位网络资源
  • host 表示合法的 Internet 主机名或者 ip 地址
  • port 指一个端口号,为空则为 80
  • abs_path 指定请求的 URL

HTTP 请求

  • http请求由请求行、请求头、请求正文组成。
    • 请求体第一行就是请求行;请求行由请求方式,请求路径、请求协议版本三部分组成
    • 请求头指一行一行的键值对
    • 正文即是内容;GET请求没有正文,求情内容直接跟在地址后面

HTTP 响应

  • 在接收和解释请求消息后,服务器返回一个 HTTP 响应消息。HTTP 响应由状态行、消息报头、响应正文组成
    • 状态行由协议版本、状态码、请求结果组成
    • 消息报头于请求头大致相同
    • 响应正文就是浏览器呈现的内容

消息头

​ HTTP消息由客户端到服务器的请求和服务器到客户端的响应组成,请求消息和响应消息都是由开始行(对于请求消息, 开始行就是请求行;响应消息则是状态行),消息报头,空行,消息正文(可选)组成

​ 每一个报头域都是由  名字+":"+空格+值  组成,消息报头域的名字大小写无关

请求头

​ 请求报头允许客户端向服务器传递请求的附加信息以及客户端自身的信息

  • referer: 该请求头指明请求从哪里来

    ​ 此请求头后面接的值为上一个页面的URL地址,通常用来做统计、防盗链

响应头

​ 响应报头允许服务器传递不能放在状态行中的附加响应消息,以及关于服务器的信息和对Request-URL所标识的资源进行下一步访问的信息

  • location(重定向):location 响应报头域用于重定向接收者到一个新位置

    ​ location响应报头域,常用在更换域名时

    response.sendRedirect("http://www.baidu.com");
    
  • refresh:定时自动跳转到 [url]指定的页面(单位为秒),可以在页面通过meta标签实现,也可以在后台实现;不带 [url] 是指每 [content] 秒刷新一次页面

    <meta http-equiv="refresh" content="3;url=http://www.baidu.com">
    

Servlet的实现

​ Servlet是Server与Applet的缩写,是服务端小程序的意思,使用Java编写的服务端程序可以生成动态的web页。servlet主要运行在服务器端,由服务器调用执行,是一种按照Servlet标志来开发的类。即:要实现web开发,需要实现Servlet标志

  • Servlet 本身也是Java类,但是需要遵循Servlet规范进行编写。他没有main()方法,他的创建、使用、销毁都由Servlet容器(如Tomcat)进行管理。即:写自己的类,不写main()方法,提供给别人调用
  • Servlet 和HTTP 协议紧密连接,他可以处理 HTTP 协议相关的内容
  • 提供 Servlet 功能的服务器就是 Servlet 容器;如:Tomcat、Jetty、WebLogic Server、WebSphere、JBoss 等等
/** * 实现 Servlet * 1. 创建普通 Java 类 * 2. 实现 Servlet 的规范,即继承 HttpServlet 类 * 3. 重写 Service 方法,用来处理请求 * 4. 设置注解,指定访问的路径 */// @WebServlet("/servlet01") // 设置注解// 指定多个访问路径@WebServlet(urlPatterns = {"/servlet01", "/servlet001"})public class Servlet00 extends HttpServlet {    @Override    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        System.out.println("11");        // 通过流输出数据到浏览器        resp.getWriter().write("hello");    }}

创建Web项目

'file' -> 'new' -> 'project' -> 左侧选择 'Java Enterprise' -> 其他库和框架栏勾选 'Web Application' -> 'next' -> 键入项目名,更改路径 -> 'finish'

更改站点名(项目名): 'Edit Configurations' -> 'Deployment' -> 'Application context'

实现Servlet规范

​ 即继承 HttpServlet类

package com.servlet;import javax.servlet.http.HttpServlet;public class Servlet00 extends HttpServlet {    // code here}

重写 Servlet 方法

​ 满足 Servlet 规范只是让我们的类能够满足接收请求的要求,接收到请求后需要对请求进行分析,以及进行业务逻辑处理,计算出结果,则需要添加代码,在规范中有一个叫service的方法,专门用来做请求处理的操作,业务代码可以写在该方法中

设置注解

​ 在完成代码编写后,需要向服务器说明特定的请求对应特定的资源

​ 开发 servlet 项目,使用 @WebServlet 将一个继承于 javax.servlet.http.HttpServlet 的类定义为 Servlet 组件

​ 在 Servlet3.0中,可以使用 @WebServlet 注解将一个继承于 javax.servlet.http.HttpServlet 的类标注为可以处理用户请求的 Servlet

servlet的生命周期

​ servlet 没有 main() 方法,不能独立运行,他的运行完全由 Servlet 引擎来控制和调度。所谓生命周期,指的是 servlet容器何时创建servlet实例,何时调用其他方法进行请求的处理、何时销毁其实例的整个过程。

  • 实例和初始化时机

    当请求到达容器时,容器查找该 servlet 对象是否存在,如果不存在,则会创建实例并进行初始化

  • 就绪 / 调用 / 服务阶段

    有请求到达容器时,容器调用 servlet 对象的 service() 方法,处理请求的方法在整个生命周期中可以被多次调用;HttpServlet的 service() 方法,会根据请求方式来调用 doGet() 或者 doPost() 方法。但是,这两个方法默认情况下会抛出异常,需要子类去 override

  • 销毁时机

    当容器关闭时(应用程序停止时),会将程序中的 Servlet 实例进行销毁

    上述的生命周期可以通过 Servlet 中的生命周期方法来观察。在 Servlet 中有三个生命周期方法,不由用户手动调用,而是在特定的时机由容器自动调用,观察这三个生命周期方法即可观察到 Servlet 的生命周期

init方法,在Servlet实例创建之后执行(证明 Servlet有实例创建了),在整个生命周期内只执行一次

public void init(ServletConfig config) throws ServletException{    System.out.println("实例被创建了...");}

service方法,每次有请求到达某个 Servlet 方法时执行,用来处理请求(证明该Sevlet 进行服务了)。此方法可以多次调用

public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException{    System.out.println("服务被调用...");}

destroy方法,Servlet 实例销毁时执行(证明该 Servlet 的实例被销毁了),同一生命周期只执行一次

public void destroy(){    System.out.println("实例销毁了...");}

​ Servlet的生命周期,可以分为四部:servlet类加载 -> 实例化 -> 服务 -> 销毁

​ servlet工作流程:

​ 1. 客户端向servlet容器发出HTTP请求

​ 2. servlet 容器 接收 客户端的请求

​ 3. servlet 容器创建一个 HttpServletRequest 对象,将客户端请求信息封装到这个对象中

​ 4. servlet 容器创建一个 HttpServletResponse 对象

​ 5. servlet 容器调用 HttpServlet 对象的 service 方法,把Request与 Response 作为参数,传给 HttpServlet

​ 6. HttpServlet 调用 HttpServletRequest 对象有关方法,获取 Http 请求消息

​ 7. HttpServlet 调用 HttpServletResponse 对象有关方法,生成响应数据

​ 8. servlet 容器把 HttpServlet 的响应结果传给客户端

HttpServletRequest 对象

​ 用来接收客户端发送过来的请求信息,例如请求参数或请求头信息。service() 方法中形参接收的是 HttpServlet 接口的实例化对象。表示该对象主要应用于Http协议

​ HttpServletRequest 是 ServletRequest 的子接口,SerlvetRequest 只有他一个子接口。接口不合并的原因是因为现在主要使用 http 协议,后续如果出现更多协议就可以直接继承ServletRequest就行

接收请求

常用方法
// getRequestURL()  获取客户端请求的完整 url(从http开始,到 ? 前面结束)	String url = request.getRequestURL().toString();// getRequestURI()  获取客户端请求的部分URL(从站点名开始,到 ? 前结束)	String uri = request.getRequestURI();// getQueryString()  获取请求中的参数部分	String queryString = request.getQueryString();// getMethods()  获取客户端请求方式	String methods = request.getMethods();// getProtocol()  获取 HTTP 版本号	String protocol = request.getProtocol();// getContextPath()  获取 webapp 名字	String webapp = request.getContextPath();
获取参数方法
// getParameter(val) 获取指定名称的参数,返回字符串	String uname = request.getParameter("uname");// getParameterValues(String name) 获取指定名称参数的所有值,返回数组(一般用于复选框传值)	String[] hobbys = request.getParameterValues("hobby");

请求乱码

​ 在 tomcat8 版本之后,GET请求不会乱码

​ request的默认语言编码是 ISO-8859-1 此编码不支持中文,解析时会出现乱码。我们需要设置 request 的编码方式或在接收到数据后通过对应编码格式还原

方式一
request.setCharacterEncoding("UTF-8");

这种方式只针对 POST 有效(必须在接收所有数据之前设定)

方式二
new String(request.getParameter(name).getBytes("ISO-8859-1"), "UTF-8");

借助String对象的方法,该请求方式对任何请求有效,是通用的

请求转发

​ 请求转发是一种服务器行为 (此操作由服务器控制)。当客户端请求到达后,服务器进行转发,此时会将请求对象进行保存,地址栏中的 URL地址不会改变,得到响应后,服务端再将响应发送给客户端。从始至终只有一个请求被发出,数据可以共享

实现方式如下,达到多个资源协同响应的效果

request.getRequestDisPatcher(url).forward(request, response);

request作用域

​ 通过该对象可以在一个请求中传递数据,作用范围:在一次请求中有效,即服务器跳转有效

// 设置域对象内容request.setAttribute(String name, Object value);// 设置域对象内容request.getAttribute(String name);// 删除域对象内容request.removeAttribute(String name);

​ request 域对象中的数据在一次请求中有效,则经过请求转发,request 域中的数据依然存在,则在请求转发的过程中可以通过 request 来传输 / 共享数据

HttpServletResponse 对象

​ Web服务器会针对每次收到的客户端http请求分别创建一个 request请求对象和 response响应对象

​ 通过 request 对象获取客户数据;通过 response 对象向客户端输出数据

​ HttpServletResponse 对象的主要功能是用于服务器对客户端的请求进行响应,将 Web服务器处理好的结果返回给客户端。 service() 方法中形参接收的是 HttpServletResponse 接口的实例化对象。这个对象中封装了向客户端发送的数据、发送响应头,发送响应码的方法

响应数据

​ 响应时需要获取输出流

​ getWrite() 获取字符流(只能响应回字符)

​ getResponse() 获取字节流(能响应一切数据)

​ 响应回的数据到客户端被浏览器解析;两者不能同时使用

// 字符输出流PrintWrite write = response.getWrite();write.write("hello");write.write("<h2>Hello</h2>")
// 字节输出流ServletOutputStream out = response.getOutputStream();out.write("hello".getBytes());out.write("<h2>Hello</h2>".getBytes());

响应乱码

​ 在响应内容中含有中文,则可能会出现乱码,造成原因是服务器和客户端使用了不同的编码格式

getWrite() 字符乱码

​ 由于服务器进行编码默认使用 ISO-8859-1 格式编码,该编码不支持中文,所以我们需要替换服务器的编码格式

response.setCharacterEncoding("UTF-8");

​ 同时,指定客户端的解码方式

response.setHeader("content-type", "text/html;charset=UTF-8");

​ 保证发送端和接收端的编码一次

同时设置服务器和客户端的编码格式

response.setContentType("text/html;charset=UTF-8");
getOutputStream() 字节乱码

​ 由于此方式获取到的字节流响应时本身就是传输的字节,所以有可能出现乱码,当服务器和客户端使用的编码方式一致时则正确显示。

​ 为了保存数据正确性,应当同时设置客户端和服务器的编码格式

重定向

​ 重定向是一种服务器指导的客户端行为,服务器在接收到客户端发出的请求后会在响应的同时给客户端一个新的地址(response.sendRedirect(url));当客户端收到响应后会立刻发出第二个请求,服务器接收请求并作出相应,重定向完成

​ 重定向中有两个请求存在,并且属于客户端行为

​ 地址栏会发生改变

​ 有两次请求,数据无法共享

// 重定向跳转到 index.jsp 页面response.SendRedirect("index.jsp");
请求转发与重定向的区别

image-20210729163152466

Cookie对象

​ Cookie是浏览器提供的一种技术,通过服务器的程序能将一些只需要保存在客户端或者在客户端进行处理的数据放在本地计算机上,不需要通过网络传输,并且能够减少服务器的负载,但是由于 cookie是服务端保存在客户端的信息,所以其安全性比较差。常见的记住密码就是通过cookie来实现的

​ 有一个专门操作 Cookie 的类 javax.servlet.http.Cookie 服务器端响应发送给客户端,保存在浏览器。下次再访问时把 Cookie 带回服务器

​ Cookie 的格式:键值对用 "=" 连接,多个键值对用 ";" 隔开

Cookie对象的创建和发送

​ 通过 new Cookie("key", "value"); 来创建一个 Cookie 对象,要想将 Cookie 随响应发送到客户端需要先添加到 response 对象中,response.addCookie(cookie) ; 此时该 Cookie 对象会随着响应发送到客户端,在浏览器上可以看到

// 创建 Cookie 对象Cookie cookie = new Cookie("uname", "Zhangsan");// 发送 Cookie 对象response.addCookie(cookie);

​ 返回的是数组,获取单个 cookie 需要遍历数组;getName() 获取名称、getValue() 获取 cookie值

// 获取 Cookie 数组Cookie[] cookies = request.getCookies();// 判断数组是否为空if(cookies != null && cookies.length > =){    // 遍历 cookies 数组    for(Cookie cookie : cookies){        System.out.println(cookie.getName());        System.out.println(cookie.getValue());    }}

​ cookie 的到期时间指该 cookie 何时失效,默认为浏览器关闭即失效。我们可以手动设定 cookie 的有效时间,通过 setMaxAge(int time); 方法设定 cookie 的最大有效时间,单位是秒

到期时间的取值
  • 负整数

    若为负数表示不存储该 cookie

    cookie 的maxAge 属性默认值就是 -1,表示只在浏览器内存中存活,一旦关闭浏览器窗口,那么cookie就会消失

  • 正整数

    若大于0的整数,表示存储的秒数

    表示 cookie 对象可存活指定的秒数,当生命大于 0 时,浏览器会把 cookie 保持到硬盘,就算关闭浏览器或重启客户端电脑,cookie 也会存活相应的时时

  • 若为0,表示删除该cookie

    cookie 生命等于0是一个特殊的值,表示 cookie 被作废,就算浏览器已经保存了这个cookie,通过 cookie 的 setMaxAge(0)来删除这个cookie,无论在浏览器内存还是客户端硬盘都会删除这个cookie

Cookie的注意点

  1. cookie 只会保存在当前浏览器中
  2. cookie中不能出现中文,如果有中文则通过 URLEncoder.encode() 来进行编码,获取时使用 URLDecode.decode() 来解码
String name = "姓名";String value = "张三";// 发送时编码name = URLEncoder.encode(name);value = URLEncoder.encode(value);// 创建cookie对象Cookie cookie = new Cookie(name, value);// 发送cookieresponse.addCookie(cookie);// 获取时解码URLDecoder.decode(cookie.getName());URLDecoder.decode(cookie.getValue());
  1. 同名 cookie 问题

    如果服务器发送了重复的cookie,那么新的cookie会覆盖原有的cookie

  2. 浏览器存放 Cookie 的数量

    cookie 的存储是有上限的,也会有大小的限定。cookie一般是由服务器创建和设定,后期结合Session来实现回话跟踪

Cookie的路径

​ cookie 的 setPath() 方法设置 cookie 的路径,这个路径直接决定服务器的请求是否会从浏览器中加载某些 cookie。

情况1:当前服务器下任何项目的任意资源都可以获取Cookie对象

// 当前项目为 s01Cookie cookie = new Cookie("XXX", "XXX");// 设置路径为 "/" 表示在当前服务器下任何项目都可以访问到 Cookie 对象Cookie.setPath("/");response.assCookie(cookie);

情况2:当前项目下的资源可获取 Cookie 对象(默认不设置 Cookie 的path)

// 当前项目为 s01Cookie cookie = new Cookie("XXX", "XXX");// 设置路径为 "/01" 表示在当前项目下任何项目都可以访问到 Cookie 对象Cookie.setPath("/s01"); // 默认情况response.assCookie(cookie);

情况3:指定项目下的资源可以获取 Cookie 对象

// 当前项目为 s01Cookie cookie = new Cookie("XXX", "XXX");// 设置路径为 "/02" 表示只有在 s02 项目下才可以访问到 Cookie 对象;Cookie.setPath("/s02"); // 默认情况response.assCookie(cookie);

情况4:指定目录下的资源可以获取 Cookie 对象

// 当前项目为 s01Cookie cookie = new Cookie("XXX", "XXX");// 设置路径为 "/01/cook" 表示只有在 s01/cook 目录下才可以访问到 Cookie 对象;Cookie.setPath("/s01/cook"); // 默认情况response.assCookie(cookie);

​ 如果设置了 path,当前访问路径包含了 cookie 的路径(当前访问路径在 cookie 路径基础上要比 cookie 的范围小) cookie 机会加载到 request 对象中

​ cookie 的路径是指可以访问该 cookie 的顶层目录,该路径的子路径也可以访问该 cookie

总结:当访问的路径包含了 cookie 的路径时,则该请求将带上该 cookie;如果访问路径不包含 cookie 路径,则该请求不会携带 cookie

HttpSession对象

​ HttpSession 对象是 java.servlet.http.HttpSession 的实例,他是一个纯粹的接口,不像 HttpServletRequest或HttpServletResponse 一样有父接口。他本身就属于 HTTP 协议的范畴

​ 对于服务器而言,每一个连接到他的客户端都是一个 Session ,servlet 容器使用此接口创建 HTTP 客户端和 HTTP 服务器之间的会话。会话将保留指定的时间段,跨多个连接或来自用户的页面请求。一个会话通常对应一个用户,该用户可能多次访问一个站点。可以通过此接口查看和操作有关某个会话的信息,比如说会话标识符、创建时间和最后一次访问时间。在整个Session 中,最重要的就是属性的操作

​ Session 无论客户端还是服务器都可以感知到,若重新打开一个新的浏览器则无法取得之前设置的 Session。因为每一个 Session 只保存在当前的浏览器中,并且只能在对应的页面才能获取

​ Session 的作用就是为了标识一次会话,或者说确定一个用户;在一次会话(一个用户多次请求)期间共享数据。我们可以通过 request。getSession() 方法来获取当前的 Session 对象

// 如果当前 Session 对象存在,则获取;如果 Session 对象不存在,则会创建HttpSession session = request.getSession();

标识符 JSESSIONID

​ 每个 Session 都有一个唯一表示符

​ 每当一次请求到达服务器,如果开启了会话,服务器第一步会查看是否从客户端回传一个名为 JSESSIONID 的 cookie。如果没有就会创建一个新的 session 对象,并加上一个唯一标识符。服务器是根据 JSESSIONID 值查看是否含有该 session对象

​ session 是一个特殊的cookie,他的值是请求获取到的 session 对象;他的有限时间截至到浏览器关闭,session 的底层还是依赖 cookie 来实现

Session 域对象

​ Session 用来表示一次会话,在一次会话中数据可以共享,这是 session 作为域对象存在,可以通过 setAttribute(name, value) 方法向域对象中添加数据。通过 getAttribute(name) 从域对象中获取数据,通过 removeAttribute(name) 从域对象中移除数据

// 获取 session 对象HttpSession session = request.getSession();// 设置 session 域对象session.setAttribute("uname", "admin");// 获取指定名称的 sessionString uname = (String)request.getAttribute("uname");// 移除指定名称的 session 域对象session.removeAttribute("uname");

​ 数据存储在 session 域对象中,当 session 对象不存在了,或者两个不同的 session 对象时,数据就不能共享了,这就需要使用 session 生命周期

Session 对象的销毁

默认到期时间

​ 当客户端第一次请求 servlet 并且操作时,session 对象生成,Tocat 中的session 默认的存活时间为 30min,即不操作界面的时间,操作界面就会重新计时

​ 修改 session 的默认到期时间

# Tomcat conf目录下 web.xml 文件中修改<session-config>    <session-timeout>30</session-timeouts></session-config>
自己设定到期时间

​ 我们可以在程序中设置 session 的到期时间,不用修改 tomcat;通过 session.setMaxInactiveInterval(int),单位是秒

// 获取session对象HttpSession session = request.getSession();// 设置session的最大不活动时间session.setMaxInactiveInterval(15); // 15秒 

​ 我们也可以通过 getMaxInactiveInterval() 方法来查看当前 session 对象的最大不活动时间

// 获取 session 的最大不活动时间int time = session.getMaxInactiveInterval();
立即失效

​ 通过 session.invalidate() 方法让 session 立刻失效

// 销毁对象session.invalidate();
关闭浏览器

​ session 底层依赖与 cookie,cookie的有效时间为关闭浏览器。从而关闭cookie就会不在与 JSESSION 对应,session就失效了

关闭服务器

​ 关闭服务器,session即销毁。意味着此次会话结束,数据共享结束

ServletContext 对象

​ 每个 web 应用都有且仅有一个 ServletContext 对象,又称 Application 对象,该对象始于应用程序相关的。在 Web 容器启动时,会给每一个web程序创建一个 ServletContext 对象

​ 该对象有两个作用:

​ > 作为域对象用来共享数据,此数据在整个应用程序中共享

​ > 该对象中保存了当前应用程序的相关信息;例如可以通过 getServerInfo() 获取当前服务器信息、getRealPath(String path) 获取资源真实路径

ServletContest 对象的获取

  1. 通过 request 对象获取

    // 只要有 request 对象就可以获取 servletContext 对象ServletContext servletContext = request.getServletContext();
    
  2. 通过 session 对象获取

    ServletContext servletContext = request.getSession().getServletContext();
    
  3. 通过 servletConfig 对象获取,在 Servlet 标准中提供了ServletConfig 方法

    ServletConfig servletConfig = getServletConfig();ServletContext servletContext = servletConfig.getServletContext();
    
  4. 直接获取,Servlet 类中提供了直接获取 ServletContext 对象的方法

    ServletContext servletContext = getServletContext();
    

常用方法

// 获取 servletContext 对象ServletContext servletContext = request.getServletContext();// 获取当前服务器版本信息String servletInfo = servletContext.getServletInfo();// 获取项目的真实路径String realPath = servletContext.getRealPath("/");

ServletContext 域对象

​ 通过向 ServletContext 中存放数据,可以使整个应用程序共享某些数据。因为数据被保存后不手动移除会一直保存所以不建议存放过多数据

// 获取 Servlet Context 对象ServletContext servletContext = request.getServletContext();// 设置域对象servletContext.setAttribute("name", "zhangsan");// 获取域对象String name = (String) servletContext.getAttribute("name");// 移除域对象servletContext.removeAttribute("name");

Servlet 的三大域对象

  1. request 域对象

    在一次请求中有效;请求转发有效、重定向失效

  2. session 域对象

    在一次会话中有效;请求转发和重定向都有效、session销毁后失效

  3. servletContext 域对象

    在整个应用程序中有效;服务器关闭后失效

文件上传和下载

文件上传

​ 文件上传涉及到前台页面和后台服务端代码的编写;前台发送文件、后台接受并保存文件,这属于一个完整的文件上传

前台页面

​ 上传文件的页面一般是一个表单,并且请求方式为 POST,其次表单的 enctype 属性必须设置为 enctype = "multipart/form-data";意思是设置表单类型为文件上传表单,属性的默认类型是application/x-www-form-urlencoded 不能用于文件上传。必须修改、

<!--	文件上传表单		1. 表单提交类型 POST		2. 表单类型 enctype="multipart/form-data" 		3. 表单元素类型 文件域设置 name 属性值--><form method="post" action="uploadServlet" enctype="multipart/form-data">    姓名:<input type="text" name="uname"><br/>    文件:<input type="file" name="myfile">br/>    <!-- button 默认的 type 属性是 submit,做提交按钮可以不设置 type -->    <button type="submit">提交</button></form>
后台实现

​ 使用注解 @MultipartConfig标识为支持文件上传, Servlet 将 Multipart/form-data 的post请求封装成 Part 通过part对上传文件进行操作

@WebServlet("/uploadServlet")@MultipartConfig // 文件上传必须加的注解,前台加了这个注解后端不加会报空指针错误public class UploadServlet extends HttpServlet {    @Override    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        System.out.println("文件上传...");        // 为保证数据正确性,首先设置编码        req.setCharacterEncoding("UTF-8");        // 获取普通表单项参数        String uname = req.getParameter("uname"); // 表单元素中 uname 的值        System.out.println("uname:" + uname);        // 获取 Part 对象,上传的文件        Part part = req.getPart("myfile"); // 表单中 file 文件域的 name 值        // 通过 part 对象得到上传的文件名        String fileName = part.getSubmittedFileName();        System.out.println("上传的文件名:" + fileName);        // 得到文件存放的路径        String filePath = req.getServletContext().getRealPath("/");        System.out.println(filePath); // 文件存放的路径        // 存放文件        part.write(filePath + "/" + fileName);    }}

文件下载

​ 即将服务器上的资源下载(拷贝)到本地,可以通过两种方式下载:通过超链接下载、通过代码下载

​ 项目内的文本或图片资源文件夹需要通过 Tomcat -> Deployment 进行上传才能通过项目发布,进行访问

超链接下载

​ 当超链接遇到浏览器不能识别的资源是会自动下载,否则会直接显示资源。我们也可以设置 download 属性规定浏览器下载(有些浏览器不支持)

默认下载

<!-- 浏览器不识别的资源,自动下载 --><a href="test.zip">点击下载</a>

值得 download 属性下载

<a href="test.txt" download>点击下载</a>

​ download 属性可以不写任何信息,会自动使用默认的文件名。如果设置了 download 属性的值,则使用设置的值作为文件名。当用户打开浏览器点击链接时会自动下载该文件

后台代码实现下载
实现步骤
  1. 需要通过 response.setContentType() 方法设置 Content-type 头字段的值,为浏览器无法使用某种方法或激活某个程序来处理的 MIME 类型,例如 application/octet-streamapplication/x-msdownload
  2. 需要通过 response.setHeader() 方法设置 Content-Disposition 头的值为 attachment;filename=文件名
  3. 读取下载文件,调用 response.getOutputStream() 方法向客户端写入附件内容
代码实现
@WebServlet("/downloadServlet")public class DownloadServlet extends HttpServlet {    @Override    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        System.out.println("文件下载...");        // 设置编码格式        req.setCharacterEncoding("UTF-8");        resp.setContentType("text/html;charset=UTF-8");        // 获取参数;非空判断,去除字符串前后空格        String fileName = req.getParameter("fileName");        if(fileName == null || "".equals(fileName.trim())){            resp.getWriter().write("请输入要下载的文件名");            resp.getWriter().close();            return;        }        // 获取路径        String path = req.getServletContext().getRealPath("/download/");        // 通过路径得到文件对象        File file = new File(path + fileName);        // 判断文件对象是否存在并是一个标准文件        if(file.exists() && file.isFile()){            // 设置响应类型            resp.setContentType("application/x-msdownload");            // 设置头信息            resp.setHeader("Content-Disposition", "attachment;filename=" + fileName);            // 得到文件输入流            InputStream in = new FileInputStream(file);            // 得到字节输出流            ServletOutputStream out = resp.getOutputStream();            // 定义 byte 数组            byte[] bytes = new byte[1024];            // 定义长度            int len = 0;            // 循环输出            while ((len = in.read(bytes)) != -1){                // 输出                out.write(bytes, 0, len);            }            out.close();            in.close();        }else {            resp.getWriter().write("文件不存在");            resp.getWriter().close();        }    }}

过滤器和监听器

  • 过滤器随着tomcat启动而建立,tomcat销毁时过滤器也销毁

  • 判断用户权限是否能执行相应操作

过滤器 Filter

​ 用于在 Servlet 之外对 Request 或者 Response 进行修改。他主要用于对用户请求进行预处理,也可以对 HttpServletResponse 进行最后处理。

​ Filter处理流程:对用户请求进行预处理 -> 将请求交给Servlet进行处理并生成响应 -> 对服务器进行后处理

​ 在一个 web 应用中,可以开发编写多个 filter,他们组合起来称作一个 Filter 链

image-20210810165651834

image-20210810165910779

​ 若是一个过滤器链,先配置 (按照首字母排序) 的先执行(请求时的执行顺序);响应时顺序相反

​ 在 HttpServletRequest 到达 Servlet 之前,拦截客户的 HttpServletRequest。根据需要检查 HttpServletRequest,也可以修改 HttpServletRequest 头和数据

​ 在HttpServletResponse 到达客户端之前,拦截 HttpServletResponse。根据需要检查 HttpServletResponse,也可以修改 HttpServletResponse头和数据

过滤器实现

​ 通过 javax.servlet.Filter 接口实现过滤器,其中定义三个方法:init()、doFilter()、destroy() 分别在相应的时机执行,后期通过对应方法观察其生命周期

​ 实现步骤

​ > 编写 Java 类实现接口,并实现 doFilter() 方法

​ > 通过 @WebFilter 注解设置他所能拦截的资源

@WebFilter("/*") // 拦截所有的请求资源public class Filter01 implements Filter {    // 初始化方法    @Override    public void init(FilterConfig filterConfig) throws ServletException {    }    // 过滤方法    @Override    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {        // 放行方法;必须写;放行方法前写做请求拦截、方法后做响应拦截        filterChain.doFilter(servletRequest, servletResponse);    }    // 销毁方法    @Override    public void destroy() {    }}
过滤器实例
请求乱码处理

​ Tomcat 8 及以上版本:post请求乱码需要处理,GET 请求不会乱码

​ Tomcat 7 及以下版本都会乱码,需要处理

@WebFilter("/*")public class Filter00 implements Filter {    public Filter00(){    }    @Override    public void init(FilterConfig filterConfig) throws ServletException {    }    @Override    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {        // 首先使请求、响应基于 HTTP        HttpServletRequest request = (HttpServletRequest) servletRequest;        HttpServletResponse response = (HttpServletResponse) servletResponse;        // 处理请求乱码(post请求)        request.setCharacterEncoding("UTF-8");        // 处理 GET 请求,服务器版本在 Tomcat8 以下        String method = request.getMethod();        // 如果是 GET 请求        if("GET".equalsIgnoreCase(method)){ // equalsIgnoreCase() 方法不区分大小写            // 服务器版本在 Tomcat8 以下            String serverInfo = request.getServletContext().getServerInfo();            // 得到具体的版本号            String version = serverInfo.substring(serverInfo.indexOf("/")+1, serverInfo.indexOf("."));            // 判断服务器版本是否小于8            if(Integer.parseInt(version) < 8){                // 得到自定义内部类                // MyWapper 继承了 HttpServletRequestWapper 对象,而 HttpServletRequestWapper 对象实现了 HttpServletRequest 接口。所以 MyWapper 本质也是 request 对象                HttpServletRequest myRequest = new MyWapper(request);                // 放行资源                filterChain.doFilter(myRequest, response);            }        }    }    @Override    public void destroy() {    }    /**     * 定义内部类,继承HttpServletRequestWrapper 包装类对象,重写 getParameter() 方法     */    class MyWapper extends HttpServletRequestWrapper{        // 定义成员变量,提升构造器中的 request 对象的范围        private HttpServletRequest request;        public MyWapper(HttpServletRequest request) {            super(request);            this.request = request;        }        @Override        public String getParameter(String name) {            String value = request.getParameter(name);            if(value != null && !"".equals(value.trim())){                try{                    // 将默认的 ISO-8859-1 编码字符转换成 UTF-8                    value = new String(value.getBytes("ISO-8859-1"), "UTF-8");                } catch (UnsupportedEncodingException e) {                    e.printStackTrace();                }            }            return value;        }    }}
用户非法访问拦截

​ 非法访问拦截(用户未登录,拦截请求到登录页面)

​ 拦截所有资源

​ 需要被放行的资源

​ > 不需要登录即可以访问的资源

​ > 放行指定页面(登录页面或者注册页面)

​ > 放行静态资源(例如:css、js、image 等资源)

​ > 放行指定操作(例如登录操作或者注册操作)

​ > 登录状态放行(如果存在指定 session 对象,则为登录状态)

@WebFilter("/*")public class LoginFilter implements Filter {    @Override    public void init(FilterConfig filterConfig) throws ServletException {    }    @Override    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {        // filterChain.doFilter(servletRequest, servletResponse);        HttpServletRequest request = (HttpServletRequest) servletRequest;        HttpServletResponse response = (HttpServletResponse) servletResponse;        // 获取请求路径并放行        String uri = request.getRequestURI();        System.out.println(uri);        if(uri.contains("login.jsp")){            filterChain.doFilter(servletRequest, servletResponse);            return;        }        // 放行静态资源        if(uri.contains("/js") || uri.contains("/html") || uri.contains("/css")){            filterChain.doFilter(servletRequest, servletResponse);            return;        }        // 指定操作放行        if(uri.contains("/login")){            filterChain.doFilter(servletRequest, servletResponse);            return;        }        // 判断是否登录状态        String uname = (String) request.getSession().getAttribute("user");        // 判断 session 是否为空        if(uname != null) {            filterChain.doFilter(servletRequest, servletResponse);            return;        }        // 当用户未登陆时,跳转到 登录页面(不能全部重定向,否则会死循环)        response.sendRedirect("login.jsp");    }

监听器

​ web监听器是 Servlet 中的一种特殊的类,能帮助开发者监听 Web 中的特定事件,比如 ServletContext、HttpSession、ServletRequest 等的创建和销毁;变量的创建、销毁或修改等。可以在某些动作后增加处理实现监控,比如统计在线人数

实现

​ 如果是写实现类需要加上注解@WebListener

监听器分为三类八种

​ (1)监听生命周期,对象的创建和销毁;需要在 xml 文件中配置

​ > ServletRequestListener

​ > HttpSessionListener

​ > ServletContextListener

​ (2)监听值的变化;需要在 xml 文件中配置

​ > ServletRequestAttributeListener

​ > HttpSessionAttributeListener

​ > ServletContextAttributeListener

​ (3)针对 Session 中的对象

​ > HttpSessionActivationListener

​ > HttpSessionBindingListener

实例

在线人数监控

实现步骤

​ > 创建监听器,实现 HttpSessionListener 接口

​ > 通过 @WebListener 注解配置该监听器

创建一个类,实现 HttpSessionListener 接口,用来检测 Session 的创建和销毁

  1. 在类中定义一个成员变量来存储当前的 Session个数 (OnlineListener.java)
@WebServlet("/login")public class LoginServlet extends HttpServlet {    @Override    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        System.out.println("用户登录...");        String uname = req.getParameter("name");        System.out.println(uname);        if("admin".equals(uname)){            resp.sendRedirect("index.jsp");            req.getSession().setAttribute("user", uname);        }else{            req.getRequestDispatcher("login.jsp").forward(req, resp);        }    }}// 监听器@WebListenerpublic class OnlineListener implements HttpSessionListener {    // 在线人数    private Integer onlineNumber = 0;    /**     * 新 session 被创建;人数 +1     * @param se     */    @Override    public void sessionCreated(HttpSessionEvent se) {        onlineNumber ++ ;        // 将人数设置到 ServletContext 中        se.getSession().getServletContext().setAttribute("onlineNumber", onlineNumber);    }    /**     * 新 session 被创建;人数 -1     * @param se     */    @Override    public void sessionDestroyed(HttpSessionEvent se) {        onlineNumber -- ;        // 将人数设置到 ServletContext 中        se.getSession().getServletContext().setAttribute("onlineNumber", onlineNumber);    }}

java 工具

maven

lombok

​ 添加注解,自动生成 toString、getter、setter 方法

  1. 添加依赖

            <dependency>            <groupId>org.projectlombok</groupId>            <artifactId>lombok</artifactId>            <optional>true</optional>        </dependency>
    
  2. 使用注解

    @Data // 生成setter/getter、equals、canEqual、hashCode、toString方法@Setter @Getter  // 单独生成 get set 方法@NoArgsConstructor  // 无参构造@AllArgsConstructor  // 全参构造
    

linux

(https://www.cnblogs.com/yuan-liu/p/15253329.html)

  常用命令

按i插入按Esc退出编辑按Shift+:输入指令:w 保存文件但不退出vi:w file 将修改另外保存到file中,不退出vi:w! 强制保存,不推出vi:wq 保存文件并退出vi:wq! 强制保存文件,并退出viq: 不保存文件,退出vi:q! 不保存文件,强制退出vi:e! 放弃所有修改,从上次保存文件开始再编辑whoami   		 			查看当前用户useradd [uasenamename]	  	新建一个用户su [username]				切换用户ps -ef | more				显示所有进程kill -[PID]					结束进程,-进程号init 0						关机,粗暴方式poweroff      				关机pwd     		 			打开当前所在目录cd      		 			切换目标路径ls -a   		 			查看目录下的文件ll      		 			以列的方式显示目录下的文件列表mkdir   		 			创建文件目录rm -rf 			 			删除文件或目录rm ri  			 			删除文件前询问用户cp     			 			复制文件命令mv     		 	 			移动文件或修改文件名 [mv 目录1 目录2] 目录1文件移到目录2

centos8 安装jdk(finalshell)

​ 需要 linux 系统有线连接成功

​ linux 控制台获取 ip地址(ifconfig);需要root用户

​ ens33 -> inet 就是IP地址

image-20210902162706168

​ finalshell 新建 SSH 连接

image-20210902162846543

​ 名称自取,主机即ip地址,端口默认22,认证方法选密码,用户为root用户,密码为自设的密码

image-20210902162941220

​ 新建弹出窗口选保存,然后连接

​ 连接成功可以输入命令 ifconfig 检测

image-20210902163155025

​ 输入相关命令建立文件夹

​ > cd /root 定位到root文件夹下

​ > mkdir jdk 创建名为jdk的文件夹

image-20210902163509145

​ 上传对应的压缩包到新建的文件夹内

​ 定位工作目录到 /root/jdk 下,使用 ls 命令检索到该目录下的 jdk 压缩包,使用 tar -xvf packageName 进行解压

image-20210902163903235

​ 命令行输入命令 vi /etc/profile 进入修改配置文件

image-20210902164648482

​ 拉到文件最下方,输入以下内容 /root/jdk 为jdk路径

export JAVA_HOME=/root/jdk/jdk1.8.0_131export JAVA_BIN=/root/jdk/jdk1.8.0_131/binexport PATH=$JAVA_HOME/bin:$PATHexport CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jarexport JAVA_HOME JAVA_BIN PATH CLASSPATH

​ 保存输入内容并退出,输入命令 source /etc/profile 使配置生效后输入 java -version 成功看到版本号则安装成功

image-20210902165321649

centos8 安装 tomcat(finalshell)

​ 定位到对应位置,解压文件

image-20210902165939338

​ 依次执行下列命令

安装防火墙,如果已安装会失败,走流程yum install firewalld firewalld-config启动防火墙systemctl start firewalld 放行8080端口firewall-cmd --zone=public --add-port=8080/tcp --permanent说明:–zone 作用域–add-port=8080/tcp 添加端口,格式为:端口/通讯协议–permanent #永久生效,没有此参数重启后失效重启防火墙firewall-cmd --reload或firewall-cmd --complete-reload两者的区别就是第一个无需断开连接,就是firewalld特性之一动态添加规则,第二个需要断开连接,类似重启服务检查已开放端口firewall-cmd --list-all定位到目标目录cd /root/tomcat/apach-tomcat-9.0.22/bin启动命令./startup.sh关闭命令./shutdown.sh

image-20210902170921834

​ 此时tomcat已经准备完毕,输入以下命令运行tomcat

​ 首先定位到 tomcat bin目录文件,使用 ./startup.sh 启动tomcat,在浏览器属于 Linux的 ip地址 + 端口号即可,关闭使用 ./shutdown.sh 命令

image-20210902171421472

linux 安装mysql

​ rpm 后缀文件解压方式不同于之前(使用 rpm -ivh packageName 进行解压)

image-20210902172156368

​ 解压完成后输入命令检测MySQL是否安装成功

ps -ef | grep mysqlmysqladmin --version

image-20210902172556446

git svn

​ 备份代码、版本控制

gitlab

​ 检入:上传代码 检出:下载代码

部分命令git remote -v		查看远程库git init			初始化本地库,会生成一个.git的隐藏文件git status   		检查本地仓库文件状态

linux 安装gitlab

​ 运行下列命令,初始化 git 安装环境

sudo dnf install -y curl policycoreutils openssh-server
sudo systemctl enable sshd
sudo systemctl start sshd
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo systemctl reload firewalld
sudo dnf install postfix
sudo systemctl enable postfix
sudo systemctl start postfix

​ 运行命令解压 gitlab rpm -i /root/gitlab-ce-12.10.10-ce.0.el8.x86_64.rpm 注意路径

​ 解压完毕运行 gitlab-ctl reconfigure 重新设置gitlab

​ 重新启动命令 gitlab-ctl restart

​ 浏览器访问服务器ip 即可进入 gitlab 页面,端口默认 80,默认用户为 root

​ 生成ssh key:GIT CMD 控制台输入 ssh-keygen -t rsa -C "2452791011@qq.com",根据提示进行操作,会在目标路径生成对应的.ssh文件夹。就得到了id_rsa和id_rsa.pub两个文件。其中id_rsa中的内容便是私钥,id_rsa.pub中的内容便是公钥

​ 添加 ssh key:gitlab网页右上角用户 -> settings -> 左侧 SSH Keys;将 id_rsa.pub 文件内容复制 ADD key

image-20210903145001627

​ 新增项目,设置为 public 类型。

​ 注意:设置完一定要再次运行 gitlab-ctl reconfigure,否则git账号不能进行ssh连接,会报权限不够的错误。

​ 设置完毕输入命令 ssh -T git@192.168.30.130 测试是否连接成功,出现welcome则是成功

image-20210903145526556

​ 查看提交作者:git config --global --list

git config --global user.name "username" 配置姓名

git config --global user.email "email" 配置email地址

​ 会在当前目录下产生 .gitconfig 文件保存配置信息

git add filename
git commit -m "commit info"
git push name [master]

​ 本地仓库推送到远程仓库

image-20210903153016261

git pull origin master  文件检出,从服务器同步数据到本地库
git add .   表示把所有文件暂存
git clone git@192.168.30.130:root/gitlab.git  从 gitlab 克隆数据文件到本地私有仓库
	git clone [url]  url可以使用获取 git remote -v 命令获取

IDEA 连接 gitlab

image-20210906101923257

前后端不分离

Thymeleaf

​ spring 官方建议替代 jsp ,有网无网皆可以运行,开箱即用,多方言整合,完美贴合spring

环境配置

jar包依赖

<dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-thymeleaf</artifactId></dependency>

spring环境

spring:  thymeleaf:    cache: false    prefix: classpath:/views/    mode: HTML    encoding: utf-8    servlet:      content-type: text/html

数据连接池配置

alibaba 数据连接池

<dependency>    <groupId>com.alibaba</groupId>    <artifactId>druid</artifactId>    <version>1.1.8</version></dependency>

配置文件

​ type 属性设置三方数据源类型

datasource:url: jdbc:mysql://127.0.0.1:3306/t1958?userSSL=false&useUnicode=true&characterEncoding=UTF-8username: rootpassword: '000000'driver-class-name: com.mysql.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSource

热部署

<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-devtools</artifactId>    <optional>true</optional>    <scope>true</scope></dependency>

服务监控与管理

<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-actuator</artifactId></dependency>

redis

​ 以 key-value 形式存储数据的内存型数据库,和传统的关系型数据库不同,不一定遵循传统数据库的一些基本要求

​ 这类数据库被称为 NoSQL(not only sql)

给 redis 键 设置过期时间

EXPIRE key seconds seconds取具体数值单位是秒,返回值为1是代表设置成功

TTL key 查看键剩余的生存时间

简介

优缺点

​ 优点:

​ 对数据高并发读写

​ 对海量数据的高效率存储和访问

​ 对数据的可扩展性和高可用性(集群性)

​ 缺点:

​ redis(ASID)处理非常简单

​ 无法做到太复杂的关系型数据库模型

数据结构和功能

操作命令

​ 启动 redis 命令 -> 连接redis 命令(需要在对应路径下使用命令)

image-20210906171622748

​ get 和 set 命令 (不存在键是新增,已经存在该键则是修改)

image-20210906171953347

五种数据类型操作

字符串(String)操作

​ 新增键值对:SET [key] [value]

​ 查看键对应的值:get [key]

​ 删除键值对:DEL [key]

set [key] [value] ex 10   		设置自动删除  (10秒不get就会自动删除)keys *   			   			获取所有键del [key]		   				删除对应的键值对incr [key] 	   					自增,数值加一(原子型)decr [key] 	   					自减,数值减一incrbyfloat [key] number  		操作浮点数,加对应的number,加负数可以实现减decrbyflaot

hash 数据类型数据

​ 添加hash数据:hset [hashName] [key] [value]

​ 获取hash数据:hget [hashName] [key] [value]

​ 获取多个hash数据:hmget [hashName] [key1] [key2] ...

HMSET hashkey name "redis tutorial" description "redis basic commands"

list 列表数据类型

image-20210906174539860

set 集合数据类型

image-20210906174805808

有序 sset 集合数据

​ 重复数据会覆盖前一次的数据

image-20210906175136160

事务

​ 存在语法错误所有命令都不会执行

​ 存在运行错误,除错误命令其他命令都会正常执行

​ 开启事务并执行(multi -> exec)

image-20210907091328081

​ 开启事务后取消(multi -> discard)

image-20210907091749695

​ 监视的键值被其他客户端修改,事务执行结果会返回空

image-20210907092913678

​ 事务回滚(不会回滚,代码内部错误不会执行)

image-20210907095339988

​ 消息发布与订阅(第二步也是发布,直接执行1,3然后发布)

image-20210907100927253

游标遍历

scan 0 第一次遍历从0开始

image-20210907101809355

​ 限制条数返回 scan 0 count [number] (使用 count 限制返回条数,返回条数有出入)

image-20210907102314346

​ 值得返回对应名称的键 scan 0 match [name] (match 关键字,支持正则表达式)

image-20210907102529794

redis三种模式

主从复制关系

​ 设置一个主机两从机

image-20210907104021600

​ 修改从机配置(redis.windows.config 79行)

​ 添加 slaveof 127.0.0.1 [port]

image-20210910090310254

​ 启动主机和从机

​ 分别从主机和从机路径下执行命令 redis-server.exe redis.windows.conf 启动各自的服务器

​ 输入命令,查看主机配置情况

image-20210907112517812

​ 主机添加键值对,从机连接查看。不能修改值则配置成功

哨兵模式

​ 从机配置与主从复制配置基本一致

​ 修改哨兵配置文件 sentinel monitor mymaster 127.0.0.1 [port] [number](指定哨兵的监视对象,number表示通过多少个哨兵可以选择新的主机)

image-20210907114256934

​ 哨兵模式启动

​ 启动主机和从机(与主从配置启动一样)

​ 启动哨兵命令 redis-server.exe ./redis.windows.conf --sentinel

​ 哨兵模式成功启动后,关闭主机。过一段时间随机一个从机就会顶替主机

集群模式

​ 修改对应的配置文件

image-20210907115542383

​ 修改配置结束运行所有服务机

​ 运行命令 redis-cli --cluster create 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 --cluster-replicas 1 等待提示键入 yes 等待集群创建

​ 集群中每个节点负责处理一部分哈希槽,可以人工分配槽位

redis持久化

​ 如果AOF和RDB都开启,优先使用AOF进行数据恢复

​ java往redis传数据需要全部实现序列化接口

RDB机制

​ RDB其实就是把数据以快照的形式保存在磁盘上。什么是快照呢,可以理解成把当前时刻的数据拍成一张照片保存下来。

​ 优势:

​ 适合大规模的数据恢复,对数据完整性和一致性要求不高。数据恢复时比 AOF 快

​ 劣势:

​ 如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失

​ 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务较长时间

AOF机制

​ 采用文件追加的方式,追加操作命令到文件内,不存放键值对。避免出现文件越来越大情况,新增了重写机制。

​ 优势:

​ 该机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步

​ 劣势:

​ 相同数据集的AOF文件要大于RDB文件,恢复速度慢于RDB

​ AOF运行效率比RDB慢,每秒同步策略效率好,不同步策略和RDB相同

Java连接redis

​ Java插入到redis数据是经过序列化的数据

​ springboot 连接 redis;导入对应 jar 包

<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency>    <groupId>org.redisson</groupId>    <artifactId>redisson</artifactId>    <version>3.16.1</version></dependency>

​ 修改配置文件

spring:  redis:    host: 127.0.0.1    port: 6379    database: 0

​ 部分方法

	@Autowired    private RedisTemplate redisTemplate;    /**     * 操作普通字符串     * @param key     * @param value     */    public void StringSet(String key, String value){        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();        valueOperations.set(key, value);    }    /**     * 操作列表     */    public void ListSet(String key, List<Object> values){        ListOperations<String, Object> listOperations = redisTemplate.opsForList();        for(Object value : values){            listOperations.leftPush(key, value);        }    }    /**     * 操作集合     * @param key     * @param values     */    public void SetSet(String key, Set<Object> values){        SetOperations<String, Object> setOperations = redisTemplate.opsForSet();        for(Object value : values){            setOperations.add(key, value);        }    }    /**     * 获取字符串     */    public String StringGet(String key){        ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();        return valueOperations.get(key);    }    /**     * 列表弹出元素     */    public Object ListLeftPop(String key){        ListOperations<String, Object> listOperations = redisTemplate.opsForList();        return listOperations.leftPop(key, 2, TimeUnit.SECONDS);    }    /**     * 集合弹出元素     */    public Object SetPop(String key){        SetOperations<String, Object> setOperations = redisTemplate.opsForSet();        return setOperations.pop(key);    }

Java通过连接池连接redis

​ springboot2开始不推荐使用 jedis (同步方式,效率低) 方式连接;目前推荐使用 lettuce(响应式,异步。效率高)

# 配置 jedis 参数spring:  redis:    host: 127.0.0.1    port: 6379    database: 0    lettuce:      pool:        max-active:

java配置redis集群、哨兵

# 配置 jedis 参数spring:  redis:    #host: 127.0.0.1    #port: 6379    #database: 0    lettuce:      pool:        max-active: 8    # 集群    cluster:      nodes: 127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381    # 哨兵    sentinel:      master:  # 哨兵模式主机的别名      nodes: #哨兵的地址

redis 锁

@GetMapping("/redis/lock")// 如果 key 在 redis中不存在,就会设置该键值对。存在则不执行// 同时还可以设置键的过期时间public void lock(){    Boolean result1 =        redisTemplate.opsForValue().setIfAbsent("redislock", "redislock1", 10, TimeUnit.SECONDS);    System.out.println("第一次" + result1);    Boolean result2 =        redisTemplate.opsForValue().setIfAbsent("redislock", "redislock2", 10, TimeUnit.SECONDS);    System.out.println("第二次" + result2);}

​ 加锁设置过期时间:预防加锁的客户端死机

​ 过期时间设置限制:

​ 删除别人的锁:对锁加上标识,在删除时进行判断

redlock

缓存

缓存雪崩

​ 缓存雪崩是指缓存中大批量数据到过期时间,而查询量巨大引起数据库压力过大甚至宕机。缓存击穿是并发同一条数据,缓存雪崩则是不同数据都过期了

​ 解决方案:

​ 缓存数据过期时间随机,防止同一时间大量数据过期

​ 设置热点数据永不过期

​ 如果是分布式部署,可以将热点数据分布在不同的缓存数据库

缓存穿透

​ 缓存穿透指的查询缓存和数据库中都不存在的数据,这样每次请求直接打到数据库,就好像缓存不存在一样。

​ 解决方案:

​ 加校验:如果 key 不存在就不放行,通过布隆算法判别

​ 返回不存在的查询结果

缓存击穿

  一个并发访问量比较大的key在某个时间过期,导致所有的请求直接打在DB上。

  缓存里面没有需要的数据,走数据库查询需要的数据

  解决方案:

    数据库加锁,查询到了就将数据放在缓存。后面就都走缓存

    提前放置数据到缓存,避免流量过多被击穿

    设置热点数据永不过期

    补救后进行限流,避免二次被击穿

lua

​ lua 数组下标从 1 开始计数

​ lua + redis 优点:

​ 在redis 服务器上执行

​ 不能被其他客户端的命令打断,保证了原子性

​ 减少网络交互过程

消息中间件

(https://www.cnblogs.com/yuan-liu/p/15249866.html)

​ RabbitMQ : 适合中小型项目,代码量较少

​ ActiveMQ :apach旗下产品,并发处理不如rabbitMQ

​ RocketMQ : 阿里旗下,同时支持消息中间件事务和数据库事务

​ Kafka : 功能最弱

rabbitMQ安装

​ 安装软件 Erlang -> rabbitmq

​ 配置环境变量

​ 新建 Erlang 环境变量: 变量名ERLANG_HOME , 变量值 -> erl文件目录路径

image-20210908174210400

​ 新建 rabbitmq 环境变量

image-20210908174844403

​ 配置 path : 新建两个变量 %ERLANG_HOME%\bin 和 %RABBITMQ_SERVER%\sbin。

image-20210908175029489

​ 开启 rabbitmq :win + R 键入命令: rabbitmq-server -detached

​ 关闭 rabbitmq :win + R 键入命令: rabbitmqctl stop

简介

​ 优点:

​ 销峰:限流,使流量数据变平稳

​ 解耦:降低程序耦合度

​ 缺点:

​ 系统开发复杂度提高

​ 中间环节多,容易在运行期间出错

死信

​ 不能被处理的消息

​ 避免传入多条相同数据造成死信,可以使用幂等对数据进行判断

​ 幂等:对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源的影响是一致的

​ 解决方式:

​ 消息带上过期时间属性,如果超时就由服务器进行删除

springboot + rabbitmq

​ 添加 jar包

<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-amqp</artifactId></dependency>

​ 配置文件

spring:  rabbitmq:    host: 127.0.0.1    port: 5672    password: guest    username: guest    virtual-host: /

rabbitmq交换器

​ 消费者消费一个消息,被消费的消息在队列中在还存不存在:

​ 被消费的消息会被服务器从队列中删除

​ 默认情况:只要消费者接受了消息,他马上就会删除消息

​ 确定消息被正确消费,只有正确证明了这个消息才能把消息从队列删除。

​ 采用手动确认的方式,数据不能丢失。自动确认模式可以丢失数据,性能更高

​ 如果服务器死了,没有被消费的消息在服务器重启后,还是否存在:

​ spring封装,默认情况下,消息队列会持久化消息

direct(默认)直连交换机

​ 精确匹配路由键

​ 如果路由键匹配的话,消息就投递到相应的队列

topic 主题交换机

​ 可以进行模糊匹配路由键

​ 支持在路由键中使用通配符,# 匹配路由键中一个或多个词,* 匹配路由键的一个词

fanout 扇形交换机

MyCat

​ 分库分表中间件

安装

​ 修改 conf -> wrapper.conf 文件

image-20210910095740700

​ mycat bin 路径下cmd ,执行 mycat install ; installed 则代表成功

image-20210910095831324

配置

	1. 在 server.xml 配置连接 mycat 的用户和密码	2. 在 schema.xml 下配置 mycat 和真实数据库的关联	3. rule.xml 配置分片规格	4. 重新启动 mycat 使配置生效

​ 配置 server 位置 conf ->server.xml

<!-- 表级 DML 权限设置 --><!-- 0表示不可以,1可以 -->		<privileges check="false">   <schema name="TESTDB" dml="0110" >    <table name="tb01" dml="0000"></table>    <table name="tb02" dml="1111"></table>   </schema></privileges>

​ 权限顺序:insertupdateselectdelete

server.xml 详解

<?xml version="1.0" encoding="UTF-8"?><!-- - - Licensed under the Apache License, Version 2.0 (the "License"); 	- you may not use this file except in compliance with the License. - You 	may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 	- - Unless required by applicable law or agreed to in writing, software - 	distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT 	WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the 	License for the specific language governing permissions and - limitations 	under the License. --><!DOCTYPE mycat:server SYSTEM "server.dtd"><mycat:server xmlns:mycat="http://io.mycat/">	<system>	<!--配置的端口号-->    <property name="serverPort">8066</property>	<property name="nonePasswordLogin">0</property> <!-- 0为需要密码登陆、1为不需要密码登陆 ,默认为0,设置为1则需要指定默认账户-->	<property name="useHandshakeV10">1</property>	<property name="useSqlStat">0</property>  <!-- 1为开启实时统计、0为关闭 -->	<property name="useGlobleTableCheck">0</property>  <!-- 1为开启全加班一致性检测、0为关闭 -->		<property name="sequnceHandlerType">2</property>	<property name="subqueryRelationshipCheck">false</property> <!-- 子查询中存在关联查询的情况下,检查关联字段中是否有分片字段 .默认 false -->      <!--  <property name="useCompression">1</property>--> <!--1为开启mysql压缩协议-->        <!--  <property name="fakeMySQLVersion">5.6.20</property>--> <!--设置模拟的MySQL版本号-->	<!-- <property name="processorBufferChunk">40960</property> -->	<!-- 	<property name="processors">1</property> 	<property name="processorExecutor">32</property> 	 -->        <!--默认为type 0: DirectByteBufferPool | type 1 ByteBufferArena | type 2 NettyBufferPool -->		<property name="processorBufferPoolType">0</property>		<!--默认是65535 64K 用于sql解析时最大文本长度 -->		<!--<property name="maxStringLiteralLength">65535</property>-->		<!--<property name="sequnceHandlerType">0</property>-->		<!--<property name="backSocketNoDelay">1</property>-->		<!--<property name="frontSocketNoDelay">1</property>-->		<!--<property name="processorExecutor">16</property>-->		<!--			<property name="serverPort">8066</property> <property name="managerPort">9066</property> 			<property name="idleTimeout">300000</property> <property name="bindIp">0.0.0.0</property> 			<property name="frontWriteQueueSize">4096</property> <property name="processors">32</property> -->		<!--分布式事务开关,0为不过滤分布式事务,1为过滤分布式事务(如果分布式事务内只涉及全局表,则不过滤),2为不过滤分布式事务,但是记录分布式事务日志-->		<property name="handleDistributedTransactions">0</property>					<!--			off heap for merge/order/group/limit      1开启   0关闭		-->		<property name="useOffHeapForMerge">1</property>		<!--			单位为m		-->        <property name="memoryPageSize">64k</property>		<!--			单位为k		-->		<property name="spillsFileBufferSize">1k</property>		<property name="useStreamOutput">0</property>		<!--			单位为m		-->		<property name="systemReserveMemorySize">384m</property>		<!--是否采用zookeeper协调切换  -->		<property name="useZKSwitch">false</property>		<!-- XA Recovery Log日志路径 -->		<!--<property name="XARecoveryLogBaseDir">./</property>-->		<!-- XA Recovery Log日志名称 -->		<!--<property name="XARecoveryLogBaseName">tmlog</property>-->		<!--如果为 true的话 严格遵守隔离级别,不会在仅仅只有select语句的时候在事务中切换连接-->		<property name="strictTxIsolation">false</property>				<property name="useZKSwitch">true</property>			</system>		<!-- 全局SQL防火墙设置 -->	<!--白名单可以使用通配符%或着*-->	<!--例如<host host="127.0.0.*" user="root"/>-->	<!--例如<host host="127.0.*" user="root"/>-->	<!--例如<host host="127.*" user="root"/>-->	<!--例如<host host="1*7.*" user="root"/>-->	<!--这些配置情况下对于127.0.0.1都能以root账户登录-->	<!-- 此处是开放了本机和外网的请求链接,因为我的mycat安装在虚拟机上,所以本机是通过ip链接	192.*是为了让navicat可以连接使用(配置完整的虚拟机IP也可以)	127.*是为了让虚拟机本身可以通过命令连接-->	<firewall>	   <whitehost>	      <host host="192.*" user="root"/>		  <host host="127.*" user="root"/>	   </whitehost>       <blacklist check="false">       </blacklist>	</firewall>		<!-- 此处定义了一个root用户,可以管理的逻辑库为mydatabase,对应schema.xml中的<schema name="mydatabase" > -->	<user name="root" defaultAccount="true">		<property name="password">123456</property>		<property name="schemas">mydatabase</property>				<!-- 表级 DML 权限设置 -->		<!-- 				<privileges check="false">			<schema name="TESTDB" dml="0110" >				<table name="tb01" dml="0000"></table>				<table name="tb02" dml="1111"></table>			</schema>		</privileges>				 -->	</user>	<!--<user name="user">		<property name="password">123456</property>		<property name="schemas">mycat1,test3</property>		<property name="readOnly">true</property>	</user>--></mycat:server>

​ 连接数据库 schema.xml

<dataNode name="dn1" dataHost="localhost1" database="db1" /><dataNode name="dn2" dataHost="localhost1" database="db2" /><dataNode name="dn3" dataHost="localhost1" database="db3" /><!--<dataNode name="dn4" dataHost="sequoiadb1" database="SAMPLE" />  <dataNode name="jdbc_dn1" dataHost="jdbchost" database="db1" /> <dataNode	name="jdbc_dn2" dataHost="jdbchost" database="db2" /> <dataNode name="jdbc_dn3" 	dataHost="jdbchost" database="db3" /> --><!-- JDBC --><dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"          writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">    <heartbeat>select user()</heartbeat>    <!-- can have multi write hosts -->    <writeHost host="hostM1" url="localhost:3306" user="root"               password="000000">    </writeHost>    <!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> --></dataHost>

schema.xml 配置详解

<?xml version="1.0"?><!DOCTYPE mycat:schema SYSTEM "schema.dtd"><mycat:schema xmlns:mycat="http://io.mycat/">	<!--	name:为mycat逻辑库的名字,对应server<property name="schemas">mydatabase</property>,	建议设置跟数据库一样的名称	checkSQLschema:自动检查逻辑库名称并拼接,true会在sql语句中的表名前拼接逻辑库名,	例如select * from mydatabase.t_user;	sqlMaxLimit:查询保护、如果没有写limit条件,会自动拼接。只查询100条。	-->	<schema name="mydatabase" checkSQLschema="true" sqlMaxLimit="100">		<!--		name:为物理数据库的表名,命名与物理数据库的一致 		dataNode:为dataNode标签(<dataNode name="dn1" dataHost="dtHost1" database="db1" />)里面的name值		dataNode里面填写的节点数量必须和rule里面的规则数量一致		例如rule里面只定义了两个0-1M=0  1M-2M=1那么此处只可以指定两个节点,1M=10000,M为单位万		primaryKey:为表的ID字段,建议和rule.xml里面指定的ID和物理库的ID一致		rule:分片规则,对应rule.xml中<tableRule name="student_id">的name		type:表格类型,默认非global,用于全局表定义		-->		<table name="t_user" dataNode="dn1,dn2,dn3" primaryKey="id" rule="auto-sharding-long">			<!--ER分片注意childTable 标签需要放到table标签内,是主外键关联关系,				name:为物理数据库的表名,命名与物理数据库的一致 				primaryKey:为表t_loginlog的ID字段,建议和rule.xml里面指定的ID和物理库的ID一致.				joinKey:从表t_loginlog的外键字段,需要和物理库的字段名称一致				parentKey:为主表t_user的字段名,依据此字段做关联,进行ER分片			-->					<childTable name="t_loginlog" primaryKey="id" joinKey="user_id" parentKey="id"></childTable>		</table>		<table name="t_student" dataNode="dn1,dn3" primaryKey="id" rule="student_id" />		<table name="t_dictionaries" dataNode="dn1,dn2,dn3" type="global" />		<table name="t_teacher" dataNode="dn1" />    </schema>				<!-- name:节点名称,用于在table标签里面调用		dataHost:dataHost标签name值(<dataHost name="dtHost1">)		database:物理数据库名,需要提前创建好实际存在的-->		<dataNode name="dn1" dataHost="dtHost1" database="db1" />		<dataNode name="dn2" dataHost="dtHost1" database="db2" />		<dataNode name="dn3" dataHost="dtHost2" database="db3" />			<!--	name:节点名称,在上方dataNode标签中调用	maxCon:底层数据库的链接最大数	minCon:底层数据库的链接最小数	balance:值可以为0,1,2,3,分别表示对当前datahost中维护的数据库们的读操作逻辑	0:不开启读写分离,所有的读写操作都在最小的索引号的writeHost(第一个writeHost标签)	1:全部的readHost和备用writeHost都参与读数据的平衡,如果读的请求过多,负责写的第一个writeHost也分担一部分	2 :所有的读操作,都随机的在所有的writeHost和readHost中进行	3 :所有的读操作,都到writeHost对应的readHost上进行(备用writeHost不参加了),在集群中没有配置ReadHost的情况下,读都到第	一个writeHost完成	writeType:控制当前datahost维护的数据库集群的写操作	0:所有的写操作都在第一个writeHost标签的数据库进行	1:所有的写操作,都随机分配到所有的writeHost(mycat1.5完全不建议配置了)	dbtype:数据库类型(不同数据库配置不同名称,mysql)	dbDriver:数据库驱动,native,动态获取	switchType:切换的逻辑	-1:故障不切换	1:故障切换,当前写操作的writeHost故障,进行切换,切换到下一个writeHost;	slaveThreshold:标签中的<heartbeat>用来检测后端数据库的心跳sql语句;本属性检查从节点与主节点的同步情况(延迟时间数),配合心	跳语句show slave status; 读写分离时,所有的readHost的数据都可靠	-->	<dataHost name="dtHost1" maxCon="1000" minCon="10" balance="1"			  writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">		<!--用于验证心跳,这个是mysql主库的配置-->		<heartbeat>select user()</heartbeat>				<writeHost host="127.0.0.1" url="192.168.199.11:3306" user="root" password="123456">			<readHost host="127.0.0.1" url="192.168.199.12:3306" user="root" password="123456" />		</writeHost>		</dataHost>	<dataHost name="dtHost2" maxCon="1000" minCon="10" balance="1"			  writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">		<!--用于验证心跳,这个是mysql主库的配置-->		<heartbeat>select user()</heartbeat>				<writeHost host="127.0.0.1" url="192.168.199.13:3306" user="root" password="123456">			<readHost host="127.0.0.1" url="192.168.199.13:3306" user="root" password="123456" />		</writeHost>		</dataHost></mycat:schema>

​ 配置规则 conf-rule.xml

<tableRule name="sharding-by-intfile">    <rule>        <columns>city</columns>        <algorithm>hash-int</algorithm>    </rule></tableRule>

rule.xml 详解

<?xml version="1.0" encoding="UTF-8"?><!-- - - Licensed under the Apache License, Version 2.0 (the "License"); 	- you may not use this file except in compliance with the License. - You 	may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 	- - Unless required by applicable law or agreed to in writing, software - 	distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT 	WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the 	License for the specific language governing permissions and - limitations 	under the License. --><!DOCTYPE mycat:rule SYSTEM "rule.dtd"><mycat:rule xmlns:mycat="http://io.mycat/"><!--schema.xml中配置的rule="student_id" 所对应的规则,规则对应主键,列名为id需要与数据库的列名对应algorithm对应下方function-->	<tableRule name="student_id">        <rule>            <columns>id</columns>            <algorithm>student_text</algorithm>        </rule>    </tableRule>			<tableRule name="rule1">		<rule>			<columns>id</columns>			<algorithm>func1</algorithm>		</rule>	</tableRule>	<tableRule name="rule2">		<rule>			<columns>user_id</columns>			<algorithm>func1</algorithm>		</rule>	</tableRule>	<tableRule name="sharding-by-intfile">		<rule>			<columns>sharding_id</columns>			<algorithm>hash-int</algorithm>		</rule>	</tableRule>	<tableRule name="auto-sharding-long">		<rule>			<columns>id</columns>			<algorithm>rang-long</algorithm>		</rule>	</tableRule>	<tableRule name="mod-long">		<rule>			<columns>id</columns>			<algorithm>mod-long</algorithm>		</rule>	</tableRule>	<tableRule name="sharding-by-murmur">		<rule>			<columns>id</columns>			<algorithm>murmur</algorithm>		</rule>	</tableRule>	<tableRule name="crc32slot">		<rule>			<columns>id</columns>			<algorithm>crc32slot</algorithm>		</rule>	</tableRule>	<tableRule name="sharding-by-month">		<rule>			<columns>create_time</columns>			<algorithm>partbymonth</algorithm>		</rule>	</tableRule>	<tableRule name="latest-month-calldate">		<rule>			<columns>calldate</columns>			<algorithm>latestMonth</algorithm>		</rule>	</tableRule>		<tableRule name="auto-sharding-rang-mod">		<rule>			<columns>id</columns>			<algorithm>rang-mod</algorithm>		</rule>	</tableRule>		<tableRule name="jch">		<rule>			<columns>id</columns>			<algorithm>jump-consistent-hash</algorithm>		</rule>	</tableRule><!--在conf中需要添加student_text.txt规则文件--> <function name="student_text"        class="io.mycat.route.function.AutoPartitionByLong">        <property name="mapFile">student_text.txt</property>    </function>	<function name="murmur"		class="io.mycat.route.function.PartitionByMurmurHash">		<property name="seed">0</property><!-- 默认是0 -->		<property name="count">2</property><!-- 要分片的数据库节点数量,必须指定,否则没法分片 -->		<property name="virtualBucketTimes">160</property><!-- 一个实际的数据库节点被映射为这么多虚拟节点,默认是160倍,也就是虚拟节点数是物理节点数的160倍 -->		<!-- <property name="weightMapFile">weightMapFile</property> 节点的权重,没有指定权重的节点默认是1。以properties文件的格式填写,以从0开始到count-1的整数值也就是节点索引为key,以节点权重值为值。所有权重值必须是正整数,否则以1代替 -->		<!-- <property name="bucketMapPath">/etc/mycat/bucketMapPath</property> 			用于测试时观察各物理节点与虚拟节点的分布情况,如果指定了这个属性,会把虚拟节点的murmur hash值与物理节点的映射按行输出到这个文件,没有默认值,如果不指定,就不会输出任何东西 -->	</function>	<function name="crc32slot"			  class="io.mycat.route.function.PartitionByCRC32PreSlot">	</function>	<function name="hash-int"		class="io.mycat.route.function.PartitionByFileMap">		<property name="mapFile">partition-hash-int.txt</property>	</function>	<function name="rang-long"		class="io.mycat.route.function.AutoPartitionByLong">		<property name="mapFile">autopartition-long.txt</property>	</function>	<function name="mod-long" class="io.mycat.route.function.PartitionByMod">		<!-- how many data nodes -->		<property name="count">3</property>	</function>	<function name="func1" class="io.mycat.route.function.PartitionByLong">		<property name="partitionCount">8</property>		<property name="partitionLength">128</property>	</function>	<function name="latestMonth"		class="io.mycat.route.function.LatestMonthPartion">		<property name="splitOneDay">24</property>	</function>	<function name="partbymonth"		class="io.mycat.route.function.PartitionByMonth">		<property name="dateFormat">yyyy-MM-dd</property>		<property name="sBeginDate">2015-01-01</property>	</function>		<function name="rang-mod" class="io.mycat.route.function.PartitionByRangeMod">        	<property name="mapFile">partition-range-mod.txt</property>	</function>		<function name="jump-consistent-hash" class="io.mycat.route.function.PartitionByJumpConsistentHash">		<property name="totalBuckets">3</property>	</function></mycat:rule>

分库分表

​ 解决大数据量

​ 拆分概念

​ 垂直拆分:按照业务、模块进行拆分

​ 水平拆分:一个表数据量过大,可以把该表按照某种规则划分

​ 如何拆分:如果表多且各项业务逻辑清晰,低耦合,首先垂直切分;表不多,数据大、数据热点高选择水平切分

​ 事务问题:

​ 执行分库分表后,数据库分布不同库,数据库管理出现困难

​ 跨表跨库Join问题:

​ 表的关联操作受到限制

​ 额外的数据管理负担和数据运算压力:

​ 数据的定位问题和数据的增删查改的重复问题,通过应用程序可以解决但会造成额外的逻辑运算

spring-cloud

微服务:由一系列微小服务组成,每个服务器独立运行在自己的进程中,独立部署,基于分布式管理

springBoot:是一个含概多个子项目的开发工具

  建立父子工程,父工程添加添加配置文件

<!--    降 springboot 版本--><parent>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-parent</artifactId>    <version>2.2.0.RELEASE</version>    <relativePath/> <!-- lookup parent from repository --></parent><properties>    <java.version>1.8</java.version></properties><dependencyManagement>    <dependencies>        <dependency>            <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-dependencies</artifactId>            <version>Hoxton.RELEASE</version>            <type>pom</type>            <scope>import</scope>        </dependency>    </dependencies></dependencyManagement><repositories>    <repository>        <id>spring-milestones</id>        <name>Spring Milestones</name>        <url>https://repo.spring.io/milestone</url>    </repository></repositories>

体系组件

体系组件名称 作用定位
Eureka 注册中心,提供服务注册与发现服务
Feign 请求客户端,简化微服务之间的请求调用
Hystrix 容错处理
Ribbon 为微服务提供负载均衡
Zuul 网关,提供微服务转发与校验服务
Config 分布式配置,提供统一配置服务
Sleuth 服务追踪,提供微服务链路追踪服务

Eureka

  项目运行时注册中心每次都要先启动

负载均衡、链路跟踪

elk环境

E

 是一个高度可扩展的开源全文搜索和分析引擎。能够快速,实时的存储,搜索和分析大量数据,通常用做底层引擎技术。

使用:

 bin路径启动 elasticsearch

 浏览器测试,127.0.0.1:9200

 配置 ik 分词器到 plugins 目录下

  config 目录下 elasticsearch.yml 文件可以修改端口信息等

简介

 一个节点就是一个es服务器

 节点分角色

  主节点:创建索引,删除索引

  数据节点:数据的存储、插入、修改、删除

 一个节点能否有两个角色

  可以,主节点负责维护集群,数据节点主要是对数据进行维护

 分片

  类似与MySQL的分库分表,不过MySQL借助了第三方组件,ES本身实现了该功能

  在创建索引是需要提前指定分片的数量

  ES 默认有五个主分片,每个主分片都有一个副本

  对文档的新建、索引、删除请求都是写操作。必须在主分区上面完成之后才能被复制到相关的副本分片

 健康度

L

在bin目录下建立一个文件 conf.conf 输入

input { stdin{} }output { stdout{} }

使用命令logstash -f conf.conf 启动

在bin目录下新建文件 elk.conf;在bin下指定路径运行 elk.conf

input { stdin{ } }output {	elasticsearch {		host => {"127.0.0.1:9200"}		index => "es-message=%{+YYYY.MM.dd}"  #对日志进行索引归档	}	stdout { codec => rubydebug}}

启动输入 test 信息,成功执行下一步

打开浏览器,输入 http://127.0.0.1:9200/es-message-2021.06.11/_search?pretty 需要修改到对应的日期

K

启动命令:bin路径下cmd执行 kibana

其他

Windows 结束某个端口进程

image-20210909084219782

Markdown 语法

&emsp; 或者 &#8195;	首行缩进,两个空格&ensp; 或者 &#8194;	首行缩进,一个空格<p style="text-indent:2em">[content]</p>  首行缩进==[content]==		高亮 [content] 
posted @ 2021-10-14 19:51  YuanLiu  阅读(136)  评论(0)    收藏  举报