Java校招面试汇总
- 一、JavaSE
-
- Ⅰ、基础知识
- Ⅱ、字符串
- Ⅲ、集合
-
- 1、说说Java中常用的容器有哪些?
- 3、迭代器 Iterator 是什么?
- 4、如何边遍历边移除 Collection 中的元素?
- 5、Iterator 和 ListIterator 有什么区别?
- 6、如何实现数组和 List 之间的转换?
- 7、ArrayList和LinkedList的区别
- 8、ArrayList 和 Vector 的区别是什么?
- 9、插入数据时,ArrayList、LinkedList、Vector谁速度较快?
- 10、多线程场景下如何使用 ArrayList?
- 11、说一下HashSet的实现原理?
- 12、HashSet与HashMap的区别
- 10、HashMap的实现原理
- 11.说说HashMap的put方法执行流程?
- 12.说说HashMap的get方法执行流程?
- 13,哈希冲突有几种解决办法?
- 14、HashMap 与 HashTable的区别
- 15、ConcurrentHashMap 的实现原理
- 16、ConcurrentHashMap 不支持 key 或者 value 为 null 的原因?
- 17.说说ArrayList 的扩容机制?
- 18.Array和ArrayList有何区别?
- 19.comparable和comparator的区别?
- 20.Collection和Collections有什么区别?
- Ⅳ、异常
- Ⅴ、IO流
- 二、多线程
- 三、数据库
- 四、框架
-
- Ⅰ、Spring
-
- 1、使用Spring框架的好处是什么?
- 2、Spring的IOC理解
- 3、什么是DI?
- 4、什么是AOP?
- 5、Spring通知(Advice)有哪些类型?
- 6、Spring容器的启动流程
- 7、Spring框架使用了哪些设计模式?
- 8、BeanFactory和ApplicationContext有什么区别?
- 9、Spring Bean的生命周期?
- 10、Spring中bean的作用域
- 11、Spring框架中的Bean是线程安全的么?如果线程不安全,那么如何处理?
- 12、Spring基于xml注入bean的几种方式
- 13、Spring的自动装配
- 14、 springmvc常用到的注解,作用、原理是什么?
- 16、 SpringMVC执行流程和原理
- 17、有哪些重要的Spring注解?
- 18、Spring事务的实现方式和实现原理
- 19、Spring如何管理事务的?
- Ⅱ、 MyBaits
-
- MyBaits的优缺点
- 2、相对于JDBC,MyBatis做了哪些改进?
- 3、 MyBatis编程步骤
- 4、说说MyBatis的工作原理
- 5、MyBatis 的 Executor 接口是如何操作数据库的?
- 5、#{} 和 ${} 的区别是什么?
- 6、当实体类中的属性名和表中的字段名不一样 ,怎么办 ?
- 7、通常一个mapper.xml文件,都会对应一个Dao接口,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?
- 8、MyBatis的Xml映射文件中,不同的xml映射文件,id是否可以重复?
- 9、Mybatis是如何进行分页的?分页插件的原理是什么?
- 10、 MyBatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
- 11、MyBatis是否支持延迟加载?如果支持,它的实现原理是什么?
- 12、MyBatis动态sql有什么用?执行原理?有哪些动态sql?
- 13、使用MyBatis的mapper接口调用时有哪些要求?
- 14、在mapper中如何传递多个参数?
- 15、 一对一,一对多的关联查询 ?
- 16、MyBatis实现一对一和一对多有几种方式?具体怎么操作的?
- 17、 什么是MyBatis的接口绑定?有哪些实现方式?
- Ⅲ、SpringBoot
-
- 1、SpringBoot 有哪些优点?
- 2、SpringBoot 的核心注解是哪个?它主要由哪几个注解组成的?
- 3、SpringBoot 的核心配置文件有哪几个?它们的区别是什么?
- 4、SpringBoot Starter的工作原理
- 5、 SpringBoot 配置加载顺序
- 6、 SpringBoot 有哪几种读取配置的方式?
- 7、 SpringBoot 实现热部署有哪几种方式?
- 8、 如何重新加载SpringBoot上的更改,而无需重新启动服务器?
- 9、SpringBoot事物的使用
- 10、什么是 JavaConfig?
- 11、SpringBoot 中如何实现定时任务 ?
- 12、 有哪些spring-boot-maven-plugin命令?
- 13、SpringBoot的自动配置原理是什么?
- 14、 什么是YAML? 它有哪些优点?
- 14、SpringBoot多数据源拆分的思路
- 15、SpringBoot多数据源事务如何管理?
- 16、SpringBoot 打成的 jar 和普通的 jar 有什么区别 ?
- 五、Git
- 六、Linux
- 七、redis面试题
一、JavaSE
Ⅰ、基础知识
1、Java中引用数据类型有哪些,它们与基本数据类型有什么区别?
只要不是基本数据类型,都是引用数据类型;
创建引用数据类型时,在栈内存中存储引用地址,在堆内存存储具体的值;
栈内存引用地址指向堆内存空间;
用equals()方法比较内存地址,==和!=是比较数值的。
2、Java中的自动装箱与拆箱
装箱就是自动将基本数据类型转换为包装器类型,拆箱就是自动将包装器类型转换为基本数据类型。
3、==和equals()的区别
-
==
基本类型:比较的是值是否相同; 引用类型:比较的是引用是否相同;
String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true
System.out.println(x==z); // false
System.out.println(x.equals(y)); // true
System.out.println(x.equals(z)); // true
-
equals()
equals 本质上就是 ==,String 和 Integer 等类重写了 equals 方法,把它变成了值比较; String、Date、File、包装类都重写了Object类的equals方法。
class Cat {
public Cat(String name) {
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Cat c1 = new Cat("叶痕秋");
Cat c2 = new Cat("叶痕秋");
System.out.println(c1.equals(c2)); // false
4、static和final的区别
5、什么是内部类?内部类的作用
-
成员内部类
可以无条件访问外部类的所有成员,包括私有的和静态的; 当成员内部类拥有和外部类同名的成员时,访问时默认是成员内部类的成员。
-
局部内部类
定义在方法或者作用域里面的类,访问权限仅限于方法内或者该作用域内。
-
静态内部类
可以不依赖外部类而实例化,不可以有与外部类相同的成员,不可以访问外部类非静态的成员。
6、抽象类和接口的区别
1. 成员区别:
-
抽象类
抽象类有构造方法,可以用子类实例化; 既可以有成员变量也可以有常量; 成员方法既可以有抽象的也可以有非抽象的。
-
接口
接口没有构造方法,只能定义常量,默认修饰符:public static final; 可以用default和static修饰。
2. 关系区别:
抽象类可以继承类,可以单继承,不能多继承,但可以多层继承;
类实现借口,可以实现单接口,也可以实现多接口;
一个类在继承父类的同时,还可以实现多个接口;
接口继承接口,可以多继承,因为接口的方法一般默认是抽象的。
3. 使用原理不同:
当关注事物本质的时候,用抽象类;当关注操作的时候,用接口;
抽象类表示这个对象是什么,例如Student类和Teacher都是Person类,都有人类的共有属性,可以定义一个人类的抽象类;
接口表示这个对象能做什么,是对类行为的约束;
即它的实现类可以做什么,但实现类是什么,怎么做的,接口并不关心;
例如人类吃东西,鸟类也吃东西,它们都有一个“吃东西”的方法,可以定义一个“吃东西”的接口,让人类和鸟类都去实现它。
4. 功能不同:
抽象类的功能要远大于接口,由于一个类只能继承一个类,所以在一个抽象类中,你必须继承或编写其所有子类的所有共有属性;
虽然接口的功能较少,但接口只是对一个动作的描述,一个类可以实现多个接口,从而在设计阶段降低难度。
7、instanceof关键字的作用
instanceof严格来说是Java中一个双目运算符,用来判断一个对象是否为一个类的实例。用法如下:
boolean result = obj(对象)instanceof int i = 0;
System.out.println(i instanceof Integer);//编译不通过 i必须是引用类型,不能是基本类型System.out.println(i instanceof Object);//编译不通过
Integer integer = new Integer(1);
System.out.println(integer instanceof Integer);//true
//false ,在 JavaSE规范 中对 instanceof 运算符的规定就是:如果 obj 为 null,那么将返回false。
System.out.println(null instanceof Object);
8、什么是泛型?泛型的好处
泛型是编写代码时可以规定一种通用的数据类型,而不是具体的;
比如ArrayList就是一种泛型,提高代码的复用性。
9、Java中创建对象的几种方式?
- 通过new。
- 通过反射机制Class类的newInstance()方法或者Constructor类中的newInstance()方法。
String str1 = String.Class.newInstance();
String str2 = String.Class.getConstructor().newInstance();
-
克隆
实现 Cloneable 接口,重写 Object 类的 clone() 方法; 没有实现 Cloneable 接口,会抛出CloneNotSupportedException异常; Object 类提供的 clone() 方法,访问权限是 protected,所以如果不重写 clone() 方法,是没有权限调用的。
-
通过对象的反序列化
String str1 = new String("反序列化一个对象");
// 序列化一个girlFriend
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("String.obj"));
objectOutputStream.writeObject(String1);
objectOutputStream.close();
// 反序列化出来
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("String.obj"));
String str2 = (String) objectInputStream.readObject();
objectInputStream.close();
10、Java的四种引用
-
强引用
在内存不足时也不会被回收,使用方法为:String str = new String("str");
-
软引用
在内存不足时会被回收,使用方法为: SoftReference<String> wrf = new SoftReference<String>(new String("str"));
-
弱引用
只要jvm垃圾回收器发现了它,就将其回收,使用方法为: WeakReference<String> wrf = new WeakReference<String>(str);
-
虚引用
和弱引用差不多,不同的是虚引用在被JVM回收之前会被放入ReferenceQueue中, 而其他引用是在被JVM回收之后放入ReferenceQueue。
11、Files的常用方法都有哪些?
Files. exists():检测文件路径是否存在。
Files. createFile():创建文件。
Files. createDirectory():创建文件夹。
Files. delete():删除一个文件或目录。
Files. copy():复制文件。
Files. move():移动文件。
Files. size():查看文件个数。
Files. read():读取文件。
Files. write():写入文件。
12、Java的反射机制
对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法。
13、反射的实现方式
1、类名.class,不会对类进行任何初始化;
2、Class.forName(“类的路径”),对类进行静态初始化;
3、对象名.getClass(),对类进行静态和非静态初始化;
4、基本类型的包装类,调用包装类的Type属性来获得该包装类的Class对象
14、Java反射API有哪些?
1、Class:表示正在运行的Java应用程序中的类和接口,所有获取对象的信息都需要Class类来实现。
2、Field:提供有关类和接口的属性信息,以及对它的动态访问权限。
3、Constructor:提供关于类的单个构造方法的信息以及它的访问权限 4、Method:提供类或接口中某个方法的信息
15、反射机制的优缺点
-
优点:
能动态的获取类的实例,提高代码灵活性 -
缺点:
①反射性能较低,因为需要对字节码进行解析,对内存中的对象进行解析
解决方案:
- 通过setAccessible(true)关闭JDK的安全检查来提升反射速度;
- 多次创建一个类的实例时,有缓存会快很多
- ReflflectASM工具类,通过字节码生成的方式加快反射速度
②程序不安全,因为反射会获取类的私有属性和方法
16、如何利用反射创建对象?
- 使用 Class 对象的 newInstance()方法来创建该 Class 对象对应类的实例,但是这种方法要求该 Class 对象对应的类有默认的空构造器。
- 调用 Constructor 对象的 newInstance(),先使用 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance()方法来创建 Class
对象对应类的实例,通过这种方法可以选定构造方法创建实例。
17、Java中哪些地方用到了反射?
- 工厂模式中的简单工厂模式优化
- 代理模式中的动态代理方式实现
- Java JDBC数据库操作
Ⅱ、字符串
1、String、StringBuffer和StringBuilder区别
-
数据可变
String 底层使用一个不可变的字符数组 private final char value[]; StringBuffer 和 StringBuilder 都继承了 AbstractStringBuilder, 底层使用的是可变字符数组: char[] value;
-
线程安全
StringBuilder 是线程不安全的,效率较高; 而 StringBuffer 是线程安全的,效率较低。 通过他们的 append() 方法看, StringBuffer 有同步锁,而 StringBuilder 没有。
2、String str=“i” 与 String str=new String(“i”)一样吗?
不一样,因为内存的分配方式不一样。
String str="i"的方式,Java 虚拟机会将其分配到常量池中;
而String str=new String("i") 则会被分到堆内存中。
代码示例:
String x = "叶痕秋";
String y = "叶痕秋";
String z = new String("叶痕秋");
System.out.println(x == y); // true
System.out.println(x == z); // false
String x = "叶痕秋" 的方式,Java 虚拟机会将其分配到常量池中,而常量池中没有重复的元素,
比如当执行“叶痕秋”时,java虚拟机会先在常量池中检索是否已经有“叶痕秋”,
如果有那么就将“叶痕秋”的地址赋给变量,如果没有就创建一个,然后在赋给变量;
而 String z = new String(“叶痕秋”) 则会被分到堆内存中,即使内容一样还是会创建新的对象。
3、String 类的常用方法都有那些?
indexOf():返回指定字符的索引
charAt():返回指定索引处的字符
replace():字符串替换
trim():去除字符串两端空白
split():分割字符串,返回一个分割后的字符串数组
getBytes():返回字符串的 byte 类型数组
length():返回字符串长度
toLowerCase():将字符串转成小写字母
toUpperCase():将字符串转成大写字符
substring():截取字符串
equals():字符串比较
4、String s = new String(“xyz”); 创建了几个StringObject?是否可以继承String类?
先判断字符串常量池里面有没有"xyz"字符串对象,
如果有,就不会在常量池里面创建"xyz"对象,但是会在堆内存里面创建一"xyz"个对象,
并将对象地址返回给 str 变量,这种情况创建一个对象;
如果常量字符串没有,就会现在常量池里面创建"xyz"字符串,
然后再在堆内存里面创建"xyz"字符串对象,这种情况会创建两个对象
至于String类是否继承,答案是否定的,因为String默认final修饰,是不可继承的。
5、String s = “a”+“b”+“c”+“d”; 创建了几个对象?
String s1 = "a";
String s2 = s1 + "b";
String s3 = "a" + "b";
System.out.println(s2 == "ab");//flase
System.out.println(s3 == "ab");//true
这说明javac编译可以对字符串常量直接相加的表达式进行优化,
不必要等到运行期再去进行加法运算处理,而是在编译时去掉其中的加号,
直接将其编译成一个这些常量相连的结果。
String s = "a"+"b"+"c"+"d"; 相当于直接定义了一个”abcd”的字符串,
所以,上面的代码应该只创建了一个String对象。
String s ="a" + "b" +"c" + "d";
System.out.println(s== "abcd");//true
6、equals ()使用时的注意事项
常量和确定值要放在equals的左边,如果把不确定的值放在左边,不确定值为null时,就会报错。
String str = null;
System.out.println(str.equals("123"));
Ⅲ、集合
1、说说Java中常用的容器有哪些?
容器主要包括 Collection 和 Map 两种,Collection 存储着对象的集合,而 Map 存储着键值对(两个对象)的映射表。
Collection
1、Set:
-
TreeSet
基于红黑树实现,支持有序性操作,例如根据一个范围查找元素的操作。 但是查找效率不如 HashSet,HashSet 查找的时间复杂度为 O(1),TreeSet 则为 O(logN)。
-
HashSet:
基于HashMap实现,支持快速查找,但不支持有序性操作, 并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的。
-
LinkedHashSet
是 HashSet 的子类,并且其内部通过 LinkedHashMap 来实现的, 内部使用双向链表维护元素的插入顺序。
2、List:
-
ArrayList
基于动态数组实现,支持随机访问。
-
Vector
和 ArrayList 类似,但它是线程安全的。
-
LinkedList
基于双向链表实现,只能顺序访问,但是可以快速地在链表中间插入和删除元素, 不仅如此,LinkedList 还可以用作栈、队列和双向队列。
Map
- TreeMap
- HashMap
数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突).JDK1.8以后在解决哈希冲突时有了较
大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间 - HashTable
数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的 - LinkedHashMap
继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加
了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的
操作,实现了访问顺序相关逻辑。
3、迭代器 Iterator 是什么?
Iterator 接口提供遍历任何 Collection 的接口,可以从一个 Collection 中使用迭代器方法来获取迭代器实例。
特点是只能单向遍历,但是更加安全,因为它可以确保在当前遍历的集合元素被更改
的时候,就会抛出 ConcurrentModificationException 异常。
4、如何边遍历边移除 Collection 中的元素?
使用 Iterator.remove() 方法,如下:
Iterator<Integer> it = list.iterator();
while(it.hasNext()){
*// do something*
it.remove();
}
5、Iterator 和 ListIterator 有什么区别?
- Iterator 可以遍历 Set 和 List 集合,而 ListIterator 只能遍历 List。
- Iterator 只能单向遍历,而 ListIterator 可以双向遍历(向前/后遍历)。
- ListIterator 实现 Iterator 接口,然后添加了一些额外的功能,比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。
6、如何实现数组和 List 之间的转换?
- 数组转 List:使用 Arrays. asList(array) 进行转换。
- List 转数组:使用 List 自带的 toArray() 方法。
代码示例:
// list to array
List<String> list = new ArrayList<String>();
list.add("123");
list.add("456");
list.toArray();
// array to list
String[] array = new String[]{
"123","456"};
Arrays.asList(array);
7、ArrayList和LinkedList的区别
- 数据结构实现:
ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。 - 随机访问效率:
ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。 - 增加和删除效率:
在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为ArrayList 增删操作要影响数组内的其他数据的下标。 - 内存空间占用:
LinkedList 比 ArrayList 更占内存,因为 LinkedList 的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素,所以,从双向链表中的任意一个结点开始,都可以很方便地访问该节点的前后元素。 - 线程安全:
ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用LinkedList。
8、ArrayList 和 Vector 的区别是什么?
这两个类都实现了 List 接口,他们都是有序集合
- 线程安全:
Vector 使用了 Synchronized 来实现线程同步,是线程安全的,但效率较低,而 ArrayList 是非线程安全的。 - 性能:
ArrayList 在性能方面要优于 Vector。 - 扩容:
ArrayList 和 Vector 都会根据实际的需要动态地调整容量,只不过在 Vector 扩容每次会增加 1 倍,而 ArrayList 只会增加 50%。 - 同步:
Vector类的所有方法都是同步的,可以由两个线程安全地访问一个Vector对象、但是一个线程访问Vector的话代码要在同步操作上耗费大量的时间。Arraylist不是同步的,所以在不需要保证线程安全时时建议使用Arraylist。
9、插入数据时,ArrayList、LinkedList、Vector谁速度较快?
ArrayList和Vector 底层的实现都是使用数组方式存储数据,所以索引数据快而插入数据慢。
Vector 中的方法由于加了 synchronized 修饰,性能上较ArrayList差。
LinkedList 使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但插入数据时只需要记录当前项的前后项即可,所以 LinkedList 插入速度较快。
10、多线程场景下如何使用 ArrayList?
ArrayList 不是线程安全的,如果遇到多线程场景,可以通过 Collections 的 synchronizedList() 方法将其转换成线程安全的容器后再使用。
11、说一下HashSet的实现原理?
基于HashMap实现,默认构造函数是构建HashMap,封装了一个 HashMap 对象来存储所有元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存。
12、HashSet与HashMap的区别
10、HashMap的实现原理
- HashMap是基于哈希表实现的,由数组+链表组成,jdk8后加入了红黑树;内部维护了一个存储数据的Entry数组,采用链表解决冲突,每一个Entry本质上是一个单向链表;每一个元素都是一个key-value对,内部通过单链表解决哈希冲突,容量不足时,会自动增长。
- 实现了Map、Cloneable(可克隆)、Serializable(可序列化)这三个接口,能被克隆,支持序列化。
- HashMap是非线程安全的,但只适用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap。
- HashMap中key和value都允许为null,key为null的键值对永远都放在以table[0]为头结点的链表中。
HashMap的总体结构如下:
11.说说HashMap的put方法执行流程?
- 将给定的key值通过哈希算法和“与”操作计算得出数组下标,如果数组下标为空,就将对应的key和value封装成一个对象存入数组中。
- 如果数组下标不为空,在jdk8中,会先判断该node对象是在链表还是红黑树中,如果在红黑树中,就存入红黑树,还要判断该红黑树节点是否有key值,如果有,直接更新value值。
- 如果在链表中,则使用尾插法添加到链表最后一个位置,同时还要遍历链表,如果存在key值,则直接更新value值。
- 插入之后链表长度会更新,如果长度大于8,则将链表转为红黑树。
- 最后判断是否要进行扩容,如果需要就扩容,如果不需要就退出put()方法。
链表转为红黑树的原因是:当链表长度过大时,会增加查询的时间,使性能降低,因为每次插入链表的时候都会遍历。
12.说说HashMap的get方法执行流程?
根据key的hashcode算出元素在数组中的下标,之后遍历Entry对象链表,直到找到元素为止。
13,哈希冲突有几种解决办法?
-
链地址法:
对于相同的哈希值,使用链表进行连接; 特点:处理冲突简单,无堆积现象,但查询效率低。
-
再哈希法:
提供多个哈希函数,当第一个哈希函数计算的hashcode的值相等时,使用第二个第三个第四个,直到所有的对象的hashcode的值都不相等。 特点:不易产生聚集,但增加了计算时间。
-
建立公共溢出区:
将哈希表分为基本表和溢出表,将溢出的数据都放在溢出表中。
-
开放定址法:
遇到哈希冲突时,寻找新的空闲的哈希地址,有两种方法:
-
线性探测法:
当存放的地址被占用时,hashcode值往后面+1并对存储空间的长度取模,直到找到一个空余的地址,取模是为了保证找到的地址在有效空间之内。
-
平方探测法:
hash(key) = hash(key) ± 1²
14、HashMap 与 HashTable的区别
- 线程安全:
HashMap 是非线程安全的,HashTable 是线程安全的;HashTable 内部的方法基本
都经过 synchronized 修饰。 - 效率:
因为线程安全的问题,HashMap 要比 HashTable 效率高一点。 - 对Null key 和Null value的支持:
HashMap 中,null 可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为 null,但是在 HashTable 中 put 进的键值只要有一个 null,直接抛NullPointerException异常。 - 初始容量大小和每次扩充容量大小的不同 :
创建时如果不指定容量初始值,Hashtable 默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap 默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。
创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为2的幂次方大小。 - 底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。
- 推荐在单线程环境下使用 HashMap ,如果需要多线程使用则用 ConcurrentHashMap 替代。
15、ConcurrentHashMap 的实现原理
调用put()方法时,首先进入乐观锁(就是一个死循环),然后,在乐观锁中判断容器是否初始化,如果没有则调用 initTable() 方法初始化容器,
如果已经初始化,则通过key的hash值来判断table中是否存在相同的key,如果不存在,执行casTabAt()方法插入元素,并退出循环,这里利用了CAS操作,保证节点为空时不加锁。
如果存在,则加上synchronized锁,再判断该节点的hash值是否为MOVED,如果是说明tab在扩容,调用helpTransfer(tab, f)方法扩容。
最后遍历链表,查找是否存在相同的key,如果存在则更新value,如果不存在则将新的节点添加到链表末尾。
如果链表长度大于等于TREEIFY_THRESHOLD,即链表长度达到了8,需要将链表转化为红黑树以提高查找效率。
而ConcurrentHashMap的get()方法是不加锁的,因为volatile关键字修饰,保证每次获取值都是最新的。
16、ConcurrentHashMap 不支持 key 或者 value 为 null 的原因?
因为 ConcurrentHashMap 是用于多线程的 ,如果ConcurrentHashMap.get(key)得到了 null ,就无法判断,是映射的value为 null ,还是没有找到对应的key而为 null ,而用于单线程环境下的 HashMap 却可以用containsKey(key) 判断到底是否包含了这个 null 。
17.说说ArrayList 的扩容机制?
18.Array和ArrayList有何区别?
Array可以容纳基本数据类型和对象,而ArrayList只能容纳对象
Array是指定大小的,ArrayList 的容量是根据需求自动扩展
ArrayList有addAll(),removeAll(),iterator()等等
如果大小指定,并且主要存储和遍历数据,使用Array;
对于基本类型数据,ArrayList 使用自动装箱来减少编码工作量;而当处理固定大小的基本数据类型的时候,这种方式相对比较慢,这时候应该使用Array;
如果使用多维数组,使用[]数组比 List更容易。
19.comparable和comparator的区别?
-
Comparable是一个接口,它只有一个方法compareTo(),实现了Comparable接口的类可以进行自然排序。自然排序指的是,按照对象的自身属性进行排序,例如数字从小到大,字符串按照字典顺序等。实现了Comparable接口的类可以直接使用Collections.sort()方法进行排序。
-
Comparator也是一个接口,它有两个方法compare()和equals(),实现了Comparator接口的类可以进行定制排序。定制排序指的是,按照指定的排序规则进行排序,例如按照年龄从小到大,按照身高从高到低等。实现了Comparator接口的类需要将Comparator对象传入到Collections.sort()方法中,或者使用Arrays.sort()方法进行排序。
-
Comparable是在类内部实现的,实现了Comparable接口的类可以直接调用compareTo()方法进行排序。
-
Comparator是在类外部实现的,可以为一个类创建多个Comparator对象,每个Comparator对象可以按照不同的排序规则进行排序。排序时需要将Comparator对象传入到排序方法中。
-
Comparable的排序规则是固定的,而Comparator的排序规则是可以动态变化的。
- 总的来说,Comparable适用于自然排序,Comparator适用于定制排序。当需要对一个类进行排序时,如果排序规则是固定的,可以实现Comparable接口,如果排序规则是动态变化的,可以实现Comparator接口。
20.Collection和Collections有什么区别?
-
Collection是Java集合框架中的一个接口,它是所有集合类的父接口,提供了对集合元素的基本操作(添加、删除、查找、遍历等)。Collection接口直接继承自java.lang.Iterable接口,因此它的所有子类都可以使用Java的for-each循环进行遍历。
-
Collections是Java集合框架中的一个工具类,提供了一系列静态方法,用于对集合进行操作,例如排序、查找、替换、同步等。Collections类中的所有方法都是静态方法,因此不需要创建Collections对象就可以使用这些方法。
-
简单来说,Collection是一个接口,用于定义集合类的基本操作;Collections是一个工具类,用于提供集合类的一些常用操作。两者之间的区别是:Collection是一个接口,需要通过具体的实现类来创建集合对象;而Collections是一个工具类,不需要创建对象,直接通过类名调用静态方法即可。
Ⅳ、异常
1、如何选择异常类型?
2、JVM 是如何处理异常的?
在一个方法中如果发生异常,这个方法会创建一个异常对象,并转交给 JVM,该异常对象包含异常名称,异常描述以及异常发生时应用程序的状态。
JVM 会查找是否有可以处理异常的代码,如果有,则调用异常处理代码,如果没有,JVM 就会终止应用程序。
3、throw 和 throws 的区别是什么?
throw用在方法内部,只能用于抛出方法或代码块中的一种异常,受查异常和非受查异常都可以被抛出。
throws用在方法声明上,可以抛出多个异常,用来标识该方法可能抛出的异常列表。
4、Java常见异常有哪些?
- java.lang.NullPointerException (空指针异常)
调用了未经初始化的对象或者是不存在的对象,比如new了一个空的字符串或数组,在调用时可能会报空指针异常。- java.lang.ClassNotFoundException 指定的类不存在
通常都是程序试图通过字符串来加载某个类时可能引发异常。比如:调用Class.forName()。- java.lang.NumberFormatException 字符串转换为数字异常
当试图将一个String转换为指定的数字类型,而该字符串确不满足数字类型要求的格式时,抛出该异常。- java.lang.IndexOutOfBoundsException 数组下标越界异常
- java.lang.IllegalArgumentException 方法的参数错误
- java.lang.IllegalAccessException 没有访问权限
- java.lang.ArithmeticException 运算异常
- java.lang.ClassCastException 数据类型转换异常
- java.lang.FileNotFoundException 文件未找到异常
当程序试图打开一个不存在的文件进行读写时将会引发该异常。该异常由FileInputStream