目录
一、方法的语法
方法(method):是可以完成某个特定功能的并且可以被重复利用的代码片段。在C语言中,方法被称为“函数”。在java中不叫函数,叫做方法。
- 程序开始执行的时候是先执行main方法,因为main方法是一个入口。
- main方法不需要程序员手动调用,是由JVM调用的。但是除了main方法之外其他的方法,都需要程序员手动调用,方法只有调用的时候才会执行,方法不调用是不会执行的。
- 方法定义在类体当中,方法定义的先后顺序没有关系,都可以。
- 方法体中声明定义的变量属于局部变量,方法结束之后,局部变量占用的内存会自动释放。
- 在java语言中所有的方法体中的代码都必须遵循自上而下的顺序依次逐行执行。
语法:
//方法定义:
[修饰符列表] 返回值类型 方法名(形式参数列表){
方法体;
}
//方法调用
类名.方法名(实际参数列表);
语法解释:
1.修饰符列表:
- 修饰符列表不是必选项,是可选的。如:public static。
2.返回值类型:
- 可以是任何类型,java中合法的数据类型就行,如:byte shortint long float double boolean char String...
3.返回值:
- 一般指的是一个方法执行结束之后的结果。出现return或return+值这样的语句,方法结束。
- void:当一个方法执行结束不返回任何值的时候,返回值类型也不能空白,必须写上void关键字。(main方法结束之后不需要给JVM返回任何执行结果)
- return+值:如果返回值类型不是void,那么在方法体执行结束的时候必须使用"return+值;"。除了void之外,剩下的都必须有“return 值;”这样的语句。
- return:如果返回值类型是void,那么在方法体当中不能有“return 值;”这样的语句。但是可以有“return;”语句。这个语句“return;”的作用就是用来终止当前方法的。
- 在同一个域中(一个大括号内),"return语句"下面不能再编写其它代码。编写之后编译报错。
4.方法名:
- 方法名要见名知意。(驼峰命名方式)
- 方法名在标识符命名规范当中,要求首字母小写,后面每个单词首字母大写。
5.形参:
- 形式参数列表中的每一个参数都是“局部变量”,方法结束之后内存释放。
- 形参有多个的话使用“逗号,”隔开。如,public static void sumInt(int x, int y){}。
- 形参的个数是:0~N个。
- 形参的数据类型起决定性作用,形参对应的变量名是随意的。
6.方法体:
- 由Java语句构成,java语句以“;”结尾。
- 在方法体中的代码遵循自上而下的顺序依次逐行执行。
7.方法调用:
- 实参和形参类型一致。
- 当调用者和被调用者在同一个类中的时候,类名可以省略。如,MethodTest02.sumInt(100, 200);可写成sumInt(100, 200);
应用场景:
比如求和运算x+y=z,根据不同的需求,数据不同,就要重复写一样的代码。有了方法之后,只需直接调用该方法即可。
例1.1:没有使用方法
public class MethodTest01{
// 入口主方法。
public static void main(String[] args){
// 需求1:请编写程序,计算100和200的求和。
int x = 100;
int y = 200;
int z = x + y;
System.out.println(x + "+" + y + "=" + z);
// 需求2:请编写程序,计算666和888的求和。
int a = 666;
int b = 888;
int c = a + b;
System.out.println(a + "+" + b + "=" + c);
// 需求3:请编写程序,计算111和222的和
int m = 111;
int n = 222;
int k = m + n;
System.out.println(m + "+" + n + "=" + k);
}
}
例1.2:使用加法方法
public class MethodTest02{
// 方法定义在类体当中。
// 方法定义的先后顺序没有关系。都可以。
// 主方法。入口。
public static void main(String[] args){ // 自上而下依次逐行执行。
MethodTest02.sumInt(100, 200); // 需求1:请编写程序,计算100和200的求和。
MethodTest02.sumInt(666, 888); // 需求2:请编写程序,计算666和888的求和。
MethodTest02.sumInt(111, 222); // 需求3:请编写程序,计算111和222的和
//以上调用的方法在同一个类中,也可省略类名直接写成:sumInt(111, 222);
}
// 在类体当中定义一个方法,这个方法专门来完成求和。
// x y z在以下的sumInt方法中都属于局部变量
// 局部变量有一个特点:方法结束之后,局部变量占用的内存会自动释放。
public static void sumInt(int x, int y){ // 自上而下的顺序依次逐行执行。
int z = x + y;
System.out.println(x + "+" + y + "=" + z);
}
}
例子2:返回值类型
public class MethodTest03{
//方法定义在这里可以。
// main方法结束之后不需要给JVM返回任何执行结果。
public static void main(String[] args){
// 调用方法
int jieGuo = MethodTest03.sum(100, 200);//声明变量时,类型要和方法中返回的类型一致
System.out.println(jieGuo); //300
double jieGuo2 = MethodTest03.sum(100, 200);//int转double是小转大,自动类型转换,可以
System.out.println(jieGuo2); //300.0
public static int sum(int a, int b){
return a + b;
}
}
例子3:同类中调用可省略类名
// 类1
public class MethodTest04{
public static void daYin1(){ //方法可以写在前面
System.out.println("hello world1!");
}
// 入口
public static void main(String[] args){
daYin1();// “类名. ”可以省略
daYin2();
MyClass.print();// 跨类调用,“类名.”就不能省略了。
}
public static void daYin2(){ //方法可以写在后面
System.out.println("hello world2!!!");
}
}
// 类2
class MyClass{
public static void print(){
System.out.println("打印");
}
}
例子4:代码执行顺序
public class MethodTest05{
public static void main(String[] args){
System.out.println("main begin");
m1(); // 调用m1方法
System.out.println("main over");
}
public static void m1(){
System.out.println("m1 begin");
m2();// 调用程序不一定写到main方法中,其他方法中也可以调用方法
System.out.println("m1 over");
}
public static void m2(){
System.out.println("m2 begin");
T.m3();
System.out.println("m2 over");
}
}
class T{
public static void m3(){
System.out.println("m3 begin");
System.out.println("执行T中的方法m3");
System.out.println("m3 over");
}
}
/*
执行结果:
main begin
m1 begin
m2 begin
m3 begin
执行T中的方法m3
m3 over
m2 over
m1 over
main over
main结束,整个程序就结束了
*/
break和returnd的区别:
- break用来终止switch和离它最近的循环。
- return用来终止离它最近的一个方法。
例子5:return 的作用
public class MethodTest06{
public static void main(String[] args){ //main方法的返回值类型是void,表示没有返回值。
for(int i = 0; i < 10; i++){
if(i == 5){
//break; // 终止for循环
return; // 终止当前的方法
}
System.out.println("i = " + i);
}
System.out.println("Hello World!");
}
}
方法执行过程中内存的变化:
JVM(java虚拟机)三块主要的内存:
- 栈内存(stack)
- 堆内存
- 方法区内存
方法区:类加载器classloader将硬盘上的.class字节码文件装载到JVM的时候,会将字节码文件存放到方法区。存储代码片段。
栈内存:方法调用的时候,该方法需要的内存空间在栈中分配。存储方法运行过程中需要的内存,以及栈中会存储方法的局部变量。
栈数据结构特点:先进后出,后进先出。
方法调用叫做:压栈。分配空间
方法结束叫做:弹栈。释放空间
例子6:方法调用的执行过程就是压栈和弹栈的过程
public class MethodTest08{
//主方法,入口
public static void main(String[] args){
System.out.println("main begin");
int x = 100;
m1(x);
System.out.println("main over");
}
public static void m1(int i){ // i是局部变量
System.out.println("m1 begin");
m2(i);
System.out.println("m1 over");
}
public static void m2(int i){
System.out.println("m2 begin");
m3(i);
System.out.println("m2 over");
}
public static void m3(int i){
System.out.println("m3 begin");
System.out.println(i);
System.out.println("m3 over");
}
}
二、方法的重载(overload)
方法重载是指在一个类中定义多个同名的方法,但要求每个方法具有不同的参数的类型或参数的个数。调用重载方法时,java编译器能通过检查调用的方法的参数类型和个数选择一个恰当的方法。
1、什么时候需要考虑使用方法重载:
在同一个类当中,如果“功能1”和“功能2”它们的功能是相似的,那么可以考虑将它们的方法名一致,这样代码既美观,又便于后期的代码编写。
2、什么时候代码会发生方法重载:
- 条件1:在同一个类当中
- 条件2:方法名相同
- 条件3:参数列表不同(参数个数不同、参数类型不同、参数顺序不同)
例子1.1:未使用方法重载
方法sumInt、sumLong、sumDouble的功能“相似”,缺点有:
- 代码不美观(不好看、不整齐)。
- 程序员需要记忆更多的方法名称,程序员比较累。
public class OverloadTest01{
//主方法
public static void main(String[] args){
int x = sumInt(10, 20);
System.out.println(x);
long y = sumLong(10L, 20L);
System.out.println(y);
double z = sumDouble(10.0, 20.0);
System.out.println(z);
}
// 定义一个计算int类型数据的求和方法
public static int sumInt(int a, int b){
return a + b;
}
// 定义一个计算long类型数据的求和方法
public static long sumLong(long a, long b){
return a + b;
}
// 定义一个计算double类型数据的求和方法
public static double sumDouble(double a, double b){
return a + b;
}
}
例子1.2 :方法重载机制
java编译器会通过方法名进行区分,但是在java语言中允许方法名相同的情况出现。如果方法名相同的情况下,编译器会通过方法的参数类型进行方法的区分。
public class OverloadTest02{
public static void main(String[] args){
// 对于程序员来说,只需要记忆一个方法名即可。
System.out.println(sum(10, 20));
System.out.println(sum(10L, 20L));
System.out.println(sum(10.0, 20.0));
}
// 定义一个计算int类型数据的求和方法
public static int sum(int a, int b){
System.out.println("int求和");
return a + b;
}
// 定义一个计算long类型数据的求和方法
public static long sum(long a, long b){
System.out.println("long求和");
return a + b;
}
// 定义一个计算double类型数据的求和方法
public static double sum(double a, double b){
System.out.println("double求和");
return a + b;
}
}
例子2:方法重载的条件(参数个数、顺序、类型)
public class OverloadTest03{
public static void main(String[] args){
// 1、参数个数不同。
m1();
m1(100);
// 2、参数的顺序不同。
m2(10, 3.14);
m2(3.14, 10);
// 3、参数的类型不同。
m3(100);
m3(3.14);
}
// 1、参数个数不同。
public static void m1(){
System.out.println("m1无参数的执行!");
}
public static void m1(int a){
System.out.println("m1有一个int参数执行!");
}
// 2、参数的顺序不同。
public static void m2(int x, double y){
System.out.println("m2(int x, double y)");
}
public static void m2(double y, int x){
System.out.println("m2(double y, int x)");
}
// 3、参数的类型不同。
public static void m3(int x){
System.out.println("m3(int x)");
}
public static void m3(double d){
System.out.println("m3(double d)");
}
}
class MyClass{
// 不在同一个类当中,不能叫做方法重载。
public static void m1(int x, int y){
}
}
例子3:方法重载和返回值类型以及修饰符无关
// 报错。这不是重载,这是方法重复了。
public static int m5(){
return 1;
}
public static double m5(){
return 1.0;
}
// 报错。这不是重载,是重复了。
void m6(){ // 这个方法没有修饰符列表
}
public static void m6(){ // 这个有修饰符列表
}
例子4:最常见的方法重载—println()方法
public class OverloadTest04{
public static void main(String[] args){
// println是方法名,是SUN公司已经写好的,参数类型可以随便传。
System.out.println(10);
System.out.println(3.14);
System.out.println(true);
System.out.println('a');
System.out.println("abc");
System.out.println(100L);
System.out.println(3.0F);
}
代码的封装:有时候一个操作可能有多个地方会需要使用,这个时候我们就会把彼此相关数据和操作包围起来,形成一个class“类,想要访问对象的数据只能通过已定义的接口。
例如:“System.out.println();”这行代码每次打印输出都写很麻烦,可以封装一个工具类,然后编译成.class文件直接调用。
例子5:
S.java:封装
public class S{
// 以下所有的p()方法构成了方法的重载。
// 换行
public static void p(){
System.out.println();
}
// 输出byte
public static void p(byte b){
System.out.println(b);
}
// 输出short
public static void p(short s){
System.out.println(s);
}
// 输出int
public static void p(int i){
System.out.println(i);
}
// 输出long
public static void p(long l){
System.out.println(l);
}
// 输出float
public static void p(float f){
System.out.println(f);
}
// 输出double
public static void p(double d){
System.out.println(d);
}
// 输出boolean
public static void p(boolean b){
System.out.println(b);
}
// 输出char
public static void p(char c){
System.out.println(c);
}
// 输出String
public static void p(String s){
System.out.println(s);
}
}
ShuChu.java: 调用
public class ShuChu{
public static void main(String[] args){
S.p("Hello World!"); // 调用.相当于System.out.println("Hello World!");
S.p(100);
S.p('a');
S.p(true);
S.p(100 + 200);
S.p(10 / 3);
}
}
三、方法的递归
什么是递归:方法自己调用自己,就是方法递归。
递归时没有结束条件会发生栈内存溢出错误:StackOverflowError
注:在实际的开发中,不建议轻易的选择递归,能用for循环while循环代替的,尽量使用循环来做。因为循环的效率高,耗费的内存少。递归耗费的内存比较大,另外递归的使用不当,会导致JVM死掉。
例子1:编写程序,计算1~n的和。
方法1:使用循环
public class RecursionTest01{
public static void main(String[] args){
// 1~20的和
int retValue1 = sum(20);
System.out.println(retValue1);
}
// 计算1~n和的方法
public static int sum(int n){
int result = 0;
for(int i = 1; i <= n; i++){
result += i;
}
return result;
}
}
方法2:递归
public class RecursionTest02{
public static void main(String[] args){
int r = sum(3);
System.out.println(r); // 6
}
// 递归
public static int sum(int n){
if(n == 1){
return 1;
}
return n + sum(n-1);
}
}
例子2:计算1-n的阶乘
方法1:循环
public class RecursionTest03{
public static void main(String[] args){
int r = mul(4);
System.out.println(r); // 24
}
// 循环
public static int mul(int n){
int result = 1;
for(int i = 1; i <= n; i++){
result *= i;
}
return result;
}
}
方法2:递归
public class RecursionTest04{
public static void main(String[] args){
int r = mul(4);
System.out.println(r); // 24
}
// 递归
public static int mul(int n){
if(n == 1){
return 1;
}
return n * mul(n-1);
}
}