java(二)
本文最后更新于 2024年7月9日 下午
Java(二)
32. Collection和 Collections的区别?
- Collection 是一个接口,它是 Set、List等容器的父接口;
- Collections 是个一个工具类,提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等。
33. 常见的Exception有哪些?
常见的RuntimeException:
ClassCastException
//类型转换异常IndexOutOfBoundsException
//数组越界异常NullPointerException
//空指针ArrayStoreException
//数组存储异常NumberFormatException
//数字格式化异常ArithmeticException
//数学运算异常
checked Exception:
NoSuchFieldException
//反射异常,没有对应的字段ClassNotFoundException
//类没有找到异常IllegalAccessException
//安全权限异常,可能是反射时调用了private方法
34. 接口是否可继承(extends)接口?抽象类是否可实现 (implements)接口?抽象类是否可继承具体类(concrete class)?
- 接口可以继承接口,而且支持多重继承。抽象类可以实现(implements)接口,抽象类可继承具体类也可以继承抽象类。
35. 一个”.java”源文件中是否可以包含多个类(不是内部类)? 有什么限制?
- 可以,但一个源文件中最多只能有一个公开类(public class)而且文件名必须和 公开类的类名完全保持一致。
36. 内部类可以引用它的包含类(外部类)的成员吗?有没有 什么限制?
一个内部类对象可以访问创建它的外部类对象的成员,包括私有成员。
1
2
3
4
5
6
7
8
9
10
11class Human {
String name = "aa";
public String gender = "man";
private int height = 190;
class Man {
{
System.out.println(name + gender + height);
}
};
}但是在外部要获得内部类需要先实例化外部类再实例化内部类(可见21):
Human.Man man = new Human().new Man();
37.Error和Exception的区别?
Error:JVM 无法解决的严重问题,如栈溢出StackOverflowError
、内存溢出OOM
等。程序无法处理的错误。
Exception:其它因编程错误或偶然的外在因素导致的一般性问题。可以在代码中进行处理。如:空指针异常、数组下标越界等。
38. 运行时异常和非运行时异常(checked)的区别?
unchecked exception
包括RuntimeException
和Error
类,其他所有异常称为检查(checked)异常。
RuntimeException
由程序错误导致,应该修正程序避免这类异常发生。checked Exception
由具体的环境(读取的文件不存在或文件为空或sql异常)导致的异常。必须进行处理,不然编译不通过,可以catch或者throws。
39. throw和throws的区别?
- throw:用于抛出一个具体的异常对象。
- throws:用在方法签名中,用于声明该方法可能抛出的异常。子类方法抛出的异常范围更加小,或者根本不抛异常。
40. BIO/NIO/AIO区别的区别?
同步阻塞IO(BIO): 用户进程发起一个IO操作以后,必须等待IO操作的真正完成后,才能继续运行。
同步非阻塞IO(NIO): 客户端与服务器通过Channel连接,采用多路复用器轮询注册的Channel
。提高吞吐量和可靠性。用户进程发起一个IO操作以后,可做其它事情,但用户进程需要轮询IO操作是否完成,这样造成不必要的CPU资源浪费。
异步非阻塞IO(AIO): 非阻塞异步通信模式,NIO的升级版,采用异步通道实现异步通信,其read和write方法均是异步方法。用户进程发起一个IO操作,然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知。类似Future模式。
41. 守护线程是什么?
- 守护线程是运行在后台的一种特殊进程。
- 它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。
- 在 Java 中垃圾回收线程就是特殊的守护线程。
42. try{} 里有一个 return 语句,那么紧跟在这个 try后的 finally{} 里的代码会不会被执行,什么时候被执行,在 return 前还是后?
- 会执行,在方法返回调用者前执行.
43. 实现 Serializable 接口之后,为什么还要显示指定 serialVersionUID 的值?
如果不显式指定 serialVersionUID,JVM 在序列化时会根据属性自动生成一个 serialVersionUID,然后与属性一起序列化,再进行持久化或网络传输. 在反序列化时,JVM 会再根据属性自动生成一个新版 serialVersionUID,然后将这个新版 serialVersionUID 与序列化时生成的旧版 serialVersionUID 进行比较,如果相同则反序列化成功,否则报错.
如果显式指定了 serialVersionUID,JVM 在序列化和反序列化时仍然都会生成一个 serialVersionUID,但值为我们显示指定的值,这样在反序列化时新旧版本的 serialVersionUID 就一致了.
如果我们的类写完后不再修改,那么不指定 serialVersionUID,不会有问题,但这在实际开发中是不可能的,我们的类会不断迭代,一旦类被修改了,那旧对象反序列化就会报错。
44. static 属性为什么不会被序列化?
因为序列化是针对对象而言的,而 static 属性优先于对象存在,随着类的加载而加载,所以不会被序列化.
看到这个结论,是不是有人会问,serialVersionUID 也被 static 修饰,为什么 serialVersionUID 会被序列化? 其实 serialVersionUID 属性并没有被序列化,JVM 在序列化对象时会自动生成一个 serialVersionUID,然后将我们显示指定的 serialVersionUID 属性值赋给自动生成的 serialVersionUID.
45. transient关键字的作用?
Java语言的关键字,变量修饰符,如果用 transient 声明一个实例变量,当对象存储时,它的值不需要维持。
也就是说被 transient 修饰的成员变量,在序列化的时候其值会被忽略,在被反序列化后, transient 变量的值被设为初始值, 如 int 型的是 0,对象型的是 null。
46. 线程的创建方式
- 继承Thread类创建线程
- 实现Runnable接口创建线程
- 使用 Callable 和 Future 创建线程, 有返回值
- 使用线程池创建线程
1 |
|
47. 当一个线程进入一个对象的 synchronized 方法 A 之后,其它线程是否可进入此对象的 synchronized 方法 B?
- 不能。其它线程只能访问该对象的非同步方法,同步方法则不能进入。因为非静态方法上的 synchronized 修饰符要求执行方法时要获得对象的锁,如果已经进入A方法说明对象锁已经被取走,那么试图进入 B 方法的线程就只能在等锁池(注意不是等待池哦)中等待对象的锁。
48. 请说出与线程同步以及线程调度相关的方法。
- wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
- sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理 InterruptedException异常;
- notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由 JVM确定唤醒哪个线程,而且与优先级无关;
- notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;
49. synchronized关键字的用法?
- synchronized关键字可以将对象或者方法标记为同步,以实现对对象和方法的互斥访问,可以用 synchronized(对象) {… }定义同步代码块,或者在声明方法时将 synchronized 作为方法的修饰符。
50. 启动一个线程是调用 run()还是 start()方法?
- 启动一个线程是调用 start() 方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由 JVM 调度并执行,这并不意味着线程就会立即运行。run() 方法是线程启动后要进行回调(callback)的方法。
51. 什么是线程池(thread pool)?
在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在 Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁,这就是”池化资源”技术产生的原因。线程池顾名思义就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。
Java 5+中的 Executor接口定义一个执行线程的工具。它的子类型即线程池接口是 ExecutorService。要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,因此在工具类 Executors 面提供了一些静态工厂方法,生成一些常用的线程池(使用方法可见46),如下所示:
- newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
- newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
- newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说 JVM)能够创建的最大线程大小。
- newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
- newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。
52. 线程生命周期以及状态关系
- 其中 Running表示运行状态,线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
- Runnable表示就绪状态,也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。
- Blocked表示阻塞状态,阻塞状态又有多种情况,可能是因为调用 wait()方法进入等待池,也可能是执行同步方法或同步代码块进入等锁池,或者是调用了 sleep()方法或 join()方法等待休眠或其他线程结束,或是因为发生了 I/O中断。阻塞的情况分三种:
- 等待阻塞 – 通过调用线程的wait()方法,让线程等待某工作的完成。
- 同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
- 其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
53. 简述 synchronized 和 java.util.concurrent.locks.Lock的异同?
- Lock 是 Java 5 以后引入的新的 API,和关键字 synchronized 相比主要相同点:Lock 能完成 synchronized 所实现的所有功能;
- 主要不同点:Lock 有比 synchronized 更精确的线程语义和更好的性能,而且不强制性的要求一定要获得锁。synchronized 会自动释放锁,而 Lock 一定要求程序员手工释放,并且最好在 finally 块中释放(这是释放外部资源的最好的地方)。
54. Java中如何实现序列化,有什么意义?
- 序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决对象流读写操作时可能引发的问题(如果不进行序列化可能会存在数据乱序的问题)。
- 要实现序列化,需要让一个类实现 Serializable接口,该接口是一个标识性接口,标注该类对象是可被序列化的,然后使用一个输出流来构造一个对象输出流并通过 writeObject(Object) 方法就可以将实现对象写出(即保存其状态);如果需要反序列化则可以用一个输入流建立对象输入流,然后通过 readObject 方法从流中读取对象。序列化除了能够实现对象的持久化之外,还能够用于对象的深度克隆。
55. Java中有几种类型的流?
- 字节流和字符流。
- 字节流继承于 InputStream、OutputStream,字符流继承于 Reader、Writer。在 java.io包中还有许多其他的流,主要是为了提高性能和使用方便。
56. 谈谈你对反射的理解
反射机制
所谓的反射机制就是java语言在运行时拥有一项自观的能力。通过这种能力可以彻底了解自身的情况为下一步的动作做准备。
Java的反射机制的实现要借助于4个类:class,Constructor,Field,Method;其中class代表的时类对 象,Constructor-类的构造器对象,Field-类的属性对象,Method-类的方法对象。通过这四个对象我们可以粗略的看到一个类的各个组成部分。
Java反射的作用
- 在Java运行时环境中,对于任意一个类,可以知道这个类有哪些属性和方法。对于任意一个对象,可以调用它的任意一个方法。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java 语言的反射(Reflection)机制。
Java 反射机制提供功能
在运行时判断任意一个对象所属的类。
在运行时构造任意一个类的对象。
在运行时判断任意一个类所具有的成员变量和方法。
在运行时调用任意一个对象的方法
57. GET 和POST 的区别
- GET 请求的数据会附在URL 之后(就是把数据放置在 HTTP 协议头中),以?分割URL 和传输数据,参数之间以&相连,如:login.action?name=zhagnsan&password=123456。POST 把提交的数据则放置在是 HTTP 包的包体中。
- GET 方式提交的数据最多只能是 1024 字节,理论上POST 没有限制,可传较大量的数据。其实这样说是错误的,不准确的:“GET 方式提交的数据最多只能是 1024 字节”,因为 GET 是通过 URL 提交数据,那么 GET 可提交的数据量就跟URL 的长度有直接关系了。而实际上,URL 不存在参数上限的问题,HTTP 协议规范没有对 URL 长度进行限制。这个限制是特定的浏览器及服务器对它的限制。IE 对URL 长度的限制是2083 字节(2K+35)。对于其他浏览器,如Netscape、FireFox 等,理论上没有长度限制,其限制取决于操作系统的支持。
- POST 的安全性要比GET 的安全性高。注意:这里所说的安全性和上面 GET 提到的“安全”不是同个概念。上面“安全”的含义仅仅是不作数据修改,而这里安全的含义是真正的 Security 的含义,比如:通过 GET 提交数据,用户名和密码将明文出现在 URL 上,因为(1)登录页面有可能被浏览器缓存,(2)其他人查看浏览器的历史纪录,那么别人就可以拿到你的账号和密码了,除此之外,使用 GET 提交数据还可能会造成 Cross-site request forgery 攻击。
- Get 是向服务器发索取数据的一种请求,而 Post 是向服务器提交数据的一种请求,在 FORM(表单)中,Method 默认为”GET”,实质上,GET 和 POST 只是发送机制不同,并不是一个取一个发!
58. Cookie 和Session 的区别
Cookie 是 web 服务器发送给浏览器的一块信息,浏览器会在本地一个文件中给每个 web 服务器存储 cookie。以后浏览器再给特定的 web 服务器发送请求时,同时会发送所有为该服务器存储的 cookie
Session 是存储在 web 服务器端的一块信息。session 对象存储特定用户会话所需的属性及配置信息。当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去
Cookie 和session 的不同点
无论客户端做怎样的设置,session 都能够正常工作。当客户端禁用 cookie 时将无法使用 cookie
在存储的数据量方面:session 能够存储任意的 java 对象,cookie 只能存储 String 类型的对象