java面试题(二)

简介: 1、用最有效率的方法计算 2 乘以 8?答: 2 << 3(左移 3 位相当于乘以 2 的 3 次方,右移 3 位相当于除以 2 的 3 次方)。 补充:我们为编写的类重写 hashCode 方法时,可能会看到如下所示的代码,其 实我们不太理解为什么要使用这样的乘法运算来产生哈希码(散列码),而且为 什么这个数是个素数,为什么通常选择 31 这个数?前两个问题的答案你可以自己 百度一下,选择 31 是因为可以用移位和减法运算来代替乘法,从而得到更好的性 能。说到这里你可能已经想到了:31 * num 等价于(num << 5) - num,左移 5 位相当于乘以 2 的 5 次

1、用最有效率的方法计算 2 乘以 8?

答:

2 << 3(左移 3 位相当于乘以 2 的 3 次方,右移 3 位相当于除以 2 的 3 次方)。

补充:我们为编写的类重写 hashCode 方法时,可能会看到如下所示的代码,其

实我们不太理解为什么要使用这样的乘法运算来产生哈希码(散列码),而且为

什么这个数是个素数,为什么通常选择 31 这个数?前两个问题的答案你可以自己

百度一下,选择 31 是因为可以用移位和减法运算来代替乘法,从而得到更好的性

能。说到这里你可能已经想到了:31 * num 等价于(num << 5) - num,左移 5

位相当于乘以 2 的 5 次方再减去自身就相当于乘以 31,现在的 VM 都能自动完成

这个优化

public class PhoneNumber {

private int areaCode;

private String prefix;

private String lineNumber;

@Override

public int hashCode() {

final int prime = 31;

int result = 1;

result = prime * result + areaCode;

result = prime * result

  + ((lineNumber == null) ? 0 : lineNumber.hashCode());

result = prime * result + ((prefix == null) ? 0 : prefix.hashCode());

return result;

}

@Override

public boolean equals(Object obj) {

if (this == obj)

  return true;

if (obj == null)

  return false;

if (getClass() != obj.getClass())

  return false;

PhoneNumber other = (PhoneNumber) obj;

if (areaCode != other.areaCode)

  return false;

if (lineNumber == null) {

  if (other.lineNumber != null)

  return false;

} else if (!lineNumber.equals(other.lineNumber))

  return false;

if (prefix == null) {

  if (other.prefix != null)

  return false;

} else if (!prefix.equals(other.prefix))

  return false;

return true;

}

}

2、在 Java 中,如何跳出当前的多重嵌套循环?

答:

在最外层循环前加一个标记如 A,然后用 break A;可以跳出多重循环。(Java 中

支持带标签的 break 和 continue 语句,作用有点类似于 C 和 C++中的 goto 语

句,但是就像要避免使用 goto 一样,应该避免使用带标签的 break 和 continue,

因为它不会让你的程序变得更优雅,很多时候甚至有相反的作用,所以这种语法

其实不知道更好)

3、构造器(constructor)是否可被重写(override)?

答:

构造器不能被继承,因此不能被重写,但可以被重载。

4、两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?

答:

不对,如果两个对象 x 和 y 满足 x.equals(y) == true,它们的哈希码(hash code)应当相同。Java 对于 eqauls 方法和 hashCode 方法是这样规定的:

(1)如果两个对象相同(equals 方法返回 true),那么它们的 hashCode 值一定要相同;

(2) 如果两个对象的 hashCode 相同,它们并不一定相同。

当然,你未必要按照要求 去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现 在 Set 集合中,同时增加新元素的效率会大大下降(对于使用哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)。

5、当一个对象被当作参数传递到一个方法后,此方法可改变 这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?

答:

是值传递。Java 语言的方法调用只支持参数的值传递。当一个对象实例作为一个

参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调

用过程中被改变,但对对象引用的改变是不会影响到调用者的。C++和 C#中可以

通过传引用或传输出参数来改变传入的参数的值。在 C#中可以编写如下所示的代

码,但是在 Java 中却做不到

6、String 和 StringBuilder、StringBuffer 的区别?

答:

Java 平台提供了两种类型的字符串:String 和 StringBuffer/StringBuilder,它

们可以储存和操作字符串。其中 String 是只读字符串,也就意味着 String 引用的

字符串内容是不能被改变的。而 StringBuffer/StringBuilder 类表示的字符串对象

可以直接进行修改。StringBuilder 是 Java 5 中引入的,它和 StringBuffer 的方

法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有被

synchronized 修饰,因此它的效率也比 StringBuffer 要高。

面试题 1 - 什么情况下用+运算符进行字符串连接比调用

StringBuffer/StringBuilder 对象的 append 方法连接字符串性能更好?

面试题 2 - 请说出下面程序的输出。

class StringEqualTest {

   public static void main(String[] args) {

       String s1 = "Programming";

       第 226 页 共 485 页

       String s2 = new String("Programming");

       String s3 = "Program";

       String s4 = "ming";

       String s5 = "Program" + "ming";

       String s6 = s3 + s4;

       System.out.println(s1 == s2);

       System.out.println(s1 == s5);

       System.out.println(s1 == s6);

       System.out.println(s1 == s6.intern());

       System.out.println(s2 == s2.intern());

   }

}

补充:解答上面的面试题需要清楚两点:1. String 对象的 intern 方法会得到字符

串对象在常量池中对应的版本的引用(如果常量池中有一个字符串与 String 对象

的 equals 结果是 true),如果常量池中没有对应的字符串,则该字符串将被添加

到常量池中,然后返回常量池中字符串的引用;2. 字符串的+操作其本质是创建

了 StringBuilder 对象进行 append 操作,然后将拼接后的 StringBuilder 对象用

toString 方法处理成 String 对象,这一点可以用 javap -c StringEqualTest.class

命令获得 class 文件对应的 JVM 字节码指令就可以看出来

7、描述一下 JVM 加载 class 文件的原理机制?

答:

JVM 中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java 中的

类加载器是一个重要的 Java 运行时系统组件,它负责在运行时查找和装入类文件

中的类。

由于 Java 的跨平台性,经过编译的 Java 源程序并不是一个可执行程序,而是一

个或多个类文件。当 Java 程序需要使用某个类时,JVM 会确保这个类已经被加载、

连接(验证、准备和解析)和初始化。类的加载是指把类的.class 文件中的数据读

入到内存中,通常是创建一个字节数组读入.class 文件,然后产生与所加载类对应

的 Class 对象。加载完成后,Class 对象还不完整,所以此时的类还不可用。当类

被加载后就进入连接阶段,这一阶段包括验证、准备(为静态变量分配内存并设

置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后 JVM 对

类进行初始化,包括:1)如果类存在直接的父类并且这个类还没有被初始化,那么

就先初始化父类;2)如果类中存在初始化语句,就依次执行这些初始化语句。

类的加载是由类加载器完成的,类加载器包括:根加载器(BootStrap)、扩展加

载器(Extension)、系统加载器(System)和用户自定义类加载器

(java.lang.ClassLoader 的子类)。从 Java 2(JDK 1.2)开始,类加载过程采

取了父亲委托机制(PDM)。PDM 更好的保证了 Java 平台的安全性,在该机制

中,JVM 自带的 Bootstrap 是根加载器,其他的加载器都有且仅有一个父类加载

器。类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类加载

第 228 页 共 485 页第 229 页 共 485 页

器自行加载。JVM 不会向 Java 程序提供对 Bootstrap 的引用。下面是关于几个类

加载器的说明:

Bootstrap:一般用本地代码实现,负责加载 JVM 基础核心类库(rt.jar);

Extension:从 java.ext.dirs 系统属性所指定的目录中加载类库,它的父

加载器是 Bootstrap;

System:又叫应用类加载器,其父类是 Extension。它是应用最广泛的

类加载器。它从环境变量 classpath 或者系统属性 java.class.path 所指定的目

录中记载类,是用户自定义加载器的默认父加载器。

8、char 型变量中能不能存贮一个中文汉字,为什么?

答:

char 类型可以存储一个中文汉字,因为 Java 中使用的编码是 Unicode(不选择

任何特定的编码,直接使用字符在字符集中的编号,这是统一的唯一方法),一

个 char 类型占 2 个字节(16 比特),所以放一个中文是没问题的

9、抽象类(abstract class)和接口(interface)有什么异同?

答:

抽象类和接口都不能够实例化,但可以定义抽象类和接口类型的引用。一个类如

果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实

现,否则该类仍然需要被声明为抽象类。接口比抽象类更加抽象,因为抽象类中

可以定义构造器,可以有抽象方法和具体方法,而接口中不能定义构造器而且其

中的方法全部都是抽象方法。抽象类中的成员可以是 private、默认、protected、

public 的,而接口中的成员全都是 public 的。抽象类中可以定义成员变量,而接

口中定义的成员变量实际上都是常量。有抽象方法的类必须被声明为抽象类,而

抽象类未必要有抽象方法。

10、静态嵌套类(Static Nested Class)和内部类(Inner Class)的不同?

答:

Static Nested Class 是被声明为静态(static)的内部类,它可以不依赖于外部类

实例被实例化。而通常的内部类需要在外部类实例化后才能实例化,其语法看起

来挺诡异的,如下所示。

/**

* 扑克类(一副扑克)

* @author 骆昊

*

第 230 页 共 485 页

*/

public class Poker {

   private static String[] suites = {"黑桃", "红桃", "草花", "方块"};

   private static int[] faces = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};

   private Card[] cards;

   /**

    * 构造器

    *

    */

   public Poker() {

       cards = new Card[52];

       for(int i = 0; i < suites.length; i++) {

           for(int j = 0; j < faces.length; j++) {

               cards[i * 13 + j] = new Card(suites[i], faces[j]);

           }

       }

   }

   /**

    * 洗牌 (随机乱序)

    *

    */

   public void shuffle() {

       for(int i = 0, len = cards.length; i < len; i++) {

           int index = (int) (Math.random() * len);

           Card temp = cards[index];

           cards[index] = cards[i];

           cards[i] = temp;

       }

   }

   /**

    * 发牌

    * @param index 发牌的位置

    *

    */

   public Card deal(int index) {

       return cards[index];

   }

   /**

    * 卡片类(一张扑克)

    * [内部类]

    * @author 骆昊

    *

    */

   public class Card {

       private String suite; // 花色

       private int face; // 点数

       public Card(String suite, int face) {

           this.suite = suite;

           this.face = face;

       }

       @Override

       public String toString() {

           String faceStr = "";

           switch(face) {

               case 1: faceStr = "A"; break;

               第 233 页 共 485 页

               case 11: faceStr = "J"; break;

               case 12: faceStr = "Q"; break;

               case 13: faceStr = "K"; break;

               default: faceStr = String.valueOf(face);

           }

           return suite + faceStr;

       }

   }

}

class PokerTest {

   public static void main(String[] args) {

       Poker poker = new Poker();

       poker.shuffle(); // 洗牌

       Poker.Card c1 = poker.deal(0); // 发第一张牌

// 对于非静态内部类 Card

// 只有通过其外部类 Poker 对象才能创建 Card 对象

       Poker.Card c2 = poker.new Card("红心", 1); // 自己创建一张牌

       System.out.println(c1); // 洗牌后的第一张

       System.out.println(c2); // 打印: 红心 A

   }

}

面试题 - 下面的代码哪些地方会产生编译错误?

class Outer {

   class Inner {}

   public static void foo() { new Inner(); }

   public void bar() { new Inner(); }

   public static void main(String[] args) {

       new Inner();

   }

}

注意:Java 中非静态内部类对象的创建要依赖其外部类对象,上面的面试题中 foo

和 main 方法都是静态方法,静态方法中没有 this,也就是说没有所谓的外部类对

象,因此无法创建内部类对象,如果要在静态方法中创建内部类对象,可以这样

做:

new Outer().new Inner();

相关文章
|
3月前
|
算法 Java
50道java集合面试题
50道 java 集合面试题
|
6月前
|
缓存 Java 关系型数据库
2025 年最新华为 Java 面试题及答案,全方位打造面试宝典
Java面试高频考点与实践指南(150字摘要) 本文系统梳理了Java面试核心考点,包括Java基础(数据类型、面向对象特性、常用类使用)、并发编程(线程机制、锁原理、并发容器)、JVM(内存模型、GC算法、类加载机制)、Spring框架(IoC/AOP、Bean生命周期、事务管理)、数据库(MySQL引擎、事务隔离、索引优化)及分布式(CAP理论、ID生成、Redis缓存)。同时提供华为级实战代码,涵盖Spring Cloud Alibaba微服务、Sentinel限流、Seata分布式事务,以及完整的D
346 1
|
6月前
|
存储 安全 Java
常见 JAVA 集合面试题整理 自用版持续更新
这是一份详尽的Java集合面试题总结,涵盖ArrayList与LinkedList、HashMap与HashTable、HashSet与TreeSet的区别,以及ConcurrentHashMap的实现原理。内容从底层数据结构、性能特点到应用场景逐一剖析,并提供代码示例便于理解。此外,还介绍了如何遍历HashMap和HashTable。无论是初学者还是进阶开发者,都能从中受益。代码资源可从[链接](https://pan.quark.cn/s/14fcf913bae6)获取。
296 3
|
5月前
|
缓存 Java API
Java 面试实操指南与最新技术结合的实战攻略
本指南涵盖Java 17+新特性、Spring Boot 3微服务、响应式编程、容器化部署与数据缓存实操,结合代码案例解析高频面试技术点,助你掌握最新Java技术栈,提升实战能力,轻松应对Java中高级岗位面试。
464 0
|
5月前
|
Java 数据库连接 数据库
Java 相关知识点总结含基础语法进阶技巧及面试重点知识
本文全面总结了Java核心知识点,涵盖基础语法、面向对象、集合框架、并发编程、网络编程及主流框架如Spring生态、MyBatis等,结合JVM原理与性能优化技巧,并通过一个学生信息管理系统的实战案例,帮助你快速掌握Java开发技能,适合Java学习与面试准备。
235 2
Java 相关知识点总结含基础语法进阶技巧及面试重点知识
|
3月前
|
算法 Java
50道java基础面试题
50道java基础面试题
|
6月前
|
存储 安全 Java
2025 最新史上最全 Java 面试题独家整理带详细答案及解析
本文从Java基础、面向对象、多线程与并发等方面详细解析常见面试题及答案,并结合实际应用帮助理解。内容涵盖基本数据类型、自动装箱拆箱、String类区别,面向对象三大特性(封装、继承、多态),线程创建与安全问题解决方法,以及集合框架如ArrayList与LinkedList的对比和HashMap工作原理。适合准备面试或深入学习Java的开发者参考。附代码获取链接:[点此下载](https://pan.quark.cn/s/14fcf913bae6)。
3172 48
|
6月前
|
算法 架构师 Java
Java 开发岗及 java 架构师百度校招历年经典面试题汇总
以下是百度校招Java岗位面试题精选摘要(150字): Java开发岗重点关注集合类、并发和系统设计。HashMap线程安全可通过Collections.synchronizedMap()或ConcurrentHashMap实现,后者采用分段锁提升并发性能。负载均衡算法包括轮询、加权轮询和最少连接数,一致性哈希可均匀分布请求。Redis持久化有RDB(快照恢复快)和AOF(日志更安全)两种方式。架构师岗涉及JMM内存模型、happens-before原则和无锁数据结构(基于CAS)。
172 5
|
6月前
|
Java API 微服务
2025 年 Java 校招面试全攻略:从面试心得看 Java 岗位求职技巧
《2025年Java校招最新技术要点与实操指南》 本文梳理了2025年Java校招的核心技术栈,并提供了可直接运行的代码实例。重点技术包括: Java 17+新特性(Record类、Sealed类等) Spring Boot 3+WebFlux响应式编程 微服务架构与Spring Cloud组件 Docker容器化部署 Redis缓存集成 OpenAI API调用 通过实际代码演示了如何应用这些技术,如Java 17的Record类简化POJO、WebFlux构建响应式API、Docker容器化部署。
275 5