java基础阶段几个必会面试题

简介: java基础阶段几个必会面试题

1.说出你对面向对象的理解

      在我理解,面向对象是向现实世界模型的自然延伸,这是一种“万物皆对象”的编程思想。在现实生活中的任何物体都可以归为一类事物,而每一个个体都是一类事物的实例。面向对象的编程是以对象为中心,以消息为驱动,所以程序=对象+消息。

      面向对象有三大特性,封装、继承和多态

      封装就是将一类事物的属性和行为抽象成一个类,使其属性私有化,行为公开化,提高了数据的隐秘性的同时,使代码模块化。这样做使得代码的复用性更高。

      继承则是进一步将一类事物共有的属性和行为抽象成一个父类,而每一个子类是一个特殊的父类--有父类的行为和属性,也有自己特有的行为和属性。这样做扩展了已存在的代码块,进一步提高了代码的复用性。

      如果说封装和继承是为了使代码重用,那么多态则是为了实现接口重用。多态的一大作用就是为了解耦--为了解除父子类继承的耦合度。如果说继承中父子类的关系式IS-A的关系,那么接口和实现类之之间的关系式HAS-A。简单来说,多态就是允许父类引用(或接口)指向子类(或实现类)对象。很多的设计模式都是基于面向对象的多态性设计的。

2.JVM的内存区及其GC算法

参考:https://blog.csdn.net/anjoyandroid/article/details/78609971

元空间:jdk1.8取消了持久代新增了元空间,并将方法区放在元空间中

3.集合框架下的各种接口和实现类有哪些,分别有啥特点

参考:

https://blog.csdn.net/sdgihshdv/article/details/72566485

https://blog.csdn.net/suifeng629/article/details/82179996

https://blog.csdn.net/C18298182575/article/details/87167323

4.string类有啥特点,有哪些常用的API

1.String类对象的相等判断使用equals()方法完成,“==”实现的是地址数值的比较

2.字符串内容一旦声明则不可改变,String类对象内容的改变是依靠引用关系的变更实现的。

3.String类有两种实例化方式,使用直接赋值可以不产生垃圾空间,并且可以自动入池,不要使用构造方法赋值。

一些常见API:

indexOf():检索字符串中某个字符或某段字符的下标。

lastIndexOf():和indexOf类似,不过是查找最后一个出现的位置。

str.lastIndexOf(str,index):从下标index往前查找最后一个出现的位置

substring():返回一个字符串的子字符串

charAt(index):返回下标对应的字符

trim():去掉字符串前后的空格

startsWith()/endsWith():检测字符串是否已制定字符串开头或结尾,返回值是boolean

split()/根据括号内的字符串分离字符串,返回值是一个字符串数组

....

5.stringBuilder和stringBuffer的区别?

运行速度:StringBuilder >StringBuffer >String

线程安全:StringBuilder是线程不安全的,而StringBuffer是线程安全的

String:适用于少量的字符串操作的情况

StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况

StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

为什么StringBuilder是不安全的?

下面是StringBuilder 的append方法源码

char[] value;
int count;
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}

对于count + =len;不是一个原子操作 两个线程同时执行假设都是 计数器为 10 执行完后 就会变成11 而不是12

什么是原子操作:

 简单的例子:

      转账,A转给B100,因为停电,导致A转出了100,B却没收到100,所以要把100回滚给A。

原子操作就是多线程下各线程同时执行失败且同时成功,在两个线程下,由于count继承于父类AbstractStringBuilder,当

其中一个线程对coun执行+len后,另一线程取到的count值仍为原来的count值,故+len后和上一个线程得到的结果一样,

故线程不安全

而stringBuffer中源码:

@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}

当一个线程访问append后会立即上锁,从而另一个线程无法访问append方法,故是线程安全的

在多线程下,stringBuffer下各线程需要频繁的加锁解锁操作,从而需要运行更长的时间,虽然stringBuilder不需要加锁解锁,

但由于线程不安全性,更适用于单线程。

6.线程创建的3种方式,线程阻塞的API有哪些及其之间的区别?

Runnable,Thread,通过 Callable 和 Future 创建线程三种方式

1. 继承Thread类来创建一个线程, 并实现run方法(线程需要完成的功能); 构建子类对象,start()启动线程

2. 实现Runnable接口来创建一个线程, 实现Runnable,实现run()方法; 将Runnable接口类的对象作为参数传递给Thread类对象, 并调用start()方法;

3. 实现Callable接口来创建一个线程, 先定义一个Callable的实现类, 重写call()方法, call()有返回值; 两种执行方式:

1). 借助FutureTask执行, 创建Callable实现类的对象, 并作为参数传递给FutureTask, FutureTask作为参数传递给Thread类的对象, 并执行start()方法;

2). 借助线程池来执行, 先创建线程池, 然后调用线程池的submit方法, 并将Callable实现列作为参数传入

方法二的好处:

1. 可以将一个Runnable实现类传递给多个线程对象, 适合用多个相同程序代码的编程处理同一个资源

2. Thread类创建线程是采用继承的方式, 而Java中只能单继承, 如果某个子类的需要创建线程只能采用实现Runnable接口或者实现Callable接口的方式.


方法三的好处:

1. 有返回值

2. call()可以抛出异常

3. 运行Callable任务可以得到一个Future兑现,表示异步计算的结果. 它提供了检测计算是否完成的方法(isDone())以等待计算的完成,并检索计算的结果.

线程阻塞api:

sleep()方法;:该方法允许指定以ms为单位的一段时间作为参数, 它使得线程在指定的时间内进入阻塞状态,不能得到CPU时间, 指定时间已过,线程重新进入可执行状态.

suspend()和resume()方法:配套使用, suspend()使得线程进入阻塞状态,且不会自动恢复, 必须将其对应的resume()调用, 才可以使线程进入可执行状态.

yield();:使得线程放弃当前分得的CPU时间, 但是不使线程阻塞, 即线程仍然处于可执行状态;

wait()和notify()方法:配套使用,若wait()有参数,相当于sleep(但可以通过notify强行唤醒), wait()没有参数,相当于suspend(), 需要通过notify唤醒

sleep(0)和sleep(1)和不要sleep的区别

sleep(0),如果线程调度器的可运行队列中有大于或等于当前线程优先级的就绪线程存在,操作系统会将当前线程从处理器上移除,调度其他优先级高的就绪线程运行;如果可运行队列中的没有就绪线程或所有就绪线程的优先级均低于当前线程优先级,那么当前线程会继续执行,就像没有调用 Sleep(0)一样。

Sleep(1),会引发线程上下文切换:调用线程会从线程调度器的可运行队列中被移除一段时间,这个时间段约等于 timeout 所指定的时间长度。为什么说约等于呢?是因为睡眠时间单位为毫秒,这与系统的时间精度有关。通常情况下,系统的时间精度为 10 ms,那么指定任意少于 10 ms但大于 0 ms 的睡眠时间,均会向上求值为 10 ms。

7.抽象类和接口的区别?有了抽象类为啥还要接口?

1.一类可以实现多个接口但只能继承自一个抽象类,从抽象类派生出的子类同样可以实现接口,从而,我们能得出一个结论:接口是为Java实现多继承而存在的

2.抽象类中可以存在非抽象的方法,可接口不能存在非抽象的方法,并且接口里面的方法只是一个声明,必须用 public abstract来修饰,没有具体的实现

3.抽象方法中的成员变量可以被不同的修饰符修饰,而接口中的成员变量默认都是静态常量

4.抽象类是对对象进行的抽象,而接口是一种行为规范,这一点是比较重要的.

(所以为什么有了接口还要有抽象类)

8.冒泡排序,选择排序,快速排序(了解)

冒泡排序:什么是冒泡?比如说水底随机产生一些气泡,一起往上冒泡,越轻的气泡往上冒的越快

具体:12 34 10 78 67

如果从小到大排序:先将67和78比较,67比78小,依次往前比较,小的放前面,打的放后面,以此为一轮排序,然后再将新的数组重复上述过程,共需要n轮排序(n为元素个数);

选择排序:从一个数组里选出最小的元素放在数组第一位并交换位置,然后再将去掉第一位的数组找出最小元素并放在这个新数组第一位,

重复此操作。

12 34 10 78 67

第一轮:10| 34 12 78 67

第二轮:10 12| 34 78 67

第三轮:10 12 34| 78 67

第四轮:10 12 34 67| 78

排序结束

快速排序:基于基数排序。先取任意一基数,一般为数组第一个元素(由于当第一个元素为最小值(最大值)时会使排序出现错误,故有时候也取中间的元素),然后将比基数小的数作为一个数组,比基数大的数作为一个数组,再将新的两个数组分别递归排序。

通过基数分成两个数组的过程:12 34 10 78 67 8 假设数组为arr

取一基数temp=12 取low=0(数组第一位),high=5(数组最后一位)

第一轮:第一步:先从后往前比较:arr[high]=8<12=temp,结束这一步操作,high与low不变。如果这里arr[high]>12,则令high-1得到新的high将arr[high]与temp比较,依此下去直到arr[high]<temp,这种情况high发生改变,low不变。

第二步:再从前往后将arr[low]与temp比较,原理与第一步相同,因为arr[1]>temp,此时low=1,结束这一步操作。

第三步:交换arr[low]与arr[high]的值

第一轮结果:12 8 10 78 67 34(low=1,high=5)

第二轮:与第一轮一样,第一步,从arr[high]往前,直到arr[2]=10<12,此时high=2,结束这一步

第二步,从arr[low]往后,12,8,10都不大于12,到这里的时候,因为low=2=high,故比较,得到索引index=low=high=2

第二轮结果:12 8 10 78 67 34

因为index得到了值3,将arr[index]作为分界点将最后一轮结果数组[12 8 10 78 67 34]分为两个数组[12 8 10]和[78 67 34]

将新的到的两个数组重复进行上述操作

[12 8 10]->因为12为最大值,故取中间值8->[8]和[10 12]->[8]、[10]、[12]

[78 67 34]->取67,->[34]、[67 78]->[34]、[67]、[78]->[8]、[10]、[12]、[34]、[67]、[78]

(拓展:希尔排序、插入排序)

9.什么是死锁?如何避免死锁

死锁的定义:所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。

产生原因:

1) 系统资源的竞争

通常系统中拥有的不可剥夺资源,其数量不足以满足多个进程运行的需要,使得进程在 运行过程中,会因争夺资源而陷入僵局,如磁带机、打印机等。只有对不可剥夺资源的竞争 才可能产生死锁,对可剥夺资源的竞争是不会引起死锁的。

2) 进程推进顺序非法

进程在运行过程中,请求和释放资源的顺序不当,也同样会导致死锁。例如,并发进程 P1、P2分别保持了资源R1、R2,而进程P1申请资源R2,进程P2申请资源R1时,两者都 会因为所需资源被占用而阻塞。

四个产生死锁的条件:

互斥条件:进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。

不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。

请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。

循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被 链中下一个进程所请求。即存在一个处于等待状态的进程集合{Pl, P2, ..., pn},其中Pi等 待的资源被P(i+1)占有(i=0, 1, ..., n-1),Pn等待的资源被P0占有。

避免死锁:

1.加锁顺序(线程按照一定的顺序加锁)

2.加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)

3.死锁检测

参考:https://blog.csdn.net/ls5718/article/details/51896159

相关文章
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
71 2
|
20天前
|
Java 程序员
Java社招面试题:& 和 && 的区别,HR的套路险些让我翻车!
小米,29岁程序员,分享了一次面试经历,详细解析了Java中&和&&的区别及应用场景,展示了扎实的基础知识和良好的应变能力,最终成功获得Offer。
53 14
|
1月前
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
|
1月前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
1月前
|
存储 缓存 Oracle
Java I/O流面试之道
NIO的出现在于提高IO的速度,它相比传统的输入/输出流速度更快。NIO通过管道Channel和缓冲器Buffer来处理数据,可以把管道当成一个矿藏,缓冲器就是矿藏里的卡车。程序通过管道里的缓冲器进行数据交互,而不直接处理数据。程序要么从缓冲器获取数据,要么输入数据到缓冲器。
Java I/O流面试之道
|
25天前
|
Java 编译器 程序员
Java面试高频题:用最优解法算出2乘以8!
本文探讨了面试中一个看似简单的数学问题——如何高效计算2×8。从直接使用乘法、位运算优化、编译器优化、加法实现到大整数场景下的处理,全面解析了不同方法的原理和适用场景,帮助读者深入理解计算效率优化的重要性。
30 6
|
1月前
|
存储 缓存 Java
大厂面试必看!Java基本数据类型和包装类的那些坑
本文介绍了Java中的基本数据类型和包装类,包括整数类型、浮点数类型、字符类型和布尔类型。详细讲解了每种类型的特性和应用场景,并探讨了包装类的引入原因、装箱与拆箱机制以及缓存机制。最后总结了面试中常见的相关考点,帮助读者更好地理解和应对面试中的问题。
57 4
|
1月前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
114 4
|
2月前
|
存储 安全 算法
Java面试题之Java集合面试题 50道(带答案)
这篇文章提供了50道Java集合框架的面试题及其答案,涵盖了集合的基础知识、底层数据结构、不同集合类的特点和用法,以及一些高级主题如并发集合的使用。
115 1
Java面试题之Java集合面试题 50道(带答案)
|
2月前
|
存储 Java 程序员
Java面试加分点!一文读懂HashMap底层实现与扩容机制
本文详细解析了Java中经典的HashMap数据结构,包括其底层实现、扩容机制、put和查找过程、哈希函数以及JDK 1.7与1.8的差异。通过数组、链表和红黑树的组合,HashMap实现了高效的键值对存储与检索。文章还介绍了HashMap在不同版本中的优化,帮助读者更好地理解和应用这一重要工具。
66 5