一文明白为何说万类皆有构造器

❤️❤️❤️有很多初学的小伙伴其实都有疑惑,天天说调用构造器,new调用构造器,到底什么是构造器,这个东西是干嘛的,很重要吗,一起来了解一下构造器吧❤️❤️❤️

一、什么是构造器

❤️❤️类中的构造器通常也叫构造方法、构造函数,构造器在每个项目中几乎无处不在,是在进行创建对象的时候必须要调用的。(通俗来说就是你如果要new一个对象(实例化)就要调用它的构造器)

并且构造器有以下两个特点:

  1. ❗️❗️❗️必须和类的名字相同
  2. ❗️❗️❗️必须没有返回类型,也不能写void
  • 🚩构造器格式如下:
[修饰符,比如publicprivate](没有返回类型,包括void) 类名 (参数列表,可以没有参数){
	//不能有return
}

二、构造器能干嘛(作用)

1. 使用new创建对象的时候必须使用类的构造器 (new 本质在调用构造方法)

2. 构造器中的代码执行后,可以给对象中的属性初始化赋值(初始化对象的值)
  • ❗️❗️❗️注意❗️❗️❗️
    定义有参构造后,如果想用无参构造,需要显示的定义一个无参构造,为什么这么说

要解决这个问题首先我们先来看一下构造器怎么用


三、怎么用构造器

(1)、默认构造器

在java中,即使我们在编写类的时候没有写构造器,那么在编译之后也会自动的添加一个无参构造器,这个无参构造器也被称为默认的构造器。

通俗版:新建一个类,不提供任何构造器,编译器会默认提供一个无参构造器,这就是为什么没定义任何构造器,却可以new 某个对象(实例化)

🔥🔥【示例】

public class Student{

}

main方法:
//编译通过,因为有无参构造器
Student s = new Student();

但是,大家看下下面的例子

public class Student{
   private String name;
   public Student(String name){
   this.name = name;
   }
}

main:
Student s = new Student()

大家觉得编译会通过吗?可以去试一下,答案是会编译错误的

因为没有无参构造器了,这就说明如果自定义了构造器,则会覆盖默认构造器。

这时我们再回到上面的问题**(定义有参构造后,如果想用无参构造,需要显示的定义一个无参构造)**

首先JVM会默认为类提供无参数构造方法,但是这只能是在没有任何有参构造方法的条件下,现在我们定义了有参数构造方法(JVM认为你不需要无参数构造方法了),所以JVM不再提供无参数的构造的构造方法了,如果这时还想要new Person()调用无参构造方法,就得自己写一个无参构造方法,这时你写的就叫显示的定义无参构造。

(2)、构造器重载(有参构造器)

重载简而言之就:同个方法名,不同的参数列表

除了无参构造器之外,很多时候我们还会使用有参构造器,与普通方法一样,构造器也支持重载在创建对象时候可以给属性赋值,一个对象中是可以支持同时定义多个构造器,通过不同的参数列表来实现重载。

通俗来说,我们经常看到代码中new一个对象时,有时传入参数,有时又可以不用传。比如:new People()跟new People(“6“,“张三”),这里就是重载了,我们把6岁的张三传进了构造器,成为该类的常量。

    //有参构造
public class Student{
    String age;
    String name;
    //可以通过new Person()调用
    public Person(String name) {
        this.name = name;
    }

    //可以通过new Person("age","name")调用
    public Person(String age, String name) {
    //这个就是显示的
        this.age = age;
        this.name = name;
    }
}

(3)、两个构造器之间的调用

  • 使用this关键字,在一个构造器中可以调用另一个构造器的代码

注意:this的这种用法不会产生新的对象(不是new出来的),只是调用了构造器中的代码而已。一般情况下只有使用new关键字才会创建新对象

public class Student{
    private String name;
    //报错,递归构造函数调用,说明我们调用了自己
    //Ctrl+点击this()发现会跑到构造方法Student()
    public Student(){
       this();
    }

    //this是调用类,this()是调用构造器
    public Student(String name){
       this.name = name;
    }
}

四、拓展

(1)、不希望对象(类)被外部创建

一些特殊需求,不希望定义的对象被外部创建(典型的就是单例了),那直接将构造器的修饰符改为 private即可。这样就不能在外部通过new来创建这个对象了。

简单补充介绍一下单例

单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

注意:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
  • ☀️实例

    不希望外界通过new People()来实例化,就把构造器改为private,因为new实质是调用构造器,如果私有化(private)了,调用不了自然就不能实例化了

 public class People {
     private People(){
 
 	}
 }

(2)、构造器调用的时机

先来看一个例子

package javaopp.类与对象;

class Student1{
    public Student1(){
        System.out.println("调用无参数的构造方法");
    }
}
public class people{
    public static void main(String[] args) {
        Student1 student1 = new Student1();
        System.out.println(student1);
    }
}

结果为

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eDAx1cip-1657553097697)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\image-20220711012558281.png)]

显而易见,构造方法在new的时候(创建对象即实例化的时候)就被使用了,不管里面有什么,第二行是main方法输出的System.out.println(student1);

(3)、构造器的继承

继承是Java面向对象三大特性之一,重要性不言而喻,构造器又和继承有什么联系呢

子类构造器默认调用父类无参构造器如果父类没有无参构造器,则必须在子类构造器的第一行通过 super关键字指定调用父类的哪个构造器,具体看下文例子。

final类是不允许被继承的,编译器会报错。很好理解,由于final修饰符指的是不允许被修改,而继承中,子类是可以修改父类的,重写父类方法,这里就产生冲突了,所以final类是不允许被继承的。

  • ☀️实例

定义父类构造器,由于该构造器自定义了一个带参构造器,覆盖了默认的无参构造器,所以不能直接 new SuperClass() 调用了,除非再定义一个无参构造器

/**
 * 父类构造器
 */
public class Father {
    /**
	 * 自定义有参构造器,定义有参构造后,就自动消除了默认生成的无参构造器
	 */
    public Father(String str){
    //str为传入的参数
        System.out.println("父类的有参构造方法,参数为:" + str);
    }
}

定义子类构造器,继承Father,由于Father没有无参构造器,所以必须在子类构造器中通过 super(“字符串”)来调用,否则编译器会报错✋✋✋

/**
 * 子类构造器
 */
public class Son extends Father {
    /**
     * 无参构造器
     */
    public Son(){
        //由于Father没有无参构造器,所以必须在子类构造器中通过 super("字符串")来调用,否则编译器会报错。
        //如果没有定义下面这一句,则编译器会默认调用 super()
		super("");
    }
	/**
     * 有参构造器
     */
    public Son(String str){
        //由于Father没有无参构造器,所以必须在子类构造器中通过 super("字符串")来调用,否则编译器会报错。
        //如果没有定义下面这一句,则编译器会默认调用 super()
		super(str);
    }
}

(4)、构造器的执行顺序

💛💛💛构造器、静态代码块、构造代码块💛💛💛

1)、无继承情况
public class People {

/**
 * 静态代码块
 */
static {
    System.out.println("静态代码块,程序启动后执行,只会执行一次");
}

/**
 * 默认的无参构造器
 */
public People(){
    System.out.println("默认构造器");
}

/**
 * 构造器重载,自定义一个带参构造器
 * @param str
 */
public People(String str){
    System.out.println("带参构造器,参数:" + str);
}

/**
 *构造代码块,和静态代码块区别在没有static
 */
{
    System.out.println("构造代码块,每次调用构造方法都会执行一次");
}

}

❤️实例化People

public static void main(String[] args){
    System.out.println("------------------------------------");
    People people = new People();
    System.out.println("------------------------------------");
    People people1 = new People("俺是参数");
}

❤️❤️执行以上代码,输出:

------------------------------------
静态代码块,程序启动后执行,只会执行一次
构造代码块,每次调用构造方法都会执行一次
默认构造器
------------------------------------
构造代码块,每次调用构造方法都会执行一次
带参构造器,参数:俺是参数
2)、有继承的情况
  • ❤️定义父类SuperClass
/**
 * 父类构造器
 */
   public class Father {

   {
       System.out.println("父类构造代码块,每次调用构造方法都会执行的");
   }

   /**
    * 父类无参构造方法
    */
      public Father(){
      System.out.println("父类的默认构造方法");
      }

   /**
    * 重载,自定义父类带参构造方法
    * @param str
    */
      public Father(String str){
      System.out.println("父类的带参构造方法,参数为:" + str);
      }

   static {
       System.out.println("父类的静态代码块,程序启动后执行,只会执行一次");
   }

}
  • ❤️❤️定义子类Son,继承Father
/**
 * 子类构造器,继承Father
 */
public class Son extends Father{
    
static {
      System.out.println("子类的静态代码块,程序启动后执行,只会执行一次,先执行父类的,再执行子类的");
       }
    
    {
       System.out.println("子类构造代码块,每次调用构造方法都会执行的");
    }
    
    /**
     * 无参构造器
     */
      public Son(){
//这里没有指定调用父类哪个构造器,会默认调用 super(),调用父类的无参构造器
      }
    
   /**
    * 重载构造器,传两个参数
    * @param str
    * @param str1
    */
  public Son(String str,String str1){
  //必须写在构造器第一行,调用父类构造器 public Father(String str)
  super(str);
  System.out.println("子类带参构造器:" + str1);
  }
}
  • ❤️❤️❤️实例化Son,子类
public static void main(String[] args){
    System.out.println("=======================================");
    Son Son = new Son();
    System.out.println("=======================================");
    Son Son = new Son();("子类参数1","子类参数2");
}
  • ❤️❤️❤️❤️执行以上代码,输出:
=======================================
父类的静态代码块,程序启动后执行,只会执行一次
子类的静态代码块,程序启动后执行,只会执行一次,先执行父类的,再执行子类的
父类构造代码块,每次调用构造方法都会执行
父类的默认构造方法
子类构造代码块,每次调用构造方法都会执行
=======================================
父类构造代码块,每次调用构造方法都会执行
父类的带参构造方法,参数为:子类参数1
子类构造代码块,每次调用构造方法都会执行
子类带参构造器:子类参数2
查阅资料总结得

🔥🔥🔥无继承的情况下的执行顺序

  • 静态代码块:只在程序启动后执行一次,优先级最高

  • 构造代码块:任何一个构造器被调用的时候,都会先执行构造代码块,优先级低于静态代码块

  • 构造器:优先级低于构造代码块

    • 优先级:静态代码块 > 构造代码块 > 构造器

🔥🔥🔥有继承的情况下的执行顺序

  • 1.父类静态代码块:只在程序启动后执行一次,优先级最高

  • 2.子类静态代码块:只在程序启动后执行一次,优先级低于父类静态代码块

  • 3.父类构造代码块:父类任何一个构造器被调用的时候,都会执行一次,优先级低于子类静态代码块

  • 4.父类构造器:优先级低于父类构造代码

  • 5.子类构造代码块:子类任何一个构造器被调用的时候,都会执行一次,优先级低于父类构造器

  • 6.子类构造器:优先级低于子类构造代码块

    • 总结一下优先级:父类静态代码块 > 子类静态代码块 > 父类构造代码块 > 父类构造器 > 子类构造代码块 > 子类构造器

🔥🔥 这里是哆啦的Java学习日志,不定期会分享Java学习过程中的重难点 基础好学,但是难融会贯通
对本文中有自己的想法或者建议可以私信博主,大家可以一起进步💖💖

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值