【最新Android高级面试知识点干货分享(一)】

2020年开年不利,大家都过得不容易。我们大家都辛苦了!
通过这次的疫情,使我更加直观也更进一步的理解了,何谓“命运共同体”。
如果你现在有工作,那么恭喜你。你要做的就是少点抱怨,更加努力的工作,把工作做好,多存点钱。
如果你正在找工作,那么我想说的是:不要急,选好自己心仪的行业,心仪的公司,好好准备面试,向它发起挑战。只要你真正努力了,真正问心无愧了,那么胜利终将属于你!!

转载请说明出处

2020年Android高级面试知识点干货分享系列,我将分为两大篇(Java基础篇Android开发篇),多小节的形式进行连载(由于要对笔记进行排版整理)。这些完全都是我个人今年在找工作时的面试准备(知识点笔记),现在分享出来,希望能对你有一点点帮助(如果有些知识点我没有到位的,或者存在错误的,欢迎留言指正。我会持续更新)。

《Java篇》

一、== 与 equals()的区别

对于这两者的区别,想深刻理解的,得对Java类加载机制,JVM内存模型,汇编指令集等都有个了解。可以自己写个java测试类,利用javac编译成class文件,然后再利用javap -c xxx来对字节码进行反汇编,查看代码的执行过程。从而从根源上搞懂==与equals的区别。

$1.1、==

比较操作符,比较的是两个操作数变量的值是否相等。

  • 当比较的是int 基本类型时,比较值;
  • 当变量为引用类型时,比较的是引用类型变量的内存地址。

下面以引用类型为例看下==的执行过程:

public class Test {

    public static void main(String[] args) {
        String var1 = "Hello";
        String var2 = "World";
        boolean result = (var1 == var2);
        System.out.println("result=" + result);
    }
}

下面利用javap -c Test对其进行反汇编,结果信息如下:

Compiled from "Test.java"
public class ext.sunny.com.activitylifedemo.Test {
//构造方法代码块执行
  public ext.sunny.com.activitylifedemo.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":
()V
       4: return

  public static void main(java.lang.String[]);
    Code:
    //ldc:指令,用于将int、float、String类型常量从常量池中压到栈顶
    //这里指将字符串常量"Hello",压入栈顶
       0: ldc           #2                  // String Hello
       //astore_1:pop出栈顶的引用值,并且将它赋值给变量var1
       2: astore_1
       //ldc:将字符常量“World"从常量池中压到栈顶
       3: ldc           #3                  // String World
       //astroe_2:与上面一样,pop出栈顶的引用值,并且将它赋值给变量var2
       5: astore_2
       //aload指令:加载本地变量,并压入操作数栈
       //aload_1:将var1变量的引用值压入操作数栈
       //aload_2:将var2变量的引用值压入操作数栈
       6: aload_1
       7: aload_2
       //if_acmpne:有条件转移指令。可以理解像java中的if条件语句
       //如果两个对象引用不相等,则跳转
       //iconst_0、iconst_1:表示将int类型常量0与1压入栈
       8: if_acmpne     15
      11: iconst_1
      12: goto          16
      15: iconst_0
      //istore_3:将局部变量(这里指上面条件指令后的结果)存入3变量,即result中。
      16: istore_3
////下面就是System.out.print()打印输出的执行过程了。
      17: getstatic     #4                  // Field java/lang/System.out:Ljava/
io/PrintStream;
      20: new           #5                  // class java/lang/StringBuilder
      23: dup
      24: invokespecial #6                  // Method java/lang/StringBuilder."<
init>":()V
      27: ldc           #7                  // String result=
      29: invokevirtual #8                  // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      32: iload_3
      33: invokevirtual #9                  // Method java/lang/StringBuilder.ap
pend:(Z)Ljava/lang/StringBuilder;
      36: invokevirtual #10                 // Method java/lang/StringBuilder.to
String:()Ljava/lang/String;
      39: invokevirtual #11                 // Method java/io/PrintStream.printl
n:(Ljava/lang/String;)V
      42: return
}

$1.2、equals()

为Object类中的方法:

public boolean equals(Object obj) {
    return (this == obj);
}

注意点:

  • equals()不能比较基本数据类型;
  • 如果equals()比较的对象没有复写超类的equals()方法,则比较的是内存地址;
  • 如果比较的对象有复写equals()方法,比如String,且有自己的比较规则,则按照自己的比较规则进行,比如String对象比较的是char字符值。

扩展】:
在使用HashMap时,如果其Key你是用的引用对象,为了避免Hash冲突,此引用对象应该重写equals()方法,制定自己的比较规则。

二、String、StringBuffer与StringBuilder的区别

这里主要要理解Java的运行时数据区:
PC计数器、栈、本地方法栈、方法区(常量池属于里面的一块特殊内存空间)、堆

$2.1、String

分两种:字符串常量、对象

  • String是字符串常量,则定义后其值存放在常量池,不可变(底层采用字符数组来存储,且是final,因此不可变);
  • 如果是用new关键字生成出来的String对象则存放在堆中(只是将其引用值赋值给变量存入栈中)。

$2.2、StringBuffer

  • 继承自AbstractStringBuilder
  • 是线程安全的,因为里面的操作方法都用synchronized关键字(常用于多线程),同时效率不高
  • 值可变。

$2.3、StringBuilder

  • 继承自AbstractStringBuilder
  • 非线程安全(无synchronized关键字)
  • 值可变
  • 效率比StringBuffer高,常用于单线程操作多个字符串。

备注:
我们在拼接字符串时,会习惯性的使用“+”,其实这个加号我们反汇编(可以参照上面的反汇编代码)可以发现,它里面就是new的StringBuilder对象来实现的。且它是每一行遇到+就会生成StringBuilder对象。因此在开发中,如果是很简单的拼接或者是测试可以用“+”,但其他情况(非多线程下),从效率以及内存上建议使用StringBuilder。

下一小节还是Java篇,我将分享以下面试笔记【Java集合】、【反射与泛型】、【JVM内存模型】

原创文章 80 获赞 34 访问量 17万+

猜你喜欢

转载自blog.csdn.net/u011153817/article/details/105834607