0%

基本概念

GET用来获取资源,他只是查询,获取数据,不会修改服务器的数据;这也说明了他的一定的安全性。因为他是读取的,因此可以对get请求进行缓存。

Post可以向服务器发送修改请求,进行数据的修改。但是post请求不会进行缓存。如果对数据的修改缓存到本地,而服务器什么也没做,那么我们的修改没有意义。

区别

参考(抄袭):GET 和 POST 的区别? - 知乎 (zhihu.com)

我们常说的一些区别都是一些表面上的,比如:GET没有POST安全、GET请求时URL的长度是有限制的、GET没有bodyPOSTbody等等。这些都是针对浏览器中的要求, 在使用HTTP作为接口进行传输时,就没有这么多条条框框了,此时GETPOST只是HTTP协议中的两种请求方式,而HTTP协议是基于TCP/IP的应用层协议, 无论GET还是POST,用的都是同一个传输层协议,所以在传输上没有区别

  1. 我们前面说,无论是GET请求还是POST请求,其本质都是不安全的,为什么这样说呢?如果仅仅从GET请求的参数在地址栏是可见的,POST是不可见的,那就太肤浅了。 由于HTTP自己本身是一个明文协议,每个HTTP请求和返回的数据在网络上都是明文传播,无论是urlheader还是body。 只要在网络节点捉包,就能获取完整的数据报文,要防止泄密的唯一手段就是使用HTTPS(用SSL协议协商出的密钥加密明文HTTP数据)。

  2. 为什么在浏览器中GET请求方式的url长度有限制呢?这是因为浏览器要对url进行解析,而解析的时候就要分配内存。对于一个字节流的解析,必须分配buffer来保存所有要存储的数据。而url这种东西必须当作一个整体看待,无法一块一块处理,于是就处理一个请求时必须分配一整块足够大的内存。如果url太长,而并发又很高,就容易挤爆服务器的内存。

  3. POST是发送两个请求吗? 上面提到POST请求可以被分为“请求头”和“请求体”两个部分,那这两部分是一起发送出去呢?还是先发“请求头”,再发“请求体”呢? 在HTTP协议中并没有明确说明POST会产生两个数据包。之所以会发两个数据包,则是出于以下考虑:如果服务器先收到“请求头”,则会对其进行校验,如果校验通过,则回复客户端“100 - Continue”,客户端再把”请求体“发给服务器。如果请求被拒了,服务器就回复个400之类的错误,这个交互就终止了。这样做的优点是可以避免浪费带宽传输请求体,但是代价就是会多一次Round Trip。如果刚好请求体的数据也不多,那么一次性全部发给服务器可能反而更好。所以说,这和POST完全没有关系,只是基于两端的一种优化手段罢了。

Get方法会产生一个TCP数据包,浏览器会把Header和Data一并发送出去,服务器响应200(OK),并回传相应的数据

Post方法

  1. post请求不会被缓存
  2. post请求不会保存在浏览器记录中
  3. post对数据长度没有要求
  4. 效率低
  5. 刷新之后数据会被重新提交

Get方法

  1. 刷新无害
  2. 可以被缓存
  3. 会保存在浏览器记录中
  4. 对数据长度有限制
  5. url是可见的
  6. 效率较快

Http协议

HTTP协议与Session会话 | 偷掉月亮 (moonshuo.cn)

介绍

HTTP,超文本传输协议。最大的作用就是确定了请求和响应数据的格式,浏览器发送给服务器数据:请求报文;服务i去返回给浏览器数据 :响应报文。

请求

请求包含三个部分:

  • 请求行:请求的方式,请求的URL,请求的协议(一般都是HTTP1.1)

image-20220407131452781

  • 请求消息头:通过具体的参数对本次请求进行详细说明,以键值对的方式进行展现。

    Accept代表可以接受的文件类型,其他表示编码格式,接受的用语言等等

    image-20220402143706322

    比较重要的消息头:

    名称 功能
    Host 服务器的主机地址
    Accept 声明当前能够接受的媒体类型
    Referer 当前请求来源页面的地址
    Content-Length 请求内容的长度
    Content-Type 请求体的内容类型
    Cookie 浏览器访问服务器时携带的Cookie数据
    • 请求体

    它将一个页面表单中的组件值通过param1=value1&param2=value2的键值对形式编码成一个格式化串,它承载多个请求参数的数据。不但报文体可以传递请求参数,请求URL也可以通过类似于“/chapter15/user.html? param1=value1&param2=value2”的方式传递请求参数。

    get方式,没有请求体,但是有一个queryString,在URL之后

    image-20220407132139686

    post方式,有请求体,from data

    image-20220407132212295

    json格式,有请求体,request payload

    响应

    • 响应行:协议+响应状态码+响应状态

      image-20220407132537095

    • 响应头

    image-20220407132607612

    • 响应体

    响应的实际内容,响应的使响应的标签等

Http是无状态的

当我们传输两次请求时,服务器无法区分是否是同一个客户端发送过来的,比如第一次请求是添加购物车,第二次是请求结账,但是服务器无法判断两次请求的主题是否为同一人。

Session会话

Session会话是浏览器通过给不同的请求分配id,标记同一个主体所发送的请求事务,达到跟踪的效果

在控制台获得打印session的消息

1
2
3
4
5
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession httpSession= req.getSession();
System.out.println(httpSession.getId());
System.out.println(httpSession.isNew());
}

可以发现两次的请求的session都是相同的,但是第一次请求会创建一个,但是第二次不会去新创建一个session

image-20220402151448524

常用的API,注意session的有效会话为半个小时

1
2
3
4
5
6
7
8
9
10
11
//获取当前会话
HttpSession httpSession= req.getSession();
//获取sessionid
System.out.println(httpSession.getId());
//判断是否为新的
System.out.println(httpSession.isNew());
//获取最大会话时间
System.out.println(httpSession.getMaxInactiveInterval());
//强制会话失效
httpSession.invalidate();
//获取会话的时间等

Session保存作用域

1
2
//向当前的session保存作用域保存一个数据,可以通过另外一个组件获得响应的值,但是与对应的sessionid相对应
httpSession.setAttribute("name","java");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.zss.servlets;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
* @author zss
*/
public class GetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Object nameObj=req.getSession().getAttribute("name");
System.out.println(nameObj);
}
}

image-20220402153847848

但是 注意,当我们使用两种不同的浏览器获取数据时,其对应的主体对象不同,其session也会不同,所以我们无法通过再次访问另外一个页面再次对其进行访问

image-20220402154450198

cookies

image-20220415211406461

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@WebServlet(urlPatterns = {"/Demo01"})
public class Demo01Servlet extends HttpServlet {

@Override
public void init(){

}

@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//创建一个cookie对象
Cookie cookie=new Cookie("name","value");
//将cookie对象保存到浏览器
resp.addCookie(cookie);


req.getRequestDispatcher("hello.html").forward(req,resp);

}
}

运行以上代码之后,可以在这里找到cookies保存的地址

image-20220415212718679

image-20220415212935029

我们也可以对其进行设置时常,保证cookie保存的时间,时间过去,浏览器会进行删除,cookie可以帮助我们记住用户名和密码,并且设置时间,并且设置几天免登录等等操作。

Java中可变参数与作用域使用细节

可变参数

概念:Java中允许将同一个类中多个同名同功能但是参数个数不同的方法,封装成一个方法,可以通过可变参数实现。一个形参列表中只能有一个可变参数。

基本语法:

访问修饰符 返回类型 方法名(数据类型 …形参名){

}

举个例子

首先大家看一下以下一段代码,如果想要求得100个,那岂不是很麻烦,这个时候就可以使用可变参数

1
2
3
4
5
6
7
8
9
10
11
12
13
class Function {
public int sun(int n1, int n2) {
return n1 + n2;
}

public int sun(int n1, int n2, int n3) {
return n1 + n2 + n3;
}

public int sun(int n1, int n2, int n3, int n4) {
return n1 + n2 + n3 + n4;
}
}

使用可变参数进行实现,这个时候就可以把参数当作数组来进行对待,其用法与数组类似:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**利用递归求取斐波那契数,数量大于2*/
import java.util.Scanner;
public class Test {

public static void main(String []args){
Test test=new Test();
test.sums(10,20,30,40,50);
}

public int sums(int ...num){
System.out.println("参数的个数为"+num.length);
int res=0;
for (int i=0;i<num.length;i++){
res=res+num[i];
}
System.out.println("和为"+res);
return res;
}

}

image-20220417114801844

作用域使用细节

  1. Java中,主要的变量就是属性(成员变量)与局部变量
  2. 局部变量一般是指成员方法中定义的变量。
  3. 作用域分类:全局变量:就是属性,作用域为整个类体,局部变量:也就是除了属性之外的其他变量,作用域为定义他的代码块
  4. 全局变量使用可以不赋值,系统自动匹配默认值,而局部变量必须赋值后才能使用。
  5. 全局变量可以加上修饰符,但是局部变量不可加修饰符。

Java中方法的重载典型例子

Java中允许同一类中,多个同名方法的存在,但是要求形参列表不一致。

在Java中常见的System.out.println(“”);这个输出语句其实也是重载的一种,在Javaapi文档中查看System,println函数可以理解为println(int x),println(char x)等等。可以发现他们都调用了println函数,但是其形参不同。

image-20220417114631182

Java中构造器

基本语法

修饰符 方法名(形参列表){

方法体;}

作用

主要作用是完成对新对象的初始化,并不去创建对象。

构造器举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Test {

public static void main(String []args){
//利用构造器进行赋值,完成属性的初始化
Cat test=new Cat("阿华",20);
System.out.println(test.name+test.age);
}
}
class Cat{
String name;
int age;
//构造器没有返回值,不能用int,void修饰
//构造器方法名与类名一致
//
public Cat(String pName,int pAge){
name=pName;
age=pAge;
System.out.println("构造器被调用了");
}
}

image-20220417115106496

构造器的使用细节

  1. 一个类可以定义多个不同的构造器,即构造器的重载,构造器也可以仅仅只定义部分属性
  2. 构造器没有返回值
  3. 方法名字与类名字必须一致
  4. 参数列表与成员方法一样的规则
  5. 构造器的调用系统完成
  6. 如果没有去定义构造器,系统会自动给类生成一个默认的无参构造方法,比如Cat(){},可以用Javap(反编译)命令查看—–javap 类名.class
  7. 一旦自己定义构造器,默认的构造器就被覆盖了

运算符 作用 示例 结果
+ 相加 7+7 14
字符串相加 “我爱你”+“我不爱你” “我爱你我不爱你”
- 相减 7-7 0
* 相乘 7*7 49
/ 相除 7/7 1
% 取余数 11%7 4
++ 自增前++ a=2;b=++a;(在其他运算之前就将a+1) a=3;b=3;
自增后++ a=2;c=a++; a=3;b=2;
自减前– a=2;d=–a; a=1;d=1
自减后– a=2;e=a–; a=1;e=2;

运行以下示例查看最后结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Test {
public static void main(String [] args){
int s1=7;
int s2=s1+s1;
int s3=s1-s1;
int s4=s1*s1;
int s5=s1/s1;
int s6=11%7;
String s7="我爱你"+"我不爱你";
System.out.println("7+7="+s2+"\t7-7="+s3+"\t7*7="+s4+"\t7/7="+s5+"\t11%7="+s6+"\t\"我爱你\"+\"我不爱你\"="+s7);
int a=2;
int b=++a;
System.out.println("++a之后a="+a+"\tb="+b);
a=2;
int c=a++;
System.out.println("a++之后a="+a+"\tc="+c);
a=2;
int d=--a;
System.out.println("--a之后a="+a+"\td="+d);
a=2;
int e=a--;
System.out.println("a--之后a="+a+"\te="+e);
}
}

image-20220306174905757

运算符 作用 示例 结果
== 比较是否相等 1==1 1与1相等,结果为true
!= 判断是否不等 1!=2 1与2不相等,结果为true
< 小于 1<2 true
> 大于 2>1 true
<= 小于等于 1<=1 true
>= 大于等于 1>=1 true
instanceof 检查是否为类的对象instanceof是Java中的二元运算符,左边是对象,右边是类;当对象是右边类或子类所创建对象时,返回true;否则,返回false,注意左边的对象不能是基本数据类型,左边的对象实例和右边的类不在同一个继承树上。instanceof详解参考,后面作者会详细写出应用。 “hsp” instanceof String true
1
2
3
4
5
6
7
8
9
10
11
12
public class Test {
public static void main(String [] args){
int a=9;
int b=8;
System.out.println(a==b);
System.out.println(a!=b);
System.out.println(a>=b);
System.out.println(a<=b);
System.out.println(a>b);
System.out.println(a<b);
}
}

image-20220306175303656

Java中的逻辑运算符与赋值运算符三元运算符

逻辑运算符

运算符 作用 举例
&& 与,两边判断值都为真才为真 true&true=true;true&false=false
|| 或,只要一边为真,结果就为真 true||false=true
取反,结果与原来相反 !true=fasle
^ 异或,不相同的时候取反 fasle^true=true

基本赋值运算符

=,简单的将右边的值赋值给左边

复合赋值运算符

+=,/=,-=,%=。简单说明以下+=,其他基本都一样。

a+=b;—->a=a+b;

运行以下代码,可以查看出相应的结果。

1
2
3
4
5
6
7
8
9
10
public class Test {
public static void main(String [] args){
int a=9;
int b=8;
a=a+b;
b/=a;
System.out.println(a);
System.out.println(b);
}
}

image-20220704174732071

三元运算符

基本语法:条件表达式?运行结果1:运行结果2;(如果为真,则运行结果1,假则运行结果2)

1
2
3
4
5
6
7
public class Test {
public static void main(String [] args){
int a=9;
int b=8;
System.out.println(a>b?"a大于b":"a小于b");
}
}

image-20220704174742517

Java如何键盘输入

  1. 导入该类所在的包,Java中键盘输入的函数在所在的包为java.util.Scanner;scanner类表示一个简单的用户扫描器,可以接受用户的输入。
  2. 创建该对象,即声明变量。
  3. 调用里面的功能,定义输入的值变量,键盘输入的值赋值给谁??注意Scanner包中拥有next方法,仔细查看Javaapi文档,next用于将输入的下一个标记为对应的类型,包括nextint,nextlong等等数据类型。

运行以下代码接受用户的输入年龄,姓名,成绩等

1
2
3
4
5
6
7
8
9
10
11
12
13
//1.引入Java的包
import java.util.Scanner;
public class Test {
public static void main(String [] args){
//创建对象用于接受用户的输入,test为一个对象
Scanner test=new Scanner(System.in);
System.out.println("请输入您的年龄");
int age=test.nextInt();
System.out.println("请输入您的姓名");
String name=test.next();
System.out.println("尊敬的"+name+"您的年龄为"+age);
}
}

image-20220306180610067

逻辑运算符:https://moonshuo.cn/posts/59455.html

位运算符

计算机在运行和存储过程中都是以补码的方式进行的(非常重要!!!),与运算,或运算等都是以补码进行位运算。

运算符 含义 举例 解释 结果
& 位与运算符 4&5 0000 0100(4的补码,不是源码)&0000 0101(5的补码),对应的位进行与逻辑运算 0000 0100(4的补码,其结果与源码相同)–>4
| 位或运算符 -4|5 -4补码:11111111 11111111 11111111 11111100 5补码:00000000 00000000 00000000 00000101 11111111 11111111 11111111 11111101(此为结果的补码,转换成相应的源码)—>1…….00000011=-3
^ 位异或逻辑 4^5 0000 0100^0000 0101,对应位置进行异或逻辑运算 0000 0001(此为结果的补码)——>源码表示为1
~ ~4 看下面 -5

解释:

  1. 4表示为00000000 00000000 00000000 00000100,第一位为符号位,0表示正数,1表示负数。
  2. 4的补码=4的源码,为00000000 00000000 00000000 00000100。
  3. ~4操作,得到的是结果的补码:11111111 11111111 11111111 11111011
  4. 源码为:10000000 00000000 00000000 00000101—->-5

我们再来推导以下~(-2)做练习

  1. 先得到-2源码:10000000 00000000 00000000 00000010
  2. -2得反码:11111111 11111111 11111111 11111101
  3. -2得补码:11111111 11111111 11111111 11111110
  4. ~(-2)操作(得到的是结果的补码):00000000 00000000 00000000 00000001
  5. 结果的源码为:00000000 00000000 00000000 00000001—>1
1
2
3
4
5
6
7
8
9
public class Test {
public static void main(String [] args){
System.out.println(4&5);
System.out.println(-4|5);
System.out.println(4^5);
System.out.println(~4);
System.out.println(~-2);
}
}

image-20220704174936719位移运算符

这里位运算也都是按照补码进行位运算

运算符 作用 举例 解释 结果
>> 右移位运算,负数补充1,正数补充0 8>>1 8右移1位(1000)——0100 4
-8>>4 -8右移四位 -1
<< 左移位运算 9<<2 9左移两位(0000 1001)——0010 0100 36
>>> 逻辑右移,也叫无符号右移,高位补充0 -8>>>2

这里举一个简单的例子:

  1. -8的源码为:10000000 00000000 00000000 00001000
  2. -8反码为:11111111 11111111 11111111 11110111,其补码为11111111 11111111 11111111 11111000
  3. 位移二位,高位补0,得到结果的补码:00111111 11111111 11111111 11111110
  4. 则结果的反码为:11000000 00000000 00000000 00000001,源码为001111111 11111111 11111111 11111110=1073741822
1
2
3
4
5
6
7
public class Test {
public static void main(String [] args){
System.out.println(-8>>1);
System.out.println(9<<2);
System.out.println(-8>>>2);
}
}

image-20220704174945978