A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© lst168 初级黑马   /  2018-11-20 15:43  /  892 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

day03-day06笔记整理

1、栈的特点:先进后出 (FILO, First In Last Out);入口和出口在同一侧

2、队列的特点:先进先出 (FIFO, First In First Out);入口和出口在两端

3、数组的特点
        查询快:通过 (第一个元素地址值 + 索引) 可以快速计算出该索引元素的地址值

        增删慢:增加一个元素, 要创建长度+1的新数组, 然后将原数组元素复制到新数组, 然后存入新元素; 删除类似

4、链表的特点:

        查询慢: 要找到其中某个节点, 只能从第一个节点一个一个向后寻找

        增删快: 只需要修改保存的下一个节点的地址值, 就可以快速完成增删

5、红黑树的特点:

元素存储过程中就完成了大小排序

查询比链表快, 增删比数组快 (数组和链表的折中)

6、List集合的特点

        1.、元素存取有序 (存入和取出元素的顺序一致) 321->321  排序: 从小到大

        2.、元素可以重复  1 1 1 1

        3.、有索引

4、常用成员方法:void add(int index, E element)、E get(int index)、E remove(int index)、E set(int index, E element)

7、ArrayList的特点:

        查询快

        增删慢

        线程不安全, 效率高

8、LinkedList的特点:

        1、查询慢

        2、增删快

        3、线程不安全, 效率高

4、常用成员方法:void addFirst(E e)、void addLast(E e)、E getFirst()、E getLast()、E removeFirst()、E removeLast()、E pop()、void push(E e)

9、Vector的特点:

        1、查询慢

        2、增删快

        3、(同步)线程安全, 效率低

9、HashSet特点:

        1. 元素不可重复

        2. 没有索引

        3. 元素存取无序 (存入和取出顺序有可能不一致)

        4. 底层采用 哈希表 结构. (查询快)

                哈希表 = 数组 + 链表或红黑树

10、哈希表:

        JDK 8以前   : 哈希表 = 数组 + 链表

        JDK 8及之后 : 哈希表 = 数组 + 链表或红黑树

        数组中存储的每个元素, 是哈希值相同的一组节点的链表或红黑树

11、HashSet集合保证添加元素不重复的原理:

        调用 add(E e) 添加元素时, 先调用 hashCode() 获取哈希值, 和当前HashSet集合中的元素比较

                如果哈希值不同, 则认为元素不重复, 添加, 并返回true

                如果哈希值相同, 则可能是哈希冲突, 所以继续调用元素的 equals() 方法和所有哈希值相同的元素比较

                        如果 equals() 比较所有元素都没有相同的, 则认为元素不重复, 添加, 并返回true

                        如果 equals() 比较出有相同的元素, 则认为元素重复, 不添加, 并返回false

12、LinkedHashSet特点:

        1. 元素存取有序 (存入和取出顺序一致)

        2. 元素不可重复

        3. 没有索引

       

LinkedHashSet底层数据结构:

        哈希表 + 链表   (也就是: 数组 + 链表或红黑树 + 链表)

        其中, 哈希表用于存储数据, 额外的链表用于记录元素添加时的先后顺序, 以便在获取元素时保持顺序一致

13、可变参数:

格式: 用在方法的参数中

            修饰符 返回值类型 方法名(int... 变量名) {

                    // 可以直接将 变量名 当作 数组名 使用

            }

            方法名();

注意事项:

        1. 可变参数可以传递的参数个数, 可以是 0个, 1个, 多个

        2. 一个方法的参数列表中, 只能有一个可变参数

        3. 如果方法的参数有多个, 可变参数必须写在参数列表的最后

14、Map集合特点:

        1. 是双列集合, 一个元素包含两个值 (键key, 值value) 键值对

        2. key和value的类型可以相同, 也可以不同

        3. key不允许重复, value可以重复

        4. key和value是一一对应的, 一个键只能对应一个值

15、Map中常用的方法:

V put(K key, V value)、V remove(Object key)、V get(Object key)、boolean containsKey(Object key)、Set<K> keySet()、Set<Map.Entry<K,V>> entrySet()

16、keySet()遍历步骤:

        1. Map对象调用 keySet() 方法, 获取包含所有key的Set集合

        2. 遍历Set集合, 获取每个key

        3. 通过Map对象调用 get(Object key) 方法根据key获取到value

17、entrySet()方法遍历Map步骤:

        1. Map对象调用 entrySet() 获取包含所有Entry对象的Set集合

        2. 遍历Set集合, 获取每个Entry对象

        3. 调用Entry对象的 getKey() 和 getValue() 方法获取键和值

18、Map双列集合总结:

Map接口: 双列集合的根接口, 规定了共性的方法

        |_ HashMap类: 哈希表=数组+链表+红黑树. key无序不可重复, 可存null键null值, 线程不安全效率高

        |        |_ LinkedHashMap类: 哈希表+链表. 哈希表实现key不可重复, 链表实现key存取有序

        |

        |_ Hashtable类: 哈希表. Hash特性针对key, key无序不可重复, 不可存null键null值, 线程安全效率低

        |_ TreeMap类: 红黑树结构(存入时就排序). Tree特性针对key, 可以按照key排序, 要求key具备比较性

        |_ 遍历

                |_ keySet(): 获取所有key组成的Set集合, 遍历Set集合拿到key, 通过Map的get(key)得到value

                |        |_ 对于Set<Key>的遍历

                |                |_ 增强for

                |                |_ 迭代器

                |_ entrySet(): 获取所有的key和value组成的Entry对象的Set集合, 遍历出entry对象, 通过entry对象的getKey()获取对应的key, 通过Entry对象的getValue方法获取对应的value

                        |_ 对Set<Entry>的遍历

                                |_ toArray()

                                |_ 增强for

                                |_ 迭代器

19、异常与错误:

错误(Error): 不能捕获处理的严重问题. 绝症

        必须将程序停下来, 修改代码才能解决

        错误的类名都是 "XxxError" 方式

异常(Exception): 可以捕获处理的问题. 发烧感冒吃个药就好了

        程序执行起来后, 如果有合适的处理方式, 即使发生异常, 程序也能处理该异常并继续运行

        异常的类名都是 "XxxException" 方式

        1. 编译时异常:

                编译时期就会发生的异常, 必须在编译时期处理  Unhandled exception XxxException

        2. 运行时异常:

                编译时正常, 运行时才会发生的异常

20、异常产生的过程:

        1. 当执行的代码发生异常操作时, JVM会创建一个对应的异常类对象, 包含异常的内容, 原因, 位置

                如 new ArrayIndexOutOfBoundsException(), new NullPointerException()

        2. 如果执行代码的方法没有对异常进行 try...catch 处理, 则会向该方法调用处的方法抛(向上层抛). 如果所有方法(包括main()方法)都没有 try...catch 处理异常, 则该异常会被JVM按照默认的处理方式处理

        3. JVM对于异常的默认处理方式是:

                1. 将异常信息(内容, 原因, 位置)打印到控制台

                2. 终止当前的程序

21、throw制造异常:

异常产生的过程:

        1. 当执行的代码发生异常操作时, JVM会创建一个对应的异常类对象, 包含异常的内容, 原因, 位置

                如 new ArrayIndexOutOfBoundsException(), new NullPointerException()

        2. 如果执行代码的方法没有对异常进行 try...catch 处理, 则会向该方法调用处的方法抛(向上层抛). 如果所有方法(包括main()方法)都没有 try...catch 处理异常, 则该异常会被JVM按照默认的处理方式处理

        3. JVM对于异常的默认处理方式是:

                1. 将异常信息(内容, 原因, 位置)打印到控制台

                2. 终止当前的程序

22、throws声明抛出异常:

异常处理的第一种方式:

        throws, 声明抛出 (方法自己不处理, 交给方法调用者处理, "甩锅给别人")

        作用: 告诉方法的调用者, 调用此方法有可能发生异常, 需要在编写代码时处理



格式:

        修饰符 返回值类型 方法名() throws 异常类名1, 异常类名2, ... {

           

        }

注意:

        1. throws 必须写在方法声明上

        2. throws 后面的异常类名, 一般是 Exception 或 Exception的子类

                (RuntimeException及其子类也行, 但是没有什么意义)

        3. 方法内部如果抛出了多个异常对象, throws 后也必须声明多个异常

                如果抛出的异常对象有子父类关系, 那么直接声明父类异常即可

        4. 调用了一个带有 throws 声明抛出异常的方法, 就必须处理该异常:

                要么继续声明 throws 抛出

                要么 try...catch 捕获处理异常

23、try {} catch{} 捕获异常:

1. try 中可能会抛出多种异常, 就可以写多个 catch 分别处理每种异常

2. 如果 try 中产生了异常, 就会从产生异常的那一行代码直接跳转到对应的 catch 中执行处理代码, 然后继续执行 try...catch 之后的其他代码; 如果 try 中没有产生异常, 那就不会执行 catch , 执行完 try 中的代码后, 继续执行 try...catch 之后的其他代码

24、finally代码块

1. finally 必须和 try...catch 一起使用

2. finally 一般用于释放资源 (IO流时用到)

25、捕获多个异常:

        1. 多个异常分别 try...catch 处理

        2. 一个 try 多个 catch

        如果异常存在继承关系, 子类异常在上, 父类异常在下

        3. 多个异常, 一次捕获一次处理

                用Exception多态捕获

26、子父类继承关系下, 子类重写父类带有 throws 的方法:

        1. 如果父类抛出多个异常, 子类重写父类方法时可以有3种方式:

                a: 抛出和父类相同的异常

                b: 抛出父类异常的子类

                c: 不抛出异常

        2. 父类方法没有抛出异常, 子类重写父类该方法时也不可抛出异常

                此时子类产生该异常, 只能捕获处理, 不能声明抛出

       一般情况下:

    父类方法声明的异常是什么样的, 子类重写的方法声明异常就什么样, 保持一致即可       

27、并发与并行:

并发: (交替执行) 指两个或多个事件在"同一时间段内"发生

并行: (同时执行) 指两个或多个事件在"同一时刻"发生 (同时发生)

28、线程:

线程: 是进程内的一个独立执行单元 (一条代码执行路径),一个程序运行后至少有一个进程, 一个进程中可以包含多个线程

多线程的好处:效率高

                        多个线程之间互不影响

29、实现多线程的第一种方式:

        1. 定义类, 继承 Thread 类

        2. 重写 run() 方法, run方法内部是线程要执行的任务

        3. 创建Thread子类的对象, 调用 start() 方法启动线程

   注意:

        必须调用 start() 方法来开启线程, 不能直接调用 run() 方法, 调用 run() 会变成单线程

        同一个线程对象, 不能多次调用 start() 方法

        Java是抢占式调度, 不同线程的代码, 执行顺序是随机的

30、实现线程的一种方式:

继承Thread类

定义类继承Thread

重写run()方法, 要执行的任务

创建子类的对象, 调用start()方法启动线程

31、Thread常用方法:

Thread()、Thread(String threadName)、Thread(Runnable target)、Thread(Runnable target, String threadName)、 void run()、void start()、String getName()、void setName(String name)、static Thread currentThread()、static void sleep(long millis)

32、创建多线程的第二种方式:

1. 定义类, 实现Runnable接口

2. 重写 run() 方法, 要执行的代码(任务)

3. 创建Runnable实现类对象 (任务对象)

4. 创建Thread类对象, 在构造方法中传入Runnable实现类对象 (将任务和线程绑定)

5. 通过Thread对象调用 start() 方法启动线程

33、实现Runnable的好处:

1. 避免单继承的局限性

2. 增强了程序的扩展性, 降低了程序的耦合性(解耦)

                线程是Thread, 任务是Runnable实现类对象. 相当于将线程和任务分离

               

耦合性: 相互之间的关系的紧密程度

耦合性高: 相互之间的关系非常紧密

耦合性低: 相互之间的关系不太紧密

我们追求 "低耦合"

34、线程安全问题:

问题发生场景:

        多个线程操作共享资源

问题发生原因:

        JVM是抢占式调度, CPU在每个线程之间切换是随机的, 代码执行到什么位置是不确定的

        在操作共享资源时, 由于一个线程还没有执行完, 另一个线程就来操作, 就会出现问题

如何解决:

        在操作共享资源时, 让线程一个一个来执行, 不要并发操作共享变量, 就可以解决问题

35、解决多线程操作共享数据的安全问题的3种方式:

        1. 同步代码块

        2. 同步方法

        3. Lock锁机制

注意:

        锁对象可以是"任意类型的一个对象"

        锁对象必须是"被多个线程共享的唯一的"对象

        锁对象的作用: 只让一个线程在同步代码块中执行

36、同步代码解决线程安全问题:

同步的原理:

    线程进入同步代码块前, 会"争夺锁对象", "只有一个线程"会抢到锁对象

    进入同步代码块的线程, 会"持有锁对象", 并执行同步代码块中的代码

    此时同步代码块外的线程, 处于"阻塞"状态, 只能等待

    当同步代码块内的线程执行完代码块, 会离开同步代码块, 并"归还锁对象"给同步代码块

    等在同步代码块外的其他线程就可以继续争夺锁对象

37、同步方法:

使用 synchronized 关键字修饰的方法, 具有默认的锁对象

38、Lock锁解决线程安全问题:

实现方式:

public class RunnableImpl implements Runnable {

    // 成员变量创建锁对象, 该锁对象也要所有线程共享唯一一个

    Lock lock = new ReentrantLock();  // 成员变量



    @Override

    public void run() {

        // 加锁

        lock.lock();

        try {

            // 操作共享变量的代码...

        } finally {

            // 在finally中保证释放锁

            lock.unlock();  

        }

    }

}

39、线程生命周期的6中状态:

1. "NEW 新建"

       线程被创建, 但没有调用 start() 启动

    2. "RUNNABLE 可运行"

       调用 start()方法后已启动, 但可能正在执行 run() 方法的代码, 也可能正在等待CPU的调度

    3. "BLOCKED (锁)阻塞"

       线程试图获取锁, 但此时锁被其他线程持有

    4. "WAITING 无限等待"

       通过锁对象调用无参的 wait() 进入此状态.

       等待其他线程通过锁对象执行 notify() 或 notifyAll() 才能结束这个状态

    5. "TIMED_WAITING 计时等待"

       如通过锁对象调用有参的 wait(long millis) 或 sleep(long millis), 则进入此状态.

       直到时间结束之前被其他线程通过锁对象执行 notify()或 notifyAll()唤醒, 或时间结束自动唤醒

    6. "TERMINATED 终止"

       run()方法结束(执行结束, 或内部出现异常), 则进入此状态

40、wait() 和 sleep() 的区别:

        1. wait会释放锁, 恢复时需要重新获取锁; sleep不会释放锁

        2. wait可以被notify/notifyAll唤醒; sleep不会

        3. wait要用锁对象调用; sleep要用Thread类名调用

       











0 个回复

您需要登录后才可以回帖 登录 | 加入黑马