java面试题整理

基本类

Object类

wait() 和 notify的联系

  • wait()、notify/notifyAll() 方法是Object的本地final方法,无法被重写。
  • wait()使当前线程阻塞,前提是 必须先获得锁,一般配合synchronized 关键字使用,
  • 由于 wait()、notify/notifyAll() 在synchronized 代码块执行,说明当前线程一定是获取了锁的。
    • 当前线程执行wait() 方法时,会释放当前的锁,让出CPU ,进入等待状态。
    • notify或者notifyall 方法执行 只是唤醒沉睡的线程,不会立即释放锁,锁的释放要看具体情况、
  • wait方法需要被try catch 包围。
  • notify 和wait的顺序 不能错。 一定要先wait 才能notify 否者将不会唤醒线程。

hashCode() 和== 和equals的区别

  • hashCode 方法在java中用来比较两个对象是否相等。但是hashCode 并不是完全可靠,因为有不同的对象他们生成的hashcode也会一样(hash 冲突)

  • equals 方法 也是用来比较 两个对象是否相等。

    • equals相等的两个对象他们的hashCode一定相等,所以equals方法一定是可靠的。
    • hashCode相等的两个对象 他们的equals() 可能不相等。
    • hashCode 不相等的两个对象 他们的equals() 一定不相等
  • equlas() 方法是Object 的方法 内部实现是

    public boolean equals(Object obj) {
        return (this == obj);
    }
    

可以看到内部也是由== 实现的。但是最大的区别是可以自己根据场景重写equals方法。

  • == 是一个操作符,不可以重写。 = = 操作符是用来比较两个对象是否是同一个,也就是说引用地址是否相同。

  • 使用目的的不相同,==主要是用于基础类型的比较,基础类型只要值相等就是true,在比较引用类型的时候就是比较引用的地址。而equals方法则主要用于引用对象的比较,不重写的情况和= =一样

Object 类中有哪些方法

  • getClass() 方法 返回 一个对象的运行时类
  • hashCode() 方法 获取对象的哈希码值
  • equals() 方法 比较两个对象是否相等
  • clone() 方法 创建并返回此对象的一个副本
  • toString() 方法 输出对象的字符串表示
  • notify() 方法 唤醒一个等待线程
  • notifyAll()方法 唤醒所有等待线程
  • wait() 方法 是线程进入等待状态 释放CPU资源
  • finalize() 方法 被弃用了

Clone方法 深拷贝,浅拷贝

  • 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,不会影响另一个对象拷贝得到的数据。
  • 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
  • 一个类有一个对象,其成员变量中又有一个对象,该对象指向另一个对象,另一个对象又指向另一个对象,直到一个确定的实例。这就形成了对象图。那么,对于深拷贝来说,不仅要复制对象的所有基本数据类型的成员变量值,还要为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象图进行拷贝!
    简单地说,深拷贝对引用数据类型的成员变量的对象图中所有的对象都开辟了内存空间;而浅拷贝只是传递地址指向,新的对象并没有对引用数据类型创建内存空间。

Stirng 类

String类的不可变性解释

== 不可变类(immurable)满足以下条件 ==

  • 类添加final 修饰符,保证类不被继承
  • 保证所有成员变量必须私有,并且加上final 修饰符
  • 不提供改变成员变量的方法
  • 通过构造器初始化所有成员 进行深拷贝
    在这里插入图片描述
  • String 类被final 修饰 不可以被继承
  • String 内部所有成员都是私有的
  • 没有value 的setter方法
  • 当传入数组value的时候是进行copy
  • 获取value的时候不是直接返回对象的引用,而是对象的拷贝

new String(“xxx”) 在内存中的分配

String str = new String(“abc”) 执行的构造函数如下
在这里插入图片描述
在这里插入图片描述
new String(“abc” ) 是先在堆区分配对象内存 然后调用 构造函数将“abc".value的值赋给str对象 的value 他们是不同的对象
在这里插入图片描述
但是他们的value的值 都是指向相同的字符串常量 ”abc“.

总结:
new String(“abc”) 会首先在堆区给对象分配内存,然后在常量池里面存在有没有"abc"常量,如果没有创建”abc’常量 并且指向它。
String str1 = new String(“abc”) 和 String str2 = “abc" 的作用一致,但是因为str1 是同过new操作得到的对象 所以比较耗费内存,建议还是直接用str2的方式,给堆空间节省内存。

String StringBuilder StringBuffer的区别

  • String 是java 非常基础的类,提供了构造和管理字符串的各种基本逻辑,它是典型的immurable(不可变)类。它被final 修饰,所属性也都是final 的,由于它的不可变性,所以每次操作都会生成新的String对象。
  • StringBuffer 是一个字符串变量的类。用append 或者add把字符串添加到有序列的末尾或者指定位置,StringBuffer 是一个线程安全的类。方法用synchronized 关键字修饰。但是在保证了线程安全的情况下,也带来了格外的性能开销。
  • StraigBuilder 类是java1.5 中新增的类,在能力上和StringBuffer类没有本质上的区别,只是去掉了线程安全的部分,有效的减少了性能开销。如果没有线程安全的要求 ,推荐使用。

集合类

有哪些集合类

java 集合类都放在java.util 包中 集合类存放都是对象的引用而不是对象本身。
集合类主要有 set(集) 、list(列表)和 map(映射)
集合的接口分为: Collection 和Map 接口 而 list、set 实现了Collection接口

ArrayList LinkedList Vector 扩容方式 && 线程安全

  • ArrayList 默认容量是 10 当 add一个元素而数组满的情况触发扩容机制,扩容1.5倍,线程不安全
    在这里插入图片描述
  • LinkedList 由于底层数据结构是 链表 所以不需要扩容,线程不安全
  • Vector 默认扩容 1倍 ,由于它是线程安全的类 ,所以带来了格外的性能开销。一般没有线程安全要求不推荐使用。
    JAVA集合之Vector详解
    Java集合之ArrayList详解
    Java集合之LinkedList详解

Map类

HashMap类
jdk1.7和1.8HashMap的区别
  • 在1.7之前使用数组加链表,他是数据结点是一个Entry结点,是HashMap的一个内部类,而1.8是数组加链表加红黑树,并且数据结点改成了Node结点
  • 1.7之前是的数据插入是头插法,可能会在多线程调用时,扩容过程中可能在里面resize方法中又调用了一个transfer方法,把数据域进行了一个rehash,可能会造成链变成死链。
  • 1.8之后变成了尾插法。虽然不会出现了死链的情况,但是还是会有扩容数据丢失的情况
put()方法实现原理
  • 最开始会先判断 table是否为空,如果为空代表table 没有元素,进行第一次扩容(初始化)
  • 根绝寻址算法 (length -1) & hash(key) 得到一个索引,然后判断索引桶位是否有元素,如果有元素,就说明发生hash冲突,需要进行链化 或者当 链的长度大于8 && 数组的长度达到64 就进行树化。然后将key:value 插入到尾部
  • 其中还需要判断是否需要扩容,扩容阈值是 负载因子0.75* 默认容量16 = 12 。一次扩容两倍
get() 方法实现原理
  • 根据寻址算法得到索引桶位 ,判断是否有值和 key是否相等 key的hash 是否相等,如果不相等 就判断是否树化或者链化,然后根据红黑树或者链表进行查询key的value。
扩容方式
  • jdk 1.7版本因为entry结点是头插法,所以在多线程环境下 可能会出现死链现象,因为可能在resize方法中又调用了transfer方法,导致数据错误。
  • HashMap的扩容是在数组长度的四分之三的时候进行扩容,扩容阈值的计算方法是 负载因子0.75* 容量16 =12. 一次扩容两倍。例如16–>32
  • 扩容时将分成两个链,地位链的索引还是不变,高位链的索引 当前数组下标位置+ 扩容之前数组的长度.
线程安全性
  • HashMap 是线程不安全的容器,如果需要使用线程安全的map容器,可以使用 ConcurrentHashMap,HashTable,Collections.SynchronizedMap 类。

锁/同步

锁的种类
  • 公平锁/非公平锁
  • 可重入锁
  • 独享锁/共享锁
  • 互斥锁/读写锁
  • 乐观锁/悲观锁
  • 分段锁
  • 偏向锁/轻量级锁/重量级锁
  • 自旋锁
CAS
  • cas 是乐观锁的实现方式一种,比较和置换。
  • jdk中很多并发包都使用cas,比如AtomicLong ,AtomicInterge,Unsafe
偏向锁/轻量级锁/重量级锁
  • 偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。jvm默认设置偏向锁。
  • 轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
  • 重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。
synchronized
  • synchronized 是可重入锁。当一个线程拥有一个对象的锁的时候,再次遇到synchronized 锁住的同一个对象 ,是不会进入阻塞的,因为他在这个对象头里面的owner里面 并且没有释放锁
synchronized 修饰静态方法/静态变量/成员方法/成员变量的区别
  • 静态方法和静态变量都是 synchronized(当前类的class)
  • 成员方法和成员变量都是 synchronized(this)
volatile
  • volatile 关键字保证了 可见性。
  • 当线程读取一个被volatile关键字修饰的变量时,他不会从自己的缓存中读取,而是每次都去主存中读。这样会保证了在每次修改后都能获取到最新值。
  • valatile关键字 只能保证可见性和禁止指令重排。而不能保证线程安全,线程安全需要保证原子性。

ReentrantLock

ReentrantLock和synchronized的区别

相比于synchronized 它具备如下特点

  • 可中断
  • 可以设置超时时间
  • 可以设置公平锁( 防止锁饥饿)
  • 支持多个条件变量
  • 支持可重入
ReentranlLock公平锁/非公平锁
  • synchronized 是不公平锁,当持有者释放锁的时候,阻塞队列的线程会抢占式获得锁资源
  • ReentrantLock 锁 可以是公平的 也可以不公平

    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

死锁

形成的原因

*交叉闭环申请。即线程在获得了锁A并且没有释放的情况下去申请锁B,这时,另一个线程已经获得了锁B,在释放锁B之前又要先获得锁A,因此闭环发生,陷入死锁循环。

避免方式
  • 增加随机睡眠时间
  • 合理的安排线程执行顺序
wait() 和 sleep的区别
  • sleep 是Thead 的静态方法

  • wait 是Object对象的方法

  • sleep不需要强制和synchronized配合使用,但wait需要和synchronized 使用

  • sleep 不释放锁,wait 释放锁

  • 共同点 都会让线程进入阻塞

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值