[TOC]

0x05 JAVA控制流程语句

1.顺序结构语句

Q:什么是流程控制语句?
答:流程控制语句,可以控制程序得执行流程;

流程控制语句得分类:

  • 顺序结构
  • 选择(条件)结构 : if / switch
  • 循环结构 : for / while / do…while

Q:顺序结构语句执行流程?
答:从上而下,依此执行;


2.选择结构

选择结构分类:if语句 / switch 语句

使用案例:

//选择语句案例:
import java.util.Scanner;

import com.sun.xml.internal.ws.api.streaming.XMLStreamReaderFactory.Default;;
class Demo_SelectStatement {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("请输入您选择的序号(0-2):");
int list = sc.nextInt();
System.out.print("请输入成绩:");
int score = sc.nextInt();
sc.close();

/**
* IF语句:
* a.比较表达式无论简单还是复制,结果必须是boolean类型;
* b.if语句控制的语句体如果是一条语句大括号可以省略(与其他编程语言一致);
* c.一般来说有左大括号就没有分号,有分号就没有左大括号(代码if语句结束);
**/
if(list == 0){
System.out.println("0.您输入序号是:"+list);
}else if(list == 1){
System.out.println("1.您输入序号是:"+list);
}else if(list == 2){
System.out.println("2.您输入序号是:"+list);
}else{
System.out.println("3.您输入序号有误!");
};

if(list % 2 == 0)
System.out.println("少年,您发现了新大陆!if没有{}");

/**
* SWITCH语句:
* a.表达式可以接收byte,short,char,int类型的数据,且引用数据类型还可以接收枚举(JDK1.5),String字符串(JDK1.7)
* b.执行流程:先计算表达式的值然后在和case后面的匹配,如果有就执行对应的case语句,否则执行default控制语句
* c.case后面只能是常量,不能是变量或者表达式,
* d.switch语句遇到break就结束(不加break可能导致case穿透),遇到continue就继续执行下一个case语句
* e.switch语句总是从上执行到下,不论default发在哪里
**/
switch(score)
{
case 90:
System.out.println("评分为优");
break;
case 80:
System.out.println("评分为良");
break;
case 70:
System.out.println("评分为中等");
break;
case 60:
System.out.println("评分为良");
break;
default:
System.out.println("不及格");
break; //最后一个break可以省略
}

//switch 注意点
int x = 1;
int y = 1;
switch(x){
default:
y++; //2
case 2:
y++; //3
case 3:
y++; //4
}
System.out.println(y); //4

}
}

//########### 执行结果 ##########
// 请输入您选择的序号(0-2):3
// 请输入成绩:90
// 3.您输入序号有误!
// 评分为优
// 4


3.循环结构

循环结构分类:for / while / do…while
跳出循环:break; [ 只能在switch和循环中 ]
继续执行:continue; [ 只能在循环中 ]
结束方法:return value; 返回值作用不是结束循环而是结束方法;

(1) for循环
for循环执行流程:

  • 1.执行初始化语句
  • 2.执行判断语句,查看返回值是true(继续执行)还是false(退出循环)
  • 3.执行循环体语句
  • 4.执行循环后的操作表达式
  • 5.回到上面2处理进行判断

Q:三种循环语句的区别?

  • do…while循环至少执行一次循环体
  • for和while循环必须先判断条件是否成立然后再决定是否执行循环体;

案例:

// 循环语句结构
class Demo_Circulation {
public static void main(String[] args) {
//1.for 语句
int sum = 0;
for(int i = 0; i <= 10; i++)
{
if(i % 2 == 0)
{
sum += i;
System.out.print(i+" ");
}
}
System.out.println("\n0~10之间偶数之和:"+sum);
//水仙花数
for(int i = 100; i <= 999; i++)
{
int i1 = i / 100 % 10; //百位
int i2 = i / 10 % 10; //十位
int i3 = i % 10; //个位
if(Math.pow(i1, 3) + (Math.pow(i2, 3)) + (Math.pow(i3,3)) == i)
{
System.out.println("水仙花数:"+i);
}
}

//2. while 语句
int count = 0; //计数器
int number = 100;
while(number <= 999) {
int ge = number % 10;
int shi = number / 10 % 10;
int bai = number / 100 % 10;

if((Math.pow(ge, 3) + Math.pow(shi, 3) + Math.pow(bai, 3)) == number) {
System.out.print(number+" | ");
count++;
}
number++;
}
System.out.println("水仙花数的个数:"+count);

//3. do...while 语句
int x = 0;
sum = 0;
do {
sum += x;
x++;
}while(x <= 100);
System.out.println("从1+2+..+100 = "+sum);
}
}

//########## 执行结果 ##########
// 0 2 4 6 8 10
// 0~10之间偶数之和:30
// 水仙花数:153
// 水仙花数:370
// 水仙花数:371
// 水仙花数:407
// 153 | 370 | 371 | 407 | 水仙花数的个数:4
// 从1+2+..+100 = 5050



(2)控制跳转语句标号
标号:标记某个循环对其控制 (外outer/内inner),使用它可以跳出多重循环;
标号组成规则:其实就是合法的标识符。

// 控制流程案例
class Demo_Demo1 {
public static void main(String[] args) {
//* 1.九九乘法表 */
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("");
}

//* 2. 控制跳转语句标号 | mark 标记*/
outer: for(int x = 1; x < 10; x++)
{
System.out.println("外循环:x = "+x);
inner: for(int y = 1; y < 10; y++)
{
System.out.print("内循环:y = "+y +"\t");
if(x * y == 4)
{
System.out.println("以跳出多重循环");
break outer; //直接跳出多重循环 break 标号
}
}
}

//3.面试题 (下面http只是一个标号,后面是一个注释所有可以正确执行)
http://www.baidu.com
System.out.println("我也是可以正确执行得");
}
}
// ##########################
// 外循环:x = 1
// 内循环:y = 1 内循环:y = 2 内循环:y = 3 内循环:y = 4 以跳出多重循环
// 我也是可以正确执行得

注意事项:

  • 一定要注意控制条件语句控制的那个变量问题,不要弄丢了否则会导致死循环;
  • 死循环方法:while(true) / for(;;)

0x06 JAVA函数(方法)

A:为什么要有方法?

  • 提高代码的复用性,使程序更加简洁易读,模块易扩展;

B:什么是方法?

  • 完成特定功能的代码块。

方法的格式:

main方法的格式详细解释:
public static void main(String[] args)
- public : 被jvm调用,所以权限要足够大
- static : 被jvm调用,不需要创建对象直接类名.调用即可
- void : 被jvm调用,不需要有任何的返回值
- main : 只有这样写才能被jvm识别,main不是关键字
- String[] args : 以前是用来接收键盘录入的

修饰符 返回值类型 方法名(参数类型 参数名1,参数类型 参数名2...) {
方法体语句;
return 返回值;
}

//方法的格式说明
4* 修饰符:目前就用 public static。后面我们再详细的讲解其他的修饰符。
4* 返回值类型:就是功能结果的数据类型。
4* 方法名:符合命名规则即可。方便我们的调用。
4* 参数:
44* 实际参数:就是实际参与运算的。
44* 形式参数;就是方法定义上的,用于接收实际参数的。
4* 参数类型:就是参数的数据类型
4* 参数名:就是变量名
4* 方法体语句:就是完成功能的代码。
4* return:结束方法的。
4* 返回值:就是功能的结果,由return带给调用者。
```


```java
import java.util.Scanner;
// JAVA函数(方法案例)
class Demo_Function {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("5 + 12 = " +sum(5,12)); //调用方法

System.out.print("请输入显示多少个斐波那契:");
int count = sc.nextInt();
sc.close();
System.out.println("显示"+count+"斐波那契数列:");
fibonacci(count);


//递归 -> 汉诺塔
char a ='A',b='B',c='C';
hanio(3,a,b,c);
//递归 => 斐波那契
System.out.println("第"+count+"个斐波那契数值为:"+fibo(10));
System.out.println("第5个人的年龄为:"+age(5));
}
//1.方法需要 明确参数列表以及返回值类型
public static int sum(int x, int y)
{
return x + y;
}

//2.斐波那契
public static void fibonacci(int count)
{
int a = 0;
int b = 1;
for(int i = 0; i < count; i++)
{
int temp = a; //坑
a = b;
b += temp;
System.out.print(a+"\t");
}
return ; //返回值类型是void,return可以忽略系统也会默认加上return ;
}

//3.函数递归 汉诺塔
public static void hanio(int n,char a,char b,char c){
if(n==1)
System.out.println("移动"+n+"号盘子从"+a+"到"+c);
else{
hanio(n-1,a,c,b);//把上面n-1个盘子从a借助b搬到c
System.out.println("移动"+n+"号盘子从"+a+"到"+c);//紧接着直接把n搬动c
hanio(n-1,b,a,c);//再把b上的n-1个盘子借助a搬到c
}
}

//4.函数递归 斐波那契
public static int fibo(int i)
{
if(i == 1 || i == 2 )
{
return 1;
}else{
return (fibo(i-1)+fibo(i-2));
}
}

//5.函数递归 吃年龄问题
public static int age(int person)
{
if (person == 1)
{
return 10;
}else{
return age(person-1) + 2;
}
}

}
//###########################
// 5 + 12 = 17
// 请输入显示多少个斐波那契:10
// 显示10斐波那契数列:
// 1 1 2 3 5 8 13 21 34 55 移动1号盘子从A到C
// 移动2号盘子从A到B
// 移动1号盘子从C到B
// 移动3号盘子从A到C
// 移动1号盘子从B到A
// 移动2号盘子从B到C
// 移动1号盘子从A到C
// 第10个斐波那契数值为:55

WeiyiGeek.递归原理图

方法的注意事项:

  • a:方法不调用不执行
  • b:方法与方法是平级关系,不能嵌套定义
  • c:方法定义的时候参数之间用逗号隔开
  • d:方法调用的时候不用再传递数据的类型
  • e:如果方法有明确的返回值,一定要有return带回一个值,如果没有return返回值 可导致 输出调用/赋值调用错误;


(1)方法重载概述和使用
A:什么是方法重载:

  • 在同一个类中,方法名相同,参数列表不同。与返回值类型无关。
  • 参数列表不同:
    • :参数个数不同
    • :参数类型不同
    • :参数的顺序不同(算重载,但是在开发中不用)

案例:

// 方法重载 overload 案例
class Demo_Overload {
public static void main(String[] args) {
//1.重载:方法名称相同。参数列表格式/类型不同(顺序不同),并且返回类型值无关
System.out.println("重载案例1:"+sum(5,9)); //重载案例1:14.0
System.out.println("重载案例2:"+sum(5,9,12)); //重载案例2:26
System.out.println("重载案例3:"+sum(6,5.3,7.7)); //重载案例3:19.0

System.out.println("#### 重载案例4 ####");
System.out.println(isEqual(1,6));
System.out.println(isEqual(6.1,6.1));
}

//(1) add方法求两个参数的和
//注意返回的类型:
public static double sum(int a,int b)
{
return (a + b);
}

//(2) add方法求三个整型参数的和
public static int sum(int a, int b, int c)
{
return a + b + c;
}

//(3) add方法求三个浮点数参数这和
//注意设置参数列表类型
public static double sum(int a, double b, double c)
{
return a + b + c;
}

//案例4:比较两个整数/浮点数是否相等
public static boolean isEqual(int a, int b)
{
return (a==b)?true:false;
}
public static boolean isEqual(double a, double b)
{
return (a==b)?true:false;
}
}

// #### 重载案例4 ####
// false
// true


(2)方法可变参数的概述和使用
描述:定义方法的时候不知道该定义多少个参数
格式:修饰符 返回值类型 方法名(数据类型… 变量名){}
注意事项:

  • 这里的变量其实是一个数组
  • 如果一个方法有可变参数,并且有多个参数,那么可变参数肯定是函数的最后一个

基础实例:

public class Demo1_ChangeAbleArgs {
4public static void main(String[] args) {
44//实例1.可变参数函数讲解
44int[] arr = {11,33,22,77,32};
44print(arr);
44println(arr);
44printlns("重点:可变参数特征", arr);
4}
4
4//可变参数函数接收实际上是数组(与下面int ... arr)进行对比
4public static void print(int[] arr)
{
44for (int i = 0; i < arr.length; i++) {
444System.out.print(arr[i] + " ");
44}
44System.out.println();
4}
4
4//可变参数采用...表示
4public static void println(int ... arr)
{
44//foreach 方式值得学习
44for (int i : arr) {
444System.out.print(i + " - ");
44}
4}
4
4//多个参数,可变参数一定要放在函数参数的最后
4public static void printlns(String test, int ... arr)
{
44System.out.println("\n#####可变参数一定要放在函数参数的最后######\n" + test);
44 for (int i : arr) {
444System.out.print(i + " # ");
44}
4}
}

执行实例:

11 33 22 77 32 
11 - 33 - 22 - 77 - 32 -
#####可变参数一定要放在函数参数的最后######
重点:可变参数特征
11 # 33 # 22 # 77 # 32 #


0x07 JAVA数组

Q:为什么要有数组(容器)
答:数组是储存在堆上的对象,可以存储同类型变量数据的多个值

数组的概念:

  • 数组是存储同一种数据类型多个元素的集合,也是一个容器
  • 数组可以存储基本数据类型也能存储引用数据类型

数组定义格式与初始化:

//如何对数据进行初始化
a.动态初始化,只指定长度由系统给出初始化值
数据类型[] 数组名 = new 数据类型[数组长度] //[] 有几个代表几维数组
int[] arr = new int[5] //从内存中开辟5个连续的空间来存储5个int类型数据 动态初始化

b.静态初始化,给出初始值由系统判断长度
数据类型[] 数组名 = new 数据类型[]{元素1,元素2,....};
int[] arr = new int{1,2,3};
int[] arr = {1,2,3}; //简化类型


//补充:对于动态初始化来说
整数类型:byte , short, int , long 默认初始值都是0
浮点类型:float , double 默认初始化初始值是0.0
布尔类型:boolean 默认初始值是'\u0000'
//注解:\u0000,其实每一个0其实代表的是16进制的0,4个0代表了16个二进制位


1.数组内存详解

(1) java内存分配及栈堆的区别

  • 栈(stack):存储局部变量,特点:先进后出
  • 堆(heap):存储new出来的数组或对象
  • 方法区:面向对象部分讲解
  • 本地方法区:和系统相关
  • 寄存器:给CPU使用

案例:

//数组案例
class Demo_array {
public static void main(String[] args) {
//#案例1.1: 动态初始化
int[] arr = new int[5]; //动态创建5个连续的空间
System.out.println(arr[0]); //系统默认初始化值,整数类型都是0
arr[0] = 10;
arr[1] = 11;
arr[2] = 12;
System.out.println(arr[0]); //对数组重新赋值后输出
/**
* 数组arr信息:[[email protected]
* [代表是数组,由几个代表几维
* I代表是int类型
* @是固定标识位
* 8102c8代表16进制的地址值
*/
System.out.println("数组arr信息:"+arr);

//#案例1.2 (3个引用两个数组)
int[] arr1 = new int[3];
int[] arr2 = new int[3];
int[] arr3 = arr2; // arr3 实际指向arr2的内存地址
arr1[0] = 1;
arr2[1] = 2;
arr3[1] = 3; //arr3将回覆盖arr2[1]的值
arr3[2] = 3;
System.out.println("arr1数组:"+arr1[0] + "\t" + arr1[1] + "\t" + arr1[2]);
System.out.println("arr2数组:"+arr2[0] + "\t" + arr2[1] + "\t" + arr2[2]);
System.out.println("arr3数组:"+arr3[0] + "\t" + arr3[1] + "\t" + arr3[2]);
//############ 执行结果 ####################
// 0
// 10
// 数组arr信息:[[email protected]
// arr1数组:1 0 0
// arr2数组:0 3 3
// arr3数组:0 3 3

//#案例2.2 静态初始值 (注意不允许动静结合)
int[] arr4 = new int[] {1,2,3,4,5}; //声明数组引用 (先默认初始化值0 / 然后在显示初始化值)
int[] arr6; //另外一种书写方法
arr6 = new int[] {5,4,3,2,1};

int[] arr5 = {1,2,3,4,5}; //简写形式声明和赋值在同一行(必须)
System.out.println(arr4[0]); //1
System.out.println(arr5[4]); //5
}
}

WeiyiGeek.数组内存图


2.一维数组/多维数组

(1) 一维数组
数组遍历:依此输出数组元素中每一个元素;
数组属性:arr.length = 数组的长度;
数组最大索引值:arr.length - 1;

一维数组案例:

//一维/多维数组
class Demo_ArrayDemo {
public static void main(String[] args) {
//案例1:常规数组遍历
int[] arr = {126,115,17,129,80};
for(int i = 0; i < arr.length; i++)
{
System.out.print(arr[i]+"\t");
}
System.out.println();

//案例2: 封装方法调用数组遍历
print(arr);
//案例3:获取数组中的最大值
System.out.println("\n数组arr最大值:"+printMax(arr));
//案例4:反转数组
reverseArray(arr);
//案例5:查找数组元素(返回其索引)
int res = searchIndex(115, arr);
if(res != -1)
{
System.out.println("\n查询值得Index = " + res);
}else{
System.out.println("\n未查询到相应值的索引!");
}
}

/***
* function_name: print
* function:数组的遍历
* param:参数列表int[] arr
* return:返回值类型void
*/
public static void print(int[] arr)
{
for(int i = 0; i < arr.length; i++)
{
System.out.print(arr[i]+"\t");
}
}

/**
* function_name:printMax
* function:获取数组中最大值
* 参数:int[] arr
* 返回:int 类型
*/
public static int printMax(int[] arr1) {
int max = arr1[0]; //这里记录数组中第一值就行
for(int i = 1; i < arr1.length; i++)
{
max = max > arr1[i] ? max:arr1[i];
}
return max;
}

/**
* function_name : reverseArray
* function:数组元素反转
* 参数:传入数组
* 返回值:void
**/
public static void reverseArray(int[] arr2) {
for(int i = 0 ; i < arr2.length / 2 ; i++)
{
int temp = arr2[i];
arr2[i] = arr2[arr2.length-1-i]; //注意这里需要进行-1
arr2[arr2.length-1-i] = temp;
}
//之后再调用上面的输出即可
print(arr2);
}

/**
* function_name: searchIndex
* function:查找对应元素的索引
* 参数:传入查找的int值,与int[] arr数组
* 返回值:int类型的索引值
*/
public static int searchIndex(int value,int[] arr3)
{
int index = -1;
for(int i = 0; i < arr3.length; i++)
{
if(arr3[i] == value)
{
return i;
}
}
return index;
}
}

//############## 执行结果 ############
// 126 115 17 129 80
// 126 115 17 129 80
// 数组arr最大值:129
// 80 129 17 115 126
// 查询值得Index = 3



(2) 多维数组
二维数组格式:

1. 数据类型[][] 数组名 = new 数组类型[m][n]; #常用
2. 数据类型 数组名[][] = new 数据类型[m][n];
3. 数据类型[] 数组名[] = new 数组类型[m][n];
4. 数据类型[][] 数组名 = new 数组类型[m][];

int[][] arr = new int[3][2];

#注意数组定义得时候
int[] x,y[] #此时x是一维数组,Y是二维数组;

#多维数组得默认初始化值为:null;


(3)数组传递参数引用
基本数据类型得值传递,不改变原来得值;因为调用后就会弹栈,局部变量随之消失;
引用数据类型得值传递,改变原址;因为即使方法弹栈,但是堆内存数组对象还在,可以通过地址继续访问;

Q:JAVA中到底传值还是传址?
答:即是传值也是传地址,基本数据类型传递得值,引用数据类型传递得地址;但是常常我们再面试中会说java只有传值,因为地址值也是值;

案例:

//*多维数组 *//
class Demo_MutilArray {
public static void main(String[] args) {
//1. 方式1
int[][] arr = new int[2][3]; // 默认初始化值为0 (注意与下面方式4得异同)
//2. 方式2
int[] arr1[] = new int[2][3];
//3. 方式3
int arr2[][] = new int[2][3];

System.out.println("方式1:"+arr[0]+" ,首地址:"+ arr); //打印得是一维arr[0]得地址与arr[0][0]首地址 【注意这里与C语言不同】
System.out.println("方式2:"+arr1[0][0]);
System.out.println("方式3:"+arr2[1][2]); // 2行/三列

//4. 方式4
int[][] arr3 = new int[3][]; //存放了三个一维数组 默认初始化值为null,由于我们没有给出二维数组得值;
System.out.println(arr3[0] +", " +arr3[1] +", "+arr3[2]);
arr3[0] = new int[3]; //第一个一维数组存放了3个int值
arr3[1] = new int[5]; //第二个一维数组存放了5个int值
System.out.println(arr3[0] +", " +arr3[1] +", "+arr3[2]);

//############### 执行结果 ###############
// 方式1:[[email protected] ,首地址:[[[email protected]
// 方式2:0
// 方式3:0
// null, null, null
// [[email protected], [[email protected], null

//**案例1 */
int[][] arr4 = {{1,2,3},{4,5},{6,7,8,9}};
System.out.println("arr4:"+arr4+", arr[0]:"+ arr[0] + ", arr[0][0]:" + arr[0][0]+"\n");
//arr4 : 是一个二维数组地址值
//arr4[0] : 一维数组得地址值
//arr4[0][0] : 二维数组中得元素值
//外循环控制行,内循环控制列
for(int i = 0; i < arr4.length; i++ )
{
for(int j = 0; j < arr4[i].length; j++) //获取每一一个一维数组中得元素
{
System.out.print(arr4[i][j] + "\t");
}
System.out.println("\n");
}

//**案例2:将数组进行参数传递后值将被改变 */
int[] arr5[] = {{1,2},{3,4,7}};
arrayChange(arr5); //数组中得值将发生改变
for(int i = 0; i < arr5.length; i++ )
{
for(int j = 0; j < arr5[i].length; j++) //获取每一一个一维数组中得元素
{
System.out.print(arr5[i][j] + "\t");
}
System.out.println();
}
}

/**
* 函数名:arrayChange
* 函数功能:实现二维数组中值进行*+1
* 函数参数:array[][] 二维数组
* 函数返回值:none
*/
public static void arrayChange(int[][] array){
for(int i = 0; i < array.length; i++ )
{
for(int j = 0; j < array[i].length; j++) //获取每一一个一维数组中得元素
{
array[i][j] += 1;
}
}
}

}

// ############### 执行结果 ##############
// arr4:[[[email protected], arr[0]:[[email protected], arr[0][0]:0
// 1 2 3
// 4 5
// 6 7 8 9

// 2 3
// 4 5 8

WeiyiGeek.二维数组内存图


n.数组越界和空指针

java数组中常见的异常情况:

  1. a:数组索引越界异常: java.lang.ArrayIndexOutOfBoundsException
  • 原因:访问了不存在的索引
  1. b:空指针异常: java.lang.NullPointerException
  • 原因:数组已经不指向堆内存空间地址,而您还使用数组名去访问元素;
// 数组异常案例
class Demo_ArrayException {
public static void main(String[] args) {
//1.数组索引越界异常
int[] arr = new int[] {0,1,2}; //注意不能动静结合(这里与c/c++中数组是不一样的)
//发生异常: java.lang.ArrayIndexOutOfBoundsException
//System.out.println(arr[3]); //发生异常: java.lang.ArrayIndexOutOfBoundsException

//2.空指针异常
int[] arr1 = new int[5];
arr1 = null; // 使用null进行赋值为空,== 野指针(访问不再是执行堆内存空间的实体)所有再次调用回导致空指针异常
//System.out.println(arr1[1]); //发生异常: java.lang.NullPointerException
}
}