分享

Java编程思想读书笔记-1(第2-7章)

 mythtian 2005-10-21



一.所有对象都必须由你建立
1.    存储在哪里
1.    寄存器:我们在程序中无法控制
2.    stack:存放基本类型的数据和对象的reference,但对象本身不存放在stack中,而是存放在Heap中
3.    Heap:存放用new产生的数据
4.    Static storage:存放在对象中用static定义的静态成员
5.    Constant storage:存放常量
6.    NON-RAM:硬盘等永久存储空间
2.    特例:基本型别
基本类型数据存放在Stack中,存放的是数据。而产生对象时,只把对象的reference存放在stack中,用于指向某个对象,对象本身存放在Heap中。
3.    Java中的数组
当你产生某个存储对象的数组时,真正产生的其实是存储reference的数组。引数组建立后,其中的每一个reference都会被自动设为null,表示“不指向任何对象”。
二.建立新的数据型别:Class
1.    数据成员和函数
1.1    基本成员的缺省值
1)    当class的某个成员属于基本型别时,即使你没有为它提供初值,Java仍保证它有一个缺省值。
2)    只有当变量身份是“class内的成员时,Java才保证为该变量提供初值。
三.函数(Mehtods),引数(arguments),返回值(return values)
1.    引数列
当引数传递的是对象时,传递的是对象的reference。
四.注解用内嵌式文档
Java提供两种注解风格:/*XXXX*/、//XXXX

第3章    控制程序流程


一.使用Java运算符
1.关系运算符
1.) 当对两个对象运用关系运算符进行比较时,比较的是object reference,如:
  1. java/lang/Integer.java.html" target="_blank">Integer n1 = new java/lang/Integer.java.html" target="_blank">Integer(3);
  2. java/lang/Integer.java.html" target="_blank">Integer n2 = new java/lang/Integer.java.html" target="_blank">Integer(3);
  3. java/lang/System.java.html" target="_blank">System.out.println(n1==n2);

结果为false,因为两个object reference(n1和n2)值是不同的
2) quals()的缺省行为也是拿referenct来比较。不过Java中的class覆写了equals方法,如:
  1. java/lang/Integer.java.html" target="_blank">Integer n1 = new java/lang/Integer.java.html" target="_blank">Integer(3);
  2. java/lang/Integer.java.html" target="_blank">Integer n2 = new java/lang/Integer.java.html" target="_blank">Integer(3);
  3. java/lang/System.java.html" target="_blank">System.out.println(n1.quals(n2));//值为true

2.    逻辑运算符
1)    只能将and、or、not施用于boolean值身上。如果逻辑运算符两边的值存在non-boolean值,将会出错,如:
  1. int test1 = 1;
  2. java/lang/System.java.html" target="_blank">System.out.println((test && 1<2);//编辑出错,test是non-boolean值
3.    位移运算符
如果所操作的位移对象是char、byte、short,位移动作发生之前,其值会先被晋升为int,运算结果会是int。
二.流程控制
1.    迭代(iteration)
1.1    逗号运算符
逗号运算符只能用于for循环的控制表达式中的initialization和step两部分中,如:for(int i=0, j=I+1; I<5; i++, j=I*2)
1.2    break和continue
break表示退出循环;continue表示退出本次循环,回来循环起始位置。
1.3    label
label只有放在迭代语句之前才起作用,在label和迭代语句之间插入任何语句都不会起作用。
2.    Switch
switch中的选择器必须是int或char型,如:
  1. float i = 2;
  2. switch ( i )//将出错,因为i不是int或char之一

3.    计算细节
1)    从float或double转为整数值,总是以完全舍弃小数的方式进行。
4.    Math.random()的输出范围是[0, 1]。

第4章    初始化和清理


一.以构造函数(constructor)确保初始化的进行
如果某个class具备构造函数,Java便会在对象生成之际,使用者有能力加以操作之前,自动调用其构造函数,于是便能名确保初始化动作一定被执行。
二.函数重载(Method overloading)
1.    区分重载函数
由于只能从函数名和函数的引数列来区分两个函数,而重载函数具有相同的函数名称,所以每个重载函数都必须具备独一无二的引数列。
2.    Default构造函数
1)    default构造函数是一种不带任何引数的构造函数。如果你所开发的class不具任何构造函数,编译器会自动为你生成一个default构造函数。
2)    如果你自行定义了任何一个构造函数(不论有无引数),编译器就不会为你生成default构造函数。
3)    如果定义了一个class,如
  1. class Bush{
  2.   Bush(int I){}
  3. }

当想用new Bush();来产生class的实例时,会产生错误。因为在定义class时已定义了构造函数,所以编译器就不会为class生成default构造函数。当我们用new Bush()来产生实例时,会尝试调用default构造函数,但在class中没有default构造函数,所以会出错。如:
  1. class Sundae
  2. {
  3.     Sundae(int i) {}
  4. }
  5. public class IceCream
  6. {
  7.     public static void main(java/lang/String.java.html" target="_blank">String[] args)
  8. {
  9.             //Sundae x = new Sundae();会编译出错,无构造函数Sundae()
  10.         Sundae y = new Sundae(1);
  11.     }
  12. }

*:在定义一个class时,如果定义了自己的构造函数,最好同时定义一个default构造函数
3.    关键字this
1)    this仅用于函数之内,能取得“唤起此一函数“的那个object reference。
2)    在构造函数中,通过this可以调用同一class中别的构造函数,如
  1. public class Flower{
  2.         Flower (int petals){}
  3.         Flower(java/lang/String.java.html" target="_blank">String ss){}
  4.         Flower(int petals, Sting ss){
  5.         //petals++;调用另一个构造函数的语句必须在最起始的位置
  6. this(petals);
  7.         //this(ss);会产生错误,因为在一个构造函数中只能调用一个构造函数
  8.         }
  9. }

**:1)在构造调用另一个构造函数,调用动作必须置于最起始的位置
        2)不能在构造函数以外的任何函数内调用构造函数
        3)在一个构造函数内只能调用一个构造函数
4.    Static的意义
无法在static函数中调用non-static函数(反向可行)。为什么不能呢,我们看下面的例子。
例4.2.4.1
假设能在static函数中调用non-static函数,那么(a)处就将出错。因为在没有产生Movie class实例之前,在就不存在Movie class内的name实例,而在getName()中却要使用name实例,显然的错误的。
  1. class Movie{
  2.     java/lang/String.java.html" target="_blank">String name = “”;
  3.     Movie(){}
  4.     public Movie(java/lang/String.java.html" target="_blank">String name) { this.name = name; }
  5.     public static java/lang/String.java.html" target="_blank">String getName() { return name; }
  6. }
  7. public class Test{
  8.     public static void main(java/lang/String.java.html" target="_blank">String[] args){
  9.         //下面两名先产生实例后再调用getName()没有问题
  10.         //Movie movie1 = new Movie(“movie1”);
  11.         //String name1 = movie1.getName();
  12.         //下面一名将出错
  13.         //String name2 = Movie.getname(); (a)
  14.     }
  15. }

三.清理(cleanup):终结(finalization)与垃圾回收(garbage collection)
1)你的对象可能不会被回收
只有当程序不够内存时,垃圾回收器才会启动去回收不再被使用的对象的内存空间。某个对象所占用的空间可能永远不会被释放掉,因为你的程序可能永远不会逼近内存用完的那一刻,而垃圾回收器完全没有被启动以释放你的对象所占据的内存,那些空间便会在程序终止时才一次归还给操作系统
3)    只有在采用原生函数(native methods)时,才使用finalize()。
四.成员初始化(member initialization)
1)    函数中的变量不会被自动初始化,如
  1. void f(){
  2.         int i;
  3.         i++;
  4. }

将发生编译错误,因为i没有被初始化。
2)    class的数据成员会被自动初始化,具体情况如下(见P220例子):
基本型别:boolean:false、char:null(\u0000)、byte:0、short:0、int:0、
long:0 、float:0、double:0
            对象(reference):null
1.    初始化次序
1)    所有变量一定会在任何一个函数(甚至是构造函数)被调用之前完成初始化(见P233例子)
2)    在产生一个class的对象(包含static成员的class的代码被装载)时,首先自动初始化class中的static成员变量,再执行所有出现于static数据定义处的初始化动作,最后执行static block,所有这些初始化操作只在第一次生成该对象时进行。
3)    自动初始化class中的其它成员变量。
4)    执行所有出现于数据定义处的初始化动作。如:int i=1;的执行顺序是先把I自动初始化为0,再执行数据定义处的初始化动作,初始化为1。
5)    执行non-static block
6)    调用构造函数。
例:
  1. class Cup{
  2.     Cup(int marker){
  3.         java/lang/System.java.html" target="_blank">System.out.println("Cup(" + marker + ")");
  4.     }
  5.     void f(int marker){
  6.         java/lang/System.java.html" target="_blank">System.out.println("f(" + marker + ")");    
  7.     }    
  8. }
  9. class Cups{
  10.     static Cup c1 = new Cup(11);
  11.     static Cup c2;
  12.     Cup c3 = new Cup(33); 
  13.     Cup c4;
  14.     {
  15.         c3 = new Cup(3);
  16.         c4 = new Cup(4);
  17.     }
  18.     static{
  19.         c1 = new Cup(1);
  20.         c2 = new Cup(2);
  21.     }
  22.     Cups(){
  23.         java/lang/System.java.html" target="_blank">System.out.println("Cups()");
  24.     }
  25. }
  26. public class ExplicitStatic{
  27.     public static void main(java/lang/String.java.html" target="_blank">String[] args){
  28.         java/lang/System.java.html" target="_blank">System.out.println("Inside main()");
  29.         Cups.c1.f(99);
  30.     }
  31.     static Cups x = new Cups();
  32.     static Cups y = new Cups();
  33. }

结果为:
Cup(11)
Cup(1)
Cup(2)
Cup(33)
Cup(3)
Cup(4)
Cups()
Cup(33)
Cup(3)
Cup(4)
Cups()
Inside main()
f(99)
2.    Array的初始化
1)    定义数组时不能指定大小。如int[4] iArr = {0, 1, 2, 3};,由于指定了数组的大小,会编译出错。
2)    数组只是存放reference的数组。Array与non-array的结构图如下:
a)对于基本型别数据,存放的是数据。如
int i = 5;
 
b)对于class变量,存放的是reference,这个reference指向一个存有class实例的内存空间。如
  1. java/lang/String.java.html" target="_blank">String s = “hello”;

 
变量s存放的是一个reference,这个reference指向一个存有String实例的内存空间。
c)对于基本型别数组,存放的是reference数组,数组中的每一个reference都指向一个class实例的内存空间。如
  1. int[] ia = {10, 11, 12};

 
数组ia存放的是一个reference数组,数组中的每一个reference都指向一个的int实例的内存空间。
d)对于class数组,存放的是reference数组,数组中的每一个reference都指向一个的class实例的内存空间。如
  1. java/lang/String.java.html" target="_blank">String[] sa = {“hello1”, “hello2”, “hello3”}; 
 
 
数组sa存放的是一个reference数组,数组中的每一个reference都指向一个的String实例的内存空间。
3)    任何数组都要进行初始化,使用没有进行初始化的数组会产生运行时错误,如: 
  1. int[] iArr;
  2. java/lang/System.java.html" target="_blank">System.out.pritnln(iArr[0]);//产生错误,因为iArr还未初始化

数组初始化可在任何地方,可用以下方法来对数组进行初始化:
a)    int[] iArr = {1,1,1,1};//数组的长度为{}元素的个数
b)    int i = 10; 
  1. int[] iArr = new int[i];//数组的长度可为变量(这在C/C++中不行)
  2. java/lang/System.java.html" target="_blank">System.out.println(iArr[0]);//iArr[0]是一个int,自动初始化值为0
  3. java/lang/Integer.java.html" target="_blank">Integer[] iArr2 = new java/lang/Integer.java.html" target="_blank">Integer[i];
  4. java/lang/System.java.html" target="_blank">System.out.println(iArr2[0]);//iArr[0]是一个reference,自动初始为null


I)    对于基本型别数组,new产生的是用于存放数据的数组;否则,产生的只是存放reference的数组。
II)    new可用来初始化基本型别的数组,但不能产生non-array的  基本型别数据。
c)    int[] iArr = new int[]{1,1,1,1};
  1. java/lang/Integer.java.html" target="_blank">Integer[] iArr2 = new java/lang/Integer.java.html" target="_blank">Integer[]{new java/lang/Integer.java.html" target="_blank">Integer(1), new java/lang/Integer.java.html" target="_blank">Integer(2)};

3.    多维数组(Multidimensional)arrays
多维数组每一维的大小可以不一样,如:
  1. java/lang/Integer.java.html" target="_blank">Integer[][][] a5;
  2. a5 = new java/lang/Integer.java.html" target="_blank">Integer[3];
  3. for(int i=0; i<a5.length; i++)
  4.    a5[i] = new java/lang/Integer.java.html" target="_blank">Integer[i+1];
  5.    for(int j=0; j<a5[i].length
  6.        a5[i][j] = new java/lang/Integer.java.html" target="_blank">Integer[i+j+1];

第5章    隐藏实现细节


一.Java访问权限饰词(access specifiers)
Java有public、protect、friendly、private四种访问权限,并且这四访问权限的访问范围越来越小。
1.    friendly
1)    果一个class内的数据成员或方法没有任何权限饰词,那么它的缺省访问权限就是friendly。同一个package内的其它所有classes都可以访问friendly成员,但对package以外的classes则形同private。
2)  对于同一个文件夹下的、没有用package的classes,Java会自动将这些classes初见为隶属于该目录的default package,可以相互调用class中的friendly成员。如以下两个class分别在同一个文件夹的两个文件中,虽然没有引入package,但隶属于相同的default package。
  1. class Sundae{
  2.         //以下两个方法缺省为friendly
  3.             Sundae(){}
  4.         java/lang/Void.java.html" target="_blank">Void f() {java/lang/System.java.html" target="_blank">System.out.println(“Sundae.f()”);
  5. }
  6. public class IceCream{
  7.             public static void main(java/lang/String.java.html" target="_blank">String[] args){
  8.             Sundae x = new Sundae();   
  9.             x.f();
  10.             }
  11. }

2.    public:可以被任何class调用
3.    private:private成员只能在成员所属的class内被调用,如:
  1. class Sundae{
  2.     private Sundae(){}//只能在Sundae class中被调用
  3.     Sundae(int i) {}
  4.     static Sundae makASundae()    {
  5.         return new Sundae();
  6.     }
  7. }
  8. public class IceCream{
  9.     public static void main(java/lang/String.java.html" target="_blank">String[] args){
  10.         // Sundae class中构造函数Sundae()是private,
  11.         // 所以不能用它进行初始化
  12.         //Sundae x = new Sundae();
  13.         Sundae y = new Sundae(1);//Sundae(int)是friendly,可以在此调用
  14.         Sundae z = Sundae.makASundae();
  15.     }
  16. }

4.    protected:具有friendly访问权限的同时,又能被subclass(当然包括子孙类,即子类的子类)所访问。即,既能被同一package中的classes访问,又能被protected成员所在class的subclass访问。

二.Class的访问权限
    1.Class同样具有public、protect、friendly、private四种访问访问权限:
        1)public:在任何地方都可被使用
2)protect、private:除了它自己,没有任何class可以使用,所以class不能是
protected或private(inner class除外)
3)    friendly:同一个package中的classes能用
2.    如何调用构造函数被声明为private的class
1)    用static函数
2)    用Singteton模式
  1. class Soup{
  2.     private Soup(){}
  3.      //(1)静态函数方法
  4.         public static Soup makeSout(){
  5.             return new Soup();
  6.         }
  7.      //(2)The "Singleton" pattern:
  8.      private static Soup ps1 = new Soup();
  9.      public static Soup access(){
  10.          return ps1;
  11.      }
  12.      public void f(java/lang/String.java.html" target="_blank">String msg){
  13.          java/lang/System.java.html" target="_blank">System.out.println("f(" + msg + ")");
  14.      }
  15.  }
  16.  public class Lunch{
  17.      public static void main(java/lang/String.java.html" target="_blank">String[] args){
  18.          //Soup priv1 = new Soup();编译错误
  19.          Soup priv2 = Soup.makeSout();
  20.          Soup priv3 = Soup.access();
  21.          priv2.f("priv2");
  22.          priv3.f("priv3");
  23.      }


第6章    重复运用classes


一.继承(inheritance)
1.    在derived class中overriding某个函数时,只能覆写base class中的接口,即base class中的public或protected或friendly函数。如果试图overriding一个private函数,虽然编译通过,但实际上你只是在derived class中添加了一个函数。如
  1. class Cleanser{    
  2.      private void prt(){//(b)
  3.         java/lang/System.java.html" target="_blank">System.out.println("Cleanser.prt()");
  4.     }
  5.  }
  6. public class ExplicitStatic extends Cleanser{
  7.     public void prt(){
  8.         java/lang/System.java.html" target="_blank">System.out.println("ExplicitStatic.prt()");
  9.     }
  10.     public static void main(java/lang/String.java.html" target="_blank">String[] args){
  11.         Cleanser x = new ExplicitStatic();
  12.         x.prt();//(a)
  13.     }
  14. }

因为Cleanser中的prt()是private,所以不能在其derived class中被覆写。ExplicitStatic中的prt()只是ExplicitStatic中的一个函数,所以当试图在(a)处通过多态来调用prt()时,会发生错误。如果把(b)处的private去掉,则结果为
    ExplicitStatic.prt()
2.    Super的使用
1)通过关键字super可以调用当前class的superclass(父类)。
例6.1.1.1
  1. class Base{
  2.     Base(){java/lang/System.java.html" target="_blank">System.out.println("Base()");}
  3.     public void scrub() { java/lang/System.java.html" target="_blank">System.out.println(" Base.scrub()"); }
  4. }
  5. class Cleanser extends Base{
  6.     private java/lang/String.java.html" target="_blank">String s = new java/lang/String.java.html" target="_blank">String("Cleanser");
  7.     public void append(java/lang/String.java.html" target="_blank">String a) { s+=a; }
  8.     public void dilute() { append(" dilute()"); }
  9.     public void apply() { append(" apply()"); }
  10.     public void scrub() { append(" scrub()"); }
  11.     public void print() { java/lang/System.java.html" target="_blank">System.out.println(s); }
  12.     Cleanser(){
  13.         java/lang/System.java.html" target="_blank">System.out.println("Cleanser(): " + s);
  14.     }
  15.     public static void testStatic(){
  16.         java/lang/System.java.html" target="_blank">System.out.println("testStatic()");
  17.     }
  18.     public static void main(java/lang/String.java.html" target="_blank">String[] args){
  19.         Cleanser x = new Cleanser();
  20.         x.dilute(); x.apply(); x.scrub(); x.print();
  21.     }
  22.  }
  23. public class ExplicitStatic extends Cleanser{
  24.     ExplicitStatic(){
  25.         java/lang/System.java.html" target="_blank">System.out.println("ExplicitStatic()");
  26.     }
  27.     public void scrub(){
  28.         append(" Detergen.scrub()"); 
  29.         super.testStatic();
  30.         super.scrub();//调用的是Cleanser.scrub()
  31.     }
  32.     public void foam() { append(" foam()"); }
  33.     public static void main(java/lang/String.java.html" target="_blank">String[] args){
  34.         ExplicitStatic x = new ExplicitStatic();
  35.         x.dilute(); x.apply(); x.scrub(); x.foam();
  36.         x.print(); java/lang/System.java.html" target="_blank">System.out.println("Test base class:");
  37.         Cleanser.main(args);   
  38.         testStatic();
  39.     }
  40. }

运行结果:
Base()
Cleanser(): Cleanser
ExplicitStatic()
testStatic()
Cleanser dilute() apply() Detergen.scrub() scrub() foam()
Test base class:
Base()
Cleanser(): Cleanser
Cleanser dilute() apply() scrub()
testStatic()
2)通过super来调用superclass中的成员时,调用的是最近成员。
例6.1.1.2
  1. class Base{
  2.     protected java/lang/String.java.html" target="_blank">String baseS = "Base";//(a)
  3.     //private String baseS = "Base";
  4.     Base(){java/lang/System.java.html" target="_blank">System.out.println("Base()");}
  5. }
  6. class Cleanser extends Base{
  7.     protected java/lang/String.java.html" target="_blank">String baseS = "Cleanser";//(b)
  8.     public java/lang/String.java.html" target="_blank">String s = new java/lang/String.java.html" target="_blank">String("Cleanser");
  9.     Cleanser(){
  10.         java/lang/System.java.html" target="_blank">System.out.println("Cleanser(): " + s);
  11.     }
  12.     Cleanser(java/lang/String.java.html" target="_blank">String a){
  13.         java/lang/System.java.html" target="_blank">System.out.println("Cleanser(" + a + "): s = " + s );
  14.     }
  15.  }
  16. public class ExplicitStatic extends Cleanser{
  17.     java/lang/String.java.html" target="_blank">String s2 = s;
  18.     java/lang/String.java.html" target="_blank">String baseS = super.baseS; //(c)
  19.     ExplicitStatic(){
  20.         super("ExplicitStatic");
  21.         java/lang/System.java.html" target="_blank">System.out.println("ExplicitStatic():s2 = " + s2 + ", baseS = " 
  22.           + baseS + "super.baseS = " + super.baseS); 
  23.         baseS = "ExplicitStatic";
  24.         java/lang/System.java.html" target="_blank">System.out.println("baseS = " + baseS + " , super.baseS = " + super.baseS);
  25.     }
  26.     public static void main(java/lang/String.java.html" target="_blank">String[] args){
  27.         ExplicitStatic x = new ExplicitStatic();
  28.     }
  29. }

结果1:
Base()
Cleanser(ExplicitStatic): s = Cleanser
ExplicitStatic():s2 = Cleanser, baseS = Cleanser,super.baseS = Cleanser
baseS = ExplicitStatic , super.baseS = Cleanser
在上面例子中,在三个class中都存在String bases实例。在ExplicitStatic中如果直接调用baseS,则实际调用的是当前类ExplicitStatic中的baseS(即(c)处的成员);如果通过super.bases来调用baseS,则调用的是离当前类ExplicitStatic最近的baseS成员,即Cleanser class中的baseS实例(即(b)处),产生的结果如结果1所示。如果把(b)处语句注释掉,则将调用Base class中的baseS,结果如结果2所示。
结果2:
Base()
Cleanser(ExplicitStatic): s = Cleanser
ExplicitStatic():s2 = Cleanser, baseS = Base,super.baseS = Base
baseS = ExplicitStatic , super.baseS = Base
3.    Base class的初始化
2.1    当你产生derived class对象时,其中会包含base class子对象(subobject)。这个子对象就和你另外产生的base class对象一模一样。
2.2    通过super()可调用base class的构造函数,但必须放在构造函数的第一行,并且只能在构造函数中运用。
2.3    初始化顺序为:
1)    加载代码(.class文件
2)    初始化class的静态成员,初始化顺序了“从里到外”,即从base class开始。
3)    在derived class的构造函数中调用base class的构造函数。
如果在derived class的构造函数中没有通过super()显式调用调用base class的构造函数,编译器会调用bass class的default构造函数并自动生成相应的调用语句,从而产生一个base class实例。如果在derived class的构造函数中通过super()显示调用了父类的构造函数,则调用所指定的构造函数。调用构造函数的调用顺序是“从里到外”。
4)    调用derived class的构造函数。
**:当base class没有default构造函数时,必须在derived class的构造函数中通过super显示调用base class的构造函数。
例:下面代码的初始化过程为:
1)    装载ExplicitStatic的代码(装载ExplicitStatic.class文件)。
2)    发现ExplicitStatic有关键字extends,装载ExplicitStatic的base class的代码(装载Cleanser.class文件)。
3)    发现Cleanser有关键字extends,装载Cleanser的base class的代码(装载Base.class文件)。
4)    初始化Base class中的静态成员。
5)    初始化Cleanser class中的静态成员。
6)    初始化ExplicitStatic class中的静态成员。
如果把(c)处的代码注释掉,那么初始化工作到此就结束了。
7)    为ExplicitStatic对象分配存储空间,并把存储空间初始化为0。
8)    在ExplicitStatic class的构造中调用super("ExplicitStatic")(在ExplicitStatic class的构造函数中显式调用父类的构造函数),试图产生一个Cleanser class实例。
9)    为Cleanser对象分配存储空间,并把存储空间初始化为0。
10)    由于Cleanser class又是继承自Base class,会在Cleanser class的构造函数中通过super()(由于没有显式调用父类的构造函数,所以自动调用父类的default构造函数)调用父类的构造函数,试图产生一个Cleanser class实例。
11)    产生一个Base class实例。先初始化成员变量,再调用构造函数。
12)    回到Cleanser class,产生一个实例。首先初始化Cleanser class中的成员数据,再执行构造函数Cleanser(String a)中的其余部分。
13)    回到ExplicitStatic class,产生一个实例。首先初始化ExplicitStatic class中的成员数据,再执行构造函数ExplicitStatic ()中的其余部分(System.out.println(“ExplicitStatic()”))。
  1. class Base{
  2.     static int s1 = prt("s1 initialized.", 11);
  3.     int i1 = prt("i1 initialized.", 12);
  4.     Base(){
  5.         java/lang/System.java.html" target="_blank">System.out.println("Base()");
  6.         java/lang/System.java.html" target="_blank">System.out.println("s1 = " + s1 + " ,i1 = " + i1);
  7.         draw();//(d)
  8.     }
  9.     void draw(){
  10.         java/lang/System.java.html" target="_blank">System.out.println("base.draw:s1 = " + s1 + " ,i1 = " + i1);
  11.     }
  12.     static int prt(java/lang/String.java.html" target="_blank">String s, int num) {
  13.         java/lang/System.java.html" target="_blank">System.out.println(s);
  14.         return num;
  15.     }
  16. }
  17. class Cleanser extends Base{
  18.     static int s2 = prt("s2 initialized.", 21);
  19.     int i2 = prt("i2 initialized.", 22);
  20.     Cleanser(){
  21.         java/lang/System.java.html" target="_blank">System.out.println("Cleanser()");
  22.         java/lang/System.java.html" target="_blank">System.out.println("s2 = " + s2 + " ,i2 = " + i2);
  23.     }
  24.     Cleanser(java/lang/String.java.html" target="_blank">String a){
  25.         //super();(b)
  26.         java/lang/System.java.html" target="_blank">System.out.println("Cleanser(" + a + ")");
  27.         java/lang/System.java.html" target="_blank">System.out.println("s2 = " + s2 + " ,i2 = " + i2);
  28.     }
  29.     
  30.     void draw(){
  31.         java/lang/System.java.html" target="_blank">System.out.println("Cleanser.draw:s2 = " + s2 + " ,i2 = " + i2);
  32.     }
  33.  }
  34. public class ExplicitStatic extends Cleanser{
  35.     static int s3 = prt("s3 initialized.", 31);
  36.     int i3 = prt("i3 initialized", 31);
  37.     ExplicitStatic(){
  38.         super("ExplicitStatic");//(a)
  39.         java/lang/System.java.html" target="_blank">System.out.println("ExplicitStatic()");
  40.         java/lang/System.java.html" target="_blank">System.out.println("s3 = " + s3 + " ,i3 = " + i3);
  41.     }
  42.     public static void main(java/lang/String.java.html" target="_blank">String[] args){
  43.         ExplicitStatic x = new ExplicitStatic();//(c)
  44.     }
  45. }

结果:
s1 initialized.
s2 initialized.
s3 initialized. 
//如果把(c)处的代码注释掉,输出结果到此为止,不会输出下面结果
i1 initialized. 
Base()
s1 = 11 ,i1 = 12
Cleanser.draw:s2 = 21 ,i2 = 0//(d)处结果
i2 initialized.
Cleanser(ExplicitStatic)//(a)处结果
s2 = 21 ,i2 = 22
i3 initialized
ExplicitStatic()
s3 = 31 ,i3 = 31
由于在Base()中调用draw()时,Cleanser中的i2还未进行初始化,而在为Cleanser对象分配存储空间时,把存储空间初始化为0,所以此时i2为0。
2.4    代码及结果中的(a)说明了是先产生当前class的base class的实例,否则在derived class中无法调用base class的成员。在调用Cleanser class的构造函数Cleanser(String a)时,在Cleanser(String a)中没有用super显式调用Base class的构造函数,所以系统会自动生成调用Base class的default构造函数的语句,如(b)。
4.    组合与继承之间的快择
.    1)继承表示的是一种“is-a(是一个)”的关系,如货车是汽车中的一种;组合表示的是一种“has-a(有一个)”的关系,如汽车有四个轮子。
        2)是否需要将新的class向上转型为base class。
5.    在继承中的访问权限
protect变量能被子孙类所调用。如Base class中的baseS能被Cleanser class和ExplicitStatic class调用。
  1. class Base{
  2.     protected java/lang/String.java.html" target="_blank">String baseS = "Base";
  3.     //private String baseS = "Base";
  4.     Base(){java/lang/System.java.html" target="_blank">System.out.println("Base()");}
  5. }
  6. class Cleanser extends Base{
  7.     protected java/lang/String.java.html" target="_blank">String baseS = "Cleanser";
  8.     public java/lang/String.java.html" target="_blank">String s = new java/lang/String.java.html" target="_blank">String("Cleanser");
  9.     Cleanser(){
  10.         java/lang/System.java.html" target="_blank">System.out.println("Cleanser(): " + s);
  11.     }
  12.     Cleanser(java/lang/String.java.html" target="_blank">String a){
  13.         java/lang/System.java.html" target="_blank">System.out.println("Cleanser(" + a + "): s = " + s );
  14.     }
  15.  }
  16. public class ExplicitStatic extends Cleanser{
  17.     java/lang/String.java.html" target="_blank">String s2 = s;
  18.     java/lang/String.java.html" target="_blank">String baseS = super.baseS;
  19.     ExplicitStatic(){
  20.         super("ExplicitStatic");
  21.         java/lang/System.java.html" target="_blank">System.out.println("ExplicitStatic():s2 = " + s2 + ", baseS = " 
  22.           + baseS + "super.baseS = " + super.baseS);
  23.         baseS = "ExplicitStatic";
  24.         java/lang/System.java.html" target="_blank">System.out.println("baseS = " + baseS + " , super.baseS = " + super.baseS);
  25.     }
  26.     public static void main(java/lang/String.java.html" target="_blank">String[] args){
  27.         ExplicitStatic x = new ExplicitStatic();
  28.     }
  29. }

结果:
Base()
Cleanser(ExplicitStatic): s = Cleanser
ExplicitStatic():s2 = Cleanser, baseS = Cleanser, super.baseS = Cleanser
baseS = ExplicitStatic , super.baseS = Cleanser
二.关键字final
1.Final data
    1.1 final data
1)当基本型别被定义为final,表示它的数据值不能被改变。如
            final int i = 9;
            i++;//编译错误,不能改变I的值
2) 当object reference被定义为final时,不能改变的只是reference而不是对象本身。如
  1. class Value{  
  2.     int i = 1;
  3. }
  4. public class ExplicitStatic extends Cleanser{
  5.     public static void main(java/lang/String.java.html" target="_blank">String[] args){
  6.             final Value v = new Value();//v.i = 1
  7.             v.i++;//v.i = 2
  8.             //v = new Value();
  9.     }
  10. }

由于v为final,所以不能通过new Value()使v重新指向一个对象;但是v所指向的对象的值是可以改变的(v.i++)。
1.2 blank finals
我们可以将数据成员声明为final但不给予初值,这就是blank finals。但blank finals必须且只能在构造函数中进行初始化。
  1. public class ExplicitStatic {
  2.     final int ib;
  3.     final int i = 1;
  4.     ExplicitStatic()
  5.     {
  6.         ib = 2;//(a)
  7.         //i = 3; (b)
  8.         java/lang/System.java.html" target="_blank">System.out.println("i = " + i  + ", ib = " + ib);
  9.     }
  10.     public static void main(java/lang/String.java.html" target="_blank">String[] args){
  11.         ExplicitStatic ex = new ExplicitStatic();
  12.     }
  13. }

ib为blank finals,所以可以在构造函数中进行初始化。如果把(a)处的代码注释掉,则ib没有初值,编译出错。而i在定义处已进行了初始化,则不能改变i的值,(b)处的代码编译错误。
**:非blank finals成员即使在构造函数中也不能更改其值
2.Final methods
    1)被声明为final的函数不能被覆写
    2)class中所有private函数自然而然会是final。
3.    Final classes
1)当一个class被声明为final时,表示它不能被继承,但class的数据成员不是final,可以被改变。如
  1. class SmallBrain{}
  2. final class Dinosaur{
  3.             int i = 7;
  4.             int j = i;
  5.             SmallBrain x = new SmallBrain();
  6.             void f(){};
  7. }
  8. //不能继承final函数
  9. //class Further extends Dinosaur{}
  10. public class ExplicitStatic{
  11.             public static void main(java/lang/String.java.html" target="_blank">String[] args){
  12.                 Dinosaur n = new Dinosaur();
  13.                 n.f();
  14.                 n.i = 40;//final class中的non-final数据成员可以被改变
  15.                 n.j++;
  16.             }
  17. }

2)final class中的所有函数也都自然是final,因为没有人能够加以覆写。

第7章    多态


一.再探向上转型(upcasting)
    将某个object reference视为一个“reference to base type“的动作,称为向上转型。
1.    Upcasting后调用某个函数时,如果derived class中覆写了该函数,则会调用derived class中的函数;否则,会调用base class中的函数。如
  1. class First{
  2.     public void prt(){
  3.         java/lang/System.java.html" target="_blank">System.out.println("First");
  4.     }
  5. }
  6. class Second extends First{
  7.     //(a)
  8.      public void prt(){
  9.         java/lang/System.java.html" target="_blank">System.out.println("Second");
  10.     }
  11. }
  12. public class ExplicitStatic{
  13.     public static void main(java/lang/String.java.html" target="_blank">String[] args){
  14.         First n = new Second();
  15.         n.prt();;
  16.     }
  17. }

结果为Second。如果当Second class中的prt()函数注释掉,将输出First。
2.    向上转型后只能调用base class中被derived class覆写的函数。
  1. /*
  2. abstract class First{
  3.     int i = 122;
  4.     public void prt(){
  5.         System.out.println("First.i = " + i);
  6.     }
  7.     public abstract void prt(First f);
  8. }
  9. class Second extends First{
  10.      public void prt(){
  11.         System.out.println("Second.i = " + i);
  12.     }
  13.     
  14.     public void prt(First i)
  15.     {
  16.         
  17.     }
  18.     
  19.     public void prt(int i)
  20.     {
  21.         
  22.     }
  23. }
  24. public class ExplicitStatic{
  25.     public static void main(String[] args){
  26.         First n = new Second();
  27.         n.prt(2);;
  28.     }
  29. }
  30. */
  31. class First{
  32.     public void prt(){
  33.         java/lang/System.java.html" target="_blank">System.out.println("First");
  34.     }
  35. }
  36. class Second extends First{
  37.     //(a)
  38.      public void prt(){
  39.         java/lang/System.java.html" target="_blank">System.out.println("Second");
  40.     }
  41.     public void prt(int i){//(a)
  42.         java/lang/System.java.html" target="_blank">System.out.println("Second.i = " + i);
  43.     }
  44. }
  45. public class ExplicitStatic{
  46.     public static void main(java/lang/String.java.html" target="_blank">String[] args){
  47.         First n = new Second();
  48.         n.prt(3);
  49.     }
  50. }

(a)处的函数只是Second class中的函数,所以不能通过n.prt(3)进行调用。
二.Abstract class和Abstract methods
1.    如果一个class中存在abstract class,则class也必须被声明为abstract class。
2.    abstract class不能被实例化。
3.    如果base class是一个abstract class,那么derived class必须实现base class中所有的abstract methods;否则,derived class也必须被声明为abstract class。
三.其它要点
1.    纯粹继承与扩充
纯粹继承:只有base class所建议的函数,才被derived class加以覆写。
扩充:除了覆写base class的函数,还实现了自己的函数
  1. abstract class First{
  2.     public abstract void f();
  3.     public abstract void g();
  4. }
  5. //纯粹继承
  6. class Second extends First{
  7.      public void f(){}
  8.      public void g(){}
  9. }
  10. //扩充
  11. class Third extends First{
  12.      public void f(){}
  13.      public void g(){}
  14.      public void u(){}//base class不存在的函数
  15. }

2.    向下转型
1)    向下转型时只能调用base class中被覆写过的函数
2)    只有本来就为derived class对象时才能正确向下转弄。
  1. class First{
  2.      public void f(){}
  3.      public void g(){}
  4. }
  5. class Second extends First{
  6.      public void f(){}
  7.      public void g(){}
  8.      public void u(){}
  9.      public void v(){}
  10. }
  11. public class ExplicitStatic{
  12.     public static void main(java/lang/String.java.html" target="_blank">String[] args){
  13.         First[] x = {new First(), new Second()};
  14.         x[0].f();
  15.         x[1].g();
  16.         //!x[1].u();class First中不存在函数u()
  17.         //((Second)x[0]).f();(a)
  18.         ((Second)x[1]).u();
  19. }
  20. }


-------------------------------------






    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多