基础语法

  1. 数据在计算机中最小单位为字节(byte,B),一个字节8个比特位即1B=8b

字面量

常用数据程序中形式说明
整数123写法一致
小数12.3写法一致
字符‘A’,’0’,’好’必须要用单引号包裹,且只能一个字符
字符串“123”必须用双引号包裹,内容可为空
布尔值true,false只有两种值
空值null特殊值
特殊字符\t,\n缩进换行等

二八十六进制

对于二进制数据需要以0B0b开头
对于八进制数据需要以0开头
对于十六进制数据需要以0x0X开头

  1. 十进制转二进制:除二取余法
  2. 二进制转八进制:每三位二进制为一位八进制
  3. 二进制转十六进制:每四位二进制为一位十六进制

基本数据类型

数据大类数据类型占用字节数数据范围
整型byte1-128~127
整型short2-32768~32767
整型int(默认)4-2147483648~2147483647
整型long8-9223372036854775808~9223372036854775807
浮点型float41.401298E-45~3.4028235E-+38
浮点型double(默认)84.9000000E-324~1.797693E+308
布尔型boolean1true,false
字符型char20~65535

表达式的自动类型提升

  • 表达式的自动类型转换

表达式中小范围类型的变量,会自动转换为表达式中较大范围的类型,再参与运算
byte short char ->int ->long -> float-> double

  1. 表达式最终结果类型由表达式中最高数据类型决定
  2. 表达式中 byte short char是直接转换成int参与运算

一维数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
在定义时就直接把数组内容写出来。
编译器会自动推算出数组的长度。
适合 已经知道数组具体内容 的场景
*/
类型[] 数组名 = new 类型[]{...}


// 动态创建数组

/*
只确定数组长度,但不提供具体值。
数组元素会被初始化为 默认值:
数值型:0
boolean:false
引用类型:null
适合 只知道大小,但内容稍后再赋值 的场景。*/
类型[] 数组名 = new 类型[数组长度]

二维数组

1
2
3
4
类型[][] 数组名  = new 类型[][]{...}


类型[][] 数组名 = new 类型[长度1][长度2]

多态

多态是继承/实现情况下的一种现象。分为行为多态对象多态
多态是对象和行为的多态,不包括成员变量

1
2
3
4
5
6
7
8
9
10
11
// A 是B C的父类,func是A的抽象方法

// 对象多态
A a1 = new B();
A a2 = new C();


// 行为多态(编译看左边,运行看右边)
a1.func();
a2.func();

多态前提

  1. 继承/实现情况下
  2. 存在父类引用子类的情况
  3. 存在方法重写

好处

  1. 多态形式下,右边是解耦的,便于扩展维护
  2. 定义方法时,使用父类类型的形参,可以接受一切子类,使方法可以接受多个类对象,更灵活

问题以及解决

  • 多态不能调用子类的独有功能

解决:类型转换

1
2
3
4
5
6
// 自动类型转换
父类 变量1 = new 子类()

// 强制类型转换
子类 变量2 = (子类) 父类变量

注意

  1. 存在继承/实现关系就可以在编译阶段进行强转,编译阶段不会报错
  2. 运行阶段发现对象的真实类型与强转后的类型不同,就会报类型转换异常

final关键字

可以修饰类,方法,变量
分别为最终类:不能再被继承
最终方法:不能再被重写
变量:有且只能赋值一次

注意

  1. final修饰基本数据类型的变量,变量存储的数据不可以改
  2. final修饰引用数据类型的变量,变量存储的地址不可以改,但是地址所指向的内容可以改

单例设计模式

作用: 确保某个类只有一个对象

步骤

  1. 把类的构造器私有
  2. 定义一个静态类变量记住类的一个对象
  3. 定义一个公开方法获取这个类变量

单例形式

  1. 上面的为饿汉式单例
  2. 还有懒汉式单例

懒汉式单例中,只需要定义一个静态变量,不用先获取对象
然后提供一个静态方法,当该变量为空时再new一个对象,最后再返回这个对象


枚举

特点

  1. 枚举类是一种特殊类,用来表示一组固定的常量
  2. 是最终类,不可以被继承,枚举类都是继承自java.lang.Enum类
  3. 枚举类第一行只能罗列常量,每个常量都会记住枚举类的一个对象
  4. 枚举类构造器必须私有,所以不能对外创建对象

书写与使用

1
2
3
4
5
6
7
修饰符 enum 枚举类名{
常量1,常量2,...
}


枚举类名 对象名 = 枚举类名.常量

常见应用场景

  1. 信息分类和标志

应用理由

  1. 枚举是有限且确定的常量,适合固定且有限的取值集合
  2. 枚举常量有名字,比数字或字符串直观。比如 Status.SUCCESS 一看就是“成功”,比 200、”OK” 更清晰
  3. 枚举能带属性和方法,不仅仅是标志,还能存储和管理相关信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public enum Status {
SUCCESS(200, "成功"),
ERROR(500, "失败");

private final int code;
private final String msg;


// 需要写含参构造器,用于常量的赋值
Status(int code, String msg) {
this.code = code;
this.msg = msg;
}

public int getCode() { return code; }
public String getMsg() { return msg; }
}


抽象类

特点

  1. 以abstract关键字修饰类或方法
  2. 抽象类有普通类的所有(构造器,成员,方法),不一定有抽象方法,抽象方法只能在抽象类中
  3. 抽象类只能继承,不能创建对象
  4. 要继承抽象类,子类必须重写所有抽象方法,否则子类也必须声明为抽象类
  5. 抽象方法只能声明不能写方法体,除非是被继承重写的

书写

1
2
3
修饰符 abstract 返回值类型 抽象类名{
修饰符 abstract 返回值类型 抽象方法名(形参列表);
}

常见应用场景

  1. 知道子类都要做某个行为,但每个子类要做的事情都不一样,父类就定义为抽象方法,交给子类去实现
  2. 这样的抽象类会更好的支持多态
  3. 适用模版方法设计模式

应用理由

模版方法设计模式:

  • 提供一个方法作为完成某些功能的模版,模版方法封装了每个实现步骤,但允许子类提供特定步骤的实现

写法:

  1. 定义一个抽象类
  2. 写一个模版方法,将相同步骤放进去
  3. 写一个抽象方法,替换到模版方法不确定的步骤中,交给子类实现

接口

特点

  1. 接口以interface关键字修饰
  2. 接口中只能写常量和抽象方法
  3. 接口中常量的public static final可以省略
  4. 抽象方法的public abstract可以省略
  5. 接口不能创建对象,但是可以被实现(implements关键字)
  6. 实现接口的类称实现类,实现类可以实现多个接口
  7. 实现类与抽象子类一样,若没有重写完实现的抽象方法,则自己要标记为实现类
  8. 如果多个接口中存在方法签名冲突(返回值不同),则此时不支持多继承,也不支持多实现
  9. 既继承父类,又实现接口,父类和接口中有同名的默认方法,则实现类优先使用父类的

书写

1
2
3
4
5
6
7
8
9
10
11
12
public interface 接口名{
// 常量
// 抽象方法
// 默认方法,可以写方法体,交给实现类调用
// 私有方法,交给同类的默认方法调用
// 静态方法,接口名.静态方法调用
}


class 实现类名 implements 接口1,接口2,...{
// 抽象方法实现
}

常见应用场景

  1. 弥补单继承的不足,实现类可以实现多个接口,拥有多个角色,更多功能
  2. 程序可以面向接口编程,利于程序的解耦合

应用理由

  1. 代码只依赖接口,不依赖具体实现类
  2. 新增实现类不影响现有代码
  3. 可以轻松使用接口的Mock进行单元测试

与抽象类相同处

  1. 多是抽象方式,都可以有抽象方法,那不能创建对象
  2. 都是派生子类形式,抽象类是子类继承形式,接口是被实现类实现
  3. 一个类继承抽象类,或者实现接口,都必须重写所有的抽象方法,否则自己要变成抽象类或者报错
  4. 都能支持多态,都能解耦合

与抽象类不同处

  1. 抽象类可以定义类的全部普通成员,接口只能定义常量,抽象方法,默认方法,私有方法,静态方法
  2. 抽象类只能被类单继承,接口可以被类多实现
  3. 一个类继承一个抽象类就不能再继承其他抽象类,一个类实现了一个接口还可以继承其他类或者实现其他接口
  4. 抽象类体现模版思想,更利于做父类,实现代码的复用性
  5. 接口更适合代码的解耦合,解耦合性更强更灵活

代码块

静态代码块

特点

  1. 类加载时自动执行,由于类只会加载一次,所以静态代码块也只会执行一次

书写

1
2
3
static{

}

常见应用场景

  1. 可完成类的初始化,例如:对静态变量的初始赋值

实例代码块

特点

  1. 每次创建对象时都会执行,且在构造器前执行

书写

1
{}

常见应用场景

  1. 和构造器一样都是用来完成对象的初始化的,例如,对实例变量进行初始化赋值

内部类

定义在另外一个类中的类就是内部类
当一个类的内部,包含了一个完整的事物,且这个事物没有必要单独设计时,可以把这个事物设计成内部类

成员内部类

特点

  1. 就相当于类中的普通成员
  2. 无static修饰,属于外部类的对象持有
  3. 成员内部类可以直接访问外部类的静态成员,也可以直接访问外部类的实例成员
  4. 使用this关键字获取当前内部类的对象,使用外部类.this获取外部类的对象

书写

1
2
3
4
5
6
7
public class 外部类名{
public class 内部类名{}
}

// 调用
外部类名.内部类名 对象名 = new 外部类名(...).new 内部类名(...)


静态内部类

特点

  1. 有static修饰的内部类
  2. 区别与成员内部类,静态内部类是外部类本身持有的
  3. 静态内部类访问外部类的静态成员,而不可以访问外部类的实例成员

书写

1
2
3
4
5
6
public class 外部类名{
public static class 静态内部类名{}
}

外部类名.内部类名 对象名 = new 外部类名.静态内部类名()


匿名内部类

特点

  1. 特殊的局部内部类
  2. 这个类不需要命名,一般有个默认的隐藏名字
  3. 本质上是子类,并会立即创建出一个对象

书写

1
2
3
4
5
6
7
8
9
10
new 类或者接口(参数列表){
//一般是方法重写
}


父类名 对象名 = new 父类名(参数列表){
// 一般是方法重写
}


常见应用场景

  1. 方便地创建一个子类对象
  2. 只想用一次,不值得专门写一个类
  3. 常见于回调、监听器、线程任务等
  4. 通常作为对象参数传递给方法使用

函数式编程

Lambda

特点

Lambda 表达式 是 定义函数的语法
Lambda 函数 是 表达式运行后生成的函数对象

  1. Lambda不是可以简化所有匿名内部类,而是只能简化函数式接口的匿名内部类
  2. Lambda函数可以简化某些匿名内部类对象,从而让代码更简洁,可读性更好

函数式接口:只有一个抽象方法的接口,可以用@FunctionalInterface注解检查

书写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

@FunctionalInterface
interface A{
void B()// 抽象方法1
}


A a = new A(){
@Override
public void B(){
//重写方法1
}
}


A a = () ->{
//重写方法1
}

Lambda表达式省略规则

  1. 参数类型可以全部不写
    1
    2
    3
    (String s) -> System.out.println(s);  
    // 等价于
    (s) -> System.out.println(s);
  2. 如果只有一个参数,可以省略(),但是多个参数不可以省略()
    1
    s -> System.out.println(s);
  3. 如果Lambda表达式中只有一行代码,大括号可以省略,但是同时也要省略;,如果这行语句是return,也必须去掉return
    1
    2
    3
    s -> System.out.println(s)

    (a, b) -> a + b

方法引用:简化Lambda表达式

静态方法引用

书写

1
类名::静态方法

常见使用场景

  1. 若Lambda表达式里只是调用一个静态方法,并且->前后参数的形式一致,就可以使用静态方法引用

实例方法引用

书写

1
对象名::实例方法

常见使用场景

  1. 如果某个Lambda表达式只是通过对象名称调用一个实例方法,并且->前后参数的形式一致,就可以使用实例方法引用

特定类的方法引用

书写

1
2
类型名称::方法

场景使用场景

  1. 若某个Lambda表达式中只是调用以特定类的实例方法,并且前面参数列表中第一个参数是作为方法的主调,后面的所有参数都是作为该实例方法的入参的,则此时就可以使用特定类的方法引用

主调:如(a1,a2) -> a1.func(a2) 则a1就是主调


常用API

String

创建字符串对象的方式

方式一:Java程序中所有的字符串文字都为此对象
方式二:调用String类的构造器初始化字符串对象

构造器说明
public String()创建一个空字符串对象,不含任何内容
public String(String original)根据传入的字符串内容,来创建字符串对象
public String(char[] chars)根据字符数组内容,创建字符串对象
public String(byte[] bytes)根据字节数组内容,创建字符串对象

两种方式区别

  1. 使用””创建的字符串对象会存入字符串常量池中,相同字符串一个地址,只存储一份
  2. 使用构造器创建的对象,每new一次就会产生一个新的字符串对象存储在堆内存中

String提供的常用方法

方法名说明
public int length()获取字符串的长度
publi char charAt(int)返回某个索引的字符
public cgar[] toCharArray()将当前字符串变成字符数组
public boolean equals(Object)与另一个字符串内容一样返回true
public boolean equalsIgnoreCase(String)与上面比,忽略大小写
public String substring(int,int)返回索引区间字符串(包前不包后)
public String substring(int)返回从索引处开始到末尾的字符串
public String replace(CharSequence,CharSequence)使用新值,将字符串内的旧值替换
public boolean contains(CharSequence)判断字符串中有没有包含某个字符串
public boolean startsWith(String)判断是不是以某个字符串开头
public String[] split(String)以某字符串作为分割

==比较的是地址,equals比较的是内容

ArrayList

是集合容器

创建方式

1
ArrayList<E> 名称 = new ArrayList<>() //创建空的集合对象

ArrayList提供的常用方法

常用方法名说明
public boolean add(E)将指定的元素添加到集合末尾
public void add(int,E)在集合指定索引处插入指定位置
public E get(int)返回指定索引处的元素
public int size()返回集合中元素个数
public E remove(int)删除并返回指定索处的元素
public boolean remove(Object)删除指定元素,判断是否成功
public E set(int,E)修改指定索引的元素,返回被修改的元素

异常

两个分支RuntimeException和编译时异常,前者编译阶段不会报错,如数组越界

处理异常

  1. 方法上使用throws关键字,可以将异常抛给调用者
  2. try…catch捕获异常
1
2
3
4
5
6
7
8
9
方法 throws 异常1,异常2,...{}

try{}
catch(异常1 变量){
//异常处理
}catch(异常2 变量){
// 异常处理
}
...
  • 方案一:底层异常往上抛,最外层捕获异常
  • 方案二:最外层捕获异常之后,尝试重新修复

异常作用

  1. 是定位bug的重要信息
  2. 可以作为方法内部的特殊返回值,以便通知上层调用者,方法的执行问题

自定义异常

Java无法为所有的问题都定义异常

自定义运行时异常

类继承自RuntimeException

重写构造器,通过throw new 异常类(*)来创建对象并抛出

有参构造器使用super调用父类的构造器

特点:编译时不报错,运行时可能出现

自定义编译时异常

类继承自Exception

重写构造器,通过throw new 异常类(*)来创建对象并抛出

有参构造器使用super调用父类的构造器

特点:编译时就会报错


泛型

作用

  1. 泛型提供了在编译阶段约束所能操控的数据类型,并自动进行检查的能力,可以避免强制类型转换,极其可能出现的异常

泛型类

1
修饰符 class 类名<类型变量,类型变量,...>{}

泛型接口

实现类对每个函数传入值类型不尽相同,用泛型可以提高灵活性

1
修饰符 interface 接口名<类型变量,类型变量,...>{}

泛型方法

1
2
3
4
5
6
7
8
修饰符 <类型变量,类型变量,...> 返回值类型 方法名(形参){}


// 是泛型方法
public <T> void func1(){}

// 不是泛型方法,这是使用泛型类的泛型
public E get(){}

通配符

  1. 即”?”,可以在使用泛型时代表一切类型,E T K V是在定义泛型的使用

    使用泛型指在已有的类的情况下使用泛型

泛型上下限

  1. 泛型上限: ?extends 类名A ?能接收的必须是A类及其子类
  2. 泛型下限: ?super 类名B ?能接收的必须是B类及其父类

泛型支持的数据类型

  1. 泛型和集合不支持8种基本数据类型,只支持对象数据类型(引用数据类型)

可以使用包装类

基本数据类型引用数据类型
byteByte
shortShort
intInteger
longLong
charCharacter
floatFloat
doubleDouble
booleanBoolean

Integer.valueOf()函数会缓存-128到127,在这个区域内不用new新对象,而超过这个区域则会new新对象

int转String:Integer.toString(int)
String转int:Integer.valueOf(String)

转其他基本数据类型也是,引用数据类型.valueOf()


Stream流

Stream 不是集合,它不会存储数据,而是对数据进行 计算/操作 的一种抽象。
结合了Lambda表达式
数据源 → Stream 流操作 → 结果。

使用步骤:获取Stream流,然后使用流的各种方法处理数据,最后收集

获取Stream流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 由Collection类提供
* 获取当前集合对象的Stream流
*/
default Stream<E> stream()


// 以下是数组的
/**
* 由Arrays类提供
* 获取当前数组对象的Stream流
*/
public static <T> Stream<T> stream(T[] array)

Stream<String> s11 = Arrays.stream(array);
/**
* 由Stream类提供
* 获取当前接受数据的Stream流
*/
public static<T> Stream<T> of(T...values)

Stream<String> s12 = Stream.stream(array);

Map集合拿Stream流

1
2
3
4
5
6
// 获取键流
Stream<String> s1 =map.keySet().stream();
// 获取值流
Stream<Integer> s2 = map.values().stream();
// 获取键值对流(转set)
Stream<Map.entry<String,Integer>> s3 = map.entrySet().stream();

Stream流常用方法

调用后会返回新的流

常用中间方法说明
Streamfilter(Predicate<? super T> predicate)用于将流中的数据过滤
Streamsorted()对元素进行升序排序
Streamsorted(Comparator<? super T> comparator)按照指定规则排序
Streamlimit(long maxSize)获取前几个元素
Streamskip(long n)跳过前几个元素
Streamdistinct()去除流中的重复元素
Streammap(Function<? super T,? extends R> mapper)对元素进行加工,并返回对应的新流
staticStreamconcat(Stream a,Stream b)合并两个流为一个流

Stream的终结方法

调用之后返回的不是流了

常用终结方法说明
void forEach(Consumer action)对流运算后的元素进行遍历
long count()统计对流运算后的元素个数
Optionalmax(Comparator<? super T> comparator)对流运算后的最大值元素
Optionalmin(Comparator<? super T> comparator)对流运算后的最小值元素

max、min返回的可能是空的,所以要放在Optional中,使用返回值.get()即可得到值

终结方法::收集Stream流

收集Stream流:将流操作后的结果转回到集合或者数组中

Stream提供的常用终结方法说明
R collect(Collector collctor)把流处理后的结果放到指定集合
Object[] toArray()把流处理后的结果放到数组
Collectors的具体收集方式说明
public staticCollector toList()元素收集进List集合
public staticCollector toSet()元素收集进Set集合
public static Collector toMap(Function keyMapper,Function valueMapper)元素收集进Map集合

可变参数

书写

定义在方法、构造器的形参列表中

1
数据类型...参数名称

作用

  1. 可以表示0个或者多个相同类型的数据给它,也可以传一个数组给它
  2. 灵活的接受数据,可以替代数组传参
  3. 这种参数在方法体内变成数组形式

File

File类对象可以代表文件或者文件夹

创建File对象

构造器说明
public File(String path)根据文件路径创建对象
public File(String parent,String child)根据父路径和子路径创建
public File(File file,String child)根据父路径对应文件和子路径名字创建
  1. 创建时不管路径到底有没有对应的文件或文件夹

判断文件类型获取文件信息

方法名称说明
public boolean exists()判断当前对象对应文件路径是否存在
publi boolean isFile()判断当时文件对象对应的是否为文件
public boolean isDirectory()判断当时文件对象对应的是否为目录
public getName()获得当前文件名称(包含后缀)
public long length()获取文件大小。返回字节个数
public long lastModified()获取文件最后修改时间
public String getPath()获取创建文件对象时使用的路径
public String getAbsolutePath()获取绝对路径

创建删除文件/文件夹

方法名称说明
public boolean createNewFile()创建一个新的空文件
public boolean mkdir()创建一个空目录
public boolean mkdirs()创建一个多级空目录
public boolean delete()删除文件、空文件夹

delete默认只能删除文件和空文件夹,删除后不会进入回收站

遍历文件夹

只能遍历一级文件夹

方法名称说明
public String[] list()获得一级文件夹下所有文件名称
public File[] ListFiles()获得一级文件夹下所有的文件对象

ListFiles注意事项

  1. 文件的对象调用或者路径不存在时,返回null
  2. 文件夹为空时返回空数组
  3. 文件夹里面有内容时,返回所有一级文件和文件夹的路径放在数组中
  4. 有隐藏文件时也会返回
  5. 无权访问文件夹时,返回null

字符集

ASCII字符集

  1. 一个字节存储一个字符
  2. 只有数字英文符号
  3. 字节首位是0
  4. 由于很多字符集都兼容了ASCII码,所以一般不会乱码

GBK

  1. 中文字符两个字符
  2. 中文字符首位必须是1
  3. 数字。英文是一个字符

Unicode >> UTF-8

可变长编码方案
分为4个长度区:1,2,3,4个字节

  1. 英文数字占1个字节
  2. 中文占3个字节

UTF-8 二进制编码方式

  1. 自上而下分别是1字节、2字节、3字节、4字节长度区间
  2. 去掉前缀码后剩下的位置会被对应字符填充,左边多的地方补充为0

0xxxxxxx (ASCII码)
110xxxxx 10xxxxxx
1110xxxx 10xxxxxx 10xxxxxx
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

编码和解码

方法名称说明
byte[] getBytes(String CharsetName)按字符集编码,CharsetName为空时是默认字符集
String(byte[],String CharsetName)按字符集解码,CharsetName为空时是默认字符集

IO流

按流方向分:

input流,负责将数据读到内存
output流,负责写数据出去

按流内容分:

字节流,适合操作所有文件
字符流,适合操作纯文本文件,如果txt文件、java文件

IO流四大类:

  • 字节输入流(InputStream)
  • 字节输出流(OutputStream)
  • 字符输入流(Reader)
  • 字符输出流(Writer)

FileInputStream(文件字节输入流)

以内存为基准,可以将磁盘文件中数据以字节的形式送到内存

  1. 创建文件字节输入流管道与源文件接通

    • public FileInputStream(File file)
    • public FileInputStream(String filePath)
  2. 每次读取一个字节,若无字节返回-1

    • public int read()
  1. 每次用一个字节数组读取数据,返回数组读取多少个字节,没有数据可读会返回-1
    • public int read(byte[] bs)

FileOutputStream(文件字节输出流)

以内存为基准,可以将内存中数据以字节的形式送到磁盘文件

  1. 创建文件字节输出流管道与源文件接通

    • public FileOutputStream(File file)
    • public FileOutputStream(String filePath)
  2. 创建文件字节输出流管道与源文件接通,可追加数据

    • public FileOutputStream(File file,boolean append)
    • public FileOutputStream(String filePath,boolean append)
  3. 写一个字节出去

    • public void write(int a)
  4. 写一个字节数组出去

    • public void write(byte[] buffer)
  5. 写一个字节数组的一部分出去

    • public void write(byte[] buffer,int pos,int len)
  6. 关闭流

    • public void close() throws IOException

可以输入"\r\n".getByte()实现换行


多线程

创建线程 - 继承Thread类

  1. 通过继承Thread类,然后覆写run方法
  2. 创建该类对象
  3. 调用对象的start方法启动线程

优缺点

  1. 简单
  2. 已经继承了Thread类,不能继承其他类,不利于功能扩展

调用start才是启动子线程,直接调用run还是单线程

创建线程 - 实现Runnable接口

  1. 通过实现Runnable接口,然后覆写run方法
  2. 创建该类任务对象
  3. 任务对象传给Thread类构造其线程对象
  4. 调用后面构造的线程对象的start方法启动线程

优缺点

  1. 任务类只是实现接口,还能实现其他接口和继承其他类,扩展性强
  2. 需要多一个Runnable对象

可以用匿名内部类方法简化,Runnable是典型的函数式接口

创建线程 - 利用Callable接口、FutureTask类实现

上面两种run方法都不能返回结果,若使用静态变量存储结果,不能保证读取变量时子线程运行完毕

  1. 创建任务对象
    • 定义一个类实现Callable接口,重写call方法,封装要做的事情和返回的数据
    • 把Callable类型的对象封装成FutureTask(线程任务对象)
  2. 把线程任务对象交给Thread对象
  3. 调用Thread类的start函数启动线程
  4. 线程执行完毕后通过FutureTask对象的get方法去获取线程任务执行的结果

get方法会阻塞当前线程,直到有返回

优缺点:

  1. 实现的是接口,而且可以获取线程执行完毕后的结果
  2. 编码稍复杂

Thread常用方法