您的位置:首页 > 编程语言 > Java开发

Java基础(十一)★流★线程

2018-04-02 10:06 176 查看

IO
流动的是数据。---用于传输数据的一套API---IO-> Input Output---输入输出流---当数据是从外部流向程序的时候,输入流;数据是从程序流向外部的时候,输出流。读取文件---将数据从文件读到程序中---输入流;向文件中写入数据---数据从程序流向了文件---输出流。
根据数据的传输方向:输入流、输出流
根据数据的传输形式:字节流、字符流
 输入流输出流
字节流InputStreamOutputStream
字符流ReaderWriter
这四个基本流都是抽象类。
数据的来源(目的地):硬盘,网络,输入设备,内存
向TXT文件写入一个字符串---输出流,字符流,和文件有关的流---FileWriter
FileWriter(String path)---创建一个新的文件。如果文件不存在,会创建一个新文件。

流中的异常处理

1. 将流对象放在try外定义并且赋值为null,放在try中初始化
2. 在关流之前需要判断流对象是否初始化成功
3. 需要将流强制置为null以防关流失败无法释放文件
4. 需要在写完数据之后手动添加flush操作以防关流失败产生数据丢失
读取一个TXT文件---输入流 字符流 和文件有关的流---FileReader
练习:复制文件

练习:剪切文件
注意:Java中的原生的流对office组件支持的不好---POI

缓冲流

BufferedReader---需要传入字符输入流,真正读取数据的是传入的这个字符输入流,缓冲流仅仅提供了一个缓冲区。
---从字符输入流中读取数据,提供一个缓冲区,允许进行按行读取。
BufferedWriter---newLine()---写入一个换行---产生新的一行
\r\n \n
writer.write(“abc”);
writer.newLine(); -> \r\n \n
练习:统计Java代码的行数

StringReader

读取字符串的流。

字节流

以字节形式读取数据---没有缓冲区
练习:用字节流来复制一个文件---统计时间

转换流

InputStreamReader---转换输入流---将字节流转化为字符流---底层是以字节流形式读取,但是表层是以字符流形式操作
OutputStreamWriter---转换输出流---将字符流转化为字节流---表层是以字符流形式写出,底层是以字节流形式来传输
练习:转换文件编码

系统流/标准流

都是字节流
System.in 标准输入流
System.out 标准输出流
System.err 标准错误流
练习:从控制台获取一行数据
BufferedReader ---转换--- System.in

打印流

PrintStream/PrintWriter---传入流之后向指定的位置打入数据
合并流
序列化、反序列化流
Properties
静态导入
枚举
可变参数

合并流

SequenceInputStream---字节流---可以合并多个字节输入流,将这多个字节输入流进行合并的时候,需要将这个多个流放到一个Vector集合,利用Vector集合产生一个Enumeration对象,利用Enumeration对象来构造合并流对象---合并流只有输入没有输出

---只有输入流没有输出流---如果要合并多个流,需要将这些流放到一个Vector对象中,然后利用Vector产生Enumeration对象,然后利用Enumeration对象来构建一个合并流对象

序列化/反序列化流

序列化:将对象进行完整存储---持久化
反序列化:将对象还原回来
注意:
1. 如果一个对象要想被序列化,这个对象对应的类必须实现一个接口---Serializable---这个接口中没有任何的方法和属性,仅仅用来标识这个类产生的对象可以被序列化
2. 被static/transient修饰的属性不会被序列化
3. 每一个类在序列化的时候都会有一个版本号。如果没有手动指定版本号,那么在编译的时候会根据当前类中的方法和属性自动计算一个版本号。对象在反序列化的时候会比较对象中的版本号和当前类中的版本号是否一致。如果手动指定了版本号,就不再自动计算。---版本号serialVersionUID---默认用private static final long修饰
4. 集合/数组不能被序列化
盒子---锁---钥匙---产生器

Properties

是一个可以持久化的映射。键和值的类名默认为String,而且只能是String.---Properties对象必须存储到properties文件中。properties文件中不能存储中文,一旦输入中文,就会变成对应的编码---properties文件的默认编码格式为ISO-8859-1
JDK5的特性
自动封箱拆箱 泛型 增强for循环 静态导入 可变参数 枚举 反射 注解 动态代理 内省

静态导入

import static 包名.类名.方法名; ---只能够导入静态方法

可变参数

是指参数个数不做限定。用 ... 定义,本质上是一个数组。可变参数在使用的时候可以不传值也可以传入一个或者多个值。

枚举

适用于一些取值相对固定并且能够一一列举的场景(星期、月份、季节)。用enum来定义一个枚举类。枚举常量必须定义在首行,而且之间用 , 隔开。
枚举类中允许定义一切的方法和属性。---可以定义抽象方法
所有的枚举类的顶级父类---java.lang.Enum
switch---需要一个表达式,表达式的值是byte/short/char/int, JDK1.7允许使用String,JDK1.5开始支持枚举常量

在取值相对固定且能够一一列举的情况下,建议使用枚举类。
通过enum来定义一个枚举类。枚举常量必须放在枚举类的首行。枚举类中的构造函数只能是私有的。枚举类中允许定义任何的方法和属性,包括抽象方法。
Java中枚举的顶级父类是java.lang.Enum---JDK1.5的特性之一
从JDK1.5开始,枚举常量支持switch-case

Junit测试

单元测试。需要导入Junit库,在要测试的方法之上添加@Test---要求测试的方法无参无返回值非静态

Debug调试

断点调试。
F5---钻入方法中,查看方法的执行
F6---执行到下一行
F7---返回上一步
F8 ---执行到下一个断点
 

线程
进程---计算机在执行的任务或者逻辑---服务(没有界面的进程)
线程---进程中任务的一个小任务---QQ,下载软件,JVM
记事本是单进程多线程

自定义线程

1. 继承 Thread类,重写其中的run方法,将线程要执行的逻辑写到run方法。通过线程对象身上的start方法来启动线程
2. 实现Runnable接口,重写的run方法。通过Thread对象来启动这个线程
3. 实现Callable<T>接口,重写的call方法。---现阶段要求知道即可
线程的执行并不是有序的,是相互抢占,而且抢占并不是只在线程执行的开始而是发生在线程执行的每一步过程中---由于多个线程之间相互抢占资源导致出现了不符合常理的情况,称之为多线程的并发安全问题
 


同步代码块---将可能出现问题的代码放到了synchronized中,需要一个锁对象---必须是所有线程都认识的资源---共享资源,类的字节码,this
同步方法---synchronized修饰方法---锁对象是this
死锁---产生的原因:多个线程,共享资源过多,锁对象不统一,锁的嵌套---避免死锁:统一锁对象,减少锁的嵌套
同步/异步---如果一个对象在某个时间段内只允许一个线程操作--同步
同步一定是安全的,不安全一定是异步的。
HashMap---异步式线程不安全
Hashtable---同步式线程安全
ConcurrentHashMap---异步式线程安全
通过等待唤醒机制调节线程之间的执行顺序---线程之间的相互通信
线程在等待期间是位于线程池中的。---线程池本质上是一个存储线程的队列
a1,a2,c1,c2() -> a1 running -> a1,a2,c1,c2() -> a1 running -> a2,c1,c2(a1) -> a2 running -> c1,c2(a1,a2) -> c1 running -> a1,c1,c2(a2) -> c1 running -> a1,c2(a2,c1) -> c2 running -> a1(a2,c1,c2) -> a1 running -> a1,a2(c1,c2) -> a1 running -> a2(c1,c2,a1) -> a2 running -> (c1,c2,a1,a2)
总结:sleep和wait有什么区别?
sleep方法需要指定睡眠时间,到点自然醒。释放执行权,不释放锁。被设计在Thread类上,是一个静态方法
wait方法可以指定等待时间也可以不指定,如果不指定时间需要唤醒。释放执行权,释放锁。被设计在了Object类上,是一个普通的方法、wait方法必须结合锁来使用。

守护线程

守护其他线程的执行。当被守护的线程结束之后,守护线程无论完成与否都会随之结束。
只要代码中出现了守护线程,要么这个线程是守护线程要么就是被守护的线程---如果出现了多个被守护的线程,那么以最后一个被守护的线程作为结束标志。
 

线程的状态

 


守护线程

守护其他线程的执行。当被守护的线程结束之后,守护线程无论完成与否都会随之结束。
只要代码中出现了守护线程,要么这个线程是守护线程要么就是被守护的线程---如果出现了多个被守护的线程,那么以最后一个被守护的线程作为结束标
b76e
志。

线程的优先级

线程有1-10这10个优先级。优先级越高,理论上线程抢到资源的概率越大。但是相邻两个优先级之间几乎看不出差别,至少相差5个优先级才会略有差别。
如果没有设置优先级,默认优先级为5

单例模式

保证全局过程中只存在一个唯一实例的模式
饿汉式相对懒汉式来说耗费内存。懒汉式相对饿汉式而言,存在线程安全问题。

练习:生产消费模型
一个线程表示生产者,另一个线程表示消费者。商品数量总数不超过1000
0 300 140 160
160 400 380 180
180 820 0 1000
1000 0 630 370

套接字

实际上是一套用于网络通信的API---本质上是一套基于网络传输数据的流
IP地址---IPv4---由四组数组成的IP地址,每组数的范围在0~255之间---IPv6
端口---用于和外界进行信息交互的媒介--- 0~65535---其中0~1024已经被计算机内部以及一些常用应用占用
news.baidu.com --- 域名
DNS服务器---将域名解析为对应的IP地址
网络模型
七层
物理层 数据链路层 网络层---IP  传输层---UDP/TCP 会话层 应用层 表示层---http,ftp,POP3,SMTP,TELNET...

UDP

基于流的。不需要建立连接,不可靠。传输速度比较快,会对数据进行封包,每个包不超过64k。适用与一些要求速度而不要求可靠性的场景。
发送端:
1. 创建套接字对象
2. 准备数据包,绑定要发送的地址
3. 发送数据
4. 关流
接收端:
1. 创建套接字对象,绑定端口号
2. 准备数据包
3. 接收数据
4. 关流
5. 将数据从数据包解析出来
练习:单人聊天
255.255.255.255---广播地址---只要是同一个网段内的计算机都可以收到这个数据

TCP

基于流的。建立连接,经过三次握手,可靠。传输速度相对较慢,理论上不限制数据大小。适用于要求可靠性而不要求速度的场景。
客户端:
1. 创建客户端套接字对象
2. 发起连接,绑定连接地址
3. 获取输出流,写出数据
4. 通知服务器端数据写出完毕
5. 关流
服务器端:
1. 创建服务器端套接字对象,绑定端口号
2. 接受连接,获取一个Socket对象
3. 获取输入流,读取数据
4. 通知客户端数据已经读取完毕
5. 关流
注意:receive/connect/accept/read/write都会产生阻塞
扩展:BIO---BlockingIO---同步式阻塞式IO
NIO---NewIO---NonBlockingIO---同步式非阻塞式IO---JDK1.4
AIO---AsynchronousIO---异步式非阻塞式IO---JDK1.8
练习:上传文件
反射

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  线程