注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

欢迎来到三件事的空间

欢迎登陆 javacs.cn

 
 
 

日志

 
 

java继承容易犯的错误  

2010-09-14 14:59:49|  分类: java |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

public abstract class A {
int i=1;
public void printI() {
    System.out.println("i="+i);
}
}

public class B extneds A{
int i=2;
public static void main(String[] args) {
    B b=new B();
    b.printI();
}
}

那么,控制台打出来的i的值是多少?
呵呵,如果一下功夫就能说出正确结果1,那么,下面部分就不需要往下看了。

1、类的继承知识点
(1)java不支持多重继承,也就是说子类至多只能有一个父类
(2)子类继承了其父类中不是私有的成员变量和成员方法,作为自己的成员变量和方法
(3)子类中定义的成员变量和父类中定义的成员变量相同时,则父类中的成员变量不能被继承
(4)子类中定义的成员方法,并且这个成员方法的名字,返回类型,及参数个数和类型与父类的某个成员方法完全相同,则父类的成员方法不能被继承。

2、答案是2者如是说
子类B中的变量i和父类A中的变量i重名, 那么子类B中的变量i将会覆盖掉父类中的同名变量i. 则访问父类中的变量时jvm会把子类cast到父类.所以,打印出的结果应该是“i=2”;

3、歧义的产生
歧义的产生最关键的地方是子类B中的变量i将会覆盖掉父类中的同名变量i的覆盖两个字。这里,我觉得这两个字容易误导。应该改为屏蔽或隐藏。因为在这里父类的成员变量是没有被改变。

4、jvm的执行过程
(1)子类B 的构造方法被调用,实例化一个B对象,B对象的成员被初始化
(2)jvm隐含的调用父类的构造方法,实例化一个A对象,A对象的成员被初始化。
(3)由于A对象的printI()未被屏蔽,所以调用的A对象的printI()函数。
那么,在这里A的成员函数当然是访问自己的成员变量了。

5、super关键字
super关键字在java中的作用是使被屏蔽的成员变量或者成员方法或变为可见,或者说用来引用被屏蔽的成员变量和成员成员方法。super是用在子类中,目的是访问直接父类中被屏蔽的成员。上面的代码也可以这样写:

public abstract class A {
int i=1;
public void printI() {
    System.out.println("i="+i);
}
}
public class B extends A{
public int i=2;
public void printI(){
   super.printI();
}
public static void main(String[] args){
   B b= new B();
   b.printI();
  
}
}

附录:java实例化的本质:

当new一个对象的时候发生了什么?我就给大家讲讲吧,如果不对的地方,还请大家指正
如new MyObject();

1:寻找类定义
jvm会在自己的一个名叫“方法区”的内存块中,寻找名叫“MyObject”的Class对象(注意class也是一个对象,该对象记录了所有类的定义),如果有,则按照Class对象的定义,生成一个MyObject对象。

2:加载类定义
如果“方法区”中没有名为“MyObject”的Class对象,jvm会用当前类的类加载器(classloader)从当前的classpath路径 寻找名为"MyObject.class"的文件,如果找到,则将文件进行分析,转换为Class对象存放在“方法区”中,否则抛出 “ClassNotFoundException”。对于jdk的class,jvm启动时,会用启动类加载器加载,对于用户的class,则会用应用程 序类加载器实时加载,所谓实时加载,指的是遇到的时候再加载,而不是预先一次性加载。关于类加载器,有三级,jvm严格的限制了每一级的加载权限,加载模 式为“双亲委托模式”,加载任何类,都先由父加载器加载。


3:给对象分配内存空间
找到MyObject的类定义后,jvm在内存“堆”中,开辟一个空间,该空间按照MyObject类定义开辟,并将该空间中的各个内存段设置默认值,对应的就是对象的属性初始化默认值。


4:对象的初始化顺序
对象的初始化都先从父类开始,顺序如下:
给父类静态变量默认值
对父类静态变量赋值
执行父类静态块

给当前类静态变量默认值
对当前类静态变量赋值
执行当前类静态块

给父类变量默认值
对父类变量赋值
执行父类构造函数

给当前类变量默认值
对当前类变量赋值
执行当前类构造函数

5:对象构造完成



还有一点要提醒楼主,当你new一个String的时候,只是生成一个String对象,而没有生成Object对象,Object的类定义在“方法区” 这块内存中,当new String的时候,jvm会检查String的父类,找出父类的定义,并找出哪些是String可以拥有的,然后按照筛选出来的父类定义和String 本身的类定义,在堆中分配一个内存块(就是俗称的生成了一个对象),而没有专门为String的父类Object分配空间。

有这样的一段代码:

public class Test {
static{
   System.out.println("静态块");
}
{
   System.out.println("初始化模块");
}
public Test() {
   System.out.println("构造方法");
}
public static void show(){
   System.out.println("静态方法");
}
public static void main(String[] args) {
   new Test();
}
}

输出结果:

静态块

初始化模块

构造方法

在上面的代码中我们可以看到,总共存在这样几个可以做初始化的地方:
1、静态初始化模块
2、默认初始化
3、初始化模块(即只有'{}')
4、构造方法

现在我们来说明这些初始化的步骤:
1、在类第一次被加载的时候的,静态初始化模块会工作完成一个部分初始化任务
2、在类构造对象的时候,具体步骤如下:
    a、系统创建类的实例对象
    b、执行默认初始化,如果程序中指定了默认初始化的值,那么就按照默认值进行初始化,如果程序中没有指定初始化的值,那么由系统自动赋值,赋值的原则为: “一切都是0”,整型属性赋值0,浮点属性赋值0.0,字符属性赋值‘\0',布尔类型属性赋值false,引用类型属性赋值null
    c、执行初始化模块,如果程序中存在多个初始化模块,那么按照书写顺序执行所有初始化模块。
    d、执行对象中的构造方法。

   
/***
* 继承类的初始化:
* 1.装载程序的时候首先找到的是它的基础类,如果有多个基础类则会一级一级的
* 往上找,最后找到根基础类.
*
* 2.执行根基础类中的static初始化,再执行下一个衍生类中的static,依此类推,一直保持这个顺序.
*
* 3.此时类已经装载完毕,开始创建对象,所有的基本数据类型都会设成它们的默认值,对象句柄设为null.
*
* 4.调用基础类的构造方法,基础类的构建采用与衍生类构造方法完全相同的处理过程
*
* 5.构造方法初始完之后,进行变量的初始化.
*
* 6.执行构造方法中剩余的部分.
*/

具体示例:

//下面是一个类中的静态变量、变量以及构造函数的初始化顺序。

public class InitialOrderTest
{
    public static String staticField = "静态变量";
    public String field = "变量";

    static
    {
       System.out.println(staticField);
       System.out.println("静态初始化块");
    }

    {
       System.out.println(field);
       System.out.println("初始化块");
    }

    public InitialOrderTest()
    {
    System.out.println("构造器");
    }

    public static void main(String[] args)
    {
    new InitialOrderTest();       
    }
}

//**********************
//输出结果为;
//**********************
//静态变量
//静态初始化块
//变量
//初始化块
//构造器

//下面是子类和父类中静态变量、变量以及构造函数的初始化顺序。public class InitialOrder extends Parent
{
public static String s_StaticField = "子类--静态变量";
public String s_Field = "子类--变量";

static
{
   System.out.println(s_StaticField);
     System.out.println("子类--静态初始化块");
}
   
{
   System.out.println(s_Field);
   System.out.println("子类--初始化块");
}

public InitialOrder()
{
   System.out.println("子类--构造器");
}

public static void main(String[] args)
{
   new InitialOrder();
}
}

class Parent
{
    public static String p_StaticField = "父类--静态变量";
    public String p_Field = "父类--变量";

    static
    {
        System.out.println(p_StaticField);
        System.out.println("父类--静态初始化块");
    }

    {
    System.out.println(p_Field);
    System.out.println("父类--初始化块");
    }

    public Parent()
    {
    System.out.println("父类--构造器");
    }
}

//**********************
//输出结果为;
//**********************
//父类--静态变量
//父类--静态初始化块
//子类--静态变量
//子类--静态初始化块
//父类--变量
//父类--初始化块
//父类--构造器
//子类--变量
//子类--初始化块
//子类--构造器

  评论这张
 
阅读(85)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018