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的主要内容,如果未能解决你的问题,请参考以下文章