shuijibaobao

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
统计
 

多线程

并发:在同一时刻,有多个指令在单个CPU上交替执行
并行:在同一时刻,有多个指令在多个CPU上同时执行

实现方式

继承Thread类

//MyThread.java
    public class MyThread extends Thread{

    }

//test.java
    public static void main(String[] args){
//多线程的第一种启动方式:
// 1.自己定义一个类继承Thread
// 2.重写run方法
// 3。创建子类的对象,并启动线程
    MyThread t1 = new MyThread();
    MyThread t2 = new MyThread();

    tl.setName("线程1");
    t2.setName("线程2");
    
    t1.start();
    t2.start();
    }

实现Runnable接口

//MyRun.java
    public class MyRun implements Runnable{
        @Override
        public void run() {
            //书写线程要执行的代码
            for (int i = 0; i < 10; i++){
                //获取到当前线程的对象
                Thread t = Thread.currentThread();
                System.out.println(t.getName() + "HelloWorld!");
            }
        }
    }

//test.java

// 多线程的第二种启动方式:
// 1.自己定义一个类实现Runnable接口
// 2.重写里面的run方法
// 3.创建自己的类的对象
// 4.创建一个Thread类的对象,并开启线程

//创建MyRun的对象
//表示多线程要执行的任务
    MyRun mr = new MyRun()
//创建线程对象
    Thread t1 = new Thread(mr);
    Thread t2 = new Thread(mr);
//给线程设置名字
    t1.setName("线程1");
    t2.setName("线程2"):
//开启线程
    t1.start();
    t2.start();


利用Callable接口和Future接口

/*
多线程的第三种实现方式:
特点:可以获取到多线程运行的结果
    1.创建一个类MyCallable实现Callable接口
    2.重号ca11 (是有返回值的,表示多线程运行的结果)
    3.创建MyCallable的对象 (表示多线程要执行的任务)
    4.创建FutureTask的对象 (作用管理多线程运行的结果)
    5.创建Thread类的对象,并启动(表示线程)
*/
//创建MyCallable的对象 (表示多线程要执行的任务)
    MyCallable mc = new MyCallable();
//创建FutureTask的对象(作用管理多线程运行的结果)
    FutureTask<Integer> ft = new FutureTask<>(mc);

//创建线程的对象
    Thread t1 = new Thread(ft);

//启动线程
    tl.start();

//获取多线程运行的结果
    Integer result = ft.get();

    System.out.println(result);

img

常见的成员方法

img

基础方法

/*
    String getName()    返回此线程的名称
    void setName(String name)    设置线程的名字(构造方法也可以设置)
细节:
1、如果我们没有给线程设置名字,线程也是有默认的名字的格式: Thread-X(X字号,从0开始的)
*/

//MyThread.java
    public class MyThread extends Thread{
        public MyThread(){

        }
//重写
    public MyThread(String name){
        super(name);
    }

    @Override
    public void run(){
        for (int i = 0; i < 1; i++){
            System.out .printIn(getName() + "@" + i);
        }
    }
    }

//test.java
    MyThread t1 = new MyThread("飞");
    MyThread t2 = new MyThread("克");

    t1.start();
    t2.start();

/*
    static Thread currentThread()
细节:
获取当前线程的对象。
当JVM虚拟机启动之后,会自动的启动多条线程。
其中有一条线程就叫做main线程。
他的作用就是去调用main方法,并执行里面的代码。
在以前,我们写的所有的代码,其实都是运行在main线程当中。
*///    static void sleep(long time)  
//    让线程休眠指定的时问,单位为毫秒


    Thread t = Thread.currentThread();
    String name = t.getName();
    System.out.println(name);//main

    System.out.println("11111111111");
    Thread.sleep(5000);
    System.out.println("22222222222");

//run()方法中,每隔一秒进行线程
    try{
        Thread.sLeep(1000);
    }catch (InterruptedException e){
        e.printStackTrace();
    }

关于优先级的方法:

//  setPriority(int newPriority)    设置线程的优先级,默认值5
//  final int getPriority()    获取线程的优先级

    MyRunnable mr = new MyRunnable();//创建线程对象
    
    Thread t1 = new Thread(mr,"机");
    Thread t2 = new Thread(mr,"克");
    
    t1.setPriority(1);
    t2.setPriority(10);//只是抢占的概率高
    
    tl.start();
    t2.start();
//守护线程
//    final void setDaemon(boolean on)  设置为守护线程
/*细节:
  当其他的非守护线程执行完中之后,守护线程会陆续结束
  迪俗易懂:
  当女神线程结束了,那么备胎也没有存在的必要了
*/
    MyThread1 t1 = new MyThread1();MyThread2 t2 = new MyThread2();

    tl.setName("女神");
    t2.setName("备胎");

//把第二个线程设置为守护线程
    t2.setDaemon(true);
    
    tl.start();
    t2.start();
//女神结束后,备胎陆陆续续的结束
//应用场景:聊天和发送软件
//出让线程/礼让线程
//  public static void yield()

//  run(),输出后
//尽可能的均匀
    Thread.yield();
//插入线程
//  public final void join()

    MyThread2 t2 = new MyThread2();

    tl.setName("土豆");

    t.join();
//插入到当前线程之前,全部执行完毕再执行当前线程
    for (int i = 0; i < 10; i++){
        System.out.println("main线程" + i);
    }

线程的生命周期

img

线程的安全问题

//表示这个类所有的对象,都共享ticket数据
    static int ticket = 0;//0 ~ 99
//同一张或者超出范围

//线程执行代码时可能来不及执行,执行权被其他线程抢走
//有随机性

同步代码块

把操作共享数据的代码锁起来
格式:

    synchronized(锁){
        操作共享数据的代码
}

特点1: 锁默认打开,有一个线程进去了,锁自动关闭
特点2: 里面的代码全部执行完毕,线程出来,锁自动打开
img

//细节:
//1. synchronized在循环里面
//2. 锁对象一定唯一

同步方法

就是把synchronized关键字加到方法上
格式:
修饰符 synchronized 返回值类型 方法名(方法参数) {...}

特点1: 同步方法是锁住方法里面所有的代码
特点2:锁对象不能自己指定
img

StringBuilder 用于多线程不安全
使用StringBuffer

lock锁

虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock

Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化

  • ReentrantLock构造方法

    方法名 说明
    ReentrantLock() 创建一个ReentrantLock的实例
  • 加锁解锁方法

    方法名 说明
    void lock() 获得锁
    void unlock() 释放锁
  public class Ticket implements Runnable {
      //票的数量
      private int ticket = 100;
      private Object obj = new Object();
      private ReentrantLock lock = new ReentrantLock();

      @Override
      public void run() {
          while (true) {
              //synchronized (obj){//多个线程必须使用同一把锁.
              try {
                  lock.lock();
                  if (ticket <= 0) {
                      //卖完了
                      break;
                  } else {
                      Thread.sleep(100);
                      ticket--;
                      System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");
                  }
              } catch (InterruptedException e) {
                  e.printStackTrace();
              } finally {
                  lock.unlock();
                  //一定会执行
              }
              // }
          }
      }
  }

  public class Demo {
      public static void main(String[] args) {
          Ticket ticket = new Ticket();

          Thread t1 = new Thread(ticket);
          Thread t2 = new Thread(ticket);
          Thread t3 = new Thread(ticket);

          t1.setName("窗口一");
          t2.setName("窗口二");
          t3.setName("窗口三");

          t1.start();
          t2.start();
          t3.start();
      }
  }

死锁

出现了锁的嵌套

生产者和消费者(等待唤醒机制)

生产者消费者模式是一个十分经典的多线程协作的模式
img

代码示例:

  public class Desk {

      //定义一个标记
      //true 就表示桌子上有汉堡包的,此时允许吃货执行
      //false 就表示桌子上没有汉堡包的,此时允许厨师执行
      //public static boolean flag = false;
      private boolean flag;

      //汉堡包的总数量
      //public static int count = 10;
      //以后我们在使用这种必须有默认值的变量
     // private int count = 10;
      private int count;

      //锁对象
      //public static final Object lock = new Object();
      private final Object lock = new Object();

      public Desk() {
          this(false,10); // 在空参内部调用带参,对成员变量进行赋值,之后就可以直接使用成员变量了
      }

      public Desk(boolean flag, int count) {
          this.flag = flag;
          this.count = count;
      }

      public boolean isFlag() {
          return flag;
      }

      public void setFlag(boolean flag) {
          this.flag = flag;
      }

      public int getCount() {
          return count;
      }

      public void setCount(int count) {
          this.count = count;
      }

      public Object getLock() {
          return lock;
      }

      @Override
      public String toString() {
          return "Desk{" +
                  "flag=" + flag +
                  ", count=" + count +
                  ", lock=" + lock +
                  '}';
      }
  }

  public class Cooker extends Thread {

      private Desk desk;

      public Cooker(Desk desk) {
          this.desk = desk;
      }
  //    生产者步骤:
  //            1,判断桌子上是否有汉堡包
  //    如果有就等待,如果没有才生产。
  //            2,把汉堡包放在桌子上。
  //            3,叫醒等待的消费者开吃。

      @Override
      public void run() {
          while(true){
              synchronized (desk.getLock()){
                  if(desk.getCount() == 0){
                      break;
                  }else{
                      //System.out.println("验证一下是否执行了");
                      if(!desk.isFlag()){
                          //生产
                          System.out.println("厨师正在生产汉堡包");
                          desk.setFlag(true);
                          desk.getLock().notifyAll();
                      }else{
                          try {
                              desk.getLock().wait();
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                      }
                  }
              }
          }
      }
  }

  public class Foodie extends Thread {
      private Desk desk;

      public Foodie(Desk desk) {
          this.desk = desk;
      }

      @Override
      public void run() {
  //        1,判断桌子上是否有汉堡包。
  //        2,如果没有就等待。
  //        3,如果有就开吃
  //        4,吃完之后,桌子上的汉堡包就没有了
  //                叫醒等待的生产者继续生产
  //        汉堡包的总数量减一

          //套路:
              //1. while(true)死循环
              //2. synchronized 锁,锁对象要唯一
              //3. 判断,共享数据是否结束. 结束
              //4. 判断,共享数据是否结束. 没有结束
          while(true){
              synchronized (desk.getLock()){
                  if(desk.getCount() == 0){
                      break;
                  }else{
                      //System.out.println("验证一下是否执行了");
                      if(desk.isFlag()){
                          //有
                          System.out.println("吃货在吃汉堡包");
                          desk.setFlag(false);
                          desk.getLock().notifyAll();
                          desk.setCount(desk.getCount() - 1);
                      }else{
                          //没有就等待
                          //使用什么对象当做锁,那么就必须用这个对象去调用等待和唤醒的方法.
                          try {
                              desk.getLock().wait();
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                      }
                  }
              }
          }

      }
  }

  public class Demo {
      public static void main(String[] args) {
          /*消费者步骤:
          1,判断桌子上是否有汉堡包。
          2,如果没有就等待。
          3,如果有就开吃
          4,吃完之后,桌子上的汉堡包就没有了
                  叫醒等待的生产者继续生产
          汉堡包的总数量减一*/

          /*生产者步骤:
          1,判断桌子上是否有汉堡包
          如果有就等待,如果没有才生产。
          2,把汉堡包放在桌子上。
          3,叫醒等待的消费者开吃。*/

          Desk desk = new Desk();

          Foodie f = new Foodie(desk);
          Cooker c = new Cooker(desk);

          f.start();
          c.start();

      }
  }

阻塞队列

img

  public class Cooker extends Thread {
  
      private ArrayBlockingQueue<String> bd;
  
      public Cooker(ArrayBlockingQueue<String> bd) {
          this.bd = bd;
      }
  //    生产者步骤:
  //            1,判断桌子上是否有汉堡包
  //    如果有就等待,如果没有才生产。
  //            2,把汉堡包放在桌子上。
  //            3,叫醒等待的消费者开吃。
  
      @Override
      public void run() {
          while (true) {
              try {
                  bd.put("汉堡包");
                  System.out.println("厨师放入一个汉堡包");
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      }
  }
  
  public class Foodie extends Thread {
      private ArrayBlockingQueue<String> bd;
  
      public Foodie(ArrayBlockingQueue<String> bd) {
          this.bd = bd;
      }
  
      @Override
      public void run() {
  //        1,判断桌子上是否有汉堡包。
  //        2,如果没有就等待。
  //        3,如果有就开吃
  //        4,吃完之后,桌子上的汉堡包就没有了
  //                叫醒等待的生产者继续生产
  //        汉堡包的总数量减一
  
          //套路:
          //1. while(true)死循环
          //2. synchronized 锁,锁对象要唯一
          //3. 判断,共享数据是否结束. 结束
          //4. 判断,共享数据是否结束. 没有结束
          while (true) {
              try {
                  String take = bd.take();
                  System.out.println("吃货将" + take + "拿出来吃了");
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
  
      }
  }
  
  public class Demo {
      public static void main(String[] args) {
          ArrayBlockingQueue<String> bd = new ArrayBlockingQueue<>(1);
  
          Foodie f = new Foodie(bd);
          Cooker c = new Cooker(bd);
  
          f.start();
          c.start();
      }
  }

线程的状态

img

七个练习

线程池

原理:

  1. 创建一个池子,池子中是空的
  2. 提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可
  3. 但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待

img

//1.获取线程池对象
    ExecutorService pool1 = Executors.newCachedThreadPool();
//2.提交任务
    pool1.submit(new MyRunnable());
    pool1.submit(new MyRunnable());
    pool1.submit(new MyRunnable());
    pool1.submit(new MyRunnable());
//3.销毁线程池
    pool1.shutdown();


ExecutorService pool1 = Executors.newFixedThreadPool(3);

自定义线程池

img
img

/**
 * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
*/
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
        new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.AbortPolicy()) ;

// 提交5个任务,而该线程池最多可以处理4个任务,当我们使用AbortPolicy这个任务处理策略的时候,就会抛出异常
    for(int x = 0 ; x < 5 ; x++) {
        threadPoolExecutor.submit(() -> {
            System.out.println(Thread.currentThread().getName() + "---->> 执行了任务");
        });
    }

线程池合适大小

最大并行数:

//向Java虚拟机返回可用处理器的数目
int count = Runtime.getRuntime().availableProcessors();

img

额外扩展

资料中有

网络编程

在网络通信协议下,不同计算机上运行的程序,进行的数据传输
C/S: Client/Server
B/S: Browser/Server
BS优缺点:

  1. 不需要开发客户端,只需要页面 + 服务端
  2. 用户不需要下载,打开浏览器就能使用
  3. 如果应用过大,用户体验受到影响

CS优缺点:

  1. 画面可以做的非常精美,用户体验好
  2. 需要开发客户端,也需要开发服务端
  3. 用户需要下载和更新的时候太麻烦

网络编程三要素

IP

IPV4:

全称: Internet Protocol version 4,互联网通信协议第四版
32位
点分十进制

IPV6:

128位
冒分十六进制、0位压缩表示法

特殊IP地址

127.0.0.1,也可以是localhost: 是回送地址也称本地回环地址,也称本机IP,永远只会寻找当前所在本机。
常用CMD命令:
ipconfig:查看本机IP地址
ping:检查网络是否连通

InetAddress
//获取InetAddress的对象
//IP的对象 一台电脑的对象
    InetAddress address = InetAddress.getByName("DESKTOP-50JJSAM");
    System.out.println(address);

    String name = address.getHostName();
    System.out.printIn(name);//DESKTOP-50JJSAM
    
    String ip = address.getHostAddress();
    System.out.println(ip);//192.168.1.100

端口号

应用程序在设备中唯一的标识。
端口号:由两个字节表示的整数,取值范围:0~65535
其中0~1023之间的端口号用于一些知名的网络服务或者应用
我们自己使用1024以上的端口号就可以了

协议

img

UDP

UDP发送数据

  • 构造方法

    方法名 说明
    DatagramSocket() 创建数据报套接字并将其绑定到本机地址上的任何可用端口
    DatagramPacket(byte[] buf,int len,InetAddress add,int port) 创建数据包,发送长度为len的数据包到指定主机的指定端口
  • 相关方法

    方法名 说明
    void send(DatagramPacket p) 发送数据报包
    void close() 关闭数据报套接字
    void receive(DatagramPacket p) 从此套接字接受数据报包
    public class SendDemo {
        public static void main(String[] args) throws IOException {
            //创建发送端的Socket对象(DatagramSocket)
            // DatagramSocket() 构造数据报套接字并将其绑定到本地主机上的任何可用端口
            DatagramSocket ds = new DatagramSocket();
    
            //创建数据,并把数据打包
            //DatagramPacket(byte[] buf, int length, InetAddress address, int port)
            //构造一个数据包,发送长度为 length的数据包到指定主机上的指定端口号。
            byte[] bys = "hello,udp,我来了".getBytes();
    
            DatagramPacket dp = new DatagramPacket(bys,bys.length,InetAddress.getByName("127.0.0.1"),10086);
    
            //调用DatagramSocket对象的方法发送数据
            //void send(DatagramPacket p) 从此套接字发送数据报包
            ds.send(dp);
    
            //关闭发送端
            //void close() 关闭此数据报套接字
            ds.close();
        }
    }
    

UDP接收数据

  • 构造方法

    方法名 说明
    DatagramPacket(byte[] buf, int len) 创建一个DatagramPacket用于接收长度为len的数据包
  • 相关方法

    方法名 说明
    byte[] getData() 返回数据缓冲区
    int getLength() 返回要发送的数据的长度或接收的数据的长度
    public class ReceiveDemo {
        public static void main(String[] args) throws IOException {
          	//创建接收端的Socket对象(DatagramSocket)
          	DatagramSocket ds = new DatagramSocket(12345);
    
          	//创建一个数据包,用于接收数据
          	byte[] bys = new byte[1024];
          	DatagramPacket dp = new DatagramPacket(bys, bys.length);
    
          	//调用DatagramSocket对象的方法接收数据
          	ds.receive(dp);
    
          	//解析数据包,并把数据在控制台显示
          	System.out.println("数据是:" + new String(dp.getData(), 0, dp.getLength()));
            }
        }
    

练习(UDP发,UDP收):

/*
    UDP发送数据:
        数据来自于键盘录入,直到输入的数据是886,发送数据结束
 */
public class SendDemo {
    public static void main(String[] args) throws IOException {
        //创建发送端的Socket对象(DatagramSocket)
        DatagramSocket ds = new DatagramSocket();
        //键盘录入数据
        Scanner sc = new Scanner(System.in);
        while (true) {
          	String s = sc.nextLine();
            //输入的数据是886,发送数据结束
            if ("886".equals(s)) {
                break;
            }
            //创建数据,并把数据打包
            byte[] bys = s.getBytes();
            DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("192.168.1.66"), 12345);

            //调用DatagramSocket对象的方法发送数据
            ds.send(dp);
        }
        //关闭发送端
        ds.close();
    }
}

/*
    UDP接收数据:
        因为接收端不知道发送端什么时候停止发送,故采用死循环接收
 */
public class ReceiveDemo {
    public static void main(String[] args) throws IOException {
        //创建接收端的Socket对象(DatagramSocket)
        DatagramSocket ds = new DatagramSocket(12345);
        while (true) {
            //创建一个数据包,用于接收数据
            byte[] bys = new byte[1024];
            DatagramPacket dp = new DatagramPacket(bys, bys.length);
            //调用DatagramSocket对象的方法接收数据
            ds.receive(dp);
            //解析数据包,并把数据在控制台显示
            System.out.println("数据是:" + new String(dp.getData(), 0, dp.getLength()));
        }
        //关闭接收端
//        ds.close();
    }
}
三种方式

单播:
单播用于两个主机之间的端对端通信
组播:
组播用于对一组特定的主机进行通信
广播:
广播用于一个主机对整个局域网上所有主机上的数据通信

组播

// 发送端
public class ClinetDemo {
    public static void main(String[] args) throws IOException {
        // 1. 创建发送端的Socket对象(DatagramSocket)
        DatagramSocket ds = new DatagramSocket();
        String s = "hello 组播";
        byte[] bytes = s.getBytes();
        InetAddress address = InetAddress.getByName("224.0.1.0");
        int port = 10000;
        // 2. 创建数据,并把数据打包(DatagramPacket)
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
        // 3. 调用DatagramSocket对象的方法发送数据(在单播中,这里是发给指定IP的电脑但是在组播当中,这里是发给组播地址)
        ds.send(dp);
        // 4. 释放资源
        ds.close();
    }
}
// 接收端
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        // 1. 创建接收端Socket对象(MulticastSocket)
        MulticastSocket ms = new MulticastSocket(10000);
        // 2. 创建一个箱子,用于接收数据
        DatagramPacket dp = new DatagramPacket(new byte[1024],1024);
        // 3. 把当前计算机绑定一个组播地址,表示添加到这一组中.
        ms.joinGroup(InetAddress.getByName("224.0.1.0"));
        // 4. 将数据接收到箱子中
        ms.receive(dp);
        // 5. 解析数据包,并打印数据
        byte[] data = dp.getData();
        int length = dp.getLength();
        System.out.println(new String(data,0,length));
        // 6. 释放资源
        ms.close();
    }
}

广播

// 发送端
public class ClientDemo {
    public static void main(String[] args) throws IOException {
      	// 1. 创建发送端Socket对象(DatagramSocket)
        DatagramSocket ds = new DatagramSocket();
		// 2. 创建存储数据的箱子,将广播地址封装进去
        String s = "广播 hello";
        byte[] bytes = s.getBytes();
        InetAddress address = InetAddress.getByName("255.255.255.255");
        int port = 10000;
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
		// 3. 发送数据
        ds.send(dp);
		// 4. 释放资源
        ds.close();
    }
}

// 接收端
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        // 1. 创建接收端的Socket对象(DatagramSocket)
        DatagramSocket ds = new DatagramSocket(10000);
        // 2. 创建一个数据包,用于接收数据
        DatagramPacket dp = new DatagramPacket(new byte[1024],1024);
        // 3. 调用DatagramSocket对象的方法接收数据
        ds.receive(dp);
        // 4. 解析数据包,并把数据在控制台显示
        byte[] data = dp.getData();
        int length = dp.getLength();
        System.out.println(new String(data,0,length));
        // 5. 关闭接收端
        ds.close();
    }
}

单播换一下IP

TCP

TCP发送数据

  • Java对基于TCP协议的的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信。

  • Java为客户端提供了Socket类,为服务器端提供了ServerSocket类

  • 构造方法

    方法名 说明
    Socket(InetAddress address,int port) 创建流套接字并将其连接到指定IP指定端口号
    Socket(String host, int port) 创建流套接字并将其连接到指定主机上的指定端口号
  • 相关方法

    方法名 说明
    InputStream getInputStream() 返回此套接字的输入流
    OutputStream getOutputStream() 返回此套接字的输出流
  • 示例代码

    public class Client {
        public static void main(String[] args) throws IOException {
            //TCP协议,发送数据
    
            //1.创建Socket对象
            //细节:在创建对象的同时会连接服务端
            //      如果连接不上,代码会报错
            Socket socket = new Socket("127.0.0.1",10000);
    
            //2.可以从连接通道中获取输出流
            OutputStream os = socket.getOutputStream();
            //写出数据
            os.write("aaa".getBytes());
    
            //3.释放资源
            os.close();
            socket.close();
        }
    }
    

TCP接收数据

  • 构造方法

    方法名 说明
    ServletSocket(int port) 创建绑定到指定端口的服务器套接字
  • 相关方法

    方法名 说明
    Socket accept() 监听要连接到此的套接字并接受它
  • 注意事项

    1. accept方法是阻塞的,作用就是等待客户端连接
    2. 客户端创建对象并连接服务器,此时是通过三次握手协议,保证跟服务器之间的连接
    3. 针对客户端来讲,是往外写的,所以是输出流
      针对服务器来讲,是往里读的,所以是输入流
    4. read方法也是阻塞的
    5. 客户端在关流的时候,还多了一个往服务器写结束标记的动作
    6. 最后一步断开连接,通过四次挥手协议保证连接终止

三次握手,四次挥手代码

  public class Server {
      public static void main(String[] args) throws IOException {
          //TCP协议,接收数据
  
          //1.创建对象ServerSocker
          ServerSocket ss = new ServerSocket(10000);
  
          //2.监听客户端的链接
          Socket socket = ss.accept();
  
          //3.从连接通道中获取输入流读取数据
          InputStream is = socket.getInputStream();
          int b;
          while ((b = is.read()) != -1){
              System.out.println((char) b);
          }
  
          //4.释放资源
          socket.close();
          ss.close();
      }
  }

练习题

反射

反射允许对封装类的字段,方法和构造函数的信息进行编程访问。

获取class对象的三种方式

  1. Class.forName("全类名");
  2. 类名.class
  3. 对象.getClass()
    源代码阶段1 加载阶段2 运行阶段3
//1.第一种方式
//最常用
//余类名 : 包名 + 类名
    Class clazz = Class.forName("com.itheima.myreflect1.Student");

//2.第二种方式
//一般更多的是当做参数进行传递
    Class clazz2 = Student.class;

    System.out.println(clazz1 == clazz2);//true

//3.第三种方式
//当我们已经有了这个类的对象时,才可以使用
    Student s = new Student();
    Class clazz3 = s.getClass();

获取构造方法

img

//2.获取构造方法
//所有公共的
    Constructor[] cons1 = clazz.getConstructors();
    for (Constructor con : cons1){
        System.out.printIn(con);
    }
//获取所有的
    Constructor[] cons2 = clazz.getDeclaredConstructors();
//获取单个,顺序获取
    Constructor con1 = clazz.getDeclaredConstructor();
    System.out.println(con1);
//参数对应
    Constructor con2 = clazz.getDeclaredConstructor(String.class);
    Constructor con3 = clazz.getDeclaredConstructor(String.class,int.class);

    int modifiers = con4.getModifiers();//权限修饰符,以整数形式

    Parameter[] parameters = con4.getParameters();//获取所有参数

//表示临时取消权限校验
//暴力反射
    con4.setAccessible(true);
    Student stu = (Student) con4.newInstance("张三"23);
    
    System.out.println(stu);

获取成员变量

img

 //获取class字节码文件的对象
     Class clazz = Class.forName("com.itheima.myreflect3.Student");

//获取所有的成员变量 
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields){
        System.out.printIn(field);
    }

//获取单个的成员变量
    Field gender = clazz.getDeclaredField("name");

//获取权限修饰符
    int modifiers = name.getModifiers();

//获取成员变量的名字 
    String n = name.getName();

//获取成员变量的数据类型
    Class<?> type = name.getType();

//获取成员变量记录的值
    Student s = new Student( "zhangsan", 23, "男");
    name.setAccessible(true);
    String value = (String) name.get(s);

//修改对象里面记录的值
    name.set(s,"lisi");

获取成员方法

img

public class ReflectDemo6 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//1.获取class对象
        Class<?> clazz = Class.forName("com.itheima.reflectdemo.Student");


//2.获取方法
//getMethods可以获取父类中public修饰的方法
        Method[] methods1 = clazz.getMethods();
        for (Method method : methods1) {
            System.out.println(method);
        }


//获取所有的方法(包含私有)
//但是只能获取自己类中的方法
        Method[] methods2 = clazz.getDeclaredMethods();
        for (Method method : methods2) {
            System.out.println(method);
        }


//获取指定的方法(空参)
        Method method3 = clazz.getMethod("sleep");

        Method method4 = clazz.getMethod("eat",String.class);

//获取指定的私有方法
        Method method5 = clazz.getDeclaredMethod("playGame");

// 获取方法的修饰符
        int modifiers = m.getModifiers();
// 获取方法的名字
        String name = m.getName();        

// 获取方法的形参
        Parameter[] parameters = m.getParameters();
        for (Parameter parameter : parameters){
            System.out.printIn(parameter);
        }

//获取方法抛出的异常        
        Class[] exceptionTypes = m.getExceptionTypes();
   

/*
方法运行
Method类中用于创建对象的方法
Object invoke(Object obj,object... args): 运行方法
参数一:川bj对象调用该方法
参数二: 调用方法的传递的参数《如果没有就不写)
返回值:方法的返回值《如果没有就不写)
*/
        
        Student s = new student();
        m.setAccessible(true);
//参数一s: 表示方法的调用者
//参数二"汉堡包":表示在调用方法的时候传递的实际参数
        m.invoke(s,"汉堡包");
    }
}

反射的作用

  1. 获取一个类里面所有的信息,获取到了之后,执行其他的业务逻辑
  2. 结合配置文件,动态的创建对象并调用方法

练习1:

img

练习2:

需求: 利用反射根据文件中的不同类名和方法名,创建不同的对象并调用方法。

分析:

  1. 通过Properties加载配置文件
  2. 得到类名和方法名
  3. 通过类名反射得到Class对象
  4. 通过Class对象创建一个对象
  5. 通过Class对象得到方法
  6. 调用方法
public class ReflectDemo9 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //1.读取配置文件的信息
        Properties prop = new Properties();
        FileInputStream fis = new FileInputStream("day14-code\\prop.properties");
        prop.load(fis);
        fis.close();
        System.out.println(prop);

        String classname = prop.get("classname") + "";
        String methodname = prop.get("methodname") + "";

        //2.获取字节码文件对象
        Class clazz = Class.forName(classname);

        //3.要先创建这个类的对象
        Constructor con = clazz.getDeclaredConstructor();
        con.setAccessible(true);
        Object o = con.newInstance();
        System.out.println(o);

        //4.获取方法的对象
        Method method = clazz.getDeclaredMethod(methodname);
        method.setAccessible(true);

        //5.运行方法
        method.invoke(o);

    }
}

配置文件中的信息:
classname=com.itheima.a02reflectdemo1.Student
methodname=sleep

动态代理

特点:无侵入式的给代码增加额外的功能
代理里面就是对象要被代理的方法

动态代理三要素:

  1. 真正干活的对象
  2. 代理对象
  3. 利用代理调用方法

切记一点:代理可以增强或者拦截的方法都在接口中,接口需要写在newProxyInstance的第二个参数里。

public class Test {
    public static void main(String[] args) {
    /*
        需求:
            外面的人想要大明星唱一首歌
             1. 获取代理的对象
                代理对象 = ProxyUtil.createProxy(大明星的对象);
             2. 再调用代理的唱歌方法
                代理对象.唱歌的方法("只因你太美");
     */
        //1. 获取代理的对象
        BigStar bigStar = new BigStar("鸡哥");
        Star proxy = ProxyUtil.createProxy(bigStar);

        //2. 调用唱歌的方法
        String result = proxy.sing("只因你太美");
        System.out.println(result);
    }
}
/*
* 类的作用:
*       创建一个代理
* */
public class ProxyUtil {
    /*
    *
    * 方法的作用:
    *       给一个明星的对象,创建一个代理
    *
    *  形参:
    *       被代理的明星对象
    *
    *  返回值:
    *       给明星创建的代理
    *
    * 需求:
    *   外面的人想要大明星唱一首歌
    *   1. 获取代理的对象
    *      代理对象 = ProxyUtil.createProxy(大明星的对象);
    *   2. 再调用代理的唱歌方法
    *      代理对象.唱歌的方法("只因你太美");
    * */
    public static Star createProxy(BigStar bigStar){
       /* java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法:

        public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
        参数一:用于指定用哪个类加载器,去加载生成的代理类
        参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
        参数三:用来指定生成的代理对象要干什么事情*/
        Star star = (Star) Proxy.newProxyInstance(
                ProxyUtil.class.getClassLoader(),//参数一:用于指定用哪个类加载器,去加载生成的代理类
                new Class[]{Star.class},//参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
                //参数三:用来指定生成的代理对象要干什么事情
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        /*
                        * 参数一:代理的对象
                        * 参数二:要运行的方法 sing
                        * 参数三:调用sing方法时,传递的实参
                        * */
                        if("sing".equals(method.getName())){
                            System.out.println("准备话筒,收钱");
                        }else if("dance".equals(method.getName())){
                            System.out.println("准备场地,收钱");
                        }
                        //去找大明星开始唱歌或者跳舞
                        //代码的表现形式:调用大明星里面唱歌或者跳舞的方法
                        return method.invoke(bigStar,args);
                    }
                }
        );
        return star;
    }
}
public interface Star {
    //我们可以把所有想要被代理的方法定义在接口当中
    //唱歌
    public abstract String sing(String name);
    //跳舞
    public abstract void dance();
}
public class BigStar implements Star {
    private String name;


    public BigStar() {
    }

    public BigStar(String name) {
        this.name = name;
    }

    //唱歌
    @Override
    public String sing(String name){
        System.out.println(this.name + "正在唱" + name);
        return "谢谢";
    }

    //跳舞
    @Override
    public void dance(){
        System.out.println(this.name + "正在跳舞");
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    public String toString() {
        return "BigStar{name = " + name + "}";
    }
}

结束!
2023.8.31 17:41

posted on   水吉z  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
 
点击右上角即可分享
微信分享提示