初始化和清除(1)
- java自動調用構造器以保證每個對象的初始化。
- 構造器方法的名字和類名一致。
代碼示例:
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
- 構造器參數提供了對象初始化的方式
- java中,創建和初始化是統一的概念。
- 構造器沒有返回值。
初始化和清除(2)
- 方法重載:同樣的方法名,參數不一樣。
代碼示例:
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
- 原型和方法重載結合使用:
- 整數值被定義爲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)
- 如果參數超過方法參數的範圍,需要對參數進行窄化操作
代碼示例:
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)
- 不能通過返回值類型來區分重載方法
初始化和清除(3)
- 默認構造器沒有參數,用於創建默認對象。
- 如果類裏面沒有構造器,編譯器會自動創建一個默認構造器。
代碼示例:
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{
}
- 如果自定義了構造器,編譯器就不會生成構造器了。
代碼示例:
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)
- 調用方法時,編譯器將對象的引用作爲第一個參數隱式傳遞給方法。***this***對應這個引用。
- ***this***只能在非靜態方法中被調用
- 如果在一個方法內部調用同一類的其他方法,不需要使用***this***,直接調用方法即可。***this***被自動用於這些方法調用上了。
- **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
- ***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
⚠️外部工具方法是保證代碼跨對象使用並減少重複代碼
- 從構造器中調用構造器,可以使用***this***,以減少重複代碼。
- 在構造器中使用***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)
- java的垃圾回收器會回收並恢復不再使用的對象的內存
- gc只會釋放使用new分配的內存,不會釋放其他方式生成的內存。爲了處理這種情況,java提供了可自定義的finalize方法。
- finalize方法的工作機制:當gc準備釋放對象的內存時將首先調用finalize方法,並在下一次垃圾回收時回收並釋放對象內存。
- finalize和C++中的destructor的區別:
- 對象不一定被垃圾回收
- 垃圾回收不是銷燬
- 垃圾回收是有開銷的
- finalize並不是通用意義上的清除方法。
- 垃圾回收只和內存相關,它唯一存在的理由就是恢復程序不再使用的內存
- finalize方法只與內存及其回收相關。
- 垃圾回收只關心如何釋放對象,不關心對象如何創建,finalize的使用僅限於特殊的內存分配場合。之所以使用finalize方法是因爲在分配內存時使用了類似C語言的做法,主要通過調用native methods。native methods使用malloc函數分配內存,除非調用free函數進行釋放。finalize方法會調用free函數。
結論:finalize並不適合進行java的常規清除操作
- 清除的難題:在需要清除的時候進行清除。但是這個理念在’C++'的destructor的理念中是有衝突的。在C++中,所有的對象都應該被清除。如果是局部對象,那麼就應該對象創建的花括號裏面進行清除。如果是使用new創建的對象,就應該使用delete()進行釋放。這會導致難以跟蹤的問題。
- java不允許創建局部對象(對象不允許存放在棧中)。對象都是通過new創建的。而gc正好可以用來回收內存。所以java中沒有destructor。但是爲了滿足需求,有時需要編寫一些類似destructor的方法,用來調用並釋放內存。
- 終結條件:在對象回收時發現未合理清除的部分。
代碼示例:
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
- JVM的堆有堆指針,這樣內存分配會更快。垃圾回收會將廢棄的對象內存回收後。會壓縮堆裏面所有的對象。避免分頁錯誤。
- 垃圾回收的實現方式
- 引用計數(簡單但是緩慢)
- 對象引用鏈
- jvm採用自適應型的垃圾回收策略。
- stop-and-copy
- mark-and-sweep
結論:JVM會根據實際情況選用相關的垃圾回收策略
15. 其他提高JVM執行效率的方式:
- JIT編譯器:將程序編譯成本地機器碼,缺點是代碼在編譯時需要時間,同時增大程序體量,導致內存分頁,最終降低程序運行效率。
- 替代完全JIT的方式,使用lazy-evaluation方式。現有方案Java HotSpot technologies
初始化和清除(6)
- java儘量保證變量初始化
- 方法變量必須有初始化值
- 字段會自動獲得默認值,如果是對象,得到的默認值爲null。
- 可以在定義變量的時候給變量賦初始化值
- 可以使用方法初始化變量,方法也可以使用參數,但是方法使用的參數不能是尚未初始化的字段。
初始化和清除(7)
- 構造器初始化,使初始化更加靈活,但在進入構造器方法之前,自動初始化就已經執行了。
- 初始化的順序:初始化的順序取決於類中變量的定義順序。在所有方法調用之前,變量就已經初始化了。
代碼示例:
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()
- 靜態數據初始化: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)
- static變量的初始化只在需要的時候進行。當第一個對象創建的時候執行初始化。
- 變量的初始化順序是先static後非static
- 顯式的靜態初始化:java使用static語句塊將靜態初始化包含起來。
- 非靜態實例初始化:和靜態初始化類似,除了沒有static。
初始化和清除(8)
- 數組的定義:在類型後加上[],也可以在標識之後加[]
- 數組的初始化,可以使用特殊語法,如:
int[] a1 = { 1, 2, 3, 4, 5 };
- 數組的基本成員量:length
- 如果在編寫代碼時不知道數組裏有多少元素,可以使用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]
- 數組元素會默認初始化爲零值。
- 可變參數列表,代碼示例:
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
- java SE5引入新的可變參數列表特性寫法:…
- 使用可變參數需要避免重載出現衝突,通常應當對某個版本的重載方法使用可變參數列表
初始化和清除(9)
- Java SE5新增關鍵詞:enmu
- 枚舉的每個實例都是常量,所以都是大寫。
代碼示例:
package initialization;
/**
* @author vincient
* @create 2020-02-03 2:41 PM
*/
public enum Spiciness {
NOT, MILD, MEDIUM, HOT, FLAMING
}
- 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
- 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.