面向对象分析和设计笔记——第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)共同的接口建立了一个基本形式,让程序员可以陈述所有的实现该接口的子型别的共同点。任何子型别都可以以不同的方法体来表现此共同接口,相比抽象类,“多态”程度更高;

posted @ 2020-04-25 14:33  小菜在路上  阅读(159)  评论(0编辑  收藏  举报