作用域(Scope)
作用域是指计算机语言中变量、函数、类等起作用的范围
- 变量的作用域有大有小,外部变量在函数内可以访问,而函数中的本地变量,只有本地才可以访问。
- 变量的作用域,从声明以后开始。
- 在函数里,我们可以声明跟外部变量相同名称的变量,这个时候就覆盖了外部变量。
- 变量的使用范围由作用域决定,作用域由词法规则决定,词法分析生成作用域链,之后查找变量就沿着这条作用域链查找,与函数调用栈就没有关系了。一般函数的生存期就是出栈后就结束了,如果是引用对象会在本次GC中回收,如果产生了闭包,那就要等到引用闭包的变量销毁,生存期才结束。
生存期(Extent)
生存期是变量可以访问的时间段,也就是从分配内存给它,到收回它的内存之间的时间。
- Java 对象所采用的内存超出了申请内存时所在的作用域,所以也就没有办法自动收回。所以 Java 采用的是自动内存管理机制,也就是垃圾回收技术。
程序运行过程中栈的变化
- 代码执行时进入和退出一个个作用域的过程,可以用栈来实现。每进入一个作用域,就往栈里压入一个数据结构,这个数据结构叫做栈桢(Stack Frame)。栈桢能够保存当前作用域的所有本地变量的值,当退出这个作用域的时候,这个栈桢就被弹出,里面的变量也就失效了。
- 变量的使用范围由作用域决定,作用域由词法规则决定,词法分析生成作用域链,之后查找变量就沿着这条作用域链查找,与函数调用栈就没有关系了。一般函数的生存期就是出栈后就结束了,如果是引用对象会在本次GC中回收,如果产生了闭包,那就要等到引用闭包的变量销毁,生存期才结束。
根据类型检查是在编译期还是在运行期进行的,我们可以把计算机语言分为两类:
静态类型语言(全部或者几乎全部的类型检查是在编译期进行的)。
动态类型语言(类型的检查是在运行期进行的)。
类型系统
高级语言为什么要增加类型这种机制呢?
类型是针对一组数值,以及在这组数值之上的一组操作。比如,对于数字类型,你可以对它进行加减乘除算术运算,对于字符串就不行。
- 类型是高级语言赋予的一种语义,有了类型这种机制,就相当于定了规矩,可以检查施加在数据上的操作是否合法。因此类型系统最大的好处,就是可以通过类型检查降低计算出错的概率。所以,现代计算机语言都会精心设计一个类型系统,而不是像汇编语言那样完全不区分类型。
根据类型检查是在编译期还是在运行期进行的,我们可以把计算机语言分为两类:
- 静态类型语言(全部或者几乎全部的类型检查是在编译期进行的)。
- 动态类型语言(类型的检查是在运行期进行的)。
静态类型和动态类型说的是什么时候检查的问题,强类型和弱类型说的是就算检查,也检查不出来,或者没法检查的问题。
面向对象的语义特征
从类型角度
- 类型处理是语义分析时的重要工作。现代计算机语言可以用自定义的类来声明变量,这是一个巨大的进步。因为早期的计算机语言只支持一些基础的数据类型,比如各种长短不一的整型和浮点型,像字符串这种我们编程时离不开的类型,往往是在基础数据类型上封装和抽象出来的。所以,我们要扩展语言的类型机制,让程序员可以创建自己的类型。
从作用域角度
- 首先是类的可见性。作为一种类型,它通常在整个程序的范围内都是可见的,可以用它声明变量。当然,一些像 Java 的语言,也能限制某些类型的使用范围,比如只能在某个命名空间内,或者在某个类内部。
- 对象的成员的作用域是怎样的呢?我们知道,对象的属性(“属性”这里指的是类的成员变量)可以在整个对象内部访问,无论在哪个位置声明。也就是说,对象属性的作用域是整个对象的内部,方法也是一样。这跟函数和块中的本地变量不一样,它们对声明顺序有要求,像 C 和 Java 这样的语言,在使用变量之前必须声明它。
从生存期的角度
- 对象的成员变量的生存期,一般跟对象的生存期是一样的。在创建对象的时候,就对所有成员变量做初始化,在销毁对象的时候,所有成员变量也随着一起销毁。当然,如果某个成员引用了从堆中申请的内存,这些内存需要手动释放,或者由垃圾收集机制释放。
- 但还有一些成员,不是与对象绑定的,而是与类型绑定的,比如 Java 中的静态成员。静态成员跟普通成员的区别,就是作用域和生存期不同,它的作用域是类型的所有对象实例,被所有实例共享。生存期是在任何一个对象实例创建之前就存在,在最后一个对象销毁之前不会消失。