java小白面试攻略
- java基础
- 1.什么是面向对象
- 2. JDK、JVM、JRE三者之间的区别和联系
- 3. == 和 equals的区别
- 3. 为什么重写equals时还需要重写hashCode()方法
- 4. String字面量和new出来的变量有什么区别
- 5. 简述final作用
- 6. 为什么局部内部类/匿名内部类只能访问局部final变量?
- 7. String、StringBuffer、StringBuilder区别与场景
- 8. 接口与抽象类的区别
- 9. List和Set的区别
- 10. ArrayList和LinkedList的区别
- 11. HashMap和HashTable的区别、以及底层实现是什么?
- 12 .HashMap中的hash为什么要右移16异或?
- 13. 线程安全的Map集合
- hashMap解决hash冲突的方式
- Spring boot中自动装配机制的原理
前言:记录面试找工作的学习历程
java基础
1.什么是面向对象
对比面向过程,是两种不同的处理问题的角度;面向过程更注重于事情的每一个步骤和顺序,面向对象则注重事情有哪些参与者,以及各自需要做什么。面向过程比较直接高效,而面向对象更易于复用、扩展和维护。
面向对象有三大特点:封装、继承和多态。
封装 的意义在于明确外部调用者允许其使用的方法和数据项,外部调用者无需关心内部的实现细节,防止外部调用者的随意修改。(javabean的属性封装/orm框架(mybatis对sql的连接等)
)
继承 继承基类的方法,并作出自己的改变或扩展。
多态 同属一个基类的不同子类,对同一方法展现出不同的执行逻辑。(实现方式:继承、重写、父类引用指向引用对象
),好处是易于代码的扩展,如果想调用父类某一方法a()做不同的逻辑,那么就可以实现一个类去继承这个父类,重写该方法a(), 去调用子类的该方法,这样就可以不在动父类代码的前提下做这件事。
其中多态需要注意的是:编译类型和运行类型
编译类型由声明变量时使用的类型决定,运行类型由实际付给该变量的对象决定;
编译时该变量只能调用其编译类型具有的方法,因为编译阶段能调用那些成员是由编译类型决定的;
属性没有重写之说,属性的值看编译类型
可以调用父类中的所有成员(需要遵循访问权限)
不能调用子类中特有的成员
最终运行看子类的具体实现
属性看编译,方法看运行
父类引用指向子类对象:Animal cat=new Dog();
cat编译类型是Animal,运行类型是Dog;
2. JDK、JVM、JRE三者之间的区别和联系
jdk : java development kit 就是Java开发工具包;(Java开发可以调用的工具包)
jre : java runtime environment 就是Java运行时环境;(Java代码运行所必须的基础)
jvm: java virtual machine 就是Java虚拟机(编译解释Java源代码)
JDK=JRE+多种Java开发工具
JRE=JVM+各种类库
这三者的关系是一层层的嵌套关系。JDK>JRE>JVM
3. == 和 equals的区别
==比较的是栈中的值,如果比较基本数据类型,则比较的是数;如果是引用数据类型,比较的是堆中内存的地址。(jvm主要分为堆内存和栈内存,基本数据的值是存储在栈中,引用数据类型是栈保存对象的地址,实际存储是在堆中)
equals:在所有类的父类Object中,equals的比较默认是采用==进行比较,部分子类一般会重写。比如在String中,equals就是重写的,比较的是字符串的内容。
3. 为什么重写equals时还需要重写hashCode()方法
在Object类有hashCode方法,他会在散列集合中会用到,比如HashMap、HashTable这些,当往这样一些集合类中添加元素时需要判断这个元素是否存在,而如果直接使用equals方法效率太低,所以一般是直接使用对象的hashcode值进行取模运算;如果集合中没有这个对象的hashcode值则直接存进去,而如果有,再用equals比较,相同直接覆盖,不同在散列到其他地址。这样一来实际调用equals的次数就降低了。hashcode默认是将对象的内存地址换成int数值返回,这个值就是哈希码,作用是确定对象在哈希表中的存储位置。
如果只重写equals方法而不重写hashcode方法就可能会导致a.equals(b)相同而hashcode不同,导致这个类无法与所有的散列集合使用,因为散列集合是使用hashcode计算key的存储位置,如果存储两个完全相同的对象但是有不同的hashcode,导致这两个完全相同对象存储在表的不同位置,会出现一些错误。就会出现一个悖论(一个对象应该在一个位置)。
4. String字面量和new出来的变量有什么区别
比如:String s1="hello"
, String s2=new String("hello")
s1存储在堆内存中的常量池中,s2存储在堆内存中
5. 简述final作用
最终的:
修饰类:表示类不可以被继承
修饰方法:表示该方法不可被子类重写,但是可以重载
修饰变量:表示该变量是常量
修饰类变量:声明时必须指定初始值或者在静态代码块中指定初始值
修饰成员变量:可以在声明时初始化或者在非静态代码块或构造器中指定初始值
6. 为什么局部内部类/匿名内部类只能访问局部final变量?
7. String、StringBuffer、StringBuilder区别与场景
String是final修饰的,不可变,每次操作都会产生新的String对象
StringBuffer、StringBuilder都是在原对象上操作
StringBuffer是线程安全的,StringBuilder是线程不安全的
StringBuffer的方法都是使用synchronlized修饰的
性能:StringBuilder > StringBuffer > String
(String变量在作为参数传递时是拷贝了一份数据传入,不是引用传入)
优先使用StringBuilder , 在多线程共享变量时优先使用StringBuffer
8. 接口与抽象类的区别
接口的方法只能是public abstract方法,成员变量只能是public static final类型
抽象类只能单继承,接口可以多个实现
抽象类的本质是类,除了abstract方法,还可以包含类的所有成员;接口的本质是对类的操作进行约束,指定标准规范。使用场景:当你关注一个事务的本质时,用抽象类;当你关注一个操作时用接口
9. List和Set的区别
List集合元素是有序可重复的,允许多个Null存在;Set是无序不重复的,最多只能有1个Null
List可以使用Iterator取出所有元素,也可以用get(index)获取指定下标元素,Set只能用Iterator获取所有元素
List和Set都是Collection接口的子接口,是常用的集合类型;List的实现有基于数组的ArrayList(线程不安全,效率高)和Vector(线程安全)以及基于链表的LinkedList(线程不安全)
Set实现类有:HashSet和TreeSet
HashSet通过哈希码来保存元素,所以可以防止元素重复。它是一种无序的,线程不安全,运行效率高,常用的集合类
TreeSet基于树状结构,可以实现排序功能,支持自然排序(即对于基本数据类型可以实现默认升序排列)
10. ArrayList和LinkedList的区别
ArrayList 与 LinkedList 都是 List 接口的实现类,因此都实现了 List 的方法,只是实现的方式有所不同。
ArrayList: 底层基于动态数组,查询修改快,删除添加慢
对于随机访问,ArrayList优于LinkedList,ArrayList可以根据下标以O(1)时间复杂度对元素进行随机访问。而LinkedList的每一个元素都依靠地址指针和它后一个元素连接在一起,在这种情况下,查找某个元素的时间复杂度是O(n)
LinkedList底层基于双向链表,对于插入和删除操作,LinkedList优于ArrayList,因为当元素被添加到LinkedList任意位置的时候,不需要像ArrayList那样重新计算大小或者是更新索引。
LinkedList比ArrayList更占内存,因为LinkedList的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。
ArrayList底层原理: ArrayList,底层封装了一个数组,利用一个int size()统计数组中元素的个数,创建集合对象时,底层数组长度为0,只有当第一次往集合中添加(第一次调用add方法)元素的时候,为数组分配空间,长度默认为10,如果存储的元素个数达到数组的长度上限时,自动扩容,每次扩容为原来的1.5倍(每次扩容倍数太大,空间利用率低;每次扩容一个空间,导致频繁的扩容,降低程序效率)
11. HashMap和HashTable的区别、以及底层实现是什么?
区别:HashMap方法没有加Synchronized修饰,线程不安全,HashTable是线程安全的
hashmap性能比hashtable要好,因为hashtable采用了全局同步锁来保证安全性,对性能影响较大;HashMap允许key和value为空,HashTable则不允许;对于底层实现,hashMap采用数组+链表+红黑树来实现,而HashTable使用数组+链表实现;最后两个key的散列算法不同。
HashMap底层实现:数组+链表实现
当存入元素时会计算key的hash值,二次hash对数组取模,对应到数组下标,
如果没有产生hash冲突,则直接创建Node存入数组,
如果产生冲突(对应到数组下标时,该下标已经有元素),此时该下标对应的是链表,将存入元素的value与链表元素进行equals比较,相同则取代该元素,不同则判断链表高度在插入链表(链表高度达到8,并且数组长度到64则转变为红黑树,长度低于6则转回链表)
key为null时 ,直接存到下标为0的位置。
12 .HashMap中的hash为什么要右移16异或?
通过二次散列也就是右移16异或来增加key的随机性,降低hash冲突的概率
13. 线程安全的Map集合
Hash Map不是线程安全的,在多线程的环境下,HashMap可能会出现数据丢失和获取不了最新数据的问题;我们想要线程安全可以使用ConcurrentHashMap,它是线程安全的Map实现类,它在juc包下的。
线程安全的Map实现类除了ConcrruentHashMap还有HashTable,但是HashTable是采用全局使用Synchronized同步锁来保证安全,比较低效,所以一般考虑线程安全是都使用ConcrruentHashMap。ConcrruentHashMap的底层是采用数组+链表+红黑树,它支持高并发的访问和更新,是线程安全的。其通过在部分加锁和采用CAS算法来实现同步,get方法没有加锁。
hashMap解决hash冲突的方式
hashmap底层是采用了数组这样的一个结构存储数据元素,数组的默认长度是16,当我们通过put方法去添加数据的时候,hashmap会根据key的hash值进行取模运算最终把这个值存储到数组的一个指定位置,但是这样的一个设计方式会存在hash冲突的问题,就是两个不同hash值的key最终取模以后会落到同一个数组下标,所以hashMap引入了一个链式寻址法来解决这个冲突问题;hashmap会将这些冲突的key组成一个单向链表,采用尾插法将这个key保存到链表尾部,另外为了避免链表过长,导致我的查询效率降低
Spring boot中自动装配机制的原理
自动装配简单的说就是自动把第三方组件的bean装载到ioc容器中,不需要开发人员再去写bean相关的一个配置;在spring boot应用中只需要在启动类上去加上@SpringBootApplication注解就可以去实现自动装配;@SpringBootApplication是一个复合注解,真正去实现自动装配的是@EnableAutoConfiguration; 自动装配的实现主要依靠三个核心的关键技术
资产管理系统的研发人员在编写代码时,当括号有多层嵌套时,希望编辑器能快速匹配到当前括号的另一半括号。现给定一个只包括{ ,},[,],(,) 的字符串s,请编写一个方法,当括号都成对且以正确的顺序闭合时,请返回相应的颜色字符串;否则,请返回“输入错误”。
其中,{}对应红色,[]对应黄色,()对应蓝色。
// {}红 []黄 蓝()
public class Test {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.next();
String[] str = s.split("");
Map<String,String> map = new HashMap<>();
map.put("{","红");
map.put("}","红");
map.put("[","黄");
map.put("]","黄");
map.put("(","蓝");
map.put(")","蓝");
solution(str,map);
}
public static void solution(String[] arr,Map<String,String> map){
Stack<String> stacks = new Stack<>();
for (String value : arr) {
if (stacks.empty()) {
stacks.push(value);
} else {
String tmp = stacks.pop();
if (!map.get(tmp).equals(map.get(value)) || map.get(tmp).equals(map.get(value)) && tmp.equals(value)) {
stacks.push(tmp);
stacks.push(value);
}
}
}
if (!stacks.empty()){
System.out.println("输入错误");
}else Arrays.stream(arr).forEach(s -> System.out.print(map.get(s)));
}
}