引言
在掌握了 Java 基础之后,踏入中级编程的领域就意味着要处理更复杂的问题,运用更高级的特性。中级 Java 编程包含了面向对象的高级特性、异常处理、集合框架、多线程编程等内容。这些特性能够让你编写出更高效、更具可维护性和可扩展性的程序。本教程将通过详细且专业的代码示例和深入浅出的讲解,带你逐步掌握 Java 中级编程的核心要点。
1. 面向对象的高级特性
1.1 抽象类和接口
抽象类
抽象类是一种不能被实例化的类,它主要用于为子类提供一个通用的模板。抽象类可以包含抽象方法和具体方法。抽象方法只有声明,没有实现,需要子类来实现。
java
// 定义一个抽象类 Animal
abstract class Animal {
// 成员变量
protected String name;
// 构造方法
public Animal(String name) {
this.name = name;
}
// 抽象方法,用于发出声音
public abstract void makeSound();
// 具体方法,用于睡觉
public void sleep() {
System.out.println(name + " is sleeping");
}
}
// 定义一个子类 Dog 继承自 Animal
class Dog extends Animal {
// 构造方法
public Dog(String name) {
super(name);
}
// 实现抽象方法
@Override
public void makeSound() {
System.out.println(name + " says Woof!");
}
}
// 定义一个子类 Cat 继承自 Animal
class Cat extends Animal {
// 构造方法
public Cat(String name) {
super(name);
}
// 实现抽象方法
@Override
public void makeSound() {
System.out.println(name + " says Meow!");
}
}
public class AbstractClassExample {
public static void main(String[] args) {
// 创建 Dog 对象
Dog dog = new Dog("Buddy");
dog.makeSound();
dog.sleep();
// 创建 Cat 对象
Cat cat = new Cat("Whiskers");
cat.makeSound();
cat.sleep();
}
}
在上述代码中,Animal
是一个抽象类,包含一个成员变量 name
、一个构造方法、一个抽象方法 makeSound()
和一个具体方法 sleep()
。Dog
和 Cat
类继承自 Animal
类,并实现了 makeSound()
方法。
接口
接口是一种完全抽象的类型,它只包含抽象方法和常量。一个类可以实现多个接口,从而实现多重继承的效果。
java
// 定义一个接口 Flyable
interface Flyable {
// 常量
int MAX_ALTITUDE = 10000;
// 抽象方法,用于飞行
void fly();
}
// 定义一个接口 Swimmable
interface Swimmable {
// 抽象方法,用于游泳
void swim();
}
// 定义一个类 Duck 实现 Flyable 和 Swimmable 接口
class Duck implements Flyable, Swimmable {
private String name;
public Duck(String name) {
this.name = name;
}
@Override
public void fly() {
System.out.println(name + " is flying at altitude up to " + MAX_ALTITUDE + " meters");
}
@Override
public void swim() {
System.out.println(name + " is swimming gracefully");
}
}
public class InterfaceExample {
public static void main(String[] args) {
Duck duck = new Duck("Donald");
duck.fly();
duck.swim();
}
}
在上述代码中,Flyable
和 Swimmable
是两个接口,Duck
类实现了这两个接口,并实现了接口中的抽象方法。接口中的常量 MAX_ALTITUDE
可以在实现类中直接使用。
1.2 多态
多态是指同一个方法调用可以根据对象的不同类型而表现出不同的行为。多态主要通过继承和接口实现。
java
// 定义一个父类 Shape
class Shape {
// 计算面积的方法
public double area() {
return 0;
}
}
// 定义一个子类 Circle 继承自 Shape
class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
}
// 定义一个子类 Rectangle 继承自 Shape
class Rectangle extends Shape {
private double length;
private double width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
@Override
public double area() {
return length * width;
}
}
public class PolymorphismExample {
public static void printArea(Shape shape) {
System.out.println("The area of the shape is: " + shape.area());
}
public static void main(String[] args) {
Shape circle = new Circle(5);
Shape rectangle = new Rectangle(4, 6);
printArea(circle);
printArea(rectangle);
}
}
在上述代码中,Shape
是父类,Circle
和 Rectangle
是子类。printArea()
方法接受一个 Shape
类型的参数,根据传入的实际对象类型调用相应的 area()
方法,实现了多态。
2. 异常处理
在 Java 中,异常是指程序在运行过程中出现的错误或意外情况。异常处理可以让程序在出现异常时能够继续执行,提高程序的健壮性。
2.1 异常的分类
Java 中的异常分为两类:受检查异常(Checked Exception)和非受检查异常(Unchecked Exception)。受检查异常必须在方法签名中声明或捕获,非受检查异常不需要。
2.2 异常处理的语法
异常处理使用 try-catch-finally
语句块。此外,还可以使用 throws
关键字在方法签名中声明可能抛出的异常。
java
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class ExceptionHandlingExample {
public static void readFile(String filePath) throws FileNotFoundException {
File file = new File(filePath);
Scanner scanner = new Scanner(file);
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
scanner.close();
}
public static void main(String[] args) {
try {
readFile("nonexistent.txt");
} catch (FileNotFoundException e) {
System.out.println("Error: File not found - " + e.getMessage());
} finally {
System.out.println("This block always executes");
}
}
}
在上述代码中,readFile()
方法使用 throws
关键字声明可能抛出 FileNotFoundException
异常。在 main()
方法中,调用 readFile()
方法并使用 try-catch-finally
语句块进行异常处理。
2.3 自定义异常
除了 Java 提供的异常类,我们还可以自定义异常类。自定义异常类通常继承自 Exception
或其子类。
java
// 自定义异常类
class NegativeNumberException extends Exception {
public NegativeNumberException(String message) {
super(message);
}
}
// 计算平方根的类
class SquareRootCalculator {
public static double calculateSquareRoot(double number) throws NegativeNumberException {
if (number < 0) {
throw new NegativeNumberException("Cannot calculate square root of a negative number");
}
return Math.sqrt(number);
}
}
public class CustomExceptionExample {
public static void main(String[] args) {
try {
double result = SquareRootCalculator.calculateSquareRoot(-4);
System.out.println("Square root: " + result);
} catch (NegativeNumberException e) {
System.out.println("Exception caught: " + e.getMessage());
}
}
}
在上述代码中,NegativeNumberException
是自定义异常类,继承自 Exception
。SquareRootCalculator
类的 calculateSquareRoot()
方法在遇到负数时抛出 NegativeNumberException
异常。
3. 集合框架
Java 集合框架提供了一组用于存储和操作数据的类和接口。常用的集合类有 List
、Set
和 Map
。
3.1 List
List
是一个有序的集合,允许存储重复的元素。常用的实现类有 ArrayList
和 LinkedList
。
java
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class ListExample {
public static void main(String[] args) {
// 创建一个 ArrayList 对象
List<String> arrayList = new ArrayList<>();
arrayList.add("Apple");
arrayList.add("Banana");
arrayList.add("Cherry");
// 使用迭代器遍历 ArrayList
Iterator<String> arrayListIterator = arrayList.iterator();
while (arrayListIterator.hasNext()) {
System.out.println(arrayListIterator.next());
}
// 创建一个 LinkedList 对象
List<String> linkedList = new LinkedList<>();
linkedList.add("Dog");
linkedList.add("Cat");
linkedList.add("Bird");
// 使用增强 for 循环遍历 LinkedList
for (String animal : linkedList) {
System.out.println(animal);
}
}
}
在上述代码中,我们创建了 ArrayList
和 LinkedList
对象,并向其中添加元素。使用迭代器和增强 for
循环分别遍历这两个列表。
3.2 Set
Set
是一个不允许存储重复元素的集合。常用的实现类有 HashSet
和 TreeSet
。
java
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
public class SetExample {
public static void main(String[] args) {
// 创建一个 HashSet 对象
Set<String> hashSet = new HashSet<>();
hashSet.add("Apple");
hashSet.add("Banana");
hashSet.add("Apple"); // 重复元素,不会被添加
// 使用迭代器遍历 HashSet
Iterator<String> hashSetIterator = hashSet.iterator();
while (hashSetIterator.hasNext()) {
System.out.println(hashSetIterator.next());
}
// 创建一个 TreeSet 对象
Set<String> treeSet = new TreeSet<>();
treeSet.add("Dog");
treeSet.add("Cat");
treeSet.add("Bird");
// 使用增强 for 循环遍历 TreeSet
for (String animal : treeSet) {
System.out.println(animal);
}
}
}
在上述代码中,我们创建了 HashSet
和 TreeSet
对象,并向其中添加元素。由于 Set
不允许重复元素,所以重复的元素不会被添加。使用迭代器和增强 for
循环分别遍历这两个集合。
3.3 Map
Map
是一种键值对的集合,每个键对应一个值。常用的实现类有 HashMap
和 TreeMap
。
java
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
public class MapExample {
public static void main(String[] args) {
// 创建一个 HashMap 对象
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("Apple", 1);
hashMap.put("Banana", 2);
hashMap.put("Cherry", 3);
// 使用迭代器遍历 HashMap 的键值对
Iterator<Map.Entry<String, Integer>> hashMapIterator = hashMap.entrySet().iterator();
while (hashMapIterator.hasNext()) {
Map.Entry<String, Integer> entry = hashMapIterator.next();
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// 创建一个 TreeMap 对象
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put("Dog", 1);
treeMap.put("Cat", 2);
treeMap.put("Bird", 3);
// 使用增强 for 循环遍历 TreeMap 的键值对
for (Map.Entry<String, Integer> entry : treeMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
在上述代码中,我们创建了 HashMap
和 TreeMap
对象,并向其中添加键值对。使用迭代器和增强 for
循环分别遍历这两个 Map
。
4. 多线程编程
多线程编程可以让程序同时执行多个任务,提高程序的性能和响应速度。
4.1 创建线程的两种方式
在 Java 中,创建线程有两种方式:继承 Thread
类和实现 Runnable
接口。
继承 Thread
类
java
class MyThread extends Thread {
private String threadName;
public MyThread(String name) {
threadName = name;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(threadName + ": " + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadExample {
public static void main(String[] args) {
MyThread thread1 = new MyThread("Thread 1");
MyThread thread2 = new MyThread("Thread 2");
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread finished");
}
}
在上述代码中,MyThread
类继承自 Thread
类,并重写了 run()
方法。在 main()
方法中,创建了 MyThread
对象并调用 start()
方法启动线程。使用 join()
方法等待线程执行完毕。
实现 Runnable
接口
java
class MyRunnable implements Runnable {
private String threadName;
public MyRunnable(String name) {
threadName = name;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(threadName + ": " + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class RunnableExample {
public static void main(String[] args) {
MyRunnable myRunnable1 = new MyRunnable("Runnable 1");
MyRunnable myRunnable2 = new MyRunnable("Runnable 2");
Thread thread1 = new Thread(myRunnable1);
Thread thread2 = new Thread(myRunnable2);
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread finished");
}
}
在上述代码中,MyRunnable
类实现了 Runnable
接口,并重写了 run()
方法。在 main()
方法中,创建了 MyRunnable
对象,并将其作为参数传递给 Thread
类的构造函数,然后调用 start()
方法启动线程。使用 join()
方法等待线程执行完毕。
4.2 线程同步
当多个线程访问共享资源时,可能会出现数据不一致的问题。线程同步可以解决这个问题。
java
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized void decrement() {
count--;
}
public int getCount() {
return count;
}
}
class IncrementThread implements Runnable {
private Counter counter;
public IncrementThread(Counter counter) {
this.counter = counter;
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
}
}
class DecrementThread implements Runnable {
private Counter counter;
public DecrementThread(Counter counter) {
this.counter = counter;
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
counter.decrement();
}
}
}
public class ThreadSynchronizationExample {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread incrementThread = new Thread(new IncrementThread(counter));
Thread decrementThread = new Thread(new DecrementThread(counter));
incrementThread.start();
decrementThread.start();
incrementThread.join();
decrementThread.join();
System.out.println("Final count: " + counter.getCount());
}
}
在上述代码中,Counter
类的 increment()
和 decrement()
方法使用了 synchronized
关键字,确保同一时间只有一个线程可以访问这些方法,从而避免了数据不一致的问题。
结论
通过本教程的学习,你已经深入掌握了 Java 中级编程的核心内容,包括面向对象的高级特性、异常处理、集合框架和多线程编程。这些知识将帮助你编写出更高效、更复杂、更具可维护性的 Java 程序。希望你在今后的学习和实践中不断巩固和提高自己的编程能力,挑战更复杂的项目。