一个前端的java后端之旅(二) 面向对象绕不过?那就干掉它

java跟咱们用过的(c++/JavaScript)有很大的不一样,尽管都支持面向对象,但你仍能够在c++,JavaScript中不使用类和对象,而java就不一样了,就连 main入口方法都是类的方法,它是一个纯粹的面向对象的语言,因此面向对象在java学习中是没法绕过的,那既然绕不过,就先干掉他,在适应了它的规则后再深刻了解它。

java中的类Class

说到类,其实如今作前端的同窗也会很熟悉,若是你使用过TypeScript,那应该对类的概念就更清楚不过了,这里不对类和对象的概念再作无心义的描述,让咱们直接开始coding,复习一下类和对象的基本使用方法。html

因为没有很好的想法,那就创造一个接近实际的需求,咱们来用收货地址来熟悉类和对象基本使用前端

创造需求---收货地址类

根据上一篇文章中建立的java项目,咱们在src下建立一个包learning.oop,而后在里面建立一个Class文件DiliveryAddress,注意类名最好大写开头,驼峰结构命名,idea会自动帮咱们建立与Class文件名相同的类,文件名与类名保持一致。java

package learning.oop;

public class DeliveryAddress {
}

复制代码

好了,如今该想一想里面应该设计什么字段了。我就直接提供了:c++

  • id: 收货地址自己须要一个id,来表明具体哪个收货地址
  • userId: 用户Id
  • receiverName: 收件人名字
  • receiverPhone: 收件人电话
  • receiverProvince: 省
  • receiverCity: 市
  • receiverDistrict: 区
  • receiverAddress: 详细地址
  • receiverZip: 邮政编码
  • isDefault: 是不是默认地址
  • createTime: 建立时间
  • updateTime: 修改时间

你是否露出了惊讶的表情,长大了嘴巴,心想,不就是个例子嘛,要不要这么认真,举个猫狗动物的例子它不香吗?要搞这么多字段的东西。(请合拢你张大的嘴巴),咱们不是儿戏,咱们要经过接近真实的东西快速上手java。git

初始化属性

在类中,咱们能够包含仅表示数据的属性以及操做数据的方法,对于表示数据的属性,尽可能使用private关键字,不要把属性直接暴露出去,若是你有修改和获取数据的需求,则应该使用修改器setter获取器getter的方式来进行。github

接下来先根据上述需求,来建立类中的属性,设计过程当中想一想它应该是哪一种数据类型spring

import java.util.Date;

public class DeliveryAddress {
    private Integer id;
    private Integer userId;
    private String receiverName;
    private String receiverPhone;
    private String receiverProvince;
    private String receiverCity;
    private String receiverDistrict;
    private String receiverAddress;
    private String receiverZip;
    private Boolean isDefault;
    private Date createTime;
    private Date updateTime;
}

复制代码

以上,咱们使用了四种数据类型,分别是Integer,String,Boolean,Date类型,Date类型引入了一个java.util.Date的包,说明它是那个包中所定义的一种类,并非基础的数据类型。其余类型也很容易认,认真观察这些类型都是大写开头,因此他们本质也是一种类,这种形式叫作包装类。说到包装了类,有些包装类还对应有基础类型(好比int Interger均可以定义整数)。windows

好比int类型为整型(定义整数用的),他是定义整数变量的基本类型,对应的Interger是它的包装类,定义的是一种对象,纯粹定义的数字能够理解为就是一种数字,可是数字要想使用一些例如toString()的方法,就要借助对象,因此就会进行Integer类型的包装,若是用int定义的变量,在使用方法的时候java会自动帮咱们进行包装,在java中这种转变叫作自动装箱,说到这里,若是用int变量接收一个Interger定义的变量,java就会自动拆除变量的方法,让他变成一个纯数字,在java中这种转变就是自动装箱(手动拆箱装箱本身理解咯)。后端

若是想详细了解java中的变量,能够自行查阅资料,但更建议的是边学边记。这里就再也不作拓展(好吧,是由于我会的也很少)。api

添加无参构造函数

既然是前端者的后端之旅,那么你们对JavaScript的构造函数必定不会陌生,它是经过constructor进行定义的,java不一样于此,是经过与类名相同的方法来进行构造函数的定义的,若是咱们不定义构造函数,系统会默认给咱们添加一个没有参数且函数体为空的构造函数,固然咱们也能够自定义带参数的构造函数。

package learning.oop;

import java.util.Date;

public class DeliveryAddress {
    private Integer id;
    private Integer userId;
    private String receiverName;
    private String receiverPhone;
    private String receiverProvince;
    private String receiverCity;
    private String receiverDistrict;
    private String receiverAddress;
    private String receiverZip;
    private Boolean isDefault;
    private Date createTime;
    private Date updateTime;

    // 无参构造函数
    public DeliveryAddress(){
        this.createTime = new Date();
    }

    public Date getCreateTime() {
        return createTime;
    }
}

复制代码

这里添加了一个无惨构造函数,里面对createTime作了初始化操做,由于这个属性应该是不能够改变的,并且是在建立对象时候就应该拥有值的。下面还定义了一个public方法,类型为Date,返回createTime,注意构造函数和getCreateTime中的写法,一个带this,一个不带,两种形式均可以。

tips 注意构造函数也要加public修饰符

如今咱们去src下的Main方法里面实例化几个对象试试看。

import learning.oop.DeliveryAddress;

public class Main {

    public static void main(String[] args) {
        DeliveryAddress address1 = new DeliveryAddress();
        System.out.println(address1);
        System.out.println(address1.toString());
        System.out.println(address1.getClass());
        System.out.println(address1.getCreateTime());
        System.out.println(address1.getCreateTime().getTime());
    }
}

复制代码

我打印了其中的toString()getClass()方法的运行结果,toString()与JavaScript中的方法相似,在咱们呢直接打印这个变量的时候,就会输出它的toString()结果,所以咱们能够看到直接打印与打印toString()结果相同,而后好奇打印了getClass(),获得了这个对象对应的类所在具体地址(包名)learning.oop.DeliveryAddress

经过getCreateTime()方法,咱们也成功得到了对象建立的时间,,这个createTime实际上是个Date类型的对象,咱们能够经过它上面的方法获取本身想要的时间形式,例如想要时间戳,能够经过getTime()方法获取。

你们能够经过文档看看Date类定义了哪些方法。其实彻底不用所有看,本身须要用哪些就看哪些。

添加带参构造函数/方法

what?构造函数能够添加多个?使得没错,在C++/Java中都是能够的,这种特色叫作重载,TypeScript中也支持重载,但因为它自己仍是由JavaScript写的,因此它的重载并非真正的重载。

public DeliveryAddress( Integer userId, String receiverName, String receiverPhone, String receiverProvince, String receiverCity, String receiverDistrict, String receiverAddress, String receiverZip ) {
        this.userId = userId;
        this.receiverName = receiverName;
        this.receiverPhone = receiverPhone;
        this.receiverProvince = receiverProvince;
        this.receiverCity = receiverCity;
        this.receiverDistrict = receiverDistrict;
        this.receiverAddress = receiverAddress;
        this.receiverZip = receiverZip;
        this.isDefault = false;
    }
    
    public DeliveryAddress( Integer userId, String receiverName, String receiverPhone, String receiverProvince, String receiverCity, String receiverDistrict, String receiverAddress, String receiverZip, Boolean isDefault ) {
        this.userId = userId;
        this.receiverName = receiverName;
        this.receiverPhone = receiverPhone;
        this.receiverProvince = receiverProvince;
        this.receiverCity = receiverCity;
        this.receiverDistrict = receiverDistrict;
        this.receiverAddress = receiverAddress;
        this.receiverZip = receiverZip;
        this.isDefault = isDefault;
    }
复制代码

此次我添加了两个带参的构造方法,他们的惟一不一样就在于isDefault属性的设置,由于java不支持设置默认参数,因此只能用重载的形式来进行设置。同时仔细观察这里的全部属性设置都会使用this.xxx = xxx,而没有直接使用xxx = xxx,后者实际上是错误的,这样java分辨不出来你到底要给谁设置值,因此若是属性名跟参数名相同,那就必须加this

咱们能够去Main方法中添加两个实例试试

import learning.oop.DeliveryAddress;

public class Main {

    public static void main(String[] args) {
        DeliveryAddress address1 = new DeliveryAddress();
        DeliveryAddress defaultAddress = new DeliveryAddress(
                1,
                "张三",
                "13122888888",
                "上海",
                "上海",
                "浦东新区",
                "XX小区XX楼XX户",
                "20000",
                true
                );
        DeliveryAddress address2 = new DeliveryAddress(
                1,
                "张三",
                "13122888888",
                "上海",
                "上海",
                "浦东新区",
                "XX小区XX楼XX户",
                "20000",
        );

        System.out.println(address1.getCreateTime());
        System.out.println(defaultAddress.getCreateTime());
        System.out.println(address2.getCreateTime());
    }
}

复制代码

enter description here

咱们打印了三个地址的建立时间,但一看,彷佛不对啊,为何后面两个是null,难道不该该是正常时间?

这实际上是正常现象,三个构造方法是独立的,后两个调用的时候并无去设置时间,因此不会有。

好吧,这真的是故意的,但又一点不是故意的我忘记重写toString()方法了,那么下面咱们来解决这些问题,同时两个带参的构造函数彷佛很大一部分代码是重复的,咱们也顺手将代码重复的问题进行解决。

改良

...
    private static int counter = 0;
    
    public DeliveryAddress(){
        this.id = DeliveryAddress.counter;
        DeliveryAddress.counter++;
        this.createTime = new Date();
        this.updateTime = this.createTime;
    }

    public DeliveryAddress( Integer userId, String receiverName, String receiverPhone, String receiverProvince, String receiverCity, String receiverDistrict, String receiverAddress, String receiverZip ) {
        this();
        this.userId = userId;
        this.receiverName = receiverName;
        this.receiverPhone = receiverPhone;
        this.receiverProvince = receiverProvince;
        this.receiverCity = receiverCity;
        this.receiverDistrict = receiverDistrict;
        this.receiverAddress = receiverAddress;
        this.receiverZip = receiverZip;
        this.isDefault = false;
    }

    public DeliveryAddress( Integer userId, String receiverName, String receiverPhone, String receiverProvince, String receiverCity, String receiverDistrict, String receiverAddress, String receiverZip, Boolean isDefault ) {
        this(
            userId,
            receiverName,
            receiverPhone,
            receiverProvince,
            receiverCity,
            receiverDistrict,
            receiverAddress,
            receiverZip
        );
        this.isDefault = isDefault;
    }

    @Override
    public String toString() {
        String res = "id: " + this.id + "\n" +
                "userId: " + this.userId + "\n" +
                "receiverName: " + this.receiverName + "\n" +
                "receiverPhone: " + this.receiverPhone + "\n" +
                "receiverCity: " + this.receiverCity + "\n" +
                "receiverDistrict: " + this.receiverDistrict + "\n" +
                "receiverAddress: " + this.receiverAddress + "\n" +
                "receiverZip: " + this.receiverZip + "\n" +
                "isDefault: " + this.isDefault + "\n";
        return res;
    }
复制代码

这里咱们仍是三个构造方法,第一个无惨,后两个有参,第三个比第二个多个isDeafult,这些都没问题,前面提出的问题咱们又是怎么解决的呢?咱们经过给第二个构造函数添加this(),这表明若是要执行第二个构造方法,就会先执行第一个,给第三个构造方法添加了个this(xxxxx)里面传进了不少参数,这表明要使用第三个构造方法,就必须调用第二个,同时,也作到了代码的简化,第三个构造函数仅仅是比第二个多了个isDefault的参数而已,其余代码同样,那就直接调用第二个就行了。

最后实现了个publish String toString()方法,这个方法奇怪的地方是在它头上多了个@Override的东西,这个东西叫作注解,能够暂时理解为要重写方法,就必须用这个注解进行标识(实际上是由于我也不会注解,后面再研究好了)。

tips 这里注意一下又添加了一个属性,private static int counter = 0,这个属性与其余不一样的在于多了个static关键词的描述,而且直接初始化为0,添加了static的属性与JavaScript中的做用同样,它就变成了类的属性,而不是某个实例的属性,这里经过它模拟了收货地址id的自增。固然,也可使用static建立静态方法,它也是属于类的方法,既能够经过类名调用,也能够经过实例进行调用。

而后咱们能够回去Main方法中完善一下三个实例的输出

import learning.oop.DeliveryAddress;

public class Main {

    public static void main(String[] args) {
        DeliveryAddress address1 = new DeliveryAddress();
        DeliveryAddress defaultAddress = new DeliveryAddress(
                1,
                "张三",
                "13122888888",
                "上海",
                "上海",
                "浦东新区",
                "XX小区XX楼XX户",
                "20000",
                true
                );
        DeliveryAddress address2 = new DeliveryAddress(
                1,
                "张三",
                "13122888888",
                "上海",
                "上海",
                "浦东新区",
                "XX小区XX楼XX户",
                "20000"
        );

        System.out.println(address1.getCreateTime());
        System.out.println(defaultAddress.getCreateTime());
        System.out.println(address2.getCreateTime());
        System.out.println(address1);
        System.out.println(defaultAddress);
        System.out.println(address2);
    }
}

复制代码

此次就成功看到了三个建立时间的输出,而且看到直接打印对象,打印出了咱们toString()方法返回的值。也证实以前说的直接打印对象其实输出的就是toString()

属性默认值

在上面运行结果中咱们看到第一个地址的不少属性值都是null,这明显是不行的,若是这些属性没有被赋值,咱们须要给他们添加默认值,添加默认值有两种方式:

  1. 能够在设置属性的时候直接初始化话,就像上面所说的private static int counter = 0同样,只不过普通属性不加static
  2. 也能够像createTime同样放在无惨构造函数里进行初始化

这里我将其完善一下,使用第一种方式进行初始化。

private Integer id;
    private Integer userId = -1;
    private String receiverName = "";
    private String receiverPhone = "";
    private String receiverProvince = "";
    private String receiverCity = "";
    private String receiverDistrict = "";
    private String receiverAddress = "";
    private String receiverZip = "";
    private Boolean isDefault = false;
    private Date createTime;
    private Date updateTime;
    private static int counter = 0;
复制代码

回到Main函数从新运行代码结果为

这样就舒服多了,未填的值直接为设置的初始值。

工欲善其事必先利其器:idea的利用

以上全部的属性,构造方法,普通方法都是咱们本身写的,其实彻底没有必要让本身这么累,idea工具能够帮助咱们作这些事,右键点击idea写代码的区域,点击generate,或者按相应的快捷键,generate选项右边有快捷键标识,Mac的是⌘+N,windows自行查看(个人win懒得开机😂)

经过这些选项,能够自行生成getter setter 构造函数 toString()等方法,这里你们自行体验,我也去体验一把。

所谓的getter就是获取私有属性的方法,setter就是设置私有属性的方法,由于他们的书写只是体力活,因此你们能够用idea工具生成,若是有须要更改的再去更改。

小结

本篇文章简单介绍了java中类的使用,包括构造方法,构造方法的重载,调用其余构造方法,toString()方法,属性值设置等,而且里面穿插了包装类,拆箱装箱等小知识点。

固然,本文知识java面向对象的开始,由于java总体都是面向对象的,后续将继续分享接口、继承反射、bean、泛型、注解、等内容,尽可能让快速掌握可使用spring boot的程度,而后再补充java的其余知识。有一点要保证的是,这些内容加起来确定不会像字典同样厚,但关键内容仍然会保证不缺失。

相关文章
相关标签/搜索