目 录CONTENT

文章目录

从0开始学Java——面向对象上(7)

Eric
2022-01-23 / 1 评论 / 0 点赞 / 212 阅读 / 10,874 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2023-12-12,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

1 面向对象思想概述

1.1 概述

  • Java语言是一种面向对象的程序设计语言,面向对象思想(OOP)是一种程序设计思想,我们在面向对象思想的指引下,使用Java语言去设计、开发计算机程序。

  • 这里的对象泛指现实中的一切事物,每种事务都具备自己的属性行为

  • 面向对象思想就是在计算机程度设计的过程中,参照现实中的事物,将事物的属性特征、行为特征抽象出来,描述成计算机事物的设计思想。

  • 面向对象思想区别于面向过程思想(POP),强调的是通过调用对象的行为来实现功能,而不是自己一步一步的去操作实现。

1.2 面向过程和面向对象的区别

  • 面向过程(POP):Process Oriented Programming

    • 以函数(方法)为最小单位。
    • 数据独立于函数之外。
    • 以过程、步骤为主,考虑怎么做?
  • 面向对象(OOP):Object Oriented Programming

    • 以类、对象为最小单位。
    • 类包括数据和方法。
    • 以对象(谁)为主,考虑谁来做,谁能做?
  • 面向对象依然包含面向过程,只不过关注点变了,关注谁来做。

  • 面向过程中程序员的角色:程序员是具体执行者。

  • 面向对象中程序员的角色:程序员是指挥者。

  • 面向对象思想是一种更符合我们思考习惯的思想,它可以将复杂的事情简单化,并将我们从执行者变成了指挥者。

  • 例子:将大象装进冰箱

  • 面向过程:

① 打开冰箱
② 将大象装进冰箱
③ 将冰箱门关闭
  • 面向对象:
冰箱{
    开门(){}
    关门(){}
}
大象{
    进入(冰箱){}
}
人{
    打开(冰箱){}
    操作(大象){
      大象.进入(冰箱);
    }
    关闭(冰箱){
      冰箱.关门();
    }
}

1.3 面向对象的基本特征

  • 面向对象的语言中,包含了三大基本特征:封装继承多态

2 类和对象

2.1 概述

  • 环顾周围,我们会发现很多对象,比如:桌子、椅子、电脑、同事、同学等。桌子属于办公用品,同事和同学都是人类,那么什么是类?什么是对象?

2.2 什么是类?

  • 类是一类具有相同特性的事物的抽象描述,是一组相关属性行为的集合。可以将类看成是一类事物的模板,使用事物的属性特性行为特性来描述该类事物。

  • 类可以看做是一个模板,或者图纸,系统根据类的定义来创建对象,以后我们要造一个汽车,怎么造?类就是这个图纸,规定了汽车的详细信息,然后根据图纸将汽车造出来。

  • 示例:英雄联盟中的类和对象

img

英雄就是类,具体的英雄,如盖伦、提莫就是对象。

  • 示例:月饼模具和月饼

img

月饼模具是类,使用月饼模具制作的月饼就是对象。

  • 现实生活中,描述一类事物:

    • 属性:就是该事物的状态信息。
    • 行为:就是该事物能够做什么?
  • 示例:猫

  • 属性:名字、体重、年龄、颜色。

  • 行为:走、跑、叫。

2.3 什么是对象?

  • 对象:就是一类事物的具体体现。对象是类的一个实例,必然具有该类事物的属性和行为。

  • 示例:一只小猫

  • 属性:tom、5kg、2岁,黄色。

  • 行为:溜墙根走、蹦跶的跑、喵喵叫。

2.4 类和对象的关系

  • 类是对一类事物的描述,是抽象的

  • 对象是一类事物的实例,是具体的

  • 类是对象的模板,对象是类的实体

img

3 类的定义

3.1 事物和类的对比

  • 现实世界中的一类事物:

    • 属性:事物的状态信息。
    • 行为:事物能够做什么。
  • Java中用class描述事物也是如此:

    • 成员变量:对应事物的属性
    • 成员方法:对应事物的行为

3.2 类的定义

  • 语法:
访问修饰符 class 类名{
    // 成员变量
    
    // 成员方法
}
  • 定义类:就是定义类的成员,包括成员变量成员方法

  • 成员变量:和以前定义变量几乎一模一样,只不过位置发生了改变。在类中,方法外

  • 成员方法:和以前写的main方法类似,只不过功能更加丰富了。

  • 示例:

/**
 * 学生类
 *
 * @author open1024
 * @version 1.0
 * @since 2021-08-26 19:23
 */
public class Student {
    /**
     * 姓名
     */
    String name;

    /**
     * 年龄
     */
    int age;

    /**
     * 性别
     */
    char gender;

    /**
     * 学习的方法
     */
    public void study() {
        System.out.println("学习使我快乐");
    }

}

4 对象的创建和使用

4.1 对象的创建和使用

  • 对象的创建语法:
// 匿名对象
new 类名();
// 给创建的对象命名,或者说,将创建的对象用一个引用数据类型的变量保存起来
类名 对象名 = new 类名();
  • 对象的使用语法:
// 赋值
对象名.成员变量 = 值;
// 访问
对象名.成员变量;
  • 示例:
/**
 * @author open1024
 * @version 1.0
 * @since 2021-08-26 19:30
 */
public class StudentTest {
    public static void main(String[] args) {
        // 匿名对象
        System.out.println(new Student());
        // 把创建的对象用一个引用数据类型的变量保存起来
        Student student = new Student();
        System.out.println("student = " + student);

        student.name = "法外狂徒--张三";
        student.age = 18;
        student.gender = '男';

        System.out.println("姓名:" + student.name + ",年龄:" + student.age + ",性别:" + student.gender);

        student.study();
    }
}

4.2 类的访问机制

  • 在一个类中的访问机制:类中的方法可以直接访问类中的成员变量,但是static方法访问非static的成员变量,编译不通过。

  • 在不同类中的访问机制:先创建要访问类的对象,然后用对象访问类中定义的成员。

4.3 匿名对象

  • 可以不定义对象的句柄,而直接调用这个对象的方法,这样的对象就叫做匿名对象。

  • 匿名对象的语法:

new 类名();
  • 应用场景:

    • 如果对一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
    • 实际开发中,经常将匿名对象作为实参传递给一个方法调用。

5 关键字 package和import

5.1 package(包)

  • package语句作为Java源文件的第一条语句,指明该文件中定义的类所在的包(如果缺省该语句,则指定为无名包)。

  • 语法:

package 顶层包名.子包名;
  • 包对应于文件系统的目录,package语句中,用.来指明包(目录)的层次。

  • 包通常用小写单词标识。通常使用所在公司域名的倒置,比如:com.github.xxx

  • 包的作用:

    • 包帮助管理大型软件系统:将功能相近的类划分到同一个包中,比如:MVC设计模式
    • 包可以包含类和子包,划分项目层次,便于管理。
    • 解决类命名冲突的问题。
    • 控制访问权限。
  • MVC设计模式:

    • MVC是常用的设计模式之一,将整个程序分为三个层次:视图模型层控制器层数据模型层。这种将程序输入输出、数据处理以及数据的展示分离开来的设计模式,使得程序结构变得灵活而且清晰,同时也描述了程序各个对象间的通信方式,降低了程序的耦合性。
    • 模型层(model):主要处理数据。
    • 控制层(controller):处理业务逻辑。
    • 视图层(view):显示数据。
  • jdk中的主要包:

    • java.lang:包含一些Java语言的核心类,如String、Math、Integer、System和Thread,提供常用功能。
    • java.net:包含执行和网络相关的操作的类和接口。
    • java.io:包含能提供多种输入、输出的功能的类。
    • java.util:包含一些实用工具类,如定义系统特性、接口的集合框架类、使用和日期日历相关的函数。
    • java.text:包含了一些java格式化相关的类。
    • java.sql:包含了java进行JDBC数据库编程的相关类/接口。
    • java.awt:包含了构成抽象窗口工具集的多个类,这些类被用来构建和管理应用程序的图形用户界面。
  • 示例:

package com.github.model;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-08-27 06:36
 */
public class Student {
    String name;

    /**
     * 年龄
     */
    int age;

    /**
     * 性别
     */
    char gender;

    /**
     * 学习的方法
     */
    public void study() {
        System.out.println("学习使我快乐");
    }
}

5.2 import(导入)

  • 为了使用定义在不同包中的Java类,需要使用import语句来引入指定包层次下所需要的类或全部类(*)。

  • import语句告诉编译器到哪里去寻找类

  • 语法:

import 包名.类名;
  • 注意事项:

    • 在源文件中使用import显示的导入指定包下的类或接口。
    • import声明在package声明和class声明之间。
    • 如果需要导入多个类或接口,那么就并列的显示多个import语句即可。
import java.util.*;
    • 如果导入的类或接口是java.lang包下的,或者是当前包下的,则可以省略此import语句。
    • 如果在代码中使用不同包下的同名的类,那么就需要使用类的全类名(包名.类名)的方式指定调用的是哪个类。
    • 如果已经导入java.a包下的类,那么如果需要使用a包的子包下的类,仍然需要导入。
    • import static组合的使用:调用指定类或接口下的静态的属性或方法。
  • 示例:

package com.github.model;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-08-27 06:36
 */
public class Student {
    String name;

    /**
     * 年龄
     */
    int age;

    /**
     * 性别
     */
    char gender;

    /**
     * 学习的方法
     */
    public void study() {
        System.out.println("学习使我快乐");
    }
}
package com.github.service;

import com.github.model.Student;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-08-27 06:42
 */
public class StudentService {
    
    private Student student = new Student();
    
    public void study(){
        student.study();
    }
    
}

6 类的成员之一:属性

6.1 语法格式

  • 语法:
访问修饰符 class 类名{
    访问修饰符 数据类型 属性名 = 初始化值;
    访问修饰符 数据类型 属性名;
}
  • 访问修饰符:

    • 常用的权限访问修饰符有:private、缺省、protected、public。
    • 其他的访问修饰符:static、final。
  • 数据类型:任何基本数据类型或任何引用数据类型。

  • 属性名:属于标识符,符合命名规则和规范即可。

  • 示例:

package com.github.model;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-01 06:16
 */
public class Person {
    /**
     * 声明public类型的变量name
     */
    public String name = "张三";
    /**
     * 声明private类型的变量age
     */
    private int age;
}

6.2 变量的分类

  • 成员变量:方法体外,类体内声明的变量。

  • 局部变量:在方法体内声明的变量。

img

img

注意:成员变量和局部变量在初始化值方面的异同。

  • 同:都有生命周期。

  • 异:局部变量除形参外,均需显示初始化。

  • 成员变量和局部变量的区别:

成员变量局部变量
声明的位置直接声明在类中,方法体外方法形参或内部,代码块内,构造器内等
访问修饰符private、public、static、final等不能用权限访问修饰符,可以用final修饰
初始化值有默认的初始化值没有默认初始化值,必须显示赋值,方可使用
内存加载位置堆空间或静态域内栈空间

6.3 成员变量的特点

6.3.1 成员变量的默认初始化值

  • 当一个对象被创建后,会对其中各种类型的成员变量自动进行初始化赋值。除了基本数据类型之外的变量类型都属于引用数据类型。
成员变量类型初始化值
byte0
short0
int0
long0L
float0.0F
double0.0
char‘\u0000’
booleanfalse
引用数据类型(数组,类,接口)null

6.3.2 类变量的值是所有对象共享的,而实例变量的值是每个对象独立的

  • 示例:
package com.github.demo;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-01 06:48
 */
public class ChineseTest {
    public static void main(String[] args) {
        Chinese c1 = new Chinese();
        Chinese c2 = new Chinese();

        c1.name = "张三";
        c2.name = "李四";
        c2.gender = '女';

        // c1.country = "中国";
        Chinese.country = "中国";// 推荐

        System.out.println("c1.country = " + Chinese.country + ",c1.name = " + c1.name + ",c1.gender = " + c1.gender);
        System.out.println("c2.country = " + Chinese.country + ",c2.name = " + c2.name + ",c2.gender = " + c2.gender);
    }
}

class Chinese {
    /**
     * 国家
     */
    static String country = "中国";

    /**
     * 姓名
     */
    String name;

    /**
     * 性别
     */
    char gender = '男';
}

6.4 如何在类外面访问成员变量?

  • 类变量:
类名.静态成员变量 // 推荐
对象名.静态成员变量 // 不推荐
  • 实例变量:
对象名.静态成员变量
  • 示例:
package com.github.demo;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-01 06:48
 */
public class ChineseTest {
    public static void main(String[] args) {
        // 类名.静态成员变量
        System.out.println(Chinese.country);

        Chinese c1 = new Chinese();
        // 对象名.非静态成员变量
        System.out.println(c1.name);
        // 对象名.静态成员变量 不推荐
        System.out.println(Chinese.country);
        // 对象名.非静态成员变量
        System.out.println(c1.gender);
    }
}

class Chinese {
    /**
     * 国家
     */
    static String country = "中国";

    /**
     * 姓名
     */
    String name;

    /**
     * 性别
     */
    char gender = '男';
}

7 类的成员之二:方法

7.1 什么是方法(函数)?

  • 方法是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称之为函数或过程。

  • 将功能封装为方法的目的是:可以实现代码重用,简化代码。

  • Java里面的方法不能独立存在,所有的方法必须定义在类里面。

  • 示例:

package com.github.demo1;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-01 20:18
 */
public class Person {
    private int age;

    /**
     * 声明方法getAge
     *
     * @return
     */
    public int getAge() {
        return this.age;
    }

    /**
     * 声明方法setAage
     *
     * @param age
     */
    public void setAge(int age) {
        // 将参数age的值赋给类成员变量age
        this.age = age;
    }
}

7.2 语法格式

  • 格式:
访问修饰符 返回值类型 方法名(参数类型1 参数名1,参数类型2 参数名2,...){
    // 方法体程序
    return 返回值;
}
  • 解释:

    • 访问修饰符:private、缺省、protected、public等。
    • 返回值类型:
      • 没有返回值:void。
      • 有返回值:声明返回值的类型。和方法体中的return 返回值 ;搭配使用。
    • 方法名:属于标识符,命名时遵循标识符命名规则和规范,尽量做到见名知意
    • 形参列表:可以包含零个、一个或多个参数。多个参数时,中间用逗号,隔开。
    • 返回值:方法在执行完毕后返回给调用它的程序的数据。

7.3 方法的分类

  • 按照是否有形参及返回值:
无返回值有返回值
无形参void 方法名(){}返回值类型 方法名(){}
有形参void 方法名(形参列表){}返回值类型 方法名(形参列表){}

7.4 方法的调用

  • 方法通过方法名被调用,且只有被调用才会执行。

img

7.5 方法的注意事项

  • ① 方法被调用一次,就会执行一次。

  • ② 没有具体返回值的情况,返回值类型用关键字void表示,那么方法体中可以不使用return语句;如果使用,仅仅用来结束方法。

  • ③ 定义方法时,方法的结果应该返回给调用者,交由调用者处理。

  • ④ 方法中只能调用方法或属性,不可以在方法内部定义方法

7.6 练习

7.6.1 练习1

  • 创建一个Person类,其定义如下:

img

  • 要求:

  • ①创建Person类的对象,设置该对象的name、age和sex属性,调用study方法,输出字符串“studying”,调用showAge()方法显示age值,调用addAge()方法给对象的age属性值增加2岁。

  • ②创建第二个对象,执行上述操作,体会同一个类的不同对象之间的关系。

  • 示例:

package com.github.exec.demo1;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-02 20:42
 */
public class Person {

    /**
     * 姓名
     */
    String name;

    /**
     * 年龄
     */
    int age;

    /**
     * 性别
     */
    int sex;

    /**
     * 学习
     */
    public void study() {
        System.out.println("studying");
    }

    /**
     * 显示年龄
     */
    public void showAge() {
        System.out.println(this.age);
    }


    /**
     * 添加年龄
     *
     * @param age 年龄
     */
    public void addAge(int age) {
        this.age += age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + this.name + '\'' +
                ", age=" + this.age +
                ", sex=" + this.sex +
                '}';
    }
}
package com.github.exec.demo1;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-02 20:54
 */
public class PersonTest {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name = "open1024";
        p1.age = 20;
        p1.sex = 1;

        p1.study();
        p1.addAge(2);
        p1.showAge();
        System.out.println("p1 = " + p1);

        Person p2 = new Person();
        p2.name = "张三";
        p2.age = 18;
        p2.sex = 2;

        p2.study();
        p2.addAge(2);
        p2.showAge();
        System.out.println("p2 = " + p2);
    }
}

7.6.2 练习2

  • 利用面向对象的编程思想,设计类Circle计算圆的面积。

  • 示例:

package com.github.exec.demo2;

/**
 * 利用面向对象的编程思想,设计类Circle计算圆的面积
 *
 * @author open1024
 * @version 1.0
 * @since 2021-09-02 21:04
 */
public class Circle {

    private int radius;

    public void setRadius(int radius) {
        this.radius = radius;
    }

    /**
     * 计算圆的面积
     *
     * @return double
     */
    public double calculateAreaOfCircle() {
        return Math.PI * Math.pow(this.radius, 2);
    }
}
package com.github.exec.demo2;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-02 21:07
 */
public class CircleTest {
    public static void main(String[] args) {
        Circle circle = new Circle();
        circle.setRadius(2);
        double area = circle.calculateAreaOfCircle();
        System.out.println("area = " + area);
    }
}

8 可变参数

8.1 概述

  • JDK 5 提供了Varargs(variable number of arguments)机制,允许直接定义能和多个实参相匹配的形参,从而可以用一种更简单的方式,来传递个数可变的实参。

  • JDK 5 之前采用数组形参来定义方法,传入多个同一类型的变量:

public static void test(int a, String[] args){}
  • JDK 5 之后,采用可变个数的形参来定义方法,传入多个同一类型的变量:
public static void test(int a,String... args){}

8.2 注意事项

  • ① 语法:
// 参数的类型名,如:int、long、String等
方法名(参数的类型名... 参数名){}
  • ② 可变参数:方法参数部分指定类型的参数个数可以是0、1或多个。

  • ③ 可变个数形参的方法和同名的方法之间,彼此构成方法重载。

  • ④ 可变参数方法的使用和方法参数部分是数组的使用方式是一致的。

  • ⑤ 方法的参数部分如果有可变参数,需要放在形参声明的最后。

  • ⑥ 在一个方法的形参位置,有且只能有一个可变个数形参。

8.3 应用示例

  • 示例:
package com.fairy;

import java.util.Arrays;

/**
 * ② 和 ③ 是等同的,如果使用的是IDEA,会帮你将③格式化成②
 * 
 * @author open1024
 * @version 1.0
 * @since 2021-09-03 14:16
 */
public class VarargsDemo {
    public static void main(String[] args) {
        Varargs varargs = new Varargs();
        
        // ①
        varargs.test("abc");
        
        // ②
        varargs.test("bcd", "e", "f");

        // ③
        varargs.test("cde", new String[]{"g", "h"});
    }
}

class Varargs {

    public void test(String msg) {
        System.out.println("msg = " + msg);
    }

    public void test(String str, String... msg) {
        System.out.println("str = " + str);
        System.out.println("msg = " + Arrays.toString(msg));
    }
}

9 方法重载(overload)

9.1 概念

  • 方法重载:在同一类中,允许存在多个同名方法,只要它们的参数个数或参数类型不同即可。

9.2 特点

  • 和返回值类型无关,只看参数列表,且参数列表必须不同(参数个数或参数类型)。

  • 调用的时候,根据方法参数列表的不同来区别。

9.3 应用示例

  • 示例:
package com.github.overload;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-03 10:10
 */
public class Array {

    /**
     * 返回两个整数的和
     *
     * @param num1 num1
     * @param num2 num2
     * @return int
     */
    public int add(int num1, int num2) {
        return num1 + num2;
    }

    /**
     * 返回三个整数的和
     *
     * @param num1 num1
     * @param num2 num2
     * @param num3 num3
     * @return int
     */
    public int add(int num1, int num2, int num3) {
        return num1 + num2 + num3;
    }

    /**
     * 返回两个小数的和
     *
     * @param num1 num1
     * @param num2 num2
     * @return double
     */
    public double add(double num1, double num2) {
        return num1 + num2;
    }
}

9.4 方法重载的好处

  • 使用方法重载,可以为编程带来方便:让方法的调用者,在调用方法的时候,不需要为了相似的功能去查阅文档,查找各种不同的方法名,降低学习成本,提高开发效率。

  • 例如:System.out.println()方法就是典型的方法重载,其内部声明如下

public void print(boolean b){}
public void print(char c){}
public void print(int i){}
public void print(long l){}
public void print(float f){}
public void print(double d){}
 public void print(char s[]){}
public void print(String s){}
public void print(Object obj){}
public void println(){}
public void println(boolean x){}
public void println(char x){}
public void println(int x){}
public void println(long x){}
public void println(float x){}
public void println(double x){}
public void println(char x[]){}
public void println(String x){}
public void println(Object x){}

9.5 练习

9.5.1 练习1

  • 编写程序,定义三个重载方法并调用,方法名为mOverload

  • ① 三个方法分别接收一个int参数、两个int参数和一个String参数,分别执行平方运算并输出结果、乘法运算输出结果、以及输出字符串。

  • ② 在主类的main方法中分别用参数区别调用三个方法。

  • 示例:

package com.github.overload.exec;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-03 10:49
 */
public class OverloadDemo1 {

    /**
     * 执行平方运算并输出结果
     *
     * @param num
     */
    public void mOverload(int num) {
        System.out.println((int) Math.pow(num, 2));
    }

    /**
     * 乘法运算并输出结果
     *
     * @param num1
     * @param num2
     */
    public void mOverload(int num1, int num2) {
        System.out.println(num1 * num2);
    }

    /**
     * 输出字符串信息
     *
     * @param str str
     */
    public void mOverload(String str) {
        System.out.println(str);
    }

}
package com.github.overload.exec;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-03 10:54
 */
public class OverloadDemo1Test {
    public static void main(String[] args) {
        OverloadDemo1 overloadDemo1 = new OverloadDemo1();

        overloadDemo1.mOverload(1);

        overloadDemo1.mOverload(1, 2);

        overloadDemo1.mOverload("你好,世界");
    }
}

9.5.2 练习2

  • 定义三个重载方法max(),第一个方法求两个int值中的最大值,第二个方法求两个double值中的最大值,第三个方法求三个double值中的最大值,并分别调用这三个方法。

  • 示例:

package com.github.overload.exec;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-03 10:57
 */
public class ArrayDemo {

    /**
     * 求两个int值的最大值
     *
     * @param num1 num1
     * @param num2 num2
     * @return int
     */
    public int max(int num1, int num2) {
        return num1 > num2 ? num1 : num2;
    }

    /**
     * 求两个double值的最大值
     *
     * @param num1 num1
     * @param num2 num2
     * @return double
     */
    public double max(double num1, double num2) {
        return num1 > num2 ? num1 : num2;
    }


    /**
     * 求三个double值的最大值
     *
     * @param num1 num1
     * @param num2 num2
     * @param num3 num3
     * @return double
     */
    public double max(double num1, double num2, double num3) {
        return num1 > num2 ? (num1 > num3 ? num1 : num3) : (num2 > num3 ? num2 : num3);
    }
}
package com.github.overload.exec;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-03 11:08
 */
public class ArrayDemoTest {
    public static void main(String[] args) {
        ArrayDemo demo = new ArrayDemo();
        int max = demo.max(1, 2);
        System.out.println("max = " + max);
        double max1 = demo.max(1.1, 2.2);
        System.out.println("max1 = " + max1);
        double max2 = demo.max(1.1, 1.2, 1.3);
        System.out.println("max2 = " + max2);
    }
}

10 方法的值传递机制

10.1 概述

  • 方法,必须由其所在类或对象调用才有意义。如果方法有参数:

    • 形参:方法声明时的参数。
    • 实参:方法调用的时候实际传递给形参的参数值。
  • 那么,Java中的实参值是如何传入方法的?

  • Java里面方法的参数传递方式只有一种:值传递。即将实际参数值的副本传入方法内,而参数本身不受影响:

    • 形参是基本数据类型,那么实参将基本数据类型变量的”数据值“传递给形参。
    • 形参是引用数据类型,那么实参将基本数据类型变量的”地址值“传递给形参。

10.2 基本数据类型的参数传递

  • 示例:
package com.demo1;

/**
 * 基本数据类型的参数传递
 *
 * @author open1024
 * @version 1.0
 * @since 2021-09-03 14:53
 */
public class TransferParameters {
    public static void main(String[] args) {
        int x = 5;
        System.out.println("修改之前:x=" + x);
        change(x);
        System.out.println("修改之前:x=" + x);
    }

    public static void change(int x) {
        System.out.println("change修改之前:x=" + x);
        x = 3;
        System.out.println("change修改之后:x=" + x);
    }
}

img

10.3 引用数据类型的参数传递

  • 示例:
package com.demo2;

/**
 * 引用数据类型的参数传递
 *
 * @author open1024
 * @version 1.0
 * @since 2021-09-03 14:57
 */
public class TransferParameters {
    public static void main(String[] args) {
        Person p = new Person();
        p.age = 5;
        System.out.println("修改之前的age=" + p.age); // 5
        change(p);
        System.out.println("修改之后的age=" + p.age); // 3
    }

    public static void change(Person p) {
        System.out.println("change: 修改之前的age=" + p.age); // 5
        p.age = 3;
        System.out.println("change: 修改之后的age=" + p.age); // 3
    }
}

class Person {
    int age;
}

img

11 递归

11.1 概述

  • 递归:一个方法体内调用它自身。

  • 方法递归包含了一种隐式的循环,它会重复的执行某段代码,但这种重复执行无需循环控制。

  • 递归一定要向已知方向递归,否则这种递归就会变成无穷递归,类似于死循环。

注意:递归需要慎用,一旦递归链过长,会引起内存溢出。

11.2 练习

11.2.1 练习1

  • 计算 1 ~ 100 之间的自然数的和。

  • 示例:

package com.recursion;

/**
 * 计算 1 ~ 100 之间的自然数的和
 *
 * @author open1024
 * @version 1.0
 * @since 2021-09-03 15:52
 */
public class Demo1 {
    public static void main(String[] args) {
        int sum = sum(100);
        System.out.println("sum = " + sum);
    }

    /**
     * 递归 计算 1 ~ 100 之间的自然数的和
     *
     * @param n 自然数
     * @return int 和
     */
    public static int sum(int n) {
        if (n == 1) {
            return 1;
        }
        return n + sum(n - 1);
    }
}

11.2.2 练习2

  • 使用递归求阶乘n!的算法。

  • 示例:

package com.recursion;

/**
 * 使用递归求阶乘`n!`的算法。 n!= n * (n-1)*...*2*1
 * 
 * @author open1024
 * @version 1.0
 * @since 2021-09-03 20:17
 */
public class Demo2 {
    public static void main(String[] args) {
        int result = factorial(5);
        System.out.println("result = " + result);
    }

    /**
     * 递归求阶乘`n!`的算法
     * 
     * @param n
     * @return
     */
    public static int factorial(int n) {
        if (n == 1) {
            return 1;
        }
        return n * factorial(n - 1);
    }
}

11.2.3 练习3

  • 已知有一个数列:f(0) = 1,f(1) = 4,f(n+2)=2*f(n+1) + f(n),其中n是大于0 的整数,求f(10)的值。

  • 思路:x=n+2 => f(x)=2*f(n-1)+f(n-2)

  • 示例:

package com.recursion;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-03 20:22
 */
public class Demo3 {

    public static void main(String[] args) {
        int result = recursion(0);
        System.out.println("result = " + result);
        result = recursion(1);
        System.out.println("result = " + result);
        result = recursion(2);
        System.out.println("result = " + result);
        result = recursion(10);
        System.out.println("result = " + result);
    }

    public static int recursion(int n) {
        if (n == 0) {
            return 1;
        }
        if (n == 1) {
            return 4;
        }
        return 2 * recursion(n - 1) + recursion(n - 2);
    }
}

11.2.4 练习4

  • 输入一个数据n,计算斐波那契数列(Fibonacci)的第n个值。

  • 1 1 2 3 5 8 13 21 34 55。

  • 规律:一个数等于前两个数之和。

  • 要求:计算斐波那契数列(Fibonacci)的第n个值,并将整个数列打印出来。

  • 示例:

package com.recursion;

/**
 * 斐波那契数列
 *
 * @author open1024
 * @version 1.0
 * @since 2021-09-03 20:34
 */
public class Demo4 {
    public static void main(String[] args) {
        int result = fibonacci(1);
        System.out.println("result = " + result);
        result = fibonacci(2);
        System.out.println("result = " + result);
        result = fibonacci(3);
        System.out.println("result = " + result);
        result = fibonacci(10);
        System.out.println("result = " + result);
    }

    public static int fibonacci(int n) {
        if (n == 1) {
            return 1;
        }
        if (n == 2) {
            return 1;
        }

        return fibonacci(n - 1) + fibonacci(n - 2);
    }
}

12 对象数组

12.1 概述

  • 数组是用来存储一组数据的容器,一组基本数据类型的数据可以用数组装,那么一组对象也可以使用数组装,换言之,数组中的元素既可以是基本数据类型,也可以是引用数据类型。

  • 当数组中的元素是引用数据类型的时候,我们就称之为对象数组。

注意:对象数组,首先要创建数组对象本身,即确定数组的长度,然后再创建每一个元素对象,如果不创建,数组的元素的默认值就是null,很容易出现空指针异常NullPointerException

  • 示例:
package com.github.array;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-02 21:32
 */
public class Person {
    /**
     * 名称
     */
    String name;

    /**
     * 年龄
     */
    int age;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + this.name + '\'' +
                ", age=" + this.age +
                '}';
    }
}
package com.github.array;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-02 21:32
 */
public class PersonTest {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name = "李白";
        p1.age = 18;

        Person p2 = new Person();
        p2.name = "杜甫";
        p2.age = 20;

        Person[] arr = new Person[]{p1, p2};

        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}

12.2 练习

  • 定义类Student,包含三个属性:学号number(int)、年级grade(int)、成绩score(int)。创建20个学生对象,学号从1到20,年级和成绩由随机数确定。

  • 问题一:打印出3年级(grade为3)的学生信息。

  • 问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息。

提示:

  • 生成随机数:Math.random(),返回值为boolean类型。

  • 冒泡排序:

img

  • 算法思路:

    • 内层循环(j):
      • 执行范围:0 ~ (数组长度 - i - 1)
      • 当前元素和下一个元素进行比较。
      • 如果当前元素比下一个元素大,那么就交换两个元素。
      • 内层循环执行完毕,会将最大的元素移动到数组的末尾。
    • 外层循环(i):
      • 执行范围:0 ~ (数组长度-1)
      • 每执行一次,就将当前范围内的最大元素移动到当前范围内的末尾。
  • 示例:

package com.github.exec.demo3;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-02 21:41
 */
public class Student {
    /**
     * 学号
     */
    int number;

    /**
     * 年级
     */
    int grade;

    /**
     * 分数
     */
    int score;

    @Override
    public String toString() {
        return "Student{" +
                "number=" + this.number +
                ", grade=" + this.grade +
                ", score=" + this.score +
                '}';
    }
}
package com.github.exec.demo3;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-02 21:41
 */
public class StudentTest {
    public static void main(String[] args) {
        // 创建Student对象数组,长度为20
        Student[] studentArr = new Student[20];
        // 遍历对象数组,创建20个Student对象,并存入到数组中
        for (int i = 0; i < studentArr.length; i++) {
            Student student = new Student();
            // 给Student对象设置学号number
            student.number = i + 1;
            // 给Student对象设置年龄grade
            student.grade = (int) (Math.random() * 10) + 1;
            // 给Student对象设置分数score
            student.score = (int) (Math.random() * 100);
            // 框Student对象存入到数组中
            studentArr[i] = student;
        }

        // 打印出3年级(grade为3)的学生信息
        for (int i = 0; i < studentArr.length; i++) {
            Student student = studentArr[i];
            if (student.grade == 3) {
                System.out.println("3年级(grade为3)的学生信息 = " + student);
            }
        }

        // 使用冒泡排序按学生成绩排序,并遍历所有学生信息
        // 外层循环控制冒泡的次数
        for (int i = 0; i < studentArr.length; i++) {
            // 内层循环控制当前元素和下一个元素之间的大小
            // -i 是因为已经排好序的元素不需要再动
            // -1 是因为避免j+1找下一个元素的时候数组越界
            for (int j = 0; j < (studentArr.length - i - 1); j++) {
                if (studentArr[j].score > studentArr[j + 1].score) {
                    Student temp = studentArr[j];
                    studentArr[j] = studentArr[j + 1];
                    studentArr[j + 1] = temp;
                }
            }
        }

        System.out.println("冒泡排序后的数组");

        // 输出数组
        for (Student student : studentArr) {
            System.out.println(student);
        }

    }
}

13 OOP特征之一:封装

13.1 概述

  • 程序设计追求“高内聚、低耦合”

    • 高内聚:类的内部数据操作细节自己完成,不允许外部干涉。
    • 低耦合:仅对外暴露少量的方法用于使用。
  • 封装:隐藏对象内部的复杂性,只对外部公开简单的接口,便于外界调用,从而提高系统的可扩展性、可维护性。换言之,将该隐藏的隐藏起来,该暴露的暴露出来

13.2 为什么要进行封装?

  • 使用者对类内部定义的属性(对象的成员变量)的直接操作会导致数据的错误、混乱或安全性问题。

  • 示例:

package com.encapsulation.demo1;

/**
 * 动物
 *
 * @author open1024
 * @version 1.0
 * @since 2021-09-03 22:13
 */
public class Animal {

    /**
     * 腿
     */
    public int legs;

    /**
     * 吃饭的方法
     */
    public void eat() {
        System.out.println("吃饭");
    }

    /**
     * 移动的方法
     */
    public void move() {
        System.out.println("移动");
    }
}
package com.encapsulation.demo1;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-03 22:14
 */
public class AnimalTest {
    public static void main(String[] args) {
        Animal animal = new Animal();
        // 如果这边赋值的是-100?,不合法啊
        // 应该将legs属性保护起来,防止乱用
        animal.legs = 4;
        System.out.println(animal.legs); // 4
        animal.eat();
        animal.move();
    }
}

13.3 Java中的封装

  • Java中通过将数据声明为私有的(private),再提供公共的(public)方法:setXxx()getXxx()来实现对该属性的操作,以实现以下的目的:

  • ① 隐藏一个类中不需要对外提供的实现细节。

  • ② 使用者只能通过事先定制好的方法来访问数据,可以方便的加入控制逻辑,限制对属性的不合理操作。

  • ③ 便于修改,增强代码的维护性。

  • 示例:

package com.encapsulation.demo2;

/**
 * 动物
 *
 * @author open1024
 * @version 1.0
 * @since 2021-09-03 22:13
 */
public class Animal {

    /**
     * 腿 将属性进行私有化
     */
    private int legs;

    public int getLegs() {
        return this.legs;
    }

    public void setLegs(int legs) {
        if (legs != 0 && legs != 2 && legs != 3) {
            System.out.println("动物的腿一般为0、2、4");
            return;
        }
        this.legs = legs;
    }

    /**
     * 吃饭的方法
     */
    public void eat() {
        System.out.println("吃饭");
    }

    /**
     * 移动的方法
     */
    public void move() {
        System.out.println("移动");
    }
}
package com.encapsulation.demo2;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-03 22:14
 */
public class AnimalTest {
    public static void main(String[] args) {
        Animal animal = new Animal();
        // 非法 动物的腿一般为0、2、4
        animal.setLegs(-100);
        System.out.println(animal.getLegs());
        animal.eat();
        animal.move();
    }
}

13.4 四种访问权限修饰符

  • Java权限修饰符public、protected、缺省、private置于类的成员定义前,用来限定对象对该类成员的访问权限。
修饰符类内部同一个包中不同包的子类同一个工程
private
缺省
protected
public
  • 对于class的权限修饰符只可以用public和缺省:

    • public类可以在任意地方被访问。
    • 缺省类只可以被同一个包内部的类访问。

13.5 练习

  • 创建程序,在其中定义两个类:Person和PersonTest。定义如下:用setAge()设置人的合法年龄(0~130),用getAge()返回人的年龄。用PersonTest类中实例化Person类的对象b,调用setAge()和getAge()方法,体会Java的封装性。

  • 示例:

package com.encapsulation.demo3;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-04 05:45
 */
public class Person {
    /**
     * 年龄
     */
    private int age;

    /**
     * 获取年龄
     *
     * @return int
     */
    public int getAge() {
        return this.age;
    }

    /**
     * 设定年龄
     *
     * @param age
     *            年龄
     */
    public void setAge(int age) {
        if (age < 0 || age > 130) {
            System.out.println("输入不合法");
            return;
        }
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" + "age=" + this.age + '}';
    }
}
package com.encapsulation.demo3;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-04 05:47
 */
public class PersonTest {
    public static void main(String[] args) {
        Person person = new Person();
        person.setAge(50);
        System.out.println(person.getAge());
        System.out.println(person);
    }
}

14 类的成员之三:构造器(构造方法)

14.1 概述

  • 语法:
访问修饰符 类名 (参数列表){
    // 初始化语句;
}
  • 构造器的组成:

img

  • 示例:
package com.constructor;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-04 06:23
 */
public class Animal {
    /**
     * 腿
     */
    private int legs;

    public Animal() {
        this.legs = 4;
    }

    public Animal(int legs) {
        this.legs = legs;
    }

    public int getLegs() {
        return this.legs;
    }

    public void setLegs(int legs) {
        this.legs = legs;
    }
}
package com.constructor;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-04 06:25
 */
public class AnimalTest {
    public static void main(String[] args) {
        // 创建Animal的实例,调用无参构造器,将legs初始化为4
        Animal animal = new Animal();
        System.out.println("animal.getLegs() = " + animal.getLegs());
    }
}

14.2 构造器的作用

  • ① 创建对象。

  • ② 给对象进行初始化。

  • 示例:

Order o = new Order();
  • 示例:
Person p = new Person("张三",15);

14.3 构造器的特征

  • ① 构造器具有和类相同的名称。

  • ② 构造器不声明返回值类型(和声明void不同)。

  • ③ 构造器不能被static、final、synchronized、abstract、native修饰,不能有return语句返回值。

14.4 构造器的分类

  • 根据参数的不同,构造器可以分为如下两类:

  • ① 隐式无参构造器(系统默认提供)。

  • 显式定义一个或多个构造器(无参、有参)。

14.5 使用构造器的注意事项

  • ① Java语言中,每个类都至少有一个构造器。

  • ② 默认构造器的修饰符和所属类的修饰符一致。

  • ③ 一旦显式的定义了构造器,则系统不再提供默认构造器。

  • ④ 一个类可以创建多个重载的构造器。

  • ⑤ 父类的构造器不可以被子类继承。

14.6 构造器的重载

  • 构造器一般用来创建对象的同时初始化对象,如:
class Person{
    String name;
    int age;
    
    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }
}
  • 构造器重载使得创建对象更加灵活,可以很方便的创建不同的对象,如:
class Person{
    String name;
    int age;
    private Date birthday;
    
    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }
    
    public Person(String name,int age,Date birthday){
        this(name,age);
        this.birthday = birthday;
    }
    ...
}
  • 构造器重载,参数列表必须不同。

14.7 练习

  • ① 定义Student类,有4个属性:

    • String name;
    • int age;
    • String school;
    • String major;
  • ② 定义Student类的3个构造器:

public Student(String name,int age){}
public Student(String name,int age,String school){}
public Student(String name,int age,String school,String major){}
  • ③ 在main方法中分别调用不同的构造器创建不同的对象,并输出其属性值。

  • 示例:

package com.constructor.demo2;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-04 06:49
 */
public class Student {
    /**
     * 名称
     */
    private String name;

    /**
     * 年龄
     */
    private int age;

    /**
     * 学校
     */
    private String school;

    /**
     * 专业
     */
    private String major;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Student(String name, int age, String school) {
        this(name, age);
        this.school = school;
    }

    public Student(String name, int age, String school, String major) {
        this(name, age, school);
        this.major = major;
    }

    @Override
    public String toString() {
        return "Student{" + "name='" + this.name + '\'' + ", age=" + this.age + ", school='" + this.school + '\''
            + ", major='" + this.major + '\'' + '}';
    }
}
package com.constructor.demo2;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-04 06:51
 */
public class StudentTest {
    public static void main(String[] args) {
        Student s1 = new Student("张三", 25);
        System.out.println("s1 = " + s1);

        Student s2 = new Student("李四", 18, "中国社会大学");
        System.out.println("s2 = " + s2);

        Student s3 = new Student("王五", 17, "中国社会大学", "萌萌哒专业");
        System.out.println("s3 = " + s3);
    }
}

14.8 属性赋值总结

  • 到目前为止,有很多位置都可以对类的属性进行赋值,现总结这几个位置,并指明赋值的先后顺序。

  • 赋值的位置:

    • ① 默认初始化。
    • ② 显示初始化。
    • ③ 构造器中初始化。
    • ④ 通过“对象.属性“”对象.方法“的方式赋值。
  • 赋值的先后顺序:① → ② → ③ → ④

15 JavaBean

15.1 概述

  • JavaBean是一种Java语言编写的可重用组件。

  • 所谓JavaBean,是指符合如下标准的Java类:

    • 类是公共的
    • 有一个无参的公共的构造器
    • 有属性,且有对应的getter和setter方法
  • 用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用Java代码创建的对象进行打包,并且其他的开发者可以通过内部的Jsp页面、Servle、其他JavaBean或者应用来使用这些对象。

  • 用户可以认为JavaBean提供了一种随时随地的复制和粘贴的功能,而不用关系任何改变。

15.2 应用示例

  • 示例:
package com.javabean;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-04 08:17
 */
public class Person {
    /**
     * 名称
     */
    private String name;

    /**
     * 年龄
     */
    private int age;

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

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

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

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

    @Override
    public String toString() {
        return "Person{" + "name='" + this.name + '\'' + ", age=" + this.age + '}';
    }
}

16 关键字 this

16.1 概述

  • 在Java中,this关键字比较难理解,它的作用和其词义比较接近:

    • ① 它在方法内部使用,表示这个方法所属对象的引用。
    • ② 它在构造器内部使用,表示该构造器正在初始化的对象。
  • this可以调用类的属性、方法和构造器。

  • 当在方法内部需要用到调用该方法的对象是,就需要使用this,具体来说,就是用this来区分属性局部变量

16.2 this调用属性和方法

  • ① 在任意方法或构造器内,如果使用当前类的成员变量或成员方法可以在前面添加this,增强程序的阅读性。不过,通常情况下,我们都习惯省略this。

  • ② 当形参和成员变量同名的时候,如果在方法内部或构造器内部需要使用成员变量,必须添加this来表明该变量是类的成员变量。

  • ③ 使用this访问属性和方法时,如果在本类中没有找到,会从父类中查找。

  • 示例:

package top.open1024.demo;

/**
 * @author open1024
 * @version 1.0
 * @since 2021-09-04 09:11
 */
public class Person {

    /**
     * 姓名
     */
    private final String name;

    /**
     * 年龄
     */
    private final int age;

    /**
     * 构造方法
     * 
     * @param name 姓名
     * @param age 年龄
     */
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取信息
     */
    public void getInfo() {
        System.out.println("姓名:" + this.name);
        this.speak();
    }

    /**
     * 说话
     */
    private void speak() {
        System.out.println("年龄:" + this.age);
    }
}
0

评论区