第7章 对象的组织
- 一般在应用系统中,同一类型的对象通常有很多,需要对这些对象进行有效的组织。
- 一维和二维数组是存储同一类型的多个对象常用的数据结构。
对象数组
除了基本数据类型,数组的元素也可以是对象,称为对象数组。在这种情况下,数组的每一个元素都是一个对象的引用。
用数组存储对象
- 与C、C++不同,Java在数组的声明中并不为数组元素分配内存,因此[]中不需要指明数组中元素的个数,即数组长度。
- 静态初始化:使用{}在声明和定义数组的同时对数组元素进行初始化,type[] a = {new type(),new type(),new type()}。
- 动态初始化:使用运算符new为数组分配空间,type[] a = new type[size]。
- 对于对象数组,使用运算符new只是为数组本身分配空间,数组元素都为null。
- 给每一个数组元素分配空间,并将其引用赋值给对应的数组元素,a[i] = new type()。
- 数组下标为整数,从0开始,最大下标为数组长度减1。
- 对于每个数组都有一个属性length指明它的长度,即数组元素的个数。
- Java对数组元素要进行越界检查以保证安全性。
二维数组
- 数组元素的类型不仅可以是基本数据类型、对象,还可以是数组。
- 如果一维数组的元素又是一维数组,则此数组为二维数组。
- 二维数组的声明方式:type a[][],此时还没有给数组分配空间。
- 静态初始化:在声明和定义数组的同时给出数组元素的值,例如 int a[][] = {{1,2},{2,3,4},{2,1,3,5}},不必指出数组每一维的大小,系统会根据初始化时给出的初始值的个数自动算出数组每一维的大小。
- 动态初始化:对于二维数组来说,分配内存空间有两种方法:
- 一种方法是直接为每一维分配空间,例如 int a[][] = new int[2][3];
- 另一种方法是从最高维开始(而且只能从最高维开始),分别为每一维分配空间。
String s[][] = new String[2][];
s[0] = new String[2];
s[1] = new String[3]; //可以不一致
s[0][0]= "GOOD";
s[0][1]= "LUCK";
s[1][0]= "TO";
s[1][1]= "YOU";
s[1][2]= "!!!";
集合(Collection)
- 数组是Java提供的随机访问对象序列的有效方法,数组是一个简单的线性序列,访问元素的速度较快,但是其缺点是数组的大小自创建以后就固定了,在数组对象的整个生存期内其大小不可改变。
- 如果序列的大小需要动态的改变,就需要用集合类型。
- Java中有很多与集合相关的接口和类,它们被组织在以Collection接口和Map接口为根的层次结构中,称为集合框架。
Java集合框架
- Java集合框架提供了一些现成的数据结构可供使用,程序员可以利用集合框架快速编写代码,并获得优良性能。
- 集合框架是为表示和操作集合而设计的一种统一标准的体系结构,包含三大块内容:对外的接口、接口的实现、集合运算算法。
- 接口是表示集合的抽象数据类型,使集合的操作与表示分开;Java的集合接口具有层次结构。
- 实现是指实现集合接口的Java类,是可重用的数据结构。
- 算法是指执行运算的方法,例如在集合上进行查找,算法是可重用的功能。
Collection接口
- Collection接口是List、Set的父接口,AbstractCollection类实现了Collection接口。
- Collection接口里定义的常用方法有:
- boolean add(E e) —— 增加一个元素
- boolean remove(Object o) —— 删除一个元素
- void clear() —— 删除全部元素
- int size() —— 返回元素的个数
- boolean isEmpty() —— 判断是否为空
- boolean contains(Object o) —— 判断给定的对象是否在集合中
- Iterator iterator() —— 返回集合的迭代器
Set接口
- Set是一个不含重复元素的集合,是数学中“集合”的抽象。
- Set接口继承了Collection接口,并且禁止重复元素。
- 实现Set接口的类有HashSet和TreeSet。
List接口
- 实现List接口的类中的元素是有顺序的、可以包含重复元素、且每个元素都有一个index值(从0开始)标明元素在列表中的位置。
- 实现List接口的类有Vector、ArrayList、LinkedList、Stack。
Map接口
- Map是从关键字到值的映射。
- Map中不能有重复的关键字。
- 每个关键字只能映射到一个值。
- Map中的常用方法有:
- V put(K key, V value) —— 将一对关键字和值放入map中
- V get(Object key) —— 获得指定关键字对应的值,如果该关键字没有对应的值,返回null
- V remove(Object key) —— 删除指定关键字和其对应的值
- Set<K> keySet() —— 返回关键字的集合
Enumeration
- 枚举,实现Enumeration接口的对象产生一个元素序列。
- 调用nextElement()方法返回下一个元素。
//打印向量Vector v中的所有元素:
for(Enumeration e=v.elements(); e.hasMoreElements();){
System.out.println(e.nextElement());
}
Iterator接口
- Iterator用于遍历集合对象。
- 使用Iterator遍历集合时可以执行删除操作,与enumeration不同。
- Iterator的方法有:
- boolean hasNext() —— 判断是否还有后续元素。
- E next() —— 返回下一个元素。
- void remove() —— 删除底层集合对象中最后一个通过Iterator返回的元素。
常用集合类
- 我们选择一些常用的集合类,并介绍其常用方法:
- Vector —— 向量
- ArrayList —— 数组链表
- LinkedList —— 链表
- HashSet —— 哈希集合
- HashMap —— 哈希映射
- Hashtalbe —— 哈希表
Vector
- Vector能够存储任意对象。
- Vector不能存储基本类型的数据,除非将这些数据包裹在包裹类中。
- Vector的容量能够根据空间需要自动扩充。
- 增加元素方法的效率较高,除非空间已满,在这种情况下,在增加之前需要花费一定的时间先扩充容量。
Vector v = new Vector();
v.add("Hello");
v.add("World");
v.get(1);
ArrayList
- ArrayList与Vector的功能类似。
- 区别是Vector是线程同步的,多个线程访问时,只有一个可以进入访问,其他线程阻塞。而ArrayList是非线程同步的,也是非线程安全的,但比较高效。
LinkedList
- 双向链表。
- 可以在链表头和链表尾增加元素。
- 可以做为队列使用。
HashSet
- HashSet实现了Set接口。
- HashSet中的元素是无序的。
HashMap
- HashMap实现了Map接口,用于存储多组关键字和值。
- HashMap是非同步的,且允许空值null。
Hashtable
- Hashtable实现了Map接口,用于存储多组关键字和值。
- Hashtable是同步的,且不允许空值null。
第8章 线程
- 线程基础
- 线程同步
- 线程的生命周期
- 生产者与消费者问题
线程基础
- 多线程是Java语言的内嵌特性。
- Java通过多线程的机制实现了程序中的并行操作。
- Java 虚拟机允许应用程序并发地运行多个执行线程。
线程的概念
- 多任务操作系统可以创建多个进程(Process),并为每个进程分配资源,而一个进程中又可以创建多个线程(Thread),操作系统调度多线程并发(Cocurrent)执行。
- 进程是资源分配的单位,线程是调度执行的单位。
- 多线程是Java语言的内嵌特性。
- Thread类、Runnable接口、Object类、以及synchronized关键字共同构成Java语言本身对线程的支持。
Thread类
- Thread类是Java用于支持线程的类,常用方法有:
- void run() —— 线程的执行体,Thread类的子类应该覆盖此方法。
- void start() —— 启动线程,Java虚拟机会调用线程的run()方法。
- static void sleep(long millis) —— 休眠指定的时间,单位为毫秒。
- static void yield() —— 暂停正在执行的线程,让其它线程执行。
- Thread(Runnable target) —— 以一个实现Runnalbe接口的对象为参数构造线程对象。
- 从Thread类派生一个子类,并创建这个子类的对象,就可以产生一个新的线程。
- 线程类应该覆盖(Override)Thread类的run()方法,在run()方法中给出线程要执行的语句块。
- 需要调用线程对象的start()方法来启动线程,线程启动后自动进入run()方法执行,原线程将同时继续往下执行。
Runnable接口
- Runnable只有一个run()方法,实现Runnable接口的类的实例可以用来创建线程。
- 由于Java仅支持单继承,如果一个类已经继承了某个父类,此时只能通过实现Runnable接口来支持线程。
public class A extends B implements Runnable{
public void run(){
//线程执行体
}
}
//启动线程的代码
A a = new A();
Thread t = new Thread(a);
t.start();
线程同步
- 如果线程之间是独立的、不同步的,即每个线程都可以独自运行,则不需要考虑别的同时运行的线程的状态或动作。
- 但更多的情况是,同时运行的多个线程需要共享数据,并且要考虑到彼此的状态和动作。
- 互斥:多个线程访问同一个资源,但在同一时刻只能有一个线程访问这个资源,否则会破坏数据的完整性。
- 同步:线程之间存在相互配合的关系,一个线程需要等待另外一个线程完成某种操作后才能继续执行。
synchronized关键字
- synchronized关键字用于多线程互斥访问同一个对象的情况,确保同一时刻只有一个线程访问指定的对象。
synchronized(对象的引用){
语句块;
}
- synchronized关键字也可以放在方法声明前,确保同一时刻只有一个线程访问当前对象,相当于synchronized(this)。
public synchronized void f(){
//方法的实现
}
- synchronized的工作原理是:当一个线程试图进入synchronized语句块执行时,首先判断对象是否已经上锁,如果没有上锁,则继续执行,如果已经上锁(说明已经有其他线程获得锁并进入synchronized语句块执行),则进入阻塞(Blocked)状态,直到其他线程释放锁后被唤醒,然后重新竞争synchronized锁。
Thread类的同步方法
- static void sleep(long millis) —— 休眠指定的时间,单位为毫秒。
- static void yield() —— 暂停正在执行的线程,让其它线程执行。
- void join() —— 在线程t2中调用t1.join(),则t2等待t1终止才继续执行。
- void join(long millis) —— 同上,指定了等待时间,超时也会继续执行。
Object类的同步方法
- void wait() —— 使当前线程进入等待状态,直到另外一个线程唤醒它。
- 该线程必须具有对象的同步锁。
- 线程进入等待状态后,同步锁被释放,其他线程可以获得这个对象的同步锁。
- 处于等待的线程需要其它线程来唤醒才能继续执行。
- void wait(long timeout) —— 同上,但是指定了等待的超时时间,单位为毫秒。
- void notify() —— 唤醒等待此对象的一个线程。
- void notifyAll() —— 唤醒等待此对象的全部线程。
线程的生命周期
- 线程的生命周期是线程从产生到终止的全过程,一个线程在任何时刻都处于某种线程状态。
- 一个线程的生命周期由线程类、新线程(New Thread)、就绪状态(Ready)、运行状态(Running)、阻塞状态(Blocked)、等待状态(Waiting)和定时等待状态(Timed Waiting)和终止状态(Terminated)构成。
线程类
- 线程类是一个继承了Thread类或者实现了Runnable接口的Java类。
- 该类必须覆盖Thread类的run()方法,或者实现Runnable接口的run()方法。
- run()方法是线程的执行体,线程的执行从run()方法开始,当run()方法返回时,线程终止。
新线程
- 继承Thread类的线程类的对象是一个新线程,对于实现Runnable接口的类需要把该类的对象作为Thread类的构造方法的参数传递给Thread类来创建新线程。
- 新线程只是一个空的线程对象,并没有为其分配资源。
- 程序调用新线程的start()方法后,系统才为新线程分配资源,并自动调用其run()方法使线程处于等待CPU资源的就绪状态。
就绪
- 对于单CPU的系统,不可能同时运行所有线程。处于就绪状态的线程在等待Java虚拟机调度执行。
- Java虚拟机按照一定的策略调度多个线程分时占用处理器,并发执行。
运行
- 线程抢占处理的能力被分为若干个优先级,优先级高的线程比优先级低的线程抢占处理器的能力强。
- 获得处理器资源的线程进入运行状态,得到执行。
阻塞
- 一个同步语句块/方法是一段对特定资源的互斥访问代码,同一时刻只有一个线程可以进入同步语句块/方法执行。
- 当一个线程试图进入一个同步语句块/方法的时候,如果没有其它线程进入这个同步语句块/方法,则线程获得同步锁,并进入同步语句块/方法独占执行;如果已经有其它线程进入这个同步语句块/方法,则线程放弃处理器,进入阻塞状态。
- 只有当其它线程的执行完同步语句块/方法并释放同步锁时,这个线程才有机会获得同步锁,进而进入同步语句块/方法执行。
synchronized(obj){//同步语句块
//对obj对象的独占访问
}
//同步方法,相当于synchronized(this)
public synchronized returnValue methodName(){
//对this对象的独占访问
}
等待
- 如果一个线程需要等待其它线程完成特定的操作之后才能继续执行,则线程可以调用等待对象的wait()方法使自己进入等待状态。
- 处于等待状态的线程放弃处理器,释放同步锁。
- 处于等待状态的线程一直等待其它线程调用等待对象的notify()或者notifyAll()方法来唤醒它,它自己是无法醒过来了的。
- 如果一个线程需要等待另外一个线程结束之后继续执行,可以调用那个线程的join()方法使自己进入等待状态。当那个线程执行结束后,线程被唤醒并继续执行。
- 进入等待状态的线程T会释放对同步锁。其它线程唤醒线程T之后,线程T需要与其它线程竞争同步锁。再次获得同步锁之后,线程T回到等待前的状态。
synchronized (obj) {
while (执行条件不满足){
obj.wait();
}
//此处执行条件已满足,可以执行相应操作
}
定时等待
- 一个处于等待状态的线程是永远无法自己醒过来的,Java的定时等待状态是一种可以设置超时的等待状态。
- 处于定时等待状态的线程在等待指定的超时时间之后,如果依然没有被唤醒,可以自己醒过来。
- 一个线程可以调用等待对象的wait(timeout)方法使自己等待被其它线程唤醒或者等待一段时间之后自己醒过来。
- 一个线程可以调用Thread.sleep(millis)方法使自己进入定时等待状态指定的时间,然后自己醒过来。
- 一个线程可以调用另外一个线程的join(millis)方法等待那个线程结束后被唤醒或者等待指定时间之后自己醒过来。
- 调用wait(timeout)导致调用线程释放同步锁,返回后需要与其它线程竞争同步锁。
终止
- 当线程的run()方法返回时,线程进入终止状态,线程将释放占有的资源并消亡,线程的生命周期结束。
状态转换图
生产者与消费者问题
- 生产者与消费者问题是一个经典的线程同步问题。
- 生产者与消费者问题是从操作系统中的许多实际同步问题中抽象出来的具有代表性的问题。
问题描述
- 信息社会是由若干个消息的生产者和若干个消息的消费者组成的。
- 生产者生产消息,放入消息缓冲区;消费者从消息缓冲区中获取消息,使用消息。
- 由于生产者和消费者彼此独立,且运行速度不确定,所以很可能出现生产者已产生了消息而消费者却没有来得及接受消息这种情况。
- 为此,需要引入由一个或者若干个存储单元组成的临时存储区,以便存放生产者所产生的消息,平滑线程间由于速度不确定所带来的问题。这个临时存储区叫做消息缓冲区,通常用一维数组实现的循环队列来表示。
同步关系
- 生产者线程与消费者线程之间存在的同步关系如下:
- 同一时刻只能有一个生产者或者一个消费者访问消息缓冲区。
- 当消息缓冲区为空时,消费者等待生产者生产消息。
- 当消息缓冲区满时,生产者等待消费者消费消息。
Java实现
- 缓冲区采用数组实现的循环队列:缓冲区空的条件为rear==front,缓冲区为满的条件为(rear + 1) % size == front;
- 随机数对象Random rand = new Random()用于产生线程等待的随机毫秒数;
- 线程的退出:处于运行状态的线程通过置toStop标志为true使线程从run()方法返回;处于等待状态的线程由于无法自己醒过来,需要置toStop标志为true,主程序唤醒它们之后才能检测到toStop标志。
第9章 输入输出
- Java输入输出相关的类主要是在java.io包中定义的。
- Java使用流进行输入和输出。流是有序的数据序列,分为输入流和输出流。
- java.io包分为两个主要部分:字符流和字节流。字符是16位的Unicode字符,而字节是8位的一个字节。
- 字节流分为InputStream和OutputStream;而字符流分为Reader和Writer。
File类
- 文件和路径的抽象表示,并不负责读写数据。
- 构造方法:
- File(File parent, String child) —— 根据父目录parent和名字child创建File实例。
- File(String pathname) —— 根据路径名创建File实例。
- File(String parent, String child) —— 根据父目录parent和名字child创建File实例。
列出目录下所有文件
- 列出目录下的文件和目录可以使用list()或者listFiles()方法。
- 对于目录下的子目录,需要递归才能把子目录下的文件和目录列出来。
- 方法说明:
- String[] list() —— 返回目录下的文件名和子目录名的字符串数组。
- File[] listFiles() ——返回目录下的文件和子目录的File对象数组。
- boolean isDirectory() 是否是目录。
- boolean isFile() 是否是文件。
列出给定扩展名的文件
- FilenameFilter接口:
- public boolean accept(File path, String name) —— path是文件或目录所在的目录,name是文件名或目录名。
- 如果要列出,返回true,否则返回false。
- File类的方法:
- String[] list(FilenameFilter filter) ——返回目录下满足条件的文件名和子目录名的字符串数组。
- File[] listFiles(FilenameFilter filter) —— 返回目录下满足条件的文件和子目录的File对象数组。
- 设计一个实现FilenameFilter接口的类,实现accept()方法,判断给定扩展名,把这个类的对象传递给list()或者listFiles()方法。
创建目录
- 创建目录可以使用File类提供的方法:
- boolean mkdir() —— 创建目录。
- boolean mkdirs() —— 创建整个目录结构。
删除文件和目录
- 删除文件很简单,只需调用File类的delete()方法即可:
- boolean delete() —— 删除文件或目录,如果是目录,目录为空才能删除,删除成功返回true,否则返回false。
- 删除目录分为两种情况:
- 空目录:直接删除即可。
- 非空目录:首先要删除目录中的文件和子目录,需要通过递归自底向上删除目录。
移动文件/目录
- 移动文件和目录非常简单,使用File类提供的renameTo()方法即可。
- 这个方法虽然名义上只是为文件或目录更名,但功能远比此强大。
- 文件和目录能够以统一的形式移动和改名,无论目录是否为空,无论是否跨越磁盘分区:
- boolean renameTo(File dest) —— 重命名/移动文件或目录
操作文件属性
- 可以通过File类的以下方法操作文件属性:
- boolean canRead() —— 对本应用程序而言是否可读。
- boolean canWrite() —— 对本应用程序而言是否可写。
- boolean canExecute() —— 对本应用程序而言是否可执行。
- boolean setReadable(boolean readable, boolean ownerOnly) —— 将文件的可读属性设置为readable,如果ownerOnly为true,则仅对本应用程序有效,否则对全体用户有效。
- boolean setWritable(boolean writable, boolean ownerOnly) —— 设置文件的可写属性。
- boolean setExecutable(boolean executable, boolean ownerOnly) —— 设置文件的可执行属性。
- boolean setReadable(boolean readable) —— ownerOnly为true。
- boolean setWritable(boolean writable) —— ownerOnly为true。
- boolean setExecutable(boolean executable) —— ownerOnly为true。
- boolean setReadOnly() —— 设置为只读。
获得可用空间
- 在JDK6.0之前,获取总空间、已用空间、可用存储是很困难的事情,需要借助Java本地调用(JNI)才能做到。
- 在JDK6.0中引入了获取可用空间的3个方法:
- long getTotalSpace() —— 返回当前文件或者目录所在的分区的总存储空间大小,单位为字节。
- long getFreeSpace() —— 返回当前文件或者目录所在的分区的剩余存储空间大小,单位为字节。
- long getUsableSpace() —— 返回当前文件或者目录所在的分区的剩余存储空间大小,单位为字节。此方法将检查写权限和其他操作系统限制,因此与getFreeSpace()相比,此方法更准确。
获得目录占用空间
- 获得目录占用的总空间需要计算目录下,以及目录中的所有子目录下的每个文件的尺寸,需要递归。
- 需要使用File类的方法:
- boolean isFile() —— 判断是否是文件。
- long length() —— 获得文件的大小,单位为字节。
输入输出流
- I/O流的概念
- InputStream
- OutputStream
- Reader
- Writer
- InputStreamReader
- OutputStreamWriter
I/O流概述
- Java为输入输出准备了繁杂的编程接口,结合文件系统和网络通信的知识,可以开发出许许多多精彩纷呈的应用程序。
- Java中主要有4个输入输出的抽象类,InputStream、OutputStream、Reader和Writer。
- 字节流:InputStream和OutputStream。
- 字符流:Reader和Writer。
- ObjectInputStream和ObjectOutputStream分别继承自InputStream和OutputStream,用于从输入流读取Java对象和向输出流中输出Java对象。
- 可以被输入输出的对象必须实现Serializable接口,即实现序列化。
InputStream
- InputStream是一个抽象类,它是所有面向字节的输入流的父类。
- InputStream的主要方法有:
- int read(byte[] b) —— 读取字节到字节数组中,返回读取的字节数。
- int read(byte[] b, int off, int len) —— 读取最多len个字节到字节数组b中,在字节数组b中从 下标off开始存放,返回值是实际读取的字节数。
- void close() —— 关闭输入流,并释放关联的资源。
- long skip(long n) —— 跳过n个字节。
OutputStream
- OutputStream是一个抽象类,它是所有面向字节的输出流的父类。
- OutputStream的主要方法有:
- void write(byte[] b) —— 将字节数组输出到输出流。
- void write(byte[] b, int off, int len) —— 输出字节数组b中从下标off开始的len个字节。
- void close() —— 关闭输出流,并释放关联的资源。
- void flush() —— 刷新输出流,迫使缓冲的字节输出。
Reader
- Reader是一个抽象类,它是所有面向字符的输入流的父类。
- Reader的主要方法有:
- int read(char[] cbuf) —— 将字符读到数组中。
- abstract int read(char[] cbuf, int off, int len) —— 将len长的字符读到数组中下标off开始的数组元素中。
- void close() —— 关闭
Writer
- Writer是一个抽象类,它是所有面向字符的输出流的父类。
- Writer的主要方法有:
- void write(char[] cbuf) —— 输出字符数组。
- void write(char[] cbuf, int off, int len) —— 输出字符数组中指定的字符。
- void write(String str) —— 输出一个字符串。
- void write(String str, int off, int len) —— 输出一个字符串的子串。
- abstract void flush() —— 刷新输出流。
- abstract void close() —— 关闭输出流。
InputStreamReader
- InputStreamReader是从字节流向字符流转化的桥梁。
- InputStreamReader读取字节,使用 特定的字符集解码成字符。
- InputStreamReader的主要方法有:
- InputStreamReader(InputStream in) —— 使用系统默认字符集构建InputStreamReader。
- InputStreamReader(InputStream in, Charset cs) —— 使用给定的字符集构建InputStreamReader。
- InputStreamReader(InputStream in, String charsetName) —— 使用字符集名称构建InputStreamReader。
- int read(char[] cbuf, int offset, int length) —— 读入字符到字符数组中的指定位置。
- String getEncoding() —— 返回字符集的名称。
- void close() —— 关闭输入流。
OutputStreamWriter
- OutputStreamWriter是字符流向字节流转换的桥梁。
- 写入OutputStreamWriter的字符将被使用特定的字符集编码成字节。
- OutputStreamWriter主要的方法有:
- OutputStreamWriter(OutputStream out) —— 创建一个使用默认字符集编码的OutputStreamWriter。
- OutputStreamWriter(OutputStream out, Charset cs) —— 创建指定字符集的OutputStreamWriter。
- OutputStreamWriter(OutputStream out, String charsetName) —— 创建指定字符集名称的OutputStreamWriter。
- void write(char[] cbuf, int off, int len) —— 输出字符数组的一部分。
- void write(String str, int off, int len) —— 输出字符串的一部分。
文件读写
- 文本文件读写
- 二进制文件读写
- 随机文件读写
- 处理压缩文件
文本文件读写
- FileReader —— 从文本文件中读取字符。
- FileWriter —— 创建文本文件并往其中写入字符数据。
- BufferedReader —— 有缓冲,具有readLine()方法,可以对换行符进行鉴别,一行一行地读取。
- BufferedWriter —— 有缓冲,具有newLine()方法,可以写入换行符。
二进制文件的读写
- FileInputStream —— 从二进制文件读入字节流。
- FileOutputStream —— 向二进制文件写入字节流。
- BufferedInputStream —— 有缓冲的字节输入流。
- BufferedOutputStream —— 有缓冲的字节输出流。
随机文件读写
- 随机读写是相对顺序读写而言的,所谓随机读写,是指可以在任何时候将存取文件的指针指向文件内容的任何位置。
- RandomAccessFile —— Java用于支持随机文件读写的类,主要方法有:
- RandomAccessFile(File file, String mode) —— mode为打开方式,"r"为只读,"rw"为读写。
- void setLength(long newLength) —— 设置文件的长度。
- void seek(long pos) —— 设置文件指针的位置,即从文件开始的偏移字节数。
处理压缩文件
- java.util.zip包中提供了一些类,可以处理压缩的输入输出流。
- GZIPOutputStream和ZipOutputStream可分别把数据压缩成GZIP格式和Zip格式。
- GZIPInputStream和ZipInputStream可以分别把压缩成GZIP或Zip的数据解压缩恢复原状。
对象序列化
- 对象序列化是一种使得Java对象能够被存储和被传输的一种机制。
- 基于Java提供的对象输入输出流ObjectInputStream和ObjectOutputStream,可以直接把Java对象作为可存储 、可传输的对象写入文件,在网络上传输。
对象序列化的概念
- Java的对象序列化是一种使得对象能够被存储和被传输的一种机制。
- 支持序列化的类需要实现Serializable接口,这个接口没有任何方法。
- java.io包提供了对象输入输出流ObjectInputStream和ObjectOutputStream,可以直接把Java对象输入输出成字节流。
- 这两个输入输出类不会保存和读取对象中的transient和static类型的变量,这使得我们可以选择性地存储必要的信息。
- JDK提供的大多数类都实现了Serializable接口,这意味着它们的实例都能够被序列化,能够被存储和传输。
- 一个Java类的实例能否被序列化,可以通过JDK的bin目录下的serialver命令来判断,例如serialver java.util.Vector。
- 没有实现Serializable接口的类的实例,不能基于对象输入输出流进行存取,否则将抛出NotSerializableException。
对象序列化的原理
- 通过输出流保存的对象都获得一个序列号。
- 当要保存一个对象时,先检查该对象是否被保存了。
- 如果以前保存过,只需写入“与已经保存的具有序列号的对象相同”的标记;否则,保存该对象。
- Java对象序列化机制专门设计了一个没有定义任何方法的Serializable接口,当一个Java对象实现该接口后,Java将为其提供一个唯一的序列号。
第10章 数据库
- JDBC:Driver、DriverManager、Connection、Statement、ResultSet、PreparedStatement、DatabaseMetaData、ResultSetMetaData。
- 访问数据库实例:查询数据、插入数据、删除数据、更新数据、
带参数的SQL语句、获取元数据。
JDBC
- JDBC简介
- Driver
- DriverManager
- Connection
- Statement
- ResultSet
- PreparedStatement
- DatabaseMetaData
- ResultSetMetaData
JDBC简介
- JDBC是Java Database Connectivity的缩写,在整个Java技术体系中,JDBC的地位相当于Windows的ODBC/ADO、.NET的ADO.NET,职责是为Java应用程序访问数据库提供一种通用的手段。
- JDBC屏蔽了各种数据库管理系统在技术细节方面的差异,为程序的可移植性提供了良好的保证。
- JDBC包含两个包:java.sql和javax.sql,它们提供了Java程序操作数据库的接口和类。
Driver
- Driver是一个接口,数据库驱动程序需要实现Driver接口。
- 程序在连接数据库前,必须先加载特定厂商提供的数据库驱动程序,例如Class.forName("com.mysql.jdbc.Driver")。
- 常用的数据库驱动程序有:
- JDBC-ODBC桥 —— sun.jdbc.odbc.JdbcOdbcDriver
- SQL Server 2000 —— com.microsoft.jdbc.sqlserver.SQLServerDriver
- Oracle —— oracle.jdbc.driver.OracleDriver
- MySql —— com.mysql.jdbc.Driver
DriverManager
- DriverManager类用来建立和数据库的连接,以及管理JDBC驱动程序。
- DriverManager的常用方法有:
- static Connection getConnection(String url, String user, String password) —— 给出用户名和密码,建立数据库连接。
- static Connection getConnection(String url) —— 建立数据库连接,可以在url中给出用户名和密码。
- 各种数据库的连接url是不同的:
- JDBC-ODBC桥 —— jdbc:odbc:[odbcsource]
- SQL Server 2000 —— jdbc:microsoft:sqlserver://127.0.0.1:1433
- Oracle —— jdbc:oracle:thin:@127.0.0.1:1521:[sid]
- MySql —— jdbc:mysql://127.0.0.1:3306/[dbname]?useUnicode=true&characterEncoding=gbk
Connection
- Connection接口代表与数据库的连接,应用程序可以通过Connection创建语句对象,提交事务,回滚事务,获取数据库元数据。
- Connection的常用方法有:
- Statement createStatement() —— 创建一个语句对象,语句对象用于执行SQL语句。
- PreparedStatement prepareStatement(String sql) —— 使用带参数的SQL语句创建预处理语句对象。
- CallableStatement prepareCall(String sql) —— 创建一个用于调用存储过程的CallableStatement对象。
- void setAutoCommit(boolean autoCommit) —— 设置自动提交事务。
- void commit() —— 提交事务。
- void rollback() —— 回滚事务。
- DatabaseMetaData getMetaData() —— 获取数据库元数据。
- void close() —— 关闭数据库连接。
Statement
- Statement提供在底层连接上执行不带参数的SQL语句,并且访问结果。
- Statement常用的方法有:
- ResultSet executeQuery(String sql) —— 执行查询语句返回结果集。
- int executeUpdate(String sql) —— 执行SQL语句,INSERT、UPDATE、DELETE,数据定义语句DDL。
- boolean execute(String sql) —— 执行SQL语句。
- void addBatch(String sql) —— 增加批处理语句。
- int[] executeBatch() —— 执行批处理语句。
- void clearBatch() —— 清楚批处理语句。
- void close() —— 关闭语句对象。
ResultSet
- 在Statement执行SQL语句时,有时会返回结果集ResultSet。
- ResultSet抽象了执行SELECT语句的结果,提供了逐行访问结果的方法,通过它可以访问结果集的不同字段。
- ResultSet的常用方法有:
- boolean next() —— 把当前的指针向下移动一位。
- String getString(int columnIndex) —— 获取指定字段的值,字段下标从“1”开始,返回String类型。
- String getString(String columnLabel) —— 返回指定字段的值,字段通过字段名指定,返回String类型。
- void close() —— 关闭结果集。
- 其他get方法:getFloat()、getDouble()、getDate()、getBoolean()等。
PreparedStatement
- PreparedStatement接口继承自Statement。
- PreparedStatement用于执行预编译的SQL语句,预编译的SQL语句可以多次绑定参数执行,效率较高。
PreparedStatement pstmt = con.prepareStatement(
"UPDATE EMPLOYEES SET SALARY = ? WHERE ID = ?");
pstmt.setBigDecimal(1, 153833.00)
pstmt.setInt(2, 110592)
DatabaseMetaData
- 数据库元数据,通过它可以获取数据库各个方面的信息。
- 元数据包含了数据库的相关信息,例如当前数据库连接的用户名、使用的JDBC驱动程序、数据库允许的最大连接数、数据库的版本等。
ResultSetMetaData
- 结果集元数据,通过它可以获得结果集相关的信息,例如字段个数、字段名、字段类型等。
访问数据库
- 查询数据
- 插入数据
- 删除数据
- 更新数据
- 带参数的SQL语句
- 获取元数据