字符集
字符集基础
-
一堆字符的集合,包含很多字符,并且每个字符都有一个数字编号与之对应
-
常见字符集有:ASCII字符集,GBK字符集,Unicode字符集等
-
计算机根据字符集,可对字符进行编码,以便计算机识别和存储各种文字
常用字符集
ASCII字符集
-
美国信息交换标准代码,包括了数字,英文,符号
-
使用一个自己存储一个字符,一个字节是8位,共可以表示128个字符信息,对英文数字来说是够用的
GBK
-
中国的码表,包含几万个汉字等字符
-
在gbk中,英文字符占一个字节,中文字符占两个字节
Unicode字符集
-
统一码,也叫万国码,是计算机科学领域的一项业界标准,包括字符集,编码方案等,Unicode是为了解决传统字符编码方案的局限产生的
-
utf-8是Unicode一种常见编码方式,以单个字节为单位对Unicode中字符进行编码
编码注意
-
utf-8编码后,一个中文一般以三个字节的形式存储,英文与数字字符以一个字节保存,utf-8也兼容ASCII编码表
-
技术人员都应该使用utf-8的字符集编码
注意
-
字符解码时使用的字符集和编码时使用的字符集必须一致,否则会出现乱码
-
英文和数字一般不会乱码(gbk和utf-8都兼容ASCII)
API
String编码
byte[] getBytes()
- 使用平台的默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中
byte[] getBytes(String charsetName)
- 使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
String解码
String (byte[] bytes)
- 通过使用平台的默认字符集解码指定的字节数组来构造新的String
String (byte[] bytes , String charsetName)
- 通过指定的字符集解码指定的字节数组来构造新的String
IO流概述
用来读写数据的(数据持久化保存)
-
I表示input,把硬盘文件中的数据读到内存的过程,称之输入,负责读
-
O表示output,把内存中的数据写出到硬盘文件的过程,称之输出,负责写
按流的方向分
- 输入流
- 输出流
按流操作数据的单位分
-
字节流
操作所有类型的文件(包括音频视频图片) -
字符流
只能操作纯文本文件(包括java文件,txt文件等)
IO流体系
字节流
-
输入流 InputStream
-
输出流 OutputStream
字符流
-
输入流 Reader
-
输出流 Writer
字节流
文件字节输入流:FileInputStream
作用
以内存为基准,把磁盘文件中的数据以字节形式读取到内存中去
构造器
public FileInputStream(File file)
创建字节输入流管道与源文件对象接通
public FileInputStream(String pathname)
创建字节输入流管道与源文件路径接通
方法名称
public int read()
每次读取一个字节返回,如果字节已经没有可读的返回-1,性能较慢
public int read(byte[] buffer)
每次使用字节数组来读取数据,返回读取的字节个数,如果没有可读返回-1,读取的性能得到提升
一次读取全部字节
-
定义一个字节数组与文件一样大,然后使用读取字节数组的方法,一次性读取完成
public int read(byte[] buffer)
每次读取一个字节数组返回 -
官方为字节输入流InputStream提供了api可以直接把文件的全部数据读取到一个字节数组中
public byte[] readAllBytes() throws IOException
直接将当前字节输入流对应的文件对象的字节数据装到一个字节数组返回 -
一次读取全部字节可能内存溢出!!!
文件字节输出流:FlieOutputStream
作用
以内存为基准,把内存中的数据以字节的形式写出到磁盘文件中去的流
构造器
public FileOutputStream(File file)
创捷字节输出流管道与源文件对象接通
public FileOutputStream(File file,boolean append)
创建字节输出流管道与源文件对象接通,可追加数据
public FileOutputStream(String filepath)
创建字节输出流管道与源文件路径接通
public FileOutputStream(String filepath,boolean append)
创建字节输出流管道与源文件路径接通,可追加数据
写数据api
public void write(int a)
写一个字节出去
public void write(byte[] buffer)
写一个字节数组出去
public void write(byte[] buffer,int pos,int len)
写一个字节数组的一部分出去
流的关闭与刷新
flush()
刷新流,还可以继续写数据
close()
关闭流,释放资源,但是在关闭之前会先刷新流,一旦关闭,就不能再写数据
文件拷贝
任何文件的底层都是字节,拷贝是一字不漏的转移字节,没有任何问题
资源释放的方式
try-catch-finally
-
finally:放在try-catch后面,无论是正常执行还是异常执行代码,最后一定要执行,除非jvm退出
-
作用:一般用于进行最后的资源释放操作(专业级做法)
-
格式
try{
FileOutputStream fos = new FileOutputStream("a.txt");
fos.write(97);
fos.close();
} catch (IOException e) {
e.printStackTrace();
} finally {...}
finally虽然可以用于释放资源,但释放资源的代码过于繁琐
jdk7/9改进,后期流的操作有专门工具类,了解
字符流
文件字符输入流:Reader
作用
- 以内存为基准,把磁盘文件中的数据以字符的形式读取到内存中
- 更适合读写中文,最小单位是字符
构造器
public FileReader(File file)
创建字符输入流管道与源文件对象接通
public FileReader(String pathname)
创建字符输入流管道与源文件路径接通
方法名称
public int read()
每次读取一个字符返回,如果没有读到,返回-1
特点
读取中文字符不会出现乱码(如果代码文件编码一致),性能较慢
文件字符输入流:FileReader
作用
以内存为基准,把磁盘文件中的数据以字符的形式读取到内存中去
方法名称
public int read()
每次读取一个字符返回,如果字符已经没有可读的返回-1
public int read(char[] buffer)
每次读取一个字符数组,返回读取的字符数,如果字符已经没有可读的返回-1,读取的性能得到提升
文件字符输出流:FileWriter
作用
以内存为基准,把内存中的数据以字符的形式写出到磁盘文件中去的流
构造器
public FileWriter(File file)
创建字符输出流管道与源文件对象接通
public FileWriter(File file , boolean append)
创建字符输出流管道与源文件对象接通,可追加数据
public FileWriter(String filepath)
创建字符输出流管道与源文件路径接通
public FileWriter(String filepath, boolean append)
创建字符输出流管道与源文件路径接通,可追加数据
写数据出去的api
void write(int c)
写一个字符
void write(char[] cbuf)
写一个字符数组
void write(char[] cbuf,int off ,int len)
写入字符数组的一部分
void write(String str)
写一个字符串
void write(String str, int off ,int len)
写一个字符串的一部分
流的关闭与刷新
flush()
刷新流,还可以继续写数据
close()
关闭流,释放资源,但是在关闭之前会先刷新流,一旦关闭,就不能再写数据
字符输出流实现换行
os.write("\r\n")
缓冲流
概述
-
也称高效流或高级流,之前学习的文件字节/字符流可以称为原始流/基础流
缓冲流=基础流+缓冲区 -
作用:可以提高原始字节流,字符流读写数据的性能
缓冲流可以减少磁盘打内存的操作次数
字节缓冲流性能优化原理
-
字节缓冲输入流自带至少8kb缓冲数组,可以减少磁盘读数据到内存的次数,提高读的效率
-
自己缓冲输出流自带至少8kb缓冲数组,可以减少内存写数据到磁盘的次数,提高写的效率
字节缓冲流
字节缓冲输入流:BufferedInputStream
提高字节输入流读取数据性能,读数据api并无变化
字节缓冲输出流:BufferedOutputStream
提高字节输出流写出数据的性能,写数据的api并无变化
构造器
public BufferedInputStream(InputStream is)
可以把低级的字节输入流包装成一个高级的缓冲字节输入流管道,从而提高字节输入流读数据的性能
public BufferedOutputStream(OutputStream os)
可以把低级的字节输出流包装成一个高级的缓冲字节输出流,从而提高写数据的性能
建议使用字节缓冲输入流,字节缓冲输出流,结合字节数组的方式,提高读写性能
字符缓冲流
字符缓冲输入流
BufferedReader
缓冲流=基础流+缓冲区
作用
提高原始字符输入流读数据的性能,此外还提供了按行读取数据的功能
构造器
public BufferedReader(Reader r)
可以把低级的字符输入流包装成一个高级缓冲字符输入流管道,从而提高读数据性能
新增功能
public String readLine()
读取一行数据返回,如果读取没有完毕,无行可读返回null
字符缓冲输出流
BufferedWriter
缓冲流=基础流+缓冲区
作用
提高字符输出流去数据性能,此外多了换行功能
构造器
public BufferedWriter(Writer w)
可以把低级的字符输出流包装成一个高级的缓冲字符输出流管道,从而提高写数据性能
新增功能
public void newLine()
换行操作
转换流
字符输入转换流:InputStreamReader
-
可以把原始的字节流按照指定编码转换成字符输入流
-
构造器
public InputStreamReader(InputStream is)
把原始的字节流按照代码默认编码转换成字符输入流
public InputStreamReader(InputStream is , String charset)
把原始的字节流按照指定编码转换成字符输入流
字符输出转换流:OutputStreamWriter
-
字符流通往字节流的桥梁
-
构造器
public OutputStreamWriter(OutputStream os)
把原始的字节输出流按照默认编码转换成字符输出流
public OutputStreamWriter(OutputStream os, String charset)
把原始的字节输出流按照指定编码转换成字符输出流 -
扩展:JDK11之后的新构造
FileReader fr = new FileReader("a.txt",Charset.forName("gbk"));
FileWriter fw = new FileWriter("b.txt",Charset.forName("utf-8"));
序列化流
对象序列化
使用到的流是对象字节输出流
ObjectOutputStream
作用
以内存为基准,把内存中的对象存储到磁盘文件中去,称为对象序列化
构造器
public ObjectOutputStream(OutputStream out)
把低级字节输出流包装成高级的对象字节输出
序列化方法
public final void writeObject(Object obj)
把对象写出到对象序列化流的文件中去
序列化对象的要求
必须实现序列化接口Serializable
对象反序列化
使用到的流是对象字节输入流
ObjectInputStream
作用
以内存为基准,把存储到磁盘文件中去的对象数据恢复成内存中的对象,称为对象反序列化
构造器
public ObjectInputStream(InputStream out)
把低级字节输入流包装成高级对象字节输入流
序列化方法
public Object readObject()
把存储到磁盘文件中的对象数据恢复成内存中的对象返回
序列化多个对象
使用集合保存,序列化整个集合
打印流
作用
- 打印流可以实现方便,高效的打印数据到文件
- 可以实现打印什么数据就是什么数据
打印流一般指PrintStream,PrintWriter两个类
PrintStream
构造器
public PrintStream(OutputStream os)
打印流直接通向字节流输出流管道
public PrintStream(File f)
打印流直接通向文件对象
public PrintStream(String filepath)
打印流直接通向文件路径
方法
public void print(XXX xx)
打印任意类型的数据出去
PrintWriter
构造器
public PrintWriter(OutputStream os)
打印流直接通向字节输出流管道
public PrintWriter(Writer w)
打印流直接通向字符输出流管道
public PrintWriter(File f)
打印流直接通向文件对象
public PrintWriter(String filepath)
打印流直接通向文件路径
方法
public void print(XXX xx
)
打印任意类型的数据出去
PrintStream和PrintWriter的区别
-
打印数据功能上是一模一样的,都是使用方便,性能高效
-
PrintStream继承自字节输出流OutputStream,支持写字节数据的write(int)方法
-
PrintWriter继承自字符输出流Writer,支持写字符数据的writer(String)
输出语句的重定向
属于打印流的一种应用,可以把输出语句的打印位置改到文件
PrintStream ps=new PrintStream("文件地址");
System.setOut(ps);
Properties
就是一个Map集合,但一般不会当集合使用,因为HashMap更好用
核心作用
读取key=value格式的配置文件,然后保存到Properties集合中
属性文件
后缀是.properties结尾的文件,里面的内容都是key=value,后续做系统配置信息的
API
构造器
void load(InputStream inStream)
从输入字节流读取属性列表(键和元素对)
void load(Reader reader)
(最常用)
从输入字符流读取属性列表(键和元素对)
public Object setProperty(String key,String value)
保存键值对(put)
public String getProperty(String key)
使用此属性列表中指定的键搜索属性值(get)
public Set<String> stringPropertyNames()
所有键的名称的集合(keySet())
IO框架
commons-io概述
-
是apache开源基金组织提供的一组有关IO操作的类库,可以提高IO功能开发的效率
-
工具包提供了很多有关io操作的类,主要的两个是FileUtils,IOUtils
FileUtils主要方法
static String readFileToString(File file,String encoding)
读取文件中的数据,返回字符串
static void copyFile(File srcFile,File destFile)
复制文件
static void copyDirectoryToDirectory(File srcDir , File destDir)
复制文件夹
导入commons-io-2.6.jar
-
在项目中创建一个文件夹:lib
-
将commons-io-2.6.jar文件复制到lib文件夹
-
在jar文件上点右键,选择Add as Library -> 点击OK
-
在类中导包使用
多线程
概念
线程(thread)是一个程序内部的一条执行路径
- main方法的执行其实就是一条单独的执行路径
- 程序中如果只有一条执行路径,那么程序就是单线程程序
多线程是指从软硬件上实现多条执行流程的技术,多线程技术可以充分发挥多核CPU的优势,同时完成几件事而互不干扰
多线程的创建
继承Thread类
- Java是通过java.lang.Thread类来代表线程的.按照面向对象的思想,Thread类应该提供了实现多线程的方式
步骤
-
定义一个子类MyThread继承线程类java.lang.Thread 重写run()方法
-
创建MyThread类的对象
-
调用子类对象的start()方法启动线程(线程启动后会自动执行run方法)
优缺点
-
优点
编码简单 -
缺点
线程类已经继承Thread,无法继承其他类,不利于扩展
实现Runnable接口
步骤
-
定义一个实现类MyRunnable实现Runnable接口,重写run()方法
-
创建实现类对象
-
new Thread(实现类对象)
可以匿名内部类形式 -
调用线程对象的start()方法启动线程
构造器
public Thread(String name)
可以为当前线程指定名称
public Thread(Runnable target)
封装Runnable对象成为线程对象
public Thread(Runnable target,String name)
封装Runnable对象成为线程对象,并指定线程名称
优缺点
-
优点
线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强 -
缺点
编程多一层对象包装,如果线程有执行结果是不可以直接返回的
JDK 5.0新增,实现Callable接口
- 利用Callable,FutureTask接口实现
步骤
- 得到任务对象
- 定义类实现Callable接口,重写call方法,封装要做的事情
- 用FutureTask把Callable对象封装成线程任务对象
-
把线程任务对象交给Thread处理
-
调用Thread的start方法启动线程,执行任务
-
线程执行完毕后,通过FutureTask方法去获取任务执行的结果
方法
public FutureTask<>(Callable call)
把Callable对象封装成FutureTask对象
public V get() throws Exception
获取线程执行call方法返回的结果
优缺点
优点
- 线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强
- 可以获取线程执行的结果
缺点
编码复杂一点
为什么不直接调用run方法而是调用start
- 直接调用run方法会当成普通方法执行,此时相当于还是单线程执行
- 只有调用start方法才是启动一个新的线程执行
Thread的常用方法
构造器
public Thread(String name)
可以为当前线程指定名称
public Thread(Runnable target,String name)
封装Runnable对象成为线程对象,并指定线程名称
方法
String getName()
获取当前线程的名称,默认线程名称是Thread-索引
void setName(String name)
设置线程名称
public static Thread currentThread()
返回对当前正在执行的线程对象的引用
静态方法,可以直接使用Thread类调用
public static void sleep(long time)
让线程休眠指定的时间,单位为毫秒
public void run()
线程任务方法
public void start()
线程启动方法
线程安全
概念
多个线程同时操作一个共享资源的时候可能会出现业务安全问题,成为线程安问题
原因
-
多线程环境
-
同时访问共享资源
-
多条语句访问共享资源
让多条语句变成一个整体可以解决安全问题
线程同步
核心思想
-
加锁,把共享资源进行上锁,每次只能一个线程进入访问,完毕以后再解锁,其他线程才能进来
-
将访问共享资源的多条语句变成一个整体,一次只让一个线程执行
同步代码块
作用
把出现线程安全问题的核心代码给上锁
原理
每次只能一个线程进入,执行完毕后自动解锁,其他线程才可以进来执行
格式
synchronized(同步锁对象){
//操作共享资源的核心代码
}
锁对象要求
理论上
- 任意一个对象都可以作为锁对象(锁对象必须多个线程共享一个)
规范上
- 建议使用共享资源作为锁对象
- 对于实例方法可以使用this作为锁对象(注意保证唯一性)
同步方法
作用
把出现线程安全问题的核心方法给上锁
原理
每次只能一个线程进入,执行完毕之后自动解锁,其他线程才可以进来执行
格式
修饰符 synchronized 返回值类型 方法名称(形参列表){
//操作共享资源的代码
}
底层原理
- 隐式锁对象的,只是锁的范围是整个方法代码
- 如果方法是实例方法,同步方法默认用this作为的锁对象
- 如果方法是静态方法,同步静态方法默认用类名.class作为的锁对象
Lock锁
- 为了清晰表达如何加锁解锁,JDK5以后提供一个新的锁对象Lock
- Lock实现提供比使用synchronized方法和语句可以更广泛的锁定操作
- Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来构建Lock锁对象
方法
public ReentrantLock()
获得Lock锁的实现类对象
void lock()
获得锁
void unlock()
释放锁