第五章 初始化与清理
一、用构造器确保初始化
1、什么是构造器(constructor)?
创建对象时被自动调用的特殊方法。
2、通过构造器,类的设计者可以确保每个对象都会得到初始化。
3、不接受任何参数的构造器叫做默认构造器,
如果某个class具备构造函数,Java便会在对象生成之际,使用者有能力加以操作之前,自动调用其构造函数,于是便能名确保初始化动作一定被执行。
二、方法重载(method overloading)
什么是方法重载?
在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义。
如何区分重载方法?
规则很简单:每个重载的方法都必须有一个独一无二的参数类型列表。
由于只能从函数名和函数的引数列来区分两个函数,而重载函数具有相同的函数名称,所以每个重载函数都必须具备独一无二的引数列。
注意:Java的方法重载要求同名的方法必须有不同的参数表,仅有返回类型不同是不足以区分两个重载的方法。
方法重载具体规范
1、方法名一定要相同。
2、方法的参数表必须不同,包括参数的类型或个数,以此区分不同的方法体。
1)如果参数个数不同,就不管它的参数类型了!
2)如果参数个数相同,那么参数的类型或者参数的顺序必须不同。
3、方法的返回类型、修饰符可以相同,也可不同。
三、默认构造器
1、 default构造函数是一种不带任何引数的构造函数。如果你所开发的class不具任何构造函数,编译器会自动为你生成一个default构造函数。
2、如果你自行定义了任何一个构造函数(不论有无引数),编译器就不会为你生成default构造函数。
3、如果定义了一个class,如
class Bush{
Bush(int I){}
}
当想用new Bush();来产生class的实例时,会产生错误。因为在定义class时已定义了构造函数,所以编译器就不会为class生成default构造函数。当我们用new Bush()来产生实例时,会尝试调用default构造函数,但在class中没有default构造函数,所以会出错。如:
class Sundae
{
Sundae(int i) {}
}
public class IceCream
{
public static void main(String[] args)
{
//Sundae x = new Sundae();会编译出错,无构造函数Sundae()
Sundae y = new Sundae(1);
}
}
注意:在定义一个class时,如果定义了自己的构造函数,最好同时定义一个default构造函数
四、This关键字
1、 this仅用于函数之内,能取得“唤起此一函数“的那个object reference。
2、 在构造函数中,通过this可以调用同一class中别的构造函数,如
public class Flower{
Flower (int petals){}
Flower(String ss){}
Flower(int petals, String ss){
//petals++;调用另一个构造函数的语句必须在最起始的位置
this(petals);
this(ss); //会产生错误,因为在一个构造函数中只能调用一个构造函数
//Constructor call must be the first statement in a constructor
//this(petals,ss);
}
注意: 1)在构造调用另一个构造函数,调用动作必须置于最起始的位置
2)不能在构造函数以外的任何函数内调用构造函数
3)在一个构造函数内只能调用一个构造函数
3、Static含义
无法在static函数中调用non-static函数(反向可行)
非静态函数可以直接调用静态函数。为什么不能呢,我们看下面的例子。
例4.2.4.1
假设能在static函数中调用non-static函数,那么(a)处就将出错。因为在没有产生Movie class实例之前,在就不存在Movie class内的name实例,而在getName()中却要使用name实例,显然的错误的。
class Movie{
String name = "12";
Movie(){}
public Movie(String name) {
this.name = name;
}
public static String getName() {
return name;//Cannot make a static reference to the non-static field name
}
}
public class Test{
public static void main(String[] args){
//下面两名先产生实例后再调用getName()没有问题
Movie movie1 =new Movie("mo");
String name1 = movie1.getName();
//下面一名将出错
String name2 = Movie.getName();
}
}
五、清理:总结处理和垃圾回收
Java垃圾回收器只负责回收由new分配的内存,但不知道如何释放(并非使用new)获得的内存。
注意:1、对象可能不被垃圾回收
只有当程序不够内存时,垃圾回收器才会启动去回收不再被使用的对象的内存空间。某个对象所占用的空间可能永远不会被释放掉,因为你的程序可能永远不会逼近内存用完的那一刻,而垃圾回收器完全没有被启动以释放你的对象所占据的内存,那些空间便会在程序终止时才一次归还给操作系统。
2、垃圾回收并不等于“析构”(析构函数是c++中销毁对象必须用到的函数)
3、垃圾回收只与内存有关
只有在采用原生函数(native methods)时,才使用finalize()。
六、成员初始化
1、函数的变量不会被初始化
void f(){
int i;
i++;
}
会报错,提示i not initialized
2、
class的数据成员会被自动初始化,具体情况如下:
基本型别:boolean:false、char:null(\u0000)、byte:0、short:0、int:0、
long:0 、float:0、double:0 对象(reference):null
3、指定初始化:
1)可以为类的成员变量赋初值进行指定初始化
2)可以对类创建new对象进行指定初始化。
3)调用某个方法进行指定初始化
例如:
public class MethodInit1 {
int i=f();
int f(){
return 11;
}
}
4)调用带参数的方法进行指定初始化
public class MethodInit1 {
int i=f();
int j=g(i);
int g(int n){
return n*10;
}
int f(){
return 11;
}
}
5)调用的方法可以带有参数,但是参数必须是已经初始化的。
public class MethodInit1 {
int j=g(i);
int i=f();
int g(int n){
return n*10;
}
int f(){
return 11;
}
}
}
换一下顺序会报错,因为int j=g(i);其中i未初始化。上述程序的正确性取决于初始化顺序,而与编译方式无关。
七、构造器初始化
1、初始化顺序:在类的内部,变量定义的先后顺序决定了初始化顺序,即使变量定义散布于方法定义之间,他们仍旧会在任何方法(包括构造器)被调用之前得到初始化。
例如:
class Window{
Window(int marker){
System.out.println("Window("+marker+")");
}
}
class House{
Window window1=new Window(1);
House(){
System.out.println("House()");
window3 =new Window(33);
}
Window window2 = new Window(2);
//void f(){
//System.out.println("f()");
//}
Window window3=new Window(3);
}
public static void main(String[] args) {
House house=new House();
//house.f();
}
/*output
Window(1)
Window(2)
Window(3)
House()
Window(33)
//f()
2、静态数据初始化
静态数据初始化顺序:
1)先静态对象,而后是“非静态对象”
2)要执行main(),必须加载静态初始化类(Table 和Cupboard 初始化),这导致对应Bowl 类的先被执行 ,后执行非静态Cupboard初始化
如下例子:
package com.myfirst.test;
class Bowl{
Bowl(int marker){
System.out.println("Bowl("+marker+")");
}
void f1(int marker){
System.out.println("f1("+marker+")");
}
}
class Table{
static Bowl bowl1 = new Bowl(1);
Table(){
System.out.println("Table()");
bowl2.f1(1);
}
void f2(int marker){
System.out.println("f2("+marker+")");
}
static Bowl bowl2=new Bowl(2);
}
class Cupboard{
Bowl bowl3 = new Bowl(3);
static Bowl bowl4 = new Bowl(4);
Cupboard(){
System.out.println("Cupboard()");
bowl4.f1(2);
}
void f3(int marker){
System.out.println("f3("+marker+")");
}
staticBowl bowl5 = new Bowl(5);
}
public class StaticInitialzation {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("创建对象newcupboard");
new Cupboard();
System.out.println("创建对象newcupboard");
new Cupboard();
table.f2(10);
cupboard.f3(11);
}
static Table table=new Table();
static Cupboard cupboard= new Cupboard();
}
3、显式的静态初始化
Java允许多个静态初始化动作组织成一个特殊的“静态句子”
如下例子:静态初始化动作只执行一次
package com.myfirst.test;
class Cup{
Cup(int marker){
System.out.println("Cup("+marker+")");
}
void f1(int marker){
System.out.println("f1("+marker+")");
}
}
class Cups{
static Cup cup1;
static Cup cup2;
static{
cup1=new Cup(1);
cup2=new Cup(2);
}
Cups(){
System.out.println("cups()");
}
}
public class ExplicitSatic {
public static void main(String[] args) {
System.out.println("inside main()");
Cups.cup1.f1(99);
}
static Cups cups = new Cups();
static Cups cups1 = new Cups();
}
/*outpu
Cup(1)
Cup(2)
cups()
cups()
inside main()
f1(99)
4、非静态实例初始化
八、数组初始化
1、数组初始化实例:
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] a1={1,2,3,4,5};
int[] a2;
a2=a1;//将一个数组赋值给另一个数组
for(int i=0 ;i<a1.length;i++)
System.out.println("a1["+i+"]="+a1[i]);
//java中计数是从第0个元素开始的,所以能使用的最大下标数十length-1
System.out.println("----------------------");
for (int i = 0; i < a2.length; i++) {
a2[i]=a2[i]+1;
System.out.println("a2["+i+"]="+a2[i]);
}
System.out.println("-----------------------");
for(int i=0 ;i<a1.length;i++)
System.out.println("a1["+i+"]="+a1[i]);
}
/*output
a1[0]=1
a1[1]=2
a1[2]=3
a1[3]=4
a1[4]=5
----------------------
a2[0]=2
a2[1]=3
a2[2]=4
a2[3]=5
a2[4]=6
-----------------------
a1[0]=2
a1[1]=3
a1[2]=4
a1[3]=5
a1[4]=6
2、数组初始化实例二三
public class ArrayNew {
public static void main(String[] args) {
// TODO Auto-generated method stub
Random random=new Random();
//random.nextInt(15)取得15以内的随机数
int[] a = new int[random.nextInt(15)];
System.out.println("a的长度"+a.length);
//Arrays.toString(a)方法,他将产生一维数组的可打印版本。
System.out.println(Arrays.toString(a));
//Integer是一个类不是基本类型(以下是一个引用数组)
Integer[] b =new Integer[random.nextInt(20)];
System.out.println("b的长度"+b.length);
//Arrays.toString(b)由于interger是非基本类型,所以产生null
System.out.println(Arrays.toString(b));
for (int i = 0; i < b.length; i++) {
b[i]=random.nextInt(30);
}
System.out.println(Arrays.toString(b));
//--------使用花括号列表来初始化对象数组---------------
int[] c = {new Integer(1),new Integer(2),3};
System.out.println(Arrays.toString(c));
Integer[] d={new Integer(1),new Integer(2),3};
System.out.println(Arrays.toString(d));
}
}
/*output
a的长度12
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
b的长度9
[null, null, null, null, null, null, null, null, null]
[25, 27, 11, 25, 20, 7, 19, 15, 8]
[1, 2, 3]
[1, 2, 3]
3、可变参数列表:
1) object数组
使用foreach来迭代数组
package com.myfirst.test;
class A{}
public class VarArgs {
static void printArray(Object[] args){
for (Object object : args) {
System.out.print(object+" ");
}
System.out.println();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
printArray(new Object[]{new Integer(1),new Float(3.12),new Double(11.11)});
printArray(new Object[]{"s1","b2"});
//打印类的名称和对象的地址
printArray(new Object[]{new A(),new A()});
printArray((Object[]) new Integer[]{1,2,3});
//printArray();//会报错The method printArray(Object[]) in the type VarArgs is not applicable for the arguments ()
printArray(args);//it is ok
}
}/*output
1 3.12 11.11
s1 b2
com.myfirst.test.A@1a1c887 com.myfirst.test.A@743399
1 2 3
2) Object之外类型的可变参数列表实例
public class AarargType {
//character即是:char 固定长度 (char) 或可变长度 (varchar) 字符数据类型。
static void f(Character...args){
//args.getClass()将产生对象的类
System.out.print(args.getClass());
System.out.println(args.length);
}
static void g(int...args){
System.out.print(args.getClass());
System.out.println(args.length);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
f('a');
f();
g(1);
g();
System.out.println(new int[0].getClass());
}
}
九、枚举类型
1、枚举类型的实例是常量
2、用大写字母表示(如果一个名字中含有多个单词,用下划线表示)
3、创建enum时,编译器会自动添加一些有用的特征:如下
1)它会创建toString()方法,以便显示某个enum实例的名字
2)会创建ordinal()方法,用来表示特定enum常量的声明顺序
3)static values()方法,根据声明顺序,产生常量的数组
4、enum其中一个实用的特性,可以在swith语句使用
实例如下:
public enum Spiciness {
NOT,TONG,LOVE,YOU
}
public class EnumOrder {
public static void main(String[] args) {
Spiciness spiciness = Spiciness.LOVE;
System.out.println(spiciness);
for (Spiciness spiciness2 : Spiciness.values()) {
System.out.println(spiciness2+"ordinal"+spiciness2.ordinal());
}
}
}