初始化与清理
用构造器确保初始化,构造器
编程的安全问题是我们越来越关注的问题,如果没有及时清理不用的对象,回收内存资源,就容易出现内存溢出问题
- 因为每个对象的出现都需要初始化, 只有初始化后, 才能再内存中分配合适的资源,而Java中提供构造方法进行初始化。 调用构造器是编译器的责任。
- 构造器的名字问题很是困扰, 因为不能和成员的名称相冲突, 于是Java就默认用类名字为构造器取名字
- 关于构造器,只要知道无参构造方法和有参构造方法, 构造器前面说了是为了 创建对象, 再内存空间中分配大小的时候用的。现在要说的是代码中的关系: 再代码中, 无参构造, 是成员变量被赋予默认值, 如果我们再创建对象的时候增加参数, 再有参构造中进行赋值初始化成员变量, 这样就很省事,对于代码而言。
- 但我们如果只有有参构造方法, 就指能再创建对象的时候使用带有参数的, 比如:
- new Demo(a , b)这样的化, 有哪位你没有设置无参构造, 所以再创建对象的时候不能用 new Demo(). 不过构造方法可以重载。
- 还有如果没有写构造方法,系统默认使用无参构造, 但如果i你写了一个有参构造, 那么系统就不会默认给你赋予无参构造。
方法重载
- 关于重载, 不用可以这么记:之前提到过的方法签名的概念, 是指参数和方法名字构成一个独一无二的签名, 所以再调用的时候可以通过签名去调用, 所以只要你们签名, 也就是参数和方法名字不同时冲突就不会调用错, 这样的概念叫做方法的重载。
- 基本类型的重载, 传入的参数比重载类型的参数来得大就自动转型, 比如方法中参数类型为long 而我们的调用的方法,给的参数类型为int , 那么Java将会自动类型转换。
- 一个有返回值的方法, 一个没返回值的方法,参数相同, 可这两个方法区别也很大, 但是这种方式,再调用的时候,会使阅读代码的人难以判断, 于是就把返回值问题,丢给命名上面去入手。
为了副作用而调用: 调用没有返回值, 也就是没有返回数据的方法, 但是这个方法中会产生我们想要的影响, 这种影响也被业内称为副作用。
this关键字
- this 关键字的用法:
是为了处理变量名不好取, 容易冲突的问题, 然后用this进行代替, 另外调用本方法内的方法的时候不需要加类型,直接调用方法名即可, 所以不用this进行修饰。
再构造器中调用构造器, this(参数), 参数怎么加取决于你调用的是哪个构造方法。
static特点: 里面必须全部都是静态的,所以所以里面不能有通过new 这种动态创建对象, 所以也就不能用this了
清理:终结处理和垃圾回收
- finalize, 一般垃圾回收器会自动回收垃圾, 可是有一些垃圾不会被自动回收, 所以我们需要主动去回收它,finalize方法。
不会被自动回收的垃圾, 就是指不是用new进行再堆内存生成的对象,是说java可以通过调用本地方法进行调用非Java代码,调用c或者c++,但是c和c++可以调用其他代码, 所以 实际上可以调用任何代码,而c中是用malloc() 来分配空间,用free进行释放资源
- 里面对于主动回收, 其实是用finalize进行标记垃圾, 到时候垃圾回收器, 开始清理的时-候就会将其一起清理掉,也就是说如果没有面临着内存耗尽的情况就不会被回收。
c中有局部对象的概念, 局部对象是创建堆栈上面的, 而Java对象是创建再堆上的所以说, Java中是抛弃了局部对象的, 不允许出现局部变量。 在右花括号前面进行析构操作, 而Java是没有的, 所以可能出现有时候能没回收垃圾的情况,所以垃圾回收不能完全代替析构函数。
- 所以说,不论是终结,还是垃圾回收,都是不能保证一定会发生,
关于finalize的用途
- 虽然话是这么说, 但是finalize可以拿去验证终结的条件, 比如一个例子: 书本都要进行签入, 如果签入则用false表示, 所以在finalize方法中, 加入判断语句, 如果代表签入状态的属性表示为未签入, 那这个跟我们的需求不相符, 所以我们要进行回收, 而finalize, 再进行通知垃圾回收器。 另外, 可以在主函数末尾调用 System.gc()的方法, 这样的话, 可以直接进行垃圾回收。
对象可能不被垃圾回收
垃圾回收不等于c++中的析构。
- 垃圾回收器是如何工作的:
在说回收器是如何工作的时候我们得先了解一下,c的堆栈可以比喻成一个院子, 而Java的堆,可以比喻成一个传送带, 一个传送带比院子来得小, 但是传送带是傻瓜式的分配空余的空间, 一步一步地向前分配, 而院子也就c中,得我们手动进行分配。 于是, Java的效率在这里扳回一成, Java运行起来也就显得快了。
- 还有一件事: Java的堆并不完全像传送带,因为传送带频繁的调度,会让内存资源耗尽, 然而回收器会对对象重新排列成一个高速的可,有无限空间进行分配的堆模型
- 计数回收机制:当有引用连接对象的时候,引用计数加1,当引用离开作用域,或者为null的时候引用计数减1, 当引用计数为0的时候, 该对象就会被垃圾回收器回收。
以上是常被Java文档说的回收技术, 可是,没有一款的jvm虚拟机是用这技术进行回收, 嗯~ o( ̄▽ ̄)o 因为不够高大上。
- 遍历对象的所有层次的方式, 意思是说, 便利所有引用指向的对象, 排除掉指向引用的引用, 留下真正的对象。 应用这种方式记录对象的回收机制有一个叫做: 复制式的回收机制: 创建另外一个堆, 然后在本堆中,用遍历对象的所有层次的方式筛选出所有的对象, 然后进行复制到新的堆上面, 本堆中剩下的自然是垃圾, 于是再进行删除操作。
- 复制回收机制,很强大,这样剩下的垃圾肯定很少, 可是再于, 复制一个堆, 这样的操作很占内存, 然后我们在清理了很多垃圾,只剩很少垃圾的时候, 仅仅要做的是检查然后删除少量的垃圾, 这样再用复制回收就显得杀鸡用牛刀的感觉,所以Java采用的是自适应的机制, 再垃圾剩的很少的时候, 它会采用标志-清理, 原理是:会从堆栈和静态存储区出发,遍历所有引用,然后找出存活的对象, 然后给对象添加标志,再添加标志完成的时候, 就会开始清理未带有标志额对象。
- 关于停止-复制, 垃圾回收动作发生的同时,程序会被暂停,而进行清理操作。
- Java虚拟机是以块为分配的基本单位
- Just-In-Time JIT 编译器,这种技术是把程序的全部代码或者部分代码编译成机器码, Java虚拟机本来的工作就是这个, 当加载某个类,创建第一个对象的时候, 编译器会找到.class文件然后将对象的字节码文件用JIT编译,可以,如果全部拿去编译,会因为程序代码过于散落,而导致编译的时候会增加可执行代码的长度,且花费更多的时间, 于是可以采用有必要的时候进行编译的方式, 就是说不用执行的代码是i不会被编译的。
- Java程序最初是仅仅通过解释器解释执行的,即对字节码逐条解释执行,这种方式的执行速度相对会比较慢,尤其当某个方法或代码块运行的特别频繁时,这种方式的执行效率就显得很低。于是后来在虚拟机中引入了JIT编译器(即时编译器),当虚拟机发现某个方法或代码块运行特别频繁时,就会把这些代码认定为“Hot Spot Code”(热点代码),为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各层次的优化,完成这项任务的正是JIT编译器,JIT编译器位于虚拟机当中和解释器一起存在。
成员初始化
Java为什么没给局部变量进行添加初始化操作, 因为读者觉得,局部变量没有初始化,可能是程序员的疏忽,如果我们帮它进行初始化操作, 那么程序员可能拿不到所想要的值, 也就会造成了一点, 逻辑上的错误。
构造器初始化
- 关于构造器的初始化操作, 得知道, 成员变量的初始化会先于构造器的初始化,比如说,成员变量 Int a ; 会先被赋值为0, 然后再构造器中再进行赋值操作。
- 初始化顺序: 即使成员变量散落再方法定义的之间, 但它仍然会比任何方法之前初始化。变量定义的先后顺序将会决定变量的初始化顺序。
- 静态成员变量会再成员变量初始化之前进行初始化操作, 但是他们只会初始化操作一次。
- 即使构造函数没有显示地使用static关键字,实际上也是静态方法,虚拟机加载字节码文件,得以运行,所以dog类被加载时候必须在加载静态变量,且只会加载一次。
- 加载时候, 会将存储区域清零操作,就是说进行成员变量地初始化操作, 比如int类型编程0,然后再进行我们定义地初始化操作,
- 然后我们地构造器初始化地操作
- 代码块地初始化操作也会优先于构造器地初始化。
数组初始化
- 数组只是相同类型地对象序列用标识符封装再一起。
- Java会处理下标越界地问题, 而c语言只会默默地接受
- 可变参数列表,没什么好说地。。 object... xx,表示的是数组,这个只是形式不一样而已。然后里面还带有自动包装的机制,举例:如果是Integer... xxx传入的参数类型为int, 可以自动转化为原本Integer.
枚举类型
- 枚举类型,基本不用。知道有这个东西就够了:
- finalize, 一般垃圾回收器会自动回收垃圾, 可是有一些垃圾不会被自动回收, 所以我们需要主动去回收它,finalize方法。
public enum Spiciness{
NOT, MILD, MEDIUM, HOT, FLAMING
//上面是具名值,要用大写来代替
}
public class EnumOrder{
public static void main(String[] args){
for(Spiciness s: Spiciness.values())
System.out.println(s + ", ordinal "+s.ordinal())
}
}
另外,为什么一定要初始化呢, 因为如果不初始化的话, c语言中的变量默认是随机数。嗯~ o( ̄▽ ̄)o具体来说,变量是再栈上面进行分配的, 栈使用的是Ram, 掉电内容丢失, 内容丢失是指,内容会出现不稳定,随机变一个数,随机的数一般来说就是0,但有几率是其他数,所以这是一个不确定的数。