一、多线程
1、线程间通信
(1)创建线程因为操作系统进行调度的,创建顺序不确定的,在程序通过条件控制,让线程执行有
顺序的,这个个过程--等待唤醒(通知)机制
(2)等待唤醒(通知)机制调用方法
第一个 wait() : 释放锁,需要被唤醒
第二个 notify()/notifyAll():通知其他线程
* 上面三个方法都是Object类里面的方法
(3)编写线程间通信例子
/**
* 创建两个线程
* 创建一个初始 变量 值是0
* 实现一个线程对变量+1 ,另外一个线程对变量-1
* 交替,进行10次
*/
2、线程其他概念
(1)线程生命周期
- 线程的生命周期有五种状态:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)、死亡(Dead)。
(2)线程阻塞:
- 线程调用了sleep()方法,主动放弃所占用的CPU资源;
- 线程试图获取一个同步监视器,但该同步监视器正被其他线程持有;
- 线程执行过程中,同步监视器调用了wait(),让它等待某个通知(notify);
- 线程执行过程中,同步监视器调用了wait(time)
- 线程执行过程中,遇到了其他线程对象的加塞(join);
public enum State {
NEW, 新建
RUNNABLE, 准备就绪
BLOCKED, 阻塞
WAITING, 不见不散
TIMED_WAITING, 过时不候
TERMINATED; 终结
}
(3)Thread和Runnable区别
1. 适合多个相同的程序代码的线程去共享同一个资源。
2. 可以避免java中的单继承的局限性。
3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
(4)释放锁操作
* 当前线程的同步方法、同步代码块执行结束。
* 当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、该方法的继续执行。
* 当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致当前线程异常结束。
* 当前线程在同步代码块、同步方法中执行了锁对象的wait()方法,当前线程被挂起,并释放锁。
(5)死锁
不同的线程分别锁住对方需要的同步监视器对象不释放,都在等待对方先放弃时就形成了线程的死锁。
一旦出现死锁,整个程序既不会发生异常,也不会给出任何提示,只是所有线程处于阻塞状态,无法继续。
(6)sleep()和wait()方法的区别
- sleep()不释放锁,wait()释放锁
- sleep()自动在指定时间唤醒,wait()可以指定时间也可以使用notify或notifyAll唤醒
- sleep()在Thread类中声明的静态方法,wait方法在Object类中声明
二、泛型
1、什么是泛型:
(1)在jdk1.5开始出现新特性,成为泛型
(2)使用泛型可以限定集合或者数组放什么类型数据,方法里面传递什么类型参数
2、演示泛型好处
(1)问题代码
public class GenericDemo1 {
public static void main(String[] args) {
//如果参数是字符串 abc,转换int类型出现异常 java.lang.ClassCastException
add("abc",2);
}
public static void add(Object m,Object n) {
//Object 强转 int
int m1 = (Integer) m;
int n1 = (Integer)n;
System.out.println(m1+n1);
}
}
(2)泛型解决问题
- <自定义类型1,自定义类型2> :使用大写字母代表自定义类型 <T,V>
- <>可以写在类部分,写在方法部分
public class GengricDemo2 {
public static void main(String[] args) {
CompareData<Integer> compareData1 = new CompareData<Integer>(1,2);
CompareData<Double> compareData2 = new CompareData<Double>(1.1,2.3);
}
}
//泛型类
class CompareData<T> {
//定义T类型属性
private T a;
private T b;
//创建构造
public CompareData(T a,T b) {
int a1 = (Integer)a;
int b1 = (Integer)b;
System.out.println(a1+b1);
}
}
3、泛型类和泛型接口
(1)泛型可以在类上面,或者接口上面使用
** <类型变量列表>中的类型变量不能用于静态成员上。
【修饰符】 class 类名<类型变量列表>{
}
public class User<T,V> {}
【修饰符】 interface 接口名<类型变量列表>{
}
public interface IDao<T,A> {}
(2)举例演示
public class GengricDemo3 {
public static void main(String[] args) {
//语文老师
Student<String> student1 = new Student<String>("张无忌","优秀");
//数学老师
Student<Double> student2 = new Student<>("乔峰",60.1);
//英语老师使用时:
Student<Character> stu3 = new Student<Character>("张三", 'C');
}
}
class Student<T> {
private String name; //姓名
private T score; //分数
public Student() {
super();
}
public Student(String name, T score) {
super();
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public T getScore() {
return score;
}
public void setScore(T score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
}
interface IDao<T> {
public void add(T name);
}
class IDaoClass implements IDao<Double> {
@Override
public void add(Double name) {
}
}
4、类型变量的上限
(1)语法: <类型变量 extends 上限>
表示含义:类型变量是上限或者上限的子类
public class GenericDemo4 {
public static void main(String[] args) {
Stu<Integer> stu = new Stu<>(10);
}
}
class Stu<T extends Integer> {
private T score;
public Stu(T score) {
this.score = score;
System.out.println(score);
}
}
5、泛型擦除
(1)泛型在编译时候出现,编译之后没有泛型这个东西了
处理机制是通过类型擦除,擦除规则:
若泛型类型没有指定具体类型,用Object作为原始类型;
若有限定类型< T exnteds XClass >,使用XClass作为原始类型;
若有多个限定< T exnteds XClass1 & XClass2 >,使用第一个边界类型XClass1作为原始类型;
6、泛型方法
(1)在方法上面使用泛型
【修饰符】 <类型变量列表> 返回值类型 方法名(【形参列表】)【throws 异常列表】{
//...
}
public static <T> void add(T a,T b) throws Exception {}
(2)举例演示
public class GenericDemo5 {
public static void main(String[] args) {
//getScore("优秀");
getScore(100);
// getScore('A');
}
public static <T extends Integer> void getScore(T score) {
System.out.println(score);
}
}
6、类型通配符?
当我们声明一个方法时,某个形参的类型是一个参数化的泛型类或泛型接口类型,
但是在声明方法时,又不确定该泛型实际类型,我们可以考虑使用类型通配符
package com.atguigu.generic;
public class GenericDemo6 {
public static void main(String[] args) {
//int类型
StudentInfo<Integer> s1 = new StudentInfo<>("lucy",100);
//字符串
StudentInfo<String> s2 = new StudentInfo<>("mary","优秀");
//字符类型
StudentInfo<Character> s3 = new StudentInfo<>("jack",'B');
//把上面三种不同类型数据放到一个数组里面
//定义StudentInfo类型数组 泛型是通配符 ?
StudentInfo<?>[] arr = new StudentInfo[3];
//向数组放值
arr[0]=s1;
arr[1]=s2;
arr[2]=s3;
StudentInfoPrint.print(arr);
}
}
class StudentInfoPrint {
public static void print(StudentInfo<?>[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
class StudentInfo<T>{
private String name;
private T score;
public StudentInfo() {
super();
}
public StudentInfo(String name, T score) {
super();
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public T getScore() {
return score;
}
public void setScore(T score) {
this.score = score;
}
@Override
public String toString() {
return "姓名:" + name + ", 成绩:" + score;
}
}
7、类型通配符上限和下限
(1)上限
格式:<? extends E>
? 代表接收E类型或者E的子类型的元素
<? extends Number>
(2)下限
格式:<? super E>
? 代表接收E类型或者E的父类型的元素
<? super String>
三、数据结构入门
1、什么是数据结构
(1)多个数据存储特点(在内存如何存储的)
(2)多个数据之间关系体现出来
2、逻辑结构和物理结构
3、常用数据结构
(1)集合
(2)线性结构
(3)树形结构
(4)图形结构
4、数组存储结构特定
(1)连续的存储空间
(2)一次申请一大段连续的空间,一旦申请到了,内存就固定了。
(3)所有数据存储在这个连续的空间中,数组中的每一个元素都是一个具体的数据(或对象 地址),
所有数据都紧密排布,不能有间隔。
(4)数组查询方便
5、链表存储结构特点
(1)普通单链表:元素节点和执向下一个节点指针
(2)查询不方便,添加效率相对高
6、树形存储结构特点