Java 学习(2022-07-11)

104 阅读4分钟

2022-07-11日 Java 学习

1. 泛型

为什么需要泛型?

  • 不安全,对数据格式没有约束
  • 需要进行数据类型转换
// 传统方法解决 -> 使用泛型
// 1. 当我们这样写, ArrayList<Dog> 集合里面只能存放 Dog 类型
ArrayList<Dog> list = new ArrayList<Dog>();
list.add(new Dog("小花", 18));
list.add(new Dog("小张", 10));
list.add(new Dog("小狗", 1));
list.add(new Cat("招财猫", 1));

1.1 泛型语法

  • interface 接口{}
  • class 类<K,V>{}
  • 其中 T,K,V 代表数据类型
  • 任何字母都可以,常用T(Type)表示
  • 给泛型的类型必须是 引用类型
  • 在给泛型传入的必须是指定类型 or 该类型的子类
练习:HashSet 和 HashMap 使用泛型,并且遍历 
HashSet<Student> students = new HashSet<Student>();

students.add(new Student("jack", 18));
students.add(new Student("mary", 1));

for (Student student : students) {
    System.out.println(student.getName());
}


Map<String, Student> map = new HashMap<String, Student>();
map.put("李永奇", new Student("李永奇", 18));
map.put("提交", new Student("唐", 18));

Set<Map.Entry<String, Student>> entries = map.entrySet();

Iterator<Map.Entry<String, Student>> iterator = entries.iterator();

while (iterator.hasNext()) {
    Map.Entry<String, Student> next =  iterator.next();
    System.out.println(next.getKey() + next.getValue());
}

1.1.1 泛型实例化

1.2 自定义泛型

1.2.1 泛型类

  • 普通成员可以使用泛型
  • 使用泛型的数组,不能初始化
  • 静态方法中不能使用类的泛型
  • 泛型类的类型,是在创建对象的时候确定的
  • 如果没有指定类型,默认 Object

1.2.2 泛型接口

  • 在接口中,静态成员也不能使用泛型
  • 没有指定类型,默认 Object
  • 泛型接口的类型,在继承接口实现接口时确定
class Pigs implements IUsb<String, Integer>{
    @Override
    public Integer get(String s) {
        return null;
    }
}
interface IUsb<U, R> {
 R get(U u);
}

1.2.3 泛型方法

public static void main(String[] args) {
    Car car = new Car();
    car.run("宝马", 100);
}

class Car {
    public void run(){ // 普通方法
    }
    public <T,R> void run(T t, R r){ // 泛型方法
    }
}

1.3 泛型继承和通配符

  • 泛型不具备继承性
  • <?>:支持任意泛型类型
  • <? extends A> :支持 A 类以及 A 类的子类,规定了泛型上限
  • <? super A> :支持 A 类以及 A 类的父类,规定了泛型下限
public static void printCollection1(List<?> c){}

public static void printCollection2(List<? extends AA> c){}

public static void printCollection3(List<? super AA> c){}

2. JUnit (单元测试)

在需要进行单元测试的方法上,添加 @Test,然后按住Alt + Enter,一般选择5.4的路径

@Test
public void m1(){
    System.out.println("m1 被调用");
}

3. 多线程

3.1 并发基础

3.2 线程

一个进程拥有多个线程

  • 单线程:同一时刻,只允许执行一个线程
  • 多线程:同一时刻,可以执行多个线程
  • 并发:同一时刻,多个任务交替执行
  • 并行,同一时刻,多个任务同时执行
  • 当主进程挂掉时,子进程不一定会挂,会维持到应用结束
// 获取电脑 CPU 有多少核
Runtime runtime = Runtime.getRuntime();
int cpuNums = runtime.availableProcessors();
System.out.println(cpuNums);

3.2.1 创建线程的两种方式

  1. 继承 Thread类,重写 run方法

public static void main(String[] args) {
    Cat cat = new Cat();
    cat.start();  // 开启线程,会执行 cat 的 run 方法
    while(true){
        cat.run(); 
    }
}

// 1. 该类可以当做线程使用
// 2. 我们会重写 run 方法,这里写自己的业务代码
// 3. run Thread 类 实现了 Runnable 接口的 run 方法
class Cat extends Thread{
    @Override
    public void run() {
        System.out.println("小猫咪");
        try {
            Thread.sleep(1000);  // 1秒休眠一次
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
(1) 
public synchronized void start(){
    start0();
}

(2) 
start0() 是本地方法,由 JVM 调用,底层是 C/C++ 实现
真正实现多线程的效果是 start0(), 而不是 run
private native void start0()

  1. 实现Runnable接口,重写run方法
public static void main(String[] args) {
    Dog dog = new Dog();
    Thread thread = new Thread(dog);
    thread.start();
}

class Dog implements Runnable {
    int count = 0;
    @Override
    public void run() {
        while(true){
            if(count == 10)
                System.out.println("小狗叫了" + ++count + "次" + Thread.currentThread().getName());
            // 休眠 1 秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  1. Runable 的方式更加适合多个线程共享一个资源的情况
  2. 通过在 while 循环中使用变量来退出线程
  3. 线程常用方法
  • setName 设置线程名字
  • getName 获得线程名字
  • start 开启线程
  • run 调用线程对象 run 方法
  • setPriority 设置线程优先级
  • getPriority 获得线程优先级
  • sleep 线程休眠
  • interrupt 中断线程 (休眠线程)
  • yield 让出 cpu,让其他线程执行,不一定能礼让成功
  • join 线程插队
  • setDaemon 守护线程,当主线程结束后,立即结束

3.2.2 线程的生命周期

  1. NEW:尚未启动
  2. RUNABLE:在 Java 虚拟机中运行的线程
  3. BLOCKED:线程被阻塞
  4. WAITING:等待另外一个线程执行特定动作
  5. TIMED_WAITING:正在等待另一个线程执行动作到达指定等待时间
  6. TERMINATED:线程终止

3.2.3 线程同步(Synchronized)

在多线程编程时,一些敏感数据不允许被多个线程同时访问,限制同一时刻只允许一个线程操作数据,称为线程同步。

  1. 同步代码块
synchronized(对象){ // 得到对象锁,才能操作同步代码
    //  需要被同步的代码
}

// synchronized 还可以放到方法声明中,表示整个方法为同步方法
public synchronized void m(){
    // 需要被同步的代码
}

3.3 锁

3.4 线程池

3.5 并发容器

3.6 JUC