本帖最后由 小石姐姐 于 2018-8-9 10:53 编辑
一、线程池
1、线程间通信
多个线程在处理同一资源时即存在竞争也存在合作关系,多个线程并发执行时, 在默认情况下CPU是随机切换线程的。
等待唤醒机制
wait/notify 就是线程间的一种协作机制, 用于解决线程间通信的问题
java.lang.Object类:
[Java] 纯文本查看 复制代码 Object obj= new Ojbect();
成员方法 (只能通过"锁对象"调用)
[Java] 纯文本查看 复制代码 void notify(): 随机唤醒在同一个锁对象上的某一个处于等待状态的线程
[font=微软雅黑][size=3] [/size][/font]obj.notify();
[font=微软雅黑][size=3] [/size][/font]void notifyAll(): 唤醒所有在同一个锁对象上处于等待状态的线程
[font=微软雅黑][size=3] [/size][/font]obj.notifyAll();
[font=微软雅黑][size=3] [/size][/font]void wait(): 让当前线程处于无限等待状态, 同时释放锁
[font=微软雅黑][size=3] [/size][/font]obj.wait();
wait和notify/notifyAll的执行原理:
执行wait,进入wait set中,处于“WAITING”,不去竞争锁程状态,等待“notify通知",被通知后也不能立即恢复执行(当初中断的地方是在同步块内, 而此刻它已经不持有锁, 所以它需要再次尝试去获取锁(很可能面临其它线程的竞争)),获取锁后恢复执行,获取失败进入锁阻塞。
调用 wait() 和 notify() 需要注意的细节:
1. wait() 与 notify() 必须要由"同一个锁对象"调用
2. wait() 与 notify() 是属于Object类的方法
因为锁对象可以是任意对象, 而任意对象的所属类都是继承了Object类的
3. wait() 与 notify() 必须要在"同步代码块"或者是"同步方法"中使用
因为必须要通过锁对象调用这2个方法
线程状态图:
2、线程池
线程池:一个容纳多个线程的容器
线程池的代码实现
java.util.concurrent.Executors类: 线程池工厂类, 用于管理线程池
静态方法:
[Java] 纯文本查看 复制代码 static ExecutorService newFixedThreadPool(int nThreads): 创建固定数量线程的线程池(常用)
java.util.concurrent.ExecutorService接口: 真正执行任务的线程池服务 成员方法:
[Java] 纯文本查看 复制代码 Future submit(Runnable task): 提交一个Runnable任务
void shutdown(): 通知线程执行完任务后关闭. 如不调此方法, 则线程执行完任务后仍在运行以便重复使用 线程池的创建和使用步骤:
1. 使用Executors的静态方法 newFixedThreadPool(int nThreads) 创建线程池ExecutorService
[Java] 纯文本查看 复制代码 ExecutorService executorService = Executors.newFixedThreadPool(2);
2. 创建一个任务类, 实现Runnable接口, 重写run()方法
3. 调用ExecutorService对象的 submit(Runnable task) 方法, 传递任务给线程池, 执行任务
[Java] 纯文本查看 复制代码 executorService.submit(new RunnableImpl());
4. 调用ExecutorService对象的 shutdown() 方法, 销毁线程池 (不建议执行)
[Java] 纯文本查看 复制代码 executorService.shutdown();
二、Lambda表达式
1、Lambda表达式的3个部分:
1. 一些参数 ()
接口中抽象方法的参数列表. 没参数就空着; 有参数就写, 多个参数用逗号分隔
2. 一个箭头 ->
将参数传递给方法体
3. 一段代码 {}
重写接口抽象方法的方法体
Lambda标准格式:
// 写成一行
(参数列表) -> {一些重写方法的代码}
// 写成多行
(参数列表) -> {
一些重写方法的代码
}
2、 函数式编程和面向对象方式的对比
面向对象方式的代码
[Java] 纯文本查看 复制代码 new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "新线程创建了");
}
}).start(); 函数式编程的代码
[AppleScript] 纯文本查看 复制代码 new Thread(()-> {
System.out.println(Thread.currentThread().getName() + "新线程创建了");
[font=微软雅黑][size=3] [/size][/font]}
[font=微软雅黑][size=3] [/size][/font]).start(); 案例:厨师喊吃饭
[Java] 纯文本查看 复制代码 /*
厨师接口
*/
public interface Cook {
void makeFood();
}
/*
Lambda标准格式: 厨师做饭(无参无返回值)
[Java] 纯文本查看 复制代码 */
public class Test {
public static void main(String[] args) {
// Lambda表达式方式
invodeCook(()->{
System.out.println("吃饭啦");
});
}
// 调用厨师做饭
public static void invodeCook(Cook cook) {
cook.makeFood();
}
}
案例: 数组排序 匿名内部类和Lambda表达式
匿名内部类
[Java] 纯文本查看 复制代码 // Arrays.sort(persons, new Comparator<Person>() {
// @Override
// public int compare(Person o1, Person o2) {
// return o1.getAge() - o2.getAge();
// }
// }); Lambda表达式
[Java] 纯文本查看 复制代码 Arrays.sort(persons, (Person o1, Person o2) -> {
return o1.getAge() - o2.getAge();
}); 3、Lambda省略格式和使用前提
函数式接口"有且仅有一个抽象方法"
但函数式接口对于哪些方法算作抽象方法有特殊规定:
1>有方法体的方法"不算作"抽象方法, 如默认方法, 静态方法, 私有方法
2> 抽象方法与java.lang.Object类中的方法定义相同的, 也"不算作"抽象方法
因为任何实现本接口的实现类, 都会直接或间接继承java.lang.Object类的public的方法, 所以在创建
实现类时其实不用重写该抽象方法, 也就不算作抽象方法
总结:
Lambda表达式的使用前提:
1. Lambda只能用于接口, 且"接口中有且仅有一个抽象方法"(也称为"函数式接口") 普通类, 抽象类不能用
2. 使用Lambda必须具有上下文推断
方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例
(简而言之: 作为参数类型的接口, 必须是函数式接口)
问题分析:
需求:10个顾客,2张餐桌,2个服务员.同时只能2人进餐.
[Java] 纯文本查看 复制代码 public class Myrunnable implements Runnable {
static Object obj=new Object();
@Override
public void run() {
// Object obj=new Object();
synchronized (obj){
System.out.println(Thread.currentThread().getName()+"服务员开始为顾客点菜.......顾客开始用餐");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("顾客食用完毕"+Thread.currentThread().getName()+"服务员收拾桌子");
}
}
} 实现类
[Java] 纯文本查看 复制代码 public class Demo00 {
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(2);
for (int i = 0; i < 10; i++) {
es.submit(new Myrunnable());
}
es.shutdown(); 该需求不需要锁对象
原因:买票案例中private int booking=100;是成员变量,是各线程共有的;
而本案例中顾客只在方法内体现,不被共享,所以不用锁对象;
如果用锁对象该案例应如何编写程序?
[Java] 纯文本查看 复制代码 ExecutorService es = Executors.newFixedThreadPool(2);
for (int i = 0; i < 10; i++) {
es.submit(new Myrunnable());
此代码无法实现lock;
因为每次new Myrunnable()便进行一次new Object(),synchronized (obj)进行中的锁对象每次都不相同;static Object obj=new Object();应为静态成员对象。
三、递归
1、递归概括
方法内部调用方法自己 (所以必须定义方法);
递归的使用前提:
调用方法时, 方法的主体不变, 但每次传递的参数值不同, 可以使用递归;
递归时的注意事项:
1> 递归要有限定条件(出口), 保证递归能够停止(就是在某种情况下方法不再调用自己), 否则会栈内存溢出
2> 递归次数不能太多, 否则会栈内存溢出
3>构造方法不能递归
2、案例:
计算1~n的和
[Java] 纯文本查看 复制代码 public class Test {
public static void main(String[] args) {
int sum1 = sum(3);
System.out.println(sum1);
}
public static int sum(int ) {
// 出口
if (n == 1) {
return 1;
}
return n+ sum(n- 1);
}} 四、IO流
1、File类
1.1 File类的静态成员变量
java.io.File类: 文件和目录的路径名的抽象表现形式, 主要用于文件和目录的创建, 查找和删除等操作
静态成员变量
[Java] 纯文本查看 复制代码 static String pathSeparator: 路径分隔符的字符串形式
static char pathSeparatorChar: 路径分隔符的char形式 Windows系统是 分号;
Linux系统是 冒号:
[Java] 纯文本查看 复制代码 static String separator: 文件名称分隔符的字符串形式
static char separatorChar: 文件名称分隔符的char形式 Window系统是 反斜杠\
Linux系统是 正斜杠/
1.2 绝对路径和相对路径
绝对路径:
以盘符开始的路径
如: "D:\\a\\hi.txt"
相对路径:
不以盘符开始的简化路径. 相对于项目的根目录
如: "a\\1.mp3", "123.txt"
注意事项:
1.路径不区分大小写 (在Windows系统中不区分大小写, Linux, Mac区分)
2.路径一般写成字符串, 而字符串中一个\是转义, 所以要写两个\\(正则表达式)
1.3 File类: 构造、获取、判断方法
构造
[Java] 纯文本查看 复制代码 File File(String pathname): 根据 路径字符串 封装一个File对象
File f1 = new File("z:\\z.txt");
File File(String parent, String child): 根据 父路径字符串 和 子路径字符串 封装File对象
File f2 = new File("a\\", "a.txt");
File File(File parent, String child): 根据 父路径的File对象 和 子路径 封装File对象
File f3 = new File(new File("d:\\"), "d.txt"); 获取
[Java] 纯文本查看 复制代码 String getAbsolutePath(): 返回此File的绝对路径名字符串
String s=f3.getAbsolutePath();
String getPath(): 获取File对象的封装路径 (创建对象时传入的路径)
String s=f3.getPath();
String getName(): 获取File对象的文件名或目录名
String s=f3.getName();
long length(): 获取File表示的文件大小的字节数 (不能获取目录的大小)
long l=f3.length(); 判断
[Java] 纯文本查看 复制代码 boolean exists(): 判断File对象代表的文件或目录是否实际存在
boolean b=f3.exists();
boolean isDirectory(): 判断File表示的是否为目录
boolean b=f3.isDirectory();
boolean isFile(): 判断File表示的是否为文件
boolean b=f3.isFile(); 1.4 File类: 创建、删除、遍历方法
创建、删除
[Java] 纯文本查看 复制代码 boolean createNewFile(): 当文件不存在时, 创建一个新的空文件
File fileB = new File("day08\\a\\b\\b.txt");
fileB.createNewFile();
boolean delete(): 删除由此File表示的文件或目录. (删除目录时必须是空目录)
fileB. delete();
boolean mkdir(): 创建File表示的目录
File dirA = new File("day08\\a");
dirA.mkdir();
boolean mkdirs(): 创建File表示的多级目录
File dirAB = new File("day08\\a\\b");
dirAB.mkdirs(); 注意事项:
重要英文单词的含义: (起变量名时会用到)
file: 文件
directory: 目录
path: 路径
遍历
[Java] 纯文本查看 复制代码 String[] list(): 获取当前File目录下的所有子文件或目录的名字数组
File[] files = module.listFiles();
File[] listFiles(): 获取当前File目录中的所有子文件或目录的File对象数组
String[] list = module.list(); 1.5 FileFilter文件过滤器的原理和使用
java.io.File类: Filter过滤器
[Java] 纯文本查看 复制代码 File[] listFiles(FileFilter filter): 通过File对象过滤, 返回文件过滤器过滤后的File对象数组
File[] listFiles(FilenameFilter filter): 通过File对象的文件名过滤, 返回文件过滤器过滤后的File对象数组 java.io.FileFilter接口: 用于File对象的过滤器
[Java] 纯文本查看 复制代码 boolean accept(File pathName): true则会将参数的File对象加入返回的File[], false则不加入
java.io.FilenameFilter接口:
boolean accept(File dir, String name): true则会将参数的File对象加入返回的File[], false则不加入
综合案例
补充String类中的方法:
[AppleScript] 纯文本查看 复制代码 boolean endsWith(String suffix): 判断字符串是否以指定的字符串结尾
String toLowerCase(): 将字符串转为小写字母
需求: 打印当前模块中的.java文件 (包含子目录)
[Java] 纯文本查看 复制代码 public class Test {
public static void main(String[] args) {
print(new File("day08"));
}
public static void print(File dir) {
// 获取当前目录中的文件, 使用过滤器
/*File[] files = dir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return new File(dir, name).isDirectory() ||
name.toLowerCase().endsWith(".java");
}
});*/ [Java] 纯文本查看 复制代码 // 使用Lambda表达式简化
File[] files = dir.listFiles((d, name) -> new File(d, name).isDirectory() ||
name.toLowerCase().endsWith(".java"));
for (File file : files) {
if (file.isFile()) {
System.out.println(file);
} else if (file.isDirectory()) {
print(file);
}
错题分析:
[Java] 纯文本查看 复制代码 public static void main(String[] args) {
List<File> list=new ArrayList<>();
File f=new File("F:\\IdeaProjects");
list= getlist(f);
for (File f1 : list) {
System.out.println(f1);
}
}
public static List<File> getlist(File f) {
List<File> list=new ArrayList<>();
File[] files = f.listFiles();
for (File fi : files) {
if(fi.isDirectory()){
list.add(fi);
(getlist(fi));
}if(fi.isFile()){
list.add(fi);
}
}
return list;
}
} 标红处方法被调用了,但值未返回
正确代码: list.addAll(getlist(fi));
2、IO流的分类:
字节流 (读写字节: byte, 可以读写所有类型的文件, 包括视频, 图片, 音频, 文本等)
字节输入流: java.io.InputStream 抽象类
字节输出流: java.io.OutputStream 抽象类
字符流 (读写字符: char, String, 只能读写文本文件)
字符输入流: java.io.Reader 抽象类
字符输出流: java.io.Writer 抽象类
2.1 字节流
1> FileOutputStream和FileInputStream
FileOutputStream
java.io.OutputStream抽象类: 字节输出流 (顶层类)
成员方法
void close() :释放资源
写字节的成员方法
[Java] 纯文本查看 复制代码 abstract void write(int b): 一次写一个字节 (参数是int是为了代码中写表示byte的整数方便不用强转)
fos.write(97);
void write(byte[] b): 一次写一个字节数组
byte[] bytes={1,2,3,}
fos.write(bytes);
void write(byte[] b, int off, int len): 一次写一个字节数组的一部分
byte[] bytes={1,2,3,}
fos.write(bytes,0,2); java.io.FileOutputStream类: 文件字节输出流 (向文件写数据)
写多个字节时:
如果第一个字节是正数中的 0~127, 则记事本会查询 ASCII码表 显示字符
如果第一个字节是负数中的:-128~-1, 则记事本会查询 GBK码表 显示字符.(将两个连续的byte组合为一个中文)
构造方法
[Java] 纯文本查看 复制代码 FileOutputStream(String name): 通过文件路径创建文件字节输出流
FileOutputStream fos = new FileOutputStream("day09\\testOutputStream1.txt");
FileOutputStream(File file): 通过File对象创建文件字节输出流 字节输出流: 续写操作
java.io.FileOutputStream类: 文件字节输出流
带有 续写 功能的构造方法, 不会清空文件
[Java] 纯文本查看 复制代码 FileOutputStream(String name, boolean append): 通过文件路径创建文件字节输出流, true可以续写
FileOutputStream(File file, boolean append): 通过File对象创建文件字节输出流, true可以续写 换行符:
Windows系统: "\r\n"
Linux系统: "\n"
MacOS系统: "\r"
FileInputStream
java.io.InputStream抽象类: 字节输入流 (顶层类)
常用成员方法
void close(): 释放资源
读数据的方法
[Java] 纯文本查看 复制代码 int read(): 一次读一个字节
int by; // int变量用来存储每次读到的数据
while ((by = fis.read()) != -1) {
System.out.print((char)by); // 不要换行, 文件中自带换行的byte
} 读到文件末尾返回-1 (返回int也是为了代码编写方便)
[Java] 纯文本查看 复制代码 int read(byte[] b): 一次读一个字节数组
读到的字节存放在参数中的字节数组中, 返回int值是本次读到的字节的个数. 读到文件末尾返回-1
[Java] 纯文本查看 复制代码 byte[] bytes = new byte[1024]; // 定义字节数组, 用于保存每次读取到的数据
while ((len = fis.read(bytes)) != -1) {
String s = new String(bytes, 0, len); // 将读取到的字节转换为字符串, 读到多少个就转换多少个
System.out.print(s);
}
while ((len = fis.read(bytes)) != -1) {
// 将读取到的字节数组转换为String, 读到多少就转换多少, 不要全转
String s = new String(bytes, 0, len);
java.io.FileInputStream类: 文件字节输入流构造方法
[Java] 纯文本查看 复制代码 FileInputStream(String name): 使用文件路径创建文件字节输入流
FileInputStream(File file): 使用File对象创建文件字节输入流 2> 字节缓冲流 BufferedInputStream(InputStream in)和BufferedOutputStream(OutputStream out)
构造方法:
[Java] 纯文本查看 复制代码 public BufferedInputStream(InputStream in) :创建一个 新的缓冲输入流。
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));
public BufferedOutputStream(OutputStream out) : 创建一个新的缓冲输出流。
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt")); 3> 对象序列化流: ObjectOutputStream和ObjectInputStream
对象(反)序列化流: ObjectOutputStream
java.io.ObjectOutputStream类: 对象字节输出流
构造方法
[Java] 纯文本查看 复制代码 ObjectOutputStream(OutputStream out)
特有成员方法
[Java] 纯文本查看 复制代码 void writeObject(Object obj): 将对象写出
创建对象输出流
[Java] 纯文本查看 复制代码 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student.txt"));
写对象
[AppleScript] 纯文本查看 复制代码 Student s = new Student("小美女", 18);
oos.writeObject(s); 释放资源
[Java] 纯文本查看 复制代码 oos.close();
注意:
被读写的对象的类, 必须实现 java.io.Serializable 接口, 否则会抛出 NotSerializableException
该类的所有属性也都必须是可序列化的. (实现 java.io.Serializable 接口)
如果某个属性不需要序列化, 则需要用 transient 修饰为瞬态的
如果要将集合序列化, 那么集合中存储的对象也必须是可序列化的
对象反序列化流: ObjectInputStream
java.io.ObjectInputStream类: 对象字节输入流
构造方法
[Java] 纯文本查看 复制代码 ObjectInputStream(InputStream in)
特有成员方法
[Java] 纯文本查看 复制代码 Object readObject(): 读取对象
创建对象输入流
[Java] 纯文本查看 复制代码 ObjectInputStream oos = new ObjectInputStream(new FileInputStream("student.txt"));
读对象
[Java] 纯文本查看 复制代码 Object o = oos.readObject();
Student s = (Student)o;
System.out.println(s); 释放资源
oos.close();
transient瞬态关键字: 避免属性序列化
static 修饰的成员变量属于类不属于对象, 所以不能序列化
transient 修饰的成员变量, 不能被序列化
如果对象的某个属性不希望被序列化, 可以使用transient修饰, 这样就不会被对象流写到文件中
4> 打印流PrintStream
PrintStream特点:
1. 只有输出流, 没有输入流
2. PrintStream永远不会抛出IOException
3. 有特殊方法 print(), println(), 可以输出任意类型的值
java.io.PrintStream类: 字节打印流
构造方法
[Java] 纯文本查看 复制代码 PrintStream(File file): 创建字节打印流, 输出到一个文件
PrintStream(OutputStream out): 创建字节打印流, 输出到一个字节输出流
PrintStream(String fileName): 创建字节打印流, 输出到一个文件路径 注意事项:
如果用 write() 方法, 会查编码表
如果用 print(), println(), 则原样输出
java.lang.System类:
静态方法
[Java] 纯文本查看 复制代码 static void setOut(PrintStream out): 设置System.out的输出目的地为参数的打印流
2.2 字符流
1> 字符流 FileReader和FileWriter
FileReader(只能读写文本文件 char,String)
java.io.Reader抽象类: 字符输入流 (顶层)
常用成员方法
[Java] 纯文本查看 复制代码 void close() :关闭此流并释放与此流相关联的任何系统资源
[font=微软雅黑][size=3]读[/size][/font]
int read(): 一次读一个字符char, 返回读到的字符.
读到文件末尾返回-1 (返回int为了代码编写方便)
int read(char[] cbuf): 一次读取一个字符数组char[] 返回读取的字符个数. 读到文件末尾返回-1
java.io.FileReader类: 文件字符输入流
构造方法
[Java] 纯文本查看 复制代码 FileReader(File file): 根据File对象创建文件字符输入流
FileReader(String fileName): 根据File对象创建文件字符输入流 FileWriter
java.io.Writer抽象类: 字符输出流 (顶层类)
常用成员方法
[Java] 纯文本查看 复制代码 abstract void close(): 刷新缓冲区并释放资源
abstract void flush() :刷新缓冲区 写
[Java] 纯文本查看 复制代码 void write(int c): 写一个字符 (int类型为了代码编写方便)
void write(char[] cbuf): 写一个字符数组
abstract void write(char[] b, int off, int len): 写一个字符数组的一部分
void write(String str): 写一个字符串
void write(String str, int off, int len): 写一个字符串的一部分 注意: write()方法只是将数据写到内存缓冲区, 最后必须调用flush()或close()才能将数据真正写入磁盘
flush():缓存作用,flush()后可以继续编写;可省略
close():缓存且释放资源,close()后不可继续写;不可省略
作用:
1. 创建FileWriter对象
2. 根据构造方法中的路径, 在磁盘上创建文件 ("如果文件已存在, 则清空文件内容")
3. 将FileWriter对象指向磁盘上的文件
写的其他方法:
[Java] 纯文本查看 复制代码 void write(char[] cbuf): 写一个字符数组
abstract void write(char[] b, int off, int len): 写一个字符数组的一部分
void write(String str): 写一个字符串
void write(String str, int off, int len): 写一个字符串的一部分 2> 字符缓冲流BufferedReader(Reader in)和 BufferedWriter(Writer out)
构造方法:
[Java] 纯文本查看 复制代码 public BufferedReader(Reader in) :创建一个 新的缓冲输入流。
BufferedReader br = new BufferedReader(new FileReader("br.txt"));
public BufferedWriter(Writer out) : 创建一个新的缓冲输出流。
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt")); 特有方法:
[Java] 纯文本查看 复制代码 BufferedReader: public String readLine() : 读一行文字。
while ((line = br.readLine())!=null) {
System.out.print(line);
System.out.println("‐‐‐‐‐‐");
BufferedWriter: public void newLine() : 写一行行分隔符,由系统属性定义符号。 [Java] 纯文本查看 复制代码 // 写出数据
bw.write("黑马");
// 写出换行
bw.newLine();
bw.write("程序");
bw.newLine();
bw.write("员");
3> 转换流InputStreamReader和OutputStreamWriter
InputStreamReader类
[Java] 纯文本查看 复制代码 InputStreamReader(InputStream in) : 创建一个使用默认字符集的字符流。
InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt"));
InputStreamReader(InputStream in, String charsetName) : 创建一个指定字符集的字符流。
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK"); OutputStreamWriter类
[Java] 纯文本查看 复制代码 OutputStreamWriter(OutputStream in) : 创建一个使用默认字符集的字符流。
OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("out.txt"));
OutputStreamWriter(OutputStream in, String charsetName) : 创建一个指定字符集的字符流。
OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("out.txt") , "GBK");
2.3 IO异常处理
IO异常处理处理方法有2种:
1,抛出()声明问题:如果报错,后面所有代码将无法执行
2.使用try catch
[Java] 纯文本查看 复制代码 FileWriter fw = null;
try {
// 流对象初始化
fw = new FileWriter("day09\\testFileWriter2.txt", true); // 带有续写
// IO操作
// 循环写5次
for (int i = 0; i < 5; i++) {
// 写入内容和换行
fw.write("HelloWorld\r\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 当流对象不为null时释放资源
if (fw != null) {
// 释放资源
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(fw!=null){
fw.close()
}}}} 2.4 InvalidClassException异常: 原因和解决方案
serialVersionUID序列号的作用:
序列化操作时, 会根据类生成一个默认的 serialVersionUID 属性, 写入到文件中.
该版本号是根据类中成员计算出来的一个数值. 当类的成员发生改变时, 序列版本号也会发生变化
当代码中的类和读取的对象序列版本号不一致时, 就会抛出InvalidClassException
为了避免这种问题, 我们可以手动写死这个序列号, 这样无论成员如何变化, 序列版本号都是一样的
解决方法: IDEA设置生成序列版本号:
在IDER中实行如下操作
Setting -> Editor -> Inspections -> Java -> Serialization issues -> 勾选 Serializable class without'serialVersionUID'
然后在类名上按 Alt + Enter 提示就会有 Add 'serialVersionID' field
2.5 字符编码和字符集
1> 编码表: 字符和二进制数字的对应规则
字符集和编码表: 字符集包含编码表
ASCII字符集
ASCII编码表
ISO-8859-1字符集:
Latin-1: 拉丁字符. 没有中文. 每个字符由1个byte组成
GB字符集
GB2312编码表: 每个字符由2个byte组成
GBK编码表: 每个字符由2个byte组成
GB18030编码表: 每个字符由1, 2, 4个byte组成
Unicode
UTF-8: ASCII字符占1个byte, 拉丁字符占2个byte, 中文占3个byte, Unicode辅助字符占4个byte
UTF-16
UTF-32
ANSI: 表示使用系统默认编码表
2> 乱码原因: 读写编码不一致
GBK文件中存储的是"你好"在GBK中对应的byte
而IDEA中使用FileReader读取文件时, 是将byte按照UTF-8编码表查找字符, 结果找不到, 就显示了问号
|