java全端课3--java-面向对象(中)

一、类的成员之三:构造器(必须掌握)

1.1 构造器有什么用

构造器的作用是为实例对象的实例变量初始化。在new对象的时候用。

1.2 构造器长什么样?

  • 构造器的名称必须与所在的类名完全一致,包括大小写。
  • 构造器没有返回值类型。不写void,也不写int,String等类型。一旦你写了返回值类型,它就是普通方法。

1.3 构造器有什么特点或要求?

  • 所有类都有构造器
  • 如果一个类没有手动编写构造器,那么编译器会给你自动添加一个默认的无参构造。
  • 但是如果我们手动编写了构造器,那么编译器就不会再给你添加任何构造器了。
  • 构造器可以重载。构造器有时候也叫做构造方法,或构造函数。
  • 构造器的修饰符只能是public、protected、缺省、private,不能加其他修饰符(static,final,abstract等)

1.4 构造器如何快速的创建?

Alt + Insert

二、面向对象的基本特征之一:封装

2.1 为什么要封装?

生活中,快递为什么有包装盒/包装袋?

  • 私密性:保护隐私
  • 安全性:避免损坏
  • 方便运输
  • ......

Java中的类及其成员,也需要封装:

  • 广义的封装概念:边界感,

    • 例如:把一类事物的共同特征封装到一个类中,把一个完整的功能封装到一个方法中。
    • 组件之间的封装,例如:项目中用到第三方的支付功能,微信、支付宝、银行等,只能调用对方开放的接口,无法获取内部的实现细节。
  • 狭义的封装:对类或成员加权限修饰符,控制它们的可见性范围

    • 隐藏实现细节,便于使用
    • 安全
arduino 复制代码
package com.test.constructor;

public class Student {
    private String name;//属性私有化
    private int age;//属性私有化

    public Student() {//无参构造
    }

    public Student(String name, int age) {//有参构造
        this.name = name;
        this.age = age;
    }

    public void setAge(int age){
        if(age >= 18 && age<=35) {
            this.age = age;
        }else{
            System.out.println("年龄不合法!");
        }
    }

    public String getInfo(){
        return "姓名:" + name +",年龄:" + age;
    }
}
csharp 复制代码
package com.test.constructor;

public class TestStudent {
    public static void main(String[] args) {
        //调用Student类的无参构造创建Student对象
        Student s1 = new Student();
        System.out.println(s1.getInfo());

        //调用Student类的有参构造创建Student对象
        Student s2 = new Student("张三",23);
        System.out.println(s2.getInfo());

        //s2.age = -18;//不安全
        s2.setAge(-18);//安全
        System.out.println(s2.getInfo());

        s2.setAge(30);//安全
        System.out.println(s2.getInfo());
    }
}

2.2 四种权限修饰符

对类或成员加权限修饰符,控制它们的可见性范围。

private(私有的) 缺省(不写) protected(受保护) public(公共的)
本类
本包其他类 ×
其他包的子类(继承) × ×
其他包的非子类 × × ×

2.3 get/set方法

  • set方法:修改某个或某些属性的值

    • 如果是静态变量的set方法,那么出现局部变量与静态变量重名时,通过"类名.静态变量"进行区分
    • 如果是实例变量的set方法,那么出现局部变量与实例变量重名时,通过"this.实例变量"进行区分
  • get方法:获取某个或某些属性的值

    • 如果某个属性的类型是boolean,那么它的get方法,一般以is开头。

问:有构造器,和get/set会不会冲突?

答:构造器在new对象,赋初始值。

  • set方法在new对象,用于修改属性的值。
  • get方法在new对象,用于获取某个属性的值。
  • 用快捷键生成构造器、get/set方法 Alt + Insert

三、标准Javabean

bean:豆。

Java:产咖啡的印尼的爪哇岛。

Javabean:咖啡豆,Java类。

什么是标准Javabean?

  • 属性私有化
  • 提供无参构造器。有参构造可选。因为后期Java对象的创建通常都是交给Spring等框架,而这些框架默认都是用无参构造创建对象。
  • 提供合适的get/set方法
  • 重写 equals和hashCode,toString方法
ini 复制代码
package com.test.bean;

import java.util.Objects;

public class Employee {
    private int id;
    private String name;
    private int age;
    private char gender;
    private double salary;
    private String tel;
    private String address;

    public Employee() {
        System.out.println("一个新员工入职");
    }

    public Employee(int id, String name, int age) {
        this();//调用本类的其他构造器,调用无参构造
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public Employee(int id, String name, int age, char gender, double salary, String tel, String address) {
//        this.id = id;
//        this.name = name;
//        this.age = age;
        this(id,name,age);//调用本类的其他构造器,调用有参构造
        this.gender = gender;
        this.salary = salary;
        this.tel = tel;
        this.address = address;
    }
    
    //... 省略 get set...
    
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + ''' +
                ", age=" + age +
                ", gender=" + gender +
                ", salary=" + salary +
                ", tel='" + tel + ''' +
                ", address='" + address + ''' +
                '}';
    }
    
    //作用要明确,用于比较两个Employee对象的属性值是不是相同
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Employee employee = (Employee) o;
        return id == employee.id && age == employee.age && gender == employee.gender && Double.compare(salary, employee.salary) == 0 && Objects.equals(name, employee.name) && Objects.equals(tel, employee.tel) && Objects.equals(address, employee.address);
    }

    @Override
    public int hashCode() {
        int result = id;
        result = 31 * result + Objects.hashCode(name);
        result = 31 * result + age;
        result = 31 * result + gender;
        result = 31 * result + Double.hashCode(salary);
        result = 31 * result + Objects.hashCode(tel);
        result = 31 * result + Objects.hashCode(address);
        return result;
    }
}
csharp 复制代码
package com.test.bean;

public class TestEmployee {
    public static void main(String[] args) {
        Employee e1 = new Employee();
        Employee e2 = new Employee(2,"熊二",25);
        Employee e3 = new Employee(3,"张三",23,'男',15000,"10086","北京");


        //toString()作用等价于原来的getInfo()
        //但是它比getInfo()方便,打印对象时,不用手动调用,它会自动调用
/*        System.out.println(e1.toString());
        System.out.println(e2.toString());
        System.out.println(e3.toString());*/
        System.out.println(e1);//如果没有写toString方法,那么打印的是地址值
        System.out.println(e2);
        System.out.println(e3);

        Employee e4 = new Employee(2,"熊二",25);
        System.out.println(e2 == e4);//false
        //e2和e4是地址值,这里比较的是两个对象的地址值
        System.out.println(e2.equals(e4));//比较两个对象是否相等  false
        //默认情况下,equals等价于 ==
        //如果不想让equals方法等价于==,就必须重写
        System.out.println(e1.equals(e2));//false
    }
}

四、对象数组

1、什么是对象数组

把对象存到数组中,这样的数组就称为对象数组。其实,它和普通的数组是一样的,只是因为对象数组使用起来相对复杂一点。

2、如何声明和使用

ini 复制代码
元素的类型[] 数组名; //此时元素的类型是类类型,例如:Student,Rectangle,String等

静态初始化:

ini 复制代码
元素的类型[] 数组名 = {对象1,对象2,对象3};

动态初始化:

ini 复制代码
元素的类型[] 数组名 = new 元素的类型[长度];

数组的遍历:

css 复制代码
for(int i=0; i<数组名.length; i++){
    数组名[i]是元素,此时元素是对象
}

五、面向对象的基本特征之二:继承

5.1 什么是继承?

生活中:

  • 基因
  • 财产
  • 才艺

继承:延续性、扩展性

继承上一代的优良传统,并发扬光大。青出于蓝而胜于蓝。

Java中为什么要有继承的设计呢?

  • 代码的复用性:子类可以复用父类的代码
  • 代码的扩展性:子类可以重写父类的方法或扩展父类没有的成员
  • 表示事物之间的is-a关系。例如:Student类继承Person类,Student is a Person.学生类是人类的一个分支。

5.2 如何继承?(重要)

arduino 复制代码
【修饰符】 class 父类{
    
}
javascript 复制代码
【修饰符】 class 子类 extends 父类{//子类是从父类中延伸出来的新的分类,更具体的分类
    
}

父类:SuperClass,称为父类或超类 ,基类。

子类:SubClass,称为子类或派生类。

5.3 继承的特点或要求(重要)

1、Java中只允许单继承。比喻:每一个子类只有1个亲生父亲。

2、但是Java中支持多层继承。比喻:代代相传。你的父亲仍然有父亲。爷爷的特征到孙子类仍然是保留的。

3、Java中一个父类可以同时有多个子类。比喻:支持多胎。支持家族兴旺。

4、父类的所有成员变量、成员方法都会继承到子类中。因为成员变量代表事物的数据特征,成员方法代表事物的行为/功能特征,而子类是父类这个事物分类下的一个分支,所以这些特征都会延续下来。但是,父类中私有的成员变量、成员方法,在子类中不能直接使用,可以间接使用。

5、父类的构造器不会继承到子类中。但是,子类的构造器中又必须调用父类的构造器。因为子类继承了父类声明的所有成员变量,那么创建子类对象时就需要为这些成员变量初始化,而为这些成员变量初始化的代码已经在父类的构造器中写过了,可以不用重复编写这些代码了,直接调用它们即可。

  • super():表示调用父类的无参构造。这句代码可以省略。
  • super(实参列表):明确表示调用父类的有参构造。这句代码不能省略,一旦省略,就表示调用无参构造了。
  • 它们必须在子类构造器的首行。

案例一:子类直接用父类的成员

arduino 复制代码
package com.test.inherited;

public class Person {
    public String name;
    public int age;

    public String getPersonInfo(){
        return "姓名:" + name + ",年龄:" + age;
    }
}
java 复制代码
package com.test.inherited;

public class Student extends Person{

    public int score;//成绩

    public String getStudentInfo(){
        return "姓名:" + name + ",年龄:" + age +",成绩:" + score;
        //直接使用父类非private的属性
    }
}
java 复制代码
package com.test.inherited;

public class Student extends Person{

    public int score;//成绩

    public String getStudentInfo(){
        return getPersonInfo() +",成绩:" + score;
        //间接使用父类非private的方法
    }
}

案例二:子类间接使用父类的成员

arduino 复制代码
package com.test.inherited;

public class Person {
    private String name;
    private int age;

    public String getPersonInfo(){
        return "姓名:" + name + ",年龄:" + age;
    }
}
ruby 复制代码
package com.test.inherited;

public class Student extends Person{

    public int score;//成绩

    public String getStudentInfo(){
        //return "姓名:" + name + ",年龄:" + age +",成绩:" + score;
        //报错,因为name和age此时在父类Person中是private
        return getPersonInfo() +",成绩:" + score;
        //直接调用父类的方法,从而间接使用父类的私有属性
    }
}

5.4 方法的重写(重要)

1、什么是方法的重写?

方法的重写(Override)是指子类覆盖/重写/覆写父类的某个方法。因为子类会继承父类的所有方法,但是某些方法的方法体功能实现不适用于子类,那么子类就可以重新实现它。

2、如何重写方法?

  • 修饰符:

    • 权限修饰符:public、protected、缺省、private,其中private的方法是不能被重写的。并且子类重写时,方法的权限修饰符必须大于等于父类被重写方法的权限修饰符。

      • 父类被重写方法是public,子类这个方法只能是public。
      • 父类被重写方法是protected,子类这个方法可以是public,protected
      • 父类被重写方法是缺省,子类这个方法可以是public,protected,缺省
    • 其他修饰符:static,静态方法不能被重写。

  • 返回值类型:

    • void和基本数据类型:子类必须保持一致

    • 引用数据类型:子类重写时,方法的返回值类型可以是小于等于

      • 例如父类被重写方法的返回值类型是Person,那么子类重写方法的返回值类型可以是Person,也可以是Student
  • 方法名:必须完全相同

  • 形参列表:必须完全相同,这里相同是指类型、个数、顺序。不包括形参名。

  • 方法体:子类重写就是为了重新实现方法体,根据功能的需求来。

3、如何调用父类被重写的方法?

super.父类被重写方法

4、方法重载与重写的区别

方法的重载 方法的重写
位置 同一个类 或 父子类 父子类
英文单词 Overload Override
修饰符 不看 不能重写private和static的方法。权限修饰符必须满足 >=
返回值类型 不看 void和基本数据类型:必须相同 引用数据类型:满足<=
方法名 必须相同 必须相同
形参列表 必须不同 必须相同

5.5 根父类(了解)

Object是java.lang包下的一个类,它是所有Java类的根,老祖宗。我们称为根父类。

Object类的方法,所有子类都会继承,即所有子类都有这些方法。但是,对于toString,equals和hashCode方法来说,子类通常都会重写。

java 复制代码
//重写equals,hashCode,toString
//快捷键Alt + Insert 或 Ctrl + O
    @Override  //这个是注解。它是用于标记以下方法是重写的意思,它会告诉编译器,让编译器对该方法做严格的格式检查,看它是否满足重写的要求
               //但是,如果你本身没有违反重写的要求,@Override可以去掉。
                //加或不加@Override它,不影响重写的本质。只是格式检查是不是彻底的问题。
                //建议重写方法上保留它。

5.6 特殊关键字

5.6.1 native(了解)

native修饰的方法,称为本地方法,或内置的方法,它是内置在JVM相关的底层代码中,通过C/C++语言实现的,不是用Java语言实现。在Java层面看不到它的方法体。

本地方法的执行会自动调用本地方法库。本地方法的执行会在本地方法栈中开辟独立的内存空间。Java虚拟机中的栈空间分为两块:虚拟机栈(服务于Java的方法)和本地方法栈(服务于C/C++的函数)。

提示:native的方法,虽然看不到它的源码,但是(1)在Java中可以正常调用。(2)如果子类继承了本地方法,只要它不是private,不是static,不是final,子类可以对它用Java代码进行重写。即本地方法在使用层面,把它当成普通的方法即可。

5.6.2 final(重要)

final:最终。

在Java中,它是修饰符,可以用于修饰:类、方法、变量。

修饰符类 修饰符方法 修饰变量
作用 这个类不能被继承(比喻:太监类) 这样的方法不能被重写(可以被继承) 这样的变量称为常量,值不能被修改。
举例 String,Math,System等 Object类中的getClass()

对于final修饰变量来说:

(1)final可以修饰局部变量,可以修饰静态变量,可以修饰实例变量

(2)静态变量 + final 建议大写,其他的变量加final一般不大写

(3)静态变量和实例变量 + final之后,它们都没有set方法,可以有get方法

(4)实例变量 + final,可以在变量后面直接写 = 值,也可以在构造器中进行初始化。

修改单词大小写的快捷键:Ctrl+Shift+U

六、抽象类(重要)

6.1 什么是抽象类?

语法层面来说:有abstract修饰的类,就是抽象类。

csharp 复制代码
【权限修饰符】 abstract class 类名{
    
}

6.2 为什么要有抽象类?

父类是代表很多子类共同的特征,把很多子类共同的属性、方法抽取到父类中,从而避免在不同的子类中重复声明。

随着父类共同方法的抽取越来越抽象,就会出现抽象方法,即在父类中写不了具体方法体的方法,这样方法必须用abstract修饰,表示没有方法体的抽象方法,Java中规定,包含抽象方法的类必须是抽象类,这样的类是不能直接new对象的。

不能对抽象类直接new,如果可以 new 对象,就可以直接通过对象调用抽象方法,但是抽象方法没有方法体可执行。

问:抽象类有构造器吗?

答:一定有。此时构造器是给子类用的。

6.3 抽象类怎么用

抽象类的存在的意义:

  • 它代表所有子类共同的特征

  • 抽象类就是用来被继承的,子类继承抽象类,必须重写抽象类的所有抽象方法,否则子类也是是抽象类

  • 重新父类抽象方法快捷键

    • Ctrl + O 可以重写父类抽象的和非抽象的方法
    • Ctrl + I 重写父类的抽象方法

七、接口(重要)

7.1 什么是接口?

生活中:USB接口,电源插座的接口等,代表连接口。其实还代表了"标准"规范的意思。

Java中的接口也是代表(1)连接口(2)行为规范,方法的规范。

例如:后期做项目时,会调用银行接口,支付宝接口,微信支付接口来完成支付功能。

例如:后端服务器与前端的界面之间要约定双方传数据的规范,标准,这是也是接口的一部分。

从语法层面,说接口是用interface关键字声明的类型。

7.2 如何声明接口

csharp 复制代码
【修饰符】 interface 接口名{
    
}

7.3 接口的成员

回忆类的成员:

scss 复制代码
(1)成员变量:静态变量和实例变量
(2)成员方法:静态方法和非静态方法
(3)构造器:无参构造和有参构造
(4)代码块
(5)内部类

接口的成员有限制,明显与类不同:

JDK8之前:

  • 公共的静态的常量:public static final (3个关键字可以省略),因为接口是代表一种标准规范,那么具体的值必须是明确的,写死的。
  • 公共的抽象方法`:public abstract(2个关键字可以省略),因为接口是代表一种标准规范,只是说它有什么功能,但是功能的具体实现要有子类来实现。抽象方法是没有方法体,子类必须实现/重写它。

JDK8之后:

  • 公共的静态方法:public static(其中public可以省略,static不能省略),静态方法必须有方法体。静态方法的调用不需要接口的对象。

  • 公共的默认方法:public default(其中public可以省略,default不能省略)

    • 为了接口的升级,Java允许定义默认方法。默认方法是可以写方法体的,子类可以选择实现它,也可以选择不实现它。
    • 如果没有默认方法,按照之前的语法规则,接口中只能增加抽象方法,一旦父接口增加抽象方法,就会影响所有的实现类。

JDK9之后:

  • 私有的方法(了解):用于表示接口中内部使用的方法,通常是因为两个静态方法,或两个默认方法之间有共同代码,抽取出来的一个内部方法,这段代码只是功能的一部分,不是完整的功能,不希望外部调用,所以私有化。

7.4 接口的特点

  • 接口不能直接new对象
  • 接口允许多实现
  • 接口与接口支持多继承

问:抽象类与接口有什么区别?

相同的:它们都不能new对象,因为它们里面都可能有抽象方法。

不同点:

(1)成员不同(见上)

(2)类与类之间有"单继承"的限制,需要通过接口来解决这个问题,接口允许多实现

7.5 如何使用接口的各个成员

  • 静态的,用接口名.静态成员
  • 非静态的,创建子类对象,然后对象名.非静态成员

7.6 接口的作用/意义

因为接口允许多实现,可以解决抽象类单继承的限制问题

类与接口之间可以是简单的has-a或like-a的关系,而不是死板的is-a的关系。

  • 如果是is-a的关系,在逻辑层面要求比较严格。

    • Bird is a Animal。
    • Student is a Person。
    • Circle is a Shape。
  • 而has-a或like-a的关系的话,我们只要某个类想要拥有这个接口的方法,那么就可以继承它,不用管逻辑关系。

    • Bird has a fly方法。
    • Plane has a fly方法。
    • Kite has a fly方法。
    • Superman has a fly方法。
    • UFO like a flyable things。

例如:连接wifi,不一定是电脑,手机。可以是任意设备,只要遵循wifi的通信规则设计连网能力,家里任何设备都可以连接wifi。

相关推荐
半部论语几秒前
SpringMVC 中的DispatcherServlet生命周期是否受Spring IOC 容器管理
java·后端·spring
Asthenia041219 分钟前
JavaSE-常见排序:Arrays/Collections/List/StreamAPI
后端
Asthenia041226 分钟前
深入浅出分析JDK动态代理与CGLIB动态代理的区别
后端
追逐时光者1 小时前
C#/.NET/.NET Core技术前沿周刊 | 第 32 期(2025年3.24-3.31)
后端·.net
uhakadotcom1 小时前
轻松掌握XXL-JOB:分布式任务调度的利器
后端·面试·github
小杨4041 小时前
springboot框架项目实践应用十三(springcloud alibaba整合sentinel)
spring boot·后端·spring cloud
程序员一诺1 小时前
【Python使用】嘿马python数据分析教程第1篇:Excel的使用,一. Excel的基本使用,二. 会员分析【附代码文档】
后端·python
神奇侠20242 小时前
快速入手-基于Django-rest-framework的serializers序列化器(二)
后端·python·django
Asthenia04122 小时前
基于Segment-Mybatis的:分布式系统中主键自增拦截器的逻辑分析与实现
后端
Asthenia04122 小时前
Seata:为微服务项目的XID传播设计全局的RequestInterceptor-将XID传播与具体FeignClient行为解耦
后端