前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java编程思想第五版精粹(五)-初始化和清理(下)

Java编程思想第五版精粹(五)-初始化和清理(下)

作者头像
JavaEdge
发布2020-05-27 10:15:22
4550
发布2020-05-27 10:15:22
举报
文章被收录于专栏:JavaEdge

1

成员初始化

Java 尽量保证所有变量在使用前都能得到恰当的初始化。

1.1

局部变量

以编译时错误的方式呈现。编译器可以为 i 赋一个默认值,但是未初始化的局部变量更可能是程序员的疏忽,所以强制程序员提供一个初始值,往往能帮助找出程序里的 bug。

1.2

类的成员变量

类的成员变量是基本类型时,每个基本类型数据成员保证都会有一个初始值。

引用的默认值为 null

1.3

指定初始化

给一个变量赋初值,一种很直接的方法是在定义类成员变量的地方为其赋值。

也可以通过调用某个方法来提供初值:

2

构造器初始化

灵活的方式。因为可以在运行时调用方法进行初始化。但这无法阻止自动初始化,它会在构造器被调用前发生。因此,如果使用如下代码:

代码语言:javascript
复制
public class Counter {
    int i;
    
    Counter() {
        i = 7;
    }
    // ...
}

i 首先会被初始化为 0,然后变为 7。

这种情形对于所有的基本类型和引用及在定义时已指定初值的变量,都成立。

因此,编译器不会强制你要在构造器的某个地方或在使用它们前初始化——初始化早已得到了保证。

2.1

初始化的顺序

在类中变量定义的顺序决定了它们初始化的顺序。

即使将变量的定义分散在了各个方法的定义之间,变量们仍会在任何方法(包括构造器)被调用前得到初始化。

2.2

static数据的初始化

静态数据只占用一份存储区域。

static 不能用于局部变量,只能作用于属字段。

  • 如果一个字段是static的基本类型,你没有初始化它,那么它就会获得基本类型的标准初值
  • 如果它是对象引用,那么它的默认初值就是 null。

如果在定义时进行初始化,那么静态变量看起来就跟非静态变量一样。

静态初始化只有在必要时刻才会进行。如果不创建实例,也不引用静态类,那么静态的类对象永远不会被创建。只有在第一个实例对象被创建(或被访问)时,它们才会被初始化。

初始化的顺序是先静态对象(如果之前没有被初始化),然后是非静态对象。

2.3

创建对象的过程

假设有个类 Dog,总结:

  1. 虽然没有使用static,构造器实际上也是static方法。所以,当首次创建 Dog 对象或是首次访问 Dog 类的静态方法/属性,Java 解释器必须在类路径中查找,以定位 Dog.class
  2. 当加载完 Dog.class(即创建一个 Class 对象),静态初始化的所有动作都会执行。因此,静态初始化只会在首次加载 Class 对象时初始化一次
  3. 当用 new Dog() 创建,首先在堆给 Dog 分配存储空间
  4. 分配的存储空间首先被清零,将 Dog 对象中的所有字段置为默认值
  5. 执行所有字段定义处的初始化
  6. 执行构造器

2.4

显式的静态初始化

可以将一组静态初始化动作放在静态块,一段跟在 static 关键字后面的代码块。当首次创建这个类的对象或首次访问这个类的静态成员(甚至不需要创建该类的对象)时

2.5

非静态实例初始化

实例初始化的类似语法,初始化每个对象的非静态变量。

看起来像静态代码块,只不过没static。实例初始化子句是在两个构造器之前执行的。

代码语言:javascript
复制
public class Mugs {
    Mug mug1;
    Mug mug2;
    {
        mug1 = new Mug(1);
        mug2 = new Mug(2);
        System.out.println("mug1 & mug2 initialized");
    }
}    

3

数组初始化

数组是相同类型的、用一个标识符名称封装到一起的一个对象序列或基本类型数据序列。通过方括号下标操作符 [] 来定义和使用。

要定义一个数组引用,只需在类型名加上方括号,方括号也可在标识符后。

代码语言:javascript
复制
int[] a1; // 更合理,表明是"一个 int 数组类型
int a1[]; // 符合 C/C++ 习惯

编译器不允许指定数组的大小。你所有的只是对数组的一个引用(已为该引用分配了足够存储空间),还没给数组对象分配空间。

为了给数组创建相应的存储空间,必须写初始化表达式。对于数组,初始化动作可以出现在代码的任何地方。

也可以使用一种特殊的初始化表达式,必须在创建数组地方。由一对花括号括起来的值组成。这时,存储空间的分配(相当于 new) 由编译器负责。

代码语言:javascript
复制
int[] a1 = {1, 2, 3, 4, 5};

在 Java 中可以将一个数组赋值给另一个数组,其实真正做的只是复制了一个引用。

数组都有一个固定成员 length:数组有多少元素,无法修改。

发生越界访问时:

  • C 和 C++ 会默认接受,并允许你访问所有内存,内存越界bug由此而生
  • Java 会报运行时错误(异常)

3.1

动态数组创建

不确定数组中需要多少元素怎么办?

可以直接使用 new 在数组中创建元素。下面例子中,尽管创建的是基本类型数组,new 仍然可以工作(不能用 new 创建单个的基本类型数组):

代码语言:javascript
复制
int[] a = new int[rand.nextInt(20)];

创建了一个非基本类型的数组,那么其实创建的是一个引用数组。如果忘记了创建对象,但试图使用数组中的空引用,就会在运行时产生异常。

可以用花括号括起来的列表来初始化数组:

代码语言:javascript
复制
Integer[] a = {
        1, 2,
        3, // Autoboxing 初始化列表的最后逗号可选
           // 维护长列表更容易,但只能用于数组定义处。
};
Integer[] b = new Integer[] {
        1, 2,
        3, // Autoboxing
};

第二种和第三种形式可以用在任何地方,甚至用在方法的内部。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/05/04 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档