深入理解java:异常、多线程和输入流输出
1 异常:不断抛出异常,直到有一对try..catch..捕捉到异常
清晰区分了业务代码(正常情况下)和异常代码
- 基本语法
try{
//<!-- 可能出异常的代码 -->
//之后的代码不在执行
}catch(<!-- 异常类型 e -->){
//<!-- 出异常时要执行的代码 -->
e.printStackTrace();//显示出异常的逐层调用
throw e;//在次抛出异常,多次捕获
//throw new Exception();抛出新异常
//throw new Exception("something",e);带上内部信息
}catch(/*<!-- 其他类型的异常 e -->*/){
}
- 要求明确的语法处理
//<!-- 函数throws自定义异常 -->
//<!-- 自定义异常类 -->
class OpenException extends Throwable{}
class ElseException extends Exception{}
//<!-- 抛出异常的函数 -->
void readFile() throws OpenException,其他类{
throw new OpenException();//想主动抛出函数得加throws
}
//<!-- 想要调用readFile()就得做好try..catch.. -->
main{
try{
readFile();
}catch(OpenException e){
}catch(ElseException e){
}
}
子类覆盖父类函数时:不能抛出比父类函数还多的异常(但可以不抛出或少)
原因:当使用向上造型构造子类时,编译器会以父类检查语法错误。
如果抛出了更多的异常,但是不用catch捕获(编译器通过),那么
在运行时会因为动态绑定而发生危险。
相似:向上造型时,调用覆盖父类的函数(jvm动态绑定),但不能调用
新增加的
子类函数(编译器认为语法错误)
构造函数throws exception:子类必须抛出父类构造所有的异常(可以更多)
原因:子类构造时一定会super(),所以就可能抛父类构造函数所有异常。
3. try-with-resource:语法糖,finally{.close()}
4. 断言:assert 表达式; 不成立时即抛出异常
main{
//:"sss",带上出错的信息输出
assert a==5:"计算错误";
}
- 测试驱动:JUnit框架
项目-New-JUnit testcase
RunAs JUnit.. - 程序调试:debug
2 多线程
1 基本概念
- 进程:一个活动程序及其相关资源表,进程独享了整个虚拟内存
- 线程:单个顺序的流控制,分享了CPU,共享内存。
- java从语言级别支持:object类中函数
- 线程体:重写run()来使线程执行特定内容
- 创建:
//extends class Thread,new a myThread as a thread
class myThread extends Thread{
@Override
public void run(){
//...
}
}
//implements interface Runable
class myTask implements Runnable{
public void run(){
//..
}
}
//use Runnable instance to construct a Thread
Thread thread=new Thread(new mytask());
thread.start();
//匿名类new Thread{...}
//lambda表达式创建Runnable实例
- 什么时候使用多线程:两个任务单元明显的并发
多线程是并发的处理,当一个程序需要同时做(同时画图)或者花
费大量等待(下载多个文件)时,可以为每个单元建立线程对象(lambda表达式
很方便)。
2 线程基本控制
- 线程状态:创建-start-调度(就绪,阻塞,运行)-中止
- 线程结束:默认线程执行完,无限循环时用控制变量break
- 设定优先级:setPriority()
- Daemon:setDaemon(true),随着普通退出而自动退出
3 同步:语言级别的控制,安全性+顺序性
- 对象互斥锁:每个对象上都有一个锁标记
//在线程的run里小心的使用临界资源
//得到该对象的锁,其他线程则得不到该锁了
synchronized(对象){
//临界区
}
//将同一个类实例当参数构造线程时,线程们使用同一个this
//需要该类的方法为同步方法
public synchronized void push(char c){}
- 释放锁,对象.wait();//先释放锁,接着阻塞线程
- 通知,对象.notifyAll();//通知其他线程可以从阻塞转就绪,重新开始活动
- 当前线程主动wait阻塞自己,让其他线程重启自己(只是就绪不一定能获得锁)
- 死锁:相互等待
4 并发API:简化了线程控制,线程安全的类:java.util.concurrent
- AtomicInteger:cnt.getAndIncrement();//线程安全的++cnt
- 集合:ArrayList/HashMap不安全,Vector及Hashtable是安全的
新增线程安全的类及它们的优化:
CopyOnWriteArrayList,CopyOnWriteArraySet:
ConcurrentHashMap:putIfAbsent(),remove(),replace()
ArrayBlockingQueue:put(),take()//线程安全且实现了waitnotify - 线程池:每一个线程任务都要new thread实例?
Executors:用有限的thread执行更多的任务(runnable)
class myTask implements Runnable{}
ExecutorService pool=Executors.newCachedThreadPool();
myTask t1 =new myTask();
myTask t2 =new myTask();
myTask t3 =new myTask();//runnable instance
pool.execute(t1);//execute(Runnable r)==thread.start();
pool.execute(t2);
pool.execute(t3);
pool.shutdown();//关闭线程池
5Timer:java.util.Timer,java.swing.Timer
- 底层线程实现:
Timer–Thread
TimerTask-Runnable
Timer.schedule(TimerTask,1000,1000)–Thread.start() - swing.timer:
timer=new Timer(1000,(e)->{setTitle(new java.util.Date().toString())});
- 在线程中更新界面draw()会带来不同步?
交给主线程统一处理,SwingUitlites.invokeLater(lambda);
6stream:对容器collection函数式风格的操作
//并行处理的底层实现
//链式编写:函数式风格filter sorted limit map,执行后还是流
//末端操作:max min count forEach findAny,执行后不再是流
List<Integer> a=new List<>;
//Arrays.stream(a)//数组流化
//a.stream()//普通流
a.parallelStream()//并行流
.filter(i->i>20)
.map(i->i*i)
sorted()
.distinct()
.limit(10)
.max();
3 流,不同类型的输入输出的抽象,所有对流的操作都要try..catch
1 字节流:只能按字节(二进制)处理,标准流/文件流
//标准输入字节流InputStream
byte[] buffer= new byte[1024];
//标准输入流读字节到buffer.按字节计算长度
int len=System.in.read(buffer);
//注意String是按照字符数计算长度
String =new String(buffer,0,len);
//read();
//mark();
//reset();
//文件字节流,内存字节内容-->二进制文件
FileOutputStream out =new FileOutputStream("a.dat");
out.write(buffer);
2 过滤器:包装字节流对象,使之能够处理基本数据类型
//写
//基本类型内容-->二进制文件
DataOutputStream out =new DataOutputStream(
new BufferOutputStream(
new FileOutputStream("a.dat")));
int i=12313;
out.writeInt(i);//输出文件仍是i的16进制形式
//读
//二进制文件-->基本类型
DataInputStream in =new DataInputStream(
new BufferInputStream(
new FileOutInStream("a.dat")));
int j=in.readInt();
3 文本数据的字符流:Reader/Writer
read()是阻塞的,所以读网页(等待时间很长时)用线程方式并发
//可适应不同编码
//写
//内存-->字符流-->文本文件
PrintWriter out = new PrintWriter(
new BufferedWriter(
new OutPutStreamWriter(
new FileOutputStream("a.txt"))));
int i=123321;
out.println(i);//文件里就是123321
//读
//文本文件-->字符流 -->内存
BufferedReader=new BufferedReader(
new InPutStreamReader(//字节流转成reader,指定解码方式
new FileInputStream("a.txt"),"utf8")));
String line;
while((line=in.readLine()!=null)){
System.out.println(line);
//getLineNumber();
}
//格式化输出
//format()
System.out.printf()
//在reader或者InputStream上建立Scanner,方便读入数据
Scanner in= new Scanner(System.in);
in.nextInt();
4 总结:
二进制读写->字节流
字符的读写—> 文本处理->Reader包装
|-> 数据处理->Scanner包装
5 对象串行化
//Serializable只是个接口标记,什么都不用实现
class student implements Serializable{}
main{
student s1=new student();
//输出文件里是二进制
ObjectOutputStream out =new ObjectOutputStream(
new FileOutputStream("obj.dat")) ;
out.writeObject(s1)
out.close();
ObjectInputStream in =new ObjectInputStream(
new FileInputStream("obj.dat"));
student s2=(student) in.readObject();
in.close();
//s2是新创建的对象(s1,s2引用的实体不同,内容相同)
}
6 举个例子:背单词(jase项目)
- 界面:JFrame,init()放置标签资源,start()开始执行
- 读:线程,Reader读文件到List使用字符串处理函数处理文本
- 容器:存储内容
- 显示:Timer控制显示到标签