java基础:常见的概念问题,面试笔试可能会遇到哟

来源:互联网 发布:范思哲男装高仿淘宝 编辑:程序博客网 时间:2024/06/05 10:59

这几天在csdn上闲看大家的提问,发现有一些概念自己也顶不真,整理一下,以备不时之需。

  • 直接量
    直接量就是程序中通过代码直接给定的值。java的语法支持为8中基本类型、字符串和null指定直接量。其中null类型比较特殊,只有一个值null。
    关于直接量容易记错也是面试考试中经常命中的一点就是它的存储问题,典型的如下:

    String s1 = “nihao”;
    String s2 = “nihao”;
    String s3 = “ni” + “hao”;
    System.out.println(s1 == s2);
    System.out.println(s1 == s3);

初学概念不清的童鞋可能会觉得是三个指向不同堆内存的引用,而给出错误的答案。实质上,上面的两条输出均为true,这是因为String是一种典型的不可变类,它创建出来的对象不可能被改变,java设计者考虑到内存等原因,采用常量池来缓存这些直接量。当程序第一次使用某个字符串常量时,即把该常量放入常量池中,后续调用直接取出即可。

常量池,顾名思义就是一些不会被改变的数据,它是随着java文件编译时即被确定好并保存在.class文件内的,包括一些类、方法、接口中定义的常量,当然也包括字符串直接量。它最牛掰的地方是可以确保每一个字符串常量有且仅有一个。所以上面给的字符串s1,s2,在编译已经定型,它俩都对应常量池中的”nihao”,所以s1等于s2,而s3本身也是常量,编译时也被确定解析成”nihao”,这也是使用直接量生成String的效率高于StringBuilder和StringBuffer的原因
(String str=”ni”+”hao”的效率高于new StringBuilder().append(“ni”).append(“hao”))。因此,三者都指向常量池中的”nihao”,输出结果自然相等。

  • Integer和Long的小数值缓存
    列为看官再看一眼下面的问题:

    Integer a = 100;
    Integer b = 100;
    Integer c = 128;
    Integer d = 128;
    System.out.println(a == b); // 1
    System.out.println(a == 100); // 2
    System.out.println(c == d); // 3
    System.out.println(c == 128); // 4

会输出什么呢?不注意的话还真容易答错。2、4毫无疑问为true,实际上除了3是false之外,其余的均是true。你可能会觉得奇怪,为什么两个值为100的Integer对象相等,而两个128却不相等?

原来java提供的8个封装类和String一样,也是不可变类。设计者为了避免重复创建相同的对象而提供了一个内置的缓存IntegerCache,对值介于-128到127的Integer实例进行缓存,有兴趣的可以看源码,所以a,b指向的实际是同一个对象,c,d则不是。同样的设计也存在于Long中,Float则没有。

  • 初始化块
    看官们创建类的时候,是不是会经常加入一些无需参数的初始化行为?

初始化块可看做类中的第四种成员(成员变量、方法、构造器),常用{}括起来,里面可包括任何可执行的语句。一般可分为普通初始化块和静态初始化块,前者对应的是对象的初始化,后者对应的是类的初始化,两者的区别明白了吧。

对象的初始化,是在创建对象时,才会执行的。java里面创建一个对象时,如果该类没有加载,则会先加载类;若已加载,系统就会为该对象的所有变量分配内存,接着才会为这些变量执行初始化(包括成员变量初值和代码块),最后才会执行构造器。

类的初始化就更屌了,使用static修饰,作用于对整个类(类变量),在类初始化阶段已经完成,而不是在创建对象时才执行,因此一般情况下,静态初始化块总是比普通初始化块先执行。

有些面试或考试中经常会把构造器加进去,给你制造麻烦,比如让讨论执行顺序的问题。

讨论顺序之前,我们先明确一下几个概念或者结论:

  1. java中允许多个普通初始化块和静态初始化块同时出现,但这样实际编程中并不推荐,因为完全可以将其合并起来,以免代码雍蔽不堪;
  2. 同种初始化块的执行顺序依赖于它的排列顺序;
  3. 成员变量实际也是一种普通代码块;
  4. JVM中首次使用某个类,会经历加载、连接、初始化三个步骤。我们关注的第三个步骤中加入该类的直接父类没有被初始化,则程序会先初始化该父类,如果父类的父类还没有初始化,就继续往上追寻,直到Object类,等父辈的都搞定了,然后才是自己。这一点与构造器十分类似。

有了上面的基础,小试牛刀一下,列为看管你道是下面会有什么输出?

public class GrandFather {static {    System.out.println("爷爷的静态初始化块");}{    System.out.println("爷爷的普通态初始化块");}GrandFather() {    System.out.println("爷爷的无参构造器");}}class Dad extends GrandFather {{    System.out.println("父亲的普通态初始化块");}static {    System.out.println("父亲的静态初始化块");}Dad() {    System.out.println("父亲的无参构造器");}Dad(String name) {    System.out.println("父亲的有参构造器,name: " + name);}}class I extends Dad {static {    System.out.println("我的静态初始化块");}{    System.out.println("我的普通态初始化块");}I() {    super("hello");    System.out.println("我的无参构造器");}}public class InitTest {/** * @param args */public static void main(String[] args) {    // TODO Auto-generated method stub    new I();}}

输出:
爷爷的静态初始化块
父亲的静态初始化块
我的静态初始化块
爷爷的普通态初始化块
爷爷的无参构造器
父亲的普通态初始化块
父亲的有参构造器,name: hello
我的普通态初始化块
我的无参构造器

总结一下:初始化实际上是一种假象,在命令行下用javac编译之后,java类中的初始化块并不会继续存在,初始化块中的代码实际上会被还原到每个构造器中,并且放在构造器所有代码之前。

看官们再考虑一下这个问题:http://www.360doc.com/content/13/0419/16/11965070_279489758.shtml


1 0
原创粉丝点击