文档: https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/basis/Java%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86.md
JAVA基础知识
Java 程序从源代码到运行
先由jdk中的javac将源代码.java文件编译成jvm可以理解的.class字节码文件;
再由jvm解释器将字节码文件解释称机器可执行的二进制机器码, 最终执行
java基本数据类型
hashCode()与equals()
- hashCode()是啥?
hashCode()的作用是获取哈希码, 也叫散列码, 具体是将对象的物理地址转换成一个整数,然后该整数通过hash函数的算法就得到hashcode, hashcode代表着该对象在hash表中的位置;
hashCode()方法的存在是为了提升查找的便捷性;
比如容器中有100个元素, 只能存不同的元素, 那第101个元素的加入需要跟1-100逐个对比; 如果利用hashCode(), 将100个元素分布在hash表1-5号位置上, 每个位置20个元素, 则第101个元素的加入, 则最多需对比hash表的相同位置, 再对比20个元素, 极大的提升了查找效率.
- 为啥重写equals()时必须重写hashCode()方法
对于Map, Set等类型使用时, 重写了对象的equals()方法, 对象的相等取决于内容, 此时hashCode()返回的hashcode也应考虑对象内容相等, 而不是只关联对象的物理地址; 以保证相同的对象返回相同的hashcode
hashCode()与 equals()的相关规定
- 如果两个对象相等,则 hashcode 一定也是相同的
- 两个对象相等,对两个对象分别调用 equals 方法都返回 true
- 两个对象有相同的 hashcode 值,它们也不一定是相等的
- 因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
- hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
常量池
Java 基本类型的包装类的大部分都实现了常量池技术,即 Byte,Short,Integer,Long,Character,Boolean;前面 4 种包装类默认创建了数值[-128,127] 的相应类型的缓存数据,Character创建了数值在[0,127]范围的缓存数据,Boolean 直接返回True Or False。如果超出对应范围仍然会去创建新的对象
String StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的?
String 类中使用 final 关键字修饰字符数组来保存字符串,private final char value[],所以String 对象是不可变的。
而 StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的。
String 中的对象是不可变的,也就可以理解为常量,线程安全。
StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。
StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。
异常
finally 中有return
当 try 语句和 finally 语句中都有 return 语句时,在方法返回之前,finally 语句的内容将被执行,并且 finally 语句的返回值将会覆盖原始的返回值。如下:
public class Test {
public static int f(int value) {
try {
return value * value;
} finally {
if (value == 2) {
return 0;
}
}
}
}
如果调用 f(2),返回值将是 0,因为 finally 语句的返回值覆盖了 try 语句块的返回值。
线程与进程
- 进程是程序的一次执行过程,是系统运行程序的基本单位,也是系统分配资源的最小单位
- 线程是进程划分成的更小的运行单位,是一个进程中代码的不同执行路线。
- 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间及一些进程级的资源,线程上下文切换比进程上下文切换要快得多
线程生命周期
java代码执行顺序
如果子类继承父类,则先执行父类的静态变量和静态代码块 ,再执行子类的静态变量和静态代码块。 同样,接着执行父类非静态代码块和构造函数 ,再执行子类的非静态代码块和构造函数 。