一,转换流【工具流】
-
概述
- 是一个能够在字节流和字符流之间相互转换的io流【工具流】。
- 无论字节流操作数据还是字符流操作数据最终都是转变到字节的操作。无论到文档中去读还是写得到的都是数字【字节数】使用字节流还是字符流参考编码集要和文档的编码集要一致,不一致就会出现乱码。写程序的时候使用的编码集一定和要操作的文档的编码集一致吗?但是我们又要保证不能乱码,就得转换编码集【转换流有这个功能】
- 他是字符流的子类但是本质上是一个字符流,他拥有字符流所有的功能,也可以被字符缓冲流加强【他可以把字节流变成字符流】
- OutputStreamWriter:字符流到字节流的桥梁【以字符的形式写出去,文件接收到的是字节形式】
构造方法:OutputStreamWriter(OutputStream os, String charSetName)
- 解释:【 String charSetName参数可给可不给:给就按给定的字符集创建对象;不给:就按照默认的字符集创建对象】
常用方法:就是字符输出流的常用方法
代码示例
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
public class Demo_OutW {
public static void main(String[] args) throws IOException {
// 创建输出转换流的对象
FileOutputStream fos = new FileOutputStream("a\a.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos,"utf8");
// 单纯创建文件的时候是utf8,使用转换流进行输出的时候,按照默认的编码集变成gbk
osw.write("你好");
osw.flush();
}
}
- 输出转换流原理图
- InputStreamReader:字节流到字符流的桥梁,可以指定编码形式【按照指定的编码集的字节格式去读取内容到内存中的一个位置,然后再把这些读到的字节按照编码集转换成为对应的字符,使用字符流读取到目标内存中】
构造方法:InputStreamReader(InputStream is, String charSetName)
解释:【 String charSetName参数可给可不给:给就按给定的字符集创建对象;不给:就按照默认的字符集创建对象】
使用:直接使用 Reader 类中的方法即可(该类是Reader的子类)
常用方法就是字符输入流的常用方法。
代码示例
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class Demo_OutW {
public static void main(String[] args) throws IOException {
// 创建输出转换流的对象
FileOutputStream fos = new FileOutputStream("a\a.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos,"utf8");
// 单纯创建文件的时候是utf8,使用转换流进行输出的时候,按照默认的编码集变成gbk
osw.write("你好");
osw.flush();
FileInputStream fis = new FileInputStream("a\a.txt");
InputStreamReader isr = new InputStreamReader(fis);
int i = isr.read();
System.out.println((char)i);// 浣 乱码
// 文件的编码集是utf8 流的编码集默认的gbk
FileInputStream fis1 = new FileInputStream("a\a.txt");
InputStreamReader isr1 = new InputStreamReader(fis1,"utf8");
int i1 = isr1.read();
System.out.println((char)i1);// 你 不乱码
// 文件的编码集是utf8 流的编码集指定的utf8
}
}
- 字符输入转换流原理简图
- 说明:
无论是读 取的时候,还是写出的时候,都需要参考读取文件和目标文件的编码形式【不一致会发生乱码现象优先选择转换流】
读取源文件时,解码的形式【流的编码集】必须和源文件的编码形式一致【这样才能避免乱码】
写出到目标文件时,编码形式必须和目标文件的编码形式一致【这样才可以避免乱码】
二,对象流【操作java对象资源数据的流】
-
对象输出流【对象序列化流】
- 序列化:【ObjectOutputStream】
- 概述:用于将内存中的对象内容数据写出到磁盘文件中保存。
- 序列化:就是把内存中的数据写出到磁盘文件中行为叫做序列化【数据的瞬时态转变为持久态】
- 瞬时态:数据只能存在一定时间,不能长久保存的状态【内存中的数据】
- 持久态:数据被载体封装起来可以长时间保存的状态【磁盘文件中的数据】
- 作用:就是把对象型数据写入到磁盘文件中【数据的持久化 瞬时态到持久态】
- 构造方法:ObjectOutputStream(OutputStream os):创建一个序列化流对象【使os字节输出流拥有序列化的功能】
- 说明:序列化流是一个字节流,拥有字节流的所有共性行为,但是一般不用
- 特色成员方法:writeObject(Object o):将对象o中的数内容写出到指定磁盘文件
- 注意事项:
- 对象序列化流使用前提:写出的对象的类型必须实现Serializable接口
【面试题】:序列化数据,必须要实现Serializable接口吗?
不一定,序列化数据有很多种方式,不一定实现接口。
序列化对象型数据时常用序列化流,要求对象类型类必须实现Serializable接口。
2. 序列化之后的磁盘文件内容人看不懂【乱码】,是计算机可以看的懂的东西
代码示例
//定义测试类 import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; public class Demo_ObjectIO { public static void main(String[] args) throws IOException { // 创建序列化流的对象,将对象写入文件中 FileOutputStream fos = new FileOutputStream("a/Student.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); Student student = new Student("花花", 521); String ss =new String("春夏秋冬"); oos.writeObject(student);// 需要一个 student 对象 与 student 类 oos.writeObject(ss); // 报错,没有序列化 // 写出的对象的类型必须实现Serializable接口 // 当student类实现Serializable接口后就是实现类效果 // 序列化的目的是将内存中的东西保存到磁盘里面,是计算机或虚拟机保存内容的 // 人是看不懂的,人如果需要看的化需要序列化 oos.close(); //输出的内容:��srcom.ujiuye.demo.Student���&w7^�IageLnametLjava/lang/String;xp t花花t } } //定义student类 import java.io.Serializable;
//实现Serializable接口
public class Student implements Serializable{
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Student() {
super();
// TODO Auto-generated constructor stub
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the age
*/
public int getAge() {
return age;
}
/**
* @param age the age to set
*/
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
三,对象输入流【反序列化流】
- 反序列化【ObjectInputStream】
- 概述:是将磁盘文件中的对象数据【序列化过来】读回到内存中的io流
- 前提:操作的文件内容一定是序列化的数据
- 作用:把磁盘文件中的对象数据读到内存中【数据持久态到瞬时态】
- 构造方法:ObjectInputStream(InputStream in):创建一个反序列化流对象【让字节输入流in拥有反序列化功能】
- 是一个字节流拥有所有字节流的共性方法,但是不常用。使用它的特有功能
- 特有方法:
readObject():读取文件中的任意类型对象数据
代码示例
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
public class Demo_ObjectIO2 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//创建反序列化流的对象
FileInputStream fis = new FileInputStream("a/Student.txt");
ObjectInputStream ois =new ObjectInputStream(fis);
//反序列化 student对象的数据
Object object = ois.readObject(); //ois.readObject();调用方法是object类型的
//默认的是object类型,需要强转为student
Student student =(Student) object;
System.out.println(student);//Student [name=花花, age=521]
Object object1 = ois.readObject();
String student1 =(String ) object1;
System.out.println(student1);//春夏秋冬
}
}
- 注意事项:
- 序列化的时候一个文件可以序列化多种数据类型的对象
- 无论是序列化还是反序列化特有方法调用一次只能操作一个对象,如何操作多个对象,把多个对象放到一个集合对象中,序列化集合,反序列化回来也是一个集合。
- 操作的文件内容一般是序列化过来的内容
- 操作的文件不能人为的进行修改【哪怕修改后恢复原来的样子也不行】,原因:序列化的时候会给每一个文件创建一个序列号,这个序列号是唯一,是反序列化的一个凭证,文件手动进行了修改,破坏了这个序列号。反序列时通过序列号来判定要读取的内容以及类型,序列号一旦破坏了,反序列化不会成功。
- 序列化之后如果对象类型发生改变:属性发生改变,会影响反序列化操作。
四,Properties【属性集】
-
概述
- 是一个单独在操作文本文件和内存数据交互的双列集合。他是HashTable的子类。所以他拥有Map集合的所有特性,也拥有自己的特有功能。
- 本质:是一个双列集合【存放数据的容器】
- 主要作用:完成配置文件和程序运行时数据传输交互存储数据的工作。
- properties 的特有方法里面的泛型固定是 String
- 特殊的功能:他可以自己把文件中的数据读取到自己内部来,也可以把内部的数据写出到文件中
-
Properties中的特有方法
- getProperty(String propertyName):根据一个字符串类型的属性名称,获取一个对应的属性的值【相当于Map集合的get(Key k)方法】根据key获取value的值
- 属性集中:key :属性名 value :属性值
- 属性集中就可以存放对象的相关信息 name = “张三”
2. setProperties(String propertyName, String propertyValue):将一个属性名和对应的属性值添加到当前对象中【相当于Map集合的put(Key k,Value v)】
3. stringPropertyNames():获取当前属性集对象中的所有属性名称的Set集合【相当于keySet()方法】
代码示例
import java.util.Properties;
import java.util.Set;
public class Demo_Properties {
public static void main(String[] args) {
//创建属性集的对象
Properties p = new Properties();
//添加数据
p.put(123,new Student()); //普通方法有泛型,默认的是object类型
// p.setProperty(345, new Student()); 没有泛型添加不了,泛型固定是String
p.setProperty("name", "wangwu"); //特有方法的泛型固定是string
//获取
System.out.println(p.get(123)); //普通方法
System.out.println(p.getProperty("name")); //特有方法,数据类型指定是字符串所有只能传nama
Set<Object> set= p.keySet();
System.out.println(set);
Set<String > set2 = p.stringPropertyNames(); // 只能得到String类型的key
System.out.println(set2 );
}
}
- 注意:优先使用特有方法【不用考虑泛型的问题,同时避免类型转换的问题】
-
Properties中和配置文件交互的方式
- 利用他的下面的方法通过流对象进行读写功能。
- 读:
- load(InputStream is)
load(Reader r)
- 使用指定的io流去读取磁盘文件【配置文件】中的内容到properties集合中
- 注意事项:
- 配置文件的书写是有固定格式的:键值对格式【内容】
- 具体格式:
- key:value
- key value
- key = value
- 每一对数据必须独行存在,不能有分号
- 写出
- store(OutputStream os,String comment)
store(Writer w,String comment)
- 把properties中的内容按照指定的io流写到指定的文件中
说明:无论是os还是w,关联的都是对应的配置文件
参数 comment 必须传值,他是对写出内容的描述。不想写 传null 即可
代码示例
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.NonReadableChannelException;
import java.util.Properties;
import java.util.Set;
public class Demo_Properties02 {
public static void main(String[] args) throws IOException {
//创建属性集的对象
//属性集读的功能
/*properties.txt文件里面的内容是
*lisi:59
*zhangsan 89
*wangwu=78
*/
Properties p = new Properties();
FileInputStream fis = new FileInputStream("a/properties.txt");
p.load(fis);
System.out.println(p); //{lisi=59, zhangsan=89, wangwu=78}
//遍历
Set<String > keySet = p.stringPropertyNames();
for (String name : keySet) {
System.out.println(name);
System.out.println(p.get(name));
}
//属性集写出去
FileOutputStream out = new FileOutputStream("a/m.txt",true);//可以续写
p.store(out, "xiatianwrite");//刷新之后在a文件夹里面有一个m.txt文件
//m.txt文件里面会有xiatianwrite,及具体的时间 加 properties.txt同样的内容
}
}
//注意:属性集读写的过程中一般不用中文
五,多线程相关的三组概念
-
程序和进程
- 程序(program):是数据和逻辑的集合体。一般是存在于磁盘中,是一个静态资源。比如: qq、微信、 浏览器 等。程序就是程序员使用一定的编程语言编写出来的代码
- 进程(process):就是一个正在运行的程序。一般是在内存中。有自己独立的空间互不干扰,自己有自己独立的资源,资源共享给内部的成员使用。拥有资源分配权的。
例如:打开资源管理器查看进程和应用程序
-
进程和线程
- 进程(process):就是一个正在运行的程序。一般是在内存中。有自己独立的空间互不干扰,自己有自己独立的资源,资源共享给内部的成员使用。拥有资源分配权的
例如:打开资源管理器查看进程和应用程序
- 线程(thread):就是完成进程过程中其中的单条线路。一个进程有多条线程。也有自己独立的空间,一般存在于内存中的栈空间【通过方法来运行的】,但是共享进程的资源,没有资源分配权
比如:典型的线程 main方法【主线程】 - 进程和线程的关系:进程包含线程、线程是进程的成员。
-
并行和并发
- 并行(parallel):同一时刻多个事件同时发生互不干扰的运行方式。永远不会发生交叉。
- 并发(concurrent):同一时刻运行多个事件同时发起,但是不同时执行。【强调的是同一段时间内多个事件交替执行的方式】
六,CPU的调度
- 分时调度: 根据要执行的程序个数,安排合适的分配执行机制进行执行。比如:每一个程序的运行次序和运行时间提前都规划好,按照规划的方案进行执行。
- 抢占式调度: CPU随机的来执行程序,到每次执行那个程序CPU说了算。java的jvm的CPU调度使用的就是抢占式调度。
-
既然只有一个cpu,还需要在不同的任务之间来回切换,那么CPU效率到底是提升了还是降低了?
- 从整体效率来说提升了,对于单个的程序来说程序执行效率部分是降低,部分的提升的。
- 并发技术,解决的是不同的设备之间速率不同的问题
Cpu:10^-9秒
内存:10^-6秒
磁盘:10^-3秒
人:10^0秒
- 通过上面数据的对比,CPU的效率太高,价格昂贵,CPU执行程序的时候等其他的硬件去执行,有点资源浪费。不可能要CPU休息,要尽量的要CPU工作。采用抢占式调度,在程序键不停的切换,达到效率的最大化
七,多线程的实现方式
- Thread类:是用来描述线程的特征和行为的类。一个对象就相当于一条线程。
- 构造方法:
- Thread():创建一个默认名称的线程对象,没有线程任务
- Thread(String name):创建一个线程名称为name 的线程对象,没有线程任务
- Thread(Runnable r):创建一个默认线程名称并以r为执行任务的线程
- 对象
- Thread(Runnable r,String name):创建一个线程名称为name并以r为执行任务的线程对象
- 常用方法:
- run()方法: 封装线程要做的任务【要干的事】
- start()方法: 开启线程并通知 JVM 调用 run 方法执行该线程的任务
-
方式一:继承Tread类
- 步骤:
- 创建类继承Tread类【继承run方法将来重写】
- 重写run方法【重新指定该线程的任务】
- new 线程对象调用start()方法启动线程执行run方法
代码示例
//定义一个类继承Thread
public class MyThread extends Thread{
//重写run方法 封装 [指定] 新线程要干的事
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("春夏秋冬");
}
}
}
//定义测试类//开启新线程方式一
public class MyThread_Test {
public static void main(String[] args) {
//在主线程中开启新的线程 [创建新的 Thread 的对象]
MyThread thread = new MyThread();//新线程
thread.start();//需要调用这个才能开启新的线程干事
//下面的for循环是主线程的内容 [干的事]
for (int i = 0; i < 50; i++) {
System.out.println("我爱我的祖国");
}
//这里有两条线程 [平行的],一起来抢占cpu 随机执行没有先后顺序
}
}
-
方式二:实现Runnable接口
- Runnable:是一个接口,里面有且只有一个run方法,他的实现类必须重写run方法,所以我们就可以认为这个接口的存在意义:就是来指定线程任务的。但是这里指定的这个任务和线程没有关系。这个任务最终肯定要给线程执行,利用Thread(Runnable r)构造方法让任务和线程产生关系。
- 步骤:
- 创建类实现Runnable接口【类的对象叫做线程任务对象】
- 重写run方法【指定线程任务】
- 利用Thread(Runnable r)构造方法创建线程对象【绑定任务对象和线程对象一起】
- 启动线程【调用start()方法】
代码示例
public class MyRunnable implements Runnable {
//重写run方法,指定线程任务,[定义线程要干的事]
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("夏天");
}
}
}
//定义测试类public class MyRunnable_Test {
public static void main(String[] args) {
//创建线程对象
//创建线程的执行的任务对象,任务在 MyRunnable 里面
MyRunnable target = new MyRunnable();
//绑定任务给线程
Thread thread = new Thread(target); //
//开启线程(新线程)
thread.start();
//主线程打印东西
for (int i = 0; i <50; i++) {
System.out.println("花花");
}
}
}
-
两种方式的比较
- 继承方式:run方法和线程类是一个整体,关系比较亲密,耦合性强。不利于我们开发和维护
- 实现方式:做到了run方法和线程分离,之间没有直接的关系,这样耦合性低,利于开发维护。同时run方法所在的类对象可以独立存在,随意使用,拓展性强了。
- 接口实现优势:
- 耦合性低利于开发维护
- 扩展性强