java基础面试题

1、jdk和jre的区别?
区别1:本质区别:

​ JDK(Java Development Kit)是Java开发工具包,是Sun Microsystems针对Java开发员的产品。

​ JRE(Java Runtime Environment)是运行java程序所必须的环境和集合。

区别2:组成区别:

​ JDK包含一组java工具,例如(javadoc等);java运行环境(JRE);java核心类库API

​ JRE则包含有JVM和java的核心类库

在这里插入图片描述

2、==和equals的区别?
A、对于==,比较的是值是否相等

​ a、如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;

​ b、如果作用于引用类型的变量,则比较的是所指向的对象的地址

B、对于equals方法,注意:equals方法不能作用于基本数据类型的变量,equals继承Object类,其拥有比较是否是同一个对象

​ a、如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;

​ b、如果对equals方法进行重写,则按重写后的方式比较。例如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。

3、两个对象的hashCode()相同,则equals()也一定为true吗?

​ 结论:在hashCode()和equals()方法没有被重写的前提下,两个对象equals相等,则它们的hashcode必须相等,反之则不一定。

​ 原因:hashCode ()方法继承自Object,其本质是通过将该对象的内部地址通过计算转换成一个整数值,(其本质是使用杂凑算法运算出来的,建议回答问题的时候不要说这个算法,以防面试官提问这个算法的相关问题),既然是算出来的,那么就是不准确的(接近准确但还是有误差),肯定会有一种情况,在某一时刻,多个对象传回相同的整数值,所以就能够出现“hashCode相同,对象不一定相等”的情况;那么,反过来,“相等的对象,hashCode一定相同么?“,是的,两对象相等,hashCode 一定相同,因为hashCode()是同一种计算方法,所以,相等的对象,通过HashCode计算得出的值一定相同。

4、final在java中的作用
final的作用主要有三点:

​ A、使用final修饰符修饰一个类,那么这个类被称为终态类,其含义是不能被其它的java类继承(也就是该类不能被扩展,例如String类)

​ B、使用final修饰符修饰一个方法,那么这个方法就被称为终态方法,其含义就是不能被子类重写这个方法

​ C、使用final修饰符修饰属性,那么这个属性就是一个常量,其含义就是在程序运行期间不能给该属性再次赋值

5、Math.round(1.5)和Math.round(-1.5)分别是多少?为什么?

Math.round(1.5)是2,Math.round(-1.5)的值是-1。

原因是Math.round()的实现原理是在参数值基础上加0.5,然后对相加后的结果做向下取最小整数。

1.5+0.5为2.0,最小整数即为2

-1.5+0.5为-1.0,最小整数即为-1

6、String属于基础数据类型吗?

String不属于基础数据类型,它是引用数据类型,表示字符串对象。

Java中的数据分为基础类型和引用类型,其区别在于:

​ A、基础类型只能表示简单的数据和字符,而引用数据类型可以复杂的数据结构

​ B、基础类型只能表示数据本身,而引用数据类型还可以表示对数据的操作(也就是对象中的方法)

​ C、基础类型在内存中是分配存储空间直接存储数据值,而引用数据类型则是存储的是对象在内存中的引用(也就是对象地址)

7、java中操作字符串的类由哪些?它们有什么区别?

Java中操作字符串的类主要有String、StringBuffer和StringBuilder类

区别:

​ A、运行效率不同:

从运行效率上,StringBuilder大于StringBuffer,StringBuffer大于String

String是字符串常量值其改变都是创建新对象,而StringBuffer和StringBuilder都是字符串变量值,其改变不用创建新对象。所以StringBuffer和StringBuilder效率高于String

StringBuffer在多线程中具有线程锁,所以在多线程的情况下执行效率低于StringBuilder

​ B、线程安全不同:

StringBuilder是线程不安全的;而StringBuffer是线程安全的

​ C、数据值是否能改变:

String是字符串常量值,所以一旦创建不能改变其值;而StringBuffer和StringBuilder则是字符串变量值,所以其值是可以直接改变的

所以如果创建一个String类的字符串”abc”,在其值上添加字符串”de”其本质是创建新的字符串对象”abcde”,而不是直接改其值。例如

String str = “abc”;

String str1 = str + “de”; //创建新对象”abcde”

但是注意如下代码:

String str = “abc” + “de”; //此句不是先创建两个字符串对象”abc”和”de”,然后在创建字符串”abcde”,而是直接创建字符串”abcde”,等同于String str = “abcde”。这是JVM做了优化的原因

8、String两种创建对象方式的区别?

String两种创建对象的方法分别为常量式创建和对象式创建

常量式创建:String str = “abc”;

对象式创建:String str = new String(“abc”);

区别:

​ A、常量式创建字符串是在字符串池中创建字符串对象,然后将字符串对象的引用保存在声明的字符串变量中,如图:

在这里插入图片描述

B、对象式创建字符串则是先在字符串池中创建字符串对象,然后在堆内存中创建一个新的字符串对象,堆中字符串对象去引用字符串池中的字符串,所以这种方式实质是创建了两个对象

在这里插入图片描述

9、如何将一个字符串反转?

方法一:使用字符串拼接方法

String str1 = "hello world!"; String str2 = "";

  for(int i=str1.length()-1;i>=0;i--)

​    str2 += str1.charAt(i);

  str1 = str2;

System.out.println(str1);

方法二:使用StringBuilder方法

String str1 = "hello wrold!";

  StringBuilder str2 = new StringBuilder(str1);

  str1 = str2.reverse().toString();

System.out.println(str1);

方法三:使用首尾交换字符的方法

String str1 = "hello world!";

  **char**[] cs = str1.toCharArray();

  **int** len = cs.length - 1;

  **int** half = (**int**)Math.*floor*(len /2);

  **char** c ;

  **for**(**int** i= 0 ; i <= half; i++) {

​    c = cs[i]; cs[i] = cs[len - i]; cs[len - i] = c;

  }

  str1 = String.valueOf(cs);

System.out.println(str1);
10、String类的常用方法?

​ A、length():获取字符串的长度

​ B、charAt():获取字符串指定索引位的字符

​ C、concat():在原字符串末尾拼接一个字符串,并返回新的字符串

​ D、trim():去掉字符串首尾两端的空格

​ E、indexOf():查找原字符串中是否存在指定的字符或字符串,并返回其索引位。如果未找到返回-1

​ F、equals():比较两个字符串的值是否相等

​ G、toLowerCase():将原字符串中的英文大写字符转换成小写字符

​ H、toUpperCase():将原字符串中的英文小写字符转换成大写字符

​ I、substring():从原字符串中截取一个新的字符串

​ J、split():按照指定的分隔符号将原始字符串分割成一个字符串数组

11、抽象类必须有抽象方法吗?

​ 在java中,抽象类不一定具有抽象方法,也就是可以有抽象方法,也可以没有抽象方法。抽象方法不是抽象类的必要条件。

​ 抽象类指用abstract修饰符修饰的类,该类不能被实例化。

​ 例如:public abstract class Car{}

​ 抽象方法指用abstract修饰符修饰的方法,该方法没有方法体,必须被子类重写

例如:public abstract void open();

12、普通类和抽象类有哪些区别?

​ (1) 抽象类不能被实例化,而普通类可以被实例化;

​ (2) 抽象类不能被声明为静态的,而普通类可以被声明为静态的;

​ (3) 抽象类中可以有抽象方法,而普通类中不能有抽象方法;

​ (4) 抽象类不能被声明为final的,而普通类可以被声明为final的;

13、抽象类能够使用final修饰吗?

抽象类不能被final修饰,原因抽象类一般都是继承中的顶层类,而继承的顶层类就是要子类去继承扩展的;而final修饰的类是终态类,是不能被子类继承的,这就与其本意冲突。所以抽象类不能是final修饰的。

14、接口和抽象类的联系和区别?

​ **联系:**他们都不能实例化对象,都可以包含抽象方法,而且抽象方法必须被继承的类全部实现。

区别:

  1. 接口里只能包含抽象方法,静态方法和默认方法,不能为普通方法提供方法实现,抽象类则完全可以包含普通方法。也就说抽象类普通方法既可以是抽象的,也可以是非抽象的,但是接口里的普通方法必须全部是抽象方法。

    2.接口里只能定义静态常量,不能定义普通成员变量,抽象类里则既可以定义普通成员变量,也可以定义静态常量。

    3.接口不能包含构造器,抽象类可以包含构造器。

    4.抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法;抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的。

    5.接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法; 接口里不能包含初始化块,但抽象类里完全可以包含初始化块。

    6.一个类只能继承一个抽象类,而一个类却可以实现多个接口。

15、java中的io流分为几种?

(1) 按流向分:输入流和输出流

(2) 按类型分:

​ ① 字节流:InputStream,OutputStream

​ ② 字符流:Reader和Writer

​ ③ 节点流:

​ FileInputStream FileOutputStream FileReader

​ FileWriter PrintStream PrintWriter 等

​ ④ 处理流:

​ 1) 转换流:InputStreamReader/OutputStreamWriter

​ 2) 缓冲流:BufferedInputStream/BufferedOutputStream

​ BufferedReader/BufferedReader等

备注:处理流和节点流的区分方法,节点流在新建时需要一个数据源(文件、网络)作为参数,而处理流需要一个节点流作为参数

16、BIO和NIO和AIO的区别?

(1) 同步阻塞I/O,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池机制来改善。BIO方式适用于连接数目比较小且固定的架构,这种方式对服务端资源要求比较高,并发局限于应用中,在jdk1.4以前是唯一的io现在,但程序直观简单易理解

(2) 同步非阻塞I/O,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求时才启动一个线程进行处理。NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,jdk1,4开始支持

(3) 异步非阻塞I/O,服务器实现模式为一个有效请求一个线程,客户端的IO请求都是由操作系统先完成了再通知服务器用其启动线程进行处理。AIO方式适用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,jdk1.7开始支持。

(4) 总结:BIO是一个连接一个线程;NIO是一个请求一个线程;AIO是一个有效的请求一个线程

17、Files的常用方法有哪些?

(1) copy(InputStream in,Path filepath):将输入流中的所有字节复制到文件

(2) copy(Path filepath,OutputStream):将文件中所有字节复制到输出流

(3) copy(Path source,Path target):将源文件的所有字节复制到目标文件

(4) createDirectory:创建一个目录

(5) createFile:创建一个文件

(6) newBufferedReader:创建一个文件的缓存字符输入流读取文件内容

(7) newBufferedWriter:创建一个文件的缓存字符输出流写入文件内容

18、java的容器有哪些?

所谓容器指专门用于存储数据的java对象。

(1) 广义理解:

​ ① 数组:用于存储固定长度的固定类型数据的容器

​ ② 字符串:用于存储固定长度字符的容器,其底层实现就是一个char型数组

​ ③ 集合:用于存放任意类型对象,不确定长度的对象容器,包括List,Set和Ma

(2) 狭义理解:集合容器

|Collection

|  ├List

|  │-├LinkedList

|  │-├ArrayList

|  │-└Vector

|  │ └Stack

|  ├Set

|  │├HashSet

|  │├TreeSet

|  │└LinkedSet

|

|Map

Hashtable

HashMap

19、Collection和Collections的区别?

Collection是集合类的上级接口,继承与他有关的接口主要有List和Set

Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全等操作

20、List和Set以及Map有什么区别?

(1) List的元素以线性方式存储,可以存放重复对象,主要实现类ArrayList和LinkedList

(2) Set中的对象不按特定(HashCode)的方式排序,并且没有重复对象,Set主要有以下两个实现类:HashSet和TreeSet

(3) Map是一种把键对象和值对象映射的集合,它的每一个元素都包含一个键对象和值对象。 Map主要有以下两个实现类:HashMap,LinkedHashMap和HashTable

21、HashMap和Hashtable有什么区别?

(1) 继承不同:

public class Hashtable extends Dictionary implements Map

public class HashMap extends AbstractMap implements Map

(2) Hashtable 中的方法是同步的,而HashMap中的方法在缺省情况下是非同步的。在多线程并发的环境下,可以直接使用Hashtable,但是要使用HashMap的话就要自己增加同步处理了。

(3) Hashtable中,key和value都不允许出现null值。在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,即可以表示 HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。

(4) 两个遍历方式的内部实现上不同。Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。

(5) 哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值。

(6) Hashtable和HashMap它们两个内部实现方式的数组的初始大小和扩容的方式。HashTable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。

22、如何决定使用HashMap还是TreeMap?

(1) TreeMap的key值要求实现Comparable接口,其默认按照key值升序排序;

(2) HashMap的key值实现散列hashCode(),不支持排序

(3) 对于在 Map 中插入、删除、定位一个元素这类操作,HashMap 是最好的选择,因为相对而言 HashMap 的插入会更快(TreeMap插入数据需要进行排序,要消耗更多的时间),但如果你要对一个 key 集合进行有序的遍历,那 TreeMap 是更好的选择

23、说一下HashMap的实现原理?

HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序。

HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。如图:

在这里插入图片描述

创建一个HashMap就是初始化一个Entry类型的数组。Entry代表的就是Map中的一个键值对,其具有key,value和一个指向下一个元素的引用,通过这样的方式就构成了链表结构。

在map集合put(存)数据时,先根据key的hashCode计算其hash值,根据hash值获取当前数据在Entry数组中的下标,如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。

24、HashSet的实现原理?

(1) HashSet是基于HashMap实现的,默认构造函数是构建一个初始容量为16,负载因子为0.75 的HashMap。封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。

(2) 当我们试图把某个类的对象当成 HashMap的 key,或试图将这个类的对象放入 HashSet 中保存时,重写该类的equals(Object obj)方法和 hashCode() 方法很重要,而且这两个方法的返回值必须保持一致:当该类的两个的 hashCode() 返回值相同时,它们通过 equals() 方法比较也应该返回 true。通常来说,所有参与计算 hashCode() 返回值的关键属性,都应该用于作为 equals() 比较的标准。

(3) HashSet的其他操作都是基于HashMap的。

25、ArrayList和LinkedList的区别和优缺点对比?

区别:

(1) ArrayList是实现了基于动态数组的数据结构,LinkedList是基于链表结构。

(2) 对于随机访问的get和set方法,ArrayList要优于LinkedList,因为LinkedList要移动指针。

(3) 对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。

优缺点对比:

(4) 对ArrayList和LinkedList而言,在列表末尾增加一个元素所花的开销都是固定的。对 ArrayList而言,主要是在内部数组中增加一项,指向所添加的元素,偶尔可能会导致对数组重新进行分配;而对LinkedList而言,这个开销是 统一的,分配一个内部Entry对象。

(5) 在ArrayList集合中添加或者删除一个元素时,当前的列表所所有的元素都会被移动。而LinkedList集合中添加或者删除一个元素的开销是固定的。

(6) LinkedList集合不支持 高效的随机随机访问(RandomAccess),因为可能产生二次项的行为。

(7) ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间

(8) 总结:查询用ArrayList,增删改用LinkedList

26、如何实现数组和List的转换?

(1) List转数组:toArray(arraylist.size()方法

在这里插入图片描述

(2) 数组转List:Arrays的asList(a)方法

javaString[] strs = {"a","b","c"};

 List<String> list = Arrays.*asList*(strs);

System.***out\***.println(list);
27、ArrayList和Vecotor的区别?

(1) Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe),而ArrayList的方法不是,由于线程的同步必然要影响性能,因此,ArrayList的性能比Vector好。

(2) 当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小,这样,ArrayList就有利于节约内存空间。

28、Array和ArrayList有何区别?

(1) 存储数据类型不同:Array可以存储基本型数据,也可以存储对象;而ArrayList只能存储对象数据;

(2) 是否支持异构数据不同:Array只能存储相同类型的数据,不支持异构数据的存储;而ArrayList可以存储不同类型的数据,支持异构数据的存储;

(3) 长度是否可以改变:Array一旦创建,其长度不能改变;而ArrayList的长度可以在程序运行期间动态改变;

29、在Queue中poll()和remove()有什么区别?

(1) Queue接口与List、Set同一级别,都是继承了Collection接口。

(2) Queue接口表示队列,其是一种数据结构。队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。

(3) remove() : 移除队列头的元素并且返回,如果队列为空则抛出异常

(4) poll() : 移除队列头的元素并且返回,如果队列为空则返回null

(5) 区别:在移除队列头元素时,当队列为空的时候,用remove()方法会抛出异常,用poll()方法则会返回null

(6) 备注:1和2点属于了解内容,回答问题可以不用回答

30、哪些集合类线程是安全的?

(1) Vector:就比Arraylist多了个同步化机制(线程安全)。

(2) Hashtable:就比Hashmap多了个线程安全。

(3) ConcurrentHashMap:是一种高效但是线程安全的集合。

(4) Stack:栈,也是线程安全的,继承于Vector

(5) ConcurrentHashMap ,Map接口下实现线程安全的类

(6) 了解内容:线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。

31、迭代器Iteraotr是什么?

(1) 迭代器(Iterator)模式,又叫做游标模式,它的含义是,提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。

​ 注意:Java的集合框架的集合类,也称为容器类。

(2) 从定义上看,迭代器是为容器而生,它本质上就是一种遍历的算法。因为容器的实现千差万别,很多时候不可能知道如何去遍历一个集合对象的元素。Java为我们提供了使用迭代的Iterator接口,

(3) 简单的说,迭代器就是一个接口Iterator,实现了该接口的类就叫做可迭代类,可以通过这些类和接口实现对集合元素的遍历。

32、Iterator如何使用?有什么特点?

(1) Iterator的使用

​ ① 通过集合对象的iterator()方法获取迭代器对象

​ ② 循环遍历,在循环条件部分判断迭代器hasNext()方法的返回值是否为真,如果是真,则在循环中使用迭代器对象的next()方法获取当次循环要遍历的迭代器对象

​ ③ 例如:

   List<String> list = **new** ArrayList<String>() {

   {add("a");add("b");add("c");

   }

  };

  Iterator<String> it = list.iterator();

  **while**(it.hasNext()) {

   String s = it.next();

}

(2) Iterator的特点:

​ ① Iterator遍历集合元素的过程中不允许线程对集合元素进行修改,否则会抛出ConcurrentModificationEception的异常。

​ ② Iterator遍历集合元素的过程中可以通过remove方法来移除集合中的元素。

​ ③ Iterator必须依附某个Collection对象而存在,Iterator本身不具有装载数据对象的功能。

​ ④ Iterator.remove方法删除的是上一次Iterator.next()方法返回的对象。

33、ListIterator和Iterator有什么区别?

(1) ListIterator有add()方法,可以向List中添加对象,而Iterator不能;

(2) ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator不可以;

(3) ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能;

(4) 都可实现删除对象,但是ListIterator可以实现对象的修改,set()方法可以实现。Iierator仅能遍历,不能修改;

34、怎么确保一个集合不能被修改?

(1) 可以使用 Collections. unmodifiableCollection(Collection c) 方法来创建一个只读集合,这样改变集合的任何操作都会抛出 Java.lang.UnsupportedOperationException 异常。

(2) 实例:

List<String> list = **new** ArrayList<>();

 list.add("x");

 Collection<String> clist =  Collections.*unmodifiableCollection*(list);

 clist.add("y"); // 运行时此行报错

System.***out\***.println(list.size());
35、并发和并行有什么区别?

(1) 并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。

(2) 并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。

(3) 并行在多台处理器上同时处理多个任务,并发是在一台处理器上“同时”处理多个任务。

(4) 通俗的例子理解并发并行(此处不用回答)

你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。 (不一定是同时的)

你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。

36、线程和进程的区别?

(1) 根本区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位

(2) 在开销方面:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。

(3) 所处环境:在操作系统中能同时运行多个进程(程序);而在同一个进程(程序)中有多个线程同时执行(通过CPU调度,在每个时间片中只有一个线程执行)

(4) 内存分配方面:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源。

(5) 包含关系:没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。

37、守护线程是什么?

(1) 守护线程,是运行在后台的一种特殊线程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。

(2) 守护线程的特点:如果进程中的其它用户线程都已经结束,那么守护线程也就结束并退出JVM。简言之,就是守护线程具备自动结束生命周期的作用。

(3) 相关内容:

① 如何创建守护线程:

Thread t = new Thread( ()->{

 线程操作内容

});

t.setDaemon(true);

t.start();

备注:守护线程的设置必须在该线程调用start()方法之前,否则会报出IllegalThreadStateException异常

38、线程创建有几种方式?

(1) 第一种为继承Thread类;

在这里插入图片描述

(2) 第二种为实现Runnable接口,创建线程时必须创建Thread类的实例,通过Thread类的构造函数将Runnable对象转为Thread对象;

在这里插入图片描述

在这里插入图片描述

(3) 第三种为实现Callable接口,创建线程时需要一个FutureTask对象,再使用Thread类的构造函数包装构建线程,这种方式可以获取线程执行后的返回值

在这里插入图片描述

在这里插入图片描述

39、说一下runnable和callable有什么区别?

(1) 两者最大的不同点是:实现Callable接口的任务线程能返回执行结果;而实现Runnable接口的任务线程不能返回结果;

(2) Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛出

(3) 备注:Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取‘将来’结果;当不调用此方法时,主线程不会阻塞!

40、线程有哪些状态?

(1) 新建状态(New): 当用new操作符创建一个线程时, 例如new Thread®,线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码

(2) 就绪状态(Runnable):一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程。当start()方法返回后,线程就处于就绪状态。

(3) 运行状态(Running):当准备就绪的线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.

(4) 阻塞状态(Blocked)

​ 线程运行过程中,可能由于各种原因进入阻塞状态:

A、线程通过调用sleep方法进入睡眠状态;

B、线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;

C、线程试图得到一个锁,而该锁正被其他线程持有;

D、线程在等待某个触发条件;

所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态

(5) 死亡状态(Dead)

​ 有两个原因会导致线程死亡:

  1. run方法正常退出而自然死亡,

  2. 一个未捕获的异常终止了run方法而使线程猝死。

​ 备注:为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false.

在这里插入图片描述

41、线程的sleep()方法和Object的wait()方法有什么区别?

(1) sleep()方法是线程Thread类的静态方法,调用此方法会让当前线程暂停执行指定时间.将CPU时间片分给其他线程,但是对象的锁依然保持.休眠时间结束后会自动恢复到就绪状态;

(2) wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁,线程暂停执行,进入对象的等待池,只有调用对象的notify()方法或者notifyAll()方法时,才能唤醒等待池中的线程进入等锁池,如果线程重新获得对象的锁就可以进入到就绪状态;

42、notify()和notifyAll()有什么区别?

notify()是唤醒线程等待池中的随机一个线程;而notifyAll()是唤醒线程等待池中的所有线程

备注:以下部分不用回答。理解即可

如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁

当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争

优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。

相关概念:

锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中。

等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁后,进入到了该对象的等待池中

43、线程的run()和start()有什么区别?

(1)run()相当于线程的任务处理逻辑的入口方法,它由Java虚拟机在运行相应线程时直接调用,而不是由应用代码进行调用。

(2)start()的作用是启动相应的线程。启动一个线程实际是请求Java虚拟机运行相应的线程,而这个线程何时能够运行是由线程调度器决定的。start()调用结束并不表示相应线程已经开始运行,这个线程可能稍后运行,也可能永远也不会运行

44、创建线程池有几种方式?

(1) newSingleThreadExecutor

创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

(2) newFixedThreadPool

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

(3) newCachedThreadPool

创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

(4) newScheduledThreadPool

创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

备注:线程池概念:不用回答

一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。 例如,线程数一般取cpu数量+2比较合适,线程数过多会导致额外的线程切换开销。

45、线程池有几种状态?

(1) RUNNING**:运行状态**

​ ① 状态说明:线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。

​ ② 状态切换:线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0!

(2) SHUTDOWN**:关闭状态**

​ ① 状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。

​ ② 状态切换:调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。

(3) STOP**:停止状态**

​ ① 状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。

​ ② 状态切换:调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP。

(4) TIDYING**:整理状态**

​ ① 状态说明:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。

​ ② 状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。
当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。

(5) TERMINATED**:终止状态**

​ ① 状态说明:线程池彻底终止,就变成TERMINATED状态。

​ ② 状态切换:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。

在这里插入图片描述

46、线程池中submit()和execute()有什么区别?

(1) 线程池中的execute方法大家都不陌生,即开启线程执行池中的任务。还有一个方法submit也可以做到,它的功能是提交指定的任务去执行并且返回Future对象,即执行的结果

(2) submi()和execute()区别:

​ ① 方法接收的参数不同:

submit()可以接收Runnable接口对象,也可以接收Callable接口对象;

execute()只能接收Runnable接口对象

​ ② 方法返回值不同:

submit()有Future类型的返回值

execute()没有返回值

​ ③ 能够将线程中的异常交给外部调用者处理

Submit()比exeucte()更方便将线程执行中的异常交给外部调用者处理,只要调用submit()方法返回的Fature对象的get()方法即可捕获处理线程执行中产生的异常对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hu9a5cvm-1618058246216)(C:\Users\admid\AppData\Roaming\Typora\typora-user-images\image-20210410202450692.png)]

47、在java中怎么保证多线程的运行安全?

(1) 使用synchronized关键字修饰多线程需要使用的公有资源(可以是共同调用的方法,也可以是共同执行的语句块),依赖JVM实现锁,因此在这个关键字作用对象的作用范围内是同一时刻只能有一个线程进行操作;

示例:

在这里插入图片描述

在这里插入图片描述

(2) 使用LOCK接口类型对象,通过在代码中显示的添加同步锁。这种方式需要用代码创建Lock接口对象,在同步资源开始时调用lock()获取锁,执行完毕后调用unlock()释放锁。ReentrantLock是Lock接口的一个典型实现类

在这里插入图片描述

48、什么是死锁?如何防止死锁?

(1) 概念:线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。当线程进入对象的synchronized代码块时,便占有了资源,直到它退出该代码块或者调用wait方法,才释放资源,在此期间,其他线程将不能进入该代码块。当线程互相持有对方所需要的资源时,会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁。

(2) 死锁通俗的例子(这个不用回答,但可以比较形象的类比死锁的概念,在面试中可以适当用这样的生活中例子来解释一些比较抽象的java概念):假设张三和李四两个不同班级的同学今天都承接打扫卫生的工作任务,在工作中,需要用到拖布和扫把(这些是学校的公有资源且各自只有一个),张三和李四在同一时间开始工作,张三先去拿扫把再去取拖布,李四先取了拖布再拿扫把。这个时候就出现了张三拿了扫把,李四拿了拖布,两个人都需要对方的工具才能开始打扫卫生,但是两个人都不愿意放下手上已经拿到的工具,此时就是死锁(换句话说张三,李四谁也干不了活)

(3) 避免死锁的方法:

​ ① 从加锁顺序解决:当多个线程需要相同的一些锁,但是按照不同的顺序加锁,死锁就很容易发生。如果能确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生。

​ ② 给锁上添加时限:获取锁的时候加一个超时时间,这也就意味着在尝试获取锁的过程中若超过了这个时限该线程则放弃对该锁请求。若一个线程没有在给定的时限内成功获得所有需要的锁,则会进行回退并释放所有已经获得的锁,然后等待一段随机的时间再重试。

​ ③ 进行死锁检测

49、ThreadLocal是什么?有哪些使用场景?

(1) ThreadLocal概念:

​ ① ThreadLocal,很多地方叫做线程本地变量,或者叫做线程本地存储,其用于为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量

(2) 使用场景:

​ ① 线程使用的数据对象不需要在多线程之间共享;

​ ② 线程使用的数据对象需要在线程内部调用的多个方法之间传递

​ ③ 例如线程A和B有都需要各自数据data,且这些数据放在线程外部保存并准备在线程内部的多个方法中使用(你可以理解为线程中a()方法用于存储数据data,b()方法负责使用),那么如果保证每个线程存储的数据data不会被别的线程所修改。此时就可以使用ThreadLocal,它本质是一个Map,以每个线程的标识为key,可以存储任何数据值。因为每个线程只能获取自身的标识,不能获取别的线程标识,所以多个线程可以使用它存储各自的数据,彼此之间不发生影响

50、什么是反射?

(1) 概念: Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。而这也是Java被视为动态(或准动态,为啥要说是准动态,因为一般而言的动态语言定义是程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。)语言的一个关键性质。

(2) 作用:我们知道反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!

51、什么是java的序列化?什么时候需要使用对象的序列化?

(1) Java序列化就是指对象的序列化,其本质就是通过对象流将程序中的对象信息输出到指定位置(序列化)以及把指定位置的对象信息读取到内存中重新构建一个对象(反序列化)

(2) 什么时候需要使用序列化:

假如我们有两台计算机中都在执行java程序,其中A计算机上有一个张三学生对象信息,这个信息B计算机想进行使用。但是AB是两台不同的计算机,是无法直接调用的。那么此时我们就可以使用对象的序列化了。将A计算机上的学生张三对象序列化之后通过网络传输到B计算机,B计算机经过反序列化就可以获取和A计算机上一样的学生对象张三进行使用。

52、动态代理是什么?有哪些应用?

(1) 动态代理:当想要给实现了某个接口的类中的方法,加一些额外的处理。比如说加日志,加事务等。可以给这个类创建一个代理,故名思议就是创建一个新的类,这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外处理的新类。这个代理类并不是定义好的,是动态生成的。具有解耦意义,灵活,扩展性强。

(2) 动态代理实现:首先必须定义一个接口,还要有一个InvocationHandler(将实现接口的类的对象传递给它)处理类。再有一个工具类Proxy(习惯性将其称为代理类,因为调用他的newInstance()可以产生代理对象,其实他只是一个产生代理对象的工具类)。利用到InvocationHandler,拼接代理类源码,将其编译生成代理类的二进制码,利用加载器加载,并将其实例化产生代理对象,最后返回。

(3) 动态代理的应用:Spring的事务管理器(给业务方法上添加事务的回滚,提交等行为)

53、为什么要使用对象的克隆?

在java程序中,有时候我们需要多个类型相同的对象,例如我们需要100个学生对象,这100个学生都是男生,年龄都是18岁,就是学号和姓名不同。如果我们按照原始方式每个学生对象单独创建(也就是new),那么效率是比较低的。更好的方式是我们只需要先创建一个学生,赋予它学号,姓名,性别和年龄。然后以第一个学生对象去克隆99个新的学生对象,这些克隆的学生对象只需要修改学号和姓名,不需要单独在设置年龄和性别。那么效率比原始创建的方式要高。所以在这种场合下需要使用对象克隆。

54、如何实现对象的克隆?

(1) 方法一:通过重写clone()方法,该方法要求类实现Cloneable接口,且不能实现深克隆

在这里插入图片描述

(2) 方法二:通过对象的序列化和反序列化实现,此种方式可以实现深克隆

55、深克隆和浅克隆的区别是什么?

(1) 浅克隆,在clone对象时,只会把基本数据类型的数据进行复制过去;如果是引用类型,只会把引用复制过去,也就是被克隆对象和原始对象信息,共同引用一个引用类型的属性。

(2) 深克隆:在克隆时,会把基本数据类型的数据和引用类型的数据,同时复制。克隆对象和原始对象不共同引用一个引用类型

56、throw和throws的区别?

(1) 含义不同:throw是抛出异常对象,而throws是方法运行有可能会抛出异常的声明

(2) 书写位置不同:throw写在要抛出异常的方法体中,而throws写在要抛出异常的方法签名之后

(3) 语法不同:throw之后是异常的对象,而throws之后是要抛出异常的类型名称

(4) throw只能抛出一个异常对象,而throws可以声明多种不同类型的异常抛出声明

57、final和finally和finalize有什么区别?

(1) final是修饰符,可以修饰类,属性和方法,分别表示终态类,常量和终态方法

(2) finally是异常处理关键字,表示异常处理完毕最后 一定要执行的操作

(3) finalize()是Object继承的方法,表示对象在内存空间释放前要执行的操作

58、try-catch-finally中那个部分可以省略?

catch或finally都可以省略,也就是可以是try{}catch{},也可以是try{}finally{}

59、try-catch-finally中,如果catch中return了,那么finally还会执行吗?

finally还是会执行,因为finally表示最后一定要执行,在之前出现return,那么也会在return之前先执行finally然后在return

60、常见的异常类有哪些?

(1) NullPointerException:空指针异常

(2) ArrayIndexOutOfBoundsException:数组下标越界异常

(3) ClassNotFoundException:类未找到异常

(4) IOException:输入输出流异常

的属性。

(2) 深克隆:在克隆时,会把基本数据类型的数据和引用类型的数据,同时复制。克隆对象和原始对象不共同引用一个引用类型

56、throw和throws的区别?

(1) 含义不同:throw是抛出异常对象,而throws是方法运行有可能会抛出异常的声明

(2) 书写位置不同:throw写在要抛出异常的方法体中,而throws写在要抛出异常的方法签名之后

(3) 语法不同:throw之后是异常的对象,而throws之后是要抛出异常的类型名称

(4) throw只能抛出一个异常对象,而throws可以声明多种不同类型的异常抛出声明

57、final和finally和finalize有什么区别?

(1) final是修饰符,可以修饰类,属性和方法,分别表示终态类,常量和终态方法

(2) finally是异常处理关键字,表示异常处理完毕最后 一定要执行的操作

(3) finalize()是Object继承的方法,表示对象在内存空间释放前要执行的操作

58、try-catch-finally中那个部分可以省略?

catch或finally都可以省略,也就是可以是try{}catch{},也可以是try{}finally{}

59、try-catch-finally中,如果catch中return了,那么finally还会执行吗?

finally还是会执行,因为finally表示最后一定要执行,在之前出现return,那么也会在return之前先执行finally然后在return

60、常见的异常类有哪些?

(1) NullPointerException:空指针异常

(2) ArrayIndexOutOfBoundsException:数组下标越界异常

(3) ClassNotFoundException:类未找到异常

(4) IOException:输入输出流异常

(5) ArithmeticException:算术异常

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值