8道经典Java笔试题,你会多少?
- 1、请说一下,LinkedList和ArrayList、HashMap和HashTbale的区别?
- (1)、LinkedList和ArrayList的区别?
- (2)、HashMap和HashTable的区别?
- 2、谈谈JVM垃圾回收机制,以及代码出现内存泄露的情况?
- (1)、JVM垃圾回收机制
- (2)、内存泄露
- 3、==和equals的区别?举例说明?
- 4、列举几个常用的设计模式,画出三个常用的UML图并说明其优点和使用场景?
- (1)、常用设计模式
- (2)、常用的三种设计模式
- 5、谈谈MVC、MVP、MVVM?
- 1、MVC
- 2、MVP
- 3、MVVM
- 4、备注
- 6、谈谈普通类、抽象类、接口、继承以及实现?
- 7、谈谈线程和进程的区别?
- (1)、什么是进程和线程?
- (2)、进程与线程的区别
- 8、说明异常处理的方式,并谈谈throws、throw、try、catch、finally的区别?
- 1、JAVA异常处理
- 2、try、catch、finally、throw、throws的区别
1、请说一下,LinkedList和ArrayList、HashMap和HashTbale的区别?
(1)、LinkedList和ArrayList的区别?
List是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式,它继承至Collection
。而LinkedList和ArrayList是List的两个重要实现类。
ArrayList:
- ArrayList可以看作是能够自动增长容器的数组
- ArrayList的toArray方法返回一个数组
- ArrayList的asList方法返回一个列表
- ArrayList底层的实现是Array,使用数组扩容实现。
LinkedList:
- 一个双链表,在添加和删除元素时具有比ArrayList更好的性能,但在set和get方面弱于ArrayList,当然,这些对比都是指数据量很大或者操作很频繁。
(2)、HashMap和HashTable的区别?
- 两者父类不同
HashMap
是继承自AbstractMap
类,而HashTbale
是继承Dictionary
类,不过它们都实现了同时实现了map
、Cloneable
(复制)、Serializable
(可序列化)这三个接口。 - 对外提供的接口不同
HashTable
比HashMap
多提供了elments()
和contains()
两个方法。elments()
方法继承自Hashtable
的父类Dictionnary
。elements()
方法用于返回此Hashtable
中value
的枚举。contains()
方法判断该Hashtable
是否包含传入的value
。它的作用与containsValue()
一致。事实上,contansValue()
就只是调用了一下contains()
方法。 - 对
null
的支持不同Hashtable
:key和value都不能为null。HashMap
:key可以为null,但是这样的key只能有一个,因为必须保证key的唯一性;可以有多个key值对应的value为null。 - 安全性不同
HashMap
是线程不安全的,在多线程并发的环境下,可能会产生死锁等问题,因此需要开发人员自己处理多线程的安全问题。Hashtable
是线程安全的,它的每个方法上都有synchronized
关键字,因此可直接用于多线程中。虽然HashMap
是线程不安全的,但是它的效率远远高于Hashtable
,这样设计是合理的,因为大部分的使用场景都是单线程。当需要多线程操作的时候可以使用线程安全的ConcurrentHashMap
。ConcurrentHashMap
虽然也是线程安全的,但是它的效率比Hashtable
要高好多倍。因为ConcurrentHashMap
使用了分段锁,并不对整个数据进行锁定。 - 初始容量大小和每次扩充容量大小不同
- 计算
hash
值的方法不同
2、谈谈JVM垃圾回收机制,以及代码出现内存泄露的情况?
(1)、JVM垃圾回收机制
垃圾收集主要是针对堆和方法区进行。程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后就会消失,因此不需要对这三个区域进行垃圾回收。
1、判断一个对象是否可被回收算法
(1)、引用计数算法
(2)、可达性分析算法
(3)、方法区的回收
(4)、finalize()
方法
2、Java中的四种引用类型
(1)、强引用
(2)、弱引用
(3)、软引用
(4)、虚引用
3、垃圾回收算法
(1)、标记-清除算法
(2)、复制算法
(3)、标记-整理算法
(4)、分代收集算法
4、垃圾回收器
(1)、Serial
收集器
(2)、ParNew
收集器
(3)、Parallel Scavenge
收集器
(4)、Serial Old
收集器
(5)、Parallel Old
收集器
(6)、CMS
收集器
(7)、G1
收集器
5、内存分配策略
- 对象优先在
Eden
分配:大多数情况下,对象在新生代Eden
上分配,当Eden
空间不够时,发起Minor GC
。 - 大对象直接进入老年代:大对象是指需要连续内存空间的对象,最典型的大对象是那种很长的字符串以及数组。经常出现大对象会提前触发垃圾收集以获取足够的连续空间分配给大对象。
-XX:PretenureSizeThreshold
,大于此值的对象直接在老年代分配,避免在Eden
和Survivor
之间的大量内存复制。 - 长期存活的对象进入老年代:为对象定义年龄计数器,对象在 Eden 出生并经过
Minor GC
依然存活,将移动到Survivor
中,年龄就增加 1 岁,增加到一定年龄则移动到老年代中。-XX:MaxTenuringThreshold
用来定义年龄的阈值。 - 动态对象年龄判定:虚拟机并不是永远要求对象的年龄必须达到
MaxTenuringThreshold
才能晋升老年代,如果在Survivor
中相同年龄所有对象大小的总和大于Survivor
空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代,无需等到MaxTenuringThreshold
中要求的年龄。 - 空间分配担保:在发生
Minor GC
之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,那么 Minor GC 可以确认是安全的。如果不成立的话虚拟机会查看HandlePromotionFailure
的值是否允许担保失败,如果允许那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Minor GC
;如果小于,或者HandlePromotionFailure
的值不允许冒险,那么就要进行一次Full GC
。
Full GC
的触发条件
对于 Minor GC
,其触发条件非常简单,当 Eden
空间满时,就将触发一次 Minor GC
。而 Full GC
则相对复杂,有以下条件:
- 调用
System.gc()
:只是建议虚拟机执行Full GC
,但是虚拟机不一定真正去执行。不建议使用这种方式,而是让虚拟机管理内存。 - 老年代空间不足:老年代空间不足的常见场景为前文所讲的大对象直接进入老年代、长期存活的对象进入老年代等。为了避免以上原因引起的
Full GC
,应当尽量不要创建过大的对象以及数组。除此之外,可以通过-Xmn
虚拟机参数调大新生代的大小,让对象尽量在新生代被回收掉,不进入老年代。还可以通过-XX:MaxTenuringThreshold
调大对象进入老年代的年龄,让对象在新生代多存活一段时间。 - 空间分配担保失败:使用复制算法的
Minor GC
需要老年代的内存空间作担保,如果担保失败会执行一次Full GC
。具体内容请参考上面的第 5 小节。 -
JDK 1.7
及以前的永久代空间不足:在JDK 1.7
及以前,HotSpot
虚拟机中的方法区是用永久代实现的,永久代中存放的为一些Class
的信息、常量、静态变量等数据。当系统中要加载的类、反射的类和调用的方法较多时,永久代可能会被占满,在未配置为采用CMS GC
的情况下也会执行Full GC
。如果经过Full GC
仍然回收不了,那么虚拟机会抛出java.lang.OutOfMemoryError
。为避免以上原因引起的Full GC
,可采用的方法为增大永久代空间或转为使用CMS GC
。 -
Concurrent Mode Failure
:执行CMS GC
的过程中同时有对象要放入老年代,而此时老年代空间不足(可能是GC
过程中浮动垃圾过多导致暂时性的空间不足),便会报Concurrent Mode Failure
错误,并触发Full GC
。
(2)、内存泄露
- 静态集合类:如
HashMap
、LinkedList
等等。如果这些容器为静态的,那么它们的生命周期与程序一致,则容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏。简单而言,长生命周期的对象持有短生命周期对象的引用,尽管短生命周期的对象不再使用,但是因为长生命周期对象持有它的引用而导致不能被回收。 - 各种连接,如数据库连接、网络连接和
IO
连接等:在对数据库进行操作的过程中,首先需要建立与数据库的连接,当不再使用时,需要调用close
方法来释放与数据库的连接。只有连接被关闭后,垃圾回收器才会回收对应的对象。否则,如果在访问数据库的过程中,对Connection
、Statement
或ResultSet
不显性地关闭,将会造成大量的对象无法被回收,从而引起内存泄漏。 - 变量不合理的作用域:一般而言,一个变量的定义的作用范围大于其使用范围,很有可能会造成内存泄漏。另一方面,如果没有及时地把对象设置为
null
,很有可能导致内存泄漏的发生。 - 内部类持有外部类:如果一个外部类的实例对象的方法返回了一个内部类的实例对象,这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持有外部类的实例对象,这个外部类对象将不会被垃圾回收,这也会造成内存泄露。
- 改变哈希值:当一个对象被存储进
HashSet
集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet
集合中时的哈希值就不同了,在这种情况下,即使在contains
方法使用该对象的当前引用作为的参数去HashSet
集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet
集合中单独删除当前对象,造成内存泄露 - 过期引用:如果栈先增长再收缩,那么从栈中弹出的对象将不会被当作垃圾回收,即使程序不再使用栈中的这些对象,他们也不会回收,因为栈中仍然保存这对象的引用,俗称过期引用,这个内存泄露很隐蔽。
- 缓存泄露:内存泄漏的另一个常见来源是缓存,一旦你把对象引用放入到缓存中,他就很容易遗忘,对于这个问题,可以使用
WeakHashMap
代表缓存,此种Map的特点是,当除了自身有对key
的引用外,此key
没有其他引用那么此map
会自动丢弃此值 - 监听器和回调:内存泄漏第三个常见来源是监听器和其他回调,如果客户端在你实现的API中注册回调,却没有显示的取消,那么就会积聚。需要确保回调立即被当作垃圾回收的最佳方法是只保存他的若引用,例如将他们保存成为
WeakHashMap
中的键。
3、==和equals的区别?举例说明?
(1)、“==
”比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象,比较的是真正意义上的指针操作。
- 比较的是操作符两端的操作数是否同一个对象。
- 两端的操作数必须是同一类型的(可以是父子类之间)才能编译通过。
- 比较的是地址。如果是具体的阿拉伯数字的比较,值相等则为
True
,如int a=10
与long b =10L
与double c=10.0
都是相同的(True
),因为他们都指向地址为10的堆。
(2)**equals
**用来比较的是对象的内容是否相等,由于所有的类都是继承自java.lang.object
类的,所以适用于所有对象,如果没有对该方法进行覆盖的话,调用的仍然是object类中的方法,而object
中的equals
方法返回的却是’’==
’'的的判断。
补充:
1、所有比较是否相等时,都是用equals
并且在对常量相比较时,把常量写在前面,因为使用object
的equals object
可能为null
,则空指针。
2、在阿里的代码规范中只使用equals
,阿里插件默认会识别,并可以快速修复,推荐安装阿里插件来排查老代码使用“==
”,替代成equals
。
4、列举几个常用的设计模式,画出三个常用的UML图并说明其优点和使用场景?
(1)、常用设计模式
创建型:简单工厂模式、工厂方法模式、抽象工厂模式、建造者模式、单例模式
结构型:适配器模式、桥接模式、装饰模式、外观模式、享元模式、代理模式
行为型:命令模式、中介者模式、观察者模式、状态模式、策略模式
(2)、常用的三种设计模式
工厂模式:工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
1. UML
2. 优点
- 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
- 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,是因为所有的具体工厂类都具有同一抽象父类。
- 使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了。这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”。
3. 适用场景
- 一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。
- 一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
- 将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
单例模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。单例模式是一种对象创建型模式。单例模式又名单件模式或单态模式。
1. UML
2. 优点
- 提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它,并为设计及开发团队提供了共享的概念。
- 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。
- 允许可变数目的实例。我们可以基于单例模式进行扩展,使用与单例控制相似的方法来获得指定个数的对象实例。
3. 适用场景
- 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器,或者需要考虑资源消耗太大而只允许创建一个对象。
- 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。
- 在一个系统中要求一个类只有一个实例时才应当使用单例模式。反过来,如果一个类可以有几个实例共存,就需要对单例模式进行改进,使之成为多例模式
适配器模式:将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
1. UML
(1)、类适配器
(2)、对象适配器
2. 优点
- 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。
- 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。
- 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。
- 类适配器模式还具有如下优点:由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。
- 对象适配器模式还具有如下优点:一个对象适配器可以把多个不同的适配者适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。
3. 适用场景
- 系统需要使用现有的类,而这些类的接口不符合系统的需要。
- 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
5、谈谈MVC、MVP、MVVM?
1、MVC
MVC
全名是Model View Controller
,是模型(model
)-视图(view
)-控制器(controller
)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC
被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。
2、MVP
MVP
的全称为Model-View-Presenter
,Model
提供数据,View
负责显示,Controller/Presenter
负责逻辑的处理。MVP
与MVC
有着一个重大的区别:在MVP
中View
并不直接使用Model
,它们之间的通信是通过Presenter
(MVC
中的Controller
)来进行的,所有的交互都发生在Presenter
内部,而在MVC
中View
会直接从Model
中读取数据而不是通过 Controller
。
3、MVVM
MVVM
是Model-View-ViewModel
的简写。它本质上就是MVC
的改进版。MVVM
就是将其中的View
的状态和行为抽象化,让我们将视图 UI
和业务逻辑分开。当然这些事ViewModel
已经帮我们做了,它可以取出 Model
的数据同时帮忙处理 View
中由于需要展示内容而涉及的业务逻辑。微软的WPF带来了新的技术体验,如Silverlight
、音频、视频、3D、动画……,这导致了软件UI层更加细节化、可定制化。同时,在技术层面,WPF也带来了 诸如Binding、Dependency Property、Routed Events、Command、DataTemplate、ControlTemplate
等新特性。MVVM(Model-View-ViewModel)
框架的由来便是MVP(Model-View-Presenter)
模式与WPF结合的应用方式时发展演变过来的一种新型架构框架。它立足于原有MVP
框架并且把WPF的新特性糅合进去,以应对客户日益复杂的需求变化。
4、备注
WPF(Windows Presentation Foundation)
是微软推出的基于Windows
的用户界面框架,属于.NET Framework 3.0
的一部分。它提供了统一的编程模型、语言和框架,真正做到了分离界面设计人员与开发人员的工作;同时它提供了全新的多媒体交互用户图形界面。)
6、谈谈普通类、抽象类、接口、继承以及实现?
继承 | 被继承 | 实现 | 被实现 | |
普通类 | 普通类、抽象类 | 普通类、抽象类 | 接口 | 无 |
抽象类 | 普通类、抽象类 | 普通类、抽象类 | 接口 | 无 |
接口 | 接口 | 接口 | 无 | 无 |
注意:抽象类在实现接口时可以不实现接口中的方法,但被普通类继承时必须全部实现,包括接口中的方法。
7、谈谈线程和进程的区别?
(1)、什么是进程和线程?
1、进程是资源分配的基本单位
进程控制块 (Process Control Block, PCB
) 描述进程的基本信息和运行状态,所谓的创建进程和撤销进程,都是指对 PCB
的操作。
下图显示了 4 个程序创建了 4 个进程,这 4 个进程可以并发地执行。
2、线程是独立调度的基本单位
一个进程中可以有多个线程,它们共享进程资源。
QQ 和浏览器是两个进程,浏览器进程里面有很多线程,例如 HTTP 请求线程、事件响应线程、渲染线程等等,线程的并发执行使得在浏览器中点击一个新链接从而发起 HTTP 请求时,浏览器还可以响应用户的其它事件。
(2)、进程与线程的区别
1. 拥有资源
进程是资源分配的基本单位,但是线程不拥有资源,线程可以访问隶属进程的资源。
2. 调度
线程是独立调度的基本单位,在同一进程中,线程的切换不会引起进程切换,从一个进程中的线程切换到另一个进程中的线程时,会引起进程切换。
3. 系统开销
由于创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、I/O
设备等,所付出的开销远大于创建或撤销线程时的开销。类似地,在进行进程切换时,涉及当前执行进程 CPU
环境的保存及新调度进程 CPU
环境的设置,而线程切换时只需保存和设置少量寄存器内容,开销很小。
4. 通信方面
线程间可以通过直接读写同一进程中的数据进行通信,但是进程通信需要借助 IPC。
8、说明异常处理的方式,并谈谈throws、throw、try、catch、finally的区别?
1、JAVA异常处理
Java
异常是Java
提供的一种识别及响应错误的一致性机制。Java异常机制可以使程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高程序健壮性。在有效使用异常的情况下,异常能清晰的回答what, where, why
这3个问题(异常类型回答了“什么”被抛出,异常堆栈跟踪回答了“在哪“抛出,异常信息回答了“为什么“会抛出。)
2、try、catch、finally、throw、throws的区别
- try:用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
- catch:用于捕获异常。
catch
用来捕获try语句块中发生的异常。 - finally:
finally
语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有finally
块,执行完成之后,才会回来执行try
或者catch
块中的return
或者throw
语句,如果finally中使用了return
或者throw
等终止方法的语句,则就不会跳回执行,直接停止。 - throw:用于抛出异常。
- throws:用在方法签名中,用于声明该方法可能抛出的异常。