NIO.2中Path、Paths、Files类的使用
Java NIO 概述
-
Java NIO (New IO,Non-Blocking IO)是从Java 1.4版本开始引入的一套新的IO API,可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的(IO是面向流的)、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。
-
Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套就是网络编程NIO。
-
|-----java.nio.channels.Channel
-
|-----FileChannel:处理本地文件
-
|-----SocketChannel:TCP网络编程的客户端的Channel
-
|-----ServerSocketChannel:TCP网络编程的服务器端的Channel
-
|-----DatagramChannel:UDP网络编程中发送端和接收端的Channel
-
-
NIO. 2
- 随着JDK 7 的发布,Java对NIO进行了极大的扩展,增强了对文件处理和文件系统特性的支持,以至于我们称他们为NIO.2。因为NIO 提供的一些功能,NIO已经成为文件处理中越来越重要的部分。
Path、Paths和Files核心API
-
早期的Java只提供了一个File类来访问文件系统,但File类的功能比较有限,所提供的方法性能也不高。而且,大多数方法在出错时仅返回失败,并不会提供异常信息。
-
NIO. 2为了弥补这种不足,引入了Path接口,代表一个平台无关的平台路径,描述了目录结构中文件的位置。Path可以看成是File类的升级版本,实际引用的资源也可以不存在。
-
在以前IO操作都是这样写的:
import java.io.File;
File file = new File(“index.html”);
- 但在Java7 中,我们可以这样写:
import java.nio.file.Path;
import java.nio.file.Paths;
Path path = Paths.get(“index.html”);
-
同时,NIO.2在java.nio.file包下还提供了Files、Paths工具类,Files包含了大量静态的工具方法来操作文件;Paths则包含了两个返回Path的静态工厂方法。
-
Paths 类提供的静态get() 方法用来获取Path 对象:
-
static Pathget(String first, String … more) : 用于将多个字符串串连成路径
-
static Path get(URI uri): 返回指定uri对应的Path路径
-
Path接口
Path常用方法:
-
String toString() :返回调用Path 对象的字符串表示形式
-
boolean startsWith(String path) : 判断是否以path 路径开始
-
boolean endsWith(String path) : 判断是否以path 路径结束
-
boolean isAbsolute() : 判断是否是绝对路径
-
Path getParent() :返回Path对象包含整个路径,不包含Path 对象指定的文件路径
-
Path getRoot() :返回调用Path 对象的根路径
-
Path getFileName() : 返回与调用Path 对象关联的文件名
-
int getNameCount() : 返回Path 根目录后面元素的数量
-
Path getName(int idx) : 返回指定索引位置idx 的路径名称
-
Path toAbsolutePath() : 作为绝对路径返回调用Path 对象
-
Path resolve(Path p) :合并两个路径,返回合并后的路径对应的Path对象
-
File toFile(): 将Path转化为File类的对象
Files 类
java.nio.file.Files 用于操作文件或目录的工具类。
Files常用方法:
-
Path copy(Path src, Path dest, CopyOption … how) : 文件的复制
-
PathcreateDirectory(Path path, FileAttribute<?> … attr) : 创建一个目录
-
Path createFile(Path path, FileAttribute<?> … arr) : 创建一个文件
-
void delete(Path path) : 删除一个文件/目录,如果不存在,执行报错
-
void deleteIfExists(Path path) : Path对应的文件/目录如果存在,执行删除
-
Path move(Path src, Path dest, CopyOption…how) : 将src 移动到dest 位置
-
long size(Path path) : 返回path 指定文件的大小
Files常用方法:用于判断
-
boolean exists(Path path, LinkOption … opts) : 判断文件是否存在
-
boolean isDirectory(Path path, LinkOption … opts) : 判断是否是目录
-
boolean isRegularFile(Path path, LinkOption … opts) : 判断是否是文件
-
boolean isHidden(Path path) : 判断是否是隐藏文件
-
boolean isReadable(Path path) : 判断文件是否可读
-
boolean isWritable(Path path) : 判断文件是否可写
-
boolean notExists(Path path, LinkOption … opts) : 判断文件是否不存在
Files常用方法:用于操作内容
-
SeekableByteChannel newByteChannel(Path path, OpenOption…how) : 获取与指定文件的连接,how 指定打开方式。
-
DirectoryStream
newDirectoryStream(Path path) : 打开path 指定的目录 -
InputStream newInputStream(Path path, OpenOption…how):获取InputStream 对象
-
OutputStream newOutputStream(Path path, OpenOption…how) : 获取OutputStream 对象
网络编程
网络编程概述
-
Java是Internet 上的语言,它从语言级上提供了对网络应用程序的支持,程序员能够很容易开发常见的网络应用程序。
-
Java提供的网络类库,可以实现无痛的网络连接,联网的底层细节被隐藏在Java 的本机安装系统里,由JVM 进行控制。并且Java 实现了一个跨平台的网络库,程序员面对的是一个统一的网络编程环境。
网络基础
- 计算机网络:
把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息、共享硬件、软件、数据信息等资源。
- 网络编程的目的:
直接或间接地通过网络协议与其它计算机实现数据交换,进行通讯。
-
网络编程中有两个主要的问题:
-
如何准确地定位网络上一台或多台主机;定位主机上的特定的应用
-
找到主机后如何可靠高效地进行数据传输
-
网络通信要素概述
如何实现网络中的主机互相通信
-
通信双方地址
-
IP
-
端口号
-
-
一定的规则(即:网络通信协议。有两套参考模型)
-
OSI参考模型:模型过于理想化,未能在因特网上进行广泛推广
-
TCP/IP参考模型(或TCP/IP协议):事实上的国际标准。
-
网络通信协议
OSI参考模型 | TCP/IP参考模型 | TCP/IP参考模型各层对应协议 |
---|---|---|
应用层 | 应用层 | HTTP、FTP、Telnet、DNS… |
表示层 | ||
会话层 | ||
传输层 | 传输层 | TCP、UDP、… |
网络层 | 网络层 | IP、ICMP、ARP… |
数据链路层 | 物理+数据链路层 | Link |
物理层 |
通信要素1:IP 和端口号
-
IP 地址:InetAddress
-
唯一的标识Internet 上的计算机(通信实体)
-
本地回环地址(hostAddress):127.0.0.1 主机名(hostName):localhost
-
IP地址分类方式1:IPV4和IPV6
-
IPV4:4个字节组成,4个0-255。大概42亿,30亿都在北美,亚洲4亿。2011年初已经用尽。以点分十进制表示,如192.168.0.1
-
IPV6:128位(16个字节),写成8个无符号整数,每个整数用四个十六进制位表示,数之间用冒号(:)分开,如:3ffe:3201:1401:1280:c8ff:fe4d:db39:1984
-
-
-
IP地址分类方式2:公网地址(万维网使用)和私有地址(局域网使用)。192.168.开头的就是私有址址,范围即为192.168.0.0–192.168.255.255,专门为组织机构内部使用
-
特点:不易记忆
-
端口号标识正在计算机上运行的进程(程序)
-
不同的进程有不同的端口号
-
被规定为一个16 位的整数0~65535。
-
端口分类:
-
公认端口:0~1023。被预先定义的服务通信占用(如:HTTP占用端口80,FTP占用端口21,Telnet占用端口23)
-
注册端口:1024~49151。分配给用户进程或应用程序。(如:Tomcat占用端口8080,MySQL占用端口3306,Oracle占用端口1521等)。
-
动态/私有端口:49152~65535。
-
-
-
端口号与IP地址的组合得出一个网络套接字:Socket。
InetAddress类
-
Internet上的主机有两种方式表示地址:
-
域名(hostName):www.atguigu.com
-
IP地址(hostAddress):202.108.35.210
-
-
InetAddress类主要表示IP地址,两个子类:Inet4Address、Inet6Address。
-
InetAddress类对象含有一个Internet主机地址的域名和IP地址:www.atguigu.com和202.108.35.210。
-
域名容易记忆,当在连接网络时输入一个主机的域名后,域名服务器(DNS)负责将域名转化成IP地址,这样才能和主机建立连接。-------域名解析
-
InetAddress类没有提供公共的构造器,而是提供了如下几个静态方法来获取InetAddress实例
-
public static InetAddress getLocalHost()
-
public static InetAddress getByName(String host)
-
-
InetAddress提供了如下几个常用的方法
-
public String getHostAddress():返回IP 地址字符串(以文本表现形式)。
-
public String getHostName():获取此IP 地址的主机名
-
public boolean isReachable(int timeout):测试是否可以达到该地址
-
/**
* 一、实现网络通信需要解决的两个问题
* 1.如何准确地定位网络上一台或多台主机;定位主机上的特定的应用
* 2.找到主机后如何可靠高效地进行数据传输
* 二、网络通信的两个要素:
* 1.对应问题一:IP和端口号
* 2.对应问题二:提供网络通信协议:TCP/IP参考模型(应用层、传输层、网络层、物理+数据链路层)
* 三、通信要素一:IP和端口号
* 1.IP的理解
* 1. IP:唯一的标识 Internet 上的计算机(通信实体)
* 2. 在Java中使用InetAddress类代表IP
* 3. IP分类:IPv4 和 IPv6 ; 万维网 和 局域网
* 4. 域名: www.baidu.com www.mi.com www.sina.com www.jd.com
* 域名解析:域名容易记忆,当在连接网络时输入一个主机的域名后,域名服务器(DNS)负责将域名转化成IP地址,这样才能和主机建立连接。 -------域名解析
* 5. 本地回路地址:127.0.0.1 对应着:localhost
* 2.InetAddress类:此类的一个对象就代表着一个具体的IP地址
* 2.1实例化
* getByName(String host) 、 getLocalHost()
* 2.2常用方法
* getHostName() / getHostAddress()
* 3.端口号:正在计算机上运行的进程。
* 要求:不同的进程不同的端口号
* 范围:被规定为一个 16 位的整数 0~65535。
* 端口号与IP地址的组合得出一个网络套接字:Socket
*/
public class InetAddressTest {
public static void main(String[] args) throws UnknownHostException {
InetAddress inet1 = InetAddress.getByName("www.baidu.com");
//InetAddress inet1 = InetAddress.getByName("180.101.49.11");(等同于)
System.out.println(inet1);//www.baidu.com/180.101.49.12
InetAddress inet2 = InetAddress.getByName("127.0.0.1");
//InetAddress inet2 = InetAddress.getByName("localhost");(等同于)
System.out.println(inet2);//127.0.0.1
InetAddress localHost = InetAddress.getLocalHost();
System.out.println(localHost);//LAPTOP-MHUJ3K0O/192.168.31.150
System.out.println(inet1.getHostName());//www.baidu.com
System.out.println(inet1.getHostAddress());//180.101.49.12
}
}
通信要素2:网络通信协议
- 网络通信协议
计算机网络中实现通信必须有一些约定,即通信协议,对速率、传输代码、代码结构、传输控制步骤、出错控制等制定标准。
- 问题:网络协议太复杂
计算机网络通信涉及内容很多,比如指定源地址和目标地址,加密解密,压缩解压缩,差错控制,流量控制,路由控制,如何实现如此复杂的网络协议呢?
- 通信协议分层的思想
在制定协议时,把复杂成份分解成一些简单的成份,再将它们复合起来。最常用的复合方式是层次方式,即同层间可以通信、上一层可以调用下一层,而与再下一层不发生关系。各层互不影响,利于系统的开发和扩展。
TCP/IP协议簇
-
传输层协议中有两个非常重要的协议:
-
传输控制协议TCP(Transmission Control Protocol)
-
用户数据报协议UDP(User Datagram Protocol)。
-
-
TCP/IP 以其两个主要协议:传输控制协议(TCP)和网络互联协议(IP)而得名,实际上是一组协议,包括多个具有不同功能且互为关联的协议。
-
IP(Internet Protocol)协议是网络层的主要协议,支持网间互连的数据通信。
-
TCP/IP协议模型从更实用的角度出发,形成了高效的四层体系结构,即物理链路层、IP层、传输层和应用层。
TCP 和UDP
-
TCP协议:
-
使用TCP协议前,须先建立TCP连接,形成传输数据通道
-
传输前,采用“三次握手”方式,点对点通信,是可靠的
-
TCP协议进行通信的两个应用进程:客户端、服务端。
-
在连接中可进行大数据量的传输
-
传输完毕,需释放已建立的连接,效率低
-
-
UDP协议:
-
将数据、源、目的封装成数据包,不需要建立连接
-
每个数据报的大小限制在64K内
-
发送不管对方是否准备好,接收方收到也不确认,故是不可靠的
-
可以广播发送
-
发送数据结束时无需释放资源,开销小,速度快
-
Socket
-
利用套接字(Socket)开发网络应用程序早已被广泛的采用,以至于成为事实上的标准。
-
网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。
-
通信的两端都要有Socket,是两台机器间通信的端点。
-
网络通信其实就是Socket间的通信。
-
Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输。
-
一般主动发起通信的应用程序属客户端,等待通信请求的为服务端。
-
Socket分类:
-
流套接字(stream socket):使用TCP提供可依赖的字节流服务
-
数据报套接字(datagram socket):使用UDP提供“尽力而为”的数据报服务
-
Socket类的常用构造器:
-
public Socket(InetAddress address,int port)创建一个流套接字并将其连接到指定IP地址的指定端口号。
-
public Socket(String host,int port)创建一个流套接字并将其连接到指定主机上的指定端口号。
Socket类的常用方法:
-
publicInputStream getInputStream()返回此套接字的输入流。可以用于接收网络消息
-
publicOutputStream getOutputStream()返回此套接字的输出流。可以用于发送网络消息
-
publicInetAddress getInetAddress()此套接字连接到的远程IP地址;如果套接字是未连接的,则返回null。
-
publicInetAddress getLocalAddress()获取套接字绑定的本地地址。即本端的IP地址
-
public int getPort()此套接字连接到的远程端口号;如果尚未连接套接字,则返回0。
-
public int getLocalPort()返回此套接字绑定到的本地端口。如果尚未绑定套接字,则返回-1。即本端的端口号。
-
public void close()关闭此套接字。套接字被关闭后,便不可在以后的网络连接中使用(即无法重新连接或重新绑定)。需要创建新的套接字对象。关闭此套接字也将会关闭该套接字的InputStream和OutputStream。
-
public void shutdownInput()如果在套接字上调用shutdownInput()后从套接字输入流读取内容,则流将返回EOF(文件结束符)。即不能在从此套接字的输入流中接收任何数据。
-
public void shutdownOutput()禁用此套接字的输出流。对于TCP套接字,任何以前写入的数据都将被发送,并且后跟TCP的正常连接终止序列。如果在套接字上调用shutdownOutput()后写入套接字输出流,则该流将抛出IOException。即不能通过此套接字的输出流发送任何数据。
TCP网络编程
Java语言的基于套接字编程分为服务端编程和客户端编程,其通信模型如图所示:
基于Socket的TCP编程
-
客户端Socket的工作过程包含以下四个基本的步骤:
-
创建Socket:根据指定服务端的IP 地址或端口号构造Socket 类对象。若服务器端响应,则建立客户端到服务器的通信线路。若连接失败,会出现异常。
-
打开连接到Socket 的输入/出流:使用getInputStream()方法获得输入流,使用getOutputStream()方法获得输出流,进行数据传输
-
按照一定的协议对Socket 进行读/写操作:通过输入流读取服务器放入线路的信息(但不能读取自己放入线路的信息),通过输出流将信息写入线程。
-
关闭Socket:断开客户端到服务器的连接,释放线路
-
客户端创建Socket对象
-
客户端程序可以使用Socket类创建对象,创建的同时会自动向服务器方发起连接。Socket的构造器是:
- Socket(String host,int port)throws UnknownHostException,IOException:向服务器(域名是host。端口号为port)发起TCP连接,若成功,则创建Socket对象,否则抛出异常。
- Socket(InetAddress address,int port)throws IOException:根据InetAddress对象所表示的IP地址以及端口号port发起连接。
-
客户端建立socketAtClient对象的过程就是向服务器发出套接字连接请求
Socket s = new Socket(“192.168.40.165”,9999);
OutputStream out = s.getOutputStream();
out.write(" hello".getBytes());
s.close();
服务器建立ServerSocket对象
-
ServerSocket对象负责等待客户端请求建立套接字连接,类似邮局某个窗口中的业务员。也就是说,服务器必须事先建立一个等待客户请求建立套接字连接的ServerSocket对象。
-
所谓“接收”客户的套接字请求,就是accept()方法会返回一个Socket 对象
ServerSocket ss = new ServerSocket(9999);
Socket s = ss.accept ();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int num = in.read(buf);
String str = new String(buf,0,num);
System.out.println(s.getInetAddress().toString()+”:”+str);
s.close();
ss.close();
例题
1.客户端发送内容给服务端,服务端将内容打印到控制台上。
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
/*
代码示例1:客户端发送信息给服务端,服务端将数据显示在控制台上
*/
public class TCPTest1 {
@Test
public void client() {
Socket socket = null;
OutputStream os = null;
try {
//1.创建Socket对象,指明服务器端的ip和端口号
InetAddress inet = InetAddress.getByName("127.0.0.1");
socket = new Socket(inet, 5151);
//2.获取一个输出流,用于输出数据
os = socket.getOutputStream();
//3.写出数据的操作
os.write("baby,baby,where are you?".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.资源的关闭
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
public void server() {
ServerSocket serverSocket = null;
Socket accept = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
//1.创建服务器端的ServerSocket,指明自己的端口号
serverSocket = new ServerSocket(5151);
//2.调用accept()表示接收来自于客户端的socket
accept = serverSocket.accept();
//3.获取输入流
is = accept.getInputStream();
//4.读取输入流中的数据
baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
System.out.println(baos.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (baos != null) {
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (accept != null) {
try {
accept.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}