面向对象分析和设计笔记——第3章
[1]封装性
对象是Java封装性最大的体现
public ClassName{
成员变量/数据成员:访问权限 数据类型 变量名 ;
成员方法:访问权限 返回值类型 方法名(){函数体;}
构造方法:访问权限 类名(){}
}
对象的创建与使用:1)对象变量(或引用reference)的声明:声明时操作系统仅会在栈(Stack)内为其分配一个存储首地址数据的32位内存空间;
2)对象的创建:通过new关键字和实参来调用构造函数来构建对象并初始化成员变量/数据成员,此时操作系统才会在堆(Heap)内为对象分配存储对象信息的内存空间,同时将引用地址值返回给Stack中的引用变量;
3)对象的使用:通过点运算符“.”可以实现对对象属性的访问和方法的调用,reference可以是已初始化的对象变量,也可以是生成对象的表达式,如:
reference.variable; reference.methodName([paramlist]); float tx = pointOne.x; float tx = new Point2D(100.0, 200.0).x; pointOne.setX(300.0);
new Point2D(100.0, 200.0).setX(300.0);
注:1){}“类体”或可以认为是“类的作用域”;
2)Java中的方法只能作为类的一部分创建,所有的方法都不能游离于类之外;调用方法是通过方法名和实参类型来匹配的;
3)当为某个类撰写多个构造方法时,若想在其中某个构造方法内部调用另一个构造方法,使用this ([参数列表]);
4)Java中的reference不同于C++,不可以对引用变量进行“加加减减”的操作;
注:Java基本上是“纯”的面向对象语言,唯一“不纯”的地方在于存在8个基本数据类型(byte\short\int\long; float\double; char; boolean)为了弥补这个问题,Java提供了基本类库,提供了这8个基本数据类型的包装类(Wrapped type);此外如果精度还是不够,还可以考虑使用BigDecimal和BigInteger这两个类;
注:1)IEEE754
2)BigDecimal类
3)BigInteger类
[2]继承性
1)类型为Employee的一个对象首次创建时,或者Employee类的静态方法或数据首次调用时,JVM必须查找环境变量classpath所指定的位置,找出并装载Employee.class;
2)Employee.class装载后,如果它有超类,那么便会继续装载它的超类,如果超类还有超类,便会继续装载第二个超类,以此类推。从超类到Employee类的所有静态初始化模块顺序进行,静态初始化动作仅发生一次
3)当1)中创建一个Employee对象时,从超类到Employee类,每一类会分别顺序执行如下步骤:Object*4→Person*3→Employee*3
在堆内分配一个足够的存储空间,reference地址一样;
系统会先用默认值初始化对象的成员变量;
执行所有出现于数据定义处的初始化动作;
执行构造函数;
4)如果应用程序没有结束,在同一个应用程序中再创建类Employee的对象时,则从3)开始循环执行;
1 public class Person{ 2 private String name; 3 private String address; 4 public Person (String initialName, String initialAddress){ 5 name = initialName; 6 address = initialAddress; 7 } 8 public String getName(){ 9 return name; 10 } 11 public String getAddress(){ 12 return address; 13 } 14 public String toString(){ 15 return "name:" + name + "|address:" + address; 16 } 17 }
1 public class Employee extends Person{ 2 private double salary; 3 public Employee(String initialName, String initialAddress, double initialSalay){ 4 super(initialName, initialAddress); 5 salary = initialSalary; 6 } 7 public double getSalary(){ 8 return salary; 9 } 10 public void setSalary(double newSalary){ 11 salary = newSalary; 12 } 13 public String toString(){//override 14 return super.toString() + "|salary:" + salary;//return "name:" + getName() + "|address:" + getAddress() + "|salary:" + salary; 15 } 16 }
对象转型:
向上转型(upcasting)时,父类的引用变量虽然指向子类对象,但不能通过该引用调用子类所特有的方法;如果想通过父类对象的引用调用子类特有的方法,则必须对其进行强制类型转换;upcasting是自然的、合法的,基本都是legal的
1 Employee employee = new Employee("lee", "99Ave", 30); 2 Person person = employee;double salary = ((Employee)person).getSalary();
向下转型(downcasting)有着特殊性:如果父类对象引用指向的是一个子类的对象,则可以使用downcasting,把超类引用显式地转型为一个子类的对象;如果超类对象引用指向的是一个自身类对象则不可以使用downcasting,illegal的
1 Person person = new Person("lee", "99MainAve"); 2 double salsry = ((Employee))person).getSalary();//ClassCastException
downcasting常与instanceof操作符合用以避免非法的向下转型
1 Person person = new Employee("lee", "100 MainAve", 100); 2 if(person instanceof Employee){ 3 salary = ((Employee)person).getSalary(); 4 }
重写和重载:
重写只覆写方法体,返回值类型、方法名、形参类型都完全一样,是子类与父类之间的关系;重载是拓展新的方法,只发生在一个类内,不是多个类之间的关系
equals();toString();是Java中重写频率最高的两个方法
1 public Boolean equals(Object o){//因为重写要求形参类型不能变,所以隐式地将子类对象当作父类变量,upcasting 2 if (o instanceof Employee){ 3 Employee e = (Employee)p;//downcasting 4 return this.getName().equals(e.getName()) && this.getAddress().equals(e.getAddress()) && this.getSalary() == e.getSalary(); 5 }else{//this 指向调用该方法的实例对象,即equals左边的对象 6 return false; 7 } 8 }//逻辑运算符参与构成的逻辑表达式的运算结果任然是布尔型量
在大部分类中,继承自Object类的equals()方法都不适用,因此每个类都需要覆写该方法,API中“wrapper classes”、Date、String中可供调用的equals();方法都是经overwrite过的,先在形参除向上转型再在函数体内向下转型是重写equals();方法中的模板
1 public String toString(){ 2 return "name:" + name' 3 }
在大部分类中,继承自Object类的toString()方法也都不适用,因此每个类都需要覆写该方法,如果不覆写则返回@+字符串地址的Hash码;覆写后的toString();方法一般返回一个表示该对象特征的字符串
final关键字:
1)final类
final的类,不能被继承;
2)final方法
父类中final的方法,子类中不能overwrite这个方法,但可以overload这个方法;
3)final变量
基本类型:不能被再次赋值;对象类型:不能修改指针,但可以修改指针指向的内部的值;
[3]多态性:是由继承性带来的
多态的变量FacultyMember m;//静态绑定,允许将多种类型的子类变量当作一种数据类型来对待
多态的方法调用m.toString();//JVM会自动利用instanceof进行点运算符的动态绑定,从而可以区分不同子类之间的差异性
1 FacultyMember[] members = new FacultyMember [8]; 2 for (int index = 0; index<members.length; index++){ 3 System.out.println(members[index].toString()); 4 }
[4]“1对多”关联关系的实现
加<>, <>内为变量名,多个变量间用","间隔;泛型类内的数据类型、构造函数形参类型等均可待定,用<>内的变量表示即可;在声明泛型对象时,数据类型要具体化
如何撰写一个泛型类:
1)先写出一个实际的类;
2)将此类中准备改变的类型名(如应用时,可能int要改变为float或char);即改用一个自己指定泛型参数(如E、A、B);
3)类中的方法可以使用泛型参数类型,static方法无法访问泛型类的类型参数;(因为具体的参数类型在实例化的时候才知道)
4)使用泛型类时,必须在创建对象的时候指定类型参数的值;
方法也可以声明为泛型方法:
1)调用泛型方法与调用普通方法一样,不必指明参数类型;
2)泛型方法可以看作被无限次重载过;
1 public class GenericMethods{ 2 public <T> void f(T x){ 3 System.out.println(x.toString()); 4 } 5 6 public static void main(String[] args){ 7 genericMethods gn = new GenericMethods(); 8 gn.f("Hello"); 9 gn.f(new Product("coffee", 27, "34")); 10 gn.f(new Point2D(23, 45)); 11 } 12 }
Arrays:管理对象个数确定
一维数组
多维数组:java中并没有真正的多维数组,只有数组的数组,java中多维数组不一定是规则的矩阵形式;
每一行的大小可以单独定义如,int[][] xx;
xx[0] = new int[3];
xx[1] = new int[2];
容器类:管理对象个数不确定
Collection<E>:其内每个位置仅持有一个元素,提供add();方法添加元素,包括
List<E>:以特定次序存储一组元素; ArrayList<E>,Vector<E>
Set<E>:元素不得重复
Map<K, V>:其内每个位置所持有的时一群成对的key-value(键值-实值对),类似小型数据库,提供put();方法添加元素
1 ArrayList<String> list = new ArrayList<String>(); 2 list.add("ArrayList"); 3 list.add("and"); 4 list.add("for-each"); 5 String result = ""; 6 for(String element : list){ 7 result +=element; 8 } 9 stdOut.println(result);
1 Collection<Point2D> c; 2 for(Point2D point : c){ 3 int x = point.getX(); 4 } 5 //c为容器类型的变量,容器的名称 6 //Point2D为容器内的变量类型 7 //point为Point2D类型的变量,每次循环的执行都从容器中取出对象并赋给该变量来运用
[5]基类的重新考虑——继承与接口
基态类型的变量都是多态的;通过基态变量+“.”运算符激活的基态方法都是多态的,JVM会自动地动态绑定方法地调用和方法地方法体之间的关联关系;
抽象类存在的原因:
1)让客户端程序员无法产生(new一个)其对象,并因此确保这只是一个“接口”而无实体;//与非抽象基类的差异
2)共同的函数特征建立了一个基本形式,让程序员可以陈述所有子类的共同点,任何子类都可以以不同的方法体来表现此一共同的函数特征;
接口是一个"pure"抽象类:
1)接口内的所有变量都默认为是public static final的;接口类型不存在构造方法;接口内所有的方法都是static、public的;
2)在架构设计时,如何使用抽象类和接口来解决问题,是一个非常复杂的问题,抽象类更侧重于归纳同一父类的子类的共同特征,如果属性,方法;接口更侧重于定义任意的类有没有相同语义的方法,它是一个一经定义不轻易更改的规范,它的修改在项目中,往往是动一发而牵全身,即使有考虑不周到的地方,也会使用新增接口的形式去弥补。其实abstract class表示的是"is-a"关系,interface表示的是"like-a"关系。最顶级的是接口,然后是抽象类实现接口,最后才到具体类实现。
1)使用接口,能够实现子型别被向上转型至多个基类型别;
2)让客户端程序员无法产生其对象,并因此确保这知识一个“接口”而无实体;
3)共同的接口建立了一个基本形式,让程序员可以陈述所有的实现该接口的子型别的共同点。任何子型别都可以以不同的方法体来表现此共同接口,相比抽象类,“多态”程度更高;