JavaAdv笔记

javaAdv笔记

String类

String是一个类,属于引用类型。Java程序中一切使用""引起来的内容都属于String类的实例,称为字符串对象。

字符串定义后是一个常量,值不可更改,String字符串是一个不可变字符串字符串实际是一个字符数组。

String str="hello";//这句话在执行时,创建一个hello字符串对象,将其地址保存在str变量中
str="abc";//这里看似是在改变字符串的值,实际是又创建了一个字符串对象abc,将其地址保存在str变
量中
//以上两句话,在内存中会有两个字符串对象hello和abc,str只引用最后赋值的字符串地址abc
String str2="abc";
//字符串实际是一个字符数组
char[] list = {'a','b','c'};
String str3= new String(list);
//这里str2和str3中保存的内容是一样的
常用构造方法说明
String()创建一个空白字符串对象,实际创建一个空字符数组
String(String str)创建一个指定的字符串对象,实际是创建一个形参字符
串的副本
String(char[] list)创建一个指定字符数组的字符串对象。
String(byte[] list)按默认编码格式创建一个指定字节数组的字符串对象。
String(byte[] list,String
charsetName)
按指定的编码格式创建一个指定字节数组的字符串对
象。
StringBuilder类

用于表示可变字符串的一个类,是非线程安全的,建议在单线程环境下使用,效率略高于StringBuffer。

StringBuffer类

用于表示可变字符串的一个类,是线程安全的,建议在多线程环境下使用,效率略低于StringBuilder。
StringBuilder和StringBuffer中的方法作用都一致,只不过StringBuffer中的方法使用了synchronized关键字修饰,表示一个同步方法,在多线程环境下不会出现问题。

以StringBuilder为例:

常用构造方法说明
String()创建一个空白字符串对象,实际创建一个空字符数组
String(String str)创建一个指定的字符串对象,实际是创建一个形参字符
串的副本
String(char[] list)创建一个指定字符数组的字符串对象。
String(byte[] list)按默认编码格式创建一个指定字节数组的字符串对象。
String(byte[] list,String
charsetName)
按指定的编码格式创建一个指定字节数组的字符串对
象。
可变字符串与不可变字符串之间的转换

有些方法如indexOf()、charAt()等,在String和StringBuilder中都存在,可以不用转换。
但有些方法如getBytes()、contains()等,只能通过String调用,这时就需要进行转换。
不可变字符串转换为可变字符串
通过创建一个可变字符串对象,将不可变字符串作为参数实现转换

//定义一个不可变字符串对象
String str="hello";
//创建一个可变字符串对象,将不可变字符串对象作为参数
StringBuilder sb = new StringBuilder(str);

可变字符串转换为不可变字符串
通过调用可变字符串的toString()方法实现转换

//创建一个可变字符串对象
StringBuilder sb = new StringBuilder("hello");
//调用toString()转换为String类型
String str=sb.toString();
比较String、StringBuilder、StringBuffer这三个类的区别

相同点

  • 这三个类都可以用于表示或操作一个字符串
  • 这三个类中有公共的方法,如indexOf()、charAt()等、
  • 这三个类都是被final修饰的类,不能被继承

不同点

  • String定义的字符串是一个常量,不可变;StringBuilder和StringBuffer定义的字符串是一个变量,可以改变。
  • String类中的方法调用后,会创建字符串副本,不会影响原字符串;可变字符串对象调用的方法,直接操作原字符串,会影响。
  • StringBuilder是非线程安全的,StringBuffer是线程安全的,其中的方法使用synchronized修饰为同步方法。在多线程情景下,需要使用StringBuffer。StringBuilder效率略高于StringBuffer。

总结
在程序中,如果要频繁操作某个字符串,一定使用可变字符串类StringBuilder或StringBuffer创建对象。

包装类

java是纯面向对象语言,宗旨是将一切事物视为对象处理。但原始类型不属于对象,不满足面向对象的思想,但原始类型使用时无需创建对象,保存在栈中。为了让原始类型也有对象的类类型,达到"万物皆对象"的思想,所以就有了包装类的概念。
包装类就是原始类型对应的类类型。包装类通常用于将字符串转换为对应的原始类型。在web应用中,从浏览器中获取到后台的数据,全是String类型,一定要使用转换的方法。

包装类原始类型将字符串转换为原始类型
BytebyteByte.parseByte(String str)
ShortshortShort.parseShort(String str)
IntegerintInteger.parseInt(String str)
LonglongLong.parseLong(String str)
FloatfloatFloat.parseFloat(String str)
DoubleduobleDouble.parseDouble(String str)
BooleanbooleanBoolean.parseBoolean(String str)
Characterchar

特点

  • 八个原始类型中,除了int和char之外,其余类型的包装类,都是将首字母改为大写。int为Integer,char为Character。
  • 除了Character类之外,其余类都有至少两个构造方法:参数为原始类型或字符串的构造方法。Character的构造方法只有一个,参数为char变量。
  • 除了Character类之外,其余类都有静态方法parse原始类型(String str),用于将字符串转换为相
    应的原始类型
    • 数值型的包装类的parseXXX()方法,如果不是一个真正的对应类型的数,转换时会抛出NumberFormatException异常。如"123abc"、"123.456"都不能使用Integer.parseInt()转换
    • Boolean类型中的parseBoolean()方法,参数如果是"true"这个单词,无论大小写,都能转换为真正的boolean值的true,只要不是"true"这个单词,转换结果都为false
  • 除了Boolean类之外,其余类都有MAX_VALUE和MIN_VALUE这两个静态属性,用于获取对应原始类型支持的最大最小范围
  • 所有包装类中都有一个compareTo(参数1,参数2)方法,用于比较两个参数
    • 如果是数值型,参数1>参数2返回1,参数1<参数2返回-1,相同返回0
    • 如果是Boolean型,两个参数相同返回0,不同时,如果参数1为true返回1,否则返回-1
    • 如果是Character型,返回参数1-参数2的值。
  • 所有包装类中都有toString()方法,用于将包装类对象转换为String字符串对象

装箱和拆箱

  • 所有包装类都有一个静态方法valueOf(原始类型),将某个原始类型的数据转换为相应的包装类对象,这个过程称为装箱。
//手动装箱
int i=123;//定义一个原始类型的数据
Integer aInteger=Integer.valueOf(i);//调用包装类的valueOf()方法将原始类型转换为包
装类对象
  • 所有包装类都有一个原始类型Value()方法,用于将包装类对象转换为原始类型,这个过程称为拆箱
//手动拆箱
Integer aInteger=new Integer(123);//创建一个包装类对象
int i = aInteger.intValue();//调用包装类的"原始类型Value()"方法将其转换为原始类型
  • 自动装箱拆箱。在jdk1.5之后,加入了自动装箱拆箱的特性,可以直接在原始类型和对应的包装类中互相赋值
//自动装箱
Integer aInteger=123;
//自动拆箱
int i=aInteger;

字符串与原始类型之间的转换

原始类型转换为字符串:

String.valueOf(原始类型参数);

int num=123;
String str=String.valueOf(num);
System.out.println(str.length());

字符串转换为原始类型:
使用原始类型对应的包装类,调用其pareseXXX(字符串)方法

String num="123";

Date类

用于表示日期时间的类,位于java.util包下

构造方法

常用构造方法说明
Date()创建当前瞬间的日期对象
Date(long l)创建指定毫秒数对应的日期对象。(从1970.1.1起经过了的毫秒数)
Date(int year,int month,int date)该构造方法已过时。创建指定年月日的日期对象。(年从1900年起经过

常用方法

常用方法
getTime()得到对应Date对象指定的毫秒数。
setTime(long l)设置毫秒数
after(Date when)判断调用日期对象是否在when之后
before(Date when)判断调用日期对象是否在when之前

SimpleDateFormat类

是一个用于格式化日期的类。
构造方法

//参数为一个日期模板
SimpleDateFormat(String pattern);
//如定义这样的一个模板对象
SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
//创建一个Date对象
Date now = new Date();
//调用格式化方法,参数为一个Date对象
String res=sdf.formart(now);
//输出的形式为2022/07/28
System.out.println(res);

日期格式化的模板
在这里插入图片描述

异常

当程序没有按程序员的意愿正常执行,中途出错导致程序中断,出现这种情况,就称为异常。学习异常就是认识异常的种类和如何避免异常出现。为了让程序能正常执行,就需要学会解决异常。
异常的产生
异常在程序中以对象的形式存在。当代码执行时出现异常,虚拟机会自动创建一个相应的异常对象,如果没有对该异常进行处理,就会导致程序中断。
异常的分类
异常在程序中以对象的形式存在,所以异常有对应的类。
“异常家族”
Throwable是异常类的根类,通常所说的异常,其实指的是Exception子类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wOqrcbTB-1659848262798)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20220806202710428.png)]

Error错误

如果出现XXXXXError,如StackOverflowError栈空间溢出时,无法通过额外的代码去解决,只能修改源码。

Exception异常

如果出现XXXXException,如NullPointerException空指针异常时,可以通过额外代码去避免。

运行时异常和非运行时异常

如果一个异常属于RuntimeException异常类的子类,称为运行时异常,可以通过编译,运行时可能抛出异常对象。
如果一个异常属于Exception异常类的子类,称为非运行时异常,无法通过编译,只有处理后才能编译运行。

  • 运行时异常(RuntimeException),可以通过编译,可以不用处理,但在运行时有可能会抛出异常。

    常见运行时异常说明出现的情景
    NullPointerException空指针异常用空对象null调用方法或属性
    ArrayIndexOutOfBoundsException数组下标越界异常使用数组时,下标超出范围
    ClassCastException对象转型异常Dog dog=(Dog)new Person();(Person类和Dog类不是继承关系)
    InputMismatchException输入类型不匹配异常如需要int类型的数据,输入String类型的数据。
    ArithmeticException算术运算异常0当分母
    IllegalArgumentException非法参数异常调用方法的参数不合理
  • 非运行时异常(除RuntimeException),无法通过编译,必须要处理后才能编译运行。

    常见非运行时异常说明出现的情景
    IOException输入输出流异常当使用流对象
    SQLException数据库SQL相关异常操作数据库时
    FileNotFoundException文件未找到异常方法的参数为文件
处理异常Exception

只要处理Exception异常类的子类时,都称为处理异常。处理异常的目的就是为了保证程序正常运行,不要中断。
方式一:try-catch-finally语句
这种方式能成功地处理异常,无论会不会抛出异常,都能让程序保证正常执行。

try{
  //可能出现异常的代码
}catch(异常类 异常对象){
  //如果出现异常对象,且与catch中的异常类匹配,则执行
}catch(异常类 异常对象){//后续的catch中的异常类型只能与之前的异常类型同级或是父类
  //如果出现异常对象,且与catch中的异常类匹配,则执行
}finally{
  //无论程序是否会抛出异常,一定要执行的代码
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G2YCadFx-1659848262800)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20220806203427358.png)]

执行流程:先执行try中的内容,当出现异常,与后续的catch进行匹配,如果匹配对应的异常类型或异常父类型,则执行大括号中的内容,最终一定执行finally中的内容
try-catch注意

  • 执行try中的内容时,当某行代码抛出异常后,不再执行try中该行代码后续的内容。
  • 无论try中是否会抛出异常,finally中的内容一定执行。通常finally用于释放资源。
  • 如果有多个catch,需要将异常子类放在最前,异常父类放在最后
  • try、catch、finally都不能单独使用,try需要配合catch或finally或catch和finally一起使用
  • try中定义的内容,无法在try之外的地方使用
  • try中如果有return,不影响finally的执行,并且finally优先于return执行

方式二:throws关键字
这种方式,可以让非运行时异常通过编译,定义方法的时候,声明可能抛出的异常。
用法:方法的参数小括号之后,添加"throws 异常类型1,异常类型2…"

public class Test{
  public void fun() throws InterruptException{//在定义方法时,声明可能抛出的异常类型
    //如果直接写这句话,会报错,因为sleep()方法会抛出一个非运行时异常,必须要处理
    Thread.sleep(5000);
 }
}

throw和throws

  • throws表示用于方法声明异常。使用时写在方法的小括号之后
//用于声明方法的异常
public void fun() throws Exception{
 
}
  • throw用于手动抛出异常对象。使用时写在{}中,通常用于满足某种条件时,强制中断程序。
public void fun(){
  //用于手动抛出一个异常对象
  RuntimeException e =  new RuntimeException();
  throw  e;
}
自定义异常

可以自定义异常,在满足某种条件下,手动通过throw关键字抛出异常,人为中断程序。
自定义异常步骤

  • 定义一个类,继承某个异常类。
    • 如果继承的是RuntimeException,表示自定义的异常类属于运行时异常,该异常对象可以不用处理。
    • 如果继承的是非RuntimeException,表示自定义的异常类属于非运行时异常,该异常对象必须要处理。
  • [可选操作]定义一个无参数的构造方法,调用父类中无参的构造方法,定义一个带字符串参数的构造方法,调用父类带字符串参数的构造方法。

数组与集合

数组的特点

  • 数组中保存的元素都是有序的,可以通过下标快速访问
  • 数组中保存的数据都是同一种类型
  • 数组的长度在定义后,无法改变
  • 数组无法获取其中保存的实际元素数量

集合的特点

  • 能保存一组数据,可以有序也可以无序
  • 集合的容量可变
  • 集合中可以保存不同类型的数据
  • 可以获取集合中实际的元素数量

集合框架(集合家族)

Iterator接口并不算严格意义上的集合的"根",它称为迭代器,用于遍历集合元素的一个工具接口。
所以集合的根接口为:Collection接口和Map接口,位于java.util包中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1K7dDaTS-1659848262802)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20220806210741709.png)]

Collection接口

核心的两个子接口:Set和List。

这两个接口都可以保存一组数据,Set接口保存数据时,是无序不重复的;List接口保存数据时,是有序可重复的。

List接口(有序可重复)

有序集合,元素可以重复,允许保存null,可以通过索引获取对应位置上的元素。

在接口中定义了一些操作元素的方法,如获取元素数量、添加、删除、替换、截取等。

ArrayList实现类(掌握)
  • 采用数组实现的集合
  • 可以通过索引访问元素、可以改变集合大小。如果要在其中插入或删除元素时,会影响其余元素。该集合查询效率高、增删中间元素效率低。
  • 该集合对象中保存的元素,都是引用类型(对象的内存地址)。即使保存了123,其实不是保存的int类型的123,而是Integer类型的123.

构造方法

常用构造方法说明
ArrayList()创建一个无参的ArrayList对象。实际是创建了一个空的Object数组。在调用添加元素方法时,才会初始化该数组大小为10。
ArrayList(int创建一个指定容量的ArrayList对象,实际是创建一个指定大小的Object数组,如果参数为负,会抛出IllegalArgumentException异常
LinkedList实现类
  • 采用双向链表实现的集合
  • 集合中保存的每个元素也称为节点,除首尾节点外,每个节点即保存了自己的数据,也保存了其前一个和后一个节点的地址
  • 如果在其中进行插入和删除的操作时,不影响其他元素的位置,只需要重新定义新节点的前后节点位置即可。
  • 如果要查询某个节点的索引,需要从头结点或尾结点开始一步步得到目标节点位置
    所以中间进行插入和删除的效率高,随机读取的效率低

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WUQ5gBf6-1659848262805)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20220806204435209.png)]

构造方法

常用构造方法说明
LinkedList()创建一个空链表

ArrayList和LinkedList的区别

  • 两者都是List接口的实现类,保存的元素有序可重复,允许保存null,拥有一些公共的方法,如
    size(),isEmpty(),subList(int from,int to)等
  • ArrayList采用数组实现,对于随机读取效率更高,通常用于查询;LinkedList采用双向链表实现,插入删除不影响其他元素位置,通常用于中间插入删除。
Set接口(无序不重复)

无序集合,元素不可以重复,允许保存null,没有索引。

在接口中定义了一些操作元素的方法,如获取元素数量、添加、删除、替换、截取等。

哈希表hash table

哈希表,也称为散列表,是一种数据结构,能更快地访问数据。

要保存的数据称为原始值,这个原始值通过一个函数得到一个新的数据,这个函数称为哈希函数,这个新数据称为哈希码,哈希码和原始值之间有一个映射关系,这个关系称为哈希映射,可以构建一张映射表,称为哈希表。在哈希表中,可以通过哈希码快速访问对应的原始值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n1Y8hyps-1659848262807)(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20220806204644919.png)]

哈希码的特点

  • 如果两个对象的hashCode不同,这两个对象一定不同

  • 如果两个对象的hashCode相同,这两个对象不一定相同

    • 如"通话"和"重地"这两个字符串的hashCode相同,但是两个对象
    • hashCode相同,对象不同,称为哈希冲突
  • 本质是一个HashMap对象,调用add()方法,实际调用的也是HashMap中的put()方法,参数作为put()方法的键,new Obejct()作为put()方法的值

HashSet实现类
  • 采用哈希表实现

  • 元素不能重复,无序保存,允许保存null

  • 本质是一个HashMap对象,调用add()方法,实际调用的也是HashMap中的put()方法,参数作为put()方法的键,new Obejct()作为put()方法的值

构造方法

常用构造方法说明
HashSet()创建一个默认的集合对象,实际是创建了一个大小为16,加载因子为0.75的HashMap对象
HashSet(int capacity)创建一个指定容量的集合对象,实际是创建了一个指定大小,加载因子为0.75的HashMap对象
HashSet(int capacity,floatloadFactor)创建一个指定容量和指定加载因子的集合对象。

HashSet添加数据的原理

if(两个对象hashCode相同&&两个对象equals相同){视为同一对象,不能添加}

每次向集合中添加元素时,先判断该元素的hashCode是否存在,

  • 如果不存在,视为不同对象,直接添加
  • 如果存在,再判断equals方法的结果
    • 如果为false,视为不同对象,直接添加
    • 如果为true,视为同一对象,不能添加

可见不能添加的条件是两个对象的hashCode相同并且equals结果为true。如果每次都只判断equals的话,过程可能会很久,效率不高,如果每次只判断hashCode的话,有可能会有哈希冲突,所以先判断hashCode,再判断equals,既能保证效率又能保证添加进去的元素都是不相同的元素。

equals方法和hashCode的关系

  • 如果两个对象的equlas方法比较结果为true,在没有重写equals方法的前提下,hashcode相同吗?
    • 相同
  • 如果两个对象的hashCode不同,在没有重写equals方法的前提下,equals方法比较结果为?
    • false
  • 如果两个对象的hashCode相同,equals方法比较结果为?
    • 可能为true也可能是false
String str1="通话";
String str2="重地";
//以上两个字符串对象的hashCode相同,但equals方法结果为false
String str3="abc";
String str4="abc";
//以上两个字符串的hashCode相同,equals方法结果为true
TreeSet实现类
  • 特殊的Set实现类,数据可以有序保存,可以重复,不能添加null元素
  • 采用红黑树(自平衡二叉树)实现的集合
    • 二叉树表示某个节点最多有两个子节点
    • 某个节点右侧的节点值都大于左侧节点值
  • 只能添加同一种类型的对象且实现了Comparable接口的对象
    • 实现Comparable接口后必须要重写compareTo方法
    • 每次调用添加时,参数会自动调用该方法
  • 添加的元素可以自动排序
  • compareTo方法的返回值决定了能否添加新元素和新元素的位置
    • 如果返回0,视为每次添加的是同一个对象,不能重复添加
    • 如果返回正数,将新元素添加到现有元素之后
    • 如果返回负数,将新元素添加到现有元素之前

构造方法

常用构造方法说明
TreeSet()实际会创建一个TreeMap对象
Map接口

Map 接口主要有两个实现类:HashMap 类和 TreeMap 类。

其中,HashMap 类按哈希算法来存取键对象,而 TreeMap 类可以对键对象进行排序。

HashMap实现类
  • JDK1.8之后,HashMap的数据结构采用"数组+链表+红黑树"实现
    • 当没有哈希冲突时,元素保存到数组中
    • 如果哈希冲突,在对应的位置上创建链表,元素保存到链表中
    • 当链表元素数量大于8,转换为红黑树
  • 数据采用"键值对"的形式保存,键称为key,值称为value ,键不能重复,允许null,值没有限
    制,键和值都是引用类型
  • 在哈希表中,哈希码就是键,保存的数据就是值,可以通过键得到相应的值。
常用构造方法说明
HashMap()创建一个空的集合对象,默认大小为16,加载因子为0.75
遍历集合的方式
遍历List

方式一:普通for循环

List list= new ArrayList();
list.add("123");
list.add("123");
list.add("123");
list.add("123");
for(int i=0;i<list.size();i++){
  Object obj= list.get(i);
}

方式二:增强for循环

List list= new ArrayList();
list.add("123");
list.add("123");
list.add("123");
list.add("123");
for(Object o:list){
  Object obj= o;
}

方式三:迭代器

List list= new ArrayList();
//获取迭代器对象
Iterator it= list.iterator();
//hasNext判断是否还有下一个元素
while(it.hasNext()){
  //获取读到的元素
  Object obj=it.next();
}
遍历Set

方式一:增强for循环

Set list= new HashSet();
list.add("123");
list.add("qwe");
list.add("hello");
list.add(null);
for(Object o:list){
  Object obj= o;
}

方式二:迭代器

Set list= new HashSet();
//获取迭代器对象
Iterator it= list.iterator();
//hasNext判断是否还有下一个元素
while(it.hasNext()){
  //获取读到的元素
  Object obj=it.next();
}
泛型

一种规范,常用于限制集合中元素的类型,省去遍历元素时转型的过程。

//定义集合,默认不限制元素类型
List list = new ArrayList();
list.add("hello");
list.add("world");
list.add(123);
//如果没有限制集合的类型,要通过循环做某个事情时,可能对于某些元素并不适用
for(int i=0;i<list.size();i++){
  //String有length属性,Integer没有,运行时就会抛出异常
  System.out.println(list.get(i).length);
}

用法:

在定义集合时,在接收集合变量的数据类型后,写上<数据类型>。

集合类或接口<数据类型> 集合变量名 = new 集合实现类<>();

List<String> list = new ArrayList<>();
//这样只能在集合中保存String
//list.add(123);//不能通过编译
//如果要限制集合保存int类型的数据,不能写int,要写int的包装类Integer。因为集合中保存的都是引
用类型。
//List<int> list2=new ArrayList();//无法通过编译
List<Integer> list2=new ArrayList();
Collections集合工具类
  • Collection是集合的根接口,定义了集合的方法
  • Collections是集合的工具类,定义了很多静态方法,直接通过类名使用

常用方法

常用方法
Collections.shuffle(List list)打乱有序集合中的元素顺序
Collections.swap(List list,int i,int j)交换有序集合中i和j索引上的元素
Collections.replaceAll(List list,ObjectoldVal,Object newVal)将有序集合中的旧元素替换为新元素
Collections.fill(List list,Object obj)将有序集合使用obj填充
Collections.rotate(List list,int distance)将最后distance个元素放在最前
Collections.sort(List list)对有序集合中的元素进行排序,元素必须实现了Comparable接口
Collections.max(Collection list)得到有序集合中的最小元素,元素必须实现了Comparable接口
Collections.min(Collection list)得到有序集合中的最大元素,元素必须实现了Comparable接口
集合与数组之间的转换
  • 集合转换为数组

    Object[] 数组 = 集合对象.toArray();
    List list  = new ArrayList();
    list.add(123);
    Object[] obj = list.toArray();
    System.out.println((Integer)obj[0]);
    
  • 数组转换为数组集合

    int[] ints={1,2,3,9,22,16};
    //将数组保存到一个数组集合中,上边定义的那个数组就是集合中的第一个元素
    List<int[]> ints1 = Arrays.asList(ints);
    //遍历转换后的数组
    for (int[] ints2 : ints1) {
    //遍历数组中的元素 
      for (int i : ints2) {
        System.out.println(i);
     }
    }
    

File文件

Java中可以将本地硬盘中的文件(文件和目录)以对象的形式表示。

就能通过Java代码对本地文件进行读取或操作。

构造方法

常用构造方法说明
File(String pathName)根据文件的完整路径创建对象
File(String parent,String name)根据文件所在的父目录路径和自身文件名创建对象
File(File parent,String name)根据文件所在父目录文件对象和自身文件夹创建对象
//创建一个File对象 new File(String pathName);
File file1 = new File("d:/xiaopw84in1111");
//创建一个File对象 new File(String parent,String child);
File file2 = new File("D:\\xiaopw84in1111","Readme-说明.htm");
//创建一个File对象 new File(File parent,String child);
File parent = new File("D:/xiaopw84in1111");
File file3 = new File(parent, "Readme-说明.htm");

IO、流(Stream)

IO
I:Input输入

O:Output输出

在Java中,流用于表示计算机硬盘与内存之间传输数据的通道。

将内存中的数据存入到硬盘中,称为写write,也称为输出

将硬盘中的数据存入到内存中,称为读read,也称为输入

流的分类

Java中将流定义成了类,以对象的形式保存。流有"四大家族",是所有流的父类。

InputStream字节输入流
FileInputStream 文件字节输入流

按字节读取本地文件,本地文件必须存在

FileInputStrream构造方法

常用构造方法说明
FileInputStream(String pathName)根据文件名创建文件字节输入流对象
FileInputStream(File file)根据文件对象创建文件字节输入流对象
ObjectInputStream对象字节输入流(反序列化)

反序列化:将文件转换为对象的过程

ObjectInputStream构造方法

常用构造方法说明
ObjectInputStream(InputStream创建一个对象字节输入流对象,参数为一个字节输入流对象,由于是抽象类,所以要使用其子类,如FileInputStream对象,在其中定义要读取的文件
OutputStream字节输出流
FileOutputStream文件字节输出流

按字节将数据写入到本地文件中,本地文件可以不存在

FileOutputStrream构造方法

常用构造方法说明
FileOutputStream(String pathName)根据文件名创建文件字节输出流对象,写入内容时覆盖原内容
FileOutputStream(StringpathName,boolean append)根据文件名创建文件字节输出流对象,写入内容时追加在原内容之后
FileOutputStream(File file)根据文件对象创建文件字节输出流对象,写入内容时覆盖原内容
FileOutputStream(File file,booleanappend)根据文件对象创建文件字节输出流对象,写入内容时追加在原内容之后
ObjectOutputStream对象字节输出流(序列化)

序列化:将对象转换为文件的过程

被序列化的对象,必须要实现Serializable接口。

这个接口是一个特殊的接口,没有定义方法,只是给这个类打上标记,表示该类可以被序列化。

ObjectOutputStream构造方法

常用构造方法说明
ObjectOutputStream(InputStream创建一个对象字节输入流对象,参数为一个字节输入流对象,由于是抽象类,所以要使用其子类,如FileOutputStream对象,在其中定义要写入的文件
Writer字符输出流
FileWriter文件字符输出流

按字符写入。
FileWriter构造方法

常用构造方法说明
FileWriter(String fileName)按文件名创建字符输出流对象
FileWriter(String fileName,boolean append)按文件名创建字符输出流对象,允许追加写入
FileWriter(File file)按文件对象创建字符输出流对象
FileWriter(File file,boolean append)按文件对象创建字符输出流对象,允许追加写入
BufferedWriter字符缓冲输出流

自带字符数组(缓冲区)的字符输出流。

BufferedWriter构造方法

常用构造方法说明
BufferedWriter(Writer writer)创建一个带有缓冲区(字符数组)的字符输出流对象,参数为一个Writer对象,但Writer是一个抽象类,所以要使用其子类,如FileWriter对象,在FileWriter中定义要写入的文件
BufferedWriter(Writer writer,int size)创建一个指定缓冲区(字符数组)大小的字符输出流对象
Reader字符输入流
FileReader文件字符输入流

按字符读取文件。

FileReader构造方法

常用构造方法说明
FileReader(String fileName)根据文件名创建文件字符输入流对象
FileReader(File file)根据文件对象创建文件字符输入流对象
BufferedReader字符缓冲输入流

自带字符数组(缓冲区)的字符输入流。默认字符数组大小为8192,每次最多读取8192个字符
在读取纯文本文件(txt或md等)时,首选该类。

BufferedReader构造方法

常用构造方法说明
BufferedReader(Reader reader)创建一个带有缓冲区(字符数组)的字符输入流对象,默认缓冲区大小为8192。参数注意是一个Reader类型的对象,但Reader是一个抽象类,所以要使用其子类,如FileReader,然后在FileReader中定义要读取的文件信息
BufferedReader(Reader reader,int size)创建一个指定缓冲区(字符数组)大小的字符输入流对象
按方向分类

输入流:InputStream、Reader
作用:将硬盘中的数据读取到内存中

输出流:OutputStream、Wrtiter
作用:将内存中的数据写入到硬盘中

按类型分

字节流:InputStream、OutputStream
作用:适用于非文本类型,如图片、文件等的读写

字符流:Reader、Writer
作用:适用于文本类型,尤值txt格式文件的读写

流的四个父类的特点

  • 以上四个类都在java.io包下,都是抽象类,不能直接创建其对象,要使用其子类
  • 这四个类都有close()方法,用于关闭流对象释放资源
  • 输出流(OutputStream和Write)都有flush()方法,用于将流中的数据到冲刷到硬盘中
    • 在使用输出流对象时,一定要调用flush()或close()方法后,才能真正将数据写到硬盘中
  • 输入流(InputStream和Reader)都有read()方法读取数据到内存中,输出流都有write()方法写入数据到硬盘中
  • 所有的流类中,以Stream结尾的,都是字节流,以Reader或Writer结尾的都是字符流
  • 读取硬盘中的数据时,使用输入流;将数据写入到硬盘中时,使用输出流
  • 读取或写入文本文件时,使用字符流;读取或写入非文本文件时,使用字节流

使用FileInputStream和FileOutputStream读写时的注意事项

  • 在使用read(byte[] bytes)方法时,每次都会读取指定数组大小的字节,将读取到的字节保存到数组中,该方法返回读取到的字节数量。如果最后一次读取的字节数不足字节数组大小时,只会用读取到的内容覆盖最前的几个元素。所以会导致读取到的内容多于实际内容。
  • 在使用write(byte[] bytes)方法时,会将字节数组中的所有内容写入到输出流中,在最后一次写入时,可能会写入多余的内容。所以在写入时,最好使用write(byte[] bytes,int off,int len)方法,表示将字节数组中的元素,从off开始,写入len个字节。如有文件test.txt,其中保存aaabbbccc
FileInputStream fis =  new FileInuptStream("d:/test1.txt");
FileOutputStream fos =  new FileOutptStream("d:/test2.txt");
byte[] bytes=new bytes[4];
//第一次能读取4个装满数组,所以是aaab
int count=fis.read(bytes);
//使用这个方法写入实际读取到的字节数
fos.write(bytes,0,count);
//第二次能读取4个装满数组,所以是bbcc
count=fis.read(bytes);
//使用这个方法写入实际读取到的字节数
fos.write(bytes,0,count);
//第三次读取只能读取c,其余的元素依然是上次的元素,所以的是cbcc
count=fis.read(bytes);
//使用这个方法写入实际读取到的字节数
fos.write(bytes,0,count);
//fos.write(bytes);//如果这样写,最后一次会多写入bcc
fis.close();
fos.close();

使用FileInputStream和FileOutputStream实现单个文件的复制

package com.hqyj.IOTest;
import java.io.*;
public class Test3 {
  public static void main(String[] args) throws IOException {
    //创建源文件对象
    File source = new File("d:/SteamTools-develop.zip");
    //创建文件字节输入流对象,用于读取源文件中的内容
    FileInputStream fis = new FileInputStream(source);
    //创建目标文件对象
    File target = new File("C:\\Users\\Administrator\\Desktop",
source.getName());
    //创建文件字节输出流对象,用于写入内容到该文件中
    FileOutputStream fos = new FileOutputStream(target);
    //创建一个字节数组,按数组读写,这里表示8M
    byte[] bytes = new byte[1024 * 1024 * 8];
    //整个数组读取,i表示读取到的字节数量
    int i=fis.read(bytes);
    while(i!=-1){
      //循环读取的同时,按字节数组写入,实际读到了多少字节,就写入多少字节
      fos.write(bytes,0,i);
      //下次读取
      i=fis.read(bytes);
   }
    fos.close();
    fis.close();
    if (target.exists()) {
      System.out.println("复制成功");
   }else{
      System.out.println("复制失败");
   }
 }
}

文件夹的复制

package com.hqyj.homework;
import java.io.*;
public class FileCopy {
  public static void main(String[] args) {
    File source = new File("d:/xiaopw84in1111");
    File target = new File("C:\\Users\\Administrator\\Desktop/copy");
    copyDir(source, target);
    //source:  d:/信息    假设该文件夹下只有一个info.txt文件
    //target:  桌面:/copy
    //1.调用copyDir方法,判断source是一个文件夹,创建目标文件夹target"桌面:/copy"
    //2.遍历source文件夹,发现info.txt文件,用child表示
    // 此时的source是"d:/信息/info.txt",即child,target是"桌面:/copy/info.txt"
    // 所以要按File(File parent,String child)构造方法创建目标文件对象,即File("桌/copy","info.txt"),对应代码为File(target,child.getName())
    //3.递归调用copyDir方法,参数为copyDir(child,File(target,child.getName()))
 }
  /*
  * 定义复制文件夹的方法
  * */
  public static void copyDir(File source,File target) {
    //判断如果是文件,直接按单文件复制的方式调用
    if(source.isFile()){
      copyFile(source,target);
   }else{//如果是文件夹
      //创建目标文件夹
      target.mkdir();
      //展开原文件夹
      for (File child : source.listFiles()) {
        //当前新的目标文件是 保存在目标文件夹下的源文件
        //如原文件为"d:/source/info.txt" 新目标为"d:/target/info.txt"
        //所以按File(File parent,String child)构造方法创建目标文件对象
        File newTarget = new File(target, child.getName());
        //递归调用时的源文件依然是当前遍历出来的子文件
        copyDir(child,newTarget);
     }
   }
 }
  /*
  * 定义单个文件复制的方法
  * */
  public static void copyFile(File source, File target) {
    FileInputStream fis = null;
    FileOutputStream fos = null;
    try {
      //创建用于输入输出的流对象
      fis = new FileInputStream(source);
      fos = new FileOutputStream(target);
      //定义字节数组
      byte[] bytes = new byte[1024 * 1024 * 8];
      //按字节读取,将读取到的内容马上写入
      int count = fis.read(bytes);
      while (count != -1) {
        fos.write(bytes, 0, count);
        count = fis.read(bytes);
     }
   } catch (FileNotFoundException e) {
      System.out.println("文件不存在" + e);
   } catch (IOException e) {
         System.out.println("读写异常" + e);
   } finally {
      try {
        if (fis != null) {
          fis.close();
       }
        if (fos != null) {
          fos.close();
       }
     } catch (IOException e) {
        System.out.println("关闭流对象异常" + e);
     }
   }
 }
}

序列化与反序列化的案例

  • Person类,必须要实现Serializable接口才可序列化

    package com.hqyj.ObjectStreamTest;
    import java.io.Serializable;
    //如果要序列化某个类的对象,要将该类实现Serializable接口
    //这个接口中没有任何方法,只是一个标记,表示该类的对象可以被序列化
    public class Person implements Serializable {
      private String name;
      private int age;
      private String sex;
      @Override
      public String toString() {
        return "Person{" +
            "name='" + name + '\'' +
            ", age=" + age +
            ", sex='" + sex + '\'' +
            '}';
     }
      public Person(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
     }
    //这里省略get/set
    }
    
  • Main类

    package com.hqyj.ObjectStreamTest;
    import java.io.*;
    import java.util.ArrayList;
    public class Main {
      public static void main(String[] args) throws IOException,
    ClassNotFoundException {
        //创建一个对象
        Person p = new Person("张敏", 22, "男");
        //最终保存的文件
        File file = new File("d:/person.p");
        //创建一个字节输出流对象,参数为要写入的文件对象,即上一步的file
        OutputStream fos = new FileOutputStream(file);
        //创建一个对象输出流对象,用于序列化,参数为OutputStream对象,即上一步的fos
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        //可以简写为
        // ObjectOutputStream oos = new ObjectOutputStream(new
    FileOutputStream(new File("d:/person.p")));
        //调用writeObject(Object obj)方法,将对象写入到文件中
        oos.writeObject(p);
        //关闭
        oos.close();
       
        //创建一个对象字节输入流对象,用于反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new
    File("D:/person.p")));
        //readObject();读取内容,返回对象类型
        Object o = ois.readObject();
        //要经过转型后才能当做Person对象使用
        Person person = (Person) o;
        System.out.println(person.getName());
     }
    }
    

a.util.ArrayList;
public class Main {
public static void main(String[] args) throws IOException,
ClassNotFoundException {
//创建一个对象
Person p = new Person(“张敏”, 22, “男”);
//最终保存的文件
File file = new File(“d:/person.p”);
//创建一个字节输出流对象,参数为要写入的文件对象,即上一步的file
OutputStream fos = new FileOutputStream(file);
//创建一个对象输出流对象,用于序列化,参数为OutputStream对象,即上一步的fos
ObjectOutputStream oos = new ObjectOutputStream(fos);
//可以简写为
// ObjectOutputStream oos = new ObjectOutputStream(new
FileOutputStream(new File(“d:/person.p”)));
//调用writeObject(Object obj)方法,将对象写入到文件中
oos.writeObject§;
//关闭
oos.close();

  //创建一个对象字节输入流对象,用于反序列化
  ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new

File(“D:/person.p”)));
//readObject();读取内容,返回对象类型
Object o = ois.readObject();
//要经过转型后才能当做Person对象使用
Person person = (Person) o;
System.out.println(person.getName());
}
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值