Java面试八股文-基础篇
1. Java语言特性
1.1 Java的跨平台特性是如何实现的?
Java的跨平台特性主要通过JVM(Java虚拟机)实现,具体过程如下:
- 编译阶段:Java源代码(.java文件)通过javac编译器编译成字节码(.class文件),字节码是一种与平台无关的中间代码。
- 运行阶段:不同平台(如Windows、Linux、macOS)都有对应的JVM实现,JVM负责将字节码解释或编译为本地机器码执行。
- JVM的作用:JVM作为中间层,屏蔽了不同平台的硬件和操作系统差异,使得相同的字节码能够在不同平台上运行。
这种”一次编写,到处运行”的特性是Java的核心优势之一,也是Java能够在企业级应用中广泛应用的重要原因。
示例:
1 2 3 4 5 6
| public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } }
|
编译后生成的.class文件可以在任何安装了JVM的平台上运行,无需重新编译。
1.2 Java中的八大基本数据类型
Java中的八大基本数据类型可以分为四大类:整数类型、浮点类型、字符类型和布尔类型。具体如下:
| 数据类型 |
字节数 |
范围 |
默认值 |
示例 |
| byte |
1 |
-128 ~ 127 |
0 |
byte b = 100; |
| short |
2 |
-32768 ~ 32767 |
0 |
short s = 1000; |
| int |
4 |
-2^31 ~ 2^31-1 |
0 |
int i = 100000; |
| long |
8 |
-2^63 ~ 2^63-1 |
0L |
long l = 100000L; |
| float |
4 |
约±3.40282347E+38F |
0.0f |
float f = 3.14f; |
| double |
8 |
约±1.7976931348623157E+308 |
0.0d |
double d = 3.14; |
| char |
2 |
0 ~ 65535 |
‘\u0000’ |
char c = 'A'; |
| boolean |
1位 |
true/false |
false |
boolean b = true; |
注意事项:
- 整数类型默认是int,浮点数默认是double
- 声明long类型时,需要在数值后面加L或l
- 声明float类型时,需要在数值后面加F或f
- char类型可以存储一个Unicode字符,包括中文字符
示例:
1 2 3 4 5 6 7 8
| byte age = 25; int salary = 10000; long population = 1000000000L; float pi = 3.14f; double e = 2.71828; char grade = 'A'; boolean isStudent = true;
|
1.3 什么是自动装箱和拆箱?
自动装箱(Autoboxing)是指Java编译器自动将基本数据类型转换为对应的包装类对象的过程。例如,将int转换为Integer,将double转换为Double等。
自动拆箱(Unboxing)是指Java编译器自动将包装类对象转换为对应的基本数据类型的过程。例如,将Integer转换为int,将Double转换为double等。
原理:
- 自动装箱时,编译器会调用包装类的valueOf()方法
- 自动拆箱时,编译器会调用包装类的xxxValue()方法(如intValue()、doubleValue()等)
示例:
1 2 3 4 5 6 7 8 9 10 11 12
| Integer i = 100; Double d = 3.14;
int j = i; double e = d;
List<Integer> list = new ArrayList<>(); list.add(100); int k = list.get(0);
|
注意事项:
- 自动装箱和拆箱会带来一定的性能开销,在大量操作时应注意优化
- 包装类对象可能为null,自动拆箱时可能会导致NullPointerException
- 对于Integer,valueOf()方法会缓存-128到127之间的整数对象,这可能会导致一些意想不到的结果
示例:
1 2 3 4 5 6 7 8 9
| Integer a = 127; Integer b = 127; System.out.println(a == b);
Integer c = 128; Integer d = 128; System.out.println(c == d); System.out.println(c.equals(d));
|
1.4 Java中的访问修饰符有哪些?
Java中的访问修饰符用于控制类、方法、属性的访问权限,共有四种:
| 修饰符 |
本类 |
同包 |
子类 |
其他包 |
| private |
✓ |
✗ |
✗ |
✗ |
| default(默认) |
✓ |
✓ |
✗ |
✗ |
| protected |
✓ |
✓ |
✓ |
✗ |
| public |
✓ |
✓ |
✓ |
✓ |
详细说明:
- private:最严格的访问权限,只能在定义它的类内部访问。适用于类内部的辅助方法或属性。
- default(默认):没有显式声明访问修饰符时的默认权限,只能在同一个包内访问。
- protected:可以在同一个包内访问,也可以被不同包中的子类访问。适用于需要被子类继承的方法或属性。
- public:最宽松的访问权限,可以在任何地方访问。适用于对外提供的接口或方法。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| package com.example.parent;
public class Parent { private int privateField = 1; int defaultField = 2; protected int protectedField = 3; public int publicField = 4; private void privateMethod() { System.out.println("private method"); } void defaultMethod() { System.out.println("default method"); } protected void protectedMethod() { System.out.println("protected method"); } public void publicMethod() { System.out.println("public method"); } }
package com.example.parent;
public class SamePackageChild extends Parent { public void accessFields() { System.out.println(defaultField); System.out.println(protectedField); System.out.println(publicField); } }
package com.example.child;
import com.example.parent.Parent;
public class DifferentPackageChild extends Parent { public void accessFields() { System.out.println(protectedField); System.out.println(publicField); } }
package com.example.other;
import com.example.parent.Parent;
public class DifferentPackageNonChild { public void accessFields() { Parent parent = new Parent(); System.out.println(parent.publicField); } }
|
注意事项:
- 类只能使用 public 或默认访问修饰符
- 接口中的方法和常量默认是 public 的
- 局部变量不能使用访问修饰符
- 访问修饰符的选择应遵循”最小权限原则”,即尽可能使用限制更严格的访问修饰符,以提高代码的安全性和可维护性
2. 面向对象编程
2.1 什么是面向对象编程?
面向对象编程(Object-Oriented Programming,OOP)是一种编程范式,它以对象为中心,将数据和操作数据的方法封装在一起,通过对象之间的交互来实现功能。
OOP的核心思想:
- 一切皆对象:将现实世界中的事物抽象为程序中的对象
- 封装:将数据和方法封装在对象中,对外提供接口
- 继承:通过继承实现代码复用
- 多态:通过多态实现接口复用
OOP的优点:
- 可维护性:代码结构清晰,易于理解和维护
- 可复用性:通过继承和多态实现代码复用
- 可扩展性:通过面向接口编程,易于扩展功能
- 安全性:通过封装保护数据,提高代码的安全性
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setAge(int age) { if (age > 0 && age < 150) { this.age = age; } } public int getAge() { return age; } public void sayHello() { System.out.println("Hello, my name is " + name + ", I'm " + age + " years old."); } }
public class Main { public static void main(String[] args) { Person person = new Person("Alice", 25); person.sayHello(); person.setAge(26); person.sayHello(); } }
|
2.2 什么是封装、继承和多态?
2.2.1 封装(Encapsulation)
封装是指将对象的状态(属性)和行为(方法)封装在一起,通过访问修饰符控制外部对对象内部状态的访问权限。
封装的优点:
- 安全性:保护对象的内部状态,防止外部代码随意修改
- 可维护性:隐藏实现细节,只对外提供公共接口
- 灵活性:可以在不影响外部代码的情况下修改内部实现
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| public class BankAccount { private String accountNumber; private double balance; public BankAccount(String accountNumber, double initialBalance) { this.accountNumber = accountNumber; this.balance = initialBalance; } public void deposit(double amount) { if (amount > 0) { balance += amount; System.out.println("存款成功,当前余额:" + balance); } else { System.out.println("存款金额必须大于0"); } } public void withdraw(double amount) { if (amount > 0 && amount <= balance) { balance -= amount; System.out.println("取款成功,当前余额:" + balance); } else { System.out.println("取款金额无效"); } } public double getBalance() { return balance; } }
public class Main { public static void main(String[] args) { BankAccount account = new BankAccount("123456", 1000.0); account.deposit(500.0); account.withdraw(200.0); System.out.println("当前余额:" + account.getBalance()); } }
|
2.2.2 继承(Inheritance)
继承是指子类继承父类的属性和方法,实现代码复用的机制。
继承的优点:
- 代码复用:子类可以继承父类的属性和方法,避免重复代码
- 扩展性:子类可以在父类的基础上添加新的属性和方法
- 多态的基础:继承是实现多态的必要条件
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| public class Animal { protected String name; public Animal(String name) { this.name = name; } public void eat() { System.out.println(name + " is eating"); } public void sleep() { System.out.println(name + " is sleeping"); } }
public class Dog extends Animal { public Dog(String name) { super(name); } @Override public void eat() { System.out.println(name + " is eating bones"); } public void bark() { System.out.println(name + " is barking"); } }
public class Cat extends Animal { public Cat(String name) { super(name); } @Override public void eat() { System.out.println(name + " is eating fish"); } public void meow() { System.out.println(name + " is meowing"); } }
public class Main { public static void main(String[] args) { Dog dog = new Dog("Buddy"); dog.eat(); dog.sleep(); dog.bark(); Cat cat = new Cat("Kitty"); cat.eat(); cat.sleep(); cat.meow(); } }
|
2.2.3 多态(Polymorphism)
多态是指同一方法在不同对象上有不同的行为表现。Java中的多态分为两种:
- 编译时多态(静态多态):通过方法重载实现
- 运行时多态(动态多态):通过方法重写实现
多态的条件:
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| public class Animal { public void makeSound() { System.out.println("Animal makes sound"); } }
public class Dog extends Animal { @Override public void makeSound() { System.out.println("Dog barks"); } }
public class Cat extends Animal { @Override public void makeSound() { System.out.println("Cat meows"); } }
public class Main { public static void main(String[] args) { Animal animal1 = new Dog(); Animal animal2 = new Cat(); animal1.makeSound(); animal2.makeSound(); printInfo(10); printInfo("Hello"); } public static void printInfo(int num) { System.out.println("Integer: " + num); } public static void printInfo(String str) { System.out.println("String: " + str); } }
|
2.3 什么是抽象类和接口?
2.3.1 抽象类(Abstract Class)
抽象类是使用abstract关键字修饰的类,它具有以下特点:
- 不能实例化:抽象类不能直接创建对象,只能被继承
- 可以包含抽象方法:使用
abstract关键字修饰的方法,没有方法体,需要子类实现
- 可以包含非抽象方法:抽象类可以包含已经实现的方法
- 可以包含构造方法:抽象类的构造方法用于子类初始化
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public abstract class Animal { public abstract void makeSound(); public void eat() { System.out.println("Animal is eating"); } public Animal() { System.out.println("Animal constructor"); } }
public class Dog extends Animal { @Override public void makeSound() { System.out.println("Dog barks"); } @Override public void eat() { System.out.println("Dog is eating bones"); } }
public class Main { public static void main(String[] args) { Animal dog = new Dog(); dog.makeSound(); dog.eat(); } }
|
2.3.2 接口(Interface)
接口是使用interface关键字定义的抽象类型,它具有以下特点:
- 不能实例化:接口不能直接创建对象,只能被实现
- Java 8之前:只能包含抽象方法和常量
- Java 8及以后:可以包含默认方法(default method)和静态方法(static method)
- 方法默认是public abstract:接口中的方法默认具有public abstract修饰符
- 常量默认是public static final:接口中的常量默认具有public static final修饰符
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| public interface Animal { String CATEGORY = "MAMMAL"; void makeSound(); default void eat() { System.out.println("Animal is eating"); } static void sleep() { System.out.println("Animal is sleeping"); } }
public class Dog implements Animal { @Override public void makeSound() { System.out.println("Dog barks"); } @Override public void eat() { System.out.println("Dog is eating bones"); } }
public class Main { public static void main(String[] args) { Animal dog = new Dog(); dog.makeSound(); dog.eat(); Animal.sleep(); System.out.println(Animal.CATEGORY); } }
|
2.4 抽象类和接口的区别?
抽象类和接口的区别:
| 特性 |
抽象类 |
接口 |
| 关键字 |
abstract class |
interface |
| 构造方法 |
有 |
无 |
| 方法实现 |
可以包含非抽象方法 |
Java 8之前只能包含抽象方法,Java 8及以后可以包含默认方法和静态方法 |
| 方法修饰符 |
可以有各种访问修饰符 |
方法默认是public abstract |
| 变量 |
可以有各种类型的变量 |
只能包含public static final常量 |
| 继承关系 |
单继承,一个类只能继承一个抽象类 |
多实现,一个类可以实现多个接口 |
| 设计理念 |
强调”is-a”关系,用于代码复用 |
强调”has-a”关系,用于定义行为规范 |
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| public abstract class Vehicle { protected String brand; public Vehicle(String brand) { this.brand = brand; } public abstract void start(); public void stop() { System.out.println(brand + " is stopping"); } }
public interface Flyable { int MAX_HEIGHT = 10000; void fly(); default void land() { System.out.println("Landing..."); } }
public class Airplane extends Vehicle implements Flyable { public Airplane(String brand) { super(brand); } @Override public void start() { System.out.println(brand + " is starting"); } @Override public void fly() { System.out.println(brand + " is flying at " + MAX_HEIGHT + " meters"); } }
public class Main { public static void main(String[] args) { Airplane airplane = new Airplane("Boeing 747"); airplane.start(); airplane.fly(); airplane.land(); airplane.stop(); } }
|
使用场景:
- 抽象类:当多个类有共同的父类,并且需要共享代码时使用
- 接口:当多个类需要实现相同的行为,但它们之间没有继承关系时使用
3. 字符串操作
3.1 String、StringBuffer和StringBuilder的区别?
String、StringBuffer和StringBuilder是Java中用于处理字符串的三个类,它们的主要区别如下:
| 特性 |
String |
StringBuffer |
StringBuilder |
| 可变性 |
不可变 |
可变 |
可变 |
| 线程安全 |
安全(不可变) |
安全(同步方法) |
不安全 |
| 效率 |
低(每次修改都创建新对象) |
中(同步开销) |
高(无同步开销) |
| 适用场景 |
字符串不经常修改的场景 |
多线程环境下的字符串操作 |
单线程环境下的字符串操作 |
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| String str = "Hello"; str = str + " World";
System.out.println(str);
StringBuffer sb = new StringBuffer("Hello"); sb.append(" World");
System.out.println(sb.toString());
StringBuilder sb2 = new StringBuilder("Hello"); sb2.append(" World");
System.out.println(sb2.toString());
long startTime = System.currentTimeMillis();
String s = ""; for (int i = 0; i < 10000; i++) { s += i; } long stringTime = System.currentTimeMillis() - startTime; System.out.println("String time: " + stringTime + "ms");
startTime = System.currentTimeMillis(); StringBuffer sb3 = new StringBuffer(); for (int i = 0; i < 10000; i++) { sb3.append(i); } long stringBufferTime = System.currentTimeMillis() - startTime; System.out.println("StringBuffer time: " + stringBufferTime + "ms");
startTime = System.currentTimeMillis(); StringBuilder sb4 = new StringBuilder(); for (int i = 0; i < 10000; i++) { sb4.append(i); } long stringBuilderTime = System.currentTimeMillis() - startTime; System.out.println("StringBuilder time: " + stringBuilderTime + "ms");
|
注意事项:
- 如果字符串不经常修改,使用String
- 如果在多线程环境下操作字符串,使用StringBuffer
- 如果在单线程环境下操作字符串,使用StringBuilder(性能最好)
3.2 如何判断两个字符串是否相等?
在Java中,判断两个字符串是否相等有两种方法:
3.2.1 使用equals()方法
作用:判断两个字符串的内容是否相等
适用场景:比较字符串的实际内容
示例:
1 2 3 4 5 6 7
| String str1 = "Hello"; String str2 = new String("Hello"); String str3 = "Hello";
System.out.println(str1.equals(str2)); System.out.println(str1.equals(str3));
|
3.2.2 使用==运算符
作用:判断两个字符串的引用是否指向同一个对象
适用场景:比较字符串对象的内存地址
示例:
1 2 3 4 5 6 7
| String str1 = "Hello"; String str2 = new String("Hello"); String str3 = "Hello";
System.out.println(str1 == str2); System.out.println(str1 == str3);
|
3.2.3 字符串常量池
字符串常量池是Java堆内存中一个特殊的区域,用于存储字符串常量。当创建字符串时,JVM会先检查字符串常量池中是否已存在相同内容的字符串:
- 如果存在,直接返回常量池中的引用
- 如果不存在,创建新的字符串并放入常量池
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13
| String str1 = "Hello"; String str2 = "Hello"; System.out.println(str1 == str2);
String str3 = new String("Hello"); String str4 = new String("Hello"); System.out.println(str3 == str4);
String str5 = new String("Hello").intern(); System.out.println(str1 == str5);
|
注意事项:
- 通常情况下,我们需要比较的是字符串的内容,所以应该使用
equals()方法
- 只有在需要比较字符串对象是否相同时,才使用
==运算符
equals()方法是Object类的方法,String类重写了该方法以比较字符串内容
- 对于null字符串,使用
equals()方法会抛出NullPointerException,因此在比较前应该先检查null
3.3 String的intern()方法有什么作用?
String的intern()方法是Java中用于字符串常量池操作的重要方法,其主要作用是:
1. 作用:
- 将字符串添加到字符串常量池中
- 返回常量池中的引用
- 如果常量池中已存在该字符串,则直接返回常量池中的引用
2. 工作原理:
- 当调用
intern()方法时,JVM会检查字符串常量池中是否已存在该字符串
- 如果存在,直接返回常量池中的引用
- 如果不存在,将该字符串添加到常量池中,然后返回引用
3. 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| String str1 = new String("Hello"); String str2 = str1.intern(); String str3 = "Hello";
System.out.println(str1 == str2); System.out.println(str2 == str3);
String s1 = "Java"; String s2 = new String("Java"); String s3 = new String("Java").intern();
System.out.println(s1 == s2); System.out.println(s1 == s3);
long startTime = System.currentTimeMillis();
String[] arr = new String[100000]; for (int i = 0; i < 100000; i++) { arr[i] = new String("String" + i % 100); } long time1 = System.currentTimeMillis() - startTime;
startTime = System.currentTimeMillis(); String[] arr2 = new String[100000]; for (int i = 0; i < 100000; i++) { arr2[i] = new String("String" + i % 100).intern(); } long time2 = System.currentTimeMillis() - startTime;
System.out.println("不使用intern(): " + time1 + "ms"); System.out.println("使用intern(): " + time2 + "ms");
|
4. 优缺点:
优点:
- 减少内存使用:相同内容的字符串只存储一份
- 提高字符串比较效率:可以使用
==运算符进行比较
缺点:
- 可能会增加常量池的内存使用
- 频繁调用intern()可能会影响性能
5. 使用场景:
- 当需要大量存储相同内容的字符串时
- 当需要频繁比较字符串时
- 当需要确保字符串引用相同时
6. 注意事项:
- 在Java 6及之前,字符串常量池位于永久代,空间有限
- 在Java 7及之后,字符串常量池移至堆中,空间更大
- 过度使用intern()可能会导致常量池溢出
4. 异常处理
4.1 Java中的异常体系
Java的异常体系是基于类层次结构的,所有异常都继承自Throwable类。异常体系主要分为两大类:
1. 异常层次结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| Throwable ├── Error │ ├── VirtualMachineError │ │ ├── OutOfMemoryError │ │ └── StackOverflowError │ └── AWTError └── Exception ├── RuntimeException │ ├── NullPointerException │ ├── IllegalArgumentException │ ├── ArrayIndexOutOfBoundsException │ ├── ClassCastException │ └── ArithmeticException └── CheckedException ├── IOException │ ├── FileNotFoundException │ └── IOException ├── SQLException └── ClassNotFoundException
|
2. 异常分类:
(1) Error(错误):
- 表示严重的系统错误,程序无法恢复
- 由JVM抛出,如OutOfMemoryError、StackOverflowError
- 不需要捕获和处理
(2) Exception(异常):
- 表示程序运行时的异常情况,程序可以捕获和处理
- 分为受检异常和非受检异常
3. 受检异常与非受检异常:
| 特性 |
受检异常(Checked Exception) |
非受检异常(Unchecked Exception) |
| 继承关系 |
继承自Exception但不是RuntimeException |
继承自RuntimeException |
| 编译检查 |
编译器强制要求处理 |
编译器不强制要求处理 |
| 处理方式 |
必须使用try-catch捕获或throws声明 |
可以不处理,也可以处理 |
| 常见例子 |
IOException、SQLException、ClassNotFoundException |
NullPointerException、ArrayIndexOutOfBoundsException、ArithmeticException |
4. 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| import java.io.FileInputStream; import java.io.IOException;
public class CheckedExceptionExample { public static void main(String[] args) { try { FileInputStream fis = new FileInputStream("test.txt"); fis.close(); } catch (IOException e) { e.printStackTrace(); } try { readFile(); } catch (IOException e) { e.printStackTrace(); } } public static void readFile() throws IOException { FileInputStream fis = new FileInputStream("test.txt"); fis.close(); } }
public class UncheckedExceptionExample { public static void main(String[] args) { String str = null; System.out.println(str.length()); int[] arr = new int[5]; System.out.println(arr[10]); int a = 10; int b = 0; System.out.println(a / b); } }
|
5. 异常处理的原则:
- 只捕获可以处理的异常
- 对于无法处理的异常,应该向上抛出
- 不要捕获所有异常(如catch (Exception e))
- 异常处理应该尽可能具体
- 应该在finally块中释放资源
- 不要在finally块中使用return语句,会覆盖try或catch中的return值
4.2 try-catch-finally的执行顺序?
try-catch-finally是Java中用于异常处理的核心结构,其执行顺序如下:
1. 基本执行顺序:
- 首先执行
try块中的代码
- 如果
try块中发生异常,执行对应的catch块
- 无论是否发生异常,
finally块都会执行
- 如果
try或catch中有return语句,finally块会在return之前执行
2. 详细执行流程:
情况1:try块中无异常
- 执行try块中的代码
- 执行finally块中的代码
- 执行try块后的代码
情况2:try块中有异常,且被catch捕获
- 执行try块中异常之前的代码
- 执行对应的catch块中的代码
- 执行finally块中的代码
- 执行catch块后的代码
情况3:try块中有异常,且未被catch捕获
- 执行try块中异常之前的代码
- 执行finally块中的代码
- 异常向上抛出
3. 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
| public class TryCatchFinallyExample1 { public static void main(String[] args) { try { System.out.println("Executing try block"); int result = 10 / 2; System.out.println("Result: " + result); } catch (ArithmeticException e) { System.out.println("Executing catch block"); e.printStackTrace(); } finally { System.out.println("Executing finally block"); } System.out.println("Executing after try-catch-finally"); } }
public class TryCatchFinallyExample2 { public static void main(String[] args) { try { System.out.println("Executing try block"); int result = 10 / 0; System.out.println("Result: " + result); } catch (ArithmeticException e) { System.out.println("Executing catch block"); e.printStackTrace(); } finally { System.out.println("Executing finally block"); } System.out.println("Executing after try-catch-finally"); } }
public class TryCatchFinallyExample3 { public static void main(String[] args) { try { System.out.println("Executing try block"); int result = 10 / 0; System.out.println("Result: " + result); } catch (NullPointerException e) { System.out.println("Executing catch block"); e.printStackTrace(); } finally { System.out.println("Executing finally block"); } System.out.println("Executing after try-catch-finally"); } }
public class TryCatchFinallyExample4 { public static int test() { try { System.out.println("Executing try block"); return 1; } catch (Exception e) { System.out.println("Executing catch block"); return 2; } finally { System.out.println("Executing finally block"); } } public static void main(String[] args) { int result = test(); System.out.println("Result: " + result); } }
public class TryCatchFinallyExample5 { public static int test() { try { System.out.println("Executing try block"); return 1; } catch (Exception e) { System.out.println("Executing catch block"); return 2; } finally { System.out.println("Executing finally block"); return 3; } } public static void main(String[] args) { int result = test(); System.out.println("Result: " + result); } }
|
4. 注意事项:
finally块中的代码总是会执行,除非在try或catch块中调用了System.exit()
- 不要在
finally块中使用return语句,会覆盖try或catch中的return值
- 应该在
finally块中释放资源,如关闭文件、数据库连接等
try-with-resources语句(Java 7+)可以自动关闭实现了AutoCloseable接口的资源,简化代码
4.3 throw和throws的区别?
throw和throws是Java中用于异常处理的两个关键字,它们的区别如下:
1. 基本区别:
| 特性 |
throw |
throws |
| 位置 |
方法内部 |
方法声明处 |
| 作用 |
抛出具体的异常对象 |
声明方法可能抛出的异常类型 |
| 数量 |
一次只能抛出一个异常对象 |
可以声明多个异常类型 |
| 语法 |
throw new Exception(); |
public void method() throws Exception1, Exception2 |
| 执行 |
会立即终止当前方法的执行 |
不会终止方法的执行,只是声明异常 |
2. 详细说明:
(1) throw:
- 用于在方法内部抛出具体的异常对象
- 抛出异常后,当前方法的执行会立即终止
- 抛出的异常需要被捕获或向上传递
- 可以抛出受检异常或非受检异常
(2) throws:
- 用于在方法声明处声明该方法可能抛出的异常类型
- 声明异常后,方法的执行不会立即终止
- 声明的异常需要由调用者处理或继续向上传递
- 只能声明受检异常,非受检异常不需要声明
3. 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| public class ThrowExample { public static void main(String[] args) { try { divide(10, 0); } catch (ArithmeticException e) { System.out.println("Caught exception: " + e.getMessage()); } } public static int divide(int a, int b) { if (b == 0) { throw new ArithmeticException("Division by zero"); } return a / b; } }
import java.io.IOException;
public class ThrowsExample { public static void main(String[] args) { try { readFile(); } catch (IOException e) { System.out.println("Caught exception: " + e.getMessage()); } } public static void readFile() throws IOException { throw new IOException("File not found"); } }
public class ThrowThrowsExample { public static void main(String[] args) { try { validateAge(15); } catch (IllegalArgumentException e) { System.out.println("Caught exception: " + e.getMessage()); } } public static void validateAge(int age) throws IllegalArgumentException { if (age < 18) { throw new IllegalArgumentException("Age must be at least 18"); } System.out.println("Age is valid"); } }
|
4. 注意事项:
throw抛出的异常如果是受检异常,必须在方法声明中使用throws声明,或者在方法内部使用try-catch捕获
throws声明的异常必须是Exception或其子类
- 对于非受检异常(RuntimeException及其子类),不需要在方法声明中使用
throws声明
- 使用
throw和throws时,应该选择合适的异常类型,避免使用过于宽泛的异常类型
5. 其他基础问题
5.1 Java中的main方法为什么是public static void?
Java中的main方法是程序的入口点,其签名为public static void main(String[] args),每个部分都有其特定的作用:
1. 各部分的作用:
(1) public:
- 访问修饰符,确保JVM可以从外部访问该方法
- 如果使用private或protected,JVM将无法访问该方法,导致程序无法启动
(2) static:
- 静态修饰符,确保JVM可以直接调用该方法,不需要创建对象
- 因为在程序启动时,还没有创建任何对象,所以必须使用static
(3) void:
- 返回值类型,表明main方法不返回任何值
- 因为main方法是程序的入口点,JVM不需要其返回值
(4) main:
(5) String[] args:
- 方法参数,用于接收命令行参数
- args是一个字符串数组,存储从命令行传入的参数
2. 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public class MainMethodExample { public static void main(String[] args) { System.out.println("Hello, World!"); System.out.println("Command line arguments:"); for (int i = 0; i < args.length; i++) { System.out.println("Argument " + (i + 1) + ": " + args[i]); } } }
|
3. 命令行参数示例:
假设编译后的类名为MainMethodExample,运行时可以传入命令行参数:
1
| java MainMethodExample arg1 arg2 arg3
|
输出:
1 2 3 4 5
| Hello, World! Command line arguments: Argument 1: arg1 Argument 2: arg2 Argument 3: arg3
|
4. 注意事项:
- main方法的签名必须严格按照
public static void main(String[] args)的格式
- args参数的名称可以修改,但类型必须是String[]
- 在Java 5及以后,args参数可以使用可变参数语法:
public static void main(String... args)
- main方法可以被其他方法调用,但通常不这样做,因为它是程序的入口点
- 如果一个类中没有main方法,JVM将无法直接运行该类
5.2 什么是Java的反射机制?
Java的反射机制是指在运行时动态获取类的信息(如类名、属性、方法等)并操作对象的能力。通过反射,程序可以在运行时加载、探测和使用编译时完全未知的类。
1. 反射的核心类:
Class:表示类的类型信息
Constructor:表示类的构造方法
Method:表示类的方法
Field:表示类的属性
Modifier:表示修饰符
2. 反射的主要功能:
- 在运行时获取类的信息:类名、父类、接口、属性、方法等
- 在运行时创建对象:通过构造方法创建实例
- 在运行时调用方法:调用对象的方法,包括私有方法
- 在运行时访问属性:访问和修改对象的属性,包括私有属性
- 在运行时动态加载类:加载编译时未知的类
3. 反射的使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method;
public class ReflectionExample { public static void main(String[] args) throws Exception { Class<?> clazz = Class.forName("java.lang.String"); System.out.println("Class name: " + clazz.getName()); System.out.println("Super class: " + clazz.getSuperclass().getName()); Constructor<?> constructor = clazz.getConstructor(String.class); Object obj = constructor.newInstance("Hello, Reflection!"); System.out.println("Created object: " + obj); Method method = clazz.getMethod("length"); int length = (int) method.invoke(obj); System.out.println("Length: " + length); Field field = clazz.getDeclaredField("value"); field.setAccessible(true); char[] value = (char[]) field.get(obj); System.out.println("Value: " + new String(value)); Class<?> dynamicClass = Class.forName("java.util.ArrayList"); Object list = dynamicClass.newInstance(); Method addMethod = dynamicClass.getMethod("add", Object.class); addMethod.invoke(list, "Item 1"); addMethod.invoke(list, "Item 2"); Method sizeMethod = dynamicClass.getMethod("size"); int size = (int) sizeMethod.invoke(list); System.out.println("List size: " + size); } }
|
4. 反射的优缺点:
优点:
- 灵活性高:可以在运行时动态操作类和对象
- 解耦:减少类之间的依赖关系
- 可扩展性:便于框架的设计和实现
- 可以操作私有成员:突破访问限制
缺点:
- 性能开销:反射操作比直接调用慢
- 代码可读性差:反射代码通常比较复杂
- 安全隐患:可能会破坏封装性
- 编译时类型检查缺失:容易出现运行时错误
5. 反射的应用场景:
- 框架设计:如Spring、Hibernate等框架广泛使用反射
- 动态代理:实现AOP等功能
- 序列化和反序列化:如JSON、XML等格式的转换
- 类加载器:动态加载类
- 测试工具:如JUnit等测试框架
- 依赖注入:如Spring的IoC容器
6. 注意事项:
- 反射操作会降低性能,应避免在性能敏感的代码中频繁使用
- 反射可能会破坏封装性,应谨慎使用
- 反射操作需要处理多种异常,代码复杂度较高
- 在使用反射访问私有成员时,需要设置
setAccessible(true)
7. 反射与注解的结合:
反射经常与注解结合使用,通过反射获取类、方法、属性上的注解信息,实现更灵活的功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| import java.lang.annotation.*; import java.lang.reflect.Method;
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @interface Test { String value(); }
class TestClass { @Test("测试方法1") public void testMethod1() { System.out.println("Test method 1"); } @Test("测试方法2") public void testMethod2() { System.out.println("Test method 2"); } }
public class AnnotationReflectionExample { public static void main(String[] args) throws Exception { Class<?> clazz = TestClass.class; Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { if (method.isAnnotationPresent(Test.class)) { Test testAnnotation = method.getAnnotation(Test.class); System.out.println("Method: " + method.getName() + ", Annotation value: " + testAnnotation.value()); method.invoke(clazz.newInstance()); } } } }
|
5.3 什么是Java的注解?
Java的注解(Annotation)是一种特殊的标记,用于为代码添加元数据(metadata)。注解本身不会直接影响代码的执行,但可以被编译器、工具或运行时环境使用。
1. 注解的基本概念:
- 元数据:描述数据的数据,注解就是一种元数据
- 标记:注解作为一种标记,可以添加到类、方法、属性、参数等元素上
- 不改变代码行为:注解本身不会改变代码的执行逻辑
- 可被处理:注解可以被编译器、工具或运行时环境处理
2. 注解的分类:
(1) 内置注解:
@Override:标记方法重写父类方法
@Deprecated:标记已过时的元素
@SuppressWarnings:抑制编译器警告
@SafeVarargs:抑制可变参数的类型安全警告
@FunctionalInterface:标记函数式接口
(2) 元注解:
@Retention:指定注解的保留策略
@Target:指定注解可以应用的目标元素
@Documented:指定注解是否包含在JavaDoc中
@Inherited:指定注解是否可以被继承
@Repeatable:指定注解是否可重复使用
(3) 自定义注解:
3. 注解的语法:
1 2 3 4 5 6
| [元注解] public @interface 注解名称 { 类型 属性名() [default 默认值]; }
|
4. 注解的使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| public class BuiltInAnnotationExample { @Override public String toString() { return "BuiltInAnnotationExample"; } @Deprecated public void oldMethod() { System.out.println("This method is deprecated"); } @SuppressWarnings("unchecked") public void suppressWarningExample() { List list = new ArrayList(); list.add("Hello"); } @FunctionalInterface interface MyFunction { void apply(); } }
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented @Inherited public @interface CustomAnnotation { String value() default "default value"; int number() default 0; }
public class CustomAnnotationExample { @CustomAnnotation(value = "test", number = 1) public void testMethod() { System.out.println("Test method with custom annotation"); } @CustomAnnotation public void defaultMethod() { System.out.println("Default method with custom annotation"); } }
|
5. 注解的保留策略:
RetentionPolicy.SOURCE:仅在源码中保留,编译时丢弃
RetentionPolicy.CLASS:保留到编译后的字节码中,但运行时不加载(默认)
RetentionPolicy.RUNTIME:保留到运行时,可以通过反射获取
6. 注解的目标元素:
ElementType.TYPE:类、接口、枚举
ElementType.FIELD:字段
ElementType.METHOD:方法
ElementType.PARAMETER:参数
ElementType.CONSTRUCTOR:构造方法
ElementType.LOCAL_VARIABLE:局部变量
ElementType.ANNOTATION_TYPE:注解类型
ElementType.PACKAGE:包
ElementType.TYPE_PARAMETER:类型参数(Java 8+)
ElementType.TYPE_USE:类型使用(Java 8+)
7. 注解的处理:
(1) 编译时处理:
- 编译器根据注解生成代码、检查错误等
- 例如,
@Override注解会检查方法是否真的重写了父类方法
(2) 运行时处理:
- 通过反射获取注解信息并处理
- 例如,Spring框架通过反射获取
@Autowired注解来实现依赖注入
(3) 编译后处理:
- 使用注解处理器(Annotation Processor)在编译后生成代码
- 例如,Lombok库通过注解处理器生成getter、setter等方法
8. 注解的应用场景:
- 框架配置:如Spring、Hibernate等框架使用注解进行配置
- 代码生成:如Lombok使用注解生成重复代码
- 测试工具:如JUnit使用注解标记测试方法
- 代码检查:如FindBugs使用注解检查代码质量
- 依赖注入:如Spring使用
@Autowired注解实现依赖注入
- AOP:如Spring使用
@Aspect注解实现切面编程
9. 注解与反射的结合:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| import java.lang.annotation.*; import java.lang.reflect.Method;
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @interface Log { String value() default ""; }
class Service { @Log("执行添加操作") public void add() { System.out.println("Add operation"); } @Log("执行删除操作") public void delete() { System.out.println("Delete operation"); } }
public class AnnotationProcessingExample { public static void main(String[] args) throws Exception { Service service = new Service(); Class<?> clazz = service.getClass(); Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { if (method.isAnnotationPresent(Log.class)) { Log logAnnotation = method.getAnnotation(Log.class); System.out.println("执行方法:" + method.getName() + ",操作:" + logAnnotation.value()); method.invoke(service); System.out.println("方法执行完成\n"); } } } }
|
10. 注意事项:
- 注解本身不会改变代码的执行逻辑,需要通过工具或框架来处理
- 过多使用注解可能会使代码变得难以理解
- 注解的处理会增加一定的性能开销
- 自定义注解时,需要合理选择保留策略和目标元素
5.4 Java 8的新特性有哪些?
Java 8是Java语言的一个重要版本,引入了许多新特性,大幅提升了代码的简洁性和表达能力。以下是Java 8的主要新特性:
1. Lambda表达式:
概念:Lambda表达式是一种简洁的匿名函数,可以作为参数传递给方法或存储在变量中。
语法:(参数列表) -> 表达式 或 (参数列表) -> { 代码块 }
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| Runnable runnable1 = new Runnable() { @Override public void run() { System.out.println("Hello, Java 7!"); } };
Runnable runnable2 = () -> System.out.println("Hello, Java 8!");
Comparator<Integer> comparator = (a, b) -> a.compareTo(b);
BiFunction<Integer, Integer, Integer> add = (a, b) -> { int sum = a + b; return sum; };
|
2. 函数式接口:
概念:只有一个抽象方法的接口,用于支持Lambda表达式。
注解:@FunctionalInterface
常见函数式接口:
Function<T, R>:接收一个参数,返回一个结果
Consumer<T>:接收一个参数,无返回值
Supplier<T>:无参数,返回一个结果
Predicate<T>:接收一个参数,返回布尔值
BiFunction<T, U, R>:接收两个参数,返回一个结果
示例:
1 2 3 4 5 6 7 8 9 10 11
| @FunctionalInterface interface Calculator { int calculate(int a, int b); }
Calculator add = (a, b) -> a + b; Calculator subtract = (a, b) -> a - b;
System.out.println(add.calculate(10, 5)); System.out.println(subtract.calculate(10, 5));
|
3. 方法引用:
概念:使用::操作符引用已有方法,简化Lambda表达式。
类型:
- 静态方法引用:
ClassName::staticMethod
- 实例方法引用:
instance::instanceMethod
- 构造方法引用:
ClassName::new
- 类的实例方法引用:
ClassName::instanceMethod
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Function<String, Integer> parseInt = Integer::parseInt; System.out.println(parseInt.apply("123"));
String str = "Hello"; Supplier<Integer> length = str::length; System.out.println(length.get());
Supplier<List<String>> listSupplier = ArrayList::new; List<String> list = listSupplier.get();
Function<String, String> toUpperCase = String::toUpperCase; System.out.println(toUpperCase.apply("hello"));
|
4. 流(Stream)API:
概念:用于处理集合的高级API,支持链式操作和并行处理。
主要操作:
- 中间操作:filter、map、sorted、distinct等
- 终端操作:collect、forEach、reduce、count等
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve");
List<String> filteredNames = names.stream() .filter(name -> name.length() > 3) .collect(Collectors.toList()); System.out.println(filteredNames);
List<Integer> nameLengths = names.stream() .map(String::length) .collect(Collectors.toList()); System.out.println(nameLengths);
List<String> sortedNames = names.stream() .sorted() .collect(Collectors.toList()); System.out.println(sortedNames);
int sum = IntStream.range(1, 1000000) .parallel() .sum(); System.out.println(sum);
|
5. 默认方法和静态方法:
概念:接口中可以定义默认方法和静态方法。
默认方法:使用default关键字,提供方法的默认实现
静态方法:使用static关键字,直接在接口中定义静态方法
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| interface Vehicle { void start(); default void stop() { System.out.println("Vehicle stopping"); } static void honk() { System.out.println("Beep beep!"); } }
class Car implements Vehicle { @Override public void start() { System.out.println("Car starting"); } @Override public void stop() { System.out.println("Car stopping"); } }
Car car = new Car(); car.start(); car.stop(); Vehicle.honk();
|
6. 新的日期时间API:
概念:引入了java.time包,提供了更清晰、更强大的日期时间处理功能。
主要类:
LocalDate:日期
LocalTime:时间
LocalDateTime:日期时间
ZonedDateTime:带时区的日期时间
Duration:时间间隔
Period:日期间隔
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| LocalDate today = LocalDate.now(); System.out.println("Today: " + today);
LocalDate date = LocalDate.of(2026, Month.APRIL, 12); System.out.println("Date: " + date);
LocalTime now = LocalTime.now(); System.out.println("Now: " + now);
LocalTime time = LocalTime.of(14, 30, 45); System.out.println("Time: " + time);
LocalDateTime dateTime = LocalDateTime.now(); System.out.println("DateTime: " + dateTime);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); String formattedDateTime = dateTime.format(formatter); System.out.println("Formatted: " + formattedDateTime);
LocalDateTime parsedDateTime = LocalDateTime.parse("2026-04-12 14:30:45", formatter); System.out.println("Parsed: " + parsedDateTime);
LocalDate tomorrow = today.plusDays(1); System.out.println("Tomorrow: " + tomorrow);
LocalDate lastMonth = today.minusMonths(1); System.out.println("Last month: " + lastMonth);
|
7. Optional类:
概念:用于处理可能为null的值,避免NullPointerException。
主要方法:
of():创建非null的Optional
ofNullable():创建可能为null的Optional
empty():创建空的Optional
isPresent():检查值是否存在
get():获取值(如果不存在则抛出异常)
orElse():获取值,如果不存在则返回默认值
orElseGet():获取值,如果不存在则通过Supplier获取默认值
orElseThrow():获取值,如果不存在则抛出指定异常
map():转换值
flatMap():转换为另一个Optional
filter():过滤值
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| Optional<String> optional1 = Optional.of("Hello"); Optional<String> optional2 = Optional.ofNullable(null); Optional<String> optional3 = Optional.empty();
System.out.println(optional1.isPresent()); System.out.println(optional2.isPresent()); System.out.println(optional3.isPresent());
System.out.println(optional1.get());
System.out.println(optional2.orElse("Default")); System.out.println(optional3.orElseGet(() -> "Generated Default"));
Optional<Integer> lengthOptional = optional1.map(String::length); System.out.println(lengthOptional.get());
Optional<String> filteredOptional = optional1.filter(s -> s.length() > 3); System.out.println(filteredOptional.isPresent());
String result = optional1 .map(String::toUpperCase) .filter(s -> s.startsWith("H")) .orElse("Default"); System.out.println(result);
|
8. Nashorn JavaScript引擎:
概念:Java 8引入的新JavaScript引擎,替代了Rhino,提供了更好的性能和兼容性。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException;
public class NashornExample { public static void main(String[] args) throws ScriptException { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("nashorn"); engine.eval("print('Hello, Nashorn!')"); engine.eval("function add(a, b) { return a + b; }"); Object result = engine.eval("add(10, 5)"); System.out.println("10 + 5 = " + result); engine.put("name", "Java"); engine.eval("print('Hello, ' + name)"); } }
|
9. 其他新特性:
- 类型注解:可以在类型声明处使用注解
- 重复注解:可以在同一元素上重复使用相同的注解
- 方法参数名称:在编译时保留方法参数名称,便于反射获取
- 并行数组操作:
Arrays类新增了并行操作方法,如parallelSort、parallelSetAll等
- CompletableFuture:提供了更强大的异步编程支持
10. 总结:
Java 8的新特性大幅提升了代码的简洁性、可读性和表达能力,特别是Lambda表达式、Stream API和新的日期时间API,已经成为现代Java开发的标配。这些特性使得Java代码更加优雅、高效,同时也为函数式编程在Java中的应用奠定了基础。