2020年6月18日 星期四
1、单例模式
(1)单例模式定义
小白:
单例模式是保证一个类只有一个实例,保证线程安全。
答案:
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡 的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个 Printer Spooler,以避免 两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。 总之,选择单例模式就是为了避免不一致状态。
(2)单例模式特点
小白:
保证一个类只有一个实例,保证线程安全。
答案:
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
- 单例模式保证了全局对象的唯一性,比如系统启动读取配置文件就需要单例保证配置的一致性。
(3)单例模式的四大原则
小白:唯一性,静态单例。
答案:
- 构造私有;
- 以静态方法或者枚举返回实例 ;
- 确保实例只有一个,尤其是多线程环境 ;
- 确保反序列换时不会重新构建对象。
(4)实现单例模式的方式
小白:
饿汉式、懒汉式、静态内部类、双重检索、枚举。
答案:
(1)饿汉式(立即加载);
(2)懒汉式(延迟加载);
(3)同步锁(解决线程安全问题):
(4)双重检查锁(提高同步锁的效率);
(5) 静态内部类;
(6)内部枚举类实现(防止反射攻击);
2、你是怎样理解面向对象的
小白:
面向对象三大特点
封装、继承、多态、
答案:
面向对象是利于语言对现实事物进行抽象。面向对象具有以下四大特征:
(1)继承:继承是从已有类得到继承信息创建新类的过程 ;
(2)封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口;
(3)多态性:多态性是指允许不同子类型的对象对同一消息作出不同的响应;
(4)抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。
3、int和Integer有什么区别
(1)int是基本数据类型,Integer是int的封装类型;
(2)int可以没有初始值,Integer必须设置初始值;
(3)int默认为0,Integer默认为null;
4、==和equals的区别
==
(1)在基本类型比较时,比较的是数值;
(2)在封装类型比较时,比较的是地址值;
equals
(1)没重写equals方法时,比较的是地址值,因为equals在object类中的实现就是==;
(2)重写equals方法后,比较的是对象中的属性值;
5、谈谈你对反射的理解
(1)反射机制
小白:
反射机制是通过getClass方法获取java类自身的一些属性,比如类名、变量、方法等。
答案:
所谓的反射机制就是 java 语言在运行时拥有一项自观的能力。通过这种能力可以彻底的了解自身的情况为下一步的动作做准备。 Java 的反射机制的实现要借助于 4 个类:class,Constructor,Field,Method; 其中 class 代表的时类对 象,Constructor-类的构造器对象,Field-类的属性对象,Method-类的方法对象。通过这四个对象我们可以粗略的看到一个 类的各个组 成部分。
(2)Java反射的作用
小白:
获取这个类的属性,比如类名、变量、方法、参数等。
答案:
在 Java 运行时环境中,对于任意一个类,可以知道这个类有哪些属性和方法。对于任意一个对象,可以调用它的任意一个方法。这种动态获取类的信息 以及动态调用对象的方法的功能来自于 Java 语言的反射(Reflection)机制。
(3)Java反射机制提供功能
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法;
6、ArrayList和LinkedList区别
小白:
(1)ArrayList的底层实现是数组,linkedlist的底层实现是链表;
(2)ArrayList的插入和更新更快,LinkedList的添加和删除更快,因为这也是数组和链表的区别;
答案:
(1)ArrayList 是实现了基于动态数组的数据结构,LinkedList 基于链表的数据结构。
(2)对于随机访问 get 和 set,ArrayList 觉得优于 LinkedList,因为 LinkedList 要移动指针。
(3)对于新增和删除操作 add 和 remove,LinkedList 比较占优势,因为 ArrayList 要移动数据。 这一点要看实际情况的。若只对单条数据插入或删除, ArrayList 的速度反而优于 LinkedList。但若是批量随机的插入删除数据,LinkedList 的速度大大优于 ArrayList. 因为 ArrayList 每插入一条数据,要移动 插入点及之后的所有数据。
7、HashMap底层源码
1.7之前是数组+链表的形式;
1.8开始是数组+链表+红黑树的形式;
8、HashMap和HashTable的区别
小白:
(1)HashMap线程不安全,HashTable线程安全,HashTable内部包含synchronized,HashMap要想线程安全,可以使用一些同步方法。
(2)HashMap的key和value允许为null,HashTable的key和value都不可以为null;
答案:
(1)线程安全性不同 HashMap 是线程不安全的,HashTable 是线程安全的,其中的方法是 Synchronize 的,在多线程并发的情况下,可以直接使用 HashTabl,但是使用 HashMap 时必须自己增加同步处理。
(2)是否提供 contains 方法 HashMap 只有 containsValue 和 containsKey 方法;HashTable 有 contains、containsKey 和 containsValue 三个方法,其中 contains 和 containsValue 方法功能 相同。
(3)key 和 value 是否允许 null 值 Hashtable 中,key 和 value 都不允许出现 null 值。HashMap 中,null 可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为 null。
(4)数组初始化和扩容机制 HashTable 在不指定容量的情况下的默认容量为 11,而 HashMap 为 16,Hashtable 不要求底层数组的容量一定要为 2 的整数次幂,而 HashMap 则要求一 定为 2 的整数次幂。 Hashtable 扩容时,将容量变为原来的 2 倍加 1,而 HashMap 扩容时,将容量变为原来的 2 倍。
9、TreeSet和HashSet的区别
小白:
set是没有重复的元素;
TreeSet是有序的set;
HashSet是无序的Set;
答案:
HashSet 是采用 hash 表来实现的。其中的元素没有按顺序排列,add()、remove()以及 contains()等方法都是复杂度为 O(1)的方法。
TreeSet 是采用树结构实现(红黑树算法)。元素是按顺序进行排列,但是 add()、remove()以及 contains()等方法都是复杂度为 O(log (n))的方法。它还提供 了一些方法来处理排序的 set,如 first(), last(), headSet(), tailSet()等等。
10、StringBuffer和StringBuilder的区别
小白:
stringbuffer是线程安全的,stringbuilder是线程不安全的,stringbuilder速度比stringbuffer要快,因为没有同步方法;
答案:
(1)StringBuffer 与 StringBuilder 中的方法和功能完全是等价的;
(2)只是 StringBuffer 中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是线程不 安全的;
(3)在单线程程序下,StringBuilder 效率更快,因为它不需要加锁,不具备多线程安全而 StringBuffer 则每次都需要判断锁,效率相对更低
11、Final、Finally、Finalize区别
(1)final可以修饰类、变量、方法
修饰类时,这个类不能被继承。
修饰变量时,这个变量必须赋值,在引用中只能读取不可修改,即为常量;
修饰方法时,这个方法不能被子类重写;
(2)finally:通常放在 try…catch 的后面构造最终执行代码块,这就意味着程序无论正常执行还是发生异常,这里的代码只要 JVM 不关闭都能执行,可以将 释放外部资源的代码写在 finally 块中。
(3)finalize() Object 类中定义的方法,Java 中允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集 器在销毁对象时调用的,通过重写 finalize() 方法可以整理系统资源或者执行其他清理工作。
12、什么是Java序列化,如何实现Java序列化
小白:Java序列化是通过IO流的形式将对象存储在内存中,再通过反序列化将内存中的对象转为java对象,有助于对象的传输,实现serializable接口即可实现序列化,transient或static修饰的变量不会被序列化和反序列化。
答案:
序列化就是一种用来处理对象流的机制,所谓对象流就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可以将流化后的对象传输于网络之间,
序列化的实现:
- 实现serializable接口,该接口没有需要实现的方法;然后使用一个输出流(FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,然后使用writeObject(Obejct obj)方法就可以将obj写出(即保存其状态),要恢复的话则用输入流。
13、Object中有哪些方法
- getcode()
- getClass()
- toString()
- equals()
- finalize()
- wait()
- notify()
- notifyAll()
- clone()
14、线程由几种状态,产生的条件是什么
小白:
(1)创建状态:Thread thread = new Thread();
(2)就绪状态:调用start()后;
(3)执行状态:
(4)阻塞状态:
造成阻塞状态的三种原因:
- 等待阻塞
- 线程阻塞
- sleep造成的阻塞
(5)死亡状态
答案:
(1)创建状态:Thread thread = new Thread();
(2)就绪状态:调用start()后;
(3)执行状态:只能从就绪状态进入执行状态;
(4)阻塞状态:阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行,直到线程进入就绪状态,才会有机会转到运行状态。
造成阻塞状态的三种原因:
- 等待阻塞:通过调用线程的wait()方法,让线程等待某工作的完成。
- 线程阻塞:线程获取synchronized同步锁失败(因为锁被其它线程锁占用),它会进入同步阻塞状态;
- 其它阻塞:通过调用线程的sleep()或join()或发出了IO请求时,线程就会进入阻塞状态。当sleep()超时、join()等待线程终止或超时、或者IO处理完毕时,线程重新转入就绪状态。
(5)死亡状态:线程执行完了或者因异常退出了 run()方法,该线程结束生命周期。
15、产生死锁的基本条件
(1)产生死锁的原因
小白:不会
答案:
- 因为系统资源不足;
- 进程运行推进的顺序不合适;
- 资源分配不当;
如果系统资源充足,进程的资源请求都能得到满足,死锁出现的可能性就会很低,否者就会因为争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。
(2)产生死锁的四个必要条件
小白:
- 互斥条件
- 请求和保持条件
- 不剥夺条件
- 环路等待条件
答案:
① 互斥条件:指进程对所分配到的资源进行排它性使用,在一段时间内某资源只由一个进程占用,如果此时还有其他进程请求资源,则请求者只能等待,直至占有资源的进程被释放。
② 请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
③ 不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
④环路等待条件:在发生死锁时,必然存在一个进程,资源的唤醒链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。
(3)死锁的解除和预防
小白:
强制解锁;
答案:
如何不让四个条件成立,如何确定资源的合理分配,避免进程永久占据系统资源;
也要防止进程在处于等待状态的情况下占用资源。