java面试知识点复习01(基础知识)

177 阅读9分钟

⚠️ 注意:基本数据类型存放在栈中是一个常见的误区! 基本数据类型的存储位置取决于它们的作用域和声明方式。如果它们是局部变量,那么它们会存放在栈中;如果它们是成员变量,那么它们会存放在堆/方法区/元空间中。

在 Java 中,包装类(例如 IntegerCharacterByteShortLongBoolean)具有缓存机制,用于提高性能并减少内存使用。两种浮点数类型的包装类 Float,Double 并没有实现缓存机制。以下是有关包装类缓存机制的详细信息:

包装类缓存

1. 缓存机制概述

  • 目的:为了避免频繁创建相同值的包装类对象,Java 提供了一个缓存机制,特别是在小范围内的值(例如从 -128 到 127 的 Integer)。
  • 效果:当创建的包装类值在缓存范围内时,返回的是已存在的对象引用,而不是新创建一个对象。这样可以节省内存并提高性能。

2. 具体实现

  • Integer

    • Java 中的 Integer 类缓存了从 -128 到 127 的整数。
    • 例如,Integer a = 100;Integer b = 100; 将引用同一个对象。
    Integer a = 100;
    Integer b = 100;
    System.out.println(a == b); // 输出 true
    
  • ByteShortCharacter

    • 这些类也有类似的缓存机制,Byte 缓存范围是 -128 到 127,Short 是 -128 到 127,Character 是 0 到 127。
  • Long

    • Long 类缓存的范围是 -128 到 127,但由于其范围更大,可能会影响性能。
  • Boolean

    • Boolean 只缓存两个值:Boolean.TRUEBoolean.FALSE

3. 不在缓存范围内的值

  • 对于不在缓存范围内的值,Java 会创建新的对象。
Integer c = 200;
Integer d = 200;
System.out.println(c == d); // 输出 false,因为 c 和 d 是不同的对象

考点

Integer i1 = 40; //缓存对象
Integer i2 = new Integer(40);//自己new一个 
System.out.println(i1==i2); //

输出false Integer i1=40 自动装箱 。等价于Integer.valueOf(40)

  • Integer.valueOf()源码如下 image.png
  • 🎉对于引用类型 == 比较的是对象的内存地址 对于基本数据类型 则比较的是值
  • 🏍对于引用类型(equals没有被重写)equals比较的是地址 对于重写过equals方法的类,比如String 则 比较的是值
  • 总结 如果一个类没有重写 equals() 方法,那么它将继承 Object 类中的 equals() 实现。默认情况下,Object 类的 equals() 方法是基于引用的比较,也就是说,它检查两个对象是否指向同一个内存地址。

Objetc类 equals源码 image.png

image.png

image.png

装箱-拆箱

  • 装箱:将基本类型用它们对应的引用类型包装起来;
  • 拆箱:将包装类型转换为基本数据类型;

装箱其实就是调用了 包装类的valueOf()方法,拆箱其实就是调用了 xxxValue()方法。

  • Integer i = 10 等价于 Integer i = Integer.valueOf(10)
  • int n = i 等价于 int n = i.intValue();

变量

成员变量与局部变量的区别

  • 语法形式:从语法形式上看,成员变量是属于类的,而局部变量是在代码块或方法中定义的变量或是方法的参数;成员变量可以被 public,private,static 等修饰符所修饰,而局部变量不能被访问控制修饰符及 static 所修饰;但是,成员变量和局部变量都能被 final 所修饰。
  • 存储方式:从变量在内存中的存储方式来看,如果成员变量是使用 static 修饰的,那么这个成员变量是属于类的,如果没有使用 static 修饰,这个成员变量是属于实例的。而对象存在于堆内存,局部变量则存在于栈内存。
  • 生存时间:从变量在内存中的生存时间上看,成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动生成,随着方法的调用结束而消亡。
  • 默认值:从变量是否有默认值来看,成员变量如果没有被赋初始值,则会自动以类型的默认值而赋值(一种情况例外:被 final 修饰的成员变量也必须显式地赋值),而局部变量则不会自动赋值。

重载-重写

特性重载(Overload)重写(Override)
定义在同一个类中,方法名相同但参数列表不同(参数类型、个数或顺序)。在子类中,方法名、参数列表和返回类型与父类方法完全相同,重新实现父类方法。
作用范围同一个类中。子类和父类之间。
参数列表必须不同(参数类型、个数或顺序)。必须相同。
返回类型可以不同。必须相同(或为父类方法返回类型的子类型)。
访问修饰符可以不同。不能比父类方法更严格(例如,父类是protected,子类不能是private)。
异常抛出可以抛出不同的异常或不抛出异常。不能抛出比父类方法更多的检查异常(可以抛出更少或不抛出)。
静态方法可以重载静态方法。不能重写静态方法(静态方法属于类,不属于实例)。
绑定方式编译时多态(静态绑定)。运行时多态(动态绑定)。
目的提供多个方法实现,处理不同类型或数量的参数。子类重新定义父类方法的行为,实现多态性。
示例```java```java
class A {class A {
void show(int a) { }void show() { System.out.println("A"); }
void show(String s) { } // 重载}
}class B extends A {
@Override
void show() { System.out.println("B"); } // 重写
}
```

关键区别总结

  1. 重载:同一个类中,方法名相同但参数列表不同,用于处理不同类型或数量的参数。
  2. 重写:子类重新定义父类方法,方法名、参数列表和返回类型必须相同,用于实现多态性。

面向对象

  • 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,涉及到访问修饰符。
  • 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
  • 子类可以重写父类的方法

在Java中,对象类型引用类型是两个重要的概念,它们描述了对象和变量之间的关系。以下是它们的详细解释:

1. 对象类型(Object Type)

  • 定义:对象类型是指对象实际所属的类或类型。

  • 特点

    • 对象类型是在运行时确定的,由new关键字创建的对象决定。
    • 对象类型决定了对象的行为和属性(即对象可以调用哪些方法和访问哪些字段)。
  • 示例

    Animal myDog = new Dog(); // 对象类型是 Dog
    

    在这里,myDog的实际对象类型是Dog,而不是Animal


2. 引用类型(Reference Type)

  • 定义:引用类型是指变量声明时的类型,用于指向对象的引用。

  • 特点

    • 引用类型是在编译时确定的,由变量的声明类型决定。
    • 引用类型决定了变量可以访问哪些方法和字段(即编译时的类型检查)。
    • 引用类型可以是类、接口、数组等。
  • 示例

    Animal myDog = new Dog(); // 引用类型是 Animal
    

    在这里,myDog的引用类型是Animal,因此编译器会认为myDog是一个Animal类型的变量。


多态特点

  • 对象类型和引用类型之间具有继承(类)/实现(接口)的关系;
  • 引用类型变量发出的方法调用的到底是哪个类中的方法,必须在程序运行期间才能确定;
  • 多态不能调用“只在子类存在但在父类不存在”的方法;
  • 如果子类重写了父类的方法,真正执行的是子类重写的方法,如果子类没有重写父类的方法,执行的是父类的方法。

深浅拷贝

详见 Java基础常见面试题总结(中) | JavaGuide

1. 浅拷贝(Shallow Copy)

  • 定义:浅拷贝会创建一个新对象,并将原对象的所有字段复制到新对象中。如果字段是基本类型,则直接复制值;如果字段是引用类型,则复制引用(即新对象和原对象共享同一个引用类型字段)。

  • 特点

    • 新对象和原对象的引用类型字段指向同一个内存地址。
    • 修改其中一个对象的引用类型字段会影响另一个对象。
  • 实现方式

    • ✔默认的clone()方法(需要实现Cloneable接口)。
    • 通过构造函数或工厂方法复制字段。
      代码举例
 class Address implements Cloneable{
    private String name;

    public Address(String name) {
        this.name = name;
    }
    @Override
    public Address clone() {
        try {
            return (Address) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}

 class Person implements Cloneable {
    private Address address;

    public Person(Address address) {
        this.address = address;
    }
    public Address getAddress() {
        return address;
    }
    @Override
    public Person clone() {
        try {
            Person person = (Person) super.clone();
            return person;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}
public class test {
    public static void main(String []args){
        Person person1 = new Person(new Address("武汉"));
        Person person1Copy = person1.clone();
   System.out.println(person1.getAddress() == person1Copy.getAddress());// true 同一个引用
    }

    public static void printArray(String name,int... numbers) {
        System.out.println("name is "+name);
        for (int number : numbers) {
            System.out.print(number + " ");
        }
    }
}

2. 深拷贝(Deep Copy)

  • 定义:深拷贝会创建一个新对象,并递归地复制原对象的所有字段。如果字段是引用类型,则会创建该字段的新对象(而不是复制引用),从而确保新对象和原对象完全独立。

  • 特点

    • 新对象和原对象的引用类型字段指向不同的内存地址。
    • 修改其中一个对象的引用类型字段不会影响另一个对象。
  • 实现方式

    • 手动实现clone()方法,递归调用引用类型字段的clone()方法。
    • 🐛使用序列化和反序列化(如ObjectOutputStreamObjectInputStream)。
    • 使用第三方库(如Apache Commons Lang的SerializationUtils.clone())。

字符串常量池

Java 中,使用 String s1 = new String("abc"); 这行代码会创建两个对象:

  1. 字符串常量对象"abc" 是一个字符串字面量,它会被存储在字符串常量池中。这个对象是自动创建的,在程序运行时,字符串常量池会确保每个字面量只存在一个实例。
  2. 字符串实例对象new String("abc") 会使用 new 关键字显式地创建一个新的 String 对象。这个对象是在堆内存中创建的,与字符串常量池中的 "abc" 是两个不同的对象。

java异常

image.png