【重磅2024Java面经】更符合大数据开发的java面经!
213、JVM、JRE和JDK的关系是什么
JDK是java开发工具包的缩写,功能齐全的java sdk,拥有jre的一切,能够创建和编译程序
JRE是java运行环境,包括JVM、java类库和一切命令
JVM是java虚拟机
214、Java 是编译执行的语言,还是解释执行的语言?
java首先被编译成.class文件,然后再通过JVM从.class文件中读一行解释执行一行
215、数据类型
基本数据类型:数值型(整数:byte(1个字节)、short(2个字节)、int(4个字节)、long(8个字节);浮点:float(4个字节)、double(8个字节))字符型:char(2个字节);布尔型;
引用数据类型:class、interface、数组、string
216、break、continue、return区别
break:用于跳出一个循环体或者完全结束一个循环
continue:跳出本次循环,结束正在执行的循环,进入下一次循环条件判定
return:不再执行下面的代码,结束当前的方法,直接返回
217、static关键字
一个成员变量或者成员方法可以在没有所属类的实例变量的下被访问,static方法是编译时静态绑定的,和任何实例都无关
static静态方法不能引用非静态资源
static静态方法里面可以引用静态资源
非静态方法里面可以引用静态资源
补充:为什么使用static关键字
在没有创建对象的情况下也想调用方法
218、java静态变量、代码块、和静态方法的执行顺序是什么?
219、面向对象和面向过程
面向过程:性能对面向对象高,类调用时候需要实例化,开销较大,没有面向对象容易维护和复用和扩展
面向对象:容易维护复用和扩展,有封装继承多态的特性,系统更加灵活,但性能较低
220、Java三大特性
封装:把客观事物封装成抽象类,让类可以把自己的数据和方法只让可信的类或者对象操作
继承:使用现有类,并进行扩展
多态:是指在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为
221、重载和重写
重写发生在子类与父类之间,返回值和形参都不能改变,外壳不变,核心重写
重载是在一个类里面,方法名相同,参数不同,返回值的类型也可以不同,每个重载的方法都有一个独一无二的参数列表
222、抽象类和接口的区别
抽象类可以提供成员方法的实现细节,接口只能存在public abstract 方法;
抽象类中的成员变量是各种类型的,而接口中的成员变量只能是public static final类型的;
接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
一个类只能继承一个抽象类,而一个类却可以实现多个接口。
区别:抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象;
接口不包含方法的实现,接口中只能包含方法,接口中的常量不能有任何访问修饰符;
223、==和equals区别是什么
== 常用于相同的基本数据类型之间的比较,也可以用于相同类型的对象之间的比较
若==用于比较基本数据类型,那么比较的是两个基本数据类型的值是否相等
若==是比较的两个对象,则比较的是两个对象的引用,判断是否指向同一块内存区域
equals主要用于两个对象之间,监测一个对象是否等于另一个对象
224、字符型常量和字符串常量
字符常量是单引号引起的一个字符,字符串常量是双引号引起的若干个字符
字符常量相当于一个整型值ASCII,字符串常量代表一个地址值
字符常量只占2个字符,字符串常量占若干个字节
补充:String str="aaa"与 String str=new String("aaa")一样吗?
不一样
前者运行时候会在常量池中查找”aaa“字符串,若没有,会将”aaa“字符串放进常量池,再将其地址赋给a,若有,将找到的字符串的地址赋给a
后者会在推内存中开辟一片新的空间存放对象,同时会将”aaa“放进常量池,无论常量池中有没有”aaa“字符串,程序都会在堆内存中开辟一块新空间存放对象
225、包装类是什么
java为每一种基本数据类型都引入了对应的包装类型,把基本数据类型转换成包装类型的过程叫做装箱,反之则是拆箱
Java 为每个原始类型提供了包装类型:
原始类型: boolean,char,byte,short,int,long,float,double
包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
与基本数据类型的区别:包装类可以为null,基本类型不可以;包装类型可用于泛型,而基本类型不可以,基本类型比包装类型更高效,基本类型在栈中直接存储具体数值,而包装类则存储的是堆中的引用,包装类型需要占用更多的内存空间
补充:int和integer的区别
integer是int的包装类,int是基本数据类型
integer变量必须是实例化后才能使用,int变量不需要
integer实际是对象的引用,指向此new的integer对象,int是直接存储数据值
integer的默认值是null,int的默认值是0
226、反射
在运行状态下,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,动态获取、调用对象的机制称为反射;
class:可以获取类的属性、方法
field:表示类的成员变量,可以用来获取和设置类之中的属性值
method:表示类中的放啊,它可以用来获取类中的方法信息或者执行方法
constructor:表示类的构造方法
用途:可以让开发人员通过外部类的全路径名创建对象,并使用这些类,扩展方法
- Class.forName(“类的路径”);当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。
Class clz = Class.forName("java.lang.String");
- 类名.class。这种方法只适合在编译前就知道操作的 Class。
- 对象名.getClass()。
- 如果是基本类型的包装类,可以调用包装类的Type属性来获得该包装类的Class对象。
225、泛型
泛型就是将类型参数化,其在编译时候才能确定具体的参数
引出泛型的原因:每次使用都需要强制转换成想要的类型;在编译时编译器不知道类型转换是否正常,运行时候才知道安全不
泛型只存在于编译阶段,而不存在于运行阶段,class文件中没有泛型这个概念
泛型优势
泛型的主要目标是提高java程序的类型安全
泛型的一个附带好处是使用时候直接得到目标类型,消除许多强制类型转换
由于泛型的实现方式,支持泛型不需要JVM或类文件更改
补充:什么是泛型中的限定通配符和非限定通配符 ?
一种是<? extends T>它通过确保类型必须是T的子类来设定类型的上界
另一种是<? super T>它通过确保类型必须是T的父类来设定类型的下界。
226、序列化
序列化就是把java对象转换成字节序列的过程,反序列化是将字节序列恢复为java对象的过程
对内存中的对象进行持久化或网络传输的时候,就需要序列化与反序列化
1、对象序列化可以实现分布式对象
2.java对象序列化不仅保留一个对象的数据,而是递归保存对象引用的每个对象的数据
3、序列化可以将内存厚葬类写入文件或者数据库中
4、对象、文件、数据、有不同的格式,很难统一传输和保存,在序列化之后就全是字节流
序列化实现方式:
java.io.Serializable:一个类的序列化可以通过实现java.io.Serializable接口启用序列化功能,仅用于标识
Externalizable:继承上一个接口,定义两个抽象方法,使用时需要重写两个方法,自行决定存储哪些信息
若类中的字段不想进行序列化时候,使用transient关键字修饰,防止序列化,只能修饰变量,不能修饰类和方法
序列化的方式:实现serializable接口或者Externalizable接口,可序列化类的所有子类型本身都是可序列化的
并且静态变量不会被序列化
227、异常处理
Exception
和 Error
二者都是 Java 异常处理的重要子类,各自都包含大量子类。
Exception
:程序本身可以处理的异常,通过catch捕获,
Error:属于程序无法处理的错误,不能通过catch捕获,系统崩溃、内存不足、堆栈溢出等,一旦发生,程序终止
228、throw 和 throws 的区别是什么?
throw 关键字用在方法内部,只能用于抛出一种异常,用来抛出方法或代码块中的异常,受查异常和非受查异常都可以被抛出。
throws 关键字用在方法声明上,可以抛出多个异常,用来标识该方法可能抛出的异常列表。一个方法用 throws 标识了可能抛出的异常列表,调用该方法的方法中必须包含可处理异常的代码,否则也要在方法签名中用 throws 关键字声明相应的异常。
229、字符流与字节流的区别?
读写的时候字节流按字节流读写,字符流按字符读写
字节流适合所有类型文件的数据传输,字符流只能处理纯文本数据,其他数据不行
读文件需要对内容按行处理时候,使用字符流
补充:java的设计模式
1、单例模式
某个类只能生成一个实例,单例类必须自己创建自己的唯一的实例
2、工厂模式
通过一个工厂类来实现对象的创建,而无需直接暴露对象的创建逻辑给客户端
3、建造者模式
允许通过一步一步地构建复杂对象来创建不同类型的对象,使用一个建造者来封装对象的创建过程并将其分解为多个简单的步骤
230、BIO、NIO、AIO的区别?
231、java中集合
List、Set、Queue、Map四种
list:代表了有序可重复的集合,可以直接根据元素的索引来访问
set:代表无序不可重复的集合,只能根据元素来访问
queue:是队列集合
map:代表的是存储kv对的集合,可根据元素的k来访问v
补充:线程安全与不安全的即集合
线程安全:ConcurrentHashMap、Hashtable:比HashMap多了个线程安全、Vector:比Arraylist多了个同步化机制。
232、Arraylist与 LinkedList 异同点?
是否保证线程安全:都是不同步的,都不保证线程安全
底层数据结构:arraylist底层使用object数组,linkedlist底层使用双向循环链表数据结构
插入和删除是否受元素位置影响:arraylist采用数据存储,在插入和删除元素时候受位置影响;linkedlist采用链表存储,插入删除时候不受影响
是否支持快速随机访问:linkedlist不支持高效的随机元素访问,arraylist有随机访问的功能
内存空间占用:arraylist空间占用主要体现在list列表的结尾会预留一定的空间;linkedlist的空间花费在于放存放直接前驱和直接后继
233、array和arraylist区别
array可以包含基本类型和对象类型,arraylist只能包含对象类型
array大小固定,arraylist的大小是动态变化的
arraylist提供了许多处理元素方法,
注意:arraylist的扩容本质——>就是计算出新的扩容数组的size后实例化,并将原有数组内容复制到新的数组中去,默认情况下,新的容量会是原来容量的1.5倍
234、Hash
在将键值对存入数组之前,将key通过哈希算法计算出哈希值,把哈希值作为数组的下标,把该下表对应的位置作为键值对的存储位置,通过该方法建立的数组就叫做哈希表,这个存储位置就叫做桶。哈希表通过字符串的key直接访问元素
hashMap
hashMap是用哈希表+红黑树实现的map类(说白了就是hashmap用了拉链法的哈希表)
哈希函数计算结果越分散均匀,哈希碰撞的概率就会越小,map的存取效率就会越高
哈希表长度越长,空间成本越大,哈希函数计算结果越分散均匀
扩容机制(负载因子)和哈希函数越合理,空间成本越小,哈希函数计算结果越均匀
即使负载因子和哈希函数设计的再合理,也避免不了出现桶内系欸但过多的情况,会出现严重的性能问题;于是引入红黑树,当链表场长度超过8时候,链表就转化为红黑树(快速增产改查提高效率)
hashmap中的元素是没有顺序的,因为哈希函数与桶数组容量有关,每次节点到了临界值后,就会自动扩容,扩容后的桶数组容量都会乘2
补充:解决hash冲突的方法有哪些?
开放定址法(再散列法):在遇到哈希冲突的时候,去寻找一个新的空闲的哈希地址
再哈希法:同时构造多个不同的哈希函数,等发生哈希冲突的时候就使用给第二个、第三个等其他的哈希函数计算地址,直到不发生冲突为止,不易发生聚集,但是时间很长
建立公共溢出区:将哈希表分为公共表和溢出表,当溢出发生时候,将所有溢出数据统一放到溢出区
补充:hashMap和hashSet的区别
235、heap和stack有什么区别
申请方式不同:stack由系统自动分配;heap由自己申请,new
申请大小的限制:栈顶的地址和栈的最大容量是系统预先定好的,在windows下,栈的大小是2M;堆是不连续的内存区域,使用链表来存储空闲内存地址的
申请效率的比较:stack由系统自动分配,速度较快,heap由new分配的内存,速度较慢
236、java中的垃圾回收机制
标记清除法、标记整理法、复制算法、分代收集算法
1、标记清除法
利用可达性遍历内存,把存活对象和垃圾对象进行标记,再遍历一遍,将所有标记的对象回收掉(标记和清除的效率都不行,会产生大量不连续的空间分片,可能导致之后程序运行的时候需要分配队形而找不到连续分片而不得触发一次GC)
2、标记整理法
利用可达性遍历内存,把存活对象和垃圾对象进行标记,将所有的存活的对象向一端移动,将端边界以外的对象都回收掉(适用于存活对象多,垃圾少的情况,无空间碎片产生)
3、复制算法
将内存按照容量大小分为大小相等两块,每次只使用一块,用完就将还存活的对象移到另一块上,然后再把使用过的内存空间移除
237、线程和进程的区别
进程是操作系统资源分配的基本单位,线程是处理器任务调度和执行的基本单位
一个进程可以包含多个线程
同一个进程的线程共享本地进程的地址空间和资源,而进程之间的地址空间和资源相对独立
一个进程崩溃,再保护模式下对其他进程没有影响,但是一个线程崩溃整个进程都会死掉
每个独立的进程有程序运行的入口和出口,但是线程不能独立执行,必须依附在应用程序中
238、线程创建方式
239、使用线程的好处
线程间的切换和调度成本远小于进程,多线程并发编程也是开发高并发系统的基础
多核CPU时代意味着多个线程可以同时运行,减少了线程上下文切换的开销
240、线程5种基本状态
1、新建状态(线程对象创建时候的状态,2、就绪状态(当调用线程对象的start方法之后,线程进入就绪状态)
3、运行状态(cpu开始调度处于就绪状态的线程时候,进入运行状态)
4、阻塞状态(因为某种原因,暂时放弃对cpu的使用权,停止执行——同步阻塞、等待阻塞:执行了.wait方法之后进入阻塞、其他阻塞)
5、死亡状态(线程执行完毕或因为异常退出run方法,该线程结束生命周期)
241、线程死锁
多个线程同时被阻塞,它们中的一个或者全部都在等待其他某个资源被释放,所以程序不可能额正常终止
死锁条件:
- 互斥条件:该资源任意一个时刻只由一个线程占用。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:线程已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
242、sleep和wait的区别
sleep:是Thread类的静态方法,当前线程将睡眠n毫秒,线程进入阻塞状态,当睡眠时间到了,会接触阻塞,
wait:是object的方法,必须与synchronized关键字一起使用,线程进入阻塞状态
243、 synchronized的用法有哪些?
修饰普通方法:作用于当前对象实例,进入同步代码前要获得当前对象实例的锁
修饰静态方法:作用于类,进入同步代码块前要获得当前类对象的锁
修饰代码块:指定加锁对象,对给定对象加锁,进入同步代码块前要获得给定对象的锁
244、java和scala的区别与联系
1、scala是基于jvm的,因此可以与java语言进行相互操作
2、scala语言可以使用java类库,可以利用java的丰富资源
3、scala和java都是面向对象的语言,都支持面向对象编程
区别:
1、scala是一种混合式编程语言,完全面向对象编程,函数时编程
2、scala语言比java更简洁,代码量更少,也更加灵活
3、scala支持类型推断,可以自动推断变量的类型
4、scala语言的并发编程更加方便
#牛客在线求职答疑中心##牛客解忧铺#不定期分享大数据相关技术面经,本着免费原则,随意白嫖