Thinking in Java 系列 ---(三)初始化和清理

    xiaoxiao2022-07-14  160

    文章目录

    Initialization & Cleanup用构造方法来确保初始化 Guaranteed initialization with the constructor构造方法规则 方法的重载 Method overloading什么是重载方法重载范围 this关键字static静态 清理,垃圾回收- clean up成员初始化 Member initialization什么时候初始化变量比较好 初始化顺序代码块和语句在方法之前执行包括构造方法对象和static初始化顺序(重点) 数组的初始化array初始化的两种方式可变参数列表Variable argument lists 总结

    Initialization & Cleanup

    java引入构造方法来进行对象的初始化 引入garbage collection 来进行垃圾回收,清理对象释放内存

    用构造方法来确保初始化 Guaranteed initialization with the constructor

    每个类都必须有构造方法,有了构造方法,当对象被创建的时候,构造方法自动被调用,也就是说只有通过调用构造方法才能创建对象。

    构造方法规则

    不包含任何参数的构造方法为默认构造方法。如果你显示定义了自己的构造方法,则默认的构造方法就不存在了。如果没定义则编译器会提供一个默认无参的构造方法。构造方法无返回值。

    class Rock { Rock() { System.out.print("Rock "); } }

    Now, when an object is created: new Rock();storage is allocated and the constructor is called.

    方法的重载 Method overloading

    什么是重载方法

    每个方法名字相同,但是他们有唯一形式的参数列表。(只看这一点就可以了,只要参数列表形式不同就属于重载) 根据方法的参数列表(参数数量和类型)而不是返回值来判断是否重载方法,重载方法的参数顺序不同也属于重载方法。所以如果方法参数列表相同且名称相同他就是同一个的方法。

    下面这个方法实例就是参数列表顺序不同他属于不同的方法重载,但是尽量不要这样写,因为会给代码维护造成困难。

    public void f(String s, int i) { System.out.println("String: " + s + ", int: " + i); } public void f(int i, String s) { System.out.println("int: " + i + ", String: " + s); } public OverloadingOrder() { f("String first", 11); f(99, "Int first"); }

    重载范围

    当重载方法范围比较小时候,还是根据他指定类型来调用

    void f1(char x) { System.out.println(("f1(char) ")); } void f1(short x) { System.out.println("f1(short) "); } void f1(int x) { System.out.println("f1(int) ");} public static void main(String[] args) { ReplacingStringTokenizer t = new ReplacingStringTokenizer(); short a = 3; int b = 3; t.f1(a);//f1(short) t.f1(b);//f1(int) t.f1(3);//f1(int) }

    this关键字

    this关键字只能用在非静态方法里面用来作为引用代表这个对象。 The this keyword—which can be used only inside a non-static method—produces the reference to the object that the method has been called for. this关键字只用在一些特殊场合你需要明确指出当前对象。 The this keyword is used only for those special cases in which you need to explicitly use the reference to the current object.

    常用场合1:For example, it’s often used in return statements when you want to return the reference to the current object:比如当你想要返回当前对象的引用:

    public class Leaf { int i = 0; //方法,返回值为该对象 Leaf increment() { i++; return this; } //打印 void print() { System.out.println("i = " + i); } //构造方法 public Leaf() { increment().increment().increment().print(); } } /*Output: i = 3

    常用场合2,3:2)当一个类中有多个构造方法,在一个构造方法中调用另一个构造方法,这时可以使用this避免重复代码。3)区分形参和成员变量(十分常见)

    public class Flower { int petalCount = 0;//花瓣数量 String s = "initial value";//成员变量s //构造方法1 public Flower(int petals) { petalCount = petals; print("Constructor w/ int arg only, petalCount= " + petalCount); } //构造方法2 public Flower(String ss) { s = ss; print("Constructor w/ String arg only, s = " + ss); } //构造方法3 public Flower(String s, int petals) {//形参s this(petals);//调用构造方法1 // Another use of "this" this.s = s;//用this区分形参s和本身的成员变量s,这里把形参赋给成员变量s,这种用法十分常见 print("String & int args"); } //无参构造方法 public Flower() { this("hi", 47);//调用构造方法3,调用构造方法是要放在其他语句前面,不然编译器报错 print("default constructor (no args)"); } //打印方法 void printPetalCount() { // ! this(11); //Not inside non-constructor! print("petalCount = " + petalCount + " s = " + s); } public static void main(String[] args) { Flower x = new Flower();//调用无参构造方法 x.printPetalCount(); } }

    static静态

    this关键字不能出现在静态方法中,不能在静态方法中调用非静态方法,所以静态方法类似于全局方法,但是java中没有全局方法。一些人认为静态方法不属于OOP,因为他不需要对象。所以如果程序中包含大量静态方法应该慎重考虑。但有时静态方法确实很实用。

    清理,垃圾回收- clean up

    Your objects might not get garbage collected.你的对象不是总会被垃圾回收Garbage collection is not destruction.垃圾回收不是销毁Garbage collection is only about memory. 垃圾回收只是关于内存

    通常手动清理是用不到的,但是一些少数特殊情况需要用到cleanup java的具体清理内存使用generation garbage collection 方法,可以参照我的另一篇文章有详细介绍操作系统中内存分配和管理(垃圾回收机制)

    成员初始化 Member initialization

    编译器会强制程序员给每个局部变量初始化。 成员变量会被默认初始化,引用的默认初始化值为null

    什么时候初始化变量比较好

    定义成员变量在声明时候初始化更好:而不是在构造方法中 比如:

    public class ME { private int i = 100; public ME() { } }

    增加了可读性,我们在读代码的时候会从头往下读。 如果有超过一个构造方法,我们不需要重复的在构造方法里初始化。

    It makes it clear at a glance how the variable is initialized. Typically, when reading a program and coming across a variable, you’ll first go to its declaration (often automatic in IDEs). You see the default value right away. With style constructor, you need to look at the constructor as well.If you have more than one constructor, you don’t have to repeat the initializations (and you cannot forget them).

    初始化顺序

    代码块和语句在方法之前执行包括构造方法

    即使成员变量在方法定义之间,初始化变量还是会在任何方法之前执行。 between method definitions, but the variables are initialized before any methods can be called—even the constructor

    //counter中的i 会先被初始化为0然后再赋值为7 class Counter { int i; Counter() { i = 7; } } class Window { Window(int marker) { System.out.println("Window(" + marker + ")"); } } class House { Window w1 = new Window(1); // Before constructor执行 House() { // Show that we're in the constructor: System.out.println("House()"); w3 = new Window(33); } Window w2 = new Window(2);//执行在其他方法之前 void f() { System.out.println("f()"); } //执行在其他方法之前 int a = 4; Window w3 = new Window(3); } public class TestMain { public static void main(String[] args) { House house = new House(); house.f(); // Shows that construction is done } /* Window(1) Window(2) Window(3) House() Window(33) f()*/

    对象和static初始化顺序(重点)

    对象初始化的顺序其实就是程序运行的执行代码顺序。 方法是什么时候调用什么时候才初始化 java虚拟机会在加载.class文件时先查找静态变量并存入静态域。而我们在编写代码时候所有的调用都是编写过程而不是运行过程,初始化顺序是讲的运行过程。 Dog dog = new Dog();

    static的变量会优先于其他非static变量被初始化,而且仅初始化一次,当提及Dog dog 时候,相当于初始化static成员,java会加载Dog.class类进行static初始化。当直接创建对象new Dog()时候,相当于调用static构造方法,因为构造方法是static的。

    To summarize the process of creating an object, consider a class called Dog:总结一下初始化顺序,举例,一个类叫做Dog:

    构造方法是一个静态方法,当第一次使用类中的静态方法或静态域的时候,java开始运行时,JVM就会定位Dog.class文件来先把静态变量存储到静态与。当new对象的时候,java会自动调用构造方法。Even though it doesn’t explicitly use the static keyword, the constructor is actually a static method. So the first time an object of type Dog is created, or the first time a static method or static field of class Dog is accessed, the Java interpreter must locate Dog.class, which it does by searching through the classpath.当Dog类加载的时候所有static的变量和代码块就会被初始化,并且只初始化一次。As Dog.class is loaded (creating a Class object, which you’ll learn about later), all of its static initializers are run. Thus, static initialization takes place only once, as the Class object is loaded for the first time.当读到new Dog( )语句的时候,这个时候会在堆上给对象分配内存。When you create a new Dog( ), the construction process for a Dog object first allocates enough storage for a Dog object on the heap.这时所有的非静态变量和代码块会被初始化。This storage is wiped to zero, automatically setting all the primitives in that Dog object to their default values (zero for numbers and the equivalent for boolean and char) and the references to null.构造方法被调用 Constructors are executed. As you shall see in the Reusing Classes chapter, this might actually involve a fair amount of activity, especially when inheritance is involved.

    以下实例:

    class Cup { Cup(int marker) { System.out.println("Cup(" + marker + ")"); } void f(int marker) { System.out.println("f(" + marker + ")"); } } class Cups { static Cup cup1;//(1) static Cup cup2;//(2) static {//(3) cup1 = new Cup(1); cup2 = new Cup(2); } Cups() { System.out.println("Cups()"); } } public class TestMain { public static void main(String[] args) { System.out.println("Inside main()"); Cups.cup1.f(99); // 当调用Cups时会执行static而不执行构造方法,因为没new对象 } } /* Inside main() Cup(1) Cup(2) f(99) */ 基类静态代码块,基类静态成员字段 (并列优先级,按代码中出现先后顺序执行)(只有第一次加载类时执行)派生类静态代码块,派生类静态成员字段 (并列优先级,按代码中出现先后顺序执行)(只有第一次加载类时执行)基类普通代码块,基类普通成员字段 (并列优先级,按代码中出现先后顺序执行)基类构造函数派生类普通代码块,派生类普通成员字段 (并列优先级,按代码中出现先后顺序执行)派生类构造函数

    数组的初始化array

    array中的每个元素类型都相同。 声明用中括号[]赋值用大括号{}

    初始化的两种方式

    第一种方式:直接分配元素int[] a1 = { 1, 2, 3, 4, 5 }; 第二种方式:指定分配大小int[] a1 = new int[10]; 10个元素的大小

    ArrayList 和 数组区别

    数组必须在创建时候指定大小,而ArrayList不用。数组必须指定放入位置 my[1] = b;如果超出会崩溃。Arraylist中set(index,object)与add(index,object)方法的区别,超出也会崩溃,set是在指定位置替换,add是添加到指定位置后面的元素向后移动一位ArrayList中 的 为prameterized type 表示一个String的list

    现代版本的java是用ArrayList来替换了vector. (Note that the modern Java container library replaces Hashtable with HashMap.)现代java中还用HashMap 代替了Hashtable

    []定义数组int[] a1或者指定数组中某个元素a[2]

    public class TestMain { public static void main(String[] args) { int[] a = new int[10]; int[] b = {1, 2, 3, 4,};//最后一个逗号可加可不加 //打印array System.out.println(Arrays.toString(a)); System.out.println(Arrays.toString(b)); for (int i : b) { System.out.println(i); } } } /* [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] [1, 2, 3, 4] 1 2 3 4*/

    可变参数列表Variable argument lists

    java1.5之后,可以通过可变参数列表来接收不确定数量的形参:(他其实是一个数组) 在使用的时候要注意重载可能造成的混淆。

    public class TestMain { //求传入參数的和的方法 public void add(int x,int...arr)//第二个参数为可变參数为一个数组,元素需要相同类型 { int sum=x;//先把第一个參数的值复制给sum //剩余的参数作为一个array数组 for(int i=0;i<arr.length;++i) { sum+=arr[i];//求和 } System.out.println(sum); } public static void main(String[] args) { // TODO Auto-generated method stub TestMain tt = new TestMain(); tt.add(2);//传参为2,可变參数长度为0 tt.add(2,3);//和为5, 可变参数长度1 tt.add(2,3,4,5);//和为14 tt.add(2,3,4,5,6,7);//27 } }

    总结

    构造器的出现更加保障了java初始化和清理的安全性与可靠性。java的垃圾收集系统简化了程序并且增加了管理内存的安全性。但是增加了runtime的消耗。所以java的速度问题还是成为这门编程语言的缺陷。

    最新回复(0)