【西行 - 猴王保唐僧 】 面向对象之类和方法

  • J3 - 白起
  • Java(Java基础 # 对象 # 方法 # 笔记)

1、类和对象

Java 是面向对象的程序设计语言,雷士面向对象的重要内容,我们可以吧类当成一种自定义类型,可以使用类来定义变量,这种类型的变量统称为引用变量。

所有类都是引用变量类型。

1.1 定义类

Java 语言类的简单语法如下:

[修饰符] class [类名] {
    零到多个构造器定义...
    零到多个Field...
    零到多个方法...
}

语法格式中,修饰符可以是 public 、final 、abstract 、或者完全省略这三个修饰符,类名只要是一个合法的标识符即可。

类中可以包含三种成员:

  • 构造器:构造器用于构造该类的实例,Java 语言通过 new 关键字来调用构造器,从而返回该类的实例。
  • Field:Field 用于定义该类或该类的实例所包含的状态数据。
  • 方法:方法用于定义该类过该类的实例的行为特征或者功能实现。

这三种成员可以定义零个或多个,如果三种成员都只定义零个,就是定义了一个空类,这没有太大的实际意义。

类中各成员之间的定义顺序没有任何影响,各成员之间可以相互调用,但是,static 修饰的成员不能访问没有 static 修饰的成员。

构造器是一个类创建对象的根本途径,如果一个类没有构造器,这个类通常无法创建实例。因此 Java 语言提供了一个功能:如果程序员没有为一个类编写构造器,则系统会为该类提供一个默认的构造器。一旦程序员为一个类提供了构造器,系统将不再为该类提供构造器。

定义 Field 的语法格式如下:

[修饰符] Field类型 Field[= 默认值];

格式说明:

  • 修饰符:修饰符可以省略,也可以是 public、protected、private、static、final,其中 public、protected、private 三个最多只能出现其中之一,可以与 static、final 组合起来修饰 Field。
  • Field 类型:Field 类型可以是 Java 语言允许的任何数据类型,包括基本类型和现在介绍的引用类型。
  • Field 名:Field 名只要符合标识符即可,但也尽量通俗易懂,便于理解。
  • 默认值:定义 Field 可以指定一个可选的默认值。

方法定义格式如下:

[修饰符] 方法返回类型 方法返回名(形参列表) {
    // 由零条到多条可执行性语句组成的方法体
}

格式说明:

  • 修饰符:修饰符可以省略,也可以是 public、protected、private、static、final、abstract,其中 public、protected、private 三个最多只能出现其中之一;abstract 和 final 最多只能出现其中之一,他们可以与 static 组和起来休息方法。
  • 方法返回值类型:返回值类型可以是 Java 语言允许的任何数据类型,包括基本类型和引用类型;如果声明了方法返回值类型,则方法体内必须有一个有效的 return 语句,该语句返回一个变量或一个表达式,这个变量或者表达式的类型必须与此处声明的类型匹配。另外,如果方法没有返回值,则必须使用 void 来声明没有返回值。
  • 方法名:符合 Java 标识符即可,但尽量通俗易懂。
  • 形参列表:形参列表用于定义该方法可以接受的参数,新参列表由零组到多组 ”参数类型 形参名“ 组合而成,多组参数之间以英文逗号隔开,形参类型和形参名之间以英文空格隔开。

static 是要给特殊的关键字,它可用于修饰方法、Field 等成员。static 修饰的成员表明它属于这个类本身,而不是属于该类的单个实例,反之则属于单个实例,而不属于该类。

构造器是一个特殊的方法,定义构造器语法格式与定义方法的语法格式很像,定义构造器的语法格式如下:

[修饰符] 构造器名(形参列表) {
	// 由零条到多条可执行性语句组成的构造体
}

格式说明:

  • 修饰符:修饰符可以省略,也可以是 public、protected、private 其中之一。
  • 构造器名:构造器名必须和类名相同。
  • 形参列表:和定义方法形参列表的格式完全相同。

值得注意的是,构造器不能定义返回类型,也不能使用 void 定义构造器没有返回值。

1.2 对象的产生和使用

创建对象的根本途径是构造器,通过 new 关键字来调用某个类的构造器即可创建这个类的实例。

创建对象示例:

// 定义 girlFriend 变量,同时为该变量赋值
GirlFriend girlFriend = new GirlFriend();

如果访问权限允许,类里定义的方法和 Field 都可以通过类或实例来调用。

类或实例访问方法或 Field 的语法是:类.Field | 方法,或者实例.Field | 方法,这种方式中,类或实例是主调者,用于方法该类或该类实例的指定 Field 或方法。

static 修饰的方法和 Field ,既可以通过类来调用,也可通过实例来调用,反之则只能通过实例来调用。

下面代码通过 GirlFriend 实例来调用 GirlFriend 的 field 和方法。

// 创建对象
GirlFriend girlFriend = new GirlFriend();
// 调用 girlFriend 的 name Field,直接为该 Field 赋值
girlFriend.name = "刘亦菲";
// 输出 girlFriend 的 name Field 的值,将输出 刘亦菲
System.out.println(girlFriend.name);
// 调用 girlFriend 的 toString 方法
System.out.println(girlFriend.toString());

2、方法详解

Java 里面的方法不能独立存在,所有的方法都必须定义在类里。方法在逻辑上要么属于类,要么属于对象。

因此,如果需要定义方法,则只能在类体内定义,不能独立定义一个方法。一旦将一个方法定义在某个类的类体内,如果这个方法使用了 static 修饰,则这个方法属于这个类,否则这个方法属于这个类实例。

因为 Java 里的方法不能独立存在,它必须属于一个类或一个对象,因此方法不能像函数那样被独立执行,执行方法时必须使用类或对象来作为调用者,既所有方法都必须使用功能 “类.方法” 或 “对象.方法” 的形式来调用。至于同一个类中,方法之间的互相调用,如果没有被 static 修饰则默认用 this 调用反之则用类调用。也就是说,表面上看起来某些方法可以被独立执行,但实际上还是使用 this 或者类来作为调用者。

使用 static 修饰的方法既可以使用类作为调用者来调用,也可以使用功能对象作为调用者来调用。但不论使用哪种方式最终的结果是一定相同的,因为其实际还是类作为调用者。

未使用 static 修饰的方法属于类的实例,其只能使用对象作为调用者调用,而且使用不同的对象作为调用者得出的结果可能不相同。

2.1 方法的参数传递机制

如果声明方法时包含了形参声明,则调用方法时必须给这些形参指定参数值,调用方法时实际传给形参的参数值也被称为实参。

Java 中方法中的参数传递只有值传递。所谓值传递,就是将实际参数值得副本(复制品)传入方法内,而参数本省不会受到任何影响。

下面来看看方法参数传递的效果代码。

public class MethodsTest {

    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        swap(a, b);
        System.out.println("a 、b 交换值之后 a = " + a + ",b = " + b);

    }

    public static void swap(int a, int b) {
        // 交换 a 和 b 的值
        int temp = a;
        a = b;
        b = temp;

        System.out.println("swap 方法里 a 、b 交换值之后 a = " + a + ",b = " + b);
    }
}

运行结果:

swap 方法里 a 、b 交换值之后 a = 20,b = 10
a 、b 交换值之后 a = 10,b = 20

从结果上来看,main 方法里的变量 a 和 b 并不是 swap 方法里的 a 和 b,而是 main 方法里变量 a 和 b 的一个复制品。

main 方法及 swap 方法在栈中变换的示意图如下:

在这里插入图片描述

如图所示,在 main 方法中调用 swap 方法实际就是在栈中开辟一个空间,并重新产生两个变量 a 和 b 并将 main 方法中的 a、b 变量值赋值给 swap 内的变量。此时栈中存在 两个 a 变量、两个 b 变量,只是存在不同的方法区中(栈帧)。

在 swap 中进行值交换时,只是单纯的改变了 swap 方法区的变量,而 main 方法区的变量没有任何改动,所以就有了上面的打印结果。

前面看到的是基本类型参数传递,Java 对于引用类型的参数传递,一样采用的是值传递方式。

public class DataSwap{
    public int a;
    public int b;
}

public class MethodsTest {


    public static void main(String[] args) {
        // 创建引用类型对象
        DataSwap dataSwap = new DataSwap();
        // 赋值
        dataSwap.a = 10;
        dataSwap.b = 20;
        // 调用交换方法
        swap(dataSwap);
        System.out.println("a Field、b Field交换值之后 a = " + dataSwap.a + ",b = " + dataSwap.b);

    }

    public static void swap(DataSwap dw) {
        // 交换 a 和 b 的值
        int temp = dw.a;
        dw.a = dw.b;
        dw.b = temp;

        System.out.println("swap 方法里 a Field、b Field 交换值之后 a = " + dw.a + ",b = " + dw.b);
    }
}

运行结果:

swap 方法里 a Field、b Field 交换值之后 a = 20,b = 10
a Field、b Field交换值之后 a = 20,b = 10

从运行结果来看,在 swap 方法里,a、b 两个 Field 值被交换成功。不仅如此,main 方法里 swap 方法执行结束后,a、b 两个 Field 值也被交换了。这会让人造成一种错觉:调用 swap 方法时,传入 swap 方法的就是 DataSwap 对象本身,而不是它的复制品。但这只是一种错觉,因为 dataSwap 和 dw 变量指向同一个对象地址,其中一个变量改变地址所指向的内存值另一个同样跟着改变,内存示意图如下。

在这里插入图片描述

2.2 形参个数可变的方法

从 JDK1.5 之后,Java 允许定义形参个数可变的参数,从而允许位方法指定数量不确定的形参。

如果在定义方法时,在最后一个形参的类型后增加三点(…),则表明该形参可以接受多个参数值,多个参数值被当成数组传入。

下面看一个形参可变个数的方法代码。

public class MethodTest {

    public static void main(String[] args) {
        // 调用方法
        variable(1, "疯狂 Java 讲义", "Java 高并发编程", "深入 Java 虚拟机");
    }

    public static void variable(int a, String... books){
        // books 可以当成数组输出
        for (String book : books) {
            System.out.println(book);
        }
        // 输出整数值 a
        System.out.println(a);

    }
}

允许结果:

疯狂 Java 讲义
Java 高并发编程
深入 Java 虚拟机
10

从运行结果可以看出,下面两个方法前面效果完全一样。

// 以可变数形参来定义方法
public static void variable(int a, String... books);
// 下面采用数组形参定义方法
public static void variable(int a, String[] books);

上面两种形式在方法体内都可以把 books 当成数组处理,但区别是调用两个方法时存在差别,对于可变参数调用方法更加简洁,如下面代码所示。

variable(10, "疯狂 Java 讲义", "Java 高并发编程", "深入 Java 虚拟机");

采用数组形参来声明方法,调用共时则必须传给该形参一个数组,如下面代码所示。

// 调用 variable 方法时传入一个数组
variable(10, new String[]{"疯狂 Java 讲义", "Java 高并发编程", "深入 Java 虚拟机"});

对比两种调用方法代码,明显第一种形式更加简洁。

最后,数组形式的形参可以处于形参列表的任意位置,但个数可变的形参只能处于形参列表的最后,也就是所,一个方法中最多只能有一个长度可变的形参。

2.3 递归方法

一个方法体内调用共它自身,被称为方法递归。

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

例如计算 10!则使用递归方式实现代码如下。

public class FactorialTest {

    public static void main(String[] args) {
        // 调用递归方法,计算 10!
        System.out.println(factorial(10));
    }

    public static int factorial(int value) {
        // 结束条件,一定要有,value 变为 1 就结束
        if (value == 1) {
            return 1;
        }
        // 递归,计算
        return value * factorial(value - 1);
    }
}

对于 factorial(10),即等于 10 * factorial(10 - 1) ,其中 factorial(10 - 1) 又等于 9 * factorial(9 - 1)…依次类推,最终得到 factorial(1) 等于 1 ,即 1 是可计算的,然后一路反计算回去,就可以最终得到 factorial(10) 的值。

仔细看上面递归的过程,当一个方法不断地调用它本身时,必须在某一个时刻方法的返回值是一个确定的,即不再调用它本身,否则这种地柜就变成了无穷递归,类似于死循环。所以定义递归方法时有一条最重要的规定:递归一定要向已知方向递归

2.4 方法重载

Java 允许同一个类里定义多个同名方法,只要形参列表不同就行。

如果同一个类中包含了两个或两个以上方法的方法名相同,担形参列表不同,则被称为方法重载。

方法重载的要求是方法名相同,形参列表不同。至于方法的其他部分,如方法返回类型,修饰符都和方法重载没有任何关系。

public class OverloadTest {

    public static void main(String[] args) {
        OverloadTest overloadTest = new OverloadTest();
        // 调用 test 方法,没有带参数,所以运行无参方法
        overloadTest.test();
        // 调用 test 方法,传入一个参数,所以运行带一个参数的方法
        overloadTest.test("J3 关注一波不迷路");
    }


    public void test() {
        System.out.println("无参 test 方法");
    }

    public void test(String str) {
        System.out.println("带一个参数的 test 方法,参数 str = " + str);
    }
}

运行结果完全正常,虽然 test 方法的方法名相同,但因为它们的形参列表不同,所以系统可以正常区分出这两个方法。

那为什么方法的返回值不能用于区分重载?

因为对于 int f(){} 和 void f(){} 两个方法,如果调用 int result = f();,系统可以识别是调用返回类型是 int 的方法;但Java 调用方法时是可以忽略返回值的,如果采用如下方法来调用 f(); 则不能确定到底调用哪个方法。因此 Java 里不能使用功能方法返回值类型作为方法重载的依据。

好了,今天的内容到这里就结束了,关注我,我们下期见

查阅或参考资料:

《Java核心技术第10卷》

《疯狂 Java 讲义》

联系方式:

QQ:1491989462

微信:13207920596

做个好友,来个点赞之交。


  • 由于博主才疏学浅,难免会有纰漏,假如你发现了错误或偏见的地方,还望留言给我指出来,我会对其加以修正。

  • 如果你觉得文章还不错,你的转发、分享、点赞、留言就是对我最大的鼓励。

  • 感谢您的阅读,十分欢迎并感谢您的关注。

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

个人站点:J3

CSDN:J3 - 白起

掘金:J3-白起

知乎:J3-白起

这是一个技术一般,但热衷于分享;经验尚浅,但脸皮够厚;明明年轻有颜值,但非要靠才华吃饭的程序员。

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

J3code

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值