一、基础
1.标识符注意点
所有的标识符都应该以字母(A-Z或者a-z),美元符($),或者下划线(_)开始
首字符之后可以是字母 ,美元符 ,下划线或者数字的任何字符组合
不能使用关键字作为方法名或者变量名
标识符是大小写敏感的
合法标识符举例: age、$salary、_value、__1_value
非法标识符举例:123abc、-salary、#abc
2.数据类型
强类型语言
要求变量的使用要严格符合规定,所有变量都必须先定义后才能使用
Java 数据类型分为两大类:
- 基本数据类型:primitive type
- 引用数据类型:reference type
3.变量
类变量(成员变量)
实例变量:从属于对象
局部变量:必须声明和初始化值
public class variable{
public static void mian(String[] args){
static int allClicks = 0; // 成员变量
String str = "Hello World"; // 实例变量
public void method(){
int i = 0; // 局部变量
}
}
}
4.运算符
幂运算 2^3
Math.pow(3,2);
二、流程控制
1.用户交互Scanner
java.util.Scanner 是 java5 的新特性
可以通过 Scanner 类来获取用户的输入 Scanner scanner = new Scanner(System.in);
通过 Scanner 类的 next() 与 nextLine() 方法获取输入的字符串,再读取前我们需要使用 hasNext() 与 hasNextLine() 判断是否还有输入的数据。
属于IO流的类,最后要关闭scanner.close();
- next():
- 一定要读取到有效字符后才可以结束输入
- 对输入有效字符之前遇到的空白,next() 会自动消除
- 只有输入有效字符后才将其后面的空白作为分隔符或结束符
- next() 不能得到带有空格的字符串
- nextLine():
- 以Enter为结束,也就是说nextLine() 是返回输入回车前的所有字符
- 可以获得空白
2.选择结构
switch 从 JavaSE 7 开始支持字符串 String 类型了。
3.循环练习题
do{
// 代码块
} while (布尔表达式)
for(初始化; 布尔表达式; 更新){
// 代码语句
}
public class Practice3 {
public static void main(String[] args) {
for (int i = 1; i < 10; i++) {
for (int j = 1; j <= i; j++) {
int multi = i * j;
System.out.print(j + "*" + i + "=" + multi + "\t");
}
System.out.println();
}
}
}
/*
1.素数:
判断条件1:只能被1和本身整除的称为素数;
判断条件2:在区间(1,x/2)中找不到能整除素数x的整数;
判断条件3:在区间(1,sqrt(x))中找不到能整除素数x的整数; 算术平方根 sqrt(square root)
外层循环遍历被除数i(因为1既不是素数也不是和数,所以直接从2开始遍历)
*/
public class Practice {
public static void main(String[] args) {
for (int i = 2; i <= 100; i++) {
//定义一个逻辑值flag,初始值为true
boolean flag = true;
//内层循环遍历除数j
//(注意:此处若不取边界,则当i=4时,j=2会因为小于i/2=2而直接跳出内循环)
for (int j = 2; j <= (i / 2); j++) {
//判断是否存在除数j能整除i,若存在,则修改flag的值并跳出循环
if (i % j == 0) {
flag = false;
break;
}
}
//根据flag的值判断是否输出i
if (flag) {
System.out.print(i + " ");
}
}
}
}
三、方法
1.重载(overload)
在一个类中,有相同的函数名称,不同的形式参数。
- 方法重载的规定:
- 方法的名称必须相同
- 参数列表必须不同(个数不同,类型不同,参数排列顺序不同)
- 方法的返回类型可以不同可以相同
2.可变参数
在方法声明中,在指定参数类型后加一个省略号(…)
一个方法中只能指定一个可变参数,必须是方法的最后一个参数,任何普通参数都要在它之前声明。
四、数组
数组元素通过索引访问的,数字索引从0开始。
1.数组的四个特点
- 其长度是确定的。数组一旦被创建,它的大小就是不可改变的。
- 其元素类型是相同的,不允许出现混合类型。
- 数组的元素可以是任何类型,包括基本数据和引用类型。
- 数组变量是引用类型,数组也可以看成对象,数组中的每个元素都是该对象的成员变量。数组本身就是对象,Java 对象存储在堆中,因此数组无论保存的是基本类型还是引用类型,数组对象本身是在堆中
2.内存分析
3.冒泡排序-八大排序
时间复杂度O(n²)
public static int[] bubbleSort(int[] array) {
// 外层循环,最大排序 length-1 次
for (int i = 0; i < array.length - 1; i++) {
// 定义flag判断数组是否排序好
boolean flag = false;
// 内层循环,如果第一个数大于第二个数,则交换位置。
for (int j = 0; j < array.length - 1 - i; j++) {
if (array[j + 1] < array[j]) {
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
flag = true;
}
}
if (!flag){
break;
}
}
return array;
}
五、面向对象
OOP(Object-Oriented Program)以类的方式组织代码,以对象的方式封装数据。
1.方法的调用
静态方法可以通过类名.方法名()
直接调用
非静态方法通过实例化类new 类名.方法名()
调用
2.值传递和引用传递
3.构造器
- 必须和类名相同
- 没有返回值类型,不能写 void
作用
- new 本质是在调用构造方法
- 初始化对象的值
4.封装
对象代表什么,就要封装对应的数据,并提供数据对应的行为。
继承的特点
- Java 只能单继承,不能多继承,但是可以多层继承
- Java 中所有的类都直接访问或者间接继承于Object类
- 子类只能访问父类中的非私有成员
属性私有 get/set
子类继承父类的哪些内容?
虚方法表:非public ,非static ,非final
5.字符串
String
== 号比较的是什么?
基本数据类型比较基础值,引用数据类型比较的是地址值
字符串的比较
boolean equals方法(要比较的字符串)
完全一样是true,不同为falseboolean equalsIgnoreCase(要比较的字符串)
忽略大小写
遍历字符串
public char charAt(int index)
根据索引返回字符public int length()
字符串的长度- 数组的长度:
数组名.length
为属性 - 字符的长度:
字符串对象.length()
为方法
截取字符串
String subString(int beginIndex, int endIndex)
截取字符串 [左闭右开)String subString(int beginIndex)
从当前截取到末尾
字符串替换
String replace(旧值, 新值)
替换
StringBuilder
StringBuilder 是一个容器,创建之后内容是可变的。
- 字符串的拼接
- 字符串的反转
常用方法
方法名 | 说明 |
---|---|
public StringBuilder append(任意类型) |
添加数据,并返回对象本身 |
public StringBuilder reverse() |
反转容器中的内容 |
public int length() |
返回长度(字符出现的个数) |
public String toString() |
通过toString()就可以实现把StringBuilder转换为String |
StringJoiner
构造方法名 | 说明 |
---|---|
public StringJoiner(间隔符号) |
创建一个StringJoiner 对象,指定字符拼接的间隔符 |
public StringJoiner(间隔符号, 开始符号, 结束符号) |
创建一个StringJoiner对象,指定字符拼接的间隔符、开始符号、结束符号 |
小结
字符串拼接的底层原理
- 如果没有变量参与,都是字符串相加,编译后就是拼接结果,会复用串池的字符串
- 如果有变量参与,每一行拼接代码,都会在内存中创建新的字符串,浪费内存
StringBuilder的底层原理
- 默认容量16,如果添加元素超过默认值,扩容:老容量*2+2 = 34
- 如果超过默认扩容的数值,扩容:以实际长度为准
6.关键词解释
Static(静态)
- 静态方法只能访问静态变量和静态方法
- 非静态方法可以访问静态变量或者静态方法,也可以访问非静态的成员变量和非静态的成员方法。
- 静态方法中是没有this 关键字
总结:
- 静态方法中,只能访问静态。
- 非静态可以访问所有。
- 静态中没有this 关键字
权限修饰符
7.继承
继承本质是对某一批类的抽象。“is a”关系
extends
扩展,子类是父类的扩展。
Java中类只有单继承,没有多继承。
1)Super关键字
- super 调用父类的构造方法,必须放在构造方法的第一个
- super 只能出现在子类的方法或构造方法中
- super 和 this 不能同时调用构造方法
2)重写
- 方法名和参数类型必须相同
- 子类修饰符的范围一定要大于等于父类
- 子类抛出的异常一定要小于等于父类
子类可以与父类方法体不同。
父类的引用指向了子类
8.多态
- 多态是方法的多态,属性没有多态
- 父类和子类,有联系 类型转换异常! ClassCastException!
- 存在条件:继承关系,方法需要重写,父类的引用指向子类对象
- 编译看左边,执行看右边
instanceof & 类型转换
instanceof :对象 instanceof 类
判断左边的对象是否属于右边的类
类型转换:
- 子类转换成父类:向上转型
- 父类转换成子类:向下转换 强制转换
static 父子类加载顺序:
父类静态变量
父类静态代码块
子类静态变量
子类静态代码块
父类普通变量
父类构造代码块
父类构造方法
子类普通变量
子类构造代码块
子类构造方法
抽象类
abstract
- 不能 new 抽象类,只能靠子类实现它:约束
- 抽象类中不一定有抽象方法,可以写普通方法
- 抽象类可以有构造方法
- 抽象方法一定在抽象类中
接口
interface 是一种规则,对行为的抽象。
- 接口中默认的是隐式的
public abstract
修饰的方法 - 接口中定义的变量是
public static final
修饰的常量 - 接口不能被实例化,没有构造方法
- 子类必须重写接口中的方法
JDK8以后接口中新增的方法
1)允许在接口中定义默认方法,需要使用关键字 default 修饰
- 作用:解决接口升级的问题
- 接口中默认方法的定义格式:
- 格式:public default 返回值类型 方法名(参数列表){}
- 范例:
public default void show(){ }
- 接口中默认方法的注意事项:
- 默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉 default 关键字
- public 可以省略,default 不能省略
- 如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
- 允许在接口中定义静态方法,需要使用关键字 static 修饰
2)接口中静态方法的定义格式:
- 格式:public static 返回值类型 方法名(参数列表)
- 范例:
public static void show(){ }
- 接口中静态方法的注意事项:
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
- public 可以省略,static 不能省略
JDK9新增的方法
接口定义私有方法
私有方法分为两种:普通的私有方法,静态的私有方法
适配器设计模式
- 当一个接口中抽象方法过多,但是我只要使用其中一部分的时候,就可以适配器设计模式
- 书写步骤:
编写中间类XXXAdapter,实现对应的接口
对接口中的抽象方法进行空实现
让真正的实现类继承中间类,并重写需要用的方法
为了避免其他类创建适配器类的对象,可以用 abstract 进行修饰
内部类
- 内部类可以直接访问外部类的成员,包括私有
- 外部类要访问内部类的成员,必须创建对象
- 成员内部类
- 外部类编写方法,对外提供调用内部类对象(private修饰时)
- 外部类名.内部类名 对象名 = 外部类对象.内部类对象
范例:Outer.Inner oi = new Outer().new Inner();
- 静态内部类
- 外部类.内部类 对象名 = new 外部类.内部类名();
范例:Outer.Inner oi = new Outer.Inner();
- 局部内部类
- 匿名内部类
- 格式
new 类名或者接口名(){
重写方法;
};
- 格式细节
- 包含了继承或实现,方法重写,创建对象。
- 整体就是一个类的子类对象或者接口的实现类对象
- 使用场景
- 当方法的参数是接口或者类时,以接口为例,可以传递这个接口的实现类对象,如果实现类只要使用一次,就可以用匿名内部类简化代码。
六、API
Math
System
Runtime
Object
正则表达式
爬虫
贪婪爬取:获取尽可能多的的数据
非贪婪爬取:获取尽可能少的的数据
时间
date
- 如何创建日期对象?
Date date = new Date ();
Date date = new Date (指定毫秒值);
- 如何修改时间对象的毫秒值
- setTime(毫秒值)
- 如何获取时间对象中的毫秒值
- getTime();
SimpleDateFormat
- SimpleDateFormat 的两个作用
- 格式化
- 解析
- 如何指定格式
- yyyy年MM月dd日 HH:mm:ss
Calendar
概述:
- Calendar 代表了系统当前时间的日历对象,可以单独修改、获取时间中的年,月,日
- 细节:Calendar 是一个抽象类,不能直接创建对象
获取 Calendar 日历类对象的方法
public static Calendar getInstance()
获取当前时间的日历对象
Calendar 常用方法
方法名 | 说明 |
---|---|
public final Date getTime() |
获取日期对象 |
public final setTime(Date date) |
给日历设置日期对象 |
public long getTimeInMillis() |
拿到时间毫秒值 |
public void setTimeMillis(long millis) |
给日历设置时间毫秒值 |
public int get(int field) |
取日历中的某个字段信息 |
public void set(int field, int value) |
修改日历的某个字段信息 |
public void add(int field, int amount) |
为某个字段增加/减少指定的值 |
总结
- Calendar表示什么?
- 表示一个时间的日历对象
- 如何获取对象
- 通过 getInstance 方法获取对象
- 常用方法:
- setXxx:修改
- getXxx:获取
- add:在原有的基础上进行增加或减少
- 细节点:
- 日历类中月份的范围:0~11
- 日历类中星期的特点:星期日是一周中的第一天
Array
Lambda表达式
七、集合
集合存储引用数据类型和包装类
单列集合(Collection)
单列集合的祖宗,它的功能全部单列集合都可以继承。
方法名称 | 说明 |
---|---|
public boolean add(E e) |
把给定的对象添加到当前合集中 |
public void clear() |
清空集合中所有的元素 |
public boolean remove(E e) |
把给定的对象在当前集合中删除 |
public boolean contains(Object obj) |
判断当前集合中是否包含给定的对象 |
public boolean isEmpty() |
判读当前集合是否为空 |
public int size() |
返回集合中元素的个数/集合的长度 |
遍历
1、迭代器遍历(Iterator)
迭代器在 Java 中的类是 Iterator,迭代器是集合专用的遍历方式。
方法名称 | 说明 |
---|---|
Iterator<E> iterator() |
返回迭代器对象,默认指向当前集合的0索引 |
boolean hasNext() |
判读当前位置是否有元素,有元素返回true,没有元素返回false |
E next() |
获取当前位置的元素,并将迭代器对象移向下一个位置 |
迭代器的细节注意点:
- 报错 NoSuchElementException
- 迭代器遍历完毕,指针不会复位
- 循环中只能用一个 next 方法
- 迭代器遍历时,不能用集合的方法进行增加或删除
- 如果要删除:可以用迭代器提供的 remove 方法进行删除(没有添加方法)
2、增强for遍历
所有的单列集合和数组才能用增强for循环
格式:
for(数据类型 变量 : 数组/集合){
代码块;
}
3、lambda表达式遍历
public class TestFor {
public static void main(String[] args) {
// 创建集合对象
Collection<String> collection = new ArrayList<>();
// 添加元素
collection.add("aaa");
collection.add("bbb");
collection.add("ccc");
// iterater 迭代器遍历
Iterator<String> it = collection.iterator();
while (it.hasNext())
System.out.println(it.next());
// forEach 遍历元素
collection.forEach(System.out::println);
}
}
List
特点:
- 有序:存和取的元素顺序一致
- 有索引:可以通过索引操作元素
- 可重复:存储的元素可以重复
| 方法名称 | 说明 |
| --- | --- |
|void add(int index,E element)
| 在此集合中的指定位置插入指定的元素 |
|E remove(int index)
| 删除指定索引处的元素,返回被删除的元素 |
|E set(int index,E element)
| 修改指定索引处的元素,返回被修改的元素 |
|E get(int index)
| 返回指定索引处的元素 |
ArrayList
ArrayList集合底层原理
- 利用空参创建的集合,在底层创建一个默认长度为0的数组
- 添加一个元素时,底层会创建一个新的长度为10的数组
- 存满时,会扩容1.5倍
- 如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准
| ArrayList成员方法 | 方法名 | 说明 |
| --- | --- | --- |
| 增 |boolean add(E e)
| 添加元素,返回值表示是否成功添加 |
| 删 |boolean remove(E e)
| 删除指定元素,返回值表示是否删除成功 |
| |E remove(int index)
| 删除指定索引的元素,返回被删除的元素 |
| 改 |E set(int index,E e)
| 修改指定索引下的元素,返回原来的元素 |
| 查 |E get(int index)
| 获取指定索引的元素 |
| |int size()
| 集合的长度,也就是集合中元素的个数 |
LinkedList
- 底层数据结构是双向链表,查询慢,增删快,但是如果操作的是首尾元素,速度也是极快的
- LinkedList 本身多了很多直接操作首尾元素的特有API
| 特有方法 | 说明 |
| --- | --- |
|public void addFirst(E e)
| 在该列表开头插入指定的元素 |
|public void addLast(E e)
| 将指定的元素追加到此列表的末尾 |
|public E getFirst()
| 返回此列表中的第一个元素 |
|public E getLast()
| 返回此列表的最后一个元素 |
|public E removeFirst()
| 从此列表中删除并返回第一个元素 |
|public E removeLast()
| 从此列表中删除并返回最后一个元素 |
Set
- 无序:存取顺序不一样
- 不重复:可以去除重复
- 无索引:没有带索引的方法,所以不能用普通for循环遍历,也不能通过索引遍历
Set 集合的实现类
- HashSet:无序、不重复、无索引
- LinkedHashSet:有序、不重复、无索引
- TreeSet:可排序、不重复、无索引
Set 接口中的方法上基本上与 Collection 的 API 一致
HashSet
HashSet底层原理
- HashSet集合底层采取哈希表存储数据
- 哈希表是一种对于增删改查数据性能都较好的结构
JDK8以前:新元素存入数组,老元素挂在新元素下面
JDK8以后:新元素直接挂在老元素下面
注意:
- JDK8以后,当链表长度超过8,而且数组长度大于等于64时,自动转换为红黑树。
- 如果集合中存储的是自定义对象,必须要重写 hashCode 和 equals 方法
LinedHashSet
- LinkedHashSet 集合的特点和原理是怎么样的?
- 有序、不重复、无索引
- 这里的有序指的是保证存储和取出的元素顺序一致
- 原理:底层数据结构是哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序
- 在以后如果要数据去重,我们使用哪个?
- 默认使用HashSet
- 如果要求去重且存取有序,才使用LinkedHashSet
HashCode(哈希值)
哈希值
- 根据 hashCode 方法算出来的int类型的整数
- 该方法定义在 Object 类中,所有对象都可以调用,默认使用地址值进行计算
- 一般情况下,会重写 hashCode 方法,利用对象内部的属性值计算哈希值
TreeSet
TreeSet 的特点
- 不重复、无索引、可排序
- 可排序:按照元素的默认规则(由小到大)排序
- TreeSet 集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好
TreeSet 集合默认的规则
- 对于数据类型:Integer,Double,默认按照从小到大的顺序进行排序
- 对于字符、字符串类型:按照字符在ASCII码表中的数字升序进行排序
TreeSet的两种比较方法
方法一
- 默认排序/自然排序:JavaBean类实现 Comparable 接口指定比较规则
方法二
- 比较器排序:创建 TreeSet 对象时候,传递比较器 Comparator 指定规则
TreeSet排序规则
/*
要操作的类实现 Comparable 接口,实现 compareTo 方法。
*/
@Override
public int compareTo(Student o){
// 指定排序规则
// 只看年龄,想要按照年龄的升序进行排列
return this.getAge() - o.getAge();
}
- this:表示当前要添加的元素
- o:表示已经在红黑树存在的元素
- 返回值:
- 负数:认为要添加的元素是小的,存左边
- 正数:认为要添加的元素时大的,存右边
- 0:认为要添加的元素已经存在,舍弃
总结
- 如果想要集合中的元素可重复
- 用 ArrayList 集合,基于数组的。(用的最多)
- 如果想要集合中的元素可重复,而且当前的增删操作明显多于查询
- 用 LinkedList 集合,基于链表的
- 如果想要集合的元素去重
- 用 HashSet 集合,基于哈希表。(用的最多)
- 如果想要集合中的元素去重,而且保证存取顺序
- 用 LinkedHashSet 集合,基于哈希表和双链表,效率低于 HashSet
- 如果想要集合中的元素进行排序
- 用 TreeSet 集合,基于红黑树,后续也可以用 List 集合实现排序
双列集合(Map)
双列集合的特点
- 数列集合一次需要存一对数据,分别为键和值
- 键不能重复,值可以重复
- 键和值是一一对应的,每一个键只能找到自己对应的值
- 键+值这个整体 我们称之为“键值对”或者“键值对对象”,在Java中叫做“Entry对象”
Map的常见API
Map是双列集合的顶层接口,它的功能是全部双列集合都可以继承使用的
方法名称 | 说明 |
---|---|
V put(k key,V value) |
添加元素 |
V remove(Objcet key) |
根据键删除键值对元素 |
void clear() |
移除所有的键值对元素 |
boolean contatinsKey(Object key) |
判断集合是否包含指定的键 |
boolean containsValue(Object value) |
判断集合是否包含指定的值 |
boolean isEmpty() |
判断集合是否为空 |
int size() |
集合的长度,也就是集合中键值对的个数 |
put 方法的细节:
- 添加/覆盖
- 在添加数据时,如果键不存在,那么直接把键值对对象添加到 map 集合中,方法返回null
- 在添加数据时,如果键是存在的,那么会把原有的键值对对象覆盖,会把被覆盖的值进行返回
HashMap
HashMap的特点
- HashMap 是 Map 里面的一个实现类
- 没有额外的特有方法,直接使用 Map 里面的方法就可以
- 特点是由键决定的:无序、不重复、无索引
- HashMap 跟 HashSet 底层原理是一模一样的,都是哈希表结构
- 依赖 hashCode 方法和 equals 方法保证键的唯一
- 如果键存储的是自定义对象,需要重新 hashCode 和 equals 方法
如果值存储自定义对象,不需要重新 hashCode 和 equals 方法
LinkedHashMap
- 由键决定:有序、不重复、无索引
- 这里的有序指的是 保证存储和取出的元素顺序一致
- 原理:底层数据结构依然是哈希表,只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序
HashTable
Properties
TreeMap
- TreeMap 跟 TreeSet 底层原理一样,都是红黑树结构
- 由键决定特性:不重复、无索引、可排序
- 可排序:对键进行排序
- 注意:默认按照键的从小到大进行排序,也可以自己规定键的排序规则
代码书写两种排序规则
- 实现 Comparable 接口,指定比较规则
- 创建集合时传递 Comparator 比较器对象,指定比较规则(当方法一不满足时,使用方法二)
问题
- TreeMap 添加元素的时候,键是否需要重写 hashCode 和 equals 方法?
此时是不需要重写的
- HashMap 是哈希表结构的,JDK8开始由数组、链表、红黑树组成的
既然有红黑树,HashMap 的键是否需要实现 Comparable 接口或者传递比较器对象呢?
不需要的,因为在HashMap 的底层,默认是利用哈希值的大小关系来创建红黑树的
- TreeMap 和 HashMap 谁的效率高?
如果是最坏情况,添加了8个元素,这8个元素形成了链表,此时 TreeMap 的效率要更高
但是这种情况出现的几率非常的少
一般而言,还是 HashMap 的效率要更高
- 你觉得 Map 集合中,如果键重复了,Java 会提供一个不会覆盖的 put 方法吗?
此时 putIfAbsent 本身不重要
传递一个思想:
代码中的逻辑都有两面性,如果我们只知道了其中的A面,而且代码中还发现了有变量可以控制两面性的发生。那么该逻辑一定会有B面。
习惯:
boolean 类型的变量控制,一般只有AB两面,因为 boolean 只有两个值
int 类型的变量控制,一般至少有三面,因为 int 可以取多个值。
- 三种双列集合,以后如何选择?
HashMap LInkedHashMap TreeMap
默认:HashMap(效率最高)
如果要保证存取有序:LinkedHashMap
如果要进行排序:TreeMap
数据结构
栈
后进先出,先进后出
队列
先进先出,后进后出
数组
数组是一种查询快,增删慢的模型
- 查询速度快:查询数据通过地址值和索引定位,查询任意数据耗时相同(元素在内存中是连续存储的)
- 删除效率低:要将原始数据删除,同时后面每个数据前移
- 添加效率极低:添加位置后的每个数据后移,再添加元素
链表
- 链表中的结点是独立的对象,在内存中是不连续的,每个节点包含数据值和下一个结点的地址
- 链表查询慢,无论查询哪个数据都要从头开始找
- 链表增删相对快
二叉树
二叉树的遍历方式
- 前序遍历: 根左右
- 中序遍历: 左根右
- 后序遍历: 左右根
- 层序遍历: 一层一层遍历
二叉查找树
小的存左边,大的存右边,一样的不存
平衡二叉树
规则:任意节点左右子树高度差不超过1
数据结构(平衡二叉树)需要旋转的四种情况
左左 | 一次右旋 |
---|---|
左右 | 先局部左旋,再整体右旋 |
右右 | 一次左旋 |
右左 | 先局部右旋,再整体左旋 |
红黑树
红黑树不是高度平衡的,它的平衡是通过“红黑规则”进行实现的
规则如下:
- 每个节点或是红色的,或者是黑色的
- 根节点必须是黑色
- 如果一个节点没有子节点或父节点,则该节点相应的指针属性值为Nil ,这些Nil 视为叶节点,每个叶节点(Nil)是黑色的
- 不能出现两个红色节点相连的情况
- 对每个节点,从该节点到某个后代叶节点的简单路径上,均包含相同数目的黑色节点
红黑树增删改查的性能都很好
泛型
概述
JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查
泛型的格式:<数据类型>
注意:泛型只能支持引用数据类型
泛型的好处
- 统一数据类型
- 把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为在编译阶段类型就能确定下来。
泛型的细节
- 泛型中不能写基本数据类型
- 指定泛型的具体类型后,传递数据时,可以传入该类类型或者子类类型
- 如果不写泛型,类型默认是Objcet
泛型的继承和通配符
泛型不具备继承性,但是数据具备继承性
- ? 也表示不确定的类型
- ? extends E :表示可以传递E 或者E 所有子类类型
- ? super E :表示可以传递E 或者E 所有父类类型
应用场景:
- 如果我们在定义类、方法、接口的时候,如果类型不确定,就可以定义泛型类、泛型方法、泛型接口
- 如果类型不确定,但是指定以后只能传递某个继承体系中的,就可以用泛型的通配符
可变参数
JDK5的可变参数,方法形参的个数是可以发生变化的,0 1 2 3 ... n
格式:属性类型...名字 int...args
- 可变参数本质上就是一个数组
- 作用:在形参中接收多个数据
- 格式:数据类型...参数名称 例:
int...a
- 注意事项:
- 形参列表中可变参数只能有一个
- 可变参数必须放在形参列表的最后面
Collections
- java.util.Collecntions:是集合工具类
- 作用:Collections 不是集合,而是集合的工具类
| 方法名称 | 说明 |
| --- | --- |
|public static <T> boolean addAll(Collection<T> c,T... elements
| 批量添加元素 |
|public static void shuffle(List<?> list)
| 打乱List集合元素的顺序 |
|public static<T> void sort(List<T> list)
| 排序 |
|public static<T> void sort(List<T> list, Comparator<T> c)
| 根据指定的规则排序 |
|public static<T> int binarySearch(List<T> list, T key)
| 以二分查找法查找元素 |
|public static<T> void copy(List<T> list, T obj)
| 拷贝集合中的元素 |
|public static<T> int fill(List<T> list, T obj)
| 使用指定的元素填充集合 |
|public static<T> void max/min(Collection<T> coll)
| 根据默认的自然排序获取最大/小值 |
|public static<T> void swap(List<?> list, int i, int j)
| 交换集合中指定位置的元素 |
不可变集合
创建不可变集合的应用场景
- 如果某个数据不能被修改,把它防御性地拷贝到不可变集合是个很好的实践
- 当集合对象被不可信的库调用时,不可变形式是安全的
简单理解:不想让别人修改集合中的内容
创建不可变集合的书写格式
在 List、Set、Map 接口中,都存在静态的 of 方法,可以获取一个不可变的集合
方法名称 | 说明 |
---|---|
static<E> List<E> of(E...elements) |
创建一个具有指定元素的 List 集合对象 |
static<E> Set<E> of(E...elemnts) |
创建一个具有指定元素的 Set 集合对象 |
static<K,V> Map<K,V> of(E...elements) |
创建一个具有指定元素的 Map 集合对象 |
注意:这个集合不能添加,不能删除,不能修改
细节:
当我们要获取一个不可变得 Set 集合时,里面的参数一定要保证唯一性
创建 Map 不可变集合
- 键是不能重复的
- Map 里面的 of 方法,参数是有上限的,最多只能传递 20 个参数,10 个键值对
- 如果我们要传递多个键值对对象,数量大于10个,在 Map 接口中还有一个方法
如果是 JDK10 之后的版本可以用 copyOf()
方法
- 不可变集合的特点?
- 定义完成后不可以修改,或者添加、删除
- 如何创建不可变集合?
- List、Set、Map 接口中,都存在 of 方法可以创建不可变集合
- 三种方式的细节
- List:直接用
- Set:元素不能重复
- Map:元素不能重复、键值对数量最多是10个
超过10个用 ofEntrys 方法
Stream流
面试题
Stream 流的使用步骤
获取方式 | 方法名 | 说明 |
---|---|---|
单列集合 | default Stream<E> stream() |
Collection 中的默认方法 |
双列集合 | 无 | 无法直接使用 stream 流 |
数组 | public static<T> Stream<T> stream(T[] array) |
Arrays 工具类中的静态方法 |
一堆零散数据 | public static<T> Stream<T> of(T...values) |
Stream 接口中的静态方法 |
注意:Stream 接口中静态方法 of 的细节
方法的形参时一个可变参数,可以传递一堆零散的数据,也可以传递数组
但是数组必须是引用数据类型,如果传递基本数据类型,是会把整个数组当做一个元素,放到 Stream 当中
中间方法
终结方法
/*
创建一个 ArrayList 集合,并添加一下字符串
"zhangsan,23"
"lisi,24"
"wangwu,25"
保留年龄大于等于24岁的人,并将结果收集到 Map 集合中,姓名为键,年龄为值
*/
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "zhagnsan,23", "lisi,24", "wangwu,25");
Map<String, Integer> collect = list.stream()
.filter(s -> Integer.parseInt(s.split(",")[1]) >= 24)
// Collectors.toMap(键的规则,值的规则)
.collect(Collectors.toMap(s -> s.split(",")[0],
s -> Integer.parseInt(s.split(",")[1])));
System.out.println(collect);
}
总结
- Stream 流的作用
结合了Lambda表达式,简化合集、数组的操作
- Stream 的使用步骤
- 获取 Stream 流对象
- 使用中间方法处理数据
- 使用终结方法处理数据
- 如何获取 Stream 流对象
- 单列集合:Collection 中的默认方法 stream
- 双列集合:不能直接获取
- 数组:Arrays 工具类型中的静态方法 stream
- 一堆零散的数据:Stream 接口中的静态方法 of
- 常见方法
- 中间方法:filter,limit,skip,distinct,concat,map
- 终结方法:forEach,cout,collect
方法引用
- 什么是方法引用?
把已经存在的方法拿过来用,当做函数式接口中抽象方法的方法体
::
是什么符号?
方法引用符
- 方法引用时要注意什么?
- 需要有函数式接口
- 被引用的方法需要已经存在
- 被引用方法的形参和返回值需要跟抽象方法的形参和返回值保持一致
- 被引用方法的功能需要满足当前的要求
方法引用的分类
引用静态方法
格式:类名::静态方法
范例:Integer::parseInt
引用成员方法
格式:对象::成员方法
- 其他类:
其他类对象::方法名
- 本类:
this::方法名
(静态方法没有this) - 父类:
super::方法名
(静态方法没有super)
引用构造方法
格式:类名::new
范例:Student::new
引用数组的构造方法
格式:数据类型[]::new
范例:int[]::new
细节:数组的类型,需要跟流中数据的类型保持一致
总结
- 引用静态方法
类名::静态方法
- 引用成员方法
对象::成员方法
this::成员方法
super::成员方法
- 引用构造方法
类名::new
- 使用类名引用成员方法
类名::成员方法
不能引用所有类中的成员方法
如果抽象方法的第一个参数是A类型的
只能引用A类中的方法
- 引用数组的构造方法
数据类型[]::new
标签:Java,int,元素,基础,对象,集合,方法,public From: https://www.cnblogs.com/SunIcecream/p/17304378.html