Skip to content

JAVA

INFO

终究走上这条老路了。

数组

数组长度

  查看数组长度可以使用array.length属性。例如:

java
int[] numbers = {1, 2, 3, 4, 5};
System.out.println("数组长度: " + numbers.length); // 输出: 数组长度: 5

数组相关方法

  Java中的数组是固定大小的,创建后长度不可更改。常用的数组相关方法包括:

  1. Arrays.toString(array):将数组转换为字符串表示形式,便于打印输出。
  2. Arrays.sort(array):对数组进行排序,默认按升序排序。
  3. Arrays.copyOf(original, newLength):复制数组,创建一个新的数组,长度为newLength
  4. Arrays.equals(array1, array2):比较两个数组是否相等,元素和顺序都相同。
  5. Arrays.fill(array, value):将数组的所有元素设置为指定的值。
  6. Arrays.binarySearch(array, key):在排序数组中搜索指定的值,返回索引位置,如果未找到则返回负数。

虚函数

  在JAVA中,private, static 和 final 方法默认都是非虚函数,其他方法默认都是虚函数。

  虚函数允许子类重写父类的方法,从而实现多态性。Java通过动态绑定来决定在运行时调用哪个方法版本。

面向对象

  面向对象编程(OOP)是一种编程范式,强调通过对象来组织代码。Java是一种典型的面向对象编程语言,三大核心概念:

  1. 封装
  2. 继承
  3. 多态

多态

  多态性是面向对象编程的核心概念之一,允许对象在不同的上下文中表现出不同的行为。Java通过方法重载和方法重写来实现多态性。

  实现方式:

  1. 方法重载(编译时多态):同一个类中,方法名相同但参数列表不同。
  2. 方法重写(运行时多态):子类重写父类的方法,调用时根据对象的实际类型决定调用哪个方法。
  3. 接口实现:通过接口定义方法,类实现接口并提供具体实现。
  4. 抽象类:抽象类可以包含抽象方法,子类必须实现这些方法,从而实现多态性。

方法重写注意事项

  1. 访问修饰符:子类重写的方法的访问修饰符不能比父类的方法更严格。例如,父类方法是public,子类方法不能是protectedprivate
  2. 返回类型:子类重写的方法的返回类型必须与父类方法的返回类型相同,或者是其子类型(协变返回类型)。
  3. 抛出异常:子类重写的方法不能抛出比父类方法更多或更广泛的检查型异常,但可以抛出未检查异常。
  4. 方法名称和参数列表:子类重写的方法必须与父类方法具有相同的方法名称和参数列表。
  5. 私有方法和静态方法不能被重写。

  多态使得我们可以编写更灵活和可扩展的代码,提高代码的可维护性和重用性。例如:

java
class Animal {
    void sound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Dog barks");
    }

    void fetch() {
        System.out.println("Dog fetches the ball");
    }
}

Animal myAnimal = new Dog();
myAnimal.sound(); // 输出: Dog barks

  如上,但当我们想使用狗狗特有的方法时,就需要进行强制类型转换:

java
Dog myDog = (Dog) myAnimal;
myDog.fetch(); // 输出: Dog fetches the ball

  如果是多种动物,就要使用instanceof进行判断:

java
if (myAnimal instanceof Dog) {
    Dog myDog = (Dog) myAnimal;
    myDog.fetch();
}

  Java16以后,可以使用模式匹配简化上述代码:

java
if (myAnimal instanceof Dog myDog) {
    myDog.fetch();
}

继承

  对于父类构造函数的访问特点:

  1. 子类不能继承父类的构造函数,但可以通过super关键字调用父类的构造函数。
  2. 如果子类的构造函数没有显式调用父类的构造函数,Java会自动调用父类的无参构造函数。
  3. 子类可以通过super(arguments)调用父类的有参构造函数,必须在子类构造函数的第一行。

接口与类

相同点:

  1. 都可以包含方法和属性。
  2. 都可以被实现或继承。
  3. 接口文件保存在.java文件中,编译后生成.class文件,类也是如此。

不同点:

  1. 接口只能包含抽象方法和常量,而类可以包含具体方法和变量。
  2. 类可以继承一个父类,而接口可以实现多个接口。
  3. 接口不能实例化,而类可以实例化对象。
  4. 接口支持多继承,而类不支持多继承。
  5. 接口中的方法默认是public abstract的,而类中的方法可以有不同的访问修饰符。
  6. 接口中的变量默认是public static final的,而类中的变量可以有不同的访问修饰符和非静态属性。
  7. 接口没有构造方法,而类有构造方法用于初始化对象。

访问修饰符

  接口中的方法默认是public的,不能使用其他访问修饰符。而类中的方法可以使用publicprotectedprivate和默认(包级别)访问修饰符,具体含义如下:

  1. public:方法对所有类可见。
  2. protected:方法对同一包中的任何类和包外的子类可见。
  3. 默认(包级别):方法对同一包中的任何类都可见。
  4. private:方法仅对所属类可见。

非访问修饰符

  接口中的方法默认是abstract的,不能使用其他非访问修饰符。而类中的方法可以使用finalstaticsynchronized等非访问修饰符,具体含义如下:

  1. final:方法不能被子类重写。
  2. static:方法属于类而不是实例,可以通过类名直接调用。
  3. synchronized:方法在多线程环境下是线程安全的。

static

  static关键字用于声明类的静态成员,包括静态变量和静态方法。静态成员属于类本身,而不是类的实例,可以通过类名直接访问。

  静态方法多出现在测试类和工具类,在java Bean中很少会使用。

Java Bean类

  Java Bean是一种符合特定规范的Java类,通常用于封装数据。Java Bean类具有以下特点:

  1. 私有属性:Java Bean类的属性通常是私有的,通过公共的getter和setter方法进行访问和修改。
  2. 公共无参构造方法:Java Bean类通常具有一个公共的无参构造方法,允许通过反射等机制创建对象实例。
  3. 可序列化:Java Bean类通常实现Serializable接口,以支持对象的序列化和反序列化。
  4. 遵循命名规范:Java Bean类的getter和setter方法遵循特定的命名规范,如getPropertyName()setPropertyName()

测试类

  测试类是用于测试Java代码功能和行为的类,通常包含多个测试方法。测试类通常使用JUnit等测试框架进行编写和执行。 测试类的命名通常以被测试类的名称加上Test后缀命名,例如MyClassTest。测试类中的测试方法通常使用@Test注解标记,以便测试框架识别和执行。

工具类

  工具类是包含静态方法和常量的类,通常用于提供通用的功能和操作。工具类通常不包含实例变量和实例方法,所有的方法都是静态的,可以通过类名直接调用。常见的工具类包括MathArraysCollections等。

  工具类的构造函数一般设为private,以防止创建实例。

标记接口

  标记接口是一种没有任何方法或属性的接口,用于标识类具有某种特性或行为,属于一个特定的类型。

  例如,Serializable接口用于标识一个类的对象可以被序列化。标记接口通常用于类型检查和运行时行为控制。EventListener也是一个标记接口,用于标识事件监听器类,只要是继承于这个类的子类就表明是用来处理事务的。

反射

  反射是Java的一种强大机制,允许程序在运行时动态地获取类的信息,并操作类的属性和方法。通过反射,可以实现动态加载类、调用方法、访问字段等功能。

  反射的主要类包括ClassMethodFieldConstructor。使用反射时,可以通过类名获取Class对象,然后使用该对象获取类的方法、字段和构造函数,也可以通过getClass从实例获取。

java
Dog myDog = new Dog("旺财", "拉布拉多", 3);

System.out.print(Dog.class);

Class<?> clazz = myDog.getClass();
System.out.print(clazz);

  可以使用Class.forName("类的全限定名")动态加载类:

java
Class<?> clazz = Class.forName("com.example.MyClass");
Object obj = clazz.getDeclaredConstructor().newInstance();

  反射可以编写极其通用的代码,可以绕过private等访问修饰符访问类的私有成员,但也会带来性能开销和安全风险,因此应谨慎使用。

数据结构

  Java提供了丰富的数据结构,主要包括以下几种:

  1. 数组(Array):用于存储固定大小的同类型元素的集合,支持随机访问。
  2. 列表(List):如ArrayListLinkedList,用于存储动态大小的有序元素集合,支持重复元素。
  3. 集合(Set):如HashSetTreeSet,用于存储不重复的元素集合,支持快速查找。
  4. 映射(Map):如HashMapTreeMap,用于存储键值对映射关系,支持快速查找和更新。
  5. 栈(Stack):后进先出(LIFO)的数据结构,支持压入和弹出操作。
  6. 队列(Queue):先进先出(FIFO)的数据结构,支持入队和出队操作。

Collection

  Collection是Java集合框架的根接口,定义了一组通用的方法,用于操作一组对象。Collection接口有两个主要子接口:ListSet

  1. List接口:表示有序的元素集合,允许重复元素。常用实现类有ArrayListLinkedList。有序指的是存入和读取的顺序是一样的。可以使用for循环和索引进行访问。
  2. Set接口:表示不重复的元素集合,常用实现类有HashSetTreeSet。不可以使用索引和for进行访问。

Collection迭代

  可以使用以下几种方式迭代Collection中的元素:

  1. 增强型for循环(foreach):
java
for (E element : collection) {
    // 处理元素
}
  1. 使用Iterator接口:
java
Iterator<E> iterator = collection.iterator();
while (iterator.hasNext()) {
    E element = iterator.next();
    // 处理元素
}

  迭代器遍历完毕是不会复位的,要想重新遍历,需要重新获取一个新的迭代器对象。遍历的时候,不能使用集合的remove方法删除元素,否则会抛出ConcurrentModificationException异常。可以使用迭代器的remove方法安全地删除当前元素。

  1. 使用forEach方法(Java 8及以上版本):
java
collection.forEach(element -> {
    // 处理元素
});

Collection 常用方法

  对于Collection,常用的方法包括:

  1. boolean add(E e):向集合中添加元素。
  2. boolean remove(Object o):从集合中删除指定元素。
  3. int size():返回集合中元素的数量。
  4. boolean contains(Object o):检查集合中是否包含指定元素。
  5. void clear():清空集合中的所有元素。
  6. boolean isEmpty():检查集合是否为空。

List

  List是Java集合框架中的一个接口,表示有序的元素集合,允许重复元素。List接口继承自Collection接口,提供了一组用于操作有序集合的方法。常用的List实现类包括ArrayListLinkedList

List 常用方法

  List接口提供了丰富的方法来操作有序集合,常用的方法包括:

  1. void add(int index, E element):在指定索引位置插入元素。如果不给index,则默认添加到末尾。
  2. E get(int index):获取指定索引位置的元素。
  3. E set(int index, E element):替换指定索引位置的元素。
  4. E remove(int index):删除指定索引位置的元素。当给出的参数不为整数时,则删除指定元素。想删除指定元素类型为整数时,需要强制转换为对象类型,如list.remove((Integer) 10)

:::tips 为什么remove(int index)会首先选择删除指定索引?   因为Java中的方法重载机制会根据参数类型来选择合适的方法。当传入一个整数参数时,Java会优先匹配remove(int index)方法,因为它的参数类型与传入的参数类型完全匹配。而remove(Object o)方法需要将整数参数自动装箱为Integer对象,这在方法匹配时优先级较低。因此,当传入一个整数时,Java会选择调用remove(int index)方法。 :::

类型转换

  Java中的类型转换分为两种:自动类型转换(隐式转换)和强制类型转换(显式转换)。

  1. 自动类型转换:当一个较小范围的数据类型赋值给较大范围的数据类型时,Java会自动进行类型转换。例如,将int类型赋值给long类型。
java
int a = 10;
long b = a; // 自动类型转换
&emsp;&emsp;除此之外,`char`/`byte`/`short`类型在运算时会自动提升为`int`类型然后再进行运算。
  1. 强制类型转换:当一个较大范围的数据类型赋值给较小范围的数据类型时,需要进行强制类型转换,可能会导致数据丢失。例如,将double类型转换为int类型。
java
double x = 9.99;
int y = (int) x; // 强制类型转换

数据类型范围比较

从小到大依次为:byte < short < int < long < float < double

字符串

运算符 +

  在Java中,字符串的连接操作使用+运算符。当使用+运算符连接字符串时,Java会创建一个新的字符串对象,而不是修改原有的字符串对象。这是因为字符串在Java中是不可变的(immutable)。除此之外,如果+两边有一个是字符串类型,另一个是基本数据类型,Java会将基本数据类型转换为字符串类型,然后进行连接操作。再者,如果有多个+操作,Java会从左到右依次进行连接操作,因此对于1+99+"aaa"会变成100aaa

switch

  在Java中,switch语句用于基于变量的值执行不同的代码块。switch语句可以使用以下数据类型:byteshortcharint、枚举类型(enum)、字符串(String)和包装类(如ByteShortCharacterInteger)。

  有类似于js的箭头函数语法,可以省略掉冒号和break:

java
switch (variable) {
    case value1 -> {
        // code block
    }
    case value2 -> {
        // code block
    }
    default -> {
        // default code block
    }
}

Java内存

  Java内存主要分为以下几个区域:

  1. 堆内存(Heap):用于存储对象实例和数组,是垃圾回收器管理的主要区域。堆内存分为新生代(Young Generation)和老年代(Old Generation)。
  2. 栈内存(Stack):用于存储方法调用和局部变量,每个线程都有自己的栈内存。
  3. 方法区(Method Area):用于存储类的元数据、静态变量和常量池。方法区在JVM规范中被称为永久代(PermGen),但在Java 8及以后版本中被称为元空间(Metaspace)。
  4. 本地方法栈(Native Method Stack):用于支持本地方法调用,存储本地方法的相关信息。
  5. 程序计数器(Program Counter Register)/寄存器:用于存储当前线程执行的字节码指令地址,是线程私有的。

创建新对象

  创建新对象时,Java会在堆内存中分配空间,并初始化对象的属性。具体步骤如下:

  1. 类加载:JVM首先检查类是否已经加载,如果没有加载,则通过类加载器加载类的字节码。
  2. 分配内存:JVM在堆内存中为新对象分配足够的空间,并在占内存中申明局部变量,将堆内存的地址值存储起来。之后,堆内存里还要存储成员方法的地址,以便调用。
  3. 初始化对象:JVM调用类的构造方法,初始化对象的属性。初始化分为默认初始化、显式初始化和构造方法初始化三个阶段。
  • 默认初始化:JVM为对象的属性分配默认值(如0、null等)。
  • 显式初始化:如果在声明属性时指定了初始值,JVM会将这些值赋给属性。
  • 构造方法初始化:JVM调用构造方法,执行构造方法中的代码,进一步初始化对象的属性。
  1. 返回引用:JVM返回新对象的引用,供程序使用。

字符串

  1. java中字符串是不可变的,也就是说字符串一旦创建就不能被修改。
  2. 字符串常量池:为了提高效率,Java在内存中维护了一个字符串常量池。当创建字符串字面值时,JVM会首先检查常量池中是否存在相同的字符串,如果存在则返回该字符串的引用,否则在常量池中创建一个新的字符串对象。因此,使用字符串字面值创建的字符串对象可能会共享同一个内存地址。如下地址是不同的:
java
String str1 = new String("hello");
String str2 = "hello";
System.out.println(str1 == str2); // false

字符串比较

  在Java中,字符串比较可以使用==运算符和equals()方法。==运算符比较的是两个字符串对象的引用地址是否相同,而equals()方法比较的是字符串的内容是否相同。因此,在比较字符串时,通常建议使用equals()方法以确保比较的是内容而不是引用地址。

java
String str1 = new String("hello");
String str2 = "hello";
System.out.println(str1 == str2); // false
System.out.println(str1.equals(str2)); // true
System.out.println(str1.equalsIgnoreCase("HELLO")); // true 忽略大小写的比法

字符串相关方法

  Java提供了丰富的字符串操作方法,常用的方法包括:

  1. length():返回字符串的长度。
  2. charAt(int index):返回指定索引处的字符。注意!java不允许直接使用[1]这种形式进行取值
  3. substring(int beginIndex, int endIndex):返回字符串的子串。
  4. indexOf(String str):返回指定子串在字符串中首次出现的索引。
  5. lastIndexOf(String str):返回指定子串在字符串中最后一次出现的索引。
  6. toUpperCase():将字符串转换为大写。
  7. toLowerCase():将字符串转换为小写。
  8. trim():去除字符串两端的空白字符。
  9. replace(CharSequence target, CharSequence replacement):替换字符串中的指定子串。
  10. split(String regex):根据指定的正则表达式分割字符串,返回字符串数组。

StringBuilder

  StringBuilder是Java中用于创建和操作可变字符串的类。与String类不同,StringBuilder允许在原有字符串的基础上进行修改,而不需要创建新的字符串对象,从而提高了性能,特别是在需要频繁修改字符串的场景下。Java在底层进行了优化,因此打印StringBuilder对象时会自动调用其toString()方法,将其转换为字符串进行输出,而非其地址值。

  需要注意的是,StringBuilder是非线程安全的,如果在多线程环境下使用,建议使用StringBuffer类,它是线程安全的。

创建

  可以通过以下方式创建StringBuilder对象:

java
StringBuilder sb1 = new StringBuilder(); // 创建一个空的StringBuilder
StringBuilder sb2 = new StringBuilder("Hello"); // 使用初始字符串创建StringBuilder
StringBuilder sb3 = new StringBuilder(50); // 创建一个具有指定初始容量的StringBuilder

常用的方法

  StringBuilder类提供了多种方法来操作字符串,常用的方法包括:

  1. append(String str):将指定字符串追加到当前StringBuilder对象的末尾。
  2. insert(int offset, String str):在指定位置插入字符串。
  3. replace(int start, int end, String str):替换指定范围内的子串为新的字符串。
  4. delete(int start, int end):删除指定范围内的子串。
  5. reverse():反转StringBuilder中的字符顺序。
  6. toString():将StringBuilder对象转换为String对象。

StringJoiner

  StringJoiner是Java 8引入的一个类,用于高效地构建由多个字符串组成的单一字符串,特别适用于需要在字符串之间添加分隔符的场景。StringJoiner允许指定前缀和后缀,从而更灵活地控制生成的字符串格式。

  与StringBuilder类似,StringJoiner也是可变的,但它专注于字符串的连接操作,提供了更简洁的API来处理分隔符和边界。例如,以下代码展示了如何使用StringJoiner来连接字符串:

java
StringJoiner sj = new StringJoiner(", ", "[", "]");
sj.add("Apple");
sj.add("Banana");
sj.add("Cherry");
System.out.println(sj.toString()); // 输出: [Apple, Banana, Cherry]

集合

  Java集合框架提供了一组接口和类,用于存储和操作一组对象。相较于数组,集合具有更强的灵活性和功能,支持自动扩容、元素的添加和删除等操作。集合不可以存储基本数据类型,但可以通过包装类(如IntegerDouble等)来存储对应的对象类型。

  集合的使用要搭配上泛型(Generics),以确保类型安全,避免在运行时出现类型转换异常。具体的示例如下:

java
List<Integer> numbers = new ArrayList<>();
numbers.add(10); // 自动装箱,将基本类型int转换为Integer对象
numbers.add(20);
Integer firstNumber = numbers.get(0); // 自动拆箱,将Integer对象转换为基本类型int

  主要的集合类型包括:

  1. 列表(List):有序集合,允许重复元素。常用实现类有ArrayListLinkedList
  2. 集合(Set):无序集合,不允许重复元素。常用实现类有HashSetTreeSet
  3. 映射(Map):键值对集合,键唯一,值可以重复。常用实现类有HashMapTreeMap
  4. 队列(Queue):先进先出(FIFO)的集合,常用实现类有LinkedListPriorityQueue
  5. 栈(Stack):后进先出(LIFO)的集合,Java中可以使用Deque接口的实现类如ArrayDeque来实现栈的功能。

常用方法

  Java集合框架提供了丰富的方法来操作集合,常用的方法包括:

  1. boolean add(E e):向集合中添加元素。
  2. boolean remove(Object o):从集合中删除指定元素。
  3. E remove(int index):从列表中删除指定索引处的元素。
  4. E get(int index):获取列表中指定索引处的元素。
  5. E set(int index, E element):替换列表中指定索引处的元素。
  6. int size():返回集合中元素的数量。
  7. boolean contains(Object o):检查集合中是否包含指定元素。
  8. void clear():清空集合中的所有元素。
  9. boolean isEmpty():检查集合是否为空。

Map

HashMap

  HashMap是Java集合框架中的一个重要类,用于存储键值对映射关系。它基于哈希表实现,提供了高效的插入、删除和查找操作。HashMap允许使用null作为键和值,并且不保证元素的顺序。

  HashMap的工作原理基于哈希函数,将键映射到哈希表中的索引位置。当插入一个键值对时,HashMap会计算键的哈希码,并根据哈希码确定存储位置。如果发生哈希冲突(即不同的键映射到相同的索引位置),HashMap会使用链表或红黑树来存储冲突的元素。

  常用的HashMap方法包括:

  1. put(K key, V value):将指定的键值对插入到HashMap中。
  2. get(Object key):根据键获取对应的值。
  3. remove(Object key):根据键删除对应的键值对。
  4. containsKey(Object key):检查HashMap中是否包含指定的键。
  5. containsValue(Object value):检查HashMap中是否包含指定的值。
  6. size():返回HashMap中键值对的数量。
  7. clear():清空HashMap中的所有键值对。

  HashMap的初始化方式有多种,可以通过无参构造函数创建一个默认容量和负载因子的HashMap,也可以指定初始容量和负载因子。例如:

java
HashMap<String, Integer> map1 = new HashMap<>(); // 默认初始容量16,负载因子0.75
HashMap<String, Integer> map2 = new HashMap<>(32); // 指定初始容量32,默认负载因子0.75
HashMap<String, Integer> map3 = new HashMap<>(32, 0.5f); // 指定初始容量32和负载因子0.5

  容量因子(load factor)是一个介于0和1之间的浮点数,表示哈希表在自动扩容之前可以达到的填充程度。默认负载因子为0.75,表示当哈希表的填充程度达到75%时,会触发扩容操作。较高的负载因子可以节省内存空间,但可能会增加哈希冲突的概率,从而影响性能。较低的负载因子可以减少哈希冲突,但会增加内存消耗。

TreeMap

  TreeMap是Java集合框架中的一个类,用于存储键值对映射关系。它基于红黑树实现,提供了有序的键值对存储,按照键的自然顺序或自定义的比较器进行排序。与HashMap不同,TreeMap不允许使用null作为键,但允许使用null作为值。

  TreeMap的工作原理基于红黑树数据结构,确保在插入、删除和查找操作中保持平衡,从而提供了较高的性能。由于TreeMap是有序的,因此可以方便地进行范围查询和排序操作。

  常用的TreeMap方法包括:

  1. put(K key, V value):将指定的键值对插入到TreeMap中。
  2. get(Object key):根据键获取对应的值。
  3. remove(Object key):根据键删除对应的键值对。
  4. containsKey(Object key):检查TreeMap中是否包含指定的键。
  5. containsValue(Object value):检查TreeMap中是否包含指定的值。
  6. size():返回TreeMap中键值对的数量。
  7. clear():清空TreeMap中的所有键值对。
  8. firstKey():返回TreeMap中最小的键。
  9. lastKey():返回TreeMap中最大的键。
  10. subMap(K fromKey, K toKey):返回指定范围内的键值对视图。

Stack

  Java 推荐使用 Deque(双端队列)来模拟栈。可以使用 ArrayDeque 或 LinkedList 来实现栈的功能。以下是使用 ArrayDeque 实现栈的示例:

java
Deque<Integer> stack = new ArrayDeque<>();
stack.push(10); // 压入元素
stack.push(20);
int top = stack.pop(); // 弹出元素,top = 20
int peek = stack.peek(); // 查看栈顶元素,peek = 10

队列

  Java 提供了多种队列实现,常用的有 LinkedList 和 PriorityQueue。以下是使用 LinkedList 实现队列的示例:

java
Queue<Integer> queue = new LinkedList<>();
queue.offer(10); // 入队
queue.offer(20);
int front = queue.poll(); // 出队,front = 10
int peek = queue.peek(); // 查看队首元素,peek = 20

  但实际上,Deque 也可以用来实现队列功能:

java
Deque<Integer> deque = new ArrayDeque<>();
deque.offerLast(10); // 入队
deque.offerLast(20);
int front = deque.pollFirst(); // 出队,front = 10
int peek = deque.peekFirst(); // 查看队首元素,peek = 20

LinkedList

  LinkedList类实际上是个双向链表,其实现了Deque接口,因此可以同时用作栈和队列。使用LinkedList作为栈时,可以使用push()pop()方法;作为队列时,可以使用offer()poll()方法。

Arrays 类

  Arrays类是Java标准库中的一个实用工具类,提供了多种静态方法来操作数组。常用的方法包括:

  1. Arrays.sort(array):对数组进行排序,支持基本数据类型和对象数组。
  2. Arrays.binarySearch(array, key):在排序后的数组中进行二分查找,返回指定元素的索引。
  3. Arrays.equals(array1, array2):比较两个数组是否相等。
  4. Arrays.fill(array, value):将数组的所有元素填充为指定的值。
  5. Arrays.copyOf(original, newLength):复制数组,返回一个新的数组,长度为newLength
  6. Arrays.toString(array):将数组转换为字符串表示形式,便于打印输出。
  7. Arrays.asList(array):将数组转换为固定大小的列表,返回一个List视图。
  8. Arrays.stream(array):将数组转换为流,便于进行函数式编程操作。

Arrays.stream的意义

  Arrays.stream(array)方法将数组转换为流(Stream),使得可以利用Java 8引入的流式API对数组进行更灵活和强大的操作。这个的存在是为了直接处理原生数组,避免将其转换为集合类型,从而节省内存和提高性能。

  然而,使用Arrays.stream()时需要注意以下几点:

  1. 基本数据类型数组:对于基本数据类型数组(如int[]double[]等),Arrays.stream()会返回对应的原始流类型(如IntStreamDoubleStream等),而不是对象流。这些原始流提供了专门的方法来处理基本数据类型,避免了装箱和拆箱的开销。但是!!!,对于这个流没有collect方法,需要使用toArray()方法将其转换为数组,使用boxed()方法将其转换为对象流,或者直接用sum(),max()等函数。

  2. 对象数组:对于对象数组(如String[]Integer[]等),Arrays.stream()会返回一个对象流(Stream<T>),可以直接使用流的各种操作方法。

Arrays.asList的局限性

  Arrays.asList(array)方法将数组转换为一个固定大小的列表(List视图),但这个列表有一些局限性需要注意:

  1. 固定大小:由Arrays.asList()返回的列表是固定大小的,不能添加或删除元素。如果尝试调用add()remove()方法,会抛出UnsupportedOperationException异常。
  2. 共享数据:返回的列表是原始数组的视图,对列表的修改会直接影响原始数组,反之亦然。这意味着如果修改了列表中的元素,原始数组也会相应地改变。
  3. 基本数据类型数组:对于基本数据类型数组(如int[]double[]等),Arrays.asList()会将整个数组作为单个元素添加到列表中,而不是将数组的每个元素分别添加为列表的元素。这可能导致意外的行为。
java
int[] arr = {1, 2, 3};
List<int[]> list = Arrays.asList(arr);
System.out.println(list.size()); // 输出: 1
System.out.println(list.get(0)[0]); // 输出: 1

  我对这个的理解是,只是给了你个视图,并非真正创建一个新的Array,因此可以用一些数组的方法,比如Arrays.sort()对其进行排序,但不能用集合的方法去修改它的大小。真的想用集合的话,可以用new ArrayList<>(Arrays.asList(array))来创建一个新的可变大小的列表。

  包(Package)是Java中用于组织类和接口的命名空间。通过将相关的类和接口放在同一个包中,可以避免命名冲突,并且便于管理和维护代码。包的命名通常采用反向域名的形式,以确保唯一性。例如,假设一个公司的域名是example.com,那么该公司的Java包可以命名为com.example。在这个包下,可以创建子包来进一步组织类,例如com.example.utils用于存放工具类,com.example.models用于存放数据模型类。

  在Java源文件中,可以使用package关键字来声明类所属的包。例如:

java
package com.example.utils;
public class StringUtils {
    // 类的内容
}

  要使用其他包中的类,可以使用import关键字导入所需的类或整个包。例如:

java
import com.example.models.User;
import com.example.utils.*;
public class Main {
    public static void main(String[] args) {
        User user = new User();
        StringUtils utils = new StringUtils();
    }
}

  仅两种情况下不需要导包:

  1. 使用与当前类在同一包中的类。
  2. 使用java.lang包中的类,因为该包会被自动导入。

final

  final关键字用于声明常量、方法和类,表示它们不能被修改或继承。具体含义如下:

  1. final变量:表示常量,一旦赋值后不能再修改。通常使用大写字母和下划线命名常量。final修饰的基本数据类型变量,其值不能改变;final修饰的引用数据类型变量,其引用地址不能改变,但引用对象的内容可以改变。
  2. final方法:表示方法不能被子类重写。
  3. final类:表示类不能被继承。

双列集合

  双列集合是指同时存储键和值的集合,常见的双列集合有Map接口及其实现类,如HashMapTreeMap。双列集合允许通过键来访问对应的值,提供了高效的查找、插入和删除操作。 “键+值”这个整体,我们称为键值对/键值对对象/一个Entry(条目),每个Entry包含一个键和一个值。

Map的遍历方式

  可以使用以下几种方式遍历Map中的键值对:

  1. 使用entrySet()方法获取键值对集合,然后使用增强型for循环遍历:
java
Map<String, Integer> map = new HashMap<>();
for (Map.Entry<String, Integer> entry : map.entrySet()) {
    String key = entry.getKey();
    Integer value = entry.getValue();
    // 处理键值对
}
  1. 使用keySet()方法获取键集合,然后通过键获取对应的值:
java
for (String key : map.keySet()) {
    Integer value = map.get(key);
    // 处理键值对
}
  1. 使用values()方法获取值集合,遍历值:
java
for (Integer value : map.values()) {
    // 处理值
}
  1. 使用forEach方法(Java 8及以上版本):
java
map.forEach((key, value) -> {
    // 处理键值对
});

Collections集合工具类

  Collections类是Java标准库中的一个实用工具类,提供了多种静态方法来操作集合。常用的方法包括:

  1. sort(List<T> list):对列表进行排序,要求列表中的元素实现Comparable接口。
  2. reverse(List<?> list):反转列表中的元素顺序。
  3. shuffle(List<?> list):随机打乱列表中的元素顺序。
  4. binarySearch(List<? extends Comparable<? super T>> list, T key):在排序后的列表中进行二分查找,返回指定元素的索引。
  5. max(Collection<? extends T> coll):返回集合中的最大元素。
  6. min(Collection<? extends T> coll):返回集合中的最小元素。
  7. fill(List<? super T> list, T obj):将列表中的所有元素替换为指定的对象。
  8. copy(List<? super T> dest, List<? extends T> src):将源列表中的元素复制到目标列表中,要求目标列表的大小不小于源列表。
  9. synchronizedList(List<T> list):返回一个线程安全的列表。
  10. addAll(Collection<? super T> c, T... elements):将指定的元素添加到集合中。

  有很多,这和go里面slices包的功能类似,都是提供一些对集合进行操作的工具方法,减少了我们自己去实现这些常见功能的麻烦。