我的学习阶段是跟着CZBK黑马的双源课程,学习目标以及博客是为了审查自己的学习情况,毕竟看一遍,敲一遍,和自己归纳总结一遍有着很大的区别,在此期间我会参杂Java疯狂讲义(第四版)里面的内容。
前言:此随笔主要是Java基础中的基础,相信大家对这方面肯定有着自己的理解和认识,具体详解可以参照万能的baidu,有的我就一笔带过,希望在我的学习之路上能够有大牛进行指导,也有更多的小伙伴共勉。
1.异常
异常在我们平时编译的时候会有两种情况,一种是编译报错,一种是运行报错,还有一种较为严重的就是直接error;
异常的根类是java.lang.Throwable
而我上面提到的两种异常是他的子类
error:是指严重的不可进行补救的;
Exception:是指编译(checked)上或者是运行(runtime)后出现问题通常可以通过debug进行断点检测来看问题出现在哪里。
这里异常有几种常用的方法,我认为这个方法应该是在运行过程中出现问题日志记录来接收问题的。
·printStackTrace打印详细的错误信息
·getMessage 获取发生异常的原因
·toString 获取异常的类型和异常描述信息(一般不用)
而出现异常的话一般会在控制台上显示错误信息,这时便可以通过查看API文档来查看错误出现问题原因,以及如何避免和解决。
这里就会学到两种异常的抛出一种是Throw 一种是Try{}catch{}
这里的Throw一般是抛出系统的异常,但是不申明不捕获,用在方法内来申明一个异常;
而Throws是申明异常,写在类名后跟着抛出异常的类名;
一般常用的就是Try...cathc来抛出异常,这里如果有异常的话,可以在catch 中捕捉到并且对异常进行申明;但是一旦异常出现后面的代码就不进行继续运行了,这里就要引出一个finally就是无论是否有异常,finally里的代码一定会执行,这对于启用很多物理内存网络空间等等如果仅开启由于中间的错误导致没有关闭是非常的不好,这时finally代码块里面的内容则起到了作用,这里要注意尽量避免在finally代码块中写入return语句,这样无论怎么执行都会返回finally代码块中的内容也是不好的。
异常的注意事项:
·多异常用try。catch捕获,可以多个异常分别捕获分别处理,也可以多个异常一次捕获一次处理,也可以一次捕获多次处理;
·运行时异常被抛出可以不处理;
·如果父类抛出多个异常的话,子类重写父类方法也要抛出多个异常;
·如果父类没有抛出异常的话,子类重写父类的方法可以不抛出异常,但是要进行异常捕获处理。
到此异常的内容就基本了解了。下面是重头戏线程;
2.线程
线程这里有同步问题和线程池问题,下面先说一下线程是什么逐个攻破v把;
在此之前我们要了解并发和并行的区别是什么;
并发就是在一段时间内,多个事情间隔去做,
并行是在一段时间内,多个事情同时去做;
像我们所用的手机为社么可以又听歌又玩游戏,这里就是多进程多线程了,进程就好比我们运行的一个软件,而线程就可以当作是这个软件的多个功能,为什么可以边听歌边找歌曲呢/?这里就是因为这一个进程里面有多个线程,而不同的线程之间的相互调配来让我们能进行边听歌边找歌曲这个操作。而像我们买电脑的时候cpu就会显示多少核,多闲线程,像之前的单核电脑多线程,那就是一个人同时做多个事情,现在的多核心多线程就是多个人分工去做多个事情,效率更高更快,总而言之一个程序至少有一个进程,而一个进程至少有一个线程。下面说一下线程的入门了;
线程这里有几个专有名词,分时调度,抢占式调度,前者是评价分配,而后者则是通过设置优先级来优先获得线程的使用权。
线程的使用刚开始是通过创建线程对象,然后再通过对象使用开启新的线程:
这里线程要执行的内容是在run()内进行执行的,成为线程执行体;
创建了Thread实例即创建了线程对象,
通过start方法对线程对象打开新线程。
这里涉及到Thread类的构造方法,
这里我写一个public Thread(Runnable target,String name)分配一个带有制定目标新的线程对象并指定名字,这里就提到了Runnable接口(后文会写到)
这里的常用方法:
·getName:获取当前线程的名称
·start:java虚拟机调用此线程方法
·run:线程在执行任务的时候要执行 代码
·sleep:在执行线程时暂停指定秒数
·currentThread:返回对当前正在执行的线程对象的引用
创建线程的方式有两种第一种就如上继承Thread类方式,第二种是实现Runnable接口的方式,这里使用Runnable接口只需要重写里面的run方法即可
实现了Runnable接口,使得该类有了多线程的特征。Thread实际上也是实现了Runnable接口。
那么这两种创建方式有什么区别呢/
第一类继承Thread不适合资源共享,而实现了Runnable接口的话,则容易实现资源共享。
适合多个相同的程序代码的线程去共享一个资源。
可以避免java但继承的局限性。
增加程序的健壮性,大妈可以被多个线程共享,代码和线程独立。
线程池只能放入实现的Runnable接或者Callable类线程,不能直接放入继承类的Thread。
3.线程安全
线程安全问题都是由静态变量及全局变量引起的,这时就要考虑线程同步问题。
synchronized 和lock 加锁解决线程同步的问题
1.同步代码块
2.同步方法
3.锁机制
这里要注意的是多个线程同步必须要使用同一把锁。
lock相比与synchronized来说 同步代码块和同步方法锁定操作这样的功能都有,而且对于lock来说更加趋向于面向对象过程,加锁和解锁方式方法化了。
4.线程状态
总共有六种状态,包括创建新的线程,使用线程,关闭线程,又需要了解Bolcked(阻塞状态),Waiting(等待状态),TimedWaiting(即使等待状态)
TimedWaiting时间等待一般使用sleep方法传入一个参数等待的时间,等时间到了之后才回去使用线程;
Blocked阻塞状态在学习了锁之后能更好的理解,就是一个正在阻塞等待一个在锁对象的线程。
Waiting无限等待这里的要想这个线程被使用就需要被唤醒来达到停止等待的状态这里就会调用notify和notifyAll方法。来唤醒等待的线程。
5.等待唤醒机制
多个线程在处理同一个资源,但是处理的动作却不相同。
线程之间的通信,多个线程并发执行是,在默认状态下CPU是随机切换线程的,当我们需要多个线程完成同一件事情时,那就我们就需要线程之间的通讯来相互协调,更加有效的利用资源,这是就需要等待唤醒机制。
谈到线程我们一般想到的都是竞争,但是如果协调处理一个问题的时候等待唤醒机制是一个不错的选择。但是要注意的是等待唤醒机制中等待状态在唤醒的时候,并不能直接立即进入使用状态,而是在唤醒之后,需要去唤醒锁,因为实在同步块中,唤醒是需要获取锁,从而从Waiting状态变成Runnable状态。
6.线程池
线程池顾名思义就是在一个容器中放入多个线程方便使用。
优点:
1.降低资源损耗,因为线程一旦完成所要做的任务后就需要销毁线程,一个线程的创建和销毁极大的浪费了时间。
2.提高响应速度,在线程使用阶段就可以直接使用,省去了创建时间,可以立即执行。
3.提高线程的可管理性,可以根据系统的承受能力,调整线程池中工作线程的树木,防止因为消耗过多的内存而导致消耗过大内存死机。
线程池的使用:
在Executorzs类中有创建线程池的方法:
ExecutorService newFixedThreadPool,参数传入线程池内的线程数量
获取一个ExecutorService对思想获取线程池的某个线程并执行,
执行步骤:
1.创建线程池对象
2.创建Runnable接口的子类对象task
3.提交Runnable接口子类对象
4.关闭线程池(一般不做)
7.Lambda表达式
这里的Lambda表达式是为了更加简化代码的书写更偏向于如何去做,而不是面向对象面向过程,就分析线程来说,如果要创建一个对象必须要先实现Runnable接口(线程池在今后用的多,所以第一种Thread继承方法创建就了解即可了),然后又要在实现类中创建一个对象来使用Runnable接口内的run方法,而这里的run方法才是方法的核心,才是要线程要去做的,为了减少书写可以用匿名内部类来覆盖重写run方法,所以方法名称,参数,返回值又不得不写一遍,这里就可以用Lambda表达式来简化书写使用真正的run线程方法:
new Thread()->System.out.println("start").start();
这段代码和线程使用的代码是一样的,不再有不得不创建接口对象的束缚,也不再有抽象方法覆盖重写的负担。
Lambda的格式:
(参数类型 参数名称)->{代码语句}
匿名内部类可以帮我们省去实现类定义的操作,但是匿名内部类实在是太复杂了
new Thread(new Runnable(){
@Override
public void run(){
s方法;
}
});
这样我们就可以使用Lambda表达式来进行简化书写。上面所写的Lambda表达式是最简书写格式建议在后面熟悉掌握的情况下再去进行简化书写。