IO
含义
输入输出
分类
根据传输的单位
- 字节流
- 字符流
根据流向
- 输入流
- 输出流
字节流
含义
都是从InputStream或OutputStream派生的类
FileInputStream
public class FileInputStreamDemo {
// FileInputStream
//含义:从文件中按照一个一个字节方式读取数据到内存
public static void main(String[] args) throws Exception {
InputStream inputStream = new FileInputStream("C:\\a.txt");
//读一个字节,如果返回-1代表全部读到最末尾了
//int result = inputStream.read();
//System.out.println(result);
//读取全部内容
/*
int content=-1;
while((content=inputStream.read())!=-1){
System.out.println(content);
}
*/
byte [] cache = new byte[1024];
int length=-1;
//把读取到的字节放入cache这个缓存中,返回的是读取到了多个字节,如果-1代表读取到了末尾
while((length = inputStream.read(cache))!=-1){
String strData = new String(cache, 0, length);
System.out.println(strData);
}
//new String(byte [] data,int offset,int length)
//代表将字节数组构造成字符串,offset代表从数组什么位置开始,length取多少个字节
}
}
FileOutputStream
public class FileOutputStreamDemo {
public static void main(String[] args) {
//字节输出流
//含义:按照字节输出单位从内存数据写出到磁盘
String name ="你好张三"; //磁盘中
//IO步骤
//第一步新建管道
//第二步读写数据
//第三步关闭管道
boolean sign = write("中国你好", "C://aa//bb//a.txt");
System.out.println(sign?"成功":"失败");
}
/**
* 写数据到文件
* @param content 内容
* @param path 存储路径
* @return
*/
public static boolean write(String content,String path){
OutputStream outputStream = null;
try {
File target = new File(path);
if(!target.getParentFile().exists()){
target.getParentFile().mkdirs();
}
outputStream = new FileOutputStream(path);
outputStream.write(content.getBytes());
return true;
}catch (IOException e) {
e.printStackTrace();
}finally {
try {
outputStream.close();
} catch (IOException e) {
//TODO 记录到日志文件中
e.printStackTrace();
}
}
return false;
}
}
字节流copy
public class FileUtil {
public static void main(String[] args) {
copy("C:\\03懒汉式单例设计模式.mp4","D:\\one.mp4");
}
// 文件--copy-->文件
public static boolean copy(String source,String target){
if(StringUtil.isEmpty(source) || StringUtil.isEmpty(target)){
return false;
}
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = new FileInputStream(source);
outputStream = new FileOutputStream(target);
byte [] cache = new byte[1024];
int len = -1;
while((len=inputStream.read(cache))!=-1){
//System.out.println(new String(cache,0,len));
outputStream.write(cache, 0, len);
}
return true;
}catch (IOException e) {
//TODO 记录日志
e.printStackTrace();
}finally {
try {
if(inputStream!=null){
inputStream.close();
}
if(outputStream!=null){
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
}
字符流
FileReader
public class FileReaderDemo {
public static void main(String[] args) throws Exception {
Reader reader = new FileReader("C:\\a.txt");
/*
//读取到一个字符
int result = reader.read();
System.out.println((char)result);
*/
/*
int len = -1;
while((len=reader.read())!=-1){
System.out.println((char)len);
}
*/
//cache
char [] cache = new char[1024];
//[ , , , , ,,,,,,,,, ];
int len = -1;
while((len=reader.read(cache))!=-1){
//[中,国 ,你 ,好 , ,,,,,,,,, ];
String result = new String(cache, 0, len);
System.out.println(result);
}
reader.close();
}
}
FileWriter
public class FileWriterDemo {
public static void main(String[] args) throws IOException {
Writer writer = new FileWriter("c:\\b.txt");
//writer.write(97);
writer.write("中国加油");
//一定要关闭,调用关闭方法,会刷新缓存
writer.close();
//文件复制 采用字符流 TODO
}
}
字符缓冲流
- BufferedReader
- BufferedWriter
public class BufferedReaderDemo {
public static void main(String[] args) throws Exception {
//缓冲字符输入流
//对字符流进一步的包装
Reader reader = new FileReader("c:\\a.txt");
BufferedReader bufferedReader = new BufferedReader(reader);
String result = null;
//readLine 读取一样的数据,如果范围null代表读取到了末尾
while((result = bufferedReader.readLine())!=null){
System.out.println(result);
}
bufferedReader.close();
//缓冲字符输出流
//对字符流进一步的包装
Writer writer = new FileWriter("c:\\b.txt");
BufferedWriter bufferedWriter = new BufferedWriter(writer);
bufferedWriter.write("test");
bufferedWriter.close();
}
}
转换流
public class IOConverter {
public static void main(String[] args) throws Exception {
//testInputStreamToReader();
OutputStream outputStream = new FileOutputStream("c:\\b.txt");
//字节输出流装换成字符输出流
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
bufferedWriter.write("中国你好");
//bufferedWriter.newLine();
bufferedWriter.write("\r\n");
bufferedWriter.write("世界你好");
bufferedWriter.close();
}
/**
* 字节输入流-->字符输入流
* @throws FileNotFoundException
* @throws IOException
*/
private static void testInputStreamToReader() throws FileNotFoundException, IOException {
InputStream inputStream = new FileInputStream("C:\\a.txt");
//装换流(字节输入流-->字符输入流)
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
System.out.println(bufferedReader.readLine());
bufferedReader.close();
}
}
标准输入输出流
含义
标准输入System.in
标准输出System.out
案例
public class StandardIO {
public static void main(String[] args) throws FileNotFoundException {
//用来修改标准输出流的指向
/*
OutputStream outputStream = new FileOutputStream("C:\\a.txt");
PrintStream printStream = new PrintStream(outputStream);
//用来修改标准输出流的指向
System.setOut(printStream);
System.out.println("abc");
*/
//标准输入流相关操作
//如何获取键盘输入的信息
Scanner scanner = new Scanner(System.in);
String data = null;
System.out.println("请输入数据");
while((data=scanner.nextLine())!=null){
if(data.equals("88")){
break;
}
System.out.println("请输入数据");
//获取控制台输入的整数
//int result = scanner.nextInt();
//System.out.println(result);
System.out.println(data);
}
}
}
序列化&反序列化
-
序列化
将对象转换成二进制保存到磁盘
-
反序列化
将磁盘中的文件读取出来并转换成对象
作用
- 将对象在网络上传输
- 将对象保存到文件
如何序列化步骤
-
将类实现序列化接口
-
生产版本号
版本号用来比较类的结构
案例
public class User implements Serializable {
//序列化
//实现一个接口
//生成版本号,是根据类的结构生成的
private static final long serialVersionUID = -6015352078148104614L;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public User(String name) {
super();
this.name = name;
}
}
public class ObjectStreamDemo {
public static void main(String[] args) throws Exception {
//saveUser();
loadUser();
}
private static void loadUser() throws IOException, FileNotFoundException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("c:\\a.txt"));
User user = (User) objectInputStream.readObject();
System.out.println(user.getName());
objectInputStream.close();
}
private static void saveUser() throws FileNotFoundException, IOException {
User user = new User("zs");
OutputStream out = new FileOutputStream("c:\\a.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(out);
objectOutputStream.writeObject(user);
objectOutputStream.close();
}
}
网络协议
通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样。在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。
网络通信协议有很多种,目前应用最广泛的是TCP/IP协议(Transmission Control Protocal/Internet Protoal传输控制协议/英特网互联协议),它是一个包括TCP协议和IP协议,UDP(User Datagram Protocol)协议和其它一些协议的协议组,在学习具体协议之前首先了解一下TCP/IP协议组的层次结构。
在进行数据传输时,要求发送的数据与收到的数据完全一样,这时,就需要在原有的数据上添加很多信息,以保证数据在传输过程中数据格式完全一致。TCP/IP协议的层次结构比较简单,共分为四层,如图所示。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4CEcYz9u-1627087841296)(assets/图片1.png)]
上图中,TCP/IP协议中的四层分别是应用层、传输层、网络层和链路层,每层分别负责不同的通信功能,接下来针对这四层进行详细地讲解。
链路层:是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤、网线提供的驱动。
网络层:网络层是整个TCP/IP协议的核心,它主要用于将传输的数据进行分组,将分组数据发送到目标计算机或者网络。
传输层:主要使网络程序进行通信,在进行网络通信时,可以采用TCP协议,也可以采用UDP协议。
应用层:主要负责应用程序的协议,例如HTTP协议、FTP协议等。
ip和端口
要想使网络中的计算机能够进行通信,必须为每台计算机指定一个标识号,通过这个标识号来指定接受数据的计算机或者发送数据的计算机。
在TCP/IP协议中,这个标识号就是IP地址,它可以唯一标识一台计算机,目前,IP地址广泛使用的版本是IPv4,它是由4个字节大小的二进制数来表示,如:00001010000000000000000000000001。由于二进制形式表示的IP地址非常不便记忆和处理,因此通常会将IP地址写成十进制的形式,每个字节用一个十进制数字(0-255)表示,数字间用符号“.”分开,如 “192.168.1.100”。
随着计算机网络规模的不断扩大,对IP地址的需求也越来越多,IPV4这种用4个字节表示的IP地址面临枯竭,因此IPv6 便应运而生了,IPv6使用16个字节表示IP地址,它所拥有的地址容量约是IPv4的8×1028倍,达到2128个(算上全零的),这样就解决了网络地址资源数量不够的问题。
通过IP地址可以连接到指定计算机,但如果想访问目标计算机中的某个应用程序,还需要指定端口号。在计算机中,不同的应用程序是通过端口号区分的。端口号是用两个字节(16位的二进制数)表示的,它的取值范围是065535,其中,01023之间的端口号用于一些知名的网络服务和应用,用户的普通应用程序需要使用1024以上的端口号,从而避免端口号被另外一个应用或服务所占用。
接下来通过一个图例来描述IP地址和端口号的作用,如下图所示。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l1f4Rgnl-1627087841299)(assets/图片2.png)]
从上图中可以清楚地看到,位于网络中一台计算机可以通过IP地址去访问另一台计算机,并通过端口号访问目标计算机中的某个应用程序。
InetAdderss
JDK中提供了一个InetAdderss类,该类用于封装一个IP地址,并提供了一系列与IP地址相关的方法,
常用方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eFIns30r-1627087841301)(assets/图片3.png)]
案例
TODO
TCP\UDP
传输层的两个重要的高级协议,分别是UDP和TCP,其中UDP是User Datagram Protocol的简称,称为用户数据报协议,TCP是Transmission Control Protocol的简称,称为传输控制协议。
UDP
UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。
由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议。
常见类
DatagramPacket 就是数据包
DatagramSocket 传输载体
案例
public class Receiver {
public static void main(String[] args) throws Exception {
//接收者,接收从什么发过来的数据
DatagramSocket socket = new DatagramSocket(9999);
//定义一个空的数据包
byte [] cache = new byte [1024] ;
DatagramPacket packet = new DatagramPacket(cache,cache.length);
//接受数据包
socket.receive(packet);
//获取接受到的数据
byte [] data = packet.getData();
int length = packet.getLength();
System.out.println(new String(data,0,length));
//打印从哪里接收到的数据
InetAddress sender = packet.getAddress();
System.out.println("发送者的ip"+sender.getHostAddress());
socket.close();
}
}
public class Sender {
public static void main(String[] args) throws Exception {
//发送者
DatagramSocket socket = new DatagramSocket();
//如下在准备数据包
String content = "hello";
byte data [] = content.getBytes();
DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getLocalHost(), 9999);
socket.send(packet);
socket.close();
}
}
TCP
TCP协议是面向连接的通信协议,即在传输数据前先在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”。第一次握手,客户端向服务器端发出连接请求,等待服务器确认,第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求,第三次握手,客户端再次向服务器端发送确认信息,确认连接。整个交互过程如下图所示。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8anvZ92v-1627087841304)(file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml1124\wps4.png)]
由于TCP协议的面向连接特性,它可以保证传输数据的安全性,所以是一个被广泛采用的协议,例如在下载文件时,如果数据接收不完整,将会导致文件数据丢失而不能被打开,因此,下载文件时必须采用TCP协议。