欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

JAVA-方法

时间:2023-08-01

目录

1、为什么需要方法

2、什么是方法

3、方法的定义     

4、方法的返回值

5、方法的调用

6、实参和形参之间的关系

7、方法执行中JVM的内存分配

8、方法的重载 (overload)

9、方法的递归调用

10、递归调用在JVM中的内存分配


PS:判断素数时,程序中i<=n/2用的是什么原理? 

1、为什么需要方法

        在代码的编写过程中,经常会遇到重复对某个功能进行实现的要求,这就意味着我们人需要代码能够进行重复的利用,以便于简化代码的编写和调用,这就是方法存在的必然因素。所以在设计代码的构成的时候,对于能够构成独立功能的代码,可以考虑剥离出来,形成一个单独的方法,便于日后的调用和使用。

2、什么是方法

        方法其实就是一段独立的代码片段,该代码片段独立的实现某个功能,可以重复的调用,它的作用就相当于C语言中的函数.

3、方法的定义     

         修饰符列表        返回值类型        方法名(形式参数列表){

                方法体;

        }

       方法体由{ }括号括起来

         ①修饰符列表,目前先了解public static,其他写法后续了解

        ②返回值类型:一般是方法执行结束后,会有一个值,该值返回到方法调用处供程序员使用。

        ③方法名:遵循java中对于标识符的规定就可以了

        ④形参列表:其实就是变量的定义,多个形参之间用逗号分隔,更重要的是数据类型,和变量的名称无关。

        ⑤方法体:java语句,一般用于实现该方法所需要完成的功能。

————方法定义的注意事项:

        (1)方法的定义要在类体中进行定义,类中可以定义多个方法,类的定义不存在定义的先后顺序之分

         (2)方法不可以嵌套定义,但是可以嵌套使用

         (3)方法体的代码和普通的java代码一样,遵循从上到下的执行原理。

4、方法的返回值

        返回值关键字:return + 字面值/运算或者运行结果为字面值的一个表达式

        返回值用return语句实现,可以返回任意数据类型的数值。但是注意如果方法有返回值,那么方法首部中,返回值类型一定不能为空或者void,返回值的数据类型要和return语句返回的数据的数据类型相对应,如果返回值和返回值类型不能匹配、无法 进行自动类型转换或者没有人为的进行强制类型转换,会报错【error:不兼容的类型】,注意一个方法在某种条件下只能存在一个返回值。

        返回值的类型可以是任意的数据类型:{

                 byte,short ,int,long,float,double,String,char,boolean,void,引用数据类型;

        }

        如果方法不需要返回值,那么返回值类型不能不写,要写明“void”,并且return语句不能有返回值,例如:

public static void 方法名(形式参数1……n){ 方法体; 【return;】 //不写返回值,或者去掉该语句,不写return语句;}

        为什么没有返回值的情况下可以写“return;”呢?保留这个功能是因为,return从本质上代表着方法的中止语句,遇到该语句,它所在的方法就彻底的执行结束了。其实return语句就相当于方法的结束语句,类似于循环的break语句的作用,只不过中止的是整个方法的执行过程,

例如:

public static void pu(int n){ for(int i=0;i<=5;i++){ System.out.println(i); if(i==2)return; } }

输出结果如下:

012进程已结束,退出代码为 0

 【很明显在遇到return之后,整个方法体的执行就彻底结束了。】

        但是如果需要返回值,在方法结束之后一定要返回一个具体数据类型的值,否则会报错。注意这个返回值并不是一定要接收的,由调用者选择是否设置一个对应数据类型的变量接收该值,然后供后期的运算使用。但是一般会接收的,因为一般那个数据是程序员所期望的数据,一般具有一定的作用。

break和return的区别

·break用于结束循环,但是如果在该循环后还有相关的java代码,依旧可以顺序执行

·但是return不同,一旦在方法中,该语句执行,那么整个方法就结束了,在return语句之后的任意java语句都是无法再进行执行的,方法就彻底的中止了。所以return语句的执行意味着方法的结束,所以在它所在的方法作用域当中,其后不能再编写任何代码,是无法访问到的

public static void pu(int n){ return; System.out.print("end"); }

如上代码会报错,如下:

D:IdeaJAVATESTsrctest3.java:68:9java: 无法访问的语句

break也是同理:

for(int i=0;i<=5;i++){ break; System.out.print(i); }

 也依旧会报错:

D:IdeaJAVATESTsrctest3.java:72:13java: 无法访问的语句

以上在编译阶段就能够发现错误,无需到运行阶段。 

        //关于返回值类型和return语句之间的对错,在编译阶段就能够检查到,无需等到巡行后报错。

5、方法的调用

        方法只定义不调用是不会发挥方法的作用的,所以只有我们去调用写好的方法才能够发挥其作用。方法可以重复多次的进行合理调用

——调用语句:

                对于静态的方法,采用【类名·方法名(实际参数)】的方式进行调用,当调用方法和非           调用方法在同一个类体中的时候,类名可以省略。如果省略了类名,默认从当前类找方法,           但是找不到,会报错。 所以如果调用的方法不是在当前类,请务必加上类名

                对于非静态的方法,如果在同一个类当中,采用【方法名(实际参数)】既可以完成方            法的调用。

——调用原理:

        类——汽车工厂       实际参数——原材料         形式参数——材料接收器   

        方法——产生不同产品的车间        返回值——产物

        在车间中(类),用户拿着原材料(实际参数)放入材料接收器(形式参数),经过产生不同产品的车间(方法)的处理,取到产物(返回值)。

        在方法的调用过程中,形参列表可以有0~N个,是局部变量,有自身的数据类型,形参中起决定性作用的是形参的数据类型,名字只是在该方法作用域内可以使用的一个变量,变量名是任意的。

        在用户调用的时候,传递过来的原材料就是实参,注意实参的类型一定要和形参的数据类型对应相同,参数的数目也要相同。

        因为方法体中代码自上而下执行的特性,当被调用方法全部执行结束或者由于遇到return结束之后,调用方法的那一行代码才相当于执行结束,之前提到方法不能嵌套定义,但是注意方法可以任意调用,也可以嵌套调用,但是注意不要调用成环,这样程序会没有出口,导致“死循环”;

6、实参和形参之间的关系

        在用户调用方法并传递了一定数目的实参的时候,如果实参的数据类型均为一般数据类型,那么在实参和形参之间传递的只是数值,即变量所在内存单元存储的字面值,两个变量的内存单元是不相同的两个内存单元,占用两个不同的内存地址,二者互不影响。但是如果实参的数据类型是引用数据类型,那么在实参和形参之间传递的是实参所在的地址,即二者是指的一个内存单元地址,如下所示:

        其实就相当于形参变量被赋予了实参变量所包含的值,这就相当于一般的赋值,所以在这个关系当中,类型的转换规则依旧满足。

       规则:数据类型的转换,有强制类型转换和自动类型转换两种。

1、在基本数据类型中,除了boolean类型的数据之外,其他的数据类型之间是可以互相的实现数据类型的转换的。

2、从小的数据类型转换成大的数据类型的时候,是自动类型转换,计算机会自动的进行该转换

3、从大的数据类型转换成小的数据类型的时候,是强制类型转换

        数据类型,范围由小到大:

        boolean=byte

        但是强制类型转换有可能会导致数据损失精度,尽量避免

4、sun公司给予了byte,short,char三种数据类型特权,如果赋值的整形字面值在这几种数据类型的范围内,就可以直接的进行赋值,而不损失精度,其实内部也有强制类型转换过程,只不过是由计算机完成的。

5、当byte,short,char和int类型的数据进行运算时,均转换为int类型然后在进行运算

6、当多种数据类型都在一起进行运算时,会转换成最大取值范围的数据类型进行运算

7、方法执行中JVM的内存分配

        方法只有调用并为之分配内存空间后才可以真正的发挥作用,如果不调用,JVM是不会为之分配内存空间的,只有在调用的时候才会为其动态的分配内存空间。

        在JVM的内存划分上,有三块主要的内存空间,分别是:方法区内存,堆内存和栈内存,当然还存在其他内存空间,但是主要的就这三种。

        首先我们要了解以下栈这种数据结构,以便于更好的理解在方法调用时内存的分配。

栈(stack)————

        一种数据结构(数据存储的形态和方式),可以用于数据存储,该数据结构有两种操作,压栈(push)和弹栈(pop),压栈即往在栈内分配内存单元(存入信息),弹栈即从栈内释放内存单元(取出信息),并且栈这种数据结构入口和出口是同一个,所以参考以下羽毛球桶,只能从一个口存取,并且先存入的”乒乓球“在下边,后放入的”乒乓球“在上边,如果取出时,先取出的是后放入的乒乓球,即栈的存取遵循【先入后出】的存取原则。

        栈的最顶部的元素是栈顶元素,最底部的(最先存入的元素)是栈底元素,通常会有一个指针,叫做栈帧,指向当前的栈顶元素。并且栈顶元素是栈中唯一一个活跃的元素,即栈顶元素处于活跃的状态,其他元素静止不动。

了解栈的结构之后,就相对容易理解在

方法调用时的内存分配————

         方法在执行时,代码片段(字节码文件)以及java中自带类库中的类对应的字节码文件在类加载时,有类加载器放入方法区当中,所以在三块主要的内存区域当中,方法区内存在代码开始执行前最先使用,字节码文件在方法区内存中只有一份,但是可以被重复的进行调用。

        代码的执行过程是在栈内存中分配内存的,每次调用一个方法的时候,会在栈内存中单独的为该方法的执行分配独立的活动场所(内存空间),此时发生的是压栈操作,在当前活跃的方法执行结束或者遇到return语句之后,在栈内存中,会发生弹栈动作,释放当前栈顶元素所占用的活动空间,栈帧下移,指向当前活跃的新的栈顶元素。

        因为遇到return语句之后,就会发生弹栈操作,所以return就相当于弹栈操作。

        而方法中定义的局部变量,包括内部定义的变量以及形参,都是在栈为该方法分配的内存单元中储存,所以局部变量在运行阶段内存是在栈中分配的。

        如下代码所示:

public class test3 { public static void main(String[] args) { pu(2); } public static void pu(int n){ int i=10; }}

        其内存分配图如下所示:

        当main方法开始执行,main压栈,然后当调用pu方法时,为pu方法的执行分配活跃的内存单元,因为该方法有自身的局部变量和形参,所以内部包含两个局部变量。 

        栈内存中如下图所示:

8、方法的重载 (overload)

        对于功能相似,参数不同(参数的顺序、数据类型或者参数的数目)的几个方法,可以利用方法的重载,简化代码的处理、编写和调用,优化代码。

例如:当对多种类型的两个数进行求和时,照常写的代码如下所示:

public static void main(String[] args) { int a=1; int b=2; float c=1; float d=2; int sumInt=sumInt(a,b); float sumFloat=sumFloat(c,d); System.out.println(sumInt); System.out.println(sumFloat); } public static int sumInt(int a,int b){ return a+b; } public static float sumFloat(float a,float b){ return a+b; }

运行结果:

33.0进程已结束,退出代码为 0

         但是很明显,sumInt和sumFloat的功能在本质上是相同的,但是却用了两个方法名去命名两个方法,这样在调用的时候,程序员需要记忆更多的方法相关信息,避免调用出现错误,同时增加了代码的编写复杂度。

        那么有没有一种方法能够让调用者感觉只调用了一个方法,但是实现了多种功能的效果呢?这就是方法重载的最根本的意义。

        如果利用方法重载,那么以上的程序只需要编写为:

public static void main(String[] args) { int a=1; int b=2; float c=1; float d=2; int sumab=sum(a,b); float sumcd=sum(c,d); System.out.println(sumab); System.out.println(sumcd); } public static int sum(int a,int b){ return a+b; } public static float sum(float a,float b){ return a+b; }

         很明显在代码的理解、编写和调用上更加的美观方便,一劳永逸,用一个方法名,传入不同的参数就可以实现不同的效果。

        经过观察,可以发现如果想要构成方法重载,那么在这几个重载方法之间,他们的方法名是相同的,但是参数列表不同。

参数的类型不同:可行

private static int sum(int a,int b){ return a+b; } public static float sum(float a,float b){ return a+b; }

参数的顺序不同:可行 

private static int sum(int a,float b){ return 0; } public static int sum(float a,int b){ return 1; }

参数的数目不同:可行

public static float sum(int a,int b){ return a+b; } public static int sum(int a){ return 2; }

 方法的修饰符列表不同:不可行

方法的返回值类型不同:不可行

所以方法的重载和方法名、参数有关,和修饰符列表以及方法的返回值无关;

即重载方法的方法名必须相同,参数必须不同,仅修改修饰符列表和返回值不能构成重载。

9、方法的递归调用

        递归调用:在一个方法的内部调用自身方法,注意要合理的利用,否则可能会导致无限次的调用,使代码的运行陷入死循环,最终导致内存的溢出,代码报错,并不能达到程序员的预期,方法的功能也就不存在意义了,所以递归一定要求停止的一个条件存在,保证这个调用环的结束和中止。

        所以递归操作就是一个不断的压栈,然后再出栈的过程,很消耗内存。尽可能少的利用递归,但是有时候递归很重要,能够解决一些具体的复杂问题,例如在求阶乘的时候

10、递归调用在JVM中的内存分配

public class test3 { static int sum=0; static int n=0; public static void main(String[] args) { Scanner in=new Scanner(System.in); int n=in.nextInt(); System.out.println(sum(n)); System.out.println(test(n)); } //不使用递归完成1~n的求和 public static int sum(int n){ int sum=0; for(int i=1;i<=n;i++){ sum+=i; } } //使用递归计算1~n的求和 public static int sum(int i){ if(i==1) return 1; else if(i>1){ return i+sum(i-1); }else{ return 0; } } //计算5的阶乘,不用递归 public static int test(int n){ int end=1; for(int i=n;i>=1;i--){ end=end*i; } return end; } //计算5的阶乘,用递归完成 public static int test(int n){ if (n==1) return 1; else return n*test(n-1); }

输入:515120120进程已结束,退出代码为 0

阶乘内存分配图:(仅展示栈内存)

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。