1. 对象序列化机制的理解

  2. 对象流序列化与反序列化字符串操作

  3. 自定义类实现实现序列化与反序列化操作

  4. seriaVersionUID的理解

  5. 自定义类可序列化的其他要求

  6. RandomAccessFile实现数据的读写操作

  7. RandomAccessFile实现数据的插入

  8. NIO介绍及NIO2中PATH、PATHS、Files的介绍

  9. 使用第三方jar包实现数据读写

  10. 网络编程的概述

  11. IP的理解与InetAddress类的实例化

  12. 端口号的理解

  13. TCP和UDP网络通信协议的对比

  14. TCP网络编程例题

  15. UDP网络编程举例

  16. URL类的理解和实例化

  17. URL网络编程实现Tomcat服务端数据下载

 

1,对象序列化机制的理解

    对象流:ObjectInputStream和ObjectOutputStream是可用于存储或读取基本数据类型数据或对象的处理流,它可以将Java中的对象写入到磁盘中,也能将它从磁盘中还原回来
    序列化:用ObjectOutputStream类保存基本数据类型或对象的机制
    反序列化:用ObjectInputStream类读取基本数据类型或对象的机制
    对象能够持久化到磁盘中(序列化)的前提是该对象的类是可序列化的,反序列化的前提也是对象的类是可序列化的。除了能在内存和磁盘间进行读取和写入,序列化的对象也能在网络中进行传输(后者才是重点使用的)

day27-IO流与网络编程_IO流和网络编程

 

2,对象流序列化与反序列化字符串操作

3,自定义类实现实现序列化与反序列化操作

    自定义可序列化的类Person

package com.atguigu.java;

import java.io.Serializable;

/**
 * Person要序列化,需要满足以下要求
 * 1,实现Serializable接口
 * 2,提供一个全局常量
 */

public class Person implements Serializable { // 该接口没有规定要实现的抽象方法,这种接口称为标识接口,表明实现该接口的类是可序列化的

    public static final long serialVersionUID = 475463534564L;

    String name;
    int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
// 序列化:使用ObjectOutputStream将内存中的Java对象保存到磁盘或是通过网络传输出去
@Test
public void testObjectOutputStream() {
    ObjectOutputStream oos = null;
    try {
        oos = new ObjectOutputStream(new FileOutputStream("object.dat")); // 传入文件流对象,指明保存对象的路径
        oos.writeObject(new String("我爱北京")); // 序列化过程
        oos.flush();

        oos.writeObject(new Person("王明", 23));
        oos.flush();

    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(oos != null) {
            try {
                oos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
// 反序列化:使用ObjectInputStream将磁盘中的对象还原为内存中的一个Java对象
@Test
public void testObjectInputStream() {
    ObjectInputStream ois = null;
    try {
        ois = new ObjectInputStream(new FileInputStream("object.dat")); // 指明要读取的文件路径
        Object obj = ois.readObject();
        String str = (String) obj;

        Person p = (Person) ois.readObject(); // 读取时要按照写入的顺序,但实际开发中同一文件中保存的一般是序列化的相同类型的对象

    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } finally {
        if(ois != null) {
            try {
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

 

4,seriaVersionUID的理解

    Java的序列化机制是通过在运行时判断类的seriaVersionUID来验证版本一致性。在反序列化时,JVM会把传进来的字节流中的seriaVersionUID与本地相应类的seriaVersionUID进行比较,如果相同则认为是一致,可以进行反序列化,否则就是不一致,抛出异常
    如果在定义可序列化的类时没有定义seriaVersionUID,则会根据类的内部细节自动生成,而当类的实例变量发生修改,该值也可能发生变化
    所以当在类中声明了seriaVersionUID,在反序列化之前即使类的实例变量被修改,该UID值还是不变,序列化对象与类的seriaVersionUID还是一致,可以进行反序列化;而自动生成的seriaVersionUID,当类的实例变量发生修改,该值也被修改,在修改之前的序列化的对象与现在的类的seriaVersionUID不一致,反序列化就会抛出异常
 

5,自定义类可序列化的其他要求

    类内部的所有属性也必须是可序列化的(默认情况下,基本数据类型是可序列化的),不满足这个条件序列化就会报错。当类的某些属性被static和transient修饰,ObjectOutputStream和ObjectInputStream在序列化该类的对象时就不能序列化这些属性,因为static修饰的属性归类所有,而如果不想某个属性被序列化,就用transient修饰它,被这两个关键字修饰的属性在对象被序列化时保存为默认值0或null或false
 

6,RandomAccessFile实现数据的读写操作

    随机存储文件流RandomAccessFile直接继承自Object类,实现了DataInput和DataOutput接口,既能作为输入流,也能作为输出流

day27-IO流与网络编程_IO流和网络编程_02

// 既可以作为输入流,也可以作为输出流
@Test
public void test1() {
    RandomAccessFile raf1 = null;
    RandomAccessFile raf2 = null;
    try {
        raf1 = new RandomAccessFile(new File("1.jpg"), "r");
        raf2 = new RandomAccessFile(new File("2.jpg"), "rw");

        byte[] buffer = new byte[1024];
        int len;
        while((len = raf1.read(buffer)) != -1) {
            raf2.write(buffer, 0, len);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(raf1 != null) {
            try {
                raf1.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(raf2 != null) {
            try {
                raf2.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

// 作为输出流写出到文件时,默认情况下从头开始对文件内容进行覆盖
@Test
public void test2() throws IOException {
    RandomAccessFile raf1 = new RandomAccessFile(new File("hello.txt"), "rw");
    raf1.write("xyz".getBytes()); // 文件内容由abcdefghijklmn变为xyzdefghijklmn
    raf1.close();
}

 

7,RandomAccessFile实现数据的插入

@Test
public void test3() throws IOException {
    RandomAccessFile raf1 = new RandomAccessFile("hello.txt", "rw");
    raf1.seek(3); // 文件中有一个指针指向操作的位置,默认在文件开头。将指针移动到下标为3的位置(以字符为单位)
    // 创建一个下标为3到文件末尾长度的StringBuffer,用来保存这段文本
    StringBuffer builder = new StringBuffer((int) new File("hello.txt").length());
    byte[] buffer = new byte[20];
    int len;
    while((len = raf1.read(buffer)) != -1) {
        builder.append(new String(buffer, 0, len)); // 将每次读入的数据保存到builder中
    }
    raf1.seek(3); // 当读取完后,指针移动到文件末尾,再将它移回下标为3的位置
    raf1.write("abc".getBytes()); // 在下标为3的后面覆盖abc
    raf1.write(builder.toString().getBytes()); // 接着在后面覆盖原来保存的数据,就实现了在用来文件中下标为3的位置插入abc
    raf1.close();
}

 

8,NIO介绍及NIO2中PATH、PATHS、Files的介绍

day27-IO流与网络编程_IO流和网络编程_03

day27-IO流与网络编程_IO流和网络编程_04

day27-IO流与网络编程_IO流和网络编程_05

 

9,使用第三方jar包实现数据读写

    在当前module下新建目录libs,将jar包放在libs下,右键jar包选择add as library,就能作为JDK以外的API被使用

day27-IO流与网络编程_IO流和网络编程_06

public static void main(String[] args) {
    File srcFile = new File("day27\\1.jpg");
    File destFile = new File("day27\\3.jpg");

    try {
        FileUtils.copyFile(srcFile, destFile); // 导入的jar包提供,底层实际还是使用之前讲的io下的功能
    } catch (IOException e) {
        e.printStackTrace();
    }
}

 

10,网络编程概述

day27-IO流与网络编程_IO流和网络编程_07

day27-IO流与网络编程_IO流和网络编程_08

 

11,IP的理解与InetAddress类的实例化

    IP地址用来唯一标识Internet上的一台计算机,在Java中使用InetAddress类的对象代表一个IP

public static void main(String[] args) {
    try {
        InetAddress inet1 = InetAddress.getByName("192.168.10.14"); // 实例化一个指定IP地址的对象
        System.out.println(inet1);

        InetAddress inet2 = InetAddress.getByName("www.atguigu.com"); // 通过域名实例化的对象
        System.out.println(inet2); // 还会通过域名解析得到对应的IP地址

        InetAddress inet3 = InetAddress.getByName("127.0.0.1"); // 本机回环地址
        System.out.println(inet3);

        InetAddress inet4 = InetAddress.getLocalHost(); // 获取本地IP
        System.out.println(inet4); // xurui/192.168.0.1

        System.out.println(inet2.getHostName()); // 获取主机名。www.atguigu.com
        System.out.println(inet2.getHostAddress()); // 获取主机地址。163.177.20.210
    } catch (UnknownHostException e) {
        e.printStackTrace();
    }
}

 

12,端口号的理解

day27-IO流与网络编程_IO流和网络编程_09

 

13,TCP和UDP网络通信协议的对比

day27-IO流与网络编程_IO流和网络编程_10

 

14,TCP网络编程例题

// 实现客户端给服务器端发送一句话。先启动服务端,再启动客户端建立连接
// 客户端
@Test
public void client() {
    Socket socket = null;
    OutputStream os = null;
    try {
        InetAddress inet = InetAddress.getByName("192.168.0.14"); // 传入服务器端的IP地址
        socket = new Socket(inet, 8899); // 通过IP+端口号的方式创建一个用于连接socket对象
        os = socket.getOutputStream(); // 返回一个输出流,用于客户端向服务器端发送数据
        os.write("你好,我是客户端".getBytes()); // 写入数据,转换为字节流。实际也是通过网络传输数据的动作
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(os != null) {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(socket != null) { // socket也是一种需要手动关闭的资源
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
// 服务器端
@Test
public void server() {
    ServerSocket ss = null;
    Socket socket = null;
    InputStream is = null;
    ByteArrayOutputStream baos = null;
    try {
        ss = new ServerSocket(8899); // 创建的该对象用来指明服务端监听的端口号
        socket = ss.accept(); // 调用该方法表示接收来自客户端的socket
        is = socket.getInputStream(); // 通过上面返回的socket对象获取输入流

        // 这种写法可能会出现乱码
//            byte[] buffer = new byte[1024];
//            int len;
//            while((len = is.read(buffer)) != -1) {
//                String str = new String(buffer, 0, len);
//                System.out.print(str);
//            }
        baos = new ByteArrayOutputStream();
        byte[] buffer = new byte[5];
        int len;
        while((len = is.read(buffer)) != -1) {
            baos.write(buffer, 0, len);
        }
        System.out.println(baos.toString());
        System.out.println("收到了来自:" + socket.getInetAddress().getHostAddress() + "的数据"); // 获取客户端的IP地址
    } 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(socket != null) {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(ss != null) {
            try {
                ss.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
// 客户端给服务器发送一张照片
@Test
public void client() throws IOException { // 要用try-catch-finally处理
    Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9090); // 此时本机既充当客户端也充当服务器端
    OutputStream os = socket.getOutputStream(); // 这个输出流用于给服务端传输数据
    FileInputStream fis = new FileInputStream(new File("1.jpg")); // 这个输入流用于读取本地数据

    byte[] buffer = new byte[1024];
    int len;
    while((len = fis.read(buffer)) != -1) {
        os.write(buffer, 0, len); // 通过网络传输数据的动作
    }
    fis.close();
    os.close();
    socket.close();
}
@Test
public void server() throws IOException {
    ServerSocket ss = new ServerSocket(9090);
    Socket socket = ss.accept(); // 获取客户端的socket
    InputStream is = socket.getInputStream(); // 这个输入流用于读取客户端传来的数据
    FileOutputStream fos = new FileOutputStream(new File("4.jpg")); // 这个输出流用于保存数据到本地

    byte[] buffer = new byte[1024];
    int len;
    while((len = is.read(buffer)) != -1) {
        fos.write(buffer, 0, len);
    }
    fos.close();
    is.close();
    socket.close();
    ss.close();
}
    第二个例子中,客户端传输图片完成后就执行了关闭流的操作,服务端的read()也就结束。第三个例子中,客户端传输图片完成后等待服务端发送数据,而服务端的read()等待客户端继续传输图片数据,所以需要客户端在图片传输完成后手动关闭输出流以结束服务端的read()

// 客户端给服务器发送一张照片,服务器端保存到本地并返回“发送成功”给客户端
@Test
public void client() throws IOException { // 要用try-catch-finally处理
    Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9090); // 此时本机既充当客户端也充当服务器端
    OutputStream os = socket.getOutputStream(); // 这个输出流用于给服务端传输数据
    FileInputStream fis = new FileInputStream(new File("1.jpg")); // 这个输入流用于读取本地数据

    byte[] buffer = new byte[1024];
    int len;
    while((len = fis.read(buffer)) != -1) {
        os.write(buffer, 0, len); // 通过网络传输数据的动作
    }
    socket.shutdownOutput(); // 关闭输出流,是为了明确告知服务端从客户端到服务端的数据传输已终止

    InputStream is = socket.getInputStream(); // 用来获取从服务端传过来的数据
    ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 内部有支持扩容的数组
    byte[] buffer1 = new byte[20];
    int len1;
    while((len1 = is.read(buffer1)) != -1) {
        baos.write(buffer1, 0, len1);
    }
    System.out.println(baos.toString());

    fis.close();
    os.close();
    socket.close();
    baos.close();
}
@Test
public void server() throws IOException {
    ServerSocket ss = new ServerSocket(9090);
    Socket socket = ss.accept(); // 获取客户端的socket
    InputStream is = socket.getInputStream(); // 这个输入流用于读取客户端传来的数据
    FileOutputStream fos = new FileOutputStream(new File("4.jpg")); // 这个输出流用于保存数据到本地

    byte[] buffer = new byte[1024];
    int len;
    while((len = is.read(buffer)) != -1) { // 不同于读入本地的文件,当读完后返回-1的情况,该方法会一直等待客户端传输数据,
                                    // 需要客户端手动关闭输出流来结束该方法
        fos.write(buffer, 0, len);
    }
    System.out.println("图片传输完成");

    OutputStream os = socket.getOutputStream(); // 这个输出流用于服务端给客户端传输数据
    os.write("我已收到客户端发送的图片".getBytes()); // 通过网络传输数据的动作

    fos.close();
    is.close();
    socket.close();
    ss.close();
    os.close();
}

 

15,UDP网络编程举例

// 发送端
@Test
public void sender() throws IOException {
    
    DatagramSocket socket = new DatagramSocket();
    String str = "我是UDP方式发送的";
    byte[] data = str.getBytes();
    InetAddress inet = InetAddress.getLocalHost();
    DatagramPacket packet = new DatagramPacket(data,0,data.length,inet,9090);
    socket.send(packet);
    socket.close();
}
// 接收端
@Test
public void receiver() throws IOException {

    DatagramSocket socket = new DatagramSocket(9090);
    byte[] buffer = new byte[100];
    DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);
    socket.receive(packet);
    System.out.println(new String(packet.getData(),0,packet.getLength()));
    socket.close();
}

 

16,URL类的理解和实例化

    URL即统一资源定位符,它表示Internet上某一资源的地址。URL由协议、主机名、端口号、资源地址、传输列表组成,比如http://localhost:8080/examples/beauty.jpg?username=Tom

public static void main(String[] args) {
    try {
        URL url = new URL("http://localhost:8080/examples/beauty.jpg?username=Tom");
        System.out.println(url.getProtocol()); // http
        System.out.println(url.getHost()); // localhost
        System.out.println(url.getPort()); // 8080
        System.out.println(url.getPath()); // /examples/beauty.jpg
        System.out.println(url.getFile()); // /examples/beauty.jpg?username=Tom
        System.out.println(url.getQuery()); // username=Tom
    } catch (MalformedURLException e) {
        e.printStackTrace();
    }
}

 

17,URL网络编程实现Tomcat服务端数据下载

    下载安装好tomcat后,添加到PATH变量中,命令行执行catalina run启动,将图片资源放在webapps\examples目录下

public static void main(String[] args) {
    HttpURLConnection urlConnection = null;
    InputStream is = null;
    FileOutputStream fos = null;
    try {
        URL url = new URL("http://localhost:8080/examples/1.jpg");
        urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.connect();
        is = urlConnection.getInputStream();
        fos = new FileOutputStream("day27\\5.jpg");

        byte[] buffer = new byte[1024];
        int len;
        while((len = is.read(buffer)) != -1) {
            fos.write(buffer, 0, len);
        }
        System.out.println("下载完成");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(is != null) {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(fos != null) {
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(urlConnection != null) {
            urlConnection.disconnect();
        }
    }
}