javaday11
目录
Day11---IO2+泛型+集合. 1
1 IO综合练习. 1
1.1 练习1:文件复制. 1
1.2 练习2:批量读写. 1
2 序列化 / 反序列化. 1
2.1 概述. 1
2.2 特点/应用场景. 1
2.3 ObjectOutputStream1
2.4 ObjectInputStream1
2.5 练习1:将学生信息序列化至磁盘【序列化】. 1
3 泛型. 1
3.1 概念. 1
3.2 作用. 1
3.3 泛型声明. 1
3.4 常用名称. 1
3.5 用途:编译时类型检查. 1
3.6 用途:代码通用性更强. 1
4 Collection接口. 1
4.1 概述. 1
4.2 集合的继承结构. 1
4.3 常用方法. 1
4.4 练习1:测试常用方法. 1
5 List接口. 1
5.1 概述. 1
5.2 特点. 1
5.3 常用方法. 1
5.4 练习1:测试常用方法. 1
6 ArrayList1
6.1 概述. 1
6.2 创建对象. 1
6.3 练习1:测试常用方法. 1
7 LinkedList1
7.1 概述. 1
7.2 常用方法. 1
7.3 练习1:测试迭代器遍历. 1
8 扩展. 1
8.1 BIO、NIO、AIO的区别. 1
8.2 数组和链表区别. 1
Day11---IO2+泛型+集合
1 IO综合练习
1.1 练习1:文件复制
创建day11工程,
创建cn.tedu.io包
创建Test1_Copy.java
package cn.tedu.io;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Scanner;
//测试文件的复制
public class Test1_Copy {
public static void main(String[] args) {
//1、接收键盘输入源文件路径 和 目标文件路径
String fromStr = new Scanner(System.in).nextLine();
File from = new File(fromStr);
String toStr = new Scanner(System.in).nextLine();
File to = new File(toStr);
//2、调用copy()完成复制
copy(from,to);
}
//复制文件
public static void copy(File from, File to) {
InputStream in = null;//声明in变量,保证finally可用
OutputStream out = null;//声明out变量,保证finally可用
try {
//1、高效读取from文件
in = new BufferedInputStream(new FileInputStream(from));
//2、高效写出到to文件
out = new BufferedOutputStream(new FileOutputStream(to));
//3、正式开始 边读边写
int b = 0;//定义变量,记录读取到的数据
while( ( b = in.read() ) != -1) {
out.write(b);//读到一个字节就写出一个字节
}
System.out.println("文件复制完成...");
} catch (Exception e) {
System.out.println("文件复制失败...");
e.printStackTrace();
}finally {//保证资源一定会关闭
//4、关闭资源
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
1.2 练习2:批量读写
package cn.tedu.io;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Scanner;
//批量读写
public class Test2_Copy2 {
public static void main(String[] args) {
// 1、接收键盘输入源文件 路径 和 目标文件路径
String fromStr = new Scanner(System.in).nextLine();// D:\teach\1.txt
File from = new File(fromStr);
String toStr = new Scanner(System.in).nextLine();// D:\teach\222.txt
File to = new File(toStr);
// 2、调用copy()完成复制
copy(from, to);
}
// 复制文件
private static void copy(File from, File to) {
InputStream in = null;// 声明in变量,保证finally可用
OutputStream out = null;// 声明out变量,保证finally可用
try {
// 1、高效读取from文件
in = new BufferedInputStream(new FileInputStream(from));
// 2、高效写出到to文件
out = new BufferedOutputStream(new FileOutputStream(to));
// 3、正式开始 边读边写
int b = 0;// 定义变量,记录读取到的数据
//字节数组,提高单字节读写效率,底层数组默认大小是8*1024
//1024字节=1KB
byte[] bs = new byte[8*1024];
while ((b = in.read(bs)) != -1) {
out.write(bs);// 读到一个字节就写出一个字节
}
System.out.println("文件复制完成...");
} catch (Exception e) {
System.out.println("文件复制失败...");
e.printStackTrace();
} finally {// 保证资源一定会关闭
// 4、关闭资源
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2 序列化 / 反序列化
2.1 概述
序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。
在序列化期间,对象将其当前状态写入到临时或持久性存储区。
以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。
序列化:利用ObjectOutputStream,对象的信息,按固定格式转成一串字节值输出并持久保存到磁盘化。
反序列化:利用ObjectInputStream,读取磁盘中序列化数据,重新恢复对象。
2.2 特点/应用场景
1、 需要序列化的文件必须实现Serializable接口以启用其序列化功能。
2、 不需要序列化的数据可以被修饰为static的,由于static属于类,不随对象被序列化输出。
3、 不需要序列化的数据也可以被修饰为transient临时的,只在程序运行期间,在内存中存在不会被序列化持久保存。
4、 在反序列化时,如果和序列化的版本号不一致时,无法完成反序列化。
5、 每个被序列化的文件都有一个唯一id,如果没有添加编译器会根据类的定义信息计算产生一个版本号。
6、 常用于服务器之间的数据传输,序列化成文件,反序列化读取数据。
7、 常用于使用套接字流在主机之间传递对象。
2.3 ObjectOutputStream
ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。
ObjectOutputStream(OutputStream out)
创建写入指定 OutputStream 的 ObjectOutputStream。
void writeObject(Object obj)
将指定的对象写入 ObjectOutputStream。
2.4 ObjectInputStream
ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
ObjectInputStream(InputStream in)
创建从指定 InputStream 读取的 ObjectInputStream。
Object readObject()
从 ObjectInputStream 读取对象,读取序列化数据。
2.5 练习1:将学生信息序列化至磁盘【序列化】
package cn.tedu.seri;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
//对象的序列化反序列化
public class Test3_Serializable {
public static void main(String[] args) {
// method();//对象的序列化
method2();//对象的反序列化
}
//对象的反序列化:指从磁盘中加载被序列化过的文件,变成Java对象的过程 -- 读取
//要求:1、类必须有可序列化的标志,实现接口 2、可序列化的类必须有唯一的versionId才能反序列化成功
private static void method2() {
try {
//1、创建ObjectInputStream对象
ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:\\teach\\1.txt"));
//2、反序列化--恢复成Student对象
Student s = (Student) in.readObject();//-向下转型-把父类类型转成子类类型-强转
System.out.println(s);//默认显示地址值,重写toString()看属性值
//3、关闭资源
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//对象的序列化:指把程序中产生的对象永久保存到磁盘中 -- 写出
private static void method() {
try {
//1、创建Student对象
Student s = new Student("jack",20,"BJ","java");
//2、序列化
ObjectOutputStream out =
new ObjectOutputStream(new FileOutputStream("D:\\teach\\1.txt"));
out.writeObject(s);
//3、关闭资源
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//创建Student类
//4、想要完成序列化,类必须有标志,实现序列化接口,接口里是空实现,只是用来做标志
class Student implements Serializable{
private static final long serialVersionUID = 1L;
//构造方法:source-倒数第三个-ok
public Student() { }
public Student(String name, int age, String addr, String subject) {
super();
this.name = name;
this.age = age;
this.addr = addr;
this.subject = subject;
}
private String name;
private int age;
private String addr;
private byte gender;
private String subject;
private byte snum;
//get()/set()
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;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
public byte getGender() {
return gender;
}
public void setGender(byte gender) {
this.gender = gender;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public byte getSnum() {
return snum;
}
public void setSnum(byte snum) {
this.snum = snum;
}
//默认显示地址值,重写toString()看属性值
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", addr=" + addr + ", gender=" + gender + ", subject="
+ subject + ", snum=" + snum + "]";
}
}
3 泛型
3.1 概念
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable{}
public interface Deque<E> extends Queue<E> {}
public interface Queue<E> extends Collection<E> {}
public interface Collection<E> extends Iterable<E> {}
我们上面的代码中出现的<?>是什么东西呢 。它叫泛型,常用来和集合对象一同使用,所以我们在开始学习集合之前,必须先了解下什么是泛型。而且泛型概念非常重要,它是程序的增强器,它是目前主流的开发方式。
泛型是(Generics)JDK1.5 的一个新特性,其实就是一个『语法糖』。
本质上就是编译器为了提供更好的可读性而提供的一种小手段,小技巧,虚拟机层面是不存在所谓『泛型』的概念的。
3.2 作用
l 通过泛型的语法定义,约束集合元素的类型,编译器可以在编译期提供一定的类型安全检查,避免运行时才暴露bug。
l 代码通用性更强,后面有案例
l 泛型可以提升程序代码的可读性,但它只是一个语法糖(编译后这样的东西就被删除,不出现在最终的源代码中),对于JVM运行时的性能是没有任何影响的。
3.3 泛型声明
泛型可以在接口、方法、返回值上使用:
java.util.List泛型接口/类:
public interface Collection<E> {}
泛型方法的声明:
public <E> void print(E e) {}
在方法返回值前声明了一个<E>表示后面出现的E是泛型,而不是普通的java变量。
3.4 常用名称
l E - Element (在集合中使用,因为集合中存放的是元素)
l T - Type(Java 类)
l K - Key(键)
l V - Value(值)
l N - Number(数值类型)
l ? - 表示不确定的java类型
3.5 用途:编译时类型检查
package cn.tedu.generic;
import java.util.ArrayList;
import java.util.List;
//泛型案例
public class Test4_Generic {
public static void main(String[] args) {
//数组里的元素都是相同类型
int[] a = {1,2,3,4,5};
List list = new ArrayList();
//向集合中添加元素
list.add("rose");
list.add(9.9);
list.add(10);
//如果我想要把数据都获取出来
//增强for循环 foreach结构 1 2:3 3是要遍历的数据 1是数据的类型 2就是个变量名
for(Object obj : list) {
String s = (String)obj;//-向下转型,把obj是Object类型转成小类型String类型-强转
System.out.println(s);
}
// Exception in thread "main" java.lang.ClassCastException: java.lang.Double cannot be cast to java.lang.String
//以上的错误就是因为,编译时期不报错,运行时期才说类型转换异常。
//1、<?>是泛型的标志,?的类型必须是引用类型 --包装类类型才是引用类型基本类型
//2、泛型常用于类型检查
List<Integer> list2 = new ArrayList<>();
list2.add(100);
//3、由于list集合已经要求了元素的类型必须是String类型,所以加入的元素只要不是String各类型都会报错
//好处:把运行时期才会报的ClassCastException提前到了编译时期
// list2.add(“10”);
}
}
3.6 用途:代码通用性更强
传统方式通过重载多态实现,方法同名,参数类型不同。
package cn.tedu.generic;
import java.util.ArrayList;
import java.util.List;
//泛型案例
public class Tes5_Generic2 {
public static void main(String[] args) {
Integer[] a = {100,200,300,400,500};
print(a);
Double[] b = {1.1,2.2,3.3,4.4,5.5};
print(b);
//这样不好,每次要打印不同类型的数据,都需要提供对应的print()
}
private static void print(Double[] b) {
for (Double d : b) {
System.out.println(d);
}
}
private static void print(Integer[] a) {
for (Integer i : a) {
System.out.println(i);
}
}
}
泛型方式
package cn.tedu.generic;
//泛型案例
public class Tes5_Generic2 {
public static void main(String[] args) {
Integer[] a = {100,200,300,400,500};
print(a);
Double[] b = {1.1,1.2,1.3};
print(b);
String[] c = {"","","","",""};
print(c);
}
//2、写出通用代码,不需要像以前一样,new个[],就需要提供对应的print()
private static <E> void print(E[] e) {
for (E e2 : e) {
System.out.println(e2);
}
}
}
4 Collection接口
4.1 概述
英文名称Collection,
是用来存放对象的数据结构。
其中长度可变,而且集合中可以存放不同类型的对象。
并提供了一组操作成批对象的方法。
数组的缺点:长度是固定不可变的,访问方式单一,插入、删除等操作繁琐。
4.2 集合的继承结构
Collection接口
-- List接口 : 数据有序,可以重复。
-- ArrayList子类
-- LinkedList子类
-- Set接口 : 数据无序,不可以存重复值
-- HashSet子类
-- Map接口 : 键值对存数据
-- HashMap
Collections工具类
4.3 常用方法
boolean add(E e):添加元素。
boolean addAll(Collection c):把小集合添加到大集合中 。
boolean contains(Object o) : 如果此 collection 包含指定的元素,则返回 true。
boolean isEmpty() :如果此 collection 没有元素,则返回 true。
Iterator<E> iterator():返回在此 collection 的元素上进行迭代的迭代器。
boolean remove(Object o) :从此 collection 中移除指定元素的单个实例。
int size() :返回此 collection 中的元素数。
Objec[] toArray():返回对象数组
4.4 练习1:测试常用方法
package cn.tedu.collection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
//测试collection接口
public class Test6_Collection {
public static void main(String[] args) {
//1、创建Collection对象
//a、Collection是集合的层次结构的父接口,存放的都是对象,这些对象也叫元素,默认是Object
Collection<String> c = new ArrayList<>();
//2、常用方法
c.add("tony");
c.add("hanmeimei");
c.add("lilei");
c.add("xiongda");
c.add("xionger");
// c.clear();
System.out.println(c);
//
System.out.println( c.contains("lilei") );
System.out.println( c.hashCode() );
System.out.println( c.isEmpty() );
System.out.println( c.equals("tony") );
System.out.println( c.isEmpty() );
System.out.println( c.remove("hanmeimei") );
System.out.println(c);
System.out.println( c.size() );
System.out.println( Arrays.toString( c.toArray() ) );
//
System.out.println();
//
Collection<String> c2 = new ArrayList<>();
c2.add("华安");
c2.add("秋香姐");
System.out.println( c.addAll(c2) );
System.out.println( c.containsAll(c2) );
// System.out.println( c.removeAll(c2) );//移除c2的元素
// System.out.println( c.retainAll(c2) ); //移除c的元素
System.out.println(c);
//!!!迭代获取集合里的每个元素
// Iterator<E> iterator() :返回在此 collection 的元素上进行迭代的迭代器。
Iterator it = c.iterator();//想要获取集合中的每个元素iterator()
while( it.hasNext() ) {//判断有没有元素
Object obj = it.next();//获取遍历到的元素
System.out.println(obj);
}
}
}
5 List接口
5.1 概述
有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
5.2 特点
1、 数据有序
2、 允许存放重复元素
3、 元素都有索引
5.3 常用方法
ListIterator<E> listIterator()
返回此列表元素的列表迭代器(按适当顺序)。
ListIterator<E> listIterator(int index)
返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始。
void add(int index, E element)
在列表的指定位置插入指定元素(可选操作)。
boolean addAll(int index, Collection<? extends E> c)
将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)。
List<E> subList(int fromIndex, int toIndex)
返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。
E get(int index)
返回列表中指定位置的元素。
int indexOf(Object o)
返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
5.4 练习1:测试常用方法
package cn.tedu.collection;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
//测试List接口
public class Test7_List {
public static void main(String[] args) {
//1、创建对象
//List是接口
List<Integer> list = new ArrayList<>();
//2、常用方法
list.add(100);
list.add(101);
list.add(102);
list.add(103);
list.add(102);
list.add(105);
System.out.println(list);
// list.clear();
System.out.println(list.contains(100));
System.out.println(list.equals(101));
System.out.println(list.hashCode() );
System.out.println( list.isEmpty() );
System.out.println(list.remove(2) );
System.out.println(list.size() );
System.out.println(list.toArray() );
//特有方法
System.out.println(list+"======");
System.out.println( list.indexOf(105) );
System.out.println(list.lastIndexOf(102) );
list.add(3,105);
System.out.println(list);
System.out.println(list.get(3));
System.out.println(list.set(0,99));
System.out.println(list);
System.out.println(list.subList(2, 4));//含头不含尾,类似于String.subString()
System.out.println(list);
//遍历集合中元素
//ListIterator继承了Iterator,并提供了更加完善的功能,除了正常向后遍历还增加了向前遍历的功能
ListIterator it = list.listIterator();
while(it.hasNext()) {
Object obj = it.next();
System.out.println(obj);
}
//TODO 子接口提供的遍历方式-如:向前遍历
}
}
6 ArrayList
6.1 概述
1) 存在于java.util包中。
2) 内部用数组存放数据,封装了数组的操作,每个对象都有下标。
3) 内部数组默认初始容量是10。如果不够会以1.5倍容量增长。
4) 查询快,增删数据效率会降低。
6.2 创建对象
new ArrayList():初始容量是10
6.3 练习1:测试常用方法
常用API,包括下标遍历,迭代器遍历
7 LinkedList
7.1 概述
双向链表,两端效率高。底层就是数组和链表实现的。
7.2 常用方法
add()
get()
size()
remove(i)
remove(数据)
iterator()
addFirst() addLast()
getFirst() getLast()
removeFirst() removeLast()
7.3 练习1:测试迭代器遍历
双向链表:下标遍历效率低,迭代器遍历效率高
8 扩展
8.1 BIO、NIO、AIO的区别
阻塞IO,BIO 就是传统的 java.io 包,它是基于流模型实现的,交互的方式是同步、阻塞方式,也就是说在读入输入流或者输出流时,在读写动作完成之前,线程会一直阻塞在那里,它们之间的调用时可靠的线性顺序。它的有点就是代码比较简单、直观;缺点就是 IO 的效率和扩展性很低,容易成为应用性能瓶颈。
非阻塞IO,NIO 是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer 等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层高性能的数据操作方式。
异步IO,AIO 是 Java 1.7 之后引入的包,是 NIO 的升级版本,提供了异步非堵塞的 IO 操作方式,所以人们叫它 AIO(Asynchronous IO),异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。但目前还不够成熟,应用不多。
8.2 数组和链表区别
List是一个接口,它有两个常用的子类,ArrayList和LinkedList,看名字就可以看得出一种是基于数组实现另一个是基于链表实现的。
数组ArrayList遍历快,因为存储空间连续;链表LinkedList遍历慢,因为存储空间不连续,要去通过指针定位下一个元素,所以链表遍历慢。
数组插入元素和删除元素需要重新申请内存,然后将拼接结果保存进去,成本很高。例如有100个值,中间插入一个元素,需要数组重新拷贝。而这个动作对链表来说,太轻松了,改变一下相邻两个元素的指针即可。所以链表的插入和修改元素时性能非常高。
实际开发就根据它们各自不同的特点来匹配对应业务的特点。业务一次赋值,不会改变,顺序遍历,就采用数组;业务频繁变化,有新增,有删除,则链表更加适合。