毕向东Java基础教程-Java基础语法【下】
本篇为Java基础语法【下】,包含程序流程控制、函数、数组这三部分内容。
程序流程控制
顺序结构
判断结构
三种格式:
/*=======第一种格式=======*/
if(条件表达式)
{
执行语句;
}/*=======第二种格式=======*/
if(条件表达式)
{
执行语句;
}
else
{
执行语句;
}/*=======第三种格式=======*/
if(条件表达式)
{
执行语句;
}
else if(条件表达式)
{
执行语句;
}
...
else
{
执行语句;
}
注意:
if
语句后若未写大括号,那就只能控制离它最近的单条语句。
if else
与三元运算符的区别在于:三元运算符由于是运算符,因此必须有结果。
- Java语言中语句一般有两种结束方式:以分号
;
或后大括号}
结束,以}
结束的一般为流程控制语句。
- Example1:if语句无控制体的情况
if(false); { System.out.println("Hello World!"); //会打印Hello World!,实际上if没有控制体,大括号内容叫做局部代码块 }
Example2:局部代码块
{ int m = 89; System.out.println("Hello World!..."+m); } System.out.println("Over..."+m); //编译报错
局部代码块一执行完,m
变量在内存中就会被释放,因此局部代码块可以定义局部变量的生命周期。为了节约内存空间,在确保后面不会在使用此变量的情况下,可以使用局部代码块的形式,执行完后,其空间就会被腾出来(虚拟机在内存当中开辟的空间是有限的,除非在启动虚拟机的时候设置需要的空间)。
switch(表达式)
{
case 取值1:
执行语句;
break;
case 取值2:
执行语句;
break;
...
default:
执行语句;
break;
}
特点:
switch
能判断的数据类型有限,只有四种:byte
,short
,int
,char
(Java1.5 为switch
增加了枚举类型,Java1.7 增加了字符串类型)。
- 程序一执行到
switch
,会把大括号内所有的语句加载到内存中。
- switch中条件放置无顺序,但执行有顺序。
Example1:即使default放在第一位,也会先判断case
switch(表达式) { default: 执行语句; break; case 取值1: 执行语句; break; case 取值2: 执行语句; break; ... }
- 程序遇到满足条件的值后,如果没有
break
,则会继续向下执行,之后的case
不再进行判断,直接执行里面的语句,直到遇到break
或执行到大括号终结。
Example2
int x = 2; swtich(x) { default: System.out.println("d"); //break; case 4: System.out.println("a"); //break; case 1: System.out.println("b"); break; case 3: System.out.println("c"); break; } //输出结果为d, a, b
- Example3:多个值,对应相同执行语句
int month = 3; switch(month) { case 3: case 4: System.out.println(month+"月对应的是春季"); break; }
执行到case 3
,满足条件,但没有执行语句和break
,继续往下执行并且不判断,执行完case 4
中的语句再break
。
if
和swtich
的应用:- if
- 对具体的值进行判断
- 对值的区间进行判断
- 对运算结果是
boolean
类型的表达式进行判断
- switch
- 对具体的值进行判断
- 值的个数通常是固定的
switch
语句,因为其会将具体的答案都加载进内存,效率相对高一点。常用的为if,不建议用
switch
语句,因为其功能性较差,且书写麻烦。循环结构 三种格式:
/*=======while语句格式=======*/
while(条件表达式)
{
执行语句;
}/*=======do while语句格式=======*/
do
{
执行语句;
}while(条件表达式);
/*=======for语句格式=======*/
for(初始化表达式;
循环条件表达式;
循环后的操作表达式)
{
执行语句;
(循环体)
}
特点:
do while
:无论条件是否满足,循环体至少执行一次。
for
的三个表达式运行顺序:初始化表达式只执行一次;判断循环条件,为真就执行循环体,再执行循环后的操作表达式,接着继续判断循环条件,重复整个过程,直到条件不满足为止。
while
与for
可以互换,区别在于for
为了循环而定义的变量在for
循环结束就在内存中释放,而while
循环使用的变量在循环结束后还可以继续使用。
- 最简单无限循环格式:
while(true)
,for(; ; )
,无限循环存在的原因是并不知道循环多少次,而是根据某些条件,来控制循环(for
两边的表达式可以不写,中间不写会默认为true
)。
- Example1:
while
语句错误写法
while(条件表达式); //while语句后加分号,会一直循环 { 执行语句; }
- Example2:
for
第一和第三个表达式的非常规写法
int x = 1; for(System.out.println("a"); x<3; System.out.println("c")) { System.out.println("d"); x++; } //输出 a d c d c //注意能执行成功,只要满足表达式的条件即可
Example3: 打印九九乘法表
for(int x = 1;
x <= 9;
x++)
{
for(int y = 1;
y <= x;
y++)
{
System.out.print(y + "*" + x + "=" + y * x + "\t");
// "\t":制表符,可以用来对齐
}
System.out.println();
}
输出结果为
文章图片
常用转义符:
n
:回车
t
:制表符
b
:退格
-
r
:按下回车键
windows系统中回车符其实是由rn
这两个符号组成的,linux中回车符是n
,dos可以识别n
和rn
,但是windows的一些软件中只能识别rn
。
Example4:打印带双引号的hello world
System.out.println(""hello world""); //错误写法,会识别成前两个双引号为一对 System.out.println("\"hello world\""); //正确写法
Example5:打印带反斜杠的hello world
System.out.println("\\hello world");
break
(跳出):应用于选择结构(swtich)和循环结构(loop)contine
(继续):应用于循环结构(loop)注意:
- 这两个语句离开应用范围就无意义。
- 这两个语句单独存在下面都不可以有语句,因为执行不到。
- 如果出现了循环嵌套,
break
要跳出指定循环,可以通过标号实现——标号的出现,可以让这两个语句作用于指定的范围(标号用得并不多)。
Example
xiaoqiang:for(int x=0; x<3; x++) { wangcai:for(int y=0; y<4; y++) { System.our.println("x=" + x); break xiaoqiang; } } xiaoqiang:for(int x=0; x<3; x++) { wangcai:for(int y=0; y<4; y++) { System.our.println("x=" + x); continue xiaoqiang; } }
- 函数就是定义在类中的具有特定功能的一段独立小程序
- 函数也称为方法
修饰符 返回值类型 函数名(参数类型 形式参数1, 参数类型 形式参数2, ...)
{
执行语句;
return 返回值;
}
Example
class FunctionDemo
{
public static void main(String[] args)
{
int c = add(3,4);
System.out.println("c="+c);
}
int add(int a, int b)
{
return a+b;
}
}
编译报错:无法从静态上下文中引用非静态方法
add(int, int)
,需要在add()
函数前加static
修饰符(注意是在编译过程中失败)。函数的特点
- 定义函数可以将功能代码进行封装
- 便于对该功能进行复用,函数的出现提高了代码的复用性
- 函数只有被调用才会被执行
- 对于函数没有具体返回值的情况,返回值类型用关键字
void
表示,那么该函数中的return
语句如果在最后一行可以省略不写
- 函数中只能调用函数,不可以在函数内部定义函数。
- 定义函数时,函数的结果应该返回给调用者,交由调用者处理。
Example
class Demo
{
public static void main(String[] args)
{
System.out.println(add(3,5));
}
public static void add(int a, int b)
{
System.out.println(a + b);
return;
}
}
编译出错:
System.out.println(add(3,5));
此处不允许使用void
类型。函数的应用 Example:判断成绩所属的等级
public static char getLevel1(int num)
{
char level;
if(num>=90 && num<=100) level = 'A';
else if(num>=80 && num<=89) level = 'B';
else if(num>=70 && num<=79) level = 'C';
else if(num>=60 && num<=69) level = 'D';
else if(num<60) level = 'E';
return level;
}public static char getLevel2(int num)
{
char level;
if(num>=90 && num<=100) level = 'A';
else if(num>=80 && num<=89) level = 'B';
else if(num>=70 && num<=79) level = 'C';
else if(num>=60 && num<=69) level = 'D';
else if(num<60 || num>100) level = 'E';
return level;
}
两个函数均编译错误 :可能尚未初始化变量
level
;编译时不会判断数值(即使涵盖了所有的情况),只管语法错误,所以最后应加else
语句。函数的重载(overload) 重载的概念:在同一类中,允许存在一个以上的同名函数,只要它们的参数个数或者参数类型不同即可。
重载的特点:与返回值类型无关,只看参数列表。
重载的好处:方便于阅读,优化了程序设计(函数的功能一样,仅仅是参与运算的未知内容不同时,可以定义多函数,却使用统一函数名称,这样方便阅读,在调用时,虚拟机通过参数列表的不同来区分同名函数)。
Example1
public static int add(int a, int b)
{
return a+b;
}
public static double add(int a, int b)
{
return a+b;
}
编译报错:已在XX中定义
add(int, int)
…,Java是严谨性语言,如果函数出现调用的不确定性,会编译失败。Example2:打印乘法表
/**
打印指定参数的任意乘法表
*/
public static void printCFB(int num)
{
for(int x=1;
x<=num;
x++)
{
for(int y=1;
y<=x;
y++)
{
System.our.print(y+"*"+x+"="+y*x+"t");
}
System.out.println();
}
}
/**
打印标准乘法表(不指定参数的情况下)
*/
public static void printCFB()
{
for(int x=1;
x<=9;
x++)
{
for(int y=1;
y<=x;
y++)
{
System.our.print(y+"*"+x+"="+y*x+"t");
}
System.out.println();
}
}
如上,一般只要一重载,代码就会重复;可以通过调用的方式,减少重复代码。
/**
打印指定参数的任意乘法表
*/
public static void printCFB(int num)
{
for(int x=1;
x<=num;
x++)
{
for(int y=1;
y<=x;
y++)
{
System.our.print(y+"*"+x+"="+y*x+"t");
}
System.out.println();
}
}
/**
打印标准乘法表。
*/
public static void printCFB()
{
printCFB(9);
}
一般由于参数个数的原因重载,都能复用;而如果是参数类型,则不行。
函数调用 栈的形式
数组 数组的定义 概念:同一类型数据的集合,其实数组就是一个容器。
好处:可以自动给数组中的元素从0开始编号,方便操作这些元素。
格式:
/*=======第一种格式=======*/
元素类型[] 数组名 = new 元素类型[元素个数或数组长度];
/*=========示例========*/
int[] arr = new int[5];
/*=======第二种格式=======*/
元素类型[] 数组名 = new 元素类型[]{元素1, 元素2, ...};
/*=========示例========*/
int[] arr = new int[]{3,5,1,7};
//用new,常规初始化方式
int[] arr = {3,5,1,7};
//静态初始化方式
//以上两种方式在大部分情况下都是通用的,但它们在重新赋值或传参时会有一点小差别
Example
int[] arr = new int[3];
System.out.println(arr[0]);
System.out.println(arr[1]);
//均输出0(默认赋值为0)
数组的内存分配及特点 Java内存分为五片:
- 寄存器(CPU使用)
- 本地方法区(调用系统底层内容)
- 方法区(别名【数据区、共享区、share data】,存储方法)
- 栈内存(基本数据类型):存储的都是局部变量,变量所属的作用域一旦结束,该变量就会自动释放。
- 堆内存(引用数据类 ):存储数组和对象(其实数组也是对象),凡是new建立的对象都在堆中。
- 每一个实体都有首地址值。
- 堆内存中的每一个变量都有默认初始化值,根据类型的不同而不一样,整数:0,小数:0.0或者0.0f,boolean:false,char :‘u0000’,实体:null。
- 垃圾回收机制:当堆中的实体没有被任何变量指向时,不像栈一样,会立刻被释放,而是会被视作垃圾,被自动回收,垃圾回收机制会不定时的,自动检测堆里的垃圾,进行回收。C++需要程序员手动回收堆里的垃圾,析构函数。
int[] arr = new int[3];
//arr在栈中,new int[3]在堆中,堆中存的是实体,实体的用途:用于封装数据,存储很多数据
文章图片
Example2
sytem.out.print(arr[0]);
//先找arr,再根据地址找数组,找到数组后找对应的元素
arr = null;
//让arr不指向任何实体对象
数组操作常见问题 Example1:
ArrayIndexOutOfBoundsException
异常int[] arr = new int[3];
System.out.println(arr[3]);
编译完,不会出错,因为编译时不会在内存中建立数组,因此也不会找角标
运行时,产生问题,当访问到数组中不存在的角标/索引时,就会产生
ArrayIndexOutOfBoundsException
异常。Example2:
NullPointerException
异常int[] arr = new int[3];
arr = null;
System.out.println(arr[0]);
运行出错,当引用型变量没有任何实体指向时,还在用其操作实体,就会产生
NullPointerException
异常。Example3
int[] arr = new int[3];
System.out.println(arr);
//[I@c17164
@
右边:哈希值,哈希本身是一种算法,由这个算法来定义数组实体在内存中存储的位置,可以简单理解为是一个地址值,windows支持的,Java调用。@
左边:代表实体的类型, [
:数组型, I
:int型。测试的时候用,当我们不知道这个变量接收的数据是什么类型时,可以打印出来看看。
数组常见操作 Example1:获取最值(最大值,最小值)
class Demo
{
public static void main(String[] args)
{
int[] arr = {-34,-19,-11,-109,-3,-56};
int max = getMax(arr);
System.out.println("max="+max);
}
public static int getMax(int[] arr)
{
int max = arr[0];
for(int x=1;
xmax) max = arr[x];
}
return max;
}
}
注意max初始化的问题,若初始化为0,则判断不出最大值。
Example2-1:选择排序,特定位置和其他位置比较
public static void selectSort(int[] arr)
{
for(int i = 0;
i < arr.length-1;
i++)
{
for(int j = i+1;
j < arr.length;
j++)
{
if(arr[i]>arr[j])
{
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
}
//性能问题
//在selectSort中,每次把最小的元素放到固定位置的过程中,会进行大量元素的交换,实际上只要找到值最小的元素,再与固定位置交换即可
public static void selectSort_2(int[] arr)
{
for(int x=0;
xarr[y])
{
num = arr[y];
index = y;
}
}
if(index!=x)
{
int temp = arr[x];
arr[x] = arr[index];
arr[index] = temp;
}
}
}
Example2-2:冒泡排序,相邻元素比较
public static void bubbleSort(int[] arr)
{
for(int x=0;
xarr[y+1])
{
int temp = arr[y];
arr[y] = arr[y+1];
arr[y+1] = temp;
}
}
}
}
//数组中元素的交换可以提取出来,写作函数
public static void swap(int[] arr,int a,int b)
{
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
public static void bubbleSort_2(int[] arr)
{
for(int x=0;
xarr[y+1])
swap(arr,y,y+1);
}
}
}
//其中的for循环也可以写成以下形式
for(int x=arr.length-1;
x>0;
x--)
{
for(int y=0;
y
注意:
1、可以通过调用
Arrays.sort(arr);
实现排序功能2、操作的是同一空间
文章图片
Example3:折半查找(二分查找)
public static int func(int[] arr, int value)
{
int min, max, mid;
min = 0;
max = arr.length;
mid = (min+max)/2;
while(min <= max)
{
mid = (min+max)/2;
//mid = (a+b)>>2;
if(arr[mid] < value)
min = mid+1;
else if(arr[mid] > value)
max = mid-1;
else
return mid;
}
return -1;
}
注意:
- 给定一个有序数组,若往该数组中存储一个元素,并保证数组仍然是有序的,那么这个元素存储的角标如何获取-->return min。
int[] arr = {13,15,19,27,33,45,78,106}; Arrays.binarySearch(arr,45); //若元素存在,返回元素所在的位置; //若元素不存在,返回的是-插入点-1;之所以-1,是因为避免返回0,例如Arrays.binarySearch(arr,5)
文章图片
public static void main(String[] args)
{
toHex(60);
}
//0,1,2,3,4,5,6,7,8,9,A, B, C, D, E, F
//0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
public static void toHex(int num)
{
for(int x = 0;
x < 8;
x++)
{
int temp = num & 15;
if(temp > 9)
System.out.print((char)(temp-10+'A'));
else
System.out.print(temp);
num = num >>> 4;
}
}
/*
什么时候使用数组呢?
如果数据出现了对应关系,而且对应关系的一方是有序的数字编号,可作为角标使用。将这些数据存储到数组中,根据运算的结果作为角标直接去查数组中对应的元素即可。
这种方式:称为查表法。
(存在对应关系,但无序,可以使用map)
*/
public static void toHex_2(int num)
{
if(num == 0)
{
System.out.print("0");
return;
}
char[] chs = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
char[] arr = new char[8];
int pos = arr.length;
while(num != 0)
{
int temp = num & 15;
arr[--pos] = chs[temp];
num = num >>> 4;
}
for(int x = pos;
x < arr.length;
x++)
{
System.out.print(arr[x]);
}
}
/*十进制转二进制/八进制类似
可以通过函数调用的方式,将通用的代码抽出来
*/
//十进制-->十六进制
public static void toHex(int num)
{
trans(num,15,4);
}
//十进制-->二进制
public static void toBinary(int num)
{
trans(num,1,1);
}
//十进制-->八进制
public static void toOctal(int num)
{
trans(num,7,3);
}
public static void trans(int num, int base, int offset)
{
if(num == 0)
{
System.out.print("0");
return;
}
char[] chs = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
char[] arr = new char[32];
int pos = arr.length;
while(num != 0)
{
int temp = num & base;
arr[--pos] = chs[temp];
num = num >>> offset;
}
for(int x = pos;
x < arr.length;
x++)
{
System.out.print(arr[x]);
}
}
注意:可以通过Integer类的方法直接将十进制数转换为对应的进制数
System.out.println(Integer.toBinaryString(-6));
System.out.println(Integer.toHexString(5));
System.out.println(Integer.toOctalString(32));
二维数组 格式:
格式1:
int[][] arr = new int[3][2];
- 定义了名称为
arr
的二维数组 - 二维数组中有3个一维数组
- 每一个一维数组中有2个元素
- 一维数组的名称为
arr[0]
,arr[1]
,arr[2]
- 给第一个一维数组1角标位赋值为78写法:
arr[0][1] = 78;
int[][] arr = new int[3][];
- 二维数组中有3个一维数组
- 每个一维数组都是默认初始化值null
- 可以对这三个一维数组分别进行初始化
arr[0] = new int[3]; arr[1] = new int[1]; arr[2] = new int[2];
int[][] arr = {{3,1,7},{5,8,2,9},{4,6}};
还可以写成如下方式:
int[][] arr = new int[3][2];
int[] arr[] = new int[3][2];
int arr[][] = new int[3][2];
内存图示:
文章图片
Example1:直接打印二维数组
int[][] arr1 = new int[3][2];
System.out.println(arr);
//[[I@c17164
System.out.println(arr[0]);
//[I@1fb8ee3
System.out.println(arr[0][0]);
//0int[][] arr2 = new int[3][];
System.out.println(arr);
//[[I@c17164
System.out.println(arr[0]);
//null
System.out.println(arr[0][0]);
//NullPointerException
【毕向东Java基础教程-Java基础语法【下】】Example2:数组长度
System.out.println(arr.length);
//打印二维数组的长度-->就是一维数组的个数。
System.out.println(arr[1].length);
//打印二维数组中角标为1的一维数组的长度。
推荐阅读
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 图书集合完毕
- 事件代理
- Java|Java OpenCV图像处理之SIFT角点检测详解
- java中如何实现重建二叉树
- 数组常用方法一
- 【Hadoop踩雷】Mac下安装Hadoop3以及Java版本问题
- 感谢那年夏天
- Java|Java基础——数组
- RxJava|RxJava 在Android项目中的使用(一)