大家好我是Java小羽,最近临近毕业,在苦逼的找工作,所以总结了一下Java的基础面试题,希望可以帮到大家。
1.面向对象
什么是面向对象?
其实是一种优良的设计设计模式,更多的是以人类的思维来理解计算机的编程逻辑。
面向对象三大特性
封装:封装的意义在于,使用者无需关心内部实现细节,只需要关心如何使用外部调用。
最常用的例子就是Java实体类,属性私有,通过set方法赋值,get方法取值。
继承:就是继承父类的方法,并做自己的改变或拓展。子类继承父类的时候,子类可直接调用父类的方法和属性,只需要定义自己的拓展类就可以了。
多态:多态字面意思就是多种形态,是指对象所属的类型不同,外部对同一个方法的调用,实际上锁执行的逻辑不同。
最常见的就是父类引用指向子类对象。
//Father 父类
//Son 子类
Father father=new Son();
编译时表现的形态是子类,运行时表现的形态是父类。实际上这个例子运行时调用的是父类的方法。
我们再通过一个例子深入理解一下多态:
package Demo;
/**
* @author shenyi
* @data 2022/9/12
* @apiNote
*/
public class oop {
public void faction1(){
System.out.println("aaa");
}
public void faction(){
System.out.println("111");
}
public static void main(String[] args) {
oop o=new child();
o.faction1(); //实际上调用的是子类重写的faction1方法 输出bbb。
o.faction(); //可以调用父类中的方法,无法调用子类中特有的方法。
}
}
class child extends oop{
@Override
public void faction1(){
System.out.println("bbb");
}
public void faction2(){
System.out.println("ccc");
}
}
多态总结:
-
可以使用父类引用指向子类对象(向上转型)
-
该引用只能调用父类已经存在的方法,无法调用子类特有的方法
-
当继承的子类重写父类的方法时,调用的方法其实是子类重写过的方法。
-
变量是不能被重写的,重写只是针对方法,重写变量会编译报错。
2.JDK、JRE、JVM三者区别
JDK:jdk其实就是Java的开发工具。
JRE:jre是Java程序运行时的环境。
JVM:jvm是Java虚拟机,也就是java跨平台的关键。
三者关系是 JDK包含JRE,JRE包含JVM
Java程序在系统中的运行流程:

3.==和equals比较
== :对于基本数据类型来说是比较的两个数据的值,也就是对比栈中的数据,对于引用数据类型来说对比的是堆中存放数据的地址。
equals:在object中默认也是采用==号进行比较,通常会重写。重写之后的equals比较的是两个变量的值。
String str1 "Hello";
String str2 new string("Hello");
String str3=str2;//引用传递
System.out.println(strl ==str2);//false
System.out.println(str1 ==str3);//false
System.out.println(str2 ==str3);//true
System.out.println(str1.equals(str2));//true
System.out.println(str1.equals(str3));//true
System.out.println(str2.equals(str3));//true
4.hashCode与equals
什么是hashCode?
hashCode()其实就是获取哈希码,也叫做散列码;它其实返回的是一个int的整数。这个哈希码的作用是确定该对象的在哈希表中的位置,简单的来说,就是根据“key”(hashCode()的返回值)快速检索出对应的“value”(存放对象的地址),以此来快速找到所需要的对象。
为什么要有hashCode呢?
因为通过hashcode()可以快速的找到所需要的对象。
举个例子:
HashSet如何检查重复?
HashSet的特性是不能存放相同的值,且无序。
-
当对象加入hashSet时,首先通过hashCode来确定对象存储的位置
-
当hashCode存在冲突时,先判断当前位置是否有值
-
若没有值则重新计算hashCode,若有值则调用equals()判断该地址中的值是否相等。
-
若该地址中的值相等则为同一个对象,执行不成功;若不相等则在该值后面以链表的形式存储最新的值。
这样最大的好处就是减少了equals调用的次数,相应的就大大提高了执行的速度。
总结:
-
若两个对象相等,则hashCode一定相等。
-
若hashCode相等,则两个对象不一定相等。
-
若hashCode相等,equals也相等,则两个对象相等。
因此,equals方法和hashCode方法必须重写。
hashCode()的默认行为是对堆上的对象产生独特的值。如果没有重写hashCode的话,则相等的两个对象无论如何都不可能相等。
5.简述final作用是什么?
-
final修饰变量的话,变量需要初始化值,赋值之后无法改变
-
final修饰方法的话,该方法无法被重写
-
final修饰类的话,该类无法被继承
6.String、StringBuffer、StringBuilder的区别
-
String是不可变的,如果尝试去修改,会新生成一个字符串对象,StringBuffer和StringBuilder是可变的
-
StringBuffer是线程安全的,StringBuilder是线程不安全的,所以在单线程环境下String Builder效率会更高
7.重载和重写的区别
重载:发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。 重写:发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为private,子类就不能重写该方法。
//此时不是重载,会在编译时报错
public int add(int age,String name);
public String add(int age,String name);
8.接口和抽象类的区别
-
抽象类可以存在普通成员函数,而接口中只能存在oublic abstract方法。
-
抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的。
-
抽象类只能继承一个,接口可以实现多个。
使用场景:当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。
9.List和Set的区别
-
List: 有序,按对象进入的顺序保存对象,可重复,允许多个Nul元素对象,可以使用Iterator取出所有元素, 在逐一遍历,还可以使用get(int index)获取指定下标的元素
-
Set: 无序,不可重复,最多允许有一个Nul元素对象,取元素时只能用lterator接口取得所有元素,在逐一遍 历各个元素
10.ArrayList和LinkedList的区别
-
ArrayList是基于动态数组的,拥有连续的存储空间,扩容机制是为当前数组长度的1.5倍;LinkedList是基于链表,可以存储在分散的内存中
-
ArrayList查询快,增删慢;LinkedList增删快,查询慢。
11.HashMap和HashTable的区别?底层实现是什么?
区别:
-
HashMap方法没有synchronized修饰,线程非安全,HashTable线程安全;
-
HashMap:允许key和value为null,而HashTable不允许
底层实现:数组+链表实现
-
jdk8开始链表高度到8、数组长度超过64,链表转变为红黑树,元素以内部类Nod节点存在。
-
计算key的hash值,二次hash然后对数组长度取模,对应到数组下标
-
如果没有产生hash冲突(下标位置没有元素),则直接创建Node存入数组,
-
如果产生hash冲突,先进行equall比较,相同则取代该元素,不同,则判断链表高度插入链表,链表高度达到8,并且数组长度到64则转变为红黑树,长度低于6则将红黑树转回链表
-
key为nul,存在下标o的位置
12.ConcurrentHashMap的扩容机制
1.8版本
-
1.8版本的ConcurrentHashMap不再基于Segment?实现
-
当某个线程进行put时,如果发现ConcurrentHashMap.正在进行扩容那么该线程一起进行扩容
-
如果某个线程put时,发现没有正在进行扩容,则将key-value添加到ConcurrentHashMap中,然后判断是否超过阈值,超过了则进行扩容
-
ConcurrentHashMap是支持多个线程同时扩容的
-
扩容之前也先生成一个新的数组
-
在转移元素时,先将原数组分组,将每组分给不同的线程来进行元素的转移,每个线程负责一组或多组的元素转移工作
13. ConcurrentHashMap原理,jdk7和jdk8版本的区别
jdk7:
-
数据结构:ReentrantLock+Segment+HashEntry,一个Segment中包含一个HashEntry数组,每个HashEntry.又是一个链表结构
-
元素查询:二次hash,第一次Hash定位到Segment,第二次Hash定位到元素所在的链表的头部 锁:Segment分段锁Segment继承了ReentrantLock,锁定操作的Segment,其他的Segment不受影响,并发度为segment个数,可以通过构造函数指定,数组扩容不会影响其他的segment
-
get方法无需加锁,volatile保证
jdk8:
-
数据结构:synchronized+CAS+Node+红黑树,Node的val和next都用volatile修饰,保证可见性
-
查找,替换,赋值操作都使用CAS
-
锁:锁链表的head节点,不影响其他元素的读写,锁粒度更细,效率更高,扩容时,阻塞所有的读写操作、并发、扩容
-
读操作无锁: Node的val和next使用volatile修饰,读写线程对该变量互相可见 数组用volatile修饰,保证扩容时被读线程感知
14.Jdk1.7到Jdk1.8 HashMap发生了什么变化(底层)?
-
1.7中底层是数组+链表,1.8中底层是数组+链表+红黑树,加红黑树的目的是提高HashMapi插入和查询整体效率
-
1.7中链表插入使用的是头插法,1.8中链表插入使用的是尾插法,因为1.8中插入key和vlue时需要判断链表元素个数,所以需要遍历链表统计链表元素个数,所以正好就直接使用尾插法
15.说一下HashMap的Put方法
-
根据Key通过哈希算法与与运算得出数组下标
-
如果数组下标位置元素为空,则将key和value封装为Entry>对象(JDK1.7中是Entry>对象,JDK1.8中是Node对象)并放入该位置
-
如果数组下标位置元素不为空,则要分情况讨论
-
如果是DK1.7,则先判断是否需要扩容,如果要扩容就进行扩容,如果不用扩容就生成tny对象,并使用头插法添加到当前位置的链表中
-
如果是DK1.8,则会先判断当前位置上的Node的类型,看是红黑树Node,还是链表Node
-
如果是红黑树Node,则将key和value封装为一个红黑树节点并添加到红黑树中去,在这个过程中会判断红黑树中是否存在当前key,如果存在则更新value
-
如果此位置上的Node对象是链表节点,则将key和value封装为一个链表Node并通过尾插法插入到链表的最后位置去,因为是尾插法,所以需要遍历链表,在遍历链表的过程中会判断是否存在当前key,如果存在则更新value,当遍历完链表后,将新链表Node插入到链表中,插入到链表后,会看当前链表的节点个数,如果超过了8,那么则会将该链表转成红黑树
-
将key和value封装位Node插入到链表或红黑树中后,再判断是否需要进行扩容,如果需要就扩容,如果不需要就介绍PUT方法
16.泛型中extendsi和super的区别
-
<?extends T>表示包括T在内的任何T的子类
-
<?super T>表示包括T在内的任何T的父类
17.深拷贝和浅拷贝
-
浅拷贝是指,只会拷贝基本数据类型的值,以及实例对象的引用地址,并不会复制一份引用地址所指向的对象,也就是浅拷贝出来的对象,内部的类属性指向的是同一个对象
-
深拷贝是指,既会拷贝基本数据类型的值,也会针对实例对象的引用地址所指向的对象进行复制,深拷贝出来的对象,内部的类执行指向的不是同一个对象
18.CopyOnWriteArrayListl的底层原理是怎样的
-
首先CopyOnWriteArrayListP内部也是用过数组来实现的,在向CopyOnWriteArrayListi添加元素时,会复制一个新的数组,写操作在新数组上进行,读操作在原数组上进行
-
并且,写操作会加锁,防止出现并发写入丢失数据的问题
-
写操作结束之后会把原数组指向新数组
-
CopyOnWriteArrayList允许在写操作时来读取数据,大大提高了读的性能,因此适合读多写少的应用场景,但是CopyOnWriteArrayLista会比较占内存,同时可能读到的数据不是实时最新的数据,所以不适合实时性要求很高的场景
19.Java中的异常体系
-
Java中的所有异常都来自顶级父类Throwable。
-
Throwable下有两个子类Exception和Error。
-
Error是程序无法处理的错误,一旦出现这个错误,则程序将被迫停止运行。
-
Exception不会导致程序停止,又分为两个部分RunTimeExceptioni运行时异常和CheckedException检查常。
-
RunTimeException常常发生在程序运行过程中,会导致程序当前线程执行失败。CheckedException常常发生在程序编译过程中,会导致程序编译不通过。
20.Java类加载器
-
JDK自带有三个类加载器:bootstrapClassLoader、ExtClassLoader、AppClassLoader。
-
BootStrapClassLoader是ExtClassLoader的父类加载器,默认负责加载%JAVA HOME%lib下的jar包和class.文 件。
-
ExtClassLoader是AppClassLoader的父类加载器,负责加载%AVA_HOME%Iib/ext文件夹下的jar包和class类:
-
AppClassLoader是自定义类加载器的父类,负责加载classpath下的类文件。 继承ClassLoader?实现自定义类加载器
21.双亲委派模型

双亲委派模型的好处:
-
主要是为了安全性,避免用户自己编写的类动态替换Java的一些核心类,比如String。
-
同时也避免了类的重复加载,因为JVM中区分不同类,不仅仅是根据类名,相同的clss文件被不同的 ClassLoaderj加载就是不同的两个类。
22.GC如何判断对象可以被回收
-
引用计数法:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以 回收,
-
可达性分析法:从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任 何引用链相连时,则证明此对象是不可用的,那么虚拟机就判断是可回收对象。
GC Roots的对象有:
-
虚拟机栈栈帧中的本地变量表)中引用的对象
-
方法区中类静态属性引用的对象
-
方法区中常量引用的对象
-
本地方法栈中Nl(即一般说的Native方法)引用的对象
23.JVM中哪些是线程共享区

原文始发于微信公众号(Java小羽):Java基础高频面试题总结
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/136594.html