Java面向对象面试题总结(上)

简介: 在Java中,重写(Override)与重载(Overload)是两个重要的概念,关联到方法的定义与调用。重写是指子类对继承自父类的方法进行新的实现,以便提供子类特有的行为,其关键在于方法签名一致但方法体不同。重载则允许在同一个类中定义多个同名方法,只要参数列表不同即可,以此提供方法调用的灵活性。重写关注多态性,而重载强调编译时多态。

一、重写和重载

在Java中,重写(Override)和重载(Overload)是面向对象编程中两个非常重要的概念,它们都与方法的定义和调用有关,但两者有着本质的区别。

1、重写(Override)

重写是子类对父类中继承来的方法进行重新定义(也就是方法签名相同,但方法体不同)。当子类需要修改从父类继承来的方法的行为时,就会使用到重写。重写的目的是允许子类提供特定于自己的实现。

重写的规则

  1. 方法名、参数列表必须相同:这是为了保持多态性,即父类类型的引用可以指向子类对象,并调用实际子类对象的方法。
  2. 返回类型:对于非静态方法,返回类型可以是父类方法的返回类型的子类型(Java 5及以后版本支持协变返回类型)。对于静态方法,返回类型必须相同。
  3. 访问修饰符:子类方法的访问级别不能低于父类方法的访问级别(但可以有更高的访问级别)。
  4. 异常:子类方法抛出的异常应该是父类方法抛出异常的子类或没有异常(Java 7及以后版本支持更灵活的异常处理规则)。

示例

class Animal {
   
    void eat() {
   
        System.out.println("This animal eats food.");
    }
}

class Dog extends Animal {
   
    @Override
    void eat() {
   
        System.out.println("Dog eats dog food.");
    }
}

在这个例子中,Dog类重写了Animal类的eat方法。

2、重载(Overload)

重载是在同一个类中,允许存在多个同名的方法,只要它们的参数列表不同即可。参数列表不同意味着参数的数量、类型或顺序至少有一项不同。重载的主要目的是提供灵活的方法调用,允许根据传递的参数类型或数量来调用不同的方法实现。

重载的规则

  1. 方法名必须相同
  2. 参数列表必须不同(参数的数量、类型或顺序不同)。
  3. 方法的返回类型、访问修饰符以及抛出的异常类型与重载无关。

示例

class MathUtils {
   
    // 方法1
    int add(int a, int b) {
   
        return a + b;
    }

    // 方法2,重载
    double add(double a, double b) {
   
        return a + b;
    }

    // 方法3,重载(参数数量不同)
    int add(int a, int b, int c) {
   
        return a + b + c;
    }
}

在这个例子中,MathUtils类有三个名为add的方法,但它们的参数列表不同,因此它们是重载关系。

总结

  • 重写是子类对父类方法的重新定义,用于提供特定于子类的实现。
  • 重载是在同一个类中允许存在多个同名但参数列表不同的方法,用于提供灵活的方法调用。
  • 重写是面向对象多态性的体现,而重载是编译时多态性的体现。

二、equals 和 hashcode

1、简述 hashCode()equals(Object obj) 的作用及其关系

hashCode() 方法用于获取对象的哈希码,即一个整数。这个哈希码在基于哈希的集合(如HashSet、HashMap等)中用于确定对象的存储位置。

equals(Object obj) 方法用于比较两个对象是否相等。默认情况下,它比较的是对象的引用地址;但在自定义类中,通常会重写该方法以比较对象的内容。

这两个方法之间的关系是:如果两个对象通过 equals(Object obj) 方法比较是相等的,那么调用这两个对象中任一对象的 hashCode() 方法必须产生相同的整数结果。这是Java集合框架正常工作的基本要求。

2、为什么要在自定义类中同时重写 hashCode()equals(Object obj) 方法?

如果只重写 equals(Object obj) 方法而不重写 hashCode() 方法,那么在基于哈希的集合中,即使两个对象通过 equals(Object obj) 方法比较是相等的,但由于它们的哈希码不同,这些集合也可能无法正确地处理它们(如无法正确去重)。

因此,为了保证自定义对象在Java集合框架中的正确性,当重写 equals(Object obj) 方法时,通常也需要重写 hashCode() 方法,以确保 equals(Object obj) 相等的对象具有相同的哈希码。

3、请解释为什么hashCode()方法可能产生哈希碰撞,以及这是否会影响equals(Object obj)方法的正确性?

哈希碰撞是指不同的对象产生相同的哈希码。由于哈希码是一个整数,而整数的范围是有限的,而对象的数量可以是无限的,因此哈希碰撞是不可避免的。

哈希碰撞本身不会影响 equals(Object obj) 方法的正确性。equals(Object obj) 方法用于比较对象的内容是否相等,而哈希码只是用于在集合中快速定位对象的一种机制。即使两个对象产生了哈希碰撞,只要它们的 equals(Object obj) 方法比较不相等,它们就不会被视为集合中的相同元素。

然而,哈希碰撞可能会影响基于哈希的集合的性能,因为它可能增加在集合中查找元素的时间复杂度。

4、在重写 hashCode() 方法时,有哪些注意事项?

在重写hashCode()方法时,应该确保在同一个Java应用程序执行期间,只要对象的equals比较中所用的信息没有被修改,那么对该对象多次调用hashCode方法必须始终如一地返回同一个整数。

如果两个对象通过 equals(Object obj) 方法比较是相等的,那么这两个对象的 hashCode() 方法必须产生相同的整数结果。

不要求如果两个对象通过 equals(Object obj) 方法比较是不相等的,那么调用这两个对象中任一对象的 hashCode() 方法必须产生不同的整数结果。但是,为不相等的对象产生不同整数结果可能会提高哈希表的性能。

三、抽象类和接口有什么区别?

抽象类和接口都不能够实例化,但是可以定义抽象和接口类型的引用。一个类如果继承了某个抽象类或者实现某个接口都需要对其中的抽象方法进行实现,否则该类仍然需要被声明为抽象类。

接口比抽象类更加抽象,因为抽象类中可以定义构造器,可以有抽象方法和具体方法,而接口中不能定义构造器而且其中的方法全部是抽象方法。

四、抽象类是什么?它和接口有啥区别?

接口用于规范,抽象类用于共性。声明方法的存在而不去实现它的类叫做抽象类。接口时抽象类的变体。在接口中,所有的方法都是抽象的。

五、讲讲类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数,字段,当new的时候,他们的执行顺序

在Java中,当使用new关键字创建一个类的实例时,会遵循一个特定的顺序来初始化对象。这个顺序涉及到父类和子类的静态数据、构造函数以及字段(成员变量)的初始化。下面是这个顺序的详细解释:

1、静态块和静态变量(父类到子类)

  • 首先,会初始化父类中定义的静态变量和静态初始化块(如果有的话),按照它们在代码中出现的顺序进行。
  • 然后,会初始化子类中定义的静态变量和静态初始化块(如果有的话),也是按照它们在代码中出现的顺序进行。
  • 需要注意的是,静态初始化只会在类被加载到JVM时发生一次,与创建类的实例数量无关。

2、实例变量(父类到子类)

  • 在创建类的实例时,会首先为父类中的实例变量分配内存并默认初始化(例如,数值类型变量初始化为0,对象引用初始化为null)。
  • 然后,会执行父类的非静态初始化块(如果有的话)。
  • 接着,会执行父类的构造函数,此时父类的实例变量可以被显式初始化。
  • 同样的过程会发生在子类上,但会在父类初始化之后进行。子类中的实例变量会被分配内存并默认初始化,然后执行子类的非静态初始化块(如果有的话),最后执行子类的构造函数。

3、构造函数(父类到子类)

  • 在创建对象时,构造函数的调用是遵循从父类到子类的顺序的。这意味着在子类的构造函数中,可以通过super()(显式或隐式)调用父类的构造函数,并且这个调用必须是子类构造函数中的第一条语句(除了注释和变量声明)。
  • 如果子类没有显式调用父类的构造函数,则会自动调用父类的无参构造函数(如果父类没有定义无参构造函数且子类没有显式调用其他构造函数,则会导致编译错误)。

综上所述,当使用new关键字创建类的实例时,执行顺序大致如下:

1、父类静态变量和静态初始化块(按出现顺序)。
2、子类静态变量和静态初始化块(按出现顺序)。
3、父类实例变量默认初始化。
4、父类非静态初始化块(如果有的话)。
5、父类构造函数。
6、子类实例变量默认初始化。
7、子类非静态初始化块(如果有的话)。
8、子类构造函数。

这个顺序确保了父类在子类之前被完全初始化,从而保证了继承体系中的正确性和稳定性。

六、Java 创建对象有几种方式?

  • new 创建新对象
  • 通过反射机制创建对象
  • 采用 clone 创建对象
  • 通过序列化机制
目录
相关文章
|
2天前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
7天前
|
存储 缓存 Oracle
Java I/O流面试之道
NIO的出现在于提高IO的速度,它相比传统的输入/输出流速度更快。NIO通过管道Channel和缓冲器Buffer来处理数据,可以把管道当成一个矿藏,缓冲器就是矿藏里的卡车。程序通过管道里的缓冲器进行数据交互,而不直接处理数据。程序要么从缓冲器获取数据,要么输入数据到缓冲器。
Java I/O流面试之道
|
4天前
|
存储 缓存 Java
大厂面试必看!Java基本数据类型和包装类的那些坑
本文介绍了Java中的基本数据类型和包装类,包括整数类型、浮点数类型、字符类型和布尔类型。详细讲解了每种类型的特性和应用场景,并探讨了包装类的引入原因、装箱与拆箱机制以及缓存机制。最后总结了面试中常见的相关考点,帮助读者更好地理解和应对面试中的问题。
21 4
|
4天前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
33 4
|
28天前
|
存储 安全 算法
Java面试题之Java集合面试题 50道(带答案)
这篇文章提供了50道Java集合框架的面试题及其答案,涵盖了集合的基础知识、底层数据结构、不同集合类的特点和用法,以及一些高级主题如并发集合的使用。
79 1
Java面试题之Java集合面试题 50道(带答案)
|
12天前
|
Java 关系型数据库 数据库
面向对象设计原则在Java中的实现与案例分析
【10月更文挑战第25天】本文通过Java语言的具体实现和案例分析,详细介绍了面向对象设计的五大核心原则:单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则帮助开发者构建更加灵活、可维护和可扩展的系统,不仅适用于Java,也适用于其他面向对象编程语言。
10 2
|
17天前
|
存储 Java 程序员
Java面试加分点!一文读懂HashMap底层实现与扩容机制
本文详细解析了Java中经典的HashMap数据结构,包括其底层实现、扩容机制、put和查找过程、哈希函数以及JDK 1.7与1.8的差异。通过数组、链表和红黑树的组合,HashMap实现了高效的键值对存储与检索。文章还介绍了HashMap在不同版本中的优化,帮助读者更好地理解和应用这一重要工具。
40 5
|
16天前
|
存储 Java
[Java]面试官:你对异常处理了解多少,例如,finally中可以有return吗?
本文介绍了Java中`try...catch...finally`语句的使用细节及返回值问题,并探讨了JDK1.7引入的`try...with...resources`新特性,强调了异常处理机制及资源自动关闭的优势。
17 1
|
25天前
|
Java 程序员
Java 面试高频考点:static 和 final 深度剖析
本文介绍了 Java 中的 `static` 和 `final` 关键字。`static` 修饰的属性和方法属于类而非对象,所有实例共享;`final` 用于变量、方法和类,确保其不可修改或继承。两者结合可用于定义常量。文章通过具体示例详细解析了它们的用法和应用场景。
25 3
|
28天前
|
Java
Java面试题之cpu占用率100%,进行定位和解决
这篇文章介绍了如何定位和解决Java服务中CPU占用率过高的问题,包括使用top命令找到高CPU占用的进程和线程,以及使用jstack工具获取堆栈信息来确定问题代码位置的步骤。
78 0
Java面试题之cpu占用率100%,进行定位和解决
下一篇
无影云桌面