java面试题收集整理(2)

本文详细梳理了Java面试中常见的核心概念,包括作用域、重载与覆盖的区别、构造器、接口与抽象类的关系、多态实现、抽象方法与static、native的限制、以及String对象的特性等。此外,还探讨了错误与异常、同步异步的使用场景,旨在帮助求职者深入理解Java语言的关键知识点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、请说出作用域 public,private,protected,以及不写时的区别

这四个作用域的可见范围如下表所示。
作用域
当前类        同一 package       子孙类        其他 package
public
√                            √                       √                         √
protected
√                           √                         √                        ×
private

√                           ×                           ×                       ×

如果未指定,则有包访问权限。在当前类和同一包内可访问

2、Overload 和 Override 的区别。Overloaded 的方法是否可以改变返回值的类型?

Overload 是重载的意思,Override 是覆盖的意思,也就是重写。
重载 Overload 表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即
参数个数或类型不同, 注:返回值类型不同不能区分)。
重写 Override 表示子类中的方法可以与父类中的某个方法的名称和参数完全相同,通过子类创
建的实例对象调用这个方法时,将调用子类中的定义方法,这相当于把父类中定义的那个完全
相同的方法给覆盖了,这也是面向对象编程的多态性的一种表现。子类覆盖父类的方法时,只
能比父类抛出更少的异常,或者是抛出父类抛出的异常的子异常,因为子类可以解决父类的一
些问题,不能比父类有更多的问题。 子类方法的访问权限只能比父类的更大,不能更小。如果
父类的方法是 private 类型,那么,子类则不存在覆盖的限制,相当于子类中增加了一个全新的
方法。
至于 Overloaded 的方法是否可以改变返回值的类型这个问题,要看你倒底想问什么呢?这个题
目很模糊。如果几个 Overloaded 的方法的参数列表不一样,它们的返回者类型当然也可以不一
样。但我估计你想问的问题是:如果两个方法的参数列表完全一样,是否可以让它们的返回值
不同来实现重载 Overload。这是不行的,我们可以用反证法来说明这个问题,因为我们有时候
调用一个方法时也可以不定义返回结果变量,即不要关心其返回结果,例如,我们调 用
map.remove(key)方法时,虽然 remove方法有返回值,但是我们通常都不会定义接收返回结果的
变量,这时候假设该类中有两个名称和参数列表完全相同的方法,仅仅是返回类型不同,java
就无法确定编程者倒底是想调用哪个方法了,因为它无法通过返回结果类型来判断。
override 可以翻译为覆盖,从字面就可以知道,它是覆盖了一个方法并且对其重写,以求达到不
同的作用。对我们来说最熟悉的覆盖就是对接口方法的实现,在接口中一般只是对方法进行了
声明,而我们在实现时,就需要实现接口声明的所有方法。除了这个典型的用法以外,我们在
继承中也可能会在子类覆盖父类中的方法。在覆盖要注意以下的几点:
1、覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果;
2、覆盖的方法的返回值必须和被覆盖的方法的返回一致;
3、覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;
4、被覆盖的方法不能为 private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。

overload 对我们来说可能比较熟悉,可以翻译为重载,它是指我们可以定义一些名称相同的方
法,通过定义不同的输入参数来区分这些方法,然后再调用时,VM 就会根据不同的参数样式,
来选择合适的方法执行。在使用重载要注意以下的几点:
1、在使用重载时只能通过不同的参数样式。例如,不同的参数类型,不同的参数个数,不同的
参数顺序(当然,同一方法内的几个参数类型必须不一样,例如可以是 fun(int,float),但是不能
为 fun(int,int));
2、不能通过访问权限、返回类型、抛出的异常进行重载;
3、方法的异常类型和数目不会对重载造成影响;
4、对于继承来说,如果某一方法在父类中是访问权限是 priavte,那么就不能在子类对其进行重
载,如果定义的话,也只是定义了一个新方法,而不会达到重载的效果。

3、构造器 Constructor 是否可被 override?

构造器 Constructor 不能被继承,因此 不能重写 Override,但可以被重载 Overload

4、接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承具体类

(concrete class)? 抽象类中是否可以有静态的 main 方法?

接口可以继承接口。抽象类可以实现(implements)接口,抽象类可继承具体类。抽象类中可
以有静态的 main 方法。
备注:只要明白了接口和抽象类的本质和作用,这些问题都很好回答,你想想,如果你是 java
语言的设计者,你是否会提供这样的支持,如果不提供的话,有什么理由吗?如果你没有道理
不提供,那答案就是肯定的了。

只有记住抽象类与普通类的唯一区别就是不能创建实例对象和允许有 abstract 方法

5、写 clone()方法时,通常都有一行代码,是什么?

clone 有缺省行为,super.clone();因为首先要把父类中的成员复制到位,然后才是复制自己的成
员。

6、java 中实现多态的机制是什么?

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

7、abstract class 和 interface 有什么区别?

含有 abstract 修饰符的 class 即为抽象类,abstract 类不能创建的实例对象。含有 abstract 方法的
类必须定义为 abstract class,abstract class 类中的方法不必是抽象的。abstract class 类中定义抽
象方法必须在具体(Concrete)子类中实现,所以,不能有抽象构造方法或抽象静态方法。如果的
子类没有实现抽象父类中的所有抽象方法,那么子类也必须定义为 abstract 类型。

接口(interface)可以说成是抽象类的一种特例,接口中的所有方法都必须是抽象的。接口中的
方法定义默认为 public abstract 类型,接口中的成员变量类型默认为 public static final。
下面比较一下两者的语法区别:
1.抽象类可以有构造方法,接口中不能有构造方法。
2.抽象类中可以有普通成员变量,接口中没有普通成员变量
3.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的
普通方法。
4. 抽象类中的抽象方法的访问类型可以是 public,protected 和(默认类型,虽然
eclipse 下不报错,但应该也不行),但接口中的抽象方法只能是 public 类型的,并且默认即为
public abstract 类型。
5. 抽象类中可以包含静态方法,接口中不能包含静态方法
6. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,
但接口中定义的变量只能是 public static final 类型,并且默认即为 public static final 类型。
7. 一个类可以实现多个接口,但只能继承一个抽象类。

8、abstract 的 method 是否可同时是 static,是否可同时是 native

abstract 的 method 不可以是 static 的,因为抽象的方法是要被子类实现的,而 static 与子类扯不
上关系!
native 方法表示该方法要用另外一种依赖平台的编程语言实现的,不存在着被子类实现的问题
所以,它也不能是抽象的,不能与 abstract 混用。例如,FileOutputSteam 类要硬件打交道,底
层的实现用的是操作系统相关的 api 实现,例如,在 windows 用 c 语言实现的,所以,查看 jdk
的源代码,可以发现 FileOutputStream 的 open 方法的定义如下:
private native void open(String name) throwsFileNotFoundException;
如果我们要用 java 调用别人写的 c 语言函数,我们是无法直接调用的,我们需要按照 java 的要
求写一个 c 语言的函数,又我们的这个 c 语言函数去调用别人的 c 语言函数。由于我们的 c 语
言函数是按 java 的要求来写的,我们这个 c语言函数就可以与 java 对接上,java 那边的对接方
式就是定义出与我们这个 c函数相对应的方法,java 中对应的方法不需要写具体的代码,但需
要在前面声明 native。

9、super.getClass()方法调用

下面程序的输出结果是多少?
import java.util.Date;
public
classTest extends Date{
public static void main(String[] args){
new Test().test();
}
public void test(){
System.out.println(super.getClass().getName());
}
}
很奇怪,结果是 Test
这属于脑筋急转弯的题目
在 test 方法中,直接调用 getClass().getName()方法,返回的是 Test 类名
由于 getClass()在 Object 类中定义成了 final,子类不能覆盖该方法,所以,在
test 方法中调用 getClass().getName()方法,其实就是在调用从父类继承的 getClass()方法,等效
于调用 super.getClass().getName()方法,所以,super.getClass().getName()方法返回的也应该是
Test。
如果想得到父类的名称,应该用如下代码:
getClass().getSuperClass().getName();

10、String s = "Hello";s = s + " world!";这两行代码执行后,原始的 String 对象中的内容到底变了没有?

没有。因为 String 被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。在这段
代码中,s 原先指向一个 String 对象,内容是 "Hello",然后我们对 s 进行了+操作,那么 s 所指
向的那个对象是否发生了改变呢?答案是没有。这时,s 不指向原来那个对象了,而指向了另一
个 String 对象,内容为"Hello world!",原来那个对象还存在于内存之中,只是 s 这个引用变量
不再指向它了。
通过上面的说明,我们很容易导出另一个结论,如果经常对字符串进行各种各样的修改,或者
说,不可预见的修改,那么使用 String 来代表字符串的话会引起很大的内存开销。因为 String
对象建立之后不能再改变,所以对于每一个不同的字符串,都需要一个 String 对象来表示。这
时,应该考虑使用 StringBuffer 类,它允许修改,而不是每个不同的字符串都要生成一个新的对
象。并且,这两种类的对象转换十分容易。
同时,我们还可以知道,如果要使用内容相同的字符串,不必每次都 new 一个 String。例如我
们要在构造器中对一个名叫 s 的 String 引用变量进行初始化,把它设置为初始值,应当这样做:
public class Demo {
private String s;
...
public Demo {
s = "Initial Value";
}
...
}
而非
s = new String("Initial Value");
后者每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为 String
对象不可改变,所以对于内容相同的字符串,只要一个 String 对象来表示就可以了。也就说,
多次调用上面的构造器创建多个对象,他们的 String 类型属性 s 都指向同一个对象。
上面的结论还基于这样一个事实:对于字符串常量,如果内容相同,Java认为它们代表同一个
String 对象。而用关键字 new 调用构造器,总是会创建一个新的对象,无论内容是否相同。

11、是否可以继承 String 类?

String 类是 final 类故不可以继承。

12、String s = new String("xyz");创建了几个 String Object? 二者之间有什么区别?

两个或一个,”xyz”对应一个对象,这个对象放在字符串常量缓冲区,常量”xyz”不管出现多少
遍,都是缓冲区中的那一个。New String 每写一遍,就创建一个新的对象,它一句那个常量”xyz”
对象的内容来创建出一个新 String 对象。如果以前就用过’xyz’,这句代表就不会创建”xyz”自己
了,直接从缓冲区拿。

13、下面这条语句一共创建了多少个对象:String s="a"+"b"+"c"+"d";

答:对于如下代码:
String s1 = "a";
String s2 = s1 + "b";
String s3 = "a" + "b";
System.out.println(s2 == "ab");
System.out.println(s3 == "ab");
第一条语句打印的结果为 false,第二条语句打印的结果为 true,这说明 javac 编译可以对字符串
常量直接相加的表达式进行优化,不必要等到运行期去进行加法运算处理,而是在编译时去掉
其中的加号,直接将其编译成一个这些常量相连的结果。
题目中的第一行代码被编译器在编译时优化后,相当于直接定义了一个”abcd”的字符串,所以,
上面的代码应该只创建了一个 String 对象。写如下两行代码,
String s = "a" + "b" + "c" + "d";
System.out.println(s == "abcd");
最终打印的结果应该为 true。

14、error 和 exception 有什么区别?

error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程
序能处理这样的情况。 exception 表示一种设计或实现问题。也就是说,它表示如果程序运行
正常,从不会发生的情况。

15、同步和异步有何异同,在什么情况下分别使用他们?举例说明。

如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据
可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。
当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法
的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值