ADT and OOP

Posted yinghuoshouxin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ADT and OOP相关的知识,希望对你有一定的参考价值。

内容列表:

1.编程语言中的数据类型(Data type in programming languages)

2.静态与动态数据类型(Static vs. dynamic data types)

3.类型检查(Type checking)

4.可变性与不可变性(Mutability & Immutability)

5.快照图(Snapshot diagram)

6.复杂数据类型:数组和集合(Complex data types: Arrays and Collections)

7.有用的不变类(Useful immutable types)

8.空引用(Null references)

本课目的:

1.了解数据类型和静态的基本知识,编程语言中的动态类型检查

2.理解可变和可变对象

3.识别别名并了解可变性的危险性

4.使用不可变性来提高正确性、清晰度和易变性

5.使用快照图来演示特定时间的状态在程序执行过程中。

6.使用数组和集合处理复杂数据类型

7.了解空引用的危害并避免它

 

1·编程语言中的数据类型

(Data type in programming languages)

 

类型与变量

类型是一组值,以及可以在这些值上执行的操作。

 

例:
– boolean:  (true or false).
– int: (0, 1, -47).
– double:  (3.14, 1.0, -2.1).
– String: (“hello”, “example”).

变量:存储一个特定类型的值的命名位置

例:

int a;

Java中的数据类型

Java的数据类型有两种,分为基本数据类型(primitive types)及对象数据类型(object types)。例:

基本数据类型(primitive types):int ,float,boolean等

对象数据类型(object types)    :String ,Integer ,BigInteger等

注:根据java的公约,原始类型以小写字母开始,而对象类型以大写字母开始。

 

基本数据类型(primitive types) 对象数据类型(object types)
int ,long,byte,short,char,double,float,boolean Classes,interfaces, Arrays, Enums,Annotations
除了价值之外没有身份 有别于价值的同一性
不可变的(Immutable) 一些是可变的(mutable),一些则不可变
存储在栈(Stack)中 存储在堆(heap)中,由Java的垃圾收集机制管理(garbage collector)
不能实现一些复杂的表述 可以以泛型的形式表示一些复杂概念
耗费小 耗费大

基本数据类型都是不可变的(Immutable),而对象数据类型一些是可变的(mutable),一些则不可变。基本数据类型存储在栈(Stack)中,而对象数据类型存储在堆(heap)中,由Java的垃圾收集机制管理(garbage collector)。基本数据类型通常不能实现一些复杂的表述,而对象数据类型则可以以泛型的形式表示一些复杂概念。基本数据类型一般来时花费很小,而对象数据类型花费较大。

对象类型层次结构(Hierarchy of object types)

根是对象(所有非原语都是对象)

除对象以外的所有类都有一个父类,用扩展子句指定

例:

  class Guitar extends Instrument { ... }

1.如果扩展子句省略,则默认为对象

2.类是其所有超类的实例。

2.1从其超类继承可见字段和方法

2.2可以重写方法来改变他们的行为

技术分享图片

 

 

Boxed primitives(基本数据类型的包装类)

对象数据类型里还有一种由基本数据类型的封装的包装数据类(Boxed primitive),例如 Boolean , Integer,Short,Long,Float。主要是在Collection中使用。一般不要主动去使用包装类,因为这样做的效率低,代价很高。需要编译器做出自动转换。

例:

List<Integer> list = new ArrayList<Integer>();
list.add(1) ;
List.add(50);
1和50不是对象,但编译器能够通过,后台自动转换,但是会降低效率

相当于:

list.add(Integer.valueOf(1));
list.add(Integer.valueOf(50));

运算符(Operators)

运算符:执行简单计算的符号(+,-,/,*,=)

运算顺序:括号优先,乘除优先

例:

String text = "hello" + " world";

text = text + " number " + 5;

// text = "hello world number 5”

运算(Operations)

运算是输入和产生输出的函数(有时也会改变值本身)。

作为中缀、前缀或后缀运算符。  例如,a+b调用操作+: int×int -int。

作为对象的方法。   例如,bigIt1.add(bigIt2)调用操作add: BigTimeXBigToige:BigToCipe。

作为一个函数。  例如,Math.Sin(θ)调用Sin操作:double → double
在这里,Math不是一个对象而是包含正弦函数的类


Overloading operators/operations  重载运算符和操作

???、

2.静态与动态数据类型

(Static vs. dynamic data types)

 

 

java是一种静态类型语言。

-所有变量的类型在编译时(程序运行前)都是已知的,编译器也可以推断出所有表达式的类型。

如果A和B被声明为int,则编译器得出结论A+B也是int。

Eclipse环境在编写代码的过程中找错,这样,你会在编写代码的时候发现错误。

 

在动态类型语言(如Python)中,这种检查被推迟到运行时(程序运行时)

3.类型检查

  (Type checking)

静态类型检查(Static checking)与动态类型检查(Dynamic checking

语言可以提供的三种自动检查:

1、静态类型检查(Static checking):bug在程序运行之前自动的被找到,在编译时检查bug。可在编译阶段发现错误,避免了将错误带入到运行阶段,可提高程序正确性/健壮性

2、动态类型检查(Dynamic checking):bug在程序执行时被发现

3、无检查:自己亲自一个一个查吧

不用说,静态捕获一个bug比动态捕获它要好,动态捕捉它比不捕获它要好。

注:

静态检查范围:

语法错误,     比如额外的标点符号或假词。偶数
错误的名字,      比如Math.sine(2)(正确的名字是sin)
错误的参数数,   比如Math.sin(30, 20)
错误的参数类型,比如Math.sin("30")
错误返回类型,   如声明返回 int,最终返回“30”;

 

动态检查范围:

非法参数值。例如,当Y实际上为0时,整数表达式X/Y仅是错误的;否则它工作。因此,在这个表达式中,除以零不是静态错误,而是动态错误
不可代表的返回值,即当特定返回值不能在类型中表示时。
超出范围索引,例如,在字符串上使用负的或太大的索引。
调用空对象引用的方法。

 

静态检查vs动态检查:

静态检查:关于“类型”的检查,不考虑值

因此,如果误差只由某些值引起,例如除以零或索引超出范围,编译器不会对其产生静态错误。

动态检查:关于“值”的检查

 

特殊情况

一些本应该被动态检查的错误没有被检查。
1.整数除法:  5/2不返回一个分数,它返回截断的整数。
2.整数溢出。如果计算结果过于积极或太消极,无法适应有限的范围,它会悄悄地溢出并返回错误的答案(没有静态/动态检查)。例如,int=200000×200000;
3.浮点类型中的特殊值。NaN (“Not a Number”),正无穷大,负无穷大。例如, double a = 7/0;

 

 不匹配类型(Mismatched Types)

Java会直接对不匹配类型报错

例: String five = 5; // ERROR!

Conversion by casting 类型转换

例:

int a = 2;         // a = 2
double a = 2;       // a = 2.0 (Implicit)
int a = 18.7;         // ERROR
int a = (int)18.7;       // a = 18
double a = 2/3;        // a = 0.0
double a = (double)2/3;     // a = 0.6666…

 

4.可变性与不可变性

(Mutability & Immutability)

前言

    (1) 改变一个变量、改变一个变量的值,二者有何区别? 

        改变一个变量:将该变量指向另一个值的存储空间。

        改变一个变量的值:将该变量当前指向的值的存储空间中写入一个新的值。

    (2)“Change is a necessary evil.”变化是“罪恶”,但程序不能没有变化,

         要尽量避免变化,来避免程序中的副作用

    (3) 下面介绍java数据类型的Mutability与Immutability。

不变性(Immutability)

    (1)不变性是一个重要的设计原则

    (2)不变数据类型:一旦被创建,其值不能改变。

    (3)如果是引用类型,也可以是不变的:一旦确定其指向的对象,就不能再被改变

            我们可以使用final关键字来实现这一点:

            技术分享图片

            a.    如果编译器无法确定final变量不会改变,就提示错误,这也是静态类型检查的一部分。

            b.    所以,尽量使用 final变量作为方法的输入参数、作为局部变量。 

            c.    final表明了程序员的一种“设计决策”。

            需要注意的点:

                - final类无法派生子类

                - final类无法改变值/引用

                - final方法无法被子类重写

不变性与可变性

满足不变性的对象:一旦被创建,始终指向同一个值/引用 

满足可变性的对象:拥有方法可以修改自己的值/引用

 例:

  String与StringBuilder

        a.    String即为一个不可变的数据类型,一但创建,其值无法更改。

        b.    为了在一个String后面添加字符,需要新建一个String对象。

            例如:concat方法执行过程:

                    技术分享图片

        c.    而StringBuilder是一个可变类型,其具有修改内部值的方法。

            例如:append方法执行过程:

                技术分享图片

         d.    其之间的差异在只有一个引用指向该值时,没有区别。

            然而,在多个引用的时候,差异就出现了。

            技术分享图片

            t并不能改变s指向的值,而tb是可以改变sb指向的值的。

(2) 可变数据类型的优点与潜在风险:

    优点:

    a.    使用 不可变类型,对其频繁修改会产生大量的临时拷贝(需要垃圾回收) ,而可变类型

        最少化拷贝以提高效率。

    b.     使用可变数据类型,可获得更好的性能,也适合于在多个模块之间共享数据。

    但可变类型潜在风险,而不可变类型更“安全”,在其他质量指标上表现更好。

    选取时,要进行折中,看你看中哪个质量指标。

   

风险举例:

    a.    传递可变类型值时:

    技术分享图片

    该函数超出了spec范畴,因为其改变了输入参数的值。且这种错误非常难以跟踪和发现。

    对于其他程序员来说,也难以理解。

    b.    返回可变类型值时:

技术分享图片

    在这种情况中,使用的Date是mutable的,因此对其返回值进行更改会导致其对象的值发生变化。

    要避免这点,就应该使用java.time中的其他immutable的类,如:LocalDateTime与Instant

    或者在返回date类型时,进行防御性拷贝:

(3)防御性拷贝

    接上面的例子2,通过防御性拷贝,给客户端返回一个全新的Date对象,这样就避免了表

    示泄露,即避免了对原数据的修改。

    但是:大部分时候拷贝不会被客户端修改, 可能造成大量的内存浪费。

              如果使用不可变类型,则节省了频繁复制的代价。

    

(4)安全地使用可变类型:局部变量,不涉及共享,只有一个引用。

         如果有多个引用(别名),使用可变类型就非常不安全。

5.快照图(Snapshot diagram)

Snapshot Diagram是一种能表示程序运行时状态的图。使用绘Snapshot Diagram的方式对我们了解在运行时发生了什么是很有帮助的,尤其是在解决一些复杂的bug时。

Snapshot Diagram也对我们理解一些Java的概念有帮助,例如基本数据类型(primitive type)和对象数据类型(object type)、不可变值(immutable value)以及不可变引用(immutable reference)、指针混淆(pointer aliasing)等等。

Snapshot Diagram也更加直观的阐述的改变引用与改变值的内在实质。

--当为一个变量或者变量域赋值的时候,你其实是在改变变量指针的指向,你把变量指向了一个不同的值。

--当你改变一个可变值的时候,例如数组和list,你其实是在改变值里面的引用。

 

怎么画出Snapshot Diagram

对于基本数据类型,直接使用箭头指向就好了。

技术分享图片

 

对于对象数据类型,需要在外面有一个边界

技术分享图片

对于不可变对象,则是一个双层的边界。并且要注意改变不可变对象的方式是改变指向。

技术分享图片

 

 可变对象

技术分享图片

对于基本数据类型的不可变引用(使用了final关键字),使用双层的线段指向值,代表不能改变。

技术分享图片

 









以上是关于ADT and OOP的主要内容,如果未能解决你的问题,请参考以下文章

抽象数据类型(ADT)和面向对象编程(OOP)3.5 ADT和OOP中的等价性

ADT OOP

软构笔记-8-ADT和OOP中的“等价性”

软件构造 第三章第五节 ADT和OOP中的等价性

抽象数据类型(ADT)和面向对象编程(OOP)3.2规约

抽象数据类型(ADT)和面向对象编程(OOP)3.4 面向对象的编程