0%

2017年9月13日 下午4:15

先确定存储结构然后搭配相应的算法
前面的第一章就是我们要考虑的知识点,这个是死的
基础知识点是重点,掌握之后算法其实是很好写的,正如我以前所说的

2017年9月13日 下午4:15

通过这次实验,我实现了快排和二分归并排序。在快排中,主要是先part函数,进行分解,并且在分解的同时排好序,从分治的角度去开,快排其实是侧重于分治中的第一步分解,然后,省略了合并。在分解的过程中关键的两点就是边界和过程的模拟,同样也是大量例子的分析和尝试才是完全弄清楚。接下来是第二个实验二分归并排序,二分归并排序,同样,一开始有很多的细节不清楚,比如说当是奇数时如何处理,这个解决办法就是通过举一个三个数的例子来尝试,发现第一个和第二数为一组,而第三个数为第二组。然后就按分治规定的方法写,但是写到最后的时候发现一个问题,排出来的顺序,即使是错了,错的也一定规律没有,然后我用断点,不断的输出断点信息,终于发现二分归并需要大量的空间,这是我在一开始没有想到的。改正后我又发现一个问题,就是他在合并的时候,总有一部分树的分支没有合并,导致了结果出错,最后我同样通过断点方法解决了,发现其实是边界处理的问题,这就体现除了在写递归的时候,边界处理是一个特别关键的问题。

这个背包问题最难的地方是去理解他的递推方程,它使用(k-1,y)和(k,y-m[k-1][0])+ m[k-1][1]分别求没放入第k个在y限制下的最大值,以及放入第k个在y-m[k-1][0]限制下的最大值。这两个进行比较求最大值。但是有这个是仅仅不够的,当测试值为(1 1,1 2)时,就会发现求出来的值比实际的值多一,根据追踪现象来看,其实是当y-m[k-1][0]<0 时会出现这样的问题,说明这里是少一个约束。从这道题中可以看出,在写程序中,思路是一部分,其实还有一部分重要的内容都包含了约束,约束有很多不是自己能够直接想出来的,需要我们在写程序的时候不断的进行数据测试,和自己笔算的结果进行对比来进行发下。

mergetSort

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//
// main.cpp
// mergeSort
//
// Created by czh on 2017/3/31.
// Copyright © 2017年 czh. All rights reserved.
//

//#include <iostream>
//
//int main(int argc, const char * argv[]) {
// // insert code here...
// std::cout << "Hello, World!\n";
// return 0;
//}
#include<stdio.h>
#define END 10
int seral_a[END] = {10,9,6,5,8,7,4,3,2,1};
int seral[END];

int merge(int l,int r, int k)
{
int j=k+1;
int i=l;
int temp = l;

for(int q=l;q<=r;q++)
{
seral[q]=seral_a[q];//copy
}

// for(int i=l;i<k;i++)
// while(i<k)
// {
// for(int j=k;j<=r;j++)
// while(j<=r || i<=k)
while(temp<=r) //while(temp<=(r-1))找了一个小时
{
if(seral[i]>seral[j])
{
seral_a[temp]=seral[j];
if(j<r)
{
j++;
}
else
{
while(temp<=(r-1))
{
temp++;
seral_a[temp]=seral[i];

i++;
}
break;
}

}
else{
seral_a[temp]=seral[i];
if(i<k){
i++;
}
else
{
while(temp<=(r-1))
{
temp++;
seral_a[temp]=seral[j];

j++;
}
break;
}
}
temp++;
}
// }
return 0;
}

int mergeSort(int l ,int r)
{
int k =0;
if((r-l)>=1)
{
k = (r+l)/2;
mergeSort(l,k);
mergeSort(k+1, r);
merge(l ,r ,k);

}
return 0 ;
}

int main()
{
mergeSort(0 , END-1);

for(int i = 0;i<END; i++)
{
printf("%d " ,seral_a[i]);
}
return 0;
}

//3 8 2 6

//0 1 2
//2+0 /2 = 1
//
//0 1 2 3
//3+0 /2 =1

quickSort

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
//
// main.cpp
// cspAuthority
//
// Created by czh on 2017/3/19.
// Copyright © 2017年 czh. All rights reserved.
//

#include <stdio.h>
#define END 10
int seral[END]={1,2,3,10,9,8,7,6,5,4};
int k=0;

int part(int l , int r )
{
int temp=0;
int k=l;

int j=r;
int i=l;

// for(int i=0;i<=j;i++)
while(i<j)
{
while(i<j)
{
if(seral[j]<seral[k])
{
temp=seral[j];
seral[j]=seral[k];
seral[k]=temp;

k=j;

j--;
break;
}
j--;
}
while(j>i)
{
if(seral[i]>seral[k])
{
temp=seral[i];
seral[i]=seral[k];
seral[k]=temp;

k=i;

i++;
break;
}
i++;
}
}

return k;

}

void quickSort(int l,int r)
{
if((r-l)>=1)
{
k= part(l,r);
quickSort(l,k-1);
quickSort(k+1,r);
}


}

int main()
{
printf("adsfsfs");
quickSort(0, END-1);

for(int i=0; i<END; i++)
{
printf("%d ",seral[i]);
}
return 0;
}


//12 3 14 25 13
//3 12 14 25 13
//
//left
//
//right


//14 25 13
//13 25 14
//13 14 25

背包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//
// main.cpp
// package
//
// Created by czh on 2017/4/15.
// Copyright © 2017年 czh. All rights reserved.
//

#include <iostream>

using namespace std;

int m[100][2]={0};

int package(int k,int y){
int temp1=0,temp2=0;
int ans =0;
if(k<1 || y<=0){
return 0;
}
else{
temp1 = package(k-1, y);

temp2 = package(k,y-m[k-1][0])+m[k-1][1];
if(y-m[k-1][0]<0){//约束条件 举例子(1 1,1 2) 和程序对比
ans = temp1;
}
else{
ans = temp1 > temp2 ? temp1:temp2;
}

return ans;
}
}

int main(int argc, const char * argv[]) {
// insert code here...
int n=0;
int b=0;

cout<<"please input n(num) and b(border) like 'n b'"<<endl;
cin>>n;
cin>>b;

cout<<"please intput weight and value like 'w v'"<<endl;
for(int i=0; i<n; i++)
{
cin>>m[i][0];
cin>>m[i][1];
}

cout<<package(n,b)<<endl;

return 0;
}

2017年9月12日 下午6:13

TP从执行的步骤上来开,真正项目的启动放在最后一步
前面90%的步骤都是在读取配置文件
这说明给了一个道理
1. 读取配置文件,并进行设置就是框架的主要工作,这些都在项目启动前完成。
2. 与之对应的是,我们在使用框架的时候回会写大量的的配置文件,这下就知道原因了

2017年9月12日 下午5:26

JSP页面实现批量删除数据 - 饮冰室人的博客 - CSDN博客

概述

  • 前面的笔记是针对于入门知识点的,而这篇笔记针对于功能
  • 同时,这篇是希望以后忘了之后,看这篇文章能快速找到一个写servlet+jsp项目的正确的思路
  • 中期项目名字是MVCDemo

常用几个小功能的总结

(我这里没有听,我觉得自己看看也就会了,需要使用这个功能的时候去对应的项目去找源代码就行了)

ajax注意点

  1. get 中文乱码,出现中文的时候使用post来解决
  2. Post 可以设置请求头 和 请求体
    ajax.txt

    下载

  3. 下载时文件名容易出现中文乱码,关于这个问题有统一的解决方案
    下载.txt
    下载 1.txt

中期练习(最后有总结)


效果图1

效果图2

我先说说我在拼凑整个基础项目过程中遇到的问题,这里有已经解决的,还有没有解决的问题。其次,我在把各个功能简单的介绍一下,方便以后回忆。

在拼凑功能时遇到的问题

  1. 添加分页功能时(包括ajax)
    1. 首先我上来就抄,没有思考一下
      1. 这个功能是否依赖于jar?
        1. 依赖于那些架包?
      2. 依赖于那些实体类、servlet等.java文件?
      3. 他使用的视图jsp文件是那个?
    2. 我直接就是看见那个就粘贴复制那个,造成了很多没见过的异常,其实总结来说一句话“少东西”。在这个过程中不断的增长补补,一次次的启动服务器,很是浪费时间。
    3. 总结:抄,也要讲究思路,不要认为简单,自己也要认真的分析一次。
  2. 使用ajax来实现每页显示条数
    1. 使用ajax造成的问题
    2. 我的确通过ajax实现了将数据的取回并动态刷新了中的数据
    3. 但是此时的分页条处理我们手选的那个每页显示,分页条中的其他内容其实是没有变的。这就是数据和分页条数据显示不同步
    4. 我现在没时间解决他了,先写下来,以后有时间了整体重新写一下
      1. 我试过方法将include指令写在ajax中,但是会报错,因为这里使用了jsp指令,这个指令是不会等你js的function触发了之后才执行,他在jsp页面在jvm处理的时候就执行了,这时候还没有给了用户。
      2. 由于这里变成了一个递归所以就会不停地报错,shit。
      3. /* str+=“<div align=‘center’><jsp:include page=‘/page.jsp’></jsp:include></div>" */
    5. 出现这个问题的根本原因是:没有考虑ajax异步刷新会造成数据不同步,造成在设计的时候没有考虑,最后完成后才发现,想改都不知道去哪下手,还不如重新。
  3. 当在一个查询页面加入批量删除,ajax,分页,模糊查询时,这些功能一定会相互影响,我这里就举例说了。因为,这些问题造成的原因还是我没有进过系统的分析,就急急忙忙的想完成任务。最后自己看的都恶心。

从分页(page)中学习到的编程思路

  1. 封装实体对象page.java,让对象作为传递的信息的信使,解决了信息传递不方便的情况,不用一个个传了
  2. 其中这个page对象与视图中的分页栏可以理解成是同一个东西。分页栏就是去除了page对象的内容而已。
  3. 封装page对象可以使得业务分层。当我们知道分页栏其实就是page对象时,我们在编码的时候就不用考虑视图层的内容了,只需考虑“我们的数据源有哪些(视图层传入),这些数据源如处理,并将处理的数据给了page实体对象然他保存,最后通过要返回的数据是page对象和借助page对象让sql查询出的数据集。”
  4. 这个思维的核心就是掌握了我有哪些数据,我要输出哪些数据
  5. 当然这也是写servlet的一个主要思维,page实体对象在这个过程中可以理解成一个小小的技巧吧。

中期练习总结:

  1. 写项目时一定要经过严密的思考,尽可能的想清楚们一点。
    1. 思考的时间一定要超过编码时间。
  2. 写Servlet的主要思维
    1. 掌握了我有哪些数据,我要输出哪些数据
    2. 我(servlet)是老大,我下面是有人的,可不能自己干活,我指挥指挥就行了

2017年9月12日 下午4:15

JULY 算法01
重新理解数据结构+算法

现在回过头去总结上面的两篇文章

  1. 在第一篇中是从算法的角度(与之对应的是数据结构)去分析如何去学习一个算法
  2. 而第二偏重使用数据结构的角度去分析如何设计一个数据结构的
  3. 这两篇文章互为补充,构成了我现在认为学习数据结构和算法的基本方法

在解决了方法论的问题之后,我们就要安下心去读书,掌握基础再是最重要的

2017年9月12日 下午4:15

概述:

  1. 今天的这篇文章最大的收获有两点
    1. 数据结构和算法是不同的两个东西
    2. 在广义的数据结构这一大家庭中,可以粗略的分为两个步骤
      1. 第一步骤是写工具
      2. 第二步骤是使用工具
  2. 以前说起说数据结构:不就是链表、树、图那些吗。这样去总结数据结构我觉得是很不正确的,因为这两个步骤
    1. 既说明了我们学习、使用、创造一个数据结构的步骤
    2. 同时也体现了一种分层的思想,这种分层是必须的,因为这两个步骤中需要一个思维的转变
      1. 当形成一种思维时,思维是可以灵活的用到其他地方的
  3. 我主要参考文章个人对数据结构的理解和总结 - zolalad的专栏 - CSDN博客

数据结构和算法的不同

  1. 由于要复习考研的知识,我又捡起了数据结构和算法。
  2. 我今天突然意识到算法和数据结构其实是不同的东西,以前我的视角里只有各种算法,分治、回溯、dp等等.算法可以说是一种思想,一种理论的东西,他的实现一定是依靠一定的数据结构,在这一过程中他需要你有抽象概括能力,其实就是需要你转换成计算机语言去表述。

ADT是核心(抽象数据类型)

  1. ADT = 数据逻辑结构 + 基本操作
    1. 这里的基本操作我是忽视的重要知识点
  2. 因此学习数据结构的核心就是去掌握每个数据的逻辑结构以及它对应的操作有哪些
  3. 补充
    1. ADT是不包含存储结构的
    2. 这个存储结构同样也是需要掌握的
    3. 总结:对于每个知识点,一共需要掌握三个部分

区分开逻辑结构和存储结构

  1. 对于一批数据,数据的运算是定义在数据的逻辑结构之上的,而运算的具体实现就依赖于数据的存储结构。
  2. 这里的逻辑结构=数据结构=ADT(不准确)
  3. 可以把ADT就是一个工具类,你作为一个工具类的使用者,不用管它实现;而存储结构可以理解成ADT这个工具的具体实现。这就说明我的思维要分为两步骤,第一步骤是写工具,然后第二步骤是使用工具
  4. 给我的感觉就是,存储结构站在逻辑结构来说其实是不可见的。这种感觉就像我们在javaEE中mvc模式,你一开始在没写业务逻辑之前就要将基本的dao层(以及dbutils)准备的差不多,准备完之后,我们就可以理解成—我只要一句话就可以完成对数据的数据库处理。每当写完dao层之后,我就会提醒自己—忘了dao层是如何实现的,因为这时的角度要从数据处理的角度,转移到业务功能的实现上,你实现dao层的思维和你写业务的思维是不一样的,当然这也体现了mvc架构的最重要的目的。而现在也是一样的,实现一个存储结构就好比是一个你实现dao层,首先完成的是它,当我选好自己适合的存储结构,并通过代码去实现逻辑结构下要求的基本操作时,我就要转变一个编码思路,我才不管你一个insert()操作是使用顺序存储还是链式存储,你给我实现了就行了,我能用insert()完成我想要的功能就可以了。

数据结构(逻辑结构),存储结构,基本操作三者的关系

  1. 分类
    1. 基本操作
    2. 基本数据结构(逻辑结构)
    3. 基本存储结构
  2. 理解:
    1. 对于我们常说的数据结构(逻辑结构),存储结构,基本操作者三者来说,他们没啥特别神秘的,因为是是那些固定的几种,各自都不超过10种。
    2. 但是根据他们之间的组合关系(这种组合关系需要和自己的需求相对应)确实特别多的,举个例子:同一个逻辑结构-链表,我可以选择让实现不同的基本操作,对应的基本操作我可以根据自己的需要来选择不同的存储结构,我是要顺序的还是链式的,甚至其他的存储结构都可以。这个过程就是一个选择的过程。

附录:一些资料

数据结构 - 话题精华 - 知乎

优秀博客推荐:各种数据结构与算法知识入门经典

数据结构与算法系列 目录 - 如果天空不死 - 博客园

这是一篇以前对我启发比较大的一篇文章:
五大常用算法:分治、动态规划、贪心、回溯和分支界定 - yapian8的专栏 - CSDN博客

2017年9月12日 下午2:54

来源:浅谈Java Web 之过滤器Filter_慕课手记

概述

  1. 关于filter的知识点看那个连接就可以了
  2. 其他就是我对filter的一些理解了

文中摘抄

  1. 其不用于处理客户端请求,
  2. 只用于对request和response进行修改
  3. 并管理web服务器的所有资源(通过对Jsp文件、Servlet文件、Html文件以及一些静态的图片等资源进行拦截实现)

我对filter的理解

  1. 这篇文章认为写的很精炼,因此关于filter我也不赘述了
  2. 在会使用filter之后,我说说我对filter的理解
  3. Filter的作用在这篇文章中说的是解决了——每个Servlet中都书写同样的代码定会为此工程增添很高的冗余度。作者这么说,也说明了filter其实是可以用servlet代替的,这里先不管他好不好。
  4. 除了这个原因外,我觉得在编写一个filter的过程中,这个过程是独立的。如果我们使用servlet来实现filter的功能,先不谈冗余度的事,在这个过程中我们是把他当做一段逻辑控制来写的,是整个业务逻辑的一部分。而如果写在filter中,写filter的整个过程其实是可以把业务逻辑放在一边,思维变成了—我给你“配”一个filterfilter可以将一部分特定的逻辑代码从原先的业务逻辑代码中抽离出来,这一个“配”字我觉得就是filter的核心,这样的思维才是filter给我们带来最宝贵的东西,体现了一种分离的思想

结合android中的四大组件,我也说说我对组件的理解

  1. 组件的第一点是独立的,这点最最重要
  2. 也就是像上面说的filter一样,我可以站在一个第三方的角度,我给项目配、配、配
  3. 一个组件就像一个配件一样,让项目更加的完整
  4. 我这里只是组件的一个感觉是配,在写项目的时候你可以随时加,你也项目可以一开始就准备好,当然也可以最后再弄,看心情就行了额。

2017年9月11日 上午11:55

步骤

  1. 先看一下这个 servlet简介 + 项目配置步骤
  2. 新建项目,更改项目字符编码,添加jar包
  3. webcontent下新建一个floder,直接ui的库文件复制过来,现在不要html页面
  4. 把html文件粘贴到jsp中,jsp的头还得留着啊
  5. 把粘过来的html中的link啥的一些引入库文件地址改一改
  6. 增加项目目录,其中有dao,service,servlet,dbutils
  7. 在web.xml中注册servlet
  8. 再添加各种jar包
  9. 好了!可以开始写逻辑完成功能了。

2017年9月11日 上午9:23

概述

  1. 我结合我昨天看的一个电影加勒比海盗5-死无对证来说明我对cookie和session的理解吧。
  2. 电影中的小男主角在自己还是一个小屁孩的时候,去荷兰号去找父亲,父亲当时将自己脖子中的项链给了小男主角,就是希望有一天等小男主角长大之后还可以相认。果然,那主角不负众找到了三叉戟,破除了海洋中的所有诅咒。最后,小男主角和父亲相见的时候,父亲第一眼看的是哪个项链,然后确认了之后他们拥抱在一起(ps:关键是小男主角还找了一个漂亮的媳妇,bravo!)
  3. 先说cookie
  4. 这里的项链就是我们这里的一个cookie,cookie由服务器产生,cookie在给了客户端之后服务器就没有了(给了儿子,父亲自然就没了),由于http是无状态的,所以一旦下一次访问(男主就和父亲很难相见),服务器是不认识谁了,但是客户端有了这个cookie,服务器就根据这个cookie知道你是你是谁了,这两个人就相认了。
  5. 这里有几个特殊情况,例如这个cookie让别人拿走,用在了其他的浏览器上,那么服务器也是不知道了(这样父亲就认错孩子了),但是利用这种情况,可以做多个客户端浏览器的同步。还有就是一个服务器可以为很多不同的用户服务(父亲有好多的孩子,都给了他们不同的项链),服务器在把自己的cookie给出去之后,其实对于自己产生的cookie的具体内容也不知道了,他得根据客户端给他啥,他就知道啥,认为这就是自己给客户端的,简单的说:给啥我要啥(这一点的判断是浏览器根据访问的url来划分的,浏览器访问不同的url,会把不同的url对应的cookie全都传过去),这也就解决了一个服务器多个客户端的问题,客户端给我啥我要啥,这时候可以理解成的情景是:就是两个人在普通的交流,这个交流就一个回合,但是客户端这人用本子记录内容,但是服务器这个人比较懒
  6. 对于服务器来说,只要有人访问他,他就给他一个cookie,其中就是一个Jsessionid。对于双11来说,你想想他会产生多少个Jsessonid,服务器这么做的原因是想和各种来找他的人都建立起关系,维护住关系。
  7. cookie中还有一个domain的概念,我理解就是兄弟姐妹之间公用一个cookie作为信物,而不是每个人一个。
  8. 还要就是session
  9. Session是保存在服务端,这个可以理解成父亲在根据cookie认出自己的儿子之后,从自己的记忆中找到了很多自己和儿子相处的儿时时光,这只有父亲一个知道,其他人根本不知道,是私有的。这里的私有记忆就是session,只能让服务器自己程序执行的时候使用,作为自己一些功能实现的判断依据,用来写代码逻辑用的
  10. 这里还说明了其实session和cookie其实是完全不同的东西。他们之间的联系是当一个 Session 开始时,Servlet 容器将创建一个 HttpSession 对象,Servlet 容器为 HttpSession 分配一个唯一标识符,称为 Session ID这个sessionid是一个cookie,是为了让服务端能够区分开不同的用户,那么根据这个cookie就可以在服务端的代码逻辑中过滤一些人(eg:防止用户没登录就访问购物车界面,然后告知他没登录)。这一点也可以从javaEE手册中证明,session和cookie是没有继承和实现关系的。
  11. 其他:
    1. session服务器内存中 cookie客户端硬盘中
    2. Session的有效时间通过xml控制,而cookie直接通过代码控制
      1. Tomcat自带一个web.xml文件,如我们在使用session中没有在自己的项目web.xml中声明有效时间,那么默认使用就是tomcat中web.xml文件的
    3. 在禁止使用cookie的地方使用session
      1. response.encodeURL的用法 - zhaogengzi的专栏 - CSDN博客
      2. response.encodeURL的用法 - langqiao123的专栏 - CSDN博客


可以从http报文头中去观察cookie的过程

详细的知识点

直接看这个txt文档排版比较整齐,下面那个是为了方便粘贴复制
cookie和session.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
================================
================================
================================

会话跟踪

1. 什么是会话
* 用户拨打10086,从服务台接通后会话开始;
* 用户发出话费查询请求,服务台响应。这是该会话中的一个请求;
* 用户发出套餐变更请求,服务台响应。这是该会话中的又一个请求;
* ...
* 用户挂断电话,会话结束。

2. 会话的特性
* 一个会话中可能包含多个请求;
* 一个会话中发出请求的用户是唯一的;

3. JavaWeb会话
* 从用户打开本站第一个页面开始,会话也开始了;
* 用户会发出0~n个请求;
* 用户关闭浏览器会话结束了。

4. 什么是会话跟踪技术
HTTP是无状态协议,也就是没有记忆力的协议,每个请求之间无法共享数据。
这就无法知道会话什么时候开始,什么时候结束,也无法确定发出请求的用户身份。这说明需要使用额外的手段来跟踪会话!
* 在一个会话中共享数据即会话跟踪技术

------------------------------------

Cookie

1. 什么是Cookie

* Cookie是HTTP协议的规范之一,它是服务器和客户端之间传输的小数据。
* 首先由服务器通过响应头把Cookie传输给客户端,客户端会将Cookie保存起来。
* 当客户端再次请求同一服务器时,客户端会在请求头中添加该服务器保存的Cookie,发送给服务器。
* Cookie就是服务器保存在客户端的数据!
* Cookie就是一个键值对!!!

2. Cookie规范
* Cookie通过请求头和响应头在服务器与客户端之间传输;
* Cookie大小限制在4KB之内;
* 一台服务器在一个客户端最多保存20个Cookie;
* 一个浏览器最多可以保存300个Cookie;

虽然Cookie规范是如此,但在今天,浏览器厂商的竞争异常激烈,所以多少会超出Cookie规则的限制。但也不会超出过多!

3. Cookie与请求头和响应头

* 服务器向客户端发送Cookie的响应头为Set-Cookie,例如:Set-Cookie:cookiename=cookievalue
* 客户端向服务器发送Cookie的请求头为Cookie,例如:Cookie:cookiename=cookievalue

4. Servlet中向客户端发送Cookie
Cookie cookie1 = new Cookie("test1", "abcdefg");
Cookie cookie2 = new Cookie("test2", "ABCDEFG");
response.addCookie(cookie1);
response.addCookie(cookie2);

5. Servlet中获取客户端发送过来的Cookie

Cookie[] cs = request.getCookies();
if(cs != null) {
for(Cookie c : cs) {
System.out.println(c.getName() + "=" + c.getValue());
}
}

=============================

Cookie的细节

1. Cookie的maxAge
当服务器创建Cookie对象后,可以调用setMaxAge()方法设置Cookie的最大生命。
* maxAge > 0:表示Cookie在客户端硬盘上保存的最大时间,单位为秒;
* maxAge < 0:表示Cookie不会被浏览器保存到硬盘上,而只在浏览器内存中存活,一旦客户端关闭浏览器在,那么Cookie就消失;
* maxAge == 0:表示删除Cookie,例如客户端硬盘已经存在名为abc的Cookie,如果服务器再向客户端发送名为abc,并且maxAge为0的Cookie,那么表示删除客户端上的名为abc的Cookie。

2. Cookie的path
浏览器在访问BServlet时,是否要带上AServlet保存的Cookie呢?这要看Cookie的path了。
现有资源如下:
* http://localhost:8080/day06_2/servlet/AServlet
* http://localhost:8080/day06_2/servlet/BServlet,保存名为xxx的Cookie
* http://loclahost:8080/day06_2/servlet/CServlet,保存名为yyy的Cookie
* http://loclahost:8080/day06_2/servlet/user/DServlet, 保存名为zzz的Cookie

// 没有设置Cookie的path
AServlet {
Cookie c = new Cookie("xxx", "XXX");
response.addCookie(c);
}
// 设置了Cookie的path为/day06_2
CServlet {
Cookie c = new Cookie("yyy", "YYY");
c.setPath="/day06_2";
response.addCookie(c);
}
DServlet {
Cookie c = new Cookie("zzz", "ZZZ");
resposne.addCookie(c);
}


在BServlet中保存的Cookie没有设置path,那么它的path默认为当前BServlet的所在路径,即“/day06_2/servlet”。
在CServlet中保存的Cookie设置了path为/day06_2。
在DServlet中保存的Cookie没有设置path,那么它的path默认为DServlet的所在路径,即“day06_2/servlet/user”


当访问AServlet时,是否要带上xxx这个Cookie呢?因为AServlet的访问路径为/day06_2/servlet/BServlet,它包含了xxx的path,即/day06_2/servlet,所以需要带上。

当访问AServlet时,是否要带上yyy这个Cookie呢?因为AServlet的访问路径为/day06_2/servlet/BServlet,它包含了xxx的path,即/day06_2,所以需要带上。

当访问AServlet时,是否要带上zzz这个Cookie呢?因为AServlet的访问路径为/day06_2/servlet/BServlet,它不包含zzz的path,即/day06_2/servlet/user,所以不会带上。


3. Cookie的domain
Cookie的path是在同一主机中指定共享Cookie,如果主机不同那么就一定不能共享Cookie,无论path是什么。
如果希望不同的二级域名中可以共享Cookie,那么就要设置Cookie的domain了。
例如:news.baidu.com、tieba.baidu.com、zhidao.baidu.com,它们的域名不同,但百度希望它们之间可以共享Cookie,那么就要设置domain了。

1). 设置Cookie的path为“/”,例如:cookie.setPath("/");
2). 设置Cookie的domain,例如:cookie.setDomain(".baidu.com"),其中domain中没有指定域名前缀!

在news.baidu.com主机中的某个项目中保存了Cookie
在tieba.baidu.com主机中某个项目中获取Cookie

当然这需要配置两个虚拟主机才行。


4. Cookie保存中文
Cookie的name和value都是不能保存中文的,但可以先把中文转换成URL编码,然后在保存到Cookie的name和value中。
String name = "姓名";
String value = "张三";
name = URLEncoder.encode(name, "utf-8");
value = URLEncoder.encode(value, "utf-8");

Cookie c = new Cookie(name, value);
response.addCookie(c);

在获取Cookie时,再使用URL解码即可。
Cookie[] cs = request.getCookies();
if(cs != null) {
for(Cookie c : cs) {

byte[] b=c.getName().getBytes(iso-8859-1);
String name = new String(b,"utf-8");
String name = URLDecoder.decode(c.getName(), "utf-8");
String value = URLDecoder.decode(c.getValue(), "utf-8");
System.out.println(name + "=" + value);
}
}

=============================
=============================
=============================

HttpSession

在JavaWeb中提供了HttpSession类,用来表示http会话。

1. 获取HttpSession
HttpSession session = request.getSession();
HttpSession session = request.getSession(false);

2. 域功能
session是域对象,所以有setAttribute()和getAttribute()等方法
服务器会为每个会话创建一个session对象,所以session中的数据可供当前会话中所有servlet共享。

3. 登录案例
请求功能:
1. 如果登录功能,在session中保存user对象
2. 访问index1.jsp,查看session中是否存在user对象,如果存在,说明已经登录过。
3. 访问index2.jsp,查看session中是否存在user对象,如果存在,说明已经登录过。
如果关闭了浏览器,那么会话结束,再打开浏览器就开始了一个新会话,那么直接访问index1.jsp或index2.jsp时,session是新的,没有保存user对象,那么表示还没有登录。

4. session的原理
session是依赖Cookie实现的。
session是服务器端对象
当用户第一次使用session时(表示第一次请求服务器),服务器会创建session,并创建一个Cookie,在Cookie中保存了session的id,发送给客户端。这样客户端就有了自己session的id了。但这个Cookie只在浏览器内存中存在,也就是说,在关闭浏览器窗口后,Cookie就会丢失,也就丢失了sessionId。
当用户第二次访问服务器时,会在请求中把保存了sessionId的Cookie发送给服务器,服务器通过sessionId查找session对象,然后给使用。也就是说,只要浏览器容器不关闭,无论访问服务器多少次,使用的都是同一个session对象。这样也就可以让多个请求共享同一个session了。
当用户关闭了浏览器窗口后,再打开浏览器访问服务器,这时请求中没有了sessionId,那么服务器会创建一个session,再把sessionId通过Cookie保存到浏览器中,也是一个新的会话开始了。原来的session会因为长时间无法访问而失效。
当用户打开某个服务器页面长时间没动作时,这样session会超时失效,当用户再有活动时,服务器通过用户提供的sessionId已经找不到session对象了,那么服务器还是会创建一个新的session对象,再把新的sessionId保存到客户端。这也是一个新的会话开始了。

  设置session超时时间
web.xml文件中配置如下:
<session-config>
<session-timeout>30</session-timeout>
</session-config>


5. session与浏览器
session对象是保存在服务器端的,而sessionId是通过Cookie保存在客户端的。
因为Cookie不能在多个浏览器中共享,所以session也不能在多个浏览器中共享。也就是说,使用IE登录后,再使用FireFox访问服务器还是没有登录的状态。

而且同时打开多个相同浏览器的窗口,是在使用同一session。如果你使用的是老浏览器,例如IE6,那么就会每个窗口一个session。

6. session的API
* String getId():获取sessionId;
* int getMaxInactiveInterval():获取session可以的最大不活动时间(秒),默认为30分钟。当session在30分钟内没有使用,那么Tomcat会在session池中移除这个session;
* void setMaxInactiveInterval(int interval):设置session允许的最大不活动时间(秒),如果设置为1秒,那么只要session在1秒内不被使用,那么session就会被移除;
* long getCreationTime():返回session的创建时间,返回值为当前时间的毫秒值;
* long getLastAccessedTime():返回session的最后活动时间,返回值为当前时间的毫秒值;
* void invalidate():让session失效!调用这个方法会被session失效,当session失效后,客户端再次请求,服务器会给客户端创建一个新的session,并在响应中给客户端新session的sessionId;
* boolean isNew():查看session是否为新。当客户端第一次请求时,服务器为客户端创建session,但这时服务器还没有响应客户端,也就是还没有把sessionId响应给客户端时,这时session的状态为新。

7. URL重写
session依赖Cookie,这是因为服务器需要把sessionId保存到客户端。如果用户的浏览器关闭了Cookie功能,那么session不能使用了!
还可以在浏览器关闭了Cookie后使用URL重写的方法保存sessionId,这需要在每个URL后面都加上sessionId!这样用户的请求中就包含了sessionId,服务器就可以通过sessionId找到对应的session对象了。
使用response.encodeURL()方法对URL进行编码,这样URL中会智能的添加sessionId。
 当浏览器支持cookie时,response.encodeURL()方法不会在URL后追加sessionId
当浏览器不支持cookie时,response.encodeURL()方法会在URL后追加sessionId

2017年9月11日 上午8:23

概述

  1. 这篇文章是老师给的笔记
  2. 前面的几篇文章,可以把它们理解成使用servlet的前期环境配置,配置就基本上不动了,此时还没有真正的根据业务来写代码,而这篇文章其实是做项目期间最有常用的一些内容,称之为我的编码手册,写逻辑的时候忘了,来这里找,复制过去就好了。
    1. servlet三个作用域
    2. Servlet上下文对象
    3. Servlet实现类HttpServlet的使用
    4. servlet简介 + 项目配置步骤
  3. 其中包括了最常用给的request和response,除了这两个外还有解决乱码,以及路径上的一些理解总结
  4. 附录中有原稿

1
2
3
4
5
6
7
8
请求响应对象

request和response
* 当服务器接收到请求后,服务器会创建request和response对象,把请求数据封装到request对象中;
* 然后调用Servlet的service()方法时把这两个对象传递给service()方法;
* 在service()方法中可以通过request对象获取请求数据,可以使用response对象向客户端完成响应;

* 每次请求服务器都会创建新的request和response对象,即每个请求有自己独自的request和response对象。

response对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
response对象

1. response简介
response是响应对象,用来在Servlet的service()方法中向客户端响应数据。
response的功能如下:
* 设置响应头
* 发送状态码
* 设置响应正文
* 重定向

2. response响应正文
* ServletOutputStream getOutputStream():用来向客户端响应字节数据;
* PrintWriter getWriter():用来向客户端响应字符数据;

response.getWriter().print("你好");//向客户端响应字符数据
byte[] bytes = ...;
response.getOutputStream().write(bytes);//向客户端响应字节数据

3. response字符编码
* Tomcat响应数据默认使用ISO-8859-1
* 通常浏览器默认使用GBK编码
* response.setCharacterEncoding("utf-8");//设置response.getWriter()的字符编码

1)
response.getWriter().print("大家好");
因为Tomcat默认使用的是ISO-8859-1编码,不支持中文。所以一定编码!

2)
response.setCharacterEncoding("utf-8");
response.getWriter().print("大家好");
因为已经设置了字符流编码为utf-8,所以响应给客户端的数据为utf-8编码!
但因为浏览器默认使用的是gbk来解析响应数据,所以乱码!如果浏览器使用utf-8编码,那么就不会乱码了。

3)
response.setCharacterEncoding("gbk");
response.getWriter().print("大家好");
因为设置了字符流编码为gbk,所以响应给客户端的数据为gbk编码!
因为浏览器默认使用gbk来解析数据,所以不会出现乱码!如果浏览器使用utf-8编码,那么就会出现乱码!

4)
response.setContentType("text/html;charset=utf-8");
response.getWriter().print("大家好");
setContentType()方法有两个作用:
* 设置字符流编码。等同与调用了response.setCharacterEncoding("utf-8");
* 设置Content-type响应头,即通知浏览器响应数据的编码为utf-8
因为设置字符流的编码为utf-8,所以响应给客户端数据为utf-8编码
因为设置了Content-type头为utf-8,所以浏览器会使用utf-8来解析响应数据
没有乱码!

5)
response.setContentType("text/html;charset=gbk");
response.getWriter().print("大家好");
* 设置了字符流为gbk,所以响应给客户端的数据为gbk
* 设置了Content-type头为gbk,所以通知浏览器响应数据为gbk编码
没有乱码!

6)
response.setHeader("Content-type", "text/html;charset=utf-8")
等同于
repsonse.setContentType("text/html;charset=utf-8")

7)
response.getOutputStream().write("大家好".getBytes("gbk"));
响应的数据是gbk编码
客户端浏览器默认使用gbk编码
所以没有乱码

======================

4. response字符流缓冲区
response字符流缓冲区大小为8KB,当向字符流中写入数据后,数据可能只在缓冲区中,而没有发送到浏览器。
可以调用response.flushBuffer()或response.getWriter().flush()方法刷新缓冲区,把数据发送到浏览器。

======================

5. 设置响应头

response.setHeader("Content-type", "text/html;charset=utf-8");
等同与
response.setContentType("text/html;charset=utf-8");

response.setHeader("Refresh", "5; URL=http://www.baidu.com");

======================

6. 指定状态码

response.setStatus(200):设置状态码为200
response.sendError(404, “您要查找的资源不存在”):设置状态码为404
repsonse.sendError(500, “服务器出氏了”):设置状态码为500

在调用sendError()方法时,不只是设置了状态码,而且还会给浏览器一个显示错误的页面。

======================

7. 重定向

response.sendStatus(302);
repsonse.setHeader("Location", "http://www.baidu.com");

快捷的方法重定向:

response.sendRedirect("http://www.baidu.com");

* 重定向是两次请求
* 重定向不局限与当前应用,也可以是其他应用,例如重定向到百度
* 重定向响应码为302,而且必须有Location响应头
* 重定向与response响应流同时使用。

request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
request

1. request功能介绍
* 获取请求头
* 获取请求参数
* Servlet三大域对象之一
* 请求包含和请求转发

2. request域方法
* void setAttribute(String name, Object value):添加或替换request域属性
* Object getAttribute(String name):获取request域指定名称的域属性
* void removeAttribute(String name):移除request域指定名称的域属性
* Enumeration getAttributeNames():获取所有request域的属性名称

3. request获取请求头
* String getHeader(String name):获取指定名称的请求头
* int getIntHeader(String name):获取指定名称的请求头,把值转换成int类型。
* Enumeration getHeaderNames():获取所有请求头名称

4. request获取请求数据相关其他方法
重点:
* String getMethod():获取请求方式
* String getContextPath():获取上下文路径,即“/” + 应用名称,例如:/day05_1
* void setCharacterEncoding(String):设置请求体的编码
* String getRemoteAddr():获取客户端IP地址

非重点:
* int getContentLength():获取请求体字节数
* Locale getLocale():获取请求Locale,例如zh_CN表示中文,中国
* String getCharacterEncoding():获取请求体编码,在没有调用setCharacterEncoding()之前该方法返回null
* String getQueryString():获取参数列表,例如:username=zhangSan&password=123
* String getRequestURI():返回请求URI路径,从应用名称开始,到参数之前这一段,例如:/day05_1/AServlet
* StringBuffer getRequestURL():整个请求URL,不包含参数部分
* String getServletPath():返回Servlet路径,从应用名称后开始,到参数之前这一段,不包含应用名称。
* String getServerName():返回主机名,例如:localhost
* int getServerPort():返回服务器端口号,例如:8080

5. 获取请求参数
获取请求参数,即获取超链接上的参数和表单中的参数
* String getParameter(String name):获取指定名称的参数,如果存在同名参数,那么该方法只获取第一个参数值
* String[] getParameterValues(String name):获取指定名称的参数,因为同名参数的存在,所以返回值为String[]
* Enumeration getParameterNames():获取所有参数名称
* Map getParameterMap():获取所有参数,封装到Map中,key为参数名称,value为参数值。

6. 请求包含和请求转发
* 请求包含和请求转发都是在一个请求中,访问两个Servlet。
* 请求包含和请求转发都是有一个Servlet去调用执行另一个Servlet
* 请求包含和请求转发都可以共享request中的数据,因为都是一个请求。

* 从AServlet请求转发到BServlet
> 在AServlet中可以设置响应头
> 在AServlet中不能使用响应流输出

如果在AServlet中执行了响应操作,那么有两种可能:
* 如果在AServlet中响应的数据导致response提交,那么在转发时抛出异常;
* 如果在AServlet中响应的数据没有导致response提交,那么response中的数据会被清空。

* 从AServlet请求包含BServlet
> 在AServlet可以设置响应头
> 在AServlet可以使用响应流输出

* 请求转发和请求包含都要使用RequestDispatcher对象:RequestDispatcher rd = request.getRequestDispatcher("/BServlet");
* 请求转发执行RequestDispatcher的forward()方法:rd.forward(request,response);
* 请求包含执行RequestDispatcher的include()方法:rd.include(request,response);
* 请求转发和请求包含的路径都是服务器端路径,相对当前应用


7. 请求转发与重定向
* 请求转发是一个请求,而重定向是两个请求
* 请求转发,是使用RequestDispatcher来完成,重定向使用response对象来完成
* 请求转发的路径都是服务器端路径,而重定向是客户端路径,需要给出应用名称
* 请求转发在浏览器地址栏中的地址是第一个Servlet的路径,而重定向在地址栏中的地址是第二个请求的Servlet的路径
* 请求转发中的两个Servlet是可以共享request数据的,而重定向因为是两个请求,所以不能共享request数据
* 请求转发只能转发到本应用的其他Servlet,而重定向可以重定向到其他应用中。

8. request.getParameter()和request.getAttribute()
* getParameter()是获取客户端参数,它是从客户端传递给服务器的数据。
* getAttribute()是获取服务器端自己设置的数据,而不是客户端的数据。
* request没有setParameter()方法,不能自己设置参数,参数都由客户端传递
* request有setAttribute()方法,在getAttribute()之前,需要先setAttribute()才能获取到。
* getAttribute()和setAttribute()是用来在请求转发和请求包含中的多个Servlet中共享数据。

路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

路径

1. 客户端路径和服务器端路径
* 客户端路径需要给出应用名称,例如:/day05_1/AServlet
* 服务器端路径无需给出应用名称,例如:/AServlet

2. 客户端路径
1). 页面中都是客户端路径:
* 超链接的href
* 表单的action
* <img>的src
2). 重定向也是客户端路径:response.sendRedirect("/day05_1/BServlet");

3. 服务器端路径
* <url-pattern>
* 请求转发和请求包含
* ServletContext获取资源等

乱码

1
2
3
4
5
6
7
8
9
10
11
12
乱码
1. 请求编码
* 客户端发送的数据编码:由浏览器来决定:
1). 如果是在地址栏中直接给出url,那么一般都是默认为GBK,但这个可能不太大。
2). 如果是通过页面上的表单或超链接发出请求,那么由当前页面的编码来决定发送的参数的编码。

* 无论浏览器发送过来的是什么编码的数据,Tomcat都默认使用ISO-8859-1来解码
1). POST:可以使用request.setCharacterEncoding()方法来设置请求体数据的编码,因为POST请求参数在请求体中,所以是可以设置编码的。在使用request.getParameter()方法获取参数之前,先使用request.setCharacterEncoding()方法来设置编码即可。
2). GET:没有方法可以设置它,因为参数在url中。所以使用request.getParameter()获取到的数据一定是错误的使用了iso-8859-1解码的。可以再使用iso-8859-1把字符串转回到byte[],再重新使用正确的编码来解码即可。
String s = request.getParameter("s");//使用iso-8859-1错误的解码了
byte[] bytes = s.getBytes("iso-8859-1");//退回错误的解码,让字符串通过iso-8859-1返回到字节数据,即还原字节数据
s = new String(bytes, "utf-8");//重新使用正确的utf-8来解码。

附录:

request和response.txt
servlet.txt
tomcat及web入门.txt