5_方法
1 何谓方法?
例子:
System.out.println();
System 是类,out 是对象,println() 是方法
所谓方法就是:语句的集合,它们在一起执行一个功能
- 方法是解决一类问题的步骤有序组合
- 方法包含在类中
- 方法在程序中被创建,在其他地方被引用
设计方法的原则:
方法的本意是功能块,就是实现某个功能的语句块的集合。我们设计方法的时候最好保持方法的原子性,就是一个方法只完成一个功能,这样有利于我们后期的扩展。
2 方法的定义&调用
2.1 方法定义
java 的方法类似其它语言的函数,是一段用来完成特点功能的代码片段,一般情况下,一个方法包含以下语法:
方法 包含一个 方法头 和 方法体。
-
修饰符:修饰符,这是可选的,告诉编译器如何调用该方法,定义了该方法的访问类型。
-
返回值类型: 方法的返回值,returnValueType 是方法的返回值,有些方法执行所需要的操作,但没有返回值,在这种情况下,returnValueType 是关键字void。
-
方法名:是方法的实际名称,方法名和参数表共同构成方法签名
-
参数类型:参数像时一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。 参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
- 形式参数:在方法被调用时用于接受外界输入的数据。
- 实参:调用方法时实际传给方法的数据。
-
方法体:方法体包含具体的语句,定义改方法的功能。
修饰符 返回值类型 方法名(参数类型 参数名 ){ ... 方法体 ... return 返回值; }
方法的定义练习
// 有参有返回值(求三个数的平均值)
public int getavg(double a,double b,double c) {
double result = a+b+c/3;
return result;
}
// 有参无返回值(打印 M 行,指定 N 个 * 的矩形)
public void print rectangle(int m, int n) {
//打印M行星
for (int i = 0; i < m; i++) {
//每行中打印N颗星
for (int j = 0; j < n; j++) {
System.out.print("*");
}
System.out.println();
}
}
// 无参有返回值(从键盘录入得到一个整数)
public static int getNumber() {
Scanner input = new Scanner(System.in);
int number = input.nextInt();
return number;
}
// 无参无返回值(打印3行,每行3个*的矩形)
public void printRectangle() {
System.out.println("***");
System.out.println("***");
System.out.println("***");
}
2.2 方法调用:
-
调用方法:对象名.方法名(实参列表)
-
java支持两种调用方法的方式,根据对象是否是返回值来选择。
当方法返回一个值的时候,方法调用通常被当做一个值,例如
int larger = max(30,40);
如果方法返回值是 void,方法调用一定是一条语句、
System.out.println("hello,jack!");
在同一个类中:
- 对于静态方法,其它的静态或非静态方法都可以直接调用它。
- 对于非静态方法,非静态可以直接调用,静态方法只能通过对象来调用它。
- 静态方法不能被非静态方法覆盖。(没有子父类继承关系,不能被重写,两种方法内存管理机制完全不同,所以不能覆盖。)
//static 和类一起加载的,存在的特别早 一个已经存在了调用一个不存在的东西,就会报错了
public static void a() {
b();
}
//类实例化 , 之后才存在 那怎么实例化呢?通过 new 么
public void b() {
}
在不同类之间:无论调用方法是非静态还是静态,如果被调用的方法是:
- 静态方法:则通过类名与对象都可以调(但通过对象的方式不建议使用,因为它属于非静态调用的方式)
- 非静态方法,则只能通过对象才可以调用它
匿名对象
匿名:简单的说就是没有名字的对象
匿名优点:节省为 ‘ 对象地址 ’ 开辟的空间
正常写法:
Student stu = new Student();
String name = stu.getName ;
new Student() 对象的名字为stu;
> 匿名写法:
> 1. Student[] stu = new Student[100];
> stu[0] = new Student();
表示的是stu数组的第一个元素为一个Student的对象,并且 new Student() 是没有名字的。
2 直接用对象调用方法
new studnet().getName;
>
2.3 值传递 & 引用传递
参数传递,可以理解当我们要调用一个方法时,我们会把指定的数值,传递给方法中的参数,这样方法中的参数就拥有了这个指定的值,可以使用该值,在方法中运算了。这种传递方式,我们称为参数传递。
Java 都是值传递
值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
我们来总结一下,值传递和引用传递之前的区别的重点是什么。
值传递 | 引用传递 | |
---|---|---|
根本区别 | 会创建副本(copy) | 不创建副本 |
所以 | 函数中无法改变原始值 | 函数可以改变原始值 |
我们来举一个形象的例子。再来深入理解一下值传递和引用传递。
你有一把钥匙,当你的朋友想要去你家的时候,如果你直接
把你的钥匙给他了,这就是引用传递。这种情况下,如果他对这把钥匙做了什么事情,比如他在钥匙上刻下了自己名字,那么这把钥匙还给你的时候,你自己的钥匙上也会多出他刻的名字。
你有一把钥匙,当你的朋友想要去你家的时候,你复刻
了一把新钥匙给他,自己的还在自己手里,这就是值传递。这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙。
但是,不管上面那种情况,你的朋友拿着你给他的钥匙,进到你的家里,把你家的电视砸了。那你说你会不会受到影响?而我们在pass方法中,改变user对象的name属性的值的时候,不就是在“砸电视”么。
public static void main(String[] args) {
ParamTest pt = new ParamTest();
User hollis = new User();
hollis.setName("Hollis");
hollis.setGender("Male");
pt.pass(hollis);
System.out.println("print in main , user is " + hollis);
}
public void pass(User user) {
user = new User();
user.setName("hollischuang");
user.setGender("Male");
System.out.println("print in pass , user is " + user);
}
上面的代码中,我们在pass方法中,改变了user对象,输出结果如下:
print in pass , user is User{name='hollischuang', gender='Male'}
print in main , user is User{name='Hollis', gender='Male'}
我们来画一张图,看一下整个过程中发生了什么,然后我再告诉你,为啥Java中只有值传递。

稍微解释下这张图,当我们在main中创建一个User对象的时候,在堆中开辟一块内存,其中保存了name和gender等数据。然后hollis持有该内存的地址0x123456
(图1)。
当尝试调用pass方法,并且hollis作为实际参数传递给形式参数user的时候,会把这个地址0x123456
交给user,这时,user也指向了这个地址(图2)。
然后在pass方法内对参数进行修改的时候,即user = new User();
,会重新开辟一块0X456789
的内存,赋值给user。后面对user的任何修改都不会改变内存0X123456
的内容(图3)。
上面这种传递是什么传递?肯定不是引用传递,如果是引用传递的话,在user=new User()
的时候,实际参数的引用也应该改为指向0X456789
,但是实际上并没有。
通过概念我们也能知道,这里是把实际参数的引用的地址复制了一份,传递给了形式参数。所以,上面的参数其实是值传递,把实参对象引用的地址当做值传递给了形式参数。Java中的传递,是值传递,而这个值,实际上是对象的引用(就是copy对象指向堆内存的地址)。
3 方法重载
方法名相同,参数列表不同
参数列表不同的意思是:参数类型和参数个数不同,参数顺序不同不同
4 命名行传参
有时候你希望运行一个程序的时候在传递给它消息,这要靠传递命名行参数给 main() 函数实现。
public class A {
public static void main(String[] args) {
for (int i = 0; i < args.length ; i++) {
System.out.println("args["+i+"]"+args[i]);
}
}
}

因为在类的上面有一行代码,package.pers.kang.work.work1 所以是不能直接运行的。
必须退到 src 下输入java pers.kang.work.work1.A 才能运行
最后 输入参数,执行方法

5 可变参数(不定向参数)
我们在写方法的时候,有很多不确定的参数。
所以
-
JDK 1.5 开始,Java 支持传递同类型的可变参数给一个方法。
-
在方法声明中,在指定参数类型后加一个省略号(...)。
-
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数,任何普通的参数必须在它之前声明。
本质上就是数组
public static void main(String args[]) {
//调用可变参数的方法
printMax(43,2,2,4,5,234);
printMax(new double[]{1,2,3});
}
public static void printMax(double...numbers) {
}
//错误 必须是最后一个可变参数
public void test(int...i,int x) {}
//正确
public void test(int x,int...i) {
}
6 递归(高频问点)
什么是递归? ----A 方法自己调用 A 方法!就是自己调用自己
// 报错,stackOverflowError 栈溢出异常,不要这样玩,不是这样使用的,
public class A {
public static void main(String[] args) {
A test = new A();
test.test();
}
public void test(){
test();
}
}
递归的好处:
利用递归可以用简单的程序来解决一些复杂的问题,它通常把一个大型的复杂的问题层层转化为一个与原问题相似规模较小的问题来求解,递归策略只需少量的程序就可以描述出解题过程所需要多次重复计算,大大减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。
递归结构包含两个部分:
- 递归头:什么时候不调用自身方法。如果没有头,将陷入死循环。(会一直)
- 递归体:什么时候需要调用自身方法。
public class A {
public static void main(String[] args) {
System.out.println(f(5));
}
public static int f(int n) {
if(n == 1) {
return 1;
}else{
return n * f(n-1);
}
}
}
本文来自博客园,作者:走马!,转载请注明原文链接:https://www.cnblogs.com/zou-ma/p/16018478.html
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术