早期的一次java演讲,Bill Joy(创始人之一)声称“java = C + + - -”
原则:程序设计就是将发生变化的东西同保持不变的东西分隔开
//以上是名言
1.实现方案的隐藏:
(1)将属性都设为private,避免直接访问属性,而只是提供相应的方法来对属性的访问。(2)抽象化,将实现类与接口相分离,对外界只提供相应接口,而隐藏方法的具体实现,作用:a.防止不该接触实现细节的程序员去改变实现的细节,而影响其他用户的使用。b.允许库的设计人员修改内部的实现细节,而不会对客户程序员造成什么影响。因为注入他们程序的只是接口。
2.方案的重复使用:
代码与设计方案的重复使用是面向对象中最重要的部分。
包含(组织)关系:例如单独将一个变速器设为一个类,而将汽车设为一个新类,将变速器类的对象注入到汽车这个新类中,这样的化需要使用到变速器的车类都可以使用到,这就是很好的实现了代码的重复使用,如果将这个类并在每一个汽车类中,那么有几种汽车就要重复代码几遍,这样的设计就会显得很差。
继承关系:一种重要的内部处理是,将衍生类当基础类来处理,即有一个参数为基础类的方法,此时我们可以调用方法而传递的参数是一个衍生类,程序会正确的处理成一个衍生类型,这个叫做UpCasting(上溯类型)。
程序会根据传递的参数的类型,会自动处理的去调用相应类的方法(可能子类重写了父类的方法,),编译器和运行系统会负责所有的实现细节,不用我们自己控制的,这种情况叫多形性,实现多形性的方法叫动态绑定。
我们在选择类的关系时,首先应该考虑组织(包含)关系,其次再考虑继承关系,继承会让程序变的很复杂
3.集合的使用:
最好的方式是集合的选择能根据自己的使用方式自动改变积累的实现方式,使得我们不用根据不同的需求(select,delete,update),来进行集合的更换(典型的像vector与list),但另一个方面,集合方面的效率的开销相对与垃圾处理器的开销和在window下运行的开销,算不上什么了。
在进行集合元素的迭代的时候最好选择继承器(iterator),使用与所有的集合类。
4.java是实现单根结构的设计模式的,即所有的类最终都是从一个基础类中进行继承的(Object类)。这样使得所有类的所有对象都有一个通用的接口,使得最终都属于同样的类型。所带来的方便:a.这能使得所有的对象都拥有一些基本的操作,并且所有的对象都是在内存堆中创建的,可以极大的简化参数的传递。
b.更方便的实现垃圾收集器,可以将一些必要的支持安装于基础类中(即Object),收集器可以将适当的消息发送给所有的对象。
5.java编程比C++简单:
a. java使用单根结构,并且我们只能在内存堆中以一种方式来创建对象(new)而不能在堆栈中进行创建,而C++允许在堆栈中进行创建,并且是分配,释放存储空间最有效的方式,而在内存堆中进行对象的创建是要在运行期间付出昂贵代价的。 b. java使用垃圾处理器来进行垃圾的回收,然后自动释放那些对象所占据的内存,而C++中,在用完一个对象后必须显示将其删除。(java系统的垃圾收集器一直是一个不清楚的结构,有时我们要在特殊的程序中避免垃圾收集器(实时系统))。
6.多线程:
多线程的处理在一般情况下还是蛮简单的,但当多个线程同时运行,并试图访问同一个资源,这样的话就必须进行一些处理:对共享的资源在使用时必须进入锁定状态,就是每个线程按顺序进行访问该资源,访问时资源被锁定,访问后资源被解锁,
同一时间只有一个线程能使用特定的内存空间(属于资源的一种),为实现该目的必须使用synchronized关键字,其他类型的资源必须由程序员明确锁定,通常要求程序员创建一个对象,用它来代表一把锁,每次多个线程访问该资源时都必须检查该锁。在设计单例模式也用到:http://www.cnblogs.com/shenliang123/archive/2012/03/26/2417968.html
7.web: 客户机/服务器:
(服务器:将各种元素集中到一起,信息仓库,用于投递信息的软件以及信息和存放该软件的机器。)
8.java不是十分容易就可以学会的语言,java中的类就是一种类型,只不过是一种复杂的类型。
9.在html中可以嵌入java小程序片(applet),在访问一个服务器时,服务器会根据请求将html和applet一并发送到客户的浏览器上,这个应用还是蛮好的。
10.服务器端编程:浏览器向服务器发出复杂的请求通常涉及数据库的事务,服务器进行处理后会将它格式化为html作为结果返回,这些请求都是需要服务器端的代码进行处理的,所有服务器端的编码还是很重要的(servlet)。
11.java与C++比较:各有各的优势,作用于不同的领域,java强大的类型检查和错误控制系统是不能比拟的,java的运行速度是不能跟C++比拟的,即使已经得到了大大的改变。
12.连接数据库可以选择jdbc,hibernate,jdo,jdo和hibernate相似都是面向
对象的,但jdo是面向数据库表的,而hibernate是面向类的对象的
第二章,一切都是对象
1.用句柄来操作对象:
在java的程序中一切都可看作是对象,但操作标示符实际上是一个指向对象的“句柄”,就像电视机和遥控器一样,(电视机就是一个对象,遥控器就是一个句柄,其实所有的操作都是在遥控器上进行的),但其实不是每个句柄都要对应某个对象,但最好这样,就是每次申明一个变量时都初始化。
2.java中数据的保存位置6个:
a.寄存器(处理器内部):存储量很小,是由编译器分配的,我们并没有直接的控制权。
b.堆栈(位于随机存储访问区[RAM]):一般对象的句柄都是存储在这里的,通过指针的移动来保存,释放内存,但对象是不存储在这里的,在存储时编译器必须准确知道保存的所有数据的长度和存储时间,影响灵活性。
c.堆(内存堆RAM):用来保存对象,只要是有new的代码来创建对象,就能在运行期间自动进行保存,使得编译器不用知道要存储多少对象和对象保存的时间,更有灵活性,但因此而付出的是运行时的内存分配(例外的,java为了对那些特别小并且简单的对象访问的高效,不使用new来创建,创建一个并非句柄的变量来存储实际值,存储在堆中)。
d.静态存储:位于RAM的指定位置,用static关键字指出一个对象的某些元素是静态的,但一个对象永远不会处于静态存储区。
e.常数存储:将常数直接存储在程序代码内部,
f.非RAM存储区
3.java中增加了两个类,用于进行搞进度的计算:BigInteger和BigDecial.
作用域(scope):由花括号的位置决定。Java中在一个大的作用域中已经申明变量X了,那么不能更小的作用域中进行重新的申明。
4.java对全局变量会赋初值(boolean默认值是false),但若为局部变量就不会进行初始化,在编译时报错,因此局部变量必须要进行初始化。
5.java中调用一个类中的方法,先实例化一个对象(static除外),然后用对象来调用正确的方法,面向对象的程序设计通常称为“向对象发送信息”,如
Int x = a.f(); 其他a为对象,f()为消息。
6.静态方法static:使用静态方法主要是为了能直接通过类来调用它的方法,在静态方法中只能使用静态的成员变量,因为非静态的成员变量必须通过对象来实例化,反正只要抓住静态方法不要创建类,就可以解决其他的一些问题了。
7.文档注释:
类文档标记:
@see:类名/完整类名/完整类名#方法名,表示引用其他文档中的类
@version:版本信息, @author:作者信息, Html的嵌入
变量文档标记:
只能包括嵌入的html和@see引用
方法文档标记:除了包括嵌入的html和@see引用外还包括
@param 参数名 说明 @return 返回值说明 @exception 完整类名 说明
8.从一个对象到另一个对象的赋值,实际就是将句柄从一个地方复制到另一个地方,那么C = D意味着C,D句柄都是指向D开始所对应的对象。
还有种现象叫“别名”:就是将一个对象A复制给另外一个对象B,那么B将会被垃圾收集器自动清除,此时B相当于是A对象的别名,指向的都是A对象,这时用A,B都可以改变对象中的属性值。关于别名下面会进行详细的说明
9.逻辑运算符:&&和||属于运算具有“短路“的特点,就是在明确表达式的返回的值后就不在向下进行计算了,而&和|就不具有这样的特点,会将所有的表达式都进行计算后进行返回
10.java里需要用到逗号的唯一场所的就是for循环。
11.C++含有sizeof()方法,来得到数据类型在不同的位数电脑上所占的位数,而java是不含有此方法的,因为java的所有数据类型在所有的机器上大小都是相同的。
12.标签:对java来说唯一使用到标签的地方就是在嵌套循环语句前,位置就是在紧靠在循环语句的前面,两者之间最好不要插入其他的任何东西,在循环语句前嵌入标签的理由是,可以让break或continue跳出多重循环,因为理论上break和continue都只是中断当前的循环。
13.将float或double类型的数据转化为整数值后,总是将小数部分“砍掉”,而不做任何的进位处理,Math.rondom()产生的随机数的范围是[0,1).
14.方法过载(重载):
就是相同的方法名,但方法参数的类型,数量,顺序不同,而单单是方法的返回值不一样就不能叫做方法过载,因为有时候我们并不关心方法的返回值,如调用f();编译器根本不能判定被调用的方法是含有返回值的还是不含有返回值的。
15.有个疑问,如果有两个同类型的对象,那么如果为这两个对象调用同一个方法呢?如:
class Ban{
f(){
………};
}
Ban ban1 = new Ban(); Ban ban2 = new Ban(); ban1.f(1); ban2.f(2);
那么只有一个f()方法,那么怎么知道是ban1调用的还是ban2调用的呢?
其实编译器会为我们做一些幕后的操作,他会将句柄当做自变量传递给方法f(); 所以前面的两个方法在编译后变成了f(ban1, 1); f(ban2, 2);
16.this关键字:
this都是指向这个对象或当前对象,而且本身会产生当前对象的一个句柄。
在构建器里调用构建器:可能需要在一个构造器中调用例外一个构造器,为了防止代码的重复,就用this,但只能调用一次,不能调用两次,而且必须放在最前面。
17.在一个类的内部定义一个对象句柄,如果不将其初始化为一个新对象,那么那个句柄就会获得一个空值。
第5章:隐藏实施过程
“进行面向对象的设计时,一项基本的考虑是:如何将发生变化的东西跟保持不变的东西相分离”
java 解释器的工作程序(浏览器中一般都含有java解释器):首先:它会找到环境变量classpath变量(在安装jdk时必须进行配置),classpath会包含一个或多个目录,它们作为根,从这里展开对.class文件的搜索。
解释器在遇到import时,它会搜索classpath下指定的路径,在查找子目录(提供的包名),然后查找名称适当的已编译的文件。
有时我们想自己创建一些新的工具类,那么可以将这些类编译放到tools目录下,如将System.out.println()创建一个别名,那么就可以自己写一个类:
java的默认的访问没有关键字,但它通常成为“友好(friendly)”访问,这意味着现在包内的其他的所有类都能访问该友好类成员,但对包外的类是私有的,因此也说“友好”元素具有“包访问”权限。但如果那个类中有一个申明public 的static方法,那么包外还是能够通过类名来进行该静态方法的访问,虽然不能创建该类的对象。
子类实例化过程,子类对象在实例化之前程序会默认首先调用父类中的构造方法之后再调用子类自己的构造方法,但调用的基类的构造方法是默认的构造方法,如果要调用含有参数的父类构造方法,必须显示的调用(用super关键字)。其实在子类构造中隐含了一个super()语句,因此如果一个类的构造方法被私有化后,那么这个类就不能被继承了。被子类覆写的方法不能拥有比父类方法更严格的访问权限。可以通过super来调用父类中的方法。父类总的private方法是不能被覆写的。
我们在创建一个类的时候可以这样设计:将public成员写在最开始,接着是protected, private成员,这样做的好处是能自上而下进行阅读,首先看到对自己最有用的(public).
每种非基本数据类型都含有一个toString()方法。有时我们为了输出方便我们需要重写该方法。
创建一个对象句柄:在下面情形下可以对其初始化一个对象。a.对象定义的时候,这意味着他们在构造器创建之前肯定得到了初始化。b.在那个类的构造器中。C.紧靠在实际要求使用那个类的时候。
合成就是在一个类中加入另外一个类的句柄,然后初始化一个对应的对象。在合成与继承之间如何选择: a.当想利用新类内部一个现有类的特征,而不想使用它的接口,就使用合成,就是说可以嵌入一个现有类的对象类实现新类的特征,使得新类的用户看到的是我们已定义的接口,而不是嵌入类的接口,这种需求就要在新类中嵌入现有类的private的对象。Car就是一个很好的例子,有engine, door, window, wheel等的合成。继承与合成是设计类时非常重要的,要考虑详细,可以到项目完成时在重新构想一边,看看结构是不是合理。选择的重点就是是否需要使用现有类的接口,需要就用继承,如果只是为了实现新类的接口而需要某一个现有类特征,就使用合成。
总结继承的关系 = “新类属于现有类的一种类型”
10.上溯造型(对多形性):表示将子类的句柄转化为父类的句柄,可以去调用参数为父类的方法,但传递的参数却是子类的句柄。这样做很方便,能省掉很多的代码,否则在一个类中写一个对基类和衍生类都能调用的方法,那么有多少衍生类就需要重复写这个方法几遍,如果采用上溯造型就可以只写一个参数为基类的即可,子类也能作为参数调用,而且若以后我们增加了更多的衍生类,那么也不用于更改该方法,是软件具有更好的“扩展性”。而由于方法在编译期间是不能判断传入的参数是该基类的哪一个衍生类,因此为了能顺利完成调用,还需要“后期绑定”,更准确的讲叫“动态(运行期)绑定”, 在运行期判断相应的对象的类型,并准确执行相应的方法(这样必须在对象中安插某种特殊的标记)。但如果一个方法申明为final,那么编译器就不会执行“动态绑定”,因此执行速度就增加了。这样我们就能使用一种新的实例化方式:基类 句柄 = new 衍生类(); 在里实际上创建的是一个衍生类对象,但句柄指向的却是基类,将该参数传入相应的方法并调用该句柄所对应对象的方法,此时编译器会知道会去调用衍生类的覆盖方法(动态绑定已经生效)。还有一点,如果衍生类中有个方法的方法名跟基类的相同,但参数的类型or顺序or个数不一样,那么这个同样叫做过载(重写)。
11.final的使用:a.基本数据类型中使用final修饰表示该数据是一个常数,永远不能改变,并且必须要赋初值(可以是一个表达式),而如过是修饰一个句柄(即对象或数组),那么只是不能再将该句柄指向其他的新对象,但可以改变对象的属性或数值。当用static和final同时修饰时该数据就是一个全局常数(一般采用大写)。无论是将其定义为static还是定义为final,在编译期间都是不知道值的,只有在运行期(初始化)才知道。b.自变量用final修饰,如方法的参数被定为final,那么在方法内部不能改变该参数句柄所对应的对象,只能读取它。c.方法被final修饰,理由一:不想让继承类来覆盖和改写该方法,防止继承类改变它本来的含义。理由二:会使代码的执行效率变高。(但这样做要慎重)。
12.类的整个初始化顺序(包括继承):装载程序先到外面去找到那个类,找到后查看是否有基类(extends),有的话随之将其载入,无论是否需要生成基类的对象,这个过程都会发生,如果该基类还含有基类,也将其载入,依此类推。之后会在根基类中执行static初始化,在到衍生类中初始化,以此类推。保证这个顺序是很重要的,因为可能衍生类的初始化可能依赖基类成员。至此必要的类都已经装载完毕,可以进行对象的创建,首先,这个类中的基础类型数据都是初始化为默认值,对象句柄初始为null(为对象分配的存储空间初始化成二进制0),随后调用基础类构造器,这个调用是自动的,我们也可自己显示调用(super),如果基类的构造方法中含有基类的抽象方法,那么会调用子类的方法实现,基础类的构建采用与衍生类构建器完全相同的处理过程。基础类构建器完成后,实例变量会按本来的顺序进行初始化。最后执行构建器的剩余部分。
13.抽象类:里面的方法可以只有声明,没有方法主体,也可以有完整的方法,此时类必须声明为abstract,而在衍生类中必须实现基类的抽象方法,也有例外,如果不实现那么该衍生类也要成为抽象类。抽象类是不能被实例化的,保证了它的“纯洁性”。接口(interface):是对抽象类更深一步的抽象,接口也包含了基本数据类型的数据成员,但他们默认为static和final.在一般情况下,我们首先会选择接口,因为它同时获得了抽象类和接口的好处。我们还能extends继承一个接口来扩展新接口的内容,而且此时支持多重继承。
14.由于置入一个接口的所有字段都自动具有static和final属性,所有说接口时对常数值进行分组的一个好工具,在外部通过 接口.常数 进行引用,但获得的只是一个基本数据类型的数,不拥有额外的类型安全性,例如:本想用来代表一个月份的int变量,可能获得的是一个整数值。下面处理比较合理:
这是一个final类,其他类不能继承它,并且构造方法是私有的,就不能实例化了,唯一的实例就是那些final static对象,它们是在类本身内部创建的,这样就可以通过名字或数组的索引来调用,在main中我们可以知道此时是类型安全的,因为m是一个对象,只能将其分配给Month2。
15.内部类:将一个类定义置于另一个类中,对内部类的需求并不会太明显,至少不会立即感觉到自己要使用内部类了,有种情况就是外部类有一个方法而这个方法的返回值就是内部类的句柄,想在外部类非static方法内部之外生成内部类的一个对象,必须将那个对象的类型设为“外部类.内部类”。使用内部类的情形:(1)我们准备实现某形式的接口,使自己能创建或返回一个句柄,例如:当我们准备上溯到一个基础类(特别是接口),内部类就开始发挥其关键作用了(从用于实现的对象来生成一个接口的句柄具有上溯造型到基础类相同的效果),代码在181,(2)要解决一个复杂的问题,并希望创建一个类,用来辅助自己的程序,并且不愿意将其公开,在方法的内部创建一个内部类,或者在任意的一个范围内创建一个内部类,但使用该内部类只能在内部类的内部,外部就不能调用了。还有匿名内部类,不是很清楚如何正确的使用它们。内部类有一个重要的特点就是可以范围该类中的被封装的私有成员变量。而且很重要的一点:就是内部类的对象默认持有创建它的那个封装类的一个对象句柄。
16.构建构造器时的一个特别有效的规则:尽可能简单的方法使对象进入就绪状态,如果可能尽量避免调用任何的方法,在构建器内唯一能够安全调用的是基类中具有final的属性方法(private也可,自动为final),这些方法不能被覆盖,不会受到子类的影响(基类的构造方法中调用该类中的抽象方法来对对象进行初始化,但该抽象方法已经被子类覆盖,那个在调用父类的构造方法的时候,根据动态绑定原则,会自动去调用被子类覆盖的方法,但由于参数的原因,使得该对象可能未被正确的初始化)。
17.在“上溯造型”中能够通过基类来调用子类的所有方法,那么必须保证子类只含有基类的所有方法,不含有额外的方法。此时我们为了能调用子类额外的方法,那么必须使用“下溯造型”,但“上溯造型”肯定是安全的,因为基础类不可能拥有一个比衍生类更大的接口,故能保证基础类发送的每一条消息衍生类都能接受到,但“下溯造型”就不行了。
第八章:对象的容纳
1.数组:length只表示该数组能容纳多少元素或句柄,并不能得知实际包含多少,数组的访问速度比集合类要快,但其他的操作就不行,不够灵活,数组的大小必须事先确定,并且一旦创建并且初始化就不能删除里面的元素,以为占有的内存是不能释放的。
2.集合类:集合类在不使用范型的情况下,是不能自主控制放入集合内的对象的类型的,因为集合类定义的是一个根类型Object,但如果放入集合的对象不是同一个类型,那么在取出集合类对象时就会报错了,除了范型,控制集合中存入的对象的类型的就是将该集合放入一个方法中,该方法的参数就是某一对象的类型,然后将该对象存入集合中,这样就能保证存入集合的对象只会有一种类型,否则会在编译期间报错。
3.stack:满足“先入先出”的原则,就是堆栈中最先压入的对象要最先取出。
4.ArrayList与LinkedList比较:按顺序遍历集合的元素与插入与删除操作则LinkedList效率高,而随机访问集合中的元素则ArrayList效率高。
5.HashSet与ArraySet,我们默认选择HashSet,一样在Map的选择中,我们优先选择HashMap
6.java中提供了两个静态类来对数组或列表进行排列和搜索,用于数组的是Arrays,用于列表的是Collections,Collections中有个控制列表等集合不可修改的属性,这个还是蛮重要的,这样我们可以将一个容器设为private,然后写一个方法是来返回一个不可修改的容器类句柄,这样我们就可以保证用户不能修改容器内的元素,而在该类中还是可以修改的。还有一个静态方法是控制容器类同步的,叫synchronizedCollection,
7.关键字Instanceof告诉我们对象是不是属于一个特定类型的的实例,返回值是boolean值,如if(x instanceof dog){(dog)x.bark()};
转载于:https://blog.51cto.com/13003811/1934110