CoreJava Day14
14.1 多线程的通信
■ 因为线程的死锁,从而引发要多线程的通信
死锁:每个线程不释放自己拥有的资源,却申请别的线程拥有的资源,会造成死锁问题
■ 线程间的通信:等待通知机制
■ 每一个对象都有一个等待队列。
线程t1对O调用wait方法,△必须是在对O加锁的同步代码块中。
结果:1、线程t1会释放它拥有的所有的锁标记
2、会进入O的等待队列,开始阻塞
线程t2对o调用notify/notifyAll方法时,也必须是在对o加锁的同步代码块中。结果:会从o的等待队列中释放一个/全部线程
■
▲ 初始状态 ▲阻塞状态 ▲终止状态
\ / ┍ 1 ┓
\ / \ 2sleep /
\start / \ 3join /stop
\ / \ /
┙ ┕ \ /
▲ 可运行状态 _ _ _ OS选中 _ _ _\ ▲运行状态
(只缺CPU) \ CPU到期或调用yield
┍ / \
\ / \wait
\ Synchronized/ \
\ / \
\ / \
\ / \
\ ┕ ┙
▲ 锁池状态 <------ ▲等待队列
notify/notifyall
练习
1、利用线程的通信机制,用两个线程打印以下的结果:
1 2 A 3 4 B 5 6 C 7 8 D ... 49 50 Y 51 52 Z
2、思考为什么调用wait()方法之前用while判断,而不用if判断
currentThread
public static Thread currentThread()返回对当前正在执行的线程对象的引用。
14.2 IO流
14.2.1 java.io Class File
■ 一个File对象代表了一个文件或目录
File f=new File("1.txt");//在堆里申请了个File对象的空间
f.createNewFile();//创建了个文件对象,不会产生文件,只有操作File对去创建文件
f.delete();
f.mkdir();
System.out.println(f.getName());//相对路径
System.out.println(f.getAbsolutePath());//绝对路径
■ File[] listFiles(FileFilter filter)
返回表示此抽象路径名所表示目录中的文件和目录的抽象路径名数组,这些路径名满足特定过滤器。
java.io
接口 FileFilter
accept
boolean accept(File pathname)
File pathname是指被遍历目录中所有文件和目录
作业:打印目录下所有的.java文件,包括子目录(提示:递归)
14.2.2
■ io流是用于JVM和数据源之间交换数据
| | | |
| | | |
| | | |
| --------------- DB |
| JVM 流 net |
| --------------- file|
| | | |
| | | |
■ 一个流也是个对象
流的分类:输入流、输出流
字节流、字符流
节点流、过滤流 (功能,过滤流是给其它增加个功能,本身不传输数据)
■ 装饰模式
武器
/ \
/ \
枪(节点流) 零件
/|\ \
/ | \ \ \ \
瞄 消 M P S L
(过滤流)
14.2.3.1 字节输入流
java.io
类 InputStream
字节输入流的所有类的超类
■ java.io
类 FileInputStream
public FileInputStream(String name)
throws FileNotFoundException//文件不存在会抛异常
FileInputStream(File file)
FileInputStream(FileDescriptor fdObj)
■ FileInputStream中方法介绍
void close()
关闭此文件输入流并释放与此流有关的所有系统资源
int read()
从此输入流中读取一个数据字节。
返回下一个数据字节;如果已到达文件末尾,则返回 -1。
int read(byte[] b)
从此输入流中将最多 b.length 个字节的数据读入一个字节数组中。
返回:读入缓冲区的字节总数,如果因为已经到达文件末尾而没有更多的数据,则返回 -1。
int read(byte[] b, int off, int len)
从此输入流中将最多 len 个字节的数据读入一个字节数组中。
CoreJava Day15
15
15.1.1 DataInputStream/DataOutStream增加读写
关流只关最外层的过滤流就行了\
15.1.2 BufferedInputStream/BufferedOutputStream增加缓冲区
void flush()刷新此缓冲的输出流。
15.1.3 管道流(和UnixC++中的FIFO相同)
■ PipedInputStream和PipedOutputStream (字节流)
这两个是节点流,注意,用来在线程间通信。
所有输入流读方法都是阻塞的
PipedOutputStream pos=new PipedOutputStream();
PipedInputStream pis=new PipedInputStream();
try
{
pos.connect(pis);
new Producer(pos).start();//线程类对象,在构造时,使用管道流通信
new Consumer(pis).start();//线程类对象,在构造时,使用管道流通信
}
catch(Exception e)
{
e.printStackTrace();
}
15.1.4 随机存取文件
RondomAccessFile类允许随机访问文件,这个类也是支持直接输出输入各种数据类型。
GetFilepoint()可以知道文件中的指针位置,使用seek()定位。
Mode(“r”:随机读;”w”:随机写;”rw”:随机读写)
1) 实现了二个接口:DataInput和DataOutput;
2) 只要文件能打开就能读写;
3) 通过文件指针能读写文件指定位置;
4) 可以访问在DataInputStream和DataOutputStream中所有的read()和write()操作;
5) 在文件中移动方法:
a. long getFilePointer(): 返回文件指针的当前位置。
b. void seek(long pos): 设置文件指针到给定的绝对位置。
c. long length(): 返回文件的长度。
15.2 字符流
■ 字符流可以解决编程中字符的编码问题。从字符到整数,对字符集和整数集建立一一对应的关系,就算叫做编码,从整数映射到字符,就叫做解码。
■ 编码问题:
字节流的字符编码:
字符编码把字符转换成数字存储到计算机中,按ASCii将字母映射为整数。
把数字从计算机转换成相应的字符的过程称为解码。
编码的方式:
每个字符对应一个整数。不同的国家有不同的编码,
当编码方式和解码方式不统一时,产生乱码。因为美国最早发展软件,所以每种的编码都向上兼容ASCII 所以英文没有乱码。
ASCII(数字、英文)1个字符占一个字节(所有的编码集都兼容ASCII)
ISO8859-1(欧洲) 1个字符占一个字节
GB-2312/GBK 1个字符占两个字节
Unicode 1个字符占两个字节(网络传输速度慢)
UTF-8 变长字节,对于英文一个字节,对于汉字两个或三个字节。
■ InputStreamReader和OutputStreamWriter(字节流转化成字符流的桥转换器)
这两个类不是用于直接输入输出的,他是将字节流转换成字符流的桥转换器,并可以指定编解码方式。
Reader和Writer (字符流类,所有字符流的父类型)
1) Java技术使用Unicode来表示字符串和字符,而且提供16位版本的流,以便用类似的方法处理字符。
2) InputStreamReader和OutputStreamWriter作为字节流与字符流中的接口。
3) 如果构造了一个连接到流的Reader和Writer,转换规则会在使用缺省平台所定义的字节编码和Unicode之间切换。
BufferedReader/(BufferedWriter,不常用)(这两个类需要桥转换)
PrintWriter(带缓存的字符输出流,不需要桥转换)
常用输入输出类型,不需要桥接,其中其它方法请参看API文档。
以上两个都是过滤流,需要用其他的节点流来作参数构造对象。
BufferedReader的方法:readLine():String ,当他的返回值是null时,就表示读取完毕了。要注意,再写入时要注意写换行符,否则会出现阻塞。
BufferedWriter的方法:newLine() ,这个方法会写出一个换行符。
PrintWriter的方法:println(….String,Object等等)和write(),println(...)这个方法就不必再写换行符了,在使用时会自动换行。
注意:在使用带有缓冲区的流时,在输入之后就要flush()方法,把缓冲区数据发出去。
原则:保证编解码方式的统一,才能不至于出现错误。
java.io包的InputStreamread输入流的从字节流到字符流的桥转换类。这个类可以设定字符转换方式。
OutputStreamred:输出流的字节流桥转换成字符流
Bufferread有readline()使得字符输入更加方便。
在I/O流中,所有输入方法都是阻塞方法。
Bufferwrite给输出字符加缓冲,因为它的方法很少,所以使用父类PrintWrite,它可以使用字节流对象,而且方法很多。
15.3 StringTokenizer
java.util
类 StringTokenizer
15.4 对象序列化
■ 把对象放在IO流上
ObjectInputStream和ObjectOutputStream(对象流)
对象流是过滤流,需要节点流作参数来构造对象。用于直接把对象写入文件和从文件读取对象。
只有实现了Serializable接口的类型的对象才可以被读写,Serializable接口是个标记接口,其中没有定义方法。对象会序列化成一个二进制代码。
writeObject(o),readObject()这两个是对象读写操作时用的方法。
Object o = new Object();
FileOutputStream fos=new FileOutputStream("Object.txt");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(o);
oos.close();
FileInputStream fis =new FileInputStream(“Object.txt”);
ObjectInputStream ois =new ObjectInputStream(fis);
Object o = (Object)Ois.readObject();
ois.close();
对象流读取结束返回 EOFException异常对象。
一个类中有其他类型的对象,那么,这个类实现了Serializable接口,在对象序列化时,也同样要求这个类中属性都能够对象序列化(基本类型除外)。
注意:对于对象流的操作,在写对象时要一次写入完毕,如果使用追加模式写入,只会读取到上一次写入的对象,使用对象流写入时,会先写入一个头部,然后写入数据,
最后加上结束符号,如果使用追加方式写入的话,那就会在结束符号继续向下写入,但是在读取时只会读到结束符为止,以后再次写入的数据就会丢失。
注意:在使用对象流写入对象时要一次向文件写入,不能够采用追加方式。
serialver命令判断是否一个属性或对象可序列化,
serialver TestObject(TestObject必须为已经编译,也就是.class)
执行结果:如果不可序列化;则出现不可序列化的提示。如果可以序列化,那么就会出现序列化的ID:UID。
Externalizable这是Serializable的子接口,他可以让用户自定义如何序列化对象。
readExternal(ObjectInput in),writeExternal(ObjectOutput out)这是这个接口中的两个方法,通过这两个方法可以定制序列化过程。这个方法不安全,可以调用以上两个方法改变对象的状态。
transient只能用来修饰属性。表示这个属性在对象序列化时将被忽略。
transient int num;
表示当我们对属性序列化时忽略这个属性(即忽略不使之持久化)。所有属性必须都是可序列化的,特别是当有些属性本身也是对象的时候,要尤其注意这一点。
java.util.StringTokenizer类,这个类是用于字符串截取的。
StringTokenizer(参数1,参数2)按某种符号隔开文件
StringTokenizer(s,”:”) 用“:”隔开字符,s为对象。
补充:字节流结束返回-1,字符流结束返回null,对象流结束返回 EOFException
引申---------〉异常经常被用在流程控制, 异常也是方法的一种返回形式。
作业:
用字符流把文件中的唐诗排列成古文的格式
用对象序列化把若干Employee对象写到文件中,再读取出来
项目练习:
修改Bank类,账户信息会采用对象序列化的方式存放在文件中.当Bank对象生成的时候会读取文件,设置账户集合.当账户信息改变的时候,会随时更新文件
设计一个FileDAO类(文件数据访问对象),负责对文件的访问,包括存放账户,提取账户等方法,在Bank类中,会通过FileDAO对象来访问文件
注意:如果已有的账户对象会存在文件中,那么为新的账户对象分配id的做法也应相应的改变,过去的用static属性的做法不再合适,应该改为,把下一个可用的id存放在一个文件中,
每创建一个新对象的时候都会读取这个文件,获得新对象的id,并且修改文件中的id,使其加1.这个工作可以放在Account类的构造方法中
网络编程
网络基础知识
网络编程的目的就是指直接或间接地通过网络协议与其他计算机进行通讯。
计算机网络形式多样,内容繁杂。网络上的计算机要互相通信,必须遵循一定的协议。目前使用最广泛的网络协议是Internet上所使用的TCP/IP协议。
IP地址:计算机在网络中唯一标识,相对于internet,IP为逻辑地址。
IP地址分类:
A类地址
A类地址第1字节为网络地址,其它3个字节为主机地址。另外第1个字节的最高位固定为。
A类地址范围:1.0.0.1到126.155.255.254。
A类地址中的私有地址和保留地址:
10.0.0.0到10.255.255.255是私有地址(所谓的私有地址就是在互联网上不使用,而被用在局域网络中的地址)。
127.0.0.0到127.255.255.255是保留地址,用做循环测试用的。
B类地址
B类地址第1字节和第2字节为网络地址,其它2个字节为主机地址。另外第1个字节的前两位固定为10。
B类地址范围:128.0.0.1到191.255.255.254。
B类地址的私有地址和保留地址
172.16.0.0到172.31.255.255是私有地址
169.254.0.0到169.254.255.255是保留地址。如果你的IP地址是自动获取IP地址,而你在网络上又没有找到可用的DHCP服务器,这时你将会从169.254.0.0到169.254.255.255中临得获得一个IP地址。
C类地址
C类地址第1字节、第2字节和第3个字节为网络地址,第4个个字节为主机地址。另外第1个字节的前三位固定为110。
C类地址范围:192.0.0.1到223.255.255.254。
C类地址中的私有地址:
192.168.0.0到192.168.255.255是私有地址。
D类地址
D类地址不分网络地址和主机地址,它的第1个字节的前四位固定为1110。
D类地址范围:224.0.0.1到239.255.255.254
Mac地址:每个网卡专用地址,也是唯一的。
端口(port):应用程序(进程)的标识(网络通信程序)OS中可以有65536(2^16)个端口,进程通过端口交换数据。连线的时候需要输入IP也需要输入端口信息。
计算机通信实际上的主机之间的进程通信,进程的通信就需要在端口进行联系。
192.168.0.23:21
协议:为了进行网络中的数据交换(通信)而建立的规则、标准或约定,协议是为了保证通信的安全。
不同层的协议是完全不同的。
OSI网络参考模型(理论性较强的模型)
七层,应用层、表示层、会话层、传输层、网络层、数据链路层、物理层:
网络层:寻址、路由(指如何到达地址的过程)
传输层:端口连接
TCP模型:应用层/传输层/网络层/网络接口
层与层之间是单向依赖关系,上层依赖于下层,下层不依赖于上层,层与层之间的连接是虚连接。对等层之间建立协议。
端口是一种抽象的软件结构,与协议相关:TCP23端口和UDT23端口为两个不同的概念。
端口应该用1024以上的端口,以下的端口都已经设定功能。
TCP/IP模型
Application
(FTP,HTTP,TELNET,POP3,SMPT)
Transport
(TCP,UDP)
Network
(IP,ICMP,ARP,RARP)
Link
(Device driver,….)
注:
IP:寻址和路由
ARP(Address Resolution Protocol)地址解析协议:将IP地址转换成Mac地址
RARP(Reflect Address Resolution Protocol)反相地址解析协议:与上相反
ICMP(Internet Control Message Protocol)检测链路连接状况。利用此协议的工具:ping , traceroute
TCP Socket
TCP是Tranfer Control Protocol的简称,是一种面向连接的保证可靠传输的协议。通过TCP协议传输,得到的是一个顺序的无差错的数据流。发送方和接收方的成对的两个socket之间必须建立连接,以便在TCP协议的基础上进行通信,当一个socket(通常都是server socket)等待建立连接时,另一个socket可以要求进行连接,一旦这两个socket连接起来,它们就可以进行双向数据传输,双方都可以进行发送或接收操作。
1) 服务器分配一个端口号,服务器使用accept()方法等待客户端的信号,信号一到打开socket连接,从socket中取得OutputStream和InputStream。
2) 客户端提供主机地址和端口号使用socket端口建立连接,得到OutputStream和InputStream。
TCP/IP的传输层协议
建立TCP服务器端
一般,我们把服务器端写成是分发Socket的,也就是总是在运行,
创建一个TCP服务器端程序的步骤:
1). 创建一个ServerSocket
2). 从ServerSocket接受客户连接请求
3). 创建一个服务线程处理新的连接
4). 在服务线程中,从socket中获得I/O流
5). 对I/O流进行读写操作,完成与客户的交互
6). 关闭I/O流
7). 关闭Socket
ServerSocket server = new ServerSocket(post)
Socket connection = server.accept();
ObjectInputStream put=new ObjectInputStream(connection.getInputStream());
ObjectOutputStreamo put=newObjectOutputStream(connection.getOutputStream());
处理输入和输出流;
关闭流和socket。
典型的服务器端。
public class Server1 {
public static void main(String[] args) throws Exception {
ServerSocket ss=new ServerSocket(9000);
while(true){
Socket s=ss.accept();//获得一个Socket对象。
Thread t=new Thread1(s);//分发Socket。
t.start();
}
}
}
class Thread1 extends Thread{
Socket s;
public Thread1(Socket s){
this.s=s;
}
public void run(){
try {
OutputStream o=s.getOutputStream();
PrintWriter out=new PrintWriter(o);
out.println("Hello Client");
out.flush();
s.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
建立TCP客户端
创建一个TCP客户端程序的步骤:
1).创建Socket
2). 获得I/O流
3). 对I/O流进行读写操作
4). 关闭I/O流
5). 关闭Socket
Socket connection = new Socket(127.0.0.1, 7777);
ObjectInputStream input=new ObjectInputStream(connection.getInputStream());
ObjectOutputStream utput=new ObjectOutputStream(connection.getOutputStream());
处理输入和输出流;
关闭流和socket。
练习:
实现一个网络应用,客户端会给服务器发送一个字符串,服务器会把这个字符串转换成大写形式发回给客户端
并有客户端显示,同时,服务器会告知客户端,他是第几个客户端