最近很火的一道 面向对象的面试题 ——携程Java工程师(SBU)

携程Java后台开发三面面经 前言携程是我面试的第一个互联网公司,投递的岗位是后台开发实习生,总共面了三面,止步于人才库。中间兜兜转转,复杂的心理活动,不足与外人道也。唯有面试的技术部分与大家共享。宣讲会完了之后有个手写代码的笔试,大致内容:1已知有一颗二叉排序树,向树里面插入节点,如果该节点已存在(节点值相等),将节点中的count字段加一;如果不存在,将节点插入树中,并将节点的count值置为1。自行设计数据结构,插入算法... 阅读详情

该题目源自微信公众号(程序员的那些事)的推送:携程 Java 工程师的一道面向对象面试题

题目是这样的:求下面程序的输出:

最近这道面试题,传遍程序员各大小园地,本小白特摘抄整理与大家一起学习:

原题:

package com.gxlee;

public class Base {
    private String baseName=  "base";
    public Base() {
        callName();
    }
    public void callName(){
        System.out.println(baseName);
    }
    
    static class Sub extends Base{
        private String baseName = "sub";
        public void callName(){
            System.out.println(baseName);
        }
    }
    public static void main(String[] args) {
        Base b = new Sub();
        System.out.println(b);
    }
}

求这段程序的输出。


很显然一开始我也做错了,原因很简单,这道题目考察什么我大概知道,可是之前在学习类的加载机制时对类的初始化过程还比较了解,但实例变量的初始化过程比较模糊。这里还牵扯的难点有:继承的时候子类的同名属性不会覆盖父类,会将父类的属性隐藏;在父类的构造函数里面调用虚函数引起多态的变态代码。

接下来查看了一下深入Java虚拟机(Bill Venners著) 等才对该问题有了清楚的认识。下面我会尽量简单而清晰的将其分析的透彻明了。

前面我在JAVA类加载和初始化 中简单的讲解了类的加载链接初始化过程,有兴趣的可以看看,里面也有一道变态的题目。

类的实例变量初始化的过程 
一旦一个类被加载连接初始化,他就可以随时被使用了,程序可以访问他的静态字段,调用静态方法,或者创建它的实例。在Java程序中类可以被明确或者隐含地实例化,有四种途径:明确使用new操作符;调用Class或者Constructor对象的newInstance()方法;调用任何现有对象的clone()方法;或者通过objectInputStream类的getObject()方法反序列化。

当虚拟机创建一个新的实例时,都需要在堆中为保存对象的实例分配内存。所有在对象的类中和它的超类中声明的变量(包括隐藏的实例变量)都要分配内存。一旦虚拟机为新的对象准备好堆内存,它立即把实例变量初始化为默认的初始值。这一点很类似于类变量在链接的准备阶段赋予默认初始值是一样样的。

一旦虚拟机完成了为新的对象分配内存和为实例变量初始化为默赋予正确认的初始值后,接下来就会为实例变量的初始值。即调用对象的实例初始化方法,在java的class文件中称之为< init >()方法,类似于类初始化的< clinit >()方法。

一个< init >()方法可能包含三种代码:

  • 调用另一个< init >()方法
  • 实现对任何实例变量的初始化
  • 构造方法体的代码

实际上,一般有下面的情况:

  • 构造函数明确的调用了同一个类中的另一个构造方法,即调用了this(),它对应的< init >()方法由两部分构成: 
    一个同类的< init >(…)方法的调用 
    实现了对应构造方法的方法体的字节码

  • 不是以this()开始的,也不是Object,由三部分组成: 
    超类的< init >()方法调用 
    任意实例变量初始化方法的字节码 
    实现了对应构造方法的方法体的字节码

什么意思?简单理解就是说< init >()就是从class文件字节码角度的构造函数,一般由Java代码里面的几部分构成。

超类的< init >()方法调用———————>对应super() 
任意实例变量初始化方法的字节码————>对应定义变量时的赋值代码 
实现了对应构造方法的方法体的字节码——>构造函数里面的代码

注:Java保证了一个对象被初始化前其父类也必须被初始化。有下面机制来保证:Java强制要求任何类的构造函数中的第一句必须是调用父类构造函数或者是类中定义的其他构造函数。如果没有构造函数,系统添加默认的无参构造函数,如果我们的构造函数中没有显示的调用父类的构造函数,那么编译器自动生成一个父类的无参构造函数。

举个栗子:

class B {   private int b = 10;   public B(){     b = 100;   } }

编译成class文件后,使用命令 javap -c B.class 反编译 
反编译B.class

很显然可以看到初始化< init >()分为三部分

// 第一部分:父类的<init>()方法 0: aload_0 1: invokespecial #1                  // Method java/lang/Object."<init>":()V // 第二部分:实例变量初始化,也就是定义变量时的赋值 4: aload_0 5: bipush        10 7: putfield      #2                  // Field b:I // 第三部分:构造函数方法体 10: aload_0 11: bipush        100 13: putfield      #2                  // Field b:I 16: return

学习到这里总结一下前面学习的内容就是: 
Java实例变量在初始化时的顺序是父类的初始化代码(xxx—>xxx—>xxx)—>定义变量时直接赋值—>构造函数代码块。

回头看开始的问题: 
直到Base类和Sub类完成了初始化过程(类初始化完成) 
在初始化Sub对象前,首先在堆区开辟内存并将子类中的baseName和父类中的baseName(已被隐藏)均赋为null 
接下来执行对象的初始化过程,由于Sub类的构造函数没有写,初始化代码包含三部分:

  1. super();调用Base类的init<>()

    1. 调用super()也就是Object类的init<>()
    2. baseName = “base”;这里是父类的baseName赋值。
    3. 父类构造函数里面的:调用callName()由于该函数是在Sub类的里面调用的,所以当前的this其实是子类,由于多态调用子类Sub的callName方法此时子类的baseName变量还未赋值还是null!
  2. baseName = “sub”;这里是子类的baseName赋值。

  3. 空(构造函数什么都没有)  

  4. 如果你还不知道答案继续往下看:


解答此题关键在于理解和掌握类的加载过程以及子类继承父类后,重写方法的调用问题:

从程序的执行顺序去解答:

1、Base b = new Sub();

  声明父类变量b对子类的引用,JAVA类加载器将Base,Sub类加载到JVM;

2、JVM为Base,Sub 的的成员开辟内存空间

  此时,Base 和Sub类中的值为null;

3、new sub()

  调用构造函数,由于Sub类继承自Base而无自定义构造器,因此先调用父类Base的无参构造;

4、父类午餐构造的本质为:

  public Base(){

  baseName= "base";

  callName();

    }

  即将父类的baseName赋值为“base”,赋值后调用callName();

5、callName 在子类中被重写,因此调用子类的callName();

6、调用子类的callName,打印baseName

  此baseName为子类Sub的成员变量,而此时该成员尚未初始化,因此为Null;

7、实际上在new Sub()时,实际执行过程为:

  public Sub(){

      super();

   baseName = "sub"; 

    }

  可见,在baseName = "sub"执行前,子类的callName()已经执行,所以子类的baseName为默认值状态null;

总结:

最后放张图片,相信大家看到就秒懂了。


来自未老莫还乡 的评论:

   【不要在构造器里调用可能被重载的虚方法,这是极度危险的】。

  构造器的初始化顺序大概是:父类静态块 ->子类静态块 ->父类初始化语句 ->父类构造函器 ->子类初始化语句 子类构造器。

   父类构造器执行的时候,调用了子类的重载方法,然而子类的类字段还在刚初始化的阶段,刚完成内存布局,只能输出null。

别再折腾了!Win10下用VS2022编译OSG 3.6.5,我踩过的坑都给你填平了 本文详细介绍了在Win10系统下使用VS2022编译OpenSceneGraph 3.6.5的完整避坑指南。从环境准备、CMake配置到编译过程中的常见错误解决方案,提供了实战经验分享,帮助开发者高效完成OSG部署,避免常见陷阱。 阅读详情

相关推荐

RT-Thread NANO V4.1.1 在Cortex-M0上移植踩坑记:解决内存分配宏导致的线程创建失败

本文详细记录了RT-Thread NANO V4.1.1在Cortex-M0平台移植过程中遇到的内存分配宏配置问题,导致线程创建失败的解决方案。通过分析内存管理算法宏定义,将RT_USING_SMALL_MEM替换为RT_USING_SMALL_MEM_AS_HEAP,成功解决了线程创建问题,为开发者提供了实用的移植经验。

weixin_29189373的博客 306

深入理解Semaphore原理

1. 信号量Semaphore的介绍 我们以一个停车场运作为例来说明信号量的作用。假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了三辆车,看门人允许其中它们进入进入,然后放下车拦。以后来的车必须在入口等待,直到停车场中有车辆离开。这时,如果有一辆车离开停车场,看门人得知后,打开车拦,放入一辆,如果又离开一辆,则又可以放入一辆,如此往复。 在这个停车场系统中,车位是公共资源,每辆...

qq_39241239的博客 930

ThinkBook 16 2024 Ubuntu驱动全攻略:从无线网卡到触控板

本文详细介绍了ThinkBook 16 2024在Ubuntu系统下的驱动问题解决方案,涵盖无线网卡、蓝牙、显卡和触控板等关键硬件的驱动修复步骤。通过具体命令和配置指南,帮助用户快速解决兼容性问题,提升Ubuntu在ThinkBook上的使用体验。

weixin_29200741的博客 826

Semaphore实现原理全面解析

Semaphore(信号量)是一个同步工具类,通过Semaphore可以控制同时访问共享资源的线程个数。

专注Java后端技术干货、项目源码总结分享,期待您的关注。 1786

介绍七种常见还危险的病毒(带样本)

简介:能够感染用户计算机bai系统中的.exe、.dll和.html文件,将自身加密以后缀加到目标文件中。样本(源文件):https://wwx.lanzoux.com/iJx4njrpz1c / https://wwx.lanzoui.com/iYRNLwd8ldc。英文名称:Virus.Win32.Viking.A(标准) / Worm.Win32.Fujack.A(标准)英文名称:Net-Worm.Win32.Blaster.A(标准)/ Lovesan.A/ Msblast.A。

2301_78858007的博客 8393

调试GMS应用,报错“此设备未获得play保护机制认证”问题解决

不少同学在调试GMS相关应用时,需登录Google账号,有时会弹出如下通知。Google登录界面也会出现如下提示这个报错的原因是设备未通过Google认证,google服务器未配置荣耀设备的型号白名单导致国内网页有一些指导方法在鸿蒙\荣耀的设备上消除这个提示,但本质的问题没有解决,无法登录Google账号。

胡同老道的博客 2万+

Semaphore

1、Semaphore 是什么 Semaphore 通常我们叫它信号量, 可以用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理的使用资源。 可以把它简单的理解成我们停车场入口立着的那个显示屏,每有一辆车进入停车场显示屏就会显示剩余车位减1,每有一辆车从停车场出去,显示屏上显示的剩余车辆就会加1,当显示屏上的剩余车位为0时,停车场入口的栏杆就不会再打开,车辆就无法进入停车场...

qq_29860591的博客 781

GEE:哨兵时间序列遥感数据和动态阈值方法计算物候时期EOS/SOS(2)

本文将分享和解释论文《Improved Estimates of Arctic Land Surface Phenology Using Sentinel-2 Time Series》中使用到的基于阈值估算北极地区的植被物候,特别是北极地区的植被季节开始和结束的日期(SoS和EoS)的方法和代码。该方法使用的是使用哨兵2数据,并实现了两种阈值方法( 无平滑的阈值方法、平滑后的阈值方法)。

养乐多的博客 2971

Semaphore原理解析

Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。

m0_38033199的博客 579

C# MES通信从入门到精通(1)——串口传输文件

本文详细介绍了在Mes通信领域如何使用串口发送文件,由实战经验总结,堪称MES通信领域的葵花宝典!

qq_34059233的博客 3749

uniapp - H5网页uni.getLocation报错getLocation:fail Geolocation permission denied.Get ipLocation failed.

uni.getLocation getLocation:fail Geolocation permission denied.Get ipLocation failed.,uni.getLocation定位失败,uni-app getLocation()无法获取定位,一直fail()uniapp移动端h5网页,uniApp微信内置浏览器定位失败,如何解决这个问题,uniapp h5 uni.getLocation获取位置失败怎么办,UNI-APP获取当前位置,uni.getLocation定位失败,unia

🏆 前端领域优质创作者 4241

Semaphore使用以及原理

1. Semaphore的使用 1) Semaphore,俗称信号量 基于AbstractQueuedSynchronizer实现!AQS 2) Semaphore管理着一组许可permit,许可的初始数量通过构造函数设定。 3) 默认使用非公平的方式 sync = new NonfairSync(permits); 使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数 4) 当线程要访问共享资源时,需要先通过acquire()方法获取许可。获取到之后许可

NotRepent的博客 2338

PyTorch深度学习实战(29)——变分自编码器(Variational Autoencoder, VAE)

变分自编码器 (Variational Autoencoder, VAE) 是一种结合了自编码器和概率建模的生成模型,通过编码器将输入数据映射到潜在空间中的概率分布,并通过解码器将从潜在空间采样得到的潜在变量映射回原始数据空间,实现了数据的生成和特征学习。

盼小辉丶的博客 1万+

java页面间面向对象的方法面试题_最近很火的一道 面向对象的面试题 ——携程Java工程师(SBU)...

题目是这样的:求下面程序的输出:最近这道面试题,传遍原题:packagecom.gxlee;public classBase {private String baseName= "base";publicBase() {callName();}public voidcallName(){System.out.println(baseName);}static class Sub extendsB...

weixin_42303282的博客 144

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

【多线程】Semaphore实现原理

前言 Semaphore,信号量,一般用于控制同时访问资源的线程数量。可以认为Synchronized代表的是一把锁,那么Semaphore就是多把锁。 常用方法 public class Semaphore implements java.io.Serializable { //构造方法,传入令牌数,默认实例化一个非公平锁 public Semaphore(int permits); //获取一个令牌,在获取成功之前,以及被其他线程中断之前,当前线程会被阻塞 pub

阳阳的博客 1672

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

携程校招Java岗位面经(二面)

一入Java深似海,还得继续修炼呀!

m0_62600503的博客 1507

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

错题知识点总结之Java语法

1.子父类 在调用子类构造器之前,会先调用父类构造器,当子类构造器中没有使用"super(参数或无参数)"指定调用父类构造器时,是默认调用父类的无参构造器,如果父类中包含有参构造器,却没有无参构造器,则在子类构造器中一定要使用“super(参数)”指定调用父类的有参构造器,不然就会报错。 如下代码: class Base{ public Base(String s){ ...

比丘走白鹿 314

Semaphore(信号量)的工作原理及实例

一:工作原理 Semaphore是一种在多线程环境下使用的设施,该设施负责协调各个线程,以保证它们能够正确、合理的使用公共资源的设施,也是操作系统中用于控制进程同步互斥的量。Semaphore是一种计数信号量,用于管理一组资源,内部是基于AQS的共享模式。它相当于给线程规定一个量从而控制允许活动的线程数。 synchronized 和 ReentrantLock 都是一次只允许一个线程访问某个...

li-zhang-yu的博客 2551

Java并发神器Semaphore全方位解析

AQS通过内部的一个statestate > 0:表示有可用的许可。state == 0:表示没有可用的许可。

赵KK的博客 5265

什么是Semaphore? 其内部原理如何?

Semaphore维护了一组许可(permits),线程可以通过获取许可来访问某些资源。如果没有可用的许可,线程将会被阻塞,直到有许可可用。使用Semaphore可以有效地控制并发访问共享资源的线程数量。Semaphore是一个用于控制对共享资源访问的实用工具,可以有效限制并发线程的数量,避免资源的过度争用。它内部通过计数器和条件队列实现线程的同步与协调,为多线程开发提供了简单有效的机制。

java永无止境! 537

Semaphore 使用及原理详解

Semaphore 是一个强大的并发控制工具,它通过维护许可数量来控制对共享资源的访问。理解其基于 AQS 的实现原理有助于更好地使用它。在实际开发中,Semaphore 常用于限流、连接池管理、生产者-消费者模式等场景。

weixin_46322148的博客 1112

携程Java后端实习一面

携程的面试比较注重八股文和项目,算法相关没有字节腾讯严厉,大家参加携程的技术岗面试需要重视八股文和项目细节,要学会深挖项目,希望大家早日oc😊👍。

qq_51118755的博客 1471

深入解析 Semaphore:原理、使用场景与最佳实践

Semaphore是一个非常强大的并发控制工具,适用于限制并发线程数量、实现资源池等场景。它通过信号量机制有效地管理并发访问资源的线程数,保证系统的稳定性。掌握Semaphore的使用方法,能够帮助开发者有效地解决线程同步与资源管理问题。通过本文的介绍,相信你已经深入理解了Semaphore的工作原理与使用场景,并能在实际开发中更高效地应用这一工具来提高系统的并发性能与稳定性。

一碗黄焖鸡三碗米饭的博客 1271

携程Java后端一面+二面+HR面(已意向)

一面 1、聊聊你研究生的课题(智能机器人相关) 2、几个人做的啊,分工是什么样的 3、如果以后想把你们做的东西商业化 产品化,怎么去收集用户的信息? 云?用户定期上传数据到云服务器,公司负责处理这些数据(不懂。朝着云的方向扯了一些) 4、 进程与线程的区别 调度:进程是资源管理分配的基本单位,线程是程序执行调度的基本单位。 切换:线程上下文切换比进程上下文切换要快得多。 拥有资源: 进程是拥有资源的一个独立单位,线程不拥有系统资源,但是可以访问隶属于进程的资源。 系统开销: 创建或撤销进程时,系

王二的博客 2160

Semaphore的原理及代码实战详解

当需要对并发访问的资源进行控制和保护时,信号量(Semaphore)是一个常用的同步机制。它可以限制同时访问某个资源的线程数量,并确保线程按照预期的顺序执行。在本篇博客中,我们将介绍信号量的原理、使用方法以及提供一个代码示例。在多线程编程中,当多个线程需要同时访问共享资源时,可能会出现竞争条件和资源冲突的问题。信号量通过维护一定数量的许可证来解决这个问题。每当一个线程要访问该资源时,它首先必须获取一个许可证。如果许可证可用,线程可以继续执行操作;否则,线程将被阻塞等待直到有其他线程释放许可证。

weixin_67589420的博客 1536

携程Java后端(一)

交换两个变量的值,不借助第三个变量 栈实现 int exchange(int x,int y) { stack S; push(S,x); push(S,y); x=pop(S); y=pop(S); } 位运算(异或) 通过异或运算能够使数据中的某些位翻转,其他位不变。这就意味着任意一个数与任意一个给定的值连续异或两次,值不变。 int a=10,b=12; //a=1010^b=1100; a=a^b; //a=0110^b=1100; b=a...

lijieling123的博客 1140
上一篇: Mavan打包项目报错以及解决办法。
下一篇: <meta http-equiv="" content="">详解
白衣染霜陈
博客等级 码龄22年 682粉丝 338原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值