thinking in java test chapter7练习(1)~(10)组合与继承

练习(1):创建一个简单的类。在第二个类中,将一个引用定义为第一个类的对象。运用惰性初始化来实例化这个对象。
编译器不会给每个引用默认创建对象,因为这样会造成资源的浪费。想要初始化引用,必须按这种格式:类型 类名 = new 类型;
初始化引用可以在一下4种位置上进行:
1,在定义对象的地方。就是我们在新创建一个引用时直接在后面创建对象。我们知道编译器加载java文件时先加载各种静态成员,再加载成员变量,再加载方法,所以这时引用初始化先于构造器被调用。这时如果我们的引用指向的对象的构造器中有什么方法的话,也会优先于当前对象(实例化后)的构造方法运行。
2,在构造器中初始化。
3,在要使用到时初始化。
4,使用实例初始化。(关于实例初始化的解释)
http://www.programcreek.com/2011/10/java-class-instance-initializers/
http://www.open-open.com/solution/view/1409534328385

而方法3就是练习中所说的惰性初始化。这个方法保证了在需要时才生成对象,同1,2方法相比,减少了额外的负担。

public class test1 {
    public static void main(String[] args){
        Bath bath = new Bath();
        bath.cook();
    }
}

class Bath{
    Soap soap1 = new Soap("在定义对象的地方初始化");//在定义对象的地方之间初始化
    Soap soap2;
    Soap soap3;
    public Bath(){
        soap2 = new Soap("在构造器中初始化");
    }

    public void cook(){
        soap3 = new Soap("惰性初始化");
    }

    {
        Soap soap4 = new Soap("实例初始化");
    }
}

class Soap{
    private String name;
    public Soap(String str){
        name = str;
        System.out.println("name :" + name);
    }
}

控制台输出结果:
name :在定义对象的地方初始化
name :实例初始化
name :在构造器中初始化
name :惰性初始化
可以看到惰性初始化是等到被用到时在初始化。而1,4两种方法是先于构造器运行的。

练习(2):从Detergent.main()中继承产生一个新的类。覆盖scrub()并添加一个名为sterilize()的新方法。
这个练习就是让我们练习一下类的继承,覆盖父类方法,添加新方法。

public class test2  extends Object{//默认继承自object,不写也没事
    private String name;//当其它类继承当前类时,会访问所有public内容,为了保护数据成员,将其声明为私有的

    public test2(String name){//方法包括构造方法,允许覆盖重写,所有是公有的
        this.name = name;
    }
    @Override
    public String toString() {//重写object中的方法toString()
        return "name : " + name;
    }

    public void setName(String name) {//添加一个新方法
        this.name = name;
    }
}

练习(3):证明前面这句话。
前面的话是:构建过程是从基类“向外”扩散的,所有基类在导出类(也就是继承自其父类的子类)构造器可以访问它之前,就已经完成了初始化。即使你不为Cartoon()创建构造器,编译器也会为你合成一个默认的构造器,该构造器将调用基类的构造器。
练习(4):证明基类构造器:(a)总是会被调用;(b)在导出类构造器之前被调用。
上面两个练习将在一个程序中证明。

public class test3 {
    public static void main(String[] args){
        LearnigAndroid android = new LearnigAndroid();
    }
}

class LearnigEnglish {
    LearnigEnglish(){
        System.out.println("you are learning english");
        System.out.println("you finished learning english and passed CET_4");
    }
}

class LearningJava extends LearnigEnglish{
    LearningJava(){
        System.out.println("you are learning java");
        System.out.println("you can make some small java project");

    }
}

class LearnigAndroid extends LearningJava{
    public LearnigAndroid(){
        System.out.println("you are learning android");
        System.out.println("you can make an APP");
    }
}

当我们注销LearningAndroid的构造方法时,控制台输出:
you are learning english
you finished learning english and passed CET_4
you are learning java
you can make some small java project
写出LearningAndroid构造方法后,输出:
you are learning english
you finished learning english and passed CET_4
you are learning java
you can make some small java project
you are learning android
you can make an APP
其实上面两个练习证明的道理很好理解,就像代码所描述一样,在学会Android之前你总得学会java吧,在学会java之前你总得会点英语吧。如果英文字母都不认识,怎么可能一步就跳到学会Android?

练习(5):创建两个带有默认构造器(空参数列表)的类A和类B。从A 中继承产生一个名为C的新类,并在C内创建一个B类的成员。不要给C编写构造器。创建一个C类的对象并观察其结果。

public class test5 {
    public static void main(String[] args){
        C c = new C();
    }
}
class A{
    public A(){
        System.out.println("A is construct");
    }
}

class B{
    public B(){
        System.out.println("B is construct");
    }
}

class C extends A{
    B b = new B();
}

控制台输出:
A is construct
B is construct

练习(6):用Chess.java来证明前面一段话。
前一段话是:如果不在BoardGame()中调用基类构造器,编译器将“抱怨”无法找到符合Game形式的构造器。而且调用基类构造器必须是你在导出类构造器中要做的第一件事(如果你做错了编译器就会提醒你)。
我们不一定要按书上的代码来,这里笔者修改了一下练习(3)的代码(肯定不是因为笔者懒。。。)

public class test3 {
    public static void main(String[] args){
        LearnigAndroid android = new LearnigAndroid("小明");
    }
}

class LearnigEnglish {
    LearnigEnglish(String name){
        System.out.println(name + " are learning english");
        System.out.println(name + " finished learning english and passed CET_4");
    }
}

class LearningJava extends LearnigEnglish{
    LearningJava(String name){
        super(name);
        System.out.println(name + " are learning java");
        System.out.println(name + " can make some small java project");

    }
}

class LearnigAndroid extends LearningJava{
    public LearnigAndroid(String name){
        super(name);
        System.out.println(name + " are learning android");
        System.out.println(name + " can make an APP");
    }
}

super语句可以理解为把参数传给基类构造器使用。
当我们注释掉子类的super语句时,编译器报错:there is no default constructor available inxxx.
当我们将super语句移动到构造器最后一行时,编译器报错:Call to super() must be first statement in constructor body.
super语句必须是构造器中的第一个。
从逻辑上也很好理解这两个。
我们把name看成一个人,要想LearningEnglish,必须要有一个人,而LearningJava继承自LearningEnglish,所以也需要一个人,但这个人传进来的时候不是天生就会English的,这个人是空白的。所以需要super调用一下父类的构造方法,先让这个人学会English,然后才能学java。

练习(7):修改练习(5),使A和B以带参数的构造器取代默认的构造器。为C写一个构造器并在其中执行所有的初始化。

public class test5 {
    public static void main(String[] args){
        C c = new C(10);
    }
}
class A{
    public A(int i){
        System.out.println(i + "A is construct");
    }
}
class B{
    public B(int i){
        System.out.println(i + "B is construct");
    }
}
class C extends A{
    B b ;
    public C(int i) {
        super(i);
        b = new B(i);
    }
}

练习(8):创建一个基类,它仅有一个非默认的构造器;再创建一个导出类,它带有默认构造器和非默认构造器。在导出类的构造器中调用基类的构造器。
非默认的构造器就是带参数的构造器。一般当你创建一个类并且没有给其写任何构造器时,java就会默认给其创建一个构造器。该构造器里面不做任何事。如果你自己写了一个无参构造器,也就相当于覆盖了java给其添加的默认构造器。当然如果你写了一个带参构造器后,java也不会给其添加无参构造器。该类此时只有你写的那个带参构造器。
我们继续修改练习(3)的代码(真的不是懒。。。)

public class test3 {
    public static void main(String[] args){
//        LearnigAndroid android = new LearnigAndroid("小明");
        LearningJava java1 = new LearningJava();
        LearningJava java2 = new LearningJava("小红");
    }
}

class LearnigEnglish {
    LearnigEnglish(String name){
        System.out.println(name + " are learning english");
        System.out.println(name + " finished learning english and passed CET_4");
    }
}

class LearningJava extends LearnigEnglish{
    public LearningJava(){
        super("name");
    }
    public LearningJava(String name){
        super(name);
        System.out.println(name + " are learning java");
        System.out.println(name + " can make some small java project");

    }
}

这里我们发现在LearningJava的两个构造器中,无论是有参还是无参,都必须super基类的构造方法。
练习3,4我们已经证明过对象的构建过程是从基类开始的,由于基类只有一个带参的构造器,所以我们必须用super传一个name回去,才能保证基类的构造器会顺利调用。

练习(9):创建一个Root类,令其含有名为Component1,Component2,Component3类的各一个实例(这些也由你写)。从Root中派生一个类Stem,也含有上述各“组成部分”。有的类都应带有可打印出类的相关信息的默认构造器。
派生就相对于创建一个类Stem继承自Root.

public class test9 {
    public static void main(String[] args){
        Stem stem = new Stem();
    }
}
class Component1{
    public Component1(){
        System.out.println("Component1 is created");
    }
}
class Component2{
    public Component2(){
        System.out.println("Component2 is created");
    }
}
class Component3{
    public Component3(){
        System.out.println("Component3 is created");
    }
}
class Root{
    Component1 component1;
    Component2 component2;
    Component3 component3;
    public Root(){
        component1 = new Component1();
        component2 = new Component2();
        component3 = new Component3();
        System.out.println("Root is created");
    }
}
class Stem extends Root{
    public Stem(){
        System.out.println("Stem is created");
    }
}

练习(10):修改练习10,使每个类都仅具有非默认的构造器。
这里不是笔者笔误,而是原文有错,查英文原版后看到一个单词previous,所以题目应该是修改练习9,使每个类都仅具有非默认的构造器。

public class test9 {
    public static void main(String[] args){
        Stem stem = new Stem(10);
    }
}
class Component1{
    public Component1(int i){
        System.out.println(i + "Component1 is created");
    }
}
class Component2{
    public Component2(int i){
        System.out.println(i + "Component2 is created");
    }
}
class Component3{
    public Component3(int i){
        System.out.println(i + "Component3 is created");
    }
}
class Root{
    Component1 component1;
    Component2 component2;
    Component3 component3;
    public Root(int i){
        component1 = new Component1(1);
        component2 = new Component2(2);
        component3 = new Component3(3);
        System.out.println(i + "Root is created");
    }
}
class Stem extends Root{
    public Stem(int i){
        super(i);
        System.out.println("Stem is created");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值