java 基础题目--01

本文深入探讨Java多态性,通过实例代码详细分析了构造函数、静态方法、继承及覆盖等概念,解释了如何确定调用哪个方法以及方法执行流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1.输出结果多少?

class Test {
    public static void main(String[] args) {
        System.out.println(new B().getValue());
    }
    static class A {
        protected int value; 
        public A (int v) {
            setValue(v);
        }
        public void setValue(int value) {
            this.value= value;
        }
        public int getValue() {
            try {
                value ++;
                return value;
            } finally {
                this.setValue(value);
                System.out.println(value);
            }
        }
    }
    static class B extends A {
        public B () {
            super(5);
            setValue(getValue()- 3);
        }
        public void setValue(int value) {
            super.setValue(2 * value);
        }
    }
}

 

思考和解决这个题的主要核心在于对java多态的理解。个人理解时,执行对象实例化过程中遵循多态特性 ==> 调用的方法都是将要实例化的子类中的重写方法,只有明确调用了super.xxx关键词或者是子类中没有该方法时,才会去调用父类相同的同名方法。

Step 1:  new B()构造一个B类的实例

此时super(5)语句调用显示调用父类A带参的构造函数,该构造函数调用setValue(v),这里有两个注意点一是虽然构造函数是A类的构造函数,但此刻正在初始化的对象是B的一个实例,因此这里调用的实际是B类的setValue方法,于是调用B类中的setValue方法 ==> 而B类中setValue方法显示调用父类的setValue方法,将B实例的value值设置为2 x 5 = 10
紧接着,B类的构造函数还没执行完成,继续执行setValue(getValue()- 3) // 备注1语句。

先执行getValue方法,B类中没有重写getValue方法,因此调用父类A的getValue方法。这个方法比较复杂,需要分步说清楚:

  1. 调用getValue方法之前,B的成员变量value值为10。
  2. value++ 执行后, B的成员变量value值为11,此时开始执行到return语句,将11这个值作为getValue方法的返回值返回出去
  3. 但是由于getValue块被try finally块包围,因此finally中的语句无论如何都将被执行,所以步骤2中11这个返回值会先暂存起来,到finally语句块执行完毕后再真正返回出去。
  4. 这里有很重要的一点:finally语句块中 this.setValue(value)方法调用的是B类的setValue方法。为什么?因为此刻正在初始化的是B类的一个对象(运行时多态),就像最开始第一步提到的一样(而且这里用了使用了this关键词显式指明了调用当前对象的方法)。因此,此处会再次调用B类的setValue方法,同上,super.关键词显式调用A的setValue方法,将B的value值设置成为了2 * 11 = 22
  5. 因此第一个打印项为22。  
  6. finally语句执行完毕 会把刚刚暂存起来的11 返回出去,也就是说这么经历了这么一长串的处理,getValue方法最终的返回值是11。

回到前面标注了 //备注1 的代码语句,其最终结果为setValue(11-3)=>setValue(8)
而大家肯定也知道,这里执行的setValue方法,将会是B的setValue方法。 之后B的value值再次变成了2*8 = 16;

Step2:  new B().getValue()

B类中没有独有的getValue方法,此处调用A的getValue方法。同Step 1,

  1. 调用getValue方法之前,B的成员变量value值为16
  2. value++ 执行后, B的成员变量value值为17,此时执行到return语句,会将17这个值作为getValue方法的返回值返回出去
  3. 但是由于getValue块被try finally块包围而finally中的语句无论如何都一定会被执行,所以步骤2中17这个返回值会先暂存起来,到finally语句块执行完毕后再真正返回出去。
  4. finally语句块中继续和上面说的一样: this.setValue(value)方法调用的是B类的setValue()方法将B的value值设置成为了2 * 17 = 34
  5. 因此第二个打印项为34。
  6. finally语句执行完毕 会把刚刚暂存起来的17返回出去。
  7. 因此new B().getValue()最终的返回值是17.

Step3:  main函数中的System.out.println

将刚刚返回的值打印出来,也就是第三个打印项:17

最终结果为 22 34 17。 如果朋友们在看的过程中仍然有疑问,可以亲自把代码复制进去ide,在关键语句打下断点,查看调用方法的对象以及运行时的对象值,可以有更深刻的理解。

2.继续输出多少?

public class Test {
    public static void main(String[] args) {
        System.out.println("return value of getValue(): " +
        getValue());
    }
     public static int getValue() {
         try {
             return 0;
         } finally {
             return 1;
         }
     }
 }

  结果为:

如果try语句里有return,返回的是try语句块中变量值。 
详细执行过程如下:

  1. 如果有返回值,就把返回值保存到局部变量中;
  2. 执行jsr指令跳到finally语句里执行;
  3. 执行完finally语句后,返回之前保存在局部变量表里的值。

如果try,finally语句里均有return,忽略try的return,而使用finally的return.

3.根据以下代码段,执行new Child("John", 10); 要使数据域data得到10,则子类空白处应该填写

class Parent {

    private int data;

    public Parent(int d){ data = d; }

}

class Child extends Parent{

    String name;

    public Child(String s, int d){

        ___________________

        name = s;

    }

}

分析:首先汪先生想的是子类可以直接给从父类继承的变量赋值即可,于是这样操作

提示很明确,父类的成员变量为私有,我把它改为共有试试。然受这样提示,父类没有默认的构造函数。这个咋办呢?

编译都不通过,当类显示定义带参构造函数,而没有显示定义无参的情况,无参构造消失。
子类构造方法会的第一行会默认加上一句隐式super(),也就是访问父类的无参构造器,即使不写,编译器也会默认为我们加上。但是现在没有无参构造。而如果我们想要访问父类有参构造器,必须手动加上,一旦加上super(参数),那么就不会有隐式的super()。

根据以上的理论,,这样写是没有问题的。

package com.wx.day07;

public class Parent {

    public int data;

    public Parent(int d) {
        data = d;
    }
    public Parent (){}
}


package com.wx.day07;

public class Child extends Parent {
    String name;
    public Child(String s, int d) {
        super.data=d;
        name = s;
    }
}

 除了这种办法当然就不调父类的无参构造,转而调父类的有参构造来初始化父类,即super(agrs);

4.输出多少? 

   

调试结果一目了然,这TM原来是个赋值操作。赋值操作是有返回值的,赋了什么值就返回什么值。


5.输出多少?

  

首先分析一下方法的形参存在哪里?一个方法的变量和另一个方法的参数之间有关系吗?

Var1和Var2初始化后:

 执行doSomething();方法

 

6.看图猜输出

public class Example {
    String str = new String("good");
    char[] ch = {'a', 'b', 'c'};

    public static void main(String[] args) {
        Example example = new Example();
        example.change(example.str, example.ch);
        System.out.println(example.str);
        System.out.println(example.ch);
    }

    public void change(String str, char[] ch) {
        str = "test ok";
        ch[0] = 'g';
    }
}

 这个题很简单,主要是学会看懂下面的内存图

  

     跟着汪先生据图分析,首先明白这些数据存在哪里的,类的成员变量是存在堆上面的,并且基本类型数据如果不给值会默认赋初值,方法的变量是存在栈上面的,他们都有固定的生命周期,第一张图没有问题了。

    

  调用change方法,会在栈中开辟第二个栈帧,并且指向够很明确,但是执行str = "test ok",会在方法区(也叫永久区)中的String类维护的字符串常量池中创建一个拘留字符串"test ok",并且此时这个引用str指向他,执行ch[0] = 'g',注意此时传的是一个数组,数组的数据本来就会放在堆上面,并且传数组其实是传地址,所以他会精确操作原来的地址空间,所以内存图变成下面这样:

  传对象和传数组都是传的地址,

 



public class Example {
    String str = new String("good");
    char[] ch = {'a', 'b', 'c'};
    A a=new A(5);
    public static void main(String[] args) {
        Example example = new Example();
        example.change(example.str, example.ch,example.a);
        System.out.println(example.str);
        System.out.println(example.ch);
        System.out.println(example.a.temp);
    }
    public void change(String str, char[] ch,A a) {
        str = "test ok";
        ch[0] = 'g';
        a.temp=10;
    }
}
class A {
    int temp=5;
    public A(int temp){
        this.temp=temp;
    }
}

 

7.看图猜输出

public class Test1 {
    public static void main(String[] args) {
        try {
            int i = 100 / 0;
            System.out.print(i);
        } catch (Exception e) {
            System.out.print(1);
            throw new RuntimeException();
        } finally {
            System.out.print(2);
        }
        System.out.print(3);
    }
}

  首先,我们要理解Try...catch...finally与直接throw的区别:try catch是直接处理,处理完成之后程序继续往下执行,throw则是将异常抛给它的上一级处理,程序便不往下执行了。对于本题而言,执行100/0,这个一执行,就抛异常,Try...catch...finally这套就是处理这个异常的,处理完继续会继续往下执行,所以直接进入catch,catch抛的异常是给上级处理,程序不会再往下执行,因此执行完finally语句块之后,程序终止了。

   

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时空恋旅人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值