点点滴滴啊

导航

 

java

基础

  • 简述面向对象

    ​ 面向对象是一种编程思想,把现实中的具体事物或抽象的事物看做对象,并且对象具有某些属性和行为;在java中,某一类事物映射为类,对象就是这一类事物的实例,属性表现为对象的属性,行为表现为对象的方法;在java中体现为抽象、封装、继承、多态;

    抽象:抽象就是把某一类事物中的属性与行为抽象成java中的一个类来表现出来,包括了数据抽象和行为抽象。

    封装:封装是将数据或实现细节隐藏起来,对数据的访问只能通过特别的方式访问。面向对象始于封装,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一系列受保护的接口访问其他对象。

    继承:继承是一种联结类的层次结构,并支持和鼓励重用,它提供了一种明确的表述共性的方法。对象的一个新类可以从一个原有的类中派生,这个过程称为继承;新类继承原有类的特性,新类称为原始类的子类(派生类),原始类称为基类。派生类可以从基类那里继承方法和实例变量,并根据实际需要可以新增或修改新的方法使之符合新类的需要。

    多态:多态允许不同的类可以对同一个行为做出不同响应;多态性在java中体现为运行时多态和编译时多态,运行时多态:父类/接口的变量指向子类、实现类的实例(继承与实现,方法的重写与实现);编译时多态表现为方法的重载。

  • final 修饰符

    ​ final可以修饰类、变量、方法;修饰类时,表示该类不可继承;修饰变量时,变量为常量;修饰方法表示方法不可被重写。final修饰符不能与abstract修饰符一起使用。

  • 接口与抽象类

    ​ 接口是定义一系列方法的声明,使用 interface 来声明,使用 implements 来实现接口,抽象类是使用abstract 关键字声明的类;

    • 抽象类可以有方法实现与实例变量、抽象方法;而接口中只能有方法声明与静态常量,并且必须声明为公共的,JDK1.8支持了接口默认实现方法。
    • 一个类只能继承一个抽象类,而可以实现多个接口,这个类必须实现接口的所有方法或抽象类的所有抽象方法;如果不实现,则需要将类声明为抽象类。
    • 抽象类可以继承一个抽象类,但是可以实现多个接口(可以不用实现接口方法);接口只能继承接口,不能继承类或抽象类。
    • 接口、抽象类或抽象方法不能被 final、static 修饰符修饰;接口和抽象类都不能被实例化
    • 接口不能有构造器,而抽象类可以有构造器。
  • 普通类和抽象类有哪些区别?

    普通类和普通方法可以被static和final修饰,而抽象类、方法不能被static和final修饰;
    普通类不能有抽象方法;抽象类不能被实例化;含有抽象方法的类必须声明为抽象类;
    抽象类的子类实现抽象类,必须实现抽象方法,不然只能声明为抽象类。

  • 对象中的hashCode与equal方法的作用,以及关系

    ​ hashCode用于为集合容器提供hash算法的基础,而equal用于比较对象是否相等。

    ​ 他们之间的关系是hashCode相等,equal不一定相等,但是equal相等,则hashCode必须相等,这样才能保证集合容器的hash算法正确性。

  • == 和 equals 的区别是什么?

    ==:运算符号,比较基础数据类型的值是否相等以及比较引用的是否是同一个对象。
    equals:方法,比较两个对象之间的内容是否相等。

  • 浅克隆与深度克隆

    ​ 浅克隆只能复制对象的基础数据类型值和与其关联对象的引用地址,而复制对象的引用地址之后,还是可以通过该引用地址修改原始对象的状态。深度克隆会进行递归克隆,会克隆原始对象以及与其关联对象的值,修改不会影响原始对象状态。

  • 序列化

    ​ 序列化是将内存中的对象状态转换为可以存储或传输的形式,比如二进制或字符串等,然后在IO流传输或者保存在磁盘中;然后通过读取该数据,进行反序列化,将数据还原为java对象。

  • java的引用类型

    • 强引用

      强引用指实例化出来的引用,如new、反射,在没有被引用时,会被回收。

    • 软引用

      通过 SoftReference 创建出来的对象,在内存不够用时,GC会首先回收该对象。

    • 弱引用
      通过 WeakReference 创建出来的对象,在下一次GC回收时进行回收。

    • 虚引用

      虚引用无法直接使用,需要配合引用队列进行使用,使用 PhantomReference 来实现虚引用;它存在的唯一作用就是当它指向的对象回收时,它本身会被加入到引用队列中,这样我们可以知道它指向的对象何时被销毁。

  • java 值传递

    ​ java使用的是值传递,在运行时,是将参数的值传递给方法里面,如果是引用类型则传递的复制后的对象句柄,当修改该对象句柄不会影响调用者的实参;这里区别于引用传递的是,引用传递传递的是实际的内存指针,修改该参数,会导致调用者的实参被修改;

  • java 数据类型

    ​ 八种基础类型与引用类型,基础类型有 byte、char、short、int、long、double、float、boolean;引用类型指创建对象之后,指向对象在内存中的地址;

  • int 类型

    ​ int类型的数据,-128 ~ 127之间的int型数据,在jvm启动时,对该范围的int数据进行了缓存,缓存的数据是缓存在堆中。

  • 包装类型

    ​ 包装类型是java提供的一种将基础数据类型转换为对象的快捷方式;包装类型有装箱和拆箱机制,装箱机制会将基础数据类型转换为对象,而拆箱会将对象类型转换为基础数据类型,并且装箱和拆箱分为手动和自动,自动是指在某些情况下隐式进行,而手动需要调用相应的API来达到转换。装箱拆箱机制在某些情况下会导致性能消耗。

  • string

    ​ String 是一个不可变的特殊java类;每次创建String,都会在常量池中创建一个 String 对象,在内存中是共享的,如果使用String创建过多字符串,会导致占用大量常量池内存,并且降低程序效率;可以使用 StringBuffer、StringBuilder 来动态创建字符串。StringBuffer是线程安全的,而StringBuilder是线程不安全的;

  • String a = new String("aa"),b = "aa";的区别。

    两种方式创建的对象数不同,变量a可能会创建两个对象,但是最少创建一个对象,而变量b最多创建一个对象。
    变量a是在常量池中找到字符串 "aa" 或创建之后,还会在堆中开辟一块内存空间保存指向常量池字符串的引用,有可能创建两个对象;而变量b只会在常量池中查找是否有 “aa”字符串,如果没有则创建一个,有则直接将地址赋值给栈指针,也就是最多只创建一个对象;

  • 异常

    ​ java中将异常分为两种,error和exception,error代表系统发生重大错误,导致jvm无法正常执行,不可恢复,比如内存溢出;而exception表示的是可以恢复的异常,是程序运行环境不能保证程序正常运行,比如IO异常等。
    ​ exception分为可检查异常和运行时异常。检查异常,代表了某些情况下,不能满足程序的正常运行环境,而运行时异常是在程序运行时发生的,一般是程序逻辑发生错误,导致程序无法按照预期执行。

  • throw 和 throws

    throw 用于抛出异常,而 throws 用于声明方法可能出现异常

  • final、finally、finalize

    ​ final 是java关键字,修饰类时,类不可继承;修饰方法时,方法不可重写;修饰字段时,字段是常量;
    ​ finally 是用于在异常声明块中,保证不能发生什么,都会执行的操作;比如在 try-catch-finally 中,无论发生异常没有都需要释放IO资源;
    ​ finalize 是object的一个方法,用于GC垃圾回收时执行,最多只执行一次,但是由于GC线程是守护线程,所以finalize执行时间不确定,是否执行也不确定;

  • try-catch-finally 中哪个部分可以省略?

    可以省略finally部分;

  • try-catch-finally 中,如果catch中有return,finally 还会执行吗?

    会执行。

    如果try中没有异常,则顺序为try→finally,如果try中有异常,则顺序为try→catch→finally。

    try/catch中的return:在finally前执行,并将返回值暂存,之后执行finally,执行完finally后将返回值返回。引用类型的状态可能在catch和finally中被修改。

    finally中的return(不推荐这么写):会在try/catch中return执行前执行并返回,不会执行try/catch中的return。

  • 常见的异常类有哪些?

    FileNotFoundFileException(文件未找到)、NullPointerException(空指针)、ClassCastException(类型转换异常)、ArrayIndexOutOfBoundsException(数组下标越界)、NumberFormatException(number格式异常)、SQLException(Sql异常)、IOException(IO异常)、SystemException(系统异常)等。

IO

  • java 中 IO 流

    ​ IO 流分为两种,字节流和字符流; InputStream和OutputStream 字节流,write 和 reader 是字符流;

    ​ 字节流是以字节为单位,是所有流的基础,将数据转换为字节,一个字节字节的进行操作;而字符流是在字节流的基础上,封装成以字符为单位的流,底层也是字节流,只是在输入时将字符转换为字节,输出时将字节转为字符。字符流不能处理图像、视频的媒体资源,适合处理字符数据,而字节流可以处理所有类型的数据。字符流需要注意编码,输入和输出编码要一致,不一致会导致乱码。

  • BIO、NIO、AIO

    BIO是阻塞IO,在操作IO时,线程会阻塞等待IO操作完成;
    NIO是同步非阻塞IO,在操作IO时,数据传输是同步的,当前线程可以干别的事情,不会阻塞;非阻塞模式适用于高负载、高并发的网络应用,而阻塞模式适用于低负载、低并发的应用;
    AIO是NIO2,也是异步非阻塞IO,在IO操作后会直接返回,完成IO操作后,会通知线程IO操作完成。

  • NIO与BIO区别

    NIO是一个线程对应一个IO,并且在没有数据读取时,会阻塞线程,如果并发量大,线程数始终无法满足IO操做的需求,导致系统吞吐量变慢(阻塞线程占用了线程资源,线程资源包括内存、句柄等资源)。NIO的多路复用技术,是使用通知的方式通知监听线程来处理IO事件,如果没有IO操作就没有线程处理,也就不会占用系统资源(内存资源、句柄资源) 。

  • NIO

    NIO使用多路复用模型,监听IO事件的发生,使用 select 来选择事件类型(connect、accept、read、write),通过channel读取数据写入到buffer或从buffer写数据到channel中。

    • Channel

      双向读写,可支持多种协议,如Socket、TCP、UDP以及IO。

    • Buffer

      数据缓冲区,对应了ByteBuffer、CharBuffer、DoubleBuffer、 FloatBuffer、IntBuffer、 LongBuffer,、ShortBuffer,分别对应基本数据类型: byte、char、double、 float、int、 long、 short。

      NIO中还有MappedByteBuffer, HeapByteBuffer, DirectByteBuffer等。HeapByteBuffer从jvm堆内存中分配内存,DirectByteBuffer从系统内存中分配,少了一步从系统内存复制到堆内存的操作。

    • Selector

      NIO实现多路复用的基础,使用selector监听多个channel,当其中一个channel发生IO事件,就调用对应的逻辑进行处理。

    • SelectionKey

      获取监听事件,并进行处理。

常用API

  1. java 中的 Math.round(-1.5) 等于多少?

    ​ -1,round是四舍五入,在原始值基础上加0.5然后向下取整。

  2. java中操作字符串都有哪些类?它们之间有什么区别?

    ​ String、StringBuffer、StringBuilder,string是不可变的,如果大量对string进行拼接,推荐使用stringBuilder;StringBuffer是线程安全的,StringBuilder 是线程不安全的。

  3. String 类的常用方法都有那些?

    toString、charAt、indexOf、split、 replace、matches、toLowerCase、toUpperCase 等。

  4. Files 的常用方法都有哪些?

    ​ 根据文件创建流、创建文件、创建文件夹、判断文件是否存在、删除文件、移动文件、复制文件、读取文件、写入文件等操作。

虚拟机


  • 简述堆栈(java内存结构)

    • 程序计数器

      线程私有,用于标记每一个线程执行字节码到哪一行。

    • java虚拟机栈

      虚拟机栈是线程私有的;虚拟机栈用于描述java方法执行的内存模型:每个方法在执行时都会创建一个栈帧用于存储方法的局部变量表、操作数栈、方法出口信息等,一个方法的执行过程代表着一个栈帧在虚拟机栈中的入栈与出栈过程。栈帧中的局部变量表包含着编译期可知的各种基础数据类型与对象引用类型。

    • java本地方法栈

    • 是java虚拟机中内存占用最大的一块,并且所有线程共享该区域内存;在程序运行中,所有对象实例都在堆中分配内存,并且GC回收器主要是对堆进行回收;方法区、运行时常量池、字符串常量池也在堆中

    • 方法区

      也叫永久带,1.8移除,变更为元空间(本地内存),在堆中分配的一块内存空间,用于存放类信息、常量、静态变量、即时编译后的代码数据等。

    • 运行时常量池

      用于存放类中的常量池信息,也就是编译期生成的各种字面量和符号引用;也可以在运行期间将新的常量放入常量池中,比如String.intern(),存放在元空间。

    • 字符串常量池

      字符串常量池,用于存放字符串实例,jdk1.6存放在方法区,而jdk1.7移动到堆中

  • 类加载器

    ​ 类加载器分为 启动类加载器、扩展类加载器、应用程序类加载器;java虚拟机采用双亲委派机制进行类的加载,当加载一个类时,首先委托给父类加载器进行加载,当父类加载器无法加载时,由子加载器进行加载,如果没有加载器能够加载将会抛出异常,使用双亲委派机制会给类带来层级关系,一个类不会被加载两次,java中最基础的类会被首先加载,这样保证了jvm基础行为。

  • 类加载顺序

    加载 -> 验证 -> 准备 -> 解析 -> 初始化

    • 加载

      加载class文件流,如从网络加载、本地文件、数据库、java文件编译成class

    • 验证

      验证class文件的格式是否满足jvm虚拟机的要求

    • 准备

      在该阶段为类变量分配内存空间并且进行初始化(设置为零值),对静态常量进行赋值操作。

    • 解析

      将类中使用到的符号引用解析为直接引用。

    • 初始化

      开始执行程序代码,按照程序给类变量进行赋值。

  • 类实例化顺序

    父类静态变量 -> 父类静态块 -> 子类静态变量 -> 子类静态代码块 -> 父类属性 -> 父类构造器 -> 子类属性 -> 子类构造器。

  • GC算法

    标记清除:标记清除将会导致内存零碎
    标记整理:虚拟机暂停时间不可控
    复制算法:内存浪费
    分代:根据生命周期长短进行分类;首先回收生命周期短的对象,并整理内存,并将生命周期长的对象放入单独的内存区域。好处是内存回收时间短,但是实现复杂。

  • GC收集器

    Serial、Serial old、parnew、parallel scavenge、CMS、parallel old、G1

集合

  • Collection 和 map 接口是集合框架内所有集合的父接口。

    Collection 的子接口有 List 和 Set接口 ,Map 的实现有 HashMap、TreeMap、Hashtable、LinkedHashMap、ConcurrentHashMap、Properties 等。

    List接口实现有 ArrayList、LinkedList等,Set接口实现有 HashSet、TreeSet、LinkedHashSet 等

  • Collection 和 Collections 有什么区别?

    Collection是list和set的公共接口,Collections是集合的工具类。

  • Collections的主要方法

    主要有将集合封装为不可变的集合、返回集合的安全视图、创建空集合、将集合封装为同步集合等。

  • List、Set、Map区别

    List接口存储一组可重复的对象;Set存储一组不重复的对象;Map使用键值对存储,内部维护与key关联的值,key与value,key不能重复。

    List是无序集合,与数组一样,按照存入顺序进行进行排序,通过下标进行获取数据,也可以通过 Iterator 进行遍历;

    Set是有序集合,并且不能重复,虽然与list都是继承自Collection,但是实现却不一样,list使用Array,而set使用map进行实现;

    Map是键值对映射集合,通过关键字能快速查询某项数据的时候使用。

  • List

    • ArrayList
      • 线程不安全,容量可动态扩展,按存储顺序有序;
      • 底层实现使用数组;
      • 插入根据位置不同而效率不同,如果插入末尾,则效率是O(1),而在指定位置 i 插入或删除元素,则效率是 O(n-i),因为插入在指定位置时,需要将指定位置与指定位置之后的元素往后移动,而删除时需要将删除元素后的元素往前移动;
      • 支持快速随机访问;
      • 内存占用由于会预留空间,所以内存占用有部分浪费;
    • LinkedList
      • 线程不安全
      • 底层使用双向链表实现
      • 插入效率由于是使用链表实现,所以插入、删除效率都是 O(1),但是修改则不是,需要进行遍历查找,然后修改值,效率低。
      • 由于使用链表实现,不支持高效的快速随机访问。只能按照顺序、反序进行访问。可以实现队列
      • 内存由于是使用链表形式,耗费在每一个元素需要维护上一个元素与下一个元素的引用。
    • CopyOnWriteArrayList
      • 不推荐使用,每次修改都会复制数组,代价高,并且不知道存放了多少数据,可能会导致内存故障,也不是实时一致,只支持最终一致。
      • 线程安全,但是只支持最终一致性,不支持实时一致性。
      • 实现:使用读写分离来实现高并发,在增加、修改、删除操作时,都将原始数组替换为修改之后的数组,在修改操作期间,其他线程读取数据时读取的数据还是原来的数据,而修改操作完成之后,就可以读取到新的数据,这样就不会导致数据紊乱。并且增修改都使用了lock锁,并将底层数据使用volatile 修饰来保证对数组的修改能被其他线程可见。
      • 与 ArrayList 一样,底层使用数组进行实现,支持快速随机访问,并且插入效率一样受插入方式影响。
      • 内存占用大,在修改操作时,会有两个数组同时驻存在内存中。
  • Array和ArrayList的区别

    ​ ArrayList 底层是使用 Array 实现的;

    ​ Array 可以装基础数据类型,ArrayList 只能装引用类型(基础数据类型的包装类);

    ​ Array大小是固定的,ArrayList可以根据需要自动扩容;

    ​ Array 效率高,ArrayList由于需要扩容,效率比 array 低;

    ​ ArrayList 提供了基础的操作接口,操作元素比 Array 方便;

  • ArrayList 和 LinkedList 的区别是什么?

    ArrayList 底层实现是数组,随机访问元素效率高,向数组尾部添加元素效率高,但是在中间添加元素或者删除元素时,需要移动数组,最坏的情况是删除list的第一个元素,需要把后面的所有元素往前移动;ArrayList 访问效率高,但是添加和删除的效率低;

    LinkededList 底层是使用链表实现的,每个元素都有指向上一个元素和下一个元素的指针,添加或删除元素只需要把操作指针就行,不需要扩容,效率高;但由于是链表形式的,需要从头遍历来查找元素,查找效率低;

  • ArrayList 和 Vector 的区别是什么?

    ​ ArrayList和Vector 底层都是使用数组实现,但是 ArrayList 是线程不安全的,而 Vector 是线程安全的;所以不考虑线程安全,Vector效率比ArrayList低;ArrayList扩容为1.5倍,Vector扩容为2倍。

  • 如何实现数组和 List 之间的转换?

    使用 Array.asList(), 或者使用 ArrayList 的 addAll 方法,将数组转为 list;使用 List.toArray 将list转为数组。Array.asList()返回的数组是一个不可变的ArrayList。

  • set

    • HashSet
      • 线程不安全
      • 底层实现使用 HashMap,数据存储在key中,所有的value都设置为同一个常量对象。
      • 不能重复,只能有一个元素为null
      • 快速存取,无序。
    • TreeSet
      • 线程不安全
      • 底层使用 TreeMap (红黑树) 实现,数据存储在key,所有value设置为同一个常量对象,不使用value。
      • 不能存储null
      • 有序,根据实现 Comparable.compareTo 来进行排序。
      • 不支持快速添加,因为需要进行排序。
    • LinkedHashSet
      • 线程不安全
      • 使用 LinkedHashMap 实现,根据HashCode来决定元素存储位置,但是另外使用链表保存了原始的插入顺序
      • 可以存储null值
      • 迭代速度比HashSet快,但是插入、修改、删除时比HashSet慢,总体比TreeSet快。
  • 说一下 HashSet 的实现原理?

    hashSet 底层使用 hashMap 来实现,基本上所有操作委托给hashMap的。

  • map

    • HashMap
      • 线程不安全
      • 使用hash算法进行存储,equal 判断对象是否相等;同一个位置使用链表保存多个数据,达到一定深度,转换为红黑树进行存储。
      • 自动扩容,大小扩大2倍
      • value可以为空,key只能有一个null值
      • 支持快速存取
    • LinkedHashMap
      • 线程不安全
      • 使用hash算法进行存储,equal 判断对象是否相等;
      • 实现是继承自 HashMap,添加了顺序链表,能够根据添加顺序进行遍历
      • value可以为空,key只能有一个null值
      • 迭代遍历速度比HashMap快,但是插入、修改、删除时较HashMap慢
    • TreeMap
      • 线程不安全
      • 使用红黑树实现
      • 有序
      • 性能较 HashMap、LinkedHashMap 低
      • key 不能为null
    • Hashtable
      • 线程安全,使用synchronized关键字实现
      • 使用hash算法进行存储,但是hashCode直接使用对象的hashCode,而hashMap内部重新进行了再次hash
      • 自动扩容,大小扩大2倍+1
      • 不允许null键和null值
      • 写入较慢,性能比HashMap低,能安全的进行迭代
    • ConcurrentHashMap
      • 线程安全,1.7使用 分段锁 + 链表,而1.8使用cas\synchronized 和 链表转红黑树来实现
      • 使用hash算法进行存储,equal 判断对象是否相等;
      • 不允许null key与null value
      • 无序
      • 性能比hashTable好,较HashMap低
  • HashMap 和 Hashtable 有什么区别?

    HashMap继承自AbstractMap,而hashTable继承自 Dictionary 类。

    HashMap是线程不安全的,而HashTable是线程安全的,而hashMap在单线程中效率高,HashTable在单线程中效率低。

    hashMap可以存放Null值,而HashTable不行。 hashMap扩容是为2倍,而hashTable 为 2n+1倍,初始容量不一样,hashMap初始容量为16(2的n次方),而hashTable为11(素数)。

  • 如何决定使用 HashMap 还是 TreeMap?

    需要对key进行排序,选择使用treeMap,而添加、修改、删除、查询操作频繁使用hashMap;

  • 说一下 HashMap 的实现原理?

    hashMap实现原理:对key 进行 hash,得到hashcode,然后把key和对应的值存入到数组中(key和value组成 map 的内部数据结构 Map.Entry);如果 key 有相同的hashcode,存放到数组的同一个位置,然后把当前的entry.next 值指向之前的entry,形成一个链表;如果元素个数超过 数组长度 * 加载因子,就进行扩容;扩容是把数组扩大两倍;

  • 在 Queue 中 poll()和 remove()有什么区别?

    poll 与 remove 都返回队列头的数据,但是 poll 在队列头数据为null的情况下,返回null,而remove会抛出异常。

    peek和element区别:他们都是不移除的方式查看队列头数据,peek在队列头为空是,返回null,而element会抛出异常。

    add和offer:都是像向队列添加数据,add在队列满时会抛出异常,offer在队列满时添加返回false。

  • 哪些集合类是线程安全的?

    HashTable、ConcurrentHashMap、Vector、CopyOnWriteArrayList、CopyOnWriteArraySet;

  • 迭代器 Iterator 是什么?

    迭代器是用于循环查看集合中的元素接口,提供了判断集合中是否有元素、获取下一个元素的方法;

  • Iterator 怎么使用?有什么特点?

    一般与 while 结合使用,首先判断是否有下一个元素,如果有,则获取下一个元素;特点是,在调用 iterator 方法时,不能移除集合中的元素,否则会抛出异常;

  • Iterator 和 ListIterator 有什么区别?

    Iterator 提供了基本的迭代方法,而 ListIterator 在iterator基础上进行了扩展,专用于迭代list,能够反向迭代,在迭代时能删除元素、添加元素、修改元素list中的数据;

  • 怎么确保一个集合不能被修改?

    使用 Collections 类对集合进行同步包装,或者在操作集合类的方法中添加锁,(synchronized 关键字和 lock锁)。

线程

  • 线程

    ​ jvm运行在操作系统上的进程中,进程之间内存是独立的;而线程是运行在进程中的基础单元,相当于进程内的多个子任务,线程共享进程的内存,并且线程之间上下文切换代价比进程低;但是线程由于共享进程的内存,所以会造成编程复杂度提高,会引发多线程之间的同步问题。

  • 线程的状态

    新建、就绪、运行、阻塞、死亡

    新建:线程实例创建出来,但是线程还未启动,调用start方法进入可运行状态。

    运行:线程处于运行状态或者等待CPU分片状态,在线程执行中,调用sleep、wait等方法,从运行状态进入阻塞中,以及线程执行完成线程销毁进入死亡状态。

    阻塞:调用Thread.sleep、Object.wait、Thread.join等方法后,需要等待休眠时间到达或者被其他线程调用 Object.notifyAll 、Object.notify或其他线程执行完成后唤醒,进入可运行状态。

    死亡:当线程执行完成或者其他终止条件时,进入死亡状态。

  • 守护线程

    ​ 守护线程是一类特殊的线程;守护线程是一个在jvm后台执行的线程,执行优先级 非常低,用于服务其他非守护线程或任务,垃圾回收线程就是典型的守护线程;当jvm中所有线程都是守护线程,jvm就可以退出,而如果有非守护线程,则不能退出;

  • 线程安全

    ​ 线程安全是指在多线程数据共享的情况下,在同步机制的保证下,程序能正确的运行并且运行结果是预期的。

  • 竞态条件

    ​ 指多个线程对一个资源进行竞争。因为线程是随机竞争资源的,需要使用同步机制来保证在竞争时程序是按预期运行。

  • 异步与同步

    ​ 同步是调用者需要等待被调用者的消息。

    ​ 异步是调用者不需要等待被调用立即将消息告知,之后被调用者通过消息通知机制(状态标识、回调、通知)将消息传递给调用者。

  • 并行与并发

    ​ 并行指能CPU时间分配下多个任务能循环渐进的执行(单cpu轮询);并发是指能同时进行多个任务(多cpu同时执行任务)。

  • 阻塞与非阻塞

    阻塞指的是调用者在调用时,线程会被阻塞,一直到被调用者执行完成。

    非阻塞指调用者调用时,线程不会被阻塞。

    于同步异步的区别在于,同步异步关注的是消息通知,而阻塞非阻塞关注的是调用者状态。

  • java线程调度

    线程调度指系统为线程分配处理器使用权的过程。线程调度方式有两种,抢占式线程调度与协同式线程调度。java使用抢占式线程调度。

    协同式:由线程控制CPU使用权,线程执行完成主动通知系统,系统将CPU使用权交由其他线程,好处是切换线程时可知的,不会有线程安全问题,但是不能控制CPU占用的时间,会导致系统崩溃。

    抢占式:由操作系统为线程分配CPU使用权。缺点是CPU使用权切换不可知,有线程安全问题,好处是CPU使用时间由系统控制,保证了CPU不会被一直占用。

    java的线程调度算法:根据线程的优先级和线程饥饿情况来等数据算出一个优先级来分配CPU执行时间片。

  • java中加锁的方式有几种

    synchronized关键字,同步方法、同步静态方法、同步块来实现加锁。同步方法加锁的对象是this,静态同步方法加锁对象是方法所属的class,同步块加锁对象是目标对象。

    重入锁 ReentrantLock,需要显示的加锁与释放锁,另外可以使用 ReentrantReadWriteLock 锁,ReentrantReadWriteLock可以将锁分为读锁与写锁,读锁可以运行多个线程访问,但是写锁只能有一个线程访问,线程访问写锁后,其他线程不能访问读锁。

    synchronized与Lock区别:

    • synchronized 是虚拟机提供的关键字,而lock是java类库提供的接口。
    • lock需要手动释放,而synchronized 不需要。
    • lock可以有策略的分配锁,如公平锁,并且lock可中断,synchronized则能中断、不公平。
    • synchronized 不可以判断获取锁的状态,而lock可以。
    • synchronized 不可以放弃获取锁,而lock可以超时放弃获取锁(tryLock)。
  • synchronized 和 volatile 的区别

    ​ synchronized是用于加锁,保证线程同步,不能停止CPU重排序;而 volatile 是保证对象的改变对其他线程可见,不会造成缓存不一致,可以禁止重排序,但不能保证线程安全。

  • 如何获取锁的状态

    通过thread的holdsLock方法来获取当前线程是否持有某个对象的锁,并且执行该方法的线程必须持有该锁才返回true。ReentrantLock.isLocked 获取同步锁状态。

  • 线程执行时发生异常

    线程发生异常并且没有被捕获会导致线程退出。thread类提供了一个异常发生时处理的接口,来自定义线程异常发生时的处理方法,但是不能使线程恢复。

  • volatile 的作用

    volatile 关键字用于修饰变量,修饰之后,变量的修改总是对其他线程可见,也就是保证变量的可见性,并且可以保证指令不被重排序。

  • jvm内存模型规则

    jvm内存模型规则是保证java程序在不同内存架构、CPU架构中有确定性的行为。java线程模型为多线程发生的变动被其他线程可见提供了保证。

    1. 程序次序规则:线程内的代码能够按照先后顺序执行
    2. 管程锁定规则:对于同一个锁,解锁操作总是发生在另一个加锁操作之前
    3. volatile规则:前一个对 volatile 的写总是发生在后一个 volatile 读之前
    4. 线程启动规则:一个线程的执行总是发生在调用start方法之后
    5. 线程终止规则:线程的所有操作总是发生在线程终止之前
    6. 对象终结规则:一个对象的终结总是发生在一个对象创建之后
    7. 可传递性:A操作发生在B之前,B发生在C之前,那么A一定发生在C之前
  • 单例模式双检锁

    双检锁是指在实例化对象前,先判断对象不存在,然后加锁,再一次判断对象是否存在,如不存在则创建对象。缺点在于赋值操作不是原子的,需要添加 volatile 关键字让指令重排序功能失效,并且对其他线程立即可见。

  • 死锁

    死锁形成是因为持有锁的线程都持有另外线程需要的锁,并且需要另外线程持有的锁,但是没有释放锁的意愿,导致死锁。

    破解死锁只需要破坏其中任意一个条件,1. 规定获取锁的顺序,2. 一定时间内获取不到锁,释放已经持有的锁,3. 检查需要的锁是否满足要求,不满足则等待到锁资源满足条件。

  • ThreadLocal 变量

    java线程本地变量,是使用空间换取线程安全;为每一个线程都单独绑定一个变量的副本(副本不互相影响),线程之间就不需要进行同步操作,节省了同步需要消耗的时间,加快了程序的运行效率。

  • wait()为什么声明在Object类中
    wait方法用于让当前线程进入休眠状态,并且释放锁,只能在同步块中使用。

    java提供的锁是对象锁,在java多线程中,多线程竞争锁都是竞争的对象的锁,通过将wait()声明在基类Object中,可以用于区分当前线程需要的是哪一个对象锁,并通过直接操作Object提供的多线程通信方法,来让多线程间的通信更加明确。

  • 同步块内发生异常会发什么

    同步块内发生异常,synchronized 无论如何都会释放锁,而 lock 接口需要在 finally 中手动释放锁保证发生异常时能正常释放锁。

  • 多线程中的忙循环

    忙循环是指空循环,在满足条件下,一直执行,不释放CPU资源,这么做的目的是是保留CPU缓存;因为一个线程醒来之后,可能在其他CPU核心建立缓存,而使用忙循环可以避免重建CPU缓存以及可以直接使用原来的缓存,而不需要等待重建缓存的时间。

  • wait、notify、notifyAll、join、sleep、yield

    wait:当前线程进入阻塞状态,让出CPU执行时间,释放锁资源,需要由其他线程调用notify、notifyAll进行唤醒,然后进入等待锁队列;

    notify:唤醒某一个线程,但是唤醒的是哪一个线程不确定。

    notifyAll:唤醒等待锁队列中的所有的线程,然后由这些线程竞争锁。

    join:调用thread.join的线程需要等待thread代表的线程执行完成,然后才能继续执行调用线程。

    sleep:休眠一定时间,让出CPU,不释放锁资源,进入阻塞状态。

    yield释放CPU资源,不释放锁,进入就绪状态,让其他相同优先级或更高优先级线程执行,但是可能当前线程又获取到CPU资源继续执行。

  • 为什么要将 wait 方法放入循环中

    在线程wait期间,线程执行依赖的逻辑判断条件是否改变无法保证,所以把wait放入循环中,重新判断一次,以保证程序的逻辑正确。

  • 为什么 wait 和 notify 必须在同步块中执行

    原因在于wait和notify之间的关系,他们是用于线程之间的通信,并且wait会释放锁,也就是说已经拥有锁,所以需要在同步块内调用,不在同步块内调用无法保证wait和 notify 的执行顺序,另外也失去了线程通信的意义。

  • 实现线程的几种方式

    继承Thread类、实现Runnable接口,继承Thread类可以直接实现一个线程,但是java中是单继承的,如果需要继承其他类就不能使用继承Thread类了;而实现Runnable接口可以完美的避免单继承,因为可以实现多个接口。

  • CAS原理

    cas是 compare and swap 的缩写。基本原理是:内存中的当前值(V),将内存中的值(V)保存在当前线程的副本值(A)中,准备更新的值(B);将(V)与(A)进行比较,就可以知道在这期间是否有其他线程对该值进行了修改,如果没有修改,则将(V)替换为(B),如果修改了,则重新读取内存的值(V),保存在当前线程的临时副本(A)中,重复上面的操作到修改成功为止;现代处理器提供了保证比较与交换操作是原子的指令。

    具体操作:当改变一个变量a时,首先从内存中将a读取出来保存在当前线程的临时变量b中,然后进行比较,
    当a=b时,说明没有线程对值进行l修改,将a的值替换为需要更改的值;当a!=b时,说明有其他线程更改了值,则从内存中获取a的最新值并保存在b中,然后继续之前比较操作。

    • ABA问题

      ABA问题是指,例如在线程A中,读取到的值是1,而在比较交互之前,线程B将值修改为2,然后线程B或者另外的线程将值修改为1,然后线程A运行,读取到的值还是1,导致的后果是,线程A看现在的值还以为是以前的值。

    在juc包下,提供了 AtomicStampedReference 来解决ABA问题。在AtomicStampedReference中,为变量值单独保存了一个版本号,每次修改时,比较版本号是否大于等于当前版本号;如果大于等于,则修改值并且版本号增加;如果小于,则说明当前值被修改,则修改失败,通过版本号来保证修改不会丢失。

  • 自旋锁与互斥锁

    自旋锁指的是,在没有获取到锁时,将一直循环的尝试去获取锁,知道获取到锁为止,在这期间,由于是一直循环,并不会释放CPU,可以等待锁释放之后立即获取。

    互斥锁:在没有获取锁时,进入阻塞状态等待锁被释放,不能及时的获取锁。

  • 锁升级

    • 偏向锁

      偏向锁适用于没有线程竞争的同步操作,加锁无需额外的消耗,和非同步方法之间性能差不多;偏向锁预计基本上没有线程竞争锁,当有多个线程使用锁时,自动升级为轻量级锁;

      比较锁的对象头中的threadID,如果当前线程的ThreadId和对象头中的threadId不一致,升级为轻量级锁;

    • 轻量级锁

      轻量级锁适用于少量线程竞争锁的情况,且持锁时间短。轻量级锁,线程的竞争不会阻塞,使用自适应自旋,提高程序的响应速度。
      在两个线程竞争锁时,有更其他的线程来获取锁,升级为重量级锁。

      线程1在栈帧中复制对象头的MarkWord,然后使用CAS将对象头中的lock record替换为替换为当线程1栈帧中lock record,如果在CAS时,有线程2进行同样操作,则会导致线程2进行CAS操作,(1.这时如果线程2自旋超过一定次数)或 (2.有其他线程,如线程3需要获取锁,而线程1还没有释放锁,或者线程1还在执行,线程2还在自旋等待),则升级为重量级锁,会把未拥有锁的线程进行阻塞,防止CAS空转。

    • 重量级锁

      升级为重量级锁后,就不会进行锁降级;重量级锁,适用于多个线程竞争锁,锁的持有时间长的场景。

    • 锁粗化

      加锁时,按理同步块作用域应该越小越好,但是如果存在连续加锁的情况下,会导致加锁的性能消耗变大,JVM会将这多个且连续的加锁操作连接在一起,扩展为一个范围更大加锁操作,避免频繁加锁释放锁操作带来的性能消耗。

    • 锁消除

      Java虚拟机在JIT编译时,会对运行时上下文进行分析,去除没有竞争的加锁操作,来把没有必要的加锁操作清除掉,节省没有用的加锁释放锁带来的性能消耗。

  • JUC包下的常用类

    • 并发工具类

      • Callable 接口

        与Runnable类似,但是Runnable不会返回执行结构,而Callable可以返回执行结果。

      • FutureTask类

        可以取消的异步运算,可以获取的线程执行结果,并且可以取消,与Callable或Runnable结合使用

      • CountDownLatch 类

        支持一个线程等待一组线程执行完成之后在继续执行。通过给多个线程分发标识,通过标识来判断自己等待执行的线程是否执行完成,这些标识不可以重复利用。

      • CyclicBarrier

        与 CountDownLatch一样,区别在于 CyclicBarrier 的标识可以重复利用,并且 CyclicBarrier 与 CountDownLatch 最大的区别在于: CyclicBarrier 是一组线程彼此等待,直到最后一个线程达到,在一起继续后续操作,继续互相等待,也就是重复利用标识;而 CountDownLatch 是一个线程等待一组线程。

      • Phaser

        与 CyclicBarrier 类似,但是 CyclicBarrier 的参与者数量不可变,而 Phaser则可以随时增加或减少参与者的数量。

      • Semaphore 类

        Semaphore 是一个信号量同步类,当线程执行时,需要从 Semaphore 中获取信号量之后才能执行,如果获取不到信号量,则进行等待。可以实现在同一时间内,只有一组线程中几个获取到了信号量的线程进行执行,其他线程则需要等待信号量释放之后才能执行。
        如果信号量不释放,则永远进入等待状态,除非超时。

      • Exchanger

        用于线程之间交换数据,当两个线程到达交换点之后,进行数据的交换。

    • atomic 包

      atomic 包下提供了一系列原子变量类,支持原子操作

    • 并发容器类

      线程安全容器

      1. ConcurrentHashMap:并发版 HashMap
      2. CopyOnWriteArrayList:并发版 ArrayList
      3. CopyOnWriteArraySet:并发 Set
      4. ConcurrentLinkedQueue:并发队列 (基于链表)
      5. ConcurrentLinkedDeque:并发队列 (基于双向链表)
      6. ConcurrentSkipListMap:基于跳表的并发 Map
      7. ConcurrentSkipListSet:基于跳表的并发 Set
      8. ArrayBlockingQueue:阻塞队列 (基于数组)
      9. LinkedBlockingQueue:链表阻塞队列
      10. LinkedBlockingDeque:阻塞队列 (基于双向链表)
      11. PriorityBlockingQueue:线程安全的优先队列
      12. SynchronousQueue:读写成对的队列
      13. LinkedTransferQueue:基于链表的数据交换队列
      14. DelayQueue:延时队列
  • 线程池

    (1)创建线程花费的大量的时间和资源,使用线程池预先创建一定数量的线程,达到热启动的目的。

    (2)管理线程的创建、销毁、创建数量、销毁等,来节省自己创建线程所花费的时间。

    (3)可以达到重复利用线程目的;

    ​ java提供的线程池有几个重要的参数,1. 线程核心数量、2. 线程最大数量、3.线程最大空闲时间、4.任务队列、5.创建线程所用的工厂、6.超出任务队列数量时的处理策略。线程数量变化:当任务数超过线程池核心数量后,新任务保存在队列中,当队列满了之后,将创建线程,不超过最大数量,来处理任务,如果线程数达到最大数量还不能满足,并且任务队列满,将调用线程溢出处理程序。

    ​ 可以通过工厂类 Executors 来创建jdk提供的默认线程池类型,有4种类型,固定线程数量线程池、单一线程数量线程池、缓存线程池、周期性处理任务线程池

  • 线程池状态

    线程池的状态有5种;
    RUNNING:线程池能够接受线程,并且执行线程;
    SHUTDOWN:线程池关闭,不在接受新建线程,但继续处理已经接受的线程。
    STOP:线程池不在接受新任务,并且不在执行任务,stop之前的任务也会放弃执行
    TIDYING:线程池中没有任务需要执行,会变为  TIDYING 状态
    TERMINATED:线程池彻底终止,死亡;
    
  • 线程池中 submit()和 execute()方法的区别

    ​ submit与execute 都是提交线程让线程池执行,但是 submit 与 execute 接受的参数不同,submit 能接收 runnable、callable接口,而 execute 只能接收 Runnable 的参数类型;

    submit 能返回线程执行结果,并且能获取抛出的异常信息,而 execute 不能返回线程执行结果也不能获取线程执行异常的详细信息;

反射

  • 反射

    反射是指在运行时获取java对象的属性和调用java方法。

  • 常见使用反射框架

    Spring、Mybatis、web服务器调用servlet方法、JDBC使用反射加载数据库驱动等等。

  • 反射的优缺点

    提高了java的灵活性,可以在运行时创建对象、判断对象所属类、获取对象属性、调用对象方法。

    由于反射需要解析class对象,来解析对象,执行效率较慢。

  • 反射的作用

    在运行时判断对象所属的类、运行时构建类的对象、运行时调用任意一个对象的方法。

    由于反射可以在java程序运行时获取到类的所有信息,那么就可以通过类的信息来操作对象。

  • java 持久化

    ​ 序列化指将 java 对象转换成字节流,传输到网络中或者保存到磁盘中;在需要将对象保存到磁盘中或者传输到网络中时,需要进行序列化;使用java关键字transient可以让字段不进行序列化。

  • 动态代理

    动态代理是指为其它对象提供一种代理以控制对这个对象的访问;

    在某些情况下,客户不想或者不能直接引用另一个对象,这时候代理对象可以在客户端和目标对象之间起到中介的作用。

    静态代理在程序运行前已经存在这个目标类的代理class,而动态代理是在程序运行时,通过反射机制生成代理类。 动态代理分为:生成目标类实现了的接口的另一个实现类(兄弟类)和生成一个目标类的子类。

  • 实现动态代理

    实现动态代理的方式有两种,使用java api 的Proxy工具实现基于接口的实现类动态代理,或者使用CGLib工具类实现基于子类的动态代理。

Java Web

  • jsp 和 servlet 的区别

    jsp:是结合了 html 与 java 的特性,并且 jsp 是 servlet 的一种特殊形式,在web服务器运行时会将 jsp 编译成 servlet;servlet是 java web 服务器应用程序的一个组件,运行在服务器端,由web容器控制,用于生成动态内容;servlet 是实现了 servlet 接口的 java 类;

    jsp 是 html 页面中嵌套了 java 代码,侧重于界面展示;
    servlet 是 html 代码 与 java 代码分离,注重逻辑控制;
    mvc 模式中,servlet 位于 controller 层,而 jsp 位于 view 层;

  • jsp的内置对象

    page: 当前 jsp 页面对象
    exception: 异常对象
    config:服务器配置信息
    request:请求对象
    response:响应对象
    session:会话
    application:保存整个应用程序的信息
    pageContext:页面上下文信息
    out:往 web 浏览器输出页面信息

  • jsp 的 4 种作用域

    page:当前页面
    request:请求
    session:会话
    application:应用程序

  • session 的工作原理

    在用户第一次访问应用时,服务器根据浏览器请求中是否有session ID 、者session id判断是否失效,如果不存在或者失效,服务器自动创建一个对象并保存在服务端,如果存在或者未失效,则从服务器查询出对应session,判断用户的身份信息以及其他操作;

  • cookie工作原理

    由服务器产生内容,浏览器收到请求后保存在本地;当浏览器再次访问时,浏览器会自动带上cookie,这样服务器就能通过cookie的内容来认证身份。

  • session 和 cookie 有什么区别?

    session 保存在服务器端,安全性比cookie高,cookie 保存在客户端,不安全。
    session 中保存的是对象,cookie保存的字符串。
    session 数据没有限制,cookie 大小有限制,最大为保存 4k 的数据。
    session 是整个服务端应用作用域内共享,而 cookie 会区分路径,同一个网站下不同路径不能访问。
    session 不会被禁用,而 cookie 可能被禁用。
    session 会在关闭浏览器时自动丢失,而服务器还会保存 session 信息,需要超时自动销毁;而 cookie 会保存在客户端,但是有时间限制。

  • 客户端禁止 cookie 实现 session

    在浏览器请求的的url后拼接 SESSIONID。

网络

  • http 响应码 301 和 302

    301:moved permanently 被请求资源已经永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干URI之一。如果可能,拥有连接编辑功能的客户端应当自动把请求的地址修改为从服务器反馈回来的地址。除非额外指定,否则这个响应也是可缓存的。

    302:found 请求的资源现在临时从不同的 uri 响应请求。这样的重定向是临时的,客户端应继续向原有地址发送以后的请求,中有在cache-control 或expires 中进行了指定的情况下,这个响应才是可缓存的。

    区别:301是永久重定向,搜索引擎在抓取新的内容时,也将旧的网址替换为了重定向之后的网址;而302是暂时的重定向,搜索引擎抓取的新内容保留旧的地址,因为服务器返回302,搜索引擎任务新的网址只是暂时的;

  • forward 和 redirect 的区别?

    ​ 转发(forward):转发是发生在web程序内部的,可以共享同一个请求,并且在浏览器的url地址不会改变,因为转发是发生在服务器内部;
    ​ 重定向(redirect):由浏览器端重新发起的一个指向新RUL的请求;重定向不能共享请求,浏览器url地址栏内容会发生改变;一般用于需要跳转到其他网站或不保留之前的用户状态。

    ​ forward效率比redirect效率更高,因为forward直接在服务器内部就进行了转发,而redirect需要通过浏览器进行一次跳转;

  • tcp 和 udp的区别

    TCP是面向连接的,在发送数据之前需要建立连接,而UDP是无连接的,发送数据之前不需要建立连接;
    TCP是可靠的,TCP传送的数据是无差错、不丢失、不重复的,且按顺序到达,而UDP进最大努力传输数据,不保证可靠性,可能会造成数据包丢失、并且不能保证顺序;
    TCP通过校验和、重传机制、序号标识、滑动窗口、确认应答实现可靠传输。
    UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性较高的通信或广播通信;
    TCP只支持点对点通信,而UDP可以实现一对一、一对多、多对多的交互通信;
    TCP耗费资源比UDP多;

  • tcp协议名词

    标志位 英文 含义
    SYN synchronouse 建立连接
    ACK acknowledgement 确认标志
    PSH push 传送标志
    FIN finish 结束标志
    RST reset 重置标志
    URG urgent 紧急标志

    syn:同步序列编号(Synchronize Sequence Numbers),是TCP/IP建立连接时使用的握手信号。

    seq:序号(sequence number)

    ack:确认号(acknowledgement number),

  • tcp 为什么要三次握手

    tcp协议是双向可靠传输协议。

    需要双方都能知道对方的状态,使用序列号seq确认双方是否准备好,然后使用序列号来确认双方发送的数据起始位,才能保证双方传输数据包的正确性;

    A ------------------------------------------------------------------B
    A|--------- syn,seq(An) --------------------------->----|B A向B发起请求(SYN)
    A|<------<--<------- syn,seq(Bn),ack(An + 1)-----|B B确认并回复A(与B向A发起请求合并SYN/ACK)
    A|----syn,seq(An+1),ack(Bn + 1)------>---->--->|B A确认并回复B(ACK)

    不能改为两次握手,两次握手,只有接收端知道发送端的序列号,而发送端不知道接收端的序号,发送端没有办法接收接收端的数据,无法保证数据的正确性、有序性,以及不能进行数据重发;而且改为两次握手,会发生安全事故。

  • 四次挥手

    客户端发起中断请求(FIN=1,Seq=x)

    服务端接收到客户端的中断请求,返回确认信息(ACK=1,Ack=x+1)

    服务端发送完数据后向客户端发送中断请求(FIN=1,Seq=y)

    ​ 消息超时重传机制:服务端发起FIN请求后,一段时间内没接收到ACK请求会重新发起FIN请求。

    客户端接收到服务端的中断请求,返回确认信息(ACK=1,Ack=y+1)。

    ​ 客户端在接收到了服务端的FIN请求,会进入TIME_WAIT状态,会等待2MSL;如果超过2MSL没有收到服务端的FIN请求,则说明服务端正确接收到了ACK。(2MSL时间内,服务端可能会再次重新发起FIN请求)

    MSL:网络中数据报文存在的最大时间

  • tcp 粘包

    tcp 粘包是指上一个tcp包的尾部和下一个tcp包的头部同时存在缓冲区中,而tcp是流式的,数据无边界,所以产生了粘包;在发送端,会将多个过小的数据包存放在缓冲区,进行合并发送,发生粘包;而在接收端,缓冲区的数据存放速度大于读取的速度,会造成包堆积,发生粘包;解决办法是在数据包中添加整个数据的长度,根据长度来获取数据包,就能准确读取每一个包。

  • OSI 的七层模型

    物理层、数据链路层、网络层、传输层、会话层、表示层、应用层

  • http的get 和 post 区别

    get请求的数据明文拼接在url后,使用 ? 分隔数据,多个数据使用 & 符号拼接,不安全,post请求的数据存放在请求体中,比get安全;
    get请求的数据由于是拼接在url后,每一个浏览器支持的url长度不一样,可能有限制,所以数据传输大小有限制,而post请求是存放在请求体中,没有大小限制;

    get请求传输的数据只能是字符,而post可以传输二进制,支持任意格式的数据;
    get请求语义是获取某个资源,post语义是提交。
    get请求可以被记录,post不可以;get请求浏览器会缓存,post不会;get请求的参数可以被记录,post不会;

  • 跨域

    跨域指的是浏览器安全策略(同源策略)限制,保证了不同源的脚本不会互相交互;当协议、端口、域名是一致的时候,浏览器就会任务两个页面具有相同的源,只要有一个不一致,会认为不是相同的源,就需要跨域。

  • 跨域实现

    1、使用 html 标签来实现,如 img 、script、css等标签可以实现跨域
    2、jsonp实现跨域,并且 jsonp获取的数据是使用 javaScript解释器来执行;并且只支持get请求
    3、cors实现跨域,cors是一个http资源共享规范,使用http头告诉浏览器该网站可以访问来自不同服务器指定的资源,并且支持任意的http方法
    4、使用代理

  • JSONP 实现原理

    jsonp的原理是利用html的script标签的 src属性来实现的,通过把需要跨域请求的数据当做javascript代码来进行解析,就能获取到不同服务器下的数据。

  • XSS 攻击

    xss 是指跨站脚本攻击,原理是向 web 页面插入恶意代码,当用户访问 web 页面时,使用恶意代码,获取 cookie 信息、破坏页面结构、重定向到其他网站;避免方式是使用过滤器对请求参数进行过虑,将特殊字符转换为其他同样语义的字符;

    73.CSRF 攻击

    CSRF 指的是,跨站请求伪造;CSRF 是一种挟持用户在已经登录的 web 应用上执行非用户本意的操作;解决方法:验证 http referer 字段;在 http 请求头中添加自定义字段并验证(把token放入请求头中);

posted on 2021-07-14 15:57  丶点滴  阅读(26)  评论(0编辑  收藏  举报