黑马程序员——Java基本语法(二):程序流程控制、函数、数组

——- android培训java培训、期待与您交流! ———-

 

一、程序流程控制

        程序流程控制(又称语言)有:判断结构、选择结构、循环结构

1、判断结构——if

      if(条件表达式){执行语句}

      if(条件表达式){执行语句}  else{执行语句}

       if(条件表达式){执行语句}  else if(条件表达式){执行语句}......else{执行语句}

 注:a).如果if语句中只有一条语句,那么可以不写大括号。

         b).如果有多条语句且if语句没写大括号,if就只能控制离它最近的单条语

         c).三元运算符就是if else语句的简写格式。if else运算后,有一个具体的结果时,可以简化写成三元运算符。

    d).如果if( 表达式 )后面加上分号,表示无论表达式为true或者false,都不执行任何语句。

 示例:判断结构的使用

class IfElseDemo 
{
	public static void main(String[] args) 
	{
		if(false);
		{
			System.out.println("Hello World!");
		}
	}
}

运行结果为:

注:由于if( false );语句后面加了分号,因此不执行任何操作。{ System.out.println("Hello World"); }为局部代码块。

局部代码块:局部代码块可以定义局部变量的生命周期。
class IfElseDemo 
{
	public static void main(String[] args) 
	{
		
		{
			int m=5;
			System.out.println(m);
		}
		System.out.println("Hello World!"+m);
	}
}

运行结果为:
注:变量m是局部变量,生命周期仅在局部代码块括号中,当代码执行到局部代码块右括号外,变量m便消失了。局部代码块外的代码当然也就访问不到变量m了。

 2、选择结构——switch

格式:

switch(表达式) 

{

case 取值1:

     执行语句;

     break;

case 取值2:

     执行语句;

     break;

.................

default:

     执行语句;

     break;

}

注:a).表达式的值只有4种:byte,short,int,char

        b). 结束switch有2种情况:1)遇到break;2)switch结尾

       c).casedefault没有顺序。先执行第一个case,没有匹配的case执行default

        d).如果匹配的case或者default没有对应的break,那么程序会继续向下执行,运行可以执行的语句,直到遇到break或者switch结尾结束。

       e).进入switch语句后,执行顺序是先执行case,然后从上到下,最后再执行default。即使default放在case上面,执行顺序也不变。

        f).建议:当值少时使用switch;当值多时和有double时,可用if

示例:选择结构的使用

1)部分case和default中没有break语句的情况:

class SwitchDemo 
{
	public static void main(String[] args) 
	{
		int m=3;
		switch(m)
		{
			case 1:
				System.out.println("1");
			break;
			case 2:
				System.out.println("2");
		
			case 3:
				System.out.println("3");
		
			case 4:
				System.out.println("4");
			
			default:
				System.out.println("default");
			break;
		}
	}
}

运行结果为:

2)多个case同一种处理方式的情况:

class SwitchDemo 
{
	public static void main(String[] args) 
	{
		int m=2;
		switch(m)
		{
			case 3:
			case 4:
			case 5:
				System.out.println("春");
			break;
			case 6:
			case 7:
			case 8:
				System.out.println("夏");
			break;
			case 9:
			case 10:
			case 11:
				System.out.println("秋");
			break;
			case 12:
			case 1:
			case 2:
				System.out.println("冬");
			break;
			default:
				System.out.println("default");
			break;
		}
	}
}

运行结果为:

注:ifswitch语句的应用:
        if:
1.对具体的值进行判断
              
2.对区间判断。
              3.对运算结果是boolean类型的表达式进行判断。

switch:1.对具体的值进行判断
              
2. 值的个数通常是固定的。
建议:对于几个固定的值判断,使用switch语句;因为switch语句会将具体的答案都加载进内存,效率相对高。

3、循环结构——while,do.....while,for

1)  while(条件表达式)

     {

           执行语句;

     }

2)  do

     {

           执行语句;

     }while(条件表达式);

注:while:先判断条件,只有条件满足后才执行循环体。注意不要写成while(x<5);因为后面的分号就是循环体,代表不执行任何语句,这个循环就成了一个死循环。

         do....while:先执行循环体,再判断条件,条件满足,再执行循环体;也就是说,do....while无论条件是否满足,循环体至少要执行一次。

3)  for(初始化表达式;循环条件表达式;循环后的操作表达式)

     {

           执行语句;

     }

注:1).for循环的初始化表达式、循环后的操作表达式可以是多个表达式,通过逗号分隔。

            如:for(int a = 1,b =2; a < 2 & b < 3; a++,b++){}

        2).变量有自己的作用域,对于for来讲,如果将用于控制循环的增量定义在for语句中,那么该变量只能在for语句中有效,for语句执行完毕后,该变量在内存中将被释放,当循环体结束后,想变量不被释放,可用while。

        3).for和while可以进行互换,如果需定义循环增量,用for更为适合。

        4).当要对语句执行很多次时,可用循环结构。

class ForDemo 
{
	public static void main(String[] args) 
	{
		int x=1;
		for(System.out.println("A");x<3;System.out.println("C"))
		{
			System.out.print("D");
			x++;
		}
	}
}

注:最简单无限循环格式:while(true){}, for(;;){},对于for来讲,不写条件表达式默认就是ture。无限循环存在的原因是并不知道循环多少次,而是根据某些条件,来控制循环。

什么时候使用循环结构?

        当要对某些语句执行很多次时,就使用循环结构。

循环注意:一定要明确哪些语句需要参与循环,哪些不需要。

示例:

1)1~10之间的所有数的和。

/*
需求:1~10之间的所有数的和。
思路:用到了累加的思想。通过sum变量记录住每次变化的结果,
      通过循环的形式,进行累加的动作。
*/
class ForDemo 
{
	public static void main(String[] args) 
	{
		int sum=0;
		for(int i=1;i<=10;i++)
		{
			sum+=i;
		}
		System.out.println(sum);
	}
}
 

2)1~100之间7的倍数。

/*
需求:1~100之间7的倍数。
思路:用到了计数器的思想。通过一个变量记录住数据的状态变化,
      通过循环的形式完成操作。
*/
class ForDemo 
{
	public static void main(String[] args) 
	{
		int num=0;
		for(int i=1;i<=100;i++)
		{
			if(i%7==0)
			num++;
		}
		System.out.println(num);
	}
}

3)语句的嵌套——循环嵌套

/*循环嵌套*/
class ForDemo 
{
	public static void main(String[] args) 
	{
		for(int i=1;i<=3;i++)
		{
			for(int j=1;j<=10;j++)
		    {
				System.out.print("*");
			}
			System.out.println();
		}
	}
}

运行结果为:

结论:对于打印长方形,外循环控制行数,内循环控制的是每一行的列数,也就是一行中元素的个数。

4)语句的嵌套——九九乘法表

class ForDemo 
{
	public static void main(String[] args) 
	{
		for(int i=1;i<=9;i++)
		{
			for(int j=1;j<=i;j++)
		    {
				System.out.print(j+"*"+i+"="+i*j+"\t");
			}
			System.out.println();
		}
	}
}

运行结果为:

 

注:1、代码中的"\t"是一个转义字符,也就是制表符。还有其他的一些转义字符\n:回车\b:退格\r:回车符
       2、windows系统中回车符其实是由两个转义字符组成的:\r\n,linux中回车符是\n。

二、其他流程控制语句

1、break(跳出):应用范围:选择结构,循环结构。

2、continue(继续):只能应用于循环结构,特点:结束本次循环继续下次循环。

注:1).break和continue语句都有作用范围。

        2).break和continue单独存在时,如果在此之后还有任何语句都执行不到。

示例:

1)break的使用:

class ForDemo 
{
	public static void main(String[] args) 
	{
		w:for(int i=1;i<=10;i++)
		{
			q:for(int j=1;j<=10;j++)
		    {
				System.out.print(i+":::"+j);
				break q;
			}
			System.out.println();
		}
	}
}

注:其中的w和q都是标号。标号:只能用于循环结构,用于给循环体标注名字,标号的出现,可以让这两个语句作用于指定的范围。运行结果是:

2)continue的使用:

class ForDemo 
{
	public static void main(String[] args) 
	{
		w:for(int i=1;i<=10;i++)
		{
			q:for(int j=1;j<=10;j++)
		    {
				System.out.print(i+":::"+j+"  ");
				continue w;
			}
			System.out.println();
		}
	}
}

运行结果为:

三、函数

定义:也成方法,就是定义在类中具有特定功能的一段独立小程序。

格式:修饰符   返回值类型   函数名(参数类型  形式参数1,参数类型  形式参数2,..........参数类型  形式参数n)

            {

                      执行语句;

                      return  返回值;

            }

其中:

   返回值类型:函数运行后的结果的数据类型。

   参数类型:是形式参数的数据类型。

   形式参数:是一个变量,用于存储调用函数时传递给函数的实际参数。

   实际参数:传递给形式参数的具体数值。

   return:用于结束函数。

   返回值:该值会返回给调用者。

特点:

      1).定义函数可以将功能代码进行封装,便于对该功能进行复用。

      2).函数的出现提高了代码的复用性。

      3).函数只有被调用才会被执行。

      4).对于函数没有具体返回值的情况,返回值类型用关键字void表示,那么该函数中的return语句如果在最后一行可以省略不写。

注:a).函数中只能调用函数,不可以在函数内部定义函数。

       b).定义函数时,函数的结果应该返回给调用者,交由调用者处理。

定义一个函数的思想?

       1.既然函数是一个独立的功能,那么先明确该功能的结果是什么?——即明确函数返回值类型

       2.明确在定义该功能的过程中是否需要未知的内容参与运算?——即明确函数列表(参数类型和个数)

示例:定义一个打印九九乘法表功能的函数

class ForDemo 
{
	public static void main(String[] args) 
	{
		print99();//调用函数
	}
    public static void print99()//定义函数
	{
		for(int i=1;i<=9;i++)
		{
			for(int j=1;j<=i;j++)
		    {
				System.out.print(j+"*"+i+"="+i*j+"\t");
			}
			System.out.println();
		}
	}
}

运行结果为:

函数的特性——重载(overLoad)

       定义:在同一个类中,允许存在一个以上的同名函数,只要它们的参数个数或者参数类型不同即可。

       特点:重载与返回值类型无关,只看参数列表。

       好处:方便了阅读,优化了程序设计。

示例:public static int add(int a,int b){return a+b;}//定义返回两个整数的和的函数
           public static int add(int a,int b,int c){return a+b+c;}//定义返回三个整数和的函数
           public static String add(String a,String b)//定义返回两个连接字符串的函数

什么时候定义重载?

       当定义的功能相同,但参数运算的未知数内容不同,那么,这时就定义一个函数名称以表示该功能,方便阅读,而通过参数列表的不同来区分多个同名函数。

四、数组

定义:同一种类型数据的集合,相当于一个容器,可以自动给数组中的元素从0开始编号。

一维数组的格式:

        格式一:元素类型[ ]  数组名 = new 元素类型[ 元素个数或数组长度 ] ;    //定义一个容器,但是没有明确容器中的具体数据

                如:int [ ] arr=new int[3];    或者写成: int  arr[ ]  =new int [3];

        格式二:元素类型[ ]  数组名 = new 元素类型[ ]{ 元素,元素,........元素} ;    //定义一个容器,存储已知的数值

                如:int [ ] arr =new int[ ]{3,4,5};

        格式三:元素类型[ ]  数组名 ={ 元素,元素,........元素} ;  //静态初始化格式

                如:int [ ] arr={3,4,5};

        其中:new是用来在对内存中产生一个容器实体。

在内存中的分配如下:

注:int [ ] arr=new int[3];arr[0]=5;这两句话在内存中都做了什么事呢?

       1).先将arr变量加载到栈内存中

       2).new int[3]:在堆内存中开辟空间,分配内存地址,假设内存地址值为0x2233,实体中的变量int类型数组默认值为0,arr[0]=5;将5赋值给数组中的0角标位

       3).将内存地址值传给arr变量,arr变量通过内存地址值指向数组

       4).当arr=null时就不指向堆,就和数组没关系了,堆内存中的数组实体就变成了垃圾,会在不确定的时间内被垃圾回收器回收

内存结构:Java程序 运行时,需在内存中分配空间,处理方式不同。

内存结构有:栈内存,堆内存,方法区,本地方法区,寄存器

其中:栈内存:用于存储局部变量,当数据使用完,所占空间会自动释放

           堆内存:1.数组和对象,通过new建立的实例都存放在堆内存中

                          2.每个实体都有内存地址值

                          3.实体中的变量都有默认的初始化值;例如:int型数组默认为0,double型数组默认为0.0,float型数组默认为0.0f,boolean型默认为false

                          4.实体不再被使用,会在不确定的时间内被垃圾回收器回收

在运行时易出现的异常:

       1.如在运行时出现ArrayIndexOutOfBoundsException:3错误时,意为:在操作数组时访问到了数组中不存在的角标。

       2.如在运行时出现NullPointerException错误时,意为:空指针异常,当引用没有任何指向值为null的情况,该引用还在使用操作实体。

二维数组:数组的数组

        格式一:int [][] arr=new int[3][4];  //表示定义了名称为arr的二维数组。其中,有三个一维数组,每个一维数组中有4个元素。一维数组的名称分别为:arr[0],arr[1],arr[3]。给第一个一维数组1脚标位赋值为23写法是:arr[0][1] = 23。

        格式二:int [][] arr=new int[3][];    //注:此种格式中每个一维数组都是默认初始化值null

        格式三:int [][] arr={{1,2,3,4},{4,5,6},{7,8,9,3}}; //每一个一维数组中具体元素都初始化了。

在内存中的分配如下:

 

注:int[ ][ ] arr=new int[2][3];这句话在内存中都做了什么事呢?

       1).先将arr变量加载到栈内存中

       2).new int[2][3]:在堆内存中开辟空间,分配内存地址,假设内存地址值为0x0079指向数组,数组中又各自包含了内存地址值指向了数组,即数组的数组

       3).将内存地址值传给arr变量,arr变量通过内存地址值指向数组

       4).当arr=null时就不指向堆,就和数组没关系了,堆内存中的数组实体就变成了垃圾,会在不确定的时间内被垃圾回收器回收

数组的常见操作:

       对数组操作最基本的动作就是存和取。 核心思想:就是对角标的操作。

操作1:获取元素中的最大/少值

/**
 * 需求:第1题: 定义一个二维int数组,编写代码获取最小元素。
 * 
 * 思路:1.定义变量记录住每次比较后较大的值。
 *       2.对数组中的元素进行遍历取出,和变量中记录的元素进行比较。
 *       3.遍历结果,该变量记录就是最大值。
 */
public class Test1 
{

	public static void main(String[] args)
	{
		//定义一个二维数组
		int [][] arr=new int [][]{{1,2,5},{1,2,4},{2,5,6},{2,-2,2}};
		//调用getMinValue方法,获取数组中的最小值
		int minValue=getMinValue(arr);
		System.out.println("这个二维数组中的最小值是:"+minValue);		
	}
	/**
	 * 获取二维数组中的最小值
	 * @param arr 接收一个二维整数类型的数组
	 * @return 返回二维int数组中的最小元素
	 */
	public static int getMinValue(int[][] arr)
	{
		//定义变量,用来记录元素中最小值
		int minValue=arr[0][0];
		//遍历二维数组,获取其中最小值
		for(int x=0;x<arr.length;x++){
			for(int y=0;y<arr[x].length;y++){
				//将minValue的值与元素进行比较,获取最小值,并赋给minValue
				if(minValue>arr[x][y])
					minValue=arr[x][y];
			}
		}//返回所得的最小值
		return minValue;
	}
}

运行结果为:

操作2:选择排序

       思路:先拿0角标位和所有角标位的元素进行比较,然后再拿1角标位和其他角标位数尽心比较(除0角标位),然后再拿2角标位和其他角标位数尽心比较(除0,1角标位)..........以此类推。

注:选择排序的最值出现在头角标位置。

代码显示:

class Test2
{
	public static void main(String[] args)
	{
		int [] arr=new int []{3,1,4,6,2};
		for(int i=0;i<arr.length-1;i++)//arr.length-1:最后一位不用再比
		{
			for(int j=i+1;j<arr.length;j++)//int j=i+1;相同的角标位不用比
			{
				if(arr[i]>arr[j])
				{
					int temp=arr[i];
					arr[i]=arr[j];
					arr[j]=temp;
				}
			}
		}
		for(int i=0;i<arr.length;i++)
		{
			System.out.print(arr[i]+"  ");
		}
	}
}

显示结果为:

操作3:冒泡排序

       思路:相邻的两个元素进行比较,如果符合条件就换位置。

注:冒泡排序的第一圈最值出现在最后位

代码显示:

class Test2
{
	public static void main(String[] args)
	{
		int [] arr=new int []{3,1,4,6,2};
		for(int i=0;i<arr.length-1;i++)
		{
			for(int j=0;j<arr.length-i-1;j++)//arr.length-i-1中的-i:让每一次比较的元素减少,-1:避免角标越界
			{
				if(arr[j]>arr[j+1])
				{
					int temp=arr[j];
					arr[j]=arr[j+1];
					arr[j+1]=temp;
				}
			}
		}
                for(int i=0;i<arr.length;i++)
		{
			System.out.print(arr[i]+"  ");
		}
	}
}

显示结果为:

注:Java中已经定义好了排序方法,可直接拿来使用,如Arrays.sort(arr);最快的排序方法是希尔排序。

操作4:折半查找(又称二分查找):前提条件:必须保证查找前数组是有序的。

       思路:1.设计三个变量记录角标:min,max,mid。min为0角标位,max为数组最大角标位,mid为(min+max)/2。

                  2.将已知元素与中间值位元素进行比较,如果相同则返回角标位,程序结束。

                  3.如果比中间值元素大,说明待查找的元素的位置可能在mid与max角标之间,设置max = mid - 1,mid = (max + min)/2,重复第1、2步的操作。

                  4.如果比中间值元素小,说明待查找的元素的位置可能在mid与min角标之间,设置min = mid +1,mid = (max + min)/2,重复第1、2步的操作。

                  5.如果数组中不存在待查找的元素,那么按照如上流程,最终min角标值会大于max角标值,此时返回-1。

代码显示:

class Test4 
{
	public static void main(String[] args) 
	{
		int [] arr={1,3,4,8,10,15,21};
		System.out.println("index="+halfSearch(arr ,15));
		System.out.println("index="+halfSearch_1(arr ,15));
	}
	//方法一:
	public static int halfSearch(int [] arr ,int key)
	{
		int min=0,max=arr.length-1,mid=(min+max)/2;
		while(arr[mid]!=key)
		{
			if(key>arr[mid])
				min=mid+1;
			else if(key<arr[mid])
				max=mid-1;
			mid=(min+max)/2;
			if(min>=max)
				return -1;
		}
		return mid;
	}
	//方法二:
	public static int halfSearch_1(int [] arr ,int key)
	{
		int min=0,max=arr.length-1,mid;
		while(min<=max)
		{
			mid=(min+max)>>1;//相当于mid=(min+max)/2;
			if(key>arr[mid])
				min=mid+1;
			else if(key<arr[mid])
				max=mid-1;
			else
				return mid;
		}
		return -1;
	}
}

运行结果为:

注:在实际开发中,折半查找不需要我们自己去写,JDK中已经提供了相关的API供调用。如:Arrays.binarySearch(arr,15);其显示结果为负数。

其原因是:

操作5:进制转换

十进制——>二进制

思路:二进制就是十进制的余数倒序。

代码显示:

class  Test5
{
	public static void main(String[] args) 
	{
		StringBuilder sb=new StringBuilder();//定义一个容器
		int num=6;
		while(num>0)
		{
			sb.append(num%2);//将余数存进容器
			num=num/2;
		}
		System.out.println(sb.reverse());//倒序输出
	}
}

运行结果为:

十进制——>二进制、八进制、十六进制

思路:以十进制转成十六进制为例:就是将十进制数与15相与,其值为十六进制的最低位;然后将十进制右移4位,再与15相与,得到十六进制的倒数第二位.......直到相与的结果是0为止。进而可以推理得到,十进制转换为二进制和八进制的规律与转换成十六进制很相似,只是偏移量和相与的数字不同而已。

十进制转换成二进制的偏移量为1,相与数字为1。

十进制转换成八进制的偏移量为3,相与数字为7。

示例:十进制转换成十六进制

public class ArrayDemo {

	public static void main(String[] args) {
		int num=60;
		toChange(num);
	}
	public static void toChange(int num){
		StringBuilder sb=new StringBuilder();
		while(num!=0){
			int temp=num&15;
			if(temp>9)
				sb.append((char)(temp-10+'A'));
			else
			    sb.append(temp);
			num=num>>>4;
		}
		System.out.println(sb.reverse());
	}
}
运行结果为:

使用查表法将十进制——>二进制、八进制、十六进制:

public class ArrayDemo {

	public static void main(String[] args) {
		int num=60;
		toBin(num);
		toOctal(num);
		toHex(num);
	}
	//十进制——>二进制
	public static void toBin(int num){
		toChange(num,1,1);
	}
	//十进制——>八进制
	public static void toOctal(int num){
		toChange(num,7,3);
	}
	//十进制——>十六进制
	public static void toHex(int num){
		toChange(num,15,4);
	}
	//使用查表法进行进制转换
	public static void toChange(int num,int base,int offset){
		StringBuilder sb=new StringBuilder();
		char[] arr={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
		while(num!=0){
			int temp=num&base;
			sb.append(arr[temp]);
			num=num>>>offset;
		}
		System.out.println(sb.reverse());
	}
}

运行结果为:

注: 1.如果数据出现了对应关系,而对应关系的一方是有序的数字编号,并作为角标使用,这时候可以使用数组。

     2.将这些数据存储到数组中,根据运算的结果作为角标直接去查数组中对应的元素即可,这种方式称为查表法。

 ——- android培训java培训、期待与您交流! ———-

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值