TCP通信

package network;


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

/**
 * TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象,从而在通信的两端形成网络虚拟链路,一旦建立了虚拟的网络链路,两端的程序就可以通过该链路通信
 *
 * Java对于基于TCP协议的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信
 * Java为客户端提供了 Socket类 ,为服务端提供了ServerSocket类
 */
public class TcpDemo {
    public static void main(String[] args) {

    }

    /**
     * 服务端:
     * 1、创建服务器端的Socket对象(ServerSocket)
     * 2、获取输入流,读数据,并把数据显示在控制台
     * 3、释放资源
     */
    public static void server() throws IOException {
        //创建服务器端的Socket对象(ServerSocket)
        //ServerSocket(int port) 创建绑定到指定端口的服务器套接字
        ServerSocket serverSocket = new ServerSocket(10086);

        //accept() 侦听要连接到此套接字并接受它
        Socket accept = serverSocket.accept();

        //获取输入流
        InputStream is = accept.getInputStream();
        byte[] bys = new byte[1024];
        int read = is.read(bys);
        String s = new String(bys, 0, read);

        System.out.println(s);

        //回复客户端
        OutputStream os = accept.getOutputStream();
        os.write("收到了你的来信".getBytes(StandardCharsets.UTF_8));


    }

    /**
     * 客户端:
     * 1、创建客户端的Socket对象(Socket)
     * 2、获取输出流,写数据
     * 3、释放资源
     */
    public static void client() throws IOException {
        //创建客户端的Socket
        //Socket(InetAddress address, int port) 创建流套接字并将其连接到指定IP地址的指定端口号。
        Socket socket = new Socket("192.168.3.14",10086);

        //获取输出流,写数据(发送到服务端)
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("你好,TCP".getBytes(StandardCharsets.UTF_8));

        //读取服务端回复数据
        InputStream inputStream = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int read = inputStream.read(bytes);
        String s = new String(bytes, 0, read);
        System.out.println(s);
        //关闭资源
        socket.close();

    }


}

UDP通信

package network;

import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;

/**
 * UDP协议实现通信
 * UDP协议是一种不可靠的网络协议,它在两端各建立一个Socketd对象,但是两个Socker只是发送数据和接收数据,
 * 因此对于基于UDP协议的通信双方而言,没有所谓的客户端和服务端的概念
 * Java提供了 DatagramSocket 类作为基于UDP协议的Socket实现功能
 */
public class UdpDemo {
    public static void main(String[] args) {

    }

    /**
     * UDP发送数据步骤:
     * 1、创建发送端的Socket对象(DatagramSocket)
     * 2、创建数据,并把数据打包
     * 3、调用DatagramSocket对象的方法发送数据
     * 4、关闭发送端
     * @throws IOException
     */
    public static void send() throws IOException {
        //创建发送对象
        DatagramSocket datagramSocket = new DatagramSocket();//"192.168.3.14"

        //创建数据,并打包数据
        //DatagramPacket(byte[] buf, int length, InetAddress address, int port) 构造一个数据包,发送长度为 length的数据包到指定主机上的指定端口号。
        byte[] bytes = "你好455454".getBytes(StandardCharsets.UTF_8);//发送数据
        InetAddress address = InetAddress.getByName("192.168.3.14");//发送IP
        int port = 10086;//端口

        DatagramPacket dp = new DatagramPacket(bytes, bytes.length,address,port);
        //调用发送方法 send(DatagramPacket p)
        datagramSocket.send(dp);
        //关闭资源
        datagramSocket.close();
    }

    /**
     * UPD接收数据:
     * 1、创建接收端的Socket对象(DatagramSocket)
     * 2、创建一个数据包,用于接收数据
     * 3、调用DatagramSocket对象方法接收数据
     * 4、解析数据包、并把数据在控制台显示
     * 5、关闭接收端
     * @throws IOException
     */
    public static void receive() throws IOException {
        //创建接收对象
        int port = 10086;        //指定端口
        DatagramSocket datagramSocket = new DatagramSocket(port);

        //创建数据包,接收数据
        byte[] bytes = new byte[1024];
        //调用DatagramSocket对象方法接收数据
        DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
        datagramSocket.receive(dp);
        //解析数据
        byte[] data = dp.getData();
        int len = dp.getLength();
        String s = new String(data,0,len);//将数据转换成String
        System.out.println(s);
        //关闭
        //datagramSocket.close();
    }
}

网络编程三要素

1、IP

  • IP地址是网络中的合并的唯一表示,它分为两大类:
  • IPv4
  • IPv6

2、端口(port)

  • 设备上应用程序的唯一标识符
  • 端口号:用两个字节表示的整数,它的取值是0~65535,其中0~1023之间的端口用于一些知名的网络服务和应用
  • 普通的应用程序需要使用1024以上的端口

3、协议

  • 计算机网络中,连接和通信的规则被称为网络通信协议
  • UDP协议:用户数据报协议(User Datagram Protocol)
  • UPD协议是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接,简单来说,当一台计算机向另一台计算机发送数据时,发送端不会确认接收端舒服存在就会发送数据,同样,接收端不会反馈是否接收成功信息
  • 由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据传输
  • ⬆:由于UDP面向的无连接性,不能保证数据的完整性,因此传输重要数据时不建议使用UDP协议
  • TCP协议:(Transmission Control Protocol)
  • TCP协议是面向连接的通信协议,即传输数据前需要在发送端和接收端建立逻辑连接,然后在传输数据,它提供了两台计算机之间可靠无差错的数据传输,在TCP连接中必须要明确客户端和服务端,由客户端向服务端发出连接请求,每次连接的创建都要经过“三次握手”
  • 三次握手
  • 第一次握手,客户端向服务端发出连接请求,等待服务器确认
  • 第二次握手,服务端向客户端回送一个响应,通知客户端收到了连接的请求
  • 第三次握手,客户端再次向服务端发送确认信息,然后建立连接
  • ⬆:重要数据传输建议用此协议

生产者和消费者

通过送奶工人(生产者)将牛奶放入奶箱(容器),提醒用户(消费者)取奶和开启等待和唤醒等待的业务行为演示生产者和消费者的逻辑关系,
运行入口:

package yield;

/**
 * demo
 */
public class Demo {
    public static void main(String[] args) {
        //创建奶箱对象,这是共享数据区域
        Box box = new Box();

        //创建生产者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用存储牛奶的操作
        Producer producer = new Producer(box);

        //创建消费者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用获取牛奶的操作
        Customer customer = new Customer(box);

        //创建线程对象,分别把生产者和消费者对象作为构造方法参数传递
        Thread t1 = new Thread(producer);//生产者线程
        Thread t2 = new Thread(customer);//消费者线程

        //启动线程
        t1.start();
        t2.start();


    }
}

奶箱(容器):

package yield;

/**
 * 奶箱(容器)
 */
public class Box {
    //奶瓶数量
    private int milk;
    //奶箱状态(默认情况下是没有牛奶的)
    private boolean state = false;

    /**
     * 存储牛奶的动作
     *
     * @param milk 奶瓶
     */
    public synchronized void put(int milk) {
        //奶箱中还有牛奶(等待消费)
        if (state){
            //wait() 等待消费
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果没有牛奶,就放入牛奶(生产牛奶)
        this.milk = milk;
        System.out.println("送奶工将第 " + this.milk + " 放入奶箱");

        //放入完毕,修改状态
        this.state = true;
        //放入后提醒消费者取奶(唤醒消费者)
        this.notifyAll();
    }

    /**
     * 拿出牛奶动作
     */
    public synchronized void get() {
        //奶箱中没有牛奶,等待放入牛奶(等待生产)
        if (!state){
            //等待生产
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果有牛奶,就拿出牛奶
        System.out.println("用户拿到第 " + this.milk + " 瓶奶");

        //拿出完毕后,修改状态
        this.state = false;
        //拿出牛奶后,提醒生产者继续放牛奶(唤醒生产者)
        this.notifyAll();
    }
}

送奶工人(生产者):

package yield;

/**
 * 送奶工人(生产者)
 */
public class Producer implements Runnable {
    private Box box;

    public Producer(Box box) {
        this.box = box;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 5; i++){
            this.box.put(i);//放入牛奶
        }
    }
}

用户(消费者):

package yield;

/**
 * 牛奶订购者(消费者)
 */
public class Customer implements  Runnable{
    private Box box;

    public Customer(Box box) {
        this.box = box;
    }

    @Override
    public void run() {
        while (true){
            box.get();//获取牛奶
        }
    }
}

生产者和消费者.png

线程安全

运行代码:

 public static void main(String[] args) {
        SellTicket st = new SellTicket();

        Thread t1 = new Thread(st,"窗口1");
        Thread t2 = new Thread(st,"窗口2");
        Thread t3 = new Thread(st,"窗口3");
        //启动线程
        t1.start();
        t2.start();
        t3.start();
    }

Demo1:

package course;

/**
 * 买票窗口
 */
public class SellTicket implements Runnable {
    private int tickets = 10;
    @Override
    public void run() {
        int sum = 10;
        while (true){
            if (tickets > 0) {
                tickets--;
                System.out.println(Thread.currentThread().getName() + "正在出售第" + (sum - tickets) + "张票");
            }
        }
    }
}

线程安全1.png

线程安全问题
运行以上代码,3个售票窗口售卖10张票会出现超卖或少卖的情况
为什么会出现问题?(这也是我们判断多线程程序是否会有数据安全问题的标准)

  1. 是否是多线程环境
  2. 是否有共享数据
  3. 是否有多条语句操作共享数据

如何解决多线程按问题?

  • 让程序没有安全问题的环境
  • 实现方式:把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可

解决方法:

  1. 同步代码块:锁多条语句操作共享数据,可以使用同步代码块
    格式:
    synchronized(任意对象){

     多条语句操作共享数据的代码

    }

  2. 通过Lock类

Demo2(修改Demo1,使用synchronized关键字):

package course;

/**
 * 买票窗口
 */
public class SellTicket implements Runnable {
    private int tickets = 10;
    //使用同步代码块
    private Object obj = new Object();

    @Override
    public void run() {
        int sum = 10;
        synchronized (obj) {
            while (true) {
                if (tickets > 0) {
                    tickets--;
                    System.out.println(Thread.currentThread().getName() + "正在出售第" +(sum - tickets)+ "张票");
                }
            }
        }
    }
}

线程安全2.png

Demo3(修改Demo2,使用Lock类):

package course;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 买票窗口
 */
public class SellTicket implements Runnable {
    private int tickets = 10;
    //使用Lock  通过Lock的实现类
    private Lock lock = new ReentrantLock();


    @Override
    public void run() {
        int sum = 10;
        while (true) {
            lock.lock();
            if (tickets > 0) {
                tickets--;
                System.out.println(Thread.currentThread().getName() + "正在出售第" +(sum - tickets)+ "张票");

            }

            lock.unlock();
        }
    }
}

线程安全3.png

进销存/线程概述

进程:

  1. 进程是正在运行的程序
  2. 是系统进行资源分配和调用的独立单位
  3. 每一个进程都有它自己的内存空间和系统资源

线程:

  1. 是进程中的单个顺序控制流,是一条执行路径
  2. 单线程:一个程序如果只有一条执行路径,就是单线程程序
  3. 多线程:一个程序刚有多条执行路径,就是多线程程序

线程详细描述:以下摘自 知乎

  1. 线程在进程下行进(单纯的车厢无法运行)
  2. 一个进程可以包含多个线程(一辆火车可以有多个车厢)
  3. 不同进程间数据很难共享(一辆火车上的乘客很难换到另外一辆火车,比如站点换乘)
  4. 同一进程下不同线程间数据很易共享(A车厢换到B车厢很容易)
  5. 进程要比线程消耗更多的计算机资源(采用多列火车相比多个车厢更耗资源)
  6. 进程间不会相互影响,一个线程挂掉将导致整个进程挂掉(一列火车不会影响到另外一列火车,但是如果一列火车上中间的一节车厢着火了,将影响到所有车厢)
  7. 进程可以拓展到多机,进程最多适合多核(不同火车可以开在多个轨道上,同一火车的车厢不能在行进的不同的轨道上)
  8. 进程使用的内存地址可以上锁,即一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。(比如火车上的洗手间)-"互斥锁"
  9. 进程使用的内存地址可以限定使用量(比如火车上的餐厅,最多只允许多少人进入,如果满了需要在门口等,等有人出来了才能进去)-“信号量”

线程有两种调度模型:

  1. 分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片
  2. 抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么随机选择一个,优先级高的线程或的CPU时间片相对多一些

NOTE:

  1. Java使用的是抢占式调度模型
  2. 假如计算机只有一个CPU,那么CPU在某一时刻只能执行一条指令,线程只有得到CPU时间片(也就是使用权),才可以执行指令
  3. 所以说线程程序的执行是 随机性 ,因为谁抢到CPU的使用权不一定

线程生命周期
线程生命周期.png

Java特殊打印流

package io;

import java.io.*;

/**
 * 特殊输入流:
 *  1、标准输入流
 *      System.in:通常,该流对应于键盘输入或由主机环境或用户指定的另一个输入源
 */
public class Standard {
    public static void main(String[] args) throws IOException{
//        systemIn();
        print();
    }

    /**
     * 标准输入流:
     * System.in
     * 通常,该流对应于键盘输入或由主机环境或用户指定的另一个输入源
     */
    public static void systemIn()throws IOException {
        //标准输入流
        InputStream is = System.in;
        //字符流
        InputStreamReader isr = new InputStreamReader(is);
        //字符缓冲流
        BufferedReader br = new BufferedReader(isr);

        //读取输入内容
        System.out.println(br.readLine());

        //将读取的内容转换整型 注意:需要输入数字
        //System.out.println(Integer.parseInt(br.readLine()));
    }

    /**
     * 标准输出流:
     * System.out
     */
    public static void systemOut() {
        //这个 没什么好说的
        //System.out.println();

    }

    /**
     * 字节打印流:
     * PrintStream
     * 该类打印到文件的内容: 输出即所得,输入什么就写入什么
     */
    public static void print() throws IOException{
        //字节打印流:操作文件
        PrintStream ps = new PrintStream("P:\\demo\\ccccc.txt");
        //打印内容
        ps.print("---打印开始---");
        //打印换行符
        ps.println();
        //打印 int
        ps.print(100);
        ps.println();
        //打印 float
        ps.print(100.99);
        ps.println();
        //打印 char
        ps.print('a');
        ps.println();
        ps.print("---打印结束---");
    }
}

特殊字符打印.png

Java字符流操作

package io;

import java.io.*;

/**
 * 字符流操作
 * InputStreamReader:是从字节流到字符流的桥梁
 * 他读取字节,并使用指定的编码将其接吗为字符
 * 使用的字符集可以由名称指定,也可以被明确指定,或者使用平台默认的字符集
 * 
 * OutputStreamWrite:是从字符流到字节流的桥梁
 * 使用指定的编码将写入的字符编码为字节
 * 字符集可以由名称指定,也可以被明确指定,或者使用平台默认的字符集
 */
public class IODemo2 {
    public static void main(String[] args) {

//        writeBase();
//        readBase();
//        buffwritedBase();
        buffreadBase();

    }

    /**
     * 字符读取操作
     */
    public static void readBase() {
        try {
            //字符读取操作 指定字符集
            InputStreamReader isr = new InputStreamReader(new FileInputStream("P:\\demo\\aaaaa.txt"), "UTF-8");

            //读取一个
            int ch;
            while ((ch = isr.read()) != -1) {
                System.out.print((char) ch);
            }

            System.out.println("\r\n");

            //读取多个
            char[] chs = new char[1024];
            int len;
            while ((len = isr.read(chs)) != -1) {
                System.out.print(new String(chs, 0, len));
            }

            isr.close();
        } catch (IOException e) {
            System.out.println(e);
        }
    }

    /**
     * OutputStreamWriter:
     * 字符写入操作
     * write : 写入数据(写入的数据暂存在缓冲区)
     * flush : 刷新缓冲区(将缓冲区数据写入到文件)
     * close : 关闭资源(关闭前刷新缓冲区)
     */
    public static void writeBase() {
        try {
            //字符写入操作 指定字符集
            OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("P:\\demo\\aaaaa.txt"), "UTF-8");
            osw.write("我喜欢编程");//直接写入字符
            osw.write("\r\n");//直接写入字符
            osw.write("我喜欢Java");//直接写入字符
//            osw.close();

            osw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 字符缓冲流 写入数据
     * newLine:根据当前平台写入自适应的换行符号
     */
    public static void buffwritedBase(){
        try{
            BufferedWriter bw = new BufferedWriter(new FileWriter("P:\\demo\\bbbbbb.txt"));

            //写入数据
            for (int i = 0; i < 10; i++){
                bw.write("Hello"+i);
                bw.newLine();//自适应平台的换行符,该方法是字符缓冲流类(BufferedWriter)的特有方法
                bw.flush();
            }


            bw.close();
        }catch (IOException e){
            System.out.println(e);
        }
    }

    /**
     * 字符缓冲流 读取数据
     * readLine:每次读取一行内容(不包括换行符),返回 null 表示结束
     */
    public static void buffreadBase() {
        try{
            BufferedReader br = new BufferedReader(new FileReader("P:\\demo\\bbbbbb.txt"));
            //读取数据
            String str;
            while ((str = br.readLine()) != null){
                System.out.println(str);
            }

            br.close();
        }catch(IOException e){
            
        }
    }
}

Java文件读写和复制

package io;

import java.io.*;
import java.nio.charset.StandardCharsets;

/**
 * IO流概述和分类
 * IO流概述:
 * 1、IO:输入/输出(Input/Output)
 * 2、流:是一种抽象的概念,是对数据传输的总称,也就是说数据在设备间的传输称为流,流的本质是数据传输
 * 3、IO流就是用来处理设备间数据传输问题的(如:文件上传、下载、复制等操作)
 *
 * IO流分类:
 * 1、按照数据的流向分为:
 *      输入流:读数据
 *      输出流:写数据
 * 2、按照数据类型来分:
 *      字节流:
 *      字节输入流、字节输出流
 *      字符流:
 *      字符输入流、字符输出流
 *
 *      NOTE:
 *      1、可以通过普通编辑器打开并编辑的文件就是字符流(如txt文件)
 *      2、图像、视频等无法用普通编辑器编辑的文件就是字节流
 *      3、如果不知道具体属于什么数据类型,就用字节流
 * 3、字节流写入数据:
 *      字节流抽象基类:
 *      1、InputStream:这个抽象类是表示字节输入流的所有类的超类
 *      2、OutputStream:这个抽象类是表示字节输出流的所有类的超类
 *      3、字节流抽象类子类的特点:子类名称都是以其父类名作为子类名的后缀(如:FileInputStream)
 *      FileOutputStream:
 *      1、创建文件输出流以指定的名称写入文件
 * 4、字节缓冲流:
 *      字节缓存流:
 *      BufferOutptStream:该类实现缓冲输出流,通过设置,应用可以向底层输出流写入字节,然后一次调用即可输出流的内容
 *      BufferedIlputStream:该类将创建一个内部缓冲区数组,当从流中读取或跳过字节时,内部缓冲区将根据所包含的输入流中从新填充,一次很多字节
 *
 */

public class IoDemo {

    public static void main(String[] args) throws IOException {
        /*byteBaseWrite();
        byteBaseRead();
        copyBase();
        copyImg();*/

//        buffDemo();
        copyVideo();
    }

    /**
     * 字节流的写入基本操作
     * @throws IOException
     */
    public static void byteBaseWrite() {

        FileOutputStream fi = null;
        try {
            //构造方法需要指定一个资源路径
            boolean append = true;//如果指定第二个参数为true 则在原文件后面追加内容
            fi = new FileOutputStream("P:\\demo\\ddddd.txt",append);

            //写入单个数据 注意:参数是 int 类型
            fi.write(97);//注意:这是字节操作,因此97对应的是字符的a,所以文件中被写入了一个a

            //换行
            fi.write("\r\n".getBytes(StandardCharsets.UTF_8));

            //写入多个数据 注意:参数是byte数组
            byte[] bytes = "我喜欢Java".getBytes(StandardCharsets.UTF_8);//将字符串转换为byte字节
            fi.write(bytes);//我喜欢Java


        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //操作完后一定要关闭资源
            if (fi != null) {
                try {
                    fi.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 字节流的读取基本操作
     */
    public static void byteBaseRead() {
        FileInputStream fi = null;
        try {
            fi = new FileInputStream("P:\\demo\\ddddd.txt");
            //一次读取一个字节
            /*int read;
            while ((read = fi.read()) != -1){
                System.out.print(read);
            }*/

            //一次读取一个字节数组
            byte[] bys = new byte[5];//字母和数字:一个字节, 汉字:GBK 2个字节 UTF8 3个字节
            int len = fi.read(bys);//从该输入流读取最多 b.length 个字节的数据到一个字节数组

            System.out.println(new String(bys,0,len));

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 复制文件:
     *      其实就是将一个文件的数据复制到另一个文件
     *
     *      FileInputStream:打开文件,读取其中数据
     *      FileOutputStream:创建新文件,并将上面读取到的数据写入
     */
    public static void copyBase() {

        FileInputStream fi = null;
        FileOutputStream fi1 = null;
        try {
            //打开文件
            fi = new FileInputStream("P:\\demo\\ddddd.txt");

            //创建文件
            fi1 = new FileOutputStream("P:\\demo\\bbbbbbb.txt");

            //读取打开的文件内容,将内容写入到新文件
            int by;
            while ((by = fi.read()) != -1){
                fi1.write(by);
            }


        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //操作完后一定要关闭资源
            if (fi != null) {
                try {
                    fi.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

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

    }

    /**
     * 复制图片
     */
    public static void copyImg() {
        //打开文件
        FileInputStream fi = null;
        //写入文件
        FileOutputStream fos = null;
        try {
            //打开文件
            fi = new FileInputStream("C:\\Users\\admin\\Pictures\\IntelliJ IDEA 构建Java项目06.png");
            //写入文件
            fos = new FileOutputStream("C:\\Users\\admin\\Pictures\\ttttt.png");

            //读取/写入文件 单个字节
            int len;
            while ((len = fi.read()) != -1) {
                fos.write(len);
            }

            //读取/写入文件 字节数组
            byte[] bys = new byte[1024];
            int lens;
            while ((lens = fi.read(bys)) != -1) {
                fos.write(bys,0,lens);
            }


        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 字节缓冲输出流和输入流
     */
    public static void buffDemo() {

        BufferedOutputStream bos = null;
        BufferedInputStream bis = null;
        try{
            //创建缓冲输出流
            bos = new BufferedOutputStream(new FileOutputStream("P:\\demo\\aaaaa.txt"));
            //写入数据
            bos.write("我喜欢Java,还喜欢Linux".getBytes(StandardCharsets.UTF_8));
            //释放资源
            bos.close();

            //创建缓冲输入流
            bis = new BufferedInputStream(new FileInputStream("P:\\demo\\aaaaa.txt"));
            //读取数据
            byte[] bys = new byte[1024];
            int len;
            while ((len = bis.read(bys)) != -1) {
                System.out.println(new String(bys,0,len));
            }

            //释放资源
            bis.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    /**
     * 复制视频
     */
    public static void copyVideo() {
        BufferedInputStream fis = null;
        BufferedOutputStream fos = null;

        try{
            //打开文件
            fis = new BufferedInputStream(new FileInputStream("C:\\Users\\admin\\Videos\\video_2020-12-18_17-34-09.mp4"));
            //准备写入的文件
            fos = new BufferedOutputStream(new FileOutputStream("C:\\Users\\admin\\Videos\\video.mp4"));

            //读取文件/写入文件
            byte[] bys = new byte[1024];
            int len;
            while ((len = fis.read(bys)) != -1) {
                System.out.println(len);
                fos.write(bys,0,len);
            }

            fos.close();
            fis.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

Java字节流操作

package io;

import java.io.*;
import java.nio.charset.StandardCharsets;

/**
 * IO流概述和分类
 * IO流概述:
 * 1、IO:输入/输出(Input/Output)
 * 2、流:是一种抽象的概念,是对数据传输的总称,也就是说数据在设备间的传输称为流,流的本质是数据传输
 * 3、IO流就是用来处理设备间数据传输问题的(如:文件上传、下载、复制等操作)
 *
 * IO流分类:
 * 1、按照数据的流向分为:
 *      输入流:读数据
 *      输出流:写数据
 * 2、按照数据类型来分:
 *      字节流:
 *      字节输入流、字节输出流
 *      字符流:
 *      字符输入流、字符输出流
 *
 *      NOTE:
 *      1、可以通过普通编辑器打开并编辑的文件就是字符流(如txt文件)
 *      2、图像、视频等无法用普通编辑器编辑的文件就是字节流
 *      3、如果不知道具体属于什么数据类型,就用字节流
 * 3、字节流写入数据:
 *      字节流抽象基类:
 *      1、InputStream:这个抽象类是表示字节输入流的所有类的超类
 *      2、OutputStream:这个抽象类是表示字节输出流的所有类的超类
 *      3、字节流抽象类子类的特点:子类名称都是以其父类名作为子类名的后缀(如:FileInputStream)
 *      FileOutputStream:
 *      1、创建文件输出流以指定的名称写入文件
 * 4、字节缓冲流:
 *      字节缓存流:
 *      BufferOutptStream:该类实现缓冲输出流,通过设置,应用可以向底层输出流写入字节,然后一次调用即可输出流的内容
 *      BufferedIlputStream:该类将创建一个内部缓冲区数组,当从流中读取或跳过字节时,内部缓冲区将根据所包含的输入流中从新填充,一次很多字节
 *
 */

public class IoDemo {

    public static void main(String[] args) throws IOException {
        /*byteBaseWrite();
        byteBaseRead();
        copyBase();
        copyImg();*/

//        buffDemo();
        copyVideo();
    }

    /**
     * 字节流的写入基本操作
     * @throws IOException
     */
    public static void byteBaseWrite() {

        FileOutputStream fi = null;
        try {
            //构造方法需要指定一个资源路径
            boolean append = true;//如果指定第二个参数为true 则在原文件后面追加内容
            fi = new FileOutputStream("P:\\demo\\ddddd.txt",append);

            //写入单个数据 注意:参数是 int 类型
            fi.write(97);//注意:这是字节操作,因此97对应的是字符的a,所以文件中被写入了一个a

            //换行
            fi.write("\r\n".getBytes(StandardCharsets.UTF_8));

            //写入多个数据 注意:参数是byte数组
            byte[] bytes = "我喜欢Java".getBytes(StandardCharsets.UTF_8);//将字符串转换为byte字节
            fi.write(bytes);//我喜欢Java


        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //操作完后一定要关闭资源
            if (fi != null) {
                try {
                    fi.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 字节流的读取基本操作
     */
    public static void byteBaseRead() {
        FileInputStream fi = null;
        try {
            fi = new FileInputStream("P:\\demo\\ddddd.txt");
            //一次读取一个字节
            /*int read;
            while ((read = fi.read()) != -1){
                System.out.print(read);
            }*/

            //一次读取一个字节数组
            byte[] bys = new byte[5];//字母和数字:一个字节, 汉字:GBK 2个字节 UTF8 3个字节
            int len = fi.read(bys);//从该输入流读取最多 b.length 个字节的数据到一个字节数组

            System.out.println(new String(bys,0,len));

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 复制文件:
     *      其实就是将一个文件的数据复制到另一个文件
     *
     *      FileInputStream:打开文件,读取其中数据
     *      FileOutputStream:创建新文件,并将上面读取到的数据写入
     */
    public static void copyBase() {

        FileInputStream fi = null;
        FileOutputStream fi1 = null;
        try {
            //打开文件
            fi = new FileInputStream("P:\\demo\\ddddd.txt");

            //创建文件
            fi1 = new FileOutputStream("P:\\demo\\bbbbbbb.txt");

            //读取打开的文件内容,将内容写入到新文件
            int by;
            while ((by = fi.read()) != -1){
                fi1.write(by);
            }


        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //操作完后一定要关闭资源
            if (fi != null) {
                try {
                    fi.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

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

    }

    /**
     * 复制图片
     */
    public static void copyImg() {
        //打开文件
        FileInputStream fi = null;
        //写入文件
        FileOutputStream fos = null;
        try {
            //打开文件
            fi = new FileInputStream("C:\\Users\\admin\\Pictures\\IntelliJ IDEA 构建Java项目06.png");
            //写入文件
            fos = new FileOutputStream("C:\\Users\\admin\\Pictures\\ttttt.png");

            //读取/写入文件 单个字节
            int len;
            while ((len = fi.read()) != -1) {
                fos.write(len);
            }

            //读取/写入文件 字节数组
            byte[] bys = new byte[1024];
            int lens;
            while ((lens = fi.read(bys)) != -1) {
                fos.write(bys,0,lens);
            }


        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 字节缓冲输出流和输入流
     */
    public static void buffDemo() {

        BufferedOutputStream bos = null;
        BufferedInputStream bis = null;
        try{
            //创建缓冲输出流
            bos = new BufferedOutputStream(new FileOutputStream("P:\\demo\\aaaaa.txt"));
            //写入数据
            bos.write("我喜欢Java,还喜欢Linux".getBytes(StandardCharsets.UTF_8));
            //释放资源
            bos.close();

            //创建缓冲输入流
            bis = new BufferedInputStream(new FileInputStream("P:\\demo\\aaaaa.txt"));
            //读取数据
            byte[] bys = new byte[1024];
            int len;
            while ((len = bis.read(bys)) != -1) {
                System.out.println(new String(bys,0,len));
            }

            //释放资源
            bis.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    /**
     * 复制视频
     */
    public static void copyVideo() {
        BufferedInputStream fis = null;
        BufferedOutputStream fos = null;

        try{
            //打开文件
            fis = new BufferedInputStream(new FileInputStream("C:\\Users\\admin\\Videos\\video_2020-12-18_17-34-09.mp4"));
            //准备写入的文件
            fos = new BufferedOutputStream(new FileOutputStream("C:\\Users\\admin\\Videos\\video.mp4"));

            //读取文件/写入文件
            byte[] bys = new byte[1024];
            int len;
            while ((len = fis.read(bys)) != -1) {
                System.out.println(len);
                fos.write(bys,0,len);
            }

            fos.close();
            fis.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}