初始化和清理
1.this关键字
如果有同一个类型的两个对象a和b,如何才能让这两个对象同时调用peel方法呢:
如:
Banana a = new Banana();
Banana b = new Banana;
a.peel(1);
b.peel(2);
如果只有一个peel方法,它如何知道是被a还是b调用呢?
为了能用简便,面向对象的语法来编写代码,即"发送消息给对象";编译器做了一些幕后工作,他暗自把“所操作对象的引用”作为第一个参数传递给了peel->所以上述两个方法的调用变成了这样:a.peel(a,1),b.peel(b,1);这是内部的表示形式;虽然我们不能这样书写代码,并试图通过编译,不过这种写法的确能帮你了解实际发生的事情。
如果你希望在方法内部获得对当前对象的引用,由于这个引用是编译器“偷偷”传入的,没有什么标识符可用;但为此有一个专门的关键字:this;this关键字只能在方法内部使用,表示“对调用方法的那个对象”的引用;
当前方法的this引用会自动引用于同一类的其他方法->
只有当需要明确指出对当前对象引用的时候,才需要使用this关键字;如需要返回当前对象的引用的时候,就常常在return语句里这样写:
return this;
注:有人执意将this放在每一个方法调用和字段引用前,认为这样“更清晰更明确”。但是千万别这么做,我们使用高级语言的原因之一就是它帮我们做一些事情。要是把this放在一些没有必要的地方,就会使读你程序的人不知所措,因为别人写的代码不会到处用this。遵循一种一致而直观的编程风格能节省时间和金钱.
1.通过this返回对当前对象的引用,所以很容易在一条语句中对同一个对象执行多种操作;
2.一些必要的地方才需要调用this
3.构造器中调用构造器,this添加参数列表-产生对符合此列表的某个构造器的明确调用(除构造器之外,编译器禁止在其他任何方法调用构造器)
4.解决参数名称和数据成员名称一致,数据成员采用this.的形式引用
5.static方法就是没有this的方法,在static方法的内部不能调用非静态方法(这不是完全不可能,如果你传递一个对象的引用到静态方法里(静态方法可以创建其自身的对象),然后通过这个引用(和this效果相同)就可以调用非静态方法和访问非静态数据成员了。但通常要达到这样的效果,你只需写一个非静态方法即可);反过来可以;可以在没有创建对象的前提下;仅仅通过类本身调用static方法,这实际上正是static方法的主要用途;
有些人认为static不是面向对象的,因为它的确具有全局函数的语义;使用static方法时,由于不存在this,所以不是通过“向对象发送消息”的方式来完成的;的确,要是在代码中出现了大量的static方法,就该重新考虑一下自己的设计了;然而static的概念有其实用之处,许多时候都用到它;对于它是否真的面向对象,留给理论家去讨论吧;
2.终结清理和垃圾回收
1.对象可能不被垃圾回收
2.垃圾回收并不等于析构
3.垃圾回收只与内存有关
垃圾回收本身也有开销
finalize:
假定工作原理:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用finalize方法,并且在下一次垃圾回收动作发生时,才会真正的回收对象占用的内存;【不过要是“垃圾回收”没有发生的话,则无法释放;这个策略是恰当的,只要程序没有濒临存储空间用完的一刻;对象占用的空间总也得不到释放;因为垃圾回收也是有开销的】
通过某种创建对象方式以外的方式为对象分配了存储空间:是由于在分配内存的时候可能使用了C语言的做法,这种做法主要发生在使用“本地”方法的情况下;本地方法是一种在Java中调用非Java代码的方式。本地方法目前只支持C/C++,但他们可以调用其他语言写的代码,所以实际上可以调用任何代码;非Java代码中,也许会调用类似C的的malloc函数系列来分配存储空间,而且除非调用了free函数,否则存储空间将得不到释放,从而造成内存泄露;当然free是C/C++的函数,所以要在finalize中用本地方法调用它;
【记住:无论是垃圾回收还是终结,都不保证一定会发生。如果Java虚拟(jvm)并未面临内存耗尽的情况,它是不会浪费时间去执行垃圾回收以恢复内存的】
3.Java堆模型
1.一些java虚拟机的堆实现中,更像一个传送带,每分配一个新对象,就往前移动一格;对象存储空间的分配速度非常快,Java的“堆”指针只是简单的移动到尚未分配的区域;(C++的堆像一个院子,每个对象都负责管理自己的地盘,一段时间后,对象可能被销毁,但地盘必须加以重用);不过Java的堆并不完全像传送带那样工作,否则会导致频繁的内存页面调度,会显著影响性能;而垃圾回收介入之后,它工作时,一面回收空间,一面使队中的对象紧凑排列,这样“堆指针”就可以很容易移动到更靠近传送带的开始处;也就尽量避免了页面错误->通过垃圾回收器对对象重新排列,实现了一种告诉的,有无限空间可供分配的堆模型.
4.垃圾回收机制:
1.引用计数:垃圾回收器在含有全部对象的列表遍历,发现某个对象的引用计数为0,就是释放其占用的空间;不过对于对象之间的循环引用,则会出现“对象应该被回收,不过引用计数不为0”;对于垃圾回收器而言,定位这样的交叉引用的对象组所需的工作量极大;
2.更快的模式:对任何“活”的对象,一定能最终追溯到其存活在堆栈或静态存储区之中的引用;因此,如果从堆栈和静态存储区开始,遍历所有的引用,就能找到所有活着的对象;
停止-复制
标记-清扫
两种方式程序将暂停
自适应的,分代的,停止-复制,标记-清扫式垃圾回收器
3.java虚拟机许多附加技术->JIT(just in time)编译器技术->
新版JDK的Java HotSpot技术
6.初始化
1.方法局部变量,编译时错误保证;【当然编译器可以为局部变量赋一个默认值,不过未初始化的局部变量更有可能是程序员的疏忽,所以采用默认值反而会掩盖这种失误,因此强制程序员提供一个初始值,往往可以找到程序中的bug】
2.类的内部,变量定义的先后顺序决定了初始化的顺序;会在任何方法(包括构造器)被调用之前得到初始化
3.静态数据初始化,无论创建多少个对象,静态数据都只占用一份存储区域->静态初始化只有在必要时才进行->
4.构造器可以看成是静态方法;首次创建类对象/访问类的静态方法,静态域->载入类.class->静态初始化,只在class加载的时候初始化一次->new,在堆上为该对象分配足够的存储空间->这块存储空间请0,所有基本类型被置默认值,引用null->执行所有出现于字段定义处的初始化动作->执行构造器->
5.static{},静态子句;{},实例初始化子句;
6.数组初始化int[]array= {};int[] array = new int[]{}->前者只能用在数组定义处,而后者可以传参
可变参数列表,如int...args,String...args;可变参数列表使重载过程变的复杂了->如f(Integer...args);f(Long...args)->当调用f()的时候则编译出错,因为不知道该调用哪个f方法->可以给每个方法都就加上一个非可变参数->你应该总是只在方法的一个版本上使用可变参数列表,或者压根就不是用它;
7.enum
枚举是类,且有自己的方法;enum的名字能够倍加清楚的表明程序意欲何为的;创建enum时,编译器会自动添加一些有用的特性,如创建toString方法,很方便的显示每个enum实例的名字;还会创建ordinal方法,表示某个特定enum常量的声明顺序;static values方法,用来按照enum常量的声明顺序,产生由这些常量构成的数组;与switch是绝佳的组合;
直接将所得到的类型直接拿来使用,而不必过多的考虑,将enum用作另一种创建数据类型的方式;
在枚举之前,我们需要创建一个整型常量集,但是这些常量值并不会将其自身的取值限制在这个常量集的范围内,因此显的更有风险; 更难以使用;
当然以前这样的枚举安全机制:(个人认为还是可以满足需求的,只不过没有一些编译器添加的有用的特性,而且序列化的时候会有问题)
public class Oriented
{
private int value;
//两个安全非final枚举
public static final Oriented ORI = new Oriented(1);
public static final Oriented OR2 = new Oriendted(2);
//私有构造函数
private Oriented(int v)
{
value = v;
}
}
8.垃圾回收器的确增加了运行时的开销,而且Java解释器从来就很慢;随着时间推移,java在性能方面已经取得了长足的进步,不过速度问题仍然是它涉及某些编程领域的障碍.
9.部分源码
package com.book.chap5.initAndDestroy;
/** *//**
*
*Finalize用法,验证终结条件
*<p>本例的终结条件是:所有的book对象在垃圾回收都应该checkin;main方法中,有一本书没有被签入,被finalize方法发现,输出消息</p>
*
*@author landon
*@since JDK1.6
*@version 1.0 2012-4-12
*
*/
public class FinalizeUsage
{
public static void main(Stringargs)
{
Book novel = new Book(true);
novel.checkIn();
new Book(true);
//强制进行终结动作[当然也可以通过重复的执行程序,假设程序将要分配大量的存储空间,而导致垃圾回收动作的进行,最终也能找到错误的Book]
System.gc();
}
}
class Book
{
private boolean checkOut;
public Book(boolean checkOut)
{
this.checkOut = checkOut;
}
/** *//**
* 检入,书籍只有检入后才可以被垃圾回收
*/
public void checkIn()
{
checkOut = false;
}
@Override
protected void finalize()
{
if(checkOut)
{
System.out.println("Error: has book checked out");
try
{
//调用父类的finalize方法
super.finalize();
}
catch (Throwable e)
{
e.printStackTrace();
}
}
}
}
package com.book.chap5.initAndDestroy;
/** *//**
*
*枚举enum的初步用法
*
*@author landon
*@since JDK1.6
*@version 1.0 2012-4-12
*
*/
public class SimpleEnum
{
public static void main(Stringargs)
{
Spiciness howHot = Spiciness.MEDIUM;
//打印枚举,toString方法已实现
System.out.println(howHot);
//ordinal按照声明顺序从0开始
for(Spiciness spiciness : Spiciness.values())
{
System.out.println(spiciness + " ordinal:" + spiciness.ordinal());
}
testSwitchEnum(Spiciness.MILD);
testSwitchEnum(Spiciness.HOT);
testSwitchEnum(Spiciness.FLAMING);
}
//测试enum和switch的组合
public static void testSwitchEnum(Spiciness spiciness)
{
switch(spiciness)
{
//注意case必须是NOT必须是无限制的枚举名称[spiciness的类型已经确定是枚举Spiciness,没有必要特意加上Spiciness.NOT,画蛇添足],而不是Spiciness.NOT,否则编译报错The qualified case label Spiciness.NOT must be replaced with the unqualified enum constant NOT
case NOT:
System.out.println("not");
break;
case MILD:
System.out.println("mild");
break;
case MEDIUM:
System.out.println("medium");
break;
case HOT:
case FLAMING:
default:
System.out.println("too hot");
}
}
}
//枚举声明
enum Spiciness
{
//常量,大写
NOT,MILD,MEDIUM,HOT,FLAMING
}
package com.book.chap5.initAndDestroy;
/** *//**
*
*可变参数
*<p>可变参数列表可以使用任何类型的参数,包括基本类型</p>
*
*@author landon
*@since JDK1.6
*@version 1.0 2012-4-12
*
*/
public class VarArgs
{
public static void main(Stringargs)
{
printArgsClass(1);
//输出class [I getClass,产生对象的类
System.out.println(new int[0].getClass());
f(1,'a');
//The method f(float, Character[]) is ambig uous for the type VarArgs
//f('a','b');
}
public static void printArgsClass(intargs)
{
System.out.println(args.getClass());
}
//重载的时候,使用可变参数列表一定要谨慎;非常有可能出现模棱两可的调用->从而导致编译报错
public static void f(float f,Characterargs)
{
System.out.println("first method");
}
public static void f(Characterargs)
{
System.out.println("second method");
}
}
posted on 2013-01-08 17:04
landon 阅读(1685)
评论(0) 编辑 收藏 所属分类:
Program 、
Book