Java 基础面试300题 (1-50)
Java 基础知识300题,含答案。内容广泛,涵盖数据类型,运算符,字符串,日期时间处理、类和对象,内部类 ,错误处理,lambda表达式,多线程等等。解答深入浅出,并附有大量代码片段,易于理解。适合初学者厘清、深化并巩固java基础知识。
001. 什么是方法重载和方法重写?
当一个类中有两个或多个具有相同名称但参数个数或者类型不同的方法时,就会发生方法重载。如下代码所示:
java
public class Calculator {
public int add (int a, int b) {
return a+b;
}
public double add (double a, double b) {
return a+b;
}
public int add (int a) {
return a+a;
}
}
当子类中存在与超类方法具有相同名称和参数数量的方法时,就会发生方法重写。以下代码演示了这一点:
java
public class Animal {
public void saySomething () {
System.out.println("I am an animal");
}
}
public class Dog extends Animal {
public void saySomething () {
System.out.println("I am a dog");
}
}
002. 面向对象编程(OOP)有什么优点?
面向对象编程具有下面优点:
-
可重用性:继承、组合和多态性等OOP原则有助于重复使用现有代码
-
可扩展性:使用继承等OOP原则编写的代码使代码可扩展
-
安全:OOP原则,如封装,有助于将数据和在数据上操作的代码保持在一起,并使代码安全
-
简单性:Java类代表现实世界的对象。这使得代码非常容易理解
-
可维护性:使用OOP原则编写的代码更容易维护
003.编写一段代码,演示封装。
封装是指将数据和在数据上运行的代码作为一个单元保存在一起。简单地创建一个带有私有字段和公共getter/setter方法的类就是封装的一个例子。以下代码片段演示了这一点:
java
public class Laptop {
private String memory;
public String getMemory () {
return memory;
}
public String setMemory (String newMemory){
memory = newMemory;
}
}
在上述代码片段中,我们定义了一个名为Laptop
类,它有一个名为memory
的私有字段,以及用于访问/修改其值的公共getter/setter
方法。 memory
字段不能直接在类外访问,只能通过其getter/setter
方法访问,由此形成封装。
004. 继承关系有几种的类型?
继承关系指定了如何重用代码。 有两种类型的继承关系:
IS-A :IS-A关系是通过继承实现的。即通过创建一个子类。 例如,假设Camera
是一个子类,继承自类Electronics
。 在这种情况下,我们可以说,Camera
IS-A Electronics
电子产品
HAS-A :HAS-A关系可以通过组合创建,即通过创建与另一个类对应的字段。 假设在Camera
类中,有一个名为Battery
的对象。在这种情况下,我们可以说Camera
HAS-A Battery
的对象
005. 声明实例变量的最佳实践是什么?
实例变量应始终声明为私有, 并具有公共的getter/setter
方法。 这样首先可以保护实例变量,同时在程序员的控制下进行修改。 符合封装原则。
如果将实例变量声明为 public
则违反了封装原则,带来一些潜在的安全问题。 因为public
实例变量可以在类外进行修改,因此无法控制恶意的修改,或者无心之失导致的错误修改。 如果需要从子类访问实例变量,则可以声明变量为protected
。
006. 什么是单例类,如何实现?
如果一个类在任何给定时间只允许创建一个实例 , 该类被称为Singleton
类,其实例也称为单例。 Singleton
类可以通过私有构造函数和public getter
方法实现。如下代码示例:
java
public class MySingleton {
private static MySingleton mySingletonInstance;
private MySingleton () {
}
public static MySingleton getInstance () {
if (mySingletonInstance == null) {
mySingletonInstance = new MySingleton (); //
create the object only if it is null
}
return mySingletonInstance;
}
public void doSomething () {
System.out.println("I am here....");
}
//使用单例
public static void main(String a[]) {
MySingleton mySingleton = MySingleton.getInstance();
mySingleton.doSomething();
}
}
在上面代码片段中,我们定义了一个私有构造函数和一个公共的静态getInstance()
方法。该方法检查是否存在实例。如果实例不存在,它会使用私有构造函数创建一个实例。如果存在,则返回该实例。由于MySingleton
类的构造函数是私有的,因此任何需要使用该类实例的外部类都只能够通过getInstance()
方法获取它, 而该方法确保MySingleton
类只有一个实例。
007. 下面代码编译能够通过吗?,它是使用方法重载还是重写?
java
class Electronics {
public void displayPrice (Float price) {} //Line 1
}
class Camera extends Electronics {
public void displayPrice (String price) {} //Line 2
}
上面的代码没有错误,编译会通过。它使用了方法重载机制。相机类Camera
是电子产品类Electronics
的子类,两个类都有一个名为displayPrice()
的方法,但它们在参数类型上不同,因此是方法重载。
008.编写一段代码,演示方法重载 。
当一个类中有两个或多个方法具有相同名称但参数个数或类型不同时,就会发生方法重载。如下代码演示了这一点:
java
class Laptop {
public void displayPixel (int width, int height) { }; //Line 1
public void displayPixel (float width, float height) {}; //Line 2
}
在上面代码中,Laptop
类有两个方法,名称都是displayPixel
,但这两个方法的参数类型不同。第1行的displayPixel
方法有两个参数,均为int
类型。 第2行的displayPixel
方法也有两个参数,但都是float
类型。 因此,我们说displayPixel
方法发生了重栽。
009. 什么是多态? Java如何支持多态?
多态字面意思是"多种形式"。 它能够使用相同的接口来执行不同的代码。 Java通过方法重载和方法覆盖实现多态。 当类中有许多方法具有相同名称但参数数量/类型不同的时,就会发生方法重载。 编译器根据传递的参数个数和类型确定具体应该调用那个版本的重载方法。 当子类与超级类中的方法具有相同名称和类型签名时,就会发生方法重写。 根据运行时调用该方法的对象类型,可以确定具体调用的那个版本的重写方法。
010.Java如何实现抽象?
抽象是面向对象编程的重要概念。 它强调对象只应该暴露必要的信息给最终用户,而隐藏内部实现细节。Java通过抽象类和接口实现抽象。因此,可以在抽象类或接口中声明想要暴露给外部世界的方法。而实际实现则在继承抽象类或实现接口的具体类中。外部代码不知道实现类,不需要和实现类打交道,它们通过接口或抽象类与代码交互。
011. 如何定义Java主方法?
下面两种方式均可以声明Java主方法:
arduino
//方式一
public static void main (String argument [])
//方式二
static public void main (String argument [])
其中关键字public
、 static
和void
的顺序无关紧要。
012.如何定义Java类?
以下是声明Java类的几种方式:
kotlin
//方式a
public class Class1 {}
//方式b
class Class2 {}
//方式c
private class Class3 {}
//方式d
protected class Class4 {}
//方式e
static class Class5{}
方式a声明一个类为public
,方式 b 不使用任何访问修饰符,因此它具有默认访问权限,即只能在软件包内访问。 方式 c 和方式d分别使用private
和proected
的访问修饰符。 请注意,最外层的高级类不能使用这些修饰符。 这些修饰符只能用于内部类。 方式e 使用static
关键字。 同样,只有内部类才具有此访问修饰符。
013. 如何定义标识符?
标识符是给类、方法、变量或接口的名称。 Java标识符必须遵循下面几条规则:
- 标识符必须以字母表、美元符号
$
或下划线字符_
开头 - 标识符可以包含字母、数字、
$
和_
- 标识符不能包含特殊字符
- 标识符区分大小写
下面是一些合法的标识符示例:
- b
- $a
- book
- author1
- myName
- ___1_c
014.哪些修饰符不适用实例变量?
下面几个修饰符不适用实例变量:
abstract
关键字仅对类、接口和方法有效,不能用于实例变量。synchronized
和native
这两个关键字仅对方法有效,不能用于实例变量。void
关键字用来说明方法不返回任何值,也不能用于实例变量。
015.哪些修饰符适用方法声明?
声明(或定义)方法时,可以指定以下关键字:
public
:访问修饰符, 说明外部类可以访问该方法private
:访问修饰符,方法只能够在类内部访问protected
:访问修饰符,方法可以由子类访问static
:用于表示该方法是类级方法final
:用于表示无法覆盖该方法abstract
:用于指定子类应覆盖该方法native
:用于指定该方法在C++等另一种语言中实现synchronized
:用于指示一次只能由一个线程访问该方法
016. 引用变量未显式初始化时有默认值吗?如何在代码中处理?
当引用变量未显式初始化时,其值默认为null
空值。如果这些变量没有得到正确处理,可能会导致出现空指针异常,即NullPointerException
。为避免此类异常 ,您可以执行以下操作之一:
- 确保正确初始化变量
- 在代码中明确添加空值检查
- 使用Java 8可选项
017. 关键字"transient"和"native"有什么作用?
关键字transient
是一个实例变量修饰符。它告诉JVM在序列化对象时,应忽略该变量。
关键字native
是一个方法修饰符。它说明该方法由另外一种语言(不是java)实现,比如用C
或者C++
语言实现。
018.Java中有哪几种类型的注释?
注释是添加到代码中的一些文本,提供有关代码的附加信息。它描述了代码对阅读者的作用。编译器会忽略注释。Java支持以下3种类型的注释:
单行注释 :此类注释以两个正斜杠//
开头,在行尾结束。如下代码所示:
java
int count; //这存储对象的数量
多行注释 :以/*
开头,以*/
结尾。两者之间的任何内容都被视为注释,并被编译器忽略。 如下代码示例:
java
/*
This for loop repeats a block of code 10 times
*/
for (int i=0; i < 10; i++) {
}
文档注释 :文档注释是使用JavaDoc工具为代码生成文档的注释 。它们以/**
开头,以*/
结尾。如下代码示例:
java
/**
* Method that does something
* @param a
*/
public void myMethod (int a) {
}
019. 什么是JVM、JRE和JDK?
JDK代表Java开发工具包。它由编写和运行Java程序所需的工具和库组成。 它包括Java编译器、JRE和其他工具。
JRE代表Java运行时环境。它由运行Java程序所需的工具组成。它包含在JDK中,但也可以单独安装。如果在没有JDK的情况下自行安装JRE,可以运行Java程序,但无法编写和编译Java代码。JRE包括JVM和其他一些库。
JVM代表Java虚拟机。Java代码被编译成字节码,在JVM中执行。JVM提供了一个Java代码的执行环境。
020.如何读取用户输入的值? 用代码说明。
Java提供了一个名为java.util.Scanner
的类,可用于读取用户的输入。如下代码示例:
ini
System.out.println("Enter a Number:");
Scanner scanner = new Scanner (System.in);
int num = scanner.nextInt();
System.out.println("The entered number is ":num);
scanner.close();
上述代码创建一个与System.in
对应的Scanner
,这是标准输入。然后,它要求用户输入一个数字,并通过scanner.nextInt
方法将此数字读入变量num
,然后打印。以下是输出:
typescript
Enter a Number:
10
The entered number is 10
nextInt
方法用于读取整数。除此之外,Scanner
类还有其他几种方法,可用于读取不同类型的数据,如String、float、long、boolean
等。
021.有几种方法声明short
类型的数组?
以下任何一种方式都可以声明一个short
类型的数组:
ini
a. short a1[];
b. short [] a2;
c. short b1[] [];
d. short [] [] b2;
e. short [] c1 = {2,3,4};
f. short c2[] = {2,3,4};
方式a和方式b声明一个一维short
类型数组。方式c和方式d 声明一个二维short
类型数组。方式c和方式f 声明一个一维short
类型数组并赋初值。在上述所有方式中,方括号可以放在变量名之后或short
关键字之后。
022.如何将int
类型显式转换为byte
类型? 有必要吗 ?
下述方式将整数int
类型变量显式转换为 byte
类型 :
java
int i = 30;
byte bValue = (byte)i;
某些情况下,如果不显示将int
类型转换为byte
类型 ,会发生编译错误,如下所示:
java
byte bValue = i; //compilation error
这是因为与字节类型相比,int
类型是一个更广泛的类型。因此,Java无法自动将int
类型转换为byte
类型,需要显式转换。
023.举一个隐式类型转换例子
当一个值被分配给更广泛的数据类型的变量时,就会发生隐式转换。 而不需要显式转换。例如,如果将int
类型的值分配给long
类型变量,将发生隐式转换。如下代码片段所示:
java
int iValue = 250;
long lValue = iValue;
上述代码表明,int
类型变量总是可以分配给long
类型的变量,并且默认情况下进行转换。
024. 如何为short
类型分配浮点值?
以下代码演示了如何将浮点值转换为short
变量:
java
float fValue = 37000.02F;
short sValue = (short) fValue;
由于float
是比short
更广泛的数据类型,因此需要显式转换。
025.Java基本数据类型默认值是什么?
Java基本数据类型的默认值如下:
java
int = 0
double = 0.0d
float = 0.0f
boolean = false
long = 0L
char = 'u0000'
026. 下面代码中,哪些行会编译通过,哪些会编译错误?
java
int [] [] add = new int [2] []; //Line 1
int [] subtract = new int [2]; //Line 2
int iValue = 2; //Line 3
add[1] = subtract; //Line 4
add[1] = iValue; //Line 5
第1行没有错误,编译通过,因为它创建了一个二维数组。第2行也编译通过,它创建了一个一维数组。第3行在声明和初始化一个int
类型的变量,没有语法错误,编译通过。
在Java中,由于二维数组是数组的数组,因而第4行也编译通过,因为它将单维数组分配给二维数组中的另外一个维度。第5行会导致编译错误,因为无法将int
类型变量分配给int
类型的数组。
027.下述代码会编译通过吗?
java
public class Car {
public Car (String carName) {}
}
Car myCar = new Car("Benz");
Car [] myCars = { myCar, new Car("Ford"), new
Car("BMW")
};
上述代码没有问题,会编译通过。 这段代码首先创建一个Car
类 。然后创建了一个名为myCar
的Car
类对象,接着创建了一个名为myCars
的Car
类型数组,并用三个值初始化。 第一个值使用了myCar
变量,后两个值直接调用了Car
类的构造方法创建。
028. 以下哪些是初始化数组的正确方法?
java
a. int ttt = new int[3][3];
b. int[]ttt = new int[3][3];
c. int[][]ttt = new int[3][3];
d. int ttt [3][3] = new int[][];
在Java中,声明多维数组的正确语法是:
java
int[][]ttt =new int[3][3];
因此,答案c
是正确的。
029. Java中有哪几种基础数据类型?
Java中有八种基础数据类型,如下:
类型 | 宽度(位) | 范围 |
---|---|---|
byte | 8 | -128 到 127 |
short | 16 | -32,768 到 32,767 |
int | 32 | -2,147,483,648 到 2,147,483,647 |
long | 64 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 |
float | 32 | 1.4e--045 到 3.4e+038 |
double | 64 | 4.9e--324 到 1.8e+308 |
char | 16 | 0 到 65536 |
boolean | 1 | true/false |
030. 如何为浮点数变量赋值?
下面是几种方式为浮点类型的变量赋值:
java
float fValue1 = 100.5f;
float fValue2 = 100.5F;
float fValue3 = (float) 100.5;
默认情况下,十进制数字被视为double
。 因此,为了将其分配给浮点数变量,后面需要跟f
或F
,或者应该明确地将其转换为浮点数类型。
031. 下面代码片段中有什么错误,如何修复?
java
byte bValue = 10; //line 1
bValue = bValue + 10; //line 2
上述代码中第2行的是错误的, 它直接将int
类型的值分配给byte
变量, 会导致编译错误。可以通过以下显式转换来修复:
java
bValue = (byte) (bValue + 10);
032.如何使用八进制和十六进制字面量为int
变量赋值?
可以将八进制和十六进制字面量赋值给一个int
变量,如下所示:
ini
int iValue1 = 0100;
int iValue2 = 0xf1dead;
八进制字面量需要以0
为前缀,其值只能使用数字0-7
十六进制字面量需要以0x
作为前缀。其值可以使用数字0-9
和字符A、B、C、D、E
和F
,大小写无关。
033.下面代码片段中,哪些将编译通过,哪些会导致错误?
java
char cValue1 = '@'; //Line 1
char cValue2 = '\u004E'; //Line 2
char cValue3 = 80000; //Line 3
char cValue4 = (char)--100; //Line 4
char cValue5 = 128; //Line 5
除第3行外,所有行都将编译通过。第3行导致编译错误,因为char
类型的是0到65,536。正确的做法是显示转换:
java
char cValue3 = (char) 80000;
034.执行下面代码时会发生什么?
java
int [] iArray = new int[5];
int iInt = --5;
iArray[iInt] = 5;
上面代码执行时会抛出一个ArrayIndexOutofBoundsException
(数组索引越界异常),这是因为iInt
是一个负数,不能用作数组索引。
035. 下面代码片段中的输出是什么?
java
public class ArrayDec {
public static void main (String argv[]){
ArrayDec ad = new ArrayDec ();
ad.arrayMethod();
}
public void arrayMethod () {
int intArr1[]= {1,2,3};
int[] intArr2 = {1,2,3};
int intArr3[] = new int[] {1,2,3};
System.out.print(intArr3.length);
}
}
上面的代码的输出3
。由于所有数组声明都正确,编译会通过。它打印数组intArr3
的长度,也就是3
。
036. 下面代码片段中,哪些会编译错误,为什么?
java
String str = "Hello";
long lng = 99;
double dbl = 1.11;
int i = 1;
int j = 0;
j = i << str; //line 1
j = i << j; //line 2
j = i << dbl; //line 3
j = i << lng; //line 4
第1行和第3行将导致编译错误,因为它们使用不兼容的基本数据类型。第1行使用<<
与int
和字符串值,而第3行使用具有int
和double
值的<<
运算符。
037.以下代码的输出是什么?
java
int x = --1;
x = x>>>24;
System.out.println(x);
上述代码输出是:255
。 解释如下:
代码首先将 整数变量x
设置为-1
,从二进制来说,实际上是将所有32位均设置为1
,然后将值向右移动24位,用0填充前24位,不考虑符号扩展。最终结果是将x设置为255
。
arduino
11111111 11111111 11111111 11111111 //--1的二进制 表示
//向右移动24位后
00000000 00000000 00000000 11111111 //255 的二进制表示
038. 如何连接字符串?
与字符串一起使用时, +
运算符可用于字符串连接。如下代码示例:
java
String sum = "one" + "two";
在上述代码中,字符串one
和 two
被连接,并将结果分配给字符串变量sum
。sum
的最最终值是 onetwo
039.以下代码的输出是什么?
java
public class Test {
public static void main (String argument[]) {
String name = "Java";
int iInt1 = 100;
int iInt2 = 200;
System.out.println(name + iInt1 + iInt2);
}
}
上述代码编译正确,运行后产生以下输出:Java100200
由于+
运算符与字符串和int
变量一起使用,最终值被视为字符串,并将结果连接并显示。
040. %
是什么运算符?
%
运算符被称为取模运算符或者余数运算符。 它返回将左边的数字除以右边的数字后得到的余数。 如下代码示例:
java
100 % 4 //produces 0
10 % 4 // produces 2
041.以下代码的输出是什么?
java
final int iIntFinal = 100;
int iValue = iIntFinal ++;
System.out.println("输出是:"+iValue);
上述代码会导致编译错误。因为代码试图使用增量运算符来增加常量 iIntFinal
, iIntFinal
是常量,无法修改。
042. 编译和执行以下代码时会发生什么?
java
int iValue = 100;
if(iValue = 100) { //line 1
System.out.println("iValue is: "+iValue);
}
上述代码中, 第1行在条件判断中使用了赋值运算符=
,而不是比较运算符==
, 其结果是将值100分配给变量iValue
,并产生int
结果, 而If
条件语句需要一个布尔值来测试,因此会导致编译错误。
043.以下代码会编译吗?如果是,输出是什么?
java
int [] iArray = new int[10];
if(iArray instanceof Object) { //Line 1
System.out.println("对象");
}
else
{
System.out.println("整数");
}
上述代码编译良好。 因为数组是一个对象实例。 因此,当第1行执行时,if
条件返回true
,从而显示以下输出:对象
044. 下面代码的输出是什么?
java
int iValue = 10;
int jValue = 20;
if(iValue && jValue) { //line 1
System.out.println("True will be printed...");
}
else {
System.out.println("False will be printed...");
}
上述代码中的第1行将导致编译错误。因为运算符&&
是逻辑AND
运算符,它只能接受布尔操作数,而不能接受 int
类型。
045. 下面代码的输出是什么,为什么?
java
System.out.println("Output is "+ ((10 <
100) ^ (100 > 10)));
上述代码将产生以下输出:
csharp
Output is false
因为 10 < 100
返回 true
,也就是 1
, 而100 >10
也返回 true
,还是 1
。
运算符^
是逻辑异或操作,即XOR
操作, 当两个值相同时, 返回0
,否则返回1
。由于两个值都是true
,XOR
运算符返回0
,对应于false
。
046.以下代码片段输出是什么?
java
if(!(100 == 1000)) { //line 1
System.out.println("TRUE gets printed");
}
else {
System.out.println("FALSE gets printed");
}
上述代码中,第1行检查条件100 == 1000
,结果false
, 运算符 !
是逻辑NOT
运算符,它返回其操作数的相反值,在上面情况下,它返回 true
。因此,if
检查条件的最终求值为true
,从而产生以下输出:
objectivec
TRUE gets printed
047.解释增量和减量运算符。
增量运算符由两个加号表示。它将操作数增加1
。如下代码所示:
java
int i = 10;
i++; // sets i to 11
减量运算符由两个减号表示。它将操作数递减1。如下代码所示 :
java
int i = 10;
i-- ; // sets i to 9
048.解释instanceOf
运算符。
instanceOf
运算符可用于检查对象是否是某个类的实例。它返回一个布尔值。如果被比较的两个对象都是同一类型,则返回true
,否则返回false
。如下代码所示:
java
String str = "Hello";
str instanceof String //returns true
String str = null;
str instanceof String //returns false
049. 用代码示例解释三元运算符。
三元运算符也称为条件运算符, 它是if-else
语句的简写形式。 语法如下:
makefile
condition?TruePath:FalsePath
它评估一个条件,如果为true
,它将执行 ?
符号之后的代码,如果为false
,它将执行 :
符号之后的代码。如下代码 所示:
java
int iValue2 = (iValue1 == 100) ? 200: 100; //Line 1
上述代码中使用了三元运算符,它首先评估iValue==100
条件, 由于该条件为true
,因此执行?
后的代码,从而将200
分配给变量iValue2
050.Java中有哪些条件语句?
条件语句对表达式进行评估,并根据评估结果执行不同的代码块。Java中有两个条件语句:
if
语句:if else
是一个条件分支语句。它测试一个条件,根据条件是否为真,执行不同的代码路径 。语法如下:
java
if(condition){
//some code here
}
else { // this part is optional
//some code here
}
另外一个条件语句是switch
语句。 switch
语句是一个多路分支语句。可以使用它代替几个if else
语句,简化代码。switch
语句将指定的表达式与每个case
值匹配。一旦找到匹配项,将执行该case
语句中的代码逻辑。switch
语句具有以下语法:
java
switch(expression) {
case value1:
//some code
break; //optional
case value2:
//some code
break; //optional
.....
case valuen:
//some code
break; //optional
default:
//some code
break; //optional
}
}