1、Java集合 的理解
答:
Arraylist 基于动态数组的数据结构,对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针
Linkedlist 基于链表的数据结构,对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
Vector:基于Synchronized实现的线程安全的ArrayList
HashSet 根据hashcode和equals来判断是否是同一个对象,如果hashcode一样,并且equals返回true,则是同一个对象。无序的,存放的是唯一值
TreeSet 不能存放重复对象,但是TreeSet会自动排序,如果存放的对象不能排序则会报错,所以存放的对象必须指定排序规则。
LinkedHashSet 按照插入顺序保存对象,同时还保存了HashSet的查询速度。
Hashmap 据键的 HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度。键唯一,只能有一个null
Hashtable 它不允许记录的键或者值为空;它支持线程的同步,写入时会比较慢
LinkedHashMap 比 HashMap 多了一个链表的结构,输出时其元素是有顺序的,而 HashMap 输出时是随机的
TreeMap 不仅可以保持顺序,而且可以用于排序(TreeMap实现sortMap接口,能够把它保存的记录根据键排序,默认是按升序排序)
2、Java并发包当中的类,它们都有哪些作用,以及它们的实现原理。
答:有很多,建议看 Java API 学习,包 java.util.concurrent.*
volatile 变量自身有两个特性:
原子性:对于任意单个volatile变量的读/写具有原子性,但是类似与volatileVal++这种复合操作来说,它就不具有原子性。
可见性:对于一个volatile变量的读,总是能看到任意线程对这个volatile变量最后的写入。
volatile 的使用条件要严格遵循 —— 即变量真正独立于其他变量和自己以前的值 —— 在某些情况下可以使用 volatile 代替 synchronized 来简化代码。然而,使用 volatile 的代码往往比使用锁的代码更加容易出错。
ConcurrentHashMap 是线程安全的HashMap的实现,默认构造同样有initialCapacity和loadFactor属性,不过还多了一个concurrencyLevel属性。其内部使用锁分段技术,维持这锁Segment的数组,在Segment数组中又存放着Entity[]数组,内部hash算法将数据较均匀分布在不同锁中。
ReentrantLock volatile特性及AQS同步器正是ReentrantLock实现的基础。通过上面AQS的介绍及原理分析,可知道是以volatile维持的int类型的state值,来判断线程是执行还是在syn队列中等待。ReentrantLock的实现不仅可以替代隐式的synchronized关键字,而且能够提供超过关键字本身的多种功能。
CopyOnWriteArrayList是一个线程安全、并且在读操作时无锁的ArrayList
CopyOnWriteArraySet基于CopyOnWriteArrayList实现,区别是保证了无重复元素,但在add时每次都要进行数组的遍历,因此性能会略低。
3、IO包和NIO包中的内容
答:NIO包(java.nio.*)引入了四个关键的抽象数据类型,它们共同解决传统的I/O类中的一些问题。
1.Buffer:它是包含数据且用于读写的线形表结构。其中还提供了一个特殊类用于内存映射文件的I/O操作。
2.Charset:它提供Unicode字符串影射到字节序列以及逆影射的操作。
3. Channels:包含socket,file和pipe三种管道,它实际上是双向交流的通道。
4. Selector:它将多元异步I/O操作集中到一个或多个线程中
IO使得I/O比传统I/O更加高效。在一个I/O操作占很高比例的程序中,想想看会有什么不同。如果一个应用需要使用socket拷贝文件或者传输字节,使用NIO可能会得到更快的性能.
为NIO的API是基于java.io之上扩展的功能。NIO通过扩展本地的IO API,NIO的核心是IO线程池,为广大开发者使用更加强大的方式操作数据流带来了新的可能性。
IO NIO
面向流 面向缓冲
阻塞IO 非阻塞IO
无 选择器
4、Java的虚拟机的内容。这部分主要包括三部分,GC、类加载机制,以及内存。
答:首先java内存分配,分为堆区、虚拟机栈、本地方法栈、方法区、程序计数器。根据对象存活时间分为年轻代、年老代、永久代。
年轻代分为eden区、survivior0、survivior1区。在年轻代上GC采用停止复制算法,优先清理eden区上的无效对象,剩余以此存入两个存活区。
如果年轻代经过一次GC仍有10%存活,将分配部分对象到年老代。年老代采用标记整理算法,标记仍然存活的对象,整理移动保证连续性。
永久代(方法区)的GC主要有常量池中的常量,无用的类信息。如果没有引用和相关对象实例会进行垃圾回收。
虚拟机的类加载机制:虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。
类装载器把一个类装入JVM中需要经过加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)阶段。
其中准备、验证、解析3个部分统称为连接(Linking)。
加载阶段:
(1) 通过一个类的全限定名来获取定义此类的二进制字节流
(2) 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
(3) 在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口。
验证阶段:
文件格式验证:验证字节流是否符合Class文件格式的规范;例如::常量池中的常量是否有不被支持的类型。
元数据验证:对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求;例如:这个类是否有父类,除了java.lang.Object之外。
字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。
符号引用验证:确保解析动作能正确执行。
准备阶段:
为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。
方法区的分配包括类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在堆中。
解析阶段
是虚拟机将常量池内的符号引用替换为直接引用的过程。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。
初始化阶段
(1) 遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。
生成这4条指令的最常见的Java代码场景是:使用new关键字实例化对象的时候,读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候,
以及调用一个类的静态方法的时候。
(2) 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
(3) 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
(4)当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
类加载器优先级由高到低 : 启动类加载器 扩展类加载器 应用程序类加载器 自定义类加载器
Bootstrap ClassLoader–》 Extension ClassLoader --》Application ClassLoader --》User ClassLoader
双亲委派模型:
(1).如果一个类加载器收到了类加载请求,它不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器去完成。
(2).每一层的类加载器都把类加载请求委派给父类加载器,直到所有的类加载请求都应该传递给顶层的启动类加载器。
(3).如果顶层的启动类加载器无法完成加载请求,子类加载器尝试去加载,如果连发起类加载请求的类加载器也无法完成加载请求时,将会抛出ClassNotFoundException。
5、Java中间件,分布式系统、分布式缓存、消息队列
答:
JAVA中间件:服务框架中间件:解决集群间的访问通信问题。消息中间件:解决应用之间的消息传递、解耦、异步的问题。数据访问中间件:解决应用访问数据库的共性问题。
分布式系统:一定是有多个节点组成的系统,一般一个节点就是一台服务器,节点之间是相互连通协作的,这些连通的节点上部署了我们的组件,共同服务于一个大型系统。比如淘宝网,在对浏览器发送请求的背后,是一个大型的分布式系统为我们服务,整个系统有的负责请求处理,有的负责存储,有的负责计算,最终通过相互的协作把请求的结果返回给浏览器,并呈现给我们。
分布式缓存:就是把数据存放在不同的物理机器上,利用分布式缓存中间件进行处理数据。Redis和memcached缓存系统都是以key-value的形式存储和访问数据,在内存中维护一张巨大的HashTable。但是redis比memcached支持的数据类型更多,有五种数据类型:String、Hash、List、Set、ZSet(有序集合)。注意:分布式缓存系统需要用到一致哈希算法,它的的好处在于节点个数发生变化(减少或增加)时无需重新计算哈希值,避免大量key的重新映射,保证数据储存或读取时可以正确、快速地找到对应的节点
消息中间件:负责消息的收发管理,利用高效可靠的异步消息传递机制集成到分布式系统。五大优点:解耦、异步、横向扩展、安全可靠、顺序保证。常见的有activeMQ(支持多语言,实现jms1.1),RabbitMQ(支持更多语言,基于AMQP规范),kafka(高吞吐量,分布式,分区,O(1)磁盘顺序提供消息持久化)
分布式和集群的区别:
分布式是指将不同的业务分布在不同的地方。
集群指的是将几台服务器集中在一起,实现同一业务。
负载均衡提供了一种廉价有效的方法扩展服务器带宽和增加吞吐量,加强网络数据处理能力,提高网络的灵活性和可用。
6、Hashmap为什么存取效率高?
因为HashMap的底层数据结构,是链表+数组的组合方式,也叫链表散列、“拉链法”
存储:往HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标), 如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放。如果数组该位置上没有元素,就直接新建节点添加到数组。
读取:从HashMap中get元素时,首先计算key的hashCode,找到数组中对应位置的某一元素,然后通过key的equals方法在对应位置的链表中找到需要的元素。
简单来说:HashMap 在底层将 key-value 当成一个整体进行处理,这个整体就是一个 Entry 对象。HashMap 底层采用一个 Entry[] 数组来保存所有的 key-value 对,当需要存储一个 Entry 对象时,会根据hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry时,
也会根据hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该Entry。
7、哪些情况下sql索引会失效?
1、where条件中用or,即使其中有条件带索引,也不会使用索引查询。这就是查询尽量不要用or的原因。要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引。
alter table dept drop index myind; --删除符合索引
alter table dept add index myind (dname);
explain select * from dept where dname='研发部' or loc='aa'; --dname列上索引不生效
2、对于多列索引,where条件不是使用最左边的列,则不会使用索引。
alter table dept add index myind (dname,loc);
explain select * from dept where dname='研发部'; 会显示使用到了索引myind
explain select * from dept where loc='MsBDpMRX'; 不会显示使用到了索引myind
3、like的模糊查询以%开头,索引失效
explain select * from dept where dname like '%研发部'; 不会显示使用到了索引myind
explain select * from dept where dname like '研发部%'; 会显示使用到了索引myind
4、如果列类型是字符串,那一定要在条件中将数据使用引号引用起来。否则不使用索引。(添加时,字符串必须’’)
select * from dept from dname=1234; //不会使用到索引
select * from dept from dname='1234'; //会使用到索引
5、如果mysql估计使用全表扫描要比使用索引快,则不使用索引。
show status like 'Handler_read%'; --查看索引的使用情况
注意:
handler_read_key:这个值越高越好,越高表示使用索引查询到的次数。
handler_read_rnd_next:这个值越高,说明查询低效。
8、Java中的String,StringBuilder,StringBuffer三者的区别
- 在执行速度上,执行速度依次为:StringBuilder > StringBuffer > String
String最慢的原因:String对象一旦创建之后该对象是不可更改的,但后两者是可以更改的。Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢。 - 在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的
String:适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况