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);
    }
}
相关推荐
救救孩子把12 分钟前
深入理解 Java 对象的内存布局
java
落落落sss15 分钟前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
万物皆字节20 分钟前
maven指定模块快速打包idea插件Quick Maven Package
java
夜雨翦春韭27 分钟前
【代码随想录Day30】贪心算法Part04
java·数据结构·算法·leetcode·贪心算法
简单.is.good33 分钟前
【测试】接口测试与接口自动化
开发语言·python
我行我素,向往自由34 分钟前
速成java记录(上)
java·速成
一直学习永不止步39 分钟前
LeetCode题练习与总结:H 指数--274
java·数据结构·算法·leetcode·数组·排序·计数排序
邵泽明40 分钟前
面试知识储备-多线程
java·面试·职场和发展
Yvemil71 小时前
MQ 架构设计原理与消息中间件详解(二)
开发语言·后端·ruby
程序员是干活的1 小时前
私家车开车回家过节会发生什么事情
java·开发语言·软件构建·1024程序员节