第2章Java语法基础
2.1 基本格式
1、所有Java代码都应该在一个class中。
2、Java是严格区分大小写的。
3、Java是一种自由格式的语言。Java代码分为结构定义语句和功能执行语句,功能执行语句最后必须以分号结束。
2.2注释
1、单行注释://
2、多行注释:/* */
3、文档注释是Java中特有的一种注释,它可以通过JDK中的工具(javadoc.exe)解析,生成帮助文档。文档注释:/** 注释内容 */
4、作用:⑴、解释说明代码的含义
⑵、当代码发生错误时,帮助调试
5、/* */不能被嵌套,且注释不会出现在字节码文件中,即它不会影响到字节码文件的大小。
注意:面试时,一定要写注释:而且写代码时,第一件事就写多行注释。
2.3 标识符
1、标识符:可以理解为程序中我们自定义的一些名字,包括:包名、类名、方法名、变量名、常量名。
2、标识符的命名规则
⑴、由大小写字母、数字、下划线(_)和美元符号($)组成
⑵、开头不能是数字。
⑶、不能使用关键字。
⑷、推荐使用全英文。
3、标识符通用规范
⑴、类名、接口名:所有单词首字母大写,例如:XxxYyyZzz
⑵、变量名、方法名:从第二个单词开始首字母大写,例如:xxxYyyZzz
⑶、常量名:所有字母都大写,单词之间用下划线分割,例如:XXX_YYY_ZZZ
⑷、包名:全部小写,例如:xxx.yyy.zzz
4、关键字
注:java 无sizeof,goto, const 关键字,但不能用goto const作为变量名
5、注意:
⑴、标示符区分大小写
⑵、java语言使用Unicode标准字符集,在程序开发中,虽然可以使用汉字、日文等作为标示符,但不提倡尽量连下划线和数字都不用
⑶、起名字时,要注意提高阅读性
2.4 常量与变量
2.4.1常量
1、分类:整数常量、小数常量、布尔常量(true和false)、字符串常量,null常量、字符常量:用单引号括起来的单个字符构成的常量,如字母、数字、字符等。
2、常量也称final常量
3、常量声明格式:final数据类型 常量名称 = 值;
4、常量名称注意事项:
⑴、必须用大写字母
⑵、当定义常量如果属于“成员变量”,则必须在定义时就赋给初值,否则将会在编译时出现错误。
5、二进制的由来
6、进制转换
⑴、十进制转二、八、十六进制
除法取余,将要转换的数除以进制数,记住余数,再除以进制数,记住余数,直到这个数等于0为止,将所有余数反转就是对应的二进制表现形式。
⑵、二、八、十六进制转十进制
乘法,将要转换的数编号,编号从低位开始,从0开始,将每一位上的数乘以进制数的编号次方,最后将所有乘得的结果相加就是十进制表现形式。
⑶、二进制和八进制互转
八进制的每一位对应二进制的三位。之所以可以三位代表一位,是因为三位二进制数代表的最大值为7,所以逢八进一,就得跳到下一位。十六进制也是这个道理,四位二进制代表的最大整数是15.
⑷、二进制和十六进制互转
十六进制的每一位对应二进制的四位。
7、二进制负数
一个负数的二进制表现形式就是这个负数忽略符号的正数对应的二进制取反再加一。计算机中存储的二进制数最高位是0则是正数,是1则是负数。
2.4.2 变量
1、概念:将不确定的数据进行存储。
⑴、内存中的一个存储区域
⑵、该区域有自己的名称和类型
⑶、该区域的数据可以在同一类型范围内不断变化
2、什么时候定义变量?当数据不确定,而且需要进行存储时,就定义一个变量来完成存储动作。
3、数据类型
Java语言是强类型语言,对于每一种数据都定义了明确的具体数据类型,在内存中分配了不同的内存空间。
⑴、分类
a.基本数据类型: 8种
整数:① byte 1个字节 ② short 2个字节
③ int 4个字节 ④ long 8个字节
注意:整数之所以分这么多类,是为了节省内存空间
浮点数:① float 4个字节 ② double 8个字节
注意:单精度与双精度的精度不一样。为什么要以双精度为默认类型?
字符:char 2个字节,如char ch = ‘4’;字符型数据之所以可以运算,是因为它在ASCII码表中有对应的数字。Char类型中也可以放中文,如char ch = ‘你’;。
布尔:boolean 1个字节
b.引用数据类型:类、接口、数组都是引用数据类型
⑵、格式: 数据类型 变量名 = 初始化值;
⑶、类型转化
①、自动类型转换:在byte、short、char参与运算的时候会自动提升为int,相当于将一个占空间较小的值放入了一个较大的空间。
注意:{int a = 3,b = b + 2;},会报精度损失,原因是在b+2时,b会自动提升为int类型,但在赋值时左边的b为byte型,将int型变为byte类型会出现精度损失。
②、强制类型转换:可以将一个占空间较大的值使用(类型)的形式强制放入一个较小的空间,有可能损失精度。
③、字符串转换:任何值和字符串相加都会得到字符串。
⑷、有效范围
⑸、转义字符:在字符常量中,斜杠(\)是一个特殊的字符,它的作用是用来转变后面一个字符的含义,这些字符通常是不可见的或者有特殊意义的。
①'\r' 回车,回到一行的开始,window系统中,回车符是由两个字符(\r\n)来表示的。
②'\n' 换行,换到下一行,注意:charch=‘\n’是可以的,因为\n表示一个回车符
③'\t' 制表符,键盘上的Tab
④'\b' 类似退格,键盘上的Backspace
以上字符都不可见,无法直接表示,所以用斜杠加上另外一个字符来表示。
⑤'\'' 单引号,Java代码中单引号表示字符的开始和结束,如果直接写程序会认为前两个是一对,报错。
⑥'\"' 双引号,Java代码中双引号表示字符串的开始和结尾,如果要写一个包含双引号的字符串那么这个双引号也需要转义。
⑦'\\' 斜杠,Java代码中的斜杠是转义字符,用来和后面一个字符配合使用,在真正需要用斜杠的时候那么就要用另一个斜杠来转义。
以上字符都有特殊意义,无法直接表示,所以用斜杠加上另外一个字符来表示。
⑹、字符串:由若干个字符组成的一串。可以是一个字符、多个字符、或者一个都没有。字符串没有固定大小。
2.4.3 类型转换细节
1、精度损失
publicclass e{
publicstaticvoidmain(String[] args) {
byte b = 4;//在内存中运行实质,4是int型,赋给b时,先自动强转
byte b1 = 3;
byte b2 = 7;
b = b1 + b2;
}
}
结果:错误,可能损失精度,原因是b1,b2是变量,无法确定,不能判断、赋值,假如b2=127就会丢失精度。
2、溢出
publicclass e {
publicstaticvoidmain(String[] args) {
int x;
int x1 = Integer.MAX_VALUE;
int x2 = 98;
x = x1 + x2;
Sop(x);
}
}
结果是-2147483551,原因是默认的整型int运算时,不会报告损失精度的错误,一旦超出范围,会默认强转转换,保留自己原有位置,高位舍弃。
2.5 运算符
2.5.1 算术运算符
1、除法运算时,0不可以作为除数,系统会报出ArithmeticException异常
2、注意细节:
①、Sop(3500 / 1000 * 1000);结果为3000
②、如果涉及到负数时,负号只参考被模数,如5%-2=1 -5%3=-1
③、模运算的应用:开关运算
④、“+”可以作为连接符,也可以表示正号。任何数据与字符串相加是连接,都会变成字符串
⑤、取模的规律:如果模数大于被模数值为模数;如果模数等于被模数或者等于1,值为0。
3、++、--
①、{int a = 3,b; b = a++; }的内存图解
②、{int a = 3; a =a++; }的内存图解
2.5.2赋值运算符
1、面试题:
{short s = 3; s += 4;} => 结果是:s=7;
{short s = 3; s = s + 4;} =>结果是:报错,损失精度,原因与前面byte的例题相同
两者的区别:前者是一次运算, +=是赋值运算符等,内部会自动完成强转动作,即转换成short类型,,但后者是俩次运算,他先会被提升为int类型,运算后的结果还是int类型,如果赋值给short类型,会造成精度损失。
2.5.3 比较运算符
1、比较运算符运行结束之后返回的都是boolean值。
2、注意运算符==不要写成=
2.5.4 逻辑运算符
1、逻辑运算符的作用:连接boolean类型的表达式。
2、短路:在逻辑表达式中,从左端的表达式可推断出整个表达式的值,&& 在前半是false的时候短路,|| 在前半是true的时候短路。
面试题:有两个int型变量a和b,在不使用第三个变量的情况下交换两个变量中的值. {a=a^b;b=a^b;a=a^b;}即:a^b^b=a。将a=a^b代入b=a^b则得b=a^b^b=a;同理可以得到a=a^(b^a) =b;轻松完成交换。
2.5.5位运算符
1、“按位与”运算
“按位与”:&,将两个二进制数每一位进行&运算,两边都为1结果才为1,只要有一边是0,结果就为0。可以用于取某个二进制数的某一段数字。
2、“按位或”运算
“按位或”:|,将两个二进制数每一位进行|运算,两边都为0结果才为0,只要有一边是1,结果就为1。
注意:以上操作若操作数类型不同时,则结果的精度与精度高的操作数相同。
3、“按位异或”运算
“按位异或”:^,将两个二进制数每一位进行^运算,只要两边不同结果就为1,相同则为0。一个数异或同一个数两次,结果还是原来那个数,可用于加密。
4、java的移位运算符有三种:
①、<<左移:低位补0。左移几位就相当于乘以2的几次方。
②、>>右移:高位出现空位用原来高位来补。右移几位就相当于除以2的几次方。
③、>>>:无无符号右移:高位补0。正数移动没区别,负数移动后变为正数。
④、面试题:2*8最有效的计算方式:2<<3,因为底层的运算是二进制运算,而位运算也是二进制运算。
⑤、练习:将十进制转为十六进制
publicclass OperateDemo {
publicstaticvoid main(String[] args) {
int num = 60;
//获取60的最低4位,通过&15
int n1 = num & 15;
System.out.println((char)(12-10+'A'));
//获取下一组4位,将60右移4位
//为什么要用>>>?如果要转换的数是负数,这样做可以防止高位补1
int temp = 60 >>> 4;
//对temp的值进行最低4位&的获取
int n2 = temp & 15;
System.out.println(n2);
}
}
5、反码:~,就是取反
2.5.6三元运算符
1、语法:表达式 ? 结果1 : 结果2
2、如果表达式结尾为true取结果1,为false则取结果2。
注意:①、三元运算符也是有短路的效果,根据表达式的结果,只运行冒号一边的,另外一边的不参与运行。
②、注意运算过程中的自动类型提升
2.5.7运算符优先级
2.6 程序流程控制语句
2.6.1判断结构
1、if语句:
①、简单的if语句,如果它控制的语句只有一条,括号可以省略
②、if else语句:此结构与三元运算符相似,三元运算符是if else语句
③、if……else if语句:这只是一个单条语句,只能有一个执行。
2.6.2 选择结构——switch语句
1、 特点
①、switch语句选择的类型只有四种:byte、short、char、int
②、case之间与default没有顺序,先执行第一个case,没有匹配的case就执行default
③、结束switch语句的情况有两种:遇到break,或执行到switch语句结束。
④、如果匹配的case或default没有对应的break,那么程序会继续向下面执行,运行可以执行的语句,直到遇到break或者switch语句结束。
2、if与switch区别
①、if: a、对具体的值进行判断
b、对区间判断
c、对运算结果是boolean型的表达式进行判断。
②、switch: a、对具体的值进行判断
b、值的个数通常是固定的
3、技巧:对于几个固定的值判断,建议用switch语句,因为switch会将具体的答案都加载进内存,效率相对高一点。
2.6.3 循环结构
1、while循环
先判断while中的表达式结果是否为true,true则执行循环体,执行结束之后再次判断,如果表达式结果为false则跳出循环。
2、do...while循环
先执行一次循环体,然后判断while中的表达式,如果是true继续执行,如果是false则跳出循环。
3、for循环
①、格式:for(初始化表达式;循环条件表达式;循环后的操作表达式){}
for循环的括号中有三条语句,都是可选项。
语句1:这条语句会在整个循环开始之前执行,且仅运行一次,不参与循环。
语句2:必须是一个返回boolean值的表达式,如果写了这个语句那么每次循环开始之前会判断,true则执行循环,false则不执行。没写则直接执行。
语句3:这条语句在每次循环体运行结束之后执行。
这些语句的执行顺序:语句1à语句2à循环体à语句3à语句2à循环体à语句3……,例如:
publicclass ForTest {
publicstaticvoid main(String [] args) {
int x = 1;
for(System.out.println("a");x<3;System.out.println("c")) {
System.out.println("d");
x++;
}
}
}
其执行结果:a d c d c
②、for与while的联系与区别
a、他们可以互换
b、区别:如果变量只为循环增量而存在,也就是循环控制,为了节省内存,用for,因为该增量只在for语句内有效,for语句执行结束后,该变量也被释放,而while循环结束后,增量依然存在
c、无限循环最简单的形式:while(true){}和for(;;){}
4、循环嵌套——for练习:
①、打印九九乘法表
publicclass MultiplicationTable {
publicstaticvoidmain(String[] args) {
for(inti=1;i<=9;i++){
for(intj=1;j<=i;j++){
Sop(i+"*"+j+"="+i*j+"\t");
}
Sop();
}
}
}
②、打印 * * * *
* * *
* *
*
publicclass e {
publicstaticvoidmain(String[] args) {
for (int i = 1; i < 5; i++) {
for (int j = 1; j < i ; j++) {
Sop(" ");
}
for (int j = 5-i; j >=1 ; j--) {
Sop("* ");
}
Sop();
}
}
}
5、continue、break、return
①、continue:跳过一次循环,继续执行下一次
②、break:结束循环,break单独存在时,后面不要定义语句;如果break想要跳出当前指定的循环,可以通过标号来完成,例如:
outer:for(int i=0; i<3;i++) {
inner:for(intj=0;j<3;j++) {
if(j>1)
break outer;
System.out.print(i+"and"+j+" ");
}
}
打印结果是:0and0 0and1
注意:里面可以往外跳,但外面不可以跳进里面
③、return:结束方法
2.7 方法
1、定义格式:[<修饰符>]<返回值><方法名>([参数列表]){[java语句]}
注意:参数是有序的
2、形参、实参及返回值
①、形参:方法在声明时,必须指定运行所需要的参数格式,规定了所需参数的数目、类型和名称。
②、实参:在实际调用一个java方法时,必须提供与其形参相匹配的数据。实参可以是常量、变量或表达式。
③、参数匹配:数目、类型和名称出现的顺序必须和相应的形参保持一致。
3、方法的特点
①、封装功能代码
②、提高复用性
③、只有被调用,才会被执行
④、方法只能调用方法,不能在方法中定义方法。也就是说方法之间是平级的,没有包含关系,只有调用动作。
⑤、方法的结果应该返回给调用者,交由调用者处理。
4、如何定义方法
①、明确这个功能的结果是什么,即明确返回值类型。
②、明确这功能在实现的过程中是否需要未知内容的参与运算,即明确参数列表。
5、方法重载(overload)
①、定义:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或类型不一样。
②、特点:与返回值无关,只看参数列表。
③、打印multiplication table
import java.util.Scanner;
publicclass MultiplicationTable {
publicstaticvoidmain(String[] args) {
Scanner scan = new Scanner(System.in);
Sop("请输入你想要的:");
int num = scan.nextInt();
table(num);
}
publicstaticvoidtable(int num) {
for(inti=1;i<=num;i++){
for(intj=1;j<=i;j++){
Sop(i+"*"+j+"="+i*j+"\t");
}
Sop();
}
}
publicstaticvoid table(){
table(9);
}
}
2.8 数组
2.8.1一维数组
1、声明数组的格式
①、数组元素类型 数组名[];
②、数组元素类型[] 数组名;
2、为了使用数组还得为其分配内存空间,且要指明数组的长度,分配内存空间的格式:
数组名 = new 数组元素类型[数组元素个数]
注意:代码一般写成:元素类型[] 数组名 = new 元素类型[元素个数]
①、数组的引用其实就是数组在内存中的地址。数组名持有的是引用,而new所建立的对象是引用对象。
②、数组变量:与它所引用的数组是俩个相互分离的实体,为数组变量分配的空间保存着对数组的对象的引用。
3、内存空间的划分
①、寄存器
②、本地方法区:与系统无关,运行本地系统平台的内容。
③、方法区
④、栈内存:存储的都是局部变量,且所属的作用区一旦结束,该变量自动释放
⑤、堆内存:存储的是数组和对象,堆内存会为实体默认初始化,其规则如下:
4、常见问题
①、ArrayIndexOutOfBoundsException:当访问到数组中不存在角标。
②、NullPointerException;当引用型变量没有任何实体指向时,操作实体就会发生该异常。
5、数组初始化
①、元素类型[] 数组名 = new 元素类型[]{元素1,元素2,元素3 … …元素n}
②、元素类型[] 数组名 = {元素1,元素2,元素3 … …元素n}
2.8.2 常见操作
1、遍历
对数组操作最基本的操作是存和取,其核心思想就是对角标的操作。而变量就是对角标的操作,代码如下:
for(int x = 0; x < 数组名.length;x++){
Sop(数组名[x]);
或者Sop(“arr[“+x+”]=”+arr[x]+”;”);
}
反向遍历的for语句:for(int x = 数组名.length-1; x>=0;x--){}
2、选择排序
publicstaticvoid selectSort(int[] arr){
for (int i = 0; i < arr.length-1; i++) {
//之所以减1,是因为最后一个同自己比较没有意义。
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;
}
}
}
}
3、冒泡排序
publicstaticvoidbubbleSort(int[] arr){
for (int i = 0; i < arr.length-1; i++) {
//之所以减1,是因为最后一个同自己比较没有意义。
for (int j = 0; j < arr.length-1-i; j++) {
//-1:防止角标越界;-i:前面的排序已将其置于正确的位置,
//之后就没必要再在未来的数据遍历中考虑该值了。
if(arr[j]>arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
4、排序的性能问题
通过增加两个变量num= arr[i],index = x;通过num的比较,用index记录角标,具体代码如下:
publicstaticvoidselectSort(int[] arr){
for (int i = 0; i < arr.length-1; i++) {
int num = arr[i];
int index = i;
for (int j = i; j < arr.length; j++) {
if(num>arr[j]){
num = arr[j] ;
index = j;
}
if (index!=i) {
int temp = i;
i = index;
index = temp;
}
}
}
}
5、线性查找
publicstaticint getIndex(int[] arr, int key) {
for (int i = 0; i < arr.length; i++) {
if(arr[i]==key){
return i;
}
}
return -1; //一定要定义return,因为有返回值类型。
}
6、折半查找
①、折半的前提;数据是有序的。
②、折半查找的方法:binarySearch(arr,num);
方法一:
privatestaticint binarySearch_1(int[] arr, int key) {
int left = 0;
int right = arr.length-1;
while(left<right) {
int mid = (left + right)/2;
if(key>arr[mid]){
left=mid+1;
}elseif(key<arr[mid]){
right=mid-1;
}else
return mid;
}
return -1;
}
方法二;
privatestaticint binarySearch_2(int[] arr, int key) {
int left = 0;
int right = arr.length-1;
int mid = (left + right)/2;
while(arr[mid]!=key) {
if(key>arr[mid]){
left=mid+1;
}elseif(key<arr[mid]){
right=mid-1;
}
if(left>right) {
return -1;
}
mid= (left + right)/2;
}
return -1;
}
7、进制转换
①、查表法:如果数据出现了对应关系,而且对应关系的一方是有序的数字编号,并作为角标使用,就可以将这些数据存储到数组中,根据运算的结果作为角标去查找数组中对象的元素即可。
②、代码
publicclass TableWorking {
publicstaticvoidmain(String[] args) {
toHex(60);
toBinary(60);
toOctal(60);
}
publicstaticvoidtoOctal(int num) {
trans(num,7,3);
}
publicstaticvoid toBinary(int num) {
trans(num,1,1);
}
publicstaticvoidtoHex(int num) {
trans(num,15,4);
}
publicstaticvoidtrans(int num, int base, int offset) {
if(num==0){
Sop("0");
return;//这个不省的原因是不写他的话,就会执行下面的代码,浪费空间
}
//定义一个对应的关系表
char[] chs = {'0','1','2','3','4','5','6',
'7','8','9','A','B','C','D','E','F'};
char[] arr = newchar[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++) {
Sop(arr[x]);
}
Sop();
}
}
2.8.3 二维数组
1、格式:
①、元素类型[][] 数组名 = new 元素类型[m][n]
②、元素类型[][] 数组名 = new 元素类型[m][]
2、默认初始化:null
3、另一种定义格式:元素类型[][] 数组名 ={{元素i},{元素j}, … …{元素n}}