第一章 EMMET语法
1. syntax 快捷键介绍
- Child : >
-
相当于创建子标签
-
nav>ul>li
<nav> <ul> <li></li> </ul> </nav>
- Subling : +
-
相当于创建平级标签
-
div+p+bq
<div></div> <p></p> <blockquote></blockquote>
- Climb-up : ^
-
相当于创建了与父标签同级的标签
-
div+div>p>span+em^bq
<div></div> <div> <p><sapn><em></em></sapn></p> <blockquote></blockquote> </div>
-
div+div>p>span+em^^bq
<div></div> <div> <p><sapn><em></em></sapn></p> </div> <blockquote></blockquote>
- Group: ()
-
div>(header>ul>li*2>a)+footer>p
<div> <header> <ul> <li></li> <li></li> </ul> </header> <footer> <p></p> </footer> </div>
-
(div>dl>(dt+dd))+footer>p
<div> <dl> <dt></dt> <dd></dd> </dl> </div> <footer> <p></p> </footer>
- Item number: $
-
*ul>li.item$2
<ul> <li class="item1"></li> <li class="item2"></li> </ul>
<!--h$[title=item$]{Header $}*3--> <h1 title="item1">Header 1</h1> <h2 title="item2">Header 2</h2> <h3 title="item3">Header 3</h3>
-
*ul>li.item$$$2
<ul> <li class="item001"></li> <li class="item002"></li> </ul>
-
*ul>li.item$@-2
<!--倒序的--> <ul> <li class="item2"></li> <li class="item1"></li> </ul>
-
ul>li.item$@3*2
<!--从三开始数两个数--> <ul> <li class="item3"></li> <li class="item4"></li> </ul>
- ID and Class attributes
-
#header
<div id=“#header”></div>
-
.title
<div class="title"></div>
-
form#search.wide
<form id="#search" class="wide"></form>
-
p.class1.class2.class3
<p class="class1 class2 class3"></p>
- Custom attributes
-
p[title=“Hi”]
<p title="Hi"></p>
-
td[rowspan=2 colspan=3 title]
<td rowspan="2" colspan="3" title=""></td>
-
[a=‘value1’ b=“value2”]
<div a="value1" b="value2"></div>
- Text
-
a{Click me}
<a href=""> Cilck me</a>
-
p>{Click }+a{here}+{to continue}
<p> Click <a href="">here</a> to continue </p>
第二章 HTML简介
浏览器内核
浏览器 | 内核 | CSS前缀 |
---|---|---|
IE / Edge | Trident | -ms- |
FireFox | Gecko | -moz- |
safar | Webkit | -webkit- |
Chrome | Chromium/Blink | |
Opera(最新版的Opera使用Blink内核) | Presto | -o- |
1. 注释
<!--
此为html的注释书写方式
html文件以.html和.htm为后缀,全文不区分大小写
具有较高的容错性,浏览器具有一个标签解析器和脚本解析器,从上往下解析
用户书写的html页面,如果出现严重的错误,则无法显示相应的结构和样式
在html页面上主要存在以下三种
结构(html) 页面上有什么 一个人身高 体型 素颜
样式(css html*) 页面有的这个东西是什么样子的 打扮化妆以后的样子
动作(js) 页面上能够具有什么操作 这个人的谈吐举止
在1996年css出现之前html也能够渲染页面样式,之后样式被css取代
以下第一句为DTD信息,规定了整个页面使用的是第几版的html规范,可以使用
哪些标签,哪些标签不能使用,此处使用的是html5,从第五版开始,dtd不再
发生改变,如果不书写DTD,则默认使用4.01版本
-->
2.根标签
- 的根标签,在html和xml中都有且仅有一个根标签,嵌套在最外层
- lang 属性可选,用来描述当前页面是何种语言的网页
- en 为英文网页,此处比对我们的操作系统的地区,如果不一致,则google翻译提示是否进行翻译
3. 一级子元素
1. head
head:一级子元素,也可以称之为html标签的子元素,用来设置
页面的头信息,例如标签,编码,引入的js css等
注意书写规范为,子标签必须在父标签向右一个制表符
-
meta
meta:此标签可以设置页面的编码,关键字,描述等;此处为设置编码,如果不指明编码则可能会导致页面的乱码
编码集 用途 iso-8859-1 此编码为最早的编码,不支持中文,部分开发工具和浏览器默认使用此编码 gb2312 国标码测试版,在iso-8859-1的基础上修改而来,支持中文但是存在2000多个无法识别的中文生僻字 gbk 国标码,完全支持所有中文,国内大部分中文网站采用windows系统默认使用此编码 utf-8 互联网安全编码,支持中文,国外大部分网站多使用此编码很多开发工具默认使用此编码,OS系统默认为此编码 -
title
用来设置页面的标题,支持中文
2.footer
h5新增标签,表示页脚,用来放置作者信息 网站版权 法律合作信息等
- address
用来存地址等信息
4.其他元素
- header
h5新增标签,表示页眉,一般放置页面的标题,搜索栏导航栏等元素
- section
h5新增标签,表示整个页面中主体部分,放置完整的文章等
- form
表单元素,此元素可以通过嵌套在内部的各种表单项元素以键值对的形式收集用户填写的信息,例如用户名 密码 等等;当表单提交时,最终将信息提交到action设置的目的地
action:属性表示表单提交到的目的地
method:提交表单的方式,存在get和post两种方式
- label
用来设置表单项外的文本,for属性对应表单项中的id属性
- input
属性 | 含义 |
---|---|
type=“text” | 单行输入框 |
type=“password” | 密码输入框 |
type=“radio” | 单选框 |
type=“checkbox” | 多选框 |
type=“submit” | 提交按钮 |
type=“reset” | 重置 |
name | 表示键值对的键,可以随意书写不能不写 |
id | 对应label中的for属性,使之连为一体 |
required | 表示必填项 |
minlength/maxlength | 最小长度/最大长度 |
placeholder | 提示文本,用户输入时消失 |
value | 此属性一般不书写,就表示键值对的值,用户在此单行文本输入框中输入的值就是value值 |
autocomplete | 设置为off,则关闭自动完成功能 |
5. HTML部分标签列表
- 因为有Typroa支持部分的HTML标签,因此部分显示不出来的标签会在前边加上空格,让大纲显示该标签
- 以下仅仅是个人学习是学到的标签及标签内的部分属性,全部标签请移步W3C https://www.w3school.com.cn/tags/tag_doctype.asp
- HTML注释
<!--这是一段注释,注释不会再浏览器中显示-->
<!DOCTYPE>
- DTD信息
规定了整个页面使用的是第几版的html规范,可以使用哪些标签,哪些标签不能使用,此处使用的是html5,从第五版开始,DTD不再发生改变,如果不书写DTD,则默认使用4.01版本
< a>
- 超链接
属性 | 值 | 描述 |
---|---|---|
href | URL | 必填属性表示链接提交到的目的地 后面可以书写相对路径或者绝对路径 |
target | _self _blank | 默认为_self ,目标页面取代当前页面 _blank为目标页面与当前页面同时存在 |
< address>
- 用来书写地址等信息
< body>
- 页面的正文,所有的结构样式都写在body元素中
< br>换行
< center>
- 包含的内容强制居中,建议使用CSS代替
< div>
- div : 此元素相当于一个容器,div元素结束后自动换行
- 最外层的 div 一般设置一个id=“container”,表示容器
< footer>
- html5新增标签,表示页脚,通常用来放置作者信息,网站版权,法律合作信息等
< form>
-
表单元素,通过巧挑在内部的各种表单元素以键值对的形式手机用户填写的信息,例如用户名,密码等
属性 值 描述 action URL 表示表单提交到的目的地 GET
POST声明表单提交的方式,有get和post两种方式 表单提交时 GET 和 POST两种提交方式的不同
GET : get 提交表单的速度较快。安全性低,通过浏览器的地址栏进行传输
格式:目的地 ?key=value&key2=value2&keyN=valueN
最多传递256个字符,不支持中文仅仅支持字符串
如果使用链接提交则肯定为GET
POST : post 提交速度慢,安全性高,不通过浏览器的地址栏传递,无法从地址栏发现用户书写的内容,通过消息体传递值,格式与get一致,同样不支持中文,但是没有大小限制,如果进行上传操作必须使用post,链接提交无法使用post,必须用get
< h1> - < h6>
- 标题,h1字体最大,h6最小,和typora的大纲标题一个样
< head>
- 用来设置页面的头部信息,例如标签,编码,引入等
- 可以包含的标签有 ,,
< header>
- html5新增的标签表示页眉,一般放置页面的标题,搜索框导航栏等
< hr>
- 分割线,一般是一条线可以设置宽度的颜色
- width有设置像素值和百分比两种形式,建议用css代替
< html>
- html的根标签,在html和xml中都有且只有一个根标签,嵌套在最外层
属性 | 值 | 描述 |
---|---|---|
lang | 可选,用来描述当前是哪种语言的页面,en为英文网页,会对比操作系统的地区,如果不一致个谷歌翻译提示是否进行翻译,中文为 zh-CN |
< label>
- 用来设置表单项外的文本,for属性对象表单项中的id属性
< input>
属性 | 值 | 描述 |
---|---|---|
autocomplete | off | 关闭自动完成 |
autofoucs | 自动获得焦点,兼容性较差,有时需要手动实现 | |
id | 对应label的for属性 | |
minlegth/maxlength | 填写内容的最小长度和最大长度 | |
name | 表示键值对的键,遵循命名规范 | |
required | 表示该项不能不填,必填项 | |
value | 一般不填写,用户填写的内容就是value的值 | |
表示该行是单行文本输入框 | ||
单行密码输入框 | ||
单选框checked表示默认选中,单选框的name属性必须一样 | ||
复选框 | ||
上传文件,必须将method设置为post的表单项 | ||
隐藏域,用户无法从页面查看的文本域,一般是开发者放在表单中的元素,当表单提交是,一键值对的形式在用户不知情的情况下提交到服务器端,多使用在分页,修改场合 | ||
必须填写合法的邮箱名,否则无法验证通过 | ||
tel | 必须填写合法的电话,pattern属性就对应正则表达式 | |
date | 输入合法的日期按照yyyy-MM-dd格式输出 | |
number | 输入数字 | |
submit | 提交按钮,不写name只写value值表示按钮上的文字,点击此按钮之后,如果没有违反验证规则,则表单提交到action设置的目的地 | |
reset | 点击此按钮后,所有填写的数据全部清空 |
< meter>
- 进度条兼容性较差
< option>
- 下拉框的选项,selected选项代表默认选中
- 需要个标签配合使用
< select>
- 下拉框,需要个标签配合使用
<label for="locationid">归属地</label>
<select name="location" id="locationid">
<option value="huibei">湖北</option>
<option value="sichuan">四川</option>
</select>
< textarea>
- 多行文本输入框
属性 | 值 | 描述 |
---|---|---|
cols | 表示一行可以输入多少个字符 | |
表示最多可以输入多少行 |
6.特殊字符
特殊符号 | 转义字符[点击查看] |
---|---|
> | > |
< | < |
& | & |
" | " |
‘ | ' |
xml中无法使用 |
第三章 XML
1. XML规范
- xml 的 API 开发文档与HTML 文档一致
- 头信息之前不能书写任何字符
- 全文小写,无容错性
- 在HTML中,首行为一个DTD信息,这个DTD规范了全文为那种版本的html,可以使用那些标签,出现多少次,不能使使用哪些标签,如果不写则默认4.01DTD
- xml默认不存在DTD,如果用户不写DTD,则全文无DTD,标签可以随意书写,支持中文
2. XML标签
1. CDATA[]
<![CDATA[ 在嵌套在此格式之后,用户书写的空格,换行,特殊字符 都可以被正确的识别,不需要转义字符进行转义 CDATA一般嵌套SQL语句,当封装SQL语句时, 如果空格换行特殊字符不能正确识别,则可能会出现错误 ]]>
<我是根元素>
<student id="et001" name="elena">
<hobby>购物</hobby>
<hobby>跑步</hobby>
<location>济南</location>
<info>
来自济南的
elena
非常喜欢
购物和跑步
><'"&
</info>
</student>
<student id="et002" name="penny">
<hobby>睡觉</hobby>
<hobby>吃饭</hobby>
<location>青岛</location>
<info>
<![CDATA[
来自青岛的
penny
非常喜欢
吃饭
><'"&
> < ' " &
]]>
</info>
</student>
</我是根元素>
2. 内部DTD
2.1 元素<!ELEMENT>
- 将DTD文件和xml文件书写在一些,用来约束全文可以使用哪些标签,不能使用那些标签
]>
*:表示此元素可以出现任意多个,也可以不出现
如果元素后面没有书写任何标识符,则表示元素只能书写一次,也不能不出现
元素,元素,元素:注意这里严格区分先后顺序,必须按照逗号的分割排列元素
(元素1|元素2|元素N):枚举写法,表示其中任选其一
?:可以出现0个或者1个
+:至少一个上不封顶
(#PCDATA):表示元素中可以书写任意字符串,不再嵌套子元素
ANY:元素中可以是任意字符串,也可以再有子元素
(#PCDATA|元素名)*:表示元素中可以是任意字符串,也可以再次嵌套
特定元素,也可以两者同时存在
EMPTY:表示空元素,元素中不能存在子元素,也没有字符串
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE 学生信息[
<!ELEMENT 学生信息 (student*)>
<!ELEMENT student (name,age,(location|home),hobby*,gf?,teacher+,job*,
criminal)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
<!ELEMENT location (#PCDATA)>
<!ELEMENT home (#PCDATA)>
<!ELEMENT hobby ANY>
<!ELEMENT gf (#PCDATA)>
<!ELEMENT teacher (#PCDATA)>
<!ELEMENT job (#PCDATA|secondJob)*>
<!ELEMENT secondJob (#PCDATA)>
<!ELEMENT criminal EMPTY>
]>
<学生信息>
<student>
<name>elena</name>
<age>23</age>
<location>济南</location>
<hobby>游戏</hobby>
<hobby>购物</hobby>
<gf>damon</gf>
<teacher>aleric</teacher>
<teacher>tom</teacher>
<job>
软件工程师
<secondJob>特工</secondJob>
</job>
<criminal />
</student>
</学生信息>
2.1 属性<!ATTLIST>
使用内部DTD来约束元素的属性
<!ATTLIST 依附元素名 属性名 属性值类型 默认值类型>ID:表示这个属性全文唯一,不能重复,不能以数字开头
CDATA:表示属性值可以是任意字符串
(数据1|数据2|数据N):表示默认值任选其一
#REQUIRED:表示默认值必须书写不能不写
#IMPLIED:表示默认值爱写不写
#FIXED:属性值固定,为后面的默认值,不得更改,存在浏览器差异性
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE 学生信息 [
<!ELEMENT 学生信息 (student*)>
<!ELEMENT student (name,age)>
<!ATTLIST student sid ID #REQUIRED>
<!ATTLIST student home CDATA #IMPLIED>
<!ATTLIST student salary (10k|12k|14k) "12k">
<!ATTLIST student job CDATA #FIXED "软件工程师">
<!ELEMENT name (#PCDATA)>
<!ELEMENT age (#PCDATA)>
]>
<学生信息>
<student sid="et001" home="济南" salary="12k"
job="软件工程师">
<name>elena</name>
<age>23</age>
</student>
</学生信息>
3. 外部DTD
- 拓展名为.dtd,是一个独立的文件,不需要书写<!DOCTYPE>标签
<?xml version="1.0" encoding="utf-8" ?>
<!ELEMENT student (name,age)>
<!ELEMENT name (#PCDATA)>
<!ATTLIST name id ID #REQUIRED>
<!ELEMENT age (#PCDATA)>
- 引入DTD存在以下两种方式
- 本地方式:
- 网络方式:
<?xml version="1.0" encoding="UTF-8" ?>
<!--本地方式导入DTD文件-->
<!DOCTYPE student SYSTEM "outterDTD4.dtd">
<student>
<name id="et001">penny</name>
<age>23</age>
</student>
第四章 CSS
1. CSS简介
- CSS(Cascading style Sheet) 层叠样式表
- CSS注释与java多行注释一样都为 /**/
1. 三种引入css的方式
- 内嵌式
直接将css代码写在head标签内,是一种优缺点都不是很明显的书写方式,在 style 标签内只能书写 css 代码,不能写html标签
<style>
*{margin:0;}
</style>
- 外链式
通过 link 标签引入一个独立的css文件到本页面,rel表示引入的是一个样式表,href表示引入的css文件路径,这种方式被普遍使用
<link rel="stylesheet" href="./css/style.css">
- 行内式
在标签内直接书写css,这种方式严重违背了解耦结构和样式的原则; 将结构和样式再次耦合在一起,但是由于其优先级极高,所以使用较多
<div style="background-color:red"></div>
- 三种引入方式的优先级
- 行内式 > 外链式和内嵌式谁放在后边,就会覆盖之前出现冲突的样式
2. CSS继承性和层叠性
- 继承性
在 CSS中,没有任何冲突的前提下,字标签会完全继承父标签的所有css渲染设置,如果存在冲突则以字标签为准。继承特性
- 层叠性
当多种基本选择器出现冲突时,浏览器渲染的优先级情况:
id选择器>类别选择器>标记选择器
注意以上优先级与顺序无关,如果存在行内式,则一切以行内式为准
2. CSS选择器
- css选择器写的越详细越好
当多种基本选择器出现冲突时,浏览器渲染的优先级情况
id选择器>类别选择器>标记选择器
注意以上优先级与顺序无关。如果存在行内式,则一切以行内式为准
- 基本选择器
/*标记选择器*/
tagName{
使用标签名作为选择元素的依据,一般极少单独使用,非常容易引起误操作
}
p{color:red;}
- 类选择器
/*类选择器*/
.className{
根据.class属性值精确选取元素,class全文不唯一,可以随意使用
}
.test{color:red;}
- id选择器
/*id选择器*/
#idName{
使用#id值来拿去元素,id全文唯一不可以出现重复
}
- 交集选择器
/*由一个标签后边紧跟类别或者#id,必须同时满足两个条件才可以成功选取*/
tagName.className{}
tagName#idName{}
- 并集选择器
/*有多个基本或者符合选择器用都好隔开,只要返祖其中任意一个就可以成功选取*/
sel1,sel2,sel3,selN{}
-
后代选择器
在 CSS中,没有任何冲突的前提下,字标签会完全继承父标签的所有css渲染设置,如果存在冲突则以字标签为准。继承特性
sel1 sel2 sel3 sel4{
根据左祖先右后代的继承原则,可以精确的拿取具有层级关系的子元素,没有个数限制
}
- 全选选择器
*{
拿取页面的全部元素
}
- 其他选择器
选择器 | 含义 |
---|---|
p[title = “val”] | 匹配 属性有title且值为val 的p元素 |
p[titlt ^= “v”] | 匹配 属性有title且值以v开头的p元素 |
p[titlt $= “v”] | 匹配 属性有title且值以v结尾 的p元素 |
p[title *=“v”] | 匹配 属性有title且值包含v 的p元素 |
3. CSS常见元素类型
- 块元素(block)
eg: div p h1-h6 ul li img*
这些元素结束之后自带换行,易航智能存在一个元素 ,无法横向排列,设置这写盒子的盒子模型有效,文本对齐方式无效
- 内联元素(inline)
eg:a label sapn img*
这些元素结束之后没有换行,一行可以存在多个,从左往右排列,设置这些元素的盒子模型很多参数无效,设置文本对齐方式无效
img (inline-block内联块元素)
是一种内联元素(不换行),但是具备所有块元素的特性,可以设置盒子模型等*
- 空元素(empty)
eg: br hr meta
这种元素一般用来设置参数或者特定的结构或者样式
通过设置display属性可以用来修改元素的类型
属性 | 元素类型 |
---|---|
display:block | 设置元素为块元素 |
display:inline | 设置元素为内联元素 |
display:flex | 设置元素为flex布局 |
display:none | 设置元素为隐藏 |
4. 盒子模型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bhe7b6Lm-1595911803055)(D:\Study\Yitu\前端\盒子模型.png)]
1.标准文本流
在css中所有的元素在没有设置任何定位 浮动等参数的前提下,从上到下一次排列的顺序称之为标准文本流,由于大部分的元素都是块元素,所以基本上所有的元素易航智能存在一个从上往下排列
**在制作页面的时候,一般先写好html呈现标准文本流的状态,然后统一书写css,而不是html和css同时书写 **
2.盒子模型
在css中将所有的元素看作是一个具有四个边框的盒子,这些盒子按照标准文本流的形态从上往下排列,盒子的四个边框分别为 外边距margin 内边距padding 边框border,通过设置border属性可以显示元素的边框
-
border
border:边框类型 边框粗细 边框颜色
solid单实线 double双实线 dotted点状线
-
margin
margin有四个属性margin-top,margin-right,margin-bottom,margin-left
/*margin的简略写法*/ /* --四个参数-- margin: 上 右 下 左; --三个参数-- margin: 上 (右下) 左; --两个参数-- margin: (上下) (右左); --一个参数-- margin: (上右下左); */ /*最简单的网页居中方法*/ margin:0 auto;
-
padding
padding同样遵循上述的简略写法;
不同的浏览器都设置了元素边框之间存在默认的缝隙
为了统一距离一般都要设置 *{margin:0;padding:0;}
5. 背景图和浮动
1. 背景图
/*设置背景图*/
backgroud-image:url("");
/*只允许从左往右排列一次*/
background-repeat:repeat-x;
/*设置背景图位置*/
background-position:bottom;
/*去除立标的标记*/
ul{
list-style-type:none;
}
2. 浮动
-
块元素在不设置情况下会自动占满父元素的宽度
-
默认块元素会占满父元素的宽度,设置浮动之后按照设置的浮动方向进行缩小,缩小到内部的内容宽度为准,之后漂浮在页面上,原来的位置被后边的元素占据
/*左右浮动*/ float:left; /*设置元素不受到其他元素浮动的影响*/ clear:left;/*不受左浮动影响*/ clear:right;/*不受右浮动影响*/ clear:both;/*不受两侧浮动影响*/
6. 定位
1. 相对定位
元素根据原先所在位置的左上角进行定位,定位之后元素依然保持原来的类型,原来的位置也依旧被占用。偏移量 top 和 bottom 只能写一个。left 和 right 也只能写一个
/*设置为相对定位*/
position:relative;
/*向下偏移量300像素 距离顶部300px*/
top:300px;
/*向右偏移量300像素 距离左端300px*/
left:300px;
2.绝对定位
元素根据其距离最近的定位过的父元素的左上角进行定位,如果元素的祖先元素都没有被定位过,则根据body也就是浏览器的左上角进行定位(这样可能会出现问题)
元素定位之后,不再保留原先的类型,不在沾满一行,漂浮在页面上,原有的位置被之后的元素占据,每对偏移量同样只能书写一个
position:absolute;
top:300px;
left:300px;
7.布局
1. 圣杯布局
-
在html4.01等之前的版本中html并没有专门的布局元素而是统一使用div进行布局
-
html5出现了布局元素,通常是采用圣杯布局包含 header(页眉),nav(导航栏),section(网页的主体),aside(侧边栏),footer(页脚),article(放置完整的文章)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YN0Co1Tp-1595911803059)(D:\Study\Yitu\前端\css\CssDayOfficial\attachment\html5布局.jpg)]
常用属性
属性 含义 其他 font-family 字体种类 多种字体用 , 分开 color:red 字体颜色 可以用颜色名或者rgb font-size 字体大小 可以用px也可以用em font-weight:bolder 字体加粗 text-decoration:none 去掉下划线 underline下划线
overline上划线
line-through贯穿线
blink闪烁line-height20px 行高 a:hover 鼠标悬停的样式 visited被点击后的样式 display:block 设置为块元素 inline内联元素
flex设置为Flex布局border:solid 2px red; 设置边框为2px红色实线 double双实线
dotted虚线box-shadow:15px 15 px 15px gray 设置阴影
2. FLex布局
采用lex布局的元素,被称为Flex容器(Flex container),简称容器,子元素自动成为容器成员,成为Flex项目(Flex item),简称项目
容器默认存在两根主轴,水平方向主轴(main axis)垂直方向交叉轴(cross axis)默认项目按照主轴排列
- main start/main end : 主轴开始、结束位置
- cross start/cross start : 交叉轴开始、结束位置
- main size/cross size :单个项目占据主轴、交叉轴的空间
属性 | 描述 |
---|---|
flex-direction | 决定排列方向 |
flex-wrap | 一行排不下时如何换行 |
flex-flow | 前两种的简写格式 |
justify-content | 在x轴对齐方式 |
align-items | 在y轴如何对齐 |
align-content |
-
flex-direction
决定项目的排列方向
属性 含义 row[默认] 主轴水平方向,起点在左端 row-reverse 主轴水平方向,起点在右端 column 主轴垂直方向,起点在上边沿 column-reverse 主轴垂直方向,起点在下边沿 -
flex-wrap
定义换行情况
属性 含义 nowrap[默认] 不换行 wrap 换行,第一行在上 wrap-reverse 换行,第一行在下 -
flex-flow
flex-direction 和 flex-wrap的简写
属性 含义 flex-flow:row nowrap 第一个参数为flex-direction第二个参数为flex-wrap -
justify-content
定义项目与在主轴上的对齐方式
属性 含义 flex-start 左对齐 flex-end 右对齐 center 居中对齐 space-between 项目两端对齐边缘,项目之间空隙相同 space-around 项目之间的间隙相同,为两端到边缘的两倍 -
align-items
定义在垂直方向上的对齐方式
属性 含义 flex-start 对齐上边沿 flex-end 对齐下边沿 center 居中对齐 baseline 项目的第一行文字基线对齐 stretch[默认] 如果未设置高度或高度为auto,将沾满整个容器的高度 -
align-content
定义多条轴线的对齐方式,如果项目只有一根轴线,该属性不起作用,所以必须设置flex-wrap:wrap;
属性 含义 flex-start 与交叉轴起点对齐 flex-end 与交叉轴终点对齐 center 居中对齐 stretch[默认值] 占满整个交叉轴 space-around 项目上下之间距离相等,上边沿与下边沿有空隙,大小为项目间的一半 space-between 上边沿与下边沿无空隙,项目上下间距离相等 如果不给项目设置高度,但是给容器设置align-content不为stretch时,同一轴线上的项目高度都会变为该轴线上高度最高的项目的高度。
项目属性
-
order
定义项目排列顺序
item{ order:integer; } /*数值越小,排列的越靠前,默认为零,可以使付负数*/
-
flex-grow
定义项目的放大比例,默认为0,如果空间有剩余,项目也不放大,也可以是小数,按照比例占据剩余空间,数值相同则等分空间,设置为2,则占据值为1的项目空间的2倍
item{ flex-grow:<数字>; }
-
flex-shrink
定义项目缩小比例默认值为1,如果看空间不足则等比例缩小。设置为0,则不进行缩小。如果设置了一个值为0,其他项目都为1,则当空间不足时,该项目不缩小
如果都设置为0,则当空间不足,都不缩小,项目撑破容器溢出
设置为不为0的非负数,则效果等同设置为1
item{ flex-shrink:<非负整数>; }
-
flex-basis
定义在分配多余空间之前,项目占据的主轴空间,如果空间充足,则设置了数值的项目占据设定的空间;如果空间不足,则该项目也会缩小,小于设定值
-
flex
flex是flex-grow,flex-shrink,flex-basis的简写
属性 含义 flex: 0 1 auto 项目不放大,等比例缩小。第一个必填,其他选填 flex:auto; 相当于 flex: 1 1 auto; flex:none; 相当于 flex: 0 0 auto; -
align-self
默认为auto,表示继承父元素的align-items属性,可以单独设置align-items属性
第五章 JavaScript
1. JS简介
js => JavaScript页面脚本语言,前段三大基础之一,用来负责页面的动作
相关类库 prototype YUI Dojo Extjs ajax jQuery easyUI等
1.基本数据类型
String ,number,boolean,null,undefined
2.复合数据类型
Array,Object
3.赋值
-
ES5规范
var i = 3; var j = 1.1; var x = true; var y = 'etoak' /*由于js没有默认值,如果没有声明初始值,则会变成undefined,即未定义类型*/ var a;
-
ES6规范
var赋值存在许多bug,如越级bug等。
ES6新规范中使用 let 赋值变量 const复制常量,同时不书写分号
let i = 3 i++ /*字符串赋值 " 和 ' 都可以用推荐用'*/ let y = 'etoak' const PI = 3.14
2. BOM七对象
- 在JS中,将浏览器的页面分为七大对象,每个对象存在自己的属性和激发事件
- BOM是browser object model的缩写,简称浏览器对象模型
- BOM提供了独立于内容与浏览器窗口进行交互的对象,BOM主要用于管理窗口与窗口之间的通讯,因此这个和新对象是window
对象 | 含义 |
---|---|
window | 七对象的父对象,也是其他六个的顶层对象,是一个全局变量,表示整个页面的所有要素 |
document | 页面的正文 |
location | 页面的地址栏信息,同时负责页面的跳转 |
history | 负责封装页面的缓存和历史记录 |
screen | 封装用户显示器信息 |
navigator | 读取用户当前的浏览器信息 |
frame | 页面的一个框架,目前已淘汰 |
BOM和DOM的区别
Document Object Model(文档对象模型),就是把「文档」当做一个「对象」来看待。
Browser Object Model(浏览器对象模型),即把「浏览器」当做一个「对象」来看待。
区别:DOM描述了处理网页内容的方法和接口,BOM描述了与浏览器进行交互的方法和接口。在 DOM 中,文档中的各个组件(component),可以通过 object.attribute 这种形式来访问。一个 DOM 会有一个根对象,这个对象通常就是 document。而 BOM 除了可以访问文档中的组件之外,还可以访问浏览器的组件,比如问题描述中的 navigator(导航条)、history(历史记录)等等。在这种 「XOM」的模型中,最应该理解的就是 Object Model。Object Model 就表示你可以通过像操作对象一样,来操作这个 X。再解释一下什么是对象(Object)。在编程领域中,对象就是指的一种拥有具体数据(Data)并且具有(并不总是)特定行为(Behavior)的东西。例如,一个人 ,就可以看做一个对象。人的年龄、性别、身高、体重就是上文说的具体「数据」,通常将对象拥有的具体数据称作对象的「属性(Attribute)」;而人吃饭,睡觉,行走等能力,就是上文所说的「行为」,通常,我们把对象的行为称作对象的「方法(Method)」。另外,对象是可以嵌套的,也就是是说,一个对象的属性也可以是对象。上文所说的「像操作对象一样」,最主要就是指访问对象的属性和调用对象的方法。对象的属性可以通过 object.attribute 这种形式来访问,对象的方法可以通过 object.method(arguments) 这种形式来调用。对应到 DOM 中,document 这个根对象就有很多属性,例如 title 就是 document 的一个属性,可以通过 document.title 访问;document 对象也有很多方法,例如 getElementById,可以通过 document.getElementById(nodeId) 调用。
- DOM 是 W3C 的标准; [所有浏览器公共遵守的标准]
- BOM 是 各个浏览器厂商根据 DOM
在各自浏览器上的实现;[表现为不同浏览器定义有差别,实现方式不同]- window 是 BOM 对象,而非 js 对象;
3. DOM的使用
/*
* Html
* |
* DOM (文档对象模型:将正文上下文看做一个对象,全部封装在文档节点document中,
* html和xml都只有一个文档节点,也就是html)
* |
* Javascript
*
* Javascript不能直接修改页面的结构和样式
* 而必须通过文档对象模型来修改页面的结构和样式
*
* 浏览器内置两个解析器,一个是标签解析器,一个是脚本解析器,从上往下解析,如果出现解析异常则停止解析
* 通过控制台报错,如果解析无误,则将整个html文档节点封装进document中,全文只有一个文档节点
* 根据这个节点中的结构样式,创建一个文档对象模型格式如下:
*
* document
* |
* html
* |
* |
* ---------------------------------------
* | |
* | |
* head body
* | |
* ---------- ----------------------
* | | | |
* title meta button table
* |
* tr
* |
* td
* Js中的的方法可以对这个模型进行增删查改,修改之后,文档对象模型与页面实际结构和样式
* 不再统一,浏览器根据这个模型重新刷新页面,使之保持一致
* 我们这里去掉了window.onload之后那么浏览器从上往下解析,首先解析到的就是
* let domBtn = document.getElementById(‘btn’)
* 从模型中根据btn这个id拿取元素节点,可是注意window.onload前提被删除了,
* 整个页面还没有解析,模型未被创建,所以根本无法从模型中根据id拿取元素,所以报错
* 那么是不是必须书写window.onload来保证模型创建完整呢?
* 不是必须书写,通常将js脚本书写在页面body闭合标签标签之前,保证最后执行js脚本
* 优先加载html和css 并且保证模型创建完毕
1. JS实例一
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>1)如何绑定一个js函数</title>
<!--
类似css的style标签,在此标签内书写Js脚本
-->
<script>
/*
onload:激发事件之一,激发事件有很多种,此处表示页面载入无误
此处和BOM内置对象一起使用表示当前窗口载入无误,那么执行一个叫做etoak的函数
*/
window.onload = etoak
/*
function:固定写法表示是一个函数
注意不书写返回值,也不书写void,函数名推荐使用驼峰命名
括号内为实参,可选
*/
function etoak(){
//弹出一个对话框
/*
由于window是全局变量,所以所有的对象都是window的子对象
所以window可以省略
*/
alert('青山一道同云雨,明月何曾是两乡')
/*
根据id属性值从全文拿取唯一节点(Node)
*/
let domBtn = document.getElementById('btn')
/*
onclick:表示单击
给拿取的btn节点绑定单击事件,后面可以绑定一个有名的函数,也可以直接
绑定一个匿名函数
*/
domBtn.onclick = function(){
/*
innerText:属性,拿取节点中嵌套的值,不支持标签
*/
alert('button元素中的文本是'+domBtn.innerText)
}
}
</script>
</head>
<body>
<button id="btn">点我试试!!</button>
</body>
</html>
2.JS原理
HTML》DOM文档对象模型:将正文全文看做一个对象,全部封装在文档节点document中,html和xml都只有一个文档节点,也就是html
JavaScript**不能直接修改**页面的样式和结构而必须通过文档对象模型来修改页面的结构和样式,浏览器内置两个解析器,一个是标签解析器,一个是脚本解析器,从上往下解析,如果出现解析异常则停止解析,通过控制台报错;如果解析无误,则将整个html文档节点封装进document中,全文只有一个文档节点。根据这个节点中的结构样式
Js中的的方法可以对这个模型进行增删查改,修改之后,文档对象模型与页面实际结构和样式不再统一,浏览器根据这个模型重新刷新页面,使之保持一致。我们这里去掉了window.onload之后那么浏览器从上往下解析,首先解析到的就是let domBtn = document.getElementById(‘btn’)从模型中根据btn这个id拿取元素节点,可是注意window.onload前提被删除了,整个页面还没有解析,模型未被创建,所以根本无法从模型中根据id拿取元素,所以报错。
那么是不是必须书写window.onload来保证模型创建完整呢?
不是必须书写,通常将js脚本书写在页面body闭合标签标签之前,保证最后执行js脚本。优先加载html和css 并且保证模型创建完毕。
3.JS实例二
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>2)文档对象模型</title>
<style>
table{
width:200px;
border-collapse: collapse;
}
table,tr,td{
border:solid 2px red;
}
</style>
<script>
//窗口加载无误,执行此匿名方法
window.onload = function(){
//根据id从全文拿取一个元素节点
let domBtn = document.getElementById('btn')
//当按钮单击时,执行此匿名方法
domBtn.onclick = function(){
//根据table标签名拿取一个元素节点列表NodeList,这个NodeList封装了所有匹配table的元素
//也就是将所有标签名为table的元素,装到一个“数组”中
let domTb = document.getElementsByTagName('table')[0]
//控制台打印
//console.log(domTb)
//创建一个tr元素节点<tr></tr>
let domTr = document.createElement('tr')
/*
* 修改这个元素的节点,向内部添加文本或者超文本
* innerText:向元素节点中添加文本
* innerHTML:向元素节点中添加超文本
* 添加前:<tr></tr>
* 添加后:<tr><td style="color:coral">我是添加的一列</td></tr>
* 不管是innerHTML还是innerText如果元素节点中存在节点值不管是文本还是子元素则全部被覆盖
*/
domTr.innerHTML = '<td style="color:coral">我是添加的一列</td>'
/*
* 将组装好的tr追加到table中作为子元素,不影响原先的子元素
*
* 元素节点.appendChild(子元素)
* 元素节点中原先的子元素不受影响,追加到原先子元素之后
*/
domTb.appendChild(domTr)
//通过修改style属性来间接修改页面的样式
/*
* Math:计算内置对象,专门用来进行数学运算
* Math.floor():返回小于或者等于一个给定数字的最大整数
* Math.random():返回介于 0(包含) ~ 1(不包含)之间的数
* */
let r = Math.floor(Math.random()*255)
let g = Math.floor(Math.random()*255)
let b = Math.floor(Math.random()*255)
/*
* 节点.style.样式名 = 样式值
* 注意这里的样式名与CSS的样式名书写略有出入,不能出现- 必须 使用驼峰书写格式
* css:background-color
* js:backgroundColor
* font-size ==> fontSize
*
*/
//domTb.style.backgroundColor = 'rgb('+r+','+g+','+b+')'
/*
使用ES6提供的模板字符串来避免进行字符串的拼接
`${要输出的值}`
* */
domTb.style.backgroundColor = `rgb(${r},${g},${b})`
}
}
</script>
</head>
<body>
<button id="btn">添加一行</button>
<table>
<tr>
<td>我是默认存在的一列</td>
</tr>
</table>
<script>
/*
* js如果放置在此处,则不需要每次都添加window.onload
* 页面的加载顺序应该是
* html ==> css ==>js
* */
</script>
</body>
</html>
4.DOM节点
- DOM中共有12种节点,最为常用的试以下五种,根据节点的不同NodeType,NodeValue,NodeName三个属性也不同
节点类型 | 描述 | NodeType | NodeValue | NodeName |
---|---|---|---|---|
文档节点 | document,一个文档只能有一个文档元素,在html中就是html,xml中就是根元素 | 9 | 不可用 | 永远叫做#document |
元素节点 | 页面中的各种元素,例如 div p table,通过getElementById(‘idName’)等可以拿取,可以使用 innerHTML和innerText修改元素内的结构 | 1 | 不可用 | 标签名字 |
属性节点 | 一般是元素中的 class、id、src、href都属于属性 | 2 | 属性值 | 属性名 |
文本节点 | 就是嵌套在标签中的内容 | 3 | 嵌套的文本 | 永远是#text |
注释节点 | 8 | 不可用 |
5.节点相关方法及常用选择器
-
节点相关方法
方法 含义 firstChild 获取元素的首个节点 lastChild 获取元素的最后一个节点 childNodes 获取元素的节点列表 appendChild 在元素后边添加节点 parentNode 获取父节点 -
常用选择器
类型 方法 描述 Node document.getElementById(‘idName’) 根据唯一的id属性拿取一个元素节点 NodeList document.getElementsByTagName(‘tagName’) 根据标签名拿取类数组,里边封装了所有附条件的元素节点 NodeList document.getElementsByName(‘name’) 根据name属性拿取类数组,里边封装了所有附条件的元素节点 NodeList document.getElementsByClassName(‘classname’) 根据class属性拿取类数组,里边封装了所有附条件的元素节点 使用JS打印九九乘法表
<script> let str = '' for(let a = 1;a<=9;a++){ for(let b = 1;b<=a;b++){ str += `${b}*${a}=${b*a}\t` } str += '<br />' } document.write(str) </script>
4.JS引用
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>5)外部引入js文件</title>
</head>
<body>
<!--
onblur:失去焦点
此处函数传递的是实参
-->
测试1: <input type="text" name="test1"
onblur="func3('测试',true,100)"/>
<!-- 引入独立的js文件到本页面,类似css的外链式 -->
<script src="./script/myjs1.js"></script>
<!--
注意引入js文件的script标签中src表示源,后面是独立js文件的路径
script标签要么引入js文件要么直接书写,不允许同时引入和书写js
-->
<script src="./script/myjs2.js"></script>
</body>
</html>
5.使用对象字面量和构造方法创建对象
1. 对象字面量
/*
对象字面量
let 对象名 = {
属性名:属性值,
属性名:属性值,
方法名:function(){
}
上变得是ES5的写法,简称为钩子函数
ES6简化为 方法名(){}
}
*/
let stu = {
name:'赵信',
age=10,
hobby:['吃饭','睡觉','打豆豆'],
run(){
console.log(`今年${this.age}岁的${this.name}非常喜欢${this.hobby[1]}`)
}
}
- 拿取属性值
属性值 = 对象名.属性名
- 调用方法
对象名.方法名()
- 删除属性
delete 对象名.属性名
/*注意此处删除的是属性,而不是属性的值*/
2. 使用构造方法创建灵活的对象
/*
使用构造方法创建灵活的对象
function 对象名(args1,args2){
this.args1 = args1
this.args2 = args2
this.方法名 = function(){
}
}
*/
function person(name,age,hobby){
this.name = name
this.age = age
this.hobby = hobby
this.run = function(){
if(age > 18){
console.log(`欢迎您回来${this.name}`)
}esle{
console.log(`年龄在18岁以下,禁止访问本页面`)
}
}
}
let oneperson = new person('尚朝晖',20,['游戏','学习'])
console.log(`${oneperson.name}\n${oneperson.hobby[0]}`)
oneperson.run()
6.数组
1. 如何创建数组
/*括号里的数字是数组的长度*/
/*JS中所有的length都是属性*/
let arr = new Array(4)
arr[0] = 100
arr[1] = true
arr[2] = 'etoak'
arr[3] = null
2. 如何遍历数组
- for循环遍历
for(let i = 0;i < arr.length;i++){
console.log(`数组元素值是${ar[i]}`)
}
- for in循环**[ES5]**
/*
for(let key in arr){
如果是单值类型集合,那么这个key就是索引值
如果是键值对,则是属性名
}
*/
for(let key in arr){
console.log(`数组索引值是${key}`)
console.log(`数组元素值是${ar[key]}`)
}
- for of循环**[ES6]**
/*
for(let val of arr){
这个val就是值
}
此循环不能用来迭代自己创建的js对象,因为自己创建的对象底层没有迭代器
*/
for(let val of arr){
console.log(`数组元素值是${value}`)
}
- forEach
/*
arr.forEach(function(被遍历的对象,索引值){
注意被遍历的对象和索引值位置不能变
})
*/
arr.forEach(function(val.index){
console.log(`索引值是${index}`)
console.log(`数组元素值是${value}`)
})
3. 向数组添加一个元素
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>8)小练习</title>
<link rel="stylesheet" href="css/mystyle.css">
</head>
<body>
<div id="container">
<fieldset>
<legend>添加用户</legend>
<label for="nameid">姓名: <input type="text" name="name" id="nameid"
placeholder="请输入姓名" minlength="4" maxlength="8"></label>
<label for="emailid">邮箱: <input type="text" name="email" id="emailid"
placeholder="请输入邮箱"></label>
<label for="ageid">年龄: <input type="number" max="65" min="18" placeholder="岁" id="ageid"></label>
<button style="border-radius:18px;background-color:lightblue;">添加用户</button>
</fieldset>
<hr>
<div id="show"></div>
</div>
<script>
/*
* 创建一个数组
* */
const items = [
//注意level字段'1'为管理员 '0'为普通用户
{id:1,name:'elena',email:'et01@etoak.com',age:20,level:'0'},
{id:2,name:'penny',email:'et02@etoak.com',age:20,level:'0'},
{id:3,name:'matt',email:'et03@etoak.com',age:30,level:'0'},
{id:4,name:'damon',email:'et04@etoak.com',age:40,level:'0'},
{id:5,name:'admin',email:'et05@etoak.com',age:50,level:'1'},
{id:6,name:'stefan',email:'et06@etoak.com',age:55,level:'0'},
{id:7,name:'aleric',email:'et07@etoak.com',age:30,level:'0'},
{id:8,name:'damon',email:'et08@etoak.com',age:40,level:'0'},
{id:9,name:'jack',email:'et09@etoak.com',age:20,level:'0'}
]
//向数组中添加一个元素
/*
* 数组.push(追加的元素)
*
* 注意在Js中基本数据类型,在定义const常量之后划分内存地址,不能再次进行任何变动,
* 但是在数组,对象中,虽然也划分了内存地址,但是在数组和元素中可以进行任意的增删
* */
items.push({
id:10,name:'boonie',email:'et10@etoak.com',age:23,level:'0'
})
onload = ()=>{
//显示默认数据
show()
//拿取button按钮
let domBtn = document.getElementsByTagName('button')[0]
domBtn.onclick = ()=>{
//console.log('~~~~')
/*
* 点击添加按钮之后,表格中多出一行
* id:为现有数组的长度+1
* name:用户填写数据
* age:用户填写数据
* email:用户填写数据
* level:全都是'0'
* */
let domInputs = document.getElementsByTagName('input')
let name = domInputs[0].value
let email = domInputs[1].value
let age = domInputs[2].value
let item = {
id:items.length+1,
/*
* ES6:新特性
* 如果属性名和属性值重名,则直接书写即可不需要书写名值对形式
* */
name,
email,
age,
level:'0'
}
items.push(item)
//添加之后重新遍历数组
show()
}
}
//此函数用来拿取数据
function show(){
let table =
'<table id="tb"><thead><tr><th>ID</th><th>姓名</th><th>邮箱</th><th>年龄</th><th>权限</th><th>操作</th></tr></thead><tbody>'
items.forEach(item=>{
//tis:表示当前行的span元素节点
//this.parentNode:表示span的父节点,这里就是td元素节点
//this.parentNode.parentNode:表示tr元素节点
table +=
`<tr><td>${item.id}</td><td>${item.name}</td><td>${item.email}</td><td>${item.age}</td>
<td>${item.level=='0'?'普通用户':'管理员'}</td><td><span οnclick="del(this.parentNode.parentNode)">删除</span></td></tr>`
})
table += '</tbody></table>'
//将组装好的table放置进div中
document.getElementById('show').innerHTML = table
}
//此函数用来删除数据
function del(tr){
/*
* 这里拿取了tbody,之后调用removeChild来删除子元素传递过来的子元素tr
* */
document.getElementsByTagName('tbody')[0].removeChild(tr)
//tr.remove()
}
</script>
</body>
</html>
JS比较
- 在JS中字符串比较没有equals()方法,一般用不同数量的等号进行比较
运算符 | 含义 |
---|---|
= | 赋值 |
== | 两者如果是同一种数据类型则直接比较 否则转换成同一种数据类型进行比较 |
=== | 两者如果不是同一种数据类型,立刻返回false 是同一种数据类型,则进行比较 |
ES6新特性,箭头函数
在ES6中,匿名函数可以省略为箭头函数,类似于lambda表达式,匿名函数去掉function,在参数后添加 => 如果之后一个参数,则括号省略
上述forEach可以简略为
arr.forEach(val => console.log(`数组元素值是${value}`))
/*如果大括号内直接就是return语句,则大括号省略,return省略*/
7.JS常用20个字符串方法
- 蓝色的与Java中用法一致,红色为JS新的
-
charAt()
/*返回对应下标的字符*/ let str = '01234' alert(str.charAt(0)) //0
-
charCodeAt()
/*返回对应下边字符的 Unicode 编码*/ let str = 'AaBb' alert(str.charCodeAt(0)) //65
-
concat()
/*拼接字符串,不会改变现有的字符串,返回新的字符串*/ let str = 'Hello' let newStr = str.concat('World ','JavaScript') alert(newStr) // HelloWorld JavaScript
-
fromCharCode
/*从Unicode编码转换成字符*/ alert(String.frmCharCode(72,69,76,76,79))//HELLO
-
indexOf
/*查找指定字符串的第一次出现的下标,如果不存在返回-1*/ let str = 'HelloWorld' alert(str.indexOf('l'))//2 /*从下边指定下标开始查找的方式,后边的参数是可选的*/ alert(str.indexOf('l',3))//3
-
lastIndexOf
/*查找指定字符串最后一次出现的位置*/ let str = 'HelloWorld' console.log(str.lastIndexOf('l'))//8 /*指定开始搜索的位置*/ console.log(str.lastIndexOf('h',5))//-1
-
match
/*根据正则表达式在字符串中搜索匹配项,如果没有则返回null或者信息数组*/ let intRegex = /[0-9 -()+]+$/ let str1 = '999' let str2 = '999 JS Coders' //'999',index:0,input:'999'group:undefined console.log(str1.match(intRegex)) //null console.log(str2.match(intRegex))
-
replace
/*在字符串中用一些字符替换另外一些字符串,或者替换一个与正则表达式匹配的字符串*/ let myString = '999 JavaScript Code' //999 Jqyery Code console.log(myString.replace(/JavaScript/i,'Jquery')) //The Jquery Code console.log(myString.replace(new RegExp('999','gi'),'The'))
-
search
8. JS函数常见书写方式
- 直接在标签上绑定
<tagName 事件="函数1(实参)"></tagName>
<script>
function 函数1(形参){}
</script>
- 直接在script书写,绑定匿名
<tagName>XXX</tagName>
<scrript>
onload = function(){
let arg = 拿去元素节点
arg.事件 = function(){}
}
</scrript>
- 直接在script中书写,绑定有名函数
<tagName>XXXX</tagName>
<script>
onload = function(){
let args = 拿取元素节点
args..事件 = 函数名(实参)
}
function 函数名(形参){}
</script>
- 直接调用函数
<tagName></tagName>
<script>
function 函数名(形参){}
函数名(实参)
</script>
9.手动轮播图和验证
1.手动轮播图
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>10)简易相册</title>
<style>
body{
background-color: black;
}
div#container{
width:830px;
height:900px;
margin:0 auto;
}
</style>
</head>
<body>
<div id="container">
<table>
<caption><img src="./image/logo.png"></caption>
<tr>
<!--
type="image"
引入一张图片,这张图片可以点击,如果放置在form表单内,则可以提交表单
-->
<td><input type="image" src="image/left.gif"
onclick="change('left')"></td>
<td><img src="image/1.jpg" id="mypic"></td>
<td><input type="image" src="image/right.gif"
onclick="change('right')"></td>
</tr>
</table>
</div>
<script>
let currentPage = 1
function change(flag){
//拿取img元素
let domImg = document.getElementById('mypic')
if(flag=='left'){
if(currentPage>1){
currentPage--
}else{
alert('已经是首页了')
}
}
if(flag=='right'){
if(currentPage<6){
currentPage++
}else{
alert('已经是末页了')
}
}
domImg.src = `image/${currentPage}.jpg`
}
</script>
</body>
</html>
2. 表单验证
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>11)表单验证</title>
</head>
<body>
<!--
对表单项进行验证
onsubmit:表单提交时,如果return后面的函数返回false,则表单无法提交
否则表单可以提交
-->
<form action="wow10.html" method="get"
onsubmit="return checkAll()">
<!--
1)单行文本输入框
要求用户输入在4到8位之间
-->
<label for="nameid">用户姓名:</label>
<input type="text" name="name" id="nameid" required placeholder="请输入用户姓名"
autocomplete="off" onblur="checkName()" />
<span id="name_msg"></span>
<br>
<label for="passid">用户密码:</label>
<!--
this:书写在哪里就表示本元素
-->
<input type="password" name="pass" id="passid" required placeholder="请输入用户密码"
autocomplete="off" onblur="checkPass(this.value)" />
<span id="pass_msg"></span>
<br>
<input type="submit" value="提交">
<input type="reset" value="取消">
</form>
<script>
let flagName = false
let flagPass = false
function checkName(){
//拿取span
let domSp = document.getElementById('name_msg')
//拿取单行文本输入框
let domInput = document.getElementById('nameid')
if(domInput.value.length<4||domInput.value.length>8){
//添加错误提示
domSp.innerHTML='<img src="image/wrong.png">用户姓名不能小于4位或者大于8位'
//文字设置为红色
domSp.style.color = 'red'
flagName = false
return
}
domSp.innerHTML='<img src="image/right.png">用户姓名符合要求'
domSp.style.color = 'green'
flagName = true
}
function checkPass(d){
//拿取span
let domSp = document.getElementById('pass_msg')
if(d.length<4||d.length>8){
domSp.innerHTML='<img src="image/wrong.png">用户密码不能小于4位或者大于8位'
domSp.style.color = 'red'
flagPass = false
return
}
domSp.innerHTML='<img src="image/right.png">用户密码符合要求'
domSp.style.color = 'green'
flagPass = true
}
function checkAll(){
return flagName&&flagPass
}
</script>
</body>
</html>
ES6新特性
第六章 dom4j
1. 创建lib目录
FIle -> Project Structure -> Modules -> Dependencies
点击 加号 选择 1.JARs or directory 选择 JAR directory
2. 使用Dom4j读取xml文件
- 创建扫描仪
SAXReader sax = new SAXReader();
- 创建文档对象模型
//这个对象模型就是要读取的文件
DOcument doc = sax.read(new File("test.xml"));
- 获取根元素
Element root = doc.getRootElement();
- 获取一级子元素并遍历
List<Element> firstList = root.elements();
for(Element firstEle : firstList){}
- 获取每个以及子元素的属性和值
List<Attribute> firstAttrs = firstEle.attribute();
for(Attribute firstAtt : firstAttrs){
sout(firstAtt.getName());
sout(firstAtt.getValue());
}
xml文件如下
<?xml version="1.0" encoding="utf-8" ?>
<root>
<student id="et01" name="elena">
<email>et01@etoak.com</email>
<phone>1111111</phone>
</student>
<student id="et02" name="stefan">
<email>et02@etoak.com</email>
<phone>222222</phone>
</student>
<student id="et03" name="damon">
<email>et03@etoak.com</email>
<phone>3333333</phone>
</student>
<student id="et04" name="tom">
<email>et04@etoak.com</email>
<phone>4444444</phone>
</student>
</root>
java代码如下
package com.etoak.test;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.util.List;
/**
* Created by eleven on 2020/3/17.
*/
public class Readxml {
public static void main(String[] args) {
try{
//1.拿取Xml解析器,类似Scanner
SAXReader sax = new SAXReader();
/*
2.使用SAXReader对象对xml文件进行解析
如果解析无误则返回一个文档对象模型,
这个文档对象模型与xml完全相同的结构
*/
Document doc = sax.read(new File("etoak.xml"));
//3.拿取根元素
Element root = doc.getRootElement();
System.out.println("根元素名字是:" + root.getName());
//4.拿取任意一个一级子元素
List<Element> firstList = root.elements();
for (Element firstEle : firstList){
System.out.println("一级子元素的元素名是:" + firstEle.getName());
//拿取一级子元素的属性
List<Attribute> firstAttrList = firstEle.attributes();
for(Attribute fisrtAttr : firstAttrList){
System.out.println("属性名是:" + fisrtAttr.getName());
System.out.println("属性值是:" + fisrtAttr.getValue());
}
//拿取二级子元素
List<Element> secondList = firstEle.elements();
for (Element secondEle : secondList) {
System.out.println("二级子元素名字是:" + secondEle.getName());
System.out.println("二级子元素内嵌套的值是:" + secondEle.getText());
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
3.使用Dom4j创建xml文件
- 创建文档对象模型
//这个对象模型是空的
Document doc = DocumentHelper.createDocument();
- 添加节点
//添加根节点
Element root = doc.addElement("root");
//添加一级子节点
Element one = root.addElement("one");
//添加二级子节点
Element two = one.addElement("two");
//给一级子节点添加属性和值 第一个参数为属性名,第二个为属性值
one.addAttribute("id","01");
//给二级子元素内赋值
two.setText("two");
- 创建流
OutputStream os = new FileOutStream("test.xml");
- 规定输出格式
//有缩进,跟自己写的格式一致
OutputFormat format = OutputFormat.createPrettyPrint();
//规定输出编码
format.setEncoding("utf-8");
- 输出
//第一个参数为要输出文件的路径,第二个为输出的格式
XMLWriter xw = new XMLWriter(os,format);
//把文档对象模型输出
xw.write(doc);
//关闭输出
xw.close();
package com.etoak.test;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class WriteXml {
public static void main(String[] args) {
try {
/*
* 使用Java代码利用DOM4j技术来创建一个XML
* */
//1)创建一个文档对象模型
Document doc = DocumentHelper.createDocument();
//2)创建一个根元素
/*
* <root>
* </root>
* */
Element root = doc.addElement("root");
//3)创建一级子元素
/*
* <root>
* <student></student>
* </root>
* */
Element student = root.addElement("student");
//给一级子元素添加属性
/*
* <root>
* <student id="et001" name="elena"></student>
* </root>
* */
student.addAttribute("id","et001");
student.addAttribute("name","elena");
//4)创建二级子元素
/*
* <root>
* <student id="et001" name="elena">
* <email>XXXX</email>
* <phone>XXXX</phone>
* </student>
* </root>
*
* */
Element email = student.addElement("email");
email.setText("et01@etoak.com");
Element phone = student.addElement("phone");
phone.setText("111111");
//再次添加一个一级子元素
/*
* <root>
* <student id="et001" name="elena">
* <email>XXXX</email>
* <phone>XXXX</phone>
* </student>
* <person>XXXX</person>
* </root>
*
* */
Element person = root.addElement("person");
person.setText("我是Person中的值");
//设置一个流,参数表示生成的xml叫什么输出到哪里
OutputStream os = new FileOutputStream("etoak2.xml");
//设置xml的格式
//此处设置为标准格式,自带换行,与我们书写习惯相同,否则输出的xml从左往右不换行
OutputFormat format = OutputFormat.createPrettyPrint();
//设置xml文件的编码
format.setEncoding("utf-8");
//输出xml文件
//括号内传递两个参数,第一个表示输出到哪里叫什么,第二个是格式和编码是什么
XMLWriter xw = new XMLWriter(os,format);
//根据拼装好的文档对象模型进行输出
xw.write(doc);
xw.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
第七章 JDBC
-
jdbc(Java DataBase Connectivity)
在C语言的Odbc基础上发展而来,是最早的ORM(Object Relation Mapping对象映射关系)工具,可以使用面向对象的Java代码
对关系型数据库进行操作。
ORM工具随着不断的发展衍生出了很多框架那么大部分框架都是在JDBC基础上发展而来,JDBC是几乎所有框架的基础,也是效率最高的ORM工具
JDBC是第一方技术,所有的接口等全部放置在java.sql包中全手动 JDBC
半自动 myBatis
全自动 Hibernate1)创建关系表
2)创建实体类
A:一般放置在com.etoak.po包中或者bean包中
B:尽量使用包装类封装字段,字段名对应
C:必须书写空参构造方法,酌情覆盖有参构造方法
D:酌情覆盖toString()
3)创建工厂类
A:工厂类一般放置在com.etoak.factory包中,此类链接数据库
B:加载驱动,只需要加载一次放置在静态初始化块中
C:封装一个方法返回链接
4)书写dao层
A:dao层全部放置在com.etoak.dao包中
B:接口用来设置需要被实现的方法
C:实现类实现接口中的方法
创建数据表
DROP table if EXISTS person;
CREATE table person (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(10) NOT NULL,
pass VARCHAR(10) NOT NULL,
age INT,
salary DOUBLE,
birth DATE
);
insert INTO person VALUES (null,'elena','12345',20,5000.00,'1990-06-03');
insert INTO person VALUES (null,'penny','123465',30,6000.00,'1993-06-13');
insert INTO person VALUES (null,'aleric','1234545',40,7000.00,'1992-06-05');
insert INTO person VALUES (null,'bonnie','1432345',20,3000.00,'1996-06-03');
insert INTO person VALUES (null,'matt','123345',24,15000.00,'1997-06-04');
insert INTO person VALUES (null,'damon','123445',23,25000.00,'1992-06-04');
insert INTO person VALUES (null,'用户名','1232245',29,5000.00,'1998-07-01');
1. JavaBean实体类
如果一个Java类仅仅封装了属性,没有其他方法,则称之为POJO类或实体类,好像也叫作JavaBean,通常放置在com.xxx.po 或者 com,xxx.bean 包中
这个类中封装的属性必须对用数据库中的字段,必须覆盖空参的构造方法,酌情覆盖全参构造方法,酌情覆盖toString()方法
public class Person{
//使用包装类封装字段,必须对应表中的字段
private Integer id;
private String name;
private String pass;
private Integer age;
private Double salary;
private Date birth;
/*
* 必须覆盖空参的构造方法
* 覆盖空参构造方法之后可以new空参的对象
* Person per = new Person();
* */
public Person() {
}
/*
* 酌情覆盖全参构造方法,这样我们就可以直接new一个带有六个参数的对象
* Person per = new Person(XX,XX,XX,XX,XX,XX);
* */
public Person(Integer id, String name, String pass, Integer age, Double salary, Date birth) {
this.id = id;
this.name = name;
this.pass = pass;
this.age = age;
this.salary = salary;
this.birth = birth;
}
//各个属性的get和set方法
//看需求是否覆盖 toString() 方法
}
2. 工厂类
- 一般放置在**com.xxx.factory包中**
1.加载驱动
static{
try{
Class.forName("com.mysql.jdbc.Driver");
}catch(Exception e){
e.printStackTrace();
}
}
2.获取链接
public static Connection getConn(){
try{
//2.加载链接
/*
* 完全版:
* jdbc:mysql://localhost:端口号/database
* jdbc:mysql://远程ip地址:端口号/database
* 如果数据库在本地,则可以使用简化版:
* jbdc:mysql:///database
* user:用户名
* password:用户密码
* */
return DriverManager.getConnection("jdbc:mysql:///et1912","root","dream");
}catch(Exception e){
e.printStackTrace();
return null;
}
}
3.释放资源
public static void close(ResultSet rs, Statement st,Connection con){
try {
if(rs!=null)
rs.close();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
if(st!=null)
st.close();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
if(con!=null)
con.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
代码
package com.etoak.factory;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class Factory {
/*
* A:加载驱动
* 不同的数据库品牌厂商提供了不同的驱动,可以正确识别各种厂商的数据库品牌
* 语法等,加载驱动之后,在Java端可以使用相同的Java代码操作不同品牌不同语法的数据库
此处使用静态初始化块仅仅加载一次即可
* */
static{
try {
//使用反射类加载的方式加载驱动
//不同品牌的驱动不同,需要导入数据库厂商提供的jar包
Class.forName("com.mysql.jdbc.Driver");
} catch (Exception ex) {
ex.printStackTrace();
}
}
/*
* B:获取连接
* 连接数据库,通过驱动管理器提供连接地址,用户名和密码可以链接数据库
* */
public static Connection getCon(){
try {
/*
* 注意这里三个参数分别是
* url:链接地址
* 格式完全版
* jdbc:mysql://localhost:8080/数据库名
* jdbc:mysql://远程ip地址:8080/数据库名
* 如果数据库就在本地,则可以使用简化版
* jdbc:mysql:///数据库名
* user:用户名
* password:密码
* */
return DriverManager.getConnection(
"jdbc:mysql:///et1912","root","etoaketoak");
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
/*
* C:释放资源
* 释放资源时顺序如下:
* 首先关闭结果集,关闭执行器,最后关闭连接
* */
public static void close(ResultSet rs, Statement st,Connection con){
try {
if(rs!=null)
rs.close();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
if(st!=null)
st.close();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
if(con!=null)
con.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
}
3.dao持久化层
- dao(Data Access Object) 数据访问对象
- 通常放置在**com.xxx.dao包下**
1. 接口
/*
* Dao:Data Access Object数据访问对象
* 这里的接口一般由项目经理或者一些规范的制定者来执行
* 如何实现不关心,仅仅提供了一个规范
* DaoIf 是DaoInterface的简写
* */
public interface PersonDaoIf {
//添加一个用户
public boolean addPerson(Person per);
//根据id删除一个用户
public boolean delPersonById(Integer id);
//拿取全部用户
public List<Person> queryAll();
//根据用户名查询
public boolean queryName(String name);
//根据用户名和密码查询
public Person queryPerson(String name,String pass);
//修改用户资料
public boolean updatePerson(Person per);
}
public interface PersonDaoIf2 {
//1添加用户
public boolean addPerson(Person per);
//2根据id删除
public boolean delPersonById(Integer id);
//3根据name删除
public boolean delPersonByName(String name);
//4拿取全部
public List<Person> queryAll();
//5姓名查重
public boolean queryName(String name);
//6登录查询
public Person queryPerson(String name,String pass);
//7拿取总记录数
public Integer queryCount();
//8分页查询
public List<Person> queryPage(Integer index,Integer max);
//9姓名模糊分页查询
public List<Person> queryNameLikePage(String args,Integer index,Integer max);
//10条件分页查询
public List<Person> queryLikePage(Person per,Integer index,Integer max);
//11批量删除
public boolean multiDel(String[] args);
//12修改
public boolean updatePerson(Person per);
}
2.接口实现类[Statement]
- 创建 PersonDaoIf 接口的实现类 PersonDaoImpl
- 右键空白处 **(或者alt + insert)**选择 Implements Method,实现接口中的抽象方法
public class PersonDaoImpl implements PersonDaoIf{
//设置连接
Connection conn;
//设置执行器
Statement st;
//结果集
ResultSet rs;
@Override
public boolean addPerson(Person per) {
try {
//书写sql语句
/*
* java.util.Date无法和字符串直接通过+拼接成sql语句
* 可以有以下两种方式
* 1)将java.util.Date转换为java.sql.Date,可以直接拼接
* 2)将java.util.Date转换为字符串
*
*
* */
String sql =
"insert into person values (null,'"+per.getName()+"','"+per.getPass()+"',"+per.getAge()+","
+per.getSalary()+",'"+new SimpleDateFormat("yyyy-MM-dd").format(per.getBirth()) +"')";
//获取连接
con = Factory.getCon();
//拿取执行器
st = con.createStatement();
//执行器根据sql语句调用方法
return st.executeUpdate(sql)==1;
} catch (Exception ex) {
ex.printStackTrace();
return false;
} finally {
//不管是否执行正常都要释放资源
Factory.close(null,st,con);
}
}
}
存在的安全隐患
- 当传入的是一个恒等的条件表达式的时候,语句相当于查找全表数据
- 可以被SQL注入
3.接口实现类[PreparedStatement]
- 创建类PersonDaoImpl2实现PersonDaoIf2接口
- PreparedStatement是Statement的子类
- 使用PreparedStatement执行器时,使用 ? 先进行站位,后填充
- 填充日期类型时,需要用**new java.sql.Date(xxx.getDate().getTime())**
package com.etoak.dao;
import com.etoak.factory.Factory;
import com.etoak.po.Person;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
public class PersonDaoImpl2 implements PersonDaoIf2 {
Connection con;
Statement st;
/*
* 使用执行器PreparedStatement,它是Statement的子类
* */
PreparedStatement pst;
ResultSet rs;
@Override
public boolean addPerson(Person per) {
try {
/*
* 在使用Statement时,需要我们对sql语句进行拼接,但是在进行拼接时非常的不方便,
* 并且安全性较低,容易出现sql注入安全隐患
* 使用PreparedStatement则可以使用?作为占位符的形式来组装sql语句,不再需要我们
* 进行复杂的sql拼接了
*
* 注意使用?占位,必须填充占位符,因为sql语句中不能再执行时存在?
* */
String sql = "insert into person values (null,?,?,?,?,?)";
con = Factory.getCon();
//获取执行器的同时,加载带有?占位符的sql语句
pst = con.prepareStatement(sql);
//由于存在占位符所以必须填充
/*
* pst.set数据类型(index,要填充的值)
* index:表示从左往右?的顺序,注意从1开始
* */
pst.setString(1,per.getName());
pst.setString(2,per.getPass());
pst.setInt(3,per.getAge());
pst.setDouble(4,per.getSalary());
/*
* java.util.Date和java.sql.Date的区别
*
* 1)java.util.Date是sql.Date的父类
* 2)util.Date一般可以设置年月日小时分钟秒
* 而sql.Date只能精确到年月日
* 3)util.Date不能和字符串直接拼接成sql语句
* 而sql.Date可以
* 4)通过getTime()进行转换
* */
pst.setDate(5,new java.sql.Date(per.getBirth().getTime()));
return pst.executeUpdate()==1;
} catch (Exception ex) {
ex.printStackTrace();
return false;
} finally {
//长按ctrl或者command键可以溯源
Factory.close(null,pst,con);
}
}
@Override
public boolean delPersonById(Integer id) {
try {
String sql = "delete from person where id = ?";
con = Factory.getCon();
//获取执行器的同时加载sql语句
pst = con.prepareStatement(sql);
pst.setInt(1,id);
return pst.executeUpdate()==1;
} catch (Exception ex) {
ex.printStackTrace();
return false;
} finally {
Factory.close(null,pst,con);
}
}
@Override
public boolean delPersonByName(String name) {
try {
String sql = "delete from person where name = ?";
con = Factory.getCon();
pst = con.prepareStatement(sql);
pst.setString(1,name);
return pst.executeUpdate()>=1;
} catch (Exception ex) {
ex.printStackTrace();
return false;
} finally {
Factory.close(null,pst,con);
}
}
@Override
public List<Person> queryAll() {
try {
String sql = "select * from person";
con = Factory.getCon();
pst = con.prepareStatement(sql);
rs = pst.executeQuery();
List<Person> list = new ArrayList<>();
while(rs.next()){
list.add(new Person(rs.getInt(1),
rs.getString(2),rs.getString(3),rs.getInt(4)
,rs.getDouble(5),rs.getDate(6)));
}
return list;
} catch (Exception ex) {
ex.printStackTrace();
return null;
} finally {
Factory.close(rs,pst,con);
}
}
@Override
public boolean queryName(String name) {
try {
String sql = "select * from person where name = ?";
con = Factory.getCon();
pst = con.prepareStatement(sql);
pst.setString(1,name);
return pst.executeQuery().next();
} catch (Exception ex) {
ex.printStackTrace();
return false;
} finally {
Factory.close(rs,pst,con);
}
}
@Override
public Person queryPerson(String name, String pass) {
try {
String sql = "select * from person where name = ? and pass = ?";
con = Factory.getCon();
pst = con.prepareStatement(sql);
pst.setString(1,name);
pst.setString(2,pass);
rs = pst.executeQuery();
if(rs.next()){
return new Person(rs.getInt(1),
rs.getString(2),rs.getString(3)
,rs.getInt(4),rs.getDouble(5),rs.getDate(6));
}
return null;
} catch (Exception ex) {
ex.printStackTrace();
return null;
} finally {
Factory.close(rs,pst,con);
}
}
@Override
public Integer queryCount() {
try {
String sql = "select count(*) from person";
con = Factory.getCon();
pst = con.prepareStatement(sql);
rs = pst.executeQuery();
/*
* 这里的rs返回的是一个两行一列的表格
* ------------
* | |
* | count |
* ------------
* | |
* | 记录数 |
* ------------
*
* 指针指向表头,使其下移一行
*/
rs.next();
return rs.getInt(1);
} catch (Exception ex) {
ex.printStackTrace();
return null;
} finally {
Factory.close(rs,pst,con);
}
}
@Override
public List<Person> queryPage(Integer index, Integer max) {
try {
/*
* select 字段 from 表 limit x,y;
* x:表示起始索引值
* y:表示显示几条记录
* */
String sql = "select * from person limit ?,?";
con = Factory.getCon();
pst = con.prepareStatement(sql);
pst.setInt(1,index);
pst.setInt(2,max);
rs = pst.executeQuery();
List<Person> list = new ArrayList<>();
while(rs.next()){
list.add(new Person(rs.getInt(1),
rs.getString(2),
rs.getString(3),
rs.getInt(4),
rs.getDouble(5),
rs.getDate(6)));
}
return list;
} catch (Exception ex) {
ex.printStackTrace();
return null;
} finally {
Factory.close(rs,pst,con);
}
}
//根据姓名模糊查询并且分页
@Override
public List<Person> queryNameLikePage(String args, Integer index, Integer max) {
try {
String sql = "select * from person where name like ? limit ?,?";
con = Factory.getCon();
pst = con.prepareStatement(sql);
//对左模糊右模糊进行拼接
pst.setString(1,"%"+args+"%");
pst.setInt(2,index);
pst.setInt(3,max);
rs = pst.executeQuery();
List<Person> list = new ArrayList<>();
while(rs.next()){
list.add(new Person(rs.getInt("id")
,rs.getString("name")
,rs.getString("pass")
,rs.getInt("age")
,rs.getDouble("salary")
,rs.getDate("birth")));
}
return list;
} catch (Exception ex) {
ex.printStackTrace();
return null;
} finally {
Factory.close(rs,pst,con);
}
}
@Override
public List<Person> queryLikePage(Person per, Integer index, Integer max) {
return null;
}
/*
* 此处传入的是字符串类型的数组,内部封装了所有的id
* 根据这些id批量删除,注意不需要考虑id的真实性情况
*
* */
@Override
public boolean multiDel(String[] args) {
try {
String sql = "delete from person where id in (";
String sum = "";
for(int i = 0;i<args.length;i++){
sum = args[i]+","+sum;
}
sum = sum.substring(0,sum.length()-1);
sql += sum+")";
con = Factory.getCon();
st = con.createStatement();
/*
* pst = con.preparedStatement(sql);
* pst.executeUpdate()>=1
* */
return st.executeUpdate(sql)>=1;
} catch (Exception ex) {
ex.printStackTrace();
return false;
} finally {
Factory.close(null,pst,con);
}
}
@Override
public boolean updatePerson(Person per) {
try {
String sql =
"update person set name = ?,pass = ?,age = ?,salary = ?,birth = ? where id = ?";
con = Factory.getCon();
pst = con.prepareStatement(sql);
pst.setString(1,per.getName());
pst.setString(2,per.getPass());
pst.setInt(3,per.getAge());
pst.setDouble(4,per.getSalary());
pst.setDate(5,new java.sql.Date(per.getBirth().getTime()));
pst.setInt(6,per.getId());
return pst.executeUpdate()==1;
} catch (Exception ex) {
ex.printStackTrace();
return false;
} finally {
Factory.close(null,pst,con);
}
}
}
1.书写套路注意事项
- Statement执行器
//在实现的方法中,第一步现获取连接
conn = Factory.getConn();
//第二步,获取执行器
st = conn.createStatement();
//第三步。书写Sql语句,这里可以先将SQL语句写完整之后在根据是否是字符类型的加双引号
String sql = "select * from person";
//如果没有返回结果集,则返回执行结果,
//此方法返回的是受影响的行数,返回值为int
return st.executeUpdate(sql) > 0;
//如果有返回结果集,则第四步,获得结果集
rs = st.executeQuery(sql);
//第五步,创建一个新的空集合,给定泛型确保只能存储该类型的对象
List<Person> list = new ArrayList<>();
//第六步 把结果集中的结果依次添加到空集合中
while(rs.next()){
list.add(new Person(rs.getInt(1),...));
}
//第七步,关闭连接
Factory.closeConn(rs,st,conn);
- PreparedStatement执行器
//1获取连接
conn = Factory.getConn();
//2.书写sql语句,先使用占位符 ? 来填充
String sql = "select * from person where name like ? limit ?,?";
//3.获取执行器d的同时把sql语句传入
pst = conn.preparedStatement(sql);
//4.给占位符传入相应的值,第一个参数代表是sql语句中第几个问号,第二个参数代表传入的值
//4.1 模糊查询时拼接字符串
pst.setString(1,"%" + per.getName() + "%");
//4.2传入其他参数
pst.setInt(2,per.getAge());
//4.3 Date类型,因为util.Date不能拼接sql语句,所以要转化成sql.Date
pst.setDate(3,new java.sql.Date(per.getBirth().getTime()));
//5.执行
pst.executeQuery();
2. Date注意事项
- 执行sql语句拼接Date时
- java.util.Date无法和字符串直接通过+拼接成sql语句
可以有以下两种方式
1)将java.util.Date转换为java.sql.Date,可以直接拼接
2)将java.util.Date转换为字符串
String sql = "insert into person values('"+ new SimpleDateFormat("yyyy-MM-dd").format(Date) +"')"
- 创建对象把String转换成Date类型时
Person per = new Person(new SimpleDateFormat("yyyy-MM-dd").parse("1999-01-01"));
- PreparedStatement传入Date
pst = conn.preparedStatement(sql);
pst.setDate(1,new java.sql.Date(per.getBirth().getTime()));
4. 测试
package com.etoak.test;
import com.etoak.factory.Factory;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
public class Test {
public static void main(String[] args) {
try {
//调用工厂获取连接,从而链接数据库
Connection con = Factory.getCon();
/*
* 如果出现Can not found class com.mysql.jdbc.Driver则是没导包
* 或者驱动书写错误
* */
if(con==null){
System.out.println("链接失败!!");
return;
}
System.out.println("链接成功!!");
/*
* 获取连接之后可以获取执行器,执行器用来执行SQL语句
* 主要有以下两种执行器
* Statement
* PreparedStatement
* */
Statement st = con.createStatement();
String dml1 = "insert into person values (null,'测试1','12345',30,2000.00,'2000-03-03')";
String dql1 = "select * from person";
/*
* 获取执行器之后可以根据SQL语句类型的不同,调用以下几种方法
*
* 1) boolean execute()
* 如果执行DQL语句,则返回true
* 如果执行DML语句,则返回false,但是依然可以执行
* 此方法使用较少
*
* 2) int executeUpdate()
* 如果执行DQL语句,立刻报错
* 如果执行DML语句,则返回更改的记录数
*
* 3) ResultSet executeQuery()
* 如果执行DML语句,立刻报错
* 执行DQL语句返回一个结果集ResultSet,通过解析这个结果集,可以拿取封装在里面的值
*
*
* 4) int[] executeBatch()
* 执行批处理,一次执行多条SQL语句
* */
//int count = st.executeUpdate(dml1);
//System.out.println(count);
ResultSet rs = st.executeQuery(sql1);
/*
* 要进行解析之前,首先判断是否存在有效数据,如果没有有效数据,则不需要进行任何解析
不要根据ResultSet是否为null,来判断是否存在有效数据,因为ResultSet类似一个表格
存在表头,永远不为null
存在一个 boolean next() 结果集类似一个表格,默认指针指向第一行表头,当我们调用
.next()时,如果指针可以下移,返回true,如果不存在有效数据了,则指针无法下移一行,返回false
所以我们可以根据.next()方法是否返回true来判断是否存在有效数据
* */
while(rs.next()){
/*
* 拿取数据:
* get数据类型(列数)或者get数据类型(列名)
* */
/*System.out.println("ID:"+rs.getInt(1)+"\t姓名:"+rs.getString(2)
+"\t密码:"+rs.getString(3)+"\t年龄:"+rs.getInt(4)+"\t薪资:"
+rs.getDouble(5)+"\t生日:"+rs.getDate(6));*/
System.out.println("ID:"+rs.getInt("id")+"\t姓名:"
+rs.getString("name")+"\t密码:"+rs.getString("pass")
+"\t年龄:"+rs.getInt("age")+"\t薪资:"+rs.getDouble("salary")
+"\t生日:"+rs.getDate("birth"));
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
1. 注意点
- 创建执行器
- 通常有两种 Statement , PreparedStatement
返回类型 | 执行器方法 | 解释 |
---|---|---|
boolean | execute() | 如果执行DML语句则返回true 如果执行DML语句,则返回false,但是依然可以执行 此方法使用较少 |
int | executeUpdate() | 如果执行DQL语句立即报错 执行DML语句则返回受影响的记录数 |
ResultSet | executeQuery() | 如果执行DML语句立即报错 执行DQL语句返回一个结果集ResultSet,通过解析这个结果集,可以拿取封装在里面的值 |
int[] | executeBatch() | 执行批处理,一次执行多条SQL语句 |
语言 | 全称 | 包含内容 |
---|---|---|
SQL | Structured Query Language,结构化查询语言 | DDL、DML、DQL、DCL、TCL |
DDL | Data Definetion Language,数据定义语言 | create(新建)、alter(修改)、drop(删除)、truncate(截断) |
DML | Data Manipulation Language,数据操纵语言 | insert (插入)、update(修改)、delete(删除) |
DQL | Data Query Language,数据查询语言 | select(查询) |
DCL | Data Control Language数据控制语言 | grant(授权)、revoke(取消授权) |
TCL | Translation Control Language事务控制语言 | commit(提交)、rollback(回滚)、savepoint(保存还原点),只会影响DML操作。 |
- 拿取有效数据
要进行解析之前,首先判断是否存在有效数据,如果没有有效数据,则不需要进行任何解析
不要根据ResultSet是否为null,来判断是否存在有效数据,因为ResultSet类似一个表格, 存在表头,永远不为null, 存在一个 boolean next() 结果集类似一个表格,
默认指针指向第一行表头,当我们调用 rs .next() 时,如果指针可以下移,返回true,如果不存在有效数据了,则指针无法下移一行,返回false所以我们可以根据.next()方法是否返回true来判断是否存在有效数据
rs = st.executeQuery(sql);
while(rs.next()){
//TODO
}
- 拿取数据
- 可以通过 get数据类型(列数) 或者 get数据类型(列名)来拿取
Integer id = rs.getInt(1);
String name = rs,getString("name");
2.执行器的executeBatch()方法
- 首先需要关闭mysql的自动提交事务,conn.setAutoCommit(false); 出错时不会提交
- 用 addBatch(sql)方法 把要执行的SQL语句挨个放进执行器中
- 执行方法并提交
- 打开MySQL的自动提交
public class TestBatch {
public static void main(String[] args) {
try {
//获取连接
Connection con = Factory.getCon();
//关闭mysql的自动提交事务
con.setAutoCommit(false);
//获取执行器
Statement st = con.createStatement();
//设置要批处理得sql
//注意批处理不能执行dql语句
String dml1 = "insert into person values (null,'elena','12345',30,5000.00,'1990-03-01')";
String dml2 = "insert into person values (null,'damon','33345',20,6000.00,'1993-03-01')";
String dml3 = "insert into person values (null,'stefan','53345',17,4000.00,'1992-03-01')";
String dml4 = "delete from person where id = 1";
//将要执行的sql语句添加进缓存中
//这里每条sql语句都类似一个子弹,此处压入弹夹
st.addBatch(dml1);
st.addBatch(dml2);
st.addBatch(dml3);
st.addBatch(dml4);
//进行批处理操作
/*
* 返回的是一个int类型的数组,内部封装了更改的记录数
*
* */
int[] count = st.executeBatch();
//提交事务
con.commit();
//恢复自动提交
con.setAutoCommit(true);
for(int i:count){
System.out.println("更改的记录数是~~~~》"+i);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
5. 🐴Statement和PreparedStatement的区别
- 相同点
两者都为接口
public interface Statement implements Wrapper
public interface PreparedStatement implements Statement PreparedStatement是Statement的子类
- 不同点
①:Statem只能执行静态语句; PreparedStatement 可以还行IN参数的sql语句,所谓IN参数是指,sql语句可以进行字段等数据的更改
并不是一个固定的语句。②PreparedStatement存在一个强大缓存区,可以对sql语句
进行预编译,在执行相同的sql语句时,PreparedStatement
将语句加载进缓存区,仅仅编译一次,当第二次执行此语句时
不需要再次进行编译,也就是说相同的sql语句执行多条
仅仅编译一次,PreparedStatement仅对改动数据进行修改
而不再进行编译;
Statement只要语句发生了改变,则必须重新进行编译③:PreparedStatement支持对sql语句使用?占位符,从而对
sql语句进行字段参数的修改,降低了开发难度,并且从根本上杜绝了
sql注入安全隐患④如果sql语句不需要多次执行,或者?过多,则效率可能较
Statement低
第八章 DButil
- DBUtil是一个轻量级的JDBC框架,封装了原来的方法,并且可以自动释放资源,不需要手动释放资源
1. 实例
- QueryRunner 执行器,代替了Statement和PreparedStatement,封装了原来的执行器
package com.etoak.dao;
import com.etoak.factory.Factory;
import com.etoak.po.Person;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import java.sql.Connection;
import java.text.SimpleDateFormat;
import java.util.List;
public class PersonDaoImpl3 implements PersonDaoIf3 {
Connection con;
//拿取选择执行器,这个执行器封装了原先的执行器
QueryRunner qr = new QueryRunner();
//当我们使用DBUtils框架时,不需要手动释放资源了,此框架自动释放
@Override
public boolean addPerson(Person per) {
try {
String sql = "insert into person values (null,?,?,?,?,?)";
/*
* 如果执行DML语句
*
* int qr.update(con,sql,填充的占位符)
* 返回值是更改的记录数
* con:链接
* sql:带有占位符或者拼接的sql语句
* */
return qr.update(Factory.getCon(),sql
,per.getName(),per.getPass(),per.getAge(),per.getSalary(),per.getBirth())==1;
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
}
@Override
public boolean delPersonById(Integer id) {
try {
String sql = "delete from person where id = ?";
con = Factory.getCon();
return qr.update(con,sql,id)==1;
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
}
@Override
public boolean delPersonByName(String name) {
try {
String sql = "delete from person where name = ?";
con = Factory.getCon();
return qr.update(con,sql,name)>=1;
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
}
@Override
public boolean delPersonByAgeAsc(Integer age) {
try {
String sql = "delete from person where age > ?";
con = Factory.getCon();
return qr.update(con,sql,age)>=1;
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
}
@Override
public boolean multiDelById(String[] args) {
try {
String sql = "delete from person where id in (";
String sum = "";
for(int i = 0;i<args.length;i++){
sum = args[i]+","+sum;
}
sum = sum.substring(0,sum.length()-1);
sql += sum+")";
con = Factory.getCon();
return qr.update(con,sql)>=1;
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
}
@Override
public List<Person> queryAll() {
try {
String sql = "select * from person";
con = Factory.getCon();
/*
* 如果执行DQL语句
*
* qr.query(con,sql,new ResultSetHandler(),填充的占位符);
* con:链接
* sql:拼接或者带有占位符的sql语句
* ResultSetHandler():接口 封装了各种形式的结果集,根据返回值不同一般我们调用其
* 子接口
* 1)返回实体类 new BeanHandler(实体类.class)
* 2)返回封装实体类的List new BeanListHandler(实体类.class)
* 3)返回count max min avg调用的函数 new ScalarHandler()
* */
return qr.query(con,sql,new BeanListHandler<Person>(Person.class));
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
@Override
public boolean queryName(String name) {
try {
String sql = "select * from person where name = ?";
con = Factory.getCon();
/*
* 注意这里不能使用new BeanListHandler()
* 因为此接口封装了结果集,存在表头,永远不为空
* */
return qr.query(con,sql,new BeanHandler<Person>(Person.class),name)!=null;
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
}
@Override
public Person queryPerson(String name, String pass) {
try {
String sql = "select * from person where name = ? and pass = ?";
con = Factory.getCon();
return qr.query(con,sql,new BeanHandler<Person>(Person.class),name,pass);
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
@Override
public Integer queryCount() {
try {
String sql = "select count(*) from person";
con = Factory.getCon();
return Integer.parseInt(qr.query(con,sql,new ScalarHandler()).toString());
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
@Override
public List<Person> queryPage(Integer index, Integer max) {
try {
String sql = "select * from person limit ?,?";
con = Factory.getCon();
return qr.query(con,sql,new BeanListHandler<Person>(Person.class),index,max);
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
@Override
public List<Person> queryBirthByPage(String birthBegin, String birthEnd, Integer index, Integer max) {
try {
String sql = "select * from person where birth between ? and ? limit ?,?";
con = Factory.getCon();
return qr.query(con,sql,new BeanListHandler<Person>(Person.class),birthBegin,birthEnd,index,max);
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
@Override
public List<Person> queryNameLikeByPage(String args, Integer index, Integer max) {
try {
String sql = "select * from person where name like ? limit ?,?";
con = Factory.getCon();
return qr.query(con,sql,new BeanListHandler<Person>(Person.class),"%"+args+"%",index,max);
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
@Override
public List<Person> queryByPage(Person per, Integer index, Integer max) {
try {
/*
* 姓名 模糊
* 密码 模糊
* 年龄 准确
* 薪资 准确
* 生日 准确
*
* 模块化书写
* 不管结果如何必须分页,如果用户不填写任何查询条件,则全部拿取
* */
String sql = "select * from person where 1 = 1 and ";
if(per.getName()!=null){
sql += "name like '%"+per.getName()+"%' and ";
}
if(per.getPass()!=null){
sql += "pass like '%"+per.getPass()+"%' and ";
}
if(per.getAge()!=null){
sql += "age = "+per.getAge()+" and ";
}
if(per.getSalary()!=null){
sql += "salary = "+per.getSalary()+" and ";
}
if(per.getBirth()!=null){
sql += "birth = '"+new SimpleDateFormat("yyyy-MM-dd").format(per.getBirth()) +"' and ";
}
sql = sql.substring(0,sql.length()-4);
sql += " limit "+index+","+max;
return qr.query(Factory.getCon(),sql,new BeanListHandler<Person>(Person.class));
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
@Override
public boolean updatePerson(Person per) {
try {
String sql = "update person set name = ?,pass = ?,age = ?,salary = ?,birth = ? where id = ?";
con = Factory.getCon();
return qr.update(con,sql,per.getName(),per.getPass(),per.getAge()
,per.getSalary(),per.getBirth(),per.getId())==1;
} catch (Exception ex) {
ex.printStackTrace();
return false;
}
}
}
2.注意点
1. 执行DML 语句
当执行增,删,改 操作时,使用update()方法,
QueryRunner qr = new Queryunner();
public boolean addPerson(Person per){
String sql = "insert into person values(null,?,?)";
conn = Factory.getCOnn();
return qr.update(conn,sql,per.getName(),per.getPass(),);
}
2. 执行DQL语句
执行查询数据时,使用query方法
qr.query(conn,sql,new ResultSetHandler(),填充的占位符)
前两个与上边的一样
ResultSetHandler()这个接口封装了各种形势的结果集,根据返回结果不同,调用其子接口,把要获取的实体类的反射传入构造方法中
接口 | 范例 | 返回类型 |
---|---|---|
BeanHandler | new BeanHandler(实体类.class) | 实体类 |
BeanListHandler | new BeanListHandler(Person.class) | 返回封装类的集合 |
ScalarHandler | new ScalarHander() | 返回count min avg等调用函数结果 |
return qr.query(conn,sql,new BeanHandele<Person>(Person,class),per.getName()) != null;
3. 接口
接口 | 用途 |
---|---|
ArrayHandler | 将查询结果的第一行数据,保存到Object数组中 |
ArrayListHandler | 将查询的结果,每一行先封装到Object数组中,然后将数据存入List集合 |
BeanHandler | 将查询结果的第一行数据,封装到user对象 |
BeanListHandler | 将查询结果的每一行封装到user对象,然后再存入List集合 |
ColumnListHandler | 将查询结果的指定列的数据封装到List集合中 |
MapHandler | 将查询结果的第一行数据封装到map结合(key列名,value列值) |
MapListHandler | 将查询结果的每一行封装到map集合(key列名,value列值),再将map集合存入List集合 |
BeanMapHandler | 将查询结果的每一行数据,封装到User对象,再存入mao集合中(key列名,value列值) |
KeyedHandler | 将查询的结果的每一行数据,封装到map1(key列名,value列值 ),然后将map1集合(有多个)存入map2集合(只有一个) |
ScalarHandler | 封装类似count、avg、max、min、sum…函数的执行结果 |
第九章 servlet
1. Eclipse相关配置
1. 设置为Java视图
点击上放的 Window --> show view --> other -->选中 Java下边的Package Explorer 点击Open
2. 配置Tomcat
同样在 show view中选中 other,搜索Server点击Open
在Server里边会有一个蓝色的连接,双击连接,选中 tomcat8.5
点击next选中自己的tomcat的目录和 JDK 的路径,点击finish
重新在server中双击tomcat 在 Server Locations选中第二个Use
接着把Deploy Path改成 webapps
配置工程,右键tomcat选中add and remove,吧想要的工程添加进去进行了
更改html、CSS、JS等可以不用关闭Tomcat。更改Servlet一定要关闭tomcat
2. 常见HTTP错误
1.404后边存在一个路径
- 表单的action或者链接的href在提交之后没有被web.xml中的url-pattern拦截到
- 从浏览器地址书写的路径错误,路径格式为http://localhost:8080/工程名/页面
- 文件放置的位置出现问题,在eclipse下的页面资源应该放置在WebContent下
- 没有成功部署
2. 404后边没有路径
- tomcat开始失败,原因
- 文件缺失,在安装tomcat之后对tomcat进行增删
- web.xml解析异常,其中节点值必须以 / 开头 节点值必须对应
- 已经开启了一个tomcat,因为只有一个8080 端口,所以默认情况下只能开一个tomcat
3.405
Post提交覆盖doPost,GET提交覆盖doGet,或者method属性拼写错误,注意表单和链接提交默认都是GET,链接无法改动,请求转发跳转前后一致,重定向跳转之后一定是doGet
4.500
出现异常,例如空指针异常等,或者在web.xml中的servlet-class节点中无法找到Servlet实例地址。如果是异常则找第一个自己写的类。
5.常用接口
3.创建Servlet的两种方式
1.继承HttpServlet,并覆盖相应方法
1. 修改web.xml配置文件
首先要打开WEB-INF 中的 web.xml文件
此文件是整个文件中最为重要的一个文件,当打开tomcat时,tomcat会对工作空间中所有的 web.xml进行解析,如果出现解析错误,则tomcat开启失败,只要有一个工程解析错误,就无法工作
<servlet>
<!--
Step3)
此处必须对应servlet-mapping子节点中的
servlet-name节点值,如果不匹配,则无法开启tomcat
-->
<servlet-name>suibianxie</servlet-name>
<!--
Step4)
此节点值为Servlet实例的确切路径,最终找到处理用户请求的
Servlet实例,长按Ctrl或者Command键可以检查此处正确与否,
如果此处为无效地址,则500错误
Can not found class 类名 Exception
-->
<servlet-class>com.etoak.servlet.UserLogin</servlet-class>
</servlet>
<servlet-mapping>
<!--
Step2)
当拦截成功时,读取此处的节点值,到
servlet的子节点servlet-name中读取与之相同的节点值
注意如果全文没有与此节点匹配的servlet-name节点值,则
tomcat无法开启
-->
<servlet-name>suibianxie</servlet-name>
<!--
Step1)
此处为拦截路径,这里监听 表单或者链接
提交的路径,如果拦截失败,则报404错误
如果拦截成功则读取servlet-name的节点值
此处书写的格式为 /拦截路径
-->
<url-pattern>/userlog</url-pattern>
</servlet-mapping>
2.书写实现类
package com.etoak.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.etoak.dao.UserDaoIf;
import com.etoak.dao.UserDaoImpl;
import com.etoak.po.User;
/*
* 一个普通的Java类只要继承了HttpServlet抽象类,则这个类就可以称之为
* Servlet类
*
* */
public class UserLogin extends HttpServlet{
/*
* 此方法专门用来处理post提交的请求
* 没有返回值
* HttpServletRequest:接口,封装了所有提交过来的请求
* HttpServletResponse:接口,封装了所有要发送走的响应
*
* */
@Override
protected void doPost(HttpServletRequest request
, HttpServletResponse response)
throws ServletException, IOException {
/*
* Servlet默认不支持中文,所以我们需要添加中文支持,
* 以下两句为软编码,添加中文支持
* */
//设置响应的编码
/*
* response.setContentType("MIME;charset=编码");
* 注意 MIME:是专门用来提示浏览器按照何种语法解析的设置
* 此处填写了text/html表示提示浏览器根据html语法进行解析
*
* MIME存在数十种写法
* text/html:根据html语法解析
* text/css:根据css语法解析
* text/javascript:根据js语法解析
* text/plain:被浏览识别为字符串
* application/json:根据json语法解析
* image/jpeg:被浏览器识别为一张jpeg格式的图片
*
* 注意!!!此处如果书写错误
* 则浏览器提示下载文件
* */
response.setContentType("text/html;charset=utf-8");
//设置请求的编码
request.setCharacterEncoding("utf-8");
//拿取字符输出流,一般不使用字节流
PrintWriter out = response.getWriter();
//引入dao层
UserDaoIf dao = new UserDaoImpl();
//接受页面表单提交过来的用户名和密码
/*
* 注意这里 request.getParameter("这里对应页面name属性值")
*
* <input type="text" name="对应这里">
* */
String name = request.getParameter("myname");
String pass = request.getParameter("mypass");
System.out.println(name+"\t"+pass);
//使用JDBC技术调用dao层
User u = dao.queryUser(name, pass);
if(u!=null) {
//普通用户登录
if(u.getLevel().equals("0")) {
out.println("欢迎您回来"+u.getName());
out.print(u.getGender().equals("0")?"女士":"先生");
out.println("<hr />");
out.println("以下为普通用户页面结构样式...");
out.close();
return;
}
//管理员登录
if(u.getLevel().equals("1")) {
out.println("欢迎您回来管理员"+u.getName()+"<hr />");
out.println("<a href='showuser'>查看所有普通用户资料</a>");
out.close();
return;
}
//超级管理员登录
if(u.getLevel().equals("2")) {
out.println("超级管理员"+u.getName()+"您好<hr />");
out.println("<a href='showadmin'>查看所有管理员资料</a>");
out.close();
return;
}
return;
}
//动态输出页面
// request.getContextPath():拿取/工程名
// 此处组合成了一个绝对路径 /工程名/XXXX.html
out.println("数据库中查无此人,请您先<a href='"+request.getContextPath()+"/reg.html'>注册</a><hr>");
out.close();
}
}
2.创建Servlet
在servlet包下右键新建,选择other搜索servlet,点击open,创建书写Class Name,点击Next,在下方的拦截地址中改为对应的拦截地址,next,专责需要覆盖的方法
4.Servlet注意点
-
给Servlet添加中文支持
Servlet默认不支持中文,所以我们需要添加中文支持
MIME写法 含义 text/html 根据html语法解析 text/css 根据css语法解析 text/javascript 根据js语法解析 text/plain 被浏览识别为字符串 application/json 根据json语法解析 image/jpeg 被浏览器识别为一张jpeg格式的图片 //设置响应的编码 MIME存在数十种写法,上边写了几种常用的 //response.setContentType("MIME;chaeset=编码"); //此处书写错误,浏览器会提示下载文件注意 response.setContentType("text/html;charset=utf-8"); //设置请求编码 request.setCharacterEncoding("utf-8");
-
方法含义
方法 含义 request.getParameter() 从前端页面根据name获取value request.getContextPath() 拿取 /工程名 request,getParameterValues() 拿取复选框的值,返回String[] -
重定向之后不能再次提交请求问题
在下边这种情况下,重定向之后一定要加return
if(u != null){ response.sendRedirect(request.getContextPath() + "/showadmin"); //return; } response.sendRedirect(request.getContextPath() + "/showlog");
如果在重定向结束之后没有写return语句,会有500异常,Cannoot call sendRedirect() after the response has been committed
5. Servlet两种跳转方式
1.请求转发
浏览器发送请求到Servlet1,Servlet1执行请求转发之后,再讲请求转发到最终目的地Servlet2,由于不再是浏览器亲自发送到到最终目的地,所以浏览器的地址栏显示信息只是跳转前的路径,因为是同义词请求,request范围有效,跳转前是doXXX之后依旧是doXXX
其中一种
request.getRequestDispatcher(“showadmin”).forward(request, response);第一个参数为要跳转的地址
2.重定向
浏览器发送请求到Servlet1,Servlet1返回相应到浏览器,浏览器重新发送请求到Servlet2,由于不再是一次请求,所以request范围失效。由于是浏览器轻自发出请求,所以浏览器的地址栏会变为最终跳转的路径。跳转之前是doXX之后肯定为doGet
路径书写格式可以固定为绝对路径写法
response.sendRedirect("/工程名/拦截路径");
response.sendRedirect("/工程名/xxx.html");
response.sendRedirect("/工程名/xxx.jsp");response.sendRedirect(request.getContextPath() + “/showadmin”)
6. Servlet生命周期
package com.etoak.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/testlife")
public class TestLife extends HttpServlet {
private String code;
/*
* #Servlet的生命周期是怎样的?
Servlet是一个单实例多线程的Java程序
某一个Servlet ~~~~~~~》 通桑
用户 ~~~~~~~》一百多号人
* 构造方法(由于Servlet是单实例多线程的Java程序,仅仅存在一个实例
* 所以构造方法仅仅执行1次)
* |
* |
* init()(此方法称之为初始化方法,一般用来读取一些资源,拿取配置文件中的数据等等
* 类似做了一个准备工作,它也仅仅执行一次)
* |
* |
* service(此方法根据请求的多少,执行多次,一个请求对应线程分配出来的一个
* service(),这个方法会根据请求的类型调用相应的doGet()或者doPost(),service()
* 不推荐我们覆盖,如果覆盖会导致效率低下,并且无法再次自动调用doGet或者doPost)
* |
* |
* doGet||doPost(被service方法调用,根据请求是Get还是Post分配相应的doGet和doPost
* 没有返回值,专门用来书写业务逻辑,执行多次)
* |
* |
* destroy()(当tomcat关闭时,执行此方法,一般用来释放资源等等,仅仅执行一次)
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
*
* 服务器 ======》 蓝石商务中心
* 容器(不严谨的理解为tomcat) ======》 易途科技401和411
* 容器中的工程 ======》 et1912
* 每一个Servlet实例 ======》 每个同学
* 某个Servlet实例 ======》 通桑
* web.xml ======》 座次表
* 客户端 ======》 张老师
*
* */
private static final long serialVersionUID = 1L;
public TestLife() {
System.out.println("我是Servlet的构造方法~~~~~~");
}
public void init() throws ServletException {
System.out.println("我是Servlet的初始化方法init~~~~~~");
/*
* Servlet中四个最常用的接口
*
* 1)ServletConfig
* 封装某个特定Servlet实例的私有信息,书写在哪个Servlet中就只能拿取
* 当前这个Servlet自己的参数
*
* 2)ServletContext
* 封装了整个工程中所有Servlet实例共享的参数
*
* 3)HttpServletRequest
* 封装所有提交过来的请求信息
* 4)HttpServletResponse
* 封装了所有要发送走的响应信息
*
*
* */
/*
* TestLife tl = new TestLife();
* ServletConfig config = tl.getServletConfig();
* 如果不使用this,则根据以上书写
* */
//ServletConfig config = this.getServletConfig();
/*
* 使用ServletConfig也可以拿取ServletContext
* ServletContext application = config.getServletContext();
* */
//System.out.println("类名是~~~~》"+config.getServletName());
//拿取ServletContext
ServletContext application = this.getServletContext();
System.out.println("工程名是~~~~~》"+application.getContextPath());
System.out.println("拿取服务器中web服务器软件中被部署工程的绝对路径~~~~~》"+application.getRealPath("/"));
//拿取web.xml中封装的参数
System.out.println("封装的全局参数是~~~~~》"+application.getInitParameter("东京奥运会"));
//读取配置好的编码类型
code = application.getInitParameter("mycode");
}
public void destroy() {
System.out.println("我是Servlet的销毁方法destroy~~~~~~");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset="+code);
request.setCharacterEncoding(code);
PrintWriter out = response.getWriter();
//接受链接提交过来的值
String value = request.getParameter("etoak");
out.print("我是动态输出的页面,链接传递过来的值是====》"+value);
out.close();
System.out.println("我是负责业务逻辑的doGet()~~~~~~");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
}
第十章 jsp
1. Session
//拿取专门用来封装权限信息的第三范围
HttpSession(中文翻译:会话)
Servlet中两种会话跟踪机制之一,另外一种是Cookie
HttpSession被翻译为会话,用来表示与用户交互的一段时间,HttpSession通过request.getSession()来创建,可以封装权限信息.用来证明用户的身份,从而避免不必要的一些身份验证工作
request.getSession()可以创建一个新的session或者在当前存在session的前提下,直接拿取已经存在的session
默认情况下用户与服务器交互,只可能存在一个session
创建session之后可以在session中封装信息,这个信息只要能证明用户的身份 资格等就统称为权限信息
session被创建之后默认最大不活动周期为1800s,在1800s以内用户封装的权限信息可以在不同的Servlet中拿取
通过
session.setAttribute(String,Object) 来封装信息
通过
session.getAttribute(String),来拿取信息
支持中文
创建之后从1800s开始倒计时,如果没有请求提交,则倒计时一直在消耗,倒计时结束,session销毁
内部的信息无法拿取
当用户再次提交请求时,倒计时重置。所以理论上用户如果一直在操作,则session一直存在如果用户关闭浏览器,或者调用session.invalidate()则session立刻销毁
范围 | 对象 | 解释 |
---|---|---|
第一范围 | PageContext | 一般不用来传递值,跳转立刻失效,通过技术手段 可以使第一范围读取其它范围得值 |
第二范围 | HttpServletRequest | 一般用来进行一次性的值的传递,A~~》B一锤子买卖,用来封装时效性数据,比如从数据库中查询的批量数据,如果跳转为重定向,则立刻失效,应为重定向不是同一次请求,无法取出 |
第三范围 | HttpSession | 一般用来封装权限信息,不局限任何跳转方式,只要在session没销毁前就可以, 一直读取或者封装. |
第四范围 | ServletContext | 一般用来封装共享信息,全文只有一份,只要tomcat不关闭,永远有效 |
造成session失效的三种状况
- 超过最大不活动周期 默认1800s可以更改,存在40s左右的误差
- session.invalidate()立刻销毁
- 关闭浏览器
2. jsp指令
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G3FuwY0e-1595911803064)(D:\Study\Yitu\前端\Jsp\JspDaysOfficial\JspDay1\attachment\JSP语法图.jpg)]
1. include指令
使用include指令元素引入一个外部页面到本页,本页称之为主页
而引入的页面称之为辅页
file:表示引入页面的路径
注意 主页引入辅页,但是辅页不能再去引入主页,会导致无穷死循环
<%@ include file="data2.jsp"%>
2.page指令
- page 指令元素共有12中写法,有血必须书写,又是否书写不重要
序号 | 标签 | 作用 |
---|---|---|
1 | language | 当前页面的语言类型,默认java |
2 | contentType | 页面软编码的一部分,在指令元素外书写request.setCharacterEncoding(“编码”)可以补全软编码 |
3 | pageEncoding | 设置页面的转换编码,jsp~~》servlet,注意默认是iso-8859-1不能书写任何中文,保存报错,要更改为utf-8或者gbk |
4 | import | 表示本页面导包,多个包用逗号隔开,注意最精简不要少于三级包 |
5 | session | 当前页面是否支持session会话跟踪机制,默认支持,底层存在pageContext.getSession()源码,当我们打开jsp页面时session自动创建,这与html不同,html不会自动创建session |
6 | info | 用来设置页面的一段信息,嵌套在页面中,一般是作者的签名等,通过getServletInfo()可以取出 |
7 | buffer | 当前页面的缓存,多大的文件保存在本地,默认8kb |
8 | autoFlush | 可以设置当前页面自动刷新 |
9 | isELIgnored | 是否忽略EL表达式,不忽略 |
10 | errorPage | 当前页面如果出现异常自动跳转到哪个页面 |
11 | isErrorPage | 当前页面是否可以直接使用exception内置对象,默认不可以 |
12 | isThreadSafe | 当前线程是否安全,注意默认是true,单实例多线程,不安全!! |
3.jsp九个内置对象
1. request对象
-
客户端的请求信息被封装在request对象中,通过它才能了解到客户的需求,然后做出响应。它是HttpServletRequest类的实例
序号 方法 说明 1 object getAttribute(String name) 返回指定属性的属性值 2 Enumeration getAttributeNames() 返回所有可用属性名的枚举 3 String getCharacterEncoding() 返回字符编码方式 4 int getContentLength() 返回请求体的长度(以字节数) 5 String getContentType() 得到请求体的MIME类型 6 ServletInputStream getInputStream() 得到请求体中一行的二进制流 7 String getParameter(String name) 返回name指定参数的参数值 8 Enumeration getParameterNames() 返回可用参数名的枚举 9 String[] getParameterValues(String name) 返回包含参数name的所有值的数组 10 String getProtocol() 返回请求用的协议类型及版本号 11 String getScheme() 返回请求用的计划名,如:http.https及ftp等 12 String getServerName() 返回接受请求的服务器主机名 13 int getServerPort() 返回服务器接受此请求所用的端口号 14 BufferedReader getReader() 返回解码过了的请求体 15 String getRemoteAddr() 返回发送此请求的客户端IP地址 16 String getRemoteHost() 返回发送此请求的客户端主机名 17 void setAttribute(String key,Object obj) 设置属性的属性值 18 String getRealPath(String path) 返回一虚拟路径的真实路径 19 String getContextPath() 拿取/工程名
4.EL表达式
1.基本运算
${"3+2"}=${3+2} // 3+2=5
${3>2} //true
${3>2 ? true:false} //true
2.范围取值
EL表达式的四范围为pageScope,requestScope,sessionScope,applicationScope,当没有一样的键的时候,范围可以省略,当省略时,有多个范围的,省略范围的取值拿取现存范围中范围最小的。
<%
pageContext.setAttribute("elena", "elenaPage");
request.setAttribute("elena", "elenaRequest");
session.setAttribute("elena","elenaSession");
application.setAttribute("elena", "elenaApplication");
%>
<!--java代码-->
<%=pageContext.getAttribute("elena") %>
<%=request.getAttribute("elena") %>
<%=session.getAttribute("elena") %>
<%=application.getAttribute("elena") %>
<br />
<!--El表达式-->
${pageScope.elena } <br />
${requestScope.elena } <br />
${sessionScope.elena } <br />
${applicationScope.elena }
${elena} //page
3. 判断是否为空
是空返回true,不是返回false
${empty sessionScope.zhangsan}
4. 接收值
-
接收链接或者表单传递过来的值
接收值(java):<%=request.getParameter("etoak") %> <br /> <!--param必须写--> 接收值(el表达式):${param.etoak}
-
从web.xml中取值
<h3>从web.xml取值</h3> java取值:<%=application.getInitParameter("罗永浩") %> <br /> El:${initParam.罗永浩 }
5. 拿取自定义数据类型的值
可以省略范围直接通过 key.value来取值,有点类似 js
public class Game{
private Integer id;
private String name;
private Integer price;
private String seller;
//全参构造器和getset方法
}
<%
Game game = new Game(1,"动物之森",500,"任天堂");
request.setAttribute("mygame", game);
request.getRequestDispatcher("show4.jsp").forward(request, response);
%>
<h3>拿取自定义数据类型中的属性值</h3>
使用java:
游戏ID<%=((Game)request.getAttribute("mygame")).getId() %>
游戏名<%=((Game)request.getAttribute("mygame")).getName() %>
游戏售价<%=((Game)request.getAttribute("mygame")).getPrice() %>
发行商<%=((Game)request.getAttribute("mygame")).getSeller() %>
<hr />
游戏ID:${requestScope.mygame.id }
游戏名:${requestScope.mygame.name}
售价:${mygame.price }
发行商:${mygame.seller}
6. EL注意点
<% int i = 3%>
java:<%=i%> //3
EL:${i} //什么都不显示
EL是通过键来拿取值的,上边的i是值,想要拿取到i
需要添加范围随便写
request.setAttribute("i",i);
5. JSTl
-
全称Jsp Standard Tag Lib ,jsp第二代标签库,需要导入jar包
<!-- 通常用c来表示JSTL--> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
1. 赋值 <c:set>
属性 | 含义 |
---|---|
var | 键 |
value | 值 |
scope | 范围 |
- 存在种范围page,request,session,application
<c:set var="elena" value="elenaPage" scope="page"></c:set>
2. 取值<c:out>
属性 | 描述 |
---|---|
value | 要输出的信息,支持字符串直接输出,Java代码以及EL表达式 |
default | 如果value输出失败,默认显示的值 |
<c:out value="这里直接通过value输出的值"></c:out>
<c:out value="<%=str %>"></c:out>
<c:out value="${pageScope.elena}"></c:out>
<c:out value="${requestScope.elena}"></c:out>
<c:out value="${sessionScope.elena}"></c:out>
<c:out value="${applicationScope.elena}"></c:out>
<!--不知名范围的拿取现存范围中最小的-->
<c:out value="${elena}"></c:out> //page
<c:out value="${requestScope.elena}" default="删除成功!"></c:out>
3.删除<c:remove>
属性 | 描述 |
---|---|
var | 要删除的键 |
scope | 要删除的键值对的范围,注意如果不指明范围,则只要符合全部删除 |
<c:remove var="elena" scope="request" />
4.循环遍历 <c:forEach>
属性 | 描述 |
---|---|
items | 表示被遍历的循环体,可以书写Java代码,也可以从el表达式中拿取 |
var | 表示每个被遍历的对象的key值,可以随意书写,范围默认是pageContext最小范围 |
begin | 起始下标 |
end | 结束下标 |
//<c:forEach>相当于
for(Game ge:(List)request.getAttribute("mylist")){
//此处pageContext范围是标签固定范围
pageContext.setAttribute("suibianxie",ga);
}
<%
List<Game> list = new ArrayList<Game>();
list.add(new Game(1,"动物森友会",530,"任天堂"));
list.add(new Game(2,"生化危机3",440,"卡普空"));
list.add(new Game(3,"FF7Re",500,"SE"));
list.add(new Game(4,"骑马与砍杀2",300,"MB"));
list.add(new Game(5,"仁王2",390,"光荣"));
list.add(new Game(6,"十三机兵防卫圈",400,"香草社"));
request.setAttribute("mylist", list);
%>
<c:forEach items="${requestScope.mylist}" var="suibianxie" begin="1" end="3">
<tr>
<td><c:out value="${pageScope.suibianxie.id}"></c:out></td>
<td><c:out value="${suibianxie.name}"></c:out></td>
<td><c:out value="${suibianxie.price}"></c:out></td>
<td><c:out value="${suibianxie.seller}"></c:out></td>
</tr>
</c:forEach>
注意:items后边写El表达式一定要写完整,不然会找不到属性,切记切记
5.流程控制<c:choose>
相当于Java中的if else,可以不书写otherwise,但是不能不书写when.
如果互相嵌套,则c:choose必须和c:when在一起
<c:choose>
<c:when test="${3>10}">
<c:out value="输出这里了吗?"></c:out>
</c:when>
<c:otherwise>
<c:out value="最终输出了这里!!!!!"></c:out>
</c:otherwise>
</c:choose>
<c:choose>
<c:when test="${3>10}">
<c:out value="输出这里了吗?"></c:out>
</c:when>
<c:otherwise>
<c:choose>
<c:when test="${2>1}">
<c:out value="最终输出了这里!!!!!~~~~~~"></c:out>
</c:when>
</c:choose>
</c:otherwise>
</c:choose>
6.Jsp同步分页
所谓分页就是从数据库中拿取一部分的数据
1.分页的类型
- 假分页
一次将所有数据从数据库中取出,用户需要哪几条就显示哪几条
- 真分页
用户需要哪几条就从数据库中取出哪几条
2. 分页公式
select 字段 from 表 limit x,y
## x:起始索引值
## y:一共显示条
3. 分页四要素
步骤 | 要素 | 结果 | 建议变量名 |
---|---|---|---|
1 | 总记录数 | dao层获取 | allRecord |
2 | 每页记录数 | 自己设置 | pageRecord |
3 | 总页数 | (allRecord + pageRecord - 1) / pageRecord | allOage |
4 | 当前页 | 默认1 | currentPage |
分页完成后要进行下边的操作
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" import="com.etoak.dao.ComputerDaoIf,com.etoak.dao.ComputerDaoImpl,com.etoak.po.*,java.util.*" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Jsp同步分页</title>
<style>
body{
background-color:silver;
}
div#container{
width:900px;
margin:0 auto;
text-align:center;
}
table,tr,th,td{
border:solid 2px gray;
}
table{
width:100%;
text-align:center;
border-collapse:collapse;
box-shadow:15px 15px 15px silver;
}
thead{
background-color:coral;
}
tbody{
background-color:whitesmoke;
color:navy;
}
input[type=button]{
border-radius:15px;
width:60px;
background-color:pink;
}
a:hover{
cursor:pointer;
color:deeppink;
text-decoration:underline;
}
a{
color:navy;
text-decoration:none;
}
</style>
</head>
<body>
<%--
在对数据库的最基本操作中 增删查改 是基础的四门功课
分页:拿取一部分数据
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
分页的类型:
假分页
一次将所有的数据从数据库取出,用户需要哪几条就显示哪几条
真分页
用户需要哪几条就从数据库中取出哪几条
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
分页公式
select 字段 from 表 limit x,y;
x:表示起始索引值
y:表示一共显示几条记录
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
分页四要素
不管使用何种分页方式,何种技术,只要拿取了以下四个数据,则分页可得
1:总记录数 dao
2:每页记录数 自己订
3:总页数 (总记录数+每页记录数-1)/每页记录数
4:当前页 默认值是1,随着用户的操作不断的更改,是一个变量
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
分页公式结合分页四要素
select 字段 from 表 limit (当前页-1)*每页记录数,每页记录数;
List<Computer> list = dao.queryPage((当前页-1)*每页记录数,每页记录数);
--%>
<%--
<c:choose>
判断session范围中是否存在权限信息
<c:when test="${!empty u}">
--%>
<%
ComputerDaoIf dao = new ComputerDaoImpl();
//1:拿取总记录数
Integer allRecord = dao.queryCount();
//2:设置每页记录数
Integer pageRecord = 5;
//3:拿取总页数
Integer allPage = (allRecord+pageRecord-1)/pageRecord;
//4:设置当前页默认值
Integer currentPage = 1;
//接受表单提交的值
String value = request.getParameter("cup");
/*
第一次打开本页面肯定为null,从上往下执行,表单还未提交
*/
if(value!=null){
currentPage = Integer.parseInt(value);
}
List<Computer> list = dao.queryPage((currentPage-1)*pageRecord,pageRecord);
pageContext.setAttribute("mylist",list);
%>
<div id="container">
<table>
<caption>计算机售价明细</caption>
<thead>
<tr>
<th>ID</th>
<th>品名</th>
<th>售价</th>
<th>生产商</th>
<th>CPU型号</th>
<th>GPU型号</th>
<th>硬盘容量</th>
<th>内存容量</th>
<th>产地</th>
<th>发售时间</th>
<%--
只要不是平民登录
--%>
<c:if test="${u.level!='0'}">
<th colspan="3">操作</th>
</c:if>
</tr>
</thead>
<tbody>
<c:forEach items="${mylist}" var="co">
<tr>
<td><c:out value="${co.id}"></c:out></td>
<td><c:out value="${co.name}"></c:out></td>
<td><c:out value="${co.total}"></c:out></td>
<td><c:out value="${co.production}"></c:out></td>
<td><c:out value="${co.cputype}"></c:out></td>
<td><c:out value="${co.gputype}"></c:out></td>
<td><c:out value="${co.disktype}"></c:out></td>
<td><c:out value="${co.ramtype}"></c:out></td>
<td><c:out value="${co.location}"></c:out></td>
<td><c:out value="${co.sellertime}"></c:out></td>
<c:if test="${u.level!='0'}">
<td><a href="delcomputer?id=${co.id}&info=fake">假删除</a></td>
<td><a href="delcomputer?id=${co.id}&info=true">真删除</a></td>
<td><a href="computer_update.jsp?id=${co.id}">编辑</a></td>
</c:if>
</tr>
</c:forEach>
</tbody>
</table>
<br />
<form action="computer_show.jsp">
<input type="hidden" name="cup" />
<input type="button" value="首页" οnclick="change(1)"
<%=currentPage==1?"disabled":"" %> />
<input type="button" value="上一页" οnclick="change(<%=currentPage-1 %>)"
<%=currentPage==1?"disabled":"" %> />
<input type="button" value="下一页" οnclick="change(<%=currentPage+1 %>)"
<%=currentPage==allPage?"disabled":"" %> />
<input type="button" value="末页" οnclick="change(<%=allPage %>)"
<%=currentPage==allPage?"disabled":"" %> />
</form>
<a href="computerquit">用户登出</a>
<%--
1)普通用户只能看到用户登出链接
2)管理员可以看到查看全部普通用户资料链接,点击之后
跳转到computer_usershow.jsp页面,此页面存在一个表格,内部是所有
普通用户资料。实现分页功能,实现真删除,假删除,编辑功能,
提升权限(普通用户=》管理员)
3)超级管理员可以看到查看全部管理员用户资料链接,点击之后
跳转到computer_adminshow.jsp页面,此页面存在一个表格,内部是所有
管理员资料。实现分页,假删除,编辑功能,降低权限(管理员=》普通用户)
要求:Jsp页面使用EL表达式和JSTL标签,尽量使用Servlet调用dao层
(分页页面可以直接在Jsp页面调用dao层)
所有页面必须使用权限控制,非登录人员登入直接跳转到登录页
dao层谨慎书写,如果之前已经写过,可以不再书写,工厂,实体类等不需要再次
书写
--%>
</div>
<script>
/*
此处传递过来的形参就是点击按钮之后当前页的值
*/
function change(cr){
/*
document.forms:表示拿取全文中所有的表单,由于这里只有一个表单
所以添加[0]表示拿取的就是全文中的唯一表单
.cup.value=cr
表示将隐藏域的值赋值成cr也就是当前页的值
*/
document.forms[0].cup.value = cr
//使用js提交表单
document.forms[0].submit()
}
</script>
<%-- </c:when> --%>
<%-- <c:otherwise> --%>
<%--
此处是放置不经过登录直接在浏览器地址栏输入此页面,逃避登录步骤
由于没有从session中无法获得权限信息,所以进显示以下内容
--%>
<!-- <p>您还没有登录,请您先登录</p><hr /> -->
<%-- </c:otherwise> --%>
<%-- </c:choose> --%>
</body>
</html>
7. 过滤器[filter]
Servlet
> Filter在Servlet中可以设置一个或者多个过滤器,这些过滤器起到过滤的作用,在Servlet执行doGet()或者执行doPost()
> 之前过滤器会挡在之前首先执行过滤工作,例如可以设置编码,可以查看权限
> init():在打开tomcat时,整个工程中所有的过滤器全部启动,仅仅执行一次
> doFilter():当拦截的Servlet要执行之前支持过滤器的doFilter(),注意在doFilter()中存在
> 一个chain.doFilter()方法,如果过滤器过滤结束,则支持此方法之后继续执行Servlet实例
> 而如果过滤失败,不支持chain.doFilter()则后面的Servlet不再执行了
> destroy():关闭tomcat时执行
>
> 多个过滤器:
> 打开tomcat时:
> Servlet的init()
> 过滤器1的init()
> 过滤器2的init()
请求发送时:
过滤器1的doFilter()执行到chain.doFilter(request,response)时
开始执行
过滤器2的doFilter()执行到chain.doFilter(request,response)时
开始执行
Servlet的doGet()||doPost()
之后执行
过滤器2的chain.doFilter(request,response)之后的代码
过滤器1的chain.doFilter(request,response)之后的代码
这叫做过滤链
过滤器可以配置在web.xml中
或者使用Servlet3.0中注解直接配置在Filter类中
<filter>
<filter-name>suibianxie</filter-name>
<filter-class>监听器的类的地址</filter-class>
<init-param>
<param-name>私有参数名</param-name>
<param-value>私有参数值</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>suibianxie</filter-name>
<url-pattern>拦截的Servlet或者jsp路径</url-pattern>
</filter-mapping>
过滤顺序:
如果配置在web.xml中
注意过滤顺序是web.xml中从上到下过滤
注意servlet一定放置在最后
如果使用Servlet3.0注解
官方至今没有提供任何设置顺序的解决方案,API文档中没有任何方法可以设置顺序
非官方提出的说法按照过滤器字符排列顺序,例如
A.java肯定在B.java之前执行
FilterA.java肯定在FilterB.java之前执行
Filter0Etoak.java肯定在Filter1Etoak.java之前执行
未经过官方认定
1. 设置编码的
package com.etoak.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
/*
* urlPatterns="这里表示要拦截的请求"
* 注意如果设置为 /* 则表示拦截所有请求
* initParams={
* @WebInitParam(name="表示这个过滤器的键",value="表示这个过滤器的值"),
* @WebInitParam(name="表示这个过滤器的键",value="表示这个过滤器的值"),
* }
* 在WebInitParam中部署的私有参数只有本类可以拿取
* 可以通过以下几种手段拿取
* ServletConfig config = this.getServletConfig();
* String value = config.getInitParameter("键");
*
* 或者通过过滤器中的参数FilterConfig来拿取
*
* */
@WebFilter(urlPatterns="/*",initParams={
@WebInitParam(name="encode",value="utf-8"),
@WebInitParam(name="thisiskey", value="thisisvalue")
})
//urlPatterns={"/UserManagerServlet","/index.jsp"}
public class Filter1Encoding implements Filter {
private String code;
public void destroy() {
System.out.println("编码过滤器的destroy~~~");
}
/*
* ServletRequest和ServletResponse是
* HttpServletRequest与HttpServletResponse的父接口
*
* 注意部分方法无法直接调用
* 重定向和请求转发无法直接使用,需要通过多态等进行转换
* */
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("编码过滤器开始过滤啦!!~~~");
request.setCharacterEncoding(code);
chain.doFilter(request, response);
}
//当打开tomcat时直接执行此方法
public void init(FilterConfig fConfig) throws ServletException {
System.out.println("编码过滤器启动啦!");
code = fConfig.getInitParameter("encode");
System.out.println(code);
}
}
2. 设置权限的
package com.etoak.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.etoak.po.User;
@WebFilter(urlPatterns= {"/delcomputer","/computerquit","/computerupdate","/computer_show.jsp","/computer_update.jsp"})
public class Filter2Authority implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("权限过滤器开始过滤啦!!~~~");
HttpServletRequest req = (HttpServletRequest)request;
HttpSession session = req.getSession();
User u = (User)session.getAttribute("u");
if(u==null) {
HttpServletResponse res = (HttpServletResponse)response;
res.sendRedirect(req.getContextPath()+"/computer_log.jsp");
return;
}
chain.doFilter(request, response);
}
public void init(FilterConfig fConfig) throws ServletException {
System.out.println("权限过滤器启动啦!");
}
}
3. 注意点
- urlPatterns=“这里表示要拦截的请求”
- 注意如果设置为 /* 则表示拦截所有请求
- initParams={
- @WebInitParam(name=“表示这个过滤器的键”,value=“表示这个过滤器的值”),
- @WebInitParam(name=“表示这个过滤器的键”,value=“表示这个过滤器的值”),
- }
- 在WebInitParam中部署的私有参数只有本类可以拿取
- 可以通过以下几种手段拿取
- ServletConfig config = this.getServletConfig();
- String value = config.getInitParameter(“键”);
- 或者通过过滤器中的参数FilterConfig来拿取
- */
第十一章 AJAX和JSON
1.AJAX
Asynchronous JavaScript and XML
异步的js和xml
1. 创建异步请求的步骤
-
创建异步请求
let request //1)此函数用来创建异步请求 function create(){ //创建一个异步请求 /* * 这种创建方式是level2版本的创建方式,如果要针对ie6 7 8 等老旧浏览器 * 则应该使用level1版本 * * if(window.XMLHttpRequest){ * request = new XMLHttpRequest() * }else{ * request = new ActiveXObject("Microsoft.XMLHttp") * } * * 同步请求: * HttpServletRequest * ServletRequest * */ request = new XMLHttpRequest() }
-
创建函数用来发送请求
//2)此函数用来给服务器发送请求,一般称之为主函数(非官方) function checkName(value){ //A:创建异步请求 create() //B:给服务器发送异步请求 /* * request.open(type,url,async) * * url:表示发送异步请求的目的地(拦截地址) * 如果是get,则从此处传递值 * 本例: * 'check?key=value&key2=value2' * type:请求类型 post get * async:表示是否使用异步,默认true使用,更改为false则使用同步,当响应返回之前 * 浏览器锁死,用户无法进行任何操作 * */ request.open('post','check',true) //C:如果使用POST则需要书写此句,表示使用字符流进行值的传递,GET自动字符流不需要书写 request.setRequestHeader('Content-Type','application/x-www-form-urlencoded') //D:声明回调函数 //注意这里函数名可以随意书写,但是没有括号,如果存在括号则表示调用,没有括号表示声明 //调用立刻执行,而声名需要前面的条件满足才可以 request.onreadystatechange = callback //E:设置传递的参数,注意POST从此处传递值,GET不从此处传值,也必须书写,填写null //request.send('etoak='+value) request.send(`etoak=${value}`) }
-
书写回调函数接受响应信息
readyState状态
readyState值 状态 补充描述 0 链接尚未初始化 调用request.oprn() 1 链接初始化 请求已经准备好 2 请求正在处理 服务器正在处理请求时会做出一个相应,readyState=2时,响应收不提供一个关于响应的信息,并提供一个状态码 3 得到服务器的响应 这个阶段数据下载到请求对象,但是响应数据还没有准备好,不能使用 4 服务器响应准备就绪 现在服务器处理完请求数据可以使用了 status状态
状态码 描述 1xx 消息 2xx 成功 3xx 重定向 4xx 请求错误404 405 5xx 服务端异常 500 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rR6uvpoc-1595911803067)(D:\Study\Yitu\前端\AJAX\AjaxDaysOfficial\attachment\ajax回调函数.jpg)]
//3)回调函数,接受服务器处理完的响应信息 function callback(){ //首先判断服务器发送数据是否完整 if(request.readyState==4){ //完整的返回的数据没有报错 if(request.status==200){ //接受返回的字符串 let value = request.responseText //拿取span元素 let nodeSpan = document.getElementById('name_msg') //拿取submit表单元素 let nodeSub = document.getElementById('sub') if(value=='bingo'){ //设置错误提示信息 nodeSpan.innerHTML = '<img src="image/wrong.png">用户姓名已经被占用' //设置样式 nodeSpan.style.color = 'red' //禁用提交按钮 nodeSub.disabled = true return; } nodeSpan.innerHTML = '<img src="image/right.png">用户姓名可以使用' nodeSpan.style.color = 'green' nodeSub.disabled = false } } }
@WebServlet("/check")
public class CheckName extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* Ajax支持三种数据的输出
* 1)字符串 text/plain
* 2)xml text/xml
* 3)json application/json
* 所以在这里不管怎么书写也不能书写text/html,因为根本不支持html
* */
response.setContentType("text/plain;charset=utf-8");
request.setCharacterEncoding("utf-8");
//拿取要输出的字符流
PrintWriter out = response.getWriter();
//接受提交过来的用户名
String name = request.getParameter("etoak");
//模拟数据库查询
if(name.equalsIgnoreCase("elena")){
/*
* 返回字符串
* out.print(要返回的数据)
* 只支持 字符串 json xml
* 注意不能使用println()否则会自动添加\n
* */
out.print("bingo");
out.close();
return;
}
out.print("suc");
out.close();
}
注意点
- servlet只能书写一下三种MIME标签
Ajax支持三种数据的输出
1)字符串 text/plain
2)xml text/xml
3)json application/json
所以在这里不管怎么书写也不能书写text/html,因为根本不支持html
- 给POST请求设置编码
request.setRequestHeader(‘Content-Type’,‘application/x-www-form-urlencoded’)
如果写错了会有500错误,提示response.send()这里报错,如果这里出错就找设置编码的
2. JSON
第十二章 Jquery
第十三章 LayUI
第十四章 VUE
其他 IDE的使用
1. Eclipse
2. VS Code
3. 颜色
- #F8F8F8 ,这里边的表格那种灰色
- #B0C4DE 适合表头,淡刚蓝
- #DCDCDC 亮灰色 Gainsboro
- #d8d8d8 灰色 与白色对应明显