Java编程思想---第五章 初始化与清理(下)

第五章 初始化与清理(下)

5.7 构造器初始化

  可以使用构造器来进行初始化,在运行时可以调用方法或执行某些动作来确定初值,但是我们无法阻止自动初始化的进行,它将在构造器被调用之前发生。例如:

public class Counter {
  int i;
  Counter() { i = 7; }
}

  那么i首先被置为0,然后变成7.编译器不会强制你一定要在构造器的某个地方或在使用它们之前对元素进行初始化,因为初始化早已得到了保证。

5.7.1 初始化顺序

  在类的内部,变量定义的先后顺序决定了初始化的顺序,即使变量定义散布于方法定义之间,它们仍旧会在任何方法(包括构造器)之前得到初始化。例如:

package com.example.demo;

class Window {
    Window(int marker){
        System.out.print("Window(" + marker + ")\n");
    }
}

class House{
    Window w1 = new Window(1);

    House(){
        System.out.print("House()\n");
        w3 = new Window(33);
    }

    Window w2 = new Window(2);

    void f(){
        System.out.print("f()\n");
    }

    Window w3 = new Window(3);
}
public class test {
    public static void main(String[] args) {
        House h = new House();
        h.f();
    }
}

  输出结果为:

  由输出可见,w3这个引用会被初始化两次,一次是在调用构造器前,一次是在调用期间,第一次引用的对象将被丢弃并作为垃圾回收。

5.7.2 静态数据的初始化

  无论创建多少个对象,静态数据都只占用一份存储区域,static关键字不能应用于局部变量,因此它只能作用于域。如果一个域是静态的基本类型域,且没有对它进行初始化,那么它就会获得基本类型的标准初值,如果他是一个对象引用,那么他的默认初始化值及时null。

  看看下面这个例子:

  

package com.example.demo;

class Bowl {
    Bowl(int marker) {
        System.out.print("Bowl(" + marker + ")\n");
    }

    void f1(int marker) {
        System.out.print("f1(" + marker + ")\n");
    }

}

class Table {
    static Bowl bowl1 = new Bowl(1);

    Table() {
        System.out.print("Table()\n");
        bowl2.f1(1);
    }

    void f2(int marker) {
        System.out.print("f2(" + marker + ")\n");
    }

    static Bowl bowl2 = new Bowl(2);
}

class Cupboard {
    Bowl bowl3 = new Bowl(3);
    static Bowl bowl4 = new Bowl(4);

    Cupboard() {
        System.out.print("Cupboard()\n");
        bowl4.f1(2);
    }

    void f3(int marker) {
        System.out.print("f3(" + marker + ")\n");
    }

    static Bowl bowl5 = new Bowl(5);
}

public class StaticInitialization {
    public static void main(String[] args){
        System.out.print("Creating new Cupboard() in main\n");
        new Cupboard();
        System.out.print("Creating new Cupboard() in main\n");
        new Cupboard();
        table.f2(1);
        cupboard.f3(1);
    }

    static Table table = new Table();
    static Cupboard cupboard = new Cupboard();

}

  输出结果如下:

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)

  由输出可见,静态初始化只有在必要时才会进行,此后,静态对象不会再次被初始化。

  初始化的顺序是先静态对象,而后是非静态对象。从输出结果可以观察到这一点,要执行main()就必须加载StaticInitialization类,然后其静态域table和cupboard被初始化,浙江导致他们对应的类也被加载,并且由于他们也都包含静态的Bowl对象,因此Bowl随后也被加载,这样,在这份特殊的程序中的所有类在main()开始之前就都被加载了。但实际情况通常并非如此,不会像这个例子一样把所有的事物都通过static联系起来。

  总结:假设有一个名为Dog的类,

1、即使没有显示使用static关键字,构造器实际上也是静态方法,当首次创建Dog对象时,或者Dog类的静态方法/静态域被首次访问时,Java解释器必须查找类路径,以此定位Dog.class文件。

2、然后载入Dog.class,有关静态初始化的所有动作都会执行,因此静态初始化只在Class对象首次加载的时候进行一次。

3、当用new Dog()创建对象时,首相将在堆上为Dog对象分配足够的存储空间。

4、这块存储空间会被清零,这就自动地将Dog对象中所有基本类型数据都设置成了默认值,而引用则被设置为null。

5、执行所有出现于字段定义处的初始化动作。

6、执行构造器。

5.7.3 显式的静态初始化

  Java允许将多个静态初始化动作组织成一个特殊的静态字句(静态块),例如:

public class Spoon {
  static int i;

  static {
    i = 47;
  }
}

  尽管上面的方法看起来像个方法,但它实际只是一段跟在static关键词后的代码,与其他静态初始化动作一样,这段代码仅执行一次。

5.7.4 非静态实例初始化

  Java中也有被称为实例初始化的类似语法,用来初始化每一个对象的非静态变量。看起来与静态初始化子句一模一样只不过少了static关键字。这种语法对于支持匿名内部类的初始化是必须的,但它也是的你可以保证无论调用了哪个显式构造器,某些操作都会发生。实例化子句是在构造器之前执行的。

5.8 数组初始化

  数组是相同类型的、用一个标识符名称封装到一起的一个对象序列或基本类型数据序列。数组是通过方括号下标操作符[]来定义和使用的,要定义一个数组只需要在类型后加上一对空的方括号即可:

int[] a1;

int a1[];

  两种格式的含义是一样的,后一种更符合C和C++程序员的习惯,不过前一种格式或许更合理。编译器不允许指定数组的大小。

  在Java中可以将一个数字赋值给另一个数组: a2 = a1,但是真正做的只是复制了一个数组赋值给另一个数组,示例:

package com.example.demo;

public class ArrayOfPrimitives {
    public static void main(String[] args) {
        int[] a1 = { 1, 2, 3, 4, 5};
        int[] a2;
        a2 = a1;

        for(int i = 0; i < a2.length; i++) {
            a2[i] = a2[i] + 1;
        }

        for(int i = 0; i < a1.length; i++) {
            System.out.print("a1[" + i + "] = " + a1[i] + "\n");
        }
    }
}

  输出结果为:

a1[0] = 2

a1[1] = 3

a1[2] = 4

a1[3] = 5

a1[4] = 6

  可以看出,代码中给出了a1的初值但a2却没有,在上述代码中,a2是在后面被赋给另一个数组,由于a2和a1是相同数组的别名,因此通过a2所做的修改可以在a1中可以看到。

  所有数组都有一个固定成员,也就是length,可以通过他获取数组包含元素个数,且不可以修改。Java数组和C、C++类似,从0开始计数,最大下标为length-1,要是超出这个边界,Java会出现运行异常。

5.8.1 可变参数列表

  第二种形式提供了一张方便的语法来创建对象并调用方法,以获得与C的可变参数列表一样的效果,这可以应用于参数个数或类型未知的场合,由于所有的类都直接或间接继承于Object类,所以可以创建Object数组为参数的方法,像下面这样调用:

  

package com.example.demo;

class A {}

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()});
    }
}

  输出结果如下:

47 3.14 11.11

one two three

[email protected] [email protected] [email protected]

  可以看到print方法使用Object数组作为参数,使用foreach语法进行遍历,打印每个对象,打印出的内容只是类的名称已经后面跟一个@符号以及多个十六进制数字,于是默认就是打印类的名字和对象地址。有了可变参数,就再也不用显示编写数组语法了,当你指定参数时编译器会为你去填充数组。

5.9 枚举类型

  在Java SE5中添加了一个看似很小的特性,即enum关键字,它是的我们在需要群组合并使用枚举类型集时,可以很方便地处理,在此之前你需要创建一个整数常量集,但是这些枚举值并不会必然的将其自身的取值限制在这个常量集的范围之内,因此他们显得更有风险,并且难以使用。一个简单的例子:

public enum Spiciness {
  NOT, MILD, MEDIUM, HOT, FLAMING
}

  这里创建了一个名为Spiciness的枚举类型,它具有五个具名值,由于枚举类型的实例是常量,因此按照命名惯例它们都适用大写字母表示。

  为了适用enum,需要创建一个该类型的引用,并将其赋值给某个实例:

public class SimpleEnumUse {
  public static void main(String[] args) {
    Spiciness howHot = Spiciness.MEDIUM;
    System.out.println(howHot);
  }
}

  输出结果为:MEDIUM

  在创建enum时,编译器会自动添加一些有用的特性,例如toString(),你可以很方便地显示某个enum实例的名字;ordinal(),用来表示某个特定enum常量的声明顺序;static values(),用来按照enum常量的声明顺序,产生由这些常量值构成的数组:

public class VarArgs {
    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

  由于switch是要在有限的可能值集合中进行选择,因此它与enum是绝佳的组合。大体上你可以将enum用作另外一种创建数据类型的方式,然后直接将所得到的类型拿来使用。

原文地址:https://www.cnblogs.com/parable/p/11444586.html

时间: 2024-08-09 17:23:55

Java编程思想---第五章 初始化与清理(下)的相关文章

Java 编程思想 第五章 ----初始化与清理(1)

从今天开始每天一小时的java 编程思想的阅读和编码,其实就是把书上的代码抄下来. 5.5 清理:终结处理和垃圾回收 初始化和清理工作同等重要,但是清理工作却被常常忘记,但是在使用对象之后,对对象弃之不顾的做法并不是很安全.Java有自己的垃圾回收器负责回收无用的对象占据的内存资源.但也有特殊情况:假定你的内存区域不是用new获得的,这是无法用垃圾回收器释放所以java中允许在类中定义一个名为 finalize()的方法.       工作原理: 一旦垃圾回收器准备好释放对象占用的存储空间,将首

Java 编程思想 第五章 初始化与清理 上

休整几天,闲了蛋疼也没写文章,这开学了坚持每天写// 必须的天天写.不敢再松懈了.羡慕一好朋友能坚持的静下心来学习. 5.1 用构造器确保初始化 在Java中,通过提供构造器,类的设计者可确保每个对象都会得到初始化.  创建对象时,如果其类具有构造器,Java就会在用户有能力操作兑现之前自动调用相应的构造器,从而保证了初始化的进行.  当然了 接下来的我们需要的问题是: 1.所取的任何名字都可能与类的某个成员名称相冲突: 2.调用股早期是编译器的责任,所以必须让编译器知道应该调用哪个方法: 采取

Java编程思想:第5章 初始化与清理

随着计算机革命的发展,“不安全”的编程方式已经逐渐成为编程代价高昂的主因之一. 初始化和清理,正是涉及安全的俩个问题. 5.1 用构造器确保初始化 构造器名与类名相同,没有返回值 5.2 方法重载 构造器的重载与方法重载 5.2.1 区分重载的方法 参数列表的不同(不同顺序也是重载,但不建议这么做) 5.2.2 涉及基本类型的重载 void print(char c){System.out.println("The character is "+c); }    void print(

Thinking In Java笔记(第五章 初始化与清理(三))

第五章 初始化与清理 5.6 成员初始化 Java尽力保证:所有变量在使用前都能得到恰当的初始化.对于方法的局部变量,Java以编译错误的形式来保证.如下: void f() { int i; i++; //Error:i not initialized } 会得到一条错误的消息,提示i可能没有初始化.编译器可以给i赋初值,但是并没有这么做,因为没有初始化是程序员的疏忽,为了保证程序的稳定性,能帮助程序员找出程序的缺陷所在. 但如果是类的数据成员是基本类型,类的每个基本类型成员变量都会保证有一个

Thinking In Java笔记(第五章 初始化与清理(二))

第五章 初始化与清理(二) 5.5 清理:终结处理和垃圾回收 清理的工作常常被忽略,Java有垃圾回收器负责回收无用对象占据的内存资源.但也有特殊情况:假定对象(并非使用new)获得了一块"特殊"的内存区域,由于垃圾回收器只知道释放那些由new分配的内存,所以不知道如何释放特殊内存.Java允许在类中定义一个名为finalize()的方法,工作原理"假定"是这样的:一旦垃圾回收器准备好释放对象占用的存储空间,首先调用其finalize()方法,并且在下一次垃圾回收动

Java编程思想笔记(第二章)

第二章  一切都是对象 尽管Java是基于C++的,但相比之下,Java是一种更纯粹的面向对象程序设计语言. c++和Java都是杂合型语言(hybird language) 用引用(reference)操作对象 类似遥控器(引用)来操作电视(对象) 在Java中你可以创建一个引用,但是没有与任何对象关联,比如: String s; 这个时候如果用则会报错.安全的做法是: 创建一个引用的同时并进行初始化 String s="1111"; 必须由你创建所有对象 New关键字的意思是给我一

Java编程思想——第17章 容器深入研究(two)

六.队列 排队,先进先出.除并发应用外Queue只有两个实现:LinkedList,PriorityQueue.他们的差异在于排序而非性能. 一些常用方法: 继承自Collection的方法: add 在尾部增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常 remove 移除并返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常 element 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementExce

java编程思想笔记(第一章)

Alan Kay 第一个定义了面向对象的语言 1.万物皆对象 2.程序是对象的集合,他们彼此通过发送消息来调用对方. 3.每个对象都拥有由其他对象所构成的存储 4.每个对象都拥有其类型(TYpe) 5.某一特定类型的所有对象都可以接收同样的消息. Booch提出一种更简洁的描述: 对象拥有状态(state) 行为(behavior) 和标识(identity) 每个对象都有一个接口 每个对象都属于定义了特性和行为的某个类(特性可以理解为属性的状态,行为可以理解为method) 在面向对象的程序设

java 编程思想 一 第二章(对象)

上班之余发现有很多空闲时间,享受生活 又觉得有点空虚,而且对自己的基础知识总觉得掌握的不是很牢固,有点似懂非懂的感觉,近来刚好有时间,所以就考虑继续学习,然后再经过考虑和各大博主推荐选择了<java编程思想>这本书,在此分享学习心得跟大家共勉,也算是对自己的监督吧.(本内容需要有一定的基础才能看,类似于基础回顾,强化理解,新手可能有些地方不太能听懂) 一.什么是对象? 这并不是我们男女朋友那种对象哈哈. 简言之:万事万物皆对象. 个人理解:我们所要处理的事务或者建立的某种模型的抽象总结.具体就