何谓方法?
-
System.out.println(); 那么它是什么呢?
System: 是一个系统的类
out: 是System下的一个输出对象
println(): 它就是一个方法
整句理解为:意思就是调用我们系统类里面的标准输出对象out中的方法,叫做println()方法
◆Java方法是语句的集合,它们在一起执行一个功能。
方法是解决一类问题的步骤的有序组合
方法包含于类或对象中
方法在程序中被创建,在其他地方被引用
◆设计方法的原则:方法的本意是功能块,就是实现某个功能的语句块的集合。我们设计方法的时候,最好保持方法的原子性,就是一个方法只完成1个功能,这样利于我们后期的扩展
◆回顾:方法的命名规则
变量的命名规范
所有变量,方法,类名要做到:见名知意
类成员变量:首字母小写和驼峰命名原则:nothSalary 除了第一 个单词以外,后边的单词首字母大写
局部变量:首字母小写和驼峰命名原则
常量命名:大写字母和下划线:MAX_VALUE
类名:首字母大写和驼峰命名原则:Man,GoodMan
方法名:首字母小写和驼峰命名原则:run(),runRun()
小代码示例:
package Rabbit.method;
public class Demo01 {
//main方法
public static void main(String[] args) {
int sum = add(1,2);
System.out.println(sum);
}
//加法
public static int add(int a,int b){
return a+b;
}
// public static long add(long a,long b,long c){
// return a+b+c;
// }
}
方法的定义
我们先来理解一下什么叫做方法,通俗一点来说:
在我们的日常生活中,方法可以理解为要做某件事情,而采取的解决办法。
如:小明同学在路边准备坐车来学校学习。这就面临着一件事情(坐车到学校这件事情)需要解决,解决办法呢?可采用坐公交车或坐出租车的方式来学校,那么,这种解决某件事情的办法,我们就称为方法。
在java中,方法就是用来完成解决某件事情或实现某个功能的办法。
方法实现的过程中,会包含很多条语句用于完成某些有意义的功能——通常是处理文本,控制输入或计算数值。
我们可以通过在程序代码中引用方法名称和所需的参数,实现在该程序中执行(或称调用)该方法。方法,一般都有一个返回值,用来作为事情的处理结果。
-
java的方法类似于其它语言函数,是一段用来完成特定功能的代码片段,一般情况下,定义一个方法包含一下语法:
修饰符 返回值类型 方法名(参数类型 参数名1,参数类型 参数名2,......){
执行语句
………
return 返回值;
}方法包含一个方法头和一个方法体。下面是一个方法的所有部分:
修饰符: 修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。
返回值类型: 方法可能会返回值。 return Value Type是方法返回值的数据类型。有些方法执行所需的操作,但没有返回值。在这种情况下, return Value Type是关键字void
方法名: 是方法的实际名称。方法名和参数表共同构成方法签名。
参数类型: 参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的
**参数类型:**顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
形式参数: 在方法被调用时用于接收外界输入的数据
实参: 调用方法时实际传给方法的数据
方法体: 方法体包含具体的语句,定义该方法的功能。
方法体方法调用
◆调用方法:对象名.方法名(实参列表)
◆Java支持两种调用方法的方式,根据方法是否返回值来选择。
◆当方法返回一个值的时候,方法调用通常被当做一个值。例如:int larger max(30, 40);
◆如果方法返回值是void,方法调用一定是一条语句。
System. out. printin("Hello,Rabbit!" );
代码来啦!:
package Rabbit.method; public class Demo02 { public static void main(String[] args) { int max = max(20,20); System.out.println(max); } public static int max(int num1,int num2){ int result = 0; if (num2==num1){ System.out.println("num1==num2"); return 0; //终止方法 } if (num1>num2){ result = num1; }else { result = num2; } return result; } }
◆拓展了解:值传递和引用传递,为什么说java是值传递?
注意:以下为参考内容(copy的!!!)
值传递:
在方法被调用时,实参通过形参把它的内容副本传入方法内部,此时形参接收到的内容是实参值的一个拷贝,因此在方法内对形参的任何操作,都仅仅是对这个副本的操作,不影响原始值的内容。
来看个例子:
1public static void valueCrossTest(int age,float weight){
2 System.out.println(“传入的age:”+age);
3 System.out.println(“传入的weight:”+weight);
4 age=33;
5 weight=89.5f;
6 System.out.println(“方法内重新赋值后的age:”+age);
7 System.out.println(“方法内重新赋值后的weight:”+weight);
8 }
9
10//测试
11public static void main(String[] args) {
12 int a=25;
13 float w=77.5f;
14 valueCrossTest(a,w);
15 System.out.println(“方法执行后的age:”+a);
16 System.out.println(“方法执行后的weight:”+w);
17}
复制代码
输出结果:
1传入的age:25
2传入的weight:77.5
3
4方法内重新赋值后的age:33
5方法内重新赋值后的weight:89.5
6
7方法执行后的age:25
8方法执行后的weight:77.5
复制代码
从上面的打印结果可以看到:
a和w作为实参传入valueCrossTest之后,无论在方法内做了什么操作,最终a和w都没变化。
这是什么造型呢?!!
下面我们根据上面学到的知识点,进行详细的分析:
首先程序运行时,调用mian()方法,此时JVM为main()方法往虚拟机栈中压入一个栈帧,即为当前栈帧,用来存放main()中的局部变量表(包括参数)、操作栈、方法出口等信息,如a和w都是mian()方法中的局部变量,因此可以断定,a和w是躺着mian方法所在的栈帧中
如图:而当执行到valueCrossTest()方法时,JVM也为其往虚拟机栈中压入一个栈,即为当前栈帧,用来存放valueCrossTest()中的局部变量等信息,因此age和weight是躺着valueCrossTest方法所在的栈帧中,而他们的值是从a和w的值copy了一份副本而得,如图:
。
因而可以a和age、w和weight对应的内容是不一致的,所以当在方法内重新赋值时,实际流程如图:也就是说,age和weight的改动,只是改变了当前栈帧(valueCrossTest方法所在栈帧)里的内容,当方法执行结束之后,这些局部变量都会被销毁,mian方法所在栈帧重新回到栈顶,成为当前栈帧,再次输出a和w时,依然是初始化时的内容。
因此:
值传递传递的是真实内容的一个副本,对副本的操作不影响原内容,也就是形参怎么变化,不会影响实参对应的内容。
引用传递:
”引用”也就是指向真实内容的地址值,在方法调用时,实参的地址通过方法调用被传递给相应的形参,在方法体内,形参和实参指向通愉快内存地址,对形参的操作会影响的真实内容。
举个栗子:
先定义一个对象:
1public class Person {
2 private String name;
3 private int age;
4
5 public String getName() {
6 return name;
7 }
8 public void setName(String name) {
9 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YpVvdW14-1626605843522)(file:///C:\Users\兔子\AppData\Roaming\Tencent\QQ\Temp%W@GJ$ACOF(TYDYECOKVDYB.png)]this.name = name;
10 }
11 public int getAge() {
12 return age;
13 }
14 public void setAge(int age) {
15 this.age = age;
16 }
17}
复制代码
我们写个函数测试一下:
1public static void PersonCrossTest(Person person){
2 System.out.println(“传入的person的name:”+person.getName());
3 person.setName(“我是张小龙”);
4 System.out.println(“方法内重新赋值后的name:”+person.getName());
5 }
6//测试
7public static void main(String[] args) {
8 Person p=new Person();
9 p.setName(“我是马化腾”);
10 p.setAge(45);
11 PersonCrossTest§;
12 System.out.println(“方法执行后的name:”+p.getName());
13}
复制代码
输出结果:
1传入的person的name:我是马化腾
2方法内重新赋值后的name:我是张小龙
3方法执行后的name:我是张小龙
复制代码
可以看出,person经过personCrossTest()方法的执行之后,内容发生了改变,这印证了上面所说的“引用传递”,对形参的操作,改变了实际对象的内容。
那么,到这里就结题了吗?
不是的,没那么简单,
能看得到想要的效果
是因为刚好选对了例子而已!!!
下面我们对上面的例子稍作修改,加上一行代码,
1public static void PersonCrossTest(Person person){
2 System.out.println(“传入的person的name:”+person.getName());
3 person=new Person();//加多此行代码
4 person.setName(“我是张小龙”);
5 System.out.println(“方法内重新赋值后的name:”+person.getName());
6 }
复制代码
输出结果:
1传入的person的name:我是马化腾
2方法内重新赋值后的name:我是张小龙
3方法执行后的name:我是马化腾
复制代码
`
为什么这次的输出和上次的不一样了呢?
看出什么问题了吗?
按照上面讲到JVM内存模型可以知道,对象和数组是存储在Java堆区的,而且堆区是共享的,因此程序执行到main()方法中的下列代码时
1Person p=new Person();
2 p.setName(“我是马化腾”);
3 p.setAge(45);
4 PersonCrossTest§;
复制代码
JVM会在堆内开辟一块内存,用来存储p对象的所有内容,同时在main()方法所在线程的栈区中创建一个引用p存储堆区中p对象的真实地址,如图:当执行到PersonCrossTest()方法时,因为方法内有这么一行代码:
1person=new Person();
复制代码
JVM需要在堆内另外开辟一块内存来存储new Person(),假如地址为“xo3333”,那此时形参person指向了这个地址,假如真的是引用传递,那么由上面讲到:引用传递中形参实参指向同一个对象,形参的操作会改变实参对象的改变。
可以推出:实参也应该指向了新创建的person对象的地址,所以在执行PersonCrossTest()结束之后,最终输出的应该是后面创建的对象内容。
然而实际上,最终的输出结果却跟我们推测的不一样,最终输出的仍然是一开始创建的对象的内容。
由此可见:引用传递,在Java中并不存在。
但是有人会疑问:为什么第一个例子中,在方法内修改了形参的内容,会导致原始对象的内容发生改变呢?
这是因为:无论是基本类型和是引用类型,在实参传入形参时,都是值传递,也就是说传递的都是一个副本,而不是内容本身。
有图可以看出,方法内的形参person和实参p并无实质关联,它只是由p处copy了一份指向对象的地址,此时:
p和person都是指向同一个对象。
因此在第一个例子中,对形参p的操作,会影响到实参对应的对象内容。而在第二个例子中,当执行到new Person()之后,JVM在堆内开辟一块空间存储新对象,并且把person改成指向新对象的地址,此时:
p依旧是指向旧的对象,person指向新对象的地址。
所以此时对person的操作,实际上是对新对象的操作,于实参p中对应的对象毫无关系。
结语因此可见:在Java中所有的参数传递,不管基本类型还是引用类型,都是值传递,或者说是副本传递。
只是在传递过程中:
如果是对基本数据类型的数据进行操作,由于原始内容和副本都是存储实际值,并且是在不同的栈区,因此形参的操作,不影响原始内容。
如果是对引用类型的数据进行操作,分两种情况,一种是形参和实参保持指向同一个对象地址,则形参的操作,会影响实参指向的对象的内容。一种是形参被改动指向新的对象地址(如重新赋值引用),则形参的操作,不会影响实参指向的对象的内容。方法的重载
- 重载就是在一个类中,有相同的函数名称,但形参不同的函数。
- 方法的重载的规则:
- 方法名称必须相同。
- 参数列表必须不同(个数不同;或者类型不同;参数排列顺序不同等)。
- 方法的返回类型可以相同也可以不相同。
- 仅仅返回的方法不同不足以造成方法重载。
-
现实理论:
方法名称相同时,编译器会根据调用方法的参数个数;参数类型等去逐个匹配,以选择对应方法,如果匹配失败,则编译器报错。
简单的方法重载代码示例:
package Rabbit.method; public class Demo02 { public static void main(String[] args) { double max = max(10,20,30); System.out.println(max); } //比大小 public static double max(double num1,double num2){ double result = 0; if (num2==num1){ System.out.println("num1==num2"); return 0; //终止方法 } if (num1>num2){ result = num1; }else { result = num2; } return result; } public static int max(int num1,int num2){ int result = 0; if (num2==num1){ System.out.println("num1==num2"); return 0; //终止方法 } if (num1>num2){ result = num1; }else { result = num2; } return result; } public static int max(int num1,int num2,int num3){ int result = 0; if (num2==num1){ System.out.println("num1==num2"); return 0; //终止方法 } if (num1>num2){ result = num1; }else { result = num2; } return result; } }
命令行传参(了解一下)
有时候你希望运行一个程序时再传递给它消息。这要靠传递命令行参数给mian()函数实现。
package Rabbit.method; public class Demo03 { public static void main(String[] args) { //args.length 数组的长度 for (int i = 0; i <args.length ; i++) { System.out.println("args[" + i + "]"+args[i]); } } }
- 找到java文件所在文件夹,打开命令行。如果使用IDAE就右键选择show in explore
- 在资源管理器的url前加上cmd+空格+回车在此文件夹打开命令行
- 输入java index.java 编译Java文件为class文件,index.java 为你需要编译的文件名称
- 使用cd…/返回src目录
- 输入java Rabbit.methed.Demo03 This is Rabbit ;传递参数This is Rabbit相当于{“This”,”is”,”Rabbit”}这个Rabbit.methed.Demo03就是你的运行路径。
可变参数也叫不定向参数(理解一下)
-
JDK1.5开始。Java传递同类的可变参数给一个方法。
-
在方法声明中,在指定参数类型后加一个省略号(…)。
-
一个方法只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
package Rabbit.method; public class Demo04 { public static void main(String[] args) { // 调用可变参数方法 printMax(34,3,3,2,56.5); printMax(new double[]{1,2,3}); } public static void printMax( double... numders){ if (numders.length == 0){ System.out.println("No argument passed"); return; } double result = numders[0]; //排序! for (int i = 1; i < numders.length; i++) { if (numders[i] > result){ result = numders[i]; } } System.out.println("The max vlue is " +result); } }
递归
——小重点小难点
-
A方法调用B方法,我们很容易理解!
-
递归就是,A方法调用A方法!就是自己调用自己,简单点说就是函数自身间接或者直接调用函数本身。
-
应用场景:利用递归可以简单的程序来解决一些复杂的问题。它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程需要的多次重复计算,大大的减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。
-
递归结构包括两个部分:
递归头:什么时候不掉用自身方法。如果没有头,将陷入死循环。
递归体:什么时候需要调用自身方法。
最简单的递归示例
public class recursionDemo {
public static void main(String[] args) {
show();
}
private static void show() {
method();
}
private static void method() {
show();
}
}
阶乘小代码:
package Rabbit.method;
public class Demo06 {
public static void main(String[] args) {
System.out.println(f(4));
}
//1! 1
//2! 2*1
//5! 5*4*3*2*1 阶乘
public static int f (int n) {
if (n == 1) {
return 1;
}else {
return n*f(n-1);
}
}
}
图片理解:
大基数最好不要用递归,如果基数太大会计算机会带不动