随着计算机革命的发展,“不安全”的编程方式已经逐渐成为编程代价高昂的主因之一。html
初始化和清理,正是涉及安全的俩个问题。java
5.1 用构造器确保初始化编程
构造器名与类名相同,没有返回值数组
5.2 方法重载安全
构造器的重载与方法重载函数
5.2.1 区分重载的方法post
参数列表的不一样(不一样顺序也是重载,但不建议这么作)this
5.2.2 涉及基本类型的重载url
void print(char c){System.out.println("The character is "+c); }
void print(byte b){System.out.println("The byte is "+b);}
void print(short s){System.out.println("The short is "+s);}
void print(int i){System.out.println("The int is "+i);}
void print(long l){System.out.println("The long is "+l);}
void print(float f){System.out.println("The float is "+f);}
void print(double d){System.out.println("The double is "+d); }spa
1)若是直接传入数值,如 print(5)则5会被当作 int x = 5; print(x)去寻找最合适的print(int)
2)常量以外的类型会去寻找最合适的类型,没有同类型,可是有更大类型时会类型提高
3)char是一个特例,若是没有print(char)则char当作int,执行print(int)
4)须要用cast才能放入小类型的方法里
5.2.3 用返回值区分重载方法
void f(){}
int f(){return 1;}
这样是不能够的,由于不少时候咱们不须要返回值,只是调用而已,那么咱们会写:
f();这样是没法区分它是哪一个方法的
5.3 默认构造器
若是类里没有构造器,编译器会帮你弄一个。
有了就不会帮你造一个了。
5.4 this关键字
能够用于指代调用对象方法时的“当前对象”,能够解决参数和域重名问题。
5.4.1 构造器中调用构造器
this(参数);
必须放于第一行,且最多只能调用一个。
5.4.2 static的含义
static方法就是没有this的方法,它属于类,不属于对象。
5.5 清理:最终处理和垃圾回收
Java垃圾回收器负责回收由new建立而且再也不被使用的对象。
特殊状况是:若是一个对象以非new方式得到了一块内存区域,GC就不知道怎么回收这块特殊的内存。Java提供了finalize()方法,它的工做原理“假定”是这样的:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用finalize()方法, 而且在下一次垃圾回收动做发生时,才会真正回收对象内存。因此finalize()方法能够在垃圾回收时作一些重要的清理工做。
//此处疑惑感谢@Launcher的细心解答。
//---------------解答开始
也就是你有这样一个 class,伪码:
class A{
IntPtr m_pBuffer;
A(){
m_pBuffer = malloc(1000);
}
void finalize(){
free(_M_pBuffer);
}
}
A 对象没有用 new ,而是使用 malloc 得到了一块特殊的内存区 m_pBuffer。
A a = new A(); // 仍是使用 new 得到对象 A
可是对象 a 自己不用 new 得到的特殊内存区 m_pBuffer,不会被 GC 释放,因此你须要实现 finalize,在里面手动释放。
//---------------解答结束
这里有一个误区:
finalize() 与 C++中的析构函数(销毁对象时必需要用到这个函数)相同。
这是不正确的,C++中对象必定会被销毁,即析构函数必定被调用,而Java里的对象并不老是被垃圾回收。换句话说:
1)对象可能不被垃圾回收
2)垃圾回收不等于析构
5.5.1 finalize()用途何在
3)垃圾回收只与内存有关。
Java中不管对象是如何建立的,GC都会回收它的内存,对finalize()的须要限制到一种特殊状况:用了建立对象之外的方式,为对象分配了存储空间。
之因此要有finalize(),是因为在分配内存时可能采用了相似C语言的作法,这种状况主要发生在使用Java调用本地代码,如用C的malloc()函数系列来分配存储空间,因此须要在finalize()里调用本地代码,使用free()来释放这些内存。
因此不要过多使用finalize(),它不是进行普通清理工做的合适场所。
5.5.2 你必须实施清理
C++中全部对象都会被销毁。包括局部对象(在栈上建立的)和用new建立的对象(delete调用析构函数)。
Java不容许建立局部对象,必须使用new建立,也没有delete可用,释放对象内存只能由GC来完成,可是它并不保证必定去回收。若是但愿在进行释放存储空间以外的清理操做,得明确在finalize()里使用,这等于析构了,可是没析构方便。
若是JVM没有面临内存耗尽的状况,它是不会浪费时间和资源去执行垃圾回收以恢复内存的。
5.5.3 终结条件
由于finalize()方法是在垃圾回收前执行的,因此复写finalize方法能够检测"逻辑上"的这个对象是否应该被终结。借此来发现程序中是否有缺陷。如:某对象表明了一个打开着的文件,而咱们规定这种对象回收时必须为关闭状态。为了检测程序是否有缺陷,咱们能够复写finalize方法检测是否打开状态,而后在某次finalize中查看是否存在问题,虽然finalize并不必定会调用,可是只要调用了一次咱们就知道是否有问题,这才是关键。
咱们能够利用System.gc()来建议作GC操做,但JVM有可能不听咱们的。
5.5.4 垃圾回收器如何工做
在之前所用过的程序语言中,在堆上分配对象的代价十分高昂,所以容易有JAVA中的对象在堆上分配方式也很是高昂。但实际上垃圾回收器对提升对象建立的速度有很是明显的效果。Java中堆内存的分配方式就像传送带同样,经过移动“堆指针”来完成内存分配,而其中最关键的是垃圾回收器在工做时,一边回收空间,一边使堆中对象紧密排列,从而保证了“堆指针”能够很容易分配内存,实现了高速的无限空间的可供分配堆模型。
先看其余系统的垃圾回收机制模型:
1.引用计数,简单但很慢的垃圾回收技术。且难以处理循环引用问题。
2.更快的模式,从堆栈和静态存储区开始,用引用向下搜索全部对象,以及对象间引用,保证每一个活的对象都被搜索到且避免了循环引用问题。
1和2都是寻找“活”着对象的方式,在基于2的方式之下,Java虚拟机采用“自适应”技术。
如何处理寻找到的存活对象,取决于不一样Java虚拟机的实现。
A.中止-复制 stop-and-copy, 暂停程序的运行,而后将全部存活的对象从当前堆复制到新的堆里,而且紧密排列,而后就能够简单直接的分配新空间了。当把对象从一处搬到另外一处时,全部指向它的那些引用都必须修正。问题一:对于这种“复制式”回收器而言,效率会下降,须要在2个堆里倒腾,还要维护比实际须要多一倍的空间。某些Java虚拟机的解决方法是,按需从堆里分配几块较大的内存,复制动做发生在这些大块内存以前。问题二:对于复制,程序进入稳定状态以后,只会产生少许垃圾,甚至没有垃圾,尽管如此,复制式回收器仍然会把全部内存从一处复制到另外一处,很浪费。一些Java虚拟机会进行检查:要是没有新垃圾产生,就会转换到另外一种工做模式这种模式叫 标记-清扫。通常用途而言,标记清扫的效率很低,可是若是垃圾不多甚至没有,它的速度就很快了。
B.标记清扫 mark-and-clean, 所依据的思路一样是从堆栈和静态存储区出发,遍历全部引用,进而找出全部存活对象。每当它找到一个存活对象,就会给这个对象一个标记,这个过程不会回收任何对象。只有当标记工做所有完成以后,清理动做才会开始。清理过程当中没有标记的对象将被释放,不会发生任何复制动做。因此剩下的堆空间是不连续的,垃圾回收器但愿获得连续空间的话就得从新整理剩下的对象。
这里讨论内存分配以较大的“块”为单位的Java虚拟机。严格意义来讲,中止-复制是要求释放垃圾对象以前,必须把对象从一个堆复制到新堆里的,这将致使大量的内存复制。此处有了块以后,垃圾回收器就能够往“废弃”的块里复制对象了,每一个块有本身的代数记录它是否存活(废弃)。若是块在某处被引用,代数增长,垃圾回收器会对上次回收动做以后新分配的块进行整理。这对处理大量短命的临时对象颇有帮助。垃圾回收器会按期进行完整的清理动做-大型对象仍然不会被复制,内含小型对象的那些块则被复制并整理。Java虚拟机会进行监视,若是全部对象都很稳定,垃圾回收器效率很低的话,就会切换到“标记-清扫”方式;一样虚拟机会跟踪“标记-清扫”的效果,要是堆空间出现不少碎片,就会切换回“中止-复制”方式。这就是自适应技术。
Java虚拟机还有不少其余用于提高速度的附加技术。尤为是与加载器操做有关的,被称为“即时Just-In-Time,JIT”编译器技术。这种技术能够把程序部分或所有翻译成本地机器码(这原本是Java虚拟机的工做),程序运行速度所以得以提高。当须要装载某个类时,编译器会先找到class文件,而后把字节码装入内存。此时有两种方案可供选择,一是让即时编译器编译全部代码,缺点是这种加载动做散落在整个程序生命周期内,累加起来要花费更多时间;而且会增长可执行代码长度,致使页面调度,从而下降程序运行速度。二是采用惰性评估,执行频繁的方法和代码会采用即时编译,执行次数越多速度越快。
5.6 成员初始化
Java尽力保证:全部变量在使用前都获得恰当的初始化。对于方法中的局部变量,以编译错误形式贯彻这种保证。对类成员以默认值执行初始化。
5.6.1 指定初始化
能够在申明变量时直接以赋值符号完成初始化。在针对类成员变量初始化时,若是直接给出值,会使得全部对象有相同的值,有时是咱们但愿的,但有时显得不够灵活。
5.7 构造器初始化
利用构造器来初始化能够获得更大的灵活性。但要牢记:没法阻止自动初始化的进行,它将在构造器被调用前发生。在构造器赋值成员变量以前,变量已经被赋予了默认值。----编译器不会强制咱们在构造器初始化,由于初始化早已获得了保证。
5.7.1 初始化顺序
变量定义的顺序决定了初始化顺序。即便变量可能定义在构造器或其余方法以后,但仍旧会在任何方法(包括构造器)被调用以前获得初始化。
5.7.2 静态数据的初始化
动做执行于类加载时,且只执行一次。总结对象建立的过程,假设有个Dog类
1.构造器是静态方法,只是没有static。因此当首次调用构造器,或首次使用Dog类的静态域或方法时,Java解释器必须找到类路径,定位Dog.class文件。
2.而后载入class文件,有关静态初始化的全部动做都会被执行。
3.当用new Dog()建立对象的时候,首先将堆上为Dog对象分配足够的存储空间。
4.这块存储空间会被清零,这就自动的将Dog对象中全部基本类型设置为默认值0和false,引用类型设置为null
5.按申明顺序执行全部出现于字段定义处的初始化动做。
6.执行构造器。
5.7.3 显式的静态初始化
即在static块里完成初始化
5.7.4 非静态实例的初始化
即无static的块里完成初始化
总结:
5.8 数组初始化
int[] a1;
如何初始化?
1. Integer[] a1 = {1,2,3};//只能在申明处用这种方式初始化
2. Integer[] a1 = new Integer[]{1,2,3};
3. Integer[] a1;
a1 = a2;
能够用赋值让2个数组引用指向同一个数组对象。因此容许存在Integer[] a1;这种只有引用没有指向的状况
注意:全部数组都有一个固定成员,能够经过它获取数组里包含了多少元素,可是不能修改它。这个成员就是length,咱们只能访问0-(length-1)范围,下标检查是自动进行的,相比于C和C++用这些开销来得到安全是值得的。
5.8.1 可变参数列表
在参数列表的末尾能够选择传入0-N个参数,会自动变成数组,能够做为可选参数存在
5.9 枚举类型
Java SE5开始添加了enum关键字用于建立枚举类型。功能比C、C++完善得多。
public enum Spiciness{
NOT, MILD, MEDIUM, HOT, FLAMING
}
示例建立了一个名为Spiciness的枚举类型,它有5个常量实例。
使用时:
Spiciness howHot = Spiciness.MEDIUM;
System.out.println(howHot);
--> MEDIUM
当建立了enum时,编译器会自动添加一些有用的特性:
1.toString()用于显示实例名字
2.ordinal()显示该实例常量声明顺序,从0开始
3.静态的values() 方法,按照声明顺序产生一个全值数组
注意:
A.enum自己并非新的数据类型,它属于编译器行为,本质上仍是类,有本身的方法
用类分析器分析后:
public final class study.core.反射.Spiciness extends java.lang.Enum{ //域 public static final study.core.反射.Spiciness NOT; public static final study.core.反射.Spiciness MILD; public static final study.core.反射.Spiciness MEDIUM; public static final study.core.反射.Spiciness HOT; public static final study.core.反射.Spiciness FLAMING; private static final study.core.反射.Spiciness[] ENUM$VALUES; //构造器 private Spiciness(java.lang.String, int); //方法 public static study.core.反射.Spiciness[] values( ); public static study.core.反射.Spiciness valueOf(java.lang.String);}B.enum能够配合switch使用