第六讲 关于方法

第六讲 关于方法

1 关于程序设计的理念

关于程序设计:
	我们在学习程序设计的时候,该以什么样的心态和角色去学习?
		- 程序设计,核心是设计,我们应该以设计者的角色去学习
		- 何为设计?设计的主体是使用者,也就是说我们是设计者,我们要给用户设计软件,满足用户的需求。
		- 用户对需求有什么样的要求?或者是说怎样叫做满足用户需求?简单来讲,用户希望我们做出来的东西能够让他们满意。
		- 因此,我们时时刻刻都要想着,怎样让用户满意。那么我们在设计软件的时候、设计程序的时候,要考虑很多因素。我们的代码要漂亮,要简洁高效,同时我们的软件要能够让用户使用起来方便、快捷、稳定...关键是要实现用户想要的功能。
		- 我们在学习的过程中要建立起设计者的角色定位,要在学习的过程中保持以用户需求为目标,我们在设计、实现我们的程序的时候,要实时的考虑功能性、健壮性、安全性等等。而不是简单地实现了事。
		- 题外话,关于设计。我们本身就应该要保持设计的理念来处理日常的生活、工作、学习,这不仅在程序设计中,我们的人生也是如此。我们要学会做规划,这是一个对于未来的设计。另外,每天做的事情,我们也要做设计,这些设计都是为了人生的厚度做准备的。没有设计,就浑浑噩噩的,一塌糊涂。

2 关于方法

1 方法是什么?
	方法是处理业务逻辑的。我们刚谈了设计,我们是设计者,为用户设计软件,那么用户的需求或者是说用户需要使用一些功能,我们进行实现,这些功能或者叫做业务,有一定的逻辑,这些东西叫做业务逻辑。方法就是处理这些业务逻辑的。
	比如println()方法,用来向控制台输出用户想要输出的结果。这就是一个方法,它用来处理业务逻辑。
	
2 定义方法的原则
	一个方法应该尽可能的单一,也就是说一个方法尽可能的只处理一个业务逻辑。不要很多业务逻辑在同一个方法中实现。
	比如:println()方法中,除了打印的业务逻辑之外,还有计算+-*/的业务逻辑,你让用户怎么用?
	sum(int x, int y) {
		// 这里有没有必要自己再重新写一套关于如何输出的业务逻辑?
		// 我们只希望在这里做求和运算而已,如果要用到print这样的业务逻辑的时候
		// 我们可以调用print相关的方法。
	}
3 关于方法的调用
	我们知道main()方法是入口,它可以调用其他的方法,这些方法包括了java中已写好的一些方法,这些方法是SUN公司java团队写好的,提供给广大开发者用的。也可以包括程序员自己定义的方法。
	自己定义的方法也可以调用库中的方法,也可以调用自己写的其他的方法。C语言中也是这么做的。
	int printSum() {
		printf(sum());
	}

4 关于方法的返回值
	我们知道方法是用来处理业务逻辑的,也可以理解为处理某些数据从而得到程序员/用户想要得到的结果的。这种结果分为两种,第一种不需要给出去的,也就是说在该方法中处理完了就了事了,不需要给其他的方法,或者是没有其他的用途。另一种是需要给出去的,需要给到其他的方法或者是其他的地方,供它们来使用。
	我们知道方法返回结果的时候,使用的是return,执行遇到了return该方法结束,并将值返回。不用return,有没有其他的办法返回结果呢?在C语言中,全局变量能够保存返回值。在java中我们说了没有全局这个概念了,因为java是面向对象的,那么在java中也可以用某种变量来保存方法的返回值。
	关于有返回值的方法:返回值的类型一定要与方法名前的返回值类型相匹配,否则编译报错。一个方法返回了结果,调用该方法的时候,我们可以接收也可以不接收这个返回值。

5 关于方法的执行
	方法在什么时候执行?方法在调用的时候被执行,没有被调用的方法,是不会被执行的。我们在定义方法的时候,在其他方法中没有调用该方法,该方法永远不会被执行。有一个方法不需要被调用也会被执行,这个方法是main方法,因为他是应用程序的入口,jvm在加载完程序后会自动寻找main方法,如果找不到,会直接报错。编译时,没有main方法是可以的。因为编译只要符合词法、语法的规则。运行则不然,一个程序要运行起来,一定要有一个入口,该入口就是main()。main方法,在java中没有返回值,也就是说main中一般情况下是没有return的,那么什么时候main方法运行结束呢?
	当main方法中的代码执行完比之后,就会自动结束。因为java中代码是顺序执行的。当最后一行代码执行完毕之后,下一条是}的时候,就自动结束了。
	那么,main方法运行结束,程序是否结束了?不是。这涉及到多线程。多线程是未来要讲的重点。这里先不说。

6 再次理解什么是方法
	首先我们要理解什么是程序?程序包括哪些部分?
	程序包括了两部分:数据、处理数据的方法。数据结构是组织数据的,算法是处理数据的,怎样处理的?靠的是组织指令。如,查找算法,可以暴力查找,也可以用二分查找,这些东西无非是如何使用for循环,说白了就是如何编排语句。
	方法就是对语句的编排。也就是对指令的组织。所以说,方法是用来组织处理数据的指令的。
	方法只针对某个具体的数据吗?比如sum(100,200)?当然不是,方法可以看成是提供某一类功能的一个公式。它具有通用性。
	

3 关于jvm的内存结构(重点)

# 关于栈:
	栈是一种数据结构(stack),栈是一种先进后出(FILO),或者是说后进先出(LIFO)的数据结构。也就是说数据存入栈中,先存入的只能是最后才能取到。最后存入的,最先被取到。栈只有一个入口,压栈、弹栈都从这个口子进行。因此有了LIFO.栈是有大小的,如果满了,就不能再往栈中放入数据。超出了之后,就会溢出。stack over flow
	如果我们用C语言去实现,无非是一个一维的数组,数组存入数据的时候从0开始,到n-1;取的时候,我们可以从最后一个开始取。往栈中存入数据的时候,叫做压栈push,从栈中取出数据的时候,叫弹栈pop。
在jvm中,存在内存空间,最主要的内存空间有三块
	其中一块叫做栈内存空间,这个内存空间存入和取出数据遵循先进后出(后进先出)。是栈的特性。因此,我们将这块空间取名栈内存空间。这一部分要深入理解,就要讲到汇编。后面有机会给你们讲汇编。另一个叫做堆内存空间,还有一个叫做方法区内存空间。
注意:
	栈内存空间采用的是栈的操作,先进后出,后进先出。但是堆内存空间,不是堆的操作,我们只是将这块空间叫做堆内存空间。同样方法区内存空间,不是用来存放方法的,与方法无关,只不过我们叫方法区内存空间。
这三块内存空间有什么用?都是用来存放什么东东的?(超级重点)
	1、方法区内存空间:存放代码片段,存放常量、静态(被static修饰)的数据(这个后面会详细说)。
	解释:我们知道xxx.java文件是开发者写好的源文件,这个无法在JVM上运行,JVM运行的是编译好的文件,即.class文件。
	我们知道,我们的源文件在硬盘上,就是说xxx.java文件写好之后,就存在硬盘上。编译后生成的.class文件存放在哪里?也在硬盘上。
	硬盘上的东西能直接跑起来吗?不能。需要加载到内存中。我们以前就说过,只有将代码加载到内存中,才能够将这些代码的执行交给CPU。
	我们知道,只有将.class文件加载到JVM中,程序才能运行,.class文件中保存的代码的二进制形式,.class文件中的内容要放到某个内存中才能让程序执行。
	我们又知道,java程序只能跑在JVM上,JVM就有一块内存空间用来存放代码。存放的是.class文件中的内容。只不过我们没有做什么操作,只是使用java命令,让.class文件进行了加载和运行。这个加载的过程是有指令帮我们做了。不是由我们双击鼠标或者是其他的方式。那么存放这些.class文件的内存空间叫做方法区内存空间。这个加载的过程叫做类加载(这是一种机制,未来再说,很有用)。这个空间存放的代码的二进制形式。只有将代码加载到内存中,CPU才有可能执行,否则没法执行。
	我们在桌面上双击一个.exe文件,实际是将程序的代码加载到内存的过程,只有这样该代码才能执行。否则,在硬盘上存放的程序,都无法执行。CPU是大脑,但是它能处理的数据来自于寄存器,只跟寄存器通信,寄存器呢只跟内存和CPU交互。不会直接去硬盘上取。因为内存很有限,而硬盘很大,存取速度很慢。内存的存取速度快一些,寄存器存取速度非常快,但寄存器很小。
	除了存放.class文件中的代码之外,这部分内存还存放了常量、静态的数据等等(这个以后我们再详细讲解)。

2 栈内存空间
	这是一个栈结构的内存空间,它先进后出或者说是后进先出。栈内存空间的作用是,当方法被执行的时候,是在栈内存空间中分配内存用以执行方法。那么,方法中的参数、方法中定义的变量及数据,在这里分配空间存储(当前就这么理解。不完全是这样。讲面向对象的时候我们还会完善)。
	当方法被调用且执行的时候,方法会压栈,方法执行结束后,会弹栈。压栈就是进入栈内存空间,弹栈就是退出栈内存空间。

3 堆内存空间,这个我们暂时用不到,我们后面再说。它的作用很重要。相当于C语言中用malloc、new申请到的空间都出自这里。
public class JVMTest02 
{
	public static void main(String[] args) 
	{
		int x = 10;
		int y = 20;
		int res = 0;
		res = sum(x, y); 
		System.out.println(res);
	}

	static int sum(int x, int y) {
		int res = x + y;
		return res;
	}
}

在这里插入图片描述

  • 注意
static 修饰的变量以及常量是放在方法区内存中的。
栈内存空间中,只存放局部变量,局部变量包括:方法的参数,方法内定义的非静态变量,以及循环条件中的参数。
for (int i = 0; ;)// 这种方式,循环执行完毕,i不存在了
// 下面的方式,循环结束i还在栈中存在。
int i = 0;
for (i;;)

记住,方法在栈中执行,方法中的局部变量在栈中分配空间。方法运行完毕后,出栈,该方法所使用到的栈内存空间都会释放。
public class JVMTest03 
{
	public static void main(String[] args) 
	{
		System.out.println("main start!");
		m1();
		System.out.println("main over!");
	}
	static void m1() {
		System.out.println("----m1----start!");
		m1();
		System.out.println("----m1----over!");
	}
}
// 这是一个没有结束条件的递归
// 递归是怎么运行的?会不断地入栈,
// 因为没有出口,所以不停地在栈内存空间中要内存。
// 直到栈内存空间溢出。jvm停止运行。JVM不停止,还能工作吗?不能
// 我们在日常开发中尽量避免使用递归,有边界的递归也有可能出现StackOverFlowError。
// 因为递归太深了之后,可能造成该异常。
// 当然,有些问题必须要用递归,那么我们要小心,检查递归的出口条件。
// 如果出口条件正确,但是无法解决递归深度的问题,
// 那么我们应该对JVM栈内存空间的大小进行设置。
// 修改jvm栈内存大小的方法:java -Xss xxxM/GB xxx.java。这些是对JVM进行调优的。
// 关于递归,能用for/while循环解决的问题,不要递归,递归很危险。
// 当我们在写项目的时候,如果方法嵌套得太深,也有可能出现这些问题。

在这里插入图片描述

4 方法的重载

重载(overload)
	方法名是可以同名的,因为java中有这样的机制,叫做方法重载。
	方法重载机制发生的条件:(这三个条件一定要同时满足)
		1. 在同一个类中
		2. 方法名相同
		3. 方法的参数列表不相同。
			参数类型不同
			参数个数不同
			参数位置不同
public class OverloadTest01 
{
	public static void main(String[] args) 
	{
		System.out.println("Hello World!");
		System.out.println('a');
		System.out.println(100);
		System.out.println(3.14F);
	}
}
println()方法怎么做到能打印各种数据类型的内容?
	我们知道,一个方法要么有参数列表,要么没有参数列表。只有这两种情况。
	如果一个方法有参数,给该方法传入实参的时候,一定要对应个数和类型。
	如:sum(int x, int y)------------>sum(3, 2)-------X--??--> sum(33.0, 2.0)、sum(3)、sum(3, 2, 1)
	在上面的代码中,println()方法只接收一个参数。那么我们可以猜测println方法一定有以下几种形式,而且这些形式是并存的:
	println(int arg)
	println(String arg)
	println(char arg)
	println(float arg)
	方法名能重复吗?可以
	如果方法名不能重复,会出现什么问题?
	用户需求:张三是用户,他说,请你帮我开发一个程序,该软件能够计算两个数的和。张三说他希望简单,一个窗口 __ + __ = __就够了。
	
	我是设计者,我的思考如下:
		首先,拆解需求:两个数,和,这里可以确定,和就是相加,我用加法就可以了。
		两个数可以有哪些形式?两个整数相加、两个小数相加,一个整数+一个小数,一个小数+一个整数
		我来做设计:定义一个方法,名字叫做add()
		两个整数:int addInt(int x, int y)
		两个小数:double addDouble(double x, double y)
		一个整数一个小数: double addIntAndDouble(int x, double y)
		一个小数一个整数: double addIntAndDouble(double x, int y)
		
		这样写代码感觉很多冗余。
		我的想法是这样的:对于张三来说,它只要看到一个窗口,那么我只给他提供一个方法叫做add(),它传入什么样的参数,我就匹配什么样的参数。这样的话,用户使用起来很舒服。
		
		
public class Add 
{
	static int add(int x, int y){
		return x + y;
	}
	static double add(double x, double y){
		return x + y;
	}
	static double add(int x, double y){
		return x + y;
	}
	static double add(double x, int y){
		return x + y;
	}
}
像这样没有main方法的类,里面定义了一些方法,功能相似,它不能单独运行,这样的做法叫做封装。
    
public class OverloadTest01 
{
	public static void main(String[] args) 
	{
		
		System.out.println(Add.add(100, 199));
		System.out.println(Add.add(1.0, 100.8));
		System.out.println(Add.add(100, 199.5));
		System.out.println(Add.add(1.0, 100));
	}

}
这也解释了方法重载的好处。
    
解释一下:
    这两个文件在同一个文件夹下,Add.class, 一个是OverloadTest01.class,一定要在同一个文件夹下。
    Add.add()这种调用方法,一定是静态的方法也就是被static修饰的方法才能直接用类名去调用。
    非静态的不能这样做。这样做编译报错。为什么不能,我们后面再详细讲。
    不能是Add.java源文件,这个东西不会被加载到内存中,只有Add.class文件才会被加载到内存中。所以一定要编译,两个文件都要编译,才能正常执行。

作业:
        写递归,画内存图,测试,图、代码、分析思路放到博客上,不要照抄。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值