Thingking in Java学习笔记

来源:互联网 发布:数据库工程师考试试题 编辑:程序博客网 时间:2024/05/03 20:20

Thinking Java学习笔记

参考资料:

1.      书中所有例子程序:http://www.assembla.com/code/PedroMVU/subversion/nodes/Ejercicios/Java?rev=33

2.      Java API http://download.oracle.com/javase/6/docs/api/overview-summary.html

 

第二章:一切都是对象

存储到什么地方:

1.      寄存器:最快的存储区,位于处理器内部,但是寄存器的数量极其有限,不能直接控制,也不能再程序中感觉到寄存器存在的任何迹象。

2.      堆栈:速度仅此次于与寄存器,位于通用RAM(随机访问寄存器)中,通过堆指针可以从处理器哪里得到支持。堆指针若向下移动,则分配新的内存,若向上移动,则释放内存。但java的对象并不存储在其中。

3.      堆:一种通用的内存池,也位于RAM去,用于存放所有的JAVA对象。编译器不需要知道存储的数据在堆里存活多长时间。用堆进行存储分配和清理比用堆栈进行存储分配需要更多的时间。

4.      常量存储:直接存放在程序代码内部。

5.      RAM存储:流对象和持久化对象。

Java的基本类型,因为特别小,如果在堆中创建,不是很有效,因此创建一个并非引用的“自动”变量,这个变量用来存储“值“,并放在堆栈中,会更加高效。因此,Java的基本类型

所占得存储空间的大小事不变的,也是使Java具有可移植性的原因之一。因此,java中没有sizeof这个操作符。

 

作用域:

  隐藏:

    { int x = 12;

     {

        Int x =96;

      }

    }//C中,将一个较大作用域的变量“隐藏”起来的做法,在Java中是不允许的。

  对象的作用域:

  {

    String s = new String(“a String”);

  }//引用s在作用域终点就消失了,然后s所指向的String对象仍然继续占据内存空间。Java有一个垃圾回收器,用来监视用new创建的所有对象,并辨别那些不会被在引用的对象。

 

当变量作为类的成员使用时,Java才会确保给定其默认值,以确成员变量得到初始化,防止产生程序错误,然后对于局部变量却不适用。

 

整个包名都是小写的。

 

类成员和类方法:使用static关键字定义的方法和成员,是类方法和类成员,这些方法和数据只是作为整个类,而不是类的某个特殊对象而存在的。

 

JavaDoc 是用于提取注释的工具,他用来查找程序内的特殊注释标签。他不仅解析由这些标签标记的信息,也将毗邻注释的类名或方法名抽取出来。所有的Javadoc命令都只能在/**注释中出现。Javadoc只能为publicprotected成员进行文档注释,private和包内可访问的成员的注释会被忽略掉。

 

第三章:操作符

赋值:对一个对象进行赋值,我们真正操作的是对对象的引用,所以倘若“将一对象赋值给另一个对象”,实际上是将“引用”从一个地方赋值到另一个地方。这种特殊的现象通常称作“别名现象”。

cass Letter

{

  char c;

}

 

Public class PassObject

{

  static void f(Letter y)

   {

     y.c = ‘z’;

   }

   Publicstatic void main(String[] args)

   {

      Letter x = new Letter();

      x.c = ‘a’;

      f(x);

      System.out.println(“x:c “+ x.c);

   }

}

 

整数除法会直接去掉结果的小数位,而不是四舍五入。如果响应好得到四舍五入的结果,就需要使用java.lang.Math中的round()方法。

==!=比较的是对象的引用。如果想比较两个对象的实际内容是否相同,必须使用所有对象都使用的特殊方法equals()。单这个方法不适用于“基本类型”。由于equals()默认的行为时比较引用,所以除非在自己的新类中覆盖equals()方法。

 

(&&)、或(||)、非(!)操作只能应用于布尔值,不能将一个非布尔值应用在逻辑表达式中。Int i = 0;

 

第四章:操作符

Java引入了foreach语法,用于数组和容器。

float[] f = newfloat[10];

for(float x :f)

{

   System.out.println(x);

}

 

尽管goto仍然是Java中的一个保留关键字,但是在语言中并未使用它,java没有goto。而是采用了break label; 或者 continue label的方式来实现goto的功能。在Java中,标签起作用的唯一地方刚好在迭代语句之前。

 

第五章:初始化与清理

可以在构造器内使用this语句调用另一个构造器,但是却不能调用两个构造器,并且必须将构造器置于最起始处,否则编译器会报错。

假定你的对象(并非使用new)获取了一块“特殊的”内存区域,由于垃圾回收期只知道释放那些经由new分配的内存,所以他不知道如何释放该对象的这块“特殊的”内存。为了应对这种情况,Java语句允许在类中定义一个名为finalize()的方法。他的工作原理假定是这样的:一旦垃圾回收器准备好释放对象占用的内存空间,将首先调用其finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象所占用的内存。

无论是“垃圾回收”还是“终结”,都不保证一定会发生。如果java虚拟机(JVM)并未面临内存耗尽的情形,他是不会浪费时间去执行垃圾回收以恢复内存的。System.gc()用于强制执行终结动作。

Java的“堆指针”只是简单地移动到尚未分配的区域。当垃圾回收工作的时候,将一面回收空间,一面使堆中的对象紧凑排列,这样“堆指针”就可以很容易移动到更靠近传送带的开始出,也就尽量避免了页面错误。

 

初始化的顺序是:

(1)    静态对象

(2)    非静态对象

(3)    构造器

具体来说:

(1)      即使没有显示地使用static关键字,构造器实际上也是静态方法。因此当首次创建类型为Dog的对象时(构造器可以看成静态方法),或者Dog类的静态方法或静态域首次被访问时,Java解释器必须先查找类路径,以定位Dog.class文件。

(2)      然后载入Dog.classs,有关静态初始化的所有动作都会执行。因此,静态初始化只在Class对象首次加载的时候进行一次。

(3)      当用new Dog()创建对象的时候,首先将在堆上为Dog对象分配足够的存储空间。

(4)      这块存储空间会自动被清零,这就自动地将Dog对象中的所有基本类型数据都设置成了默认值,而引用被设置成null

(5)      执行所有出现于字段定义处的初始化动作。

(6)      执行构造器。

静态初始化:

   将多个静态初始化动作组织成一个特殊的“静态子句”(有时也叫做“静态块”)。例如:

Public classSpoon

{

   Static int I;

   Static {

       I = 47;

   }

}

尽管这段代码看起来像个方法,但实际上只是一段跟在static关键字后面的代码,与其他静态初始化动作一样的,这段代码仅执行一次,当首次生成这个类的一个对象时,或者首次访问属于哪个类的静态数据成员时。

 

实例初始化:

  与静态子句类似,也有实例初始化,用来初始化每一个对象的非静态变量。

  Publicclass Mugs{

    Mug mug1;

    Mug mug2;

    {

        mug1 = new Mug(1);

        mug2 = new Mug(2);

    }

 

数组:

   Int[]a1;  int a[]; 两种格式的含义是一样的(一般采用第一种格式),编译器不允许指定数组的大小。现在a1拥有的只是数组的一个引用,但是对象本身没有分配任何空间。为了给数组创建相应的存储空间,必须写初始化表达式,对于数组,初始化动作可以出现在代码的任何定法。

(1)      只能在创建数据的地方出现的初始化方法:

Int[] a1 = { 1,3,4,5}

(2)      可以直接用new在数组里创建元素,尽管创建的是基本类型数组,new仍然可以工作(不能用new创建单个的基本类型数据)。

Int[] a;

a = new int[rand.nextInt(20)];

(3)      用花括号括起来的列表来初始化对象数组

Integer[] a = { new Integer(1), new Integer(2), 3,}; //只能用于数组被定义处

Integer[] b = new Integer[]{ new Integer(1), newInteger(2),3,}; //可以用于任何地方。

(4)      可变参数列表:

Void printArray(Object … args){ …….. }

printArray((Object[]) new Integer[]{2,3,});

printArray(34,56,78);

printArray();

printArray(new A(),new B());

有了可变参数,就再也不用显示地编写数组语法了,当指定参数时,编译器实际上会为你填充数据组。你获取的仍然是一个数组。

(5)      数组的成员函数length

 

枚举类型:

   publicenum Spciciness{

NOT, MILD,MEDIUM, HOT, FLAMING}

由于枚举类型的实例实际上是常量,因此按照命名惯例他们都是用大写字母表示,如果在一个名字中有多个单词,用下划线将他们分割开。当你创建enum时候,编译器会自动添加一些有用的特性。

SpicinesshowHot = Spiciness.MEDIUM;

尽管enum看起来像是一个新的数据类型,但是这个关键字只是为enum生成对应的类,产生了某种便器行为,因此在很大程度上,可以将enum当做其他任何类来处理。事实上enum确实是类,并且有自己的方法。例如:

(1)      toString()方法,enum自己创建了toString()方法,可以很方便地显示某个enum实例的名字。

(2)      ordinal()方法,用来表示某个特定enum常量的声明顺序。

(3)      static values()方法,用来按照enum声明的顺序产生有这些常量值构成的数组。

 

第六章:访问权限控制

从最大权限到最小权限依次为:publicprotected,包访问权限(没有关键字)和private

 包:

未命名的包,也称为默认包。当编写一个Java源文件时,此文件通常被称为编译单元(有时也称为转译单元)。每个编译单元都必须有一个后缀名.java而在编译单元内则可以有一个public类,该类的名称必须与文件的名称相同(包括大小写,但不包括文件的后缀名.java)。每个编译单元只能有一个public类,否则编译器就不能接受。如果在该便一单元之中还有额外的类的话,那么在包之外的世界是无法看到这些类的,这是因为他们不是public类,而他们主要是用来为public类提供服务支持的。

Java可运行程序是一组可以打包并压缩为一个java文档文件(JAR,使用javajar文档生成器).class文件。Java解释器负责这些文件的查找、装载和解释。

类库实际上是一组类文件,其中每个文件都有一个public类,以及任意数量的非public类。因此每个文件都有一个构件,如果希望这些构件(每一个都有他们自己的独立的.java.class文件)从属于一个群组,就可以使用关键字package。如果使用package语句,它必须是文件中除注释以外的第一个程序代码。

Java解释器的运行过程如下:首先,找出环境变量CLASSPATH中包含的一个或多个目录,用作查找.class文件的根目录。从根目录开始,解释器获取包的名称并将每个句点替换成反斜杠,以从CLASSPATH根中产生一个路径名称。得到的路径将会与CLASSPATH中的各个不同的项链接,解释器就在这些目录中查找与你所有创建的类名称相关的.class文件。但是解释器还会去查找某些涉及Java解释器所在位置的标准目录。但是在使用JAR文件的时候有一点变化。必须在CLASSPATH中将JAR文件的实际名称写清楚,而不仅指明它所在位置的目录。

使用import static语句导入static 方法。

 

成员访问权限

(1)      包访问权限,不提供任何访问权限修饰符,意味着当前的包中的其他类对这个成员都有访问权限,但是对于包之外的所有类,这个成员都是private,不可以访问。去得对某个类成员的访问权限的唯一途径是:1> 使该成员成为public 2> 通过不加访问权限修饰词并将其他类放置现在同一个包内的方式给成员赋予包访问权限;3> 通过继承技术,继承类可以访问public成员也可以访问protected成员。

(2)      public

(3)      private

(4)      protected

类访问权限

  类既不可以是private的(这样会使得除该类之外,其他任何类都不可以访问它),也不可以是protected的。所以对于类的访问权限,仅有两个选择:包访问权限或public,若谷哦不希望其他人和人对该类拥有访问权限,可以把所有的构造器指定为private

 

第七章 复用类

继承使用extends关键字来实现的。可以为每个类创建一个main()方法,这种在每个类中都设置一个main()方法的技术可以使每个类的单元测试都变得简单易行。而且在完成单元测试之后,也无需删除main(),可以将其留待下次测试。即使一个程序中含有多个类,也只有命令行所调用的那个类的main()方法会被调用。

  基类初始化:当创建了一个导出类的对象时,该对象包含了一个基类的子对象,这个子对象与你用基类直接创建的对象是一样的,二者的区别在于,后者来自于外部,而基类的子对象被包撞在导出类对象内部。对基类子对象的正确初始化也是至关重要的,而且也仅有一种方法来保证着一点,在构造器中调用基类构造器来执行初始化,而基类构造器聚友执行基类初始化所需要的所有知识和能力。Java会自动在导出类的构造器中插入对基类构造器的调用。构造过程是从基类“向外”扩散的,所以基类在导出类构造器可以访问它之前就已经完成了初始化。如果没有默认的基类构造器(不带参数),或者想要调用一个带参数的及类构造器,就必须使用关键字super来现实地调用基类构造器,并配以适当的参数列表。调用基类构造器必须是在导出类构造器中要做的第一件事。

  @Override注解,不是关键字,但是可以把它当成关键字使用。当想要覆写某个方法时,可以选择添加这个注解,在你不留心重载而非覆写了该方法时,编译器就会生成一跳错误信息:

   Class Lisa extends Homer{

        @Override void doh(Milhouse m){

           System.out.println(“doh(Milhouse”));

   }

}

 

Protected 关键字: 就类用户而言,这是private的,但是对于任何继承与此类的导出类或其他任何未育同一个包内的类来说,却是可以访问的。Protected提供了包内访问权限。

 

final关键字:不可改变的。有三种情况用到了final:

(1)      final数据:

常量:一个用不改变的编译时常量,在java中,这类常量必须是基本数据类型,并且以关键字final表示在对整个常量进行定义的时候,必须对其进行赋值。一个既是static又是final的域只占用一段不能改变的存储空间。

对象引用:对于对象引用,final是引用恒定不变,一旦引用被初始化指向一个对象,就无法再把它改为指向另一个对象,然而,对行啊其自身却是可以被修改的,java并未提供使任何对象恒定不变的途径。

空白final java允许生成“空白final”,所谓空白final是指被声明为final但又未给定初值的域,无论什么情况,编译器都确保空白final在使用前必须被初始化。

final参数:java允许在参数列表中以声明的方式将参数指定为final,这意味着你无法在方法中更改参数所指向的对象。

(2)      final 方法:

使用final方法的原因有两个:一个是把方法锁定,以防止任何类继承修改它的含义,不能被覆盖;第二个是效率,如果将一个方法指定为final,就是同意编译器将针对该方法的所有调用都将转为内嵌调用。

类中所有的private方法都隐式地指定为final的,由于无法取用private方法,所以也就无法覆盖它。

(3)      final类:

将某个类的整体定义为final时,就表明了不能继承该类。final类的域可以根据个人的意愿选择为是或者不是final,无论类是否被定义为final,都无法覆盖这些方法和成员变量。因此,final类中的所有的方法都隐式地指定为final的。

初始化的过程:

  每个类的编译代码都存在于它自己的独立的文件中。该文件只在需要使用程序代码时才会被加载。一般来说,可以说,类的代码在初次使用时才加载,这通常是指加载发生于创建类的第一个对象之时,但是当访问static域或static方法的时候,也会发生加载。

   对一个类进行加载的过程,编译器注意它是否有一个基类,如果有,就继续进行加载,不管你是否打算生成一个该基类的对象,这都要发生。如果该基类还有其自身的基类,那么第二个基类就会被加载,如此类推。接下来,根基类中的static初始化被齿形,然后是下一个导出类,以此类推。这种方式很重要,因为导出类的static初始化可能会依赖于基类的成员是否能被正确初始化。至此为止,必要的类都已加载完成,对象就可以被创建了。首先,对象中所有的基本类型都会被设为默认值,对象引用被设为null—这是通过将对象内存设置为二进制零值而一举生成的。然后,基类的构造器会被调用。

 

第八章:多态

绑定:将一个方法调用同一个方法主体关联起来被称为绑定。有两种绑定方式:

(1)      前期绑定:若在程序执行前进行绑定的话,叫做前期绑定。

(2)      后期绑定:在运行时根据对象的类型进行绑定。也叫做动态绑定或运行时绑定。要想实现后期绑定,编译器必须能够具有如下机制:编译器一直不知道对象的类型,但是方法待用机制能找到正确的方法体,并加以调用。Java中除了static方法和final方法之外,其他所有的方法都是后期绑定。

 

多态:可能会认为所有的事物都可以多态地发生。然后只有普通的方法可以是多态的。如果直接访问某个域,这个访问就将在编译期进行解析。因此,任何域访问操作都将有编译器解析,因此不是多态的。如果某个方法时静态的,他的行为就不具有多态性。

原创粉丝点击