第1章 对象入门 . Alan Kay总结了Smalltalk的五大基本特征。这是第一种成功的面向对象程序设计语言,也是Java的基础语言。通过这些特征,我们可理解“纯粹”的面向对象程序设计方法是什么样的: . (1) 所有东西都是对象。可将对象想象成一种新型变量;它保存着数据,但可要求它对自身进行操作。理论上讲,可从要解决的问题身上提出所有概念性的组件,然后在程序中将其表达为一个对象。 . (2) 程序是一大堆对象的组合;通过消息传递,各对象知道自己该做些什么。为了向对象发出请求,需向那个对象“发送一条消息”。更具体地讲,可将消息想象为一个调用请求,它调用的是从属于目标对象的一个子例程或函数。 成功的人生,需要自己去经营,别再说了,莫再等了,现在就为自己的人生做好规划,为人生点亮一盏明灯,赢在人生起跑点上。 (3) 每个对象都有自己的存储空间,可容纳其他对象。或者说,通过封装现有对象,可制作出新型对象。所以,尽管对象的概念非常简单,但在程序中却可达到任意高的复杂程度。 特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系 (4) 每个对象都有一种类型。根据语法,每个对象都是某个“类”的一个“实例”。其中,“类”(Class)是“类型”(Type)的同义词。一个类最重要的特征就是“能将什么消息发给它?”。 ... (5) 同一类所有对象都能接收相同的消息。这实际是别有含义的一种说法,大家不久便能理解。由于类型为“圆”(Circle)的一个对象也属于类型为“形状”(Shape)的一个对象,所以一个圆完全能接收形状消息。这意味着可让程序代码统一指挥 “形状”,令其自动控制所有符合“形状”描述的对象,其中自然包括“圆”。这一特性称为对象的“可替换性”,是OOP最重要的概念之一。 . 第2章 一切都是对象 2.1 用句柄操作对象 ! 创建一个String句柄: String s; 这里创建的只是句柄,并不是对象。若此时向s发送一条消息,就会获得一个错误(运行期)。这是由于s实际并未与任何东西连接(即“没有电视机”)。因此,一种更安全的做法是:创建一个句柄时,记住无论如何都进行初始化: String s = "a"; 根据专家观察,这样的理论和现象都是值得各位站长深思的,所以希望大家多做研究学习,争取总结出更多更好的经验! 创建句柄时,我们希望它同一个新对象连接。通常用new关键字达到这一目的。new的意思是:“把我变成这些对象的一种新类型”。所以在上面的例子中,可以说: String s = new String("a"); 本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系。 2.2 程序运行时,我们最好对数据保存到什么地方做到心中有数。特别要注意的是内存的分配。有六个地方都可以保存数据: (1)寄存器 本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系。 (2)堆栈:驻留于常规RAM(随机访问存储器)区域可通过它的“堆栈指针”获得处理的直接支持。这是一种特别快、特别有效的数据保存方式,仅次于寄存器。创建程序时, Java编译器必须准确地知道堆栈内保存的所有数据的“长度”以及“存在时间”。这是由于它必须生成相应的代码,以便向上和向下移动指针。这一限制无疑影响了程序的灵活性,所以尽管有些Java数据要保存在堆栈里——特别是对象句柄,但Java 对象并不放到其中。 .. (3)堆:一种常规用途的内存池(也在RAM区域)其中保存Java对象。和堆栈不同,“内存堆”或“堆”(Heap)最吸引人的地方在于编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间。因此,用堆保存数据时会得到更大的灵活性。要求创建一个对象时,只需用new命令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保存。当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间 ! (4)静态存储:这儿的“静态”(Static)是指“位于固定位置”(尽管也在RAM里)程序运行期间,静态存储的数据将随时等候调用。可用static关键字指出一个对象的特定元素是静态的。Java对象本身永远都不会置入静态存储空间。 成功的人生,需要自己去经营,别再说了,莫再等了,现在就为自己的人生做好规划,为人生点亮一盏明灯,赢在人生起跑点上。 (5)常数存储常数值通常直接置于程序代码内部。这样做是安全的,因为它们永远都不会改变。有的常数需要严格地保护,所以可考虑将它们置入只读存储器(ROM)。 成功的人生,需要自己去经营,别再说了,莫再等了,现在就为自己的人生做好规划,为人生点亮一盏明灯,赢在人生起跑点上。 (6)非RAM存储若数据完全独立于一个程序之外,则程序不运行时仍可存在,并在程序的控制范围之外。其中两个最主要的例子便是“流式对象”和“固定对象”。对于流式对象,对象会变成字节流,通常会发给另一台机器。而对于固定对象,对象保存在磁盘中。即使程序中止运行,它们仍可保持自己的状态不变。对于这些类型的数据存储,一个特别有用的技巧就是它们能存在于其他媒体中。一旦需要,甚至能将它们恢复成普通的、基于RAM的对象。Java 1.1提供了对Lightweight persistence的支持。 .. 2.3 对象的作用域 { String s = new String("a string"); } /* 作用域的终点 */ 那么句柄s会在作用域的终点处消失。然而,s指向的String对象依然占据着内存空间。在上面这段代码里,我们没有办法访问对象,因为指向它的唯一一个句柄已超出了作用域的边界。 ... 2.4 javadoc注释 . 1. @param 格式如下: @param 参数名 说明其中,“参数名”是指参数列表内的标识符,而“说明”代表一些可延续到后续行内的说明文字。一旦遇到一个新文档标记,就认为前一个说明结束。可使用任意数量的说明,每个参数一个。 特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系 2. @return 格式如下: @return 说明其中,“说明”是指返回值的含义。它可延续到后面的行内。 . 3. @exception 违例标记的格式如下: @exception 完整类名 说明其中,“完整类名”明确指定了一个违例类的名字,它是在其他某个地方定义好的。而“说明”(同样可以延续到下面的行)告诉我们为什么这种特殊类型的违例会在方法调用中出现。 .. 4. @deprecated 这是Java 1.1的新特性。该标记用于指出一些旧功能已由改进过的新功能取代。该标记的作用是建议用户不必再使用一种特定的功能,因为未来改版时可能摒弃这一功能。若将一个方法标记为@deprecated,则使用该方法时会收到编译器的警告。 第3章 控制程序流程 “就象任何有感知的生物一样,程序必须能操纵自己的世界,在执行过程中作出判断与选择。” . 3.1 赋值在为对象“赋值”的时候,情况却发生了变化。对一个对象进行操作时,我们真正操作的是它的句柄。所以倘若“从一个对象到另一个对象”赋值,实际就是将句柄从一个地方复制到另一个地方。这意味着假若为对象使用“C=D”,那么C和D最终都会指向最初只有D才指向的那个对象。 对真正的成功者来说,不论他的生存条件如何,都不会自我磨灭 3.2 == 问题 Integer n1 = new Integer(47); Integer n2 = new Integer(47); System.out.println(n1.equals(n2)); //结果是false ,因为比较的是n1和n2的引用(即比较的是指向n1和n2的指针) 对真正的成功者来说,不论他的生存条件如何,都不会自我磨灭 每个类都有一个默认的equals()方法,因为Object 就有这样一个方法;所有的类默认继承它,但比较的是引用 ! 第六章 重复运用classes 在面向过程的语言中重复运用代码只是简单的复制代码,以达到重复运用的目的,而在面向对象的java程序中,代码的重用主要体现在2点 1、在新的class中使用既有的class,这中方法称之为"组合"。但是这种重用方式只是很单纯的重复运用以有的代码 功能,而非重复运用其形式。 2、让新的class成为既有class的一类,并且根据需要加入新的功能,而无须更动原有class,这种方法称之为"继承"。 组合语法 其实组合我们在以前的例子中已经大量的用到了,我们只要将对象句柄放置在class中就是组合! class compostion { private String s; compostion() { System.out.println("compostion()"); s=new String("hello"); } public String toString() { return s; } } public class test { compostion c; //对象句柄 int i; public void show() { System.out.println("int = "+i); System.out.println("compostion = "+c); } public static void main(String args[]) { test t = new test(); t.show(); } } 其中每个非基本数据类型的对象都有一个toString()方法,该函数用于将compostion转换为一个string,和其他string相加class中基本数据类型会被初始化为默认值,而对象句柄会被初始化为null。如果你要使用该句柄,切记要初始化,否则会抱空指针错误! 继承 继承是java语言中极其重要的一部分,使用关键字extends来实现,这样变自动的让子类获得了父类中所有的成员数据和函数。而java中所有的类甚至包括你自己已经定义的或者将要定义的类都是继承自object类的,在编译器内部进行的隐式继承 class base { int i=10; public void show() { System.out.println("base method"); } public static void main(String args[]) // java允许在同一个文件中的class拥有各自的main() { new base().show(); } } class derived extends base //继承 { public void show() //覆盖了base的函数 { System.out.println("derived method"); super.show(); //调用base的函数 } public void newMethod() //子类中新加入的函数 { System.out.println(i); //打印base中的数据 } public static void main(String args[]) { derived d = new derived(); d.show(); d.newMethod(); } } base的初始化 当子类被初始化的时候系统会先将被继承的父类初始化,java编译器会在调用子类构造函数之前调用父类的构造函数 class base { base() { System.out.println("base method"); } } class derived extends base { derived() { //super(); 系统会自动加入对父类的调用 System.out.println("derived method"); } public static void main(String args[]) { derived d = new derived(); } } 假如你的父类是带有引数的class,那么编译器是不会自动调用构造函数的,你必须使用super来调用,否则系统会抱错 class base { base(String s) { System.out.println(s); } } class derived extends base { derived() { super("base method"); //必须是构造函数的第一行语句 System.out.println("derived method"); } public static void main(String args[]) { derived d = new derived(); } } 兼容组合和继承 有的时候,我们在编写class的时候不但用到组合,还用到继承。 组合和继承之间的选择 如果你只是希望在新class中使用到既有class的功能,并且隐藏起实现细目,那么最好使用组合。 如果你希望为既有的class开发一种特殊版本,那么继承再好不过了。 如果你的既有类和新类是一种is a的(是一个)关系,那么使用继承,如果是一种has a(有一个)关系,那么使用组合 protected 我们再来复习一下protexted的意义:继承此class的子类,可以访问该类的成员,并且对于一个包内的其他类是friendly的 渐进式开发 继承的优点之一就是支持渐进式开发,在这种开发模式下,你可以在程序中加如新的程序代码,但是却不影响父类的代码 向上转型 由于继承的关系,在子类中可以使用父类的所有函数,并且任何发送给父类的消息,也可以发送给子类 class base { protected void show() { System.out.println("base method"); } protected void getSomeone( base b) { b.show(); } } class derived extends base { public static void main(String args[]) { derived d = new derived(); d.getSomeone(d); } } 注意在getsomeone的函数定义中,我们定义的是他只能接受一个base类的句柄,然而他居然接受了子类的句柄。因为子类虽然和父类不太相同,但是他毕竟是父类的一种因此适用与父类的函数当然也适用与子类咯,我们把这种把子类转型为父类的做法叫做向上转型upcasting class base { public void methodOne() { System.out.println("base.methodOne"); } public void methodTwo() { System.out.println("base.methodTwo"); } } class derived extends base { public void methodOne() { System.out.println("derived.methodOne"); } public void methodTwo() { System.out.println("derived.methodTwo"); } public void methodNew() { System.out.println("derived.methodNew"); } public static void main(String[] args) { base d = new derived(); d.methodOne(); //虽然已经向上转型成为了base,但是调用的函数还是基类的函数,显示derived.methodOne d.methodTwo(); //同上 //d.methodNew(); 因为已经向上转型,所以丢失了子类特有的函数 } } 为什么需要向上转型? 子类是父类的一个超集,因此子类中至少包含父类中的函数,并且可能会更多。然而向上转型会使子类遗失和父类不同的方法,向上转型一定是安全的,因为这是从特殊类型改变成通用类型。 组合vs继承 当你需要向上转型的时候是使用继承的最佳时间 关键字final 什么是final?就是"最终"的意思,也就是不可改变的意思,我们在这里讨论3中final:data、method、class final data final的数据是固定不变的,不变的数据称之为常量,他是很有用的,因为它 1、可以是永不改变的编译期常量。编译期常量可以在编译期执行某些计算,减少执行期的负担,此类常量必须是基本数据类型,使用final修饰,必须给定初值。 2、可以在执行期被初始化,而你却不想再改变他。 如果某个数据既有static还有final,那么他就会拥有一块无法改变的储存空间。当把final用于对象时,final让句柄保持不变不能再重新指向其他对象,然而对象本身却是可以改变的,这点和final的基本数据类型不能改变其值的特点有所不同。 class Value { int i = 1; } class finalData { //编译期常量 final int i = 10; static final int II = 20; //典型的常量 public static final int III= 30; //执行期常量 final int iiii = (int)(Math.random()*20); static final int iiiii = (int)(Math.random()*20); Value v = new Value(); final Value vv =new Value(); static final Value vvv =new Value(); //数组 final int[] a = { 1,2,3,4,5,6}; public void show(String id) { System.out.println(id+" : "+"iiii ="+iiii+" , iiiii = "+ iiiii); } public static void main(String[] args) { finalData fd = new finalData(); //fd.i++; 不能改变值 fd.vv.i++; //final对象可以改变其对象值 fd.v=new Value(); //非final for(int i = 0;i
thinking in java 学习心得
最新推荐文章于 2019-01-14 20:40:00 发布