xx同学总结的面经

1.在java中,函数传递参数时,基本类型为值传递,对象和数组为引用传递。但传的引用不可在函数中重新new 一个新的地址空间。String虽然为一个对象,但可以认为是值传递,因为每一次改变String的值就相当于重新new了一下。StringBuffer则不会。String还重新了equals()和hashCode()方法。

https://www.cnblogs.com/xxq1991/p/3751349.html

java内存分配和String类型的深度解析:http://www.importnew.com/15671.html

2. if(false && a[-1]=='b') 可以正常编译运行,但if(a[-1]=='b' && false)就是抛出java.lang.ArrayIndexOutOfBoundsException: -1 数组越界异常。

     将两个int型合并为String:若str = a + b + "";则是a和b的值先相加,然后再转为String。若str = a + "" + b,则是将a和b合并成字符串。

      boolean数组默认初始化全为false

3.Object类中的equals方法和“==”是一样的,没有区别,即俩个对象的比较是比较他们的栈内存中存储的内存地址。而String类,Integer类,Character类等等一些类,是重写了equals方法,才使得equals和“==不同”,他们比较的是值是不是相等。

4.java异常分为checked异常和unchecked(runtime)异常,checked异常是必须要显示的处理的,这体现了Java的设计哲学:没有完善错误处理的代码根本没有机会被执行

Checked异常处理方法有两种:

1 当前方法知道如何处理该异常,则用try...catch块来处理该异常。

2 当前方法不知道如何处理,则在定义该方法中声明抛出该异常。

我们比较熟悉的Checked异常有:Java.lang.ClassNotFoundException,Java.lang.NoSuchMetodException,

java.io.IOException

Runtime如除数是0和数组下标越界等,其产生频繁,处理麻烦,若显示申明或者捕获将会对程序的可读性和运行效率影响很大。所以由系统自动检测并将它们交给缺省的异常处理程序。当然如果你有处理要求也可以显示捕获它们。

我们比较熟悉的RumtimeException类的子类有:

Java.lang.ArithmeticException,Java.lang.ArrayStoreExcetpion,Java.lang.ClassCastException,

Java.lang.IndexOutOfBoundsException,Java.lang.NullPointerException

try catch与throws:try类型的会让这个流程继续走下去,不过执行的是catch里面得代码块。throws是抛给了jvm,其实说白了就是这个流程不再执行了。 见测试代码

throws与throw:https://blog.csdn.net/sdr_zd/article/details/75675004

5.java中静态代码块 static{}只会在类被加载到内存时执行一次,之后销毁;初始化代码块 { } 会在每次调用构造函数时执行一次。JDBC中Class.forName(Driver),就是把Driver加载到内存中,执行static{}中的程序。

代码块的执行顺序:静态代码块->构造代码块->构造方法

若有继承关系,那么执行顺序将会是先执行父类的静态代码块,再执行子类的静态代码块,然后执行父类的构造代码块,执行父类的构造方法,再执行子类的构造代码块,执行子类的构造方法。

7.Java反射机制,https://www.cnblogs.com/Eason-S/p/5851078.html。反射机制的优点:只要知道一个类的名字就可以获得该类的信息和创建该类的对象,在工厂模式中,只要传输String型的类的名字就可以创建相应的对象,Factory中不再需要分支来判断,就算再添加新的类也不需要修改Factory类,做到进一步解耦。

Spring中的IOC用的就是java反射机制。

10 自动拆箱与装箱,直接等于常量以及valueOf() https://www.nowcoder.com/profile/789192100/test/16939521/15318#summary

       Integer的值缓存范围:-128至127 https://blog.csdn.net/why15732625998/article/details/79437930

11 java 动态代理vs静态代理  https://blog.csdn.net/hejingyuan6/article/details/36203505

12 final的四种用法:https://www.cnblogs.com/dotgua/p/6357951.html

13 多态:若子类重写了父类的成员变量和方法,则上转型对象只会调用子类重写的方法,调用成员变量依旧是父类的。但在子类中可以访问到父类的成员变量,其实两个变量是同时存在的。

      即:成员变量是父类的,方法是子类的。

集合框架

1.泛型:参数化类型;优点:通过泛型可以定义类型安全的数据结构(类型安全),而无须使用实际的数据类型(可扩展)。这能够显著提高性能并得到更高质量的代码(高性能),因为您可以重用数据处理算法,而无须复制类型特定的代码(可重用)。

2.用迭代器Iterator遍历ArrayList和LinkedList的优点是不需要知道集合的内部结构,只是发出向前、向后遍历就行。其实Iterator()内还是用的get(i)方法获取元素,在遍历ArrayList时与用for循环+get(i)方法遍历时效性差不多,但比用for循环+get(i)方法遍历LinkedList时效性要好得多,因为for循环+get(i)遍历链表,每一次调用get(i)方法都从第0个元素开始直到找到第i个元素(当然,LinkedList是双向链表,会先将i与size比较,判断是属于前一半还是后一半,如果是后一半,就从last向前遍历,总之只遍历一般的链表),而使用Iterator可以根据当前元素来找到下一个元素的位置,不用再重头遍历。

linklist是双向链表的另一个好处是可以很轻易的就反转链表。

因为Set是无序的,因此不能用for循环+get(i)遍历,就没有get()方法。只能用迭代器或foreach遍历。

Iterator()是接口Collection的一个方法,因此list,set的实现类都可用,但是ListIterator只能用来遍历List。Iterator接口提供了很多对集合元素进行迭代的方法。每一个集合类都包含了可以返回迭代器实例的迭代方法。迭代器可以在迭代的过程中删除底层集合的元素,但是不可以直接调用集合的remove(Object Obj)删除,可以通过迭代器的remove()方法删除。

java.util包下面的所有的集合类都是快速失败的,即在遍历的时候集合不能被修改。java.util.concurrent包下面的所有的类都是安全失败的,因为它是拷贝一份集合再进行遍历的。

3.ArrayList:当add()容量够时,就是直接在后面添加,速度很快。

当add()容量不够时,就将新建一个更大的数组(扩充50%),然后把旧数组的内容复制过去。

当在中间位置插入时,会把插入点及后面的数据后移一个位置。然后插入。

当在中间位置删除时,会将删除点后面的数据前移一个位置。

所以说任何时间点,其内存都是连续的,随机索引访问效率很高。插入,删除效率低。或者容量满时add()效率低。

为什么说ArrayList是线程不安全的?https://blog.csdn.net/u012859681/article/details/78206494

4.一个已经实现comparable的类的对象或数据,可以通过Collections.sort(list) 或者Arrays.sort(arr)实现排序。通过Collections.sort(list,Collections.reverseOrder());对list进行倒序排列。

String类,Integer类,Character类等已实现了comparable接口,放入TreeSet或TreeMap中会自动排序;

辨析:Comparable接口与Comparator接口。如需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口),比如引用的为第三方jar包;那么,我们可以新建一个Comparator实现类的比较器来进行排序。

TreeSet<E> 中的元素要实现Comparable接口,因为TreeSet内元素是有序的,而这个排序的标准是通过实现Comparable.comparaTo()方法自定义决定的。

5.Java集合类框架的最佳实践有哪些?牛客网120题第32题。

6.HashMap的容量(capacity),负载因子(load factor)和扩容极限(threshold resizing)。如果负载因子越大,对空间的利用更充分,然而后果是查找效率的降低,因为hashmap是用链表来解决冲突的;如果负载因子太小,那么散列表的数据将过于稀疏,对空间造成严重浪费。系统默认负载因子为0.75,一般情况下我们是无需修改的。

HashMap与HashTable区别:http://www.importnew.com/24822.html

那HashMap为什么会用到红黑树呢?

在JDK1.8之前,HashMap直接用链表来解决hash冲突的,每次空间不足扩容时,会调用transfer()方法将旧空间里的链表挪到新空间里去,但采用的是头部插入的方法。如旧表(旧空间)里的链表是1->5->null,挪到新空间内后就变成了5->1->null,正是因为这个原因,当多个线程同时运行到transfer()时,会出现循环链表的情况,即1->5,5->1(5被从头部插入了两次,一次是在插入1前,一次是在其后),当下次遍历HashMap时就会进入死循环,CPU利用率近100%。这就是HashMap线程不安全的原因https://coolshell.cn/articles/9606.html)。还有一个原因是在用迭代器遍历时,由于Iterator快速失败机制,会抛出ConcurrentModificationException。JDK1.8中,当同一个桶中的元素超过8时,由链表切换成红黑树解决hash冲突,据说也解决了死循环的问题。

红黑树对于平衡二叉树(AVL)的优点:https://blog.csdn.net/mmshixing/article/details/51692892

HashMapHashtableConcurrentHashMap synchronized Map 的原理和区别

Java7、Java8/HashMap、ConcurrentHashMap:http://www.importnew.com/28263.html

ConcurrentHashMap是分段锁机制,JDK1.8后ConcurrentHashMap由锁的分段机制变为CAS(Compare and Swap)。

   https://www.jianshu.com/p/c0642afe03e0      https://blog.csdn.net/programerxiaoer/article/details/80040090                                                                                                                                                                                                                                                                                  

7 hashCode与equals:之所以有hashCode方法,是因为在批量的对象比较中,hashCode要比equals来得快,如hashset是不可以重复的。那么set存储数据的时候是怎样判断存进的数据是否已经存在?假设set中已有100个元素,当再插入一个元素时,若使用equals,则需要比较100次,显然效率很低。若使用hashcode,则一次就行。如果hashcode相同,这时候再使用equals来判断是否是同一个对象地址,而非仅仅是hashcode相同而已。所以如果一个类重写了equals,则一定也要重写hashcode,因为当hashset或hashmap来存储该对象时,首先比较是hashcode,当其相等时才比较

使用HashMap,如果key是自定义的类,就必须重写hashcode()和equals()。https://blog.csdn.net/tuolaji8/article/details/48417031

8  HashSet内部还是用HashMap来实现的,只不过每个value都是同一个名为“PRESENT的object对象。

    TreeMap中使用红黑树保存节点顺序的,每个节点是实现了Map.Entry的一个类的对象,该类中有left,right,parent成员变量。

     TreeSet内部是用TreeMap来实现的。

9  PriorityQueue:优先级队列默认是小根堆排序(根据实现的comparable接口或传入是comparator对象确定),因为Queue底层是数组,堆最合适,而树结构不合适(如平衡二叉树,红黑树)。每次poll()就从堆顶取一个元素,将最后一个元素放到堆顶替代,然后就自顶向下调整一下堆。每次offer()或add()一个元素就放到数组的最后(堆的最后),然后从该元素自底向上调整,直至找到合适的位置。

多线程编程

1.实现Callable接口。相较于实现Runnable接口的方式,方法可以有返回值,并且可以抛出异常。

    Thread类实现了Runnable接口,所以才有run方法。

2.线程同步锁  同步代码块:当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块;但当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

同步方法与同步代码块,静态同步与非静态同步:https://blog.csdn.net/silencecarrot/article/details/52415704或下面红色链接

同步监视器、同步锁:https://blog.csdn.net/weixin_36937689/article/details/72904707

并发包:Synchronized关键字的机制与监视器,锁。https://www.cnblogs.com/longshiyVip/p/5213771.html  亦可见3 volatile关键字链接中的链接:https://www.jianshu.com/p/96c89e6e7e90(内含synchronized和ReentrantLock的原理及区别)  

lock()和lockInterruptibly()的区别:http://www.dewen.net.cn/q/9077  中断锁:即使用lockInterruptibly()加锁,若一直在等待锁,则在别的线程中使本线程调用interrupt()时,此线程就会以抛出InterruptException异常的方式中断。

逻辑上lock锁是对象内存堆中头部的一部分数据。JVM中的每个对象都有一个锁(或互斥锁),任何程序都可以使用它来协调对对象的多线程访问。如果任何线程想要访问该对象的实例变量,那么线程必须拥有该对象的锁(在锁内存区域设置一些标志)。所有其他的线程试图访问该对象的变量必须等到拥有该对象的锁有的线程释放锁(改变标记)。一旦线程拥有一个锁,它可以多次请求相同的锁,但是在其他线程能够使用这个对象之前必须释放相同数量的锁。如果一个线程请求一个对象的锁三次,如果别的线程想拥有该对象的锁,那么之前线程需要 “释放”三次锁。

使用synchronized实现死锁https://blog.csdn.net/a158123/article/details/78616562

3 volatile关键字 Java并发编程的三大概念:原子性,有序性,可见性 。  以及volatile关键字(volatile能保证该变量对所有线程的可见性以及程序执行的有序性,但不保证原子性。)   http://www.importnew.com/24082.html

volatile关键字修饰的变量在第一个线程中被修改时,会立即更新到主存中去,当第二个线程在使用这个变量时(相当于做了读取一次操作),会发现本线程工作内存中的这个变量值无效了,需要从主存中更新。若第二个线程拿到最新的值后做的不是原子性操作,如i++,会分为三步:1.获取i;  2.i+1;  3.写入i。若在进行完第一步时,第三个线程对该变量(i)做了修改,此时第二个线程依旧用第一步的i值进行第二步操作。即volatile不保证原子性

4  i++如何保证线程安全?i++不是原子性操作 所以volatile不能保证i++线程安全。可以用锁(synchronized,lock)保证线程安全,亦可用AtomicIntege代替int i。AtomicIntege用的是CAS机制。

   常见的线程同步方案:

(1)synchronized首选 

(2)ReentrantLock 

(3)Atomic原子类,效率较高,可用于优化 

(4)ThreadLocal,特殊情况上使用,spring有介绍。以后再补充。

5 线程池   https://www.cnblogs.com/dolphin0520/p/3932921.html       https://www.cnblogs.com/aspirant/p/6920418.html

6 线程5中状态 https://www.cnblogs.com/wangyichuan/p/5990821.html

7 并发编程之ThreadLocal、Volatile、synchronized、Atomic关键字  https://blog.csdn.net/u010687392/article/details/50549236

     ThreadLocal  http://www.cnblogs.com/dolphin0520/p/3920407.html

8 sleep() 和 wait() 的区别?sleep()是Thread类的静态方法,wait()是Object类的方法;调用sleep方法的线程不会释放对象锁,而调用wait() 方法会释放对象锁

   start()与run()    直接调用run()方法,只会在主线程中顺序执行run()方法中的代码。

   yield()方法使线程放弃执行权,进入就绪状态。但并不能保证执行权就让给了别的线程,因为下一执行权有可能还会轮到它。

9  CAS机制https://blog.csdn.net/qq_32998153/article/details/79529704

10 Java中的锁 (偏向锁、轻量级锁、自旋锁、重量级锁https://blog.csdn.net/zqz_zqz/article/details/70233767

11 BlockingQueue接口 https://www.cnblogs.com/KingIceMou/p/8075343.html

100 关于以上内容常见的面试题:https://www.cnblogs.com/wangjintao-0623/p/9727272.html

JVM

1.垃圾回收是在内存中存在没有引用的对象或超过作用域的对象时进行。

   图解JVM垃圾回收算法  https://www.cnblogs.com/sunfie/p/5125283.html   四种算法:(1)引用计数算法。及依托于根搜索算法延伸出的(2)标记-清除算法(3)复制算法(4)标记-整理算法。其中复制算法常用于新生代,eden与两个survivor,标记整理算法用于老年代。

JVM 新生代为何需要两个 Survivor 空间?https://blog.csdn.net/mdreamlove/article/details/79462745  当eden区满了的时候,就会触发minor gc,清除eden区和from survivor区,并将存活的对象移动到to survivor区,存活的对象年龄+1,两个survivor交换身份。

finalize()方法释放特殊的区域 :

1)由于在分配内存的时候可能采用了类似 C语言的做法,而非JAVA的通常new做法。这种情况主要发生在native method中,比如native method调用了C/C++方法malloc()函数系列来分配存储空间,但是除非调用free()函数,否则这些内存空间将不会得到释放,那么这个时候就可能造成内存泄漏。但是由于free()方法是在C/C++中的函数,所以finalize()中可以用本地方法来调用它。以释放这些“特殊”的内存空间。

2)又或者打开的文件资源,这些资源不属于垃圾回收器的回收范围。

2. JVM  https://www.cnblogs.com/itar/p/7424311.html      JDK1.8后持久代 -》元空间  加载类的时候,将类加载到方法区,并在堆区生成一个该类的Class类的对象。JVM面试 https://blog.csdn.net/zd836614437/article/details/64126826

3 类加载器:ClassLoader类的作用就是根据一个指定的类的全限定名,找到对应的Class字节码文件,然后加载它转化成一个java.lang.Class类的一个实例.

类加载机制:http://www.importnew.com/25295.html

启动(Bootstrap)类加载器:它不是Java类(另外两个加载器是java类。),因此它不需要被别人加载,它嵌套在Java虚拟机内核里面,也就是JVM启动的时候Bootstrap就已经启动,它是用C++写的二进制代码(不是字节码),它可以去加载别的类。它负责将<Java_Runtime_Home>/lib下面的类库加载到内存中(比如rt.jar)

标准扩展(Extension)类加载器:它负责将< Java_Runtime_Home >/lib/ext或者由系统变量java.ext.dir指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。如我们可以将servlet.jar放在/lib/ext下,就可以直接使用servlet了。

系统(System)类加载器:是由 Sun 的AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径(CLASSPATH,比如WEB-INF下的class和lib文件夹)中指定的类库加载到内存中。开发者可以直接使用系统类加载器。

为什么要使用双亲委派机制?https://blog.csdn.net/qq_36721257/article/details/78290273

对父类和子类的加载顺序:先静态后非静态,先父类后子类https://blog.csdn.net/sunroyfcb/article/details/81637565

系统、当前和线程上下文类加载器:https://www.jianshu.com/p/891c05b0ac05

4 什么是OOM?来源于java.lang.OutOfMemoryError,即内存溢出。内存溢出主要原因:https://blog.csdn.net/skiof007/article/details/79373903(1)虚拟堆内存分配太少了。(2)申请的内存没有释放,造成内存泄漏,gc无法回收,进而导致内存溢出。虽然Java中有自动垃圾回收机制,但如果是死循环或将某个对象的引用放到了全局的Map中,虽然方法结束了,但是由于垃圾回收器会根据对象的引用情况来回收内存,导致该对象不能被及时的回收。

避免方法:(1)多分配内存 (2)对于一些资源的使用,要及时释放,比如在finally中释放。

排查方法:(1)查看服务器日志、GC日志或系统kill进程日志 (2)使用工具查看内存dump文件。

用代码实现堆溢出,栈溢出:https://blog.csdn.net/u011983531/article/details/63250882

5 JMM(Java Memory Model  Java内存模型)Java 的并发采用的是共享内存模型,即每个线程都有自己的工作内存,里面存放在从主存拷贝过来的变量副本。当线程A要与线程B通讯时,A要先将变量从工作内存刷新到主存中去,B再从主存中读取。这个过程是隐式执行的。

JavaEE

框架

(1)Spring:依赖注入的方式:构造方法注入,setter注入,基于注解的注入,接口注入。IOC的实现原理是java的反射机制IOC的优点是,传统的工厂模式虽然可以做到依赖抽象非具体,但依然躲不掉new一个对象,所以当替换一个接口的实现时,依然要修改代码。而Spring将类之间的依赖关系写到配置文件中,修改依赖关系则不需要再修改代码。

AOP:反射机制,代理模式。通过它让我们将业务逻辑从应用服务(如事务管理)中分离出来,实现了高内聚开发(所谓高内聚是指一个软件模块是由相关性很强的代码组成,只负责一项任务,也就是常说的单一责任原则。),应用对象只关注业务逻辑,不再负责其它系统问题(如日志、事务等)。Spring支持用户自定义切面。https://blog.csdn.net/javazejian/article/details/54561302

在OOP中,正是这种分散在各处且与对象核心功能无关的代码(横切代码)的存在,使得模块复用难度增加。AOP则将封装好的对象剖开,找出其中对多个对象产生影响的公共行为,并将其封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),切面将那些与业务无关,却被业务模块共同调用的逻辑提取并封装起来,减少了系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。使用动态代理和反射机制可以为一个或多个接口在运行期动态生成实现对象,生成的对象中实现接口的方法时可以添加增强代码,从而实现AOP。缺点是只能针对接口进行代理,另外由于动态代理是通过反射实现的,有时可能要考虑反射调用的开销。

Spring IOC的工厂在提供对象时还是调用了默认无参的构造方法,如果该bean内没有无参的构造方法,使用工厂获得对象时会抛出异常。在Spring中,从容器中获得的实例默认是singleton,即默认每个bean名称只维护一个bean的实例,若想每次都想获得一个新实例,则把bean的scope属性设置为prototype。

依赖注入三种方式:构造方法传值注入,setter注入(依赖的不是属性,而是属性的setter方法),接口注入。

依赖注入底层用的是反射机制,class类创建对象时要用到该类的一个无参的构造方法

(2)Struts2:执行过程https://blog.csdn.net/snow_7/article/details/51513381

(3)Hibernate:实现的是ORM,优点是程序员不需要会写sql语句。Configuration,SessionFactory,Session的作用:https://www.cnblogs.com/baizhanshi/p/7686959.html

     Hibernate缓存机制:http://www.cnblogs.com/xiaoluo501395377/p/3377604.html

     Hibernate的缺点:不适合批量操作,这也是OR框架的弱点;要使用数据库的特定优化机制的时候,不适合用Hibernate。

框架的优点:为使用框架前,假设表单传递过来100个参数,则需要用100个参数并调用100次request.getParameter()来获得参数,然后写sql语句,再传100值进去才能存入数据库。用了框架,这些问题则迎刃而解。

SpringBoot

上下文(Context)的概念:https://blog.csdn.net/dufufd/article/details/79512803

1.PreparedStatement与Statement,PreparedStatement的优点:

1 可以写动态参数化的查询。

2 SQL语句会预编译在数据库系统中。执行计划同样会被缓存起来,它允许数据库做参数化查询。使用预处理语句比普通的查询更快,因为它做的工作更少(数据库对SQL语句的分析,编译,优化已经在第一次查询前完成了)。

3 可以防止SQL注入式攻击。

详解见:http://www.importnew.com/5006.html

2.Servlet(Server Applet),一般 Servlet 只初始化一次(只调用一次init(),只有一个对象,当 Server 不再需要 Servlet 时(一般当 Server 关闭时),Server 调用 Servlet 的 destroy() 方法。从中我们也能看到同一个类型的Servlet对象在Servlet容器中以单例的形式存在,所以在多线程环境中使用servlet的成员变量要注意线程安全。servlet初始化参数的使用非常依赖于部署描述文件(web.xml),该文件可存放servlet所需要的起始参数以及web应用程序的结构数据。当servlet容器读取web.xml文件内容后。可以将这些起始参数封装成一个对象并在调用init()方法时传递给servlet,这个对象就是ServletConfig对象所以我们可以在Servlet内覆写init方法,并通过ServletCongig对象来取得某些初始参数。

Servelt常用的请求相应方式:https://blog.csdn.net/magi1201/article/details/45271987

web.xml配置Servlet:https://blog.csdn.net/m0_37630602/article/details/65443660

 servlet容器(如tomcat)先将jsp转译成servlet,然后再编译成class文件时,普通的html是通过使用out.println()转换的。

jsp动态include与静态include:https://www.cnblogs.com/zhaideyou/p/5929930.html

3 字节流与字符流 以及编码格式问题 https://blog.csdn.net/pangzhe_sd/article/details/49252591

    字符流牵涉到要转码,所以有个缓冲区。而字符流可以直接对文件操作,但此方法是直接对硬盘操作,会有多次IO,因此字节流操作也有相应的带有缓冲区的类,如bufferinputstream

      tomcat中编译jsp的编码格式情况:<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>

(1)tomcat根据 pageEncoding中编码格式来读取jsp,让后翻译成统一的UTF-8 Java源码(.java文件),所以pageEncoding的值一定要和jsp的编码格式一致。 (2)Javac将统一的UTF-8 .java文件编译成unicode java字节码文件(.class文件)。(3)tomcat根据charset的编码格式生成html发给浏览器,所以charset要与浏览器编码格式一致。

4 Session与Cookie:Cookie在浏览器客户端记录用户的信息,正常情况下,浏览器每次请求服务器都会把相应的Cookie携带过去,除非用户设置浏览器禁用Cookie。Session是与浏览器对应的,数据存储在服务器上。那么每次浏览器来请求访问,服务器是怎么找到此浏览器对应的Session的呢?是因为浏览器访问时,携带的Cookie中含有该Session的ID,所以Session是依赖于Cookie的。 如果说浏览器禁用了Cookie,那么若想用session,浏览器每次请求时就要直接在URL后面把SessionID作为参数传过去。

猜你喜欢

转载自blog.csdn.net/anhuibozhoushatu/article/details/87602343