整型:byte(8)、short(16)、int(32)、long(64)浮点型:float(32)、double(64)布尔型:boolean(8)字符型:char(16) 2.String、StringBuffer、StringBuilder
String 由 char[] 数组构成,使用了 final 修饰,是不可变对象,可以理解为常量,线程安全;对 String 进行改变时每次都会新生成一个 String 对象,然后把指针指向新的引用对象。StringBuffer 线程安全;StringBuiler 线程不安全。操作少量字符数据用 String;单线程操作大量数据用 StringBuilder;多线程操作大量数据用 StringBuffer。 二、集合 1.List(线性结构)
ArrayList Object[] 数组实现,默认大小为 10 ,支持随机访问,连续内存空间,插入末尾时间复杂度 o(1),插入第 i 个位置时间复杂度 o(n - i)。扩容,大小变为 1.5 倍,Arrays.copyOf(底层 System.ArrayCopy),复制到新数组,指针指向新数组。Vector 类似 ArrayList,线程安全,扩容默认增长为原来的 2 倍,还可以指定增长空间长度。linkedList 基于链表实现,1.7 为双向链表,1.6 为双向循环链表,取消循环更能分清头尾。 2.Map(key-value键值对)
HashMap
底层数据结构,JDK 1.8 是数组 + 链表 + 红黑树,JDK 1.7 无红黑树。链表长度大于 8 时,转化为红黑树,优化查询效率。初始容量为 16,通过 tableSizeFor 保证容量为 2 的幂次方。寻址方式,高位异或,(n-1)&h 取模,优化速度。扩容机制,当元素数量大于容量 x 负载因子 0.75 时,容量扩大为原来的 2 倍,新建一个数组,然后转移到新数组。基于 Map 实现。线程不安全。
HashMap (1.7) 多线程循环链表问题
在多线程环境下,进行扩容时,1.7 下的 HashMap 会形成循环链表。怎么形成循环链表: 假设有一 HashMap 容量为 2 , 在数组下标 1 位置以 A -> B 链表形式存储。有一线程对该 map 做 put 操作,由于触发扩容条件,需要进行扩容。这时另一个线程也 put 操作,同样需要扩容,并完成了扩容操作,由于复制到新数组是头部插入,所以 1 位置变为 B -> A 。这时第一个线程继续做扩容操作,首先复制 A ,然后复制 B ,再判断 B.next 是否为空时,由于第二个线程做了扩容操作,导致 B.next = A,所以在将 A 放到 B 前,A.next 又等于 B ,导致循环链表出现。
HashTable
线程安全,方法基本全用 Synchronized 修饰。初始容量为 11 ,扩容为 2n + 1 。继承 Dictionary 类。 三、多线程 volatile
在多线程环境下,保证变量的可见性。使用了 volatile 修饰变量后,在变量修改后会立即同步到主存中,每次用这个变量前会从主存刷新。禁止 JVM 指令重排序。单例模式双重校验锁变量为什么使用 volatile 修饰? 禁止 JVM 指令重排序,new Object()分为三个步骤:申请内存空间,将内存空间引用赋值给变量,变量初始化。如果不禁止重排序,有可能得到一个未经初始化的变量。 四、JAVA虚拟机 1.内存模型
堆 由线程共享,存放 new 出来的对象,是垃圾回收器的主要工作区域。栈 线程私有,分为 Java 虚拟机栈和本地方法栈,存放局部变量表、操作栈、动态链接、方法出口等信息,方法的执行对应着入栈到出栈的过程。方法区 线程共享,存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等信息,JDK 1.8 中方法区被元空间取代,使用直接内存。 2.类加载机制
双亲委派模式
当一个类需要加载时,判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载。加载的时候,首先会把该请求委派该父类加载器的 loadClass() 处理,因此所有的请求最终都应该传送到顶层的启动类加载器 BootstrapClassLoader 中。当父类加载器无法处理时,才由自己来处理。当父类加载器为 null 时,会使用启动类加载器 BootstrapClassLoader 作为父类加载器。 3.可以作为GCRoot的对象
a) 虚拟机栈中引用的对象(栈帧中的本地变量表)b) 方法区中类静态属性引用的对象c) 方法区中常量引用的对象d) 本地方法栈中 Native 方法引用的对象 五、数据库 1.聚簇索引和非聚簇索引
都使用 B+ 树作为数据结构聚簇索引中数据存在主键索引的叶子结点中,得到 key 即得到 data ;非聚簇索引的数据存在单独的空间。聚簇索引中辅助索引的叶子结点存的是主键;非聚簇索引中叶子结点存的是数据的地址;聚簇索引的优势是找到主键就找到数据,只需一次磁盘 IO ;当 B+ 树的结点发生变化时,地址也会发生变化,这时非聚簇索引需要更新所有的地址,增加开销。 2.为什么使用B+树作为索引
磁盘 IO 的次数是衡量索引数据结构好坏的重要指标
B+树作为数据库索引:
1.单一节点存储更多的元素,使得树更加矮胖,查询的IO次数更少
2.所有数据都放在叶子节点,所有查询都要查找到叶子节点,查询性能稳定
3.由于叶子节点形成有序链表,便于范围查询。
主外键需要添加索引经常查询的数据列最好建立索引对于需要在指定范围内快速或频繁查询的数据列,因为索引已经排序,其指定的范围是连续的,查询可以利用索引的排序,加快查询的时间经常用在 where 子句中的数据列,将索引建立在 where 子句的集合过程中,对于需要加速或频繁检索的数据列,可以让这些经常参与查询的数据列按照索引的排序进行查询,加快查询的时间。