《JAVA编程思想》日志(五)--------初始化与清理

来源:互联网 发布:java 圣思园教程百度云 编辑:程序博客网 时间:2024/06/05 09:53

本章主要讲解java的构造器(构造方法)和“垃圾处理器”。

5.1 用构造器确保初始化

首先,构造器的方法名与类名相同,保证了构造器名称不会与类中成员命名冲突,同时编译器在初始化期间能够自动明确调用该构造器。在java中,“初始化”与“创建”捆绑在一起,不能分离。例如,在创建对象时,new Rock();将会为对象分配存储空间,并调用相应的构造器,以确保在你能操作对象前,它已经被恰当的初始化。(***)

其次,不接受任何参数的构造器叫做默认构造器,也是无参构造器。当然,也可以带有形式参数,例如,

package init;public class Construstor {public static void main(String[] args){for(int i=0;i<8;i++){ new Rock(i);             //因为构造方法是带有形式参数的,所以在初始化对象时就提供实际参数}}}class Rock{Rock(int i){                             //构造方法含参System.out.println("Rock"+i+"");}}

构造器是一种特殊类型的方法,因为没有返回值,不会返回任何东西,不同于void。

package init;public class UseThis {String s;void print(){System.out.println(s);}public static void main(String[] args) {UseThis u=new UseThis(); //静态方法必须通过对象的引用才能调用非静态方法u.print();               //用于测试未初始化的string变量的默认值 ,结果输出null     }}

5.2方法重载

为了让方法名相同而形式参数不同的构造器同时存在,必须用到方法重载。每个重载的方法都必须有一个独一无二的参数形式列表。

5.2.1涉及基本类型的重载

基本类型能从一个“较小”的类型自动提升为一个“较大”的类型。

如果传入的数据类型(实际参数类型)小于方法中声明的形式参数类型,实际数据类型会被提升;

char类型不同,如果无法找到恰好接收char参数的方法,就会把char直接提升为int类型;

如果传入的实际参数大于重载方法声明的形式参数,会发生窄化转换,如果不这样做,编译器会报错。

5.2.2不能用返回值来区分重载方法

5.3默认构造器

它的作用是创建一个“默认对象”。

public class Construstor {public static void main(String[] args){<span style="white-space:pre"></span>//Rock r=new Rock();           //(1)Rock r=new Rock(1);}}
//class{}                                     //如果你写的类中没有构造器,编译器会自动创建一个默认构造器,(1)可以运行
class Rock{Rock(int i){                          //如果已经定义了构造器,无论是否有参数,编译器就不会帮你创建默认构造器,此时(1)报错System.out.println("Rock"+i+"");}}
5.4 this 关键字

this关键字只能在方法内部使用,表示对“调用方法的那个对象的引用”。this的用法和其他对象引用并无不同。但要注意,如果在方法内部调用同一个类的另一个方法,不需要使用this,直接调用即可。当前方法的this引用会自动应用于同一类的其他方法。

只有当需要明确指出对当前对象的引用时,才需要使用this关键字。例如,当需要返回对当前对象的引用时,return this;

this关键字对于将当前对象传递给其他方法很有用。(????书上例子没看懂)

package init;public class UseThis {static String s="hello";            //在静态的main函数输出s,必须是静态的数据成员,不能调用非静态方法和非静态数据成员,除非在静态方法里创建对象的UseThis(){                          //引用,用这个引用去调用非静态的数据成员和成员方法this("hi",47);}UseThis(String s,int i){//this.s=s;                //注释这行后,输出hi,hello;System.out.println(s);     }public static void main(String[] args) {UseThis u=new UseThis();System.out.println(s);}}          //但是,非静态方法可以访问静态数据成员和成员方法。
this.s=s;表示形式参数的复制给了数据成员的引用,因为这二者指向了同一个值。所以,程序中加上这句话后,输出结果相同,都是“hi”。因为参数s的名称与数据成员名字相同,所以使用this.s代替数据成员,this 代表的当前对象的引用。

5.4.1在构造器中调用构造器

可以为一个类写多个构造器,在一个构造器中调用另一个构造器(只能调用一个,且调用必须置于最起始处),以避免重复代码。在构造器中,如果为this添加了参数列表,将产生符合此参数列表的某个构造器的调用。

除构造器外,编译器禁止在其他任何地方调用构造器。

5.5 清理:终结处理和垃圾回收(此部分内容会在以后 单独提出,整理成文************************************)

(1)对象可能不被垃圾回收

(2)垃圾回收并不等于“析构”

(3)垃圾回收只与内存有关:垃圾回收器的唯一目的是回收程序不再使用的内存,无论对象时怎样创建的。

  java的垃圾回收机制并不是万能的。假定你的对象获得了一块“特殊”的内存区域,并非使用new,由于垃圾回收器只知道释放那些经由new分配的内存,所以它不知道该如何释放这块“特殊”内存。为了应对这种情况,java允许在类中定义finalize()方法,就能在垃圾回收时做一些重要的清理工作。

5.6成员初始化

package init;public class UseThis {String s;UseThis ut;                  void print(){System.out.println(s);System.out.println(ut);}public static void main(String[] args) {UseThis u=new UseThis(); //静态方法必须通过对象的引用才能调用非静态方法u.print();               //用于测试为初始化的string变量的默认值      }}
在类里定义了一个对象引用,如果不将其初始化,此引用就会获得一个特殊值null。在上述程序中,对象引用指的是ut,还是u????

5.6.1指定初始化

对于基本或非基本类型的对象,都可以在定义时初始化,int i=99;

也可以创建对象时初始化,

        UseThis u=new UseThis(1);     //如果没有为u指定初始值就尝试使用它,就会出现运行时错误。

    也可以通过某个方法初始化,int i=f( a);但 a 必须已经初始化才可以使用.

5.7 构造器初始化

牢记:无法阻止自动初始化的执行,它将在构造器被调用之前发生。

5.7.1初始化顺序

package init;class Window{Window(int i){System.out.println("window"+i);}}class House{Window w1=new Window(1);House(){System.out.println("house()");w3=new Window(33);  //w3会被初始化两次,第一次在构造器前,即(1)处,第二次在构造器内,也就是这里。第一次引用的对象被丢弃,并作为垃圾回收}Window w2=new Window(2);void f(){System.out.println("f()");//}Window w3=new Window(3);   //该定义也先于方法被调用前(1)}public class OrderOfInit {public static void main(String[] args) {House h=new House();    h.f();             //调用house的f方法,但是house类中的变量会先于方法调用之前被定义并初始化}}/*输出顺序:window1window2window3house()window33f()*/
5.7.2静态数据初始化

无论创建多少个对象,静态数据的都只占用一份存储区域。

static关键字不能应用于局部变量,只能作用于域。

如果一个域是基本类型,在没有初始化前,会获得默认初值,如果是对象引用,默认初始值是null。

static int i=47;

House h2=new House(2);

   static House h1=new House(1);     //静态成员初始化与非静态没有什么区别,但是要先于上面的非静态初始化(*****)

同时,静态初始化只在class对象首次加载时执行一次。而非静态初始化在每次class加载时都会执行。

5.7.3显式的静态初始化

“静态程序块”:

class Spoon{

static int i;

static {           //表示这段代码只会被执行一次:当首次生成这个类的对象时,或者首次访问属于这个类的静态数据成员时。

i=47;          

}

}

5.7.4非静态实例的初始化

package init;class Mug{Mug(int i){System.out.println("Mug"+i);}}public class Mugs {Mug mug1;Mug mug2;{mug1=new Mug(1);mug2=new Mug(2);         //实例初始化子句,相对于}//static Mug mug1;             //如果程序改成这样,输出也会改变,因为静态代码段,也会先于构造器执行,但是只执行一次//static Mug mug2;             /*Mug1//static{                        Mug2//mug1=new Mug(1);           Mugs//mug2=new Mug(2);           Mugs(int)//}Mugs(){System.out.println("Mugs");}Mugs(int i){System.out.println("Mugs(int)");}public static void main(String[] args) {new Mugs();new Mugs(1);}}/*Mug1Mug2MugsMug1Mug2Mugs()*///由输出可以看出,无论调用那个显式构造器,实例初始化子句都会先于构造器被调用
5.8数组初始化

int [] a;与int a[];含义相同,但是前一种 更合理,表明类型是“一个int型数组”。

package init;public class ArrayOfPrimitives {public static void main(String[] args) {int[] a1={1,2,3,4,5};   //直接在定义时初始化,此时存储空间的分配将由编译器负责int[] a2;           //编译器不允许指定数组大小,现在拥有的只是对数组的引用,而且已经为该引用分配了足够多的内存空间,而且也没有给数组对象本身分配任何空间a2=a1;                  //此时传递的是引用,a1,a2指向了同一个数组for(int i=0;i<a2.length;i++){   //数组的初始化动作可以在代码任何地方a2[i]=a2[i]+1;      //a2指向的值改变了,对应的a1指向的值也会改变  }for(int i=0;i<a1.length;i++){      //所有数组,无论是对象还是基本类型,都有一个固定成员lengthSystem.out.print(a1[i]+" ");}}}/*output:2 3 4 5 6*/
分别创建基本类型的数组和非基本类型的数组:

package init;import java.util.*;public class ArrayOfPrimitives {public static void main(String[] args) {Random rand=new Random(47);int[] a=new int[rand.nextInt(20)];     //定义随机大小的int型数组                                       //应该尽量在数组定义时将其初始化Integer[] b=new Integer[rand.nextInt(20)];//定义随机大小的非基本类型数组,此时还没有初始化,还是引用数组Integer[] c={new Integer(1),  //也可以用花括号括起来的列表初始化对象数组new Integer(2),3,               //逗号是可选的};System.out.println(Arrays.toString(a));//打印未初始化的a数组//System.out.println(Arrays.toString(b));//运行异常,因为b数组未初始化,是空引用System.out.println(Arrays.toString(c));for(int i=0;i<b.length;i++){b[i]=rand.nextInt(50);//只有当把对象赋值给引用后,初始化进程才算结束}System.out.println(Arrays.toString(b));//此时可以打印出b数组}}/*output:[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0][1, 2, 3][43, 11, 11, 29, 18, 0, 22, 7, 38, 28, 1, 39, 9, 28, 48]*/
5.8.1可变参数列表

应用于参数个数或类型位置未知的场合

以Object数组为参数的方法:

package init;class A{}public class VarArgs {static void printArray1(Object[] args){ //定义Object数组,与下面的方法进行比较for(Object obj:args){              //用foreach语法System.out.print(obj+" ");}System.out.println();}static void printArray2(Object... args){ //定义Object,可变参数列表,参数个数和类型不限for(Object obj:args){              //用foreach语法System.out.print(obj+" ");}System.out.println();}static void printArray3(int i,Object... args){ //也可以在可变参数列表中指定一个object之外的类型for(Object obj:args){              //用foreach语法System.out.print(obj+" ");}System.out.println();}//static void printArray3(Object... args){ //与上面的参数列表不同,方法名相同,但是这样做是不安全的//for(Object obj:args){                //会导致编译器运行错误,要用参数列表严格分开//System.out.print(obj+" ");//}//System.out.println();//}static void printArray3(char i,Object... args){ //将上面参数列表的int改为char就可以避免错误for(Object obj:args){              System.out.print("four");}System.out.println();}public static void main(String[] args) {printArray1(new Object[]           //对于第一种方法,需要创建object对象引用{new A(),new Integer(47),"one",1});//创建了四种不同的对象,其中new A输出的是类的名字和对象的地址//printArray1(1,2,3,4);  运行错误,必须创建object对象printArray1(new Object[]{new Integer(47),new Float(3.14),new Double(11.11)});printArray2(1,2,3,4);        //可以是任何类型,可以混合使用printArray2(new A(),new Integer(48),"two",2);printArray3(3,"three");      //参数列表中指定了一个int型printArray3('a',"three");    //调用的是第四个方法,因为a是char型的}}/*output:init.A@139a55 47 one 1 47 3.14 11.11 1 2 3 4 init.A@1db9742 48 two 2 three four*/
5.9 枚举类型--------enum关键字

package init;public enum Week {MON,TEU,WED,THU,FRI; //创建枚举类型集,因为枚举类型的实例是整型常量,要求都大写}
package init;public class EnumUse {public static void main(String[] args) {Week w1=Week.MON;   //给枚举类型实例化,引用w1指向某个实例System.out.println(w1);for(Week w:Week.values()){  //产生Week中常量值System.out.println(w+",ordinal "+w.ordinal());//表示enum常量的声明顺序}switch(w1){                          //枚举可应用于switch语句中case MON:System.out.println("周一");break;default:System.out.println("hello");}}}/*output:MONMON,ordinal 0TEU,ordinal 1WED,ordinal 2THU,ordinal 3FRI,ordinal 4周一*/



1 0
原创粉丝点击