第五章 初始化与清理

第五章 初始化与清理

  • 问题:
    1. 忘记初始化、不知道如何初始化
    2. 忘记清理已经不用的变量 ——> 资源用尽
  • 解决:
    1. 构造器:一个在创建对象时被自动调用的特殊方法
    2. 垃圾回收器:对于不再使用的内存资源,垃圾回收器会自动将其释放。

5.1 用构造器确保初始化

Java会在用户有能力操作对象之前自动调用相应的构造器,从而保证初始化的进行。
  • 构造器命名:

    1. 避免与成员命名冲突
    2. 编译器必须知道构造器名称,以便调用

    构造器采用和类相同的名称

package com.alex2ice.chapter5;
import static com.alex2ice.util.Print.print;
/**
 * @author Alex2ice
 * @version v1.0.0
 * @time 2023/2/20 15:56
 * @description 用构造器确保初始化
 */
public class Demo01 {
    // 如果没有写构造器,会默认生成一个无参构造器
    Demo01() { // 无参构造器
        print("Demo01");
    }
    // 构造器重载
    Demo01(int id) { // 有参构造器
        print("Demo01 " + id);
    }
    public static void main(String[] args) {
        new Demo01();
        new Demo01(512); // new表达式返回了对新对象的引用,但构造器不会返回任何东西。
    }
}

5.2 方法重载

Java中,想要以不同的方式创建对象,就必须对构造器进行重载,同时也可以对成员方法进行重载 

5.2.1 区分重载方法

每个重载的方法必须有一个独一无二的参数类型列表
参数数目不同、类型不同、顺序不同...都可以

5.2.2 涉及基本类型的重载

基本类型能自动从一个“较小”的类型提升至一个“较大”的类型
public class Demo02 {
  static void f1(char x) {printnb("f1(char)");}
  static void f1(byte x) {printnb("f1(byte)");}
  static void f1(short x) {printnb("f1(short)");}
  static void f1(int x) {printnb("f1(int)");}
  static void f1(long x) {printnb("f1(long)");}
  static void f1(float x) {printnb("f1(float)");}
  static void f1(double x) {printnb("f1(double)");}
  static void f2(byte x) {printnb("f2(byte)");}
  static void f2(short x) {printnb("f2(short)");}
  static void f2(int x) {printnb("f2(int)");}
  static void f2(long x) {printnb("f2(long)");}
  static void f2(float x) {printnb("f2(float)");}
  static void f2(double x) {printnb("f2(double)");}
  static void f3(short x) {printnb("f3(short)");}
  static void f3(int x) {printnb("f3(int)");}
  static void f3(long x) {printnb("f3(long)");}
  static void f3(float x) {printnb("f3(float)");}
  static void f3(double x) {printnb("f3(double)");}
  static void f4(int x) {printnb("f4(int)");}
  static void f4(long x) {printnb("f4(long)");}
  static void f4(float x) {printnb("f4(float)");}
  static void f4(double x) {printnb("f4(double)");}
  static void f5(long x) {printnb("f5(long)");}
  static void f5(float x) {printnb("f5(float)");}
  static void f5(double x) {printnb("f5(double)");}
  static void f6(float x) {printnb("f6(float)");}
  static void f6(double x) {printnb("f6(double)");}
  static void f7(double x) {printnb("f7(double)");}
  public static void main(String[] args) {
    printnb("const value: ");
    f1(5);f2(5);f3(5);f4(5);f5(5);f6(5);f7(5);
    print();
    printnb("char value: ");
    char x = 'x';
    f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);
    print();
    printnb("byte value: ");
    byte b = 1;
    f1(b);f2(b);f3(b);f4(b);f5(b);f6(b);f7(b);
    print();
    printnb("short value: ");
    short s = 1;
    f1(s);f2(s);f3(s);f4(s);f5(s);f6(s);f7(s);
    print();
    printnb("int value: ");
    int i = 1;
    f1(i);f2(i);f3(i);f4(i);f5(i);f6(i);f7(i);
    print();
    printnb("long value: ");
    long l = 1;
    f1(l);f2(l);f3(l);f4(l);f5(l);f6(l);f7(l);
    print();
    printnb("float value: ");
    float f = 1;
    f1(f);f2(f);f3(f);f4(f);f5(f);f6(f);f7(f);
    print();
    printnb("double value: ");
    double d = 1;
    f1(d);f2(d);f3(d);f4(d);f5(d);f6(d);f7(d);
    /*
      const value: f1(int)f2(int)f3(int)f4(int)f5(long)f6(float)f7(double)
      char value: f1(char)f2(int)f3(int)f4(int)f5(long)f6(float)f7(double)
      byte value: f1(byte)f2(byte)f3(short)f4(int)f5(long)f6(float)f7(double)
      short value: f1(short)f2(short)f3(short)f4(int)f5(long)f6(float)f7(double)
      int value: f1(int)f2(int)f3(int)f4(int)f5(long)f6(float)f7(double)
      long value: f1(long)f2(long)f3(long)f4(long)f5(long)f6(float)f7(double)
      float value: f1(float)f2(float)f3(float)f4(float)f5(float)f6(float)f7(double)
      double value: f1(double)f2(double)f3(double)f4(double)f5(double)f6(double)f7(double)
     */
  }
}
  • 结论
    • 常数值5被当作 int 处理,所以如果有某个重载方法接受int型参数,它就会被调用。

    • 其它类型,如果传入的数据类型小于方法中声明的形式参数类型,实际数据类型就会被提升。

    • char型,如果无法找到恰好接受char参数的方法,就会把char直接提升至 int 型

      方法接受较小的基本类型作为参数。但如果传入的实际参数较大,就得通过类型转换来执行窄化转换。

public class Demo03 {
    static void f1(char x) {printnb("f1(char)");}
    static void f1(byte x) {printnb("f1(byte)");}
    static void f1(short x) {printnb("f1(short)");}
    static void f1(int x) {printnb("f1(int)");}
    static void f1(long x) {printnb("f1(long)");}
    static void f1(float x) {printnb("f1(float)");}
    static void f1(double x) {printnb("f1(double)");}
    static void f2(char x) {printnb("f2(char)");}
    static void f2(byte x) {printnb("f2(byte)");}
    static void f2(short x) {printnb("f2(short)");}
    static void f2(int x) {printnb("f2(int)");}
    static void f2(long x) {printnb("f2(long)");}
    static void f2(float x) {printnb("f2(float)");}
    static void f3(char x) {printnb("f3(char)");}
    static void f3(byte x) {printnb("byte)");}
    static void f3(short x) {printnb("f3(short)");}
    static void f3(int x) {printnb("f3(int)");}
    static void f3(long x) {printnb("f3(long)");}
    static void f4(char x) {printnb("f4(char)");}
    static void f4(byte x) {printnb("f4(byte)");}
    static void f4(short x) {printnb("f4(short)");}
    static void f4(int x) {printnb("f4(int)");}
    static void f5(char x) {printnb("f5(char)");}
    static void f5(byte x) {printnb("f5(byte)");}
    static void f5(short x) {printnb("f5(short)");}
    static void f6(char x) {printnb("f6(char)");}
    static void f6(byte x) {printnb("f6(byte)");}
    static void f7(char x) {printnb("f7(char)");}
    public static void main(String[] args) {
        printnb("double value: ");
        double d = 1;
        f1(d);f2((float) d);f3((long) d);f4((int) d);f5((short) d);f6((byte) d);f7((char) d);
        /*
            double value: f1(double)f2(float)f3(long)f4(int)f5(short)f6(byte)f7(char)
         */
    }
}

5.2.3 以返回值区分重载方法

void f() {}
int f() {return 1;}
int i = f(); 确实可以根据此区分重载方法
f(); 调用函数而忽略返回值,就无法判断所调用函数是哪一个
所以,根据方法返回值区分重载函数是行不通的

5.3 默认构造器

默认构造器,又叫无参构造器,它的作用是创建一个“默认对象”。
在类没有构造器时,编译器会自动创建一个默认构造器。
但是,如果已经定义了一个构造器(无论是否有参数),编译器就不会自动帮你创建默认构造器。

5.4 this关键字

public class Demo05 {
    // f()方法如何区分是那个对象调用的方法?
    // java编译器会暗自把“所操作对象的引用”作为第一个参数传递给 f().
    // Demo05.f(d1, 1);
    // Demo05.f(d2, 2);
    public void f(int i) {
        System.out.println("f方法被调用了" + i);
    }
    public static void main(String[] args) {
        Demo05 d1 = new Demo05(), d2 = new Demo05();
        d1.f(1);
        d2.f(2);
    }
}
java编译器会暗自把“所操作对象的引用”作为第一个参数传递给 f().
this 关键字:在方法内部,表示“调用方法的那个对象”的引用。

this的使用

public class Demo05 {
  int i = 0;
  public Demo05 increment() {
    i++;
    // this作为参数
    Test.test(this);
    // this作为返回值
    return this;
  }

  @Override
  public String toString() {
    return "" + i;
  }
  public static void main(String[] args) {
    Demo05 d1 = new Demo05();
    System.out.println(d1.increment().increment().increment().toString());
  } // 3
}
class Test {
    public static void test(Demo05 d) {
      System.out.println(d);
    }
}

5.4.1 在构造器中调用构造器

调用该类的其它构造器,可以通过 this(所调用构造器需要的参数); 调用
public class Demo05 {
    int i;
    public Demo05() {
        this(512); // this必须写在首行
        // 先调用有参构造器,再执行无参构造器后续代码
        System.out.println("无参构造器被调用了");
    }
    public Demo05(int i) {
        System.out.println("有参构造器被调用了");
        this.i = i;
    }
    public Demo05 increment() {
      // this(); // Call to 'this()' must be first statement in constructor body
      i++;
      return this;
    }
  public static void main(String[] args) {
    Demo05 d1 = new Demo05(); // 有参构造器被调用了    无参构造器被调用了
  }
}

5.4.2 static 的含义

static方法就是没有this的方法。  
在static内部不能调用其它非静态方法,但反过来可以
static方法很像全局方法,,在类中置入static方法就可以访问其它static方法和static域

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

Java有垃圾回收器负责回收无用对象占据的内存资源
但如果对象不是通过new来获得的,那么垃圾回收器就不知道该如何释放这块“特殊”内存。

Java允许在类中定义一个名为finalize()的方法。
原理:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。
System.gc()用于强制进行终结动作。
无论是垃圾回收还是终结都不保证一定会发生。如果JVM 未面临用尽的情况,它是不会浪费时间去执行垃圾回收以恢复内存的

5.5.1垃圾回收器如何工作

引用计数法(简单但速度很慢,在对象中记录引用该对象的引用数目),引用数目为0时,回收该对象内存。
垃圾回收器的思想:对任何“活”的对象,一定能最终追溯到其存活在堆栈或静态存储区之中的引用。

  1. 停止-复制
    我们把内存分成两部分,一段时间内只允许在其中一部分内存分配空间。
    当这块内存满了之后,我们把所有存活对象全部复制到另一部分内存上,并对引用进行修正,当前内存则全部清空。
  2. 标记-清扫 对一般用途而言,这种方式很慢,但如果只有少量垃圾甚至没有产生垃圾时,速度就很快了。
    思路:先对追溯到的存活对象进行标记,标记结束后,再清理释放掉没有标记的对象。
    问题:剩下的可用空间会是不连续的,不便于堆指针后续使用,所以想要连续的空间的话,就需要重新整理剩下的对象。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值