文章目录
- 1. 数据结构
- 2.流
- 3.线程池 多线程
- 3.1线程
- 3.2 线程池
- 4.锁
- 5.面向对象
- 5.2 封装、继承、多态
- 5.2抽象、接口
- 5.3重写 、 重载
- 5.4final
- 6.设计模式
- 7.反射
- 8.异常
- 9.常用类
- 9.1 String
- 9.2 Object
- 9.3 数组
- 10 其他
- Linux基础
- cookie / session的区别
- 转发 、 重定向的区别
- http与https 的区别
1. 数据结构
java的数据结构有哪些?
线性结构:数组、链表、哈希表;队列、栈
非线性结构有:堆、树(二叉树、B树、B+树、红黑树)
图
常用的集合类有List集合,Set集合,Map集合,其中List集合与Set集合继承了Collection接口,
List有序可重复的集合接口,继承自Collection接口,表示元素按照插入顺序排列。
Set无序不重复的集合接口,继承自Collection接口,表示元素唯一性。
Map键值对映射的集合接口,表示具有唯一键和对应值的集合。
集合的遍历:可以使用迭代器(Iterator)来遍历集合中的元素,也可以使用增强型for循环(foreach)来简化遍历操作。迭代器提供了对集合中元素的统一访问方式。
1.List、Set 和 Map 之间有什么区别?它们的常用实现类有哪些?
List、Set和Map是Java集合框架中的三个核心接口,它们之间的区别如下:
List:
list是有序可重复的集合接口,可以按照顺序或者指定的顺序访问和操作元素,还可以通过索引访问元素
常用实现类:ArrayList、LinkedList
Set:
set是无序不重复的集合接口,不能通过索引访问元素。
常用实现类:HashSet、TreeSet
Map:
map是键值对映射的集合接口,每个键只能对应一个值。键值均可以为空
常用实现类:HashMap、ConcurrentHashMap
3.ArrayList 和 LinkedList 的区别是什么?它们适用于不同的场景吗?
ArrayList:底层是数组,查询快增删慢,可以通过索引随机访问,时间复杂度是O(1),
LinkedList: 底层是链表,查询慢增删块,不能使用索引访问,需要从头结点或者尾结点开始遍历,时间复杂度为O(n)
ArrayList适用于需要快速随机访问元素的场景,例如需要频繁地根据索引读取或修改元素的情况。
LinkedList适用于需要频繁进行插入和删除操作的场景,特别是在中间或开头进行插入和删除操作比较多的情况。
对于大部分常见的情况,ArrayList的性能要优于LinkedList,因为数组的访问速度更快。
但在某些特定的场景下,LinkedList可能会更适合,例如需要频繁进行插入和删除操作,并且对于随机访问的性能要求较低的情况。
4.HashSet 和 TreeSet 的区别是什么?它们如何保证元素的唯一性?
HashSet的底层是hash表且是无序不重复的集合接口
TreeSet的底层是红黑树,是有序不重复的集合接口
为了保证元素的唯一性,HashSet和TreeSet在判断元素是否重复时,依赖于元素的equals方法(和哈希码)
在选择HashSet和TreeSet时,需要根据具体的需求进行选择:
如果只关心元素的唯一性,而不关心元素的顺序,可以选择HashSet,它的插入、删除和查找操作的性能较好。
如果需要对元素进行排序,可以选择TreeSet,它会根据元素的顺序进行存储,但由于需要维护红黑树的平衡性,插入、删除和查找操作的性能稍低于HashSet。
总结:HashSet和TreeSet都可以保证元素的唯一性,HashSet适用于无序需求,而TreeSet适用于有序需求。
5.HashMap 和 HashTable 的区别是什么?它们的线程安全性如何?
HashMap是非线程安全的,存储的键和值都可以为null
而HashTable是线程安全的,存储的键和值都不可以为null
HashMap的性能通常比HashTable更好。
6.ConcurrentHashMap 是如何实现线程安全的?它与 HashMap 的区别是什么?
实现线程安全的几个关键点:
ConcurrentHashMap内部使用了分段式的锁,将整个数据结构分成一些独立的部分,并称为“段”并对不同的段进行了锁的力度的控制。还使用了volatile关键字来确保可见性,确保当一个线程修改了某个段的内容后,其他线程可以立即看到修改的结果。还使用并发安全的数据结构(hashEntry数组和链表)与线程安全的迭代器
与HashMap相比,ConcurrentHashMap的区别如下
:
ConcurrentHashMap是线程安全的,可以在多线程环境下进行并发操作,而HashMap是非线程安全的。在并发环境下,ConcurrentHashMap的性能通常比HashMap好,因为它通过分段锁的机制允许多个线程同时进行读操作,提高了并发性能。
7.如何遍历集合框架中的元素?有哪些遍历方式?
1.使用迭代器(Iterator):通过调用集合的iterator()方法获取迭代器对象,然后使用while循环和next()方法逐个访问元素,直到遍历完所有元素。
List<String> list = new ArrayList<>();
// 添加元素到列表中
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {String element = iterator.next();// 处理元素
}
2.使用增强型for循环(foreach):适用于数组和实现了Iterable接口的集合类,可以直接通过for循环遍历元素,不需要显式使用迭代器。
List<String> list = new ArrayList<>();
// 添加元素到列表中
for (String element : list) {// 处理元素
}
3.**使用Lambda表达式和Stream API:**从Java 8开始,引入了Lambda表达式和Stream API,可以通过Stream的forEach()方法对集合进行遍历,并结合Lambda表达式进行元素处理。
List<String> list = new ArrayList<>();
// 添加元素到列表中
list.stream().forEach(element -> {// 处理元素
});
4.使用普通的for循环:适用于数组和实现了RandomAccess接口的集合类,通过下标访问元素进行遍历。
List<String> list = new ArrayList<>();
// 添加元素到列表中
for (int i = 0; i < list.size(); i++) {String element = list.get(i);// 处理元素
}
8.collection与collections的区别
Collection 是一个集合接口
Collection,提供了对集合对象进行基本操作的通用接口方法,所有集合都是它的子类,比如 List、Set 等。
Collections 是一个包装类
Collections,是一个工具类,它包含了很多静态方法,不能被实例化,比如排序方法: Collections. sort(list)等。
9.如何实现数组和 List 之间的转换?
数组转 List:使用 Arrays. asList(array) 进行转换。
List 转数组:使用 List 自带的 toArray() 方法。
10.HashMap的实现原理:
HashMap 基于 Hash 算法实现的,我们通过 put(key,value)存储,get(key)来获取。当传入 key 时,HashMap 会根据 key. hashCode() 计算出 hash 值,根据 hash 值将 value 保存在 bucket 里。当计算出的 hash 值相同时,我们称之为 hash 冲突,HashMap 的做法是用链表和红黑树存储相同 hash 值的 value。当 hash 冲突的个数比较少时,使用链表否则使用红黑树
2.流
IO流根据流向分为 输入流和输出流
根据类型分为字节流与字符流,字节流用于处理二进制数据,以字节为单位进行读写操作;字符流用于处理文本数据,以字符为单位进行读写操作。
字节流:InputStream、OutputStream、FileInputStream、FileOutputStream等。
字符流:Reader、Writer、FileReader、FileWriter等。
还有缓冲流,他是基于字节流或字符流的装饰器,通过在内存中设置缓冲区来提高读写的效率。缓冲流可以减少对底层流的频繁读写操作,从而提高性能。常用的缓冲流类有:
字节缓冲流:BufferedInputStream、BufferedOutputStream。
字符缓冲流:BufferedReader、BufferedWriter。
3.线程池 多线程
3.1线程
1.创建线程有三种方式
1.继承Thread,重写run方法
2.实现Runable接口,重写run方法
3.实现Callable接口,重写call方法
2.Runnable与callable区别
Runnable规定的方法是run(),Callable规定的是call()
Runnable 的任务执行后无返回值,Callable的任务执行后有返回值
call()方法可以抛出异常,run()方法不可以,因为run()方法本身没有抛出异常,所以自定义的线程类在重写run()方法的时候也无法抛出异常
运行Callable 任务可以拿到一个Future对象,表示异步计算的结果。
3.线程的状态
五态:1.新建2.就绪3.运行4.阻塞5.终止
七态:1.新建2.就绪3.运行4.阻塞5.等待6.超时等待7.终止
线程生命周期,主要有五种状态:
当线程创建后进入新建状态,调用start()方法进入就绪状态cup分配时间片,拿到分配的时间片后进入运行状态。
新建状态(New) : 当线程对象创建后就进入了新建状态.如:Thread t = new MyThread();
就绪状态(Runnable):当调用线程对象的start()方法,线程即为进入就绪状态.
运行状态(Running):当CPU调度了处于就绪状态的线程时,此线程才是真正的执行,即进入到运行状态
阻塞状态(Blocked):处于运状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入就绪状态才有机会被CPU选中再次执行.
根据阻塞状态产生的原因不同,阻塞状态又可以细分成三种:
等待阻塞:运行状态中的线程执行wait()方法,本线程进入到等待阻塞状态
同步阻塞:线程在获取synchronized同步锁失败(因为锁被其他线程占用),它会进入同步阻塞状态
其他阻塞:调用线程的sleep()或者join()或发出了I/O请求时,线程会进入到阻塞状态.当sleep()状态超时.join()等待线程终止或者超时或者I/O处理完毕时线程重新转入就绪状态
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期
3.2 线程池
线程池的创建有七个参数,分别是:核心线程数,最大线程数,工作队列,线程工厂,存活时间,存活时间单位,拒绝策略
1. 线程池的工作流程
接收到任务,首先判断一下核心线程是否已满,如果未满,则去创建一个新的线程执行任务,如果核心线程数已满,工作对列未满,将线程存储到工作对列中,等待核心线程获取执行;如果工作对列已满且线程数小于最大线程数,则创建一个新的线程线程处理任务(需要获取全局锁);如果线程数超过了最大线程数,按照四种拒绝策略处理任务,四种拒绝策略有:1.提交任务的线程自己去执行该任务 2.默认拒绝策略,会抛出异常 3.直接丢弃任务,没有任何异常抛出4.丢弃最老的任务,其实就是把最早进入工作队列丢弃,然后把新任务加入到工作对列
2. 三种常见的线程池
创建线程池的方式有多种,我了解到的有,1.固定大小的线程池,可控制并发的线程数,超出的线程会在工作队列中等待。2.带有缓存的线程池。3,可以执行延迟任务的线程池
2.为什么使用线程池
线程池可以降低线程生命周期的系统开销问题,加快响应速度;统筹内存和CPU的使用,避免资源使用不当;可以统一管理资源
4.锁
1. 锁的类型
锁分为乐观锁、悲观锁、synchronized
乐观锁是每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。
悲观锁每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻止,直到这个锁被释放
2…lock和synchronize的区别synchronized
都是解决线程安全的工具,synchronize是java中的同步关键字;而lock是J.U.C包中提供的接口
synchronized 可以给类、方法、代码块加锁;而 lock 只能给代码块加锁。
synchronized 不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁;而 lock 需要自己加锁和释放锁,如果使用不当没有 unLock()去释放锁就会造成死锁。
2.lock和synchronize的区别
这个问题我从四个方面来回答
第一个,从功能角度来看,lock和synchronize都是java中用来解决线程安全问题的一个工具,
第二个,从特性来看,首先synchronize是java中的同步关键字;而lock是J.U.C包中提供的接口而这个接口它有很多的实现类其中就包括reentrantLock这样一个重入锁的实现,其次synchronize可以通过两种方式去控制锁的力度,一种是把synchronize关键字修饰在方法层面,另一张种是修饰在代码块上,并且我们可以通过synchronize加锁对象的生命周期来控制锁的作用范围,比如锁对象是静态对象或者类对象那么这个锁就属于全局锁;如果锁对象是普通实例对象,那么这个锁的范围取决于这个实例的生命周期。lock中锁的力度是通过它里面提供的lock( )方法和unlock( )方法来决定的,包裹在两个方法之间的代码是可以保证线程安全的,而锁的作用域取决于lock实例的生命周期。
lock比synchronize的灵活性更高,lock可以自主的去决定什么时候加锁,什么时候释放锁,只需要调用lock()和unlock()方法就可以了。同时lock还提供了非阻塞的竞争锁的方法,叫trylock(),这个方法可以通过返回true/false来告诉当前线程是否已经有其他线程正在使用锁,而synchronize由于是关键字所以他无法去实现非阻塞竞争锁的方法,另外synchronize锁的释放是被动的,就是当synchronize同步代码块执行结束以后或者代码出现异常的时候才会被释放。
最后lock提供了公平锁和非公平锁的机制,公平锁是指线程竞争锁资源时候如果已经有其他线程正在排队或者等待锁释放那么当前竞争锁的线程是无法插队的;而非公平锁就是不管是否有线程在排队等待锁他都会去尝试竞争一次锁,synchronize只提供了一种非公平锁的实现。
第三个,从性能方面来看,synchronize和lock在性能方面相差不大。在实现上会有一定的区别,synchronize引入了偏向锁,轻量级锁,重量级锁,以及锁升级的机制去实现锁的优化,而lock中用到了自旋锁的方式,去实现性能优化,以上就是我对这个问题的理解。
3.说一下死锁
当线程 A 持有独占锁a,并尝试去获取独占锁 b 的同时,线程 B 持有独占锁 b,并尝试获取独占锁 a 的情况下,就会发生 AB 两个线程由于互相持有对方需要的锁,而发生的阻塞现象,我们称为死锁。
产生死锁的原因(1) 因为系统资源不足。(2) 进程运行推进的顺序不合适。(3) 资源分配不当等。
怎样防止死锁
1、尽量使用try lock( )防范,设置超时时间,超时则关闭,防止死锁
2、使用安全类concurrent 代替自己手写锁
3、减少锁的使用粒度,避免几个功能共用一把锁
4、减少同步代码块
5.面向对象
5.2 封装、继承、多态
1.谈谈对面向对象的理解
面向对象 ( Object Oriented ) 是将现实问题构建关系,然后抽象成 类 ( class ),给类定义属性和方法后,再将类实例化成 实例 ( instance ) ,通过访问实例的属性和调用方法来进行使用。面向对象有三大特性,封装、继承和多态。封装隐藏了类的内部实现机制,可以在不影响使用功能的情况下改变类的内部结构,同时也保护了数据。隐藏内部细节,只暴露给外界访问方法。继承,子类继承父类,表明子类是特殊的父类,并且拥有父类不具有的一些属性或方法,Java通过extends关键字实现继承,父类中通过private 定义的变量和方法不会被继承,子类不可以直接操作父类私有的(private)变量和方法,多态指的是类和类的关系,两个类由继承关系存在有方法的重写,因此可以调用父类引用指向子类对象。多态必备三要素,继承,重写,父类引用指向子类对象
2.继承
通过extends关键字来构成继承关系
子类无法继承父类的私有方法,无法继承构造方法
子类在创建对象时,默认会先调用父类的构造方法
支持单继承,具有传递性,耦合性非常强
子类可以修改父类的功能,也可以拓展自己的功能
3.封装
把相关的数据封装成一个类组件,仅对外提供操作数据的方法
4.多态
多态的前提是继承+重写、父类引用指向子类对象
多态对象使用的成员变量是父类的
若使用的方法被重写了则使用的是子类的方法
静态资源 调用谁的就是谁的
不用关心某个对象是什么类型的,就可以直接使用其方法
5.2抽象、接口
1.抽象
Java中被abstract关键字修饰的方法叫抽象方法,只有声明没有方法体。
被abstract关键字修饰的类叫抽象类,可以不包含抽象方法,不可被实例化
子类继承抽象类之后,a.继续抽象,b.重写父类的所有抽象方法
abstract不能与private连用,子类无法重写
abstract不能与static连用,存在加载顺序的问题
abstract不能与final连用,无法重写
2.接口
被interface修饰的类叫接口,理解为是一种特色的类,由全局常量和公共的抽象方法所组成。通过implements来实现接口,接口与类之间可以多实现,接口与接口之间可以实现多继承,降低了耦合性
3.接口与抽象类的区别
实现:抽象类的子类使用extends来继承;接口必须使用implements来实现接口
构造函数:抽象类可以有构造函数;接口没有
实现数量:类可以实现很多个接口;但是只能继承一个抽象类
访问修饰符:接口中的方法默认使用public;抽象类中的方法可以是任意访问修饰符
何时使用
需要为一些类提供公共的实现代码时,应优先考虑抽象类
当注重代码的扩展性跟可维护性时,应当优先采用接口
5.3重写 、 重载
1.重写
重写也可以看做覆盖,子类重新定义父类中具有相同名称和参数的虚函数,函数特征相同,但函数的具体实现不同,它主要在继承关系中出现
1.继承以后,子类就拥有了父类的功能。
2.在子类中,可以添加子类特有的功能,也可以修改父类原有的功能。
3.子类中方法的签名与父类完全一致时,会发生覆盖/复写的现象
4.父类的私有方法不能被重写
5.重写的要求是:两同两小一大两同:方法名 参数列表完全一致
两小:子类返回值类型小于等于父类的返回值类型
子类抛出异常小于等于父类方法抛出异常一大:子类方法的修饰符权限要大于等于父类被重写方法的修饰符权限
2.重载
重载是函数名相同,参数列表不同,重载只是在类的内部存在,但是不能返回类型来判断。
1.重载overload与重写override的区别
重载:是一个类中的现象,同一个类中存在方法名相同,参数列表不同的方法
重写:是指建立了继承关系之后,子类对父类的方法不满意,可以重写,遵循两同两小一大的原则
重载的意义:是为了外界调用方法时方便,不管传入什么样的参数,都可以匹配到对应的同名方法
重写的意义:在不修改源码的情况下,进行功能的修改与拓展(OCP原则:面向修改关闭,面向拓展开放)
5.4final
1.final 的作用
被final修饰的类是最终类,不能被继承
被final修饰的方法是最终实现,不可被重写
被final修饰的变量是常量必须初始化,且不可被改变
2.被final修饰的属性是否可以被修改?为什么
不可以
6.设计模式
1.设计模式有
单例模式(Singleton Pattern):确保一个类只有一个实例,并提供全局访问点。
工厂模式(Factory Pattern):通过工厂类创建对象,隐藏对象的具体实现。
抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无需指定其具体类。
建造者模式(Builder Pattern):将一个复杂对象的构建过程与其表示分离,使同样的构建过程可以创建不同的表示。
原型模式(Prototype Pattern):通过复制已有对象来创建新对象,避免了创建过程中的复杂性。
适配器模式(Adapter Pattern):将一个类的接口转换为客户端所期望的另一个接口。
装饰器模式(Decorator Pattern):动态地给对象添加额外的职责,避免使用子类扩展功能。
代理模式(Proxy Pattern):通过代理对象控制对实际对象的访问,并提供额外的功能。
观察者模式(Observer Pattern):定义了对象之间的一对多依赖关系,当一个对象改变状态时,所有依赖它的对象都会得到通知。
策略模式(Strategy Pattern):定义一系列算法,将每个算法都封装起来,并且使它们可以互换。
模板方法模式(Template Method Pattern):定义一个算法的骨架,将一些步骤延迟到子类中实现。
迭代器模式(Iterator Pattern):提供一种访问容器对象中各个元素的方法,而不需要暴露其内部结构。
2.设计模式的使用场景
单例模式(Singleton Pattern):适用于需要确保只有一个实例存在的情况,比如数据库连接池、日志记录器等。
工厂模式(Factory Pattern):适用于需要根据参数创建不同对象的情况,比如创建不同类型的数据库连接。
抽象工厂模式(Abstract Factory Pattern):适用于需要创建一系列相关对象的情况,比如创建不同操作系统下的窗口、按钮等界面组件。
建造者模式(Builder Pattern):适用于创建复杂对象的情况,通过将对象的构建步骤进行组合和组装,使得创建过程更灵活、可扩展。
原型模式(Prototype Pattern):适用于需要创建大量相似对象的情况,通过复制已有对象来提高创建效率。
适配器模式(Adapter Pattern):适用于需要将一个类的接口转换为另一个类的情况,解决接口不兼容的问题。
装饰器模式(Decorator Pattern):适用于需要动态地为对象添加额外功能的情况,比如为文件流添加缓冲区、加密等操作。
代理模式(Proxy Pattern):适用于需要通过代理对象控制对实际对象的访问的情况,可以实现延迟加载、权限控制等功能。
观察者模式(Observer Pattern):适用于对象间存在一对多的依赖关系,当一个对象状态发生改变时,通知所有依赖它的对象。
策略模式(Strategy Pattern):适用于需要在运行时动态选择算法的情况,通过定义一系列算法并封装起来,使得可以灵活切换算法实现。
模板方法模式(Template Method Pattern):适用于定义一个算法的骨架,将一些步骤延迟到子类实现的情况。
迭代器模式(Iterator Pattern):适用于需要遍历集合对象的情况,通过提供一种统一的访问方式,可以遍历不同类型的集合对象。
7.反射
1.反射是什么
Java 程序开发语言的特征之一,可以在运行时获取一个类的所有信息,可以获取到任何定义的信息(包括成员变量,成员方法,构造器等),并且可以操纵类的字段、方法、构造器等部分。
2.获取class对象的三种方式
对象.getclass()类.classClass.forname("类全路径")
3.反射的运行原理
磁盘中的.java源码通过javac编译成.class字节码文件,classLoad类加载器将.class字节码加载到虚拟机中,文件中的成员变量封装为成员类,方法被封装为成员方法类,构造方法被封装为构造类,运行时则进入运行阶段,加载到内存中变为class类
8.异常
1.常见异常
1、算术异常类:ArithmeticException
2、空指针异常类:NullpointerException
3、类型强制转换异常:ClassCastException
4、数组下标越界异常:ArrayIndexOutOfBoundsException
5、文件未找到异常:FileNotFoundException
6、操作数据库异常:SQLException
7、I/O 异常的根类:IOException
2.异常处理
处理异常有两种方式
1.在方法声明的位置上使用throws关键字抛出,谁调用就抛给谁
2.使用try…catch语句对异常进行捕捉。这个异常不会上报,自己把异常事件处理了。异常抛到此处位置为止,不在上抛了。
3.异常的分类
1.error
Error是非程序异常,即程序不能捕获的异常,一般是编译或者系统性的错误,如OutOfMemorry内存溢出异常等
2.exception
Exception是程序异常类,由程序内部产生。Exception又分为运行时异常,非运行时异常类。
9.常用类
9.1 String
1.String常用的方法有
indexOf():返回指定字符的索引。
charAt():返回指定索引处的字符。
replace():字符串替换。
trim():去除字符串两端空白。
split():分割字符串,返回一个分割后的字符串数组。
getBytes():返回字符串的 byte 类型数组。
length():返回字符串长度。
toLowerCase():将字符串转成小写字母。
toUpperCase():将字符串转成大写字符。
substring():截取字符串。
equals():字符串比较。
2.Stringbuilder / StringBuffer的区别
1.从线程安全方面来说:StringBuffer是线程安全的。StringBuilder是线程不安全的
2. 从执行效率上来说,StringBuilder > StringBuffer > String
3.源码体现:本质上都是在调用父类抽象类AbstractStringBuilder来干活,只不过Buffer把代码加了同步关键字,使得程序可以保证线程安全
9.2 Object
常用方法 toString( ), equles( ) getClass(), hashCode( )
9.3 数组
1.数组常用方法
push : 尾部追加
sort: : 排序
join : 分隔
reverse : 反转
splice :删除元素或者添加元素,
2.数组扩容原理
利用数组复制方法可以变通的实现数组扩容。
System.arraycopy()可以复制数组。
Arrays.copyOf()可以简便的创建数组副本。
创建数组副本的同时将数组长度增加就变通的实现了数组的扩容。
10 其他
Linux基础
Linux常用命令大全
常用指令
解压 tar
查看进程 ps
关闭进程 kill /kill -9
查看日志 grep tail
cookie / session的区别
存储位置不同:session 存储在服务器端;cookie 存储在浏览器端。
安全性不同:cookie 安全性一般,在浏览器存储,可以被伪造和修改。
容量和个数限制:cookie 有容量限制,每个站点下的 cookie 也有个数限制。
存储的多样性:session 可以存储在 Redis 中、数据库中、应用程序中;而 cookie 只能存储在浏览器中。
转发 、 重定向的区别
地址栏 url 显示:foward url 不会发生改变,redirect url 会发生改变;
数据共享:forward 可以共享 request 里的数据,redirect 不能共享;
效率:forward 比 redirect 效率高。
http与https 的区别
HTTP协议以明文方式发送内容,不提供任何方式的数据加密。HTTP协议不适合传输一些敏感信息
https则是具有安全性的ssl加密传输协议
http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
https协议需要到ca申请证书。HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。