高级编程语言的组成:关键字、标识符、注释、常量与变量、语句、函数、数组,下面一一介绍各个组成元素。
a) 关键字
i. 定义:关键字是一些英文单词,但在java中有特殊含义,自定义的变量不能和关键字重名。
ii.注:关键字一般都小写。
b) 标识符
i. 定义:标识符就是自定义的变量名
ii.规则:26个字母大小写、0-9、_、$组成
iii.注1:不能以数字开头
iv.注2:不能含有关键字
c) 注释
i. 注释在编译时全被删除。
ii.作用:注解程序和调试程序。
iii. 注释能用JDK/bin中的javadoc工具去提取成软件说明书。
d) 常量和变量运算符
i. 类型转换原则:占用内存小的类型向占用内存大的类型进行转换,这样能保证不丢失精度。
ii.注:只要是整数就是int型!!!赋值运算会进行自动强转,纯算数运算也会自动强转,但算数运算且含有变量时不会自动强转。
iii.注:JVM的自动强制类型转化只是针对需要强制类型转化的语句,如:byte b = 4;若是自动类型提升,JVM根本不会检查什么,直接提升就行了,如:byte b = 4;int a = b +10;
1. Bytea = 4;实际上JVM首先会检查4这个int型的数字是否超过byte的长度,若没超过则自动进行强制转化,若超过了就报错。
2. Bytea = 4+5;
JVM发现4、5都是int型,那么进行自动检查,看一下9转换成byte后是否丢失精度,发现不丢失,则直接自动进行强制类型转化。但,
byte a = 4,b = 5;
Byte c = a + b;//此时会报错!
第一句话JVM也会首先检查4和5能否转化成byte类型,发现可以,就自动强转;但第二句话,JVM也会自动检查,但发现等号后面出现变量,即使该变量是的int类型能转化为byte,但此时也会报错!
综上:
a) 只要是数字就是int型
b) 强制类型转化:所占内存大的类型赋值给所占内存小的类型
c) 自动类型提升:所占内存小的类型赋值给所占内存大的类型
d) 但大内存类型赋值给小内存类型的时候若不使用强转,JVM会自动判断大内存类型转换后是否会丢失精度,若不丢失精度,则自动强制类型转换,否则会报错。但这种赋值方法只适用于等号右侧是具体的值,不能使变量。
i. Bytea = 10;//进行一次自动的强制类型转化
ii.Byteb = 10;
Byte a = b;//右侧是int型的变量,无法进行自动的强转
iii. Byteb = 10;
Int a = b;//这是自动类型提升,不是强制类型转化,肯定不会丢失精度,这时候等号右侧是变量无所谓。
iv.自动类型提升:将所占内存小的类型——>所占内存空间大的类型
1. inta = 3;
2. byteb = 5;
3. a =a + b;//发生一次自动类型提升
v. 强制类型转换:将所占内存小的类型——>所占内存大的类型
1. //强制类型转换
2. byte c = 3;
3. //报错:这里的5是int型四个字节,3是byte型一个字节,最后的结果要求是byte型,如果不进行强制类型转换就会报错。
4. c = c + 5;
5. //强制类型转换:只截取int型最低位一个字节,若一个数的二进制高于四位,那么经过强制类型转化后也只有4为,就会丢失精度。
6. c = (byte)(c + 5);
vi.ASKII码表:二进制与字符对应表。
1. 0-48
2. a-97
3. A-65
vii. Unicode国际标准码表:java使用的是Unicode码表,该表兼容任何国家的语言。
viii. Char与int转化
1. System.out.println('a');//输出a
2. System.out.println(97);//输出97
3. System.out.println((char)(97));//强制类型转化int—>char
4. 注:char比int短,所以char转int要强制类型转化,而int转char只需自动类型提升。
e) 语句
i. 运算符共五种:算数运算符、赋值运算符、比较运算符、逻辑运算符、位运算符
ii.算数运算符 + -* / %(求余/取模) ++ --
1. 取模:正负只与前面那个数有关
2. 任何数%2结果都是0/1,可以实现开关交替运算
3. a =i++运算过程
a) Temp= i ;
b) i =i+1;
c) a = temp;
例:
Int i = 3;
I = i++;
System.out.println(“i=”+i);//i为3
Temp = i; //temp = 3;
I = i+1;//i=4;
I = temp;//i=3;
iii. 赋值运算符=、+=、-=、*=、/=、%=
1. 将左右两边运算后的值赋给左边
2. 例:
Short s = 3;
S += 4;
S = s + 4;
问: S += 4;S = s + 4;这两句话有什么区别?
答:Short s = 3;这是讲int转换成short,需要进行强制类型转化,用自动类型提升的方法赋值时,JVM自动检查了一下,发现3这个int型值转化成short后不会发生精度丢失,因此就自动默默地转化了;但执行S = s + 4时,由于等号右侧有int型还有变量,JVM不会自动强转,报错!而S += 4不会报错,因为他是赋值运算,和Short s = 3一样,只要不丢失精度JVM都会做自动强转。
iv.比较运算符==、!=、>、<、>=、<=、instanceof
比较运算符结果是boolean
“Hello”instanceof String 判断Hello是否是String类的对象
v. 逻辑运算符&、|、^、!、&&、||
1. ^:异或
只有真真=假,其余和或|一模一样
2. &与&&的异同
a) 同:他们的运算结果都是一样的
b) 异:a>0&a<10两条式子都要运算
a>0&&a<10左边一旦为false,右边不计算了,结果就是false
c) &&优点:&&比&效率高了一点点
3. |与||的异同
a) 同:他们的运算结果都是一样的
b) 异:a>0|a<10两条式子都要运算
a>0||a<10左边一旦为true,右边不计算了,结果就是true
c) ||优点:||比|效率高了一点点
vi.位运算符>>、<<、>>>、&、|、^、~
1. 位运算符是针对二进制进行的
2. 与运算&
Java中只要是数字就是int型。
一个int四个字节(4bit),一个比特=8个二进制位。
然后上下两行分别作与运算,得出的结果转化为十进制就是6&3的值。
3. 异或运算^
a) 特点:对同一个二进制异或两次得到原始二进制。因此可用于对二进制文件加密解密。
4. 取反运算~
a) 取二进制数的反码。
5. 左移运算<<
将二进制向左移动两位,高位丢掉,低位补足。
左移运算的运用:A左移几位就是A*2的几次方。
3<<10就是:3*2的10次方
6. 右移运算>>
3>>10就是:3 / 2的10次方
注:>>、<<运算都可以带正负,正负根据第一个数字的正负。
因此,二进制的最高位是符号位,右移之后最高位应当说原二进制的最高位。
7. 无符号右移运算>>>
与>>一样,只不过没有正负,因此,向右移动之后最高位用0补。
8. 三元运算符(条件表达式)?表达式1:表达式2
条件为true,执行表达式1;
条件为false,执行表达式2。
vii. 局部代码块
1. 局部代码块中声明的变量只在该代码块中有用,出了代码块该变量内存空间就被释放。
2. 局部代码块可以让程序员决定变量的生命周期,主动地释放变量内存,从而节省空间,提高性能!平时编程要注重这个!
3. 局部代码块中的局部变量生命周期:在局部代码块中被创建,到“}”时结束。
注:函数中的其他局部变量只有当函数执行完后,函数从方法区中消失,紧接着函数中所有的局部变量从栈内存中消失。
viii. 判断语句if-else
ix.选择语句switch-case
switch(表达式)//表达式只支持:short、int、byte、char、String、枚举
{
case 取值1:
执行语句;
break;
case 取值1:
执行语句;
break;
default:
执行语句;
break;
}
注:switch-case语句中只有一个大括号。
注:default可以随便放,不管放哪儿都是最后一个执行。
注:按照case的摆放顺序执行,若都不满足最后找default。
注:switch两种结束方式:
1.执行到break结束
2.全部执行一遍后结束,因此最后一个case或default的break可以不写!
注:
int x =3;
switch(x)
{
default:
System.out.println(“c”);
case 1:
System.out.println(“a”);
case 2:
System.out.println(“b”);
}
x=3先判断case1、case2发现都不匹配,就执行default,此时已经找到了一个满足条件的东西了,而且那个里面没有break,此时不会再判断case是否满足条件,而是依次执行,直到遇到break或执行完一遍switch-case。因此现在输出:cab
注:提高switch-case复用性
case 3:
case 4:
case 5:
System.out.println(“lala”);
break;
x=3、4、5都执行同一条语句。
x. 转义字符
1. 用于字符串中“hello\nworld”,且转义字符都是针对\后的字符进行的。
a) \n:回车
b) \t:制表符
c) \b:退格
d) \r:按下回车键
i. windows中回车符: \r\n
ii.linux中回车符: \n
e) \”:在字符串中使用双引号
f) \\:\
xi.break
1. break:跳出(终止)
2. break运用范围:switch、循环语句(for、while、do-while)。
3. break后不能再写语句了,写了会报错,因为break后语句永远也执行不到。
4. for(inti = 0 ; i<3 ; i++){
if(i>1)
break;//break只作用于循环或switch,不作用于if,因此跳出for循环
}
5. 若有多层循环,break只跳出离他最近的循环。
6. 跳出指定循环
for(int i = 0 ;i<3 ; i++){
for(int j = 0 ;j<3 ; j++){
break;//默认跳出最内层循环
}
}
wangcai:for(inti = 0 ; i<3 ; i++){
xiaoqiang:for(intj = 0 ; j<3 ; j++){
breakwangcai;//跳出指定循环
}
}
xii. continue
1. continue:退出本次循环,进入下一次循环。continue后的语句不执行了。
2. 作用范围:循环(for、while、do-while)
3. continue单独存在时后面不能有任何语句,否则会报错,因为continue后的语句永远也执行不到,但continue在if中,if外可以紧跟其他的语句,如:
for(int i = 0 ;i<5:i++){
continue;//错!!!
System.out.println(“xxx”);
}
for(int i = 0 ;i<5:i++){
if(i%2==0)
continue;//正确!
System.out.println(“xxx”);
}
4. 结束本次循环,进入指定的循环
wangcai:for(inti = 0 ; i<3 ; i++){
xiaoqiang:for(intj = 0 ; j<3 ; j++){
continuewangcai;//进入指定的循环
}
}
f) 函数
i. 编译和运行的过程
javac XXX.java:启动了java的编译器,将xxx.java编译成xxx.class文件;java xxx:启动了java虚拟机,运行xxx这个java程序。-运行时,JVM首先找main函数,找不到报NoSuchMethedError,找到将main函数入系统栈,执行到add方法时,再将add方法入栈,计算完结果返回给main函数,add出栈,这时候再执行main函数。
ii.函数重载
1. 同一个类+函数名相同+(参数类型不同or参数个数不同)+返回值类型不同
2. 函数重载有个原则,函数重载是根据函数名和形参类型寻找合适的函数,若符合这两个条件却找到了两个函数,会报错。因此,不能出现有相同函数名和形参的函数。
g) 数组
i. 定义
1. 第一种定义方式int[] a = new int[3];
ii.JVM内存的划分
1. 寄存器
2. 本地方法区:调用操作系统的方法。
3. 方法区(数据共享区)
a) 方法区分为静态方法区、非静态方法区
b) 静态方法区存放静态函数、静态成员变量
c) 非静态方法区存放非静态函数、类
4. 栈内存
a) 存储局部变量,变量所属的作用域运行结束,该变量就自动释放空间。(for、while、do-while、函数、局部代码块中的变量都是局部变量)
5. 堆内存
a) 存储数组和对象和成员变量(其实数组就是对象)
b) 每一个对象都有首地址。
c) publicstatic void main(String[] args){
int[] arr = newint[3];
}
执行main函数前JVM为main函数开辟一块内存空间,执行int[] arr时在main函数的栈内存中新建一个int类型的引用;接着执行new int[3],在堆内存中开辟一块连续的内存空间,堆内存中的对象都有首地址,=即为把堆内存的首地址赋给arr这个int类型的引用,从而建立起了从引用名到对象间的联系。
arr[2]=80即把堆内存中数组的值改为80。
arr = null即把arr所对应的地址去掉变为null,即把箭头删掉,此时堆内存中的对象没有变量引用它,就变成了垃圾,等待JVM自动回收。
public staticvoid main(String[] args){
int[] x = newint[3];
int[] y = newint[3];
x[0]=9;
y[0]=34;
}
进一步:x = y; y = null;
x、y指向了同一个对象。
6. 第二种定义方式int[] a = new int[]{89,20,300};
7. 第三种定义方式int[] a = {89,20,300};
8. 选择第一种定义方式:需要一个容器,知道容器的大小,但不明确容器中的数据。
选择第二、三中定义方式:需要一个容器,而且数据已知。
三种方式左侧都不变。
h) 二维数组
i. 定义方法一:int[][] a = new int[3][2];
ii.内存分配过程
二维数组本质:一个大一维数组+n个小一维数组,大数组存放n个小数组的首地址,小数组存放数字。
首先执行main函数,JVM为main函数分配栈内存空间,执行int[][]a时在main方法栈中为局部变量a开辟一块内存空间初始化值为null。接着执行new int[3][2],首先在堆内存中开辟3个连续的空间,并且在这个三个空间中存放小数组的起始地址;紧接着分别开辟三个一维数组,并将起始地址赋给大数组,最后将大数组的起始地址赋给栈内存中的a。
iii. 定义方法二
1. 先给大数组初始化,在分别给小数组初始化
int[][] arr =new arr[3][];//只给大数组分配3个连续空间
arr[0] = newint[2];
arr[1] = newint[1];
arr[2] = newint[3];
2. 注:二维数组中每个小数组长度可以不一样。
iv.定义方法三
int[][]arr = {{3,3,4},{1,2},{,2,4,3,2}};
v. 数组的输出
1. int[]arr = new int[3];
System.out.println(arr);//输出:[@c1f3fe
注:输出的是这个一维数组的首地址,[表示它是一维数组,@后面的是地址的哈希值。
System.out.println(arr[0]);//输出:值
2. int[][]arr = new int[3][2];
System.out.println(arr);//输出:[[@c1f3fe
注:[[表示二维数组
System.out.println(arr[0]);//输出:[@f7s8d
System.out.println(arr[0][0]);//输出值
3. int[][]arr = new int[3][];
System.out.println(arr);//输出:[[@c1f3fe
注:[[表示二维数组
System.out.println(arr[0]);//输出:null
System.out.println(arr[0][0]);//发生空指针异常!
注:int[][] arr = new int[3][]这种定义方式只给大数组分配内存,未给小数组分配内存,因此访问小数组时出现空指针异常!
练习
1.高效计算2*8
乘法其实是二级制相乘,并且和十进制乘法法则一样。
2*8效率低,因为在底层进行了多次相乘和相加。
2 * 8 = 2 * 2的三次方,就是2<<3。这种方式效率高,不进行相乘相加操作,只要向左移位即可。
2.将两个值互换,不借助第三个变量
法一:
int a = 3,b = 4;
a = a+b;
b = a-b;
a = a-b;
弊端:若两个int长度过大,相加之后会超出int长度,从而精度丢失。
法二:
int a =3,b=4;
a = a^b; //
b=a^b;//a^b^b
a=a^b;//a^b^a
注:异或具有交换律:a^b^b = b^a^b=a