怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏9)

简介: 怒肝俩月,新鲜出炉史上最有趣的Java小白手册,第一版,每个 Java 初学者都应该收藏

八、Java 构造方法


假设现在有一个 Writer 类,它有两个字段,姓名和年纪:


public class Writer {
    private String name;
    private int age;
    @Override
    public String toString() {
        return "Writer{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


重写了 toString() 方法,用于打印 Writer 类的详情。由于没有构造方法,意味着当我们创建 Writer 对象时,它的字段值并没有初始化:


Writer writer = new Writer();

System.out.println(writer.toString());



输出结果如下所示:


Writer{name='null', age=0}


name 是字符串类型,所以默认值为 null,age 为 int 类型,所以默认值为 0。


让我们为 Writer 类主动加一个无参的构造方法:


public Writer() {

   this.name = "";

   this.age = 0;

}



构造方法也是一个方法,只不过它没有返回值,默认返回创建对象的类型。需要注意的是,当前构造方法没有参数,它被称为无参构造方法。如果我们没有主动创建无参构造方法的话,编译器会隐式地自动添加一个无参的构造方法。这就是为什么,一开始虽然没有构造方法,却可以使用 new Writer() 创建对象的原因,只不过,所有的字段都被初始化成了默认值。


接下来,让我们添加一个有参的构造方法:


public Writer(String name, int age) {

   this.name = name;

   this.age = age;

}


现在,我们创建 Writer 对象的时候就可以通过对字段值初始化值了。


Writer writer1 = new Writer("沉默王二",18);

System.out.println(writer1.toString());



来看一下打印结果:


Writer{name='沉默王二', age=18}


可以根据字段的数量添加不同参数数量的构造方法,比如说,我们可以单独为 name 字段添加一个构造方法:


public Writer(String name) {

   this.name = name;

}


为了能够兼顾 age 字段,我们可以通过 this 关键字调用其他的构造方法:


public Writer(String name) {

   this(name,18);

}


把作者的年龄都默认初始化为 18。如果需要使用父类的构造方法,还可以使用 super 关键字,手册后面有详细的介绍。


九、Java 抽象类


当我们要完成的任务是确定的,但具体的方式需要随后开个会投票的话,Java 的抽象类就派上用场了。这句话怎么理解呢?搬个小板凳坐好,听我来给你讲讲。




01、抽象类的 5 个关键点


1)定义抽象类的时候需要用到关键字 abstract,放在 class 关键字前。


public abstract class AbstractPlayer {

}


关于抽象类的命名,阿里出品的 Java 开发手册上有强调,“抽象类命名要使用 Abstract 或 Base 开头”,记住了哦。


2)抽象类不能被实例化,但可以有子类。


尝试通过 new 关键字实例化的话,编译器会报错,提示“类是抽象的,不能实例化”。


image.png


通过 extends 关键字可以继承抽象类,继承后,BasketballPlayer 类就是 AbstractPlayer 的子类。


public class BasketballPlayer extends AbstractPlayer {

}



3)如果一个类定义了一个或多个抽象方法,那么这个类必须是抽象类。


当在一个普通类(没有使用 abstract 关键字修饰)中定义了抽象方法,编译器就会有两处错误提示。


第一处在类级别上,提醒你“这个类必须通过 abstract 关键字定义”,or 的那个信息没必要,见下图。


image.png


第二处在方法级别上,提醒你“抽象方法所在的类不是抽象的”,见下图。


image.png


4)抽象类可以同时声明抽象方法和具体方法,也可以什么方法都没有,但没必要。就像下面这样:


public abstract class AbstractPlayer {

   abstract void play();

 

   public void sleep() {

       System.out.println("运动员也要休息而不是挑战极限");

   }

}


5)抽象类派生的子类必须实现父类中定义的抽象方法。比如说,抽象类中定义了 play() 方法,子类中就必须实现。


public class BasketballPlayer extends AbstractPlayer {

   @Override

   void play() {

       System.out.println("我是张伯伦,篮球场上得过 100 分");

   }

}


如果没有实现的话,编译器会提醒你“子类必须实现抽象方法”,见下图。


image.png


02、什么时候用抽象类


与抽象类息息相关的还有一个概念,就是接口,我们留到下一篇文章中详细说,因为要说的知识点还是蛮多的。你现在只需要有这样一个概念就好,接口是对行为的抽象,抽象类是对整个类(包含成员变量和行为)进行抽象。


(是不是有点明白又有点不明白,别着急,翘首以盼地等下一篇文章出炉吧)


除了接口之外,还有一个概念就是具体的类,就是不通过 abstract 修饰的普通类,见下面这段代码中的定义。


public class BasketballPlayer {

  public void play() {

       System.out.println("我是詹姆斯,现役第一人");

   }

}



有接口,有具体类,那什么时候该使用抽象类呢?


1)我们希望一些通用的功能被多个子类复用。比如说,AbstractPlayer 抽象类中有一个普通的方法 sleep(),表明所有运动员都需要休息,那么这个方法就可以被子类复用。


public abstract class AbstractPlayer {

   public void sleep() {

       System.out.println("运动员也要休息而不是挑战极限");

   }

}



虽然 AbstractPlayer 类可以不是抽象类——把 abstract 修饰符去掉也能满足这种场景。但 AbstractPlayer 类可能还会有一个或者多个抽象方法。


BasketballPlayer 继承了 AbstractPlayer 类,也就拥有了 sleep() 方法。


public class BasketballPlayer extends AbstractPlayer {

}

1

2

BasketballPlayer 对象可以直接调用 sleep() 方法:


BasketballPlayer basketballPlayer = new BasketballPlayer();

basketballPlayer.sleep();

1

2

FootballPlayer 继承了 AbstractPlayer 类,也就拥有了 sleep() 方法。


public class FootballPlayer extends AbstractPlayer {

}

1

2

FootballPlayer 对象也可以直接调用 sleep() 方法:


FootballPlayer footballPlayer = new FootballPlayer();

footballPlayer.sleep();

1

2

2)我们需要在抽象类中定义好 API,然后在子类中扩展实现。比如说,AbstractPlayer 抽象类中有一个抽象方法 play(),定义所有运动员都可以从事某项运动,但需要对应子类去扩展实现。


public abstract class AbstractPlayer {

   abstract void play();

}



BasketballPlayer 继承了 AbstractPlayer 类,扩展实现了自己的 play() 方法。


public class BasketballPlayer extends AbstractPlayer {

   @Override

   void play() {

       System.out.println("我是张伯伦,我篮球场上得过 100 分,");

   }

}



FootballPlayer 继承了 AbstractPlayer 类,扩展实现了自己的 play() 方法。


public class FootballPlayer extends AbstractPlayer {

   @Override

   void play() {

       System.out.println("我是C罗,我能接住任意高度的头球");

   }

}


3)如果父类与子类之间的关系符合 is-a 的层次关系,就可以使用抽象类,比如说篮球运动员是运动员,足球运动员是运动员。


相关文章
|
7月前
|
设计模式 安全 Java
面向对象编程的精髓:Java设计模式 - 原型模式(Prototype)完全参考手册
【4月更文挑战第7天】原型模式是OOP中的创建型设计模式,用于通过复制现有实例创建新实例,尤其适用于创建成本高或依赖其他对象的情况。它包括Prototype接口、ConcretePrototype实现和Client客户端角色。优点是性能优化、避免子类化和动态增加产品族。实现包括定义原型接口、实现具体原型和客户端调用克隆方法。最佳实践涉及确保克隆正确性、选择深拷贝或浅拷贝及考虑线程安全。但需注意克隆方法管理、性能开销和循环引用等问题。在Java中,实现Cloneable接口和覆盖clone方法可实现原型模式。
88 4
|
7月前
|
网络协议 Java Maven
Java自救手册
Java自救手册
97 2
|
7月前
|
算法 Java
「译文」Java 垃圾收集参考手册(四):Serial GC
「译文」Java 垃圾收集参考手册(四):Serial GC
|
7月前
|
算法 Java PHP
「译文」Java 垃圾收集参考手册(一):垃圾收集简介
「译文」Java 垃圾收集参考手册(一):垃圾收集简介
|
6月前
|
Java 开发者
【技术成长日记】Java 线程的自我修养:从新手到大师的生命周期修炼手册!
【6月更文挑战第19天】Java线程之旅,从新手到大师的进阶之路:始于创建线程的懵懂,理解就绪与运行状态的成长,克服同步难题的进阶,至洞悉生命周期的精通。通过实例,展示线程的创建、运行与同步,展现技能的不断提升与升华。
41 2
|
7月前
|
Java 开发者
效率工具RunFlow完全手册之Java开发者篇
这篇手册面向Java开发者,介绍了如何使用QLExpress执行Java代码和进行方法验证。示例包括数学计算、读取系统环境变量及格式化输出。QLExpress提供运行时执行Java代码的功能,并支持QLExpress语法的脚本文件。
49 3
效率工具RunFlow完全手册之Java开发者篇
|
6月前
|
Java 数据安全/隐私保护
Java基础手册二(类和对象 对象创建和使用 面向对象封装性 构造方法与参数传递 this关键字 static关键字 继承 多态 方法覆盖 final关键字 访问控制权限修饰符)
Java基础手册二(类和对象 对象创建和使用 面向对象封装性 构造方法与参数传递 this关键字 static关键字 继承 多态 方法覆盖 final关键字 访问控制权限修饰符)
38 0
|
6月前
|
存储 Java
Java基础手册(标识符 关键字 字面值 变量 数据类型 字符编码 运算符 控制语句 方法及方法重载和递归 面向对象与面向过程)
Java基础手册(标识符 关键字 字面值 变量 数据类型 字符编码 运算符 控制语句 方法及方法重载和递归 面向对象与面向过程)
46 0
|
6月前
|
存储 自然语言处理 Java
Java IO流完全手册:字节流和字符流的常见应用场景分析!
【6月更文挑战第26天】Java IO流涵盖字节流和字符流,字节流用于二进制文件读写及网络通信,如图片和音频处理;字符流适用于文本文件操作,支持多语言编码,确保文本正确性。在处理数据时,根据内容类型选择合适的流至关重要。
93 0
|
7月前
|
算法 安全 Java
「译文」Java 垃圾收集参考手册(三):GC 算法基础篇
「译文」Java 垃圾收集参考手册(三):GC 算法基础篇