黑马程序员技术交流社区

标题: 学习经历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