1 对象(Double Elephants)

1.1 抽象

  1. 万物皆对象。什么东西都可以抽象成属性和方法,达到可重用的目的。
  2. 对象之间交流靠发送消息。就是通过调用某个特定对象的方法来达到消息传递的目的。
  3. 每个对象由其它对象组成。对象可重用,可以根据简单的对象构建更加复杂的对象。
  4. 每个对象都有对应的类型。即对象是某个类的实例,这个类便是类型,不同类型之间的区别是:不同的类可以发送和接收不同的消息。
  5. 特定类型的所有对象可以接收和发送同一类消息。继承相关。

1.2 每个对象都有一个接口

接口决定了实现该接口的类型的对象能发出什么消息。比如手机在初期就被设计成为可以[打电话]、[发短信]、[拍照],这些动作可以看做是接口,实现了这些接口就是一部手机。接口把这些必要的方法聚集起来,就能让实现接口的类型发出指定的消息。

1.3 每个对象都提供服务

将对象看作服务提供者,避免对象累赘,提高对象的内聚性,可重用性。

1.4 被隐藏的具体实现

访问控制:将程序开发人员分为类创建者和客户端程序猿,只向客户端程序猿暴露必要的部分,因为有些属性和方法在类内部操作是必要的,但是客户端程序猿并不需要关心,不让其访问和操作对象内部脆弱的地方,可以减少bug,提高安全性,也可以让他们看出哪些东西对他们来说是重要的。其次,访问控制让接口的实现和调用分离,即使内部发生了翻天覆地的变化,也不会影响客户端程序的使用。

public:任何人可使用;private:类型内部可使用,外部无法访问;protected:类型内部可使用,与private相似,但是继承的类可以直接访问protected;无访问指定词:包访问权限,在同一个包内,可以看做是public,但是在不同类中却可以看做是private,无法访问无访问指定词的属性和方法;

 package com.chenlei.study.thinkinginjava;
/**
* @author chenlei
* @date 2016年4月18日 下午10:55:16
* @version 1.0
* @description
*/
public class AccessSpecifier {
String string = "default field from same package";
String print(){
return "default method from same package";
}
}
 package com.chenlei.study.utils;
/**
* @author chenlei
* @date 2016年4月18日 下午10:56:47
* @version 1.0
* @description
*/
public class AccessSpecifierOtherPackage {
String string = "default field from another package";
String print(){
return "default method from another package";
}
}
 package com.chenlei.study.thinkinginjava;

 import com.chenlei.study.utils.AccessSpecifierOtherPackage;

 /**
* @author chenlei
* @date 2016年4月18日 下午10:59:41
* @version 1.0
* @description 访问限定词
*/
public class AccessSpecifierEntry { public static void main(String[] args) {
String stringSamePackage = new AccessSpecifier().string;
new AccessSpecifier().print(); String stringOtherPackage = new AccessSpecifierOtherPackage().string;//The field AccessSpecifierOtherPackage.string is not visible
new AccessSpecifierOtherPackage().print();//The method print() from the type AccessSpecifierOtherPackage is not visible
}
}

1.5 复用

复用并不是一味地继承,这样可能导致过分复杂的设计,而是在合适的时候用不同的已有对象组合成一个新的对象。

1.6 继承

拥有基类所有的属性和方法,但是想要扩展或改写某些方法时,不必创建新对象,可以继承基类并进行方法覆盖或扩展。这就意味着继承自基类的子类在接收到父类能够接手的消息时,能够做出响应,如果不覆盖父类的方法,子类将会做出和父类一样的响应,否则做出自己特定响应。也就是说子类能够对发送给父类的所有消息作出相应。

当基类完全继承和覆盖父类方法并不添加其他属性和方法,也不实现其他接口时,可以说子类是一个基类完全的替代,子类就是一个基类类型;当子类不满足前面的条件时,子类和基类就不完全一样了,子类就不是一个纯粹的基类类型。

 package com.chenlei.study.thinkinginjava;
/**
* @author chenlei
* @date 2016年4月18日 下午11:27:46
* @version 1.0
* @description 转型
*/
public class Cast {
public static void main(String[] args) {
Rectangle rectangle = new Rectangle();
Circle circle = new Circle();
Shape shape = new Shape(); shape = circle;
circle.computeArea();
shape.computeArea(); Circle circle2 = (Circle) shape;//转换成功,因为shape指向circle的地址,是一个circle的实例 shape = new Shape();
circle = (Circle) shape;//com.chenlei.study.thinkinginjava.Shape cannot be cast to com.chenlei.study.thinkinginjava.Circle
circle.computeArea();
shape.computeArea(); shape = rectangle;
rectangle.computeArea();
shape.computeArea(); shape = new Shape();
rectangle = (Rectangle) shape;//com.chenlei.study.thinkinginjava.Shape cannot be cast to com.chenlei.study.thinkinginjava.Rectangle
rectangle.computeArea();
shape.computeArea();
}
} class Shape{
protected Double area = 1d;
public void computeArea(){
System.out.println("shape");
System.out.println(area);
};
} class Circle extends Shape{
@Override
public void computeArea(){
System.out.println("circle");
super.computeArea();
}
} class Rectangle extends Shape{
private Double width = 1d;
private Double height = 2d;
private Double perimeter; @Override
public void computeArea() {
System.out.println("rectangle");
area = width * height;
System.out.println(area);
} public void computePerimeter(){
System.out.println("rectangle");
perimeter = 2 * (width + height);
System.out.println(perimeter);
}
}

子类可以自动转化成一个父类,因为父类的所有消息,子类都可以响应;一个子类却不能强制转化为父类,因为子类有很多,无法具体到某一个特定的子类(除非父类引用的子类的一个实例)。很显然子类之间也无法相互强制转换,在编译期就会出错。所以向上转型是安全的,向下转型是不安全的,因为一个圆一定是一个形状,但是一个形状却不能具体到到底是一个圆还是一个三角形。

1.7 多态

  1. 同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。
  2. 当把一个子类看作一个父类类型时(泛化),调用这个泛化的父类类型的方法或接口,到底是调用的父类的方法呢还是调用子类的方法?(子类)
  3. 非面向对象的语言编译器,会将会将函数的调用在运行前绑定,运行时直接解析到具体地址;面向对象的语言在编译时并不能确定具体哪一个方法将被调用,而是用一小段代码通过在对象中存储的具体信息来计算方法体的地址,方法体的地址会直到运行时才能被确定。
  4. 下面这个例子因为多态,程序更加简洁易扩展,而且因为多态才让不同的对象通过同一个方法做了正确的事
 package com.chenlei.study.thinkinginjava;
/**
* @author chenlei
* @date 2016年4月19日 下午10:45:56
* @version 1.0
* @description 多态
*/
public class Polymorphism { public static void main(String[] args) {
Bird bird = new Bird();
Chicken chicken = new Chicken();
Goose goose = new Goose(); move(bird);//----bird move----
move(chicken);//----chicken walk----
move(goose);//----goose fly----
} public static void move(Bird bird){
bird.move();
} } class Bird{
public void move(){
System.out.println("----bird move----");
}
} class Chicken extends Bird{
@Override
public void move() {
System.out.println("----chicken walk----");
}
}
class Goose extends Bird{
@Override
public void move() {
System.out.println("----goose fly----");
}
}

1.8 单根继承

所有对象最终都继承自Object类。单根继承保证所有对象都具备某些功能,比如Object的hashCode和equals方法等,这让一些更高级别的操作更加便利,比如GC和异常处理。

1.9 容器

1.Java里常用的容器,后面必须专门花时间总结,并且从源码角度分析与实现。

[Thinking in Java]Nothing but Java-LMLPHP

2.范型:在JSE5之前,容器里只能存储Object,这就使得从容器中取回对象时需要进行不安全的向下转型,JSE5引入范型(一对尖括号,中间是类型 List<String> list = new ArrayList<String>(); ),这就使得取回元素时就像放入时一样,不需要再转型。

1.10 对象的创建和生命周期

对象的存储位置可以是堆栈或堆。前者需要在程序编写时确定对象的存储空间、生命周期和类型,虽然在某种情况下这种控制方法很有效,但是却牺牲了灵活性,甚至因为程序未及时销毁对象导致内存泄漏;后者的创建和销毁是动态管理的,只有在程序运行需要的时候才能确定对象的存储空间、生命周期和类型,Java中采用的后者,创建对象时需要使用new关键字构建动态对象,Java专门的垃圾回收机制自动释放对象占用的内存,来避免暗藏的内存泄漏问题,可以不显式地在代码中调用方法释放内存,这些都是基于单根继承和对象在堆中管理这两个特性来实现的。

1.11 异常处理

我觉得异常处理主要有两点作用:一是捕获到异常后作出报告、捕获并对异常情况作特殊处理,避免程序终止;二是便于完善代码,使程序更加健壮。

1.12 并发编程

多线程、共享资源锁

1.13 Java与Internet

  1. C/S,环境、版本更新问题。
  2. B/S,只需要一个浏览器。
  3. 服务器返回静态页面:功能单一,交互困难不优雅。
  4. 服务器返回静态页面和客户端代码:插件,比如Applet、Flash等;脚本语言,如JavaScript、VBScript等。前者只要下载插件就能百分百运行程序,但是并不是所有浏览器都有各自的插件版本(针对Applet),所有浏览器都支持javascript,但是各个浏览器对其实现方式各有不同,调试起来一团糟。
  5. 服务器端编程:这才是Java发威的地方。
05-11 13:41