A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© bo134 中级黑马   /  2016-3-26 14:38  /  448 人查看  /  3 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

为什么会这样输出?这就要明确掌握Java中构造函数的调用顺序:

(1)在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制0;
(2)调用基类构造函数。从根开始递归下去,因为多态性此时调用子类覆盖后的draw()方法(要在调用RoundGlyph构造函数之前调用),由于步骤1的缘故,我们此时会发现radius的值为0;
(3)按声明顺序调用成员的初始化方法;
(4)最后调用子类的构造函数。

只有非private方法才可以被覆盖,但是还需要密切注意覆盖private方法的现象,这时虽然编译器不会报错,但是也不会按照我们所期望的来执行,即覆盖private方法对子类来说是一个新的方法而非重载方法。因此,在子类中,新方法名最好不要与基类的private方法采取同一名字(虽然没关系,但容易误解,以为能够覆盖基类的private方法)。

Java类中属性域的访问操作都由编译器解析,因此不是多态的。父类和子类的同名属性都会分配不同的存储空间,如下:

// Direct field access is determined at compile time.
class Super {
    public int field = 0;
    public int getField() {
        return field;
    }
}

class Sub extends Super {
    public int field = 1;
    public int getField() {
        return field;
    }
    public int getSuperField() {
        return super.field;
    }
}

public class FieldAccess {

    public static void main(String[] args) {
        Super sup = new Sub();
        System.out.println("sup.filed = " + sup.field +
                ", sup.getField() = " + sup.getField());
        Sub sub = new Sub();
        System.out.println("sub.filed = " + sub.field +
                ", sub.getField() = " + sub.getField() +
                ", sub.getSuperField() = " + sub.getSuperField());
    }

}

输出:

sup.filed = 0, sup.getField() = 1
sub.filed = 1, sub.getField() = 1, sub.getSuperField() = 0

Sub子类实际上包含了两个称为field的域,然而在引用Sub中的field时所产生的默认域并非Super版本的field域,因此为了得到Super.field,必须显式地指明super.field。

2. is-a关系和is-like-a关系
is-a关系属于纯继承,即只有在基类中已经建立的方法才可以在子类中被覆盖,如下图所示:

基类和子类有着完全相同的接口,这样向上转型时永远不需要知道正在处理的对象的确切类型,这通过多态来实现。

is-like-a关系:子类扩展了基类接口。它有着相同的基本接口,但是他还具有由额外方法实现的其他特性。

缺点就是子类中接口的扩展部分不能被基类访问,因此一旦向上转型,就不能调用那些新方法。

3. 运行时类型信息(RTTI + 反射)
概念
RTTI:运行时类型信息使得你可以在程序运行时发现和使用类型信息。
使用方式
Java是如何让我们在运行时识别对象和类的信息的,主要有两种方式(还有辅助的第三种方式,见下描述):

一种是“传统的”RTTI,它假定我们在编译时已经知道了所有的类型,比如Shape s = (Shape)s1;
另一种是“反射”机制,它运行我们在运行时发现和使用类的信息,即使用Class.forName()。
其实还有第三种形式,就是关键字instanceof,它返回一个bool值,它保持了类型的概念,它指的是“你是这个类吗?或者你是这个类的派生类吗?”。而如果用==或equals比较实际的Class对象,就没有考虑继承—它或者是这个确切的类型,或者不是。
工作原理
要理解RTTI在Java中的工作原理,首先必须知道类型信息在运行时是如何表示的,这项工作是由称为Class对象的特殊对象完成的,它包含了与类有关的信息。Java送Class对象来执行其RTTI,使用类加载器的子系统实现。

无论何时,只要你想在运行时使用类型信息,就必须首先获得对恰当的Class对象的引用,获取方式有三种:
(1)如果你没有持有该类型的对象,则Class.forName()就是实现此功能的便捷途,因为它不需要对象信息;
(2)如果你已经拥有了一个感兴趣的类型的对象,那就可以通过调用getClass()方法来获取Class引用了,它将返回表示该对象的实际类型的Class引用。Class包含很有有用的方法,比如:

package rtti;
interface HasBatteries{}
interface WaterProof{}
interface Shoots{}

class Toy {
    Toy() {}
    Toy(int i) {}
}

class FancyToy extends Toy
implements HasBatteries, WaterProof, Shoots {
    FancyToy() {
        super(1);
    }
}

public class RTTITest {

    static void printInfo(Class cc) {
        System.out.println("Class name: " + cc.getName() +
                ", is interface? [" + cc.isInterface() + "]");
        System.out.println("Simple name: " + cc.getSimpleName());
        System.out.println("Canonical name: " + cc.getCanonicalName());
    }

    public static void main(String[] args) {
        Class c = null;
        try {
            c = Class.forName("rtti.FancyToy"); // 必须是全限定名(包名+类名)
        } catch(ClassNotFoundException e) {
            System.out.println("Can't find FancyToy");
            System.exit(1);
        }
        printInfo(c);

        for(Class face : c.getInterfaces()) {
            printInfo(face);
        }

        Class up = c.getSuperclass();
        Object obj = null;
        try {
            // Requires default constructor.
            obj = up.newInstance();
        } catch (InstantiationException e) {
            System.out.println("Can't Instantiate");
            System.exit(1);
        } catch (IllegalAccessException e) {
            System.out.println("Can't access");
            System.exit(1);
        }
        printInfo(obj.getClass());
    }

}

输出:

Class name: rtti.FancyToy, is interface? [false]
Simple name: FancyToy
Canonical name: rtti.FancyToy
Class name: rtti.HasBatteries, is interface? [true]
Simple name: HasBatteries
Canonical name: rtti.HasBatteries
Class name: rtti.WaterProof, is interface? [true]
Simple name: WaterProof
Canonical name: rtti.WaterProof
Class name: rtti.Shoots, is interface? [true]
Simple name: Shoots
Canonical name: rtti.Shoots
Class name: rtti.Toy, is interface? [false]
Simple name: Toy
Canonical name: rtti.Toy

(3)Java还提供了另一种方法来生成对Class对象的引用,即使用类字面常量。比如上面的就像这样:FancyToy.class;来引用。
这样做不仅更简单,而且更安全,因为它在编译时就会受到检查(因此不需要置于try语句块中),并且它根除了对forName方法的引用,所以也更高效。类字面常量不仅可以应用于普通的类,也可以应用于接口、数组以及基本数据类型。

注意:当使用“.class”来创建对Class对象的引用时,不会自动地初始化该Class对象,初始化被延迟到了对静态方法(构造器隐式的是静态的)或者非final静态域(注意final静态域不会触发初始化操作)进行首次引用时才执行:。而使用Class.forName时会自动的初始化。

为了使用类而做的准备工作实际包含三个步骤:
- 加载:由类加载器执行。查找字节码,并从这些字节码中创建一个Class对象
- 链接:验证类中的字节码,为静态域分配存储空间,并且如果必需的话,将解析这个类创建的对其他类的所有引用。
- 初始化:如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块。

这一点非常重要,下面通过一个实例来说明这两者的区别:

package rtti;
import java.util.Random;
class Initable {
        static final int staticFinal = 47;
        static final int staticFinal2 = ClassInitialization.rand.nextInt(1000);

        static {
            System.out.println("Initializing Initable");
        }
}
class Initable2 {
        static int staticNonFinal = 147;

        static {
            System.out.println("Initializing Initable2");
        }
}
class Initable3 {
        static int staticNonFinal = 74;

        static {
            System.out.println("Initializing Initable3");
        }
}
public class ClassInitialization {

        public static Random rand = new Random(47);

        public static void main(String[] args) {
            // Does not trigger initialization
            Class initable = Initable.class;
            System.out.println("After creating Initable ref");
            // Does not trigger initialization
            System.out.println(Initable.staticFinal);
            // Does trigger initialization(rand() is static method)
            System.out.println(Initable.staticFinal2);

            // Does trigger initialization(not final)
            System.out.println(Initable2.staticNonFinal);

            try {
                Class initable3 = Class.forName("rtti.Initable3");
            } catch (ClassNotFoundException e) {
                System.out.println("Can't find Initable3");
                System.exit(1);
            }
            System.out.println("After creating Initable3 ref");
            System.out.println(Initable3.staticNonFinal);
        }
}

输出:

After creating Initable ref
47
Initializing Initable
258
Initializing Initable2
147
Initializing Initable3
After creating Initable3 ref

评分

参与人数 4黑马币 +49 收起 理由
337262778 + 13
持之一以贯之 + 19
太阳当空照 + 13 很给力!
穿礼服的猫 + 4 很给力!

查看全部评分

3 个回复

倒序浏览
写这么多。。不累吗
回复 使用道具 举报
这看的都头疼啊
回复 使用道具 举报
分开发比较好吧,这么长,小朋宇直接劝退啊
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马