基础类库学习(下)

学习类

  • 此类作用

  • 此类常用的方法

  • 此类的相关面试题和练习

1、异常(异常体系概念)

  • 类定义

用于描述程序运行出现非正常情况的一组类

  • 顶层父类:Throwable

只有继承了Throwable类才能成为异常类是所有异常的父类

  • Error:错误

Throwable的子类

通过java代码逻辑不能处理的严重错误:error

  • Exception:异常

Throwable的子类

通过java代码逻辑可以处理的不正常情况:exception

2、异常体系的特点

  • 异常类都必须直接或间接继承Throwable
  • 子类都是以父类名字为后缀
  • 异常类的子类 常用方法都继承与Throwable异常之间的区别是类名和异常原

3、Throwable类

  • Throwable构造方法

Throwable():无参构造,异常原因是null

Throwable(String mseesage):有参构造,返回异常信息

  • Throwable常用方法

throwable.printStatckTrace();打印异常信息

throwable.getMessage();获取异常原因

throwable.toString();获取异常描述

 Throwable  t1=new Throwable();
            //System.out.println(t1);//java.lang.Throwable
            //System.out.println("getMessage="+t1.getMessage());//getMessage=null
            t1.printStackTrace();
            //java.lang.Throwable
            //    at com.zhiyou100.day11.throwable.Demo01.main(Demo01.java:5)
            t1=new Throwable("出差错了");//java.lang.Throwable: 出差错了
            //System.out.println(t1);//java.lang.Throwable
            //System.out.println("getMessage="+t1.getMessage());//getMessage=出差错了
             t1.printStackTrace();
            //java.lang.Throwable:出差错了
            //    at com.zhiyou100.day11.throwable.Demo01.main(Demo01.java:5)

常见异常:

//{  :Error:(18, 2) java: 解析时已到达文件结尾

           //long[][][]  arr=new long[1024][1024][1024];//OutOfMemoryError: Java heap space

           //A a=new A();//Error:(19, 20) java: 找不到符号

           //Excption
           String s="";
           //s=null;
           //ystem.out.println(s.length());//NullPointerException
           s="";
           //System.out.println(s.charAt(0));//StringIndexOutOfBoundsException: String index out of range: 0

           int[] arr=new int[1];
           // System.out.println(arr[2]);//ArrayIndexOutOfBoundsException: 2
            List list=new ArrayList();
           // System.out.println(list.get(1));//IndexOutOfBoundsException: Index: 1, Size: 0

           //System.out.println(1/0);//ArithmeticException: / by zero

          //System.out.println(new SimpleDateFormat("yyyy-MM-dd").parse("1827/11/11"));//ParseException: Unparseable date: "1827/11/11"

          Object obj=new Object();
          //s=(String)obj;//ClassCastException: java.lang.Object cannot be cast to java.lang.String

          s="abc";
         //System.out.println(Integer.parseInt(s));//NumberFormatException: For input string: "abc"

         //s=new String(new byte[]{1,4,5,7,9},"UTy-0");//UnsupportedEncodingException: UTy-0


        List list2=new ArrayList();
        list2.add("acb");
        for(Object o:list2){
            //list2.add(1);//ConcurrentModificationException
        }

4、异常处理

java处理异常的方式:

  • 1.抛出机制:把方法内可能产生的异常抛给方法的调用者
  • 2.捕获机制:把方法中出现的异常通过代码逻辑处理掉,类似于现实中出现问题 自己解决

4.1、抛出机制

通过在方法声明上添加throw异常类型 把方法 可能出现的异常抛给方法的调用者

package com.zhiyou100.day11.throwable;

import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;

public class Demo02 {
    //一个类为什么要有main方法:程序的入口
    //jvm自动调用main方法中的代码
    public static void main(String[] args)throws ParseException {
        System.out.println(1);
        test2();
        System.out.println(2);
    }
    public static void test2() throws ParseException {
        test1();
    }
    //抛出机制
    //在方法声明上加throws ParseException 作用
    //1  当前方法可能出现异常:ParseException
    //2  如果当前方法出现异常ParseException 异常对象被抛给方法的调用者 去处理
    public static void test1()throws ParseException {
        System.out.println(11);
        System.out.println(new SimpleDateFormat("yyyy-MM-dd").parse("1827/11/11"));
        System.out.println(22);
    }



    //抛出机制注意事项:
    //1  如果通过抛出机制处理异常  一旦出现此类异常 当前方法结束  其他代码不再执行
    //2  如果一直通过抛出机制处理异常  异常最终被抛给虚拟机jvm
    //         jvm处理异常的方式固定:1 通过系统错误输出流打印异常详细信息  2 关闭虚拟机
    //3 子类重写父类的方法:子类方法不能抛出多个 大的异常

}

class Fu{
    protected void hehe() throws IOException {}
}
class Zi extends  Fu{
    public void hehe()throws FileNotFoundException, EOFException {}
}
//子类重写父类的方法:范围修饰符 范围不能缩小 可以扩大
//子类重写父类的方法:不能比父类抛出多的 大的异常

4.2、捕获机制

通过try-catch代码块,对产生的异常进行处理

package com.zhiyou100.day11.throwable;

import java.io.FileNotFoundException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Demo03 {
    public static void main(String[] args) {
         //捕获机制
        // 捕获机制注意事项
        //1  如果try代码块出现异常  try中的其他代码不再执行 而是执行对应的catch代码块
        //2  如果使用trycatch对异常进行处理 如果产生异常 当前方法正常执行
        //3  捕获父类异常的catch代码块 必须放在捕获子类异常catch代码块后面
        //4   如果在try或者catch代码块中 停止虚拟机  finally代码块 将不再执行
          test1();
    }
    public static void test1(){
        System.out.println(11);
        Date date;
        try{   //try代码块:可能产生异常的代码
            System.out.println("try---1");
            date=new SimpleDateFormat("yyyy/MM/dd").parse("1827/11/11");
            String s=null;
            //System.out.println(s.length());
            int[] arr=new int[1];
            //System.out.println(arr[1]);
            System.out.println("try---2");
        }catch(ParseException e){//定义引用接受捕获到的异常对象
            System.out.println("出现异常了!ParseException");//处理异常的代码
            date=new Date();
        }catch(NullPointerException e){//定义引用接受捕获到的异常对象
            System.out.println("出现异常了!NullPointerException");//处理异常的代码
        }catch(ArrayIndexOutOfBoundsException e){//定义引用接受捕获到的异常对象
            System.out.println("出现异常了!ArrayIndexOutOfBoundsException");//处理异常的代码
        }catch(IndexOutOfBoundsException e){//定义引用接受捕获到的异常对象
            System.out.println("出现异常了!IndexOutOfBoundsException");//处理异常的代码
        }finally{//无论是否出现异常 无论如何处理异常 都要执行的代码
            //一般是:关闭流 释放资源的代码
            System.out.println("finally代码块");
        }
        System.out.println(22);

       //int m=test3();//m=3    //int b=1; int a=b; a++;
        test4();
    }
    //return的作用:1 把方法的返回值返回给调用者  2 结束方法
    public static int test31(){//3
        int n=1;
        try{
            n=2;
            new SimpleDateFormat("yyyy/MM/dd").parse("1827/11/11");
            n=3;
            System.out.println(1111);
            return n;//  3
            //return 3;
        }catch(Exception e){
            n=4;
            return n;
            //return 4;
        }finally{
            n=5;
            System.out.println(1112);
            //return 5;
        }
    }
    public static int test32(){//5
        int n=1;
        try{
            n=2;
            new SimpleDateFormat("yyyy/MM/dd").parse("1827/11/11");
            return 3;
        }catch(Exception e){
            return 4;
        }finally{
            return 5;
        }
    }

    public static int test2(){
           int n=1;
           try{
               n=2;
               new SimpleDateFormat("yyyy/MM/dd").parse("1827/11/11");
               n=3;
           }catch(Exception e){
               n=4;
           }finally{
               n=5;
           }
           n=6;
           return n;//6
    }

    public static void test4(){
        try{
            System.out.println(11110);
            System.exit(0);// 如果在try或者catch代码块中 停止虚拟机  finally代码块 将不再执行
        }catch(Exception e){

            System.out.println(11111);
        }finally{
            System.out.println(1112);
        }
    }
}

5、异常分类

  • 按照异常的严重情况(是否可以通过代码逻辑进行捕获)
Error:程序出现的非常严重的异常 不能通过代码逻辑进行处理只能停止程序修改代码

Exception:可以通过代码逻辑进行处理的异常情况
  • 把Exception按是否继承RuntimeException分类
运行时异常(未检查异常):直接或者间接继承RuntimeException

编译时异常(已检查异常):没有继承RuntimeException
  • 运行时异常和编译时异常的区别

6、自定义异常

根据项目需求 定义jre中没有的异常a

注意事项:1 异常类必须直接或者间接继承Throwable

​ 2 异常类之间的区别:异常类名+异常原因(通过构造方法的参数列表)

package com.zhiyou100.day11.throwable;

import java.util.Scanner;

public class Demo05 {
    //4 选择处理方式
    public static void main(String[] args)throws AgeTooBigException {
            //3 设置产生异常的条件
            int age=new Scanner(System.in).nextInt();
            if(age>150){
                   //产生一个异常情况
                   //new AgeTooBigException("年龄不能小于150!");//创建一个异常对象 不是程序出现异常情况
                  throw new AgeTooBigException("年龄不能小于150!");
            }
    }
    // 面试题:throws和throw的区别
    //相同之处:都是异常处理的关键字
    // 不同之处
    //1 位置不同:throws用在方法声明上 throw用在方法体中
    //2 后面跟的内容不同:throws后面跟多个异常类型 throw后面跟一个异常对象
    //3 作用不同:throws用在方法声明上 来表示当前方法可能产生哪些异常 一旦产生这些异常 异常会被封装为对象 抛给调用者
    //            throw用在方法体中 表示程序出现指定的异常情况
    


    // 面试题:final finally finalize的区别
	//final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。
	//finally 是异常处理语句结构的一部分,表示总是执行。
	//finalize 是 Object 类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方提供垃圾收集时的其他资源回收,例如关闭文件等
}
//1 创建一个Throwable的子类
class  AgeTooBigException extends Exception{
     //2 通过构造方法参数列表传递异常原因
      public AgeTooBigException(String message){
          super(message);
      }
}

补全默认的代码

//面试题:补全所有默认的内容
class Person extends Object{//1 一个类没有继承其他类  默认继承object类
	//2 一个类没有构造方法 默认有一个五参数的构造方法
	public Person(){
		//3 所有子类构造方法的第一个语句 默认通过super()调用父类五参数的构造方法
	}
	//4 成员变量有默认初始值
	int name = 0;
}
class Student01  extends Person{
	int age ;////4 成员变量有默认初始值
	void hehe(){//调用成员前面默认有this.  父类成员前面默认有supeer.
		System.out.println(this.age+":::"+super.name);
	}
	//2 一个类没有构造方法 默认有一个五参数的构造方法
}

7、 File(文件/文件夹)

  • 属性
//字段:static String separator ;
		System.out.println("File.separator="+File.separator);//\
  • 构造方法
//构造方法:
File file1;
//File(File parent, String child) 
File file2=new File("E:\\java37");
file1=new File(file2,"day11课堂记录.md");

//File(String pathname)  
file1=new File("E:\\java37\\day11课堂记录.md");
file1=new File("E:/java37/day11课堂记录.md");

//File(String parent, String child) 
file1=new File("E:/java37","day11课堂记录.md");
  • 转义字符
//转义字符:\   :转换字符的含义: 把特殊字符转化为普通字符  把普通字符转换为特殊字符
//
System.out.print('\n');//转义字符\把普通字符n 转换为特殊字符\n 换行符号
System.out.print('\t');//转义字符\把普通字符n 转换为特殊字符\t 制表符
System.out.print('\'');//转义字符\把特殊字符'  转换普通字符\' 单引号
System.out.println('\\');//转义字符\把特殊字符\  转换普通字符\\ 反斜线
  • 普通方法
//普通方法
//1 获取文件名字
//String getAbsolutePath() :获取绝对路径
//String getPath():获取创建对象时 构造方法的参数
//String getName():获取文件/文件夹名字  不带父目录
file1=new File("E:\\java37\\day11课堂记录.md");//绝对路径:带盘符的就是绝对路径
file2=new File("1.txt");//相对路径:相当于当前项目:C:\Users\Administrator\Desktop\java37workspace\java37_se
System.out.println("file1.getName="+file1.getName());//day11课堂记录.md
System.out.println("file2.getName="+file2.getName());//1.txt
System.out.println("file1.getPath="+file1.getPath());//E:\java37\day11课堂记录.md
System.out.println("file2.getPath="+file2.getPath());//1.txt
System.out.println("file1.getAbsolutePath="+file1.getAbsolutePath());//E:\java37\day11课堂记录.md
System.out.println("file2.getAbsolutePath="+file2.getAbsolutePath());//C:\Users\Administrator\Desktop\java37workspace\java37_se\1.txt


//2判断
//boolean exists() :判断是否存在
//isDirectory() :判断是否为目录   文件必须存在
//isFile() :判断是否为文件  文件必须存在
file2=new File("src");
System.out.println(file1.isDirectory()+"::"+file1.isFile()+":::"+file1.exists());
System.out.println(file2.isDirectory()+"::"+file2.isFile()+":::"+file2.exists());
file2=new File("E:\\java37\\day13课堂记录.md");
System.out.println(file2.isDirectory()+"::"+file2.isFile()+":::"+file2.exists());//false::false:::false

//3 创建
//boolean createNewFile();  只能创建文件/不能创建文件夹
//boolean mkdir();  只能创建一层目录
//boolean mkdirs(); 可以创建多层目录
if(!file2.exists()){
    System.out.println("file2.createNewFile()="+file2.createNewFile());
}
file2=new File("src/test/aa/bb");
if(!file2.exists()){
    System.out.println("file2.mkdir()==="+file2.mkdir());
    System.out.println("file2.mkdirs()==="+file2.mkdirs());

}

//4 删除
//void deleteOnExit() ;//程序结束前删除
//boolean delete();  删除文件/文件夹  但删除文件夹时 文件夹必须时空的
file2=new File("src/test/aa/bb");
file1=new File("src/test/aa/bb/Test02.java");
//System.out.println("file1.delete()="+file1.delete());//file1.delete()=true
//System.out.println("file2.delete()="+file2.delete());//file2.delete()=false
file1.deleteOnExit();
System.out.println(file1.exists());//true

//5 获取
// length() : 获取文件字节数  不能获取文件夹
// File getParentFile() :获取直接父目录
// String getParent() :获取直接父目录的路径
// File[] listFiles() :获取当前目录下的所有直接子文件/子文件夹 
file1=new File("src/test/aa/bb/Test3.java");
System.out.println("getParent="+file1.getParent());
System.out.println("getParentFile() ="+file1.getParentFile());
System.out.println("length="+file1.length());//139
file1=new File("src");
System.out.println("length="+file1.length());//0

File[] zis=file1.listFiles();
for(File zi:zis){
    System.out.println(zi.getAbsolutePath());
}

//6 重命名:
//boolean renameTo(File dest) 
file1=new File("src/test/aa/bb/Test3.java");
System.out.println(file1.renameTo(new File("src/test/aa/bb/Test03.java")));
  • 练习
//练习题: 写一个方法 获取参数字符串表示的文件/文件夹的大小
//练习题:写一个方法遍历参数字符串表示的文件夹
// 递归:
//练习题: 写一个方法 获取参数字符串表示的文件/文件夹的大小
public static long getLength(String fileName)throws IOException{
    //封装file对象
    File file=new File(fileName);
    //判断是否存在
    if(!file.exists()){
        throw new FileNotFoundException(file.getAbsolutePath()+"文件/文件夹不存在!");
    }
    //判断文件的类型
    if(file.isFile()){
        return file.length();
    }
    //是文件夹
    long length=0;
    File[] zis=file.listFiles();
    for (File zi : zis) {
        //对所有的子文件进行当前方法的操作:判断是否存在--判断是不是文件--判断是不是文件夹--获取所有子文件
        length+=getLength(zi.getAbsolutePath());
    }
    return length;

}
//练习题:写一个方法遍历参数字符串表示的文件夹
public static void printFile(String fileName)throws IOException{
    //封装file对象
    File file=new File(fileName);
    //判断是否存在
    if(!file.exists()){
        throw new FileNotFoundException(file.getAbsolutePath()+"文件/文件夹不存在!");
    }
    //判断文件的类型
    if(file.isFile()){
        System.out.println("文件:"+file.getAbsolutePath());
        return;
    }
    //是文件夹
    System.out.println("文件夹:"+file.getAbsolutePath());
    File[] zis=file.listFiles();
    for (File zi : zis) {
        printFile(zi.getAbsolutePath());
    }
}

7.1、递归

public static void main(String[] args) {
    //递归:方法自己调用自己
    //  方法递归:必须有出口
    getSum(10);

}
//求1到100的和:1到n的和 等于1到n-1的和+n
public static int getSum(int n){
    if(n==1){
        return 1;
    }
    return getSum(n-1)+n;
}

//面试题:1 1 2 3 5 8 13 21 34 55  斐波那契数列
//:   前两个数是1   第n的数=  第n-1的数+ 第n-2的数
public static int get1(int n){
    if(n==1||n==2){return 1;}
    return get1(n-1)+get1(n-2);
}

public static int get2(int n){
    int[] arr=new int[n];
    for (int i = 0; i < arr.length; i++) {
        if(i==0||i==1){arr[i]=1;}
        else{
            arr[i]=arr[i-1]+arr[i-2];
        }
    }
    return arr[n-1];
}

public static int get3(int n){

    if(n<=2){
        return 1;
    }
    int a=1,b=1;
    for(int i=3;i<=n;i++){

        int c=b;
        b=a+b;
        a=c;
    }
    return b;
}

8、io流概念(重点)

概念

io: in out
流:一组有序的 有起点和终点的字节集合
流:一组用于描述数据传输的类

分类

按方向分类:参照物是内存/读进来 写出去
       输入流:把硬盘/外部存储设备上的内容读到内存中:::::in/reader
       输出流:把内存中的信息写到硬盘/外部存储设备上:::::out/writer
       eg: 把a文件中的内容写到b文件中:
按基本单位分类:
       字节流:操作的基本单位是字节::::Stream
       字符流:操作的基本单位是字符对应的字节集合::::Reader/Writer
按关联的对象分类:
        节点流:关联的是文件   :类似于毛坯房
        装饰流/过滤流:关联的是其他流 :精装房:::对关联的流进行功能的加强/增加       

学习

* 每个流的特点
* 使用每组流实现文件的复制

8.1、字节流

  • abstract class InputStream :字节输入流的顶层父类
  • abstract class OutputStream :字节输出流的顶层父类

子类FileInputStream:字节输入流

子类FileOutputStream:字节输出流

  • 类的介绍
 * 字节流:
     * FileInputStream:字节输入流
     * 
     * 构造方法:
     *       FileInputStream(File file) 
             FileInputStream(String name) 
       普通方法:
             int read() :一次读一个字节   到达文件末尾返回-1
             int read(byte[] b) :一次读一个字节数组  到达文件末尾返回-1   返回值是本次读取到的有效字节数
             int read(byte[] b, int off, int len) : 一次读一个字节数组的一部分
             void close()  
             
             
     * FileOutputStream:字节输出流
     * 构造方法:  如果目的文件不存在 会创建目的文件  如果目的文件存在 会删除 任何再创建一个空的
     *      FileOutputStream(File file) 
			FileOutputStream(File file, boolean append) :续写
			FileOutputStream(String name) 
			FileOutputStream(String name, boolean append)  
       普通方法:
     *      void write(byte[] b):一次写一个字节数组 
			void write(byte[] b, int off, int len) :一次写一个字节数组 的一部分
			void write(int b) :一次写一个字节
  • 代码
public static void test01()throws Exception{
    //1 创建流关联源文件
    FileInputStream  in=new FileInputStream("src/com/zhiyou100/day12_io/1.txt");
    //2 选择读的方式:逐个字节的读
    //		while(true){
    //			int n=in.read();
    //			//判断是否到达文件末尾
    //			if(n==-1){
    //				break;
    //			}
    //			System.out.print((char)n+",");
    //		}
    //		System.out.println();

    //2 选择读的方式:逐个字节数组的读
    byte[] arr=new byte[10];
    while(true){
        int n=in.read(arr);//读取最多10个字节 存储到arr中  返回值是读取的有效字节数
        if(n==-1){
            break;
        }
        //打印前n个有效字节
        for(int i=0;i<n;i++){
            System.out.print(arr[i]+",");
        }
        System.out.println();
    }

    //3 关闭流 释放资源
    in.close();
}
public static void test02()throws Exception{
    //1 创建流关联目的文件:
    FileOutputStream out=new FileOutputStream("src/com/zhiyou100/day12_io/3.txt",true);
    //2 选择写的方式:逐个字节的读
    out.write('2');
    out.write('b');
    out.write('\n');
    byte[] arr="111?\r\n".getBytes();
    out.write(arr);
    arr="我很好!一会儿见!\r\n".getBytes();//x x x x x x 
    out.write(arr, 1, 6);
    //3 关闭流 释放资源
    out.close();
}
  • 实现文件的复制
//实现文件的复制
public static void copy1(String yuanName,String muDiName){
    //1 创建file对象
    File yuan=new File(yuanName);
    File muDi=new File(muDiName);
    //2 判断文件是否存在
    if(!yuan.exists()){
        throw new RuntimeException(yuanName+":::源文件不存在!");
    }
    if(!muDi.exists()){
        //获取目的文件的目录
        File path=muDi.getParentFile();
        if(!path.exists()){
            path.mkdirs();
        }
        try {
            muDi.createNewFile();
        } catch (IOException e) {
            throw new RuntimeException(e);//异常转换!!!
        }
    }
    //3 创建流关联文件
    FileInputStream fin=null;
    FileOutputStream fout=null;
    try{
        fin=new FileInputStream(yuan);
        fout=new FileOutputStream(muDi);

        //4 选择读写方式
        //逐个字节
        //			while(true){
        //				int n=fin.read();
        //				if(n==-1){break;}
        //				fout.write(n);
        //			}
        int n;
        while((n=fin.read())!=-1){
            fout.write(n);
        }
    }catch(IOException e){
        throw new RuntimeException(e);//异常转换!!!
    }finally{
        //5 关闭流释放资源
        try {
            fin.close();
            fout.close();
        } catch (IOException e) {
            throw new RuntimeException(e);//异常转换!!!
        }
    }
}
//实现文件的复制
public static void copy2(String yuanName,String muDiName){
    //1 创建file对象
    File yuan=new File(yuanName);
    File muDi=new File(muDiName);
    //2 判断文件是否存在
    if(!yuan.exists()){
        throw new RuntimeException(yuanName+":::源文件不存在!");
    }
    if(!muDi.exists()){
        //获取目的文件的目录
        File path=muDi.getParentFile();
        if(!path.exists()){
            path.mkdirs();
        }
        try {
            muDi.createNewFile();
        } catch (IOException e) {
            throw new RuntimeException(e);//异常转换!!!
        }
    }
    //3 创建流关联文件
    FileInputStream fin=null;
    FileOutputStream fout=null;
    try{
        fin=new FileInputStream(yuan);
        fout=new FileOutputStream(muDi);

        //4 选择读写方式
        //逐个字节数组
        byte[] arr=new byte[1024];
        //				while(true){
        //					int n=fin.read(arr);//读取最多1024个字节 把读取的字节存储到arr中 返回值是本次读取的有效字节数
        //					if(n==-1){break;}
        //					fout.write(arr,0,n);
        //				}
        int n;
        while((n=fin.read(arr))!=-1){
            fout.write(arr,0,n);
        }
    }catch(IOException e){
        throw new RuntimeException(e);//异常转换!!!
    }finally{
        //5 关闭流释放资源
        try {
            fin.close();
            fout.close();
        } catch (IOException e) {
            throw new RuntimeException(e);//异常转换!!!
        }
    }
}

8.2、字符流

  • 方法
   字符流:每次操作的是同一个字符对应的字节整体
	    字符流顶层父类: Reader +Writer
	    字符流:  FileReader+FileWriter
	    
	   字符输入流:FileReader
	   构造方法:
	     FileReader(File file) 
         FileReader(String fileName)  
               普通方法:
	     int read()  :一次写一个字符
	     int read(char[] cbuf):一次写一个字符数组
	     int read(char[] cbuf,int off, int len):一次写一个字符数组的一部分
	     void close():关闭流 释放资源
	     
	 字符输出流:FileWriter
	 构造方法:
	     FileWriter(File file) 
		 FileWriter(File file, boolean append) 
		 FileWriter(String fileName) 
		 FileWriter(String fileName, boolean append)  
            普通方法:
         void write(char[] cbuf, int off, int len) 
         void write(char[] cbuf) 
		 void write(int c) 
		 void write(String str, int off, int len)
		 void write(String str) 
		 String getEncoding() 
		 void close():关闭流 释放资源 
  • 代码
public static void main(String[] args)throws Exception {
    copy01(new File("src/com/zhiyou100/day12_io/Demo01_File.java"),new File("src/com/zhiyou100/day12_io/1.txt"));
    copy02(new File("src/com/zhiyou100/day12_io/Demo01_File.java"),new File("src/com/zhiyou100/day12_io/2.txt"));
}

public static void copy01(File yuan,File muDi)throws Exception{
    long time1=System.currentTimeMillis();
    //1 创建流于文件关联
    FileWriter out=new FileWriter(muDi);
    FileReader in=new FileReader(yuan);
    //2 选择读写方式:逐个字符读写
    int n;
    while((n=in.read())!=-1){
        out.write(n);
    }
    in.close();
    out.close();
    long time2=System.currentTimeMillis();
    System.out.println(time2-time1);
}
public static void copy02(File yuan,File muDi)throws Exception{
    long time1=System.currentTimeMillis();
    //1 创建流于文件关联
    FileWriter out=new FileWriter(muDi);
    FileReader in=new FileReader(yuan);
    //2 选择读写方式:逐个字符读写
    char[] arr=new char[1024];
    int n;
    while((n=in.read(arr))!=-1){
        out.write(arr,0,n);
    }
    in.close();
    out.close();
    long time2=System.currentTimeMillis();
    System.out.println(time2-time1);
}

8.3、转换流

  • 方法
     * 转换流:把字节流转换为字符流
     * InputStreamReader 是字节流通向字符流的桥梁
     * OutputStreamWriter 是字符流通向字节流的桥梁
     * 
     * OutputStreamWriter:
     * 构造方法:
	     * OutputStreamWriter(OutputStream out):            使用平台默认编码集把字节流转换为字符流。 
	       OutputStreamWriter(OutputStream out, String charsetName)  使用制定编码集把字节流转换为字符流
       普通方法:和FileWriter完全相同
                     
       InputStreamReaderL:
       构造方法:
           InputStreamReader(InputStream in) :            使用平台默认编码集把字节流转换为字符流。 
           InputStreamReader(InputStream in, String charsetName) : 使用制定编码集把字节流转换为字符流 
       普通方法:和FileReader完全相同  
            String getEncoding()  获取编码集       
  • 代码
//使用转换流 实现文件的复制
public static void copy01(File yuan,File muDi)throws Exception{
    //1  创建节点流 与文件关联
    FileInputStream fin=new FileInputStream(yuan);
    FileOutputStream fout=new FileOutputStream(muDi);

    //2 创建转换流与节点流关联
    InputStreamReader isr=new InputStreamReader(fin,"GBK");
    OutputStreamWriter osw=new OutputStreamWriter(fout,"GBK");

    System.out.println("isr.getEncoding()="+isr.getEncoding());
    System.out.println("osw.getEncoding()="+osw.getEncoding());
    //3 选择读写方式:逐个字符读写/逐个字符数组读写
    int n;
    while((n=isr.read())!=-1){
        osw.write(n);
        osw.flush();//除了FileOutputStream 其他输出流都需要flush
    }

    //4 关闭流 释放资源
    isr.close();
    osw.close();

}

8.4、高效流

  • 介绍和方法
  高效流:过滤流的一种  / 增加节点流的传输效率
     Buffered 
     BufferedInputStream:高效字节输入流
                      构造方法:BufferedInputStream(InputStream in) 
                      普通 方法和FileInputStream 完全相同:  传输效率高而已
     BufferedOutputStream:高效字节输出流
                      构造方法:BufferedOutputStream(OutputStream out) 
                      普通 方法和FileOutputStream 基本相同:  传输效率高而已 /需要flush
      
     BufferedReader:高效字符输入流
                        构造方法:BufferedReader(Reader in) 
                        普通方法和FileReader基本相同       
                         特有方法:String readLine() :支持逐行读    如果已到达流末尾,则返回 null  
                                                 
     BufferedWriter:高效字符输出流
                        构造方法: BufferedWriter(Writer out) 
                         普通方法和FileWriter基本相同    
                         特有方法: void newLine():写一个换行符                 
  • 代码
//使用高效字符流 实现文件的读写
public static void copy01(File yuan,File muDi)throws Exception{
    //1 创建字符流 关联文件
    //2 创建高效流 关联节点流
    BufferedReader bin=new BufferedReader(new FileReader(yuan));
    BufferedWriter bout=new BufferedWriter(new FileWriter(muDi));
    //3 选择读写方式: 逐行读写
    String line;
    while((line=bin.readLine())!=null){//读取的信息中没有换行符号
        bout.write(line);
        bout.newLine();//写出去一个换行符号
        bout.flush();
    }

    //4 关闭流
    bout.close();
    bin.close();

}

9、线程

9.1、概念

进程:电脑中正运行的程序
	 正在生产的工厂
线程:正在运行的程序种正在执行的代码块
     工厂中正在运行的流水线
     线程是进程的执行单元 一个进程至少有一个线程
     线程是进程的执行空间
多线程/线程并发:同一个进程中有多个线程在同时执行
		      程序中需要多个代码块同时执行--需要多线程
多线程执行原理:
		CPU在时间轮片内在多个线程之间随机切换

9.2、垃圾回收机制

为什么一个类运行必须有main方法?
	运行一个类时,JVM会自动为此类的main方法创建一个线程,线程名字为main线程 用于执行main方法中的代码
垃圾回收机制:
	JVM会不定时的启动垃圾回收器对象 垃圾回收对象会根据创建的对象是否存在更多引用来判断此对象是否是垃圾
	如果是垃圾就调用此对象的finalize方法 来消回对象释放内存
	程序员可通过System.gc()方法来运行垃圾回收器 来干涉垃圾回收机制
垃圾回收和main线程不是同一个线程

9.3、Thread

1、创建线程方式1

     *Thread:线程类
	 * 构造方法:
	 *   Thread() 
		 Thread(Runnable target) 
		 Thread(Runnable target, String name) 
		 Thread(String name) 
     * 普通方法:
	 *   static Thread currentThread()   :获取当前线程对象
	 *   String getName()                :获取线程对象的名字
	 *   void setName(String name)       :设置线程对象的名字
	 *   void join()                     : 线程等待
	 *   void run()                      :执行线程任务    
	 *   static void sleep(long millis)  :线程休眠
	 *   void start()                    :启动线程
     线程任务:线程中关联的代码块

     创建线程的方式1:继承Thread类 重写run方法
         (1)将类声明为 Thread 的子类。
         (2)该子类应重写 Thread 类的 run 方法。
         (3)接下来可以分配实例
         (4)并启动该子类的实例        

2 、案例

package com.zhiyou100.day13_thread;

public class Demo02Thread {
	public static void main(String[] args) {
		  //3 创建线程对象
		 MyThread21  t1=new MyThread21();
		 //t1.setName("线程1111");
		// MyThread21  t2=new MyThread21("线程222222222222222");
		 MyThread21  t2=new MyThread21();//线程有默认名字:Thread-index
		 
		 //4 启动线程
		 t1.start();//调用线程对象的start方法 jvm会为此线程开辟执行空间 调用此线程对象的run方法 执行其线程任务
		 t2.start();
		 Thread thread=Thread.currentThread();//获取当前线程对象:当前代码在那个线程中执行 获取的就是当前线程
		 String name=thread.getName();
		for(int j=0;j<=10;j++){
			    try {Thread.sleep(100);} catch (Exception e) {}
				System.out.println(name+":::::j="+j);
		}
	}
}
//1 创建一个类继承Thread类
class MyThread21 extends Thread{
	MyThread21(){}
	MyThread21(String name){//有参数的构造方法 传递异常原因
		super(name);
	}
	//2 重写run方法:run方法的代码块是线程要执行的代码
	public void run(){
		//获取当前线程名字
		String name=this.getName();
		for(int j=0;j<=10;j++){
			    try {Thread.sleep(100);} catch (Exception e) {}
				System.out.println(name+":::::j="+j);
		}
	}
}

3、 创建线程方式2

 创建线程方式2:实现Runnable接口 实现run方法
          1 创建一个类实现runnable接口
          2 实现run方法:封装的线程任务
          3 创建实现类的对象
          4 创建Thread对象 并通过构造方法参数列表来关联实现类
          5 开启线程
  • 案例
package com.zhiyou100.day13_thread;

public class Demo03Thread {
	public static void main(String[] args) {
		//3 创建实现类对象
		MyImpRunnable  imp=new MyImpRunnable();
		//MyImpRunnable  imp2=new MyImpRunnable();
		//4 创建线程对象 关联实现类对象
		//两个线程对象的线程任务完全相同  使用同一个实现类对象即可
		Thread t1=new Thread(imp, "线程1111111111");
		t1.setName("线程11111111111111111111");
		Thread t2=new Thread(imp, "线程222");
		//5 启动线程
		t1.start();
		t2.start();
		//t1相关的run方法有两个:线程类的run方法---实现类的run方法 
		
	}

}
//1 实现runnbale接口
class MyImpRunnable implements Runnable{
	//2 实现run方法:封装线程任务
	public void run(){
		for (int i = 0; i <10; i++) {
			 try {Thread.sleep(100);} catch (Exception e) {}
			 System.out.println(Thread.currentThread().getName()+"::::"+(char)(Math.random()*10+'0'));
		}
	}
}


4、 Thread类的原理

//t1相关的run方法有两个:线程类的run方法---实现类的run方法 
interface MyRunnable{
	public void run();
}
//默认Thread
class MyThread{
	private MyRunnable r;
	MyThread(){}
	MyThread(MyRunnable r){
		this.r=r;
	}
	public void start(){
		if(r!=null){
			r.run();
		}else{
			this.run();
		}
	}
	public void run(){}	
}

5、实现三个线程分别打印50个不同的字符

  • 实现1:用三个类描述
package com.zhiyou100.day13_thread;

public class Demo04LianXi01 {
	public static void main(String[] args) {
		MyPrintThread1  p1=new MyPrintThread1(); p1.setName("打印数字:::"); 
		MyPrintThreada  pa=new MyPrintThreada();pa.setName("打印小写字母!!!!!!");
		MyPrintThreadAB pA=new MyPrintThreadAB(); pA.setName("打印大写字母@@@@@@@@@@@@@@@@");
		
		p1.start(); pa.start(); pA.start();

	}
}
//使用继承Thread类实现
class MyPrintThread1 extends Thread{
	   public void run(){
		   for (int i = 0; i < 50; i++) {
			   try {Thread.sleep(100);} catch (Exception e) {}
			   char c=(char)(Math.random()*10+'0');
			   System.out.println(this.getName()+"   c="+c);
		   }
	   }
}
//NoClassDefFoundError: com/zhiyou100/day13_thread/MyPrintThreadA (wrong name: com/zhiyou100/day13_thread/MyPrintThreada)
class MyPrintThreada extends Thread{
	   public void run(){
		   for (int i = 0; i < 50; i++) {
			   try {Thread.sleep(100);} catch (Exception e) {}
			   char c=(char)(Math.random()*26+'a');
			   System.out.println(this.getName()+"   c="+c);
		   }
	   }
}
class MyPrintThreadAB extends Thread{
	   public void run(){
		   for (int i = 0; i < 50; i++) {
			   try {Thread.sleep(100);} catch (Exception e) {}
			   char c=(char)(Math.random()*26+'A');
			   System.out.println(this.getName()+"   c="+c);
		   }
	   }
}

  • 实现2:用一个类来描述
package com.zhiyou100.day13_thread;

public class Demo04LianXi01 {
	public static void main(String[] args) {
		MyPrint p1=new MyPrint(10, '0'); p1.setName("打印数字::");
		MyPrint pa=new MyPrint(26, 'a'); pa.setName("打印小写::::");
		MyPrint pA=new MyPrint(26, 'A'); pA.setName("打印大写::::::");
		p1.start();pa.start();pA.start();

	}
}

//把三各类 写成一个类:::把共同的内容提取出来  把不同的内容写成变量
//方法中可以使用的变量::成员变量+方法参数列表
//此处只能选择:成员变量
class MyPrint extends Thread{
	   private int fw;
	   private int qsz;
	   MyPrint(int fw,int qsz){this.fw=fw;this.qsz=qsz;}
	   public void run(){
		   for (int i = 0; i < 50; i++) {
			   try {Thread.sleep(100);} catch (Exception e) {}
			   char c=(char)(Math.random()*fw+qsz);
			   System.out.println(this.getName()+"   c="+c);
		   }
	   }
}
  • 实现3:实现接口
public class Demo04LianXi02 {
	public static void main(String[] args) {
		MyPrintImp p1=new MyPrintImp(10, '0');
		MyPrintImp pa=new MyPrintImp(26, 'a');
		MyPrintImp pA=new MyPrintImp(26, 'A');
		new Thread(p1, "打印数字::").start();
		new Thread(pa, "打印小写::::").start();
		new Thread(pA, "打印大写::::::").start();
	}
}
class MyPrintImp implements Runnable{
	private int fw,qsz;
	MyPrintImp(int fw,int qsz){this.fw=fw;this.qsz=qsz;}
	public void run(){
		   for (int i = 0; i < 50; i++) {
			   try {Thread.sleep(100);} catch (Exception e) {}
			   char c=(char)(Math.random()*fw+qsz);
			   System.out.println(Thread.currentThread().getName()+"   c="+c);
		   }
	}
}

6、两种创建线程方式比较

// 继承Thread和实现Runable接口:那个更好
//Runable接口更好!
//1    java只支持类与类的单继承  但支持一个类实现多个接口的同时 继承父类  所以实现Runnable接口 不影响当前类的功能扩展
//    而继承Thread类 此类就没法再继承其他类 无法再扩展
//2    实现Runnable接口的实现类对象 中只有一个run方法 是对线程任务的封装    更符合java完全面向对象的思想

9.4、join

1、 join概念

package com.zhiyou100.day13_thread;

public class Demo05Join {
	//void join()
	//当a线程需要b线程的执行结果时  在a线程任务中调用b线程对象的join方法:a线程等待 直到b线程执行完毕 a线程才继续执行


	public static void main(String[] args) {
		//要求:打印小写的线程 打印到b时  等到大写线程完毕 才继续执行
		
		Print052A p2=new Print052A(); p2.setName("打印大写:::::");
		Print051a p1=new Print051a(p2);p1.setName("打印小写::");
		p1.start();p2.start();

	}

}
class Print051a extends Thread{
	//定义一个成员变量Thread类型的  接受要等到的线程对象
	private Thread t;
	Print051a(Thread t){this.t=t;}
	public void run(){
		   for (int i = 0; i < 50; i++) {
			   try {Thread.sleep(100);} catch (Exception e) {}
			   char c=(char)(Math.random()*26+'a');
			   System.out.println(Thread.currentThread().getName()+"   c="+c);
			   if(c=='b'&&t!=null){
				   //调用:p2的join方法
				   try {t.join();} catch (Exception e) {}
			   }
		   }
	}
}
class Print052A extends Thread{
	public void run(){
		   for (int i = 0; i < 50; i++) {
			   try {Thread.sleep(100);} catch (Exception e) {}
			   char c=(char)(Math.random()*26+'A');
			   System.out.println(Thread.currentThread().getName()+"   c="+c);
		   }
	}
}

2 、join实现死锁

package com.zhiyou100.day13_thread;

public class Demo05JoinDieLock {
    /*
     * 死锁情况1:两个线程互相调用对方的join方法
     * 
     * */
	public static void main(String[] args) {
		Print053 t1=new Print053(26, 'a', 'b');t1.setName("xiaoxie!!!!");
		Print053 t2=new Print053(26, 'A', 'C');t2.setName("daxie*************");
		t1.setT(t2);
		t2.setT(t1);
		t1.start();
		t2.start();
	}

}
class Print053 extends Thread{
	//定义一个成员变量Thread类型的  接受要等到的线程对象
	private Thread t;
	private int fw,qsz;
	private char joinChar;
	Print053(int fw,int qsz,char joinChar){this.fw=fw;this.qsz=qsz;this.joinChar=joinChar;}
	public void setT(Thread t) {
		this.t = t;
	}

	public void run(){
		   for (int i = 0; i < 50; i++) {
			   try {Thread.sleep(100);} catch (Exception e) {}
			   char c=(char)(Math.random()*fw+qsz);
			   System.out.println(Thread.currentThread().getName()+"   c="+c);
			   if(c==joinChar&&t!=null){
				   //调用:p2的join方法
				   try {t.join();} catch (Exception e) {}
			   }
		   }
	}
}

9.5、线程安全问题(重点)

1、案例:实现5个学生给一个老师交4本

package com.zhiyou100.day13_thread;

public class Demo06LianXi {
	//模拟五个学生给同一个老师交自己的4份作业::交作业互不影响
	//五个学生对应五个线程
	//五个线程关联同一个老师对象
	public static void main(String[] args) {
		Teacher06  t=new Teacher06("韩老师");
		
		Student06Thread s11=new Student06Thread();s11.setName("韩寒11"); 
		Student06Thread s12=new Student06Thread();s12.setName("韩信22222");
		Student06Thread s13=new Student06Thread();s13.setName("韩雪3333333");
		Student06Thread s14=new Student06Thread();s14.setName("韩红4444444444");
		Student06Thread s15=new Student06Thread();s15.setName("韩庚55555555555555");
		//保证五个学生使用的是同一个老师:::
		s11.t=t;s12.t=t;s13.t=t;s14.t=t;s15.t=t;
		
		s11.start();s12.start();s13.start();s14.start();s15.start();

	}

}
class Teacher06{
	String name;
	int num=0;
	public Teacher06(String name) {
		this.name = name;
	}
}
class Student06Thread extends Thread{
	Teacher06  t;
	public void run(){
		
		for (int i =1; i <=4; i++) {
			t.num++;
		    try {Thread.sleep(100);} catch (Exception e) {}
			System.out.println(this.getName()+"交了自己的第"+i+"本作业!!!"+"  老师的作业本书是:::"+t.num);
		}
	}
}

  • 出现问题:打印的结果无法预期
韩庚55555555555555交了自己的第1本作业!!!  老师的作业本书是:::5
韩寒11交了自己的第1本作业!!!  老师的作业本书是:::5
韩雪3333333交了自己的第1本作业!!!  老师的作业本书是:::5
韩红4444444444交了自己的第1本作业!!!  老师的作业本书是:::5
韩信22222交了自己的第1本作业!!!  老师的作业本书是:::5
韩红4444444444交了自己的第2本作业!!!  老师的作业本书是:::10
韩雪3333333交了自己的第2本作业!!!  老师的作业本书是:::10
韩寒11交了自己的第2本作业!!!  老师的作业本书是:::10
韩信22222交了自己的第2本作业!!!  老师的作业本书是:::13
韩庚55555555555555交了自己的第2本作业!!!  老师的作业本书是:::13
韩信22222交了自己的第3本作业!!!  老师的作业本书是:::15
韩红4444444444交了自己的第3本作业!!!  老师的作业本书是:::15
韩雪3333333交了自己的第3本作业!!!  老师的作业本书是:::15
韩庚55555555555555交了自己的第3本作业!!!  老师的作业本书是:::15
韩寒11交了自己的第3本作业!!!  老师的作业本书是:::15
韩雪3333333交了自己的第4本作业!!!  老师的作业本书是:::20
韩信22222交了自己的第4本作业!!!  老师的作业本书是:::20
韩红4444444444交了自己的第4本作业!!!  老师的作业本书是:::20
韩寒11交了自己的第4本作业!!!  老师的作业本书是:::20
韩庚55555555555555交了自己的第4本作业!!!  老师的作业本书是:::20

2、线程安全问题


	//线程安全问题:多个线程同时操作一个共享数据,当一个线程在使用共享数据时 其他线程对数据进行了修改
	//         造成数据前后不一致的现象
	//线程安全问题的前提条件:1 多线程 2有共享数据 3多个语句操作共享数据
	//线程安全问题::结果无法预期  需要避免
	//解决线程安全问题:使用synchronized实现线程同步:一个线程在使用共享数据时 他、其他线程等待

3、同步代码块

package com.zhiyou100.day13_thread;

public class Demo07Synchronized {

	//线程安全问题:多个线程同时操作一个共享数据,当一个线程在使用共享数据时 其他线程对数据进行了修改
	//         造成数据前后不一致的现象
	//线程安全问题的前提条件:1 多线程 2有共享数据 3多个语句操作共享数据
	//线程安全问题::结果无法预期  需要避免
	//解决线程安全问题:使用synchronized实现线程同步:一个线程在使用共享数据时 他、其他线程等待
	/*同步代码块:
	 *  synchronized(锁对象){
	 *        操作共享数据的所有代码
	 *  }
	 *  
	 *锁对象:那个线程拿到锁对象 那个线程就有共享数据的使用权   其他要使用此共享数据的线程就需要等待  
	 *注意1: 锁对象必须唯一
	 *注意2:  synchronized的代码块必须包含所有操作共享数据的代码
	 * */
	public static void main(String[] args) {
        Teacher07  t=new Teacher07();
        //Object obj=new Object();
		
		Student07Thread s11=new Student07Thread();s11.setName("韩寒11"); 
		Student07Thread s12=new Student07Thread();s12.setName("韩信22222");
		Student07Thread s13=new Student07Thread();s13.setName("韩雪3333333");
		Student07Thread s14=new Student07Thread();s14.setName("韩红4444444444");
		Student07Thread s15=new Student07Thread();s15.setName("韩庚55555555555555");
		//保证五个学生使用的是同一个老师:::
		s11.t=t;s12.t=t;s13.t=t;s14.t=t;s15.t=t;
		//保证五个学生使用同一个锁对象
		//s11.obj=obj;s12.obj=obj;s13.obj=obj;s14.obj=obj;s15.obj=obj;
		
		s11.start();s12.start();s13.start();s14.start();s15.start();

	}
}
class Teacher07{
	int num=0;
}
class Student07Thread extends Thread{
	Teacher07  t;
	//Object obj;
	public void run(){
		
	   for (int i =1; i <=4; i++) {
		    synchronized (t) {
				t.num++;
			    try {Thread.sleep(50);} catch (Exception e) {}
				System.out.println(this.getName()+"交了自己的第"+i+"本作业!!!"+"  老师的作业本书是:::"+t.num);
			}
			try {Thread.sleep(50);} catch (Exception e) {}
		}
	}
}

4、同步代码块实现死锁

package com.zhiyou100.day13_thread;

public class Demo07SynchronizedDieLock {
   /*
    * 使用Synchronized实现死锁现象:两个线程 两个同步代码块  两个线程的内外锁交替
    * 
    * */
	public static void main(String[] args) {
		//
		Object o1=new Object();
		Object o2=new Object();
		DieLockThread t1=new DieLockThread(o1, o2);
		DieLockThread t2=new DieLockThread(o2, o1);
		
		t1.setName("线程111"); t2.setName("线程2222222222222");
		
		t1.start(); t2.start();

	}

}
class DieLockThread  extends Thread{
	  private Object nei,wai;

	public DieLockThread(Object nei, Object wai) {
		this.nei = nei;
		this.wai = wai;
	}
	public void run(){
		for (int i = 0; i < 10000; i++) {
			 try {Thread.sleep(50);} catch (Exception e) {}
			 synchronized (wai) {
			 	  System.out.println(this.getName()+"拿到自己的外锁::"+wai);
			 	  try {Thread.sleep(50);} catch (Exception e) {}
			 	  synchronized (nei) {
			 		   System.out.println(this.getName()+"拿到自己的内锁::"+nei);
			 		  try {Thread.sleep(50);} catch (Exception e) {}
				  }
			 	 System.out.println(this.getName()+"释放内锁::"+nei);
			 }
			 System.out.println(this.getName()+"释放外锁::"+wai);
		}
	}
	  
}

posted @ 2021-09-06 17:30  RenVei  阅读(46)  评论(0编辑  收藏  举报