Java基础(三)- 多线程、网络通信、单元测试、反射、注解、动态代理

多线程基础

线程:一个程序内部的一条执行流程,只有一条执行流程就是单线程

java.lang.Thread代表线程

主线程退出,子线程存在,进程不会退出

可以使用jconsole查看

创建线程

有多个方法可以创建线程

  • 继承Thread类

    • 优点:编码简单
    • 缺点:无法继承其他类,不利于功能的扩展
  • 实现Runnable接口

    • 优点:任务类只是实现了接口,可以继续继承其他类、实现其他接口,扩展性强
    • 缺点:需要多创建一个Runnable对象
  • 实现Callable接口和FutureTask类

    • 优点:可以返回线程执行结束之后的结果
    • 缺点:编码复杂

    执行为什么是start()?

    使用run不是多线程, 相当于直接调用方法 还是单线程

    start->start0(本地方法 JVM调用 C/C++实现的)
    

方法一

java 复制代码
public class Demo1 {
    public static void main(String[] args) throws Exception {
        //main是主线程执行的

        //新建了一个t线程
        Thread t = new primeThread();
        //启动线程 start自动调用run方法 必须要调用start方法
        //如果是t.run() 相当于直接调用方法 还是单线程
        t.start();

        for (int i = 0; i < 5; i++) {
            System.out.println("主线程");
            Thread.sleep(500);
        }
    }
}

class primeThread extends Thread{
    public primeThread(){

    }

    @Override
    public void run() {
        //描述线程的执行的任务
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程");
            try {
                Thread.sleep(500);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

方法二

java 复制代码
public class Demo2 {
    public static void main(String[] args) throws Exception {
        //runnable只是一个任务对象
        Runnable target = new prime1Thread();
        //需要线程对象接受任务对象 开辟新的线程
        new Thread(target).start();
        for (int i = 0; i < 5; i++) {
            System.out.println("主线程");
            Thread.sleep(500);
        }
    }
}

class prime1Thread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程");
            try {
                Thread.sleep(500);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}


//可以使用匿名内部类
public class Demo2 {
    public static void main(String[] args) throws Exception {
        //需要线程对象进行调用任务对象开辟新的线程
        new Thread(()-> {
                for (int i = 0; i < 5; i++) {
                    System.out.println("子线程");
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }	
                }
        }).start();
        for (int i = 0; i < 5; i++) {
            System.out.println("主线程");
            Thread.sleep(500);
        }
    }
}

方法三

java 复制代码
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Demo3 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建一个Callable对象
        Callable<String> myCallable = new MyCallable(100);

        // 把Callable的对象封装成一个FutureTask对象(任务对象)
        // 未来任务对象的作用?
        // 1、是一个任务对象,实现下Runnable对象
        // 2、可以在线程执行完毕之后,用未来任务对象调用get方法获取线程执行完毕的结果
        //也可以使用匿名内部类
        FutureTask<String> stringFutureTask = new FutureTask<>(myCallable);

        new Thread(stringFutureTask).start();

        //获取结果会阻塞线程
        System.out.println(stringFutureTask.get());

    }
}


//泛型
class MyCallable implements Callable<String>{
    private  int n;

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

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

线程方法

  • setPriority()更改线程的优先级
  • getPriority()获取线程的优先级
  • interrupt中断线程,并不是真正的结束线程 所以一般用于中断正在休眠的线程
  • yield线程的礼让,不一定礼让成功(和join相反,线程的插队)
java 复制代码
public class Demo4 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread1("1号线程");
//        t1.setName("1号线程");//启动之前取名字
        t1.start();
        t1.join();
//        System.out.println(t1.getName());

        Thread t2 = new Thread1("2号线程");
//        t2.setName("2号线程");//启动之前取名字
        t2.start();
        t2.join();//t2线程执行完成之后才能继续往下执行
//        System.out.println(t2.getName());

        Thread t3 = new Thread1("3号线程");
        t3.start();
        t3.join();

        Thread m = Thread.currentThread();
        m.setName("最牛逼的名字");
//        System.out.println(m.getName());
        for (int i = 0; i < 5; i++) {
            System.out.println(m.getName()+"输出"+(i+1));
        }
    }
}

class Thread1 extends Thread{
    public Thread1(String name) {
        super(name);
    }

    @Override
    public void run() {
        Thread t= Thread.currentThread();
        for (int i = 0; i < 3; i++) {
            System.out.println("子线程"+t.getName()+"输出:"+(i+1));
        }
    }
}

线程终止

  • 当线程执行完成时,自动退出
  • 使用变量来控制run方法退出的方式停止线程

守护线程

当所有的用户线程都退出时,守护线程自动退出

垃圾回收机制

java 复制代码
public class Test {
    public static void main(String[] args) {
        //子线程设置为守护线程
        myDaemonThread myDaemonThread = new myDaemonThread();
        myDaemonThread.setDaemon(true);
        myDaemonThread.start();
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + " 执行");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }
}

/*
守护线程:
当用户线程退出后 子线程也自动退出
 */
class myDaemonThread extends Thread {
    @Override
    public void run() {
        while (true) {
            System.out.println(Thread.currentThread().getName() + " 正在执行");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

线程安全

概念

多个线程同时操作同一个共享资源的时候可能出现业务安全问题

模拟线程安全问题

java 复制代码
package Thread_;

public class Demo5 {

    public static void main(String[] args) {
        Thread xiaoHong = new DrawThread("小红");
        Thread xiaoMing = new DrawThread("小明");
        xiaoMing.start();
        xiaoHong.start();

    }
}
class Account{
    private static double moneys = 100000;
    private Account(){}

    public static double getMoneys() {
        return moneys;
    }

    public static void setMoneys(double moneys) {
        Account.moneys = moneys;
    }

    public static boolean drawMoneys(double moneys){
        String name = Thread.currentThread().getName();
        if (moneys>Account.getMoneys()){
            System.out.println(name+"来取钱,钱不够");
            return false;
        }

        Account.moneys-=moneys;
        System.out.println(name+"来取钱,取钱成功,剩余"+Account.moneys);
        return true;
    }
}

class DrawThread extends Thread{
    public DrawThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        Account.drawMoneys(100000.0);
    }
}

线程同步

认识线程同步

多个线程实现先后依次访问共享资源

**加锁:**每次只允许一个线程加锁,加锁之后才能访问,访问完毕之后自动解锁,然后其他线程才能再加锁继续

方法一:同步代码块

把访问共享资源的核心代码给上锁,保证线程安全

java 复制代码
synchronized(同步锁){
    访问共享资源的核心代码
}

对于当前同时执行的线程来说,同步锁必须是同一把(同一对象

锁对象的选择:

  • 实例对象:使用this
  • 静态对象:使用类型.class
java 复制代码
public class Demo5 {

    public static void main(String[] args) throws InterruptedException {
        Account acc1 = new Account(100000);
        Thread xiaoHong = new DrawThread("小红",acc1);
        Thread xiaoMing = new DrawThread("小明",acc1);
        xiaoMing.start();
        xiaoHong.start();

        Account acc2 = new Account(100000);
        Thread daGang = new DrawThread("大纲",acc2);
        Thread daLi = new DrawThread("大力",acc2);
        daGang.start();
        daLi.start();
    }
}

class Account {
    private double moneys;

    public Account() {
    }

    public Account(double moneys) {
        this.moneys = moneys;
    }

    public double getMoneys() {
        return moneys;
    }

    public void setMoneys(double moneys) {
        this.moneys = moneys;
    }

    public void drawMoneys(double moneys) throws InterruptedException {
        String name = Thread.currentThread().getName();
        /*
         * 两个人同时竞争lock这个对象(这把锁),只有一个人能够得到
         * 上锁之后另外一个人要等待开锁
         *
         * 但是这个lock对于所有的对象是一个锁
         * 一个对象上锁的时候 和该对象无关的对象也无法进入核心代码
         * 非static建议使用 this
         * static建议使用 ClassName.class
         * */
        synchronized (this) {
//            Thread.sleep(5000); 测试
            if (moneys > this.getMoneys()) {
                System.out.println(name + "来取钱,钱不够");
            } else {
                this.moneys -= moneys;
                System.out.println(name + "来取钱,取钱" + moneys + "成功,剩余" + this.moneys);
            }
        }
    }
}

class DrawThread extends Thread {
    private Account acc;
    public DrawThread(String name,Account acc) {
        super(name);
        this.acc = acc;
    }

    @Override
    public void run() {
        try {
            acc.drawMoneys(100000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

方法二:同步方法

访问共享资源的核心方法给上锁

java 复制代码
修饰符 synchronized 返回值类型 方法名称(形参列表){
    操作共享资源的代码
}
java 复制代码
public class Demo5 {

    public static void main(String[] args) throws InterruptedException {
        Account acc1 = new Account(100000);
        Thread xiaoHong = new DrawThread("小红", acc1);
        Thread xiaoMing = new DrawThread("小明", acc1);
        xiaoMing.start();
        xiaoHong.start();

        Account acc2 = new Account(100000);
        Thread daGang = new DrawThread("大纲", acc2);
        Thread daLi = new DrawThread("大力", acc2);
        daGang.start();
        daLi.start();
    }
}

class Account {
    private double moneys;

    public Account() {
    }

    public Account(double moneys) {
        this.moneys = moneys;
    }

    public double getMoneys() {
        return moneys;
    }

    public void setMoneys(double moneys) {
        this.moneys = moneys;
    }

    /*
    有一个隐含的锁 实例方法是 this  静态方法是 类型.class
     */
    public synchronized void drawMoneys(double moneys) throws InterruptedException {
        String name = Thread.currentThread().getName();
        if (moneys > this.getMoneys()) {
            System.out.println(name + "来取钱,钱不够");
        } else {
            this.moneys -= moneys;
            System.out.println(name + "来取钱,取钱" + moneys + "成功,剩余" + this.moneys);
        }
    }
}

class DrawThread extends Thread {
    private Account acc;

    public DrawThread(String name, Account acc) {
        super(name);
        this.acc = acc;
    }

    @Override
    public void run() {
        try {
            acc.drawMoneys(100000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

方法三:Lock锁

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

Lock是接口 ,不能直接实例化,可以采用它的实现类**ReentrantLock**来构建Lock锁对象。

java 复制代码
package Thread_;

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

public class Demo5 {

    public static void main(String[] args) throws InterruptedException {
        Account acc1 = new Account(100000);
        Thread xiaoHong = new DrawThread("小红", acc1);
        Thread xiaoMing = new DrawThread("小明", acc1);
        xiaoMing.start();
        xiaoHong.start();

        Account acc2 = new Account(100000);
        Thread daGang = new DrawThread("大纲", acc2);
        Thread daLi = new DrawThread("大力", acc2);
        daGang.start();
        daLi.start();
    }
}

class Account {
    /*
    创建了一个锁对象 每一个账户都有一个自己的锁对象
    不允许二次赋值
    */
    private final Lock lk = new ReentrantLock();
    private double moneys;

    public Account() {
    }

    public Account(double moneys) {
        this.moneys = moneys;
    }

    public double getMoneys() {
        return moneys;
    }

    public void setMoneys(double moneys) {
        this.moneys = moneys;
    }

    public void drawMoneys(double moneys) throws InterruptedException {
        String name = Thread.currentThread().getName();
        try {
            lk.lock();
            if (moneys > this.getMoneys()) {
                System.out.println(name + "来取钱,钱不够");
            } else {
                this.moneys -= moneys;
                System.out.println(name + "来取钱,取钱" + moneys + "成功,剩余" + this.moneys);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            lk.unlock();//无论try中代码是否有错误 都会解锁
        }

    }
}

class DrawThread extends Thread {
    private Account acc;

    public DrawThread(String name, Account acc) {
        super(name);
        this.acc = acc;
    }

    @Override
    public void run() {
        try {
            acc.drawMoneys(100000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

释放锁的时机

  • 当前线程的同步方法、同步代码块执行结束
  • 当前线程在同步方法、同步代码块中遇到breakreturn
  • 当前线程在同步方法、同步代码块中出现了未处理的Error或者Exception,导致异常结束
  • 当前线程在同步方法、同步代码块中执行了线程对象的wait()方法,当前线程暂停 释放锁,等待唤醒

不释放锁

  • Thread.sleep()Thread.yeild不会释放锁
  • suspend()挂起方法,也不会释放锁
    • suspendresume控制线程,不推荐使用

线程死锁

多个线程都占用了对方的锁资源,但是不肯相让,导致了死锁

java 复制代码
public class Demo {
    public static void main(String[] args) {
        new Thread(new MyDeadThread(false)).start();
        new Thread(new MyDeadThread(true)).start();
    }
}
class MyDeadThread implements Runnable{
    private boolean flag;
    private static Object o1 = new Object();
    private static Object o2 = new Object();
    public MyDeadThread() {
    }

    public MyDeadThread(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
       while (true){
           /*
           flag=true 占用o1锁 抢夺o2锁
           flag=false 占用o2锁 抢夺o1锁
           如果两个线程 一个占用o1 一个占用o2 那么就造成死锁
            */
           if (flag){
               synchronized (o1){
                   System.out.println("o1");
                   synchronized (o2){
                       System.out.println("o2");
                   }
               }
           }else {
               synchronized (o2){
                   System.out.println("o2");
                   synchronized (o1){
                       System.out.println("o1");
                   }
               }
           }
       }
    }
}

线程通信

当多个线程共同操作共享资源的时候,线程间通过某种方式相互告知自己的状态,相互协调,避免无效的资源争夺

生产者消费者模型

  • 生产者线程负责生产数据
  • 消费者线程负责消费生产者生产的数据
  • 生产者生产完数据应该等待,通知消费者消费;消费者消费完数据也应该等待,通知生产者生产
java 复制代码
public class ThreadTest {
    public static void main(String[] args) {
        Desk desk = new Desk();

        //3个生产者
        new Thread(()-> {while (true){desk.put();}},"厨师1").start();
        new Thread(()-> {while (true){desk.put();}},"厨师2").start();
        new Thread(()-> {while (true){desk.put();}},"厨师3").start();

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


import java.util.ArrayList;
import java.util.List;

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

    //这个五个人是同一把锁
    public synchronized void put(){
        try {
            String name = Thread.currentThread().getName();
            if (list.isEmpty()){
                list.add(name+"做的肉包子");
                System.out.println(name+"做的肉包子");
                Thread.sleep(500);

            }
            //等待自己 唤醒别人 先唤醒后等待
            //只能线程对象调用
            this.notify();
            this.wait();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    public synchronized void get(){
        try {
            String name = Thread.currentThread().getName();
            if (!list.isEmpty()){
                System.out.println(name + "吃了"+list.remove(0));
                Thread.sleep(500);
            }
            //等待自己 唤醒别人 先唤醒后等待
            //只能线程对象调用
            this.notify();
            this.wait();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }
}

线程池

概念

可以复用线程的技术

**不使用线程池:**用户每发起一个请求,后台就需要创建一个新线程来处理,下次新任务来了肯定又要创建新线程处理的,而创建新线程的开销是很大的,并且请求过多时,肯定会产生大量的线程出来,这样会严重影响系统的性能。

使用ExecutorService创建线程池

使用ExecutorService的实现类ThreadPoolExecutor创建一个线程池对象(JDK5.0之后提供代表线程池的接口:ExecutorService

  1. corePoolSize:指定线程池的核心线程的数量
  2. maximumPoolSize:指定线程池的最大线程的数量
  3. keepAliceTime:指定临时线程的存活时间
  4. unit:指定临时线程存货时间的单位(秒、分、时、天)
  5. workQueue:指定线程池的任务队列
  6. threadFactory:指定线程池的线程工厂
  7. handler:指定线程池的任务拒绝策略(线程都在忙,任务队列也满了的时候,新任务来了该怎么处理)
java 复制代码
        /*
        ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
         */
        ExecutorService poolExecutor = new ThreadPoolExecutor(3, 5, 8, TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
    

什么时候创建临时对象?

新任务提交时发现核心线程都在忙任务队列也满了,并且还可以创建临时线程,才会创建

什么时候会开始拒绝新任务?

核心线程和临时线程都在忙,任务队列也满了

新任务拒绝策略

处理Runnable任务

java 复制代码
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class _ThreadPool {
    public static void main(String[] args) {
        /*
        ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
         */
        ExecutorService poolExecutor = new ThreadPoolExecutor(3, 5,
                8, TimeUnit.SECONDS, new ArrayBlockingQueue<>(4),
                Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());

        MyRunnable myRunnable1 = new MyRunnable();
        MyRunnable myRunnable2 = new MyRunnable();
        MyRunnable myRunnable3 = new MyRunnable();

        //三个核心线程在忙
        poolExecutor.execute(myRunnable1);
        poolExecutor.execute(myRunnable2);
        poolExecutor.execute(myRunnable3);

        //任务队列占满
        poolExecutor.execute(myRunnable3);
        poolExecutor.execute(myRunnable3);
        poolExecutor.execute(myRunnable3);
        poolExecutor.execute(myRunnable3);

        //可以创建两个临时线程
        poolExecutor.execute(myRunnable3);
        poolExecutor.execute(myRunnable3);

        //拒绝新任务
        poolExecutor.execute(myRunnable3);

//        poolExecutor.shutdown();//等任务执行完后关闭线程池
//        poolExecutor.shutdownNow();//立刻关闭线程池
    
    }
}

class MyRunnable implements Runnable {
    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name + "666");
        try {
            Thread.sleep(100000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

处理Callable任务

java 复制代码
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class _ThreadPool {
    public static void main(String[] args) throws  Exception {
        /*
        ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
         */
        ExecutorService poolExecutor = new ThreadPoolExecutor(3, 5,
                8, TimeUnit.SECONDS, new ArrayBlockingQueue<>(4),
                Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());

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

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

    }
}


class MyCallable implements Callable<String> {
    private  int n;

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

    @Override
    public String call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum+=i;
        }
        return Thread.currentThread().getName()+"计算出1-"+n+"的和为"+sum;
    }
}

使用Executors创建线程池(大型并发系统不建议)

(线程池的工具类)调用方法返回不同特点的线程池对象

  • FixedThreadPool、SingleThreadExecutor允许请求队列长度为Integer.MAX_VALUE
  • CachedThreadPool允许创建线程数量为Integer.MAX_VALUE

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

java 复制代码
ExecutorService pool = Executors.newFixedThreadPool(3);

核心线程配置数量

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

并发和并行

并发的含义

进程中的线程是由CPU负责调度执行的,但是CPU能同时处理线程的数量是有限的。

为了保证全部线程都能往前执行,CPU会轮询为系统的每个线程服务,由于CPU切换速度很快,给我们的感觉就是这些线程在同时执行,这就是并发

并行的含义

同一时刻上,同时有多个线程在被CPU调度执行

线程生命周期

也就是线程从生到死的过程,经历的各种状态以及状态转换

理解线程这些状态有利于提高并发编程的理解能力

扩展:悲观锁和乐观锁

悲观锁:一开始就加锁,没有安全感,每次只能一个线程进入,访问完毕后再解锁。线程安全 性能较差

乐观锁:一开始不上锁,认为没问题,等出现线程安全的时候才开始控制。线程安全 性能较好

java 复制代码
//乐观锁
import java.util.concurrent.atomic.AtomicInteger;

public class Demo7 {
    //一个静态变量,100个线程,每个线程对其加100次
    public static void main(String[] args) {
        Runnable mRunnable = new MRunnable2();
        for (int i = 0; i < 100; i++) {
            //100个线程执行相同的任务
            new Thread(mRunnable).start();
        }
    }
}

class MRunnable2 implements Runnable {
//    private int count;
    //整数修改的乐观锁:原子类,
    private AtomicInteger count = new AtomicInteger();
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("count====>" + (count.incrementAndGet()));
        }
    }
}

多线程练习

java 复制代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;

public class Test1 {
    public static void main(String[] args) throws InterruptedException {
        /**
         * 目标:有100份礼品,小红,小明两人同时发送,当剩下的礼品小于10份的时候则不再送出,
         * 利用多线程模拟该过程并将线程的名称打印出来。并最后在控制台分别打印小红,小明各自送出多少分礼物。
         */
        ArrayList<String> gifts = new ArrayList<>();
        String[] names = {"口红", "包包", "腰带", "剃须刀", "香水", "衣服"};
        Random r = new Random();
        for (int i = 0; i < 100; i++) {
            gifts.add(names[r.nextInt(names.length)] + (i + 1));
        }

        sendThread xm = new sendThread(gifts, "小明");
        sendThread xh = new sendThread(gifts, "小红");
        xm.start();
        xh.start();

        xm.join();
        xh.join();

        System.out.println("小明送出去" + xm.getCount());
        System.out.println("小红送出去" + xh.getCount());

    }
}


class sendThread extends Thread {
    private ArrayList<String> gifts;
    private int count;

    public int getCount() {
        return count;
    }

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

    public sendThread() {
    }

    public sendThread(ArrayList<String> gifts, String name) {
        super(name);
        this.gifts = gifts;
    }

    @Override
    public void run() {
        Random r = new Random();
        String name = Thread.currentThread().getName();
        while (true) {
            synchronized (gifts) {
                int length = gifts.size();
                if (length < 10)
                    break;
                String s = gifts.remove(r.nextInt(length));
                System.out.println(name + "送出礼物" + s);
                ++count;
            }
        }
    }
}

网络编程基础

可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信的)

java.net.*的包下

网络通信三要素

  • IP地址:设备在网络中的地址,是唯一的标识
  • 端口号:应用程序在设备中唯一的标识
  • 协议:连接和数据在网络中传输的规则

java获取Ip地址InetAddress

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/fe8b9baf7b3b42619311c8ac4ff54acf.png)
java 复制代码
import java.net.InetAddress;
import java.net.UnknownHostException;

public class GetIP {
    public static void main(String[] args) throws Exception {
        //本机
        InetAddress ip = InetAddress.getLocalHost();
        System.out.println(ip.getHostName());
        System.out.println(ip.getHostAddress());

        //指定
        InetAddress ipBaiDu = InetAddress.getByName("www.baidu.com");
        System.out.println(ipBaiDu.getHostName());
        System.out.println(ipBaiDu.getHostAddress());
        
        //本机ping 百度
        System.out.println(ipBaiDu.isReachable(6000));
    }
}

UDP通信

java.net.DatagramSocket实现UDP通信

一发一收

Client

java 复制代码
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;


public class Client {
    public static void main(String[] args) throws Exception {
        //创建客户端 以及客户端端口
        DatagramSocket socket  = new DatagramSocket(6666);

        String data = "我是客户端,哈哈哈";
        byte[]bytes = data.getBytes();
        //创建数据包
        DatagramPacket packet = new DatagramPacket(bytes,bytes.length, InetAddress.getLocalHost(),5555);

        //发送数据
        socket.send(packet);

        System.out.println("客户端数据发送完毕");
        //释放资源
        socket.close();
    }
}

Serve

java 复制代码
import java.net.DatagramPacket;
import java.net.DatagramSocket;


public class Serve {
    public static void main(String[] args) throws Exception {
        System.out.println("===服务端启动===");
        //创建服务端 注册服务端端口
        DatagramSocket socket = new DatagramSocket(5555);

        byte[] buffer = new byte[1024*64];//64KB  UDP一个数据包最大为64KB
        //创建一个用来接收数据的数据包对象
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

        //接受数据
        socket.receive(packet);

        //从字节数组中获取接受的数据
        int len = packet.getLength();
        String data = new String(buffer,0,len);
        System.out.println(data);

        //获取客户端的IP 端口
        System.out.println(packet.getAddress().getHostAddress());
        System.out.println(packet.getPort());

        //释放资源
        socket.close();
    }
}
多发多收

可以多个用户同时发送

Client

java 复制代码
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;


public class Client {
    public static void main(String[] args) throws Exception {
        //创建客户端 以及客户端端口(默认随机分配)
        DatagramSocket socket  = new DatagramSocket();
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("请输入消息://exit是退出");
            String msg = sc.nextLine();
            if (msg.equals("exit")){
                System.out.println("欢迎下次光临");
                break;
            }
            byte[]bytes = msg.getBytes();
            //创建数据包
            DatagramPacket packet = new DatagramPacket(bytes,bytes.length, InetAddress.getLocalHost(),5555);

            //发送数据
            socket.send(packet);
        }
        socket.close();

    }
}

Serve

java 复制代码
import java.net.DatagramPacket;
import java.net.DatagramSocket;


public class Serve {
    public static void main(String[] args) throws Exception {
        System.out.println("===服务端启动===");
        //创建服务端 注册服务端端口
        DatagramSocket socket = new DatagramSocket(5555);

        byte[] buffer = new byte[1024*64];//64KB  UDP一个数据包最大为64KB
        //创建一个用来接收数据的数据包对象
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

        while (true) {
            //接受数据
            socket.receive(packet);

            //从字节数组中获取接受的数据
            int len = packet.getLength();
            String data = new String(buffer,0,len);
            System.out.println(data);

            //获取客户端的IP 端口
            System.out.println(packet.getAddress().getHostAddress());
            System.out.println(packet.getPort());
            System.out.println("----------------");
        }
    }
}

TCP通信

客户端:java.net.Socket

一发一收

client

java 复制代码
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

public class ClientTCP {
    public static void main(String[] args) throws Exception {
        //创建socket对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 5555);

        //从socket通信管道中得到一个字节输出流
        OutputStream os = socket.getOutputStream();

        //封装成数据输出流
        DataOutputStream dataOutputStream = new DataOutputStream(os);

        //写入数据
        dataOutputStream.writeUTF("你好呀!");
        //关闭数据流
        dataOutputStream.close();

        //关闭socket
        socket.close();
    }
}

serve

java 复制代码
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ServeTCP {
    public static void main(String[] args) throws Exception{
        System.out.println("--服务端启动--");
        //创建服务端对象 绑定端口
        ServerSocket serverSocket = new ServerSocket(5555);

        //等待连接
        Socket socket = serverSocket.accept();

        //接受数据
        InputStream ds = socket.getInputStream();

        //封装
        DataInputStream dataInputStream = new DataInputStream(ds);

        //接受数据
        String s = dataInputStream.readUTF();
        System.out.println(s);

        //客户端ip地址
        System.out.println(socket.getRemoteSocketAddress());

        dataInputStream.close();
        socket.close();
    }
}
多发多收

client

java 复制代码
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Objects;
import java.util.Scanner;

public class ClientTCP {
    public static void main(String[] args) throws Exception {
        //创建socket对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 5555);

        //从socket通信管道中得到一个字节输出流
        OutputStream os = socket.getOutputStream();

        //封装成数据输出流
        DataOutputStream dataOutputStream = new DataOutputStream(os);

        Scanner sc = new Scanner(System.in);
        while (true) {
            //写入数据
            System.out.println("请说:");
            String s = sc.nextLine();
            if (Objects.equals(s, "exit")){
                System.out.println("欢迎下次光临");
                break;  
            }
            dataOutputStream.writeUTF(s);
            dataOutputStream.flush();
        }
        //关闭数据流
        dataOutputStream.close();

        //关闭socket
        socket.close();
    }
}

serve

java 复制代码
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ServeTCP {
    public static void main(String[] args) throws Exception{
        System.out.println("--服务端启动--");
        //创建服务端对象 绑定端口
        ServerSocket serverSocket = new ServerSocket(5555);

        //等待连接
        Socket socket = serverSocket.accept();

        //接受数据
        InputStream ds = socket.getInputStream();

        //封装
        DataInputStream dataInputStream = new DataInputStream(ds);

        //接受数据
        while (true) {
            try {
                String s = dataInputStream.readUTF();
                System.out.println(s);

                //客户端ip地址
//                System.out.println(socket.getRemoteSocketAddress());
            } catch (IOException e) {
                System.out.println(socket.getRemoteSocketAddress()+"离线");
                break;
            }
        }

        dataInputStream.close();
        socket.close();
    }
}
多个客户端连接一个服务端

服务端:

  • 主线程负责接受客户端连接
  • 子线程负责具体每一个客户端

client

java 复制代码
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

public class ClientTCP {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("127.0.0.1", 8888);
        OutputStream os = socket.getOutputStream();
        DataOutputStream dos = new DataOutputStream(os);
        Scanner sc = new Scanner(System.in);
        while (true){
            String s = sc.nextLine();
            if (s.equals("exit")){
                System.out.println("欢迎下次光临");
                dos.close();
                socket.close();
                break;
            }
            dos.writeUTF(s);
            dos.flush();
        }

    }
}

serve

java 复制代码
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;

public class ServeTCP {
    public static void main(String[] args) throws Exception {
        System.out.println("服务端开启...");
        ServerSocket serverSocket = new ServerSocket(8888);
        while (true) {
            Socket socket = serverSocket.accept();
            System.out.println(socket.getRemoteSocketAddress()+"上线了");
            new Thread(new SocketThread(socket)).start();
        }
    }
}

class SocketThread implements Runnable{
    private Socket socket;
    public SocketThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        SocketAddress remoteSocketAddress = socket.getRemoteSocketAddress();
        try {
            InputStream is = socket.getInputStream();
            DataInputStream dis = new DataInputStream(is);
            while (true) {
                try {
                    String s = dis.readUTF();
                    System.out.println(remoteSocketAddress+"发送:"+s);
                } catch (Exception e) {
                    System.out.println(remoteSocketAddress+"下线了");
                    socket.close();
                    dis.close();
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

案例:群聊

client

java 复制代码
import java.io.DataInputStream;
import java.io.DataOutputStream;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;

public class ClientChat {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("127.0.0.1", 8888);
            new ClientThread(socket).start();
            OutputStream os = socket.getOutputStream();
            DataOutputStream dos = new DataOutputStream(os);
            Scanner sc = new Scanner(System.in);
            while (true) {
                String s = sc.nextLine();
                if (s.equals("exit")) {
                    System.out.println("欢迎下次光临");
                    socket.close();
                    dos.close();
                    break;
                }
                dos.writeUTF(s);
                dos.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class ClientThread extends Thread {
    private Socket socket;

    public ClientThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();
            DataInputStream dis = new DataInputStream(is);
            while (true) {
                try {
                    String msg = dis.readUTF();
                    System.out.println(msg);
                } catch (Exception e) {
                    dis.close();
                    socket.close();
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

serve

java 复制代码
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class ServeChat {
    public static final List<Socket> onlineUsers = new ArrayList<>();

    public static void main(String[] args) throws Exception{
        System.out.println("==服务器启动==");
        ServerSocket serverSocket = new ServerSocket(8888);
        while (true) {
            Socket socket = serverSocket.accept();
            onlineUsers.add(socket);
            new ServeReaderThread(socket).start();

        }
    }
}

class ServeReaderThread extends Thread {
    private Socket socket;

    public ServeReaderThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();
            DataInputStream dis = new DataInputStream(is);
            while (true) {
                try {
                    String msg = dis.readUTF();
                    System.out.println(msg);
                    sendAllOnlineUsers(socket,msg);
                } catch (Exception e) {
                    ServeChat.onlineUsers.remove(socket);
                    socket.close();
                    dis.close();
                    System.out.println(socket.getRemoteSocketAddress() + "下线");
                    break;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void sendAllOnlineUsers(Socket socket,String msg) throws Exception {
        for (Socket onlineUser : ServeChat.onlineUsers) {
            SocketAddress remoteSocketAddress = socket.getRemoteSocketAddress();
            if (Objects.equals(onlineUser.getRemoteSocketAddress(),remoteSocketAddress)){
                continue;
            }
            OutputStream os = onlineUser.getOutputStream();
            DataOutputStream dos = new DataOutputStream(os);
            dos.writeUTF(remoteSocketAddress+"说:"+msg);
            dos.flush();
        }
    }
}

案例:简易BS架构

java 复制代码
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Serve {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(8080);

        while (true){
            Socket socket = serverSocket.accept();
            System.out.println(socket.getRemoteSocketAddress()+"上线了");
            new CThread(socket).start();
        }
    }
}

class CThread extends Thread{
    private Socket socket;
    public CThread(Socket socket){
        this.socket=socket;
    }
    @Override
    public void run() {
        try {
            OutputStream os = socket.getOutputStream();
            PrintStream ps = new PrintStream(os);
            /*
            服务器必须给浏览器相应Http协议规定的格式
             */
            ps.println("HTTP/1.1 200 OK");
            ps.println("Content-Type:text/html;charset=UTF-8");
            ps.println();//必须换行
            ps.println("<div style='color:red;font-size:120px;'>java666</div>");
            ps.close();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

改进:线程池

java 复制代码
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Serve {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(8080);
        ThreadPoolExecutor pool = new ThreadPoolExecutor(16 * 2, 16 * 2, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(8), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
        while (true){
            Socket socket = serverSocket.accept();
            System.out.println(socket.getRemoteSocketAddress()+"上线了");
            pool.execute(new CThread(socket));
        }
    }
}

class CThread implements Runnable{
    private Socket socket;
    public CThread(Socket socket){
        this.socket=socket;
    }
    @Override
    public void run() {
        try {
            OutputStream os = socket.getOutputStream();
            PrintStream ps = new PrintStream(os);
            /*
            服务器必须给浏览器相应Http协议规定的格式
             */
            ps.println("HTTP/1.1 200 OK");
            ps.println("Content-Type:text/html;charset=UTF-8");
            ps.println();//必须换行
            ps.println("<div style='color:red;font-size:120px;'>java666</div>");
            ps.close();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

案例:多用户即时通信系统

需求分析

  • 用户登录
  • 拉取在线用户
  • 无异常退出
  • 私聊
  • 群聊
  • 发文件
  • 服务器推送新闻

java高级

单元测试

就是针对最小的功能单元(方法),编写测试代码对其进行正确性测试

junit单元测试框架

  • 可以灵活的编写测试代码,可以针对某个方法执行测试,也支持一键完成对全部的方法自动化测试
  • 不需要程序员去分析测试结果,会自动生成测试报告

具体使用

java 复制代码
public class Demo {
    public static void printNumber(String name){
        if (name==null)return;
        System.out.println("名字长度:"+name.length());
    }

    public static int getMaxIndex(String data){
        if (data==null)
            return -1;
        return data.length();
    }
}
java 复制代码
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

/*
测试类
 */
public class DemoTest {
    @Before
    public void test1(){
        System.out.println("---------Before---------");
    }
    @After
    public void test2(){
        System.out.println("---------After---------");
    }
    @AfterClass
    public static void test3(){
        System.out.println("---------AfterClass---------");
    }
    @BeforeClass
    public static void test4(){
        System.out.println("---------BeforeClass---------");
    }
    /*
    公开 无返回值
     */
    @Test //测试方法
    public void testPrintNumber(){
        Demo.printNumber("admin");
        Demo.printNumber(null);
    }

    @Test //测试方法
    public void testGetMaxIndex(){
        //断言机制:可以通过预测业务方法的结果来测试 bug
        System.out.println(Demo.getMaxIndex("admin"));
        System.out.println(Demo.getMaxIndex(null));

        //断言机制:可以通过预测业务方法的结果来测试 bug
        Assert.assertEquals("有bug",4,Demo.getMaxIndex("admin"));
    }
}

以下是学习框架源码的时候会用到,开发几乎不会用

反射

认识反射

加载类,并允许以编程的方式解剖类中的各个成分(成员变量、方法、构造器等)

步骤

  • 加载类,获取类的字节码:Class对象
  • 获取类的构造器:Constructor对象
  • 获取类成员变量:Field对象
  • 获取类成员方法:Method对象

获取类的字节码

  • Class c1 = 类名.class
  • 调用Class提供的方法 public static Class forName(String package); 全类名
  • Object的方法 对象.getClass()

获取类的构造器

java 复制代码
import java.lang.reflect.Constructor;

public class Demo1 {
    public static void main(String[] args) throws Exception {
        Class c = Cat.class;
        Constructor constructor = c.getDeclaredConstructor();
        System.out.println(constructor.getName()+"--"+constructor.getParameterCount());

        Cat o = (Cat) constructor.newInstance();
        System.out.println(o);

        Constructor declaredConstructor = c.getDeclaredConstructor(String.class, int.class);
        System.out.println(declaredConstructor.getName()+"--"+declaredConstructor.getParameterCount());
        declaredConstructor.setAccessible(true);//打破修饰符的限制
        Cat o1 = (Cat)declaredConstructor.newInstance("学习", 5);
    }
}   

class Cat{
    private String name;
    private int age;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public Cat() {
    }

    private Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

获取类的成员变量

java 复制代码
import java.lang.reflect.Field;

public class Demo1 {
    public static void main(String[] args) throws Exception {
        Class c = Cat.class;
        Field[] fields = c.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getName()+"--"+field.getType());
        }

        Field name = c.getDeclaredField("name");
        System.out.println(name.getName()+"--"+name.getType());
        Cat cat = new Cat();
        name.setAccessible(true);
        name.set(cat,"猫猫");
        System.out.println(name.get(cat));
    }
}

class Cat{
    public static int a;
    public static final String COUNTRY ="中国";
    private String name;
    private int age;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public Cat() {
    }

    private Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

获取类的成员方法

作用、应用场景

基本作用:可以得到一个类全部成分对其操作;可以破坏封装性;适合做java的框架

java 复制代码
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args)throws Exception {
        Student stu = new Student("小明", 18, 82.5);
        Teacher tea = new Teacher("大强", 58);
        saveObject(stu);
        saveObject(tea);
    }
    public static void saveObject(Object obj) throws Exception {
        Class o = obj.getClass();
        String cname = o.getSimpleName();
        PrintStream ps = new PrintStream(new FileOutputStream("./out/obj.txt",true));
        ps.println("---------"+cname+"---------");
        Field[] fields = o.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            String name = field.getName();
            String value = field.get(obj)+"";
            ps.println(name+":"+value);
        }
        ps.close();
    }
}
class Student{
    private String name;
    private int age;
    private double sorce;

    public Student(String name, int age, double sorce) {
        this.name = name;
        this.age = age;
        this.sorce = sorce;
    }
}

class Teacher{
    private String name;
    private int age;

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

注解

就是java中特殊的标记,比如@override、@Test等

作用:让其他程序根据注解信息来决定怎么执行程序

注解可以用在类、方法、构造器、成员变量、参数等等

自定义注解

java 复制代码
public @interface 注解名称{
    public 属性类型 属性名() default 默认值;
}

只有一个注解 且为 value 可以省略不写value

注解原理

注解本质就是一个接口,java中所有的注解都是继承了Annotation的接口

@注解(...)其实就是一个实现类对象,实现了该注解以及Annotation的接口

元注解

修饰注解的注解

注解的解析

就是判断类上、方法上、成员变量上是否存在注解,并把注解里的内容给解析出来。

要解析谁的注解,就要先拿到谁

Class、Method、Field,Constructor、都实现了AnnotatedElement接口,它们都拥有解析注解的能力。

java 复制代码
package annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE,ElementType.METHOD})//当前被修饰的注解只能使用在类上
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest{
    String value();
    double aaa() default 100;
    String[] bbb();
}
java 复制代码
package annotation;

@MyTest(value = "大强",aaa = 199.9,bbb={"css","java","html"})

public class Demo {
@MyTest(value = "小明",aaa = 99.9,bbb={"java","html"})
    void test(){}
}
java 复制代码
package annotation;


import java.lang.reflect.Method;
import java.util.Arrays;

public class AnnotationTest {
    public static void main(String[] args) throws Exception {
        Class c = Demo.class;
        Method test = c.getDeclaredMethod("test");
        if (c.isAnnotationPresent(MyTest.class)) {
            MyTest myTest = (MyTest) c.getDeclaredAnnotation(MyTest.class);
            System.out.println(myTest.value());
            System.out.println(myTest.aaa());
            System.out.println(Arrays.toString(myTest.bbb()));
        }
        if (test.isAnnotationPresent(MyTest.class)) {
            MyTest myTest = test.getDeclaredAnnotation(MyTest.class);
            System.out.println(myTest.value());
            System.out.println(myTest.aaa());
            System.out.println(Arrays.toString(myTest.bbb()));
        }
    }
}

应用场景

模拟junit

java 复制代码
package annotation;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class TestTest {
    @MyTest2
    public void test1(){
        System.out.println("==test1==");
    }
    public void test2(){
        System.out.println("==test2==");
    }
    
    public void test3(){
        System.out.println("==test3==");
    }

    public static void main(String[] args) throws Exception {

        Class c = TestTest.class;
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(MyTest2.class)){
                method.invoke(new TestTest());
            }
        }
    }
}

动态代理

概念

对象做的事情太多的话,可以通过代理来转移部分职责

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-blog.csdnimg.cn/direct/867cad296ec44d6385e47ae55f719acc.png)
java 复制代码
package proxy;

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

public class ProxyUtil {
    public static Star createProxy(BigStar bigStar) {
        /*
        参数1:指定一个类加载器
        参数2:指定生成的代理是什么样子,也就是有什么方法
        参数3:指定生成的代理对象要干什么事情
         */
        return (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("准备话筒,收钱20w");
                }else if (method.getName().equals("dance")){
                    System.out.println("准备场地,收钱1000w");

                }
                return method.invoke(bigStar,args);
            }
        });
    }
}
java 复制代码
package proxy;

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(name+"正在跳舞~~~");

    }
}
java 复制代码
package proxy;

public interface Star {
    public String sing(String name);
    public void dance();
}
java 复制代码
package proxy;

public class Test {
    public static void main(String[] args) {
        BigStar s = new BigStar("杨超越");
        Star starProxy = ProxyUtil.createProxy(s);
        String rs = starProxy.sing("好日子");
        System.out.println(rs);
        System.out.println("--------------------------");
        starProxy.dance();
    }
}

坦克大战

java坐标系

下图说明了Java坐标系。坐标原点位于左上角,以像素为单位。在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。

java 复制代码
package tankeGame;

import javax.swing.*;
import java.awt.*;


public class Draw extends JFrame {//JFrame 对应窗口 可以理解为一个画框
    private MyPanel mp =null;//定义一个画板

    public Draw(){
        //初始化画板
        mp = new MyPanel();
        //画板放入窗口
        this.add(mp);
        //设置窗口大小
        this.setSize(1000,800);
        //可以显示
        this.setVisible(true);
        //点窗口的× 程序退出
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {
        new Draw();
    }
}

//1.定义一个MyPanel继承JPanel,这个就是画板  画图形
class MyPanel extends JPanel{
    /*
    MyPanel:画板(面板)对象
    Graphics g:画笔

    paint调用时机:
    1.组件第一次在屏幕中显示的时候,系统自动调用
    2.窗口最小化 再最大化
    3.窗口大小发生变化
    4.repaint函数被调用
     */
    @Override
    public void paint(Graphics g) {//绘图的方法
        super.paint(g);//调用父类的方法完成初始化

        //画一个圆
        g.drawOval(10,10,100,100);
        //画直线
        g.drawLine(10,10,60,60);
        //画矩形
        g.drawRect(10,10,100,100);
        //填充矩形
        //设置画笔颜色
        g.setColor(Color.BLUE);
        g.fillRect(50,50,100,100);
        g.fillOval(200,200,50,60);

        //画图片
        //1.加载图片资源
        Image image = Toolkit.getDefaultToolkit().getImage("d:/shangan.png");
        //2.画图片
        g.drawImage(image,300,300,300,300,this);

        //画字符串
        g.setColor(Color.cyan);
        g.setFont(new Font("隶书",Font.BOLD,50));
        //位置是字体的左下角
        g.drawString("yb0os1",500,100);
    }
}

事件处理机制

委派事件模型

java 复制代码
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

//事件控制 键盘控制小球的移动
//画笔
public class BallMove extends JFrame {
    private DrawBall ball = null;

    public BallMove() {
        ball = new DrawBall();
        this.add(ball);
        this.setVisible(true);
        this.setSize(500, 400);
        this.addKeyListener(ball);//JFame对象可以监听ball上面发生的键盘事件
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {
        new BallMove();
    }
}

//画板
//KeyListener 监听器 监听键盘事件
class DrawBall extends JPanel implements KeyListener {
    int x = 10;
    int y = 10;

    @Override//有字符输出时 该方法会触发
    public void keyTyped(KeyEvent e) {

    }

    @Override//当某个键被按下时 该方法会触发
    public void keyPressed(KeyEvent e) {
//        System.out.println((char) e.getKeyChar() + "被按下");
        //根据用户按下的不同键,来处理小球的移动
        //java中给每一个键分配一个值
        switch (e.getKeyCode()){
            case KeyEvent.VK_DOWN://向下的箭头
                ++y;break;
            case KeyEvent.VK_UP://向上的
                --y;break;
            case KeyEvent.VK_LEFT://向左
                --x;break;
            case KeyEvent.VK_RIGHT://向右
                ++x;break;
        }

        //重绘面板
        this.repaint();
    }

    @Override//当某个键被松开时 该方法会触发
    public void keyReleased(KeyEvent e) {

    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        g.fillOval(x, y, 20, 20);
    }
}
相关推荐
2401_857439692 小时前
SSM 架构下 Vue 电脑测评系统:为电脑性能评估赋能
开发语言·php
SoraLuna3 小时前
「Mac畅玩鸿蒙与硬件47」UI互动应用篇24 - 虚拟音乐控制台
开发语言·macos·ui·华为·harmonyos
xlsw_3 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹4 小时前
基于java的改良版超级玛丽小游戏
java
Dream_Snowar4 小时前
速通Python 第三节
开发语言·python
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭4 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫5 小时前
泛型(2)
java
超爱吃士力架5 小时前
邀请逻辑
java·linux·后端
南宫生5 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石5 小时前
12/21java基础
java