目录
2 静态与动态类型检查(Static/dynamic data type checking)
3 可变与不可变数据类型(Mutability/Immutability)
1 数据类型
在Java中,主要包括两种数据类型:基本数据类型(primitive types)和对象数据类型(object types)。
- 基本数据类型:包括int,long,byte,char,float,double,boolean。这些类型的数据,只有值,没有标识id,并且为immutable类型。
- 对象数据类型:包括Class ,interfaces ,arrays ,enums ,annotations。这些类型的数据,有值,并且有标识id,其中部分为mutable类型。
为了更清楚的阐述这两种类型,结合 个人理解 做出一些解释:为什么说基本数据类型是可变的,且无id的?如果我们申请一个int型变量a,赋予初值为7。再申请一个int型变量b,赋予初值也为7。Java在处理时,a申请时,java在栈中搜索是否有内存存储了整型的7,如果没有,便申请一块内存来存储整型7。在处理b的声明时,发现存在整型7,则直接将b指向对应内存的位置。因此,a和b实际上指向同一块内存。但是又因为int型是immutable的,因此,改变a的值,使得a=6,实际上是让a指向了内存存储整型6的地方(如果没有则申请一块新的位置),并不是修改原内存空间的值。b仍然指向之前的地址,而a指向了一块新的地址。这种行为实际上和immutable的对象数据类型很相似。我认为,不管是对象数据类型和基本数据类型,变量名称都与C语言的指针相似。
在容器类中,要求存储的元素必须为Object,因此,当我们想在容器类中存储基本数据类型时,就需要对其进行转化,将其转化为对象数据类型,即:Boolean, Integer, Short, Long, Character, Float, Double型,这些与Object相同,有id,且为immutable类型。Java有时候会自动的进行转换。如:
List<Integer> list = new ArrayList<>();
list.add(1);
Java会自动将1转化为Integer型。
2 静态与动态类型检查(Static/dynamic data type checking)
2.1 静态检查
Java是一种静态类型语言,即:所有变量的类型在编译时已知,因此编译器可以推导表达式类型。显然,在Java中申请变量时,都需要对变量的类型进行声明,这与Python这种动态语言大不相同。静态检查是在编译阶段进行的类型检查。主要包括:
- 语法错误Syntax errors
- 函数名错误Wrong names
- 参数错误Wrong number of arguments
- 参数类型错误Wrong argument types
- 返回值类型错误Wrong return types
从静态检查的主要内容中可以看出,静态检查主要考察考虑特点类型,名称,不考虑特定值。
2.2 动态检查
Java虽然不是一种动态类型语言,但是同样会进行动态类型检查。动态类型检查主要包括:
- 非法的参数值Illegal arguments values
- 非法的返回值Unrepresentable return values
- 越界Out-of-range indexes
- 空指针Calling a method on a null object reference
其中非法是指数值不合法,如除以0等等。动态类型检查是考虑程序在运行中特定值引起的问题。这种由特定值引起的错误无法被静态检查发现(编译器不能确定在程序运行的过程中,这些变量的取值)。
3 可变与不可变数据类型(Mutability/Immutability)
3.1 改变变量
改变一个变量,是指改变变量所指向的存储空间,类比于C语言,可以理解为等于改变指针指向。
3.2 改变值
改变一个变量的值:是指改变该变量当前指向空间的值,类比于C语言,相当于改变指针指向空间的内容。
3.3 不可变类型
对于非引用类型,不可变类型:一旦创建值不能再被改变
对于引用类型,不可变类型:一旦确定指向对象,不能再改变指向其他对象
方法:使用关键词final修饰
注意:
- fina l类 无法派生子类
- final 变量 无法改变引用
- final 方法 无法被子类重写
例如:String就是一个Immutable的对象,给String增加字符时,实际上是创建了一个新的String,并修改原String指向新生成的String
图1 String是不可变类型
编译器进行静态类型检查时,如判断final变量首次赋值后发生了改 变,就会提示错误。
3.4 防御性拷贝(Defensive Copying)
对于可变类型,因为其值可以被改变,因此在方法中通常需要进行防御性拷贝,防止信息泄露,导致在外部修改内部属性。主要包括:
- 构造函数时进行防御性拷贝
- 返回值时进行防御性拷贝
前者是为了防止在传入时,外部修改导致内部变化。后者是为了防止client修改导致内部改变以及内部修改导致client出现错误。
4 代码快照
基本类型:
可变引用的(无final修饰):
图2 基本类型-可变引用
不可变引用的(有final修饰):
图3 基本类型-不可变引用
对象类型:
可变引用的(无final修饰),Mutable:
图4 Object-non final-Mutable
可变引用的(无final修饰),Immutable:
图5 Object-non final-Immutable
不可变引用的(final修饰),Mutable:
图6 Object-final-Mutable
不可变引用的(final修饰),Immutable:
图7 Object-final-Immutable
5 知识框图