[软件构造]02-数据类型和类型检验

本文包含以下几个方面:

1.编程语言的数据类型

2.静态检查与动态检查

3.可变类型与不可变类型

4.数组与集合

5.聚合体的迭代器

6.巧用不可变性

正文:

一、编程语言的数据类型:

基本数据类型:int,long,boolean(在java中与整型隔离),double,char。(小写)

对象数据类型(object reference):String,BigInteger等。(首字母大写)

在实际使用中,基本数据类型是很容易使用的:

int value=3;
char text="c";

但是,当想对基本数据类型执行相应操作,或者借助它们生成聚合体(例如List)就必须使用相应的对象类型,例如:

List<Integer> value=new ArrayList<Integer>();
int s=Integer.parseInt("18");

二、静态检查与动态检查:

Java是一个静态类型语言,它所有变量类型在编译时已知,因此编译器可以推导表达式类型,并在编译阶段进行类型检查(动态语言在运行时检查类型)。静态语言会检查语法错误(动态则会检查除了类型的语法错误,例如非法参数值,索引越界,空指针)、类名、函数名错误、参数数目错误、参数类型错误、返回值类型错误等。所以总的来说静态检查是检查类型而不是值,动态检查正相反。

例如:

int a="c";//Eclipse直接在代码区提示"type mismatch"
for(int i=0;i<=list.size();i++)//Eclipse只有在运行后才提示索引超限异常

这里的type mismatch应当强调的是两种变量的使用契约是一致的,例如double a=2中2被隐式转化为2.0,或者long和int在某些情景可以混用,这些都是不破坏使用契约的情景。

三、可变类型与不可变类型

变量的"变"有两层意思:一是改变变量,将该变量名指向另一个存储空间;二是改变变量值,将该变量指向的存储空间写入新值。

在可变性中我们强调第二层意思。所以不变数据类型(Immutable)就是一旦被创建就不得修改值(通常是final)。引用类型也可以是不可变的,说明它不能更改指向对象(但被指对象的值可以变)。编译器进行静态检查时,若发现final首次赋值后值发生改变就会报错(cannot assigned)。编程时尽量用final变量作为方法的输入参数和局部变量。注意,final不能派生子类、不能改变值和引用、方法不能被子类重写。

例如,对于Immutable的String和Mutable的StringBuiler,有不同的用法:

String s="m";
s=s+"n";//原来的"m"成为垃圾,s重新指向存储着"mn"的一个内存
StringBuilder sb="a";
sb.append("b");//在sb指向的内存上直接修改

这样看来,不可变类型对待修改会产生大量的临时拷贝(垃圾回收)但是更安全、其他质量指标更好,而可变类型可以最少化拷贝以提高效率所以性能更好、适于共享,但是函数调用可能会改变可变类型的值,引发其他相关函数的结果错误。例如:

class A(){
    public int a=一个正数;//假定对于这个类来说a是可变的
    function1(int a)
    {将a变成相反数}
    function2(int a)
    {计算a的算术平方根}
}
//一旦先调用A.function1再调用A.function2,就会引发错误!

对于这种情况,可变类型在使用时最好使用防御式拷贝。最简单的防御式拷贝就是返回一个新类型,这个类型的内容等价于原来想传递的变量。

四、数组与集合

Java的聚合体与C语言相比可以说晦涩难懂。C语言的聚合体类型相对统一,但Java对每个聚合体还分支出了不同的子类聚合体。常见的有:

        List类:

        ArrayList——动态数组,查询快,增删慢(移动相关),线性不安全,效率高。

        LinkedList——链表数组,查询慢(遍历相关),增删快,线性不安全,效率高。

        Vector——与C语言的Vector类似。

        Set类:

        HashSet——哈希表只依赖与hashCode()和equals(),当放入新元素时借助这两个方法检查是否有重复元素。最终这个集合是无序的。

        LinkedHashSet——哈希表和链表的结合,它是有序的,顺序是元素添加顺序。

        TreeSet——由红黑树实现,调用compareTo实现自动排序。

        Map类:

        HashMap、LinkedHashMap、Hashtable、TreeMap。

它们的常见操作如下:

1)数组(array):T[] a=new in[len]是不可改变长度的定长数组。

       操作:索引a[i],赋值a[2]=0,长度a.length等。

2)列表(list):List<T> list=new  ArrayList<T>()是边长数组,是抽象接口。

       操作:索引list.get(2),赋值list.set(2,0),长度list.size()等。

3)集合(set):Set s1=new HashSet()。元素不可重复,而且排列是无序的。集合是抽象接口。

       操作:s1.contains(),s1.containsAll()检查子集,s1.removeAll()等。

4)地图(map):Map<T1,T2> map=new HashMap<T1,T2>()存储键值对。地图是抽象接口。

       操作:map.put(key,val),map.get(key),map.containsKey(key),map.remove(key)。

五、聚合体的迭代器

除了Array,其余聚合体都可以采用迭代器循环方法。例如:

int[] a={1,2,3,4,5};
for(int i=0;i<a.length;i++){...}
List<L> A=new ArrayList<L>();
for(L item:a){...}//迭代器循环1
Iterator it=A.iterator();
while(it.hasNext()){...}//迭代器循环2

注意,在动态数组或迭代器循环中,切不可循环remove()!这是因为这些聚合体动态地删除元素后内存位置发生改变,但迭代器或索引没有变,所以程序找不到下一个要删除的元素,导致删除失败!        

六、巧用不可变性

基本类型及其封装对象是不可变的,但是这种不可变性是运行时产生的,静态检查无法发现。于是使用不可修改封装(unmodifiable wrap),剔除任何修改它的操作,并对修改操作返回“不支持操作异常”。它用来保护只读数据结构,定义方式为public static <T> src<T>。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值