java面试题

基本概念与常识

java语言的特点

  • 简单容易
  • 面向对象(封装、继承、多态)
  • 平台无关性(通过不同平台的jvm实现)
  • 支持多线程(c++无多线程机制,需要依赖操作系统的多线程机制)
  • 适合网络编程
  • 可靠、安全
  • 编译与解释并存(java源码先经编译器编译为字节码,再由解释器解释执行)

JVM vs JDK vs JRE

java语言的“一次编译,到处运行”是通过不同平台的jvm来实现的。
JVM有很多种,例如Hotspot VM、J9 VM、JRocket等,只要满足jvm规范即可,每个人都可以实现自己的jvm。

JDK(Java Develoment Kit),包含了开发java程序需要的javac等、运行java程序的jre环境。

JRE,运行java程序需要的jvm、类库。

部署jsp的web程序需要jdk的原因是jps -> servlet的转换需要jdk的编译工具。

什么是字节码?采用字节码的好处是什么?

许多语言的源代码经编译器编译为.class文件,这个.class文件就是字节码。
字节码只面向虚拟机,不针对特点语言,不针对特点平台,能在各个平台的虚拟机上执行。java源码先经编译器编译为字节码,再由解释器逐行解释为机器码执行。相比传统解释性语言将源码直接逐行解释为机器码,java的运行效率要高,且保持有解释性语言可移植性的特点。

对于Hotspot VM, 在执行字节码前会判断是否存在已编译版本,若有执行编译后的机器码。若无逐行读取字节码进行解释执行并计数,这段字节码执行次数达到某个阈值时,被判定为“热点代码”,会被提交给JIT编译器编译。JIT编译器会对这段字节码进行分析,生成精简的机器码。

Oracle JDK vs OpenJDK

oracle jdk与open jdk源码上基本一样。oracle jdk相比open jdk经过了更多的测试,对错误的修复,更加稳定,且jvm的性能更好,适合商用。

java 与 c++ 的区别

相同

  • 都是面向对象语言(封装、继承、多态)

封装,指事物抽象为类
继承,子类继承父类,复用字段、方法。
多态,父类引用可以指向子类实例。子类通过override父类方法来实现特定的行为。方法重载也是多态。

不同

  • java不使用指针来访问内存,更安全
  • java不用手动释放内存,有垃圾回收机制
  • java为单继承,但能实现多个接口。c++为多继承。
  • java只支持方法重载。c++支持方法重载、操作符重载。操作符重载比较复杂,这与java简单化的初衷违背。

基本语法

字符常量、字符串常量的区别

形式:字符常量为由单引号引起的一个字符。字符串常量为双引号引起的0个、几个字符。
含义:字符常量为ASCII值,能参与表达式运算。字符串常量是一个地址,指向全局String共享池的一个字符串对象。
内存占用:字符常量占2个字节。字符串常量占若干个字节。

使用过可变长参数吗

    public void method(String... args){
   
        for (int i = 0; i < args.length; i++) {
   

        }
    }

可变长参数,可以接受0个、多个参数,实际就是一个数组,查看class文件的方法描述符可以看到是[Ljava/lang/String;。可变长参数只能是方法的最后一个参数。方法重载时,优先匹配固定参数的方法。

注释有多少种?越多越好吗?

  • 单行注释
  • 多行注释
  • 文档注释

代码容易理解,就可免去注释。
hashCode()定义在 JDK 的 Object 类中,这就意味着 Java 中的任何类都包含有 hashCode() 函数。另外需要注意的是: Object 的 hashCode() 方法是本地方法,也就是用 C 语言或 C++ 实现的,该方法通常用来将对象的内存地址转换为整数之后返回。

标识符和关键字的区别是什么?

关键字:例如private、public、class、void、return、final等
标识符:由字母、下划线、数字组成,不能以数字开头。标识符作为类名、字段名、方法名、变量名等。

自增自减运算符

int i = 0;
int j = ++i; ++i先增后赋值,j = 1
int j = i++; i++先赋值后增,j = 0
–i和i–同++。

continue、break、return的区别

continue只能用在循环中,用于结束本次循环,继续下次循环
break能用于循环中时,结束循环,执行循环下面的代码。break也可用于switch中的case、default中,退出switch体,执行switch体下面的代码。
return结束方法的执行

  • return; //无返回值的方法
  • return value; //又返回值的方法

什么是方法?方法有几种类型?

方法是行为的体现。
方法按有无参数、有无返回值全排列有4种类型。

静态方法为什么不能访问实例字段?

在类加载的准备阶段分配内存给静态字段,因此静态方法可访问静态字段。实例字段是在创建实例对象的时候分配内存的。当我们访问静态方法时,可能尚未创建任何对象,实例字段无内存分配,因此静态方法不能访问实例字段。

静态方法和实例方法有何不同

  1. 调用方式
    静态方法有两中,1是类名.静态方法、2是实例对象.静态方法。但不建议用第2种方式,这会混淆静态方法和实例方法。
    实例方法是实例对象.静态方法
  2. 访问成员限制
    静态方法只能访问静态字段。实例方法能访问静态字段、实例字段。

重载和重写的区别

重载: 发生在同一个类或父类和子类之间,相同方法名,不同参数列表(参数个数、参数类型、参数类型顺序任一不同)。方法返回类型、抛出异常、访问修饰符对重载无影响,可以修改。发生在编译期,调用重载方法时由编译器匹配参数列表符合的方法,若无法匹配成功,报错。重载是同一个方法名,传入不同的参数,来执行不一样的操作。
重写:发生在父类和子类之间,相同方法名,相同参数列表。子类的方法返回类型须小于等于父类的方法返回类型,子类方法的抛出异常须小于等于父类的抛出异常,子类方法的访问修饰符须大于等于父类的修饰符。重写,对于相同的输入,子类需做出不同于父类的操作。发生在运行期。
private、final、static修饰的方法不能重写。但static修饰的方法可以在子类中再次定义。构造方法不能重写。

==与equals的区别

==,比较值是否相等。

  • 对于基本数据类型,==比较值是否相等
  • 对于引用类型,==比较内存地址是否相等。本质上也是比较值,因为引用类型变量保存的是内存地址。

Object中定义了equals方法,其实现就是使用==。java中Object是所有类的父类,因此每个类都有equals方法,且默认比较内存地址是否相等。通过重写equals方法,可以自定义实现判断两个对象是否相等。例如String就重写了equals方法。String的equals方法,先判断内存地址是否相等,若是那么两个对象相等。若否,如果传入的对象是String,则比较两个字符串的字符是否全部一样。如果传入的对象不是是String,则两个对象不一样。

重写过hashCode()和equals()方法吗?为什么重写了equals()一定要重写hashCode()?

hashCode()方法是获取对象的哈希码。
利用哈希函数生成对象的哈希码,哈希函数尽量对不同对象生成不同的哈希码,以此来初步区分对象。但不同对象的哈希码仍有可能相同,因此需要equals方法来区分。
结论

  • 哈希码不同的对象不相等
  • 哈希码相同的对象由equals方法区分

hashCode()是在Object类中定义的native方法,一般是将对象的内存地址转换为整数。
HashMap利用哈希码能快速定位对象存储位置,且能快速地初步判断两个对象是否相等,避免大量调用equals方法,提高效率。

要知道,HashSet底层是利用了HashMap,HashMap的put方法调用putVal方法。这个putVal方法尤其关键。下面的是本人简化后的,具体的代码请自行查看。

final V putVal(int hash, K key, V value) {
   
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if (tab为空)//tab为空即map为空,利用resize方法初始化tab
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)//利用哈希码定位,找不到相同哈希码的数组中节点,说明map中无该key,直接创建一个新节点
            tab[i] = newNode(hash, key, value, null);
        else {
    //利用哈希码定位,找到了相同哈希码的数组中的节点,说明map中可能有该key、也可能没有该key
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k)))) //经哈希码、equals方法判断,发现key已经存在
                e = p;
            else if (p instanceof TreeNode)//这是经哈希码、equals方法判断后数组中不存在key,该数组中的节点是数节点,在红黑树中查找是否存在该key
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
    //这是经哈希码、equals方法判断后数组中不存在key,该数组中的节点是链表头节点,在该链表中查找是否存在该key
                for (int binCount = 0; ; ++binCount) {
   
                    if ((e = p.next) == null) {
   //查找完链表发现并存在在该key,即map中无该key,创建一个新节点
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k)))) //链表中存在该key
                        break;
                    p = e;
                }
            }
            if (e != null) {
    // e!=null说明map中存在该key,设置新值,返回旧值
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;//设置新值
                afterNodeAccess(e);
                return oldValue;//返回旧值
            }
        }
        return null;//说明map中不存在该key,返回null。
    }

常用的哈希算法

  • 直接取余法
    hash(key) = key mod PrimeNumber

为什么重写了equals()一定要重写hashCode()?
若重写了equals、未重写hashCode方法,默认的hashCode返回是对象内存地址的整数表示,每个对象都不一样。该类作为HashMap的key时,原本equals相等的对象,因哈希码不一样被错判为不想等而导致出现重复的key。

基本数据类型

了解java的基本数据类型吗

4种整形,都是有符号数,默认值为0

  • byte,1字节
  • short,2字节
  • int,4字节
  • long,8字节,值后面加L
    2种浮点数
  • float,4字节,值后面加f,默认为0f
  • double,8字节,值后加d。小数默认为double。默认值为0d。
    1种字符
  • char,2个字节,默认值为’u0000’
    1种布尔变量
  • boolean,默认为false

包装类型的常量池技术了解吗?

byte、short、int、long的包装类Byte、Short、Integer、Long都有ByteCache、ShortCache的xxCache内部类。这些内部类缓存了[-128, 127]的值,这些包装类的valueOf静态方法,若在[-128, 127]则返回缓存的引用,否则new 一个新的对象。char有Character,Character的内部类CharacterCache缓存了[0,127]的字符,Character的valueOf效果同整形包装类。Boolean的valueOf直接返回TRUE或FALSE的引用。

Float、Double无缓存。

Integer a = 1;查看class文件发现,实际上是 Integer a = Integer.valueOf(1);
因为有包装类型的常量池技术
导致

Integer a = 3;
Integer b = 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值