JAVA面试题2021详解

        这篇文章是本人在记忆并理解优质JAVA面试题时记录的笔记,我会将问题涉及到的知识点进行汇总。(本文参考其他博主的回答,再进一步进行分析),知识点均摘自jdk1.8的api文档

一、String类能不能被继承?为什么?

回答:String类不能被继承,因为String类是被final修饰的类,final修饰过的类不能被继承、final修饰过的变量不能被修改。

Class String

String的父类:

String类的官方完整描述:

  • public final class String
    extends Object
    implements Serializable, Comparable<String>, CharSequence

切记:String对象一旦被创建就是固定不变的了,对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象。

延伸一下:

java中的String和char有什么区别?

1. char是基本数据类型,而String是个类,属于引用数据类型。String类可以调用方法,具有面向对象的特征。

2.char是表示的是字符,定义的时候用单引号,只能存储一个字符。

 而String表示的是字符串,定义的时候用双引号,可以存储一个或者多个字符。

  • 例如:

         String str = "abc";
    
  • 相当于:

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

3.char数据类型(因此Character对象封装的值)基于原始Unicode规范,其将字符定义为固定宽度的16位实体。

        A String表示UTF-16格式的字符串,其中补充字符由代理对表示 。 索引值是指char代码单元,所以补充字符在String中使用两个StringString类提供处理Unicode代码点(即字符)的方法,以及用于处理Unicode代码单元(即char值)的方法。

二、Java语言有哪些特点?

  1. 简单易学;

  2. 面向对象(封装,继承,多态);

  3. 平台无关性( Java 虚拟机实现平台无关性);

  4. 支持多线程( C++ 语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而 Java 语言却提供了多线程支持);

  5. 可靠性;

  6. 安全性;

  7. 支持网络编程并且很方便( Java 语言诞生本身就是为简化网络编程设计的,因此 Java 语言不仅支持网络编程而且很方便);

  8. 编译与解释并存;

这里补充一下JAVA跨平台的知识

        Java语言之所以流行,是和Java的许多优点分不开的。而Java的最大优点就是跨平台。Java的跨平台可以使用8个字概括:一次编译,到处运行

        Java程序的跨平台主要是指(Java源代码编译后的)字节码文件可以在任何(具有Java虚拟机的)计算机或者电子设备上运行。

Java语言的执行过程

1) Java程序的运行要经过编辑、编译、解释并执行三个阶段。
2) Java源代码经过编译后生成字节码文件(.class)。字节码文件与平台无关的,不面向
任何具体平台,只面向虚拟机。
3) 字节码文件需要再解释成相应平台的机器码并运行。解释是通过 Java虚拟机来执行的。Java虚拟机是可运行Java字节码文件的虚拟计算机。Java虚拟机(中的Java解释器)负责将字节码文件解释成为特定的机器码进行运行。不同平台的虚拟机是不同的,但它们都提供了相同的接口。

 tips:C语言也可以跨平台(多次编译,到处运行),C语言的源代码使用不同的编译器进行编译,编译之后直接生成指定平台的机器码。

总结

Java语言具有一次编译,到处运行的特点。是说编译后的.class可以跨平台运行。

Java语言可以跨所有的平台吗?不见得,要看是否提供并安装相应的虚拟机。
 

三、为什么说 Java 语言“编译与解释并存”?

        高级编程语言按照程序的执行方式分为编译型解释型两种。

        简单来说,编译型语言是指编译器针对特定的操作系统将源代码一次性翻译成可被该平台执行的机器码;解释型语言是指解释器对源程序逐行解释成特定平台的机器码并立即执行。比如,你想阅读一本英文名著,你可以找一个英文翻译人员帮助你阅读, 有两种选择方式,你可以先等翻译人员将全本的英文名著(也就是源码)都翻译成汉语,再去阅读,也可以让翻译人员翻译一段,你在旁边阅读一段,慢慢把书读完。

        Java 语言既具有编译型语言的特征,也具有解释型语言的特征,因为 Java 程序要经过先编译,后解释两个步骤,由 Java 编写的程序需要先经过编译步骤,生成字节码(\*.class 文件),这种字节码必须由 Java 解释器来解释执行。因此,我们可以认为 Java 语言编译与解释并存。

四、import java 和 javax 有什么区别?

        刚开始的时候 JavaAPI 所必需的包是 java 开头的包,javax 当时只是扩展 API 包来使用。然而随着时间的推移,javax 逐渐地扩展成为 Java API 的组成部分。但是,将扩展从 javax 包移动到 java 包确实太麻烦了,最终会破坏一堆现有的代码。因此,最终决定 javax 包将成为标准 API 的一部分。

        所以,实际上 java 和 javax 没有区别。这都是一个名字。

五、==和 equals 的区别

对于基本数据类型来说,==比较的是值。

对于引用数据类型来说,==比较的是对象的内存地址。

== 等于比较运算符,如果进行比较的两个操作数都是数值类型,即使他们的数据类型不相同,只要他们的值相等,也都将返回true。如果两个操作数都是引用类型,那么只有当两个引用变量的类型具有父子关系时才可以比较,而且这两个引用必须指向同一个对象,才会返回true.(在这里我们可以理解成==比较的是两个变量的内存地址)

 因为 Java 只有值传递,所以,对于 == 来说,不管是比较基本数据类型,还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量存的值是对象的地址。

         equals() 作用不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等。equals()方法存在于Object类中,而Object类是所有类的直接或间接父类。在Object类中的equals()方法体内实际上返回的就是使用==进行比较的结果.但是我们知道所有的类都继承Object,而且Object中的equals()方法没有使用final关键字修饰,那么当我们使用equal()方法进行比较的时候,我们需要关注的就是这个类有没有重写Object中的equals()方法。

equals() 方法存在两种使用情况:

  • 类没有覆盖 equals()方法 :通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象,使用的默认是 Objectequals()方法。

  • 类覆盖了 equals()方法 :一般我们都覆盖 equals()方法来比较两个对象中的属性是否相等;若它们的属性相等,则返回 true(即,认为这两个对象相等)。

  • String 中的 equals 方法是被重写过的,因为 Object 的 equals 方法是比较的对象的内存地址,而 String 的 equals 方法比较的是对象的值。

  • 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。

六、hashCode()与 equals()

        面试官可能会问你:“你重写过 hashcode 和 equals么,为什么重写equals 时必须重写 hashCode 方法?”

1.hashCode()介绍:

    hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是确定该对象在哈希表中的索引位置hashCode()定义在 JDK 的 Object 类中,这就意味着 Java 中的任何类都包含有 hashCode() 函数。另外需要注意的是:Object 的 hashcode 方法是本地方法,也就是用 c 语言或 c++ 实现的,该方法通常用来将对象的 内存地址 转换为整数之后返回。

public native int hashCode();

2.为什么有 hashCode?

以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode

        当我们把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与其他已经加入的对象的 hashcode 值作比较,如果没有相符的 hashcode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals() 方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。

3.为什么重写 equals 时必须重写 hashCode 方法?

        如果两个对象相等,则 hashcode 一定也是相同的。两个对象相等,对两个对象分别调用 equals 方法都返回 true。但是,两个对象有相同的 hashcode 值,它们也不一定是相等的 。因此,equals 方法被覆盖过,则 hashCode方法也必须被覆盖。

  hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)

4.为什么两个对象有相同的 hashcode 值,它们也不一定是相等的?

        因为 hashCode() 所使用的杂凑算法也许刚好会让多个对象传回相同的杂凑值。越糟糕的杂凑算法越容易碰撞,但这也与数据值域分布的特性有关(所谓碰撞也就是指的是不同的对象得到相同的 hashCode

        我们刚刚也提到了 HashSet,如果 HashSet 在对比的时候,同样的 hashcode 有多个对象,它会使用 equals() 来判断是否真的相同。也就是说hashcode 只是用来缩小查找成本。

代码分析:

案例:学生对象的成员变量值相同,就认为是一个对象

Student类:

public class Student implements Comparable<Student>{
    private int stuid;
    private String stuname;

    public Student(int stuid, String stuname) {
        this.stuid = stuid;
        this.stuname = stuname;
    }
    //重写
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return stuid == student.stuid && Objects.equals(stuname, student.stuname);
    }

    @Override
    public int hashCode() {
        return Objects.hash(stuid, stuname);
    }

    @Override
    public String toString() {
        return "Student{" +
                "stuid=" + stuid +
                ", stuname='" + stuname + '\'' +
                '}';
    }

    public int getStuid() {
        return stuid;
    }

    public void setStuid(int stuid) {
        this.stuid = stuid;
    }

    public String getStuname() {
        return stuname;
    }

    public void setStuname(String stuname) {
        this.stuname = stuname;
    }

    @Override
    public int compareTo(Student s) {
        //return 0;//只输出一个对象,因为返回0代表着,每个对象都相同(重复元素)
        //return 1;//全输出,一个一个对象按新建对象的顺序输出(升序)
       //return -1;//降序全输出
        int num=this.stuid-s.stuid;//按id比较
        //id相同时,按名字的自然排序比较
        int num2=num==0?this.stuname.compareTo(s.stuname):num;//string类型自带compareTo方法
        return num2;
    }
}

调用类:

//案例需求:学生对象的成员变量值相同,就认为是一个对象
public class HashSet2 {
    public static void main(String[] args) {
        HashSet<Student> hs=new HashSet<Student>();
        Student ly=new Student(007,"will");
        Student ho=new Student(003,"tt");
        Student ly2=new Student(007,"will");
        //再添加一个ly2,其元素为Student(007,"will");时,会添加成功
        //此时我们要重写hasCode和equals方法
        hs.add(ly);
        hs.add(ho);
        for (Student s:hs){
            System.out.println(s.getStuid()+",and,"+s.getStuname());


        }

    }
}

七、什么是序列化?什么是反序列化?(I/O流)

        如果我们需要持久化 Java 对象,比如将 Java 对象保存在文件中,或者在网络传输 Java 对象,这些场景都需要用到序列化。

简单来说:

  • 序列化:将数据结构或对象转换成二进制字节流的过程

  • 反序列化:将在序列化过程中所生成的二进制字节流的过程转换成数据结构或者对象的过程

对于 Java 这种面向对象编程语言来说,我们序列化的都是对象(Object)也就是实例化后的类(Class),但是在 C++这种半面向对象的语言中,struct(结构体)定义的是数据结构类型,而 class 对应的是对象类型。

面向对象中的对象序列化,并不概括之前原始对象所关系的函数。这种过程也称为对象编组(marshalling)。从一系列字节提取数据结构的反向操作,是反序列化(也称为解编组、deserialization、unmarshalling)。

综上:序列化的主要目的是通过网络传输对象或者说是将对象存储到文件系统、数据库、内存中。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值