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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

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

                                                                                                
                                                   Day03 List Set

一.数据结构

  数据结构: 就是数据的存储方式

        不同的数据结构代表了不同的存储方式

        不同的数据结构中, 会有不同的存入, 取出, 查找的方式和效率, 数据的存放方式也不同

(1) 栈结构

  特点:先进后出

适用场景:反转内容

(2) 队列结构

      特点:先进先出

适用场景:高并发场景

(3) 数组结构

特点:查询快,增删慢

适用场景:查询多,增删少的数据存储场景

(4) 链表结构

特点:查询慢,增删快

适用场景:查询少,增删多的场景,因为栈和队列增删频繁

(5) 红黑树结构

特点:元素存储过程中完成了大小排序

        查询比链表快,增删比数组快

适用场景:查询和增删都有,需要元素自动排序的场景

二   List集合

特点:

    (1) 元素存取有序

    (2) 元素可以重复

    (3) 有索引

List子体系的实现类都具有上述特点

成员方法:

void add(int index, E element): 将指定的元素, 添加到该集合中的指定位置上

E get(int index): 返回集合中指定位置的元素

E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素

E set(int index, E element): 用指定元素替换集合中指定位置的元素, 返回值的更新前的元素

三  ArrayList集合

数据结构:数组

特点:查询快 增删慢 线程不安全,效率高

四 LinkedList集合

数据结构:链表

特点:查询慢,增删快 线程不安全,效率高

特有成员方法

void addFirst(E e): 将指定元素插入此列表的开头

void addLast(E e): 将指定元素添加到此列表的结尾

E getFirst(): 返回此列表的第一个元素

E getLast(): 返回此列表的最后一个元素

E removeFirst(): 移除并返回此列表的第一个元素

E removeLast(): 移除并返回此列表的最后一个元素

E pop(): (其实就是removeFirst())从此列表所表示的栈中弹出一个元素

void push(E e): (其实就是addFirst())将元素添加到此列表所表示的栈中

五  Vector集合

数据结构:数组

特点:查询慢 增删快 线程安全,效率低

六 Set集合

特点:

      (1)元素不可重复

      (2) 没有索引

七 HashSet集合

         (1). 元素不可重复

        (2). 没有索引

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

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

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

     常用方法

boolean add(E e): 添加元素, 根据元素的 hashCode() 和 equals() 方法判断是否重复. 重复则不添加并返回false, 不重复则添加并返回true

(5) HashSet原理: 哈希值

public native int hashCode();    计算一个对象的哈希值

作用:方法内部的算法用于将对象计算为一个哈希值, 便于根据哈希值比较对象是否"相等"   哈希值主要是为了提高对象存储在 哈希表 中的效率

(6) HashSet原理: 哈希表结构

    哈希表:

              jdk1.8版本之前:哈希表=数组+链表

             jdk1.8版本之后:哈希表=数组+链表

哈希表=数组+红黑树

(7) HashSet原理: 存储元素不重复的原理

    调用add方法添加元素,会先调用hashCode方法获取哈希值,和当前集合中元素比较,如果哈希值不同,认为元素不重复,添加,如果相同,调用equals方法和哈希值相同的元素比较,相同不添加,不相同添加元素.

八 LinkedHashSet集合

数据结构:哈希表 + 链表

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

        2. 元素不可重复

        3. 没有索引

九  可变参数

   格式:用在方法的参数中

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

注意事项:

  (1) 传递多个参数,可以是0个或多个

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

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

十  Collections集合工具类

   静态方法

static <T> boolean addAll(Collection<?superT> c, T... elements):往集合中添加一些元素

        static void shuffle(List<?> list): 打乱集合顺序

        static <T> void sort(List<T> list): 将集合中元素按照默认规则排序

        static <T> void sort(List<T> list,Comparator<? super T> c):将集合中元素按照指定规则排序
                                                                                     Day04 【Map】

一.

1.1   Map集合

Map集合是双列集合,一一对应的关系,Map中的集合不能包含重复的键,值可以重复,每个键只能对应一个值.

1.2Map常用子类

HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。

LinkedHashMap<K,V>:HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。

1.3 Map接口中的常用方法

(1) public V put(K key, V value):  把指定的键与指定的值添加到Map集合中。

(2)  public V remove(Object key): 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。

(3)  public V get(Object key) 根据指定的键,在Map集合中获取对应的值。

(4)   boolean containsKey(Object key) 判断集合中是否包含指定的键。

(5)  public Set<K> keySet(): 获取Map集合中所有的键,存储到Set集合中.

(6) public Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)。

1.4 Map集合遍历键找值方式

   格式   keyset()

获取Map中所有的键,键是唯一的,返回一个Set集合存储所有的键,再通过get(K key)获取键对应的值.

1.5 Entry键值对对象

方法:  public Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)。

public K getKey():获取Entry对象中的键。

public V getValue():获取Entry对象中的值。

1.7HashMap 存储自定义类型键值

当给HashMap中存放自定义对象时,如果自定义对象作为key存在,这时要保证对象唯一,必须复写对象的hashCode和equals方法 (无序).

1.8 LinkedHashMap

HashMap的子类,特点:有序.

二. 补充知识点

1  of()方法

   of()方法只是Map,List,Set这三个接口的静态方法,其父类接口和子类实现并没有这类方法,返回的集合是不可变的.

2 Debug追踪

断点:

        breakpoint, 在debug模式下, 程序运行到断点会暂停住, 便于我们在程序运行过程中查看

Debug调试程序:

    可以让代码逐行执行,查看代码执行的过程,调试程序中出现的bug

使用方式:

    在行号的右边,鼠标左键单击,添加断点(每个方法的第一行,哪里有bug添加到哪里)

    右键,选择Debug执行程序

    程序就会停留在添加的第一个断点处

执行程序:

    f8:逐行执行程序

    f7:进入到方法中

    shift+f8:跳出方法

    f9:跳到下一个断点,如果没有下一个断点,那么就结束程序

    ctrl+f2:退出debug模式,停止程序

    Console:切换到控制台
                                                                                       Day  05 异常 多线程

一 ,异常

1.异常的概念:指的是程序在执行过程中, 出现的非正常的情况, 最终会导致JVM的非正常停止

2.异常的分类

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

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

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

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

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

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

        1. 编译时异常:

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

        2. 运行时异常:

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

(3) 异常关键字:   throw 制造异常

作用;在方法中抛出指定的异常对象

格式:

        throw new 异常类名("异常原因字符串");

注意事项:

    1. throw 必须写在方法的内部

        2. throw 后面new的异常对象, 必须是 "Exception" 或 "Excetion的子类" 的对象

        3. 一个方法内部 throw 了一个异常对象, 则该方法可以分为2种情况来处理该异常:

                如果 throw 的是"运行时异常"(RuntimeException及其子类)对象, 那么可以不处理

                        该异常最终会交给JVM处理, 结果就是: 打印异常信息到控制台, 并立刻结束程序

                如果 throw 的是"编译时异常"(Exception及其子类), 则必须处理:

                        处理方式1: throws 抛出

                        处理方式2: try...catch 捕获

二 Objects 工具类 方法:requireNonNull()非空判断

(1  )静态方法

static <T> T requireNonNull(T obj): 检查传入参数是不是null. 是null则抛异常; 非null则返回该对象

        static <T> T requireNonNull(T obj, String message): 检查传入参数是不是null. 是null则抛异常, 同时带有原因; 非null则返回该对象

(2)  throws声明抛出异常

异常处理的第一种方式:

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

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

格式:

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

注意事项:

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

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

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

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

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

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

                要么继续声明 throws 抛出

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

(3) 抓获异常

               

    try {

        // 可能产生异常的代码

    } catch (异常类名 变量名) {

        // 处理异常的代码

        // 一般会将异常信息存储到日志中

    } ........

注意事项:

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

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

(4) Throwable 中的3个异常处理方法

成员方法

      String getMessage(): 异常的信息. 没有原因返回null

        String toString(): 异常的类型和原因信息

        void printStackTrace(): 使用标准错误输出流打印异常信息

(5) finally 代码块

格式:

         try {

        // 可能发生异常的代码

    } catch(异常类型 异常变量名) {

        // 处理异常

    }

        ...

        catch(异常类型 异常变量名) {

        // 处理异常

    } finally {

        // 无论是否发生异常, 是否捕获, 最后都会执行的代码.

        // 通常在这里执行释放资源的操作

    }

注意事项:

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

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

(6) 异常注意事项:捕获多个异常的3中方式

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

    try {

            // 可能发生异常的代码

    } catch (异常类型 变量名) {

            // 处理异常

    }

   

    try {

            // 可能发生异常的代码

    } catch (异常类型 变量名) {

            // 处理异常

    }

2. 一个try多个catch

try {

        // 可能发生异常的代码

        // 可能发生异常的代码

        // 可能发生异常的代码

    }catch (异常类型1 变量名){  // 子类异常在上

        // 处理异常1

    }catch (异常类型2 变量名){  // 父类异常在下

        // 处理异常2

    }

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

             用Exception多态捕获

try {

                // 可能发生异常的代码

        // 可能发生异常的代码

        // 可能发生异常的代码

        // 可能发生异常的代码

        // 可能发生异常的代码

    }catch (Exception e){  // 父类异常多态接收

        // 处理异常

    }

4.运行时异常,可以不抛出throws也不try ....catch ,交给JVM处理

5 .finally 中有retuen语句

如果 finally 代码块中有 return 语句, 则永远返回 finally 中的 return 语句的值

应该避免在 finally 中写 return 语句

6  子父类继承重写方法时的异常要求

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

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

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

                b: 抛出父类异常的子类

                c: 不抛出异常

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

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

   

   

一般情况下:

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

7.自定义异常

   如果Java提供的异常类不足以满足我们的需求, 我们也可以自己定义异常类

        定义编译时异常: 继承 Exception

        定义运行时异常: 继承 RuntimeException

三, 多线程

1 计算机基本概念:

   (1)并发与并行

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

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

  (2) 进程

概述 : 一个应用程序在内存中的一次执行过程

每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程

        进程也是程序的一次执行过程,是系统运行程序的基本单位

        系统运行一个程序即是一个进程从创建、运行到消亡的过程

(3)线程

概述;  是进程内的一个独立执行单元 (一条代码执行路径)

        一个程序运行后至少有一个进程, 一个进程中可以包含多个线程

(4) 线程的调度

      方式  1. 分时调度: 所有线程轮流使用CPU, 平分占用CPU的时间

               2. 抢占式调度: 优先让优先级高的线程使用CPU; 如果优先级相同, 则随机选择一个线程执行

(5)主线程

主线程:

        我们以前编写的代码, 也在一条线程中执行, 该线程叫作"main线程", 也称为"主线程"


如果我们没有额外创建线程, 那么我们的程序就只有一个线程, 即主线程, 此时程序是"单线程"的

单线程的执行特点:

        同一个线程内的代码, 从上往下依次执行

三,创建多线程程序的第一种方法:继承Thread类

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

        1. 定义类, 继承 Thread 类

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

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


java.lang.Thread类: 表示线程. 实现了Runnable接口

    void start(): 启动线程, 即让线程开始执行run()方法中的代码

   

注意:

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

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

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


                                                  Day 06  线程 同步 线程间通信

一 线程

   (1) 多线程原理

         1  随机性

         2 多线程情况下, 每个线程都有各自的栈内存

            每个线程各自的方法调用, 进的是各自线程的栈

           "栈"是每个线程各自的, "堆"是所有线程共用的

  (2) Thread 常用方法

        构造方法:

     Thread(): 创建Thead对象

    Thread(String threadName): 创建Thead对象并指定线程名

    Thread(Runnable target): 通过Runnable对象创建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): 让所在线程睡眠指定的毫秒

二 创建多线程程序的方式2:实现Runnable接口

    创建线程的第2种方式:

        1. 定义类, 实现Runnable接口

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

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

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

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

三 Thread和Runnable的区别

   实现Runnable的好处:

        1. 避免单继承的局限性

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

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

四 匿名内部类方式创建线程

  格式    new 父接口/父类() {

                       重写方法

                      };


                继承Thread类

                实现Runnable接口

五  线程安全问题

问题发生场景:

        多个线程操作共享资源

问题发生原因:

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

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

如何解决:

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

六 线程的解决方式

1 同步代码块

    格式                 synchronized (锁对象) {

                // 操作共享数据的代码

    }

注意:  

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

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

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

2 同步方法

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


(1  )非静态同步方法的锁对象: this

   

        // 非静态同步方法

        public synchronized void method(){

                // 可能会产生线程安全问题的代码

        }

(2) 静态同步方法

         public static synchronized void method(){

                // 可能会产生线程安全问题的代码

        }

静态同步方法的锁对象: 当前类的字节码对象 Class对象

  获取一个类的字节码对象的3种方式:

       1. 对象名.getClass()  

       2. 类名.class

      3. Class.forName("类的全名");

字节码对象的特点:

        同一个类, 他的字节码对象只有"唯一的一个"

(3) Lock接口

       成员方法

                     void lock(): 获取锁

                    void unlock(): 释放锁

            

七 同步技术解决线程安全问题的原理

同步的原理:

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

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

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

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

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

八 线程间的通信

(1)   线程的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()方法结束(执行结束, 或内部出现异常), 则进入此状态

(2 )  Object类中关于线程的方法

       oid notify(): 随机唤醒在同一个锁对象上的某一个处于等待状态的线程

        void notifyAll(): 唤醒所有在同一个锁对象上处于等待状态的线程

        void wait(): 让当前线程处于"无限等待"状态

        void wait(long timeout): 让当前线程处于"计时等待"状态, 时间到或被唤醒后结束此状态

        void wait(long timeout, int nanos): 让当前线程处于计时等待状态, 时间到或被唤醒后结束此状态

注意事项:    以上方法只能通过锁对象调用




0 个回复

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