1. Java基本语法

2021/10/14 22:14:38

本文主要是介绍1. Java基本语法,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

主要来自于《尚硅谷Java教程》

目录
  • 变量和运算符
    • 关键字和保留字
    • 标识符
    • 变量
    • 数据类型
      • 基础数据类型
        • 整数类型
        • 浮点类型
        • 字符类型
        • 布尔类型
      • String数据类型
    • 类型转换
      • 自动类型提升
      • 强制类型转换
    • 进制转换
      • 二进制
    • 运算符
      • 算数运算符
        • 取模运算
        • 自增/自减运算符
      • 赋值运算符
      • 比较运算符
      • 逻辑运算符
      • 位运算符
      • 三元运算符
  • 程序流程控制
    • 使用Scanner从键盘获取数据
    • 分支结构
    • 循环结构
      • 带标签的break和continue
  • 数组
    • 概述
    • 一维数组
      • 初始化
        • 默认初始化值
      • 内存结构的简单说明
    • 多维数组
      • 二维数组
        • 默认初始化值
    • 常见算法
      • 随机生成一个两位数
      • 数组的反转
      • 二分查找
    • 排序算法
      • 冒泡排序
      • 快速排序
    • Arrays工具类的使用

变量和运算符

关键字和保留字

  • 关键字(keyword)是被Java赋予了特殊含义、用作特殊用途的字符串,全部为小写,如定义数据类型的class, boolean和用于定义流程控制的switch, while等。

  • 保留字(reserved word)在现有Java版本尚未使用,但以后版本可能会作为关键字使用,需要在命名标识符时避免使用,包括:goto, const

标识符

标识符(identifier)是对变量、方法和类等命名时使用的字符序列。

  • 由26个英文字母、0-9、_$组成。

  • 严格区分大小写。

  • 不能以数字开头、不能包含空格。

命名规范:

  • 包名:多单词的所有字母都小写,helloworldtest
  • 类名、接口名:所有单词首字母大写,HelloWorldTest
  • 变量名、方法名:第一个单词首字母小写,其余单词首字母大写,helloWorldTest
  • 常量名:所有字母大写,多单词时用下划线连接,HELLO_WORLD_TEST

变量

变量用于在内存中保存数据,概念:

  • 内存中的一个存储区域。
  • 该区域的数据可以在同一类型范围内不断变化,Java属于强类型语言。
  • 变量是程序中最基本的数据单元,包含变量类型变量名和储存的

需要注意:

  • 变量的作用域:其定义所在的一对{}内。

数据类型

按照数据类型可以分为:

  • 基本数据类型(primitive type):整数(byte, short, int, long)、浮点(float, double)、字符(char)、布尔(boolean)。
  • 引用数据类型(reference type):类(class)、接口(interface)、数组([])。

按照声明位置的不同可以分为:

  • 成员变量:在方法体外,类体内声明的变量,分为实例变量与类变量(以static修饰)。
  • 局部变量:在方法体内部声明的变量,包括形参(方法、构造器中的变量)、方法局部变量(在方法内定义)、代码块局部变量(在代码块定义)。

具体内容参考面向对象。

基础数据类型

整数类型

类型 占用空间 范围
byte 1 byte = 8 bit \(-128\) ~ \(127\)
short 2 bytes \(-2^{15}\) ~ \(2^{15}-1\)
int 4 bytes \(-2^{31}\) ~ \(2^{31}-1\)
long 8 bytes \(-2^{63}\) ~ \(2^{63}-1\)

主要注意,Java各个整数类型有固定的字段长度和表达范围,不受OS影响,以保证可移植性。

  • Java的整数类型默认为int
  • 声明long类型时,如果数字大小超过int的范围,数字后须加l或者L,这是因为Java默认把数字当成int类型来处理。

浮点类型

类型 占用空间 范围
单精度float 4 bytes \(-3.403\times 10^{38}\) ~ \(3.403\times 10^{38}\)
双精度double 8 bytes \(-1.798\times 10^{308}\) ~ \(1.798\times 10^{308}\)

Java的浮点类型常量默认为double,声明float类型,需要加fF。浮点型常量有两种表现形式:

  • 十进制:如5.12, 512.0f, .512。
  • 科学计数法:5.12e2, 512E2, 100E-2。

字符类型

char类型用来表示通常意义上的字符,占用2 bytes。

  • Java中的所有字符都使用Unicode编码,故一个字符可以存储一个字母或一个汉字。
  • 可以直接用Unicode编码来表示字符常量:\u000a表示\n,其中000a为十六进制整数。
  • char类型可以运算,因为它有对应的Unicode码。

布尔类型

boolean只能取turefalse

String数据类型

String属于引用数据类型,声明时使用一对""

  • String类型可以和8种基本数据类型变量做运算,且只能是连接运算+,运算的结果仍然是String

不能通过强制类型转换将String转换为其他基本数据类型。

String str1 = 123 + "";
// int num1 = (int) str1;  // 编译不通过
int num1  = Integer.parseInt(str1);  // 123

字符串连接:

char c = 'a';
int num = 10;
String str = "hello";

System.out.println(c + num + str);  // output: 107hello
System.out.println(c + str + num);  // output: ahello10
System.out.println(c + (num + str));  // output: a10hello
System.out.println(str + num + c);  // output: hello10a

类型转换

只讨论7种基本数据类型变量间的运算,不包含boolean

自动类型提升

  • 当容量小的数据类型变量转换为容量大的数据类型的变量时,结果自动提升为容量大的数据类型。
  • 顺序表示为:byte / short / char < int < long < float < double,这里的容量指表示数据范围的大小而不是占用空间的大小,例如:long占用8个字节,float占用4个字节,但是float的表示范围比long更大。
short s1 = 123;
double b1 = s1;
System.out.println(d1); // output: 123.0
  • 特别地,当byte / short / char这3种类型做运算时,结果为int型,包括相同种类(如2个byte变量相加)。
byte b1 = 1;
short s1 = 10;
// short s3 = b1 + s1; // 编译不通过
int i1 = b1 + s1;

强制类型转换

将容量大的数据类型变量强制赋值给容量小的数据变量,可能会导致精度损失

// 没有精度损失
long l1 = 123;
short s1 = (short) l1;  // 123

// 精度损失,截断
double d1 = 12.9;
int i1 = (int) d1;  // 12

// 精度损失,溢出
int i2 = 128;
byte b = (byte) i2;  // -128

进制转换

对于整数类型,有4种表达方式:

  • 二进制(binary):以0b0B开头。
  • 十进制(decimal):0-9。
  • 八进制(octal):0-7,以0开头。
  • 十六进制(hex):以0x0X开头,A-F不区分大小写,0x21af + 1 == 0x21b0

二进制

Java整数常量默认是int类型,当用二进制定义整数时,第32位是符号位。正数的原码、补码、反码相同,负数的补码为其反码+1。 计算机底层都以补码的方式存储数据。

  • 原码:直接将一个数值转换为二进制,最高位为符号位。
  • 负数的反码:符号位为1,其余对原码按位取反。
  • 负数的补码:其反码+1。

运算符

算数运算符

取模运算

结果的符号与被模数相同。

int m1 = 12;
int n1 = 5;
System.out.println(m1 % n1);  // 2

int m2 = -12;
int n2 = 5;
System.out.println(m2 % n2);  // -2

int m3 = 12;
int n3 = -5;
System.out.println(m3 % n3);  // 2

int m4 = -12;
int n4 = -5;
System.out.println(m4 % n4);  // -2

自增/自减运算符

自增/自减运算符不会改变变量的数据类型。

short s1 = 100;
s1 -= 1;
s1--;
// s1 = s1 - 1;  // 编译失败,需要强制类型转换
System.out.println(s1);  // 98

赋值运算符

与自增运算符类似,+=, -=, *=, /=等赋值运算符不会改变数据类型。

比较运算符

比较运算符的结果都是boolean类型,包括==, !=, <, >, <=, >=, instanceof,其中instanceof用来检查是否是类的对象,用法如下:

System.out.println("Hello" instanceof String);  // ture

逻辑运算符

运算符 含义
& 逻辑与
&& 短路与
| 逻辑或
|| 短路或
! 逻辑非
^ 逻辑异或

位运算符

注意,没有<<<运算符。

运算符 含义 示例
<< 左移 3 << 2 => 12
>> 右移 3 >> 1 => 1
>>> 无符号右移 3 >>> 1 => 1
& 与运算 6 & 3 => 2
| 或运算 6 | 3 => 7
^ 异或运算 6 ^ 3 => 5
~ 取反运算 ~6 => -7
  • 位运算符操作的都是整数类型的数据。
  • <<:空位补0,被移除的高位丢弃,在一定范围内,每向左移动1位,相当于* 2
  • >>:正数右移后空缺位补0,负数补1,在一定范围内,每向右移动1位,相当于/ 2
  • >>>:最高位无论是1还是0,右移空缺位都补0。
  • ~:按照补码,各位取反。

2 << 38 << 1的复杂度是\(O(1)\),比2 * 8效率更高。

三元运算符

结构:(条件表达式) ? 表达式1 : 表达式2

  • 三元表达式可以改写为if-else语句,反之则不一定,参见下一条。
  • 表达式1表达式2要求是一致的,例如(m > n) ? 2 : "n is bigger"编译不通过。

程序流程控制

使用Scanner从键盘获取数据

  • 导包:import java.util.Scanner;
  • 实例化Scanner类。
  • 调用next()等方法读取数据。
import java.util.Scanner;

public class HelloWorld{
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);
        int num = scan.nextInt();
        String str = scan.next();
        System.out.println(num + str);
    }
}

分支结构

除了if-else结构外,有关switch-case结构需要注意:

  • switch中的表达式只能是这6种数据类型之一:byteshortcharint、枚举(JDK5.0新增)、String(JDK7.0新增)。
  • case之后只能声明常量,不能声明范围。
  • default结构是可选的,而且位置灵活。

另外switch-case结构比if-else结构效率稍高。

循环结构

带标签的break和continue

label: for(int i = 1; i <= 4; i++){
    for(int j = 1; j <= 10; j++){
        System.out.println(j);
        if (j % 4 == 0){
            // continue;  // 结束包裹此关键字最近的一层循环结构
            continue label;  // 结束指定标签的一层循环结构
        }
    }
}

数组

概述

数组(array)是多个相同类型数据按一定顺序排列的集合。

  • 有序排列。
  • 数组属于引用数据类型的变量。
  • 创建数组对象会在内存中开辟一整块连续的空间。
  • 数组的长度一旦确定就不能修改。
  • 分类:按照维度分为一维数组和多维数组;按照数据类型分为基本数据类型元素的数组和引用数据类型元素的数组。

一维数组

初始化

数组一旦初始化完成,其长度就确定了。

// 静态初始化:数组的初始化和数组元素的赋值操作同时进行
int[] ids = new int[] {1001, 1002, 1003, 1004};

// 动态初始化:数组的初始化和元素赋值分开进行
String[] names = new String[5];
names[0] = "Wang";

// 获取数组长度
System.out.println(ids.length + " " + names.length);

默认初始化值

  • 整型:0
  • 浮点型:0.0
  • char型:0或\u0000,而非'0'
  • boolean型:false
  • 引用数据类型:null

内存结构的简单说明

JVM中的内存结构简单分为:

  • 栈(stack):局部变量,如上面代码中的变量idsnames(仅存放数组的地址)。
  • 堆(heap):new出来的对象、数组等,如上述代码中idsnames对应数组开辟的连续空间。
  • 方法区:常量池、静态域等。

多维数组

二维数组

初始化方法如下:

// 静态初始化:数组的初始化和数组元素的赋值操作同时进行
int[][] arr1 = new int[][] {{1, 2, 3}, {4, 5}, {6}};

// 动态初始化:数组的初始化和元素赋值分开进行
String[][] arr2 = new String[5][3];
String[][] arr3 = new String[6][];

// 也是正确的写法
int[][] arr4 = {{1, 2, 3}, {4, 5}, {6}};  // 类型推断
int arr5[][] = new int[][] {{1, 2, 3}, {4, 5}, {6}};

默认初始化值

针对动态初始化方法一,例如int[][] arr = new int[3][4];,则初始化值为

  • 外层元素:地址值。
  • 内层元素:与一维数组相同。
int[][] arr = new int[3][4];
System.out.println(arr[0]);  // 地址值:[I@28d93b30
System.out.println(arr[0][0]);  // 0

针对动态初始化方法二,例如int[][] arr = new int[3][];,则初始化值为

  • 外层元素:null
  • 内层元素:不能调用,否则报错。

常见算法

随机生成一个两位数

(int) (Math.random() * (99 - 10 + 1) + 10);
/*
 * Math.random()                    =>   [0.0, 1.0)
 * Math.random() * 90               =>   [0.0, 90.0)
 * (int) (Math.random() * 90)       =>   [0, 89]
 * (int) (Math.random() * 90 + 10)  =>   [10, 99]
 */

数组的反转

在空间复杂度为常数级的情况下,反转数组。主要思想为交换头尾元素。

// 随机生成20个两位数
int[] arr = new int[20];
for(int i = 0; i < arr.length; i++){
    arr[i] = (int) (Math.random() * 90 + 10);
    System.out.print(arr[i] + " ");
}
System.out.println();

// reverse 1
/*
for (int i = 0; i < arr.length / 2; i++){
	int temp = arr[i];
	arr[i] = arr[arr.length - 1 - i];
	arr[arr.length - 1 - i] = temp;
}
*/

// reverse 2: 双指针交换,实际操作上与方法一相同
for (int i = 0, j = arr.length - 1; i < j; i++, j--){
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

二分查找

// 二分查找(必须是有序的)
int [] arr2 = new int[] {-98, -34, 2, 34, 54, 66, 79, 105, 210, 333};
int head = 0;  // 初始前索引
int end = arr2.length - 1;  // 初始后索引
boolean isFound = false;  // 是否被找到

int target = 210;  // 查找的目标值
while (head < end){
    int middle = (head + end) / 2;
    if (target == arr2[middle]){
        System.out.println("Find " + target + ", index = " + middle + ".");
        isFound = true;
        break;
    }
    else if (target < arr2[middle]){
        end = middle - 1;
    }
    else {
        head = middle + 1;
    }
}

if (!isFound){
    System.out.println("Can't find " + target + ".");
}

排序算法

衡量排序算法:

  • 时间复杂度:关键字比较次数和记录的移动次数。
  • 空间复杂度:算法需要多少辅助内存。
  • 稳定性:若两个记录A和B的关键字值相等,排序后A、B的先后次序保持不变,则称算法是稳定的。

冒泡排序

for (int i  = 0; i < arr.length - 1; i++){
    for (int j = 0; j < arr.length - 1 - i; j++){
        if (arr[j] > arr[j+1]){
            int temp = arr[j];
            arr[j] = arr[j+1];
            arr[j+1] = temp;
        }
    }
}

快速排序

public class QuickSort {
    // 交换数组的两个索引上的值
    private static void swap(int[] data, int i, int j){
        int temp = data[i];
        data[i] = data[j];
        data[j] = temp;
    }

    // 子方法
    private static void subSort(int[] data, int start, int end){
        if (start < end){
            int base = data[start];
            int low = start;
            int high = end + 1;
            while (true){
                // 通过两个while循环,向右移动low,向左移动high
                // 直到找到大于base的low和小于base的high
                while (low < end && data[++low] <= base);
                while (high > start && data[--high] >= base);

                // low和high没有碰面,需要交换,循环继续
                if (low < high){
                    swap(data, low, high);
                }
                else {
                    break;
                }
            }
            
            // 将base移到中间,base左边全部是比它小的数字,右边全部是比它大的数字
            swap(data, start, high);
            
            // 递归地对base左半部分和右半部分排序
            subSort(data, start, high - 1);
            subSort(data, high + 1, end);

        }
    }

    // 快速排序
    public static void quickSort(int[] data){
        subSort(data, 0, data.length - 1);
    }
}

Arrays工具类的使用

java.util.Arrays是操作数组的工具类,里面定义了很多操作数组的方法。

// 1. 判断两个数组是否相等
int[] arr1 = new int[] {1, 2, 3, 4, 5};
int[] arr2 = new int[] {1, 2, 3, 4, 5};
System.out.println(Arrays.equals(arr1, arr2));
// true

// 2. 输出数组
System.out.println(Arrays.toString(arr1));
// [1, 2, 3, 4, 5]

// 3. 将数组填充为指定值
Arrays.fill(arr1, 10);
System.out.println(Arrays.toString(arr1));
// [10, 10, 10, 10, 10]

// 4. 对数组排序
int [] arr3 = new int[] {98, 34, 2, 34, 54, -66, -79, 105, -210, 333};
Arrays.sort(arr3);
System.out.println(Arrays.toString(arr3));
// [-210, -79, -66, 2, 34, 34, 54, 98, 105, 333]

// 5. 二分查找
System.out.println(Arrays.binarySearch(arr3, 105));  // 如果找到,输出索引
System.out.println(Arrays.binarySearch(arr3, -2));  // 负数表示没找到
// 8
// -4


这篇关于1. Java基本语法的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程