Java面试题

JVM、 JRE、 JDK之间的关系 

     
JVM负责将Java字节码翻译成机器码,以便在特定平台上执⾏Java程序。
JDK是Java的开发⼯具包,它包含了开发和编译Java程序所需的⼯具,以及运⾏Java程序的JRE
JRE是Java应⽤程序运⾏的环境,它包含了运⾏Java程序所需的库、 Java虚拟机(JVM)以及其他⽀持⽂件。

简⽽⾔之, JVM提供了Java程序运⾏的虚拟环境, JDK是⽤于开发Java程序的⼯具包,⽽JRE是⽤于运⾏Java程序的
环境。在开发阶段,你需要JDK;⽽在部署和运⾏阶段,你只需要JRE。

Java有⼏种数据类型
  • 整型: byte 、 short、 int、 long (int默认)

  • 浮点型: float 、 double (double默认)

  • 布尔型: boolean

  • 字符型: char

对应的包装类为 Byte 、 Short 、 Integer 、 Long 、 Float 、 Double 、 Character 、 Boolean

基础数据类型的转换⽅式
  • ⾃动类型转换:⼩–>⼤ byte --> short --> int --> long --> float --> double
  • 强制类型转换:⼤–>⼩ ⼩类型 变量名 = (⼤类型) 值
     

注意:⾃增/⾃减运算符、复合赋值运算符底层做了优化,内部⾃动强制类型转换; 如:++, --, +=, -=, ......

基本类型和包装类型的区别


为了在对象环境中使⽤基本类型, Java提供了对应的包装类型。

  • 基本类型直接存储数据值,在栈上分配内存,占⽤固定的内存空间,
  • 包装类型是对象动态分配内存,在堆上创建,包含额外的信息,例如⽅法和字段,同时也占⽤更多的内存空间。

不过Java提供了⾃动装箱(autoboxing)和拆箱(unboxing)的功能,使得基本类型和包装类型之间的转换更加
⽅便。

什么是⾃动装箱和⾃动拆箱

⾃动装箱是指将基本类型转换为对应的包装类型,⽽⾃动拆箱是指将包装类型转换为对应的基本类型。这些转换通常由编译器⾃动处理。

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

注意:所有整型包装类对象之间值的标记,全部使用equals方法比较。

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

1.声明位置:

  • 成员变量:声明在类中,但在方法,构造函数或块之外。它们是类的一部分,因此可以被整个类的方法访问。
  • 局部变量:声明在方法,构造函数或块中。它们只在声明它们的方法,构造函数或块中可见。

2.作用域

  • 成员变量:具有类的整个实例的生命周期,可以在整个类中使用。
  • 局部变量:仅在声明它们的方法,构造函数或块中可见,并且只在该方法,构造函数或块的执行期间存在。

3.生命周期

  • 成员变量:随着对象的创建而创建,随着对象的销毁而销毁。
  • 局部变量:在声明时分配内存,在方法调用结束时销毁。

4.默认值

  • 成员变量:如果不显示初始化,会有默认值(例如,整数默认为0,对象引用默认为null等)。
  • 局部变量:不会自动初始化,必须在使用之前显示初始化。
静态变量是什么?

静态变量是被static关键字修饰的变量,被类的所有实例共享,无论一个类创建了多少个对象,它们都共享同一份静态变量。静态变量通过类名来访问。

值传递和引用传递的区别

参数传递⽅式主要有值传递和引⽤传递两种,但需要注意的是Java中的参数传递是始终按值传递的。

  1. 值传递:在值传递中,传递给函数的是实际参数的值的副本。当在函数内修改这个副本时,不会影响到原始值
  2. 引⽤传递:⽅法接收的直接是实参所引⽤的对象在堆中的地址,不会创建副本,对形参的修改将影响到实参。

但在Java中,虽然传递的是引⽤的值(对象的地址),但仍然是按值传递。实际上,传递的是引⽤的副本,因此在函数内对引⽤的修改会影响到原始的引⽤,但⽆法修改引⽤指向的对象。
类和对象

⾯向对象和⾯向过程有什么区别

⾯向对象编程(OOP)和⾯向过程编程是两种不同的编程范式。

  1. ⾯向对象会先抽象出具有状态、⾏为的对象,然后⽤对象执⾏⽅法的⽅式解决问题,强调封装、继承和多态,更容易扩展和维护,因为修改⼀个对象不会影响到其他对象,适合处理复杂的系统。
  2. ⾯向过程将系统视为⼀系列的过程或函数,通过调⽤这些过程来完成任务。强调的是算法和流程,如果需要修改⼀个过程,可能会影响到调⽤这个过程的其他地⽅,更适合简单的、线性的任务。
⾯向对象的三⼤特征

1.封装
为了提⾼代码的安全性,隐藏对象的内部细节,封装将对象的内部状态(字段、属性)隐藏起来,并通过定义公共的⽅法(接⼝)来操作对象,外部代码只需要知道如何使⽤这些⽅法⽽⽆需了解内部实现。
2.继承
允许⼀个类(⼦类)继承另⼀个类(⽗类)的属性和⽅法的机制。⼦类可以重⽤⽗类的代码,并且可以通过添加新的⽅法或修改(重写)已有的⽅法来扩展或改进功能,提⾼了代码的可重⽤性和可扩展性。
3.多态
多态是指相同的操作或⽅法可以在不同的对象上产⽣不同的⾏为,通过⽅法的重载和重写实现。多态允许以⼀致的⽅式处理不同类型的对象,提⾼了代码的灵活性。

说⼀说你对多态的理解

因为⼦类其实是⼀种特殊的⽗类,因此Java允许把⼀个⼦类对象直接赋给⼀个⽗类引⽤变量,⽆须任何类型转换,或者被称为向上转型,向上转型由系统⾃动完成。
当把⼀个⼦类对象直接赋给⽗类引⽤变量时,例如BaseClass obj = new SubClass();,这个obj引⽤变量的编译时类型是BaseClass,⽽运⾏时类型是SubClass,当运⾏时调⽤该引⽤变量的⽅法时,其⽅法⾏为总是表现出⼦类⽅法的⾏为特征,⽽不是⽗类⽅法的⾏为特征,这就可能出现:相同类型的变量、调⽤同⼀个⽅法时呈现出多种不同的⾏为特征,这就是多态。

编译时多态和运⾏时多态的区别

1.编译时多态
编译时多态也被称为静态多态或早期绑定。它是指在编译阶段,编译器就能够确定调⽤哪个⽅法,这是通过⽅法的重载来实现的。编译时多态主要依赖于⽅法的签名(⽅法名和参数列表),根据调⽤时提供的参数类型和数量来确定具体调⽤的⽅法。

package com.company;

public class Main {
    public int add(int a,int b) {
        return a+b;
    }
    public double add(double a,double b) {
        return a+b;
    }

    public static void main(String[] args) {
        Main example=new Main();
        int resultInt=example.add(2,3);//编译时确定调⽤add(int, int)
        double resultDouble=example.add(2.0,3.0);//编译时确定调⽤add(double, double)
    }
}

2.运⾏时多态

运⾏时多态也被称为动态多态或晚期绑定。指的是在程序运⾏时,根据实际对象的类型来确定调⽤的⽅法,这是通过⽅法的重写来实现的。运⾏时多态主要依赖于对象的实际类型,⽽不是引⽤类型。

package com.company;

class Animal {
    void makeSound() {
        System.out.println("sound");
    }
}

class Dog extends Animal {
    @Override
    void makeSound() {
        System.out.println("Bark");
    }
}


public class Main {
    public static void main(String[] args) {
        Animal myDog=new Dog();//运⾏时多态,myDog的实际类型是Dog 
        myDog.makeSound();//运⾏时确定调⽤Dog类的makeSound⽅法
    }
}

3.总结

  1. 编译时多态是通过⽅法的重载在编译阶段确定调⽤的⽅法。
  2. 运⾏时多态是通过⽅法的重写在程序运⾏时确定调⽤的⽅法,实现动态绑定。
接⼝和抽象类有什么区别

1.从定义上来说
接⼝是⼀种抽象类型,它定义了⼀组⽅法(⽅法签名)但没有实现任何⽅法的具体代码。接⼝中的⽅法默认是抽象的,且接⼝中只能包含常量(static final变量)和抽象⽅法。
抽象类是⼀个类,可以包含抽象⽅法和具体⽅法。抽象类中的抽象⽅法是没有实现的⽅法,⽽具体⽅法则包含实现代码。抽象类不能直接实例化,通常需要⼦类继承并实现其中的抽象⽅法。
2.继承
接⼝⽀持多继承,⼀个类可以实现多个接⼝
Java中不⽀持多继承,⼀个类只能继承⼀个抽象类。如果⼀个类已经继承了⼀个抽象类,就不能再继承其他类。
3.构造器
接⼝不能包含构造器,因为接⼝不能被实例化。类实现接⼝时,必须实现接⼝中定义的所有⽅法。抽象类可以包含构造器,⽤于初始化抽象类的实例。当⼦类实例化时,会调⽤⽗类的构造器。
4.访问修饰符
接⼝中的⽅法默认是public abstract的。接⼝中的变量默认是public static final的。抽象类中的抽象⽅法默认是protected的,具体⽅法的访问修饰符可以是public、protected或private。
5.实现限制
类可以同时实现多个接⼝,实现接⼝的类必须提供接⼝中定义的所有⽅法。
⼀个类只能继承⼀个抽象类,继承抽象类的⼦类必须提供抽象类中定义的所有抽象⽅法的实现。

构造⽅法能不能重写

构造⽅法不能重写。因为构造⽅法需要和类保持同名,⽽重写的要求是⼦类⽅法要和⽗类⽅法保持同名。如果允许重写构造⽅法的话,那么⼦类中将会存在与类名不同的构造⽅法,这与构造⽅法的要求是⽭盾的。

说⼀说你对Java访问权限的了解

在Java中,访问权限通过访问修饰符来控制。主要的访问修饰符有四个:public、protected、default(默认修饰符)和private。这些修饰符可以⽤于类、变量、⽅法和构造⽅法。

  • 修饰符public表示对所有类可⻅。⼀个类如果被声明为public,那么它可以被其他任何类访问。
  • 修饰符protected表示对同⼀包内的类和所有⼦类可⻅。⼦类可以访问⽗类中声明为protected的成员,⽽不管⼦类与⽗类是否在同⼀包中。
  • 如果没有使⽤任何访问修饰符(即没有写public、protected、private),则默认为包级别访问。这意味着只有同⼀包中的类可以访问。
  • 修饰符private表示对同⼀类内可⻅。私有成员只能在声明它们的类中访问。

 你对static关键字有哪些认识

static是Java中的关键字,⽤于声明静态成员(静态变量或静态⽅法)

1.静态变量
使⽤static关键字声明的变量称为静态变量,也叫类变量。它们属于类⽽不是实例,因此所有实例共享相同的静态变量。
2.静态⽅法
使⽤static关键字声明的⽅法称为静态⽅法。静态⽅法属于类⽽不属于实例,可以通过类名调⽤,⽽不需要创建类的实例。
3.静态代码块
使⽤static关键字声明的代码块称为静态代码块。它在类加载时执⾏,并且只执⾏⼀次。

4.静态内部类
在类中使⽤static关键字声明的内部类称为静态内部类
静态成员可以被类直接访问,⽽不需要创建类的实例。然⽽,静态成员⽆法直接访问⾮静态成员,因为⾮静态成员依赖于类的实例。

static和final有什么区别
  • static⽤于修饰成员时,该成员成为类级别的,⽽不是实例级别的。静态成员属于类,⽽不是属于类的实例。
package com.company;




public class Main {
   static  int  staticVariable;  //静态变量
   static  void staticMethod(){  //静态方法

   }

    public static void main(String[] args) {
        int value =Main.staticVariable;
        Main.staticMethod();
    }
}
  • 当final⽤于修饰变量、⽅法或类时,表示它是不可变的。对于变量,⼀旦赋值后就不能再修改;对于⽅法,表示⽅法不能被⼦类重写;对于类,表示类不能被继承。
package com.company;

final class  FinalClass{ //不可变的类
    
}


public class Main {
   final int  constantValue=42; //不可变的变量
   final void finalMethod(){  //不可变的方法
       
   }
}
final、finally、finalize的区别
  1. final就是不可变的意思,可以修饰变量、⽅法和类。修饰变量时,这个变量必须初始化,所以也称为常量。
  2. finally是异常处理的⼀部分,只能⽤在try/catch中,并且附带⼀个语句块表示这段语句⼀定会被执⾏,⽆论是否抛出异常。
  3. finalize是java.lang.Object中的⽅法,也就是每⼀个对象都有这个⽅法,⼀个对象的finalize⽅法只会调⽤⼀次,调⽤了不⼀定被回收,因为只有对象被回收的时候才会被回收,就会导致前⾯调⽤,后⾯回收的时候出现问题,不推荐使⽤。
方法的重载重写有什么区别

重载

  • ⽅法的重载指的是在同⼀个类中,可以有多个⽅法具有相同的名称,但是它们的参数列表不同(参数的类型、个数、顺序),具有以下特点。
  • ⽅法的返回类型可以相同也可以不同。
  • ⽅法的重载与⽅法的访问修饰符和返回类型⽆关。编译器根据⽅法的参数列表来区分不同的重载⽅法

重写

方法的重写指的是在⼦类中重新定义⽗类中已经定义的⽅法,⽅法名、参数列表和返回类型都必须相同。

  • 重写是实现多态的⼀种⽅式,提供了⼀种⼦类可以提供⾃⼰特定实现的机制。
  • 重写的⽅法不能⽐被重写的⽅法有更低的访问权限。
  • ⼦类⽅法不能抛出⽐⽗类⽅法更多的异常(⼦类⽅法可以不抛出异常或抛出⽗类异常的⼦类)。

总结

  • 重载是在同⼀个类中定义多个⽅法,⽅法名相同但参数列表不同。
  • 重写是在⼦类中重新定义⽗类中已有的⽅法,⽅法名和参数列表必须相同。
  • 重载与返回类型和访问修饰符⽆关,⽽重写要求⽅法签名相同。
  • 重载是编译时多态,⽽重写是运⾏时多态。

重载(overloading)是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。最常用的地方就是构造器的重载。

重写发生在子类和父类之间,重写方法返回值和型参都不能改变,与方法返回值和访问修饰符无关,即重载的方法不能根据返回类型进行区分。

深拷⻉和浅拷⻉有什么区别
  • 浅拷⻉:浅拷⻉创建⼀个新对象,然后将原对象的⾮静态字段复制到新对象。如果字段是基本数据类型,那么就复制其值;如果字段是引⽤类型,复制的就是引⽤⽽不是实际对象。
  • 深拷⻉:创建⼀个新对象,并递归复制原对象中的所有引⽤类型的字段指向的对象,⽽不是共享引⽤。因此,新对象和原对象中的引⽤类型字段引⽤的是两组不同的对象。
package com.company;

//浅拷⻉
class Person implements Cloneable {
    String name;
    Address address;

    @Override
    protected Object clone()throws CloneNotSupportedException {
        return super.clone();
    }
}

class Address {
    String city;
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1=new Person();
        person1.name="John";
        person1.address=new Address();
        person1.address.city="New York";
        Person person2=(Person)person1.clone();
        System.out.println(person1==person2);// false
        System.out.println(person1.address==person2.address);// true,引⽤对象共享
    }
}
package com.company;

import org.apache.commons.lang3.SerializationUtils;

import java.io.Serializable;

//深拷⻉
class Person implements Serializable {
    String name;
    Address address;
   public Person deepCopy(){

        return SerializationUtils.clone(this);
    }


}

class Address implements Serializable{
    String city;
}

public class Main {
    public static void main(String[] args)  {
        Person person1=new Person();
        person1.name="John";
        person1.address=new Address();
        person1.address.city="New York";
        Person person2=person1.deepCopy();
        System.out.println(person1==person2);// false
        System.out.println(person1.address==person2.address);// false,引⽤对象不共享
    }
}

总结:

  • 浅拷⻉复制对象,包括对象的引⽤,两者共享引⽤对象。
  • 深拷⻉复制对象及其引⽤的对象,两者不共享引⽤对象,即使引⽤对象也会被复制。
  • 在Java中,浅拷⻉通常通过clone⽅法实现,深拷⻉可以通过⼿动实现clone⽅法、序列化和反序列化,或者使⽤第三⽅库来实现。

 

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

人生如梦亦如幻唯心在

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值