java基础知识

28 阅读12分钟
  1. 聊一聊 java中的编译器和解释器? Java源代码---->编译器---->jvm可执行的Java字节码(即虚拟指令)---->jvm---->jvm中解释器----->机器可执行的二进制机器码---->程序运行。

  2. java数据类型

    整数类型(byte,short,int,long)

    浮点类型(float,double)

    字符型(char)

    布尔型(boolean)

  3. switch 是否能作用在 byte 上,是否能作用在 long 上,是否 能作用在 String 上? 在 Java 5 以前,switch(expr)中,expr 只能是 byte、short、char、int。从 Java5 开始,Java 中引入了枚举类型,expr 也可以是 enum 类型,从 Java 7 开始,expr 还可以是字符串(String),但是长整型(long)在目前所有的版 本中都是不可以的

  4. 用最有效率的方法计算 2 22 = 8?为什么? 2<<3 数据是以二进制的形式存储和处理的。位运算是直接对二进制位进行操作,乘法运算通常会涉及到更复杂的硬件电路和算法,需要多个时钟周期来完成。而位运算只需要简单的移位操作,在硬件层面可以更快地执行,因此在性能上更具优势.

  5. 访问修饰符 public,private,protected,以及不写(默认)时的 区别? private 当前类 default 同包 protected 同包子类 public 都可以

  6. final 有什么用?

    被final修饰的类不可以被继承 被final修饰的方法不可以被重写 被final修饰的变量不可以被改变

  7. 成员变量与局部变量的区别有哪些?

    1. 作用域,成员变量:针对整个类有效。局部变量:只在某个范围内有效。
    2. 存储位置,成员变量:随着对象的创建而存在,随着对象的消失而消失,存储在堆内存中。局部变量:在方法被调用,或者语句被执行的时候存在,存储在栈内存中。当方法调用完,或者语句结束后,就自动释放。
    3. 生命周期,成员变量:随着对象的创建而存在,随着对象的消失而消失。局部变量:当方法调用完,或者语句结束后,就自动释放。
    4. 初始值,成员变量:有默认初始值。局部变量:没有默认初始值,使用前必须赋值。
  8. 若一个类没有声明构造方法, 程序能正确执行吗?为什么?
    可以执行,默认会有无参构造方法的

  9. == 和 equals 的区别是什么?

    1. 基本数据类型==比较的是值,引用数据类型 == 比较的是内存地址
    2. 类没有覆盖equals() 方法。则通过 equals() 比较该类的两个对象时, 等价于通过“==”比较这两个对象,也就是比较地址,如果重写了 equal比较的是对象里的数据
  10. hashCode 与 equals (重要) hashcode相等对象不一定相等,equals相等,一定相等,因为 hashset,我们是根据hashcode快速定位到索引,然后根据 equals去比较数据,提高效率

  11. 当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是 引用传递?

    1. 基本数据类型,一个方法不能修改一个基本数据类型的参数

    2. 引用作为参数就不一样,对象引用及其他的拷贝同时 引用同一个对象。

  12. 字符串常量池?在 jvm哪个位置?

    1. new好的对象不会放在常量池。因为字符串在开发过程经常用到,为了提升性能和减少内存消耗,而设计的一个特殊内存区域
    2. 存储位置和版本变化,1.7 及前是方法去的永久带,1.8 元空间(堆)
  13. String s = new String("aaa")创建了几个对象? 1 个或者 2 个,为什么?就是在于常量池是否有对象,有就只在堆创建对象,如果没有,在常量迟创建对象

  14. String 可变吗? 不可变,被 final修饰了,

  15. String s1 = "aa"; String s2 = "bb"; String s3 = s1 + s2;

    • 常量池原本没有 "aa" 和 "bb" 的情况:总共创建了 4 个对象,分别是常量池中的 "aa""bb" 对象,堆中的 StringBuilder 对象以及拼接结果 "aabb" 的 String 对象。
  16. 在使用 HashMap 的时候,用 String 做 key 有什么好处? 首先是因为 String被 final修饰,不可变。 String 类是不可变的,一旦创建,其值不能被修改,哈希值缓存String 类会缓存其哈希值。当第一次调用 String 对象的 hashCode() 方法时,计算得到的哈希值会被存储起来。后续再次调用该方法时,直接返回缓存的哈希值,避免了重复计算,提高了 HashMap 操作的效率

  17. 简单说说自动装箱与拆箱? 装箱 int-》integer 拆箱integer->int Integer a= 127 与 Integer b = 127相等吗 对于对象引用类型:==比较的是对象的内存地址。 对于基本数据类型:==比较的是值。如果整型字面量的值在-128到127之间,那么自动装箱时不会new 新的Integer 对象,而是直接引用常量池中的Integer对象,超过范围 a1==b1的结果是false

  18. ArrayList 和 LinkedList 的区别是什么? list从数据结构是数组,数组就有索引,查找比较快,但是新增比较慢,LinkedList用的是链表,查询慢,插入快,只需要指向头尾指针,但是内存占用比较大,首位引用

  19. ArrayList的扩容机制? 如果没有指定大小,默认为 10,当容量不足是,>=默认的个数的时候就开始扩容,也就是添加地 11 个的时候开始扩容,按照 1.5 倍开始扩容,复制数组,原来的数组释放,添加元素,添加到末尾。 如何优化数组? 减少扩容,根据自己的业务设置相对合理的默认大小

  20. for循环和 迭代器区别? for 循环适用于简单的正向或反向遍历,尤其是在性能要求较高的场景;而 ListIterator 则适用于需要双向遍历和在遍历过程中修改列表结构的场景。 当调用 list.remove(fruit) 时,集合的 modCount 会增加,但迭代器的 expectedModCount 不会更新。当再次调用迭代器的 next() 方法时,就会发现 expectedModCount 和 modCount 不相等,从而抛出 ConcurrentModificationException 异常。

解决方法

如果需要在遍历过程中修改列表结构,可以使用迭代器本身的 remove() 方法,因为该方法会同时更新 expectedModCount 和 modCount,保证两者的一致性。 21. transient什么时候使用?作用是什么? 不希望该字段被序列化和反序列化,也是对数据的保护,不希望该数据存储到文件和网络传输,还有就是如果这个字段占用资源很大,在序列化的时候会占用很大资源 22. HashSet如何检查重复?HashSet是如何保证数据不可重复的? 首先 hashSet底层就是 map,其实就是现比较 hashCode相同,那就比较 equals,然后 put(值,present) 23. 说一下 HashMap 的实现原理? 当调用 put(key, value) 方法插入键值对时,主要步骤如下:

  1. 计算键的哈希值:通过 hash() 方法计算键的哈希值。

  2. 定位数组位置:根据哈希值和数组长度,通过 (n - 1) & hash 计算出键值对应该存储的数组位置。

  3. 检查数组位置:

    • 如果该位置为空,直接创建一个新的节点放入该位置。
    • 如果该位置已经有节点:
      • 首先判断第一个节点的键是否与要插入的键相等,如果相等则直接覆盖其值。
      • 如果不相等,判断该节点是链表节点还是红黑树节点。
        • 如果是链表节点,遍历链表,查找是否存在相同的键,如果存在则覆盖其值;如果不存在,则将新节点插入到链表尾部。当链表长度达到 8 且数组长度达到 64 时,将链表转换为红黑树。
        • 如果是红黑树节点,调用红黑树的插入方法插入新节点。 检查是否需要扩容:插入新节点后,如果 size 超过 threshold,则进行扩容操作。
  4. 为什么 hashmap使用尾插法,不用头插法 头插法在多线程的时候容易形成链表环路,因为在扩容的时候,A-》B->C 会变成 C->B->A 顺序已经变了,a线程认为是C->B->A,B线程认为是 A->B->C
    juejin.cn/post/723600…

  5. hashmap什么时候开始扩容? 有个公式,mashmap默认是 16*负载因子是 0.75 也就是 12 当put第 13 个的时候就开始扩容

  6. h = key.hashCode()) ^ (h >>> 16) 为啥要? 把键的原始哈希码和其右移 16 位后的结果进行异或(^)运算,得到一个新的哈希值,目的就是为了减少hash碰撞,

  7. 简单总结一下HashMap是使用了哪些方法来有效解决哈希冲突的:

    1. 使用链地址法(使用散列表)来链接拥有相同hash值的数据;
    2. 使用2次扰动函数(hash函数)来降低哈希冲突的概率,使得数据分布更平均;
    3. 引入红黑树进一步降低遍历的时间复杂度,使得遍历更快
  8. 简单说一说红黑树? blog.csdn.net/weixin_4478…

  9. 平衡二叉树

    1. 每个节点最多只有两个子节点
    2. 每个节点的值比它的左子树所有的节点大,比它的右子树所有节点小
    3. 每个节点左子树的高度与右子树高度之差的绝对值不超过1 (左左:右旋解决
      左右:先左旋再右旋
      右右:左旋解决
      右左:先右旋再左旋)
  10. 红黑树规则

    1. 每个节点不是黑色就是红色
    2. 根节点为黑色
    3. 红色节点的父节点和子节点不能为红色
    4. 所有的叶子节点都是黑色(如果一个节点是红色的,则它的两个子节点都是黑色的,下面都有隐藏的空的黑色叶子节点(NIL 节点)
    5. 每个节点到叶子节点的每个路径黑色节点的个数都相等 先变色,在旋转 自己理解就是新增的时候就是红色,然后是否变色,需要先将所有如果子节点是空就假装补成黑色,然后计算数目然后旋转
  11. 谈一谈ConcurrentHashMap 在 1.7 和 1.8?

    1. 1.7 使用的是分段锁,每个断就是每个**Segment**,**继承ReentrantLock锁**ConcurrentHashMap底层是由两层嵌套数组来实现的,-   ConcurrentHashMap对象中有一个属性segments, 类型为Segment[]; Segement对象中有一个属性table,类型为HashEntry.当调用ConcurrentHashMap的put方法时,先根据key计算出对应的Segment[]的数组下表j。确定好当前key,value应该插入到哪个Segment对象中,如果Segments[j]数组为空,则利用自旋锁方式在j位置生成一个Segment对象。
      
    2. 1,8 使用的cas和synchronized关键字,数组+链表+红黑树,
      

之后调用Segment对象的put方法。Segment对象的put方法会先加锁,然后根据key计算出对应的hashEntry[]的数组下表i,然后将key和value封装为HashEntry对象放入该位置,此过程和JDK7中的HashMap的put方法一样,然后解锁。

  1. hashmap 1.7用的分段锁 1.8 后续改为 cas+sync 为什么?
    1. 减少锁的粒度和开销- Java 1.7 分段锁:在 Java 1.7 里,HashMap 采用分段锁(Segment)机制,将整个数据结构分成多个段(Segment),每个段相当于一个小的 HashMap,不同的段可以被不同的线程同时访问,以此来提高并发性能。然而,这种方式需要额外维护多个 Segment 对象,并且在计算分段时也有一定的开销。Java 1.8 CAS + synchronized:Java 1.8 去掉了分段锁,采用 CAS + synchronized 机制。当多个线程对不同的桶进行操作时,不会产生锁竞争,因为每个桶可以独立进行操作。并且,synchronized 在 JDK 1.6 之后进行了大量优化,引入了偏向锁、轻量级锁等,在锁竞争不激烈时,加锁和解锁的成本较低。CAS 操作在无锁状态下可以高效地完成一些更新操作,避免了锁的使用,进一步提高了性能。
    2. 数据结构不一样,一个是数组+链表,一个是数组+链表+红黑树
  2. Collection 和 Collections 有什么区别? Collection是一个集合接口,顶级接口,set和 list都继承于它,Collections只是个工具类,提供了一些类似排序
  3. 双亲委派模型简单说一下?
    • 启动类加载器(Bootstrap ClassLoader) :最顶层的类加载器,由 C++ 实现,负责加载 Java 的核心类库,如 java.lang 包下的类。
  • 扩展类加载器(Extension ClassLoader) :由 Java 代码实现,负责加载 Java 的扩展类库,通常是 jre/lib/ext 目录下的类。
  • 应用程序类加载器(Application ClassLoader) :也由 Java 代码实现,负责加载用户类路径(classpath)上的类,一般来说,用户自己编写的类都是由该类加载器加载。
  • 自定义类加载器:用户可以根据需求自定义类加载器,继承自 java.lang.ClassLoader 类。
  1. 简单说一说异常的分类,结构? Throwable,有两个重要的子类:Exception(异常) 和 Error(错误) Error这些问题一般是由系统本身或底层资源耗尽等原因导致的,程序通常无法通过代码来处理这些错误,#### OutOfMemoryError,#### StackOverflowError exception 类表示程序可以捕获和处理的异常情况