1、LinkedList集合的特点
LinkedList集合特点: 线程不安全的类,执行效率高 链接列表结构,查询慢,增删快
2、插入元素
public void addFirst(Object e):在列表开头插入元素
LinkedList<String> link = new LinkedList<>() ; link.addFirst("hello") ; link.addFirst("world") ; link.addFirst("JavaEE") ; link.addFirst("Android") ;
3、末尾加入元素
public void addLast(Object e):将元素追加到列表的末尾
link.addLast("Php") ;
4、获取列表的第一个元素
public Object getFirst():获取列表的第一个元素
System.out.println(link.getFirst());
5、获取列表的最后一个元素
public Object getLast():获取列表的最后一个元素
System.out.println(link.getLast());
6、删除列表的第一个元素
public Object removeFirst(): 删除列表的第一个元素,并获取第一个元素
// public Object removeFirst(): 删除列表的第一个元素,并获取第一个元素 System.out.println(link.removeFirst());
7、删除列表的最后一个元素
public Object removeLast():删除列表的最后一个元素,并获取最后一个元素
System.out.println(link.removeLast());
Set集合: 无序(存储和取出不一致), 能够保证元素唯---------------------去重
//创建HashSet集合对象 Set<String> set = new HashSet<>() ; //添加字符串元素 set.add("hello") ; set.add("hello") ; set.add("world") ; set.add("JavaEE") ; set.add("JavaEE") ; set.add("world") ; set.add("android") ; set.add("php") ; set.add("php") ; set.add(null) ; System.out.println(set); //元素唯一------------------------[null, world, JavaEE, android, php, hello]
HashSet: 底层数据结构是一个哈希表(桶结构) 线程不安全的类---->不同步---->执行效率高
String类型:String类型本身已经重写了hashCode()和equals,如果hashCode和equals()都相同, 那么认为同一个元素,存储以前的值
Student s1 = new Student("高圆圆"42) ; * Student s2 = new Student("高圆圆"42) ; * * HashSet集合依赖于add方法---->HashMap的put方法 * * 首先要比较元素的哈希码值相同----->hash()就相同 * 还要比较成员信息是否相同,对应存储自定的类必须要重写Object的equals方法 * * 应用场景: * 在一些需求中,如果没有明确要求元素重复,那就可以使用hashSet,保证元素唯一! * 类型:String,Integer,Long,....常用类都已经重写了hashCode和equals方法 */
1、Student的这个类,必须手动给出hashCode()和equals
2、Hashset集合不能保证顺序迭代恒久不变!
//创建HashSet集合对象 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("-------------------------------"); //System.out.println(s1.hashCode()); //System.out.println(s2.hashCode()); //添加集合中 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());-----------------卢俊义---28 武松---30 宋江---35 宋江---30
//需要创建一个大的集合 ArrayList<ArrayList<Student>> bigArray = new ArrayList<>() ; //第一个子集合ArrayList<Student> ArrayList<Student> firArray = new ArrayList<>() ; Student s1 = new Student("高圆圆",42) ; Student s2 = new Student("文章",35) ; Student s3 = new Student("王宝强",30) ; firArray.add(s1) ; firArray.add(s2) ; firArray.add(s3) ; //将第一个子集合添加到大集合中 bigArray.add(firArray) ; //第二个子集合ArrayList<Student> ArrayList<Student> secArray = new ArrayList<>() ; Student s4 = new Student("张三",42) ; Student s5 = new Student("王五",35) ; Student s6 = new Student("李四",30) ; secArray.add(s4) ; secArray.add(s5) ; secArray.add(s6) ; //将第二个子集合添加到大集合中 bigArray.add(secArray) ; //第三个子集合ArrayList<Student> ArrayList<Student> thirArray = new ArrayList<>() ; Student s7 = new Student("盲僧",42) ; Student s8 = new Student("亚索",35) ; Student s9 = new Student("提莫",30) ; thirArray.add(s7) ; thirArray.add(s8) ; thirArray.add(s9) ; //将第三个集合添加到集合中 bigArray.add(thirArray) ; //ArrayList<ArrayList<Student>>遍历大集合 for(ArrayList<Student> arr:bigArray){ for(Student s: arr){ System.out.println(s.getName()+"\t"+s.getAge()); } }
无序性,元素唯一 * * 底层依赖于TreeMap集合, 红黑树结构(也称为 "自平衡的二叉树结构"),可以实现Map的自然排序以及比较器排序取决于 * 使用的构造方法
自然排序:TreeSet<E>(),E类型必须实现Comparable接口,实现自然排序(实现的compareTo(T t))
构造方法: * public TreeSet():构造一个空的树,实现元素自然排序 (取决于存储的元素类型能否实现Comparable接口) * * * 自然排序 ----->执行的TreeSet无参构造方法,而且前提条件当前存储类型必须实现Comparable接口
比较器排序: * public TreeSet(Comparator<? super E> comparator) * * Comparator是一个接口类型 * 1)自定义一个类实现Comparator接口,重写compare方法 * 2)使用接口的匿名内部类(推荐)
//创建TreeSet集合对象 // public TreeSet(Comparator<? super E> comparator) //MyComparator myComparator = new MyComparator() ; //方式1:接口子实现类对象 //方式2:接口的匿名内部类 TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() { @Override public int compare(Student s1, Student s2) { //主要条件:按照学生的年龄从小到大排序 //s1---->就是刚才自然排序里面this //s2---->就是刚才自然排序里面s 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()); }
泛型高级通配符(了解) * <?> :任意Java类型,包括Object * <? super E> : 向上限定:E类型以及E父类 * <? extends E>: 向下限定:E以及它的子类 */
Map集合:键映射到值的对象,Map集合可以多个值,但键必须唯一!
Java提供了双列集合,Map<K,V>:提供一个键值对元素(两种类型),键必须唯一(Map针对键有效,跟值无关) * Map<Integer,String> * id name * 1 张三 * 2 李四 * 3 王五 * 4 赵六 * 1 马七
Collection:只能存储一种类型 Collection<E> 简单记"光棍" * Map集合:可以两种类型的,键的类型,值的类型 Map<K,V> 简单记"夫妻对" * * 遍历方式不同 * Collection:就通过5种方式(List) * Map:两种方式: * 方式1:获取所有的K的集合(键的集合) * 通过键获取值 * 方式2: 获取所有的键值对对象Map.Entry<K,V> ("结婚证") * 通过键值对对象获取所有的键("结婚证男方") * 通过键值对对象获取所有的值("结婚证女方") * * * 有内在联系: * TreeSet集合---->Collection---->间接的使用到了TreeMap集合的put方法 * HashSet阶------>Collection---->间接使用到了HashMap的put方法
V put(K key,V value):添加键值对元素 * 注意事项: * 如果key是第一次添加,那么返回的结果为null * 如果key是否重复添加,第二次添加,返回的上一次添加的键对应的值 * * V remove(Object key):删除指定的键,返回被删除键对应的值 * void clear() * * boolean containsKey(Object key) :是否包含指定的键 (使用居多) * boolean containsValue(Object value):是否包含指定的值
1 删除指定的键
V remove(Object key):删除指定的键,返回被删除键对应的值
void clear()
System.out.println(map.remove("杨过"));----------------小龙女
2 是否包含指定的键
boolean containsKey(Object key) :是否包含指定的键 (使用居多)
System.out.println(map.containsKey("周杰伦")) ;-------------false System.out.println(map.containsKey("王宝强")) ;-------------true
3 是否包含指定的值
boolean containsValue(Object value):是否包含指定的值
System.out.println(map.containsValue("郭蓉")) ;-----------------false System.out.println(map.containsValue("小龙女")) ;-----------------false
Map遍历功能方式1: Set<K> keySet() :获取当前Map集合中的所有的键的集合 (将所有的丈夫集中起来,找对应的妻子) + V get(Object key):通过键获取值
// 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); }
Map遍历功能方式2: 获取所有的结婚证 (键值对对象) Set<Map.Entry<K,V>> entrySet() 通过键值对象 获取键 /获取值(通过结婚证找男方/女方) K getKey() V getValue()
//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); }
Map集合针对键有效:不能迭代顺序恒久不变----------------可以进行简单的去重 HashMap的put方法依赖于hashCode()和equals方法,键的类型必须重写Object类的hashCode和equals方法,保证键唯一!
HashMap<Student,String> map = new HashMap<>() ; //创建几个学生对象 Student s1 = new Student("文章",35) ; Student s2 = new Student("文章",35) ; Student s3 = new Student("文章",37) ; Student s4 = new Student("潘玮柏",40) ; Student s5 = new Student("赵又廷",39) ; Student s6 = new Student("蔡徐坤",38) ; Student s7 = new Student("蔡徐坤",38) ; Student s8 = new Student("肖战",30) ; map.put(s1,"足球") ; map.put(s2,"篮球") ; map.put(s3,"足球") ; map.put(s4,"吸毒") ; map.put(s5,"高圆圆") ; map.put(s6,"乒乓球") ; map.put(s7,"篮球") ; map.put(s8,"演戏") ; //遍历 Set<Student> students = map.keySet(); for(Student key :students){ //通过键获取值 String hobit = map.get(key); System.out.println(key.getName()+"---"+key.getAge()+"---"+hobit); } 文章---37---足球 赵又廷---39---高圆圆 肖战---30---演戏 潘玮柏---40---吸毒 蔡徐坤---38---篮球 文章---35---篮球
TreeMap: 红黑树结构---针对Map的键按照条件排序---键属于自定义的情况
方式1: public class Student implements Comparable<Student> public int compareTo(Student s) { //主要条件:学生的年龄从小到大排序 int num = this.age - s.age ; //如果年龄相同,要比较姓名的内容是否相同 int num2 = (num==0)? (this.name.compareTo(s.name)):num ; return num2;
方式2: //比较器排序:匿名内部类 TreeMap<Student,String> tm = new TreeMap<>(new Comparator<Student>() { @Override public int compare(Student s1, Student s2) { //主要条件:学生的年龄从大到小排序 int num = s2.getAge() - s1.getAge() ; //如果年龄相同,要比较姓名的内容是否相同 int num2 = (num==0)? (s1.getName().compareTo(s2.getName())):num ; return num2; } }) ;
1.自然升序排序
public static <T extends Comparable<? super T>> void sort(List<T> list):按照自然升序排序(针对List集合排序)
//public static <T extends Comparable<? super T>> void sort(List<T> list): Collections.sort(list);
2.比较器排序
public static <T> void sort(List<T> list,Comparator<? super T> c):按照比较器排序针对List集合
//使用比较器排序:针对List集合 //public static <T> void sort(List<T> list,Comparator<? super T> c):按照比较器排序针对List集合 Collections.sort(list, 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; } });
3.自然顺序中List的最大值
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T>:获取当前自然顺序中List的最大值
//public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> Integer max = Collections.max(list); System.out.println(max);
4.最小值
public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T>:最小值
System.out.println(Collections.min(list));
5.List集合顺序反转
public static void reverse(List<?> list):对List集合顺序反转
Collections.reverse(list);//反转 System.out.println(list);
6.随机置换
public static void shuffle(List<?> list):随机置换
// public static void shuffle(List<?> list):随机置换 Collections.shuffle(list); System.out.println(list);
```java //1)牌盒 //创建一个牌盒Map:HashMap<Integer,String> key:编号 value:牌 HashMap<Integer, String> hm = new HashMap<>(); //创建一个ArrayList集合:存储编号 ArrayList<Integer> arrayList = new ArrayList<>() ; //2)装牌 //创建点数数组 String[] numbers = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"} ; //创建花色数组 String[] colors = {"♥","♠","♣","♦"} ; //拼接 //定义牌的编号:0开始 int index = 0 ; for(String number:numbers){ for(String color:colors){ String porker = number.concat(color); //将编号以及牌都添加HashMap集合 hm.put(index,porker) ; //单独给ArrayList存储编号 arrayList.add(index) ; index ++ ; } } //给HashMap集合添加小王,大王,给ArrayList添加小王,大王的编号 hm.put(index,"小王") ; arrayList.add(index) ; index ++ ; hm.put(index,"大王") ; arrayList.add(index) ; // System.out.println(arrayList); //3)洗牌:随机置换 ArrayList<Integer> 洗的是编号 Collections.shuffle(arrayList); // System.out.println(arrayList); //4)发牌 /* 为了保证牌有序,发也是编号 * 三个人都分别是 TreeSet<Integer>集合 * 创建一个集合:diPai * 判断: * 如果角标>=牌盒整个size()-3 底牌 * 如果角标 %3 == 0 第一个人的 * 如果角标 %3 == 1 第二个人的 * %3 == 2 第三个人的 */ TreeSet<Integer> player1 = new TreeSet<>() ; TreeSet<Integer> player2 = new TreeSet<>() ; TreeSet<Integer> player3 = new TreeSet<>() ; //底牌 TreeSet<Integer> diPai = new TreeSet<>() ; for(int x = 0 ;x < arrayList.size() ; x ++){ //0开始 if(x >= arrayList.size()-3){ diPai.add(arrayList.get(x)) ; }else if(x % 3 == 0 ){ player1.add(arrayList.get(x)) ; }else if(x % 3 == 1){ player2.add(arrayList.get(x)) ; }else if(x % 3 ==2){ player3.add(arrayList.get(x)) ; } } //看牌://看牌:每一个人都可以看牌,还可以看底牌,所以看牌封装一个功能 lookPoker("张俊杰",player1,hm); lookPoker("高圆圆",player2,hm); lookPoker("赵又廷",player3,hm); lookPoker("底牌",diPai,hm); } public static void lookPoker(String name,TreeSet<Integer> ts,HashMap<Integer,String> hm){ //玩家1的牌是:xxx... //玩家2的牌是:xxx.... System.out.print(name+"的牌是:"); //遍历TreeSet集合,获取每一个编号 for(Integer key: ts){ //获取到每一个编号---在HashMap集合中属于key(键) 编号 String poker = hm.get(key); //在大Map集合中通过键获取值 System.out.print(poker+" "); } System.out.println(); }
包含所有的错误以及异常! 它是一个超类(父类)
error: 非常严重问题 (跟代码没有太大有关系)
OOM Out Of Memory:内存溢出 (严重问题)
举例:
手机移动端 (旅游app)
下拉刷新的速度比图片还快(一个activity:界面 一次性刷新很多图片)
error----> 在生活中 "地震了,不可抗力的因素"
Exception:异常
编译时期异常和运行时期异常(RuntimeException):程序在运行过程中出现问题(代码书写不严谨)
只要不是RuntimeException的子类都是属于编译时期异常
Exception:异常
编译时期异常: 在程序,运行前需要检查的! 在生活中 "长途旅行之前,检查你的车胎情况"...
运行时期异常: 在程序.程序代码逻辑问题(代码不严谨) 在生活中 "no zuo no die"
```
很多的子类: NullPointerException, ClassCastException, ArrayIndexOutOfBoundsException....
运行时期异常:
一般程序员逻辑结构不严谨导致的问题,调用者可以进行显示处理(try...catch.../throws)
也可以不进行显示处理,通过逻辑语句进行处理!
编译时期异常:调用者必须显示处理,不处理,编译通过不了,程序运行不了如果在当前方法中已经去捕获了try...catch...,调用者无序进行处理,
但是如果在方法中抛出异常的,调用者必须处理(捕获/抛出throws)
标准格式:try...catch...finally
throws:抛出
//使用try...catch进行捕获异常 try{ //可能出现问题代码 int a = 10 ; int b = 0 ; //直接获取到的,以后可能值---->通过一些方法获取到的值 System.out.println(a/b); System.out.println("over"); }catch(ArithmeticException e){ //捕获异常:可以使用大的Exception,但是捕获:具体异常具体捕获 System.out.println("除数不能为0"); }
1)有的时候没有办法去抛出,继承关系中,如果子类继承父类,父类的该方法没有异常,子类重写该方法的时候,只能try...catch2)子类继承父类,如果父类的该方法本身抛出异常了,那么子类重写该方法的时候,要么跟父类的方法的异常类名一致,要么是该异常的类子类!
共同点:都是抛出
* 用法不同:
* 1)使用位置不同
* throws:
* a)将异常抛出在方法声明上
* b)在方法名的后面可以跟多个异常类名,中间逗号隔开!
* throw
* a)在方法的语句体中某个逻辑语句中
* b)它后面只能跟异常对象,而不是类名
*
*
* 2)调用者是否处理不同
* throws:调用者必须进行显示处理(try...catch/throws),否则报错
* throw:调用者无须显示处理,一般情况都是在逻辑语句进行处理
*
* 3)出现异常是否肯定性
* throws:在方法上的,执行某个方法的代码中,可能有问题(表示出现异常的一种可能性)
* throw:执行某段代码一定会执行这个异常(表示出现异常的一种肯定性)
*
* 4)
* throws---->将具体的处理交给jvm---通过jvm吧异常信息打印控制台上
* 显示的底层源码而且会显示当前错误消息字符串
*
* throw---->程序 中某段代码有问题:只是打印异常类名(jvm处理)
private static void method2() throws ArithmeticException { int a = 10 ; int b = 0 ; //逻辑语句 if(b!=0){ System.out.println(a/b); }else{ //抛出一个对象 //throw 匿名对象 new XXXException() ; throw new ArithmeticException() ; } }
finally:不能单独使用,它是结合try...catch....finally:异常的标准格式
* finally用法:
* 特点:
* 释放相关的资源,代码一定执行的
*
* 除非在执行finally,jvm退出了!
成员变量:
* public static final InputStream in:标准输入流
InputStream in = System.in ; //字节输出流
//键盘录入的就可以用到Scanner / BufferedRead 字符缓冲输入流:读数据
Scanner sc = new Scanner(in)
* public static final PrintStream out:标准输出流
PrintStream ps = System.out ; //字节打印流
//println(任何数据类型):PrintStream流中的一些工
ps.println(10);
ps.println(12.56);* public static final PrintStream err:错误输出流(打印错误信息/一些信息需要用户引起注意:相关的日志)
* 后期使用日志文件:log4j.properites
* 级别
* info:详情信息
* debug:断点调试模式
* error:错误信息
* warning:警告信息
*
*
*
* System.exit(0) :jvm退出 ,0表示正常终止
System.gc() ;* public static void gc():手动开启垃圾回收器,会去回收内存中没有更多引用的对象!
1.start() 和 run() 区别
Thread 类中的start() 和 run() 方法有什么区别
1.start():通过start()方法来启动一个线程,此时线程处于就绪状态,可以被JVM来调度执行,在调度过程中,JVM通过调用线程类的run()方法来完成实际的业务逻辑,但run()方法结束后,此线程就会终止,所以通过start()方法可以达到多线程的目的.2.run():如果直接调用线程类的run()方法,会被当做一个普通的函数调用,程序中仍然只有主线程这一个线程,即start()方法能够一步的调用run()方法,但是直接调用run()方法确实同步的,无法达到多线程的目的.
2.线程的状态
线程的状态有哪些
NEW 新建状态/初始状态 线程被构建,但是还没有调用start()方法.
RUNNABLE 运行状态/执行状态 Java线程将操作系统中的就绪和运行两种状态称为:运行中
BLOCKED 阻塞状态 表示线程阻塞于锁
WAITING 等待状态 表示线程进入等待状态,进入该状态表示当前线程需要等待其他 线程做出一些特定的动作TIME_WAITING 超时等待状态 该状态不同于WAITING,它是可以在指定的时间自行返回的.
TERMINATED 终止状态 表示当前线程已经执行完毕.
为什么wait, notify 和 notifyAll这些方法不在thread类里面?而是定义在Objec类中
JAVA提供的锁是对象级别的而不是线程级别的,每个对象都有锁,通过线程获得. 如果线程需要等待某些锁那么调用对象中的wait()方法机就意义了,如果wait()方法定义在Thread类中,线程正在等待的是哪个锁就不明显了. 简单的锁,由于wait,notify,notifyAll都是锁级别的操作,所以把他们定义在Obkect类中,因为锁属于对象.
sleep方法和wait方法的区别?
原理不同:sleep()方法是Thread类的静态方法,是线程用来控制自身流程的,它会使此线程暂停执行一段时间,而把执行机会让给其他线程,等到计时时间一到,此线程会自动"苏醒".而wait()方法是object类的方法,用于线程间的通信,这个方法会使当前拥有该对象锁的进程等待,知道其他线程调用notify()(或者notifyALL方法)时才"苏醒" 1.sleep()方法和wait()方法来自不同的类分别是Thread和Object.2.sleep()方法没有释放锁,而wait方法释放了锁,是的其他线程可以使用同步控制块或者方法.3.wait,notify和nitifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用.4.sleep必须捕获异常,wait,notify,notifyAll同样需要捕获异常.
校验多线程是否是安全问题的标准是什么?如何解决?
标准:1.当前是否是一个多线程环境;
2.是否存在共享数据;
3.是否有多条语句对共享数据进行操作;
数组高级查找算法之二分搜索算法前提条件数组必须有序,如果数组本身无序,让我们查询元素,先排序再去查找!(有条件需求先排序,再查 如果没有条件需求,只能使用基本元素查找法:从头查找到尾)
//返回值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 ;
如果需求明确要求手动书写,直接可以使用工具类即可!Arrays工具类:
里面提供排序sort(任何数组进行排序) :元素升序排序 Integer,String 里面提供二分搜素法(任何类型进行查询 ,int key)
int index4 = Arrays.binarySearch(arr, 55);
System.out.println(index4);--------------------------------- 4
int index5 = Arrays.binarySearch(arr, 66);
System.out.println(index5);--------------------------------- -6
面试题:
如果在某个方法中捕获异常,
但是该方法有返回值类型,如果在catch语句出现return语句,finally代码还会执行吗?
如果会执行,在return前还是在后
答:
finally会执行,但是现在这个代码,在catch语句已经形成返回路径,它会记录最终返回就是30;finally是去释放资源用的,
很少牵扯业务代码;都会执行的,除非jvm退出!执行在return前.
int num = getNum(10) ; System.out.println(num); private static int getNum(int i) { try{ i = 20 ; //i = 20 ; System.out.println(i/0); //除数为0 }catch (ArithmeticException e){ i = 30 ; //i =30 return i ; // return i = return 30 :已经在catch语句形成返回的路径 返回结果就是30 }finally { //finally代码一定会执行,除非jvm退出了 i = 40 ; // i = 40 } return i;
1.创建线程的实现
创建线程的实现 方式1:
1)将一个类声明为Thread的子类
2) 这个子类应该重写Thread类的run方法
3)然后可以分配并启动子类的实例。
启动线程用的start()而不是run()
run()只是一个普通方法,不会出现线程的执行具有随机性(不会互相抢占cpu执行权)
不会出现两个线程并发执行
2.Thread类的构造方法:
Thread(String name):创建线程类对象,设置名称
3.Thread类的成员方法
public final String getName():获取线程名称
public final void setName(String name):设置线程名称
4.线程的优先级
Thread类中静态常量字段(成员变量field)
public static final int MAX_PRIORITY 10 最大优先级
public static final int MIN_PRIORITY 1 最小优先级
public static final int NORM_PRIORITY 5 默认优先级
public final void setPriority(int newPriority):设置线程的优先级
public final int getPriority():获取优先级
t1.setPriority(10); //最大优先级-------------------设置优先级 t2.setPriority(1);//最小优先级 int num1 = t1.getPriority();//---------------------获取优先级 int num2 = t2.getPriority(); int num3 = t3.getPriority();
3.守护线程
public final void setDaemon(boolean on)参数为true,表示标记当前线程为守护线程,当正在运行的线程如果都是守护线程,则jvm自动退出 这个方法必须在启动线程之前调用(start()之前)
//创建两个线程 ThreadDaemon td1 = new ThreadDaemon() ; ThreadDaemon td2 = new ThreadDaemon() ; //设置名称 td1.setName("张飞"); td2.setName("关羽"); //设置为守护线程 td1.setDaemon(true) ;//----------------------true--标记当前线程为守护线程 td2.setDaemon(true) ; //启动线程 td1.start(); td2.start(); // public static Thread currentThread():获取正在运行的线程执行对象的引用 Thread.currentThread().setName("刘备"); //提供for循环: for(int x = 0 ; x < 5 ; x ++){ System.out.println(Thread.currentThread().getName()+":"+x); } ```
4.join方法
public final void join() throws InterruptedException://等待该线程终止!
底层依赖于线程安全的方法join(0): 永远等待(当前执行完毕结束!)
又依赖于wait(long time)考虑安全问题:
三个线程---优先级 都是默认(5)
都去设置join(): 三个线程谁先抢占到谁先执行
//创建三个线程 JoinThread jt1 = new JoinThread() ; JoinThread jt2 = new JoinThread() ; JoinThread jt3 = new JoinThread() ; //设置线程名称 jt1.setName("李渊") ; jt2.setName("李世民") ; jt3.setName("李元霸") ; //启动线程 jt1.start(); //jt1调用join try { jt1.join(); } catch (InterruptedException e) { e.printStackTrace(); } jt3.start(); ```
5.yiela方法(暂停当前正在执行的线程,执行对方线程)
public static void yield()://暂停当前正在执行的线程,执行对方线程
main方法: //创建两条线程对象 YieldThread yt1 = new YieldThread() ; YieldThread yt2 = new YieldThread() ; //设置名称 yt1.setName("高圆圆") ; yt2.setName("赵又廷") ; //启动线程 yt1.start(); yt2.start(); 重写run方法: public void run() { for(int x = 0 ; x <100 ; x ++){ System.out.println(getName()+":"+x); Thread.yield(); //暂停当前线程,执行对方线程-----------高0-赵0-高1- } }
6.电影院卖票例题
电影院有三个窗口,共同出售100张票,使用多线程创建方式1来进行实现!分析
1)自定义类SellTicket extends Thread
tickets:票 = 100张;
2)重写run方法
耗时的操作 :模拟一致有票,使用while(true)
3)在main用户(主线程)创建三个 sellTicket 对象
4)分别设置线程名称:窗口1,窗口2,窗口3
5)分别启动线程
7.多线程的实现步骤2
多线程的实现方式2步骤1)自定义类实现Runnable接口
2)重写Runnable接口的run方法
3)在main用户线程中
可以分配类的实例(创建类的实例)
4)创建当前类对象,然后创建Thread类对象,将当前类对象作为参数来传递
当前类---->"资源共享类"
Thread(Runnable target, String name)
5)分别启动线程即可!
public void run() { //耗时的操作 for(int x = 0 ; x < 100 ; x ++){ //public static Thread currentThread() System.out.println(Thread.currentThread().getName()+":"+x); } } 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(); }
8.第二种实现方式--静态代理
静态代理* 特点:真实角色和代理角色必须实现同一个接口* 真实角色:专注于自己的功能* 代理角色:完成对真实角色功能的"增强"
public static void main(String[] args) { //接口多态 //Mary mary = new You() ; You mary = new You() ; mary.mary(); System.out.println("----------------------"); //静态代理:通过婚庆公司帮助自己You来完成结婚 //真实角色 You you2 = new You() ; // MyRunnable WeddingCompany wc = new WeddingCompany(you2) ;// Thread类对象 wc.mary(); //定义一个接口的接口 interface Mary{ void mary() ;//结婚 } //自己:真实角色 class You implements Mary{ @Override public void mary() { System.out.println("结婚了,很开心..."); } } //代理角色:婚庆公司 在你结婚之前,它可以给你布置婚礼线程, 结婚之后,开开心心吃席 class WeddingCompany implements Mary{ //将真实角色作为参数传递 private You you ; public WeddingCompany(You you){ this.you = you ; } @Override public void mary() { System.out.println("给你布置婚礼现场..."); you.mary(); //只要专注于自己的事情! System.out.println("婚礼线程布置完毕,吃席...");
9.检验多线程安全问题的标准(同步代码块)
1)是否是多线程环境 是 不能更改,使用多线程实现
2)是否存在共享数据 是 (资源类的数据: tickets 票) 必须要有共享数据
3)是否存在多条语句对共享数据的操作 是 解决点
解决----Java提供同步机制:同步代码块 将多条对共享数据包裹起来
synchronized(锁对象){
将多条对共享数据包裹起来
}锁对象:必须要多个线程使用的同一个锁对象,而不是分别自己的锁对象!
while(true){ //t1,t2,t3 //解决方案: //将多条语句对共享数据的操作包裹起来 //synchronized (new Object()){ //锁对象 :三个线程分别使用自己的锁 //必须为是同一个锁对象 synchronized (obj){ //模拟网络延迟 //判断 if(tickests>0){//100>0 //t1先进来,睡眠150毫秒,t1已经睡完了,执行下面的操作 //t3先进来,睡眠150毫秒,t3醒来之后 //t2最后抢占到,醒来之后 try { Thread.sleep(100); //单位为毫秒数 } catch (InterruptedException e) { e.printStackTrace(); } //输出窗口信息 System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickests--)+"张票");
10.电影院卖票案例
public class SellTicket implements Runnable { //成员变量;100张票 public static int tickests = 100 ; //创建一个锁对象: //public Object obj = new Object() ; //创建Demo类对 Demo d = new Demo() ; //t1,t2,t3 @Override public void run() { //模拟一直票 while(true){ //t1先抢占到CPU执行权 //t1,t2,t3在抢占CPU执行权 //同步代码块 synchronized (d){ //t1进来之后,别的t2,t3线程进不来的 //t3进来,t1,t2进不来 if(tickests>0){//100>0 try { Thread.sleep(100); //单位为毫秒数 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickests--)+"张票"); } }//t1出来,t3出来---回到同步代码块之前,继续抢 //锁的释放,同步结束之后就会释放锁(同步锁)
11.同步方法
什么是同步方法:如果一个方法的方法体的第一句话就是同步代码块
可以将synchronized关键字提取到方法声明上,跟在权限修饰符的后面
权限修饰符 synchronized 返回值类型 方法名(形式列表){ //非静态的同步方法 业务逻辑... }
public void run() { while(true){ if(x % 2 ==0){ // // synchronized (obj){ // synchronized (this){ //跟下面的非静态同步方法锁对象一致 synchronized (SellTicket.class){ //跟下面的静态的同步方法锁对象一致 if(tickets>0){ //睡眠 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票"); } } }else{ sellTicket() ; //同步方法 } x ++ ; //x=1 //public synchronized void sellTicket() {//sellTicket 的锁对象是什么? 默认都是非静态的同步锁的是this:当前类对象的地址值引用 public static synchronized void sellTicket() { //静态的同步方法:锁对象,跟类相关: 当前类的字节码文件对象: 类名.class if(tickets>0){ //睡眠 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
12.死锁问题
线程安全问题:可以通过同步方法或者是同步代码块去解决,但是执行过程中就可能出现死锁问题
死锁问题:
(使用同步机制解决线程安全) 线程和线程之间出现了互相等待的情况!
解决方案:
多个线程之间的通信:必须使用的是一个资源类对象,而不能是每一个线程在使用自己的资源类对象!
使用生成者和消费者模式思想去解决,前提条件:生成者线程和消费者线程 必须操作的同一个资源类对象!
//锁对象的类 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");// "if objB" } } }else{ //t2 synchronized (MyMonitor.objB){ System.out.println("else ObjB"); //else objB synchronized (MyMonitor.objA){ System.out.println("else objA"); //"else objA" } } } /** * t2线程先抢到了 * else ObjB ---->等待ObjA锁释放 * if ObjeA ---->等待ObjB锁释放 * * t1线程先抢占到了 * if ObjeA * else ObjB */
13.使用生成者和消费者思想模式---解决线程死锁问题(包子)
1.思想概念
* 1)StuffBun包子类属性
* 包含包子的名称name
* 包子的大小type
* 2)生产者资源类 SetBun 产生包子
* 3)消费者资源类 GetBun 使用包子
* 4)ThreadDemo:main 用户线程
*
* 按照上面的方式:模拟生产者产生数据,消费者使用数据出现问题 null---null
*
* 生产资源类中和消费者资源类中所操作的包子对象不是同一个对象!
*
* 可以将包子通过生产资源类或者消费者资源类 通过构造方法传递
*
*
* 优化1:
* 加入while循环,模拟包子一直生产和一直消费!
*
* 出现问题:数据紊乱:加入同步代码块给每一个资源类中都加入解决! 将多条语句对共享数据的操作包起来!
2.测试类
//创建一个包子对象 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();
3.包子类
//包子类/资源类 public class StuffBun { //成员变量不私有化 String name ;//包子的类型(肉包子,菜包子) String bunType ;//大包子/小包子 }
4.生产者类/生产者资源类
//生成者资源类 public class SetBun implements Runnable { //声明这个包子类 private StuffBun stu ; public SetBun(StuffBun stu){ this.stu = stu ; } //定义一个统计变量 int x = 0 ; @Override public void run() { //产生包子 //不断的产生数据 while(true){ synchronized (stu){ if(x % 2 == 0){//t1 stu.name = "肉包子" ; stu.bunType = "大包子"; }else{ stu.name = "菜包子" ; stu.bunType = "小包子" ; } } x ++ ;
5.消费者资源类
//消费者资源类 public class GetBun implements Runnable { private final StuffBun stb; //声明包子类的变量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); }
14.包子问题优化2
问题:
* 消费者资源类所在的消费者线程中,每次输出一大片的内容:
* 线程的执行随机性----线程抢占CPU的执行权,一点点时间片执行很多次
*
优化2:
* 想出现依次打印
* 肉包子---大包子
* 菜包子---小包子
* ...
*
* wait()+notify()--->实现同步机制(并且同时信号法:将死锁问题解决!)
```
1.包子类
public class StuffBun { //成员变量不私有化 String name ;//包子的类型(肉包子,菜包子) String bunType ;//大包子/小包子 //定义标记:表示是否存在包子数据 boolean flag ; //默认false,没有数据 }
2.生产者资源类
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(stu.flag){ //锁对象调用wait发那个发 try { stu.wait();//释放锁对象... } catch (InterruptedException e) { e.printStackTrace(); } } if(x % 2 == 0){//t1 stu.name = "肉包子" ; stu.bunType = "大包子"; }else{ stu.name = "菜包子" ; stu.bunType = "小包子" ; } //如果现在有数据了 //改变信号 stu.flag = true ;//有数据类 //通知(唤醒)消费者线程,赶紧使用数据 stu.notify(); //唤醒对方线程 } x ++ ; }
3.消费者资源类
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){ synchronized (stu){ //如果当前消费资源类中存在包子数据,先等待消费使用完毕数据 if(!stu.flag){ //等待使用完毕数据 try { stu.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(stu.name+"---"+stu.bunType); //改变信号值 //如果包子消费完毕 stu.flag = false ; //唤醒对方线程(生产者资源类,别等了,产生数据) stu.notify(); }
4.测试类
//创建一个包子对象 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();
15.Lock
JDK5以后提供java.util.current.locks.Lock :提供比syncrhonized方法(/同步代码块)更具体的锁定操作多个线程并发访问,抢占共享资源数据,通过lock实现多个线程对某个共享资源进行独占访问,不会安全问题!
1.资源类
* 资源类---需要被多个线程进行共享 */ 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(); }
2.测试类
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(); }