Java面向对象之方法重载、可变个数的形参、方法参数的值传递机制、递归(recursion)方法
1 - 方法的重载(over load)
/*
* 1-方法的重载(over load)
* ①概念:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
* ②特点:与返回值类型无关,只看参数列表,且参数列表必须不同(参数个数或参数类型)。调用时,根据方法参数列表的不同来区别。
*
* ③总结:"两同一不同" 即同一个类,相同的方法名。参数列表不同:参数个数不同,参数类型不同。
*
* 2-判断是否是重载:跟方法的权限修饰符,返回值类型,形参变量名,方法体都没有关系。
*
* 3-在通过对象调用方法时,如何确定某一个指定的方法:方法名 --> 参数列表
*
*/
代码示例(如下两个同名方法就够成方法重载)
// 以下示例就叫做方法重载 // 反转数组1(int[]) public void reverseArray(int[] array){ for(int i = 0;i < array.length / 2;i++){ int temp = array[i]; array[i] = array[array.length - i - 1]; array[array.length - i - 1] = temp; } } // 反转数组2(String[]) public void reverseArray(String[] array){ } // 注意看它们的方法名和参数
练习题
/* * 1-编写程序,定义三个重载方法并调用。方法名为mOL。三个方法分别接收一个int参 数、两个int参数、一个字符串。 * 分别执行平方运算并输出结果,相乘并输出结果,输出字符串信息,在主类的main()方法中分别用参数区别调用三个方法。 * * 2-定义三个重载方法max(), * 第一个方法求两个int值中的最大值, * 第二方法求两个double值中的最大值, * 第三个方法求三个double值的最大值, * 并分别调用三个方法 */ public class OverLoadTest { public static void main(String[] args) { // TODO Auto-generated method stub OverLoadTest load = new OverLoadTest(); load.mOL(5); load.mOL(4, 5); load.mOL("hello world"); double res1 = load.max(1.0, 2.0); System.out.println(res1); double res2 = load.max(20, 30); System.out.println(res2); double res3 = load.max(1, 2, 3); System.out.println(res3); } // 1.如下三个方法构成重载 public void mOL(int i) { System.out.println(i*i); } public void mOL(int j,int i) { System.out.println(j * i); } public void mOL(String s) { System.out.println(s); } // 2.如下三个方法构成重载 public int max(int i,int j) { // if(i > j) { // return i; // }else { // return j; // } return (i > j)? i : j; } public double max(double i,double j) { return (i > j)? i : j; } public double max(double d1,double d2,double d3) { double max = (d1 > d2)? d1 : d2; return (max > d3)? max : d3; } }
2 - 可变个数的形参
JavaSE 5.0 中提供了Varargs(variable number of arguments)机制,允许直接定 义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可 变的实参。
//JDK 5.0以前:采用数组形参来定义方法,传入多个同一类型变量 public static void test(int a ,String[] books);
//JDK5.0:采用可变个数形参来定义方法,传入多个同一类型变量 public static void test(int a ,String…books);
/*
* 可变个数形参的方法
* 1-jdk 5.0新增的内容
* 2-具体使用
* ①可变个数形参的格式:数据类型 ... 变量名
* ②当调用可变个数形参的方法时,传入的参数个数可以是:0个,1个,2个,或多个 ...
* ③可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载
* ④可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载,换句话说,二者不能共存。
* ⑤可变个数形参在方法的形参中,必须声明在末尾。
* ⑥可变个数形参在方法的形参中,最多只能声明一个可变形参。
*/
public class MethodArgsTest { public static void main(String[] args) { // TODO Auto-generated method stub } public void show(int i) { } public void show(String s) { } public void show(String ... strs) { } }
3-具体使用代码示例
public class MethodArgsTest { public static void main(String[] args) { // TODO Auto-generated method stub MethodArgsTest test = new MethodArgsTest(); test.show(1); test.show("hello world"); test.show("hello","world"); test.show(); } public void show(int i){ } public void show(String s){ System.out.println(s); } public void show(String ... strings){ // System.out.println("执行可变个数形参方法 ..."); for(int i = 0;i < strings.length;i++){ System.out.print(strings[i]+"\t"); } System.out.println(); } // public void show(String[] args){ // System.out.println(""); // } // 调用时:test.show(new String[]{"aa","bb"}); public void show(int i,String ... strings){ // 可变个数形参,必须声明在末尾,且最多只能声明一个。 } }
3 - 方法参数的值传递机制
1 方法,必须由其所在类或对象调用才有意义。若方法含有参数:
✔ 形参:方法声明时的参数
✔ 实参:方法调用时实际传给形参的参数值
2 Java的实参值如何传入方法呢?
Java里方法的参数传递方式只有一种:值传递。 即将实际参数值的副本 (复制品)传入方法内,而参数本身不受影响。
✔ 形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参
✔ 形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参
1-关于变量的赋值
/* * 关于变量的赋值 * 1-如果变量是基本数据类型,此时赋值的是变量所保存的数据值。 * 2-如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值 */ public class ValueTransferTest { public static void main(String[] args) { // 基本数据类型之局部变量赋值 int m = 20; int n = m; System.out.println("m="+m+", n="+n); n = 10; System.out.println("m="+m+", n="+n); // 引用数据类型 Order order1 = new Order(); order1.orderId = 50; Order order2 = order1; // order1 和 order2 的地址值相同,都指向了堆空间中的同一个实体 System.out.println("order1.Id="+order1.orderId+"\norder2.Id="+order2.orderId); order2.orderId = 100; System.out.println("order1.Id="+order1.orderId+"\norder2.Id="+order2.orderId); } } class Order{ int orderId; }
2-方法参数的值传递机制
/* * 方法的形参的传递机制:值传递 * 1-形参:方法定义时,声明的小括号内的参数 * 2-实参:方法调用时,实际传递给形参的数据 * * 3-值传递机制 * 如果形参是基本数据类型,此时实参赋给形参的是实参真实存储的数据值。 * 如果参数是引用数据类型,此时赋值的是变量所保存的数据的地址值。 */ public class ValueFransferTest { public static void main(String[] args) { // 交换两个变量的值操作 int m = 10; int n = 20; System.out.println("交换前:m="+m+"\tn="+n); // 交换前:m=10 n=20 // int temp = m; // m = n; // n = temp; ValueFransferTest test = new ValueFransferTest(); test.swap(m,n); System.out.println("交换后:m="+m+"\tn="+n); // 交换后:m=10 n=20 } public void swap(int m,int n) { int temp = m; m = n; n = temp; } }
图解
3-在声明的方法中实现 m 和 n 的值交换
public class ValueTransferTest { public static void main(String[] args) { Data data = new Data(); data.m = 10; data.n = 20; System.out.println("交换前:m="+data.m+"\tn="+data.n); // 交换 m 和 n 的值 // int temp = data.m; // data.m = data.n; // data.n = temp; ValueTransferTest test = new ValueTransferTest(); test.swap(data); System.out.println("交换后:m="+data.m+"\tn="+data.n); } public void swap(Data data){ int temp = data.m; data.m = data.n; data.n = temp; } } class Data{ int m; int n; }
图解
4-值传递机制总结(重点)
/*
* 如果形参是基本数据类型,此时实参赋给形参的是实参真实存储的数据值。
* 如果参数是引用数据类型,此时赋值的是变量所保存的数据的地址值。
*/
4 - 面试题
/* * 面试题: * 定义一个int型的数组,int[] arr = new int[]{12,3,3,34,56,77,43}; 让数组的每个位置上的值去除 以首位置的元素 * 得到的结果,作为该位置上的新值,遍历新的数组 */ public class ValueFransferTest { public static void main(String[] args) { int[] arr = new int[] {12,3,3,34,56,77,43}; // 写法1 // for(int i = arr.length - 1;i >= 0;i--) { // arr[i] = arr[i] / arr[0]; // } // 写法2 int temp = arr[0]; for(int i = 0;i < arr.length;i++) { arr[i] = arr[i] / temp; System.out.println(arr[i]); } } }
5 - 值传递机制练习
1-题目
/*
* 1-定义一个Circle类,包含一个double型的radius属性代表圆的半径
* 一个findArea()方法返回圆的面积
*
* 2-定义一个类PassObject,在 类中定义一个方法printAreas(),
* 该方法的定义如下:public void printAreas(Circle c,int time)
* 在printAreas方法中打印输出1到time之间的每个整数半径值,以及对应的面积
* 例如,times为5,则输出半径1,2,3,4,5,以及对应的圆面积。
* 在main方法中调用printAreas()方法,调用完毕后输出当前半径值。
*/
2-在 Circle.java 文件中
/* * 定义一个Circle类,包含一个double型的radius属性代表圆的半径 * 一个findArea()方法返回圆的面积 */ public class Circle { double radius; // 半径 // 求圆的面积 public double findArea(){ return Math.PI * radius * radius; } }
3- 在 PassObject.java 文件中
/* * * 2-定义一个类PassObject,在 类中定义一个方法printAreas(), * 该方法的定义如下:public void printAreas(Circle c,int time) * 在printAreas方法中打印输出1到time之间的每个整数半径值,以及对应的面积 * 例如,times为5,则输出半径1,2,3,4,5,以及对应的圆面积。 * 在main方法中调用printAreas()方法,调用完毕后输出当前半径值。 */ public class PassObject { public static void main(String[] args) { PassObject test = new PassObject(); test.printAreas(new Circle(),5); } public void printAreas(Circle c,int time){ System.out.println("Radius\t\tAreas"); for(int i = 1;i <= time;i++){ c.radius = i; System.out.println(c.radius+"\t\t"+c.findArea()); } } }
6 - 递归(recursion)方法
/* * 1-递归方法:一个方法体内调用它本身 * 2-方法递归包含了一种隐式的循环,它会重复执行某段代码,单这种重复执行无须循环控制 * 3-递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。 */ public class RecursionTest { public static void main(String[] args) { // 例题:计算1-100之间所有自然数之和 // 方式1 int sum = 0; for(int i =0;i <= 100;i++) { sum += i; } System.out.println(sum); // 方式2 RecursionTest test = new RecursionTest(); int res = test.getSum(100); System.out.println(res); } // 方式2 public int getSum(int n) { if(n == 1) { return 1; }else { return n + getSum(n - 1); } } }
经典例题:斐波那契数列
/* * 输入一个数据n,计算斐波那契数列(Fibonacci)的第n个值 1 1 2 3 5 8 13 21 34 55 * 规律:一个数等于前两个数之和 * 要求:计算斐波那契(Fibonacci)的第n个值,并将整个数列打印出来 */ public class FibonacciTest { public static void main(String[] args) { // 递归方法 FibonacciTest test = new FibonacciTest(); int res = test.Fibonacci(10); System.out.println(res); } public int Fibonacci(int n) { // 出口 if((n <= 2)) { return 1; } return Fibonacci(n - 1) + Fibonacci(n - 2); } }
总结递归的使用
1. 必须有一个明确的结束条件
2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出