黑马程序员Java零基础视频教程_下部(P135-P200)

  • 1 多线程
    • 1.1 什么是多线程?
    • 1.2 多线程的并发与并行
    • 1.3 多线程的实现方式
      • 1.3.1 继承Thread类的方式进行实现
      • 1.3.2 实现Runnable接口的方式进行实现
      • 1.3.3 利用Callable接口和Future接口方式实现
    • 1.4 常见的成员方法
      • 1.4.1 线程的优先级
      • 1.4.2 守护线程 SetDaemon(boolean on)
      • 1.4.3 礼让线程 yield()(了解)
      • 1.4.4 插入线程 join()(了解)
      • 1.4.5 线程的生命周期
    • 1.5 线程安全的问题
      • 1.5.1 同步代码块
      • 1.5.2 同步代码块的两个小细节
      • 1.5.3 同步方法
      • 1.5.4 lock锁
    • 1.6 死锁
    • 1.7 生产者和消费者(等待唤醒机制第一种实现方式)
      • 1.7.1 消费者等待
      • 1.7.1 生产者等待
    • 1.8 阻塞队列(等待唤醒机制第二种实现方式)
      • 1.8.1 阻塞队列的继承结构
      • 1.8.2 线程的状态
    • 1.9 综合练习
      • 1.9.1 买电影票
      • 1.9.2 抢红包
      • 1.9.3 抽奖
      • 1.9.4 多线程统计
      • 1.9.5 线程栈
      • 1.9.6 多线程比较
    • 1.10 线程池
    • 1.11 自定义线程池超详细解析
    • 1.12 最大并行数
    • 1.13 线程池多大合适
    • 1.14 多线程的额外扩展内容
  • 2 网络编程
    • 2.1 什么是网络编程?
    • 2.2 网络编程三要素
      • 2.2.1 IP
      • 2.2.2 IPv4的地址分类
      • 2.2.3 InetAddress的使用
      • 2.2.4 端口号
      • 2.2.5 协议
    • 2.3 UDP通信程序
      • 2.3.1 UDP发送通信程序
      • 2.3.2 UDP接送通信程序
      • 2.3.3 UDP聊天室小练习
      • 2.3.4 UDP三种通信方式
    • 2.4 TCP通信程序
      • 2.4.1 三次握手协议
      • 2.4.2 四次挥手协议
    • 2.5 综合练习
      • 2.5.1 多发多收
      • 2.5.2 接收并反馈
      • 2.5.3 上传文件
      • 2.5.4 文件名重复
      • 2.5.5 多线程版的服务端
      • 2.5.6 线程池版的服务端
      • 2.5.7 BS架构模型
  • 3 反射
    • 3.1 反射的概述
    • 3.2 获取class对象的三种方式
    • 3.3 反射获取构造方法
    • 3.4 反射获取成员变量
    • 3.5 反射获取成员方法
  • 4 动态代理
    • 4.1 动态代理的思想分析
    • 4.2 动态代理的代码实现

1 多线程

1.1 什么是多线程?

  • 线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
  • 进程:进程是程序的基本执行实体


多线程就是可以同时干多件事情,把等待的时间充分利用起来

多线程——提高程序的运行效率

多线程的应用场景

  1. 软件中的耗时操作——拷贝、迁移大文件、加载大量的资源文件
  2. 所有的聊天软件
  3. 所有的后台服务器

1.2 多线程的并发与并行

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

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

但是电脑没有多个CPU啊?


以2核4线程为例:

1.3 多线程的实现方式

多线程的实现方式
①继承Thread类的方式进行实现
②实现Runnable接口的方式进行实现
③利用Callable接口和Future接口方式实现

1.3.1 继承Thread类的方式进行实现

创建新执行线程有两种方法。一种方法是将类声明为Thread 的子类。该子类应重写Thread 类的run 方法。接下来可以分配并启动该子类的实例。
例如,计算大于某一规定值的质数的线程可以写成:

class PrimeThread extends Thread {PrimeThread(long minPrime) {this. minPrime = minPrime;}public void run() {// compute primes larger than minPrime}
}

然后,下列代码会创建并启动一个线程:

PrimeThread p = new Prime Ihread(143);
p. start();

MyThread.java

public class MyThread extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println("HelloWorld");}}
}

Main.java

import java.lang.String;public class main {public static void main(String[] args){/** 1.自己定义一个类继承Thread* 2.重写run方法* 3.创建子类的对象,并启动线程**  */MyThread t1 = new MyThread();t1.start();}}

但是目前上面只运行了单个线程,所以与之前学的差不多,唯一区别就是我把函数方法换成了start
下面我们建立2个线程看一下结果

public class MyThread extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName()+"HelloWorld");}}
}
import java.lang.String;public class main {public static void main(String[] args){/** 1.自己定义一个类继承Thread* 2.重写run方法* 3.创建子类的对象,并启动线程**  */MyThread t1 = new MyThread();MyThread t2 = new MyThread();t1.setName("线程1");t2.setName("线程2");t1.start();t2.start();}
}

1.3.2 实现Runnable接口的方式进行实现

创建线程的另一种方法是声明实现 Runable 接口的类。该类然后实现run方法。然后可以分配该类的实例,在创建Thread 时作为一个参数来传递并启动。采用这种风格的同一个例子如下所示:

class PrimeRun implements Runnable {long minPrime;PrimeRun (1ong minPrime){this. minPrime = minPrime;
}public void run()//compute primes larger than minPrime

然后,下列代码会创建并启动一个线程:

PrimeRun P = new PrimeRun(143);
new Thread(p). start() ;

每个线程都有一个标识名,多个线程可以同名。如果线程创建时没有指定标识名,就会为其生成一个新名称。

public class MyRun implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println("helloworld");}}
}
public class main {public static void main(String[] args){/** 1.自己定义一个类继承Runable* 2.重写run方法* 3.创建自己类的对象* 4.创建Thread类的对象并启动线程*/MyRun mr = new MyRun();Thread t1 = new Thread(mr);t1.start();}}

同样看不到,我们还是采用一些手段

public class MyRun implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) {Thread thread = Thread.currentThread();System.out.println(thread.getName()+": helloworld");}}
}

其中,Thread thread = Thread.currentThread();表示的是该时刻运行的是那个线程就指那个线程

import java.lang.String;public class main {public static void main(String[] args){/** 1.自己定义一个类继承Runable* 2.重写run方法* 3.创建自己类的对象* 4.创建Thread类的对象并启动线程*/MyRun mr = new MyRun();Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);t1.setName("线程1");t2.setName("线程2");t1.start();t2.start();}}

1.3.3 利用Callable接口和Future接口方式实现

为什么会有第三种呢?其实是对前面两种的补充,前两种是没有返回值的,我想要多线程的结果所以第三种来了!!!

import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer>{@Overridepublic Integer call() throws Exception {int sum =0;for (int i = 0; i <= 100; i++) {sum = sum + i;}return sum;}
}
import java.io.File;
import java.lang.String;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class main {public static void main(String[] args) throws ExecutionException, InterruptedException {/** 1.创建一个类Callable实现Callable接口* 2.重写call(是有返回值的,表示多线程运行的结果)* 3.创建MyCallable的对象(表示多线程要执行的任务)* 4.创建FutureTask的对象(作用管理多线程运行的结果)* 5.创建Thread类的对象*/MyCallable mc = new MyCallable();FutureTask<Integer> ft = new FutureTask<>(mc);//利用futureTask管理MyCallable 的结果Thread t1 = new Thread(ft);t1.start();System.out.println(ft.get());}}

1.4 常见的成员方法


Mythread.java

public class MyThread extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName()+":"+i);}}
}

Main.java

import java.lang.String;
public class main {public static void main(String[] args){MyThread t1 = new MyThread();MyThread t2 = new MyThread();t1.start();t2.start();//Thread-1:0//Thread-1:1//Thread-0:0//Thread-1:2//Thread-0:1//Thread-1:3//...}}

默认名字:Thread-X(0开始)

public class MyThread extends Thread{public MyThread() {}public MyThread(String name) {super(name);}@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName()+":"+i);}}
}
import java.lang.String;
public class main {public static void main(String[] args){MyThread t1 = new MyThread("小飞棍");MyThread t2 = new MyThread("来喽");t1.start();t2.start();}
}

观察下面的运行

import java.lang.String;
public class main {public static void main(String[] args){Thread t1 = Thread.currentThread();String name = t1.getName();System.out.println(name);//main//都是在main线程下发生滴}}

sleep方法

1.4.1 线程的优先级


随机性,线程的随机性运行时间的随机性


线程轮流进行,执行时间也是差不多的


Java默认有10档 1到10 默认是5
查看优先级

import java.lang.String;
import java.util.SortedMap;public class main {public static void main(String[] args){MyRun mr = new MyRun();Thread t1 = new Thread(mr,"飞机");Thread t2 = new Thread(mr,"坦克");System.out.println(t1.getPriority());//5System.out.println(t2.getPriority());//5}}

更换优先级

import java.lang.String;
import java.util.SortedMap;public class main {public static void main(String[] args){MyRun mr = new MyRun();Thread t1 = new Thread(mr,"飞机");Thread t2 = new Thread(mr,"坦克");t1.setPriority(1);t2.setPriority(10);System.out.println(t1.getPriority());//1System.out.println(t2.getPriority());//10//但绝不是t1先运行完再运行t2  t1先运行完的概率高}
}

1.4.2 守护线程 SetDaemon(boolean on)

细节: 当其他的非守护线程执行完毕之后,守护线程会陆续结束
通俗易懂: 当女神线程结束了,那么备胎也没有存在的必要了

备胎可能执行不完程序就结束了

同样!!!不说女神先执行完 而是同时执行

应用场景:

1.4.3 礼让线程 yield()(了解)


当遇到yield 抢夺权重新抢

1.4.4 插入线程 join()(了解)

1.4.5 线程的生命周期


1.5 线程安全的问题


Mythread.java

public class MyThread extends Thread{//这个类共享tictekstatic int ticket = 0;@Overridepublic void run() {while(true){if(ticket<100){try {Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}ticket++;System.out.println(getName()+"正在卖第"+ticket+"张票");}else break;}}
}

main.java

import java.lang.String;
import java.util.SortedMap;public class main {public static void main(String[] args){MyThread t1 = new MyThread();MyThread t2 = new MyThread();MyThread t3 = new MyThread();t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}}

上面代码有问题,有超出100的票 有重复的票 有的票没卖

1.5.1 同步代码块

因为线程在执行的时候,CPU的执行权随时会被抢走!!!

如果我们能把共享数据的这部分代码能够被锁起来,当我用的时候,别人抢不走


线程安全问题解决!!看下面代码

public class MyThread extends Thread{//这个类共享tictekstatic int ticket = 0;static Object obj = new Object();//锁对象!!!非常的随机 但是!!一定要唯一@Overridepublic void run() {while(true){synchronized (obj){if(ticket<100){try {Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}ticket++;System.out.println(getName()+"正在卖第"+ticket+"张票");}else break;}}}
}

1.5.2 同步代码块的两个小细节


如果锁不一样,那就没意义了

1.5.3 同步方法

  • 特点1:同步方法是锁住方法里面所有的代码
  • 特点2:锁对象不能自己指定
    非静态:this
    静态:当前类的字节码文件对象

记住快捷键Ctrl+Alt+M 方法生成

import javax.xml.namespace.QName;public class MyRun implements Runnable{int ticket = 0;//static 没必要加了就 因为我只会建立一个Runable类@Overridepublic void run() {while(true){if (method()) break;}}private  synchronized boolean method() {//thisif(ticket<100){try {Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}ticket++;System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");}else return true;return false;}
}
import java.lang.String;
import java.util.SortedMap;public class main {public static void main(String[] args){MyRun mr = new MyRun();Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);Thread t3 = new Thread(mr);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}}

1.5.4 lock锁

有没有可能手动释放锁和加锁

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class MyThread extends Thread{//这个类共享tictekstatic int ticket = 0;static  Lock lock = new ReentrantLock();@Overridepublic void run() {while(true){lock.lock();try {if(ticket<100){Thread.sleep(500);ticket++;System.out.println(getName()+"正在卖第"+ticket+"张票");}else break;} catch (InterruptedException e) {throw new RuntimeException(e);} finally {lock.unlock();}}}
}

1.6 死锁

也就是锁出现了锁的嵌套

死锁是什么?是程序的错误,千万不要在程序中涉及死锁


1.7 生产者和消费者(等待唤醒机制第一种实现方式)

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


桌上上有面条,吃货来吃,没有面条,厨师来做

1.7.1 消费者等待

当第一次线权被吃货抢到,那就wait,那就厨师来做,做完之后,厨师负责唤醒notify

1.7.1 生产者等待

当厨师抢到执行权,做好面条,执行权又被厨师抢到,那就等待wait

Cook.java

public class Cook extends Thread {@Overridepublic void run() {/*1.循环2.同步代码块3.判断共享数据是否到了末尾(到了末尾)4.判断共享数据是否到了末尾(没有到末尾)(核心逻辑)* */while (true){synchronized (Desk.lock){if(Desk.count==0){break;}else{if(Desk.foodFlag==0){try {Desk.lock.wait();//当前线程跟锁进行绑定} catch (InterruptedException e) {throw new RuntimeException(e);}}else{Desk.count--;System.out.println("吃货在吃,还可以吃 "+Desk.count+" 碗");Desk.lock.notify();Desk.foodFlag=0;}}}}}
}

Foodie.java

public class Foodie extends Thread{@Overridepublic void run() {while(true){synchronized (Desk.lock){if(Desk.count==0){break;}else{if(Desk.foodFlag==1){try {Desk.lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}else {System.out.println("厨师正在做面条");Desk.lock.notify();Desk.foodFlag=1;}}}}}
}

Desk.java

public class Desk {public  static  int foodFlag = 0; //用boolea 行不行,行 但是当线程多的时候!不够用public static int count = 10;//表示吃货最多吃10碗public  static Object lock = new Object();}

Main.java

import java.lang.String;
public class main {public static void main(String[] args){Cook cook = new Cook();Foodie foodie = new Foodie();cook.setName("吃货");foodie.setName("厨师");cook.start();foodie.start();}}

1.8 阻塞队列(等待唤醒机制第二种实现方式)


我们可以规定管道中最多放多少面条

1.8.1 阻塞队列的继承结构

它一共继承了4个接口

  1. Iterable (可以用增强For和迭代器遍历)
  2. Collection
  3. Queue
  4. BlockingQueue

  • ArrayBloackingQueue:底层是数组,有界(需要指定队列的长度)
  • LinkedBlockingQueue:底层是链表,无界,但不是真正的无界,最大为int的最大值(不需要指定队列长度)

需求:利用阻塞队列完成生产者和消费者(等待唤醒机制)的代码

细节:生产者和消费者必须使用同一个阻塞队列

Cook.java

import java.util.concurrent.ArrayBlockingQueue;public class Cook extends Thread {ArrayBlockingQueue<String> queue;public Cook(ArrayBlockingQueue<String> queue) {this.queue = queue;}@Overridepublic void run() {while(true)//不需要写锁{try {queue.put("面条");System.out.println("厨师做了1晚面条");} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

Foodie.java

import java.util.concurrent.ArrayBlockingQueue;public class Foodie extends Thread{ArrayBlockingQueue<String> queue;public Foodie(ArrayBlockingQueue<String> queue) {this.queue = queue;}@Overridepublic void run() {while(true)//不需要写锁{try {String food = queue.take();System.out.println("吃货在吃面");} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

main.java

import java.lang.String;
import java.util.concurrent.ArrayBlockingQueue;public class main {public static void main(String[] args){ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);Cook cook =new Cook(queue);Foodie foodie  = new Foodie(queue);cook.setName("吃货");foodie.setName("厨师");cook.start();foodie.start();}}
厨师做了1晚面条
厨师做了1晚面条
吃货在吃面
厨师做了1晚面条
吃货在吃面
吃货在吃面
厨师做了1晚面条
厨师做了1晚面条
吃货在吃面
厨师做了1晚面条
吃货在吃面
吃货在吃面
厨师做了1晚面条
厨师做了1晚面条
吃货在吃面
吃货在吃面
厨师做了1晚面条
厨师做了1晚面条
吃货在吃面
厨师做了1晚面条
吃货在吃面
厨师做了1晚面条
厨师做了1晚面条
吃货在吃面
吃货在吃面

为什么我限制了一碗,不说ABAB那样展示,因为输出语句是咋锁的外面的,但是运行没问题

1.8.2 线程的状态

回顾

但是上面不完整,看下面的七大状态

java是没有定义运行状态的,当运行的时候,交给操作系统了,虚拟机不管了。

1.9 综合练习

1.9.1 买电影票

public class MyThread extends Thread{static int ticket = 1000;@Overridepublic void run() {while(true){synchronized (MyThread.class){if(ticket==0){break;}else{ticket--;try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(getName()+"正在出售,还有"+ticket+"张在售");}}}}
}
import java.lang.String;
import java.util.concurrent.ArrayBlockingQueue;public class main {public static void main(String[] args){MyThread myThread1 = new MyThread();MyThread myThread2 = new MyThread();myThread1.setName("售票口1");myThread2.setName("售票口2");myThread1.start();myThread2.start();}}

1.9.2 抢红包

import java.util.Random;public class MyThread extends Thread{static double hongbao = 100;static int num = 3;@Overridepublic void run() {synchronized (MyThread.class){if(num == 0){System.out.println(getName()+"没抢到");}else{double price=0;if(num == 1){price = hongbao;}else{Random r = new Random();double bounds = hongbao-(num-1)*0.01;price = r.nextDouble(bounds);if(price<0.01)price =0.01;}hongbao = hongbao - price;System.out.println(getName()+"抢到了"+price+"元");num--;}}}
}
import java.lang.String;
import java.util.concurrent.ArrayBlockingQueue;public class main {public static void main(String[] args){MyThread myThread1 = new MyThread();MyThread myThread2 = new MyThread();MyThread myThread3 = new MyThread();MyThread myThread4 = new MyThread();MyThread myThread5 = new MyThread();myThread1.setName("张三");myThread2.setName("李四");myThread3.setName("王五");myThread4.setName("赵六");myThread5.setName("小白");myThread1.start();myThread2.start();myThread3.start();myThread4.start();myThread5.start();}}

1.9.3 抽奖

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;public class MyThread extends Thread{ArrayList<Integer> list;public MyThread(ArrayList<Integer> list) {this.list = list;}@Overridepublic void run() {while (true){synchronized (MyThread.class){if(list.size()==0){System.out.println("抽完了");break;}else{Collections.shuffle(list);int price = list.remove(0);System.out.println(getName()+"产生了一个"+price+"元大奖");}}try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
import java.lang.String;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.concurrent.ArrayBlockingQueue;public class main {public static void main(String[] args){ArrayList<Integer> arrayList = new ArrayList<>();Collections.addAll(arrayList,10,5,20,50,100,200,500,800,2,80,300,700);MyThread myThread1 = new MyThread(arrayList);MyThread myThread2 = new MyThread(arrayList);myThread1.setName("抽奖箱1");myThread2.setName("抽奖箱2");myThread1.start();myThread2.start();}}

1.9.4 多线程统计

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;public class MyThread extends Thread{ArrayList<Integer> list;static ArrayList<Integer> arrayList1 = new ArrayList<>();static ArrayList<Integer> arrayList2 = new ArrayList<>();public MyThread(ArrayList<Integer> list) {this.list = list;}@Overridepublic void run() {while (true){synchronized (MyThread.class){if(list.size()==0){if("抽奖箱1".equals(getName())){System.out.println(getName()+arrayList1);}else{System.out.println(getName()+arrayList2);}break;}else{Collections.shuffle(list);int price = list.remove(0);if("抽奖箱1".equals(getName())){arrayList1.add(price);}else{arrayList2.add(price);}}}try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
import java.lang.String;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.concurrent.ArrayBlockingQueue;public class main {public static void main(String[] args){ArrayList<Integer> arrayList = new ArrayList<>();Collections.addAll(arrayList,10,5,20,50,100,200,500,800,2,80,300,700);MyThread myThread1 = new MyThread(arrayList);MyThread myThread2 = new MyThread(arrayList);myThread1.setName("抽奖箱1");myThread2.setName("抽奖箱2");myThread1.start();myThread2.start();}}

1.9.5 线程栈

如果上面的抽奖箱有100个 难道要创建100个ArrayList嘛?

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;public class MyThread extends Thread{ArrayList<Integer> list;public MyThread(ArrayList<Integer> list) {this.list = list;}@Overridepublic void run() {ArrayList<Integer> boxList = new ArrayList<>();while (true){synchronized (MyThread.class){if(list.size()==0){System.out.println(getName()+boxList);break;}else{Collections.shuffle(list);int price = list.remove(0);boxList.add(price);}}try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}


内存中,堆是唯一的!但是栈!是跟线程有关的!也就是说一个线程就有一个栈!!!!



1.9.6 多线程比较

import com.sun.source.tree.BreakTree;import javax.swing.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.Callable;public class MyCallable implements Callable<Integer>{ArrayList<Integer> list;public MyCallable(ArrayList<Integer> list) {this.list = list;}@Overridepublic Integer call() throws Exception{ArrayList<Integer> boxList = new ArrayList<>();while (true){synchronized (MyThread.class){if(list.size()==0){System.out.println(Thread.currentThread().getName()+boxList);break;}else{Collections.shuffle(list);int price = list.remove(0);boxList.add(price);}}try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}}if(boxList.size()==0) return null;else{return Collections.max(boxList);}}}
import java.lang.String;
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class main {public static void main(String[] args) throws ExecutionException, InterruptedException {ArrayList<Integer> arrayList = new ArrayList<>();Collections.addAll(arrayList,10,5,20,50,100,200,500,800,2,80,300,700);MyCallable mc = new MyCallable(arrayList);FutureTask<Integer> ft1 = new FutureTask<>(mc);FutureTask<Integer> ft2 = new FutureTask<>(mc);Thread myThread1 = new Thread(ft1);Thread myThread2 = new Thread(ft2);myThread1.setName("抽奖箱1");myThread2.setName("抽奖箱2");myThread1.start();myThread2.start();System.out.println(ft1.get());System.out.println(ft2.get());}}

1.10 线程池




线程池诞生!!!!

线程池主要核心原理,

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

import javax.xml.namespace.QName;public class MyRun implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"-----------------");}}
import java.lang.String;
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.*;public class main {public static void main(String[] args) throws InterruptedException {ExecutorService pool1 = Executors.newCachedThreadPool();pool1.submit(new MyRun());Thread.sleep(1000);pool1.submit(new MyRun());Thread.sleep(1000);pool1.submit(new MyRun());Thread.sleep(1000);pool1.submit(new MyRun());Thread.sleep(1000);pool1.submit(new MyRun());}}

1.11 自定义线程池超详细解析

import java.lang.String;
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.*;public class main {public static void main(String[] args) throws InterruptedException {ExecutorService pool1 = Executors.newFixedThreadPool(3);pool1.submit(new MyRun());pool1.submit(new MyRun());pool1.submit(new MyRun());pool1.submit(new MyRun());pool1.submit(new MyRun());}}

我们约束了3个线程,但是不够灵活,当我提交的任务足够多的时候




什么时候会创建临时线程——当我的队伍排满的时候!我才会去创建临时线程
不是说先到先服务

什么时候才会拒绝服务!看下面:

拒绝策略的方法:

import java.lang.String;
import java.util.concurrent.*;public class main {public static void main(String[] args) throws InterruptedException {/*ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂。任务的拒绝策略);参数一:核心线程数量          不能小于0参数二:最大线程数            不能小于0,最大数量>=核心线程数量参数三:空闲线程最大存活时间    不能小于0参数四:时间单位              用TimeUnit指定参数五:任务队列              不能为null参数六:创建线程工厂           不能为null参数七:任务的拒绝策略          不能为null
*/ThreadPoolExecutor pool = new ThreadPoolExecutor(3,  //核心线程数量, 能小于06,       //最大线程数,不能小于0,最大数量>=核心线程数量60,      //空闲线程最大存活时间TimeUnit.SECONDS, //时间单位new ArrayBlockingQueue<>(3),//任务队列Executors.defaultThreadFactory(),//创建线程工厂new ThreadPoolExecutor.AbortPolicy() //任务的拒绝策略);}}

自定义线程池小结:

  • ①创建一个空的池子
  • ②有任务提交时,线程池会创建线程去执行任务,执行完毕归还线程

不断的提交任务,会有以下三个临界点:

  • 当核心线程满时,再提交任务就会排队
  • 当核心线程满,队伍满时,会创建临时线程
  • 当核心线程满,队伍满,临时线程满时,会触发任务拒绝策略

1.12 最大并行数


什么是最大并行数?

这个与CPU有关,比如4核8线程

4核:CPU有4个大脑——可以做四件事情
因特尔发明了——超线程技术(分身),把4个大脑虚拟成8个


1.13 线程池多大合适

这个要看自己的项目是什么类型的?——有两种类型

  1. CPU密集型运算
    如果我的项目计算比较多,或者访问数据库比较少,读取本地文件少

  2. I/O密集型运算

1.14 多线程的额外扩展内容


以上内容是面试要会的内容,实际开发可能很少遇到。

2 网络编程

2.1 什么是网络编程?

  • 网络通信协议下,不同计算机上运行的程序进行的数据传输

  • 应用场景:即时通信、网游对战、金融证券、国际贸易、邮件、等等。

不管是什么场景,都是计算机跟计算机之间通过网络进行数据传输

  • java中可以使用java.net包下的技术开发出常见的网络应用程序。

  • 常见的网络架构:BS、CS


比如QQ,Steam


一般大厂 两种都会兼顾


BS架构的优缺点

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

CS架构的优缺点

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

2.2 网络编程三要素

  • IP 设备在网络中的地址,是唯一的标识。
  • 端口号 应用程序在设备中唯一的标识。
  • 协议 数据在网络中传输的规则,常见的协议有UDP、TCP、http、 https、 ftp。

2.2.1 IP

  • 全称: Internet Protocol,是互联网协议地址,也称IP地址。
    是分配给上网设备的数字标签。
  • 通俗理解:上网设备在网络中的地址,是唯一的
  • 常见的IP分类为:IPv4、IPv6

IPv4
全称: Internet Protocol version4,互联网通信协议第四版


每一组是0~255 没有负数

但是也就43亿的IP,不够使用,2019年就应该分配完了。
所以IPv6来喽!

2.2.2 IPv4的地址分类

每个网卡有一个IP地址

他们下面的共享一个公网IP,然后由路由器分配局域网IP


不一样!
假设 我现在局域网有6个设别,都是有路由器分配的。


当你换了网上网, 你的局域网的IP就会更改。

自己给自己发数据,那就用127.0.0.1

  • ipconfig:查看本机IP地址
  • ping:检查网络是否连通

2.2.3 InetAddress的使用

这个对象就表示IP的对象

import java.lang.String;
import java.net.InetAddress;
import java.net.UnknownHostException;public class main {public static void main(String[] args) throws UnknownHostException {InetAddress address =  InetAddress.getByName("DESKTOP-JL38L7P");System.out.println(address);String name = address.getHostName();System.out.println(name);String ip = address.getHostAddress();System.out.println(ip);}}

2.2.4 端口号

应用程序在设备中唯一的标识。

端口号:由两个字节表示的整数,取值范围: 0~65535

其中0~1023之间的端口号用于一些知名的网络服务或者应用。我们自己使用1024以上的端口号就可以了。

注意:一个端口号只能被一个应用程序使用。

2.2.5 协议

计算机网络中,连接和通信的规则被称为网络通信协议

  • OSI参考模型:世界互联协议标准,全球通信规范,单模型过于理想化,未能在因特网上进行广泛推广
  • TCP/IP参考模型(或TCP/IP协议):事实上的国际标准。


OSI:

TCP/IP:

UDP协议

  • 用户数据报协议(User Datagram Protocol)
  • UDP 是面向无连接通信协议。
  • 速度快,有大小限制一次最多发送64K,数据不安全,易丢失数据

网络会议、在线视频、视频聊天

TCP协议

  • 传输控制协议TCP(Transmission Control Protocol)
  • TCP协议是面向连接的通信协议。
  • 速度慢,没有大小限制,数据安全

发送邮件、下载软件、发送消息

2.3 UDP通信程序

2.3.1 UDP发送通信程序


import java.io.IOException;
import java.lang.String;
import java.net.*;
import java.nio.charset.StandardCharsets;public class main {public static void main(String[] args) throws IOException {//1.创建DatagramSocket对象(快递公司)//细节://绑定端口I以后我们就是通过这个端口往外发送//空参:所有可用的端口中随机一个进行使用//有参:指定端口号进行绑定DatagramSocket ds = new DatagramSocket();//2.打包数据String str = "你好帅";byte[] bytes = str.getBytes();InetAddress address = InetAddress.getByName("127.0.0.1");int port = 10086;DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);//3.发送数据ds.send(dp);//4.释放资源ds.close();}
}

2.3.2 UDP接送通信程序


import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;public class ReceiveMessageDemo {public static void main(String[] args) throws IOException {//1.创建DatagramSocket对象(快递公司)//细节://在接收的时候,一定 要绑定端口//而且绑定的端口一定要跟发送的端口保持一致DatagramSocket ds = new DatagramSocket(10086);//一定要一直与接收端口//2.接受数据包byte[] bytes = new byte[1024];DatagramPacket dp = new DatagramPacket(bytes,bytes.length);ds.receive(dp);byte[] data = dp.getData();int len = dp.getLength();InetAddress address = dp.getAddress();int port = dp.getPort();System.out.println("接受的数据"+new String(data,0,len));System.out.println("该数据是从 "+address+" 这台电脑的 "+port+" 端口发出的");ds.close();}
}

receive会死等,先运行接受,再运行发送

import java.io.IOException;
import java.lang.String;
import java.net.*;
import java.nio.charset.StandardCharsets;public class main {public static void main(String[] args) throws IOException {//1.创建DatagramSocket对象(快递公司)//细节://绑定端口I以后我们就是通过这个端口往外发送//空参:所有可用的端口中随机一个进行使用//有参:指定端口号进行绑定DatagramSocket ds = new DatagramSocket();//2.打包数据String str = "你好帅";byte[] bytes = str.getBytes();InetAddress address = InetAddress.getByName("127.0.0.1");int port = 10086;//这是接收端口DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);//3.发送数据ds.send(dp);//4.释放资源ds.close();}}

2.3.3 UDP聊天室小练习


ReceiveMessageDemo

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;public class ReceiveMessageDemo {public static void main(String[] args) throws IOException {DatagramSocket ds = new DatagramSocket(10086);byte[] bytes = new  byte[1024];DatagramPacket dp = new DatagramPacket(bytes,bytes.length);while (true) {ds.receive(dp);byte[] data = dp.getData();int len = dp.getLength();String hostAddress = dp.getAddress().getHostAddress();String hostName = dp.getAddress().getHostName();//4.打印数据System.out .println("ip为:"+ hostAddress + ",主机名为"+ hostName + "的人,发送了数据: " + new String(data,0,len));}}
}

SendMessageDemo

import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class SendMessageDemo {public static void main(String[] args) throws IOException {DatagramSocket ds = new DatagramSocket();//随机端口发送数据Scanner sc = new Scanner(System.in);while (true){System.out.println("请输入:");String str = sc.nextLine();if("886".equals(str)){break;}byte[] bytes = str.getBytes();InetAddress address = InetAddress.getByName("127.0.0.1");int port = 10086;DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);ds.send(dp);}ds.close();}}

2.3.4 UDP三种通信方式

  • 单播
    一对一

以前的代码就是单播的

  • 组播
    接受端是一组

组播地址:224.0.0.0~239.255.255.255
其中224.0.0.0~224.0.0.255为预留的组播地址


  • 广播
    接收端为全部

广播地址255.255.255.255

2.4 TCP通信程序


客户端

  • 创建客户端的Socket对象(Socket)与指定服务端连接Socket(String host, int port)
  • 获取输出流,写数据OutputStream getOutputStream( )
  • 释放资源void close()

服务器

  • 创建服务器端的Socket对象(ServerSocket)ServerSocket(int port)
  • 监听客户端连接,返回一个Socket对象Socket accept( )
  • 获取输入流,读数据,并把数据显示在控制台InputStream getInputStream( )
  • 释放资源void close()
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;public class Server {public static void main(String[] args) throws IOException {ServerSocket ss = new ServerSocket(10086);Socket socket = ss.accept();InputStream is = socket.getInputStream();int b;while ((b=is.read())!=-1){System.out.println((char)b);}socket.close();ss.close();}
}
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;public class Client {public static void main(String[] args) throws IOException {Socket socket = new Socket("127.0.0.1",10086);//直接运行报错,我要链接服务端才行OutputStream os = socket.getOutputStream();os.write("你好你好".getBytes());os.close();socket.close();}
}

上面出现了中文乱码的现象

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;public class Server {public static void main(String[] args) throws IOException {ServerSocket ss = new ServerSocket(10086);ss.accept();//如果没有  // 卡死Socket socket = ss.accept();InputStream is = socket.getInputStream();InputStreamReader isr = new InputStreamReader(is);BufferedReader br = new BufferedReader(isr);int b;while ((b=br.read())!=-1){System.out.print((char)b);}socket.close();ss.close();}
}

2.4.1 三次握手协议



2.4.2 四次挥手协议


2.5 综合练习

2.5.1 多发多收


Server

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;public class Server {public static void main(String[] args) throws IOException {ServerSocket ss = new ServerSocket(10086);while (true){Socket socket = ss.accept();InputStream is = socket.getInputStream();InputStreamReader isr = new InputStreamReader(is);BufferedReader br = new BufferedReader(isr);int b;while((b= br.read())!=-1){System.out.print((char)b);}System.out.println();}}
}

Client

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;public class Client {public static void main(String[] args) throws IOException {Socket socket = new Socket("127.0.0.1",10086);OutputStream os = socket.getOutputStream();while(true){Scanner sc = new Scanner(System.in);System.out.println("请输入:(输入886退出聊天)");String str = sc.nextLine();if("886".equals(str)){break;}else{os.write(str.getBytes());}}os.close();socket.close();}
}

2.5.2 接收并反馈


Server

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;public class Server {public static void main(String[] args) throws IOException {ServerSocket ss = new ServerSocket(10086);System.out.println("等待客户端连接");Socket socket = ss.accept();System.out.println("客户端连接完成");InputStream is = socket.getInputStream();InputStreamReader isr = new InputStreamReader(is);int b;System.out.println("准备读取客户端发送过来的数据");while((b = isr.read())!=-1){System.out.println("正在读取");System.out.println((char) b);}System.out.println("读取完毕,准备回写");String str = "NTMD";OutputStream os = socket.getOutputStream();os.write(str.getBytes());System.out.println("回写成功");socket.shutdownOutput();os.close();}
}

Client

import java.io.*;
import java.net.Socket;
import java.util.Scanner;public class Client {public static void main(String[] args) throws IOException {Socket socket = new Socket("127.0.0.1",10086);String str = "见到你很高兴!";OutputStream os = socket.getOutputStream();os.write(str.getBytes());socket.shutdownOutput();InputStream is = socket.getInputStream();InputStreamReader isr = new InputStreamReader(is);int b;while((b = isr.read())!=-1){System.out.println((char) b);}os.close();socket.close();}
}

2.5.3 上传文件

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;public class Server {public static void main(String[] args) throws IOException {ServerSocket ss = new ServerSocket(10086);Socket socket = ss.accept();BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("F:\\QQ_DownLoad_Document\\Ma.pdf"));int len;byte[] bytes = new  byte[1024];while((len=bis.read(bytes))!= -1){bos.write(bytes,0,len);}bos.flush();BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));bw.write("上传成功");bw.newLine();bw.flush();socket.close();ss.close();}
}
import java.io.*;
import java.net.Socket;
import java.util.Scanner;public class Client {public static void main(String[] args) throws IOException {Socket socket = new Socket("127.0.0.1",10086);BufferedInputStream bis = new BufferedInputStream(new FileInputStream("F:\\QQ_DownLoad_Document\\Xu Ma.pdf"));BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());byte[] bytes = new byte[1024];int len;while((len= bis.read(bytes))!=-1){bos.write(bytes,0,len);}bos.flush();socket.shutdownOutput();BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));String s = br.readLine();System.out.println(s);socket.close();}
}

2.5.4 文件名重复

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;public class Server {public static void main(String[] args) throws IOException {ServerSocket ss = new ServerSocket(10086);Socket socket = ss.accept();BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());String sttr = UUID.randomUUID().toString().replace("-","");BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("F:\\QQ_DownLoad_Document\\"+sttr+".pdf"));int len;byte[] bytes = new  byte[1024];while((len=bis.read(bytes))!= -1){bos.write(bytes,0,len);}bos.flush();BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));bw.write("上传成功");bw.newLine();bw.flush();socket.close();ss.close();}
}

String sttr = UUID.randomUUID().toString().replace("-",""); 跟平常在网站上下载的一些图片,视频 一堆乱码的名字原理!!!!!!!!!!!!!!!!

2.5.5 多线程版的服务端


MyRunnable

import java.io.*;
import java.net.Socket;
import java.util.UUID;public class MyRunnable implements Runnable {Socket socket;public MyRunnable(Socket socket) {this.socket = socket;}@Overridepublic void run() {try {BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());String sttr = UUID.randomUUID().toString().replace("-","");BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("F:\\QQ_DownLoad_Document\\"+sttr+".pdf"));int len;byte[] bytes = new  byte[1024];while((len=bis.read(bytes))!= -1){bos.write(bytes,0,len);}bos.flush();BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));bw.write("上传成功");bw.newLine();bw.flush();} catch (IOException e) {throw new RuntimeException(e);} finally {if(socket !=null){try {socket.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
}

Server

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;public class Server {public static void main(String[] args) throws IOException {ServerSocket ss = new ServerSocket(10086);while (true) {Socket socket = ss.accept();new Thread(new MyRunnable(socket)).start();}}
}

2.5.6 线程池版的服务端


2.5.7 BS架构模型



在BS架构中,我们的客户端就是浏览器,服务端就是接受浏览器的发送内容

3 反射

3.1 反射的概述

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

提示的方法就是通过反射来实现的
Ctrl+P 反射可以获得我忘了的形参


3.2 获取class对象的三种方式

获取class对象的三种方式

  • Class. forName(“全类名”);
  • 类名. class
  • 对象. getClass();

3.3 反射获取构造方法

3.4 反射获取成员变量

3.5 反射获取成员方法

4 动态代理

4.1 动态代理的思想分析

如果我想着要我的类中的成员方法添加我想要的。 如果直接添加——侵入式修改

如果我不想修改原来的代码,又想添加功能,怎么办???

所以动态代理!!!

特点:无侵入式的给代码增加额外的功能


对象有什么方法想被代理,代理就一定要有对应的方法


4.2 动态代理的代码实现

有需要再学

黑马程序员Java零基础视频教程_下部(P135-P200)相关推荐

  1. 黑马程序员Java零基础视频教程_下部(P52-P134)

    黑马程序员Java零基础视频教程_下部(P52-P134) 1. 异常 1.1 异常体系介绍 1.2 编译时异常和运行时异常 1.3 异常在代码中的两个作用 1.4 JVM虚拟机默认处理异常的方式 1 ...

  2. 黑马程序员Java零基础视频教程_上部(P1-P80)

    黑马程序员Java零基础视频教程_上部(P1-P80) 1. Java入门 1.1 Java学习介绍 1.2 Java人机交互 1.2.1 常用CMD命令 2. Java基础学习 2.1 Java入门 ...

  3. 黑马程序员Java零基础视频教程笔记-Java基础概念

    文章目录 一.注释 二.关键字 三.字面量 四.变量 五.数据存储 六.数据类型 七.标识符 八.键盘录入 一.注释 1. 定义 注释就是对代码进行解释说明的文字 2. 注释的分类 ① 单行注释:// ...

  4. 黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day10-字符串

    1.API和API帮助文档 API:应用程序接口. 简单理解:API就是别人已经写好的东西,我们不需要自己编写,直接使用即可. JavaAPI:指的就是JDK中提供的各种功能的Java类.这些类将底层 ...

  5. 黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day15-阶段项目(拼图小游戏)(上篇)

    1.主界面分析 1.练习:创建主界面1 到IDEA中创建一个宽603像素,高680像素的游戏主界面 到IDEA中创建一个宽488像素,高430像素的登录界面 到IDEA中创建一个宽488像素,高500 ...

  6. 黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day12-学生管理系统

    基础版本: 需求:采取控制台的方式去书写学生管理系统. 分析: (1).初始菜单: (2).学生类: 属性:id.姓名.年龄.家庭住址. (3).添加功能: 键盘录入每一个学生信息并添加,满足以下要求 ...

  7. 黑马程序员Java零基础视频教程笔记-方法

    文章目录 一.什么是方法 二.最简单的方法定义和调用 三.带参数的方法定义和调用 四.带返回值方法的定义和调用 五.方法的小结 六.方法的重载 七.方法的三个练习:遍历求最大值和判断是否存在 八.方法 ...

  8. 黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day14-面向对象进阶02

    1.权限修饰符和代码块 1.1 权限修饰符 权限修饰符:是用来控制一个成员能够被访问的范围的. 可以修饰:成员变量.方法.构造方法.内部类. 巧计举例: private--------私有的----- ...

  9. 黑马程序员Java零基础视频教程(2022最新Java)B站视频学习笔记-Day7-综合练习

    1.卖飞机票 需求:机票价格按照淡季旺季.头等舱和经济舱收费.输入机票原价.月份和头等舱或者经济舱. 按照如下规则进行计算机票价格:旺季(5-10月)头等舱9折,经济舱8.5折:淡季(11月到来年4月 ...

最新文章

  1. 常见数字IC设计、FPGA工程师面试题
  2. 怎样解决样本不平衡问题
  3. python语言中文社区-python的汉语
  4. 网盘纷退场 “云存储”仍是刚需
  5. kafka使用_kafka使用Interceptors实现消息端到端跟踪
  6. 【Jetson Nano学习笔记】1. 系统镜像和ROS的安装
  7. protobuf windows lib链接库生成
  8. 拳王公社:有哪些一个人就能做的创业赚钱项目?90后年赚30W逆袭案例!
  9. YUV格式学习:YUV444转换RGB24
  10. php获取表单ip,PHP获取用户IP代码实现
  11. 为什么谷歌被骂上热搜一点也不冤,详解FLoC背后联邦计算
  12. 20144303《Java程序设计》第10周学习总结
  13. Ubuntu开发问题集
  14. Python: pipenv包管理、虚拟环境创建、autopep8、flask安装及初试
  15. 联想拯救者r7000p安装Linux双系统(一)
  16. 机器学习:模型评估与选择:性能度量——代价敏感错误率与代价曲线
  17. mysql 备份 access_备份access数据库
  18. android鼠标单击双击事件互不影响
  19. python 词表里的词不符合_收藏干货丨初中英语单词1600个词表+mp3下载
  20. windows10 DOS命令 小计

热门文章

  1. 设计模式六大原则——SOLID
  2. 电池容量足够低如何触发自动关机
  3. 进程间通信(IPC):管道(Pipe)
  4. 计算机协会申请流程,成立计算机协会的申请书
  5. CSS3之动画模块实现轮播图
  6. “我裂开了”!微信新表情上线引热议
  7. 关于 CSS 的英文单词换行 (word-break 和 word-wrap 的区别)
  8. Excel·VBA工作表整列拆分为工作簿
  9. Keynote教程-iPhone3GS
  10. 最最完整的二维码生成教程:MFC下利用libqrencode库生成二维码,显示在屏幕上并保存