尚硅谷JavaSE笔记合集
文章名 | 链接 |
---|---|
【JavaSE】异常 | 文章地址 |
【JavaSE】常用类:String、LocalDateTime… | 文章地址 |
【JavaSE】枚举 | 文章地址 |
【JavaSE】注解 | 文章地址 |
【JavaSE】集合框架 | 文章地址 | HashMap源码解析 | List相关实现类源码解析 |
【JavaSE】泛型 | 文章地址 |
【JavaSE】IO流 | 文章地址 | 字符编码详解 |
【JavaSE】网络编程,BIO需求演进 | 文章地址 |
【JavaSE】反射 | 文章地址 |
【JavaSE】jdk8新特性 | 文章地址 |
一、File类
1.1 实例化方法
- java.io.File :文件 或 文件目录 的抽象表示形式(与平台无关)
- 程序中的File对象,可能没有一个真实存在的文件或目录
- 访问文件内容,需要使用输入/输出流
注意:盘符 与 路径
/**
* 1.如何创建file类的实例
* - File(String):通过 绝对路径或者相对路径 创建File对象
* - File(String,String):通过 路径(String+String) 创建File对象
* - File(File,String):通过 File对象+String路径 创建对象
* 2.路径:
* - 相对路径:相对于 当前工程或者当前模块,取决于运行程序的对象
* - 绝对路径:包含盘符
*
* - windows和DO系统分隔符:\\
* - unix和URL分隔符:/
* - 根据操作系统,File类动态提供的分隔符:public static final String separator
* 注意:Java程序支持跨平台运行,因此路径分隔符要慎用
*/
public class FileTest {
@Test
public void test1(){
//1.String创建:文件不存在可以输出
File file1=new File("hello.txt");
File file2=new File("E:"+File.separator+"io");
System.out.println(file1);
System.out.println(file2);
//2.String+String创建
File file3=new File("E:\\","io");
System.out.println(file3);
//3.File+String创建
File file4=new File(file3,"hello.txt");
System.out.println(file4);
}
}
1.2 创建、删除、重命名方法
/**
* 创建硬盘中对应的文件或文件目录
* 1.createNewFile() :创建文件。若文件存在,则失败返回false
* 2.mkdir() :创建目录。若目录存在或上级目录不存在,则失败返回false
* 3.mkdirs() :创建多级目录。若目录存在,失败返回false
* !!!注意:Java中的删除不走回收站。删除目录不能有子目录或文件
* 4.delete():删除磁盘中的文件或者文件夹。
* !!!注意:如下方法调用者必须存在,参数必须不存在
* 5.renameTo(File):重命名并移动
*/
public class FileCreateTest {
@Test
public void test1() throws IOException {
File file =new File("hi.txt");
if(file.exists()){
//4.删除文件
file.delete();
System.out.println("删除文件成功!");
}else {
//1.创建文件
file.createNewFile();
System.out.println("创建文件成功!");
}
File dir1=new File("E:\\io");
File dir2=new File("E:\\io\\io\\io");
//2.创建目录
boolean mkdir = dir1.mkdir();
if (mkdir){
System.out.println("目录创建成功");
}
//3.创建多级目录
boolean mkdirs = dir2.mkdirs();
if (mkdirs){
System.out.println("创建多级目录成功!");
}
}
@Test
public void test3(){
//5. 重命名并移动
File file1=new File("hi.txt"); //!!!必须存在
File file2=new File("hello.txt"); //!!!必须不存在
file1.renameTo(file2);
}
}
1.3 获取信息方法
- list()、listFiles():可传入过滤器
/**
* - 1.getAbsolutePath():获取绝对路径
* - 2.getPath() :获取路径
* - 3.getName() :获取名称
* - 4.getParent():获取上层文件目录路径。若无,返回null
* - 5.length() :获取文件长度(即:字节数)。不能获取目录的长度。
* - 6.lastModified() :获取最后一次的修改时间,毫秒值
* !!!注意:只适用于文件目录
* - 7.public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组
* - 8.public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组
* - 扩展,方法中可传入过滤器
*/
public class FileTest {
@Test
public void test2(){
File file1=new File("hello.txt");
File file2=new File("E:\\hi.txt");
//1. 获取绝对路径
//F:\IDEA\JavaProjects\JavaseTest\IO_Demo\hello.txt
//E:\hi.txt
System.out.println(file1.getAbsolutePath());
System.out.println(file2.getAbsolutePath());
//2. 获取路径
System.out.println(file1.getPath()); //hello.txt
System.out.println(file2.getPath()); //E:\hi.txt
//3. 获取名称
System.out.println(file1.getName()); //hello.txt
System.out.println(file2.getName()); //hi.txt
//4. 获取上层文件目录路径。若无,返回null
System.out.println(file1.getParent()); //null
System.out.println(file2.getParent()); //E:\
//5. 获取文件长度(即:字节数)。不能获取目录的长度。
System.out.println(file1.length()); //0
System.out.println(file2.length()); //0
//6. 获取最后一次的修改时间,毫秒值
System.out.println(file1.lastModified()); //0
System.out.println(file2.lastModified()); //1665047928806
}
@Test
public void test4(){
//如下的两个方法适用于文件目录:
File file3=new File("E:\\io");
//7. 获取目录下所有 文件或目录名称 的String数组
String[] list = file3.list();
System.out.println(Arrays.toString(list)); //[dir, hi.txt]
//8. 获取目录下所有 文件或目录 的File数组
File[] files = file3.listFiles();
System.out.println(Arrays.toString(files));//[E:\io\dir, E:\io\hi.txt]
}
}
1.4 判断方法
/**
* 1.isDirectory():判断是否是文件目录!!!常用
* 2.isFile() :判断是否是文件!!!常用
* 3.exists() :判断是否存在!!!常用
* 4.canRead() :判断是否可读
* 5.canWrite() :判断是否可写
* 6.isHidden() :判断是否隐藏
*/
public class FileTest {
@Test
public void test5(){
File file1=new File("hello.txt");
File file2=new File("hello1.txt");
File dir1=new File("E:\\io");
File dir2=new File("E:\\io1");
//1.判断是否是文件目录!!!常用
System.out.println(file1.isDirectory()); //false
System.out.println(file2.isDirectory()); //false
System.out.println(dir1.isDirectory()); //true
System.out.println(dir2.isDirectory()); //false
//2.判断是否是文件!!!常用
System.out.println(file1.isFile()); //true
System.out.println(file2.isFile()); //false
System.out.println(dir1.isFile()); //false
System.out.println(dir2.isFile()); //false
//3.判断是否存在!!!常用
System.out.println(file1.exists()); //true
System.out.println(file2.exists()); //false
System.out.println(dir1.exists()); //true
System.out.println(dir2.exists()); //false
//4.判断是否可读
System.out.println(file1.canRead()); //true
System.out.println(file2.canRead()); //false
System.out.println(dir1.canRead()); //true
System.out.println(dir2.canRead()); //false
//5.判断是否可写
System.out.println(file1.canWrite()); //true
System.out.println(file2.canWrite()); //false
System.out.println(dir1.canWrite()); //true
System.out.println(dir2.canWrite()); //false
//6.判断是否隐藏
System.out.println(file1.isHidden()); //false
System.out.println(file2.isHidden()); //false
System.out.println(dir1.isHidden()); //false
System.out.println(dir2.isHidden()); //false
}
}
1.5 练习
题目一
-
在file目录下,创建多个文件和目录
-
实现一个方法,用于删除file目录中指定的文件
public class Pratise1 {
/**
* 1.在file目录下,创建多个文件和目录。
* 2.实现一个方法,用于删除file目录中指定的文件。
*/
@Test
public void test1() throws IOException {
//1.在file目录下,创建多个文件和目录。
File file=new File("E:\\pratise1");
File file_hello=new File(file,"hello.txt");
File file_hi=new File(file,"hi.txt");
File dir_hello=new File(file,"hello");
//不存在则创建成功,存在返回false
file_hello.createNewFile();
file_hi.createNewFile();
dir_hello.mkdir();
//2.实现一个方法,用于删除file目录中指定的文件
//如果是目录,且不是空目录,则删除失败
deleteFile(file,"hello.txt");
}
public boolean deleteFile(File dir,String filename){
if (!dir.isDirectory()){
System.out.println("删除失败,指定的文件不是目录!");
return false;
}
File file=new File(dir,filename);
if(file.delete()){
System.out.println("删除成功!");
return true;
}else{
System.out.println("删除失败!");
return false;
}
}
}
题目二
- 判断目录下是否有后缀名为.jpg的文件,有则输出文件名称
/**
* 1.判断目录下是否有后缀名为.jpg的文件,有则输出文件名称
*/
public class Pratise2 {
@Test
//list()
public void test1(){
File file=new File("C:\\Users\\李泽伟\\Pictures\\Saved Pictures") ;
String[] list = file.list();
for (String s : list) {
if (s.endsWith(".jpg")){
System.out.println(s);
}
}
}
@Test
//listFiles()
public void test2(){
File file=new File("C:\\Users\\李泽伟\\Pictures\\Saved Pictures") ;
File[] files = file.listFiles();
for (File file1 : files) {
if (file1.getName().endsWith(".jpg")) System.out.println(file1.getName());
}
}
@Test
//listFiles()+FilenameFilter
public void test3(){
File file=new File("C:\\Users\\李泽伟\\Pictures\\Saved Pictures") ;
File[] files = file.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".jpg");
}
});
for (File file1 : files) {
System.out.println(file1.getName());
}
}
}
题目三
-
遍历目录,包括子目录和文件。输出目录结构
- 计算目录占用的空间大小
public class Pratise3 { @Test public void test1(){ System.out.println("目录总大小为:"+eachDir1(new File("E:\\pratise3"), 0)); } //1.计算目录占用的空间大小 public Long eachDir1(File file,int num){ //1.目录大小:用于遍历目录时对下属文件大小进行叠加 Long size=0L; //2.获取层级 int now=num; for (int i = 0; i < now; i++) { //3.输出对应的层级符 System.out.print("--"); } //4.输出当前file名 System.out.println(file.getName()); //5.如果是目录,则修改层级并对目录下的文件或目录进行递归 if (file.isDirectory()){ now++; File[] files = file.listFiles(); //5.1.循环目录下属文件,叠加下属文件大小 for (File file1 : files) { size+=eachDir1(file1,now); } //5.2 返回目录大小 return size; }else{ //5.2 返回文件大小 return file.length(); } } } ============================================================================= pratise3 --1.txt --2.txt --3.txt --目录一 ----4.txt ----目录二 ------5.txt 目录总大小为:85
-
遍历目录,包括子目录和文件。输出目录结构
- 删除目录下的所有子目录和文件
public class Pratise3 { @Test public void test2(){ eachDir2(new File("E:\\pratise4"), 0); } //2.删除目录下的所有子目录和文件 public void eachDir2(File file,int num){ //1.获取层级 int now=num; for (int i = 0; i < now; i++) { //2.输出对应的层级符 System.out.print("--"); } //3.输出当前file名 System.out.println(file.getName()); //4.如果是目录,则修改层级并对目录下的文件或目录进行递归 if (file.isDirectory()){ now++; File[] files = file.listFiles(); for (File file1 : files) { eachDir2(file1,now); } } //5.输出层级关系进行删除操作:如果是第一层则目录则保留 if (num!=0){ file.delete(); } } }
二、原理、分类
2.1 IO流原理
- java.io
- 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
- 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中。
2.2 流的分类
- 操作数据的单位不同:字节流,字符流
- 数据的流向不同:输入流,输出流
- 流的角色的不同:节点流,处理流
抽象基类 | 字节流 | 字符流 |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
2.3 IO 流体系
三、文件流/节点流
3.1 概述
/**
* 注意:字符流和字节流不能乱用
* 1. 对于文本文件(.txt,.java,.c,.cpp),使用字符流处理。
* - 因为字符流处理时会使用字符集进行解码和编码,非文本文件的二进制数据存在字符编码表找不到的情况
* 字符流:二进制数据 --编码-> 字符编码表 --解码-> 二进制数据
* 2. 对于非文本文件(.jpg,.mp3,.mp4,.avi,.doc,.ppt,...),使用字节流处理。
* - 字节流:二进制数据 —> 二进制数据
*
* - 流的体系结构
* 抽象基类 节点流(或文件流)
* InputStream FileInputStream :read()、read(byte[])、read(byte[],int,int)
* OutputStream FileOutputStream :write(int)、write(byte[])、write(byte[],0,len)、FileOutputStream(file,true)追加文件
* Reader FileReader :read()、read(char[])、read(char[],int,int)
* Writer FileWriter :write(int)、write(char[])、write(char[],0,len)、FileWriter(file,true)追加文件
*
*/
3.2 FileReader-读入
/**
* 读入:被读文件一定要存在,否则就会报FileNotFoundException
* - read():返回读入的一个字符。如果达到文件末尾,返回-1。
* - read(char[]):返回每次读入byte数组的字符个数。如果达到文件末尾,返回-1
* 注意:read(char[])是覆盖之前的数据
* - read(String)
* - read(char[],int,int)
*/
public class FileReadTest {
//相较于当前工程
public static void main(String[] args) {
File file =new File("hello.txt");
//F:\software\IDEA\JavaProjects\JavaseTest\hello.txt
System.out.println(file.getAbsoluteFile());
}
@Test
//相较于当前的Module:read()
public void test1(){
FileReader fr= null;
try {
//1.实例化要操作文件的File对象
File file =new File("hello.txt");
//2.创建与之连接的流
fr = new FileReader(file);
int data;
//3.读取数据
while((data=fr.read())!=-1){
System.out.println((char)data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭流
if (fr!=null){
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
//相较于当前的Module:read(char[])
public void test2(){
FileReader fr= null;
try {
//1.实例化要操作文件的File对象
File file =new File("hello.txt");
//2.创建与之连接的流
fr = new FileReader(file);
int data;
//3.读取数据
int len;
char[] buf=new char[5];
while((len=fr.read(buf))!=-1){
//方式一:
//错误写法:i < buf.length
for (int i = 0; i < len; i++) {
System.out.println(buf[i]);
}
//方式二
//错误写法:new String(buf,0,buf.length)
String str=new String(buf,0,len);
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭流
if (fr!=null){
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
3.3 FileWriter-写出
/**
* 写出:写入的文件可以不存在的。并不会报异常
* - 文件不存在:自动创建
* - 文件存在:流在构造器中设置是否追加
* 1.FileWriter(file,false) / FileWriter(file):覆盖
* 2.FileWriter(file,true):追加
* - write(int)
* - write(String)
* - write(char[])
* - write(String,int,int)
* - write(char[],int,int)
*/
public class FileWriteTest {
//write(String)
@Test
public void test1(){
FileWriter fw= null;
try {
//1.实例化要操作文件的File对象
File file=new File("hello.txt");
//2.创建与之连接的流
fw = new FileWriter(file,true);
//3.写入数据
fw.write("1");
fw.write("2");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fw!=null){
try {
//4.关闭流
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
3.4 文本复制
注意:不能使用字符流来处理图片等字节数据
/**
* 文件字符流输入输出实现文本复制
* 注意:不能使用字符流来处理图片等字节数据
*/
public class ReadWriteCopyTest {
//read(char[])、write(char[],int,int)
@Test
public void test1(){
FileReader fr= null;
FileWriter fw= null;
try {
//1.实例化要操作文件的File对象
File src=new File("hello.txt");
File dest=new File("copy.txt");
//2.创建与之连接的流
fr = new FileReader(src);
fw = new FileWriter(dest);
char[] buf=new char[5];
int length;
//3.读写数据
while((length=fr.read(buf))!=-1){
fw.write(buf,0,length);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭流
if (fr!=null){
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fw!=null){
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
3.5 FileInputStream-读文本乱码
/**
* 读:
* - read()
* - read(byte[])
* - read(byte[],int,int)
* 写:
* - write(int)
* - write(byte[])
* - write(byte[],int,int)
*
* 注意:不能使用字符流来处理图片等字节数据
*/
public class FileInputTest {
//read(byte[])、new String(byte[],int,int)
@Test
public void test1(){
FileInputStream fis= null;
try {
//1.实例化要操作文件的File对象
File file=new File("hello.txt"); //hello中
//2.创建与之连接的流
fis = new FileInputStream(file);
byte[] buf=new byte[3];
int length;
//3.读取数据
while((length=fis.read(buf))!=-1){
//由于“中”在文本中编码为三个字节,在读取时被拆开,所以解码字符错误
System.out.println(new String(buf,0,length)); //hel lo� ��
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭流
if (fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
3.6 非文本复制
/**
* 实现:文件字节流输入输出实现图片复制
*
* 读:
* - read()
* - read(byte[])
* - read(byte[],int,int)
* 写:
* - write(int)
* - write(byte[])
* - write(byte[],int,int)
*
*/
public class InputOutputCopy {
//read(byte[])、write(byte[],int,int)
@Test
public void test(){
File src=new File("C:\\Users\\李泽伟\\Pictures\\Saved Pictures\\QQ图片20200223235928.jpg");
File dest=new File("1.jpg");
Long start=System.currentTimeMillis();
testCopyFile(src,dest);
Long end=System.currentTimeMillis();
System.out.println("花费时间:"+(end-start)); //288
}
public void testCopyFile(File src, File dest){
FileInputStream fis= null;
FileOutputStream fos= null;
try {
fis = new FileInputStream(src);
fos = new FileOutputStream(dest);
byte[] buf=new byte[1024];
int length;
while((length=fis.read(buf))!=-1){
fos.write(buf,0,length);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos!=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
四、缓冲流
4.1 概述
- 提高输入、输出效率
- 缺省创建一个8192个字节(8Kb)的内部缓冲区数组
- 先把数据 读/写 到缓冲区中,装满后再一次性 读/写 到输入输出流
- 需要“套接”在节点流之上
/**
* 注意:
* - 需要“套接”在节点流之上
* - 关闭最外层流即可,内层节点流会自动关闭。自动刷新缓存区
*
* 抽象基类 缓冲流(处理流的一种)
* InputStream BufferedInputStream :
* OutputStream BufferedOutputStream :flush()
* Reader BufferedReader :readLine()
* Writer BufferedWriter :newLine()、flush()
*
*/
4.2 非文本复制
/**
* 缓冲流:非文本文件复制
* - flush()
* - 只需关闭外层流,自动刷新缓冲区
*/
public class InputOutputCopy {
@Test
public void test(){
BufferedInputStream bis= null;
BufferedOutputStream bos= null;
try {
//1.创建文件对象
File src=new File("1.jpg");
File dest=new File("2.jpg");
//2.创建文件的连接流
FileInputStream fis=new FileInputStream(src);
FileOutputStream fos=new FileOutputStream(dest);
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//3.数据读写实现复制
byte[] buf=new byte[1024];
int length;
while((length=bis.read(buf))!=-1){
bos.write(buf,0,length);
//bos.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.资源关闭
if(bis!=null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bos!=null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
4.2 证明缓冲流效果
/**
* 证明:缓冲流提高输入、输出效率
* 没有缓冲:288。具体实现参考3.6节
* 加上缓冲:4
*/
public class EffectTest {
@Test
public void test(){
File src=new File("C:\\Users\\李泽伟\\Pictures\\Saved Pictures\\QQ图片20200223235928.jpg");
File dest=new File("1.jpg");
Long start=System.currentTimeMillis();
testCopyFileBuf(src,dest);
Long end=System.currentTimeMillis();
System.out.println("花费时间:"+(end-start)); //288 -> 4
}
public void testCopyFileBuf(File src, File dest){
BufferedInputStream bis= null;
BufferedOutputStream bos= null;
try {
//1.实现流与文件的连接
bis = new BufferedInputStream(new FileInputStream(src));
bos = new BufferedOutputStream(new FileOutputStream(dest));
//2.输入输出操作
byte[] buf = new byte[1024];
int length;
while ((length = bis.read(buf)) != -1) {
bos.write(buf, 0, length);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭资源
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
4.3 文本复制
/**
* 文本复制
* - String readLine()
* - newLine()/ +"\n"
*/
public class ReadWriteTest {
@Test
public void test(){
BufferedReader br= null;
BufferedWriter bw= null;
try {
//1.实现流与文件的连接
br = new BufferedReader(new FileReader("copy.txt"));
bw = new BufferedWriter(new FileWriter("copy1.txt"));
//2.实现输入输出
//方式一
// char[] flush=new char[1024];
// int length;
// while((length=br.read(flush))!=-1){
// bw.write(flush);
// }
//方式二
String data;
while((data=br.readLine())!=null){
// bw.write(data+"\n");
bw.newLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭资源
if (br!=null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bw!=null){
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
4.4 课后练习
练习1:证明缓存流的作用
参考4.2小节
练习2
实现照片的加密
/**
* 练习2:实现照片的加密与解密
*/
public class Encode {
@Test
public void test1(){
BufferedInputStream bis= null;
BufferedOutputStream bos= null;
try {
//1.实现流与文件的连接
bis = new BufferedInputStream(new FileInputStream("1.jpg"));
bos = new BufferedOutputStream(new FileOutputStream("3.jpg"));
//2.实现图片复制+加密
byte[] buf=new byte[1024];
int length;
while((length=bis.read(buf))!=-1){
for (int i = 0; i < length; i++) {
buf[i]= (byte) (buf[i] ^ 5);
}
bos.write(buf);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.资源关闭
if (bis!=null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bos!=null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
public void test2(){
BufferedInputStream bis= null;
BufferedOutputStream bos= null;
try {
//1.实现流与文件的连接
bis = new BufferedInputStream(new FileInputStream("3.jpg"));
bos = new BufferedOutputStream(new FileOutputStream("4.jpg"));
//2.实现图片复制+解密
byte[] buf=new byte[1024];
int length;
while((length=bis.read(buf))!=-1){
for (int i = 0; i < length; i++) {
buf[i]= (byte) (buf[i] ^ 5);
}
bos.write(buf);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.资源关闭
if (bis!=null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bos!=null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
练习3
统计文本文件上每个字符出现的次数
/**
* 练习3:统计文本文件上每个字符出现的次数
*/
public class CharCount {
@Test
public void test(){
BufferedReader br= null;
BufferedWriter bw= null;
try {
//1.实现文件与流的连接
br = new BufferedReader(new FileReader("hello.txt"));
bw = new BufferedWriter(new FileWriter("charCount.txt"));
//2.统计
char[] flush=new char[1024];
int length;
//定义map集合
Map<Character,Integer> map=new HashMap<Character,Integer>();
//遍历char型数组,判断元素是否为map中的key
//是则添加:key-value+1
//否则添加:key-1
while((length=br.read(flush))!=-1){
for (int i = 0; i < length; i++) {
if (map.get(flush[i])!=null) {
map.put(flush[i],map.get(flush[i])+1);
}else{
map.put(flush[i],1);
}
}
}
//3.输出统计结果
Set<Map.Entry<Character, Integer>> entries = map.entrySet();
for (Map.Entry<Character,Integer> entry : entries) {
switch (entry.getKey()){
case ' ':
bw.write("空格="+entry.getValue());
break;
case '\t':
bw.write("tab键="+entry.getValue());
break;
case '\r':
bw.write("回车="+entry.getValue());
break;
case '\n':
bw.write("换行="+entry.getValue());
break;
default:
bw.write(entry.getKey()+"="+entry.getValue());
break;
}
bw.newLine();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭资源
if(br!=null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bw!=null){
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
五、转换流
5.1 概述
- 提供字节流与字符流之间的转换
- 字节流中的数据都是字符时,转成字符流操作更高效。
- 很多时候我们使用转换流来处理文件乱码问题。实现编码和解码的功能。
/**
* - 属于字符流
* InputStreamReader:字节输入流 --> 字符的输入流
* OutputStreamWriter:字符输出流 --> 字节的输出流
*
*
* 抽象基类 转换流(处理流的一种)
* InputStream
* OutputStream
* Reader InputStreamReader :InputSreamReader(InputStream,String)编码方式
* Writer OutputStreamWriter :OutputSreamWriter(OutputStream,String)编码方式
*
*/
5.2 改变编码格式
/**
* 转换流:实现文本文件编码格式的转换
* - 解码:字节、字节数组 ---> 字符数组、字符串
* - 编码:字符数组、字符串 ---> 字节、字节数组
*/
public class InputStreamReaderTest {
@Test
public void test(){
InputStreamReader isr= null;
OutputStreamWriter osw= null;
try {
//1.实现文件与流的连接
//参数2指明了字符集,具体使用哪个字符集,取决于文件hello.txt保存时使用的字符集
isr = new InputStreamReader(new FileInputStream("hello.txt"),"utf-8");
osw = new OutputStreamWriter(new FileOutputStream("hello_gbk.txt"),"gbk");
//2.数据格式改变
char[] flu=new char[20];
int length;
while((length=isr.read(flu))!=-1){
osw.write(flu,0,length);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭资源
if (isr!=null){
try {
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (osw!=null){
try {
osw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
六、标准输入、输出流
- System.in
- 系统标准的输入设备,默认为键盘
- 类型是InputStream
- System.out
- 分系统标准的输出设备,默认为显示器
- 类型是PrintStream,其是FilterOutputStream的子类 --> OutputStream的子类
- 重定向:改变系统默认设备
- System.setIn(InputStreamin)
- System.setOut(PrintStreamout)
/**
*
* 练习:从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续进行输入操作,
* - 直至当输入“e”或者“exit”时,退出程序。
*/
public class SystemInTest {
public static void main(String[] args) {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(System.in));
while(true){
System.out.println("请输出字符串:");
String data=br.readLine();
if ("e".equalsIgnoreCase(data) || "exit".equalsIgnoreCase(data)) {
System.out.println("退出程序!");
break;
}
System.out.println(data.toUpperCase());
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br!=null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
/**
* 练习:模拟Scanner,将每行输入转换为int、double、float、boolean、short、byte、String
*/
public class ScannerTest {
private static BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
public static String nextStr(){
String data=null;
try {
data=br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
public int nextInt() {
return Integer.parseInt(nextStr());
}
public double nextDouble() {
return Double.parseDouble(nextStr());
}
public double nextFloat() {
return Float.parseFloat(nextStr());
}
public boolean nextBoolean() {
return Boolean.parseBoolean(nextStr());
}
public double nextShort() {
return Short.parseShort(nextStr());
}
public double nextByte() {
return Byte.parseByte(nextStr());
}
}
七、打印流
- 实现将基本数据类型的数据格式转化为字符串输出
- 打印流:PrintStream和PrintWriter
- 一系列的 print(重载)、println(重载) ,用于多种数据类型的输出
- 不会抛出IOException异常
- 有自动flush功能
/**
*
* 打印流练习:将系统标准输出流进行重定向
*
* 抽象基类 打印流(处理流的一种)
* InputStream
* OutputStream PrintStream: print()、println()、PrintStream(OutputStream,boolean)是否自动刷新缓存
* Reader
* Writer PrintWriter: print()、println()、PrintWriter(OutputStream,boolean)是否自动刷新缓存
*
*/
public class PrintStreamTest {
@Test
public void test(){
PrintStream ps= null;
try {
ps = new PrintStream(new FileOutputStream("printStream.txt"),true);
//重定向系统输出
if (ps!=null) System.setOut(ps);
// 输出ASCII字符
for (int i = 0; i <= 255; i++) {
// 每50个数据一行
if (i%50==0){
System.out.println();
}
System.out.print((char)(i-1));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (ps!=null){
ps.close();
}
}
}
}
八、数据流
- 操作 基本数据类型、String 数据
- 注意:读取不同类型的数据的顺序要与当初写入文件时,保存的数据的顺序一致!
/**
*
* 练习1:将内存中的字符串、基本数据类型的变量写出到文件中。
* 练习2:将文件中的字符串、基本数据类型的变量读取到内存中。
*
* 抽象基类 打印流(处理流的一种)
* InputStream DataInputStream: readUTF()、readFully(byte[])、readBoolean()、readByte()、readChar()...
* OutputStream DataOutputStream: writeUTF()、writeFully(byte[])、writeBoolean()、writeByte()、writeChar()...
* Reader
* Writer
*
*/
public class OtherStreamTest {
//练习1:将内存中的字符串、基本数据类型的变量写出到文件中。
@Test
public void test3() throws IOException {
//1.
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
//2.
dos.writeUTF("刘刚");
dos.flush();//刷新操作,将内存中的数据写入文件
dos.writeInt(23);
dos.flush();
dos.writeBoolean(true);
dos.flush();
//3.
dos.close();
}
//练习2:将文件中的字符串、基本数据类型的变量读取到内存中。
@Test
public void test4() throws IOException {
//1.
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
//2.
String name = dis.readUTF();
int age = dis.readInt();
boolean isMale = dis.readBoolean();
System.out.println("name = " + name);
System.out.println("age = " + age);
System.out.println("isMale = " + isMale);
//3.
dis.close();
}
}
九、对象流
9.1 序列化机制
- 序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的Java对象
- 序列化是RMI(Remote Method Invoke –远程方法调用)过程的参数和返回值都必须实现的机制,而RMI 是JavaEE的基础。因此序列化机制是JavaEE平台的基础
- 对象支持序列化机制的要求
- 类实现了 Serializable接口 或 Externalizable接口
- 类提供一个全局常量:serialVersionUID
- 保证其内部所有属性也必须是可序列化的
- static 和 transient修饰的成员变量不能序列化
9.2 序列化与反序列化
/**
* 作用:存储和读取基本数据类型数据或对象的处理流。强大之处在于:
* 1.序列化:可以把内存中的java对象写入到数据源或通过网络传输出去
* 2.反序列化:可以把对象从数据源、网络中还原回来
*
* 抽象基类 对象流
* InputStream ObjectInputStream(反序列化): readObject()
* OutputStream ObjectOutputStream(序列化): writeObject()、flush()
* Reader
* Writer
*/
public class ObjectIOTest {
//1.序列化:可以把内存中的java对象写入到数据源或通过网络传输出去
@Test
public void test1(){
ObjectOutputStream oos= null;
try {
//1.文件与流的连接
oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
//2.操作
oos.writeObject(new String("我爱Java!"));
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭资源
if (oos!=null){
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//2.反序列化:可以把对象从数据源、网络中还原回来
@Test
public void test2(){
ObjectInputStream ois= null;
try {
//1.文件与流的连接
ois = new ObjectInputStream(new FileInputStream("object.dat"));
//2.操作
String str = (String) ois.readObject();
System.out.println(str);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
//3.关闭资源
if (ois!=null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
9.3 自定义可序列化类
- 对象支持序列化机制的要求:
- 类实现了 Serializable接口 或 Externalizable接口
- 类提供一个全局常量:serialVersionUID
- 保证其内部所有属性也必须是可序列化的
注意:static 和 transient 修饰的成员变量不能序列化
Person类
/**
* 自定义可序列化类
* 1.实现接口:Serializable 或 Externalizable
* 2.提供一个全局常量:serialVersionUID
* 3.内部所有属性也必须是可序列化的。(默认情况下,基本数据类型、String等可序列化)
*
* 注意:ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
*/
//1.实现接口:Serializable 或 Externalizable
public class Person implements Serializable {
//2.提供一个全局常量:serialVersionUID
private static final long serialVersionUID = -684966670754667710L;
String name;
int age;
//3.保证内部所有属性是可序列化的
Account account;
//注意:static和transient修饰的成员变量不能序列化
transient Long idCard;
static String address="北京";
public Person() {
}
public Person(String name, int age, Account account, Long idCard) {
this.name = name;
this.age = age;
this.account = account;
this.idCard = idCard;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public Long getIdCard() {
return idCard;
}
public void setIdCard(Long idCard) {
this.idCard = idCard;
}
public static String getAddress() {
return address;
}
public static void setAddress(String address) {
Person.address = address;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", account=" + account +
", idCard=" + idCard +
'}';
}
}
//1.实现接口:Serializable 或 Externalizable
class Account implements Serializable {
//2.提供一个全局常量:serialVersionUID
private static final long serialVersionUID = -618956670754667710L;
//3.保证内部所有属性是可序列化的
Long balance;
public Account() {
}
public Account(Long balance) {
this.balance = balance;
}
public Long getBalance() {
return balance;
}
public void setBalance(Long balance) {
this.balance = balance;
}
@Override
public String toString() {
return "Account{" +
"balance=" + balance +
'}';
}
}
测试类
/**
* ObjectInputStream(反序列化): readObject()
* ObjectOutputStream(序列化): writeObject()、flush()
*/
public class DiySerializableObjectTest {
//1.序列化:可以把内存中的java对象写入到数据源或通过网络传输出去
@Test
public void test1(){
ObjectOutputStream oos= null;
try {
//1.文件与流的连接
oos = new ObjectOutputStream(new FileOutputStream("person.dat"));
//2.操作
oos.writeObject(new Person("宋红康",30,new Account(10000000l),6745463544l));
oos.flush();
oos.writeObject(new Person("周阳",40,new Account(10000000l),5465454646l));
oos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭资源
if (oos!=null){
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//2.反序列化:可以把对象从数据源、网络中还原回来
@Test
public void test2(){
ObjectInputStream ois= null;
try {
//1.文件与流的连接
ois = new ObjectInputStream(new FileInputStream("person.dat"));
//2.操作
Person shk = (Person) ois.readObject();
System.out.println(shk);
Person zy = (Person) ois.readObject();
System.out.println(zy);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
//3.关闭资源
if (ois!=null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
9.4 serialVersionUID的理解
反例情况:
1.创建Person类。不指定serialVersionUID,系统随机生成uid:111
2.序列化Person对象A
3.修改Person类。serialVersionUID修改为:222
4.反序列化Person对象A,系统找不到uid:111的Person类,无法完成反序列化
- 简单来说,就是用于反序列化时找到UID对应的类
十、随机存取文件流
10.1 概述
- java.io.RandomAccessFile:直接继承于
java.lang.Object
类,实现了DataInput、DataOutput
两个接口,意味着这个类 既可以读也可以写。 - 支持“随机访问” :在文件的任意地方来读、写文件
- 支持只访问文件的部分内容
- 可以向已存在的文件后追加内容
- 维护标示当前读写处位置的 记录指针:记录指针可以自由移动
- getFilePointer():获取指针位置
- seek(long):将指针定位到指定位置。文件字节数
- 构造器(指定对象的访问模式):RandomAccessFile(File, String)、RandomAccessFile(String, String)
- r:以只读方式打开。不会创建文件,文件不存在会出现异常
- rw:打开以便读取和写入
- rwd:打开以便读取和写入;同步文件内容的更新
- rws打开以便读取和写入;同步文件内容和元数据的更新
10.2 读写操作
/**
* 1.直接继承于java.lang.Object类,实现了DataInput和DataOutput接口
* 2.既可以读也可以写
* 3.可写情况下:
* - 文件不存在,则创建文件
* - 文件存在,对原有内容从头覆盖(不会清空)
*/
public class RandomIOTest {
@Test
public void test(){
RandomAccessFile read= null;
RandomAccessFile write= null;
try {
//1.文件与流的连接
read = new RandomAccessFile("1.jpg","r");
write = new RandomAccessFile("5.jpg","rw");
//2.数据处理
byte[] buf=new byte[20];
int length;
while((length=read.read(buf))!=-1){
write.write(buf,0,length);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭资源
if (read!=null){
try {
read.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (write!=null){
try {
write.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//文件存在,对原有内容从头覆盖(不会清空)
@Test
public void test1(){
RandomAccessFile write= null;
try {
//1
write = new RandomAccessFile("copy.txt","rw");
//2
write.write("abc".getBytes());
} catch (Exception e) {
e.printStackTrace();
} finally {
//3
if (write!=null){
try {
write.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
11.2 插入操作
/**
* RandomAccessFile的使用
* - 可以通过相关的操作,实现RandomAccessFile“插入”数据的效果
*
* 思考:将StringBuilder替换为ByteArrayOutputStream
*/
public class InsetTest {
@Test
public void test(){
RandomAccessFile raf= null;
try {
File file=new File("hello.txt");
raf = new RandomAccessFile(file,"rw");
//1.先保存指定位置的后面内容
StringBuilder sb=new StringBuilder((int) file.length());
raf.seek(8);
byte[] buf=new byte[20];
int length;
while((length=raf.read(buf))!=-1){
sb.append(new String(buf,0,length));
}
//2.在指定位置插入数据
raf.seek(8);
raf.write("国我爱你!".getBytes("UTF8"));
//3.将保存的内容继续写入
raf.write(sb.toString().getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (raf!=null){
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//思考:将StringBuilder替换为ByteArrayOutputStream
@Test
public void test1(){
RandomAccessFile raf= null;
ByteArrayOutputStream baos=null;
try {
File file=new File("hello.txt");
raf = new RandomAccessFile(file,"rw");
baos =new ByteArrayOutputStream();
//1.先保存指定位置的后面内容
raf.seek(8);
byte[] buf=new byte[20];
int length;
while((length=raf.read(buf))!=-1){
baos.write(buf,0,length);
}
//2.在指定位置插入数据
raf.seek(8);
raf.write("国我爱你!".getBytes("UTF8"));
//3.将保存的内容继续写入
raf.write(baos.toString().getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (raf!=null){
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
十一、NIO.2
概述
-
Java NIO
(New IO,Non-Blocking IO)是从Java 1.4版本开始引入的一套新的IO API,可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的(IO是面向流的)、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。 -
Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套就是网络编程NIO。
|-----java.nio.channels.Channel |-----FileChannel:处理本地文件 |-----SocketChannel:TCP网络编程的客户端的Channel |-----ServerSocketChannel:TCP网络编程的服务器端的Channel |-----DatagramChannel:UDP网络编程中发送端和接收端的Channel 12345
-
随着JDK 7 的发布,Java对NIO进行了极大的扩展,增强了对文件处理和文件系统特性的支持,以至于我们称他们为
NIO.2
。因为NIO 提供的一些功能,NIO已经成为文件处理中越来越重要的部分。 -
早期的Java只提供了一个File类来访问文件系统,但File类的功能比较有限,所提供的方法性能也不高。而且,大多数方法在出错时仅返回失败,并不会提供异常信息。
-
NIO. 2
为了弥补这种不足,引入了Path
接口,代表一个平台无关的平台路径,描述了目录结构中文件的位置。Path可以看成是File类的升级版本,实际引用的资源也可以不存在。-
在以前IO操作都是这样写的:
import java.io.File; File file = new File(“index.html”);
-
但在Java7 中,我们可以这样写:
import java.nio.file.Path; import java.nio.file.Paths; Path path = Paths.get(“index.html”);
-
-
同时,NIO.2在
java.nio.file
包下还提供了Files、Paths
工具类,Files
包含了大量静态的工具方法来操作文件;Paths
则包含了两个返回Path
的静态工厂方法。
Path接口
Paths
Paths
类提供的静态get()
方法用来获取Path
对象:static Pathget(String first, String … more)
: 用于将多个字符串串连成路径static Path get(URI uri)
: 返回指定uri对应的Path路径
Files 类