IO
概述和分类
IO流用来处理设备间数据的传输问题(文件复制、上传、下载),注意IO流的input和output都是相对于内存来说的内存都是句子的主语,input是内存从磁盘中读,output是内存向磁盘里写
-
IO流的分类
-
按照数据的流向
- 输入流:读数据
- 输出流:写数据
-
按照数据类型来分
-
字节流
- 字节输入流
- 字节输出流
-
字符流
- 字符输入流
- 字符输出流
-
-
-
IO流的使用场景
- 如果操作的是纯文本文件,优先使用字符流
- 如果操作的是图片、视频、音频等二进制文件,优先使用字节流
- 如果不确定文件类型,优先使用字节流.字节流是万能的流
-
使用注意事项
- 使用OutPut流的话,如果代码中的文件地址不存在,会在磁盘中创建此文件,如果文件已经存在,在进行写入操作之前会清空文件中的内容
编码表
-
什么是字符集
是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
l计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBXXX字符集、Unicode字符集等
-
常见的字符集
-
ASCII字符集:
lASCII:是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)
基本的ASCII字符集,使用7位表示一个字符,共128字符。ASCII的扩展字符集使用8位表示一个字符,共256字符,方便支持欧洲常用字符。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
-
GBXXX字符集:
GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等
-
Unicode字符集:
UTF-8编码:可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用 中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码
编码规则:
128个US-ASCII字符,只需一个字节编码
拉丁文等字符,需要二个字节编码
大部分常用字(含中文),使用三个字节编码
其他极少使用的Unicode辅助字符,使用四字节编码
-
-
编码与解码
unicode是万国码,支持多国的语言,UTF-8并不是码表,而是一种编码规则。对于一段字符串,首先在unicode码表中找到对应的数字,然后根据utf-8编码规则转变为二进制数据存储在计算机中
- 编码
byte[] getBytes() 使用平台的默认字符集将该 String编码为一系列字节 byte[] getBytes(String charsetName) 使用指定的字符集将该 String编码为一系列字节 - 解码
String(byte[] bytes) 使用平台的默认字符集解码指定的字节数组来创建字符串 String(byte[] bytes, String charsetName) 通过指定的字符集解码指定的字节数组来创建字符串
字节流
-
字节流抽象基类
- InputStream:这个抽象类是表示字节输入流的所有类的超类
- OutputStream:这个抽象类是表示字节输出流的所有类的超类
- 子类名特点:子类名称都是以其父类名作为子类名的后缀
-
字节输出流
-
换行 "\r\n"
-
FileOutputStream(String name):创建文件输出流以指定的名称写入文件
- 写数据的方法分类 | 方法名 | 说明 | | -------------------------------------- | -------------------------------------------------- | | void write(int b) | 将指定的字节写入此文件输出流 一次写一个字节数据 | | void write(byte[] b) | 将 b.length字节从指定的字节数组写入此文件输出流 一次写一个字节数组数据 | | void write(byte[] b, int off, int len) | 将 len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流 一次写一个字节数组的部分数据 |
-
FileOutputStream(String name,boolean append)第二个参数如果传入true,就可以实现在原本的文本的基础上的追加操作,而不是清空覆盖
-
为什么会出现中文乱码
已知GBK编码中两个字节表示一个中文字符,unicode编码中三个字节表示一个中文字符。中文字符相应的字节都是负数。当数据从磁盘中读取到内存中时,会一个个进行扫描,扫描到第一个负数,表示这是一个中文字符的起始,如果一开始是gbk编码,那么一个字符被编码成三个字节,解码的时候必须将三个字节合并解析,但是如果编码解码使用的不是同一套规则,解码的时候将两个字节合并解析,那么最后识别出来的就是乱码
字节缓冲流
-
构造方法:
方法名 说明 BufferedOutputStream(OutputStream out) 创建字节缓冲输出流对象 BufferedInputStream(InputStream in) 创建字节缓冲输入流对象
字节缓冲流本质是给字节流添加了一个容量很大的字节数组,每次依然是由字节流完成从数据源中的数据读取操作,读取到的大量数据都存入到缓冲流中,然后由字节流从数组中一个个的读取byte结果
下图展示了如果字符缓冲流接收的是数组对象,底层的原理。实际上一次IO会利用字节流从磁盘中读取8192个字节大小的数据,然后在内存中,通过长度为1024的字节数组将数据从缓冲输入流数组转移到缓冲输出流数组中,最终通过write方法将字节缓冲输出流中的数据写入到硬盘中
字符流
多线程
多线程的认识
并行/并发
进程/线程
创建线程
线程状态
| 线程状态 | 具体含义 |
|---|---|
| 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 | 一个完全运行完成的线程的状态。也称之为终止状态、结束状态 |
线程优先级
守护线程
线程同步
Synchronized
Lock
生产者消费者案例
线程池
频繁的创建和销毁线程对系统资源的消耗可能大于业务处理对系统的消耗,为了提高性能,可以创建线程池来重复利用线程池中的线程。
Executors默认线程池
企业开发中很少自定义线程池,而是使用JDK中自带的线程池。可以使用Executors提供的静态方法来创建线程池。
-
Executors.newCachedThreadPool() 创建一个默认线程池,初始容量为空,最大容量是int类型的最大值
-
Executors.newFixedThreadPool(int n) 创建指定线程数量的线程池
ThreadPoolExecutor线程池
可以看到Executors静态方法的底层创建的是ThreadPoolExecutor对象
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
- corePoolSize:最大核心线程数(核心线程一经创建不会被销毁),不能小于0
- maximumPoolSize:最大线程数,大于0
- keepAliveTime:临时线程最大存活时间
- unit:临时线程存活时间的时间单位
- workQueue:任务队列,没有线程处理的任务会被放到任务队列中
- ThreadFactory:线程工厂(按照默认的方式创建线程对象)
- RejectedExecutionHandler:任务的拒绝策略
- ThreadPoolExecutor.AbortPolicy: 丢弃任务并抛出RejectedExecutionException异常。是默认的策略。
- ThreadPoolExecutor.DiscardPolicy: 丢弃任务,但是不抛出异常 这是不推荐的做法。
- ThreadPoolExecutor.DiscardOldestPolicy: 抛弃队列中等待最久的任务 然后把当前任务加入队列中。
- ThreadPoolExecutor.CallerRunsPolicy: 调用任务的run()方法绕过线程池直接执行。
网络编程
在网络通信协议下,不同计算机上运行的程序,可以实现数据传输。
网络编程三要素
IP地址
要想让网络中的计算机能够互相通信,必须为每台计算机指定一个标识号,通过这个标识号来指定要接收数据的计算机和识别发送的计算机,而IP地址就是这个标识号。也就是设备的标识
- IPv4:给每个连接在网络上的主机分配一个32bit地址。按照TCP/IP规定,IP地址用二进制来表示,每个IP地址长32bit,也就是4个字节。例如一个采用二进制形式的IP地址是“11000000 10101000 00000001 01000010”,这么长的地址,处理起来也太费劲了。为了方便使用,IP地址经常被写成十进制的形式,中间使用符号“.”分隔不同的字节。于是,上面的IP地址可以表示为“192.168.1.66”。IP地址的这种表示法叫做“点分十进制表示法”,这显然比1和0容易记忆得多
- IPv6:为了扩大地址空间,通过IPv6重新定义地址空间,采用128位地址长度,每16个bit一组,分成8组十六进制数,这样就解决了网络地址资源数量不够的问题
端口
网络的通信,本质上是两个应用程序的通信。
每台计算机都有很多的应用程序,那么在网络通信时,如何区分这些应用程序呢?如果说IP地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的应用程序了。也就是应用程序的标识
端口号:两个字节表示的整数,它的取值范围是0 ~ 65535。其中,0~1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败
协议
通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样。在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。常见的协议有UDP协议和TCP协议
UDP通信程序
发送数据
-
构造方法
方法名 说明 DatagramSocket() 创建数据报套接字并将其绑定到本机地址上的任何可用端口 DatagramPacket(byte[] buf,int len,InetAddress add,int port) 创建数据包,发送长度为len的数据包到指定主机的指定端口 -
相关方法
方法名 说明 void send(DatagramPacket p) 发送数据报包 void close() 关闭数据报套接字 void receive(DatagramPacket p) 从此套接字接受数据报包
接收数据
-
构造方法
方法名 说明 DatagramPacket(byte[] buf, int len) 创建一个DatagramPacket用于接收长度为len的数据包 -
相关方法
方法名 说明 byte[] getData() 返回数据缓冲区 int getLength() 返回要发送的数据的长度或接收的数据的长度
TCP通信程序
类加载
类加载的时机
用到的时候就加载,不用不加载
- 创建类的实例(对象)
- 调用类的类方法
- 访问类或者接口的类变量,或者为该类变量赋值
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
类加载的过程
加载
- 通过包名 + 类名,获取这个类,准备用流进行传输(通过类的全限定类名获取定义此类的二进制字节流)
- 在这个类加载到内存中(将这个字节流所代表的静态存储结构转化为运行时数据结构)
- 加载完毕创建一个class对象(在内存中生成一个代表这个类的java.lang.class对象,注意任何一个类在被使用时,系统都会为之创建一个java.lang.class对象)
链接
验证
确保class字节流中的信息符合虚拟机规范
准备
为类变量分配内存并设置默认初始化值
解析
将类的二进制数据中的符号引用替换为直接引用(如果本类中用到了其他的类,在一开始将本类加载到虚拟机中的时候,并不清楚本类的其他类是否被加载到虚拟机中,所以类中的其它类会先用符号引用标识,在执行到解析的时候,会将符号引用转变为其他类在虚拟中的地址,也就是直接引用)
初始化
给静态变量赋值以及初始化其他资源
类加载器
分类
- 启动类加载器:虚拟机内置的类加载器
- 平台类加载器:负责加载JDK中一些特殊的模块
- 系统类加载器:负责加载用户类路径上所指定的类库
双亲委派模型
反射
利用反射可以无视修饰符获取类里面所有的属性和方法。
先获取配置文件中的信息,动态获取信息并创建对象和调用方法
如果想要调用其他类的study方法,不需要修改源代码通过new创建对象,只需要修改prop文件中的className
反射获取类对象
- 类名.class
- 对象名.getClass()
- Class.forName(全类名)
反射获取构造方法
| 方法名 | 说明 |
|---|---|
| Constructor<?>[] getConstructors() | 返回所有公共构造方法对象的数组 |
| Constructor<?>[] getDeclaredConstructors() | 返回所有构造方法对象的数组 |
| Constructor getConstructor(Class<?>... parameterTypes) | 返回单个公共构造方法对象 |
| Constructor getDeclaredConstructor(Class<?>... parameterTypes) | 返回单个构造方法对象 |
注意单个构造方法需要传入和构造器一样的参数列表,基本数据类型的变量传入的也是类变量
反射创建对象
- 方法介绍
| 方法名 | 说明 |
|---|---|
| T newInstance(Object...initargs) | 根据指定的构造方法创建对象 |
| setAccessible(boolean flag) | 设置为true,表示取消访问检查 |
反射获取成员变量
- 方法分类
| 方法名 | 说明 |
|---|---|
| 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) | 返回单个成员方法对象 |