特殊参数之一:可变参数
在JDK1.5之后,当定义一个方法时,形参的类型可以确定,但是形参的个数不确定,那么可以考虑使用可变参数。可变参数的格式:
【修饰符】 返回值类型 方法名(【非可变参数部分的形参列表,】参数类型... 形参名){ }
可变参数的特点和要求:
(1)一个方法最多只能有一个可变参数
(2)如果一个方法包含可变参数,那么可变参数必须是形参列表的最后一个
(3)在声明它的方法中,可变参数当成数组使用
(4)其实这个书写...
【修饰符】 返回值类型 方法名(【非可变参数部分的形参列表,】参数类型[] 形参名){ }
只是后面这种定义,在调用时必须传递数组,而前者更灵活,既可以传递数组,又可以直接传递数组的元素,这样更灵活了。
1、方法只有可变参数
案例:求n个整数的和
package com.haogu.test05.param; public class NumberTools { int total(int[] nums){ int sum = 0; for (int i = 0; i < nums.length; i++) { sum += nums[i]; } return sum; } int sum(int... nums){ int sum = 0; for (int i = 0; i < nums.length; i++) { sum += nums[i]; } return sum; } }
package com.haogu.test05.param; public class TestVarParam { public static void main(String[] args) { NumberTools tools = new NumberTools(); System.out.println(tools.sum());//0个实参 System.out.println(tools.sum(5));//1个实参 System.out.println(tools.sum(5,6,2,4));//4个实参 System.out.println(tools.sum(new int[]{5,6,2,4}));//传入数组实参 System.out.println("------------------------------------"); System.out.println(tools.total(new int[]{}));//0个元素的数组 System.out.println(tools.total(new int[]{5}));//1个元素的数组 System.out.println(tools.total(new int[]{5,6,2,4}));//传入数组实参 } }
2、方法包含非可变参数和可变参数
-
非可变参数部分必须传入对应类型和个数的实参;
-
可变参数部分按照可变参数的规则传入0~n个对应类型的实参或传入1个对应类型的数组实参;
案例:
n个字符串进行拼接,每一个字符串之间使用某字符进行分割,如果没有传入字符串,那么返回空字符串""
package com.haogu.test05.param; public class StringTools { String concat(char seperator, String... args){ String str = ""; for (int i = 0; i < args.length; i++) { if(i==0){ str += args[i]; }else{ str += seperator + args[i]; } } return str; } }
package com.haogu.test05.param; public class StringToolsTest { public static void main(String[] args) { StringTools tools = new StringTools(); System.out.println(tools.concat('-')); System.out.println(tools.concat('-',"hello")); System.out.println(tools.concat('-',"hello","world")); System.out.println(tools.concat('-',"hello","world","java")); } }
5.5.2 特殊参数之二:命令行参数(了解)
通过命令行给main方法的形参传递的实参称为命令行参数
public class TestCommandParam{ //形参:String[] args public static void main(String[] args){ System.out.println(args); System.out.println(args.length); for(int i=0; i<args.length; i++){ System.out.println("第" + (i+1) + "个参数的值是:" + args[i]); } } }
命令行:
java TestCommandParam
java TestCommandParam 1 2 3
java TestCommandParam hello haogu
5.5.3 方法的参数传递机制
方法的参数传递机制:实参给形参赋值,那么反过来形参会影响实参吗?
-
方法的形参是基本数据类型时,形参值的改变不会影响实参;
-
方法的形参是引用数据类型时,形参地址值的改变不会影响实参,但是形参地址值里面的数据的改变会影响实参,例如,修改数组元素的值,或修改对象的属性值。
-
注意:String、Integer等特殊类型容易错
-
1、形参是基本数据类型
案例:编写方法,交换两个整型变量的值
package com.haogu.test05.param; public class PrimitiveTypeParam { void swap(int a, int b){//交换两个形参的值 int temp = a; a = b; b = temp; } public static void main(String[] args) { PrimitiveTypeParam tools = new PrimitiveTypeParam(); int x = 1; int y = 2; System.out.println("交换之前:x = " + x +",y = " + y);//1,2 tools.swap(x,y);//实参x,y是基本数据类型,给形参的是数据的“副本”,调用完之后,x与y的值不变 System.out.println("交换之后:x = " + x +",y = " + y);//1,2 } }
2、形参是数组
package com.haogu.test05.param; public class ArrayTypeParam { void sort(int[] arr){//给数组排序,修改了数组元素的顺序,这里对arr数组进行排序,就相当于对nums数组进行排序 for (int i = 1; i < arr.length - 1; i++) { for (int j = 0; j < arr.length - i - 1; j++) { if(arr[j] > arr[j+1]){ int temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } } void iterate(int[] arr){//输出数组的元素,元素之间使用空格分隔,元素打印完之后换行 //这个方法没有修改元素的值 for (int i = 0; i < arr.length; i++) { System.out.print(arr[i]+" "); } System.out.println(); } public static void main(String[] args) { ArrayTypeParam tools = new ArrayTypeParam(); int[] nums = {4,3,1,6,7}; System.out.println("排序之前:"); tools.iterate(nums);//实参nums把数组的首地址给形参arr,这个调用相当于输出nums数组的元素 //对数组的元素值没有影响 tools.sort(nums);//对nums数组进行排序 System.out.println("排序之后:"); tools.iterate(nums);//输出nums数组的元素 //上面的代码,从头到尾,堆中只有一个数组,没有产生新数组,无论是排序还是遍历输出都是同一个数组 } }
3、形参是引用数据类型
package com.haogu.test05.param; public class ReferenceTypeParam { void swap(MyData my){//形参my是引用数据类型,接收的是对象的地址值,形参my和实参data指向同一个对象 //里面交换了对象的两个实例变量的值 int temp = my.x; my.x = my.y; my.y = temp; } public static void main(String[] args) { ReferenceTypeParam tools = new ReferenceTypeParam(); MyData data = new MyData(); data.x = 1; data.y = 2; System.out.println("交换之前:x = " + data.x +",y = " + data.y);//1,2 tools.swap(data);//实参是data,给形参my的是对象的地址值,调用完之后,x与y的值交换 System.out.println("交换之后:x = " + data.x +",y = " + data.y);//2,1 } }
public class MyData{ int x; int y; }
4、形参指向新对象
package com.haogu.test05.param; public class AssignNewObjectToFormalParam { void swap(MyData my){ my = new MyData(); //这里让my形参指向了新对象,此时堆中有两个MyData对象,和main中的data对象无关 int temp = my.x; my.x = my.y; my.y = temp; } public static void main(String[] args) { //创建这个对象的目的是为了调用swap方法 AssignNewObjectToFormalParam tools = new AssignNewObjectToFormalParam(); MyData data = new MyData(); data.x = 1; data.y = 2; System.out.println("交换之前:x = " + data.x +",y = " + data.y);//1,2 tools.swap(data);//调用完之后,x与y的值交换? System.out.println("交换之后:x = " + data.x +",y = " + data.y);//1,2 } }
5.6 方法的重载
-
方法重载:指在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可,与修饰符和返回值类型无关。
-
参数列表:数据类型个数不同,数据类型不同(按理来说数据类型顺序不同也可以,但是很少见,也不推荐,逻辑上容易有歧义)。
-
重载方法调用:JVM通过方法的参数列表,调用匹配的方法。
-
先找个数、类型最匹配的
-
再找个数和类型可以兼容的,如果同时多个方法可以兼容将会报错
-
案例,用重载实现:
(1)定义方法求两个整数的最大值
(2)定义方法求三个整数的最大值
(3)定义方法求两个小数的最大值
(4)定义方法求n个整数最大值
package com.haogu.test06.overload; public class MathTools { //求两个整数的最大值 public int max(int a,int b){ return a>b?a:b; } //求两个小数的最大值 public double max(double a, double b){ return a>b?a:b; } //求三个整数的最大值 public int max(int a, int b, int c){ return max(max(a,b),c); } //求n整数的最大值 public int max(int... nums){ int max = nums[0];//如果没有传入整数,或者传入null,这句代码会报异常 for (int i = 1; i < nums.length; i++) { if(nums[i] > max){ max = nums[i]; } } return max; } }
1、找最匹配的
package com.haogu.test06.overload; public class MethodOverloadMosthMatch { public static void main(String[] args) { MathTools tools = new MathTools(); System.out.println(tools.max(5,3)); System.out.println(tools.max(5,3,8)); System.out.println(tools.max(5.7,2.5)); } }
2、找唯一可以兼容的
package com.haogu.test06.overload; public class MethodOverloadMostCompatible { public static void main(String[] args) { MathTools tools = new MathTools(); System.out.println(tools.max(5.7,9)); System.out.println(tools.max(5,6,8,3)); // System.out.println(tools.max(5.7,9.2,6.9)); //没有兼容的 } }
5.7 方法的递归调用
递归调用:方法自己调用自己的现象就称为递归。
递归的分类:
-
递归分为两种,直接递归和间接递归。
-
直接递归称为方法自身调用自己。
-
间接递归可以A方法调用B方法,B方法调用C方法,C方法调用A方法。
注意事项:
-
递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出。
-
在递归中虽然有限定条件,但是递归深度不能太深,否则效率低下,或者也会发生栈内存溢出。
-
能够使用循环代替的,尽量使用循环代替递归
-
案例:计算斐波那契数列(Fibonacci)的第n个值,斐波那契数列满足如下规律,
1,1,2,3,5,8,13,21,34 ....
即从第三个数开始,一个数等于前两个数之和。假设f(n)代表斐波那契数列的第n个值,那么f(n)满足:
f(n) = f(n-2) + f(n-1);
package com.haogu.test07.recursion; public class FibonacciTest { public static void main(String[] args) { FibonacciTest t = new FibonacciTest(); //创建FibonacciTest的对象,目的是为了调用f方法 for(int i=1; i<=10; i++){ System.out.println("斐波那契数列第" +i +"个数:" + t.f(i)); } System.out.println(t.f(20));//6765 } //使用递归的写法 int f(int n){//计算斐波那契数列第n个值是多少 if(n<1){//负数是返回特殊值1,表示不计算负数情况 return 1; } if(n==1 || n==2){ return 1; } return f(n-2) + f(n-1); } }
5.8 对象数组
数组是用来存储一组数据的容器,一组基本数据类型的数据可以用数组装,那么一组对象也可以使用数组来装。
即数组的元素可以是基本数据类型,也可以是引用数据类型。当元素是引用数据类型是,我们称为对象数组。
注意:对象数组,首先要创建数组对象本身,即确定数组的长度,然后再创建每一个元素对象,如果不创建,数组的元素的默认值就是null,所以很容易出现空指针异常NullPointerException。
对象数组的声明和使用
案例:
(1)定义矩形类,包含长、宽属性,area()求面积方法,perimeter()求周长方法,String getInfo()返回圆对象的详细信息的方法
(2)在测试类中创建长度为5的Rectangle[]数组,用来装3个矩形对象,并给3个矩形对象的长分别赋值为10,20,30,宽分别赋值为5,15,25,遍历输出
package com.haogu.test08.array; public class Rectangle { double length; double width; double area(){//面积 return length * width; } double perimeter(){//周长 return 2 * (length + width); } String getInfo(){ return "长:" + length + ",宽:" + width + ",面积:" + area() + ",周长:" + perimeter(); } }
package com.haogu.test08.array; public class ObjectArrayTest { public static void main(String[] args) { //声明并创建一个长度为3的矩形对象数组 Rectangle[] array = new Rectangle[3]; //创建3个矩形对象,并为对象的实例变量赋值, //3个矩形对象的长分别是10,20,30 //3个矩形对象的宽分别是5,15,25 //调用矩形对象的getInfo()返回对象信息后输出 for (int i = 0; i < array.length; i++) { //创建矩形对象 array[i] = new Rectangle(); //为矩形对象的成员变量赋值 array[i].length = (i+1) * 10; array[i].width = (2*i+1) * 5; //获取并输出对象对象的信息 System.out.println(array[i].getInfo()); } } }