自学Java基础知识第十七天

day17

1. 字符流

1.1 字节流读取中文文本乱码问题

问题 : 使用字节输入流读取带有中文文本文件, 一边读取文件, 一边查看文件内容, 导致了按照字节进行读取, 有可能将中文拆分开, 将拆分的不完成的字节转换成字符, 于是发生数据读取出来乱码问题

 

解决 : 当读取带有中文文本时, 不要使用字节流, 使用字符流进行操作即可

 

代码

package com.ujiuye.io;

import java.io.FileInputStream;

import java.io.IOException;

public class Demo01_字节流读取中文文本乱码 {

public static void main(String[] args) throws IOException{

         FileInputStream fis = new FileInputStream("中文.txt");

         byte[] b = new byte[2];

         int len;

         while((len = fis.read(b)) != -1) {

          System.out.print(new String(b,0,len));

         }

         fis.close();

}

}

 

读取结果:  ?? 就是出现了乱码

 

 

 

 

源文件内容:

 

 

 

 

1.2 字符流读取文件原理

   所有文件都是由字节组成, 字符流也是从文件中读取出字节数据, 字符流先从文件中读取出一个字节, 验证这个字节结果是否为一个正数, 证明读到了一个符号, 一个字母,一个数字, 那么直接参考编码表将字节转换成字符数据获取到;

   平台默认的编码表GBK, 中文占有2个字节, 中文第一个字节为负数, 如果从文件中读取到第一个字节为负数, 证明读取到中文, 动态向下读取出一个字节, 将两个字节的结果转换成整数, 参考编码表转换成一个字符

 

 

1.3 字符输入流

  1. Reader : 字符输入流的抽象父类, 来自于java.io, 抽象类不能实例化对象, 需要子类, FileReader

 

  1. FileReader 构造方法:

   FileReader(String path) : path所表示的文件路径分装在字符输入流中, 以后输入流重文件中读取出字符数据

FileReader(File path) : path所表示的文件路径分装在字符输入流中, 以后输入流重文件中读取出字符数据

 

  1. 读取文件方法:

1) read() : 表示每次从文件中读取出一个字符, 返回值类型int, 如果返回-1,证明文件读取完毕

2) read(char[] ch) : 表示每次最多从文件中读取出ch.length个字符, 将读取到的字符结果放置到参数数组ch, 返回值类型int, 表示每次读取到的字符个数, 如果返回-1, 证明文件读取完毕

3) close() : 关闭资源

 

代码

package com.ujiuye.io;

import java.io.FileReader;

import java.io.IOException;

public class Demo02_字符流读取中文文本 {

public static void main(String[] args) throws IOException{

        // 1. 创建出字符输入流, 绑定一个数据源

FileReader fr = new FileReader("中文.txt");

// 2. 使用单个字符读取文件

// len表示每次读取到的字符对应的整数结果

/*int len;

while((len = fr.read()) != -1) {

System.out.print((char)len);

}*/

// len表示每次读取到的字符个数

int len;

char[] ch = new char[2];

while((len = fr.read(ch)) != -1) {

System.out.print(new String(ch,0,len));

}

// 3. 关闭资源

fr.close();

}

}

 

 

1.4 字符输出流

  1. Writer : 是字符输出流抽象父类, 来自于java.io, 抽象类不能实例化对象, 需要一个子类FileWriter

 

  1. FileWriter构造方法:

   FileWriter(String path) : path所表示的文件路径封装在字符输出流中, 以后输出流向文件中写入字符数据

FileWriter(File path) : path所表示的文件路径封装在字符输出流中, 以后输出流向文件中写入字符数据

 

 

  1. 向文件中写入字符方法:

  

 

 

 

  

 

代码

package com.ujiuye.io;

import java.io.FileWriter;

import java.io.IOException;

public class Demo03_字符流向文件中写入数据 {

public static void main(String[] args) throws IOException{

       // 1. 创建出一个字符输出流, 绑定一个数据目的

FileWriter fw = new FileWriter("字符流.txt");

// 2. 写入单个字符

fw.write('a');

// 写入字符数组

char[] ch = {'A','1','?',''};

fw.write(ch);

// 写入字符数组的一部分

fw.write(ch, 1, 2);

// 写入字符串

String s = "今天星期二";

fw.write(s);

// 写入字符串的一部分

fw.write(s, 0, 1);

 

fw.close();

}

}

 

 

1.5 flushclose方法

FileWriter 类型, 底层带有默认的数组缓冲, 使用数组的方式写入文件, 如此提高文件读写效能, 如果向文件中写入内容时, 没有刷新数据,也没有关闭资源, 数据都存储在底层数组缓冲区中, 没有同步到文件中, 因此文件中有可能缺失数据

 

  1. flush() : 表示刷新, IO流资源存在于底层缓冲区中的数据, 同步到文件中, 流资源刷新之后, 还能继续使用
  2. close() : 表示关闭资源, 在关闭资源之前, 先调用flush功能, 将底层缓冲区中的数据, 同步到文件中, 然后在关闭资源, 关闭流资源之后, 流不能在继续使用

 

代码

package com.ujiuye.io;

import java.io.FileWriter;

import java.io.IOException;

public class Demo03_字符流向文件中写入数据 {

public static void main(String[] args) throws IOException{

       // 1. 创建出一个字符输出流, 绑定一个数据目的

FileWriter fw = new FileWriter("字符流.txt");

//aA1?1?今天星期二今

// 2. 写入单个字符

fw.write('a');

// 写入字符数组

char[] ch = {'A','1','?',''};

fw.write(ch);

// 写入字符数组的一部分

fw.write(ch, 1, 2);

fw.flush();

// 写入字符串

String s = "今天星期二";

fw.write(s);

// 写入字符串的一部分

fw.write(s, 0, 1);

 

fw.close();

}

}

 

 

1.6 字符流复制

  1. 字符流可以复制纯文本文件(纯文本文件表示可以使用txt记事本打开文件, 打开后可以读懂),但是不建议使用, 效率低

   

 

 

 

  1. 字符流不能复制非纯文本文件, 例如 : 图片,视频...一律不能复制

   

 

 

 

  1. 字节流和字符流使用:

   a : 如果做文件的复制, 建议字节流完成复制

   b : 如果有带有中文文件, 边读边看, 防止中文乱码问题出现, 使用字符流进行文件内容的读取

 

复制文本文件代码

package com.ujiuye.io;

import java.io.FileReader;

import java.io.FileWriter;

import java.io.IOException;

public class Demo04_字符流可以复制文本 {

public static void main(String[] args) throws IOException{

         FileReader fis = new FileReader("字符流.txt");

         FileWriter fw = new FileWriter("字符流copy.txt");

         

         int len;

         while((len = fis.read()) != -1) {

          fw.write(len);

         }

         

         fw.close();

         fis.close();

}

}

 

 

复制非纯文本文件代码

package com.ujiuye.io;

import java.io.FileReader;

import java.io.FileWriter;

import java.io.IOException;

public class Demo05_字符流不能复制非纯文本 {

public static void main(String[] args) throws IOException{

FileReader fis = new FileReader("D:\\0810Java系统班\\day16\\图解\\IO流向.png");

        FileWriter fw = new FileWriter("D:\\IO流向.png");

        

        int len;

        while((len = fis.read()) != -1) {

         fw.write(len);

        }       

        fw.close();

        fis.close();

}

}

 

 

1.7 字符高效缓冲流

  1. BufferedReader : Reader一个子类, 表示高效字符输入流, 包装类, 将一个普通字符输入流包装成一个高效字符输入流

  BufferedReader(Reader in)

  a : 高效原理, 当创建出一个BufferedReader 字符输入流时, 类型底层会默认创建出一个大小为8192的字符数组, 每次通过read方法读取内存时, 最多读取出8192个字符, 将读取到字符放置到底层数组缓冲区中, 以后读取从数组中读取内容, 减少与磁盘文件交互次数,从而提高读写性能. 一直到文件读取完毕为止

 

 

  1. BufferedWriter : Writer一个子类, 表示高效字符输出流, 保证类, 将一个普通字符输出流包装成一个高效字符输出流

  BufferedWriter(Writer in)

   b :高效原理, 当创建出一个BufferedWriter字符输出流时, 类型底层默认创建出一个大小为8192的字符数组, 向文件中写入字符内容, 先写入到底层数组缓冲中, 当将8192写满,或者通过flush以及close方法, 可以将缓冲区中的数据同步到文件中, 减少与磁盘文件交互次数,从而提高读写效能

 

代码

package com.ujiuye.io;

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.FileReader;

import java.io.FileWriter;

import java.io.IOException;

public class Demo06_字符高效缓冲流 {

public static void main(String[] args) throws IOException{

        // 1. 定义出高效缓冲流

BufferedReader br = new BufferedReader(

new FileReader("Info.txt"));

BufferedWriter bw = new  BufferedWriter(

new FileWriter("InfoCopy.txt"));

 

// 2. 边读边写

// len表示每次读取到的字符转换成整数结果

int len;

while((len = br.read()) != -1) {

bw.write(len);

}

bw.close();

br.close();

}

}

 

 

1.8 字符高效缓冲流特有方法

  1. BufferedReader: 有特有方法

   readLine() :  每次可以从文件中读取出一行数据, 读取出数据返回值类型String, 当读取到null, 证明文件读取完毕

 

  1. BufferedWriter: 有特有功能

   newLine() : 表示向文件中写入一次回车换行

 

 

代码

package com.ujiuye.io;

import java.io.BufferedReader;

import java.io.BufferedWriter;

import java.io.FileReader;

import java.io.FileWriter;

import java.io.IOException;

import java.util.Arrays;

public class Demo07_字符高效流特有功能 {

public static void main(String[] args) throws IOException{

       BufferedReader br = new BufferedReader(

        new FileReader("产品.txt"));

       BufferedWriter bw = new BufferedWriter(

        new FileWriter("产品Copy.txt"));

       String s;

       while((s = br.readLine()) != null) {

        String[] arr = s.split("\\|");

        System.out.println(Arrays.toString(arr));

        

        bw.write(s);

        bw.newLine();        

       }      

       br.close();

       bw.close();

}

}

 

 

2. 转换流

  1. 编码表:

1) UTF-8 : 万国码表, 一个数字, 一个字母,一个符号, 占有1个字节, 一个中文占有3个字节

2) GBK : 国标码, 兼容ASCII和所有中文文字, 一个数字, 一个字母,一个符号, 占有1个字节, 一个中文占有2个字节

 

  1. 修改文件的编码集, Eclipse

  选中指定文件, 鼠标右键-->properties(属性)

  

 

 

 

  1. 转换流解决不同编码集乱码问题:

1) InputStreamReader(InputStream in,String charsetName) : 字节流向字符桥梁

   a : 通过参数in从文件中读取出字节数据

   b : 通过参数charsetName给定的编码表, 将字节通过对应编码表转换成字符

2) OutputStreamWriter(OutputStream out, String charsetName) : 字符流向字节桥梁

   a : 将字符通过给定的charsetName编码表, 转换成对应字节

   b : 使用out将字节数据写入到文件中

 

注意 : 使用转换流资源时,给出的编码集一定要与文件中实际编码保持一致

 

代码

package com.ujiuye.io;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStreamReader;

import java.io.OutputStreamWriter;

public class Demo09_转换流解决不同编码集文件读写 {

public static void main(String[] args) throws IOException{

         // 1. 创建出一个转换流(输入流)

 InputStreamReader isr = new InputStreamReader(

 new FileInputStream("UTF-8.txt"),"UTF-8");

 OutputStreamWriter osw = new OutputStreamWriter(

 new FileOutputStream("GBK.txt"), "GBK");

 

 int len;

 while((len = isr.read()) != -1) {

 osw.write(len);

 }

 osw.close();

 isr.close();

}

}

 

 

3. IO流异常标准处理方式(扩展)

JDK1.7版本, 针对IO流异常处理有了新的异常处理语法结构:

 

try(

    需要创建IO流资源;

){

    IO流使用过程;

}catch(异常类型 对象名){

    异常处理方式;

}

 

1) 将需要创建的IO流资源创建过程,写入到try小括号中

2) try大阔号中, 写入可能发生问题的代码

3) catch一样匹配和捕获异常

4) 优势 : 当流资源使用完毕, 不需要手动关闭流资源, try小括号自动再留资源 使用完毕之后进行关闭, 并且处理关闭流资源异常情况

 

最标准的异常处理方式

package com.ujiuye.io;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

public class Demo10_IO流中异常标准处理方式 {

public static void main(String[] args) {

       // 1. 为了在finally中关闭资源, 变量可以使用, 提高变量的作用范围

FileInputStream fis = null;

FileOutputStream fos = null;

 

         try {

fis = new FileInputStream("a\\中文.txt");

fos = new FileOutputStream("中文Cpoy.txt");

int len;

while((len = fis.read()) != -1) {

fos.write(len);

}

 

} catch (FileNotFoundException e) {// ctrl + t : 能查看当前类型继承关系

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}finally {

try {

                // 2. 关闭资源也会有异常,需要try...catch处理, 但因为fos初始值为null,

                // 因此, 验证fos不为null再关闭流资源

if(fos != null) {

fos.close();

}

} catch (IOException e) {

e.printStackTrace();

}finally {

try {

                    // 3. fis关闭资源的方式与fos一致

if(fis != null) {

fis.close();

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

}

 

 

JDK7异常新语法处理IO流异常

package com.ujiuye.io;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

public class Demo11_IO流异常全新处理方式 {

public static void main(String[] args) {

         try(

         FileInputStream fis = new FileInputStream("a\\中文.txt");

         FileOutputStream fos = new FileOutputStream("中文Cpoy.txt");

         ){

          int len;

   while((len = fis.read()) != -1) {

  fos.write(len);

   }

         }catch(IOException e) {

          e.printStackTrace();

         }        

         System.out.println("over代码结束");

}

}

 

 

4. 多线程

4.1 多线程相关概念

  1. 程序 : 就是一系列数据和逻辑结合体, 例如 : 写出java文件,就是程序
  2. 进程 : 正在内存中运行程序,称为进程
  3. 线程 : 进程(正在运行代码), 需要有代码的独立执行通道, 代码独立执行通道称为线程,

   线程代码执行通道彼此之间互相独立, 如果程序中有多条代码的执行通道, 称程序为多线程程序

  举例 : 例如main方法, 本身就是一条线程, 表示程序中代码的执行通道, 代码经过执行通道才能运行

 

 

 

 

 

  1. 并行 : 如果多个程序要求同时执行, 硬件完全支持, 效果让多个程序同时都在执行

   举例 : 饭店, 客户, 点了5道菜, 5个厨师每一个人炒一道菜, 5道菜同时在炒

 

  1. 并发 : 如果多个程序要求同时执行, 硬件设备不能完全支持, CPU处理程序速度非常快, 于是CPU在多个程序之间来回切换执行, 感受不到CPU对于程序切换执行

   举例 : 饭店, 客户, 点了5道菜, 1个厨师, 架起5口锅, 1个厨师在5个菜中来回翻炒

 

注意 :  多线程代码运行都是并发执行机制, 因此多线程程序执行具有很大随机性

 

 

 

4.2 多线程的实现方式

4.2.1 继承Thread

  1. Thread线程类介绍:

  

 

 

 

  1. 多线程实现步骤:

1) 自定义出一个类, Thread类作为自定义类的直接父类, 于是自定义类也是线程类

2) 重写Thread父类中的run方法功能, 将需要独立运行的代码设计在run方法中

3) 创建一个自定义线程类对象

4) 调用从父类Thread继承来的start方法功能:

   start() :

   a : 开启一个线程

   b : 在独立线程通道中, JVM虚拟机主动调用运行当前线程的run方法

 

  注意 : 每一个线程类对象只能开启一次

 

代码

package com.ujiuye.thread;

// 1. 自定义出一个类, Thread类作为自定义类的直接父类, 于是自定义类也是线程类

public class MyThread extends Thread {

// 2. 重写Thread父类中的run方法功能, 将需要独立运行的代码设计在run方法中

 @Override

     public void run() {

       for(int i = 1; i <= 10; i++) {

       System.out.println("run---" + i);

       }

     }

}

 

 

package com.ujiuye.thread;

public class TestThread {

// main方法本身就是一条线程

public static void main(String[] args) {

          // 除了main方法线程之外, 还需要额外的, 独立线程通道

  // 3. 创建一个自定义线程类对象

  MyThread my = new MyThread();

  // 4. 调用从父类Thread继承来的start方法功能,开启线程

  my.start();

  

  // main方法线程中,设计出循环

  for(int i = 1; i <= 10; i++) {

  System.out.println("main---" + i);

  }

 }

}

 

 

4.2.2 实现Runnable接口

  1. Thread线程类就是Runnable的一个实现类, Runnable是实现一个线程接口
  2. 多线程实现步骤:

1) 自定义出一个类, 实现Runnable接口

2) 重写Runnable中唯一方法功能run , 将需要独立运行代码设计到run

3) 创建出一个自定义线程类对象

4) 借助Thread线程类中构造方法, Runnable实现类对象封装在一个Thread类型中

   Thread(Runnable run) ;

5) 借助封装后的Thread类型对象,调用start方法功能:

   a : 开启一个独立线程通道

   b : 运行就是构造参数中的Runnable实现类中的run方法功能  

 

 

代码

package com.ujiuye.thread;

public class MyThread2 implements Runnable {

@Override

public void run() {

for(int i = 1; i <= 10; i++) {

System.out.println("runnable---"+i);

}

}

}

 

package com.ujiuye.thread;

public class TestThread {

// main方法本身就是一条线程

public static void main(String[] args) {

          // 除了main方法线程之外, 还需要额外的, 独立线程通道

  // 3. 创建一个自定义线程类对象

  MyThread my = new MyThread();

  // 4. 调用从父类Thread继承来的start方法功能,开启线程

  my.start();   

  

  // 使用Runnable接口实现方法创建出一个独立线程通道

  MyThread2 my2 = new MyThread2();

  Thread t = new Thread(my2);

  t.start();

  

  // main方法线程中,设计出循环

  for(int i = 1; i <= 10; i++) {

  System.out.println("main---" + i);

  }

 }

}

 

 

4.2.3 匿名内部类实现多线程

  1. 匿名内部类 : 本质就是一个类的子类或者是一个接口的实现类
  2. new 父类或者父接口(){

      // 大括号就表示父类的子类或者接口实现类, 实现过程

}

 

整体语法结构, 相当于创建出一个匿名内部类对象

 

代码

package com.ujiuye.thread;

public class Demo02_匿名内部类对象实现多线程 {

    // 1. main方法本身就是一个线程

public static void main(String[] args) {

// 定义出一个线程

         new Thread() {

          @Override

          public void run() {

          for(int i = 1; i <= 100; i++) {

          System.out.println("线程1---"+ i);

          }

          }

         }.start();

         

         new Thread() {

          @Override

          public void run() {

          for(int i = 1; i <= 50; i++) {

          System.out.println("线程2---"+ i);

          }

          }

         }.start();

         

         new Thread() {

          @Override

          public void run() {

          for(int i = 1; i <= 10; i++) {

          System.out.println("线程3---"+ i);

          }

          }

         }.start();

         

         for(int i = 1; i <= 10; i++) {

          System.out.println("main---" + i);

         }

}

}

posted @ 2020-09-07 20:29  master_hxh  阅读(202)  评论(0编辑  收藏  举报