JavaSE之异常、线程知识点总结
一、异常
1.异常
(1)描述:程序出现了问题,指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。
(2)java.lang.Throwable:异常类,Java 语言中所有错误或异常的超类。
(3)分类:
1)error:严重错误(严重问题),无法通过处理进行修复的错误。
比如:内存异常!OOM(Out Of Memory)
解决:通过加入内存条,或者将Linux虚拟机的内存空间设置小一点。
2)Exception: 表示异常,异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的。
编译时期异常:只要不是RuntimeException中的子类都是属于编译时期。
运行时期异常RuntimeException:代码逻辑不够严谨,程序在执行过 程中就会出现问题。
(4)处理方式:
1)try…catch…finally:标准格式,捕获异常
try {
可能出现问题的代码;
}catch(异常名 变量) {
针对问题的处理;
}finally {
释放资源;
}
2)throws:抛出异常 (抛出到方法上)
throws 异常类名
2.编译时期和运行时期的区别
(1)编译时期的异常:开发者必须要进行显示处理,否则编译通过不了。
举例:
String转Date的方法parse(String source) throws ParseException{}
IO流:IOException
(2)运行时期异常:
1)代码逻辑的问题,可以不进行显示处理,在代码中加入一些逻辑判断。
2)进行显示处理:try…catch…。
3.Throwable中的方法
(1)public String getMessage()
作用:获取异常的消息字符串。
(2)public String toString()
作用:获取异常信息的简单描述。
异常信息:异常的全限定名称:包名.类名 : 调用该异常对象的getMessage()获取详细的消息字符串。
(3)public void printStackTrace()
作用:将此对象的信息追踪到堆栈中,具体获取到源码中出问题的位置。
4.捕获异常(try-catch-finally)
(1)流程:jvm通过调用main方法,执行try语句;
如果有异常在内存中产生一个异常实例,与catch中的异常类进行匹配;
如果异常类一致,执行catch语句,不相同输出异常信息;
如果finally之前没有退出jvm的语句,需要执行finally语句,释放系统资源。
(2)处理异常:
1)自己手动处理
2)使用Throwable类中一些方法处理,即jvm将异常信息输出在控制台,并且可以跟踪异常类源码(出现异常的代码位置)。
(3)捕获异常标准格式的变形
try…catch…catch…
try…finally…
try…catch…
(4)存在单个异常的格式:
try{
可能出现问题代码
}catch(异常类名 变量名){
处理异常
}
(5)存在多个异常的格式:
try{
可能出现问题代码
}catch(异常类名1 变量名1){
处理异常1
}catch(异常类名1 变量名2){
处理异常2
}...
(6)JDK7之后:提供的处理方式
try{
可能出现问题的代码
}catch(异常类名1 | 异常类名2 | 异常类名3 ...... 变量名){
处理异常
}
(7)捕获的标准格式
try{
可能出现问题的代码
}catch(异常类名 对象名){
对象名.printStackTrice() ;
}finally{
释放相关系统资源close()
}
(8)finally特点:
释放相关的系统资源,而且finally中的代码一定会执行的;除非在执行 finally语句之前JVM退出了(System.exit(0))。
(9)注意:
1)alt+shift+z:自动添加try…catch…处理异常
2)如果try中存在多个异常,一个一个try…catch…处理,或一个try 多个catch…处理。
3)java.lang.ArithmeticException:除数不能为0,属于运行时期异常。
4)使用多异常处理格式,后面的catch语句中的异常类要么是同级关系要么是前面异常的类父类。
5)使用catch中存在多个异常类的格式时,多个异常类必须是平级关系。
5.抛出异常
(1)throws/throw:抛出异常
(2)位置:抛出在方法声明上,如果方法中的语句体出现了异常,将异常交给 jvm,jvm将异常信息输出控制台上。
(3)throws举例:
public static void main(String[] args) throws ParseException {
String str="2020-10-30";
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
Date date=sdf.parse(str);
}
(4)throw举例:
if(表达式){
语句;
}else{
throw new 异常类名();
}
6.throws和throw的区别
(1)throws:抛出
1)书写位置是在方法声明上。
2)throws的后面跟的异常类名,而且可以跟多个异常类名,中间使用逗号隔开。
3)对于throws它是由调用者进行处理,必须要对带throws的方法进行处理。
4)throws表示抛出异常的一种可能性,这个方法可能出现会问题,也可能没有问题。
(2)throw:抛出
1)书写位置是在方法体中
2)throw的后面跟的异常对象,一般都是匿名对象 new XXXException(),而且只能跟一个异常对象。
3)由方法中逻辑判断进行处理(由方法体中语句进行处理)。
4)throw:表示抛出异常一种肯定性,执行到逻辑判断的时候,一定会执行 这个异常。
7.异常的注意事项
子类继承父类时
(1)父类成员方法有抛出异常
对父类的成员方法进行重写,如果父类的成员方法有异常,那么子类的该方法针对异常处理,要么是跟父类的异常类名一致,要么是父类异常类的子类。
(2)父类成员方法没有抛出异常
父类的成员方法没有抛出异常,子类的该方法不能抛出异常,只能 try…catch…。
8.自定义异常
(1)自定义编译期异常: 自定义类 并继承于 java.lang.Exception 。
(2)自定义运行时期的异常类:自定义类 并继承于 java.lang.RuntimeException 。
9.面试题
如果捕获异常的时候,
try…catch…finally,catch中如果有return语句,那么finally中的代 码还会执行吗?在return之前执行,还是return之后执行?
finally一定会执行,除非Jvm退出了不执行,在return 之前执行。
举例:
try {
a = 20 ; //a =20
System.out.println(a/0);//除数为0的异常
} catch (Exception e){//执行catch语句: 捕获具体的异常
a = 30 ; //a = 30
return a ; //return 30 :已经形成返回路径,如果catch中有return语句,finally代码一定会执行,在return之前执行。
}finally {
a = 40 ; //a = 40
}
return a;
当finally有return时,会直接返回。不会再去返回try或者catch中的返回值,而finally没有return时,try和catch 的return语句并不会马上执行,而是执行完finally代码块之后再返回try和catch里面的值。
10.final,finally,finalize的区别
(1)final:终态的,最终的,是一个修饰符。
修饰类,该类不能被继承;
修饰方法,该方法不能被重写;
修饰变量,该变量成为一个常量。
(2)finally:是try-catch-finally语句的一部份;
finally部分中进行系统资源的释放;
如果其前方没有退出jvm的语句,finally中的代码一定执行。
(3)finalize:Object类中的一个方法;
在gc回收垃圾时,会调用该方法,将没有更多引用的对象回收。
二、线程
1.进程与线程
(1)进程:本地开启一个客户端,就是一个进程,能够调用系统资源的独立单位。
(2)多进程的意义:就是为了提高cpu的使用率。
举例:现在大部分计算机都是多进程的计算机。
(3)打游戏的同时,可以听音乐,这两个进程是同时的吗?
这两个进程不是同时进行的,它是在两个进程之间抢占cpu的时间片。两个进程之间高效的来回切换。
(4)线程:线程是依赖于进程的,将线程看成是进程中某个"任务"。
举例:开启360----开启一个进程
杀毒的同时可以清理内存…
(5)多线程的意义:
为了提高应用程序的使用率,每个线程为了抢占CPU的执行权,他们的抢占过程中具有随机性。
(6)并行和并发
并行:是逻辑上同时发生,指在某一个时间内同时运行多个程序。
并发:是物理上同时发生,指在某一个时间点同时运行多个程序。
(7)Java程序的运行原理:
由java命令启动JVM,JVM启动就相当于启动了一个进程。
接着由该进程创建一个主线程去调用main方法。
(8)jvm是单线程还是多线程
jvm:里面至少有两条线程。
main方法: 主线程,“用户线程”。
垃圾回收线程:通过gc回收没有更多引用的对象。
jvm是可以运行多个线程并发执行。
(9)如何实现一个多线程程序
线程是依赖于进程,创建进程,创建系统资源,但是Java语言不能够直接创建系统资源,JDK提供一个类:Thread线程类。
2.线程程序
(1)单线程程序:在程序的执行过程中,如果执行路径只有一条,就是单线程程序。
(2)多线程程序:在程序的执行过程中,如果执行路径有多条,就是多线程程序。
(3)线程程序的创建
方式1:
1)自定义一个类继承Thread类
2)重写Thread类中的run方法
3)在当前用户线程中(main)创建该类对象,
4)启动线程:对象.start()方法
(5)注意:java.lang.IllegalThreadStateException:非法线程状态异常
出现原因:多次启动同一个线程。
(6)调用run()方法是单线程的
因为run()方法直接调用其实就相当于普通的方法调用,所以看到的是 单线程的效果。
(7)run()和start()的区别
run():仅仅是封装被线程执行的代码,直接调用是普通方法 。
start():首先启动了线程,然后再由jvm去调用该线程的run()方法。
(8)线程的调度
分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片。
抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。
3.Thread中的方法
(1)使该线程开始执行:(准备就绪)
public void start()
Java 虚拟机调用该线程的 run 方法。
(2)获取线程名称
public static Thread currentThread()
作用:表示当前正在运行的线程
getName()
作用:获取线程名称
setName(String name)
作用:设置当前线程名称
(3)public final void setPriority(int newPriority)
作用:设置线程的优先级
(4)public final int getPriority():
作用:获取线程的优先级
1)线程的优先级:常量值
public static final int MAX_PRIORITY:最大优先级(10)
public static final int MIN_PRIORITY:最小优先级(1)
public static final int NORM_PRIORITY:默认优先级(5)
优先级越大的线程抢占CPU的执行权几率大
优先级越小的线程抢占CPU的执行几率小
符合线程的执行具有随机性
2)tp3.setPriority(100);
设置有问题:java.lang.IllegalArgumentException
大于最大优先级或者小于最小优先级:参数是非法的。
(5)public final void setDaemon(boolean on)
作用:设置守护线程
注意:在启动线程之前,调用这个方法才能设置为守护线程。
如果当前运行中线程都是守护线程,JVM退出,但守护线程不会立即停止。
(6)public final void join() throws InterruptedException
作用:等待该线程终止
(7)public static void yield()
作用:暂停当前正在执行的线程对象,并执行其他线程(理解为"谦让")。
(8)public static void sleep(long millis) throws InterruptedException
作用:线程睡眠(阻塞式方法)。
参数为时间毫秒值
(9)@Deprecated:JDK提供内置注解:标记方法是否以过时。
public final void stop()
作用:已过时的方法,但是可以使用!强迫线程停止执行。
(10)public void interrupt():
作用:中断线程,中断线程的一种状态!(6种状态),打破了状态之后,继续执行程序。
4.多线程的实现方式2(推荐)
(1)自定义一个类(资源类),这个类实现Runnable接口。
(2)重写Runnable接口中的run方法。
(3)在主线程中main
1)创建当前资源类对象,
2)在创建Thread类对象,将资源类对象作为参数进行传递
(4)启动线程 start方法
(5)原子性操作:最基本最简单的操作 (tickets–)
5.线程组:ThreadGroup
(1)描述 :一个线程集合。
(2)常用方法:
Thread类中:
public final ThreadGroup getThreadGroup()
作用:获取当前线程所属的线程组。
public Thread(ThreadGroup group,String name)
作用:分配一个新的Thread对象。
ThreadGroup
public final String getName()
作用:获取线程组名称
ThreadGroup(String name)
作用:构造一个新的线程组。
(3)注意:
1)线程组中包含很多个线程,每一个线程默认的线程组名称是main。
2)线程启动过程:start(),当前启动的线程添加到线程组中。
3)将所有的线程添加线程组之后,线程执行完毕了,变成垃圾被回收。
6.线程池
(1)创建一个有固定线程数的线程池,当这些线程使用完毕终止了,它(线程对象)不会被回收掉,会归还到线程池中,下次再利用。
(2)ExecutorService 接口:不能实例化
间接通过:java.util.concurrent.Executors :线程池的工厂类。
静态工厂方法模式: 23种设计模式(思想)---- 创建型设计模式中一种。
(3)静态功能:返回值ExecutorService
public static ExecutorService newFixedThreadPool(int nThreads)
提交异步任务(执行)
Future submit(Callable task)
Future submit(Runnable task)
Future:
V get()
作用:获取当前计算结果的值。
返回值:表示异步计算的结果。
void shutdown()启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
(4)多线程的第三种实现方式
1)创建指定线程数的线程池
Executors.newFixedThreadPool(int nThreads)
2)提交异步任务
Future submit(Callable call)
线程对象名.submit(Runnable/Callable实现类对象);
3)关闭线程池
线程对象名.shutDown();
7.Callable创建线程
(1)创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例。
(2)使用FutureTask类来包装Callable对象,该FutureTask对象封装了 Callable对象的call()方法的返回值。
(3)使用FutureTask对象作为Thread对象的target创建并启动线程(因为 FutureTask实现了Runnable接口)。
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
8.线程匿名内部类
(1)格式:
new 类名/接口名(){
重写方法
};
(2)匿名内部类
本质:继承了该类或者实现了该接口的子类对象
好处:降低了耦合性。(解耦)
(3)实现方式
第一种:继承关系,继承自Thread类
new Thread() {
重写run()方法
}.start();
第二种:实现关系,实现Runnable接口
new Thread(new Runnable() {
重写run()方法
}).start();
9.Timer:定时器工具
(1)包:java.util.Timer;
(2)描述:可以重复执行某个任务,或者执行一次任务。
(3)构造方法:构造一个新的计时器
public Timer()
(4)成员方法:
public void cancel()
作用:终止定时器
public void schedule(TimerTask task,Date time)
作用:在指定日期时间内容将执行这个任务。
public void schedule(TimerTask task, long delay)
作用:在指定时间后执行当前这个task任务。
public void schedule(TimerTask task, long delay,long period):
在指定时间后执行这个任务,然后每经过period时间后重复执行任务。
(5)TimerTask:由定时器Timer来安排是执行一次,还是重复执行的任务。
public abstract void run()
作用:任务要执行的操作
三、线程的使用及问题
1.多线程实现的方式对比
(1)方式:
方式1:
1)自定义一个类MyThread,继承自 Thread
2)重写Thread类中run方法
3)在main中,创建该线程类对象
方式2:
1)自定义一类MyRunnable implements Runnable
2)重写run方法
3)创建资源类对象,创建线程类对象,将资源类对象当做参数传递。
4)线程对象.start()启动线程。
(2)如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。
(3)总结:实现Runnable接口比继承Thread类所具有的优势
1)适合多个相同的程序代码的线程去共享同一个资源。
2)可以避免java中的单继承的局限性。
3)增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
4)线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
2.Java能否直接开启线程
(1)Java不能够直接开启线程,需要通过Thread类对象调用start()启动。
(2)start方法:启动线程,通过JVM调用run方法执行线程操作。
start方法本身就是同步方法,线程安全的方法
(3)底层实现:start方法将所有的线程开启(在运行之前),将线程添加到线 程组中。依赖start0方法调用run方法。
(4)注意:
ThreadGroup:一个线程组中包含多个线程。
start0():本地方法,C++语言实现,间接实现的。
3.线程的状态与生命周期
(1)线程的状态
State:线程的状态是Thread类内部枚举
线程状态 | 导致状态发生条件 |
---|---|
NEW(新建) | 线程刚被创建,但是并未启动。还没调用start方法。 |
Runnable(可运行) | 线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。 |
Blocked(锁阻塞) | 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状 态;当该线程持有锁时,该线程将变成Runnable状态。 |
Waiting(无限等待) | 一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个 状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。 |
Timed Waiting(计时 等待) | 同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态 将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、 Object.wait。 |
Teminated(终止) | 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡 |
(2)生命周期:
1)新建:创建线程对象。就是刚使用new方法,new出来的线程;
2)就绪:有执行资格,没有执行权。就是调用的线程的start()方法后,这时候线程处于等待CPU分配资源阶段,谁先抢的CPU资源,谁开始执行。
3)运行:有执行资格,有执行权。当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能。
4)阻塞:没有执行资格,也没有执行权。
在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态;
比如sleep()、wait()之后线程就处于了阻塞状态,这个时候需要其他机制将处于阻塞状态的线程唤醒,比如sleep已达到指定的沉睡时间,或者调用notify或者notifyAll()方法。
唤醒的线程不会立刻执行run方法,它们要再次等待CPU分配资源进入运行状态。
5)销毁:线程对象成为垃圾,等待被回收。如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束,那么线程就要被销毁,释放资源。
4.多线程的安全问题
(1)线程安全:如果有多个线程在同时运行一段代码。程序每次运行结果和单线程运行的结果一样,而且其他变量的值也和预期的一样,就是线程安全的。
(2)线程安全问题:当多条语句在操作同一个线程共享数据时,一个线程对这多条语句只执行了一部分,还没有执行完;另一个线程参与进来执行。导致共享数据的错误,即多个线程执行的随机性,引起执行结果的不稳定。
(3)检验多线程安全问题的标准
1)查看当前环境是否是多线程环境(是)
2)当前程序中是否存在共享数据(门票 (存在))
3)是否有多条语句对共享数据进行操作(存在)
(4)解决方法:同步机制 (synchronized)
1)同步方法
2)同步代码块
3)Lock锁
5.同步机制
(1)同步
1)前提:多个线程
2)解决问题的时候要注意:多个线程使用的是同一个锁对象。
3)好处:同步的出现解决了多线程的安全问题。
4)弊端:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
(2)同步代码块
synchronized(锁对象){ //门的开/关
多条语句对共享数据进行操作
}
(3)同步方法
1)定义:如果一个方法进来是一个同步代码块,就将synchronized放在方法声明上。
2)格式
权限修饰符 synchronized 返回值类型 方法名(形式参数列表){
...
}
3)静态的同步方法:使用static修饰同步方法
格式
权限修饰符 static synchronized 返回值类型 方法名(形式参数列表){
...
}
(4)Lock
1)juc包下的接口:java.util.concurrent.locks.Lock
2)描述:Lock接口提供一些比synchronized更具体的锁定操作,更体现 面向对象。
3)实例化:Lock不能直接实例化,只能通过其具体的子实现类ReentrantLock创建对象。
4)功能:
public void lock():获取锁
public void unlock():试图释放锁
(5)死锁:加入同步之后,两个或者两个以上的线程在互相抢占资源的时候出 现一种互相等待的情况。
6.锁对象
(1)对象选择:
1)同步代码块:可以是任意Java类对象,多个线程应该使用的是同一把锁,即对象地址值一致。通常情况下选择此共享资源对象作为锁对象。
2)同步方法:
非静态的同步方法:this,当前这个所在类的对象的地址值引用。
静态的同步方法:当前类的字节码文件对象,当前类名.class。
(2)释放锁对象:
1)当线程的同步方法、同步代码块执行结束,就可以释放同步监视器 。
2)当线程在同步代码块、方法中遇到break、return终止代码的运行,也可释放 。
3)当线程在同步代码块、同步方法中遇到未处理的Error、Exception,导致该代码结束也可释放同步监视器 。
4)当线程在同步代码块、同步方法中,程序执行了同步监视器对象的wait方法,导致方法暂停,释放同步监视器。
(3)不释放锁对象:
1)当线程在执行同步代码块、同步方法时,程序调用了Thread.sleep()/Thread.yield()方法来暂停当前程序,当前程序不会释放同步监视器。
2)当线程在执行同步代码块、同步方法时,其他线程调用了该线程的suspend方法将该线程挂起,该线程不会释放同步监视器。注意尽量避免使用suspend、resume。
7.线程通信问题
(1)概念:多个线程针对同一个资源数据进行不同的操作。
(2)生产者消费者模型:
当生产者资源类所在的线程产生数据,需要等待消费者消费数据,生产者线程不知道消费者线程是否将数据使用完毕。
消费者资源类所在的线程使用数数,需要等待生产者生产数据,消费者线程不知道当前生产者线程是否产生数据了。
(3)解决:等待唤醒机制
8.等待唤醒机制
(1)描述:是多个线程间的一种协作机制。这多个线程之间共享一个资源,只是其各个线程的操作不同。当一个线程完成了其操作后,就进入了等待状态,等待其他线程执行完各自的操作之后再唤醒该线程。在有多个线程进行等待时,如果需要,可以使用 notifyAll()来唤醒所有的等待线程。
(2)等待唤醒中方法的作用
1)wait:线程不再活动,不再参与调度,进入wait set 中,因此不会 浪费CPU 资源,也不会去竞争锁了,这时的线程状态即是WAITING。它还要等着别的线程调用notify方法,将在这个锁对象上等待的线 程从wait set 中释放出来,重新进入到调度队列(ready queue)中 。
2)notify:则选取所通知对象的 wait set 中的一个线程释放;
3)notifyAll:则释放所通知对象的 wait set 上的全部线程。
(3)注意:
哪怕只通知了一个等待的线程,被通知线程也不能立即恢复执行,因为它当初中断的地方是在同步块内,而此刻它已经不持有锁,所以它需要再次尝试去获取锁(很可能面临其它线程的竞争),成功后才能在当初调用wait 方法之后的地方恢复执行。
(4)总结如下:
如果能获取锁,线程就从 WAITING 状态变成 RUNNABLE状态;
否则,从 wait set 出来,又调用wait和notify方法需要注意的细节
1)wait方法与notify方法必须要由同一个锁对象调用。
因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的 wait方法后的线程。
2)wait方法与notify方法是属于Object类的方法的。
因为:锁对象可以是任意对象,而任意对象的所属类都是继承了 Object类的。
3)wait方法与notify方法必须要在同步代码块或者是同步方法中使用。因为:必须要通过锁对象调用这2个方法进入 entry set,线程就从 WAITING 状态又变成 BLOCKED 状态。
9.sleep和wait的区别
(1)sleep方法没有释放锁,而wait方法释放了锁。
(2)sleep方法属于Thread类,wait方法属于Object类。
(3)sleep方法:可以让一个线程进入睡眠状态,等待一段时间之后,可以自动苏醒进入就绪状态。
wait方法:当锁对象调用该方法之后,其进入阻塞状态,只有通过多对象调用notify()方法或notifyll()方法,才能再次进入就绪状态。
四、设计原则与模式
1.设计原则
(1)分类:单一职责原则、开闭原则、里氏替换原则、依赖注入原则、接口分离原则、迪米特原则。
(2)单一职责原则:高内聚,低耦合。
每个类应该只有一个职责,对外只能提供一种功能,而引起类变化的原因应该只有一个。在设计模式中,所有的设计模式都遵循这一原则。
(3)开闭原则:对扩展开放,对修改关闭。对类的修改应该通过增加代码进行,而不应该修改现有代码。
(4)接口分离原则:不应该强迫程序依赖不需要使用的方法。一个接口不需要提供太多的行为,一个接口应该只提供一种对外的功能,不应该把所有的操作都封装到一 个接口中。
(5)依赖注入原则:要依赖于抽象,不要依赖于具体实现。在应用程序中,所有的类如果使用或依赖于其他的类,则应该依赖这些其他类的抽象类,而不是这些其他 类的具体类。
2.设计模式
(1)分类
创建型模式:对象的创建
结构型模式:对象的组成(结构)
行为型模式:对象的行为
(2)创建型模式:(6个)
简单工厂模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式、 单例模式。
(3)结构型模式:(7个)
外观模式、适配器模式、代理模式、装饰模式、桥接模式、组合模式、 享元模式。
(4)行为型模式:(10个)
模版方法模式、观察者模式、状态模式、职责链模式、命令模式、访问 者模式、策略模式、备忘录模式、迭代器模式、解释器模式。
3.创建型模式之简单工厂模式
(1)简单工厂模式,也称静态工厂方法模式,它定义一个具体的工厂类负责创建一些类的实例。
(2)过程:
1)需要提供一个工厂类:xxxFactory
2)构造方法是私有的,不能直接实例化
3)需要在工厂类中提供一个封装有具体类对象的创建方式的静态方法。
4)创建某个对象:具体类名 对象名=工厂类.静态方法名();
(3)优点:客户端不需要再负责对象的创建,从而明确了各个类的职责。
(4)缺点:这个静态工厂类负责所有对象的创建,如果有新的对象增加,或者 某些对象的创建方式不同,就需要不断的修改工厂类,不利于后期的维护。
4.创建型模式之工厂方法模式
(1)创建抽象类的子类对象,需要对应的工厂类实现对象的创建过程。
举例:
抽象的动物类:Animal
子类:Cat/Dog要有自己的工厂类
(2)优点:客户端不需要在负责对象的创建,从而明确了各个类的职责,如果有新的对象增加,只需要增加一个具体的类和具体的工厂类即可,不影响已有的代码,后期维护容易,增强了系统的扩展性。
(3)缺点:需要提供新增类的工厂类,代码量增加了。
5.创建型模式之单例模式
(1)单例模式:保证在内存中始终只有一个该类对象,该类对象的创建时自动创建。
(2)具体分为:饿汉式和懒汉式
(3)饿汉式:类一加载就创建对象。
1)优点:是不会出现任何安全问题的单例模式。
2)实现方式:
①创建一个类,这个类成员位置:定义静态实例变量:创建该类对象
②无参构造方法私有化
③提供对外的公共静态功能:获取该类对象
3)代表:
java.lang.Runtime:表示与Java应用程序相关的运行环境!
获取/执行相关的系统软件/查看系统变量…
(4)懒汉式:用的时候,才去创建对象
1)实现方式:创建一个类 。
①构造方法私有化。
②在成员位置,并不是一开始就直接创建该类实例。
③在当前类定义一个公共的静态功能,返回值是该类本身。
判断当前变量是否为空,
若为null,直接创建该类对象
返回该类实例
2)懒汉式:可能会出现问题的单例模式
①延迟加载
②线程的安全问题
③解决线程安全问题:使用同步代码块将多条对共享数据的操作包裹起来。
6.代理模式
(1)概念:让别人帮助我们完成一些事情。
(2)代理分类:
静态代理
动态代理:JDK动态代理、Cglib动态代理
(3)静态代理
1)真实角色只专注于自己的事情。
2)代理角色来完成真实角色完成不了的事情。
3)代理角色和真实角色都需要实现同一个接口。
4)举例:
Thread类与多线程的实现方式2
Tread类(代理角色):启动线程(完成自己的事情),系统资源启动
自定义类(真实角色):MyRunnable implements Runnable{}
代理角色重写run方法(),完成代理的业务...
创建自定义类对象
Thread t = new Thread(自定义类对象(资源类) ) ;
t.start()--jvm创建系统资源调用资源类的run方法
现实生活:结婚这个事情
1)我们自己操办整个结婚的过程
2)找婚庆公司操办
五、其他知识点
1.获取字节码文件的方式
Object类中的getClass()方法
任意Java类型的class属性
反射:Class.forName(“类的全限定名称”)…
2.数据库连接池
(1)后面: 每次连接数据库的时候—产生Connection:连接对象
(2)提供一些第三方:德鲁伊(druid:阿里的),cp30,dbcp
(3)当前创建一个连接池:固定的可重复利用的Connection对象,每次使 用完毕close(),将它归还到连接池中。
参数:最大连接数量10
最小连接数量
数据库连接对象查过最大连接数量的超时间
数据库的密码
用户名
连接url地址…
3.插入排序
(1)思想:从1角标对应的元素开始和前面的元素比较,如果本身值大,比 较结束;如果小于前面的值,将当前元素插入该元素前面;依次这样比 较,2,3,4…角标元素比较。
(2)核心代码:
for (int i = 1; i < arr.length; i++) {
for (int j = i; j >0; j--) {
if(arr[j]<arr[j-1]) {
arr[j]=arr[j]+arr[j-1];
arr[j-1]=arr[j]-arr[j-1];
arr[j]=arr[j]-arr[j-1];
}
}
}
4.反射机制
Java代码经历三个阶段
编译阶段(SOURCE) javac
CLASS阶段:xxx.class 反射来完成----Class类
运行阶段
总结
本文主要总结了javaSE的异常与线程部分的知识点,以及一些设计原则和设计模式的解释和使用。
从异常的概念、分类、处理方式、以及一些常见问题等方面总结了JavaSE异常部分的知识点。
从线程的含义与意义、java中创建线程的方式、线程中的常用类与方法、线程的使用与常见问题等方面总结了JavaSE线程部分的知识点。