1、IO流
IO流的分类
- 按照数据的流向
- 输入流:读数据
- 输出流:写数据
- 按照数据类型来分
- 字节流
- 字节输入流
- 字节输出流
- 字节类
- 字节流抽象基类
- nputStream:这个抽象类是表示字节输入流的所有类的超类
- OutputStream:这个抽象类是表示字节输出流的所有类的超类
- 子类名特点:子类名称都是以其父类名作为子类名的后缀
- 使用字节输出流写数据的步骤
- 创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流对象指向文件)
- 调用字节输出流对象的写数据方法
- 释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)
- 字节输入流读取数据的步骤
- 创建字节输入流对象
- 调用字节输入流对象的读数据方法
- 释放资源
- 字节缓冲流
- 字节缓冲流介绍
- lBufferOutputStream:该类实现缓冲输出流.通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用
- lBufferedInputStream:创建BufferedInputStream将创建一个内部缓冲区数组.当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节
- 字节缓冲流介绍
- 字节流抽象基类
- 字符流
- 字符输入流
- Writer: 用于写入字符流的抽象父类
- FileWriter: 用于写入字符流的常用子类
- BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小。默认值足够大,可用于大多数用途
- 字符输出流
- Reader: 用于读取字符流的抽象父类
- FileReader: 用于读取字符流的常用子类
- BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途
- 为什么会出现字符流?
- 字符流的介绍
- 由于字节流操作中文不是特别的方便,所以Java就提供字符流
- 字符流 = 字节流 + 编码表
- 中文的字节存储方式
- 用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,如何识别是中文的呢?
- 汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数
- 字符流的介绍
- 常见的字符集
- ASCII字符集:
- lASCII:是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)
- 基本的ASCII字符集,使用7位表示一个字符,共128字符。ASCII的扩展字符集使用8位表示一个字符,共256字符,方便支持欧洲常用字符。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
- GBXXX字符集:
GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等 - Unicode字符集:
- UTF-8编码:可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码
- 编码规则:
- 128个US-ASCII字符,只需一个字节编码
- 拉丁文等字符,需要二个字节编码
- 大部分常用字(含中文),使用三个字节编码
- 其他极少使用的Unicode辅助字符,使用四字节编码
- ASCII字符集:
- 字符输入流
- 转换流
- InputStreamReader:是从字节流到字符流的桥梁,父类是Reader
- 它读取字节,并使用指定的编码将其解码为字符
- 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
- OutputStreamWriter:是从字符流到字节流的桥梁,父类是Writer
- 是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节
- 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
- InputStreamReader:是从字节流到字符流的桥梁,父类是Reader
- 对象操作流
- 对象序列化介绍
- 对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象
- 这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息
- 字节序列写到文件之后,相当于文件中持久保存了一个对象的信息
- 反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化
- 对象序列化流: ObjectOutputStream
- 将Java对象的原始数据类型和图形写入OutputStream。 可以使用ObjectInputStream读取(重构)对象。 可以通过使用流的文件来实现对象的持久存储。
- 如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象
- 对象反序列化流: ObjectInputStream
- ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象
- serialVersionUID 对象序列化流序列化
- transient 对象中的某个成员变量的值不想被序列化,给该成员变量加transient关键字修饰
- 对象序列化介绍
- 字节流
- IO流的使用场景
- 如果操作的是纯文本文件,优先使用字符流
- 如果操作的是图片、视频、音频等二进制文件,优先使用字节流
- 如果不确定文件类型,优先使用字节流.字节流是万能的流
- File类
方法名 | 说明 |
---|---|
File(String pathname) | 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例 |
File(String parent, String child) | 从父路径名字符串和子路径名字符串创建新的 File实例 |
File(File parent, String child) | 从父抽象路径名和子路径名字符串创建新的 File实例 |
public boolean createNewFile() | 当具有该名称的文件不存在时,创建一个由该抽象路径名命名的新空文件 |
public boolean mkdir() | 创建由此抽象路径名命名的目录 |
public boolean mkdirs() | 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录 |
public boolean delete() | 删除由此抽象路径名表示的文件或目录 |
public boolean isDirectory() | 测试此抽象路径名表示的File是否为目录 |
public boolean isFile() | 测试此抽象路径名表示的File是否为文件 |
public boolean exists() | 测试此抽象路径名表示的File是否存在 |
public String getAbsolutePath() | 返回此抽象路径名的绝对路径名字符串 |
public String getPath() | 将此抽象路径名转换为路径名字符串 |
public String getName() | 返回由此抽象路径名表示的文件或目录的名称 |
public File[] listFiles() | 返回此抽象路径名表示的目录中的文件和目录的File对象数组 |
2、多线程
2.1 实现多线程方式
继承Thread类
- 实现步骤
1. 定义一个类MyThread继承Thread类
2. 在MyThread类中重写run()方法
3. 创建MyThread类的对象
4. 启动线程
实现Runnable接口 - 实现步骤
- 定义一个类MyRunnable实现Runnable接口
- 在MyRunnable类中重写run()方法
- 创建MyRunnable类的对象
- 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
- 启动线程
- 示例:
public class MyRunnable implements Runnable {
@Override
public void run() {
}
}
MyRunnable my = new MyRunnable();
Thread t1 = new Thread(my);
实现Callable接口
- 实现步骤
- 定义一个类MyCallable实现Callable接口
- 在MyCallable类中重写call()方法
- 创建MyCallable类的对象
- 创建Future的实现类FutureTask对象,把MyCallable对象作为构造方法的参数
- 创建Thread类的对象,把FutureTask对象作为构造方法的参数
- 启动线程
- 再调用get方法,就可以获取线程结束之后的结果。
- 示例代码:
//线程开启之后需要执行里面的call方法
MyCallable mc = new MyCallable();
//Thread t1 = new Thread(mc);
//可以获取线程执行完毕之后的结果.也可以作为参数传递给Thread对象
FutureTask<String> ft = new FutureTask<>(mc);
//创建线程对象
Thread t1 = new Thread(ft);
String s = ft.get();
//开启线程
t1.start();
System.out.println(s);
2.2 线程调度
两种调度方式
- 分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
- 抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些
Java使用的是抢占式调度模型
2.3 线程同步
- 同步代码块
synchronized(任意对象) {
多条语句操作共享数据的代码
}
- 同步方法
修饰符 synchronized 返回值类型 方法名(方法参数) {
方法体;
}
- 静态同步方法
修饰符 static synchronized 返回值类型 方法名(方法参数) {
方法体;
}
2.4 死锁
- 生产者和消费者
Object类的等待和唤醒方法
方法名 | 说明 |
---|---|
void wait() | 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法 |
void notify() | 唤醒正在等待对象监视器的单个线程 |
void notifyAll() | 唤醒正在等待对象监视器的所有线程 |
- 阻塞队列
- 常见BlockingQueue:
- ArrayBlockingQueue: 底层是数组,有界
- LinkedBlockingQueue: 底层是链表,无界.但不是真正的无界,最大为int的最大值
- 常见BlockingQueue:
- 线程状态
线程状态 | 具体含义 |
---|---|
NEW | 一个尚未启动的线程的状态。也称之为初始状态、开始状态。线程刚被创建,但是并未启动。还没调用start方法。MyThread t = new MyThread()只有线程象,没有线程特征。 |
RUNNABLE | 当我们调用线程对象的start方法,那么此时线程对象进入了RUNNABLE状态。那么此时才是真正的在JVM进程中创建了一个线程,线程一经启动并不是立即得到执行,线程的运行与否要听令与CPU的调度,那么我们把这个中间状态称之为可执行状态(RUNNABLE)也就是说它具备执行的资格,但是并没有真正的执行起来而是在等待CPU的度。 |
BLOCKED | 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。 |
WAITING | 一个正在等待的线程的状态。也称之为等待状态。造成线程等待的原因有两种,分别是调用Object.wait()、join()方法。处于等待状态的线程,正在等待其他线程去执行一个特定的操作。例如:因为wait()而等待的线程正在等待另一个线程去调用notify()或notifyAll();一个因为join()而等待的线程正在等待另一个线程结束。 |
TIMED_WAITING | 一个在限定时间内等待的线程的状态。也称之为限时等待状态。造成线程限时等待状态的原因有三种,分别是:Thread.sleep(long),Object.wait(long)、join(long)。 |
TERMINATED | 一个完全运行完成的线程的状态。也称之为终止状态、结束状态 |
-
线程池
系统创建一个线程的成本是比较高的,因为它涉及到与操作系统交互,当程序中需要创建大量生存期很短暂的线程时,频繁的创建和销毁线程对系统的资源消耗有可能大于业务处理是对系统资源的消耗,这样就有点"舍本逐末"了。针对这一种情况,为了提高性能,我们就可以采用线程池。线程池在启动的时,会创建大量空闲线程,当我们向线程池提交任务的时,线程池就会启动一个线程来执行该任务。等待任务执行完毕以后,线程并不会死亡,而是再次返回到线程池中称为空闲状态。等待下一次任务的执行。
- 线程池使用
-
Executors
- static ExecutorService newCachedThreadPool() 创建一个默认的线程池
- static newFixedThreadPool(int nThreads) 创建一个指定最多线程数量的线程池
-
ThreadPoolExecutor
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略); public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler); corePoolSize: 核心线程的最大值,不能小于0 maximumPoolSize:最大线程数,不能小于等于0,maximumPoolSize >= corePoolSize keepAliveTime: 空闲线程最大存活时间,不能小于0 unit: 时间单位 workQueue: 任务队列,不能为null threadFactory: 创建线程工厂,不能为null handler: 任务的拒绝策略,不能为null 任务拒绝策略 ThreadPoolExecutor.AbortPolicy: 丢弃任务并抛出RejectedExecutionException异常。是默认的策略。 ThreadPoolExecutor.DiscardPolicy: 丢弃任务,但是不抛出异常 这是不推荐的做法。 ThreadPoolExecutor.DiscardOldestPolicy: 抛弃队列中等待最久的任务 然后把当前任务加入队列中。 ThreadPoolExecutor.CallerRunsPolicy: 调用任务的run()方法绕过线程池直接执行。
-
- 线程池使用
-
原子性
-
volatile:强制线程每次在使用的时候,都会看一下共享区域最新的值
解决问题:当A线程修改了共享数据时,B线程没有及时获取到最新的值,如果还在使用原先的值,就会出现问题 1. 堆内存是唯一的,每一个线程都有自己的线程栈。 2. 每一个线程在使用堆里面变量的时候,都会先拷贝一份到变量的副本中。 3. 在线程中,每一次使用是从变量的副本中获取的。
-
synchronized
synchronized解决:1. 线程获得锁 2. 清空变量副本 3. 拷贝共享变量最新的值到变量副本中 4. 执行代码 5. 将修改后变量副本中的值赋值给共享数据 6. 释放锁
-
原子性
-
概述 :
所谓的原子性是指在一次操作或者多次操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断,要么所有的操作都不执行,多个操作是一个不可以分割的整体。 volatile关键字不能保证原子性 count++ 不是一个原子性操作, 他在执行的过程中,有可能被其他线程打断
-
原子更新基本类型
- AtomicBoolean: 原子更新布尔类型
- AtomicInteger: 原子更新整型
- 原理 :自旋锁 + CAS 算法
- CAS算法:
- 有3个操作数(内存值V, 旧的预期值A,要修改的值B)
- 当旧的预期值A == 内存值 此时修改成功,将V改为B
- 当旧的预期值A!=内存值 此时修改失败,不做任何操作
- 并重新获取现在的最新值(这个重新获取的动作就是自旋)
- AtomicLong: 原子更新长整型
-
原子更新数组
-
原子更新引用
-
原子更新属性
-
synchronized和CAS的区别 :
相同点: 在多线程情况下,都可以保证共享数据的安全性。
不同点:
1. synchronized总是从最坏的角度出发,认为每次获取数据的时候,别人都有可能修改。所以在每次操作共享数据之前,都会上锁。(悲观锁)
2. cas是从乐观的角度出发,假设每次获取数据别人都不会修改,所以不会上锁。只不过在修改共享数据的时候,会检查一下,别人有没有修改过这个数据。
3. 如果别人修改过,那么我再次获取现在最新的值。
4. 如果别人没有修改过,那么我现在直接修改共享数据的值.(乐观锁)
-
-
3、并发工具类
Hashtable
Hashtable出现的原因 :在集合类中HashMap是比较常用的集合对象,但是HashMap是线程不安全的(多线程环境下可能会存在问题)。为了保证数据的安全性我们可以使用Hashtable,但是Hashtable的效率低下。
ConcurrentHashMap
ConcurrentHashMap出现的原因 :在集合类中HashMap是比较常用的集合对象,但是HashMap是线程不安全的(多线程环境下可能会存在问题)。为了保证数据的安全性我们可以使用Hashtable,但是Hashtable的效率低下。基于以上两个原因我们可以使用JDK1.5以后所提供的ConcurrentHashMap。
底层原理:
- 如果使用空参构造创建ConcurrentHashMap对象,则什么事情都不做。在第一次添加元素的时候创建哈希表
- 计算当前元素应存入的索引。
- 如果该索引位置为null,则利用cas算法,将本结点添加到数组中。
- 如果该索引位置不为null,则利用volatile关键字获得当前位置最新的结点地址,挂在他下面,变成链表。
- 当链表的长度大于等于8时,自动转换成红黑树6,以链表或者红黑树头结点为锁对象,配合悲观锁保证多线程操作集合时数据的安全性
CountDownLatch
使用场景: 让某一条线程等待其他线程执行完毕之后再执行
方法 | 解释 |
---|---|
public CountDownLatch(int count) | 参数传递线程数,表示等待线程数量 |
public void await() | 让线程等待 |
public void countDown() | 当前线程执行完毕 |
Semaphore
使用场景: 可以控制访问特定资源的线程数量。
总结 :
- HashMap是线程不安全的。多线程环境下会有数据安全问题
- Hashtable是线程安全的,但是会将整张表锁起来,效率低下
- ConcurrentHashMap也是线程安全的,效率较高。 在JDK7和JDK8中,底层原理不一样。
4、网络编程
端口
设备上应用程序的唯一标识
端口号
用两个字节表示的整数,它的取值范围是0~65535。其中,0~1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败
协议
计算机网络中,连接和通信的规则被称为网络通信协议
UDP
-
UDP协议
用户数据报协议(User Datagram Protocol)UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。 由于使用UDP协议消耗系统资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输。 例如视频会议通常采用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议。
-
UDP发送数据
UDP协议是一种不可靠的网络协议,它在通信的两端各建立一个Socket对象,但是这两个Socket只是发送,接收数据的对象,因此对于基于UDP协议的通信双方而言,没有所谓的客户端和服务器的概念 Java提供了DatagramSocket类作为基于UDP协议的Socket
-
UDP三种通讯方式
- 单播
单播用于两个主机之间的端对端通信 - 组播
组播用于对一组特定的主机进行通信 - 广播
广播用于一个主机对整个局域网上所有主机上的数据通信
- 单播
TCP
-
TCP协议
传输控制协议 (Transmission Control Protocol)TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手” 三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠 第一次握手,客户端向服务器端发出连接请求,等待服务器确认 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求 第三次握手,客户端再次向服务器端发送确认信息,确认连接 完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛。例如上传文件、下载文件、浏览网页等 四次挥手 第一次挥手,客户端向服务端发出取消连接请求 第二次挥手,服务端向客户端返回一个响应,表示收到客户端取消请求 第三次挥手,服务端向客户端发出确认取消消息 第四次挥手,客户端再次发送确认消息,连接取消
-
TCP通讯
Java对基于TCP协议的的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信。 Java为客户端提供了Socket类,为服务器端提供了ServerSocket类
NIO、BIO、阻塞IO、非阻塞IO
- BIO
阻塞型IO、面向流,数据传输是单向的 - NIO
- 非阻塞型IO、面向缓冲区,缓冲区是双向的
- 缓冲区
用来存储数据 - 通道
用来建立连接和传输数据 - 选择器
- 监视通道状态、连接和数据
- 选择器对象
- Selector 选择器对象
- SelectionKey 绑定的key
- SelectableChannel 选择器通道
- SocketChannel
- ServerSocketChannel
HTTP协议
- 协议版本
HTTP1.0: 每次请求和响应都需要建立一个单独的连接
HTTP1.1:支持长连接 - 请求信息
- 请求行
请求方式 GET
URL 请求资源路径,如果省略就是默认
协议版本 HTTP1.1 - 请求头
- Host 用来指定请求的服务端地址
- Connection 取值为keep-alive表示需要持久连接
- User-Agent 客户端的信息
- Accept 指定客户端能够接收的内容类型
- Accept-Encoding 指定浏览器可以支持的服务器返回内容压缩编码类型
- Accept-Language 浏览器可接受的语言
- 请求空行 空行
- 请求体 GET没有请求体
- 请求行
- 响应信息
- 响应行
- 协议版本 HTTP1.1
- 响应状态码 200,404,500
- 状态信息 状态码对应信息
- 响应头
- Content-Type
- text/html 文本类型
- image/png png格式文件
- image/jpeg jpg格式文件
- Content-Type
- 响应空行 空行
- 响应体
- 响应行
5、JVM
5.1 类加载器
- 类加载的过程
- 类加载时机
- 创建类的实例(对象)
- 调用类的类方法
- 访问类或者接口的类变量,或者为该类变量赋值
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
- 类加载过程
-
加载
通过包名 + 类名,获取这个类,准备用流进行传输 在这个类加载到内存中 加载完毕创建一个class对象
-
链接
-
验证
确保Class文件字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全(文件中的信息是否符合虚拟机规范有没有安全隐患)
-
准备
负责为类的类变量(被static修饰的变量)分配内存,并设置默认初始化值(初始化静态变量)
-
解析
将类的二进制数据流中的符号引用替换为直接引用(本类中如果用到了其他类,此时就需要找到对应的类)
-
-
初始化
根据程序员通过程序制定的主观计划去初始化类变量和其他资源(静态变量赋值以及初始化其他资源)
-
- 类加载时机
- 类加载的分类
- 分类
- Bootstrap class loader:虚拟机的内置类加载器,通常表示为null ,并且没有父null
- Platform class loader:平台类加载器,负责加载JDK中一些特殊的模块
- System class loader:系统类加载器,负责加载用户类路径上所指定的类库
- 类加载器的继承关系
- System的父加载器为Platform
- Platform的父加载器为Bootstrap
- 分类
- 双亲委派模型
-
介绍
如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式
-
5.2 反射
-
介绍
是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能够调用它的任意属性和方法; 这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。
-
获取Class类对象的三种方式
- 类名.class属性
- 对象名.getClass()方法
- Class.forName(全类名)方法
-
获取里面的构造方法对象
- getConstructor (Class<?>… parameterTypes)
- getDeclaredConstructor (Class<?>… parameterTypes)
-
如果是public的,直接创建对象
- newInstance(Object… initargs)
-
如果是非public的,需要临时取消检查,然后再创建对象
- setAccessible(boolean) 暴力反射
-
获取成员变量
方法名 | 说明 |
---|---|
Field[] getFields() | 返回所有公共成员变量对象的数组 |
Field[] getDeclaredFields() | 返回所有成员变量对象的数组 |
Field getField(String name) | 返回单个公共成员变量对象 |
Field getDeclaredField(String name) | 返回单个成员变量对象 |
- 获取成员方法
方法名 | 说明 |
---|---|
Method[] getMethods() | 返回所有公共成员方法对象的数组,包括继承的 |
Method[] getDeclaredMethods() | 返回所有成员方法对象的数组,不包括继承的 |
Method getMethod(String name, Class<?>… parameterTypes) | 返回单个公共成员方法对象 |
Method getDeclaredMethod(String name, Class<?>… parameterTypes) | 返回单个成员方法对象 |
5.3 http服务器
- 通过反射和配置文件优化
- 把Servlet信息写到properties配置文件中
格式为:servlet-info=/servlet/UserServlet,全类名;/servlet/loginServlet,全类名 - 定义一个接口ServletConcurrentHashMap,接口中定义ConcurrentHashMap,该集合存储所有的servlet信息
- 定义一个接口ParseServletConfig,该接口中定义一个方法(parse)
- 定义ParseServletConfig的实现类,解析配置文件,并把配置文件中Servlet信息存到map集合中
- 在main方法的第一行,开启一条线程执行解析配置文件的代码
- 把Servlet信息写到properties配置文件中
6、XML
6.1 语法规则
-
文档声明必须是第一行第一列
- <?xml version="1.0" encoding="UTF-8" standalone="yes”?>
version:该属性是必须存在的 encoding:该属性不是必须的 打开当前xml文件的时候应该是使用什么字符编码表(一般取值都是UTF-8) standalone: 该属性不是必须的,描述XML文件是否依赖其他的xml文件,取值为yes/no
- <?xml version="1.0" encoding="UTF-8" standalone="yes”?>
-
必须存在一个根标签,有且只能有一个
-
XML文件中可以定义注释信息
<!--注释的内容-->
-
XML文件中可以存在CDATA区
6.2 xml解析
- Node对象
- Document对象:整个xml文档
- Element对象:所有标签
- Attribute对象:所有属性
- Text对象:所有文本内容
6.3 DTD约束
- 什么是约束
用来限定xml文件中可使用的标签以及属性 - 约束的分类
DTD
schema - 编写DTD约束
-
步骤
- 创建一个文件,这个文件的后缀名为.dtd
- 看xml文件中使用了哪些元素
<!ELEMENT> 可以定义元素 - 判断元素是简单元素还是复杂元素
简单元素:没有子元素。
复杂元素:有子元素的元素;
-
代码
<!ELEMENT persons (person)> <!ELEMENT person (name,age)> <!ELEMENT name (#PCDATA)> <!ELEMENT age (#PCDATA)>
-
- 引入DTD约束
-
引入本地dtd
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE persons SYSTEM 'persondtd.dtd'>
-
在xml文件内部引入
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE persons [ <!ELEMENT persons (person)> <!ELEMENT person (name,age)> <!ELEMENT name (#PCDATA)> <!ELEMENT age (#PCDATA)> ]>
-
引入网络dtd
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE persons PUBLIC "dtd文件的名称" "dtd文档的URL">
-
- DTD语法
- 定义元素
-
定义一个元素的格式为:<!ELEMENT 元素名 元素类型>
-
简单元素:
EMPTY: 表示标签体为空 ANY: 表示标签体可以为空也可以不为空 PCDATA: 表示该元素的内容部分为字符串 <!ELEMENT age (#PCDATA)>
-
复杂元素:
直接写子元素名称. 多个子元素可以使用","或者"|"隔开; ","表示定义子元素的顺序 ; "|": 表示子元素只能出现任意一个 "?"零次或一次, "+"一次或多次, "*"零次或多次;如果不写则表示出现一次 <!ELEMENT persons (person+)>
-
- 定义属性
-
格式:<!ATTLIST 元素名称 属性名称 属性的类型 属性的约束>
-
类型:
CDATA类型:普通的字符串 -
属性的约束:
// #REQUIRED: 必须的 // #IMPLIED: 属性不是必需的 // #FIXED value:属性值是固定的 例:<!ATTLIST person id CDATA #REQUIRED>
-
- 定义元素
6.4 schema约束
- schema和dtd的区别
- schema约束文件也是一个xml文件,符合xml的语法,这个文件的后缀名.xsd
- 一个xml中可以引用多个schema约束文件,多个schema使用名称空间区分(名称空间类似于java包名)
- dtd里面元素类型的取值比较单一常见的是PCDATA类型,但是在schema里面可以支持很多个数据类型
- schema 语法更加的复杂
- 编写schema约束
- 步骤
- 创建一个文件,这个文件的后缀名为.xsd。
- 定义文档声明
- schema文件的根标签为:
- 在中定义属性:
xmlns=http://www.w3.org/2001/XMLSchema - 在中定义属性 :
targetNamespace =唯一的url地址,指定当前这个schema文件的名称空间。 - 在中定义属性 :
elementFormDefault="qualified“,表示当前schema文件是一个质量良好的文件。 - 通过element定义元素
- 判断当前元素是简单元素还是复杂元素
- 步骤
- 引入schema约束
- 步骤
- 在根标签上定义属性xmlns=“http://www.w3.org/2001/XMLSchema-instance”
- 通过xmlns引入约束文件的名称空间
- 给某一个xmlns属性添加一个标识,用于区分不同的名称空间
格式为: xmlns:标识=“名称空间地址” ,标识可以是任意的,但是一般取值都是xsi - 通过xsi:schemaLocation指定名称空间所对应的约束文件路径
格式为:xsi:schemaLocation = “名称空间url 文件路径”
- 步骤
7、日志
- 日志的级别
- DEBUG < INFO < WARN < ERROR < FATAL