反射
一、获取class对象:
1、类名.class:这种获取方法只有在编译前已经声明了该类的类型才能获取到
1 | Class<HashMap> hashMap = HashMap.class; |
2、实例.getClass:通过实例化对象获取该实例的Class对象
3、Class.forName("类的全限定名"):通过类的全限定名获取Class对象
二、构造类的实例化对象
1、Class对象调用newInstance()方法,调用默认无参构造器
1 | Class<?> hashMapClass = Class.forName("java.util.HashMap"); |
2、Constructor构造器调用newInstance()方法
1 | Class<?> hashMapClass = Class.forName("java.util.HashMap"); |
三、获取类的所有信息

1、获取类中的变量(Field)
getFields:获取类中所有被public修饰的变量
getFields(name):根据变量名获取类中的一个变量,该变量必须被public修饰
getDeclaredFields():获取类中所有的变量,但无法获取继承下来的变量
getDeclaredField(name):获取类中的某个变量,但无法获取继承下来的变量
2、获取类中的方法
getMethods():获取类中被public修饰的所有方法
getMethod(name, paramTypes):根据名字和参数类型获取对应方法,该方法必须被public修饰
getDeclaredMethods():获取所有方法,但无法获取继承下来的方法
getDeclaredMethod(name, paramTypes):根据名字和参数类型获取对应方法,无法获取继承下来的方法
3、获取类的构造器(Constructor)
getConstructors():获取类中所有被public修饰的构造器 Constructor
getConstructor(paramTypes):根据参数类型获取类中某个构造器,该构造器必须被public修饰
getDeclaredConstructors():获取类中所有构造器 Constructor
getDeclaredConstructor(paramTypes):根据参数类型获取对应的构造器
异常处理
1 | ┌───────────┐ |
Throwable是异常体系的根,它继承自Object,分为Error和Exception,前者表示严重的错误,程序对此一般无能为力;后者表示运行时的错误,应该被捕获并处理。
Exception又分为两大类:
RuntimeException以及它的子类;- 非
RuntimeException(包括IOException、ReflectiveOperationException等等)
捕获异常使用try...catch语句,把可能发生异常的代码放到try {...}中,然后使用catch捕获对应的Exception及其子类:
集合类
Collection:List、Set
List实现类
| 实现类 | 说明 | 用法 |
|---|---|---|
| ArrayList | 数组结构存储数据,查询速度快,增删改慢 | List list = new ArrayList() |
| LinkList | 链表结构存储数据,增删速度快,查询稍慢; | List list = new LinkList(); |
| Vector | 数据结构存储数据 |
Set实现类
| 实现类 | 说明 |
|---|---|
| Hashset | 底层数据结构是哈希表,存储的数据无序 |
| LinkedHashSet | 通过哈希表加上链表的形式存储数据,hash存储元素,链表维护元素的存入的先后顺序 |
Map实现类
| 实现类 | 说明 |
|---|---|
| HashMap | 最常用的Map,存储没有顺序 |
| LinkedHashMap | 保存了记录的插入顺序 |
进程与线程
创建线程
Java 有 3 种创建线程的方式:
- 继承 Thread 类,重写 run() 方法
- 实现 Runnable 接口,重写 run() 方法
- 实现 Callable 接口,重写 call() 方法
继承 Thread 类
需要实现 run() 方法,因为 Thread 类也实现了 Runable 接口。
当调用 start() 方法启动一个线程时,虚拟机会将该线程放入就绪队列中等待被调度,当一个线程被调度时会执行该线程的 run() 方法。
1 | public class MyThread extends Thread { |
1 | public static void main(String[] args) { |
实现 Runnable 接口
需要实现接口中的 run() 方法。使用 Runnable 实例再创建一个 Thread 实例,然后调用 Thread 实例的 start() 方法来启动线程。
1 | public class MyRunnable implements Runnable { |
1 | public static void main(String[] args) { |
实现 Callable 接口
需要实现接口中的 call() 方法。 call() 方法有返回值,返回值通过 FutureTask 进行封装。
1 | public class MyCallable implements Callable<Integer> { |
1 | public static void main(String[] args) throws ExecutionException, InterruptedException { |
实现处理线程的返回值
方式一:主线程等待法
如果未获取到值,则主线程等待,一直到获取值为止。
1 | public class CycleWait implements Runnable{ |
方式二:join()
使用 Thread 的 join() 阻塞当前线程以等待子线程处理完毕。
1 | public class CycleWait2 implements Runnable{ |
方式三:Callable + FutureTask
实现 Callablee 接口,使用 FutureTask 接收 call() 方法返回的数据。
1 | public class CycleWait3 implements Callable<String>{ |
方式四:线程池 + Future
线程池执行任务,返回的结果使用 Future 接收。
1 | public class CycleWait4 implements Callable<String>{ |
三、基础线程机制
Executor
Executor 管理多个异步任务的执行,而无需程序员显式地管理线程的生命周期。这里的异步是指多个任务的执行互不干扰,不需要进行同步操作。
主要有三种 Executor:
- CachedThreadPool:一个任务创建一个线程;
- FixedThreadPool:所有任务只能使用固定大小的线程;
- SingleThreadExecutor:相当于大小为 1 的 FixedThreadPool。
1 | public static void main(String[] args) { |
Daemon
Java 中有两类线程:
- 用户线程(User Thread):运行在前台的线程,比如主线程。
- 守护线程(Daemon Thread):程序运行时在后台提供服务的线程,不属于程序中不可或缺的部分。
一旦所有的用户线程都退出了,虚拟机也就退出了,因此不要在守护线程中执行业务逻辑操作,因为随时都可能中断(甚至无法执行 finnally 中的语句)。
注意:在线程启动之前使用 setDaemon() 方法可以将一个线程设置为守护线程。
1 | public static void main(String[] args) { |
start() & run()
start()
启动一个新线程,新线程会执行 run() 方法;
start 不能被重复调用,如果重复调用,会抛
java.lang.IllegalException。run()
和一个成员方法相同,可以被重复调用;
单独调用 run() 不会启动新线程
sleep() & wait()
sleep() 和 wait() 的区别:
持有者
sleep() 是 Thread 的静态方法;
wait() 是 Object 的成员方法,所以任何对象都可以调用 wait()
锁是否会释放
sleep() 会一直持有同步锁,继续占用 CPU 资源;
wait() 会释放同步锁,使得其他线程可以使用同步块或者同步方法
使用范围
wait() 只能在同步代码块或同步方法中使用,否则,会抛
java.lang.IllegalMonitorStateException;sleep() 可在任何地方使用
yield() & wait()
Thread.yield() 声明了当前线程已经完成了生命周期中最重要的部分,可以切换给其它线程来执行。该方法只是对线程调度器的一个建议,而且也只是建议具有相同优先级的其它线程可以运行。
yield() 和 wait() 的区别:
yield() 是 Thread 的静态方法
wait() 是 Object 的成员方法
yield() 让当前线程由“运行状态”进入“就绪状态”;
wait() 让当前线程由“运行状态”进入“等待状态”
yield() 不会释放同步锁;
wait() 会释放同步锁,使得其他线程可以使用同步块或者同步方法
notify() & notifyAll()
锁池:线程 A 已经拥有某个对象的锁,线程 B、C 想要调用这个对象的同步方法,此时线程 B、C 就会被阻塞,进入一个地方去等待锁的释放,这个地方就是该对象的锁池。
等待池:假设线程 A 调用了某个对象的 wait() 方法,线程 A 释放该对象的锁,同时线程 A 进入等待池,进入等待池的线程不会竞争该对象的锁。
notifyAll() 让所有处于等待池的线程进入锁池去竞争锁。
四、线程生命周期和状态
6 种线程状态
新建(New)
线程被创建,但是还没有调用 start() 方法
可运行(Runnable)
可能正在运行,也可能正在等待 CPU 时间片。
包含了操作系统线程状态中的运行状态(Running)和就绪状态( Ready)。
调用 start() 方法后开始运行,线程这时候处于 Ready 状态。Ready 状态的线程获得了 CPU 时间片后就进入Running 状态。
阻塞(Blocked)
等待获取一个排它锁,如果其他线程释放了锁就会结束此状态。
无限期等待(Waiting)
等待其它线程显式地唤醒,否则不会被分配 CPU 时间片。
| 进入方法 | 退出方法 |
|---|---|
| 没有设置 Timeout 参数的 Object.wait() 方法 | Object.notify() / Object.notifyAll() |
| 没有设置 Timeout 参数的 Thread.join() 方法 | 被调用的线程执行完毕 |
| LockSupport.park() 方法 | LockSupport.unpark(Thread) |
限期等待(Timed Waiting)
无需等待其它线程显式地唤醒,在一定时间之后会被系统自动唤醒。
| 进入方法 | 退出方法 |
|---|---|
| Thread.sleep() 方法 | 时间结束 |
| 设置了 Timeout 参数的 Object.wait() 方法 | 时间结束 / Object.notify() / Object.notifyAll() |
| 设置了 Timeout 参数的 Thread.join() 方法 | 时间结束 / 被调用的线程执行完毕 |
| LockSupport.parkNanos() 方法 | LockSupport.unpark(Thread) |
| LockSupport.parkUntil() 方法 | LockSupport.unpark(Thread) |
调用 Thread.sleep() 方法使线程进入限期等待状态时,常常用“使一个线程睡眠”进行描述。
调用 Object.wait() 方法使线程进入限期等待或者无限期等待时,常常用“挂起一个线程”进行描述。
睡眠和挂起是用来描述行为,而阻塞和等待用来描述状态。
阻塞和等待的区别在于:
阻塞是被动的,它是在等待获取一个排它锁。
等待是主动的,通过调用 Thread.sleep() 和 Object.wait() 等方法进入。
死亡(Terminated)
可以是线程完成任务后自己结束,或者产生了异常而结束。
线程生命周期
线程在生命周期中并不是固定处于某一个状态而是随着代码的执行在不同状态之间切换:

线程创建之后它将处于NEW 状态,调用 start() 方法后开始运行,线程这时候处于 READY 状态。Ready 状态的线程获得了 CPU 时间片(timeslice)后就处于 RUNNING 状态。
当线程执行 wait()方法之后,线程进入 WAITING 状态。进入 WAITING 状态的线程需要依靠其他线程的通知才能够返回到运行状态,而 TIME_WAITING 状态相当于在等待状态的基础上增加了超时限制,比如通过 sleep(long millis)方法或 wait(long millis)方法可以将 Java 线程置于 TIMED WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。
当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 BLOCKED 状态。
线程在执行 Runnable 的run()方法之后将会进入到 TERMINATE 状态。
五、终止线程的几种方式
1. 退出标志
使用退出标志,使线程正常退出,也就是 run 方法完成后线程终止。
需要 while() 循环以某种特定的条件下退出,最直接的方法就是设计一个 boolean 类型的标志,并且通过设置这个标志来控制循环是否退出。 一般需要加上 volatile 来保证标志的可见性。
2. Thread.stop()
Thread.stop() 强制终止线程(不建议使用)。
3. interrupt() 中断线程
线程处于阻塞状态,使用 interrupt() 则会抛出 InteruptedException 异常。
(线程处于阻塞状态)
使用 while(! isInterrupted()) {…} 来判断线程是否被中断,使用 interrupt() 则线程终止。
(线程处于运行状态)
综合线程处于“阻塞状态”和“运行状态”的终止方式,通用的终止线程的形式如下:
1 | try{ |
举例如下:
1 | public class InterruptExample implements Runnable{ |
JAVA I/O
I/O 介绍
I/O(Input/Outpu) 即输入/输出 。
学术的说 I/O 是信息处理系统(计算机)与外界(人或信息处理系统)间的通信。如计算机,即 CPU 访问任何寄存器和 Cache 等封装以外的数据资源都可当成 I/O ,包括且不限于内存,磁盘,显卡。
软件开发中的 I/O 则常指磁盘、网络 IO。
补充:
同步 & 异步
同步、异步是消息通知机制。
- 同步:同步就是发起一个调用后,被调用者未处理完请求之前,调用不返回。
- 异步:异步就是发起一个调用后,立刻得到被调用者的回应表示已接收到请求,但是被调用者并没有返回结果,此时我们可以处理其他的请求,被调用者通常依靠事件,回调等机制来通知调用者其返回结果。
同步和异步的区别最大在于异步的话调用者不需要等待处理结果,被调用者会通过回调等机制来通知调用者其返回结果。
阻塞 & 非阻塞
阻塞、非阻塞是等待通知时的状态。
- 阻塞: 阻塞就是发起一个请求,调用者一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当条件就绪才能继续。
- 非阻塞:非阻塞就是发起一个请求,调用者不用一直等着结果返回,可以先去干其他事情。
举个生活中简单的例子:
你妈妈让你烧水,小时候你比较笨啊,在哪里傻等着水开( 同步阻塞 )。等你稍微再长大一点,你知道每次烧水的空隙可以去干点其他事,然后只需要时不时来看看水开了没有( 同步非阻塞 )。后来,你们家用上了水开了会发出声音的壶,这样你就只需要听到响声后就知道水开了,在这期间你可以随便干自己的事情( 异步非阻塞 )。
Java I/O 分类
Java 的 I/O 大概可以分成以下几类:
- 磁盘操作:File
- 字节操作:InputStream 和 OutputStream
- 字符操作:Reader 和 Writer
- 对象操作:Serializable
- 网络操作:Socket
- 新的输入/输出:NIO & AIO