方法是类和对象的重要组成部分,逻辑上要么属于类,要么属于对象,在Java方法中方法不能独立存在,必须要定义在类中。
一、方法的声明与调用
1.方法的声明
一个类中,声明格式为:
[修饰符]返回值类型 方法名([参数列表]){
方法体
}
(1)修饰符以及定义
- 修饰符可以同时有几个 如public static final
public final class Example2_3 {}
- public : 被其修饰的方法可被任意访问
- protected : 可以被其子类以及同一包中的类访问
- 缺省:没有修饰符,只能被同一包中的类访问
- private :只能在此类中被访问
- static : 静态方法,static修饰类时不能修饰外部类,只能修饰内部类
- final :最终方法,不能在子类中进行修改
- abstract :抽象方法,只允许声明,不允许实现,必须在具体的子类中实现
abstract class Animal{
/**
* 抽象类中本身存在构造器,便于子类实例化时调用
* 抽象方法只能有方法的声明,不能有方法体
* abstract不能修饰构造器(构造函数),属性(成员变量)
* abstract不能修饰私有方法、静态方法、final的方法,因为抽象类需要子类继承,与不能再被定义的规则相悖
* 抽象方法依靠与抽象类存在,即类不是抽象类则抽象方法不能在此类中定义
* 若子类没有重写父类中所有的抽象方法,则子类也是抽象类,需要用abstract修饰
*
*/
abstract void move();
}
(2)返回值类型
- 返回值是从方法体内向方法体外返回的数据内容
- 方法若没有返回值,返回值用void表示,用void修饰方法时,方法体内可出现return,功能是终止方法执行,此时return后面的表达式不会生效
- 方法若有返回值,返回值是数据类型,数据类型包括基本数据类型(8个)(byte、short 、int 、long、float、double、char、boolean)和引用类型(String)
- 方法的返回值不是void,方法体中要使用return关键字返回返回值
- return后面的表达式要保证方法在任何情况下都能返回相应数据类型的值
return 表达式; //表达式的数据类型要与方法体的返回值类型相同
public int getBoardNo(int boardNo) {
if (isAllInOneBoard()) {
if (boardNo == 1) {
return 1;
}
} else {
return boardNo;
}
return boardNo;//由于里面的条件语句未能完全覆盖,所以需要在条件语句外面返回数据;当符合条件语句时,执行了里面的return外面的retrun就不会再执行。
}
还有另外一种写法:
public int getBoardNo(int boardNo) {
if (isAllInOneBoard()) {
if (boardNo == Constants.DRIVER_BOARD_NO || boardNo == Constants.SIGNAL_BOARD_NO || boardNo == Constants.TEMPERATURE_BOARD_NO
|| boardNo == BoardID.SignalBoard || boardNo == BoardID.TemperatureMonitoringBoard || boardNo == BoardID.DriverBoard) {
return Constants.DRIVER_BOARD_NO;
}else {
return boardNo;//由于里面的条件语句完全覆盖,所以在条件语句里面返回数据,就不需要在条件语句外面返回数据了;
}
} else {
return boardNo;
}
}
(3)参数列表
- 参数类型、个数无限制,多参数用“,”隔开
people(String n, int a) {
name = n;
age = a;
}
2.方法的调用
前面提到静态方法(static),main函数是一个典型的静态方法,可以直接通过类名调用此方法,通过此分为四大类说明:静态方法中调用静态方法,静态方法中调用非静态方法,非静态方法调用静态方法,非静态方法中调用非静态方法
(1)静态方法调用静态方法
- 同一个类:方法名();
- 不同类:类名.方法名();
public class ExamplePrac2 {
public static void main(String[] args) {
isStatic();//静态调用静态方法
People.selfInfo();//不
}
private static void isStatic() {
System.out.println("静态调用静态方法");
}
}
class People {
static void selfInfo() {
System.out.println("不同类,静态调用静态方法");
}
}
(2)非静态方法调用静态方法
- 同一个类:方法名();
- 不同类:类名.方法名();
public class ExamplePrac2 {
private void unStatic() {
isStatic(); //同类,非静态类调静态类;
People.selfInfo();//不同类,非静态类调用静态类
}
private static void isStatic() {
}
}
class People {
static void selfInfo() {
}
}
(3)静态方法调用非静态方法
- 同一个类,需要通过对象来调用
- 不同类,也需要通过对象来调用
类名 对象名 = new 类名();
对象名.静态方法名();
public class ExamplePrac2 {
public static void main(String[] args) {
ExamplePrac2 examplePrac2 = new ExamplePrac2();//静态调非静态需要实例化对象
examplePrac2.unStatic();
People people = new People();//不同类,静态调非静态需要实例化对象
people.unSelfInfo();
}
private void unStatic() {
}
}
class People {
private void unSelfInfo() {
}
}
(4)非静态方法调用非静态方法
- 同一个类,方法名();
- 不同类,类名 对象名 = new 类名(); 对象名.静态方法名();
类名 对象名 = new 类名();
对象名.静态方法名();
public class ExamplePrac2 {
private void unStatic() {
unStatic1();//同类,非静态类调非静态类;
People people = new People();
people.unSelfInfo();//不同类,非静态类调非静态类;
}
private void unStatic1(){
System.out.println("非静态调用非静态方法");
}
}
class People {
static void selfInfo() {
System.out.println("不同类,静态调用静态方法");
}
void unSelfInfo() {
System.out.println("不同类,静态调用非静态方法");
}
}
(5)调用非静态方法和静态方法总结
- 调用静态方法时,同类可直接调用方法名(),不同类则是类.方法名()
- 只有同类非静态方法调用非静态方法时,可直接调用方法名();
其它情况下调用非静态方法均要实例化对象通过对象调用非静态方法,类名 对象名 = new 类名(); 对象名.静态方法名();
public class ExamplePrac2 {
public static void main(String[] args) {
isStatic();//静态调用静态方法
People.selfInfo();//不同类,静态调用静态方法
ExamplePrac2 examplePrac2 = new ExamplePrac2();//静态调非静态需要实例化对象
examplePrac2.unStatic();
People people = new People();//不同类,静态调非静态需要实例化对象
people.unSelfInfo();
}
private static void isStatic() {
}
private void unStatic() {
isStatic(); //同类,非静态类调静态类;
People.selfInfo();//不同类,非静态类调用静态类
unStatic1();//同类,非静态类调非静态类;
People people = new People();
people.unSelfInfo();//不同类,非静态类调非静态类;
}
private void unStatic1(){
}
}
class People {
static void selfInfo() {
}
void unSelfInfo() {
}
}
二、方法的参数传递机制
先回忆方法的定义:
[修饰符]返回值类型 方法名([参数列表]){
方法体
}
- 形参:参数列表中的参数
- 实参:调用方法时把实际值进行拷贝然后传递给形参的值
1.参数为简单类型时的传递情况
(1)举例
public class TransferParameter {
static void swap(int a, int b) {
int temp;
temp = a;
a = b;
b = temp;
System.out.println("交换中:a="+a+",b="+b);
}
public static void main(String[] args) {
int a = 5;
int b = 8;
System.out.println("交换前:a="+a+",b="+b);
swap(a, b);//给swap的形参赋值使其有值,参数传递过程拷贝完就结束
System.out.println( "交换结束后:a="+a+",b="+b );
}
}
输出结果:
交换前:a=5,b=8
交换中:a=8,b=5
交换结束后:a=5,b=8
(2)分析
- main方法中定义的两个变量a,b是实参,属于main方法的栈区
- swap方法中的两个变量a,b是形参,形参a,b是在swap方法内有效的局部变量,属于swap方法的栈区
- 两方法中的定义的变量a,b名称虽然相同,但是内存地址完全不同,也就是执行 swap(a, b)方法时,只是将main方法中变量a,b的实际值拷贝给到swap中形参a、b,拷贝完成后,参数的传递过程就结束了,接着执行swap方法中的语句,不会影响到main方法的a,b的值。当swap方法执行完后,程序返回到main方法,swap方法中的局部变量的生命周期结束,分配的内存被系统回收,过程参考下图:
- 简单类型传递是具体值传递,如果在被传递函数中改变了这个传进来的值,不会改变原始的值
2.参数为引用类型时的传递情况
(1)举例1
class Pass {
int height;
int weight;
Pass(int h, int w) {
height = h;
weight = w;
}
public static void change(Pass var) {
var.height = 160;
var.weight = 180;
}
}
public class Example3_3 {
public static void main(String[] args) {
Pass p = new Pass(170, 200);
System.out.println("调用方法前");
System.out.println("身高" + p.height);
System.out.println("体重" + p.weight);
p.change(p);
System.out.println("调用方法后");
System.out.println("身高" + p.height);
System.out.println("体重" + p.weight);
}
}
输出结果:
调用方法前
身高170
体重200
调用方法前var对象
160
180
调用方法后
身高160
体重180
(2)分析1
- 首先明确,创建一个对象时,系统内存中有两个东西,栈内存存放引用对象地址,堆内存存放的是对象本身具体的值,具体在Java基础学习二——如何创建与使用对象一节中有详细解释
- 接着分析,p是Pass类的一个对象,var也是Pass类的一个对象,程序在执行change方法时,将p对象的栈区地址传递给了var对象,此时,var对象和p对象的栈地址相同。因此调用change方法后,由于var对象和p对象指向同一栈内存地址,栈内存指向同一堆内存,所以,改变var引用变量的值即改变了main方法中所指向的变量的值。
- 引用类型传递是栈地址的传递,操作任何一个引用变量都会影响到在堆内存中实际对象的值
(3)举例2
//求平面直角坐标系中两个点之间的举例
class Point{
double x;
double y;
Point(double a,double b){
x=a;
y=b;
};
static double distance(Point p1,Point p2) {
return Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}
}
public class TextPoint {
public static void main(String[] args){
Point p1 = new Point(1,2);
Point p2 = new Point(4,6);
System.out.printf( "两个点之间的距离为: %.2f 米",p1.distance(p1,p2)); //保留两位小数
}
}
输出结果
两个点之间的距离为: 5.00 米
Process finished with exit code 0
(4)输出数据的格式控制
输出控制符 | 含义 |
%d | 表十进制整数 |
%s | 表示输出字符串 |
%f | 表示输出浮点数 |
%c | 表示输出字符 |
1.递归定义
- 递归必须要有结束条件,否则会进入到无限递归的状态
- 定义递归方法时要遵循“递归向着已知方向进行”
- 递归分为两个阶段:回溯阶段和递堆阶段,递归相当于数学归纳法,需要起始条件(结束条件)和递推公式
2.几个经典递归算法例子
(1)求n的阶层
写正式代码前先列出结局问题的办法
public class RecursionDemo {
/**
* n的阶层 f(n)=f(n-1)*n
* n>=1;f(1)=1
*/
private static int fac(int n){
int m;
if(n==1){
return 1;
}else {
return n*fac(n-1);
}
}
public static void main(String[] args){
System.out.println( fac(5));
}
}
求递归的过程:回溯找f(n),找结果的一个过程
(2)斐波那契数列
/**
* 数列:1,2,3,5,8...
* 分析:f(n)=f(n-1)+f(n-2);且f(1)=f(2)=1
*/
private static int fib(int n){
if(n==1||n==2){
return 1;
}
return fib(n-2)+fib(n-1);
}
public static void main(String[] args){
System.out.println(fib(8));
}
由于我忘记考虑n=2的情况,漏写了这一条件程序进入无限递归的状态,因此递归方法中结束条件很重要。
1.概念
- 在同一个类中,两个或两个以上的方法具有相同的名称和不同的形参,方法名和形参合称为方法头标识
- 重载的优点是程序更清晰、易读、便利
- 重载的方法,方法名必须相同,参数必须不同(指参数的类型、个数或顺序不同)
- 不能通过返回值类型和方法的修饰符来区别重载的方法
2.举例
class People {
static void selfInfo() {}
void unSelfInfo() {}
void unSelfInfo(String string) {}
private void unSelfInfo(String str, int i) {}
public void unSelfInfo(int i,String str){}
}