Java基础
1、new一个对象发生了什么?
Java在new一个对象的时候会先查看对象所属类有没有被加载到内存,没有的话会先通过类的全限定名来加载。所有new 一个对象有两步:加载并初始化类和创建对象。
1>类加载过程:
Java是使用双亲委派模型来进行类的加载:如果一个类加载器(ClassLoader)收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委托给父类加载器去完成,每一层的类加载器都是如此。加载请求最终都会传到顶层的启动类加载器中,只有当父类加载器反馈无法完成加载请求时,子加载器才会自己去加载。
使用双亲委派机制的好处:能够确保一个类的全局唯一性,当程序中出现多个限定名相同的类时,类加载器在执行加载时只会加载其中一个类。
- 加载
- 验证
- 准备
- 解析
- 初始化
2>创建对象:
- 在堆区分配对象需要的内存
分配的内存包括本类和父类的所有实例变量,不包括静态变量 - 对所有实例变量赋默认值
将方法区内对实例变量的定义拷贝一份到堆区,赋默认值 - 执行实例初始化代码
初始化顺序是先初始化父类再初始化子类,初始化时先执行实例代码块然后是构造方法 - 如果有类似于Child c = new Child()形式的c引用的话,在栈区定义Child类型引用变量c,然后将堆区对象的地址赋值给它
- 需要注意的是,每个子类对象持有父类对象的引用 ,可在内部通过super关键字来调用父类对象,但在外部不可访问
2、@Autowired 和 new对象有什么区别
根本原因在于当Spring框架帮我们管理的时候就会自动的初始化接下来会用到的属性,而通过new对象的方式,在该new对象中使用到的一些实例就需要自己去做初始化,否则就会报空指针异常
集合相关
1、ArrayList相关问题
(参考:https://www.cnblogs.com/maoyali/p/8805975.html)
基于数组实现,是一个动态数组,容量自动增长。
不是线程安全的,只能在单线程环境下使用,多线程环境下可以考虑用Collections.synchronizedList(List l)
函数返回一个安全的ArrayList类,也可以使用concurrent并发包下的CopyOnWriteArrayList类。
ArrayList实现了Serializable接口,因此它支持序列化,能够通过序列化传输,实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问,实现了Cloneable接口,能被克隆。
每个ArrayList实例都有一个容量,该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向ArrayList中不断添加元素,其容量也自动增长。自动增长会带来数据向新数组的重新拷贝,因此,如果可预知数据量的多少,可在构造ArrayList时指定其容量。在添加大量元素前,应用程序也可以使用ensureCapacity操作来增加ArrayList实例的容量,这可以减少递增式再分配的数量。
- 【初始化】 ArrayList()初始化后的底层数组长度为0,且在添加第一个元素时,底层数据长度变为10,之后扩容按原来的1.5倍进行扩容
提供三个构造器,默认初始化容量为10的空列表、指定初始容量的空列表、以及包含指定collection元素的列表 - 【增】 直接从尾部追加单个数据 | 在指定的index处添加数据(从最尾部开始到index位置,依次往后复制一位(或者指定集合长度),然后将新增的元素放入index处)效率低
- 【查】 添加元素前会判断数组容量是否充足,不够将会数组扩容到之前容量的1.5倍,将原数据拷贝到新数组中。所以如果我们事先知道将会添加多少数据到集合的话最好在初始化时传入参数指定集合长度,避免数组扩容发生。
- 获取元素,根据下标的index,直接能得到元素。效率高
- 【删】 删除元素 效率低
删除指定下标元素,直接从最后一个元素到index的后一位元素一次往前覆盖,将末尾元素置空。
删除指定元素,从0到size位置循环遍历,判断是否找到该元素,找到该元素之后记录下标,然后根据下表移除该元素(同1)
删除列表包含集合c的所有元素
删除列表中给定谓词的所有元素
清空集合,循环集合从0到index进行置空处理,然后将size修改为0
- 【改】 直接将index位置的元素更换为element
2、HashMap相关问题
参考(https://www.iteye.com/blog/zhangshixi-672697)
3、如何删除List中的某个元素
- 使用Iterator,顺序向下,如果找到元素,则使用remove方法进行移除
- 倒序遍历List,如果找到元素,则使用remove方法进行移除
4、Iterator 和 ListIterator 的区别是什么?
- Iterator 可用来遍历 Set 和 List 集合,但是 ListIterator 只能用来遍历 List
- Iterator 对集合只能是前向遍历,ListIterator 既可以前向也可以后向
- ListIterator 实现了 Iterator 接口,并包含其他的功能。比如:增加元素,替换元素,获取前一个和后一个元素的索引等等
数据结构
1、BTree、B+Tree、红黑树
树形结构是一类高级非线性数据结构,其中最重要的就是树和二叉树,一些重要的算法和高性能的系统几乎都用到了树形结构。
-
BTree
-
B+Tree
-
红黑树
2、字典树、双数组字典树
- 字典树
字典树又称为单词查找树。哈希树的变种,常用于统计,查找搜索引擎中用于分词,词频统计(TF/IDF),自动补全机制等。
查找效率高:其核心思想是利用公共前缀来减少查询时间。
代码实现
class TreeNode{
final static int MAX_SIZE = 26;
char data; //表示当前节点存的字母
boolean isEnd = false; //表示是否为叶子节点
TreeNode [] childs; //表示子节点
public TreeNode(){
childs = new TreeNode[MAX_SIZE]; //因为英文最多26个字母
isEnd = false;
}
}
public class TireTree {
//redis 同时只会执行一条命令.inc 0 ++, 释放 setnx set 一个设置失效时间. lua zookeeper 节点一定有序 TCC MQ
public static void createTireTree(TreeNode node,String str){ //单词全部转成小写
//ascii A => 65,a=>97 -97 >0
//a->0 b->1,c->2
char d[] = str.toCharArray();
for(int i = 0 ;i < d.length; i++){
int loc = d[i] - 'a'; //转成0~25之间的数字了 这里是一个技巧
if(node.childs[loc] == null){//我们把英文字母存到一个数组里面0~25 a['a'] === a[97] => a[0] = 'a'缩小空间,0+97
node.childs[loc] = new TreeNode();
node.childs[loc].data = d[i];
}
node = node.childs[loc];
}
node.isEnd = true;
}
public static boolean find(String str,TreeNode node){
char d[] = str.toCharArray();
for(int i = 0 ; i < d.length ; i ++) {
int loc = d[i] - 'a';
if(node.childs[loc] != null){
node = node.childs[loc];
}else{
return false;
}
}//O(Nlog(n)) O(n)
return node.isEnd;
}
public static void main(String[] args) {
String s[] = {"java","ps","php","ui","css","js"};
TreeNode root = new TreeNode();
for(String ss : s){
createTireTree(root,ss);
}
System.out.println("插入完成");
System.out.println(find("java", root));
System.out.println(find("jav", root)); //找前缀就是自动补全
}
}
- 双数组字典树
Spring相关
1、Spring的 IOC 容器比New对象究竟好在哪?
- 资源集中管理,实现资源的可配置和易管理
- 降低了使用资源双方的依赖成都,耦合度
SpringBoot相关
1、SpringBoot是如何实现自动化配置的?
1>自动配置原理
自动配置的核心注解是 @SpringBootApplication
,启动类注解,是一个复合注解:其中包含 @SpringBootConfiguration
和 @EnableAutoConfiguration
。
(1) @SpringBootConfiguration:该注解上有@Configuration
表明springboot启动类是一个配置类
(2) @EnableAutoConfiguration:表示开启自动化配置,也是复合注解:包含 @AutoConfigurationPackage
和 @Import(AutoConfigurationImportSelector.class)
① @AutoConfigurationPackage:该注解上有一个 @Import(AutoConfigurationPackages.Registrar.class)
,其中Register 类的作用是将启动类所在包下的所有子包组件注入到spring容器中
② @Import(AutoConfigurationImportSelector.class):其中AutoConfigurationImportSelector
类中有一个getCandidateConfigurations()
方法,该方法通过SpringFactoriesLoader.loadFactoryNames()
方法查找位于META-INF/spring.factories
文件中的所有自动配置类,并加载这些类。
spring.factory 文件以 key-value 键值对的形式存储在文件里,其中一个key=EnableAutoConfiguration,对应的value就是一个个以AutoConfiguration结尾来命名的自动配置类
springboot在整个启动过程中,其实就是在类路径的META-INF/spring.factory 文件中找到EnableConfiguration对应的所有自动配置类,
然后将自动配置类加载到spring容器中。
2>自动配置生效
前面加载的所有配置类并不是都生效的,需要通过@ConditionOnxxx
注解实现,我们要使用那些类,就直接在springboot项目的pom.xml文件中导入相应的启动器(jar包)即可,这样springboot就会利用@ConditionOnxxx注解使我们需要的自动配置类生效,将该类注入到spring容器中,这样就完成了整个自动配置的过程。
3>获取属性值
在spring注入bean的过程中,所需要的属性值是通过xxxProperties的bean来获得的。在每一个xxxAutoConfiguration类上面都有一个@EnableConfigurationProperties(xxxProperties.class)
注解,表示开启配置属性,它的参数是以一个xxxProperties类。
在xxxProperties类上有@ConfigurationProperties(prefix="xxx")
,该注解作用是绑定配置文件(application.yml)属性到对应bean上。
<总结>
1.Springboot启动会加载大量的自动配置类
2.我们要先确认pom文件中引入的jar包有没有在springboot默认提供的自动配置类
3.有的话就不需要你配置对应的组件,没有的话需要手动配置组件,因为jar包只是个启动器
4.给容器中自动配置类添加组件时会从properties类中获取某些属性,我们只需要在yml配置文件中指定属性值即可
xxxxAutoConfigurartion负责把自动配置类添加到容器中,xxxxProperties封装了自动配置类的固有属性和方法,我们可以通过yml文件或properties文件设置属性值和获取方法
2、SpringAop有哪些通知类型,执行顺序?
- 前置通知(@Before)
- 返回通知(@AfterReturning)
- 异常通知(@AfterThrowing)
- 后置通知(@After)
- 环绕通知(@Around)[优先级最高]
基础相关
1、说说hashcode()和equals()之间的关系?
equals()
判断两个对象是否相等
hashcode()
获取hash码,确定该对象在hash表中的位置
1>不会创类对应的散列表: 不会在HashSet、Hashtable、HashMap等本质是散列表的数据结构中用到该类。比如普通类的quals()比较中,两个对象相等,hashcode也不一定相等。
2>会创建类对应的散列表: 会在HashSet、Hashtable、HashMap等本质是散列表的数据结构中用到该类。比如将对象加入到HashSet中,如果重写了equals()不重写hashcode()的话可能会出现重复数据,因为equals相等他们的hashcode不一定相等,但是hashSet底层是先判定hashcode是否相等再判断equals是否相等。如果出现hashcode碰撞,hashcode相等但是equals不相等,会将对象散列到其他位置
<总结>
1) 当我们向一个set、HashMap、HashSet、HashTable集合中添加某个元素,集合会首先调用该对象的hashCode方法,这样就可以直接定位它所存储的位置,若该处没有其他元素,则直接保存。若该处已经有元素存在,就调用equals方法来匹配这两个元素是否相同,相同则不存,不同则散列到其他位置
2) 对于List集合、数组而言,他就是一个累赘,不重要;但是对于HashMap、HashSet、HashTable而言,它变得异常重要
消息队列
1、对mq的理解及应用场景?
是一种应用程序之间的通信方法,通过读写出入队列的消息来通信。
1>mq的作用
- 解耦:订单系统调用库存系统,库存系统无法访问将导致订单失败,mq解耦之后直接订单系统处理之后就返回成功,待库存系统恢复之后订阅到消息在执行库存相关的操作
- 异步:将不必要的操作进行异步处理,提高处理时间。eg:用户注册成功之后发送短信和邮箱通知操作
- 消峰:高峰期的时候将请求写入mq,系统从mq拉取不超过能处理的请求数量
2>缺点 - 系统的可用性降低:如果mq挂点会导致整个系统崩溃
- 复杂性提高:解决消息重复消费、消息丢失、消息传递的顺序
- 一致性:异步操作中如果有系统写库失败就会产生数据不一致的情况