Java Study Summary

1. Class Loader:

   a. Load parent class

   b. allocate mem for static member and method

   c. Init parent class static member

   d. Class Load before Class construction.


[code="java"]package oracle.test;

//: Beetle.java
// The full process of initialization.
class Insect {
    Child c ;
    int i = 9;
    int j;

    Insect() {
        prt("i = " + i + ", j = " + j);
        j = 39;
        try {
            c = new Child();
        } catch (Exception e) {
        }
    }
    static int x1 = prt("static Insect.x1 initialized");

    static int prt(String s) {
        System.out.println(s);
        return 47;
    }
}
public class Beetle extends Insect {
    int k = prt("Beetle.k initialized");

    Beetle() {
        prt("k = " + k);
        prt("j = " + j);
    }
    static int x2 = prt("static Beetle.x2 initialized");

    static int prt(String s) {
        System.out.println(s);
        return 63;
    }

    public static void main(String[] args) {
        prt("Beetle constructor");
        Beetle b = new Beetle();
    }
} ///:~


Result:

[code="java"]static Insect.x1 initialized
static Beetle.x2 initialized
Beetle constructor
i = 9, j = 0
Child.I inited
Father()
Child()
Beetle.k initialized
k = 63
j = 39


2. Java using dynamic binding(Runtime binding)
to find method body.  "final a method
" can turn off the dynamic binding, then the compilor can compile more efficient execute code.


3. 即使不包括任何abstract 方法,亦可将一个类声明成“抽象类”。如果一个类没必要拥有任何抽象方法,而

且我们想禁止那个类的所有实例,这种能力就会显得非常有用。


4. 合并一个具体类与接口的时候,具体类必须首先出现,然后才是接口(否则编译器会报错)。


5. 到底应该使用一个接口还是一个抽象类呢?若使用接口,我们可以同时获得抽象类以及接口的好处。

所以假如想创建的基础类没有任何方法定义或者成员变量,那么无论如何都愿意使用接口,而不要选择抽象类。

事实上,如果事先知道某种东西会成为基础类,那么第一个选择就是把它变成一个接口。

只有在必须使用方法定义或者成员变量的时候,才应考虑采用抽象类。


6.我们只能对单独一个类应用extends(扩展)关键字。但由

于接口可能由多个其他接口构成,所以在构建一个新接口时,extends 可能引用多个基础接口。

接口的名字只是简单地使用逗号分隔。


7.置入一个接口的所有字段都自动具有static 和final
属性,所以接口是对常数值进行分组的一个好工

具.


8. 枚举实现单例:

[code="java"]package oracle.test;

public enum TestSingletonEnum {
    INSTANCE1;
   
    public void printHello(String name){
        System.out.println("Hello:" + name);
    }
   
    public static void main(String[] args) {
        TestSingletonEnum.INSTANCE1.printHello("Sunny");
    }
}


9. 枚举实例:

[code="java"]package oracle.test;

public enum MonthEnum {
    JAN("January"),
    FEB("February"),
    MAR("March"),
    APR("April"),
    MAY("May"),
    JUN("June"),
    JUL("July"),
    AUG("August"),
    SEP("September"),
    OCT("October"),
    NOV("November"),
    DEC("December");
   
    public String name;

    private MonthEnum(String n) {
        name = n;
    }
   
    public String toString(){
        return name;
    }
   
    public static void main(String[] args) {
        MonthEnum m = MonthEnum.JAN;
        System.out.println(m);
        m = MonthEnum.DEC;
        System.out.println(m);
        System.out.println(m == MonthEnum.DEC);
        System.out.println(m.equals(MonthEnum.DEC));
    }

}



10. 内部类(非static)的对象默认持有创建它的那个封装类的一个对象的句柄。

内部类不可拥有static 数据或static 内部类。

static 内部类意味着:

(1) 为创建一个static 内部类的对象,我们不需要一个外部类对象。

(2) 不能从static 内部类的一个对象中访问一个外部类对象。


11. 接口内的内部类:

[code="java"]//: IInterface.java
// Static inner classes inside interfaces
interface IInterface {
static class Inner {
int i, j, k;
public Inner() {}
void f() {}
}
} ///:~

12.  大家在每个类里都设置一个main(),将其作为那个类的测试床使用。这样做的一个

缺点就是额外代码的数量太多。若不愿如此,可考虑用一个static 内部类容纳自己的测试代码。

[code="java"]class TestBed {
TestBed() {}
void f() { System.out.println("f()"); }
public static class Tester {
public static void main(String[] args) {
TestBed t = new TestBed();
t.f();
}
}
}

这样便生成一个独立的、名为TestBed$Tester 的类(为运行程序,请使用“java TestBed$Tester”命

令)。可将这个类用于测试,但不需在自己的最终发行版本中包含它。


13. 内部类实例化:

[code="java"]OuterClass out = new OuterClass();
OuterClass.InnerClass inner = out.new InnerClass();
inner.method();


14. 从内部类继承

[code="java"]class WithInner {
class Inner {}
}
public class InheritInner
extends WithInner.Inner {
//! InheritInner() {} // Won't compile
InheritInner(WithInner wi) {
wi.super();
}
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner ii = new InheritInner(wi);
}
}

15.  构建器 初始化的实际过程是这样的:

(1) 在采取其他任何操作之前,为对象分配的存储空间初始化成二进制零。

(2) 就象前面叙述的那样,调用基础类构建器。此时,被覆盖的draw()方法会得到调用(的确是在

RoundGlyph 构建器调用之前),此时会发现radius 的值为0,这是由于步骤(1)造成的。

(3) 按照原先声明的顺序调用成员初始化代码。

(4) 调用衍生类构建器的主体。

例如:


[code="java"]abstract class Glyph {
    abstract void draw();

    Glyph() {
        System.out.println("Glyph() before draw()");
        draw();
        System.out.println("Glyph() after draw()");
    }
}
class RoundGlyph extends Glyph {
    int radius = 1;

    RoundGlyph(int r) {
        radius = r;
        System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
    }

    void draw() {
        System.out.println("RoundGlyph.draw(), radius = " + radius);
    }
}
public class PolyConstructors {
    public static void main(String[] args) {
        new RoundGlyph(5);
    }
}

结果:


[code="java"]Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5


16. 设计构建器时一个特别有效的规则是:用尽可能简单的方法使对象进入就绪状态;如果可能,避免调

用任何方法。在构建器内唯一能够安全调用的是在基础类中具有final 属性的那些方法(也适用于private

方法,它们自动具有final 属性)。这些方法不能被覆盖,所以不会出现潜在的问题。


17.事实上是用Object 的hashCode()方法生成每个对象的散列码,而且默认情况下只使用它的对象的地址



18.  HashSet 显然要比ArraySet 出色得多,而且性能明显与元素的多寡关系不大。一般编写程序的时候,几乎永远用不着使用ArraySet。


19. 当我们使用Map 时,首要的选择应该是HashMap。同时由于TreeMap 出色的put()性能,所以如果需要创建大量Map,而且只有在以后才需要涉及大量检索操作,那么最佳的策略就是:

创建和填充TreeMap;以后检索量增大的时候,再将重要的TreeMap 转换成HashMap——使用HashMap(Map)构建器。

同样地,只有在事实证明确实存在性能瓶颈后,才应关心这些方面的问题——先用起来,再根据需要加快速度。


20. Java IO Sources & Sinks:

Java 1.0 class -> Java 1.1 class

=================================================

InputStream -> Reader [converter: InputStreamReader]

OutputStream -> Writer [converter: OutputStreamWriter]

FileInputStream -> FileReader

FileOutputStream -> FileWriter

StringBufferInputStream -> StringReader

(no corresponding class)-> StringWriter

ByteArrayInputStream -> CharArrayReader

ByteArrayOutputStream -> CharArrayWriter

PipedInputStream -> PipedReader

PipedOutputStream -> PipedWriter


21. file process in java 1.0:

[code="java"]DataInputStream in =
new DataInputStream(
new BufferedInputStream(
new FileInputStream(args[0])));
String s, s2 = new String();
while((s = in.readLine())!= null)
s2 += s + "\n";
in.close();

File process in Java 1.1

[code="java"]BufferedReader in =
new BufferedReader(
new FileReader(args[0]));
String s, s2 = new String();
while((s = in.readLine())!= null)
s2 += s + "\n";
in.close();


22. 序列化:

若想保存系统状态,最安全的做法是当作一种“微观”操作序列化。如果序列化了某些东西,再去做其他一

些工作,再来序列化更多的东西,以此类推,那么最终将无法安全地保存系统状态。相反,应将构成系统状

态的所有对象都置入单个集合内,并在一次操作里完成那个集合的写入。这样一来,同样只需一次方法调

用,即可成功恢复之。


23. 克隆时要注意的两个关键问题是:几乎肯定要调用super.clone(),以及注意将克隆设为public。


24.内存管理

java 做的系统给人的印象是什么?占内存!说道这句话就会有N多人站出来为java辩护,并举出一堆的性能测试报告来证明这一点。其实从理论上来讲java做的系统并不比其他语言开发出来的系统更占用内存,那么为什么却有这么N多理由来证明它确实占内存呢?两个字,陋习。



(1)别用new Boolean()。

   在很多场景中Boolean类型是必须的,比如JDBC中boolean类型的set与get都是通过Boolean封装传递的,大部分ORM也是用Boolean来封装boolean类型的,比如:



ps.setBoolean("isClosed",new Boolean(true));



ps.setBoolean("isClosed",new Boolean(isClosed));



ps.setBoolean("isClosed",new Boolean(i==3));

   



   通常这些系统中构造的Boolean实例的个数是相当多的,所以系统中充满了大量Boolean实例小对象,这是相当消耗内存的。Boolean类实际上只要两个实例就够了,一个true的实例,一个false的实例。

Boolean类提供两了个静态变量:

public static final Boolean TRUE = new Boolean(true);

public static final Boolean FALSE = new Boolean(false);

需要的时候只要取这两个变量就可以了,

比如:

ps.setBoolean("isClosed",Boolean.TRUE);

那么象2、3句那样要根据一个boolean变量来创建一个Boolean怎么办呢?可以使用Boolean提供的静态方法:

Boolean.valueOf()

比如:

ps.setBoolean("isClosed",Boolean.valueOf(isClosed));

ps.setBoolean("isClosed",Boolean.valueOf(i==3));

因为valueOf的内部实现是:return (b ? TRUE : FALSE);

所以可以节省大量内存。相信如果Java规范直接把Boolean的构造函数规定成private,就再也不会出现这种情况了。



(2)别用new Integer。

   和Boolean类似,java开发中使用Integer封装int的场合也非常多,并且通常用int表示的数值通常都非常小。SUN SDK中对Integer的实例化进行了优化,Integer类缓存了-128到127这256个状态的Integer,如果使用Integer.valueOf(int i),传入的int范围正好在此内,就返回静态实例。这样如果我们使用Integer.valueOf代替new Integer的话也将大大降低内存的占用。如果您的系统要在不同的SDK(比如IBM SDK)中使用的话,那么可以自己做了工具类封装一下,比如IntegerUtils.valueOf(),这样就可以在任何SDK中都可以使用这种特性。



(3)用StringBuffer代替字符串相加。这个我就不多讲了,因为已经被人讲过N次了。我只想将一个不是笑话的笑话,我在看国内某“著名”java开发的WEB系统的源码中,竟然发现其中大量的使用字符串相加,一个拼装SQL语句的方法中竟然最多构造了将近100个string实例。无语中!



(4)过滥使用哈希表,有一定开发经验的开发人员经常会使用hash表(hash表在JDK中的一个实现就是HashMap)来缓存一些数据,从而提高系统的运行速度。比如使用 HashMap缓存一些物料信息、人员信息等基础资料,这在提高系统速度的同时也加大了系统的内存占用,特别是当缓存的资料比较多的时候。其实我们可以使用操作系统中的缓存的概念来解决这个问题,也就是给被缓存的分配一个一定大小的缓存容器,按照一定的算法淘汰不需要继续缓存的对象,这样一方面会因为进行了对象缓存而提高了系统的运行效率,同时由于缓存容器不是无限制扩大,从而也减少了系统的内存占用。现在有很多开源的缓存实现项目,比如ehcache、 oscache等,这些项目都实现了FIFO、MRU等常见的缓存算法。



(5)避免过深的类层次结构和过深的方法调用。因为这两者都是非常占用内存的(特别是方法调用更是堆栈空间的消耗大户)。



(6)变量只有在用到它的时候才定义和实例化。



(7)尽量避免使用static变量,类内私有常量可以用final来代替。

25 JVM中对象的生命周期大致可分为7个阶段:创建阶段(Creation)、应用阶段(Using)、不可视阶段(Invisible)、不可到达阶段 (Unreachable)、可收集阶段(Collected)、终结阶段(Finalized)、释放阶段(Free)。

※     创建阶段

系统通过下面的步骤,完成创建对象的过程:

(1)       为对象分配存储空间。

(2)       开始构造对象。

(3)       递归调用其超类的构造方法。

(4)       进行对象实例初始化与变量初始化。

(5)       执行构造方法体。

创建对象时几个关键应用规则:

(1)       避免在循环体中创建对象,即使对象占用的内存空间不大。

(2)       尽量及时使对象符合垃圾回收标准。

(3)       不要采用过深的继承层次。

(4)       访问本地变量优于访问类中的变量。

※     应用阶段

在应用阶段,对象具备下列特征:

(1)       系统至少维护对象的一个强引用(Strong Reference);

(2)       所有对该对象的引用全部是强引用,除非我们显示使用了软引用(Soft Reference)、弱引用(Weak Reference)或虚引用(Phantom Reference)。

强引用是指JVM内存管理器从根引用集合(Root set)出发遍寻堆中所有到达对象的路径。当到达对象的任意路径都不含有引用对象时,这个对象的引用就被称为强引用。

软引用具有较强的引用功能,只有当内存不够的时候,才回收这类内存。另外这些引用对象还能保证在Java抛出OutOfMemory异常之前,被 设置为NULL。它可以用于实现一些常用资源的缓存,实现Cache的功能,保证最大限度的试用内存而不引起OutOfMemory。但在某些时候对软引 用的试用会降低应用程序的运行效率与性能。

弱引用对象与Soft引用对象的最大不同就在于:GC在进行回收时,需要通过算法检查是否回收Soft引用对象,而对于Weak引用对象,GC总 是进行回收。Weak引用对象常用于Map结构中,引用占用内存空间比较大的对象,一旦对象的强引用为null时,这个对象的引用就不存在了,GC能够快 速回收该对象。

虚引用的用途较少,主要用于辅助finalize函数的使用。

※     不可视阶段

当一个对象处在不可视阶段,说明我们不能在其它区域的代码中引用它,此时应该主动将其设置为空,帮助JVM及时地发现这个垃圾对象,并且回收系统资源。

※     可收集阶段、终结阶段与释放阶段

对象可能处于三种情况:

(1)       回收器发现该对象已经不可到达。

(2)       Finalize方法已经被执行。

(3)       对象空间已被重用。

Java程序设计中有关内存管理的其它经验:

(1)       最基本的建议就是尽早释放无用对象的引用。

(2)       尽量少用finalize函数。

(3)       如果需要使用经常用大的图片,可以使用soft应用类型。

(4)       注意集合数据类型,包括数组、树、图、链表等数据结构,这些数据结构对GC来说,回收更为复杂。另外注意一些全局变量,静态变量,这些变量往往容易造成不必要的内存资源浪费。

(5)       尽量避免在类的默认构造器中创建、初始化大量对象,防止在调用其自类的构造器时造成不必要的内存浪费。

(6)       尽量避免强制系统做垃圾回收,增长系统做垃圾回收的最终时间,降低系统性能。

(7)       尽量避免显示申请数组空间,当不得不显示地申请数组空间时尽量准确的估计出其合理值。

(8)       尽量在远程方法调用(RMI)类应用开发时使用瞬间值(transient)变量,除非远程调用端需要获取该瞬间值变量的值。

(9)       尽量在合适的场景下使用对象池技术以提高系统性能,缩减系统开销,但是要注意对象池的尺寸不宜过大,及时清除无效对象释放内存资源,综合考虑应用运行环境的内存资源限制,避免过高估计运行环境所提供内存资源的数量。


《Java优化编程》之表达式、语句与保留字

※静态代码块

有时候为了能在引用一个类的时候作一些必要的初始化工作,经常利用static关键字加入一些静态的代码块,完成这个超前功能。

    静态代码块对静态变量的应用在声明上存在先后顺序的限制,这与方法中引用变量的情况是不同的,在方法体中引用的变量在类文件前后声明没有限制,但是如果你 试图在静态代码块中引用静态变量,就要考虑其声明的先后顺序的限定,在静态代码块中引用的静态变量必须在静态代码块之前声明。

    对静态变量的引用有下面的规则:

(1)    可以在非静态方法体中引用静态变量。

(2)    在静态方法体中不可以引用非静态变量。

(3)    可以在静态方法体中创建非静态变量。



※final关键字

    保留字final的应用范围:

(1)    用来声明类的变量。

(2)    用来声明方法的常量参数,对变量起到相应的保护作用,防止变量被无意赋值。

(3)    用来声明不可覆盖的方法,不但可以防止父类中的方法不被子类覆盖,还可以加快应用的运行速度,提高系统性能。

(4)    用来声明不可继承的类。



※使用循环语句的几个建议

(1)当做数组拷贝操作时,采用System.arraycopy()方法完成拷贝操作要比采用循环的办法完成数组拷贝操作效率高。

(2)尽量避免在循环体中调用方法,因为方法调用是比较昂贵的。

(3)最好避免在循环体内存取数组元素,比较好的办法是在循环体内采用临时变量,在循环体外更改数组的值。这是因为在循环体内使用变量比存取数组元素要快。

(4)当没有使用JIT或HotSpot虚拟机时,尽量使用0值作为终结条件的比较元素,以提高循环的性能。

(5)避免在最终条件比较时采用方法返回值的方式进行判断,这样做将增大系统开销,降低系统性能。

(6)尽量避免在循环体中使用try-catch块,最好在循环体外使用try-catch块以提高系统性能。

(7)在多重循环中,如果可能,尽量将最长的循环放在最内层,最短的循环放在最外层,以减少循环层间的切换次数。

(8)如果循环体内有if-else类逻辑判断,并且循环次数很大,最好将if-else类逻辑判断移到循环体之外,以提高应用性能。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值