java基础面试 50道
==和equals的区别
==: 对比的是栈中的值,基本数据类型是变量值,引用类型是堆中内存对象地址
equals:object中默认采用==比较,通常会重写equals方法
Object中的equals方法
public boolean equals(Object obj) {
return (this == obj);
}
String类重写的equals方法
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;
}
以下例子说明二者的区别
public static void main(String[] args) {
String s1 = "hello";
String s2 = new String("hello");
String s3 = s2; //引用传递
// s1 是在堆中的常量池中分配内存,s2是在堆中分配内存,s3赋的值是s2的引用地址
// == 对比的是栈中的内存地址,s1和s2的内存地址不是同一个地址,所以为false
System.out.println(s1 == s2); // false
System.out.println(s1 == s3); // false
System.out.println(s2 == s3); // true
// equals对比的是两个值的内容。所以结果都为true
System.out.println(s1.equals(s2)); // true
System.out.println(s1 .equals(s3)); // true
System.out.println(s2 .equals(s3)); // true
}
final的作用及用法
修饰类:表示类不可以被继承
修饰方法:表示方法不可以被重写,但可以重载
修饰变量:表示变量一旦赋值,不可以被修改
-
修饰成员变量
修饰类变量:必须在声明该变量值时指定初始值或者在静态代码块中指定初始值
修饰局部变量:可以在非静态初始化块声明该变量或在构造器中执行初始值
-
修饰局部变量:系统不会为局部变量初始化,需要由程序员显式初始化。所以局部变量既可以在声明时赋值,也可以不赋值,如果声明时不赋值,则在使用时必须先初始化该变量值
-
修饰基本类型和引用类型的区别:
如果修饰基本类型,则变量赋值后不允许修改。
如果修饰引用类型。则变量初始化后,不能再指向另一个对象的引用,但引用的值是可以改变的
String ,StringBuffer, StringBuilder三者之间的区别
String是final修饰的,不可变,每次操作都会产生新的String对象,将占用更多内存
StringBuffer和StringBuilder都是操作原对象
StringBuffer内部实现方法均有synchronized 修饰,所以是线程安全的
StringBuilder是非线程安全的
在性能上来说 StringBuilder >StringBuffer >String
使用场景:当字符串需要频繁变更时优先使用StringBuilder ,在多线程使用共享变量时使用StringBuffer
List和Set的区别
List: 按对象进入的顺序保存对象,可重复,允许多个null元素,可以使用iterator取出所有元素,逐一遍历,也可以使用get(i) 取出指定下标的元素
Set: 无序,不可重复,最多允许有一个null元素,取元素时只能使用iterator取出所有元素,逐一遍历
ArrayList和LinkedList的区别
ArrayList 基于动态数组,连续内存存储,适合下标访问(随机访问),尾部插入、删除性能较好,其他部分插入、删除都会移动数据,因此性能会低
扩容:ArrayList在使用new创建之后,并不会立即开辟数组空间,而是使用空数组代替。只有当第一个元素被加入的时候,才会真正的开辟数组空间,空间默认大小是10.也就是说,ArrayList是一个懒开辟。在加入元素时,如果size+1大于数组长度,就需要进行扩容了
扩容的机制:
ArrayList() 无参构造默认使用长度为0的数组
ArrayList(int initialCapacity) 使用指定容量的数组
ArrayList(Collection<? extends E> c) 使用 c的大小为数组容量
add(E e); 首次扩容为10 ,再次扩容大约为上次容量的1.5倍,以下为计算公式:
newCapacity=oldCapacity + oldCapacity>>1 新数组容量= 旧数组容量+旧数组容量右移一位,大约为50%
addAll(Collection<? extends E> c); 当集合没有元素时,扩容为Math.max(10,实际元素个数),当集合有元素时,扩容为 Math.max(原容量的1.5倍,实际元素个数)
list.addAll() 遵循一个规律:
list下次扩容的大小与当前元素个数两者之间找一个较大值
List<Integer> list = new ArrayList<>(); //当调用add方法时,List默认开辟一个容量为10的数组
list.addAll(Arrays.asList(1,2,3,4,5,6,7,8,9,10,11)); //list下次扩容的大小与当前元素个数两者之间找一个较大值
扩容结果为 11
List<Integer> list = new ArrayList<>(); //当调用add方法时,List默认开辟一个容量为10的数组
for (int i = 0; i<10; i++) {
list.add(i);
}
list.addAll(Arrays.asList(1,2,3)); // 先给list增加10个元素,再次触发扩容
//扩容结果 15
list.addAll(Arrays.asList(1,2,3,4,5,6));
//扩容结果 16
LinkedList 基于链表,可以存储在分散的内存中,适合做插入及删除操作,缺点:随机访问慢,占用内存多
HashMap面试
底层数据结构:1.7版本为 数组+链表 ,1.8版本为数组+链表 || 红黑树
为何要使用红黑树,为何一开始不树化,树化阈值为何是8,何时退化为链表?
红黑树本身是为了避免DOS攻击的,防止链表超长,造成性能下降,树化应当是偶然情况。
正常情况下,链表长度不会超过8,除非遇到系统被恶意攻击,构造大量相同hash值
红黑树占用的空间比链表要大很多,如非必要,尽量使用链表
如果hash值足够随机,在负载因子0.75的情况下,链表长度超过8的几率仅为 亿分之6 ,选择8就是为了树化几率足够小
树化为红黑树需满足以下条件:
数组容量大于64并且链表长度大于8
退化情况1:在扩容的时,如果拆分树时,树元素<=6 则退化为链表
退化情况2:remove树节点时root,root.left,root.right,root.left.left有一个为null,也会退化为链表
加载因子为何是0.75f?
-
在空间占用和查询时间之间取得较好的权衡
-
大于0.75,空间节省了,但会造成链表过长,影响性能
-
小于1.75,冲突减少了,但扩容会更频繁,占用更多空间
hashMap的key能否为null ,作为key的对象有什么要求?
-
hashMap的key可以为null,Map的其他实现则不行,例如:treeMap,hashTable, CurrentHashMap
-
作为key的对象,必须重写hashcode和equals方法,并且key的内容不能修改。
重写hashcode是为了让map的key具有更好的分布性。重写equals是为了计算,当两个对象的索引相同的情况下,比较两个值的内容是否相同。如果hashcode相同,不一定equals,但是两个对象如果equals,则hashcode肯定相同
创建单例模式的几种方式!
/**
* 饿汉式单例
*/
public class Singleton1 implements Serializable {
//1. 构造私有
private Singleton1() {
// 预防反射破坏单例
if (INSTANCE != null){
throw new RuntimeException("不能重复创建单例");
}
System.out.println("私有构造");
}
//2. 静态成员变量类型为当前类本身,new 的值为当前私有构造创建的唯一实例
private static final Singleton1 INSTANCE = new Singleton1();
//3. 静态方法返回当前实例
public static Singleton1 getInstance() {
return INSTANCE;
}
// 预防实现Serializable 后反序列化破坏单例
public Object readResolve(){
return INSTANCE;
}
}
/**
* 枚举饿汉式单例
*/
enum Singleton2{
INSTANCE;
//以下代码为测试使用
Singleton2(){
System.out.println("私有构造");
}
public static Singleton2 getInstance() {
return INSTANCE;
}
@Override
public String toString() {
return getClass().getName()+"@" +Integer.toHexString(hashCode());
}
}
/**
* 懒汉式单例
*/
class Singleton3{
private Singleton3(){
System.out.println("私有构造");
}
private static Singleton3 INSTANCE = null;
/**
* synchronized 解决多线程调用时,创建的对象不唯一问题
*/
public static synchronized Singleton3 getInstance() {
if (INSTANCE == null){
INSTANCE = new Singleton3();
}
return INSTANCE;
}
}
/**
* 懒汉式单例 -DCL
*/
class Singleton4{
private Singleton4(){
System.out.println("私有构造");
}
/**
* 使用双检索创建静态变量时必须加 volatile 修饰
* 解决共享变量的可见性,有序性,不能保证原子性
*/
private static volatile Singleton4 INSTANCE = null;
/**
* DCL 双检锁懒汉式单例
* 解决多线程调用时,创建的对象不唯一问题
*/
public static Singleton4 getInstance() {
if (INSTANCE == null){
synchronized(Singleton4.class){
if (INSTANCE == null){
INSTANCE = new Singleton4();
}
}
}
return INSTANCE;
}
}
/**
* 内部类懒汉式单例 --推荐此方式创建
* 优点:既满足了懒汉式特性,又解决了线程安全的问题
*/
class Singleton5{
private Singleton5(){
System.out.println("私有构造");
}
private static class Holder{
static Singleton5 INSTANCE = new Singleton5();
}
public static Singleton5 getInstance(){
return Holder.INSTANCE;
}
}
JDK中单例模式的应用:
-
Runtime 饿汉式单例实现
-
System中Console 使用双检锁懒汉式单例
-
Collections 中使用大量的单例模式
对象引用类型分为哪几类?
-
强引用 : 普通变量赋值即为强引用 例如:User u = new User();
通过 GC Root 的引用链如果强引用不到该对象,该对象才能被回收
-
软引用(SoftReference)
① 例如:SoftReference a = new SoftReference(new A());
② 如果仅有软引用该对象时,首次垃圾回收不会回收该对象,如果内存 GC仍不足,再次回收时才会释放对象
③ 软引用自身需要配合引用队列来释放
④ 典型例子是反射数据
-
弱引用(WeakReference)
① 例如:WeakReference a = new WeakReference(new A());
②如果仅有弱引用引用该对象时,只要发生垃圾回收,就会释放该对象
③ 弱引用自身需要配合引用队列来释放
④ 典型例子是 ThreadLocalMap 中的 Entry 对象
-
虚引用(PhantomReference)
① 例如:PhantomReference a = new PhantomReference(new A());
②必须配合引用队列一起使用,当虚引用引用的对象被回收时会将虚引用对象入队,由 Reference Handler 线程释放其关联的外部资源
③ 典型例子是 Cleaner 释放 DirectByteBuffer 占用的直接内存
本文链接:https://www.518wz.top/post/20.html 转载需授权!