1、HashMap 和HashTable区别
(1)线程安全性不同 HashMap是线程不安全的,HashTable是线程安全的,HashTable中的方法有Synchronize修饰,所以是线程安全的。在多线程并发的情况下,可以使用HashTable保证线程安全,但是使用HashMap时必须自己增加同步处理以保证线程安全。
(2)是否提供contains方法 HashMap与HashTable都有containsValue和containsKey方法,但是HashTable比HashMap多一个contains方法。HashTable的contains方法和containsValue方法功能相同。
(3)key和value是否允许null值 HashTable中,key和value都不允许出现null值。 HashMap中,null可以作为键,但是只有一个;HashMap中可以有一个或多个键所对应的值为null。
(4)数组初始化和扩容机制 HashTable的默认容量为11, HashMap的默认容量为16,HashTable不需要底层数组容量一定要为2的整数次幂,HashMap的底层数组容量一定要为2的整数次幂。 HashTable 扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。
2、TreeSet和HashSet 区别
HashSet是采用hash表来实现的。HashSet中的元素是无顺序排列的,add()、remove()以及contains()等方法都是复杂度为O(1)的方法。 TreeSet是采用树结构实现(红黑树算法)。元素是按顺序进行排列,但是add)、remove0以及contains0等方法都是复杂度为O(log(n))的方法。它还提供了一些方法来处理排序的set,如first(),last(),headSet(),tailSet()等等。
3、String buffer 和 String builder 区别
(1)StringBuffer与StringBuilder中的方法和功能是完全等价。 (2)StringBuffer中的方法大都采用了synchronized关键字修饰,所以StringBuffer是线程安全的。 StringBuilder中的方法没有synchronized关键字修饰,所以是线程不安全的。 (3)在单线程程序下,StringBuilder效率更快,因为StringBuilder不需要加锁,不具备多线程安全而StringBuffer则每次都需要判断锁,效率相对比较低。
4、什么是java序列化,如何实现java序列化?
序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。 序列化是为了解决在对对象流进行读写操作时所引发的问题。 序列化的实现:将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。
5、线程状态和条件
(1)新建状态(New): 线程对象创建后,就进入了新建状态。例如,Thread thread=new Thread()。
(2)就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
(3)运行状态(Running): 线程获取CPU权限进行执行。线程只能从就绪状态进入到运行状态。
(4)阻塞状态(Blocked): 阻塞状态是线程因为某种原因或异常放弃了CPU的使用权,暂时停止运行。直到线程再次进入就绪状态,才有机会转到运行状态。 阻塞的情况分三种: 1)等待阻塞---通过调用线程的 wait()方法,让线程等待某工作的完成。 2)同步阻塞---线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。 3)其他阻塞---通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
(5)死亡状态(Dead): 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
6、死锁
(1)产生死锁的原因 1)系统资源不足。 2)进程运行顺序有问题。 3)资源分配不当。 如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。
(2)产生死锁的四个必要条件 1)互斥条件: 一个资源每次只能被一个进程使用。
2)请求与保持条件: 一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3)不可剥夺条件: 进程已获得的资源,在末使用完之前,不能强行剥夺。
4)循环等待条件: 若干进程之间形成一种头尾相接的循环等待资源关系。 这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
(3)死锁的解除与预防 理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和解除死锁。所以,在系统设计、进程调度等方面注意如何不让这四个必要条件成立,如何确定资源的合理分配算法,避免进程永久占据系统资源。此外,也要防止进程在处于等待状态的情况下占用资源。因此,对资源的分配要给予合理的规划。
7、线程池
线程池就是事先将多个线程对象放到一个容器中,当使用的时候就不用new线程而是直接去池中拿线程即可,节省了开辟子线程的时间,提高的代码执行效率。
在JDK的java.util.concurrent.Executors中提供了生成多种线程池的静态方法。
(1)ExecutorService newCached ThreadPool=Executors.newCachedThreadPool();
(2)ExecutorService newFixed ThreadPool=Executors.newFixedThreadPool(4);
(3)ScheduledExecutorService newScheduled ThreadPool=Executors.newScheduledThreadPool(4);
(4)ExecutorService newSingle ThreadExecutor=Executors.newSingleThreadExecutor);
然后调用他们的execute方法即可。
优点: (1) 降低资源消耗。 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
(2) 提高响应速度。 当任务到达时,任务可以不需要等到线程创建就能立即执行。
(3) 提高线程的可管理性。 线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
8、Java 自带有哪几种线程池?
(1)newCachedThreadPool 执行很多短期异步任务,线程池根据需要创建新线程,但在先前构建的线程可用时将重用它们。可扩容,遇强则强。 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 这种类型的线程池特点是:工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger.MAXVALUE),这样可灵活的往线程池中添加线程。 如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。 在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。
(2)newFixedThreadPool 执行长期任务性能好,创建一个线程池,一池有N个固定的线程,有固定线程数的线程。 创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。FixedThreadPool是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。
(3)newSingleThreadExecutor 一个任务一个任务的执行,一池一线程。 创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。 单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。
(4)newScheduleThreadPool 创建一个定长的线程池,而且支持定时的以及周期性的任务执行。例如延迟3秒执行。
9、Java中有几种类型的流?
10、字节流如何转为字符流?
字节输入流转字符输入流通过InputStreamReader实现,该类的构造函数可以传入InputStream对象。 字节输出流转字符输出流通过OutputStreamWriter 实现,该类的构造函数可以传入OutputStream对象。
11、常见的RuntimeException
(1)java.lang.NullPointerException 空指针异常;出现原因:调用了未经初始化的对象或者是不存在的对象。 (2)java.lang.ClassNotFoundException 指定的类找不到;出现原因:类的名称和路径加载错误;通常都是程序试图通过字符串来加载某个类时可能引发异常。 (3)java.lang.NumberFormatException 字符串转换为数字异常;出现原因:字符型数据中包含非数字型字符。 (4)java.lang.IndexOutOfBoundsException 数组角标越界异常,常见于操作数组对象时发生。 (5)java.lang.IllegalArgumentException 方法传递参数错误。 (6)java.lang.ClassCastException 数据类型转换异常。
12、final在java中有什么作用?
final作为Java中的关键字可以用于三个地方。用于修饰类、类属性和类方法。 特征:凡是引用final关键字的地方皆不可修改! (1)修饰类: 表示该类不能被继承。
(2)修饰方法: 表示方法不能被重写。
(3)修饰变量: 表示变量只能一次赋值以后值不能被修改(常量)。
13、String 属于基础的数据类型吗?
在Java中,数据类型分为引用类型和基本类型。 基本类型分为八种 整型byte,short,int,long 浮点型:float,double 字符型:char Boolean型:boolean String不是基本的数据类型,是final修饰的java类,是引用类型。
14、java中操作字符串都有哪些类?它们之间有什么区别?
主要是一下三种:String、StringBuffer、StringBuilder。
三种操作方式的区别: String是不可变的对象,对每次对String类型的改变时都会生成一个新的对象,StringBuffer 和 StringBuilder是可以改变对象的。 对于操作效率:StringBuilder>StringBuffer>String对于线程安全:StringBuffer是线程安全,可用于多线程。 StringBuilder是非线程安全,用于单线程。 不频繁的字符串操作使用String。反之,StringBuffer和StringBuilder都优于String所以,如果在项目中需要拼接字符串最好是采用StringBuffer 而非 String。
15、接口和抽象类的区别
他们都不能实例化对象,都可以包含抽象方法,而且抽象方法必须被继承的类全部实现。
区别: (1)抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。 (2)抽象类要被子类继承,接口要被类实现。 (3)接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现。 (4)接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。 (5)抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。 (6)抽象方法只能声明,不能实现,接口是设计的结果,抽象类是重构的结果。 (7)抽象类里可以没有抽象方法。 (8)如果一个类里有抽象方法,那么这个类只能是抽象类。 (9)抽象方法要被实现,所以不能是静态的,也不能是私有的。 (10)接口可继承接口,并可多继承接口,但类只能单继承。
16、BIO、NIO、AIO之间的区别
(1)BIO(Blocking I/O 同步阻塞I/O模式) 在服务端启动一个ServerSocket,然后在客户端启动Socket来对服务端进行通信,默认情况下服务端需要对每个请求建立一堆线程等待请求,而客户端发送请求后,先咨询服务端是否有线程相应,如果没有则会一直等待或者遭到拒绝请求,如果有的话,客户端会线程会等待请求结束后才继续执行。 简单来说,用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行,BIO方式适用于连接数目比较小且固定的架构。
(2)NIO (New I/O 同步非阻塞的I/O模型) NIO基于Reactor,当socket有流可读或可写入socket时,操作系统会相应的通知引用程序进行处理,应用再将流读取到缓冲区或写入操作系统。当一个连接创建后,不需要对应一个线程,这个连接会被注册到多路复用器上面,所以所有的连接只需要一个线程就可以搞定,当这个线程中的多路复用器进行轮询的时候,发现连接上有请求的话,才开启一个线程进行处理。 简单来说,用户进程发起一个IO操作以后边可返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪(引入不必要的CPU资源浪费)NIO方式适用于连接数目多且连接比较短的架构。 BIO的每个连接一个单独的线程,而NIO则是每个连接共用一个线程。
(3)AIO(Asynchronous I/O 异步非阻塞的IO模型) 当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。read/write方法都是异步的,完成后会主动调用回调函数。AIO是一个有效请求一个线程。 简单来说,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知。不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。 AIO方式使用于连接数目多且连接比较长的架构。
17、java 容器结构
18、Collection 和Collections 的区别
java.util.Collection是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。
Collection 接口在Java类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。
Collections 则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。
19、List、Set、Map三者的区别
20、HashSet的实现原理
(1)基于HashMap实现的,默认构造函数是构建一个初始容量为16,负载因子为0.75的HashMap。封装了一个HashMap对象来存储所有的集合元素,所有放入HashSet中的集合元素实际上由HashMap的key来保存,而HashMap的value则存储了一个PRESENT,它是一个静态的Object对象。
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
(2)当我们试图把某个类的对象当成HashMap的key,或试图将这个类的对象放入HashSet中保存时,重写该类的equals(Object obj)方法和hashCode0方法很重要,而且这两个方法的返回值必须保持一致:当该类的两个的hashCode)返回值相同时,它们通过equals0方法比较也应该返回true。通常来说,所有参与计算hashCode)返回值的关键属性,都应该用于作为equals)比较的标准。 (3)HashSet的其他操作都是基于HashMap的。