java基础
面向对象
和面向过程进行区分,面向过程比较直接高效,而面向对象更易于复用、扩展和维护
面向对象的特性:封装、继承、多态
JDK、JRE、JVM
- JDK:Java Development Kit 开发工具
- JRE:Java Runtime Environment 运行环境
- JVM:Java Virtual Machine 虚拟机
=== 和 equals
对比变量的值和变量存储的地址
hashCode和equals
hashCode() 是获取哈希码(散列码),它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,Java 中的任何类都包含有hashCode() 函数。
- 如果两个对象相等,则hashcode一定也是相同的
- 两个对象相等,对两个对象分别调用equals方法都返回true
- 两个对象有相同的hashcode值,它们也不一定是相等的
- 因此,equals方法被覆盖过,则hashCode方法也必须被覆盖
- hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个
- 对象无论如何都不会相等(即使这两个对象指向相同的数据)
final关键字的作用
- 修饰类:表示类不可被继承
- 修饰方法:表示方法不可被子类覆盖,但是可以重载
- 修饰变量:表示变量一旦被赋值就不可以更改它的值
- 修饰基本数据类型的变量,则其数值一旦在初始化之后便不能更改
- 修饰引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。但是引用的值是可变的
String、StringBuffer、StringBuilder
- String是不可变的,如果尝试去修改,会新⽣成一个字符串对象,StringBuffer和StringBuilder是可变的
- StringBuffer是线程安全的,StringBuilder是线程不安全的,所以在单线程环境下StringBuilder效率会更高
重载和重写
- 重载:发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。
- 重写:发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围 小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为private则子类就不能重写该方法。
接口和抽象类
- 抽象类可以存在普通成员函数,而接口中只能存在public abstract 方法
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的。
- 抽象类只能继承一个,接口可以实现多个
泛型中extends和super的区别
<? extends T>
表示包括T在内的任何T的子类<? super T>
表示包括T在内的任何T的父类
List & Set
- List:有序,按对象进入的顺序保存对象,可重复,允许多个NULL元素对象,可以使用Iterator取出所有元素,在逐一遍历,还可以使用get() 获取指定下标的元素
- Set:无序,不可重复,最多允许有一个NULL元素对象,取元素时只能用Iterator接口取得所有元素,在逐一遍历各个元素
ArrayList & LinkedList
- 底层数据结构不同,ArrayList底层是基于数组实现的,LinkedList底层是基于链表实现的
- 由于底层数据结构不同,他们所适用的场景也不同,ArrayList更适合随机查找,LinkedList更适合删除和添加,查询、添加、删除的时间复杂度不同
- 另外ArrayList和LinkedList都实现了List接口,但是LinkedList还额外实现了Deque接口,所以LinkedList还可以当做队列来使用
HashMap & HashTable
- HashMap方法没有synchronized修饰,线程非安全,
- HashTable线程安全,HashMap允许key和value为NULL,而HashTable不允许
底层实现:数组+链表实现,jdk8开始链表高度到8、数组长度超过64,链表转变为红黑树,元素以内部类Node节点存在
- 计算key的hash值,二次hash然后对数组长度取模,对应到数组下标
- 如果没有产生hash冲突(下标位置没有元素),则直接创建Node存入数组
- 如果产生hash冲突,先进行equal比较,相同则取代该元素,不同,则判断链表高度插入链表,链表高度达到
- 并且数组长度到64则转变为红黑树,长度低于6则将红黑树转回链表
- key为NULL,存在下标0的位置
java中有哪些类加载器
- BootStrapClassLoader:默认负责加载%JAVA_HOME%lib下的jar包和class文件
- ExtClassLoader:负责加载%JAVA_HOME%/lib/ext文件夹下的jar包和class类
- AppClassLoader是自定义类加载器的父类,负责加载classpath下的类文件
父子类的关系 BootStrapClassLoader -> ExtClassLoader -> AppClassLoader
类加载器的双亲委派模型
JVM在加载一个类时,会调用AppClassLoader的loadClass方法来加载这个类,不过在这个方法中,会先使用ExtClassLoader的loadClass方法来加载类, 同样ExtClassLoader的loadClass方法中会先使用BootstrapClassLoader来加载类, 如果BootstrapClassLoader加载到了就直接成功,如果BootstrapClassLoader没有加载到,那么ExtClassLoader就会自己尝试加载该类, 如果没有加载到,那么则会由AppClassLoader来加载这个类。
所以,双亲委派指得是,JVM在加载类时,会委派给Ext和Bootstrap进行加载,如果没加载到才由自己进行加载。
GC如何判断对象是否能被回收
- 引用计数法:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收,
- 可达性分析法:从GCRoots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GCRoots没有任何引用链相连时,则证明此对象是不可用的,那么虚拟机就判断是可回收对象。
GCRoots的对象有
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI(即一般说的Native方法)引用的对象
JVM线程共享区
- 堆区和方法区是所有线程共享的,
- 栈、本地方法栈、程序计数器是每个线程独有的
JVM问题排查
- 使用
jmap
来查看JVM中各个区域的使用情况 - 通过
jstack
来查看线程的运行情况,比如哪些线程阻塞、是否出现了死锁