Java中初始化的相关问题
目录
已经快半个月没写博客了,这周在看 Thinking in Java 这本书,准备将书中的第五章和第七章的内容整合一下,写出这篇博客,也算是自己对Java的一些基础知识的复习。
这篇博客要说的是Java中的初始化问题,说到初始化,自然想到的是成员变量和局部变量的初始化了。那么如何对成员变量和局部变量进行初始化就是我们要探讨的问题。
首先是
局部变量的初始化
来看下这段代码:
void f(){
int i;
i++; // ERROR -- i没有被初始化
}
这里的 i 是一个局部变量,当对 i 进行 i++ 操作的时候,编译器告诉我们这样是不行的,因为 i 没有被初始化。其实万能的编译器完全可以在这种情况下为 i 赋一个初值,但是它没有这样做,Thinking in Java 这本书告诉我们:未初始化的局部变量更有可能是程序员的疏忽,所以采用默认值反而会掩盖这种失误,因此强制程序员提供初值,往往更能帮助更快发现程序中的缺陷。
局部变量是初始化还是很好理解的,编译器不会自动初始化,只能由程序员自己手动提供初始化。而在成员变量上就完全不一样了,下面我们来看看成员变量的初始化是什么样的。
成员变量的初始化
成员变量的初始化又分为自动初始化和手动初始化,并且手动初始化也有一些独特之处,下面就来看看这些不同的地方:
public class InitialValues{
boolean v1;
char v2;
byte v3;
short v4;
int v5;
long v6;
float v7;
double v8;
InitialValues ref;
void print(){
System.out.println(v1);
System.out.println(v2);
System.out.println(v3);
System.out.println(v4);
System.out.println(v5);
System.out.println(v6);
System.out.println(v7);
System.out.println(v8);
System.out.println(ref);
}
public static void main(String[] args){
new InitialValues().print();
}
}
/* output:
false
[]
0
0
0
0
0.0
0.0
null
*/
可见当没有给成员变量赋初值的时候,Java的编译器会帮我们处理“ 未初始化变量 ”的风险。这种初始化是自动初始化,当然我们也可以手动的为成员变量进行初始化,就像下面这样:
public class InitialValues{
boolean v1 = true;
char v2 = 'a';
byte v3 = 47;
short v4 = 0xff;
int v5 = 3;
long v6 = 4;
float v7 = 3.14f;
double v8 = 3.14159;
String str = new String("hello world");
}
这时是在定义类的成员变量的地方为其赋值,看起来简单明了。可是这种做法有一定的局限性:由类 InitialValues 生成的每个对象都由相同的初始值。这对于程序设计而言是相当缺乏灵活性的一种做法,所以我们需要一种新的方式:在创建对象的时候才对每个对象的成员变量进行赋值,这样每个对象都可以有不同的成员变量数据。这种方式就是下面要说的:构造器初始化。
构造器初始化
通过构造器来对成员变量做初始化是相当灵活的,请看下面一段代码:
public class InitialValue{
int i;
InitialValue(int i){
this.i = i;
}
}
这样就可以在创建多个对象时,向构造器的形参中传入不同的值。
上面讲解了两种成员变量的初始化,可见第二种构造器初始化更具灵活性。但是有一点要牢记:即使手动进行初始化了,自动初始化过程一样会在手动初始化之前完成。也就是说如果执行 new InitialValue( 7 ) 那么 i 首先会被置0,然后变成7。这种特性对于所有的基本类型和对象引用都是成立的。
静态数据的初始化
这里说的静态数据指的是成员变量为静态的情况,因为 static 关键字不能应用于局部变量。静态数据有一个特点:无论创建多少个对象,静态数据都只占用一份内存。
通过下面的一段代码,可以了解到静态数据是如何初始化的:
class Bowl{
Bowl(int i){
System.out.println("Bowl(" + i + ")")
}
void f1(int i){
System.out.println("f1(" + i + ")")
}
}
clsss Table{
static Bowl b1 = new Bowl(1);
Table(){
b2.f1(1);
}
void f2(int i){
System.out.println("f2(" + i + ")")
}
static Bowl b2 = new Bowl(2);
}
class Cupboard{
Bowl b3 = new Bowl(3);
static Bowl b4 = new Bowl(4);
Cupboard(){
b4.f1(2);
}
void f3(int i){
System.out.println("f3(" + i + ")")
}
static Bowl b5 = new Bowl(5);
}
public class Test{
public static void main(String[] args){
new Cupboard();
new Cupboard();
}
static Table table = new Table();
static Cupboard cupboard = new Cupboard();
}
/* output:
Bowl(1)
Bowl(2)
f1(1)
Bowl(4)
Bowl(5)
Bowl(3)
f1(2)
Bowl(3) // 这是在main中通过new Cupboar() 打印出来的 start
f1(2)
Bowl(3)
f1(2) // 这是在main中通过new Cupboar() 打印出来的 end
f2(1)
f3(1)
*/
可见静态初始化只在必要的时候进行,即第一次访问对象的构造器(或者第一次访问类的静态数据)时静态数据才会被初始化(其实构造器也是一个static方法)。
首先编译器会试图访问 Test.main() ,因为 main() 是静态方法,要执行静态方法必须加载 Test 类,然后静态成员变量 table 和 cupboad 被初始化,这间接的导致他们的类被加载,由于他们都包含静态的 Bowl 成员变量,所以 Bowl 类也被加载。这三个类之间通过 static 关键字联系起来。
下面总结一下对象的创建过程,假设有一个名为Student的类:
- 当首次调用构造器new Student() ,或者第一次访问Student类的静态方法/成员变量时,Java解释器找到Student.class文件
- 然后载入Student.class文件,接着进行所有静态数据的初始化(这时对象还没创建呢!从这也能看出,静态数据与单个对象无关)
- 接下来创建Student对象,为对象在堆上分配空间
- 将分配到的存储空间清零,即将所有的非静态成员变量赋上对应的初始值
- 接下来如果在成员变量的定义出有初始化动作,那就再次执行相应的初始化
- 最后执行构造器的方法体,如果构造器有初始化动作,再次覆盖之前的初始化(到这里初始化和对象的创建就完成了)
总结
相比于C++来说,Java的初始化机制能规避很多一开始的编程错误。特别是通过堆构造器的使用,能给初始化很大的灵活度,初始化在Java中占有至关重要的地位,通过Java中的构造器能保证正确的初始化,没有正确的构造器调用,Java编译器是不允许创建对象的,编译器有了更多的控制,我们的程序也就更加安全。
Java中初始化的相关问题的更多相关文章
- Java中Date各种相关用法
Java中Date各种相关用法(一) 1.计算某一月份的最大天数 Java代码 Calendar time=Calendar.getInstance(); time.clear(); time.set ...
- java中的字符串相关知识整理
字符串为什么这么重要 写了多年java的开发应该对String不陌生,但是我却越发觉得它陌生.每学一门编程语言就会与字符串这个关键词打不少交道.看来它真的很重要. 字符串就是一系列的字符组合的串,如果 ...
- 你真的了解JAVA中与Webservice相关的规范和实现吗?
非常多人在项目中使用Webservice,仅仅是知道怎样公布Webservice,怎样调用Webservice,但真要论其来龙去脉,还真不一定清楚. 一切一切还要从我们伟大的sun公司规范说起. JA ...
- java中初始化块、静态初始化块和构造方法
(所谓的初始化方法init()是另一回事, 在构造方法之后执行, 注意不要混淆) 在Java中,有两种初始化块:静态初始化块和非静态初始化块.它们都是定义在类中,用大括号{}括起来,静态代码块在大括号 ...
- java中初始化方法
本文主要是讲从<java编程思想>中看到的东西,是第七章复用类的开头内容,主要是类初始化的几种方法的位置,主要包括 1.在定义对象的地方.这意味着他们总是在构造器被调用前被初始化. 2.在 ...
- [转]java中的字符串相关知识整理
字符串为什么这么重要 写了多年java的开发应该对String不陌生,但是我却越发觉得它陌生.每学一门编程语言就会与字符串这个关键词打不少交道.看来它真的很重要. 字符串就是一系列的字符组合的串,如果 ...
- Java中SMB的相关应用
目录 SMB 服务操作 Ⅰ SMB简介 Ⅱ SMB配置 2.1 Windows SMB Ⅲ 添加SMB依赖 Ⅳ 路径格式 Ⅴ 操作共享 Ⅵ 登录验证 SMB 服务操作 Ⅰ SMB简介 SMB(全称 ...
- java中数组的相关知识
1. 2.数组的命名方法 1)int[]ages=new int[5]; 2) int[]ages; ages=new int[5]; 3)int[]ags={1,2,3,4,5}; 4)int[ ...
- java中初始化对象变量的方法
1.在类定义对象的地方初始化 2.在类构造器中初始化 3.在正要使用这些对象之前,惰性初始化,或者叫惰性载入 4.使用实例初始化 在方法里使用初始化
随机推荐
- mongodb 批量更新 数组的键操作的文件
persons该文件的数据如下面的: > db.persons.find() { "_id" : 2, "name" : 2 } { "_id& ...
- 国产操作系统剽窃Linux内核可耻!
10月28日,新浪科技发表文章,题为"国产操作系统迎来利好:部委机构正大量採购",读后有感. 如今,国家进入法制时代.政府採购"国产操作系统",似乎成为一种&q ...
- poj 3071 可能DP
http://poj.org/problem? id=3071 推方程不难,可是难在怎么算 dp[i][j]表示第i场时第j仅仅队伍存活下来的概率 方程:dp[i][j]=sigma(dp[i-1][ ...
- Android--在Android应用中愉快地写C/C++代码(转)
1 前言 一直想在android层面写c进程,然后java可以与c进程交互,以前在android源码中想玩就可以直接在init.rc中加上交叉编译好的c进程就可以了,而在ide中,也就是ndk编译后各 ...
- tar.gz文件命名及压缩解压方法
tar.gz文件命名 tar是把文件打成一个包,并不压缩; gz是用gzip把打成包的.tar文件压缩; 所以成了一个.tar.gz的文件 压缩 # tar cvfz backup.tar.gz /x ...
- 上传图片时,使用GDI+中重绘方式将CMYK图片转为RGB图片
原文:上传图片时,使用GDI+中重绘方式将CMYK图片转为RGB图片 我们知道,如果网站上传图片时,如果用户上传的是CMYK图片,那么在网站上将是无法显示的,通常的现象是出现一个红叉.下面使用将Ima ...
- WPF中CAD control的XAML实现
原文:WPF中CAD control的XAML实现 下面这个XAML文件是cad control里面最重要的一部分,使用Grid包含Viewport,Viewport中包括Camera和mod ...
- Robot Framework 快速入门_中文版
目录 介绍 概述 安装 运行demo 介绍样例应用程序 测试用例 第一个测试用例 高级别测试用例 数据驱动测试用例 关键词keywords 内置关键词 库关键词 用户定义关键词 变量 定义变量 使用变 ...
- moost — Last.fm's collection of C++ utility libraries(功能很多)
libmoost libmoost is a collection of C++ utility libraries, including: algorithms (set intersection, ...
- FFmpeg来源简单分析:结构会员管理系统-AVClass
===================================================== FFmpeg章列表: [架构图] FFmpeg源码结构图 - 解码 FFmpeg源码结构图 ...