一、I/O
1.I/O:Input/Output
2.为啥需要I/O
1)在操作系统中,一切需要永久保存的数据都以文件的形式存储。
需要长久保存的文件数据,存储在外部设备。(out)
2)但是,要显示或运行这些数据,必须将其读取到内存(input)
3)同时,内存的大小有限,因此常常需要在内存和外设之间交换数据,即I/O
3.流 有不同的类型
1)按照数据流向
a. 输出流 内存 - -> 外设
b. 输入流 外设 – > 内存
输入和输出:是以内存为基准来说的
2)按照流中,流动的数据类型来分
a. 字节流:流动的数据的类型是以字节为单位的二进制0,1数据
b. 字符流:流动的是以字符为单位的字符数据
4.何时使用字节流,何时使用字符流?
1)字符流专门用来传输文本数据。简单来说文本数据,
就是用文本编辑器打开的时候,我们人可以看懂的数据。
2)字节流可以用来传输一切类型数据。只不过针对文本(字符)数据,
在某些情况下,字节流操作起来不太方便,除了文本数据之外的其他数据,都是用字节流
3)当我们不确定所要传输的数据类型的时候,一律使用字节流
注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀
如:InputStream的子类FileInputStream
如:Reader的子类FileReader
二、字节流写数据
1.需求
* 通过OutputStream对象,完成向文本文件中输出“hello world”
*
* 但是注意到,
* 1. OutputStream是抽象类,如果要使用OutputStream对象,只能使用其子类对象,完成写入功能。
* 2. 虽然写入的数据是文本数据,但是还没学字符流,所以先使用字节流。
2.FileOutputStream的构造方法
* 创建一个FileOutputStream对象,
* 该对象完成,向由 参数name(路径名字符串)指定的目标文件写入数据的功能
* FileOutputStream(String name)
*
* 创建一个FileOutputStream对象,
* 该对象完成,向由 参数file(File对象)指定的目标文件,中写入数据的功能
* FileOutputStream(File file)
3.write方法
* public void write(int b)
* 1.将指定的字节写入此输出流。
* 2. write的常规协定是:
* 向输出流写入一个字节。要写入的字节是参数b的八个低位。b 的 24 个高位将被忽略。
*
* public void write(byte[] b)
* 将 b.length 个字节从指定的 byte 数组写入此输出流
*
* public void write(byte[] b, int off, int len)
* 将指定 byte 数组从偏移量 off 开始的 len 个字节写入此输出流
* b - 数据
* off - 数据中的初始偏移量
* len - 要写入的字节数
4.FileOutputStream使用常见问题
* 字节流写数据常见问题:
* 1. 创建文件字节输出流到底做了哪些事情?
* 1). 创建FileOutputStream对象的时候,jvm首先到操作系统,查找目标文件
* a. 当发现目标文件不存在的时候,jvm会首先创建该目标文件(内容为空)
* b. 当发现目标文件存在的时候,jvm默认会首先清空目标文件内容,做好准备
* 让,FileOutputStream,从文件头,开始写入数据
* 2). 在内存中,创建FileOutputStream对象
* 3). 在FileOutputStream对象和目标文件之间建立数据传输通道
*
* 2. 数据写成功后,为什么要close()?
* 1). 关闭此输出流并
* 2). 释放与此流有关的所有系统资源
* close的常规协定是:该方法将关闭输出流。关闭的流不能执行输出操作,也不能重新打开。
*
* 3. 如何实现数据的换行?
* 利用换行符,实现换行写入
*
* 换行符,在不同的操作系统中,表示方式是不一样的:
* 类unix操作系统中:'\n'
* windows默认的换行表示:'\r' + '\n' 用windows自带的文本编辑器对于\n是否有换行效果,还和操作系统版本有关
*
* 4. 如何实现数据的追加写入?
* 1) 在同一个流对象上,多次调用write方法,向一个文件多次写数据
* 这不叫追加写入,这叫多次写入。
* 2) 追加写入,是指在不同流对象上,调用write方法,向同一个文件中写入数据
* 这才叫追加写入
*
* 所谓追加写入,将写的内容追加到文件的末尾
* 实现文件的追加写入,在创建FileOutputStream对象的时候,使用新的构造方法:
*
* FileOutputStream(String name, boolean append)
* 创建一个向具有指定 name 的文件中写入数据的输出文件流。
* append - 如果为 true,则将字节写入文件末尾处,而不是写入文件开始处。
*
* FileOutputStream(File file, boolean append)
* 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
* append - 如果为 true,则将字节写入文件末尾处,而不是写入文件开始处。
*
* 5. 给I/O流操作加上异常处理
5.FileInputStream
* 需求:使用InputStream从该文本中,读出刚刚写入的内容,并输出在控制台。
* 同时,注意到InputStream是抽象类,不能直接实例化,需要使用其子类对象来实现功能
*
* FileInputStream的构造方法
* // 创建一个从目标文件中,以字节为单位,读取数据到内存的,文件字节输入流对象
* // 读取数据的目标文件,由参数对象file来指定
* a. FileInputStream(File file)
*
* // 创建一个从目标文件中,以字节为单位,读取数据到内存的,文件字节输入流对象
// 读取数据的目标文件,由参数name,文件的路径名字符串来指定
* b. FileInputStream(String name)
*
* 在创建FileInputStream对象的时候,jvm又做了哪些工作?
* 1. FileInputStream对象被创建之前,jvm会首先到操作系统中,找目标文件
* a. 找到,就不做任何额外工作
* b. 找不到,则直接抛出异常FileNotFoundException
* 2. 在jvm内存中,创建FileInputStream对象
* 3. 在FileInputStream对象和目标文件之间,建立数据传输通道
6.read方法
* int read()
* 从输入流中读取数据的下一个字节
* 返回值:
* a. 从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值
* b. 如果因为已经到达流末尾而没有可用的字节, 则返回值 -1
*
* int read(byte[] b)
* 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中
* 返回值:
* a.读入缓冲区的总字节数;
* b.如果因为已经到达流末尾而不再有数据可用,则返回-1
*
* int read(byte[] b, int off, int len)
* 将输入流中最多 len 个数据字节读入 byte 数组。尝试读取 len 个字节,但读取的字节也可能小于该值
*
* 参数:
* b -- 读入数据的缓冲区
* off -- 数组 b 中将写入数据的初始偏移量
* len -- 要读取的最大字节数
* 返回:
* 读入缓冲区的总字节数;如果因为已经到达流末尾而不再有数据可用,则返回-1。
7.字节流复制数据练习
package com.cskaoyan;
import java.io.*;
/*
* 字节流复制数据练习
* 把d:\\a.txt内容复制到e:\\b.txt中
* 把e:\\**.jpg内容复制到当前项目目录下的mn.jpg中
*
* */
public class Exercise {
public static void main(String[] args) throws IOException {
String srcPath = "d:\\a.txt";
String destPath = "e:\\b.txt";
// 第一个练习
//copyFileByBytes(srcPath, destPath);
//copyFileBySingleByte(srcPath, destPath);
// 第二个练习
srcPath = "C:\\Users\\ncut919\\Pictures\\Saved Pictures\\头像.jpg";
destPath = "mn.jpg";
//copyFileByBytes(srcPath, destPath);
copyFileBySingleByte(srcPath, destPath);
// 一次复制一个字节
}
// 一次复制多个字节
private static void copyFileByBytes(String srcFilePath, String destFilePath) {
InputStream in = null;
OutputStream out = null;
try {
// 创建输入流对象
in = new FileInputStream(srcFilePath);
out = new FileOutputStream(destFilePath);
// 准备字节数组缓冲区
byte[] bytes = new byte[2048];
// 存储read方法的返回值,记录实际读取到的字节个数
int len;
while((len = in.read(bytes)) != -1) {
// 将读取到的字节数据,通过输出流,写入目标文件
//out.write(bytes);
out.write(bytes, 0 , len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 文件复制完之后,关闭流,并释放资源
closeQuietly(out);
closeQuietly(in);
}
}
// 一次复制一个字节
public static void copyFileBySingleByte(String srcFilePath, String destFilePath)
throws IOException {
// 创建一个输入流对象
InputStream in = new FileInputStream(srcFilePath);
OutputStream out = new FileOutputStream(destFilePath);
// 开始复制,一次复制一个字节
int readByte;
while ((readByte = in.read()) != -1) {
out.write(readByte);
}
// 关闭流,释放资源
in.close();
out.close();
}
// 专门用来关闭流
private static void closeQuietly(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
文件复制的思路
8.字节流文件复制两种方式比较:
9.字节缓冲流
* 字节缓冲流
* 字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,
* 这是加入了数组这样的缓冲区效果,java本身在设计的时候,也考虑到了这样的情况,所以提供了字节缓冲区流
*
* 缓冲流:
* 字节缓冲输出流
* BufferedOutputStream
*
* BufferedOutputStream(OutputStream out)
* 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
*
* 字节缓冲输入流
* BufferedInputStream
*
* BufferedInputStream(InputStream in)
* 创建一个BufferedInputStream 并保存其参数,即输入流in, 以便将来使用。
*
* 缓冲流:要创建缓冲流对象,必须在构造方法中,接收一个普通的相应类型输入/输出字节流对象,即
* 缓冲流,是基于一个已有的底层字节流,创建出来的。
* a. 看起来缓冲流,是通过包装了一个底层普通字节流,得到的,这种流,我们统称为包装流
* b. 在关闭流的时候,只需要关闭,包装流,不需要关闭包装流所包装的底层流,因为
* 包装流自己会负责自己所包装的底层流的关闭
*
* 使用缓冲流,来实现文件复制的功能
*
* */
public class Demo1 {
public static void main(String[] args) throws IOException {
// 创建缓冲字节输入流对象
FileInputStream fis = new FileInputStream("d:\\a.txt");
InputStream in = new BufferedInputStream(fis);
// 缓冲字节输出流对象
FileOutputStream fos = new FileOutputStream("e:\\b.txt");
OutputStream out = new BufferedOutputStream(fos);
// 文件的复制(一次复制一个字节数组)
byte[] byteBuf = new byte[1024];
int len;
while((len = in.read(byteBuf)) != -1) {
out.write(byteBuf, 0, len);
}
// 关闭流(只需要关闭包装流本身即可,因为包装流会负责关闭它所包装的底层流)
in.close();
out.close();
}
}
10.缓冲流原理
* 缓冲流的注意事项:
* 1. 当使用缓冲字节输出流的write方法,写数据到外部设备的时候
* 2. 当我们写入少量数据的时候(缓冲区数据还没满),默认情况下,数据是不会写入外部设备
* 3. 如果我们希望,把缓冲区的数据,强行写入底层流,从而写入外部设备
* a. 缓冲流对象上调用.flush
* public void flush()
* 刷新此缓冲的输出流。这迫使所有缓冲的输出字节被写出到底层输出流中。
* b. 缓冲流对象.close
* public void close()
* 关闭此输出流并释放与此流有关的所有系统资源
* FilterOutputStream 的 close 方法先调用其 flush 方法,然后调用其基础输出流的 close 方法
三、字符流
1.引入
* 昨天我们已经,学习了字节流,下面我们首先通过字节流,完成如下功能:
* 1.向文本中写入数字或英文字符,然后将其读入内存,并在控制台上显示//hello, chujiu123
* 2.向文本中写入中文字符,将其读入内存,并在控制台上显示 //hello, chujiu123ä½ å¥½
*
* 是否两次都能正确的显示呢?
* 不包含中文字符时可以,包含中文字符就不能正确显示(出现了乱码)
* 原因是:一个英文或者数字字符'a' '1' 都对应由一个字节表示整数值
* 但是一个中文字符,对应整数值,是多个字节来表示的
*
* 通过刚刚的例子,我们可以得出一个结论:
* 在某些情况下,用字节流来操作中文不太方便。
* 核心原因在于:数据单位不一致
* 既然核心原因已经找到,那如何解决这个问题呢?
* 很显然,我们需要流中,数据的逻辑单位不再是字节数据而是字符。因此,就产生了字符流
*
* 由此我们就可以看到,字节流和字符流的区别:
* 两种流中数据的逻辑单位不同!!
2.编码表
3.常见字符集的介绍
* 首先,一定要牢记,编解码操作只针对字符或文本数据
* 对于byte,short,char,int,long,float等非字符相关数据都没有编解码
*
* 在java语言层面(针对字符串):
* a. 编码 字符 ---> 编码值
* byte[] bytes = String对象.getBytes("字符集的名称")
* b. 解码 已知编码值 ---> 字符
* String s = new String(bytes, 0, bytes.length, "字符集的名称");
*
* 如果在我们自己进行编解码的时候,没有指定编解码基于的字符集,那么这个字符集是哪个?
* a. 在IDEA中,(一般)默认情况下,如果没有设置所用的字符集的话,默认使用的是utf-8
* b. 在没有经过任何集成开发环境设置的情况下,即原生情况下,
* jdk中的默认字符集和当前操作系统使用的默认字符集(在简体中文操作系统各种gbk)
*
* 那么,当我们Coder自己在编解码的时候,如果没有指定所使用的字符集时,
* jdk在编解码时默认使用的字符集,即所谓的默认字符集
*
* 关于中文编码的常识:
* 1. GBK字符集中:用两个字节编码一个字符
* 2. utf-8字符集中:习惯上我们说3个字节编码一个字符(实际上有3个字节,也有4个字节编码的中文字符)
4.字符流的本质,转化字符输出流及其write方法
1)字符流的本质
通过编码表相关知识我们可以得出以下结论:
字符的存储和传输天然与二进制数据密切相关
字符流需要在二进制的基础上,添加基于特定编码表的字符编解码
因此我们得出结论:
字符流 = 字节流 + 编码表(根据指定编码表,编解码的过程)
2)转化字符输出流
* 接下来,我们利用Writer向文本中写入中文字符串。
* 但是,考虑到Writer是抽象类,无法直接实例化,于是我们使用其子类OutputStreamWriter。
*
* OutputStreamWriter的构造方法:
*
* // 包装一个字节流对象,创建出一个字符流对象(把一个字节流对象转换成了一个字符流对象)
* // 该构造方法,没有指定编码所使用的字符集,其实编码使用的就是默认字符集
* public OutputStreamWriter(OutputStream out)
*
* // 字符流 = 字节流 + 编码表
* // 包装一个字节流对象(并且指定编码所使用的字符集),创建出一个字符流对象(把一个字节流对象转换成了一个字符流对象)
* public OutputStreamWriter(OutputStream out, String charsetName)
3)write方法
package com.cskaoyan.transfer.writer;
import java.io.*;
Writer写字符数据的方法:
* 1.public void write(int c)
* a. 写入单个字符。
* b. 要写入的字符包含在给定整数值的 16 个低位中,16 高位被忽略。
*
* 2.public void write(char[] cbuf)
* 写入字符数组,将字符数组cbuf所有的字符数据,一次性写入底层流,进行数据传输
*
* 3.public abstract void write(char[] cbuf, int off, int len)
写入字符数组的某一部分。
参数:
cbuf - 字符数组
off - 开始写入字符处的偏移量
len - 要写入的字符数
* 4.public void write(String str)
* 写入字符串。将字符串对应的整个字符序列,一次性写入底层流
*
* 5.public void write(String str, int off, int len)
* 写入字符串的某一部分。
参数:
str - 字符串
off - 相对初始写入字符的偏移量
len - 要写入的字符数
public class Demo2 {
public static void main(String[] args) throws IOException {
// 1. 创建流对象
FileOutputStream fos = new FileOutputStream("a.txt");
Writer writer = new OutputStreamWriter(fos);
String data= "你好,通行百万";
char[] chars = data.toCharArray(); // 该方法可直接把一个字符串转化成一个字符数组
// 2. 向文本文件中写入字符数据
// a. public void write(int c)
/*for (int i = 0; i < data.length(); i++) {
writer.write(data.charAt(i));
}*/
// b. public void write(char[] cbuf)
/*writer.write(chars);*/
// c. public abstract void write(char[] cbuf, int off, int len)
/*writer.write(chars, 3, 4);*/
// d. public void write(String str)
/*writer.write(data);*/
// e. public void write(String str, int off, int len)
writer.write(data, 0, 3);
// 关闭流
writer.close();
}
}
* 所有的字符流,自带(小)缓冲区,是在编解码的时候来使用的
* 1. 所以,当我们向字符流写入少量数据的时候,数据有可能存在于自带的缓冲区中,还没有写入底层流
* 2. 为了保证,即使是少量字符数据,也能及时将少量字符数据写入,底层字节流
* a. close() 关闭此流,但要先刷新它
* b. flush() 刷新该流的缓冲
5.转化字符输入流及其read方法,以及文件读写乱码问题
1)转化字符输入流
package com.cskaoyan.transfer.reader;
import java.io.*;
* 接着,我们使用Reader,将刚刚写入文本的中文数据,读取到内存中并显示
* 但是考虑到,Reader是抽象类,我们只能使用其子类InputStreamReader
*
* InputStreamReader的构造方法:
* 通过包装一个底层的字节输入流,创建出一个字符输入流(即与默认字符集进行解码)
* public InputStreamReader(InputStream in)
*
* 通过包装一个底层的字节输入流,创建出一个字符输入流(基于指定名称的字符集进行解码)
* public InputStreamReader(InputStream in, String charsetName)
public class Demo1 {
public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
FileInputStream fis = new FileInputStream("a.txt");
// public InputStreamReader(InputStream in)
Reader reader1 = new InputStreamReader(fis);
// public InputStreamReader(InputStream in, String charsetName)
Reader reader2 = new InputStreamReader(fis, "gbk");
}
}
2)read方法
package com.cskaoyan.transfer.reader;
import java.io.*;
* Reader读数据的方法:
* 1.public int read()
* a. 读取单个字符
* b. 返回:
* 作为整数读取的字符,范围在 0 到 65535 之间 (0x00-0xffff)
* 如果已到达流的末尾,则返回 -1
* 2.public int read(char[] cbuf)
* a. 将(字符流中,读取的多个)字符读入字符数组
* b. 返回:
* 读取的字符数
* 如果已到达流的末尾,则返回 -1
* 3.public int read(char[] cbuf, int offset, int length)
* 将字符读入数组中的某一部分。
* 参数:
cbuf - 目标缓冲区
offset - 开始存储字符处的偏移量
length - 要读取的最多字符数
* 返回:
读取的字符数,如果已到达流的末尾,则返回 -1
public class Demo2 {
public static void main(String[] args) throws IOException {
// 1. 创建流对象
FileInputStream fis = new FileInputStream("a.txt");
Reader reader = new InputStreamReader(fis);
// 2. 从目标文件中,以字符为单位读取数据
// public int read()
/*int readChar;
while ((readChar = reader.read()) != -1) {
char c = (char) readChar;
System.out.print(c);
}
System.out.println();*/
// public int read(char[] cbuf)
// 习惯上给1024的整数倍的大小
char[] charBuf = new char[1024];
/*int len;
while((len = reader.read(charBuf)) != -1) {
String s = new String(charBuf, 0, len);
System.out.println(s);
}
System.out.println();*/
// public int read(char[] cbuf, int offset, int length)
int off = 5;
int len = reader.read(charBuf, off, charBuf.length - off);
System.out.println(new String(charBuf, off, len));
// 关闭流
reader.close();
}
}
3)文件读写乱码问题
package com.cskaoyan.transfer.writer;
import java.io.*;
* 在使用转化流的时候可能出现的两种乱码情况:
* a. 文本文件本身所使用的字符集和字符输入流解码所使用的字符集不一致
* b. 字符输出流编码和字符输入流解码时,所使用的字符集不一致
public class Note {
public static void main(String[] args) throws IOException {
// 第一种情况的乱码问题
//first();
// 第二种情况的乱码问题
second();
}
private static void second() throws IOException {
Writer writer = new OutputStreamWriter(new FileOutputStream("e:\\case2.txt"), "gbk");
writer.write("你好字符流");
writer.close();
Reader reader = new InputStreamReader(new FileInputStream("e:\\case2.txt"), "utf-8");
char[] chars = new char[1024];
int len = reader.read(chars);
System.out.println(new String(chars, 0, len));
reader.close();
}
private static void first() throws IOException {
// 创建流对象
Reader reader = new InputStreamReader(new FileInputStream("e:\\case1.txt"), "gbk");
// 读取字符数据
char[] charBuf = new char[1024];
int len = reader.read(charBuf);
System.out.println(new String(charBuf, 0, len));
reader.close();
}
}
6.文件字符流以及缓冲流的使用
1)文件字符流
package com.cskaoyan.transfer.simplify;
import java.io.*;
* 在使用转化流的时候,通常需要2步:
* 创建底层的字节流
* 包装字节流,指定字符集并完成转换流的创建
*
* 稍显麻烦,而大多数时候,我们所依赖的字符集都是本地字符集,
* 所以,为了简化我们的书写,转换流提供了对应的子类。
*
* 构造方法
* FileWriter
* FileWriter(String fileName)
* FileWriter(String fileName, boolean append)
*
* FileReader
* FileReader(String fileName)
* FileReader(File file)
*
* FileWriter
* FileReader
* FileWriter和FileReader实现文本文件的复制
*
* FileReader & FileWriter VS 转化流:
* 1. 转化流,创建对象麻烦,但是可以指定,编解码使用的字符集
* 2. FileReader & FileWriter,创建对象简单,但是,我们无法指定,其编解码使用的字符集,即只能使用默认的字符集来编解码
public class Demo1 {
public static void main(String[] args) throws IOException {
// 对比两种流代码的书写(FileWriter & FileReader书写更简单)
//compare();
// 使用文件字符输入/输出流 实现文件复制的功能
// 1. 创建流对象
FileReader fileReader = new FileReader("a.txt");
FileWriter fileWriter = new FileWriter("b.txt");
// 2. 复制文件
char[] charBuf = new char[1024];
int len;
while ((len = fileReader.read(charBuf)) != -1) {
fileWriter.write(charBuf, 0, len);
}
// 关闭流
fileReader.close();
fileWriter.close();
}
private static void compare() {
// 创建操作文件数据的转化字符输出流对象
//OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream("a.txt"));
// 创建FileWriter对象
//FileWriter fileWriter = new FileWriter("a.txt");
}
}
2)字符缓冲流
package com.cskaoyan.transfer.buffer;
import java.io.*;
* 1. 同在字节流中引入缓冲流的原因相同,出于效率的考虑,在字符流中,我们同样引入缓冲流。
* 2. 缓冲流中,定义了自己独有的方法(Reader和Writer中没有的方法)
*
* 缓冲字符流的构造方法:
* BufferedWriter(Writer out)
* 创建一个使用默认大小输出缓冲区的缓冲字符输出流。
*
* BufferedReader(Reader in)
创建一个使用默认大小输入缓冲区的缓冲字符输入流。
* 缓冲流独有的:
* BufferedWriter
* void newLine()
* 写入一个行分隔符。
* 行分隔符字符串由系统属性 line.separator 定义,并且不一定是单个新行 ('\n') 符。
*
* BufferedReader
* String readLine()
* 读取一个文本行。
* 通过下列字符之一即可认为某行已终止:换行 ('\n')、回车 ('\r') 或回车后直接跟着换行。
* 返回:
* 包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
*
* 练习:利用缓冲字符流,实现按行复制文本的功能
public class Demo1 {
public static void main(String[] args) throws IOException {
// 创建缓冲字符流对象
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("a.txt")));
//BufferedReader br2 = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("c.txt")));
// 按行复制文本数据
String lineStr;
while ((lineStr = br.readLine()) != null) {
// 直接向输出流中写入一行文本的内容
bw.write(lineStr);
// 在该行内容的末尾,写入换行符
bw.newLine();
}
// 关闭流(关闭最上层的包装流即可)
br.close();
bw.close();
}
}
四、StringBuffer
StringBuffer
线程安全(多线程环境下访问数据的正确性)的可变字符序列。
一个类似于 String 的字符串缓冲区,但可以修改(内容可变)
虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的 长度 和 内容。
构造方法:
public StringBuffer()
构造一个其中不带字符的字符串缓冲区,其初始容量为 16 个字符。
public StringBuffer(int capacity)
构造一个不带字符,但具有指定初始容量的字符串缓冲区。
public StringBuffer(String str)
构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容。
添加功能
不管任何数据类型都可以被添加到字符缓冲区中:
1. 如果该类型不是字符串类型 该值 先转成 字符串类型
2. 再放入字符缓冲区中
public StringBuffer append(String str) --向字符缓冲区中添加字符序列
public StringBuffer insert(int offset, String str)
虽然说,StringBuffer可以自动扩容,但是通常再开发中建议
public StringBuffer(int capacity),
如果在开发中,能有效的预估字符缓冲区所需的长度,因为每一次扩容,其实都比较耗时
删除功能
public StringBuffer deleteCharAt(int index)
public StringBuffer delete(int start, int end) [start, end)
替换功能
用所给字符串,替换掉字符缓冲区中,指定范围的字符串序列
public StringBuffer replace(int start, int end, String str)
反转功能
public StringBuffer reverse() 反转字符缓冲区中的字符序列
截取功能
public String substring(int start)
public String substring(int start, int end) [start, end)
截取功能和前面几个功能的不同:
返回值类型是String类型,本身并没有发生改变
练习
练习:
String和StringBuffer的相互转换
1.把数组拼接成一个字符串
2.把字符串反转
3.判断一个字符串是否是对称字符串
例如"abc"不是对称字符串,"aba"、"abba"、"aaa"、”mnanm"是对称字符串
public class Exercise {
public static void main(String[] args) {
int[] a = {1, 2, 3, 4};
//exercise1(a);
String s = "1234";
//exercise2(s);
exercise3("abbac");
}
// 1.把数组拼接成一个字符串 {1,2,3}
public static void exercise1(int[] a) {
StringBuffer buffer = new StringBuffer("{");
for (int i = 0; i < a.length; i++) {
int value = a[i];
buffer.append(value).append(',');
}
buffer.deleteCharAt(buffer.length() - 1);
buffer.append("}");
System.out.println(buffer.toString());
}
// 2.把字符串反转
public static void exercise2(String s) {
StringBuffer buffer = new StringBuffer();
// insert(0) "abc"
// "a" -> "ba" -> "cba"
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
buffer.insert(0, c);
}
System.out.println(buffer.toString());
}
// 3.判断一个字符串是否是对称字符串
public static void exercise3(String s) {
StringBuffer stringBuffer = new StringBuffer(s);
StringBuffer reverse = stringBuffer.reverse();
System.out.println(reverse.toString().equals(s));
}
}
从 JDK 5 开始,为该类(StringBuffer)补充了一个单个线程使用的等价类,即 StringBuilder
StringBuilder针对单线程运行环境,它的api和StringBuffer几乎没有差别
VS
StringBuffer 针对多线程运行环境
Date
类 Date 表示特定的瞬间,精确到毫秒
Date()
分配 Date 对象并初始化此对象,以表示分配它的时间(精确到毫秒)。
Date(long date)
分配 Date 对象并初始化此对象,
以表示自从标准基准时间(称为“历元(epoch)”,
即 1970 年 1 月 1 日 00:00:00 GMT)以来的指定毫秒数。
long date 表示从 The epoch 开始,经过的毫秒数
getTime()
返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。
我们看到的时间是
2020-01-10 17:00:00
DateFormat 类概述
1. 是日期/时间 格式化 子类的抽象类
2. 它以与语言无关的方式格式化并解析日期或时间
3. 因为是抽象类,所以实际使用的是SimpleDateFormat这个实现子类
y 年
M 表示年中的月份
d 表示月份中的天数
H 一天中的小时数
m 小时中的分钟数
s 分钟中的秒数
2020-01-10 17:06:00
yyyy-MM-dd HH:mm:ss
public Date parse(String source) 把一个用字符串表示的时间转化成一个Date对象,
该对象表示的时间点,就是你用字符串表示的那个时间点
public final String format(Date date) 把一个Date对象表示成一个指定格式的表示时间的字符串
Math
成员方法
public static int abs(int a) 求绝对值。
public static double ceil(double a) 取整 向大的方向
public static double floor(double a) 取整 向小的方向
public static int max(int a, int b)
public static double pow(double a, double b) a^b
public static double random() 返回带正号的 double 值,该值大于等于 0.0 且小于 1.0 [0.0 1.0) 随机数
public static int round(float a) 取整 四舍五入的取整
public static double sqrt(double a) 开方,返回正确舍入的正平方根