- 一、Java基础
- 1. JDK 和 JRE 有什么区别?
- 2. equals和==的区别
- 3. 面向对象的特征
- 4. 重载和重写的区别
- 5. final,,finally,finalize的区别
- 6. try或catch语句块中存在return语句,finally是否会执行
- 7. int和Integer的区别
- 8. java中的集合有哪些
- 9. ArrayList与LinkedList区别
- 10. ArrayList和Vector的区别
- 11. List和Map区别
- 12. List和Set区别
- 13. HashMap底层原理
- 14. hashmap和hashtable的区别
- 15. 并发编程的三要素是哪些
- 16. 创建线程有哪些方式
- 17. 不同方式创建线程的优缺点
- 18. 线程的生命周期
- 19. sleep()、wait()、join()、yield()有什么区别
- sleep()
- wait()
- yield
- join()
- 20. 线程安全问题
- 21. synchronize实现原理
- 22. synchronized和lock的区别
- synchronized和lock的用法区别
- synchronized和lock性能区别
- 23. 线程池的实现原理
2. equals和==的区别JDK:Java Development Kit 的简称,java 开发工具包,提供了 java 的开发环境和运行环境。
JRE:Java Runtime Environment 的简称,java 运行环境,为 java 的运行提供了所需环境。
JDK 包含了 JRE,同时还包含了编译 java 源码的编译器 javac,还包含了很多 java 程序调试和分析的工具。
3. 面向对象的特征==与equals的主要区别是:==常用于比较原生类型,而equals()方法用于检查对象的相等性。
如果==和equals()用于比较对象,当两个引用地址相同,==返回true。而equals()可以返回true或者false要看是否重写,equals()方法的初始默认行为是比较对象的内存地址值。
4. 重载和重写的区别1.封装
封装是面向对象的特征之一,是对象和类概念的主要特性。
封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
2.继承
面向对象编程(OOP)语言的一个主要功能就是“继承”。继承是指这样一种能力:
它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能
进行扩展。
3.多态
多态性(polymorphisn)
是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同
的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的
指针。
实现多态,有二种方式,重写,重载。
5. final,finally,finalize的区别
- 重载
表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或类型不同)。
- 重写
表示子类中的方法可以与父类中的某个方法的名称和参数完全相同,通过子类创建的实例对象调用这个方法时,将调用子类中的定义方法,这相当于把父类中定义的那个完全相同的方法给覆盖了,这也是面向对象编程的多态性的一种表现。子类覆盖父类的方法时,只能比父类抛出更少的异常,或者是抛出父类抛出的异常的子异常,因为子类可以解决父类的一些问题,不能比父类有更多的问题。子类方法的访问权限只能比父类的更大,不能更小。如果父类的方法是private类型,那么,子类则不存在覆盖的限制,相当于子类中增加了一个全新的方法。
6. try或catch语句块中存在return语句,finally是否会执行1.final
用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。
2.finally
是异常处理语句结构的一部分,表示总是执行。
3.finalize
是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,
可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等.JVM不保证
此方法总被调用。
7. int和Integer的区别不管有没有出现异常,finally.块中代码都会执行。
当try和catch中有return时,finally仍然会执行。
finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,不管finally中的代码怎么样,返回的值都不会改变,任然是之前保存的值),所以函数返回值是在finally执行前确定的。
finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。
8. java中的集合有哪些int是Java提供的8种原始数据类型之一。Java为每个原始类型提供了封装类,Integer是Java为int提供的封装类。
int的默认值为0,而Integer的默认值为null,是引用类型,即Integer可以区分出未赋值和值为0的区别,int则无法表达出未赋值的情况。
java 中的集合分为单列集合和双列集合,单列集合顶级接口为 Collection,双列集合顶级接口为 Map。
Collection 的子接口有两个:List和Set。List 接口的特点:元素可重复,有序(存取顺序)。 list 接口的实现类如下:
- ArrayList:底层实现是数组,查询快,增删慢,线程不安全,效率高
- Vector:底层实现是数组,查询快,增删慢,线程安全,效率低【废弃】
- LinkedList:底层实现是链表,增删快,查询慢,线程不安全,效率高
Set 接口的特点:元素唯一,不可重复,无序。 Set 接口实现类如下:
- HashSet:底层实现 hashMap,数组+链表实现,不允许元素重复,无序。
- TreeSet:底层实现红黑二叉树,实现元素排序
9. ArrayList与LinkedList区别Map 接口的特点:key-value 键值对形式存储数据 Map 接口实现类如下:
- HashMap:底层数组+链表实现,线程不安全效率高;
- TreeMap:底层红黑二叉树实现,可实现元素的排序;
- LinkedHashMap:底层hashmap+linkedList 实现,通过 hashmap 实现 key-value 键值对存储,通过链表实现元素有序。
10. ArrayList和Vector的区别因为Array是基于索引index)的数据结构,它使用索引在数组中搜索和读取数据是很快的。Array获取数据的时间复杂度是o(1),但是要删除数据却是开销很大的,因为这需要重排数组中的所有数据。
相对于ArrayList,LinkedList插入是更快的。因为LinkedList不像ArrayList一样,不需要改变数组的大小,也不需要在数组装满的时候要将所有的数据重新装入一个新的数组,这是ArrayList最坏的一种情况,时间复杂度是o(n),而LinkedList中插入或删除的时间复杂度仅为o(1)。ArrayList在插入数据时还需要更新索引(除了插入数组的尾部)。
类似于插入数据,删除数据时,LinkedList也优于ArrayList。
LinkedList需要更多的内存,因为ArrayList的每个索引的位置是实际的数据,而LinkedList中的每个节点中存储的是实际的数据和前后节点的位置。
如果你的应用经常需要随机访问数据,则考虑使用ArrayList。因为如果需要
LinkedList中的第n个元素的时候,你需要从第一个元素顺序数到第n个数
据,然后读取数据。
你的应用经常进行插入和删除元素,更少的读取数据,考虑使用LinkedList。,
因为插入和删除元素不涉及重排数据,所以它要比ArrayList要快。
11. List和Map区别同步性:Vector是线程安全的,也就是说是同步的,而ArrayList是线程不安全的,不是同步的。
数据增长:当需要增长时,Vector默认增长为原来一倍,而ArrayList却是原来的50%,这样ArrayList就有利于节约内存空间。
如果涉及到堆栈,队列等操作,应该考虑用Vector,如果需要快速随机访
问元素,应该使用ArrayList
12. List和Set区别Lst特点:元素有放入顺序,元素可重复;
Map特点:元素按键值对存储,无放入顺序;
List接口有三个实现类:LinkedList,ArrayList,Vector;
LinkedList:底层基于链表实现,链表内存是散乱的,每一个元素存储本身内存地址的同时还存储下一个元素的地址。链表增删快,查找慢;
Map接有三个实现类:HashMap,HashTable,LinkedHashMap
Map相当于和Collection一个级别的;Map集合存储键值对,且要求保持
键的唯一性;
13. HashMap底层原理List,Set都是继承自Collection接口
List特点:元素有放入顺序,元素可重复。Set特点:元素无放入顺序,元素不可重复(注意:元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的)
List接口有三个实现类:LinkedList,ArrayList,Vector。.Set接口有两个实现类:HashSet(底层由HashMap实现),LinkedHashSet
14. hashmap和hashtable的区别HashMap 底层是数组+链表(LinkedList)实现,hashMap 默认初始化容量为 16,也就是说数组索引值为0-15,每个数组中存储一个链表。
当 hashmap 空间使用达到 0.75 后,会对数组进行扩容,新建数组,然后将元素拷贝到新的数组中,每次扩容翻倍。
存储元素时,存储对象为 Map.Entry,entry对象包含四个信息,key、value、hash 值、链表地址值,因为存储元素时,会根据hash 值%16 计算,元素存储数组中的索引位置。
但是有可能发生 hash 碰撞现象,即两个元素不相同,却有一样的 hash 值,这样的话,就将元素在数组中存入链表中,以链表的形式进行元素的存储,第一个entry 存在链表顶端,再有hash 值一致的entry 存入,则链接在第一个元素之后。
put()方法存储元素时,根据 hash值定位数组,在链表中通过HashCode()和equals()方法,定位元素的key,若没有一致的entry,则在链表中添加entry 对象,若找到一样的entry,则将oldValue返回,将新的value 存入到entry中。
15. 并发编程的三要素是哪些相同点:
- 二者都是 key-value 的双列集合;
- 底层都是通过数组+链表方式实现数据的存储;
不同点:
- 继承的父类不同
Hashtable 继承自 Dictionary 类,而 HashMap 继承自 AbstractMap类。但二者都
实现了Map 接口。
线程安全性不同
- Hashtable 中的方法是 Synchronize 的,而 HashMap 中的方法在缺省情况下是非Synchronize 的。在多线程并发的环境下,可以直接使用Hashtable,不需要自己为它的方法实现同步,但使用HashMap时就必须要自己增加同步处理。
- hashMap 允许 null 键和 null 值,只能有一个,但是 hashtable不允许。
- HashMap 是 java 开发中常用的类,但是 Hashtable和 vector 一样成为了废弃类,不推荐使用,因为有其他高效的方式可以实现线程安全,比如ConcurrentHashMap。
16. 创建线程有哪些方式
- 原子性
原子性指的是一个或者多个操作,要么全部执行并且在执行的过程中不被其他操作打断,要么就全部都不执行。
- 可见性
可见性指多个线程操作一个共享变量时,其中一个线程对变量进行修改后,其他线程可以立即看到修改的结果。
- 有序性
有序性,即程序的执行顺序按照代码的先后顺序来执行。
17. 不同方式创建线程的优缺点
- 继承Thread类创建线程类
定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。创建Thread子类的实例,即创建了线程对象。
调用线程对象的start()方法来启动该线程。
- 通过Runnable接口创建线程类
定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。创建Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
调用线程对象的start()方法来启动该线程。
- 通过Callable和Future创建线程
创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
使用FutureTask对象作为Thread对象的target创建并启动新线程。
调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
18. 线程的生命周期
- 采用实现Runnable、Callable接口的方式创见多线程时:
优势是:线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。劣势是:编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread0方法。
- 使用继承Thread类的方式创建多线程时:
优势是:编写简单,如果需要访问当前线程,则无需使用Thread.currentThread0方法,直接使用this即可获得当前线程。
劣势是:线程类已经继承了Thread类,所以不能再继承其他父类。
19. sleep()、wait()、join()、yield()有什么区别新建New()、就绪(Runnable)、运行Running)、阻塞(Blocked)和死亡(Dead)5种状态
新建状态:
使用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保特这个状态直到程序start()这个线程。
就绪状态:
当线程对象调用了sleep()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
运行状态:
如果就绪状态的线程获取CPU资源,就可以执行ru0,此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。
死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
sleep()sleep()方法需要指定等待的时间,它可以让当前正在执行的线程在指定的时间内暂停执行,进入阻塞状态,该方法既可以让其他同优先级或者高优先级的线程得到执行的机会,也可以让低优先级的线程得到执行机会。但是sleep()方法不会释放“锁标志”,也就是说如果有synchronized同步块,其他线程仍然不能访问共享数据。
wait()wait()方法需要和notify()及notifyAll()两个方法一起介绍,这三个方法用于协调多个线程对共享数据的存取,所以必须在synchronized语句块内使用,也就是说,调用wait(),notify()和notifyAll()的任务在调用这些方法前必须拥有对象的锁。注意,它们都是Object类的方法,而不是Thread类的方法。
wait()方法与sleep()方法的不同之处在于,wait()方法会释放对象的“锁标志”。当调用某一对象的wait()方法后,会使当前线程暂停执行,并将当前线程放入对象等待池中,直到调用了notify()方法后,将从对象等待池中移出任意一个线程并放入锁标志等待池中,只有锁标志等待池中的线程可以获取锁标志,它们随时准备争夺锁的拥有权。当调用了某个对象的notifyA11()方法,会将对象等待池中的所有线程都移动到该对象的锁标志等待池。
除了使用notify()和notifyAll()方法,还可以使用带毫秒参数的wait(longtimeout)方法,效果是在延迟timeout毫秒后,被暂停的线程将被恢复到锁标志等待池。
此外,wait(),notify()及notifyAll()只能在synchronized语句中使用,但是如果使用的是ReenTrantLock实现同步,该如何达到这三个方法的效果呢?解决方法是使用ReenTrantLock…newCondition()获取一个Condition类对象,然后Condition的await(),signal()以及signalA1l()分别对应上面的三个方法。
yieldyield()方法和s1eep()方法类似,也不会释放“锁标志,区别在于,它没有参数,即yield()方法只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行,另外yield()方法只能使同优先级或者高优先级的线程得到执行机会,这也和sleep()方法不同。
join()20. 线程安全问题join()方法会使当前线程等待调用join()方法的线程结束后才能继续执行
21. synchronize实现原理线程安全是多线程领域的问题,线程安全可以简单理解为一个方法或者一个实例可以在多线程环境中使用而不会出现问题。
在Java多线程编程当中,提供了多种实现Java线程安全的方式:
- 最简单的方式,使用Synchronization关键字
- 使用java.util.concurrent.atomic包中的原子类,例如AtomicInteger
- 使用java.util.concurrent.locks包中的锁
- 使用线程安全的集合ConcurrentHashMap
- 使用volati1e关键字,保证变量可见性(直接从内存读,而不是从线程cache读)
22. synchronized和lock的区别同步代码块是使用monitorenter和monitorexit指令实现的,同步方法(在这
看不出来需要看JVM底层实现)依靠的是方法修饰符上的ACC_SYNCHRONIZED实
现。
synchronized和lock的用法区别synchronized(隐式锁):在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。
lock(显示锁):需要显示指定起始位置和终止位置。一般使用ReentrantLock类做为锁,多个线程中必须要使用一个ReentrantLock类做为对象才能保证锁的生效。且在加锁和解锁处需要通过10ck()和unlock()显示指出。所以一般会在finally块中写unlock()以防死锁。
synchronized和lock性能区别synchronized是托管给JVM执行的,而lock是Java写的控制锁的代码。在JDK1.5中,synchronize是性能低效的。因为这是一个重量级操
作,需要调用操作接口,导致有可能加锁消耗的系统时间比加锁以外的操作还多。相比之下使用Java提供的Lock对象,性能更高一些。但是到了JDK1.6,发生了变化。synchronize在语义上很清晰,可以进行很多优化,有适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在JDK1.6上synchronize的性能并不比Lock差。
23. 线程池的实现原理synchronized和lock机制区别
synchronized原始采用的是CPU悲观锁机制,即线程获得的是独占锁。独占锁意味着其他线程只能依靠阻塞来等待线程释放锁。
Lock用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐
观锁实现的机制就是CAS操作(Compare and Swap)。
当提交一个新任务到线程池时,线程池的处理流程如下:
- 线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一
个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则进
入下个流程。- 线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任
务存储在这个工作队列里。如果工作队列满了,则进入下个流程。- 线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新
的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。



