• Java底层总结(未完善)


    Java相关

    1. concurrentHashMap原理是什么?怎么保证的并发安全?

    2. synchronized的实现是怎样的?synchronized有几种状态?synchronized怎么保证不会造成死锁?synchronized和lock的区别?

    synchronized引出:解决内存不可见问题(可见:一个线程对共享变量进行修改后,其他线程可以立刻看到)。可以通过synchronized解决,其实进入 synchronized 块就是把在 synchronized 块内使用到的变量从线程的本地内存中擦除,这样在 synchronized 块中再次使用到该变量就不能从本地内存中获取了,需要从主内存中获取,解决了内存不可见问题。

    volatile和synchronized:synchronized 关键字可以保证并发编程的三大特性:原子性、可见性、有序性,而 volatile 关键字只能保证可见性和有序性,不能保证原子性,也称为是轻量级的 synchronized。

    synchronized有四种状态
    四种状态引出为了减少获得锁和释放锁带来的性能消耗而引入的
    (1)无锁:无锁的特点是修改操作在循环内进行,线程会不断的尝试修改共享资源。若没有冲突就修改成功并退出,否则就会继续循环尝试。也就是CAS(CAS是基于无锁机制实现的 - CompareAndSwap ,也就是先比较再进行修改,修改失败,自旋)。
    (2)偏向锁:偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁,降低获取锁的代价。偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程不会主动释放偏向锁。
    (3)轻量级锁:当锁是偏向锁的时候,被另外的线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,从而提高性能。但是一直自旋会导致cpu消耗大。
    (4)重量级锁:当多个线程在等待线程时候,此时该锁会升级为重量级锁,会有线程阻塞,会带来巨大的性能消耗。

    3. 自动拆箱与装箱

    装箱:就是自动将基本数据类型转换为包装器类型(int–>Integer);调用方法:Integer的
    valueOf(int) 方法。Integer i = 10; 即自动装箱。
    拆箱:就是自动将包装器类型转换为基本数据类型(Integer–>int)。调用方法:Integer的
    intValue方法。
    注意:如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象

    4. HashMap和HashTable的区别

    • 两者父类不同HashMap是继承自AbstractMap类,而Hashtable是继承自Dictionary类。
    • 对外提供的接口不同HashtableHashMap多提供了elments() 和contains() 两个方法。 elments() 方法继承自
      Hashtable的父类Dictionnary。elements() 方法用于返回此Hashtable中的value的枚举。contains()方法判断该Hashtable是否包含传入的value。它的作用与containsValue()一致。事实上,contansValue() 就只是调用了一下contains() 方法。
    • 对null的支持不同Hashtable:key和value都不能为null。HashMap:key可以为null,但是这样的key只能有一个,因为必须保证key的唯一性;可以有多个key值对应的value为null。
    • 安全性不同HashMap是线程不安全的。Hashtable是线程安全的,它的每个方法上都有synchronized 关键字,因此可直接用于多线程中。虽然HashMap是线程不安全的,但是它的效率远远高于Hashtable。当需要多线程操作的时候可以使用线程安全的ConcurrentHashMap。ConcurrentHashMap也是线程安全的,而且它的效率比Hashtable要高好多倍。因为ConcurrentHashMap使用了分段锁,并不对整个数据进行锁定。
    • 初始容量大小和每次扩充容量大小不同
    • 计算hash值的方法不同

    5. List、Set、Map在存取元素的时候有什么区别?

    • List:List存放的元素有序,且可以存放相同的元素,可以根据下标来取值。
    • Set:Set存放的元素无序,且不可以存放相同的元素,只能通过迭代器或foreach循环遍历来取值。
    • Map:Map存放的是键值对元素,它的键是不可以重复的,一旦键值重复,其值将会被覆盖,它只能通过键的值来取值。

    6. int和Integer的区别,int a=1,Integer b=1; a==b是true还是false

    int和Integer区别

    • Integer实际是对象的引用,指向此new的Integer对象;int是直接存储数据值 ;
    • Integer的默认值是null;int的默认值是0。

    int a=1,Integer b=1; a==b是true还是false
    Integer与int比较时,Ingeger都会自动拆箱(jdk1.5以上)。故为true,两个Integer对象进行比较时候,如果数值在[-128,127]之间,且数值相同就返回true,两个是一个Integer对象,否则为false。

    7. String s = “abc” 和 String s = new String(“abc”)区别?

    String特性

    • String类是final的,不可被继承。
    • String类是的本质是字符数组char[], 并且其值不可改变。
    • Java运行时会维护一个String Pool(String池), String池用来存放运行时中产生的各种字符串,并且池中的字符串的内容不重复。

    String str=new String(“abc”); 这行代码创建了几个String对象? 答案:一个或两个。分析:

    • 首先在堆中(不是常量池)创建一个指定的对象"abc",并让str引用指向该对象
    • 在字符串常量池中查看,是否存在内容为"abc"字符串对象
    • 若存在,则将new出来的字符串对象与字符串常量池中的对象联系起来
    • 若不存在,则在字符串常量池中创建一个内容为"abc"的字符串对象,并将堆中的对象与之联系起来

    也可以想到 String str = "abc"创建了0个对象或者1个对象。**

    String s = “abc” 和 String s = new String(“abc”)区别?

    • String s = “abc”。s是存储在栈中。先在栈中创建一个对String类的对象引用变量s,然后查找栈中有没有存放"abc",如果没有,则将"abc"指向该字符串在常量池中的地址,如果没有,就在常量池中创建一个对象,并放入常量池中。例子如下:
      String s1 = “abc”;
      String s2 = “abc”;
      System.out.println(s1==s2); //true
    • String s = new String(“abc”)。每调用一次就会创建一个新的对象。 例子如下:
      String s1 =new String (“abc”);
      String s2 =new String (“abc”);
      System.out.println(s1==s2); // false
      说明s1和s2是不指向同一个对象的

    8. ArrayList和LinkedList的区别

    • ArrayList基于动态数组实现的非线程安全的集合;LinkedList基于双向链表实现的非线程安全的集合。
    • 对于随机index访问的get和set方法,一般ArrayList的速度要优于LinkedList。因为ArrayList直接通过数组下标直接找到元素;LinkedList要移动指针遍历每个元素直到找到为止。
    • 新增和删除元素,一般LinkedList的速度要优于ArrayList。因为ArrayList在新增和删除元素时,可能扩容和复制数组;LinkedList只需要修改指针即可。

    9. String长度有没有限制?

    Java中的UTF-8编码的Unicode字符串在常量池中以CONSTANT_Utf8类型表示。

    
    CONSTANT_Utf8_info {
     
         u1 tag;
     
         u2 length;
     
         u1 bytes[length];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    u2是u2是无符号的16位整数,因此理论上允许的的最大长度是2^16=65536。而 java class 文件是使用一种变体UTF-8格式来存放字符的,null 值使用两个 字节来表示,因此只剩下 65536- 2 = 65534个字节。

    10. 浅拷贝和深拷贝

    • 深拷贝:原对象和副本相同,之间不相互影响
    • 浅拷贝:创建一个新对象,然后将当前对象的非静态字段复制到该新对象,如果字段是值类型的,那么对该字段执行复制;如果该字段是引用类型的话,则复制引用但不复制引用的对象。因此,原始对象及其副本引用同一个对象

    Object 类提供的 clone 是只能实现 浅拷贝的。怎么实现深拷贝?

    • 让每个引用类型属性内部都重写clone() 方法 (待完善)

    • 利用序列化

    //深度拷贝
    public Object deepClone() throws Exception{
        // 序列化
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
    
        oos.writeObject(this);
    
        // 反序列化
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
    
        return ois.readObject();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    11. java设计模式,挑两个讲讲具体

    12. java内存管理怎么实现的

    • 什么是JVM?
      JVM(Java 虚拟机)是 Java Virtual Machine 的缩写,它是一个虚构出来的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的。
      JVM 有自己的硬件架构,如处理器、堆栈、寄存器等,还有对应分指令系统。
      假如一个程序使用的内存区域是一个货架,那 JVM 就相当于是一个淘宝店铺,它不是真实存在的货架,但它和真实货架一样可以上架和下架商品,而且上架的商品数量也是有限的。

    假如货架是在深圳,那 JVM 的平台无关性就相当于是客人可以在各个地方购买你在淘宝上发布的商品,不是只有在深圳才能购买货架上的商品。

    • Java内存模型
      Java 内存模型规定了所有的变量都存储在主内存(Main Memory)中,每条线程有自己的工作内存(Working Memory),线程的工作内存中保存了线程使用到的变量的内存副本。
      线程对变量副本的所有操作都必须在工作内存中进行,不能直接读写主内存中的变量。
      不同线程之间无法直接访问其他线程工作内存中的变量,线程间变量值的传递都要通过主内存来完成。在这里插入图片描述
      主内存与工作内存的交互操作
      Java 内存模型中定义了 8 种操作来完成主内存与工作内存之间具体的交互协议,虚拟机实现时必须保证每一种操作都是原子、不可再分的。
      这 8 种操作又可分为作用于主内存的和作用于工作内存的操作。
      作用于主内存的操作
      lock(锁定)
      作用于主内存的变量,它把一个变量标识为一条线程独占的状态。
      unlock(解锁)
      作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才能被其他线程锁定。
      read(读取)
      作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便 load 时使用。
      write(写入)
      作用于主内存的变量,它把 store 操作从工作内存中得到的变量值放入主内存的变量中。
      作用于工作内存的操作
      load(载入)
      作用于工作内存的变量,它把 read 操作从主内存中得到的变量值放入工作内存的变量副本中。
      use(使用 )
      作用于工作内存的变量,它把一个工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用的变量的值的字节码执行时会执行这个操作。
      assign(赋值)
      作用于工作内存的变量,它把一个执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码执行时执行这个操作。
      store(存储)
      作用于工作内存的变量,它把工作内存中的一个变量的值传送到主内存中,以便随后的 write 操作使用。

    • JVM 是怎么划分内存的?
      JVM 在执行 Java 程序的过程中会把它管理的内存分为若干个数据区域,而这些区域又可以分为线程私有的数据区域和线程共享的数据区域。
      线程私有数据区域:
      (1)程序计数器
      为了线程切换后能恢复到正确的执行位置,每条线程都有一个私有的程序计数器。
      程序计数器在 Java 虚拟机规范中没有规定任何 OOM 情况的区域。
      (2)虚拟机栈
      虚拟机栈描述的是 Java 方法执行的内存模型,每个方法在执行时都会创建一个栈帧(Stack Frame),栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
      一个方法从调用到执行完成的过程,对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
      与程序计数器一样,Java 虚拟机栈也是线程私有的,它的生命周期与线程相同。
      在 Java 虚拟机规范中,对虚拟机栈规定了下面两种异常。
      StackOverflowError
      当执行 Java 方法时会进行压栈的操作,在栈中会保存局部变量、操作数栈和方法出口等信息。
      JVM 规定了栈的最大深度,如果线程请求执行方法时栈的深度大于规定的深度,就会抛出栈溢出异常 StackOverflowError。
      OutOfMemoryError
      如果虚拟机在扩展时无法申请到足够的内存,就会抛出内存溢出异常 OutOfMemoryError。
      (3)本地方法栈
      本地方法栈(Native Method Stack)的作用与虚拟机栈非常相似,它有下面两个特点。

    • 本地方法栈与虚拟机栈的区别是虚拟机栈为 Java 方法服务,而本地方法栈为 Native 方法服务

    • 与虚拟机栈一样,本地方法栈也会抛出 StackOverflowError 和 OutOfMemoryError 异常。
      线程共享的数据区域:
      (1) Java 堆
      Java 堆(Java Heap)也就是实例堆,它用于存放我们创建的对象实例。特点以下:
      最大:对于大多数应用来说,Java 堆是 JVM 管理的内存中最大的一块内存区域。
      线程共享:Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创建。
      存放实例:堆的唯一作用就是存放对象实例,几乎所有的对象实例都是在这里分配内存。
      GC:堆是垃圾收集器管理的主要区域,所以有时也叫 GC 堆。
      (2)方法区
      方法区(Method Area)存储的是已被虚拟机加载的数据,它有下面几个特点:
      线程共享:方法区和堆一样,是所有线程共享的内存区域。
      存储的数据类型:类信息、常量、静态变量
      异常:方法区又可分为运行时常量池和直接内存两部分。
      运行时常量池:常量池用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。运行时常量池受到方法区内存的限制,当常量池无法再申请到内存时会抛出 OutOfMemoryError 异常。
      直接内存:直接内存不是从虚拟机运行时数据区的一部分。在 JDK 1.4 中新加入了 NIO(New Input/Output)类,引入了一种基于通道与缓冲区的 I/O 方式,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作,这样能避免在 Java 堆和 Native 堆中来回复制数据。

    13. 乐观锁和悲观锁?抢购场景用什么锁,为什么?

    15. AQS实现原理

    16. volatile了解吗,有什么作用?

    作用
    volatile是Java虚拟机提供的最轻量级的同步机制。

    volatile怎么保证的可见性?
    当volatile执行写操作后,JMM会把工作内存中的最新变量值强制刷新到主内存中
    并且写操作会让其他线程中的变量缓存无效化
    这样,其他线程使用缓存时,发现本地工作内存中此变量无效,便从主内存中获取,这样获取到的变量就是最新的值,实现了线程的可见性。

    17. Java的各种锁

    独占锁:JDK中的synchronized和java.util.concurrent(JUC)包中Lock的实现类就是独占锁。
    共享锁:共享锁是指锁可被多个线程所持有。如果一个线程对数据加上共享锁后,那么其他线程只能对数据再加共享锁,不能加独占锁。获得共享锁的线程只能读数据,不能修改数据。
    互斥锁:是独占锁的一种常规实现,是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。互斥锁一次只能一个线程拥有互斥锁,其他线程只有等待。
    读写锁:读写锁相比于互斥锁并发程度更高,每次只有一个写线程,但是同时可以有多个线程并发读。
    自旋锁:自旋锁的目的是为了减少线程被挂起的几率,因为线程的挂起和唤醒也都是耗资源的操作。如果锁被另一个线程占用的时间比较长,即使自旋了之后当前线程还是会被挂起,忙循环就会变成浪费系统资源的操作,反而降低了整体性能。因此自旋锁是不适应锁占用时间长的并发情况的。在 Java 中,AtomicInteger 类有自旋的操作。
    分段锁:分段锁设计目的是将锁的粒度进一步细化,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。在 Java 语言中 CurrentHashMap 底层就用了分段锁,使用Segment,就可以进行并发使用了。
    锁升级

    18. Java集合,常见实现类

    19. Set底层? Set怎么遍历?

    底层:创建Set集合底层其实创建了一个Map集合。
    HashSet的底层是HashMap,添加元素时候,就把该值当作key,value为Object对象,实现了单列数据存储。
    treeSet的底层其实就是一个TreeMap
    linkedHashSet底层LinkedHashMap
    遍历:for循环、iterator。

    20. 多线程操作Set,线程安全怎么实现

    方案一:和list一样,使用Colletcions这个工具类syn方法类创建个线程安全的set.
    Set synSet = Collections.synchronizedSet(new HashSet<>());
    方案二:使用JUC包里面的CopyOnWriteArraySet
    Set copySet = new CopyOnWriteArraySet<>();

    21. set和list区别,使用场景

    区别

    • List中元素有放入顺序,元素可重复 ,Set中元素无放入顺序,元素不可重复。
    • Set删除和插入效率高,插入和删除不会引起元素位置改变。 List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。
    • List可以通过迭代器和for循环,Set只能通过迭代器

    22. arraylist动态扩容

    23. arraylist的addAll方法,如果容量为1,addAll一个容量为100000的数组,怎么扩容?(addAll底层)

    24. Hash因子和Hash冲突

    25. 常见的哈希函数

    26. HashMap存储原理、转红黑树原因(为什么不全部用红黑树、尾插头插的区别、尾插的优势)

    27. HashMap扩容

    28. Java的特性

    29. Default修饰符和Proteted修饰符区别

    30. 怎么设计一个哈希函数来处理诸如字符串这样的输入把他们转化为哈希值呢

    31. JVM 双亲委派机制?如何打破双亲委派机制?

    32. Java垃圾回收算法?cms为什么要两次标记呢?

    33. LinkedHashMap和HashMap有什么区别?

    34. 线程安全了解吗,(线程安全的容器原理),创建线程的方式?

    35. 线程池参数以及作用

    36. 怎么样能使线程进入阻塞状态

    37. wait()和sleep()的区别

    38. 类的加载的过程?

    39. int占多少字节,最大为多少?

    40. 来11个任务,核心线程数只有10,怎么办,再来几个任务怎么办 ?

    41.

    Spring相关

    1. spring事务失效的几种场景

    2. spring事务的传播机制

    3. A依赖B,B依赖C,C依赖A,Spring怎么解决这种情况?

    4. AOP原理?jdk和cglib实现的区别?

    5. IOC原理

    6. Spring 动态代理的实现方式

    7. 谈一下对注解的理解?注解起到了什么作用?在Spring中Component注解,容器怎么知道这是应该Component?怎么定位到这个类而不是其他的类?

    8. Spring怎么样解决循环依赖

    9. Spring的bean初始化原理

  • 相关阅读:
    CentOS7中安装MYSQL5.7数据库
    Java开发面试--RabbitMQ专区
    运算放大器的电路模型
    小白也可以开发闲鱼自动化发布工具!!!
    40个高质量ssm+vue毕设项目分享【源码+论文】(一)
    vue3生命周期源码详解
    健康中国·营养先行|2022主动健康与临床营养高峰论坛圆满落幕
    【微服务|Nacos】Nacos实现多环境和多租户的数据隔离
    neo4j迁移数据从4.4版本到5.17版本
    Nginx【1】浅聊代理
  • 原文地址:https://blog.csdn.net/qq_61465103/article/details/132987472