1:JDK 和 JRE 有什么区别 ?
- JDK:Java Development Kit(java 开发工具包) 的简称,提供了Java的开发环境和运行环境
- JRE:Java Runtime Environment(Java 运行时环境) 的简称,为Java的运行提供了所需的环境
- 具体来说JDK其实包含了JRE,同时还包含了编译Java的编译器Javac,还包含了程序调试和分析的工具;
- 简单来说:如要只是要运行java程序,装个jre就好使(客户);如果要是编写代码,就装个JDK.(程序员)
- 总结: 能用jdk用jdk,因为jdk比较全
2:== 和 equals 的区别是什么 ?
- == : 对于基本数据类型比较的是 值 是否相同(8种基本类型); 对于引用类型,比较的是引用是否相同;
- equals : 本质上(Object)是 == ,只不过是 String,Integer 等类重写了equals方法,把他变成了值比较;
- 总结: 对于8种基本类型,只能用== 如果 非8基本类型 并且 非Object(重写了Object的equals()) 比较引用 用 == ,比较值用 equals
3:两个对象的hashCode() 相同,那么equals()也一定为true,对吗 ?
- 不对,两个对象的hashCode() 相同,equals() 不一定为true;
- 举例:如: String str1 = “Ab”; str2 = “BC”; str1的hashCode和str2的hashCode相同,但equals()却不为true;
- 原因:
/**
* 比较hashCode和equals
* <p>
* 通过查看String的hashCode() 函数源码得:
* 以 String = "abc"; 为例
* (1) : h = hash(这个值默认为0) --> h = 0;
* (2) : char[] val = {'a','b','c'}; len = 3; 将循环3次,然后走公式 h = 31 * h + val[i];
* (3) : h = var[0] 也就是 a 的UniCode值 为 97 第一次循环为 h = 97;
* (4) : h = 31 * 97 + var[1](也就是 b的UniCode值 为98) h = 31 * 97 + 98 = 3105,第二次循环为 h = 3105;
* (5) : h = 31 * 3195 + var[2](也就是c的UniCode值 为99) h = 31 * 3105 + 99 = 96354 第三次循环结束;
* (6) : 循环结束,之后会 hash = h ,得 hash = 96354 ; 之后return 得出 abc 的 hashCode 为 96354
* <p>
* 通过以上可得hashCode是根据值对应的UniCode表中的值 + 算法(不同jdk版本可能不同),那么如果可以找到两个不同的值,但是通过算法却得到hashCode一样的这样一个数,那么这个问题就解决了。
* 通过查阅资料 UniCode表{@link JavaUtils#printUniCode(char)()}+ 自己相关计算得出: String str1 = "Ef" ; String str2 = "FG"; 时 两个hashCode 相同
* 于是乎 我发现了一个规律 对于 字母来说 比如 ABC 来说 相等的hashcode---> Ab,BC; BCD ---> Bc,CD; 以此类推 。
* 最后再啰嗦一句 : 对于公式 h = 31 * h + val[i] ; 中 为啥参数是31 ,我的猜想应该是在开发String的时候,通过一定的推算,得出 31 可以比较可以让HashCode值不重复。
*/
void show1() {
String str1 = new String("Ef");
String str2 = new String("FG");
System.out.println("str1的hashCode = " + str1.hashCode());
System.out.println("str2的hashCode = " + str2.hashCode());
System.out.println(str1.equals(str2));
}
4:final 在Java中有什么作用 ?
- final 修改的类叫最终类,该类不能被继承;
- final 修改的方法不能被重写;
- final 修饰的变量叫 常量,常量必须被初始化,初始化之后就不能被修改;
5:Java 中的 Math.round(-1.5) 等于多少 ?
- 等于 -1 ,round() 是四舍五入函数,需要注意的是负数5是舍的,例如: Math.round(1.5)的值是2,而 Math.round(-1.5) 是 -1;
- 注意:这块把这个拿出来就是因为是负数的情况,比较特殊,用的时候注意下就好。
6:Java 中的操作字符串都有那些类? 它们之间有什么区别 ?
- 从对象是否可变比较:
- String 声明的是不可变的对象 是 final类型的,这个value用来存放字符串,所以每次操作都会生成新对象。
- StringBuffer,和 StringBuilder 都继承 AbstractStringBuilder ,两个类对应的构造方法都是调用父类的并且在父类中存储数据的数组 char[] value 不是用final修饰的,所以对象是可变的。value 长度默认为16;当发现长度不够时,会自动扩容,扩展为原来长度的2倍+2。
- 从线程安全性比较:
- StringBuffer 很多方法都被 synchronized修饰了,所以是线程安全的,但是效率就比较低了。
- StringBuilder 正好相反,效率高了,但是线程不安全;
- 总结: 执行效率(高–>低): StringBuilder —> StringBuffer —> String; 所以在日常开发中,应尽量少用String,以提高程序效率。
7:String str = “i” 和 String str = new String(“i”) 一样吗 ?
- 不一样; 内存分配方式不一样
- String str = “i” 是向常量池中创建一个常量,有就返回该常量地址,没有就创建一个常量。
- String str = new String(“i”) 每次 new时都会向堆内存中开辟一块空间。
- 总结:对于第一种方式,将会在常量池中存放一个常量 i 并返回str地址比如是 xx001; 对于第二种方式,就会在堆内存中创建一个对象 值 存为 i ,并返回地址,比如说是 xx111;
8:如何将字符串反转 ?
- 使用StringBuilder 或者 StringBuffer 的 reverse() 方法; (日常知道就好)
9:抽象类必须有抽象方法吗 ?
- 不必须,抽象类不一定非要有抽象方法,但一个抽象方法一定是一个抽象类。
10:普通类和抽象类有何区别 ?
- 普通类
- 不能有抽象方法
- 可以直接实例化
- 继承抽象类,必须实现其抽象方法,如果不实现需也定义成抽象方法;
- 抽象类
11:抽象类能被final修饰吗 ?
- 不能; 定义抽象类,就是让其他类继承的,如果用 final修饰后,那么就不能被其他类继承,违反 抽象类 出现的意义,同时编辑器也会报错。
12:接口和抽象类有和区别 ?
- 实现
- 抽象类用 extends
- 接口用 implements
- 构造函数
- 实现数量
- 访问修饰符
- 抽象类不能被修饰成private(因为要被继承)
- 接口默认是public的
13:Java 中的 IO 流分为几种 ?
- 功能
- 类型
- 字节流 : 处理字节或二进制对象
- 字符流: 处理字符或字符串
- 字节流和字符流的区别
- 字节流是以 8 位为单位来input,output数据的
- 字符流是以 16 位 来input,output 数据的
- 总结: 关于流的使用,应该结合具体的使用场景来使用就好
14:BIO,NIO,AIO 有什么区别 ?
- BIO
- Block IO 同步阻塞式 IO , 就是我们平常使用过的传统IO。
- 优点: 简单使用方便。
- 缺点: 并发处理能力低。
- 说明:比如以前的ServerSocket 就是基于这种方式的,在高并发场景下不适用,一个连接一个线程 ; Client如果有连接请求时,Server就需要有起一个线程来处理,如果连接不做啥事也会起着。
- NIO
- New IO 同步非阻塞 IO , 是传统IO(BIO) 的升级版,Client和Server 通过 Channel(通道) 通讯,实现了多路复用 。
- 说明:服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理;
- AIO
- Asynchronous IO 是NIO的升级版, 也叫 NIO2,实现了 异步非堵塞IO,异步IO的操作基于事件和回调机制 。
- 说明:服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。