java編程思想——初始化和清除

初始化和清除(1)

  1. java自動調用構造器以保證每個對象的初始化。
  2. 構造器方法的名字和類名一致。

代碼示例:

package initialization;

/**
 * @author vincient
 * @create 2020-01-20 9:52 AM
 */
class Rock {
    public Rock() {
        System.out.print("Rock ");
    }
}

public class SimpleConstructor {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Rock();
        }
    }
}

//輸出:
//Rock Rock Rock Rock Rock Rock Rock Rock Rock Rock 

⚠️注意:構造器的名字必須與類名完全匹配
4. 默認構造器:沒有參數的構造器
5. 構造器方法也可以有參數

代碼示例:

package initialization;

/**
 * @author vincient
 * @create 2020-01-20 10:03 AM
 */
public class SimpleConstructor2 {
    public static void main(String[] args) {
        for (int i = 0; i < 8; i++) {
            new Rock2(i);
        }
    }
}

class Rock2 {
    public Rock2(int i) {
        System.out.print("Rock " + i + " ");
    }
}

//輸出結果
//Rock 0 Rock 1 Rock 2 Rock 3 Rock 4 Rock 5 Rock 6 Rock 7 
  1. 構造器參數提供了對象初始化的方式
  2. java中,創建和初始化是統一的概念。
  3. 構造器沒有返回值。

初始化和清除(2)

  1. 方法重載:同樣的方法名,參數不一樣。

代碼示例:

package initialization;

import static net.mindview.util.Print.print;

/**
 * @author vincient
 * @create 2020-01-20 10:59 AM
 */
public class Overloading {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            Tree t = new Tree(i);
            t.info();
            t.info("overloaded method");
        }
        new Tree();
    }
}

class Tree {
    int height;

    Tree() {
        print("Planting a seedling");
        height = 0;
    }

    public Tree(int initialHeight) {
        height = initialHeight;
        print("Creating new Tree that is " + height + " feet tall");
    }

    void info() {
        print("Tree is " + height + " feet tall");
    }

    void info(String s) {
        print(s + ": Tree is " + height + " feet tall");
    }
}
//輸出結果
//Creating new Tree that is 0 feet tall
//Tree is 0 feet tall
//overloaded method: Tree is 0 feet tall
//Creating new Tree that is 1 feet tall
//Tree is 1 feet tall
//overloaded method: Tree is 1 feet tall
//Creating new Tree that is 2 feet tall
//Tree is 2 feet tall
//overloaded method: Tree is 2 feet tall
//Creating new Tree that is 3 feet tall
//Tree is 3 feet tall
//overloaded method: Tree is 3 feet tall
//Creating new Tree that is 4 feet tall
//Tree is 4 feet tall
//overloaded method: Tree is 4 feet tall
//Planting a seedling

  1. 原型和方法重載結合使用:
    • 整數值被定義爲int
    • 如果數據類型小於方法參數類型,數據類型會被提升
    • char如果找不到匹配方法,則會提升爲int
package initialization;

import static net.mindview.util.Print.print;
import static net.mindview.util.Print.printnb;

/**
 * @author vincient
 * @create 2020-01-20 11:26 AM
 */
public class PrimitiveOverloading {
    void f1(char x) {
        printnb("f1(char) ");
    }

    void f1(byte x) {
        printnb("f1(byte) ");
    }

    void f1(short x) {
        printnb("f1(short) ");
    }

    void f1(int x) {
        printnb("f1(int) ");
    }

    void f1(long x) {
        printnb("f1(long) ");
    }

    void f1(float x) {
        printnb("f1(float) ");
    }

    void f1(double x) {
        printnb("f1(double) ");
    }

    void f2(byte x) {
        printnb("f2(byte) ");
    }

    void f2(short x) {
        printnb("f2(short) ");
    }

    void f2(int x) {
        printnb("f2(int) ");
    }

    void f2(long x) {
        printnb("f2(long) ");
    }

    void f2(float x) {
        printnb("f2(float) ");
    }

    void f2(double x) {
        printnb("f2(double) ");
    }

    void f3(short x) {
        printnb("f3(short) ");
    }

    void f3(int x) {
        printnb("f3(int) ");
    }

    void f3(long x) {
        printnb("f3(long) ");
    }

    void f3(float x) {
        printnb("f3(float) ");
    }

    void f3(double x) {
        printnb("f3(double) ");
    }

    void f4(int x) {
        printnb("f4(int) ");
    }

    void f4(long x) {
        printnb("f4(long) ");
    }

    void f4(float x) {
        printnb("f4(float) ");
    }

    void f4(double x) {
        printnb("f4(double) ");
    }

    void f5(long x) {
        printnb("f5(long) ");
    }

    void f5(float x) {
        printnb("f5(float) ");
    }

    void f5(double x) {
        printnb("f5(double) ");
    }

    void f6(float x) {
        printnb("f6(float) ");
    }

    void f6(double x) {
        printnb("f6(double) ");
    }

    void f7(double x) {
        printnb("f7(double) ");
    }

    void testConstVal() {
        printnb("5: ");
        f1(5);
        f2(5);
        f3(5);
        f4(5);
        f5(5);
        f6(5);
        f7(5);
        print();
    }

    void testChar() {
        char x = 'x';
        printnb("char: ");
        f1(x);
        f2(x);
        f3(x);
        f4(x);
        f5(x);
        f6(x);
        f7(x);
        print();
    }

    void testByte() {
        byte x = 0;
        printnb("byte: ");
        f1(x);
        f2(x);
        f3(x);
        f4(x);
        f5(x);
        f6(x);
        f7(x);
        print();
    }

    void testShort() {
        short x = 0;
        printnb("short: ");
        f1(x);
        f2(x);
        f3(x);
        f4(x);
        f5(x);
        f6(x);
        f7(x);
        print();
    }

    void testInt() {
        int x = 0;
        printnb("int: ");
        f1(x);
        f2(x);
        f3(x);
        f4(x);
        f5(x);
        f6(x);
        f7(x);
        print();
    }

    void testLong() {
        long x = 0;
        printnb("long: ");
        f1(x);
        f2(x);
        f3(x);
        f4(x);
        f5(x);
        f6(x);
        f7(x);
        print();
    }

    void testFloat() {
        float x = 0;
        printnb("float: ");
        f1(x);
        f2(x);
        f3(x);
        f4(x);
        f5(x);
        f6(x);
        f7(x);
        print();
    }

    void testDouble() {
        double x = 0;
        printnb("double: ");
        f1(x);
        f2(x);
        f3(x);
        f4(x);
        f5(x);
        f6(x);
        f7(x);
        print();
    }

    public static void main(String[] args) {
        PrimitiveOverloading p = new PrimitiveOverloading();
        p.testConstVal();
        p.testChar();
        p.testByte();
        p.testShort();
        p.testInt();
        p.testLong();
        p.testFloat();
        p.testDouble();
    }
}
//輸出結果
//5: f1(int) f2(int) f3(int) f4(int) f5(long) f6(float) f7(double)
//char: f1(char) f2(int) f3(int) f4(int) f5(long) f6(float) f7(double)
//byte: f1(byte) f2(byte) f3(short) f4(int) f5(long) f6(float) f7(double)
//short: f1(short) f2(short) f3(short) f4(int) f5(long) f6(float) f7(double)
//int: f1(int) f2(int) f3(int) f4(int) f5(long) f6(float) f7(double)
//long: f1(long) f2(long) f3(long) f4(long) f5(long) f6(float) f7(double)
//float: f1(float) f2(float) f3(float) f4(float) f5(float) f6(float) f7(double)
//double: f1(double) f2(double) f3(double) f4(double) f5(double) f6(double) f7(double) 
  1. 如果參數超過方法參數的範圍,需要對參數進行窄化操作

代碼示例:

package initialization;

import static net.mindview.util.Print.print;

/**
 * @author vincient
 * @create 2020-01-20 2:10 PM
 */
public class Demotion {
    void f1(char x) {
        print("f1(char)");
    }

    void f1(byte x) {
        print("f1(byte)");
    }

    void f1(short x) {
        print("f1(short)");
    }

    void f1(int x) {
        print("f1(int)");
    }

    void f1(long x) {
        print("f1(long)");
    }

    void f1(float x) {
        print("f1(float)");
    }

    void f1(double x) {
        print("f1(double)");
    }

    void f2(char x) {
        print("f2(char)");
    }

    void f2(byte x) {
        print("f2(byte)");
    }

    void f2(short x) {
        print("f2(short)");
    }

    void f2(int x) {
        print("f2(int)");
    }

    void f2(long x) {
        print("f2(long)");
    }

    void f2(float x) {
        print("f2(float)");
    }

    void f3(char x) {
        print("f3(char)");
    }

    void f3(byte x) {
        print("f3(byte)");
    }

    void f3(short x) {
        print("f3(short)");
    }

    void f3(int x) {
        print("f3(int)");
    }

    void f3(long x) {
        print("f3(long)");
    }

    void f4(char x) {
        print("f4(char)");
    }

    void f4(byte x) {
        print("f4(byte)");
    }

    void f4(short x) {
        print("f4(short)");
    }

    void f4(int x) {
        print("f4(int)");
    }

    void f5(char x) {
        print("f5(char)");
    }

    void f5(byte x) {
        print("f5(byte)");
    }

    void f5(short x) {
        print("f5(short)");
    }

    void f6(char x) {
        print("f6(char)");
    }

    void f6(byte x) {
        print("f6(byte)");
    }

    void f7(char x) {
        print("f7(char)");
    }

    void testDouble() {
        double x = 0;
        print("double argument:");
        f1(x);
        f2((float) x);
        f3((long) x);
        f4((int) x);
        f5((short) x);
        f6((byte) x);
        f7((char) x);
    }

    public static void main(String[] args) {
        Demotion p = new Demotion();
        p.testDouble();
    }
}

//執行結果:
//double argument:
//f1(double)
//f2(float)
//f3(long)
//f4(int)
//f5(short)
//f6(byte)
//f7(char)
  1. 不能通過返回值類型來區分重載方法

初始化和清除(3)

  1. 默認構造器沒有參數,用於創建默認對象。
  2. 如果類裏面沒有構造器,編譯器會自動創建一個默認構造器。

代碼示例:

package initialization;

/**
 * @author vincient
 * @create 2020-01-20 2:30 PM
 */
public class DefaultConstructor {
    public static void main(String[] args) {
        Bird b = new Bird();
    }
}

class Bird{

}
  1. 如果自定義了構造器,編譯器就不會生成構造器了。

代碼示例:

package initialization;

/**
 * @author vincient
 * @create 2020-01-20 2:35 PM
 */
public class NoSynthesis {
    public static void main(String[] args) {
        //! Bird2 b = new Bird2(); // No default 
        Bird2 b2 = new Bird2(1);
        Bird2 b3 = new Bird2(1.0);
    }
}

class Bird2 {
    public Bird2(int i) {
    }

    public Bird2(double d) {
    }
}

初始化和清除(4)

  1. 調用方法時,編譯器將對象的引用作爲第一個參數隱式傳遞給方法。***this***對應這個引用。
  2. ***this***只能在非靜態方法中被調用
  3. 如果在一個方法內部調用同一類的其他方法,不需要使用***this***,直接調用方法即可。***this***被自動用於這些方法調用上了。
  4. **this通常用於return語句中。

代碼示例:

package initialization;

/**
 * @author vincient
 * @create 2020-01-20 3:23 PM
 */
public class Leaf {
    int i = 0;

    Leaf increament() {
        i++;
        return this;
    }

    void print() {
        System.out.println("i = " + i);
    }

    public static void main(String[] args) {
        Leaf x = new Leaf();
        x.increament().increament().increament().print();
    }
}

//輸出結果:
//i = 3
  1. ***this***被用於將當前對象傳遞倒別的方法中

代碼示例:

package initialization;

/**
 * @author vincient
 * @create 2020-01-20 3:31 PM
 */
public class PassingThis {
    public static void main(String[] args) {
        new Person().eat(new Apple());
    }
}


class Person {
    public void eat(Apple apple) {
        Apple peeled = apple.getPeeled();
        System.out.println("Yummy");
    }
}

class Peeler {
    static Apple peel(Apple apple) {
        // ... remove peel
        return apple;
    }
}

class Apple {
    Apple getPeeled() {
        return Peeler.peel(this);
    }
}

//輸出結果
//Yummy

⚠️外部工具方法是保證代碼跨對象使用並減少重複代碼

  1. 從構造器中調用構造器,可以使用***this***,以減少重複代碼。
  2. 在構造器中使用***this***外加參數列表,可以調用匹配的構造器。
package initialization;

import static net.mindview.util.Print.print;

/**
 * @author vincient
 * @create 2020-01-20 4:05 PM
 */
public class Flower {
    int petalCount = 0;
    String s = "initial value";

    public Flower(int petals) {
        petalCount = petals;
        print("Constructor w/ int arg only, petalCount= " + petalCount);
    }

    public Flower(String ss) {
        print("Constructor w/ String arg only, s = " + ss);
        s = ss;
    }

    public Flower(String s, int petals) {
        this(petals);
//        this(s);
        this.s = s;
        print("String & int args");
    }

    public Flower() {
        this("hi", 47);
        print("default constructor (no args)");
    }

    void printPetalCount() {
//        this(11);
        print("petalCount = " + petalCount + " s = " + s);
    }

    public static void main(String[] args) {
        Flower x = new Flower();
        x.printPetalCount();
    }
}

//運行結果:
//Constructor w/ int arg only, petalCount= 47
//String & int args
//default constructor (no args)
//petalCount = 47 s = hi

⚠️只能在構造器中調用一次this***,構造器調用必須一開始就調用。
8. 當成員數據名和參數名一致時,使用this以表明對成員數據的調用。
9. 不可在其他成員方法中調用構造器方法。
10. static特點:
- static方法中不可使用
this

- static方法中不可調用
non-static方法
- non-static方法可以調用static方法
- 可以直接使用類調用
static*方法
- static方法可以訪問其他的靜態方法和靜態字段


初始化和清除(5)

  1. java的垃圾回收器會回收並恢復不再使用的對象的內存
  2. gc只會釋放使用new分配的內存,不會釋放其他方式生成的內存。爲了處理這種情況,java提供了可自定義的finalize方法。
  3. finalize方法的工作機制:當gc準備釋放對象的內存時將首先調用finalize方法,並在下一次垃圾回收時回收並釋放對象內存。
  4. finalize和C++中的destructor的區別:
    • 對象不一定被垃圾回收
    • 垃圾回收不是銷燬
  5. 垃圾回收是有開銷的
  6. finalize並不是通用意義上的清除方法。
  7. 垃圾回收只和內存相關,它唯一存在的理由就是恢復程序不再使用的內存
  8. finalize方法只與內存及其回收相關。
  9. 垃圾回收只關心如何釋放對象,不關心對象如何創建,finalize的使用僅限於特殊的內存分配場合。之所以使用finalize方法是因爲在分配內存時使用了類似C語言的做法,主要通過調用native methodsnative methods使用malloc函數分配內存,除非調用free函數進行釋放。finalize方法會調用free函數。

結論:finalize並不適合進行java的常規清除操作

  1. 清除的難題:在需要清除的時候進行清除。但是這個理念在’C++'的destructor的理念中是有衝突的。在C++中,所有的對象都應該被清除。如果是局部對象,那麼就應該對象創建的花括號裏面進行清除。如果是使用new創建的對象,就應該使用delete()進行釋放。這會導致難以跟蹤的問題。
  2. java不允許創建局部對象(對象不允許存放在棧中)。對象都是通過new創建的。而gc正好可以用來回收內存。所以java中沒有destructor。但是爲了滿足需求,有時需要編寫一些類似destructor的方法,用來調用並釋放內存。
  3. 終結條件:在對象回收時發現未合理清除的部分。

代碼示例:

package initialization;

/**
 * @author vincient
 * @create 2020-01-21 11:27 AM
 */
public class TerminationCondition {
    public static void main(String[] args) {
        Book novel = new Book(true);
        novel.checkIn();
        new Book(true);
        System.gc();
    }

}


class Book {
    boolean checkedOut = false;

    public Book(boolean checkOut) {
        checkedOut = checkOut;
    }

    void checkIn() {
        checkedOut = false;
    }

    @Override
    protected void finalize() throws Throwable {
        if (checkedOut) {
            System.out.println("Error: checked out");
            super.finalize();
        }
    }
}

//運行結果
//Error: checked out
  1. JVM的堆有堆指針,這樣內存分配會更快。垃圾回收會將廢棄的對象內存回收後。會壓縮堆裏面所有的對象。避免分頁錯誤。
  2. 垃圾回收的實現方式
    • 引用計數(簡單但是緩慢)
    • 對象引用鏈
    • jvm採用自適應型的垃圾回收策略。
      • stop-and-copy
      • mark-and-sweep

結論:JVM會根據實際情況選用相關的垃圾回收策略
15. 其他提高JVM執行效率的方式:
- JIT編譯器:將程序編譯成本地機器碼,缺點是代碼在編譯時需要時間,同時增大程序體量,導致內存分頁,最終降低程序運行效率。
- 替代完全JIT的方式,使用lazy-evaluation方式。現有方案Java HotSpot technologies


初始化和清除(6)

  1. java儘量保證變量初始化
    • 方法變量必須有初始化值
    • 字段會自動獲得默認值,如果是對象,得到的默認值爲null
  2. 可以在定義變量的時候給變量賦初始化值
  3. 可以使用方法初始化變量,方法也可以使用參數,但是方法使用的參數不能是尚未初始化的字段。

初始化和清除(7)

  1. 構造器初始化,使初始化更加靈活,但在進入構造器方法之前,自動初始化就已經執行了。
  2. 初始化的順序:初始化的順序取決於類中變量的定義順序。在所有方法調用之前,變量就已經初始化了。

代碼示例:

package initialization;

import static net.mindview.util.Print.print;

/**
 * @author vincient
 * @create 2020-02-02 2:14 PM
 */
public class OrderOfInitialization {
    public static void main(String[] args) {
        House h = new House();
        h.f();
    }
}

class Window {
    public Window(int marker) {
        print("Window(" + marker + ")");
    }
}

class House {
    Window w1 = new Window(1);

    public House() {
        print("House()");
        w3 = new Window(33);
    }

    Window w2 = new Window(2);

    void f() {
        print("f()");
    }

    Window w3 = new Window(3);
}
//運行結果
//Window(1)
//Window(2)
//Window(3)
//House()
//Window(33)
//f()
  1. 靜態數據初始化:static區域只有同一塊內存,不管創建了多少對象。static不適用於局部變量。

代碼示例:

package initialization;

import static net.mindview.util.Print.print;

/**
 * @author vincient
 * @create 2020-02-02 2:35 PM
 */
public class StaticInitialization {
    public static void main(String[] args) {
        print("Creating new Cupboard() in main");
        new Cupboard();
        print("Creating new Cupboard() in main");
        new Cupboard();
        table.f2(1);
        cupboard.f3(1);
    }

    static Table table = new Table();
    static Cupboard cupboard = new Cupboard();
}

class Bowl {
    public Bowl(int marker) {
        print("Bowl(" + marker + ")");
    }

    void f1(int marker) {
        print("f1(" + marker + ")");
    }
}

class Table {
    static Bowl bowl1 = new Bowl(1);

    public Table() {
        print("Table()");
        bowl2.f1(1);
    }

    void f2(int marker) {
        print("f2(" + marker + ")");
    }

    static Bowl bowl2 = new Bowl(2);
}

class Cupboard {
    Bowl bowl3 = new Bowl(3);
    static Bowl bowl4 = new Bowl(4);

    public Cupboard() {
        print("Cupboard()");
        bowl4.f1(2);
    }

    void f3(int marker) {
        print("f3(" + marker + ")");
    }

    static Bowl bowl5 = new Bowl(5);
}

//執行結果:
//Bowl(1)
//Bowl(2)
//Table()
//f1(1)
//Bowl(4)
//Bowl(5)
//Bowl(3)
//Cupboard()
//f1(2)
//Creating new Cupboard() in main
//Bowl(3)
//Cupboard()
//f1(2)
//Creating new Cupboard() in main
//Bowl(3)
//Cupboard()
//f1(2)
//f2(1)
//f3(1)
  1. static變量的初始化只在需要的時候進行。當第一個對象創建的時候執行初始化。
  2. 變量的初始化順序是先static後非static
  3. 顯式的靜態初始化:java使用static語句塊將靜態初始化包含起來。
  4. 非靜態實例初始化:和靜態初始化類似,除了沒有static

初始化和清除(8)

  1. 數組的定義:在類型後加上[],也可以在標識之後加[]
  2. 數組的初始化,可以使用特殊語法,如:
int[] a1 = { 1, 2, 3, 4, 5 };
  1. 數組的基本成員量:length
  2. 如果在編寫代碼時不知道數組裏有多少元素,可以使用new來生成數組中的元素,例如:
import static net.mindview.util.Print.print;

/**
 * @author vincient
 * @create 2020-02-03 11:13 AM
 */
public class ArrayNew {
    public static void main(String[] args) {
        int[] a;
        Random random = new Random(47);
        a = new int[random.nextInt(20)];
        print("length of a = " + a.length);
        print(Arrays.toString(a));
    }
}

//運行結果
//length of a = 18
//[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
  1. 數組元素會默認初始化爲零值。
  2. 可變參數列表,代碼示例:
package initialization;

/**
 * @author vincient
 * @create 2020-02-03 1:18 PM
 */
public class VarArgs {
    static void printArray(Object[] args) {
        for (Object obj : args) {
            System.out.print(obj + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        printArray(new Object[]{new Integer(47), new Float(3.14), new Double(11.11)});
        printArray(new Object[]{"one", "two", "three"});
        printArray(new Object[]{new A(), new A(), new A()});
    }
}

class A {

}
//運行結果
//47 3.14 11.11
//one two three
//initialization.A@511d50c0 initialization.A@60e53b93 initialization.A@5e2de80c 
  1. java SE5引入新的可變參數列表特性寫法:…
  2. 使用可變參數需要避免重載出現衝突,通常應當對某個版本的重載方法使用可變參數列表

初始化和清除(9)

  1. Java SE5新增關鍵詞:enmu
  2. 枚舉的每個實例都是常量,所以都是大寫。

代碼示例:

package initialization;

/**
 * @author vincient
 * @create 2020-02-03 2:41 PM
 */
public enum Spiciness {
    NOT, MILD, MEDIUM, HOT, FLAMING
}
  1. Enum有各種內置方法:toString(),ordinal(),values()

代碼示例:

package initialization;

/**
 * @author vincient
 * @create 2020-02-03 2:49 PM
 */
public class EnumOrder {
    public static void main(String[] args) {
        for (Spiciness s : Spiciness.values()) {
            System.out.println(s + ", ordinal " + s.ordinal());
        }
    }
}
//運行結果
//NOT, ordinal 0
//MILD, ordinal 1
//MEDIUM, ordinal 2
//HOT, ordinal 3
//FLAMING, ordinal 4
  1. enum可以用於switch語句中,代碼示例:
package initialization;

/**
 * @author vincient
 * @create 2020-02-03 2:51 PM
 */
public class Burrito {
    Spiciness degree;

    public Burrito(Spiciness degree) {
        this.degree = degree;
    }

    public void describe() {
        System.out.print("This burrito is ");
        switch (degree) {
            case NOT:
                System.out.println("not spicy at all.");
                break;
            case MILD:
            case MEDIUM:
                System.out.println("a little hot.");
                break;
            case HOT:
            case FLAMING:
            default:
                System.out.println("maybe too hot.");
        }
    }

    public static void main(String[] args) {
        Burrito
                plain = new Burrito(Spiciness.NOT),
                greenChile = new Burrito(Spiciness.MEDIUM),
                jalapeno = new Burrito(Spiciness.HOT);
        plain.describe();
        greenChile.describe();
        jalapeno.describe();
    }
}
//運行結果
//This burrito is not spicy at all.
//This burrito is a little hot.
//This burrito is maybe too hot.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章