Java学习笔记 - 4 Java核心类库

4 Java 核心类库

4.1 泛型

泛型,即“参数化类型”。就是将原来具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

作用:

  • 提高代码的复用率
  • 泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型)
4.1.1 泛型类
public class ClassName<T> {
    private T data;
    
    public 	T getData() {
        return data;
    }
    
    public void setData() {
        this.data = data;
    }
}
4.1.2 泛型接口
public interface InterfaceName<T> {
    T getData();
}

// 实现接口时可以选择指定泛型类型,也可以不指定:

// 指定类型:
public class Interface1 implements InterfaceName<String> {
    private string text;
    @Override
    public String getData() {
        return text;
    }
}

// 不指定类型:
public class Interface2<T> implements InterfaceName<T> {
    private T data;
    @Override
    public T getData() {
        return data;
    }
}
4.1.3 泛型方法
private static <T> T methodName(T a, T b) {}

// e.g.
public static <T> void print(T a) {
    system.out.println(a);
}
4.1.4 泛型限制类型

指定泛型的限定区域,例如: 必须是某某类的子类或 某某接口的实现类:

// 格式:<T extends 类或接口1 & 接口2>

// e.g.
public static void main(String[] args) {
    Plate<Apple> p = new Plate<>();
}
interface Fruit{}
class Apple implements Fruit{}
class Plate<T extends Fruit> {
    T data;
}
4.1.5 通配符 ?
  • <? extends Parent> 指定了泛型类型的上界

  • <? super Child> 指定了泛型类型的下界

  • <?> 指定了没有限制的泛型类型

Plate<? extends Fruit> p = new Plate<Apple>(); // 上界
Plate<? super Apple> p = new Plate<Fruit>(); // 下界

注意:在编译之后程序会采取去泛型化的措施,也就是说Java中的泛型只在编译阶段有效,而不会进入到运行时阶段。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦除,并在对象进入和离开方法的边界处添加类型检查和类型转换的方法。


4.2 常见类库

4.2.1 java.util.Objects
  • 此类包含static实用程序方法(null或null防范,用于计算对象的哈希代码,返回对象的字符串,比较两个对象,检索索引或子范围值是否超出范围),用于操作对象或在操作前检查某些条件
  • 源码:
// static boolean equals(Object a, Object b)
public static boolean equals(Object a, Object b) {
    return (a == b) || (a != null && a.equals(b));
}

// static boolean isNull(Object obj)
public static boolean isNull(Object obj) { return obj == null; }

// static boolean nonNull(Object obj)
public static boolean isNull(Object obj) { return obj != null; }

// static boolean requireNonNull(T obj):检查对象引用是否不是null
public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException(); //如果是空,直接抛出异常
    return obj;
}
4.2.2 java.lang.Math
//四舍五入
Math.round(-100.5); // -100
4.2.3 java.util.Arrays

binarySearch, compare, equals, sort, toString, copyOf

int[] arr = {2,3,4,5,1};
System.out.println(arr); //打印的是内存地址(哈希值)
System.out.println(Arrays.toString(arr)); //打印[2,3,4,5,1]
Arrays.sort(arr); //排序
Arrays.binarySearch(arr, 6); //二分查找
arr = Arrays.copyOf(arr, 15); //扩容至长度为15
4.2.4 java.math.BigDecimal

实现小数的精准运算

// 构造方法
public BigDecimal(String val) {}

// 常用方法
public BigDecimal add(BigDecimal augend);
public BigDecimal subtract(BigDecimal augend);
public BigDecimal multiply(BigDecimal augend);
public BigDecimal devide(BigDecimal augend);

// e.g.
BigDecimal b1 = new BigDecimal("0.1");
BigDecimal b2 = new BigDecimal("0.2");
BigDecimal b3 = b1.add(b2); // 0.3
double d = b3.doubleValue(); // 转换为double类型
4.2.5 java.util.Date

表示特定的时刻,精度为毫秒

构造方法:

  • Date() 当前时间
  • Date(long date)

方法:

  • long getTime() 返回自1970年1月1日00:00:00GMT以来的毫秒数
  • void setTime(long time) 设置时间点
4.2.6 java.text.DateFormat
  • 用于格式化和解析日期字符串
  • 是一个抽象类,使用子类SimpleDateFormat
  • 常用方法:
    • String format(Date date) 格式化日期
    • Date parse(String source) 解析日期字符串
/**
 * y: 年
 * M: 月
 * d: 日
 * H: 时
 * m: 分
 * s: 秒
 */
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
String text = format.format(new Date()); // 将date对象格式化为字符串
Date date = format.parse("2021-12-12 12:12:12"); // 将符合格式的字符串转化为date对象,可用于计算时间差
4.2.7 java.util.Calendar
  • 是一个抽象类,通过其getInstance()方法创建对象
  • 年月日时分秒都存储在filed数组里,通过传入下标(例如Calendar.YEAR)用get()方法获取
  • 常用方法:
    • set get add
    • getTime 获取日历时间表示的Date对象
    • getActualMaximum 获取某字段的
Calendar cl = Calendar.getInstance(); //创建对象

int year = cl.get(Calendar.YEAR); //2020
int year = cl.get(Calendar.MONTH); //0-11
int day = cl.get(Calendar.DAY_OF_YEAR); //一年的第几天,从0开始

cl.set(Calendar.YEAR, 2021); //设置年为2021
cl.add(Calendar.YEAR, 1); //设置年+1

Date d = cl.getTime(); //获取日历时间表示的Date对象
int m = cl.getActualMaximum(Calendar.DAY_OF_MONTH); //当前月份的最大值
4.2.8 java.lang.System

常用方法:

  • gc() 运行垃圾回收器
  • exit(int status) 终止当前运行的Java虚拟机
  • static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) 复制数组
4.2.9 java.lang.String
  • 字符串是不变的,他们的值在创建后无法更改

    String str = "abc";
    // 相当于
    char data[] = {'a', 'b', 'c'};
    string str = new String(data);
    
  • String类 用final修饰,不能被继承

  • 两个字符串内容如果完全相同,则它们采用同一块内存地址(即可共享);但如果 是通过new创建的对象,一定是新开辟的空间

  • 字符串常量池(存在方法区中)

    1. 方法区Method Area(加载代码的内存区),又称永久代Permanent Generation,被所有线程共享
    
    2. 堆heap
    1) 一个JVM实例只存在一个堆内存,大小是可以调节的;
    2) 类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行;
    3) 堆 在逻辑上分为三部分(Perm):
    - 新生代 YoungGen:存刚创建的对象,gc回收很快
    - 老年代 OldGen:存常用的对象(在新生代中连续15次没有被回收)
    - 永久代 PermGen:类、方法、常量、static修饰的(不会被垃圾回收)
    
    3. JDK1.8演变
    字符串常量池还在堆,运行时常量池还在方法区,只不过方法区的实现从永久代变成了元空间Metaspace;
    元空间与堆不相连,但与堆共享物理内存,逻辑上可认为在堆中
    
  • 一种构造方法:

    String(byte[] bytes, Charset charset) 使用charset字符集解码字节数组

  • 方法:

    • int compareTo(String anotherString) 按字典顺序比较两个字符串
    • boolean contains(CharSequence s) 是否包含指定的char值序列
    • int indexOf(int ch) 指定字符第一次出现在字符串中的索引
    • String trim() 删除首尾空格
  • 字符串拼接:

    • 每+一次就在内存中创建一个新的String对象,即产生一次垃圾(在永久代里不会被回收)
    • 因此不应该使用String,应该使用StringBuffer / StringBuilder
    String, StringBuilder, StringBuffer 的区别:
        1. String 是字符串常量,不可变,修改时会创建了一个新的String对象,然后将指针指向它
        2. StringBuffer 和 StringBuilder 是字符串变量,使用时会对对象本身进行操作(会动态扩容),而不是生成新的对象再改变对象引用,最后可以用 toString()方法转成 String
        3. StringBuffer 是线程安全的;而 StringBuilder 不是(不能保证同步),速度更快,单线程时使用
        4. 在某些特别情况下, String 对象的字符串拼接其实被 Java Compiler 编译成了 StringBuffer 对象的拼接,所以这时 String 对象的速度不比 StringBuffer 对象慢,例如:
        
    	String s1 = "This is only a" + "simple" + "test";
    	StringBuffer Sb = new StringBuilder("This is only a" ).append("simple").append("test");
    
    	其实在 Java Compiler 里,自动做了如下转换:
    	String s1 = "This is only a simple test";
    
    	但如果拼接的字符串来自另外的 String 对象的话,Java Compiler 就不会自动转换了,速度也就没那么快了,例如:
        String s2 = “This is only a”;  
        String s3 = “ simple”;  
        String s4 = “ test”;  
        String s1 = s2 + s3 + s4;  
    

4.3 集合

4.3.1 Collections
  • Java类集中保存单值的最大操作接口
  • 子接口:List,Set(区分集合中是否允许有重复元素)
4.3.2 List
  • 常用的实现类:ArrayList, Vector, LinkedList
  • 常用方法:
    • E get(int index) 根据索引位置取出元素
    • int indexOf(Object o) 查找指定对象的位置
    • void add(int index, E element) 在指定位置添加元素
    • E remove(int index) 删除指定位置的元素(重载了继承的父类Collections中的boolean remove(Object o)方法)
ArrayList
  • 构造方法:

    • 无参(初始容量10):先构造一个长度为0的列表,而添加一个元素时会自动扩容
    • 一参(指定初始容量)
    • ArrayList(Collection<? extends E> c) 构造包含指定集合元素的列表
  • boolean add(E e) 一定返回true

    • 扩容:

      private Object[] grow(int minCapacity) {
          return elementData = Arrays.copyOf(elementData, newCapacity(minCapacity)); 
      }
      
      private int newCapacity(int minCapacity) {
          int oldCapacity = elementData.length;
          int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5倍
          //如果计算的新长度比需要的长度小(还不够存):0*1.5=0, 1*1.5=1, 添加一组数据,长度不可控
          if (newCapacity - minCapacity <= 0) { 
              // 如果是第一次创建,没有传长度
      		if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { 
                  return Math.max(DEFAULT_CAPACITY, minCapacity); // 默认长度(10)与minCapacity(第一次为1)取最大
              }
              if (minCapacity < 0)
                  throw new OutOfMemoryError();
              return minCapacity;
          }
          return (newCapacity - MAX_ARRAY_SIZE <= 0) ? newCapacity : hugeCapacity(minCapacity); // MAX_ARRAY_SIZE= Integer.MAX_VALUE-8
      }
      
Vector
  • 构造方法中可以指定容量增量(即比ArrayList多一种构造方法):

    Vector(int initialCapacity, int capacityIncrement)

ArrayList, Vector, LinkedList 区别
链表(LinkedList) vs 数组(ArrayList/Vector)
1. 数组是连续存储的,链表不必相连(灵活地分配内存空间)
2. 数组查找快(通过下标查询),增删慢(需要移动元素);链表查找慢 O(n),增删快 O(1)
3. ArrayList和Vector都是基于动态数组实现的(通过新数组覆盖老数组的方式扩容),而LinkedList是通过链表实现的

ArrayList vs Vector
1. ArrayList是线程不安全的,速度较快;而Vector是线程安全的,是同步的
2. Vector比ArrayList多了一种构造方法,可以指定容量增量(默认为一倍);而ArrayList的增量为0.5倍,不能指定
4.3.3 Iterator, ListIterator 迭代器
  • Iterator 用于迭代 Collections的所有集合(List, Set)
  • ListIterator 只能迭代 List的集合
    • 可以控制指针往前走:previous()
    • 可以插入元素(插入到next()返回的元素之前):add(E e)
    • 可以修改当前指针指定的数据:set(E e)
ArrayList<Integer> data = new ArrayList<>();
data.add(1);
data.add(2);
data.add(3);
Iterator<Integer> iterator = data.iterator();
while (iterator.hasNext()) {
    Integer i = iterator.next();
}
// remove前需要先获取:
Iterator<Integer> iterator = data.iterator();
iterator.next();
iterator.remove();
forEach
  • 用于迭代数组或Colleciton的集合(用迭代器)

    int[] arr = {5,4,3,2,1}
    for (int data : arr) {
        System.out.println(data);
    }
    //
    ArrayList<String> list = new ArrayList<>();
    list.add("1");
    list.add("2");
    list.add("3");
    for (String s: list) {
        System.out.println(s);
    }
    
4.3.4 Set
  • 不包含重复元素(包括null)
  • 获取元素方法(没有get(int index)方法,即不能通过传入下标的方式查找数据):
    • iterator()方法得到迭代器
    • toArray()变成数组
HashSet
  • 散列存放(哈希表/散列表,内部有一个HashMap对象),无序存储

  • 单值存储,即重复利用了双值存储的HashMap:

    private static final Object PRESENT = new Object();
    public boolean add(E e) {
        return map.put(e, PRESENT) == null; 
    }
    
TreeSet
  • 二叉树存储(基于TreeMap),有序(根据数据的顺序)

  • 其iterator方法返回的迭代器是快速失败的:在并发修改的情况下,迭代器快速而干净地失败

    • 快速失败:如果在创建迭代器之后修改了集合(除了通过迭代器自己的remove方法),迭代器将抛出ConcurrentModificationException

    • 安全失败(通常):遍历的是复制的集合,所以迭代时不会失败

TreeSet<String> data = new TreeSet<>();
data.add("c");
data.add("a");
data.add("b");
for (String s: data) {
    System.out.println(s); // a b c
}
4.3.5 Map<K, V>
  • 存储一个个的键值对数据

  • 键key 不可重复(所以Set的内部都使用了Map)

  • 每个键最多可映射一个值

  • 方法:

    • Set<K> keySet() 得到键的set
    • 遍历:对key进行迭代,调用V get(Object key)取到值
    • 存储:V put(K key, V value) 如果产生了替换,会返回旧值,否则返回null
    • 删除:V remove(Object key) default boolean remove(Object key, Object value)
HashMap
  • 实现:哈希桶:对象数组+链表/红黑树

    • 取模:hashCode()%N (初始桶数量16,则得到0-15的下标/索引,存到对应位置)

    • 数组的每个元素都是一个链表/红黑树(解决哈希值冲突问题)

    • 1.8优化:当哈希桶(链表)中的数据量>8,链表会转化成红黑树(更利于查找);

      ​ 当哈希桶中的数据量减少到6时,从红黑树转换为链表

    • 散列因子0.75:如果桶中有75%存了数据,扩容一倍(*2),可以指定

      • 过小:浪费内存空间
      • 过大:查询效率低
    • 扩容:散列,重建

  • 搜索:不需要遍历,只需得到hashcode取余运算得到下标

Hashtable, HashMap, ConcurrentHashMap, LinkedHashMap 区别
1. HashMap线程不安全(同时,不保证同步),效率高;Hashtable线程安全(排队机制),效率低
2. ConcurrentHashMap:采用分段锁机制保证线程安全,效率又比较高(只有当操作的是同一下标的桶时才需要排队)

HashMap不保证存储顺序(因为hashcode计算),LinkedHashMap能保证存储顺序(会同时存进双向链表中)

4.4 IO

4.4.1 java.io.File
  • 文件和目录路径名的抽象表示
  • 常用构造方法:
    • File(String pathname)
    • File(File parent, String child) 文件夹,新文件名称
    • File(String parent, String child)
  • 字段:
    • static String pathSeparator 与系统相关的路径分隔符
    • static String separator 与系统相关的名称分隔符
  • 常用方法:
    • boolean delete()
    • boolean deleteOnExit()
    • boolean createNewFile() 创建指定新文件(指定文件不存在时)
    • boolean mkdir() 创建目录
    • boolean mkdirs() 创建目录(包括不存在的父目录)
    • String getAbsolutePath() 获取文件/目录的绝对路径(从盘符开始)
    • String getPath() 返回的是定义时的路径
    • String getName() 获取文件/目录名称
    • String getParent() 获取父目录名称
    • File getParentFile() 获取父目录对象
    • long length() 获取文件的大小(字节)
    • boolean exists() 判断文件/目录是否存在
    • boolean isDirectory() 判断对象是否为目录
    • boolean isFile() 判断对象是否为文件
    • File[] listFiles() 获取目录中所有的对象
    • String[] list() 获取目录中所有的对象名称
    • boolean renameTo(File deat) 重命名/移动
// 获取字段
System.out.println(File.pathSeparator); // ; 路径分隔符
System.out.println(File.separator); // \ 名称分隔符

// 构造方法1,创建新文件夹
File dir = new File("c://haha");
dir.mkdir();
// 构造方法2,创建新文件
File a = new File(dir, "a.txt");
a.createNewFile();
// 构造方法3,创建新文件
File b = new File("c://haha", "b.txt");
b.createNewFile();

//删除
a.delete();
b.delete();
  • 文件遍历

    public static void main(String[] args) throws IOException {
        File e = new File("e:\\");
        File[] files = e.listFiles();
        listFiles(files);
    }
    
    // 遍历目录,删除所有200MB以上的avi文件
    public static void listFiles(File[] files) {
        if (files != null && files.length>0) {
            for (File file : files) {
                if (file.isFile()) {
                	// 是文件
                    if (file.getName().endsWith(".avi")) {
                    	// 找到了一个avi文件
                        if (file.length() > 200*1024*1024) {
                            file.delete();
                            System.out.println(file.getAbsolutePath()+" 已删除");
                        }
                    }
                } else {
                    // 文件夹
                    File[] files2 = file.listFiles();
                    listFiles(files2); // 递归遍历子文件夹
                }
            }
        }
    }
    
文件过滤器 FileFilter
public static void listFiles(File dir) {
    // 1. 创建一个过滤器 并 描述规则
    FileFilter filter = new FileFilter() { // 匿名内部类,只使用一次
        @Override
        public boolean accept(File pathname) {
            if (pathname.getName().endsWith(".avi") || pathname.isDirectory())
                return true;
            return false;
        }
    };
    // 2. 获取文件,遍历
    File[] files = dir.listFiles(filter);
    if (dir != null && dir.length>0) {
        for (File file : dir) {
            if (file.isDirectory())
                listFiles(file);
            else 
                System.out.println(file.getAbsolutePath());
        }
    }
}
4.4.2 IO流 概述
  • 将数据传输操作看作一种数据的流动,按照流动的方向分为输入Input和输出Output
  • Java中的IO操作主要指的是java.io包下的一些常用类的使用,通过这些常用类对数据进行读取(输入Input) 和 写出(输出Output)
  • 数据传输时都是以二进制形式存储的
  • IO流的分类:
    • 按方向分:输入流 输出流
    • 按流动的数据类型:
      • 字节流:
        • 输入流:InputStream
        • 输出流:OutputStream
      • 字符流:
        • 输入流:Reader
        • 输出流:Writer
4.4.3 字节流
java.io.OutputStream
  • 字节输出流的所有类的超类
  • 常用方法:
    • void close() 关闭此输出流并释放与该流相关的所有系统资源
    • void flush() 刷新此输出流并强制写出任何缓冲的输出字节
    • void write(byte[] b) 将所有字节从指定字节数组写入到此输出流
    • void write(byte[] b, int off, int len) 将从偏移量off开始的指定字节数组中的len个字节写入输出流
    • abstract void write(int b) 将指定的字节写入此输出流(写入的字节是b的八个地位)
java.io.FileOutputStream
  • OutputStream的常用子类
  • 构造方法:
    • FileOutputStream(File file)
    • FileOutputStream(File file, boolean append) append=true表示接着原来的写
    • FileOutputStream(String name)
    • FileOutputStream(String name, boolean append)
public static void main(String[] args) throws IOException {
    FileOutputStream fos = new FileOutputStream("c://a.txt");
	fos.write(65); // A
    byte[] bytes = {65,66,67,68,69};
    fos.write(bytes); // AABCDE
    fos.close();
}
public static void main(String[] args) throws IOException {
    FileOutputStream fos = new FileOutputStream("c://a.txt");
    byte[] bytes = {65,66,67,68,69};
    fos.write(bytes); // ABCDE; 如果改成FileOutputStream("c://a.txt", true)则为AABCDEABCDE
    fos.close();
}
public static void main(String[] args) throws IOException {
    FileOutputStream fos = new FileOutputStream("c://a.txt");
    byte[] bytes = "ABCDE".getBytes();
    fos.write(bytes, 1, 2); // BC
    fos.close();
}
java.io.InputStream
  • 字节输入流:把硬盘中的文件读取输入到内存中
  • 常用方法:
    • abstract int read() 从输入流中读取下一个数据字节(返回值范围0-255;如果由于达到流末尾而没有可用字节,则返回 -1)
    • int read(byte[] b) 从输入流中读取一些字节数并存储到缓冲区数组b
    • void close()
java.io.FileInputStream
  • InputStream的常用子类
  • 构造方法:
    • FileInputStream(File file)
    • FileInputStream(String name)
public static void main(String[] args) {
    FileInputStream fis = new FileInputStream("c://a.txt"); // abcdef
    // 一次读取一个字节
    byte b = (byte) fis.read(); // 97
    char b2 = (char) fis.read(); // b
    // 循环读取一个字节直至结束:cdef
    while (b != -1) {
        byte b = (byte) fis.read();
        System.out.print((char) b); 
    }
    fis.close();
}
public static void main(String[] args) {
    FileInputStream fis = new FileInputStream("c://a.txt"); // a-z
    // 一次读取10个字节
    byte[] bytes = new byte[10];
    fis.read(bytes);
    System.out.println(new String(bytes)); //abcdefghij
    fis.read(bytes);
    System.out.println(new String(bytes)); //klmnopqrst
    fis.read(bytes);
    System.out.println(new String(bytes)); //uvwxyzqrst 放在数组的前6位,后面的没有清空
    fis.close();
}

解决该问题:

public static void main(String[] args) {
    FileInputStream fis = new FileInputStream("c://a.txt"); // a-z
    // 一次读取一组(10个)字节(常用,减少IO频率)
    byte[] bytes = new byte[10];
    int len = fis.read(bytes);
    while (len != -1) {
        System.out.println(new String(bytes, 0, len)); 
        len = fis.read(bytes);
    }
    /* abcdefghij
       klmnopqrst
       uvwxyz
     */
    fis.close();
}
文件加密和解密

加密/解密用异或运算:任何数据^相同的数字两次=其本身

public static void main(String[] args) {
    System.out.print("请输入文件存储的全路径:");
    Scanner input = new Scanner(System.in);
    String fileName = input.nextLine();
    // 原文件: a.png
    File oldFile = new File(fileName);
    // 加密存储的新文件: mi-a.png
    File newFile = new File(oldFile.getParentFile() + "mi-" + oldFile.getName());
      
    FileInputStream fis = new FileInputStream(oldFile);
    FileOutputStream fos = new FileOutputStream(newFile);
    while (true) {
        int b = fis.read();
        if (b == -1) break;
        fos.write(b^10); 
    }
    System.out.println("加密或解密完成");
    fis.close();
    fos.close();
}
4.4.4 字符流
  • 以字符为单位,只能操作文字
Writer 字符输出流
  • 常用方法:
    • abstract void close()
    • Writer append(CharSequence csq) 将指定字符串序列追加到此Writer
    • void write(int c) 写一个字符
    • void write(char[] cbuf) 写一个字符数组
    • abstract void write(char[] cbuf, int off, int len) 写一个字符数组的一部分
    • void write(String str) 写一个字符串
    • void write(String str, int off, int len) 写一个字符串的一部分
FileWriter
  • 构造方法:
    • FileWriter(File file)
    • FileWriter(File file, boolean append)
    • FileWriter(File file, Charset charset)
public static void main(String[] args) {
    FileWriter fw = new FileWriter("c://b.txt");
    fw.write('a');
    fw.write("床前明月光");
    FileWriter fw2 = (FileWriter) fw.append("锄禾日当午"); //fw==fw2
    fw2.append(", ").append("汗滴禾下土");
    fw.close(); 
}
flush刷新管道
  • 操作字符输出流时需要注意:

    输出方(内存)给输入方(硬盘)发送的过程中,有一个缓存

  • 字符输出的时候,刷新缓存空间,强制把缓存空间的内容写出到文件:fw.flush()

  • close()时会自动刷新

Reader 字符输入流
  • 常用方法:
    • abstract void close()
    • int read() 一次读取一个字符
    • int read(char[] cbuf) 一次读取一组字符至数组
    • int read(char[] cbuf, int off, int len) 一次读取字符至数组的一部分
FileReader
  • 构造方法:
    • FileReader(String fileName)
    • FileReader(String fileName, Charset charset)
    • FileReader(File file)
public static void main(String[] args) {
    FileReader fr = new FileReader("b.txt");
    // 循环一次读一个字符
    while (true) {
        int c = fr.read();
        if (c == -1) break;
        System.out.println((char) c);
    }
    fr.close();
}
public static void main(String[] args) {
    FileReader fr = new FileReader("b.txt");
    // 循环一次读一组字符到数组
    char[] chars = new char[100]; //注意:默认为0(空格)
    fr.read(chars);
    System.out.println(new String(chars)); //不足100的话后面跟着空格
    fr.close();
}

解决:

public static void main(String[] args) {
    FileReader fr = new FileReader("b.txt");
    // 循环一次读一组字符到数组
    char[] chars = new char[100]; //注意:默认为0(空格)
    int len = fr.read(chars);
    System.out.println(new String(chars, 0, len)); //不足100的话后面跟着空格
    fr.close();
}
4.4.5 转换流 InputStreamReader
  • 将字节流 装饰为 字符流:使用了装饰者设计模式
  • 输入流转换:
public static void main(String[] args) {
    FileInputStream fis = new FileInputStream("c://a.txt");
    // 将字节输入流 转换为 字符输入流
    // 参数1:要转换的字节流; [参数2:编码名称]
    InputStreamReader isr = new InputStreamReader(fis, "gbk");
    while (true) {
        int c = isr.read();
        if (c == -1) break;
        System.out.print((char) c);
    }
    
}
  • 输入流转换:
public static void main(String[] args) {
    FileInputStream fis = new FileInputStream("c://a.txt");
    // 将字节输入流 转换为 字符输入流
    // 参数1:要转换的字节流; [参数2:编码名称]
    InputStreamReader isr = new InputStreamReader(fis, "gbk");
    while (true) {
        int c = isr.read();
        if (c == -1) break;
        System.out.print((char) c);
    }
    isr.close();
}
  • 输出流转换:
public static void main(String[] args) {
    FileOutputStream fos = new FileOutputStream("c://a.txt");
    // 将字节输出流 转换为 字符输出流
    OutputStreamWriter osw = new OutputStreamWriter(fos);
    osw.write("锄禾日当午,汗滴禾下土");
    osw.flush();
    osw.close();
}
4.4.6 打印流 PrintStream, PrintWriter
  • 字符输出(System.out)
  • PrintStream 是 FileOutputStream 的子类;PrintWriter 是 Writer 的子类
PrintStream ps = new PrintStream("c://c.txt");
ps.println("锄禾日当午,汗滴禾下土");
PrintWriter pw = new PrintWriter("c://c.txt");
pw.println("锄禾日当午,汗滴禾下土");
pw.flush(); //字符流需要刷新管道
  • 也可以用于转换字节流(建议)

    FileOutputStream fos = new FileOutputStream("c://c.txt");
    PrintWriter pw = new PrintWriter(fos);
    pw.println("锄禾日当午,汗滴禾下土");
    pw.flush();
    
4.4.7 缓存读取流 BufferedReader
  • 将字符输入流转换为带有缓存,可以一次读取一行的缓存字符读取流
  • 读到结尾时返回null
FileReader fr = new FileReader("c://c.txt");
BufferedReader br = new BufferedReader(fr);
String text = br.readLine();
System.out.println(text);
4.4.8 收集异常日志
try {
    String s = null;
    s.toString();
} catch (Exception e) {
    PrintWriter pw = new PrintWriter("c://bug.txt");
    // 打印日期
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-dd HH:mm:ss");
    pw.println(sdf.format(new Date()));
    // 写入异常
    e.printStackTrace(pw);
    pw.close();
}
4.4.9 Properties
  • 是HashTable的子类,是Map集合

  • .properties文件:一行存储一个键值对,可以存储多行

  • 方法:

    • synchronized Object put(Object key, Object value) 存储键值对
    • synchronized void load(Reader reader) 把properties文件加载成程序中的Map集合
    • synchronized void load(InputStream inStream)
    • void store(Writer writer, String comments) 存储为properties文件,字符串为注释
    • void store(OutputStream out, String comments)
  • 存储:

    public static void main(String[] args) throws IOException {
        Properties ppt = new Properties();
        ppt.put("name", "金苹果");
        ppt.put("info", "讲述了苹果种植的过程");
        FileWriter fw = new FileWriter("c://book.properties");
        ppt.store(fw, "存储的图书");
        fw.close();
    }
    

    book.properties:

    #\u5B58\u50A8\u7684\u56FE\u4E66
    #Sun Aug 23 00:27:56 CST 2020
    name=金苹果
    info=讲述了苹果种植的过程
    
  • 读取:

    public static void main(String[] args) throws IOException {
        Properties ppt = new Properties();
        Reader r = new FileReader("c://book.properties");
        ppt.load(r);
        //System.out.println(ppt.get("name"));
        //System.out.println(ppt.get("info"));
        System.out.println(ppt.getProperty("name"));
        System.out.println(ppt.getProperty("info"));
    }
    
4.4.10 序列化技术
  • 对象序列化:将 Java 对象的状态转换为字节数组,以便存储或传输

  • 反序列化:将字节数组转换回 Java 对象原有的状态

  • 实现:该类需要实现Serializable接口,其包含的对象的类也需要实现Serializable接口

    public static void main(String[] args) throw IOException {
        // 序列化
        Book b = new Book("金苹果", "描述了苹果种植的过程");
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputSteam("c://book.txt"));
        oos.writeObject(b);
        oos.close();
        
        // 反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("c://book.txt"));
        Book o = (Book) ois.readObject();
    }
    
    static class Book implements Serializable {
        private String name;
        private String info;
    
        public Book() {}
        public Book(String name, String info) {
            this.name = name;
            this.info = info;
        }
        public void setName(String name) { this.name = name; }
        public String getName() { return name; }
        public void setInfo(String info) { this.info = info; }
        public String getInfo() { return info; }
    }
    
4.4.11 try-with-resources

关闭并释放资源:

  • JDK1.7之前:

    public static void main(String[] args) {
        FileReader fr = null;
        try {
            fr = new FileReader("c://book.txt");
            int c = fr.read();
            System.out.println((char) c);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fr.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
  • JDK1.7:FileReader extends InputStreamReader extends Reader implements Closeable extends AutoCloseable { void close() throws Exception; }

    =>能在try()括号中创建的对象必须实现AutoCloseable接口,即必然拥有close()方法

    public static void main(String[] args) {
        try (FileReader fr = new FileReader("c://book.txt")) {
            int c = fr.read();
            System.out.println((char) c);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        try (CloseDemo d = new CloseDemo()) {
    
        } catch (Exception e) {
    
        }
    }
    static class CloseDemo implements Closeable {
        
        @Override
        public void close() throws IOException {
            System.out.println("close方法被调用了");
        }
    }
    
  • JDK9:

    public static void main(String[] args) throw FileNotFoundException {
        FileReader fr = new FileReader("c://book.txt");
        PrintWriter pw = new PrintWriter("c://book.txt");
        try (fr; pw) {
            int c = fr.read();
            System.out.println((char) c);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值