黑马程序员技术交流社区

标题: 关于构造函数对对象进行初始化的问题,这个问题.关注点.... [打印本页]

作者: 罗安迪    时间: 2014-3-6 18:00
标题: 关于构造函数对对象进行初始化的问题,这个问题.关注点....
本帖最后由 罗安迪 于 2014-3-12 09:15 编辑

当没有显性的创建构造函数时,有一个默认的空参数构造函数存在,且其把属性都初始化了。
如下例子中,若输出x的值可以得到0 。

当我显性的创建构造函数时,默认的空参数构造函数就没有了对吧。

那假如我并不在里面把属性初始化,如下:
class A

     private int x;
     A()
         {

           }

   这里有 A的 方法。。。


为什么我还是可以通过new A() 来初始化对象A呢?

或者换种问法

我这里并没有去初识化属性x了,默认空参数构造函数也没有了,为什么输出x的值还是0呢?

我知道成员变量是被默认初识化了的,这里关注的问题是new A()时构造函数的初识化作用。



为什么还可以



作者: Monkey·D·Chas    时间: 2014-3-6 18:40
public class test {
        public static void main(String[] args) {
                A a=new A();
        System.out.println(a.x);
       
        }
       

}
class A{
       
             int x;
             A(){};
                

}
  输出结果是0, x私有属性的话是需要封装的
初始化的时候给了x 一个空间。默认应该为零!
作者: 艮昕辶    时间: 2014-3-6 19:04

c++的构造函数才有显式调用和隐式调用
没有显式构造和隐式构造的说法

我猜你是想问内存的问题
源代码是存放在代码段中
全局变量和静态变量是存放在数据段内存区
局部变量存放在栈内存区,是系统规定好的一段连续的内存区域
new运算符初始化的对象数据存放在堆内存区,是非连续的内存区域
还有常量池存放常量
作者: 罗安迪    时间: 2014-3-6 22:12
艮昕辶 发表于 2014-3-6 19:04
c++的构造函数才有显式调用和隐式调用
没有显式构造和隐式构造的说法

。。。Java的方法有隐式和显式吧 比如a.show(x)中a是隐 x是显

我问得是构造函数在对对象初识化中的对对象类的属性初识化的问题噢

不过 还是谢谢你先^ _ ^
作者: 年少丶    时间: 2014-3-7 09:47
都已经分配内存了,当然有值了
作者: pifuhanshu    时间: 2014-3-11 14:02
当有显性构造函数时,默认空构造函数就自行消失。
作者: pifuhanshu    时间: 2014-3-11 14:06
当有显性构造函数时,默认空构造函数就会自行消失。
而本题中定义的是显性空构造函数,其作用的效果跟默认空构造函数是一个结果。故原程序输出x的值可以仍得到0 。
作者: 无此姓名    时间: 2014-3-11 14:50
本帖最后由 无此姓名 于 2014-3-11 15:03 编辑

其实LZ自己已经知道答案了,只是有一部分没想通。显然x的值为0是被默认初始化了,int类型变量默认初始化的值就是0。关于默认初始化可以理解为:只要定义了变量,调用这个变量之前都要进行默认初始化。这是机器做的动作,因为创建变量时变量的值谁也说不好,机器这么做的原因我觉得是谨慎:lol。
很多时候我们定义变量还会跟着显示初始化,比如 int x=8;
它们的先后顺序是默认初始化>显示初始化>构造函数初始化.

关于“new A()时构造函数的初识化作用
其实如果你什么都不做,也不加private,这时候自己写的空参数的构造函数就没什么意义了,即使你不写,系统自动给加一个隐式的空参数构造函数,作用一样。
可以这么说:如果你要写构造函数,那系统是希望你做一些事情的,比如给对象的某些特殊属性进行初始化,这是设计构造函数的原意。如果你不想要用这个功能,或者不需要,那完全不必写,系统可以帮你做初始化动作,保证正常运行。

当然自己写空参数构造函数也不是完全无用的,举个例子:
建立了一个类,禁止建立本类的对象。这时候你就需要自己写构造函数了,在前边加上private就OK。如下代码:
  1. class Person_1
  2. {               
  3.         private Person_1() //加private,禁止建立本类对象
  4.         {        
  5.         }        
  6. }
  7.         
  8. class  Test_1
  9. {
  10.         public static void main(String[] args)
  11.         {
  12.                 Person_1 p= new Person_1();//编译时会报错,不会成功建立对象
  13.         }
  14. }
复制代码



关于执行几种初始化执行顺序,建议可以参考一下毕老师的第6天-第7个视频。加一段代码和毕老师的总结。希望能帮助理解

  1. class Person_1
  2. {
  3.         private int age;//默认初始化
  4.         private String name="haha";        //默认初始化 + 显式初始化
  5.         private static String country="China";
  6.         
  7.         {        //构造代码块,给所有对象初始化
  8.                 System.out.println(name+"..."+age);
  9.         }

  10.         Person_1(String name,int age)
  11.         {        //构造函数初始化,给特定对象初始化
  12.                 this.name=name;
  13.                 this.age=age;
  14.         }

  15. }
  16. class PersonDemo_1
  17. {
  18.         public static void main(String[] args)
  19.         {
  20.                 Person_1 p = new Person_1("zhangsan",20);
  21.         }
  22. }

  23. /*总结
  24. Person_1 p = new Person_1("zhangsan",20);
  25. 该句话都做了什么事情?
  26. 1、因为new用到了Person_1.class文件,所以会先找到Person_1.class文件并加载到内存中
  27. 2、如果有的话,执行该类中的static代码块,给Person_1.class类进行初始化
  28. 3、在堆内存中开辟空间,分配内存地址。
  29. 4、在堆内存中建立对象的特有属性,并进行默认初始化。
  30. 5、对属性进行显示初始化。
  31. 6、对对象进行构造代码块初始化
  32. 7、对对象进行对应构造函数初始化
  33. 8、将地址值赋给栈内存中的p变量。
  34. */
复制代码


作者: 冯鸿昌    时间: 2014-3-12 00:18
  1. //我们以Person类为例,看一下自定义无参构造函数与系统默认构造函数的区别
  2. class Person {
  3.        
  4.         private int age;
  5.         private String name;
  6.        
  7.         //分别查看自定义无参构造函数与系统默认构造函数生成的代码
  8.         //通过 javap 指令查看
  9.         //会发现他们的反编译代码是一样的
  10.         public Person() {
  11.                
  12.         }
  13. }

  14. //由javap -c 指令生成反编译码如下
  15. //class com.uc.TZone.Person {
  16. //private int age;
  17. //
  18. //private java.lang.String name;
  19. //
  20. //com.uc.TZone.Person();
  21. //  Code:
  22. //     0: aload_0
  23. //     1: invokespecial #12    //Method java/lang/Object."<init>":()V 该函数完成类实例变量的初始化
  24. //     4: return
  25. //}

  26. //再就是实例类中字段的初始化问题,这个其实与他们的申明顺序有直接的关系
  27. //我们仍然通过他们的反编译代码来查看
  28. class Person {
  29.        
  30.         {
  31.                 age = 15;
  32.         }
  33.         private int age = 10;
  34.        
  35.         private String name = "uc";
  36.        
  37.         {
  38.                 name = "haha";
  39.         }
  40.        
  41.         @Override
  42.         public String toString() {
  43.                 return "Person [age=" + age + ", name=" + name + "]";
  44.         }
  45.        
  46. //        public Person() {
  47. //                age = 25;
  48. //                name = "happy";
  49. //        }
  50. }

  51. //在这里大家可以考虑下对象创建成功后 age 与 name的值
  52. //在main函数中调用 System.out.println(new Person());
  53. //结果为:Person [age=10, name=haha]
  54. //还是通过 查看反编译代码来分析原因

  55. /*
  56.    com.uc.TZone.Person();
  57.    Code:
  58.       0: aload_0
  59.       1: invokespecial #12                 // Method java/lang/Object."<init>":()V
  60.                                                                                  //这里系统首次初始化
  61.       4: aload_0
  62.       5: bipush        15                                        //这里为age首次赋值 15
  63.       7: putfield      #14                 // Field age:I
  64.      10: aload_0
  65.      11: bipush        10                                        //再次给age赋值 10 所有 最后结果为10
  66.      13: putfield      #14                 // Field age:I
  67.      16: aload_0
  68.      17: ldc           #16                 // String uc
  69.      19: putfield      #18                 // Field name:Ljava/lang/String;
  70.      22: aload_0
  71.      23: ldc           #20                 // String haha
  72.      25: putfield      #18                 // Field name:Ljava/lang/String;
  73.                                                                                 // name 字段也是一样
  74.      28: return
  75. */


  76. //大家可以再考虑下在上面的Person类中加入如下的构造函数
  77. /*
  78. * public Person() {
  79. *                
  80. *                 age = 25;
  81. *                 name = "happy";
  82. *
  83. * }
  84. */

  85. //javap 反编译后如下
  86. /*
  87. * public com.uc.TZone.Person();
  88.    Code:
  89.       0: aload_0
  90.       1: invokespecial #42                 // Method java/lang/Object."<init>":()V
  91.       4: aload_0
  92.       5: bipush        15
  93.       7: putfield      #20                 // Field age:I
  94.      10: aload_0
  95.      11: bipush        10
  96.      13: putfield      #20                 // Field age:I
  97.      16: aload_0
  98.      17: ldc           #44                 // String uc
  99.      19: putfield      #31                 // Field name:Ljava/lang/String;
  100.      22: aload_0
  101.      23: ldc           #46                 // String haha
  102.      25: putfield      #31                 // Field name:Ljava/lang/String;
  103.      28: aload_0
  104.      29: bipush        25
  105.      31: putfield      #20                 // Field age:I
  106.      34: aload_0
  107.      35: ldc           #48                 // String happy
  108.      37: putfield      #31                 // Field name:Ljava/lang/String;
  109.      40: return
  110. *
  111. * 通过阅读上面的代码,我们推测 最后 age = 25, name = "happy"
  112. * 实际运行后结果 如下:
  113. * Person [age=25, name=happy]
  114. * 与我们想的一致
  115. */

  116. //结论:
  117. //1. 自定义无参构造函数与系统的默认构造函数初始化过程是一致的,我们可以在自定义函数中加入自己想要赋值的字段
  118. //2. 类中实例字段初始化的顺序与他们在源代码中存在的位置有关。需要时可以通过反编译代码查看                
复制代码





欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2