java进阶语法

参考(黑马):https://www.bilibili.com/video/BV1Cv411372m/

特殊文件

Properties属性文件

在这里插入图片描述

  • Properties是一个Map集合(键值对集合),但是我们一般不会当集合使用。
  • 核心作用:Properties是用来代表属性文件的,通过Properties可以读写属性文件里的内容。

Properties读取键值对

在这里插入图片描述

PS:

  • 如果要在控制台显示,load()方法最好接受字符流
    public static void main(String[] args) throws Exception {
        //1.创建一个Properties对象(空容器)
        Properties properties = new Properties();

        //2.开始加载属性文件中的键值对数据到properties中去
        properties.load(new FileReader("src\\SpecialFile\\users.properties"));
        System.out.println(properties);

        //3.根据键取值
        System.out.println("0:"+properties.getProperty("李笑"));

        //遍历全部键和值
        Set<String> keys = properties.stringPropertyNames();
        for(String key : keys){
            System.out.println("2:"+key+"-->"+properties.getProperty(key));
        }
    }

Properties写出键值对

在这里插入图片描述

PS:

  • store方法中的第二个参数comments仅仅只是个注释
    public static void main(String[] args) throws Exception {
        //1.创建一个Properties对象(空容器)
        Properties properties = new Properties();

        properties.setProperty("李笑","兰大");
        properties.store(new FileWriter("\"src\\\\SpecialFile\\\\users2.properties\""),"i save Properties File");


        //遍历全部键和值
        Set<String> keys = properties.stringPropertyNames();
        for(String key : keys){
            System.out.println("2:"+key+"-->"+properties.getProperty(key));
        }
    }

练习

调用Properties继承的Map方法,判断键值知否包含某一Key,并修改对象value。

    public static void main(String[] args) throws Exception {
        //1.创建一个Properties对象(空容器)
        String fileName = "data\\test_user.txt";
        Properties properties = new Properties();
        properties.load(new FileReader(fileName));

        //是Map的继承,当然包含map方法!!
        if(properties.containsKey("李笑")){
            properties.setProperty("李笑", "25");
        }

        properties.store(new FileWriter(fileName),"李笑年龄更改");
    }

XML文件

  • XML(可扩展标记语言)
  • XML 中的“<标签名>”称为一个标签或是一个元素。一般是成对出现的
  • XML中的标签名可以自己定义(可扩展),但必须要正确的嵌套。
  • XML中只能有一个根标签。
  • XML中的标签可以有属性
    在这里插入图片描述
<?xml version="1.0" encoding="UTF-8" ?>

<!--注释:以上头声明必须放在第一行,必须有-->

<users>           <!--根标签只能有一个-->
    <user id = "1">        <!-- 子标签-->
        <name>李笑</name>
        <sex></sex>
        <password>minmin</password>
        <data> 2 &lt; 5 </data>
        <data2>
            <!--特殊数据区-->
            <![CDATA[
                2< 5
            ]]>
        </data2>
    </user>
    <user id = "2">        <!-- 子标签-->
        <name>笑笑</name>
        <sex></sex>
        <password>minmin</password>
    </user>
</users>

读取XML(Dmo4j框架)

  1. DOM4J概述
    在这里插入图片描述

  2. 常用方法

  • 拿根元素
    在这里插入图片描述
    public static void main(String[] args) throws Exception {
        //1.创建Dom4J框架提供的解析器对象
        SAXReader saxReader = new SAXReader();

        //2.使用saxReaderd对象把需要解析的XML文件读成一个Document对象
        Document document =
                saxReader.read("src\\helloworld.xml");

        //3.从文档对象中解析XML数据
        Element root = document.getRootElement();
        System.out.println(root.getName());
    }
  • 取下一级对象
    在这里插入图片描述
        //1.创建Dom4J框架提供的解析器对象
        SAXReader saxReader = new SAXReader();

        //2.使用saxReaderd对象把需要解析的XML文件读成一个Document对象
        Document document =
                saxReader.read("src\\helloworld.xml");

        //3.从文档对象中解析XML数据
        Element root = document.getRootElement();
        System.out.println(root.getName());

        //4.获取根元素下的全部一级子元素
        List<Element> elements = root.elements();
        for(Element element:elements){
            System.out.println(element.getName());
        }

        //5.获取某个子元素
        Element people = root.element("people");
        System.out.println(people.getText());

        //有很多user标签,默认获取第一个
        Element user = root.element("user");
        System.out.println(user.elementText("name"));

        //6.获取元素属性信息
        System.out.println(user.attributeValue("id"));
        Attribute id = user.attribute("id");
        System.out.println(id.getName());
        System.out.println(id.getValue());

        //获取元素全部属性
        List<Attribute> attributes = user.attributes();

        //7.获取全部文本内容
        System.out.println(user.elementText("name"));
        System.out.println(user.elementText("password"));

        Element data = user.element("data");
        System.out.println(data.getText());
        System.out.println(data.getTextTrim());  //取出文本删除欠货空格
    }

写出XML(不建议用Dmo4j框架)

  • 推荐直接把程序里的数据拼接成XML格式,然后用IO流写出去!
    在这里插入图片描述

约束XML文件的编写

  • 什么是约束XML书写:就是限制XML文件只能按照某种格式进行书写。
  • 约束文档:专门用来限制xml书写格式的文档,比如:限制标签、属性应该怎么写
    在这里插入图片描述
  • DTD约束文档
    在这里插入图片描述
  • Schema约束文档
    在这里插入图片描述

日志技术

体系结构

在这里插入图片描述

Logback框架

核心模块

在这里插入图片描述

导入方式

  • 需要自行寻找三个包
    在这里插入图片描述

PS:
对Logback日志框架的控制,都是通过其核心配置文件logback.xml来实现的,

核心配置文件logback.xml

  1. 日志输出位置、输出格式设置
  • 通常可以设置2个输出日志的位置:一个是控制台、一个是系统文件
    在这里插入图片描述
  • 例子
    (控制台)
    在这里插入图片描述
    (系统文件)
    在这里插入图片描述
  1. 开启日志、取消日志
    在这里插入图片描述
  • 例子
    在这里插入图片描述

Logback日志级别

  • 日志级别指的是日志信息的类型,日志都会分级别,常见的日志级别如下(优先级依次升高):
    在这里插入图片描述
  • 设置日志级别(在核心配置文件中配置)
    在这里插入图片描述

多线程

线程创建

方式一:继承Thread类

在这里插入图片描述

优缺点

  • 优点:编码简单
  • 缺点:线程类以及继承Thread,无法继承其他类,不利于功能的扩展

PS:
在这里插入图片描述

    //main()方法是由一条默认的主线程负责执行
    public static void main(String[] args) {
        //3.创建对象
        MyThread thread = new MyThread();
        //4.启动线程
        thread.start();  //main线程 thread线程

        for (int i = 0; i < 5; i++) {
            System.out.println("主线程main输出:"+ i);
        }
    }
//1.自定义子类继承Thread线程类
public class MyThread extends Thread{
    //2.必须重写run方法
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程MyThread输出:"+ i);
        }
    }
}

方式二:实现Runnable接口

在这里插入图片描述

优缺点

  • 优点:任务类只是实现接口,可以继续继承其他类、实现其他接口,扩展性强,
  • 缺点:需要多一个Runnable对象
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程:"+i);
        }
    }
}
    public static void main(String[] args) {
        Runnable target = new MyRunnable();

        //把任务对象交给一个线程对象处理!!!
        new Thread(target).start();

        for (int i = 0; i < 5; i++) {
            System.out.println("主线程:"+i);
        }
    }
  • 匿名内部类写法
    在这里插入图片描述
    public static void main(String[] args) {
        Runnable target = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("子线程1-->:"+i);
                }
            }
        };
        new Thread(target).start();

        //简化形式1:
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("子线程2-->:"+i);
                }
            }
        }).start();

        //简化形式
        new Thread(()-> {
                for (int i = 0; i < 5; i++) {
                    System.out.println("子线程3-->:"+i);
                }
        }).start();

        for (int i = 0; i < 5; i++) {
            System.out.println("主线程-->:"+i);
        }
    }

方式三:实现Callable接口

在这里插入图片描述

  • 方法
    在这里插入图片描述
  • FutureTask API
    在这里插入图片描述

优缺点

  • 优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强;可以在线程执行完毕后去获取线程执行的结果
  • 缺点:编码复杂
public class MyCallable implements Callable<String> {
    private int n;

    public MyCallable(int n) {
        this.n = n;
    }

    @Override
    public String call() throws Exception {
        //描述线程任务,并返回线程执行后的结果
        //需求:求1~n的和
        int sum = 0;
        for (int i = 1; i <= n ; i++) {
            sum += i;
        }
        return "1~n的和为:"+sum;
    }
}
    public static void main(String[] args) throws Exception{
        Callable<String> call = new MyCallable(100);

        //未来任务对象
        //1.是一个任务对象,实现了Runnable接口
        //2.可以在线程结束后,用FutureTask的get方法拿到返回结果
        FutureTask<String> f1 = new FutureTask<>(call);

        new Thread(f1).start();

        //如果执行到这,上面线程还未执行完毕,这里代码会暂停,等待线程执行完毕
        String rs = f1.get();
        System.out.println(rs);
    }

Thread常用方法

在这里插入图片描述

  • 取线程名字
public class MyThread extends Thread{
    
    public MyThread(){}
    public MyThread(String s) {
        super(s);  //调用父类Thread的有参构造器
    }

    @Override
    public void run() {
        Thread n = Thread.currentThread();
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程"+n.getName()+"输出:"+ i);
        }
    }
}
    public static void main(String[] args) {
        Thread t1 = new MyThread();
        t1.setName("1号线程");
        t1.start();
        System.out.println(t1.getName());  //1号线程
        Thread t2 = new MyThread();
        t2.start();
        System.out.println(t2.getName());  //Thread-1

        Thread t3 = new MyThread("3号线程");
        t3.start();
        System.out.println(t3.getName());  //3号线程

        //主线程对象的名字
        //哪个线程执行它,他就会得到哪个线程的名字
        Thread n = Thread.currentThread();
        System.out.println(n.getName());   //main

        for (int i = 0; i < 5; i++) {
            System.out.println("主线程main输出:"+ i);
        }
    }

线程安全

出现原因:

  • 存在多个线程在同时执行
  • 同时访问一个共享资源
  • 存在修改该共享资源

案例 (出现安全问题)

在这里插入图片描述

  • Account类
ublic class Account {
    private double money;
    private String carID;

    public Account() {
    }

    public Account(double money, String carID) {
        this.money = money;
        this.carID = carID;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public String getCarID() {
        return carID;
    }

    public void setCarID(String carID) {
        this.carID = carID;
    }

    //小明、小红同时来取钱
    public void drawMoney(double money) {
        //先搞清楚谁来取钱
        String name = Thread.currentThread().getName();
        if(this.money>=money){
            System.out.println(name+"来取钱"+money+"成功");
            this.money -= money;
            System.out.println(name+"取钱后,余额:"+this.money);
        }else {
            System.out.println("余额不足");
        }
    }
}
  • DrawThread类
public class DrawThread extends Thread{
    private Account acc;
    public DrawThread(Account acc, String name){
        super(name);
        this.acc = acc;
    }

    public DrawThread(Account acc) {
        this.acc = acc;
    }

    @Override
    public void run() {
        acc.drawMoney(10000);
    }
}
  • 主程序
    public static void main(String[] args) {
        //1.创建一个对象,代表两个人共享账户
        Account acc = new Account(10000, "ICBC-110");

        //2.创建两个线程分别代表两人,两种不同设置线程名字的方式
        Thread n1 = new DrawThread(acc); //小明
        n1.setName("小明");
        n1.start();

        new DrawThread(acc,"小红").start(); //小红
    }

结果:
在这里插入图片描述

线程同步

常见方案

  • 加锁:每次只允许一个线程加锁,加锁后才能进入访问,访问完毕后自动解锁,然后其他线程才能再加锁进来。
    在这里插入图片描述

方式一:同步代码块

  • 作用:把访问共享资源的核心代码给上锁,以此保证线程安全。(把响应代码块上锁
  • 原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行
  • 注意:对于当前同时执行的线程来说,同步锁必须是同一把(同一个对象),否则会出bug。
    在这里插入图片描述
  • 案例解决——Account类(改)

同步锁

  1. 使用字符串"lx"作为同步锁,因为" " 括起来的对象在字符串池只有一份(不好)
  2. 如果在实例方法中,使用共享资源作为锁,本案例为:this
  3. 如果在静态方法中,使用 类名.class 作为锁,本案例为Account.class(因为Accunt.class为字节码文件,只有一份)
public class Account {
    private double money;
    private String carID;

    public Account() {
    }

    public Account(double money, String carID) {
        this.money = money;
        this.carID = carID;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public String getCarID() {
        return carID;
    }

    public void setCarID(String carID) {
        this.carID = carID;
    }
    
    public static void s_func(){   //静态方法
        synchronized (Account.class){
            
        }
    }

    //小明、小红同时来取钱
    public void drawMoney(double money) {
        //先搞清楚谁来取钱
        String name = Thread.currentThread().getName();
        //同步锁
        synchronized (this){   //共享资源作为锁
            if(this.money>=money){
                System.out.println(name+"来取钱"+money+"成功");
                this.money -= money;
                System.out.println(name+"取钱后,余额:"+this.money);
            }else {
                System.out.println("余额不足");
            }
        }
    }
}

结果:
在这里插入图片描述

方式二:同步方法

  • 作用:把访问共享资源的核心方法给上锁,以此保证线程安全。(把整个方法上锁

  • 原理:每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行。
    在这里插入图片描述

  • 底层原理:内部含有隐式锁,实例方法使用this,静态方式使用 类名.class

  • 案例解决——Account类(改)

public class Account {
    private double money;
    private String carID;

    public Account() {
    }

    public Account(double money, String carID) {
        this.money = money;
        this.carID = carID;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public String getCarID() {
        return carID;
    }

    public void setCarID(String carID) {
        this.carID = carID;
    }


    public synchronized static void s_func(){

    }

    //小明、小红同时来取钱
    public synchronized void drawMoney(double money) {
        //先搞清楚谁来取钱
        String name = Thread.currentThread().getName();
        if(this.money>=money){
            System.out.println(name+"来取钱"+money+"成功");
            this.money -= money;
            System.out.println(name+"取钱后,余额:"+this.money);
        }else {
            System.out.println("余额不足");
        }
    }
}

同步代码块 vs 同步方法哪个好?

  • 范围上::同步代码块锁的范围更小,同步方法锁的范围更大。锁的范围越小,性能越好。(但当今电脑性能,可以忽略不记)
  • 可读性上:同步方法更好

方式三:Lock锁

  • 是JDK5开始提供的一个新的锁定操作,通过它可以创建出锁对象进行加锁和解锁,更灵活、更方便、更强大。

  • Lock是接口,不能直接实例化,可以采用它的实现类ReentrantLock来构建Lock锁对象。
    在这里插入图片描述

  • 案例解决——Account类(改)

public class Account {
    private double money;
    private String carID;
    
	//锁对象,final修饰防止修改
    private final Lock lk = new ReentrantLock();  

    public Account() {
    }

    public Account(double money, String carID) {
        this.money = money;
        this.carID = carID;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    public String getCarID() {
        return carID;
    }

    public void setCarID(String carID) {
        this.carID = carID;
    }



    //小明、小红同时来取钱
    public void drawMoney(double money) {
        //先搞清楚谁来取钱
        String name = Thread.currentThread().getName();
        try {
            lk.lock();  //加锁
            if(this.money>=money){
                System.out.println(name+"来取钱"+money+"成功");
                this.money -= money;
                System.out.println(name+"取钱后,余额:"+this.money);
            }else {
                System.out.println("余额不足");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {             //防止中间代码出bug,没有解锁,导致别的线程无法继续执行
            lk.unlock();  //解锁
        }
    }
}

线程通信

  • 等待、唤醒方法
    在这里插入图片描述

  • 案例:三个生产者,每次生成一个包子放在桌子上,且桌子上只能由一个包子;两个消费者,每人每次只能取一个吃

主程序

public class ProduceConsumeT {
    public static void main(String[] args) {
        Desk desk = new Desk();

        //创建三个生产者线程
        new Thread(()->{
            while(true){
                desk.put();
            }
        },"厨师1").start();

        new Thread(()->{
            while(true){
                desk.put();
            }
        },"厨师2").start();

        new Thread(()->{
            while(true){
                desk.put();
            }
        },"厨师3").start();

        //创建两个消费者线程
        new Thread(()->{
            while(true){
                desk.get();
            }
        },"吃货1").start();

        new Thread(()->{
            while(true){
                desk.get();
            }
        },"吃货2").start();
    }
}

Desk类

public class Desk {
    private List<String> list = new ArrayList<>();

    public synchronized void put(){
        try {
            String name =  Thread.currentThread().getName();
            if(list.size() == 0){
                Thread.sleep(2000);
                System.out.println(name+"做了一个肉包子");
                list.add(name+"的肉包子");

                //唤醒别人,等待自己
                this.notifyAll();
                this.wait();
            }else{
                this.notifyAll();
                this.wait();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public synchronized void get(){
        String name = Thread.currentThread().getName();
        try {
            if(list.size() != 0){
                Thread.sleep(1000);
                System.out.println(name+"吃了"+list.get(0));
                list.clear();

                //唤醒别人,等待自己
                this.notifyAll();
                this.wait();
            }else{
                this.notifyAll();
                this.wait();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

线程池

概念

  • 线程池就是一个可以复用线程的技术
    在这里插入图片描述
  • 工作原理
    在这里插入图片描述

PS:任务必须实现任务接口,成为任务对象

创建线程池

  • JDK5.0起提供了代表线程池的接口:ExecutorService
  • 如何获得线程池对象?
    在这里插入图片描述

PS:方法二见后

  • TreadPoolExecutor
    在这里插入图片描述
    public static void main(String[] args) {
        //1.创建线程池对象
        /*
        int corePoolSize,
          int maximumPoolSize,
          long keepAliveTime,
          TimeUnit unit,
          BlockingQueue<Runnable> workQueue,
          ThreadFactory threadFactory,
          RejectedExecutionHandler handle
         */
        ExecutorService pool = new ThreadPoolExecutor(3, 5, 8, TimeUnit.SECONDS,
                               new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
                                new ThreadPoolExecutor.AbortPolicy());
    }
  • 注意事项

任务队列是指处于等待任务的队列,被线程执行的任务不在任务队列

在这里插入图片描述

  • 新任务拒绝策略
    在这里插入图片描述

线程池处理Runnable任务

  • 方法
    在这里插入图片描述
    主程序
    public static void main(String[] args) {
        //1.创建线程池对象
        ExecutorService pool = new ThreadPoolExecutor(3, 5, 8, TimeUnit.SECONDS,
                               new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
                                new ThreadPoolExecutor.AbortPolicy());

        //2.
        Runnable target = new MyRunnable();
        pool.execute(target);  //线程池自动创建一个新线程、自动处理整个任务,自动执行!
        pool.execute(target);  //线程池自动创建一个新线程、自动处理整个任务,自动执行!
        pool.execute(target);  //线程池自动创建一个新线程、自动处理整个任务,自动执行!
        pool.execute(target);  //复用核心线程
    }

MyRunnable类

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        //描述任务
        System.out.println(Thread.currentThread().getName()+":输出666");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

线程池处理Callable任务

  • 方法
    在这里插入图片描述
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService pool = new ThreadPoolExecutor(3, 5, 8, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        Future<String> f1 = pool.submit(new MyCallable(100));
        Future<String> f2 = pool.submit(new MyCallable(200));
        Future<String> f3 = pool.submit(new MyCallable(300));
        Future<String> f4 = pool.submit(new MyCallable(400));

        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());
    }

Executors工具类实现线程池

  • 是一个线程池的工具类,提供了很多静态方法用于返回不同特点的线程池对象
  • 常用线程池
    在这里插入图片描述

注意:这些方法的底层,都是通过线程池的实现类ThreadPoolExecutor创建的线程池对象

    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(3);


        Future<String> f1 = pool.submit(new MyCallable(100));
        Future<String> f2 = pool.submit(new MyCallable(200));
        Future<String> f3 = pool.submit(new MyCallable(300));
        Future<String> f4 = pool.submit(new MyCallable(400));

        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());
    }

线程池核心线程数配置多少?

  • 计算密集型任务:CPU核心数+1
  • IO密集型任务:CPU核心数*2

Executors使用可能存在的陷阱
在这里插入图片描述

并发、并行

线程生命周期

  • Java线程的六种状态
    在这里插入图片描述

  • 线程的6种状态转换
    在这里插入图片描述

PS:

  • sleep方法不会释放同步锁
  • wait方法会释放同步锁

网络通信

  • java.net.*包下提供了网络编程解决方案

基本通信架构

  • 基本的通信架构有2种形式:CS架构(Client客户端/Server服务端)、BS架构(Browser浏览器/Server服务端

网络通信三要素

  • 三要素:IP、端口、协议

IP(InetAddress)

在这里插入图片描述

  • InetAddress:代表IP对象
  • 常用方法:
    在这里插入图片描述
    public static void main(String[] args) throws Exception {
        //获取本机IP
        InetAddress ip1 = InetAddress.getLocalHost();
        System.out.println(ip1.getHostName());  //本机名字
        System.out.println(ip1.getHostAddress());  //本机ip地址

        //获取指定IP或是域名的ip地址对象
        InetAddress ipBaidu = InetAddress.getByName("www.baidu.com");
        System.out.println(ipBaidu.getHostName());
        System.out.println(ipBaidu.getHostAddress());

        //判断主机与指定ip是否连通
        System.out.println(ipBaidu.isReachable(6000));
    }

端口

在这里插入图片描述

协议

在这里插入图片描述
UDP(User Datagram Protocol):用户数据报协议;
TCP(Transmission ControlProtocol):传输控制协议。

  • UDP
    在这里插入图片描述
  • TCP
    在这里插入图片描述

UDP通信-快速入门

  • Java提供了一个java.net.DatagramSocket类来实现UDP通信
  • DatagramSocket:
    在这里插入图片描述
  • DatagramPacket:
    在这里插入图片描述
  • 简单的通讯

在这里插入图片描述

在这里插入图片描述

客户端

public class Client {
    public static void main(String[] args) throws Exception{
        //1.创建客户端对象
        DatagramSocket socket = new DatagramSocket();

        //2.创建要发送的数据包对象
        /*
           public DatagramPacket(byte buf[], int offset, int length,
                          InetAddress address, int port)
            参数一:要发出去的数据
            参数二:发送出去的数据大小
            参数三:服务端IP地址
            参数四:服务端的端口
         */
        byte[] bytes = "我是快乐的客户端".getBytes();
        DatagramPacket packet = new DatagramPacket(bytes,bytes.length,
                                                    InetAddress.getLocalHost(),6666);

        //3.正式发送
        socket.send(packet);
        System.out.println("客户端数据发送完毕");
        socket.close();
    }
}

服务端

public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("------服务器启动------");
        //1.创建服务端对象
        DatagramSocket socket = new DatagramSocket(6666);

        //2.创建一个接受数据的数据包对象
        byte[] buffer = new byte[1024 * 64];
        DatagramPacket packet = new DatagramPacket(buffer,buffer.length);

        //3.正式使用数据包接受数据
        socket.receive(packet);

        //4.从字节数组中,把接收的数据答应输出
        int len = packet.getLength(); //接收多少,取出多少
        String rs = new String(buffer,0,len);
        System.out.println(rs);

		System.out.println(packet.getAddress().getHostAddress()); //客户端ip地址
        System.out.println(packet.getPort());  //客户端端口

        socket.close();
    }
}

结果
在这里插入图片描述

UDP通信-多发多收

客户端

public class Client {
    public static void main(String[] args) throws Exception{
        //1.创建客户端对象
        DatagramSocket socket = new DatagramSocket();

        //2.创建要发送的数据包对象
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请说:");
            String msg = sc.nextLine();

            if("exit".equals(msg)){
                socket.close();
                break;
            }
            byte[] bytes = msg.getBytes();
            DatagramPacket packet = new DatagramPacket(bytes,bytes.length,
                                                        InetAddress.getLocalHost(),6666);

            //3.正式发送
            socket.send(packet);
        }
        System.out.println("客户端关闭");
    }
}

服务端

public class Server {
    public static void main(String[] args) throws Exception {
        System.out.println("------服务器启动------");
        //1.创建服务端对象
        DatagramSocket socket = new DatagramSocket(6666);

        //2.创建一个接受数据的数据包对象
        byte[] buffer = new byte[1024 * 64];
        DatagramPacket packet = new DatagramPacket(buffer,buffer.length);

        while (true) {
            //3.正式使用数据包接受数据
            socket.receive(packet);

            //4.从字节数组中,把接收的数据答应输出
            int len = packet.getLength(); //接收多少,取出多少
            String rs = new String(buffer,0,len);
            System.out.println(rs);
            System.out.println("------------------------------");
        }
    }
}

结果
在这里插入图片描述

TCP通信-快速入门

java高级

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值