Java面试题 第二部分 Java基础篇

开始本部分之前,我们先解答下上一个章节最后遗留的两个问题:

tomcat的类加载机制违反了双亲委派原则,为什么,又是如何进行类加载的

__why?__双亲委派机制保证了类加载的唯一性,是类加载安全性的保证,但是,tomcat作为web容器,可能会部署多个项目工程,不同的项目工程对于第三方类库依赖的版本不同,需要单独的加载,所以tomcat的类加载机制违反了双亲委派原则。
在这里插入图片描述

  • JVM类加载器组成:
  1. Bootstrap ClassLoader:负责加载JAVA_HOME\lib目录中并且能被虚拟机识别的类库
  2. Extension ClassLoader:负责加载JAVA_HOME\lib\ext目录中的类库
  3. Application ClassLoader:负责加载用户类路径(Classpath)上所指定的类库
  • Tomcat类加载器的组件:3个基础类加载器(common、catalina、shared)和n个Web应用类加载器
  1. Common类加载器:实现了Jar包在应用服务器与Web应用之间的共享
  2. Shared类加载器:实现了Jar包在Web应用之间的共享
  3. Catalina类加载器:加载服务器依赖的类
  4. Web应用类加载器:每个web应用独有的,web应用会对应一个StandardContext 一个WebappLoader 一个WebappClassLoader
    违背的点:WebappClassLoader加载自己的目录下的class文件,不会传递给父类加载器
    扩展:因为WebAppClassLoader加载过程违背了双亲委派模型,如何在上层的ClassLoader加载过程中获取到由WebAppClassLoader加载的类呢->线程上下文类加载器(彻底打破了双亲委派模型)
什么叫做持久代,持久代在JDK1.8中有什么变化

持久代作用:持久代是在方法区(静态区)中,用以加载和存放class文件,存放类信息。
持久代内存获取:持久代因为是方法区的一部分,所以其占用的内存是从jvm内存中获取的
常量池与持久代:常量池在jdk7之前一直在持久代中,但是jdk7开始,常量池已经不在持久代之中进行分配了,而是移到了堆中。常量池和持久代从此隔离。
持久代移除过程:1.8 移除持久代,设置Metaspace Metaspace并不在虚拟机内存中而是使用本地内存。


1 Synchronized

字节码的实现:monitorenter / monitorexit

sychronized修饰范围带来的影响(抓住下面两个关键点):
(1)synchronized可以修饰静态/非静态方法、静态/非静态代码块、修饰对象、修饰Class。
(2)关键字后面的大括号包起来的部分。这一部分是不能被多线程同时访问的。例如下图,只需要AB互斥访问即可。

class A {
	synchronized void AMethed() {}
	synchronized void bMethed() {}	
	void CMethed() {}
}

synchronized锁升级的过程,说了偏向锁到轻量级锁再到重量级锁,然后问我它们分别是怎么实现的,解决的是哪些问题,什么时候会发生锁升级。
偏向锁(线程级别的偏心的锁,单一线程持续持有锁对象的时候使用,对象头,栈帧添加threadId)
线程少,持有竞争资源时间短的情况线程自旋不阻塞,轻量级锁
自旋时间过长,自旋次数超限制,同时存在第三线程竞争的情况的时候,升级为重量级别锁

2 volatile
1 关键字解决了什么问题,实现原理是什么(缓存一致性协议,刷主存)

1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
2)禁止进行指令重排序。(volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作)

原子性(完成或者失败)/可见性(多线程下资源的共享可见性,包含正确性)/有序性(语句执行顺序),volatile保证了可见性,不能保证原子性,Java内存模型只保证了基本读取和赋值是原子性操作,如果要实现更大范围操作的原子性,可以通过synchronized和Lock来实现。由于synchronized和Lock能够保证任一时刻只有一个线程执行该代码块,那么自然就不存在原子性问题了,从而保证了原子性。x=x+1并不是原子性质的操作,分为两个原子操作,分别是读取x的值以及对x重新赋值,当x阻塞在读取阶段的时候,即便存在其他的线程修改了x的值,x的值也不会得到更新。

3 Synchronized和lock的异同

  1. synchronized(Java内置关键字,在jvm层面),Lock是个java类;
  2. synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
  3. synchronized会自动释放锁,Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
  4. 用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
  5. synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平AQS的CLH(两者皆可)
  6. Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。
  7. sychronied独享锁,lock可以做共享锁(AQS的state)

小结

公平锁/非公平锁 (ReEntrantLock可以通过AQS实现公平锁):是否按照申请锁的顺序分配锁资源

可重入锁(synchronized&ReEntrantLock)线程可以重复进入已经持有锁资源的方法块 https://www.cnblogs.com/dj3839/p/6580765.html(写得好)

独享锁(互斥锁)/共享锁(读写锁)(ReEntrantLock&Synchronied是独享锁,ReadWriteLock是共享锁通过AQS实现)

乐观锁/悲观锁(是态度,不是确切的指某一种锁,乐观无锁编程=CAS,悲观全加上锁)

分段锁(是设计模式,比如ConcurrentHashMap通过Segments分别加锁的方式来实现分段锁)

偏向锁/轻量级锁/重量级锁(针对Synchronized而言)

自旋锁(采用循环的方式尝试获得锁,消耗CPU资源,不产生上下文切换,不需要转入内核态,就是死循环的方式来等待)

AQS:抽象队列同步器的内容和作用
包含一个int类型的State标识和CLH双向队列:
公平锁&非公平锁的实现:通过CLH队列先进先出的特点来实现公平锁,非公平锁通过notify()竞争
独享锁&共享锁 :独享锁略,共享锁通过CAS的tryAcquireShare(int state)的方式来实现,state标识共享的资源个数,可以参与共享的线程数

语法

1 拆箱装箱的原理

装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型。装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的。注意这里的valueOf方法,对于Integer对象而言在-

128-127范围内的整形会存储到IntegerCache当中。

2 1.8还采用了红黑树,讲讲红黑树的特性,为什么人家一定要用红黑树而不是AVL、B树之类的?(**)

相对AVL旋转成本低,相对于B树,红黑树操作全在内存,数据库索引使用B+树,B树多路查找树,红黑二叉树

3 深克隆和浅克隆(克隆对象的引用的克隆)

浅克隆是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。
深克隆不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。

4 用不同的方法实现单例模式懒汉(线程安全【双重检验/静态内部类】/不安全)/饿汉(静态变量/静态代码块)

5 throwable、Error、Exception、RuntimeException 区别和联系各是什么?

Throwable类是Java语言中所有错误和异常的超类。(Java虚拟机抛出,Java throw语句抛出)
Error是Throwable的子类,合理的应用程序不应该尝试捕获的严重问题
Exception类及其子类是Throwable的一种子类,它表示合理的应用程序可能想要捕获的条件
RuntimeException是在Java虚拟机的正常操作期间可以抛出的那些异常的超类。RuntimeException及其子类是未经检查的异常
在这里插入图片描述

6 什么是检查异常,不受检查异常,运行时异常?并分别举例说明。

检查异常:必须处理的异常,ioexception filenotfound throw或者try catch
不受检查异常:空指针异常,数据越界业务处理 ERROR&RuntimeException
运行时异常:RuntimeException类及其子类异常
非运行时异常:Exception类及其子类

7 try、catch、finally语句块的执行顺序

(1)try return表达式 finaly return结果返回值
(2)try catch exception逻辑 exception中return逻辑 finally return结果返回值

8 对Java中Runtime的了解

JVM的运行时环境&饿汉加载的单例模式&多次重载的excute方法(返回process进程,创建子进程执行linux命令)&借助waitfor方法达到子进程同步的效果

9 stringbuffer和stringbuilder的区别

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

10 stringbuffer的扩容

扩容算法:使用append()方法在字符串后面追加东西的时候,如果长度超过了该字符串存储空间大小了就需要进行扩容:
默认2倍+2,若不足以实际所需要的容量为主
构建新的存储空间更大的字符串,将旧的复制过去

11 String和stringbuffer的区别

(1)String:是对象不是原始类型.为不可变对象,一旦被创建,就不能修改它的值.对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去.String 是final类,即不能被继承
(2)StringBuffer:是一个可变对象,修改的时不用重新建立对象。它只能通过构造函数来建立对象被建立以后,在内存中就会分配内存空间,并初始保存一个null.向StringBuffer中赋值的时候可以通过它的append方法.

12 Collections.sort底层排序方式?

Collections.sort->Arrays.sort->ComparableTimSort.sort,然后才到sort方法和它的定义,排序主体binarySort的方法,这是排序方法的实现,是通过调用Object的CompareTo进行比较的。

13 java事件机制包括哪三个部分(一种设计模式,监听者模式)

事件监听器、事件源、事件。
1、事件。一般继承自java.util.EventObject类,封装了事件源对象及跟事件相关的信息。
2、事件监听器。实现java.util.EventListener接口,注册在事件源上,当事件源的属性或状态改变时,取得相应的监听器调用其内部的回调方法。
3、事件源。事件发生的地方,因为事件监听器要注册在事件源上,所以事件源类中应该要有盛装监听器的容器(List,Set等等)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值