集合按照其存储结构可以分为两大类,分别是单列集合java.util.Collection
和双列集合java.util.Map
,今天我们主要学习Collection
单列集合,在day04时讲解Map
双列集合。
Collection:单列集合类的根接口,用于存储一系列符合某种**规则(抽象方法)**的元素,
它有两个重要的子接口,分别是java.util.List
和java.util.Set
。
List
的特点是元素有序、元素可重复,有索引。相反的是,Set
的特点是元素无序,而且不可重复,无索引。
List
接口的主要实现类有java.util.ArrayList
和java.util.LinkedList
,Set
接口的主要实现类有java.util.HashSet
和java.util.TreeSet
。
从上面的描述可以看出JDK中提供了丰富的集合类库,为了便于初学者进行系统地学习,接下来通过一张图来描述整个集合类的继承体系。
其中,橙色框里填写的都是接口类型,而蓝色框里填写的都是具体的实现类。这几天将针对图中所列举的集合类进行逐一地讲解。
集合本身是一个工具,它存放在java.util包中。在Collection
接口定义着单列集合框架中最最共性的内容。
Collection是所有单列集合的父接口,因此在Collection中定义了单列集合(List和Set)通用的一些方法,这些方法可用于操作所有的单列集合。方法如下:
public boolean add(E e)
: 把给定的对象添加到当前集合中 。//增public boolean remove(E e)
: 把给定的对象在当前集合中删除。//删一个public void clear()
:清空集合中所有的元素。//删除所有public Object[] toArray()
: 把集合中的元素,存储到数组中。//改,变成数组,转换成数组public boolean contains(E e)
: 判断当前集合中是否包含给定的对象。//查,是否包含元素,针对的是一个public boolean isEmpty()
: 判断当前集合是否为空。//查,是否没有元素,针对的是所有情况public int size()
: 返回集合中元素的个数。//查,元素的个数,针对的也是所有情况迭代:即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续再判断,如果还有就再取出来。直到把集合中的所有元素全部取出。这种取出方式,专业术语称为迭代。
Iterator接口的常用方法如下:
public E next()
:返回迭代的下一个元素(迭代器原理内置了一个指针,一开始在集合的外面,每调用一次next方法,指针向下移动一次,得到对应的元素)。public boolean hasNext()
:如果仍有元素可以迭代,则返回 true。public class IteratorDemo {
public static void main(String[] args) {
// 使用多态方式 创建对象
Collection<String> coll = new ArrayList<String>();
// 添加元素到集合
coll.add("串串星人");
coll.add("吐槽星人");
coll.add("汪星人");
//能够使用迭代器对集合进行取元素
//使用迭代器 遍历 每个集合对象都有自己的迭代器
Iterator<String> it = coll.iterator();//调用方法.var生成迭代器
// 泛型指的是 迭代出 元素的数据类型
while(it.hasNext()){
//判断是否有迭代元素
String s = it.next();//获取迭代出的元素
System.out.println(s);
}
}
}
异常 :指的是程序在执行( 编译和运行)过程中,出现的非正常的情况,最终会导致JVM的非正常停止(中断)。
声明异常throws,throw产生一个真实具体的问题,throws问题的处理方式,不负责任处理,交给jvm,中断,问题类型,原因,位置
try-catch的方式就是捕获异常。
捕获异常语法如下:
try{
//尝试,试验,检测,需要时间,越少越好
编写可能会出现异常的代码
}catch(异常类型 e){
//e=对象;//如果上面出现问题,立马catch块抓住它,进行捕获
处理异常的代码
//记录日志/打印异常信息/继续抛出异常//待会说
}
**try:**该代码块中编写可能产生异常的代码。
**catch:**用来进行某种异常的捕获,实现对捕获到的异常进行处理。
**
在idea中添加try/catch的快捷键 ctrl+alt+t 选中想被try/catch包围的语句,同时按下ctrl+alt+t,
**
注意:finally不能单独使用。
进程:进行中的程序
线程:进程中的执行路径(执行路径,能做事情的地方,通道,路径)
并行:指两个或多个事件在同一时刻发生(同时发生)。(说白了,并行就是多个事情同时进行,需要多核(多个)CPU,一个CPU在执行一个事情同时,另外一个cpu在同时执行另外的事情!!!)
并发:指两个或多个事件在同一个时间段内发生。(说白了,并发,发,发作是需要时间的,不是同时进行,cpu一行执行那个事情,一会执行另外的事情,由于CPU切换的非常快,你看起来以为是同时进行,但其实并不是!!!)
进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个线程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
线程:进程内部的一个独立执行单元(路径);一个进程可以同时并发的运行多个线程,可以理解为一个进程便相当于一个单 CPU 操作系统,而线程便是这个系统中运行的多个任务。
同步就可以让cpu在某段时间内只让一个线程进来做事情,其他线程不能进来,等你做完了才能一个一个进来(理解为排队一个个来不会有问题),同步技术包括,同步代码块,同步方法,Lock锁//记忆
当我们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题。
要解决上述多线程并发访问一个资源的安全性问题:也就是解决重复票与不存在票问题,Java中提供了同步机制(synchronized)来解决。
根据案例简述:
窗口1线程进入操作的时候,窗口2和窗口3线程只能在外等着,窗口1操作结束,窗口1和窗口2和窗口3才有机会进入代码去执行。也就是说在某个线程修改共享资源的时候,其他线程不能修改该资源,等待修改完毕同步之后,才能去抢夺CPU资源,完成对应的操作,保证了数据的同步性,解决了线程不安全的现象。
为了保证每个线程都能正常执行原子操作,Java引入了线程同步机制。
那么怎么去使用呢?有三种方式完成同步操作:
synchronized
关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。格式:
synchronized(同步锁){
//锁对象可以是任意的,但是如果这个同步代码块被多次调用,要求锁对象是唯一,才能里面代码锁起来,同步的效果,只让一个线程进来搞事情,相当于你锁上门搞事情,别人不能进来
需要同步操作的代码,同步一个个线程,只让一个线程进来搞事情,其他线程不能进来,在外面等着,等你做完事情,释放锁才能一个个进来
}//做完事情,释放锁,要求是同一把锁
同步锁:
对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.
锁对象 可以是任意类型。
多个线程对象 要使用同一把锁。
注意:在任何时候,最多**允许一个线程拥有同步锁,**谁拿到锁就进入代码块,其他的线程只能在外等着(BLOCKED进入阻塞状态)。
使用同步代码块解决代码:
public class Ticket implements Runnable{
private int ticket = 100;
static Object lock = new Object();//共享的,大家都用同一个,同一把锁
/*
* 执行卖票操作
*/
@Override
public void run() {
//每个窗口卖票的操作
//窗口 永远开启
while(true){
synchronized (lock) {
//字节码对象,this,"锁"锁对象唯一,同一把锁,里面锁起来,只让一个线程进来搞事情
if(ticket>0){
//有票 可以卖
//出票操作
//使用sleep模拟一下出票时间
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//获取当前线程对象的名字
String name = Thread.currentThread().getName();
System.out.println(name+"正在卖:"+ticket--);
}
}
}
}
}
当使用了同步代码块后,上述的线程的安全问题,解决了。
格式:
public synchronized void method(){
可能会产生线程安全问题的代码
}
同步锁是谁?
对于非static方法,同步锁就是this。
对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。
使用同步方法代码如下:
public class Ticket implements Runnable{
private int ticket = 100;
/*
* 执行卖票操作
*/
@Override
public void run() {
//每个窗口卖票的操作
//窗口 永远开启
while(true){
sellTicket();
}
}
/*
* 锁对象 是 谁调用这个方法 就是谁
* 隐含 锁对象 就是 this
*
*/
public synchronized void sellTicket(){
//this锁对象
if(ticket>0){
//有票 可以卖
//出票操作
//使用sleep模拟一下出票时间
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//获取当前线程对象的名字
String name = Thread.currentThread().getName();
System.out.println(name+"正在卖:"+ticket--);
}
}
}
//非静态同步方法,锁对象是什么呢?是this
//静态同步方法,锁对象是这个方法所在的类的字节码对象,类名.class
jdk1.5开始支持下面内容
java.util.concurrent.locks.Lock
机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。
Lock锁也称同步锁,加锁与释放锁方法化了,如下:
public void lock()
:加同步锁。public void unlock()
:释放同步锁。使用如下:
public class Ticket implements Runnable{
private int ticket = 100;
Lock lock = new ReentrantLock();//多态写法,父接口引用指向接口的实现类对象
/*
* 执行卖票操作
*/
@Override
public void run() {
//每个窗口卖票的操作
//窗口 永远开启
while(true){
//Runnable共享资源,同一个锁对象
lock.lock();//上锁,相当于关门,互斥锁,一个线程进来
if(ticket>0){
//有票 可以卖
//出票操作
//使用sleep模拟一下出票时间
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//获取当前线程对象的名字
String name = Thread.currentThread().getName();
System.out.println(name+"正在卖:"+ticket--);
}
lock.unlock();//释放锁,相当于开门
}
}
}