初始化与清理

     #### 初始化和清理涉及安全的两个问题。

     ####Java 采用构造器,并额外提供了”垃圾回收器“

              构造器:在创建对象时被自动调用的特殊方法

              垃圾回收器:对于不再使用的内存资源,垃圾回收器能自动将其释放

》》用构造器确保初始化

     #### 在创建对象时,如果其类具有构造器,Java 就会在用户有能力操作

               对象之前自动调用相应的构造器,从而保证了初始化的进行。

    #### 构造器采用与类名相同的名称。

    #### 调用构造器是编译器的责任。

    #### 在用 new  关键字创建对象时,将会为对象分配存储空间,并调用相

          应的构造器。这就确保了在你能操作对象之前,它已经被恰当地初始化

          了。

    #### 请注意:由于构造器的名称与类名完全相同,所以”每个方法首字母小

          写“的编码风格并不适用于构造器。

    #### 不接受任何参数的构造器,叫做默认构造器。--->无参构造器

   #### 如果类中已经有了带有参数的构造器,那么编译器就不会允许你以

            其他任何方式创建对象( 只能以构造器的形式创建对象)

   #### 在 Java 中,”初始化“和”创建“捆绑在一起,两者不能分离。

   #### 构造器是一种特殊的方法,它没有返回值。        

》》方法重载

    #### 所谓方法是给某个动作取的名字。

    ####在程序设计语言中,每个方法(有些称为函数)都提供一个独一无二

             的标识符

    ####为了让方法名相同而形式参数不同的构造器同时存在,必须用到

             方法重载。同时,尽管方法重载是构造器所必需的,但它亦可应用于

             其他方法,且用法同样方便。

    ####区分方法重载方法:

              ---------每个重载的方法都必须有一个独一无二的参数类型列表

    ####涉及基本类型的重载:

              --------基本数据类型能从一个”较小“的类型自动提升为一个”较大“的

                       类型,此过程一旦牵涉到重载,可能会造成一个些混淆。

             ------- 如果传入的数据类型(实际参数类型)小于方法中声明的形式

                      参数类型,实际数据类型就会被提升。

                      char  型略有不同,如果无法找到恰好接受 char 参数的方法,

                      就会把 char 直接提升至 int 型。

             ------如果传入的实际参数较大,就得通过类型转换来执行窄化转换

                    如果不这样做,编译器就会报错。

   #### 以返回值区分重载方法-------->行不通的

             -------只能以方法名和参数列表来区分各个方法

》》默认构造器

    #### 默认构造器就是没有形式参数的构造器---它的作用就是创建一个

             ”默认对象“

    #### 如果你写的类中没有构造器,则编译器会自动帮你创建一个默认

            构造器。

    ####如果已经定义了构造器(无论是否有参数),编译器就不会帮你

            自动创建默认构造器

》》 this关键字

    #### 如果你希望在方法的内部获取当前对象的引用。由于这个引用是

              由编译器”偷偷“传入的,所以没有标识符可用。但是,为此有个

              专门的关键字:this 

    #### this 关键字只能在方法内部使用,表示对”调用方法的那个对象“

              的引用。

    #### this 的用法和其他对象引用并无不同。但要注意,如果在方法

             内部调用同一个类的另一个方法,就不必使用 this ,直接调用

             即可。当前方法中的  this 引用会自动应用于同一类中的其他

             方法。

     ####  只有当需要明确指出对当前对象的引用时,才需要使用 this 

           关键字。例如,需要返回对当前对象的引用,就常常在 return 

           语句里这样写:  

                   return this; 

     #### this 关键字对于将当前对象传递给其他方法也很有用

     #### 在构造器中调用构造器:

               --------可能为一个类写了多个构造器,有时可能想在一个构

                         造器中调用另一个构造器,以避免重复代码。可用 this

                        关键字做到这一点。 

              --------在构造器中,为 this 添加参数列表,将会调用 

                        符合此参数列表的某个构造器。

              --------尽管可以使用 this 调用一个构造器,但却不能调用两个。

              -------- 通过 this 调用其他构造器,必须将 this 放在最起始处,

                         否则编译会报错。 

              ---------当数据成员 s 和参数 s 的名称相同时,可以使用 this.s

                        来代表数据成员(避免两者产生分歧)

              --------除了构造器之外,编译器禁止在其他方法中使用 this 来

                       调用其他构造器

    #### static 的含义:

              -------static 方法就是没有 this 的方法。

              -------在static 方法的内部不能调用非静态的方法

              -------static 方法可以在没有创建任何对象的前提下,仅仅通过

                      类本身来调用  static 方法

              -------Java 中禁止使用全局方法,但你在类中置入 static  方法

                       就可以访问其他  static 方法和 static 域。

              ------使用 static 方法时,由于不存在 this , 所以不是通过

                      ”向对象发送消息“的方式来完成的

》》清理:终结处理和垃圾回收

             -------Java 垃圾回收器负责回收无用对象占据的内存资源。

             -------Java 里的对象并非总是被垃圾回收,或者换句话说:

                      @@ 对象可能不被垃圾回收

                      @@ 垃圾回收并不等于”析构“

                      @@垃圾回收只与内存有关

             --------使用垃圾回收器的唯一原因是:为了回收程序不再使用

                       的内存

             -------无论对象是如何创建的,垃圾回收器都会负责释放对象

                      占据的所有内存

              补充:finalize() 不是进行普通的清理工作的合适场所。

             --------你必须实施清理:

                        @@Java 不允许创建局部对象,必须使用 new 创建对象

                       @@如果希望进行除释放存储空间之外的清理工作,还是得

                              明确调用某个恰当的 Java 方法

                       @@记住,无论是”垃圾回收“还是”终结“,都不保证一定会

                            发生。如果Java 虚拟机(JVM)并为面临内存耗尽的情形,

                            它是不会浪费时间去执行垃圾回收以恢复内存的。

            --------终结条件

                       @@注意,System.gc();  用于强制进行终结动作。

            --------垃圾回收器如何工作:

                       @@通过垃圾回收器对对象重新排列,实现了高速的、有无限

                      空间可供分配的堆模型。

                      @@其他系统中的垃圾回收机制:

                                %% ”引用记数“

                                        **这种技术是一种简单但速度很慢的垃圾回收技术

                                        **每个对象都有一个引用计数器,当有引用连接至对象

                                           时,引用计数加1 ; 当引用离开作用域或被置为 null 

                                           时,引用计数减1 

                                       **垃圾回收器会在含有全部对象的列表上遍历,当发现

                                          某个对象的引用计数为 0 时,  就释放其占用的空间。

                                       ** 这种方法有个缺陷,如果对象之间存在循环引用,

                                           可能会出现”对象应该被回收,但引用计数却不为零“

                                         的情况

                                       ** 引用计数常用来说明垃圾收集的工作方式,但是似乎

                                         从未被应用于任何一种 Java 虚拟机实现中

                              %%”自适应的垃圾回收技术“--》”标记--清扫“模式与”停止--复制“

                                                                               模式根据情况自动切换           

                                       **先寻找”活“ 的对象

                                           对任何”活“的对象,一定能最终追溯到存活在堆栈或静态

                                       存储区之中的引用。这个引用链条可能会穿过数个对象层次。

                                       对于发现的每个引用,必须追踪它所引用的对象,然后是此

                                       对象包含的所有引用,如此反复进行,直到”根源于堆栈和

                                      静态存储区的引用“所形成的网络全部被访问为止。

                                     **如何处理找到的”活“ 的对象

                                         不同的Java 虚拟机的实现都不同

                                         有一种做法为:停止---复制

                                        <

                                             停止--复制:先暂停程序的运行(所以它不属于后台回收

                                             模式),然后将所有存活的对象从当前堆复制到另一个堆,

                                             没有被复制的全部都是垃圾。当对象被复制到新推后时,

                                             它们是一个挨着一个的,所以新堆保持紧凑排列,然后

                                             就可以按一定的方法简单、直接地分配新空间了。

                                                    当把对象从一处搬到另一处时,所有指向它的那些引用

                                            都必须修正

                                       >

                                 **补充:上面的技术,需要有两个堆,这两个分离的堆之间来回

                                                 倒腾

                                       <

                                              某些 Java 虚拟机 对此问题的处理方式是:按需从堆中分配

                                         几块较大的内存,复制动作发生在这些大块内存之间

                                       >

                                 **程序进入稳定状态之后,可能只会产生少量垃圾,甚至没有垃圾

                                    尽管如此,复制式回收器仍然会将所有内存自一处复制到另一处,

                                     这很浪费。为了避免这种情形,一些 java 虚拟机会进行检查:

                                    要是没有新垃圾的产生,就会转换到另一种工作模式(即”自适应

                                   ),这种模式称为:”标记----清扫“ 

                                 **”标记---清扫“所依据的思路是从堆栈和静态存储区出发,遍历所有的

                                     引用 , 进而找出所有存活的对象。每当它找到一个存活对象,就会

                                    给对象设一个标记,这个过程不会回收任何对象。只有全部标记工作

                                     完成的时候,清理工作才会开始。在清理过程中,没有标记的对象

                                   将被释放,不会发生任何复制动作。所以剩下的堆空间是不连续的,

                                   垃圾回收器要是希望得到连续空间的话,就得重新整理剩下的对象。

                                **Java  虚拟机会进行监视,如果所有对象都很稳定,垃圾回收器的

                                  效率降低的话,就切换到”标记---清扫“方式;同样,Java  虚拟机会

                                  跟踪”标记--清扫“的效果,要是堆空间出现很多碎片,就会切换到

                                  ”停止----复制“方式。这就是”自适应“技术

                                ** Java 虚拟机中有许多附加技术用以提升速度。尤其是与加载器

                                    操作有关的,被称为”即时“编译器(JIT)的技术。

                                     <

                                           这种技术可以把程序全部或部分翻译成本地机器码(这本来是

                                        Java 虚拟机的工作),程序运行速度因此得以提升。当需要装载

                                       某个类(通常是在为该类创建第一个对象)时,编译器会先找到其

                                      .class 文件,然后将该类的字节码装入内存。此时,有两种方案

                                        可供选择:

                                           一种是:让即时编译器编译所有代码

                                           另一种是:惰性评估,意思是即时编译器只在必要的时候才编译

                                                         代码。这样,从不会被执行的代码也许就压根不会被JIT

                                                         编译。

                                    >

》》成员初始化

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

            --------对于方法中的局部变量:一定要提供初始值

            --------类的每个基本类型数据成员保证都会有一个初始值。

                       即使没有显式赋值,系统都会给定默认的值

           ---------在类里定义一个对象引用时,如果不将其初始化,此引用就会获得一个

                       特殊值 null 

           --------指定初始化:

                       @@在定义类成员变量的地方为其赋值。

                             (不论是基本类型的还是非基本类型的变量)

                      @@如果在没有给定初始值就尝试使用它,就会出现运行时错误,

                             会产生一个异常

》》构造器初始化

          ---------可以使用构造器来进行初始化。在运行时刻,可以调用方法或执行

                       某些动作来确定初值,这为编程带来了更大的灵活性。

                      但要牢记:无法阻止自动初始化的进行,它将在构造器被调用之前

                      发生

         ----------初始化顺序:

                      @@ 在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量

                               定义散布于方法定义之间,它们仍旧会在任何方法(包括构造器)

                              被调用之前得到初始化

           ---------静态数据的初始化:

                      @@无论创建多少个对象,静态数据都只占用一份存储区域。static 

                     关键字不能应用于局部变量,因此它只能作用于域。如果一个域是静态

                     的基本类型域,且没有对它进行初始化,那么它就会获得基本类型的

                    标准初值;如果它是一个对象引用,那么它的默认初始值就是null 。

                     @@ 初始化的顺序是先静态对象(如果尚未因为前面的对象创建过程而

                    被初始化),而后是“非静态”对象。

                     @@即使没有显式地使用 static 关键字,构造器实际上也是静态方法。

                    当首次创建某个类的对象时(构造器可以看成是静态方法),或者某个

                    类的静态方法或静态域首次被访问时,Java 解释器必须查找类路径,

                    以定位  .class 文件

                          然后载入 .class  文件(这将创建一个   Class 对象),有关静态初

                    始化 的所有动作都会执行。因此,静态初始化只有在 Class 对象首次

                     加载的时候进行一次。

                          当使用 new 关键字创建对象的时候,首先将在堆上为 该对象分配

                    足够的存储空间。而且这块存储空间在不用的时候,会被清零,这就

                    自动地将对象上的所有基本数据类型都设置成了默认值,而引用则被

                    设置成了  null 

        ----------显式的静态初始化:

                    @@ Java 允许将多个静态初始化动作组织成一个特殊的“静态子句

                             (有时也叫“静态块”)

                            格式如下:

                            public class Spoon{

                                    static int i ; 

                                    static  {

                                        i = 47;

                                    }

                            }

                 @@ 上面的静态块中的代码,仅仅只会执行一次。

                          当首次生成这个类的一个对象时,或者首次访问属于那个类的静态

                       数据成员时(即便从未生成那个类的对象),静态初始化动作只进行

                       一次。

         -----------非静态实例初始化

                  @@ Java 中也有被称为实例初始化的类似语法,用来初始化每一个对象

                       的非静态变量。

                  @@非静态初始化的格式:

                      例如:  class Mug{

                                         Mug(int marker){

                                         }

                                         public  void f(int marker){

                                        }

                                   }

                  

                                  pulic class Mugs{

                                         Mug   mug1;

                                         Mug   mug2;

                                         {

                                               mug1 = new Mug(1);

                                               mug2 = new Mug(2);

                                        }

                                 }

               @@ 非静态初始化的初始化块,和静态初始化的块是一样的,

                        只是少了一个 static 关键字。这种语法对于支持"匿名内

                       部类"的初始化是必须的,但是它也使得你可以保证无论

                       调用哪个显式的构造器,某些操作都会发生。实例化子

                      句是在构造器之前进行的,是在静态子句之后进行的。

               @@每次创建一个对象实例的时候,非静态初始化块都会被

                     执行一次。但是,静态初始化块仅仅只执行一次

》》数组初始化

           ----------数组是相同类型的、用一个标识符名称封装到一起的一个

                   对象序列或基本类型数据序列。

           -----------数组是通过方括号下标操作符 [ ] 来定义使用的。要定义

                    一个数组,只需在类型名后面加上一对 空括号即可:

                   int []   a1  ;  或者是  int   a1[]  ;   (前一种格式在java 中更适用)

           ---------编译器不允许指定数组的大小。

           -------- 对于数组,初始化动作可以发生在代码的任何地方,但也

                      可以使用一种特殊初始化表达式,它必须在创建数组的地方

                      出现。这种特殊的初始化是由一对花括号括起来组成的

                       在这种情况下,存储空间的分配(等价于使用  new 关键字)

                      将由编译器负责。如:

                       int[] a1 = {1,2,3,4} ;

           ----------- 在 Java 中可以将一个数组赋值给另一个数组。其实真正

                      做的只是复制了一个引用。(两个引用,指向内存的同一块

                    内存空间)

           -----------所有数组(无论它们的元素是对象还是基本类型)都有一个

                    固有成员,可以通过它获知数组内包含了多少个元素,但不能

                    对其进行修改。这个成员就是 length 

          ------------Java 数组计算是从第 0 个元素开始的,所以能使用的最大

                   下标为 length - 1 。要是超出了这个界,Java 会出现运行时

                   错误(即异常)。

           ----------- 如果在编写程序时,并不能确定在数组里面需要多少个元素,

                    可以直接用 new  在数组里面创建元素。尽管创建的是基本类型

                   数组,new  仍然可以工作。(不能用 new 创建单个的基本数据

                   类型

                  例如:  

                                <初始化基本数据类型>

                               Random rand = new Random(47);

                                int[] a1;

                                a1 = new int[rand.nextInt(20)]


                              <初始化包装类>

                             Integer []   a  = new Interger[rand.nextInt(20)];

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

                        有两种形式:

                       形式一:

                                  Integer [] a = {

                                          new Integer(1),

                                          new Integer(2)

                                  }  

                     形式二:

                                Integer[]  a = new Integer[]{

                                          new Integer(1),

                                          new Integer(2)

                                 }

                     补充:Arrays.toString() 方法属于 java.util 标准类库,

                           它将产生一维数组的可打印版本

          ----------可变参数列表:

                    例如:

                   static void printArray(Obejct... args){

                         for(Object obj : args){

                              System.out.println(obj+"");

                         }

                   }

                   说明:上面会打印数组的内容,如果数组中的内容都是

                  “引用”的话,在默认行为(没有定义 toString () 方法的话)

                   就是打印类的名字和对象的地址

                  @@  foreach 遍历 使用了 可变参数列表的内容

                  @@  getClass( )  方法属于 Object 的一部分,它将产生对象

                          的类并且在打印该类时,可以看到表示该类类型的

                         编码字符串。

                  @@ 可变参数列表不依赖自动包装机制,而实际上使用的是

                        基本类型。

                  @@ 可变参数列表使得重载变得复杂了。

》》枚举类型

           ----------enum 关键字,它使得我们在需要群组并使用枚举类型

                   集时,可以很方便地使用。

           ---------  Java 中枚举类型:

                    例如:

                          public  enum Spiciness{

                                   NOT, MILD , MEDIUM , HOT , FLAMING

                          }

                   说明:枚举类型的实例都是常量,因此按照命名惯例它们都

                 用大写字母表示(如果在一个名字中有多个单词,用下划线将

                 它们隔开)

        -----------  为了使用  enum ,需要创建一个该类型的引用,并将其赋值

                      某个实例:

                      public  class SimpleEnumUse{

                                public static void main(String[] args){

                                      Spiciness howHot = Spiciness.HOT;

                                      System.out.println(howHot);

                               }

                      }

     ---------------在你创建 enum 时,编译器会自动添加一些有用的特性。

                       例如:  toString() 方法: 可以很方便的显示某个 enum 

                                                                    实例的名字

                                   ordinal() 方法: 用来表示某个特定 enum 常量的

                                                                声明顺序

                                  static values() 方法:用来按照 enum 常量的声明

                                                                 顺序,产生由这些常量值构成的

                                                                 数组

        ------------enum 确实是类,并且具有自己的方法。

        ------------enum 有一个特别实用的特性,即它可以在 switch 语句

                       内使用。

         -----------由于 switch 是要在有限的可能值集合中进行选择,因此它

                      与 enum 正是绝佳的组合。请注意, enum 的名字是如何

                     能够清楚地表明程序意欲何为的。

         -----------可以将 enum 用作另外一种创建数据类型的方式,然后直接

                      将所得到的类型拿来使用。

》》总结:

        -----------构造器,这种精巧的初始化机制,暗示读者:初始化在 Java

                      中占有至关重要的地位。

        -----------构造器能保证正确的初始化和清理(没有正确的构造器调用,

                     编译器就不允许创建对象),所以有了完全的控制,也很安全。

        -----------在Java 中,垃圾回收器会自动为对象释放内存,可以极大地

                      简化编程工作,而且在处理内存的时候也更安全。

        ---------- 垃圾回收器增加了运行时的开销

        ----------随着时间的推移,Java 在性能方面已经取得了长足的进步,但

                    速度问题仍然是涉足某些特定编程领域的障碍。

         ---------由于要保证所有对象都被创建,构造器实际上要比上面讨论的

                     更加复杂。特别当通过组合继承生成新类的时候,这种保证

                      仍然成立,并且需要一些附加的语法来提供支持。

                    

猜你喜欢

转载自blog.csdn.net/lierming__/article/details/79777339
今日推荐