IO流是Java对文件进行操作,同时还可以对文件的内容读取和写入,在Java中,这些操作文件的类称之为流
笔试题:下列哪些流是字节流(ABC)?
A:FileInputStream
B:ObjectOutputStream
C:DataOutputStream
D:BufferedReader
E:FileWriter
F:InputStreamReader总结:流的命名规则,看后缀和哪个父类一样就是什么类
属于InputStream的子类,属于文件输入流,负责对文件按照字节为单位对数据进行读取
//1.创建输入流对象,提供了多种有参构造
FileInputStream fis = new FileInputStream(参数);
//参数:可以写字符串,表示读取的文件地址
//参数:可以写File类型的参数,可以判断文件是否存在,防止文件丢失异常(FileNotFoundException)
//2.通过流对象.方法(),对文件进行读取
read():运行一次,表示读取一个字节
read(字节数组):表示每次读取的内容会保存到字节数组里面,并且会返回读取的真实长度,如果没有数据会返回-1
close():关闭流,一般流使用完后都需要关闭,为了节省资源
字节流读取中文可能会出现中文乱码,原因在于每次读取的中文未必会全部读完,如果没有读取完只读取了一个汉字的一部分就会出现乱码,不同的编码方式占字节数不同,如果是UTF-8每个汉字占三个字节,如果是GBK每个汉字占两个字节
属于OutputStream子类,属于文件字节输出流,负责将数据进行写入
//1.创建输出流对象
FileOutpStream fos = new FileOutputStream(参数);
//一个参数的构造:String 或者 File
//两个参数的构造:String,boolean 或者 File,boolean
//boolean表示是否是追加模式,默认值是false,表示不追加,如果是true,会在原来的数据基础上写入新内容
//2.流对象.调用方法(),进行写入数据
write():写入一个字节,参数int,编写ASCII
write(字节数组):写入一个字节数组
flush():清空缓存,Java读写都是基于内存的,每次读取和写入存储到缓存中,清空了缓存后,才会到本地磁盘,如果没有清空缓存,但是关闭了资源(先清空缓存,再写入本地磁盘)
close():关闭流
//测试文件复制(先对文件进行读取,再对文件进行写入)
public class TestCopy {
public static void main(String[] args) {
File in = new File("D://IO//后台管理登陆 -联想浏览器 2025-01-05 15-31-02.mp4");
File out = new File("d://IO//movie//myPC.mp4");
if (in.exists()){
File parentOut = out.getParentFile();
if (!parentOut.exists()){
parentOut.mkdirs();
}
//统计当前系统时间毫秒
long start = System.currentTimeMillis();
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(in);
fos = new FileOutputStream(out);
byte[] bs = new byte[1024];
int len;
while ((len = fis.read(bs)) != -1) {
fos.write(bs,0,len);
}
//再统计一次系统时间毫秒数
long end = System.currentTimeMillis();
long duration = end - start;
System.out.println("复制成功:"+duration+"ms");
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
try {
if (fis != null) fis.close();
if (fos != null) fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}else {
System.out.println("要复制的文件不存在");
}
}
}
//新版try-catch 实现文件复制
public class TestCopy2 {
public static void main(String[] args) {
//try(){}catch{}
//可以将需要关闭资源的内容放入()编写 它会帮你自动关闭
try (
FileInputStream fis = new FileInputStream("D://IO//后台管理登陆 -联想浏览器 2025-01-05 15-31-02.mp4");
FileOutputStream fos = new FileOutputStream("D://IO//movie//myPC2.mp4")
){
long start = System.currentTimeMillis();
byte[] bs = new byte[1024];
int len;
while ((len = fis.read(bs)) != -1) {
fos.write(bs, 0, len);
}
long end = System.currentTimeMillis();
System.out.println("复制成功:"+(end - start)+"ms");
}catch (IOException e) {
e.printStackTrace();
}
}
}
缓冲流:是属于处理流,可以提高文件的读写效率,原理是每次进行文件读取的时候不会立即写入,会先放入缓冲区(内存)等待缓冲区存储满了再一口气写入到本地磁盘,由于缓冲区是属于内存的,而访问内存的速度会高于本地磁盘,所以才可以提高读写效率,同时还减少了访问本地磁盘的次数,提高了本地磁盘的使用寿命
//字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(字节输入流);
bis.read(); bis.read(字节数组); close();
//字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(字节输出流);
bos.write(); bos.write(字节数组); close();
字符流:非常适合读写文本内容,因为中文在字符流中也算是一个字符,不会像字节流一样对中文拆分字节进行读取,不会出现中文乱码的问题
FileReader:
read():返回的是一个整型,表示数据的ASCII;
read(字符数组):将读取内容存储到字符数组,返回读取长度,否则为-1;
close():关闭流;
FileWriter:
write(int c):写入单个字符。
write(String str): 将字符串内容写入到本地文件;
write(字符数组):写入字符数组。
flush():刷新该流的缓冲
close():关闭流;
//测试字符流和缓冲流
public class TestChar {
public static void main(String[] args) {
try(
FileReader fr = new FileReader("D://IO//testIO.txt");
FileWriter fw = new FileWriter("D://IO//result3.txt");
BufferedReader br = new BufferedReader(fr);
BufferedWriter bw = new BufferedWriter(fw);
){
String msg;
while((msg = br.readLine()) != null){
bw.write(msg);
bw.newLine();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
InputStreamReader:本质上就是字符输入流,提供了一个有参构造,将字节流转换成字符输入流
InputStreamReader isr = new InputStreamReader(字节输入流);
//Scanner-->System.in(底层就是InputStream);
//socket.getInputStream();
OutputStreamWriter:本质上就是字符输出流,提供了一个有参构造,将字节流转换成字符输出流
OutputStreamReader osw = new OutputStreamReader(字节输出流);
//模拟Scanner对象获取控制台数据
public class TestScanner {
private InputStream is;
private InputStreamReader isr;
private BufferedReader br;
//new对象的时候执行
public TestScanner(InputStream is) {
this.is = is;
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
}
//private InputStreamReader isr = new InputStreamReader(is);
//private BufferedReader br = new BufferedReader(isr);
public String next(){
String msg = null;
try {
msg = br.readLine();
} catch (IOException e) {
throw new RuntimeException(e);
}
return msg;
}
public int nextInt(){
String msg = null;
try {
msg = br.readLine();
} catch (IOException e) {
throw new RuntimeException(e);
}
int result = Integer.parseInt(msg);
return result;
}
public static void main(String[] args) throws IOException {
//System.in表示系统输入流,它可以获取控制台数据,本质上是一个字节输入流
//Scanner sc = new Scanner(System.in);
TestScanner ts = new TestScanner(System.in) ;
InputStream is = System.in;
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
while (true) {
System.out.println("Enter a word: ");
String msg = br.readLine();
System.out.println("读取控制台数据:"+msg);
}
}
}
class Test{
public static void main(String[] args) {
//加载类(加载成员变量)
TestScanner ts = new TestScanner(System.in) ;
System.out.println("请输入姓名:");
String name = ts.next();
System.out.println("请输入年龄:");
int age = ts.nextInt();
System.out.println("姓名:"+name+"年龄:"+age);
}
}
序列化:将Java中的对象转换成字节序列(如果字节序列写入到本地文件就会形成一个文件)的过程
反序列化:将字节序列转换回Java中的对象
面试题:Java创建对象的方式有哪些?
- new对象():通过构造方法创建对象
- 克隆:不走构造方法,是堆内存直接将已经存在的对象拷贝一个新的
- 反序列化:不走构造方法将对象的字节流恢复为对象,需实现 Serializable 接口。
- 反射:通过类的Class对象调用 newInstance() 方法创建对象,要求类必须有无参构造器。通过构造器对象创建对象,支持有参构造器和私有构造器(需设置 setAccessible(true) )。走构造方法
ObjectOutputStream: 用于实现序列化,将Java中的对象转换成本地文件
writeObject(对象):
ObjectInputStream: 用于实现反序列化的,将本地文件转换成Java中的对象
对象 = (对象)readObject(); //默认返回Object
Object类是属于java.lang包下的,无需导入包(import),是Java类中所有类的父类,本身Object类提供了很多公开的方法,所以Java中的所有类都可以使用这些方法
面试题:Object类有哪些常用方法?
equals(): 等价于==,用于比较地址是否一致,如果子类重写了equals(),会根据子类重写后的规则来,比如:String、Integer····就重写了equals()
toString(): 将对象转换成字符串输出,默认是引用地址(在堆内存存储的位置),如果重写了,就会返回重写后的toString()的返回值
hashcode(): 获取哈希码的
什么是哈希码:用于表示一个对象的基本特征,一般是由32位的二进制数构成,形成int类型保存结果,主要包含三大类
- 对象类:根据对象的引用地址计算得到
- 基本类型封装类:保存的就是对应的数值
- String类:根据字符串的内容计算得到
- 哈希冲突:当值不同但是hashcode相同时,就是哈希冲突,比如:“重地”和“通话”,如果值相同,最终转换的hashcode一定相同,如果hashcode相同,存储的值不一定相同
getClass(): 获取类对象(User.class属于类对象,new User()属于对象),是做反射的前提
notify(): 唤醒随机一个处于等待的线程
notifyAll(): 唤醒全部属于等待的线程
wait(): 让线程处于等待,需要其他线程进行唤醒
clone(): 用于克隆对象的
浅克隆:只能针对于对象中的基本类型和String类型进行克隆,但是如果是引用类型的属性,是不能克隆的,只是把地址复制一份,指向的还是同一个对象。实现:实现Cloneable接口,重写clone()
深克隆:不仅可以针对于String类型和基本类型的属性进行克隆,引用类型的属性也可以支持克隆
实现:实现Cloneable接口,重写clone(),同时引用类型的属性也要实现Cloneable接口,重写clone()
finalize(): GC垃圾回收机制使用的方法,自动将堆内存的无用对象进行回收,Java会自动调用,目的是防止内存溢出
面试题:深克隆和浅克隆的区别?
//克隆对象:
//浅克隆:需要实现Cloneable接口表示支持克隆,重写父类的clone()方法
//深克隆:需要实现Cloneable接口表示支持克隆,重写父类的clone()方法,对象属性也要这么做
public class Emp implements Cloneable{
Integer id;
String name;
EmpInfo info;
public Emp(){
System.out.println("无参构造");
}
@Override
protected Emp clone() throws CloneNotSupportedException {
Emp emp = (Emp) super.clone();
emp.info = info.clone();
return emp;
}
}
class EmpInfo implements Cloneable{
Integer id;
String sex;
Integer age;
@Override
protected EmpInfo clone() throws CloneNotSupportedException {
return (EmpInfo) super.clone();
}
}
class TestClone{
public static void main(String[] args) throws CloneNotSupportedException {
Emp emp = new Emp();//走构造方法
emp.id = 10;
emp.name = "张三";
emp.info = new EmpInfo();
emp.info.id = 10;
emp.info.age = 18;
emp.info.sex = "男";
Emp emp1 = emp.clone();//在堆内存直接复制一个对象出来
System.out.println(emp+" "+emp.info);
System.out.println(emp1+" "+emp1.info);
emp.name = "李四";
emp.info.age = 19;
System.out.println(emp1.name+" "+emp1.info.age);
}
}