美文网首页Java干货分享
Java类结构和初始化面试问题

Java类结构和初始化面试问题

作者: 淡定_蜗牛 | 来源:发表于2019-03-15 10:15 被阅读0次

    1.简介

    类结构和初始化是每个Java程序员都应该熟悉的基础知识。本文提供了有关您可能遇到的一些面试问题及答案。

    2.问题

    Q1. 描述 final 关键字应用于类,方法,字段或局部变量时的含义。

    当应用于不同的语言结构时,final关键字具有多种不同的含义:

    • 一个final类是不能被继承的类
    • final方法是不能在子类中覆盖的方法
    • final字段是具有在构造或初始化代码块被初始化,之后不能被修改的字段
    • final变量是可被分配(并具有被分配)仅一次并且之后绝不会被修改的变量

    Q2. 什么是默认方法?

    在Java 8之前,接口只能有抽象方法,即没有主体的方法。从Java 8开始,接口方法可以具有默认实现。如果实现类不重写此方法,则使用默认实现。这些方法适当地用default关键字标记。

    默认方法的一个突出用例是向现有接口添加方法。如果您没有将此类接口方法标记为default,则此接口的所有现有实现都将中断。添加具有默认实现的方法可确保旧代码与此接口的新版本的二进制兼容性。

    一个很好的例子是Iterator接口,它允许类成为for-each循环的目标。这个接口最初出现在Java 5中,但是在Java 8中它接收了另外两个方法,forEach和spliterator。它们被定义为具有实现的默认方法,因此不会破坏向后兼容性:

    
    public interface Iterable<T> {
     
        Iterator<T> iterator();
     
        default void forEach(Consumer<? super T> action) { /* */ }
     
        default Spliterator<T> spliterator() { /* */ }
    }
    
    

    Q3. 什么是静态类成员?

    类的静态字段和方法不绑定到类的特定实例。相反,它们绑定到类对象本身。在编译时解析静态方法或寻址静态字段,因为与实例方法和字段相反,我们不需要遍历引用并确定我们所指的实际对象。

    Q4. 如果一个类没有任何抽象成员,可以将其声明为抽象类吗?这类的目的是什么?

    是的,即使一个类不包含任何抽象成员,也可以将其声明为抽象类。作为一个抽象类,它不能被实例化,但它可以作为某个层次结构的根对象,提供对其实现有用的方法。

    Q5. 什么是构造函数链接?

    构造函数链接是一种通过提供按顺序相互调用的多个构造函数来简化对象构造的方法。

    最具体的构造函数可以采用所有可能的参数,并且可以用于最详细的对象配置。一个不太具体的构造函数可以通过提供一些默认值的参数来调用更具体的构造函数。在链的顶部,无参数构造函数可以使用默认值实例化对象。

    下面是一个类的示例,该类可以模拟在特定天数内可用的百分比。如果在使用无参构造函数时未指定默认值,则使用默认值10%和2天:

    
    public class Discount {
     
        private int percent;
     
        private int days;
     
        public Discount() {
            this(10);
        }
     
        public Discount(int percent) {
            this(percent, 2);
        }
     
        public Discount(int percent, int days) {
            this.percent = percent;
            this.days = days;
        }
     
    }
    
    

    Q6. 什么是覆盖和重载方法?他们有什么不同?

    当您定义具有与超类中相同签名的方法时,在子类中完成方法的覆盖。这允许运行时根据您调用方法是实际对象类型方法。方法toString,euals和hashCode在子类中经常被重写。

    方法的重载发生在同一个类中。当您创建具有相同名称但具有不同类型或数量的参数的方法时,会发生重载。这允许您根据您提供的参数类型执行某些代码,而方法的名称保持不变。

    这是java.io.Writer抽象类中的重载示例。以下方法都是名为write,但其中一个接收int而另一个接收char数组。

    
    public abstract class Writer {
     
        public void write(int c) throws IOException {
            // ...
        }
     
        public void write(char cbuf[]) throws IOException {
            // ...
        }
     
    }
    
    

    Q7. 你能覆盖静态方法吗?

    不,你不能。根据定义,如果方法的实现是在运行时通过实际实例的类型(称为动态方法查找的过程)确定的,则只能覆盖方法。静态方法实现是在编译时使用引用的类型确定的,因此无论如何,覆盖都没有多大意义。虽然您可以使用与超类中完全相同的签名向静态方法添加子类,但这在技术上并不叫覆盖。

    Q8. 什么是不可变类,你怎么能创建一个?

    创建后,不能更改不可变类的实例。通过更改,我的意思是通过修改实例字段的值来改变状态。不可变类具有许多优点:它们是线程安全的,当您没有可变状态需要考虑时,它更容易推理它们。

    要使类不可变,您应该确保以下内容:

    • 所有字段都应声明为private和final字段;它们应该在构造函数中初始化,并且从那时起就不会改变;
    • 该类应该没有setter或其他方法来改变字段的值;
    • 通过构造函数传递的类的所有字段都应该是不可变的,或者应该在字段初始化之前复制它们的值(否则我们可以通过保持这些值并修改它们来更改此类的状态);
    • 该类的方法不应该是可以覆盖的; 要么所有方法都应该是final, 要么构造函数应该是private,并且只能通过静态工厂方法调用。

    Q9. 你如何比较两个枚举值:equals()或==?

    实际上,你可以使用两者。该枚举值对象,这样他们就可以与之比较的equals() ,但他们也实现为静态下的常数,所以你也可以使用==比较他们。这主要是代码样式的问题,但如果要保存字符空间(并且可能跳过不需要的方法调用),则应将枚举与==进行比较。

    Q10. 什么是初始化程序块?什么是静态初始化程序块?

    初始化程序块是类作用域中的卷括号代码块,它在实例创建期间执行。您可以使用它来就地初始化单行更复杂的字段。

    实际上,编译器只是在每个构造函数中复制此块,因此从所有构造函数中提取公共代码是一种很好的方法。

    静态初始化程序块是一个支持卷曲的代码块,前面有静态修饰符。它在类加载期间执行一次,可用于初始化静态字段或一些副作用。

    Q11. 什么是标记接口?Java中标记接口有哪些值得注意的例子?

    标记接口是没有任何方法的接口。它通常由类实现或由另一个接口扩展以表示某个属性。标准Java库中最广为人知的标记接口如下:

    • Serializable用于明确表示该类可以序列化;
    • Cloneable允许使用clone方法克隆对象(没有Cloneable接口,此方法抛出CloneNotSupportedException);
    • 在RMI中使用Remote来指定可以远程调用方法的接口。

    Q12. 什么是单例,如何在Java中实现?

    Singleton是面向对象编程的模式。单例类可能只有一个实例,通常是全局可见且可访问的。

    在Java中有多种创建单例的方法。以下是使用静态字段进行就地初始化的最简单示例。初始化是线程安全的,因为保证静态字段以线程安全的方式初始化。构造函数是私有的,因此外部代码无法创建多个类的实例。

    
    public class SingletonExample {
     
        private static SingletonExample instance = new SingletonExample();
     
        private SingletonExample() {}
     
        public static SingletonExample getInstance() {
            return instance;
        }
    }
    
    

    但是这种方法可能有一个严重的缺点 - 实例化将在首次访问此类时进行实例化。如果这个类的初始化是一个繁重的操作,我们可能希望推迟它直到实际需要实例(可能永远不会),但同时保持它的线程安全。在这种情况下,我们应该使用一种称为双重检查锁定的技术。

    Q13. 什么是var-arg?对var-arg有什么限制?你怎么能在方法体内使用它?

    Var-arg是方法的可变长度参数。一个方法可能只有一个var-arg,它必须在参数列表中排在最后。它被指定为类型名称,后跟省略号和参数名称。在方法体内部,var-arg用作指定类型的数组。

    以下是标准库中的示例 - Collections.addAll方法,它接收集合,可变数量的元素,并将所有元素添加到集合中:

    
    public static <T> boolean addAll(
      Collection<? super T> c, T... elements) {
        boolean result = false;
        for (T element : elements)
            result |= c.add(element);
        return result;
    }
    
    

    Q14. 你能访问一个超类的重写方法吗?你能以类似的方式访问超级超类的重写方法吗?

    要访问超类的重写方法,可以使用super关键字。但是你没有类似的方法来访问超级超类的重写方法。

    作为标准库中的示例,LinkedHashMap类扩展了HashMap,并且主要重用其功能,在其值上添加链接列表以保留迭代顺序。LinkedHashMap重用其超类的clear方法,然后清除其链表的头尾引用:

    
    public void clear() {
        super.clear();
        head = tail = null;
    }
    
    
    每日福利

    欢迎大家关注公众号:「Java知己」,关注公众号,回复「1024」你懂得,免费领取 30 本经典编程书籍。关注我,与 10 万程序员一起进步。 每天更新Java知识哦,期待你的到来!

    Java知己

    相关文章

      网友评论

        本文标题:Java类结构和初始化面试问题

        本文链接:https://www.haomeiwen.com/subject/siwkmqtx.html