在 Java 中,经过提供构造器,类的设计者可确保每一个对象都会获得初始化。Java 会保证初始化的进行。构造器采用与类相同的名称。java
由于能够要用多种方式来建立并初始化一个对象,因此就须要多个构造器,而构造器的名称又须要和类名相同,因此必须容许方法名相同而形式参数不一样的构造器存在,因此 Java 中有方法重载。算法
class Rock { Rock() { // 默认构造器 } Rock(int i) { // 带参数的构造器 System.out.println("i=" + i); } void print() { System.out.println("i = null"); } void print(int i) { System.out.println("i = " + i); } } // 初始化 Rock r1 = new Rock(); // 调用默认构造器 Rock r2 = new Rock(1); // 调用带参数的构造器 r2.print(); r2.print(1);
方法签名是由方法名和参数类型列表构成的,因此用参数类型列表区分重载方法。不能用返回值区分重载方法编程
因为基本类型可能会从一个“较小”的类型自动提高为一个“较大”的类型,因此在重载方法中须要特别注意:数组
int
处理因为同一类型的对象均可以调用相同的方法,为了在方法中区分不一样的对象,会把对象的引用做为参数传递给方法,a.fun(1)
在编译器内部会被翻译成ClassName.fun(a, 1)
,咱们能够经过this
关键字在方法中访问到对象的引用。dom
this
,直接调用便可。public class Flower { int petalCount = 0; String s = "initial value"; Flower(int petals) { petalCount = petals; System.out.println("int arg ,petalCount = " + petalCount); } Flower(String ss) { s = ss; System.out.println("string arg ,s = " + s); } Flower(String s, int petals) { this(petals); // this 只能调用一次构造器 this.s = s; System.out.println("string & int arg, s = " + s + ", petalCount = " + petalCount); } Flower() { this("hello", 24); } public static void main(String[] args) { Flower flower = new Flower(); System.out.println("flower.petalCount = " + flower.petalCount); System.out.println("flower.s = " + flower.s); } } // int arg ,petalCount = 24 // string & int arg, s = hello, petalCount = 24 // flower.petalCount = 24 // flower.s = hello
static 方法就是没有 this 的方法,在 static 中不能调用非静态方法,可是反过来能够。函数
Java 中没有用于释放对象的 delete,由于垃圾回收器会自动帮你释放存储空间,所以 Java 中没有析构函数。可是垃圾回收不能彻底代替析构函数,若是但愿进行除释放存储空间以外的清理工做,咱们须要明确调用某个 Java 方法。例如某个类打开了一个文件,垃圾回收不能自动帮咱们关闭这个文件。为何这个工做不能有 finalize() 方法来完成呢,缘由其实在上面已经说明了,对象可能不会被垃圾回收,也就是说 finalize() 方法可能永远都不会被调用。ui
若是 JVM 没有面临内存耗尽的状况,它是不会浪费时间去执行垃圾回收以恢复内存的。this
虽然咱们不能用 finalize() 方法来进行“清理”,可是咱们能够利用它验证某个对象的终结条件。仍是刚才那个打开文件的例子,假设在文件没有关闭的时候,垃圾回收将对象回收了,这就会产生一些很是难找的 bug。而 finalize() 能够帮助咱们发现这种 bug。lua
class Book { boolean checkedOut = false; Book(boolean checkOut) { checkedOut = checkOut; } void checkIn() { checkedOut = false; } protected void finalize() { if (checkedOut) { System.out.println("Error: checked out"); } // super.finalize(); } } public class TerminationCondition { public static void main(String[] args) { Book novel = new Book(true); novel.checkIn(); new Book(true); System.gc(); } } // Error: checked out
如上面这个例子,咱们但愿 Book 在被回收前已经 checkIn 了,因此咱们在 finalize() 中写了一个条件语句来判断。翻译
System.gc()
强制 GCsuper.finalize();
垃圾回收器会提升对象在堆上建立的速度,这是由于 Java 的堆的实现与 C++ 的不一样,其更像是一个传送带,每分配一个对象,它就往前移动一格,因此“堆指针” 只是简单的移动到还没有分配的空间,这意味 Java 中在堆上的分配速度很是快。固然,若是只是简单的像传送带同样工做的话,Java 的堆会占用大量的虚拟内存,进而致使频繁的页面调度,并可能会致使内存资源耗尽,所以须要有垃圾回收器的介入。垃圾回收会一边回收空间一边对堆进行“紧凑”操做。
几种常见的垃圾回收机制:
JVM 中采用的垃圾回收机制:
Java 中 JIT(Just-In-Time)技术:
这种技术能够把程序所有或部分翻译成本地机器码,而不是经过 JVM,进而提高程序的运行速度。
当须要装载某个类时(第一次建立这个类时),编译器会找到其.class 文件,而后将该类的字节码装入内存,此时有两种作法:
Java 尽力保证:全部变量在使用前都能获得适当的初始化。
局部变量没有默认初始值,若有在未初始化前使用它会报错编译错误,而类变量则有默认初始值。
Java 容许在定义类成员变量的时候为其赋值进行初始化。非基本类型也能够,同时可使用已经函数或已经初始化好的变量进行初始化,但要保证初始化顺序的正确。
public class InitialValues { boolean t = false; char c = 'a'; byte b = 1; short s = 2; int i = func(s); long l = 4 + i; float f = (float)5.0; // 浮点数字面量是 double 类型的 double d = 6.0; String reference = new String("hello world"); // 非基本类型也能够 int func(short s) { return s*2; } }
public class InitialValues { boolean t = false; char c = 'a'; byte b = 1; short s = 2; int i = 3; long l = 4; float f; double d; String reference; { f = (float) 1.0; d = 2*f; reference = new String("hello"); reference = reference + f + d; } static { System.out.println("hello"); } static int a; static { System.out.println("A is " + a); a = 2; } }
对象的建立过程:
数组是同类型的、用一个标识符名称封装到一块儿的一个对象序列或基本类型数据序列。
定义方式:
int[] a; //建议使用这种 int a[]; //这样也能够, 可是不能指定数组的类型。`int a[3];` 这样是不容许的
int[] a;
这样只是定义了一个数组的引用,咱们可使用new
来建立一个数组,也能够直接初始化数组:
int[] a = new int[3]; // 使用 new 来建立一个数组,这时真实数据会分配在堆中,因此默认值都为“零” int[] b = {1, 2, 3}; // 直接初始化一个长度为3的数组 Integer[] c = new Integer[3]; // 建立一个对象数组,保存引用,这时初始值都为 null Random rand = new Random(2); int len = rand.nextInt(20); int[] c = new int[len]; // 长度不必定要是一个字面值,能够是变量
数组初始化的坑点:
InitialValues initialValues = new InitialValues(); initialValues.printInitialValues(); String[] stringArray = {"hello", "world"}; // initialValues.printStringArrary({"hello", "world"}); // 编译错误 initialValues.printStringArrary(stringArray); initialValues.printStringArrary(new String[]{"hello", "world"}); //正确打开方式 int[] intArray = {1, 2, 3, 4}; // initialValues.printIntArray({1, 2, 3, 4}); // 编译错误 initialValues.printIntArray(intArray); initialValues.printIntArray(new int[]{1, 2, 3, 4});
在方法中,用ClassName... ArgName
的形式能够定义可变参数列表,在方法中,ArgName 本质上是一个数组。在可变列表中可使用任何类型,包括基础类型。这里传入基本类型时,没有依赖自动装包和解包,这意味着,ClassName 为 int 时,ArgName 是一个 int[],而不是 Integer。在重载方法时,应该只在一个方法中使用可变参数列表
static void printArray(Object... args) { for(Object arg: args) { System.out.print(arg + " "); } System.out.println(); } static void f(int required, int... args) { System.out.println("Required: " + required); for(int i: args) { System.out.print(i + " "); } System.out.println(); } public static void main(String[] args) { printArray(1, 2, 3, 4, 5); f(1); f(1, 2, 3); Integ }
toString()
显示其名称,ordinal()
表示声明顺序enum EnumDemo { HELLO, WORLD, }; EnumDemo e1 = EnumDemo.HELLO; System.out.println(e1); // 自动调用toString() System.out.println(e1.ordinal()); for(EnumDemo e: EnumDemo.values()) { System.out.println("EnumDemo: " + e + " ordinal " + e.ordinal()); } // HELLO // 0 // EnumDemo: HELLO ordinal 0 // EnumDemo: WORLD ordinal 1
本文首发于Code & Fun