Java后端开发面试知识总结——JavaSE部分

1.Java中是如何支持正则表达式操作的?

java中的String类提供了支持正则表达式操作的方法,包括:matches()、replaceAll()、replaceFirst()、split()。Java中可以用Pattern类表示正则表达式对象,它提供了丰富的API进行各种正则表达式操作,如:

import java.util.regex.Matcher;
import java.util.regex.Pattern;
class RegExpTest {
    public static void main(String[] args) {
        String str = "成都市(成华区)(武侯区)(高新区)";
        Pattern p = Pattern.compile(".*?(?=\\()");
        Matcher m = p.matcher(str);
        if(m.find()) {
            System.out.println(m.group());
        }
    }
}

2. 请你简单描述一下正则表达式及其用途

在编写处理字符串的程序时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。计算机处理的信息更多的时候不是数值而是字符串,正则表达式就是在进行字符串匹配和处理的时候最为强大的工具,绝大多数语言都提供了对正则表达式的支持。

3.请你比较一下Java和JavaSciprt?

JavaScript 与Java是两个公司开发的不同的两个产品。两者都是开发编程语言,两种语言之间的差异可以从以下几个方面进行对比。
(1)基于对象和面向对象:Java是一种真正的面向对象的语言,即使是开发简单的程序,必须设计对象;JavaScript是种脚本语言,它可以用来制作与网络无关的,与用户交互作用的复杂软件。它是一种基于对象(Object-Based)和事件驱动(Event-Driven)的编程语言,因而它本身提供了非常丰富的内部对象供设计人员使用。
(2)解释和编译:Java的源代码在执行之前,必须经过编译。JavaScript是一种解释性编程语言,其源代码不需经过编译,由浏览器解释执行。(目前的浏览器几乎都使用了JIT(即时编译)技术来提升JavaScript的运行效率)
(3)强类型变量和类型弱变量:Java采用强类型变量检查,即所有变量在编译之前必须作声明;JavaScript中变量是弱类型的,甚至在使用变量前可以不作声明,JavaScript的解释器在运行时检查推断其数据类型。

4.请你说明一下,在Java中如何跳出当前的多重嵌套循环?

在最外层循环前加一个标记如A,然后用break A;可以跳出多重循环。(Java中支持带标签的break和continue语句,作用有点类似于C和C++中的goto语句,但是就像要避免使用goto一样,应该避免使用带标签的break和continue,因为它不会让你的程序变得更优雅,很多时候甚至有相反的作用,所以这种语法其实不知道更好)

5.请你讲讲&和&&的区别?

&运算符有两种用法:(1)按位与;(2)逻辑与。&&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true整个表达式的值才是true。&&之所以称为短路运算是因为,如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算。很多时候我们可能都需要用&&而不是&,例如在验证用户登录时判定用户名不是null而且不是空字符串,应当写为:username != null &&!username.equals(""),二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行字符串的equals比较,否则会产生NullPointerException异常。

6.int和Integer有什么区别?

int是8种基本数据类型的其中一种,而Integer属于类。我们知道,java属于一种面向对象的编程语言,因此我们在很多时候虽然使用的是数据,但是需要将这些基本数据类型当成对象进行操作,因为java就为基本数据类型提供了对应的包装类,对数据进行包装,以后对数据的操作就可以变成对象的操作。因此这里的int和Integer属于基本类型和包装类的关系。java中的基本数据类型和对应的包装类:
基本类型:boolean、 char、 byte、 int、 short、 long、 float、 double
包装类:Boolean、Character、Byte、Integer、Short、Long、Float、Double

7.java中为什么会有基本数据类型

因为在java中,我们通过new方式创建的对象是存储在堆里面的,对于我们在方法中经常操作的数据类型,如果每次创建对象就会导致资源消耗太大,因此就提供了8个基本数据类型,存储的时候,就存储在方法栈中,使用的时候就更加方便。

8.请你讲讲数组(Array)和列表(ArrayList)的区别?什么时候应该使用Array而不是ArrayList?

Array和ArrayList的不同点:
Array可以包含基本类型和对象类型,ArrayList只能包含对象类型。
Array大小是固定的,ArrayList的大小是动态变化的。
ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。

9.请你解释什么是值传递和引用传递?

值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量.
引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。 所以对引用对象进行操作会同时改变原对象.

10.请你说说Lamda表达式的优缺点

(1)优点:1. 简洁。2. 非常容易并行计算。3. 可能代表未来的编程趋势。
(2)缺点:1. 若不用并行计算,很多时候计算速度没有比传统的 for 循环快。(并行计算有时需要预热才显示出效率优势)2. 不容易调试。3. 若其他程序员没有学过 lambda 表达式,代码不容易让其他语言的程序员看懂。

11.你知道java8的新特性吗,请简单介绍一下

(1)lambda表达式。
(2)方法引用− 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
(3)默认方法—接口中可以有默认实现的方法
(4)Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常
(5)Date Time API − 加强对日期与时间的处理。

12.请你说明符号“==”比较的是什么?

“==”的两边的操作对象如果是对象,那么比较的就是两个对象的引用地址,判断两个对象的引用地址是否一样,如果一样,表示的是同一个对象,返回true。如果两边是基本类型,就是比较数值是否相等。

13.请你解释Object若不重写hashCode()的话,hashCode()如何计算出来的?

Object的hashCode()方法属于本地方法,也就是c或者c++实现的方法,如果我们在编写一个类的时候,没有重写这个方法,那么在后续调用该方法时,返回的就是这个对象在内存中的地址。

14.请你解释为什么重写equals还要重写hashcode?

假如我们重写了一个类中的equals方法,判别两个对象是否equals的规则就是里面的属性是否的一样,此时,如果两个对象满足equals规则,返回true,我们认为两个对象是相等的,但是在java中规定,两个对象相等必须要满足对应的hashcode相等,如果我们没有重写hashcode()方法,那么两个对象的hashcode就是对象地址,显然是不一样的,就会产生矛盾。

15. 请你介绍一下map的分类和常见的情况

java中有关Map接口的实现类有四个:HashMap、Hashtable、LinkedHashMap、TreeMap。Map主要用于存储健值对,根据键得到值,因此不允许键重复(重复了覆盖了),但允许值重复。
(1)Hashmap 是一个最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。 HashMap最多只允许一条记录的键为Null;允许多条记录的值为 Null;HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致。如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力,或者使用ConcurrentHashMap。
(2)Hashtable与 HashMap类似,它继承自Dictionary类,不同的是:它不允许记录的键或者值为空;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了 Hashtable在写入时会比较慢。
(3)LinkedHashMap 是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.也可以在构造时用带参数,按照应用次数排序。在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比 LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。
(4)TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。

16.请你讲讲Java里面的final关键字是怎么用的?

(1)final修饰类或者方法时:当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。
(2)“使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。在最近的Java版本中,不需要使用final方法进行这些优化了。“
(3)对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。

17.请你谈谈关于Synchronized和ReentrantLock

(1)实现原理:synchronized是java规范中的一个关键字,属于JVM底层层面的锁,对象只有在同步块或者同步方法中才能调用wait/notify方法。而ReentrantLock属于API层面的锁,实现的是Lock接口。
(2)是否手动释放锁:synchronized不需要用户去手动释放锁,用synchronized关键字修饰的代码块或者方法在执行完成之后或者抛出异常的时候就会自动释放持有的锁;ReentrantLock需要用户手动的上锁和释放锁,如果没有手动的释放锁,就可能造成死锁。一般通过lock()和unlock()方法搭配try/finally语句块来使用,而unlock切记一定需要放在finally语句块中,不然的话当程序抛出异常的时候,就会导致锁无法释放。
(3)是否可中断:synchronized是不可中断类型的锁,除非加锁的代码执行完毕或者抛出异常;而ReentrantLock是可中断的。
(4)是否是公平锁:synchronized为非公平锁。 ReentrantLock则即可以选公平锁也可以选非公平锁,通过构造方法new ReentrantLock时传入boolean值进行选择,为空默认false非公平锁,true为公平锁。
(5)锁是否可以绑定条件:synchronized不能绑定; ReentrantLock通过绑定Condition结合await()/singal()方法实现线程的精确唤醒,而不是像synchronized通过Object类的wait()/notify()/notifyAll()方法要么随机唤醒一个线程要么唤醒全部线程。
(6)锁的对象:synchronized锁的是对象,锁是保存在对象的对象头里面,根据对象头里面的数据判断是否有线程获得锁/争抢锁;ReentrantLock锁的是线程,根据进入的线程和int类型的state表示锁的获取/争抢

18.锁的分类与使用

我们可以根据锁的不同特性,进行不同的分类
乐观锁/悲观锁
独占锁/共享锁
互斥锁/读写锁
可重入锁
公平锁/非公平锁
分段锁
偏向锁/轻量级锁/重量级锁

1、乐观锁/悲观锁
乐观锁与悲观锁并不是特指某两种类型的锁,是人们定义出来的概念或思想,主要是指看待并发同步的角度。
乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS(Compare and Swap 比较并交换)实现的。
悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。比如Java里面的同步原语synchronized关键字的实现就是悲观锁。
悲观锁适合写操作非常多的场景,乐观锁适合读操作非常多的场景,不加锁会带来大量的性能提升。
悲观锁在Java中的使用,就是利用各种锁。
乐观锁在Java中的使用,是无锁编程,常常采用的是CAS算法,典型的例子就是原子类,通过CAS自旋实现原子操作的更新。
2、独占锁/共享锁
独享锁是指该锁一次只能被一个线程所持有。
共享锁是指该锁可被多个线程所持有。
对于Java ReentrantLock而言,其是独享锁。但是对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,其写锁是独享锁。
读锁的共享锁可保证并发读是非常高效的,读写,写读,写写的过程是互斥的。
独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。
对于Synchronized而言,当然是独享锁。
3、互斥锁/读写锁
上面讲的独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。
互斥锁在Java中的具体实现就是ReentrantLock。
读写锁在Java中的具体实现就是ReadWriteLock。
4、可重入锁
可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。说的有点抽象,下面会有一个代码的示例。
对于Java ReetrantLock而言,从名字就可以看出是一个重入锁,其名字是Re entrant Lock 重新进入锁。
对于Synchronized而言,也是一个可重入锁。可重入锁的一个好处是可一定程度避免死锁。

synchronized void setA() throws Exception{
  Thread.sleep(1000);
  setB();
}

synchronized void setB() throws Exception{
  Thread.sleep(1000);
}

上面的代码就是一个可重入锁的一个特点。如果不是可重入锁的话,setB可能不会被当前线程执行,可能造成死锁(因为方法A的执行需要B先执行,但是B的执行需要获得锁,而这个锁在方法A的手里,A没有执行完是不会释放这个锁的)。
5、公平锁/非公平锁
公平锁是指多个线程按照申请锁的顺序来获取锁。
非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。
对于Java ReetrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。
对于Synchronized而言,也是一种非公平锁。由于其并不像ReentrantLock是通过AQS的来实现线程调度,所以并没有任何办法使其变成公平锁。
6、分段锁
分段锁其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。
我们以ConcurrentHashMap来说一下分段锁的含义以及设计思想,ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap(JDK7和JDK8中HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。
当需要put元素的时候,并不是对整个hashmap进行加锁,而是先通过hashcode来知道他要放在哪一个分段中,然后对这个分段进行加锁,所以当多线程put的时候,只要不是放在一个分段中,就实现了真正的并行的插入。
但是,在统计size的时候,可就是获取hashmap全局信息的时候,就需要获取所有的分段锁才能统计。
分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。
7、偏向锁/轻量级锁/重量级锁
这三种锁是指锁的状态,并且是针对Synchronized。在Java 5通过引入锁升级的机制来实现高效Synchronized。这三种锁的状态是通过对象监视器在对象头中的字段来表明的。
偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让他申请的线程进入阻塞,性能降低。
8、自旋锁
在Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。

19.谈谈CAS算法

CAS(Compare and Swap 比较并交换)是乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其他线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。
CAS操作中包含三个操作数——需要读写的内存位置(V)、进行比较的预期原值(A)和拟写入的新值(B)。如果内存位置V的值与预期原值A相匹配,那么处理器会自动将该位置值更新为新值B,否则处理器不做任何操作。无论哪种情况,它都会在CAS指令之前返回该位置的值(在CAS的一些特殊情况下将仅返回CAS是否成功,而不提取当前值)。CAS有效地说明了“我认为位置V应该包含值A;如果包含该值,则将B放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可”。这其实和乐观锁的冲突检查+数据更新的原理是一样的。

20.请你介绍一下volatile关键字

volatile关键字是用来保证有序性和可见性的。这跟Java内存模型有关。比如我们所写的代码,不一定是按照我们自己书写的顺序来执行的,编译器会做重排序,CPU也会做重排序的,有序性实现的是通过插入内存屏障来保证的。可见性:首先Java内存模型分为,主内存,工作内存。比如线程A从主内存把变量从主内存读到了自己的工作内存中,做了加1的操作,但是此时没有及时将变量的最新值刷新会主内存中,线程B此时读到的还是旧值。

21.请说明Java中的方法重写(Overriding)和方法重载(Overloading)是什么意思?

Java中的方法重载发生在同一个类里面两个或者是多个方法的方法名相同但是参数不同的情况。与此相对,方法覆盖是说子类重新定义了父类的方法。方法重写必须有相同的方法名,参数列表和返回类型。方法的重载不能使用方法的返回值类型进行区分。
注意:方法的重写和重载都是java多态的实现方式。

22.请你谈一下面向对象的"六原则一法则"(也就是设计模式遵循的原则)

1、单一职责原则:一个类只负责一个功能模块,尽量使得类呈现出高内聚低耦合。
2、接口隔离原则:接口隔离和单一职责原则有点类似,只不过单一职责描述的是类,而接口隔离描述的是接口,也就是接口中定义的方法不要太臃肿,需要接口也呈现一定的内聚性。例如:一个接口中包含大量的方法,但是实际上这些方法可以根据特性分成在不同的接口中,使得接口精简。
3、里氏替换原则:任何时候都可以用子类型去替换父类型。
4、依赖倒转原则:体现出的核心思想就是面向对象编程。也就是声明的方法的参数类型、方法的返回值类型以及变量的引用类型尽量采用抽象类型(即接口)而不是具体的类型。
5、合成复用原则:也就是优先使用聚合或者合成关系来复用代码,而不是去随便继承一个类以求复用类中的方法。
6、开闭原则:软件设计的应该追求对扩展开放,对修改关闭。
7、迪米特法则:迪米特法则也叫最少知道原则,也就是一个类的对象应该尽可能的对其他对象知道的最少,因为我们面向对象编程特点就是封装性,一个类的内部结构只需要提供对外暴露的接口方法就行。

23.请说明如何通过反射来获取或设置对象私有字段的值?

首先通过一个对象,获取到该对象对应的Class对象clazz,然后通过调用该clazz对象的getDeclaredField()方法来获取对应的Field对象,再通过这个字段对象的setAccessible(true)将对设置为可以访问,接下来就可以通过get/set方法对这个字段进行访问或者修改

24.java内部类和外部类之间的访问权限

内部类和匿名内部类可以直接访问外部类中所有权限的字段或者方法(因为内部类持有一个外部类的引用),反之也一样(但是外部类要想访问内部类的字段,需要实例化一个内部类的对象),外部类也是可以访问内部类的所有权限的字段。
静态内部类只能访问外部类的静态字段或者方法

25.聊一聊java语言中和异常处理相关的关键字都有哪些?分别代表什么含义?

1、java中和异常相关的关键字有:try、catch、finally、throws、throw
2、try关键字用于定义一个代码块,然后执行这个代码块中的语句,当代码块中的程序执行出现异常的时候,我们可以使用catch关键字去捕获这个异常,然后根据这个异常进行相应的处理,或者我们可以直接将这个异常向上层抛出去,由上层的代码去捕获并处理,如果在代码中,有必须要释放的资源需要关闭,我们应该将关闭资源的代码放在finally语句块中,因为finally语句块无论是否抛出异常都会被执行。上述所说的四个关键字都可以被理解为是对异常的处理,但是throw关键字用于手动的抛出一个异常,是异常的产生方式。

26.请你聊聊抽象类和接口之间的区别?

抽象类必须使用abstract关键字修饰,子类必须实现抽象类中的抽象方法,如果有未实现的,那么子类也是一个抽象类,必须用abstract修饰。抽象类不能创建对象,但是可以声明变量。
接口中的变量隐式的使用public static final进行修饰,并且需要给出初始值。方法隐式的使用public abstract修饰,并且只能是public修饰,不然那会编译报错。接口中的方法在jdk8之前默认不能有实现,但是在jdk8开始可以有默认的实现。
两者之间的主要区别:
(1)抽象类只能继承一个,但是可以实现多个接口
(2)抽象类和接口的实现类/子类必须实现其中所有的抽象方法,抽象类的子类如果没有完全实现其中的抽象方法,那么子类也必须声明为abstract的,抽象类可以有非抽象的方法。
(3)接口中的变量必须是public static final修饰,并且需要给出初始值,实现类不能重新定义,也不能修改它的值。
(4)接口中的方法默认是public abstract修饰,不能使用static进行修饰,抽象类允许有static方法。

27.请说明一下final finally finalize关键字的区别

final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。
finally是异常处理语句结构的一部分,表示总是执行。
finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源
回收,例如关闭文件等

28.请说明面向对象的特征有哪些方面

(1)抽象
(2)封装
(3)继承
(4)多态

29.请说明Comparable和Comparator接口的作用以及它们的区别

Comparable接口中需要实现类去实现里面的compareTo()方法,用于对两个对象进行排序,例如:class A implements Compareable,那么A的一个对象a1.compareTo(a2),就是比较两个对象a1和a2的大小,比较规则在compareTo方法中定义。
comparator接口用于定义一个自定义的比较器,实现这个接口的类需要重写里面的compare方法。这个方法传入的两个参数a,b比较的就是这两个参数的大小,返回值为负数、0、正数。

30.java支持多继承吗?

java不支持多继承,一个子类只能继承自一个父类,但是java支持多实现,也就是一个类可以实现多个接口,去继承多个接口中的多个方法。

31.java中通过反射获取对象的方式

(1)通过类对象(Class对象clazz)调用newInstance()方法,例如:String.class.newInstance()
(2)通过类对象(clazz)的getConstructor()或者getDeclaredConstructor()方法来获取到构造器(Constructor)对象
,然后调用构造器对象的newInstance()方法。
两种方法的区别:
第一种方法、通过Class的newInstance()方法只能使用默认的构造方法,换句话说就是使用此方法是不能通过带参的构造函数来生成一个类的实例;
第二种方法、通过Constructor的newInstance()方法可以使用默认的构造方法,也可以使用带参的构造方法来生成一个类的实例

32.java中的静态变量存在哪里?

说到这个问题,实际上和jvm的内存模型有关,在jvm中,运行时数据区分为:程序计数器、堆、本地方法栈、java栈、方法区。其中堆和方法区是所有线程共有的,而程序计数器、java栈、本地方法栈是每个线程独有的。
方法区:也叫作静态区,方法区中包含加载进内存的每个类对象对应的Class实例和静态变量,也就是方法区中的数据是在整个程序中永远唯一的元素。例如:Class类的实例clazz以及静态变量。

33.请你解释一下类的加载机制——双亲委派模型,以及好处是什么?

在java中,类的加载由不同的类加载器完成的,类加载器自顶向下有:引导类加载器、扩展类加载器、系统类加载器(应用类加载器),同时我们也可以自定义类加载器,类的加载机制,双亲委派机制指的是,当一个类需要进行加载时,会首先将这个类给父类去加载,这样不断向上递归,当父类发现自己无法加载时,就会向下递交,交给子类去加载,当父类可以加载时,这个类就由父类去加载。
采用双亲委派机制的好处是避免类被重复加载,同时也保护了我们java核心代码不被恶意的修改。

34.请你谈谈StringBuffer和StringBuilder有什么区别,底层实现上呢?

StringBuffer和StringBuilder都继承自AbstractStringBuilder。
1、StringBuffer 与 StringBuilder 中的方法和功能完全是等价的,
2、只是StringBuffer 中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是线程不安全的。
3、在单线程程序下,StringBuilder效率更快,因为它不需要加锁,不具备多线程安全,而StringBuffer则每次都需要判断锁,效率相对更低

35.请说明String是否能被继承?

String类不能被继承,并且字符串String对象也不能被修改,因为里面的char[]数组是用的final修饰的,所以我们每次在一个字符串后面使用“+”连接字符串的时候,jvm底层在字符串常量池(存在于方法区中)都为我们创建了新的字符串。

36.请列举你所知道的Object类的方法并简要说明

(1)默认的构造器方法,用于构造对象的时候调用。
(2)clone方法,用于实现对象的克隆,创建并返回次对象的一个副本。
(3)equals(Object o)方法,指示其他对象与当前对象是否相等。
(4)finalize()方法,用于jvm底层垃圾回收器进行垃圾回收的时候调用,注意在这个方法中我们可以将一个被标识为垃圾的对象进行复活。
(5)getClass()方法,用于返回一个对象的Class实例。
(6)hasCode()方法,用于计算并返回这个类的哈希值,默认的话返回的是对象的地址。
(7)toString()方法,用于将当前对象转换成字符串并返回,如果不重写的话,默认返回的就是这个对象的地址。
(8)wait()方法,如果一个对象被当成一个锁,那么调用这个对象的wait方法就会导致当前这个线程进入阻塞状态,并且当前线程需要释放这个对象锁,由其他线程调用notify()方法来唤醒这个线程。
(9)notify()方法,用于随机在阻塞队列中唤醒一个线程。
(10)notifyAll()方法,用于唤醒所有的阻塞线程。

37.Object类中的wait()方法的实现原理

wait()方法和notify()方法和notifyAll()方法搭配使用,wait()方法用于将当前线程放入到阻塞队列。其实现的基本原理为:
1.将当前线程封装成objectwaiter对象node
2.将这个线程对象添加到阻塞对象中
3.释放这个线程所持有的锁
4.最终底层调用park方法将线程挂起

38.请说明Collection 和 Collections的区别

需要说明的是,两个类几乎没有任何的联系。
Collection是集合类的一个上级接口,继承自它的子接口有Set和List。
Collections是java提供的一个工具类,用于实现对各种集合类进行操作,例如实现HashMap的线程安全,集合的排序等。

39.请你说说Iterator和ListIterator的区别?

Iterator和ListIterator的区别是:
Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List。
Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向。
ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。

40.请解释为什么集合类(Collection)没有实现Cloneable和Serializable接口?

克隆(cloning)或者是序列化(serialization)的语义和含义是跟具体的实现相关的。因此,应该由集合类的具体实现来决定如何被克隆或者是序列化。

41.请解释一下TreeMap?

TreeMap是一个有序的key-value集合,基于红黑树(Red-Black tree)的 NavigableMap实现。映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator进行排序,具体取决于使用的构造方法。
TreeMap的特性:
根节点是黑色
每个节点都只能是红色或者黑色
每个叶节点(NIL节点,空节点)是黑色的。
如果一个节点是红色的,则它两个子节点都是黑色。
从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值