# 7. 集合(存储引用类型) 主要有三种集合: - List: 有序集合,可以放重复的数据 - Set:无序集合,不能重复 - Map:无序集合,键值对 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CK4j5u8o-1612419431401)(C:\Users\LiTangMM\AppData\Roaming\Typora\typora-user-images\image-20191202110439361.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uaXFAq5S-1612419431402)(C:\Users\LiTangMM\AppData\Roaming\Typora\typora-user-images\image-20191202111325514.png)] ### Collection接口 ```java public class CollectionTest(){ boolean add(E element); void clear(); boolean isEmpty(); int size(); Object[] toArray(); Iterator iterator(); //返回集合类依赖的迭代器对象 boolean contains(Object o); //调用集合元素equals方法比较 boolean remove(Object o); //建议:使用迭代器删除集合元素 // 增强for循环 } ``` ### 泛型 一种未知的数据类型,当我们不知道用什么类型的时候,可以使用泛型 泛型也可以是个变量,用来接收数据类型 好处: - 避免了类型转换的麻烦,存储的是什么类型,去除的就是出什么类型 - 把运行期异常提升到了编译期 在创建对象时,会把数据类型作为参数传递,确定数据类型 ```java // 泛型类 pubilc class GenericClass { private E name; public E getName(){ return name; } public void setName(E name){ this.name = name; } } // 泛型方法 // 定义在修饰符和返回类型之间 public class GenericMethod{ public void method01(M m){ sout(m); } public static void method02(M m){ sout(m) } } // 泛型接口 //... //泛型通配符 ? //ArrayList:ArrayList>:ArrayList public static void printArray(ArrayList> list){ Interator> it = list.iterator(); while(it.hasNext()){ Object o = it.next(); sout(0); } } //泛型型上下限 //泛型没有继承 extends E>:代表使用的泛型只能是E类型的子类或自己 super E>:只能是E类型的父类或自己 mainclass{ main{ GenericClass gc = new GenericClass<>(); GenericClass gc = new GenericClass<>(); GenericMethod gcc = new GenericMethod(); gcc.method("nihao"); gcc.method(123); } } ``` ### List集合 #### List 接口 `java.util.List extends Coolection` 特点: - 有序的集合 - 有索引 - 允许存储重复的元素 特有方法(与索引有关)注意索引越界异常: ```java List Methods{ public void add(int index,E element); public E get(int index); public E remove(int index); public E set(int index,E element); } ``` #### ArrayList集合 底层是数组,线程不安全的 添加时会复制原数组再添加 #### LinkedList集合 底层是双向链表(有首尾指针),线程不安全的 ```java LinkedList methods{ public E getFist(); public E getLast(); public void addFirst(E e); public E removeFirst(); public void addLast(E e); public E removeLast(); public push(E e); public E pop(); } ``` #### Vector集合 可以实现可怎张的对象数组,与ArrayList类似,线程安全的 ### Set集合 #### Set接口 `java.util.Set extends Collection` 1. 不允许存储重复的元素(元素重写了hashCode equals方法) 2. 没有索引,无带索引的方法 add方法会调用对象的hashCode方法,计算哈希值,在集合中找有没有改哈希值的元素,没有则存储;有则调用equals方法,查找是否有相等的元素,没有则存储. #### HashSet集合 `java.util.HashSet implements Set` 哈希表结构,无序 jdk1.8版本之前 哈希表=数组+链表 jdk1.8版本之后 哈希表=数组+红黑树(链表长度超过8个元素自动转换,提高查询速度) #### LinkedHashSet集合 `java.util.LinkedHashSet implements HashSet` 底层是一个哈希表+链表. 多了一条链表,保证元素有序 #### 可变参数(数组) jdk1.5之后出现的新特性 使用前提: 当方法的参数列表已经确定,但是参数个数不确定,就可以使用可变参数. 注意事项: 1. 一个方法的参数列表,只能有一个可变参数. 2. 如果方法的参数有多个,可变参数必须写在参数列表的末尾 ```java public class Demo01VarArgs{ public static void main(String[] args){ add(1,2,3,4,5); //将会创建一个长度为5的数组 } public static int add(int...arr){ int res = 0; for(int var:arr){ res += var; } return res } } ``` ### Map集合 双列集合 #### 常用子集 HashMap: 存数据采用的哈希表结构元素的存取顺序不能保证一致. 由于要保证键的唯一 不重复,需要重写简单hashCode()方法 equals()方法 LinkedHashMap: HahsMap下的一个子类,采用哈希表接口+链表结构 #### 常用方法 `public put(K key,V value)` `public remove(Object key)` `public get(Object key)` `public Set keySet()` `public Set> entrySet()` ```java package demo.hashmap; import java.util.HashMap; public class learnTest { public static void main(String[] args) { String s = "aabbccddeeffaabbddddefda"; HashMap map = new HashMap<>(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if(map.containsKey(c)){ map.put(c,map.get(c).intValue()+1); }else{ map.put(c,1); } } for (Character key:map.keySet()) { System.out.println(key+":"+map.get(key)); } } } ``` # 8. 多线程 ### 并发与并行 并发:指多个事件在同一时间发生(单核) 并行:至多个事件在同一时刻发生(多核) ### 线程 创建多线程程序的第一种方式:创建Thread类的子类 ```java package demo.Thread; /* 创建多线程程序的第一种方式:创建Threa的子类 java.lang.Thread类:是描述线程的类,要继承Thread类 实现步骤: 1. 创建一个Thred类的子类 2. 在Thread类的子类中重写Thread的run方法,设置线程任务 3. 创建Thread类的子类对象 4. 调用start方法,开启新的线程去执行run方法 结果1是两个线程并发执行 多次启动一个线程是非法的.特别是当线程已经执行结束后,不能再重新启动 java程序属于抢占式调度,哪个优先级包 */ public class Demo01Thread { public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); for (int i = 0; i < 20; i++) { System.out.println("main:"+i); } } } package demo.Thread; import java.lang.Thread; public class MyThread extends Thread { @Override public void run(){ for (int i = 0; i < 20; i++) { System.out.println("thread:"+i); } } } ``` #### 获取线程的名称 - 使用Thread类中的getName方法 - 可以先获取当前正在执行的线程,使用线程中的方法getName()获取线程的名称 String getName() : 返回线程的名称 static Thread currentThread() : 返回对当前正在执行的线程对象的引用 - 默认名称: 主线程: main 其他: Thread-0,Thread-1,Thread-2 #### 设置线程名称 - 使用Thread类中的方法setName(名字) - void setName(String name) 改变线程名称 - 创建一个带参数的构造函数,参数传递线程的名字;调用父类的带参数构造方法 - Thread(String name) #### Thread类中常用方法 ```java public class Thread{ public Thread(); public Thread(String name); public Thread(Runnable target); public Thread(Runnable target,String name); public String getName(); public void start(); //外部调用 public void run(); // 需要重写 public static void sleep(long millis); //暂停指定毫秒数 public static Thread currentThread(); } ``` #### 创建线程方式二: 1. 定义Runnable接口的实现类,并重写该方法的run()方法 2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象 3. 调用Thread对象的start方法启动线程 ```java public class MyRunnable implements Runnable{ @Override public void run(){ for(int i = 0;i < 20;i++){ sout(i); } } } public class test{ public static void main(String[] args){ MyRunnable mr = new MyRunnable(); Thread th = new Thread(mr); th.start(); } } ``` #### Thread和Runnable的区别 如果一个类继承Thread,则不适合资源共享. 但是如果实现了Runable接口的话,则容易实现资源共享. 优势: > 1. 适合多个相同的程序代码线程共享同一个资源 > 2. 可以避免java中的单继承的局限性 > 3. 增加线程的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立 > 4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类 #### 匿名内部类实现线程的创建 作用:简化代码 ```java public class Demo{ public static void main(String[] args){ new Thread(){ @Override public void run(){ //动作 } }.start(); Runnable r = new Runnable(){ @Override public void run(){ //动作 } }; new Thread(r).start(); new Thread( new Runnable(){ @Override public void run(){ //动作 } } ).start(); } } ``` ### 线程安全 多个线程同时访问共享资源 解决线程安全的方法: 1. 使用同步代码块 ```java synchronized(锁对象){ 可能出现线程安全的代码; } ``` 注意: 1. 通过代码块中的锁对象,可以使用任意对象 2. 但是必须保证多个线程使用的锁对象是同一个 3. 作用:把同步代码块锁住,只让一个线程在同步代码块中执行 原理: 1. 使用了锁对象,也叫对象监视器 2. 线程抢夺cpu执行权,遇到synchronize代码块,如果检查到锁对象,则会获取锁对象,知道代码块代码执行完成. 2. 使用同步方法 把访问了共享数据的代码抽取出来,作为一个新的方法,使用synchronized修饰. 锁对象为this. ```java public synchronized ....{ //共享数据 } ``` 使用静态同步方法 锁对象不是this(静态方法优先于对象), 是本类的class属性. 3. 使用Lock `java.util.concurrent.locks.Lock`接口 Lock接口的方法: void lock() 获取锁 void unlock() 释放锁 `java.util.concurrent.locks.ReentrantLock implements Lock` 使用步骤: 1. 在成员位置创建一个ReentrantLock对象 2. 在可能出现线程安全的代码前调用lock 3. 在可能出现线程安全的代码后调用unlock ### 线程状态 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nOEQTM1E-1612419431404)(C:\Users\LiTangMM\AppData\Roaming\Typora\typora-user-images\image-20191212095514418.png)] Object.wait() Object.notify() Thread.sleep() #### 等待唤醒 ```java public class Test { public static void main(String[] args) { // 共同锁对象 Object object = new Object(); Producer producer = new Producer(object); Consumer consumer = new Consumer(object); consumer.start(); producer.start(); } } public class Consumer extends Thread{ private Object lockobj; @Override public void run() { while (true){ synchronized (lockobj){ System.out.println("顾客线程获取锁对象"); System.out.println("顾客:我需要买东西(进入等待状态)"); // 进入等待状态 try { lockobj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } // 被唤醒后执行 System.out.println("顾客被唤醒,执行wait后的代码"); System.out.println("顾客:这是我要的东西,谢谢!"); } } } Consumer(Object object){ super(); lockobj = object; } } public class Producer extends Thread{ private Object lockobj; @Override public void run() { while(true){ synchronized (lockobj){ System.out.println("老板获取锁对象,等待5秒"); // 等待5秒 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("老板:东西准备好了(唤醒顾客)"); lockobj.notify(); } // 保证顾客拿到锁 try { System.out.println("------------"); Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } Producer(Object object){ super(); lockobj = object; } } ``` ### 等待唤醒机制 #### 线程间通信 #### 等待唤醒机制 如果能获取锁,线程就从WAITING状态变成RUNNABLE状态 否则,从wait set出来,又进入entry set,线程进入阻塞状态. 调用wait和notify方法注意: 1. wait方法和notify方法必须要由同一个锁对象调用. 因为: 对应的锁对象可以通过notify唤醒使用同一个锁对象调用wait方法后的线程 2. wait方法与notify方法是属于Object类的方法. 因为: 锁对象可以是任意对象, 而任意对象的所属类都是继承了Object类的. 3. wait方法与notify方法必须要在同步代码块或者同步函数中使用,因为: 必须要通过锁对象调用这2个方法. ### 线程池 底层实现`LinedList` 创建线程池: `java.util.concurrent.Executors` `static ExecutorService newFixedThradPoll(int nThreads)` 返回一个接口对象,通过调用接口方法获取线程 `sumbit(Runnable task)`: 提交一个`Runnable`任务用于执行 `shutdown()` : 关闭/销毁线程池 使用步骤: 1. 使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池 2. 创建一个类,实现Runnable接口,重写run方法,设置线程任务 3. 调用ExecutorService中的submint,传递线程任务,开启线程 # 9. Lambda表达式 lambda表达式的标准格式 由三部分组成: ``` 1. 一些参数 2. 箭头 3. 一行代码 ``` 格式: (参数列表) -> (一些重写方法的代码) 解释说明: ():接口中抽象方法的参数列表 ->: 传递的意思 {}: 重写接口的抽象方法的方法体 lambda简写 # 10. File 文件和目录路径的抽象表示形式 ### File类的两个静态变量 ```java public class File{ static String pathSeparator; //路径分割符 win; linux: static String separator; //文件名称分隔符 win\ linux/ } ``` #### 绝对路径 相对路径 ### File类的构造方法 ```java public class File{ public File(String pathstring); // 只封装File对象,不考虑路径的真假情况 public File(String parent, String child); // parent:父路径 child:子路径 public File(File parent, String child); // parent为File类,可使用File的方法对路径进行一些操作 } ``` ### File类常用方法--获取 ```java public class File{ // 文件 文件夹 public String getAbsolutePath(); // 绝对路径 绝对路径 public String getPath(); // 构造路径 构造路径 public String getName(); // 文件名称 目录名称 public long lenght(); // 文件大小(B) 目录大小(0B) } ``` ### File类常用方法--判断 ```java public class File{ public boolean exits(); public boolean isDirectory(); public boolean isFile(); } ``` ### File类常用方法--创建删除 ```java public class File{ public boolean createNeaFile(); public boolean delete(); public boolean mkdir(); public boolean mkdirs(); } ``` ### 目录遍历 ```java public class File{ public String[] list(); public File[] listFiles(): } ``` ### 文件过滤器 --- 转载至CSDN博主:[[李唐敏民](https://blog.csdn.net/qq_39041210 "李唐敏民")] --- 最后修改:2021 年 02 月 18 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏