day01—常用类

Object类

Java中所有类都直接或间接继承Object类,因此也继承了Object类中的方法和属性

hashCode方法

返回该对象的十进制的哈希值、

hash值是由hash算法通过对象的地址、对象中的字符串、数字等,计算出来的

相同的对象应当返回相同的哈希值,不同的对象尽量返回不同的哈希码值

toString方法

返回对象的字符串表现形式,格式是全限定名+@+十六进制的hash值(地址)

如果直接输出一个对象,那么默认会调用这个对象的toString方法,许多类都提供各自重写过得toString方法,但数组没有,因此数组使用还是继承自Object类的方法

equals方法

Object类的equals方法的作用是比较两个对象是否相等。比较的是内存地址

clone方法

该方法可以将一个对象的所有属性克隆到另一个对象,要求克隆对象实现cloneable接口,并且属于潜克隆,即引用数据类型是复制地址

Objects工具类

全部是静态方法,直接使用类名调用

equals(对象1,对象2),比较两个对象是否相等,可以防止空指针异常

isNull(),判断是否为null,为null则返回true

nonNull(),判断是否为null,和isNull正好相反

数组工具类Arrays

常用方法

Arrays.sort(),数组排序,基本数据类型默认升序,想自定义比较规则则需要使用重载的sort方法传入自定义比较器,即实现Comparator接口中的compare方法的匿名类,比较器参数只接受引用类型,基本类型需要转成包装类,如果是自己写的类型的数组排序需要此类型实现Comparable接口中的compareTo方法,否则必须传入自定义比较器

Arrays.toString(arr),遍历数组,将数组内容打印,重写了数组的toString方法

Arrays.equals(arr1,arr2),比较两个数组元素是否相等

Arrays.binarySearch(arr, 30),二分查找元素,返回元素位置索引

Arrays.copyOf(arr, 3),拷贝数组,从第一个开始拷贝指定数量的元素组成一个新数组,不足补零,返回一个新数组

Arrays.copyOfRange(arr,1,3),拷贝数组,从指定下标开始到指定下标结束

Arrays.asList,数组转List集合

包装类

包装类就是基础数据类型的对应类,因为基本数据类型不具有方法和属性。而引用数据类型可以拥有属性和方法,使用更加的灵活

包装类名

基本类型 包装类
byte Byte
short Short
int Integer
long Long
float Float
double Double
boolean Boolean
char Character

声明方式

Integer ii1 = new Integer(100);,或者使用字面量Integer ii1 = 100;,其他类型同理

常用属性

MAX_VALUE,求数型的最大值

MIN_VALUE,求数型的最小值

常用方法

toBinaryString(数据),转换成二进制,返回字符串

toHexString(数据),转换成十六进制,返回字符串

parseInt(数据),转成整数型

parseDouble(数据),转成浮点数型

parsexxxx(数据),转成对应的数据类型

valueOf(数据),抓成相应的数据类型,根据调用方法的不同决定

String字符串类

String作为引用数据类型,却是最接近基础数据类型的,本质是char数组

构造方式

第一种的使用构造函数,String str = new String("hello"),构造函数也可以使用字符数组、字节数组作为参数,甚至可以为空

第二种是使用字面量,String str = "hello"

当使用构造函数构造字符串时是在创建一个新对象,就算两个对象的值相同,两者使用==比较时也是不同地址返回false

而使用字面量构建的字符串,他会首先在静态缓冲区内查找有没有相同的字符串对象,如果有会直接返回对应地址,如果没有才会创建一个,因此如果使用字面量创建了两个一样的字符串,两者指向的是同一个字符串对象,因此==比较时返回true

String常用方法

以下都是字符串对象的方法,调用方法为字符串.方法()

charAt(int index),返回指定下标的字符

substring(开始下标,结束下标),截取并返回从开始下标到结束下标前的字符串,不包含结束下标,对原字符串无影响,只填一个参数则默认到结尾

indexOf(参数),返回从左往右查找,参数第一次在字符串中出现的下标

lastIndexOf(参数),与前者作用相同,只不过是从右向左查找

length(),返回字符串的长度

equals(需要比较的字符串),判断两个字符串的内容是否相等,和==区别开

equalsIgnoreCase(字符串),不区分大小写判断字符串内容是否相等

isEmpty(),判断字符串是否是空串

startsWith(参数),判断字符串是否是以参数为开头

endsWith(参数),判断字符串是否是以参数为结尾

contains(参数),判断字符串是否包含参数

concat(参数),将参数拼接到字符串上

replace(被替代的字符,要替换的内容),替换字符串中符合参数一的字符为参数二

replaceAll(关键字或正则,要替换的内容),替换字符串中符合正则的字符为参数二,参数一也可以填字符,此时和replace作用一样

split(截取标志),根据标志截断字符串,返回一个存储切割完字符串的数组

trim(),去除字符串两端空格

getBytes(StandardCharsets.UTF_8),将字符串变成字符数组

toCharArray(),将字符串变成字符数组

toUpperCase(),英文字符转大写

toLowerCase(),英文字符转小写

valueOf(参数),将参数转化为字符串

contentEquals(),和StringBuilder比较时使用

String特性

字符串一旦创建就无法被更改

字符串在编译期间,遇到可以直接运算的内容会先运算,比如String str = "a"+"bcd"在编译期间就会直接拼接在一起以减轻在运行其带来的压力,而非运行时才拼接,因此他可以去缓冲池中寻找,但是如果拼接的内容有变量就不一样了,因为变量在编译阶段无法知道内容因此不会去缓冲池寻找,而是直接创建一个新对象进行存储

可变字符串 StringBuffer

String有着一旦创建就无法更改的特性,因此String的拼接、删除、替换方法本质全都是新建一个字符串对象然后把操作完的字符串存在里面,因此如果出现大量字符串拼接会导致系统卡顿,因为他在不停地创建新对象

常用方法

append(),拼接字符串,返回拼接结果

delete(begin,end),删除字符串,从下标begin开始到下标end结束

replace(begin,end,替换的内容),替换字符串从下标begin到下标end的内容为第三个参数的内容

reverse(),反转字符串

toString(),将Buffer转成字符串

StringBuffer与StringBuilder的区别

StringBuilder线程不安全 速度比较快

StringBuffer线程安全 速度比较慢

StringJoiner

构造方法

new StringJoiner(间隔符号,开始符号,结束符号),可以在创建对象时指定在拼接时的格式,也可以只填间隔符号,则默认不添加开始和结束符号

例如new StringJoiner(",","[","]"),此时通过调用add方法添加元素会自动在每个元素间加,并且在整个拼接字符串前后加上[]

常用方法

add(),拼接字符串,并返回结果

length(),返回长度

toString(),转字符串

System类

常用方法

System.currentTimeMillis()获取从1970年1月1日0时到目前所经过的毫秒数

System.exit(0),结束进程运行,0表示正常等待程序完结退出,1表示强制退出

System.arraycopy(数据源,数据源下标,目标源,目标源下标,复制长度),拷贝数组,要求两者基本数据类型必须相同,如果是引用数据类型,可以子类拷贝给父类

日期Date类

构造日期对象格式Date date = new Date(毫秒数),可以直接创建,参数为空则使用当前时间初始化,参数填入毫秒数,就以从1970年经过毫秒数对应时间初始化

参数也可以以Date(int year, int month, int day)格式填入,但要求Date参数中年份的参数应该是实际需要代表的年份减去1900,实际需要代表的月份减去1以后的值,因此创建格式为Date d1 = new Date(2024-1900, 5-1, 20)

常用方法

getTime(),获取当前时间对象所转化的毫秒数

其余方法已经不推荐了,因为会遇到千年虫问题

Calendar类

Calendar类是一个抽象类,无法直接构造对象,因此可以通过向上转型Calendar cal = new GregorianCalendar();,创建一个格林威治时间初始化的Calender对象

也可以通过静态工厂Calendar cal = Calendar.getInstance()的方式来获取对应的对象,且初始化时间为当前系统时间

常用属性

属性的调用方法为Calendar.属性

YEAR,年份

MONTH,月份,从0开始,范围0~11

WEEK_OF_YEAR,一年当中的第几周,跨年的周显示为1,也就是下一年的第一周

WEEK_OF_MONTH,一个月当中的第几周

DAY_OF_MONTH,一个月当中的第几天

DATE,同上,一个月当中的第几天

DAY_OF_YEAR,一年当中的第几天

DAY_OF_WEEK,一周当中的第几天,也就是星期,需要注意星期天为1

AM_PM,上午下午。上午为1,下午为0

HOUR,12小时制的当前小时

HOUR_OF_DAY,24小时制的当前小时

MINUTE,分钟

SECOND,秒

常用方法

方法的调用方式为Calendar对象.方法()

getTime(),将Calendar类型转为Date类型

setTime(),接受一个Date类型的参数,并将日期设置为Date参数对应的日期,比如cal.setTime(new Date())

getTimeInMillis(),获取时间的毫秒值

setTimeInMillis(long),设置时间的毫秒值

set(属性,参数),设置对应时间属性为参数二的值,比如cal.set (Calendar.HOUR_OF_DAY, 15);,也可以直接设置为set(年, 月, 日, 时, 分, 秒),需要注意月份从0开始

get (属性),获取对应时间属性的值,需要注意月份从0开始

cal.add (属性, 参数);对对应时间属性进行运算,比如cal.add (Calendar.MONTH, 1);表示当前月份加一,cal.add (Calendar.HOUR_OF_DAY, -1);表示当前小时减一

设置时间如果超范围会自动给时间进位

SimpleDateFormat类

以上两种时间对象返回的时间格式都比较晦涩,可以通过本类来自定义时间表示格式

构造格式

SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");,参数字符串为想要的时间格式,要在需要替换为相应年月日的地方填入占位符,y代表年,M代表月,d代表日,h代表小时,m代表分钟,s代表秒

常用方法

format(Date对象),将指定的Date对象转化为自定义格式的字符串并返回

parse(字符串),按指定格式把String转换为Date对象并返回

JDK8新增时间类

JDK8的时间类一旦被构造无法改变,是直接创建新对象返回

ZoneId时区

常用方法

全部是静态方法,直接类名调用

getAvailableZoneIds(),获取Java中所有时区,返回一个set集合

systemDefault(),获取系统默认时区

of(时区),设置时区

Instant时间戳

一旦创建无法被修改,所有时间修改都会创建一个新对象

常用方法

now(),获取当前时间的Instant对象,世界标准时间,静态方法

ofxxx(long xxx),根据参数设置获取对象,静态方法

atZone(),指定时区,非静态方法,对象调用

isxxx(),判断,非静态方法,对象调用

minusxxx(),减少时间

plusxxx(),增加时间

ZoneDateTime时区时间

常用方法

now(),获取当前时间对象

ofxxx(long xxx),获取指定时间对象

withxxx(),修改时间

minusxxx()减少时间

plusxxx(),增加时间

DateTimeFormatter时间格式化

常用方法

ofPattern(String格式),获取格式对象

format(时间对象),将时间对象格式化

LocalDate、LocalTime、LocalDateTime

第一个是年月日,第二个是时分秒,第三个是前两个的结合

常用方法

now(),获取时间对象

of(),获取指定时间对象

getxxx(),获取单独的年月日时分秒信息

isBefore()、isAfter(),比较两个对象

withxxx(),修改时间

minusxxx(),减少时间

plusxxx(),增加时间

toLocalDate()、toLocalTime(),将第三种对象转成前两种

Duration、Period、ChronoUnit时间间隔

前者注重秒、纳秒,中间注重年月日,最后一个是所有单位

Period、Duration常用方法

between(日期对象,日期对象),后者减前者,返回一个时间间隔对象

getxxx(),单独获取时间间隔的年月日时间

toxxxx(),将时间间隔全部转成某个时间单位

ChronoUnit常用方法

ChronoUnit.单位属性.between(日期对象,日期对象),后者减前者,直接返回相应单位的时间间隔

Math类

常用方法

方法调用为Math.方法()

Math.pow(参数, 参数),求次方,参数一为底数,参数二为幂,幂次可以为负,当幂次为0~1时,等于开平方

Math.sqrt(参数),开方,求根号

Math.cbrt(参数),开立方根

Math.ceil(参数),向上取整

Math.floor(参数),向下取整

Math.round(参数),四舍五入

Math.random(),求随机数,[0~1)

Timer类

计时器,构造方式为Timer timer =new Timer();

常用方法

timer.schedule(匿名类,开始时间,间隔时间),参数一为一个匿名类

        new TimerTask() {
            @Override
            public void run() {

            }
        }

参数二为延迟多长时间计时器开始,参数三为间隔多长时间执行一次

BigDecimal类

计算机中的最大存储double在计算时仍有精度丢失,但有时候我们需要在数字计算上展现高精度,比如金融系统,比如银行等,

创建方式

BigDecimal bd=new BigDecimal(“1.0”)

常用方法

add(BigDecimal对象),加法

subtract(BigDecimal对象),减法

multiply(BigDecimal对象),乘法

divide(BigDecimal对象),除法,除不尽无法使用

divide(被除数BigDecimal对象,小数位数,舍入模式),除法重载,ROUND_CEILING向上取整,ROUND_FLOOR向下取整,ROUND_HALF_UP四舍五入

BigInteger类

和上一个常用类一样是用来表示一个大数的,只不过这个类只能表示整数,一旦创建无法改变

创建方法

BigInteger bd=new BigInteger(“1”)

BigInteger bd=BigInteger.value(long)

常用方法

add(BigInteger),加

subtract(BigInteger),减

multiply(BigInteger),乘

divide(BigInteger),除,返回商

divideAndRemainder(BigInteger),除法,返回商和余数,数组形式

equals(BigInteger),比较相等

pow(int),次幂

day2—集合

数组和集合的区别

数组是固定长度的,而集合是可变长的

数组可以存储基本数据类型也可以存储引用数据类型,而集合只能存储引用数据类型

Collection集合体系

Collection接口是整个单列集合体系的根接口,该接口有两个子接口

  • List子接口,继承自Collection接口,元素存取有序,元素可重复,有索引
    • ArrayList,List的实现类,有序表,速度快,线程不安全
    • Vector,和ArrayList类似,线程安全,速度慢
    • LinkedList,List的实现类,链表,增删快
  • Set子接口,继承自Collection接口,元素存取无序,元素不可重复,无索引
    • HashSet,Set的实现类,根据hash值来判断元素是否重复
    • LinkedHashSet,和HashSet类似,但是线程安全
    • TreeSet,Set的实现类,采用树结构存储

Collection接口的方法

boolean add(Object obj),向集合添加一个元素,返回布尔值

boolean addAll(Collection c),合并集合,将另一个集合所有元素加到当前集合中

void clear(),清空此集合内所有元素

boolean contains(Object o),检查集合内是否存在此元素,如果自定义类集合想要使用,则要求自定义类必须重写equals方法

boolean equals(Object o),判断此集合是否和参数对象相等

boolean isEmpty(),判断集合是否为空

boolean remove(Object o),在集合中删除对应元素

int size(),返回当前集合的长度

object[] toArray(),将当前集合转化为数组

集合泛型

我们在使用集合存储时会发现参数直接填写基础数据类型值也不会报错,那是因为JDK1.5之后程序帮我们自动转成了包装类

如果我们这时候存入多种数据类型发现也可以存进集合中,这实际上是因为在集合方法参数中默认采用的是Object类型,而他是所有类的父亲,因此不会报错,但这样就产生了一个问题,就是当我们从集合中取出一个元素的时候会发现元素的类型变成Object类了

List list = new ArrayList();
list.add("hhh");  // 可以存储
list.add(12);  // 也可以存储,实际上等价于list.add(new Integer(12));
String i = list.get(0) // 报错,因为获取到的为Object类型
Object j = list.get(0);

此时就需要我们进行强制类型转换,但这样有可能会出现莫名其妙的转换错误

为了解决这个问题,我们可以使用泛型来唯一确定集合要存储的数据类型,这样在运行前就可以检测出错误,同时方法内也不需要强制类型转换,大大减少了出错的概率

List<Integer> list = new ArrayList<Integer>();
list.add("hhh");  // 报错,因为已经确定集合能存储的数据类型为int的包装类Integer
int j = list.get(1);  // 获取到的元素也已经变成int型了

List集合

List子接口

List接口是继承自Collection接口的,List接口实现的集合都是元素有序,可重复的

List接口方法

除了从Collection接口继承来的那些方法外,List还有一些自己扩展的方法

void add(int index, Object o),在下标处添加元素

boolean addAll(int index, Collection c),合并集合,将另一个集合元素添加到当前集合的下标处

0bject get(int index),返回当前集合中指定位置的

List subList(int fromIndex, int tolndex),返回下标formIndex到下标toIndex之前的元素组成的集合

public int indexOf(Object o),从左往右查找元素第一次出现下标位置并返回

public int lastIndexOf(Object o),从右往左查找元素第一次出现下标位置并返回

public E set(int index, E element),修改指定位置的元素,并返回原来的元素

public E remove(int index),删除指定下标元素,并返回被删除元素

List集合遍历

  • 普通for循环
  • 增强for循环
  • forEach+lambda表达式
  • 迭代器
  • 列表迭代器(List专用),
    • 常用方法add()添加、remove()删除、hasNext()判断后一个元素存不存在、next()获取下一个元素并后移指针、hasPrevious()判断前一个元素存不存在、previous()获取前一个元素并前移指针
    • 遍历过程中需要添加或删除元素必须使用迭代器的方法,不能使用集合方法

ArrayList集合

ArrayList是List集合中用的最多的,也是最重要的,因为是List接口的实现类,因此元素是有序可重复的

他的特点是查询快,但是增删慢,运行效率快,但是线程不安全

构造方式为ArrayList<数据类型> list = new ArrayList<数据类型>();

常用方法

对List接口和Collection接口的方法重写

Vector

和ArrayList几乎一样,唯一的区别就是Vector线程安全,运行效率慢

LinkedList

链表形式,查询速度慢,但是增删操作快,尤其首尾的操作

常用方法

除了对List接口方法的重写还有各种对于首尾的操作也非常常用

public void addFirst(E e),添加元素到集合第一个

public void addLast(E e),添加元素集合最后一个

public E removeFirst(),删除第一个元素并返回

public E removeLast(),删除最后一个元素并返回

public E getFirst(),获取第一个元素

public E getLast(),获取最后一个的元素

Set集合

Set子接口

Set子接口继承自Collection接口,和List不同在于元素无序且不可重复

接口方法

完全继承自Collection

迭代器输出

Set集合因为是无序的因此不存在下标,不能和List一样使用for循环进行遍历输出

有两种方法遍历输出Set集合

  • 需要先通过集合的iterator()方法获取当前集合对象所对应的迭代器对象,然后使用迭代器的方法来进行遍历输出

    • 迭代器方法hashNext(),判断当前指针所指元素是否存在,存在返回true

    • 迭代器方法next(),返回当前元素并将指针下移,初始情况下指针指在第一个元素,如果指针所指没有元素会报NoSuchElementException

    • 迭代器指针不能复位,如果想再次遍历需要新建一个迭代器对象

      HashSet<Integer> set=new HashSet<Integer>();
      set.add(1);
      set.add(2);
      set.add(3);
      Iterator<Integer> it=set.iterator();
      while(it.hasNext()){
          Integer count=it.next();
          System.out.println(count);
      }
      
  • 第二种方法就是采用增强的for循环,格式为for(元素类型 变量名 : 需要遍历的集合),这种方式也可以遍历List集合和数组,底层就是迭代器

  • 第三种是forEach(Consumer接口实现)方法配合lambda表达式来遍历,可以遍历List集合和数组

            list.forEach( s -> System.out.println(s) );
    

HashSet

是基于hashCode方法来实现元素不重复的

底层采用哈希表存储,使用数组、链表、红黑树组成,存储方式先根据哈希来确定数组下标,如果当前位置有元素就使用equals方法判断是同一元素,如果是则不存,如果不是则采取链表形式挂在老元素下方,当链表长度大于8且数组长度大于等于64时,链表就会转变成红黑树

当存入的两个元素哈希码相同时,会调用==或者equals方法来确认,结果为true则拒绝后者

因此如果自己实现的类需要用到Set集合存储,则必须重写hashCodeequals方法,否则Set集合无法实现自动去重

idea中提供了自动重写的功能,在右键菜单选择Generate,然后选择equals()hashCode(),然后一直点击next就会自动根据属性生成重写的方法了

LinkedHashSet

也就是链表实现的HashSet,特性和HashSet一样无索引、不重复,但LinkedHashSet的数据存取顺序是有序的

因为底层虽然也是哈希表存储,但是同时会在元素之间组建一条双向链表,遍历元素是遍历双向链表,而非从数组开始遍历,因此元素是有序的

因为多了双向链表操作因此性能比HashSet弱

TreeSet

底层采用红黑树结构存储,可排序

基于类型的排序规则实现元素不重复,本身实现了SortedSet接口,对集合元素自动排序

存储元素的数据类型要求必须实现Comparable接口来指定排序规则,并且默认是通过实现CompareTo方法来确定排序规则和判断是否为重复元素,返回正数表示当前添加元素大于比较元素应存到其右子树,返回负数表示当前添加元素小于比较元素应存在其左子树,返回0表示当前添加元素和比较元素相同不存储。

如果不想使用默认排序或者无法修改类型的默认排序规则,也可以在创建TreeSet时传入比较器Comparator来实现自定义排序顺序

因此必须重写CompareTo方法,否则TreeSet无法实现元素不重复

day3—Map

Colletions工具类

定义了除存取以外的常用操作,只可以给Colletion相关集合使用

reverse(list),翻转集合列表

shuffle(list),将集合元素乱序

boolean addAll(Collection c,T...args),批量添加元素到集合

copy(list,list),拷贝集合中的元素到另一个集合

binarySearch(list,key) ,以二分查找法查找元素

fill(list,element),使用指定元素填充集合

sort(list),将集合元素根据规则升序或者降序,但是要求集合存储的数据类型必须重写compareTo方法,根据compareTo方法返回值的正负来判断顺序,也可以使用方法重载传入自定义比较器

Comparable接口可以在实现的时候使用泛型来限定compareTo方法的参数类型

自定义比较规则

如果集合存储的数据结构重写的比较规则不符合要求而且无法进行更改,就需要使用自定义比较器,此时使用Collections类对于sort方法的重载,他可以接受两个参数,一个是需要排序的集合,一个是实现了Comparator接口的自定义比较器,这个比较器可以单独定义一个类来实现,但更推荐直接写成匿名类

例如如果要根据字符串的长度来排序,此时可以单独定义一个类来实现Comparator接口中的compare方法,Comparator接口可以使用泛型来限定数据类型,这样compare方法的参数类型也会被限定,在方法内就不需要强制类型转换了

public class StringLength implements Comparator<String> {
    @Override
    public int compare(String o1, String o2) {
        return o2.length() - o1.length();
    }
}

StringLength stringLength = new StringLength();
Collections.sort(arr, stringLength);

也可以直接在参数列表中使用匿名类

Collections.sort(arr, (String o1, String o2) -> o2.length() - o1.length());

sort通过改变比较器参数位置实现升序降序的原理

首先需要知道sort是根据比较器返回的值是正值还是负值来决定元素的前后的

规定当返回正值,说明前一个值比后一个值大,此时两个元素的位置发生移动

规定当返回负值时,说明前一个值比后一个值小,此时两个元素的位置不动

规定当返回0说明两个值相等,也就是元素相同,位置也是不动,HashSet也就是根据这个值来判断元素是否相同的

而只要交换两个减数的顺序就可以改变升序降序的原理也和很简单,假设要比较的是集合中的第一个元素和第二个元素

如果用第一个元素减第二个元素,假设此时为正数,说明第一个元素比第二个元素大,此时交换位置,在集合中就变成了小的第二个元素在前,大的第一个元素在后,而如果结果为负数,说明第一个元素比第二个元素小,此时位置不动,无论如何在整体集合中就是小的第一个元素在前,大的第二个元素在后,元素从小到大,这就是升序

如果用第二个元素减第一个元素,假设此时为正数,说明第二个元素比第一个元素大,此时交换位置,在集合中就变成了大的第二个元素在前,小的第一个元素在后,而如果结果为负数,说明第二个元素比第一个元素小,此时位置不动,无论如何在整体集合中就是大的第一个元素在前,小的第二个元素在后,元素从大到小,这就是降序

实际上sort底层使用的是二分查找+插入排序,比较器的正负决定的是待排元素在有序序列中插入点的前后,与上述原理大致相同的

Map集合

Map不是继承Collection,他和Collection是两个独立的集合体系

Map接口实现的集合的特点是存储的元素是由keyvalue组成的键值对,键key不可重复,值value可重复

Map集合在使用泛型的时候需要填写两个泛型类型,一个是key,一个是value

Map接口有四个实现类

Map集合常用方法

void put(K key,V value),存入keyvalue,如果key相同会进行覆盖并返回被覆盖的值

V remove(key),根据键删除键值对

void clear(),清空Map集合

Boolean isEmpty(),判断集合是否为空

int size(),返回集合长度

bool containsKey(K key),判断参数key包不包含在集合的key键中

bool containsValue(K Value),判断参数Value包不包含在集合的Value值中

Map常用取值方法

单值取值

V get(K key),根据键key获取value

Set<K> keySet(),获取所有key值,返回一个存储key值的Set集合

Collection<V> values(),获取所有的value值,返回一个存储value值的Collection集合,此时可以构造一个ArrayList集合,并将Collection集合作为构造函数的参数传入,这样就转化成了List集合

键值对方式取值

Set<Map.Entry<K,V>> entrySet(),获取key值和value值,此时返回了一个存储了keyvalueEntry<K,V>组成的Set集合,此时的Set集合的数据类型是<Map.Entry<K,V>>,想要遍历每个keyvalue元素就需要先使用增强for循环遍历Set集合,然后使用Entry对象的取值方法来获取keyvalue

Entry常用方法

Entry.getKey(),获取key

Entry.getValue(),获取value

Entry.setValue(),设置value

lambda表达式遍历

使用forEach()方法,传入一个Biconsumer实现类的lambda表达式作为参数

HashMap

元素的存取无序,无索引,元素不能重复

最常用的Map集合,也是最重要的,线程不安全,效率高,允许使用null作为keyvalue,底层是哈希表,也是依赖hashCodeequals保证键的唯一

常用方法全部继承Map

LinkedHashMap

元素的存取有序,无索引,元素不能重复,其余和HashMap无区别

底层和HashMap一样,但是元素间存在一个双向链表

Hashtable

HashMap功能一样,线程安全,效率慢,不允许null作为keyvalue

Properties

Hashtable的子类,常用作配置文件.properties的读取,后面流处理会学,要求keyvalue的数据类型都是String,因此没有泛型

要求.properties文件中的数据要以key=values格式呈现

常用方法

load(文件流对象),根据文件流获取文件

get(“key”),根据key值从.properties文件获取value

TreeMap

本身实现了SortedMap接口(Map的子接口),可以对key自动排序,要求Key需实现Comparable接口或者在创建Map时传入自定义比较器,和TreeSet的要求是一样的,因为TreeSet底层就是TreeMap

可变参数

在参数列表使用数据类型...参数名来表示可变参数,最多只能存在一个可变参数且必须处于参数列表最后

不可变集合

使用of(集合内容)方法获取一个不可变集合,不可变集合不可删除不可添加

Stream流

单列集合可以直接使用stream()方法,双列集合可以先转成单列再使用,数组可以使用Arrays工具类里的stream()方法,零散数据可以使用Stream类的静态方法of(参数)

流中数据改变不改变原数据

中间方法

返回值均为新的Stream流对象,且原来的流只能使用一次,因此推荐使用链式编程

filter(),过滤,参数为Predicate接口实现

limit(),获取前几个元素

skip(),跳过前几个元素

distinct(),去重,依赖equals和hashCode方法

concat(),静态方法,合并a,b流为一个流,数据类型最好一样,否则会使用两者的共同父类

map(),转换流中的数据类型,参数为Function实现类

终结方法

返回值不是Stream对象,一般处于链式编程结尾

forEach(Consumer实现类),遍历,参数是函数式接口实现类

count(),返回元素数量

toArray(intFunction),转数组,默认是Object数组,可以传入intFunction实现类来创建一个指定类型的数组

collect(Collectors),收集数据并放到集合,参数是Collectors工具类,可以转成list、set和map,其中转成map需要提供键和值的规则,需要创建两个Function实现类来书写键和值的转换规则,且键不能重复

方法引用

方法引用就是在原本接受接口实现类的地方传入一个已经写好的方法,要求是

  1. 引用处需要是函数式接口
  2. 被引用的方法已经存在
  3. 被引用方法的形参和返回值需要和抽象方法的形参和返回值一致
  4. 被引用方法需要能满足当前功能

引用类型

引用静态方法

引用形式采取类名::方法名,例如Integer::parseInt

引用成员方法

引用形式采取对象::方法名,父类或者本类可以使用super::方法名this::方法名

引用构造方法

引用形式采取类名::new

类名引用成员方法

引用形式采取类名::成员方法,这种方式不是所有类都可以做到的,只能引用抽象方法第一个参数的相同类型的类

比如抽象方法第一个参数是String类型,则只能引用String的成员方法,相当于抽象方法的第一个参数来调用成员方法

第一个参数对应无参,从第二个参数开始要求抽象方法的形参和引用方法的形参必须相同

引用数组的构造方法

引用形式采取数据类型[]::new

数组类型需要和流中数据类型一致

day4—异常

异常概念

异常和错误不同,错误往往是硬件或者JVM等出现的问题,对于程序员来说无法手动处理

而异常是程序在运行时出现的问题从而导致程序意外中断,他是可以被处理的

当遇到异常时会导致程序直接结束

异常分类

Throwable:所有异常和错误的父类

  • Error:错误,无法被手动处理,例如栈溢出

  • Exception:异常,程序运行时产生的问题,可以被处理

    • RuntimeException:运行时异常,编译期间不提示,可处理可不处理,例如数组下标越界,一般是代码出错
  • CheckedException:检查性异常,必须处理,否则无法编译,比如将一个字符串类型变量赋给了整数型变量,主要是提示注意本地信息

异常处理

异常处理遵循的原则是,能自己处理一定处理,不能处理就使用throw关键字抛向上一级

程序自动抛出

如果程序在遇到不规范的代码或者不合法的操作时会自动抛出一个异常然后终止程序,比如数组下标越界

手动抛出

目的是为了告诉调用者方法出错了

throw

throw关键字用于在当前方法中抛出一个异常

当代码执行到某处后被判断为不应该往下执行时,则可以选择手动抛出一个异常

throw new 具体异常类型("显示异常信息");

需要注意throw是手动选择抛出异常,而非程序真的遇到了不合法操作等问题,比如判断用户输入的数字是否符合条件,不符合则可以抛出一个异常,这个异常是程序员手动抛出的,并非系统操作

throws

throws关键字用于在方法声明中指定该方法可能抛出的异常

public void fun() throws 异常类型 { }

当方法内部抛出指定类型的异常时,该异常会被传递给调用该方法的上级代码,并由该代码决定进行何种操作

throwsthrow是搭配使用的,当方法内有throw且未被捕获时方法必须声明throws来向上抛出异常,且throws声明的异常类型必须和throw一样或范围大于throw

异常捕获

异常捕获的原则是为了程序不会终止运行

try/catch

使用trycatch关键字可以捕获异常,此时程序不会自动结束,try/catch代码块应该放在异常可能发生的地方

使用格式为try{可能发生异常的代码}catch(异常类型 变量名){对异常的处理}

try里的代码发生异常时会先去检查catch,如果此时catch声明的异常类型包含该异常时,程序就会进入catch执行,如果不包括才会抛出异常使程序结束

比如下列代码,当load方法出现IO异常时就会被catch捕获并执行控制台输出语句,而如果出现的不是IO错误则仍然抛出异常

        try {
            db.load(file);
        } catch (IOException e) {
            throw new IOException("文件加载出错");
        }

catch连用

catch关键词可以连续使用,此时异常会从第一个catch开始向后比较,符合条件会进入相应的catch中执行程序

catch连用一般遵循的规范是异常范围从小到大,确保异常一定会被捕获到

try {
    // 可能产生异常的代码
} catch (NumberFormatException e) {
    // 操作
} catch (RuntimeException e) {
    // 操作
} catch (Exception e) {
    // 操作
}

finally

finally需要和try/catch一起使用

finally中的代码无论是否产生异常都会执行

通常会将资源释放性代码放入到finally代码块中,例如关闭流链接、关闭数据库链接等相关操作

try{
    可能发生异常的代码
}catch(异常类型 变量名){
    对异常的处理
}finally{
    资源释放性代码
}

finally和return

就算在try中遇到了return,程序也会运行finally中的代码

    public static void main(String[] args) {
        FinallyDemo2 d = new FinallyDemo2();
        System.out.println(d.f());  // 3
    }

    public int f() {
        try {
            System.out.println("正常");
            return 1;
        } catch (Exception e) {
            return 2;
        } finally {
            return 3;
        }

    }

上例中执行顺序为先遇到return 1程序将1存入返回值缓存区,然后执行finally中的语句,遇到renturn 3将3存到返回值缓存区,然后释放方法占用内存并将返回值3返回

优化

使用try时应该尽量准确定位异常发生位置,不要将大片代码放入try,这样会造成资源浪费

自定义异常

可以创建一个类并继承Exception,此时这个类就变成了一个异常类

自定义异常类型时应确保异常类型名称要语义化

自定义异常可以和throw配合使用

比如声明一个UserOrPasswordException异常,在判断用户名不正确时抛出此异常来提示

if (!username.contains(user.getName())) {
    throw new UserOrPasswordException("用户名错误");
}

常用构造方法

在构造一个异常对象的时候可以选择无参构造方法,此时构造方法会自动生成异常信息

也可以使用含参的构造方法,参数可以通过填入自定义字符串来自定义提示信息

含参的构造方法书写方式为

public UserOrPasswordException(String message) {
    super(message);
}

有throws声明的方法重写

重写子类方法时要确保子类抛出的异常不能比父类更多、更宽

常用异常方法

public void printStackTrace(),打印错误信息

day5—IO流

IO流的分类

按方向

从存储设备读取数据到内存的输入流

从内存写入数据到存储设备的输出流

按功能

分为低级流和高级流

高级流要依附于低级流,高级流之间可以嵌套

按单位

字节流,可以读写所有数据

字符流,只能读写文本数据

文件字节流

属于低级流,一次读取一个字节,主要用于拷贝

FileInputStream输入流

输入流格式为

FileInputStream fin = new FileInputStream("路径"或File对象),如果文件不存在会报错

输入流常用方法

对象.read(),从流中读取数据,一次读取一个字节,返回字节的ASCII码,读到空数据则返回-1

对象.read(byte[] b),从流读取数据,最多读取数组长度个字节,返回当前数组存储的字节长度,数据结尾返回-1

FileOutputStream输出流

输出流格式为

FileOutputStream fo = new FileOutputStream("路径"或File对象),如果文件不存在可以自动创建文件,如果存在会清空已存在文件内容

FileOutputStream fo = new FileOutputStream("路径"或File对象,是否续写),默认false

输出流常用方法

对象.write(int b),将数据写入流中,一次写入一个字节,参数要求int型,内容为字符的ASCII码

对象.write(byte[] b),将字节数组里的内容写入流中

对象.write(byte[] b,int off, int len),将字节数组里指定的内容写入流中,参数二是起始索引,参数三是长度

字节缓冲流

高级流,构造时需要传入低级流对象,依赖于低级流读取文件

因为文件字节流对存储介质的读写频率过高,导致流读写过程异常耗时,通过添加缓冲区使得对于存储介质的读写变成对缓冲区的读写来减少IO频率,而缓冲区因为在内存中因此读写速度增快,并非直接改变读取容量

输入流和输出流的缓冲区并非同一个,缓冲流的读写操作是在两个缓冲区之间操作,底层对文件的读写是缓冲流自己实现的

BufferedInputStream输入流

BufferedInputStream bin = new BufferedInputStream(字节输入流对象)

输入流常用方法

对象.read(),每次读取一个字节,返回int型字节数据,当遇到数据末尾返回-1

对象.read(byte),每次读取大量字节,返回读取字节长度,当遇到数据末尾返回-1

BufferedOutputStream输出流

BufferedOutputStream bo = new BufferedOutputStream(字节输出流对象);

输出流常用方法

对象.write(int i),将参数i写入流中

对象.write(byte,off,len),将字符数组中从off下标处长度len的字符写入流中

转换字符流

将字节流包装起来转变成字符流,因此构造时需要传入字节流对象

以字符为单位读取数据,用来读取文本,底层自带一个长度为8192的字符数组缓冲区,在缓冲区为空时输入流先将数据一次性读取到缓冲区,再在缓冲区中依次读取,输出流是现将数据依次存放在缓冲区,在缓冲区满、刷新或者关流时一次性将缓冲区内容写入文件

InputStreamReader输入流

InputStreamReader reader = new InputStreamReader(字节输入流对象),根据默认编码格式转码,idea默认utf-8

InputStreamReader reader = new InputStreamReader(字节输入流对象, 字符编码),可以指定字符编码,来确定读取字节位数

输入流常用方法

对象.read(),一次读取一个字符,但是返回值是int型,是字符转码后的十进制数据,当读到数据末尾返回-1

对象.read(char[]),一次读取多个字符,返回字符数组长度,读到数据末尾返回-1

OutputStreamWriter输出流

OutputStreamWriter out = new OutputStreamWriter(字节输出流对象)

输出流常用方法

对象.write(char c)将char类型参数c写入流中

对象.write(char[])将char数组写入流中

对象.write(String)将字符串写入流中

文件字符流

继承包装字符流,主要的不同在于构造方法,不需要传入一个字节流对象,其余方法均使用父类方法

FileReader输入流

InputStreamReader的子类

构造方法为new FileReader(路径或File对象),或者new FileReader(路径或File对象,字符编码)指定字符编码

FileWriter输出流

OutputStreamWriter的子类

构造方法为new FileWriter(路径或File对象),或者new FileWriter(路径或File对象,是否续写)默认为false,或者new FileWriter(路径或File对象,字符编码)

字符缓冲流

基本字符流已经自带8192的缓冲区,因此缓冲流在速度的提升上并不大,最重要的是两个读写方法

BufferedReader输入流

BufferedReader reader = new BufferedReader(字符输入流对象)

输入流常用方法

对象.readLine(),一次读取一行字符,返回值是String类型,到数据末尾返回null

BufferedWriter输出流

BufferedWriter bw = new BufferedWriter(字符输入流对象)

需要续写则需要传入开启续写的字符输入流对象

输出流常用方法

对象.write(String),将字符串写入文件

对象.newLine(),打印换行

PrintWriter打印流

字符打印流有缓冲区,可以自己选择开启是否自动刷新

PrintWriter writer = new PrintWriter(字符输出流对象/文件路径/file)

PrintWriter writer = new PrintWriter(路径,编码)

PrintWriter writer = new PrintWriter(路径,是否自动刷新)

输出流常用方法

对象.write(),普通打印

对象.println(String str),原样输出字符串内容,System.out.println()实际上也是在方法内构造了PrintWriter,然后使用其println()方法

对象.print(),远洋输入内容,不自动换行

对象.printf(),带占位符的打印

高级流的嵌套

比如BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("路径")));

可以通过嵌套来增强流操作的功能性

对象流/序列化流

顾名思义,就是读写对象的流操作,通常用于钝化、激活

序列化接口

对象流写入对象类要求必须实现序列化Serializable接口,这样才能把对象拆行字段然后存到文件中,即序列化

在类实现序列化接口的同时应该使用private static final long serialVersionUID = 12345678L来表示版本号

这样做的好处是,当写入对象后,如果类中字段发生变化,比如多了一个属性,在读取对象时如果没有标识会导致版本对不上而发生反序列化异常

如果添加了serialVersionUID,则不会发生异常,仅仅是新增字段初始化为默认值

如果在序列化时不希望某个属性被序列化可以在修饰符后加transient

ObjectInputStream输入流/反序列化流

ObjectInputStream oin =new ObjectInputStream(字节输入流对象);

输入流常用方法

对象.readObject(),读取对象,获取的是Object类型,需要强制转换成原来的数据类型,如果读取到空数据会报错

ObjectOutputStream输出流/序列化流

ObjectOutputStream oot=new ObjectOutputStream(字节输出流对象)

输出流常用方法

对象.writeObject(Object o),将对象写入流中

解压流

ZipInputStream zip = new ZipInputStream(字节流输入对象)

常用方法

对象.getNextEntry(),获取压缩包里的下一个文件或文件夹,可以自动遍历多级文件夹,返回一个ZipEntry对象表示文件或文件夹的路径

entry对象.isDirectory(),判断是否是文件夹

entry对象.toString(),将路径转成字符串

对象.read(),读取文件内容,和字节流一样

压缩流

ZipOutputStream zip = new ZipOutputStream(字节流输出对象)

创建ZipEntry对象ZipEntry entry = new ZipEntry(文件名),此时ZipEntry对象内没有数据,需要使用输入流将文件内容手动拷贝进去

常用方法

对象.putNextEntry(ZipEntry对象),将ZipEntry对象写入压缩流

对象.closeEntry(),关闭ZipEntry流

关闭流

为了节省资源,在不需要流操作时将流关闭

流对象.close()

File类

不属于流,代表文件或者文件夹的路径,文件或文件夹可以存在也可以不存在

通过File类可以直接操作文件或文件夹

构造方法

File file=new File("需要操作的文件路径或者文件夹路径")

常用方法

createNewFile(),创建一个新文件

mkdir(),创建一个新文件夹

mkdirs(),创建多级文件夹

delete(),删除文件或者文件夹

exists(),判断File对象对应的文件或者文件夹是否存在

getAbsolutePath(),获取文件的绝对路径

getPath(),获取定义文件时的路径

getName(),去掉路径只获取文件的名字,带路径

lastModified(),获取最后修改时间

getParent(),去掉文件或者文件夹名字,只保留前面路径

isDirectory(),判断是否是文件夹

isFile(),判断是否是文件

length(),获取文件的大小,字节表示

listFiles(),返回包含文件夹中所有内容的File数组

renameTo(),修改文件名为

递归删除目录

因为只有文件夹为空的时候可以删除,因此就需要从最深层向外一层一层删除

    public static boolean f(File file) {
        File[] files = file.listFiles();
        if (files != null) {
            for (File fi : files) {
                f(fi);
            }
        }
        return file.delete();
    }

FileFilter过滤接口

FileFilter接口只有一个抽象方法boolean accept(文件路径)

File对象使用listFiles()方法的时候支持在参数列表传入一个FileFilter接口的实现匿名类以过滤不符合accept方法的文件或文件夹

Commons-io工具包

FileUtils类

copyFile(src,dest),复制文件

copyDirectory(File,File),复制文件夹内容到目的文件夹

copyDirectoryToDirectory(File,File),复制文件夹到目的地文件夹里

deleteDirectory(File),删除文件夹

cleanDirectory(File),清空文件夹

IOUtils类

copy(input,output),复制文件

copyLarge(Reader,Writer),复制大文件

HuTool包

参考文档