特性
重载(Overload)和重写(Override)的区别是什么?
-
重载是某个方法,可以有不同的参数列表、返回值和修饰符。在实际应用中实在是太常见了,就是比如自定义个add方法吧,这个add方法可以往数组里存一个字符串;然后我可以在他下面再写个add方法,这次可以存个字符串和int数字,这样我调用的时候就可以根据需要来传参。
-
重写是子类继承父类时,子类想对父类的方法做出修改,就可以
@Override
注释,写出跟父类相同的方法体(返回值的范围可以比父类小),方法内容随意重写。
Java8的新特性你都了解哪些?
λ表达式、方法引用表达式、Stream流、新的时间类等等。
在项目中常用的λ表达式和方法引用表达式,比如对分块文件列表 进行按名称排序,就是用了λ表达式和comparingInt
方法来实现的。
抽象类和接口的区别?
-
抽象类一般用作实体类的抽象,用
abstract
修饰,接口一般是在service层、Dao层之类的系统架构上面使用,是对动作的抽象,用
interface
修饰。 -
一个抽象类只能被单继承(
extends
),一个接口可以被多实现(implements
)。 -
实现一个接口,必须实现接口中的所有方法,抽象类可以有选择地进行继承。
==
和 equals()
的区别是什么?
-
==
是判断两个变量或实例是不是指向同一个内存空间。equals
是判断两个变量或实例所指向的内存空间的值是不是相同。 -
==
是一个操作符,equals
是一个方法。
- 常用的
Object
的equals()
方法其实就是==
;而String
的equals()
重写了此方法,是比较对象的值。.所以equals()
方法有没有被重写是判断其与==
的区别的主要关注点。
阐述final、finally、finalize的区别
final
:
-
final是一个修饰符,意为最终态。如果一个类被声明为final,意味着它不能再派生新的子类。(因此一个类不能既被声明为
abstract
,又被声明为final的,因为abstract是抽象的,要使用它必须要去继承。) -
将变量或方法声明为final,可以保证他们使用中不被改变。被声明为final的变量必须在声明时给定初值,而以后的引用中只能读取,不可修改,被声明为final的方法也同样只能使用,不能重载。
finally
:
- 在异常处理时提供finally块来执行终结操作,即无论try/catch执行结果如何,都会在之后执行finally块里面的代码。
finalize
:
- finalize()是Object中的方法,当垃圾回收器将要回收对象所占内存之前被调用,即当一个对象被虚拟机宣告死亡时会先调用finalize()方法,让此对象处理它生前的最后事情(等于是下了一个最终通牒)。
String 类的常用方法都有那些?
length()
,equals()
,getByte()
,转换大小写
,trim()
,subString(int)
等等。
break和continue有什么区别??
用break语句可以使流程跳出switch语句体,也可以用break语句在循环结构终止本层循环体,从而提前结束本层循环。
continue是结束本次循环,继续下一次循环。
数据类型
String
,StringBuffer
,StringBuilder
的区别是什么?
String
是一个用final修饰的字符数组,是一个不可变的对象。每次修改字符串对象时都等同于生成了一个新的对象,然后将指针指向新对象。
StringBuffer
和StringBuilder
是可变的字符序列,都在原来的对象的本身进行操作;
StringBuffer
是线程安全;StringBuilder
是线程不安全的,但是StringBuilder
的速度是比StringBuffer
快的。
总体的速度比较上,大部分情况下StringBuilder
>StringBuffer
>String
。
int
与Integer
的区别?
首先最本质的区别就是,int类型是一个数据基本类型,而Integer是一个基本类型的包装类。
由此就能得出以下几点区别:
-
int是直接存储数据值,而Integer是对象的引用。(当new一个Integer时,实际上是生成一个指针指向此对象)
-
Integer变量必须实例化后才能使用,而int变量不需要。
-
int默认值为0,Integer默认值为null。
集合
你平常用过哪些集合?
常用的就是ArrayList,LinkedList,HashMap,其他的还有TreeMap,HashTable,TreeSet,HashSet等。
List、set、map他们的区别?
首先,List
和Set
都是Collection
下的子接口,而Map
是与Collection同级的接口。
-
Set接口最重要的一点是不包含重复元素,而且是无序的。而他下面的
TreeSet
实现了有序。 -
List接口允许重复元素,并且是保证顺序的,可以插入null元素,其下面常用的有
LinkedList
和ArrayList
。 -
Map是通过键值对的方式存储数据,每一个键都可以获取唯一对应的值。
LinkedList和ArrayList的区别?
ArrayList:
- 底层数据结构是数组。
- 由于其存放数据的是一块连续的内存空间,就可以根据数组的首地址加上偏移量,直接计算出第n个元素在内存中的位置,所以它查询快。
- 增删慢:还是由于其数据结构的原因,当想要在中间插入一条数据时,后面的数据需要全部往后挪一位,甚至是要将新数组重新分配内存空间。
LinkedList:
- 底层数据结构是双向链表。
- LinkedList存放数据是分散的,每一个数据保存自身数据+下一个数据的内存地址查询时要从头部顺着链子依次查,所以它查询慢。
- 增删快:LinkedList想要插入数据,只需要将插入位置断开,就像自行车链条一样,安插进去,修改的只是节点之间的引用。
Arraylist为什么线程不安全?怎么解决?
ArrayList添加数据时,会分为两步:存放数据和修改size。当存放数据后线程被抢,然后另一线程也存放数据,就会导致存放数据实际只有一条,而size为2。
解决方法:1. 使用synchronized
关键字。2. 使用Collections类提供的synchronizedList()
方法。
怎么删除list中的某个数据?
- 暴力for循环遍历,调用remove
- 原list叫listA,将某个数据存为另一个listB,listA调用removeAll方法,删除listB
- 得到该list的迭代器,使用迭代器的remove方法。
Java的队列有了解吗?
队列即Queue
,是与List,Set同一级别的接口。LinkedList
实现了Queue接口下的子接口Deque(双端队列)。
没有实现阻塞接口的有:LinkedList,PriorityQueue
,ConcurrentLinkedQueue
。
实现阻塞接口的:ArrayBlockingQueue
(基于数组的有界队列),LinkedBlockingQueue
(基于链接的有界队列),PriorityBlockingQueue
(基于优先级的无界队列),DelayQueue
,SynchronousQueue
HashMap
看完这篇会对HashMap有一个更深刻的认识: 手写Java HashMap核心源码
HashMap底层结构是什么?
HashMap的底层结构是链表+红黑树。HashMap如果在创建时没有指定容量,默认初始容量为16。扩容阈值为数组长度的0.75,每次扩充容量翻倍。当HashMap长度小于8时使用链表,而当HashMap长度大于8时自动转化为红黑树。
HashMap是否是线程安全的?如何保证HashMap线程安全?
HashMap不是线程安全的。
-
使用
Collection
类里面的synchronizedMap()
包装一下,即可得到线程安全的map;(使用的是悲观锁) -
使用
ConcurrentHashMap
类,可以直接得到一个线程安全的HashMap(采用的是乐观锁)
HashMap为什么初始化大小为16?
HashMap的初始化大小是将初始化大小二进制按位与,而扩容机制是HashMap每次扩容都是以 2的整数次幂进行扩容。因此最合适的初始化大小是16,4和8理论上也可以,但是因为太小容易导致map扩容影响性能;太大浪费资源。
总结就是:
-
减少hash碰撞。 -
提高map查询效率。 -
分配过小防止频繁扩容。
-
分配过大浪费资源。
HashMap怎么进行扩容?
HashMap如果在创建时没有指定容量,默认初始化大小为16。HashMap每次扩容都是以 2的整数次幂进行扩容。
红黑树和链表怎么进行转化?
当HashMap长度小于8时使用链表,而当HashMap长度大于8时自动转化为红黑树。目的是为了减少搜索时间。
纯链表的平均查找长度为(n+1)/2,红黑树平均查找长度则为log2n。 长度为8的时候,红黑树平均查找长度为3,链表平均查找长度为8/2=4,这才有转换为树的必要。链表长度如果是小于等于6, 6/2=3,速度已经很快,并且转化为树结构和生成树的时间不会很短,所以没必要转成红黑树。
HashMap和TreeMap的区别?
- 实现
HashMap基于哈希表;
TreeMap基于红黑树。 - 存储和遍历
HashMap随机存,随机遍历;
TreeMap升序存,排序遍历。 - 性能损耗
HashMap基本无损耗;
TreeMap插入删除速度慢,需要维护树的平衡。 - 键值对
HashMap键、值均可为null;
TreeMap键、值均不可为null。 - 安全性和效率
HashMap不安全,效率高;
TreeMap不安全,效率低。
HashMap和Hashtable有什么区别?
HashMap线程不安全,效率高,键值允许null,非同步,适合单线程环境;
Hashtable线程安全,效率低,不允许null键值,同步,适合多线程。
但是多线程环境下仍不建议使用HashTable,因为
1. HashTable是遗留类,内部实现很多没有优化,冗余多,
2. 内部全部加了Syn锁,重量级锁,效率低,
3. 多线程有了更好的同步的ConcurrentHashMap替代。
HashMap和LinkedHashMap有什么区别?
HashMap
是最常用的一个Map,根据key的hash值来存储数据,底层是链表+红黑树,当长度小于8时使用链表,大于8时自动转化为红黑树。HashMap是线程不安全的,如果需要线程安全,可以使用Collections.synchronizedMap()
来获取一个线程安全的map;也可以直接使用ConcurrentHashMap
类来获取一个线程安全的HashMap。
LinkedHashMap
是HashMap下面一个子类,跟HashMap的区别就是HashMap是无序的,而LinkedHashMap是可以保存插入顺序的。当对输出顺序有要求的话可以使用LinkedHashMap。一般而言LinkedHashMap的性能会比HashMap差一点(特殊情况:HashMap容量大而实际数据少时会比LinkedHashMap差,因为LinkedHashMap遍历速度只与实际数据有关,而HashMap与容量也有关)。
锁
Synchronized
和Volatile
的区别是什么?
-
synchronized 可以作用于变量、方法、对象;volatile 只能作用于变量。
-
synchronized 可以保证线程间的有序性、原子性和可见性;
volatile 只保证了可见性和有序性,无法保证原子性。 -
synchronized 有可能导致线程阻塞,volatile 线程不阻塞。
-
volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。
线程
进程和线程的区别?
进程就等于是电脑里运行的程序,线程就等于是可以让一个进程同时执行多个任务,一个进程可以有多个线程。
java里面创建线程的方式
- 实现
Runnable
接口的run()
方法 ,然后通过Thread
的start()
方法开启线程。 - 继承
Thread
类,重写run()
方法,这个本质上与实现Runnable接口的方式一致,因为Thread类本身就实现了Runnable接口。 - 实现
Callable
接口的call()
方法,然后通过Future
的get()
方法进行开启线程。与另外两个的区别是这个有返回值。 - 通过JUC里面的线程池。
Java中的线程池是什么,有哪些常用的参数?
线程池的基本功能就是进行线程的复用,当程序需要一个线程时,会先从线程池里面捞一下空闲线程来使用;用完后会将线程重新放入线程池。
corePoolSize
:池中所保存的常驻线程数。
maximumPoolSize
:池中允许的最大线程数。
keepAliveTime
: 多余的空闲线程最多存活时间。
unit
: keepAliveTime的单位。
handler
:拒绝策略,表示当队列满了,如何拒绝。java默认提供了4种饱和策略的实现方式:中止、抛弃、抛弃最旧的、调用者运行。
workQueue
:任务队列。
threadFactory
:表示生成线程池中工作线程的线程工厂。
……
常用线程池?
newCachedThreadPool
:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。newFixedThreadPool
:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。newScheduledThreadPool
: 创建一个定长线程池,支持定时及周期性任务执行。newSingleThreadExecutor
:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
线程池创建方式?
-
使用ThreadPoolExecutor类
-
使用Executors类
其实这两种方式在本质上是一种方式,都是通过ThreadPoolExecutor类的方式。
线程池的优点?
-
重用存在的线程,减少对象创建、消亡的开销,性能佳。
-
可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
-
提供定时执行、定期执行、单线程、并发数控制等功能。
1. 降低资源消耗: 重复利用线程,减少创建和销毁造成的消耗。
2. 提升响应速度: 任务到达,不需要创建,立即执行。
3. 提高可管理型: 线程是CPU调度和分派的基本单位。
服务端同步和异步你是怎么理解的?
同步在一定程度上可以看做是单线程,这个线程请求一个方法后就待这个方法给他回复,否则他不往下执行(死心眼)。
异步在一定程度上可以看做是多线程的(废话,一个线程怎么叫异步),请求一个方法后,就不管了,继续执行其他的方法。
同步,可以理解为在执行完一个函数或方法之后,一直等待系统返回值或消息,这时程序是处于阻塞的,只有接收到返回的值或消息后才往下执行其他的命令。
异步,执行完函数或方法后,不必阻塞性地等待返回值或消息,只需要向系统委托一个异步过程,那么当系统接收到返回值或消息时,系统会自动触发委托的异步过程,从而完成一个完整的流程。
同步,就是实时处理,比如服务器一接收客户端请求,马上响应,这样客户端可以在最短的时间内得到结果,但是如果多个客户端,或者一个客户端发出的请求很频繁,服务器无法同步处理,就会造成涌塞。
异步,就是分时处理,服务器接收到客户端请求后并不是立即处理,而是等待服务器比较空闲的时候加以处理,可以避免涌塞。
同步就是调用一个函数,直接函数执行完了才返回到调用函数;
异步就是被调用函数初始化完后马上返回。
JVM
分别解释一下栈和堆?
栈是运行时的单位,解决程序如何执行,代表处理逻辑
堆是存储单位,解决数据存储问题,代表数据
JVM类加载机制,调优机制是什么?
-
JVM类加载机制
JVM类加载机制分为五个部分:
加载
,验证
,准备
,解析
,初始化
。虚拟机将class文件加载到内存,并对数据校验、准备和解析,之后完成初始化。
加载就是JVM将字节码文件转化为二进制字节流加载到内存中,然后为这个类在JVM方法区创建一个Class对象(访问入口);
验证是连接阶段的第一步,这一步主要就是为了确保class文件符合虚拟机的要求;
准备阶段是最重要的,这个阶段会为类变量
分配内存
并初始化
。(类变量就是带static修饰的变量,而类成员变量是不带static修饰的,类成员变量的内存分配时机是初始化阶段。)初始化时会为类变量赋予其数据类型的零值,而不是代码里初始化的值,例如:public static int number = 3; public static final finalNumber = 3;
这里的number初始化值是0,而不是3。
但如果其被final修饰,即成为了常量,那么在准备阶段就会被赋值为3,好比这里的finalNumber的值为3。
解析阶段会将常量池内的符号引用替换为直接引用。
- 符号引用:简单的理解就是字符串,比如引用一个类,java.util.ArrayList 这就是一个符号引用,字符串引用的对象不一定被加载。
- 直接引用:指针或者地址偏移量。引用对象一定在内存(已经加载)。
初始化阶段会执行类构造器的
clinit()
方法,为类变量赋予正确的初始值(之前在准备阶段只是分配空间并赋予0值,现在才赋予正确值),而初始值设定有两种方法:- 直接声明类变量的指定初始值,比如上面的number=3,就是此时赋予;
- 使用静态代码块进行指定初始值。
-
调优机制
对JVM内存的系统级的调优主要的目的是减少GC的
频率
和Full GC
的次数,过多的GC和Full GC是会占用很多的系统资源(主要是CPU),影响系统的吞吐量。- 老年代空间不足:(会引起Full GC)
- 尽量让对象在年轻代GC时被回收;
- 让对象在年轻代多存活一段时间;
- 不要创建过大的对象;
- 数组避免直接在老年代创建对象。
- 年轻代空间不足:
- 增大年轻代空间,避免太多静态对象;
- 控制好年轻代和老年代的比例;
- 垃圾回收尽量不要手动触发 ,尽量依靠JVM自身的机制。
调优手段主要是通过控制堆内存的各个部分的比例和GC策略
- 老年代空间不足:(会引起Full GC)
双亲委派模型的工作流程是什么?
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。
(就是儿子能干也不干,先让爸爸干,爸爸想偷懒就让爷爷干。爷爷能干就干,不能干就还推给爸爸干)。
双亲委派机制可以被打破吗?
可以
- 自定义类加载器,重写loadClass方法;
- 使用线程上下文类加载器。
使用PC寄存器记录当前线程的执行地址有什么用?
因为CPU需要不停的起换各个线程,这时候切换回来以后,就得知道接从哪哪开始继续执行
GC分为几种?
- Minor GC
- Full GC
- Major GC 是清理永久代 通常至少经历过一次Minor GC
GC发生在哪里?
堆。
怎么判定垃圾?
- 引用计数法
- 可达性分析
设计模式
单例设计模式
-
饿汉式
- 私有化构造器
- 内部创建对象
- 提供公共静态方法,返回对象
优点:线程安全
缺点:加载时间长
class Singleton { // 私有化构造器 private Singleton(){} // 内部创建对象 private static Singleton instance = new Singleton(); // 对外提供静态方法返回对象 public static Singleton getInstance() { return instance; } }
-
懒汉式
- 私有化构造器
- 内部声明对象,没有初始化
- 提供公共静态方法,返回对象
优点:延迟对象的创建,节约内存空间
缺点:线程不安全(可以通过加锁改进为安全)
class Singleton { // 私有化构造器 private Singleton(){} // 内部声明对象,没有初始化 private static Singleton instance=null; //提供公共静态方法,返回对象 public static Singleton getInstance() { if(instance == null){ instance = new Singleton(); } return instance; } }
线程安全的懒汉式:
//懒汉式线程安全 class Singleton{ private Singleton(){}; private static Singleton instance = null; public static Singleton getInstance(){ if(instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
Web
post 和 get请求的区别?
get
:从服务器上获取数据,可以从url上面进行传参,传递数据量较小,安全性低;
post
:是通过报文来传递数据,一般传递一些表单、json数据等大量的数据,安全性高。
还有一点是:get产生一个TCP包,post先发送header,服务器响应后再发送data,因此产生两个数据包。
在做数据查询时,建议用Get方式;而在做数据添加、修改或删除时,建议用Post方式
JSP的内置对象有哪些?
名称 | 介绍 |
---|---|
request | request |
response | response |
session | session对象指的是客户端与服务器的一次会话,从客户连到服务器开始,直到与服务器断开连接为止。 |
out | out是向客户端输出内容常用的对象 |
page | page对象就是指向当前JSP页面本身,有点象类中的this指针。 |
application | application对象实现了用户间数据的共享,可存放全局变量。服务器的启动和关闭决定application对象的生命。 |
exception | exception |
pageContext | pageContext对象提供了对JSP页面内所有的对象及名字空间的访问。 |
config | config |
JSP的四大作用域及其范围
名称 | 作用域 |
---|---|
application | 在所有应用程序中有效 |
session | 在当前会话中有效 |
request | 在当前请求中有效 |
page | 在当前页面有效 |