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 创建线程的两种方式
- 继承
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()
- 实现
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();
}
}
}
}
Runable的方式更加适合多个线程共享一个资源的情况- 通过在 while 循环中使用变量来退出线程
- 线程常用方法
setName设置线程名字getName获得线程名字start开启线程run调用线程对象 run 方法setPriority设置线程优先级getPriority获得线程优先级sleep线程休眠interrupt中断线程 (休眠线程)yield让出 cpu,让其他线程执行,不一定能礼让成功join线程插队setDaemon守护线程,当主线程结束后,立即结束
3.2.2 线程的生命周期
NEW:尚未启动RUNABLE:在 Java 虚拟机中运行的线程BLOCKED:线程被阻塞WAITING:等待另外一个线程执行特定动作TIMED_WAITING:正在等待另一个线程执行动作到达指定等待时间TERMINATED:线程终止
3.2.3 线程同步(Synchronized)
在多线程编程时,一些敏感数据不允许被多个线程同时访问,限制同一时刻只允许一个线程操作数据,称为线程同步。
- 同步代码块
synchronized(对象){ // 得到对象锁,才能操作同步代码
// 需要被同步的代码
}
// synchronized 还可以放到方法声明中,表示整个方法为同步方法
public synchronized void m(){
// 需要被同步的代码
}