Java漫游笔记-09-多线程

什么是线程

线程,通常被称为轻量级的进程,是操作系统调度的最小单元。
线程是进程中的一个实体,同一个进程中的多个线程共享该进程的资源,因此会相会影响。

创建和启动线程

创建新执行线程有两种方法。

Thread

一种方法是将类声明为 Thread 的子类。并且,该子类应重写 Thread 类的 run() 方法。

1
2
3
4
5
public ThreadClass extends Thread {
public void run(){
// some code
}
}

然后,创建并启动一个线程:

1
2
ThreadClass thread = new ThreadClass();
thread.start();

Runable

另一种方法是声明实现 Runnable 接口的类。然后,该类实现 run() 方法。

1
2
3
4
5
public ThreadClass implements Runnable {
public void run(){
// some code
}
}

接着,通过实例化一个 Thread 对象,并将自身作为运行目标,启动线程:

1
2
3
Runnable r = new ThreadClass();
Thread thread = new Thread(r);
thread.start();

大多数情况下,如果只想重写 run() 方法,而不重写其他 Thread 类的方法,那么应使用 Runnable 接口。这很重要,因为除非程序员打算修改或增强类的基本行为,否则不应为该类创建子类。

实际上,Thread 类也实现了 Runnable 接口:

1
2
3
public class Thread implements Runnable {

}

start() & run()

start() 使线程开始执行(或者说做好随时被执行的准备),其内部调用了一个名为 start0() 的本地方法,这个本地方法会创建了真正的平台相关的本地线程,最终还会通过 Java 虚拟机调用该线程的 run() 方法。

run() 方法和普通方法没有本质区别,直接调用 run() 方法不会报错,但是却是在当前线程执行,而不会创建一个新的线程。

从方法调用的角度的看,一个线程对象只能 start() 一次,但是 run() 可以反复调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public synchronized void start() {

if (threadStatus != 0)
throw new IllegalThreadStateException();

group.add(this);

boolean started = false;
try {
// 调用本地方法启动线程
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */

}
}
}

private native void start0();

public void run() {
if (target != null) {
target.run();
}
}

在 Java 中,每次程序运行至少启动 2 个线程:一个是 main 线程,一个是垃圾回收(GC)线程。

阅读全文 »

Java漫游笔记-08-集合

什么是集合

在 Java 中,集合表示一组对象,这些对象也称为集合的元素。

使用集合框架的好处

通过 API 文档,我们不难发现,其实在 JDK 1.0 的时候,只包含了很少的集合类(例如:VectorStackArrayHashtable ),
然而,集合的应用场景非常广泛,因此,在 JDK 1.2 的时候由大牛 Josh Bloch 设计并实现的集合框架便应运而生了。
使用 JDK 提供的集合框架,主要好处如下:

  • 减少开发成本,不用开发者再造“轮子”。
  • JDK 提供的集合类都经过了良好的测试,在代码质量和性能上都有所保证。
  • 提供了一套接口标准,为不同开发者提供了一种“共同的语言”。
阅读全文 »

Java漫游笔记-07-泛型

为什么需要泛型

JDK 1.5 引入泛型。
在这之前,经常会看到这样的代码:

1
2
3
ArrayList nameList = new ArrayList(); // 不知道这个是什么类型的集合
nameList.add("Franky"); // 可以向数组列表中添加任意类型的对象
String name = (String) nameList.get(0); // 必须进行强制转换

从代码中,我们不难发现问题:

  • 当我们向数组列表中添加元素时,没有任何的类型安全检查。换言之,我们可以往列表中添加任意类型的对象。
  • 当我们从数组列表中获取一个值时,必须进行强制类型转换。

而有了泛型之后:

1
2
3
ArrayList<String> nameList = new ArrayList<String>(); // 显式声明集合中包含的是 String 对象
nameList.add("Franky"); // 只能添加 String 对象,否则编译不通过
String name = nameList.get(0); // 不需要进行类型转换

泛型的好处是显而易见的:

  • 代码具有更好的可读性,能够清楚的知道类型。(例如:ArrayList<String> 就可以这么理解:这是一个 String 类型的数组列表。)
  • 在编译期提供类型安全检查。
  • 所有的类型转换都是自动完成的。
阅读全文 »

Java漫游笔记-06-异常

人非圣贤,孰能无过?

其实圣贤也会犯错,更何况是人(yuan)写的程序。
不过,程序出错时,只要能做到以下几点,用户一般就会原谅你:

  • 用恰当的方式告知用户。
  • 处理异常,把程序拉回安全的状态,让用户可以继续操作。
  • 否则,保存数据并以合适的方式退出。
阅读全文 »

Java漫游笔记-05-事件处理

核心概念

  • 监听器:实现特定监听接口的对象。
  • 事件源:能够注册监听器并发送事件的对象。

工作机制

  • 当事件发生时,事件源将事件对象传递给所有注册的监听器对象。
  • 监听器对象根据事件对象的信息决定如何响应。
阅读全文 »

Java漫游笔记-04-接口和内部类

接口

接口主要是用于描述某个类具体什么样的功能,但是又不给出具体的实现。属于较高层次的抽象设计。
不使用抽象类,而选择接口的原因,主要是由于继承的限制。
而 java 的设计者选择不实现多种继承,主要是考虑到多重继承给语言本身所带来的复杂性和低效性。
复杂性比较好理解,而低效性就有点难理解了,目前能想到的有:在类加载阶段,一个类的初始化,需要先完成其全部父类的初始化,而接口则是真正用到其父接口的时候才会去初始化它。另外,在运行时,如果使用多继承,对象的属性或方法的引用地址计算以及对象的类型转换都会影响性能。

在语法上,一个类最多只能 extents 一个类:

1
2
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {}

但是,一个接口却可以 extents 若干个接口,例如:

1
2
public interface ConcurrentNavigableMap<K,V>
extends ConcurrentMap<K,V>, NavigableMap<K,V> {}

阅读全文 »

Java漫游笔记-03-04-反射和Class类

在运行期分析类和使用类的能力称之为反射。

Class类

要理解和使用反射,需要先认识一下 Class 类和 Class 对象。
这个 Class 类和 class 关键字可不同,Class 对象是与之相对应的对象在运行时的类型标识,是程序访问该对象的类型数据的外部接口。
简单来讲,就是:
在 Java 程序运行时,对于任意的一个类或者是对象,我们都能够通过 Class 对象来获取它的类名、属性和方法等信息。
在 API Doc 中,有如下描述:

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。
枚举是一种类,注释是一种接口。
数组也是一种类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
基本的 Java 类型( booleanbytecharshortintlongfloatdouble )和关键字 void 也表示为 Class 对象。

Class 类没有公共构造方法,因此不能显式地构造一个 Class 对象。
Class 对象是在类加载阶段由 JVM 调用类加载器(ClassLoader)中的 defineClass() 方法自动构造的。
另外,Class 对象还有点特殊:它是存放在方法区的,而不是存放在 Java 堆中。

Class 对象可以通过以下几种方法获取:

  • object.getClass()
  • ClassName.class
  • Class.forName(String className)

Class 类还是泛型的:Class<T> ,例如:String.class 实际上是 Class<String> 的一个对象。

阅读全文 »

Java漫游笔记-03-03-Object类

Object 类是所有类的超类。
所有对象(包括数组)都实现这个类的方法。
因此我们很有必要了解一下它。

equals()

Objectequals() 是直接比较两个对象的引用是否相同。

如果需要判断对象的属性值是否相等,需要覆盖这个方法。
除了 Stringequals() 很常见以外,其实还有很多地方,都可能会用到 equals() ,例如,在集合类中的 contains(Object)remove(Object) 都用到了: (o==null ? e==null : o.equals(e))

覆盖 equals() 需要注意的是,需要满足以下条件:

  • 自反性:x.equals(x) 返回 true
  • 对称性:若 x.equals(y)true ,则 y.equals(x) 亦为 true
  • 传递性:若 x.equals(y)truey.equals(z) 也为 true ,则 x.equals(z) 亦为 true
  • 一致性:x.equals(y) 的第一次调用为 true ,那么 x.equals(y) 的第 2 次、第 3 次、第 n 次调用也均为 true ,前提条件是没有修改 x 也没有修改 y;
  • 对于非空引用 x ,x.equals(null) 返回为 false
阅读全文 »

Java漫游笔记-03-02-对象和类的知识点

对象构造过程

  1. 所有属性,如果在声明中没有明确赋值,则初始化为缺省值( 0falsenull );
  2. 按照声明次序,依次执行 static 语句(块);(仅在类首次被加载的时候,执行一次,并且,如果该类继承自某个类,则先执行父类的static 语句)
  3. 按照声明次序,依次执行对象语句块(类似的,如果该类继承自某个类,则先执行父类的语句块);
  4. 执行构造方法(如果在一个构造方法中,有调用另外一个构造方法,则先执行被调用的方法主体)。
阅读全文 »

Java漫游笔记-03-01-对象和类

对象

Java 是一种面向对象的程序设计语言。
要理解什么是面向对象,首先要理解什么是对象
对象的概念非常好理解,因为它不是程序设计领域的专有名词,在我们的现实生活中,对象无处不在,一个人是一个对象,一台电脑也是一个对象。
程序设计中的对象就是用来模拟(或者叫描述)现实生活中的对象。

前人经过分析,认为对象有两大特征:它们通常都拥有行为属性
行为一般是指这个对象有哪些自发的能力,或者是,可以对这个对象施加某些操作。
属性则反映了这个对象的特点。

一个对象的行为和属性并不是绝对的,这往往取决于观察者的视角。
并且,行为和属性也不都是必需的。

阅读全文 »