黑马程序员技术交流社区
标题:
学习经历day03~day06
[打印本页]
作者:
木木鱼
时间:
2018-11-20 15:59
标题:
学习经历day03~day06
本帖最后由 木木鱼 于 2018-11-20 16:11 编辑
学习经历day03~day06
day03
数据结构
: 就是数据的存储方式
栈的特点
: 先进后出
入口和出口在同一侧
队列的特点:
先进先出
入口和出口在两端
数组的特点
:
查询快:
增删慢
链表的特点:
查询慢
增删快
红黑树: 是一种 平衡 二叉 查找 树
平衡: 左子节点和右子节点数量相等
二叉: 每个节点最多有2个子节点
查找: 节点存储的元素是按照大小顺序存储的
特点:
元素存储过程中就完成了大小排序
查询比链表快, 增删比数组快 (数组和链表的折中)
List集合体系的特点
:
元素存取有序
元素可以重复
有索引
LinkedList的特点:
查询慢
增删快
线程不安全效率高
LinkedList底层的数据结构: 链表
ArrayList的特点:
查询快
增删慢
线程不安全, 效率高
ArrayList底层的数据结构: 数组
Vector的特点:
查询慢
增删快
(同步)线程安全, 效率低
Vector底层的数据结构: 数组
Set集合体系特点
:
元素不可重复
没有索引
HashSet特点:
元素不可重复
没有索引
元素存取无序 (存入和取出顺序有可能不一致)
底层采用 哈希表 结构. (查询快) 哈希表 = 数组 + 链表或红黑树
Collections集合工具类: sort(List<T> list)
默认按照"升序"将元素排序
数字, 字母, 都可以按照升序排序
day04
Map集合特点:
是双列集合, 一个元素包含两个值 (键key, 值value)
key和value的类型可以相同, 也可以不同
key不允许重复, value可以重复
key和value是一一对应的, 一个键只能对应一个值
Map集合适合存储一对一关系的数据
HashSet底层使用的就是HashMap
LinkedHashSet底层使用的就是LinkedHashMap
keySet()遍历步骤
:
Map对象调用 keySet() 方法, 获取包含所有key的Set集合
遍历Set集合, 获取每个key
通过Map对象调用 get(Object key) 方法根据key获取到value
entrySet()方法遍历Map步骤:
Map对象调用 entrySet() 获取包含所有Entry对象的Set集合
遍历Set集合, 获取每个Entry对象
调用Entry对象的 getKey() 和 getValue() 方法获取键和值
Entry键值对对象
K getKey(): 获取Entry对象中的键
V getValue(): 获取Entry对象中的值
Entry对象就是一个节点, 节点中存储了key和value
拿到一个Entry对象就可以从中获取key和value
LinkedHashMap类
LinkedHashMap底层: 哈希表 + 链表
key不允许重复, 但key存取有序
LinkedHashSet的底层就是使用LinkedHashMap实现的
Hashtable类
Hashtable和HashMap:
相同点: 底层都是哈希表
不同点:Hashtable不允许存储null值和null键; HashMap允许存储null值和null键
Hashtable线程安全效率低; HashMap线程不安全效率高
JDK9对集合添加的优化 (方便集合初始化
)
static List<E> of(E... e): 返回包含指定元素的 不可变List 集合
static Set<E> of(E... e): 返回包含指定元素的 不可变Set 集合
static Map<K, V> of(K k1, V v1, ...): 返回包含指定键值对的 不可变Map 集合
注意:
of() 方法只适用于List接口, Set接口, Map接口, 不适用于接接口的实现类
of() 方法的返回值是一个不能改变的集合, 集合不能再使用 add(), put() 方法添加元素, 会抛出异常
Set接口和Map接口在调用 of() 方法的时候, 不能有重复的元素, 否则会抛出异常
将 不可变集合 的元素转移到常见集合实现类中
断点:
breakpoint, 在debug模式下, 程序运行到断点会暂停住, 便于我们在程序运行过程中查看
Debug调试程序:
day05
错误(Error
)
: 不能捕获处理的严重问题. 绝症
必须将程序停下来, 修改代码才能解决
异常(Exception):
可以捕获处理的问题.
程序执行起来后, 如果有合适的处理方式, 即使发生异常, 程序也能处理该异常并继续运行
编译时异常:编译时期就会发生的异常, 必须在编译时期处理
运行时异常: 编译时正常, 运行时才会发生的异常
JVM对于异常的默认处理方式是:
1. 将异常信息(内容, 原因, 位置)打印到控制台
2. 终止当前的程序
异常关键字: throw制造异常
作用:在方法中抛出指定的异常对象
1. throw 必须写在方法的内部
2. throw 后面new的异常对象, 必须是 "Exception" 或 "Excetion的子类" 的对象
3. 一个方法内部 throw 了一个异常对象, 则该方法可以分为2种情况来处理该异常:
如果 throw 的是"运行时异常"(RuntimeException及其子类)对象, 那么可以不处理
该异常最终会交给JVM处理, 结果就是: 打印异常信息到控制台, 并立刻结束程序
如果 throw 的是"编译时异常"(Exception及其子类), 则必须处理:
处理方式1: throws 抛出
处理方式2: try...catch 捕获
Objects工具类方法: requireNonNull()非空判断
操作各种对象相关的工具类
// 静态方法
static <T> T requireNonNull(T obj): 检查传入参数是不是null. 是null则抛异常; 非null则返回该对象
static <T> T requireNonNull(T obj, String message): 检查传入参数是不是null. 是null则抛异常, 同时带有原因; 非null则返回该对象
异常处理方式1: throws声明抛出
throws, 声明抛出 (方法自己不处理, 交给方法调用者处理, "甩锅给别人")
作用: 告诉方法的调用者, 调用此方法有可能发生异常, 需要在编写代码时处理
格式:
修饰符 返回值类型 方法名() throws 异常类名1, 异常类名2, ... {
}
异常处理方式2: 捕获异常
try...catch:
捕获并处理异常 (方法内部自己处理异常, 不交给别人, "自己背锅")
注意:
1. try 中可能会抛出多种异常, 就可以写多个 catch 分别处理每种异常
2. 如果 try 中产生了异常, 就会从产生异常的那一行代码直接跳转到对应的 catch 中执行处理代码, 然后继续执行 try...catch 之后的其他代码; 如果 try 中没有产生异常, 那就不会执行 catch , 执行完 try 中的代码后, 继续执行 try...catch 之后的其他代码
Throwable中的3个异常处理方法
java.lang.Throwable
// 成员方法
String getMessage(): 异常的信息. 没有原因返回null
String toString(): 异常的类型和原因信息
void printStackTrace(): 使用标准错误输出流打印异常信息
注意:
1. finally 必须和 try...catch 一起使用
2. finally 一般用于释放资源 (IO流时用到)
运行时异常, 可以不抛出 throws 也不捕获 try catch, 交给JVM处理
异常注意事项1:
捕获多个异常:
1. 多个异常分别 try...catch 处理
2. 一个 try 多个 catch
如果异常存在继承关系, 子类异常在上, 父类异常在下
3. 多个异常, 一次捕获一次处理
用Exception多态捕获
异常注意事项2: finally中有return语句
如果 finally 代码块中有 return 语句, 则永远返回 finally 中的 return 语句的值
应该避免在 finally 中写 return 语句
异常注意事项3: 子父类继承重写方法时的异常要求
子父类继承关系下, 子类重写父类带有throws的方法:
如果父类抛出多个异常, 子类重写父类方法时可以有3种方式:
1.抛出和父类相同的异常
2.抛出父类异常的子类
3.不抛出异常
父类方法没有抛出异常, 子类重写父类该方法时也不可抛出异常
此时子类产生该异常, 只能捕获处理, 不能声明抛出
一般情况下:
父类方法声明的异常是什么样的, 子类重写的方法声明异常就什么样, 保持一致即可
自定义异常类
如果Java提供的异常类不足以满足我们的需求, 我们也可以自己定义异常类
定义编译时异常: 继承 Exception
定义运行时异常: 继承 RuntimeException
多线程计算机基本概念: 并发与并行
并发
: (交替执行) 指两个或多个事件在"同一时间段内"发生
并行: (
同时执行) 指两个或多个事件在"同一时刻"发生 (同时发生)
并发
:是依靠电脑CPU快速地在多个任务之间切换执行实现的
进程
: 一个应用程序在内存中的一次执行过程
线程
: 是进程内的一个独立执行单元 (一条代码执行路径)
一个程序运行后至少有一个进程, 一个进程中可以包含多个线程
线程的调度方式
: Java使用上的是抢占是调度
1.
分时调度
: 所有线程轮流使用CPU, 平分占用CPU的时间
2.
抢占式调度
: 优先让优先级高的线程使用CPU; 如果优先级相同, 则随机选择一个线程执行
主线程
:
我们以前编写的代码, 也在一条线程中执行, 该线程叫作"main线程", 也称为"主线程"
如果我们没有额外创建线程, 那么我们的程序就只有一个线程, 即主线程, 此时程序是"单线程"的
单线程的执行特点:
同一个线程内的代码, 从上往下依次执行
创建多线程程序的方式: 继承Thread类
1. 定义类, 继承 Thread 类
2. 重写 run() 方法, run方法内部是线程要执行的任务
3. 创建Thread子类的对象, 调用 start() 方法启动线程
day06
Thread类: 表示线程. 实现了Runnable接口
// 构造方法
Thread Thread(): 创建Thead对象
Thread Thread(String threadName): 创建Thead对象并指定线程名
Thread Thread(Runnable target): 通过Runnable对象创建Thread对象
Thread Thread(Runnable target, String threadName): 通过Runnable对象创建对象并指定线程名
// 成员方法
void run(): 用于让子类重写, 表示该线程要执行的任务.不能直接调用
void start(): 启动线程, 即让线程开始执行run()方法中的代码
String getName(): 获取线程的名称
void setName(String name): 设置线程名称
// 静态方法
static Thread currentThread(): 返回对当前正在执行的线程对象的引用
static void sleep(long millis): 让所在线程睡眠指定的毫秒
Object类:
// 成员方法 (只能通过"锁对象"调用)
void notify(): 随机唤醒在同一个锁对象上的某一个处于等待状态的线程
void notifyAll(): 唤醒所有在同一个锁对象上处于等待状态的线程
void wait(): 让当前线程处于无限等待状态
void wait(long timeout): 让当前线程处于计时等待状态, 时间到或被唤醒后结束此状态
void wait(long timeout, int nanos): 让当前线程处于计时等待状态, 时间到或被唤醒后结束此状态
创建线程的第2种方式:
1. 定义类, 实现Runnable接口
2. 重写 run() 方法, 要执行的代码(任务)
3. 创建Runnable实现类对象 (任务对象)
4. 创建Thread类对象, 在构造方法中传入Runnable实现类对象 (将任务和线程绑定)
5. 通过Thread对象调用 start() 方法启动线程
Thread类: 表示线程. 实现了Runnable接口
// 构造方法
Thread Thread(Runnable target): 通过Runnable对象创建Thread对象
Thread Thread(Runnable target, String threadName): 通过Runnable对象创建对象并指定线程名
Java中多线程运行原理
随机性
抢占式, CPU高速随机切换执行(本质), 多个线程抢夺CPU的执行资源
内存
每个线程都有各自栈, 堆共用
解决多线程操作共享数据的安全问题的3种方式:
1. 同步代码块
2. 同步方法
3. 锁机制
同步代码块
: 使用 synchronized 关键字修饰的代码块, 并传入一个当作锁的对象
synchronized (锁对象) {
// 操作共享数据的代码
}
注意:
锁对象可以是任意类型的一个对象
锁对象必须是被多个线程共享的唯一对象
锁对象的作用: 只让一个线程在同步代码块中执行
锁对象:
也称为"同步锁", "对象锁", "对象监视器"
类型: 任意
必须是多个线程共享的唯一一个对象
同步的原理:
线程进入同步代码块前, 会争夺锁对象, 只有一个线程会抢到锁对象
进入同步代码块的线程, 会持有锁对象, 并执行同步代码块中的代码
此时同步代码块外的线程, 处于阻塞状态, 只能等待
当同步代码块内的线程执行完代码块, 会离开同步代码块, 并归还锁对象给同步代码块
等在同步代码块外的其他线程就可以继续争夺锁对象
静态同步方法的锁对象: 当前类的字节码对象Class对象
获取一个类的字节码对象的3种方式:
1. 对象.getClass()
2. 类名.class
3. Class.forName("类的全路径");
Lock锁
void lock(): 获取锁
void unlock(): 释放锁
线程安全和效率的特点:
线程安全, 效率低
线程不安全, 效率高
线程6个状态的名称
NEW 新建
RUNNABLE 可运行
BLOCKED 锁阻塞
WAITING 无限等待 wait()
TIMED_WAITING 计时等待 wait(long millis) sleep(long millis)
TERMINATED 终结
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/)
黑马程序员IT技术论坛 X3.2