方法的作用

在Java中,方法(Method)是一段具有特定功能的代码块,可以被重复调用。以下是一些Java方法的主要作用:

  1. 封装性:方法可以封装一段代码,使得代码更加模块化,易于理解和维护。
  2. 复用性:通过定义方法,可以避免重复编写相同的代码,提高代码的复用性。
  3. 抽象性:方法可以隐藏实现细节,只暴露必要的接口给调用者。
  4. 参数化:方法可以接受参数,使得方法更加灵活,可以处理不同的输入。
  5. 返回值:方法可以有返回值,这允许方法执行后向调用者提供结果。
  6. 错误处理:方法内部可以处理错误,并通过返回值或抛出异常的方式通知调用者。
  7. 实现算法:方法可以用来实现特定的算法或逻辑。
  8. 与对象交互:对于面向对象编程,方法可以作为对象的行为,与对象的状态进行交互。

Java中的方法定义通常包括访问修饰符、返回类型、方法名、参数列表和方法体。例如:

public int add(int a, int b) {
    return a + b;
}

在这个例子中,add是一个方法名,它接受两个整数参数ab,返回它们的和。public是访问修饰符,表示这个方法可以被任何其他类访问。int是返回类型,表示这个方法返回一个整数。

##限制条件:在主类中定义,并且由主方法直接调用的方法形式。

方法(Method)是将功能重复的代码封装成一段独立的代码,通过调用方法的方式以提高代码的复用性(减少代码重复)。方法可以接收输入参数,执行操作,并且可以选择性地返回一个值。以下是定义Java方法的基本结构:

  1. 访问修饰符(Access Modifier):定义了方法的可见性,例如 publicprotectedprivate 或包级访问(默认,不写访问修饰符)。
  2. 返回类型(Return Type):方法执行完成后返回的数据类型。如果方法不返回任何值,则使用 void 作为返回类型。
  3. 方法名(Method Name):方法的名称,应遵循Java的命名规则。
  4. 参数列表(Parameter List):方法可以接收零个或多个参数,参数列表包括参数的类型和名称。参数列表用圆括号 () 包围。
  5. 方法体(Method Body):方法的主体,即实际执行的代码,用花括号 {} 包围。
  6. 异常声明(Exception Declaration):可选,如果方法可能抛出异常,则需要在方法声明中声明这些异常。

局部变量是指在方法中声明的变量,起作用范围仅限于方法中(使用前必须进行初始化,否则编译错误)

//public static 返回类型 方法名称([参数类型 变量, ......]) {
	方法体代码;
	[return [返回值];]
//}
public class Calculator {
    // 定义一个公共方法,返回两个整数的和
    public int add(int num1, int num2) {
        int sum = num1 + num2; // 计算和
        return sum; // 返回计算结果
    }
}

Calculator 类定义了一个名为 add 的公共方法,它接受两个整数参数 num1num2,返回它们的和。方法体包含了计算和的逻辑,并使用 return 语句返回结果

##在定义方法的时候对于方法的返回值有一下的两类:

  • void:没有返回值;

  • 数据类型(基本类型,引用类型)。

    eg:定义一个没有返回值没有参数的方法

    public class TestDemo {
    
    	public static void main(String[] args) {
    
    		print();	//主方法里面直接调用方法
    
    	}
    
    	public static void print() {
    
    		System.out.println("Hello	World!"); //定义方法
    
    	}
    
    }
    
    

    方法可以分为静态方法和非静态方法(通常称为实例方法或动态方法)。这两种方法的主要区别在于它们如何与类和对象关联,以及它们的使用方式。

    1.静态方法(Static Method 类的方法)

    1. 定义:使用 static 关键字声明的方法。可访问类的静态变量和静态方法,但不能访问类的实例变量和实例方法。

    2. 属于类:静态方法属于类本身,而不是类的任何特定实例。

    3. 调用方式:可以通过类名直接调用,也可以通过类的实例调用,但推荐通过类名调用以避免混淆。

    4. 特点

      • 不能直接访问非静态成员(实例变量或实例方法),除非通过类的实例。
      • 可以访问静态变量和静态方法。
      • 通常用于工具类或实用程序类,提供不依赖于对象状态的功能。
      • 可以被继承,但继承的静态方法不能被子类覆盖。

    2非静态方法

    1. 定义:没有使用 static 关键字声明的方法。

    2. 属于实例:非静态方法属于类的实例,必须通过类的实例来调用。

    3. 调用方式:只能通过创建类的实例后,通过该实例调用。

    4. 特点

      • 可以访问类的实例变量和实例方法。
      • 通常用于需要操作对象状态或与对象行为相关的方法。
      • 可以被子类覆盖(Override),实现多态性。

    示例

    public class MyClass {
        // 静态变量
        private static int staticVar = 0;
    
        // 实例变量
        private int instanceVar = 0;
    
        // 静态方法
        public static void staticMethod() {
            System.out.println("Static method can access static variables: " + staticVar);
        }
    
        // 非静态方法
        public void instanceMethod() {
            System.out.println("Instance method can access both static and instance variables: " + staticVar + ", " + instanceVar);
        }
    }
    
    // 调用静态方法
    MyClass.staticMethod();
    
    // 创建实例并调用非静态方法
    MyClass myObject = new MyClass();
    myObject.instanceMethod();
    

3成员方法(对象的方法)

一、成员方法的定义
访问修饰符 返回参数类型 方法名(形参列表){ //方法主体
语句;
return 返回值;
}

1.形参列表:表示成员方法的输入

2.返回数据类型:表示成员方法输出,void表示没有返回值

3.方法主体:表示为了实现某一功能的代码块

4.return语句不是必须的

​ 成员方法(Member Method)是指定义在类中的方法,它们属于类的一部分,可以是静态的也可以是非静态的。

​ 在某些情况下,我们在需要定义成员方法(简称方法)。比如人类:除了有一些属性外(年龄、姓名等),我们人类还有一些行为比如:可以说话,跑步,通过学习还可以做算术题等等。这时就要用成员方法才能完成。

public class example{
    public static void main(String[] args){
        //使用方法:先创建对象,后调用方法
        Person p1 = new.Person(); //创建对象
        p1.speak();//调用方法
    }
}
 
 
class Person{ //类
    //属性
    String name;
    int age;
 
    //方法(成员方法)
    public void speak(){
        System.out.println("XXXXX");
    }
}
 
/*
    public表示访问修饰符  void表示返回数据类型  speak表示方法名  
    ()里面表示形参列表   {}表示方法体
*/
eg:
public class Method01{
	public static void main(String[] args) {
		//使用方法
		//1.方法写好后,如果不去调用(使用),不会输出
		//2.先创建一个对象,如何调用方法即可
		Person p1 = new Person();//创建对象
		p1.speak();//调用方法	
		p1.cal01();//调用cal01
		p1.cal02(10);//调用ca102方法,同时给n = 10
		
		//调用getSum方法,同时num1=10,num2=20
		//把 方法 getSum返回的值赋给变量returnRes
		int returnRes = p1.getSum(10,20);
		System.out.println("getSum返回的值=" + returnRes);
	}
}
 
class Person{//类
	String name;
	int age;
	//方法(成员方法)
	//添加speak成员方法,输出“我是一个好人”
	//解读
	//1.public:表示方法是公开
	//2.void:表示方法没有返回值
	//3.speak():speak是方法名,()里面是形参列表
	//4.{}是方法体,可以写我们要执行的代码
	//5.System.out.println()表示我们的方法是输出一句话
	public void speak() {
		System.out.println("我是一个好人");
	}
 
	//添加cal01 成员方法,可以计算从1+...+1000的结果
	public void cal01(){
		int res = 0;
		for(int i = 0; i <= 1000; i++){
			res += i;
		}
		System.out.println("cal01 计算结果=" + res);
	}
 
	//添加cal02 成员方法,该方法可以接受一个数n,计算从1+..+n的结果
	//解读
	//1.(int n)是形参列表,表示当前有一个形参n,可以接受用户输入
	public void cal02(int n){
		int res = 0;
		for(int i = 0; i <= n; i++){
			res += i;
		}
		System.out.println("cal02 计算结果=" + res);
	}
 
	//添加getSum成员方法,可以计算两个数的和
	//解读
	//1.public表示方法是公开的
	//2.int:表示方法执行后,返回一个int值
	//3.getSum: 方法名
	//4.(int num1,int num2)形参列表,两个形参,可以接受用户传入的两个数
	//return res;表示把res的值返回
	public int getSum(int num1,int num2){
		int res = num1 + num2;
		return res;
 
	}
}

![img](file:///C:\Users\kepler\AppData\Roaming\Tencent\Users\2024912594\QQ\WinTemp\RichOle\Q_]LT788ILBZ]E5_U9DPM.png)

实际参数(实参)

  1. 定义:实际参数是在调用方法时传递给方法的值或变量。

  2. 作用:实参用于将数据传递给方法。

  3. 位置:实参位于方法调用时的括号内。

  4. 类型:实参可以是常量、变量、表达式或方法的返回值。

    int x = 10;
    String str = "Kimi";
    display(x, str); // x 和 str 是实参
    

形式参数(形参)

  1. 定义:形式参数是在方法定义时使用的变量名,它们代表了方法调用时将要传递的值。
  2. 作用:形参用于接收调用方法时传递的值。
  3. 位置:形参位于方法的参数列表中,即方法名后面的括号内。
  4. 作用域:形参的作用域仅限于方法体内部。
	
/**	public void display(int num, String name (形参)) {
    // num 和 name 是形参
}
*/	
	public static void sumInt(){}
	public static void sumInt(int x){}
	public static void sumInt(int x, int y){}
	public static void sum(int a, int b, double d, String s){}
	
	形参有多个的话使用“逗号,”隔开。逗号是英文的。

形参和实参的关系

  • 当调用方法时,实参会按照顺序赋值给对应的形参。

  • 形参和实参在数量和类型上必须一致,否则编译时会报错。

  • 形参的值在方法执行期间可以被修改,但这些修改不会影响实参的值,因为实参传递的是值的副本(对于基本类型)或引用的副本(对于对象类型)。

    方法的传递

    值传递(Pass by Value)

    • 基本数据类型:对于Java的8种基本数据类型(int, double, float, char, byte, short, long, boolean),方法参数是通过值传递的。这意味着当一个基本类型的变量作为参数传递给方法时,实际上是传递了这个变量的值的副本。
    • 行为:方法内部对参数的修改不会影响到原始变量,因为方法内部操作的是原始值的副本。

    引用传递(Pass by Reference)

    • 对象类型:对于对象类型,方法参数是通过引用传递的。这意味着当一个对象作为参数传递给方法时,传递的是对象引用的副本,而不是对象本身。
    • 行为:由于方法内部和外部都引用同一个对象,所以方法内部对对象属性的修改会影响到原始对象。

    示例:

    public class Test {
        public static void main(String[] args) {
            int num = 10;
            String str = "Hello";
    
            // 基本数据类型的值传递
            changeValue(num);
            System.out.println("After changeValue: " + num); // 输出 10
    
            // 对象类型的引用传递
            changeObject(str);
            System.out.println("After changeObject: " + str); // 输出 World
        }
    
        public static void changeValue(int n) {
            n = 20; // 改变的是n的副本,不影响原始变量num
        }
    
        public static void changeObject(String s) {
            s = "World"; // 改变的是s的引用副本,但原始变量str引用的对象被改变
        }
    }
    在这个示例中,changeValue 方法尝试修改 num 的值,但实际修改的是 num 的副本,原始变量 num 的值不会被改变。而 changeObject 方法改变了 str 的引用副本,指向了一个新的字符串 "World",因此原始变量 str 的值被改变。
    

示例

javapublic class Test {
    public static void main(String[] args) {
        int a = 5;
        int b = 10;
        swap(a, b); // a 和 b 是实参
        System.out.println("After swap: a = " + a + ", b = " + b);
    }

    public static void swap(int x, int y) {
        int temp = x; // x 和 y 是形参
        x = y;
        y = temp;
        System.out.println("Inside swap: x = " + x + ", y = " + y);
    }
}

在这个例子中,尽管方法 swap 内部交换了形参 xy 的值,但原始变量 ab 的值并没有改变,因为基本数据类型是通过值传递的。

???

![img](file:///C:\Users\kepler\AppData\Roaming\Tencent\Users\2024912594\QQ\WinTemp\RichOle\D6$G}GLS996$KJ@VUID@PGT.png)

VM内存结构是与JVM的内部存储结构相关,而Java内存模型是与多线程编程相关。

​ JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一个虚构出来的计算机,有着自己完善的硬件架构,如处理器、堆栈等。

为什么需要JVM?

Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码( 字节码 ),就可以在多种平台上不加修改地运行。

Java文件必须先通过一个叫javac的编译器,将代码编译成class文件,然后通过JVM把class文件解释成各个平台可以识别的机器码,最终实现跨平台运行代码。

img

JVM内存模型

JVM内存模型可以分为两个部分,如下图所示,堆和方法区是所有线程共有的,而虚拟机栈,本地方法栈和程序计数器则是线程私有的。

img

堆(Heap)

在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old ),新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。

下图中的Perm代表的是永久代,但是注意永久代并不属于堆内存中的一部分,同时jdk1.8之后永久代也将被移除

img

堆是java虚拟机所管理的内存中最大的一块内存区域,也是被各个线程共享的内存区域,该内存区域存放了对象实例及数组(但不是所有的对象实例都在堆中)。

"栈"(Stack)

是一种遵循后进先出(LIFO,Last In First Out)原则的数据结构。它有两个主要的操作:push(入栈)和pop(出栈)。此外,还有一个peektop操作,允许查看栈顶元素而不将其移除。

栈在编程语言和内存管理中扮演着重要角色,以下是几种常见的栈类型:

  1. 表达式求值和函数调用
    • 编程语言解释器或编译器通常使用栈来评估表达式和处理函数调用,例如,操作符优先级和函数调用的返回地址。
  2. 内存栈
    • 每个线程在Java虚拟机(JVM)中都有自己的内存栈,称为虚拟机栈(JVM Stack)。这个栈用于存储局部变量和方法调用的信息。
  3. 调用栈
    • 当一个程序运行时,调用栈记录了程序执行期间函数调用的顺序。每个函数调用都有自己的栈帧,包含参数、局部变量和返回地址。
  4. 系统栈
    • 在操作系统中,系统栈用于存储中断处理程序的返回地址和寄存器状态,以及线程切换时的上下文信息。
  5. 递归
    • 递归函数通常使用栈来实现,每次递归调用都会将信息压入栈中,直到满足递归终止条件。
  6. 回溯算法
    • 回溯算法使用栈来存储中间状态,以便在搜索过程中回退到上一个状态。
  7. 网页浏览
    • 浏览器使用栈来管理用户点击链接后的页面访问历史,用户可以后退到之前访问的页面。

在JVM中,虚拟机栈具体特点如下:

  • 线程私有:每个线程创建时,虚拟机栈也会被创建,它的生命周期与线程相同。
  • 栈帧:虚拟机栈由多个栈帧组成,每个栈帧包含局部变量表、操作数栈、方法出口等信息。
  • 局部变量表:存储方法的参数和局部变量。
  • 方法调用:每当方法被调用时,一个新的栈帧就会被创建并压入虚拟机栈顶。
  • 方法返回:当方法执行完毕时,它的栈帧会被弹出,程序计数器会更新为方法调用者的PC寄存器值,以便继续执行调用者的方法。

栈的这些特性使其成为程序运行和数据管理中不可或缺的工具。

##方法重载(重点)

方法重载指的是:方法名称相同,参数的类型和个数不同。

方法重载的特点:

  1. 方法名相同:重载的方法必须具有相同的方法名。
  2. 参数列表不同:参数的数量、类型或顺序至少有一项不同。
  3. 返回类型可变:方法的返回类型可以相同也可以不同,但仅根据返回类型来区分方法是不够的,因为编译器会根据参数列表来选择正确的方法。
  4. 访问修饰符可变:方法的访问修饰符(如 public, private 等)可以相同也可以不同。
  5. 异常声明可变:方法可能抛出的异常也可以不同。

范例: 实现方法重载

public class Calculator {

    // 方法重载示例:不同的参数数量
    public int add(int a, int b) {
        return a + b;
    }

    public int add(int a, int b, int c) {
        return a + b + c;
    }

    // 方法重载示例:不同的参数类型
    public double add(double a, double b) {
        return a + b;
    }

    // 方法重载示例:不同的参数顺序
    public int multiply(int a, int b) {
        return a * b;
    }

    public int multiply(int b, int a) {
        return a * b; // 参数顺序不同,但逻辑相同
    }
}

方法重载的调用

Calculator calc = new Calculator();

// 调用不同的重载方法
int sum1 = calc.add(5, 10); // 调用第一个add方法
int sum2 = calc.add(5, 10, 15); // 调用第二个add方法
double result1 = calc.add(5.5, 10.1); // 调用第三个add方法
int product = calc.multiply(3, 4); // 调用multiply方法

在进行方法重载的时候有一个重要的原则:要求方法的返回值类型一定要相同。

public class TestDemo {
	public static void main(String[] args) {
		//此时将根据参数的类型和个数的不同执行不同的方法体
		System.out.println("hello");   //输出字符串
		System.out.println(1);			//输出整形
		System.out.println(1.1);		//输出浮点型
		System.out.println('A');		//输出字符
		System.out.println(true);		//输出布尔型
	}
}