关于并发的理论基础,见另一篇 聊聊并发和并发模型
在 Java 中创建线程
在 Java 中,线程是 java.lang.Thread 或其子类中的实例。通过 new 一个线程实例并调用 start 方法来启动线程。
1 | Thread thread = new Thread(); |
但是我们总得指定线程做一些事,可以用两种方式来指定。
关于并发的理论基础,见另一篇 聊聊并发和并发模型
在 Java 中,线程是 java.lang.Thread 或其子类中的实例。通过 new 一个线程实例并调用 start 方法来启动线程。
1 | Thread thread = new Thread(); |
但是我们总得指定线程做一些事,可以用两种方式来指定。
对于较短的文本,我们可以直接把文本存到一个String里
1 | // 整个文本 |
如果想按行读取,可以读文件并存到 List 集合里,集合的每一个元素代表每一行的一个String
1 | // 按行读取 |
Java 中的 Stream 提供了数据源,让你可以在比集合类更高的概念层上指定操作。使用 Stream,只需要指定做什么,而不是怎么做。你只需要将操作的调度执行留给实现。
简单地说,流就是一组数据,经过某种操作,产生我们所需的新流,或者输出成非流数据。
流的来源,可以是集合,数组,I/O channel, 生成器(generator)等。流的聚合操作类似 SQL 语句,比如filter, map, reduce, find, match, sorted等。
例如,从文件从获取流:
1 | try (Stream<String> lines = Files.lines(Paths.get("/path/to/file.txt"))) { |
我们平时所采用的 命令式编程(OO也是命令式编程的一种)关心解决问题的步骤。你要做什么事情,你得把达到目的的步骤详细的描述出来,然后交给机器去运行。
而函数式编程关心数据的映射,或者说,关心类型(代数结构)之间的关系。这里的映射就是数学上“函数”的概念,即一种东西和另一种东西之间的对应关系。所以,函数式编程的“函数”,是数学上的“函数”(映射),而不是编程语言中的函数或方法。
函数式编程的思维就是如何将这个关系组合起来,用数学的构造主义将其构造出你设计的程序。用计算来表示程序,用计算的组合来表达程序的组合。
在Java异常处理中,一个方法可以通过 抛出(throw) 异常来发出一个严重问题的信号。调用链中的某个方法,负责 捕获(catch) 并处理异常。捕获到的异常不仅可以在当前方法中处理,还可以将异常抛给调用它的上一级方法去处理。
异常处理的根本优点是:将错误检测和错误处理的过程解耦。
Java 的异常都派生自 Throwable 类,Throwable 又分为 Error 和 Exception。Error 不是我们的程序所能够处理的,比如系统内存耗尽。我们能预知并处理的错误属于 Exception。Exception又分为 unchecked exception 和 checked exception。 unchecked exception 属于 RuntimeException 。
当然,所有的异常都发生在运行时(Runtime),但是 RuntimeException 派生的子类异常在编译时不会被检查。
在 Java8 中,分析 java.lang.String 类的源码,可以发现 String 内部维护的是一个 char 数组。同时可以发现,String类被 final
修饰,即不可变的。
1 | public final class String |
在 Java9 中,将 char 数组优化成了 byte 数组。
1 | private final byte value[]; |
集合就是一个放数据的容器,准确的说是放数据对象引用的容器。
Java集合主要可以划分为三个部分:
假设我们现在有一个存储字符串字典键值对的类,就像这样
1 | public class Entry { |
在这个类中,我们用 int 类型来存储 key 值, 用 String 类型来存储 value 值。
现在,老板要求,除了 int 类型的 key 和 String 类型的 value之外,还得提供其他类型的 key 和 value 。 比如 double 类型的 key, boolean 类型的value。
我们不可能写很多个相似的类,只是换一下类型。8种基本数据类型或许可以这么干,但是存储的是抽象数据类型呢?我们不可能所有类型都写一个对应的类。
为了解决这个问题,我们可以用 Java 泛型: 只写一个类,实例化的时候再写明是什么类型就好了。这就是泛型类。
泛型仅仅是java的语法糖,它不会影响java虚拟机生成的汇编代码,在编译阶段,虚拟机就会把泛型的类型擦除,还原成没有泛型的代码。
继承是在现有的类的基础上创建新类的过程。继承一个类,你也就重用了它的方法,而且还可以添加新的方法和域。
举个例子:员工有薪水,管理者有薪水+奖金, 管理者继承员工,增加 bounus 字段和 setBonus 方法即可。这种情况就是管理者类继承了员工类。
1 | public class Manager extends Employee { |
Manager
类继承了Employee
类,除了获得Employee类的变量和方法外,还额外添加了bonus变量和setBonus方法。
假设有一种整数序列服务,这种服务可以计算前n个整数的平均值。就像这样:
1 | public static double average(IntSequence seq, int n){ |
我们传入一个序列seq,以及我们想计算这个序列的前n个数,它返回平均数。
然而,这样的序列可以有很多种形式,比如用户给出的序列、随机数序列、素数序列、整数数组中的元素序列……