00 可能涉及的知识
- java基础知识
- 设计模式
- 异常处理,跨域
- git使用
- SpringBoot自动配置原理,AOP,Spring源码,springmvc流程
- redis:持久化AOF
- 数据结构算法题
- 计算机网络3次握手
- 并发
java基础
final 定义的变量不能再改变值,定义的类为最终类,不能再被继承
序列化:将一个对象编码为字节流称为对象的序列化
,相反的处理过程称为反序列化
重写:子类重写父类方法
重载:同一个类,方法名字相同,而参数不同
java中值传递
- 值传递 :方法接收的是实参值的拷贝,会创建副本;引用传递 :方法接收的直接是实参所引用的对象在堆中的地址,不会创建副本,对形参的修改将影响到实参。
- java中只有值传递没有引用传递。如果参数是基本类型的话,传递的就是字面量值;如果参数是引用类型,传递的就是所引用的对象在堆中地址值的 拷贝,同样会指向该对象
多态:几个子类继承同一个父类,new对象时,把对象类型声明为父类类型,再调用对象的方法时就是调用的子类的方法。实现方法:
- 继承父类,重写方法
- 实现接口
- 抽象类,抽象方法
面向对象特征
继承
封装
多态:父类引用指向子类对象
==和equals区别
==
比较的是两个对象是否是同一对象,对比的是栈中的值,基本数据类型是变量值,引用类型是堆中内存对象的地址一个类没有重写equals方法,默认采用的是
==
(Object类中定义的),重写了equals方法按重写的规则来。String重写了equals方法,比较的是字符串内容
java public class test1 { public static void main(String[] args) { String a = new String("ab"); // a 为⼀个引⽤ String b = new String("ab"); // b为另⼀个引⽤ String aa = "ab"; // 放在常量池中 String bb = "ab"; // 从常量池中查找 if (a == b) // false,⾮同⼀对象 System.out.println("a==b"); if (aa == bb) // true System.out.println("aa==bb"); } }
字符型常量和字符串常量的区别
字符常量相当于⼀个ASCII 值,可以参加表达式运算; 字符串常量代表该字符串在内存中的地址
成员变量与局部变量的区别有哪些?
成员变量如果没有被赋值,则会⾃动赋值;⽽局部变量不会⾃动赋值
成员变量是对象的⼀部分,随着对象的创建⽽存在;⽽局部变量随着⽅法的调⽤⽽⾃动消失
成员变量可以被 public , private , static 等修饰符所修饰,⽽局部变量不能被访问控制修饰
⼀个类的构造⽅法的作⽤是什么
完成对象的初始化工作,即便没有声明构造方法也会有默认的不带参数的构造方法
静态方法和实例方法有何不同
静态⽅法访问本类的成员时,只允许访问静态成员变量、⽅法,⽽不允许访问非静态的成员变量和方法
在 Java 中定义⼀个不做事且没有参数的构造⽅法的作⽤
Java 程序在执⾏⼦类的构造⽅法之前,如果没有⽤ super() 来调⽤⽗类特定的构造⽅法,则会调⽤⽗类中“没有参数的构造⽅法”,如果父类中没有不带参数的构造方法会在编译时报错
Java 序列化中 transient 关键字的作用
被该变量修饰的关键字不会被序列化,当对象被反序列化时也不会被恢复
⽤键盘输⼊常⽤的两种⽅法
- 通过
Scanner
java Scanner input = new Scanner(System.in); String s = input.nextLine(); input.close();
- 通过
BufferedReader
java BufferedReader input = new BufferedReader(new InputStreamReader(System.in)); String s = input.readLine();
java异常类层次结构
finally 块: ⽆论是否捕获或处理异常, finally 块⾥的语句都会被执⾏。当在 try 块或 catch 块中遇到 return 语句时, finally 语句块将在⽅法返回之前被执⾏,并覆盖原始的返回值。
Throwable
类常⽤⽅法
public string getMessage()
:返回异常发⽣时的简要描述public string toString()
:返回异常发⽣时的详细信息public void printStackTrace()
:在控制台上打印Throwable
对象封装的异常信息
final
修饰类:表示类不可被继承
修饰方法:表示方法不可被子类覆盖,但是可以重载
修饰变量:如果是基本数据类型的变量,则其数值⼀旦在初始化之后便不能更 改;如果是引⽤类型的变量,则在对其初始化之后便不能再让其指向另⼀个对象
String、StringBuffer、StringBuilder
- String是final修饰的,每次操作都会产生新的String对象
- StringBuffer是线程安全的,每个方法都加了
synchronized
- StringBuilder线程不安全
一般优先用StringBuilder,多线程情况下使用StringBuffer
接口和抽象类区别
- 一个类可以实现多个接口,但只能实现一个抽象类;接口本身可以通过
extends
扩展多个接口 接口中只能有static、final修饰的变量,不能有其他变量;抽象类不一定
接口中只能有默认方法和静态方法;抽象类除了抽象方法还可以有普通方法
构造器 Constructor 是否可被 override
Constructor 不能被 override(重写),但是可以 overload(重载)
深拷贝和浅拷贝
深拷贝会重新创建一个对象然后指向它;浅拷贝传递的是对象的地址,两个变量会指向同一对象
为什么 Java 中只有值传递
对象作为参数传递的是对象地址值的拷贝;C++引用传递改变形参的值会改变实参,引用变量和被引用的变量是同一个
反射
运行时获取Class类对象及其内部信息(方法、属性、构造函数),然后控制实例对象的能力
⾃动装箱与拆箱
装箱:调用valueOf()
将基本数据类型转换成对象,如Integer.valueOf(int)
拆箱:将包装类型转换为基本数据类型;如:Integer.intValue()
说说List,Set,Map三者的区别
- List : 存储的元素是有序的、可重复的。
- Set :存储的元素是⽆序的、不可重复的
- Map:使⽤键值对(kye-value)存储,Key 是不可重复的,value可重复 的
说一下HashMap的结构、扩容机制以及树形化
HashMap是数组+链表+红黑树实现(红黑树:能够保证平衡的二叉查找树)
当HashMap中元素个数超过数组大小*loadFactor时就会扩容。默认数组大小Capacity为16,每次扩容就把数组大小扩大一倍;loadFactor为负载因子,默认为0.75,负载因子的大小决定了HashMap的数据密度
当HashMap中一个链的对象个数达到8个,并且数组容量capacity达到64,这个链表就会用红黑树表示,如果没有达到64就先扩容解决。如果树中结点个数低于6个,树就会转换为链表
HashMap 的⻓度为什么是2的幂次⽅
计算元素在数组位置时,需要用hashCode值对数组长度length取余,即: hash%length,如果length的值是2的幂次就可以用二进制与&运算,hash&(length-1)的结果和hash%length结果一样,采⽤⼆进制位操作 &,相对于%能够提⾼运算效率
负载因子值的大小,对HashMap有什么影响
- 负载因子的大小决定了HashMap的数据密度。
负载因子越大密度越大,扩容时间就越晚,发生碰撞的几率就越高,数组中的链表就越容易长, 造成查询或插入时的比较次数增多,性能会下降。
负载因子越小,就越容易触发扩容,数据密度也越小,意味着发生碰撞的几率越小,数组中的链表也就越短,查询和插入时比较的次数也越小,性能会更高,但是会浪费一定的内容空间
HashMap和HashTable区别
线程是否安全:HashMap线程不安全,HashTable线程安全,因为HashTable内部的方法基本经过
synchronized
修饰(效率低,全局一把锁)对 Null key的支持:HashMap支持存储为空的key,HashTable不允许支持为空的key
- 底层数据结构:HashMap中当链表的长度大于8,并且数组的长度达到64时就会把链表转换为红黑树;HashTable没有这样的机制
HashMap和HashSet区别
HashSet底层使用HashMap实现的,在构造器中是new 了 HashMap,大部分方法都是调用的HashMap中的方法
```java // Dummy value to associate with an Object in the backing Map private static final Object PRESENT = new Object();
public HashSet() { map = new HashMap<>();}
public boolean add(E e) { return map.put(e, PRESENT)==null; } ```
HashSet如何检查重复
先计算hashCode(),如果hashCode值不一样,则加入该对象;如果hashCode值一样,就调用equals值比较,equals()返回true就认为两个对象一样,加入失败,equals()返回值不一样就加入该对象
ArrayList扩容机制
- 如果添加元素时,先判断数组需要的最小容量,如果原数组是空数组,则指定数组最小容量为10(
DEFAULT_CAPACITY
),否则指定数组容量为数组中元素个数+1,如果需要的最小容量大于数组长度则扩容,扩容1.5倍int newCapacity = oldCapacity + (oldCapacity >> 1);
Arraylist 与 LinkedList 区别
- Arraylist 底层使⽤的是 Object 数组;对于随机访问get和set,ArrayList优于LinkedList,因为LinkedList要移动指针
- LinkedList底层使⽤的是双向链表;对于新增add()和删除操作remove(),LinkedList比较占优势,因为ArrayList要移动数据
ArrayList 与 Vector 区别
Vector和ArrayList几乎是完全相同的,都实现了相同的类和接口(TreeMap和TreeSet底层都用的红黑树)
区别在于Vector是线程安全的(方法上加了
synchronied
),ArrayList线程不安全
如何选⽤集合?
主要根据集合的特点来选⽤,⽐如我们需要根据键值获取到元素值时就选⽤ Map 接⼝下的集 合,需要排序时选择 TreeMap ,不需要排序时就选择 HashMap ,需要保证线程安全就选⽤ ConcurrentHashMap 。
当我们只需要存放元素值时,就选择实现 Collection 接⼝的集合,需要保证元素唯⼀时选择实现 Set 接⼝的集合⽐如 TreeSet 或 HashSet ,不需要就选择实现 List 接⼝的⽐如 ArrayList 或 LinkedList ,然后再根据实现这些接⼝的集合的特点来选⽤。
说一下ConcurrentHashMap
jdk.7之前 采用Segment 数组+HashEntry 数组 + 链表。Segment 的结构和 HashMap 类似,是 ⼀种数组和链表结构,⼀个 Segment 包含⼀个 HashEntry 数组,每个 HashEntry 是⼀个链表结构的元素,每个Segment对象用一把锁
jdk1.8 采用Node数组 + 链表 +红⿊树,类似HashMap ,synchronized 只锁定当前链表或红⿊⼆叉树的⾸节点,这样只要 hash 不冲突,就不会产⽣并 发,效率⼜提升 N 倍
hashCode()?????????????????????
hashCode()
默认根据堆上的对象产⽣哈希值,哈希值代表了对象的特征,同一哈希值可能对应多个对象,即哈希值相等并不代表两个对象相等