A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© Oh_JAVA 中级黑马   /  2016-7-18 15:07  /  993 人查看  /  12 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

首先,java创建对象的初始化顺序首先初始化属性,然后在调用构造方法
现在final的初始化必须在定义处或者在每个构造器中用表达式进行初始化。
  1. class Poppet{
  2.         private int i;
  3.         public Poppet(int ii) {
  4.                 i = ii;
  5.         }
  6. }

  7. public class BlankFinal {
  8.         private final int i = 0;
  9.         private final int j;
  10.         private final Poppet poppet;
  11.         public BlankFinal() {
  12.                 j=1;
  13.                 poppet = new Poppet(1);
  14.         }
  15.         public BlankFinal(int x) {
  16.                 j=x;
  17.                 poppet = new Poppet(1);
  18.         }
  19.         public static void main(String[] args) {
  20.                 new BlankFinal();
  21.                 new BlankFinal(47);
  22.         }

  23. }
复制代码

由于先属性初始化,基本数据类型会自动初始化为对应值,引用为null,如 int 会 初始化为0.那按照这样属性又为final的
所以在构造器里有进行一遍赋值,那应该会报错啊~~,可是真理就是final既可以在定义处又可在构造器中。这样不就矛盾了吗?

12 个回复

倒序浏览
final修饰的变量称为常量,常量就是我们只能赋值一次的,那么比如你声明的时候就赋值了, private final int i = 0;好,之后你就改变不了它的值了.你开始没有赋值,放在构造器里面赋值也可以,但是还是只能赋值一次.之后你就赋值不了了,所以不矛盾,反正你就赋值了一次,你也只能赋值一次.你看看是不是
回复 使用道具 举报
cat73 黑马帝 2016-7-18 17:26:38
藤椅
你需要理解一个 Java 对象创建的时候到底发生了什么,然后这个问题就好解决了。
Java 在遇到一条 new 指令的时候会做这些事情:
1. 查找这个类是否已经被加载过,如果没有则需要先进行加载。
2. 为对象分配内存,一个对象占用多大的空间在类加载完成时就可以确定,这里划分一块空间给这个对象用。
3. 将对象所属的内存空间全部初始化为 0 值(除对象头)
4. 初始化 Object Header
   到这一步完成的时候,从 JVM 的角度看,一个对象就已经创造完毕了,但从 Java 程序的角度看,这个对象的创建还没开始,构造函数还没执行,所有属性的值均为 0。
5. 调用 Java 代码中的构造函数完成对象的初始化。
   这一步完成后,在 Java 程序的角度看,一个对象才真的构造完毕。

你的问题里,基本数据类型会自动初始化为对应值,引用为null,如 int 会 初始化为0,实际上就是指第四步执行完毕后,构造代码块以及构造函数还没有执行的时候,对象的所有属性占用的内存区域全部为 0,但实际上你在没初始化的时候是不能通过 Java 代码去访问它们的(可以用反射0.0)。
回复 使用道具 举报
cat73 黑马帝 2016-7-18 17:29:54
板凳
实际上,如果关掉字节码校验,你就可以成功的加载一个不初始化 final 字段的类,并构造对象。
然后你就可以读到它们所谓的默认值了,也就是 null 跟 0。
回复 使用道具 举报
cat73 发表于 2016-7-18 17:26
你需要理解一个 Java 对象创建的时候到底发生了什么,然后这个问题就好解决了。
Java 在遇到一条 new 指令 ...

对象的所有属性占用的内存区域全部为 0   ,这句话是什么意思?并不是属性不会自动初始化,而是内存区域为0?
回复 使用道具 举报
cat73 发表于 2016-7-18 17:26
你需要理解一个 Java 对象创建的时候到底发生了什么,然后这个问题就好解决了。
Java 在遇到一条 new 指令 ...

第四步的执行完 意思是并没有真正给属性赋值吗? 对内存这些不太理解
回复 使用道具 举报
cat73 黑马帝 2016-7-18 17:55:46
7#
Oh_JAVA 发表于 2016-7-18 17:46
第四步的执行完 意思是并没有真正给属性赋值吗? 对内存这些不太理解

JVM 做的很简单,把字段空间全部抹成 0,不管里面是什么类型。
这就是一个对象默认的初始化了。

至于 final 的字段,是由构造函数赋值的,其实不赋值也没什么问题,JVM 也可以正常的执行下去。
而不给 final 字段赋值的情况是由类加载的时候的字节码校验去检查的,如果没有给 final 赋值,则拒绝加载这个类并抛错。
回复 使用道具 举报
cat73 发表于 2016-7-18 17:55
JVM 做的很简单,把字段空间全部抹成 0,不管里面是什么类型。
这就是一个对象默认的初始化了。

O(∩_∩)O谢谢!!
回复 使用道具 举报
cat73 黑马帝 2016-7-18 18:44:42
9#
Oh_JAVA 发表于 2016-7-18 18:34
O(∩_∩)O谢谢!!

也就是说,只要你能够绕过字节码校验加载一个类,那你就可以不初始化 final 的字段。
通过启动参数可以很轻松的关掉这个检查。
回复 使用道具 举报
cat73 黑马帝 2016-7-18 19:01:33
10#
通过 ASM 库,我们可以做出来一些很有趣的违反 Java 规定的类。
比如下面这个类,通过反编译工具反汇编后可以看到,这个类有两个 final 字段,但没有在任何地方对它进行初始化

回复 使用道具 举报
cat73 黑马帝 2016-7-20 14:26:34
11#
本帖最后由 cat73 于 2016-7-20 14:29 编辑

下载文件就可以看到咯。
java Test 来测试,会输出一个 null(请用 Java8)。
用反汇编工具效果如下(str 是 final 字段,然而并没有去初始化,但是依旧可以照常输出 null)↓

也算是证实了在构造函数初始化之前 final 的变量已经被设置过值了吧,这个值对于引用来说就是 null。

Test.zip

410 Bytes, 下载次数: 71

回复 使用道具 举报
cat73 发表于 2016-7-20 14:26
下载文件就可以看到咯。
java Test 来测试,会输出一个 null(请用 Java8)。
用反汇编工具效果如下(str  ...

恩恩,谢谢
回复 使用道具 举报
学习了学习了
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马