Javaweb 第7天 Servlet课程
Servlet课程
三日大纲
● 网络概念,专业术语
● Tomcat使用,发布网站,使用Myeclispe发布网站(搭建环境)
● 编写Servlet,Servlet生命周期
● 用户注册,显示所有用户,显示单个用户
● Request详解,Response详解
● 转发和重定向
● ServletConfig,ServletContext
● Session
● 用户登录
● Cookies
● 自动登录
*************************************************************************************************
1. Java Web开发介绍
1.1 JavaWeb概念
Java Web,是用Java技术来解决相关web互联网领域的技术总和。web包括:web服务器和web客户端两部分。Java在客户端的应用有java applet,不过使用得很少,Java在服务器端的应用非常的丰富,比如Servlet,JSP和第三方框架等等。Java技术对Web领域的发展注入了强大的动力。
JavaWeb开发就是指用Java语言来做网站。
注:java applet 是指将java桌面应用程序(awt)搬到网页(html)上运行,类似于flash(flex),但是由于需要用户安装jre,又被微软打压,所以没有用户愿意使用,也没有公司去开发了。
1.2 网络相关概念及术语介绍
1.2.1 IP地址
用来标识计算机在网络上的一个位置。
最大界限和最小界限:255.255.255.255 0.0.0.0
外网:115.239.210.27 (61.135.169.125) 百度的外网ip地址
内网:在同一局域网的一个ip地址,192.168.1.101
10.0.0.6,网关:192.168.1.1 或者 10.0.0.1
1.2.2 ping命令
用来测试连接 ping 192.168.1.1
万能本机地址:127.0.0.1
1.2.3 ipconfig命令
简单查看本机IP地址 ipconfig
查看本机IP MAC(物理地址) ipconfig –all
1.2.4 域名
用来绑定某个IP地址,方便用户记忆。
一个域名只能绑定一个IP地址
一个IP地址被多个域名所绑定
万能本机地址:localhost
1.2.5 DNS
域名解析就是通过域名获得ip地址。
域名解析服务器:它的ip往往是固定比如 114.114.114.114 8.8.8.8
此服务器的作用是用来把你传来的域名和ip相对应,从而让你能够通过域名访问到那台IP的机器。
钓鱼网站的原理
1、低端钓鱼:taobao.com taobaq.com
2、高端钓鱼:taobao.com taobao.com
本机hosts文件(内置的dns解析文件)
C:\Windows\System32\drivers\etc\hosts
1.2.6 端口
是用来标识一台机器上的某个应用程序
如果不写端口,那么就是80
3306 Mysql的常用端口
1.2.7 协议
为了相互访问而互相约定好的规则
http:// 超文本
https:// 安全的http协议
Jdbc:mysql:// Mysql的数据库协议
ftp:// 文件传输协议
thunder:// 迅雷
Qvod://
1.2.8 服务器
一台电脑,用来接收客户端或者浏览器传送过来的数据,服务器本身是不能接收的,需要靠它里面的程序去完成此功能。那么这个程序就成为Web服务器软件
1.2.9 Web服务器软件
1、必须要有端口,哪怕端口是80。
2、程序本身必须是一个死循环。
3、它要具有监听功能。
Tomcat就是一个Web服务器软件:
有端口,死循环,有监听
它就是一个java专用web服务器软件。
常用的web服务器软件:
IIS(Microsoft Windows) :asp.net的专用web服务器、收费
apache(apache软件基金协会):php的专用web服务器、免费,但是可以做web服务器分布式集群负载均衡的前端服务器。(以前)
nginx(来自俄罗斯):强大的静态网页web或邮件服务器,但是可以做web服务器分布式集群负载均衡的前端服务器。(现在)
Java Web开发的web服务器:
Weblogic(Oracle):收费
WebSphere(IBM):收费
JBOSS(开源):免费
Tomcat(apache):免费、小巧、轻量级
Resin:免费,新版本兼容jsp和php
Jetty:免费、小巧、更加轻量级、可以嵌入到程序中
......
1.2.10 请求和响应
请求(Request):从客户端发送数据到服务器
响应(Response):从服务器端发送数据到客户端
在B/S程序中:
客户端指的就是浏览器。
服务器端指的是Web服务器。
1.2.11 静态网站和动态网站
静态网站:网页上的内容都是写死在上面,不会发生变化,也不会记录用户数据。相关技术:html、css、js。
动态网站:可以利用服务器端的程序随时在网页上生成新的内容,并且能够记录用户数据。相关技术:servlet、jsp、asp、asp.net、php等。
动态网站最终会生成成静态网站,供用户来访问,但因为是由程序来控制,每次生成的内容可能都不一样。
1.2.12 URL和URI
URI是统一资源标识符,是一个用于标识某一资源名称的字符串。
例:file://a:1234/b/c/d.txt
URL是统一资源定位,是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。
例:百度URL即是http://www.baidu.com。
URL是URI的子集。
URI用来标识一个资源。
URL用来标识互联网上的一个资源。
*************************************************************************************************
2. Tomcat入门
2.1 Tomcat简介
开源小型web服务器,完全免费,"主要用于中小型web项目",只支持Servlet和JSP 等少量javaee规范,Apache公司jakarta 一个子项目。
2.2 Tomcat下载
http://tomcat.apache.org/download-70.cgi
2.3 启动Tomcat
2.3.1 配置JAVA_HOME环境变量
Tomcat 是java写的,需要java环境才能运行。
JAVA_HOME必须进行配置,并且指向jdk的安装目录(jre不可以)
2.3.2 启动Tomcat
运行
tomcat/bin/startup.bat(window批处理文件)
window批处理文件可以看作是一堆DOS命令的集合,里面也有顺序、选择、循环等操作,很像是一种编程语言。
找到startup.bat 双击运行。会有一个黑窗口,黑窗口不要关闭。(如果关闭,相当于把tomcat停止了。)
2.3.3 访问Tomcat管理首页
在浏览器地址栏中输入:http://localhost:8080
如果看到如下页面,证明启动成功
2.3.4 错误情况
、JAVA_HOME 没有配置正确
窗口一闪就消失。在startup.bat 文件内容最后添加pause
右键startup.bat 选择编辑。在最后面加上pause;
或者在DOS界面中手动通过命令运行startup.bat也可以看到错误。
、启动tomcat发现端口被占用
查看tomcat/logs/Catalina.out
记录,查看tomcat启动失败信息。
将占用8080端口的程序关掉即可。目前除了tomcat,一般大部分软件都不会占用8080端口,但是要小心迅雷、qvod等p2p程序。
DOS命令:netstat -ano|findstr "8080"
查看8080端口被谁占用
DOS命名:tasklist|findstr "5584"
根据pid查找程序名称
然后想怎么做就怎么做..
....
*************************************************************************************************
3. Tomcat使用
3.1 Tomcat目录
bin目录,启动和关闭
logs目录,日志目录
webapps目录:web项目部署之后的目录
work目录:jsp的一些文件放到work目录下
*************************************************************************************************
3.2 部署静态网站
网站一般都部署在Tomcat7\webapps目录下。
3.2.1 根目录网站(ROOT)
Tomcat7\webapps\ROOT:表示网站的根目录,也就是用户在访问网站的时候只需要输入http协议、域名、端口即可。不需要输入其它任何目录。
Tomcat的管理界面本身就是一个网站,而且是放在ROOT目录下面。
首页其实就是index.jsp index.jsp 作为首页访问的时候可以省略不写。
我们完全可以添加自己的静态页面
比如添加一张hello.html
在浏览器地址栏中输入:http://localhost:8080/hello.html
甚至可以将hello.html改成index.html
一般index.html的省略不写优先级比index.jsp还要高
*************************************************************************************************
3.2.2 自定义目录网站
1、新建目录放置Tomcat7\webapps目录下
2、在此目录中放入html文件
3、重启tomcat
4、访问http://localhost:8080/mao/index.html
这样,一个Tomcat就可以部署很多很多的网站。
*************************************************************************************************
3.2.3 war压缩包部署方式
此方式与3.2.1和3.2.2方式一样,只不过项目文件被打包成单独的一个war格式文件(其实就是zip形式的压缩)。
需要注意的是:一定要在该项目文件的根目录之下进行打包压缩,不能包括项目根目录。
将打包好的项目文件的扩展名改为war,并放入webapps目录中,以文件名为路径名配合浏览器进行访问。
启动Tomcat时候,会将war文件进行解压。
*************************************************************************************************
3.2.4 配置虚拟主机(了解)
在conf/server.xml文件的host元素中配置,例如:
在host标签内书写如下内容
<Context path="/maoyun" docBase="D://taobao"/>
<Context path="浏览器要访问的目录---虚拟目录" docBase="网站所在磁盘目录"/>
配置好之后,要重启服务器。
浏览器访问:http://localhost/maoyun/xxx.html
*************************************************************************************************
3.2.5 配置conf/Catalina/localhost/xxx.xml(了解)
1、如果在tomcat的conf目录下没有\Catalina\localhost,可以自己去建立文件夹。
2、在conf/Catalina/localhost 文件夹下面,去新建一个xml。xml的名字就是我们要访问的路径的名字。比如mao.xml
3、在mao.xml中写入:<Context docBase="d://taobao"/>
浏览器访问:http://localhost/mao/xxx.html
*************************************************************************************************
3.3 修改端口
(访问端口),这个默认是最初自带的意思
,这个默认是指可以省略不写
如果访问的时候
输入http://www.baidu.com
相当于http://www.baidu.com:80
真正在项目上线之后,通常采用80.这样用户访问的时候直接输入域名,不用输入端口
在tomcat/conf/server.xml
找到
<Connector port="" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
修改port的值,比如改成80
然后重新启动tomcat。
访问http://localhost
*************************************************************************************************
4. MyEclispe发布网站
4.1 MyEclispe配置JDK
*************************************************************************************************
4.2 MyEclispe配置Tomcat
1、打开myeclispeweb服务器配置界面
2、为Tomcat配置jdk
3、配置tomcat程序的目录
*************************************************************************************************
4.3 MyEclispe启动Tomcat
注意:一定要关闭之前bat方式打开的那个Tomcat,不然端口就被占用了。
成功的话:
红灯会一直亮着,证明Tomcat程序是个死循环,一直在监听着浏览器是否会访问它。
*************************************************************************************************
4.4 MyEclispe部署静态网站
部署前一定要先关闭Tomcat
完成部署后,会发现项目WebRoot中的内容已经复制到tomcat的webapps中,并以项目名称命名。
在MyEclispe中启动Tomcat:
切记:部署前一定要先关闭Tomcat
*************************************************************************************************
4.5 MyEclispe指定名称部署
可以更改每次部署到Tomcat/webapps的目录名称,默认是项目名称,但不一定非要是项目名称,甚至是ROOT都可以。
再次部署时候最好先删掉原有部署
然后再重新Add部署
最后启动Tomcat并访问
因为更改为ROOT,所以在访问的时候浏览器地址栏的url中并不需要填写二级目录
*************************************************************************************************
5. Servlet入门
5.1 Servlet介绍
5.1.1 为什么会有Servlet
Servlet是用Java代码编写的服务器端软件程序,用于处理客户机和服务器之间的消息传递。
简单来说:Servlet其实就只是Sun公司定制的一个规范。
在Web应用中,Web服务器负责监听浏览器的访问,浏览器一旦输入url地址,Web服务器便会有反应。但是要进行后续业务操作,则需要Web服务器来调用程序员自己写的方法。
程序员其实只要将此方法所在的类实现Web服务器事先调用的某个接口,并配置该类的地址告知Web服务器,Web服务器就可以在运行时候动态实例化类,并调用程序员自己写的方法。
但是有如下问题:
1、接口需要事先明确指定。
2、方法名需要事先明确指定。
Sun公司就指定了这样的一个标准,其实就是定义了接口名称和方法名。这样所有的Web服务器软件都会去遵循这个规则。那么程序员写的Java Web开发的代码就可以适用于各种Web服务器软件。
总的来说:
在整个Java Web开发过程中,程序员负责写实现代码,Tomcat负责监听浏览器,并调用程序员写的代码,Sun公司负责定制调用的规范。
对于程序员来说,要做的事情是:
1、实现Sun公司定制的规范中的实现接口,或者继承父抽象类。
2、实现Sun公司定制的规范中的方法名。
3、在该方法下,实现业务逻辑代码。
4、通过配置文件,告知Tomcat实现类的名称。
*************************************************************************************************
5.1.2 Web项目目录结构简介
*************************************************************************************************
5.2 编写Servlet
5.2.1 继承HttpServlet抽象类
package cn.itcast.web;
import javax.servlet.http.HttpServlet;
/**
* @author Administrator
* 自定义实现的Servlet规范的类
* 该类需要继承HttpServlet
*/
public
class
Test1
extends
HttpServlet{
}
*************************************************************************************************
5.2.2 实现service方法
public
class
Test1
extends HttpServlet{
/**
* 实现HttpServlet抽象类中的service方法,参数有Http的这个
* service方法有两个,参数前面带Http的方法更具一些,专门针对Http业务
*/
@Override
protected
void
service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
}
}
*************************************************************************************************
5.2.3 实现业务逻辑代码
protected
void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//可以在这里面编写我们的业务逻辑
System.out.println("这是第一个Servlet程序");
}
*************************************************************************************************
5.2.4 配置web.xml
在WebRoot/WEB-INF/web.xml中配置servlet的访问路径,并告知Tomcat类名。
<?xml
version="1.0"
encoding="UTF-8"?>
<web-app
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID"
version="3.0">
<!-- 定义servlet-name 并关联到
servlet-class-->
<servlet>
<servlet-name>test1Name</servlet-name>
<servlet-class>cn.itcast.web.Test1</servlet-class>
</servlet>
<!-- 配置url-pattern并关联到servlet-name -->
<servlet-mapping>
<servlet-name>test1Name</servlet-name>
<url-pattern>/test1.do</url-pattern>
</servlet-mapping>
<!-- 注意:url-pattern就是Tomcat监听浏览器访问的相对路径,
当浏览器对此进行访问,根据servlet-name 的对应就可以找到
servlet-class -->
</web-app>
*************************************************************************************************
5.2.5 部署项目并访问运行
*************************************************************************************************
5.3 执行流程
*************************************************************************************************
5.4 Servlet生命周期
servlet的生命周期其实就是:
什么时候创建一个servlet?
什么时候去销毁一个servlet?
并且在创建和销毁的时候,我们程序员能否知道,能做些什么。
提供的实现方法:
init和destroy:用于管理Servlet的生命周期
service方法:用于实现程序员的业务逻辑
5.4.1 init方法
当我们第一次去访问一个servlet的时候,服务器会去创建这个servlet对象。并且只会创建一次。
而当服务器创建一个serlvet的时候,会去调用init方法。
我们可以实现init方法完成一些单次并初始化的操作:
/**
* 创建servlet的时候会去调用此方法
*/
@Override
public
void init() throws ServletException {
System.out.println(this+" 这个Servlet创建了");
}
当首次访问时,会创建servlet,然后在创建servlet的时候,tomcat会调用init方法。
当再次访问的时候,不会创建servlet,所以init方法只执行一次。
如果配置了load-on-startup
表示服务器启动的时候创建servlet。
<!-- 定义servlet-name 并关联到
servlet-class-->
<servlet>
<servlet-name>test1Name</servlet-name>
<servlet-class>cn.itcast.web.Test1</servlet-class>
<!-- 启动web服务器就创建本servlet,数字表示创建的顺序 -->
<load-on-startup>1</load-on-startup>
</servlet>
5.4.2 service方法
客户端每一次请求,tomcat都会去调用servcie方法。处理用户的请求。并且给其响应。
每一次请求都会调用servcie方法。
/**
* 实现HttpServlet抽象类中的service方法,参数有Http的这个
* service方法有两个,参数前面带Http的方法更具一些,专门针对Http业务
*/
@Override
protected
void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//可以在这里面编写我们的业务逻辑
System.out.println("这是第一个Servlet程序");
}
5.4.3 destroy 方法
当服务器销毁一个servlet的时候,会调用里面的destory方法。
/**
* 销毁servlet时调用
*/
@Override
public
void destroy() {
System.out.println(this+" 这个servlet被销毁了");
}
*************************************************************************************************
6. Web网站访问方式
6.1 直接在地址栏访问
直接在浏览器中输入地址,或者通过超链接点击。
6.1.1 访问静态资源
静态资源包括:html,xml,css,js,jpg,gif,mp3,wav,doc,pdf ......等。
确认静态资源在Tomcat/webapps中的位置(只有在项目WebRoot目录中的内容才会部署到Tomcat/webapps中),浏览器直接定位访问即可。
其它静态内容,浏览器可能有时候会提示下载。
6.1.2 访问Servlet
<!-- 配置url-pattern并关联到servlet-name -->
<servlet-mapping>
<servlet-name>testName</servlet-name>
<url-pattern>/test1.do</url-pattern>
</servlet-mapping>
<!-- 注意:url-pattern就是Tomcat监听浏览器访问的相对路径,
当浏览器对此进行访问,根据servlet-name 的对应就可以找到
servlet-class -->
最终的访问路径是:
http://ip地址或域名/项目名称/url-pattern的名字
注意:url-pattern配置里面需要加 /
6.1.3 url-pattern
.url-pattern有三种写法:
1、精确匹配。以"/"开头。
<url-pattern>/test1.do</url-pattern>
用户在浏览器中输入了指定的/test1.do才能访问此servlet。
注意扩展名可以写,也可以不写。
2、全路径匹配。以"/"开头,加上通配符"*"。
<url-pattern>/*</url-pattern>
浏览器输入到项目目录中的任何地址,都会访问此servlet
3、扩展名匹配。以通配符"*"开头,加上扩展名。
<url-pattern>*.action</url-pattern>
浏览器输入到项目目录中的任何扩展名为action的地址,都会访问此servlet
6.1.4 404错误
当浏览器中输入的地址访问不到资源的时候(错误。
解决方案:输入正确的地址。
错误的页面,变成友好性的提示。
比如下面的淘宝页面:
错误时,跳转到404.html页面 -->
<error-page>
<error-code>404</error-code>
<location>/404.html</location>
</error-page>
*************************************************************************************************
6.2 表单提交方式
通过表单提交到指定的servlet。在form
的
action属性中填写servlet的地址
<form
action="test1.do">
用户名:<input
type="text"
name="username"/><br/>
<input
type="submit"
value="注册"
/>
</form>
表单提交默认是get方式。
我们也可以改成post方式。
<form
action="test1.do"
method="post">
用户名:<input
type="text"
name="username"
/><br/>
<input
type="submit"
value="注册"
/>
</form>
get的方式和post方式表面区别:
get的方式是把数据在地址栏中明文的形式发送
post则不是,而且post可以传递的数据比get多
*************************************************************************************************
7. Request和Response入门
7.1 接收请求数据
7.1.1 post方式
<form
action="test1.do"
method="post">
用户名:<input
type="text"
name="username"
/><br/>
<input
type="submit"
value="注册"
/>
</form>
service(HttpServletRequest req, HttpServletResponse resp)方法中有两个参数,一个是请求对象,一个是响应对象。
服务器接收数据属于客户端发送信息到服务器,也就是请求,需要用到请求对象。
protected
void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//可以在这里面编写我们的业务逻辑
System.out.println("这是第一个Servlet程序");
//接收表单传来的数据,getParameter中的参数与input标签中的name一致
String username = req.getParameter("username");
System.out.println(username);
}
注意:
、req.getParameter方法必返回字符串
、getParameter中的参数名称与input标签中的name一致
、Java Web接收表单元素的值一定是通过标签的name,JavaScript获得表单元素的值通常是通过id,也可以通过name
7.1.2 get方式
<form
action="test1.do"
method="get">
用户名:<input
type="text"
name="username"
/><br/>
<input
type="submit"
value="注册"
/>
</form>
protected
void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//可以在这里面编写我们的业务逻辑
System.out.println("这是第一个Servlet程序");
//接收表单传来的数据,getParameter中的参数与input标签中的name一致
String username = req.getParameter("username");
System.out.println(username);
}
表单的get方式提交在接收的时候,servlet代码和post方式接收完全一模一样。
既然get方式显示的展现传输数据,我们可以直接在浏览器上更改url上参数的名称来传递不同的值。
另一方面说明:浏览器直接输入地址、以及超链接都是属于get方式访问。
*************************************************************************************************
7.2 响应数据到浏览器
服务器响应数据到浏览器通俗的说就是服务器传回信息到客户端,其实就是在浏览器上显示信息(页面上打印信息)。
服务器返回数据到浏览器属于响应,需要用到响应对象。
protected
void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//可以在这里面编写我们的业务逻辑
System.out.println("这是第一个Servlet程序");
//接收表单传来的数据,getParameter中的参数与input标签中的name一致
String username = req.getParameter("username");
System.out.println(username);
//设置响应内容类型
resp.setContentType("text/html");
//设置编码格式
resp.setCharacterEncoding("UTF-8");
//通过响应对象,获得打印输出对象
PrintWriter out = resp.getWriter();
//向浏览器页面打印信息
out.print("恭喜"+username+"注册成功");
out.print("<br/>");
out.print("<a href='register.html'>点击返回注册页面</a>");
}
1、out.println()与out.print()的区别
out.println()表示输出的内容后面会带一个换行,但是注意,浏览器识别换行的关键点是看<br/>标签,所以遇到换行建议输出<br/>标签。
2、out.print()与out.write()的区别
out.print()将参数内容以字符串形式输出。
out.write()将参数内容以原有类型格式输出。
*************************************************************************************************
7.3 POST请求与GET请求
get方式和post方式表面区别:
get方式是把数据在地址栏中明文的形式发送
post则不是,而且post可以传递的数据比get多
以上只是表面区别
get方式和post方式深度区别:
post表示发送数据到服务器,但是因为服务器可以响应回来数据的接收情况,所以post也能获得服务器的数据。
get表示从服务器端获得数据,但是因为在从服务器获得数据的时候可以发送相应的关键字作为获得条件,所以get也能发送数据到服务器。
但是:
post应该用于发送数据到服务器的业务,比如用户注册等。
get应该用于以获得数据为主的业务,比如查询,传递某些关键字的查询等。
post的好处在于传递的数据量大,而且浏览器的地址栏上不会显示数据信息。
get的好处在于因为浏览器上有传递的参数列表,所以可以将访问效果的页面放入收藏夹,或者发送给别人。
切记:
只有表单提交并设置form的method属性为post的情况下才是post方式。
其余的任何方式都属于get方式(直接填写url、超链接、表单的get方式等等)
*************************************************************************************************
7.4 中文编码处理
注意:编码的设置一定要编写在相应的对象使用之前,否则就会无效。
7.4.1 响应中文编码
响应中文编码直接影响着响应对象输出的内容在浏览器上的显示。
//设置响应内容类型
resp.setContentType("text/html");
//设置编码格式
resp.setCharacterEncoding("UTF-8");
这两行代码通常也可以合成一行。
//设置响应内容类型及编码
resp.setContentType("text/html;charset=utf-8");
7.4.2 请求中文编码
请求中文编码直接影响着请求对象是否能够接收表单的中文内容。
1、post方式
//设置请求编码Post方式
req.setCharacterEncoding("UTF-8");
2、get方式
get方式比较麻烦,需要先以w3c标准编码分解乱码字符串,然后再以特定的编码从新组合
// 以w3c标准编码分解乱码字符串,返回byte数组
byte[] bs = username.getBytes("ISO8859-1");
// 通过特定编码重新组合
username = new
String(bs, "UTF-8");
*************************************************************************************************
8. 用户注册案例
8.1 搭建用户管理系统(超小型)
8.1.1 搭建项目结构
项目名称:userManagerSystem
项目包名:
cn.itcast.web (servlet)
cn.itcast.entity (实体类)
cn.itcast.service (业务方法)
cn.itcast.dao (数据访问对象:关于实体对象的增删改查)
8.1.2 创建数据库和表
#创建用户管理系统数据库
drop
database
if
exists
userManagerSystem;
create
database
userManagerSystem;
use
userManagerSystem;
#创建用户表
create
table
user
(
id int auto_increment,#用户id
username varchar(50) unique,#用户名,唯一
password
varchar(50),#密码
agname varchar(50),#用户昵称
primary
key(id)#设置id为主键
);
#初始化用户信息
insert
into
user(username,password,agname)values('damao','123456','大毛');
insert
into
user(username,password,agname)values('ermao','654321','二毛');
select * from
user;
8.1.3 加入SqlService数据库工具类(采用c3p0连接池)
记得导包。
<?xml
version="1.0"
encoding="UTF-8"?>
<c3p0-config>
<named-config
name="itcast">
<!-- 配置连接的参数信息 -->
<!-- 使用property 配置连接参数 name属性,表示的连接参数的key
标签中的内容,就是key对应值
-->
<property
name="driverClass">com.mysql.jdbc.Driver</property>
<property
name="jdbcUrl">jdbc:mysql:///userManagerSystem</property>
<property
name="user">root</property>
<property
name="password">123456</property>
</named-config>
</c3p0-config>
package cn.itcast.service;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public
class SqlService {
// 三剑客
Connection con = null;// 连接对象
PreparedStatement pstmt = null;// 语句对象
ResultSet rs = null;// 结果集对象
/**
* 获得连接对象
*
* @return连接对象
* @throws ClassNotFoundException
* @throws SQLException
*/
public Connection getConnection() throws ClassNotFoundException,
SQLException {
//创建c3p0连接池
ComboPooledDataSource ds = new ComboPooledDataSource("itcast");
// 通过连接池对象创建连接
con = ds.getConnection();
return
con;
}
/**
* 关闭三剑客
*
* @throws SQLException
*/
public
void close(ResultSet rs, PreparedStatement pstmt, Connection con) {
try {
if (rs != null)
rs.close();
if (pstmt != null)
pstmt.close();
if (con != null)
con.close();
} catch (SQLException e) {
// TODO: handle exception
e.printStackTrace();
}
}
/**
* 执行更新
*
* @param sql
* 传入的预设的sql语句
* @param params
* 问号参数列表
* @return影响行数
*/
public
int execUpdate(String sql, Object[] params) {
try {
this.getConnection();// 获得连接对象
this.pstmt = this.con.prepareStatement(sql);// 获得预设语句对象
if (params != null) {
// 设置参数列表
for (int i = 0; i < params.length; i++) {
开始,所以是i+1,将所有值都转为字符串形式,好让setObject成功运行
this.pstmt.setObject(i + 1, params[i] + "");
}
}
return
this.pstmt.executeUpdate();// 执行更新,并返回影响行数
} catch (ClassNotFoundException | SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
this.close(this.rs, this.pstmt, this.con);
}
return 0;
}
/**
* 执行查询
*
* @param sql
* 传入的预设的sql语句
* @param params
* 问号参数列表
* @return查询后的结果
*/
public List<Map<String, Object>> execQuery(String sql, Object[] params) {
try {
this.getConnection();// 获得连接对象
this.pstmt = this.con.prepareStatement(sql);// 获得预设语句对象
if (params != null) {
// 设置参数列表
for (int i = 0; i < params.length; i++) {
开始,所以是i+1,将所有值都转为字符串形式,好让setObject成功运行
this.pstmt.setObject(i + 1, params[i] + "");
}
}
// 执行查询
ResultSet rs = pstmt.executeQuery();
List<Map<String, Object>> al = new ArrayList<Map<String, Object>>();
// 获得结果集元数据(元数据就是描述数据的数据,比如把表的列类型列名等作为数据)
ResultSetMetaData rsmd = rs.getMetaData();
// 获得列的总数
int columnCount = rsmd.getColumnCount();
// 遍历结果集
while (rs.next()) {
Map<String, Object> hm = new HashMap<String, Object>();
for (int i = 0; i < columnCount; i++) {
开始
String columnName = rsmd.getColumnName(i + 1);
// 根据列名获得列值
Object columnValue = rs.getObject(columnName);
// 将列名作为key,列值作为值,放入hm中,每个hm相当于一条记录
hm.put(columnName, columnValue);
}
// 将每个hm添加到al中,al相当于是整个表,每个hm是里面的一条记录
al.add(hm);
}
return al;
} catch (ClassNotFoundException | SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
this.close(this.rs, this.pstmt, this.con);
}
return
null;
}
/**
* @param args
*/
public
static
void main(String[] args) {
//测试执行查询,没有参数
String sql = "select * from user";
List<Map<String, Object>> al = new SqlService().execQuery(sql, null);
for (Map<String, Object> map : al) {
System.out.println(map.get("agname"));
}
}
}
*************************************************************************************************
8.2 实现用户注册功能
8.2.1 编写用户注册页面
<!DOCTYPE HTML
PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>用户注册</title>
<meta
http-equiv="keywords"
content="keyword1,keyword2,keyword3">
<meta
http-equiv="description"
content="this is my page">
<meta
http-equiv="content-type"
content="text/html; charset=UTF-8">
<!--<link rel="stylesheet" type="text/css" href="./styles.css">-->
</head>
<body>
<form
action="register.do"
method="post">
用户名:<input
type="text"
name="username"
/><br/>
密码:<input
type="password"
name="password"
/><br/>
昵称:<input
type="text"
name="agname" /><br/>
<input
type="submit"
value="注册"
/>
</form>
</body>
</html>
8.2.2 编写实体类
package cn.itcast.entity;
/**
* @author Administrator 用户实体类
*/
public
class User {
private
int
id;
private String username;
private String password;
private String agname;
public
int getId() {
return
id;
}
public
void setId(int id) {
this.id = id;
}
public String getUsername() {
return
username;
}
public
void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return
password;
}
public
void setPassword(String password) {
this.password = password;
}
public String getAgname() {
return
agname;
}
public
void setAgname(String agname) {
this.agname = agname;
}
}
8.2.3 编写业务方法
package cn.itcast.service;
import cn.itcast.entity.User;
/**
* @author Administrator
* 用户服务类
*/
public
class UserService {
//创建数据库工具类对象
SqlService sqlService = new SqlService();
/**
* 添加用户
* @param user
*/
public
void add(User user)
{
//定义插入的sql语句
String sql = "insert into user(username,password,agname)values(?,?,?)";
//执行插入
sqlService.execUpdate(sql, new Object[]{user.getUsername(),user.getPassword(),user.getAgname()});
}
}
8.2.4 编写用户注册servlet
package cn.itcast.web;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import cn.itcast.entity.User;
import cn.itcast.service.UserService;
/**
* @author Administrator
* 用户注册Servlet
*/
public
class
Register
extends HttpServlet{
@Override
protected
void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//设置请求编码post
req.setCharacterEncoding("UTF-8");
//接收表单元素的值
String password = req.getParameter("password");
String username = req.getParameter("username");
String agname = req.getParameter("agname");
System.out.println(username);
//封装成user对象
User user = new User();
user.setAgname(agname);
user.setPassword(password);
user.setUsername(username);
//调用添加用户方法
UserService userService = new UserService();
userService.add(user);
}
}
<web-app
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID"
version="3.0">
<!-- 用户注册 -->
<servlet>
<servlet-name>Register</servlet-name>
<servlet-class>cn.itcast.web.Register</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Register</servlet-name>
<url-pattern>/register.do</url-pattern>
</servlet-mapping>
</web-app>
*************************************************************************************************
9. 显示所有用户功能
9.1 编写业务方法
/**
* 查找所有用户
* @return所有用户的集合
*/
public List<User> findAll()
{
String sql = "select * from user";
List<Map<String, Object>> al =sqlService.execQuery(sql, null);
List<User> users = new ArrayList();
//将List内的HashMap集合转换成user对象
for (Map<String, Object> map : al) {
User user = new User();
user.setAgname((String)map.get("agname"));
user.setId((int)map.get("id"));
user.setPassword((String)map.get("password"));
user.setUsername((String)map.get("username"));
users.add(user);
}
return users;
}
9.2 编写显示所有用户servlet
protected
void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//设置响应编码
resp.setContentType("text/html;charset=utf8");
//调用查找所有用户方法
UserService userService = new UserService();
List<User> users = userService.findAll();
//将所有用户显示在页面上
for (User user : users) {
resp.getWriter().print(user.getAgname());
resp.getWriter().print("<br/>");
}
}
*************************************************************************************************
10. 显示单个用户
需求:在显示所有用户的页面上的每个用户昵称加上超链接,点击超链接,新开一张页面,显示该用户的全部信息
10.1 编写业务方法
/**
* 将装有hashmap的list集合中的hashmap转成user对象
* @param al 装有hashmap的list集合
* @return装有user对象的list集合
*/
public List<User> convertHmToUser(List<Map<String, Object>> al)
{
List<User> users = new ArrayList<User>();
// 将List内的HashMap集合转换成user对象
for (Map<String, Object> map : al) {
User user = new User();
user.setAgname((String) map.get("agname"));
user.setId((int) map.get("id"));
user.setPassword((String) map.get("password"));
user.setUsername((String) map.get("username"));
users.add(user);
}
return users;
}
/**
* 根据用户名查找该用户
*
* @return单个用户对象
*/
public User findByUsername(String username) {
String sql = "select * from user where username = ?";
List<Map<String, Object>> al = sqlService.execQuery(sql,
new Object[] { username });
// 将装有hashmap的list集合中的hashmap转成user对象,并返回第一个
List<User> users = this.convertHmToUser(al);
if (users.size() > 0) {
return users.get(0);
} else {
return
null;
}
}
10.2 编写显示单个用户servlet
protected
void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//设置响应编码
resp.setContentType("text/html;charset=utf8");
//接收传来的用户名
String username = req.getParameter("username");
//调用查找用户方法
UserService userService = new UserService();
User user = userService.findByUsername(username);
resp.getWriter().print(user.getId());
resp.getWriter().print(user.getUsername());
resp.getWriter().print(user.getAgname());
}
10.3 修改显示所有用户servlet
// 将所有用户显示在页面上
for (User user : users) {
resp.getWriter().print(
"<a target='_blank' href='showSingleUser.do?username=" + user.getUsername()
+ "'>" + user.getAgname() + "</a>");
resp.getWriter().print("<br/>");
}
*************************************************************************************************
11. Request详解
11.1 请求头
如果没出来信息,按F5刷新即可。
请求头组成简要说明:
Remote Address: 远程ip地址和端口
127.0.0.1:80
Request URL: 请求的url路径
http://localhost/demo/test1.do?username=aaa
Request Method: 请求的方法
GET
Status Code: 状态码:200表示成功
200 OK
Request Headers 请求头
Accept:
text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:
gzip,deflate
Accept-Language:
zh-CN
Cache-Control:
max-age=0
Connection:
keep-alive
DNT:
1
Host:
localhost
Referer: 请求来源地址
http://localhost/demo/register.html
User-Agent:
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Maxthon/4.9.3.1000 Chrome/39.0.2146.0 Safari/537.36
X-DevTools-Emulate-Network-Conditions-Client-Id:
123701C4-8169-40C3-B753-3A6119BB11B5
Query String Parametersview sourceview URL encoded 传递的查询字符串信息(包括key和value)
username:
aaa
*************************************************************************************************
11.2 Request常用方法
11.2.1 获取客户机环境信息及请求头
环境信息:
1.getRequestURL方法返回客户端发出请求时的完整URL。
2.getRequestURI方法返回请求行中的资源名部分。
3.getQueryString方法返回请求行的参数部分。
4.getRemoteAddr方法返回发出请求的客户机的IP地址。
5.getRemoteHost方法返回发出请求的客户机的完整主机名。
6.getRemotePort方法返回客户机所使用的网络端口号。
7.getLocalAddr方法返回WEB服务器的IP地址。
8.getLocalName方法返回WEB服务器的主机名。
9.getMethod得到客户机请求方式。
请求头:
1.getHeader(String name)方法
2.getHeaders(String name)方法
3.getHeaderNames()方法
@Override
protected
void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//获得所有请求头的名称
Enumeration<String> e = req.getHeaderNames();
//遍历它
while(e.hasMoreElements())
{
String headerName = e.nextElement();
System.out.print(headerName+":");
System.out.println(req.getHeader(headerName));
}
}
11.2.2 获得客户端提交的数据
1.getParameter(name)方法
获得单项值的表单元素数据(比如文本框,单选按钮等等)
2.getParameterValues(String name)方法
获得多项值的表单元素数据(比如复选框)
接收表单中复选框的值:
兴趣爱好:<br/>
篮球
<input
type="checkbox"
name="hobby"
value="篮球"
/><br/>
足球
<input
type="checkbox"
name="hobby"
value="足球"
/><br/>
排球
<input
type="checkbox"
name="hobby"
value="排球"
/><br/>
网球
<input
type="checkbox"
name="hobby"
value="网球"
/><br/>
// 接收表单中的多值元素(复选框)
String[] hobbys = req.getParameterValues("hobby");
if (hobbys != null)
for (String hobby : hobbys) {
System.out.println(hobby);
}
3.getParameterNames方法
获得所有参数的名称(所有表单元素的name属性的值)
*************************************************************************************************
11.3 URL重写技术
URL重写,其实就是把带一大堆参数的url,变成一个看上去很规矩的url,主要目的是为了搜索引擎,和用户爽。
举例
/showSingleUser.do?username=maoyun
重写后,可以用
/maoyun.php
11.3.1 修改url-pattern
扩大该servlet的接收范围
<!-- 显示单个用户 -->
<servlet>
<servlet-name>ShowSingleUser</servlet-name>
<servlet-class>cn.itcast.web.ShowSingleUser</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ShowSingleUser</servlet-name>
<url-pattern>*.php</url-pattern>
</servlet-mapping>
11.3.2 修改servlet接收方式
//获得请求路径 /userManagerSystem/maoyun.php
String str = req.getRequestURI();
//截取出关键信息
str=str.replaceAll("/userManagerSystem/","");
str=str.replaceAll(".php", "");
//让重写的url的关键信息赋给username
String username = str;
11.3.3 修改显示所有用户servlet
//重写url后的形式
resp.getWriter().print(
"<a target='_blank' href='"+user.getUsername()+".php'>" + user.getAgname() + "</a>");
resp.getWriter().print("<br/>");
*************************************************************************************************
11.4 网站防盗链
网站防盗链就是指本网站的东西只能从本网站的打开,比如显示单个用户只能从显示所有用户的链接打开,不得通过其它方式。
可以使用请求头中的referer
referer :url ,url表示是当前的页面的路径。
如果在该页面,请求一个新地址的时候,浏览器就会把该页面的url
放到referer中,传递给服务器
protected
void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 设置响应编码
resp.setContentType("text/html;charset=utf8");
// 防盗链,获取请求头中的referer信息
String referer = req.getHeader("referer");
System.out.println(referer);
if ("http://localhost/userManagerSystem/showAllUser.do".equals(referer)) {
// 获得请求路径 /userManagerSystem/maoyun.php
String str = req.getRequestURI();
// 截取出关键信息
str = str.replaceAll("/userManagerSystem/", "");
str = str.replaceAll(".php", "");
// 让重写的url的关键信息赋给username
String username = str;
// 调用查找所有用户方法
UserService userService = new UserService();
User user = userService.findByUsername(username);
resp.getWriter().print(user.getId());
resp.getWriter().print(user.getUsername());
resp.getWriter().print(user.getAgname());
}
else
{
resp.getWriter().print("禁止非正常访问");
}
}
*************************************************************************************************
12. Response详解
12.1 响应头和状态码
Response Headersview source
Content-Length: 响应内容长度
81
Content-Type: 内容类型和编码
text/html;charset=utf-8
Date: 日期
Sun, 07 Aug 2016 17:43:18 GMT
Server: Web服务器
Apache-Coyote/1.1
常见状态码:
200 - 请求成功
404 - 请求的资源(网页等)不存在 (请求的url找不到具体的资源,例如找不到servlet,或者html,如果遇到404的错误,通常检查url是否写错,或者url-patten 是否配置错。)
500 - 内部服务器错误(如果遇到500的情况,表示的是java代码出错误)
*************************************************************************************************
12.2 Response常用方法
// 设置响应内容类型及编码
resp.setContentType("text/html;charset=utf-8");
// 通过响应对象,获得打印输出对象
PrintWriter out = resp.getWriter();
*************************************************************************************************
12.3 自动刷新页面
12.3.1 使用Response自动刷新页面
设置显示所有用户页面(首页)3秒后跳转到注册页面
通过设置相应头中的refresh来实现页面的自动跳转。
//设置自动刷新
resp.setHeader("refresh", "3;url=register.html");
12.3.2 使用html meta标签自动刷新页面
<meta
http-equiv="refresh"
content="3;url=showAllUser.do">
*************************************************************************************************
13. 重定向和转发
13.1 重定向
重定向(Redirect)就是通过各种方法将各种网络请求重新定个方向转到其它位置。
可以理解为就是完全性跳转,地址栏会发生改变,可以跳转到外部资源。
用户注册成功后,跳转到首页(显示所有用户页面)-- 使用重定向方式
//注册成功后跳转到首页
resp.sendRedirect("showAllUser.do");
跳转到百度
resp.sendRedirect("http://www.baidu.com");
重定向是响应对象的方法。可以通过get方式的url查询字符串问号方式来传递信息。
*************************************************************************************************
13.2 转发
转发(forward)
可以理解为就是内容性跳转,地址栏不会发生改变,不可以跳转到外部资源。
用户注册成功后,跳转到首页(显示所有用户页面)-- 使用转发方式
//注册成功后跳转到首页--转发
req.getRequestDispatcher("showAllUser.do").forward(req, resp);
转发是请求对象的子对象的方法。可以通过request对象来传递值。
//注册成功后跳转到首页--转发
//给request设置值
req.setAttribute("str","xixi");
req.getRequestDispatcher("showAllUser.do").forward(req, resp);
//取得重定向传递过来的值
String str = (String) req.getAttribute("str");
resp.getWriter().print(str);
*************************************************************************************************
13.3 重定向和转发的区别(面试题)
重定向(Redirect):地址栏会发生改变,可以跳转到外部资源
转发(forward):地址栏不会发生改变,不可以跳转到外部资源
重定向(Redirect):其实是客户端向服务器发送请求后,服务器响应给客户端一个302的状态码和一个新地址,客户端发现状态码是302之后会立马请求那个响应回来的新地址。
resp.sendRedirect("http://www.baidu.com");
上述代码,相当于:
resp.setStatus(302);
//告诉客户端收到响应后立即请求新的地址
resp.setHeader("location", "http://www.baidu.com");
重定向(Redirect)会产生两次请求,两次响应。
转发(forward)只会产生一次请求,一次响应,转发是内部行为,用户看到的始终是先访问的服务的地址,用户根本无法知道是不是转发。
目前建议大家尽量使用重定向,如需要传递大量值时再考虑使用转发(servlet+jsp)。
*************************************************************************************************
14. 使用Myeclispe工具创建Servlet
14.1 创建servlet
public
void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//doGet方法只有get方式请求时才会被调用
......
public
void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//doGet方法只有get方式请求时才会被调用
......
其实最先都是经过service方法,然后在看是get还是post请求,最后再选择是调用doGet还是doPost
14.2 检测doGet与doPost调用示例
14.2.1 创建登录页面
<!DOCTYPE HTML
PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>用户登录</title>
<meta
http-equiv="keywords"
content="keyword1,keyword2,keyword3">
<meta
http-equiv="description"
content="this is my page">
<meta
http-equiv="content-type"
content="text/html; charset=UTF-8">
<!--<link rel="stylesheet" type="text/css" href="./styles.css">-->
</head>
<body>
<form
action="login.do"
method="post">
用户名:<input
type="text"
name="username"
/><br/>
密码:<input
type="password"
name="password"
/><br/>
<input
type="submit"
value="登录"
/>
</form>
</body>
</html>
14.2.2 编写登录servlet查看doGet和doPost调用情况
package cn.itcast.web;
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;
public
class
Login
extends HttpServlet {
/**
* Constructor of the object.
*/
public Login() {
super();
}
/**
* Destruction of the servlet. <br>
*/
public
void destroy() {
super.destroy(); // Just puts "destroy" string in log
// Put your code here
}
/**
* The doGet method of the servlet. <br>
*
* This method is called when a form has its tag value method equals to get.
*
* @param request the request send by the client to the server
* @param response the response send by the server to the client
* @throws ServletException if an error occurred
* @throws IOException if an error occurred
*/
public
void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//doGet方法只有get方式请求时才会被调用
response.setContentType("text/html;charset=utf8");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
out.println("<HTML>");
out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
out.println(" <BODY>");
out.println("这里是get方式过来的请求");
out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
}
/**
* The doPost method of the servlet. <br>
*
* This method is called when a form has its tag value method equals to post.
*
* @param request the request send by the client to the server
* @param response the response send by the server to the client
* @throws ServletException if an error occurred
* @throws IOException if an error occurred
*/
public
void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//doPost方法只有Post方式请求时才会被调用
response.setContentType("text/html;charset=utf8");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
out.println("<HTML>");
out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
out.println(" <BODY>");
out.println("这里是post方式过来的请求");
out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
}
/**
* Initialization of the servlet. <br>
*
* @throws ServletException if an error occurs
*/
public
void init() throws ServletException {
// Put your code here
}
}
*************************************************************************************************
15. ServletConfig
servletConfig对象可以使用一个或多个<init-param>标签为servlet配置一些初始化参数,servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给servlet。进而,程序员通过ServletConfig对象就可以得到当前servlet的初始化参数信息。
有些公共的、统一的配置信息我们可以通过ServletConfig来获取,而不需要写死在代码中。毕竟软件客户可以能够更改web.xml代码的。
例如,我们可以使用ServletConfig配置字符编码
<!-- 用户注册 -->
<servlet>
<servlet-name>Register</servlet-name>
<servlet-class>cn.itcast.web.Register</servlet-class>
<!-- 设置初始化参数 -->
<init-param>
<param-name>charset</param-name>
<param-value>UTF-8</param-value>
</init-param>
</servlet>
// 设置请求编码post,不在写死的,而是从ServletConfig对象中拿
req.setCharacterEncoding(this.getServletConfig().getInitParameter("charset"));
注意:但是,此方式只能针对某一个Servlet,没有做到全局。
*************************************************************************************************
16. ServletContext
是一个全局的储存信息的空间,服务器开始,其就存在,服务器关闭,其才释放。
就像是一个供所有人使用的大仓库,始终只有唯一一个,谁都可以对它进行改变,可以想象成java中的静态全局变量。
可以通过它实现实现数据共享
简单示例:
//获得ServletContext
ServletContext sc = this.getServletContext();
//往ServletContext里面存东西
sc.setAttribute("info", "有用的信息");
// 获得ServletContext
ServletContext sc = this.getServletContext();
// 从ServletContext里面取东西
String info = (String)sc.getAttribute("info");
out.println(info);
我们可以将网站更新远小于查询的业务(比如网站公告)的内容从数据库读取后便放置ServletContext中,以后直接从ServletContext里面读取,直到数据库中该内容变更后同步修改ServletContext中的内容。
通过ServletContext统一设置字符编码
<!-- 设置ServletContext的初始化参数,这是全局的-->
<context-param>
<param-name>charset</param-name>
<param-value>UTF-8</param-value>
</context-param>
// 设置请求编码post,不在写死的,而是从ServletContext对象中拿,所有servlet都可以使用此代码
request.setCharacterEncoding(this.getServletContext().getInitParameter("charset"));
ServletContext还可以获得项目的路径
//获得项目部署之后的访问路径
System.out.println(sc.getContextPath());
//获得项目的真实路径
System.out.println(sc.getRealPath("/"));
Java Web项目与Java桌面应用程序项目读写文件路径的问题
*************************************************************************************************
17. Session
17.1 无状态
Http协议具备无状态的特性,即当客户端请求服务器,服务器返回响应之后,客户端与服务器便再也没有任何关系了。直到下次客户端再发出新的请求。
这种特性叫做无状态。
无状态的好处是可以省下大量的网络资源。
坏处是客户端和服务器端都无法准确的识别对方当前的状态。
*************************************************************************************************
17.2 Session是什么
session对象用于存储特定的用户会话所需的信息。 Session对象的引入是为了弥补HTTP协议的不足,HTTP协议是一种无状态的协议。
有时候,服务器是希望能够记住某一个浏览器进程(一个客户端)访问的状态,特别是成功登录之后的状态。不可能让浏览器在已经成功登录的情况下,访问其它的页面还需要继续登录。所以需要在服务器端记录下每个客户端的相关信息。
从而想将同一客户端的一系列请求和响应组成一个新的过程,叫做会话(Session)。
简单来说:Session可以帮助服务器成功识别并记录不同的客户端信息。
Session一般情况下和浏览器进程绑定(但有很多例外)
同一个浏览器进程(同一个客户端)访问某网站的多个页面都算做一个Session。
不同的浏览器进程(不同的客户端)访问某网站的同一个页面也算做多个不同的Session。
*************************************************************************************************
17.3 创建Session
Session对象通过request对象来获得,获得之后,该对象中会有一个SessionID的值,用来标识不同的浏览器进程。
//获得或创建Session对象
HttpSession session = request.getSession();
//页面上打印出SessionID
out.println(session.getId());
不同的浏览器进程,SessionID 是不相同的。因为它们本来就是不同的客户端。
只要是同一个浏览器进程,哪怕访问该网站不同的servlet也只是一个Session
创建Session的API:
//创建或获得Session对象,如果没有session就创建,有session不创建返回已经有的那个session
request.getSession();
request.getSession(true);
//获得Session对象,有session返回session,没有返回空
request.getSession(false);
*************************************************************************************************
17.4 使用Session
使用Session主要是使用Session来存值和取值,通常会把用户成功登录后的用户名,存放到session中。
//往session中存值,如果该值的key已存在会覆盖
session.setAttribute("myname", "maoyun");
//从session中取值
String myname = (String)session.getAttribute("myname");
//将session中的值打印在页面上
out.println(myname);
直接访问sessionTest2,此时session为空
先访问sessionTest1后再访问sessionTest2
换浏览器直接访问sessionTest2
//删除session中的值,可以用于注销用户
session.removeAttribute("myname");
*************************************************************************************************
17.5 Session销毁方式
Session 虽然存取的内容不多,但是却远远比ServletContext要大大占用服务器的空间,因为ServletContext存的东西在多,也只有一个而已。而Session存的东西再少,数量却更客户端浏览器进程相绑定,服务器(网站)是无法控制用户的访问数量,所以不能让已经创建的Session一直保留在服务器的内存中。特别该客户端其实早已经关闭掉了。
Session销毁方式有三种:
17.5.1 服务器端手动销毁
//手动销毁session,可以用于注销用户
session.invalidate();
17.5.2 正常关闭浏览器时销毁
这个其实不一定,要看浏览器的。
17.5.3 发呆暂离后销毁
因为在非正常关闭浏览器的情况下,有很多种让客户端关掉(短时间内不在访问服务器),比如断电,断网,各种自然灾害等。而此时因为服务器和客户端的那种无状态的关系,根本无法知道客户端今后还会不会访问。所以会给Session设置一个过期时间(默认30分钟),一旦超过这个时间发现客户端浏览器完全没有任何动作(鼠标晃动也算动作),就会将内存中标识此客户端的session给销毁掉,以节省有限的内存空间。
//设置过期时间,默认为秒
session.setMaxInactiveInterval(1800);
*************************************************************************************************
18. 用户登录功能
需求:用户登录需要到数据库中对用户名和密码进行比对,正确弹出登录成功的提示框,并跳转到首页。错误弹出用户名或密码错误提示框,并继续跳转回登录页面。与此同时,在首页顶部显示用户的登录情况。
public
void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 设置请求编码post,不在写死的,而是从ServletContext对象中拿,所有servlet都可以使用此代码
request.setCharacterEncoding(this.getServletContext().getInitParameter("charset"));
response.setContentType("text/html;charset=utf8");
PrintWriter out = response.getWriter();
//获得用户输入的用户名
String username = request.getParameter("username");
//获得用户输入的密码
String password = request.getParameter("password");
System.out.println(password);
UserService userService = new UserService();
//将用户输入的用户名放到数据库中去查
User user = userService.findByUsername(username);
if(user==null)
{
//用户名不对没查到
out.print("<script>alert('用户名或密码错误');window.location='login.html'</script>");
}
else
{
//拿数据库中的该用户的密码和表单传来的密码进行比较
if(user.getPassword().equals(password))
{
//将用户名放入session,日后有用
HttpSession session = request.getSession();
session.setAttribute("username", username);
//相同的话,验证成功
out.print("<script>alert('登录成功');window.location='showAllUser.do'</script>");
}
else
{
//密码不对
out.print("<script>alert('用户名或密码错误');window.location='login.html'</script>");
}
}
}
首页上可以展示出用户的登录情况
// 设置响应编码
resp.setContentType("text/html;charset=utf8");
//获得当前登录的用户名
HttpSession session = req.getSession();
resp.getWriter().print("当前登录的用户为:"+session.getAttribute("username"));
resp.getWriter().print("<hr/>");
*************************************************************************************************
19. 带验证码的注册用户
19.1 拷贝生成验证码的servlet和词语文档
词语文档放在src下面。
<servlet>
<servlet-name>Yzm</servlet-name>
<servlet-class>cn.itcast.web.Yzm</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Yzm</servlet-name>
<url-pattern>/yzm.jpg</url-pattern>
</servlet-mapping>
19.2 在注册页面中加入验证码图片
<!DOCTYPE HTML
PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>用户注册</title>
<script
type="text/javascript">
function change()
{
//改变图片,后面加上一个始终变化的毫秒能防止浏览器缓存
document.getElementById("yzm").src='yzm.jpg?t='+new Date().getTime();
}
</script>
<meta
http-equiv="keywords"
content="keyword1,keyword2,keyword3">
<meta
http-equiv="description"
content="this is my page">
<meta
http-equiv="content-type"
content="text/html; charset=UTF-8">
<!--<link rel="stylesheet" type="text/css" href="./styles.css">-->
</head>
<body>
<form
action="register.do"
method="post">
用户名:<input
type="text"
name="username"
/><br/>
密码:<input
type="password"
name="password"
/><br/>
昵称:<input
type="text"
name="agname"
/><br/>
验证码:<input
type="text"
name="yzm"
/>
<img
id="yzm"
style="cursor: pointer;" src="yzm.jpg"
onclick="change();">点击图片刷新
<br/>
<input
type="submit"
value="注册"
/>
</form>
</body>
</html>
19.3 在验证码的servlet中将生成内容放入Session中
System.out.println(checkcode);
//将验证码内容放入session中
HttpSession session = request.getSession();
session.setAttribute("checkcode", checkcode);
19.4 在注册的servlet中进行验证码
protected
void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 设置请求编码post,不在写死的,而是从ServletConfig对象中拿
req.setCharacterEncoding(this.getServletConfig().getInitParameter("charset"));
//判断用户输入的验证码是否正确
//获取用户输入的yzm
String yzm = req.getParameter("yzm");
//获取系统生成并放入session中的验证码
HttpSession session = req.getSession();
String checkcode = (String)session.getAttribute("checkcode");
//将两种验证码进行比较
if(checkcode.equals(yzm))
{
//验证码输入正确
// 接收表单元素的值
String password = req.getParameter("password");
String username = req.getParameter("username");
String agname = req.getParameter("agname");
// 封装成user对象
User user = new User();
user.setAgname(agname);
user.setPassword(password);
user.setUsername(username);
// 调用添加用户方法
UserService userService = new UserService();
userService.add(user);
//注册成功后跳转到首页--重定向
resp.sendRedirect("showAllUser.do");
}
else
{
//验证码输入不正确
resp.setContentType("text/html;charset=utf8");
//提示用户验证码输入不正确,并返回注册页面
resp.getWriter().print("<script>alert('验证码输入不正确');window.location='register.html'</script>");
}
}
*************************************************************************************************
20. Cookies
Cookies的作用是服务器可以通过服务器端代码将一些信息和数据保存在客户端的电脑的硬盘上,并且服务器端也可以获取它所保存的信息。
但是有一些限制:
大小有限制,不然网站会在你电脑上存储很多乱七八糟的东西。
不同的网站保存在客户端的信息是相互完全独立的。
20.1 Cookies的使用
20.1.1 创建Cookies并放值并存入客户端中
public
void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
// 创建Cookie对象,Cookie以键值对的形式存放
Cookie cookie1 = new Cookie("myname", "maoyun");
Cookie cookie2 = new Cookie("mypwd", "123456");
// 设置Cookie大生存时间,单位秒,默认是-1。也就是保存
cookie1.setMaxAge(7 * 24 * 60 * 60);
cookie2.setMaxAge(7 * 24 * 60 * 60);
// 添加Cookie
response.addCookie(cookie1);
response.addCookie(cookie2);
out.flush();
out.close();
}
20.1.2 读取Cookies
public
void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
//获得Cookie,返回的是个数组
Cookie[] cs = request.getCookies();
if(cs!=null)
{
//遍历Cookie数组
for (Cookie cookie : cs) {
//获得cookie的key
out.println(cookie.getName());
//获得cookie的value
out.println(cookie.getValue());
}
}
out.flush();
out.close();
}
20.1.3 测试Cookie的效果
先访问创建Cookies的servlet
在访问获取Cookies的servlet
关闭浏览器后重新打开在访问获取Cookies的servlet
关闭重启计算机后在访问获取Cookies的servlet
一周后在访问获取Cookies的servlet......
使用别的浏览器访问获取Cookies的servlet
由此可以得出,Cookie的信息是保存到客户端的硬盘上,与浏览器进程绑定,因为保存在客户端的硬盘上,所以关闭浏览器,甚至关闭计算机,只要不换浏览器,都不会消失。
但是如果Cookie被禁用或者手动清除Cookie就不行了。
当清除了Cookie之后在访问,就得不到Cookie的值了。
*************************************************************************************************
21. 自动登录功能
设计思路:只需在用户成功登录后,将用户名和密码放入Cookie中保存在客户端硬盘上,访问首页时候,从客户端硬盘上读取出来进行验证,如果成功放入Session即可。
<!DOCTYPE HTML
PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>用户登录</title>
<meta
http-equiv="keywords"
content="keyword1,keyword2,keyword3">
<meta
http-equiv="description"
content="this is my page">
<meta
http-equiv="content-type"
content="text/html; charset=UTF-8">
<!--<link rel="stylesheet" type="text/css" href="./styles.css">-->
</head>
<body>
<form
action="login.do"
method="post">
用户名:<input
type="text"
name="username"
/><br/>
密码:<input
type="password"
name="password"
/><br/>
自动登录:<br/>
<input
checked="checked"
type="radio"
name="cookiesTime"
value="-1"
/>不保存<br/>
<input
type="radio"
name="cookiesTime"
value="2592000"
/>一个月<br/>
<input
type="radio"
name="cookiesTime"
value="999999999"
/>永久<br/>
<input
type="submit"
value="登录"
/>
</form>
</body>
</html>
public
void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 设置请求编码post,不在写死的,而是从ServletContext对象中拿,所有servlet都可以使用此代码
request.setCharacterEncoding(this.getServletContext().getInitParameter("charset"));
response.setContentType("text/html;charset=utf8");
PrintWriter out = response.getWriter();
//获得用户输入的用户名
String username = request.getParameter("username");
//获得用户输入的密码
String password = request.getParameter("password");
UserService userService = new UserService();
//将用户输入的用户名放到数据库中去查
User user = userService.findByUsername(username);
if(user==null)
{
//用户名不对没查到
out.print("<script>alert('用户名或密码错误');window.location='login.html'</script>");
}
else
{
//拿数据库中的该用户的密码和表单传来的密码进行比较
if(user.getPassword().equals(password))
{
//将用户名放入session,日后有用
HttpSession session = request.getSession();
session.setAttribute("username", username);
//接收用户的Cookie时间设置
String strCookiesTime = request.getParameter("cookiesTime");
int cookiesTime = Integer.parseInt(strCookiesTime);
//创建Cookie对象
Cookie cookie1 = new Cookie("username",username);
Cookie cookie2 = new Cookie("password",password);
//设置Cookie保存时间
cookie1.setMaxAge(cookiesTime);
cookie2.setMaxAge(cookiesTime);
//保存Cookie
response.addCookie(cookie1);
response.addCookie(cookie2);
//相同的话,验证成功
out.print("<script>alert('登录成功');window.location='showAllUser.do'</script>");
}
else
{
//密码不对
out.print("<script>alert('用户名或密码错误');window.location='login.html'</script>");
}
}
}
protected
void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 设置响应编码
resp.setContentType("text/html;charset=utf8");
// 获得Cookie,返回的是个数组
Cookie[] cs = req.getCookies();
// 获得Cookie中的用户名和密码
String username = "";
String password = "";
if (cs != null) {
// 遍历Cookie数组
for (Cookie cookie : cs) {
if (cookie.getName().equals("username")) {
username = cookie.getValue();
}
if (cookie.getName().equals("password")) {
password = cookie.getValue();
}
}
// 进行验证,并模拟登录
UserService userService = new UserService();
// 将用户输入的用户名放到数据库中去查
User user = userService.findByUsername(username);
if (user != null && user.getPassword().equals(password)) {
// 验证成功,将Cookie放入Session
HttpSession session = req.getSession();
session.setAttribute("username", username);
}
}
// 获得当前登录的用户名
HttpSession session = req.getSession();
resp.getWriter().print("当前登录的用户为:" + session.getAttribute("username"));
resp.getWriter().print("<hr/>");
// 调用查找所有用户方法
UserService userService = new UserService();
List<User> users = userService.findAll();
// 将所有用户显示在页面上
for (User user : users) {
// 重写url后的形式
resp.getWriter().print(
"<a target='_blank' href='" + user.getUsername() + ".php'>"
+ user.getAgname() + "</a>");
resp.getWriter().print("<br/>");
}
}
*************************************************************************************************
22. Session和Cookies总结
22.1 Session和Cookies的区别
session机制采用的是在服务器端保持状态的方案
cookie机制采用的是在客户端保持状态的方案
简单的说:
session保存在服务器端的内存中
cookie保存在客户端的硬盘上
22.2 Session和Cookies的联系
session机制采用的是在服务器端保持状态的方案,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于cookie机制来达到保存标识的目的。
Session原理:
1、当浏览器第一次请求服务器时,服务器可以为浏览器创建一个Session对象(其实就是个集合),并且为这个浏览器分配一个绝对唯一sessionID。
2、当服务器响应浏览器的时候,会同时把这个sessionID的值,传递给浏览器。
3、浏览器拿到sessionID后,会将它保存到cookie中,名字叫做jsession。
4、当浏览器再次请求服务器的时候,会将保存在cookie中的sessionID也发送给服务器。
5、服务器依靠这个sessionID就可以找到相对应这个浏览器的Session对象了。
由此得出结论:
1、jsessionid会保存两份,服务器内存中保存一份,客户端硬盘上保存一份。不然服务器如何比对。
2、当浏览器正常关闭的时候,一般情况下,不管Cookies是如何的设置,都会将Cookies中的JsessionID给删除掉,所以在关闭浏览器之后,再次访问服务器Session也就失效了。
3、Session依赖与Cookies,但不是绝对依赖与Cookies。如果完全禁用了Cookies,也可以能实现Session,比如在每次请求服务器的时候,后面加上一个小尾巴。
/grid.jsp;JSESSIONID=fjsdklafjdsfsldkafjlsf?type=1
如果浏览器不支持Cookie或用户阻止了所有Cookie,可以把会话ID附加在HTML页面中所有的URL上,这些页面作为响应发送给客户。这样,当用户单击URL时,会话ID被自动作为请求行的一部分而不是作为头行发送回服务器。这种方法称为URL重写(URL rewriting)。
*************************************************************************************************
23.会话跟踪技术(面试题)
当用户在同一网站的多个页面之间转换时,根本无法确定是否是同一个客户,会话跟踪技术就可以解决这个问题。
当一个客户在多个页面间切换时,服务器会保存该用户的信息。
有四种方法可以实现会话跟踪技术:URL重写、隐藏域、Cookie、Session。
隐藏域
<!-- 隐藏域,页面上看不见,但是会随着表单一起提交,服务器也可以接收 -->
<input
type="hidden"
name="myhidden" value="我是隐藏域"
/>
//获得隐藏域的值
String myhidden= request.getParameter("myhidden");
*************************************************************************************************
23. JavaWeb开发中四大作用域级别(面试题)
页面级(Page):只能本页面才能共享
请求级(Request):通过forward传递了request对象的两个页面可以共享
会话级(Session):同一浏览器进程访问的多张页面可以共享
应用级(ServletContext):同一网站下,所有的客户端,所有的页面都可以共享
*************************************************************************************************
24. 总结
Javaweb 第7天 Servlet课程的更多相关文章
- 【JavaWeb】学习笔记——Servlet、Filter、Listenter
Servlet Servlet 简介 Servlet 是 Java提供的一门动态web资源开发技术 Servlet 是JavaEE 规范之一,其实就是一个接口,将来我们需要定义Servlet类实现Se ...
- MyEclipse配置Tomcat开发JavaWeb程序JSP以及Servlet
1.安装准备 1).下载安装MyEclipse2014,这已经是最新版本. 2).下载Tomcat 官网:http://tomcat.apache.org/ 我们选择8.0: http://tomca ...
- JavaWeb学习总结-04 Servlet 学习和使用
一 Servlet 1 Servlet概念 Servlet时运行在服务器端的Java程序. Servlet的框架核心是 javax.servlet.Servlet 接口. 所有自定义的Servlet都 ...
- javaweb学习总结(六)——Servlet开发(二)
一.ServletConfig讲解 1.1.配置Servlet初始化参数 在Servlet的配置文件web.xml中,可以使用一个或多个<init-param>标签为servlet配置一些 ...
- JavaWeb学习笔记之Servlet(一)
1. 引子: 当我们开始进入JavaWeb开发的学习时,我们就必须要和Servlet和HTTP这两个词进行打交道了,尤其是Servlet.即使到了后面使用JSP (我们知道JSP其本身就是一个Serv ...
- JavaWeb笔记一、Servlet 详解
一.创建一个 Servlet 简单实现类 1.创建一个 HelloServlet 类(测试 Servlet 接口方法) 1 //创建一个 HelloServlet 类并实现 Servlet 接口 2 ...
- javaweb学习总结(六)——Servlet开发(二)(转)
转载自 http://www.cnblogs.com/xdp-gacl/p/3763559.html 一.ServletConfig讲解 1.1.配置Servlet初始化参数 在Servlet的配置文 ...
- JavaWeb(HTML +css+js+Servlet....)
注意 1.不要把函数命名为add(),不然容易和自带的冲突报错 2.是createElement 不要把create中的e写掉了 3.记得是getElementsByTaxName和getElemen ...
- JavaWeb知识回顾-servlet简介。
现在公司主要用jsp+servlet这种原生的开发方式,用的是uap的开发平台,所以趁着这个时候把有关javaweb的知识回顾一下. 首先是从servlet开始. 一.什么是Servlet?(是一些理 ...
随机推荐
- Linux编程之给你的程序开后门
这里说的"后门"并不是教你做坏事,而是让你做好事,搭建自己的调试工具更好地进行调试开发.我们都知道,当程序发生异常错误时,我们需要定位到错误,有时我们还想,我们在不修改程序的前提下 ...
- 回溯法、数独与N阶可达问题
回溯法是剪了枝的穷举,这是字面上的说法,不太好理解,不如讲解实例来的酸爽,于是引出了N阶可达问题: 有N个国家,每个国家有若干城市,小明要从中国(任意一个城市)出发,遍历所有国家(假设这个遍历顺序已经 ...
- 【翻译】在Mac上使用VSCode创建你的第一个Asp.Net Core应用
Setting Up Your Development Environment 设置你的开发环境 To setup your development machine download and inst ...
- XAF-列表视图编辑模式
下面来看看XAF中列表有哪些编辑模式: 一.inline编辑 下图说明了WinForms和ASP.NET应用程序中的可编辑列表视图. 在win中,这个很友好,就像excel中编辑一样.5星功能^_^. ...
- python爬虫-知乎登录
#!/usr/bin/env python3 # -*- coding: utf-8 -*- ''' Required - requests (必须) - pillow (可选) ''' import ...
- selenium自动化过程中遇到的小问题(未完待续)
1.chrome浏览器调用不起来 代码没出错的情况下,检查下chrome浏览器的版本与chromedriver.exe的版本是否匹配;下面的表格是根据网上及官网整理的chromedriver与chro ...
- 怎样在linux或者Unix上检查端口是否在使用
英文原文链接:https://www.cyberciti.biz/faq/unix-linux-check-if-port-is-in-use-command/ Question 1: 怎样在lin ...
- 电脑自动重启(Kernel-Power 41 (63) error)的一些解决办法
查看是否有两个声卡驱动,如果有,尝试关闭其中一个. 可能是内存的问题,用memtest测试.如果有多于一个内存条,仅使用其中的一个试试. 更改电源设置,使机器工作在低耗状态. 更新所有驱动,尤其是主板 ...
- 某种数列问题 (一场欢乐赛的T2)
个人觉得挺难的一道DP题 不会 没有思路 于是去找的正解 于是.. #include <iostream> #include <cstring> #define Max 100 ...
- 面试题-Java基础-布局管理器
1.什么是布局管理器? 布局管理器用来在容器中组织组件.