16Java基础之枚举、泛型、API、Objects类、包装类

枚举

  • 枚举是一种特殊类。
  • 枚举类的格式:
    修饰符 enum 枚举类名{
    名称1,名称2...;
    其他成员...
    }

注意:

  1. 枚举类中的第一行,只能写一些合法的标识符(名称),多个名称用逗号隔开。
  2. 这些名称,本质是常量,每个常量都会记住枚举类的一个对象

案例:

// 枚举类
public enum A {
    // 枚举类的第一行必须罗列的是枚举对象的名称
    X, Y, Z;
    // 其他成员
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

测试方法:
// 目标:认识枚举类,搞清楚枚举类的特点
public class Test {
    public static void main(String[] args) {
        // 枚举无法创建对象,构造器是私有的
//        A a = new A();
        A a1 = A.X;
        A a2 = A.Y;
        System.out.println(a1);
        System.out.println(a2);
        System.out.println("---------------------------");
        //编译器为枚举类新增了几个方法
        //拿到枚举类的全部对象,放到一个数组中返回。
        A[] as = A.values();
        for (int i = 0; i < as.length; i++) {
            A a = as[i];
            System.out.println(a);
        }
        //拿到X的枚举对象
        A y = A.valueOf("X");
        System.out.println(y == a1);
        // ordinal,拿对象的索引
        System.out.println(a1.ordinal());
        System.out.println(a2.ordinal());
    }
}

输出结果:
X
Y
---------------------------
X
Y
Z
true
0
1

image

  • 上图为枚举类反编译后的代码截图。

枚举的特点

  1. 枚举类的第一行只能罗列一些名称,这些名称都是常量,并且每个常量记住的都是枚举类的一个对象。
  2. 枚举类的构造器都是私有的(写不写都只能是私有的),因此,枚举类对外不能创建对象。
  3. 枚举类中,从第二行开始,可以定义类的其他各种成员。
  4. 编译器为枚举类新增了几个方法,并且枚举类都是继承:java.lang.Enum类的,从enum类也会继承到一些方法。

枚举的应用场景

  • 枚举通常用来表示一组信息,然后作为参数进行传输。

选择定义一个一个的常量来表示一组信息,并作为参数传递,参数值不受约束。
选择定义枚举表示一组信息,并作为参数传递,代码可读性好,参数值得到了约束,对使用者更友好,建议使用。
案例:

需求:需要提供一个方法,可以完成向下取整,向上取整,四舍五入,去掉小数部分。
如果用常量来表示一组功能信息,代码如下:
常量类:
// 常量类
public class Constant {
    public static final int DOWN = 1;
    public static final int UP = 2;
    public static final int HAFL_UP = 3;
    public static final int INT = 4;
}
测试方法:
// 目标:搞清楚枚举的应用场景:信息标志和信息分类
public class Test {
    public static void main(String[] args) {
        // 需求:需要提供一个方法,可以完成向下取整,向上取整,四舍五入,去掉小数部分。
        // 常量信息标准和分类:参数值不被约束。
        System.out.println(handleData(3.768, Constant.DOWN));
        System.out.println(handleData(3.768, Constant.UP));
    }

    public static double handleData(double number,int type){
        switch(type){
            case Constant.DOWN:
                // 向下取整
                number = Math.floor(number);
                break;
            case Constant.UP:
                // 向上取整
                number = Math.ceil(number);
                break;
            case Constant.HAFL_UP:
                // 四舍五入
                number = Math.round(number);
                break;
            case Constant.INT:
                // 去掉小数部分
                number = (int)number;
                break;
            default:
                System.out.println("输入的类型有误");
        }
        return number;
    }
}
//----------------------------------------------------
如果使用枚举,代码如下:
枚举类:
public enum Constant2 {
    DOWN,UP,HAFL_UP,INT;
}

测试方法:
public class Test2 {
    public static void main(String[] args) {
        // 需求:需要提供一个方法,可以完成向下取整,向上取整,四舍五入,去掉小数部分。
        // 枚举做信息标准和分类:参数值被约束,最好的信息标志和分类的理想方案。
        System.out.println(handleData(3.768, Constant2.DOWN));
        System.out.println(handleData(3.768, Constant2.UP));
    }
    public static double handleData(double number,Constant2 type){
        switch(type){
            case Constant2.DOWN:
                // 向下取整
                number = Math.floor(number);
                break;
            case Constant2.UP:
                // 向上取整
                number = Math.ceil(number);
                break;
            case Constant2.HAFL_UP:
                // 四舍五入
                number = Math.round(number);
                break;
            case Constant2.INT:
                // 去掉小数部分
                number = (int)number;
                break;
            default:
                System.out.println("输入的类型有误");
        }
        return number;
    }
}

注意:handleData方法中的,Constant2.是可以省略的。

泛型

  • 在定义类、接口、方法是,同时声明了一个或多个类型变量(如:“”),称为泛型类、泛型接口、泛型方法,它们统称为泛型。
    image

  • 作用:泛型提供了在编译阶段约束所能操作的数据类型,并自动进行检查的能力!这样可以避免强制类型转换,及其可能出现的异常。

  • 泛型的本质:把具体的数据类型作为参数传给变量。


案例:

// 目标:认识泛型
public class Test {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add("魔法");
        list.add("霍格沃兹");
        list.add("我是一个中国人");
        list.add(true);
        list.add(123);
        list.add(12.23);

        // 1. 开发中很多时候需要统一的数据类型。(如果不使用泛型,类型没办法统一,就需要类型转换,容易出现bug)
        for (int i = 0; i < list.size(); i++) {
            Object object = list.get(i);
            String st = (String)object;// 强制类型转换,编译不报错,运行报错。
            System.out.println(st);
        }

        System.out.println("------------------");
        ArrayList<String> list2 = new ArrayList<>(); //JDK1.7开始,后面类型可以不写
        list2.add("魔法");
        list2.add("霍格沃兹");
        list2.add("我是一个中国人");
//        list2.add(true); // 报错
//        list2.add(123);  // 报错
//        list2.add(12.23);// 报错
    }
}

泛型类

  • 格式:
    修饰符 class 类名<类型变量,类型变量,……>{
    ……
    }
    举例:
public class ArrayList<E>{
  ……
}

注意:类型变量建议用大写的英文字母,常用的有E、T、K、V等。

  • E‌:通常代表Enum类型,用于表示枚举类型。 ‌
    ‌ - T‌:代表Type类型,常用于泛型类或方法中表示类型参数。 ‌
    ‌ - K‌:代表Key类型,通常用于表示键或标识符。 ‌
    ‌- V‌:代表Value类型,用于表示值或数据

案例

import java.util.ArrayList;

// 泛型类
public class MyArrayList<E> {
    private ArrayList list = new ArrayList();

    public boolean add(E e){
        list.add(e);
        return true;
    }

    public boolean remove(E e){
        return list.remove(e);
    }

    @Override
    public String toString(){
        return list.toString();
    }
}

泛型接口

  • 格式
    修饰符 interface接口名<类型变量,类型变量,……>{
    ……
    }
    举例:
    public interface A{
    ……
    }

案例:

// 需求:必须完成学生、老师数据的增删改查操作。
学生类:
public class Student {}
老师类:
public class Teacher {}
泛型接口:
// 泛型接口
//T : 表示泛型的类型,是一个占位符,在编译期会被替换为具体的类型
public interface Data<T> {
    void add(T t);
    void remove(T t);
    void update(T t);
    T get(int index);
}
学生实现类:
public class DataStudent implements Data<Student>{

    @Override
    public void add(Student student) {

    }

    @Override
    public void remove(Student student) {

    }

    @Override
    public void update(Student student) {

    }

    @Override
    public Student get(int index) {
        return null;
    }
}
老师实现类:
public class DataTeacher implements Data<Teacher>{
    @Override
    public void add(Teacher teacher) {

    }

    @Override
    public void remove(Teacher teacher) {

    }

    @Override
    public void update(Teacher teacher) {

    }

    @Override
    public Teacher get(int index) {
        return null;
    }
}
测试方法:
// 目标:认识泛型接口的使用框架
public class Test {
    public static void main(String[] args) {
        // 需求:必须完成学生、老师数据的增删改查操作。
        Student s1 = new Student();
        Data data = new DataStudent();
        data.add(s1);
        data.remove(s1);
        data.update(s1);
        data.get(1);
    }
}

泛型方法、泛型通配符、上下限

泛型方法

  • 格式
    修饰符<类型变量,类型变量,……> 返回值类型 方法名(形参){}
    举例: public static void test(T t){}

案例:

学生类:
public class Student{}

测试方法:
// 目标:认识泛型方法
public class Test {
    public static void main(String[] args) {
        //需求:要求接收任意对象数组
        String[] name = {"刘德华", "郭富城", "黎明", "张学友" };
        Student[] students = new Student[50];
        printarray(name);
        printarray(students);

    }
     // 泛型方法
    // 返回值也是泛型,这样可以避免类型转换
    public static <T> T printarray(T[]array){
        return array[0];
    }
}

通配符

  • 就是"?", 可以在"使用泛型"的时候代表一切类型;ETKV是在定义泛型的时候使用。

泛型的上下限

  • 泛型上限:?extends Car: ? 能接收的必须是Car或者其子类。
  • 泛型下限:? super Car: ? 能接收的必须是Car或者其父类。

案例

// 目标:通配符和上下限
public class Test2 {
    public static void main(String[] args) {
        // 需求:所有汽车要一起参与比赛。
        ArrayList<BMW> bmws = new ArrayList<BMW>();
        bmws.add(new BMW());
        bmws.add(new BMW());
        bmws.add(new BMW());


        ArrayList<Benz> benzs = new ArrayList<Benz>();
        benzs.add(new Benz());
        benzs.add(new Benz());
        benzs.add(new Benz());

        // 正常是送车的集合,结果送成狗了,但程序依然可以运行,这是有bug的
//        ArrayList<Dog> dogs = new ArrayList<Dog>();
//        dogs.add(new Dog());
//        dogs.add(new Dog());
//        dogs.add(new Dog());
//        go(dogs);

        go(bmws);
        go(benzs);



    }
    // 虽然BMW和Benz都是Car的子类,但是ArrayList<BMW>、ArrayList<Benz>、ArrayList<Car>并不是同一个类型
    // 通配符:其实就是?,可以使用泛型的时候代表一切类型。ETKV是在定义时使用,而?是在使用时使用
    // 泛型的上下限:? extends Car(上限,?必须是Car或者Car的子类),? super Car(下限,?必须是Car或者Car的父类)
    //public static void go(ArrayList<BMW> cars){
    //public static void go(ArrayList<?> cars){
    public static void go(ArrayList<? extends Car> cars){}
}

class Car{}
class BMW extends Car{}
class Benz extends Car{}

class Dog{}

泛型的注意事项:擦除问题、基本数据类型问题

  • 泛型是工作在编译阶段的,一旦程序编译成class文件,class文件中就不存在泛型了,这就是泛型的擦除。
  • 泛型不能直接支持基本数据类型,只能支持对象类型(引用数据类型)

API

什么是API?

  • API(Application Programming interface):应用程序编程接口。
  • 就是Java帮我们已经写好一些程序,如:类、方法等,我们直接拿过来用就可以解决一些问题。

为什么要学别人写好的程序?

  • 提高开发效率。

Object类的作用:

  • Object类是Java中所有类的祖宗类,因此,Java中所有类的对象都可以直接使用Objcet类中提供的一些方法。

Objcet类的常见方法
image

toString存在的意义:toString默认返回的地址形式其实没什么意义,开发中输出对象更想看内容,因此toString是为了让子类重写
equals存在的意义:equals方法默认是比较两个对象的地址是否一样。比较两个对象的地址是否一致,可以直接使用“==”来比较,完全没必要使用equals。因此,Object提供的equals方法的意义是为了让子类重写,以便子类自己来定义比较规则(按照内容比较)。

案例:

学生类:
import java.util.Objects;

public class Student {
    private String name;
    private int age;
    private String score;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getScore() {
        return score;
    }

    public void setScore(String score) {
        this.score = score;
    }

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

    @Override
    public boolean equals(Object o) {
        // 1.判断两个对象是否是同一个对象在比较,如果是,返回true
        if (this == o) return true;
        // 2. 判断o如果是null,或者两个对象的类型不一样,直接返回false
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name) && Objects.equals(score, student.score);
    }

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

测试方法:
// 目标:掌握object的常用方法。
public class Test {
    public static void main(String[] args) {
        Student s1 = new Student("小昭", 18, "100");
        // 1.toString默认返回的地址形式其实没什么意义,开发中输出对象更想看内容,因此toString是为了让子类重写
        //System.out.println(s1.toString());//直接输出对象时,toString方法可以不写
        System.out.println(s1);
        System.out.println("-----------------------------");
        // 2.equals方法默认是比较两个对象的地址是否一样。
        // 比较两个对象的地址是否一致,可以直接使用“==”来比较,完全没必要使用equals。
        // 因此,Object提供的equals方法的意义是为了让子类重写,以便子类自己来定义比较规则(按照内容比较)。
        Student s2 = new Student("郡主", 20, "97");
        Student s3 = new Student("郡主", 20, "97");
        System.out.println(s2.equals(s3));
    }
}

Objects

  • Object是一个工具类,提供了很多操作对象的静态方法给我们使用。

案例:

学生类:
import java.util.Objects;

public class Student {
    private String name;
    private int age;
    private String score;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getScore() {
        return score;
    }

    public void setScore(String score) {
        this.score = score;
    }

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

    @Override
    public boolean equals(Object o) {
        // 1.判断两个对象是否是同一个对象在比较,如果是,返回true
        if (this == o) return true;
        // 2. 判断o如果是null,或者两个对象的类型不一样,直接返回false
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name) && Objects.equals(score, student.score);
    }

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

测试:
// 目标:掌握object的常用方法。
public class Test {
    public static void main(String[] args) {
        Student s1 = new Student("小昭", 18, "100");
        // 1.toString默认返回的地址形式其实没什么意义,开发中输出对象更想看内容,因此toString是为了让子类重写
        //System.out.println(s1.toString());//直接输出对象时,toString方法可以不写
        System.out.println(s1);
        System.out.println("-----------------------------");
        // 2.equals方法默认是比较两个对象的地址是否一样。
        // 比较两个对象的地址是否一致,可以直接使用“==”来比较,完全没必要使用equals。
        // 因此,Object提供的equals方法的意义是为了让子类重写,以便子类自己来定义比较规则(按照内容比较)。
        Student s2 = new Student("郡主", 20, "97");
        Student s3 = new Student("郡主", 20, "97");
        System.out.println(s2.equals(s3));
        System.out.println("---------------------------------------");
        Student s4 = null;
        Student s5 = new Student("陈昆", 18, "78");
        System.out.println(s4.equals(s5)); // 如果主调对象是null,直接就会报空指针异常。
        // 今后
        System.out.println(Objects.equals(s4, s5)); // 更安全,更可靠的解决空指针异常
    }
}

包装类

为什么要有包装类?

  • 包装类就是把基本类型的数据包装成对象。
    image

包装类的其他基本操作

  • 可以把基本类型的数据转换成字符串类型。
    public static String toString(double d)
    public String toString()

  • 可以把字符串类型的数值转换成数值本身对应的数据类型。
    public static int parseInt(String s)
    public static Integer valueOf(String s)

案例:

// 目标:认识包装类,搞清楚他的作用
public class Test {
    public static void main(String[] args) {
        // 1. 把基本类型通过包装类包装成对象
        int a = 10;

//        Integer it = new Integer(a);
        Integer it = Integer.valueOf(a);//手动包装
        System.out.println(it);
        // 2. 自动装箱机制,会把基本类型的数据可以直接变成对象赋值给包装类。
        Integer it2 = 12;
        Integer it3 = 12;
        System.out.println(it2 == it3);
        // 3. 自动拆箱机制,把包装类的对象直接变成基本类型的数据。
        int it4 = it2;
        System.out.println(it4);
        System.out.println("--------------Java为包装类提供了新功能----------------");
        //1. 功能1:包装类可以把基本类型的数据转换成字符串
        int a1 = 123;
        System.out.println(Integer.toString(a1));

        String rs = a1 + ""; //这种方式简单,但是效率低,底层是通过StringBuilder实现的。
        System.out.println(rs);
        // 功能2:把字符串类型的数字转换成基本类型的数据(很有用)
        String agestr = "23";
        int a2 = Integer.parseInt(agestr);
        String corcestr = "87.2";
        double a3 = Double.parseDouble(corcestr);

        a2 = Integer.valueOf(agestr);
        a3 = Double.valueOf(agestr);
        //3.由于泛型和集合都不支持基本数据类型,因为万物皆对象,因此包装类在集合和泛型中大量使用,而且必须使用。
    }
}

总结:

  • Integer.valueOf(a)方法中,会自动创建-128~127的类,在这个区间内的对象都一样,超过这个值会新建对象。
  • 由于泛型和集合都不支持基本数据类型,因为万物皆对象,因此包装类在集合和泛型中大量使用,而且必须使用。
posted @ 2025-07-29 11:32  狂风将军  阅读(30)  评论(0)    收藏  举报