第一章.设计模式
设计模式(Design pattern),是一套被反复使用、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、保证代码可靠性、程序的重用性。
1995 年,GoF(Gang of Four,四人组)合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了 23 种设计模式。
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。-->创建对象
结构型模式,共七种:[适配器模式]、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。-->对功能进行增强
行为型模式,共十一种:策略模式、模板方法模式、[观察者模式]、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、[中介者模式]、解释器模式。
单例模式
1.目的:为了在一个类中只产生一个对象,给外界使用
2.单:一个
3.例:实例(对象)
1.饿汉式:
1.简单理解: 我很饥饿,迫不及待的想要这个对象,所以此类的对象就应该很早的创建出来
/**
* 饿汉式
* 1.简单理解: 我很饥饿,迫不及待的想要这个对象,所以此类的对象就应该很早的创建出来
*/
public class Singleton {
/*
为了防止外面随意使用构造去new对象
可以将构造私有化
*/
private Singleton(){
}
/*
由于构造私有,外界new不了对象
所以对象只能在本类中产生
饿汉式,迫不及待的想让对象产生
所以此对象变成static的,就可以随着类的加载而加载
而且此对象不能让外界直接访问到的,所以再加一个private权限
*/
private static Singleton singleton = new Singleton();
/*
定义一个方法,将本类内部创建出来的对象,返回给外界
*/
public static Singleton getSingleton(){
return singleton;
}
}
public class Test01 {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
Singleton singleton = Singleton.getSingleton();
System.out.println(singleton);
}
}
}
2.懒汉式:
1.懒汉式:什么时候想要对象,对象再产生,不着急new对象
/**
* 懒汉式
* 什么时候想要对象,对象再产生,不着急new对象
*/
public class Singleton {
/*
为了防止外面随意使用构造去new对象
可以将构造私有化
*/
private Singleton() {
}
/*
由于构造私有,外界new不了对象
所以对象只能在本类中产生
懒汉式,不用迫不及待的让对象产生
所以不用上来就new
而且此对象不能让外界直接访问到的,所以再加一个private权限
*/
private static Singleton singleton = null;
/*
定义一个方法,将本类内部创建出来的对象,返回给外界
什么时候调用此方法,什么时候再给外界产生对象,而且保证只能返回同一个对象
*/
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
public class Test01 {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
Singleton singleton = Singleton.getSingleton();
System.out.println(singleton);
}
}
}
3.小结
1.构造私有
2.对象在内部产生,并且私有,静态
3.定义方法,让外界调用方法,将本类对象返回
第二章.File类
1.File类
1.要知道的英文单词:
a.File:文件
b.Directory:文件夹(目录)
c.path:路径
d.in:输入
e.out:输出
2.以.jpg结尾的一定是图片吗?
不一定,要看类型
3.路径分隔符
路径名称分隔符:在一个路径中文件夹之间的分隔符
windows:\
linux:/
路径分隔符:路径和其他路径之间的分隔符
;
4. E:\Idea\io
io的父路径是谁?E:\Idea
5.什么是文本文档
用记事本打开人能看懂的就是文本文档
.txt .html .css .java->都是
.doc .ppt->不是
File:
1.概述:文件和目录路径名的抽象表示形式
2.通俗解释:
我们创建File对象的时候,需要传递指定文件或者文件夹的路径,那么File就可以代表此文件或者文件夹
然后也可以根据路径找到此文件或者文件夹,然后就可以通过File类中的方法去操作指定路径下的指定文件或者文件夹
2.File的静态成员
static String pathSeparator -> 与系统有关的路径分隔符 ;
static String separator-> 与系统有关的默认名称分隔符 \
public class Test01File {
public static void main(String[] args) {
//static String pathSeparator -> 与系统有关的路径分隔符 ;
String pathSeparator = File.pathSeparator;
System.out.println(pathSeparator);
//static String separator-> 与系统有关的默认名称分隔符 \
String separator = File.separator;
System.out.println(separator);
}
}
经验值:如何正确写一个路径
public class Test02File { public static void main(String[] args) { /* 写代码的时候需要跨平台 达到一次编写,到处运行的效果 如果写路径时,分隔符写死,到了别的平台上运行 有可能有问题,所以代码要写活 */ String path = "E:\Idea\io"; System.out.println(path); String path1 = "E:"+ File.separator+"Idea"+File.separator+"io"; System.out.println(path1); } }
3.File的构造方法
File(String parent, String child) -> 根据所填写的路径创建File对象
parent:父路径
child:子路径
File(File parent, String child) -> 根据所填写的路径创建File对象
parent:父路径->File对象
child:子路径
File(String pathname) -> 根据所填写的路径创建File对象
字符串的路径
public class Test03File {
public static void main(String[] args) {
/* File(String parent, String child) -> 根据所填写的路径创建File对象
parent:父路径
child:子路径*/
File file1 = new File("E:\Idea\io","1.jpg");
System.out.println(file1);
/* File(File parent, String child) -> 根据所填写的路径创建File对象
parent:父路径->File对象
child:子路径*/
File file2 = new File(new File("E:\Idea\io"),"1.jpg");
System.out.println(file2);
//File(String pathname) -> 根据所填写的路径创建File对象 字符串的路径
File file3 = new File("E:\Idea\io");
System.out.println(file3);
}
}
注意:创建File的时候,指定的路径可以不存在,但是没意义
4.File的获取方法
- public String getAbsolutePath() :返回此File的绝对路径名字符串。->带盘符的路径
- public String getPath() :将此File转换为路径名字符串。-> 获取的是File的封装路径
new File的时候,传递的是啥路径,getPath方法就获取的是啥路径
- public String getName() :返回由此File表示的文件或目录的名称。
- public long length() :返回由此File表示的文件的长度。->文件的字节数
- public File getParentFile()返回由此File表示的文件或目录的父目录,如果没有父目录,返回null。
public class Test01_Get {
public static void main(String[] args) {
File file = new File("io\1.txt");
//- public String getAbsolutePath () :返回此File的绝对路径名字符串。->带盘符的路径
String absolutePath = file.getAbsolutePath();
System.out.println("absolutePath = " + absolutePath);
/* - public String getPath () :将此File转换为路径名字符串。->获取的是File的封装路径
new File的时候, 传递的是啥路径, getPath方法就获取的是啥路径*/
String path = file.getPath();
System.out.println("path = " + path);
//- public String getName () :返回由此File表示的文件或目录的名称。
String name = file.getName();
System.out.println("name = " + name);
//- public long length () :返回由此File表示的文件的长度。->文件的字节数
File file1 = new File("E:\Idea\io\a.txt");
System.out.println("file1.length() = " + file1.length());
//- public File getParentFile () 返回由此File表示的文件或目录的父目录,如果没有父目录,返回null。
System.out.println("file1.getParentFile() = " + file1.getParentFile());
}
}
5.相对路径和绝对路径
1.绝对路径:带盘符的路径
跨盘符可以写绝对路径
2.相对路径:在idea中写的相对路径
a.找一个参照路径-> 在idea中,参照路径是当前project的绝对路径
b.哪个路径为参照路径,哪个路径就可以省略不写,剩下的就是在idea中的相对路径写法
3.相对路径举例说明:
3.1.在day19模块下创建了一个1.txt,那么1.txt的相对路径怎么写?
a.1.txt的绝对路径:E:\Idea\idea2019\workspace\220212_javase\day19\1.txt
b.1.txt的参照路径:E:\Idea\idea2019\workspace\220212_javase
c.1.txt的相对路径:day19\1.txt
4.总结:
a.在idea中相对路径一般都是从模块名开始写
b.要是不带模块名,直接写文件名或者文件夹名,默认位置在当前project下
6.File的创建方法
boolean createNewFile() -> 创建新文件
如果指定的文件之前有,创建失败,返回false
如果指定的文件之前没有,创建成功,返回true
boolean mkdirs() -> 既可以创建单级文件夹,还可以创建多级文件夹
如果指定的文件夹之前有,创建失败,返回false
如果指定的文件夹之前没有,创建成功,返回true
public class Test03_Create {
public static void main(String[] args) throws IOException {
/*
boolean createNewFile() -> 创建新文件
如果指定的文件之前有,创建失败,返回false
如果指定的文件之前没有,创建成功,返回true
*/
File file = new File("E:\Idea\io\1.txt");
System.out.println("file.createNewFile() = " + file.createNewFile());
/*
boolean mkdirs() -> 既可以创建单级文件夹,还可以创建多级文件夹
如果指定的文件夹之前有,创建失败,返回false
如果指定的文件夹之前没有,创建成功,返回true
*/
File file1 = new File("E:\Idea\io\haha\heihei\xixi\giaogiao");
System.out.println("file1.mkdirs() = " + file1.mkdirs());
}
}
7.File类的删除方法
boolean delete() -> 既可以删除文件,也可以删除文件夹 -> 不走回收站
文件:如果有,删除成功,返回true,否则返回false
文件夹:如果删除文件夹,必须保证是空文件夹
public class Test04_Delete {
public static void main(String[] args) {
File file = new File("E:\Idea\io\haha");
System.out.println("file.delete() = " + file.delete());
}
}
8.File类的判断方法
boolean isDirectory() -> 判断是否为文件夹
boolean isFile() -> 判断是否为文件
boolean exists() -> 判断文件或者文件夹是否存在
注意:判断的是类型
public class Test05_Is {
public static void main(String[] args) {
File file = new File("E:\Idea\io");
//boolean isDirectory() -> 判断是否为文件夹
System.out.println("file.isDirectory() = " + file.isDirectory());
//boolean isFile() -> 判断是否为文件
System.out.println("file.isFile() = " + file.isFile());
//boolean exists() -> 判断文件或者文件夹是否存在
System.out.println("file.exists() = " + file.exists());
}
}
9.File的遍历方法
String[] list()-> 获取指定目录下的子文件夹或者文件
File[] listFiles()-> 获取执行目录下的子文件夹或者文件的File对象
注意:listFiles底层原理其实就是调用list方法,将文件或者文件夹获取出来封装成一个一个的File对象,放到File数组中
public class Test06_Foreach {
public static void main(String[] args) {
File file = new File("E:\Idea\io");
//String[] list()-> 获取指定目录下的子文件夹或者文件
/*String[] list = file.list();
for (String s : list) {
System.out.println(s);
}*/
//File[] listFiles()-> 获取执行目录下的子文件夹或者文件的File对象
File[] files = file.listFiles();
for (File file1 : files) {
System.out.println(file1);
}
}
}
练习:遍历指定文件夹下所有的.jpg文件
步骤:
1.创建File对象,指定要遍历的文件夹路径
2.调用listFiles(),遍历指定文件夹下的内容
3.在遍历的过程中判断
4.判断如果是文件,获取文件的文件名,判断是否以.jpg结尾,如果是,直接输出
5.如果是文件夹,再遍历这个子文件夹
public class Test07_Foreach {
public static void main(String[] args) {
//1.创建File对象,指定要遍历的文件夹路径
File file = new File("E:\Idea\io");
method(file);
}
private static void method(File file) {
//2.调用listFiles(),遍历指定文件夹下的内容
File[] files = file.listFiles();
//3.在遍历的过程中判断
for (File file1 : files) {
//4.判断如果是文件,获取文件的文件名,判断是否以.jpg结尾,如果是,直接输出
if (file1.isFile()) {
String name = file1.getName();
if (name.endsWith(".jpg")) {
System.out.println(name);
}
} else {
//5.如果是文件夹,再遍历这个子文件夹
method(file1);
}
}
}
}
第三章.字节流
1.IO流介绍以及输入输出以及流向的介绍
1.概述:
I:In-> 输入
O:Out-> 输出
2.什么是IO流技术
将数据从一个设备上传输到另外一个设备上的技术
3.为什么要学IO流
之前我们保存数据可以放到集合中,数组中,但是集合和数组是临时存储,程序运行完毕,程序会从内存中释放出来,此时数组和集合中的数据就没了
我们就想,将数据永久保存起来,所以我们可以将数据放到硬盘上,只要硬盘不废,数据就还在,想用时候,直接从硬盘上读回来就直接可以用了
IO流技术就可以将内存中的数据保存到硬盘上,用的时候还可以将数据从硬盘上读回来直接用
2.IO流的流向
1.输出流:从内存出发,将数据写到硬盘的文件中
2.输入流:将数据从硬盘的文件中读到内存中
3.IO流分类
1.字节流:一切皆字节,万能流
复制的话要用字节流
字节输出流:OutputStream
字节输入流:InputStream
2.字符流:操作文本文档的->能用记事本打开,人能看懂的
.css .html .txt .java
字符输出流:Writer
字符输入流:Reader
3.单词考验
FileOutputStream->字节输出流
FileInputStream->字节输入流
FileWriter-> 字符输出流
FileReader-> 字符输入流
BufferedOutputStream->缓冲字节输出流
BufferedInputStream->缓冲字节输入流
BufferedWriter->缓冲字符输出流
BufferedReader->缓冲字符输入流
ObjectOutputStream->序列化流
ObjectInputStream->反序列化流
OutputStreamWriter->转换流->写数据
InputStreamReader->转换流->读数据
printStream->打印流->输出流
4.OutputStream中子类[FileOutputStream]的介绍以及方法的简单介绍
1.字节输出流:OutputStream->抽象类
2.OutputStream是抽象类,不能new对象,所以我们需要学习子类
FileOutputStream
3.构造:
FileOutputStream(File file)
FileOutputStream(String name)
以上两个方法new对象的时候参数要指明文件路径
4.注意:输出流在写数据的时候,如果指定的文件没有,会自动创建
5.方法:
void write(int b) -> 一次写一个字节
void write(byte[] b) -> 一次写一个字节数组
void write(byte[] b, int off, int len) -> 一次写一个字节数组一部分
b:要写的数组
off:从数组的哪个索引开始写
len:写多少个
void close() -> 关闭流对象
public class Test01_FileOutputStream {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("day19\io\out.txt");
//void write(int b) -> 一次写一个字节
fos.write(97);
//关流
fos.close();
}
}
public class Test02_FileOutputStream {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("day19\io\out.txt");
//void write(byte[] b) -> 一次写一个字节数组
byte[] bytes = {97,98,99,100};
fos.write(bytes);
//关流
fos.close();
}
}
public class Test03_FileOutputStream {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("day19\io\out.txt");
/*
void write(byte[] b, int off, int len) -> 一次写一个字节数组一部分
b:要写的数组
off:从数组的哪个索引开始写
len:写多少个
*/
byte[] bytes = {97,98,99,100,101,102};
fos.write(bytes,0,3);
//关流
fos.close();
}
}
public class Test04_FileOutputStream {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("day19\io\out.txt");
// byte[] bytes = {-28, -67, -96};
fos.write("97".getBytes());
fos.write("你好".getBytes());
fos.write("中国".getBytes());
//关流
fos.close();
}
}
//字节输出流续写追加
FileOutputStream(String name, boolean append) ->实现追加功能
append:false-> 不追加,会覆盖
append:true-> 不覆盖了,直接追加
//换行-> 换行符
\r\n -> 占两个字节
public class Test04_FileOutputStream {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("day19\io\out.txt",true);
// byte[] bytes = {-28, -67, -96};
fos.write("97".getBytes());
fos.write("你好".getBytes());
fos.write("中国".getBytes());
//关流
fos.close();
}
}
public class Test04_FileOutputStream {
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("day19\io\out.txt",true);
// byte[] bytes = {-28, -67, -96};
/*fos.write("97".getBytes());
fos.write("\r\n".getBytes());
fos.write("你好".getBytes());
fos.write("\r\n".getBytes());
fos.write("中国".getBytes());*/
fos.write("你好\r\n".getBytes());
//关流
fos.close();
}
}
第四章.哈希表结构存储过程
1.HashMap底层数据数据结构:哈希表
2.jdk7:哈希表 = 数组+链表
jdk8:哈希表 = 数组+链表+红黑树
3.
先算哈希值,此哈希值在HashMap底层经过了特殊的计算得出
如果哈希值不一样,直接存
如果哈希值一样,再去比较内容,如果内容不一样,也存
如果哈希值一样,内容也一样,直接去重复(后面的value将前面的value覆盖)
哈希值一样,内容不一样->哈希冲突(哈希碰撞)
4.要知道的点:
a.在不指定长度时,哈希表中的数组默认长度为16,HashMap创建出来,一开始没有创建长度为16的数组
b.什么时候创建的长度为16的数组呢?在第一次put的时候,底层会创建长度为16的数组
c.哈希表中有一个数据加[加载因子]->默认为0.75(加载因子)->代表当元素存储到百分之75的时候要扩容了->2倍
d.如果对个元素出现了哈希值一样,内容不一样时,就会在同一个索引上以链表的形式存储,当链表长度达到8并且当前数组长度>64时,链表就会改成使用红黑树存储
e.加入红黑树目的:查询快
外面笔试时可能会问到的变量
default_initial_capacity:HashMap默认容量 16
default_load_factor:HashMap默认加载因子 0.75f
threshold:扩容的临界值 等于 容量*0.75 = 12 第一次扩容
treeify_threshold:链表长度默认值,转为红黑树:8
min_treeify_capacity:链表被树化时最小的数组容量:64
第五章.哈希表源码分析
1.HashMap无参数构造方法的分析
//HashMap中的静态成员变量
static final float DEFAULT_LOAD_FACTOR = 0.75f;
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
解析:使用无参数构造方法创建HashMap对象,将加载因子设置为默认的加载因子,loadFactor=0.75F。
2.HashMap有参数构造方法分析
HashMap(int initialCapacity, float loadFactor) ->创建Map集合的时候指定底层数组长度以及加载因子
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);//10
}
解析:带有参数构造方法,传递哈希表的初始化容量和加载因子
-
如果initialCapacity(初始化容量)小于0,直接抛出异常。
-
如果initialCapacity大于最大容器,initialCapacity直接等于最大容器
- MAXIMUM_CAPACITY = 1 << 30 是最大容量 (1073741824)
-
如果loadFactor(加载因子)小于等于0,直接抛出异常
-
tableSizeFor(initialCapacity)方法计算哈希表的初始化容量。
- 注意:哈希表是进行计算得出的容量,而初始化容量不直接等于我们传递的参数。
3.tableSizeFor方法分析
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
8 4 2 1规则->无论指定了多少容量,最终经过tableSizeFor这个方法计算之后,都会遵循8421规则去初始化列表容量
解析:该方法对我们传递的初始化容量进行位移运算,位移的结果是 8 4 2 1 码
- 例如传递2,结果还是2,传递的是4,结果还是4。
- 例如传递3,结果是4,传递5,结果是8,传递20,结果是32。
4.Node 内部类分析
哈希表是采用数组+链表的实现方法,HashMap中的内部类Node非常重要,证明HashSet是一个单向链表
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
解析:内部类Node中具有4个成员变量
- hash,对象的哈希值
- key,作为键的对象
- value,作为值得对象(讲解Set集合,不牵扯值得问题)
- next,下一个节点对象
5.存储元素的put方法源码
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
解析:put方法中调研putVal方法,putVal方法中调用hash方法。
- hash(key)方法:传递要存储的元素,获取对象的哈希值
- putVal方法,传递对象哈希值和要存储的对象key
6.putVal方法源码
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
解析:方法中进行Node对象数组的判断,如果数组是null或者长度等于0,那么就会调研resize()方法进行数组的扩容。
7.resize方法的扩容计算
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
解析:计算结果,新的数组容量=原始数组容量<<1,也就是乘以2。
8.确定元素存储的索引
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
解析:i = (数组长度 - 1) & 对象的哈希值,会得到一个索引,然后在此索引下tab[i],创建链表对象。
不同哈希值的对象,也是有可能存储在同一个数组索引下。
其中resize()扩容的方法,默认是16
tab[i] = newNode(hash, key, value, null);->将元素放在数组中 i就是索引
i = (n - 1) & hash
0000 0000 0000 0000 0000 0000 0000 1111->15
& 0&0=0 0&1=0 1&1=1
0000 0000 0000 0001 0111 1000 0110 0011->96355
--------------------------------------------------------
0000 0000 0000 0000 0000 0000 0000 0011->3
0000 0000 0000 0000 0000 0000 0000 1111->15
& 0&0=0 0&1=0 1&1=1
0000 0000 0001 0001 1111 1111 0001 0010->1179410
--------------------------------------------------------
0000 0000 0000 0000 0000 0000 0000 0010->2
9.遇到重复哈希值的对象
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
解析:如果对象的哈希值相同,对象的equals方法返回true,判断为一个对象,进行覆盖操作。
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
解析:如果对象哈希值相同,但是对象的equals方法返回false,将对此链表进行遍历,当链表没有下一个节点的时候,创建下一个节点存储对象。
第六章.字节流
1.InputStream子类[FileInputStream]的介绍以及方法的使用
1.概述:字节输入流->抽象类
2.子类:FileInputStream->读数据
3.构造:
FileInputStream(File file)
FileInputStream(String name)
4.方法:
int read()->一次读一个字节
int read(byte[] b)->一次读一个字节数组
int read(byte[] b, int off, int len)->一次读一个字节数组一部分
b:要读取的字节数组
off:从数组的哪个索引开始读
len:读多少个
void close() -> 关流
2.一次读取一个字节
public class Test01_FileInputStream {
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream("day20\io\in.txt");
method01(fis);
}
/*
int read()->一次读一个字节,返回的是读取的字节
*/
public static void method01(FileInputStream fis) throws IOException {
/*int read1 = fis.read();
System.out.println((char) read1);
int read2 = fis.read();
System.out.println((char) read2);
int read3 = fis.read();
System.out.println((char) read3);
int read4 = fis.read();
System.out.println((char) read4);
int read5= fis.read();
System.out.println(read5);
int read6 = fis.read();
System.out.println(read6);*/
//循环读取
//定义一个变量,接收读取的内容
int len;
while((len = fis.read())!=-1){
System.out.println((char)len);
}
fis.close();
}
}
1.循环读取的时候,不要判断的时候读一次,输出的时候再读一次
2.如果一个流对象提前关闭了,此流对象就不能再使用了
3.IO流对象,如果将文件中的内容读完了,就不能再读了
3.读取-1的问题
1.每个文件最后(末尾),都有一个结束标记,此结束标记看不见,摸不着
4.一次读取一个字节数组以及过程
/*
int read(byte[] b)->一次读一个字节数组,返回的是读取的个数
*/
public static void method02(FileInputStream fis) throws Exception {
/*
创建byte数组
数组作用:看做是一个临时存储区域,我读取的数据会先放到数组中,然后我们从数组中拿数据
数组一般长度会定成1024或者其倍数
*/
/* byte[] bytes = new byte[2];
int read01 = fis.read(bytes);
//System.out.println(read01);//2 代表读了2个
System.out.println(new String(bytes,0,read01));
int read02 = fis.read(bytes);
//System.out.println(read02);//2 代表读了2个
System.out.println(new String(bytes,0,read01));
int read03 = fis.read(bytes);
//System.out.println(read03);//1 代表读了1个
System.out.println(new String(bytes,0,read03));
int read04 = fis.read(bytes);
System.out.println(read04);*/
byte[] bytes = new byte[5];
//定义一个变量,表示读取的个数
int len;
while((len = fis.read(bytes))!=-1){
System.out.println(new String(bytes,0,len));
}
fis.close();
}
5.字节流实现图片复制分析
6.字节流实现图片复制代码实现
public class Test02_CopyFile {
public static void main(String[] args)throws Exception {
//1.创建FileInputStream用于读取文件
FileInputStream fis = new FileInputStream("E:\Idea\io\5.jpg");
//2.创建FileOutputStream用于将读取到内存中的文件写到另外的位置
FileOutputStream fos = new FileOutputStream("E:\Idea\io\file\李成敏.jpg");
//3.定义一个数组
byte[] bytes = new byte[1024];
//4.定义一个变量,接收读取的字节个数
int len;
while((len = fis.read(bytes))!=-1){
//5.将读取的字节写到指定位置
fos.write(bytes,0,len);
}
//关流->先开后关
fos.close();
fis.close();
}
}
第七章.字符流
1.字节流读取中文的问题
public class Test02_FileInputStream {
public static void main(String[] args)throws Exception {
FileInputStream fis = new FileInputStream("day20\io\in.txt");
byte[] bytes = new byte[2];
int len;
while((len = fis.read(bytes))!=-1){
System.out.println(new String(bytes,0,len));
}
fis.close();
}
}
如果文件中存中文,读取的时候会有编码问题:
GBK:一个中文占2个字节
UTF-8: 一个中文占3个字节
注意:字节流确实是万能流,但是不要边读边看(输出)
字符流在操作文本文档时,[编码一致的情况下],边读边看,没有问题
2.FileReader的介绍以及使用
1.字符输入流:Reader-> 抽象类
2.子类:FileReader -> 读字符
3.构造:
FileReader(File file)
FileReader(String fileName)
4.方法:
int read()->一次读取一个字符
int read(char[] cbuf) -> 一次读取一个字符数组,返回的是读取个数
int read(char[] cbuf, int off, int len) -> 一次读取一个字符数组一部分
cbuf:要读取的字符数组
off:从数组哪个索引开始读
len:读多少个
void close() -> 关流
public class Test01_FileReader {
public static void main(String[] args) throws Exception {
/*
int read()->一次读取一个字符,返回的是读取的内容
*/
FileReader fr = new FileReader("day20\io\read.txt");
/* int read01 = fr.read();
System.out.println((char) read01);
int read02 = fr.read();
System.out.println((char) read02);
int read03 = fr.read();
System.out.println((char) read03);
int read04 = fr.read();
System.out.println((char) read04);*/
//int read05 = fr.read();
//System.out.println(read05);//-1
//循环读取
int len;
while((len = fr.read())!=-1){
System.out.println((char) len);
}
//关流
fr.close();
}
}
public class Test02_FileReader {
public static void main(String[] args)throws Exception {
//int read(char[] cbuf) -> 一次读取一个字符数组,返回的是读取个数
FileReader fr = new FileReader("day20\io\read.txt");
//定义数组
/* char[] chars = new char[2];
int read01 = fr.read(chars);
System.out.println(new String(chars));
int read02 = fr.read(chars);
System.out.println(new String(chars));
*/
char[] chars = new char[4];
//循环读取
int len;
while((len = fr.read(chars))!=-1){
System.out.println(new String(chars,0,len));
}
//关流
fr.close();
}
}
3.FileWriter的介绍以及使用
1.概述:字符输出流:Writer -> 抽象类
2.子类:FileWriter
3.构造:
FileWriter(File file)
FileWriter(String fileName)
FileWriter(String fileName, boolean append)
append:true-> 追加,续写
false-> 会创建新文件覆盖老文件
4.方法:
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)->一次写一个字符串的一部分
void flush() -> 刷新缓冲区
5.注意:
字符流,底层自带一个缓冲区
我们要是写数据的话,写的数据会先放到缓冲区中,我们需要将缓冲区中的数据刷到硬盘的文件中
public class Test01_FileWriter {
public static void main(String[] args)throws Exception {
FileWriter fw = new FileWriter("day20\io\write.txt",true);
//void write(char[] cbuf) -> 一次写一个字符数组
char[] chars = {'中','国','你','好'};
fw.write(chars);
//刷新缓冲区
//fw.flush();
//关流-> 自带刷新功能,先刷新,后关流
fw.close();
}
}
public class Test02_FileWriter {
public static void main(String[] args)throws Exception {
FileWriter fw = new FileWriter("day20\io\write.txt");
fw.write("床前明月光");
fw.write("\r\n");
fw.write("疑是地上霜");
fw.write("\r\n");
fw.write("举头望明月");
fw.write("\r\n");
fw.write("低头思故乡");
fw.write("\r\n");
fw.write("两眼泪汪汪\r\n");
fw.write("来碗饺子汤");
//刷新或者关流
fw.close();
}
}
4.FileWriter的刷新功能和关闭功能
flush:刷新,刷新完毕之后,流对象还能使用
close:先刷新,后关闭,关闭之后,流对象不能再次使用
public class Test03_FileWriter {
public static void main(String[] args)throws Exception {
FileWriter fw = new FileWriter("day20\io\write1.txt");
fw.write("离离原上草");
fw.write("一岁一枯荣");
fw.write("野火烧不尽");
fw.write("春风吹又生");
//刷新
fw.flush();
fw.write("喜洋洋");
fw.flush();
//关流
fw.close();
fw.write("懒洋洋");//fw对象用不了了
}
}
5.IO异常处理的方式
public class Test04_FileWriter {
public static void main(String[] args) {
FileWriter fw = null;
try {
fw = new FileWriter("day20\io\write2.txt");
fw.write("翠儿");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fw!=null){//如果fw没有创建成功,依然是null,如果是null,就没必要close了
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
6.JDK7之后io异常处理方式->(扩展)
1.格式:
try(io对象){
可能出现异常的代码
}catch(异常对象 对象名){
}
2.注意:用以上格式处理异常,会自动管理IO流对象
public class Test05_FileWriter {
public static void main(String[] args) {
try(FileWriter fw = new FileWriter("day20\io\write2.txt")){
fw.write("柳岩");
}catch (Exception e){
e.printStackTrace();
}
}
}
第八章.字节缓冲流
1.和普通的字节流有什么区别
a.有缓冲区,默认大小8192
b.缓冲流在读写时,都会将数据先放到缓冲区中,所以缓冲区的读写动作,不是直接调用的native方法,而是在内存中进行读写,效率会高
c.我们使用缓冲流可以尽量减少直接对系统进行读写的操作,减少了系统资源的占用
2.字节缓冲输出流:BufferedOutputStream
a.构造:
BufferedOutputStream(OutputStream out)
OutputStream:抽象类
需要传递OutputStream的子类对象
b.用法:
和FileOutputStream一样
3.字节缓冲输入流:BufferedInputStream
a.构造:
BufferedInputStream(InputStream in)
InputStream:抽象类
需要传递InputStream的子类对象
b.用法:
和FileInputStream一样
public class Demo01_BufferedInAndOut {
public static void main(String[] args)throws Exception {
//method01();
//method02_buffered();
}
private static void method02_buffered()throws Exception {
long start = System.currentTimeMillis();
//1.创建FileInputStream用于读取文件
FileInputStream fis = new FileInputStream("E:\Idea\io\集合.avi");
BufferedInputStream bis = new BufferedInputStream(fis);
//2.创建FileOutputStream用于将读取到内存中的文件写到另外的位置
FileOutputStream fos = new FileOutputStream("E:\Idea\io\新集合.avi");
BufferedOutputStream bos = new BufferedOutputStream(fos);
//3.定义一个数组
byte[] bytes = new byte[1024];
//4.定义一个变量,接收读取的字节个数
int len;
while((len = bis.read(bytes))!=-1){
//5.将读取的字节写到指定位置
bos.write(bytes,0,len);
}
long end = System.currentTimeMillis();
System.out.println(end-start);
//关流->先开后关
bos.close();
bis.close();
}
private static void method01()throws Exception {
long start = System.currentTimeMillis();
//1.创建FileInputStream用于读取文件
FileInputStream fis = new FileInputStream("E:\Idea\io\集合.avi");
//2.创建FileOutputStream用于将读取到内存中的文件写到另外的位置
FileOutputStream fos = new FileOutputStream("E:\Idea\io\新集合.avi");
//3.定义一个数组
byte[] bytes = new byte[1024];
//4.定义一个变量,接收读取的字节个数
int len;
while((len = fis.read(bytes))!=-1){
//5.将读取的字节写到指定位置
fos.write(bytes,0,len);
}
long end = System.currentTimeMillis();
System.out.println(end-start);
//关流->先开后关
fos.close();
fis.close();
}
}
第九章.字符缓冲流
1.字符缓冲输出流_BufferedWriter
1.构造:
BufferedWriter(Writer w)
2.方法:
和FileWriter一样
3.特有方法:
newLine()->换行
public class Demo02_BufferedWriter {
public static void main(String[] args)throws Exception {
BufferedWriter bw =
new BufferedWriter(new FileWriter("day20\io\bufferedwriter.txt",true));
bw.write("东临碣石");
bw.newLine();
bw.write("以观沧海");
bw.newLine();
bw.close();
}
}
2.字符缓冲输入流_BufferedReader
1.构造:
BufferedReader(Reader r)
2.方法:
和FileReader一样
3.特有方法:
String readLine()-> 一次读一行
public class Demo03_BufferedReader {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new FileReader("day20\io\bufferedreader.txt"));
/* String s1 = br.readLine();
System.out.println(s1);
String s2 = br.readLine();
System.out.println(s2);
String s3 = br.readLine();
System.out.println(s3);
String s4 = br.readLine();
System.out.println(s4);
String s5 = br.readLine();
System.out.println(s5);*/
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
}
}
3.字符缓冲流练习
将in.txt中的内容排好序,写到另外一个新文件中
c.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必得裨补阙漏,有所广益。
h.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
d.将军向宠,性行淑均,晓畅军事,试用之于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
b.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。
a.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。
i.今当远离,临表涕零,不知所言。
f.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
g.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐付托不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
e.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。
实现步骤:
1.创建BufferedReader用于读取in.txt中的内容
2.创建ArrayList集合,用于存储读取出来的内容
3.调用Collections.sort方法,进行排序
4.创建BufferedWriter,用于将集合中排好序的内容写到out.txt中
5.遍历ArrayList集合,边遍历边写
6.关流
public class Demo04_Buffered {
public static void main(String[] args)throws Exception {
//1.创建BufferedReader用于读取in.txt中的内容
BufferedReader br = new BufferedReader(new FileReader("day20\io\in.txt"));
//2.创建ArrayList集合,用于存储读取出来的内容
ArrayList<String> list = new ArrayList<>();
String line;
while((line = br.readLine())!=null){
list.add(line);
}
//3.调用Collections.sort方法,进行排序
Collections.sort(list);
//4.创建BufferedWriter,用于将集合中排好序的内容写到out.txt中
BufferedWriter bw = new BufferedWriter(new FileWriter("day20\io\out.txt"));
//5.遍历ArrayList集合,边遍历边写
for (String s : list) {
bw.write(s);
bw.newLine();
}
//6.关流
bw.close();
br.close();
}
}
第十章.转换流
1.字符编码
计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。[按照某种规则,将字符存储到计算机中,称为编码] 。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码 。比如说,按照A规则存储,同样按照A规则解析,那么就能显示正确的文本f符号。反之,按照A规则存储,再按照B规则解析,就会导致乱码现象。
- 字符编码
Character Encoding: 就是一套自然语言的字符与二进制数之间的对应规则。
2.字符集
- 字符集
Charset:也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。
计算机要准确的存储和识别各种字符集符号,需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBK字符集、Unicode字符集等。
可见,当指定了编码,它所对应的字符集自然就指定了,所以编码才是我们最终要关心的。
-
ASCII字符集 :
- ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)。
- 基本的ASCII字符集,使用7位(bits)表示一个字符,共128字符。ASCII的扩展字符集使用8位(bits)表示一个字符,共256字符,方便支持欧洲常用字符。
-
ISO-8859-1字符集:
- 拉丁码表,别名Latin-1,用于显示欧洲使用的语言,包括荷兰、丹麦、德语、意大利语、西班牙语等。
- ISO-8859-1使用单字节编码,兼容ASCII编码。
-
GBxxx字符集:
- GB就是国标的意思,是为了显示中文而设计的一套字符集。
- GB2312:简体中文码表。一个小于127的字符的意义与原来相同。但两个大于127的字符连在一起时,就表示一个汉字,这样大约可以组合了包含7000多个简体汉字,此外数学符号、罗马希腊的字母、日文的假名们都编进去了,连在ASCII里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了。
- GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等。
- GB18030:最新的中文码表。收录汉字70244个,采用多字节编码,每个字可以由1个、2个或4个字节组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等。
-
Unicode字符集 :
-
Unicode编码系统为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国码。
-
它最多使用4个字节的数字来表达每个字母、符号,或者文字。有三种编码方案,UTF-8、UTF-16和UTF-32。最为常用的UTF-8编码。
-
UTF-8编码,可以用来表示Unicode标准中任何字符,它是电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。所以,我们开发Web应用,也要使用UTF-8编码。它使用一至四个字节为每个字符编码,编码规则:
- 128个US-ASCII字符,只需一个字节编码。
- 拉丁文等字符,需要二个字节编码。
- 大部分常用字(含中文),使用三个字节编码。
- 其他极少使用的Unicode辅助字符,使用四字节编码。
-
1.如果想要不出现乱码问题,编码和解码遵循的编码规则要一样
2.在GBK中,一个中文占2个字节
3.在UTF-8中,一个中文占3个字节
3.转换流_InputStreamReader
1.作用:在读取的时候,可以指定按照什么编码去读
2.构造:
InputStreamReader(InputStream in, String charsetName)
in:抽象类,传递子类
charsetName:指定的编码表 不区分大小写
3.使用:
和FileReader一样
public class Demo01_InputStreamReader {
public static void main(String[] args)throws Exception {
InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\Idea\io\test.txt"),"GBK");
int read = isr.read();
System.out.println((char)read);
isr.close();
}
}
4.转换流_OutputStreamWriter
1.作用:按照指定的编码规则去写数据,指定什么规则,就按照什么规则去保存数据
2.构造:
OutputStreamWriter(OutputStream out,String charsetName)
out:抽象类,传递子类对象
charsetName:指定的编码表,不区分大小写
3.使用:
和FileWriter一样
public class Demo02_OutputStreamWriter {
public static void main(String[] args)throws Exception {
OutputStreamWriter osw =
new OutputStreamWriter(new FileOutputStream("E:\Idea\io\out.txt"),"GBK");
osw.write("你");
osw.close();
}
}