画布 1
Java基础
  Java基础
    int和Integer的区别？
    int a=2*8 怎么运算效率最快？
    float f=3.4;是否正确？
    short s1 = 1; s1 = s1 + 1; 有错吗？short s1 = 1; s1 += 1; 有错吗？
    Math.round(11.5) 等于多少？Math.round(-11.5)
    实现两个整数变量的交换
    == 和 equals区别
    hashCode()和 和 equals()区别
    以下代码运行结果为？
    String 和 StringBuffer 、StringBuilder 的区别？
    final 修饰的作用？
    抽象类和接口的区别？
    重载 和 重写的区别 。 重载的方法能否根据返回类型进行区分？
    char 型变量中能不能存贮一个中文汉字，为什么？
    面向对象的特征有哪些方面？
    访问修饰符 public 、private 、protected 、不写（default 默认）时的区别
    要实现多态需要的三个必要条件
  异常相关
    java 中的异常继承体系及常见运行时异常？
    异常的解决方式？
    throw 和 throws 的区别？
    常见的异常
    try-catch-finally 中哪个部分可以省略
    try-catch-finally 中， 如果 catch 中 return 了，finally 还会执行吗？
    final 、finally 、finalize 的区别?
  java 类加载机制
    java 类加载机制
    java 何时进行类的加载？
    java 创建对象的方式有几种？
    java 的类加载顺序
  jvm 相关
    jvm 虚拟机内存分配模型？
    堆和栈的区别？
    说一下 JVM 有哪些垃圾回收算法？
    jvm 虚拟机的垃圾回收机制？
    垃圾回收相关的 JVM 参数
    GC 是什么？为什么要有 GC？
    jvm 内存优化
    说一下 JVM 调优的工具？
    常用的 JVM 调优的参数都有哪些？
  多线程相关
    线程和进程的区别？
    多线程的实现方式？
    线程的生命周期
    线程有哪些状态
    sleep() 和 wait() 有什么区别
    线程的 run() 和start() 有什么区别？
    synchronized 和 Lock 的 区别？
    synchronized 和 ReentrantLock 区别是什么 ？
    说一下 atomic 的原理？
    Java 语言防止并发问题有几种解决方案？最佳解决方案是什么？
    请说出与线程同步以及线程调度相关的方法
    什么是 ThreadLocal
    关键字 volatile 、transient 各有什么作用？
    多线程之间的通信方式？
    线程池
      创建线程池有哪几种方式？
      线程池中 submit() 和 execute() 方法有什么区别
  集合相关
    java 中的集合有哪些？
    HashMap 底层原理？
    ArrayList 和 Vector 的区别？
    hashMap 和 hashTable 的区别？
    集合的线程线程安全如何理解？
    如何保证 java 集合的线程安全？
    如何实现数组和 List 之间的转换？
    Array 和ArrayList 有何区别？
    Collection 和 Collections 的区别？
    简述 Set、 HashSet、 、LinkedHashSet、TreeSet、 ConcurrentHashSet、ConcurrentSkipListSet 的异同
  I/O
    I/O 流的继承体系
    序列化和反序列化
    序列化常用技术
    BIO 、NIO 、AIO 的区别和使用场景
  JDK8 新特性
    接口的默认方法
    Lambda 表达式
    函数式接口
    Function 接口
  反射
    反射的概念
    获取 class 字节码文件对象的方式
    反射获取类的成员及反射方法
    私有成员如何通过反射访问
    反射的作用和应用场景
    是 什么是 Java 序列化？什么情况下需要序列化？
  对象拷贝
    为什么要使用克隆？
    如何实现对象克隆？
    深拷贝和浅拷贝区别是什么？
  JavaWeb
    详细描述 MVC 模式
    Servlet 和JSP 的区别？
    servlet 的生命周期？
    forward 和 redirect 的区别？
    cookie 和 session 的区别？
    说一下 session 的工作原理？
    实现会话跟踪的技术有哪些？
    如果客户端禁止 cookie 现能实现session还能用吗？
    JSP 九大内置对象
    JSP 的域对象
    JSP 中动态 include 和静态 include 的区别
    页面间对象传递的方法
    get 和 和 post 的区别 ？
    如何防止表单重复提交
    过滤器有哪些作用？
    C/S 与 与 B/S 的区别？
    tomcat 优化
    通步和异步有何异同，在什么情况下分别使用他们？举例说明。
  网络编程
    OSI 的七层模型都有哪些？
  排序算法和设计模式
    java 中的单例模式？
    手写排序方法( 最少两种：冒泡、快速)
    手写二分查找
    说一下你熟悉的设计模式？
Integer：
1、int的包装类
2、变量必须实例化才能获得
3、是对象的引用，new一个Integer时，实际上是指针指向new的对象
4、默认值为 null
int：
1、Java的基本类型之一
2、int不需要实例化就可以获得
3、int是直接存储数据
4、默认值为 0
Java 为每个原始类型提供了包装类型：
原始类型: boolean，char，byte，short，int，long，float，double
包装类型：Boolean，Character，Byte，Short，Integer，Long，Float，Double
自动装箱：基本数据类型自动转换为包装类型
利用位算 int a = 2<<3；
也就是2^3 = 8
直接通过底层二进制移位计算，
2的二进制是10，在 32 位存储器里面是 0000 0000 0000 0010
左移三位则变成了 0000 0000 0001 0000 也就是16
不正确，
因为默认带小数的都是double类型
可以写成float f = 3.4F 即可
或
float f =(float)3.4
对于前者：
1为int类型，所以他的计算结果为int类型，必须要进行类型转换
则，short s1 = (short)(s1 +1)
对于后者：
s1 += 1，会进行隐式类型转换，
等同于
s1 = (short)(s1+1)
前者：12
后者：-11
原理为+0.5，取最近的整数
public class swapDemo{
public static void main(String[] args){
int a = 10;
int b = 20;
int temp;
temp = a;
a = b;
b = temp;
System.out.println("temp="+ temp+" ；a= "+a +" b=： "+b )
}
方式二：不使用中间变量的，“和”实现
public class SwapDemo{
public static void main(String[] args) {
int i = 10;
int j = 20;
i = i + j; //i = 30; j = 20;
j = i - j; //i = 30; j = 10;
i = i - j; //i = 20; j = 10;
System.out.println("i=" + i + ";j=" + j);
// "i=20;j=10"
}
}
方式三：面试中使用的，按位亦或 “^”
public class SwapDemo{
public static void main(String[] args) {
int i = 10;
int j = 20;
i = i ^ j;
j = i ^ j;
i = i ^ j;
System.out.println("i=" + i + ";j=" + j);
// "i=20;j=10"
}
}
方式四：一句话实现交换
public class SwapDemo{
public static void main(String[] args) {
int i = 10;
int j = 20;
j = ( i + j ) - ( i = j );
System.out.println("i=" + i + ";j=" + j);
// "i=20;j=10"
}
}
}
==
比较 内存地址
equals
默认效果和==号的效果一样，也是比较的地址值比较 字面量
但大部分被重写了方法，所以是比较字面量
hashCode()：
根据对象属性值，就算他的哈希值
哈希值相同的对象，不一定是同一个对象，可能发生哈希碰撞
equals()：
一开始是比较内存地址，但大部分被重写，就比较字面量
Integer i = 100;
Integer j = 100;
System.out.print(i == j); //true
Integer i = 128;
Integer j = 128;
System.out.print(i == j); //false
原因：
Integer范围为-128-127
如果数值在范围内，直接从常量池里取。
否则就开启新空间
String：
线程安全，长度不可变性，效率最低
StringBuffer：
线程安全，长度可变，效率较低
StringBuilder：
线程不安全，长度可变，效率较高
1、不可以被继承
2、修饰常量
3、会被加入常量池
4、变量不可变
5、不可以被重写
抽象类：
1、单继承
2、可以有构造器
3、可以有抽象方法和普通方法
接口：
1、多实现
2、不可以有构造器
3、只能有抽象方法
重载：
发生在一个类中，要求方法名相同，参数列表（类型、数量可不同）
重写：
发送在子父类之间，要求方法名、参数类型、返回值都相同
不根据返回值区分
能，
因为Java默认的编码是采用UniCode
一个char占两个字符，一个汉字也占两个字符
所有能储存1个中文汉字
三大特征：封装、多态、继承
抽象
当前类 同包 子类 其他包
public √ √ √ √
protected √ √ √
default √ √
private √
第一章
要有类的继承
第二章
要有方法的重写（子类继承父类并重写父类中已有的或抽象的方法）
第三章
对象造型
Throwable：Error、Exception
Error：虚拟机错误、系统崩溃、内存错误
Exception：编译时异常、运行时异常
常见运行时异常：
数组下标越界、
类转行异常、
参数不合法异常、
数学异常、
文件找不到异常、
方法不存在异常、
空指针异常
1、try{}catch(){}finally{}
2、throws
throw：
1、用在方法体内
throw：
1、用在方法列表后
NullPointerException： 空指针异常
ClassNotFoundException： 类找不到异常
NumberFormatException： 字符串转换数字异常
IndexOutOfBoundsException： 数组下标越界异常
ClassCastException： 数据类型转换异常
FileNotFoundException： 文件未找到异常
NoSuchMethodException： 方法不存在异常
IOException： IO异常
SocketException：Socket异常
try-catch-finally 其中
catch 和 finally 都可以被省略，但是不能同时省略，
也就是说有 try 的时候，必须后面跟一个 catch 或者 finally。
finally一定执行
即使是 catch 中 return 了，catch 中的 return 会等 finally 中的代码执行完之后，才会执行
final：
修饰常量
finally：
try-catch-finally的最后一部分，一定会被执行
finalize：
Oject类的一个方法，在垃圾收集器执行的时候会调用被回收对象的此方法
加载-连接-初始化
类的加载
是指把类的.class 文件中的数据读入到 内存中，通常是创建一个字节数组读入.class 文件，然后产生与所加载类对应的 Class 对象。加载完成后，Class 对象还不完整，所以此时的类还不可用。
当类被加载后就进入连接阶段，
这一阶段包括 验证 、 准备 （ 为静态变量分配内存并设置默认的初始值 ） 和解析（将符号引用替换为直接引用）三个步骤。
最后 JVM 对类进行初始化，包括：
1）如果类存在直接的父类并且这个类还没有被初始化，那么就 先初始化父类；
2）如果类中存在初始化语句，就依次执行这些初始化语
1. 为一个类型创建一个新的对象实例时（比如 new、反射、序列化）
2. 调用一个类型的静态方法时（即在字节码中执行 invokestatic 指令）
3. 调用一个类型或接口的静态字段，或者对这些静态字段执行赋值操作时（即在字节码中，执行 getstatic 或者
putstatic 指令），不过用 final 修饰的静态字段除外，它被初始化为一个编译时常量表达式
4. 调用 JavaAPI 中的反射方法时（比如调用 java.lang.Class 中的方法，或者 java.lang.reflect 包中其他类的方法）
5. 初始化一个类的派生类时（Java 虚拟机规范明确要求初始化一个类时，它的超类必须提前完成初始化操作，
接口例外）
JVM 启动包含 main 方法的启动类时
1、new 关键字创建对象
2、反射 Class类的newInstance()方法
3、Constructor类的newInstance()方法
4、Object的clone()方法
5、反序列化方式创建对象(XML、Json反序列化)
1. 父类静态代码块
2. 子类静态代码块
3. 父类构造代码块
4. 父类构造方法
5. 子类构造代码块
6. 子类构造方法
jvm 内存分配：
1、堆内存(heap):java 对象存储空间，创建对象开辟堆内存空间，存储对象的成员，空间只能被垃圾回收器回收，
不会自动回收。内存占用超过 98%，回收只能回收 2%，内存溢出。
2、栈内存(stack)：java 中局部变量或者形式参数的存储空间，方法调用开辟空间，方法执行结束，空间自动释
放。如果递归方法或者死循环会导致栈内存溢出。
3、方法区：方法区又被称为永久区，线程共享，是用来存储已被 JVM 加载的类信息、常量、静态变量、即时
编译器编译后的代码等数据。
4、本地方法区
5、程序计数器
功能方面：堆是用来存放对象的，栈是用来执行程序的。
共享性：堆是线程共享的，栈是线程私有的。
空间大小：堆大小远远大于栈
标记-清除算法：
标记无用对象，然后进行清除回收。缺点：效率不高，无法清除垃圾碎片。
标记-整理算法：
标记无用对象，让所有存活的对象都向一端移动，然后直接清除掉端边界以外的内存。
复制算法：
按照容量划分二个大小相等的内存区域，当一块用完的时候将活着的对象复制到另一块上，然后再把已使用的内存空间一次清理掉。缺点：内存使用率不高，只有原来的一半。
分代算法：
根据对象存活周期的不同将内存划分为几块，一般是新生代和老年代，新生代基本采用复制算法，老年代采用标记整理算法。
1、jvm 的垃圾回收机制后很多方法和垃圾回收器，我只了解分代管理法和清除标记法。
2、在 jvm 内存中有新生代、年老代、永生代，新生代中又包含 Eden(伊甸园)和 survival(幸存者)，幸存者有两块
内存区域，但是使用时，仅一个区域可用，另一块幸存者内存区必须为空。
3、当创建对象时，会首先在 Eden 中开辟新的空间，如果 Eden 的内存区域不够用，无法开辟新的内存空间，则会对 Eden 进行扫描，然后 标记。垃圾回收器此时会找出哪些内存在使用中，还有哪些不是。垃圾回收器要检查完所有的对象，知道哪些有被引用，哪些没有。 对需要清除的对象进行标记，清理垃圾对象，然后将保留的对象拷贝到 Survival
幸存者区域。
4、如果 Eden 和 Survival 新生代内存区域全部存满，这时候对整个新生代内存区域进行扫描，将需要清除的对象进行标记，进行清除，将其他对象拷贝到年老代，这种 GC，称之为 YoungGC.
5、如果年老代内存区域也存满，需要对整个内存区域进行扫描，对对象进行标记，清除新生代和年老代内存区域，
将垃圾对象清除，这种 GC，称之为 Full GC。
6、垃圾回收器分为串行回收器、并行回收器、并发回收器，串行垃圾回收器单线程，效率低，并行和并发回收器为多线程，但是并发回收器会造成程序阻塞，所以使用并行回收器进行垃圾回收，过程中会产生垃圾回收碎片，
会自动转换为串行垃圾回收器，清理完碎片，自动转换为并行垃圾回收器，不会对程序造成影响。
帖子详解：https://blog.csdn.net/dd864140130/article/details/50084471
· -Xms / -Xmx — 堆的初始大小 / 堆的最大大小
· -Xmn — 堆中年轻代的大小
· -XX:-DisableExplicitGC — 让 System.gc()不产生任何作用
· -XX:+PrintGCDetails — 打印 GC 的细节
· -XX:+PrintGCDateStamps — 打印 GC 操作的时间戳
· -XX:NewSize / XX:MaxNewSize — 设置新生代大小/新生代最大大小
· -XX:NewRatio — 可以设置老生代和新生代的比例
· -XX:PrintTenuringDistribution — 设置每次新生代 GC 后输出幸存者乐园中对象年龄的分布
· -XX:InitialTenuringThreshold / -XX:MaxTenuringThreshold：设置老年代阀值的初始值和最大值
· -XX:TargetSurvivorRatio：设置幸存区的目标使用率
GC，垃圾收集
Java程序员不用担心内存管理，Java会自动监测
作用：
有效防止内存泄露问题，提高用户体验
目的
减少 GC 的频率和 Full GC 的次数
1). 新生代设置过小
一是新生代 GC 次数非常频繁，增大系统消耗；二是导致大对象直接进入旧生代，占据了旧生代剩余空间，
诱发 Full GC
2). 新生代设置过大
一是新生代设置过大会导致旧生代过小（堆总量一定），从而诱发 Full GC；二是新生代 GC 耗时大幅度增加
一般说来新生代占整个堆 1/3 比较合适
3). Survivor 设置过小
导致对象从 eden 直接到达旧生代，降低了在新生代的存活时间
4). Survivor 设置过大
导致 eden 过小，增加了 GC 频率
另外，通过-XX:MaxTenuringThreshold=n 来控制新生代存活时间，尽量让对象在新生代被回收
由内存管理和垃圾回收可知新生代和旧生代都有多种 GC 策略和组合搭配，选择这些策略对于我们这些开发人员是个难题，JVM 提供两种较为简单的 GC 策略的设置方式
1). 吞吐量优先
JVM 以吞吐量为指标，自行选择相应的 GC 策略及控制新生代与旧生代的大小比例，来达到吞吐量指标。这个
值可由-XX:GCTimeRatio=n 来设置
2). 暂停时间优先
JVM 以暂停时间为指标，自行选择相应的 GC 策略及控制新生代与旧生代的大小比例，尽量保证每次 GC 造成
的应用停止时间都在指定的数值范围内完成。这个值可由-XX:MaxGCPauseRatio=n 来设置
堆设置：
-Xms:初始堆大小
-Xmx:最大堆大小
-XX:NewSize=n:设置年轻代大小
-XX:NewRatio=n:设置年轻代和年老代的比值。如:为 3，表示年轻代与年老代比值为 1：3，年轻代占整个年轻代年老代和的 1/4
-XX:SurvivorRatio=n:年轻代中 Eden 区与两个 Survivor 区的比值。注意 Survivor 区有两个。如：3，表示 Eden：
Survivor=3：2，一个 Survivor 区占整个年轻代的 1/5
-XX:MaxPermSize=n:设置持久代大小
帖子详解：https://blog.csdn.net/sun1021873926/article/details/78002118
JDK自带的jconsole、jvisualvm
jconsole：用于对 JVM 中的内存、线程和类等进行监控；
jvisualvm：JDK 自带的全能分析工具，可以分析：内存快照、线程快照、程序死锁、监控内存的变化、gc 变化
-Xms2g：初始化推大小为 2g；
-Xmx2g：堆最大内存为 2g；
-XX:NewRatio=4：设置年轻的和老年代的内存比例为 1:4；
-XX:SurvivorRatio=8：设置新生代 Eden 和 Survivor 比例为 8:2；
-XX:+UseParNewGC：指定使用 ParNew + Serial Old 垃圾回收器组合；
-XX:+UseParallelOldGC：指定使用 ParNew + ParNew Old 垃圾回收器组合
-XX:+UseConcMarkSweepGC：指定使用 CMS + Serial Old 垃圾回收器组合；
-XX:+PrintGC：开启打印 gc 信息；
-XX:+PrintGCDetails：打印 gc 详细信息。
线程：
程序执行的最小单位，是进程的一条执行路径
进程：
是一段正在运行的代码，资源分配的最小单位
1、继承Thread
2、实现callable
3、实现runable
4、线程池
1、新建
2、就绪
3、运行
4、阻塞
5、销毁
NEW 尚未启动
RUNNABLE 正在执行中
BLOCKED 阻塞的（被同步锁或者 IO 锁阻塞）
WAITING 永久等待状态
TIMED_WAITING 等待指定的时间重新被唤醒的状态
TERMINATED 执行完成
sleep：
1、来自Thread
2、不释放锁
3、自动唤醒
wait：
1、来自Object
2、释放锁
3、手动通过notify等方法唤醒
run可重复调用，线程执行运行时代码
start启动线程，只能调用一次
相同点：
都是可以实现线程安全，给线程加锁
不同点：
synchronized：
1、可以给方法、类、代码块加锁
2、使用简单，自动获取锁和释放锁
3、不可以知道是否获取到琐
lock：
1、只等给代码块加锁
2、手动释放锁通过unlock（）
3、可以知道是否获取到琐
synchronized 早期的实现比较低效，对比 ReentrantLock，大多数场景性能都相差较大，但是在 Java 6 中对
synchronized 进行了非常多的改进。
主要区别如下：
ReentrantLock 使用起来比较灵活，但是必须有释放锁的配合动作；
ReentrantLock 必须手动获取与释放锁，而 synchronized 不需要手动释放和开启锁；
ReentrantLock 只适用于代码块锁，而 synchronized 可用于修饰方法、代码块等。
volatile 标记的变量不会被编译器优化；synchronized 标记的变量可以被编译器优化。
atomic 主要利用 CAS (Compare And Wwap) 和 volatile 和 native 方法来保证原子操作，从而避免 synchronized
的高开销，执行效率大为提升。
Java 语言中防止并发问题最直接想到的方法就是 synchronized，这种方式效果很明显，但处理起来相对麻烦。为
了简化，Java 官方推出了 java.util.concurrent 并发包用于解决多线程访问问题。日常使用中，建议你使用并发包处理多线程问题。
- wait()：
使一个线程处于等待（阻塞）状态，并且释放所持有的对象的锁；
- sleep()：
使一个正在运行的线程处于睡眠状态，是一个静态方法，调用此方法要处理 InterruptedException异常；
- notify()：
唤醒一个处于等待状态的线程，当然在调用此方法的时候，并不能确切的唤醒某一个等待状态的线程，
而是由 JVM 确定唤醒哪个线程，而且与优先级无关；
- notityAll()：
唤醒所有处于等待状态的线程，该方法并不是将对象的锁给所有线程，而是让它们竞争，只有获得锁的线程才能进入就绪状态
线程局部变量
每一个线程生成特有的一份线程局部变量，多线程之间是无法相互访问的
volatile 是一种轻量级的多线程同步问题解决方案，用 volatile 修饰的变量，线程在每次使用变量的时候，都会
读取变量修改后的最的值。
transient 用于指定在序列化的类中某个属性不参与序列化操作。
1、synchronized 同步
本质上就是“共享内存”式的通信。多个线程需要访问同一个共享变量，谁拿到了锁（获得了访问权限），谁就
可以执行。
2、while 轮询
其实就是多线程同时执行，会牺牲部分 CPU 性能。不停地通过 while 语句检测条件是否成立 ，从而实现了线程间的通信。
3、wait/notify 机制
通过 Object 类的 wait()和 notify()方法，切换线程状态，使线程阻塞或者运行。
4、管道通信
管道流主要用来实现两个线程之间的二进制数据的传播
线程池创建有七种方式，最核心的是最后一种：
newSingleThreadExecutor()：它的特点在于工作线程数目被限制为 1，操作一个无界的工作队列，所以它保证了
所有任务的都是被顺序执行，最多会有一个任务处于活动状态，并且不允许使用者改动线程池实例，因此可以避免
其改变线程数目；
newCachedThreadPool()：它是一种用来处理大量短时间工作任务的线程池，具有几个鲜明特点：它会试图缓存线
程并重用，当无缓存线程可用时，就会创建新的工作线程；如果线程闲置的时间超过 60 秒，则被终止并移出缓存；
长时间闲置时，这种线程池，不会消耗什么资源。其内部使用 SynchronousQueue 作为工作队列；
newFixedThreadPool(int nThreads)：重用指定数目（nThreads）的线程，其背后使用的是无界的工作队列，任何
时候最多有 nThreads 个工作线程是活动的。这意味着，如果任务数量超过了活动队列数目，将在工作队列中等待
空闲线程出现；如果有工作线程退出，将会有新的工作线程被创建，以补足指定的数目 nThreads；
newSingleThreadScheduledExecutor()：创建单线程池，返回 ScheduledExecutorService，可以进行定时或周期性的工作调度；
newScheduledThreadPool(int corePoolSize) ：和 newSingleThreadScheduledExecutor()类似 ，创 建的 是个
ScheduledExecutorService，可以进行定时或周期性的工作调度，区别在于单一工作线程还是多个工作线程；
newWorkStealingPool(int parallelism)：这是一个经常被人忽略的线程池，Java 8 才加入这个创建方法，其内部会构建 ForkJoinPool，利用 Work-Stealing 算法，并行地处理任务，不保证处理顺序；
ThreadPoolExecutor()：是最原始的线程池创建，上面 1-3 创建方式都是对 ThreadPoolExecutor 的封装。
execute()：只能执行 Runnable 类型的任务。
submit()：可以执行 Runnable 和 Callable 类型的任务。
Callable 类型的任务可以获取执行的返回值，而 Runnable 执行无返回值。
java 中的集合分为单列集合和双列集合，单列集合顶级接口为 Collection,双列集合顶级接口为 Map。
Collection的子接口有：Set、List
List特点：有序，可重复
List实现类：
ArrayList：底层数组、查询快、增删慢、线程不安全、效率高
Vector：古老实现类、查询、增删慢、线程安全【废弃】
LinkedList：底层链表、查询慢、增删快、线程不安全、效率高
Set特点：不可重复、无序、元素唯一
Set实现类：
HashSet：底层HashMap、数组+链表、不可重复、无序
TreeSet：底层红黑树、实现元素排序
Map接口特点：K-V键值对形式
HashMap：链表+数组、线程不安全、效率高
LinkedHashMap：hashmap+linkedList、通过链表实现元素有序、K-V键值对
TreeMap：底层红黑树、元素排序
以 jdk7 为例说明:
HashMap map = new HashMap();
在实例化后，底层创建了长度为16的一维数组,类型为Entry[] table。
...可能已经执行过多次put...
map.put(key,value1);
首先,调用key1所在类的hashCode()方法,计算key1的哈希值,此哈希值经过某种算法计算后,得到Entry[]数组的存放位置
如果此位置上的数据为空,此时的key1-value1添加成功。 ----情况1
如果此位置上的数据不为空,(此位置上存在一个或多个数据(以链表形式存在)),比较key1与已经存在的一个或多个数据的哈希值:
如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。 ----情况2
如果key1的哈希值与某一个数据(key2-value2)的哈希值的相同,继续比较: 调用key1所在类的equals(key2)方法,进行比较:
如果equals()返回false:此时key1-value1添加成功。 ----情况3
如果equals()放回true:使用value1替换value2的值。
补充: 关于情况2和情况3: 此时key1-value1和原来的数据以链表的方式存储。
在不断的添加过程中，会涉及到扩容问题: 默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来。
jdk8 相较于jdk7在底层实现方面的不同:
1. new HashMap():底层没有创建一个长度为16的Entry[]
2. jdk 8 底层的数组是: Node[],而非Entry[]
3. 首次调用put()方法时,底层创建长度为16的数组
4. jdk 7 底层结构只有: 数组 + 链表。 jdk 8 底层结构: 数组 + 链表 + 红黑树。
当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时,
此时此索引位置上的所有数据改为使用红黑树存储。
DEFAULT_INITIAL_CAPACITY ： HashMap的默认容量: 16
DEFAULT_LOAD_FACTOR : HashMap的默认加载因子: 0.75
threshold: 扩容的临界值 = 容量 * 加载因子 : 16 * 0.75 => 12
TREEIFY_THRESHOLD： Bucket中链表长度大于该默认值,转化为红黑树 : 8
MIN_TREEIFY_CAPACITY: 桶中的Node被树划时最小的hash表容量: 64
————————————————
https://blog.csdn.net/qq_43284469/article/details/108739529
相同点：
二者都是单列集合 Colleciton 子接口 List 的实现类；
二者的底层数据结构，均为数组实现，查询快，增删慢；
不同点：
ArayList 是线程不安全的的，效率高；Vector 是线程安全的，效率低，jdk 已经将其废弃，不建议使用；
相同点：
1、都是Map接口下的实现类
2、都底层都是链表+数组
3、都是K-V键值对存储形式
hashMap：
1、继承 AbstractMap 类
2、线程不安全
hashTable：
1、继承Dictionary 类
2、线程安全
线程安全
首先要明白线程的工作原理，jvm 有一个 main memory，而每个线程有自己的 working
memory，一个线程对一个 variable 进行操作时，都要在自己的 working
memory 里面建立一个 copy，操作完之后再写入 main
memory。多个线程同时操作同一个 variable，就可能会出现不可预知的结果。根据上面的解释，很容易想出相应
的 scenario。
而用 synchronized 的关键是建立一个 monitor，这个 monitor 可以是要修改的 variable 也可以其他你认为合适的
object 比如 method，然后通过给这个 monitor 加锁来实现线程安全，每个线程在获得这个锁之后，要执行完
load 到 workingmemory －> use&assign －> store 到 mainmemory
的过程，才会释放它得到的锁。这样就实现了所谓的线程安全。
什么是线程安全?线程安全是怎么完成的(原理)?
线程安全就是说多线程访问同一代码，不会产生不确定的结果。
编写线程安全的代码依靠线程同步。
1、同步代码块
2、同步方法
3、Collections工具类里的synchronized方法
数组转 List：使用 Arrays. asList(array) 进行转换。
List 转数组：使用 List 自带的 toArray() 方法
Array：
1、可以存储基本数据类型和对象
2、方法少
3、长度创建后就固定
ArrayList：
1、只能存储对象
2、方法多
3、长度灵活
Collection：
是单列集合的顶级接口
Collections：
是Coleection的工具类，提供方法
同：
1、Collection单列集合下的接口
2、无序，不可重复、元素唯一
不同：
LinkedHashSet：以链表形式排序，增删快、查找慢
HashSet：Set接口下的一个实现类、底层HashMap
TreeSet：底层红黑树、实现元素排序
java.util.concurrent 并发包下的类：
ConcurrentHashSet：HashMap 是线程不安全的，ConcurrentHashMap 并发优化的 HashMap，同 hashSet 一样，底层ConcurrentHashMap 实现:
ConcurrentSkipListSet:ConcurrentSkipListMap 是一个线程安全的有序的哈希表，并发安全主要由 CAS 来实现。
ConcurrentSkipListSet 完全是由 ConcurrentSkipListMap 实现
抽象基类 节点流 (或文件流) 缓冲流 (处理流的一种)
InputStream FileInputStream (read(byte[] buffer)) BufferedInputStream (read(byte[] buffer))
OutputStream FileOutputStream (write(cbuf,0,len)) BufferedOutputStream (write(cbuf,0,len)) / flush()
Reader FileReader(read(char[] cbuf)) BufferedReader (read(char[] cbuf) / readline())
Writer FileWriter(write(cbuf,0,len)) BufferedWriter (write(cbuf,0,len)) / flush()
序列化：
将Java对象转换为二进制流的方法流通或存储
翻序列化：
将二进制流转换为Java对象
Json、XML
BIO：同步并阻塞，一个请求分配一个线程，造成线程开销过大，资源浪费
NIO：同步并阻塞，请求会发送到多路复用器，他会轮询请求线程处理
AIO：异步非阻塞，先经过操作系统处理后，再通知服务器应用去启动线程进行处理
使用场景：
BIO：适合连接数小且固定的架构，较小构架，对服务器要求高，且局限于jdk1.4，程序易懂
NIO：适合连接数多且连接时间段的架构，聊天服务器，编程复杂，jdk1.4开始支持
AIO：适合连接数比较多且连接时间长的架构，相册服务器，充分调用 OS 参与并发操作，编程复杂，jdk7开始
Java 8 允许我们给接口添加一个非抽象的方法实现，只需要使用 default 关键字即可，这个特征又叫做扩展方法，
示例如下：
interface Formula {
double calculate(int a);
default double sqrt(int a) {
return Math.sqrt(a);
}
}
Formula 接口在拥有 calculate 方法之外同时还定义了 sqrt 方法，实现了 Formula 接口的子类只需要实现一个 calculate
方法，默认方法 sqrt 将在子类上可以直接使用。
1、map
2、Stream
3、collect(toList()) & filter
4、max&min
详细 lambda 表达式教程：百度 lambda 表达式 菜鸟教程
通过class字节码文件对象，操作获取成员信息
1、Class.forName()
2、类名.class()
3、对象.getClass()
constructor.setAccessible(true); //暴力破解
method.setAccessible(true); //暴力破解
field.setAccessible(true); //暴力破解
可以通过配置文件来动态配置和加载类，以实现软件工程理论里所提及的类与类，模块与模块之间的解耦。反射最经典的应用是 spring 框架。
保存程序在内存中的状态，并能把保存的对象读取出来
网络传递、内存对象保存到数据库
一个对象由一个我们需要的状态，通过克隆就可以复制这个状态
实现 Cloneable 接口并重写 Object 类中的 clone() 方法。
实现 Serializable 接口，通过对象的序列化和反序列化实现克隆，可以实现真正的深度克隆。
浅克隆：当对象被复制时只复制它本身和其中包含的值类型的成员变量，而引用类型的成员对象并没有复制。
深克隆：除了对象本身被复制外，对象所包含的所有成员变量也将复制。
基于MVC设计模式，及Model模型、View视图、Controller控制器
Model：处理业务逻辑模块
View：返回Model处理完的内容，并做显示，实现数据到页面的转换
Controller：获取请求映射分发给对应的Model业务处理
JavaWeb采用MVC模式，
主要分为Model模型、View视图、Controller控制器
Model模型：主要处理业务逻辑，将数据处理返回给View视图
View视图：主要获取到Model的数据，并转换显示页面上
Controller控制器：根据映射请求分发对应的Model模型做处理请求
JSP是Servlet的技术扩展
在主要的区别在于
JSP注重于视图的展示，将html与java组合
Servlet注重控制逻辑，映射分发
1、init（）初始化，只执行一次
2、service（）服务方法，请求处理
3、destroy（）销毁方法，释放资源，在服务器关闭执行
forward：请求转发，
1浏览器地址不会改变，
2客户端只请求了一次，
3能请求系统内部资源
redirect：请求重定向，
1浏览器地址会改变，
2客户端请求了两次，
3不能重定向系统内部资源
cookie：
1、存放于客户端浏览器上
2、不够安全
3、使用cookie会减轻服务器压力
4、单个cookie保存不超过4k，浏览器会限制一个站点保存20cookie
session：
1、存放在服务器上
2、安全
客户端登录完成之后，服务器会生成session，session创建完毕后会将sessionId发送给客户端，客户端每次访问服务器都会携带sessionId，服务器就可以根据sessionId来获取对应信息
③cookie：cookie 有两种，一种是基于窗口的，浏览器窗口关闭后，cookie 就没有了；另一种是将信息存储在一
个临时文件中，并设置存在的时间。当用户通过浏览器和服务器建立一次会话后，会话 ID 就会随响应信息返回存
储在基于窗口的 cookie 中，那就意味着只要浏览器没有关闭，会话没有超时，下一次请求时这个会话 ID 又会提交
给服务器让服务器识别用户身份。会话中可以为用户保存信息。会话对象是在服务器内存中的，而基于窗口的
cookie 是在客户端内存中的。如果浏览器禁用了 cookie，那么就需要通过下面两种方式进行会话跟踪。当然，在
使用 cookie 时要注意几点：首先不要在 cookie 中存放敏感信息；其次 cookie 存储的数据量有限（4k），不能将
过多的内容存储 cookie 中；再者浏览器通常只允许一个站点最多存放 20 个 cookie。当然，和用户会话相关的其
他信息（除了会话 ID）也可以存在 cookie 方便进行会话跟踪。
④HttpSession：在所有会话跟踪技术中，HttpSession 对象是最强大也是功能最多的。当一个用户第一次访问某
个网站时会自动创建 HttpSession，每个用户可以访问他自己的 HttpSession。可以通过 HttpServletRequest 对象
的 getSession 方法获得 HttpSession，通过 HttpSession 的 setAttribute 方法可以将一个值放在 HttpSession 中，
通过调用 HttpSession 对象的 getAttribute 方法，同时传入属性名就可以获取保存在 HttpSession 中的对象。与
上面三种方式不同的是，HttpSession 放在服务器的内存中，因此不要将过大的对象放在里面，即使目前的 Servlet
容器可以在内存将满时将HttpSession中的对象移到其他存储设备中，但是这样势必影响性能。添加到HttpSession
中的值可以是任意 Java 对象，这个对象最好实现了 Serializable 接口，这样 Servlet 容器在必要的时候可以将其序列化到文件中，否则在序列化时就会出现异常。
可以用，通过 url 重定向实现，session 只是依赖 cookie 存储 sessionid，如果 cookie 被禁用了，可以使用 url中添加 sessionid 的方式保证 session 能正常使用。
也就是在请求地址上放上sessionId供服务器获取
JSP 一共有 9 个内置对象：
request：负责得到客户端请求的信息，对应类型：javax.servlet.http.HttpServletRequest
response:负责向客户端发出响应，对应类型：javax.servlet.http.HttpServletResponse
session:负责保存同一客户端一次会话过程中的一些信息，对应类型：javax.servlet.http.httpsession
out：负责管理对客户端的输出，对应类型：javax.serlvet.jsp.jspwriter
application:表示整个应用环境的信息，对应类型：
javax.servlet.servletcontext
config:表示 ServletConfig，对应类型：javax.servlet.servletconfig
exception:表示页面中发生的异常，可以通过它获得页面异常信息，对应类型：java.lang.exception
pagecontext:表示这个 JSP 页面上下文，对应类型：javax.servlet.jsp.pagecontext
page:表示当前 JSP 页面本身。
1、page 是代表一个页面相关的对象和属性。一个页面由一个编译好的 java servlet 类（可以带有 include 指令，但不可以带有 include 动作）表示。这既包括 servlet 又包括编译成 servlet 的 jsp 页面。
2、request 是代表与 web 客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面，涉及多个 web组件（由于 forware 指令和 include 动作的关系）
3、session 是代表与用于某个 web 客户机的一个用户体验相关的对象和属性。一个 web 回话也可以经常跨域多个客户机请求。
4、application 是代表与整个 web 应用程序相关的对象和属性。这实质上是跨域整个 web 应用程序，包括多个页面、请求和回话的一个全局作用域。
静态 include：语法：<%@ include file="文件名" %>，相当于复制，编辑时将对应的文件包含进来，当内容变化
时，不会再一次对其编译，不易维护。
动态 include：语法：<jsp:include page="文件名">,能够自动检查被包含文件，当客户端对 JSP 文件进行请求时，
会重新将对应的文件包含进来，进行实时的更新。
request、session、application、cookie 等
Get：
1、不安全
2、从服务器获取数据
3、传输数据量小
Post：
1、安全
2、向服务器提交数据
3、传输数据大
1、使用redirect重定向
1、过滤请求
2、验证登录
3、重写编码
4、过滤词汇
5、获取信息
6、记录日志
C/S：
为客户端和服务端的架构，成本高，需要开发服务端和客户端
B/S：
为浏览器和服务端架构，成本小，只需要开发服务端即可
在开发和面试过程中，经常遇到 tomcat 调优问题，目前可以从以下几个地方去优化；
1.通过动静分离来节约 tomcat 的资源
2.调整 tomcat 的线程池
3.调整 tomcat 的连接器
4.修改 tomcat 的运行方式
5.关闭 AJP 连接器
1.通过动静分离来节约 tomcat 的资源
动静分离来节约 tomact 的资源，通常采用；静态资源如果让 Tomcat 处理的话 Tomcat 的性能会被损耗很多，
所以我们一般 都是采用：Nginx+Tomcat 实现动静分离，让 Tomcat 只负责 jsp 文件的解析工作，Nginx 实现
静态资源的访问。
2.调整 tomcat 的线程池和调整 tomcat 的连接器
2.1 调整线程池通常和调整 tomcat 的连接器配合使用，具体修改参数如下：
<Executor name="tomcatThreadPool"
namePrefix="catalina-exec-"
maxThreads="150" <!--最大处理连接数线程-->
minSpareThreads="4"
maxIdLeTime=“6000” /><!--保留最少线程 数-->
2.2 对应连接器的修改如下：
<Connector executor="tomcatThreadPool"
port="8080"
protocol="HTTP/1.1" <!-- 使用的 BIO 模式-->
connectionTimeout="20000"
redirectPort="8443"
minProcessors="5" <!-- 同时处理请求的最小数 -->
maxProcessors="75" <!-- 同时处理请求的最大数 -->
acceptCount="1000" /> <!-- 接受最大并发数量 ,超过这个数量就会返回连接被拒绝 -->
3. 修改 tomcat 的运行方式： BIO,AIO,APR 三种模式
3.1.tomcat7.0 及之前使用 BIO 模式（堵塞）
3.2.tomcat8.0 及以后使用 AIO 模式（非堵塞 IO）
3.3.如果操作系统未安装 APR 或者 APR 路径未指到 Tomcat 默认可识别的路 径，则 APR 模式无法启动，
自动切换启动 NIO 模式。所以必须要安装 APR 和 Native，直接启动就支持 APR，APR 是从操作系统级别解决
异步 IO 问题，APR 的本质就是使用 JNI 技术调用操作系统底层的 IO 接口，所以需要提前安装所需要的依赖
4.关闭 AJP 连接器
AJP 的全称 Apache JServer Protocol，使用 Nginx+Tomca t 的架构，所以用不着 AJP 协议，所以把 AJP 连接
器禁用.
配置如下：
注释<Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>
同步交互：
发送请求需要等待服务器返回，不然不能发送下一个请求
异步交互：
发送请求不需要等待服务器返回，可随意发送下一个请求
区别：
一个需要等待，一个不需要等待
大部分时候开发都会选择异步交互
使用场景：
同步：银行转账
异步：聊天信息发送等
物理层：利用传输介质为数据链路层提供物理连接，实现比特流的透明传输。
数据链路层：负责建立和管理节点间的链路。
网络层：通过路由选择算法，为报文或分组通过通信子网选择最适当的路径。
传输层：向用户提供可靠的端到端的差错和流量控制，保证报文的正确传输。
会话层：向两个实体的表示层提供建立和使用连接的方法。
表示层：处理用户信息的表示问题，如编码、数据格式转换和加密解密等。
应用层：直接向用户提供服务，完成用户希望在网络上完成的各种工作。
什么叫单例模式？
在系统的内存中，该类的实例对象有且仅有一个，称之为单例模式。java 中 RunTime 就是单例模式。
单例模式的特点：
1、类中维护该类的静态私有成员变量；
2、私有化构造方法，严禁类的外部创建对象实例；
3、对外提供公共的静态方法，获取该类对象实例；
饿汉式：
public class Singleton{
private Singleton();
public static final INSTANCE = new Singleton();
public static Singleton getInsteance(){
return this.INSTANCE ;
}
}
描述：方法常用，但容易产生垃圾
优点：没有加锁，执行效率高
缺点：浪费内存
---------------------------
懒汉试：
public class Singleton{
private Singleton();
private static Singleton instance;
public static Singleton getInstance(){
if(instance==null){
instance = new Singleton();
}
return instance;
}
}
描述：能在多线程的情况下很好工作
优点：避免内存浪费
缺点：必须加锁才能保证单例，加锁会影响效率
---------------------------
双重锁安全验证：这种方式采用双锁机制，安全且在多线程情况下能保持高性能.。（ 推荐）
public class Singleton{
private Singleton();
private static Singleton instance;
public static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
instance = new Singleton();
}
}
return instance;
}
}
冒泡排序：
public class BubbleSort{
public static void main(String[] args){
int[] arr = {6,1,3,5,7,45};
for(i=0,i<arr.length-1,i++){
for(j=0,j<arr.length-1-i,j++){
if(arr[j]>arr[j+1]){
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp
}
}
}
}
}
-----------------
快速选择排序
public class chooseSort{
int[] arr = {5,1,23,64,12,3};
for(i=0,i<arr.length-1,i++){
for(j=i+1,j<arr.length,j++){
if(arr[i]>arr[j]){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
}
public static int sort(int[] arr, goal){
int begin = 0;
int end = arr.length-1;
int mid;
while(begin<=end){
mid = (begin+end)/2;
if(arr[mid]==goal){
return mid+1;
}else if(arr[mid]>goal){
end = mid -1;
}else{
begin = mid+1;
}
}
return -1;
}
单例模式：保证实例创建一次，节省系统开销
工厂模式：代码解耦
观察者模式：定义了对象之间的一对多的依赖，这样一来，当一个对象改变时，它的所有的依赖者都会收到通知并自动更新。
外观模式：提供一个统一的接口，用来访问子系统中的一群接口，外观定义了一个高层的接口，让子系统更容易使
用。
模版方法模式：定义了一个算法的骨架，而将一些步骤延迟到子类中，模版方法使得子类可以在不改变算法结构的情况下，重新定义算法的步骤。
状态模式：允许对象在内部状态改变时改变它的行为，对象看起来好像修改了它的类。