Day17
课程内容
1、字符流
2、转换流
3、线程
4、线程类中常用方法
一、字符流的拷贝
1、使用字符输入流读取信息,使用字符输出流写出信息,最终完成文件的拷贝
2、字符流拷贝的必要性:
拷贝没有必要使用字符流进行
原因:字符流进行拷贝文本的时候,使用字符输入流在读取源文件到内存中的时候,会将字节信息--按照平台默认编码-->字符信息
使用字符输出流将内存中的字符写出到指定文件中去。会将字符信息--按照平台默认编码-->字节信息
因此:使用字符流完成拷贝,会先将字节信息转成字符信息,再将字符信息转成字节信息,完成拷贝
字节----------> 字符 ------------> 字节
拷贝过程中,就做了两次没有意义的转换的操作
3、总结:如果要完成拷贝,没有必要使用字符流的,直接使用字节流完成拷贝
4、字符流的使用场景:
在读取到字符的时候,需要人为的修改或者阅读这个字符的时候,就需要使用字符流
只是简单的将信息进行拷贝那么就没有必要使用字符流完成拷贝
import java. io. FileReader;
import java. io. FileWriter;
import java. io. IOException;
public class Demo_1 {
public static void main ( String[ ] args) throws IOException {
FileReader fr = new FileReader ( "Info.txt" ) ;
FileWriter fw = new FileWriter ( "copyInfo.txt" ) ;
int i;
while ( ( i = fr. read ( ) ) != - 1 ) {
fw. write ( i) ;
}
fr. close ( ) ;
fw. close ( ) ;
}
}
字符流拷贝非纯文本文件的问题
1、非纯文本文件:指的就是图片,视频,音频,安装包,压缩包等等非字符文本的文件
2、问题:拷贝非纯文本文件发现出现文件错误
3、原因:
字符流在进行拷贝的时候,(1)字符输入流将源文件的信息,读取的过程中,将字节信息-------查询平台默认编码------》 字符信息
由于是图片文件,字节信息是没有规律的,有可能一些字节信息没有办法查询到对应的字符的,就解码为?这个符号了
-123 --》 ?
(2)字符输出流将内存中的字符信息进行写出的时候:将字符信息------查询平台默认编码-------》字节信息
会将? ---> 63,这个过程中,会将数据进行篡改,造成源文件中的信息和目的文件写出的时候的信息不一致的
4、最终:写出到目的文件中的信息就是错误的
import java. io. FileReader;
import java. io. FileWriter;
import java. io. IOException;
public class Demo_2 {
public static void main ( String[ ] args) throws IOException {
FileReader fr = new FileReader ( "123.jpg" ) ;
FileWriter fw = new FileWriter ( "copy123.jpg" ) ;
int i;
while ( ( i = fr. read ( ) ) != - 1 ) {
fw. write ( i) ;
}
System. out. println ( '?' + 0 ) ;
System. out. println ( ( char ) - 123 ) ;
fr. close ( ) ;
fw. close ( ) ;
}
}
字符流使用小数组拷贝
1、使用FileReader中的read(char[] chs)将多个字符读取到数组中,返回的值表示的读到的有效的字符的个数,如果返回的值为-1代表读取到了文件末尾
2、使用FileWrter中的write(char[] chs,int offset,int len) 方法,将数组chs中的一部分字符(从指定的索引offset处开始的len个个数的字符)写出到指定文件中
3、Writer自带缓冲区数组的
import java. io. FileReader;
import java. io. FileWriter;
import java. io. IOException;
public class Demo_3 {
public static void main ( String[ ] args) throws IOException {
FileReader fr = new FileReader ( "Info.txt" ) ;
FileWriter fw = new FileWriter ( "copyInfo.txt" ) ;
char [ ] chs = new char [ 5 ] ;
int len;
while ( ( len = fr. read ( chs) ) != - 1 ) {
fw. write ( chs, 0 , len) ;
}
fr. close ( ) ;
fw. close ( ) ;
}
}
高效缓冲字符流
1、BufferedReader 和BufferedWriter
2、使用:
创建高效字符流之后,完成对字符流的加强。主要是使用高效缓冲流中的特有方法
高效原因和使用:都是高效字节流方式都是类似的
3、特有方法
BufferedReader
readLine():可以从输入流中,一次读取一行的数据,返回一个字符串,如果到达文件末尾,返回是null。
BufferedWriter
newLine():换行,在不同的操作系统中,完成换行,不同的操作系统,换行符是不同的,newLine这个方法可以根据计算机系统选择合适的换行符
import java. io. BufferedReader;
import java. io. BufferedWriter;
import java. io. FileNotFoundException;
import java. io. FileReader;
import java. io. FileWriter;
import java. io. IOException;
public class Demo_4 {
public static void main ( String[ ] args) throws IOException {
FileReader fr = new FileReader ( "b.txt" ) ;
BufferedReader br = new BufferedReader ( fr) ;
String str;
while ( ( str = br. readLine ( ) ) != null) {
System. out. println ( str) ;
}
FileWriter fw = new FileWriter ( "newLine.txt" ) ;
BufferedWriter bw = new BufferedWriter ( fw) ;
bw. write ( "中国" ) ;
bw. newLine ( ) ;
bw. write ( "长沙" ) ;
bw. close ( ) ;
}
private static void test_1 ( ) throws FileNotFoundException, IOException {
FileReader fr = new FileReader ( "Info.txt" ) ;
FileWriter fw = new FileWriter ( "copyInfo.txt" ) ;
BufferedReader br = new BufferedReader ( fr) ;
BufferedWriter bw = new BufferedWriter ( fw) ;
int i;
while ( ( i = br. read ( ) ) != - 1 ) {
bw. write ( i) ;
}
bw. close ( ) ;
}
}
二、转换流
1、GBK:国标码,定义都是字母和汉字,在GBK编码中,英文占一个字节的,中文占两个字节
2、UTF-8:万国码:定义了全球所有的符号。定义了这些符号和数字的对应关系。
英文是占用一个字节,中文占用三个字节的。
3、转换流
OutputStreamWriter(OutputStream out, Charset cs):是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节,能将写出的字符串按照指定的编码格式,编码成字节信息
OutputStreamWriter是Writer的子类类型,方法和Writer中的方法使用起来是一样的
InputStreamReader(InputStream in, Charset cs)是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符
能根据指定的编码从文本中读取对应的字节个数,转成字符
InputStreamReader是Reader子类
4、注意:
无论读入还是写出的时候,都要参考目标文件的编码,并保持一致
import java. io. FileInputStream;
import java. io. FileOutputStream;
import java. io. FileReader;
import java. io. FileWriter;
import java. io. InputStreamReader;
import java. io. OutputStreamWriter;
public class Demo_5 {
public static void main ( String[ ] args) throws Exception {
OutputStreamWriter osw = new OutputStreamWriter ( new FileOutputStream ( "UTF-8.txt" ) , "utf-8" ) ;
osw. write ( "中国" ) ;
osw. close ( ) ;
InputStreamReader isr = new InputStreamReader ( new FileInputStream ( "UTF-8.txt" ) , "utf-8" ) ;
int i ;
while ( ( i = isr. read ( ) ) != - 1 ) {
System. out. println ( ( char ) i) ;
}
isr. close ( ) ;
}
}
关于流剩余部分知识
import java. io. BufferedOutputStream;
import java. io. BufferedReader;
import java. io. FileInputStream;
import java. io. FileNotFoundException;
import java. io. FileOutputStream;
import java. io. IOException;
import java. io. InputStream;
import java. io. InputStreamReader;
public class Demo_6 {
public static void main ( String[ ] args) throws IOException {
try (
FileInputStream fis = new FileInputStream ( "Info.txt" ) ;
FileOutputStream fos = new FileOutputStream ( "copy.txt" ) ;
BufferedOutputStream bos = new BufferedOutputStream ( fos) ;
)
{
int i;
while ( ( i = fis. read ( ) ) != - 1 ) {
bos. write ( i) ;
}
}
}
private static void test_2 ( ) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream ( "Info.txt" ) ;
fos = new FileOutputStream ( "copy.txt" ) ;
int i;
while ( ( i = fis. read ( ) ) != - 1 ) {
fos. write ( i) ;
}
} catch ( FileNotFoundException e) {
e. printStackTrace ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
} finally {
try {
if ( fis != null) {
fis. close ( ) ;
}
} catch ( IOException e) {
e. printStackTrace ( ) ;
} finally {
try {
if ( fos != null) {
fos. close ( ) ;
}
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
}
}
private static void test_1 ( ) throws IOException {
InputStream is = System. in;
InputStreamReader isr = new InputStreamReader ( is) ;
BufferedReader br = new BufferedReader ( isr) ;
String string = br. readLine ( ) ;
System. out. println ( string) ;
BufferedReader br1 = new BufferedReader ( new InputStreamReader ( System. in) ) ;
String readLine = br1. readLine ( ) ;
System. out. println ( readLine) ;
}
}
三、多线程
多线程的三组概念
程序和进程
1、程序:一个固定的行为逻辑和数据的集合,是一个静态的概念,一般存储在磁盘中。
2、进程:一个正在运行的程序,是一个程序的一次运行,是一个动态的概念。一般存储在内存中。
进程和线程
1、进程:一个正在执行的程序,有自己的独立的资源分配,是一个独立的资源分配单位。
资源:内存,cpu
2、线程:一条独立的执行路径。多线程,在执行某个程序的时候,该程序可以有多个子任务,每个线程都能完成的其中的一条任务。
进程:工厂车间
线程:工人
3、进程和线程的关系:
进程拥有资源分配的能力,线程不具备
一个进程中,可以有多条线程,但是一个进程中,至少是要有一条线程
线程不会独立的分配资源,在一个进程中所有的线程,共享都是一个进程中的资源。
并行和并发
1、并行:多个任务(进程,线程)同时运行,在某个确定的时刻,有多个任务在同时执行。
多个cpu,多核编程
2、并发:多个任务同时发起,不能同时执行的。只能在某段时间内,这些任务都有过执行。
一个cpu
会在不同的任务之间不断的进行来回的切换,cpu运算速度特别快,切换速度也很快,用户在使用程序的时候感觉多个程序在同时的执行。
并行的关键是有同时处理多个任务的能力
并发有处理多个任务的能力,不一定是同时。
四、多线程的实现方式
(1)多线程实现的第一种方式:继承方式
1、线程类:Thread
2、定义一个类继承Thread
3、步骤
(1)自己定义一个类继承Thread
(2)重写run方法,执行新线程要执行的内容
(3)创建自定义类型的对象
(4)调用线程开启的方法start的方法
4、使用匿名内部类实现
public class Demo_1 {
public static void main ( String[ ] args) {
Thread th = new Thread ( ) {
@Override
public void run ( ) {
for ( int i = 0 ; i < 10 ; i++ ) {
System. out. println ( i) ;
}
}
} ;
th. start ( ) ;
for ( int i = 0 ; i < 10 ; i++ ) {
System. out. println ( i + " main" ) ;
}
}
private static void test_1 ( ) {
MyThread mt = new MyThread ( ) ;
mt. start ( ) ;
for ( int i = 0 ; i < 10 ; i++ ) {
System. out. println ( i + " main" ) ;
}
}
}
public class MyThread extends Thread {
@Override
public void run ( ) {
for ( int i = 0 ; i < 10 ; i++ ) {
System. out. println ( i) ;
}
}
}
(2)线程的第二种实现方法
1、实现一个Runnable接口:实现Runnable这个接口的实现类对象,表示为一个具体的任务。将来创建一个任务对象,交给线程执行。
2、步骤:
(1)定义一个类,实现Runnable接口
(2)重写任务类,就是接口中的抽象方法
(3)创建任务类对象
(4)创建线程对象,使用线程的构造方法,将要执行的任务,交给线程对象
(5)调用start方法,开启线程即可
3、使用匿名内部类实现
public class Demo_2 {
public static void main ( String[ ] args) {
Runnable task = new Runnable ( ) {
@Override
public void run ( ) {
for ( int i = 0 ; i < 10 ; i++ ) {
System. out. println ( i) ;
}
}
} ;
Thread th1 = new Thread ( task) ;
th1. start ( ) ;
for ( int i = 0 ; i < 10 ; i++ ) {
System. out. println ( i + "main" ) ;
}
}
private static void test_2 ( ) {
Task task = new Task ( ) ;
Thread th = new Thread ( task) ;
th. start ( ) ;
for ( int i = 0 ; i < 10 ; i++ ) {
System. out. println ( i + "main" ) ;
}
}
}
public class Task implements Runnable {
@Override
public void run ( ) {
for ( int i = 0 ; i < 10 ; i++ ) {
System. out. println ( i) ;
}
}
}
五、Thread类中常用的方法
获取线程的名称
1、getName()
返回该线程的名称。
2、注意
如果没有给线程起名字,线程的默认名称就是Thread—x,x是序号,从0开始
可以使用线程对象的引用调用getName方法,也可以在线程类中的直接调用getName方法
Runnable的实现类中不能直接调用getName方法的
public class Demo_3 {
public static void main ( String[ ] args) {
Runnable task = new Runnable ( ) {
@Override
public void run ( ) {
}
} ;
Thread th1 = new Thread ( task) ;
System. out. println ( th1. getName ( ) ) ;
th1. start ( ) ;
}
private static void test ( ) {
Thread th1 = new Thread ( ) {
@Override
public void run ( ) {
System. out. println ( "th1线程" ) ;
}
} ;
Thread th2 = new Thread ( ) {
@Override
public void run ( ) {
System. out. println ( "th2 线程" ) ;
}
} ;
th1. start ( ) ;
th2. start ( ) ;
System. out. println ( th1. getName ( ) ) ;
System. out. println ( th2. getName ( ) ) ;
}
}
设置线程名称
1、setName(String name)
使用此方法完成对线程的命名
2、构造方法完成对线程的命名
Thread (String name)
Thread(Runnable target, String name),给线程对象传入任务类对象的同时,完成对线程的命名
3、在给线程命名的时候,如果使用setName方法进行命名,在线程开启之前和开启之后,都能够完成对线程的命名,但是如果在线程类内部获取线程名称的时候,一定就要注意,setName方法要在线程开启之前完成
public class Demo_4 {
public static void main ( String[ ] args) {
Runnable task = new Runnable ( ) {
@Override
public void run ( ) {
System. out. println ( "task" ) ;
}
} ;
Thread thread = new Thread ( task, "线程B" ) ;
thread. start ( ) ;
System. out. println ( thread. getName ( ) ) ;
}
private static void test_1 ( ) {
MyThread mt = new MyThread ( "线程B" ) ;
mt. start ( ) ;
System. out. println ( mt. getName ( ) ) ;
}
private static void test ( ) throws InterruptedException {
Thread th = new Thread ( "线程A" ) {
@Override
public void run ( ) {
System. out. println ( getName ( ) + " run" ) ;
}
} ;
th. start ( ) ;
Thread. sleep ( 10 ) ;
System. out. println ( th. getName ( ) ) ;
}
}
获取当前的线程对象
1、某段代码要执行,一定是在一个方法之中,只要在方法中,代码就一定是被一个线程所执行。因此,对于任何一行代码,一定有执行该代码的一个线程对象
2、方法
static Thread currentThread()
返回对当前正在执行的线程对象的引用。
哪条线程在执行这段代码,返回的就是哪条线程
public class Demo_5 {
public static void main ( String[ ] args) {
Runnable task = new Runnable ( ) {
@Override
public void run ( ) {
System. out. println ( Thread. currentThread ( ) . getName ( ) ) ;
}
} ;
Thread th = new Thread ( task, "线程V" ) ;
th. start ( ) ;
}
private static void test_1 ( ) {
System. out. println ( Thread. currentThread ( ) . getName ( ) ) ;
Thread th1 = new Thread ( "线程A" ) {
@Override
public void run ( ) {
System. out. println ( Thread. currentThread ( ) . getName ( ) ) ;
}
} ;
th1. start ( ) ;
}
}
线程休眠
1、sleep(long millis) 让当前线程对象进入休眠
休眠的时间是millis
毫秒:1000 = 1秒
2、作用
当某段代码在某个位置需要休息的时候,就使用线程休眠
只要一个线程在运行的过程中,遇到了线程休眠,都要进行停一段时间
3、注意
异常:中断异常InterruptedException
在普通的方法中,调用了线程的休眠方法,可以声明该异常
但是在run方法中,只能进行捕获,不能进行声明
public class Demo_6 {
public static void main ( String[ ] args) throws InterruptedException {
Thread th = new Thread ( ) {
@Override
public void run ( ) {
for ( int i = 10 ; i > 0 ; i-- ) {
System. out. println ( i) ;
try {
Thread. sleep ( 1000 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
}
}
} ;
th. start ( ) ;
}
private static void test ( ) throws InterruptedException {
int i = 10 ;
for ( ; i > 0 ; i-- ) {
System. out. println ( i) ;
Thread. sleep ( 1000 ) ;
}
if ( i == 0 ) {
System. out. println ( "倒计时结束了" ) ;
}
}
}
守护线程
1、setDaemon(boolean flag)将一条线程设置为守护线程,只要flag指定为true,那么调用该方法的线程就成为了守护线程
说明:每条线程默认状态下,都不是守护线程
2、isDaemon() :判断某条线程是否是守护线程
3、守护线程:守护其他非守护线程的线程
4、特点:
为其他线程准备良好的运行环境。如果非守护线程执行完了,全部死亡,守护线程就没有存在的意义了,一段时间之后,守护线程就结束了。
public class Demo_7 {
public static void main ( String[ ] args) throws InterruptedException {
Thread th = new Thread ( "线程A" ) {
@Override
public void run ( ) {
while ( true ) {
try {
Thread. sleep ( 100 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System. out. println ( "我要守护我的女神main线程" ) ;
}
}
} ;
th. setDaemon ( true ) ;
th. start ( ) ;
System. out. println ( th. isDaemon ( ) ) ;
System. out. println ( Thread. currentThread ( ) . isDaemon ( ) ) ;
for ( int i = 0 ; i < 10 ; i++ ) {
Thread. sleep ( 100 ) ;
System. out. println ( i) ;
}
}
}
线程的优先级
1、通过setPriority这个方法,设置当前线程的优先级,优先级高的线程(只能说优先级高的线程在前面的时间段内,执行的次数多一点),优先级低的线程,在后面的时间段内,执行的次数多一些
2、setPriority(int priority) priority值越大,说明线程的优先级越高
priority值的范围:1 - 10
3、常量
MAX_PRIORITY 最大 10
NORM_PRIORITY 正常 5 默认线程的优先级5
MIN_PRIORITY 最小1
1、