1. 基本数据类型(Primitive Types)
Java 有 8 种基本数据类型,这些类型直接存储值,占用固定内存,效率较高。它们不属于类,没有方法或属性。
基本数据类型 | 关键字 | 大小 | 取值范围 |
---|---|---|---|
整数类型 | byte | 8 位 | -128 到 127 |
整数类型 | short | 16 位 | -32,768 到 32,767 |
整数类型 | int | 32 位 | -2^31 到 2^31-1 |
整数类型 | long | 64 位 | -2^63 到 2^63-1 |
浮点类型 | float | 32 位 | 单精度浮点数 |
浮点类型 | double | 64 位 | 双精度浮点数 |
字符类型 | char | 16 位 | 单个 Unicode 字符 |
布尔类型 | boolean | 不确定 | true 和 false |
2. 引用类型(Reference Types)
引用类型用于存储对象的引用,而不是对象本身。引用类型包括类、接口、数组、和枚举,它们在 Java 中用于更复杂的数据结构和逻辑控制。
Object obj
参数的含义在方法定义 参数类型
Object
表示可以传入任何对象类型,包含:
- 所有包装类(
Integer
、Double
、Character
等)- 自定义类
String
类型- 其他引用类型(如数组、集合等)
2.1 类(Class)
-
Java 中的大部分数据类型都是通过类创建的,类可以表示数据和行为(即属性和方法)。
-
类的实例称为对象,类的类型即为引用类型。
-
示例:
String
、Integer
、Double
、Person
等自定义类。String str = "Hello, World!"; Person person = new Person("John", 25);
2.2 接口(Interface)
-
接口是一种特殊的引用类型,用于定义类的公共行为。
-
接口不能实例化,但可以被类实现,接口类型可以用于多态性,指定对象的行为。
-
示例:
List
、Map
、Runnable
等都是接口类型。List<String> list = new ArrayList<>(); // List 是接口,ArrayList 是其实现 Runnable task = new MyRunnable(); // Runnable 是接口
2.3 数组(Array)
-
数组也是一种引用类型,表示一组相同类型的元素。
-
数组类型可以是基本数据类型数组(如
int[]
)或引用类型数组(如String[]
)。int[] numbers = {1, 2, 3}; // int 类型的数组 String[] names = {"Alice", "Bob"}; // String 类型的数组
2.4 枚举(Enum)
-
枚举类型用于表示一组有限的、固定的常量值,通常用于表示状态或一组相关常量。
-
枚举类型是一个特殊的类,它使用
enum
关键字定义。enum Day {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }Day today = Day.MONDAY;
2.5 包装类(Wrapper Class)
-
包装类是基本数据类型的对象版本,用于在需要对象类型的场景中使用基本类型的值。
-
每种基本类型都有对应的包装类,例如
int
对应Integer
,double
对应Double
。 -
包装类除了存储基本类型值,还提供了许多实用方法,如
parseInt
、toString
等。基本类型 包装类 int
Integer
double
Double
boolean
Boolean
char
Character
byte
Byte
short
Short
long
Long
float
Float
Integer number = 42; Boolean flag = true;
所有包装类都具有
toString()
方法。这是因为所有包装类都继承自Object
类,而Object
类定义了toString()
方法。因此,所有包装类(Integer
、Double
、Character
等)都可以调用toString()
方法。
3. 使用包装类的场景
3.1. 集合框架的需求
使用原因:Java 集合框架(如 List
、Set
、Map
等)只能存储对象,不能存储基本数据类型。当我们需要在集合中存储数值类型的数据时,必须使用包装类将基本类型转换为对象。这在统计、数据处理等需要存储大量数值的场景中非常常见。
示例场景
假设我们要存储一个班级中所有学生的分数,并计算平均分。我们可以使用 List<Integer>
来存储这些分数,因为集合只能存储对象类型。
List<Integer> scores = new ArrayList<>();
scores.add(85); // 自动装箱,将 int 85 转换为 Integer 对象
scores.add(90);
scores.add(78);// 计算平均分
int total = 0;
for (Integer score : scores) {total += score; // 自动拆箱,将 Integer 转换为 int 参与运算
}
double average = total / (double) scores.size();
System.out.println("Average score: " + average); // 输出: Average score: 84.3333
3.2. 需要表示 null
值的场合
使用原因:基本数据类型(如 int
、double
)不能表示 null
,而包装类可以。这在数据库查询、数据初始化和用户输入处理中非常有用,因为 null
可以表示“未赋值”或“无效”状态。例如,当从数据库读取用户年龄时,未填写的年龄字段可以用 null
表示,而不是一个默认数值(如 0
),以避免数据误解。
示例场景
假设我们从数据库中查询用户的年龄,如果用户未填写年龄,数据库会返回 null
。在 Java 中,我们可以用 Integer
来存储用户年龄,以便准确表示“未填写”的状态。
Integer age = null; // 表示年龄未填写// 判断是否填写了年龄
if (age == null) {System.out.println("Age is not provided.");
} else {System.out.println("Age: " + age);
}
3.3. 自动装箱和拆箱的便利性
使用原因:Java 支持自动装箱(基本类型自动转换为包装类)和自动拆箱(包装类自动转换为基本类型)。这种特性在集合和泛型的使用中尤其有用,因为我们可以直接操作基本类型的数值而无需手动转换,提升了代码的简洁性和可读性。
示例场景
假设我们有一个整数集合,想要将其中所有的分数加上 5 分。在遍历集合时,自动拆箱和装箱可以让我们直接进行数值操作,而无需手动转换。
List<Integer> scores = Arrays.asList(80, 85, 90);
List<Integer> updatedScores = new ArrayList<>();for (Integer score : scores) {updatedScores.add(score + 5); // 自动拆箱为 int,计算后自动装箱为 Integer
}
System.out.println("Updated scores: " + updatedScores); // 输出: [85, 90, 95]
3.4. 使用包装类的实用方法和常量
使用原因:包装类提供了许多实用方法和常量,便于数值转换、格式化、边界检查等操作。例如,Integer.parseInt()
可以将字符串转换为整数,Double.isNaN()
用于判断数值是否为 NaN
,Integer.MAX_VALUE
表示 int
的最大值。这些方法和常量使得包装类在数据处理和异常值检测中非常方便。
示例场景
假设我们从用户输入中获取一个字符串,并将其解析为整数。如果用户输入的是非数字内容,系统需要抛出异常。我们还可以检查浮点运算是否为 NaN
值。
try {int value = Integer.parseInt("123"); // 将字符串 "123" 转换为整数System.out.println("Parsed value: " + value);
} catch (NumberFormatException e) {System.out.println("Invalid number format");
}double result = 0.0 / 0.0; // 计算无效值
if (Double.isNaN(result)) {System.out.println("Result is NaN (not a number)");
}
3.5. 泛型类型的需求
使用原因:Java 泛型只能接受引用类型,不能直接使用基本数据类型。这使得包装类在泛型类和方法中不可或缺。泛型设计使代码更具通用性和类型安全性,而包装类的引入确保了基本类型的兼容性。
示例场景
假设我们创建一个泛型类 Box
,用于存储任意类型的数据。在泛型中,我们必须使用包装类来存储数值类型的数据。
class Box<T> {private T value;public Box(T value) { this.value = value; }public T getValue() { return value; }
}Box<Integer> intBox = new Box<>(100); // 必须使用 Integer 而非 int
System.out.println("Boxed value: " + intBox.getValue()); // 输出: Boxed value: 100
3.6. 多态性和对象方法调用
使用原因:包装类作为对象,能够享受面向对象编程的特性,例如多态性和对象方法调用。Java 的 Number
类是 Integer
、Double
等包装类的父类,可以用作这些包装类的通用类型。此外,包装类的对象可以直接调用许多实用方法。
示例场景
假设我们编写一个方法,接收 Number
类型的参数。这种多态性允许该方法接受 Integer
、Double
等所有包装类类型的数值参数。
public static void printDoubleValue(Number num) {System.out.println("Double value: " + num.doubleValue());
}printDoubleValue(new Integer(10)); // 自动转为 double
printDoubleValue(new Double(15.5));
3.7. 比较和排序
使用原因:包装类实现了 Comparable
接口,因此支持对象的排序和比较操作。基本数据类型不具备对象的功能,而包装类提供的比较和排序接口使其可以直接用于集合的排序需求。在排序和优先级管理场景中,包装类广泛用于 List
、Set
等集合的排序操作中。
示例场景
假设我们有一个学生分数的列表,想要按照分数从小到大排序。包装类 Integer
支持比较操作,可以直接在 Collections.sort()
中使用。
List<Integer> scores = Arrays.asList(88, 75, 90, 85);
Collections.sort(scores); // 自动排序
System.out.println("Sorted scores: " + scores); // 输出: [75, 85, 88, 90]
4.四种常用类型
4.1. String
类型
String
是 Java 中用于存储和处理文本数据的类。String
是不可变的,即一旦创建,字符串的内容就不能更改。每次对字符串进行修改时,都会创建一个新的 String
对象。
特点
- 不可变性:字符串在创建后不可修改,修改字符串时会创建新的
String
对象。 - 字符串池:Java 会将字符串字面量存储在一个字符串池(String Pool)中,以节省内存并提高性能。
- 丰富的字符串操作方法:
String
类提供了多种方法,如length()
、substring()
、charAt()
、equals()
、compareTo()
等,用于各种字符串操作。
常用方法
String str = "Hello, World!";// 获取字符串长度
int length = str.length(); // 13// 获取子字符串
String sub = str.substring(0, 5); // "Hello"// 字符串比较
boolean isEqual = str.equals("Hello, World!"); // true// 转换为大写
String upper = str.toUpperCase(); // "HELLO, WORLD!"
在 Java 中,
String
类型的对象可以通过两种方式创建:
- 直接赋值(字符串字面量):例如
String str1 = "Java";
- 这种方式会在 字符串池(String Pool) 中查找是否已有相同内容的字符串。
- 如果字符串池中存在该内容的字符串,则会复用池中的对象。
- 如果不存在,则会在字符串池中创建一个新对象。
- 使用
new
关键字:例如String str3 = new String("Java");
- 这种方式会直接在 堆内存 中创建一个新的
String
对象,而不使用字符串池。- 即使内容相同,这样创建的对象也会与字符串池中的对象不同。
String
和char
的相互转换是的,
String
和char
类型可以相互转换。以下是常用的转换方法:
从
String
提取char
:可以使用charAt(int index)
方法从String
中提取指定位置的字符。String str = "Hello"; char ch = str.charAt(1); // 提取第二个字符 'e'
将
char
转换为String
:可以直接使用Character.toString(char ch)
或String.valueOf(char ch)
,也可以通过字符串拼接的方式将char
转换为String
。char ch = 'A'; String str = Character.toString(ch); // 转换为 "A" // 或者使用 String str2 = String.valueOf(ch); // 转换为 "A"
示例
String str1 = "Java";
String str2 = "Java";
String str3 = new String("Java");// str1 和 str2 指向字符串池中的同一对象
System.out.println(str1 == str2); // true// str3 是新创建的对象,虽然内容相同,但不是同一对象
System.out.println(str1 == str3); // false
System.out.println(str1.equals(str3)); // true,内容相等
==
和equals
的区别
==
运算符:用于比较两个引用是否指向同一个对象,即比较两个对象的内存地址。
str1 == str3
比较的是两个字符串引用是否指向同一对象。- 因为
str1
指向字符串池中的对象,而str3
是一个新创建的对象,所以str1 == str3
为false
。equals
方法:用于比较两个字符串的内容是否相等。
str1.equals(str3)
比较的是两个字符串内容是否相同。- 即使两个字符串对象的引用不同,只要内容相同,
equals
方法也会返回true
。
String str1 = "Java";
和String str2 = "Java";
- 这两个字符串都是通过字面量创建的,
Java
作为字符串内容会被存储在字符串池中。- 因此,
str1
和str2
指向的是字符串池中的同一个对象。
String str3 = new String("Java");
- 使用
new
关键字创建的字符串对象总是会在堆内存中创建一个新对象。- 即使内容相同,
str3
也是一个新的对象,不会指向字符串池中的对象。- 所以,
str1 == str3
的结果为false
,因为它们不是同一个对象。
4.2. Character
类型
Character
是 char
基本数据类型的包装类,用于表示单个字符。Character
提供了大量实用方法,用于处理字符的属性,如判断字符类型(字母、数字、空格等)或大小写转换。
特点
- 字符操作方法:
Character
类提供了多种方法,用于判断字符类型、转换大小写等。 - 字符编码:
Character
基于 Unicode 设计,因此可以表示世界上几乎所有语言的字符。
常用方法
Character charValue = Character.valueOf('A'); // 创建 Character 对象// 判断是否为字母
boolean isLetter = Character.isLetter('A'); // true// 判断是否为数字
boolean isDigit = Character.isDigit('1'); // true// 转换为小写
char lower = Character.toLowerCase('B'); // 'b'
示例
Character a = Character.valueOf('A');
Character b = Character.valueOf('A');// 字符在范围内时可能会缓存
System.out.println(a == b); // true// 转换大小写
System.out.println(Character.toLowerCase('Z')); // 'z'
System.out.println(Character.toUpperCase('z')); // 'Z'
4.3. Integer
类型
Integer
是 int
基本数据类型的包装类。包装类允许在需要对象的场合使用基本类型,例如在集合框架中。Integer
提供了许多静态方法,方便整数的处理和转换。
特点
- 缓存机制:Java 对
-128
到127
之间的整数进行缓存,若数值在此范围内且通过Integer.valueOf
创建,则会返回相同的对象。 - 常用静态方法:
Integer
提供了一些实用的静态方法,如parseInt()
将字符串转为int
,valueOf()
创建Integer
对象。
常用方法
Integer intValue = Integer.valueOf(10); // 创建 Integer 对象// 将字符串转换为 int
int parsedInt = Integer.parseInt("123"); // 123// 获取 Integer 的最大值
int maxValue = Integer.MAX_VALUE; // 2147483647
Integer.parseInt("123")
的转换规则
Integer.parseInt("123")
:这个方法用于将字符串表示的数值转换为int
类型。例如Integer.parseInt("123")
的结果是123
。非法输入的处理:如果输入的字符串不是有效的数字(如包含字母),则会抛出
NumberFormatException
异常,而不会进行任何转换。
- 例如,
Integer.parseInt("abc")
会抛出异常,而不是转换为 ASCII 码。
示例
Integer a = Integer.valueOf(100);
Integer b = Integer.valueOf(100);
Integer c = Integer.valueOf(200);// a 和 b 指向相同的缓存对象
System.out.println(a == b); // true// c 不在缓存范围内,创建新对象
System.out.println(a == c); // false
4.4. Double
类型
Double
是 double
基本数据类型的包装类,表示双精度浮点数。Double
用于存储和处理小数数据,在需要对象的场合使用。
特点
- 精度:双精度浮点数具有较高的精度,适合存储大范围或高精度的小数值。
- NaN 和 Infinity:
Double
提供了NaN
(非数值)和Infinity
(无穷大)等特殊值,用于表示非法或超出范围的结果。 - 常用静态方法:
Double
提供了将字符串转换为double
的方法parseDouble()
,还可以使用Double.valueOf()
创建Double
对象。
常用方法
Double doubleValue = Double.valueOf(5.5); // 创建 Double 对象// 将字符串转换为 double
double parsedDouble = Double.parseDouble("123.45"); // 123.45// 检查是否为 NaN
boolean isNaN = Double.isNaN(0.0 / 0.0); // true// 获取 Double 的最大值
double maxValue = Double.MAX_VALUE; // 1.7976931348623157E308
示例
Double x = Double.valueOf(10.5);
Double y = Double.valueOf(10.5);// 浮点数没有缓存机制,不同对象
System.out.println(x == y); // false// 内容相同
System.out.println(x.equals(y)); // true
5.Vector
Vector
是 Java 中的一种动态数组实现类,位于 java.util
包中,因此它属于引用类型。与 ArrayList
类似,Vector
允许动态调整容量以容纳更多的元素,但与 ArrayList
不同的是,Vector
是线程安全的,所有方法都被同步(synchronized
)修饰,因此可以在多线程环境中安全使用。
Vector
的主要特点
- 动态数组:
Vector
是一个动态数组,可以自动调整容量。与普通数组不同,Vector
的大小不是固定的,它会根据需要自动扩展。 - 线程安全:
Vector
是线程安全的,因为它的所有方法都被synchronized
修饰。这样保证了在多线程环境中不会发生并发修改问题。 - 有序存储:
Vector
按照插入顺序存储元素,即添加元素的顺序就是迭代时的顺序。 - 支持随机访问:
Vector
通过索引可以快速访问元素,适合需要频繁按索引访问的场景。 - 扩容机制:
Vector
默认的初始容量是 10,当容量不够时,会自动扩展到原容量的 2 倍,或者可以指定扩容因子来控制增长速度。
5.1. 创建 Vector
Vector
提供了多种构造方法,允许我们指定初始容量和容量增量(扩展大小)。
Vector()
:创建一个默认容量为 10 的空向量。Vector(int initialCapacity)
:创建一个指定初始容量的空向量。Vector(int initialCapacity, int capacityIncrement)
:创建一个指定初始容量和容量增量的空向量。容量增量用于控制扩展速度。
import java.util.Vector;public class VectorCreationExample {public static void main(String[] args) {Vector<String> vector1 = new Vector<>(); // 默认容量 10Vector<String> vector2 = new Vector<>(20); // 指定初始容量为 20Vector<String> vector3 = new Vector<>(10, 5); // 初始容量 10,扩展增量 5System.out.println("Vector1 capacity: " + vector1.capacity()); // 输出: 10System.out.println("Vector2 capacity: " + vector2.capacity()); // 输出: 20System.out.println("Vector3 capacity: " + vector3.capacity()); // 输出: 10}
}
5.2. 添加元素
Vector
提供了多种方法来添加元素:
add(E e)
:将元素添加到Vector
的末尾。add(int index, E element)
:在指定索引处插入元素。addAll(Collection<? extends E> c)
:将另一个集合中的所有元素添加到Vector
。
import java.util.Vector;public class VectorAddExample {public static void main(String[] args) {Vector<String> vector = new Vector<>();vector.add("Alice"); // 添加到末尾vector.add("Bob");vector.add(1, "Charlie"); // 在索引 1 处插入 "Charlie"Vector<String> moreNames = new Vector<>();moreNames.add("David");moreNames.add("Eve");vector.addAll(moreNames); // 将 moreNames 中的元素添加到 vectorSystem.out.println("Vector after additions: " + vector); // 输出: [Alice, Charlie, Bob, David, Eve]}
}
5.3. 访问和查询元素
Vector
支持通过索引访问元素,还可以检查元素是否存在,获取首尾元素等。
get(int index)
:返回指定索引处的元素。firstElement()
:返回第一个元素。lastElement()
:返回最后一个元素。contains(Object o)
:检查Vector
是否包含指定的元素。indexOf(Object o)
:返回指定元素首次出现的索引位置。size()
:返回Vector
中的元素数量。
import java.util.Vector;public class VectorAccessExample {public static void main(String[] args) {Vector<String> vector = new Vector<>();vector.add("Alice");vector.add("Bob");vector.add("Charlie");// 访问元素System.out.println("First element: " + vector.firstElement()); // 输出: AliceSystem.out.println("Last element: " + vector.lastElement()); // 输出: CharlieSystem.out.println("Element at index 1: " + vector.get(1)); // 输出: Bob// 查询元素System.out.println("Contains 'Alice'? " + vector.contains("Alice")); // 输出: trueSystem.out.println("Index of 'Charlie': " + vector.indexOf("Charlie")); // 输出: 2System.out.println("Vector size: " + vector.size()); // 输出: 3}
}
5.4. 更新元素
使用 set(int index, E element)
方法可以替换 Vector
中指定索引位置的元素。
import java.util.Vector;public class VectorUpdateExample {public static void main(String[] args) {Vector<String> vector = new Vector<>();vector.add("Alice");vector.add("Bob");vector.add("Charlie");// 更新元素vector.set(1, "Bob Updated"); // 更新索引 1 的元素System.out.println("Vector after update: " + vector); // 输出: [Alice, Bob Updated, Charlie]}
}
5.5. 删除元素
Vector
提供了多种方法删除元素,可以通过索引删除,也可以按值删除,还可以清空 Vector
。
remove(int index)
:删除指定索引位置的元素。remove(Object o)
:删除首次出现的指定元素。removeAll(Collection<?> c)
:删除Vector
中包含在指定集合中的所有元素。clear()
:清空Vector
,删除所有元素。
import java.util.Vector;public class VectorRemoveExample {public static void main(String[] args) {Vector<String> vector = new Vector<>();vector.add("Alice");vector.add("Bob");vector.add("Charlie");// 按索引删除元素vector.remove(1); // 删除索引 1 的元素 "Bob"System.out.println("After removing index 1: " + vector); // 输出: [Alice, Charlie]// 按值删除元素vector.remove("Alice");System.out.println("After removing Alice: " + vector); // 输出: [Charlie]// 清空 Vectorvector.clear();System.out.println("After clearing: " + vector); // 输出: []}
}
5.6. 遍历 Vector
可以使用 for
循环、增强 for
循环或 Iterator
来遍历 Vector
。
iterator()
:返回Vector
的迭代器。elements()
:返回Enumeration
对象,适用于旧版本的代码,但现在更推荐使用Iterator
。
import java.util.Iterator;
import java.util.Vector;public class VectorIterationExample {public static void main(String[] args) {Vector<String> vector = new Vector<>();vector.add("Alice");vector.add("Bob");vector.add("Charlie");// 使用 for 循环System.out.println("Using for loop:");for (int i = 0; i < vector.size(); i++) {System.out.println(vector.get(i));}// 使用增强型 for 循环System.out.println("Using enhanced for loop:");for (String name : vector) {System.out.println(name);}// 使用 IteratorSystem.out.println("Using Iterator:");Iterator<String> iterator = vector.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());}}
}
5.7. 扩容机制
当 Vector
的容量不足以容纳新元素时,会自动扩容。默认情况下,Vector
每次扩容为原容量的 2 倍。不过,可以通过构造方法指定扩展因子,控制每次扩容的增量。
import java.util.Vector;public class VectorCapacityExample {public static void main(String[] args) {Vector<Integer> vector = new Vector<>(3, 2); // 初始容量为 3,扩展因子为 2// 添加元素触发扩容for (int i = 1; i <= 5; i++) {vector.add(i);System.out.println("Capacity after adding element " + i + ": " + vector.capacity());}}
}