Java基础八股文

1. Java基础

1.1 为什么Java代码可以实现一次编写、到处运行?

参考答案

JVM(Java虚拟机)是Java跨平台的关键。

在程序运行前,Java源代码(.java)需要经过编译器编译成字节码(.class)。在程序运行时,JVM负责将字节码翻译成特定平台下的机器码并运行,也就是说,只要在不同的平台上安装对应的JVM,就可以运行字节码文件。

同一份Java源代码在不同的平台上运行,它不需要做任何的改变,并且只需要编译一次。而编译好的字节码,是通过JVM这个中间的“桥梁”实现跨平台的,JVM是与平台相关的软件,它能将统一的字节码翻译成该平台的机器码。

注意事项

  1. 编译的结果是生成字节码、不是机器码,字节码不能直接运行,必须通过JVM翻译成机器码才能运行;
  2. 跨平台的是Java程序、而不是JVM,JVM是用C/C++开发的软件,不同平台下需要安装不同版本的JVM。

1.2 一个Java文件里可以有多个类吗(不含内部类)?

参考答案

  1. 一个java文件里可以有多个类,但最多只能有一个被public修饰的类;
  2. 如果这个java文件中包含public修饰的类,则这个类的名称必须和java文件名一致。

1.3 说一说你对Java访问权限的了解

参考答案

Java语言为我们提供了三种访问修饰符,即private、protected、public,在使用这些修饰符修饰目标时,一共可以形成四种访问权限,即private、defalut、protected、public,注意在不加任何修饰符时为defalut访问权限。

在修饰成员变量/成员方法时,该成员的四种访问权限的含义如下:

  • private:该成员可以被该类内部成员访问;
  • defalut:该成员可以被该类内部成员访问,也可以被同一包下其他的类访问;
  • protected:该成员可以被该类内部成员访问,也可以被同一包下其他的类访问,还可以被它的子类访问;
  • public:该成员可以被任意包下,任意类的成员进行访问。

在修饰类时,该类只有两种访问权限,对应的访问权限的含义如下:

  • defalut:该类可以被同一包下其他的类访问;
  • public:该类可以被任意包下,任意的类所访问。

1.4 介绍一下Java的数据类型

参考答案

Java数据类型包括基本数据类型和引用数据类型两大类。

基本数据类型有8个,可以分为4个小类,分别是整数类型(byte/short/int/long)、浮点类型(float/double)、字符类型(char)、布尔类型(boolean)。其中,4个整数类型中,int类型最为常用。2个浮点类型中,double最为常用。另外,在这8个基本类型当中,除了布尔类型之外的其他7个类型,都可以看做是数字类型,它们相互之间可以进行类型转换。

引用类型就是对一个对象的引用,根据引用对象类型的不同,可以将引用类型分为3类,即数组、类、接口类型。引用类型本质上就是通过指针,指向堆中对象所持有的内存空间,只是Java语言不再沿用指针这个说法而已。

扩展阅读

对于基本数据类型,你需要了解每种类型所占据的内存空间,面试官可能会追问这类问题:

  • byte:1字节(8位),数据范围是 -2^7 ~ 2^7-1

  • short:2字节(16位),数据范围是 -2^15 ~ 2^15-1

  • int:4字节(32位),数据范围是 -2^31 ~ 2^31-1

  • long:8字节(64位),数据范围是 -2^63 ~ 2^63-1

  • float:4字节(32位),数据范围大约是 -3.4*10^38 ~ 3.4*10^38

  • double:8字节(64位),数据范围大约是 -1.8*10^308 ~ 1.8*10^308

  • char:2字节(16位),数据范围是 \u0000 ~ \uffff

  • boolean:Java规范没有明确的规定,不同的JVM有不同的实现机制。

对于引用数据类型,你需要了解JVM的内存分布情况,知道引用以及引用对象存放的位置,详见JVM部分的题目。

1.5 int类型的数据范围是多少?

参考答案

int类型占4字节(32位),数据范围是 -2^31 ~ 2^31-1

1.6 请介绍全局变量和局部变量的区别

参考答案

Java中的变量分为成员变量和局部变量,它们的区别如下:

成员变量:

  1. 成员变量是在类的范围里定义的变量;
  2. 成员变量有默认初始值;
  3. 未被static修饰的成员变量也叫实例变量,它存储于对象所在的堆内存中,生命周期与对象相同;
  4. 被static修饰的成员变量也叫类变量,它存储于方法区中,生命周期与当前类相同。

局部变量:

  1. 局部变量是在方法里定义的变量;
  2. 局部变量没有默认初始值;
  3. 局部变量存储于栈内存中,作用的范围结束,变量空间会自动的释放。

注意事项

Java中没有真正的全局变量,面试官应该是出于其他语言的习惯说全局变量的,他的本意应该是指成员变量。

1.7 请介绍一下实例变量的默认值

参考答案

实例变量若为引用数据类型,其默认值一律为null。若为基本数据类型,其默认值如下:

  • byte:0

  • short:0

  • int:0

  • long:0L

  • float:0.0F

  • double:0.0

  • char:'\u0000'

  • boolean:false

注意事项

上述默认值规则适用于所有的成员变量,所以对于类变量也是适用的。

1.8 为啥要有包装类?

参考答案

Java语言是面向对象的语言,其设计理念是“一切皆对象”。但8种基本数据类型却出现了例外,它们不具备对象的特性。正是为了解决这个问题,Java为每个基本数据类型都定义了一个对应的引用类型,这就是包装类。

扩展阅读

Java之所以提供8种基本数据类型,主要是为了照顾程序员的传统习惯。这8种基本数据类型的确带来了一定的方便性,但在某些时候也会受到一些制约。比如,所有的引用类型的变量都继承于Object类,都可以当做Object类型的变量使用,但基本数据类型却不可以。如果某个方法需要Object类型的参数,但实际传入的值却是数字的话,就需要做特殊的处理了。有了包装类,这种问题就可以得以简化。

1.9 说一说自动装箱、自动拆箱的应用场景

参考答案

自动装箱、自动拆箱是JDK1.5提供的功能。

自动装箱:可以把一个基本类型的数据直接赋值给对应的包装类型;

自动拆箱:可以把一个包装类型的对象直接赋值给对应的基本类型;

通过自动装箱、自动拆箱功能,可以大大简化基本类型变量和包装类对象之间的转换过程。比如,某个方法的参数类型为包装类型,调用时我们所持有的数据却是基本类型的值,则可以不做任何特殊的处理,直接将这个基本类型的值传入给方法即可。

1.10 如何对Integer和Double类型判断相等?

参考答案

Integer、Double不能直接进行比较,这包括:

  • 不能用==进行直接比较,因为它们是不同的数据类型;
  • 不能转为字符串进行比较,因为转为字符串后,浮点值带小数点,整数值不带,这样它们永远都不相等;
  • 不能使用compareTo方法进行比较,虽然它们都有compareTo方法,但该方法只能对相同类型进行比较。

整数、浮点类型的包装类,都继承于Number类型,而Number类型分别定义了将数字转换为byte、short、int、long、float、double的方法。所以,可以将Integer、Double先转为转换为相同的基本数据类型(如double),然后使用==进行比较。

示例代码

1

2

3

Integer i = 100;

Double d = 100.00;

System.out.println(i.doubleValue() == d.doubleValue());

1.11 int和Integer有什么区别,二者在做==运算时会得到什么结果?

参考答案

int是基本数据类型,Integer是int的包装类。二者在做==运算时,Integer会自动拆箱为int类型,然后再进行比较。届时,如果两个int值相等则返回true,否则就返回false。

1.12 说一说你对面向对象的理解

参考答案

面向对象是一种更优秀的程序设计方法,它的基本思想是使用类、对象、继承、封装、消息等基本概念进行程序设计。它从现实世界中客观存在的事物出发来构造软件系统,并在系统构造中尽可能运用人类的自然思维方式,强调直接以现实世界中的事物为中心来思考,认识问题,并根据这些事物的本质特点,把它们抽象地表示为系统中的类,作为系统的基本构成单元,这使得软件系统的组件可以直接映像到客观世界,并保持客观世界中事物及其相互关系的本来面貌。

扩展阅读

结构化程序设计方法主张按功能来分析系统需求,其主要原则可概括为自顶向下、逐步求精、模块化等。结构化程序设计首先采用结构化分析方法对系统进行需求分析,然后使用结构化设计方法对系统进行概要设计、详细设计,最后采用结构化编程方法来实现系统。

因为结构化程序设计方法主张按功能把软件系统逐步细分,因此这种方法也被称为面向功能的程序设计方法;结构化程序设计的每个功能都负责对数据进行一次处理,每个功能都接受一些数据,处理完后输出一些数据,这种处理方式也被称为面向数据流的处理方式。

结构化程序设计里最小的程序单元是函数,每个函数都负责完成一个功能,用以接收一些输入数据,函数对这些输入数据进行处理,处理结束后输出一些数据。整个软件系统由一个个函数组成,其中作为程序入口的函数被称为主函数,主函数依次调用其他普通函数,普通函数之间依次调用,从而完成整个软件系统的功能。

每个函数都是具有输入、输出的子系统,函数的输入数据包括函数形参、全局变量和常量等,函数的输出数据包括函数返回值以及传出参数等。结构化程序设计方式有如下两个局限性:

  • 设计不够直观,与人类习惯思维不一致。采用结构化程序分析、设计时,开发者需要将客观世界模型分解成一个个功能,每个功能用以完成一定的数据处理。
  • 适应性差,可扩展性不强。由于结构化设计采用自顶向下的设计方式,所以当用户的需求发生改变,或需要修改现有的实现方式时,都需要自顶向下地修改模块结构,这种方式的维护成本相当高。

1.13 面向对象的三大特征是什么?

参考答案

面向对象的程序设计方法具有三个基本特征:封装、继承、多态。其中,封装指的是将对象的实现细节隐藏起来,然后通过一些公用方法来暴露该对象的功能;继承是面向对象实现软件复用的重要手段,当子类继承父类后,子类作为一种特殊的父类,将直接获得父类的属性和方法;多态指的是子类对象可以直接赋给父类变量,但运行时依然表现出子类的行为特征,这意味着同一个类型的对象在执行同一个方法时,可能表现出多种行为特征。

扩展阅读

抽象也是面向对象的重要部分,抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是考虑部分问题。例如,需要考察Person对象时,不可能在程序中把Person的所有细节都定义出来,通常只能定义Person的部分数据、部分行为特征,而这些数据、行为特征是软件系统所关心的部分。

1.14 封装的目的是什么,为什么要有封装?

参考答案

封装是面向对象编程语言对客观世界的模拟,在客观世界里,对象的状态信息都被隐藏在对象内部,外界无法直接操作和修改。对一个类或对象实现良好的封装,可以实现以下目的:

  • 隐藏类的实现细节;
  • 让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里加入控制逻辑,限制对成员变量的不合理访问;
  • 可进行数据检查,从而有利于保证对象信息的完整性;
  • 便于修改,提高代码的可维护性。

扩展阅读

为了实现良好的封装,需要从两个方面考虑:

  • 将对象的成员变量和实现细节隐藏起来,不允许外部直接访问;
  • 把方法暴露出来,让方法来控制对这些成员变量进行安全的访问和操作。

封装实际上有两个方面的含义:把该隐藏的隐藏起来,把该暴露的暴露出来。这两个方面都需要通过使用Java提供的访问控制符来实现。

1.15 说一说你对多态的理解

参考答案

因为子类其实是一种特殊的父类,因此Java允许把一个子类对象直接赋给一个父类引用变量,无须任何类型转换,或者被称为向上转型,向上转型由系统自动完成。

当把一个子类对象直接赋给父类引用变量时,例如 BaseClass obj = new SubClass();,这个obj引用变量的编译时类型是BaseClass,而运行时类型是SubClass,当运行时调用该引用变量的方法时,其方法行为总是表现出子类方法的行为特征,而不是父类方法的行为特征,这就可能出现:相同类型的变量、调用同一个方法时呈现出多种不同的行为特征,这就是多态。

扩展阅读

多态可以提高程序的可扩展性,在设计程序时让代码更加简洁而优雅。

例如我要设计一个司机类,他可以开轿车、巴士、卡车等等,示例代码如下:

1

2

3

4

5

class Driver {

    void drive(Car car) { ... }

    void drive(Bus bus) { ... }

    void drive(Truck truck) { ... }

}

在设计上述代码时,我已采用了重载机制,将方法名进行了统一。这样在进行调用时,无论要开什么交通工具,都是通过 driver.drive(obj) 这样的方式来调用,对调用者足够的友好。

但对于程序的开发者来说,这显得繁琐,因为实际上这个司机可以驾驶更多的交通工具。当系统需要为这个司机增加车型时,开发者就需要相应的增加driver方法,类似的代码会堆积的越来越多,显得臃肿。

采用多态的方式来设计上述程序,就会变得简洁很多。我们可以为所有的交通工具定义一个父类Vehicle,然后按照如下的方式设计drive方法。调用时,我们可以传入Vehicle类型的实例,也可以传入任意的Vechile子类型的实例,对于调用者来说一样的方便,但对于开发者来说,代码却变得十分的简洁了。

复制代码

1

2

3

class Driver {

    void drive(Vehicle vehicle) { ... }

}

1.16 Java中的多态是怎么实现的?

参考答案

多态的实现离不开继承,在设计程序时,我们可以将参数的类型定义为父类型。在调用程序时,则可以根据实际情况,传入该父类型的某个子类型的实例,这样就实现了多态。对于父类型,可以有三种形式,即普通的类、抽象类、接口。对于子类型,则要根据它自身的特征,重写父类的某些方法,或实现抽象类/接口的某些抽象方法。

1.17 Java为什么是单继承,为什么不能多继承?

参考答案

首先,Java是单继承的,指的是Java中一个类只能有一个直接的父类。Java不能多继承,则是说Java中一个类不能直接继承多个父类。

其次,Java在设计时借鉴了C++的语法,而C++是支持多继承的。Java语言之所以摒弃了多继承的这项特征,是因为多继承容易产生混淆。比如,两个父类中包含相同的方法时,子类在调用该方法或重写该方法时就会迷惑。

准确来说,Java是可以实现"多继承"的。因为尽管一个类只能有一个直接父类,但是却可以有任意多个间接的父类。这样的设计方式,避免了多继承时所产生的混淆。

1.18 说一说重写与重载的区别

参考答案

重载发生在同一个类中,若多个方法之间方法名相同、参数列表不同,则它们构成重载的关系。重载与方法的返回值以及访问修饰符无关,即重载的方法不能根据返回类型进行区分。

重写发生在父类子类中,若子类方法想要和父类方法构成重写关系,则它的方法名、参数列表必须与父类方法相同。另外,返回值要小于等于父类方法,抛出的异常要小于等于父类方法,访问修饰符则要大于等于父类方法。还有,若父类方法的访问修饰符为private,则子类不能对其重写。

1.19 构造方法能不能重写?

参考答案

构造方法不能重写。因为构造方法需要和类保持同名,而重写的要求是子类方法要和父类方法保持同名。如果允许重写构造方法的话,那么子类中将会存在与类名不同的构造方法,这与构造方法的要求是矛盾的。

1.20 介绍一下Object类中的方法

参考答案

Object类提供了如下几个常用方法:

  • Class<?> getClass():返回该对象的运行时类。
  • boolean equals(Object obj):判断指定对象与该对象是否相等。
  • int hashCode():返回该对象的hashCode值。在默认情况下,Object类的hashCode()方法根据该对象的地址来计算。但很多类都重写了Object类的hashCode()方法,不再根据地址来计算其hashCode()方法值。
  • String toString():返回该对象的字符串表示,当程序使用System.out.println()方法输出一个对象,或者把某个对象和字符串进行连接运算时,系统会自动调用该对象的toString()方法返回该对象的字符串表示。Object类的toString()方法返回 运行时类名@十六进制hashCode值 格式的字符串,但很多类都重写了Object类的toString()方法,用于返回可以表述该对象信息的字符串。

另外,Object类还提供了wait()、notify()、notifyAll()这几个方法,通过这几个方法可以控制线程的暂停和运行。Object类还提供了一个clone()方法,该方法用于帮助其他对象来实现“自我克隆”,所谓“自我克隆”就是得到一个当前对象的副本,而且二者之间完全隔离。由于该方法使用了protected修饰,因此它只能被子类重写或调用。

扩展阅读

Object类还提供了一个finalize()方法,当系统中没有引用变量引用到该对象时,垃圾回收器调用此方法来清理该对象的资源。并且,针对某一个对象,垃圾回收器最多只会调用它的finalize()方法一次。

注意,finalize()方法何时调用、是否调用都是不确定的,我们也不要主动调用finalize()方法。从JDK9开始,这个方法被标记为不推荐使用的方法。

1.21 说一说hashCode()和equals()的关系

参考答案

hashCode()用于获取哈希码(散列码),eauqls()用于比较两个对象是否相等,它们应遵守如下规定:

  • 如果两个对象相等,则它们必须有相同的哈希码。
  • 如果两个对象有相同的哈希码,则它们未必相等。

扩展阅读

在Java中,Set接口代表无序的、元素不可重复的集合,HashSet则是Set接口的典型实现。

当向HashSet中加入一个元素时,它需要判断集合中是否已经包含了这个元素,从而避免重复存储。由于这个判断十分的频繁,所以要讲求效率,绝不能采用遍历集合逐个元素进行比较的方式。实际上,HashSet是通过获取对象的哈希码,以及调用对象的equals()方法来解决这个判断问题的。

HashSet首先会调用对象的hashCode()方法获取其哈希码,并通过哈希码确定该对象在集合中存放的位置。假设这个位置之前已经存了一个对象,则HashSet会调用equals()对两个对象进行比较。若相等则说明对象重复,此时不会保存新加的对象。若不等说明对象不重复,但是它们存储的位置发生了碰撞,此时HashSet会采用链式结构在同一位置保存多个对象,即将新加对象链接到原来对象的之后。之后,再有新添加对象也映射到这个位置时,就需要与这个位置中所有的对象进行equals()比较,若均不相等则将其链到最后一个对象之后。

1.22 为什么要重写hashCode()和equals()?

参考答案

Object类提供的equals()方法默认是用==来进行比较的,也就是说只有两个对象是同一个对象时,才能返回相等的结果。而实际的业务中,我们通常的需求是,若两个不同的对象它们的内容是相同的,就认为它们相等。鉴于这种情况,Object类中equals()方法的默认实现是没有实用价值的,所以通常都要重写。

由于hashCode()与equals()具有联动关系(参考“说一说hashCode()和equals()的关系”一题),所以equals()方法重写时,通常也要将hashCode()进行重写,使得这两个方法始终满足相关的约定。

1.23 ==和equals()有什么区别?

参考答案

==运算符:

  • 作用于基本数据类型时,是比较两个数值是否相等;
  • 作用于引用数据类型时,是比较两个对象的内存地址是否相同,即判断它们是否为同一个对象;

equals()方法:

  • 没有重写时,Object默认以 == 来实现,即比较两个对象的内存地址是否相同;
  • 进行重写后,一般会按照对象的内容来进行比较,若两个对象内容相同则认为对象相等,否则认为对象不等。

1.24 String类有哪些方法?

参考答案

String类是Java最常用的API,它包含了大量处理字符串的方法,比较常用的有:

  • char charAt(int index):返回指定索引处的字符;
  • String substring(int beginIndex, int endIndex):从此字符串中截取出一部分子字符串;
  • String[] split(String regex):以指定的规则将此字符串分割成数组;
  • String trim():删除字符串前导和后置的空格;
  • int indexOf(String str):返回子串在此字符串首次出现的索引;
  • int lastIndexOf(String str):返回子串在此字符串最后出现的索引;
  • boolean startsWith(String prefix):判断此字符串是否以指定的前缀开头;
  • boolean endsWith(String suffix):判断此字符串是否以指定的后缀结尾;
  • String toUpperCase():将此字符串中所有的字符大写;
  • String toLowerCase():将此字符串中所有的字符小写;
  • String replaceFirst(String regex, String replacement):用指定字符串替换第一个匹配的子串;
  • String replaceAll(String regex, String replacement):用指定字符串替换所有的匹配的子串。

注意事项

String类的方法太多了,你没必要都记下来,更不需要一一列举。面试时能说出一些常用的方法,表现出对这个类足够的熟悉就可以了。另外,建议你挑几个方法仔细看看源码实现,面试时可以重点说这几个方法。

1.25 String可以被继承吗?

参考答案

String类由final修饰,所以不能被继承。

扩展阅读

在Java中,String类被设计为不可变类,主要表现在它保存字符串的成员变量是final的。

  • Java 9之前字符串采用char[]数组来保存字符,即 private final char[] value
  • Java 9做了改进,采用byte[]数组来保存字符,即 private final byte[] value

之所以要把String类设计为不可变类,主要是出于安全和性能的考虑,可归纳为如下4点。

  • 由于字符串无论在任何 Java 系统中都广泛使用,会用来存储敏感信息,如账号,密码,网络路径,文件处理等场景里,保证字符串 String 类的安全性就尤为重要了,如果字符串是可变的,容易被篡改,那我们就无法保证使用字符串进行操作时,它是安全的,很有可能出现 SQL 注入,访问危险文件等操作。
  • 在多线程中,只有不变的对
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java基础八股文面试题 1. Java的特点是什么? Java语言具有面向对象、跨平台、安全性、可靠性、可移植性、多线程等特点。 2. Java的基本数据类型有哪些? Java的基本数据类型包括整型、浮点型、字符型、布尔型。 3. Java中的变量命名规则是什么? Java中的变量命名规则是遵循驼峰命名法,即第一个单词小写,后面的每个单词首字母大写。 4. Java中的四种访问权限分别是什么? Java中的四种访问权限分别是public、private、protected、default。 5. Java中的final关键字有什么作用? Java中的final关键字用来修饰一个变量、一个方法或一个类,分别表示不可变、不可重写和不可继承。 6. Java中的抽象类和接口有什么区别? Java中的抽象类和接口都是用来实现多态性的机制,但是抽象类可以包含非抽象方法和属性,而接口只能包含抽象方法和常量。另外,一个类只能继承一个抽象类,但是可以实现多个接口。 7. Java中的异常处理机制是什么? Java中的异常处理机制是通过try-catch-finally代码块实现的,当程序发生异常时,会抛出一个异常对象,可以通过try-catch语句捕获并处理异常。 8. Java中的线程有哪些状态? Java中的线程有五种状态,分别是新建状态、就绪状态、运行状态、阻塞状态和死亡状态。 以上是Java基础八股文面试题的答案,希望能够帮助到您。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值