栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java枚举、注解、异常、常用类、集合

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java枚举、注解、异常、常用类、集合

枚举
  1. 不需要提供 setXxx 方法,因为枚举对象的值通常只读
  2. 对枚举对象 / 属性 使用 final + static 共同修饰,实现底层优化
  3. 枚举对象名通常使用全部大写,常量的命名规范
  4. 枚举对象根据需要,可以有多个属性
自定义类实现枚举

1】构造器私有化

2】本类内部创建一组对象

3】对外暴露对象 (通过为对象添加 public static final 修饰符)

4】可以提供 get 方法,但是不要提供 set 方法

public class enumeration01 {
    public static void main(String[] args) {
        System.out.println(Season.WINTER);
    }
}
class Season {
    private String name;
    private String desc;
    //定义四个对象
    //如果使用static 属性会导致类的加载
    //所以可以优化:static final
    public static final Season SPRING = new Season("春天", "温暖");
    public static final Season SUMMER = new Season("夏天", "炎热");
    public static final Season AUTUMN = new Season("秋天", "凉爽");
    public static final Season WINTER = new Season("冬天", "寒冷");

    //1. 将构造器私有化,目的防止其他类 new
    //2. 去掉setXXX方法,防止属性被修改
    //3. 在Season 内部,直接创建固定的对象
    private Season(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }

    public String getName() {
        return name;
    }

    public String getDesc() {
        return desc;
    }

    @Override
    public String toString() {
        return "Season{" +
                "name='" + name + ''' +
                ", desc='" + desc + ''' +
                '}';
    }
}
enum关键字实现枚举
public class enumeration02 {
    public static void main(String[] args) {
        System.out.println(Season02.SUMMER);
    }
}

enum Season02 {
    //如果使用enum 关键字来实现枚举
    //1.使用关键字 enum 替代 class
    //2. public static final Season SPRING = new Season("春天", "温暖");
    //   SPRING("春天", "温暖") ——> 常量名(实参列表)
    //3. 如果有多个常量(对象) 使用 ,号间隔即可
    //4. 如果使用enum 来实现枚举,要求将定义常量对象写在前面
    //5、如果使用无参构造器创建枚举对象,则实参列表和小括号可以省略
    SPRING("春天", "温暖"), SUMMER("夏天", "炎热"), AUTUMN("秋天", "凉爽"), WINTER("冬天", "寒冷"), WHAT;
    private String name;
    private String desc;

    private Season02(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }
    private Season02() {}//无参构造器

    public String getName() {
        return name;
    }

    public String getDesc() {
        return desc;
    }

    @Override
    public String toString() {
        return "Season{" +
                "name='" + name + ''' +
                ", desc='" + desc + ''' +
                '}';
    }
}

使用注意:

1、当我们使用 enum关键字 开发一个枚举类时,默认会 extends Enum类
2、传统的 public static final Season02 = new Season02("春天","温暖"); 
	简化成 SPRING("春天","温暖") 这里使用哪个构造器
3、如果使用无参构造器 创建枚举对象,则实参列表和小括号都可以省略
4、当有多个枚举对象时,使用 ,号间隔,最后一个以 ;号结尾
5、枚举对象必须放在枚举类的行首

enum常用方法
  1. toString :Enum类已重写过,返回当前枚举对象名,子类可以重写该方法,用于返回对象信息

  2. name : 返回当前对象名(常量名)子类不能重写

  3. ordinal :返回当前对象位置号,默认从0开始

  4. values :返回当前枚举类中所有常量

  5. valuesOf :将字符串转换成枚举对象,要求字符串必须是已有的常量名,否则报错

  6. compareTo :比较两个枚举常量,比较其编号

    package enumeration;
    
    public class enumMethod {
        public static void main(String[] args) {
            //使用Season02 枚举类,演示各个方法
            Season02 winter = Season02.WINTER;
            //输出枚举对象的名称
            System.out.println(winter.toString());
            //ordinal() 输出的是该枚举对象的次序/编号
            //WINTER 枚举对象是第4个 因此输出为3(从0开始)
            System.out.println(winter.ordinal());
            //从反编译可以看出 values方法,返回 Season02[]
            //含有定义的所有枚举对象
            Season02[] values = Season02.values();
            System.out.println("===遍历取出枚举对象(增强for)===");
            for(Season02 season02 : values) {
                System.out.println(season02);
            }
            //valuesOf:将字符串转换成枚举对象,要求字符必须为已有常量名,否则报错
            //执行流程:
            //1.根据你输入的 “XXX” 到 Season02的枚举对象去查找
            //2.如果找到了就返回,如果没找到就报错
            Season02 winter1 = Season02.valueOf("WINTER");
            System.out.println("winter1=" + winter1);
            System.out.println(winter1 == winter);//true
            //compareTo : 比较两个枚举常量 比较的是编号
            
            //相当于 Season02.SUMMER的编号[1] - Season02.WINTER的编号[3]
            System.out.println(Season02.SUMMER.compareTo(Season02.WINTER));//-2
        }
    }
    

    增强 for循环

    int[] nums = {1, 4, 9};
            //普通for循环
            for (int i = 0; i < nums.length; i++) {
                System.out.println(nums[i]);
            }
            //增强for循环
            //执行流程:依次从nums取值赋给i 若取出完毕就退出for循环
            for(int i : nums) {
                System.out.println("i=" + i);
            }
    
enum 实现接口

使用 enum关键字后,不能再继承其他类,因为enum隐式继承Enum,java是单继承机制

public class enumDetail {
    public static void main(String[] args) {
        COCO.TEA.make();
    }
}
enum COCO implements MilkTea {
    TEA;
    @Override
    public void make() {
        System.out.println("coco奶茶");
    }
}
interface MilkTea {
    public void make();
}
注解

@Override
  1. @Override 表示指定重写父类的方法
  2. 如果不写 @Override 注解,而父类仍有该方法,仍构成重写
  3. @Override 只能修饰方法,不能修饰其他类、包、属性等
  4. 查看 @Override注解 源码为 @Target(ElementType.METHOD) 说明 只能修饰方法
  5. @Target 是修饰注解的注解,称为元注解
public class Override_ {
}
class Father {
    public void say() {};
}
class Son extends Father {
    //1. @Override 注解方法放在say方法上,表示子类的say方法是重写了父类的say方法
    //2. 这里如果没有写 @Override 还是重写父类 fly方法
    //3. 如果写了 @Override注解,编译器会去检查该方法是否真的重写了父类的方法
    //   如果的确重写了,则编译通过,如果没有构成重写,则编译报错
    //4. @Override 的定义
    //   如果发现 @interface 表示一个 注解类
            
    @Override
    public void say() {
        super.say();
    }
//    @Override 【报错】
//    public void fly() {
//        System.out.println();
//    }
}
@Deprecated
  1. @Deprecated 用于表示某个程序元素(类,方法等)已过时
  2. 可以修饰类、方法、字段、包、参数 等
  3. @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
  4. @Deprecated 的作用可以做到新旧版本的兼容和过渡
package com.Annotation;

public class Deprecated_ {
    public static void main(String[] args) {
        OldSyrups oldSyrups = new OldSyrups();
        oldSyrups.OldMethod01();
    }
}
//1. @Deprecated 修饰某个元素,表示该元素已经过时
//2. 即不再推荐使用,但是仍可以使用
//3. 查看 @Deprecated注解 的源码
//4. 可以修饰 方法、类、字段、包、参数等
//5. @Documented 可以作为版本升级过渡使用


@Deprecated
class OldSyrups {
    @Deprecated
    public void OldMethod01() {}
}
@SuppressWarnings
关键字用途
allto suppress all warnings
boxingto suppress warnings relative to boxing/unboxing operations
castto suppress warnings relative to cast operations
dep-annto suppress warnings relative to deprecated annotation
deprecationto suppress warnings relative to deprecation
fallthroughto suppress warnings relative to missing breaks in switch statements
finallyto suppress warnings relative to finally block that don’t return
hidingto suppress warnings relative to locals that hide variable
incomplete-switchto suppress warnings relative to missing entries in a switch statement (enum case)
nlsto suppress warnings relative to non-nls string literals
nullto suppress warnings relative to null analysis
rawtypesto suppress warnings relative to un-specific types when using generics on class params
restrictionto suppress warnings relative to usage of discouraged or forbidden references
serialto suppress warnings relative to missing serialVersionUID field for a serializable class
static-accesso suppress warnings relative to incorrect static access
synthetic-accessto suppress warnings relative to unoptimized access from inner classes
uncheckedto suppress warnings relative to unchecked operations
unqualified-field-accessto suppress warnings relative to field access unqualified
unusedto suppress warnings relative to unused code
package com.Annotation;

import java.util.ArrayList;
import java.util.List;

public class SuppressWarnings {
    //1. 当我们不希望看见警告时,可以使用 @SuppressWarnings注解 来抑制警告信息
    //2. 在{""} 中可以写入不希望抑制(不显示)警告信息
    //3. 关于 @SuppressWarnings 的作用方位时和你放置的位置有关
    //   通常可以放置在具体的语句,方法,或者更大的范围
    //4. @SuppressWarnings 源码
    
    //(1) 放置位置就是 TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE
    //(2) 该注解有数组 String[] value() 设置一个数组比如 {"rawtypes", "unchecked", "unused"}
    @java.lang.SuppressWarnings({"rawtypes", "unchecked", "unused"})
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("jack");
        list.add("tom");
        list.add("mary");
        int i;
        System.out.println(list.get(1));
    }
}
元注解

元注解是负责对其它注解进行说明的注解

@Retention 注解

@Retention 用于描述注解的生命周期,也就是该注解被保留的时间长短。@Retention 注解中的成员变量(value)用来设置保留策略,value 是 java.lang.annotation.RetentionPolicy 枚举类型,RetentionPolicy 有 3 个枚举常量,如下所示。

  1. SOURCE:在源文件中有效(即源文件保留)
  2. CLASS:在 class 文件中有效(即 class 保留)
  3. RUNTIME:在运行时有效(即运行时保留)

生命周期大小排序为 SOURCE < CLASS < RUNTIME,前者能使用的地方后者一定也能使用。如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS 注解;如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,则可选用 SOURCE 注解。

@Target 注解

@Target 注解用来指定一个注解的使用范围,即被 @Target 修饰的注解可以用在什么地方。@Target 注解有一个成员变量(value)用来设置适用目标,value 是 java.lang.annotation.ElementType 枚举类型的数组,下表为 ElementType 常用的枚举常量。

名称说明
CONSTRUCTOR用于构造方法
FIELD用于成员变量(包括枚举常量)
LOCAL_VARIABLE用于局部变量
METHOD用于方法
PACKAGE用于包
PARAMETER用于类型参数(JDK 1.8新增)
TYPE用于类、接口(包括注解类型)或 enum 声明
@Documented

@Documented 是一个标记注解,没有成员变量。用 @Documented 注解修饰的注解类会被 JavaDoc 工具提取成文档。默认情况下,JavaDoc 是不包括注解的,但如果声明注解时指定了 @Documented,就会被 JavaDoc 之类的工具处理,所以注解类型信息就会被包括在生成的帮助文档中

@Inherited

@Inherited 是一个标记注解,用来指定该注解可以被继承。使用 @Inherited 注解的 Class 类,表示这个注解可以被用于该 Class 类的子类。就是说如果某个类使用了被 @Inherited 修饰的注解,则其子类将自动具有该注解。

异常
public class exception01 {
    public static void main(String[] args) {
        int n1 = 2;
        int n2 = 0;
        //快捷键 ctrl + alt + T
        try {
            int res = n1 / n2;
        } catch (Exception e) {
            System.out.println("错误原因=" + e.getMessage());//显示错误信息
        }
        System.out.println("程序继续执行...");
    }
}
基本概念

异常体系图

小结:

  1. 异常分为两大类,运行时异常和编译时异常
  2. 运行时异常:编译器不要求强制处理的异常,一般指编程时的逻辑错误,是应避免其出现的异常【java.lang.RuntimeException类及它的子类 都是运行时异常】
  3. 运行异常可不作处理,保证程序的可读性和运行效率
  4. 编译时异常:是编译器要求必须处理的异常
常见运行时异常

1】NullPointerException 空指针异常

当应用程序试图在需要对象的地方使用 null 时,抛出该异常

public class NullPointerException {
    public static void main(String[] args) {
        String name = null;
        System.out.println(name.length());
    }
}

2】ArithmeticException 数学运算异常

当出现异常的运算条件时,抛出此异常

public class ArithmeticException {
    public static void main(String[] args) {
        int n1 = 2;
        int n2 = 0;
        int res = n1 / n2;//除数不能=0
    }
}

3】ArrayIndexOutOfBoundsException 数组下标越界异常

用非法索引访问数组时抛出的异常,如果索引为负数或者大于等于数组大小,则该索引为非法索引

public class ArrayIndexOutOfBoundsException {
    public static void main(String[] args) {
       int[] arr = {1, 3, 5};
        for (int i = 0; i <= arr.length; i++) {//不能是 <=
            System.out.println(arr[i]);
        }
    }
}

4】ClassCastException 类型转换异常

当试图将对象强制转换为不是实例的子类时,抛出该异常

public class ClassCastException {
    public static void main(String[] args) {
        B b = new B();//向上转型
        B b2 = (B) b;//向下转型
        C c2 = (B) b;//抛出ClassCastException异常
    }
}
class A {}
class B extends A {}
class C extends A {}

5】NumberFormatException 数字格式不正确

当应用程序视图将字符串转换成一种数据类型,但该字符串不能转换为适当格式时,抛出该异常

public class exception01 {
    public static void main(String[] args) {
      String name = "绿军总冠军";
      int num = Integer.parseInt(name);//抛出异常
        System.out.println(num);
    }
}
编译异常

编译异常是指在编译期间,就必须处理的异常,否则代码不能通过编译

异常处理

异常处理就是当异常发生时,对异常处理的方式

1】try-catch-finally

程序员在代码中捕获发生的异常,自行处理

如果代码未抛出异常,则catch 不执行,直接执行finally
可以没有finally

try-catch 方式处理异常说明

public class TryCatchDetail {
    public static void main(String[] args) {
        //1.如果异常发生了,则异常发生后面的代码不会执行,直接进入到catch块
        //2.如果异常没有发生,则顺序执行try的代码块,不会进入catch
        //3.如果希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等)则使用代码 finally
        try {
            String str = "123";
            int a = Integer.parseInt(str);
            System.out.println("数字=" + a);
        } catch (NumberFormatException e) {
            throw new RuntimeException(e.getMessage());
        } finally {
            System.out.println("finally代码被执行");
        }
        System.out.println("程序继续执行...");
    }
}

public class TryCatch {
    public static void main(String[] args) {

        //1. 如果try代码块有可能有多个异常
        //2. 可以使用多个catch 分别捕获不同的异常,相应处理
        //3. 要求子类异常写在前面,父类异常卸载后面
        try {
            Person person = new Person();
//            person = null;
            System.out.println(person.getName());//NullPointerException
            int n1 = 10;
            int n2 = 0;
            int res = n1 / n2;//ArithmeticException
        } catch (NullPointerException e) {
            System.out.println(e.getMessage());
        } catch (ArithmeticException e) {
            System.out.println(e.getMessage());
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
        }
    }
}

class Person {
    private String name = "jack";

    public String getName() {
        return name;
    }
}

public class TryCatch_ {
    public static void main(String[] args) {
        
        try {
            int n1 = 10;
            int n2 = 2;
            System.out.println(n1 / n2);
        }
        finally {
            System.out.println("执行了finally...");
        }
        System.out.println("程序继续执行...");
    }
}

练习

import java.util.Scanner;

public class homework01 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入一个整数");
        String num;
        do {
            try {
                num = scanner.next();
                int realNum = Integer.parseInt(num);
                break;
            } catch (Exception e) {
                System.out.println("请重新输入");
            }
        } while (true);
        System.out.println("你输入的数=" + num);
    }
}
2】throws

import java.io.FileInputStream;

public class Throws01 {
    public static void main(String[] args) {}

    public void f2() throws Exception {
        //创建了一个文件流对象
        //1. FileNotFoundException 编译异常
        //2. 可以使用 try-catch-finally
        //3. 使用throws 跳出异常,让调用f2方法的调用者(方法)处理
        //4. throws后面的异常类型可以是方法中产生的异常类,也可以是它的父类
        //5. throws 关键字后面可以是 异常列表,即可以抛出多个异常
        FileInputStream fis = new FileInputStream("d://aa.txt");
    }
}

将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者时JVM

public class exception02 {
    //若没有显示使用try-catch-finally
    //默认使用throws
    public static void main(String[] args) throws Exception {
        int n1 = 1;
        int n2 = 0;
        int res = n1 / n2;
    }
}
throws 处理机制
1.try-catch-finally 和 throws 二选一
如果没有显示处理异常,默认使用throws

使用细节

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class Throws02 {
    public static void main(String[] args) {
        f2();
    }
    public static void f2() {
        //1. 对于编译异常,程序中必须处理:try-catch 或 throws
        //2. 对于运行时异常,程序中如果没有处理,默认throws的方法处理
        int n1 = 10;
        int n2 = 0;
        double res = n1 / n2;
    }

    public static void f1()  {
        //1. 因为 f3() 方法抛出的是一个编译异常
        //2. 即这是,就要 f1() 必须处理这个异常【throws 或 try-catch】
        try {
            f3();
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
    
    public static void f3() throws FileNotFoundException {
        FileInputStream fis = new FileInputStream("d://aa.txt");
    }
    
    public static void f4() {
        //1. 在f4() 中调用方法 f5() 是OK
        //2. 原因是 f5() 抛出的是【运行异常】
        //3. java中,不要求程序员处理,存在默认处理机制
        f5();
    }
    
    public static void f5() throws RuntimeException {}

}

class Father {
    public void method() throws RuntimeException {}
}
class Son extends Father {
    
    //4. 在throws 过程中,如果方法 try-catch,就相当于处理异常,就可以不必throws
    @Override
    public void method() throws RuntimeException {}
}
自定义异常

基本概念:

当程序中出现了某些 “错误” ,但是该错误信息并没有在 Throwable 子类中描述处理,这个时候可以自己设计异常类,用于描述该错误信息

自定义异常步骤:

  1. 定义类:自定义异常类名(自己编写)extends Exception 或 RuntimeException
  2. 如果继承 Exception 属于编译异常
  3. 如果继承 RuntimeException 属于运行异常(一般来说,继承RuntimeException)
public class CustomException01 {
    public static void main(String[] args) {
        int age = 180;
        //要求范围在 18——120 之间,否则抛出一个异常
        if (! (age >=10 && age <= 120)) {
            throw new AgeException("年龄需要在 18——120 之间");
        }
        System.out.println("年龄范围正确");
    }
}
//自定义一个异常
//1. 一般情况下,我梦定义异常是继承 RuntimeException
//2. 即把自定义异常做成 运行时异常,好处【可以使用默认处理机制】
class AgeException extends RuntimeException {
    public AgeException(String message) {
        super(message);
    }
}
throw 和 throws 的区别

练习
public class homework01 {
    public static void main(String[] args) {
        try {
            if (! (args.length == 2)) {
                throw new ArrayIndexOutOfBoundsException("参数个数错误");
            }
            int n1 = Integer.parseInt(args[0]);
            int n2 = Integer.parseInt(args[1]);

            double res = cal(n1, n2);
            System.out.println("计算结果=" + res);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println(e.getMessage());
        } catch (NumberFormatException e) {
            System.out.println("参数格式不正确,需要输入整数");
        } catch (ArithmeticException e) {
            System.out.println("出现除0异常");
        }
    }

    public static int cal(int n1, int n2) {
        return n1 / n2;
    }
}

常用类 包装类

包装类 和 基本数据转换
  1. jdk5 以前手动装箱和拆箱方式,装箱:基本数据类型——>包装类型,反之,拆箱
  2. jdk5 以后(含jdk5)的自动装箱和拆箱方式
  3. 自动装箱底层调用 valueOf方法,比如Interger.valueOf()
public class Integer01 {
    public static void main(String[] args) {
        //int --> Integer 的装箱和拆箱
        //jdk5 以前手动装箱和拆箱
        //手动装箱 int --> Integer
        int n1 = 10;
        Integer integer = new Integer(n1);//第一种方式
        Integer integer1 = Integer.valueOf(n1);//第二种方式
        //手动拆箱 Integer --> int
        int i = integer.intValue();

        //jdk5 以后(含jdk5)可以自动装箱和拆箱方式
        int n2 = 20;
        //自动装箱
        Integer integer2 = n2;//底层使用的是 Integer.valueOf(n2)
        //自动拆箱
        int n3 = integer2;//底层仍使用 intValue()方法

        //========================================
        char c1 = 'a';
        //手动装箱
        Character character = new Character(c1);
        Character character1 = Character.valueOf(c1);
        //手动拆箱
        character.charValue();
        
        //自动装箱
        char c2 = 'b';
        Character character2 = c2;//底层使用 Character.valueOf(c2)
        //自动拆箱
        char c3 = character2;//底层仍然使用 charValue()方法
    }
}

三元运算符【是一个整体】先提升优先级,所以输出--> 1.0
if-else 是分开的 不会提升优先级所以输出--> 1而不是1.0
包装类型 和 String类型的相互转换
public class WrapperVSString {
    public static void main(String[] args) {
        //包装类(Integer)-->String
        Integer i = 10;
        //方式1
        String s1 = i.toString();
        //方式2
        String s2 = i + "";
        //方式3
        String s3 = String.valueOf(i);

        //String-->包装类(Integer)
        String str4 = "绿军总冠军";
        //方式1
        Integer i2 = Integer.parseInt(str4);//使用到了自动装箱
        //方式2
        Integer i3 = new Integer(str4);//构造器
    }
}

面试题

public class Test01 {
    public static void main(String[] args) {
        Integer i = new Integer(1);
        Integer j = new Integer(1);
        System.out.println(i==j);//false
        //所以,范围如果在 -128~127 就直接返回
        Integer m = 1;//底层 Integer.valueOf(1); -->阅读源码
        Integer n = 1;
        System.out.println(m == n);//true
        //所以,范围如果在 -128~127 就直接返回
        //否则, 就new Integer(xx)
        Integer x = 128;
        Integer y = 128;
        System.out.println(x == y);//false
    }
}

public class Test02 {
    public static void main(String[] args) {
        Integer i1 = new Integer(127);
        int i2 = 127;
        System.out.println(i1 == i2);//T
        //只要有基本数据类型,判断的是值是否相同
    }
}
String类 两种创建 String对象的方式

创建字符串最简单的方式如下:

String str = "Runoob";

在代码中遇到字符串常量时,这里的值是 “Runoob”,编译器会使用该值创建一个 String 对象。

和其它对象一样,可以使用关键字和构造方法来创建 String 对象。

用构造函数创建字符串:

String str2=new String("Runoob");

String 创建的字符串存储在公共池中,而 new 创建的字符串对象在堆上:

String s1 = "Runoob";              // String 直接创建
String s2 = "Runoob";              // String 直接创建
String s3 = s1;                    // 相同引用
String s4 = new String("Runoob");   // String 对象创建
String s5 = new String("Runoob");   // String 对象创建

两种创建String对象的区别
方式一;String s = "hsp"
方式二:String s2 = new String("hsp")

String的内存分布图

public class String01 {
    public static void main(String[] args) {
        //1.String 对象用于保存字符串,也就是一串字符序列
        //2.“jack” 字符串常量,双引号括起来的字符序列
        //3.字符串的字符使用Unicode字符编码:一个字符(不区分字母还是汉字)占两个字节
        //4.String 类有很多构造函数,构造器重载
        
        //6.String 是 final类 不能被其他类继承
        //7.String 有属性:private final char value[]; 用于存放字符串内容
        //8.注意:value 是一个 final类型,不可以修改【即value不能指向新的地址,但是单个字符内容可以变化】
        String name = "jack";
        name = "tom";
        final char[] value = {'a', 'b', 'c'};
        char[] v2 = {'t', 'o', 'm'};
        value[0] = 'H';
//        value = v2; 这里不可以更改 属性value的地址
    }
}
String类【练习】

字符串的特性

1】String 是一个 final 类,代表不可变的字符序列

2】字符串是不可变的,一个字符串对象一旦被分配,其内容不可变

//以下语句创建了几个对象?【2个对象】
String s1 = "hello";
s1 = "haha";
//内存布局图如下


//以下语句创建了几个对象?【1个对象】
String a = "hello" + "abc";

//编译器进行优化,判断创建的常量池对象,是否有对象引用
//String a = "hello"+"abc"; ==> 优化等价于 String a = "helloabc";

重要规则:

String c1 = "ab" + "cd"; 常量相加,看的是池
String c1 = a + b; 变量相加,在堆中
import java.lang.String;

public class StringExercise08 {
    public static void main(String[] args) {
        String a = "hello";//创建 a对象
        String b = "abc";//创建 b对象
        //1. 先 创建一个 StringBuilder sb = StringBuilder();
        //2. 执行 sb.append("hello");
        //3. sb.append("abc")
        //4. String c = sb.toString()
        //最后其实是 c 指向堆中的对象(String) value[] --> 池中的 "helloabc"
        String c = a + b;        //创建了几个对象?【3个对象】
        String d = "helloabc";
        System.out.println(c == d);//false
        String e = "hello" + "abc";
        System.oout.println(e == d);//true
    }
}

String对象特性【练习】

String类的常见方法

1】equals 比较内容是否相同,区分大小写

String str1 = "hello";
String str2 = "Hello";
System.out.println(str1.equals(str2));//false

2】equalsIgnoreCase 忽略大小写判断内容是否相同

public class StringMethod {
    public static void main(String[] args) {
        String name = "JACK";
        if ("jack".equalsIgnoreCase(name)) {
            System.out.println("True");//输出
        } else {
            System.out.println("False");
        }
    }
}

3】length 获取字符串的个数,字符串的长度

System.out.println("塔图姆牛逼".length());//5

4】indexOf 获取字符在字符串对象中第一次出现的索引,索引从0开始,如果找不到,返回 -1

	String s1 = "abc@de@dfgab";
        int index = s1.indexOf('@');
        System.out.println(index);//3
		//可以是字符串
        System.out.println("首次出现ab的位置=" + s1.indexOf("ab"));//输出第一次出现a的位置=0

5】lastIndexOf 获取字符在字符串中最后一次出现的索引,索引从0开始,如果找不到返回 -1

		String s1 = "abc@de@dfgab";
		index = s1.lastIndexOf('@');//6
        System.out.println(index);
        System.out.println("最后出现ab的位置=" + 	 s1.lastIndexOf("ab"));//输出最后出现a的位置=10

6】substring 截取指定范围的内容

		String name1 = "hello,张三";
//下面name1.substring(6) 从索引开始截取后面所有的内容
        System.out.println(name1.substring(6));//张三
//name1.substring(0,5)表示从索引0开始截取,截取到第5-1=4的位置
        System.out.println(name1.substring(0,5));//hello
//name1.substring(2,5)表示从索引2开始截取,截取到第5-1=4的位置
        System.out.println(name1.substring(2,5));//llo

7】toUpperCase 转换成大写

8】toLowerCase 转换成小写

public class StringMethod2 {
    public static void main(String[] args) {
        String s = "hello";
        System.out.println(s.toUpperCase());//HELLO
        System.out.println(s.toLowerCase());//hello
    }
}

9】concat 拼接字符串

String s1 = "白起";
        s1 = s1.concat("万人屠").concat("秦国名将").concat("invincible");
        System.out.println(s1);//白起万人屠秦国名将invincible

10】replace 替换字符串中的字符

String s2 = "peace and war war war";
        String s11 = s2.replace("war", "love");
        
        System.out.println(s2);//peace and war war war
		System.out.println(s11);//peace and love love love

11】split 分割字符串,对于某些分割字符,我们需要转义【比如:| 或 】

String poem = "锄禾日当午@汗滴禾下土@谁知盘中餐@粒粒皆辛苦";
        //1. 以 @ 为标准对 poem 进行分割,返回一个 数组
        String[] split = poem.split("@");
        for (int i = 0; i < split.length; i++) {
            System.out.println(split[i]);
        }
        //2. 在对字符串进行分割时如果有特殊字符,需要加入转义符--> 
        String poem2 = "E:\aaa\bbb";
        String[] split2 = poem2.split("\\");
        System.out.println("====分割后的内容====");
        for (int i = 0; i < split2.length; i++) {
            System.out.println(split2[i]);
        }

12】toCharArray 转换成字符数组

String ss = "happy";
        char[] chars = ss.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            System.out.println(chars[i]);
        }

13】compareTo 比较两个字符串的大小

//compareTo 比较两个字符串的大小,
        //如果前者大则返回正数,后者大返回负数,如果相等返回0
        //(1) 如果长度相同,并且每个字符也相同,就返回0
        //(2) 如果长度相同或不相同,但在比较时可以区分大小
        
        //(3) 如果前面的部分都相同,就返回 str1.length - str2.length
        String a = "fac";
        String b = "jac";
        //char(“102”) f - char("108") j = -4
        System.out.println(a.compareTo(b));//-4

compareTo 源码

public int compareTo(String anotherString) {
        int len1 = value.length;
        int len2 = anotherString.value.length;
        int lim = Math.min(len1, len2);
        char v1[] = value;
        char v2[] = anotherString.value;

        int k = 0;
        while (k < lim) {
            char c1 = v1[k];
            char c2 = v2[k];
            if (c1 != c2) {
                return c1 - c2;
            }
            k++;
        }
        return len1 - len2;
    }

14】format 格式化字符串

	    String name = "jack";
        int age = 20;
        double score = 211.985;
        char gender = '男';
        //1. %s, %d, %.2f, %c 称为占位符
        //2. 这些占位符由后面变量来替换
        //3. %s ——> 字符串来替换
        //4. %d ——> 整数来替换
        //5. %.2f ——> 表示小数来替换,替换后,会保留小数点后两位【进行四舍五入处理】
        //6. %c ——> 使用char类型替换
//    String info = String.format("姓名是%s年龄是%d成绩是%.2f性别是%c",name, age, score, gender);

        String StrFormat = "姓名是%s 年龄是%d 成绩是%.2f 性别是%c";
        String info2 = String.format(StrFormat, name, age, score, gender);
        System.out.println(info2);

StringBuffer类 基本介绍

public class StringBuffer01 {
    public static void main(String[] args) {
        //1. StringBuffer 的直接父类是 AbstractStringBuilder
        //2. StringBuffer 实现了 Serializable 即StringBuffer对象可以串行化
        
        //4. StringBuffer 是一个 final 类,不能被继承
        
        StringBuffer stringBuffer = new StringBuffer();
    }
}

String类 和 StringBuffer类 区别

StringBuffer的构造器

public class StringBuffer02 {
    public static void main(String[] args) {
        //1. 创建一个 容量为 16的 char[] 用于存放字符内容
        StringBuffer stringBuffer = new StringBuffer();

        //2. 通过构造器指定 char[] 容量
        StringBuffer stringBuffer1 = new StringBuffer(100);

        //3. 通过给一个 String 创建 StringBuffer, char[] 容量就是 str.length() + 16;
        StringBuffer stringBuffer2 = new StringBuffer("hello");
    }
}
String和StringBuffer相互转换

String ——> StringBuffer

public class StringBuffer03 {
    public static void main(String[] args) {
        //String-->StringBuffer
        //方式一: 使用构造器
        //注意: 返回的结果是StringBuffer对象,对 str 本身没有影响
        String str = "hello future";
        StringBuffer stringBuffer = new StringBuffer(str);
        //方式二: 使用 append 方法
        StringBuffer stringBuffer1 = new StringBuffer();
        stringBuffer1 = stringBuffer.append(str);
    }
}

StringBuffer ——> String

public class StringBuffer03 {
    public static void main(String[] args) {		
		//StringBuffer-->String
        StringBuffer stringBuffer2 = new StringBuffer("绿军总冠军");
        //方法一: 使用StringBuffer提供的 toString方法
        String s = stringBuffer2.toString();
        //方法二: 使用构造器
        String s1 = new String(stringBuffer2);
    }
} 
StringBuffer常用方法

1】append 增

2】delete 删

3】replace 改

4】indexOf 查

5】insert 插入

6】length 长度

package StringBuffer;

public class StringBufferMethod {
    public static void main(String[] args) {
        StringBuffer s1 = new StringBuffer("凯尔特人");
        //增
        s1.append("塔图姆");//凯尔特人塔图姆
        s1.append("牛逼").append("10000").append("绿军总冠军");//凯尔特人塔图姆牛逼10000绿军总冠军
        System.out.println(s1.toString());

        //删
        
        s1.delete(9, 14);
        System.out.println(s1.toString());//凯尔特人塔图姆牛逼绿军总冠军

        //改
        //使用 勇士 替换 索引 [9, 11) 的字符
        s1.replace(9, 11, "勇士");
        System.out.println(s1.toString());//凯尔特人塔图姆牛逼勇士总冠军

        //查
        //查找指定的字符串在字符串第一次出现的索引,如果找不到返回 -1
        int indexOf = s1.indexOf("塔图姆");
        System.out.println(indexOf);

        //插入
        //在索引为4的位置插入 ”杜兰特“,原来索引为4的内容自动后移
        s1.insert(4, "杜兰特");
        System.out.println(s1.toString());//凯尔特人杜兰特塔图姆牛逼勇士总冠军

        //长度
        System.out.println(s1.length());
    }
}
练习

import java.util.Scanner;

public class StringBufferExercise01 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入工资:  ");
        String price = scanner.next();
        StringBuffer sb = new StringBuffer(price);
//        int i = sb.lastIndexOf(".");
//        sb = sb.insert(i-3, ",");
        for (int i = sb.lastIndexOf(".") - 3; i > 0; i -= 3) {
            sb = sb.insert(i, ",");
        }
        System.out.println(sb);
    }
}
StringBuilder类 基本介绍

StringBuilder 和 StringBuffer 均代表可变的字符序列

package StringBuilder;

public class StringBuilder01 {
    public static void main(String[] args) {
        //1. StringBuilder 继承 AbstractStringBuilder 类
        //2. 实现了 Serializable, 说明StringBuilder对象是可以串行化的(对象可以网络传输,可以保存到文件)
        //3. StringBuilder 是 final类, 不能被继承
        //4. StringBuilder 对象字符序列任然是存放在其父类 AbstractStringBuilder 的 char[] value;
        //    因此,字符序列是堆中
        //5. StringBuilder 的方法,没有做互斥处理 即没有synchronized 关键字,因此在单线程的情况下使用 StringBuilder
        StringBuilder stringBuilder = new StringBuilder();
    }
}
String、StringBuffer 和 StringBuilder比较

package StringBuilder;

public class StringVsStringBuilderVsStringBuffer {
    public static void main(String[] args) {
        long startTime = 0L;
        long endTime = 0L;
        String text = "";
        startTime = System.currentTimeMillis();
        for (int i = 0; i < 80000; i++) {
            text = text + i;
        }
        endTime = System.currentTimeMillis();
        System.out.println("String的执行时间:" + (endTime - startTime));

        StringBuffer buffer = new StringBuffer("");
        startTime = System.currentTimeMillis();
        for (int i = 0; i < 80000; i++) {
            buffer.append(String.valueOf(i));
        }
        endTime = System.currentTimeMillis();
        System.out.println("StringBuffer的执行时间:" + (endTime - startTime));

        StringBuilder builder = new StringBuilder("");
        startTime = System.currentTimeMillis();
        for (int i = 0; i < 80000; i++) {
            builder.append(String.valueOf(i));
        }
        endTime = System.currentTimeMillis();
        System.out.println("StringBuilder的执行时间:" + (endTime - startTime));
    }
}

Math类

Math 类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数

1】abs 绝对值

package Math_;

public class mathMethod {
    public static void main(String[] args) {
        int abs = Math.abs(-9);
        System.out.println(abs);//9
    }
}

2】pow 求幂

	double pow = Math.pow(2, 4);//2的4次方
        System.out.println(pow);

3】ceil 向上取整,返回 >= 该参数的最小整数

	   double ceil = Math.ceil(-3.002);
        System.out.println(ceil);//-3.0

        double ceil2 = Math.ceil(3.9);
        System.out.println(ceil2);//4.0

4】floor 向下取整,返回 <= 该参数的最大整数

	   double floor = Math.floor(-3.002);
        System.out.println(floor);//-4.0

        double floor2 = Math.floor(3.9);
        System.out.println(floor2);//3.0

5】round 四舍五入 Math.floor(该参数+ 0.5)

        long round = Math.round(2.6);
        System.out.println(round);//3

6】sqrt 求开方

        double sqrt = Math.sqrt(-9.0);
        System.out.println(sqrt);//NaN = Not a Number 表示一个无效的操作结果

        double sqrt2 = Math.sqrt(9.0);
        System.out.println(sqrt2);//3.0

7】random 求随机数 返回的是 0 <= x < 1 之间的一个随机小数

获取 a - b 之间的一个随机整数公式:

int num = (int)(Math.random() * (b-a+1) + a)
//思考: 请写出获取 a-b 之间的一个随机整数,a,b均为整数, 比如 a = 2,b = 7
        //即返回一个数x 2 <= x <= 7
        //解读:Math.random() * (b-a) 返回的就是 0 <= 数 <= b-a
        //(1) (int)(a) <= x <= (int)(int)(a + Math.random() * (b-a +1))
        //(2) 具体值进行分析:a=2 , b=7
        // (int)(a + Math.random() * (b-a +1)) = (int)(2 + Math.random() * 6)
        // Math.random() * 6 返回的是 0 <= x < 8 小数
        // 2 + Math.random() * 6 返回 2 <= x < 8 小数
        // (int) (2 + Math.random() * 6) ——> 2 <= x <= 7
        //(3)公式是 (int)(int)(a + Math.random() * (b-a +1))

        for (int i = 0; i < 10; i++) {
            System.out.println((int)( 2 + Math.random() * (7 - 2 + 1)));
        }
    }

8】max 返回最大值

9】min 返回最小值

        int min = Math.min(1, 9);
        int max = Math.max(2, 6);
        System.out.println("最小值=" + min);
        System.out.println("最大值=" + max);
Arrays类

Arrays里面包含了一系列静态方法,用于管理或操作数组(比如排序和搜索)

Arrays类常用方法

1】tostring 返回数组的字符串形式

package Arrays_;

import java.util.Arrays;

public class ArraysMethod {
    public static void main(String[] args) {
        Integer[] integers = {2, 1, 90};
        System.out.println(Arrays.toString(integers));
    }
}

tostring 源码

public static String toString(Object[] a) {
        if (a == null)
            return "null";

        int iMax = a.length - 1;
        if (iMax == -1)
            return "[]";

        StringBuilder b = new StringBuilder();
        b.append('[');
        for (int i = 0; ; i++) {
            b.append(String.valueOf(a[i]));
            if (i == iMax)
                return b.append(']').toString();
            b.append(", ");
        }
    }

2】sort 排序(自然排序和定制排序)

自然排序

package Arrays_;

import java.util.Arrays;
import java.util.Comparator;

public class ArraysMethod {
    public static void main(String[] args) {
        Integer[] integers = {2, 1, 90};
        System.out.println(Arrays.toString(integers));

        Integer arr[] = {1, -2, 31, 9, 100};
        //1. 可以直接使用冒泡排序,也可以直接使用Arrays的sort方法 排序
        //2。 因为数组是引用类型,所以通过sort排序 会直接影响到实参

        Arrays.sort(arr);//默认排序方法
        System.out.println("===排序后1===");
        System.out.println(Arrays.toString(arr));

        Integer arr2[] = {5, 1, -4, 90, 3};
        //3. sort是重载的,可以通过传入一个接口 comparator 实现定制排序
        
        
        Arrays.sort(arr2, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                Integer i1 = (Integer)o1;//向下转型
                Integer i2 = (Integer)o2;
                return i2 - i1;
            }
        });
        System.out.println("===排序后2===");
        System.out.println(Arrays.toString(arr2));
    }
}

定制排序

package Arrays_;

import java.util.Arrays;
import java.util.Comparator;

public class ArraysSortCustom {
    public static void main(String[] args) {
        int[] arr = {1, -1, 3, 80, 5};
//        m1(arr);
        m2(arr, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                int i1 = (Integer) o1;//拆箱
                int i2 = (Integer) o2;
                return i2 - i1;
            }
        });
        System.out.println(Arrays.toString(arr));
    }

    //冒泡 + 定制
    public static void m2(int[] arr, Comparator c) {
        int temp = 0;
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length -1 -i; j++) {
                //数组排序由 c.compare(arr[j], arr[j+1]) 返回的值决定
                if ((c.compare(arr[j], arr[j+1]) < 0)) {//动态绑定
                    temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
    }

    //冒泡排序
    public static void m1(int[] arr) {
        int temp = 0;
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length -1 -i; j++) {
                if (arr[j] < arr[j+1]) {
                    arr[j] = temp;
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
    }
}

3】binarySearch 通过二分搜索进行查找,要求必须排序好

package Arrays_;

import java.util.Arrays;

public class ArraysMethod02 {
    public static void main(String[] args) {
        Integer[] arr = {1, 3, 90, 123, 226};
        //binarySearch 通过二分搜索进行查找,【要求必须排序好】
        //1. 使用binarySearch 二叉查找
        //2. 要求该数组是有序的,如果该数组是无序的,不能使用binarySearch
        //3. 如果数组中不存在该元素,就返回 return -(low + 1);  // key not found.
        //   low ——> 该数应该存在的索引
        //   例如:456 索引为5,但是不存在这个数,就返回 -(5+1) = -6
        int index = Arrays.binarySearch(arr, 457);
        System.out.println("index=" + index);//-6
    }
}

4】copyOf 数组元素的复制

	    //1. 从 arr 数组中,拷贝 arr.length个元素到 newArr数组中
        //2. 如果拷贝的长度 > arr.length 就在新数组的后面 插入 null
        //3. 如果拷贝长度 <0 就抛出异常NegativeArraySizeException
        //4。 该方法的底层使用的是 System.arraycopy() 
        Integer[] newArr = Arrays.copyOf(arr, arr.length);
        System.out.println(Arrays.toString(newArr));

5】fill 数组元素的填充

Integer[] num = new Integer[]{9, 6, 8};
        //1. 使用 100000 去填充 num数组,可以理解成是替换原来的元素
        Arrays.fill(num, 100000);
        System.out.println(Arrays.toString(num));

6】equals 比较两个数组元素内容是否完全一致

	  Integer[] arr2 = {1, 3, 90, 123};
        //1. 如果 arr 和 arr2 数组的元素一样,则返回true
        //2. 如果不是完全一样,就返回false
        boolean equals = Arrays.equals(arr, arr2);
        System.out.println("equals=" + equals);

7】asList 将一组值,转换成 list

//1. asList方法,会将(2, 4, 6, 8, 10)数据转成一个List 集合
        //2. asList 编译类型 list(接口)
        //3. asList 运行类型 java.util,Arrays$ArrayList,
        // 即是Arrays类的静态内部类   private static class ArrayList extends AbstractList
        //        implements RandomAccess, java.io.Serializable
        List asList = Arrays.asList(2, 4, 6, 8, 10);
        System.out.println("asList=" + asList);
        System.out.println("asList的运行类型 " + asList.getClass());
练习
package Arrays_;

import java.util.Arrays;
import java.util.Comparator;

public class ArraysExercise01 {
    public static void main(String[] args) {
        Book[] books = new Book[4];
        books[0] = new Book("梦", 50);
        books[1] = new Book("三国演绎", 5);
        books[2] = new Book("水浒", 150);
        books[3] = new Book("知音漫客zzz", 20);

        //按照书名长度排序
        Arrays.sort(books, new Comparator() {
            @Override
            public int compare(Book o1, Book o2) {
                Book book1 = (Book) o1;
                Book book2 = (Book) o2;
                return book1.getName().length() - book2.getName().length();
            }
        });
        System.out.println(Arrays.toString(books));
    }
}

class Book {
    private String name;
    private int price;

    public Book(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public int getPrice() {
        return price;
    }

    @Override
    public String toString() {
        return "name=" + getName() + "price=" + getPrice();
    }
}
package Arrays_;

import java.util.Arrays;
import java.util.Comparator;

public class ArraysExercise01 {
    public static void main(String[] args) {
        Book[] books = new Book[4];
        books[0] = new Book("梦", 50);
        books[1] = new Book("三国演绎", 5);
        books[2] = new Book("水浒", 150);
        books[3] = new Book("知音漫客zzz", 20);

        //按照价格排序
        Arrays.sort(books, new Comparator() {
            @Override
            public int compare(Book o1, Book o2) {
                Book book1 = (Book) o1;
                Book book2 = (Book) o2;
                int priceDifferent = book1.getPrice() - book2.getPrice();
                if (priceDifferent > 0) {
                    return 1;
                } else if (priceDifferent < 0) {
                    return -1;
                } else {
                    return 0;
                }
            }
        });
        System.out.println(Arrays.toString(books));
    }
}

class Book {
    private String name;
    private int price;

    public Book(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public int getPrice() {
        return price;
    }

    @Override
    public String toString() {
        return "name=" + getName() + "price=" + getPrice();
    }
}
System类

1】exit 退出当前程序

package System_;

public class SystemMethod {
    public static void main(String[] args) {
        System.out.println("ok1");
        //1.exit(0) 表示程序退出
        //2. 0表示一个状态 【正常状态】
        System.exit(0);
        System.out.println("ok2");
    }
}

2】arraycopy 复制数组元素

比较适合底层,一般使用 Arrays.copyOf 完成复制数组

 int[] src = {1, 2, 3};
        int[] ints = new int[3];
// 【源数组】
        // src – the source array.
// 【srcPos 从源数组的哪个索引位置开始拷贝】
        // srcPos – starting position in the source array.
// 【dest 目标数组,即把源数组的数据拷贝到那个数组】
        // dest – the destination array.
// 【destPos 把源数组的数据拷贝到 目标数组的哪个索引】
        // destPos – starting position in the destination data.
// 【length 从源数组拷贝多少个数据到目标数组】
        // length – the number of array elements to be copied.

        System.arraycopy(src, 0, ints, 0, 3);
        System.out.println(Arrays.toString(ints));

3】currentTimeMillens 返回当前时间距离 1970-1-1 的毫秒数

4】gc 运行垃圾回收机制

package System_;

public class Systemgc {
    public static void main(String[] args) {
        Car car = new Car("宝马");
        car = null;
        System.gc();
    }
}
class Car {
    private String name;

    public Car(String name) {
        this.name = name;
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("进行销毁");
    }
}
BigInteger类

BigInteger 适合保存比较大的整型

package BigInteger_;
import java.math.BigInteger;

public class BigInteger01 {
    public static void main(String[] args) {

        //编程时,需要处理很大的整数,long不够用,可以使用BigInteger类来搞定
        BigInteger bigInteger = new BigInteger("10009900009999999999999999999");
        System.out.println(bigInteger);
        //1. 在对 BigInteger 进行加减乘除,需要使用对应的方法,不能直接 + - * /
        //2. 可以创建一个要操作的 BigInteger 然后进行相应操作
        BigInteger bigInteger2 = new BigInteger("100");
        //加
        BigInteger add = bigInteger.add(bigInteger2);
        System.out.println(add);
        //减
        BigInteger subtract = bigInteger.subtract(bigInteger2);
        System.out.println(subtract);
        //乘
        BigInteger multiply = bigInteger.multiply(bigInteger2);
        System.out.println(multiply);
        //除
        BigInteger divide = bigInteger.divide(bigInteger2);
        System.out.println(divide);
    }
}
BigDecimal 类

BigDecimal 适合保存精度更高的浮点型(小数)

package BigDecimal_;

import java.math.BigDecimal;

public class bigDecimal {
    public static void main(String[] args) {
        BigDecimal bigDecimal = new BigDecimal("10.9999999999999999999999");
        BigDecimal bigDecimal2 = new BigDecimal("1.3");
        System.out.println(bigDecimal);

        //1. 如果对 BigDecimal 进行运算,加减乘除,需要使用相应方法
        //2. 创建一个需要操作的 BigDecimal 然后调用相应方法
        System.out.println(bigDecimal.add(bigDecimal2));
        System.out.println(bigDecimal.subtract(bigDecimal2));
        System.out.println(bigDecimal.multiply(bigDecimal2));

        //否则可能出现 ArithmeticException 异常【无限循环小数】
        //调用 divide 方法时,需要指定精度 BigDecimal.ROUND_CEILING
        //如果出现无限循环小数,就会保留 分子的 精度
        System.out.println(bigDecimal.divide(bigDecimal2, BigDecimal.ROUND_CEILING));
    }
}

日期类 第一代日期类

1】Date :精确到毫秒,代表特定的瞬间

2】SimpleDateFormat :指定格式

3】parse :把String 格式转换为 Date

package Date_;
import java.text.ParseException;
import java.util.Date;
import java.text.SimpleDateFormat;

public class Date1 {
    public static void main(String[] args) throws ParseException {
        //1.获取当前时间
        //2.这里的Date类 是在java.util包
        Date d1 = new Date();
        System.out.println("当前日期= " + d1);

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");
        String format = simpleDateFormat.format(d1);
        System.out.println("当前时期= " + format);

        //指定毫秒数获取时间
        Date d2 = new Date(1991991919);
        System.out.println(d2);

        //1. 可以把一个格式化的 String 转成一个对应的 Date
        //2. 得到Date 仍然在输出时,按照国外形式,如果希望指定格式输出,需要使用 SimpleDateFormat 转换
        //3. 在把String ——> Date 使用的sdf 格式需要和你给出的String格式一样,否则会抛出转换异常
        String s = "1996年02月26日 10:20:36 星期六";
        Date parse = simpleDateFormat.parse(s);//alt + enter 快捷键抛出异常
        System.out.println("parse" + parse);
        System.out.println("转回= " + simpleDateFormat.format(parse));
    }
}
第二代日期类

第二代日期类,主要是 Calender 类

1】public abstract class Calender extends Object implements Serializable, Cloneable, Comparable

2】Calender 类是一个抽象类,它为特定瞬间与一组诸如 YEAR、MOUTH、DAY_OF_MOUTH、HOUR等日历字段之间的转换提供了一些方法,并且为操作日历字段(了例如获得下星期的日期)提供了一些方法

package Date_.Calender_;

import java.util.Calendar;

public class Calender01 {
    public static void main(String[] args) {
        //1. Calender 是一个抽象类,并且构造器是 protected
        //2. 可以通过 getInstance() 来获取实例
        //3. 提供大量方法和字段
        //4. Calender 没有提供对应的格式化类,因此需要程序员自己组合来输出(灵活)
        //5. 如果需要按照24小时制来获取时间 Calendar.HOUR ==改成==> Calendar.Hour_OF_
        Calendar c = Calendar.getInstance();//创建日历类的对象
        System.out.println("c=" + c);
        //获取日历类的某个日历字段
        System.out.println("年:" + c.get(Calendar.YEAR));
        //因为 Calender 返回月的时候,是按照 0 开始编号,所以+1
        System.out.println("月:" + (c.get(Calendar.MONTH) + 1));
        System.out.println("日:" + c.get(Calendar.DAY_OF_MONTH));
        System.out.println("小时:" + c.get(Calendar.HOUR));
        System.out.println("分钟:" + c.get(Calendar.MINUTE));
        System.out.println("秒:" + c.get(Calendar.SECOND));
        //Calender 没有专门的格式化方法,所以需要程序员自己组合显示
        System.out.println(c.get(Calendar.YEAR) + "-" + (c.get(Calendar.MONTH)+1) + "-"
        + c.get(Calendar.DAY_OF_MONTH) + "-" + c.get(Calendar.HOUR_OF_DAY) + "时");
        
    }
}
第三代日期类

1】LocalDate (日期/年月日):只包含日期,可以获取日期字段【以下方法JDK8加入】

2】LocalTime(时间/时分秒):只包含时间,可以获取时间字段

3】LocalDateTime :包含日期+时间,可以获取日期和时间字段

package LocalDate;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoField;

public class LocalDate_ {
    public static void main(String[] args) {
        // 使用 now() 返回当前日期对象
        LocalDateTime ldt = LocalDateTime.now();//LocalDate.now;//LocalTime.now;
        System.out.println(ldt);

        System.out.println("年=" + ldt.getYear());
        System.out.println("月=" + ldt.getMonth());
        System.out.println("月=" + ldt.getMonthValue());//获取月份的数字
        System.out.println("日=" + ldt.getDayOfMonth());
        System.out.println("时=" + ldt.getHour());
        System.out.println("分=" + ldt.getMinute());
        System.out.println("秒=" + ldt.getSecond());

        LocalDate now = LocalDate.now();//获取年月日
        System.out.println(now.getYear());

        LocalTime now2 = LocalTime.now();//获取时分秒
        System.out.println(now2.get(ChronoField.HOUR_OF_AMPM));
        System.out.println(now2.get(ChronoField.HOUR_OF_DAY));
    }
}

4】DateTimeFormatter 格式日期类

类似于SimpleDateFormat

DateTimeFormatter dtf = DateTimeFormatter.ofPattern(格式);
String str = dtf.format(日期对象);
DateTimeFormatter d = DateTimeFormatter.ofPattern("yyyy年MM月 HH时mm分ss秒");
        String format = d.format(ldt);
        System.out.println("格式化后的日期=" + format);

5】Instant 时间戳

package Instant_;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.time.Instant;

public class Instant_ {
    public static void main(String[] args) {
        //1. 通过 静态方法 now() 获取表示当前时间戳的对象
        Instant now = Instant.now();
        System.out.println(now);
        //2.通过 from 可以把 Instant 转成 Date
        Date date = Date.from(now);
        //3. 通过 date 的 toInstant() 可以把 date 转成 Instant
        Instant instant = date.toInstant();


        LocalDateTime ldt = LocalDateTime.now();
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        //4.提供 plus 和 minus 方法可以对当前时间进行加或者减
        //898天后,是什么时间
        LocalDateTime localDateTime = ldt.plusDays(898);
        System.out.println("898天后=" + dateTimeFormatter.format(localDateTime));
        
        //3456分钟之前
        LocalDateTime localDateTime2 = ldt.minusMinutes(3456);
        System.out.println("3456分钟前=" + localDateTime2);

    }
}

本章练习 String翻转
package homework_;

public class homework01 {
    public static void main(String[] args) {
        String arr = "abcdefghi";
        System.out.println("===交换前===");
        System.out.println(arr);
        try {
            arr = reverse(arr, 1, 4);
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return;
        }
        System.out.println("===交换后===");
        System.out.println(arr);
    }

    public static String reverse(String str, int start, int end) {

        // 对输入的参数做一个验证
        //(1) 写出正确的情况
        //(2) 然后取反
        if (! (str != null && start >= 0 && end < str.length() && end > start)) {
            throw new RuntimeException("参数传入不正确");
        }
        char[] chars = str.toCharArray();
        char temp;
        for (int i = start, j = end; i < j; i++, j--) {
            temp = chars[i];
            chars[i] = chars[j];
            chars[j] = chars[i];
        }
        return new String(chars);
    }
}
注册处理
package homework_;

import java.util.Scanner;

public class homework02 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("======注册页面========");
        System.out.println("请设置用户名");
        String userName = scanner.next();
        System.out.println("请设置密码");
        String passWord = scanner.next();
        System.out.println("请输入email");
        String emailxxx = scanner.next();

        try {
            userRegister(userName, passWord, emailxxx);
            System.out.println("注册成功!!!");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

    }

    public static void userRegister(String name, String pwd, String email) {

        if (! (name == null && pwd == null && email == null) ) {
            throw new RuntimeException("参数不能为空!");
        }
        
        int userLength = name.length();
        if (! (userLength >= 2 && userLength <= 4 )) {
            throw new RuntimeException("用户名长度为2或3或4");
        }

        if (! (pwd.length() == 6 && isDigital(pwd)) ) {
            throw new RuntimeException("密码长度为6,且必须为数字");
        }
        int i = email.indexOf('@');
        int j = email.indexOf('.');
        if (! (i > 0 && j > i)) {
            throw new RuntimeException("email 必须包含@ 且@ 在. 前");
        }
    }

    public static boolean isDigital(String str) {
        char[] chars = str.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] < '0' && chars[i] > '9') {
                return false;
            }
        }
        return true;
    }
}
字符串统计
package homework_;

public class homework03 {
    public static void main(String[] args) {
        String name = "Han Shun Ping";
        changeForm(name);
    }
    public static void changeForm(String name) {
        if (name == null) {
            System.out.println("不能为空");
            return;
        }
        String[] names = name.split(" ");
        if (names.length != 3) {
            System.out.println("输入格式不正确 xxx xxx xxx");
            return;
        }
        String format = String.format("%s,%s .%c", names[2], names[0], names[1].toUpperCase().charAt(0));
        System.out.println(format);
    }
}
package homework_;

public class homework04 {
    public static void main(String[] args) {
        String test = "ZheJiang University 985 211";
        method01(test);

    }
    public static void method01(String str) {
        if (str == null) {
            System.out.println("不能为空");
            return;
        }
        int AWord = 0;
        int aword = 0;
        int number = 0;
        int Other = 0;
        char[] chars = str.toCharArray();
        for (int i = 0; i <= chars.length - 1; i++) {
            if (chars[i] >= '0' && chars[i] <= '9') {
                number++;
            } else if (chars[i] >= 'a' && chars[i] <= 'z') {
                aword++;
            } else if (chars[i] >= 'A' && chars[i] <= 'Z') {
                AWord++;
            } else {
                Other++;
            }
        }
        System.out.println("数字有" + number + "个");
        System.out.println("大写字母有" + AWord + "个");
        System.out.println("小写字母有" + aword + "个");
        System.out.println("其他类型有" + Other + "个");
    }
}

集合

集合体系框架图
  1. 集合主要是两组(单列集合、双列集合)
  2. Collection 接口有两个重要的子接口 List Set ,他们的实现子类都是单列集合
  3. Map 接口的实现子类 是双列集合,存放 K-V 键值对

Collection接口 Collection 接口实现类的特点
  1. Collection 实现类可以存放多个元素,每个元素可以是 Object
  2. 有些 Collection 的实现类,可以存放重复的元素,有些不可以
  3. 有些 Collection 的实现类,有些是有序的(List),有些不是有序的(Set)
  4. Collection 接口没有直接的实现子类,是通过它的子接口Set 和 List 来实现的
Collection 接口和常用方法

package collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class Collection_ {
    //以 ArrayList 实现类来演示
    @SuppressWarnings("all")
    public static void main(String[] args) {
        //add 添加单个元素
        List list = new ArrayList();
        list.add("三国");
        list.add("水浒");
        list.add(true);
        list.add(100);
        System.out.println(list);

        //remove 删除指定元素
//        list.remove("三国");
        list.remove(3);  //按照索引删除
        System.out.println(list);

        //contains 查找元素是否存在
        System.out.println(list.contains(true));//true
        System.out.println(list.contains(100));//false

        //size 获取元素个数
        System.out.println(list.size());//3

        //isEmpty 判断是否为空
        System.out.println(list.isEmpty());//false

        //clear 清空
        list.clear();
        System.out.println(list);//[]

        //addAll 添加多个元素
        ArrayList list2 = new ArrayList();
        list2.add("斗罗大陆");
        list2.add(200);
        list.addAll(list2);
        System.out.println(list);

        //containsAll 查找多个元素是都都存在
        System.out.println(list.containsAll(list2));//true

        //removeAll 删除多个元素
        list.removeAll(list2);
        System.out.println(list);//[]

    }
}
Collection 接口遍历元素的方式

1】使用 Iterator(迭代器)

基本介绍

  1. Iterator 对象称为迭代器,主要用于遍历 Collection 集合中的元素
  2. 所有实现了 Collection接口的集合类都有一个 iterator () 方法,用于返回一个实现了 Iterator 接口的对象,即可以返回一个迭代器
  3. Iterator 的结构
  4. Iterator 仅用于遍历集合,Iterator 本身并不存放对象

**【注意】**在调用 iterator.next() 方法之前必须调用 iterator.hasNext() 进行检测,若不调用,且下一条记录无效,直接调用 iterator.next() 会抛出 NoSuchElementException 异常

package collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class iterator_ {
    public static void main(String[] args) {

        Collection list = new ArrayList();
        list.add(new Dog("罗威纳", 4000));
        list.add(new Dog("边牧", 2000));
        list.add(new Dog("柯基", 1000));
        System.out.println(list);

        //现在遍历 list 集合
        //1. 先得到 list 对应的 迭代器
        //2. 使用 while 循环遍历
        Iterator iterator = list.iterator();
        //itit ——> 快速生成 while
        // ctrl + j 显示所有快捷方式
        while (iterator.hasNext()) {//判断是否还有数据
            //返回下一个元素,类型是Object
            Object dogs = iterator.next();
            System.out.println("dogs=" + dogs);
        }
        //3. 当退出 while 循环后,这时iterator 迭代器指向最后一个元素
        //   iterator.next(); --> NoSuchElementException
        //4. 如果希望再次遍历,需要重置迭代器
        iterator = list.iterator();
        System.out.println("====第二次遍历===");
        while (iterator.hasNext()) {
            Object dogs2 = iterator.next();
            System.out.println("dogs2=" + dogs2);
        }
    }
}
class Dog {
    private String name;
    private int price;

    public Dog(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

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

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + ''' +
                ", price=" + price +
                '}';
    }
}

2】for 循环增强

增强 for循环,可以代替 iterator 迭代器,特点:增强 for 就是简化版的 iterator,本质一样,只能用于遍历经济和或数据

基本语法

for(元素类型 元素名 :集合或数组名) {
	访问元素
}
package collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class CollectionFor_ {
    public static void main(String[] args) {
        Collection list = new ArrayList();
        list.add(new Dog("罗威纳", 4000));
        list.add(new Dog("边牧", 2000));
        list.add(new Dog("柯基", 1000));

        //使用增强for
        //1. 增强for 底层仍然是迭代器
        //2. 增强for可以理解成就是简化版的 迭代器遍历
        //3. 快捷方式 I
        for (Object dogs : list) {
            System.out.println("dogs=" + dogs);
        }
        
        //增强for 也可以直接在数组使用
        int[] nums = new int[3];
        nums[0] = 10;
        nums[1] = 20;
        nums[2] = 30;
        for (int i : nums) {
            System.out.println(i);
        }
    }
}
List接口 List接口基本介绍

List 接口是 Collection 接口的子接口

  1. List 集合类中元素有序(即添加顺序和取出顺序一致)且可重复
  2. List 集合中的每个元素都有其对应的顺序索引,即支持索引
  3. List 容器中的元素都对应的一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
  4. JDK API 中的 List 接口的实现类有:Vector、ArrayList、LinkedList 等等还有很多
package list;

import java.util.ArrayList;

public class list_ {
    public static void main(String[] args) {
        //1. List 集合类中元素有序(即添加顺序和取出顺序一致)且可重复
        ArrayList arrayList = new ArrayList();
        arrayList.add("jack");
        arrayList.add("tom");
        arrayList.add("jam");
        arrayList.add("mary");
        arrayList.add("tom");
        System.out.println(arrayList);
        //2. List 集合中的每个元素都有其对应的顺序索引,即支持索引
        //   索引从 0 开始
        System.out.println(arrayList.get(3));//mary
    }
}
List接口常用方法

package list;

import java.util.ArrayList;
import java.util.List;

public class ListMethod {
    public static void main(String[] args) {
        List l = new ArrayList();
        l.add("唐三");
        l.add("奥斯卡");
        //1. void add(int index, Object ele) 在index位置插入ele元素
        //   在index = 1 的位置插入
        l.add(1,"马红俊");
        System.out.println(l);

        //2. boolean addAll(int index, Collection eles) 从index位置开始将 eles中所有元素添加进来
        ArrayList l2 = new ArrayList();
        l2.add("jack");
        l2.add("tom");
        //   在index = 2 的位置 插入
        l.addAll(2, l2);
        System.out.println(l);

        //3. Object get(int index) 获取指定index 位置的的元素
        System.out.println(l.get(4));//奥斯卡

        //4. int indexOf(Object obj) 返回obj在集合中首次出现的位置
        l.add("唐三");
        System.out.println(l.indexOf("唐三"));//0

        //5. int lastIndexOf(Object obj) 返回obj 在当前集合中末次出现的位置
        System.out.println(l);
        System.out.println(l.lastIndexOf("唐三"));//5

        //6. Object remove(int index) 移除指定的index位置的元素,并且返回此元素
        l.remove(3);
        System.out.println(l);

        //7. Object set(int index, Object ele) 设置指定的index位置的的元素ele 【相当于替换】
        l.set(0, "泰坦巨猿");
        System.out.println(l);

        //8. List subList(int fromIndex, int toIndex) 返回从fromIndex 到 toIndex位置的子集合
        //   返回的自集合 【fromIndex <= xxx < toIndex】
        List returnList = l.subList(2, 4);
        System.out.println("returnList=" + returnList);//jack 奥斯卡
    }
}
练习
package list;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ListExercise {
    public static void main(String[] args) {
        List l = new ArrayList();
        //添加10个以上元素
        for (int i = 0; i < 12; i++) {
            l.add("luck" + i);
        }
        System.out.println("list02=" + l);

        //在2号位插入一个一个元素
        l.add(1, "哈哈哈哈哈哈");
        System.out.println(l);

        //获得第5个元素
        System.out.println(l.get(4));

        //删除第6个元素
        System.out.println(l.remove(5));

        //修改第7个元素
        l.set(6, "wonderful哈哈哈");
        System.out.println(l);

        //使用迭代器遍历集合
        Iterator iterator = l.iterator();
        while (iterator.hasNext()) {
            Object obj = iterator.next();
            System.out.println("obj=" + obj);
        }
    }
}
List的三种遍历方式
package list;

import java.util.*;

public class ListFor {
    public static void main(String[] args) {
        //List 接口实现子类 Vector LinkedList
//        List list = new Vector();
//        List list = new LinkedList();
        List list = new ArrayList();
        list.add("tom");
        list.add("jack");
        list.add("鱼香肉丝");
        list.add("糖醋里脊");

        //遍历
        //1. 迭代器
        Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
            Object obj = iterator.next();
            System.out.println("obj=" + obj);
        }

        //2. 增强for循环
        System.out.println("===增强for===");
        for (Object o : list) {
            System.out.println("o=" + o);
        }

        //3. 普通for循环
        System.out.println("===普通for===");
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}
练习
package list;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

@SuppressWarnings("all")
public class ListExercise02 {
    public static void main(String[] args) {
        List arrayList = new ArrayList();
//        List linkedList = new LinkedList();
//        List VectorList = new Vector();
        arrayList.add(new Book("红楼梦", 90, "曹雪芹"));
        arrayList.add(new Book("三国", 60, "罗贯中"));
        arrayList.add(new Book("水浒", 80, "施耐庵"));
        arrayList.add(new Book("西游记", 10, "吴承恩"));


        //冒泡排序
        sort(arrayList);
        for (Object o : arrayList) {
            System.out.println(o);
        }

    }

    //静态方法
    public static void sort(List list) {
        int temp = 0;
        int listSize = list.size();
        for (int i = 0; i < listSize - 1; i++) {
            for (int j = 0; j < listSize - 1 - i; j++) {
                //取出对象
                Book book1 = (Book) list.get(j);
                Book book2 = (Book) list.get(j + 1);
                if (book1.getPrice() > book2.getPrice()) {
                    list.set(j, book2);
                    list.set(j + 1, book1);
                }
            }
        }
    }
}

class Book {
    private String name;
    private int price;
    private String author;

    public Book(String name, int price, String author) {
        this.name = name;
        this.price = price;
        this.author = author;
    }

    public String getName() {
        return name;
    }

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

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    @Override
    public String toString() {
        return "n名称:" + name + "tt价格:" + price + "tt作者:" + author;
    }
}
ArrayList 类

注意事项

  1. premits all elements ,including null , ArrayList 可以存放 null 并且是多个
  2. ArrayList 是由数组来实现数据存储的
  3. ArrayList 基本等同于 Vector ,除了ArrayList 是线程不安全的(执行效率高)在多线程情况下,不建议使用ArrayList
ArrayList 底层结构和源码分析【重点】
  1. ArrayList 中维护了一个Object 类型的数组 elementData 【transient Object[] elementData;】

    transient 修饰的变量 不会被序列化

  2. 当创建 ArrayList 对象时,如果使用的是无参构造器,则初始 elementData 容量为0 ,第一次添加,则扩容 elementData 为 10, 如果需要再次扩容,则扩容 elementData 为1.5倍

  3. 如果使用的是指定大小的构造器,则初始 elementData 容量为指定大小,如果需要扩容,则直接扩容 elementData 为1.5倍

调用ArrayList有参构造器

Vector 类
  1. Vector基本介绍

    public class Vector
        extends AbstractList
        implements List, RandomAccess, Cloneable, java.io.Serializa
    
  2. Vector 底层也是一个对象数组 protected Object[] elementData;

  3. Vector 是线程同步的,即线程安全,Vector类的操作方法带有synchronized

  4. 在开发中需要线程同步安全的,考虑使用Vector

Vector底层结构

Vector底层结构和ArrayList的比较

Vector底层结构源码分析

调用Vector有参构造器

LinkedList 类 LinkedList 基本介绍

底层结构

  1. LinkedList 实现了双向链表和双端队列特点
  2. 可以添加任意元素(元素可以重复)包括null
  3. 线程不安全,没有实现同步

双向列表模拟

package List_LinkedList;

public class LinkedList {
    public static void main(String[] args) {
        //模拟一个简单的双向链表
        Node jack = new Node("jack");
        Node tom = new Node("tom");
        Node ham = new Node("ham");

        //连接三个结点,形成双向链表
        //jack --> tom --> ham
        jack.next = tom;
        tom.next = ham;
        //ham --> tom --> jack
        ham.pre = tom;
        tom.pre = jack;

        Node first = jack;//让first引用指向jack,就是双向链表的头结点
        Node last = ham;//让last引用指向ham, 就是双向链表的尾结点
        
//===========================================================
        //从头到尾遍历
        System.out.println("===从头到尾遍历===");
        while (true) {
            if (first == null) {
                break;
            }
            //输出first 信息
            System.out.println(first);
            first = first.next;
        }
        System.out.println("===从尾到头遍历===");
        while (true) {
            if (last == null) {
                break;
            }
            //输出first 信息
            System.out.println(last);
            last = last.pre;
        }
        
//===========================================================
        //jack 和 tom 之间插入 关羽
        //1. 先创建一个 Node 结点,name 就是 关羽
        Node guanyu = new Node("关羽");
        guanyu.next = tom;
        guanyu.pre = jack;
        jack.next = guanyu;
        tom.pre = guanyu;
        //让first 再次指向 jack
        first = jack;
        System.out.println("===插入信息后 从头到尾遍历===");
        while (true) {
            if (first == null) {
                break;
            }
            //输出first 信息
            System.out.println(first);
            first = first.next;
        }
        //让last 指向 ham
        last = ham;
        System.out.println("===插入信息后 从尾到头遍历===");
        while (true) {
            if (last == null) {
                break;
            }
            //输出last信息
            System.out.println(last);
            last = last.pre;
        }
    }
}

class Node {
    public Object item;//真正存放数据的地方
    public Node pre;//指向上一个结点
    public Node next;//指向后一个结点

    public Node(Object name) {
        this.item = name;
    }

    @Override
    public String toString() {
        return "Node name=" + item;
    }
}
LinkedList 底层结构

增删改查CRUD

LinkedList 增

package List_LinkedList;

import java.util.LinkedList;
@SuppressWarnings("all")
public class LinkedListCRUD {
    public static void main(String[] args) {
        LinkedList linkedList = new LinkedList();
        linkedList.add(1);
        linkedList.add(2);
        System.out.println(linkedList);

        
    }
}

LinkedList 删

package List_LinkedList;

import java.util.LinkedList;
@SuppressWarnings("all")
public class LinkedListCRUD {
    public static void main(String[] args) {
        LinkedList linkedList = new LinkedList();
        linkedList.add(1);
        linkedList.add(2);
        System.out.println(linkedList);

        linkedList.remove();//删除第一个元素

        

LinkedList 改

LinkedList 查

LinkedList 类的三种遍历方式
  1. 迭代器
  2. 增强for循环
  3. 普通for循环
//因为LinkedList 是 实现了List接口,所以其遍历方式:
        //1. 迭代器
        Iterator iterator = linkedList.iterator();
        while (iterator.hasNext()) {
            Object obj = iterator.next();
            System.out.println(obj);
        }
        //2. 增强for循环
        for (Object o : linkedList) {
            System.out.println(o);
        }
        //3。 普通for循环
        for (int i = 0; i 
            System.out.println(linkedList.get(i));
        }
ArrayList 和 LinkedList 比较

如何选择:

  1. 如果改查操作多,选择ArrayList
  2. 如果增删操作多,选择LinkedList
  3. 一般来说,在程序中,80%-90%都是查询,所以大部分情况选择ArrayList
  4. 在一个项目中,根据业务灵活选择,可能一个模块使用ArrayList 另一个模块 使用LInkedList
  5. 两个线程都是不安全的
Set接口
  1. 无序(添加和取出的顺序不一致),没有索引
  2. 不允许重复元素,所以最多包含一个null
  3. JDK API 中的set接口实现类有:

Set接口的常用方法

和List接口一样,Set接口也是Collection的子接口,因此,常用方法和 Collection接口一样

Set接口的遍历方式

同Collection的遍历方式一样,因为Set接口是实现了Collection接口的子接口

  1. 迭代器
  2. 增强for
  3. 不能使用索引的方式来获取
package Set;

import java.util.HashSet;
import java.util.Iterator;

public class SetMethod {
    public static void main(String[] args) {
        //1. 以Set 接口的实现类 HashSet 来解释Set 接口的方法
        //2. set 接口的实现类的对象(Set接口对象),不能存放重复元素,可以存放null
        //3. set 接口的对象存放数据是无序的(即添加顺序和取出顺序不一致)
        //4, 注意:取出顺序和添加顺序虽不一致,但取出顺序是固定的
        HashSet hashSet = new HashSet();
        hashSet.add("jack");
        hashSet.add("lara");
        hashSet.add("mary");
        hashSet.add("lara");//重复元素
        hashSet.add(null);
        hashSet.add(null);
        System.out.println("hashset=" + hashSet);


        //遍历方式
        //1. 迭代器
        Iterator iterator = hashSet.iterator();
        while (iterator.hasNext()) {
            Object obj = iterator.next();
            System.out.println("obj=" + obj);
        }

        //2. 增强for
        //   增强for底层源码调用的是迭代器
        for (Object o : hashSet) {
            System.out.println("o=" + o);
        }
        //set接口对象不能通过索引来获取【不能使用普通for】
    }
}
HashSet 类
  1. HashSet 实现了Set 接口

  2. HashSet 实际上是 HashMap

    public HashSet() {
            map = new HashMap<>();
        }
    
  3. 可以存放 null 值,但只能有一个null

  4. HashSet 不保证元素是有序的,取决hash后,再确定索引的结果【即, 不保证存放元素的顺序和取出顺序一致】

  5. 不能有重复元素/对象

package Set_HashSet;

import java.util.HashSet;
import java.util.Set;

@SuppressWarnings("all")
public class HashSet01 {
    public static void main(String[] args) {
        Set set = new HashSet();
        System.out.println(set.add("jack"));//T
        System.out.println(set.add("jack"));//false
        System.out.println(set.add("mary"));//T
        System.out.println(set.add("tom"));//T
        System.out.println(set.add(null));//T
        System.out.println(set.add(null));//false

        set = new HashSet();
        System.out.println(set);//[]
        //HashSet 不能添加相同的元素
        set.add("lucy");//添加陈功
        set.add("lucy");//失败
        set.add(new Dog("puppy"));//ok
        set.add(new Dog("puppy"));//ok
        System.out.println("set=" + set);

        //面试题
        //查看add源码
        set.add(new String("hello"));//ok
        set.add(new String("hello"));//失败
        System.out.println("面试题的set=" + set);
    }
}
class Dog {
    private String name;

    public Dog(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + ''' +
                '}';
    }
}
HashSet底层机制

HashSet 底层HashMap, HashMap底层是**(数组+链表+红黑树)**

package Set_HashSet;

public class HashSet02 {
    public static void main(String[] args) {
        //模拟一个 HashSet的底层(HashMap 底层结构)

        //1. 创建一个数组,数组类型是 Node[]
        //2. Node[] 数组 称为 table(表)
        Node[] table = new Node[16];
        System.out.println("table=" + table);
        //3, 创建结点
        Node john = new Node("john", null);
        table[2] = john;
        Node jack = new Node("jack", null);
        john.next = jack; //将 jack结点 挂载到 john
        Node lucy = new Node("lucy", null);
        jack.next = lucy; //将lucy结点 挂载到 jack

        Node rose = new Node("Rose", null);
        table[3] = rose; //把rose放到索引为3的位置

        System.out.println("table=" + table);
    }
}
class Node { //结点,存储数据,可以指向下一个结点,从而形成链表
    Object item; //存放数据
    Node next; //可以指向下一个结点

    public Node(Object item, Node next) {
        this.item = item;
        this.next = next;
    }
}

分析HashSet的添加元素底层是如何实现的

分析HashSet的扩容机制和转成红黑树的机制

package Set_HashSet;

import java.util.HashSet;
@SuppressWarnings("all")
public class HashSet04 {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        for (int i = 0; i <= 7; i++) {
            hashSet.add(new B(i));
        }
        for (int i = 0; i <= 7; i++) {
            hashSet.add(new C(i));
        }
    }
}

class B {
    private int n;

    public B(int n) {
        this.n = n;
    }

    @Override
    public int hashCode() {
        return 100;
    }
}
class C {
    private int n;

    public C(int n) {
        this.n = n;
    }

    @Override
    public int hashCode() {
        return 200;
    }
}

分析HashSet的添加元素底层如何实现【 hash() + equals() 】

package Set_HashSet;

import java.util.HashSet;
import java.util.Objects;

public class HashSetExercise {
    public static void main(String[] args) {
        HashSet set = new HashSet();
        set.add(new Employee("jack", 10));
        set.add(new Employee("jack", 20));
        set.add(new Employee("jack", 10));
        System.out.println(set);
    }
}
class Employee {
    private String name;
    private int age;

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee employee = (Employee) o;
        return age == employee.age && Objects.equals(name, employee.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

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

思考题

package Set_HashSet;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@SuppressWarnings("all")
public class HashSetExercise02 {
    public static void main(String[] args) {
        Set hashSet = new HashSet();
        hashSet.add(new Employee_("jack", 2000, new MyDate(2010, 2, 14)));
        hashSet.add(new Employee_("tom", 2000, new MyDate(2020, 3, 24)));
        hashSet.add(new Employee_("jack", 2000, new MyDate(2010, 2, 14)));
        System.out.println(hashSet);
    }
}
class Employee_ {
    private String name;
    private double sal;
    private MyDate birthday;

    public Employee_(String name, double sal, MyDate birthday) {
        this.name = name;
        this.sal = sal;
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "Employee_{" +
                "name='" + name + ''' +
                ", sal=" + sal +
                ", birthday=" + birthday +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee_ employee_ = (Employee_) o;
        return Objects.equals(name, employee_.name) && Objects.equals(birthday, employee_.birthday);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, birthday);
    }
}
class MyDate {
    private Integer year;
    private Integer month;
    private Integer day;

    public MyDate(Integer year, Integer month, Integer day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    @Override
    public String toString() {
        return year + "年" + month + "月" + day + "日";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MyDate myDate = (MyDate) o;
        return Objects.equals(year, myDate.year) && Objects.equals(month, myDate.month) && Objects.equals(day, myDate.day);
    }

    @Override
    public int hashCode() {
        return Objects.hash(year, month, day);
    }
}
LinkedHashSet 类
  1. LinkedHashSet 是 HashSet 的子类

  2. LinkedHashSet 底层是一个 LinkedHashMap,底层维护了一个 (数组 + 双向链表)

  3. LinkedHashSet 根据元素的 hashCode 值来决定元素的储存位置,同时使用链表维护元素的次序,这使得元素看起来是以插入的顺序保存的

    【加入顺序和取出顺序一致】

  4. LinkedHashSet 不允许添加重复元素

LinkedHashSet底层机制

package LindedHashSet_;

import java.util.LinkedHashSet;
import java.util.Set;
@SuppressWarnings("all")
public class LinkedHashSetSource {
    public static void main(String[] args) {
        Set set = new LinkedHashSet();
        set.add(new Person("AAA", 120));
        set.add(666);
        set.add(666);
        set.add(new String("CCC"));
        set.add("hsp");
        System.out.println("set=" + set);

        //1. LinkedHashSet 加入顺序和取出元素/数据顺序一致
        //2. LinkedHashSet 底层维护的是一个 LinkedHashMap(是HashMap的子类)
        //3. LinkedHashSet 底层结构 (数组table + 双向链表)
        //4. 添加第一次时,直接将 数组table 扩容到16, 存放的结点类型是 LinkedHashMap$Entry
        //5. 数组是 HashMap$Node[] 存放的元素/数据是 LinkedHashMap$Entry 类型
        
    }
}
class Person {
    private String name;
    private int num;

    public Person(String name, int num) {
        this.name = name;
        this.num = num;
    }
}
package LindedHashSet_;

import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;

public class LinkedHashSetExercise {
    public static void main(String[] args) {
        Set set = new LinkedHashSet();
        set.add(new Car("保时捷", 1000));//oK
        set.add(new Car("宝马", 500));//ok
        set.add(new Car("保时捷", 1000));//ok——> 重写hashCode 和 equals 方法后 添加失败
        set.add(new Car("奔驰", 600));//ok
        System.out.println("set=" + set);
    }
}
class Car {
    private String name;
    private double price;

    public Car(String name, double price) {
        this.name = name;
        this.price = price;
    }

    @Override
    public String toString() {
        return "nCar{" +
                "name='" + name + ''' +
                ", price=" + price +
                '}';
    }

//    @Override
//    public boolean equals(Object o) {
//        if (this == o) return true;
//        if (o == null || getClass() != o.getClass()) return false;
//        Car car = (Car) o;
//        return Double.compare(car.price, price) == 0 && Objects.equals(name, car.name);
//    }

    @Override
    public int hashCode() {
        return Objects.hash(name, price);
    }

}
TreeSet 类
package Set_TreeSet;

import java.util.Comparator;
import java.util.TreeSet;

public class TreeSet1 {
    public static void main(String[] args) {
        //调用无参构造器 创建TreeSet时【无序】
//        TreeSet treeSet = new TreeSet();

        //使用TreeSet 提供的一个构造器,可以传入一个比较器(匿名内部类)
        //并指定排序规则
        //根据字符先后排序
        TreeSet treeSet = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                //下面 调用String的 compareTo方法,默认比较 字符的大小
               return ((String)o2).compareTo((String)o1);
            }
        });
        //添加数据
        treeSet.add("jack");
        treeSet.add("tom");
        treeSet.add("sp");
        treeSet.add("a");
        System.out.println(treeSet);

        //1. 构造器把传入的比较对象,赋给 TreeSet的底层的 TreeMap属性
        
        //2. 在调用 treeSet.add("tom"),在底层会执行到 以下代码
        
    }
}
package Set_TreeSet;

import java.util.Comparator;
import java.util.TreeSet;

public class Tree_SetExercise {
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                //根据字符长度排序
                return ((String)o2).length() - ((String)o1).length();
            }
        });

        treeSet.add("jack");//length = 4
        treeSet.add("tim");
        treeSet.add("wo");
        treeSet.add("j");
        treeSet.add("mary");//length = 4 不能加进去  因为此时比较结果 = 0
        System.out.println(treeSet);
    }
}
Map接口

  1. Map与Collection 并列存在,用于保存具有映射关系的数组:key-value
  2. Map 中的 key 和 value 可以是任何引用类型的数据,会封装到HashMap$Node 对象中
  3. Map 中的 key 不允许重复【原因和 HashSet 一样】
  4. Map 中的 value 可以重复
  5. Map 的 key 可以为 null ,value 也可以为 null ,注意 key 为 null 只能有一个, value 为 null 可以多个
  6. 常用String 类作为 Map 的 key
  7. key 和 value 之间存在单向一对一的关系,即通过指定的 key 总能找到对应的 value
package Map_;

import java.util.HashMap;
import java.util.Map;
@SuppressWarnings("all")
public class Map01 {
    public static void main(String[] args) {
        Map map = new HashMap();//k-v
        map.put("No1", "王师傅");//k-v
        map.put("No2","刘守元");//k-v
        map.put("No1", "鸭子");//当有相同的Key时,就等价于替换
        map.put("No3", "鸭子");//k-v  Map 中 value 可以重复
        map.put(null, null);//k-v
        //key 只能有一个 null ,value 可以多个 null
        map.put(null, "郭艾伦");//等价替换
        map.put("No4", null);//k-v
        map.put("No5", null);//k-v
        //常用String 类作为 Map 的 key
        Object obj = new Object();
        map.put(obj,"hello world");//k-v
        map.put(1, "大耳朵图图");

        //通过get方法,传入 key ,会返回对应的 value
        System.out.println(map.get(obj));
        System.out.println(map);
    }
}
  1. Map 存放数据的 key-value 示意图,一对 k-v 是放在一个HashMap$Node 中的,又因为Node 实现了 Entry 接口
底层结构

package Map_;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@SuppressWarnings("all")
public class MapSource_ {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("No1", "詹姆斯");
        map.put("No2", "科比");
        map.put("No3", "乔丹");

        //1. k-v 最后是 HashMap$Node nobe = newNode(hash, key, value, null)
        //2. k-v 为了啊方便程序员遍历,创建了 EntrySet 集合, 该集合存放的元素的类型 Entry
        //   而一个 Entry 对象就有k, v
        //   即:transient Set> entrySet;
        //3. entrySet 中,定义的类型是 Map.Entry, 但是实际上存放的还是 HashMap$Node
        //   因为 static class Node implements Map.Entry

        Set set = map.entrySet();
        System.out.println(set.getClass());
        for (Object obj : set) {
            //System.out.println(obj.getClass()); //HashMap$Node
            //为了从 HashMap$Node 取出 k-v
            //1. 先向下转型
            Map.Entry entry = (Map.Entry) obj;
            System.out.println(entry.getKey() + "-" + entry.getValue());
        }

        Set set1 = map.keySet();//存放所有数据的 key
        System.out.println(set1);
        Collection values = map.values();//存放所有数据的 value
        System.out.println(values);
    }
}
Map接口的常用方法

package Map_;

import java.util.HashMap;
import java.util.Map;
@SuppressWarnings({"all"})
public class MapMethod {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("詹姆斯", new Team("热火", 5));
        map.put("詹姆斯", "湖人");
        map.put("塔图姆", "凯尔特人");
        map.put("库里", "勇士");
        map.put("克莱汤普森", "勇士");
        map.put(null, "篮网");
        map.put("东七七", null);
        System.out.println(map);

        //remove 根据键删除映射关系
        map.remove(null);
        System.out.println(map);

        //get(key) 根据键获取值
        System.out.println(map.get("詹姆斯"));//返回类型是 Object

        //size 获取元素个数
        System.out.println("k-v有= " + map.size());//5

        //isEmpty 判断个数是否为 0
        System.out.println(map.isEmpty());//boolean 类型

        //clear 清空
        map.clear();
        System.out.println(map);

        //containsKey 查找键是否存在
        System.out.println(map.containsKey("库里"));//true
        System.out.println(map.containsValue("篮网"));//F 已删除
    }
}
class Team {
    private String name;
    private int num;

    public Team(String name, int num) {
        this.name = name;
        this.num = num;
    }
}
Map接口的遍历方法

package Map_;

import java.util.*;

public class MapFor {
    public static void main(String[] args) {
        HashMap map = new HashMap();
        map.put("詹姆斯", "湖人");
        map.put("塔图姆", "凯尔特人");
        map.put("库里", "勇士");
        map.put("克莱汤普森", "勇士");
        map.put(null, "篮网");
        map.put("东七七", null);

        //第一组:先取出所有的Key ,通过 key 取出对应的Value;
        Set keyset = map.keySet();
        //(1) 增强for
        for (Object key : keyset) {
            System.out.println(map.get(key));
        }
        //(2) 迭代器
        Iterator iterator = keyset.iterator();
        while (iterator.hasNext()) {
            Object key = iterator.next();
            System.out.println(map.get(key));
        }

        //第二组:取出所有的values
        Collection values = map.values();
        //因为编译类型是 Collection 所以有2种方式遍历
        //(1) 迭代器
        System.out.println("===取出所有values 迭代器===");
        Iterator iterator1 = values.iterator();
        while (iterator1.hasNext()) {
            Object value1 = iterator1.next();
            System.out.println(value1);
        }
        //(2) 增强for
        System.out.println("===取出所有values 增强for===");
        for (Object value2 : values) {
            System.out.println(value2);
        }

        //第三组:通过EntrySet 来获取 k-v
        Set entrySet = map.entrySet();
        //(1) 增强for
        System.out.println("===通过EntrySet 增强for===");
        for (Object entry : entrySet) {
            //将entry 转成 Map.Entry
            Map.Entry m = (Map.Entry) entry;
            System.out.println(m.getKey() + "-" + m.getValue());
        }
        //(2) 迭代器
        System.out.println("===通过EntrySet 迭代器===");
        Iterator iterator2 = entrySet.iterator();
        while (iterator2.hasNext()) {
            Object entry2 = iterator2.next();
            //HashMap$Node--实现--> Map.Entry (getKey,getValue)
            //向下转向 Map.Entry
            Map.Entry m2 = (Map.Entry) entry2;
            System.out.println(m2.getKey() + "-" + m2.getValue());
        }
    }
}
练习
package Map_;

import java.util.*;

@SuppressWarnings("all")
public class MapExercise {
    public static void main(String[] args) {
        Map map = new HashMap();
        Worker jack = new Worker("jack", 19000, "2020");
        Worker mary = new Worker("mary", 6000, "2019");
        Worker tim = new Worker("tim", 18000, "2021");

        map.put(jack.getId(), jack);
        map.put(mary.getId(), mary);
        map.put(tim.getId(), tim);
        map.put(1, "larr");

        //第一种遍历方式
        System.out.println("===第一种遍历方式===");
        Set keySet = map.keySet();
        for (Object key : keySet) {
            if (map.get(key) instanceof Worker) {
                Worker w = (Worker) map.get(key);
                if (w.getSalary() > 10000) {
                    System.out.println(w);
                }
            }
        }

        //第二种遍历方式
        System.out.println("===第二种遍历方式===");
        Iterator iterator = keySet.iterator();
        while (iterator.hasNext()) {
            Object key = iterator.next();
            if (map.get(key) instanceof Worker) {
                Worker w = (Worker) map.get(key);
                if (w.getSalary() > 10000) {
                    System.out.println(w);
                }
            }
        }


        //第三种遍历模式
        System.out.println("===Entry 遍历方式===");
        Set entrySet = map.entrySet();
        if (entrySet instanceof Worker) {
            Iterator iterator2 = entrySet.iterator();
            while (iterator2.hasNext()) {
                Map.Entry entry = (Map.Entry) iterator2.next();
                Worker worker = (Worker) entry.getValue();
                if (worker.getSalary() > 10000) {
                    System.out.println(worker);
                }
            }
        }

        //第四种遍历方式
        System.out.println("===values 遍历方式=== ");
        Collection values = map.values();
        Iterator iterator1 = values.iterator();
        while (iterator1.hasNext()) {
            Object o1 = iterator1.next();
            if (o1 instanceof Worker) {
                Worker w1 = (Worker) o1;
                if (w1.getSalary() > 10000) {
                    System.out.println(w1);
                }
            }
        }
    }
}

class Worker {
    private String name;
    private double salary;
    private String id;

    public Worker(String name, double salary, String id) {
        this.name = name;
        this.salary = salary;
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public String getId() {
        return id;
    }

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

    @Override
    public String toString() {
        return "n姓名" + name + "t工资=" + salary + "t编号id " + id;
    }
}

HashMap类
  1. Map接口的常用实现类:HashMap、Hashtable 和 Properties
  2. HashMap 是 Map 接口使用最频率最高的实现类
  3. HashMap 是以 key-value对 的方式来存储数据的(HashMap$Node)
  4. key 不能重复,但是value 可以重复,允许使用null 键和 null值
  5. 如果添加相同的 key,则会覆盖原来的 key-value,等同于修改(key不会替换,value会替换)
  6. 与HashSet一样,不保证映射顺序,因为底层是以hash表的方式存储的(JDK8 的 HashMap 底层: 数组+ 链表 + 红黑树)
  7. HashMap没有实现同步,因此线程不安全 ,方法没有做同步互斥的操作,没有 synchronized
HashMap底层机制

扩容机制【和HashSe相同】

  1. HashMap底层维护了Node类型的数组 table,默认为null
  2. 当创建对象时,将加载因子(loadFactor)初始化为 0.75
  3. 当添加 key-value 时,通过 key 的哈希值得到 table 的索引,然后判断该索引处是否有元素,如果没有元素直接添加,如果该索引处有元素,继续判断该元素的 key 是否和准备加入的 key 相等,如果相等,则直接替换 value;如果不相等需要判断时树结构还是链表结构,做出相应处理,如果添加时返现容量不够,则需要扩容
  4. 第一次添加,则需要扩容 table 容量为16,临界值(threshold)12
  5. 以后再扩容,则需要扩容 table 容量为原来的 2 倍,临界值为原来的2倍(即24),依次类推
  6. 在Java8中,如果一条链表的元素个数超过了 TREEIFY_THRESHOLD ( 默认值为8 ),并且 table的大小 >= MIN_TREEIFY_CAPACITY (默认值为64) 就会进行树化(红黑树)
HashMap底层触发扩容、树化情况
0 --> 16 (12) --> 32 (24) --> 64 (64 * 0.75 = 48) --> 128 (96)

最大容量是 2 ^ 30 = 1,073,741,824

Hashtable类 基本介绍
  1. 存放的元素是键值对:即 K-V
  2. Hashtable的键和值都不能为null 【HashMap 允许键和值为null】
  3. Hashtable 使用方法基本上和 HashMap一样
  4. Hashtable 是线程安全的(synchronized),HashMap 是线程不安全的
Hashtable底层机制
package Map_MapTable;

import java.util.Hashtable;

public class HashTableExercise {
    public static void main(String[] args) {
        Hashtable table = new Hashtable();
        table.put("john", 100);//ok
        table.put("lucy", 100);//ok
        table.put("tim", 100);//ok
        table.put("tim", 60);//替换
        table.put("mary", null);//NullPointerException异常
        table.put(null, 100);//NullPointerException异常
        System.out.println(table);
        
        //Hashtable的底层机制
        //1. 底层有数组 Hashtable$Entry[] 初始化为 11
        //2. 临界值 threshold 8 --> 11 * 0.75
        //3. 按照它自己的扩容机制进行的
    }
}

Hashtable扩容机制

1. 执行方法 private void addEntry(int hash, K key, V value, int index)
   添加 k-v 封装到 Entry
2. 当 if (count >= threshold) 满足时,就进行扩容
3. 按照 int newCapacity = (oldCapacity << 1) + 1; 的大小进行扩容
   【11 * 2 + 1 = 23】
Hashtable 和 HashMap 对比

Properties类 基本介绍
  1. Properties类继承自 Hashtable类并且实现了 Map接口,也是使用一种键值对的形式来保存数据
  2. 他的使用特点和Hashtable类似
  3. Properties 还可以用于 从xxx.properties 文件中,加载数据到 Properties类对象,并进行读取和修改
  4. 说明:工作后 xxx.properties 文件通常作为配置文件
Properties常用方法
package Map_Properties;

import java.util.Properties;

public class Properties_ {
    public static void main(String[] args) {
        Properties properties = new Properties();
//        properties.put(null, 200);//空指针异常
//        properties.put("ton", 100);//空指针异常
        //增
        properties.put("jack", 60);
        properties.put("mary", 80);
        properties.put("lucy", 70);
        properties.put("lucy", 20);//替换
        System.out.println(properties);

        //删
        properties.remove("jack");
        System.out.println(properties);

        //改
        properties.put("mary", "hello world");
        System.out.println(properties);

        //查
        System.out.println(properties.get("mary"));
        System.out.println(properties.getProperty("mary"));//如果 value运行类型是 String类型则返回该value ,否则返回 null
        
    }
}
TreeMap 类
package Map_TreeMap;

import java.util.Comparator;
import java.util.TreeMap;

@SuppressWarnings("all")
public class TreeMap_ {
    public static void main(String[] args) {
        //使用默认的构造器,创建TreeMap 是无序的
//        TreeMap treeMap = new TreeMap();

        //按照传入的字符串 k的大小进行排序
        TreeMap treeMap = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((String) o1).compareTo((String) o2);
            }
        });

        treeMap.put("jack", "杰克");
        treeMap.put("tom", "汤姆");
        treeMap.put("smith", "史密斯");
        treeMap.put("kristina", "罗纳尔多");

        System.out.println(treeMap);

        //按照传入的 字符串k 的长度进行比较
        TreeMap treeMap1 = new TreeMap(new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((String)o1).length() - ((String)o2).length();
            }
        });

        treeMap1.putAll(treeMap);
        treeMap1.put("john", "约翰");//替换 "jack" value ——> 输出 jack=约翰
        
        System.out.println(treeMap1);

        //底层源码
        
    }
}
⭐总结 - 如何选择集合实践类

记住!!!

1】先判断存储的类型(一组对象【单列】或一组键对值【双列】)

2】一组对象【单列】: Collection接口
允许重复:List
增删多:LinkedList 【底层维护了一个双向列表】
改查多:ArrayList 【底层维护 Object 类型的可变数组】
不允许重复:Set
无序:HashSet 【底层是 HashMap,维护了一个哈希表 ,即(数组 + 链表 + 红黑树)
排序:TreeSet
插入和取出顺序一致:LinkedHashSet 【底层维护 数组 + 双向链表】

3】一组键对值:Map
键无序:HashMap 【底层是:哈希值 jdk7 :数组 + 链表 jdk8:数组 + 链表 + 红黑树】
键排序:TreeMap
键插入和取出顺序一致:LinkedHashMap
读取文件:Properties

Collections工具类 基本介绍
  1. Collections 是一个操作 Set、List 和 Map 等集合的工具类
  2. Collections 中提供了一系列静态的方法对集合元素进行排序、查找和修改等操作
    【排序操作:均为 static 方法】
  3. reverse(List) :反转 List 中的元素的顺序
  4. shuffle(List) : 对 LIst 集合元素进行随机排序
  5. sort(List) :根据元素的自然顺序对指定 List 集合元素进行升序排序
  6. sort(List,Comparator) :根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
  7. swap(List,int,int) :将指定 List 集合中的 i 处元素 和 j 处元素进行交换
常用方法
package Collections_;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Collections_Method {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("tom");
        list.add("smith");
        list.add("king");
        list.add("milan");


        //reverse(List) 反转 List 中的元素顺序
        Collections.reverse(list);
        System.out.println("reverse方法 " + list);

        //shuffle(List) 对 List 集合元素进行随机排序
        for (int i = 0; i < 5; i++) {
            Collections.shuffle(list);
            System.out.println("shuffle方法 " + list);
        }

        //sort(List) 根据元素的自然顺序对指定 List 集合元素升序排序
        Collections.sort(list);
        //k < m < s < t
        System.out.println("sort(List)方法 " + list);

        //sort(List, Comparator) 根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
        Collections.sort(list, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                //安全校验
                if (o1 instanceof String && o2 instanceof String) {
                    //按照字符串长度大小排序
                    return ((String) o1).length() - ((String) o2).length();
                }
                return 0;
            }
        });
        System.out.println("sort(List, Comparator)方法 " + list);

        //swap(List, int, int) 将指定 List 集合中的 i 处元素 和 j 处元素进行交换
        Collections.swap(list, 1, 3);
        System.out.println("swap方法" + list);
        Collections.swap(list, 0, 100);//抛出 IndexOutOfBoundsException 索引越界异常
    }
}
package Collections_;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@SuppressWarnings("all")
public class Collections_Method2 {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("jack");
        list.add("tim");
        list.add("king");
        list.add("milan");
        list.add("he");
        list.add("jack");

        //Object max(Collection) 根据元素的自然顺序,返回给定集合中的最大元素
        System.out.println("自然顺序最大元素=" + Collections.max(list));

        //Object max(Collection, Comparator) 根据 Comparator 指定的顺序,返回给定集合中的最大元素
        //例如:返回长度的最大元素
        Object maxObject = Collections.max(list, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((String)o1).length() - ((String)o2).length();
            }
        });
        System.out.println("长度最大的元素=" + maxObject);

        //Object min(Collection)
        System.out.println("自然排序最小元素= " + Collections.min(list));
        //Object min(Collection, Comparator)
        Object minObject = Collections.min(list, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((String)o1).length() - ((String)o2).length();
            }
        });
        System.out.println("长度最小的元素=" + minObject);

        //int frequency(Collection, Object) 返回指定集合中指定元素出现的次数
        System.out.println("jack出现的次数=" + Collections.frequency(list, "jack"));

        //void copy(List dest, List src) 将src中的内容复制到dest中
        //为了完成一个完整的拷贝,需要先给dest 赋值,大小为list.size()
        ArrayList dest = new ArrayList();
        for (int i = 0; i < list.size(); i++) {
            dest.add("");
        }
        //拷贝
        Collections.copy(dest, list);
        System.out.println("拷贝后的=" + dest);

        //boolean replaceAll(List list, Object oldVal, Object newVal) 使用新值替换 List 对象的所有旧值
        //如果list中,有jack 就替换成 杰克
        Collections.replaceAll(dest, "jack", "杰克");
        System.out.println("dest替换后=" + dest);
    }
}
本章练习

package HomeWork;

import java.util.ArrayList;

public class homework1 {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList();
        arrayList.add(new News("周杰伦新专辑"做自己的英雄"霸榜三个月"));//有引号需要添加转义符
        arrayList.add(new News("是时候崛起了阿森,人的潜力是无限的,不要给自己设限度,加油,有梦就去追"));

        int size = arrayList.size();
        for (int i = size - 1; i >= 0; i--) {
//            System.out.println(arrayList.get(i));
            News news = (News)arrayList.get(i);
            System.out.println(processTitle(news.getTitle()));
        }
    }

    //专门写一个方法,处理显示新闻标题
    public static String processTitle(String title) {
        if (title == null) {
            return "";
        }
        if (title.length() > 15) {
            return title.substring(0, 15) + "...";//[0,15)
        } else {
            return title;
        }
    }
}
class News {
    private String title;
    private String content;

    public News(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    public String toString() {
        return "标题为 " + title;
    }
}

package HomeWork;

import java.util.ArrayList;
import java.util.Iterator;

@SuppressWarnings("all")
public class homework2 {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList();
        Car car = new Car("宝马", 400000);
        Car car1 = new Car("奔驰", 600000);
        //1. add 添加单个元素
        arrayList.add(car);
        arrayList.add(car1);
        System.out.println(arrayList);
        arrayList.remove(car);
        //2. remove 删除指定元素
        arrayList.remove(car);
        System.out.println(arrayList);
        //3. contains 查找元素是否存在
        System.out.println(arrayList.contains(car1));//true
        //4. size 获取元素个数
        System.out.println("元素个数=" + arrayList.size());
        //5. isEmpty 判断是否为空
        System.out.println(arrayList.isEmpty());//false
        //6. clear 清空
        arrayList.clear();
        System.out.println(arrayList);//[]
        //7. addAll 添加多个元素
        ArrayList arr = new ArrayList();
        arr.add(new Car("保时捷", 900000));
        arr.add(new Car("悍马", 30000));
        arrayList.addAll(arr);
        System.out.println("添加后=" + arrayList);
        //8. containsAll 查找多个元素是否存在
        System.out.println("是否多个元素存在" + arrayList.containsAll(arr));//true
        //9. removeAll 删除多个元素
//        arrayList.removeAll(arr);
        System.out.println(arrayList);//[]

        //遍历
        System.out.println("==================增强for 遍历=================");
        for (Object o : arrayList) {
            System.out.println(o);
        }

        System.out.println("==================迭代器 遍历=================");
        Iterator iterator = arrayList.iterator();
        while (iterator.hasNext()) {
            Object obj = iterator.next();
            if (obj instanceof Car) {
                if (((Car) obj).getName().length() > 2) {
                    System.out.println(obj);//保时捷
                };
            }
        }
    }
}
class Car {
    private String name;
    private int price;

    public Car(String name, int price) {
        this.name = name;
        this.price = price;
    }

    @Override
    public String toString() {
        return "nCar{" +
                "name='" + name + ''' +
                ", price=" + price +
                '}';
    }

    public String getName() {
        return name;
    }

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

package HomeWork;

import java.util.*;

@SuppressWarnings("all")
public class homework3 {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("jack", 650);
        map.put("tom", 1200);
        map.put("smith", 2900);
        System.out.println(map);

        //1. 将jack的工资改为2600元
        map.put("jack", 2600);
        //2. 将所有员工工资加薪 100元
        //keySet
        Set set = map.keySet();
        for (Object key : set) {
            //更新
            map.put(key, (Integer) map.get(key) + 100);
        }
        System.out.println("更新后" + map);

        //遍历遍历所有员工
        //EntrySet
        System.out.println("======遍历======");
        Set entrySet = map.entrySet();
        Iterator iterator = entrySet.iterator();
        while (iterator.hasNext()) {
            Map.Entry entry = (Map.Entry) iterator.next();
            System.out.println(entry.getKey() + "-" + entry.getValue());
        }

        //取出所有员工工资
        System.out.println("======遍历所有工资======");
        Collection values = map.values();
        for (Object value : values) {
            System.out.println("工资=" + value);
        }
    }
}

HashSet

HashSet有以下特点

  1. 不能保证元素的排列顺序,顺序有可能发生变化
  2. 不是同步的
  3. 集合元素可以是null,但只能放入一个null

当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在HashSet中存储位置。
简单的说,HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值相 等
注意,如果要把一个对象放入HashSet中,重写该对象对应类的equals方法,也应该重写其hashCode()方法。其规则是如果两个对 象通过equals方法比较返回true时,其hashCode也应该相同。另外,对象中用作equals比较标准的属性,都应该用来计算 hashCode的值。

TreeSet类
TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。
TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0

【自然排序】
自然排序使用要排序元素的CompareTo(Object obj)方法来比较元素之间大小关系,然后将元素按照升序排列。
Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现了该接口的对象就可以比较大小。
obj1.compareTo(obj2)方法如果返回0,则说明被比较的两个对象相等,如果返回一个正数,则表明obj1大于obj2,如果是 负数,则表明obj1小于obj2。
如果我们将两个对象的equals方法总是返回true,则这两个对象的compareTo方法返回应该返回0
【定制排序】
自然排序是根据集合元素的大小,以升序排列,如果要定制排序,应该使用Comparator接口,实现 int compare(T o1,T o2)方法。

最重要:

1、TreeSet 是二差树实现的,Treeset中的数据是自动排好序的,不允许放入null值。

2、HashSet 是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个null,两者中的值都不能重复,就如数据库中唯一约束。

3、HashSet要求放入的对象必须实现HashCode()方法,放入的对象,是以hashcode码作为标识的,而具有相同内容的 String对象,hashcode是一样,所以放入的内容不能重复。但是同一个类的对象可以放入不同的实例 。

package HomeWork;

import java.util.TreeSet;

public class homework5 {
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet();
        //分析源码
        
        treeSet.add(new Person());//抛出ClassCastException异常 原因:没有实现Comparator接口
    }
}
class Person implements Comparable{//实现Comparator接口即可解决
    @Override
    public int compareTo(Object o) {
        return 0;
    }
}


));
arr.add(new Car(“悍马”, 30000));
arrayList.addAll(arr);
System.out.println(“添加后=” + arrayList);
//8. containsAll 查找多个元素是否存在
System.out.println(“是否多个元素存在” + arrayList.containsAll(arr));//true
//9. removeAll 删除多个元素
// arrayList.removeAll(arr);
System.out.println(arrayList);//[]

    //遍历
    System.out.println("==================增强for 遍历=================");
    for (Object o : arrayList) {
        System.out.println(o);
    }

    System.out.println("==================迭代器 遍历=================");
    Iterator iterator = arrayList.iterator();
    while (iterator.hasNext()) {
        Object obj = iterator.next();
        if (obj instanceof Car) {
            if (((Car) obj).getName().length() > 2) {
                System.out.println(obj);//保时捷
            };
        }
    }
}

}
class Car {
private String name;
private int price;

public Car(String name, int price) {
    this.name = name;
    this.price = price;
}

@Override
public String toString() {
    return "nCar{" +
            "name='" + name + ''' +
            ", price=" + price +
            '}';
}

public String getName() {
    return name;
}

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

}

[外链图片转存中...(img-W1T3Tl0X-1655954739789)]

```java
package HomeWork;

import java.util.*;

@SuppressWarnings("all")
public class homework3 {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("jack", 650);
        map.put("tom", 1200);
        map.put("smith", 2900);
        System.out.println(map);

        //1. 将jack的工资改为2600元
        map.put("jack", 2600);
        //2. 将所有员工工资加薪 100元
        //keySet
        Set set = map.keySet();
        for (Object key : set) {
            //更新
            map.put(key, (Integer) map.get(key) + 100);
        }
        System.out.println("更新后" + map);

        //遍历遍历所有员工
        //EntrySet
        System.out.println("======遍历======");
        Set entrySet = map.entrySet();
        Iterator iterator = entrySet.iterator();
        while (iterator.hasNext()) {
            Map.Entry entry = (Map.Entry) iterator.next();
            System.out.println(entry.getKey() + "-" + entry.getValue());
        }

        //取出所有员工工资
        System.out.println("======遍历所有工资======");
        Collection values = map.values();
        for (Object value : values) {
            System.out.println("工资=" + value);
        }
    }
}

[外链图片转存中…(img-6T2dWXgf-1655954739789)]

HashSet

HashSet有以下特点

  1. 不能保证元素的排列顺序,顺序有可能发生变化
  2. 不是同步的
  3. 集合元素可以是null,但只能放入一个null

当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在HashSet中存储位置。
简单的说,HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值相 等
注意,如果要把一个对象放入HashSet中,重写该对象对应类的equals方法,也应该重写其hashCode()方法。其规则是如果两个对 象通过equals方法比较返回true时,其hashCode也应该相同。另外,对象中用作equals比较标准的属性,都应该用来计算 hashCode的值。

TreeSet类
TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。
TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0

【自然排序】
自然排序使用要排序元素的CompareTo(Object obj)方法来比较元素之间大小关系,然后将元素按照升序排列。
Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现了该接口的对象就可以比较大小。
obj1.compareTo(obj2)方法如果返回0,则说明被比较的两个对象相等,如果返回一个正数,则表明obj1大于obj2,如果是 负数,则表明obj1小于obj2。
如果我们将两个对象的equals方法总是返回true,则这两个对象的compareTo方法返回应该返回0
【定制排序】
自然排序是根据集合元素的大小,以升序排列,如果要定制排序,应该使用Comparator接口,实现 int compare(T o1,T o2)方法。

最重要:

1、TreeSet 是二差树实现的,Treeset中的数据是自动排好序的,不允许放入null值。

2、HashSet 是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个null,两者中的值都不能重复,就如数据库中唯一约束。

3、HashSet要求放入的对象必须实现HashCode()方法,放入的对象,是以hashcode码作为标识的,而具有相同内容的 String对象,hashcode是一样,所以放入的内容不能重复。但是同一个类的对象可以放入不同的实例 。

[外链图片转存中…(img-JRMUisDG-1655954739790)]

package HomeWork;

import java.util.TreeSet;

public class homework5 {
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet();
        //分析源码
        
        treeSet.add(new Person());//抛出ClassCastException异常 原因:没有实现Comparator接口
    }
}
class Person implements Comparable{//实现Comparator接口即可解决
    @Override
    public int compareTo(Object o) {
        return 0;
    }
}

[外链图片转存中…(img-lqO13oBw-1655954739790)]

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/988455.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号