Java语言基础(SE)-第二节 基础语法
语法须知
- 每一条语句都必须以分号 ; 结尾
- 程序的入口是 main 方法
- 没有 main 方法,Java 程序是无法启动的
- 方法必须包含在 class 内部,先有 class,再有方法
- public class 的名称(类的名称)须要和文件名保持一致
- 疑问:如果一个 Java 项目中有 2 个不同的 class,它们都有自己的 main 方法,那岂不是有 2 个 Java 程序入口?
- 只能选择其中一个入口开始执行程序
- 当运行的时候,会让选择执行哪一个入口
-
Java 的注释有 3 种书写格式
//这是单行注释 /* 这是多行注释 */ //自动生成文档注释,输入`/**`然后回车即可 /** * * @param a * @param b * @return */
数据类型
Java 的数据类型主要分为 2 大类
- 基本类型(Primitive Type)
```
byte:8-bit 的整数,取值范围是 [–128, 127]
short:16-bit 的整数,取值范围是 [–32768, 32767]
int:32-bit 的整数,取值范围是 [–231, 2 31– 1]
long:64-bit 的整数,取值范围是 [–263, 2 63– 1]
float:单精度 32-bit IEEE 754 浮点数,取值范围是 [1.40E–45F, 3.4028235E38F]
double:双精度 64-bit IEEE 754 浮点数,取值范围是 [4.9E-324, 1.7976931348623157E308]
boolean:布尔类型,有 true、false 两个取值
char:单个 16-bit 的 Unicode 字符
``` 2. 引用类型(Reference Type)
1. 引用类型的值是对对象的引用
字面量(Literal)
//整数
//十进制
byte v1 = 123;
//二进制(或者0B11001)
short v2 = 0b11001;
//十六进制(或者0XF78A)
int v3 = 0xf78a;
//用L或者l结尾表示long类型(或者 1991)
long v4 = 199L;
//浮点数
//以F或者f结尾表示float类型(或者123.4f)
float v5 = 123.4F;
//以d或者D结尾表示double类型(或者123.4d)
double v6 = 123.4D;
//默认就是double类型
double v7 = 123.4;
//可以用科学计数法(E或者e)
float v8 = 1.234E2F;
double v9 = 1.234e2;
//字符、字符串
//用单引号表示字符
char v10 = 'A';
//用双引号表示字符串
String v11 = "ABCD";
//布尔
boolean v12 = true;
boolean v13 = false;
//空值
String string = null;
转义序列(Escape Sequences)
\b(退格,\u0008)
\t(制表符,\u0009)
\n(换行,\u000a)
\f(换页,\u000c)
\r(回车,\u000d)
\"(双引号,\u0022)
\'(单引号,\u0027)
\\(反斜杠,\u005c)
在数字中使用下划线
-
从 Java 7 开始,可以给数字添加下划线增强可读性
int v1 = 1_000_000; int v2 = 0xff_ec_de_5e; int v3 = 0b11011101_11111111_00010010; double v4 = 1.23_45_67; long v5 = 1___0000_0000;
-
下面的用法是错误的
//不能在浮点数的小数点前后使用下划线 double r1 = 1._23; double r2 = 1_.23; //不能在数字的前后使用下划线 int r3 = _123; int r4 = 123_; //不能在特殊字母的前后使用下划线 byte r5 = 0x_12; byte r6 = 0_b10010; float r7 = 1.23F_; long r8 = 189_L;
变量的初始化
- 任何变量在使用之前都必须要先初始化(赋值)
- 局部变量:需要程序员手动初始化
-
非局部变量(实例变量、类变量):编译器会自动给未初始化的变量设置一个初始值
类型 初始默认值 byte 0 short 0 int 0 long 0L float 0.0F double 0.0D char '\u0000' boolean false 对象(引用) null
运算符(Operator)
运算符 优先级
后缀 expr++ expr--
一元(单目) ++expr --expr +expr -expr ~ !
乘除模 * / %
加减 + -
位移 << >> >>>
关系 < > <= >= instanceof
等价 == !=
按位与 &
按位异或 ^
按位或 |
逻辑与 &&
逻辑或 ||
三元(三目) ? :
赋值 = += -= *= /= %= &= ^= |= <<= >>= >>>=
- 上面一行的优先级比下面一行高,同一行的优先级一样
- 当多个优先级一样的运算符一起使用时,按照结合性进行运算
- 只有赋值运算符的结合性是从右至左
- 其他运算符的结合性都是从左至右
- 为了保证运算符按照预期执行,尽量多使用小括号
- 比如 5 * ((a + b) / c)
- 算数表达式的结果必须被使用
字符串拼接
-
可以使用加号(+)进行字符串的拼接
int age = 18; String name = "jack"; double height = 1.78; System.out.println("my name is" + name + ", age is " + age + ", height is " + height);
位运算
>>
与>>>
>>
(有符号右移):最左用符号位补齐>>>
(无符号右移):最左用 0补齐
&、|、^
也能用在 boolean 类型上- 对比
&&、||,&、|
少了短路功能 - 短路功能:
&&
左边是false右边不用算。||
左边是true右边不用算
- 对比
类型转换(Type Conversion)
- 拓宽基本类型转换(Widening Primitive Conversion)
-
数据范围小的转为数据范围大的(19种),可以自动转换(隐式转换)
byte 转 short、int、long、 float、double short 转 int、long、float、double char 转 int、long、float、 double int 转 long、float、double long 转 float、double float 转 double
-
举例
byte b = 12; short s = b; int i = s; char c = 'A'; int ix = c; long l = ix; float f = l; double d = f;
-
- 窄化基本类型转换(Narrowing Primitive Conversion)
-
数据范围大的转为数据范围小的(22种),可能会丢失精度和范围,需要强制转换
short 转 byte、char char 转 byte、short int 转 byte、short、char long 转 byte、short、char、 int float 转 byte、short、char、int、long double 转 byte、short、char、 int、 long、 float
-
举例:
short s = 512; char c = (char)s; byte b = (byte) c; double d = 1.23; float f = (float)d; int i = (int)d;
-
数字提升
一元数字提升(Unary Numeric Promotion)
- 一元数字提升:将 byte、short、char 类型的一元数字自动提升为 int 类型(拓宽基本类型转换)
- 下面的情况会执行一元数字提升
- 数组的索引、创建数组时的数组长度
- 一元运算符 +
- 一元运算符 –
- 按位取反(~)
- 位移(«、»、»>)
int []array = {11,22,33,4}; byte index = 10; //会自动转化 array[index] = 30; char c1 = 'A'; System.out.println(c1); //A System.out.println(+c1); //65 char c2 = +c1; //报错,int类型不能直接赋值给char类型 char c3 = 65; //ok
二元数字提升(Binary Numeric Promotion)
- 二元数字提升:提升一个或者两个数字(拓宽基本类型转换)
- 如果任意一个数字是 double 类型,那么另一个就会被转换为 double 类型
- 否则,如果任意一个数字是 float 类型,那么另一个就会被转换为 float 类型
- 否则,如果任意一个数字是 long 类型,那么另一个就会被转换为 long 类型
- 否则,两个数字都被转换为 int 类型
- 下面的情况会执行二元数字提升
- 乘(*)、除(/)、取余(%)
- 加法(+)、减法(–)
- 比较(<、<=、>、>=)
- 判等(==、!=)
-
位运算(&、^、 ) - 三目(? :)
byte v1 = 1; byte v2 = 1; byte v3 = v1 + v2; //报错,结果是int,不能直接赋值给byte byte v4 = v1 + 2; //报错,同上 byte v5 = 1 + v2; //报错,同上 byte v6 = 1 + 2; //ok byte v7 = 1; v7 = v7 + 2; //error v1 += 2; //ok 复合赋值运算自带转换
关键字(Keyword)
- 关键字,也叫做保留字(reserved word)
-
Java 的关键字如下所示
abstract、continue、 for、new、 switch、assert、default、goto、package、synchronized boolean、do、if、private、 this、 break、 double、 implements、protected、throw byte、else、import、 public、 throws、case、enum、instanceof、 return、transient catch、extends、 int、short、 try、char、 final、interface、static、void class、finally、 long、 strictfp、volatile、const、float、 native、super、 while
- 虽然 goto、const 未被使用,但也属于关键字
- true、false、null 不是关键字,是字面量
标识符(Identifier)
- 标识符:变量名、方法名、类名等,命名规则如下
- 不限长度的 Java 字母、Java 数字序列,但必须以 Java 字母开头(区分大小写)
- 不能使用关键字
- 不能使用字面量 true、false、null
- Java 字母
- Character.isJavaIdentifierStart 方法返回 true 的字符
- 包括 ASCII 中的 A~Z、a~z,美元符($),下划线(_),中文,韩文,日文等字符
- Java 数字
- Character.isJavaIdentifierPart 方法返回 true 的字符,即该方法返回值为true,说明要么是Java字母,要么是Java数字
- Java 数字 包括 ASCII 中的 0~9
- 命名建议
- 变量名、方法名:小驼峰,比如 myNameAndAge
- 类名:大驼峰,比如 MyNameAndAge
- 常量:比如 MY_NAME_AND_AGE
数组
- 数组的创建
- 注意: 在Java中,字符数组 != 字符串
- 字符数组:char[]
- 字符串:String
- 推荐使用 char[] arr 格式定义数组,不推荐使用 char arr[] 格式定义数组
int[] arr1; int[] arr2 = {}; //空数组 int arr3[] = {}; //空数组 //定义的时候指定数组元素 int[] arr4 = new int[] { 1, 2, 3, 4}; //上面的简化,省略了new int int[] arr5 = { 1, 2, 3, 4 }; //定义的时候指定数组长度 int[] arr6 = new int[4]; arr6[0] = 1; arr6[1] = 2; arr6[2] = 3; arr6[3] = 4; //多维数组 int[][][] arr7; int[] arr8[][];
- 注意: 在Java中,字符数组 != 字符串
- 数组的内存
- Java 的数组属于引用类型,即对象
- 数组元素存储在堆空间(Heap)
- 举例:
public static void main(String[] args) { int[] arr = new int[] { 11, 22, 33}; }
- arr是一个引用类型,在main函数中是一个局部变量,因此放在了栈中,占用8个字节,内部存储的是元素存放堆空间的首字节地址
- 数组的元素放在堆空间,分配12个字节存放11、22、33这三个元素
-
注意:Java的数组元素都是放在堆空间
//下面2者等价 int[] arr = new int[] { 11, 22, 33}; int[] arr = { 11, 22, 33};
-
Java 的堆内存申请会自动进行初始化
//元素在堆空间中统一初始化为0 int[] arr6 = new int[4]; //元素在堆空间中统一初始化为null String[] arr6 = new String[4];
- Java 的数组属于引用类型,即对象
-
数组的遍历
//遍历数组 int[] arr = {11,22,33,44}; for(int i =0; i<arr.length;i++) { System.out.println(arr[i]); } //直接遍历每个元素 for (int ele : arr) { System.out.println(ele); }
方法(Method)
- 方法(Method)
- Java 中的方法,其实就是其他编程语言中的函数(Function)
-
方法的书写格式
修饰符 返回值类型 方法名(参数列表) { 方法体 }
- 可变参数( Variable Argument)
- 可变参数必须是方法中的最后一个参数
- 举例:JDK自带的 System.out.printf 方法使用了可变参数
-
格式字符串参考API文档的 java.util.Formatter 类
//格式化打印 String name = "Jack"; int age = 20; System.out.printf("name is %s,age is %d %n",name,age); //查看printf系统源码如下 public PrintStream printf(String format, Object ... args) { return format(format, args); }
-
-
举例使用
//调用 sum(1,2,3); public static int sum(int... numbers) { int result = 0; for (int i : numbers) { result += i; } return result; } public static int sum1(int... numbers , int name) {} //错误,可变参数必须放最后面
- numbers就相当一个数组,存储着传递过来的实参,如果调用时不传任何值,numbers默认就是空数组{};
- 参数传递
- 基本类型作为参数是值传递
- 基本类型作为返回值,返回的是值
- 引用类型作为参数是引用传递(地址传递)
- 引用类型作为返回值,返回的是引用(地址)
- 基本类型作为参数是值传递
- 方法签名(Method Signature)
- 方法签名由 2 部分组成:方法名、参数类型
-
下面方法的方法签名是:
sum(int, long, double)
public static double sum(int i,long l,double d) { return i + l + d; }
- 在同一个类中,不能定义2个方法签名一样的方法
- 方法的重载(Overload)
- Java 的方法支持重载:方法名相同,方法签名不同
- 参数个数不同
- 参数类型不同
- 重载与返回值类型、参数名称无关
public static int summary(int a, int b) { return a+b; } public static double summary(double a, double b) { return a+b; } //重载跟返回值无关 // public static char summary(double a, double b) { // return a+b; // } public static int summary(String a, int... b) { return 5; }
- Java 的方法支持重载:方法名相同,方法签名不同
- 栈帧(Frame)
- 栈帧随着方法的调用而创建,随着方法结束而销毁,存储了方法的局部变量信息
- 每个函数调用都会创建一个栈帧,当调用完毕销毁函数栈
- C++中详细分析过
- 递归调用
- 如果递归调用没有终止,将会一直消耗栈空间
- 最终导致栈内存溢出(Stack Overflow)
- 所以必需要有一个明确的结束递归的条件
- 也叫作边界条件、递归基
public static void main(String[] args) { sumx(100); } public static int sumx(int n) { if(n<1) return n; return n + sumx(n-1); }
- 如果递归调用没有终止,将会一直消耗栈空间
注意事项
- Java中不存在直接获取变量内存地址的方法
- Java不建议程序员去关注内存地址
官方文档
- Java 语言规范
- Java 虚拟机规范
- Java 教程
- Java API 文档
- JAVA语言、虚拟机、API 离线中英文 文档,详见 网盘资料01[官方文档]压缩包