第一章 Java基础(上)

简介: 本文主要介绍了Java编程中的基础语法与面向对象、集合类等内容适合初学者和面试准备者参考学习。

1、基础语法与面向对象

1.1 重载与重写的区别

  • 重载是对象的方法之间,它们方法名相同,但方法的参数列表不同
  • 重写是父子类(包括接口与实现类)中两个同名方法,它们方法名相同,且方法的参数列表相同
  • 重载在编译阶段,由编译器根据传递给方法的参数来区分方法,例如
  • 而重写是在运行阶段,由虚拟机解释器去获取引用对象的实际类型,根据类型才能确定该调用哪个方法,例如
  • 有没有发生重写,可以使用 @Override 来检查
MyObject obj = ...
obj.test(123);   // 应该是调用 test(int x) 这个方法
obj.test("abc"); // 应该是调用 test(String x) 这个方法
Super obj = ...
obj.test();     // 到底是调用父类,还是子类的 test 方法,必须检查引用对象的实际类型才能确定

P.S.

  • 括号内的说明是为了严谨,自己知道就行,回答时不必说出,这样比较简洁
  • 个人觉得,在回答方法重载时,不必去细说什么参数的类型、个数、顺序,就说参数列表不同就完了
  • 个人觉得,重点在于点出:重载是编译时由编译器来区分方法,而重写是运行时由解释器来区分方法
  • 语法细节,问了再说,不问不必说
  • 重写时,子类方法的访问修饰符要 >= 父类方法的访问修饰符
  • 重写时,子类方法抛出的检查异常类型要 <= 父类方法抛出的检查异常类型,或子类不抛异常
  • 重写时,父子类的方法的返回值类型要一样,或子类方法返回值是父类方法返回值的子类

1.2 == 与 equals 的区别

  • 对于基本类型,== 是比较两边的值是否相同
  • 对于引用类型,== 是比较两边的引用地址是否相同,用来判断是否引用着同一对象
  • equals 要看实现
  • Object.equals(Object other) 的内部实现就是 ==,即判断当前对象和 other 是否引用着同一对象
  • 比如 String,它的内部实现就是去比较两个字符串中每个字符是否相同,比较的是内容
  • 比如 ArrayList,它的内部实现就是去比较两个集合中每个元素是否 equals,比较的也是内容


1.3 String,StringBuilder 和 StringBuffer 的区别

  • 它们都可以用来表示字符串对象
  • String 表示的字符串是不可变的,而后两者表示的字符串是内容可变的(可以增、删、改字符串里的内容)
  • StringBuilder 不是线程安全的,StringBuffer 是线程安全的,而 String 也算是线程安全的

适用场景

  • 大部分场景下使用 String 就足够了
  • 如果有大量字符串拼接的需求,建议用后两者,此时
  • 此字符串对象需要被多线程同时访问,用 StringBuffer 保证安全
  • 此字符串对象只在线程内被使用,用 StringBuilder 足够了

另外针对 String 类是 final 修饰会提一些问题,把握下面几点

  • 本质是因为 String 要设计成不可变的,final 只是条件之一
  • 不可变的好处有很多:线程安全、可以缓存等


1.4 说说 Java 中的异常

异常的重要继承关系如图所示,其中

  • Throwable 是其它异常类型的顶层父类
  • Error 表示无法恢复的错误,例如 OutOfMemoryError 内存溢出、StackOverflowError 栈溢出等
  • 这类异常即使捕捉住,通常也无法让程序恢复正常运行
  • Exception 表示可恢复的错误,处理方式有两种
  • 一是自己处理,用 catch 语句捕捉后,可以进行一些补救(如记录日志、恢复初始状态等)
  • 二是用 throw 语句将异常继续抛给上一层调用者,由调用者去处理
  • Exception 有特殊的子类异常 RuntimeException,它与 Exception 的不同之处在于
  • Exception 被称之为检查异常,意思是必须在语法层面对异常进行处理,要么 try-catch,要么 throws
  • RuntimeException 和它的子类被称为非检查异常(也可以翻译为字面意思:运行时异常),在语法层面对这类异常并不要求强制处理,不加 try-catch 和 throws 编译时也不会提示错误
  • 常见的非检查异常有
  • 空指针异常
  • 算术异常(例如整数除零)
  • 数组索引越界异常
  • 类型转换异常
  • ...


2、集合类

2.1 你知道的数据结构有哪些

线性结构

  • 动态数组:相对于普通数组可以扩容
  • java 中 ArrayList 就属于动态数组
  • 数组的特点是其中元素是连续存储的
  • 链表:由多个节点链在一起
  • java 中的 LinkedList 就属于链表
  • 链表的特点是其中元素是不连续存储的,每次需要根据当前节点,才能找到相邻节点
  • 栈:符合 First In Last Out(先进后出)规则
  • java 中的 LinkedList 可以充当栈
  • 它的 push 方法向栈顶添加元素
  • 它的 pop 方法从栈顶移除元素
  • 它的 peek 方法从栈顶获取元素(不移除)
  • 队列:符合 First In First Out(先进先出)规则
  • java 中 LinkedList 也可以充当队列
  • 它的 offer 方法用来向队列尾部添加元素(入队)
  • 它的 poll 方法用来从队列头部移除元素(出队)

非线性结构

  • 优先级队列:在队列基础上增加了优先级,队列会根据优先级调整元素顺序,保证优先级高的元素先出队
  • java 中 PriorityQueue 可以作为优先级队列
  • 它底层用大顶堆或小顶堆来实现
  • 它适用于实现排行榜、任务调度等编码
  • 它特别适合于流式数据的处理,利用它能够大大节省内存
  • Hash 表(哈希表,也叫散列表):由多对 key - value 组成,会根据 key 的 hash 码把它们分散存储在数组当中,其中 key 的 hash 码与数组索引相对应
  • java 中的 HashMap,Hashtable 都属于哈希表
  • 它特别适用于实现数据的快速查找
  • 红黑树:可以自平衡的二叉查找树,相对于线性结构来说,拥有更好的性能
  • java 中的 TreeMap 属于红黑树
  • 跳表:多级链表结构,也能达到与红黑树同级的性能,且实现更为简单
  • java 中的 ConcurrentSkipListMap 用跳表结构实现
  • redis 中的 SortedSet 也是用跳表实现
  • B+ 树:可以自平衡的 N 叉查找树
  • 关系型数据库的索引常用 B+ 树实现

P.S.

  • 以上数据结构不必全部掌握,根据自己实际情况,捡熟悉的回答即可
  • 以上仅是这些数据结构的简述,关于它们的详细讲解,请参考黑马《数据结构与算法》课程:


2.2 说说 java 中常见的集合类

重要的集合接口以及实现类参考下图

classDiagram

class Collection {<<interface>>}

class List {<<interface>>}

class Set {<<interface>>}

class Map {

<<interface>>

entrySet()*

keySet()*

values()*

}

Collection <|-- List

Collection <|-- Set

List <|.. ArrayList

List <|.. LinkedList

List <|.. Vector

Set <|.. HashSet

Map <|.. HashMap

Map <|.. TreeMap

Map <|.. Hashtable

Map <|.. ConcurrentHashMap

HashMap <|.. LinkedHashMap

Set <-- Map

Collection <-- Map

接口

  • 接口四个:Collection、List、Set、Map,它们的关系:
  • Collection 是父接口,List 和 Set 是它的子接口
  • Map 接口与其它接口的关系
  • Map 调用 entrySet(),keySet() 方法时,会创建 Set 的实现
  • Map 调用 values() 方法时,会用到 Collection 的实现

List 实现(常见三个)

  • ArrayList 基于数组实现
  • 随机访问(即根据索引访问)性能高
  • 增、删由于要移动数组元素,性能会受影响
  • 【进阶】但如果增、删操作的是数组尾部不牵涉移动元素
  • LinkedList 基于链表实现
  • 随机访问性能低,因为需要顺着链表一个个才能访问到某索引位置
  • 增、删性能高
  • 【进阶】说它随机访问性能低是相对的,如果是头尾节点,无论增删改查都快
  • 【进阶】说它增删性能高也是有前提的,并没有包含定位到该节点的时间,把这个算上,增删性能并不高
  • Vector 基于数组实现
  • 相对于前两种 List 实现是线程安全的
  • 【进阶】一些说法说 Vector 已经被舍弃,这是不正确的

Set 实现

  • HashSet 内部组合了 HashMap,利用 Map key 唯一的特点来实现 Set
  • 集合中元素唯一,注意需要为元素实现 hashCode 和 equals 方法
  • 【进阶】Set 的特性只有元素唯一,有些人说 Set 无序,这得看实现,例如 HashSet 无序,但TreeSet 有序

Map 实现(常见五个)

  • HashMap 底层是 Hash 表,即数组 + 链表,链表过长时会优化为红黑树
  • 集合中 Key 要唯一,并且它需要实现 hashCode 和 equals 方法
  • LinkedHashMap 基于 HashMap,只是在它基础上增加了一个链表来记录元素的插入顺序
  • 【进阶】这个链表,默认会记录元素插入顺序,这样可以以插入顺序遍历元素
  • 【进阶】这个链表,还可以按元素最近访问来调整顺序,这样可以用来做 LRU Cache 的数据结构
  • TreeMap 底层是红黑树
  • Hashtable 底层是 Hash 表,相对前面三个实现来说,线程安全
  • 【进阶】它的线程安全实现方式是在 put,get 等方法上都加了 synchronized,锁住整个对象
  • ConcurrentHashMap 底层也是 Hash 表,也是线程安全的
  • 【进阶】它的 put 方法执行时仅锁住一个链表,并发度比 Hashtable 高
  • 【进阶】它的 get 方法执行不加锁,是通过 volatile 保证数据的可见性

P.S.

  • 未标注的是必须记住的部分
  • 标注【进阶】的条目是该集合比较有特色的地方,回答出来就是加分项,不过也根据自己情况来记忆


2.3 HashMap 原理(数据结构)

底层数据结构:数组+链表+红黑树

接下来的回答中要点出数组的作用为啥会有冲突如何解决冲突

  • 数组:存取元素时,利用 key 的 hashCode 来计算它在数组中的索引,这样在没有冲突的情况下,能让存取时间复杂度达到 O(1)
  • 冲突:数组大小毕竟有限,就算元素的 hashCode 唯一,数组大小是 n 的情况下要放入 n+1 个元素,根据鸽巢原理,肯定会发生冲突
  • 解决冲突:一种办法就是利用链表,将这些冲突的元素链起来,当然在在此链表中存取元素,时间复杂度会提高为 O(n)

接下来要能说出为什么在链表的基础上还要有红黑树

  • 树化目的是避免链表过长引起的整个 HashMap 性能下降,红黑树的时间复杂度是 O(log{n})

有一些细节问题可以继续回答,比如树化的时机【进阶】

  • 时机:在数组容量达到 >= 64 链表长度 >= 8 时,链表会转换成红黑树
  • 如果树中节点做了删除,节点少到已经没必要维护树,那么红黑树也会退化为链表


2.4 HashMap 原理(扩容)

扩容因子:0.75 也就是 3/4

  • 初始容量 16,当放入第 13 个元素时(超过 3/4)时会进行扩容
  • 每次扩容,容量翻倍
  • 扩容后,会重新计算 key 对应的桶下标(即数组索引)这样,一部分 key 会移动到其它桶中


2.5 HashMap 原理(方法执行流程)

以 put 方法为例进行说明

  1. 产生 hash 码。
  1. 先调用 key.hashCode() 方法
  2. 为了让哈希分布更均匀,还要对它返回结果进行二次哈希,这个结果称为 hash
  3. 二次哈希就是把 hashCode 的高 16 位与低 16 位做了个异或运算
  1. 搞定数组。
  1. 如果数组还不存在,会创建默认容量为 16 的数组,容量称为 n
  2. 否则使用已有数组
  1. 计算桶下标。
  1. 利用 (n - 1) & hash 得到 key 对应的桶下标(即数组索引)
  2. 也可以用 hash % n 来计算,但效率比前面的方法低,且有负数问题
  3. 用 (n - 1) & hash 有前提,就是容量 n 必须是 2 的幂(如 16,32,64 ...)
  1. 计算好桶下标后,分三种情况
  1. 如果该桶位置还空着,直接根据键值创建新的 Node 对象放入该位置即可
  2. 如果该桶是一条链表,沿着链表找,看看是否有值相同的 key,有走更新,没有走新增
  • 走新增逻辑的话,是把节点链到尾部(尾插法)
  • 新增后还要检查链表是否需要树化,如果是,转成红黑树
  • 新增的最后要检查元素个数 size,如果超过阈值,要走扩容逻辑
  1. 如果该桶是一棵红黑树,走红黑树新增和更新逻辑,同样新增的最后要看是否需要扩容

P.S.

  • 以上讲解基于 jdk 1.8 及以上版本的 HashMap 实现
  • 考虑到 jdk 1.7 已经很少使用了,故不再介绍基于 1.7 的 HashMap,有需求可以看 b 站黑马面试视频


相关文章
|
11月前
|
Java
第一章初识java
第一章初识java
|
8月前
|
Java
java基础 - 个人笔记
java基础 - 个人笔记
297 2
|
11月前
|
Java
Java基础—笔记—方法篇
该内容是关于编程中方法的介绍。方法是实现特定功能的代码块,主要好处是提高代码复用性和维护性。方法定义有三种形式:无参无返回值、有参无返回值和有参有返回值。方法重载是指在同一类中,方法名相同但参数类型或个数不同的多个方法,便于简化调用。参数传递时,基本数据类型在方法内修改不会影响外部,而引用数据类型(除String外)的修改会影响外部。
34 0
|
11月前
|
存储 Java 数据库
java基础的知识点(一)
java基础的知识点(一)
|
11月前
|
Oracle Java 关系型数据库
《Java 核心技术卷1 基础知识》第二章 Java 程序设计环境 笔记
《Java 核心技术卷1 基础知识》第二章 Java 程序设计环境 笔记
58 1
|
存储 Java
Java基础之复习(下)
Java基础之复习(下)
73 0
|
分布式计算 安全 Java
第一章、初识Java
系统学习JavaSE内容,已经学过借助本篇文章快速复习,刚开始学习可以跟进博主进度。每周都会更新博客。
100 0
|
开发框架 安全 Oracle
Java 基础入门 | 第一章:认识Java语言
1.Java语言的发展历程 2.Java语言的特性 3.Java开发环境的搭建 4.第一个Java程序的编写
175 0
Java 基础入门 | 第一章:认识Java语言
|
存储 缓存 安全
java基础复习
这是前段时间学长让复习java基础时候发了一个问题的文档,根据问题在网上查阅资料文档总结记录下来的内容(我把问题的文档也放到下面方便查阅) java基础复习 开发者客栈-帮助开发者面试的平台-顽强网络 (developers.pub) http://c.biancheng.net/java/10/ Java 语言是一种分布式的面向对象语言,具有面向对象、平台无关性、简单性、解释执行、多线程、安全性等很多特点 1. 面向对象 Java 是一种面向对象的语言,它对对象中的类、对象、继承、封装、多态、接口、包等均有很好的支持。为了简单起见,Java 只支持类之间的单继承,但是可以使用接口来实现多继承
125 0
|
安全 Oracle Java
java简介与基础知识
Java是一门广泛使用的面向对象编程语言,有许多优秀的特性,本文将带您了解java.
190 0
下一篇
oss创建bucket