Java常见面试题:JavaSE基础知识(一)

  • A+
所属分类:后端开发

整理了一些java常见的面试题型和基础知识,由浅入深,预计共五章。本篇为第一章,全文一共8522字,阅读完需要XX分钟。建议使用右下角“文章目录”检索查看!

大多数知识收集于网络,知识点不足之处,还望纠正!如涉及版权之类,请邮件告知:yd@yflad.cn(取首尾,此yd 非彼 yd)( •̥́ ˍ •̀ू )

易引发联想,还是换个吧:bat@yflad.cn

Java常见面试题:JavaSE基础知识(一)

 

01、JVM、JRE和JDK的区别?

  • JVM(Java Virtual Machine):
    java虚拟机,用于保证java的跨平台的特性。 java语言是跨平台,jvm不是跨平台的。
  • JRE(Java Runtime Environment):
    java的运行环境,包括jvm+java的核心类库。
  • JDK(Java Development Kit):
    java的开发工具,包括jre+开发工具

 

02、环境变量path和classpath的作用是什么?

  1. path是配置Windows可执行文件的搜索路径,即扩展名为.exe的程序文件所在的目录,
    用于指定DOS窗口命令的路径。
  2. Classpath是配置class文件所在的目录,用于指定类搜索路径,JVM就是通过它来寻找该类的class类文件的。

 

Java语言的特性?

跨平台性、面向对象、安全性、多线程、简单易用。

 

变量有什么用?为什么要定义变量?什么时候用?

变量的作用:用来存储数据。

为什么要定义变量:用来不断的存放同一类型的常量,并可以重复使用

 

& 和 && 的区别?

  1. &运算符有两种用法: (1)按位与; (2)逻辑与。
  2. &&运算符是短路与运算。
  3. &&会出现短路,如果可以通过第一个表达式判断出整个表达式的结果,则不继续后面表达式的运算;只能操作boolean类型数据

详细介绍:展开

&&之所以称为短路运算是因为,如果&&左边的表达式的值是 false,右边的表达式会被直接短路掉,不会进行运算。

很多时候我们可能都需要用&&而不是&,例如在验证用户登录时判定用户名不是 null 而且不是空字符串,应当写为 username != null &&!username.equals(""),二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行字符串的 equals 比较,否则会产生 NullPointerException 异常。

注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。

 

数据类型

1)基本数据类型(4类8种)(字节数){数据表示范围}

整数类型:byte(1)short(2)int(4)long(8)

字符类型:char(2)

布尔类型:boolean(1)(ture false)

浮点数类型:float(4)double(8)

2)引用数据类型:

类、接口、数组

String不是基本数据类型,它定义的为对象。

3)数据类型表展开

四类 八种 字节数 数据表示范围
 

整型

 

byte 1-128~127
short 2-32768~32767
int 4-2147483648~2147483647
long 8-263~263-1
浮点型 float 4-3.403E38~3.403E38
double 8-1.798E308~1.798E308
字符型 char 2表示一个字符,如('a','A','0','家')
布尔型 boolean 1

只有两个值 true 与 false

 

类型转换

1)精度从高到低 double float long int short(char) byte

  • 自动类型转换 将一个低精度 → 高精度
  • 强制类型转换 将一个高精度 → 低精度(精度会下降)

 

String是最基本的数据类型吗?

基本数据类型包括byte、int、char、long、float、double、boolean和short。 取值范围是 -2^(字节数*8-1) ~ -2^(字节数*8-1) - 1,但Boolean是4个字节,而boolean数组1个字节,这是因为虚拟机用int来代替boolean,保证高效存取。而java.lang.String类是final类型的,因此不能继承也不能修改String类。String还重写了hashCode和equals方法,

 

数组和String有length()方法吗?

数组没有length()这个方法,有getLength()方法,但有length的属性。String有length()方法

 

是否可以继承String

String 类是 final 类,不可以被继承。

展开查看解释:展开

继承 String 本身就是一个错误的行为,对 String 类型最好的重用方式是关联关系(Has-A)和依赖关系(UseA)而不是继承关系(Is-A)

 

new一个字符串“xyz”创建了几个对象?

两个或一个,new一个字符串会产生两个对象,一个在堆上,一个在常量池中,堆上的数据每new一次产生一个新的对象,如果常量池中有需要的对象,就不会创建新的对象,直接拿来用,所以一共创建了一个对象,若常量池中没有要用的对象,则会创建一个,所以一共创建了两个对象。

 

float f=3.4;是否正确?

不正确。3.4是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型(down-casting,也称为窄化)会造成精度损失,因此需要强制类型转换float f =(float)3.4; 或者写成float f =3.4F。

 

int和Integer有什么区别?

int是java的原始数据类型,Integer是java为int提供的封装类。Integer实例变量的缺省值为 null,而int实例变量的缺省值与它们的类型有关。int与Integer之间可以进行自动拆装箱的转换,但Integer的取值范围是-128-127,若超过这个范围,就new一个新的对象返回(Valueof方法)

 

“==”和equals方法的区别?

== 和 equals 最大的区别是一个是运算符 一个是方法 

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

equals():用来比较方法两个对象的内容是否相等,字符串的比较常用equals方法。

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

如果一个类没有自己定义equals方法,那么它将继承Object类的equals方法(使用==操作符)

 

函数的定义、特点、两个明确

1)定义:函数就是定义在类中的具有特定功能的一段独立小程序。

2)特点

  1. 定义函数可以将功能代码进行封装,便于对该功能进行复用
  2. 函数只有被调用才会被执行
  3. 函数的出现提高了代码的复用性
  4. 对于函数没有具体返回值的情况,返回值类型用关键字void表示,那么该函数中的return语句如果在最后一行可以省略不写。

3)两个明确

  1. 明确要定义的功能最后的结果是什么?
  2. 明确在定义该功能的过程中,是否需要未知内容参与运算

 

重载:

概念:在同一个类中,允许存在一个以上的同名函数,只要它们的参数个数或者参数类型不同即可。

特点:与返回值类型无关,只看参数列表(参数类型以及参数个数)。

好处:方便于阅读,优化了程序设计。

 

数组:

概念:同一种数据类型的集合。

好处:可以自动给数组中的元素从0开始编号,方便操作这些元素。

 

内存结构:

栈内存:用于存储局部变量,当数据使用完,所占空间会自动释放。

堆内存:数组和对象,通过new建立的实例都存放在堆内存中。

方法区:静态成员、构造函数、常量池、线程池

本地方法区:window系统占用

 

面向对象的概述

面向对象是相对于面向过程而言的,面向过程强调的是功能,面向对象强调的是将功能封装进对象,强调具备功能的对象;

 

面向对象的思想特点:

  1. 是符合人们思考习惯的一种思想;
  2. 将复杂的事情简单化了;
  3. 将程序员从执行者变成了指挥者;

比如我要达到某种结果,我就寻找能帮我达到该结果的功能的对象,如我要洗衣服我就买洗衣机,至于怎么洗我不管。

 

面向对象的特性

3 大特性,封装、继承、多态

 

面向对象特性的理解

继承:继承是从已有类得到继承信息创建新类的过程。(java中用extends关键字表示)

查看具体:展开

多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可

封装:隐藏对象的属性和实现细节,仅对外提供公共访问方式

查看具体:展开

通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口。

多态:一个对象在程序不同运行时刻代表的多种状态,父类或者接口的引用指向子类对象

查看具体:展开

多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。多态性分为编译时的多态性和运行时的多态性。

如果将对象的方法视为对象向外界提供的服务,那么运行时的多态性可以解释为:当 A 系统访问 B 系统提供的服务时, B 系统有多种提供服务的方式,但一切对 A 系统来说都是透明的。

方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。

运行时的多态是面向对象最精髓的东西,要实现多态需要做两件事:

1. 方法重写(子类继承父类并重写父类中已有的或抽象的方法);

2. 对象造型(用父类型引用引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)

 

继承的特点、好处

继承的特点:

  1. java中只能单继承,没有多继承。
  2. java可以有多重(层)继承。

继承的好处:

  1. 继承的出现提高了代码的复用性。
  2. 继承的出现让类与类之间产生了关系,提供了多态的前提。

 

重写和重载的区别? 重载的方法能否根据返回类型进行区分?

重载:在同一类中。方法名相同,参数列表不同。重载可以改变返回类型。

重写:在不同类中(子父类中)。方法声明相同(返回类型,方法名,参数列表均相同)。

详细:展开

方法的重载和重写都是实现多态的方式区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。

重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;

重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求。

方法重载的规则:

  1. 方法名一致,参数列表中参数的顺序,类型,个数不同。
  2. 重载与方法的返回值无关,存在于父类和子类, 同类中。
  3. 可以抛出不同的异常,可以有不同修饰符

方法重写的规则:

点击详细:展开

  1. 参数列表必须完全与被重写方法的一致,返回类型必须完全与被重写方法的返回类型一致。
  2. 构造方法不能被重写,声明为 final 的方法不能被重写,声明为 static 的方法不能被重写,但是能够被再次声明。
  3. 访问权限不能比父类中被重写的方法的访问权限更低。
  4. 重写的方法能够抛出任何非强制异常(UncheckedException,也叫非运行时异常),无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。

重写需要注意:

  1. 子类方法的访问权限要大于等于父类方法的访问权限。
  2. 静态只能重写静态。但是这种情况一般不会出现。

 

final关键字

  1. 最终的意思,可以用于修饰类,方法,变量。
  2. final修饰的类不能被继承

final修饰的方法不能被重写

final修饰的变量是一个常量。只能被赋值一次

内部类只能访问被final修饰的局部变量。

 

final 、finally 、finalize

final 最终的意思。用于修饰类,成员变量,成员方法。它修饰的类不能被继承,它修饰的变量时常量,它修饰的方法不能被重写。

finally 用于异常处理中。表示,必须要执行的代码块,除非java虚拟机停止工作,否则一定会执行

finalize() 是Object类中的一个方法。用于java虚拟机的垃圾回收

 

假如catch中有return语句, finally里中的代码会执行吗?

是在return前,还是在return后呢?

会,在return前执行finally里面的代码。

 

类和对象:

类:对现实世界中某类事物的描述,是抽象的,概念上的定义。

对象:事物具体存在的个体。

 

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

1)作用域

  • 成员变量:针对整个类有效。
  • 局部变量:只在某个范围内有效。(一般指的就是方法,语句体内)

2)存储位置

  • 成员变量:随着对象的创建而存在,随着对象的消失而消失,存储在堆内存中。
  • 局部变量:在方法被调用,或者语句被执行的时候存在,存储在栈内存中。当方法调用完,或者语句结束后,就自动释放。

3)初始值

  • 成员变量:有默认初始值。
  • 局部变量:没有默认初始值,使用前必须赋值。

 

关键字private

封装在代码中的体现

  1. 私有的意思,权限修饰符
  2. 用来修饰成员变量和成员函数
  3. 用private修饰的成员只在本类中有效
  4. 私有是封装的一种体现

 

构造方法的特点、作用、注意

  1. 方法名与类名相同
  2. 没有返回类型
  3. 没有返回值

点击:展开

作用

构造函数是用于创建对象,并对其进行初始化赋值,对象一建立就自动调用相对应的构造函数

注意事项

  • 如果一个自定义类没有构造方法,系统会默认给出一个无参构造方法。
  • 如果一个自定义类提供了构造方法,那么,系统将不再给出无参构造方法。

这个时候,你可以不使用无参构造方法。如果你想使用,那么,就必须手动给出无参构造方法。

建议:一般情况下,我们的自定义类都要手动给出无参构造方法。

 

构造方法和成员方法的区别

  1. 格式区别
    1. 构造方法和类名相同,并且没有返回类型,也没有返回值。
    2. 普通成员方法可以任意起名,必须有返回类型,可以没有返回值。
  2. 作用区别
    1. 构造方法用于创建对象,并进行初始化值。
    2. 普通成员方法是用于完成特定功能的。
  3.  调用区别
    1. 构造方法是在创建对象时被调用的,一个对象建立,只调用一次相应构造函数
    2. 普通成员方法是由创建好的对象调用,可以调用多次

 

访问权限修饰符 public、 private、 protected, 以及默认时(不写)的区别

修饰符 当前类 同 包 子 类 其他包
public
protected ×
default × ×
private × × ×

 

匿名内部类

前提:继承一个类或者实现一个接口

格式:

new 父类名或者接口名(){
    重写父类方法或者实现接口中的方法。
    也可以自定义其他方法。
};

什么时候定义匿名内部类?

匿名内部类只是为了简化书写,匿名内部类有局限,通常定义匿名内部类时,该类方法不超过3个

匿名内部类的好处和弊端:

好处

  1. 简化代码书写

弊端

  1. 不能直接调用自己的特有方法
  2. 不能执行强转换动作
  3. 如果该类里面方法较多,不允许使用匿名内部类

 

为什么函数不能根据返回类型来区分重载?

因为调用时不能指定类型信息,编译器不知道你要调用哪个函数。

例如:展开

float max(int a, int b);
int max(int a, int b);

当调用 max(1, 2);时无法确定调用的是哪个,单从这一点上来说,仅返回值类型不同的重载是不应该允许的。

再比如对下面这两个方法来说,虽然它们有同样的名字和自变量,但其实是很容易区分的:

void f() {}
int f() {}

若编译器可根据上下文(语境)明确判断出含义,比如在 int x=f()中,那么这样做完全没有问题。然而,我们也可能调用一个方法,同时忽略返回值;我们通常把这称为“为它的副作用去调用一个方法” ,因为我们关心的不是返回值,而是方法调用的其他效果。所以假如我们像下面这样调用方法: f(); Java 怎样判断 f()的具体调用方式呢?而且别人如何识别并理解代码呢?由于存在这一类的问题,所以不能。

函数的返回值只是作为函数运行之后的一个“状态”,他是保持方法的调用者与被调用者进行通信的关键。并不能作为某个方法的“标识”。

 

抽象类和接口的区别

抽象类(abstract class)、接口(interface)

1)不同:

抽象类

  1. 抽象类中可以定义构造器
  2. 可以有抽象方法和具体方法
  3. 接口中的成员全都是 public 的
  4. 抽象类中可以定义成员变量
  5. 有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法
  6. 抽象类中可以包含静态方法
  7. 一个类只能继承一个抽象类

接口

  1. 接口中不能定义构造器
  2. 方法全部都是抽象方法
  3. 抽象类中的成员可以是 private、默认、 protected、 public
  4. 接口中定义的成员变量实际上都是常量
  5. 接口中不能有静态方法
  6. 一个类可以实现多个接口

2)相同:

  1. 不能够实例化
  2. 可以将抽象类和接口类型作为引用类型
  3. 一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现,否则该类仍然需要被声明为抽象类

 

抽象的(abstract)方法是否可....

抽象的(abstract)方法是否可同时是静态的(static), 是否可同时是本地方法(native),是否可同时被 synchronized

都不能。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。本地方法是由本地代码(如 C 代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。 synchronized 和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。

 

实现多态的机制是什么?

靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,而程序调用的方法在运行期才动态绑定,就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。

 

Person p = new Person();在内存中做了哪些事情?

  1. 将Person.class文件加载进内存中。
  2. 如果p定义在主方法中,那么,就会在栈空间开辟一个变量空间p。
  3. 在堆内存给对象分配空间。
  4. 对对象中的成员进行默认初始化。
  5. 对对象中的成员进行显示初始化。
  6. 调用构造代码块对对象进行初始化。(如果没有就不执行)
  7. 调用构造方法对对象进行初始化。对象初始化完毕。
  8. 将对象的内存地址赋值给p变量,让p变量指向该对象。

 

静态变量和成员变量的区别

  1. 调用方式
    1. 静态变量也称为类变量,可以直接通过类名调用。也可以通过对象名调用。
    2. 这个变量属于类。
    3. 成员变量也称为实例变量,只能通过对象名调用。这个变量属于对象。
  2. 存储位置
    1. 静态变量存储在方法区长中的静态区。
    2. 成员变量存储在堆内存。
  3. 生命周期
    1. 静态变量随着类的加载而存在,随着类的消失而消失。生命周期长。
    2. 成员变量随着对象的创建而存在,随着对象的消失而消失。
  4. 与对象的相关性
    1. 静态变量是所有对象共享的数据。
    2. 成员变量是每个对象所特有的数据。

 

静态变量和实例变量的区别?

静态变量属于类,实例变量依存于某一实例

查看详细:展开

静态变量: 是被 static 修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝;

实例变量: 必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。静态变量可以实现让多个对象共享内存。

 

静态的优点和弊端

  • 优点
    1. 对对象的共享数据进行单独空间的存储,节省内存,没有必要每个对象都存储一份
    2. 可直接被类名调用
  • 弊端
    1. 生命周期过长,随着类的消失而消失
    2. 访问出现权限,即静态虽好但只能访问静态

 

什么使用使用静态?

  1. 当所有对象共享某个数据的时候,就把这个成员变量定义为静态修饰的。
  2. 当某个方法没有访问该类中的非静态成员,就可以把这个方法定义为静态修饰。
静态的生命周期比较长,所以一般不推荐使用。

 

break 和 continue 的区别?

break 和 continue 都是用来控制循环的语句。

break 用于完全结束一个循环,跳出循环体执行循环后面的语句。

continue 用于跳过本次循环,执行下次循环。

 

单例设计模式

1)设计模式:解决某类问题行之有效的方法,是一种思想,是规律的总结

2)用来保证某个类在内存中只有一个对象

3)保证唯一性的思想及步骤

1. 为了避免其他程序建立该类对象,先禁止其他程序建立该类对象,即将构造函数私有化

2. 为了其他程序访问到该类对象,须在本类中创建一个该类私有对象

3. 为了方便其他程序访问到该类对象,可对外提供一个公共访问方式

比如API中的Runtime类就是单例设计模式。

4)单例设计模式的两种方式

饿汉式 当类加载的时候,就创建对象。

实例:展开

public class Student {
    // 私有化构造函数
    private Student() {
    }

    // 创建对象
    private static Student s = new Student();

    // 返回对象实例
    public static Student getInstance() {
        return s;
    }
}

懒汉式 当使用的时候,才去创建对象。

实例:展开

public class Student {
    // 声明变量
    private Student() {
    }

    // 声明变量
    private static Student s = null;

    // 提供对外方法
    public static Student getInstance() {
        if (s == null) {
            // 线程1就进来了,线程2就进来了。
            s = new Student();
        }
        return s;
    }
}

饿汉式和懒汉式的区别:

  • 饿汉式是类一加载进内存就创建好了对象;
  • 懒汉式则是类才加载进内存的时候,对象还没有存在,只有调用了getInstance()方法时,对象才开始创建。

懒汉式是延迟加载,如果多个线程同时操作懒汉式时就有可能出现线程安全问题,可以加同步来解决线程安全问题。但是加了同步之后,每一次都要比较锁,效率就变慢了,所以可以加双重判断来提高程序效率。

注:开发常用饿汉式,因为饿汉式简单安全。懒汉式多线程的时候容易发生问题

 

  • 扫扫关注公众号
  • weinxin
  • 扫扫体验小程序
  • weinxin
亦枫

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: