一、关于java中常用的几个算法的复杂度及稳定性
小结
-
排序速度最快的3种排序方法分别为:堆排序,快速排序,归并排序。其中只有归并排序是稳定的,其余两种均是不稳定的排序。故当需要稳定排序shi可以选择归并排序;
-
我们可以看到,堆排序的时间复杂度是很稳定的,而快速排序的时间复杂度最坏情况会达到n^2,那么为什么不都用堆排序而使用快速排序呢?
首先,时间复杂度这个概念只是一个粗略的估计概念,在真实的情况下,一般快排的效率比堆排序高很多;
对于快排来说,数组中交换数据,在数据量不是特别大的时候,而且离散程度较高的情况下效率很高;
对于堆排序,创建堆,数据交换,调整对的时间均很多。 故在实际应用中,我们用快排会更多一点 -
堆排序的典型应用:在100万个数字中找出最大的100个这种问题,构件一个小根堆之后遍历调整就可以了
-
另外,除了堆排序和归并排序的最坏情况下时间复杂度为O(nlon2n)外,其他排序算法的最坏时间复杂度均为O(n^2)
-
堆排序和归并排序在三种情况下的时间复杂度均一样,两者的差别只有空间复杂度和稳定性的差别
二、抽象类型和接口类型有什么异同?
相同之处:
6. 抽象类型和接口类型都不能被实例化,但是可以定义成抽象类型和接口类型的引用
7. 一个类如果继承或者实现了某个抽象类/接口,那么必须实现其中的所有抽象方法,否则该类必须声明为抽象类
不同之处:
8. 接口类型比抽象类型更加抽象,抽象类型中可以定义构造方法和具体方法,但是接口类型中不能定义构造方法且其中所有的方法均只能是抽象方法。抽象类型的成员可以用public,默认,protected,private修饰,但接口类型中的成员必须是public的。
9. 有抽象方法的类必须被声明为抽象类,但是抽象类不一定要有抽象方法
10. 抽象类中可以定义成员变量,但接口中的成员变量实际上都是默认用关键字final修饰的常量
三、解释内存中的栈,堆和方法区的用法
- 通常我们定义一个基本数据类型的变量,一个引用类型的引用,还有就是函数调用的现场保存都是用JVM的栈空间,而通过new和构造器创建的对象则放在堆空间,堆是垃圾收集器管理的主要空间。
- 方法区和栈都是程序中所有线程共享的内存区域,方法区用于存储已经被JVM加载的类信息,常量,静态变量,JIT编译器编译后的代码等数据;程序中的字面变量如直接书写的“100”,“hello”,和常量都是放在常量池中,常量池也是方法区的一部分。
- 栈操作起来快但是空间小,通常大量的对象都是放在堆空间中。
四、Thread类的sleep()方法和Object类的wait()方法都可以让线程暂停执行,两者有什么区别?
14. sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停指定时间,将执行机会让给其他线程,但是对象的锁依然保持,因此休眠时间结束后自动恢复(线程回到就绪状态)
15. wait()方法是Object类的方法,调用对象的wait()方法会导致当前线程放弃已拥有的对象的锁并使得当前线程暂停执行,进入对象的等待池中,只有调用对象的notify()方法(或者notifyAll()方法)时才能唤醒等待池中的线程进入等锁池。如果线程重新获得对象的锁就可以进入就绪状态。
五、线程的sleep()方法和yield()方法有什么区别?
- sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;而yield()方法只会给相同优先级或者更高优先级的线程以运行机会。
- 线程执行sleep()方法之后转入阻塞状态,而执行yield()方法后转入就绪状态。
- sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常。
- sleep()方法比yield()方法具有更好的可移植性。
六、当一个线程进入进入一个对象的synchronized方法A之后,其他线程是否可以进入该对象的其他synchronized方法B?
答:不能。其他线程只能访问该对象的非同步方法,同步方法则不能进入。因为非静态方法上的synchronized修饰符要求执行方法时要获得对象的锁,如果一个线程进入该对象的A方法说明该对象的对象锁已经被取走,那么其他任何试图进入B方法的线程就只能在等锁池等待第一个线程释放对象的线程锁。
七、什么是线程池?
答:在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其他更多的资源。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象的创建和销毁,这就是“池化资源”技术产生的原因。线程池顾名思义就是实现创建若干个可执行的线程放入一个池中,需要的时候从池中获取线程而不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁对象的开销。
八、Java中如何实现序列化,有什么意义?
答:序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化的对象进行读写操作,也可以将流化的对象传输于网络之间。序列化是为了解决对象流读写操作是可能引发的问题(如果不进行序列化可能会存在数据乱序的问题)。
要实现序列化,需要让一个类实现Serializable接口,该接口是一个标志性接口,标注该类对象是可被序列化的,然后使用一个输出流来构造一个对象输出流并通过writeObject(Object)方法就看可以将实现对象写出(即保存其状态);如果需要反序列化则可以用一个输入流建立对象输入流,然后通过readObject方法从流中读取对象。序列化除了能够实现对象的持久化之外,还能够用于对象的深度克隆。
九、面向对象设计7大原则
- 单一职责原则:一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中
- 开闭原则:一个软件应该对扩展开放,对修改关闭
- 里氏代换原则:所有引用父类的地方必须能透明地使用其子类的对象
- 依赖倒转原则:即针对接口编程,不要针对实现编程
- 接口隔离原则:每一种接口应该承担一种相对独立的角色,不多不少,不干不该干的事情,该干的事情都干。
- 合成复用原则:尽量使用组合/聚合,少用继承
- 迪米特法则:一个软件实体应该尽量少的与其他实体发生相互作用
十、Java的三大特性是什么?
封装,继承,多态
十一、要将一个类实例写入流中,需要实现什么接口?
serializable接口,具体可以查一下原型模式