面试1-10
面试题
1.jdk、jre、jvm的区别
- JDK(Java SE Development Kit),Java标准开发包,他提供了编译、运行Java程序所需的各种工具和资源,包括Java编译器、Java运行时环境,以及常用的Java类库等
- JRE(Java Runtime Environment),Java运行环境,用于运行Java的字节码文件。JRE中包括了JVM以及JVM工作所需要的类库,普通用户只需要安装jre来运行Java程序,而程序开发者必须安装JDK来编译、调试程序。
- JVM(Java Virtual Mechinal),Java虚拟机,是JRE的一部分,它是整个Java实现跨平台的最核心部分,负责运行字节码文件。
我们写Java代码用txt就可以写,但是写出来的java代码想要运行,就需要先编译成字节码,那就需要编译器,而JDK中就包含了编译器javac,编译之后的字节码,想要运行就需要一个可以执行字节码的程序,这个程序就是JVM(Java虚拟机),专门用来执行Java字节码的。
如果我们想要开发Java程序,那就需要JDK,因为要编译Java源文件。
如果我们只想运行已经编译好的Java字节码,也就是*.class文件,那么就只需要JRE。
JKD中包含了JRE,JRE中包含了JVM
2.hashCode()与equals()之间的区别
在Java中,每个对象都可以调用自己的hashCode方法得到自己的哈希值(hashcode),就相当于对象的指纹信息,通常来说世界上没有完全相同的两个指纹,在Java中做不到这么绝对,但是我们仍然可以做一些提前判断:
- 如果两个对象的hashcode不相同,那么这两个对象肯定是不同的两个对象
- 如果两个对象的hashcode相同,不代表这两个对象一定是同一个对象,也可能是两个对象
- 如果两个对象相等,那么他们的hashcode就一定相等
在Java的一些集合类的实现中,在比较两个对象是否相等时,会根据上面的原则,会先调用对象的hashcode方法得到hashcode进行比较,如果hashcode不相同,就可以直接认为这两个对象不相同,如果hashcode相同,那么就会进一步调用equals方法进行比较。而equals方法,就是用来确定两个对象是不是相等的。通常equals方法的实现会比较重,逻辑比较多,而hashcode主要就是得到一个哈希值,实际上就是一个数据,相对而言比较轻,所以在比较两个对象是,通常都会先根据hashcode比较一下。
所以我们就需要注意,如果我们重写了equals方法,那么就要注意hashcode方法,一定要保证能遵守上述原则。
package com.cloudcore.view;
import java.util.Objects;
/**
* @Author: hepeng
* @Date: 2021/12/2 21:58
*/
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public boolean equals(Object o) {
User user = (User)o;
return user.getName().equals(this.name);
}
@Override
public int hashCode() {
return name.hashCode();
}
}
package com.cloudcore.view;
import java.util.HashMap;
/**
* @Author: hepeng
* @Date: 2021/12/2 22:02
*/
public class App {
public static void main(String[] args) {
HashMap<User,String> hashMap = new HashMap<>();
hashMap.put(new User("zhouyu"),"123");
System.out.println(hashMap.get(new User("zhouyu")));
}
}
程序运行结果:123
3.String、StringBuffer、StringBuilder之间的区别
String是常量,其值不可改变的量
StringBuffer是线程安全,可以修改的
append方法被synchronized修饰
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
StringBuilder和StringBuffer是差不多的,是线程不安全的
没有锁机制
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
代码演示
public static void main(String[] args) {
String s = "周瑜";
s = "ab";
System.out.println(s);
//ab
StringBuffer sb = new StringBuffer();
sb.append("a");
sb.append("b");
System.out.println(sb);
//ab
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("a");
stringBuilder.append("b");
System.out.println(stringBuilder);
//ab
}
- String是不可改变的,如果尝试去修改,会新生成一个字符串对象,StringBuffer和StringBuilder是可变的
- StringBuffer是线程安全的,StringBuilder是线程不安全的,所以在单线程环境下StringBuilder效率会更高
4.泛型中extend和extends的区别
- 表示包括T在内的任何T的子类
- 表示包括T在内的任何T的父类
定义泛型ZhouyuList
public class ZhouyuList<E> {
public void add(E e){
//..
}
}
比如我们想存 BigDecimal
public class Main {
public static void main(String[] args) {
ZhouyuList<BigDecimal> zhouyuList = new ZhouyuList<>();
zhouyuList.add(new BigDecimal(1));
}
}
第二种情况 ZhouyuList
public class ZhouyuList<E extends Number> {
public void add(E e){
//..
}
}
public class Main {
public static void main(String[] args) {
ZhouyuList zhouyuList = new ZhouyuList<>();
zhouyuList.add(new BigDecimal(1));
zhouyuList.add(new Integer(1));
zhouyuList.add(1);
}
}
这种情况只要是Number的子类就行
5.==和equals的区别
- == :如果是基本数据类型,比较的是值,如果是引用类型,比较的是引用地址
- equals:具体看各个类重写equals方法之后的比较逻辑,比如String类,虽然是引用类型,但是String类中重写了equals方法,方法内部比较的是字符串中的各个字符是否全部相等
public static void main(String[] args) {
String s = "123";
s.equals("ddd");
}
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
if (this == anObject) {
return true;
}
先比较是不是同一个引用地址,如果是同一个引用地址那肯定相等
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
如果不是,那就看传进来的是不是一个字符串,如果不是字符串就直接返回false
如果是的话,一个一个字符逐一比较,但凡有一个字符不相同就返回false,如果全部相等才返回true
6.ApplicationContext和BeanFactory有什么区别
BeanFactory是Spring中非常核心的组件,表示Bean工厂,可以生成Bean,维护Bean,而ApplicationContext继承了BeanFactory,所以ApplicationContext拥有BeanFactory的所有特点,也是一个Bean工厂,但是ApplicationContext除了继承BeanFactory之外,还继承了诸如EnviromentCapable、MessageSource、ApplicationEventPublisher等接口,从而ApplicationContext还拥有获取系统变量、国际化、事件发布等功能,这是BeanFactory所不具备的。
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver {}
其中 ListableBeanFactory HierarchicalBeanFactory 是继承了BeanFactory的
public interface ListableBeanFactory extends BeanFactory {}
public interface HierarchicalBeanFactory extends BeanFactory {}
ApplicationContext还有一些其他功能,因为他除开继承了BeanFactory之外,还继承了像其他EnvironmentCapable、MessageSource、ResourcePatternResolver等别的一些接口
EnvironmentCapable表示获取环境变量的功能,ApplicationContext还可以获取环境变量,获取操作系统的key-value,获取jvm的环境变量的key-value,包括获取properties环境的key-value等。这些是BeanFactory所不支持的,而ApplicationContext支持。
ApplicationContext还继承了MessageSource,支持和实现国际化
ApplicationContext还继承了ApplicationEventPublisher,支持事件发布
ApplicationContext还继承了ResourcePatternResolver,支持获取资源解析器
7.ArrayList和LinkedList区别
- 首先,他们的底层数据结构不同,Arraylist底层是基于数组实现的,LinkedList的底层是基于链表实现的
- 由于底层数据结构不同,他们所适用的场景也不同,ArrayList更适合随机查找,LinkedList更适合删除和添加,查询、添加、删除的时间复杂度不同
- 另外ArrayList和LinkedList都实现了List接口,但是LinkedList还额外实现了Deque接口,所以LinkedList还可以当做队列来使用。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{}
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{}
8.B树和B+树的区别,为什么Mysql要使用B+树
B树
定义:B树又称多路平衡查找树,B树中所有节点的孩子节点数的最大值称为B数的阶
一棵m阶B树或为空树,或为满足如下特性的m叉树:
-
树中每个节点至多有m棵子树(即至多含有m-1个关键字)
-
若根节点不是终端节点,则至少有两棵子树
-
除根节点外的所有非叶节点至少有[m/2]棵子树(即[m/2]-1个关键字)
-
非叶节点的结构:
-
所有的叶节点都出现在同一层次上,并不带任何信息
三阶B树
- 三阶B树,每个节点都至多有三棵子树,也至多有两个关键字
- 三阶B树,若根节点不是终端节点,至少有两棵子树,图中三阶B树有3棵子树
B树的特点:
- 节点排序
- 一个节点可以存储多个元素,多个元素间也进行排序
B+树的特点:
-
拥有B树的特点
-
叶子节点之间有指针
-
非叶子节点上的元素在叶子节点上都冗余了,也就是叶子节点中存储了所有的元素,并且排好顺序
B+树只在叶子节点上放数据,叶子节点还会有指针,相邻的叶子节点会用链表进行连接
Mysql使用B+树,因为索引是用来加快查询的,而B+树通过对数据进行排序是可以提高查询速度的,然后通过一个节点可以存储多个元素,从而使得B+树的高度不会太高,在Mysql的一个Innodb页就是一个B+树节点,一个Iinnodb页默认16kb,所以一般情况下一颗两层的B+树可以存储2000万行左右的数据,然后通过利用B+树叶子节点存储了所有数据并且进行了排序,并且叶子节点之间有指针,可以很好的支持全表扫描,范围查找等dql语句。
9.Mysql锁有哪些,如何理解
按锁粒度分类
1.行锁:锁某行数据,锁粒度最小,并发度高
2.表锁:锁整张表,锁粒度最大,并发度低
3.间隙锁:锁的是一个区间
还可以分为:
1.共享锁:也就是读锁,一个事务给某行数据加了读锁,其他事务也可以读,但是不能写
2.排它锁:也就是写锁,一个事务给某行数据加了写锁,其他事务不能读也不能写
还可以分为:
1.乐观锁:并不会真正去锁某行记录,而是通过一个版本号来实现
2.悲观锁:上面所描述的行锁,表锁都是悲观锁
10.CopyOnWriteArrayList的底层数据结构是怎样的
为什么存在,因为ArrayList是线程不安全的
1.首先CopyOnWriteArrayList的内部也是用数组来实现的,在向CopyOnWriteArrayList添加元素时,会复制一个新的数组,写操作在新数组上进行,读操作在原数组上进行
2.并且,写操作会加锁,防止出现并发写入丢失数据的问题
3.写操作结束之后会把原数组指向新数组
4.CopyOnWriteArrayList允许在写操作时来读取数据,大大提高了读的性能,因此适合读多写少的应用场景。但是CopyOnWriteArrayList会比较占用内存,同时可能读到的数据不是实时最新的数据,所以不适合实时性要求很高的场景。
public static void main(String[] args) {
CopyOnWriteArrayList list = new CopyOnWriteArrayList();
list.add("1");
list.add("2");
list.get(1);
}
两个线程来add,只有一个线程能加到锁,会把老数组复制一份,如果此时有一个线程进来读,那么读到的就是老数组里面的元素,暂时读不到新的,
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}

浙公网安备 33010602011771号