`

Java细节之static方法重写

 
阅读更多
前言
  Java语言拾遗,高手如果感兴趣可跳过前两节。
 
类方法
  方法被声明为static后,则称为类方法。类方法相对于实例方法,前者区别于后者的地方:前者为属于该类的所有实例对象共享,无须实例化对象,仅通过类名即可访问(当然,是否能够直接访问,还取决于所声明的访问权限)。

  因为被static化后产生上述特殊性,所以static变量都会在类被加载时初始化,而类方法也同时随类加载而进驻内存。先来段代码热热身吧~
public class Test ...{
    
public static void main(String[] args) ...{
        A.print();
        System.out.println(A.publicStr);
    }

}


class A ...{
    
private static A a = new A();
    
private static String privateStr = null;
    
public static String publicStr = "A Class";
    
    
private A() ...{
        privateStr 
= "A Calss";
    }

    
    
public static void print() ...{
        System.out.println(privateStr);
    }

}

  上段代码,输出结果为:
null
A Class

  由结果可知,即字符串prvateStr的值为空。嘿,可别认为值应该是下面那样啊。那样的话,进行下去就太具挑战性了。
A Class
A Class

  请记住一点,类变量初始化的顺序与其在类中的赋值顺序一致。

重写(覆盖)
  或许大家对于面向对象编程语言最初的印象就是其语言单元是以父类、子类的关系存在着,而构建这一关系的就是继承机制了。子类可以继承父类一切非private的变量与方法,并且可以添加自己的变量与方法。在构建一个系统时,这机制让我们强烈地感觉到编程是一门优雅的艺术。

  来段小小的代码简单地展示下:
public class Test ...{
    
public static void main(String[] args) ...{
        Programmer pro 
= new Programmer("Jack");
        
        pro.printName();
        pro.printProfession();
    }

}


class Man ...{
    
private String name = null;
    
public final String characteristic = "I am a thinking animal";
    
    
public Man(String name) ...{
        
this.name = name;
    }

    
    
public void printName() ...{
        System.out.println(name);
    }

}


class Programmer extends Man ...{
    
private String profession = "Programmer";

    
public Programmer(String name) ...{
        
super(name);
    }

    
    
public void printProfession() ...{
        System.out.println(characteristic 
+ ", and a " + profession);
    }

}

  结果如下:
Jack
I am a thinking animal, and a Programmer

  如上,子类Programmer中并没定义字符串characteristic,但我们却在其方法printProfession()中调用了;同样,我们正常使用了父类定义的printName()方法。而这就是继承的简单实现。

  继承不仅仅带来以上特性。它还赋予子类重写(覆盖)父类方法的能力(因为旨在讲类方法的重写,所以这儿就不讲重载以及变量在继承机制中的问题了)。方法的重写(覆盖):继承父类的子类,可以通过拟具有相同方法名与参数组的方法来重写父类中对应的方法,从而让子类更个性化。又因为重写(覆盖)的出现,多态也随之产生。多态:通过父类变量可以引用其子类对象,从而调用子类中那些继承自自己并被重写(覆盖)的方法。
public class Test ...{
    
public static void main(String[] args) ...{
        Man manP 
= new Programmer("Jack");
        Man manS 
= new Student("Tom""MayBeHarvard");
        
        manP.printName();
        System.out.println();
        manS.printName();
    }

}


class Man ...{
    
private String name = null;
    
    
public Man(String name) ...{
        
this.name = name;
    }

    
    
public void printName() ...{
        System.out.println(name);
    }

}


class Programmer extends Man ...{
    
private String profession = "Programmer";

    
public Programmer(String name) ...{
        
super(name);
    }

    
    
public void printName() ...{
        System.out.println(
"Hey, I am a " + profession + ". You know, that work is an art.");
        System.out.print(
"My name is ");
        
super.printName();
    }

}


class Student extends Man ...{
    
private String school = null;
    
    
public Student(String name, String school) ...{
        
super(name);
        
this.school = school;
    }

    
    
public void printName() ...{
        System.out.println(
"Hi, I am a student from " + school);
        System.out.print(
"My name is ");
        
super.printName();
    }

}


  结果如下:
Hey, I am a Programmer. You know, that work is an art.
My name is Jack

Hi, I am a student from MayBeHarvard
My name is Tom

  Man类型变量引用其子类对象,并成功调用对应方法打印出个性化的自我介绍。

类方法的重写?
  进行到这儿,对类方法与继承、重写等概念应该有较清楚的认识了。如果您不是很清楚、或者我上面介绍得不够详细,请参考Java的圣经《The Java Language Specification》

  现在开始本文的问题吧:
public class Test ...{
    
public static void main(String[] args) ...{
        Man man 
= new Programmer();
        Programmer pro 
= new Programmer();
        
        man.printName(
"ManCallMe");
        System.out.println();
        pro.printName(
"CallMeByMyself");
    }

}


class Man ...{    
    
public static void printName(String name) ...{
        System.out.println(name);
    }

}


class Programmer extends Man ...{    
    
public static void printName(String name) ...{
        System.out.println(
"Hey, I am a Programmer. You know, that work is an art.");
        System.out.print(
"My name is " + name);
    }

}

  结果如下:
Hey, I am a Programmer. You know, that work is an art.
ManCallMe

Hey, I am a Programmer. You know, that work is an art.
My name is CallMeByMyself

  “结果绝对不是这样!”如果运行过这段代码,您一定会大声的对我说。的确,结果事实上是这样的:
ManCallMe

Hey, I am a Programmer. You know, that work is an art.

  按照多态的介绍,结果应该是上一个啊!?为什么事实却是纠正的那个呢?难道重写(覆盖)没有成功?事实上,重写操作是成功的,因为第二个函数调用输出的结果证明了这点。那这一切的一切的问号是为何呢?

  方法被加载的顺序是这一切的根本原因!

  当一个方法被调用时,JVM首先检查其是不是类方法。如果是,则直接从调用该方法引用变量所属类中找到该方法并执行,而不再确定它是否被重写(覆盖)。如果不是,才会去进行其它操作(例如动态方法查询),具体请参考:方法的加载

  具体到上面那段代码,因为引用man是Man类型变量,所以JVM根据上述规定,调用的是Man中的printName(),而不是Programmer中printName();而pro正是Programmer,同理,其调用是自己的printName(),出现料想外的结果也就纯属正常了。

  最后,以一段有趣的代码结束本文。它也是本文对于类方法理解的强有力证据.它正确输出了结果,呵呵~
public class Test ...{
    
public static void main(String[] args) ...{
        (Man.getMan()).printName();
    }

}


class Man ...{    
    
public static void printName() ...{
        System.out.println(
"None");
    }

    
    
public static Man getMan() ...{
        
return null;
    }

}
分享到:
评论

相关推荐

    javafinal和static总结.docx

    2. **final方法**:final方法不能被子类重写(override),这意味着即使子类继承了含有final方法的父类,也不能改变该方法的行为。这有助于保护关键逻辑不被外部随意修改。 3. **final变量**:final变量通常被视为...

    Java面向对象(进阶)- 四种权限测试与方法的重写(override-overwrite)

    - 重写的方法不能声明为 `static` 或 `final`。 - 重写的方法不能抛出更大的异常,除非捕获并重新抛出。 4. **面试题**:面试中可能会遇到关于重写的问题,如询问重写与重载的区别,或者设计一个场景来考察对重写...

    Java新手指南方法覆盖Java开发Java经验技巧共14

    在Java编程语言中,"方法覆盖"(Method Overriding)是一个重要的面向对象特性,它允许子类重写父类中的方法。本指南旨在帮助新手理解这一概念,掌握Java开发中的关键技巧,提升编程能力。这份14页的PDF文档可能涵盖...

    java细节总结 非常适合初学者学习

    - 多态:同一种行为可以通过不同方式表现,如方法重写。 - 抽象:不关注具体实现,只关注接口。 - 范型:泛型提供类型安全,避免了类型转换的麻烦。 3. **常用包**: - `java.lang`:基础包,包含所有Java程序...

    JAVA工程师常见笔试题

    - Java中的方法访问权限问题,比如在子类中可以使用哪些父类中的方法,以及方法的重写规则。例如,子类可以重写父类中声明为protected和public的方法,但是不能降低方法的访问权限,所以private方法不能在子类中被...

    Java100个面试题.doc

    Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。 4. 是否可以在static环境中访问非static变量?static变量在Java中是属于类的,它在所有的实例中的值是一样的...

    java名企面试高频考点整合全

    因为static方法是静态绑定的,在编译时就已经确定了,因此无法通过子类来重写。 **4. 是否可以在static环境中访问非static变量?** - 不可以。非static变量属于特定的对象实例,而static方法不依赖于任何实例,...

    Java 核心面试题

    Java 中 static 方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而 static 方法是编译时静态绑定的。 数据类型 Java 语言支持的 8 中基本数据类型是:byte、short、int、long、float、double、boolean、...

    JAVA第三章的课后习题答案

    3. 理解继承的工作机制:子类继承父类的属性和方法,可以重写父类的方法来实现特定功能。 4. 应用多态性:了解父类引用指向子类对象的概念,以及如何在方法签名中使用多态。 通过解答这些习题,你不仅可以巩固课堂...

    java基础之面向对象编程

    Java中的多态性主要体现在方法重载(Overloading)和方法重写(Overriding)上,以及接口的实现。 6. **抽象类**: 抽象类不能被实例化,通常包含抽象方法(没有具体实现的方法)。它用于定义一个类族,并作为子类...

    100+经典Java面试题及答案解析

    Java 中 static 方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而 static 方法是编译时静态绑定的。 自动拆装箱 自动装箱是 Java 编译器在基本数据类型和对应的对象包装类型之间做的一个转化。比如:把 ...

    java常用方法集合

    由于 `clone()` 方法是受保护的,因此实现 `Cloneable` 接口的类需要重写该方法以允许外部访问。 **示例代码详解**: ```java @Override protected Object clone() throws CloneNotSupportedException { // TODO ...

    java面向对象

    多态则是指同一种行为在不同对象上表现出不同的形态,这在Java中主要通过方法重载(Overloading)和方法重写(Overriding)来实现。 接口在Java中扮演着合约的角色,它定义了一组方法签名,但不提供实现。类可以...

    java开发工程师面试题

    - 多态在Java中的实现机制是通过方法重写和向上转型。 - abstract class和interface的区别在于abstract class可以有实现的方法,而interface不可以。 - abstract方法不能同时是static、native或synchronized,...

    JAVA理论知识基础复习

    JAVA理论知识基础复习是指对JAVA语言的基础知识进行复习和总结,本文将涵盖构造方法、new关键字、方法的重载、特殊变量this、static定义常量、继承、方法的重写、特殊变量super、多态性等相关知识点。 一、构造方法...

    java2实用教程

    4.4 STATIC关键字:解释了static关键字的使用场景,包括静态变量和静态方法。 4.5 THIS关键字:介绍了this关键字在方法中引用当前对象实例的用法。 4.6 包:讲解了包的概念、包语句和import语句,以及如何将类打包成...

    Java开发示例代码

    8. **封装**:封装是面向对象的三大特性之一,Java通过类和对象实现了数据的封装,隐藏内部实现细节,提供公共接口供外部访问。 9. **字节码与编译**:Java程序编译后生成`.class`文件,这些字节码文件由Java虚拟机...

    1000道Java 程序员必备面试题-V1版.pdf

    Java 8 中,接口新增了 default 方法和 static 方法,这两种方法可以有方法体。 抽象类和接口(Java7)的区别 抽象类可以提供成员方法的实现细节,而接口中只能存在 public abstract 方法。抽象类中的成员变量可以...

    《java面向对象程序设计-继承和多态》教案.doc

    封装是面向对象编程的基础,它涉及到将数据(属性)和操作这些数据的方法(行为)组合在一个类中,以隐藏内部实现细节。在Java中,类(class)用于定义对象的结构,通过圆点符号(.)来访问对象的成员。封装有助于...

    java面向对象的实例

    运行时多态则是通过方法的重写(Overriding)和接口实现实现,子类可以重写父类的方法,提供自己的实现,调用者可以根据对象的实际类型在运行时决定调用哪个方法。 在"动物特性的面向对象描述"这个例子中,我们可以...

Global site tag (gtag.js) - Google Analytics