练习(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");
}
}