Java核心类库(上)

文章目录

Java核心类库(上)

常用类的概述和使用

API的使用和常用包的概述

API的使用

Module(模块,如java.base模块)—>Package(包:里面有接口、类、枚举类、异常、错误等)

—>Class(类)

常用的包(熟悉)
包的名称和功能
  • java.lang包 - 该包是Java语言的核心包,并且该包中的所有内容由Java虚拟机自动导入(因此都不用导包)

    如:System类、String类、…

  • java.util包 - 该包是Java语言的工具包,里面提供了大量工具类以及集合类等

    如:Scanner类、Random类、List集合、…

  • java.io包 - 该包是Java语言中的输入输出包,里面提供了大量读写文件相关的类等

    如:FileInputStream类、FileOutputStream类、…

  • java.net包 - 该包是Java语言中的网络包,里面提供了大量网络编程相关的类等

    如:ServerSocket类、Socket类、…

  • java.sql包 - 该包是Java语言中的数据包、里面提供了大量操作数据库的类和接口等

    如:DriverManager类、Connection接口、…

  • … …

  • Java程序员在编程时可以使用大量类库,因此Java编程时需要记的很多,对编程能力本身要求不是特别的高

Object类的概述(重点)

基本概念
  • java.lang.Object类是Java语言中类层次结构的根类,也就是说任何一个类都是该类得直接或者间接子类
  • 若定义一个Java类时没有使用extends关键字声明其父类,则其父类默认为java.lang.Object类
  • Object类定义了“对象”的基本行为,被子类默认继承
常用的方法
方法声明功能介绍
Object()使用无参方式构造对象
boolean equals(Object obj)用于判断调用对象是否与参数对象相等
该方法默认比较两个对象的地址是否相等,与==运算符的结果一致
若希望比较两个对象的内容,则需要重写该方法
若该方法被重写后,则应该重写hashCode()方法来保持结果的一致性
int hashCode()用于获取调用对象的哈希码值(内存地址的编号)
若两个对象调用equals()方法相等,则各自调用该方法的结果必然相同
若两个调用对象equals()方法不相等,则各自调用该方法的结果应该不相同

为了使得该方法与equals()方法保持一致,需要重写该方法
Stirng toString()用于获取调用对象的字符串形式
该方法默认返回的字符串为:包名.类名@哈希码值的十六进制
为了返回更有意义的数据(我们想要的输出),需要重写该方法
使用print()或println()打印引用或字符串拼接引用都会自动调用该方法
Class<?> getClass()用于返回调用对象执行时的Class实例,反射机制使用
equals()方法和hashCode()方法
  • equals()方法的重写实现

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;   //当调用对象和参数对象指向同一个对象时,则内容一定相同
        if (obj == null || getClass() != obj.getClass()) return false;  //当参数为空时,则内容一定不相同(因为若调用的对象为null,会报空指针异常错误)
    	if (obj instanceof Student) {   //判断obj指向的对象是否为Student类型的对象,若是则条件成立,否则条件不成立
        	Student s = (Student) obj;  //需要将obj强转为子类对象,才能在后面调用子类中独有的方法
            return this.getId() == s.getId();
            /*
            //若是以姓名(字符串类型)作为判断基准,equals()方法重写实现如下
            return this.getName().equals(s.getName());
            */
        }
        return false;   //若类型不一致则没有可比性,则内容一定不相等
    }
    
  • hashCode()方法的重写实现

    /**
    * 重写hashCode()方法
    */
    @Override
    public int hashCode() {
    	/*
        由于equals()方法是用id值来判断是否相等,而hashCode()方法依据equals()方法结果实现,
        因此这里直接返回getId(),若id相同,equals()方法为true,hashCode()方法最后返回值也相同了
         */
        final int result = 12;
        return result * 31 + getId();
        /*
        //若是以姓名(字符串类型)作为判断基准,hashCode()方法重写实现如下
        return result * 31 + getName().hashCode();
        */
    }
    
toString()方法
  • toString()方法的重写实现

    /**
    * 重写toString()方法,为了返回更有意义的数据,需要重写该方法
    */
    @Override
    public String toString() {
    	return "学生id=" + getId() + ",学生name=" + getName();
    }
    

包装类(熟悉)

包装类的概念

通常情况下基本数据类型的变量不是对象,为了满足万物皆对象的理念就需要对基本数据类型的变量进行打包封装处理变成对象,而负责将这些变量声明为成员变量进行对象化处理的相关类,叫做包装类

包装类的分类
包装类对应的基本数据类型
java.lang.Bytebyte
java.lang.Shortshort
java.lang.Integerint
java.lang.Longlong
java.lang.Floatfloat
java.lang.Doubledouble
java.lang.Booleanboolean
java.lang.Characterchar
Integer类概述
基本概念

java.lang.Integer类内部包装了一个int类型的变量作为成员变量,主要用于实现对int类型的包装并提供int类型到String类型之间的转换等方法

常用的常量
常量类型和名称功能介绍
public static final int MAX_VALUE表示int类型可以描述的最大值,即2^31-1
public static final int MIN_VALUE表示int类型可以描述的最小是,即-2^31
public static final int SIZE表示int类型采用二进制补码形式的位数
public static final int BYTES表示int类型所占的字节个数
public static final Class TYPE表示int类型的Class实例
常用的方法
方法声明功能介绍
Integer(int value)根据参数指定的整数来构造对象(已过时,使用valueOf(int i)代替实现)
Integer(String s)根据参数指定的字符串来构造对象(已过时,使用valueOf(String s)代替实现)
int intValue()Returns the value of this Integer as an int
static Integer valueOf(int i)Returns an Integer instance representing the specified int value
static Integer valueOf(String s)Returns an Integer object holding the value of the specified String
boolean equals(Object obj)比较调用对象与参数指定的对象是否相等(已重写)
String toString()返回描述调用对象数值的字符串形式(已重写)
static int paseInt(String s)将字符串s转换为int类型并返回
static String toString(int i)获取参数指定整数的十进制字符串形式
static String toBinaryString(int i)获取参数指定整数的二进制字符串形式
static String toHexString(int i)获取参数指定整数的十六进制字符串形式
static String toOctalString(int i)获取参数指定整数的八进制字符串形式
装箱和拆箱的概念

在Java5发布之前使用包装类对象进行运算时,需要较为繁琐的“拆箱”和“装箱”操作;即运算前先将包装类对象拆分为基本类型数据,运算后再将结果封装成包装类对象

从Java5开始增加了自动拆箱和自动装箱的功能

//装箱与拆箱
//装箱
Integer it1 = Integer.valueOf(100); //将100装箱为一个Integer对象
//拆箱
int i = it1.intValue();	//将it1拆箱为一个int基本类型

//自动装箱与自动拆箱
Integer it2 = 100;  //直接将基本数据类型的100自动装箱为Integer类型
int i1 = it2;   //直接将Integer类型对象it2自动拆箱为基本数据类型int
自动装箱池

在Integer类的内部提供了自动装箱池技术,将**-128到127之间**的整数已经装箱完毕,当程序中使用该范围之间的整数时,无需装箱直接取用自动装箱池中的对象即可,从而提高效率

//装箱和拆箱的笔试考点:自动装箱池
//1、创建的Integer对象不是自动装箱池中的整数时-->创建对象后比较
Integer integer1 = 128;
Integer integer2 = 128;
Integer integer3 = new Integer(128);
Integer integer4 = new Integer(128);
System.out.println(integer1 == integer2);   //比较地址:false
System.out.println(integer1.equals(integer2));  //比较内容:true
System.out.println(integer3 == integer4);   //比较地址:false
System.out.println(integer3.equals(integer4));  //比较内容:true
//2、创建的Integer对象是自动装箱池中的整数时-->无需创建,直接从自动装箱池中拿来使用
Integer int1 = 127;
Integer int2 = 127;
Integer int3 = new Integer(127);
Integer int4 = new Integer(127);
System.out.println(int1 == int2);   //比较地址:由于127是自动装箱池范围内整数,因此int1和int2实际是从自动装箱池中取出的同一个对象,因此为true
System.out.println(int1.equals(int2));  //比较内容:true
System.out.println(int3 == int4);   //比较地址:这里不是自动装箱,和自动装箱池无关,因此两个不同对象地址不同,为false
System.out.println(int3.equals(int4));  //比较内容:true
Double类概述
基本概念

java.lang.Double类型内部包装了一个double类型的变量作为成员变量,主要用于实现对double类型的包装并提供double类型到String类之间的转换等方法

常用的常量
常用类型和名称功能介绍
public static final int SIZE表示double类型的二进制位数
public static final int BYTES表示double类型的字节个数
public static final Class TYPE表示double类型的Class实例
常用的方法
方法声明功能介绍
Double(double value)根据参数指定的浮点数据来构造对象(已过时)
Double(String s)根据参数指定的字符串来构造对象(已过时)
double doubleValue()获取调用对象中的浮点数据并返回
static Double valueOf(double d)根据参数指定浮点数据得到Double类型对象
boolean equals(Object obj)比较调用对象与参数指定的对象是否相等
String toString()返回描述调用对象数值的字符串形式
static double parseDouble(String s)将字符串类型转换为double类型并返回
boolean isNaN()判断调用对象的数值是否为非数字
扩展

java.lang.Number类是个抽象类,是上述类的父类来描述所有类共有的成员

Boolean类概述
基本概念

java.lang.Boolean类型内部包装了一个boolean类型的变量作为成员变量,主要用于实现对boolean类型的包装并提供boolean类型到String类之间的转换等方法

常用的常量
常量类型和名称功能介绍
public static final Boolean FALSE对应基值为false的对象
public static final Boolean TRUE对应基值为true的对象
public static final Class TYPE表示boolean类型的Class实例
常用的方法
方法声明功能介绍
Boolean(boolean value)根据参数执行的布尔数值来构造对象(已过时)
Boolean(String s)根据参数指定的字符串来构造对象(已过时)
boolean booleanValue()获取调用对象中的布尔数值并返回
static Boolean valueOf(boolean b)根据参数指定的布尔数值得到Boolean类型对象
boolean equals(Object obj)比较调用对象与参数指定的对象是否相等
String toString()返回描述调用对象数值的字符串形式
static boolean parseBoolean(String s)将字符串类型转换为boolean类型并返回
只要参数字符串不是true或TRUE时,则结果就是false
Character类概述
基本概念

java.lang.Character类型内部包装了一个char类型的变量作为成员变量,主要用于实现对char类型的包装并提供字符类别的判断和转换等方法

常用的常量
常量类型和名称功能介绍
public static final int SIZE表示char类型的二进制位数
public static final int BYTES表示char类型的字节个数
public static final Class TYPE表示char类型的Class实例
常用的方法
方法声明功能介绍
Character(char value)根据参数指定的字符数据来构造对象(已过时)
char charValue()获取调用对象中的字符数据并返回
static Character valueOf(char c)根据参数指定字符数据得到Character类型对象
boolean equals(Object obj)比较调用对象与参数指定的对象是否相等
String toString()返回描述调用对象数值的字符串形式
static boolean isUpperCase(char ch)判断参数指定字符是否为大写字符
static boolean isLowerCase(char ch)判断参数指定字符是否为小写字符
static boolean isDigit(char ch)判断参数指定字符是否为数字字符
static char toUpperCase(char ch)将参数指定的字符转换为大写字符
static char toLowerCase(char ch)将参数指定的字符转换为小写字符
包装类(Wrapper)的使用总结
  • 基本数据类型转换为对应包装类的方式

    调用包装类的构造方法(已过时)或者静态方法即可

  • 获取包装类对象中基本数据类型变量数值的方式

    调用包装类中的xxxValue(基本数据类型变量)方法即可

  • 字符串转换为基本数据类型的方式

    调用包装类中的paseXxx(String s)方法即可

数学处理类(熟悉)

Math类概述
基本概念

java.lang.Math类主要用于提供执行数学运算的方法,如:对数、平方根

常用的方法(都是静态的)
方法声明功能介绍
static int max(int a, int b)返回两个参数中的最大值
static int min(int a, int b)返回两个参数中的最小值
static double pow(double a, double b)返回第一个参数的幂
static int abs(int a)返回参数指定数值的绝对值
static long round(double a)返回参数四舍五入的结果
static double sqrt(double a)返回参数的平方根
static double random()返回0.0到1.0的随机数
(Random类可直接指定产生范围之间的随机数)
BigDecimal类概述
基本概念

由于float类型和double类型在运算时可能会有误差,若希望实现精确运算则借助java.math.BigDecimal类型加以描述

常用方法
方法声明功能介绍
BigDecimal(String val)根据指定参数指定的字符串来构造对象
BigDecimal add(BigDecimal augend)加法:当前调用对象 + 参数对象
BigDecimal subtract(BigDecimal subtrahend)减法:当前调用对象 - 参数对象
BigDecimal multiply(BigDecimal multiplicand)乘法:当前调用对象 * 参数对象
BigDecimal divide(BigDecimal divisor)除法:当前调用对象 / 参数对象
注意事项

若用BigDecimal进行除法运算商的结果是无理数,则此时会有异常java.lang.ArithmeticException
解决办法:第二个参数中传入RoundingMode的静态成员变量(如RoundingMode.HALF_UP表示四舍五入)

public class BigDecimalTest {
    public static void main(String[] args) {
        System.out.println(0.2 + 0.1);  //输出0.30000000000000004
        //1、实现精确运算
        BigDecimal bigDecimal1 = new BigDecimal("0.2");
        BigDecimal bigDecimal2 = new BigDecimal("0.1");
        System.out.println("精确结果是:" + bigDecimal1.add(bigDecimal2));   //输出0.3-->精确!
        System.out.println(bigDecimal1.subtract(bigDecimal2));  //实现减法运算
        System.out.println(bigDecimal1.multiply(bigDecimal2));  //实现乘法运算
        System.out.println(bigDecimal1.divide(bigDecimal2));    //实现除法运算(精确运算,必然保留小数)

        //2、注意事项:商是无理数时会报异常ArithmeticException
        BigDecimal bigDecimal3 = new BigDecimal("2");
        BigDecimal bigDecimal4 = new BigDecimal("0.3");
//      System.out.println(bigDecimal3.divide(bigDecimal4));    //java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result
        System.out.println(bigDecimal3.divide(bigDecimal4, RoundingMode.HALF_UP));  //RoundingMode.HALF_UP表示四舍五入
    }
}
BigInteger类概述
基本概念

若希望表示比long类型范围还大的整数数据,则需要借助java.math.BigInteger类型描述

常用方法
方法声明功能介绍
BigInteger(String val)根据参数指定的字符串来构造对象
BigInteger add(BigInteger val)加法
BigInteger subtract(BigInteger val)减法
BigInteger multiply(BigInteger val)乘法
BigInteger divide(BigInteger val)除法
BigInteger ramainder(BigInteger val)取余
BigInteger[] divideAndRemainder(BigInteger val)取商和取余

任务总结

  1. 常用的包(熟悉)

    java.lang、java.util、java.io、java.net、java.sql、…

  2. Object类(重点)

    概念、equals()、hashCode()、toString()、…

  3. 包装类(熟悉)

    概念、Integer类、Double类、Boolean类、Character类、…

  4. 常用的数学处理类(熟悉)

    Math类、BigDecimal类、BigInteger类、…

String类的概述和使用

String类的概念(重点)

  • java.lang.String类用于描述字符串,Java程序中所有的字符串字面值都可以使用该类的对象加以描述,如:“abc”

  • 该类由final关键字修饰,表示该类不能被继承

  • 从jdk1.9开始该类的底层不使用char[]来存储数据,而是改成byte[]加上编码标记,从而节约了一些空间

  • 到目前为止,只有Stirng这个特殊类除了new的方式外还可以直接=字符串赋值(包装类除外,因为有自动装箱)

  • 该类描述的字符串内容是个常量不可更改,因此可以被共享使用

    如:

    Strintg str1 = “abc”; - 其中"abc"这个字符串是个常量不可改变(由于数组底层是byte[]数组,而byte[]数组不可以改变长度,因此String也不可改变

    str1 = “123”; - 将"123"字符串的地址赋值给变量str1;改变str1的指向并没有改变指向的内容

01 String类型引用的指向改变

常量池的概念(原理)

由于String类型描述的字符串内容是常量不可改变,因此Java虚拟机将首次出现的字符串放入常量池中,若后续代码中出现了相同字符串内容则直接使用池中已有的字符串而无需申请内存及创建对象,从而提高了性能

//验证String中常量池的存在
String str1 = "123";
String str2 = "123";
System.out.println(str1 == str2);   //输出true:常量池是存在的

常用的构造方法(练熟、记住)

方法声明功能介绍
String()使用无参方式构造对象得到空字符序列
String(byte[] bytes, int offset, int length)使用bytes数组中下标从offset位置开始的length个字节来构造对象
String(byte[] bytes)使用bytes数组中的所有内容构造对象
String(char[] value, int offset, int count)使用value数组中下标从offset位置开始的count个字符来构造对象
String(char[] value)使用value数组中的所有内容构造对象
String(String original)根据参数指定的字符串内容来构造对象,新创建对象为参数对象的副本
常见的笔试考点
  • 请问下面的代码会创建几个对象?分别存放在什么地方?

    //1、请问下面的代码会创建几个对象?分别存放在什么地方?
    String str1 = "hello";  //创建1个对象,存放在常量池中
    String str = new String("hello");   //创建2个对象,一个参数中的"hello"存放在常量池,一个str存放在堆区中
    
  • 常量池和堆区对象的比较

    //2、常量池和堆区对象的比较
    String string1 = "hello";   //常量池
    String string2 = "hello";   //常量池
    String string3 = new String("hello");   //堆区
    String string4 = new String("hello");   //堆区
    System.out.println(string1 == string2);     //比较地址:true(都是常量池中同一个对象)
    System.out.println(string1.equals(string2));    //比较内容:true(equals已重写)
    System.out.println(string3 == string4);     //比较地址:false(不同对象堆区中不同地址)
    System.out.println(string3.equals(string4));    //比较内容:true(equals已重写)
    System.out.println(string1 == string3);     //比较地址:false(一个是常量池中,一个是堆区中)
    System.out.println(string1.equals(string3));    //比较内容:true(equals已重写)
    
  • 常量有优化机制,变量没有

    //3、常量有优化机制,变量没有
    String s1 = "abcd";
    String s2 = "ab" + "cd";    //常量优化机制-->"abcd"
    System.out.println(s1 == s2);   //比较地址:true
    
    String s3 = "ab";
    String s4 = s3 + "cd";
    System.out.println(s4 == s1);   //比较地址:false
    

常用的成员方法(练熟、记住)

方法声明功能介绍
String toString()返回字符串本身
byte[] getBytes()将当前字符串内容转换为byte数组并返回
char[] toCharArray()将当前字符串内容转换为char数组并返回
方法声明功能介绍
char charAt(int index)返回字符串指定位置index的字符
int length()返回字符串字符序列的长度
boolean isEmpty()判断字符串是否为空
  • 案例题目

    1. 使用两种方式实现字符串“123456”转换为整数123456并打印

      //1、使用两种方式实现字符串“123456”转换为整数123456并打印
      String str = new String("123456");
      //方式一:调用Integer类中的paseInt()方法即可
      int i = Integer.parseInt(str);
      System.out.println(i);
      //方式二:利用ASCII码来实现类型转换并打印
      //'1' - '0' = 1, '2' - '0' = 2, ...
      int temp = 0;
      for (int j = 0; j < str.length(); j++) {
          temp = temp * 10 + (str.charAt(j) - '0');java
      }
      System.out.println(temp);
      
    2. 判断字符串“上海自来水来自海上”是否为回文并打印,所谓回文是指一个字符序列无论从左往右读还是从右往左读都是相同的句子

      //1、先创建一个字符串
      String string = new String("上海自来水来自海上");
      //2、循环,前后对应位置一样,循环直至中间字符则是回文数;否则不是回文数
      boolean flag = true;
      for (int i = 0; i < string.length() / 2; i++) {
          if (string.charAt(i) != string.charAt(string.length() - 1 - i)) {
              //找到不对等字符,则不是回文数
              flag = false;
          }
      }
      if (flag) {
          System.out.println("\"" + string + "\"是回文数");
      } else {
          System.out.println("\"" + string + "\"不是回文数");
      }
      
方法声明功能介绍
int compareTo(String anotherString)用于比较调用对象和参数对象的大小关系
int compareToIgnoreCase(String str)不考虑大小写地比较
  • 字符串之间大小如何进行比较参考以下代码:

    //1、构造String类型的对象并打印
    String string = new String("hello");
    System.out.println(string);
    
    //2、使用构造好的对象与其他字符串对象之间比较大小并打印
    System.out.println(string.compareTo("world"));  //'h' - 'w' => 104 - 119 = -15
    System.out.println(string.compareTo("haha"));   //'e' - 'a' => 101 - 97 = 4
    System.out.println(string.compareTo("hehe"));   //'l' - 'h' => 108 - 104 = 4
    System.out.println(string.compareTo("heihei")); //'l' - 'i' => 108 - 105 = 3
    System.out.println(string.compareTo("helloworld")); //长度:5 - 10 = -5
    System.out.println(string.compareToIgnoreCase("HELLO"));    //0
    
方法声明功能介绍
String concat(String str)用于实现字符串的拼接(一般直接用+拼接)
boolean contains(CharSequence s)用于判断当前字符串是否包含指定参数指定的内容
(区分大小写地判断)
String toLowerCase()返回字符串的小写形式
String toUpperCase()返回字符串的大写形式
String trim()返回去掉最前和最后空白之后的字符串
boolean starsWith(String prefix)判读字符串是否以参数字符串开头(区分大小写地判断)
boolean starsWith(String prefix, int toffset)从指定位置offset开始是否以参数字符串perfix开头
(区分大小写地判断)
boolean endsWith(String suffix)判断字符串是否以参数字符串结尾
  • 案例题目:编程实现上述方法的使用

    String string1 = new String("   Let Me Give You Some Color To See See");
    System.out.println("string1 = " + string1);
    
    boolean isContain = string1.contains("Let");    //判断是否包含对应字符串
    System.out.println("是否包含Let:" + isContain); //true
    boolean isContain1 = string1.contains("let");
    System.out.println("是否包含let:" + isContain1);    //false,区分大小写
    
    String lowerString = string1.toLowerCase(); //将字符串中所有大写字母全部转换为小写字母
    System.out.println("转换成小写后:" + lowerString);    //转换成小写后:   let me give you some color to see see
    String upperString = string1.toUpperCase(); //将字符串中所有小写字母全部转换为大写字母
    System.out.println("转换成大写后:" + upperString);    //转换成大写后:   LET ME GIVE YOU SOME COLOR TO SEE SEE
    
    String trim = string1.trim();   //去除字符串两端的空白字符
    System.out.println("去除两端空白后:" + trim);  //去除两端空白后:Let Me Give You Some Color To See See
    
    boolean start = string1.startsWith("Let");  //判断字符串是否以参数字符串开头
    System.out.println("是否以Let开头:" + start);    //false
    start = string1.startsWith("let", 3);   //判断指定位置开始是否以参数字符串开头:区分大小写
    System.out.println("从下标3开始是否以let开头:" + start);  //false
    start = string1.startsWith("Let",3);
    System.out.println("从下标3开始是否以Let开头:" + start);  //true
    
    boolean ends = string1.endsWith("see"); //判断是否以参数字符串结尾:区分大小写
    System.out.println("string1字符串是否以see结尾:" + ends);   //false
    ends = string1.endsWith("See");
    System.out.println("string1字符串是否以See结尾:" + ends);   //true
    
方法声明功能介绍
boolean equals(Object anObject)用于比较字符串内容是否相等并返回
int hashCode()获取调用对象的哈希码值
boolean equalsIgnoreCase(String anotherString)用于比较字符串内容是否相等并返回,不考虑大小写
  • 案例题目

    提示用户从键盘输入用户名和密码信息,若输入“admin”和“123456”则提示“登录成功,欢迎使用”,否则提示“用户名或密码错误,您还有n次机会”,若用户输入三次后依然错误则提示“账户已冻结,请联系人员!”

    public static void main(String[] args) {
        for (int i = 3; i > 0; i--) {
            //1、提示用户输入用户名和密码并用变量存储
            Scanner scanner = new Scanner(System.in);
            System.out.print("请输入用户名:");
            String userName = scanner.next();
            System.out.print("请输入密码:");
            String password = scanner.next();
    
            //2、判断用户名和密码是否为"admin"和"123456"并给出对应提示
            if ("admin".equals(userName) && "123456".equals(password)) {	//字符串常量放前面是为了避免空指针异常
                System.out.println("登录成功,欢迎使用");
                break;
            }
            if (1 == i) {
                System.out.println("账户已冻结,请联系人员");
            } else {
                System.out.println("用户名或密码错误,您还有" + (i - 1) + "次机会");
            }
        }
    }
    
方法声明功能介绍
int indexOf(int ch)用于返回当前字符串中参数ch指定的字符第一次出现的下标
没有找到返回-1
int indexOf(int ch, int fromIndex)从fromIndex位置开始(包含fromIndex位置)查找ch字符第一次出现的下标
没有找到返回-1
int indexOf(String str)在字符串中检索str返回其第一次出现的位置,若找不到返回-1
int indexOf(String str, int fromIndex)从字符串的fromIndex位置开始检索str第一次出现的位置下标
若找不到返回-1
int lastIndexOf(int ch)用于返回参数ch指定的字符最后一次出现的下标
int lastIndexOf(int ch, int fromIndex)从fromIndex位置开始反向查找ch指定字符第一次出现的下标
int lastIndexOf(String str)返回str指定字符串最后一次出现的下标
int lastIndexOf(String str, int fromIndex)从fromIndex位置开始反向查找str第一次出现的下标
  • 案例题目

    编写通用的代码可以查询字符串“Good Good Study, Day Day Up!”中所有“Day”出现的索引位置并打印出来

    String string = new String("Good Good Study, Day Day Up!");
    //编写通用的代码可以查询字符串“Good Good Study, Day Day Up!”中所有“Day”出现的索引位置并打印出来
    int pos = string.indexOf("Day");    //查找到的索引位置
    while (-1 != pos) {
        System.out.println("pos" + pos);
        pos = string.indexOf("Day", pos + 1);
    }
    System.out.println("------------优化代码-------------------");
    while ((pos = string.indexOf("Day", pos)) != -1) {
        System.out.println("pos=" + pos);
        pos += "Day".length();
    }
    
方法声明功能介绍
String substring(int beginIndex, int endIndex)返回字符串中从下标beginIndex(包括)开始到endIndex(不包括)结束的子字符串(包头不包尾)
String substring(int beginIndex)返回字符串中从下标beginIndex(包括)开始到字符串结尾的子字符串
  • 提示用户从键盘输入一个字符串和一个字符,输出该字符(不含)后面的所有子字符串

    //提示用户从键盘输入一个字符串和一个字符,输出该字符(不含)后面的所有子字符串
    //1、提示用户输入一个字符串和一个字符
    System.out.println("请输入一个字符串:");
    Scanner scanner = new Scanner(System.in);
    String string = scanner.nextLine(); //next()函数遇到空格会停止输入,nextLine()可以输入包含空格的完整一行
    System.out.println("请输入一个字符:");
    String character = scanner.nextLine();
    //2、获取该字符在字符串中所在位置
    int pos = string.indexOf(character);
    //3、截取该字符所在位置开始(不含)往后的所有子字符串并输出
    String substring = string.substring(pos + 1);
    System.out.println(substring);
    

正则表达式

正则表达式的概念(了解)

正则表达式本质上就是一个“规则字符串”,可以用于对字符串数据的格式进行验证,以及匹配、查找、替换等操作。该字符串通常使用^运算符作为开头标志,使用$运算符作为结尾标志,当然也可以省略

正则表达式的规则(了解)
正则表达式说明
[abc]可以出现a、b、c中任意一个字符
[^abc]可以出现任何字符,除了a、b、c的任意字符
[a-z]可以出现a、b、c、…、z中的任意一个字符
[a-zA-Z0-9]可以出现a-z、A-Z、0-9中任意一个字符
正则表达式说明
.任意一个字符(通常不包含换行符)
\d任意一个数字字符,相当于[0-9]
\D任意一个非数字字符
\s空白字符,相当于[\t\n\x0B\f\r]
\S非空白字符
\w任意一个单词字符,相当于[a-zA-Z0-9]
\W任意一个非单词字符
正则表达式说明
X?表示X可以出现一次或一次也没有,也就是0~1次
X*表示X可以出现零次或多次,也就是0~n次
X+表示X可以出现一次或多次,也就是1~n次
X{n}表示X可以恰好出现n次
X{n,}表示X至少出现n次,也就是>=n次({}中不能有多余的空格)
X{n,m}表示X至少出现n次,但不超过m次,也就是>=n并且<=m次
正则表达式相关的方法(熟悉)
方法名称方法说明
boolean matches(String regex)判断当前正在调用的字符串是否匹配参数指定的正则表达式规则
  • 案例题目

    • 使用正则表达式描述一下银行卡密码规则:要求是由6位数字组成
  //1、定义描述规则的正则表达式字符串并使用变量记录(三种方式写正则表达式字符串)
    //String reg = "^[0-9]{6}$";
  //String reg = "[0-9]{6}";
    String reg = "\\d{6}"; //注意\需要使用转义字符写成\\
    //2、提示用户从键盘输入指定内容并使用变量记录
    Scanner scanner = new Scanner(System.in);
    String str; //用来存储输入的银行卡密码
    while (true) {
    	System.out.println("请输入您的银行卡密码:");
        str = scanner.next();
    
        //3、判断输入的密码是否满足定义好的正则表达式字符串
        if (str.matches(reg)) {
        	System.out.println("输入密码格式正确,可以检验密码是否有在数据库中了...");
            break;
         } else {
            System.out.println("输入的密码格式错误!");
         }
    }
  • 使用正则表达式描述QQ号码的规则:要求是由非0开头的5-15位数组成

    String reg = "[1-9]\\d{4,15}";
    //2、提示用户从键盘输入指定内容并使用变量记录
    Scanner scanner = new Scanner(System.in);
    String str; //用来存储输入的银行卡密码
    while (true) {
    	System.out.println("请输入您的QQ号码:");
        str = scanner.next();
    
        //3、判断输入的密码是否满足定义好的正则表达式字符串
        if (str.matches(reg)) {
        	System.out.println("输入号码格式正确,可以检验密码是否有在数据库中了...");
            break;
         } else {
            System.out.println("输入的号码格式错误!");
         }
    }
    
  • 使用正则表达式描述一下手机号码规则:要求由1开头,第二位数是3、4、5、7、8中的一位,总共11位

    String reg = "1[34578]\\d{9}";
    //2、提示用户从键盘输入指定内容并使用变量记录
    Scanner scanner = new Scanner(System.in);
    String str; //用来存储输入的银行卡密码
    while (true) {
    	System.out.println("请输入您的手机号码:");
        str = scanner.next();
    
        //3、判断输入的密码是否满足定义好的正则表达式字符串
        if (str.matches(reg)) {
        	System.out.println("输入号码格式正确,可以检验密码是否有在数据库中了...");
            break;
         } else {
            System.out.println("输入的号码格式错误!");
         }
    }
    
  • 描述身份证号码的规则:总共18位,6位数字代表地区,4位数字代表年,2位数字代表月,2位数字代表日期,3位数字代表个人,最后一位可能数字也可能是X

    String reg = "\\d{6}\\d{4}\\d{2}\\d{2}\\d{3}[0-9|X]";   // | 表示或;()括起来用来表示分割成部分
    
方法名称方法说明
String[] split(String regex)参数regex为正则表达式,以regex所表示的字符串为分隔符,将字符串拆分成字符串数组
String replace(char oldChar, char newChar)使用参数newChar替换此字符串中出现的所有参数oldChar
String replaceFirst(String regex, String replacement)将此字符串符合正则表达式regex的第一个字符串替换为replacement
String replaceAll(String regex, String replacement)将字符串中匹配正则表达式regex的所有字符串替换成replacement

方法使用举例:

//1、准备一个字符串对象并打印
String str = "1001,zhangfei,30";
//2、按照逗号对字符串进行切割
String[] sArr = str.split(",");
//3、遍历输出分割之后的字符串数组
for (int i = 0; i < sArr.length; i++) {
    System.out.println("下标为" + i + "的字符串是:" + sArr[i]);
}

System.out.println("---------------------------------------------");
//1、准备一个字符串用于替换
String str1 = "我的小名叫大帅哥";
String replace = str1.replace('我', '你');
System.out.println(replace);    //你的小命叫大帅哥

System.out.println("----------------------------------------------");
String str2 = "123abc456def789ghi";
String str3 = str2.replaceFirst("\\d+", "#"); //将第一个由多个数字组成的字符子串替换成#
System.out.println("替换后的字符串是:" + str3);
String str4 = str3.replaceAll("[a-z]+","A");  //将所有由多个字母组成的字符子串替换成A
System.out.println("替换后的字符串是:" + str4);

任务总结

  1. String类(重点)

    概念、常量池、常用的构造方法、常用的成员方法、正则表达式的概念和使用、相关的方法等

可变字符串类和日期相关类

可变字符串类(重点)

基本概念
  • 由于String类描述的字符串内容是个常量不可改变,当对String类对象进行操作时,每次都会创建新的对象,因此当需要在Java代码中描述大量类似的字符串时,只能单独申请和存储,此时会造成内存空间的浪费

    StringBuilder sb4 = sb3.insert(0,"abcd");
    System.out.println("sb4 = " + sb4); //abcdhello
    System.out.println("sb3 = " + sb3); //abcdhello,证明StringBuilder对象对应操作就是在原对象中操作
    String str1 = new String("hello");
    String str2 = str1.toUpperCase();
    System.out.println("str1 = " + str1);   //hello,证明String类的插入是创建一个新对象原hello才没有改变
    System.out.println("str2 = " + str2);   //HELLO
    
  • 为了解决上述问题,可以使用java.lang,StringBuilder类和java.lang.StringBuffer类来描述字符序列可以改变的字符串,如:“ab”

  • StringBuffer类是从jdk1.0开始存在,属于线程安全的类,因此效率比较低

  • StringBuilder类是从jdk1.5开始存在,属于非线程安全的类,效率比较高

StringBuilder类常用的构造方法
方法声明功能介绍
StringBuilder()使用无参方式构造对象,初始容量为16
StringBuilder(int capacity)根据参数指定的容量来构造对象,容量为参数指定大小
StringBuilder(String str)根据参数指定的字符串来构造对象,容量为:16+参数字符串长度
StringBuilder类常用的成员方法
方法声明功能介绍
int capacity()返回调用对象的容量
int length()返回字符串的长度,也就是字符的个数
StringBuilder insert(int offset, String str)插入字符串并返回调用对象的引用(也就是自己)
StringBuilder append(String str)追加字符串
StringBuilder deleteCharAt(int index)将当前字符串中下标为index位置的单个字符删除
StringBuilder delete(int start, int end)删除字符串(包头不包尾)
StringBuilder replace(int start, int end, String str)替换字符串(包头不包尾)
StringBuilder reverse()字符串反转
  • deleteCharAt()方法删除单个字符,每当删除一个字符后后面的字符会向前补位,因此后面的各字符下标都会前移一位

    sb3.deleteCharAt(8);	//abcd1234elloworld12345678899+123
    System.out.println("sb3 = " + sb3);
    //使用循环+deleteCharAt()删除hell0后续的ello字符
    for (int i = 8; i < 12; i++) {
        //sb3.deleteCharAt(i);	//无法删除
        sb3.deleteCharAt(8);    //想要删除需要这样
    }
    System.out.println("sb3 = " + sb3); //abcd1234loold12345678899+123->无法实现删除ello的目标,因为每删除一个都会前移
    
StringBuilder类扩容算法的源码分析

当StringBuilder字符串对象的长度超过了字符串对象的初始容量时,该字符串对象自动扩容,默认的扩容算法是:原始容量 * 2 + 2(查看append()源码可知)

注:查看源码的时候ctrl+alt+左右方向键 表示回到上/下一个位置

sb3.append("world12345678899+123");	//sb3的原始容量是21
System.out.println("sb3 = " + sb3); //abcd1234helloworld12345678899+123
System.out.println("sb3的容量现在是:" + sb3.capacity());	//21 * 2 + 2 = 44
System.out.println("sb3的长度现在是:" + sb3.length());	//33,超过原始容量21,需要扩容
常见的笔试考点
考点一:既然StringBuilder类的对象本身可以修改,那么为什么成员方法含有返回值呢?

为了连续调用,如:

sb.reverse.append("1").append("2").insert(0, "3").delete(0, 1).reverse();

考点二:如何实现StringBuilder类型和String类型之间的转换呢?

StringBuilder转换为String:String str = sb.toString();

String转换为StringBuilder:StringBuilder sb = new StringBuilder(str);

考点三:String、StringBuilder、StringBuffer之间效率高低如何?

StringBuilder > StringBuffer > String

错题:StringBuilder的内存相关问题

易错题

Java8之前的日期相关类

System类的概述
基本概念
  • java.lang.System类中提供了一些有用的类字段和方法
常用的方法
方法声明功能介绍
static long currentTimeMills()返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差
  • 该方法长用于测试某一段代码的执行效率(执行前与执行后的时间差就是执行效率)
Date类的概述
基本概念
  • java.util.Date类主要用于描述特定的瞬间,也就是年月日时分秒,可以精确到毫秒
常用的方法
方法声明功能介绍
Date()使用无参的方式构造对象,得到的是当前系统时间
Date(long date)根据参数指定毫秒数构造对象,参数为距离1970年1月1日0时0分0秒的毫秒数
long getTime()获取调用对象距离1970年1月1日0时0分0秒的毫秒数
void setTime(long time)设置调用对象的时间为距离1970年0时0分0秒time毫秒的时间
SimpleDateFormat类的概述
基本概念
  • java.text.SimpleDateFormat类主要用于实现日期和文本之间的转换
常用方法
方法声明功能介绍
SimpleDateFormat()使用无参方式构造对象
SimpleDateFormat(String pattern)根据参数指定的模式来构造对象,模式主要有:y-年 M-月 d-日 H-时 m-分 s-秒
final String format(Date date)用于将日期类型转换为文本类型
Date parse(String source)用于将文本类型转换为日期类型
SimpleDateFormat的使用举例

注意:SimpleDateFormat对象相当于是太上老君的炼丹炉,想要炼出仙丹(文本格式的日期),还需要原材料(Date对象)

public static void main(String[] args) throws Exception {
    //1、需要准备Date对象作为原材料作为转换
    Date date = new Date();
    System.out.println("date = " + date);
    //2、构造SimpleDateFormat类型的对象并指定格式
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    //3、实现日期类型向文本类型的转换并打印
    String format = simpleDateFormat.format(date);
    System.out.println("转换之后的日期为:" + format);
    //4、实现文本类型到日期类型的转换并打印
    Date parse = simpleDateFormat.parse(format);
    System.out.println("转回日期格式之后的结果为:" + parse);
}
Calendar类的概述
基本概念
  • java.util.Calendar类主要用于描述特定的瞬间,取代Date类中的过时方法实现全球化(作用)
  • 该类是个抽象类,因此不能实例化对象,其具体子类针对不同国家的日历系统,其中应用最广泛的是GregorianCalendar(格里高利历),对应世界上绝大多数国家/地区使用的标准日历系统
常用的方法
方法声明功能介绍
static Calendar getInstance()用于获取Calendar类型的引用
void set(int year, int month, int date, int hourOfDay, int minute, int second)用于设置年月日时分秒信息
Date getTime()用于将Calendar类型转换为Date类型
void set(int field, int value)设置指定字段的数值
void add(int field, int amount)向指定字段增加数值
Calendar类的使用举例
//1、获取Calendar类型的引用
Calendar instance = Calendar.getInstance();
//2、设置指定的年月日时分秒信息
instance.set(2008, 8 - 1, 8, 20, 8);    //注意:月份是从0算起,因此要-1
//3、转换为Date类型的对象
Date time = instance.getTime();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = simpleDateFormat.format(time);
System.out.println("获取到的时间是:" + format);

//4、使用set()和add()方法
System.out.println("----------------------------------------------------");
instance.set(Calendar.YEAR, 2018);  //设置指定字段YEAR为2018年
Date time1 = instance.getTime();
System.out.println(simpleDateFormat.format(time1));
instance.add(Calendar.MONTH, 1);
Date time2 = instance.getTime();    //将指定字段MONTH月份+1
System.out.println(simpleDateFormat.format(time2));
考点
  • 既然Calendar是个抽象类不能创建对象,那么getInstance()方法为什么能获取Calendar类型的引用呢?

    解析:由源码可知,getInstance()方法返回的并不是Calendar类型的对象,而是诸如GregorianCalendar等Calendar类的子类

多态的使用场合
  • 通过方法的参数传递形成多态

    如:

    public static void draw(Shape s) {
    	s.show();
    }
    draw(new Rect(1, 2, 3, 4))
    
  • 在方法体中直接使用多态的语法格式:子类对象指向的引用或实现对象指向接口的引用

    如:Accoount acc = new FixedAccount();

  • 通过方法的返回值类型形成多态

    如:

    Calendar getInstance() {
    	return new GregorianCalendar(zone, aLocale);
    }
    
Date类和Calendar类的区别
  • 官方不再推荐使用Date类,官方解释Date类不利于国际化,推荐使用Calendar类
  • Date是日期,Calendar是日历,Date是类,Calendar是抽象类。当然,你也可以觉得Calendar是Date的加强版
  • 在当前的开发环境中,Calendar使用的情况是比Date更多,毕竟它提供的方法大大的方便了我们程序猿完成跟时间有关的功能

Java8中的日期相关类(熟悉)

Java8日期类的由来

JDK1.0中包含了一个java.util.Date类,但是它的大多数方法已经在JDK1.1引入Calendar类之后被弃用了。而Calendar并不比Date好多少。它们面临的问题是:

  • Date类中的年份是从1900开始的,而月份都从0开始
  • 格式化只对Date类有用,对Calendar类则不能使用
  • 非线程安全等
Java8日期类的概述
  • Java8通过发布新的Date-Time API来进一步加强对日期与时间的处理
  • java.time包:该包日期/时间API的基础包
  • java.time.chrono包:该包提供对不同日历系统的访问
  • java.time.format包:该包能够格式化和解析日期时间对象
  • java.time.temporal包:该包包含底层框架和扩展特性
  • java.time.zone包:该包支持不同时区以及相关规则的类
LocalDate类的概述
基本概念
  • java.time.LocalDate类主要用于描述年-月-日格式的日期信息,该类不表示时间和时区信息
常用方法
方法声明功能介绍
static LocalDate now()在默认时区中从系统时钟获取当前日期
LocalTime类的概述
基本概念

java.time.LocalTime类主要用于描述时间信息,可以描述时分秒以及纳秒(表示当前时分秒时间,不显示日期

常用的方法
方法声明功能介绍
static LocalTime now()从默认时区的系统时间中获取当前时间
static LocalTime now(ZoneId zone)获取指定时区的当前时间
LocalDateTime类的概述
基本概念

java.time.LocalDateTime类主要用于描述ISO-8601日历系统中没有时区的日期时间,如2007-12-03T10:15:30

表示内容:日期+时分秒

常用的方法
方法声明功能介绍
static LocalDateTime now()从默认时区的系统时间中获取当前日期时间
static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second)根据参数指定的年月日时分秒信息来设置时间日期
int getYear()获取年份字段的数值
int getMonthValue()获取1到12之间的月份字段
int getDayOfMonth()获取日期字段
int getHour()获取小时数
int getMinute()获取分钟数
int getSecond()获取秒数
LocalDateTime withYear(int year)设置为参数指定的年
LocalDateTime withMonth(int month)设置为参数指定的月
LocalDateTime withDayOfMonth(int dayOfMonth)设置为参数指定的日
LocalDateTime withHour(int hour)设置为参数指定的时
LocalDateTime withMinute(int minute)设置为参数指定的分
LocalDateTime withSecond(int second)设置为参数指定的秒
LocalDateTime plusYears(long years)加上参数指定的年
LocalDateTime plusMonths(long months)加上参数指定的月
LocalDateTime plusDays(long days)加上参数指定的日
LocalDateTime plusHours(long hours)加上参数指定的时
LocalDateTime plusMinutes(long minutes)加上参数指定的分
LocalDateTime plusSeconds(long seconds)加上参数指定的秒
LocalDateTime minusYears(long years)减去参数指定的年
LocalDateTime minusMonths(long months)减去参数指定的月
LocalDateTime minusDays(long days)减去参数指定的日
LocalDateTime minusHours(long hours)减去参数指定的时
LocalDateTime minusMinutes(long minutes)减去参数指定的分
LocalDateTime minusSeconds(long seconds)减去参数指定的秒

注意:

  • LocalDateTime日期对象进行特征操作(设置操作withXxx,增加操作plusXxx,减去操作minusXxx)后对象具有不可变性和String类型对象相似,调用对象本身的数据内容不会发生改变,返回值相当于创建了一个新的对象,如下:

    //5、对LocalDateTime对象进行特征操作
    //5-1、设置操作
    LocalDateTime localDateTime = of.withYear(2012);    //设置年份
    System.out.println("localDateTime = " + localDateTime); //localDateTime = 2012-08-08T08:08:08
    System.out.println("of = " + of);   //of = 2008-08-08T08:08:08-->重新设置年份为2012后输出原来的of对象发现并没有改变,证明是新构造了一个LocalDateTime对象
    //5-2、增加操作
    LocalDateTime localDateTime1 = of.plusDays(1);
    System.out.println("localDateTime1 = " + localDateTime1);   //localDateTime1 = 2008-08-09T08:08:08
    System.out.println("of = " + of);   //of = 2008-08-08T08:08:08
    //5-3、减少操作
    LocalDateTime localDateTime2 = of.minusHours(5);
    System.out.println("localDateTime2 = " + localDateTime2);   //localDateTime2 = 2008-08-08T03:08:08
    System.out.println("of = " + of);   //of = 2008-08-08T08:08:08
    
Instant类的概述
基本概念
  • java.time.instant类主要用于描述瞬间的时间点信息
常用的方法
方法声明功能介绍
static instant now()从系统时钟上获取当前时间(并不是当前系统的默认时区,是本初子午线的时间,中国东八区,差8小时->与LocalDateTime类区别
OffsetDateTime atOffset(ZoneOffset offset)将此瞬间与偏移量组合以创建偏移日期时间(就是可以将时区之间的差距时间补上)
static Instant ofEpochMilli(long epochMilli)根据参数指定的毫秒数来构造对象,参数距离1970年1月1日0时0分0秒的毫秒数
long toEpochMilli()获取距离1970年1月1日0时0分0秒的毫秒数
使用举例
//1、使用Instant获取当前系统时间,并不是当前系统的默认时区,本初子午线,差8小时,东八区
Instant now = Instant.now();
System.out.println("获取到的当前时间为:" + now);

//2、加上所差的8小时(偏移量)
OffsetDateTime offsetDateTime = now.atOffset(ZoneOffset.ofHours(8));
System.out.println("补上时差之后的时间是:" + offsetDateTime);

//3、获取距离标准时间1970年1月1日0时0分0秒的毫秒数并根据此毫秒数构造对象
long l = now.toEpochMilli();
Instant instant = Instant.ofEpochMilli(l);
System.out.println("根据毫秒数构造出来的对象时间是:" + instant);
DateTimeFormatter类的概述
基本概念
  • java.time.format.DateTimeFormatter类主要用于格式化和解析日期(和SimpleDateFormat类差不多)
常用方法
方法声明功能介绍
static DateTimeFormatter ofPattern(String pattern)根据参数指定的模式来获取对象
String format(TemporalAccessor temporal)将参数指定日期时间转换为字符串
(TemporalAccessor是一个接口,
LocalDateTime、Instant等都是其实现类)
TemporalAccessor parse(CharSequence text)将参数指定字符串转换为日期时间
使用举例
//1、获取当前系统时间
LocalDateTime now = LocalDateTime.now();
//2、按照指定格式准备一个DateTimeFormatter类型的对象
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
//3、实现日期时间向字符串类型的转换并打印
String str = dateTimeFormatter.format(now);
System.out.println("当前系统时间是(调整格式为字符串后的结果):" + str);
//4、实现字符串类型到日期时间类型的转换并打印
TemporalAccessor parse = dateTimeFormatter.parse(str);
System.out.println("当前系统时间是(调整为TemporalAccessor引用后的结果):" + parse);

任务总结

  1. 可变字符串类(重点)

    StringBuilder类、StringBuffer类 --> 概念、常用方法等

  2. Java8之前的日期相关类(熟悉)

    Date类、Calendar类、SimpleDateFormat类 --> 概念和常用方法等

  3. Java8增加的日期相关类

    LocalDate类、LocalTime类、LocalDateTime类、Instant类、DateTimeFormatter类 --> 概念和常用方法等

集合类库(上)

集合的概述

集合的由来
  • 当需要在Java程序中记录单个数据内容时,则声明一个变量
  • 当需要在Java程序中记录多个类型相同的数据内容时,声明一个一维数组
  • 当需要在Java程序中记录多个类型不同的数据内容时,则创建一个对象
  • 当需要在Java程序中记录多个类型相同的对象数据时,创建一个对象数组
  • 当需要在Java程序中记录多个类型不同的对象数据时,则准备一个集合
集合的框架结构
  • Java中集合框架顶层框架是:java.util.Collection集合和java.util.Map集合
  • 其中Collection集合中存取元素的基本单位是:单个元素
  • 其中Map集合中存取元素的基本单位是:单对元素
集合家族一览

集合家族

Collection集合(重点)

基本概念
  • java.util.Collection接口是List接口、Queue接口以及Set接口的父接口,因此该接口里定义的方法既可以用于操作List集合,也可以用于操作Queue集合和Set集合
常用的方法
方法声明功能介绍
boolean add(E e)向集合中添加对象
boolean addAll(Collection <? extends E> c)用于将参数指定集合c中的所有元素添加到当前集合中
(将参数集合中的元素一个个按顺序添加到调用集合中)
boolean contains(Object o)判断是否包含指定对象
boolean containsAll(Collection <?> c)判断是否包含参数指定集合中的所有元素
boolean retainAll(Collection<?> c)保留当前集合和参数集合中共有的所有对象(交集)
(当前集合发生改变返回true,否则返回false)
boolean remove(Object o)从集合中删除对象(删除成功返回true,失败false)
boolean removeAll(Collection<?> c)从集合中删除参数指定的所有对象(参数集合c中所有元素
–> 能删除几个算几个)
void clear()清空集合
int size()返回集合中对象的个数
boolean isEmpty()判断是否为空
boolean equals(Object o)判断是否相等(已重写,判断的是两个集合元素是否全相同)
int hashCode()获取当前集合的哈希码值
Object[] toArray()将集合转换为数组(数组转换成集合:Arrays.asList(数组变量))
Iterator iterator获取当前集合的迭代器
注意
  • contains方法的工作原理是:Objects.equals(o, e),其中o代表contains方法的形式参数,e代表集合中的每个元素 --> 拿着参数对象与集合中已有的元素一次进行比较,比较的方式调用Objects中的equals方法,而该方法的代码如下:
    public static boolean equals(Object a, Object b) {	//其中a代表contains参数对象,b代表集合中已有的对象
    	return (a == b) || (a != null && a.equals(b));
    }
    

    元素包含的两种情况是:

    • contains参数对象与集合中已有对象的地址相同
    • contains参数对象不为空,则contains参数对象调用equals方法与集合中已有的元素进行比较

    代码举例:

    //当Person类中没有重写equals方法时,调用的是从Object类中继承下来的equals方法,比较的是两个对象的地址:false
    //当Person类中重写了equals方法后,则调用的是重写以后的版本,比较的是两个对象的内容:true
    b1 = c1.contains(new Person("张飞", 30));
    System.out.println("集合中是否包含30岁的张飞:" + b1);
    
    b1 = c1.contains(Integer.valueOf(2));   //true:因为java官方类已经重写了equals方法
    System.out.println("c1集合是否包含数字2:" + b1);
    
  • remove方法的工作原理:同contains()方法一样,还是调用Objects.equals(o, e)方法
  • Collection中没有提供修改方法,若想修改可以先删除后添加实现
笔试考点
  1. add()方法实参是传入一个集合:将集合作为一个对象添加到调用的集合对象中

    //将c2中所有元素添加到c1中(一个个依次添加)
    b1 = c1.addAll(c2);
    b1 = c1.add(c2);    //[one, 2, Person{name='张飞', age=30}, [three, 4]]将c2集合看作一个整体元素添加到c1中 -> c2集合是[three, 4]
    
  2. contains()方法实参是传入一个集合:判断调用的集合对象中有没有拥有参数集合整体为单位的元素

    System.out.println("c1集合中是否包含c2集合中全部元素:" + b1); //true
    b1 = c1.contains(c2);
    System.out.println("c1集合中是否包含一个c2作为整体单位的元素:" + b1); //false
    b1 = c1.contains(c1);
    System.out.println("c1集合中是否包含一个c1作为整体单位的元素:" + b1); //false
    
  3. remove()方法实参是传入一个集合:将传入的实参集合作为一个整体对象进行删除,若没有则无法删,返回false(和add()、contains()方法都是一样的)

    b1 = c1.remove(c3); //看c1里面有没有c3集合对象,有则删除,没有返回false
    System.out.println("b1 = " + b1);   //false
    System.out.println("c1 = " + c1);   //c1 = [2, three]
    

Iterator接口(重点)

基本概念
  • java.util.Iterator接口主要用于描述迭代器对象,可以遍历Collection集合中的所有元素
  • java.util.Collection接口继承自Iterator接口因此所有实现Collection接口的实现类都可以使用该迭代器对象
常用的方法
方法声明功能介绍
boolean hasNext()判断集合中是否有可以迭代/访问的元素
E next()用于取出一个元素
void remove()用于删除访问到的最后一个元素

使用举例:

Iterator iterator = collection.iterator();  //获取迭代器对象
while (iterator.hasNext()) {
    System.out.println("获取到的元素是:" + iterator.next());
}

使用迭代器取出元素相比较toString()方法优势在于:更加灵活,可以单独对一个集合元素进行处理

注:ArrayList中的toString()方法源码如下–>也是用Iterator迭代器

public String toString() {
    Iterator<E> it = iterator();
    if (! it.hasNext())
        return "[]";

    StringBuilder sb = new StringBuilder();
    sb.append('[');
    for (;;) {
        E e = it.next();
        sb.append(e == this ? "(this Collection)" : e);
        if (! it.hasNext())
            return sb.append(']').toString();
        sb.append(',').append(' ');
    }
}

注意:当我们使用迭代器的时候,不能直接使用集合Collection的方法修改集合,只能使用迭代器。如下删除操作

//使用迭代器迭代,碰到one则删除
iterator = collection.iterator();   //前面已经使用过了迭代器,记得要重置迭代器
while (iterator.hasNext()) {
    Object obj = iterator.next();
    if (obj.equals("one")) {
        iterator.remove();  //使用迭代器删除不报错
        //collection.remove(obj);    //使用集合remove()方法删除,编译不报错,运行出现ConcurrentModificationException(并发修改异常)
    }
}
System.out.println("删除后的集合:" + collection);
for each循环(重点)
基本概念
  • java5推出了增强型for循环语句,可以应用数组和集合的遍历
  • 是经典迭代的“简化版”
语法格式
for(元素类型 变量名:数组或集合名称) {
	循环体;
}
执行流程
  • 不断的从数组/集合中取出一个元素赋值给变量名并执行循环体,直到取完所有元素为止
举例
//使用foreach遍历集合或数组
for(Object obj : collection) {
    System.out.println("取出来的元素是:" + obj);
}
int[] arr = new int[] {11, 22, 33, 44, 55};
for (int i : arr) {
    System.out.println("数组中的元素有:" + i);
    i = 66; //注意:for each一般只用来遍历集合或数组,若需要修改集合或数组,不要用for each
}
for each底层源码:迭代器实现
public Iterator<E> iterator() {
    return new Itr();
}

List集合(重中之重)

基本概念
  • java.util.List集合是Collection集合的子集合,该集合允许有重复的元素并且有先后放入次序
  • 该集合的主要实现类有:ArrayList类、LinkedList类、Stack类、Vector类
  • 其中ArrayList类的底层是采用动态数组进行数据管理的,支持下标访问,增删元素不方便
  • 其中LinkedList类的底层是采用双向链表进行数据管理的,访问不方便,增删元素方便
  • 可以认为ArrayList和LinkedList的方法在逻辑上完全一样,只是在性能上有一定的差别,ArrayList更适用于访问而LinkedList更适用于插入和删除;在性能要求不是特别苛刻的情形下可以忽略这个差别
  • 其中Stack类的底层是采用动态数组进行数据管理的,该类主要用于描述一种具有后进先出特征的数据结构,叫做栈(last in first out LIFO)(已经被Deque接口取代)
  • 其中Vector类的底层是采用动态数组进行数据管理的,该类与ArrayList类相比数据线程安全的类,效率比较低(已过时,被ArrayList取代)
ArrayList类的源码分析
//1、声明一个List接口类型的引用指向ArrayList类型的对象,形成了多态
//由源码可知:当new对象时并没有申请数组的内存空间
List list = new ArrayList();
//2、向集合中添加元素并打印
//由源码可知:当调用add()方法添加元素时会给数组申请长度为10的一维数组,扩容原理是:原始长度的1.5倍
list.add("one");
System.out.println("list = " + list);

总结:ArrayList底层就是由一维数组实现的(–>数据结构:顺序表)

LinkedList类的源码分析
//1、声明一个List接口类型的引用指向LinkedList类型的对象,形成了多态
//由源码可知:当new对象时并没有申请内存空间
List list1 = new LinkedList();
//2、向集合中添加元素并打印
//由源码可知:LinkedList底层就是链表,添加元素就是双向链表的尾插法插入链表操作
list1.add("one");
System.out.println("list1 = " + list1);

总结:LinkedList底层就是链表,添加元素就是双向链表的插入操作(可以指定位置插入)

常用的方法
方法声明功能介绍
int size()获取集合长度
void add(int index, E element)向集合中指定位置添加元素
boolean addAll(int index, Collection<? extends E> c)向集合中添加所有元素
E get(int index)从集合中获取指定位置元素
int indexOf(Object o)查找参数指定的对象第一次出现的索引位置
int lastIndexOf(Object o)反向查找参数指定的对象第一次出现的索引位置
E set(int index, E element)修改指定位置的元素,返回被修改的元素
E remove(int index)删除指定位置的元素,返回被删除的元素
(删除元素后,后面的元素会向前补位)
List subList(int fromIndex, int toIndex)用于获取子List(包头不包尾;子集合和当前集合共用同一块
内存空间,对于子集合和当前集合操作会同时改变两者
  • 案例

    • 使用get()方法获取集合所有元素并使用StringBuilder按照格式打印

      List list = new LinkedList();
      
      //1、按照指定位置添加元素并打印
      list.add(0, "one");
      list.add(1, 3);
      list.add(1, "two"); //在链表中间插入
      System.out.println("list = " + list);
      //2、获取集合所有元素并使用StringBuilder按照格式打印
      StringBuilder sb = new StringBuilder();
      sb.append("[");
      for (int i = 0; i < list.size(); i++) {
          Object obj = list.get(i);
          //若取出的是最后一个元素,则拼接元素值和]
          if (i == list.size() - 1) {
              sb.append(obj).append("]");
          } else {    //否则拼接元素和逗号以及空格
              sb.append(obj).append(",").append(" ");
          }
      }
      //最后输出sb
      System.out.println("list = " + sb);
      
    • 使用循环+remove()方法删除全部集合元素

      for (int i = 0; i < list.size(); i++) { //方法一:去掉i++,因为size()会不断减少
      //          String string = (String) list.remove(i);    //删除后,后面的元素会前移,而且size()每次都会减少,这样不行
                  String string = (String) list.remove(i);    //方法一
                  System.out.println("被删除的元素是:" + string);
                  System.out.println("删除后的list = " + list);
              }
              System.out.println("-----------------------------方法二-------------------------------");
              for (int i = list.size() - 1; i >= 0; i--) {    //方法二:从后往前删,避开元素前移的问题
                  String remove = (String) list.remove(i);
                  System.out.println("被删除后的元素是:" + remove);
                  System.out.println("删除后的list = " + list);
              }
      
    • 截取子集合注意:子集合和父集合共用同一块存储空间

      List list1 = list.subList(1, 3);    //包头不包尾
      System.out.println("截取的子串:" + list1);   //截取的子串:[two, three]
      Object remove = list1.remove(0);
      System.out.println("删除的元素是:" + remove); //删除的元素是:two
      System.out.println("删除元素后的list = " + list); //删除元素后的list = [one, three, four]
      System.out.println("删除元素后的list1 = " + list1);   //删除元素后的list1 = [three]
      
    • 准备一个Stack集合,将数据11,22,33,44,55依次入栈并打印,然后查看栈顶元素并打印,然后将栈中所有数据依次出栈并打印

      再准备一个Stack对象,将数据从第一个栈中取出来放入第二个栈中,然后再从第二个栈中取出并打印

      //1、准备一个Stack类型的对象并打印
      Stack s1 = new Stack();
      Stack s2 = new Stack();
      //2、将11,22,33,44,55依次入栈并打印
      for (int i = 1; i <= 5; i++) {
          Object obj = s1.push(i * 11);
          System.out.println("入栈的元素是:" + obj);
          System.out.println("当前栈中元素有:" + s1);
      }
      //3、查看栈顶元素
      Integer peek = (Integer) s1.peek();
      System.out.println("栈顶元素是:" + peek);
      //4、将栈中所有数据依次出栈并打印
      int len = s1.size();
      for(int i = 0; i < len; i++) {
          Object p = s1.pop();
          System.out.println("出栈的元素是:" + p);
          //出栈的同时入s2栈
          s2.push(p);
      }
      System.out.println("全部出栈后栈中元素:" + s1);
      //5、打印s2栈中所有元素
      System.out.println("s2栈中的元素有:" + s2);
      //6、将s2栈中所有元素弹出并打印
      len = s2.size();
      for (int i = 0; i < len; i++) {
          Object pop = s2.pop();
          System.out.println("弹出的元素是:" + pop);
      }
      System.out.println("全部弹出后栈中元素有:" + s2);
      

Queue集合(重点)

基本概念
  • java.util.Queue集合是Collection集合的子集合,与List集合属于平级关系
  • 该集合主要用于描述先进先出特征的数据结构,叫做队列(First In First Out,FIFO)
  • 该集合主要实现类是LinkedList类,因为该类在增删方面比较有优势
常用的方法
方法声明功能介绍
boolean offer(E e)将一个对象添加至队尾,若添加成功则返回true
E poll()从队首删除并返回一个元素(出队)
E peek()返回队首的元素(但并不删除)
  • 案例

    准备一个Queue集合,将数据11,22,33,44,55依次入队并打印,然后查看队首元素并打印,然后将队列中所有数据依次出队并打印

    //1、准备一个Queue集合,将数据11,22,33,44,55依次入队并打印
    Queue queue = new LinkedList();
    for (int i = 1; i <= 5; i++) {
        queue.offer(i * 11);
    }
    System.out.println("当前队列中的元素有:" + queue);
    //2、查看队首元素并打印
    Object peek = queue.peek();
    System.out.println("队首元素是:" + peek);
    //3、将队列中所有数据依次出队并打印
    int len = queue.size();
    for (int i = 0; i < len; i++) {
        Object poll = queue.poll();
        System.out.println("出队元素是:" + poll);
    }
    System.out.println("全部出队后队列中的元素有:" + queue);
    

任务总结

  1. 集合的概述

    集合的由来、两张图

  2. Collection集合

    常用的方法

  3. Iterator接口

    概念、遍历集合、常用的方法

  4. for each结构

    是Java5新增的内容,是迭代器的简化版

  5. List集合

    概念、常用实现类、常用的方法

  6. Queue集合

    概念、常用实现类、常用的方法

集合类库(下)

泛型机制(熟悉)

基本概念
  • 通常情况下集合中可以存放不同类型的对象,是因为将所有对象都看作Object类型放入的,因此从集合中取出元素时也是Object类型,为了表达该元素真实的数据类型,则需要强制类型转换,而强制类型转换可能会引发类型转换异常(ClassCastException)
  • 为了避免上述错误的发生,从Java5开始增加泛型机制,也就是在集合名称的右侧使用<数据类型>的方式来明确要求该集合中可以存放的元素类型,若放入其他类型的元素则编译报错
  • 泛型只在编译时期有效,在运行时期不区分是什么类型
  • 若定义了泛型,但是使用时没有指定何种数据类型,则默认为Object类型
泛型机制的使用
  • Java7开始的新特性:菱形特性 --> new创建新对象时<>中的类型可以省略不写(<>要记得写)

  • 使用举例

    //1、创建一个集合,只能存储Integer类型
    List<Integer> list1 = new LinkedList<>();
    list1.add(1);
    list1.add(2);
    System.out.println("list1 = " + list1);
    //2、创建一个集合,只能存储String类型
    List<String> list2 = new LinkedList<>();
    list2.add("3"); //若添加其他类型元素将会报错
    list2.add("4");
    System.out.println("list2 = " + list2);
    //3、笔试考点:试图将list1赋值给list3,也就是覆盖list3中原来的数值,结果编译报错:集合中支持的类型不同
    List<Double> list3 = new LinkedList<>();
    //list3 = list1;    //Error:集合中支持的类型不同
    
底层原理
  • 泛型本质就是参数化类型,也就是让数据类型作为参数传递,其中E相当于形式参数负责占位(和函数形参、实参之间的关系有点类似),而使用集合时<>中的数据类型相当于实际参数,用于给形式参数E进行初始化,从而使得集合中所有的E被实际参数替换,由于实际参数可以传递各种各样广泛的数据类型,因此得名泛型
自定义泛型接口
  • 泛型接口和普通接口的区别就是后面添加了类型参数列表,可以有多个类型参数,如:<E, T, …>等
自定义泛型类
  • 泛型类和普通类的区别就是类名后面添加了类型参数列表,可以有多个类型参数,如:<E,T,…>等

  • 实例化泛型类时应该指定具体的数据类型,并且是引用数据类型而不是基本数据类型

  • 泛型类被继承时的处理方式

    • 父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型

    • 子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型

    • 代码举例

      //public class SubPerson extends Person { //子类不保留父类的泛型并且没有指定类型,此时父类中的T默认为Object类型  擦除
      //public class SubPerson extends Person<String> { //子类不保留父类的泛型但指定父类Person中的泛型T为String类型
      //public class SubPerson<T> extends Person<T> {   //子类保留父类的泛型,可以在构造子类对象时指定T的类型
      public class SubPerson<T, E> extends Person<T> {    //子类保留父类的泛型并且同时增加了新的泛型E,可以在构造子类对象时指定类型
      }
      
自定义泛型方法
  • 泛型方法就是我们输入参数的时候,输入的是泛型参数,而不是具体的参数。我们在调用这个泛型方法的时候需要对泛型参数进行实例化

  • 泛型方法的格式

    [访问权限] [static] <泛型> 返回值类型 方法名([泛型标识 参数名称]) {方法体;}

  • 在静态方法中使用泛型参数的时候,需要我们把静态方法定义为泛型方法

  • 类的静态成员方法不能使用类的泛型参数,因为类的泛型参数需要在new对象时才可确定类型,而静态是在创建对象之前就加载了的

    //类的静态成员方法不能使用类的泛型参数,因为类的泛型参数需要在new对象时才可确定类型,而静态是在创建对象之前就加载了的
    public /*static*/ T getGender() {
        return gender;
    }
    
  • 使用举例

    //自定义一个泛型方法,实现将传入参数的数组元素全部打印出来
    public static <T> void printArrar(T[] arr) {
        for (T t:
             arr) {
            System.out.println("数组中的元素有:" + t);
        }
    }
    
    public static void main(String[] args) {
        Person<Integer> test = new Person<>();
        /*int[] 注意必须是引用数据类型不能是基本数据类型*/Integer[] arr = {1, 2, 3};
        test.printArrar(arr);
    }
    
泛型在继承上的体现
  • 若B是A的一个子类或接口,而G是具有泛型声明的类或接口,则G并不是G的子类型!

    比如:String是Object的子类,但是List并不是List的子类!

    //1、声明两个List集合进行测试
    List<Animal> list1 = new LinkedList<>();
    List<Dog> list2 = new LinkedList<>();
    //list1 = list2;  //试图将list2赋值给list1,报错,因为虽然Animal是Dog的父类,但是list2和list1之间并无关系,不可将一种类型的泛型对象赋值给另一种类型
    
通配符的使用
  • 有时候我们希望传入的类型是在一个指定的范围内,此时就可以使用泛型通配符了(个人解读–>可以理解为就是泛型的多态,即父子类关系

  • 如:之前传入的类型要求为Integer类型,但是后来业务需要Integer的父类Number类可以传入

  • 泛型中有三种通配符形式:

    • <?> 无限制通配符,表示我们可以传入任意类型的参数(注意:**?通配符创建的对象不支持元素的添加操作**,因为?表示任意类型,添加时无法确定后面是否会添加其他类型;**支持获取操作**,因为获取时全部当作Object类型来处理)
    • <? extends E> 表示类型的上界是E--> 只能是E或E的子类(注意:**不支持元素的添加操作**,因为E的子类很多无法确定(可能还有孙子辈);**支持获取操作**,获取到的是E类型的引用)
    • <? super E> 表示类型的下界是E -> 只能是E或E的父类(支持元素的添加操作,但是必须是E或其子类;支持获取操作)
      //1、声明两个List集合进行测试
              List<Animal> list1 = new LinkedList<>();
              List<Dog> list2 = new LinkedList<>();
              //list1 = list2;  //试图将list2赋值给list1,报错,因为虽然Animal是Dog的父类,但是list2和list1之间并无关系,不可将一种类型
                              //的泛型对象赋值给另一种类型
              //2、使用通配符作为泛型类型的公共父类
              List<?> list3 = new LinkedList<>();
              list3 = list1;  //可以发生List<Animal>到List<?>的转换
              list3 = list2;  //可以发生List<Dog>到List<?>的转换
              //向公共父类中添加元素和获取元素
              /*list3.add(new Animal());
              list3.add(new Dog());   //?通配符不允许添加操作*/
              Object o = list3.get(0);    //允许获取操作
              //有限制通配符
              List<? extends Animal> list4 = new LinkedList<>();
      //      list4.add(new Dog());   //不支持添加操作,因为Animal的子类可能有很多,无法确定
              Animal animal = list4.get(0);   //支持获取操作,得到的是E或其子类
              List<? super Animal> list5 = new LinkedList<>();
              list5.add(new Animal());    //支持添加操作
              list5.add(new Dog());
      
  • 自我理解:泛型一般用于方法的形参中,就是为了起到方法中实参确定形参的效果。如下:

    public static void main(String[] args) {
            List<String> name = new ArrayList<String>();
            List<Integer> age = new ArrayList<Integer>();
            List<Number> number = new ArrayList<Number>();
            
            name.add("icon");
            age.add(18);
            number.add(314);
     
            getData(name);
            getData(age);
            getData(number);
    }
     
    public static void getData(List<?> data) {
          System.out.println("data :" + data.get(0));
    }
    

    解析: 因为getData()方法的参数是List类型的,所以name,age,number都可以作为这个方法的实参,这就是通配符的作用

    public static void main(String[] args) {
            List<String> name = new ArrayList<String>();
            List<Integer> age = new ArrayList<Integer>();
            List<Number> number = new ArrayList<Number>();
            
            name.add("icon");
            age.add(18);
            number.add(314);
     
            //getUperNumber(name);//1
            getUperNumber(age);//2
            getUperNumber(number);//3
           
    }
    public static void getData(List<?> data) {
          System.out.println("data :" + data.get(0));
    }
       
    public static void getUperNumber(List<? extends Number> data) {
          System.out.println("data :" + data.get(0));
    }
    

    解析: 在(//1)处会出现错误,因为getUperNumber()方法中的参数已经限定了参数泛型上限为Number,所以泛型为String是不在这个范围之内,所以会报错

    类型通配符下限通过形如 **List<? super Number>**来定义,表示类型只能接受Number及其三层父类类型,如 Object 类型的实例

Set集合

基本概念
  • java.util.Set集合是Collection集合的子集合,与List集合平级
  • 该集合中元素没有先后放入次序(注意无序是指:hashset的顺序是根据对象的hash值进行排序的,而不是对象插入的顺序排序,先插入的对象不一定排在前面,而是根据所有对象的hash值大小来进行排序),且不允许重复(Set集合最大特点)
  • 该集合的主要实现类是:HashSet类、TreeSet类以及LinkedHashSet类
  • 其中HashSet类的底层是采用哈希表进行数据管理的
  • 其中TreeSet类的底层是采用红黑树进行数据管理的
  • 其中LinkedHashSet类与HashSet类的不同之处在于内部维护了一个双向链表,链表中记录了元素的迭代顺序,也就是元素插入集合中的先后顺序,因此便于迭代
  • 哈希表的结构图如下:
    哈希表的结构
常用的方法
  • 参考Collection集合中的方法即可

  • 案例
    准备一个Set集合指向HashSet对象,向该集合中添加元素"two"并打印,再向集合中添加元素"one"并打印,再向集合中添加元素"three"并打印,再向集合中添加"one"并打印

    //1、准备一个Set集合指向HashSet对象
    Set<String> set = new HashSet<>();
    //2、向该集合中添加元素"two"并打印
    boolean b = set.add("two");
    System.out.println("打印成功吗?" + b);
    System.out.println("当前HashSet对象中元素有:" + set);
    //3、再向集合中添加元素"one"并打印
    set.add("one"); //当前HashSet对象中元素有:[one, two] -> 表明没有先后放入顺序
    System.out.println("当前HashSet对象中元素有:" + set);
    //4、再向集合中添加元素"three"并打印
    set.add("three");
    System.out.println("当前HashSet对象中元素有:" + set);
    //5、再向集合中添加"one"并打印
    set.add("one"); //当前HashSet对象中元素有:[one, two, three]->表明没有重复元素
    System.out.println("当前HashSet对象中元素有:" + set);
    
HashSet集合放入元素的原理
  • 原理详解

    • 使用元素调用hashCode()方法获取对应的哈希码值,再由某种哈希算法计算出该元素在数组中的索引位置
    • 若该位置没有元素,则将该元素直接放入即可
    • 若该位置有元素,则使用新元素与已有元素依次比较哈希值,若哈希值不相同,则将该元素直接放入
    • 若新元素与已有元素哈希值相同,则使用新元素调用equals方法与已有元素依次比较
    • 若相等则添加元素失败,否则将元素直接放入即可
  • 原理详解图:
    元素放入哈希表的过程

  • 思考:为什么要求重写equals()方法后要重写hashCode()方法呢?
    解析:当两个元素调用equals()方法相等时证明两个元素值相同,重写hashCode()方法后保证这两个元素得到的哈希码值相同,由于同一个哈希算法生成的索引位置相同,此时只需要与该索引位置已有元素比较即可,从而提高效率并避免重复元素的出现

TreeSet集合的概念
  • 二叉树主要指每个节点最多只有两个子节点的树形结构
  • 满足以下3个特征的二叉树叫做有序二叉树
    • 左子树中的任意节点元素都小于根节点元素值
    • 右子树中的任意节点元素都大于根节点元素值
    • 左子树和右子树的内部也遵循上述规则
    • 红黑树是一种特殊的有序二叉树
  • 由于TreeSet集合的底层采用红黑树进行数据的管理,当有新元素插入到TreeSet集合时,需要使用新元素与集合中已有的元素依次比较来确定新元素的合理位置(元素有大小次序,默认从小到大
  • 比较元素大小的规则有两种方式
    • 使用元素的自然规则进行比较并排序,让元素类型实现java.lang.Comparable接口
      • return 0:调用对象和参数对象相等,调用对象就是新增加的对象
      • return 小于0的数:调用对象小于参数对象,调用对象放前面
      • return 大于0的数:调用对象大于参数对象,调用对象放后面
    • 使用比较器规则进行比较并排序,构造TreeSet集合时传入java.util.Comparator接口
  • 自然排序的规则比较单一;而比较器的规则比较多元化,而且比较器优先于自然排序
TreeSet集合的使用举例
  • String、包装类等基本包装类型实现了Comparable接口,重写了compareTo(obj)方法,可以不用自己重写

    //1、创建一个TreeSet集合
    Set<Integer> set = new TreeSet<>();
    //2、使用add()方法添加元素进集合中,返回的是boolean类型
    set.add(1);
    set.add(3);
    set.add(2);
    System.out.println("当前集合中的元素有:" + set); //当前集合中的元素有:[1, 2, 3] --> 按照自然顺序排列,无关插入先后
    
  • 若是添加自定义类却未重写比较规则,会报错:java.lang.ClassCastException

  • TreeSet集合中添加自定义类 --> 使用元素的自然规则

    • Student类

      public class Student implements Comparable<Student>{	//注意要泛型,否则重写方法中的参数默认是Object类,写成Student将会报错
          private String name;
          private int age;
      
          public Student() {
          }
      
          public Student(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;
          }
      
          @Override
          public String toString() {
              return "Student{" +
                      "name='" + name + '\'' +
                      ", age=" + age +
                      '}';
          }
      
          @Override
          public int compareTo(Student o) {
              //return 0;   //调用对象和参数对象相等,调用对象就是新增加的对象
              //return -1;  //调用对象小于参数对象,调用对象放前面
              //return 1;   //调用对象大于参数对象,调用对象放后面
              //实现:先比较姓名,姓名相同时比较年龄
              int sameName = this.getName().compareTo(o.getName());
              return sameName == 0 ? this.getAge() - o.getAge() : sameName;
          }
      }
      
    • main方法

      //3、创建一个新的TreeSet集合,里面装入自定义类对象
      Set<Student> set1 = new TreeSet<>();
      set1.add(new Student("zhangfei", 30));
      set1.add(new Student("liubei", 40));
      set1.add(new Student("guanyu", 35));
      set1.add(new Student("zhangfei", 20));
      System.out.println("自定义集合set1中的元素有:" + set1);   //自定义集合set1中的元素有:[Student{name='guanyu', age=35}, Student{name='liubei', age=40}, Student{name='zhangfei', age=20}, Student{name='zhangfei', age=30}]
      
  • TreeSet集合中添加自定义类 --> 使用比较器规则:添加前准备比较器作为参数传递给集合构造方法

    //创建前先定义比较器规则 --> 使用匿名内部类 或 Lambda表达式
    /*Comparator<Student> comparator = new Comparator<Student>() {
        @Override
        public int compare(Student o1, Student o2) {    //o1表示新增加的对象,o2表示集合中已有的对象
            return o1.getAge() - o2.getAge();
        }
    };*/
    //Java8开始支持Lambda表达式:参数列表 -> {方法体;}
    Comparator<Student> comparator = (Student o1, Student o2) -> {
        return o1.getAge() - o2.getAge();
    };
    //使用比较器规则创建TreeSet集合并添加元素
    Set<Student> students = new TreeSet<>(comparator);  //比较器作为构造成员方法实参传入
    students.add(new Student("zhangfei", 30));
    students.add(new Student("liubei", 40));
    students.add(new Student("guanyu", 35));
    System.out.println("使用比较器的students集合元素有:" + students);
    

    值得注意的是:以上代码的Student类中实现Compareable接口(自然排序)中CompareTo()方法,该方法是优先姓名再年龄排序的,但是最终发现年龄才是第一优先顺序,因此可知 -> 比较器规则优先于类中自然排序规则

Map集合(重点)

基本概念
  • java.util.Map<K,V>集合中存取元素的基本单位是:单对元素,其中类型参数如下:
    • K - 键(Key),相当于一本书的目录
    • V - 值(Value),相当于一本书的内容
  • 该集合中key是不允许重复的,而且一个key只能对应一个value
  • 该集合主要实现类有:HashMap类、TreeMap类、LinkedHashMap类、Hashtable类、Properties类
  • 其中HashMap类的底层是采用哈希表进行数据管理的
  • 其中TreeMap类的底层是采用红黑树进行数据管理的
  • 其中LinkedHashMap类与HashMap类的不同之处在于内部维护了一个双向链表,链表中记录了元素的迭代顺序,也就是元素插入集合中的先后顺序,因此便于迭代
  • 其中Hashtable类是古老的Map实现类,与HashMap类相比属于线程安全的类,且不允许null作为key或者value的数值
  • 其中Properties类是Hashtable类的子类,该对象用于处理属性文件,key和value都是String类型的
  • Map集合是面向查询优化的数据结构,在大数据量的情况下有着优良的查询性能
  • 经常用于根据key检索value的业务场景
  • Set集合和Map集合之间的关系:看Set源码,其实Set集合底层就是用Map集合实现,只不过让Set集合的元素作为key,值new出一个Object()
    Set集合转换为Map集合的原理
常用的方法
方法声明功能介绍
V put(K key, V value)将Kye-Value对存入Map。若集合中已经包含该Key,
则替换该Key对应的Value,返回该Key的Value;若没有则直接插入,返回null
V get(Object key)返回与参数key对应的value对象,若不存在则返回null
boolean containsKey(Object key)判断集合中是否包含指定的key
booelan containsValue(Object value)判断集合中是否包含指定的value
V remove(Object key)根据参数指定的key进行删除
Set keySet()返回此映射中包含的键的Set视图(相当于拿出所有的key,视图
表示并没有真的生成一个Set集合)
Collection values()返回此映射中包含的值的Set视图(相当于拿出所有的value,视图
表示并没有真的生成一个Set集合)
Set<Map.Entry<K,V>> entrySet()返回此映射中包含的映射的Set视图(Entry是Map集合中的一个接口)
  • Map集合的三种遍历方式(迭代器只在Collection集合中,Map集合并没有)

    //以下是Map集合的三种遍历方式
    //1、使用keySet()
    Set<String> set = map.keySet();
    //使用key找出所有的value并按格式输出
    for (String s : set) {
        System.out.println(s + "=" + map.get(s));
    }
    //2、使用values()
    System.out.println("------------------------------------------");
    Collection<String> values = map.values();
    for (String s : values) {
        System.out.println("map中的value值有:" + s);
    }
    //3、使用entrySet()
    Set<Map.Entry<String, String>> entries = map.entrySet();
    for (Map.Entry<String, String> maps : entries) {
        System.out.println("map中的键值对有:" + maps);
    }
    
元素放入HashMap集合的原理
  • 使用元素的key调用hashCode方法获取对应的哈希码值,再由某种哈希算法计算再数组中的索引位置
  • 若该位置没有元素,则将该键值对直接放入即可
  • 若该位置有元素,则使用key与已有元素依次比较哈希值,若哈希值不同,则将该元素直接放入
  • 若key与已有元素的哈希值相同,则使用key调用equals方法与已有元素依次比较
  • 若相等则将对应的value修改,否则将键值对直接放入即可
Map源码中相关的常量
  • DEFAULT_INITIAL_CAPACITY:HashMap的默认容量是16
  • DEFAULT_LOAD_FACTOR:HashMap的默认加载因子是0.75
  • threeshold:扩容的临界值,该数值为 容量*填充因子,也就是16*0.75=12
  • TREEIFY_THRESHOLD:若Bucket中链表长度大于该默认值则转换为红黑树存储,该数值是8
  • MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量,该数值是64

Collections类

基本概念
  • java.util.Collections类主要提供了对集合操作或者返回集合的静态方法
常用的方法
方法声明功能介绍
static<T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)根据元素的自然顺序返回给定集合的最大元素
static T max(Collection<? extends T> coll, Comparator<? super T> comp)根据指定的比较器引发的顺序返回给定集合的最大元素
static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll)根据元素的自然顺序返回给定集合的最小元素
static T min(Collection<? extends T> coll, Comparator<? super T> comp)根据指定的比较器引发的顺序返回给定集合的最小元素
static void copy(List<? super T> dest, List<? extends T> src)将一个列表中的所有元素赋值到另一个列表中
方法声明功能介绍
static void reverse(List<?> list)反转指定列表中元素的顺序
static void shuffle(List<?> list)使用默认的随机源随机置换指定的列表
static<T extends Comparable<? super T>> void sort(List list)根据其元素的自然顺序将指定列表按升序排序
static void sort<List list, Comparator<? super T> c>根据指定比较器指定的顺序对指定列表进行排序
static void swap(List<?> list, int i, int j)交换指定列表中指定位置的元素
  • 使用举例

    //1、定义一个集合
    List<Integer> integers = Arrays.asList(10, 20, 45, 25, 5);
    List<Student> names = Arrays.asList(new Student("zhangfei",35), new Student("guanyu", 40), new Student("liubei", 40));
    //2、使用Collections中的各种方法对该集合进行操作
    System.out.println("integers中的最大值是(自然排序规则):" + Collections.max(integers));
    System.out.println("integers中的最小值是(自然排序规则):" + Collections.min(integers));
    Comparator<Student> comparator = (Student s1, Student s2) -> {
        return s1.getName().compareTo(s2.getName());    //使用名字进行排序
    };
    //使用比较器之后的结果
    System.out.println("names中的最大值是(比较器规则):" + Collections.max(names, comparator)); //Student{name='zhangfei', age=35}
    System.out.println("names中的最小值是(比较器规则):" + Collections.min(names, comparator)); //Student{name='guanyu', age=40}
    //反转、洗牌、排序、交换方法
    Collections.reverse(integers);
    System.out.println("integers集合反转后的元素有:" + integers);
    Collections.shuffle(integers);
    System.out.println("integers集合进行洗牌后的元素有:" + integers);
    Collections.sort(integers);
    System.out.println("integers集合进行排序后的元素有:" + integers);
    Collections.swap(integers, 0, 4);
    System.out.println("交换0和4号位置后的integers中的元素有:" + integers);
    //注意copy方法:复制过去的集合必须要有一定的空间(不是容量),否则会报错Source does not fit in dest
    //List<Integer> list = new ArrayList<>(20);   //报错:java.lang.IndexOutOfBoundsException: Source does not fit in dest
    //注意:上面的20仅仅是该集合的容量,不是当前集合的长度,因此还是不行,会报错
    List<Integer> list = Arrays.asList(new Integer[integers.size()]);   //这样就可以了
    Collections.copy(list,integers);
    System.out.println("list = " + list);
    

    注意:copy方法:复制过去的集合必须要有一定的空间(不是容量!),否则会报错Source does not fit in dest

任务总结

  1. 泛型机制(熟悉)

    概念和本质;自定义泛型接口、类、方法;继承方面的体现,通配符

  2. Set集合(熟悉)

    概念;主要实现类;常用的方法;两种方式指定排序的规则

  3. Map集合(重点)

    概念;主要实现类;常用的方法

  4. Collections(熟悉)

    概念和常用的方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值