Java 方法

Java 方法

方法的返回值 是指我们获取到的某个方法体中的代码执行后产生的结果!(前提是该方法可能产生结果)。返回值的作用是接收出结果,使得它可以用于其他的操作!

按照方法的返回值和参数类型将方法分为以下几种:

  • 无参数无返回值的方法
  • 有参数无返回值的方法
  • 有返回值无参数的方法
  • 有返回值有参数的方法

静态方法和实例方法有何不同?

1)调用方式

在外部调用静态方法时,可以使用类名.方法名的方式,也可以使用对象.方法名的方式[1],而实例方法只有对象.方法名这种方式。也就是说,调用静态方法可以无需创建对象

2)访问类成员是否存在限制

  • 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),不允许访问实例成员(即实例成员变量和实例方法)
  • 实例方法不存在以上这个限制。

[1] 不建议使用 对象.方法名 的方式来调用静态方法,容易混淆。静态方法不属于类的某个对象而是属于这个类。

静态方法为何不能调用非静态成员?

  • 静态方法是属于类的,在类加载的时候就会分配内存,可以通过类名直接访问。而非静态成员属于实例对象,只有在对象实例化之后才存在,需要通过类的实例对象去访问。
  • 在类的非静态成员不存在的时候静态方法就已经存在了,此时调用在内存中还不存在的非静态成员,属于非法操作。

方法重载和重写

重载:java 允许同一个类中,多个同名方法的存在,但要求形参列表不一致。这样,同样的一个方法能够根据输入数据的不同,做出不同的处理。(好处:减轻了起名、记名的麻烦)

重写:当子类继承自父类的相同方法,输入数据一样,但要做出有别于父类的响应时,你就要覆盖父类方法。

方法重载(Overload)

如果多个方法(比如 StringBuilder 的构造方法)有相同的名字不同的参数, 便产生了重载

规则:

  1. 方法名:必须相同
  2. 形参列表:必须不同(形参类型 或 个数 或 顺序,至少一种不一样,参数名无所谓)
  3. 返回类型:无要求,不是方法重载的必要条件。
1
2
StringBuilder sb = new StringBuilder();
StringBuilder sb2 = new StringBuilder("HelloWorld");

编译器必须挑选出具体执行哪个方法,它通过用各个方法给出的参数类型与特定方法调用所使用的值类型进行匹配来挑选出相应的方法。 如果编译器找不到匹配的参数, 就会产生编译时错误, 因为根本不存在匹配, 或者没有一个比其他的更好(这个过程被称为**重载解析(overloading resolution)**)。Java 允许重载任何方法, 而不只是构造器方法。

综上:重载就是同一个类中多个同名方法根据不同的传参来执行不同的逻辑处理。

方法重写(Override)

重写发生在运行期,是子类对父类的允许访问的方法的实现过程进行重新编写。

要求:

  1. 方法名、参数列表必须相同。
  2. 子类方法返回值类型应比父类方法返回值类型更小或相等。
  3. 抛出的异常范围小于等于父类。
  4. 访问修饰符范围大于等于父类。
  5. 如果父类方法访问修饰符为 private/final/static 则子类就不能重写该方法,但是被static修饰的方法能够被再次声明。
  6. 构造方法无法被重写。

综上:重写就是子类对父类方法的重新改造,外部样子不能改变,内部逻辑可以改变。

总结

区别点重载方法重写方法
发生范围同一个类子类
参数列表必须修改一定不能修改
返回类型可修改子类方法返回值类型应比父类方法返回值类型更小或相等
异常可修改子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等;
访问修饰符可修改一定不能做更严格的限制(可以降低限制)
发生阶段编译期运行期

方法的重写要遵循“两同两小一大”(以下内容摘录自《疯狂 Java 讲义》,issue#892open in new window ):

  • “两同”即方法名相同、形参列表相同;
  • “两小”指的是子类方法返回值类型应比父类方法返回值类型更小或相等,子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等;
  • “一大”指的是子类方法的访问权限应比父类方法的访问权限更大或相等。

⚠关于 重写的返回值类型 这里需要额外多说明一下,上面的表述不太清晰准确:如果方法的返回类型是 void 和基本数据类型,则返回值重写时不可修改。但是如果方法的返回值是引用类型,重写时是可以返回该引用类型的子类的。

什么是可变长参数?

从 Java5 开始,Java 支持定义可变长参数,所谓可变长参数就是允许在调用方法时传入不定长度的参数

比如下面这个方法就可以接受 0 个或者多个参数。

1
2
3
public static void method1(String... args) {
//......
}

另外,可变参数只能作为函数的最后一个参数,但其前面可以有也可以没有任何其他参数。

1
2
3
public static void method2(String arg1, String... args) {
//......
}

遇到方法重载的情况怎么办呢?会优先匹配固定参数还是可变参数的方法呢?

  • 答案是会优先匹配固定参数的方法,因为固定参数的方法匹配度更高。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class VariableLengthArgument {

public static void printVariable(String... args) {
for (String s : args) {
System.out.println(s);
}
}

public static void printVariable(String arg1, String arg2) {
System.out.println(arg1 + arg2);
}

public static void main(String[] args) {
printVariable("a", "b");
printVariable("a", "b", "c", "d");
}
}

输出:

1
2
3
4
5
ab
a
b
c
d

另外,Java 的可变参数编译后实际会被转换成一个****数组,我们看编译后生成的 class文件就可以看出来了。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class VariableLengthArgument {

public static void printVariable(String... args) {
String[] var1 = args;
int var2 = args.length;

for(int var3 = 0; var3 < var2; ++var3) {
String s = var1[var3];
System.out.println(s);
}
}
// ......
}

方法递归

方法递归调用内存分析

方法递归调用

重要规则

  1. 执行一个方法时,就创建一个新的受保护的独立空间(栈空间)
  2. 方法的局部变量是独立的,不会相互影响
  3. 如果方法中使用的是引用类型变量(比如数组、对象),就会共享该引用类型的数据
  4. 递归必须向退出递归的条件逼近,否则将无限递归,出现StackOverflowError,死龟了
  5. 当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用就将结果返回给谁,同时当方法执行完毕或返回时,该方法也就执行完毕。
1
2
3
4
5
6
7
8
//一般形式
func(mode){
if(endCondition) { //递归出口
end;
} else {
func(mode_small) //调用本身,递归
}
}

经典算法题:八皇后问题

作用域

在 java 编程中,主要的变量就是属性(成员变量)局部变量

java作用域的规则

属性和局部变量可以重名,访问时遵循就近原则

全局变量(属性\成员变量)

  1. 作用域为整个整体;
  2. 可以不赋值,直接使用(因为有默认值);
  3. 生命周期较长,伴随着对象创建而创建、销毁而销毁;
  4. 可以被本类使用,或其他类使用(通过对象调用);
  5. 可以加修饰符。

局部变量

  1. 一般是指在成员方法内定义的变量。(后面讲的{代码块}中的变量也是局部变量),作用域为定义它的代码块中;
  2. 必须赋值后,才能使用(因为没有默认值);
  3. 生命周期较短,伴随它的代码块的执行而创建、结束而销毁,即在一次方法调用过程中;
  4. 只能在本类对应的方法中使用;
  5. 不可以加修饰符。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class ScopeTest {
public static void main(String[] args) {
Person p1 = new Person();
T t1 = new T();

t1.test(); // 第1种跨类访问对象属性的方式

t1.test2(p1); // 第2种跨类访问对象属性的方式

}
}

class T {

public void test() {
Person p = new Person();
System.out.println(p.name);
}

public void test2(Person p) {
System.out.println(p.name);
}

}

class Person {
String name = "kobe";
}

构造方法/构造器

构造方法,是类的一种特殊方法,主要作用是完成对新对象的初始化

基本语法

1
访问修饰符 方法名(形参列表) { 方法体; }

规则

  1. 没有返回值(不用也不能写void)

  2. 方法名 和 类名 相同

  3. 参数列表 和 成员方法 一样的规则

  4. 构造器是完成对象的初始化,并不是创建对象

  5. 创建对象时,系统会自动调用该类的构造器

  6. 如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器),比如 Person(){}。(可使用javap反编译看看)

    1. 默认无参构造器
  7. 一旦自定义了构造器,默认的构造器就被覆盖,就不能再使用默认的无参构造器,除非显式定义一下,即:Dog(){}

    1. 有参构造器

参考


Java 方法
https://blog-21n.pages.dev/2022/06/07/Java-方法/
作者
Neo
发布于
2022年6月7日
许可协议