初始化和清理是涉及安全的两个问题。
1 构造器:
1.1 构造器与类同名,用来确保对象的初始化;
1.2 构造器分为默认无参构造和自定义构造(无参和有参都行),构造器支持重载,但是一旦自定义构造,系统自动放弃默认构造;
1.3 Java中对象的创建和初始化是捆绑一起的;
1.4 构造器内部可以使用this关键字调用构造器,但必须放在句首(所以只能调用一个);
2 重载
2.1 同一类中方法名相同但是参数列表不相同(包括参数顺序不同和参数不同),称为方法的重载;
2.2 如果传入的实际参数类型小于方法声明的形式参数类型,实际参数类型会被提升(向上造型);
如果传入的实际参数类型大于方法声明的形式参数类型,实际参数类型必须通过类型转换执行窄化转换(向下造 型);
3 清理(自适应GC):
3.1
a、对象可能不被垃圾回收;
b、垃圾回收不等于析构;
c、垃圾回收只和内存有关;
d、Java虚拟机(JVM)如果并未面对内存耗尽的情况,一般不会停止程序以执行垃圾回收恢复内存;
3.2 两种模式
和C++内存管理就像个院子,每个对象都有自己的地盘,对象销毁地盘重用。而java中堆的实现更像是传送带,每分配一个对象就往前移动一格。java的“堆指针”只是简单的移动到尚未分配的区域,比C++在堆栈上查找可用空间的开销小得多。
3.2.1 "停止-复制"模式:
思路:暂停程序运行,然后将所有存活对象复制到另一个堆里,当对象复制到新堆时,是一个挨着一个保持紧凑排列,重新修正所有引用,清理原来的堆。
弊端:
1、资源消耗,需要两个堆;跨堆交互,效率低下。解决:按需从堆中分配“块”,复制动作发生在这些内存块之间。
2、复制模式不能一直使用。程序稳定运行后,产生的碎片垃圾很少,此时依然采用复制模式对资源的效果就格外严重,所以需要“标记-清扫”模式。
3.2.2 "标记-清扫"模式:
思路:从静态区和栈出发,遍历所有引用,找到所有存活对象。每找到一个存活对象,给该对象一个标记。全部标记完成后,清理没有被标记的对象。因为不发生任何复制工作,所以剩下的堆空间是不连续的,垃圾回收器如果想要得到连续的堆空间,需要重新整理。
3.3 "自适应的、分代的、停止-复制、标记-清理式垃圾回收器"
思路:JVM中内存分配以较大的“块”为单位,垃圾回收器可以在回收期间向“废弃块”中拷贝对象。每个块都有自己的代数记录它是否还存活(这对大量临时对象的回收很有帮助),块被引用,代数增加。垃圾回收器会定期进行清理工作(大型对象/块不会被复制,代数会增加,小型对象的块会被复制并整理)。
JVM会进行监视,当对象都很稳定时,会切换到"标记-清扫"模式。同样,如果堆空间碎片增多,会切换为"停止-复制"模式。
个人认为块的存在时以部分冗余换取时间效率。
4 初始化:
4.1 成员初始化
4.1.1 存储
栈用来存储基本变量和引用。但是实例对象的成员变量(无论是基本类型还是引用类型)是存储如堆中的。
4.1.2 初始化
成员变量基本类型默认为0,false等,引用类型默认为null;
4.1.3 时序
变量的初始化时序在任何方法(包括构造,不包括静态方法)之前。
4.2 构造器初始化
4.2.1 目的
构造器初始化可以使用某些方法或动作来确定初值,比起定义的方式更加灵活。
4.2.2 时序
父类静态资源>子类静态资源>main方法>父类构造块>父类构造方法>子类构造块>子类构造方法
4.2.3 创建对象时序
阶段1:类加载{class文件定位>class文件加载(静态资源加载)}
阶段2:对象创建{分配存储空间>清空存储空间(默认初始化)>自定义变量初始化>对象构造器}
注意:静态资源不会主动创建,只有类加载或者对象创建的时候才会加载;
为什么清空储存空间就是默认初始化??
4.3 数组初始化
4.3.1 可变参数列表
JavaSE5之前:
Public static voidprintArray(Object[] objs){
for(Objecto:objs){
//操作
}
}
Public static void main(String[] args){
printArray(new Object[]{new Integer(12),new Float(3.14),new Double(15.22)});
printArray(new Object[]{new demo_2_1(),new Date()});
}
Object是顶级父类,通过对方法形参升级为顶级父类,保证了实际参数可以是多种类型。
JavaSE5之后:
Public static void printArray1(Object...objs){
for(Objecto:objs){
//操作
}
}
Public static void printArray2(int i,String...strs){}
Public static void main(String[] args){
printArray1(new Object[]{new Integer(12),new Float(3.14),new Double(15.22)});
printArray1(new Object[]{new demo_2_1(),new Date()});
printArray2(1,"a","b","c");
}
…代表多个个数,输出同样是提供一个类型化数组。
空类型也是合法的,如printArray1(),所以可变参数列表给重载带来了麻烦,一般编译器会使用自动包装机制匹配重载方法,但是重载方法的形参列表没有空列表的时候使用空列表方法,此时printArray()就不再合法,编译器就不知道该选择哪一个了。
5 枚举类型
枚举会将取值范围限制在拟定的范围中,适合和switch语句搭配。
//定义枚举
publicenumSpiciness{ONE,TWO,THREE,Four};
//如何使用枚举
publicstaticvoidmain(String[]args){
//想要使用枚举,需要创建一个枚举引用(本例为Spicinesss)接收枚举值
//编译器会自动为枚举类型添加方法:values()输出为依顺序的枚举值数组;ordinal()输出特定枚举值的次序
for(Spicinesss:Spiciness.values()){
System.out.println(s+"枚举顺序为"+s.ordinal());
}
}