JavaSE--第二阶段4

本文详细介绍了Java中的泛型,包括其好处、注意事项和自定义泛型的实现。接着讲解了线程的基本概念、创建方式、生命周期以及线程同步机制。此外,还深入探讨了I/O流,如文件流、字符流、节点流与处理流、打印流和转换流的使用。最后,提到了标准输入输出流和Properties类的应用。
摘要由CSDN通过智能技术生成

泛型

1、泛型的好处

  • 编译时,检查添加元素的类型,提高了安全性
  • 将运行时期的ClassCastException,转移到了编译时期变成了编译失败。
  • 减少了类型转换的次数,提高效率

2、注意事项

  • 1、interface List ,public class HashSet{}…,T,E只能为引用类型
  • 2、在给泛型指定具体类型后,可以传入该类型或其子类类型

3、自定义泛型

1、基本语法

class 类名<T,R...>{
    成员
}

interface IYse<T,R...>{}

2、注意细节

  • 普通成员可以使用泛型(属性,方法)
  • 使用泛型的数组,不能初始化
  • 静态方法中不能使用类的泛型
  • 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要制定确定类型)
  • 如果再创建对象时,没有指定泛型,默认Object
public class CustomGeneric_ {
    public static void main(String[] args) {

        //T=Double, R=String, M=Integer
        Tiger<Double,String,Integer> g = new Tiger<>("john");
        g.setT(10.9); //OK
        //g.setT("yy"); //错误,类型不对
        System.out.println(g);
        Tiger g2 = new Tiger("john~~");//OK T=Object R=Object M=Object
        g2.setT("yy"); //OK ,因为 T=Object "yy"=String 是Object子类
        System.out.println("g2=" + g2);

    }
}

//老韩解读
//1. Tiger 后面泛型,所以我们把 Tiger 就称为自定义泛型类
//2, T, R, M 泛型的标识符, 一般是单个大写字母
//3. 泛型标识符可以有多个.
//4. 普通成员可以使用泛型 (属性、方法)
//5. 使用泛型的数组,不能初始化
//6. 静态方法中不能使用类的泛型
class Tiger<T, R, M> {
    String name;
    R r; //属性使用到泛型
    M m;
    T t;
    //因为数组在new 不能确定T的类型,就无法在内存开空间
    T[] ts;

    public Tiger(String name) {
        this.name = name;
    }

    public Tiger(R r, M m, T t) {//构造器使用泛型

        this.r = r;
        this.m = m;
        this.t = t;
    }

    public Tiger(String name, R r, M m, T t) {//构造器使用泛型
        this.name = name;
        this.r = r;
        this.m = m;
        this.t = t;
    }

    //因为静态是和类相关的,在类加载时,对象还没有创建
    //所以,如果静态方法和静态属性使用了泛型,JVM就无法完成初始化
//    static R r2;
//    public static void m1(M m) {
//
//    }

    //方法使用泛型

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public R getR() {
        return r;
    }

    public void setR(R r) {//方法使用到泛型
        this.r = r;
    }

    public M getM() {//返回类型可以使用泛型.
        return m;
    }

    public void setM(M m) {
        this.m = m;
    }

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }

    @Override
    public String toString() {
        return "Tiger{" +
                "name='" + name + '\'' +
                ", r=" + r +
                ", m=" + m +
                ", t=" + t +
                ", ts=" + Arrays.toString(ts) +
                '}';
    }
}

4、泛型继承和通配符

  • 泛型不具有继承性

  • <?>:支持任意泛型类型
  • <? extends A>:支持A类以及A类的子类,规定了泛型的上限
  • <? super A>:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限
public class GenericExtends {
    public static void main(String[] args) {

        Object o = new String("xx");

        //泛型没有继承性
        //List<Object> list = new ArrayList<String>();

        //举例说明下面三个方法的使用
        List<Object> list1 = new ArrayList<>();
        List<String> list2 = new ArrayList<>();
        List<AA> list3 = new ArrayList<>();
        List<BB> list4 = new ArrayList<>();
        List<CC> list5 = new ArrayList<>();

        //如果是 List<?> c ,可以接受任意的泛型类型
        printCollection1(list1);
        printCollection1(list2);
        printCollection1(list3);
        printCollection1(list4);
        printCollection1(list5);

        //List<? extends AA> c: 表示 上限,可以接受 AA或者AA子类
//        printCollection2(list1);//×
//        printCollection2(list2);//×
        printCollection2(list3);//√
        printCollection2(list4);//√
        printCollection2(list5);//√

        //List<? super AA> c: 支持AA类以及AA类的父类,不限于直接父类
        printCollection3(list1);//√
        //printCollection3(list2);//×
        printCollection3(list3);//√
        //printCollection3(list4);//×
        //printCollection3(list5);//×
    }
    
    // ? extends AA 表示 上限,可以接受 AA或者AA子类
    public static void printCollection2(List<? extends AA> c) {
        for (Object object : c) {
            System.out.println(object);
        }
    }

    //说明: List<?> 表示 任意的泛型类型都可以接受
    public static void printCollection1(List<?> c) {
        for (Object object : c) { // 通配符,取出时,就是Object
            System.out.println(object);
        }
    }

    // ? super 子类类名AA:支持AA类以及AA类的父类,不限于直接父类,
    //规定了泛型的下限
    public static void printCollection3(List<? super AA> c) {
        for (Object object : c) {
            System.out.println(object);
        }
    }

}

class AA {}
class BB extends AA {}
class CC extends BB {}

线程

  • 程序:为完成特定任务,用某种语言编写的一组指令的集合 简而言之:就是代码

  • 并发:同一时刻,多个任务交替执行

  • 并行:同一时刻,多个任务同时执行

1、基本介绍

  • 在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用 java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM其实在就是在操作系统中启动了一个进程。

  • 创建线程两种方式

    • 继承Thread类,重写run方法
    • 实现Runnable接口,重写run方法
public class Thread01 {
    public static void main(String[] args) throws InterruptedException {

        //创建Cat对象,可以当做线程使用
        Cat cat = new Cat();
        /*
            (1)
            public synchronized void start() {
                start0();
            }
            (2)
            //start0() 是本地方法,是JVM调用, 底层是c/c++实现
            //真正实现多线程的效果, 是start0(), 而不是 run
            private native void start0();
         */
        cat.start();//启动线程-> 最终会执行cat的run方法
        System.out.println("主线程继续执行" + Thread.currentThread().getName());//名字main
        for(int i = 0; i < 60; i++) {
            System.out.println("主线程 i=" + i);
            //让主线程休眠
            Thread.sleep(1000);
        }

    }
}
class Cat extends Thread {

    int times = 0;
    @Override
    public void run() {//重写run方法,写上自己的业务逻辑

        while (true) {
            //该线程每隔1秒。在控制台输出 “喵喵, 我是小猫咪”
            System.out.println("喵喵, 我是小猫咪" + (++times) + " 线程名=" + Thread.currentThread().getName());
            //让该线程休眠1秒 ctrl+alt+t
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(times == 80) {
                break;//当times 到80, 退出while, 这时线程也就退出..
            }
        }
    }
}

2、常用方法

1、start():启动当前线程的,调用当前线程的run()方法
2、currentThread():静态方法,返回执行当前代码的线程
1、yield():线程的礼让。让出CPU,让其他线程先执行,但礼让的事件不确定,所有也不一定礼让成功
2、join():线程的插队。插队的线程一旦查对成功,则肯定先执行完插入的线程所有的任务

用户线程和守护线程

  • 用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
  • 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
  • 常见的守护线程:垃圾回收机制
如果我们希望当main线程结束后,子线程自动结束,只需将子线程设为守护线程即可
setDaemon(true); //设置成守护线程

3、线程的生命周期

在这里插入图片描述

  • NEW 尚未启动的线程处于此状态。
  • RUNNABLE 在Java虚拟机中执行的线程处于此状态。 可以细化为以下两种状态
    • Ready:就绪状态
    • Running:运行状态
  • BLOCKED 被阻塞等待监视器锁定的线程处于此状态。
  • WAITING 正在等待另一个线程执行特定动作的线程处于此状态。
  • TIMED_WAITING 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
  • TERMINATED 已退出的线程处于此状态

线程优先级设置

1、线程调度

  • 时间片

    在这里插入图片描述

  • 抢占式:高优先级的线程抢占CPU

2、Java的调度方式

  • 同优先级线程组成先进先出队列(先到先服务),使用时间片策略
  • 对高优先级,使用优先调度的抢占式策略

3、线程优先级

  • 线程优先级等级
    • MAX_PRIORITY:10
    • MIN_PRIORITY:1
    • NORM_PRIORITY:5
  • 设计的方法
    • getPriority():返回线程优先值
    • setPriority(int newPriority):改变线程的优先级
  • 说明
    • 线程创建时继承父线程的优先级
    • 低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用.

4、线程同步机制

  • 1、在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性。
  • 2、也可以这里理解:线程同步,即当有一一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作.

5、Synchronized

  • 为什么会出现线程安全问题

    • 有共享数据
  • 说明:

    • 1.操作共享数据的代码,即为需要被同步的代码
    • 2.共享数据:多个线程共同操作的变量。比如: ticket就是共享数据。
    • 3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。
      • 要求:多个线程必须要共用同一把锁。

同步的方式

  • 好处:解决了线程的安全问题
  • 坏处:操作同步代码块时,只能有一个线程参与,其他线程等待,相当于一个单线程,效率低

6、互斥锁

1、基本介绍

  • 1、Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性
  • 2、每个对象都对应于一一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
  • 3、关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一 个线程访问
  • 4、同步的局限性:导致程序的执行效率要降低
  • 5、同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)。默认锁对象是:this
  • 6、 同步方法(静态的)的锁为当前类本身。默认锁对象是:当前类.class

7、线程死锁

1、基本介绍

  • ➢不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
  • ➢出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续|

2、解决办法

  • 专门的算法,原则
  • 尽量减少同步资源的定义
  • 尽量避免嵌套同步

I/O流

1、文件

1、基本介绍

  • 文件:就是保存数据的地方

  • 文件流:文件在程序中是以流的形式老操作的

    在这里插入图片描述

2、常用方法

  • 1、new File(String pathname)

  • 2、new File(File parent,String child) //根据父目录文件+子路径构建

  • 3、new File(String parent,String child) //根据父目录+子路径构建

2、流的分类

  • 按操作数据单位不同:字节流、字符流
  • 按数据流的流向不同:输入流、输出流
  • 按流的角色的不同:节点流、处理流/包装刘

在这里插入图片描述

3、字节流

1、FileInputStream

public class FileInputStream_ {
    public static void main(String[] args) {
    }
    /**
     * 演示读取文件...
     * 单个字节的读取,效率比较低
     * -> 使用 read(byte[] b)
     */
    @Test
    public void readFile01() {
        String filePath = "e:\\hello.txt";
        int readData = 0;
        FileInputStream fileInputStream = null;
        try {
            //创建 FileInputStream 对象,用于读取 文件
            fileInputStream = new FileInputStream(filePath);
            //从该输入流读取一个字节的数据。 如果没有输入可用,此方法将阻止。
            //如果返回-1 , 表示读取完毕
            while ((readData = fileInputStream.read()) != -1) {
                System.out.print((char)readData);//转成char显示
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭文件流,释放资源.
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 使用 read(byte[] b) 读取文件,提高效率
     */
    @Test
    public void readFile02() {
        String filePath = "e:\\hello.txt";
        //字节数组
        byte[] buf = new byte[8]; //一次读取8个字节.
        int readLen = 0;
        FileInputStream fileInputStream = null;
        try {
            //创建 FileInputStream 对象,用于读取 文件
            fileInputStream = new FileInputStream(filePath);
            //从该输入流读取最多b.length字节的数据到字节数组。 此方法将阻塞,直到某些输入可用。
            //如果返回-1 , 表示读取完毕
            //如果读取正常, 返回实际读取的字节数
            while ((readLen = fileInputStream.read(buf)) != -1) {
                System.out.print(new String(buf, 0, readLen));//显示
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭文件流,释放资源.
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

2、FileOutputStream

@Test
public void writeFile() {
    //创建 FileOutputStream对象
    String filePath = "e:\\a.txt";
    FileOutputStream fileOutputStream = null;
    try {
        //得到 FileOutputStream对象 对象
        //老师说明
        //1. new FileOutputStream(filePath) 创建方式,当写入内容是,会覆盖原来的内容
        //2. new FileOutputStream(filePath, true) 创建方式,当写入内容是,是追加到文件后面
        fileOutputStream = new FileOutputStream(filePath, true);
        //写入一个字节
        //fileOutputStream.write('H');//
        //写入字符串
        String str = "hsp,world!";
        //str.getBytes() 可以把 字符串-> 字节数组
        //fileOutputStream.write(str.getBytes());
        /*
            write(byte[] b, int off, int len) 将 len字节从位于偏移量 off的指定字节数组写入此文件输出流
             */
        fileOutputStream.write(str.getBytes(), 0, 3);

    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            fileOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
public class FileCopy {
    public static void main(String[] args) {
        //完成 文件拷贝,将 e:\\Koala.jpg 拷贝 c:\\
        //思路分析
        //1. 创建文件的输入流 , 将文件读入到程序
        //2. 创建文件的输出流, 将读取到的文件数据,写入到指定的文件.
        String srcFilePath = "e:\\Koala.jpg";
        String destFilePath = "e:\\Koala3.jpg";
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        try {
            fileInputStream = new FileInputStream(srcFilePath);
            fileOutputStream = new FileOutputStream(destFilePath);
            //定义一个字节数组,提高读取效果
            byte[] buf = new byte[1024];
            int readLen = 0;
            while ((readLen = fileInputStream.read(buf)) != -1) {
                //读取到后,就写入到文件 通过 fileOutputStream
                //即,是一边读,一边写
                fileOutputStream.write(buf, 0, readLen);//一定要使用这个方法
            }
            System.out.println("拷贝ok~");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //关闭输入流和输出流,释放资源
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
                if (fileOutputStream != null) {
                    fileOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

4、字符流

1、FileReader

1、常用方法
  • 1、new FileReader(File/String)
  • 2、read:每次读取单个字符,返回该字符,如果到文件末尾返回-1
  • 3、read(char[]):批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾返回-1
  • 4、new String(char[):将char[转换成String
  • 5、new String(char,off.len):将char[]的指定部分转换成String

2、FileWriter

1、常用方法
  • 1、new FileWriter(File/String):覆盖模式,相当于流的指针在首端
  • 2、new FileWriter(File/String,true):追加模式,相当于流的指针在尾端
  • 3、write(int):写入单个字符
  • 4、write(char[]):写入指定数组
  • 5、write(char[l.off,len):写入指定数组的指定部分
  • 6、write (string) :写入整个字符串
  • 7、write(string,off,len):写入字符串的指定部分
  • String类: toCharArray:将String转换成charD
  • FileWriter使用后,必须要关闭(close)或刷新(flush), 否则写入不到指定的文件!
import org.junit.Test;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class FileWriter_ {
    public static void main(String[] args) {

        String filePath = "e:\\note.txt";
        //创建FileWriter对象
        FileWriter fileWriter = null;
        char[] chars = {'a', 'b', 'c'};
        try {
            fileWriter = new FileWriter(filePath);//默认是覆盖写入
//            3) write(int):写入单个字符
            fileWriter.write('H');
//            4) write(char[]):写入指定数组
            fileWriter.write(chars);
//            5) write(char[],off,len):写入指定数组的指定部分
            fileWriter.write("韩顺平教育".toCharArray(), 0, 3);
//            6) write(string):写入整个字符串
            fileWriter.write(" 你好北京~");
            fileWriter.write("风雨之后,定见彩虹");
//            7) write(string,off,len):写入字符串的指定部分
            fileWriter.write("上海天津", 0, 2);
            //在数据量大的情况下,可以使用循环操作.
        } catch (IOException e) {
            e.printStackTrace();
        } finally {

            //对应FileWriter , 一定要关闭流,或者flush才能真正的把数据写入到文件
            //老韩看源码就知道原因.
            /*
                看看代码
                private void writeBytes() throws IOException {
        this.bb.flip();
        int var1 = this.bb.limit();
        int var2 = this.bb.position();

        assert var2 <= var1;

        int var3 = var2 <= var1 ? var1 - var2 : 0;
        if (var3 > 0) {
            if (this.ch != null) {
                assert this.ch.write(this.bb) == var3 : var3;
            } else {
                this.out.write(this.bb.array(), this.bb.arrayOffset() + var2, var3);
            }
        }

        this.bb.clear();
    }
             */
            try {
                //fileWriter.flush();
                //关闭文件流,等价 flush() + 关闭
                fileWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        System.out.println("程序结束...");
    }

    @Test
    public void test07() throws Exception{
        String src="F:\\aaaa.txt";
        char[] buffer = new char[1024];
        FileReader fileReader = new FileReader(src);
        int len = 0;
        while ((len = fileReader.read(buffer))!=-1){
            System.out.print(new String(buffer,0,len));
        }
        fileReader.close();
    }
}

5、节点流和处理流

1、基本介绍

  • 节点流可以从一个特定的数据源读写数据
  • 处理流(也叫包装流)是“连接”在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能

在这里插入图片描述

区别和联系

  • 节点流是底层流/低级流,直接跟数据源相接。
  • 处理流包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出。
  • 处理流(也叫包装流)对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连

功能体现

  • 1、性能的提高:主要以增加缓冲的方式来提高输入输出的效率。
  • 2、操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据,使
    用更加灵活方便

2、BufferedReader和BufferedWriter

@Test
public void test01() throws Exception {

    String filePath = "e:\\a.java";
    //创建bufferedReader
    BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
    //读取
    String line; //按行读取, 效率高
    //说明
    //1. bufferedReader.readLine() 是按行读取文件
    //2. 当返回null 时,表示文件读取完毕
    while ((line = bufferedReader.readLine()) != null) {
        System.out.println(line);
    }
    //关闭流, 这里注意,只需要关闭 BufferedReader ,因为底层会自动的去关闭 节点流
    //FileReader。
    /*
            public void close() throws IOException {
                synchronized (lock) {
                    if (in == null)
                        return;
                    try {
                        in.close();//in 就是我们传入的 new FileReader(filePath), 关闭了.
                    } finally {
                        in = null;
                        cb = null;
                    }
                }
            }
         */
    bufferedReader.close();
}
public static void main(String[] args) throws IOException {
    String filePath = "e:\\ok.txt";
    //创建BufferedWriter
    //说明:
    //1. new FileWriter(filePath, true) 表示以追加的方式写入
    //2. new FileWriter(filePath) , 表示以覆盖的方式写入
    BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
    bufferedWriter.write("hello, 韩顺平教育!");
    bufferedWriter.newLine();//插入一个和系统相关的换行
    bufferedWriter.write("hello2, 韩顺平教育!");
    bufferedWriter.newLine();
    bufferedWriter.write("hello3, 韩顺平教育!");
    bufferedWriter.newLine();

    //说明:关闭外层流即可 , 传入的 new FileWriter(filePath) ,会在底层关闭
    bufferedWriter.close();
}
public static void main(String[] args) {
    //老韩说明
    //1. BufferedReader 和 BufferedWriter 是安装字符操作
    //2. 不要去操作 二进制文件[声音,视频,doc, pdf ], 可能造成文件损坏
    //BufferedInputStream
    //BufferedOutputStream
    String srcFilePath = "e:\\a.java";
    String destFilePath = "e:\\a2.java";
    //        String srcFilePath = "e:\\0245_韩顺平零基础学Java_引出this.avi";
    //        String destFilePath = "e:\\a2韩顺平.avi";
    BufferedReader br = null;
    BufferedWriter bw = null;
    String line;
    try {
        br = new BufferedReader(new FileReader(srcFilePath));
        bw = new BufferedWriter(new FileWriter(destFilePath));
        //说明: readLine 读取一行内容,但是没有换行
        while ((line = br.readLine()) != null) {
            //每读取一行,就写入
            bw.write(line);
            //插入一个换行
            bw.newLine();
        }
        System.out.println("拷贝完毕...");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        //关闭流
        try {
            if(br != null) {
                br.close();
            }
            if(bw != null) {
                bw.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3、BufferedInputStream和BufferedOutputStream

public class copy {
    public static void main(String[] args) {
        String srcFile="F:\\aaa.mp4";
        String desFile="F:\\bbb.mp4";
        BufferedInputStream bis=null;
        BufferedOutputStream bos = null;
        try {
            bis = new BufferedInputStream(new FileInputStream(srcFile));
            bos = new BufferedOutputStream(new FileOutputStream(desFile));
            byte[] bytes = new byte[1024];
            int len = 0;
            while ((len = bis.read(bytes))!=-1){
                bos.write(bytes,0,len);
            }
            System.out.println("文件拷贝完毕");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bis!=null){
                    bis.close();
                }
                if (bos!=null){
                    bos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

4、ObjectInputStream和ObjecOutputStream

1、基本介绍
  • 1、功能:提供了对基本类型或对象类型的序列化和反序列化的方法

  • 2、ObjectOutputStream提供序列化功能

  • 3、ObjectlnputStream提供反序列化功能

  • 序列化和反序列化

    • 1、序列化就是在保存数据时,保存数据的值和数据类型
    • 2、反序列化就是在恢复数据时,恢复数据的值和数据类型
    • 3、需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:
      • Serializable 这是一个标记接口,没有方法
      • Externalizable 该接口有方法需要实现,因此我们一般实现上面的 Serializable 接口

在这里插入图片描述

2、代码展示

序列化

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

public class OOS {
    public static void main(String[] args) throws Exception {
        //序列化后,保存的文件格式,不是存文本,而是按照他的格式来保存
        String filePath = "e:\\data.dat";

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));

        //序列化数据到 e:\data.dat
        oos.writeInt(100);// int -> Integer (实现了 Serializable)
        oos.writeBoolean(true);// boolean -> Boolean (实现了 Serializable)
        oos.writeChar('a');// char -> Character (实现了 Serializable)
        oos.writeDouble(9.5);// double -> Double (实现了 Serializable)
        oos.writeUTF("韩顺平教育");//String
        //保存一个dog对象
        oos.writeObject(new Dog("旺财", 10, "日本", "白色"));
        oos.close();
        System.out.println("数据保存完毕(序列化形式)");
    }
}
//如果需要序列化某个类的对象,实现 Serializable
public class Dog implements Serializable {
    private String name;
    private int age;
    //序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员
    private static String nation;
    private transient String color;
    //序列化对象时,要求里面属性的类型也需要实现序列化接口
    private Master master = new Master();

    //serialVersionUID 序列化的版本号,可以提高兼容性
    private static final long serialVersionUID = 1L;

    public Dog(String name, int age, String nation, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
        this.nation = nation;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                '}' + nation + " " +master;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

class Master implements Serializable {
}

反序列化

public class ObjectInputStream_ {
    public static void main(String[] args) throws IOException, ClassNotFoundException {

        //指定反序列化的文件
        String filePath = "e:\\data.dat";

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));

        //读取
        //老师解读
        //1. 读取(反序列化)的顺序需要和你保存数据(序列化)的顺序一致
        //2. 否则会出现异常

        System.out.println(ois.readInt());
        System.out.println(ois.readBoolean());

        System.out.println(ois.readChar());
        System.out.println(ois.readDouble());
        System.out.println(ois.readUTF());


        //dog 的编译类型是 Object , dog 的运行类型是 Dog
        Object dog = ois.readObject();
        System.out.println("运行类型=" + dog.getClass());
        System.out.println("dog信息=" + dog);//底层 Object -> Dog

        //这里是特别重要的细节:

        //1. 如果我们希望调用Dog的方法, 需要向下转型
        //2. 需要我们将Dog类的定义,放在到可以引用的位置
        Dog dog2 = (Dog)dog;
        System.out.println(dog2.getName()); //旺财..

        //关闭流, 关闭外层流即可,底层会关闭 FileInputStream 流
        ois.close();
    }
}
public class SerTest {
	public static void main(String[] args) throws Exception {
		// 创建 学生对象
		Student student = new Student("老王", "laow");
		Student student2 = new Student("老张", "laoz");
		Student student3 = new Student("老李", "laol");

		ArrayList<Student> arrayList = new ArrayList<>();
		arrayList.add(student);
		arrayList.add(student2);
		arrayList.add(student3);
		// 序列化操作
		// serializ(arrayList);
		
		// 反序列化  
		ObjectInputStream ois  = new ObjectInputStream(new FileInputStream("list.txt"));
		// 读取对象,强转为ArrayList类型
		ArrayList<Student> list  = (ArrayList<Student>)ois.readObject();
		
      	for (int i = 0; i < list.size(); i++ ){
          	Student s = list.get(i);
        	System.out.println(s.getName()+"--"+ s.getPwd());
      	}
	}

	private static void serializ(ArrayList<Student> arrayList) throws Exception {
		// 创建 序列化流 
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("list.txt"));
		// 写出对象
		oos.writeObject(arrayList);
		// 释放资源
		oos.close();
	}
}
3、注意事项
  • 1、读写顺序要一致
  • 2、要求实现序列化或反序列化对象,需要实现 Serializable
  • 3、序列化的类中建议添加SerialVersionUID.为了提高版本的兼容性
  • 4、序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员
  • 5、序列化对象时,要求里面属性的类型也需要实现序列化接口
  • 6、序列化具备可继承性,也就是如果某类已经实现了序列化,则它的所有子类也已经默认实现了序列化

5、标准输入输出流

类型默认设备
System.in 标准输入InputStream键盘
System.out 标准输出PrintStream显示器
public class InputAndOutput {
    public static void main(String[] args) {
        //System 类 的 public final static InputStream in = null;
        // System.in 编译类型   InputStream
        // System.in 运行类型   BufferedInputStream
        // 表示的是标准输入 键盘
        System.out.println(System.in.getClass());

        //老韩解读
        //1. System.out public final static PrintStream out = null;
        //2. 编译类型 PrintStream
        //3. 运行类型 PrintStream
        //4. 表示标准输出 显示器
        System.out.println(System.out.getClass());

        System.out.println("hello, 韩顺平教育~");

        Scanner scanner = new Scanner(System.in);
        System.out.println("输入内容");
        String next = scanner.next();
        System.out.println("next=" + next);
    }
}

6、转换流

1、简单介绍

InputStreamReader和OutputStreamWriter

  • 1、InputStreamReader:Reader的子类,可以将InputStream(字节流)包装成(转换)Reader(字符流)
  • 2、OutputStreamWriter:Writer的子类,实现将OutputStream(字节流)包装成Writer(字符流)
  • 3、当处理纯文本数据时,如果使用字符流效率更高,并且可以有效解决中文问题,所以建议将字节流转换成字符流
  • 4、可以在使用时指定编码格式(比如utf-8, gbk , gb2312, ISO8859-1等)
2、InputStreamReader
//将字节流 FileInputStream 转成字符流  InputStreamReader, 指定编码 gbk/utf-8
public class InputStreamReader_ {
    public static void main(String[] args) throws IOException {

        String filePath = "e:\\a.txt";
        //解读
        //1. 把 FileInputStream 转成 InputStreamReader
        //2. 指定编码 gbk
        //InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "gbk");
        //3. 把 InputStreamReader 传入 BufferedReader
        //BufferedReader br = new BufferedReader(isr);

        //将2 和 3 合在一起
        BufferedReader br = new BufferedReader(new InputStreamReader(
                                                    new FileInputStream(filePath), "gbk"));
        //4. 读取
        String s = br.readLine();
        System.out.println("读取内容=" + s);
        //5. 关闭外层流
        br.close();
    }
}
3、OutputStreamWriter
//把FileOutputStream 字节流,转成字符流 OutputStreamWriter
//指定处理的编码 gbk/utf-8/utf8
public class OutputStreamWriter_ {
    public static void main(String[] args) throws IOException {
        String filePath = "e:\\hsp.txt";
        String charSet = "utf-8";
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), charSet);
        osw.write("hi, 韩顺平教育");
        osw.close();
        System.out.println("按照 " + charSet + " 保存文件成功~");
    }
}

7、打印流

  • 打印流只有输出流,没有输入流
public class PrintStream_ {
    public static void main(String[] args) throws IOException {

        PrintStream out = System.out;
        //在默认情况下,PrintStream 输出数据的位置是 标准输出,即显示器
        /*
             public void print(String s) {
                if (s == null) {
                    s = "null";
                }
                write(s);
            }
         */
        out.print("john, hello");
        //因为print底层使用的是write , 所以我们可以直接调用write进行打印/输出
        out.write("韩顺平,你好".getBytes());
        out.close();

        //我们可以去修改打印流输出的位置/设备
        //1. 输出修改成到 "e:\\f1.txt"
        //2. "hello, 韩顺平教育~" 就会输出到 e:\f1.txt
        //3. public static void setOut(PrintStream out) {
        //        checkIO();
        //        setOut0(out); // native 方法,修改了out
        //   }
        System.setOut(new PrintStream("e:\\f1.txt"));
        System.out.println("hello, 韩顺平教育~");
    }
}

8、PrintWriter

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

        //PrintWriter printWriter = new PrintWriter(System.out);
        PrintWriter printWriter = new PrintWriter(new FileWriter("e:\\f2.txt"));
        printWriter.print("hi, 北京你好~~~~");
        printWriter.close();//flush + 关闭流, 才会将数据写入到文件..
    }
}

6、Properties

Properties的常见方法

  • load:加载配置文件的键值对到Properties对象list:将数据显示到指定设备/流对象
  • getProperty(key):根据键获取值
  • setProperty(key,value):设置键值对到Properties对象
  • store:将Properties中的键值对存储到配置文件,在idea中,保存信息到配置文件,如果含有中文,会存储为unicode码
  • http://tool.chinaz.com/tools/unicode.aspx unicode码查询工具
public static void main(String[] args) throws IOException {
    //使用Properties 类来创建 配置文件, 修改配置文件内容

    Properties properties = new Properties();
    //创建
    //1.如果该文件没有key 就是创建
    //2.如果该文件有key ,就是修改
    /*
            Properties 父类是 Hashtable , 底层就是Hashtable 核心方法
            public synchronized V put(K key, V value) {
                // Make sure the value is not null
                if (value == null) {
                    throw new NullPointerException();
                }
                // Makes sure the key is not already in the hashtable.
                Entry<?,?> tab[] = table;
                int hash = key.hashCode();
                int index = (hash & 0x7FFFFFFF) % tab.length;
                @SuppressWarnings("unchecked")
                Entry<K,V> entry = (Entry<K,V>)tab[index];
                for(; entry != null ; entry = entry.next) {
                    if ((entry.hash == hash) && entry.key.equals(key)) {
                        V old = entry.value;
                        entry.value = value;//如果key 存在,就替换
                        return old;
                    }
                }
                addEntry(hash, key, value, index);//如果是新k, 就addEntry
                return null;
            }
         */
    properties.setProperty("charset", "utf8");
    properties.setProperty("user", "汤姆");//注意保存时,是中文的 unicode码值
    properties.setProperty("pwd", "888888");

    //将k-v 存储文件中即可
    properties.store(new FileOutputStream("src\\mysql2.properties"), null);
    System.out.println("保存配置文件成功~");
}
weixin151云匹面粉直供微信小程序+springboot后端毕业源码案例设计 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值