zoukankan      html  css  js  c++  java
  • 有关Java基础的一些笔试题总结

    针对近期腾讯、京东、网易等公司的笔试。遇到一些有关Java基础的问题,在此总结。希望能通过这几道经典问题题发散,举一反三。借此打牢基础!

    自己总结,望提出宝贵意见!

    一、关于null的一道小题
    先开开胃。一道非常有意思的笔试题,题目例如以下:
    以下这段代码能正确运行吗?假设能,输出什么?

    public class NULL {
        public static void haha(){
            System.out.println("haha");
        }
        public static void main(String[] args) {
            ((NULL)null).haha();
        }
    }

    答案是能正确运行。大家看出来答案用了多久?相信大家都比我强。

    我第一次看到这个表达式。脑子一片蒙蔽,后来细致分析代码,大写的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(); 
       }
    
    }

    直接看输出结果:

    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);
        }
    }

    运行结果:

    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';
        }
    }

    选项:

    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));
        }
    }

    输出结果例如以下:

    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);  
       }  

    -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);
    
        }
    }

    而如上述凝视的部分中,在Java中假设在main方法里定义一个变量而不初始化,是不能通过编译的!
    而对于类成员变量,类载入器为我们做了初始化的工作。输出例如以下:

    0
    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();
        }
    }

    答:自然是不正确的!


    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、抽象类能够包括构造器。用于抽象类的初始化,而接口不能够;

    (未完待续)

  • 相关阅读:
    强大的Resharp插件
    配置SPARK 2.3.0 默认使用 PYTHON3
    python3 数据库操作
    python3 学习中的遇到一些难点
    log4j的一个模板分析
    MYSQL内连接,外连接,左连接,右连接
    rabbitmq实战记录
    领域模型分析
    分布式系统学习笔记
    阿里开发规范 注意事项
  • 原文地址:https://www.cnblogs.com/llguanli/p/8434953.html
Copyright © 2011-2022 走看看