面向对象

1. 面向过程和面向对象解释

1.1 面向过程

1.1.1 简述

我们回想一下, 前面我们完成一个需求的步骤是怎样的?

首先是搞清楚我们要做什么.

然后在分析怎么做.

最后我们在通过代码一步一步去实现.

上述的每一个步骤, 我们都是参与者, 并且需要面对具体的每一个步骤和过程, 这就是面向过程最直接的体现.

1.1.2 举例
  • 生活中的例子: 洗衣服. 想完成洗衣服这个需求, 我们得完成下述的每一个步骤:

找个盆.

接点水.

到洗衣液.

把衣服放进去浸泡10分钟.

把衣服揉搓干净.

漂洗.

拧干水分.

晾晒.

 注意:

上述的这些步骤, 但凡有一个我们不会做, 就没有办法完成洗衣服这个需求.

: 每一步都需要我们亲自做, 这就是: 面向过程思想.

  • 代码举例: 把数组元素按照[11, 22, 33, 44, 55]的格式进行输出.

定义方法printArray(), 参数列表为: int[] arr, 返回值的类型为String.

定义字符串变量s, 用来记录数组元素拼接后的结果.

字符串变量s的初始化值设置为[.

对数组对象进行判断, 看其是否是合法对象.

 : 数组对象不为: null

通过for循环遍历数组, 获取到每一个元素.

判断当前获取到的元素是否是最后一个元素, 并和字符串变量s进行拼接.

  • 如果是最后一个元素, 则在后边在拼接上]
  • 如果不是最后一个元素, 则在后边在拼接上,

for循环执行结束后, 字符串变量s记录的就是拼接后的结果, 将其直接返回即可.

 注意: 上述的每一步代码都需要我们编写, 这样做比较繁琐.

1.1.3 总结

面向过程思想其实就是一种编程思想. 所谓的面向过程开发, 指的就是面向着具体的每一个步骤和过程, 把每一个步骤和过程完成, 然后由这些功能方法相互调用, 完成需求.

记忆: 面向过程的代表语言是: C语言.

 

1.2 面向对象

1.2.1 简述

当需求单一, 或者简单时, 我们一步一步去操作是没问题的, 并且效率也挺高. 可随着需求的更改, 功能的增多, 发现需要面对每一个步骤就很麻烦了, 于是我们就想着, 能不能把这些步骤和功能在进行封装, 封装时根据不同的功能, 进行不同的封装, 功能类似的用一个类封装在一起, 这样结构就清晰了很多. 用的时候, 找到对应的类就可以了. 这就是面向对象的思想.

1.2.2 举例
  • 生活中的例子: 洗衣服.

想完成洗衣服这个需求, 我们可以通过洗衣机 来实现.

对于我们来讲, 洗衣机就是我们的对象.

总结: 万物皆对象.

  • 代码举例: 把数组元素按照[11, 22, 33, 44, 55]的格式进行输出.

 上述的需求, 我们可以通过Arrays类的toString()方法来实现.

Int[] arr  = {11, 22, 33, 44, 55};

sop(Arrays.toString(arr));   -> “[11, 22, 33, 44, 55]”

1.2.3 思想特点

是一种符合我们思考习惯的思想.

可以将复杂的事情简单化.

让我们从执行者变成了指挥者.

1.2.4 总结

面向对象思想是一种编程思想, 它是基于面向过程的, 强调的是以对象为基础完成各种操作.

总结来讲, 万物皆对象.

2. 类和对象

2.1 概述

问题一: 你为什么学习编程语言?

我们学习编程语言, 其实就是为了把现实世界的事物通过代码模拟出来, 实现信息化.

例如:

  • 超市的计费系统.
  • 银行的核心系统.
  • 千亿级数据仓库.

 分析PB级数据,为企业提供高效、稳健的实时数据洞察。

 采用类似阿里巴巴大数据数仓设计的分层架构思想,使用主流的实时仓库技术FlinkDruidKafka

  • 企业级360°全方位用户画像.

 360°全方位还原用户画像,实现对个体和群体信息的标签化,实现精准推荐和营销.

  • 黑马电商推荐系统.

 项目利用Neo4j构建用户和商品的关系图示,基于词向量相似度推荐商品、CTR/CVR点击率预估模型、逻辑斯特回归算法进行CTR点击率预估。

问题二: 我们是如何表示现实世界的事物呢?

属性.

– 属性指的就是事物的描述信息(名词).

– 属性在Java中被称之为成员变量.

行为.

– 行为指的就是事物能够做什么.

– 行为在Java中被称之为成员方法.

例如: 学生

  • 属性: 姓名, 年龄, 性别....
  • 行为: 学习, 吃饭, 睡觉...

 

问题三: Java语言是如何表示现实世界的事物呢?

Java语言中, 是通过来体现事物的. Java语言最基本的单位是, 它是一个抽象的概念, 看不见, 摸不着.

对象: 对象就是该类事物的具体体现, 实现.

举例:

  学生          大象

 对象 张三, 23     北京动物园叫图图的大象

 

2.2 类的定义格式

2.2.1 简述

定义类其实就是定义类的成员(成员变量和成员方法)

  • 成员变量:

和以前定义变量是一样的, 只不过位置发生了改变, 写到类中, 方法外

而且成员变量还可以不用赋值, 因为它有默认值.

  • 成员方法:

和以前定义方法是一样的, 只不过把static关键字去掉.

这点先记忆即可, 后面我们再讲解static关键字的用法.

2.2.2 格式

public class 类名 {
    //成员变量
    
    //成员方法
}

2.2.3 示例

需求

定义一个学生类.

参考代码

public class Student{
    //成员变量, 就是属性.
    String name; //姓名
    int age; //年龄
    
    //成员方法, 就是行为.
    //学习的方法
    public void study() {
        System.out.println("键盘敲烂, 月薪过万!...");
    }
    
    //吃饭的方法
    public void eat() {
        System.out.println("学习饿了要吃饭!...");
    }
}

 

2.3 类的使用

2.3.1 简述

所谓类的使用, 就是使用类中定义的成员(成员变量和成员方法).

2.3.2 格式

创建该类的对象.

 类名 对象名 = new 类名();

通过对象名.的形式, 调用类中的指定成员即可.

 //成员变量
对象名.成员变量

//成员方法
对象名.成员方法(参数列表中各数据类型对应的值...)

2.3.3 示例

需求

使用学生类中的成员.

参考代码

public class StudentTest{
    public static void main(String[] args) {
        //1. 创建学生类的对象.
        Student s = new Student();
        //2. 访问成员变量.
        System.out.println(s.name);
        System.out.println(s.age);
        //3. 给成员变量赋值.
        s.name = "张三";
        s.age = 23;
        //4. 访问成员变量.
        System.out.println(s.name);
        System.out.println(s.age);
        //5. 访问成员方法.
        s.study();
        s.eat();
    }
}

 

3. 手机类的定义和使用

3.1 需求

定义手机类Phone.

 属性: 品牌(brand), 价格(price), 颜色(color)

 行为: 打电话(call), 发短信(sendMessage)

创建测试类PhoneTest, 在类中定义main方法, 并访问手机类(Phone类)中的成员.

3.2 参考代码

//手机类
public class Phone {
    //属性, 成员变量
    //品牌
    String brand;
    //价格
    int price;
    //颜色
    String color;

    //行为, 成员方法
    //打电话
    public void call(String name) {
        System.out.println("给" + name + "打电话!...");
    }

    //发短信
    public void sendMessage(String name) {
        System.out.println("给" + name + "发短信!...");
    }
}

//手机类的测试类
public class PhoneTest {
    public static void main(String[] args) {
        //1. 创建手机类的对象.
        Phone p = new Phone();
        //2. 设置成员变量值.
        p.brand = "华为";
        p.price = 6666;
        p.color = "黑色";
        //3. 打印成员变量.
        System.out.println(p.brand);
        System.out.println(p.price);
        System.out.println(p.color);
        //4. 调用成员方法.
        p.call("夯哥");
        p.sendMessage("夯哥");
    }
}

 

4. 对象的内存图

4.1 一个对象的内存图

代码

//手机类
public class Phone {
    //属性, 成员变量
    String brand; //品牌
    int price;     //价格
    String color; //颜色

    //行为, 成员方法
    //打电话
    public void call(String name) {
        System.out.println("给" + name + "打电话!...");
    }

    //发短信
    public void sendMessage(String name) {
        System.out.println("给" + name + "发短信!...");
    }
}

//手机类的测试类
public class PhoneTest {
    public static void main(String[] args) {
        //1. 创建手机类的对象.
        Phone p = new Phone();
        //2. 设置成员变量值.
        p.brand = "华为";
        p.price = 6666;
        p.color = "黑色";
        //3. 打印成员变量.
        System.out.println(p.brand + "--" + p.price + "--" + p.color);
        //4. 调用成员方法.
        p.call("夯哥");
        p.sendMessage("夯哥");
    }
}

内存图解 

.

4.2 两个对象的内存图

代码

//手机类
public class Phone {
    //属性, 成员变量
    String brand; //品牌
    int price;     //价格
     String color; //颜色

    //行为, 成员方法
    //打电话
    public void call(String name) {
        System.out.println("给" + name + "打电话!...");
    }

    //发短信
    public void sendMessage(String name) {
        System.out.println("给" + name + "发短信!...");
    }
}

//手机类的测试类
public class PhoneTest {
    public static void main(String[] args) {
        //1. 创建手机类的对象.
        Phone p = new Phone();
        //2. 设置成员变量值.
        p.brand = "华为";
        p.price = 6666;
        p.color = "黑色";
        //3. 打印成员变量.
        System.out.println(p.brand + "--" + p.price + "--" + p.color);
        //4. 调用成员方法.
        p.call("传智播客");
        p.sendMessage("传智播客");

        Phone p2 = new Phone();
        p2.brand = "小米";
        p2.price = 3333;
        p2.color = "白色";
        System.out.println(p2.brand + "--" + p2.price + "--" + p2.color);
        p2.call("黑马程序员");
        p2.sendMessage("黑马程序员");
    }
}

内存图解

.

4.3 两个对象的内存图

代码

//手机类
public class Phone {
    //属性, 成员变量
    String brand; //品牌
    int price;     //价格
     String color; //颜色

    //行为, 成员方法
    //打电话
    public void call(String name) {
        System.out.println("给" + name + "打电话!...");
    }

    //发短信
    public void sendMessage(String name) {
        System.out.println("给" + name + "发短信!...");
    }
}

//手机类的测试类
public class PhoneTest {
    public static void main(String[] args) {
        //1. 创建手机类的对象.
        Phone p = new Phone();
        //2. 设置成员变量值.
        p.brand = "华为";
        p.price = 6666;
        p.color = "黑色";
        //3. 打印成员变量.
        System.out.println(p.brand + "--" + p.price + "--" + p.color);
        //4. 调用成员方法.
        p.call("传智播客");
        p.sendMessage("传智播客");

        Phone p2 = p;
        p2.brand = "小米";
        p2.price = 3333;
        p2.color = "白色";
        System.out.println(p.brand + "--" + p.price + "--" + p.color);
        System.out.println(p2.brand + "--" + p2.price + "--" + p2.color);
        p2.call("黑马程序员");
        p2.sendMessage("黑马程序员");
    }
}

内存图解

.

5. 成员变量和局部变量的区别

5.1 简述

  • 成员变量: 指的是定义在类中, 方法外的变量.
  • 局部变量: 指的是定义在方法中, 或者方法声明上的变量.

它们的区别如下:

定义位置不同.

– 成员变量: 定义在类中, 方法外.

– 局部变量: 定义在方法中, 或者方法声明上.

在内存中的存储位置不同.

– 成员变量: 存储在堆内存.

– 局部变量: 存储在栈内存.

生命周期不同.

– 成员变量: 随着对象的创建而存在, 随着对象的消失而消失.

– 局部变量: 随着方法的调用而存在, 随着方法的调用完毕而消失.

初始化值不同.

– 成员变量: 有默认值.

– 局部变量: 没有默认值, 必须先定义, 再赋值, 然后才能使用.

5.2 代码演示

public class VariableDemo{
    int x;
    
    public void show() {
        int y = 10;
        System.out.println(x);
        System.out.println(y);
    }
}

 

6. 封装

上述的代码中, 我们可以任意的设置属性的值, 包括我们可以设置一些非法值, 例如: 把年龄设置成负数, 这样做程序就容易出问题, 针对于这种情况, 我们可以通过private关键字来优化它.

6.1 private关键字

6.1.1 简述

private是一个关键字, 也是访问权限修饰符的一种, 它可以用来修饰类的成员(成员变量和成员方法).

6.1.2 特点

private修饰的内容只能在本类中直接使用.

6.1.3 应用场景

在实际开发中, 成员变量基本上都是用private关键字来修饰的.

如果明确知道类中的某些内容不想被外界直接访问, 都可以通过private来修饰.

6.1.4 示例

需求

定义学生类Student, 包含姓名, 年龄属性.

StudentTest测试类中, 创建Student类的对象, 并调用Student类中的成员.

对年龄或者姓名属性加private修饰, 然后观察结果.

参考代码

//学生类
public class Student{
    //属性
    String name; //姓名
    private int age; //年龄
    
    //getXxx()和setXxx()方法
    public void setAge(int a) {
        if(a >= 0 && age <= 150) {
            //年龄合法, 就赋值.
            age = a;
        }
    }
    
    public int getAge() {
        return age;
    }
    
    //行为
    public void show() {
        //被private修饰的内容, 可以在本类中直接访问.
        System.out.println(name + "..." + age);
    }
}

//学生类的测试类
public class StudentTest{
    public static void main(String[] args) {
        //1. 创建学生类的对象
        Student s = new Student();
        //2. 给成员变量赋值.
        s.name = "张三";
        //s.age = -23;//被private修饰的内容, 外界无法直接访问.
         s.setAge(-23);
        //3. 调用成员方法.
        s.show();
    }
}

 

6.2 标准代码

刚才的代码, 为了讲解private关键字, 只给年龄属性加了private, 而在实际开发中, 除非必要, 否则成员变量都要用private来修饰, 然后提供对应的getXxx()和setXxx()方法, 方便用户访问对应的成员变量, 接下来, 我们来写一个实际开发中的标准代码.

需求

定义一个标准的学生类Student, 属性: 姓名和年龄, 行为: 学习, 吃饭.

在测试类中创建学生类的对象, 然后访问类中的成员.

参考代码

public class Student {
    //属性, 全部用private修饰.
    //姓名
    private String name;
    //年龄
    private int age;

    public String getName() {
        return name;
    }
    public void setName(String n) {
        name = n;
    }

    public int getAge() {
        return age;
    }
    public void setAge(int a) {
        age = a;
    }

    //行为, 也就是成员方法.
    public void study() {
        System.out.println("键盘敲烂, 月薪过万!");
    }
    public void eat() {
        System.out.println("学习饿了就要吃饭!.");
    }
}

//测试类
public class StudentTest {
    public static void main(String[] args) {
        //1. 创建学生对象.
        Student s = new Student();
        //2. 设置成员变量值.
        s.setName("张三");
        s.setAge(23);
        //3. 打印成员变量值.
        System.out.println(s.getName() + "..." + s.getAge());
        //4. 调用成员方法.
        s.study();
        s.eat();
    }
}

 

6.3 封装的概述和好处

6.3.1 概述

封装是面向对象编程思想的三大特征之一, 所谓的封装指的就是隐藏对象的属性和实现细节, 仅对外提供一个公共的访问方式.

记忆:

 面向对象的三大特征: 封装, 继承, 多态.

问题一: 怎么隐藏?

通过private关键字实现.

问题二: 公共的访问方式是什么?

getXxx() 和 setXxx()方法.

6.3.2 原则

把不需要对外提供的内容都隐藏起来.

把属性隐藏, 并提供公共方法对其访问.

 解释: 就是成员变量都用private修饰, 并提供对应的getXxx()setXxx()方法, 除此以外, 都用public修饰.

 

6.3.3 好处

提高代码的安全性.

 这点是由private关键字来保证的.

提高代码的复用性.

 这点是由方法来保证的, 方法也是封装的一种体现形式.

 

6.4 this关键字

6.4.1 概述

this代表本类当前对象的引用, 大白话翻译: 谁调用, this就代表谁.

6.4.2 作用

用来解决局部变量和成员变量重名问题的.

6.4.3 示例

需求

定义一个标准的学生类Student, 属性: 姓名和年龄, 行为: 学习, 吃饭.

在测试类中创建学生类的对象, 然后访问类中的成员.

参考代码

public class Student {
    //属性, 全部用private修饰.
    //姓名
    private String name;
    //年龄
    private int age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
        //s.age = 23;
    }

    //行为, 也就是成员方法.
    public void study() {
        System.out.println("键盘敲烂, 月薪过万!");
    }
    public void eat() {
        System.out.println("学习饿了就要吃饭!.");
    }
}

//测试类
public class StudentTest {
    public static void main(String[] args) {
        //1. 创建学生对象.
        Student s = new Student();
        //2. 设置成员变量值.
        s.setName("张三");
        s.setAge(23);
        //3. 打印成员变量值.
        System.out.println(s.getName() + "..." + s.getAge());
        //4. 调用成员方法.
        s.study();
        s.eat();
    }
}

6.4.4 总结

Java中, 使用变量遵循就近原则, 局部位置有就使用, 没有就去本类的成员位置找, 有就使用, 没有就报错.

解释: 先这么记忆, 不严谨, 因为本类没有, 还会去父类中查找, 这点在继承部分再解释.

 

6.5 构造方法

6.5.1 概述

构造方法是用来创建对象的, 捎带着可以给对象的各个成员变量赋值.

大白话:

 构造方法就是用来快速对对象的各个属性赋值的.

6.5.2 格式

构造方法名必须和类名完全一致(包括大小写).

构造方法没有返回值类型, 连void都不能写.

构造方法没有具体的返回值, 但是可以写return(实际开发, 一般不写).

public 类名(参数类型 参数名1, 参数类型 参数名2) {  //这里可以写多个参数.
    //给对象的各个属性赋值即可.
}

6.5.3 示例: 构造方法入门

需求

定义学生类Student, 在类的空参构造中打印一句话"这是构造方法".

StudentTest测试类中, 创建学生类的对象, 并观察程序的运行结果.

 小技巧: 创建对象的格式如下:

  类名 对象名 = new 构造方法(参数列表);

参考代码

//学生类.
public class Student{
    //类的空参构造.
    public Student() {
        System.out.println("这是构造方法");
    }
}

//学生类的测试类
public class StudentTest{
    public static void main(String[] args) {
        Student s = new Student();
    }
}

 

6.5.4 构造方法的注意事项

如果我们没有给出构造方法, 系统将给出一个默认的无参构造供我们使用.

如果我们给出了构造方法, 系统将不再提供默认的构造方法给我们使用.

这个时候, 如果我们还想使用无参构造, 就必须自己提供.

建议定义类时, 我们给出无参构造, 方便用户调用(实际开发都这么做的).

思考题

:  给成员变量赋值有几种方式?

: 1. 通过setXxx()方法实现(该方式不会创建新对象).

 2. 通过构造方法实现(该方式会创建新对象).

6.6 标准的类的定义和使用

6.6.1 格式

以后在实际开发中, 也都是这样写的, 即: 标准的类的定义格式如下:

public class 类名{
    //属性(成员变量), 全部用private修饰.
    
    //构造方法, 一般提供两个(无参, 全参)
    
    //getXxx()和setXxx()方法
    
    //行为(成员方法), 根据需求来定义.
}

 

6.6.2 示例

需求

定义一个标准的学生类Student, 属性: 姓名和年龄, 行为: 学习, 吃饭.

在测试类中创建学生类的对象, 然后访问类中的成员.

参考代码

//学生类
public class Student {
    //属性(成员变量), 全部用private修饰.
    private String name;    //姓名
    private int age;        //年龄
    
    //构造方法, 一般提供两个(无参, 全参)
    public Student() {
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //getXxx()和setXxx()方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    //行为(成员方法), 根据需求来定义.
    public void study() {
        System.out.println("键盘敲烂, 月薪过万!");
    }

    public void eat() {
        System.out.println("学习饿了要吃饭");
    }
}

//学生类的测试类
public class StudentTest {
    public static void main(String[] args) {
        //1. 创建学生对象.
        Student s = new Student();
        //2. 设置成员变量值.
        s.setName("张三");
        s.setAge(23);
        //3. 打印成员变量值.
        System.out.println(s.getName() + "..." + s.getAge());
        //4. 调用成员方法.
        s.study();
        s.eat();
    }
}

 

7. 继承

7.1 概述

多个类中存在相同属性和行为时, 将这些内容抽取到单独的一个类中, 那么这多个类就无需再定义这些属性和行为了, 只要继承那个类即可. 这个关系, 就叫继承.

注意:

 有了继承以后, 我们在定义一个类的时候, 可以在一个已经存在的类的基础上, 还可以定义自己的新成员.

7.2 格式

Java中, 可以通过extends关键字来实现类与类的继承, 具体格式如下:

public class A extends B { //子承父业
    
}

解释:

  • A: 子类, 或者派生类.
  • B: 父类, 基类, 或者超类.

我们一般会念做: 子类和父类.

 

7.3 继承的好处和弊端

7.3.1 示例

需求

按照标准格式定义一个人类(Person类), 属性为姓名和年龄.

定义老师类(Teacher), 继承自人类, 并在老师类中定义teach()方法.

定义学生类(Student), 继承自人类, 并在学生类中定义study()方法.

PersonTest测试类的main方法中, 分别创建老师类和学生类的对象, 并调用各自类中的成员.

参考代码

 

7.3.2 好处

提高了代码的复用性.

提高了代码的可维护性.

让类与类之间产生关系, 是多态的前提.

7.3.3 弊端

让类与类之间产生了关系, 也就让类的耦合性增强了.

解释:

 开发原则: 高内聚, 低耦合.

  • 内聚: 指的是类自己独立完成某些事情的能力.
  • 耦合: 指的是类与类之间的关系.

 

7.4 Java中继承的特点

7.4.1 示例

定义GrandFather类, 该类有一个grandFatherSay()方法, 该方法打印一句话爷爷都是从孙子过来的.

定义Father类, 该类有一个fatherSay()方法, 该方法打印一句话爸爸都是从儿子过来的.

定义Son类, 分别先继承自GrandFather类和Father类, 此时发现, 上述的两个方法只能同时调用一个.

如果想让Son类的对象, 同时能调用上述的两个方法, 则可以这样做:

 Son类继承自Father, Father类继承自GrandFather.

7.4.2 参考代码

//爷爷类,
//一个类如果没有显式的写父类, 默认就继承自Object类.
//Object类, 是所有类的父类.
public class GrandFather {
    public void grandFather() {
        System.out.println("grandFather....");
    }
}
//父亲类
public class Father extends GrandFather{
    public void father() {
        System.out.println("father....");
    }
}
//儿子类
public class Son extends Father {
}
//测试类
public class SonTest {
    public static void main(String[] args) {
        //创建子类对象.
        Son s = new Son();
        //问: Son的对象, 可以调用哪些方法?
        s.father();         //从Father类继承过来的.
        s.grandFather();    //从Father类继承过来的, 而Father类又从GrandFather类继承过来了.
    }
}

7.4.3 总结

Java中类与类之间只能单继承, 不能多继承.

 public class A extends B,类C { //这种写法会报错.
}

Java中类与类之间, 可以多层继承.

 public class A { }

public class B extends A{ }

public class C extends B{ } //这种写法可以.

 

7.5 继承中成员变量的特点

7.5.1 示例

定义Father类, 在该类的成员位置定义变量: int age = 30;

定义Son类, 让它继承Father类, 并在该类的成员位置定义变量: int age = 20;

在测试类FatherTest的main方法中, 定义变量: int age = 10;

通过输出语句, 直接打印age变量的值, 并查看程序的运行结果.

7.5.2 参考代码

public class Father {
    int age = 30;
}
public class Son extends Father {
    int age = 20;
}
public class FatherTest {
    public static void main(String[] args) {
        int age = 10;
        System.out.println(age);  //这里打印结果会是多少呢?
    }
}

7.5.3 总结

Java中使用变量遵循就近原则, 局部位置有就使用, 没有就去本类的成员位置找. 有就使用, 没有就去父类的成员位置找, 有就使用, 没有就报错.

注意: 不考虑父类的父类这种情况, 因为会一直往上找, 直到把所有的父类都找完, 还找不到, 就报错了.

 

7.6 super关键字

7.6.1 概述

super的用法和this很像:

  • this: 代表本类对象的引用.
  • super: 代表当前对象的父类的内存空间标识.

 解释: 可以理解为父类对象引用.

7.6.2 用法

功能

本类

父类

访问成员变量

this.成员变量名

super.成员变量名

访问构造方法

this(...)

super(...)

访问成员方法

this.成员方法名(参数值...)

super.成员方法名(参数值...)

7.7 继承中构造方法的特点

7.7.1 示例

定义父类Father, 并在空参构造中打印一句话: Father空参构造.

定义子类Son, 并在空参构造中打印一句话: Son空参构造.

FatherTest测试类的main方法中, 创建Son类的对象, 并查看程序的执行结果.

分别修改Father类和Son类的代码, 添加带参构造, 并观察程序的执行结果.

7.7.2 参考代码

.

 

7.7.3 总结

子类中所有的构造方法默认都会访问父类的空参构造.

 : 为什么这样设计呢?

 : 用于子类对象访问父类数据前, 对父类数据进行初始化.

 : 每一个构造方法的第一条语句默认都是: super()

如果父类没有空参构造, 我们可以通过super(参数)的形式访问父类的带参构造.

 解释: 但是这样做比较繁琐, 所以建议我们定义类时, 永远手动给出空参构造.

 

7.8 继承中成员方法的特点

7.8.1 示例

定义Father类, 并在类中定义method()方法和show()方法.

定义Son类, 并在类中定义method()方法和show()方法.

FatherTest测试类中, 创建Son类的对象, 并调用类中的成员方法.

注释子类中的method()方法或者show()方法, 并观察程序的执行结果.

7.8.2 参考代码

.

7.8.3 总结

调用成员方法时, 也遵循就近原则, 本类中有, 就直接调用, 本类中没有, 就去父类中查找, 有就使用, 没有就报错.

7.9 方法重写

7.9.1 概述

子类中出现和父类一模一样的方法时, 称为方法重写. 方法重写要求返回值的数据类型也必须一样.

7.9.2 应用场景

当子类需要使用父类的功能, 而功能主体又有自己独有需求的时候, 就可以考虑重写父类中的方法了, 这样, 即沿袭了父类的功能, 又定义了子类特有的内容.

7.9.3 示例

需求

定义Phone类, 并在类中定义call(String name)方法.

定义NewPhone类, 继承Phone类, 然后重写call(String name)方法.

PhoneTest测试类中, 分别创建两个类的对象, 然后调用call()方法, 观察程序执行结果.

参考代码

.

7.9.4 注意事项

子类重写父类方法时, 方法声明上要用@Override注解来修饰.

父类中私有的方法不能被重写.

子类重写父类方法时, 访问权限不能更低.

 

7.10 老师和学生案例(继承版)

7.10.1 需求

已知老师类Teacher, 属性有姓名和年龄, 行为有teach().

已知学生类Student, 属性有姓名和年龄, 行为有study().

分析上述的需求, 并通过代码实现.

PersonTest类的main方法中, 分别创建老师类和学生类的对象, 然后访问其成员.

7.10.2 参考代码

.

注意: 子类继承父类时, 一般构造方法也要手动生成, 目的是为了帮助我们快速创建对象并赋值.

 

8. 多态

8.1 概述

多态指的是同一个事物(或者对象)在不同时刻表现出来的不同状态.

例如: 一杯水.

  • 常温下是液体.
  • 高温下是气体.
  • 低温下是固体.

但是水还是那杯水, 只不过在不同的环境下, 表现出来的状态不同.

8.2 前提条件

要有继承关系.

要有方法重写.

要有父类引用指向子类对象.

 

8.3 示例: 多态入门

需求

定义动物类Animal, 并在类中定义一个成员方法: eat()

定义猫类Cat, 继承Animal类, 并重写eat()方法.

AnimalTest测试类的main方法中, 通过多态的方式创建猫类对象.

通过猫类对象, 调用eat()方法.

参考代码

.

 

8.4 多态中的成员访问特点

  • 成员变量: 编译看左边, 运行看左边.
  • 成员方法: 编译看左边, 运行看右边.

需求

定义一个人类Person. 属性为姓名和年龄, 行为是: eat()方法.

定义Student类, 继承自Person类, 定义age属性及重写eat()方法.

PersonTest测试类的main方法中, 创建Student类的对象, 并打印其成员.

参考代码

.

 

8.5 好处和弊端.

8.5.1 好处

提高了程序的扩展性.

8.5.2 弊端

父类引用不能访问子类的特有功能.

:  那如何解决这个问题呢?

:  通过向下转型来解决这个问题.

8.5.3 示例

需求

定义动物类Animal, 该类有一个eat()方法.

定义猫类Cat, 继承自Animal类, 该类有一个自己独有的方法: catchMouse().

AnimalTest测试类中, 通过多态的方式创建Cat类的对象, 并尝试调用catchMouse()方法.

参考代码

略.

8.5.4 向上转型和向下转型

向上转型

//格式
父类型 对象名 = new 子类型();

//例如
Animal an = new Cat();

向下转型

//格式
子类型 对象名 = (子类型)父类引用;

//例如
Cat c = (Cat)an;

Bug

//下述代码会报: ClassCastException(类型转换异常)
Animal an = new Cat();
Cat c = (Cat)an; //这样写不报错.
Dog d = (Dog)an; //这样写会报错.

 

8.6 示例: 猫狗案例

需求

已知猫狗都有姓名和年龄, 都要吃饭.

猫类独有自己的catchMouse()方法, 狗类独有自己的lookHome()方法.

AnimalTest测试类的main方法中, 通过多态分别创建猫类, 狗类的对象.

分别通过猫类对象和狗类对象, 访问对象的成员.

参考代码

.

 

9. 两个关键字

9.1 final关键字

9.1.1 概述

final是一个关键字, 表示最终的意思, 可以修饰类, 成员变量, 成员方法.

  • 修饰的类: 不能被继承, 但是可以继承其他的类.
  • 修饰的变量: 是一个常量, 只能被赋值一次.
  • 修饰的方法: 不能被子类重写.
9.1.2 示例

需求

定义Father类, 并定义它的子类Son.

先用final修饰Father类, 看Son类是否还能继承Father类.

Son类中定义成员变量age, 并用final修饰, 然后尝试给其重新赋值, 并观察结果.

Father类中定义show()方法, 然后用final修饰, 看Son类是否能重写该方法.

参考代码

 

9.2 static关键字

9.2.1 概述

static是一个关键字, 表示静态的意思, 可以修饰成员变量, 成员方法.

9.2.2 特点

随着类的加载而加载.

优先于对象存在.

static修饰的内容, 能被该类下所有的对象共享.

 解释: 这也是我们判断是否使用静态关键字的条件.

可以通过类名.的形式调用.

9.2.3 示例

需求

定义学生类, 属性为姓名, 年龄, 毕业院校(graduateFrom).

在学生类中定义show()方法, 用来打印上述的各个属性信息.

在测试类的main方法中, 创建学生对象, 并调用学生类的各个成员.

参考代码

//学生类
public class Student {
    //属性
    String name;
    int age;
    static String graduateFrom;

    //行为
    public void show() {
        System.out.println(name + ".." + age + ".." + graduateFrom);
    }
}

//测试类
public class StudentTest {
    public static void main(String[] args) {
        //1. 设置学生的毕业院校.
        Student.graduateFrom = "传智学院";
        //2. 创建学生对象s1.
        Student s1 = new Student();
        s1.name = "刘亦菲";
        s1.age = 33;

        //3. 创建学生对象s2.
        Student s2 = new Student();
        s2.name = "赵丽颖";
        s2.age = 31;

        //4. 打印属性值.
        s1.show();
        s2.show();
    }
}

 

9.2.4 静态方法的访问特点及注意事项
  • 访问特点

 静态方法只能访问静态的成员变量和静态的成员方法.

 简单记忆: 静态只能访问静态.

  • 注意事项

在静态方法中, 是没有this, super关键字的.

因为静态的内容是随着类的加载而加载, 而this和super是随着对象的创建而存在.

 : 先进内存的, 不能访问后进内存的.

需求

定义学生类, 属性为姓名和年龄(静态修饰), 非静态方法show1(),show2(), 静态方法show3(), show4().

尝试在show1()方法中, 调用: 姓名, 年龄, show2(), show4().

 结论: 非静态方法可以访问所有成员(非静态变量和方法, 静态变量和方法)

尝试在show3()方法中, 调用: 姓名, 年龄, show2(), show4().

 结论: 静态方法只能访问静态成员.

参考代码

.

 

10. 抽象类

10.1 概述

回想前面我们的猫狗案例, 提取出了一个动物类, 这个时候我们可以通过Animal an = new Animal();来创建动物对象, 其实这是不对的, 因为, 我说动物, 你知道我说的是什么动物吗? 只有看到了具体的动物, 你才知道, 这是什么动物. 所以说, 动物本身并不是一个具体的事物, 而是一个抽象的事物. 只有真正的猫, 狗才是具体的动物. 同理, 我们也可以推想, 不同的动物吃的东西应该是不一样的, 所以, 我们不应该在动物类中给出具体的体现, 而是应该给出一个声明即可. Java中, 一个没有方法体的方法应该定义为抽象方法, 而类中如果有抽象方法, 该类必须定义为抽象类.

10.2 入门案例

需求

创建抽象类Animal.

在该类中定义抽象方法eat()

参考代码

//抽象的动物类.
public abstract class Animal {
    //抽象方法, 吃.
    public abstract void eat();
}

 

10.3 抽象类的特点

抽象类和抽象方法必须用abstract关键字修饰.

 //抽象类
public abstract class 类名{ }

//抽象方法
public abstract void eat();

抽象类中不一定有抽象方法, 有抽象方法的类一定是抽象类.

抽象类不能实例化.

– 那抽象类如何实例化呢?

– 可以通过多态的方式, 创建其子类对象, 来完成抽象类的实例化. 这也叫: 抽象类多态.

抽象类的子类:

如果是普通类, 则必须重写父抽象类中所有的抽象方法.

如果是抽象类, 则可以不用重写父抽象类中的抽象方法.

需求

定义抽象类Animal , 类中有一个抽象方法eat(), 还有一个非抽象方法sleep().

尝试在测试类中, 创建Animal类的对象, 并观察结果.

创建普通类Cat, 继承Animal类, 观察是否需要重写Animal#eat()方法.

创建抽象类Dog, 继承Animal类, 观察是否需要重写Animal#eat()方法.

参考代码

 

10.4 抽象类的成员特点

抽象类中可以有变量, 常量, 构造方法, 抽象方法和非抽象方法.

思考:  既然抽象类不能实例化, 那要构造方法有什么用?

:  用于子类对象访问父类数据前, 对父类数据进行初始化.

需求

定义抽象类Person, 在类中定义变量age, 常量country, 空参, 全参构造.

Person类中定义非抽象方法show(), 抽象方法eat().

在测试类的main方法中, 创建Person类的对象, 并调用类中的成员.

参考代码

 

10.5 案例: 老师类

10.5.1 需求

传智播客公司有基础班老师(BasicTeacher)和就业班老师(WorkTeacher), 他们都有姓名和年龄, 都要讲课.

不同的是, 基础班老师讲JavaSE, 就业班老师讲解JavaEE.

请用所学, 模拟该知识点.

10.5.2 分析

定义父类Teacher, 属性: 姓名和年龄, 行为: 讲课(因为不同老师讲课内容不同, 所以该方法是抽象的).

定义BasicTeacher(基础班老师), 继承Teacher, 重写所有的抽象方法.

定义WorkTeacher(就业班老师), 继承Teacher, 重写所有的抽象方法.

定义TeacherTest测试类, 分别测试基础班老师和就业班老师的成员.

10.5.3 参考代码

.

 

11. 接口

11.1 概述

继续回到我们的猫狗案例,我们想想狗一般就是看门,猫一般就是作为宠物了。但是,现在有很多的驯养员或者是驯兽师,可以训练出:猫钻火圈,狗跳高,狗做计算等。而这些额外的动作,并不是所有猫或者狗一开始就具备的,这应该属于经过特殊的培训训练出来的。所以,这些额外的动作定义到动物类中就不合适,也不适合直接定义到猫或者狗中,因为只有部分猫狗具备这些功能

所以,为了体现事物功能的扩展性,Java中就提供了接口来定义这些额外功能,并不给出具体实现,将来哪些猫狗需要被训练,只需要这部分猫狗把这些额外功能实现即可。

11.2 特点

接口用interface关键字修饰.

类和接口之间是实现关系, 用implements关键字表示.

接口不能实例化.

– 那接口如何实例化呢?

– 可以通过多态的方式, 创建其子类对象, 来完成接口的实例化. 这也叫: 接口多态.

接口的子类:

如果是普通类, 则必须重写父接口中所有的抽象方法.

如果是抽象类, 则可以不用重写父接口中的抽象方法.

需求

定义Jumpping接口, 接口中有一个抽象方法jump().

定义Cat类, 实现Jumpping接口, 重写jump()方法.

在测试类的main方法中, 创建Jumpping接口对象, 并调用其jump()方法

参考代码

.

11.3 成员特点

接口中有且只能有常量或者抽象方法, 原因是因为:

  • 成员变量有默认修饰符: public static final
  • 成员方法有默认修饰符: public abstract

注意: 因为接口主要是扩展功能的, 而没有具体存在, 所有接口中是没有构造方法的.

记忆: JDK1.8的时候, 接口中加入了两个新的成员: 静态方法, 默认方法(必须用default修饰).

 

需求

定义接口Inter, 测试接口中的成员特点.

参考代码

.

11.4 类与接口之间的关系

  • 类与类之间: 继承关系, 只能单继承, 不能多继承, 但是可以多层继承.
  • 类与接口之间: 实现关系, 可以单实现, 也可以多实现. 还可以在继承一个类的同时实现多个接口.
  • 接口与接口之间: 继承关系, 可以单继承, 也可以多继承.

11.5 抽象类和接口之间的区别

成员特点区别.

11.6.2 参考代码

.

 

12. 运动员和教练案例

12.1 需求

已知有乒乓球运动员(PingPangPlayer)和篮球运动员(BasketballPlayer), 乒乓球教练(PingPangCoach)和篮球教练(BasketballCoach).

他们都有姓名和年龄, 都要吃饭, 但是吃的东西不同.

乒乓球教练教如何发球, 篮球教练教如何运球和投篮.

乒乓球运动员学习如何发球, 篮球运动员学习如何运球和投篮.

为了出国交流, 跟乒乓球相关的人员都需要学习英语.

请用所学, 模拟该知识.

 

12.2 参考代码

 

13. 包

13.1 简述

(package)就是文件夹, 用来对类进行分类管理的. 例如:

  • 学生的增加, 删除, 修改, 查询.
  • 老师的增加, 删除, 修改, 查询.
  • 其他类的增删改查...
  • 基本的划分: 按照模块和功能划分.
  • 高级的划分: 就业班做项目的时候你就能看到了.

13.2 格式

package 包名1.包名2.包名3; //多级包之间用.隔开

注意:

package语句必须是程序的第一条可执行的代码.

package语句在一个.java文件中只能有一个.

13.3 常见分类

  • 按照功能分

– com.itheima.add

  • AddStudent.java
  • AddTeacher.java

– com.itheima.delete

  • DeleteStudent.java
  • DeleteTeacher.java
  • 按照模块分

– com.itheima.student

  • AddStudent
  • DeleteStudent

– com.itheima.teacher

  • AddTeacher
  • DeleteTeacher

13.4 导包

13.4.1 概述

不同包下的类之间的访问,我们发现,每次使用不同包下的类的时候,都需要加包的全路径。比较麻烦。这个时候,java就提供了导包的功能。

13.4.2 格式

import 包名;

import java.util.*; 意思是导入java.util包下所有类, 这样做效率较低, 不推荐使用.

import java.util.Scanner; 意思是导入java.util.Scanner, 推荐使用. 用谁就导入谁.

13.4.3 示例

需求

cn.itcast包下定义Teacher类, 该类下有一个method()方法.

com.itheima包下定义Student类, 该类有一个show()方法.

com.itheima包下定义Test测试类, 尝试访问Student#show(), Teacher#method().

参考代码

.

 

14. 权限修饰符

14.1 概述

权限修饰符是用来修饰类, 成员变量, 构造方法, 成员方法的, 不同的权限修饰符对应的功能不同. 具体如下:

 

public

protected

默认

private

同一个类中

同一个包中的子类或者其他类

 

不同包中的子类

 

 

不同包中的其他类(无关类)

 

 

 

14.2 示例

需求

com.itheima包下定义Father类, 该类中有四个方法, 分别是show1(), show2(), show3(), show4().

上述的四个方法分别用权限修饰符private, 默认, protected, public修饰.

Father类中, 创建main方法, 然后创建Father类的对象, 并调用上述的4个方法, 并观察结果.

com.itheima包下创建Test类及main方法, 然后创建Father类的对象, 并调用上述的4个方法, 并观察结果.

com.itheima包下创建Father类的子类Son, 添加main方法.

main方法中, 创建Son类的对象, 并调用上述的4个方法, 并观察结果.

cn.itcast包下创建Father类的子类Son, 然后在该类中添加main方法.

main方法中, 创建Son类的对象, 并调用上述的4个方法, 并观察结果.

cn.itcast包下创建Test类及main方法, 然后创建Father类的对象, 并调用上述的4个方法, 并观察结果.

参考代码

14.3 总结

访问权限修饰符的权限从大到小分别是: public > protected > 默认 > private

在实际开发中, 如果没有特殊需求, 则成员变量都用private修饰, 其它都用public修饰.

大白话总结四个访问权限修饰符的作用:

private: 强调的是给自己使用.

默认: 强调的是给同包下的类使用.

protected: 强调的是给子类使用.

public: 强调的是给大家使用.

 

方法重写简介:
概述:
子父类间, 子类出现和父类一模一样的方法时, 称为方法重写, 方法重写要求返回值的数据类型也必须一致.
使用场景:
当子类需要沿袭父类的功能, 但是功能主体又有自己额外需求的时候, 就可以考虑使用方法重写来实现了,
这样既沿袭了父类的功能, 又加入了自己额外的功能.
注意事项:
1.子类重写父类方法时, 方法声明上要用@Override注解来修饰.
2.父类中私有的方法不能被重写.
3.子类重写父类方法时, 访问权限不能更低.
访问权限修饰符范围从大到小分别是:
public > protected > 默认 > private
*/

super 和 this的用法:
关于 成员变量:
this.成员变量 访问本类的成员变量
super.成员变量 访问父类的成员变量
关于 构造方法:
this(参数1, 参数2); 访问本类的构造方法
super(参数1, 参数2); 访问父类的构造方法
关于 成员方法:
this.成员方法 访问本类的成员方法
super.成员方法 访问父类的成员方法

结论:
1. 子类所有构造方法的第一行默认都有一个super()去访问父类的空参构造.
问题1: 为什么要这样设计?
答案: 用于子类对象访问父类数据前, 对父类数据进行初始化.

问题2: 为什么访问的是空参, 而不是带参?
答案: 因为所有的类都直接或者间接继承自Object, Object类只有一个空参构造.

2. 如果父类没有空参构造怎么办?
方式1: 通过 super(参数) 的形式访问父类的带参构造.
方式2: 通过 this(参数) 的形式访问本类的其他构造.

3. 实际开发写法, 子类的空参构造访问父类的空参构造, 子类的全参构造访问父类的全参构造.
*/

继承简介:
概述:
实际开发中, 我们发现好多类中的部分内容是相似的, 每次写很麻烦, 于是我们可以把这些类中相似的部分提取出来单独的放到一个类中(父类),
然后让那多个类(子类)和这个类(父类)产生一个关系, 这个关系就叫: 继承, Java中用 extends 关键字来表示继承.
格式:
public class 类A extends 类B { //子承父业
}
叫法:
类A: 子类, 派生类
类B: 父类, 基类, 超类.
好处:
1. 提高代码的复用性.
2. 提高代码的可维护性.
3. 让类与类之间产生关系, 是多态的前提.
弊端:
类与类之间的耦合性增强了.

补充: 十大黄金开发原则之一: 高内聚, 低耦合.
内聚: 指的是类自己独立处理某些问题的能力.
耦合: 指的是类与类之间的关系.

这个类是: 测试类.
*/

构造方法简介:
概述:
// 主职 兼职
构造方法就是用来创建对象的, 捎带着可以给对象的各个属性赋值.
格式:
public 类名(数据类型 变量名, 数据类型 变量名) {
...
}
细节:
1. 构造方法名必须和类名完全一致, 包括大小写.
2. 构造方法没有明确的返回值, 但是可以写return.
3. 构造方法没有返回值的数据类型, 连void都不能写.
4. 如果定义类的时候, 你不写构造方法, 系统会默认给一个 空参构造.
5. 如果你写了(不管写的是空参还是带参), 系统都不再提供默认的构造方法了.

this简介:
概述:
它代表本类当前对象的引用, 大白话翻译: 谁调用(方法), this就代表谁.
细节:
非静态方法(没有static修饰的方法)内部都有一个 this 关键字, 谁调用(方法), this就代表谁.
作用:
用来解决 局部变量 和 成员变量 重名问题的.

使用变量遵循的原则:
使用变量遵循"就近原则", 局部位置有就使用, 没有就去本类的成员位置找,
有就使用, 没有就报错. //先这么记忆, 不严谨, 因为本类找不到, 还会去父类找.

封装的相关简介:
概述:
封装指的就是 隐藏代码的属性和实现细节, 仅对外提供一个公共的访问方式.
问题1: 怎么隐藏?
通过 private 关键字实现.
问题2: 公共的访问方式是什么?
getXxx(), setXxx()
细节: 谁需要封装呢?
1. 实际开发中, 如非必要, 属性要全部私有化.
2. 可以把不需要外界直接访问的内容, 也私有化.
好处:
1. 提高代码的安全性. 由 private 关键字保证.
2. 提高代码的复用性. 由 方法 保证.
*/

案例: 演示局部变量 和 成员变量的区别?

答案:
1. 定义位置不同.
局部变量: 方法中, 或者方法的形参上.
成员变量: 类中, 方法外.
2. 初始化值不同.
局部变量: 没有默认值, 必须先声明, 后赋值, 然后才能使用.
成员变量: 有默认值. 例如:
int: 0
double: 0.0
boolean: false
String: null
3. 在内存中的存储位置不同.
局部变量: 存储在 栈 中.
成员变量: 存储在 堆 中.
4. 生命周期不同.
局部变量: 属于方法, 随着方法的调用而存在, 随着方法的调用完毕而消失.
成员变量: 属于对象, 随着对象的创建而存在, 随着对象的消失而消失.

原文地址:https://www.cnblogs.com/shan13936/p/13811336.html