❤️❤️❤️有很多初学的小伙伴其实都有疑惑,天天说调用构造器,new调用构造器,到底什么是构造器,这个东西是干嘛的,很重要吗,一起来了解一下构造器吧❤️❤️❤️
文章目录
一、什么是构造器
❤️❤️类中的构造器通常也叫构造方法、构造函数,构造器在每个项目中几乎无处不在,是在进行创建对象的时候必须要调用的。(通俗来说就是你如果要new一个对象(实例化)就要调用它的构造器)
并且构造器有以下两个特点:
- ❗️❗️❗️必须和类的名字相同
- ❗️❗️❗️必须没有返回类型,也不能写void
- 🚩构造器格式如下:
[修饰符,比如public、private](没有返回类型,包括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);
}
}
结果为
显而易见,构造方法在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学习过程中的重难点 基础好学,但是难融会贯通
对本文中有自己的想法或者建议可以私信博主,大家可以一起进步💖💖