面试问题总结
秋水 Lv4

2023.10.8 如何理解多态,多态是什么

定义:为了节省内存空间,而且代码更整齐美观,是指不同的对象,对相同的方法做出不同的相应。(就是对于父同一个方法,不同子类调用同一个方法出现不同的方法),

实现方法:父类或接口定义的引用变量可以指向子类或具体实现类的实例对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public interface Animal {
public void eat();
}


public class cat implements Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}

public class dog implements Animal {
@Override
public void eat() {
System.out.println("狗吃肉");
}
}

public class animalImp {
public static void main(String[] args) {
Animal animal = new cat();
animal.eat();
animal = new dog();
animal.eat();
}
}


(通过父类的对象调用不同子类引用的同一方法,父类既可以调用子类的不同形态,也可以调用自己的形态)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class AnimalFu {
public void eat(boolean bool){
System.out.println("动物在吃饭");
}
}

class DogTest extends AnimalFu{
@Override
public void eat(boolean bool) {
if (bool){
System.out.println("狗吃肉");
}else {
super.eat(true);
}
}
}

class CatTest extends AnimalFu{
@Override
public void eat(boolean bool) {
if (bool){
System.out.println("猫吃鱼");
}else {
super.eat(true);
}
}
}

10.11Java中对象和对象引用的区别,引用,指向是什么意思

**基本数据类型 ** byte char short int long double boolean float

基本数据类型存放在栈中,存放的是具体的数值

引用数据类型 有两块存储空间,一个在栈中,一个在堆中。 堆中存放对象实体(使用New关键字,在堆中开辟一块新的内存空间),栈中存放对象在堆中的所在位置的首地址,引用类型变量类似于c/c++中的指针

引用 一个引用类型变量(栈中的一块内存空间)保存了一个该类型对象在堆中所在位置的首地址,也称作一个变量指向了一个该类型的对象,通过这个变量就可以操作对象中的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
   class Person {
String name;
int age;
}

public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "张伟";
p1.age = 17;
System.out.println("姓名:" + p1.name + ",年龄:" + p1.age);
Person p2 = new Person();
p2.name = "李丽";
p2.age = 12;
System.out.println("姓名:" + p2.name + ",年龄:" + p2.age);

p1 = p2;
p1.age = 13;
System.out.println("姓名:" + p1.name + ",年龄:" + p1.age); //姓名:李丽,年龄:13
System.out.println("姓名:" + p2.name + ",年龄:" + p2.age); //姓名:李丽,年龄:13
}
}

avater

多个对象在堆内存中,有着不同的内存划分,

当多个引用指向堆中同一块内存空间(p1=p2 即一个对象的引用赋值给另一个对象,两个变量所记录的地址是一样的,此时p1指向p2所指向的对象,不再指向p1原本的对象),只要通过其中任何一个引用修改了对象的数据,随后,无论使用那一个引用获取数据,得到的都是修改后的值,因此最后两行打印结果是一样的。

所以对于引用数据类型,如 Person P =new Person();将p简单理解为对象名是不合适的。

10.11浅拷贝与深拷贝

浅拷贝 :只会复制指向某个对象的指针(引用) ,而不会复制对象本身,新旧对象还是共享同一块内存

深拷贝:深拷贝会另外创建一个一摸一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象,是值而不是引用。是递归拷贝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package clone;

class Address implements Cloneable{
String city;

public Address(String city) {
this.city = city;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class Person implements Cloneable{
String name;
Address address;

public Person(String name, Address addresss) {
this.name = name;
this.address = addresss;
}
@Override
public Object clone() throws CloneNotSupportedException {
//执行浅拷贝
Person clonePerson = (Person) super.clone();

//对内部引用对象进行深拷贝
clonePerson.address=(Address) address.clone();

return clonePerson;
}
}
public class DeepCopy {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("New York");
Person person1 = new Person("Alice", address);
// 克隆对象
Person person2 = (Person) person1.clone();

System.out.println("Before modification:");
System.out.println("person1: " + person1.name + ", " + person1.address.city);
System.out.println("person2: " + person2.name + ", " + person2.address.city);

// 修改person2的地址,不会影响到person1,因为它们不共享引用对象
person2.address.city = "Los Angeles";

System.out.println("After modification:");
System.out.println("person1: " + person1.name + ", " + person1.address.city);
System.out.println("person2: " + person2.name + ", " + person2.address.city);

}
}

10.12两个对象的值相同,但却可以有不同的hashcode

首先 equals的方法具有四个特性

  • 自反性 x.equals(x)== true

  • 对称性x.equals(y) 与y.equals(x)相同

  • 传递性 x.equals(y) == true y.equals(z)== true 则 x.equals(z)

  • 一致性 对于任意非同的x和y,多次调用的结果是一致的

    哈希码相同,两个对象不一定相同。

    两个对象相同,他们的哈希码一定相同。

    为什么要重写equals方法为什么要重写hashcode方法

    hashcode 是一个整数值,是根据对象的内容计算出来的,主要用于哈希表,可以实现快速查找存在其中的对象,hashcode可以帮助我们明确对象在哈希表中的位置。

    为什么重写equals方法

    默认情况下,java中的equals方法比较的对象的引用,如果不重写equals方法,那么对于两个不同的对象,即使他们的内容相同,调用equals方法也会返回false。

    为什么要重写hashcode方法

    是因为在使用散列数据结构,比如哈希表,我们希望相同的对象具有相同的哈希码,在java中哈希表使用哈希码来确定存储对象的位置,如果两个相等的对象具有不同的哈希码,那他们将被存储在哈希表中不同的位置,导致无法正确查询。

    代码实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    package equals;

    import java.util.Objects;

    class Person{
    private String name;
    private int age;

    public Person(String name, int age) {
    this.name = name;
    this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
    if (this == obj) return true; //如果是同一个地址,则相等
    if (obj == null || getClass()!= obj.getClass())
    return false;//如果对象为空或者不是同一个实例的对象,则返回false
    Person person = (Person) obj;
    return age == person.age && Objects.equals(name,person.name);
    }
    @Override
    public int hashCode() {
    return Objects.hash(name,age);
    }


    }
    public class TestEquals {
    public static void main(String[] args) {
    Person person1 = new Person("Alice", 30);
    Person person2 = new Person("Alice", 30);
    // 比较两个对象是否相等
    System.out.println("person1 equals person2: " + person1.equals(person2));

    // 打印哈希码值
    System.out.println("Hashcode of person1: " + person1.hashCode());
    System.out.println("Hashcode of person2: " + person2.hashCode());
    }
    }

    10.12 ==与equals的区别

    equals与==的最大区别是一个是方法,一个是运算符。

    ==: 如果比较的对象是基本数据类型,则比较的是数值是否相等,如果比较的是引用数据类型,则比较的是对象的地址值是否相等。

    equals(): 用来比较两个对象的内容是否相等(string)

    注意 equals方法不能用于基本数据类型的变量,如果没有对equals方法重写,则比较的是引用类型变量所指向对象的地址。

10.12 重载与重写的区别,如何进行区分

重写是实现了在运行时多态,重载实现了编译时多态。

重载: 要求代码方法名,参数不相同 ,返回值类型可以相同,也可以不同。

    1. 重载是编译时多态
    1. 重载要求方法名相同,参数列表不同(参数类型,参数个数,甚至是参数顺序)
    1. 重载的时候,返回值类型可以相同也可以不同,无法以返回值类型判别作为重载。

重写:1. 发生在父类与子类之间,是运行时多态。

​ 2. 方法名,参数列表,返回类型(除过子类中方法的返回类型是父类中返回类型的子类,必须相同

代码理解如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Parent {
public Number getValue() {
// 父类方法返回一个Number对象
return new Integer(10);
}
}

class Child extends Parent {
// 正确的方法重写,返回类型是父类方法返回类型的子类(Integer是Number的子类)
@Override
public Integer getValue() {
// 子类方法返回一个Integer对象
return new Integer(20);
}
}

​ 3 访问修饰符的限制一定要大于被重写方法的访问修饰符

​ 4 重写方法一定不能抛出新的检查异常或者比被重写方法申请更加宽泛的检查型异常。

10.13 关于JDBC中的 Bean层理解,DAO层理解,DAOIMP理解,Servlet层理解

Bean层(Model层);定义了用户的实体,或者说应用程序的实体,用于表示程序的数据结构。

DAO层:主要用于同数据库进行交互的层,包含了与数据库进行的操作接口。

DAO实现层:实现层主要是用于实现DAO层的具体操作,即实际与数据库进行交互的层

Servlet层(Controller层):用于处理用户请求,调用DAO层和Bean层的方法,并将结果传递给前端(View层)。

10.13 关于DAO层为什么要写DAO的imp,以及里面的内容比如baseDAO和UserDAO是怎么理解的

为什么要写DAO层

首先是独立的数据访问逻辑,将数据访问从业务逻辑中分离出来,形成一个独立的模块,这样当数据库结构,查询逻辑发生变化时,只需要修改DAO层而不需要改变业务逻辑层。

为什么DAO层一定要写DAOIMP,首先这个是不必要的,但是写出来我们可以解决耦合性的问题

1 在一些简单的项目中,数据库交互逻辑比较相对简单,可以省去,但是如果在很多企业级应用中,我们可以使用dao层设计模式,dao层设计模式将数据访问逻辑与数据库操作相分离,使得代码更有可维护性,提高了可扩展性,当需求变化时,只需要修改dao层的代码,而不影响到其他模块。

10.13 关于代理设计模式对于代码的好处,然后简单实现一下

  • Post title:面试问题总结
  • Post author:秋水
  • Create time:2023-10-08 13:27:51
  • Post link:tai769.github.io2023/10/08/面试问题总结/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.