MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Java 基础类之核心奥秘解析

2023-10-313.1k 阅读

Java 基础类概述

在 Java 编程的广阔天地中,基础类是构建复杂应用程序的基石。Java 提供了丰富的基础类库,这些类涵盖了从基本数据类型操作、字符串处理、日期时间管理,到集合框架、异常处理等诸多方面。理解这些基础类的核心原理和用法,对于成为一名优秀的 Java 开发者至关重要。

基本数据类型包装类

Java 中有 8 种基本数据类型,分别是 byte、short、int、long、float、double、char 和 boolean。为了能将基本数据类型当成对象来处理,并提供一些实用的方法,Java 为每种基本数据类型都提供了对应的包装类,即 Byte、Short、Integer、Long、Float、Double、Character 和 Boolean。

以 Integer 类为例,它提供了许多静态方法来处理整数。比如,将字符串转换为整数可以使用 parseInt 方法:

public class IntegerExample {
    public static void main(String[] args) {
        String numStr = "123";
        int num = Integer.parseInt(numStr);
        System.out.println(num);
    }
}

上述代码中,Integer.parseInt 方法将字符串 numStr 转换为整数 num 并输出。

Integer 类还缓存了 -128 到 127 之间的整数对象,这是一种优化机制。当使用 valueOf 方法获取 -128 到 127 之间的整数时,会返回缓存中的对象,而不是创建新的对象。

public class IntegerCacheExample {
    public static void main(String[] args) {
        Integer num1 = Integer.valueOf(100);
        Integer num2 = Integer.valueOf(100);
        System.out.println(num1 == num2);  // 输出 true

        Integer num3 = Integer.valueOf(128);
        Integer num4 = Integer.valueOf(128);
        System.out.println(num3 == num4);  // 输出 false
    }
}

从上述代码可以看到,对于 100 这个在缓存范围内的整数,num1num2 指向同一个对象;而对于 128 这个超出缓存范围的整数,num3num4 是不同的对象。

字符串相关类

在 Java 中,字符串处理是非常常见的操作。Java 提供了 StringStringBufferStringBuilder 这几个核心类来满足不同场景下的字符串处理需求。

  1. String 类 String 类代表不可变的字符序列。一旦一个 String 对象被创建,它的值就不能被改变。每次对 String 对象进行修改操作,都会创建一个新的 String 对象。
public class StringImmutabilityExample {
    public static void main(String[] args) {
        String str1 = "Hello";
        String str2 = str1.concat(" World");
        System.out.println(str1);  // 输出 Hello
        System.out.println(str2);  // 输出 Hello World
    }
}

在上述代码中,str1.concat(" World") 操作并没有改变 str1 的值,而是返回了一个新的字符串 str2

String 类提供了丰富的方法用于字符串操作,如 length 方法获取字符串长度,equals 方法比较字符串内容等。

public class StringMethodsExample {
    public static void main(String[] args) {
        String str = "Java is great";
        System.out.println("Length: " + str.length());
        System.out.println("Contains 'is': " + str.contains("is"));
        System.out.println("Replace 'great' with 'awesome': " + str.replace("great", "awesome"));
    }
}

上述代码展示了 lengthcontainsreplace 等方法的使用。

  1. StringBuffer 类 StringBuffer 类代表可变的字符序列,线程安全。它提供了一系列方法用于在原字符串基础上进行修改,适合多线程环境下的字符串操作。
public class StringBufferExample {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer("Hello");
        sb.append(" World");
        System.out.println(sb.toString());  // 输出 Hello World
    }
}

在上述代码中,sb.append(" World") 方法直接在 sb 这个 StringBuffer 对象上进行操作,修改了其内容。

  1. StringBuilder 类 StringBuilder 类同样代表可变的字符序列,但它不是线程安全的。在单线程环境下,StringBuilder 的性能比 StringBuffer 更好,因为它没有线程同步带来的开销。
public class StringBuilderExample {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder("Hello");
        sb.append(" World");
        System.out.println(sb.toString());  // 输出 Hello World
    }
}

日期时间相关类

在现代应用程序开发中,日期和时间的处理是必不可少的。Java 在这方面经历了一些演变,早期主要使用 java.util.Datejava.util.Calendar 类,后来引入了 java.time 包下的新日期时间 API。

传统日期时间类

  1. Date 类 java.util.Date 类用于表示特定的瞬间,精确到毫秒。然而,这个类存在一些设计上的不足,比如它的许多方法已经被弃用。
import java.util.Date;

public class DateExample {
    public static void main(String[] args) {
        Date now = new Date();
        System.out.println(now);
    }
}

上述代码创建了一个表示当前时间的 Date 对象并输出。虽然输出结果看起来包含了日期和时间信息,但这种格式并不直观,并且不利于格式化和解析。

  1. Calendar 类 java.util.Calendar 类是一个抽象类,用于提供一些操作日期和时间的方法。它弥补了 Date 类在日期和时间操作上的一些不足。
import java.util.Calendar;

public class CalendarExample {
    public static void main(String[] args) {
        Calendar cal = Calendar.getInstance();
        int year = cal.get(Calendar.YEAR);
        int month = cal.get(Calendar.MONTH) + 1;
        int day = cal.get(Calendar.DAY_OF_MONTH);
        System.out.println(year + "-" + month + "-" + day);
    }
}

在上述代码中,通过 Calendar.getInstance() 获取一个 Calendar 对象,然后使用 get 方法获取年、月、日等信息。需要注意的是,Calendar 类中月份是从 0 开始计数的,所以获取月份时需要加 1。

新日期时间 API(Java 8+)

Java 8 引入了 java.time 包,提供了一套全新的日期时间 API,设计更加合理和易用。

  1. LocalDate、LocalTime 和 LocalDateTime LocalDate 类表示日期(年、月、日),LocalTime 类表示时间(时、分、秒、纳秒),LocalDateTime 类则表示日期和时间的组合。
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;

public class LocalDateTimeExample {
    public static void main(String[] args) {
        LocalDate date = LocalDate.now();
        LocalTime time = LocalTime.now();
        LocalDateTime dateTime = LocalDateTime.now();

        System.out.println("Date: " + date);
        System.out.println("Time: " + time);
        System.out.println("DateTime: " + dateTime);
    }
}

上述代码分别获取当前日期、时间和日期时间并输出,输出格式更加直观和规范。

  1. DateTimeFormatter DateTimeFormatter 类用于格式化和解析日期时间。它提供了丰富的预定义格式,也支持自定义格式。
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class DateTimeFormatterExample {
    public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now();

        // 使用预定义格式
        DateTimeFormatter isoFormatter = DateTimeFormatter.ISO_DATE_TIME;
        String isoFormat = now.format(isoFormatter);
        System.out.println("ISO Format: " + isoFormat);

        // 自定义格式
        DateTimeFormatter customFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String customFormat = now.format(customFormatter);
        System.out.println("Custom Format: " + customFormat);
    }
}

在上述代码中,首先使用 ISO_DATE_TIME 预定义格式对 LocalDateTime 对象进行格式化,然后自定义了一个格式并进行格式化操作。

集合框架

Java 的集合框架是一个庞大而强大的体系,它提供了多种数据结构和算法,用于存储、检索和操作数据。集合框架主要包括接口、实现类和算法。

集合接口

  1. Collection 接口 Collection 接口是集合框架中最基本的接口,它定义了一组用于操作集合元素的方法,如添加元素、删除元素、查询元素等。ListSetQueue 接口都继承自 Collection 接口。
import java.util.ArrayList;
import java.util.Collection;

public class CollectionExample {
    public static void main(String[] args) {
        Collection<String> collection = new ArrayList<>();
        collection.add("Apple");
        collection.add("Banana");
        System.out.println("Size: " + collection.size());
        System.out.println("Contains 'Apple': " + collection.contains("Apple"));
    }
}

上述代码创建了一个 Collection 接口的实现类 ArrayList 对象,并演示了 addsizecontains 等方法的使用。

  1. List 接口 List 接口继承自 Collection 接口,它表示一个有序的集合,允许元素重复。List 接口提供了基于索引的访问方法,使得可以方便地对列表中的元素进行插入、删除和获取操作。
import java.util.ArrayList;
import java.util.List;

public class ListExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("One");
        list.add("Two");
        list.add(1, "Three");
        System.out.println("Element at index 1: " + list.get(1));
        list.remove(2);
        System.out.println("List: " + list);
    }
}

在上述代码中,创建了一个 ArrayList 对象,使用 add 方法添加元素,使用 add(int index, E element) 方法在指定位置插入元素,使用 get 方法获取指定位置的元素,使用 remove 方法删除指定位置的元素。

  1. Set 接口 Set 接口继承自 Collection 接口,它表示一个不包含重复元素的集合。Set 接口的实现类通常会重写 equalshashCode 方法来确保元素的唯一性。
import java.util.HashSet;
import java.util.Set;

public class SetExample {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("Apple");
        set.add("Banana");
        set.add("Apple");
        System.out.println("Set: " + set);
    }
}

上述代码创建了一个 HashSet 对象,添加了两个元素,其中 Apple 重复添加,但 Set 会自动去除重复元素,最终输出的集合中只包含一个 Apple

  1. Queue 接口 Queue 接口继承自 Collection 接口,它表示一个队列,通常用于存储等待处理的元素,遵循先进先出(FIFO)的原则。
import java.util.LinkedList;
import java.util.Queue;

public class QueueExample {
    public static void main(String[] args) {
        Queue<String> queue = new LinkedList<>();
        queue.add("First");
        queue.add("Second");
        System.out.println("Poll: " + queue.poll());
        System.out.println("Queue: " + queue);
    }
}

在上述代码中,创建了一个 LinkedList 对象作为 Queue 的实现,使用 add 方法添加元素,使用 poll 方法获取并移除队列头部的元素。

集合实现类

  1. ArrayList ArrayListList 接口的一个常用实现类,它基于数组实现。ArrayList 允许快速的随机访问,但在插入和删除元素时,尤其是在列表中间位置操作时,性能相对较差,因为需要移动元素。
import java.util.ArrayList;
import java.util.List;

public class ArrayListExample {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i);
        }
        System.out.println("Element at index 5: " + list.get(5));
    }
}

上述代码创建了一个 ArrayList 对象并添加了 10 个整数,然后快速获取了索引为 5 的元素。

  1. LinkedList LinkedList 也是 List 接口的实现类,它基于双向链表实现。LinkedList 在插入和删除元素时性能较好,尤其是在列表头部和尾部操作时,但随机访问性能较差,因为需要从头或尾开始遍历链表。
import java.util.LinkedList;
import java.util.List;

public class LinkedListExample {
    public static void main(String[] args) {
        List<String> list = new LinkedList<>();
        list.addFirst("First");
        list.addLast("Last");
        System.out.println("First Element: " + list.getFirst());
        System.out.println("Last Element: " + list.getLast());
    }
}

上述代码创建了一个 LinkedList 对象,使用 addFirstaddLast 方法在链表头部和尾部添加元素,然后使用 getFirstgetLast 方法获取头部和尾部元素。

  1. HashSet HashSetSet 接口的一个常用实现类,它基于哈希表实现。HashSet 中的元素是无序的,并且通过哈希码来确定元素的存储位置,以保证元素的唯一性。
import java.util.HashSet;
import java.util.Set;

public class HashSetExample {
    public static void main(String[] args) {
        Set<Integer> set = new HashSet<>();
        set.add(1);
        set.add(2);
        set.add(1);
        System.out.println("Set: " + set);
    }
}

上述代码创建了一个 HashSet 对象,添加了两个整数,其中 1 重复添加,但 HashSet 会根据哈希码判断并去除重复元素。

  1. TreeSet TreeSet 也是 Set 接口的实现类,它基于红黑树实现。TreeSet 中的元素是有序的,默认按照自然顺序(对于数字类型从小到大,对于字符串类型按字典序)排序,也可以通过自定义比较器进行排序。
import java.util.Set;
import java.util.TreeSet;

public class TreeSetExample {
    public static void main(String[] args) {
        Set<Integer> set = new TreeSet<>();
        set.add(3);
        set.add(1);
        set.add(2);
        System.out.println("Set: " + set);
    }
}

上述代码创建了一个 TreeSet 对象,添加了三个整数,输出的集合会按照从小到大的顺序排列。

异常处理相关类

在 Java 程序执行过程中,可能会出现各种错误和异常情况。Java 提供了一套完善的异常处理机制,通过异常类来表示不同类型的异常情况。

异常类层次结构

Java 的异常类都继承自 Throwable 类,Throwable 类有两个主要的子类:ErrorException

  1. Error 类 Error 类表示严重的系统错误,通常是由 JVM 或者系统环境引起的,应用程序一般不应该捕获和处理这类错误,比如 OutOfMemoryErrorStackOverflowError 等。
public class ErrorExample {
    public static void main(String[] args) {
        try {
            // 模拟内存溢出错误
            byte[] largeArray = new byte[1024 * 1024 * 1024];
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
}

上述代码尝试创建一个非常大的数组,可能会引发 OutOfMemoryError,即使在 try - catch 块中捕获 Throwable,这种错误也很难在应用程序层面进行有效处理。

  1. Exception 类 Exception 类表示可以被应用程序捕获和处理的异常情况。它又分为两类:受检异常(Checked Exception)和非受检异常(Unchecked Exception)。

    • 受检异常:受检异常是在编译时就必须处理的异常,否则代码无法通过编译。例如 IOExceptionSQLException 等。
import java.io.FileReader;
import java.io.IOException;

public class CheckedExceptionExample {
    public static void main(String[] args) {
        try {
            FileReader reader = new FileReader("nonexistentfile.txt");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在上述代码中,FileReader 的构造函数可能会抛出 FileNotFoundException,这是 IOException 的子类,属于受检异常,所以必须在 try - catch 块中处理或者在方法声明中抛出。

- **非受检异常**:非受检异常包括 `RuntimeException` 及其子类,如 `NullPointerException`、`ArrayIndexOutOfBoundsException` 等。这类异常在编译时不需要显式处理,通常是由于程序逻辑错误导致的。
public class UncheckedExceptionExample {
    public static void main(String[] args) {
        try {
            String str = null;
            System.out.println(str.length());
        } catch (NullPointerException e) {
            e.printStackTrace();
        }
    }
}

上述代码中,尝试调用 null 对象的 length 方法,会抛出 NullPointerException,这是一个非受检异常,可以选择在 try - catch 块中捕获处理,但不是必须的。

自定义异常类

在实际开发中,有时候需要根据业务需求自定义异常类。自定义异常类通常继承自 Exception 类(如果是受检异常)或 RuntimeException 类(如果是非受检异常)。

class MyBusinessException extends Exception {
    public MyBusinessException(String message) {
        super(message);
    }
}

public class CustomExceptionExample {
    public static void processData(int value) throws MyBusinessException {
        if (value < 0) {
            throw new MyBusinessException("Value cannot be negative");
        }
        System.out.println("Processing data: " + value);
    }

    public static void main(String[] args) {
        try {
            processData(-1);
        } catch (MyBusinessException e) {
            e.printStackTrace();
        }
    }
}

在上述代码中,定义了一个自定义受检异常类 MyBusinessException,在 processData 方法中,如果传入的值为负数,就抛出这个异常。在 main 方法中调用 processData 方法时,必须捕获这个自定义异常。

结语

通过对 Java 基础类中基本数据类型包装类、字符串相关类、日期时间相关类、集合框架以及异常处理相关类的深入解析,我们了解到这些基础类是构建 Java 应用程序的核心组件。它们不仅提供了丰富的功能和便捷的操作方法,还蕴含着许多设计理念和优化策略。在实际编程中,熟练掌握并合理运用这些基础类,能够大大提高代码的质量、性能和可维护性。无论是开发小型工具还是大型企业级应用,对这些基础类的深入理解都是成为优秀 Java 开发者的必经之路。希望本文能帮助读者更深入地探索 Java 基础类的奥秘,在 Java 编程的道路上更进一步。