Java面试题整理

经过自己这段时间的面试整理的一些常见的面试题(会不断补充),希望自己早日面试成功!

1、Java怎么实现多线程同步?

为何要使用同步:

  Java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前被其他线程调用,从而保证了该变量的唯一性和准确性。

(1)同步方法

  即有synchronized关键字修饰的方法。

  由于Java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。

  代码如:

  public synchronized void save(){}

  注:synchronize关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类。

(2)同步代码块

  即有synchronized关键字修饰的语句块。

  被该关键字修饰的语句块会自动被加上内置锁,从而实现同步。

  代码如:

  public void save(int money){

    synchronized(this){

      需要修饰的具体代码块...

    }

  }

(3)使用特殊变量(volatile)实现线程同步

(4)使用重入锁实现线程同步

(5)使用局部变量实现线程同步

(6)使用阻塞队列实现线程同步

(7)使用原子变量实现线程同步

这里只针对具体有哪些方法做个总结,每种方法的具体实现请移步此博客:(https://www.cnblogs.com/XHJT/p/3897440.html)。

2、字节流和字符流转换的桥梁是什么?

InputStreamReader(把字节流转向字符流)

OutputStreamWriter(把字符流转换为字节流)

具体详见:(https://blog.csdn.net/baidu_41924510/article/details/80146168)。

3、Java中线程安全是什么意思,HashMap是线程安全的吗?

我对Java多线程的了解不多,只简单地用过两次,我有次实现的一个缓存的功能,用一个HashMap做个缓存的容器,这个在单线程的条件下使用没有问题,如果需要支持多线程就要使用支持并发的集合容器。我知道有个ConcurentHashMap可以做到线程安全,但是我自己做测试没发现ConcurentHashMap和HashMap有什么具体的差别,我就去看文档,了解了一些东西,现在对并发使用懂的不太多。

这个是在百度贴吧看到的一个小伙伴分享的回答技巧,感觉这样回答确实挺有逼格的所以就摘抄了下来,附上链接(http://tieba.baidu.com/f?kz=5563378214&mo_device=1&ssid=0&from=1086k&uid=0&pu=usm@1,sz@1320_2002,ta@iphone_1_12.2_2_9.1&bd_page_type=1&baiduid=261BF40508439E890450C10A775CADDC&tj=www_normal_10_20_10_title&referer=m.baidu.com?pn=0&&red_tag=t0102336054)。

下面是准确答案:

就是线程同步的意思,就是当一个程序对一个线程安全的方法或者语句进行访问的时候,其他的就不能再对它进行操作了,必须等到这次访问结束以后才能对这个线程安全的方法进行访问。

什么叫线程安全:

如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果都和单线程运行结果是一样的,而且其他变量的值也和预期的是一样的,就是线程安全的。

或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致改接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。

线程安全问题都是由全局变量及静态变量引起的。

若每个线程中对全局变量、只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般就需要考虑线程同步,否则就可能影响线程安全。

存在竞争的线程不安全,不存在竞争的线程就是安全的。

HashMap是非线程安全的。

4、Java中的ArrayList是如何实现动态扩容的?

这个问题你可以简单的回答,将原有数组拷贝到一个新的更大的数组中,然后抛弃原来的数组。不过你还可以加上这样一句话,我自己对这个问题也很好奇,我就看了下源代码,然后自己实现了这个动态扩容的功能,就是如果需要收缩的时候需要手动释放内存。这个问题的回答让面试官对你的学习能力有了认可。当然如果他还要继续顺着这个问题问你,Java中有垃圾收集器可以自动释放内存,你为什么要自己手动释放呢,你就可以回答,因为这个数组的内存现在不归垃圾收集器管理了,需要自己管理。这就是实现这类功能需要注意的问题。

(来自上面贴吧那个小伙伴)

5、Java的switch语句是否支持String类型的?

对Java有了解的都知道Java从JDK7开始支持,如果你只简单的回答这个问题,并不能吸引面试官。这个时候要展示你的思考能力和独特的见解。你可以这样回答,Java在JDK6之前是不支持的,从JDK7开始支持,主要是因为使用switch(String)的时候太多了,Oracle为了提高开发效率引入了这个新的语言特性,这是需求驱动的结果。这也说明昨天不支持的东西可能下个版本就支持了,这正是软件开发行业日新月异的特色。如果你这样回答逼格就提升了不少,你从面试中胜出的可能性就增加了。

(来自上面贴吧那个小伙伴)

6、什么是线程池?为什么要使用它?

创建线程需要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数有限。为了避免这些问题在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫做工作线程。从JDK1.5开始,Java API提供了Executor框架让你可以创建不同的线程池。比如单线程池,每次处理一个任务;数目固定的线程池(一个适合很多生存期短的任务的程序的可扩展线程池)。

引用自(http://f.dataguru.cn/java-913890-1-1.html)。

7、Java实现几种常见的排序方法。

冒泡排序:

 1 public static void bubbleSort(int[] a) {
 2     int temp;
 3     int size = a.length;
 4     for(int i=1; i<size; i++) {
 5         for(int j=0; j<size-i; j++) {
 6             if(a[j] < a[j+1]) {
 7                 temp = a[j];
 8                 a[j]=a[j+1];
 9                 a[j+1]=temp;
10             }
11         }
12         for(int aa : a)
13             System.out.print(aa+",");
14         System.out.println();
15     }
16 }

 

详见(https://www.cnblogs.com/wangmingshun/p/5635292.html)。

8、HashMap的工作原理?

HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用建对象的hashCode()方法来计算hashcode,然后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。HashMap在每个链表节点中储存键值对对象。

9、当两个不同的键对象的hashcode相同时会发生什么?

它们会储存在同一个bucket位置的链表中。键对象的equals()方法用来找到键值对。

10、HashMap和HashTable的区别?

HashMap和HashTable都实现了Map接口,主要的区别有:线程安全性,同步(synchronization),以及速度。

(1)HashMap几乎可以等价于HashTable。除了HashMap是非synchronize(同步)的,并可以接受为null(空)的键(key)和值(value),而HashTable则不行

(2)HashMap是非同步的,而HashTable是同步的,意味着HashMap是线程不安全的,而HashTable是线程安全的,多个线程可以共享一个HashTable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java5提供了ConcurentHashMap,他是HashTable的替代,比HashTable的扩展性更好。

(3)由于HashTable是线程安全的也是同步(synchronized)的,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过HashTable

(4)HashMap不能保证随着时间的推移Map中的元素次序是不变的。

11、我们能否让HashMap同步?

HashMap可以通过下面的语句进行同步:

map m = Collections.synchronizeMap(hashMap);

12、HashMap和HashSet的区别?

(1)HashMap实现了Map接口,HashSet实现了Set接口

(2)HashMap储存键值对,HashSet仅仅储存对象

(3)HashMap使用put()方法将元素放入map中,HashSet使用add()方法将元素放入set中

(4)HashMap中使用键对象来计算hashcode值,HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false

(5)HashMap比较快,因为是使用唯一的键来获取对象,HashSet较HashMap来说比较慢

关于HashMap的更多知识详见博客(https://blog.csdn.net/suifeng629/article/details/82179996)。

13、HashMap和TreeMap的区别?

HashMap和TreeMap都是线程不安全的,都是key不允许重复,value允许重复。

HashMap键可以为null,TreeMap键不能为null。

HashMap中元素的排列顺序是不固定的,而 TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap。

HashMap适用于在Map中插入、删除和定位元素。
Treemap适用于按自然顺序或自定义顺序遍历键(key)。

HashMap通常比TreeMap快一点(树和哈希表的数据结构使然),建议多使用HashMap,在需要排序的Map时候才用TreeMap。

Java中HashMap和TreeMap的区别深入理解:(https://www.cnblogs.com/williamjie/p/9099130.html)。

 

HashMap:基于哈希表实现,数组方式存储key/value,允许null作为key和value,不保证元素迭代顺序是按照插入时的顺序,key的hash值是先计算key的hashcode值,然后再进行计算,每次容量扩容会重新计算所以key的hash值,会消耗资源,要求key必须重写equals和hashcode方法

默认初始容量16,加载因子0.75,扩容为旧容量乘2,查找元素快,如果key一样则比较value,如果value不一样,则按照链表结构存储value,就是一个key后面有多个value;

TreeMap:基于红黑二叉树的NavigableMap的实现,不允许null,存入TreeMap的元素应当实现Comparable接口或者实现Comparator接口,会按照排序后的顺序迭代元素,两个相比较的key不得抛出classCastException。主要用于存入元素的时候对元素进行自动排序,迭代输出的时候就按排序顺序输出

14、Java中堆内存和栈内存中分别存放什么?

堆内存:存放所有new出来的对象

栈内存:存放基本类型的变量,对象的引用和方法的调用,遵循先入后出的规则

浅析Java中堆内存和栈内存的区别:(https://blog.csdn.net/jxq0816/article/details/78757952)。

15、什么是死锁?

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

16、ArrayList和LinkedList的区别?

1.ArrayList是实现了基于动态数组的数据结构,LinkedList是基于链表结构。
2.对于随机访问的get和set方法,ArrayList要优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。

所以在我们对元素进行查操作时用ArrayList,进行增删操作的时候最好用LinkedList。

17、throw和throws的区别?

throw写在方法内部,throws写在方法声明上,throws是方法声明异常,throw是方法内部抛出异常,throw语句在try中要注意,放在最后边。

18、String、StringBuilder和StringBuffer的区别?

String在给已创建的对象进行追加字符时会产生一个新的对象,追加前和追加后不是同一个对象。

StringBuilder和StringBuffer在使用其特定的方法append给自己给已创建的对象进行追加字符时不会产生一个新对象,而是把追加的字符添加到已创建的对象的值的后面,追加前和追加后是同一个对象。

StringBuilder是StringBuffer的轻量版,StringBuffer所拥有的方法StringBuilder基本都有,StringBuilder是线程不安全的,StringBuffer是线程安全的。

三者性能:StringBuilder > StringBuffer > String

19、JVM(虚拟机)的工作原理?

java源文件——java编译器——class字节码文件——内装载器(JVM)——字节码校验器(JVM)——解释器(JVM)——操作系统平台

20、&&与&的区别?

&&两边条件,只要第一个条件为false,后边就不会再判断。

&两边条件,第一个不管为true、false,后边条件都会判断。

21、方法重写与方法重载的区别?

方法的重写:子类中的方法与父类中的方法相同(方法名称、参数列表、返回类型)。

方法的重载:一个类中的方法与另一个方法相同,参数列表不同。

重写体现的是父类与子类方法之间的关系,重载体现的是一个类的内部方法之间的关系。

22、final关键字的用法?

1.放在属性前,表示为常量。

2.放在方法前,表示为终态方法,不能被重写。

3.放在类前,表示为终态类,不能被继承。

23、什么是流?

流是Java程序与目标文件通讯的一个渠道,进行数据传输,在流中的数据信息就是Java程序要读取目标文件的信息或者向目标文件写入信息。

 24、流的分类?

1.字节流:InputStream(输入流:将数据从数据源中传输到程序中)  OutputStream(输出流:从程序中将数据传输到数据源中)

2.字符流:Reader  Writer

25、Servlet的生命周期?

1.类加载

2.实例化Servlet

3.初始化(调用init()方法)

4.服务(调用service()方法)

5.销毁(调用destroy()方法)

注:Servlet在类加载时创建,项目结束或者出现错误时销毁,不是请求响应后就销毁。

26、JSP三大指令? 

1.page:为当前页面提供处理指令

2.include:include指令表示在JSP编译时插入一个包含文本或者代码的文件,把文件中的文本静态的包含过去。

3.taglib:添加外部标记

27、JSP的两个动作?

<jsp:include>:将文件动态的包含在某一个页面中。

<jsp:forward>:将客户端所发出来的请求,从一个JSP网页转发到另一个JSP页面,相当于将控制权交给了另一个JSP。

28、Spring的常用注解有哪些? 

@Controller(对应的是业务层Bean)、@Service(对应的是业务层Bean)、@Repository(对应数据访问层Bean)、@RequestMapping、@Autowired、@ResponseBody

29、什么是servlet?

Servlet是用Java编写的服务器端程序,而这些Servlet都要实现Servlet这个接口。其主要功能在于交互式的浏览和修改数据,生成动态Web内容。Servlet运行于支持Java的应用服务器中。

30、JAVA中异常的几种处理方式?

1.try catch:try{}中放入可能发生的异常的代码,catch{}中放入对捕获到异常之后的处理,其中e.printStackTrace()的作用就是在控制台打印程序出错的位置及原因。

2.throw、throws:throw是语句抛出异常,出现于函数内部,用来抛出一个具体异常实例。throws是函数方法抛出异常,一般写在方法的头部,用来抛出一些异常,本身不进行解决,抛给抛给方法的调用者进行解决(try catch)。

 31、JAVA中error和exception的异同?

error和exception都是继承于throwable类。

1.error:error是指在正常的情况下,不大可能出现的情况,绝大部分的Error都会导致程序处于非正常的、不可恢复的状态。——不可控,比较难捕获,偏底层环境。

2.exception:exception是在程序编写过程中由于语法错误或者其他逻辑错误产生的异常,分为运行时异常和编译时异常;编译时异常是开发工具在编译期显示捕获的异常,而运行时异常经常与逻辑错误挂钩,是程序员编写程序时产生的错误。(一般异常出现时我们要主动去捕获,并且去想办法修复)——相对可控

32、什么是线程?

线程是操作系统能够进行调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。

程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速。比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成该任务只需要10毫秒。

java在语言层面对多线程提供了卓越的支持,它也是一个很好的卖点。

33、线程和进程有什么区别?

线程是进程的子集,一个进程可以有多个线程,每天线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。

34、如何在java中实现多线程?

1)java.lang.Thread类的实例就是一个线程但是它需要调用java.lang.Runnable接口来执行。(简单来说就是继承Thread类实现多线程)。

2)由于线程类本身就是调用的Runnable接口,所以我们可以继承Thread类或者直接实现Runnable接口来重写run()方法来实现线程。

35、Thread类中的start()和run()方法有什么区别?

1)start()方法用来启动线程,真正实现了多线程运行。这时无需等待run()方法体代码执行完毕,可以直接执行下面的代码;通过调用Thread类的start()方法来启动一个线程,这时此线程是处于就绪状态,并没有运行。然后通过此Thread类调用方法run()来完成其运行操作的,这里方法run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程终止。然后CPU再调度其它线程。

2)run()方法当作普通方法的方式调用。程序还是要顺序执行,要等待run方法体执行完毕后,才可以继续执行下面的代码;程序中只有主线程——这一个线程,其程序执行路径还是只有一条,这样就没有达到写线程的目的。

注意:多线程就是分时利用CPU,宏观上让所有线程一起执行,也叫并发。

36、java中的volatile表示什么?

可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。

java中volatile修饰符是一种用来保证不同线程之间交互的特殊机制。当一个线程修改volatile变量,另一个线程能够看到这个修改。第一个线程通知第二个线程变量已经被修改。

 

转载于:https://www.cnblogs.com/jokerDaMoWang/p/10752045.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值