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;@Overridepublic String getData() {return text;}
}// 不指定类型:
public class Interface2<T> implements InterfaceName<T> {private T data;@Overridepublic 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); //设置年+1Date 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()方法转成 String3. 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() { // 匿名内部类,只使用一次@Overridepublic 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); // Abyte[] bytes = {65,66,67,68,69};fos.write(bytes); // AABCDEfos.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)则为AABCDEABCDEfos.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); // BCfos.close();
}
java.io.InputStream
- 字节输入流:把硬盘中的文件读取输入到内存中
- 常用方法:
abstract int read()
从输入流中读取下一个数据字节(返回值范围0-255;如果由于达到流末尾而没有可用字节,则返回 -1)int read(byte[] b)
从输入流中读取一些字节数并存储到缓冲区数组bvoid 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(); // 97char b2 = (char) fis.read(); // b// 循环读取一个字节直至结束:cdefwhile (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)); //abcdefghijfis.read(bytes);System.out.println(new String(bytes)); //klmnopqrstfis.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);}/* abcdefghijklmnopqrstuvwxyz*/fis.close();
}
文件加密和解密
加密/解密用异或运算:任何数据^相同的数字两次=其本身
public static void main(String[] args) {System.out.print("请输入文件存储的全路径:");Scanner input = new Scanner(System.in);String fileName = input.nextLine();// 原文件: a.pngFile oldFile = new File(fileName);// 加密存储的新文件: mi-a.pngFile 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)
将指定字符串序列追加到此Writervoid 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==fw2fw2.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 {@Overridepublic 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();} }
Java学习笔记 - 4 Java核心类库相关推荐
- Java学习笔记1:Java中有关print、println、printf的用法和区别
Java学习笔记1:Java中有关print.println.printf的用法和区别 最近在学习java,写一些笔记记录下. 1.print()函数是一般的标准输出,但是不换行. 2.println ...
- Java学习笔记(java基础)
Java学习笔记(第一周) Java 介绍 Java 发展方向 JVM , JDK , JRE 名词解释 Java语言的特点 Java安装 安装包的下载 配置环境变量 验证是否安装成功 Java的第一 ...
- 【java】java学习笔记之java oop(面向对象)
如下图所示为笔者总结的java oop(面向对象)学习笔记,其中,附带有代码示例(未展开),方便理解记忆.需要源文件的请到我的资源中下载,下载地址:https://download.csdn.net/ ...
- Java学习笔记01—Java概述、数据类型、变量、标识符、类型转换
1. Java概述 1.1 Java语言发展史(了解) 语言:人与人交流沟通的表达方式 计算机语言:人与计算机之间进行信息交流沟通的一种特殊语言 Java语言是美国Sun公司(Stanford Uni ...
- 【java】java学习笔记之java常用类
如下图所示为笔者总结的java常用类学习笔记,其中,附带有代码示例(未展开),方便理解记忆.需要源文件的请到我的资源中下载,下载地址:https://download.csdn.net/downloa ...
- java学习笔记(一) ----java下常用的包功能
***java下常用的包*** java.lang----包含一些java语言的核心类,如String,Math,Integer,System,Thread,提供常用的功能. java.awt---- ...
- Java 学习笔记(4)——java 常见类
上次提前说了java中的面向对象,主要是为了使用这些常见类做打算,毕竟Java中一切都是对象,要使用一些系统提供的功能必须得通过类对象调用方法.其实Java相比于C来说强大的另一个原因是Java中提供 ...
- Java学习笔记-7.Java IO流
一.输入/输出流 1.流:不同类型的输入.输出源 数据流:输入或输出的数据 Java数据流的所有接口和类都是在java.io包中定义的,因此应在程序开头加入 import java.io.* 2 ...
- JAVA学习笔记 03 - JAVA语言程序结构
本文是Java基础课程的第三课.计算机语言的程序结构无外乎顺序结构.分支结构.循环结构,本文主要介绍Java语言中这些程序结构是如何实现的 文章目录 一.程序结构 二.分支结构 1.单分支if语句 2 ...
最新文章
- 阅读准备-构建redis容器
- “docker exec“ requires at least 2 arguments. See ‘docker exec --help‘.
- 数据结构单向不循环链表实现多项式合并
- [CodeForces]Codeforces Round #432 (Div. 2)
- CF 570D. Tree Requests [dsu on tree]
- spring boot 整合mybatis + swagger2
- 乐优商城(01)--项目启动
- QQ2000 的聊天室刷屏机设计技术
- [渝粤教育] 浙江大学 设计思维与创新设计 参考 资料
- 路由器刷openwrt固件准备工作
- Treedp贪吃的九头龙详解
- Windows10视频文件没有预览图的解决办法
- 用cmd 改电脑ip
- python100个常用术语_Python 常用术语
- CSS的水平居中、垂直居中和水平垂直居中
- Android DOM解析xml
- Java实现随机人名抽取
- 机器人届的“擎天柱”来了!能够在空中变形以快速栖息的四旋翼机器人
- MATLAB生成随机点
- 暗备用的运行状态_电力系统自动装置随堂练习