面试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中做不到这么绝对,但是我们仍然可以做一些提前判断:

  1. 如果两个对象的hashcode不相同,那么这两个对象肯定是不同的两个对象
  2. 如果两个对象的hashcode相同,不代表这两个对象一定是同一个对象,也可能是两个对象
  3. 如果两个对象相等,那么他们的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
}
  1. String是不可改变的,如果尝试去修改,会新生成一个字符串对象,StringBuffer和StringBuilder是可变的
  2. StringBuffer是线程安全的,StringBuilder是线程不安全的,所以在单线程环境下StringBuilder效率会更高

4.泛型中extend和extends的区别

  1. 表示包括T在内的任何T的子类
  2. 表示包括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 泛型里面必须是Number的子类才行

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区别

  1. 首先,他们的底层数据结构不同,Arraylist底层是基于数组实现的,LinkedList的底层是基于链表实现的
  2. 由于底层数据结构不同,他们所适用的场景也不同,ArrayList更适合随机查找,LinkedList更适合删除和添加,查询、添加、删除的时间复杂度不同
  3. 另外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叉树:

  1. 树中每个节点至多有m棵子树(即至多含有m-1个关键字)

  2. 若根节点不是终端节点,则至少有两棵子树

  3. 除根节点外的所有非叶节点至少有[m/2]棵子树(即[m/2]-1个关键字)

  4. 非叶节点的结构:

    image-20211205164947954

  5. 所有的叶节点都出现在同一层次上,并不带任何信息

三阶B树

image-20211205165148575

  1. 三阶B树,每个节点都至多有三棵子树,也至多有两个关键字
  2. 三阶B树,若根节点不是终端节点,至少有两棵子树,图中三阶B树有3棵子树

image-20211205165916042

image-20211205170523603

image-20211205170540602

B树的特点:

  1. 节点排序
  2. 一个节点可以存储多个元素,多个元素间也进行排序

B+树的特点:

  1. 拥有B树的特点

  2. 叶子节点之间有指针

  3. 非叶子节点上的元素在叶子节点上都冗余了,也就是叶子节点中存储了所有的元素,并且排好顺序

    B+树只在叶子节点上放数据,叶子节点还会有指针,相邻的叶子节点会用链表进行连接

    image-20211205213219969

    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();
    }
}
posted @ 2021-12-05 22:59  正在努力的澎澎  阅读(39)  评论(0)    收藏  举报