1.面向对象基础面试题小结

面向对象基础

1 面向对象三大特点

  封装、继承、多态

  面向过程是将解决问题的过程拆分为一个个方法执行;面向对象是先抽象出对象由对象执行方法的方式解决问题

1)封装:将一个对象的属性封装在对象内部不允许外部对象直接访问对象内部信息

2)继承:不同类型对象,相互之间经常有一定共同点。具体表现为子类继承父类

  1. 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法)。但是父类的私有属性和方法,子类只能拥有,无法访问
  2. 子类可以拥有自己的属性和方法,即子类可对父类进行拓展。
  3. 子类可以自己方式实现父类方法(子类可重写父类方法)

3)多态:一个对象具有多种状态,即提供一个统一的接口使不同类型的对象可以实现同一个操作或方法。具体表现为父类引用指向子类实例。如Map<Integer, Integer>map = new HashMap<>();

  父类引用是指使用父类类型声明一个变量map(map可引用父类实例或子类实例),父类引用存放在栈中子类实例是指子类创建的具体对象如new HashMap(),子类实例存放在内存中。故声明的父类引用map指向子类实例new HashMap。

  对象实例的相等表现为内存存放的内容是否相同;引用类型的相等表现为指向的内存地址是否相同

  特点:

  1. 对象类型引用类型之间存在(继承)类/(实现)接口的关系;
  2. 父类引用可访问子类对象中继承父类的属性和方法,但父类无法访问子类特有的属性和方法
  3. 编译器只识别父类引用不考虑实际引用对象运行期间根据实际引用对象确定具体执行方法和属性;
  4. 如果子类重写父类方法,真正执行的是子类覆盖的方法,若子类没有覆盖父类方法,执行的是父类的方法。

2 构造方法

1)如果一个类没有声明构造方法,程序能正确运行吗

  可以的,类的构造方法是为了初始化对象。类即使没有声明构造方法,也会有默认的不带参数的构造方法。

2)构造方法有什么特点?是否可被@override

  特点:

  1. 名字与类相同
  2. 无返回值,不能使用void声明构造函数
  3. 生成类的对象时自动执行,无需调用

  构造方法不能被@override,但能被重载,如一个类中可以有多个构造函数

3 重写和重载的区别

  重写是指子类将父类本身的方法重写,在方法名返回值类型参数列表相同的情况下,子类对父类方法体进行重写。子类方法的访问修饰符不能低于父类。

  访问修饰符等级排序:public > protected(同一包或不同包的子类都能访问) > default(同一包的所有类都能访问) > private(只能在当前类中被访问)

  重载是指同一个类中,同名的方法有不同的参数列表参数类型参数个数参数顺序等不同)。

4 接口和抽象类的区别

  1)共同点

  1. 不能被实例化
  2. 都能拥有抽象方法
  3. 都拥有默认方法(可理解为实现方法)Java8中通过default关键字在接口中定义默认方法),如MybatisPlus中Mapper接口中默认的增删改查方法,使得其存在部分实现方法,提高代码复用

  Collection接口中的默认方法stream()获取流

  2)不同点

  1. 接口是对类的行为进行约束,即实现某接口的类型需要实现接口所有的方法,从而具备对应的行为能力;抽象类主要用于代码复用,强调所属关系(即抽象类和子类之间的继承关系,子类是抽象类的具体表现);
  2. 一个类只能继承一个父类,但可以实现多个接口;
  3. 接口中成员变量只能是public static final 类型的,不能被修改且必须有初始值;抽象类中成员变量默认是default,可在子类中重新定义可被重新赋值
  4. 接口中无构造函数抽象类中可有构造函数

  final修饰的变量可称为常量,为保证常量的值在后续代码中不被修改,Java编译器要求其在声明时构造方法时进行初始化赋值,否则会导致编译报错

 正确方式

1)声明时赋值

2)构造方式赋值

5 动态绑定和静态绑定

  1. 动态绑定:在运行时才能确定调用哪个函数或方法。当引用变量是一个基类类型,实际引用类型是派生类类型,动态绑定会根据对象实际类型调用对应方法或函数。动态绑定的作用是实现多态
  2. 静态绑定:编译时即可确定调用哪个函数或方法。如一个类中有一个静态方法,在编译时就可确定调用该方法的地址,因此静态方法在编译时就确定的

6 深拷贝、浅拷贝、引用拷贝

浅拷贝:在堆上创建一个新的对象,如果对象内部是引用类型,则复制内部对象的引用地址,拷贝对象和原对象共用同一个内部对象;当其中一个对象修改了内部数据另一个对象也会受到影响

定义一个Address类和Person类实现Cloneable接口,重写clone()方法实现浅拷贝。

package com.ku.test.basic;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class Person implements Cloneable{
    private Address address;
    @Override
    public Person clone() throws CloneNotSupportedException {
        Person person = (Person)super.clone();
        return person;
    }
}

package com.ku.test.basic;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class Address implements Cloneable{
     private String name;

     @Override
     public Address clone() throws CloneNotSupportedException {
         return (Address)super.clone();//强转为Address类型
     }
}

package com.ku.test.basic;

public class TestCopy {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person(new Address("武汉"));
        Person personCopy = person.clone();
        System.out.println(person.getAddress() == personCopy.getAddress());
    }
}
浅拷贝

深拷贝:完全复制整个对象,包含对象所包含的内部对象;拥有各自的对象以及内存地址,两个对象相互独立。

package com.ku.test.basic;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
/**
 * 深拷贝
 */
public class Person1 implements Cloneable{
    private Address address;

    @Override
    public Person1 clone() throws CloneNotSupportedException {
        Person1 person1 = (Person1) super.clone();
        person1.setAddress(person1.getAddress().clone());
        return person1;
    }
}
深拷贝
深拷贝

引用拷贝:两个不同的引用指向同一个对象

7 “==”和equals方法的区别

  Java参数传递方式是基于值传递(引用拷贝,两个引用指向同一个对象),不管是基本数据类型还是引用数据类型,==比较的都是值,不过引用类型变量存储的值是内存地址

区别:

  • 对于基本数据类型,==比较的是值;
  • 对于引用数据类型,==比较的是内存地址;

equals方法不能用于基本数据类型的比较,只能用于引用数据类型的比较。equals方法是Object类中的方法,Object是所有类的直接父类或间接父类,因此每个类中都存在equals方法。

public boolean equals(Object obj){
    return   (this == obj);
}

equals存在两种使用情况:

  • 类如果没有重写equals方法,那么equals方法相当于==,比较的是两个对象的内存地址
  • 类如果重写了equals方法,那么equals方法比较的是对象的值

创建String类型的对象时,虚拟机会在常量池中查找是否存在值与创建的值相同的对象;如果存在将其赋予当前引用,如果不存在则在常量池中创建一个新的String对象

public boolean equals(Object anObject) {
    if (this == anObject) {//地址相等则值相等,引用拷贝
        return true;
    }
    if (anObject instanceof String) {//如果每个字符相等则值相等
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

String中重写的equals方法
String中重写的equals方法

8 hashcode()方法

hashcode()的作用获取哈希码(int 整形),也被称为散列码。哈希码是确定对象在哈希表中的索引位置

   hashcode()方法是Object中的本地方法,即所有类都存在hashcode()方法。

  散列表存储的是键值对(key-value),特点是能够根据键快速检索出值。其中利用到了哈希码

为什么要拥有hashcode()方法呢?

  以HashSet检查重复为例,当对象加入HashSet时,HashSet会计算对象的hashcode判断对象插入的位置,同时也会将其与已存在对象的hashcode比较。如果不相等,则根据hashcode将对象散列到HashSet中;如果存在重复的hashcode,那么通过equals方法检查hashcode相等的两个对象是否相同,若相同则Hash不会让其加入;若不同则散列到其他位置。由于equals方法是通过重写来实现对象值的比较,而对象值的比较是需要比较对象每个成员变量的值;若成员变量较多或比较逻辑较复杂,那么equals方法开销较大。这样大大减少equals()方法比较的次数,提高了执行速度

  1. 两个对象的hashcode不相等,两个对象一定不相等;
  2. 两个对象的hashcode相等,两个对象不一定相等(哈希冲突)
  3. 两个对象的hashcode相等且equals方法返回true,那么两个对象相等

为什么重写equals()方法一定要重写hashcode()方法?

  1. 如果两个对象相等,那么他们的hashcode一定相等。如果两个对象的equals方法返回true,那么两个对象的hashcode相等。
  2. 如果重写equals方法()时,没有重写hashcode方法,则equals()方法判断相等的对象,它们的hashcode不相等

9 字符串常量池

  字符串常量池是JVM提升性能减少内存消耗针对字符串(String类)专门开辟的一块区域,目的是避免字符串的重复创建

 String s = new String("abc")会创建几个字符串对象?

  • 如果创建的字符串在字符串中不存在指向它的引用,那么首先在字符串常量池中创建一个指向该字符串的引用,然后在堆中通过new String()创建一个对象。一共创建两个对象。
  • 如果创建的字符串在字符串常量池中存在指向它的引用,那么new String()会在堆中创建一个对象。

 String 中intern()方法的作用

intren()方法是一个native(本地)方法,作用是将指定字符串对象的引用保存在字符串常量池中,分为两种情况:

  1. 如果字符串常量池中保存了指定字符串的引用,则直接返回
  2. 如果字符创常量池中没有保存指定字符串的引用,那么在字符串常量池中创建一个指向指定字符串的引用并返回

参考链接

Java基础常见面试题总结(中) | JavaGuide(Java面试 + 学习指南)

posted @ 2023-11-17 09:15  求知律己  阅读(48)  评论(0编辑  收藏  举报