1 可变参数
当方法的参数列表数据类型已经确定,但是参数的个数不确定时,可以使用可变参数
- 格式为
修饰符 返回值类型 方法名(数据类型...变量名){} - 原理:底层为一个数组,根据传递参数个数不同,会创建不同长度的数组来存储参数,传入参数可以是0个
- 终极写法
public static void func(Object...obj)```java package Pro;
public class VarArgs { public static void main(String[] args) { System.out.println(sum()); System.out.println(sum(1)); System.out.println(sum(1,2)); System.out.println(sum(1,2,3)); } public static int sum(int…args){ int sum = 0; for (int n : args) { sum += n; } return sum; } }
<a name="gcFr3"></a># 2 异常- **异常** :程序在执行过程中,出现的非正常情况,最终会导致JVM的非正常停止<a name="cPvMl"></a>## 2.1 异常的分类- `java.lang.Throwable` 是所有异常的超类,有两个直接子类 `java.lang.Error` 和 `java.lang.Exception`- **Error** 非常严重的错误,无法通过处理的错误,只能事先避免- **Exception** 编译期异常,由于使用不当,可以避免的错误- **RuntimeError** 运行期异常,java程序运行过程中出现的问题<a name="jmlmN"></a>## 2.2 throw/throws<a name="nEPiW"></a>### throw- 可以使用 `throw` 关键字在指定的方法中抛出指定的异常- 注意事项- `throw` 必须写在方法的内部- `throw` 后 `new` 的对象必须是 `Exception` 或其子类对象- `throw` 抛出指定的异常对象,就必须处理这个异常对象- `throw` 后面创建的是 `RuntimeException` 或其子类,可以不处理,默认交给JVM处理- `throw` 后面创建的是编译异常,就必须处理此异常,要么 `throws` 要么 `try...catch````javapackage Pro.Exception;public class DemoThrow {public static void main(String[] args) {int[] arr1 = null;int[] arr2 = {};// int e1 = getElement(arr1, 0);// System.out.println(e1);int e2 = getElement(arr2, 0);System.out.println(e2);}public static int getElement(int[] arr, int index){if(arr==null) {throw new NullPointerException("所传递的数组为空!");}if(index<0 || index>=arr.length){throw new ArrayIndexOutOfBoundsException("数组索引越界!");}return arr[index];}}
throws
- 可以使用
throws关键字将异常对象抛出给方法的调用者处理,最终交给JVM处理 - 注意
throws必须写在方法声明处throws声明的异常必须是Exception或其子类throws必须抛出方法内部的所有异常对象,若存在继承关系,只需抛出其父类- 调用了一个声明抛出异常的方法,就必须处理此异常,若不处理
- 要么继续抛出该异常
- 要么
try...catch```java package Pro.Exception;
import java.io.FileNotFoundException; import java.io.IOException;
public class DemoThrows { public static void main(String[] args) throws IOException { String fileName = “a.txt”; readFile(fileName); }
public static void readFile(String fileName) throws IOException {if (!fileName.equals("a.txt")) {throw new FileNotFoundException("传递的文件不是a.txt");}if(!fileName.endsWith(".txt")){throw new IOException("文件后缀名不对!");}System.out.println("路径正确,读取成功!");}
}
<a name="JnlRW"></a>## 2.3 try...catch...finally- try中可能会抛出多个异常对象,就可以使用多个catch来处理异常- 如果try中产生了异常,就会执行catch中的异常处理逻辑- 如果try中未捕获到异常,不会中执行catch中的代码块- 无论是否捕获到异常,try...catch之后的代码都可以正常执行```javapackage Pro.Exception;import java.io.FileNotFoundException;import java.io.IOException;public class DemoTryCatch {public static void main(String[] args) {String fileName = "b.txt";try{readFile(fileName);}catch (IOException e){System.out.println("文件路径不对!");}System.out.println("后续代码继续执行!");}public static void readFile(String fileName) throws IOException {if(!fileName.endsWith(".txt")){throw new IOException("文件后缀名不对!");}if(!fileName.equals("a.txt")){throw new FileNotFoundException("找不到文件" + fileName);}System.out.println("成功打开" + fileName);}}
finally是无论是否出现异常都会执行的代码块注意事项
public String getMessage()返回此throwable对象的简述public String toString()返回此throwable对象的详细信息public void printStackTrace()JVM打印异常默认调用的方法2.5 多异常处理
多异常处理有三种方式
多个异常分别处理
- 一次捕获,多次处理(一个try,多个catch)
- catch里面定义的异常变量,如果有子父类关系,那么子类的异常变量必须写在上面,否则会报错
- 一次捕获,同时处理
- try中的所有异常可以归类为一个超类异常时方可使用 ```java package Pro.Exception;
import com.sun.tools.javac.util.List;
public class DemoMultiException {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
List
// I.一次捕获,多次处理
// try{ // System.out.println(arr[2]); // System.out.println(list.get(3)); // }catch(ArrayIndexOutOfBoundsException e){ // System.out.println(e); // }catch(IndexOutOfBoundsException e){ // System.out.println(e); // }
// III.一次捕获,同时处理try{System.out.println(arr[3]);System.out.println(list.get(3));}catch(IndexOutOfBoundsException e){System.out.println(e);}System.out.println("后续代码");}
}
<a name="jfWlK"></a>## 2.6 子父类异常- 如果父类抛出了多个异常,子类重写父类方法时,有三种选择方式- 抛出和父类相同的异常- 抛出父类异常的子类- 不抛出异常- 如果父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常,若子类产生异常,只能捕获处理<a name="IH8hu"></a>## 2.7 自定义异常类```javapublic class CustomException extends Exception/RuntimeException{public CustomException(){super();}public CustomException(String exceptionMessage){super(exceptionMessage);}}
3 多线程
4.1 并发与并行
- 并发 指两个或多个事件在 同一个时间段内 发生
-
4.2 线程与进程
概念
进程 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个将进程从创建、运行到消亡的过程
线程 是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程,也可以有多个线程
线程调度
分时调度
所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间
- 抢占式调度
优先让优先级高的线程使用CPU,如果优先级相同,则随机选择一个,Java使用的为抢占式调度
4.3 创建线程类
- 主线程 执行主(main)方法的线程
创建新线程有两种方法
- 将类声明为
java.lang.Thread的子类,该子类重写Thread类的run方法,然后通过其实例启动一个线程 - 声明实现
Runnable接口的类,该类实现run方法,该类实现一个对象作为Thread类的构造参数,然后可以通过start启动一个线程
注意 : 多次启动同一个线程是非法的,特别是当一个线程已结束执行 ,不能再重新启动
创建线程的第一种方式
package multithreading;public class MyThread extends Thread{@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println("run " + i);}}}
package multithreading;public class Demo01Thread {public static void main(String[] args) {MyThread mt = new MyThread();mt.start();for (int i = 0; i < 10; i++) {System.out.println("main " + i);}}}
创建线程的第二种方式
package multithreading;public class SecondClock implements Runnable{@Overridepublic void run() {for (int i = 0; i < 60; i++) {System.out.println(i + 1);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}
package multithreading;public class Demo02Thread {public static void main(String[] args) {SecondClock sc = new SecondClock();new Thread(sc).start();}}
Thread的方法
Thread的构造方法
public Thread()分配一个新的线程对象public Thread(String name)分配一个指定名字的新的线程对象public Thread(Runnable target)分配一个带有指定目标的新的线程对象public Thread(Runnable target, String name)分配一个带有指定目标的线程对象并起名
Thread的常用方法
String getName()返回该线程的名称void setName(String name)设置线程的名称static Thread currentThread()返回对当前正在执行的线程对象的引用public static void sleep(long millis)睡眠millis毫秒public void start()使此线程启动-
两种方式的区别
实现Runnable接口比继承Thread类所具有的优势
避免了单继承的局限性(一个类只能有唯一一个直接父类)
增强了程序的扩展性,降低了程序的耦合性(解耦)
继承
Thread方式 ```java package multithreading;
public class DemoAnonymous { public static void main(String[] args) { new Thread(){ @Override public void run(){ System.out.println(“这是一个匿名内部类实现的线程”); } }.start(); } }
- 实现 `Runnable` 接口方式```javapackage multithreading;public class DemoAnonymous02 {public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {System.out.println("匿名接口对象实现多线程:" + Thread.currentThread().getName());}}).start();}}
4.4 线程安全
多个线程访问了共享数据 ,就会产生线程安全问题。 线程安全问题是应当避免的,可以让一个线程在访问共享数据是时,其他线程只能等待,而无论此线程是否抢占到CPU执行权。
线程同步
1.同步代码块 synchronized(锁对象){访问统一资源的代码块} 可以使用在某一代码块中,表示对此代码块执行线程访问的互斥操作
- 同步代码块中的锁对象可以使用任意的对象
- 必须保证多个线程使用的锁对象是同一个
- 锁对象作用:只让一个线程在同步代码块中执行
该方法的缺陷 程序频繁地判断锁、获取锁、释放锁,使程序的效率降低
package multithreading;public class SailTicket implements Runnable {private int ticketCount = 100;private Object obj = new Object();@Overridepublic void run() {while (ticketCount > 0) {System.out.println("-------------------开始抢锁--------------------");synchronized (obj) {if (ticketCount > 0) {System.out.print(Thread.currentThread().getName() + "抢到了卖票权,");try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("正在出售第" + ticketCount + "张票");ticketCount--;}}}}}
package multithreading;public class DemoSynchronized {public static void main(String[] args) {SailTicket run = new SailTicket();Thread t0 = new Thread(run);Thread t1 = new Thread(run);Thread t2 = new Thread(run);t0.start();t1.start();t2.start();}}
运行结果图
同步技术的原理 使用了一个锁对象,多个线程同时访问同步代码块时,谁抢到了锁对象谁就优先执行,其他线程则只能被阻塞,等待当前线程执行玩同步代码块后再重新开始抢夺使用权。
2.同步方法
格式:
修饰符 synchronized 返回值类型 方法名(参数列表){访问共享数据的代码}
同步技术的原理 实际上也是利用锁对象,同步方法锁使用的锁对象是this,也就是调用线程的 Runnable 对象本身
package multithreading;public class SailTicket02 implements Runnable {private int ticketCount = 100;@Overridepublic void run() {while (ticketCount > 0) {sailTicket();}}private synchronized void sailTicket() {if(ticketCount > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "抢到了售票权,正在出售第" + ticketCount + "张票");ticketCount--;}}}
package multithreading;public class DemoSynchronized02 {public static void main(String[] args) {SailTicket02 run = new SailTicket02();Thread t0 = new Thread(run);Thread t1 = new Thread(run);Thread t2 = new Thread(run);t0.start();t1.start();t2.start();}}
3.静态同步方法
使用步骤
- 使用
Lock接口的实现类ReentrantLock创建一个对象 - 在可能会出现安全问题的代码前调用
lock()方法 - 在可能会出现安全问题的代码后调用
unlock()方法 ```java package multithreading;
import java.util.concurrent.locks.ReentrantLock;
public class SailTicket03 implements Runnable { private int ticketCount = 100; ReentrantLock l = new ReentrantLock();
@Overridepublic void run() {while (true) {l.lock();if (ticketCount > 0) {try {Thread.sleep(100);System.out.println(Thread.currentThread().getName() + "抢到了售票权,正在出售第" + ticketCount + "张票");ticketCount--;} catch (InterruptedException e) {e.printStackTrace();} finally {l.unlock();}}}}
}
```javapackage multithreading;public class DemoLock {public static void main(String[] args) {SailTicket03 run = new SailTicket03();Thread t0 = new Thread(run);Thread t1 = new Thread(run);Thread t2 = new Thread(run);t0.start();t1.start();t2.start();}}
4.5 线程状态
线程的六种状态及转化
| 线程状态 | 导致状态发生的条件 |
|---|---|
| NEW | 至今尚未启动的线程处于这种状态 |
| RUNNABLE | 正在Java虚拟机中执行的线程 |
| BLOCKED | 受阻塞并等待某个监视器锁的线程 |
| WAITING | 无限期地等待另一个线程来执行某一特定操作的线程 |
| TIMED_WATING | 等待另一个线程来执行取决于指定等待时间的操作的线程 |
| TERMINATED | 已退出的线程 |
线程通信/等待唤醒
多个线程在处理同一个资源,但是处理的动作(线程的任务并不相同),线程之间就存在线程通信问题。
Object类中的方法
void wait()在其他线程调用此对象的notify()方法或notifyAll()方法之前,导致当前线程等待void notify()唤醒在此对象监视器上等待的单个线程,会继续执行wait方法之后的代码
无限期等待
package multithreading;/*卖包子的案例*/public class WaitAndNotify {public static void main(String[] args) {Object obj = new Object();//创建一个顾客线程new Thread() {@Overridepublic void run() {// 保证等待与唤醒只能有一个在执行,需要使用同步技术while (true) {synchronized (obj) {System.out.println("老板,我要买包子。");try {obj.wait();} catch (InterruptedException e) {e.printStackTrace();}//执行wait之后的代码System.out.println("老板,你这包子真不错,我还要。");System.out.println("==============================");}}}}.start();//创建一个老板线程new Thread() {@Overridepublic void run() {while (true) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (obj) {System.out.println("小伙子,你的包子做好了。");obj.notify();}}}}.start();}}
程序执行的结果 
计时等待
void sleep(long m)方法void wait(long m)方法,若在m时间内未被notify唤醒,则线程自动醒来
唤醒方法
void notify()如果有很多人等你做包子,你只做好了一个,你随便选一个给他们吃就行void notifyAll()比如说有很多个人在等你做包子,你给他们同时做好了,就可以叫他们一起来付钱(虽然包子是够的,但人性贪婪,顾客会争着抢着先付钱)4.6 线程池
频繁地创建与销毁线程需要时间,如果一个线程执行很短的时间就结束了,就会大大降低系统的效率,因此引入 线程池 的概念
- 当程序第一次启动的时候,就会创建多个线程,保存到一个集合中
- 当想要使用线程时,就可以从集合中取出线程使用
- 当线程使用完毕后,需要把线程归还给线程池
java.util.concurrent.ExecutorsJDK1.5之后的线程池工厂类
Executors 类中的静态方法
static ExecutorService newFixedThreadPool(int nThreads)创建一个可重用固定线程数的线程池
ExecutorService 接口中的方法
Future submit(Runnable task),提交一个task用于执行,并返回一个表示该任务的Future-
5 Lambda表达式
Lambda的使用前提
必须具有接口,且要求接口中有且仅有一个抽象方法
- 使用Lambda必须具有 上下文推断
注:有且仅有一个抽象方法的接口称之为 函数式接口
先来一个lambda表达式开开眼界
package LAMBDA;public class Demo01 {public static void main(String[] args) {//使用匿名内部类创建多线程new Thread(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " created.");}}).start();//使用lambda表达式创建多线程new Thread(() -> System.out.println(Thread.currentThread().getName() + " created.")).start();}}
5.1 标准格式
Lambda省去面向对象的条条框框,格式由3个部分组成
- 一些参数
- 一个箭头
- 一段代码
其标准格式为(参数类型 参数名称) -> {代码语句}
格式说明
- ():接口中抽象方法的参数列表,无参数就留空,多个参数之间用逗号分隔
- -> : 把参数传递给方法体{}
-
5.2 无参数无返回值
给定一个厨子
Cook接口,内含唯一的抽象方法cookFood()public interface Cook {void makeFood();}
使用lambda标准格式调用
invokeCook方法 ```java public class DemoInvokeCook { public static void main(String[] args) {invokeCook(() -> {System.out.println("店里来客了,快去给我做饭啦!");});
}
private static void invokeCook(Cook cook) {
cook.makeFood();
} }
<a name="BBiDU"></a>## 5.3 有参数有返回值需求描述- 使用数组存储多个Person对象- 调用 `Arrays.sort` 方法对数组中的人年龄进行升序排序```javaimport java.util.Arrays;public class DemoPerson {public static void main(String[] args) {Person[] arr = {new Person("瓜兮兮", 18),new Person("哈搓搓", 28),new Person("憨包包", 20)};for (int i = 0; i < arr.length; i++) {System.out.println(arr[i]);}Arrays.sort(arr, (Person o1, Person o2) ->{return o1.getAge() - o2.getAge();});System.out.println("=======================");for (int i = 0; i < arr.length; i++) {System.out.println(arr[i]);}}}
给定一个计算器 Calculator 接口
public interface Calculator {int calc(int a, int b);}
使用lambda标准格式计算两数之和
public class DemoInvokeCalc {public static void main(String[] args) {invokeCalc(120, 130, (int a, int b) -> {return a + b;});}private static void invokeCalc(int a, int b, Calculator calculator) {int result = calculator.calc(a, b);System.out.println("结果是:" + result);}}
5.4 省略格式
Lambda表达式的内涵:凡是可以根据上下文推导出来的内容,都可以省略书写(可推导,可省略)
可以省略的内容有
