方法的作用
在Java中,方法(Method)是一段具有特定功能的代码块,可以被重复调用。以下是一些Java方法的主要作用:
- 封装性:方法可以封装一段代码,使得代码更加模块化,易于理解和维护。
- 复用性:通过定义方法,可以避免重复编写相同的代码,提高代码的复用性。
- 抽象性:方法可以隐藏实现细节,只暴露必要的接口给调用者。
- 参数化:方法可以接受参数,使得方法更加灵活,可以处理不同的输入。
- 返回值:方法可以有返回值,这允许方法执行后向调用者提供结果。
- 错误处理:方法内部可以处理错误,并通过返回值或抛出异常的方式通知调用者。
- 实现算法:方法可以用来实现特定的算法或逻辑。
- 与对象交互:对于面向对象编程,方法可以作为对象的行为,与对象的状态进行交互。
Java中的方法定义通常包括访问修饰符、返回类型、方法名、参数列表和方法体。例如:
public int add(int a, int b) {
return a + b;
}
在这个例子中,add
是一个方法名,它接受两个整数参数a
和b
,返回它们的和。public
是访问修饰符,表示这个方法可以被任何其他类访问。int
是返回类型,表示这个方法返回一个整数。
##限制条件:在主类中定义,并且由主方法直接调用的方法形式。
方法(Method)是将功能重复的代码封装成一段独立的代码,通过调用方法的方式以提高代码的复用性(减少代码重复)。方法可以接收输入参数,执行操作,并且可以选择性地返回一个值。以下是定义Java方法的基本结构:
- 访问修饰符(Access Modifier):定义了方法的可见性,例如
public
、protected
、private
或包级访问(默认,不写访问修饰符)。 - 返回类型(Return Type):方法执行完成后返回的数据类型。如果方法不返回任何值,则使用
void
作为返回类型。 - 方法名(Method Name):方法的名称,应遵循Java的命名规则。
- 参数列表(Parameter List):方法可以接收零个或多个参数,参数列表包括参数的类型和名称。参数列表用圆括号
()
包围。 - 方法体(Method Body):方法的主体,即实际执行的代码,用花括号
{}
包围。 - 异常声明(Exception Declaration):可选,如果方法可能抛出异常,则需要在方法声明中声明这些异常。
局部变量是指在方法中声明的变量,起作用范围仅限于方法中(使用前必须进行初始化,否则编译错误)
//public static 返回类型 方法名称([参数类型 变量, ......]) {
方法体代码;
[return [返回值];]
//}
public class Calculator {
// 定义一个公共方法,返回两个整数的和
public int add(int num1, int num2) {
int sum = num1 + num2; // 计算和
return sum; // 返回计算结果
}
}
Calculator
类定义了一个名为 add
的公共方法,它接受两个整数参数 num1
和 num2
,返回它们的和。方法体包含了计算和的逻辑,并使用 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 类的方法)
-
定义:使用
static
关键字声明的方法。可访问类的静态变量和静态方法,但不能访问类的实例变量和实例方法。 -
属于类:静态方法属于类本身,而不是类的任何特定实例。
-
调用方式:可以通过类名直接调用,也可以通过类的实例调用,但推荐通过类名调用以避免混淆。
-
特点
:
- 不能直接访问非静态成员(实例变量或实例方法),除非通过类的实例。
- 可以访问静态变量和静态方法。
- 通常用于工具类或实用程序类,提供不依赖于对象状态的功能。
- 可以被继承,但继承的静态方法不能被子类覆盖。
2非静态方法
-
定义:没有使用
static
关键字声明的方法。 -
属于实例:非静态方法属于类的实例,必须通过类的实例来调用。
-
调用方式:只能通过创建类的实例后,通过该实例调用。
-
特点
:
- 可以访问类的实例变量和实例方法。
- 通常用于需要操作对象状态或与对象行为相关的方法。
- 可以被子类覆盖(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_]LT788I
LBZ]E5_U9DPM.png)
实际参数(实参)
-
定义:实际参数是在调用方法时传递给方法的值或变量。
-
作用:实参用于将数据传递给方法。
-
位置:实参位于方法调用时的括号内。
-
类型:实参可以是常量、变量、表达式或方法的返回值。
int x = 10; String str = "Kimi"; display(x, str); // x 和 str 是实参
形式参数(形参)
- 定义:形式参数是在方法定义时使用的变量名,它们代表了方法调用时将要传递的值。
- 作用:形参用于接收调用方法时传递的值。
- 位置:形参位于方法的参数列表中,即方法名后面的括号内。
- 作用域:形参的作用域仅限于方法体内部。
/** 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
内部交换了形参 x
和 y
的值,但原始变量 a
和 b
的值并没有改变,因为基本数据类型是通过值传递的。
???
![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文件解释成各个平台可以识别的机器码,最终实现跨平台运行代码。
JVM内存模型
JVM内存模型可以分为两个部分,如下图所示,堆和方法区是所有线程共有的,而虚拟机栈,本地方法栈和程序计数器则是线程私有的。
堆(Heap)
在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old ),新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。
下图中的Perm代表的是永久代,但是注意永久代并不属于堆内存中的一部分,同时jdk1.8之后永久代也将被移除
堆是java虚拟机所管理的内存中最大的一块内存区域,也是被各个线程共享的内存区域,该内存区域存放了对象实例及数组(但不是所有的对象实例都在堆中)。
"栈"(Stack)
是一种遵循后进先出(LIFO,Last In First Out)原则的数据结构。它有两个主要的操作:push
(入栈)和pop
(出栈)。此外,还有一个peek
或top
操作,允许查看栈顶元素而不将其移除。
栈在编程语言和内存管理中扮演着重要角色,以下是几种常见的栈类型:
- 表达式求值和函数调用:
- 编程语言解释器或编译器通常使用栈来评估表达式和处理函数调用,例如,操作符优先级和函数调用的返回地址。
- 内存栈:
- 每个线程在Java虚拟机(JVM)中都有自己的内存栈,称为虚拟机栈(JVM Stack)。这个栈用于存储局部变量和方法调用的信息。
- 调用栈:
- 当一个程序运行时,调用栈记录了程序执行期间函数调用的顺序。每个函数调用都有自己的栈帧,包含参数、局部变量和返回地址。
- 系统栈:
- 在操作系统中,系统栈用于存储中断处理程序的返回地址和寄存器状态,以及线程切换时的上下文信息。
- 递归:
- 递归函数通常使用栈来实现,每次递归调用都会将信息压入栈中,直到满足递归终止条件。
- 回溯算法:
- 回溯算法使用栈来存储中间状态,以便在搜索过程中回退到上一个状态。
- 网页浏览:
- 浏览器使用栈来管理用户点击链接后的页面访问历史,用户可以后退到之前访问的页面。
在JVM中,虚拟机栈具体特点如下:
- 线程私有:每个线程创建时,虚拟机栈也会被创建,它的生命周期与线程相同。
- 栈帧:虚拟机栈由多个栈帧组成,每个栈帧包含局部变量表、操作数栈、方法出口等信息。
- 局部变量表:存储方法的参数和局部变量。
- 方法调用:每当方法被调用时,一个新的栈帧就会被创建并压入虚拟机栈顶。
- 方法返回:当方法执行完毕时,它的栈帧会被弹出,程序计数器会更新为方法调用者的PC寄存器值,以便继续执行调用者的方法。
栈的这些特性使其成为程序运行和数据管理中不可或缺的工具。
##方法重载(重点)
方法重载指的是:方法名称相同,参数的类型和个数不同。
方法重载的特点:
- 方法名相同:重载的方法必须具有相同的方法名。
- 参数列表不同:参数的数量、类型或顺序至少有一项不同。
- 返回类型可变:方法的返回类型可以相同也可以不同,但仅根据返回类型来区分方法是不够的,因为编译器会根据参数列表来选择正确的方法。
- 访问修饰符可变:方法的访问修饰符(如
public
,private
等)可以相同也可以不同。 - 异常声明可变:方法可能抛出的异常也可以不同。
范例: 实现方法重载
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); //输出布尔型
}
}