JAVA
INFO
终究走上这条老路了。
数组
数组长度
查看数组长度可以使用array.length属性。例如:
int[] numbers = {1, 2, 3, 4, 5};
System.out.println("数组长度: " + numbers.length); // 输出: 数组长度: 5数组相关方法
Java中的数组是固定大小的,创建后长度不可更改。常用的数组相关方法包括:
Arrays.toString(array):将数组转换为字符串表示形式,便于打印输出。Arrays.sort(array):对数组进行排序,默认按升序排序。Arrays.copyOf(original, newLength):复制数组,创建一个新的数组,长度为newLength。Arrays.equals(array1, array2):比较两个数组是否相等,元素和顺序都相同。Arrays.fill(array, value):将数组的所有元素设置为指定的值。Arrays.binarySearch(array, key):在排序数组中搜索指定的值,返回索引位置,如果未找到则返回负数。
虚函数
在JAVA中,private, static 和 final 方法默认都是非虚函数,其他方法默认都是虚函数。
虚函数允许子类重写父类的方法,从而实现多态性。Java通过动态绑定来决定在运行时调用哪个方法版本。
面向对象
面向对象编程(OOP)是一种编程范式,强调通过对象来组织代码。Java是一种典型的面向对象编程语言,三大核心概念:
- 封装
- 继承
- 多态
多态
多态性是面向对象编程的核心概念之一,允许对象在不同的上下文中表现出不同的行为。Java通过方法重载和方法重写来实现多态性。
实现方式:
- 方法重载(编译时多态):同一个类中,方法名相同但参数列表不同。
- 方法重写(运行时多态):子类重写父类的方法,调用时根据对象的实际类型决定调用哪个方法。
- 接口实现:通过接口定义方法,类实现接口并提供具体实现。
- 抽象类:抽象类可以包含抽象方法,子类必须实现这些方法,从而实现多态性。
方法重写注意事项
- 访问修饰符:子类重写的方法的访问修饰符不能比父类的方法更严格。例如,父类方法是
public,子类方法不能是protected或private。 - 返回类型:子类重写的方法的返回类型必须与父类方法的返回类型相同,或者是其子类型(协变返回类型)。
- 抛出异常:子类重写的方法不能抛出比父类方法更多或更广泛的检查型异常,但可以抛出未检查异常。
- 方法名称和参数列表:子类重写的方法必须与父类方法具有相同的方法名称和参数列表。
- 私有方法和静态方法不能被重写。
多态使得我们可以编写更灵活和可扩展的代码,提高代码的可维护性和重用性。例如:
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如上,但当我们想使用狗狗特有的方法时,就需要进行强制类型转换:
Dog myDog = (Dog) myAnimal;
myDog.fetch(); // 输出: Dog fetches the ball 如果是多种动物,就要使用instanceof进行判断:
if (myAnimal instanceof Dog) {
Dog myDog = (Dog) myAnimal;
myDog.fetch();
}Java16以后,可以使用模式匹配简化上述代码:
if (myAnimal instanceof Dog myDog) {
myDog.fetch();
}继承
对于父类构造函数的访问特点:
- 子类不能继承父类的构造函数,但可以通过
super关键字调用父类的构造函数。 - 如果子类的构造函数没有显式调用父类的构造函数,Java会自动调用父类的无参构造函数。
- 子类可以通过
super(arguments)调用父类的有参构造函数,必须在子类构造函数的第一行。
类
接口与类
相同点:
- 都可以包含方法和属性。
- 都可以被实现或继承。
- 接口文件保存在
.java文件中,编译后生成.class文件,类也是如此。
不同点:
- 接口只能包含抽象方法和常量,而类可以包含具体方法和变量。
- 类可以继承一个父类,而接口可以实现多个接口。
- 接口不能实例化,而类可以实例化对象。
- 接口支持多继承,而类不支持多继承。
- 接口中的方法默认是public abstract的,而类中的方法可以有不同的访问修饰符。
- 接口中的变量默认是public static final的,而类中的变量可以有不同的访问修饰符和非静态属性。
- 接口没有构造方法,而类有构造方法用于初始化对象。
访问修饰符
接口中的方法默认是public的,不能使用其他访问修饰符。而类中的方法可以使用public、protected、private和默认(包级别)访问修饰符,具体含义如下:
public:方法对所有类可见。protected:方法对同一包中的任何类和包外的子类可见。- 默认(包级别):方法对同一包中的任何类都可见。
private:方法仅对所属类可见。
非访问修饰符
接口中的方法默认是abstract的,不能使用其他非访问修饰符。而类中的方法可以使用final、static、synchronized等非访问修饰符,具体含义如下:
final:方法不能被子类重写。static:方法属于类而不是实例,可以通过类名直接调用。synchronized:方法在多线程环境下是线程安全的。
static
static关键字用于声明类的静态成员,包括静态变量和静态方法。静态成员属于类本身,而不是类的实例,可以通过类名直接访问。
静态方法多出现在测试类和工具类,在java Bean中很少会使用。
Java Bean类
Java Bean是一种符合特定规范的Java类,通常用于封装数据。Java Bean类具有以下特点:
- 私有属性:Java Bean类的属性通常是私有的,通过公共的getter和setter方法进行访问和修改。
- 公共无参构造方法:Java Bean类通常具有一个公共的无参构造方法,允许通过反射等机制创建对象实例。
- 可序列化:Java Bean类通常实现
Serializable接口,以支持对象的序列化和反序列化。 - 遵循命名规范:Java Bean类的getter和setter方法遵循特定的命名规范,如
getPropertyName()和setPropertyName()。
测试类
测试类是用于测试Java代码功能和行为的类,通常包含多个测试方法。测试类通常使用JUnit等测试框架进行编写和执行。 测试类的命名通常以被测试类的名称加上Test后缀命名,例如MyClassTest。测试类中的测试方法通常使用@Test注解标记,以便测试框架识别和执行。
工具类
工具类是包含静态方法和常量的类,通常用于提供通用的功能和操作。工具类通常不包含实例变量和实例方法,所有的方法都是静态的,可以通过类名直接调用。常见的工具类包括Math、Arrays、Collections等。
工具类的构造函数一般设为private,以防止创建实例。
标记接口
标记接口是一种没有任何方法或属性的接口,用于标识类具有某种特性或行为,属于一个特定的类型。
例如,Serializable接口用于标识一个类的对象可以被序列化。标记接口通常用于类型检查和运行时行为控制。EventListener也是一个标记接口,用于标识事件监听器类,只要是继承于这个类的子类就表明是用来处理事务的。
反射
反射是Java的一种强大机制,允许程序在运行时动态地获取类的信息,并操作类的属性和方法。通过反射,可以实现动态加载类、调用方法、访问字段等功能。
反射的主要类包括Class、Method、Field和Constructor。使用反射时,可以通过类名获取Class对象,然后使用该对象获取类的方法、字段和构造函数,也可以通过getClass从实例获取。
Dog myDog = new Dog("旺财", "拉布拉多", 3);
System.out.print(Dog.class);
Class<?> clazz = myDog.getClass();
System.out.print(clazz); 可以使用Class.forName("类的全限定名")动态加载类:
Class<?> clazz = Class.forName("com.example.MyClass");
Object obj = clazz.getDeclaredConstructor().newInstance();反射可以编写极其通用的代码,可以绕过private等访问修饰符访问类的私有成员,但也会带来性能开销和安全风险,因此应谨慎使用。
数据结构
Java提供了丰富的数据结构,主要包括以下几种:
- 数组(Array):用于存储固定大小的同类型元素的集合,支持随机访问。
- 列表(List):如
ArrayList和LinkedList,用于存储动态大小的有序元素集合,支持重复元素。 - 集合(Set):如
HashSet和TreeSet,用于存储不重复的元素集合,支持快速查找。 - 映射(Map):如
HashMap和TreeMap,用于存储键值对映射关系,支持快速查找和更新。 - 栈(Stack):后进先出(LIFO)的数据结构,支持压入和弹出操作。
- 队列(Queue):先进先出(FIFO)的数据结构,支持入队和出队操作。
Collection
Collection是Java集合框架的根接口,定义了一组通用的方法,用于操作一组对象。Collection接口有两个主要子接口:List和Set。
List接口:表示有序的元素集合,允许重复元素。常用实现类有ArrayList和LinkedList。有序指的是存入和读取的顺序是一样的。可以使用for循环和索引进行访问。Set接口:表示不重复的元素集合,常用实现类有HashSet和TreeSet。不可以使用索引和for进行访问。
Collection迭代
可以使用以下几种方式迭代Collection中的元素:
- 增强型
for循环(foreach):
for (E element : collection) {
// 处理元素
}- 使用
Iterator接口:
Iterator<E> iterator = collection.iterator();
while (iterator.hasNext()) {
E element = iterator.next();
// 处理元素
} 迭代器遍历完毕是不会复位的,要想重新遍历,需要重新获取一个新的迭代器对象。遍历的时候,不能使用集合的remove方法删除元素,否则会抛出ConcurrentModificationException异常。可以使用迭代器的remove方法安全地删除当前元素。
- 使用
forEach方法(Java 8及以上版本):
collection.forEach(element -> {
// 处理元素
});Collection 常用方法
对于Collection,常用的方法包括:
boolean add(E e):向集合中添加元素。boolean remove(Object o):从集合中删除指定元素。int size():返回集合中元素的数量。boolean contains(Object o):检查集合中是否包含指定元素。void clear():清空集合中的所有元素。boolean isEmpty():检查集合是否为空。
List
List是Java集合框架中的一个接口,表示有序的元素集合,允许重复元素。List接口继承自Collection接口,提供了一组用于操作有序集合的方法。常用的List实现类包括ArrayList和LinkedList。
List 常用方法
List接口提供了丰富的方法来操作有序集合,常用的方法包括:
void add(int index, E element):在指定索引位置插入元素。如果不给index,则默认添加到末尾。E get(int index):获取指定索引位置的元素。E set(int index, E element):替换指定索引位置的元素。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中的类型转换分为两种:自动类型转换(隐式转换)和强制类型转换(显式转换)。
- 自动类型转换:当一个较小范围的数据类型赋值给较大范围的数据类型时,Java会自动进行类型转换。例如,将
int类型赋值给long类型。
int a = 10;
long b = a; // 自动类型转换
  除此之外,`char`/`byte`/`short`类型在运算时会自动提升为`int`类型然后再进行运算。- 强制类型转换:当一个较大范围的数据类型赋值给较小范围的数据类型时,需要进行强制类型转换,可能会导致数据丢失。例如,将
double类型转换为int类型。
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语句可以使用以下数据类型:byte、short、char、int、枚举类型(enum)、字符串(String)和包装类(如Byte、Short、Character、Integer)。
有类似于js的箭头函数语法,可以省略掉冒号和break:
switch (variable) {
case value1 -> {
// code block
}
case value2 -> {
// code block
}
default -> {
// default code block
}
}Java内存
Java内存主要分为以下几个区域:
- 堆内存(Heap):用于存储对象实例和数组,是垃圾回收器管理的主要区域。堆内存分为新生代(Young Generation)和老年代(Old Generation)。
- 栈内存(Stack):用于存储方法调用和局部变量,每个线程都有自己的栈内存。
- 方法区(Method Area):用于存储类的元数据、静态变量和常量池。方法区在JVM规范中被称为永久代(PermGen),但在Java 8及以后版本中被称为元空间(Metaspace)。
- 本地方法栈(Native Method Stack):用于支持本地方法调用,存储本地方法的相关信息。
- 程序计数器(Program Counter Register)/寄存器:用于存储当前线程执行的字节码指令地址,是线程私有的。
创建新对象
创建新对象时,Java会在堆内存中分配空间,并初始化对象的属性。具体步骤如下:
- 类加载:JVM首先检查类是否已经加载,如果没有加载,则通过类加载器加载类的字节码。
- 分配内存:JVM在堆内存中为新对象分配足够的空间,并在占内存中申明局部变量,将堆内存的地址值存储起来。之后,堆内存里还要存储成员方法的地址,以便调用。
- 初始化对象:JVM调用类的构造方法,初始化对象的属性。初始化分为默认初始化、显式初始化和构造方法初始化三个阶段。
- 默认初始化:JVM为对象的属性分配默认值(如0、null等)。
- 显式初始化:如果在声明属性时指定了初始值,JVM会将这些值赋给属性。
- 构造方法初始化:JVM调用构造方法,执行构造方法中的代码,进一步初始化对象的属性。
- 返回引用:JVM返回新对象的引用,供程序使用。
字符串
- java中字符串是不可变的,也就是说字符串一旦创建就不能被修改。
- 字符串常量池:为了提高效率,Java在内存中维护了一个字符串常量池。当创建字符串字面值时,JVM会首先检查常量池中是否存在相同的字符串,如果存在则返回该字符串的引用,否则在常量池中创建一个新的字符串对象。因此,使用字符串字面值创建的字符串对象可能会共享同一个内存地址。如下地址是不同的:
String str1 = new String("hello");
String str2 = "hello";
System.out.println(str1 == str2); // false字符串比较
在Java中,字符串比较可以使用==运算符和equals()方法。==运算符比较的是两个字符串对象的引用地址是否相同,而equals()方法比较的是字符串的内容是否相同。因此,在比较字符串时,通常建议使用equals()方法以确保比较的是内容而不是引用地址。
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提供了丰富的字符串操作方法,常用的方法包括:
length():返回字符串的长度。charAt(int index):返回指定索引处的字符。注意!java不允许直接使用[1]这种形式进行取值substring(int beginIndex, int endIndex):返回字符串的子串。indexOf(String str):返回指定子串在字符串中首次出现的索引。lastIndexOf(String str):返回指定子串在字符串中最后一次出现的索引。toUpperCase():将字符串转换为大写。toLowerCase():将字符串转换为小写。trim():去除字符串两端的空白字符。replace(CharSequence target, CharSequence replacement):替换字符串中的指定子串。split(String regex):根据指定的正则表达式分割字符串,返回字符串数组。
StringBuilder
StringBuilder是Java中用于创建和操作可变字符串的类。与String类不同,StringBuilder允许在原有字符串的基础上进行修改,而不需要创建新的字符串对象,从而提高了性能,特别是在需要频繁修改字符串的场景下。Java在底层进行了优化,因此打印StringBuilder对象时会自动调用其toString()方法,将其转换为字符串进行输出,而非其地址值。
需要注意的是,StringBuilder是非线程安全的,如果在多线程环境下使用,建议使用StringBuffer类,它是线程安全的。
创建
可以通过以下方式创建StringBuilder对象:
StringBuilder sb1 = new StringBuilder(); // 创建一个空的StringBuilder
StringBuilder sb2 = new StringBuilder("Hello"); // 使用初始字符串创建StringBuilder
StringBuilder sb3 = new StringBuilder(50); // 创建一个具有指定初始容量的StringBuilder常用的方法
StringBuilder类提供了多种方法来操作字符串,常用的方法包括:
append(String str):将指定字符串追加到当前StringBuilder对象的末尾。insert(int offset, String str):在指定位置插入字符串。replace(int start, int end, String str):替换指定范围内的子串为新的字符串。delete(int start, int end):删除指定范围内的子串。reverse():反转StringBuilder中的字符顺序。toString():将StringBuilder对象转换为String对象。
StringJoiner
StringJoiner是Java 8引入的一个类,用于高效地构建由多个字符串组成的单一字符串,特别适用于需要在字符串之间添加分隔符的场景。StringJoiner允许指定前缀和后缀,从而更灵活地控制生成的字符串格式。
与StringBuilder类似,StringJoiner也是可变的,但它专注于字符串的连接操作,提供了更简洁的API来处理分隔符和边界。例如,以下代码展示了如何使用StringJoiner来连接字符串:
StringJoiner sj = new StringJoiner(", ", "[", "]");
sj.add("Apple");
sj.add("Banana");
sj.add("Cherry");
System.out.println(sj.toString()); // 输出: [Apple, Banana, Cherry]集合
Java集合框架提供了一组接口和类,用于存储和操作一组对象。相较于数组,集合具有更强的灵活性和功能,支持自动扩容、元素的添加和删除等操作。集合不可以存储基本数据类型,但可以通过包装类(如Integer、Double等)来存储对应的对象类型。
集合的使用要搭配上泛型(Generics),以确保类型安全,避免在运行时出现类型转换异常。具体的示例如下:
List<Integer> numbers = new ArrayList<>();
numbers.add(10); // 自动装箱,将基本类型int转换为Integer对象
numbers.add(20);
Integer firstNumber = numbers.get(0); // 自动拆箱,将Integer对象转换为基本类型int主要的集合类型包括:
- 列表(List):有序集合,允许重复元素。常用实现类有
ArrayList和LinkedList。 - 集合(Set):无序集合,不允许重复元素。常用实现类有
HashSet和TreeSet。 - 映射(Map):键值对集合,键唯一,值可以重复。常用实现类有
HashMap和TreeMap。 - 队列(Queue):先进先出(FIFO)的集合,常用实现类有
LinkedList和PriorityQueue。 - 栈(Stack):后进先出(LIFO)的集合,Java中可以使用
Deque接口的实现类如ArrayDeque来实现栈的功能。
常用方法
Java集合框架提供了丰富的方法来操作集合,常用的方法包括:
boolean add(E e):向集合中添加元素。boolean remove(Object o):从集合中删除指定元素。E remove(int index):从列表中删除指定索引处的元素。E get(int index):获取列表中指定索引处的元素。E set(int index, E element):替换列表中指定索引处的元素。int size():返回集合中元素的数量。boolean contains(Object o):检查集合中是否包含指定元素。void clear():清空集合中的所有元素。boolean isEmpty():检查集合是否为空。
Map
HashMap
HashMap是Java集合框架中的一个重要类,用于存储键值对映射关系。它基于哈希表实现,提供了高效的插入、删除和查找操作。HashMap允许使用null作为键和值,并且不保证元素的顺序。
HashMap的工作原理基于哈希函数,将键映射到哈希表中的索引位置。当插入一个键值对时,HashMap会计算键的哈希码,并根据哈希码确定存储位置。如果发生哈希冲突(即不同的键映射到相同的索引位置),HashMap会使用链表或红黑树来存储冲突的元素。
常用的HashMap方法包括:
put(K key, V value):将指定的键值对插入到HashMap中。get(Object key):根据键获取对应的值。remove(Object key):根据键删除对应的键值对。containsKey(Object key):检查HashMap中是否包含指定的键。containsValue(Object value):检查HashMap中是否包含指定的值。size():返回HashMap中键值对的数量。clear():清空HashMap中的所有键值对。
HashMap的初始化方式有多种,可以通过无参构造函数创建一个默认容量和负载因子的HashMap,也可以指定初始容量和负载因子。例如:
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方法包括:
put(K key, V value):将指定的键值对插入到TreeMap中。get(Object key):根据键获取对应的值。remove(Object key):根据键删除对应的键值对。containsKey(Object key):检查TreeMap中是否包含指定的键。containsValue(Object value):检查TreeMap中是否包含指定的值。size():返回TreeMap中键值对的数量。clear():清空TreeMap中的所有键值对。firstKey():返回TreeMap中最小的键。lastKey():返回TreeMap中最大的键。subMap(K fromKey, K toKey):返回指定范围内的键值对视图。
Stack
Java 推荐使用 Deque(双端队列)来模拟栈。可以使用 ArrayDeque 或 LinkedList 来实现栈的功能。以下是使用 ArrayDeque 实现栈的示例:
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 实现队列的示例:
Queue<Integer> queue = new LinkedList<>();
queue.offer(10); // 入队
queue.offer(20);
int front = queue.poll(); // 出队,front = 10
int peek = queue.peek(); // 查看队首元素,peek = 20但实际上,Deque 也可以用来实现队列功能:
Deque<Integer> deque = new ArrayDeque<>();
deque.offerLast(10); // 入队
deque.offerLast(20);
int front = deque.pollFirst(); // 出队,front = 10
int peek = deque.peekFirst(); // 查看队首元素,peek = 20LinkedList
LinkedList类实际上是个双向链表,其实现了Deque接口,因此可以同时用作栈和队列。使用LinkedList作为栈时,可以使用push()和pop()方法;作为队列时,可以使用offer()和poll()方法。
Arrays 类
Arrays类是Java标准库中的一个实用工具类,提供了多种静态方法来操作数组。常用的方法包括:
Arrays.sort(array):对数组进行排序,支持基本数据类型和对象数组。Arrays.binarySearch(array, key):在排序后的数组中进行二分查找,返回指定元素的索引。Arrays.equals(array1, array2):比较两个数组是否相等。Arrays.fill(array, value):将数组的所有元素填充为指定的值。Arrays.copyOf(original, newLength):复制数组,返回一个新的数组,长度为newLength。Arrays.toString(array):将数组转换为字符串表示形式,便于打印输出。Arrays.asList(array):将数组转换为固定大小的列表,返回一个List视图。Arrays.stream(array):将数组转换为流,便于进行函数式编程操作。
Arrays.stream的意义
Arrays.stream(array)方法将数组转换为流(Stream),使得可以利用Java 8引入的流式API对数组进行更灵活和强大的操作。这个的存在是为了直接处理原生数组,避免将其转换为集合类型,从而节省内存和提高性能。
然而,使用Arrays.stream()时需要注意以下几点:
基本数据类型数组:对于基本数据类型数组(如
int[]、double[]等),Arrays.stream()会返回对应的原始流类型(如IntStream、DoubleStream等),而不是对象流。这些原始流提供了专门的方法来处理基本数据类型,避免了装箱和拆箱的开销。但是!!!,对于这个流没有collect方法,需要使用toArray()方法将其转换为数组,使用boxed()方法将其转换为对象流,或者直接用sum(),max()等函数。对象数组:对于对象数组(如
String[]、Integer[]等),Arrays.stream()会返回一个对象流(Stream<T>),可以直接使用流的各种操作方法。
Arrays.asList的局限性
Arrays.asList(array)方法将数组转换为一个固定大小的列表(List视图),但这个列表有一些局限性需要注意:
- 固定大小:由
Arrays.asList()返回的列表是固定大小的,不能添加或删除元素。如果尝试调用add()或remove()方法,会抛出UnsupportedOperationException异常。 - 共享数据:返回的列表是原始数组的视图,对列表的修改会直接影响原始数组,反之亦然。这意味着如果修改了列表中的元素,原始数组也会相应地改变。
- 基本数据类型数组:对于基本数据类型数组(如
int[]、double[]等),Arrays.asList()会将整个数组作为单个元素添加到列表中,而不是将数组的每个元素分别添加为列表的元素。这可能导致意外的行为。
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关键字来声明类所属的包。例如:
package com.example.utils;
public class StringUtils {
// 类的内容
} 要使用其他包中的类,可以使用import关键字导入所需的类或整个包。例如:
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();
}
}仅两种情况下不需要导包:
- 使用与当前类在同一包中的类。
- 使用
java.lang包中的类,因为该包会被自动导入。
final
final关键字用于声明常量、方法和类,表示它们不能被修改或继承。具体含义如下:
final变量:表示常量,一旦赋值后不能再修改。通常使用大写字母和下划线命名常量。final修饰的基本数据类型变量,其值不能改变;final修饰的引用数据类型变量,其引用地址不能改变,但引用对象的内容可以改变。final方法:表示方法不能被子类重写。final类:表示类不能被继承。
双列集合
双列集合是指同时存储键和值的集合,常见的双列集合有Map接口及其实现类,如HashMap和TreeMap。双列集合允许通过键来访问对应的值,提供了高效的查找、插入和删除操作。 “键+值”这个整体,我们称为键值对/键值对对象/一个Entry(条目),每个Entry包含一个键和一个值。
Map的遍历方式
可以使用以下几种方式遍历Map中的键值对:
- 使用
entrySet()方法获取键值对集合,然后使用增强型for循环遍历:
Map<String, Integer> map = new HashMap<>();
for (Map.Entry<String, Integer> entry : map.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
// 处理键值对
}- 使用
keySet()方法获取键集合,然后通过键获取对应的值:
for (String key : map.keySet()) {
Integer value = map.get(key);
// 处理键值对
}- 使用
values()方法获取值集合,遍历值:
for (Integer value : map.values()) {
// 处理值
}- 使用
forEach方法(Java 8及以上版本):
map.forEach((key, value) -> {
// 处理键值对
});Collections集合工具类
Collections类是Java标准库中的一个实用工具类,提供了多种静态方法来操作集合。常用的方法包括:
sort(List<T> list):对列表进行排序,要求列表中的元素实现Comparable接口。reverse(List<?> list):反转列表中的元素顺序。shuffle(List<?> list):随机打乱列表中的元素顺序。binarySearch(List<? extends Comparable<? super T>> list, T key):在排序后的列表中进行二分查找,返回指定元素的索引。max(Collection<? extends T> coll):返回集合中的最大元素。min(Collection<? extends T> coll):返回集合中的最小元素。fill(List<? super T> list, T obj):将列表中的所有元素替换为指定的对象。copy(List<? super T> dest, List<? extends T> src):将源列表中的元素复制到目标列表中,要求目标列表的大小不小于源列表。synchronizedList(List<T> list):返回一个线程安全的列表。addAll(Collection<? super T> c, T... elements):将指定的元素添加到集合中。
有很多,这和go里面slices包的功能类似,都是提供一些对集合进行操作的工具方法,减少了我们自己去实现这些常见功能的麻烦。