• on java8之初始化和清理


    1. 利用构造器保证初始化

    class Rock {
      Rock() { // This is the constructor
        System.out.print("Rock ");
      }
    }
    
    public class SimpleConstructor {
      public static void main(String[] args) {
        for(int i = 0; i < 10; i++)
          new Rock();
      }
    }
    //Rock Rock Rock Rock Rock Rock Rock Rock Rock Rock
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    1. new Rock() ,内存被分配,构造器被调用
    2. 构造器方法名与类名相同
    3. 无参构造器被称为默认构造器, 不手动去添加构造方法时,系统默认有一个无参构造方法,如果自己手动写了有参构造方法,则默认的无参构造方法失效
    class Rock2 {
      Rock2(int i) {
        System.out.print("Rock " + i + " ");
      }
    }
    
    public class SimpleConstructor2 {
      public static void main(String[] args) {
        for(int i = 0; i < 8; i++)
          new Rock2(i);
    
         //new Rock2();  编译错误
      }
     
    }
    /* Output:
    Rock 0 Rock 1 Rock 2 Rock 3 Rock 4 Rock 5 Rock 6 Rock 7
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2. 方法重载

    方法重载是必要的,它允许方法具有相同的方法名但接收的参数不同(参数类型或参数顺序不同),静态方法和成员方法名字参数相同也是方法重复, 定义不同的构造方法,实际上就是构造器重载

    //编译提示方法名重复
     public int f(){
        return 1;
      }
      public double f(){
        return 1.1;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    区分重载方法:

    //根据参数类型和位置区分重载方法
    public class OverloadingOrder {
      static void f(String s, int i) {
        System.out.println("String: " + s + ", int: " + i);
      }
      static void f(int i, String s) {
        System.out.println("int: " + i + ", String: " + s);
      }
      public static void main(String[] args) {
        f("String first", 11);
        f(99, "Int first");
      }
    }
    /* Output:
    String: String first, int: 11
    int: 99, String: Int first
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    重载与基本类型:

    基本类型可以自动从较小的类型转型为较大的类型

    public static void main(String[] args) {
          f(1);//1.0  int类型向double类型转化
    
      }
      public static void f(double a){
          System.out.println(a);
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    返回值不同不能作为重载的依据

    void f(){}
    int f() {return 1;}
    
    • 1
    • 2

    对于上面两个方法,存在以下两种调用情形:

    case1: int x=f(); 此时编译器知道调用的是有返回值的方法f()
    case2: f(); 此时没有接受变量,编译器就不知道调用哪个方法了

    因此,不能根据返回值类型区分重载的方法


    3. this关键字

    class Banana { void peel(int i) { /* ... */ } }
    
    public class BananaPeel {
      public static void main(String[] args) {
        Banana a = new Banana(),
               b = new Banana();
        a.peel(1);
        b.peel(2);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    只有一个方法 peel() ,那么怎么知道调用的是对象 a 的 peel() 方法还是对象 b 的 peel() 方法?
    答案:peel()方法中第一个参数隐密地传入了一个指向操作对象的引用

    this 关键字只能在非静态方法内部使用。当你调用一个对象的方法时,this 生成了一个对象引用
    在一个类的方法里调用该类的其他方法,不要使用 this,直接调用即可,this 自动地应用于其他方法上了

    
    public class Apricot {
      void pick() { /* ... */ }
      void pit() { 
      	pick(); 
      	//this.pick();  也可以
      /* ... */
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    this 关键字只用在一些必须显式使用当前对象引用的特殊场合。例如,用在return 语句中返回对当前对象的引用:

    public class Leaf {
      int i = 0;
      Leaf increment() {
        i++;
        return this;
      }
      void print() {
        System.out.println("i = " + i);
      }
      public static void main(String[] args) {
        Leaf x = new Leaf();
        x.increment().increment().increment().print();
      }
    }
    /* Output:
    i = 3
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    4. 在构造器中调用构造器

    package housekeeping;
    
    public class Flower {
      int petalCount = 0;
      String s = "initial value";
      Flower(int petals) {
        petalCount = petals;
        System.out.println(
          "Constructor w/ int arg only, petalCount= "
          + petalCount);
      }
      Flower(String ss) {
        System.out.println(
          "Constructor w/ String arg only, s = " + ss);
        s = ss;
      }
      Flower(String s, int petals) {
        this(petals);
        //Flower(petals);  必须使用this(petals)
        //- this(s); // Can't call two!
        this.s = s; // Another use of "this"
        System.out.println("String & int args");
      }
      Flower() {
        this("hi", 47);
        System.out.println("Zero-argument constructor");
      }
      void printPetalCount() {
        //- this(11); // Not inside non-constructor!
        System.out.println(
          "petalCount = " + petalCount + " s = "+ s);
      }
      public static void main(String[] args) {
        Flower x = new Flower();
        x.printPetalCount();
      }
    }
    /* Output:
    Constructor w/ int arg only, petalCount= 47
    String & int args
    Zero-argument constructor
    petalCount = 47 s = hi
    */
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • this调用其他的构造器这条语句必须是当前构造器的第一条语句
    • 一个构造器只能调用一次其他的构造器方法
    • 不能在构造器之外的方法里调用构造器

    static 的含义

    1. static方法中不会存在 this
    2. 不能在静态方法中调用非静态方法(所以在main函数中调用非静态方法需要先创建对象,再通过对象调用成员方法),但是非静态方法中可以调用静态方法

    5. 垃圾回收器

    使用new创建的对象会被垃圾回收器回收,但存在一些比较特殊的情况,即通过某种创建对象方式之外
    的方式为对象分配了存储空间(分配内存时可能采用了类似 C 语言中的做法,这种情况主要发生在使用 “本地方法”, JNI), 对于这种分配方式,需要使用fianlize方法来手动地释放空间(比如在finalize函数中使用C语言中的free)

    fianlize方法的另外一个用法:

    eg: 对象代表了一个打开的文件,在对象被垃圾回收之前程序员应该关闭这个文件

    class Book {
      boolean checkedOut = false;
      Book(boolean checkOut) {
        checkedOut = checkOut;
      }
      void checkIn() {
        checkedOut = false;
      }
      @SuppressWarnings("deprecation")
      @Override public void finalize() {
        if(checkedOut)
          System.out.println("Error: checked out");
        // Normally, you'll also do this:
        // super.finalize(); // Call the base-class version
      }
    }
    
    public class TerminationCondition {
      public static void main(String[] args) throws InterruptedException {
        Book novel = new Book(true);
        // Proper cleanup:
        novel.checkIn();
        // Drop the reference, forget to clean up:
        new Book(true);
        // Force garbage collection & finalization:
        System.gc();
        Thread.sleep(1000);; // One second delay
      }
    }
    /* Output:
    Error: checked out
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    上面代码中模拟书的登记情况,即某本书的对象在被垃圾回收之前应该被登记(checkedOut值为false), 为了避免遗漏登记的情况,可以finalize方法中进行判断。

    垃圾回收器如何工作:

    一种比较慢的垃圾回收机制: 引用计数法

    每个对象中含有一个引用计数器,每当有引用指向该对象时,引用计数加 1。当引用离开作用域或被置为 null 时,引用计数减 1,当发现某个对象的引用计数为 0 时,就释放其占用的空间
    缺点:如果对象之间存在循环引用,那么它们的引用计数都不为 0,就会出现应该被回收但无法被回收的情况

    一直比较块的垃圾回收机制:root追溯法

    对于任意 “活” 的对象,一定能最终追溯到其存活在栈或静态存储区中的引用
    如果从栈或静态存储区出发,遍历所有的引用,你将会发现所“活” 的对象。对于发现的每个引用,必须追踪它所引用的对象,然后是该对象包含的所有引用,如此反复进行,直到访问完 “根源于栈或静态存储区的引用” 所形成的整个网络


    6. 成员初始化

    • 成员变量即使不初始化也有一个初始值,比如int类型默认值为0,引用类型默认值是null
    • 可以指定成员变量的初值,指定初值可以使用字面量,也可以使用带有返回值的函数
    public class MethodInit {
    	int i = f();//ok
    	int j=1;//ok
    	int f() {
    		return 11;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    方法可以带有参数,但这些参数不能是未初始化的类成员变量:

    public class MethodInit {
    	int i = f();//ok
    	int j=f(i);//ok
    	int f(int a) {
    		return 11;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意不能向前引用:

    public class MethodInit {
    	int i = f(j);//not ok  j此时还没有初始化
    	int j=f(i);//ok
    	int f(int a) {
    		return 11;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    7. 构造器初始化

    public class Counter {
    	int i;
    	Counter() {
    		i = 7;
    	}
    }
    //i 首先会被初始化为 0,然后变为 7
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    类中变量定义的顺序决定了它们初始化的顺序。即使变量定义散布在方法定义之间,它们仍会在任何方法(包括构造器)被调用之前得到初始

    class Window {
      Window(int marker) {
        System.out.println("Window(" + marker + ")");
      }
    }
    
    class House {
      Window w1 = new Window(1); 
      House() {
        // Show that we're in the constructor:
        System.out.println("House()");
        w3 = new Window(33); // Reinitialize w3
      }
      Window w2 = new Window(2); 
      void f() { System.out.println("f()"); }
      Window w3 = new Window(3); 
    }
    
    public class OrderOfInitialization {
      public static void main(String[] args) {
        House h = new House();
        h.f(); // Shows that construction is done
      }
    }
    /* Output:
    Window(1)
    Window(2)
    Window(3)
    House()
    Window(33)
    f()
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    引用类型的成员变量window的初始化在house构造器之前执行, 不管window类型的引用声明的位置在house构造器前还是后

    静态数据的初始化:

    无论创建多少个对象,静态数据都只占用一份存储区域。static 关键字不能应用于局部变量,所以只能作用于属性(字段、域)。如果一个字段是静态的基本类型,你没有初始化它,那么它就会获得基本类型的标准初值。如果它是对象引用,那么它的默认初值就是 null

    • 静态初始化只有在必要时刻才会进行,第一次创建静态对象或引用静态对象
    • 初始化的顺序先是静态对象(如果它们之前没有被初始化的话),然后是非静态对象
    • 静态初始化只会在首次加载 Class 对象时初始化一次

    显式的静态初始化

    public class TerminationCondition {
         static {
            i = 48;
        }
        static int i=12;
       
        public static void main(String[] args) throws InterruptedException {
            System.out.println(TerminationCondition.i);//12
            //i值变化:0->48->12
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    上面代码段中的静态代码块只会执行一次

    非静态实例初始化

    
    class Mug {
      Mug(int marker) {
        System.out.println("Mug(" + marker + ")");
      }
    }
    
    public class Mugs {
      Mug mug1;
      Mug mug2;
      {                                         // [1]
        mug1 = new Mug(1);
        mug2 = new Mug(2);
        System.out.println("mug1 & mug2 initialized");
      }
      Mugs() {
        System.out.println("Mugs()");
      }
      Mugs(int i) {
        System.out.println("Mugs(int)");
      }
      public static void main(String[] args) {
        System.out.println("Inside main()");
        new Mugs();
        System.out.println("new Mugs() completed");
        new Mugs(1);
        System.out.println("new Mugs(1) completed");
      }
    }
    /* Output:
    Inside main()
    Mug(1)
    Mug(2)
    mug1 & mug2 initialized
    Mugs()
    new Mugs() completed
    Mug(1)
    Mug(2)
    mug1 & mug2 initialized
    Mugs(int)
    new Mugs(1) completed
    */
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    非静态代码块与静态代码块相比,只不过少了 static 关键字, 非静态代码块和静态代码块一样,在构造器之前执行


    7. 数组初始化

    Java中的数组是对象,数组变量是一个引用

    public class ArraysOfPrimitives {
      public static void main(String[] args) {
        int[] a1 = { 1, 2, 3, 4, 5 };
        int[] a2;
        a2 = a1;
        for(int i = 0; i < a2.length; i++)
          a2[i] += 1;
        for(int i = 0; i < a1.length; i++)
          System.out.println("a1[" + i + "] = " + a1[i]);
      }
    }
    /* Output:
    a1[0] = 2
    a1[1] = 3
    a1[2] = 4
    a1[3] = 5
    a1[4] = 6
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    动态数组创建
    不确定数组中需要多少个元素,可以使用 new 在数组中创建元素

    public class ArrayNew {
      public static void main(String[] args) {
        int[] a;
        Random rand = new Random(47);
        a = new int[rand.nextInt(20)];
        System.out.println("length of a = " + a.length);
        System.out.println(Arrays.toString(a));
      }
    }
    /* Output:
    length of a = 18
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    可变参数列表

    public class NewVarArgs {
      static void printArray(Object... args) {//三个.表示可变参数
        for(Object obj : args)
          System.out.print(obj + " ");
        System.out.println();
      }
      public static void main(String[] args) {
        // Can take individual elements:
        printArray(47, (float) 3.14, 11.11);
        printArray(47, 3.14F, 11.11);
        printArray("one", "two", "three");
        printArray(new A(), new A(), new A());
        // Or an array:
        printArray((Object[])new Integer[]{ 1, 2, 3, 4 });
        printArray(); // Empty list is OK
      }
    }
    /* Output:
    47 3.14 11.11
    47 3.14 11.11
    one two three
    A@19e0bfd A@139a55 A@1db9742
    1 2 3 4
    */
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    可变参数必须是参数列表中的最后一个参数,当普通参数和可变参数同时存在时,先将参数匹配前面的普通的参数,匹配完剩余的参数给可变参数

      public int f(int ...args,int b){}//  error
      public int f(int b,int ...args){}//  ok
    
    • 1
    • 2

    8. 枚举类型

    public enum Spiciness {
      NOT, MILD, MEDIUM, HOT, FLAMING
    }
    public class EnumOrder {
      public static void main(String[] args) {
        for(Spiciness s : Spiciness.values())
          System.out.println(
            s + ", ordinal " + s.ordinal());
      }
    }
    /* Output:
    NOT, ordinal 0
    MILD, ordinal 1
    MEDIUM, ordinal 2
    HOT, ordinal 3
    FLAMING, ordinal 4
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 枚举类型的toString方法被重写
    • ordinal() 方法表示某个特定 enum 常量的声明顺序(从0开始)

    枚举类型可以在switch语句中使用

    public class Burrito {
      Spiciness degree;
      public Burrito(Spiciness degree) {
        this.degree = degree;
      }
      public void describe() {
        System.out.print("This burrito is ");
        switch(degree) {
          case NOT:    System.out.println(
                          "not spicy at all.");
                       break;
          case MILD:
          case MEDIUM: System.out.println("a little hot.");
                       break;
          case HOT:
          case FLAMING:
          default:     System.out.println("maybe too hot.");
        }
      }
      public static void main(String[] args) {
        Burrito
          plain = new Burrito(Spiciness.NOT),
          greenChile = new Burrito(Spiciness.MEDIUM),
          jalapeno = new Burrito(Spiciness.HOT);
        plain.describe();
        greenChile.describe();
        jalapeno.describe();
      }
    }
    /* Output:
    This burrito is not spicy at all.
    This burrito is a little hot.
    This burrito is maybe too hot.
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
  • 相关阅读:
    通达信软件编程写一段抄底的源代码
    Visual Studio清单作用
    2023年9月青少年软件编程(C 语言) 等级考试试卷(五级)
    【CSP-J 2021】总结
    jQuery学习:显示隐藏 一级菜单
    JVM内存模型与垃圾回收
    Android Studio 实现登录注册-源代码 (连接MySql数据库)
    (web前端网页制作课作业)使用HTML+CSS制作非物质文化遗产专题网页设计与实现
    不想离职后隐私“被扒”,这几个电脑文件一定要删
    最新版本 Stable Diffusion 开源AI绘画工具之部署篇
  • 原文地址:https://blog.csdn.net/qq_43478694/article/details/126460700