Java菜鸟补给站--常见面试简答题( 二 )

目录

1.List的3三个子类的特点

2. List 和Map、Set 的区别

3. HashMap 和HashTable有什么区别?

4.数组和链表的区别

5.链表和数组使用场景

6. Java 中ArrayList 和Linkedlist区别?

7.你所知道的设计模式有哪些

8. heap 和stack有什么区别

9.既然有GC机制,为什么还会有内存泄露的情况 

10 Java中为什么会有GC机制呢? 

11对于 Java的GC哪些内存需要回收

12.Java的GC什么时候回收垃圾

13.说下原生 jdbc操作数据库流程?

14.什么要使用 PreparedStatement? 

15.关系数据库中连接池的机制是什么?

16. Map中的key和value可以为null么?

17. Java中引用类型都有哪些? (重要)


1.List的3三个子类的特点

  • ArrayList底层结构是数组,底层查询快,增删慢。
  • LinkedList底层结构是链表型的,增删快,查询慢。
  • voctor底层结构是数组线程安全的,增删慢查询慢。

2. List 和Map、Set 的区别


2.1结构特点

  • List和Set是存储单列数据的集合, Map是存储键和值这样的双列数据的集合;
  • List中存储的数据是有顺序,并且允许重复; Map中存储的数据是没有顺序的,其键是不能重复的,它的值是可以有重复的,
  • Set 中存储的数据是无序的,且不允许有重复,但元素在集合中的位置由元素的hashcode决定,位置是固定的(Set集合根据hashcode来进行数据的存储,所以位置是固定的,但是位置不是用户可以控制的,所以对于用户来说set中的元素还是无序的) ;

2.2实现类

  • List接口有三个实现类(LinkedList: 基于链表实现,链表内存是散乱的,每一个元素存储本身内存地址的同时还存储下一个元素的地址。链表增删快,查找慢; ArrayList: 基于数组实现,非线程安全的,效率高,便于索引,但不便于插入删除; Vector: 基于数组实现,线程安全的,效率低)。
  • Map接口有三个实现类(HashMap: 基于hash表的Map接口实现,非线程安全,高效,支持null值和null键; HashTable: 线程安全,低效,不支持null值和null键; LinkedHashMap: 是HashMap的一个子类,保存了记录的插入顺序; SortMap 接口: TreeMap,能够把它保存的记录根据键排序,默认是键值的升序排序)。
  • Set接口有两个实现类(HashSet: 底层是由HashMap实现,不允许集合中有重复的值,使用该方式时需要重写equals()和hashCode(方法; LinkedHashSet: 继承与HashSet,同时又基于LinkedHashMap来进行实现,底层使用的是LinkedHashMp)。

2.3区别

  • List集合中对象按照索引位置排序,可以有重复对象,允许按照对象在集合中的索引位置检索对象,例如通过list.get()方法来获取集合中的元素;
  • Map 中的每-个元素包含一个键和-一个值,成对出现,键对象不可以重复,值对象可以重复;
  • Set集合中的对象不按照特定的方式排序,并且没有重复对象,但它的实现类能对集合中的对象按照特定的方式排序,例如TreeSet类,可以按照默认顺序,也可以通过实现Java.util.Comparator< Type>接口来自定义排序方式。


3. HashMap 和HashTable有什么区别?

  • HashMap是线程不安全的,HashMap是一个接口,是 Map的一一个子接口,是将键映射到值得对象不允许键值重复允许空键和空值:由于非线程安全, HashMap的效率要较HashTable的效率高一些
  • HashTable是线程安全的一个集合,不允许null值作为-个key值或者Value值;HashTable是sychronize,多个线程访问时不需要自己为它的方法实现同步,而HashMap在被多个线程访问的时候需要自己为它的方法实现同步;
     

4.数组和链表的区别

  • 数组是将元素在内存中连续存储的;它的优点:因为数据是连续存储的,内存地址连续,所以在查找数据的时候效率比较高;它的缺点:在存储之前,我们需要申请-块连续的内存空间,并且在编译的时候就必须确定好它的空间的大小。在运行的时候空间的大小是无法随着你的需要进行增加和减少而改变的,当数据两比较大的时候,有可能会出现越界的情况,数据比较小的时候,又有可能会浪费掉内存空间。在改变数据个数时,增加、插入、删除数据效率比较低
  • 链表是动态申请内存空间,不需要像数组需要提前申请好内存的大小,链表只需在用的时候申请就可以,根据需要来动态申请或者删除内存空间,对于数据增加和删除以及插入比数组灵活。还有就是链表中数据在内存中可以在任意的位置,通过应用来关联数据(就是通过存在元素的指针来联系)


5.链表和数组使用场景

  • 数组应用场景:数据比较少;经常做的运算是按序号访问数据元素;数组更容易实现,任何高级语言都支持;构建的线性表较稳定。
  • 链表应用场景:对线性表的长度或者规模难以估计;频繁做插入删除操作;构建动态性比较强的线性表。
     

6. Java 中ArrayList 和Linkedlist区别?

  • ArrayList和Vector使用了数组的实现,可以认为ArrayList或者Vector封装了对内部数组的操作,比如向数组中添加,删除,插入新的元素或者数据的扩展和重定向。
  • LinkedList使用了循环双向链表数据结构。与基于数组的ArrayList相比,这是两种截然不同的实现技术,这也决定了它们将适用于完全不同的工作场景。
     

7.你所知道的设计模式有哪些

  • Java中-般认为有23种设计模式,我们不需要所有的都会,但是其中常用的几种设计模式应该去掌握。下面出了所有的设计模式。需要掌握的设计模式我单独列出来了,当然能掌握的越多越好。
  • 总体来说设计模式分为三大类:
  • 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
  • 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
  • 行为型模式,共十-种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
     

8. heap 和stack有什么区别

  • 从以下几个方面阐述堆(heap) 和栈(stack) 的区别。

1.申请方式

  • stack:由系统自动分配。例如,声明在函数中一个局部变量int b;系统自动在栈中为b开辟空间heap:需要程序员自己申请,并指明大小,在C中malloc函数,对于Java需要手动new Object()的形式开辟

2.申请后系统的响应

  • stack:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
     
  • heap:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。

3.申请大小的限制

  • stack:栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定的,在WINDOWS下,栈的大小是2M (也有的说是1M,总之是一个编译时就确定的常数) ,如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
  • heap:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,
     

9.既然有GC机制,为什么还会有内存泄露的情况 

  • 理论上Java 因为有垃圾回收机制(GC)不会存在内存泄露问题(这也是Java被广泛使用于服务器端编程的一个重要原因)。然而在实际开发中,可能会存在无用但可达的对象,这些对象不能被GC回收,因此也会导致内存泄露的发生。
  • 例如hibernate 的Session (- -级缓存)中的对象属于持久态,垃圾回收器是不会回收这些对象的,然而这些对象中可能存在无用的垃圾对象,如果不及时关闭(close) 或清空(flush) - -级缓存就可能导致内存泄露。
     

10 Java中为什么会有GC机制呢? 

  • 安全性考虑; -- for security.
  • 减少内存泄露; -- erase memory leak in some degree.
  • 减少程序员工作量。-- Programmers don't worry about memory releasing.
     

11对于 Java的GC哪些内存需要回收

  • 内存运行时JVM会有一个运行时数据区来管理内存.它主要包括5大部分:程序计数器(Program CounterRegister)、虚拟机栈(VM Stack).本地方法栈(Native Method Stack).方法区(Method Area).堆(Heap).而其中程序计数器、虚拟机栈、本地方法栈是每个线程私有的内存空间,随线程而生,随线程而亡
  • 例如栈中每一个栈帧中分配多少内存基本.上在类结构确定是哪个时就已知了,因此这3个区域的内存分配和回收都是确定的,无需考虑内存回收的问题。
  • 但方法区和堆就不同了,一个接口的多个实现类需要的内存可能不一样,我们只有在程序运行期间才会知道会创建哪些对象,这部分内存的分配和回收都是动态的,GC主要关注的是这部分内存。总而言之,GC主要进行回收的内存是JVM中的方法区和堆;
     

12.Java的GC什么时候回收垃圾

  • 在面试中经常会碰到这样一个问题(事实上笔者也碰到过) : 如何判断一个对象已经死去?
  • 很容易想到的一个答案是:对一一个对象添加引用计数器。每当有地方引用它时,计数器值加1;当引用失效时,计数器值减1.而当计数器的值为0时这个对象就不会再被使用,判断为已死。是不是简单又直观。然而,很遗憾。这种做法是错误的!为什么是错的呢?事实上,用引用计数法确实在大部分情况下是一个不错的解决方案, 而在实际的应用中也有不少案例,但它却无法解决对象之间的循环引用问题。比如对象A中有一个字段指向了对象B,而对象B中也有一个字段指向了对象A,而事实上他们俩都不再使用,但计数器的值永远都不可能为0,也就不会被回收,然后就发生了内存泄露。
  • 所以,正确的做法应该是怎样呢?
  • 在Java, C#等语言中,比较主流的判定一个对象已死的方法是: 可达性分析(Reachability Analysis).所有生成的对象都是一个称为"GC Roots”的根的子树。从GC Roots开始向下搜索,搜索所经过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链可以到达时,就称这个对象是不可达的(不可引用的),
  • 无论是引用计数器还是可达性分析,判定对象是否存活都与引用有关!那么,如何定义对象的引用呢?
  • 我们希望给出这样一类描述:当内存空间还够时,能够保存在内存中;如果进行了垃圾回收之后内存空间仍旧非常紧张,则可以抛弃这些对象。所以根据不同的需求,给出如下四种引用,根据引用类型的不同,GC回收时也会有不同的操作:
  • 1)强引用(Strong Reference):Object obj = new Object0;只要强引用还存在, GC永远不会回收掉被引用的对象。
  • 2)软引用(Soft Reference):描述-些还有用但非必需的对象。 在系统将会发生内存溢出之前,会把这些对象列入回收范围进行二次回收(即系统将会发生内存溢出了,才会对他们进行回收。)弱引用(Weak Reference)程度比软引用还要弱一-些。 这些对象只能生存到下次GC之前。当GC工作时,无论内存是否足够都会将其回收(即只要进行GC,就会对他们进行回收。)虚引用(Phantom Reference):-个对象是否存在虚引用, 完全不会对其生存时间构成影响。关于方法区中需要回收的是一些废弃的常量和无用的类。
  • 1废弃的常量的回收。这里看引用计数就可以了。没有对象引用该常量就可以放心的回收了。
  • 2.无用的类的回收。什么是无用的类呢?
  • A.该类所有的实例都已经被回收。也就是Java堆中不存在该类的任何实例;
  • B.加载该类的ClassLoader已经被回收;
  • C.该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法。
  • 总而言之:
  • 对于堆中的对象,主要用可达性分析判断一个对象是否还存在引用,如果该对象没有任何引用就应该被回收。n根据我们实际对引用的不同需求,又分成了4中引用,每种引用的回收机制也是不同的。对于方法区中的常量和类,当-个常量没有任何对象引用它,它就可以被回收了。而对于类,如果可以判定它为
     

13.说下原生 jdbc操作数据库流程?

  1. 第一步: Class.forName()加载数据库连接驱动;
  2. 第二步: DriverManager.getConnection()获取数据连接对象;
  3. 第三步:根据SQL获取sql会话对象,有2种方式Statement. PreparedStatement; .
  4. 第四步:执行SQL处理结果集,执行SQL前如果有参数值就设置参数值setXXX0;
  5. 第五步:关闭结果集、关闭会话、 关闭连接。
     

14.什么要使用 PreparedStatement? 

  • 1、PreparedStatement 接口继承Statement, PreparedStatement 实例包含已编译的SQL语询,所以其执行速度要快于Statement对象。
  • 2、作为Statement 的子类,PreparedStatement 继承了Statement 的所有功能。三种方法execute、 executeQuery 和executeUpdate已被更改以使之不再需要参数
  • 3、在JDBC应用中,在任何时候都不要使用Statement,原因如下:
  • 一、代码的可读性和可维护性Statement需要不断地拼接,而PreparedStatement不会。
  • 二、PreparedStatement 尽最大可能提高性能DB有缓存机制,相同的预编译语句再次被调用不会再次需要编译。
  • 三、最重要的- -点是极大地提高了安全性.Statement容易被SQL注入,而PreparedStatementc传入的内容不会和sql语句发生任何匹配关系。

15.关系数据库中连接池的机制是什么?

  • 前提:为数据库连接建立-一个缓冲池。
  • 1:从连接池获取或创建可用连接
  • 2:使用完毕之后,把连接返回给连接池
  • 3:在系统关闭前,断开所有连接并释放连接占用的系统资源
  • 4:能够处理无效连接,限制连接池中的连接总数不低于或者不超过某个限定值。
     

16. Map中的key和value可以为null么?

  • HashMap对象的key. value 值均可为null.
  • HahTable对象的key. value 值均不可为null。
  • 且两者的的key值均不能重复,若添加key相同的键值对,后面的value会自动覆盖前面的value,但不会报错。
     

17. Java中引用类型都有哪些? (重要)

  • Java中对象的引用分为四种级别,这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。
  • 强引用(StrongReference),这个就不多说,我们写代码天天在用的就是强引用。如果-个对象被被 人拥有强引用,那么垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引|用的对象来解决内存不足问题。
  • Java的对象是位于heap中的, heap 中对象有强可及对象、软可及对象、弱可及对象、虚可及对象和不可到达对象。应用的强弱顺序是强、软、弱、和虚。对于对象是属于哪种可及的对象,由他的最强的引用决定。如下代
     
  • 第一行在heap堆中创建内容为"abc" 的对象,并建立abc到该对象的强引用,该对象是强可及的。
  • 第二行和第三行分别建立对heap中对象的软引用和弱引用,此时heap中的abc对象已经有3个引用,显然此时abc对象仍是强可及的。
  • 第四行之后heap中对象不再是强可及的,变成软可及的。
  • 第五行执行之后变成弱可及的。软引用(SoftReference)
  • 如果一个对象只具有软引用,那么如果内存空间足够,垃圾回收器就不会回收它如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
  • 软引用可以和一个引用队列(ReferenceQueue) 联合使用,如果软引用所引用的对象被垃圾回收,Java 虚拟机就会把这个软引用加入到与之关联的引用队列中。软引用是主要用于内存敏感的高速缓存。在jvm报告内存不足之前会清除所有的软引用,这样以来gc就有可能收集软可及的对象,可能解决内存吃紧问题,避免内存溢出。什么时候会被收集取决于gc的算法和gc运行时可用内存的大小。
  • 当gc决定要收集软引用时执行以下过程以上面的softRef为例:
  • 1首先将softRef的referent (abc)设置为nll,再引用heap中的new String(" abc )对象。
  • 2将heap中的new String(" abc' )对象设置为可结束的(finalizable)。
  • 3当heap中的new String(" abc ")对象的finalize0方法被运行而且该对象占用的内存被释放,softRef被添加到它的ReferenceQueue(如果有的话)中。
  • 注意:对ReferenceQueue软引用和弱引用可以有可无,但是虚引用必须有。
  • 被Soft Reference指到的对象,即使没有任何Direct Reference,也不会被清除。一直要到JVM内存,不足且没有Direct Reference时才会清除,SoftReference 是用来设计object-cache 之用的。如此一来SoftReference不但可以把对象cache起来,也不会造成内存不足的错误(OutOfMemoryError) 。
  • 弱引用(WeakReference)
  • 如果- -个对象只具有弱引用,那该类就是可有可无的对象,因为只要该对象被gc扫描到了随时都会把它干掉。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。由于垃圾回收器是-一个优先级很低的线程,因此不一 -定会很快发现那些只具有弱引用的对象。
  • 弱引用可以和一个引用队列(ReferenceQueue) 联合使用,如果弱引|用所引|用的对象被垃圾回收,Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中。
     
  • 虚引用(PhantomReference)
  • "虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果-一个对象仅持有虚引用,那么它就和没有任何引用- -样,在任何时候都可能被垃圾回收。虚引用主要用来跟踪对象被垃圾回收的活动。
  • 虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列( ReferenceQueue)联合使用。当垃圾回收器准备回收-个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。
  • 程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。建立虚引用之后通过get 方法返回结果始终为null,通过源代码你会发现,虚引用通向会把引用的对象写进referent,只是get方法返回结果为null。先看一下和gc交互的过程再说一下他的作用。
  • 1不把referent设置为null, 直接把heap中的new String(" abc' )对象设置为可结束的(finalizable)。
  • 2与软引用和弱引用不同,先把PhantomRefrence对象添加到它的ReferenceQueue中.然后在释放虚可及的对象。
     

猜你喜欢

转载自blog.csdn.net/c202003/article/details/107396186