精华内容
下载资源
问答
  • 【深入理解JVM】:Java类继承关系中的初始化顺序

    万次阅读 多人点赞 2016-05-06 11:19:38
    Java类初始化的顺序经常让人犯迷糊,现在本文尝试着从JVM的角度,对Java继承继承关系中的初始化顺序进行试验,尝试给出JVM角度的解释。非继承关系中的初始化顺序对于非继承关系,主类...

    Java类初始化的顺序经常让人犯迷糊,现在本文尝试着从JVM的角度,对Java非继承和继承关系中类的初始化顺序进行试验,尝试给出JVM角度的解释。

    非继承关系中的初始化顺序

    对于非继承关系,主类InitialOrderWithoutExtend中包含了静态成员变量(类变量)SampleClass 类的一个实例,普通成员变量SampleClass 类的2个实例(在程序中的顺序不一样)以及一个静态代码块,其中静态代码块中如果静态成员变量sam不为空,则改变sam的引用。main()方法中创建了2个主类对象,打印2个主类对象的静态成员sam的属性s。

    代码1

    package com.j2se;
    
    public class InitialOrderWithoutExtend {
        static SampleClass sam = new SampleClass("静态成员sam初始化");
        SampleClass sam1 = new SampleClass("普通成员sam1初始化");
        static {
            System.out.println("static块执行");
            if (sam == null)
                System.out.println("sam is null");
            sam = new SampleClass("静态块内初始化sam成员变量");
        }
    
        SampleClass sam2 = new SampleClass("普通成员sam2初始化");
    
        InitialOrderWithoutExtend() {
            System.out.println("InitialOrderWithoutExtend默认构造函数被调用");
        }
    
        public static void main(String[] args) {
            // 创建第1个主类对象
            System.out.println("第1个主类对象:");
            InitialOrderWithoutExtend ts = new InitialOrderWithoutExtend();
    
            // 创建第2个主类对象
            System.out.println("第2个主类对象:");
            InitialOrderWithoutExtend ts2 = new InitialOrderWithoutExtend();
    
            // 查看两个主类对象的静态成员:
            System.out.println("2个主类对象的静态对象:");
            System.out.println("第1个主类对象, 静态成员sam.s: " + ts.sam);
            System.out.println("第2个主类对象, 静态成员sam.s: " + ts2.sam);
        }
    }
    
    class SampleClass {
        // SampleClass 不能包含任何主类InitialOrderWithoutExtend的成员变量
        // 否则导致循环引用,循环初始化,调用栈深度过大
        // 抛出 StackOverFlow 异常
        // static InitialOrderWithoutExtend iniClass1 = new InitialOrderWithoutExtend("静态成员iniClass1初始化");
        // InitialOrderWithoutExtend iniClass2 = new InitialOrderWithoutExtend("普通成员成员iniClass2初始化");
    
        String s;
    
        SampleClass(String s) {
            this.s = s;
            System.out.println(s);
        }
    
        SampleClass() {
            System.out.println("SampleClass默认构造函数被调用");
        }
    
        @Override
        public String toString() {
            return this.s;
        }
    }

    输出结果:

    静态成员sam初始化
    static块执行
    静态块内初始化sam成员变量
    第1个主类对象:
    普通成员sam1初始化
    普通成员sam2初始化
    InitialOrderWithoutExtend默认构造函数被调用
    第2个主类对象:
    普通成员sam1初始化
    普通成员sam2初始化
    InitialOrderWithoutExtend默认构造函数被调用
    2个主类对象的静态对象:
    第1个主类对象, 静态成员sam.s: 静态块内初始化sam成员变量
    第2个主类对象, 静态成员sam.s: 静态块内初始化sam成员变量

    由输出结果可知,执行顺序为:

    1. static静态代码块和静态成员
    2. 普通成员
    3. 构造函数执行

    当具有多个静态成员和静态代码块或者多个普通成员时,初始化顺序和成员在程序中申明的顺序一致。

    注意到在该程序的静态代码块中,修改了静态成员sam的引用。main()方法中创建了2个主类对象,但是由输出结果可知,静态成员和静态代码块只进行了一次初始化,并且新建的2个主类对象的静态成员sam.s是相同的。由此可知,类的静态成员和静态代码块在类加载中是最先进行初始化的,并且只进行一次。该类的多个实例共享静态成员,静态成员的引用指向程序最后所赋予的引用。

    继承关系中的初始化顺序

    此处使用了3个类来验证继承关系中的初始化顺序:Father父类、Son子类和Sample类。父类和子类中各自包含了非静态代码区、静态代码区、静态成员、普通成员。运行时的主类为InitialOrderWithExtend类,main()方法中创建了一个子类的对象,并且使用Father对象指向Son类实例的引用(父类对象指向子类引用,多态)。

    代码2

    package com.j2se;
    
    public class InitialOrderWithExtend {
        public static void main(String[] args) {
            Father ts = new Son();
        }
    }
    
    class Father {
        {
            System.out.println("父类 非静态块 1  执行");
        }
        static {
            System.out.println("父类 static块 1  执行");
        }
        static Sample staticSam1 = new Sample("父类 静态成员 staticSam1 初始化");
        Sample sam1 = new Sample("父类 普通成员 sam1 初始化");
        static Sample staticSam2 = new Sample("父类 静态成员 staticSam2 初始化");
        static {
            System.out.println("父类 static块 2  执行");
        }
    
        Father() {
            System.out.println("父类 默认构造函数被调用");
        }
    
        Sample sam2 = new Sample("父类 普通成员 sam2 初始化");
    
        {
            System.out.println("父类 非静态块 2  执行");
        }
    
    }
    
    class Son extends Father {
        {
            System.out.println("子类 非静态块 1  执行");
        }
    
        static Sample staticSamSub1 = new Sample("子类 静态成员 staticSamSub1 初始化");
    
        Son() {
            System.out.println("子类 默认构造函数被调用");
        }
    
        Sample sam1 = new Sample("子类 普通成员 sam1 初始化");
        static Sample staticSamSub2 = new Sample("子类 静态成员 staticSamSub2 初始化");
    
        static {
            System.out.println("子类 static块1  执行");
        }
    
        Sample sam2 = new Sample("子类 普通成员 sam2 初始化");
    
        {
            System.out.println("子类 非静态块 2  执行");
        }
    
        static {
            System.out.println("子类 static块2  执行");
        }
    }
    
    class Sample {
        Sample(String s) {
            System.out.println(s);
        }
    
        Sample() {
            System.out.println("Sample默认构造函数被调用");
        }
    }

    运行结果:

    父类 static1  执行
    父类 静态成员 staticSam1 初始化
    父类 静态成员 staticSam2 初始化
    父类 static2  执行
    子类 静态成员 staticSamSub1 初始化
    子类 静态成员 staticSamSub2 初始化
    子类 static1  执行
    子类 static2  执行
    父类 非静态块 1  执行
    父类 普通成员 sam1 初始化
    父类 普通成员 sam2 初始化
    父类 非静态块 2  执行
    父类 默认构造函数被调用
    子类 非静态块 1  执行
    子类 普通成员 sam1 初始化
    子类 普通成员 sam2 初始化
    子类 非静态块 2  执行
    子类 默认构造函数被调用

    由输出结果可知,执行的顺序为:

    1. 父类静态代码区和父类静态成员
    2. 子类静态代码区和子类静态成员
    3. 父类非静态代码区和普通成员
    4. 父类构造函数
    5. 子类非静态代码区和普通成员
    6. 子类构造函数

    与非继承关系中的初始化顺序一致的地方在于,静态代码区和父类静态成员、非静态代码区和普通成员是同一级别的,当存在多个这样的代码块或者成员时,初始化的顺序和它们在程序中申明的顺序一致;此外,静态代码区和静态成员也是仅仅初始化一次,但是在初始化过程中,可以修改静态成员的引用。

    初始化顺序图示

    非继承关系

    非继承关系初始化顺序

    继承关系

    继承关系初始化顺序

    类初始化顺序的JVM解释

    类初始化顺序受到JVM类加载机制的控制,类加载机制包括加载、验证、准备、解析、初始化等步骤。不管是在继承还是非继承关系中,类的初始化顺序主要受到JVM类加载时机、解析和clinit()初始化规则的影响。

    加载时机

    加载是类加载机制的第一个阶段,只有在5种主动引用的情况下,才会触发类的加载,而在其他被动引用的情况下并不会触发类的加载。关于类加载时机和5中主动引用和被动引用详见【深入理解JVM】:类加载机制。其中3种主动引用的形式为:

    • 程序启动需要触发main方法的时候,虚拟机会先触发这个类的初始化
    • 使用new关键字实例化对象、读取或设置一个类的静态字段(被final修饰、JIT时放入常量池的静态字段除外)、调用一个类的静态方法,会触发初始化
    • 当初始化一个类的时候,如果其父类没有初始化,则需要先触发其父类的初始化

    代码1中触发main()方法前,需要触发主类InitialOrderWithoutExtend的初始化,主类初始化触发后,对静态代码区和静态成员进行初始化后,打印”第1个主类对象:”,之后遇到newInitialOrderWithoutExtend ts = new InitialOrderWithoutExtend();,再进行其他普通变量的初始化。

    代码2是继承关系,在子类初始化前,必须先触发父类的初始化。

    类解析在继承关系中的自下而上递归

    类加载机制的解析阶段将常量池中的符号引用替换为直接引用,主要针对的是类或者接口、字段、类方法、方法类型、方法句柄和调用点限定符7类符号引用。关于类的解析过程详见【深入理解JVM】:类加载机制

    而在字段解析、类方法解析、方法类型解析中,均遵循继承关系中自下而上递归搜索解析的规则,由于递归的特性(即数据结构中栈的“后进先出”),初始化的过程则是由上而下、从父类到子类的初始化顺序。

    初始化clinit()方法

    初始化阶段是执行类构造器方法clinit() 的过程。clinit() 是编译器自动收集类中所有类变量(静态变量)的赋值动作和静态语句块合并生成的。编译器收集的顺序是由语句在源文件中出现的顺序决定的。JVM会保证在子类的clinit() 方法执行之前,父类的clinit() 方法已经执行完毕。

    因此所有的初始化过程中clinit()方法,保证了静态变量和静态语句块总是最先初始化的,并且一定是先执行父类clinit(),在执行子类的clinit()。

    代码顺序与对象内存布局

    在前面的分析中我们看到,类的初始化具有相对固定的顺序:静态代码区和静态变量先于非静态代码区和普通成员,先于构造函数。在相同级别的初始化过程中,初始化顺序与变量定义在程序的中顺序是一致的。

    而代码顺序在对象内存布局中同样有影响。(关于JVM对象内存布局详见【深入理解JVM】:Java对象的创建、内存布局、访问定位。)

    在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。而实例数据是对象真正存储的有效信息,也是程序代码中所定义的各种类型的字段内容。

    无论是从父类继承还是子类定义的,都需要记录下来,这部分的存储顺序JVM参数和字段在程序源码中定义顺序的影响。HotSpot虚拟机默认的分配策略为longs/doubles、ints、shorts/chars、bytes/booleans、oop,从分配策略中可以看出,相同宽度的字段总是分配到一起。满足这个条件的前提下,父类中定义的变量会出现在子类之前。不过,如果启用了JVM参数CompactFields(默认为true,启用),那么子类中较窄的变量也可能会插入到父类变量的空隙中。

    展开全文
  • java类继承总结一 父类类型与子类类型之间的转化问题 本文将通过一个实例描述父类类型与子类类型之间的转化问题,这个很特殊的问题常常会导致一些潜在的危险,让你整整一个晚上都在调试程序以解决一个让人抓狂的...

    java类继承总结一 父类类型与子类类型之间的转化问题 

    本文将通过一个实例描述父类类型与子类类型之间的转化问题,这个很特殊的问题常常会导致一些潜在的危险,让你整整一个晚上都在调试程序以解决一个让人抓狂的java.lang.ArrayStoreException异常。

    1. 子类数组的引用可以装换为超类数组的引用

     2. 子类的引用child可以转换为父类的引用parent(这里假设parent是父类对象,child是子类对象),但却不可以通过 parent调用child的特有方法

     

    class  Employee {
      
    protected String name;
      
    protected long salary;
      
    public Employee(String aName){
        
    this.name = aName;
      }

      
      
    public void setSalary(long aSalary){
        
    this.salary = aSalary;
      }

      
      
    public long getSalary(){
        
    return this.salary;
      }

    }


    class  Manager  extends  Employee {
      
    protected long bonus;
      
    public Manager(String aName){
        
    super(aName);
      }

      
      
    public void setBonus(long aBonus){
        
    this.bonus = aBonus;
      }

      
      
    public long getSalary(){
        
    return super.getSalary()+this.bonus;
      }

    }


    public   static   void  main(String[] args)  {
        
    //注意,这时managers 和employees 引用的是同一个数组
        Manager[] managers = new Manager[10];
        Employee[] employees 
    = managers;
        
    //(a)注意,下面三条语句是可以通过的,但运行时会抛出异常
        employees[0= new Employee("Abel"); //运行时此处抛出java.lang.ArrayStoreException异常
        managers[0].setSalary(1000);
        managers[
    0].setBonus(500);
        
    //(b)这样的语句编译和运行都没有问题
        employees[0= new Manager("Abel");
        employees[
    0].setSalary(1000);
        managers[
    0].setSalary(1000);
        managers[
    0].setBonus(500);
    }

    总结:
    1. 在java中,对象变量是多态的,一个employee既可以引用Employee类型的实例,也可以引用子类Manager类型的实例,但一个manager不能引用一个Employee类型的实例,例如:
       (1) Employee e = new Manager("Abel") 是合法的
       (2)Manager   m = new Employee("Abel") 是不合法的
      原因很容易理解,manager与employee是is-a的关系,任何一个manager都是一个emplpyee,如(1)所示,一个Employee类型的引用e可以引用Manager类型的实例,这意味着可以通过e使用Manager中任何继承自Employee的方法(注意,e不能使用Manager的特有方法,如e.setBonus(1000)是不合法的),Employee e = new Manager("Abel")的潜台词就是说,e从Employee的角度描述Manager,e就是一个Employee,不管e引用的是什么,编译器就把它当一个Employee来看。

    2. 对于上面(a)的代码,不能运行是可以理解的, employees[0]虽然在声明时是Employee类型,但Employee[] employees = managers;规定了employees[i]指向的必须是Manager类型的实例,因此employees[0] = new Employee("Abel"); 这条语句必然会引发异常。
       (a)处的代码employees[0] = new Employee("Abel");可以通过编译似乎也情有可原,因为employees声明的类型是Employee[], 那么employees[0]自然可以接受一个Employee类型的引用,但实际上

       Employee[] employees = managers;赋值的过程中又规定了employees接受的必须是Manager类型,从上面的例子可以看出,运行的时候会做这样的检查,但编译的时候不会检查的这么深入,只会检查变量的声明类型,看来java编译器还不够智能啊 :)

    3. 在实际编程的过程中,使用对象变量的多态性时一定要注意(尤其是使用数组的时候),否则编译通过,但运行报错,这个错误很可能让你调试一个晚上才能找出问题所在!!

    展开全文
  • Spring Bean的继承java类继承的区别

    千次阅读 2007-03-27 15:08:00
    1.Spring中子bean和父bean可以是不同类型,java中子必定是一个特定的父类2.Spring中继承是实例之间的关系,主要表现在参数的延续,java类中的继承之间的关系,主要表现为方法,属性的延续3.Spring子bean不可以...

    1.Spring中子bean和父bean可以是不同类型,java中子类必定是一个特定的父类

    2.Spring中继承是实例之间的关系,主要表现在参数的延续,java类中的继承是类之间的关系,主要表现为方法,属性的延续

    3.Spring子bean不可以作为父bean使用,不具有多态性,java反之 

    展开全文
  • 如果子类(接口的实现),只不过在子类中可以选择重写或者不重写该方法(即便接口要求必须重写接口中所有的方法),先extends,后implement,所以在 implement 的时候该中已经隐含有了该方法(从父类继承),...

    答案:可以(此处的相同是指方法名相同,参数列表相同,返回类型相同)

    如果子类(接口的实现类),只不过在子类中可以选择重写或者不重写该方法(即便接口要求必须重写接口中所有的方法),先extends,后implement,所以在 implement 的时候该类中已经隐含有了该方法(从父类继承),所以可以选择不重写

    如果仅是方法名相同,参数列表相同,返回类型不同则不能,原因和不能用返回值来区分方法重载类似。

    
     
    1. public class InterfaceExtendsOverride extends C implements I{
    2. // @Override
    3. // public void f() {//f()in "XXXX" clashes with f() in "XXX类名",attempting to use incompatile return type
    4. // //extends 已经从 C 继承了一个 int f(),即该类中已经隐含了一个 f(),同一个类中要有同名函数,必须参数列表不同, 显然 void f()不满足该条件,所以编译报错
    5. //
    6. // }
    7. //}
    8. //
    9. //
    10. //class C{
    11. // int f(){return 0;}
    12. //}
    13. //
    14. //interface I{
    15. // void f();
    16. //}

     

    展开全文
  • Java 继承

    千次阅读 多人点赞 2019-05-07 11:12:43
    java继承的概念 Java继承是面向对象的最显著的一个特征。继承是从已有的中派生出新的,新的能吸收已有的数据属性和行为, 并能扩展新的能力, 继承使用的是extends 的继承格式 class 父类 { } class ...
  • Java类是单继承的。classB Extends classAjava接口可以多继承。Interface3 Extends Interface0, Interface1, interface……不允许多重继承的主要原因是,如果A同时继承B和C,而B和C同时有一个D方法,A如何决定该...
  • java类继承

    千次阅读 2012-04-09 20:20:51
    今天讲的内容很重要哦,是关于继承,这将会是学习java的关键; 继承:是面向对象编程的三大特征之一,也是实现软件复用的重要手段;java继承具有单继承的特点,即每个子类只能有一个直接父类; 继承提高...
  • java IO 类继承关系图

    热门讨论 2011-05-28 09:52:08
    Java IOReader/Writer中关系继承图,IO继承关系一目了然
  • java 继承

    千次阅读 2017-09-23 14:47:19
    这个星期主要是学习了Java继承问题.继承就是由已有的创建新,通过子类继承父类的方法,实现一些功能. 下面就是老师布置作业的其中一个: 定义父类圆,通过继承,获得子类球、圆锥、圆柱,子类具有计算体积的...
  • Java泛型类继承实现

    万次阅读 2018-08-07 22:22:24
    项目中经常会碰到泛型继承实现,这里讲解一下Java是如何实现泛型继承的。 泛型父类声明 泛型父类声明如下: public class Father<T, M> { public void test(T t, M m) { // do something with ...
  • Java继承继承的加载顺序

    千次阅读 2013-04-12 11:04:31
     Java继承是使用已存在的的定义作为基础建立新的技术,新的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。这种技术使得复用以前的代码非常容易,能够大大缩短开发周期,降低...
  • java-继承实现学生和老师案例

    万次阅读 多人点赞 2017-06-25 17:23:27
    java-继承实现学生和老师案例:: 定义一个父类: package day08; public class PersonDemo { private String name; private int age; public PersonDemo(){ } public PersonDemo(String name,int ...
  • 我想问下自定义类继承Frame后,是不是自定义本身就是一个Frame,比如 class MyFrame exdents Frame{ Public MyFrame(){ add(某个控件); //这里add添加控件到的是哪里,是MyFrame吗? } } 求大大解答,谢谢...
  • java类继承

    千次阅读 2018-10-26 00:00:46
    实验四 继承 实验目的 掌握继承方法; 变量的继承和覆盖,方法的继承、重载和覆盖实现; 实验内容  实现代码 package circle; public class Circle { private double radius; public Circle() { ...
  • Java继承

    万次阅读 多人点赞 2019-06-06 17:25:11
    面向对象编程中,继承是一个比较重要的概念,继承描述的是事物之间的所属关系,是从已有的中构建出新的,已有的称为父类,新的称为子类,子类具有父类的数据属性和方法,并能扩展新的属性和方法(不能继承...
  • 概念:一个类(子类)可以使用从另一个类(父类、超类)继承属性...2、如何使用继承广义上讲,继承类、继承抽象类、实现接口都可以称为继承,但目前所说的java继承只是继承类或抽象类,即is-a模式最直接的体现。 了
  • java抽象继承

    千次阅读 2016-12-14 10:53:25
    java基础 抽象继承
  • java继承详解。

    万次阅读 多人点赞 2018-07-11 21:42:48
     Java继承具有单继承的特点, 每个子类只有一个直接父类.继承的特点Java的继承通过extends关键字实现. 实现继承的被称为子类. 被继承的被称为父类. 父类和子类的关系, 是一种一般和特殊的关系. 例如水果和...
  • java 中普通类继承,抽象类继承,接口类继承,子类一定要重写父类中的方法吗不一定。1. 普通类继承,并非一定要重写父类方法。2. 抽象类继承,如果子类也是一个抽象,并不要求一定重写父类方法。如果子类不是抽象...
  • JavaJava泛型类继承Comparable接口

    千次阅读 2020-06-04 11:01:15
    遇到一个泛型中的新应用,记录一下,如果泛型中的泛型需要进行比较可以通过让泛型继承Comparable接口的方式来实现,如下: public class SortedSymbolList<Key extends Comparable<Key>, Value> { ...
  • Java继承实例

    千次阅读 2019-05-17 15:08:28
    继承java面向对象编程技术的一块基石,因为它允许创建分等级层次的继承可以理解为一个对象从另一个对象获取属性的过程。 如果A是B的父类,而B是C的父类,我们也称C是A的子类,C是从A继承而来的。...
  • Java类之间的继承关系

    千次阅读 2016-12-15 18:38:46
    Java父类与子类继承关系,调用的各种关系示例一(子类调用父类函数):// 定义一 A public class A { // 此方法打印一句话 public void a() { System.out.println("a method ..."); }}// B 继承 A public ...
  • 答案是:java中子是不会继承父类的构造函数但是可以调用 父类 Animal: package encryption; public class Animal { private String name = "animal"; public Animal() { System.out.println(".....this ...
  • Java类继承、接口的继承和实现

    千次阅读 2017-04-23 17:08:47
    Java的接口有继承和实现两个概念,接口可以继承另一个接口,并获得其父接口里的所有方法和成员变量,接口也可以被一个实现,实现接口的需要实现接口及其父接口里的所有抽象方法,怎么理解呢? (1)如果...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,225,563
精华内容 490,225
关键字:

java类的继承

java 订阅