华清远见重庆中心—JAVA高级阶段技术总结/个人总结

String字符串

String是一个类,属于数据类型中的引用类型。

Java的一切使用""引起来的内容,都是这个类的实例,称为字符串对象。

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

String类使用时注意

由此可见,如果要频繁改动String类型变量的值,会创建很多字符串对象,效率很低。

所以在频繁改动字符串时,不要使用String类。

如果要频繁改动字符串时,使用StringBuilder或StringBuffer类。

如何创建字符串对象

1.使用""赋值创建

String str="abc";

2.通过构造方法创建

常用构造方法说明
String()创建一个空白字符串对象,即""
String(String str)创建一个指定字符串对象
String(char[] list)创建一个指定字符数组的字符串对象
String(byte[] bytes,String charsetName)按指定的编码格式创建一个指定字节数组的字符串对象

不同方式创建字符串的过程

使用""赋值的形式创建

//这句话执行时,判断"ab"是否存在于字符串缓冲区中,不存在,创建,将其地址保存到str1中
String str1 = "ab";
//这句话执行时,判断"ab"是否存在于字符串缓冲区中,已存在,将其地址保存到str2中
String str2 = "ab";
//这句话执行时,+两端如果都是""定义的字符串,拼接后再判断"ab"是否存在于字符串缓冲区中,已存在,将其地址保存到str3中
String str3 = "a" + "b";
//以上三句话,只有一个字符串对象创建,即"ab",str1,str2,str3指向了同一个地址,所以用==比较都是true
System.out.println(str1 == str2);
System.out.println(str1 == str3);

可以使用JDK中自带的反编译工具javap对class文件进行反编译。

在class文件所在目录下(项目的out目录中),进入控制台,输入 javap -c 字节码文件名.class

使用构造方法String(String str)创建

//这句话的执行流程
//1.在字符串缓冲区中寻找"ab",不存在,创建
//2.在堆中new String()创建对象,将字符串缓冲区中的"ab"的字符串地址保存在new String()的区域中
//3.将堆中new String()整个对象保存到栈中str1变量中
String str1 = new String("ab");
//这句话的执行流程
//1.在字符串缓冲区中寻找"ab",存在
//2.在堆中new String()创建对象,将字符串缓冲区中的"ab"的字符串地址保存在new String()的区域中
//3.将堆中new String()整个对象保存到栈中str1变量中
String str2 = new String("ab");


//以上两句话,在字符串缓冲区中有一个"ab"的字符串,在堆中有两个对象
//str1和str2保存堆中不同的两个地址,所以为false
System.out.println(str1 == str2);

使用+拼接""和new出来的字符串对象创建

//在字符串缓冲区中创建"ab"
String str1 = "ab";
//1.创建StringBuilder对象
//2.在字符串缓冲区中创建"a"
//3.在字符串缓冲区中创建"b"
//4.在堆中new String(),将"b"保存在其中
//5.调用StringBuilder对象的append()方法,将"a"和new String("b")拼接
String str2 = "a" + new String("b");//一共会创建"a","b",new String(),new StringBuilder()四个对象

//两个不同的地址
System.out.println(str1==str2);

总结

在使用字符串时,字符串是对象,如果要比较其值是否相同,不能使用判断,因为判断的是内存地址。

所以在比较字符串是否相同时,要使用String类重写的equals方法进行判断。

String类中equals重写的原理大致是:

判断是否为同一个字符串,再判断是否是字符串类型,再将两个字符串转换为字节数组,逐一比较字节数组中的内容,全部一致,返回true

调用equals方法时,通常将已知非空字符串作为调用者

username.equals("admin");//username可能为空,会有空指针异常

"admin".equals(username)//能避免空指针异常

字符串String类中的常用方法

方法名返回值作用
length()int得到字符串长度
toLowerCase()String转换为小写
toUpperCase()String转换为大写
trim()String去除字符串的首尾全部空格
isEmpty()boolean判断字符串长度是否为0
getBytes()byte[]转换为字节数组
toCharArray()char[]转换为字符数组
equalsIgnoreCase(String str)boolean忽略大小写比较字符串是否相同
equals(String str)boolean判断两个字符串是否相同
charAt(int index)char得到某个索引上的字符
indexOf(String str)int得到某个字符串第一次出现的索引,不存在返回-1
lastIndexOf(String str)int得到某个字符串最后一次出现的索引,不存在返回-1
contains(String str)boolean判断是否存在某个字符串
startsWith(String str)boolean判断是否以指定字符串开头
endsWith(String str)boolean判断是否以指定字符串结尾
concat(String str)String将指定字符串拼接到原字符串末尾
substring(int index)String从索引index开始截取字符串至末尾
substring(int begin,int end)String截取[begin,end)范围内的字符串
split(String regex)String[]根据字符串或正则表达式切分原字符串。相当于split(regex,0)
split(String regex,int limit)String[]根据字符串或正则表达式切分原字符串,limit为正数,数组长度最大为limit;limit为0,数组长度无限制,但省略最后的空字符串;limit为负数,数组长度无限制。
replace(String oldStr,String newStr)String将原字符串中的oldStr替换为newStr
replaceFirst(String oldStr,String newStr)String将原字符串中第一次出现的oldStr替换为newStr
String.valueOf(参数)String将参数转换为字符串。参数可以是任何数据。通常用于原始类型转换为字符串
String.format(String 格式,Object…obj)String根据指定格式转换参数。常用与将浮点数据保留指定小数位数。\n如String.format(“%4.2f”,2.345)表示将2.345保留2位小数,整体占4位,输出为字符串格式。如果实际数字总位数大于4,原样输出,如果实际数字总位数小于4,会在最前补充空格。

可变字符串

String字符串对象是一个常量,在定义后,值不可改变。

如果使用String类的对象,对其频繁更新时,就会不停地创建新对象,不停引用给同一个变量。

如果要执行10000次循环重新赋值的过程,就会创建10000个字符串对象,效率很低,这是就需要使用可变字符串对象。

public static void main(String[] args) {

    System.out.println("程序开始执行");

    String str = "hello";
    //从1970/1/1 0:8:0   至今经过了多少毫秒
    //记录开始时间
    long startTime = System.currentTimeMillis();
    //创建一个可变字符串对象
    StringBuilder sb = new StringBuilder("hello");
    //频繁更新字符串
    for (int i = 0; i < 5000000; i++) {
        //循环多少次,就会创建多少个String对象,每个对象的创建需要时间和空间
        //str += i;//实际不是更新字符串,而是创建字符串
        //全程只有一个对象参与,每次循环只是在操作该对象
        sb.append(i);
    }
    //记录结束时间
    long endTime = System.currentTimeMillis();
    System.out.println("程序执行完毕");
    System.out.println("用时"+(endTime-startTime)+"毫秒");
}

StringBuilder

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

StringBuffer

用于表示可变字符串的一个类,是线程安全的,在多线程环境下使用。

StringBuilder和StringBuffer中的方法都一致,只不过StringBuffer中的方法使用了synchronized关键字修饰,表示是一个同步方法,在多线程环境下不会出现问题。

这里以StringBuilder为例

构造方法

常用构造方法作用
StringBuilder()创建一个大小为16的字节数组,表示一个空白字符串。
StringBuilder(int capacity)创建一个指定大小的字节数组,表示一个空白字符串。
StringBuilder(String str)创建一个str的长度+16大小的字节数组。表示str这个字符串。

常用方法

常用方法作用
append(Object obj)将任意数据添加到原可变字符串末尾
delete(int start,int end)删除[start,end)范围内的字符
deleteCharAt(int index)删除index索引上的字符
insert(int index,Object obj)将obj添加到index上
replace(int start,int end,String str)将[start,end)范围内的字符替换为str
reverse()反转字符串

注意

  • String类中的所有方法调用后,都会创建一个新的String对象,即原本的String字符串不会改变

  • StringBuilder类中的所有方法都是在操作同一个字符串对象,每次调用方法,都会让原字符串发生变化

  • StringBuilder类中没有重写equals方法,所以判断两个可变字符串对象是否相同时,如果调用equals方法,

    实际调用的是Object类中未重写的方法,即==判断。所以判断可变字符串是否相同时,需要将其转换为String对象再调用equals方法。

可变字符串与String之间的转换

String转换为可变字符串

String str="hello";
//使用构造方法将String对象转换为StringBuilder对象
StringBuilder sb = new StringBuider(str);

可变字符串转换为String(任意类型对象转换为String)

  • String.valueOf(Object obj)方法

    StringBuilder sb = new StringBuider("hello");
    //将任意类型对象转换为String对象
    String str = String.valueOf(sb);
    
  • toString()方法

    StringBuilder sb = new StringBuider("hello");
    //调用任意对象的toString()方法
    String str = sb.toString();
    
  • 拼接空字符串

    StringBuilder sb = new StringBuider("hello");
    String str = sb+"";
    

比较String、StringBuilder和StringBuffer的区别

相同点:

  • 这三个类都可以表示字符串。都提供了一些操作字符串的方法。
  • 这三个类中有相同的方法,如charAt(),indexOf()等。
  • 这三个类都是被final修饰的类,不能被继承

不同点:

  • String定义的字符串是一个常量。可变字符串定义的字符串是一个变量。

  • String类中的方法调用后,不会改变原本字符串的值。可变字符串中的方法调用后,会改变原本字符串的值

  • StringBuilder是非线程安全的可变字符串类,StringBuffer是线程安全的可变字符串类,其中的方法被synchronized修饰。

  • 在频繁操作同一个字符串时,一定使用StringBuilder或StringBuffer对象。

  • 操作不经常改动的字符串,使用这三个类中相应的方法处理。

System类

这个类中包含了一些系统相关的信息和一些方法。其中的属性和方法都是静态的。

这类不能创建对象,不是因为它是以抽象类,而是因为它的构造方法是私有的。

常用方法和属性

常用方法和属性
System.ou获取标准输出流对象,用于打印信息
System.in获取标准输入流对象,用于获取输入的信息
System.err获取错误输出流对象,用于打印异常信息
System.exit(int statues)终止虚拟机运行,参数0表示正常终止
System.currentTimeMills()获取从1970/1/1 0:0:0至今经过了多少毫秒。中国是UTC(+8),所以实际是从1970/1/1 8:0:0至今经过了多少毫秒。返回值为long类型。通常称为时间戳。
System.arraycopy(原数组,原数组的起始位置,目标数组,目标数组的起始位置,要复制的元素数量)将原数组中指定数量的元素复制到新数组中

Runtime类

Runtime类的对象,表示程序运行时对象(程序运行环境对象)。

包含了程序运行环境相关的信息。常用语获取运行环境信息(如虚拟机信息),或执行某个命令。

特点

这个类不是一个抽象类,但不能创建对象,因为它的构造方法是私有的。

这个类提供了一个静态方法getRuntime(),通过该方法,可以获取一个Runtime类的对象。

这种方式可以保证该类只能创建一个对象,是Java中的一种设计模式:单例模式

public class Runtime{
    //定义了一个私有的静态成员,创建一个当前类的对象
    private static Runtime currentRuntime = new Runtime();
    //将构造方法私有,无法在外创建对象
    private Runtime();
    //定义了一个公开的静态方法,用于获取创建的唯一的当前类的对象
    public static Runtime getRuntime(){
        return currentRuntime;
    }
}

使用

public class Test {
    public static void main(String[] args) throws IOException, InterruptedException {

        //如果要使用Runtime类中的方法,先通过其静态方法获取它的对象
        Runtime runtime = Runtime.getRuntime();

        System.out.println("当前虚拟机的空闲内存"+runtime.freeMemory() / 1024 / 1024 + "MB");//b  kb  MB
        System.out.println("当前虚拟机的实际最大内存"+runtime.totalMemory()/ 1024 / 1024 + "MB");
        System.out.println("当前虚拟机的支持的最大内存"+runtime.maxMemory()/ 1024 / 1024 + "MB");

        //exec(String 指令名) 运行某个指令,返回运行的进程对象
        //mspaint画图  calc计算器 notepad记事本
        Process mspaint = runtime.exec("mspaint");
        Thread.sleep(3000);
        //销毁进程对象
        mspaint.destroy();
        /*runtime.exec("calc");
        runtime.exec("notepad");*/
        //定时关机
        //runtime.exec("shutdown -s -t 600");
        //runtime.exec("C:\\Program Files (x86)\\Tencent\\QQ\\Bin\\QQ.exe");

    }
}

Date类

date日期 data数据

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

构造方法

常用构造方法说明
Date()创建当前时间对应的日期对象
Date(long l)创建指定瞬间对应的日期对象
Date(int year,int month,int date)根据年月日创建日期对象。

常用方法

常用方法作用
getTime()得到Date对应对象的毫秒数
after(Date when)判断参数是否在调用日期之后
before(Date when)判断参数是否在调用日期之前

SimpleDateFormat类

用于格式化日期的类。

构造方法

常用构造方法作用
SimpleDateFormat(String pattern)创建一个指定日期模板的格式化日期对象

日期模板

特殊字符作用
yyyy
MM
dd
hh12小时制
HH24小时制
mm
ss
E星期
yyyy/MM/dd HH:mm:ss E2023/03/09 14:05:16 周四

两个字母都可以写成一个,如5月,M–5,MM–05

常用方法

常用方法返回值作用
format(Date date)String将Date对象按日期模板转换为字符串
parse(String str)Date将满足日期模板的字符串转换为Date对象
package com.hqyj.dateTest;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;

public class DateFormatTest {
    public static void main(String[] args) throws ParseException {


        //根据指定模板创建格式化日期对象
        //SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM/dd HH:mm:ss E");
        //format(Date date)将Date对象格式化,返回格式化后的字符串
        //String format = sdf.format(new Date());
        //System.out.println(format);

        //SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        //将满足日期模板的字符串转换为Date对象
        //Date birthday = sdf.parse("1999/9/9");
        //System.out.println(birthday);

        //计算两个输入的日期之间相隔的天数
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入第一个日期,格式如  1999/5/21");
        String dateString1 = sc.next();
        System.out.println("请输入第二个日期,格式如  1999/5/21");
        String dateString2 = sc.next();
        //将日期字符串转换为Date对象
        Date date1 = sdf.parse(dateString1);
        Date date2 = sdf.parse(dateString2);

        //得到日期对应的毫秒数
        long l = date1.getTime() - date2.getTime();
        System.out.println("相隔" + (Math.abs(l) / 1000 / 3600 / 24) + "天");
    }
}

Calendar类

表示日历的类,包含了很的多日历相关的信息。

是一个抽象类,无法创建对象,可以通过静态方法getInstrance()获取Calendar类的实例。

//获取Calendar类的实例
Calendar c = Calendar.getInstance();

日历字段

在Calendar类中,定义了很多被final static修饰的静态常量,称为日历字段。实际是一个数字,用于获取指定日历信息。

常用方法

常用方法作用
get(int field)根据日历字段获取对应的值
getMaximum(int field)获取指定日历字段的最大值,如日期最大为31
getActualMaximum(int field)获取指定日历字段的实际最大值,如11月的日期最大为30
getTime()将Calendar对象转换为Date对象
set(int field,int value)将指定的日历字段设置为指定值
set(int year,int month,int date)同时设置日历的年月日
setTime(Date date)将Date对象作为参数设置日历的信息

使用Calendar类实现万年历

package com.hqyj.dateTest;

import java.util.Calendar;
import java.util.Scanner;

public class Test2 {
    public static void main(String[] args) {
        //实现"万年历"
        //输入年份和月份,输出
        Calendar c = Calendar.getInstance();
        Scanner sc = new Scanner(System.in);
        System.out.println("输入年份");
        int year = sc.nextInt();
        System.out.println("输入月份");
        int month = sc.nextInt();
        //日历设置为当月1号
        c.set(year, month - 1, 1);

        //得到当月最大日期
        int maxDate = c.getActualMaximum(Calendar.DATE);
        //换行计数
        int count = 0;
        //输出空格
        /*
         * DAY_OF_WEEK       星期         空格数量
         * 2                  一          0
         * 3                 二           1
         * 4                 三           2
         * 5                 四           3
         * 6                 五           4
         * 7                 六           5
         * 1                 天           6
         * //周天空6 其余空DAY_OF_WEEK-2
         * */
        System.out.println("一\t二\t三\t四\t五\t六\t日");
        //获取当月1号是一周中的第几天
        int week = c.get(Calendar.DAY_OF_WEEK);
        //周天空6格
        if (week == 1) {
            System.out.print("\t\t\t\t\t\t");
            //空格也需要计数
            count += 6;
        } else {
            //其他情况空星期-2个格
            for (int j = 1; j <= week - 2; j++) {
                System.out.print("\t");
                //空格也需要计数
                count++;
            }
        }
        //输出数字
        for (int i = 1; i <= maxDate; i++) {
            System.out.print(i + "\t");
            //计数+1
            count++;
            //计数到7换行
            if (count % 7 == 0) {
                System.out.println();
            }
        }

    }
}

# 方法调用时传值问题

package com.hqyj.methodTest;

public class Main {

    /*
    * 参数是原始类型,方法内部对参数重新赋值,实参没有影响
    * */
    public static void fun1(int i) {
        i = 123;
        System.out.println(i);
    }
    /*
     * 参数是String,方法内部对参数重新赋值,实参没有影响
     * */
    public static void fun2(String str) {
        str = "new str";
        System.out.println(str);
    }

    /*
    * 参数是引用类型,方法内部在操作参数,实参受影响
    * */
    public static  void fun3(Person p){
        p.setName("吴彦祖");
        System.out.println(p.getName());
    }

    /*
     * 参数是引用类型,方法内部对形参重新赋值后操作,实参没有影响
     * */
    public static  void fun4(Person p){
        p=new Person();
        p.setName("吴彦祖");
        System.out.println(p.getName());
    }

    /*
    * 参数是引用类型,方法内部在操作参数,实参受影响
    * */
    public static  void fun5(int[] nums){
        nums[0]=123;
        System.out.println(nums[0]);
    }


    public static void main(String[] args) {
        int i=0;
        fun1(i);//123
        System.out.println(i);//0

        System.out.println("-------------");

        String oldStr="old str";
        fun2(oldStr);//new str
        System.out.println(oldStr);//old str

        System.out.println("-------------");
        Person p = new Person();
        p.setName("小王");
        fun3(p);//吴彦祖
        System.out.println(p.getName());//吴彦祖

        System.out.println("-------------");
        Person p2 = new Person();
        p2.setName("小李");
        fun4(p2);//吴彦祖
        System.out.println(p2.getName());//小李

        System.out.println("-------------");
        int[] nums={1,2,3,4};
        fun5(nums);//123
        System.out.println(nums[0]);//123

    }

}

总结

参数为引用类型(类、数组、接口),且在方法中直接操作该参数时,才会对实际参数造成影响。

fun3(Person p)参数为Person对象,方法中直接调用参数p的xxx方法,是在操作实际参数。

fun5(int[] list)参数为数组,方法中在直接操作数组某个索引对应的元素,是在操作实际参数。

fun2(String str)和fun4(Person p)都在方法中创建另一个新的对象,是在操作方法中创建的对象,不影响实际参数。

public static void fun(char[] list,Person p){
    list[0]='m';//方法内部直接操作实际参数,会影响实际参数
    p = new Person();//方法内部创建了新对象给实参重新赋值,不会影响实际参数
    p.setName("xxx");
}
public static void main(String[] args){
    Person p = new Person();
    p.setName("qwe");
    char[] list ={'a','b','c'};

    fun(list,p);

    System.out.println(p.getName());//qwe
    System.out.println(list[0]);//m
}

包装类

Java是纯面向对象语言,宗旨是将一切事物视为对象处理。

但原始类型不属于对象,不满足面向对象的思想。但原始类型无需创建对象,保存在栈中,效率更高。

为了既保证效率又让原始类型也有对应的类类型,达到"万物皆对象"的理念,所以就有了包装类的概念。

包装类就是原始类型对应的类类型。

包装类常用于字符串与原始类中之间的转换。

在web应用中,从浏览器页面中获取数据提交到服务器,全部都是String类型,所以一定要使用字符串转换为原始类型的方法。

包装类原始类型
Bytebyte
Shortshort
Integerint
Longlong
Floatfloat
Doubledouble
Characterchar
Booleanboolean

特点

  • 八个原始类型中,除了int和char,其余包装类都是将原始类型的首字母改为大写。

    int对应Integer,char对应Character

  • 包装类都是被final修饰的,不能被继承

  • 除了Character类,其余包装类都有两个过时的构造方法,参数为对应的原始类型或字符串

    Character只有一个参数为char类型的构造方法

    构造方法的目的都是将原始类型的数据转换为包装类的对象

  • 除了Character类,其余包装类都有静态方法"parse原始类型单词(String str)",用于将字符串转换为相应的原始类型

    • 数值型的包装类parseXXX()方法,如果参数不是对应的数字,就会抛出NumberFormat异常,如"123a"或"123.4"都会报错
    • boolean的包装类Boolean的parseBoolean()方法,如果参数不是"true"这个单词的四个字母,转换结果都是false
  • 除了Boolean类,其余包装类都有MAX_VALUE和MIN_VALUE这两个静态属性,用于获取对应类型支持的最大最小值

  • 所有包装类都重写了toString(),用于将包装类对象转换为String对象

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

字符串转换为原始类型

使用原始类型对应的包装类,调用parseXXX(String str)方法

String num="123";

byte b = Byte.parseByte(num);//123
short s = Short.parseShort(num);//123
int i = Integer.parseInt(num);//123
long l = Long.parseLong(num);//123

float f = Float.parseFloat(num);//123.0
double d = Double.parseDouble(num);//123.0

boolean flag = Boolean.parseBoolean(num);//false

原始类型转换为字符串

  • 使用String类的String.valueOf(Object obj)

    int num=12;
    String str = String.valueOf(num);
    
  • 拼接空白字符串

    int num=123;
    String str = num+"";
    
  • 将原始类型转换为包装类后,调用toString()

    int num=123;
    Integer integer=new Integer(num);
    String str = interger.toString();
    

装箱和拆箱

装箱

拆箱

自动装箱和拆箱

  • 自动装箱缓冲区

    //i1和i2保存的数字在byte范围[-127,127]内,这个值会共享,只会有一个"100"对象
    Integer i1 = 100;
    Integer i2 = 100;
    System.out.println(i1 == i2);//i1和i2引用同一个地址,结果为true
    
    //i3和i4保存的数字不在byte范围[-127,127]内,会创建对象
    Integer i3 = 128;//128对象
    Integer i4 = 128;//128对象
    System.out.println(i3 == i4);//i3和i4引用不同的地址,结果为false
    System.out.println(i3.equals(i4));//包装类重写了equals,会拆箱后判断,结果为ture
    
    • 使用自动装箱给包装类对象赋值,值的范围在byte范围[-127,127]内,这个值会保存在缓冲区中,如果多个对象都使用这个值,共享这一个数据,使用同一个地址,==判断结果true;值的范围不在byte范围[-127,127]内,就会创建新的包装类对象,会有不同的地址,==判断结果false
    • 引用类型对象比较相同时,不要使用==,包括包装类的对象。比较相同时,使用包装类重写的equals方法

异常

当程序没有按开发人员的意愿正常执行,中途出现错误导致程序中断,这种情况,就称为异常。

学习异常就是认识异常的种类,如何处理异常和避免异常出现。

异常的产生

异常在程序中以对象的形式存在。当代码执行过程中出现异常,虚拟机会自动创建一个异常对象,如果没有对该异常对象进行处理,就会导致程序中断,不再执行后续内容。

异常的分类

异常在程序中以对象的形式存在,就有相应的类。

所有的异常类,组成了"异常家族"。

Error错误

如果出现xxxError,如StackOverFlowError,栈溢出,无法通过额外的代码解决,只能修改源代码。

Exception异常

  • RunTimeExcetpion运行时异常

    如果一个异常类属于RunTimeExcetpion异常类的子类,称这个异常为运行时异常可以通过编译,运行时可能抛出异常对象

    常见运行时异常说明出现的情景
    NullPointerException空指针异常如用空对象null调用属性或方法
    IndexOutOfBoundsException索引越界异常如当使用某个带有索引的对象超出范围
    NumberFormatException数字格式异常如调用包装类的parseXX()方法,如果参数不能转换
    InputMismatchException输入不匹配异常如使用Scanner接收控制台输入时,如果输入的数据不是对应的类型
    ClassCastException对象转型异常如Person p = (Person)Dog dog;
    ArithmeticException算术运算异常如0当分母
  • 编译时异常

    如果一个异常类不属于RunTimeExcetpion异常类的子类,称这个异常为编译时异常无法通过编译,必须要处理异常后才能编译运行。

    常见编译时异常说明出现的情景
    IOException输入输出流异常使用流对象
    FileNotFoundException文件未找到以方法的参数为文件对象时
    SQLException数据库相关异常操作数据库时

处理异常

通常所说的处理异常,指的是处理Exception类的子类异常。

处理异常的目的,就是保证程序正常执行。

方式一:try-catch-finally语句

这种方式处理异常,无论会不会抛出异常,都能让程序正常执行

try{
    //可能出错的代码
}catch(异常类 异常对象){
    //如果try中的代码抛出异常,异常对象属于catch中的异常类型,就会执行这里的代码
}catch(异常类 异常对象){
    //如果try中的代码抛出异常,异常对象属于catch中的异常类型,就会执行这里的代码
}...{

}finally{
    //无论程序是否会抛出异常,都要执行这里的代码
}

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

try-catch-finally使用时注意
  • 如果代码会抛出多个异常,可以使用多个catch进行捕获。需要将子类异常放在最前,父类异常放在最后

  • try、catch、finally都不能单独使用,try必须配合catch或finally或一起使用

  • 无论try中的代码是否会抛出异常,finally中的代码一定会执行

  • 执行try中的代码是,如果出现异常,就不再执行try中剩余代码

  • try中定义的内容,无法在try之外的地方使用

  • try中如果有return,不影响finally的执行,finally优先于return执行

方式二:throws关键字

这种方法,可以让编译时异常通过编译。

在定义方法的时候,通过该关键字声明方法可能抛出的异常

用法:方法的参数部分后,添加 throws 异常类型1,异常类型2…

public class Test{
    //这时该方法就会有一个声明:该方法可能会抛出InterruptedException异常
    public void fun() throws InterruptedException{
        //sleep()方法在源码中声明了可能会抛出InterruptedException异常,
        //InterruptedException异常不是RuntimeException的子类异常,必须要处理才能通过编译
        //要么使用try-catch处理,要么继续声明有异常
        Thread.sleep(500);
    }
}

throw和throws

  • throws表示用于声明方法有可能出现的异常。使用时写在方法的小括号之后

    public void fun() throws InterruptedException{
        Thread.sleep(500);
    }
    
  • throw用于手动抛出异常对象。使用时,写在方法体中,“throw 异常对象”。

    常用于满足某种条件时,强制中断程序。

    public void fun(){
        throw 
    }
    

自定义异常

如果需要在某种情况下中断程序,可以自定义一个异常类。再通过throw关键字手动抛出自定义异常。

自定义异常步骤

1.创建一个类,继承某个异常类

  • 如果继承的是RuntimeException,表示自定义的异常类属于运行时异常,该异常对象可以不用处理
  • 如果继承的是非RuntimeException,表示自定义的异常属于编译时异常,该异常对象必须要处理

2.可选操作。定义带参构造方法,参数为String类型的异常信息,调用父类中的构造方法

/*
* 自定义异常
* 只需继承某个异常类即可
* 是否定义构造方法根据实际情况决定
* */
public class MyException extends NullPointerException {

    /*
    * 带参构造,参数为异常信息
    * */
    public MyException(String msg){
        super(msg);
    }
    /*
    * 无参构造
    * */
    public MyException(){
        super();
    }
}

数组和集合

数组的特点

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

集合的特点

  • 能保存一组数据,元素可以有序或无序(存入的顺序和读取的顺序不一致)
  • 集合中保存的元素的数据类型可以不同
  • 集合的容量可变
  • 可以获取集合中保存的元素实际数量

集合家族(集合框架)

图上的所有实现类,都是非线程安全的,在多线程环境下使用以上任意集合,都会出现数据不准确的情况。

Collection接口

该接口中有两个核心子接口:List和Set。

这两个接口都可以保存一组元素,List接口保存元素有序可重复Set接口保存元素无序不重复

Collection接口有一个父接口Iterable,它不是一个集合,而是用于遍历集合的工具接口,包含forEach()和iterator()方法

常用方法返回值作用
add(Object obj)boolean将元素添加到集合中
size()int获取集合中的元素数量
isEmpty()boolean判断集合是否为空
clear()void清空集合
contains(Object obj)boolean判断集合中是否包含指定元素
remove(Object obj)boolean移除集合中的指定元素
toArray()Object[]将集合转换为数组
stream()Stream获取集合的流对象,用于遍历集合
iterator()Iterator得到集合的迭代器对象,用于遍历集合

List接口(有序可重复)

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

List接口在继承Colletion接口后,又拓展了一些操作元素的方法。

拓展方法返回值作用
get(int index)Object得到指定索引的元素
set(int index,Object obj)Object使用obj替换index上的元素,返回被替换的元素
add(int index,Object obj)void将obj添加到index上
remove(int index)Object移除指定索引的元素,返回被移除的元素
indexOf(Obejct obj)int得到obj第一次出现的索引
lastIndexOf(Object obj)int得到obj最后一次出现的索引
subList(int from,int to)List截取[from,to)区间内的元素,返回子集合
List.of(E… element)List根据参数创建一个不可变集合,该集合不能对其中的元素进行修改

ArrayList实现类(掌握)

  • 采用数组实现的集合
  • 可以通过索引访问元素,可以改变集合大小,如果要在其中插入或删除元素时,会影响后续元素
  • 该集合查询效率高,中途添加和删除元素效率低
  • 集合中保存的都是引用类型。如集合中保存123,保存的不是int类型的123,而是Integer类型的123
构造方法
构造方法说明
ArrayList()创建一个Object类型的空数组。在调后续添加方法时,才会初始化数组大小为10。
ArrayList(int initialCapacity)创建一个指定容量的Object类型数组,如果参数为负数,会抛出IllegalArgumentException异常
ArrayList(Collection c)根据指定集合创建Object类型数组
常用方法

主要以List接口和Collection接口中的方法为主。

LinkedList实现类

  • 采用双向链表实现的集合

  • 集合中保存的每个元素称为节点,除首尾节点外,其他节点既保存了自己的数据,还保存了其前后节点的地址

  • 如果在双向链表的结构中进行插入和删除节点的操作时,不会影响其他节点现在的保存位置。

    添加的节点只需记录前后节点的位置接口。

  • 如果要查询某个节点的地址时,需要从头结点或尾节点开始搜索目标节点的位置

  • 双向链表在中间插入和删除数据效率高,随机读取的效率低

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

主要以List接口和Collection接口中的方法为主。由于还实现了Deque接口,所以还有一些Deque接口中的方法。如操作首尾节点的方法。

常用来自于Deque接口中的方法作用
addFirst(Object obj)添加obj为头结点
addLast(Object obj)添加obj为尾结点
getFirst()得到头结点
getLast()得到尾节点
removeFirst()移除头结点
removeLast()移除尾节点

ArrayList和LinkedList的区别

  • 这两个类都是List接口的实现类,保存的元素有序可重复,允许保存null
  • ArrayList采用数组实现,随机读取效率高,插入和删除效率低,适用于查询
  • LinkedList采用双向链表实现,插入和删除效率高,随机读取效率低,适用于频繁更新集合

Set接口(无序不重复)

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

Set接口中的方法都是继承于Collection接口。

哈希表hash table

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

假设原本的数据为左侧的数组,如要查询10,需要遍历数组,效率不高。

通过一个特定的函数"原始值%5",得到一组新数组,让新数据重新对应元素,保存到新数组中,这个新数组称为哈希表。

这时如果要查询10,由于哈希函数是10%5得到0,所以直接查询哈希表中0对应的元素即可。

整个过程中,这个函数称为哈希函数,得到的新数组称为哈希码,新数组称为哈希表,对应关系称为哈希映射。

这个哈希函数,有一定的几率让多个原始值得到相同的哈希码,这种情况称为哈希冲突(哈希码一致,实际值不同)。

为了解决哈希冲突,可以使用"拉链法",将冲突的数据保存在对应哈希码之后的链表中。

哈希码的特点

  • 如果两个对象的hashcode不同,这两个对象一定不同
  • 如果两个对象的hashcode相同,这两个对象不一定相同
    • hashcode相同,对象不同,这种现象称为哈希冲突
    • 通话”和"重地"这两个字符串的hashcode相同,但是两个不同的对象

HashSet实现类

  • 采用哈希表实现
  • 元素不能重复,无序保存,允许保存一个null
  • 本质是HashMap对象
  • 使用HashSet集合时,通常要重写实体类中的equals和hashcode方法
构造方法
常用构造方法说明
HashSet()创建一个空集合,实际是创建了一个HashMap对象
常用方法

HashSet中没有定义属于自定的方法,都是父接口Set和Collection中的方法。

HashSet添加数据的原理

如果添加的两个元素的equals方法结果为true且hashcode相同时,视为同一个对象,不能添加。

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

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

由此可见,不能添加的条件是两个对象的hashcode相同且equals的结果为true。

可以只判断equals的结果。但是如果每次都判断equals,由于重写equals时会判断很多属性,效率不高。

如果只判断hashcode是否相同,效率高,但可能会出现哈希冲突。

所以先判断hashcode,再判断equals,既能保证效率,又能保证不会添加重复元素。

equals和hashcode的关系
  • 如果两个对象的equals方法结果为true,在没有重写equals方法的前提下,hashcode相同吗?

    • 如果没有重写equals方法,默认使用Object中的方法,使用==判断,如果结果为true,说明是同一个地址,同一个对象,hashcode一定相同
  • 如果两个对象的hashcode不同,在没有重写equals方法的前提下,equals方法的结果为?

    • hashcode不同,说明不是同一个对象,没有重写equals,说明使用Object中的方法,使用==判断,结果为false
  • 如果两个对象的hashcode相同,equals方法的比较结果为?

    • 可能为true或false

      String str1="hello";
      String str2="hello";
      //str1和str2使用同一个地址,hashcode相同,equals结果为true
      String str3="通话";
      String str4="重地";
      //str3和str4不是同一个地址,但hashcode相同,这种情况称为哈希冲突,equals结果为false
      
HashSet的应用

如果要保存的对象保证不重复,且无关顺序,可以使用HashSet。重写要保存的元素的equals和hashcode方法。

Student类,保证添加对象时,不重复

public class Student {
    private int id;
    private String name;
    private String major;

    @Override
    public String toString() {
        return "Student{" +
            "id=" + id +
            ", name='" + name + '\'' +
            ", major='" + major + '\'' +
            '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return id == student.id && Objects.equals(name, student.name) && Objects.equals(major, student.major);
    }

    /*
    * 重写hashcode方法,根据所有属性生成哈希码
    * */
    @Override
    public int hashCode() {
        return Objects.hash(id, name, major);
    }

    public Student(int id, String name, String major) {
        this.id = id;
        this.name = name;
        this.major = major;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getMajor() {
        return major;
    }

    public void setMajor(String major) {
        this.major = major;
    }
}

StudentManager类

public class StudentManager {
    private HashSet<Student> hs = new HashSet<>();


    //添加时,如果对象的属性都一致,视为同一个对象,不能重复添加
    public void addStudent(Student student){
        hs.add(student);
    }

}

TreeSet实现类

  • 特殊的Set实现类,数据可以有序保存,可以重复,不能添加null

  • 采用红黑树(自平衡二叉树)实现的集合

    • 二叉树表示某个节点最多有两个子节点
    • 某个节点右侧节点值都大于左侧节点值
    • 红黑树会经过"变色"和旋转达到二叉树的平衡
  • 只能添加同一种类型的对象且该对象实现了Comparable接口

    • 每次调用add方法添加元素时,会自动调用Comparable接口中的方法compareTo()方法
    • 实现Comparable接口后必须要重写compareTo()方法,用于决定新添加的元素放在旧元素之前或之后
  • compareTo方法的返回值决定了能否添加新元素和新元素的位置

    • 如果返回0,视为每次添加都是同一个元素,不能重复添加
    • 如果返回正数,将新元素添加到现有元素之后
    • 如果返回负数数,将新元素添加到现有元素之前
  • 添加的元素可以自动排序

构造方法
常用构造方法说明
TreeSet()创建一个空集合
常用方法

能使用Set和Collection接口中的方法,还定义了一些属于它的方法

独有方法作用
first()得到集合中的第一个元素
last()得到集合中的最后一个元素
ceiling(Object obj)得到集合中比参数大的元素中的最小元素
floor(Object obj)得到集合中比参数小的元素中的最大元素
TreeSet的应用

如果要保存的元素需要对其根据某个属性排序,使用该集合。

如在集合中保存整数,即Integer对象,Integer类已经实现了Comparable接口,

如要保存自定义的元素,必须要实现Comparable接口,重写compareTo方法,自定义排序规则。

Person类

public class Person implements Comparable {
    private String name;
    private int age;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    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;
    }

    /*
        根据age属性升序保存
    */
    @Override
    public int compareTo(Object o) {
        return this.getAge() - ((Person) o).getAge();
    }
}

Main类

public class Test {
    public static void main(String[] args) {
        //使用TreeSet保存Person对象,Person有age属性,按age升序保存到集合
        TreeSet<Person> personSet = new TreeSet<>();
        Person p1 = new Person("小王",20);
        Person p2 = new Person("小李",15);
        Person p3 = new Person("小赵",21);
        personSet.add(p1);
        personSet.add(p2);
        personSet.add(p3);

        System.out.println(personSet);
    }
}

Map接口

Map称为映射,该集合中保存的数据是以键值对的形式保存,保存的键与值的映射关系。

键称为Key,值称为Value,键不能重复,允许出现一个null作为键,值没有限制。

键和值都必须是引用类型。

如,yyds – 永远单身 这就是一个映射关系,"yyds"就是键key,永远单身"就是值value

常用方法作用
size()得到键值对的数量
isEmpty()判断是否为空集合
clear()清空所有键值对
put(Object key,Object value)添加一组键值对
get(Object key)根据键得到对应的值
containsKey(Object key)判断是否存在某个键
containsValue(Object value)判断是否存在某个值
keyset()得到键的集合
values()得到值的集合
entrySet()得到键值对的集合
remove(Object key)删除指定的键值对

HashMap实现类

构造方法

常用构造方法说明
HashMap()创建一个大小为16,加载因子为0.75的空集合

常用方法

使用Map接口中的方法

  • JDK1.8之后,HashMap采用"数组+链表+红黑树"实现
    • 当没有出现哈希冲突时,元素保存在数组中
    • 如果出现哈希冲突,在对应的位置上创建链表,元素保存到链表中
    • 如果链表的长度大于8,将链表转换为红黑树

遍历集合的方式

遍历List集合

  • 普通for循环

    for(int i=0;i<集合.size();i++){
        元素 变量 =  集合.get(i);
    }
    
  • 增强for循环

    for(数据类型 变量名 : 集合){
        元素 变量 =  集合.get(i);
    }
    
  • forEach()方法

    使用该方法遍历集合时,不要使用add或remove操作,遍历会抛出异常。

    集合.forEach(obj -> {
         元素 变量 =  集合.get(i);
    });
    
  • 迭代器

    //Collection接口有一个父接口Iterable,其中有一个iterator方法用于获取迭代器对象遍历集合
      //所有Collection的子实现类都能调用该方法
    Iterator it = Collection集合.iterator();
    //hasNext()判断是否还有下一个元素
    while(it.hasNext()){
        //next()方法读取该元素
        元素 变量 = it.next();
    }
    

遍历Set集合

  • 普通for循环无法遍历Set集合,因为元素没有索引

  • 增强for循环

    for(数据类型 变量名 : 集合){
        元素 变量 =  集合.get(i);
    }
    
  • forEach()方法

    集合.forEach(obj -> {
         元素 变量 =  集合.get(i);
    });
    
  • 迭代器

    //Collection接口有一个父接口Iterable,其中有一个iterator方法用于获取迭代器对象遍历集合
    //所有Collection的子实现类都能调用该方法
    Iterator it = Collection集合.iterator();
    //hasNext()判断是否还有下一个元素
    while(it.hasNext()){
        //next()方法读取该元素
        元素 变量 = it.next();
    }
    

遍历HashMap集合

Set keySet = 集合.keySets();
for(Object key :keySet){
    Object value=集合.get(key);
}

泛型

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

//默认可以保存任意类型的元素,即Object类型
List list = new ArrayList();
list.add(123);
list.add("hello");

//遍历时只能使用Object类型获取
for(Object obj : list){

}

使用泛型

在定义集合时,在集合类或接口后写上**<引用数据类型>**

集合类或接口<引用数据类型> 集合遍历名 = new 集合实现类();

//定义只能保存整数的集合,要使用整数的包装类类型
List<Integer> list = new ArrayList();
list.add(123);
//不能添加非整数
//list.add("hello");

Collections集合工具类

  • Collection是集合的根接口,定义了操作集合中元素的方法。

  • Collections是集合的工具,定义了操作集合中元素的静态方法。

Collections中的静态方法说明
Collections.shuffle(List list)打乱集合中元素的顺序
Collections.sort(List list)对集合中的元素进行排序。元素必须实现Comparable接口。
Collections.swap(List list,int a,int b)将集合中索引a和b的元素交换位置
Collections.reverse(List list)反转集合中的元素
Collections.max(Collection c)得到集合中元素的最大值。元素必须实现Comparable接口。
Collections.rotate(List list,int distance)将集合中最后distance个元素放在集合最前
Collections.fill(List list,Object obj)使用obj填充集合

Arrays数组工具类

包含了一些操作数组的静态方法

常用静态方法说明
Arrays.sort()对数组中的元素升序排序
Arrays.asList(T… obj)将可变参数转换为ArrayList集合

集合和数组之间转化

数组转换为集合

//调用Arrays工具类的asList(1,2,6,22,11)或asList(数组)
ArrayList<Integer> list = Arrays.asList(1,2,6,22,11);

集合转换为数组

ArrayList list = new ArrayList();
list.add("sdf");
list.add(123);
list.add(null);
//调用集合的toArray()方法
Object[] objs = list.toArray();

无论是数组转换集合还是集合转换数组,都可以进行遍历。

如果集合转换为数组,遍历集合,通过索引赋值。

如果数组转换为集合,遍历数组,通过add()添加元素

# 文件类File

Java中的File类,表示本地硬盘中的文件file或文件夹directory的一个类。

通过这个类创建对象,可以读取文件信息或操作对应文件。

构造方法

常用构造方法说明
File(String pathName)根据文件的完整路径创建File对象
File(String parent,String child)根据文件的父目录的路径和自身的名称创建File对象
File(File parent,String child)根据文件的父目录对应的File对象和自身的名称创建File对象
//使用不同构造方法表示C:\Users\Administrator\Desktop\230202.txt文件
//new File(String pathName);
File file1 = new File("C:\\Users\\Administrator\\Desktop\\230202.txt");
//new File(String parentPath,String childName);
File file2 = new File("C:\\Users\\Administrator\\Desktop", "230202.txt");
//new File(File parentFile,String childName)
File parent = new File("C:\\Users\\Administrator\\Desktop");
File file3 = new File(parent, "230202.txt");
System.out.println(file1.exists());
System.out.println(file2.exists());
System.out.println(file3.exists());

常用方法

常用方法作用
exists()判断文件是否存在
isFile()判断是否为文件
isDirectory()判断是否为文件夹
getName()获取文件名
getPath()获取文件相对路径
getAbsolutePath()获取文件绝对路径
getParent()获取文件父目录路径
getParentFile()获取文件父目录对象
lastModified()获取文件最后一次修改时间
length()获取文件字节大小
isHidden()判断是否为隐藏文件
delete()删除文件或空文件夹
renameTo(File newFile)移动文件到指定文件,可重命名
mkdir()创建文件夹
list()得到文件夹中的第一层子文件名称的数组,返回String[]
listFiles()得到文件夹中的第一层子文件对象的数组,返回File[]

斐波那契数列

public class Test2 {
    public static void main(String[] args) {
        //兔子问题
        //有一对兔子,在第三个月开始,每个月都生一公一母两只小兔子
        //假设所有兔子都不死亡,第10个月一共有多少只
        //1月    2月  3月  4月  5月  6月  7月  8月  9月  10月
        //1     1    2    3   5    8    13  21   34  55
        //斐波那契数列
        System.out.println(fun(3));
        System.out.println(fun(10));
    }

    /*
    * 递归调用
    * */
    public static int fun(int n) {
        if (n > 2) {
            return fun(n - 1) + fun(n - 2);
        }
        return 1;
    }
}

递归遍历文件夹

public class Test3 {
    public static void main(String[] args) {
        //输出某个文件夹下的所有文件
        File source = new File("D:\\GamePP Wonderful Moment");
        showAllFile(source);
    }

    public static void showAllFile(File source){
        //判断如果是文件夹
        if(source.isDirectory()){
            //展开第一层
            for (File child : source.listFiles()) {
                //child就是第一层的所有子文件,有可能还是文件夹,递归调用本方法
                showAllFile(child);
            }
        }
        //输出名称
        System.out.println(source.getName());
    }
}

IO

I:input输入

O:output输出

流Stream

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

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

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

流的分类

Java中的流也是类,以对象的实现表示流。流有"四大家族",是所有流的父类。

字节输入流InputStream

FileInputStreamObjectInputStream

字节输出流OutputStream

FileOutputStreamObjectOutputStream

字符输入流Reader

FileReader、BufferedReader、InputStreamReader

字符输出流Writer

FileWriter、BufferedWriter、OutputStreamWriter

按方向分类
  • 输入流:InputStream、Reader
    • 读硬盘中的数据到程序中
  • 输出流:OutputStream、Writer
    • 将程序中的数据写到硬盘中
按数据传输类型分类
  • 字节流:InputStream、OutputStream
    • 读写非文本类型文件。如图片、多媒体文件
  • 字符流:Reader、Writer
    • 读写纯文本文件。如txt、md等

如要将硬盘中的某个txt文件中的内容读取到程序中,使用Reader

如要将硬盘中的某个图片文件中的内容读取到程序中,使用InputStream

如要将程序中的文本写入到硬盘中,保存为txt文件时,使用Writer

如要将程序中的数据写入到硬盘中,保存为非文本文件时,使用OutputStream

流的四个父类的特点

  • 这四个父类都是在java.io包下,都是抽象类,不能直接创建其对象,使用其子类对象

  • 这四个类都有close()方法,用于关闭流对象,释放资源

  • 输入流(InputStream和Reader)都有read()方法,用于读取数据,输出流(OutputStream和Writer)都有write()方法

  • 输出流(OutputStream和Writer)都有flush()方法,用于将流中的数据冲刷到硬盘中

    • 在使用输出流对象时,一定要调用flush()或close()方法后,才能真正将数据写入到硬盘中
  • 所有流中,以Stream结尾,都是字节流,数据以字节传输;以Reader或Writer结尾,都是字符流,数据以字符传输

  • 读取硬盘中的数据时,读取的文件必须存在;写入数据到硬盘中时,写入的文件可以不存在,但父目录必须存在。

FileInputStream文件字节输入流(掌握)

以字节的形式读取文件

构造方法

常用构造方法作用
FileInputStream(String filePath)根据文件完整路径创建流对象
FileInputStream(File file)根据文件对象创建流对象

常用方法

常用方法作用
read()读取一个字节,返回读取到的字节
read(byte[] bytes)读取指定数组大小的字节,返回读取到的字节数量
read(byte[] bytes,int off,int len)读取指定数组大小的字节,从off索引开始读取len个字节,返回读取到的字节数量
available()文件可读取的最大字节数量
close()关闭流对象

FileOutputStream文件字节输出流(掌握)

以字节的形式写入文件

构造方法

常用构造方法说明
FileOutputStream(String filePath)根据文件名创建流对象,覆盖写入
FileOutputStream(String filePath,boolean appen)根据文件名创建流对象,追加写入
FileOutputStream(File file)根据文件对象创建流对象,覆盖写入
FileOutputStream(File file,boolean appen)根据文件对象创建流对象,追加写入

常用方法

常用方法作用
write(int b)写入一个字节
write(byte[] bytes)写入一个字节数组
write(byte[] bytes,int off,int len)写入一个字节数组,从off开始的len个字节
flush()将流中的数据冲刷到硬盘中
close()关闭流对象

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

  • 在通过FileInputStream对象使用read(byte[] bytes)方法时,每次读取指定数组的字节,将读取到的字节保存在字节数组中。

    该方法的返回值是读取到的字节数量,如果最后一次读取的字节数量不足字节数组的大小时,只会将读取到的内容覆盖数组中最前的几个元素。

    所以最后一次读取时,读取的内容多于实际内容。

  • 在通过FileOutputStream对象使用write(byte[] bytes),会将字节数组中的所有内容写入到输出流中,在最后一次写入时,会写入多余的内容。

    所以在写入时,使用write(byte[] bytes,int off,int len)方法,表示将字节数组中的内容,从off开始写入len个

    //如有test.txt中aaabbbccc
    //读取
    FileInputStream fis = new FileInputStream("test.txt");
    
    byte[] bytes=new bytes[4];
    int count=0;
    //第一次读取到aaab,读取了4个字节,当前数组为aaab
    count=fis.read(bytes);
    //第一次读取到bbcc,读取了4个字节,当前数组为bbcc
    count=fis.read(bytes);
    //第一次读取到c,读取了1个字节,当前数组为cbcc
    count=fis.read(bytes);
    
    //写入
    FileOutputStream fos = new FileOutputStream("copy.txt");
    //如果使用fos.write(byte[]);方式,最后一次会写入多于的bcc
    fos.write(bytes);
    
    //如果使用fos.write(byte[],int,int);方式,最后一次只会写入c
    fos.write(bytes,0,count);
    

单文件复制

public class Test2 {
    public static void main(String[] args) throws IOException {

        //使用FileInputStream读取字节的同时使用FileOutputStream写入字节,实现单文件复制
        //源文件
        File source = new File("d:\\xxx.txt");
        //读取
        FileInputStream fis = new FileInputStream(source);
        //写入到目标文件中
        FileOutputStream fos = new FileOutputStream("f:\\copy.txt");

        //定义字节数组,大小为8MB
        byte[] bytes = new byte[1024*1024*8];
        //读取指定数组大小的字节,返回读取到的字节数量
        int count = fis.read(bytes);
        while (count>0) {
            //写入指定数组的字节
            //fos.write(int b);写入一个字节
            //fos.write(byte[] bytes);写入一个数组的字节
            fos.write(bytes,0,count);//写入一个数组的字节,从0开始写入读取到的数量的字节
            count = fis.read(bytes);
        }
        fis.close();
        fos.close();
    }
}

复制文件夹

public class Test3 {
    public static void main(String[] args) throws IOException {

        //复制文件夹
        File file = new File("C:\\Users\\Administrator\\Desktop\\测试");
        File target = new File("f:\\复制文件夹");

        copyDir(file,target);
    }


    /*
    * 复制单个文件
    * */
    public static void copyFile(File source,File target) throws IOException {
        //读取源文件
        FileInputStream fis = new FileInputStream(source);
        //写入新文件
        FileOutputStream fos = new FileOutputStream(target);
        //读写字节数组
        byte[] bytes=new byte[1024*1024*8];
        //尽可能按数组大小读取
        int count=fis.read(bytes);
        //如果能读到,写入
        while(count>0){
            fos.write(bytes,0,count);
            count= fis.read(bytes);
        }
        fis.close();
        fos.close();
    }

    /*
    * 复制文件夹
    * */
    public static void copyDir(File source,File target) throws IOException {
        //如果是文件夹
        if (source.isDirectory()) {
            //创建目标文件夹,可以与源文件夹同名,也可以使用参数target名
            target.mkdir();
            //展开源文件,得到第一层子文件
            for (File child : source.listFiles()) {
                //需要将所有子文件保存在上一步创建的文件夹中
                //使用构造方法File(File parent,String child),定义新目标文件的路径
                //如source为桌面的"桌面/测试/a",复制到f盘的“复制文件夹”中,递归调用的target就为"f:/复制文件夹/a"
                File newTarget = new File(target, child.getName());
                //递归调用,这时的源文件为遍历出的子文件,目标文件为上一步构造的对象
                copyDir(child,newTarget);
            }
        }else{
            //如果是文件
            copyFile(source,target);
        }

    }

}


/*
    source    桌面:\测试
    target    F:\副本
    1.调用copyDir(source,target),判断source为文件夹,创建target,F盘下“副本”文件夹创建成功
    2.遍历source,如其中有test.txt文件,用child表示
    递归调用copyDir(source,target),这时的source就是child,target得是"F:\副本\test.txt",
    可以使用File(File parent,String child)构造方法表示这个target文件,即new File(target,child.getName())
*/

FileReader文件字符输入流

按字符读取文件

构造方法

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

常用方法

常用方法作用
ready()判断是否还有下一个字符
read()读取下一个字符,返回读取到的字符
read(char[] chars)按字符数组读取,返回读取到的字符数量,读取到的字符保存在字符数组中
close()关闭流对象

BufferedReader缓冲字符输入流(掌握)

自带缓冲区(字符数组)的字符输入流。默认字符数组大小为8192,每次最多读取8192个字符。

在读取纯文本文件(txt或md)时,首选该类。

构造方法

常用构造方法作用
BufferedReader(Reader in)创建一个带有缓冲区(大小为8192的char数组)的字符输入流对象,参数为Reader类型对象,Reader是抽象类,所以实际参数为Reader的子类,如FileReader,在FileReader对象中定义要读取的文件
BufferedReader(Reader in,int size)创建一个指定缓冲区(字符数组)大小的字符输入流对象

常用方法

常用方法作用
ready()判断是否还有字符
readLine()读取整行字符
close()关闭流对象

FileWriter文件字符输出流

按字符写入文件。必须在调用flush()或close()方法后才会写入。

构造方法

常用构造方法说明
FileWriter(String filePath)根据文件路径创建流对象,覆盖写入
FileWriter(String filePath,boolean append)根据文件路径创建流对象,append为true时追加写入
FileWriter(File file)根据文件对象创建流对象,覆盖写入
FileWriter(File file,boolean append)根据文件对象创建流对象,append为true时追加写入

常用方法

常用方法作用
writer(String str)写入字符串
flush()冲刷数据到硬盘
close()关闭流对象

BufferedWriter缓冲字符输出流(掌握)

按字符写入,带有缓冲区的输出流。必须在调用flush()或close()方法后才会写入。

构造方法

常用构造方法说明
BufferedWriter(Writer writer)根据Writer对象创建字符缓冲输出流

常用方法

常用方法作用
writer(String str)写入字符串
newLine()写入空白行
flush()冲刷数据到硬盘
close()关闭流对象

序列化ObjectOutputStream对象字节输出流(掌握)

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

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

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

构造方法

构造方法说明
ObjectOutputStream(OutputStream os)根据字节输出流对象创建

常用方法

常用方法作用
writeObject(Object obj)将一个对象写入到本地文件
flush()冲刷数据到硬盘
close()关闭流对象

反序列化ObjectInputStream对象字节输入流(掌握)

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

构造方法

构造方法说明
ObjectInputStream(InputStream is)根据字节输入流对象创建

常用方法

常用方法作用
readObject()读取序列化后的文件,返回Object类型
close()关闭流对象

序列化和反序列化案例

Person类,实现Serializable接口

/*
* 如果要序列化某个类的对象,该类必须要实现Serializable接口
* 该接口中没有定义任何方法,只是一个标记接口,表明该类可序列化
* */
public class Person implements Serializable {
    private String name;
    private int age;
    private String sex;
    //省略get/set/构造方法等
}

测试类

package com.hqyj.ObjectStreamTest;

import java.io.*;
import java.util.ArrayList;

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {

        Person p1 = new Person("王海", 22, "男");
        Person p2 = new Person("刘玉梅", 29, "女");
        Person p3 = new Person("白伟强", 21, "男");

        //将程序运行中的数据(对象)保存到本地,这个过程就称为序列化
        ArrayList<Person> list = new ArrayList<>();
        list.add(p1);
        list.add(p3);
        list.add(p2);

        //创建  "对象输出字节流"  对象,参数为字节输出流对象,这里使用FileOutputStream子类
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.p"));

        oos.writeObject(list);

        oos.close();
        //创建一个对象,将其保存为一个文件,再读取该文件中保存的对象
        //ObjectInputStream  反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.p"));
        //读取序列化文件中的对象
        Object obj = ois.readObject();
        //需要转换为相应的类型后使用
        ArrayList<Person>  pList = (ArrayList<Person> ) obj;
        for (Person person : pList) {
            System.out.println(person);
        }
        oos.close();
        ois.close();
    }
}

InputStreamReader

属于转换流(将字节流转换为字符流)

OutputStreamWriter

属于转换流(将字节流转换为字符流)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值