Java方法

手写笔记转换

方法的定义

1
2
3
修饰符 返回值类型 方法名(形参列表){
	// 方法体
}

修饰符

访问修饰符

Java 中的访问修饰符用于控制类、字段、方法和构造函数的访问权限。通过使用访问修饰符,可以实现封装,保护数据,并控制不同部分之间的访问范围。

public

定义:被 public 修饰的成员可以被外界访问

作用域:在一个Java源文件(.java文件)中只有一个类被声明为 public ,且这个源文件的文件名要与这个公共类的类名相同

public 修饰类成员时可以在所有类中被访问

在继承中 public 修饰的所有成员都被继承

protect

定义:受保护的,受该类所在包保护,只能被同一个包内的类或者不同包的子类访问

作用域:被 protect 修饰的成员可以被同一个包内的所有类访问,同时也会被该类的所有子类继承

default

定义:只对同一个包中的类友好,只能被同一个包中的类访问

作用域:同一个包中的所有类可以访问,被 default 修饰的成员只能被同一个包中的子类继承

private

定义:私有的,只能被同一个类中的成员访问

作用域:同一个类

表格整理如下

修饰符 当前类 同一包内 子类(不同包) 其他包
public
protected
default
private

访问修饰符的选择动机

  • public:适用于需要被外部类广泛访问的成员。过多使用 public 可能导致封装性降低。
  • protected:适用于需要在继承关系中使用的成员。它提供了比 public 更严格的访问控制,但允许子类访问。
  • default:适用于仅在同一包内使用的类和成员。适当使用可以隐藏实现细节,减少类之间的耦合。
  • private:适用于内部实现细节,确保类的内部数据和方法不会被外部直接访问。最严格的访问控制,保护类的封装性。

静态修饰符

static

static 修饰的成员是静态方法,静态方法属于整个类,非静态方法属于单个对象

静态方法的生命周期与类一样长,随着类的消亡而消亡

非静态方法的生命周期与对象一样长,随着对象的消亡而消亡

方法调用

  • main 方法作为程序入口先压入栈区
  • 执行 main 方法中的代码,遇到方法后,将该方法压入栈区,执行完毕后出栈

方法重载

方法重载(Overloading) :在同一个类中,允许有多个同名方法,只要它们的参数列表不同(参数个数、类型或顺序)。主要关注方法的签名变化,适用于在同一类中定义不同场景下的行为。

判定依据

  • 方法在同一个类中,且具有相同的方法名
  • 具有不同的形参列表(满足以下其中之一即可)
    • 形参个数不同
    • 形参类型不同
    • 形参顺序不同

方法的参数传递

在 Java 中,参数传递只有按值传递,不论是基本类型还是引用类型。

方法传递基本数据类型

具体类型见往期博客

基本数据类型的特点

基本数据类型在栈空间中存储的是真实的数据字面值,在堆区中不开辟空间

具体表现

方法中对参数的改变不会影响原始变量的值

示例代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public static void main(String [] args)
{
    int nums=100;
    change(nums); //change(100)
    System.out.println(nums); //nums=100;
}
public static void change(int nums)
{
    nums=110;
}

内存解释

每个方法压入栈区中,都会有独立的栈空间,在该方法的栈空间中传入的实际上是参数值的副本(参数的字面值)来对形参进行初始化,在这个栈区中形参值发生了改变。方法执行完成后出栈,原来栈区中的变量不受影响

方法传递引用数据类型

除基本数据类型以外的所有数据类型包括数组,字符串,类都是引用数据类型

特点

引用数据类型在栈空间中存储的是其地址值,在堆区中开辟空间,真实的数据保存在堆区中

具体表现

方法中对参数的改变会影响原始变量的值

示例代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public static void main(String [] args)
{
    int[] nums={0,1,2};
    change(nums); //change(nums)
    System.out.println(nums); //nums={0,3,2};
}
public static void change(int[] nums)
{
    nums[1]=3;
}

内存解释

每个方法压入栈区中,都会有独立的栈空间,在该方法的栈空间中传入的实际上是参数值的副本(参数的地址值)对形参进行初始化。

在这个栈区中形参和原始变量指向同一块内存区域,对形参的修改会通过引用影响原始变量。

方法执行完成后出栈,原来栈区中的变量指向的内存区域已经受到影响

内存示意图

总结

  • 方法参数传递的本质是值传递(引用数据类型的值被视为地址值)
  • 函数调用时在函数作用域创建栈区空间,再使用实参对形参进行初始化(形参作为实参的副本)
    • 基本数据类型:形参改变不影响实参,形参在方法结束后弹出栈区
    • 引用数据类型:形参是地址值,形参地址值的改变不影响实参地址值,此时形参和实参指向同一个地址

特殊的例子String类

具体表现

作为一个单独的数据类型,传递效果与传递基本数据类型相同

作为对象的成员,传递效果与传递引用数据类型相同

特点

  • String 对象一旦创建就不可更改( String 是不可变类)
  • String 类的所有方法都不会改变 String 的内容

示例代码(作为单独的数据类型)

1
2
3
4
5
6
7
8
public static void main(String[] args) {
        String s1=new String("张三");
        change(s1);
        System.out.println(s1); // s1="张三"
    }
public static void change(String s2){
        s2="李四";
    }

解析过程

方法调用前

调用前的内存示意图

  • 首先 String s=new String("张三"),在堆中开辟内存放对象,变量 s1 在栈中,存放的是堆的地址
  • 将地址0x11传入change方法复制一份用于初始化s2,此时s2放的是地址0x11

方法调用时

  • s2="李四" ;通过=赋值是直接先去常量池寻找是否存在与“李四”相同的值,有的话直接将其地址返回。否则创建一个值,再返回其地址。这里改变的是s2s2 位于栈中

方法调用后

方法调用后内存示意图

s1s2最终存储的地址不同

示例代码(作为类的成员变量)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class Person {
    String name;
    public Person(String name){
        this.name=name;
    }
}
 
public static void main(String[] args) {
        Person p1 = new Person("张三");
        change(p1);
        System.out.println(p1.name); // 李四
}
public static void change(Person p2){
        p2.name="李四";
}

解析过程

方法调用前

方法调用前内存示意图

  • 首先 Person s=new Person("张三"),在堆中开辟内存放对象,变量p1在栈中,存放的是堆的地址
  • 将地址0x11复制一份给s2用于初始化,此时s2放的是地址0x11

方法调用时

  • p2.name="李四";要知道通过=赋值是直接先去常量池寻找是否存在与“李四”相同的值,有的话直接将其地址返回。否则创建一个值,再返回其地址。与上一问题不同的是,这里我们改变的是name,而name在堆中,所以常量池中地址是复制给了name

方法调用后方法调用后内存示意图

p1.namep2.name最终存储的地址相同

最后更新于 2025-04-16 14:45 UTC
그 경기 끝나고 좀 멍하기 있었는데 여러분 이제 살면서 여러가
使用 Hugo 构建
主题 StackJimmy 设计