1.instanceof关键字的作用
- instanceof严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例,用法为:
boolean result = obj instanceof Class
- 其中obj为一个对象,Class表示一个类或者一个接口,当obj为Class的对象,或者是其直接或间接子类或者是其接口的实现类,结果result都返回true,否则返回false
- 注意:编译器会检查obj是否能转换成右边的class类型,如果不能转换则直接报错,如果不能确定类型,则通过编译,具体看运行时定
2.Java自动装箱与拆箱
- 装箱就是自动将基本数据类型装换为包装器类型(int–>Integer);调用方法:Integer的valueOf(int)方法
- 拆箱就是自动将包装器类型装换为基本数据类型(Integer–>).调用方法:Integer的intValue方法
- 在Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行:
Integer i = new Integer(10);
而从Java SE5开始就提供了自动装箱的特性,如果要生成一个数值为10的Integer对象,只需要这样就可以了.
Integer i = 10;
在某个范围内的整型数值的个数是有限的,而浮点数却不是
3.重载和重写的区别
- 重写
从字面上看,重写就是重新写一遍的意思.其实就是在子类中把父类本身的方法重新写一遍.子类继承了父类原有的方法,但有时子类并不想原封不动的继承父类中的某个方法,所以在方法名,参数列表,返回类型(除过子类中的方法的返回值是父类中方法返回值的子类时)都相同的情况下,堆方法体进行修改或重写,这就是重写.但要注意子类函数的访问修饰权限不能少于父类的 - 重写总结:
- 发生在父类与子类之间
- 方法名,参数列表,返回类型必须相同
- 访问修饰符的限制一定要大于被重写方法的访问修饰符
- 重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常
- 重载
在一个类中,同名的方法如果有不同的参数列表(参数类型不同,参数个数不同甚至是参数顺序不同),则被视为重载.同时,重载对返回类型没有要求,可以相同也可以不同,但不能通过返回类型是否相同来判断重载 - 重载总结
- 重载Overload是一个类中多态性的一种表现 - 重载要求同名方法的参数列表不同(参数类型,参数个数甚至是参数顺序) - 重载的时候,返回值类型可以相同也可以不同
4. equals与==的区别
- ==
== 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指向同一个对象.比较的是真正意义上的指针操作.- 比较的是操作符两端的操作数是否是同一个对象 - 两边的操作数必须是同一类型的(可以是父子类之间)才能编译通过 - 比较的是地址,如果是具体的阿拉伯数字的比较,值相等则为true - equals
equals用来比较的是两个对象的内容是否相等,由于所有的类都是继承自java.lang.Object类的,所以适用于所有对象,如果没有对该方法进行覆盖的话,调用的仍然是Object类中的方法,而Object中的equals方法返回的却是==的判断 - 总结
所有比较是否相等时,都是用equals并且在对常量相比较时,把常量写在前面,因为使用object的equals,object可能为null,则空指针
在阿里的代码规范中只使用equals,阿里插件默认会识别,并可以快速修改,推荐安装阿里插件来排查老代码使用"==",替换成equals
5. Hashcode的作用
- Java的集合有两类,一类是List,还有一类是Set.前者有序可重复,后者无需不可重复.当我们在set中插入的时候怎么判断是否已经存在该元素呢,可以通过equals方法.但是如果元素太多,用这样的方法就会比较慢
- 于是有人发明了哈希算法来提高集合中查找元素的效率.这种方式将集合分为若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定该对象应该存储的那个区域
- hashCode方法可以这样理解:它返回的就是根据对象的内存地址换算出的一个值.这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上.如果这个位置上没有元素,他就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散裂其他的地址.这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次.
6.String,StringBuffer和StringBuilder的区别是什么
- String是只读字符串,它并不是基本数据类型,而是一个对象.从底层源码来看是一个final类型的字符数组,所引用的字符串不能被改变,一经定义,无法再增删改.每次对String的操作都会生成新的String对象
private final char value[];
- 每次+操作:隐式在堆上new了一个跟原字符串相同的StringBuilder对象,再调用append方法拼接+后面的字符
- StringBuffer和StringBuilder他们俩都继承了AbstractStringBuilder抽象类,从AbstractStringBuilder抽象类中我们可以看到
char[] value;
他们的底层都是可变的字符数组,所以在进行频繁的字符串操作时,建议使用StringBuffer和StringBuilder来进行操作.另外String Buffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的.StringBuilder并没有对方法进行加同步锁,所以是非线程安全的
7.ArrayList和LinkedList的区别
- Array(数组)是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的
Array获取数据的时间复杂度是O(1),但是要删除数据却是开销很大,因为这需要重排数组中的所有数据,(因为删除数据后,需要把后面所有的数据前移) 缺点:数组初始化必须指定初始化的长度,否则报错- List是一个有序的集合们可以包含重复的元素,提供了按索引访问的方式,它继承自Collection.
- List有两个重要的实现类:ArrayList和LinkedList
- ArrayList:可以看作是能够自动增长容量的数组
- ArrayList的toArray方法返回一个数组
- Array List的asList方法返回一个列表
- ArrayList底层的实现是Array,数组扩容实现
- LinkedList是一个双链表,在添加和删除元素时具有比ArrayList更好的性能.但在get和set方面弱于ArrayList.当然,这些对比都是指数据量很大或者操作很频繁
8.HashMap和HashTable的区别
- 两者父类不同
HashMap是继承自AbstractMap类,而Hashtable是继承自Dictionary类.不过他们都同时实现了map,Cloneable(可复制),Serializable(可序列化)这三个接口 - 对外提供的接口不同
- Hashtable比HashMap多提供了elements()和contains()两个方法.elements()方法继承自Hashtable的父类Dictionary.elements()方法用于返回此Hashtable中的value的枚举
- contains()方法判断该Hashtable是否包含传入的value.它的作用与containsValue()一致.事实上,containsValue()就只是调用了一下contains()方法
- 对null的支持不同
Hashtable:key和value都不能为null HashMap:key可以为null,但是这样的key只能有一个,因为必须保证key的唯一性;可以有多个key值对应的value为null - 安全性不同
- HashMap是线程不安全的,在多线程并发的环境下,可能会产生死锁等问题,因此需要开发人员自己处理多线程的安全问题
- HashTable是线程安全的,它的每个方法上都有synchronized关键字,因此可直接用于多线程中
- 虽然HashMap是线程不安全的,但是它的效率远远高于Hash table,这样设计是合理的,因为大部分的使用场景都是单线程.当需要多线程操作的时候可以使用线程安全的ConcurrentHashMap
- ConcurrentHashMap虽然也是线程安全的,但是他的效率比Hashtable要高好多倍.因为ConcurrentHashMap使用了分段所,并不对整个数据进行锁定.
- 初始容量大小和每次扩充容量大小不同
- 计算hash值的方法不同
9. Collection包结构,与Collections的区别
- Collection是集合类的上级接口,子接口由Set,List,Linked List,Vector,Stack,
- Collections是集合类的一个帮助类,它包含有各种有关集合操作的静态多态方法,用于实现对各种集合的搜索,排序,线程安全化等操作,此类不能实例化,就像一个工具类,服务于Java的Collection框架
10. Java的四种引用,强弱软虚
- 强引用
强引用是平时使用最多的引用,强引用在程序内存不足(OOM)的时候也不会被回收,使用方式:
String str = new String("str");
System.out.println(str);
- 软引用
软引用在程序内存不足时,会被回收,使用方式:
// 注意:wrf这个引用也是强引用,它是指向SoftReference这个对象的,
// 这里的软引用指的是指向new String("str")的引用,也就是SoftReference类中T
SoftReference<String> wrf = new SoftReference<String>(new String("str"));
可用场景:创建缓存的时候,创建的对象放进缓存中,当内存不足时,JVM就会回收早先创建的对象
- 弱引用
弱引用就是只要JVM垃圾回收器发现了它,就会将之回收,使用方式:
WeakReference<String> wrf = new WeakReference<String>(str);
- 虚引用
虚引用的回收机制跟弱引用差不多,但是它被回收之前,会被放入ReferenceQueue中
11. Java创建对象有几种方式
Java中提供了以下创建对象的方式:
- new创建新对象
- 通过反射机制
- 采用clone机制
- 通过反序列化机制
12.有没有可能两个不相等的对象有相同的hashcode
有可能,在产生hash冲突时,两个不相等的对象就会有相同的hashcode值.当hash冲突产生时,一般有以下几种方式来处理:
- 拉链法:每个哈希表节点都有一个next指针,多个哈希表节点可以用next指针构成一个单向链表,被分配到同一个索引上的多个节点可以用这个单向链表进行存储
- 开放定址法:一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入
- 再哈希:又叫双哈希法:有多个不同的Hash函数.当发生冲突时,使用第二个,第三个...等哈希函数计算地址,直到无冲突
13. 深拷贝和浅拷贝的区别是什么
- 浅拷贝:被复制对象的所有变量都含有与原来对象相同的值,而所有的其他对象的引用仍然指向原来的对象.换言之,浅拷贝仅仅复制所考虑的对象,而不复制他所引用的对象
- 深拷贝:被复制的对象的所有变量都含有与原来对象相同的值.深拷贝把要复制的对象那个所引用的对象都复制了一遍
14. final有哪些用法
1. 被final修饰的类不可以被继承
2. 被final修饰的方法不可以被重写
3. 被final修饰的变量不可以被改变.如果修饰引用,那么表示引用不可变
4. 被final修饰的方法,JVM会尝试将其关联,以提高运行效率
5. 被final修饰的常量,在编译阶段会存入常量池中
15.static都有哪些用法
- 两个基本的用法:静态变量和静态方法.也就是被static修饰的变量/方法都属于类的静态资源,类实例所共享
- 除了静态变量和静态方法之外,static也用于静态块,多用于初始化操作:
public class PreCache{
static{
// 执行相关操作
}
}
- 此外static也多用于修饰内部类.此时称为静态内部类
- 最后一种用法就是静态导包,即
import static.是在JDK1.5之后引入的新特性,可以用来指定导入某个类中的静态资源,并且不需要使用类名,可以直接使用此资源名
16.OOM你遇到过哪些情况,SOF你遇到过哪些情况
OOM
- OutOfMemoryError异常
除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError(OOM)异常的可能
- Java Heap溢出:
- 一般的异常信息:java.lang.OutOfMemoryError:Java heap spacess.
Java堆用于存储对象实例,我们只要不断地创建对象,并且GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,就会在对象数量达到最大堆容量先之后产生内存溢出异常.
- 一般的异常信息:java.lang.OutOfMemoryError:Java heap spacess.
- 出现这种异常,一般手段是先通过内存映像分析工具对dump出来的堆转存快照进行分析,重点是确认内存中的对象是否是必要的,先分清是因为内存泄漏(Memory Leak)还是内存溢出(MemoryOverflow)
- 如果是内存泄漏,可进一步通过工具查看泄露对象到GCRoots的引用链,于是就能找到泄露对象是通过怎样的路径与GC Roots相关联并导致垃圾收集器无法自动回收
- 如果不存在泄露,那就应该检查虚拟机的参数(-Xmx与-Xms)的设置是否适当
- 虚拟机栈和本地方法栈溢出
- 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常
- 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常
- 这里需要注意当栈的大小越大可分配的线程数就越少
- 运行时常量池溢出
- 异常信息:java.lang.OutOfMemoryError:PermGenspace
- 如果要向运行时常量池中添加内容,最简单的做法就是使用String.intern()这个Native方法.该方法的作用是:如果池中已经包含一个等于此String的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用.由于常量池分配在方法区中,我们可以通过-XX:PermSize和-XX:MaxPermSize限制方法区的大小,从而间接限制其中常量池的容量
- 方法区溢出
方法区用于存放Class的相关信息,如类名,访问修饰符,常量池,字段描述,方法描述等.也有可能是方法区中保存的class对象没有被class信息占用的内存超过了我们的配置
异常信息:java.lang.OutOfMemoryError:PermGenspace
方法区溢出也是一种常见的内存溢出异常,一个类如果要被垃圾回收器回收,判定条件是很苛刻的.在经常动态生成大量Class的应用中,要特别注意这点
SOF(堆栈溢出StackOverflow)
- StackOverflow的定义:当应用程序递归太深而发生堆栈溢出时,抛出该错误
- 因为栈一般默认为1-2m,一旦出现死循环或者是大量的递归调用,在不断的压栈过程中,造成栈容量超过1m而导致溢出
- 栈溢出的原因:递归调用,大量循环或死循环,全局变量是否过多,数组,List,map数据过大
17. 说说Java中IO流
- Java中IO流分为几种?
- 按照流的流向分,可以分为输入流和输出流
- 按照操作单元划分,可以划分为字节流和字符流
- 按照流的角色划分为节点流和处理流
- Java IO流的40多个类都是从如下4个抽象类基类中派生出来的
- InputStream/Reader:所有输入流的基类.前者是字节输入流,后者是字符输入流
- OutputStream/Writer:所有输出流的基类,前者是字节输出流,后者是字符输出流

18. Java反射的作用与原理
- 定义:
反射机制是在运行时,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意个对象,都能够调用它的任意一个方法.在Java中,只要给定类的名字,就可以通过反射机制来获得类的所有信息.
这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制 - 哪里会用到反射机制
jdbc就是典型的反射
Class.forName('com.mysql.jdbc.Driver.class'); //加载MySQL的驱动类
- 反射的实现方式:
第一步:获取Class对象,有4种方法:
- Class.forName("类的路径");
- 类名.class
- 对象名.getClass()
- 基本类型的包装类,可以调用包装类的Type属性来获得该包装类的Class对象
- 实现Java反射的类
- Class:表示正在运行的Java应用程序中的类和接口 注意:所有获取对象的信息都需要Class类来实现
- Filed: 提供有关类和接口的属性信息,以及对它的动态访问权限
- Constructor: 提供关于类的单个构造方法的信息以及它的访问权限
- Method: 提供类或接口中某个方法的信息
- 反射机制的优缺点:
优点:
- 能够运行时动态获取类的实例,提高灵活性
- 与动态编译结合
缺点:
- 使用反射性能较低,需要解析字节码,将内存中的对象进行解析
19. 说说List,Set,Map三者的区别
- List(对付顺序的好帮手):List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象
- Set(注重独一无二的性质):不允许重复的集合.不会有多个元素引用相同的对象
- Map(用key来搜索的专家):使用键值对存储.Map会维护与Key有关联的值.两个key可以引用相同的对象,但key不能重复,但也可以是任何对象
20. Object有哪些常用方法?大致说一下每个方法的含义
- clone方法
保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常,深拷贝也需要实现Cloneable,同时其成员变量为引用类型的也需要实现Cloneable,然后重写clone方法 - finalize方法
该方法和垃圾收集器有关系,判断一个对象是否可以被回收的最后一步就是判断是否重写了此方法 - equals方法
该方法使用频率非常高.一般equals和==是不一样的,但是在Object中两者是一样的.子类一般都要重写这个方法 - hashCode方法
该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法,这个方法在一些具有哈希功能的Collection中用到 - wait方法
配合synchronized使用,wait方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁.wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。
调用该方法后当前线程进入睡眠状态,直到以下事件发生
- 其他线程调用了该对象的notify方法;
- 其他线程调用了该对象的notifyAll方法;
- 其他线程调用了interrupt中断该线程
- 时间间隔到了
此时该线程就可以被调度了,如果是被中断的话,就抛出一个InterruptException异常
- notify方法
配合synchronized使用,该方法唤醒在该对象上等待队列中的某个线程(同步队列中的线程是给抢占CPU的线程,等待队列中的线程指的是等待唤醒的线程) - notifyAll方法
配合synchronized使用,该方法唤醒在该对象等待队列中的所有线程。
21. 说说什么是fail-fast?
fail-fast机制是Java集合中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。
22.说说Hashtable与HashMap的区别
- 都实现了Map,Cloneable,Serializable
- HashMap继承的是AbstractMap,并且AbstractMap也实现了Map接口。Hash table继承Dictonary
- Hashtable中大部分public修饰普通方法都是synchronized字段修饰的,是线程安全的,HashMap是非线程安全的
- Hashtable的key不能为null,value也不能为null
- HashMap的key和value都可以为null。在计算hash值的时候,有判断,如果key==null,则其hash=0;至于value是否为null,根本没有判断过
- Hashtable直接使用对象的hash值,hash值是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值,然后再使用除留取余法来获得最终的位置。然而除法运算是非常耗费时间的,效率很低。HashMap为了提高计算效率,将哈希表的大小固定为了2的幂,这样在取模运算时,不需要做除法,只需要做位运算。位运算比除法的效率要高很多
- Hash table,HashMap都使用了Iterator。由于历史原因,Hashtable还使用了Enumeration的方式
- 默认情况下,初始容量不同,Hashtable的初始长度是11,之后每次扩容变为之前的2n+1(n为上一次的长度)而HashMap的初始长度为16,之后每次扩容变为原来的两倍
24. HashMap中的key我们可以使用任何类作为key吗
平时时用的最多的就是使用String作为HashMap的key,但是现在想使用某个自定义类作为HashMap的key,那就需要注意以下几点:
- 如果类重写了equals方法,它也应该重写hashCode方法
- 类的所有实例需要遵循与equals和hashCode相关的规则
- 如果一个类没有使用equals,你不应该在hashCode中使用它
- 咱们自定义key类的最佳实践是使之为不可变的,这样,hashCode值可以被缓存起来,拥有更好的性能。不可变的类也可以确保hashCode和equals在未来不会改变,这样就会解决与可变相关的问题了
25. HashMap与ConcurrentHashMap的异同
- 都是key-value形式的存储数据;
- HashMap是线程不安全的,ConcurrentHashMap是JUC下的线程安全的;
- HashMap底层数据结构是数组+链表(JDK1.8之前)。JDK1.8之后是数组+链表+红黑树。当链表中元素个数达到8的时候,链表的查询速度不如红黑树快,链表会转为红黑树,红黑树查询速度快;
- HashMap初始数组大小为16(默认),当出现扩容的时候,以0.75*数组大小的方式进行扩容;
- ConcurrentHashMap在JDK1.8之前是采用分段锁来实现的Segment+HashEntry,Segment数组默认大小是16,2的n次方;JDK1.8之后,采用Node+CAS+Synchronized来保证并发安全进行实现
26. 红黑树有哪几个特征
- 每个节点是黑色或红色
- 根节点是黑色
- 每个叶子节点都是黑色(指向空的叶子节点)
- 如果一个叶子节点是红色,那么其子节点必须都是黑色的
- 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点
27. ArrayList在遍历的时候能否执行插入操作
不能,因为迭代器和增强for的预期变化次数是一开始就定义好的无法改变
如果在遍历的时候执行插入操作会报ConcurrentModificationException
28. Java多态体现在哪些方面
- 多态:就是类的方法或者对象具有多种状态,是OOP的第三大特征,是建立在封装和继承基础上的
- 体现在哪些方面
- 方法多态:重载体现多态,重写体现多态
- 对象多态:(1)对象的编译类型和运行类型可以不一致,编译类型在定义时就确定,不能变化
(2)对象的运行类型是可以变化的,可以通过getClass()来查看运行类型
(3)编译类型看=左边,运行类型看=右边
29. 说说深拷贝和浅拷贝
- 浅拷贝只是增加了一个指针指向已存在的内存地址
- 深拷贝是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存
- 使用深拷贝的情况下,释放内存的时候不会因为出现浅拷贝时释放同一个内存的错误
30. HashMap多线程操作导致死循环的问题
在多线程下,进行put操作会导致HashMap死循环,原因在于HashMap的扩容resize()方法。由于扩容是新建一个数组,复制原数据到数组。由于数组下标挂有链表,所以需要复制链表,但是多线程操作可能导致环形链表

被折叠的 条评论
为什么被折叠?



