分为基本数据类型和对象数据类型两种,根据Java的约定,基本类型是小写开头,而对象类型以大写字母开头
Java中的内存管理机制:- 栈:栈是一片内存区域,存储的是局部变量,凡是定义在方法中的都是局部变量(方法外的是全局变量);for循环内部定义的也是局部变量;只有先加载函数才能进行局部变量的定义,所以方法先进栈,然后再定义变量,变量有自己的作用域,一旦离开作用域,变量就会被释放。
- 堆:存储的是数组和对象(数组也是对象),凡是new建立的都是在堆中,堆中存放的都是实体(对象),实体用于封装数据,而且是封装多个(实体的多个属性),如果一个数据消失,这个实体也没有消失,还可以用,所以堆是不会随时释放的。虽然不会被释放,但是如果没有指针指向某个对象,它会被当成垃圾,由垃圾回收机制不定时的收取。
- 垃圾回收机制:Java中有独有的垃圾回收机制,可以回收不再被引用的对象。
子父类的继承关系,最大的父类是Object类
Boxed primitives:-
将基本数据类型包装成对象数据类型,: Boolean, Integer, Short, Long, Character, Float, Double
-
通常是在定义容器类型的时候使用它们(容器类型 *** 作的元素要求是对象类型,所以需要对基本数据类型进行包装,转换为对象类型)如List,Map,Set等
-
一般情况下可以和对应的基本数据类型自动转换,但会降低性能,尽量避免使用
- +,-,*,/,()……
- 字符串可以通过+相连
- 基本数据类型直接使用运算符;对象可以通过调用相应方法来使用运算符;某些静态方法也可以执行运算(如Math库中的方法)
静态类型语言:所有变量的类型在编译时已知,编译器可以推导表达式类型,可以在编译阶段进行类型检查
动态类型语言:变量类型在编译时未知或不需要知道,在运行阶段进行类型检查
一种语言可以提供3种检查:
-
静态检查:在编译之前进行检查,包括:语法错误、类名/函数名错误、参数数目错误、参数类型错误、返回值类型错误等。针对动态类型的语言会检查除类型以外的其他语法错误,主要关于“类型”的检查(如a是int,b是int,则得出a/b也是int)
-
动态检查:在运行过程中进行检查,检查非法的参数值,非法的返回值、越界、空指针等。主要关于“值”的检查(接上例,如a = 1,b = 0,执行时发现除0错误)
-
无检查:不进行检查
静态检查>动态检查>无检查
有些问题静态检查和动态检查都无法检测出来,如整数除法(截断整数)、整数溢出、浮点数的特殊类型:NaN,POSITIVE_INFINITY,NEGATIVE_INFINITY
4.3易变性和不变性改变一个变量:将变量指向另一个存储空间
改变一个变量的值:将该变量当前指向的存储空间写入一个新值
4.3.1不变性(重要设计原则)数据类型一旦被创建,其值不能改变;引用类型一旦确定其指向的对象,不能再给变其指向其他对象
Java中使用关键字“final”来标记:
- final类无法派生自己的子类
- final变量无法改变值/引用
- final方法无法被子类重写
编译器进行静态类型检查时,如判断final变量首次赋值后发生了改变,会提示错误。
尽量使用final变量作为方法的输入参数,作为局部变量
4.3.2 可变性不变对象:一旦被创建,始终指向同一个值/引用
可变对象:拥有方法可以修改自己的值/引用
eg: String是不可变类型,StringBuilder是可变数据类型
4.3.3 易变性与不变性二者区别:
当只有一个引用指向该对象时,二者没有区别;当有多个引用的时候,有差异
优缺点:
-
使用不可变类型,对齐频繁修改会产生大量的临时拷贝,性能较差,但更“安全”,在其他质量指标上表现更好;
-
可变数据类型最小化拷贝,可以提高效率,性能更高,也适合在多个模块之间共享数据。但可变性使得难以理解程序正在做什么,更难满足方法的规约。
-
所以,折中,具体情况具体分析。
-
传递可变对象:传递可变对象是一个潜在的错误源泉,一旦被无意中改变,则这种错误非常难于跟踪和发现
eg:在列表示例中,列表(sum和sumAbsolute)和myData(main)指向相同的列表。一位程序员(sumAbsolute的)认为可以修改列表;另一位程序员(main的)希望列表保持不变。因为调用顺序和别名使用,main的程序员会输。
-
返回可变值:由于值可变,则可以在外部对值进行修改,导致内部引用该值的指针的值也发生变化,这样就有可能在其他地方出错。
eg:在Date示例中,,groundhogAnswer和partyDate两个变量名都指向Date对象。这些别名位于代码的完全不同部分,由不同的程序员控制,他们可能不知道另一个在做什么。
- 进行防御式拷贝(但由于大部分时候该拷贝不会被修改,可能造成大量的内存浪费)
- 使用不可变类型,防止被修改,此时也不需要防御式拷贝
一个类可能包含一些方法,使得自己的内部参数被改变,这样调用者可以轻而易举的破坏掉封装,危害很大,这种时候就需要防御性拷贝。
防御性拷贝的关键就在于不把原本类中的对象提供给调用者,而是创建一个跟封装的类中相同的对象返回给调用者,这样,你对这个参数进行修改的时候跟封装类内部的相关参数无关,也就不会改变类中的参数。这就是防御性拷贝。
别名使可变类型具有风险,如果在一个方法中完全局部地使用可变对象,并且只对该对象进行一次引用,那么可以使用可变对象。如果有多个引用(别名),使用可变类型就会变得非常不安全
4.4代码级、运行时和时刻视图的快照图我们使用快照图表示程序在运行时的内部状态——其堆栈(正在进行的方法及其局部变量)和堆(当前存在的对象),来理解程序现在的运行状况。
快找图的优点:
- 便于程序员之间的交流
- 便于刻画各类变量随时间变化
- 便于解释设计思路
- 便于为后续课程中更丰富的设计符号铺平道路
基本数据类型:
-
基本数据类型由裸常量表示,传入箭头是对变量或对象字段中的值的引用。
对象数据类型:
-
一个对象数据类型是按其类型标记的圆,在里面写下字段名,用箭头指向它们的值。要了解更多详细信息,字段可以包括其声明的类型。
-
不可变对象用双线椭圆表示,区别于可变对象
-
可变和不可变的引用:后者用双线箭头表示
对可变值(例如:final StringBuilder sb)的不可变引用,即使我们指向同一个对象,其值也可以更改。
对不可变值(如String s)的可变引用,其中变量的值也可以更改,因为它可以重新指向不同的对象。
4.5 复杂数据类型:数组和集合 数组和列表数组是一种类型为T的固定长度序列。分为定长数组和可变数组两种。定长数组就是普通的数组,变长数组用List进行表示,list是一个接口,保存的是对象。
int [] a = new int [3];//长度为3的定长数组
List<Integer> list = new Arraylist<Integer>();//变长数组
数组的遍历:
int max = 0;
for(int i = 0;i < arrry.lenth;i++)
{
max = Math.max(array[i],max);
}
for(int x:list)
{
max = Math.max(x,max);
}
list有两个实现类:ArrayList和LinkedList,分别代表数组和链表
Set集合:无序、不可重复,与list有区别
方法:
s.contains(e);
s.containsAll(s2);
s.removeAll(s2)
Map
字典数据类型,将两组对象匹配(映射)
方法:
map.put(key,a1);//建立映射key->a1
map.get(key);//得到key对应的值
map.containsKey(key)//判断是否含有key
map.remove(key)//删除某个匹配
容器
Java 中容器框架的内容可以分为三层: 接口(模型), 模板和具体实现。
在开发中使用容器正常的流程是,首先根据需求确定使用何种容器模型,然后选择一个符合性能要求的容器实现类或者自己实现一个容器类。
list,set,map都是容器中的接口,都需要具体的实现类,但需要指定类型,不可混用,如list的实现类有arraylist和linkedlist;set的实现类有hashset;map的实现类hashmap。
接口- 它们定义了这些类型的工作方式,但不提供实现代码
- 优点:用户有权在不同情况下选择不同的实现
迭代器是一个对象,遍历一组元素并逐个返回元素。
for(A:B)形式的遍历,调用的是被遍历对象所实现的迭代器。
一个迭代器有两个方法:next():返回集合中下一个元素 ,hasnext():判断迭代器是否到达集合末尾。
迭代器的例子自己定义的一个迭代器
使用自己写的迭代器遍历数组,并删除以6开头的课程,结果出错
分析快照图可知,出错原因在于在删除第一个元素之后,index和迭代器同时+1,漏掉了中间的第二个元素。
如果使用java自带的迭代器删除会报错:
因此,推荐使用的方法是使用迭代器的删除 *** 作。
在迭代器遍历集合过程中,不可以使用集合的方法来增删元素,这里使用的subjects.remove()就是集合的方法,会破坏迭代器的结构从而抛出ConcurrentModificationException异常,应该使用迭代器的remove()方法来删除next()获取的元素,即iter.remove()
4.6有用的不可变数据类型基本类型及其封装类型都不可变,日期类型Date是可变的
Java集合类型(列表、集合、映射)的常见实现(arraylist、hashmap)都是可变的,也必须可变,因为需要进行元素的增删改等功能,但是如果需要在两种方法之间传递整个集合,这可以使用防御式拷贝(缺点是造成空间的浪费),或者下面的方法:
不可修改的包装Collections实用程序类具有获取这些可变集合的不可修改视图的方法
这种包装器的结果是不可变的,不能修改,否则会抛出异常,但是这种“不可变”是在进行阶段获得的,编译阶段无法进行静态检查。
不可修改的包装器通过截获所有将修改集合的 *** 作并抛出UnsupportedOperationException,剥夺了修改集合的能力
“2”的意思是指如果去掉那一句,则listCopy的大小会变为2。这是因为listCopy和list指向的是同一块内存空间。所以在使用不可变的包装时要防止用户绕过listcopy直接对list进行 *** 作,同时要保证不可变的对象一旦被创建就不可修改。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)