1.集合的结构
2.集合的子实现类
3.异常
4.二分搜索法
5.线程
6.同步机制
1.集合的结构
2.集合的子实现类
1)LinkedList
特有功能:
public void addFirst(Object e):在列表开头插入元素;
public void addLast(Object e):将元素追加到列表的末尾;
public Object getFirst():获取列表的第一个元素;
public Object getLast():获取列表的最后一个元素 ;
public Object removeFirst(): 删除列表的第一个元素,并获取第一个元素;
public Object removeLast():删除列表的最后一个元素,并获取最后一个元素;
例:
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList<String> link = new LinkedList<>() ;
// public void addFirst(Object e):在列表开头插入元素
link.addFirst("hello") ;
link.addFirst("world") ;
link.addFirst("JavaEE") ;
//public Object getFirst():获取列表的第一个元素
System.out.println(link.getFirst());
System.out.println(link.getLast());
// public Object removeFirst(): 删除列表的第一个元素,并获取第一个元素
System.out.println(link.removeFirst());
System.out.println(link.removeLast());
}
}
2)HashSet
应用场景:
在一些需求中,如果没有明确要求元素重复,那就可以使用hashSet,保证元素唯一;
类型:String,Integer,Long,....常用类都已经重写了hashCode和equals方法.
例:
public class HashSetDemo {
public static void main(String[] args) {
HashSet<Student> hs1 = new HashSet<>() ;
Student s1 = new Student("宋江",35) ;
Student s2 = new Student("宋江",35) ;
Student s3 = new Student("武松",30) ;
Student s4 = new Student("宋江",30) ;
Student s5 = new Student("武松",30) ;
Student s6 = new Student("卢俊义",28) ;
Student s7 = new Student("卢俊义",28) ;
System.out.println("-------------------------------");
//添加集合中
hs1.add(s1) ;
hs1.add(s2) ;
hs1.add(s3) ;
hs1.add(s4) ;
hs1.add(s5) ;
hs1.add(s6) ;
hs1.add(s7) ;
//遍历
for(Student s : hs1){
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
(注意:自定义的 Student 类中,已重写 hashCode()和 equals,否则不能保证唯一)
3)TreeSet
a.public TreeSet():构造一个空的树,实现元素自然排序 (取决于存储的元素类型能否实现Comparable接口);
例:
public class TreeSetDemo {
public static void main(String[] args) {
//创建TreeSet,无参构造方法
TreeSet<Student> ts = new TreeSet<>() ;
Student s1 = new Student("gaoyuanyuan",42) ;
Student s2 = new Student("gaoyuanyuan",42) ;
Student s3 = new Student("jacky",40) ;
Student s4 = new Student("rose",40) ;
Student s5 = new Student("tomcat",35) ;
Student s6 = new Student("jeffry",35) ;
Student s7 = new Student("liushishi",54) ;
Student s8 = new Student("liudehua",60) ;
//因为当前集合存储的自定义对象,元素要实现自然排序必须所在的类实现Compareable接口
ts.add(s1) ;
ts.add(s2) ;
ts.add(s3) ;
ts.add(s4) ;
ts.add(s5) ;
ts.add(s6) ;
ts.add(s7) ;
ts.add(s8) ;
//遍历
for(Student s : ts){
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
public class Student implements Comparable<Student>{
private String name ;
private int age ;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {//重写 toString
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//排序的代码
@Override
public int compareTo(Student s) { //
//年龄int类型
//主要条件为年龄
int num = this.age - s.age ;
//次要条件:年龄相同,还要比较姓名的内容是否相同
int num2 = (num==0)?(this.name.compareTo(s.name)):num; //此处对字符串以字典顺序比较
return num2 ;
}
}
b.比较强排序:
public TreeSet(Comparator<? super E> comparator)
Comparator是一个接口类型
1)自定义一个类实现Comparator接口,重写compare方法
2)使用接口的匿名内部类(推荐)
例:
public class TreeSetDemo {
public static void main(String[] args) {
// public TreeSet(Comparator<? super E> comparator)
//MyComparator myComparator = new MyComparator() ; //
//接口的匿名内部类
TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
//主要条件:按照学生的年龄从小到大排序
int num = s1.getAge() - s2.getAge() ;
//如果年龄相同,比较姓名是否一样
int num2 = (num==0)? (s1.getName().compareTo(s2.getName())): num ;
return num2;
}
}) ;
//创建几个学生对象
Student s1 = new Student("gaoyuanyuan",42) ;
Student s2 = new Student("gaoyuanyuan",42) ;
Student s3 = new Student("jacky",40) ;
Student s4 = new Student("rose",40) ;
Student s5 = new Student("tomcat",35) ;
Student s6 = new Student("jeffry",35) ;
Student s7 = new Student("liushishi",54) ;
Student s8 = new Student("liudehua",60) ;
ts.add(s1) ;
ts.add(s2) ;
ts.add(s3) ;
ts.add(s4) ;
ts.add(s5) ;
ts.add(s6) ;
ts.add(s7) ;
ts.add(s8) ;
for (Student s:ts) {
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
4)Map
遍历功能:
方式1 Set<K> keySet() :获取当前Map集合中的所有的键的集合
V get(Object key):通过键获取值
方式2 Set<Map.Entry<K,V>> entrySet():获取所有键值对对象
通过键值对象 获取键 /获取值
V getValue()
例:
public class MapDemo {
public static void main(String[] args) {
//创建Map集合对象
Map<String,String> map = new HashMap<>() ;
//添加元素
map.put("令狐冲","东方不败") ;
map.put("杨过","小龙女") ;
map.put("陈玄风","梅超风") ;
map.put("郭靖","黄蓉") ;
// Set<K> keySet() :获取当前Map集合中的所有的键的集合
Set<String> keySet = map.keySet();
//增强for遍历
for(String key: keySet){
//获取所有的键的元素
// V get(Object key):通过键获取值
String value = map.get(key);
System.out.println(key+"="+value);
}
//方式2:
//Set<Map.Entry<K,V>> entrySet()
Set<Map.Entry<String, String>> entry = map.entrySet();
//增强for:遍历键值对对象获取到
for(Map.Entry<String, String> en: entry){
//获取键和值
//K getKey()
// V getValue()
String key = en.getKey();
String value = en.getValue();
System.out.println(key+"="+value);
}
}
}
5)List
静态功能:
public static <T extends Comparable<? super T>> void sort(List<T> list):按照自然升序排序(针对List集合排序)
public static <T> void sort(List<T> list,Comparator<? super T> c):按照比较器排序针对List集合
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T>:获取当前自然顺序中List的最大值
public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T>:最小值
public static void reverse(List<?> list):对List集合顺序反转
public static void shuffle(List<?> list):随机置换
例:
public class CollectionsDemo {
public static void main(String[] args) {
//创建List集合
List<Integer> list = new ArrayList<>() ;
//添加元素
list.add(10) ;
list.add(50) ;
list.add(15) ;
list.add(25) ;
list.add(5) ;
list.add(12) ;
System.out.println(list);
System.out.println("---------------------------------");
//public static <T extends Comparable<? super T>> void sort(List<T> list):
Collections.sort(list);
System.out.println(list);
System.out.println("----------------------------------");
//public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T>
Integer max = Collections.max(list);
System.out.println(max);
System.out.println("----------------------------------");
System.out.println(Collections.min(list));
System.out.println("-----------------------------------");
Collections.reverse(list);//反转
System.out.println(list);
System.out.println("------------------------------------");
// public static void shuffle(List<?> list):随机置换
Collections.shuffle(list);
System.out.println(list);
}
}
3.异常
编译时期异常和运行时期异常(RuntimeException):程序在运行过程中出现问题(代码书写不严谨)
异常的处理两种方式:
标准格式:try...catch...finally
变形格式
try{
//可能出现问题的代码
}catch(异常类名 变量名){
//处理异常
}
try{
//可能出现问题的代码
}catch(异常类名 变量名){
//处理异常1
}catch(异常类名 变量名){
//处理异常2
}
//多线程:jdk5以后:Lock:接口 (锁:可重复入的互斥锁)
try{
//可能出现问题的代码
}finally{
//释放资源(系统资源)
}
throws:抛出
例:
public class ExceptionDemo1 {
public static void main(String[] args) {
int a = 10 ;
int b = 0 ;
System.out.println(a/b);
System.out.println("over");
}catch(ArithmeticException e){ //捕获异常:可以使用大的Exception,但是捕获:具体异常具体捕获
System.out.println("除数不能为0");
}
}
}
*面试题:
throws和throw的区别?
a.共同点:都是抛出
b. 用法不同:
1)使用位置不同
throws:
a)将异常抛出在方法声明上;
b)在方法名的后面可以跟多个异常类名,中间逗号隔开;
throw:
a)在方法的语句体中某个逻辑语句中;
b)它后面只能跟异常对象,而不是类名;
2)调用者是否处理不同
throws:调用者必须进行显示处理(try...catch/throws),否则报错;
throw:调用者无须显示处理,一般情况都是在逻辑语句进行处理;
3)出现异常是否肯定性
throws:在方法上的,执行某个方法的代码中,可能有问题(表示出现异常的一种可能性);
throw:执行某段代码一定会执行这个异常(表示出现异常的一种肯定性);;
4)
throws:将具体的处理交给jvm,通过jvm吧异常信息打印控制台上,显示的底层源码而且会显示当前错误消息字符串;
throw:程序中某段代码有问题:只是打印异常类名(jvm处理).
4.二分搜索法
public class BinarySearch {
public static void main(String[] args) {
int[] arr = {11,22,33,44,55} ;
//调用二分搜索方法查询
int index = binarySearch(arr, 22);
System.out.println(index);
}
//返回值int
//方法参数:数组,查询的元素
public static int binarySearch(int[] arr,int target){
//防止空指针异常
if(arr!=null){
//定义数组的最小索引:
int min = 0 ;
//定义最大索引
int max = arr.length -1 ;
//使用循环while
while(min<=max){
//计算中位点索引
int mid = (min+max)/2 ;
//如果当前中位点对应的元素小于要要查找的元素
if(target < arr[mid]){
//左半区域:继续折半
max = mid -1 ;
}else if(target > arr[mid]){
//右边区域:继续折半
min = mid + 1 ;
}else{
//查询到了
return mid ;
}
}
}
//循环结束之后,还没有找,则返回-1
return -1 ;
}
}
5.线程
1) 创建线程的实现 方式1:
1)将一个类声明为Thread的子类
2) 这个子类应该重写Thread类的run方法
3)然后可以分配并启动子类的实例,用start()启动
例:
public class MyThread extends Thread {
//重写Thread类的方法
@Override
public void run() {
//run方法里面:一般情况耗时的操作
for(int x = 0 ; x < 200 ; x ++){
System.out.println(x);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread my1 = new MyThread() ;//第一个线程对象
MyThread my2 = new MyThread() ; //第二个线程对象
my1.start();//start():有jvm调用底层run方法,出现并发执行
my2.start();
}
}
2) 创建线程的实现 方式2:
1)自定义类实现Runnable接口;
2)重写Runnable接口的run方法;
3)在main用户线程中
可以分配类的实例(创建类的实例)
4)创建当前类对象,然后创建Thread类对象,将当前类对象作为参数来传递
当前类---->"资源共享类"
Thread(Runnable target, String name);
5)分别启动线程.
例:
public class MyRunnable implements Runnable {
@Override
public void run() {
for(int x = 0 ; x < 100 ; x ++){ System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
//可以分配类的实例(创建类的实例)
MyRunnable my = new MyRunnable() ; //资源类:被多线程共享//具体类new 具体类
//创建两个线程类对象
Thread t1 = new Thread(my,"卡卡罗特") ;
Thread t2 = new Thread(my,"贝吉塔") ;
//分别启动线程
t1.start();
t2.start();
}
}
3)安全问题
检验多线程安全问题的标准;
1)是否是多线程环境是不能更改,使用多线程实现;
2)是否存在共享数据是(资源类的数据: tickets)必须要有共享数据;
3)是否存在多条语句对共享数据的操作→是,可操作
解决----Java提供同步机制:同步代码块 将多条对共享数据包裹起来
synchronized(锁对象){
将多条对共享数据包裹起来
}
锁对象:必须要多个线程使用的同一个锁对象,而不是分别自己的锁对象!
例:
public class SellTicket implements Runnable {
//成员变量;100张票
public static int tickests = 100 ;
//创建一个锁对象:
public Object obj = new Object() ;
//t1,t2,t3
@Override
public void run() {
//模拟一直票
while(true){
//t1,t2,t3
//解决方案:
//将多条语句对共享数据的操作包裹起来
//synchronized (new Object()){ //锁对象 :三个线程分别使用自己的锁
//必须为是同一个锁对象
synchronized (obj){
//模拟网络延迟
//判断
if(tickests>0){//100>0
try {
Thread.sleep(100); //单位为毫秒数
} catch (InterruptedException e) {
e.printStackTrace();
}
//输出窗口信息
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickests--)+"张票");
}
}
}
}
}
public class SellTicketTest {
public static void main(String[] args) {
//创建资源类对象SellTicket
SellTicket st = new SellTicket() ;
//创建三个线程类对象
Thread t1 = new Thread(st,"窗口1") ;
Thread t2 = new Thread(st,"窗口2") ;
Thread t3 = new Thread(st,"窗口3") ;
//分别启动线程
t1.start();
t2.start();
t3.start();
}
}
4)同步方法
如果一个方法的方法体的第一句话就是同步代码块;
可以将synchronized关键字提取到方法声明上,跟在权限修饰符的后面;
权限修饰符 synchronized 返回值类型 方法名(形式列表){ //非静态的同步方法
业务逻辑...
}
例:
在方法内:synchronized (obj){}// 此类需要新建锁对象
public Object obj = new Object();
在方法声明上: synchronized(this){}// 非静态同步;
synchronized(类名.class){}//静态同步
5)死锁问题处理
a.死锁例:
创建锁对象:
public class MyMonitor {
//提供两把锁对象
public static final Object objA = new Object() ;
public static final Object objB = new Object() ;
}
public class DieLock implements Runnable {
private boolean flag ;//标记值
public DieLock(boolean flag){
this.flag = flag ;
}
@Override
public void run() {
//判断标记值
//t1 ---->DieLock(true)
//t2 ---->DieLock(false)
if(flag){
//t1
synchronized (MyMonitor.objA){
System.out.println("if ObjeA");//"if objA"
synchronized (MyMonitor.objB){
System.out.println("if objB");
}
}
}else
//t2
synchronized (MyMonitor.objB){
System.out.println("else ObjB");
synchronized (MyMonitor.objA){
System.out.println("else objA");
}
}
}
}
}
public class DieLockDemo {
public static void main(String[] args) {
//创建资源类对象
DieLock d1 = new DieLock(true) ;
DieLock d2 = new DieLock(false) ;
//创建Thread里对象
Thread t1 = new Thread(d1) ;
Thread t2 = new Thread(d2) ;
//分别启动
t1.start();
t2.start();
}
}
以上各自创建锁对象,导致死锁,只能执行一次代码就锁住了!
b. 解决死锁例: "卖包子"实例1
public class StuffBun {
//成员变量不私有化
String name ;//包子的类型(肉包子,菜包子)
String bunType ;//大包子/小包子
}
public class SetBun implements Runnable {
//声明这个包子类
private StuffBun stu ;
public SetBun(StuffBun stu){
this.stu = stu ;
}
//定义一个统计变量
int x = 0 ;
@Override
public void run() {
//产生包子
/* StuffBun stu = new StuffBun() ;
stu.name = "肉包子" ;
stu.bunType = "大类型";*/
//不断的产生数据
while(true){
synchronized (stu){
if(x % 2 == 0){//t1
stu.name = "肉包子" ;
stu.bunType = "大包子";
}else{
stu.name = "菜包子" ;
stu.bunType = "小包子" ;
}
}
x ++ ;
}
}
}
public class GetBun implements Runnable {
//声明包子类的变量stb
private StuffBun stb ;
public GetBun( StuffBun stb){
this.stb = stb ;
}
@Override
public void run() {
//模拟要使用数据
// StuffBun stb = new StuffBun() ;
//不断使用数据
while(true){
synchronized (stb){
System.out.println(stb.name+"---"+stb.bunType);
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
//创建一个包子对象
StuffBun sbu = new StuffBun() ; //同一个对象
//创建生产资源类对象
SetBun sb = new SetBun(sbu) ;
//消费者资源类对象
GetBun gb = new GetBun(sbu) ;
//创建线程了对象
Thread t1 = new Thread(sb) ;//生产者资源类所在的生产者线程
Thread t2 = new Thread(gb) ;//消费者资源类所在的消费者线程
t1.start();
t2.start();
}
}
核心:将包子通过生产资源类或者消费者资源类,通过构造方法传递,通用一个包子对象!
c.解决死锁例(且wait()/notify()实现同步): "卖包子"实例2
例:
public class StuffBun {
//成员变量不私有化
private String name ;//包子的类型(肉包子,菜包子)
private String bunType ;//大包子/小包子
//定义标记:表示是否存在包子数据
private boolean flag ; //默认false,没有数据
//提供给包子数据进行赋值的方法
public synchronized void set(String name,String bunType){ //锁对象是this:非静态的同步方法
//如果当前生产者没有语句,需要等待生成产生数据
if(this.flag){
//锁对象调用wait发那个发
try {
this.wait();//释放锁对象...
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//赋值
this.name = name ;
this.bunType = bunType ;
//如果现在有数据了
//改变信号
this.flag = true ;//有数据类
//通知(唤醒)消费者线程,赶紧使用数据
this.notify(); //唤醒对方线程
}
//提供方法:获取包子的数据
public synchronized void get(){ //非静态的同步方法:锁对象 this
//如果当前消费资源类中存在包子数据,先等待消费使用完毕数据
if(!this.flag){
//等待使用完毕数据
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(this.name+"---"+this.bunType);
//改变信号值
//如果包子消费完毕
this.flag = false ;
//唤醒对方线程(生产者资源类,别等了,产生数据)
this.notify();
}
}
public class SetBun implements Runnable {
//声明这个包子类
private StuffBun stu ;
public SetBun(StuffBun stu){
this.stu = stu ;
}
//定义一个统计变量
int x = 0 ;
@Override
public void run() {
//产生包子
/* StuffBun stu = new StuffBun() ;
stu.name = "肉包子" ;
stu.bunType = "大类型";*/
//不断的产生数据
while(true){
if(x % 2 == 0){//t1
//stu.name = "肉包子" ;
//stu.bunType = "大包子";
stu.set("肉包子","大包子");
}else{
//stu.name = "菜包子" ;
//stu.bunType = "小包子" ;
stu.set("菜包子","小包子");
}
x ++ ;
}
}
}
public class GetBun implements Runnable {
//声明包子类的变量stb
private StuffBun stu ;
public GetBun( StuffBun stu){
this.stu = stu ;
}
@Override
public void run() {
//模拟要使用数据
// StuffBun stb = new StuffBun() ;
//不断使用数据
while(true){
stu.get();//获取包子数据
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
//创建一个包子对象
StuffBun sbu = new StuffBun() ; //同一个对象
//创建生产资源类对象
SetBun sb = new SetBun(sbu) ;
//消费者资源类对象
GetBun gb = new GetBun(sbu) ;
//创建线程了对象
Thread t1 = new Thread(sb) ;//生产者资源类所在的生产者线程
Thread t2 = new Thread(gb) ;//消费者资源类所在的消费者线程
t1.start();
t2.start();
}
}
6)Lock/Unlock
提供比syncrhonized方法(/同步代码块)更具体的锁定操作,多个线程并发访问,抢占共享资源数据,通过lock实现多个线程对某个共享资源进行独占访问,不会有安全问题.
例:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SellTicket implements Runnable {
//定义100张票
private static int tickets = 100 ;
//创建一个锁对象
Lock lock = new ReentrantLock() ;
@Override
public void run() {
//模拟一只有票
while(true){
//通过锁对象--->获取锁
lock.lock();
//try...catch...finaly:捕获异常
//使用try..finally
try{
//判断
if(tickets>0){
//睡眠100毫秒
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
}else{
break ;
}
}finally {
//释放锁
lock.unlock();
}
}
}
}
public class LockDemo {
public static void main(String[] args) {
//创建共享资源类对象
SellTicket st = new SellTicket() ;
//创建三个线程类对象
Thread t1 = new Thread(st,"窗口1") ;
Thread t2 = new Thread(st,"窗口2") ;
Thread t3 = new Thread(st,"窗口3") ;
//启动线程
t1.start();
t2.start();
t3.start();
}
}
7)线程池
特点:
在内存中创建一个固定可重用的线程数,当前线程执行完毕终止了,不会被回收掉,再次回到线程池中,等待下一次利用.
通过 工厂类:
Exceutors
创建一个固定的可重用的线程数,返回值就线程池对象
public static ExecutorService = newFixedThreadPool(int nThreads)
例:
import java.util.TreeMap;
import java.util.concurrent.Callable;
public class MyCallable implements Callable {
@Override
public Object call() throws Exception {
for(int x = 0 ; x < 100 ; x++){
System.out.println(Thread.currentThread().getName()+":"+x);
}
return null;
}
}
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo {
public static void main(String[] args) {
//通过工厂类创建线程池对象
ExecutorService threadPool = Executors.newFixedThreadPool(3);
//提交异步方法
//MyRunnable:打印x的值0-99之间的数据,不需要返回结果
// threadPool.submit(new MyRunnable()) ;
// threadPool.submit(new MyRunnable()) ;
// <T> Future<T> submit(Callable<T> task)
//Callable:提交异步计算---需要重写Callable的call来计算结果;如果没有结果,直接在call无须返回
threadPool.submit(new MyCallable()) ;
threadPool.submit(new MyCallable()) ;
threadPool.submit(new MyCallable()) ;
//void shutdown()关闭线程池
threadPool.shutdown();
}
}
6.同步机制
为了解决线程安全问题,存在4种方法
1)synchronized(锁对象){多条语句共享数据操作}
2)ThreadLocal<T>:jdk8后提供本地线程类相同的线程,使用的实例一致,不同线程对象,他的ThreadLocal实例不同;
3)volatil 也是同步的一种:修饰成员变量,线程对象每次执行的时候,会重复读取被volatile的变量;
4)wait()+notify():实现同步,线程等待→唤醒对方线程.