您的位置:首页 > 编程语言 > Java开发

关于Java基础的一些笔试题总结

2016-04-09 18:16 411 查看
针对最近腾讯、京东、网易等公司的笔试,遇到一些有关Java基础的问题,在此总结,希望能通过这几道经典问题题发散,举一反三,借此打牢基础!自己总结,望提出宝贵意见!


一、关于null的一道小题 

先开开胃,一道很有意思的笔试题,题目如下: 

下面这段代码能正确执行吗?如果能,输出什么?
public class NULL {
public static void haha(){
System.out.println("haha");
}
public static void main(String[] args) {
((NULL)null).haha();
}
}
1
2
3
4
5
6
7
8

答案是能正确运行!大家看出来答案用了多久?相信大家都比我强。我第一次看到这个表达式,脑子一片蒙蔽,后来仔细分析代码,大写的NULL是类名,括号是对null做类型强转,然后调用NULL类的静态方法。 

输出为:

haha

因为null值可以强制转换为任何java类类型,例如(String)null也是合法的。但null强制转换后是无效对象,其返回值还是为null,而static方法的调用是和类名绑定的,不借助对象进行访问,所以能正确输出。反过来,没有static修饰就只能用对象进行访问,使用null调用对象肯定会报空指针错了。这里和C++很类似。 

更多null相关知识可参看博客深入理解Java关键字null

二、有关类加载机制的 静态块、块、构造方法 执行顺序问题 

很经典、很基础的问题,题目如下:下面这段代码输出什么?
class HelloA {

public HelloA() {
System.out.println("HelloA");
}

{ System.out.println("I'm A class"); }

static { System.out.println("static A"); }

}

public class HelloB extends HelloA {
public HelloB() {
System.out.println("HelloB");
}

{ System.out.println("I'm B class"); }

static { System.out.println("static B"); }

public static void main(String[] args) {
     new HelloB();
   }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

直接看输出结果:

static A 

static B 

I’m A class 

HelloA 

I’m B class 

HelloB

可见其执行顺序为: 

1、父类静态块 

2、子类静态块 

3、父类块 

4、父类构造器 

5、子类块 

6、子类构造器

关于对于静态块,只能出现在类中,不能出现在任何方法中,且可以写在类中的任意位置(除了方法中),执行顺序与代码位置顺序一致!

如果想要深入其中原理,则需要了解static与Java的类加载机制,推荐海子的博客Java中static关键字解析,推荐一本书《深入理解Java虚拟机》,ImportNew博文Java虚拟机类加载机制,其中有一段代码与上述问题类似,但更深入,题目如下:

下面这段代码输出什么?
public class SSClass
{
static
{
System.out.println("SSClass");
}
}
public class SuperClass extends SSClass
{
static
{
System.out.println("SuperClass init!");
}

public static int value = 123;

public SuperClass()
{
System.out.println("init SuperClass");
}
}
public class SubClass extends SuperClass
{
static
{
System.out.println("SubClass init");
}

static int a;

public SubClass()
{
System.out.println("init SubClass");
}
}
public class NotInitialization
{
public static void main(String[] args)
{
System.out.println(SubClass.value);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

运行结果:

SSClass 

SuperClass init! 

123

答对了么? 

也许有人会疑问:为什么没有输出SubClass init。ok~解释一下:对于静态字段,只有直接定义这个字段的类才会被初始化,因此通过其子类来引用父类中定义的静态字段,只会触发父类的初始化而不会触发子类的初始化。 

上面就牵涉到了虚拟机类加载机制。如果有兴趣,可以继续看下去。

三、Java参数传递机制问题

下面这段程序运行结果是什么?
public class Example {

String str = new String("good");
char[] ch = { 'a', 'b', 'c' };

public static void main(String args[]) {
Example ex = new Example();
ex.change(ex.str, ex.ch);
System.out.print(ex.str + " and ");
System.out.print(ex.ch);
}
public void change(String str, char ch[]) {
str = "test ok";
ch[0] = 'g';
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

选项:

A、 good and abc 

B、 good and gbc 

C、 test ok and abc 

D、 test ok and gbc

答案为B,如果你选对了,那么你对Java中的参数传递机制已经了熟于胸了,有人可能以为Java中String和数组都是对象所以传递的肯定是对象引用类型,然后就会选D,其实这是个很大的误区:因为在java里没有引用传递,只有值传递。 

这个值指的是实参的地址的拷贝,得到这个拷贝地址后,你可以通过它修改这个地址的内容(引用不变),因为此时这个内容的地址和原地址是同一地址,但是你不能改变这个地址本身使其重新引用其它的对象,也就是值传递,可能说的不是很清楚,那么请看这个博文吧两段交换代码轻松理解Java参数传递机制,看了一定会明白!

四、Integer与int的’==’比较问题 

关于这方面的问题笔试遇到很多次,直接看代码,请问下面这段代码输出什么?
public class Test {
public static void main(String[] args) {

Integer a = new Integer(1);
Integer b = new Integer(1);
int c=1;
Integer e = 1;
System.out.println("a==b:"+(a==b));
System.out.println("a==c:"+(a==c));
System.out.println("a==e:"+(a==e));
System.out.println("c==e:"+(c==e));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

输出结果如下:

a==b:false 

a==c:true 

a==e:false 

c==e:true

解释: 

1、a与b是两个引用类型变量,他们的值是为两个对象在堆内存中的分配空间的首地址,存放在栈的局部变量表中。 

new了两个对象,堆内存中存放位置一定不同,所以a和b也一定不同,故false。 

2、c与d为值类型,其值就存放在栈中,与堆内存无关,所以引用类型a与值类型c比较,a自动拆箱为int,与c进行值比较,故true。 

3、而Integer d = 1;这条语句比较特殊,它是调用Integer.valueOf(1)自动装箱进Integer对象d,但这里有个很关键的问题,参看valueOf源码:
public static Integer valueOf(int i) {
final int offset = 128;
if (i >= -128 && i <= 127) { // must cache
return IntegerCache.cache[i + offset];
}
return new Integer(i);
}
1
2
3
4
5
6
7

-128 ~ 127 这个范围内的数被Java缓存,类似一个线程池或连接池之类的结构。 

如果valueOf的数在这个范围之内的话,取到的就是同一个对象,用 == 来比较的话 结果就是true了; 

否则就是两个new Integer(i) 的赋值语句,也就是创建了两个Integer对象,自然用 == 来比较的话 结果就是false了。 

4、两个值类型比较,自然是true!

如果想深入了解Integer与自动装箱与拆箱知识,可参看博客 Java包装类、自动装箱与拆箱知识总结 

刚刚这道题涉及了Java变量类型与内存机制的知识,可以参看博客Java内存机制学习笔记

五、Java字段的输出问题 

下面这段代码中成员变量与main()方法中定义的变量mainInt都未经过初始化,那么哪些变量能正确输出,输出什么,又有哪些输出语句不能通过编译呢?
public class Test {
//定义成员变量
public int testInt;
public float testFloat;
public boolean testBoolean;
public String testString;

public static void main(String[] args){
/*
*   如果在main方法里定义一个变量,而不进行初始化就输出,是不能通过编译的!
*   int mainInt;
*   System.out.println(mainInt);
*/
Test test = new Test();
System.out.println(test.testInt);
System.out.println(test.testFloat);
System.out.println(test.testString);
System.out.println(test.testBoolean);

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

而如上述注释的部分中,在Java中如果在main方法里定义一个变量而不初始化,是不能通过编译的! 

而对于类成员变量,类加载器为我们做了初始化的工作。输出如下:



0.0 

null 

false

六、Java构造方法问题

如下题,“下面代码中,方法一与方法二中不执行任何代码执行”,这句话对不对?
public class FatherClass {
//方法一
public FatherClass() {
}
}

public class SonClass extends FatherClass {
//方法二
public SonClass() {
}

public static void main(String[] args) {
new SonClass();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

答:自然是不对的! 

Java中对构造函数是不继承的,只是调用(显式或隐式); 

所以如果创建子类对象,那么首先调用父类的构造方法,如何调用?系统会在子类构造方法中隐式的添加一句super();所以方法二处并不是没有任何代码执行! 

那么方法一处有代码执行吗?有人可能以为它是父类就不用执行代码了。但Java中,所有类都是Object的子类!方法一处仍然需要调用父类构造方法!

这里贴出对Java构造方法的一些总结: 

总结1:构造函数不能继承,只是调用而已。 

总结2: 

如果父类没有显式编写任何构造方法,那么系统会自动添加一个无参的构造方法,如果父类显式编写了有参的构造方法,那么系统将不自动添加无参构造方法; 

则在创建子类对象时,不能通过编译,除非在子类构造方法代码体中第一行,必须是第一行显式调用父类有参构造函数! 

如下: 

SonClass (){ 

super(777);//显示调用父类有参构造函数 



如果不显式调用父类有参构造函数,系统会默认调用父类无参构造函数super(); 

但是父类中没有无参构造函数,那它就不能调用了。所以编译就无法通过了。

七、关于抽象类与接口

abstract与interface很重要,笔试时多以概念判断对错形式出题。在做一些总结!

1、抽象类 

对于抽象类有“三必须”与“五不能”。

三必须(三种情况必须定义为抽象类): 

a、一个类中直接定义了一个或多个抽象方法; 

b、一个类继承了一个抽象父类,但没有实现父类中的抽象方法; 

c、一个类实现了一个接口,但没有完全实现接口包含的抽象方法;

五不能: 

a、抽象类不能被实例化(即抽象类不能被new); 

b、abstract与final永远不能同时使用(final修饰的类不能被继承,修饰的方法不能被重写;而abstract修饰的类只能被继承才有意义,修饰的方法必须被重写才有意义); 

c、abstract与static不能同时修饰方法(static修饰的方法属于类本身,如果抽象方法被static修饰,通过类调用该方法时会因为没有方法体而出错); 

d、abstract与private不能同时使用(abstract修饰的方法必须重写才有意义,而private使访问权限受限); 

e、abstract不能修饰变量(即没有抽象变量);

2、接口 

接口是彻底化的抽象类。 

需要注意的是: 

a、一个接口可以有多个父接口,但接口只能继承接口,不能继承类; 

b、接口里的方法全是抽象方法(public abstract); 

c、接口里定义的字段(Field)只能是是常量(public static final);

3、抽象类与接口相似之处 

a、抽象类与接口不能被实例化,只能被其他类继承或实现; 

b、抽象类和接口都可以包含抽象方法,抽象类的继承类与接口的实现类都必须实现父类中的抽象方法;

4、抽象类与接口的主要区别 

a、设计目的区别:抽象类体现的是一种模板式的设计,用户可以在这个基础上增加完善功能;而接口体现的是一种规范,用户只能且必须完成这个规范;

b、抽象类可以包含普通方法,而接口不可以; 

c、Java中一个类只能有一个直接父类,但一个类可以实现多个接口,接口从某种程度上说弥补了Java单继承的不足; 

d、抽象类可以包含构造器,用于抽象类的初始化,而接口不可以;

(未完待续)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 面试题