引言
上一篇文章《java基础知识点(一)》已经介绍大部分的java基础知识点了,本篇文章主要用于补充上一篇遗漏的知识点,主要知识点有访问修饰符、接口和抽象类、面向对象的三大特征和异常处理。
访问修饰符
在java中,有四种访问修饰符,用于控制类、方法、属性和构造方法的可访问性:
public:public是最开放的访问修饰符,被声明为public的类、方法、属性和构造方法可以在任何地方被访问。其他类可以直接访问public成员,并且可以从任何地方继承public类。private:private是最严格的访问修饰符,被声明为private的类、方法、属性和构造方法只能在其自己的类中被访问。私有成员无法被其他类直接访问,通常需要提供公共的访问方法(getter和setter)来间接访问私有成员。protected:protected访问修饰符允许在同一包内的类以及不同包中的子类访问被声明为protected的成员。受保护的成员对于其他包中的非子类是不可见的。- 默认:如果没有显式地使用
public、private或protected修饰符,则使用默认访问修饰符。默认访问修饰符将类、方法、属性和构造方法限制为同一包中可见。
下面是各个访问修饰符的使用场景:
public:通常用于公共接口、公共类和对外部可见的方法,允许从任何地方访问。private:通常用于隐藏类的内部实现细节,仅在类内部可见。protected:通常用于允许子类访问父类的成员,以及在同一包内的其他类访问。- 默认:通常用于限制访问范围,仅在同一包内可见。
选择适当的访问修饰符可以控制类和其成员的访问范围,确保代码的安全性和封装性,并促进良好的编程实践。根据具体需求和设计原则,合理使用访问修饰符可以提高代码的可维护性和可重用性。
接口和抽象类
接口和抽象类是java中两种常见的抽象机制,它们用于定义类的行为和规范
抽象类
- 定义:抽象类是不能被实例化的类,它用关键字 "abstract" 声明。抽象类可以包含抽象方法(没有实现的方法)和具体方法(有实现的方法)。
- 继承:抽象类可以被继承,子类必须实现抽象类中的抽象方法。一个类只能继承一个抽象类。
- 特点:抽象类可以包含属性、构造方法、具体方法和抽象方法。它可以提供一些默认实现,并定义一些必须由子类
- 使用场景:抽象类适用于定义一组相关的类的通用行为和属性。当多个类之间存在共同的属性和行为,并且这些类之间有继承关系时,可以使用抽象类来提取共性,减少代码的重复。
接口
- 定义:接口是一种纯抽象类,它只包含方法的声明而没有方法的实现。接口用关键字
interface声明,并且可以包含常量和默认方法(Java 8+)。 - 实现:一个类可以实现(
implements)一个或多个接口,实现接口的类必须实现接口中定义的所有方法。 - 特点:接口中的方法默认是抽象的,不包含方法体。它提供了一种规范,定义了类应该具备的行为,而不关心具体的实现细节。
- 使用场景:接口适用于定义一组类共同的协议或规范。当多个类需要遵循相同的契约或规范,但它们之间没有继承关系时,可以使用接口来实现代码的解耦和多态性。
抽象类和接口的区别
- 抽象类可以包含属性、构造方法、具体方法和抽象方法,而接口只能包含常量和方法的声明。
- 类可以继承一个抽象类,但可以实现多个接口。
- 抽象类可以提供方法的默认实现,而接口中的方法默认是抽象的。
- 抽象类的设计目的是为了提供类的共性和继承关系,而接口的设计目的是为了定义类的规范和行为。
面向对象的三大特征
封装
封装是面向对象编程中的一种重要概念,它将数据和方法封装在一个单元(类)中,并对外部隐藏数据的具体实现细节。通过封装,可以实现以下几个目标:
- 数据隐藏:封装可以将类的实现细节隐藏起来,只对外部提供必要的接口。这样可以防止外部直接访问和修改类的内部数据,增强了数据的安全性和完整性。
- 数据访问控制:通过定义合适的访问修饰符(如
private、public、protected),可以控制对数据的访问权限。只有通过公共的方法(getter和setter)才能访问和修改数据,从而对数据的操作进行有效的控制。 - 代码封装和复用:封装可以将一组相关的数据和方法组织在一起,形成一个独立的模块。这样可以提高代码的可维护性和可重用性,使得代码更加模块化和易于理解。
继承
继承是面向对象编程中实现代码重用和建立类之间关系的机制。通过继承,一个类(子类)可以继承另一个类(父类)的属性和方法。继承的主要特点包括:
- 代码重用:通过继承,子类可以直接使用父类中已经定义的属性和方法,避免重复编写相同的代码,提高代码的复用性。
- 继承层次:继承可以形成类之间的层次关系,通过创建不同层次的子类可以实现更具体和抽象的对象模型。子类可以继承父类的特性,并且可以在此基础上进行扩展和修改。
- 方法重写:子类可以对父类中的方法进行重写(
Override),即重新定义父类方法的实现逻辑。通过方法重写,子类可以根据自身的需求重新定义父类方法的行为,实现多态的特性。
多态
多态是面向对象编程中的重要特性,它允许不同类型的对象对同一消息做出不同的响应。多态的实现依赖于继承和方法重写。主要有以下几种形式:
- 编译时多态:通过方法重载(
Overload)实现,同一个类中可以有多个同名的方法,但参数列表不同。编译器根据方法的参数类型和数量来选择调用适合的方法。
class Calculator {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
}
public class Main {
public static void main(String[] args) {
Calculator calculator = new Calculator();
int result1 = calculator.add(3, 5); // 调用参数为int类型的add方法
double result2 = calculator.add(3.5, 5.7); // 调用参数为double类型的add方法
}
}
- 运行时多态:通过方法重写(
Override)实现,子类可以重写父类中的方法,并在运行时根据对象的实际类型调用相应的方法。运行时多态允许同一个方法在不同的对象上表现出不同的行为,提供了灵活性和扩展性。
class Animal {
public void makeSound() {
System.out.println("The animal makes a sound.");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("The dog barks.");
}
}
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("The cat meows.");
}
}
public class Main {
public static void main(String[] args) {
Animal animal1 = new Dog();
Animal animal2 = new Cat();
animal1.makeSound(); // 调用的是Dog类中重写的makeSound方法
animal2.makeSound(); // 调用的是Cat类中重写的makeSound方法
}
}
异常处理
在Java中,异常处理是一种机制,用于捕获和处理程序中可能发生的异常情况,以保证程序的正常执行或进行适当的错误处理。异常处理可以有效地处理程序运行过程中出现的异常情况,防止程序崩溃或产生不可预知的结果。
java中的异常可以分为两类:可检查异常(Checked Exception)和不可检查异常(Unchecked Exception)。
- 可检查异常:可检查异常是指在编译时需要进行检查的异常,程序必须显式地处理或向上层抛出这些异常。这些异常通常表示程序可能遇到的外部问题,如
I/O错误、数据库连接错误等。处理可检查异常的方式有两种:
- 使用try-catch语句捕获并处理异常:
try {
// 可能抛出异常的代码
} catch (ExceptionType1 e1) {
// 异常处理逻辑
} catch (ExceptionType2 e2) {
// 异常处理逻辑
} finally {
// 可选的finally代码块,用于执行清理操作
}
- 在方法声明中使用throws关键字声明可能抛出的异常,并将异常传递给上层调用者处理:
public void methodName() throws ExceptionType1, ExceptionType2 {
// 可能抛出异常的代码
}
- 不可检查异常:不可检查异常是指在编译时不需要进行检查的异常,它们通常是由程序错误导致的,如空指针异常、数组越界异常等。不可检查异常不要求显式地捕获或声明,但仍然可以选择进行处理。
- 使用try-catch语句捕获并处理异常:
try {
// 可能抛出异常的代码
} catch (ExceptionType1 e1) {
// 异常处理逻辑
} catch (ExceptionType2 e2) {
// 异常处理逻辑
} finally {
// 可选的finally代码块,用于执行清理操作
}
- 不处理异常,将异常传播给调用者处理。
异常处理的一般原则包括:
- 在适当的地方捕获和处理异常,避免异常影响程序的正常执行。
- 提供有意义的异常信息和错误处理逻辑,以便调试和排查问题。
- 使用try-catch-finally语句块来处理异常,并确保资源的正确释放和清理。
- 避免过度捕获异常,只捕获自己能够处理的异常。
- 使用适当的异常类型来匹配具体的异常情况。
下面是一个简单的异常处理示例:
import java.util.InputMismatchException;
import java.util.Scanner;
public class ExceptionHandlingExample {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
System.out.print("请输入一个整数: ");
int num1 = scanner.nextInt();
System.out.print("请输入另一个整数: ");
int num2 = scanner.nextInt();
int result = divide(num1, num2);
System.out.println("两数相除的结果为: " + result);
} catch (InputMismatchException e) {
System.out.println("输入的不是有效的整数");
} catch (ArithmeticException e) {
System.out.println("除数不能为0");
} finally {
scanner.close();
}
}
public static int divide(int num1, int num2) {
return num1 / num2;
}
}
在上述示例中,通过使用try-catch语句块来捕获可能发生的异常。在try块中,接受用户输入的两个整数,并调用divide方法进行除法运算。如果用户输入的不是整数(InputMismatchException),或者除数为0(ArithmeticException),则会抛出相应的异常,程序会跳转到相应的catch块中进行异常处理。
在catch块中,我们根据异常类型打印出相应的错误信息。无论是否发生异常,finally块中的代码总会执行,我们在这里关闭了Scanner对象,释放相关资源。
这个示例展示了如何使用异常处理机制来保护代码,处理可能发生的错误情况,并提供有意义的错误提示。通过适当的异常处理,我们可以避免程序崩溃,并提供更好的用户体验。