java八股总结

  1. Java的特点:
  • 平台无关性:Java编译器将源代码编译成字节码,字节码可以在任何安装了Java虚拟机(JVM)的系统上运行;
  • 面向对象:OOP特性使得代码更易于维护和重用;
  • 内存管理:Java有自己的垃圾回收机制,自动管理内存和回收不再使用的对象(具体见问题34),开发者不需要手动管理内存,减少了内存泄漏和其他内存相关的问题。
  1. Java为什么是跨平台的?
  • 主要依赖于JVM
  • 编写的Java源码编译后会生成一种.class文件(字节码文件),JVM就是负责将字节码文件翻译成特定平台下的机器码然后运行;
  • 跨平台的是Java程序,编译的字节码不同平台是一样的,但是翻译的机器码是不一样的,JVM是用C/C++开发的,是编译后的机器码,不能跨平台
  1. JVM、JDK、JRE三者关系
  • JVM是java虚拟机,是java程序运行的环境,主要负责将java字节码解释或编译成机器码,并执行程序。提供了内存管理、垃圾回收、安全性等功能,使得java程序具备跨平台性;
    主要工作是解释自己的指令集(即字节码),并映射到本地的CPU指令集和OS的系统调用。
  • JDK是java开发工具包,是开发java程序所需的工具集合,包含了JVM、编译器(javac)、调试器(jdb)等开发工具,以及一系列的类库。JDK提供了开发、编译、调试和运行java程序所需的全部工具和环境;
  • JRE是java运行时环境,是java程序运行时所需的最小环境,包含了JVM和一组java类库,用于支持java程序的执行。JRE不包含开发工具,只提供java程序运行所需的运行环境。
  1. Java的编译性和解释性
  • 编译性:Java源代码首先被编译成字节码,JIT会把编译后的机器码保存起来,以备下次使用;
  • 解释性:JVM中一个方法调用计数器,当累计计数大于一定值的时候,就使用JIT进行编译生成机器码文件;否则就是用解释器进行解释执行,字节码也是经过解释器进行解释运行的。
  1. 编译型语言和解释型语言
  • 编译型语言:在程序执行之前,整个源代码会被编译成机器码或字节码,生成可执行文件。执行时直接运行编译后的代码,速度快,但是跨平台性较差。(因为机器码不具备跨平台性,不同平台编译后的机器码不同);
  • 解释型语言:在程序执行时,逐行解释执行源代码,不生成独立的可执行文件。通常由解释器动态解释并执行代码,跨平台性好,但是执行速度相对较慢;
  • 典型的编译型语言:C/C++,典型的解释型语言:Python/JavaScript
  1. Java和Python的区别
  • Java是一种已编译的编程语言,Java编译器将源代码编译为字节码,而字节码由JVM执行;
  • Python是一种解释语言,会在执行程序的同时进行翻译
  1. 八种基本的数据类型
    Java支持的数据类型分为两类:基本数据类型和引用数据类型
    基本数据类型共8中,分3类
  • 数值型:整数类型(byte、short、int[封装类为Integer]、long)和浮点类型(float、double);
  • 字符型:char(封装类为Character);
  • 布尔型:boolean
  1. Int和long的字节数和位数
  • int类型是32位(bit),占4个字节(byte),有符号整数类型,取值范围是-2^31 到 2^31-1;
  • long类型是64位(bit),占8个字节(byte),有符号整数类型, -2^63 到 2^63 -1 。
  1. 数据类型转化方式
  • 自动类型转换(隐式转换):目标类型范围>源类型,Java会自动将源类型转换成目标类型;
  • 强制类型转换(显式转换):当目标类型范围<源类型,需要使用强制类型转换将源类型转成目标类型,可能会导致数据丢失或溢出:
    目标类型 变量名=(目标类型)源类型;
  • 字符串转换:Java提供了将字符串表示的数据转换为其他类型数据的方法
    Eg:将字符串转换成整型: Integer.parseInt()、将字符串转换成浮点型:Double.parseDouble()
  • 数值之间的转换:Java提供了一些数值类型之间的转换方法:将整型转成字符型、将字符型转成整型等,通过类型的包装类来实现Eg:Character类、Integer类
  1. 为什么用bigDecimal不用double
    因为Decimal可以确保精确的十进制数值计算,避免了使用double可能出现的舍入误差。

  2. 装箱和拆箱

  • 装箱(Boxing)和拆箱(Unboxing)是将基本数据类型和对应的包装类之间进行转换的过程;
    Integer i = 10 ;// 装箱,将基本类型转化为相应的包装类类型
    int n = i ;//拆箱,将包装类类型转换成相应的基本类型
  • 自动装箱主要发生在两种情况:赋值时、方法调用的时候;
  • 自动装箱的弊端:在循环中进行自动装箱,会创建多余的对象影响程序的性能。
  1. Integer
  • Integer对应的是int类型的包装类,就是把int类型包装成Object对象,对象封装可以把属性(数据)和处理这些数据的方法结合在一起;
  • Java中绝大部分方法或类都是用来处理类类型对象,比如ArrayList集合类就只能使用类作为他的存储对象;
  • Java中,泛型只能使用引用类型,不能使用基本类型;
  • Java中,基本类型和引用类型不能直接进行转换,必须使用包装类实现,Eg:将一个int类型的值转成String类型,必须先将其转成Integer类,再toString();
  • Java集合只能存储对象,不能存储基本数据类型,如果要将int类型的数据存储在集合中,必须使用Integer包装类。
  1. Integer和int相比的优点
  • 基本类型和引用类型:int是一种基本数据类型,是预定义的,不需要实例化就可使用,这意味着存储一个整数时,不需要任何额外的内存分配;Integer是一种引用类型,需要通过实例化对象才能使用,必须为对象分配内存。所以基本数据类型的操作通常比相应的引用类型快;
  • 自动装箱和拆箱(更方便):Integer作为int的包装类,可以实现自动装箱和拆箱,这使得Java程序员可以更加方便的进行数据类型转换;
  • 空指针异常(更安全):int变量可以直接赋值为0,Integer变量必须通过实例化对象来赋值,对一个未经初始化的Integer变量进行操作,会出现空指针异常,因为null值无法进行自动拆箱。
  1. 为什么需要保留int类型
  • 包装类是引用类型,对象的引用和对象本身是分开存储的,对于基本数据类型,变量对应的内存块直接存储数据本身;
  • 在操作的时候(例如读写效率方面),更快;
  • 存储效率高。
  1. Integer缓存
  • Integer类内部实现了一个静态缓存池,用于存储特定范围内的整数值对应的Integer对象;
    默认情况下,范围是-128-127,当通过Integer.valueOf(int)方法创建一个在此范围的整数对象,不会每次都生成新的对象实例,而是服用缓存中的现有对象,会直接从内存中取出,而不需要新建一个对象。
  1. 怎么理解面向对象
  • 面向对象是一种编程范式,将现实世界中的事物抽象为对象,对象具有属性/字段(也就是数据)和行为/方法(也就是操作数据的方法),面向对象编程的设计思想是以对象为中心,通过对象之间的交互来完成程序的功能,具有灵活性和可扩展性,通过封装和继承可以更好地应对需求变化;
  1. Java面向对象的三大特性:封装、继承、多态
  • 封装:将对象的属性(数据)和行为(方法)结合在一起,对外隐藏对象的内部细节,仅通过对象提供的接口和外界交互,封装的目的是增强安全性和简化编程,使对象更加独立;
  • 继承:是一种可以使得子类自动共享父类数据结构和方法的机制,它是代码复用的重要手段,通过继承可以建立类与类之间的层次关系,使得结构更加清晰;
  • 多态:允许不同类的对象对同一消息做出响应,即同一个接口,使用不同实例而执行不同操作。多态性可分为编译时多态(重载)和运行时多态(重写),提高了代码的扩展性和复用性。
  1. 多态体现在哪几个方面?
  • 方法重载:同一个类中可以有多个同名方法,它们具有不同的参数列表(参数类型、数量或顺序不同)。
  • 方法重写:子类能够提供对父类中同名方法的具体实现,在运行时,JVM会根据对象的实际类型确定调用哪个版本的方法;
    Eg:在Animal类中定义了sound方法,子类Dog可以重写该方法实现bark,Cat实现meow;
  • 接口与实现:多个类可以实现同一个接口,并且用接口类型的引用来调用这些类的方法;
    Eg:Dog类和Cat类都实现了一个Animal接口,当用Animal类型的引用来调用makeSound方法时,Dog类会触发bark,Cat类会出发meow;
  • 向上转型和向下转型:
     向上转型:使用父类类型的引用指向子类对象(隐式/自动转换),通过这种方式可以在运行时期采用不同的子类实现;
    Animal animal = new Dog(); // 假设Dog是Animal的子类
    animal.makeSound(); // 如果makeSound()在Dog中有覆盖,则调用Dog的makeSound()
     向下转型:将父类引用转回子类类型,需要显示进行,并且只有当父类引用实际上指向的是一个子类对象时才安全,建议使用instanceof操作符检查对象的实际类型,避免ClassCastException
    Animal animal = new Dog();
    Dog dog = (Dog) animal; // 显式向下转型
    dog.fetch(); // 调用Dog特有的fetch方法
  1. 重载与重写的区别
  • 重载:在同一个类中,可以有多个同名方法,它们具有不同的参数列表,编译器会根据调用时的参数类型来决定调用哪个方法;
  • 重写:子类可重新定义父类中的方法,但方法名、参数列表和返回类型必须与父类中的方法一致,通过@override注解来明确表明这是对父类方法的重写。
  1. 抽象类和普通类的区别
  • 实例化:普通类可以直接实例化对象,而抽象类不能被实例化,只能被继承
  • 方法实现:普通类中的方法可以有具体的实现,而抽象类中的方法可以有实现也可以没有实现;
  • 继承:一个类可以继承一个普通类,而且可以继承多个接口,而一个类只能继承一个抽象类,但可以同时实现多个接口;
  • 实现限制:普通类可以被其他类继承和使用,而抽象类一般用于作为基类,被其他类继承和扩展使用。
  1. 抽象类可以被实例化吗?
    在java中,抽象类不能被实例化,这意味着不能使用new关键字直接创建一个抽象类的对象。抽象类的存在主要是为了被继承,它通常包含一个或多个抽象方法,这些方法需要在子类中被实现。
    抽象类可以有构造器,这些构造器在子类实例化时会被调用,以便进行必须的初始化工作,这个过程不是直接实例化抽象类,而是创建了子类的实例,间接使用了抽象类的构造器。

  2. 抽象类和接口

  • 抽象类用于描述类的共同特性和行为,可以有成员变量、构造方法和具体方法。用于表示一种类型的基本特征,而不提供该类型的完整实现(用abstract关键字声明的类),用于有明显继承关系的场景;
  • 接口用于定义行为规范,可以多实现,只能有常量和抽象方法。适用于定义类的能力或功能;
  1. 抽象类和接口的区别
  • 实现方式:实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类(单继承),所以,使用接口可以间接实现多重继承;
    Eg:class MyClass implements InterfaceA, InterfaceB { … } // 每个接口有自己的方法,模拟类似于“多重继承”的效果
    class MyClass extends AbstractClassA { … }
  • 方法方式:接口只有定义,不能有方法的实现,java1.8中可以定义default方法,而抽象类可以有定义与实现,方法可在抽象类中实现;
  • 访问修饰符:接口成员变量默认为public static final,必须赋初值,不能被修改;其所有的成员方法都是public、abstract。
    抽象类中的成员变量默认default,可在子类中被重定义,也可被重新赋值。抽象方法被abstract修饰,不能被private\static\synchroized\native修饰,必须以分号结尾。抽象类是用来继承,final修饰符用于禁止类被继承或方法被重写,所以抽象类不饿能使用final修饰。
  • 变量:抽象类可以包含实例变量和静态变量,而接口只能包含常量(即静态常量)。
  1. 接口可以包含构造函数吗?
    接口不能有构造函数,因为它是定义行为规范的,构造函数属于类的职责范畴,用来创建和初始化对象,所以接口不需要也不允许有构造函数。

  2. 接口里面可以定义哪些方法?

  • 抽象方法
     抽象方法是接口的核心部分,所有实现接口的类都必须实现这些方法,抽象方法默认是public和abstract,这些修饰符可以省略。

  • 默认方法
     默认方法是Java8中引入的,允许接口提供具体实现,实现类可以选择重写默认方法。

  • 静态方法
     静态方法也是在Java8中引入的,它属于接口本身,可以通过接口名直接调用,而不需要实现类的对象

  • 私有方法
     Java9中引入的,用于在接口中为默认方法或其他私有方法提供辅助功能,这些方法不能被实现类访问,只能在接口内部使用
    Eg:
    public interface Animal{
    void makeSound(); //抽象方法

    default void sleep(){ // 默认方法
    System.out.println(“Sleeping…”);
    }
    static void staticMethod(){ // 静态方法
    System.out.println(“Statc method”);
    }

Private void logSleep(){ // 私有方法
…}
}

  1. 静态变量和静态方法
  • 静态变量和静态方法是与类本身关联的,而不是与类的实例(对象)关联,它们在内存中只存在一份,可以被类的所有实例共享
  • 静态变量/类变量:在类中使用static关键字声明的变量,它属于类而不属于任何具体的对象,常用于需要在所有对象间共享的数据,如计数器、常量等。主要特点有:
     共享性:所有该类的实例共享同一个静态变量,某个实例修改了静态变量的值,其他实例也会看到这个修改;
     初始化:静态变量在类被加载时初始化,只会对其进行一次分配内存;
     访问方式:静态变量可以直接通过类名访问,也可以通过实例访问,但推荐使用类名。
  • 静态方法:在类中使用static关键字声明的方法,类似于静态变量,静态方法也属于类,而不是任何具体的对象,常用于助手方法(utility methods)、获取类级别的信息或是没有依赖于实例的数据处理,主要特点:
     无实例依赖:静态方法可以在没有创建类实例的情况下调用,对于静态方法来说,不能直接访问非静态的成员变量和方法,应为静态方法没有上下文的实例;
     访问静态成员:静态方法可以直接调用其他静态变量和静态方法,但不能直接访问非静态成员;
     多态性:静态方法不支持重写(override),但可以被隐藏。
  1. 非静态内部类和静态内部类的区别
  • 非静态内部类是指定义在另一个类中的类;静态内部类是指用static修饰的内部类;
  • 非静态内部类依赖于外部类的实例,而静态内部类不依赖于外部类的实例,可以直接使类名创建;即非静态内部类在外部类实例化后才能实例化,而静态内部类可以独立实例化;
  • 非静态内部类可以访问外部类的实例变量和方法,而静态内部类只能访问外部类的静态成员;
  • 非静态内部类不能直接定义静态成员,而静态内部类可以定义静态成员;
  • 非静态内部类可以访问外部类的私有成员,而静态内部类不能直接访问外部类的私有成员,需要通过实例化外部类来访问。
  1. 为什么非静态内部类可以直接访问外部方法?
    是因为编译器在生成字节码时会为非静态内部类维护一个指向外部类实例的引用,这个引用使得非静态内部类能够访问外部类的实例变量和方法。编译器会在生成非静态内部类的构造方法时,将外部类实例作为参数传入,并在内部类的实例化过程中建立外部类和内部类实例之间的联系,从而实现直接访问外部方法的功能。

  2. Java中final作用是什么?

  • 修饰类:当final修饰一个类时,表明这个类不能被继承,是类继承体系中的最终形态。确保该类的不可变性和安全性;
  • 修饰方法:用final修饰的方法不能在子类中被重写;
  • 修饰变量:当final修饰基本数据类型的变量时,该变量一旦被赋值就不能再改变;对于引用数据类型,用final修饰后不能改变指向对象,但是对象本身的内容可以改变。
  1. 浅拷贝和深拷贝
  • 浅拷贝:只复制对象本身和其内部的值类型字段,但不会赋值对象内部的引用类型字段。浅拷贝只是创建一个新的对象,新的对象和源对象指向同一个引用对象;
  • 深拷贝:在复制对象的同时,将对象内部的所有引用类型的字段的内容也复制一份,而不是共享引用。深拷贝会创建一个新的对象的同时,创建新的内部引用的对象。
  1. 实现深拷贝的三种方法
  • 实现Cloneable接口并重写clone()方法
     要求对象及其所有引用类型字段都实现Cloneable接口,并重写clone()方法,在clone()方法中,通过递归克隆引用类型字段来实现深拷贝
  • 使用序列化和反序列化
     通过将兑现该序列化为字节流,再从字节流反序列化为对象来实现深拷贝。要求对象及其所有引用类型字段都实现Serializable接口;
  • 手动递归复制
     针对特定对象结构,手动递归复制对象及其引用类型字段;
  1. 泛型
  • 泛型是Java编程中一个重要特性,允许类、接口、方法在定义时使用一个或多个类型参数,这些类型参数在使用时可以被指定为具体的类型。
  • 泛型的主要目的是在编译时提供更强的类型检查,并且在编译后能够保留类型信息,避免在运行时出现类型转换异常
  1. Java创建对象的方式(4种)
  • 使用new关键字:通过new关键字直接调用类的构造方法来创建对象。
    Eg: MyClass obj = new MyClass();

  • 通过Class类中的newInstance()方法:通过反射机制
    Eg: MyClass obj = (MyClass) Class.forName(“com.example.MyClass”).newInstance();

  • 使用Constructor类中的newInstance()方法:同样是使用反射机制
    Eg: Constructor constructor = MyClass.class.getConstructor();
    MyClass obj = constructor.newInstance();

  • 使用clone()方法:如果类实现了Cloneable接口,可以使用clone()方法复制对象
    Eg: MyClass obj1 = new MyClass();
    MyClass obj2 = (MyClass) obj1.clone();

  • 使用反序列化:通过将对象序列化到文件或流中,然后再进行反序列化来创建对象。

  1. New出的对象什么时候回收?
    通过new创建的对象,由java的垃圾回收器负责回收,垃圾回收器是在程序运行过程种自动进行的,它会周期性地检查不再被引用的对象,并将其回收释放内存。具体的算法有以下几种情况:
  • 引用计数法:某个对象的引用计数为0时,表示该对象不再被引用,可以被回收;
  • 可达性分析算法:从根对象(如方法区中的类静态属性、方法中的局部变量等)出发,通过对象之间的引用链进行遍历,如果存在一条引用链到达某个对象,则说明该对象是可达的,反之则不可达的对象将被回收;
  • 终结器(Finalizer):如果对象重写了finalize()方法,垃圾回收器会在回收该对象之前调用finalize()方法,对象可以在finalize()方法中进行一些清理操作。然而这一机制不被推荐,因为执行时间不确定会导致不可预计的性能问题。

你可能感兴趣的:(java)