1,用构造器确保初始化
构造器有利于减少错误,并使代码更易阅读。从概念上讲,“初始化”与“创建”是彼此独立的。在java中“初始化”和“创建”捆绑在一起,两者不能分离。
随堂练习1
创建一个类,它包含一个未初始化的String引用。验证该应用被java初始化成null。
class TestInitial{
String s;
}
class Main{
public static void main(Stirng[] args){
System.out.println(TestInitial.s);
}
}
随堂练习2
创建一个类,包含定义时就初始化的String域,以及另一个通过构造方法初始化的String域。这两个方式有何差异?
class TestInitial{
String s="nihaoma";
String s1;
TestInitial(Stirng s1){
this.s1=s1;
}
}
class Main{
public static void main(Stirng[] args){
TestInitial testInitial=new TestInitial("have a test");
System.out.println(testInitial.s);
System.out.println(testInitial.s1);
}
}
2,方法重载
在java和c++中构造器是强制重载方法名的另一个原因。为了让方法名形同而形式参数不同的构造器同时存在,必须用到方法重载。
2.1区分重载方法
每个重载方法都必须有独一无二的参数类型列表。
2.2涉及基本类型的重载
基本类型能从一个小的类型自动提升为一个较大的类型,此过程一旦牵涉到重载,可能会造成一些混淆。如果传入的数据类型(实际参数类型)小于方法中声明的形式参数类型,实际数据类型就会被提升。
如果传入的实际参数较大,就得通过类型转换来执行窄化转换。如果不这样做,编译器会报错
2.3以返回值区分重载方法
void f(){}
int f(){}
//如果这样调用f(),java无法判断该调用哪一个,所以用返回值作为重载方法的区分是行不通的。
3,默认构造器
默认构造器(又名“无参”构造器)是没有形式参数的---它的作用是创建一个“默认对象”。但是如果你已经定义了一个构造器(无论是否有参数),编译器就不会帮你自动创建默认构造器。
4,this关键字
this关键字只能在方法内部使用,表示对“调用方法的那个对象”的引用。this的用法和其他对象引用并无不同。但要注意,如果在方法内部调用同一个类的另一个方法,就不必用this,直接调用即可。
4.1,在构造器中调用构造器
可能一个类有多个构造器,有时想在一个构造器中调用另一个构造器,以避免重复代码。this关键字可以做到这一点。尽管可以用this调用一个构造器,但却在一个不能在构造器中同时调用两个。除构造器之外,编译器禁止在其他任何方法中调用构造器。
4.2,static的含义
static方法就是没有this的方法。在static方法内部不能调用非静态方法,反过来倒是可以。
5,清理:终结处理和垃圾回收
java里的对象并非总是被垃圾回收:
1,对象可能不被垃圾回收
2,垃圾回收并不等于“析构”
3,垃圾回收只与内存有关
5.1,finalize()用途何在?
5.2,你必须实施清理
记住:无论是垃圾回收还是终结,都不保证一定会发生。如果java虚拟机并未面临内存耗尽的情形,它是不会浪费时间去执行垃圾回收以及恢复内存的。
5.3,终结条件
5.4,垃圾回收如何工作
6,成员初始化
6.1,指定初始化
在定义类成员变量的位置对其进行赋值。
7,构造器初始化
7.1,初始化顺序
在类的内部,变量定义的先后顺序决定了初始化顺序。即使变量定义散布于方法定义之间,它们仍会在任何方法(构造方法)调用之前得到初始化。例如:
//: initialization/OrderOfInitialization.java
// Demonstrates initialization order.
import static net.mindview.util.Print.*;
// When the constructor is called to create a
// Window object, you'll see a message:
class Window {
Window(int marker) { print("Window(" + marker + ")"); }
}
class House {
Window w1 = new Window(1); // Before constructor
House() {
// Show that we're in the constructor:
print("House()");
w3 = new Window(33); // Reinitialize w3
}
Window w2 = new Window(2); // After constructor
void f() { print("f()"); }
Window w3 = new Window(3); // At end
}
public class OrderOfInitialization {
public static void main(String[] args) {
House h = new House();
h.f(); // Shows that construction is done
}
} /* Output:
Window(1)
Window(2)
Window(3)
House()
Window(33)
f()
*///:~
7.2,静态数据初始化
无论创建多少个对象,静态数据都只占用一份存储区域。static关键字不能应用于局部变量,因此它只能作用于域。如果一个域是静态的基本类型域,且也没有对它进行初始化,那么它就会获得基本类型的标准初值。如果它是一个对象引用,它的默认初始化值就是null。
//: initialization/StaticInitialization.java
// Specifying initial values in a class definition.
import static net.mindview.util.Print.*;
class Bowl {
Bowl(int marker) {
print("Bowl(" + marker + ")");
}
void f1(int marker) {
print("f1(" + marker + ")");
}
}
class Table {
static Bowl bowl1 = new Bowl(1);
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);
Cupboard() {
print("Cupboard()");
bowl4.f1(2);
}
void f3(int marker) {
print("f3(" + marker + ")");
}
static Bowl bowl5 = new Bowl(5);
}
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();
} /* Output:
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)
*///:~
由输出可见,静态初始化只有在必要时刻才会进行。如果不创建table对象,也不引用Table.b1或Table.b2,那么静态的Bowl b1和b2永远不会创建。只有在第一个Table对象被创建或者第一次访问静态数据时,它们才会被初始化。以后静态对象不会再被初始化。初始化顺序是先静态对象再非静态对象。
7.3,显示的静态初始化
静态代码块只执行一次。
//: initialization/ExplicitStatic.java
// Explicit static initialization with the "static" clause.
import static net.mindview.util.Print.*;
class Cup {
Cup(int marker) {
print("Cup(" + marker + ")");
}
void f(int marker) {
print("f(" + marker + ")");
}
}
class Cups {
static Cup cup1;
static Cup cup2;
static {
cup1 = new Cup(1);
cup2 = new Cup(2);
}
Cups() {
print("Cups()");
}
}
public class ExplicitStatic {
public static void main(String[] args) {
print("Inside main()");
Cups.cup1.f(99); // (1)
}
// static Cups cups1 = new Cups(); // (2)
// static Cups cups2 = new Cups(); // (2)
} /* Output:
Inside main()
Cup(1)
Cup(2)
f(99)
*///:~
7.4非静态实例初始化
java中也有被称为实例初始化的类似语法,用来初始化每一个对象的非静态变量。
//: initialization/Mugs.java
// Java "Instance Initialization."
import static net.mindview.util.Print.*;
class Mug {
Mug(int marker) {
print("Mug(" + marker + ")");
}
void f(int marker) {
print("f(" + marker + ")");
}
}
public class Mugs {
Mug mug1;
Mug mug2;
{
mug1 = new Mug(1);
mug2 = new Mug(2);
print("mug1 & mug2 initialized");
}
Mugs() {
print("Mugs()");
}
Mugs(int i) {
print("Mugs(int)");
}
public static void main(String[] args) {
print("Inside main()");
new Mugs();
print("new Mugs() completed");
new Mugs(1);
print("new Mugs(1) completed");
}
} /* Output:
Inside main()
Mug(1)
Mug(2)
mug1 & mug2 initialized
Mugs()
new Mugs() completed
Mug(1)
Mug(2)
mug1 & mug2 initialized
Mugs(int)
new Mugs(1) completed
*///:~
练习15:编写一个含有字符串域的类,并采用实例初始化方式进行初始化。
class Main {
String s ;
{
s = "young for you";
}
public Main(){
System.out.println("s="+s);
}
public static void main (String args[]){
new Main();
}
}/*Output:
s=young for you
*/
8,数组初始化
数组只是相同的类型,用一个标识符名称封装到一起的一个对象序列或基本类型数据序列,数组是通过方括号下标符[ ]来定义和使用的。定义一个数组,只需要在类型名后加上一个空方括号就可以了,方括号也可以置于标识符后面
int[] a;
int a[];
编译器不允许指定数组大小。现在拥有的只是对数组的一个引用,并没有为数组分配内存空间
数组初始化的三种方式:
a) String [] a=new String[length];
b) String [] a=new String[] {,,,....};
c) String [] a={,,,,,,......};
三种定义的原理和效果基本一致。
在java中可以将一个数组赋值给另一个数组,其实真正做的只是复制了一个引用。
如果在编写程序时,并不能确定在数组里有多少个元素。可以直接用new在数组里创建元素。尽管创建的是基本数据类型,new任然可以工作(不能用new创建单个的基本类型数据)。
8.1,可变参数列表
有了可变参数,就再也不用显式的编写数组语法了,当你指定参数时,编译器实际上会为你去填充数组。
9,枚举类型