java基础学习(四)初始化与清理

本文深入探讨Java中的构造器、方法重载、this关键字、静态关键字等核心概念,解析对象初始化过程及数组、可变参数列表、枚举类型的使用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、用构造器确保初始化
java中通过提供构造器,可确保每个对象都会得到初始化。创建对象时,如果其类具有构造器,那java会在用户操作对象之前自动调用相应的构造器,从而保证初始化的进行。
构造器的名称必须与类名完全相同。避免与成员名称相冲突,也方便编译器知道调用哪个方法。不接受任何参数的构造器叫做默认构造器,也成为无参构造器。也可以使用带有形式参数的构造器:

public class ConstructorTest {
    ConstructorTest(){
        System.out.println("无参构造器");
    }
    ConstructorTest(int a){
        System.out.print("有参构造器");
    }
}

如果类中没有构造器,编译器会自动创建一个默认的构造器,如果已经定义了一个构造器(无论是否有参数),编译器就不会再自动创建默认构造器。

在创建对象时,new ConstructorTest()将会为对象分配存储空间,并调用相应的构造器。
在java中,初始化和创建捆绑在一起,两者不能分离。

构造器是一种特殊类型的方法,它没有返回值。这与返回值为空(void)明显不同。对于空返回值,尽管方法本身不会自动返回什么,但仍可选择让它返回别的东西。构造器则不会返回任何东西。new表达式确实返回了新建对象的引用,但是构造器本身并没有任何返回值。

2、方法重载
方法名相同而形式参数不同。每一个重载的方法都必须有一个独一无二的参数类型列表。参数顺序的不同也可以区分两个方法。如果编译器能够明确判断出语义,那么也可以考虑用返回类型区别方法重载;但是有时候忽略返回值类型,只是单纯调用方法,那就不好区分了。

    static void f(int a,String b){
        System.out.print("整形a,字符串b");
    }
    static void f(String a,int b){
        System.out.print("字符串a,整形b");
    }

基本类型的重载:
基本类型能从一个较小的类型字段提升为一个较大的类型。

void f1(char a){ System.out.print("char类型方法f1  ");}
void f1(byte a){ System.out.print("byte类型方法f1  ");}
void f1(short a){ System.out.print("short类型方法f1   ");}
void f1(int a){ System.out.print("int类型方法f1  ");}
void f1(long a){ System.out.print("long类型方法f1  ");}
void f1(float a){ System.out.print("float类型方法f1  ");}
void f1(double a){ System.out.print("double类型方法f1  ");}

void f2(byte a){ System.out.print("byte类型方法f2  ");}
void f2(short a){ System.out.print("short类型方法f2  ");}
void f2(int a){ System.out.print("int类型方法f2  ");}
void f2(long a){ System.out.print("long类型方法f2  ");}
void f2(float a){ System.out.print("float类型方法f2  ");}
void f2(double a){ System.out.print("double类型方法f2  ");}

void f3(short a){ System.out.print("short类型方法f3  ");}
void f3(int a){ System.out.print("int类型方法f3   ");}
void f3(long a){ System.out.print("long类型方法f3  ");}
void f3(float a){ System.out.print("float类型方法f3  ");}
void f3(double a){ System.out.print("double类型方法f3  ");}

void f4(int a){ System.out.print("int类型方法f4  ");}
void f4(long a){ System.out.print("long类型方法f4  ");}
void f4(float a){ System.out.print("float类型方法f4  ");}
void f4(double a){ System.out.print("double类型方法f4  ");}


void f5(long a){ System.out.print("long类型方法f5  ");}
void f5(float a){ System.out.print("float类型方法f5  ");}
void f5(double a){ System.out.print("double类型方法f5  ");}

void f6(float a){ System.out.print("float类型方法f6  ");}
void f6(double a){ System.out.print("double类型方法f6  ");}

void f7(double a){ System.out.print("double类型方法f7  ");}

1、char类型:有char类型的形式参数的方法,则调用该方法;如果没有该类型的方法,直接提升至int类型;若int型也没有,直接提升至long或者double型:

public static void main(String[] args) {
        PrimitiveOverLoad pol=new PrimitiveOverLoad();
        char c='x';
        pol.f1(c);     pol.f2(c);     pol.f3(c);
        pol.f4(c);     pol.f5(c);     pol.f6(c);
        pol.f7(c);
    }
结果:
char类型方法f1  int类型方法f2     int类型方法f3   int类型方法f4  long类型方法f5  float类型方法f6  double类型方法f7  

2、byte型参数:有该类型参数的方法,那就调用该方法,没有的话,就一步步提升较大类型:

public static void main(String[] args) {
        PrimitiveOverLoad pol=new PrimitiveOverLoad();
       byte c=1;
        pol.f1(c);     pol.f2(c);     pol.f3(c);
        pol.f4(c);     pol.f5(c);     pol.f6(c);
        pol.f7(c);
    }
结果:
byte类型方法f1  byte类型方法f2   short类型方法f3  int类型方法f4  long类型方法f5  float类型方法f6  double类型方法f7  

3、short型参数:有该类型参数的方法,那就调用该方法,没有的话,就一步步提升较大类型:

public static void main(String[] args) {
        PrimitiveOverLoad pol=new PrimitiveOverLoad();
       short c=1;
        pol.f1(c);     pol.f2(c);     pol.f3(c);
        pol.f4(c);     pol.f5(c);     pol.f6(c);
        pol.f7(c);
    }
结果:
short类型方法f1   short类型方法f2  short类型方法f3  int类型方法f4  long类型方法f5  float类型方法f6  double类型方法f7  

4、int型参数:有该类型参数的方法,那就调用该方法,没有的话,就一步步提升较大类型:

public static void main(String[] args) {
        PrimitiveOverLoad pol=new PrimitiveOverLoad();
       int c=1;
        pol.f1(c);     pol.f2(c);     pol.f3(c);
        pol.f4(c);     pol.f5(c);     pol.f6(c);
        pol.f7(c);
    }
结果:
int类型方法f1  int类型方法f2      int类型方法f3   int类型方法f4  long类型方法f5  float类型方法f6   double类型方法f7  

其他剩余类型long、float、double都是类似的规则。

3、this关键字
同一个类的不同对象调用同一个方法,在编译器内部,将不同对象的引用作为第一个参数传递给该方法。但是,如果希望在方法内部获得这个当前对象的引用,可以使用 this 关键字。

this关键字只能在方法内部使用,表示对”调用方法的那个对象”的引用。与其他对象的引用的用法一样。但是,如果在方法内部调用同一个类的其他方法,可以不必写this,编译器会自动添加,直接调用就可以。
只有当需要明确指出对当前对象的引用时,才需要使用this关键字:

public class ThisTest {
    static int i=0;
    ThisTest increament(){
        i++;
        return this;
    }
    public static void main(String[] args) {
        ThisTest tt=new ThisTest();
        //调用的都是同一个对象引用
        tt.increament().increament().increament();
        System.out.print("i="+i);
    }
}
结果:
i=3

也可以使用this关键字将当前对象作为参数,传递给其他方法:

public class TestThis {
    public static void main(String[] args) {
        new Person().eat(new Apple());
    }
}
class Person{
    public void eat(Apple apple){
        //返回一个当前对象的引用
        Apple peeled=apple.getPeeled();

        System.out.print("eat apple Yummy");
    }
}
class Apple{
    Apple getPeeled(){
        //传递当前Apple对象的引用,而不是直接创建该对象引用
        return Peeler.peel(this);
    }
}
class Peeler{
    static Apple peel(Apple apple){
        //返回apple对象,可以被多个方法使用
        return apple;
    }
}

4、在构造器中调用构造器
同一个类中,使用this关键字,可以在一个构造器中调用另一个构造器。

可以为this添加参数列表,会对符合参数列表的某个构造器明确调用。

在构造器中,只能使用一次this调用其他构造器。并且必须将this构造器调用放置在最起始处。禁止在其他任何方法中调用构造器。
这里写图片描述
上图中出现错误提醒:在父类构造函数初始化之前不能引用这个变量。
在前面加上static关键字之后,就正常使用。这个是与java初始化顺序有关的。即使没有显式地使用static关键字,构造器实际上是静态方法。

或者直接传入参数值,而不是引用,也可以正常使用:

    Flower(){
       //直接传入值
        this("lisi",32);
    }

    Flower(int age){
    //参数age与数据成员age的名称相同
        this.age=age;
    }
    Flower(String s){
        this.s=s;
    }

    Flower(String s,int age){
        this(age);
        this.s=s;
    }

6、static关键字
static方法就是没有this的方法。在static方法的内部不能调用非静态方法,但是反过来可以。可以通过类本身调用static方法。java中禁止全局方法,但static方法很像全局方法。

静态方法可以访问其他static方法和static域,但是不能访问非静态方法和非静态域;

非静态方法可以访问静态或者非静态的方法和域。
这里写图片描述

7、成员初始化顺序
对于方法的局部变量,没有显式赋初值会报错。
这里写图片描述

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

public class InitialValue {
    boolean bool;
    char c;
    byte b;
    short s;
    int i;
    long lng;
    float f;
    double d;
    InitialValue iv;
    void printValue(){
        System.out.println("bool="+bool);
        System.out.println("c="+c);
        System.out.println("b="+b);
        System.out.println("s="+s);
        System.out.println("i="+i);
        System.out.println("lng="+lng);
        System.out.println("f="+f);
        System.out.println("d="+d);
        System.out.println("iv="+iv);
    }

    public static void main(String[] args) {
        InitialValue value=new InitialValue();
        value.printValue();
    }
结果:
bool=false
//char值为0,所以显示空白
c= 
b=0
s=0
i=0
lng=0
f=0.0
d=0.0
//对象引用未初始化时,引用获得特殊值null
iv=null

7.1、构造器初始化
可以通过调用方法或者指定初始化值的方式进行初始化;但是无法阻止自动初始化的进行,它将在构造器被调用之前发生。
例如:int a=7;那么a会先被置为0,然后变成7。

对于所有基本类型和对象引用,包括定义时,已经指定初始值的变量,这种情况都是成立的。

7.1.1、初始化顺序(没有静态数据的情况下)
在类的内部,变量定义的先后顺序决定了初始化的顺序,即使变量定义散布于方法定义之间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。

public class OrderInitial {
    public static void main(String[] args) {
        House house=new House();
        house.f();
    }
}
class Window{
    Window(int mark){
        System.out.println("mark = [" + mark + "]");
    }
}
class House{
    //Window对象的引用分散在方法之间
    Window w1=new Window(1);
    House(){
        System.out.println("House()");
        w3=new Window(33);
    }
    Window w2=new Window(2);
    void f(){
        System.out.println("f()");
    }
    Window w3=new Window(3);
}
结果:
//在调用House构造函数之前,先对House类中的变量初始化
mark = [1]
mark = [2]
mark = [3]
//变量初始化完毕后,再调用构造函数
House()
//在构造函数中对变量w3再次初始化,那么第一次引用的对象将被丢弃,被回收
mark = [33]
//调用House对象的方法
f()

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

public class StaticInital {
    //定义两个静态变量,一个静态方法main
    static Table table=new Table();
    public static void main(String[] args) {
       new Cupboard();
        table.f2(1);
        cupboard.f3(1);
    }
    static Cupboard cupboard=new Cupboard();
}

class Table{
    //定义两个静态变量分散在方法间
    static Bowl bowl=new Bowl(1);
    Table(){
        System.out.println("Table()");
        //调用 bowl2 的方法f1
        bowl2.f1(1);
    }
    void f2(int mark){
        System.out.println("mark = [" + mark + "]");
    }
    static Bowl bowl2=new Bowl(2);
}

class Bowl{
    Bowl(int mark){
        System.out.println("mark = [" + mark + "]");
    }
    void f1(int x){
        System.out.println("x = [" + x + "]");
    }
}

class Cupboard{
    //定义一个非静态变量,两个静态变量
    Bowl bowl2=new Bowl(3);
    static Bowl bowl4=new Bowl(4);
    Cupboard(){
        System.out.println("Cupboard()");
        bowl4.f1(2);
    }
    void f3(int y){
        System.out.println("y = [" + y + "]");
    }
    static Bowl bowl5=new Bowl(5);
}
结果:
//先初始化静态变量,再执行静态方法
//初始化静态变量Table
//在Table中,先初始化静态变量,再执行构造方法
mark = [1]
mark = [2]
Table()
x = [1]
//初始化静态变量Cupboard
//在Cupboard中,先初始化静态变量,在初始化非静态变量,最后执行构造方法
mark = [4]
mark = [5]
mark = [3]
Cupboard()
x = [2]
//执行main方法
//第一次访问静态数据的时候,它们才会被初始化,此后,静态对象不会再次被初始化。但是非静态变量会重新被初始化。
mark = [3]
Cupboard()
x = [2]
//访问静态变量的方法
mark = [1]
y = [1]

对象创建过程总结(包括初始化顺序)
1、查找class文件:当首次创建类的对象,或者首次访问类的静态方法/静态域,Java解释器必须查找类路径,以定位.class文件。
2、静态初始化:载入.class文件,执行静态初始化动作。因此,静态初始化只在类对象首次加载的时候进行一次。
3、分配存储空间:当使用new 创建对象时,首先在堆上为对象分配足够的存储空间。
4、默认值初始化:将存储空间清零,自动将对象中的所有基本类型数据都设置为默认值,而引用类型设置为null。
5、自定义初始化:执行所有出现于字段定义处的初始化动作。
6、执行构造器。

8、数组初始化
数组是相同类型的、用一个标识符名称封装到一起的一个对象序列或基本类型数据序列。

public class ArrayTest {
    public static void main(String[] args) {
        int[] array_1=new int[6];
        //不能加数组长度
        int[] array_2=new int[]{1,2,3,4};
        int[] array_3={5,6,7};
        //只是复制了一个引用
        int[] copy_array=array_2;
        for(int i=0;i<array_2.length;i++){
            array_2[i]=array_2[i]+1;
        }
        System.out.println(Arrays.toString(copy_array));
       //基本数据类型会自动初始化为默认值(数字和字符,就是0;布尔型就是false)
        System.out.println(Arrays.toString(array_1));
    }
}
结果:
[2, 3, 4, 5]
[0, 0, 0, 0, 0, 0]

9、可变参数列表
可应用于参数个数或类型未知的场合。

public class ArgsTest {
    static void printArray(Object... args){
        for(Object o:args){
            System.out.println("o="+o);
        }
    }

    public static void main(String[] args) {
        //指定参数时,编译器实际会填充为数组,获取的依旧是一个数组
        printArray(1,2,3,4);
        printArray("sw","22dede");
        //参数已经是一个数组,直接当做可变参数列表
        printArray(new int[]{43,65,55,66});
        //将0个参数传递也是可行的
        printArray();
    }
}
结果:
o=1
o=2
o=3
o=4
o=sw
o=22dede
o=[I@6d6f6e28

10、枚举类型
关键字enum。枚举类型的实例是常量,按照命名惯例,它们都用大写字母表示,有多个单词的,用下划线将它们隔开:

public class Enumtest {
    public static void main(String[] args) {
        //显示枚举值
        Spiciness mild=Spiciness.MILD;
        System.out.println("mild="+mild);
        //遍历枚举的值
        for(Spiciness s:Spiciness.values()){
            //返回值 和 声明的顺序
            System.out.println(s+" ordinal:"+s.ordinal());
        }
    }
}
enum Spiciness{
    NO,MILD,MED_HOT,FLAMING
}
结果:
mild=MILD
NO ordinal:0
MILD ordinal:1
MED_HOT ordinal:2
FLAMING ordinal:3

枚举类型可以与switch语句内使用:

public class EnumSwitch {
    public static void main(String[] args) {
        ShowCountry sc=new ShowCountry(Country.CHINA);
        sc.getCountry();
    }
}

enum Country{
    CHINA,USA,UK,CANADA
}

class ShowCountry{
    Country country;
    ShowCountry(Country country){
        this.country=country;
    }

    public void getCountry() {
        //直接将枚举类型作为选择参数
        switch (country){
            case CHINA:
                System.out.println("中国");break;
            case USA:
                System.out.println("美国");break;
            case UK:
                System.out.println("英国");break;
            default:
                System.out.println("加拿大");
        }
    }
}
结果:
中国
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值