tij学习笔记-1

来源:互联网 发布:java spring面试题2017 编辑:程序博客网 时间:2024/05/16 23:36

第2章    万事万物皆对象


一.所有对象都必须由你建立
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. Integer n1 = new Integer(3);
  2. Integer n2 = new Integer(3);
  3. System.out.println(n1==n2);

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

2.    逻辑运算符
1)    只能将and、or、not施用于boolean值身上。如果逻辑运算符两边的值存在non-boolean值,将会出错,如:
  1. int test1 = 1;
  2. 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(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(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.     String name = “”;
  3.     Movie(){}
  4.     public Movie(String name) { this.name = name; }
  5.     public static String getName() { return name; }
  6. }
  7. public class Test{
  8.     public static void main(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.         System.out.println("Cup(" + marker + ")");
  4.     }
  5.     void f(int marker){
  6.         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.         System.out.println("Cups()");
  24.     }
  25. }

  26. public class ExplicitStatic{
  27.     public static void main(String[] args){
  28.         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. 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. String[] sa = {“hello1”, “hello2”, “hello3”}; 
 
 
数组sa存放的是一个reference数组,数组中的每一个reference都指向一个的String实例的内存空间。
3)    任何数组都要进行初始化,使用没有进行初始化的数组会产生运行时错误,如: 
  1. int[] iArr;
  2. 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. System.out.println(iArr[0]);//iArr[0]是一个int,自动初始化值为0
  3. Integer[] iArr2 = new Integer[i];
  4. 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. Integer[] iArr2 = new Integer[]{new Integer(1), new Integer(2)};

3.    多维数组(Multidimensional)arrays
多维数组每一维的大小可以不一样,如:
  1. Integer[][][] a5;
  2. a5 = new Integer[3];
  3. for(int i=0; i<a5.length; i++)
  4.    a5[i] = new Integer[i+1];
  5.    for(int j=0; j<a5[i].length
  6.        a5[i][j] = new 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.         Void f() {System.out.println(“Sundae.f()”);
  5. }
  6. public class IceCream{
  7.             public static void main(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(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(String msg){
  13.          System.out.println("f(" + msg + ")");
  14.      }
  15.  }
  16.  public class Lunch{
  17.      public static void main(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.         System.out.println("Cleanser.prt()");
  4.     }
  5.  }
  6. public class ExplicitStatic extends Cleanser{
  7.     public void prt(){
  8.         System.out.println("ExplicitStatic.prt()");
  9.     }
  10.     public static void main(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(){System.out.println("Base()");}
  3.     public void scrub() { System.out.println(" Base.scrub()"); }
  4. }
  5. class Cleanser extends Base{
  6.     private String s = new String("Cleanser");
  7.     public void append(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() { System.out.println(s); }
  12.     Cleanser(){
  13.         System.out.println("Cleanser(): " + s);
  14.     }
  15.     public static void testStatic(){
  16.         System.out.println("testStatic()");
  17.     }
  18.     public static void main(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.         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(String[] args){
  34.         ExplicitStatic x = new ExplicitStatic();
  35.         x.dilute(); x.apply(); x.scrub(); x.foam();
  36.         x.print(); 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 String baseS = "Base";//(a)
  3.     //private String baseS = "Base";
  4.     Base(){System.out.println("Base()");}
  5. }
  6. class Cleanser extends Base{
  7.     protected String baseS = "Cleanser";//(b)
  8.     public String s = new String("Cleanser");
  9.     Cleanser(){
  10.         System.out.println("Cleanser(): " + s);
  11.     }
  12.     Cleanser(String a){
  13.         System.out.println("Cleanser(" + a + "): s = " + s );
  14.     }
  15.  }
  16. public class ExplicitStatic extends Cleanser{
  17.     String s2 = s;
  18.     String baseS = super.baseS; //(c)
  19.     ExplicitStatic(){
  20.         super("ExplicitStatic");
  21.         System.out.println("ExplicitStatic():s2 = " + s2 + ", baseS = " 
  22.           + baseS + "super.baseS = " + super.baseS); 
  23.         baseS = "ExplicitStatic";
  24.         System.out.println("baseS = " + baseS + " , super.baseS = " + super.baseS);
  25.     }
  26.     public static void main(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.         System.out.println("Base()");
  6.         System.out.println("s1 = " + s1 + " ,i1 = " + i1);
  7.         draw();//(d)
  8.     }
  9.     void draw(){
  10.         System.out.println("base.draw:s1 = " + s1 + " ,i1 = " + i1);
  11.     }
  12.     static int prt(String s, int num) {
  13.         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.         System.out.println("Cleanser()");
  22.         System.out.println("s2 = " + s2 + " ,i2 = " + i2);
  23.     }
  24.     Cleanser(String a){
  25.         //super();(b)
  26.         System.out.println("Cleanser(" + a + ")");
  27.         System.out.println("s2 = " + s2 + " ,i2 = " + i2);
  28.     }
  29.     
  30.     void draw(){
  31.         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.         System.out.println("ExplicitStatic()");
  40.         System.out.println("s3 = " + s3 + " ,i3 = " + i3);
  41.     }
  42.     public static void main(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 String baseS = "Base";
  3.     //private String baseS = "Base";
  4.     Base(){System.out.println("Base()");}
  5. }
  6. class Cleanser extends Base{
  7.     protected String baseS = "Cleanser";
  8.     public String s = new String("Cleanser");
  9.     Cleanser(){
  10.         System.out.println("Cleanser(): " + s);
  11.     }
  12.     Cleanser(String a){
  13.         System.out.println("Cleanser(" + a + "): s = " + s );
  14.     }
  15.  }
  16. public class ExplicitStatic extends Cleanser{
  17.     String s2 = s;
  18.     String baseS = super.baseS;
  19.     ExplicitStatic(){
  20.         super("ExplicitStatic");
  21.         System.out.println("ExplicitStatic():s2 = " + s2 + ", baseS = " 
  22.           + baseS + "super.baseS = " + super.baseS);
  23.         baseS = "ExplicitStatic";
  24.         System.out.println("baseS = " + baseS + " , super.baseS = " + super.baseS);
  25.     }
  26.     public static void main(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(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.         System.out.println("i = " + i  + ", ib = " + ib);
  9.     }
  10.     public static void main(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(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.         System.out.println("First");
  4.     }
  5. }
  6. class Second extends First{
  7.     //(a)
  8.      public void prt(){
  9.         System.out.println("Second");
  10.     }
  11. }
  12. public class ExplicitStatic{
  13.     public static void main(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.         System.out.println("First");
  34.     }
  35. }
  36. class Second extends First{
  37.     //(a)
  38.      public void prt(){
  39.         System.out.println("Second");
  40.     }
  41.     public void prt(int i){//(a)
  42.         System.out.println("Second.i = " + i);
  43.     }
  44. }
  45. public class ExplicitStatic{
  46.     public static void main(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(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. }
原创粉丝点击