------- android培训、java培训、期待与您交流! ----------
函数与数组
一、函数
1.1、什么是函数?
函数就是定义在类中的具有特定功能的一段独立小程序。函数也称为方法。
函数的格式:
修饰符 返回值类型 函数名(参数类型 形式参数1, 参数类型 形式参数2, ...)
{
执行语句;
Return 返回值;
}
返回值类型:函数运行后的结果的数据类型。
参数类型:是形式参数的数据类型。
形式参数:是一个变量,用于存储调用函数时传递给函数的实际参数。
实际参数:传递给形式参数的具体数值。
return:用于结束函数。
返回值:该值会返回给调用者。
1.2、如何定义一个新函数?
(1) 既然函数是一个独立的功能,那么该功能的运算结果是什么先明确;
明确有没结果: 函数的返回值.
(2) 再明确在定义该功能的过程中是否需要未知的的内容参与运算;
明确有没未知内容: 函数的参数列表 (参数的类型和参数的个数).
1.3、函数的特点:
1)、定义函数可以将功能代码进行封装。
2)、便于对该功能进行复用。
3)、函数只有被调用才会被执行。
4)、函数的出现提高了代码的复用性。
5)、对于函数没有具体返回值的情况,返回值类型用关键字void表示,那么该函数中的return语句如果在最后一行可以省略不写。
注意:
函数中只能调用函数,不可以在函数内部定义函数。
定义函数时,函数的结果应该返回给调用者,交由调用者处理。
例如:
class FunctionDemo
{
public static void main(String[] args)
{
getResult(4);
}
public static void getResult(int num)
{
System.out.println(num*3+5);
return; //可以省略
}
}
1.4、构造方法
构造方法时一种特殊的类的方法,方法名与类名相同,而且没有返回类型,也不需要void。构造方法的作用在于对象创建时初始化对象,给实例化对象的成员变量赋初值。
构造方法的格式:
public <类名> ([参数列表]){
......
}
注意:构造方法不能像成员方法那样被直接调用,只能在通过new运算符实例化一个对象时,由系统自动调用,实现对成员变量初始化的作用。
例如:
Book book = new Book(); //调用构造方法,实例化一个对象Book
1.5、函数的重载(Overload)
在同一个类中,多个方法具有相同的方法名,但却具有不同的的参数列表,方法之间的这种关系称为方法重载。方法重载中的参数列表必须不同,也就是说,参数个数、参数类型或参数顺序不同,至少三者中有一项不同。
特点:与返回值类型无关,只看参数列表。
什么时候用重载?
当定义的功能相同,但参与运算的未知内容不同。那么,这时就定义一个函数名称以表示起功能,方便阅读,而通过参数列表的不同来区分多个同名函数。
以下是方法重载的示例:
public void a(int a){}
public int a(){}
public void a(int a,String s){}
1.6、可变参数
从java5开始出现了可变参数,这是对java方法及数组的拓展!方法中可以接受的参数不再是固定个数的,而是随着具体需求传递的多少来决定。
定义格式: 返回值类型 方法名(参数类型 ... 形式参数){ }
可变参数的特点:
只能出现在参数列表的最后;
... 位于变量类型和变量名之间,前后有无空格都可以;
调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数。
例如:
public static int add(int x, int ... args)
{
int sum=x;
for(int i=0;i<args.length;i++)
{
sum+=args[i];
}
return sum;
}
二、数组(Array)
2.1、数组的概念
数组是具有相同数据类型的一组数的有序集合,数组中的每个数据称为数组的元素,数组中各元素具有相同的数据类型,且在内存中是连续存放的,通过下标来区分数组中的不同元素。
格式:
数据类型[] 数组名;
或者
数据类型 数组名[];
以上两种格式都能声明一个数组,其中数据类型既可以是基本数据类型,也可以是引用数据类型,其中 []是必不可少的,代表声明的是数组变量而不是普通变量,数组名可以是任意合法的变量名。
例如:
int [] score; //声明了一个名称为score的数组,数组里的元素都是int型
String name[]; //声明了一个名称为name的数组,数组里的元素都是String型
备注:
1)、数组的好处:数组里的每个元素都有编号,编号从0开始,并且依次递增,方便操作这些元素;
2)、使用Java数组:必须先声明数组,再给该数组分配内存;
3)、数组对应在内存中一段连续空间。
4)、数组元素必须是相同数据类型,也可以是引用数据类型,但是同一个数组中的元素必须是同一类数据类型。
2.2、一维数组
静态初始化:初始化时由我们自己指定每个数组元素的初始值,由系统决定需要的数组长度;
格式:
元素类型[] 数组名 = new 元素类型[]{元素1,元素2,元素3,...元素n};
或者
元素类型[] 数组名 = {元素1,元素2,元素3,...元素n};
动态初始化:初始化时由我们指定数组的长度,由系统为数组元素分配初始值;
格式:
元素类型[] 数组名 = new 元素类型[元素个数或数组长度];
数组的长度一旦确定,就不能改变,也就数组是定长的。
静态和动态初始化数组不能同时使用,也就是说不能同时指定数组的长度和元素。
2.3、二维数组
二维数组:其实是一个一维数组,它的每一个元素又是一个一维数组,可以看做是一张表格。
初始化:
静态初始化:
int[ ][ ] arr = new int[][]{{1,2},{3,4},{5,6}};
int[ ][ ] arr = {{1,2},{3,4},{5,6}};
动态初始化:
int[][] arr = new int[3][4];
定义了名字为arr的二维数组,该二维数组中有3个一维数组,每个一维数组中有4个元素。
2.4、使用java.util.Arrays类操纵数组
boolean equals(数组1,,数组2) :比较两个数组是否相等。
void fill(数组,值):将指定的值分配给数组中的每个元素。
void sort(数组):将数组中的元素按照升序排序。
int binarySearch(数组,值):在调用此方法前必须先对数组进行排序,该方法按照二分查找算法查找数组是否包含指定的值,如果包含,则返回该值在数组中的索引;如果不包含该值,则返回负值。
2.5、数组的内存分配及特点
任何一个应用程序在运行时都要在内存中开辟一个空间,其中Java程序在运行时,需要在内存中的分配空间。为了提高运算效率,对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。Java在内存中一共开辟了五个空间来进行自己数据的存储。分为:栈内存,堆内存,方法区,本地方法区,寄存器。
2.5.1栈内存:
例如,当在主函数中定义int x=3的时候这个3在哪?其实这个3就在主函数的内存区域当中,主函数运行的时候会在内存中开辟一空间,这个空间里面就会有一个变量x,它的值等于3。如果这时又写了一个show()方法,那么show方法也会在内存中开辟空间,如果这时候在show()方法中也定义一个int x=3;那么这是不冲突的,因为这两个x=3就不在一个区域中。当show方法运行结束,那么show()所占的内存也就随着释放了,如果show方法里面有一个for循环,循环里定义了intx=5;那么当for循环结束,循环里的x就被释放了。到这就可以发现栈内存的特点了。
栈内存的特点:
当数据使用完毕,数据所占空间会被自动释放。凡是局部变量都在栈内存中局部变量。局部变量就是像定义在方法里的变量,定义在方法参数上的变量,定义在for循环里的变量。
2.5.1、堆内存:
剖析 在主函数中创建一个数组 int [] x = new int[3]在内存中的情况:
首先栈内存中的主函数空间中存放的有一个x。其次new出来的东西都不在栈内存中而在堆中堆内存中存放的都是实体,实体包括数组和对象。那么等号右边一 new就在堆内存产生了一个空间这个空间就是数组然后这个空间有三个格子来存放三个元素,而每个格子都有一个编号,即数组中的角标位。那栈内存中的x与堆内存中的实体又是怎么联系起来的?首先堆内存中,实体都有一个存放位置,内存里面都是二进制的地址用地址值来标记在内存中存放的位置,如把一个数组存进堆内存中的时候,它也会有一个起始地址值,我们直接把这个地址值赋给x就行,这是一个赋值动作,只不过是赋的地址值。
这样x就有值了,我们称为x指向(引用)了这个数组。为什么叫引用数据类型?就在这呢。数组并没有真正的直接存放到x变量中去,而只存放数组的地址。就比如告诉你去某某房间,那就直接告诉你门牌号就行了。如果不想让x指向这个数组,这时就用到了以前说的null常量,这个常量只有引用数据类型能用。当x不指向这个数组,这时java虚拟机就视其为垃圾,但是不会被内存中立即清理掉而是在不定时的时间内启动一个垃圾回收垃圾将数组实体在堆内存中清除。
堆内存的特点:
1.堆内存中存放的都是实体,new出来的都是实体,实体包括数组、对象
2.每一个实体都有一个地址值
3.对内存中的实体都有默认的初始化值,这个值要根据数组中存放的类型来定,如果是int型默认的0,如布尔型默认的是false。
4.实体不再被使用是,会在不确定的时间内被垃圾回收器回收
2.6、数组常见操作
2.6.1、获取数据
获取数组中的元素,是数组中最常见的操作之一,期间通常会用到遍历。也就是说编写程序的过程中碰到数组的话,通常会用到for循环,另外数组中有一个属性可以直接获取到数组元素个数。使用方式:数组名称.length
例如:
int [] arr = {3,4,6,1,7}
//int sum=0;
for(int x=0;x<arr.lengrth;x++)
{
//sum += arr[x];//求和
System.out.println("arr["+x+"]="+arr[x]+";");
}
获取最值:
思路:
1.获取最值就要比较,每比较一次都会产生一个最大值,由于该值不确定,所以要 定义一个变量来临时存储这个值。
2.定义好临时变量以后,拿数组中的每一个元素和这个变量进行比较,如果比这个变量大的话,就用该变量记录大的值。
3.当数组中所有的值都比较过之后,那这边变量里存的就是最大的了。
代码如下:
public static int getMax(int[] arr)
{
int max=arr[0];//定义变量,初始化为数组中的任意一个元素
//int x = 0;也可以这样定义,这样定义的意思是,初始化为数组的任意一个下角标
for(int x=1;x<arr.length;x++)
{
if (max<arr[x])
max = arr[x];
//if(arr[max]<arr[x])和上面注释的相对应
//max =x;
}
return max;
//return arr[max];
}
public static void main(String[] args)
{
int[] arr ={5,1,6,4,2,8,9};
int max = getMax(arr);
System.out.println("max="+max);
}
2.6.2、数组元素排序
选择排序:
元素排序的过程是,先拿首个位置的元素,分别和后面的每一个元素进行比较,每一次比较都会有一个结果,如果比较的时候后面的某个元素比首个元素小,那就把这两个元素的位置进行交换,一轮下来之后,首个位置放置的就是元素中最小的了,然后再拿第二个元素与后面的元素分别进行比较,这个时候第一个元素就不需要再参加比较了,因为它已经是数组中最小的了,同理第二个位置也会得到一个最小值....,最后,元素就完成了从小到大的排序了,意为每一次比较的次数都会比着上一次少一,所以应该想到要用嵌套循环。
代码如下:
for (int x=0; x<arr.length-1 ; x++)
{
for(int y=x+1; y<arr.length; y++)
{
if(arr[x]>arr[y])
{
int temp = arr[x];
arr[x] = arr[y];
arr[y]= temp;
}
}
}
代码剖析:
首先外循环是比较的次数,就是首个元素和其它的比一次,第二个元素和其他的比一次,一共比了多少次。按理说应该是有多少个元素就应该比多少次,但是最后一个不用,所以循环次数是数组长度减1,所以第一行是x<arr.length-1。内循环控制每一次比的时候,有多少元素参与比较,比如说第一次就是第一个元素和后面的五个数比,第二次就是第二个元素和后面四个比,也就是说每一个元素比的时候,都只与它位置后面的元素比就行了。所以第二行是int y=x+1;比较的时候,两个数要交换位置,这时要定义一个临时变量,比如A和B要交换位置,定义一个临时变量temp,把A存到temp中,这时就认为A是空的了然后把B赋给A,这时B又是空的了,然后再把temp中存的A赋给B,这样就两个的位置就交换了。
冒泡排序:
代码如下:
for(int x=0; x<arr.length-1; x++)
{
for(int y=0; y<arr.length-x-1; y++)//-x:让每一次比较的元素减少,-1:避免角标越界。
{
if(arr[y]<arr[y+1])
{
int temp = arr[y];
arr[y] = arr[y+1];
arr[y+1] = temp;
}
}
}
第二行的减x是让每一次比较的元素都减少,第一次之后少比一个数,第二次就少比较两个数,而减1是为了角标越界。如果数组有五个元素,那么第三行的意思就是第五个元素和第六个元素比较了,这样显然是不行的,所以要减1。排序也是一种算法,其中速度最快的是希尔排序,三层循环加位运算。而面试的时候常问到的是选择排序和冒泡排序。 但是这两种性能是比较低的,为什么呢?因为每一次对数组元素排序的过程中,每一次符合条件,我们都要到数组当中对数组内部的元素进行位置的互换。数组是实体在堆内存中,而堆内存中换位置比较消耗资源。
折半查找:
要查找元素在数组中的位置,最简单的方法就是在遍历的过程找出符合条件的,首先它的返回值应该是int型,因为返回的是元素在数组中的角标。就是下面这种情况:
public static int getIndex(int[] arr,int key)
{
for(int x=0; x<arr.length; x++)
{
if(arr[x]==key)
return x;
}
return -1;
}
折半查找的方法是比较快的,就像我给你说一个100之内的数,一般都是先从一半说吧,你说50,我说不对小了,然后你再从50到100里折半说75,以此类推找到需要的值。但是前提是这个数组必须是有序的。
代码如下:
public static int getIndex_2(int[] arr,int key)
{
//先记录数组的头角标,中间和尾角标
int min = 0,max = arr.length-1,mid;
mid = (max+min)/2;
//重复查找会用到循环,条件是只要中间角标的值不死所需要的那就继续折半
while(mid]!=key)
{
//如果要找的值比中间角标对应的值大,那说明这个值在中间角标和最大角标之间,
if(key>arr[mid])
min = mid + 1;//这时就重新折半,而此是头角标就是中间角标加1
else if(key<arr[mid])
max = mid - 1;
if(min>max)
return -1;
mid = (max+min)/2;//循环变量控制
}
return mid;
}
2.6.3、 进制转换
例:十进制-->十六进制
public static void toHex(int num)
{
StringBuffer sb = new StringBuffer();
for(int x=0; x<8; x++)//int型四个字节,每次右移四个二进制,最多八次
{
//求一个数的十六进制方法--"重复右移(四位)并&15"
int temp = num & 15;//把一个数二进制的后四位求出,并求出其对应的十六进制数
if(temp>9)//如果大于9求出对应的字符
sb.append((char)(temp-10+'A'));
else
sb.append(temp);
num = num >>> 4;//控制循环变量,没循环一次往右移四位
}
System.out.println(sb.reverse());
}