面试 Java 基础八股文十问十答第十一期
相信看了本文后,对你的面试是有一定帮助的!关注专栏后就能收到持续更新!
⭐点赞⭐收藏⭐不迷路!⭐
1)static 注意事项
使用 static
时需要注意以下几点:
- 静态变量的初始化: 静态变量在类加载时被初始化,因此应该注意初始化顺序和可能的依赖关系。
- 静态方法中的限制: 静态方法只能直接访问其他静态成员,而不能访问非静态成员,因为在调用静态方法时可能还没有创建类的实例。
- 静态块的执行: 静态块在类加载时执行,且仅执行一次。因此,静态块中的代码应该只包含静态变量的初始化或其他静态成员的初始化。
- 线程安全性: 静态方法和静态变量是与类相关联的,而不是与类的实例相关联的。因此,需要注意在多线程环境下对静态成员的访问,需要采取适当的同步措施来确保线程安全性。
- 内存占用: 静态变量会随着类的加载而分配内存,并且一直存在于堆内存中,直到程序结束。因此,需要谨慎使用静态变量,避免占用过多的内存空间。
2)break、continue 和 return 的区别及作用
- break:
break
语句用于终止当前循环或switch
语句的执行,并跳出该循环或switch
语句的代码块。 - continue:
continue
语句用于终止当前循环的本次迭代,并开始下一次循环的迭代。 - return:
return
语句用于从当前方法中返回一个值,并终止该方法的执行。
区别及作用:
break
用于结束循环或switch
语句的执行,常用于循环中的条件达成时提前退出循环。continue
用于跳过当前循环的剩余部分,继续执行下一次循环的迭代,通常与条件语句一起使用。return
用于从方法中返回一个值,并终止方法的执行,常用于在方法中得到想要的结果后立即返回。
3)在 Java 中,如何跳出当前的多重嵌套循环
要从多重嵌套循环中跳出,可以使用标签(label)和 break
语句的组合。具体步骤如下:
- 在外层循环之前添加一个标签,如
outerLoop:
。 - 在需要跳出的位置使用带有标签的
break
语句,例如break outerLoop;
。 - 当执行到带有标签的
break
语句时,程序会跳出标签所标记的外层循环。
以下是一个示例:
outerLoop:
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
if (i * j > 6) {
System.out.println("Breaking at i = " + i + ", j = " + j);
break outerLoop; // 跳出外层循环
}
System.out.println(i + " * " + j + " = " + (i * j));
}
}
在上面的示例中,当 i * j
大于 6 时,会跳出外层循环,并打印跳出的位置。
4)面向对象和面向过程的区别:
- 面向对象(Object-Oriented Programming,OOP):
- 把对象作为程序的基本单元,对象之间通过消息传递进行通信和交互。
- 将数据与操作数据的函数绑定在一起,使得数据更为安全。
- 通过封装、继承和多态等特性,实现代码的重用性、可扩展性和灵活性。
- 着重于对问题进行抽象和建模,使得程序结构更加清晰、易于理解和维护。
- 面向过程(Procedural Programming):
- 把任务分解为一个个独立的步骤,通过函数调用来完成程序的执行。
- 着重于算法的实现,将数据和操作数据的函数分开处理。
- 程序的结构比较简单直接,适用于一些简单的、线性的任务。
区别:
- 面向对象强调对象和类的概念,注重于数据的封装、继承、多态等概念,而面向过程则更注重于算法的实现和步骤的顺序。
- 面向对象更适合于大型复杂系统的开发,能够更好地应对需求的变化和系统的维护,而面向过程更适合于简单的任务和小型项目。
5)面向对象的特征有哪些方面:
面向对象的特征包括:
- 封装(Encapsulation):将数据和操作数据的方法封装在一起,隐藏了对象的内部细节,只对外部暴露必要的接口,提高了数据的安全性和可靠性。
- 继承(Inheritance):通过继承机制,子类可以继承父类的属性和方法,并可以根据需要添加新的属性和方法,实现了代码的重用性和扩展性。
- 多态(Polymorphism):允许不同类的对象对同一消息做出响应,不同对象可以对同一消息作出不同的响应,提高了代码的灵活性和扩展性。
- 抽象(Abstraction):通过抽象类和接口等机制,将对象的共同特征抽象出来,形成类的层次结构,简化了系统的设计和实现。
6)什么是多态机制?Java语言是如何实现多态的?
多态(Polymorphism) 是面向对象编程中的一个重要概念,指的是同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。
在Java中,多态是通过继承和方法重写来实现的。具体来说,多态包括两种形式:
- 编译时多态(Compile-time Polymorphism),也称为方法重载(Overloading):在同一个类中,可以定义多个方法具有相同的名字,但参数类型或个数不同,编译器根据方法调用时传入的参数类型和个数来决定调用哪个方法。
- 运行时多态(Runtime Polymorphism),也称为方法重写(Overriding):子类可以重写(覆盖)父类的方法,当通过父类引用调用被子类重写的方法时,实际上会调用子类中的方法,这种行为称为动态绑定或后期绑定。
示例:
class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
@Override
void sound() {
System.out.println("Cat meows");
}
}
public class Main {
public static void main(String[] args) {
Animal animal1 = new Dog();
Animal animal2 = new Cat();
animal1.sound(); // 输出:Dog barks
animal2.sound(); // 输出:Cat meows
}
}
在上面的示例中,Animal
类有一个 sound()
方法,Dog
和 Cat
类分别重写了这个方法。在 Main
类中,创建了一个 Dog
对象和一个 Cat
对象,并用 Animal
类型的引用变量引用它们,通过这些引用变量调用 sound()
方法时,实际上调用的是对应子类中重写的方法,而不是 Animal
类中的方法,这就是运行时多态的体现。
7)面向对象五大基本原则是什么:
这指的是SOLID原则,分别是:
- 单一职责原则(Single Responsibility Principle,SRP):一个类应该只有一个引起它变化的原因,即一个类应该只有一个职责。
- 开放封闭原则(Open/Closed Principle,OCP):一个软件实体(类、模块、函数等等)应该对扩展开放,对修改关闭。即在不修改现有代码的前提下,通过添加新代码来扩展系统的功能。
- 里氏替换原则(Liskov Substitution Principle,LSP):子类必须能够替换掉它们的父类。即,如果对一个基类使用的对象,替换成其子类,程序的行为不应该有变化。
- 接口隔离原则(Interface Segregation Principle,ISP):不应该强迫一个类实现它并不使用的接口。即,一个类对另一个类的依赖应该建立在最小的接口上。
- 依赖反转原则(Dependency Inversion Principle,DIP):高层模块不应该依赖于底层模块,两者都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。即,依赖关系应该是通过抽象(接口或抽象类)而不是具体实现发生的。
8)抽象类和接口的对比:
抽象类(Abstract Class):
- 可以包含抽象方法和具体方法。
- 可以包含成员变量,可以有构造方法。
- 不能被实例化,需要被子类继承。
- 子类可以继承一个抽象类。
- 支持访问修饰符,如 public、protected、private。
接口(Interface):
- 只能包含抽象方法(Java 8 之后可以包含默认方法和静态方法)。
- 不能包含成员变量,不能有构造方法。
- 不能被实例化,需要被类实现。
- 类可以实现多个接口。
- 所有方法默认为 public。
对比:
- 用途:抽象类用于表示一种“is-a”关系,接口用于表示一种“has-a”关系,即一个类可以拥有多个接口。
- 构造方法:抽象类可以有构造方法,接口不能有构造方法。
- 继承:一个类只能继承一个抽象类,但可以实现多个接口。
- 成员变量:抽象类可以包含成员变量,而接口不能。
- 访问修饰符:抽象类的方法可以有不同的访问修饰符,而接口的方法默认为 public。
9)普通类和抽象类有哪些区别?
普通类:
- 可以被实例化,直接用来创建对象。
- 可以包含成员变量、普通方法、构造方法等。
- 不强制包含抽象方法。
- 不能被声明为抽象类。
抽象类:
- 不能被实例化,需要被继承后才能创建对象。
- 可以包含成员变量、抽象方法、普通方法、构造方法等。
- 必须包含至少一个抽象方法,否则可以直接使用普通类。
- 可以被声明为抽象类。
区别:
- 抽象类不能被实例化,普通类可以。
- 抽象类可以包含抽象方法,普通类不强制包含抽象方法。
- 抽象类通常用于作为基类,普通类用于直接创建对象。
10)抽象类能使用 final 修饰吗?
在Java中,抽象类可以使用 final
修饰,但有一些限制:
- 如果一个抽象类的所有方法都是抽象的,那么这个抽象类可以被声明为
final
。 - 如果一个抽象类有一个或多个具体方法,那么不能将该抽象类声明为
final
,因为final
类不能被继承,而其他类需要继承这个抽象类来实现具体方法。
示例:
// 可以声明为 final 的抽象类
final abstract class MyAbstractClass {
abstract void myAbstractMethod();
}
// 不能声明为 final 的抽象类
abstract class AnotherAbstractClass {
abstract void anotherAbstractMethod();
void concreteMethod() {
// 具体方法的实现
}
}
// 错误示例,final 类不能被继承
// final abstract class FinalAbstractClass {
// abstract void finalAbstractMethod();
// }
总的来说,抽象类可以使用 final
修饰,但要注意其具体方法的情况
开源项目地址:https://gitee.com/falle22222n-leaves/vue_-book-manage-system
前后端总计已经 700+ Star,1W+ 访问!
⭐点赞⭐收藏⭐不迷路!⭐