说明:
- 《Java必知必会》是一档专为Java学习者设计的专栏,旨在帮助读者系统地掌握Java编程语言的核心知识,从基础语法到高级特性,从理论应用到实战演练,一站式解决你的Java学习难题。可在我的专栏里订阅,该专栏一共有15篇,本篇是第二篇。
- 会先教会大家如何使用,并给出相应的练习题示例
- 作业习题是帮助大家巩固所学内容,需要大家独立完成
- 学完 入门篇 和 基础篇 就算掌握Java的常用基础知识了,下一篇开始学习Java程序员必备的数据库MySQL
目录
1.7 作业习题:实现一个类的构造方法,并且创建该类的一个实例(对象)
2.4 作业习题:应用面向对象编程的封装、继承、多态三大特点
4.5 作业习题:使用LocalDateTime实现计算1000次LocalDate的plusDays(1) 方法所消耗的时间
5.4 作业习题:给定两个集合 A 和 B,请编写一个方法,输出这两个集合的并集和交集
7.4 作业习题:使用非缓存输入/输出流 实现(字节和字符流) 读取和写入文件的操作
一、类和对象
1.1 类
类是创建对象的蓝图或模板。它是一个定义对象的属性(变量)和行为(方法)的模板。在Java中,类是用于定义对象的特征和行为的自定义数据类型。
public class Car {
// 属性(成员变量)
String color;
String model;
int year;
// 方法(行为)
public void start() {
System.out.println("汽车启动了");
}
public void stop() {
System.out.println("汽车停止了");
}
}
1.2 对象
对象是类的实例。当你创建一个对象时,你实际上是在类的基础上创建了一个具体的实体。对象通常具有状态(由属性表示)和行为(由方法表示)。
public class Main {
public static void main(String[] args) {
// 创建Car类的一个对象
Car myCar = new Car();
// 设置对象的属性
myCar.color = "红色";
myCar.model = "SUV";
myCar.year = 2024;
// 调用对象的方法
myCar.start();
myCar.stop();
}
}
1.3 类的构造方法
- 构造方法名必须与类名相同。
- 构造方法没有返回类型,也不可以使用void。
- 构造方法在创建对象时自动调用。
- 构造方法可以重载,即一个类可以有多个构造方法,参数列表必须不同。
定义构造方法的例子:
public class Person {
String name;
int age;
// 无参构造方法
public Person() {
name = "未知";
age = 0;
}
// 带参数的构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
以下是创建Person
对象并调用构造方法的示例:
public class Main {
public static void main(String[] args) {
// 使用无参构造方法创建对象
Person person1 = new Person();
System.out.println(person1.name + " " + person1.age); // 输出:未知 0
// 使用带参数的构造方法创建对象
Person person2 = new Person("Alice", 30);
System.out.println(person2.name + " " + person2.age); // 输出:Alice 30
}
}
1.4 this关键字
this
关键字是一个引用当前对象的特殊关键字。它可以用于类的实例方法内部,也可以用于构造方法中。
使用this
关键字来引用当前对象的属性:
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name; // 使用this区分局部变量和实例变量
this.age = age;
}
public void setName(String name) {
this.name = name; // 使用this引用当前对象的name属性
}
public String getName() {
return this.name; // 使用this引用当前对象的name属性
}
public void setAge(int age) {
this.age = age; // 使用this引用当前对象的age属性
}
public int getAge() {
return this.age; // 使用this引用当前对象的age属性
}
}
使用this
关键字来调用当前对象的其他方法:
public class Person {
// ...
public void printDetails() {
System.out.println("Name: " + this.getName()); // 调用getName()方法
System.out.println("Age: " + this.getAge()); // 调用getAge()方法
}
}
在一个类的构造方法中,可以使用this
关键字来调用同一个类的另一个构造方法:
public class Person {
private String name;
private int age;
public Person() {
this("Unknown", 0); // 调用另一个构造方法
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// ...
}
注意事项
this
关键字只能在实例方法或构造方法中使用,不能在静态方法中使用,因为静态方法不属于任何特定的对象实例。- 在构造方法中,
this
调用必须位于方法的第一行,否则编译器会报错。
1.5 抽象
抽象类
- 抽象类使用
abstract
关键字来定义。 - 它可以包含抽象方法(没有具体实现的方法)和具体实现的方法。
- 抽象类可以有构造器。
- 抽象类可以包含成员变量,包括非final变量。
抽象方法
- 抽象方法是一种没有方法体的方法,它只有签名和一个分号。
- 抽象方法也用
abstract
关键字声明。 - 抽象方法必须在子类中被实现,除非子类也是抽象类。
下面是一个Java抽象类的简单例子:
public abstract class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public abstract void sound(); // 抽象方法
public void sleep() {
System.out.println(name + " is sleeping.");
}
}
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void sound() {
System.out.println("Woof!");
}
}
1.6 接口
接口(Interface)是一种引用类型,类似于类,用于存放抽象方法和静态常量。接口定义了一个规范,规定了实现接口的类应具备哪些方法。以下是关于Java接口的一些详细信息:
接口的定义
- 接口使用
interface
关键字来定义。 - 接口中的方法默认是
public
和abstract
的,即它们没有方法体。 - 接口中的变量默认是
public
、static
和final
的,即它们是常量。 - 从Java 8开始,接口可以包含默认方法和静态方法。
接口的方法
- 抽象方法:接口中的抽象方法没有具体的实现,它们只有方法签名。
- 默认方法:从Java 8开始,接口可以包含默认方法,它们有默认的实现,但可以被实现类覆盖。
- 静态方法:接口可以包含静态方法,这些方法可以有具体的实现,但不能被实现类覆盖。
下面是一个Java接口的简单例子:
public interface Animal {
void sound(); // 抽象方法
// Java 8的默认方法
default void sleep() {
System.out.println("Animal is sleeping.");
}
// Java 8的静态方法
static int getAge() {
return 5;
}
}
public class Dog implements Animal {
@Override
public void sound() {
System.out.println("Woof!");
}
}
1.7 作业习题:实现一个类的构造方法,并且创建该类的一个实例(对象)
二、面向对象编程的特点
2.1 封装
- 隐藏实现细节:对象的内部状态(属性)和实现逻辑(方法)对外界是不可见的。
- 访问控制:通过使用访问修饰符(如
private
,protected
,public
)来控制类成员的访问级别。 - 接口与实现分离:用户只需知道如何使用对象,而不必关心对象内部的具体实现。
public class BankAccount {
private double balance; // 私有属性,外部不能直接访问
// 公共方法,用于获取账户余额
public double getBalance() {
return balance;
}
// 公共方法,用于存款
public void deposit(double amount) {
if(amount > 0) {
balance += amount;
}
}
// 公共方法,用于取款
public void withdraw(double amount) {
if(amount <= balance && amount > 0) {
balance -= amount;
}
}
}
在这个例子中,balance
是一个私有属性,只能通过 deposit
和 withdraw
方法来修改,而不能直接从类的外部访问。
2.2 继承
- 代码复用:子类可以继承父类的属性和方法,无需重新编写。
- 扩展性:子类可以添加新的属性和方法,也可以覆盖(重写)父类的方法。
- 层次结构:通过继承,可以创建类之间的层次结构。
public class Animal {
void eat() {
System.out.println("动物正在吃东西");
}
}
public class Dog extends Animal {
void bark() {
System.out.println("狗在叫");
}
}
在这个例子中,Dog
类继承了 Animal
类,因此 Dog
类的对象可以使用 eat
方法。
2.3 多态
- 方法重写:子类可以重写父类的方法,以提供不同的实现。
- 方法重载:同一个类中可以存在多个同名方法,但参数列表必须不同。
- 动态绑定:方法的调用是在运行时根据对象的实际类型来决定的。
public class Animal {
void sound() {
System.out.println("动物发出声音");
}
}
public class Dog extends Animal {
@Override
void sound() {
System.out.println("狗在叫");
}
}
public class Cat extends Animal {
@Override
void sound() {
System.out.println("猫在喵喵叫");
}
}
public class TestPolymorphism {
public static void main(String[] args) {
Animal myAnimal = new Dog(); // Dog对象被向上转型为Animal类型
Animal myOtherAnimal = new Cat(); // Cat对象被向上转型为Animal类型
myAnimal.sound(); // 输出:狗在叫
myOtherAnimal.sound(); // 输出:猫在喵喵叫
}
}
在这个例子中,尽管 myAnimal
和 myOtherAnimal
被声明为 Animal
类型,但它们分别引用了 Dog
和 Cat
类型的对象。调用 sound
方法时,会根据对象的实际类型来决定调用哪个类的方法,这就是多态的表现。
2.4 作业习题:应用面向对象编程的封装、继承、多态三大特点
三、字符串(String类)
3.1 字符串查找
boolean contains(CharSequence s)
:检查字符串是否包含指定的字符序列。int indexOf(int ch)
:返回指定字符在此字符串中第一次出现处的索引。int indexOf(int ch, int fromIndex)
:返回指定字符在此字符串中第一次出现处的索引,从指定的索引开始搜索。int indexOf(String str)
:返回指定子字符串在此字符串中第一次出现处的索引。int indexOf(String str, int fromIndex)
:返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始。int lastIndexOf(int ch)
:返回指定字符在此字符串中最后一次出现处的索引。int lastIndexOf(int ch, int fromIndex)
:返回指定字符在此字符串中最后一次出现处的索引,从指定的索引开始向后搜索。int lastIndexOf(String str)
:返回指定子字符串在此字符串中最右边出现处的索引。int lastIndexOf(String str, int fromIndex)
:返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始向后搜索。
3.2 字符串比较
boolean equals(Object anObject)
:将此字符串与指定的对象比较。boolean equalsIgnoreCase(String anotherString)
:将此字符串与另一个字符串比较,不考虑大小写。int compareTo(String anotherString)
:按字典顺序比较两个字符串。int compareToIgnoreCase(String str)
:按字典顺序比较两个字符串,不考虑大小写。
3.3 字符串修改
String concat(String str)
:将指定的字符串连接到此字符串的末尾。String replace(char oldChar, char newChar)
:返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。String replace(CharSequence target, CharSequence replacement)
:使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。String replaceAll(String regex, String replacement)
:使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。String replaceFirst(String regex, String replacement)
:使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。String substring(int beginIndex)
:返回一个新的字符串,它是此字符串的一个子字符串。String substring(int beginIndex, int endIndex)
:返回一个新字符串,它是此字符串的一个子字符串。String toLowerCase()
:使用默认语言环境的规则将此 String 中的所有小写字。String toUpperCase()
:使用默认语言环境的规则将此 String 中的所有小写字转换成大写。String trim()
:删除指定字符串的首尾空白符。
3.4 字符串与其他类型转换
char[] toCharArray()
:将此字符串转换为一个新的字符数组。static String copyValueOf(char[] data)
:返回指定数组中表示该字符序列的 String。static String copyValueOf(char[] data, int offset, int count)
:返回指定数组中表示该字符序列的 String。static String valueOf(primitive data type x)
:返回指定类型参数的字符串表示形式。
3.5 其他字符串操作
boolean isEmpty()
:当且仅当 length() 为 0 时返回 true。boolean startsWith(String prefix)
:测试此字符串是否以指定的前缀开始。boolean endsWith(String suffix)
:测试此字符串是否以指定的后缀结束。String intern()
:返回字符串对象的规范化表示形式。int hashCode()
:返回此字符串的哈希码。boolean matches(String regex)
:告知此字符串是否匹配给定的正则表达式。String[] split(String regex)
:根据匹配给定的正则表达式来拆分此字符串。String[] split(String regex, int limit)
:根据匹配给定的正则表达式来拆分此字符串,最多不超过 limit 个,如果超过了,剩下的全部放到最后一个元素中。
3.6 字符串示例:
如果对字符串的每个操作进行详细解析会导致文章的篇幅过长,为了避免冗余,选择对字符串的常用操作的示例总结,如下:
创建字符串
String str1 = "Hello";
String str2 = new String("World");
字符串长度
int length = str1.length();
字符串连接
String concatenated = str1 + " " + str2;
字符串比较
boolean isEqual = str1.equals(str2);
boolean isSame = str1 == str2; // 通常不推荐用于字符串比较,因为它比较的是引用
字符串查找
int index = str1.indexOf("lo");
boolean contains = str1.contains("ell");
字符串截取
String subStr = str1.substring(1, 4); // 从索引1到索引3(不包括4)
字符串替换
String replaced = str1.replace("Hello", "Hi");
字符串分割
String[] words = str1.split(" ");
字符串大小写转换
String upperCase = str1.toUpperCase();
String lowerCase = str1.toLowerCase();
字符串去除空格
String trimmed = str1.trim(); // 去除首尾空格
字符串格式化
String formatted = String.format("Name: %s, Age: %d", "John", 30);
字符串与基本数据类型的转换
int num = Integer.parseInt("123");
String strNum = String.valueOf(123);
字符串编码和解码
byte[] bytes = str1.getBytes(); // 默认使用平台默认编码
String strFromBytes = new String(bytes, "UTF-8"); // 使用UTF-8解码
3.7 作业习题:实现对三个数组的元素进行合并排序
四、常用类
4.1 包装类
Integer类
Integer
类是int
基本数据类型的包装类。- 它提供了多个方法来处理
int
类型的值,例如将字符串转换为整数、比较整数等。 - 常用方法包括:
parseInt(String s)
、valueOf(String s)
、toString(int i)
等。 - 自Java 5起,引入了自动装箱和拆箱,允许
int
和Integer
之间自动转换。
示例代码:
public class IntegerExample {
public static void main(String[] args) {
Integer intObj1 = 10; // 自动装箱
Integer intObj2 = Integer.valueOf("20");
int intVal = intObj1 + intObj2; // 自动拆箱
System.out.println("两个整数的和: " + intVal);
}
}
Double类
Double
类是double
基本数据类型的包装类。- 它同样提供了处理
double
值的方法,如字符串到double
的转换、比较double
值等。 - 常用方法包括:
parseDouble(String s)
、valueOf(String s)
、toString(double d)
等。 - 也支持自动装箱和拆箱。
示例代码:
public class DoubleExample {
public static void main(String[] args) {
Double doubleObj1 = 10.5; // 自动装箱
Double doubleObj2 = Double.valueOf("20.5");
double doubleVal = doubleObj1 + doubleObj2; // 自动拆箱
System.out.println("两个双精度浮点数的和: " + doubleVal);
}
}
Boolean类
Boolean
类是boolean
基本数据类型的包装类。- 提供了将字符串转换为
boolean
值的方法,以及boolean
值的常用逻辑操作。 - 常用方法包括:
parseBoolean(String s)
、valueOf(String s)
、toString(boolean b)
等。 - 同样支持自动装箱和拆箱。
示例代码:
public class BooleanExample {
public static void main(String[] args) {
Boolean boolObj1 = true; // 自动装箱
Boolean boolObj2 = Boolean.valueOf("false");
boolean boolVal = boolObj1 && boolObj2; // 自动拆箱
System.out.println("两个布尔值的逻辑与: " + boolVal);
}
}
Character类
Character
类是char
基本数据类型的包装类。- 它提供了一些处理
char
值的方法,比如判断字符是否是大写、小写、数字等。 - 常用方法包括:
isLetter(char ch)
、isDigit(char ch)
、toUpperCase(char ch)
、toLowerCase(char ch)
等。 - 也支持自动装箱和拆箱
示例代码:
public class CharacterExample {
public static void main(String[] args) {
Character charObj = 'A'; // 自动装箱
boolean isUpperCase = Character.isUpperCase(charObj); // 自动拆箱
System.out.println("字符是否为大写: " + isUpperCase);
}
}
Number类
Number
类是所有数值包装类的抽象超类。- 它提供了将数值转换为不同基本数据类型的方法,如
byteValue()
、shortValue()
、intValue()
、longValue()
、floatValue()
和doubleValue()
。 Number
类的子类包括Integer
、Double
、Float
、Byte
、Short
、Long
和BigDecimal
。
public class NumberExample {
public static void main(String[] args) {
Double numberObj = 123.456; // 自动装箱
int intValue = numberObj.intValue();
long longValue = numberObj.longValue();
float floatValue = numberObj.floatValue();
System.out.println("Double转换为int: " + intValue);
System.out.println("Double转换为long: " + longValue);
System.out.println("Double转换为float: " + floatValue);
}
}
4.2 数字处理
数字格式化
Java提供了多种方式来格式化数字,包括使用String.format()
方法和DecimalFormat
类。
Math类
Math
类是一个包含基本数学运算的类,如三角函数、对数、平方根等。它提供了静态方法,因此不需要实例化即可使用。
重要方法:
sqrt(double a)
: 返回a
的平方根。pow(double a, double b)
: 返回a
的b
次幂。sin(double a)
: 返回角度a
的正弦值(以弧度为单位)。cos(double a)
: 返回角度a
的余弦值(以弧度为单位)。tan(double a)
: 返回角度a
的正切值(以弧度为单位)。log(double a)
: 返回a
的自然对数。max(double a, double b)
: 返回a
和b
中的最大值。min(double a, double b)
: 返回a
和b
中的最小值。random()
: 返回一个大于等于0.0且小于1.0的随机浮点数。
public class MathExample {
public static void main(String[] args) {
double x = 10.0;
double y = 5.0;
// 计算平方根
double sqrtX = Math.sqrt(x);
System.out.println("Square root of " + x + " is " + sqrtX);
// 计算x的y次幂
double powXY = Math.pow(x, y);
System.out.println(x + " raised to the power of " + y + " is " + powXY);
// 计算随机数
double randomNum = Math.random();
System.out.println("A random number between 0.0 and 1.0 is " + randomNum);
}
}
Random类
Random
类用于生成伪随机数。它提供了多种方法来生成不同类型的随机数。
重要方法:
nextInt()
: 返回一个随机整数。nextInt(int bound)
: 返回一个介于0(包含)和指定值(不包含)之间的随机整数。nextDouble()
: 返回一个介于0.0(包含)和1.0(不包含)之间的随机浮点数。nextBoolean()
: 返回一个随机布尔值。
import java.util.Random;
public class RandomExample {
public static void main(String[] args) {
Random random = new Random();
// 生成一个随机整数
int intRandom = random.nextInt();
System.out.println("A random integer: " + intRandom);
// 生成一个介于0到100之间的随机整数
int intRandomBound = random.nextInt(100);
System.out.println("A random integer between 0 and 100: " + intRandomBound);
// 生成一个随机浮点数
double doubleRandom = random.nextDouble();
System.out.println("A random double between 0.0 and 1.0: " + doubleRandom);
// 生成一个随机布尔值
boolean boolRandom = random.nextBoolean();
System.out.println("A random boolean: " + boolRandom);
}
}
BigInteger类
BigInteger
类用于处理任意精度的整数运算。它没有直接对应的基本类型,因此必须使用构造函数来创建实例。
重要方法:
add(BigInteger val)
: 返回两个BigInteger
的和。subtract(BigInteger val)
: 返回两个BigInteger
的差。multiply(BigInteger val)
: 返回两个BigInteger
的乘积。divide(BigInteger val)
: 返回两个BigInteger
的商。
import java.math.BigInteger;
public class BigIntegerExample {
public static void main(String[] args) {
BigInteger bigInt1 = new BigInteger("123456789012345678901234567890");
BigInteger bigInt2 = new BigInteger("987654321098765432109876543210");
// 加法
BigInteger sum = bigInt1.add(bigInt2);
System.out.println("Sum: " + sum);
// 乘法
BigInteger product = bigInt1.multiply(bigInt2);
System.out.println("Product: " + product);
}
}
BigDecimal类
BigDecimal
类用于处理任意精度的十进制数。与BigInteger
类似,它也是不可变的,并且用于精确的数学计算。
BigDecimal(String val)
: 通过字符串构造一个BigDecimal
对象。BigDecimal(double val)
: 通过double
值构造一个BigDecimal
对象,通常不推荐使用,因为它可能会失去精度。BigDecimal(BigInteger val)
: 通过BigInteger
对象构造一个BigDecimal
对象。add(BigDecimal augend)
: 返回this
和augend
的和。subtract(BigDecimal subtrahend)
: 返回this
减去subtrahend
的结果。multiply(BigDecimal multiplicand)
: 返回this
和multiplicand
的乘积。divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
: 返回this
除以divisor
的结果,结果按照指定的scale
和roundingMode
进行四舍五入。divide(BigDecimal divisor)
: 返回this
除以divisor
的结果,不进行四舍五入。setScale(int newScale, RoundingMode roundingMode)
: 返回一个新的BigDecimal
,其值是this
值按指定scale
和roundingMode
四舍五入的结果。compareTo(BigDecimal val)
: 比较两个BigDecimal
对象的大小。intValue()
: 返回BigDecimal
的int
值,通过丢失精度。longValue()
: 返回BigDecimal
的long
值,通过丢失精度。doubleValue()
: 返回BigDecimal
的double
值,通过丢失精度。sqrt(int precision)
: 返回BigDecimal
的平方根,精度由precision
指定。pow(int n)
: 返回BigDecimal
的n
次幂。
示例代码:
import java.math.BigDecimal;
import java.math.RoundingMode;
public class BigDecimalExample {
public static void main(String[] args) {
BigDecimal bd1 = new BigDecimal("123.456");
BigDecimal bd2 = new BigDecimal("7.89");
// 加法
BigDecimal sum = bd1.add(bd2);
System.out.println("加法结果: " + sum);
// 减法
BigDecimal difference = bd1.subtract(bd2);
System.out.println("减法结果: " + difference);
// 乘法
BigDecimal product = bd1.multiply(bd2);
System.out.println("乘法结果: " + product);
// 除法
BigDecimal quotient = bd1.divide(bd2, 3, RoundingMode.HALF_UP);
System.out.println("除法结果: " + quotient);
// 四舍五入
BigDecimal rounded = bd1.setScale(2, RoundingMode.HALF_UP);
System.out.println("四舍五入结果: " + rounded);
// 比较大小
int comparison = bd1.compareTo(bd2);
if (comparison > 0) {
System.out.println("bd1 大于 bd2");
} else if (comparison < 0) {
System.out.println("bd1 小于 bd2");
} else {
System.out.println("bd1 等于 bd2");
}
}
}
4.3 系统类
System
类是一个final类,所以不允许被继承。它包含一些有用的类字段和方法,用于与系统环境进行交互。
类字段
System.in
: 标准输入流(通常是键盘输入)。System.out
: 标准输出流(通常是控制台输出)。System.err
: 标准错误输出流(通常是控制台错误输出)。
方法
arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
: 从一个数组复制一段内容到另一个数组。currentTimeMillis()
: 返回当前时间(以毫秒为单位,从1970年1月1日00:00:00 UTC开始)。gc()
: 请求Java虚拟机进行垃圾回收。exit(int status)
: 终止当前运行的Java虚拟机,status
为退出状态码。identityHashCode(Object x)
: 返回对象的内存地址的哈希码。getProperties()
: 获取当前系统属性。setProperty(String key, String value)
: 设置系统属性。lineSeparator()
: 返回当前系统的行分隔符。
import java.util.Properties;
public class SystemExample {
public static void main(String[] args) {
// 使用System.out.println输出到标准输出
System.out.println("Hello, World!");
// 使用System.err.println输出到标准错误
System.err.println("This is an error message.");
// 复制数组
char[] srcArray = {'a', 'b', 'c', 'd', 'e'};
char[] destArray = new char[5];
System.arraycopy(srcArray, 0, destArray, 0, 3);
for (char c : destArray) {
System.out.print(c); // 输出: abcde
}
System.out.println();
// 获取当前时间
long currentTime = System.currentTimeMillis();
System.out.println("Current time in milliseconds: " + currentTime);
// 获取系统属性
Properties properties = System.getProperties();
System.out.println("Java Home: " + properties.getProperty("java.home"));
System.out.println("OS Name: " + properties.getProperty("os.name"));
System.out.println("User Home: " + properties.getProperty("user.home"));
// 设置系统属性
System.setProperty("example.property", "Example Value");
System.out.println("Example Property: " + System.getProperty("example.property"));
// 获取行分隔符
String lineSeparator = System.lineSeparator();
System.out.println("Line Separator: " + lineSeparator);
}
}
System.in
是一个标准输入流,通常用于读取键盘输入。下面是一个简单的示例,展示了如何使用 System.in
读取用户输入的整数:
import java.util.Scanner;
public class SystemInExample {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个整数:");
int number = scanner.nextInt();
System.out.println("您输入的整数是:" + number);
scanner.close(); // 关闭扫描器
}
}
4.4 日期类
在Java中,日期和时间相关的类位于java.util
包中,主要有以下几个类:
Date
- 用于表示特定的瞬间,通常称为日期和时间。Calendar
- 用于日期和时间的操作,提供了一些静态方法。SimpleDateFormat
- 用于格式化和解析日期。TimeZone
- 用于表示时区。
Date类
getTime()
: 返回自1970年1月1日00:00:00 UTC以来的毫秒数。setTime(long time)
: 将日期和时间设置为指定的毫秒数。toString()
: 返回日期和时间的字符串表示。getYear()
: 获取年份。getMonth()
: 获取月份(0-11)。getDate()
: 获取日。getHours()
: 获取小时。getMinutes()
: 获取分钟。getSeconds()
: 获取秒。
import java.util.Date;
public class DateExample {
public static void main(String[] args) {
// 获取当前日期和时间
Date currentDate = new Date();
System.out.println("当前日期和时间: " + currentDate);
// 获取日期和时间的毫秒数
long time = currentDate.getTime();
System.out.println("日期和时间的毫秒数: " + time);
// 设置日期和时间
Date newDate = new Date(time);
System.out.println("设置后的日期和时间: " + newDate);
}
}
Calendar类
getInstance()
: 获取当前日历的实例。setTime(Date date)
: 将日期设置到当前日历。getTime()
: 返回当前日历的日期和时间。add(int field, int amount)
: 添加或减去指定日期字段的指定数量。get(int field)
: 获取指定日期字段的值。set(int year, int month, int date)
: 设置年份、月份和日期。
import java.util.Calendar;
public class CalendarExample {
public static void main(String[] args) {
// 获取当前日期和时间
Calendar currentCalendar = Calendar.getInstance();
System.out.println("当前日期和时间: " + currentCalendar);
// 设置日期和时间
Calendar newCalendar = Calendar.getInstance();
newCalendar.set(Calendar.YEAR, 2023);
newCalendar.set(Calendar.MONTH, Calendar.NOVEMBER);
newCalendar.set(Calendar.DAY_OF_MONTH, 1);
System.out.println("设置后的日期和时间: " + newCalendar);
}
}
SimpleDateFormat类
format(Date date)
: 将日期格式化为字符串。parse(String source)
: 将字符串解析为日期。setPattern(String pattern)
: 设置日期和时间的格式模式。、
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class SimpleDateFormatExample {
public static void main(String[] args) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String formattedDate = simpleDateFormat.format(new Date());
System.out.println("格式化后的日期: " + formattedDate);
String inputDate = "2023-11-01 12:30:45";
try {
Date parsedDate = simpleDateFormat.parse(inputDate);
System.out.println("解析后的日期: " + parsedDate);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
TimeZone类
getTimeZone(String ID)
: 根据时区ID获取时区对象。getDisplayName(boolean dst, int style)
:获取时区的简短名称和完整名称。
import java.util.TimeZone;
public class TimeZoneDisplayNameExample {
public static void main(String[] args) {
TimeZone timeZone = TimeZone.getTimeZone("GMT+8");
System.out.println("时区对象: " + timeZone);
// 获取简短的时区名称
String shortName = timeZone.getDisplayName(false, TimeZone.SHORT);
System.out.println("简短的时区名称: " + shortName);
// 获取完整的时区名称
String longName = timeZone.getDisplayName(false, TimeZone.LONG);
System.out.println("完整的时区名称: " + longName);
}
}
在Java 8及更高版本中,引入了新的日期和时间API,位于java.time
包中。这个新的API提供了更好的日期和时间处理功能,并且是线程安全的。主要有以下几个类:
LocalDate
类表示日期,不包含时间部分。它提供了日期相关的操作和格式化方法。LocalDateTime
类用于表示日期和时间,它结合了LocalDate
和LocalTime
两个类。
LocalDate类
LocalDate
类用于表示日期,不包含时间部分。它提供了日期相关的操作和格式化方法。
重要方法:
now()
: 获取当前日期。of(int year, int month, int dayOfMonth)
: 创建一个日期对象。plusYears(long yearsToAdd)
: 增加指定的年数。minusYears(long yearsToSubtract)
: 减少指定的年数。format(DateTimeFormatter formatter)
: 使用指定的格式器格式化日期。
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class LocalDateExample {
public static void main(String[] args) {
// 获取当前日期
LocalDate currentDate = LocalDate.now();
System.out.println("当前日期: " + currentDate);
// 创建特定日期
LocalDate specificDate = LocalDate.of(2023, 11, 1);
System.out.println("特定日期: " + specificDate);
// 增加天数
LocalDate newDate = currentDate.plusDays(10);
System.out.println("增加10天后: " + newDate);
// 减少天数
LocalDate oldDate = currentDate.minusDays(10);
System.out.println("减少10天后: " + oldDate);
// 格式化日期
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String formattedDate = currentDate.format(formatter);
System.out.println("格式化后的日期: " + formattedDate);
}
}
LocalDateTime类
LocalDateTime
类用于表示日期和时间,它结合了LocalDate
和LocalTime
两个类。
重要方法:
now()
: 获取当前日期和时间。of(int year, int month, int dayOfMonth, int hour, int minute, int second)
: 创建一个日期时间对象。plusHours(long hoursToAdd)
: 增加指定的小时数。minusHours(long hoursToSubtract)
: 减少指定的小时数。format(DateTimeFormatter formatter)
: 使用指定的格式器格式化日期和时间。
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class LocalDateTimeExample {
public static void main(String[] args) {
// 获取当前日期和时间
LocalDateTime currentDateTime = LocalDateTime.now();
System.out.println("当前日期和时间: " + currentDateTime);
// 创建特定日期和时间
LocalDateTime specificDateTime = LocalDateTime.of(2023, 11, 1, 12, 30, 45);
System.out.println("特定日期和时间: " + specificDateTime);
// 增加小时数
LocalDateTime newDateTime = currentDateTime.plusHours(10);
System.out.println("增加10小时后: " + newDateTime);
// 减少小时数
LocalDateTime oldDateTime = currentDateTime.minusHours(10);
System.out.println("减少10小时后: " + oldDateTime);
// 格式化日期和时间
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedDateTime = currentDateTime.format(formatter);
System.out.println("格式化后的日期和时间: " + formattedDateTime);
}
}
4.5 作业习题:使用LocalDateTime实现计算1000次LocalDate的plusDays(1) 方法所消耗的时间
五、集合类
5.1 集合接口
-
Collection:这是集合框架中的根接口,它表示一组对象,称为元素。
Collection
接口继承了Iterable
接口,因此可以被用于增强的for循环中。- List:有序集合(也称为序列),允许重复元素。实现类包括
ArrayList
、LinkedList
和Vector
。 - Set:不允许重复元素的集合。实现类包括
HashSet
、LinkedHashSet
和TreeSet
。
- List:有序集合(也称为序列),允许重复元素。实现类包括
-
Map:将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。实现类包括
HashMap
、LinkedHashMap
、TreeMap
和Hashtable
。
其他接口和类:
- Queue:用于在处理之前保存多个元素的对象。实现类包括
PriorityQueue
和LinkedList
(它实现了Queue
接口)。 - Deque:扩展了
Queue
,代表双端队列,允许元素从两端被添加或移除。实现类包括ArrayDeque
和LinkedList
。
5.2 实现类
List接口:
- ArrayList:实现了可调整大小的数组。允许包含重复元素和null值。不是线程安全的。
- LinkedList:实现了链表。允许包含重复元素和null值。不是线程安全的。
Set接口:
- HashSet:实现了基于哈希表的集合。不允许包含重复元素,最多只允许一个null值。不是线程安全的。
- LinkedHashSet:具有可预测的迭代顺序的
HashSet
,即插入顺序。不是线程安全的。 - TreeSet:实现了排序的集合。不允许包含重复元素,也不允许null值。不是线程安全的。
Map接口:
- HashMap:实现了基于哈希表的映射。允许包含一个null键和多个null值。不是线程安全的。
- LinkedHashMap:具有可预测的迭代顺序的
HashMap
,即插入顺序。不是线程安全的。 - TreeMap:实现了排序的映射。不允许包含重复键,也不允许null键,但允许null值。不是线程安全的。
- Hashtable:是Java早期版本的遗留类,实现了基于哈希表的映射。不允许包含重复键,也不允许null键和null值。是线程安全的。
5.3 集合类常用方法
Java 集合框架提供了一套丰富的接口和类,用于存储和管理对象集合。以下是集合类中的一些常用方法,按照集合接口进行分类总结:
Collection 接口
add(E e)
:添加元素到集合中。addAll(Collection<? extends E> c)
:添加指定集合中的所有元素到当前集合中。clear()
:移除集合中的所有元素。contains(Object o)
:如果集合包含指定的元素,则返回 true。containsAll(Collection<?> c)
:如果集合包含指定集合中的所有元素,则返回 true。isEmpty()
:如果集合不包含任何元素,则返回 true。iterator()
:返回集合中元素的迭代器。remove(Object o)
:从集合中移除指定的元素。removeAll(Collection<?> c)
:移除集合中所有也包含在指定集合中的元素。retainAll(Collection<?> c)
:仅保留集合中包含在指定集合中的元素。size()
:返回集合中元素的数量。toArray()
:返回包含集合中所有元素的数组。
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");
collection.add("Cherry");
// 预期结果: [Apple, Banana, Cherry]
System.out.println("Collection: " + collection);
// 检查集合是否包含某个元素
// 预期结果: true
System.out.println("Contains 'Apple': " + collection.contains("Apple"));
// 移除元素
collection.remove("Banana");
// 预期结果: [Apple, Cherry]
System.out.println("Collection after removal: " + collection);
// 获取集合大小
// 预期结果: 2
System.out.println("Size of collection: " + collection.size());
// 将集合转换为数组
Object[] array = collection.toArray();
// 预期结果: [Apple, Cherry]
System.out.println("Array: " + java.util.Arrays.toString(array));
}
}
List 接口(继承自 Collection)
add(int index, E element)
:在指定位置插入元素。addAll(int index, Collection<? extends E> c)
:在指定位置插入指定集合中的所有元素。get(int index)
:返回指定位置的元素。indexOf(Object o)
:返回指定元素在集合中首次出现的位置。lastIndexOf(Object o)
:返回指定元素在集合中最后一次出现的位置。listIterator()
:返回列表的列表迭代器。remove(int index)
:移除指定位置的元素。set(int index, E element)
:用指定元素替换指定位置的元素。
import java.util.ArrayList;
import java.util.List;
public class ListExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
// 添加元素
list.add("Apple");
list.add("Banana");
list.add("Cherry");
// 在指定位置插入元素
list.add(1, "Grape");
// 预期结果: [Apple, Grape, Banana, Cherry]
System.out.println("List after insertion: " + list);
// 获取指定位置的元素
// 预期结果: Grape
System.out.println("Element at index 1: " + list.get(1));
// 替换指定位置的元素
list.set(2, "Mango");
// 预期结果: [Apple, Grape, Mango, Cherry]
System.out.println("List after replacement: " + list);
// 移除指定位置的元素
list.remove(0);
// 预期结果: [Grape, Mango, Cherry]
System.out.println("List after removal: " + list);
}
}
Set 接口(继承自 Collection)
add(E e)
:添加元素到集合中,如果集合已经包含该元素,则不会添加。addAll(Collection<? extends E> c)
:添加指定集合中的所有元素,忽略已经存在的元素。containsAll(Collection<?> c)
:如果集合包含指定集合中的所有元素,则返回 true。
import java.util.HashSet;
import java.util.Set;
public class SetExample {
public static void main(String[] args) {
// 创建一个 Set 对象
Set<String> set = new HashSet<>();
// 添加元素
set.add("Apple");
set.add("Banana");
set.add("Cherry");
// 检查集合是否包含某个元素
System.out.println("Contains 'Apple': " + set.contains("Apple"));
// 预期输出: Contains 'Apple': true
// 获取集合大小
System.out.println("Size of set: " + set.size());
// 预期输出: Size of set: 3
// 尝试添加重复元素
set.add("Apple"); // 不会有任何效果,因为 Set 不允许重复
// 输出整个集合
System.out.println("Set: " + set);
// 预期输出: Set: [Apple, Banana, Cherry]
// 移除元素
set.remove("Banana");
System.out.println("Set after removal: " + set);
// 预期输出: Set after removal: [Apple, Cherry]
}
}
Map 接口
clear()
:移除所有映射关系。containsKey(Object key)
:如果映射包含指定的键,则返回 true。containsValue(Object value)
:如果映射将一个或多个键映射到指定的值,则返回 true。entrySet()
:返回映射中包含的映射关系的 Set 视图。get(Object key)
:返回指定键所映射的值。keySet()
:返回映射中包含的键的 Set 视图。put(K key, V value)
:将指定的值与此映射中的指定键关联。putAll(Map<? extends K, ? extends V> m)
:从指定映射中将所有映射关系复制到此映射中。remove(Object key)
:如果存在,则从映射中移除键的映射关系。size()
:返回映射中的键-值映射关系数。values()
:返回映射中包含的值的 Collection 视图。
import java.util.HashMap;
import java.util.Map;
public class MapExample {
public static void main(String[] args) {
// 创建一个 Map 对象
Map<String, Integer> map = new HashMap<>();
// 添加键值对
map.put("Apple", 1);
map.put("Banana", 2);
map.put("Cherry", 3);
// 获取键值对
System.out.println("获取键值对:");
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
}
// 预期输出: Key: Apple, Value: 1
// Key: Banana, Value: 2
// Key: Cherry, Value: 3
// 获取键对应的值
System.out.println("\n获取键对应的值:");
Integer value = map.get("Apple");
System.out.println("Value for 'Apple': " + value);
// 预期输出: Value for 'Apple': 1
// 检查键是否存在
System.out.println("\n检查键是否存在:");
boolean containsKey = map.containsKey("Apple");
System.out.println("Contains key 'Apple': " + containsKey);
// 预期输出: Contains key 'Apple': true
// 检查值是否存在
System.out.println("\n检查值是否存在:");
boolean containsValue = map.containsValue(2);
System.out.println("Contains value 2: " + containsValue);
// 预期输出: Contains value 2: true
// 移除键值对
map.remove("Cherry");
System.out.println("\n移除键值对:");
System.out.println("After removing 'Cherry': " + map);
// 预期输出: After removing 'Cherry': {Apple=1, Banana=2}
}
}
entrySet()
和 keySet()
方法用于返回映射中包含的键值对(Map.Entry
)的集合视图。这些方法提供了不同的方式来访问 Map
中的元素。
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Iterator;
public class EntrySetAndKeySetExample {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("Apple", 1);
map.put("Banana", 2);
map.put("Cherry", 3);
// 使用 entrySet() 遍历所有键值对
System.out.println("使用 entrySet() 遍历所有键值对:");
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
for (Map.Entry<String, Integer> entry : entrySet) {
System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
}
// 预期输出: Key: Apple, Value: 1
// Key: Banana, Value: 2
// Key: Cherry, Value: 3
// 使用 keySet() 遍历所有键
System.out.println("\n使用 keySet() 遍历所有键:");
Set<String> keySet = map.keySet();
for (String key : keySet) {
System.out.println("Key: " + key + ", Value: " + map.get(key));
}
// 预期输出: Key: Apple, Value: 1
// Key: Banana, Value: 2
// Key: Cherry, Value: 3
}
}
Iterator 接口
hasNext()
:如果迭代器指向的集合中还有元素,则返回 true。next()
:返回迭代器指向的集合中的下一个元素。remove()
:从迭代器指向的集合中移除迭代器返回的最后一个元素。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IteratorExample {
public static void main(String[] args) {
// 创建一个 List 对象
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");
// 获取 Iterator 对象
Iterator<String> iterator = list.iterator();
// 使用 Iterator 遍历 List
System.out.println("使用 Iterator 遍历 List:");
while (iterator.hasNext()) {
String element = iterator.next();
System.out.println(element);
}
// 预期输出: Apple, Banana, Cherry
// 尝试使用 remove 方法移除元素
System.out.println("\n尝试使用 remove 方法移除元素:");
iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
if ("Banana".equals(element)) {
iterator.remove();
}
}
System.out.println("After removing 'Banana': " + list);
// 预期输出: [Apple, Cherry]
}
}
ListIterator 接口(继承自 Iterator)
hasPrevious()
:如果迭代器指向的列表中有前一个元素,则返回 true。previous()
:返回迭代器指向的列表中的前一个元素。add(E e)
:在迭代器当前位置前添加一个元素。set(E e)
:用指定元素替换迭代器返回的最后一个元素。
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class ListIteratorExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");
ListIterator<String> listIterator = list.listIterator();
// 正向遍历列表
System.out.println("正向遍历列表:");
while (listIterator.hasNext()) {
String fruit = listIterator.next();
System.out.println(fruit);
}
// 预期输出: Apple, Banana, Cherry
// 反向遍历列表
System.out.println("\n反向遍历列表:");
while (listIterator.hasPrevious()) {
String fruit = listIterator.previous();
System.out.println(fruit);
}
// 预期输出: Cherry, Banana, Apple
// 修改列表中的元素
listIterator.set("Grape");
System.out.println("\n修改列表中的元素:");
System.out.println("After modification: " + list);
// 预期输出: [Apple, Banana, Grape]
// 在当前位置添加元素
listIterator.add("Mango");
System.out.println("\n在当前位置添加元素:");
System.out.println("After addition: " + list);
// 预期输出: [Apple, Banana, Grape, Mango]
}
}
5.4 作业习题:给定两个集合 A
和 B
,请编写一个方法,输出这两个集合的并集和交集
六、 异常处理
6.1 异常类型:
- 检查型异常(Checked Exceptions):这些异常必须被显式地捕获或者通过方法签名声明抛出。例如
IOException
和SQLException
。 - 非检查型异常(Unchecked Exceptions):这些异常不需要显式地捕获,包括运行时异常(如
NullPointerException
、ArrayIndexOutOfBoundsException
)和错误(如OutOfMemoryError
)。
6.2 异常处理关键字
-
try 块:将可能抛出异常的代码放在
try
块中。如果try
块中的代码抛出了异常,则尝试找到匹配的catch
块来处理它。 -
catch 块:用于捕获并处理
try
块中抛出的异常。可以有多个catch
块来捕获不同类型的异常。 -
finally 块:无论是否捕获或处理异常,
finally
块中的代码总是会执行。通常用于释放资源。 -
throw 关键字:用于显式地抛出一个异常。
-
throws 关键字:用于在方法签名中声明该方法可能抛出的异常。
6.3 异常处理的基本模式
try {
// 可能抛出异常的代码
} catch (特定异常类型1 e) {
// 当抛出特定异常类型1时执行的代码
} catch (特定异常类型2 e) {
// 当抛出特定异常类型2时执行的代码
} finally {
// 无论是否捕获异常都会执行的代码
}
public class ExceptionDemo {
public static void main(String[] args) {
try {
int result = divide(10, 0);
System.out.println("结果为: " + result);
} catch (ArithmeticException e) {
System.out.println("捕获到算术异常: " + e.getMessage());
} finally {
System.out.println("这是finally块,总是会执行。");
}
}
public static int divide(int a, int b) throws ArithmeticException {
return a / b; // 可能会抛出除以0的异常
}
}
6.4 作业习题:实现自定义异常处理
七、文件
7.1 File类
java.io.File
类是一个用于文件和目录操作的类。它提供了一组静态方法和实例方法,用于创建、删除、移动、重命名文件和目录,以及获取文件和目录的信息。
实例方法
boolean exists()
:判断文件或目录是否存在。boolean isDirectory()
:判断对象是否是一个目录。boolean isFile()
:判断对象是否是一个文件。boolean isHidden()
:判断文件是否是隐藏文件。long lastModified()
:返回文件或目录最后一次被修改的时间戳。String[] list()
:返回指定目录中的所有子文件和子目录的名称数组。File[] listFiles()
:返回指定目录中的所有子文件和子目录的File
对象数组。boolean mkdir()
:创建指定目录。boolean mkdirs()
:创建指定目录及其所有父目录。boolean renameTo(File dest)
:将此文件重命名为指定的文件。boolean setExecutable(boolean executable)
:设置文件是否可执行。boolean setReadable(boolean readable)
:设置文件是否可读。boolean setReadOnly()
:设置文件是否只读。boolean setWritable(boolean writable)
:设置文件是否可写。void setLastModified(long time)
:设置文件的最后修改时间戳。void setReadOnly()
:将文件设置为只读。
静态方法
File[] listRoots()
:返回所有文件系统的根目录。String pathSeparator
:文件路径分隔符。String separator
:文件系统分隔符。void setExecutable(File file, boolean executable)
:设置文件是否可执行。void setReadable(File file, boolean readable)
:设置文件是否可读。void setReadOnly(File file)
:设置文件是否只读。void setWritable(File file, boolean writable)
:设置文件是否可写。
简单的 Java 代码示例,展示了如何使用 java.io.File
类来创建、删除和重命名文件:
import java.io.File;
public class FileExample {
public static void main(String[] args) {
// 创建一个新的 File 对象
File file = new File("example.txt");
// 创建文件
if (!file.exists()) {
try {
if (file.createNewFile()) {
System.out.println("文件创建成功");
} else {
System.out.println("文件创建失败");
}
} catch (IOException e) {
e.printStackTrace();
}
} else {
System.out.println("文件已存在");
}
// 删除文件
if (file.delete()) {
System.out.println("文件删除成功");
} else {
System.out.println("文件删除失败");
}
// 重命名文件
File newFile = new File("newExample.txt");
if (file.renameTo(newFile)) {
System.out.println("文件重命名成功");
} else {
System.out.println("文件重命名失败");
}
}
}
7.2 文件的输入/输出流(非缓存、缓存)
文件的输入/输出流(I/O)操作通常使用 java.io
包中的类来实现。这个包提供了多种流类,用于读取和写入文件。而带缓存的输入/输出流(I/O)可以提高读写文件的效率。带缓存的流会先将数据读取到内存中的缓冲区,或者先将数据写入内存中的缓冲区,然后再将缓冲区的数据写入文件或从文件读取。
输入流(读取文件)
-
字节流:
FileInputStream
:用于读取以字节形式存储的文件。BufferedInputStream
:增强FileInputStream
的性能,提供缓冲功能。
-
字符流:
FileReader
:用于读取以字符形式存储的文件。BufferedReader
:增强FileReader
的性能,提供缓冲功能。
输出流(写入文件)
-
字节流:
FileOutputStream
:用于写入以字节形式存储的文件。BufferedOutputStream
:增强FileOutputStream
的性能,提供缓冲功能。
-
字符流:
FileWriter
:用于写入以字符形式存储的文件。BufferedWriter
:增强FileWriter
的性能,提供缓冲功能。
因为缓存的输入/输出处理大量数据提高性能,所以以缓存的输入/输出作为示例代码:
字节流读取文件
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class ByteStreamReadExample {
public static void main(String[] args) {
String filePath = "example.txt";
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath))) {
int data;
while ((data = bis.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
字节流写入文件
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteStreamWriteExample {
public static void main(String[] args) {
String filePath = "example.txt";
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath))) {
bos.write("Hello, World!".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
字符流读取文件
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class CharStreamReadExample {
public static void main(String[] args) {
String filePath = "example.txt";
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
字符流写入文件
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class CharStreamWriteExample {
public static void main(String[] args) {
String filePath = "example.txt";
try (BufferedWriter bw = new BufferedWriter(new FileWriter(filePath))) {
bw.write("Hello, World!");
bw.newLine(); // 写入新行
} catch (IOException e) {
e.printStackTrace();
}
}
}
7.3 缓存输入/输出流使用说明
不是一定要使用带缓存的输入/输出流。使用带缓存的流可以提高文件读写的效率,但并不是在所有情况下都是必需的。以下是一些使用或不使用带缓存流的考虑因素:
使用带缓存流的情况:
-
大量数据的读写:如果需要处理大量数据,使用带缓存流可以减少对文件系统的直接访问次数,从而提高读写速度。
-
网络文件读写:在处理网络文件时,由于网络延迟,使用带缓存流可以减少等待时间。
-
性能优化:在需要提高程序性能的情况下,使用带缓存流可以减少 I/O 操作的次数,从而优化程序性能。
不使用带缓存流的情况:
-
小文件的读写:对于小文件,由于文件本身不大,带缓存的优势可能不明显,甚至可能会增加内存使用。
-
内存限制:如果系统内存有限,使用带缓存流可能会消耗更多的内存资源,这可能会导致性能下降。
-
特定应用场景:在某些特定的应用场景下,如实时数据处理或需要实时响应的应用,使用带缓存流可能会引入额外的延迟。