Java基础(一) —— 变量、运算符、流程控制、数组与泛型

本文最后更新于:2022年2月13日 晚上

概览:Java基础,变量与常量、类型转换、流程控制、数组的使用和Java泛型基础以及可变参数。

预警!仅用于本人快速自学,不过欢迎指正。

注释

Java有三种注释:单行注释,多行注释以及文档注释。

  • 文档注释用于配合JavaDoc,用于生产API文档。
1
2
3
4
5
6
7
8
9
10
11
12
13
// 单行注释
public class Main{
/*
* @Auther Diamond 文档注释
**/
public static void main(String args[]){
/*
多行注释
*/
System.out.println("Hello World!");
}
}
/**/

关键字

  • 被赋予特殊含义有专门用途的字符串
  • Java中的关键字所有字母均为小写

标识符

  • Java对各种变量方法和类等要素命名时使用的字符序列称为标识符。

命名规则

  1. 标识符仅由26个英文字母、数字、下划线和美元符号组成。
  2. 标识符开头只能是英文字母、下划线或者美元符号。
  3. 不可以使用关键字,但能够包含关键字。
  4. 严格区分大小写,长度无限制。
  5. 另,由于Java采用的不是ASCII字符集,而是Unicode字符集,其实是可以采用汉字来命名的,但是非常不建议!

变量

  • 变量是内存中的一个存储区域
  • 该区域拥有自己的名称——变量名和类型——数据类型
  • 访问这块区域是通过变量名来进行的
  • Java中的变量需要先声明后使用

变量分类

基本数据类型

  • 数值型
    • 整数类型 byte、short、int、long
    • 浮点数类型 float、double
  • 字符型 char
  • 布尔型 boolean

引用数据类型

  • 类 class
    • 例如 String
  • 接口 interface
  • 数组 []

整数类型

  • Java中各个整数类型有固定的表数范围和字段长度,不受OS的影响。而C++则没有。
  • Java默认的整型常量为int型,没有byte和short型常量的表示法,long型常量后需要加l或者L

浮点数类型

  • Java中各个浮点数类型有固定的表数范围和字段长度,不受OS的影响。
  • Java浮点数默认为double型,float型常量后面需接f或者F

浮点数有两种表示形式

  1. 十进制表示:0.512512.0f.512f
  2. 科学计数法表示:5.12E2512e-2f

字符类型

char占据两个字节,165位,表示字符。

字符串常量:

  1. 使用单引号''括起来的字符,例'a''中'
  2. 使用转义字符\将其后的字符转变为特殊字符型常量,例换行符\n、回车符\r
  3. 直接使用Unicode值来表示字符型常量,'\uXXXX',xxxx是一个十六进制整数。

ASCII: 一个英文字符一个字节,一个中文汉字两个字节

UTF-8: 一个英文字符一个字节,一个中文三个字节

Unicode: 一个英文字符两个字节,一个中文两个字节

—- 狂神笔记《2、JavaSE:基础语法》

各种字符集和编码详解 - 快乐就好 - 博客园 (cnblogs.com)

布尔类型

  • boolean适用于逻辑运算,一般用于流程控制。
  • boolean只能取true或者false!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args){
System.out.println(".512: " + .512f);//0.512
System.out.println("512e-1f: " + 512e-2f);//5.12

System.out.println("int size: " + Integer.SIZE);//32
System.out.println("int max: " + Integer.MAX_VALUE);//2147483647

System.out.println("float max: " + Float.MAX_VALUE);//3.4028235E38
System.out.println("float min: " + Float.MIN_VALUE);//1.4E-45

System.out.println("double max: " + Double.MAX_VALUE);//1.7976931348623157E308
System.out.println("double min: " + Double.MIN_VALUE);//4.9E-324

System.out.println("char max: " + (int)Character.MAX_VALUE);//65535
}

引用类型 Reference Type

  • 使用null可以赋值给任何引用类型(类、接口、数组)的变量,用以表示这个引用类型变量中保存的地址为空。
  • 引用数据类型的大小统一为4个字节,记录的是其引用对象的地址
  • String类是引用类型,其创建出的字符串存放在数据区,保证每个字符串常量只有一个,不会产生多个副本。
1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) {
String s1 = new String("我要飞");
String s2 = new String("我要飞");

System.out.println(s1 == s2);//false

String s3 = "我要飞";
String s4 = "我要飞";

System.out.println(s3 == s4);//true
}

基本数据类型一览

类型 空间大小 范围 默认值
byte 1Byte -128 ~ 127 (byte)0
short 2Byte -2^15 ~ 2^15 -1 (short)0
int 4Byte -2^31 ~ 2^31 -1 0
long 8Byte -2^63 ~ 2^63 -1 0L
float 4Byte -3.403E38 ~ 3.403E38 0.0F
double 8Byte -1.798E308 ~ 1.798E308 0.0
char 2Byte Unicode字符,均2字节 \u0000
boolean 1Byte true,false false
  • boolean占据大小暂不清晰

其他进制数

  • 二进制数:使用0b或者0B开头,例如0b10。(JDK7及以后)
  • 八进制:使用0开头,例如010
  • 十六进制:使用0x或者0X开头,例如0x10
1
2
3
4
5
6
int i1 = 0b10;
int i2 = 010;
int i3 = 10;
int i4 = 0x10;

System.out.println(i1+" "+i2+" "+i3+" "+i4);//2 8 10 16

int类型下划线分隔符

JDK7新特型,方便程序员识别。

1
2
int i4 = 1_2345_6789;
System.out.println(i4);//123456789

变量与常量

变量作用域

  1. 静态变量:又类变量,使用static进行修饰,独立于方法之外的变量。从属于类,生命周期从类的加载到卸载。若不进行初始化则会有默认值。
  2. 成员变量:又实例变量,类内部定义,从属于对象,生命周期同对象一样,若不进行初始化则会有默认值。
  3. 局部变量:方法或者语句块内部定义的变量,局部变量是无默认值的,使用前需要声明和初始化。

常量

初始化之后不能够再改变的值,使用final来进行修饰。

1
2
final double PI = 3.14;
final String Java = "Java";

类型转换

Java是强类型语言。

1.自动类型转换

容量小的数据类型自动转换成容量大的数据类型。

1
2
char      -->        int
byte --> short --> int ---> long ---> float --> double
  • byte、short、char之间不会相关转换,默认会转到int类型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args){
int i = 12;
byte b = (byte)123;
short s = (short)345;
char c = 'a';
System.out.println(getType(s+b));//java.lang.Integer
System.out.println(getType(i+s+b));//java.lang.Integer
System.out.println(getType(c+b));//java.lang.Integer
}

// 获取类型名
public static String getType(Object test) {
return test.getClass().getName().toString();
}

2.强制类型转换

显示转换一个变量的类型:(type)var

转换的条件是数据类型必须兼容。

注意

  1. 不能对boolean类型进行类型转换。
  2. 把容量大的类型转为容量小的类型时必须使用强制类型转换。
  3. 转换过程中可能会出现溢出或者损失精度。
    • 溢出:例如int类型128转byte,超出目标的表示范围,就会截断溢出。
    • 损失精度:例如double转float
  4. 浮点数转整数是通过舍弃小数点来得到的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) {
boolean bool = true;
//int i1 = bool; 错误

int i2 = 128;
byte b1 = (byte)i2;
System.out.println(b1);//-128 溢出

float f1 = 123.456f;
int i3 = (int)f1;
System.out.println(i3);//123

double d1 = 123.456789123456789;
float f2 = (float) d1;
System.out.println(f2);//123.45679 损失精度
}

运算符

  1. 除法中int型除以int型仍然是int型,而非浮点型。
  2. 取模运算及求余运算,运算结果的符号取决于第一个数的符号。
  3. 自增自减分为前置与后置,后置是先使用再加减,前置则是先加减再使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public static String getType(Object o) {
return o.getClass().getName().toString();
}
public static void main(String[] args) {
//基本运算
int i1 = 10;
int i2 = -3;

System.out.println(i1/i2); //-3
System.out.println(i1%i2); //1

int i3 = 1;
System.out.println(i3++); //1
System.out.println(i3); //2
System.out.println(++i3); //3
System.out.println(i3); //3

/* += 这样的赋值运算符可以保证不更改元数据的类型 */

short s = (short)3;
//s = s+4; //错误 short + int 赋值给short会报错
s+=4; //这种方式可以保证short
System.out.println(s); //7
System.out.println(getType(s));//java.lang.Short

int i4 = 2;
//i4 = i4 * 0.1; // 错误,不能这样赋值
i4 *= 0.1;
System.out.println(i4); //0
System.out.println(getType(i4));//java.lang.Integer
}
  1. 逻辑运算符中&与&&都是与,不过&&是短路与,一般使用&&。或|也相同
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* & && 的差别在于&&会有短路运算 及 A&&B A错误时 B就不会被执行 */
int i5 = 1;
int i6 = 1;
if(i5 == 0 && (i6++ > 0)){
System.out.println(i5 + " " + i6);
}else{
System.out.println(i5 + " " + i6);
}

// 1 1

if(i5 == 0 & (i6++ > 0)){
System.out.println(i5 + " " + i6);
}else{
System.out.println(i5 + " " + i6);
}

// 1 2
  1. 位运算符是对二进制位进行运算。
符号 运算 补充
<< 左移 空位补零
>> 右移 空位补最高位的数据
>>> 无符号右移 空位补零
& 与运算 1 & 1 = 1,其他均为0
| 或运算 0 | 0 = 0,其他均为1
^ 异或运算 相同为0,不同为1
~ 反码 二进制按补码取反
1
2
3
4
5
6
7
int i7 = 8;
System.out.println(i7>>2); //2
System.out.println(i7<<2); //32
System.out.println(i7>>>2); //2
System.out.println(i7 & i7);//8
System.out.println(i7 | 1); //9
System.out.println(~i7); //-9
  1. 字符串连接符+,只要两侧有一个是String类型,就会将另一个自动转换为字符串再进行连接。

流程控制

switch语句

1
2
3
4
5
6
7
8
switch(exp){
case value1:
//...
break;//可选
case value2:
//...
default:
}
  1. switch(表达式)中表达式的返回值必须是下述几种类型之一:byte,short,char,int,枚举,String。String是从JDK7开始支持的,不过case后面的必须为字符串常量或者字面量。
  2. case子句后面跟的值类型必须与表达式相同,且只能是常量或者字面量,同时所有的case值均不相同。
  3. 当遇到break后会跳出switch语句,若case语句中没有break,则会继续执行后面的break语句。

for语句

增强for循环,适用于数组或者集合。

1
2
3
for(声明语句 : 表达式){
//....
}
  • 声明语句:声明一个新的局部变量,其类型应与数组元素类型匹配。
  • 表达式:数组名或者返回值为数组的方法。
1
2
3
4
String [] arg = {"hello","world","thanks","empty"};
for(String tmp : arg){
System.out.print(tmp + " ");
}

break与continue

  • break会跳出当前所在一个循环。
  • continue会是程序跳过当前的一次循环,使程序立刻跳转到下一次循环。

数组

  • 数组是相同类型数据的集合。
  • 数组描述的是相同类型的若干数据,按照一定的先后次序排列组合而成。

特点:

  1. 长度固定,一旦创建,长度不可改变。
  2. 元素类型必须相同。
  3. 元素类型可以是任意数据类型,即基本数据类型和引用数据类型都可以。
  4. 数组变量属于引用类型,数组本身就是对象,Java中对象是在堆中的,因此数组对象本身是在堆中的。

声明与创建

声明有两种风格:

1
2
3
type [] array;	//首选方式

type array[]; //同C/C++

声明数组时不能够指定长度,非法操作。

创建数组即分配内存空间,使用new操作符来创建。

1
array = new type[size];
  • 使用new type[size]来创建了一个数组,再把新创建的数组的引用赋值给了变量array。

声明与创建可以合并在一起:

1
type [] array = new type[size];
  • 数组元素通过索引来访问,索引值从0size-1
  • 数组长度的获取array.length

特点

与C/C++相比:

  1. Java不允许声明的时候数组方括号内指定元素个数!
  2. Java允许分配内存空间时使用int型变量来指定元素个数,C语言必须是常量!

初始化

静态初始化,即定义数组的同时分配空间并赋值。

1
int [] arr = {1,2,3};

动态初始化,声明与赋值分开操作。

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

默认值初始化,数组是引用类型,它的元素相当于类的实例变量,数组已经过分配空间,每个元素就会被默认值初始化。

数组的使用——参数、返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static int [] reverse(int [] arr) {
int [] array = new int[arr.length];

for(int i=0;i<arr.length;i++){
array[arr.length - i-1] = arr[i];
}

return array;
}

public static void main(String[] args) {
int [] arr = {1,2,3,4,5,6};
for(int tmp : arr){
System.out.print(tmp + " ");
}
System.out.println();

int [] arrtmp = reverse(arr);

for(int tmp : arrtmp){
System.out.print(tmp + " ");
}
System.out.println();
}

多维数组

多维数组可以看成是数组的数组。

例如,二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组。

1
2
3
4
5
6
7
int[][] darr = {{1,2},{3,4},{5,6},{7,8}};
int[][] darr = new int[len1][len2];

double [][] a = new double[3][];
a[0] = new double[3];
a[1] = new double[4];
a[2] = new double[7];
  • len1为行数,len2为列数。
  • Java数组是行优先的,同C语言。
  • 构成二维数组的一维数组不需要有相同的长度,可以在创建二维数组的时候分别指定一维数组的长度。
  • 二维数组中a.length是指该二维数组包含多少个一维数组,a[0].length是指第一个一维数组的长度。

其他

  1. 若两个数组具有相同的引用,则它们就有相同的元素,改变一个的元素,另一个也会跟着改变,毕竟都指向了同样的堆内存。
  2. 直接打印数组名字会得到数组的引用,但是char数组例外,需要先与字符串连接后才会打印数组引用。
  3. java.util.Arrays中的Arrays.toString()方法可以打印数组,Arrays.sort()方法可以对数组进行升序排序。Arrays.asList()可将一个数组转为List集合。

Java泛型与可变参数

格式:

泛型类:public class 类名<数据类型1,**···**>{}

泛型方法:public <数据类型> 返回值类型 方法名(){}

泛型接口:public interface 接口名<数据类型1,**···**>{}

注意:该数据类型只能是引用类型。

好处:
A:把运行时期的问题提前到了编译期间
B:避免了强制类型转换
C:优化了程序设计,解决了黄色警告线问题,让程序更安全

通配符:

? extends E  向下限定,E及其子类

? super E    向上限定,E及其父类

可变参数

(1)如果我们在写方法的时候,参数个数不明确,就应该定义可变参数。

(2)格式:修饰符 返回值类型 方法名(数据类型... 变量) {}

  • 可变参数实际上是一个数组!
  • 但是和数组不一样的地方在于,可变参数可以保证无法传入Null,而数组却可以传入空数组
  • 如果一个方法有多个参数,并且有可变参数,那么可变参数必须在最后面。

引用类型的例题解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Main {
public static void main(String[] args) {
Person p = new Person();
String[] fullname = new String[] { "Homer", "Simpson" };
p.setName(fullname); // 传入fullname数组
System.out.println(p.getName()); // "Homer Simpson"
fullname[0] = "Bart"; // fullname数组的第一个元素修改为"Bart"
System.out.println(p.getName()); // "Homer Simpson"还是"Bart Simpson"?
}
}

class Person {
private String[] name;

public String getName() {
return this.name[0] + " " + this.name[1];
}

public void setName(String[] name) {
this.name = name;
}
}

最终结果是:

1
Bart Simpson

原因:数组存储了地址,0x11和0x12,然后将数组赋值给了p对象,p对象指向了0x11;之后0x11被替换成了0x13,所以最终结果也替换成了0x13。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Main {
public static void main(String[] args) {
Person p = new Person();
String bob = "Bob";
p.setName(bob); // 传入bob变量
System.out.println(p.getName()); // "Bob"
bob = "Alice"; // bob改名为Alice
System.out.println(p.getName()); // "Bob"还是"Alice"?
}
}

class Person {
private String name;

public String getName() {
return this.name;
}

public void setName(String name) {
this.name = name;
}
}

最终结果:

1
Bob

原因:p的地址指向了bob指向的0x11,然后bob指向了0x12,但是没有改变p的指向。