java-learn(11) : 网络编程和多线程

网络编程和多线程

多线程

为什么要有多线程呢?

线程是操作系统能够进行运算调度的最小单元。它被包含在进程之中,是进程的实际运作单位。

什么是多线程?

应用软件中互相独立,可以同时运行的功能。

多线程的作用?

提高效率

多线程的场景?

软件中的耗时操作,所有的聊天软件,所有的服务器。

多线程的两个概念

并发:在同一时刻,有多个指令在同一个CPU上运行。

并行:在同一时刻,有多个指令在多个CPU上运行。

多线程的实现方式

第一种实现方式:

java 复制代码
package Thread_learn;

public class MyThread extends Thread{
   //重写run方法 方法中的代码就是线程要执行的内容
    @Override
    public void run(){
        for (int i = 0; i < 10; i++) {
            System.out.println(getName() + "hello world");
        }
    }
}


public class test {

    public static void main(String[] args) {
    //创建一个线程对象
        MyThread myThread1 = new MyThread();
        MyThread myThread2 = new MyThread();
     // 给线程取个名字
        myThread1.setName("线程1");
        myThread2.setName("线程2");
     // 开启一个线程
        myThread1.start();
        myThread2.start();
    }

}

第二种实现方式:

java 复制代码
// 继承Runnable
public class MyThread1 implements Runnable{
    @Override
    public void run(){
        for (int i = 0; i < 10; i++) {
            //获取当前线程对象
            Thread t1 = Thread.currentThread();
            System.out.println(t1.getName() + "hello world");
        }
    }
}
java 复制代码
public class test1 {
    public static void main(String[] args) {
        //创建一个对象
        MyThread1 myThread1 = new MyThread1();
        MyThread1 myThread2 = new MyThread1();
        //把对象传给线程
        Thread t1 = new Thread(myThread1);
        Thread t2 = new Thread(myThread2);

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

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

    }
}

第三种实现方式:

特点:可以有返回值

java 复制代码
public class MyThread2 implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
          int sum = 0;
          for(int i =1;i <= 100;i++){
              sum += i;
          }
          return sum;
    }
}

public class test2 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        //创建一个实现了Callable接口的类的对象
        MyThread2 myThread2 = new MyThread2();
        //创建一个FutureTask对象,来管理Callable对象(管理多线程运行的结果)
        FutureTask<Integer> ft = new FutureTask<Integer>(myThread2);
        //创建一个Thread对象,并启动
        Thread t1 = new Thread(ft);
       //启动这个线程
        t1.start();
        //获取线程运行结果
        System.out.println(ft.get());

    }
}

三种线程的对比

thread:

优点:代码操作简单

缺点:没有返回值,扩展性差,不能再继承其它的类

实现Runnable接口,Callable接口:

扩展性强,实现刻接口还可以继承其它类,

编程相对复杂,不能直接使用Thread类中的方法。

常见的成员方法

java 复制代码
//         Thread t1 = new Thread(new Runnable(){
//             @Override
//             public void run() {
//                 for (int i = 0; i < 10; i++) {
//                     // 得到当前线程对象
//                     System.out.println(Thread.currentThread().getName() +"@"+"hello world");
//                 }
//             }
//         });
//
//
//
//         t1.start();
//
//        System.out.println(t1.getName());
        System.out.println(1111111);
        //让当前线程休眠m毫秒
        Thread.sleep(1000);
        System.out.println(222222);
        //获取当前线程对象
        System.out.println(Thread.currentThread());
        // 通过构造方法设置线程名称
      Thread t1 = new Thread("线程1");
        System.out.println(t1.getName());
java 复制代码
 MyRunnable runnable1 = new MyRunnable();
        MyRunnable runnable2 = new MyRunnable();

        Thread t1 = new Thread(runnable1);
        Thread t2 = new Thread(runnable2);

        t1.setName("飞机");
        t2.setName("坦克");

        //设置线程优先级
        t1.setPriority(Thread.MAX_PRIORITY);
        t2.setPriority(Thread.MIN_PRIORITY);

        t1.start();
        t2.start();
       //特到线程的优先级
        System.out.println(t1.getPriority());
        System.out.println(t2.getPriority());

当所有的非守护线程结束之后,守护线程就会被立即终止。

java 复制代码
package demo1;

import Thread_learn.MyThread1;

public class demo2 {
    public static void main(String[] args) {

        MyRunnable runnable1 = new MyRunnable();
        MyThread1 runnable2 = new MyThread1();

        Thread t1 = new Thread(runnable1);
        Thread t2 = new Thread(runnable2);
        t2.setName("坦克");

        t1.setName("飞机");

//        //设置线程优先级
//        t1.setPriority(Thread.MAX_PRIORITY);
        t2.setPriority(Thread.MIN_PRIORITY);

        //守护线程在其它非守护线程执行完毕之后,也会陆续结束。

        //把第一个线程设置为守护线程
        t1.setDaemon(true);


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

    }
}


package Thread_learn;
// 继承Runnable
public class MyThread1 implements Runnable{
    @Override
    public void run(){
        for (int i = 0; i < 10; i++) {
            //获取当前线程对象
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            Thread t1 = Thread.currentThread();
            System.out.println(t1.getName() + "hello world");
        }
    }
}

package demo1;

public class MyRunnable implements Runnable{

    @Override
    public void run(){
        for (int i = 0; i < 100; i++) {
            // 获取当前线程对象
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName() + " " +i);
        }
    }

}

礼让线程

java 复制代码
//执行到当前线程的时候,把当前线程让出去,所以线程重新抢执行权
Thread.yield();

插入线程:

java 复制代码
  MyRunnable runnable1 = new MyRunnable();
        Thread t1 = new Thread(runnable1);

        t1.setName("土豆");

        t1.start();
        //礼让线程
        //表示把t这个线程,插入到当前线程之前
        t1.join();

        for (int i = 0; i < 10; i++) {
            System.out.println("main线程"+i);
        }

线程的生命周期

线程安全的问题

遇到的问题

java 复制代码
package demo1;

public class MyRunnable implements Runnable{

    static int ticket = 0;

    @Override
    public void run(){

        while(true){

            if(ticket < 100){
                ticket++;
                System.out.println(Thread.currentThread().getName() + "正在抢第" +ticket+"张票!!!");
            }else{
                break;
            }

        }

    }

}

package demo1;

public class demo4 {
    public static void main(String[] args) {
          MyRunnable myRunnable1 = new MyRunnable();
          MyRunnable myRunnable2 = new MyRunnable();
          MyRunnable myRunnable3 = new MyRunnable();

          Thread t1 = new Thread(myRunnable1);
          Thread t2 = new Thread(myRunnable2);
          Thread t3 = new Thread(myRunnable3);

          t1.setName("窗口1");
          t2.setName("窗口2");
          t3.setName("窗口3");

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

    }
}

打印结果:
窗口2正在抢第2张票!!!
窗口2正在抢第4张票!!!
窗口3正在抢第3张票!!!
窗口1正在抢第1张票!!!
窗口3正在抢第6张票!!!
窗口2正在抢第5张票!!!
窗口3正在抢第8张票!!!
窗口1正在抢第7张票!!!
窗口3正在抢第10张票!!!
窗口2正在抢第9张票!!!
窗口3正在抢第12张票!!!
窗口1正在抢第11张票!!!
窗口3正在抢第14张票!!!
窗口2正在抢第13张票!!!
窗口3正在抢第16张票!!!
窗口1正在抢第15张票!!!
窗口3正在抢第18张票!!!
窗口2正在抢第17张票!!!
窗口3正在抢第20张票!!!
窗口1正在抢第19张票!!!
窗口3正在抢第22张票!!!
窗口2正在抢第21张票!!!
窗口3正在抢第24张票!!!
窗口1正在抢第23张票!!!
窗口3正在抢第26张票!!!
窗口2正在抢第25张票!!!
窗口1正在抢第27张票!!!
窗口3正在抢第28张票!!!
窗口1正在抢第30张票!!!
窗口2正在抢第29张票!!!
窗口1正在抢第32张票!!!
窗口3正在抢第31张票!!!
窗口1正在抢第34张票!!!
窗口2正在抢第33张票!!!
窗口1正在抢第36张票!!!
窗口3正在抢第35张票!!!
窗口1正在抢第38张票!!!
窗口2正在抢第37张票!!!
窗口3正在抢第39张票!!!
窗口1正在抢第40张票!!!
窗口2正在抢第41张票!!!
窗口1正在抢第43张票!!!
窗口3正在抢第42张票!!!
窗口1正在抢第45张票!!!
窗口2正在抢第44张票!!!
窗口1正在抢第47张票!!!
窗口3正在抢第46张票!!!
窗口1正在抢第49张票!!!
窗口3正在抢第50张票!!!
窗口2正在抢第48张票!!!
窗口3正在抢第52张票!!!
窗口1正在抢第51张票!!!
窗口1正在抢第55张票!!!
窗口3正在抢第54张票!!!
窗口2正在抢第53张票!!!
窗口3正在抢第57张票!!!
窗口1正在抢第56张票!!!
窗口3正在抢第59张票!!!
窗口2正在抢第58张票!!!
窗口3正在抢第61张票!!!
窗口1正在抢第60张票!!!
窗口3正在抢第63张票!!!
窗口2正在抢第62张票!!!
窗口3正在抢第65张票!!!
窗口1正在抢第64张票!!!
窗口3正在抢第67张票!!!
窗口2正在抢第66张票!!!
窗口2正在抢第70张票!!!
窗口3正在抢第69张票!!!
窗口1正在抢第68张票!!!
窗口3正在抢第72张票!!!
窗口3正在抢第74张票!!!
窗口3正在抢第75张票!!!
窗口2正在抢第71张票!!!
窗口3正在抢第76张票!!!
窗口1正在抢第73张票!!!
窗口1正在抢第79张票!!!
窗口1正在抢第80张票!!!
窗口3正在抢第78张票!!!
窗口2正在抢第77张票!!!
窗口3正在抢第82张票!!!
窗口1正在抢第81张票!!!
窗口3正在抢第84张票!!!
窗口2正在抢第83张票!!!
窗口3正在抢第86张票!!!
窗口1正在抢第85张票!!!
窗口3正在抢第88张票!!!
窗口2正在抢第87张票!!!
窗口3正在抢第90张票!!!
窗口1正在抢第89张票!!!
窗口3正在抢第92张票!!!
窗口2正在抢第91张票!!!
窗口2正在抢第95张票!!!
窗口2正在抢第96张票!!!
窗口2正在抢第97张票!!!
窗口3正在抢第94张票!!!
窗口1正在抢第93张票!!!
窗口3正在抢第99张票!!!
窗口2正在抢第98张票!!!
窗口1正在抢第100张票!!!

我在学习多线程中的同步代码块 这个我还没用锁 我只是想看一下多线程会出现的问题
为什么票数小的可能会出现在票数大的
虽然说执行权在任意时刻都可能会被其它线程抢走
但是这个ticket是静态的呀 如果被抢走了 其它线程执行了ticket++ 那么的话当再被抢回来的时候 执行打印语句的时候,不应该是被++之后的ticket吗 怎么输出的还是被抢走之前的ticket

解答:

ticket++分为三步:

int temp = ticket;

temp = temp + 1;

ticket = temp;

应为执行权在任意时刻都可能被抢夺 所以可能就是在第二步的时候被抢走了执行权,在当被的线程执行完之后,当前线程就执行第三步,就会把之前的值给覆盖掉。

java 复制代码
static int ticket = 0;
    @Override
    public void run(){


           while(true){
               synchronized(MyRunnable.class){

                   try {
                       Thread.sleep(10);
                   } catch (InterruptedException e) {
                       throw new RuntimeException(e);
                   }

                   if(ticket < 100){
                   ticket++;
                   System.out.println(Thread.currentThread().getName() + "正在抢第" +ticket+"张票!!!");
               }else{
                   break;
               }

           }
       }

    }

同步代码块的细节:

1.synchronized需要写在循环的内部

2.传入的锁对象必须是同一个对象

同步方法:是锁方法里面所有的代码

把共享的代码抽取成一个方法,方法用synchronized来修饰,jvm会自动指定锁对象

静态方法的锁对象是当前类的字节码文件对象。非静态的是this

lock锁 :

提供获取锁和释放锁的方法

void lock():获取锁

void unlock():释放锁

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

java 复制代码
package demo1;

import java.util.concurrent.locks.ReentrantLock;

public class MyRunnable implements Runnable{

    static int ticket = 0;

    static ReentrantLock lock = new ReentrantLock();

    @Override
    public void run(){
           while(true){
                lock.lock();
                try{
                    Thread.sleep(10);
                    if(ticket < 100){
                        ticket++;
                        System.out.println(Thread.currentThread().getName() + "正在抢第" +ticket+"张票!!!");
                    }else{
                        break;
                    }
                }catch(InterruptedException e) {
                    throw new RuntimeException(e);
                }finally {
                    lock.unlock();
                }
       }
    }

}

死锁

由于锁的嵌套 导致死锁,导致程序不会终止。

生产者和消费者


线程池

开始的时候不会创建线程 会根据具体的任务创建线程

如果是限制了线程数量 并且有多个任务 线程不够用的时候就只能排队

java 复制代码
package demo4;

import java.util.Collection;
import java.util.List;
import java.util.concurrent.*;

public class test {
    public static void main(String[] args) {

        // 创建线程池对象 -- 几乎没有上限的线程数 -- 线程空闲的60s会自动销毁
        ExecutorService pool = Executors.newCachedThreadPool();
        //获取线程池对象 -- 可以设置线程数量 -- 不会自动销毁
//        ExecutorService pool = Executors.newFixedThreadPool(3);
        //提交任务
        pool.submit(new MyThread());
        pool.submit(new MyThread());
        pool.submit(new MyThread());
        pool.submit(new MyThread());
        pool.submit(new MyThread());
        //摧毁线程池
        pool.shutdown();
    }
}

自定义线程池


网络编程

什么是网络编程



网络编程三要素

ip: 设备在网络中的地址,是唯一的标识

端口号:应用程序在设备中唯一的标识

协议:数据在网络中传输的规则,常见的协议有UDP,TCP,http,https,ftp.

ip


ping 网址/ip地址

java 复制代码
  // 确认主机名称的ip地址,主机名称可以是域名,也可以是ip地址
      //一台电脑的对象
        InetAddress address = InetAddress.getByName("Hou");
        System.out.println(address);
        //获取ip地址
        System.out.println(address.getHostAddress());
        //获取主机名(如果没有这个主机 那么返回的就是ip地址)
        System.out.println(address.getHostName());

端口号

UDP通信程序

用户数据报协议:不需要确保网络是否连通

udp是面向无连接的通信协议:

特点:速度快,有大小限制,数据不安全,容易丢失数据。

应用场景:在线视频,语音通话

发送数据

java 复制代码
/*
        *  细节:
        * 绑定端口号,以后通过这个端口号往外发送信息
        *  空参:所有可以用的端口号中随机抽取一个进行使用
        *  有参:指定端口号进行绑定
        * */
        //创建对象
       DatagramSocket ds = new DatagramSocket();
       //需要发送的信息
       String info = "你好呀!!!";
       byte[] bytes = info.getBytes();
        //获取ip
       InetAddress address = InetAddress.getByName("127.0.0.1");
       int port = 10086;
       //打包
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
        //发送数据
        ds.send(dp);
        //释放资源
        ds.close();

接收数据

java 复制代码
//创建对象
        DatagramSocket ds = new DatagramSocket(10086);
        //创建接受数据的数组
        byte[] bytes = new byte[1024];
        //创建接受数据包
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
        //接受数据(是阻塞的 直到接受到数据)
        ds.receive(dp);
        //释放资源
        ds.close();

        //打印接受到的数据
        System.out.println("接受到的数据:  "+new String(dp.getData(),0,dp.getLength()));
        System.out.println("来自"+dp.getAddress().getHostAddress()+"的"+dp.getPort());

细节:接收数据的代码必须先运行

UDP的三种通信方式

单播:将数据包发送给特定的一个主机

组播:将数据包发送给特定组主机

组播的地址:224.0.0.0 - 239.255.255.255

其中224.0.0.0- 224.0.0.255为预留的组播地址

广播:将数据包发送给局域网内所有的主机

组播代码实现:

java 复制代码
public static void main(String[] args) throws IOException {

        // 创建MulticastSocket对象
        MulticastSocket ms = new MulticastSocket();
        //创建数据
        String str = "你好呀!!!";
        byte[] bytes = str.getBytes();
        //获取主机
        InetAddress address = InetAddress.getByName("224.0.0.1");
        //设定端口
        int port = 10086;
        //创建数据包
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
        //发送信息
        ms.send(dp);
        //释放资源
        ms.close();

    }
java 复制代码
public static void main(String[] args) throws IOException {

        //创建MulticastSocket对象
        MulticastSocket ms = new MulticastSocket(10086);

        //加入组播
        InetAddress address = InetAddress.getByName("224.0.0.2");
        ms.joinGroup(address);

        //创建数据包对象 来接收数据
        byte[] bytes = new byte[1024];

        DatagramPacket dp = new DatagramPacket(bytes,bytes.length);

        ms.receive(dp);

        ms.close();

        System.out.println("接受到的数据:  "+new String(dp.getData(),0,dp.getLength()));
        System.out.println("来自:  "+dp.getAddress().getHostAddress()+":"+dp.getPort());
    }

广播代码实现:

和组播的区别就是ip地址为"255.255.255.255"

java 复制代码
public static void main(String[] args) throws IOException {

        // 创建MulticastSocket对象
        MulticastSocket ms = new MulticastSocket();
        //创建数据
        String str = "你好呀!!!";
        byte[] bytes = str.getBytes();
        //获取主机
        InetAddress address = InetAddress.getByName("255.255.255.255");
        //设定端口
        int port = 10086;
        //创建数据包
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
        //发送信息
        ms.send(dp);
        //释放资源
        ms.close();

    }

TCP通信程序

传输控制协议:需要确保网络连通

tcp协议是面向连接的通信协议

特点:速度慢,没有大小控制,数据安全。

应用场景:软件下载,发送邮件

三次握手 四次挥手

三次握手:

四次挥手:

放射

放射的概念

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

三种获取class对象的方法

java 复制代码
        //全类名 : 包名 + 类名
        //最为常用
//      Class clazz =  Class.forName("getClass_demo.Student");
        Class clazz =  Class.forName("getClass_demo.Student");
        System.out.println(clazz);

        //第二种方式 主要是作为参数传递
        Class clazz2 = Student.class;
        System.out.println(clazz);

        //第三种方式
        //当我们已经有了这个类的对象是,才能使用
        Student student = new Student();
        Class clazz3 = student.getClass();
        System.out.println(clazz);

放射获取构造方法

java 复制代码
 //获取所有公共的构造方法
        Constructor<Student>[] clazz = (Constructor<Student>[]) Class.forName("getClass_demo.Student").getConstructors();

       for(Constructor<Student> c : clazz){
           System.out.println(c);
       }
        //获取所有的构造方法
       Constructor<Student>[] clazz1 = (Constructor<Student>[]) Student.class.getDeclaredConstructors();

       for(Constructor<Student> c : clazz1){
           System.out.println(c);
       }

       //获取公共的构造方法
//        Constructor<Student> clazz2 = (Constructor<Student>) Class.forName("getClass_demo.Student").getConstructor(String.class,int.class,String.class);
//        System.out.println(clazz2);

        // 获取任意一个构造方法
        Constructor<Student> clazz3 = (Constructor<Student>) Class.forName("getClass_demo.Student").getDeclaredConstructor(String.class,int.class,String.class);
        System.out.println(clazz3);

   //获取访问权限
        System.out.println(clazz3.getModifiers());

        //获取参数列表
         Parameter[] pt = clazz3.getParameters();
         for(Parameter p : pt){
             System.out.println(p);
         }
         //设置取消权限修饰符效验
         clazz3.setAccessible(true);
         //创建学生对象
         Student s = clazz3.newInstance("张三",18,"男");
        System.out.println(s);

放射获取成员变量

java 复制代码
//        Class clazz = Class.forName("getClass_demo.Student");

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


//        //获取公开的成员变量
//        Field gender = clazz.getField("gender");
//        System.out.println(gender);
//
//       Class clazz = Student.class;

        Student s = new Student();

        Class clazz = s.getClass();

        //获取所有权限修饰的成员变量
        Field name = clazz.getDeclaredField("name");
        System.out.println(name);
        name.setAccessible(true);
        //给变量赋值
        name.set(s,"zhangsan");
        System.out.println(s);
        //获取变量名称
        System.out.println(name.getName());
        //得到变量的值
        System.out.println(name.get(s));

放射获取成员方法

java 复制代码
Class clazz = Class.forName("getClass_demo.Student");
    // 返回所有公共成员方法的数组,包括继承的
//        Method[] methods = clazz.getMethods();
//        for(Method m : methods){
//            System.out.println(m);
//        }
        //返回所有的成员方法,不包括继承的
//        Method[] methods1 = clazz.getDeclaredMethods();
//        for(Method m : methods1){
//            System.out.println(m);
//        }
        Student s = new Student();
        Method m =  clazz.getMethod("eat", String.class);
        //获取参数列表
        Parameter[] pt = m.getParameters();
        for(Parameter p : pt){
            System.out.println(p);
        }
        //调用方法
        m.invoke(s,"苹果");
        Class[] exceptionTypes = m.getExceptionTypes();
        for(Class c : exceptionTypes){
            System.out.println(c);
        }

反射的作用

动态代理

创建一个接口,把需要代理的方法都写到里面,代理和原对象都需要实现些方法。

java 复制代码
package dp_demo;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyUtil {

    //创建代理对象
    public static Star getProxy(BigStar bigstar){

        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 {
                        // 参数一 : 代理的对象
                        // 参数二 : 要运行的方法
                        // 参数三 : 调用方法需要传入的实参

                        if(method.getName().equals("sing")){
                            System.out.println("准备话筒,收钱");
                        }else if(method.getName().equals("dence")){
                            System.out.println("准备场地,收钱");
                        }

                        return method.invoke(bigstar,args);

                    }
                }

        );

        return star;


    }

}
java 复制代码
package dp_demo;

public class BigStar implements Star{

    private String name;


    public BigStar() {
    }

    public BigStar(String name) {
        this.name = name;
    }
    @Override
    public String sing(String sing_name){
        System.out.println(name+"正在唱:"+sing_name);
        return "谢谢";
    }
    @Override
    public void dence(String dance_name){
        System.out.println(name+"正在跳舞:"+dance_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 + "}";
    }
}
java 复制代码
package dp_demo;

public interface Star {

    public abstract String sing(String sing_name);

    public abstract void dence(String dance_name);

}
java 复制代码
package dp_demo;

public class test {
    public static void main(String[] args) {

        BigStar bigstar = new BigStar("周杰伦");

        Star star = ProxyUtil.getProxy(bigstar);

        String result = star.sing("七里香");

        System.out.println(result);

    }
}
相关推荐
Predestination王瀞潞7 小时前
Java EE开发技术 (报错解决 兼容问题 及 Jakara EE Web 官方手册提供的API接口聚合包)
java·java-ee·jstl·jakara背景
断剑zou天涯7 小时前
【算法笔记】Manacher算法
java·笔记·算法
梦未7 小时前
Spring控制反转与依赖注入
java·后端·spring
喜欢流萤吖~7 小时前
Lambda 表达式
java
ZouZou老师8 小时前
C++设计模式之适配器模式:以家具生产为例
java·设计模式·适配器模式
曼巴UE58 小时前
UE5 C++ 动态多播
java·开发语言
VX:Fegn08958 小时前
计算机毕业设计|基于springboot + vue音乐管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计
程序员鱼皮8 小时前
刚刚,IDEA 免费版发布!终于不用破解了
java·程序员·jetbrains
steins_甲乙8 小时前
C++并发编程(3)——资源竞争下的安全栈
开发语言·c++·安全