java入门笔记基础语法篇(4)

变量

在Java中,每个变量都有一个类型(type)。在声明变量时,变量的类型位于变量
名之前。例如:
int days;
double salary;
long earthPopulation;
boolean done;
  • 在Java中,每个声明以分号结束。
  • 变量名必须是一个以字母开头并由字母或数字构成的序列。
  • 需要注意,与大多数程序设计语言相比,Java中“字母”和“数字”的范围更大。
  • 字母包括'A'~'Z'、'a'~'z'、'_'、'$'或在某种语言中表示字母的任何Unicode字符。
  • 如果想要知道哪些Unicode字符属于Java中的“字母”,可以使用Character类的isJavaIdentifierStart和isJavaIdentifierPart方法来检查。
  • 尽管$是一个合法的Java字符,但不要在你自己的代码中使用这个字符。 它只用在Java编译器或其他工具生成的名字中。
  • 另外,不能使用Java保留字作为变量名。

 初始化变量

声明一个变量之后,必须使用赋值语句对变量进行初始化,我们最好不要用未初始化的变量。否则java编译器会认为是未定义行为。

public class UninitializedVariableError {public static void main(String[] args) {int num;// 下面这行会报错,因为num未初始化System.out.println(num); }
}

在 IntelliJ IDEA 中,当你编写这段代码时,IDE 会在代码编辑区域直接给出提示。鼠标悬停在有问题的代码行上,会弹出提示框,显示类似 “Variable 'num' might not have been initialized” 的信息,同时在代码行号旁边会有红色波浪线标记。在编译运行时,运行窗口会显示编译错误信息,通常也会提示变量num未初始化。

 下面我们来看一段正确的示例,在代码中我已经给出了所有类型初始化的示例:

public class VariableInitialization {public static void main(String[] args) {// 初始化byte类型变量byte byteVar = 120;System.out.println("byte类型变量的值: " + byteVar);// 初始化short类型变量short shortVar = 3000;System.out.println("short类型变量的值: " + shortVar);// 初始化int类型变量int intVar = 2024;System.out.println("int类型变量的值: " + intVar);// 初始化long类型变量,需要在数字后面加L或llong longVar = 10000000000L;System.out.println("long类型变量的值: " + longVar);// 初始化float类型变量,需要在数字后面加F或ffloat floatVar = 3.14f;System.out.println("float类型变量的值: " + floatVar);// 初始化double类型变量double doubleVar = 3.1415926;System.out.println("double类型变量的值: " + doubleVar);// 初始化char类型变量char charVar = 'A';System.out.println("char类型变量的值: " + charVar);// 初始化boolean类型变量boolean booleanVar = true;System.out.println("boolean类型变量的值: " + booleanVar);}
}
  •  在java中,我们对一个已经声明过的变量进行赋值,就需要将变量名放在等号(=)左侧,相应取值的java表达式放在等号的右侧。我们可以将声明放在代码中的任何地方。
  • 但在Java中,变量的声明尽可能地靠近变量第一次使用的地方,这是一种良好的程序编写风格。

 常量 

在Java中,利用关键字final指示常量,例如:
public class BasicConstantExample {public static void main(String[] args) {// 使用 final 关键字声明一个 int 类型的常量final int DAYS_IN_WEEK = 7;// 下面这行代码会编译错误,因为常量的值不能被修改// DAYS_IN_WEEK = 8; System.out.println("一周的天数是: " + DAYS_IN_WEEK);// 声明一个 double 类型的常量final double PI = 3.14159;System.out.println("圆周率的值是: " + PI);}
}

在上述代码中,DAYS_IN_WEEK 和 PI 被 final 修饰,分别代表一周的天数和圆周率。当尝试修改这些常量的值时,编译器会报错,从而保证了常量值的不可变性。

  • 在java中,关键字final表示这个变量只能被赋值一次,一旦被赋值之后,就不能够再更改了。习惯上,常量名使用全大写。
  • 在Java中,经常希望某个常量可以在一个类中的多个方法中使用,通常将这些常量 称为类常量。可以使用关键字static final设置一个类常量。
 一个简单的数学常量示例:
public class MathConstants {// 定义圆周率常量,使用 public static final 修饰public static final double PI = 3.1415926;// 定义自然对数的底数常量public static final double E = 2.71828;public static void main(String[] args) {// 可以直接通过类名访问类常量System.out.println("圆周率的值是: " + MathConstants.PI);System.out.println("自然对数的底数的值是: " + MathConstants.E);// 计算半径为 5 的圆的面积double radius = 5;double area = MathConstants.PI * radius * radius;System.out.println("半径为 " + radius + " 的圆的面积是: " + area);}
}

 运算符

  • 在Java中,使用算术运算符+、-、*、/表示加、减、乘、除运算。
  • 当参与/运算的 两个操作数都是整数时,表示整数除法;否则,表示浮点除法。
  • 整数的求余操作 (有时称为取模)用%表示。
  • 例如,15/2等于7,15%2等于1,15.0/2等于7.5。
  • 需要注意,整数被0除将会产生一个异常,而浮点数被0除将会得到无穷大或NaN结果。

 算术运算符

用于执行基本的数学运算,包括加、减、乘、除、取模、自增和自减。

运算符描述示例
+加法,用于两个数相加int a = 5 + 3; // a 的值为 8
-减法,用于两个数相减int b = 7 - 2; // b 的值为 5
*乘法,用于两个数相乘int c = 4 * 6; // c 的值为 24
/除法,用于两个数相除。如果是整数相除,结果会舍去小数部分

int d = 10 / 3; // d 的值为 3

%取模(求余数),用于求两个数相除的余数int e = 10 % 3; // e 的值为 1
++自增运算符,分为前置自增(++i)和后置自增(i++)。前置自增先将变量的值加 1,再使用变量;后置自增先使用变量,再将变量的值加1

int i = 5; int j = ++i; // i 为 6,j 为 6 

int m = 5; int n = m++; // m 为 6,n 为 5

--自减运算符,分为前置自减(--i)和后置自减(i--)。原理与自增运算符类似

int x = 5; int y = --x; // x 为 4,y 为 4

int p = 5; int q = p--; // p 为 4,q 为 5

代码示例: 
//算术运算符
public class ArithmeticOperatorExample {public static void main(String[] args) {int a = 10;int b = 3;System.out.println("a + b = " + (a + b));System.out.println("a - b = " + (a - b));System.out.println("a * b = " + (a * b));System.out.println("a / b = " + (a / b));System.out.println("a % b = " + (a % b));int c = a++;System.out.println("a++ 后,a 的值是:" + a + ",c 的值是:" + c);c = ++a;System.out.println("++a 后,a 的值是:" + a + ",c 的值是:" + c);}
}
注:当然,程序员都知道加1、减1是数值变量最常见的操作。在Java中,借鉴了C和
C++的做法,也提供了自增、自减运算符:n++将变量n的当前值加1,n--则将n的值
减1。

赋值运算符

用于将一个值赋给变量,还可以结合算术运算符进行复合赋值。

运算符描述示例
=基本赋值运算符,将右边的值赋给左边的变量int x = 10;
+=加等于,先将变量与右边的值相加,再将结果赋给变量int a = 5; a += 3; // 相当于 a = a + 3,a 的值
-=减等于,先将变量与右边的值相减,再将结果赋给变量int b = 7; b -= 2; // 相当于 b = b - 2,b 的值为 5
*=乘等于,先将变量与右边的值相乘,再将结果赋给变量int c = 4; c *= 6; // 相当于 c = c * 6,c 的值为 24
/=除等于,先将变量与右边的值相除,再将结果赋给变量int d = 10; d /= 2; // 相当于 d = d / 2,d 的值为 5
%=取模等于,先将变量与右边的值取模,再将结果赋给变量int e = 10; e %= 3; // 等价于 e = e % 3,e 的值变为 1
 代码示例:
 // 赋值运算符int num3 = 5;num3 += 2;System.out.println("使用 += 后 num3 的值: " + num3);num3 -= 1;System.out.println("使用 -= 后 num3 的值: " + num3);num3 *= 3;System.out.println("使用 *= 后 num3 的值: " + num3);num3 /= 2;System.out.println("使用 /= 后 num3 的值: " + num3);num3 %= 3;System.out.println("使用 %= 后 num3 的值: " + num3);
  • 可以在赋值中使用二元运算符,这是一种很方便的简写形式。例如:
  • x +=4; 等价与x = x+4; 
  • (一般的,我们把运算符放在 = 号的左边,如 *=或%= )。

比较运算符

比较运算符用于比较两个值的大小关系,结果为布尔类型(true 或 false),以下是详细介绍及示例:

运算符描述示例
==等于,判断两个值是否相等

int x = 5;

int y = 5;

boolean isEqual = (x == y); // 结果为 t

!=不等于,判断两个值是否不相等

int m = 3;

int n = 4;

boolean isNotEqual = (m != n); // 结果为 true

>大于,判断左边的值是否大于右边的值

int a = 7;

int b = 5;

boolean isGreater = (a > b); // 结果为 true

<小于,判断左边的值是否小于右边的值

int c = 2;

int d = 4;

boolean isLess = (c < d); // 结果为 true

>=大于等于,判断左边的值是否大于或等于右边的值

int e = 5;

int f = 5;

boolean isGreaterOrEqual = (e >= f); // 结果为 true

<=小于等于,判断左边的值是否小于或等于右边的值

int g = 3;

int h = 4;

boolean isLessOrEqual = (g <= h); // 结果为 true

代码示例:
// 比较运算符boolean isEqual = num1 == num2;System.out.println("num1 是否等于 num2: " + isEqual);boolean isNotEqual = num1 != num2;System.out.println("num1 是否不等于 num2: " + isNotEqual);boolean isGreater = num1 > num2;System.out.println("num1 是否大于 num2: " + isGreater);boolean isLess = num1 < num2;System.out.println("num1 是否小于 num2: " + isLess);boolean isGreaterOrEqual = num1 >= num2;System.out.println("num1 是否大于等于 num2: " + isGreaterOrEqual);boolean isLessOrEqual = num1 <= num2;System.out.println("num1 是否小于等于 num2: " + isLessOrEqual);

逻辑运算符

逻辑运算符用于对布尔值进行逻辑操作,以下是常见的逻辑运算符及示例:

运算符描述示例
&&逻辑与,只有当两个操作数都为 true 时,结果才为 true

boolean a = true;

boolean b = false;

boolean result = a && b; // 结果为 false

||逻辑或,只要两个操作数中有一个为 true,结果就为 true

boolean m = true;

boolean n = false;

boolean res = m||n; // 结果为 true`

!逻辑非,对操作数的布尔值取反

boolean p = true;

boolean q =!p; // 结果为 false

代码示例: 

 // 逻辑运算符boolean bool1 = true;boolean bool2 = false;boolean logicalAnd = bool1 && bool2;System.out.println("逻辑与结果: " + logicalAnd);boolean logicalOr = bool1 || bool2;System.out.println("逻辑或结果: " + logicalOr);boolean logicalNot =!bool1;System.out.println("逻辑非结果: " + logicalNot);
  • Java沿用了C++的做法,使用&&表示逻辑“与”运算符,使用||表示逻辑“或”运算符。从!=运算符可以想到,感叹号!就是逻辑非运算符。
  • &&和||运算符是按照“短路”方式来求值的:如果第一个操作数已经能够确定表达式的值,第二个操作数就不必计算了。
  • 用&&运算符合并两个表达式,计算得到第一个表达式的真值为false,结果就不可能为true。
  • 因此,第二个表达式就不必计算了。可以利用这一点来避免错误。

位运算符

处理整型类型时,可以直接对组成整型数值的各个位完成操作。这意味着可以使用
掩码技术得到整数中的各个位。

位运算符用于对整数类型的二进制位进行操作,以下是常见的位运算符及示例:

运算符描述示例
&按位与,对两个操作数的对应二进制位进行与操作,只有当对应位都为 1 时,结果位才为 1int x = 5; // 二进制 0101
int y = 3; // 二进制 0011
int result = x & y; // 结果为 1,二进制 0001
|按位或,只要两个操作数中有一个为 true,结果就为 true`int a = 5; // 二进制 0101
int b = 3; // 二进制 0011
int orResult = a|b; // 结果为 7,二进制 0111`
<<左移运算符,将操作数的二进制位向左移动指定的位数,右边空出的位用 0 填充。左移 n 位相当于将操作数乘以 2 的 n 次方

int num = 5;

int result = num << 2;

>>有符号右移运算符,将操作数的二进制位向右移动指定的位数,左边空出的位用原符号位填充(正数补 0,负数补 1)。有符号右移 n 位相当于将操作数除以 2 的 n 次方并向下取整

int positive = 20;

int posResult = positive >> 2; 

int negative = -20;

int negResult = negative >> 2;

>>>无符号右移运算符,将操作数的二进制位向右移动指定的位数,左边空出的位始终用 0 填充,不考虑符号位

int negative = -20;

int result = negative >>> 2;

// -20 的二进制(补码)1111 1111 1111 1111 1111 1111 1110 1100 无符号右移 2 位后 0011 1111 1111 1111 1111 1111 1111 1011,结果为 1073741819

^按位异或,对两个操作数的对应二进制位进行异或操作,当两个对应位不同时结果位为 1,相同时结果位为 0

int m = 5;

// 二进制 0101

 int n = 3;

// 二进制 0011 

int xorResult = m ^ n;

// 结果为 6,二进制 0110

 代码示例:
// 5. 位运算符int bitNum1 = 5; // 二进制: 0101int bitNum2 = 3; // 二进制: 0011int bitwiseAnd = bitNum1 & bitNum2;System.out.println("按位与结果: " + bitwiseAnd);int bitwiseOr = bitNum1 | bitNum2;System.out.println("按位或结果: " + bitwiseOr);int bitwiseXor = bitNum1 ^ bitNum2;System.out.println("按位异或结果: " + bitwiseXor);int bitwiseNot = ~bitNum1;System.out.println("按位取反结果: " + bitwiseNot);int leftShift = bitNum1 << 2;System.out.println("左移 2 位结果: " + leftShift);int rightShift = bitNum1 >> 1;System.out.println("右移 1 位结果: " + rightShift);int unsignedRightShift = bitNum1 >>> 1;System.out.println("无符号右移 1 位结果: " + unsignedRightShift);

三目运算符

Java支持三元操作符?:,这个操作符有时很有用。如果条件为true,下面的表达式
布尔表达式 ? 表达式1 : 表达式2;
就为第一个表达式的值,否则计算为第二个表达式的值。
下面我们来看一段代码示例:
//在此表达式中,会返回x和y中较小的一个。
x<y?x:y

运算符优先级

运算符结合性
[]   ()(方法调用)从左向右
!  ~  ++  --  +(一元运算) -(一元运算) ()(强制类型转换)new从右向左
*      /   %从左向右
+  -从左向右
<<  >>  >>>从左向右
<  <=   >  >=  instanceof从左向右
==   !=从左向右
&从左向右
^从左向右
&&从左向右
|从左向右
? :从右向左
=  +=  -=  *=  /=  %=  &= = ^= <<= >>= >>>=从右向左

枚举类型

枚举类型是一种强类型,编译器会确保使用的是预先定义好的枚举常量,避免了错误的字符串或其他非法值的使用。

当变量的取值只在一个有限的集合内时,我们可以自定义枚举类型。枚举类型包括有限个命名的值。一般我们使用 enum关键字来定义枚举类型,例如:
enum Season {SPRING, SUMMER, AUTUMN, WINTER
}

上述代码定义了一个名为Season的枚举类型,包含了SPRING(春天)、SUMMER(夏天)、AUTUMN(秋天)、WINTER(冬天)四个枚举常量。

字符串

从概念上,Java 字符串是 Unicode 字符序列,如 “Java\u2122” 含 5 个 Unicode 字符 J、a、v、a 和™。Java 没有内置字符串类型,而是在标准类库中提供预定义的 String 类。每个双引号括起来的字符串,都是 String 类的实例。
public class StringExample {public static void main(String[] args) {// 定义一个普通字符串String normalString = "Hello, World!";System.out.println("普通字符串: " + normalString);// 定义一个包含Unicode字符的字符串String unicodeString = "Java\u2122";System.out.println("包含Unicode字符的字符串: " + unicodeString);// 字符串是String类的实例,可以调用String类的方法int length = normalString.length();System.out.println("普通字符串的长度: " + length);boolean isEqual = normalString.equals("Hello, World!");System.out.println("普通字符串与指定内容是否相等: " + isEqual);}
}

在上述代码中:

  1. 定义了一个普通字符串 normalString,它是 String 类的一个实例。通过 System.out.println 输出该字符串。
  2. 定义了包含 Unicode 字符(\u2122 代表商标符号™)的字符串 unicodeString,同样输出该字符串。
  3. 利用 String 类的 length 方法获取 normalString 的长度,并输出。
  4. 使用 String 类的 equals 方法比较 normalString 与指定字符串内容是否相等,并输出结果。
子串
  • String类的substring方法可以从一个较大的字符串提取出一个子串。
  • 在 Java 中,String 类的 substring 方法用于从一个较大的字符串中提取子串,它有两种重载形式,下面分别举例说明:

1. substring(int beginIndex)

该方法用于从指定的索引位置 beginIndex 开始截取字符串,一直到原字符串的末尾。

public class SubstringExample1 {public static void main(String[] args) {String originalString = "Hello, World!";// 从索引 7 开始截取字符串String subString = originalString.substring(7);System.out.println("原字符串: " + originalString);System.out.println("截取后的子串: " + subString);}
}

代码解释

  • originalString 是原始的字符串 "Hello, World!"
  • originalString.substring(7) 表示从索引为 7 的字符(索引从 0 开始计数)开始截取,即字符 W,一直到字符串末尾,所以截取后的子串是 "World!"

2. substring(int beginIndex, int endIndex)

该方法用于从指定的起始索引 beginIndex 开始截取字符串,直到指定的结束索引 endIndex 之前的位置(不包括 endIndex 位置的字符)。

public class SubstringExample2 {public static void main(String[] args) {String originalString = "Java Programming";// 从索引 0 开始,到索引 4 之前结束(不包括索引 4 的字符)String subString = originalString.substring(0, 4);System.out.println("原字符串: " + originalString);System.out.println("截取后的子串: " + subString);}
}

代码解释

  • originalString 是原始的字符串 "Java Programming"
  • originalString.substring(0, 4) 表示从索引为 0 的字符(即字符 J)开始截取,到索引为 4 的字符(即字符 P)之前结束,所以截取后的子串是 "Java"

注:

  • 索引值必须在有效范围内,即 0 到 字符串长度 - 1 之间,否则会抛出StringIndexOutofBoundsException异常。
  • 当 beginIndex 等于 endIndex 时,截取的子串为空字符串。
  • substring的工作方式有一个优点:容易计算子串的长度。
  • 字符串 s.substring(a,b)的长度为b-a。

拼接

  • 与绝大多数的程序设计语言一样,Java语言允许使用+号连接(拼接)两个字符串。
  • 这是最直观、最简单的字符串拼接方式,适用于少量字符串的拼接。
public class StringConcatenationExample {public static void main(String[] args) {String str1 = "Hello";String str2 = "World";String result = str1 + " " + str2;System.out.println(result);}
}

 代码解释:

  • + 是 Java 中的字符串拼接运算符。当 + 用于连接两个字符串时,会将它们连接成一个新的字符串。
  • 这里先将 str1 的值 "Hello" 与空格字符串 " " 拼接,得到 "Hello ",然后再将这个结果与 str2 的值 "World" 拼接,最终得到 "Hello World"
  • result 是一个新的字符串变量,用于存储拼接后的结果。

原理
在编译时,Java 编译器会将使用 + 运算符拼接字符串的代码转化为 StringBuilder 类的操作。对于简单的拼接,这种方式很方便,但如果在循环中使用,会导致性能问题,因为每次拼接都会创建新的 StringBuilder 对象。

 不可变字符串

  • String类没有提供用于修改字符串的方法。在 Java 中,String类代表不可变字符串,即字符串一旦创建,其内容就不能被改变。
  • 由于不能修改Java字符串中的字符,所以在Java文档中将String类对象称为不可变字符串。
  • 不可变字符串有一个优点:编译器可以让字符串共享。
  • 具体的工作方式,可以想象将各种字符串存放在公共的存储池中。字符串变量指向存储池中相应的位置。如果复制一个字符串变量,原始字符串与复制的字符串共享相同的字符。
  • 字符串常量池:Java 为了提高性能和减少内存开销,维护了一个字符串常量池。当创建字符串常量时,如String s = "abc";,JVM 首先会在字符串常量池中查找是否存在"abc"这个字符串。如果存在,直接返回常量池中该字符串的引用;如果不存在,则在常量池中创建该字符串,然后返回引用。这使得相同内容的字符串常量在内存中只有一份实例,进一步体现了String的不可变性。
代码示例: 
public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {private final char value[];
}
  •  String类内部使用一个private final char[]数组来存储字符串内容,final关键字确保了该数组一旦初始化,就不能再指向其他数组,从底层实现上保证了字符串的不可变性。
  • 由于value数组是private的,外部代码无法直接访问和修改该数组,同时final修饰符保证了数组引用的不可更改性。 
总而言之,Java的设计者认为共享带来的高效率远远胜过于提取、拼接字符串所带来的低效率。查看一下程序会发现:很少需要修改字符串,而是往往需要对字符串进行比较。有一种例外情况,将来自于文件或键盘的单个字符或较短的字符串汇集成字符串。为此,Java提供了一个独立的类,这在之后会介绍。

检测字符串是否相等

在Java中,我们可以使用equals方法检测两个字符串是否相等。

1. 使用 equals 方法

equals方法用于比较两个字符串的内容是否相等。它是String类重写自Object类的方法,用于专门比较字符串的实际字符序列。

String str1 = "Hello";
String str2 = "Hello";
String str3 = new String("Hello");boolean isEqual1 = str1.equals(str2); 
boolean isEqual2 = str1.equals(str3); System.out.println(isEqual1); 
System.out.println(isEqual2); 

在上述代码中,str1str2都指向字符串常量池中的同一个"Hello"字符串,str3通过new关键字创建了一个新的String对象,但内容同样是"Hello"equals方法比较的是字符串的内容,所以isEqual1isEqual2都为true

当然,如果我们要想检测两个字符串是否相等,而不区分大小写,可以使用equalsIgnoreCase
法。

2. 使用 equalsIgnoreCase 方法

equalsIgnoreCase方法与equals方法类似,也是用于比较字符串内容,但它忽略大小写。

我们要想检测两个字符串是否相等,而不区分大小写,就可以使用equalsIgnoreCase方法。

 注:请注意!请一定不要用`==`检测字符串是否相等,它只判断内存地址是否相同,即便内容一样,不同位置的字符串拷贝也会使判断出错。

只有字符串常量在虚拟机中是共享的,+substring等操作生成的字符串不共享。所以千万不要使用==判断字符串相等,否则极易出现类似随机间歇性错误的严重 bug。

public class StringEqualityIgnoreCase {public static void main(String[] args) {String str1 = "Hello";String str2 = "hello";// 比较 str1 和 str2,忽略大小写boolean result = str1.equalsIgnoreCase(str2);System.out.println("str1.equalsIgnoreCase(str2) 的结果: " + result); }
}

代码解释:在比较 str1 和 str2 时,equalsIgnoreCase 方法不考虑字母的大小写,因此即使 str1 中的 H 是大写,str2 中的 h 是小写,该方法仍会返回 true

 空串与Null串

在 Java 中,空串(Empty String)和 Null 串是两个不同的概念。

空串

空串是长度为 0 的字符串,它是 String 类的一个有效实例,在内存中是实际存在的对象,只不过该对象不包含任何字符。可以通过两种方式创建空串:

// 方式一:直接使用双引号赋值
String emptyStr1 = "";
// 方式二:使用 String 类的构造函数
String emptyStr2 = new String();
判断方式

判断一个字符串是否为空串,通常可以使用 length() 方法或者 isEmpty() 方法:

String str = "";
// 使用 length() 方法判断
if (str.length() == 0) {System.out.println("字符串为空串");
}
// 使用 isEmpty() 方法判断
if (str.isEmpty()) {System.out.println("字符串为空串");
}
使用场景
  • 初始化变量:在某些情况下,需要将字符串变量初始化为空串,以便后续根据业务逻辑进行赋值操作。
String result = "";
// 根据条件进行赋值
if (condition) {result = "Some value";
}
  • 作为方法返回值:当方法没有合适的字符串结果需要返回时,可以返回空串。
public String getResult() {// 某些条件下没有结果return "";
}

Null 串

Null 串表示一个字符串变量没有引用任何对象,即该变量不指向任何有效的 String 实例。null 是 String变量中存放的一个特殊值,它表示目前没有任何对象与该变量关联

String nullStr = null;
判断方式

判断一个字符串是否为 null,需要使用 == 运算符:

String str = null;
if (str == null) {System.out.println("字符串为 null");
}

 需要注意的是,在使用 null 字符串调用方法时会抛出 NullPointerException 异常,例如:

String nullStr = null;
// 下面这行代码会抛出 NullPointerException 异常
// System.out.println(nullStr.length()); 
使用场景
  • 表示对象未初始化:当一个字符串变量在声明时还不确定具体的值,或者在某些条件下没有合适的字符串对象可以引用时,可以将其初始化为 null
String userInput;
// 假设在某个方法中获取用户输入
userInput = getUserInput(); 
if (userInput == null) {System.out.println("未获取到用户输入");
}
  • 方法参数传递:在方法调用时,如果某个字符串参数没有合适的值可以传递,可以传递 null 表示该参数未提供。
public void processString(String str) {if (str == null) {System.out.println("传入的字符串为 null");} else {// 处理字符串}
}

注意事项

在实际开发中,经常需要同时判断字符串是否为 null 或空串,可以结合 null 判断和空串判断:

String str = null;
if (str == null || str.isEmpty()) {System.out.println("字符串为 null 或空串");
}

需要注意判断顺序,先判断 null 再判断空串,避免对 null 字符串调用 isEmpty() 方法导致 NullPointerException 异常。

 码点与代码单元

  • Java字符串由char值序列组成。
  • char数据类型是一个采用UTF-16编码表示Unicode码点的代码单元。大多数的常用Unicode字符使用一个代码单元就可以表示,而辅助字符需要一对代码单元表示。

Unicode 简介

Unicode 是一种字符编码标准,它为世界上几乎所有的字符都分配了一个唯一的数字,这个数字被称为码点。Unicode 码点的范围从 U+0000 到 U+10FFFF,可以表示超过一百万个字符。

码点(Code Point)

  • 定义:码点是指 Unicode 编码中的一个数值,它唯一标识一个字符。例如,字符 'A' 的 Unicode 码点是 U+0041,字符 '中' 的 Unicode 码点是 U+4E2D。在 Java 中,码点用 int 类型表示,因为它的取值范围超出了 char 类型所能表示的范围。
  • 获取码点:可以使用 String 类的 codePointAt(int index) 方法来获取字符串中指定位置的码点。示例代码如下:
public class CodePointExample {public static void main(String[] args) {String str = "A中";// 获取第一个字符的码点int codePoint1 = str.codePointAt(0); // 获取第二个字符的码点int codePoint2 = str.codePointAt(1); System.out.println("字符 'A' 的码点: " + Integer.toHexString(codePoint1)); System.out.println("字符 '中' 的码点: " + Integer.toHexString(codePoint2)); }
}

遍历码点:可以使用 String 类的 codePoints() 方法来遍历字符串中的所有码点。

示例代码如下:

public class CodePointTraversal {public static void main(String[] args) {String str = "A中";str.codePoints().forEach(codePoint -> {System.out.println("码点: " + Integer.toHexString(codePoint));});}
}

代码单元(Code Unit)

  • 定义:代码单元是字符串存储和处理的基本单位。在 Java 中,String 类内部使用 UTF - 16 编码来存储字符,UTF - 16 编码使用 16 位(即 2 个字节)作为一个代码单元。对于 Unicode 码点在 U+0000 到 U+FFFF 范围内的字符(称为 BMP,基本多文种平面字符),可以用一个代码单元来表示;而对于码点在 U+10000 到 U+10FFFF 范围内的字符(称为补充字符),则需要用两个代码单元(即一个代理对)来表示。
  • 获取代码单元:可以使用 charAt(int index) 方法来获取字符串中指定位置的代码单元。示例代码如下:
public class CodeUnitExample {public static void main(String[] args) {String str = "A中";// 获取第一个代码单元char codeUnit1 = str.charAt(0); // 获取第二个代码单元char codeUnit2 = str.charAt(1); System.out.println("第一个代码单元: " + codeUnit1); System.out.println("第二个代码单元: " + codeUnit2); }
}
  • 代码单元数量:可以使用 length() 方法来获取字符串中的代码单元数量。需要注意的是,对于包含补充字符的字符串,length() 方法返回的是代码单元的数量,而不是字符的实际数量。示例代码如下:
public class CodeUnitLength {public static void main(String[] args) {// 包含补充字符的字符串String str = "\uD83D\uDE00"; System.out.println("代码单元数量: " + str.length()); System.out.println("实际字符数量: " + str.codePoints().count()); }
}

码点与代码单元的关系

  • 对于 BMP 字符,一个码点对应一个代码单元。
  • 对于补充字符,一个码点对应两个代码单元。在处理包含补充字符的字符串时,需要特别注意,不能简单地使用 charAt() 和 length() 方法,而应该使用与码点相关的方法,如 codePointAt() 和 codePoints() 方法。

 输入输出

在 Java 中,输入输出(I/O)操作是非常重要的部分,它允许程序与外部环境进行数据交互,比如从键盘读取用户输入、将数据写入文件、从网络接收数据等。Java 的 I/O 操作主要分为标准输入输出、文件输入输出和网络输入输出。

格式化输出

可以使用 System.out.print(x) 将数值x输出到控制台上。这条命令将以x对应的数据类型所允许的最大非0数字位数打印输出x。
输出整数
public class IntegerOutputExample {public static void main(String[] args) {int number = 12345;// 使用 System.out.print 输出整数System.out.print("整数输出结果: ");System.out.print(number);}
}

代码解释

  • 首先定义了一个 int 类型的变量 number 并赋值为 12345
  • 第一行 System.out.print 输出提示信息 "整数输出结果: "
  • 第二行 System.out.print 输出变量 number 的值。

沿用C库函数 

  •  Java SE 沿用了 C 语言库函数中的 printf 方法风格。
  • 在 C 语言里,printf 函数是一个非常常用的输出函数,可按指定格式输出各种类型的数据。
  • 而 Java 在 java.io.PrintStream 类中也提供了 printf 方法,其功能和使用方式与 C 语言的 printf 函数有相似之处,都是支持格式化输出。
  • 代码示例:
public class PrintfExample {public static void main(String[] args) {int num = 10;double pi = 3.14159;String name = "John";// 输出整数System.out.printf("整数: %d\n", num);// 输出浮点数,保留两位小数System.out.printf("浮点数: %.2f\n", pi);// 输出字符串System.out.printf("字符串: %s\n", name);// 组合输出System.out.printf("%s 的幸运数字是 %d,他喜欢的圆周率近似值是 %.2f。\n", name, num, pi);}
}

与 C 语言 printf 的区别

注意:虽然 Java 的 printf 方法借鉴了 C 语言 printf 函数的格式化输出方式,但 Java 是面向对象的语言,printf 是 PrintStream 类的一个实例方法,并且 Java 有自己严格的类型检查机制,这和 C 语言有所不同。例如在 C 语言里,可能需要更手动地管理类型转换和内存,而 Java 会在编译阶段进行类型检查,保证代码的类型安全性。

 用于printf的转换符

转换符类型举例
d十进制整数159
x十六进制整数9f
o八进制整数237
f定点浮点数15.9
e指数浮点数1.59e+01
g通用浮点数
a十六进制浮点数0x1.fccdp3
s字符串Hello
c字符H
b布尔True
h散列码42628b2
tx 或 Tx(T 强制大写)日期时间(已经过时,应当改为使用 java.time 类)-
%百分号%
n与平台有关的行分隔符

代码示例:

public class FormatConversionExample {public static void main(String[] args) {// 整数相关转换符int intValue = 159;System.out.printf("十进制整数(d): %d\n", intValue);System.out.printf("十六进制整数(x): %x\n", intValue);System.out.printf("八进制整数(o): %o\n", intValue);// 浮点数相关转换符double doubleValue = 15.9;System.out.printf("定点浮点数(f): %.1f\n", doubleValue);System.out.printf("指数浮点数(e): %e\n", doubleValue);System.out.printf("通用浮点数(g): %g\n", doubleValue);System.out.printf("十六进制浮点数(a): %a\n", doubleValue);// 字符串、字符、布尔、散列码相关转换符String strValue = "Hello";char charValue = 'H';boolean boolValue = true;Object obj = new Object();System.out.printf("字符串(s): %s\n", strValue);System.out.printf("字符(c): %c\n", charValue);System.out.printf("布尔(b): %b\n", boolValue);System.out.printf("散列码(h): %h\n", obj);// 百分号和行分隔符转换符System.out.printf("百分号(%%): %% \n");System.out.printf("与平台有关的行分隔符(n): 第一行%n第二行");}
}

运行结果:

十进制整数(d): 159
十六进制整数(x): 9f
八进制整数(o): 237
定点浮点数(f): 15.9
指数浮点数(e): 1.590000e+01
通用浮点数(g): 15.9
十六进制浮点数(a): 0x1.fccdp3
字符串(s): Hello
字符(c): H
布尔(b): true
散列码(h): 7852e922
百分号(%): % 
与平台有关的行分隔符(n): 第一行
第二行

用于printf的转换符

public class PrintfFlagsExample {public static void main(String[] args) {double number = 3333.33;int intNumber = 159;// + 标志:打印正数和负数的符号System.out.printf("+ 标志示例:%+f\n", number);// 空格 标志:在正数之前添加空格System.out.printf("空格 标志示例:% f\n", number);// 0 标志:数字前面补0System.out.printf("0 标志示例:%010.2f\n", number);// - 标志:左对齐System.out.printf("- 标志示例:%-10.2f\n", number);// ( 标志:将负数括在括号内double negativeNumber = -3333.33;System.out.printf("( 标志示例:%(f\n", negativeNumber);//, 标志:添加分组分隔符System.out.printf(", 标志示例:%,f\n", number);// #(对于f格式)标志:包含小数点System.out.printf("#(对于f格式)标志示例:%,#.0f\n", number);// #(对于x或o格式)标志:添加前缀0x或0System.out.printf("#(对于x格式)标志示例:%#x\n", intNumber);// $ 标志:指定要格式化的参数索引System.out.printf("$ 标志示例:%1$d %1$x\n", intNumber);// < 标志:格式化前面说明的数值System.out.printf("< 标志示例:%d%<x\n", intNumber);}
}

运行结果

+ 标志示例:+3333.330000
空格 标志示例: 3333.330000
0 标志示例:003333.33
- 标志示例:3333.33  
( 标志示例:(3333.33)
, 标志示例:3,333.330000
#(对于f格式)标志示例:3,333.
#(对于x格式)标志示例:0x9f
$ 标志示例:159 9f
< 标志示例:159 9f
标志目的示例代码中的效果
+打印正数和负数的符号输出+3333.330000
空格在正数之前添加空格输出 3333.330000
0数字前面补 0输出003333.33
-左对齐输出3333.33
(将负数括在括号内输出(3333.33)
,添加分组分隔符输出3,333.330000
#(对于 f 格式)包含小数点输出3,333.
#(对于 x 或 o 格式)添加前缀 0x 或 0输出0x9f
$指定要格式化的参数索引输出159 9f
<格式化前面说明的数值输出159 9f

日期和时间转换符 

import java.util.Calendar;public class DateTimeConversionExample {public static void main(String[] args) {Calendar calendar = Calendar.getInstance();System.out.println("完整的日期和时间 (c): " + String.format("%tc", calendar));System.out.println("ISO 8601日期 (F): " + String.format("%tF", calendar));System.out.println("美国格式的日期 (月/日/年) (D): " + String.format("%tD", calendar));System.out.println("24小时时间 (T): " + String.format("%tT", calendar));System.out.println("12小时时间 (r): " + String.format("%tr", calendar));System.out.println("24小时时间,没有秒 (R): " + String.format("%tR", calendar));System.out.println("4位数字的年(前面补0) (Y): " + String.format("%tY", calendar));System.out.println("年的后两位数字(前面补0) (y): " + String.format("%ty", calendar));System.out.println("年的前两位数字(前面补0) (C): " + String.format("%tC", calendar));System.out.println("月的完整拼写 (B): " + String.format("%tB", calendar));System.out.println("月的缩写 (b或h): " + String.format("%tb", calendar));System.out.println("两位数字的月(前面补0) (m): " + String.format("%tm", calendar));System.out.println("两位数字的日(前面补0) (d): " + String.format("%td", calendar));System.out.println("两位数字的日(前面不补0) (e): " + String.format("%te", calendar));System.out.println("星期几的完整拼写 (A): " + String.format("%tA", calendar));System.out.println("星期几的缩写 (a): " + String.format("%ta", calendar));System.out.println("三位数的年中第几天(前面补0),在001到366之间 (j): " + String.format("%tj", calendar));System.out.println("两位数字的小时(前面补0),在0到23之间 (H): " + String.format("%tH", calendar));System.out.println("两位数字的小时(前面不补0),在0到23之间 (k): " + String.format("%tk", calendar));System.out.println("两位数字的小时(前面补0),在01到12之间 (I): " + String.format("%tI", calendar));System.out.println("两位数字的小时(前面不补0),在1到12之间 (l): " + String.format("%tl", calendar));System.out.println("两位数字的分钟(前面补0) (M): " + String.format("%tM", calendar));System.out.println("两位数字的秒(前面补0) (S): " + String.format("%tS", calendar));System.out.println("三位数字的毫秒(前面补0) (L): " + String.format("%tL", calendar));System.out.println("九位数字的毫秒(前面补0) (N): " + String.format("%tN", calendar));System.out.println("上午或下午的标志 (p): " + String.format("%tp", calendar));System.out.println("从GMT起,RFC 822数字位移 (z): " + String.format("%tz", calendar));System.out.println("时区 (Z): " + String.format("%tZ", calendar));System.out.println("从格林尼治时间1970-01-01 00:00:00起的秒数 (s): " + String.format("%ts", calendar));System.out.println("从格林尼治时间1970-01-01 00:00:00起的毫秒数 (Q): " + String.format("%tQ", calendar));}
}

转换符

类型

示例

c

完整的日期和时间

Sun Jan 26 14:53:00 CST 2025

F

ISO 8601 日期

2025-01-26

D

美国格式的日期 (月 / 日 / 年)

01/26/25

T

24 小时时间

14:53:00

r

12 小时时间

02:53:00 pm

R

24 小时时间,没有秒

14:53

Y

4 位数字的年(前面补 0)

2025

y

年的后两位数字(前面补 0)

25

C

年的前两位数字(前面补 0)

20

B

月的完整拼写

January

b 或 h

月的缩写

Jan

m

两位数字的月(前面补 0)

01

d

两位数字的日(前面补 0)

26

e

两位数字的日(前面不补 0)

26

A

星期几的完整拼写

Sunday

a

星期几的缩写

Sun

j

三位数的年中第几天(前面补 0),在 001 到 366 之间

026

H

两位数字的小时(前面补 0),在 0 到 23 之间

14

k

两位数字的小时(前面不补 0),在 0 到 23 之间

14

I

两位数字的小时(前面补 0),在 01 到 12 之间

02

l

两位数字的小时(前面不补 0),在 1 到 12 之间

2

M

两位数字的分钟(前面补 0)

53

S

两位数字的秒(前面补 0)

00

L

三位数字的毫秒(前面补 0)

123

N

九位数字的毫秒(前面补 0)

123000000

p

上午或下午的标志

pm

z

从 GMT 起,RFC 822 数字位移

+0800

Z

时区

CST

s

从格林尼治时间 1970-01-01 00:00:00 起的秒数

1738009980

Q

从格林尼治时间 1970-01-01 00:00:00 起的毫秒数

1738009980123

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/8324.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【单链表算法实战】解锁数据结构核心谜题——环形链表

题目如下&#xff1a; 解题过程如下&#xff1a; 环形链表&#xff1a;尾结点的next指针不为空&#xff0c;而是指向链表中的任一结点。 思路&#xff1a;快慢指针&#xff0c;慢指针每次走一步&#xff0c;快指针每次走两步。快慢指针在环中追逐相遇&#xff0c;那么这个链表…

56. 合并区间

【题目】&#xff1a;56. 合并区间 class Solution { public:vector<vector<int>> merge(vector<vector<int>>& intervals) {// 按照左端点排序sort(intervals.begin(), intervals.end(), [&](vector<int> lhs, vector<int> rhs)…

01-硬件入门学习/嵌入式教程-CH340C使用教程

前言 CH340C广泛应用于DIY项目和嵌入式开发中&#xff0c;用于USB数据转换和串口通信。本文将详细介绍CH340C的基本功能、引脚接线及使用方法。 CH340C简介 CH340C是一款USB转TTL电平转换器&#xff0c;可以将电脑的USB数据转换成串口数据&#xff0c;方便与单片机&#xff…

深度学习|表示学习|卷积神经网络|详细推导每一层的维度变化|14

如是我闻&#xff1a; 一个经典的卷积神经网络&#xff08;CNN&#xff09;架构&#xff0c;呈现的是输入图像通过多个卷积层、池化层以及全连接层&#xff0c;最终输出分类结果的过程。整个过程的核心是理解输入特征图的尺寸如何在每一层发生变化&#xff0c;我们可以通过卷积…

5.1.4 软件工具+开发环境

文章目录 软件工具软件开发环境 软件工具 软件工具是辅助软件工程实施的软件&#xff0c;也叫CASE工具。软件工具可分为支持软件开发过程的工具、软件维护工具、软件管理工具3类。 支持软件开发过程的工具 需求分析工具&#xff1a;从需求定义制定出功能规范&#xff0c;描述软…

ospf动态路由配置,cost路径调整,ospf认证实验

一、实验拓扑如图&#xff1a; 接口ip配置网络 &#xff1a;10.17.12.* 10.17.13.* &#xff0c;10.17.23.* 回环接口配置分别为 10.0.1.1 &#xff0c;10.0.1.2&#xff0c;10.0.1.3对应三台路由器 ar1配置接口ip interface GigabitEthernet0/0/0 ip address 10.17.12.1…

通过亚马逊云科技Bedrock打造自定义AI智能体Agent(上)

大家对于智能体代理Agent一定已经非常熟悉&#xff0c;自主代理&#xff08;Autonomous Agents&#xff09; 目前在AI行业极其热门并具有巨大的潜力&#xff0c;能够显著提升开发者日常的工作效率、自动化日常琐碎、重复性任务&#xff0c;并生成全新的内容。Agent可以理解用户…

Sklearn 中的逻辑回归

逻辑回归的数学模型 基本模型 逻辑回归主要用于处理二分类问题。二分类问题对于模型的输出包含 0 和 1&#xff0c;是一个不连续的值。分类问题的结果一般不能由线性函数求出。这里就需要一个特别的函数来求解&#xff0c;这里引入一个新的函数 Sigmoid 函数&#xff0c;也成…

基于STM32的循迹小车设计与实现

1 系统方案设计 根据系统设计功能&#xff0c;展开基于STM32的循迹小车设计&#xff0c;整体设计框图如图2.1所示。系统采用STM32单片机作为控制器,通过L298驱动器控制两个直流电机实现对小车的运动控制&#xff0c;两路红外模块实现黑线的检测&#xff0c;HC-SR04超声波模块实…

异或哈希总结

例题 例题1https://codeforces.com/problemset/problem/1175/Fhttps://codeforces.com/problemset/problem/1175/F 例题2https://codeforces.com/contest/2014/problem/Hhttps://codeforces.com/contest/2014/problem/H例题4https://codeforces.com/contest/1418/problem/Ght…

深入理解若依RuoYi-Vue数据字典设计与实现

深入理解若依数据字典设计与实现 一、Vue2版本主要文件目录 组件目录src/components&#xff1a;数据字典组件、字典标签组件 工具目录src/utils&#xff1a;字典工具类 store目录src/store&#xff1a;字典数据 main.js&#xff1a;字典数据初始化 页面使用字典例子&#xf…

Leecode刷题C语言之跳跃游戏②

执行结果:通过 执行用时和内存消耗如下&#xff1a; int jump(int* nums, int numsSize) {int position numsSize - 1;int steps 0;while (position > 0) {for (int i 0; i < position; i) {if (i nums[i] > position) {position i;steps;break;}}}return steps…

【C++数论】880. 索引处的解码字符串|2010

本文涉及知识点 数论&#xff1a;质数、最大公约数、菲蜀定理 LeetCode880. 索引处的解码字符串 给定一个编码字符串 s 。请你找出 解码字符串 并将其写入磁带。解码时&#xff0c;从编码字符串中 每次读取一个字符 &#xff0c;并采取以下步骤&#xff1a; 如果所读的字符是…

1月27(信息差)

&#x1f30d;喜大普奔&#xff0c;适用于 VS Code 的 GitHub Copilot 全新免费版本正式推出&#xff0c;GitHub 全球开发者突破1.5亿 &#x1f384;Kimi深夜炸场&#xff1a;满血版多模态o1级推理模型&#xff01;OpenAI外全球首次&#xff01;Jim Fan&#xff1a;同天两款国…

18款炫酷烟花合集

系列专栏 《Python趣味编程》《C/C趣味编程》《HTML趣味编程》《Java趣味编程》 写在前面 Python、C/C、HTML、Java等4种语言实现18款炫酷烟花的代码。 Python Python烟花① 完整代码&#xff1a;Python动漫烟花&#xff08;完整代码&#xff09;-CSDN博客 ​ Python烟…

【C++ 动态规划】1024. 视频拼接|1746

本文涉及知识点 C动态规划 LeetCode1024. 视频拼接 你将会获得一系列视频片段&#xff0c;这些片段来自于一项持续时长为 time 秒的体育赛事。这些片段可能有所重叠&#xff0c;也可能长度不一。 使用数组 clips 描述所有的视频片段&#xff0c;其中 clips[i] [starti, end…

可扩展架构:如何打造一个善变的柔性系统?

系统的构成:模块 + 关系 我们天天和系统打交道,但你有没想过系统到底是什么?在我看来,系统内部是有明确结构 的,它可以简化表达为: 系统 = 模块 + 关系 在这里,模块是系统的基本组成部分,它泛指子系统、应用、服务或功能模块。关系指模块 之间的依赖关系,简单…

TOGAF之架构标准规范-信息系统架构 | 数据架构

TOGAF是工业级的企业架构标准规范&#xff0c;信息系统架构阶段是由数据架构阶段以及应用架构阶段构成&#xff0c;本文主要描述信息系统架构阶段中的数据架构阶段。 如上所示&#xff0c;信息系统架构&#xff08;Information Systems Architectures&#xff09;在TOGAF标准规…

【OMCI实践】ONT上线过程的omci消息(二)

引言 在上一篇文章【OMCI实践】ONT上线过程的omci消息&#xff08;一&#xff09;-CSDN博客&#xff0c;主要介绍了ONT上线过程的OMCI交互的四个阶段&#xff0c;本篇开始介绍上线过程的omci消息&#xff0c;重点介绍涉及到的受管实体&#xff08;ME&#xff09;的属性。 OMC…

C++ STL:深入探索常见容器

你好呀&#xff0c;欢迎来到 Dong雨 的技术小栈 &#x1f331; 在这里&#xff0c;我们一同探索代码的奥秘&#xff0c;感受技术的魅力 ✨。 &#x1f449; 我的小世界&#xff1a;Dong雨 &#x1f4cc; 分享我的学习旅程 &#x1f6e0;️ 提供贴心的实用工具 &#x1f4a1; 记…