第二章 数据与表达式
第一节 基本语法元素
Java 标识符
Java 所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符。
关于 Java 标识符,有以下几点需要注意:
- 所有的标识符都应该以字母(A-Z 或者 a-z),美元符($)、或者下划线(_)开始
- 首字符之后可以是字母(A-Z 或者 a-z),美元符($)、下划线(_)或数字的任何字符组合
- 关键字不能用作标识符
- 标识符是大小写敏感的
- 合法标识符举例:age、$salary、_value、__1_value
- 非法标识符举例:123abc、-salary
Java 修饰符
像其他语言一样,Java可以使用修饰符来修饰类中方法和属性。主要有两类修饰符:
- 访问控制修饰符 : default, public , protected, private
- 非访问控制修饰符 : final, abstract, static, synchronized
在后面的章节中我们会深入讨论 Java 修饰符。
Java 变量
Java 中主要有如下几种类型的变量
- 局部变量
- 类变量(静态变量)
- 成员变量(非静态变量)
Java 关键字
下面列出了 Java 关键字。这些保留字不能用于常量、变量、和任何标识符的名称。
Java 注释
类似于 C/C++、Java 也支持单行以及多行注释。
Java 注释主要有三种类型:
- 单行注释
- 多行注释
- 文档注释
注释中的字符将被 Java 编译器忽略。
public class HelloWorld {
/**
* 这是一个文档注释示例
* 它通常包含有关类、方法或字段的详细信息
* 这是一个多行注释的示例
*/
/*
这也是一个多行
注释
*/
public static void main(String[] args){
// 这是单行注释的示例
/* 这个也是单行注释的示例 */
System.out.println("Hello World");
}
}
文档注释以 /**
开始,以 */
结束,通常出现在类、方法、字段等的声明前面,用于生成代码文档,这种注释可以被工具提取并生成 API 文档,如 JavaDoc。
文档注释的格式通常包含一些特定的标签,如 @param 用于描述方法参数,@return 用于描述返回值,@throws 用于描述可能抛出的异常等等,这些标签有助于生成清晰的API文档,以便其他开发者能够更好地理解和使用你的代码。
Java 空行
空白行或者有注释的行,Java 编译器都会忽略掉。
第二节 基本数据类型
变量就是申请内存来存储值。也就是说,当创建变量的时候,需要在内存中申请空间。
内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据。
因此,通过定义不同类型的变量,可以在内存中储存整数、小数或者字符。
Java 的两大数据类型:
- 内置数据类型
- 引用数据类型
内置数据类型
Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。
byte:
- byte 数据类型是8位、有符号的,以二进制补码表示的整数;
- 最小值是 -128(-2^7);
- 最大值是 127(2^7-1);
- 默认值是 0;
- byte 类型用在大型数组中节约空间,主要代替整数,因为 byte 变量占用的空间只有 int 类型的四分之一;
- 例子:byte a = 100,byte b = -50。
short:
- short 数据类型是 16 位、有符号的以二进制补码表示的整数
- 最小值是 -32768(-2^15);
- 最大值是 32767(2^15 - 1);
- Short 数据类型也可以像 byte 那样节省空间。一个short变量是int型变量所占空间的二分之一;
- 默认值是 0;
- 例子:short s = 1000,short r = -20000。
int:
- int 数据类型是32位、有符号的以二进制补码表示的整数;
- 最小值是 -2,147,483,648(-2^31);
- 最大值是 2,147,483,647(2^31 - 1);
- 一般地整型变量默认为 int 类型;
- 默认值是 0 ;
- 例子:int a = 100000, int b = -200000。
long:
- long 数据类型是 64 位、有符号的以二进制补码表示的整数;
- 最小值是 -9,223,372,036,854,775,808(-2^63);
- 最大值是 9,223,372,036,854,775,807(2^63 -1);
- 这种类型主要使用在需要比较大整数的系统上;
- 默认值是 0L;
- 例子: long a = 100000L,long b = -200000L。 "L"理论上不分大小写,但是若写成"l"容易与数字"1"混淆,不容易分辩。所以最好大写。
float:
- float 数据类型是单精度、32位、符合IEEE 754标准的浮点数;
- float 在储存大型浮点数组的时候可节省内存空间;
- 默认值是 0.0f;
- 浮点数不能用来表示精确的值,如货币;
- 例子:float f1 = 234.5f。
double:
- double 数据类型是双精度、64 位、符合 IEEE 754 标准的浮点数;
- 浮点数的默认类型为 double 类型;
- double类型同样不能表示精确的值,如货币;
- 默认值是 0.0d;
- 例子:
double d1 = 7D ;
double d2 = 7.;
double d3 = 8.0;
double d4 = 8.D;
double d5 = 12.9867;
7 是一个 int 字面量,而 7D,7. 和 8.0 是 double 字面量。
boolean:
- boolean数据类型表示一位的信息;
- 只有两个取值:true 和 false;
- 这种类型只作为一种标志来记录 true/false 情况;
- 默认值是 false;
- 例子:boolean one = true。
char:
- char 类型是一个单一的 16 位 Unicode 字符;
- 最小值是 \u0000(十进制等效值为 0);
- 最大值是 \uffff(即为 65535);
- char 数据类型可以储存任何字符;
- 例子:char letter = 'A';。
实例
对于数值类型的基本类型的取值范围,我们无需强制去记忆,因为它们的值都已经以常量的形式定义在对应的包装类中了。
请看下面的例子:
public class PrimitiveTypeTest {
public static void main(String[] args) {
// byte
System.out.println("基本类型:byte 二进制位数:" + Byte.SIZE);
System.out.println("包装类:java.lang.Byte");
System.out.println("最小值:Byte.MIN_VALUE=" + Byte.MIN_VALUE);
System.out.println("最大值:Byte.MAX_VALUE=" + Byte.MAX_VALUE);
System.out.println();
// short
System.out.println("基本类型:short 二进制位数:" + Short.SIZE);
System.out.println("包装类:java.lang.Short");
System.out.println("最小值:Short.MIN_VALUE=" + Short.MIN_VALUE);
System.out.println("最大值:Short.MAX_VALUE=" + Short.MAX_VALUE);
System.out.println();
// int
System.out.println("基本类型:int 二进制位数:" + Integer.SIZE);
System.out.println("包装类:java.lang.Integer");
System.out.println("最小值:Integer.MIN_VALUE=" + Integer.MIN_VALUE);
System.out.println("最大值:Integer.MAX_VALUE=" + Integer.MAX_VALUE);
System.out.println();
// long
System.out.println("基本类型:long 二进制位数:" + Long.SIZE);
System.out.println("包装类:java.lang.Long");
System.out.println("最小值:Long.MIN_VALUE=" + Long.MIN_VALUE);
System.out.println("最大值:Long.MAX_VALUE=" + Long.MAX_VALUE);
System.out.println();
// float
System.out.println("基本类型:float 二进制位数:" + Float.SIZE);
System.out.println("包装类:java.lang.Float");
System.out.println("最小值:Float.MIN_VALUE=" + Float.MIN_VALUE);
System.out.println("最大值:Float.MAX_VALUE=" + Float.MAX_VALUE);
System.out.println();
// double
System.out.println("基本类型:double 二进制位数:" + Double.SIZE);
System.out.println("包装类:java.lang.Double");
System.out.println("最小值:Double.MIN_VALUE=" + Double.MIN_VALUE);
System.out.println("最大值:Double.MAX_VALUE=" + Double.MAX_VALUE);
System.out.println();
// char
System.out.println("基本类型:char 二进制位数:" + Character.SIZE);
System.out.println("包装类:java.lang.Character");
// 以数值形式而不是字符形式将Character.MIN_VALUE输出到控制台
System.out.println("最小值:Character.MIN_VALUE="
+ (int) Character.MIN_VALUE);
// 以数值形式而不是字符形式将Character.MAX_VALUE输出到控制台
System.out.println("最大值:Character.MAX_VALUE="
+ (int) Character.MAX_VALUE);
}
}
编译以上代码输出结果如下所示:
基本类型:byte 二进制位数:8
包装类:java.lang.Byte
最小值:Byte.MIN_VALUE=-128
最大值:Byte.MAX_VALUE=127
基本类型:short 二进制位数:16
包装类:java.lang.Short
最小值:Short.MIN_VALUE=-32768
最大值:Short.MAX_VALUE=32767
基本类型:int 二进制位数:32
包装类:java.lang.Integer
最小值:Integer.MIN_VALUE=-2147483648
最大值:Integer.MAX_VALUE=2147483647
基本类型:long 二进制位数:64
包装类:java.lang.Long
最小值:Long.MIN_VALUE=-9223372036854775808
最大值:Long.MAX_VALUE=9223372036854775807
基本类型:float 二进制位数:32
包装类:java.lang.Float
最小值:Float.MIN_VALUE=1.4E-45
最大值:Float.MAX_VALUE=3.4028235E38
基本类型:double 二进制位数:64
包装类:java.lang.Double
最小值:Double.MIN_VALUE=4.9E-324
最大值:Double.MAX_VALUE=1.7976931348623157E308
基本类型:char 二进制位数:16
包装类:java.lang.Character
最小值:Character.MIN_VALUE=0
最大值:Character.MAX_VALUE=65535
Float和Double的最小值和最大值都是以科学记数法的形式输出的,结尾的"E+数字"表示E之前的数字要乘以10的多少次方。比如3.14E3就是,3.14E-3 就是 。
实际上,JAVA中还存在另外一种基本类型 void,它也有对应的包装类 java.lang.Void,不过我们无法直接对它们进行操作。
类型默认值
下表列出了 Java 各个类型的默认值:
数据类型 | 默认值 |
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0d |
char | 'u0000' |
String (or any object) | null |
boolean | false |
示例:
public class Test {
static boolean bool;
static byte by;
static char ch;
static double d;
static float f;
static int i;
static long l;
static short sh;
static String str;
public static void main(String[] args) {
System.out.println("Bool :" + bool);
System.out.println("Byte :" + by);
System.out.println("Character:" + ch);
System.out.println("Double :" + d);
System.out.println("Float :" + f);
System.out.println("Integer :" + i);
System.out.println("Long :" + l);
System.out.println("Short :" + sh);
System.out.println("String :" + str);
}
}
实例输出结果为:
Bool :false
Byte :0
Character:
Double :0.0
Float :0.0
Integer :0
Long :0
Short :0
String :null
引用类型
- 在Java中,引用类型的变量非常类似于C/C++的指针。引用类型指向一个对象,指向对象的变量是引用变量。这些变量在声明时被指定为一个特定的类型,比如 Employee、Puppy 等。变量一旦声明后,类型就不能被改变了。
- 对象、数组都是引用数据类型。
- 所有引用类型的默认值都是null。
- 一个引用变量可以用来引用任何与之兼容的类型。
- 例子:Person x = new Person("Zhangsan")。
Java 常量
常量在程序运行时是不能被修改的。
在 Java 中使用 final 关键字来修饰常量,声明方式和变量类似:
final double PI = 3.1415927;
虽然常量名也可以用小写,但为了便于识别,通常使用大写字母表示常量。
字面量可以赋给任何内置类型的变量。例如:
byte a = 68;
char a = 'A'
byte、int、long、和short都可以用十进制、16进制以及8进制的方式来表示。
当使用字面量的时候,前缀 0 表示 8 进制,而前缀 0x 代表 16 进制, 例如:
int decimal = 100;
int octal = 0144;
int hexa = 0x64;
和其他语言一样,Java的字符串常量也是包含在两个引号之间的字符序列。下面是字符串型字面量的例子:
"Hello World"
"two\nlines"
"\"This is in quotes\""
字符串常量和字符变量都可以包含任何 Unicode 字符。例如:
char a = '\u0001';
String a = "\u0001";
Java语言支持一些特殊的转义字符序列。
符号 | 字符含义 |
| 换行 (0x0a) |
| 回车 (0x0d) |
| 换页符(0x0c) |
| 退格 (0x08) |
| 空字符 (0x0) |
| 空格 (0x20) |
| 制表符 |
| 双引号 |
| 单引号 |
| 反斜杠 |
| 八进制字符 (ddd) |
| 16进制Unicode字符 (xxxx) |
自动类型转换
整型、实型(常量)、字符型数据可以混合运算。运算中,不同类型的数据先转化为同一类型,然后进行运算。
转换从低级到高级。
低 ------------------------------------> 高
byte,short,char—> int —> long—> float —> double
数据类型转换必须满足如下规则:
- 不能对boolean类型进行类型转换。
- 不能把对象类型转换成不相关类的对象。
- 在把容量大的类型转换为容量小的类型时必须使用强制类型转换。
- 转换过程中可能导致溢出或损失精度,例如:
int i =128;
byte b = (byte)i;
因为 byte 类型是 8 位,最大值为127,所以当 int 强制转换为 byte 类型时,值 128 时候就会导致溢出。
- 浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入,例如:
(int)23.7 == 23;
(int)-45.89f == -45
自动类型转换
必须满足转换前的数据类型的位数要低于转换后的数据类型,
例如: short数据类型的位数为16位,就可以自动转换位数为32的int类型,同样float数据类型的位数为32,可以自动转换为64位的double类型。
public class ZiDongLeiZhuan{
public static void main(String[] args){
char c1='a';//定义一个char类型
int i1 = c1;//char自动类型转换为int
System.out.println("char自动类型转换为int后的值等于"+i1);
char c2 = 'A';//定义一个char类型
int i2 = c2+1;//char 类型和 int 类型计算
System.out.println("char类型和int计算后的值等于"+i2);
}
}
运行结果为:
char自动类型转换为int后的值等于97
char类型和int计算后的值等于66
**解析:**c1 的值为字符 a ,查 ASCII 码表可知对应的 int 类型值为 97, A 对应值为 65,所以 i2=65+1=66。
强制类型转换
- 条件是转换的数据类型必须是兼容的。
- 格式:(type)value type是要强制类型转换后的数据类型 实例:
public class ForceTransform {
public static void main(String[] args){
int i1 = 123;
byte b = (byte)i1;//强制类型转换为byte
System.out.println("int强制类型转换为byte后的值等于"+b);
}
}
运行结果:
int强制类型转换为byte后的值等于123
隐含强制类型转换
- 整数的默认类型是 int。
- 小数默认是 double 类型浮点型,在定义 float 类型时必须在数字后面跟上 F 或者 f。
Java Number & Math 类
Java Number类
一般来说当需要使用数字的时候,我们通常使用内置数据类型,如:byte、int、long、double 等。
int a = 5000;
float b = 13.65f;
byte c = 0x4a;
然而,在实际开发过程中,我们经常会遇到需要使用对象,而不是内置数据类型的情形。为了解决这个问题,Java 语言为每一个内置数据类型提供了对应的包装类。
关于包装类的概念在第四章的第四节有更详细的介绍;可以先看。
这种由编译器特别支持的包装称为装箱,所以当内置数据类型被当作对象使用的时候,编译器会把内置类型装箱为包装类。相似的,编译器也可以把一个对象拆箱为内置类型。Number 类属于 java.lang 包。
下面是一个使用 Integer 对象的实例:
public class Test{
public static void main(String[] args){
Integer x = 5;
x = x + 10;
System.out.println(x);
}
}
以上实例编译运行结果如下:
15
当 x 被赋为整型值时,由于x是一个对象,所以编译器要对x进行装箱。然后,为了使x能进行加运算,所以要对x进行拆箱。
基本数据类型和它的包装类有什么不同呢?
package com.xml.a;
public class test04 {
public static void main(String[] args) {
int a = 10; // 这个a是基本类型的,它没有属性、方法这些东西
Integer b = 100; //这个是包装类Integer类型的,它有额外的丰富的属性和方法可以使用
a = a + 10;
b = b + 10;
System.out.println(a);
System.out.println(b);
System.out.println(b.toString());
}
}
Java Math 类
Java 的 Math 包含了用于执行基本数学运算的属性和方法,如初等指数、对数、平方根和三角函数。
Math 的方法都被定义为 static 形式,通过 Math 类可以在主函数中直接调用。
public class Test {
public static void main (String []args)
{
System.out.println("90 度的正弦值:" + Math.sin(Math.PI/2));
System.out.println("0度的余弦值:" + Math.cos(0));
System.out.println("60度的正切值:" + Math.tan(Math.PI/3));
System.out.println("1的反正切值: " + Math.atan(1));
System.out.println("π/2的角度值:" + Math.toDegrees(Math.PI/2));
System.out.println(Math.PI);
}
}
以上实例编译运行结果如下:
90 度的正弦值:1.0
0度的余弦值:1.0
60度的正切值:1.7320508075688767
1的反正切值: 0.7853981633974483
π/2的角度值:90.0
3.141592653589793
Number & Math 类方法
下面的表中列出的是 Number & Math 类常用的一些方法:
编号 | 方法与描述 |
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 |
|
Java Character 类
Character 类用于对单个字符进行操作。
Character 类在对象中包装一个基本类型 char 的值
char ch = 'a';
// Unicode 字符表示形式
char uniChar = '\u039A';
// 字符数组
char[] charArray ={ 'a', 'b', 'c', 'd', 'e' };
然而,在实际开发过程中,我们经常会遇到需要使用对象,而不是内置数据类型的情况。为了解决这个问题,Java语言为内置数据类型char提供了包装类Character类。
Character类提供了一系列方法来操纵字符。你可以使用Character的构造方法创建一个Character类对象,例如:
Character ch = new Character('a');
在某些情况下,Java编译器会自动创建一个Character对象。
例如,将一个char类型的参数传递给需要一个Character类型参数的方法时,那么编译器会自动地将char类型参数转换为Character对象。 这种特征称为装箱,反过来称为拆箱。
// 原始字符 'a' 装箱到 Character 对象 ch 中
Character ch = 'a';
// 原始字符 'x' 用 test 方法装箱
// 返回拆箱的值到 'c'
char c = test('x');
转义序列
前面有反斜杠(\)的字符代表转义字符,它对编译器来说是有特殊含义的。
下面列表展示了Java的转义序列:
转义序列 | 描述 |
| 在文中该处插入一个tab键 |
| 在文中该处插入一个后退键 |
| 在文中该处换行 |
| 在文中该处插入回车 |
| 在文中该处插入换页符 |
| 在文中该处插入单引号 |
| 在文中该处插入双引号 |
| 在文中该处插入反斜杠 |
当打印语句遇到一个转义序列时,编译器可以正确地对其进行解释。
以下实例转义双引号并输出:
public class Test {
public static void main(String[] args) {
System.out.println("访问\"谷歌!\"");
}
}
以上实例编译运行结果如下:
访问"谷歌!"
Character 方法
下面是Character类的方法:
序号 | 方法与描述 |
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
对于方法的完整列表,请参考的 java.lang.Character API 规范。
Java String 类
字符串广泛应用 在 Java 编程中,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。
在 Java 中,String
类是用于表示字符串的类,它提供了许多方法用于操作字符串。String
对象是不可变的,即一旦创建,它们的值就不能被更改。(特别注意:这里的不可变并非意味着String类型变量的值就不能改变,这一点可以转到附录C部分详细讲解。)
**注意:**String 类是不可改变的,所以你一旦创建了 String 对象,那它的值就无法改变了。
如果需要对字符串做很多修改,那么应该选择使用
StringBuffer / StringBuilder
类。
创建 String
对象
有几种方式可以创建 String
对象:
- 直接赋值:
String str1 = "Hello, World!";
通过这种方式创建String 的字符串存储在公共池中,当两个变量的字面值相同时,他们实际上可以看作是引用了相同的内存地址。
而 new 创建的字符串对象在堆上;即使当两个变量的字面值相同时,他们实际上也分别引用不同的内存地址。
- 通过构造函数:
String str2 = new String("Hello, World!");
这个图很好地说明了他们的区别:
常用的 String
方法
1. 获取字符串长度
int length = str1.length();
System.out.println("Length: " + length); // 输出:Length: 13
2. 字符串拼接
String str3 = str1.concat(" How are you?");
System.out.println(str3); // 输出:Hello, World! How are you?
或者使用 +
操作符:
String str4 = str1 + " How are you?";
System.out.println(str4); // 输出:Hello, World! How are you?
3. 字符串比较
- equals() 方法:比较两个字符串的内容是否相同。
String str5 = "Hello";
String str6 = "hello";
boolean isEqual = str5.equals(str6);
System.out.println(isEqual); // 输出:false
- equalsIgnoreCase() 方法:比较两个字符串的内容是否相同,忽略大小写。
boolean isEqualIgnoreCase = str5.equalsIgnoreCase(str6);
System.out.println(isEqualIgnoreCase); // 输出:true
4. 获取字符串中的字符
char ch = str1.charAt(0);
System.out.println(ch); // 输出:H
5. 查找子字符串
- indexOf() 方法:返回指定子字符串的第一次出现的索引。
int index = str1.indexOf("World");
System.out.println(index); // 输出:7
- lastIndexOf() 方法:返回指定子字符串的最后一次出现的索引。
int lastIndex = str1.lastIndexOf("o");
System.out.println(lastIndex); // 输出:8
6. 子字符串
- substring() 方法:返回从指定索引开始到结束的子字符串。
String subStr1 = str1.substring(7);
System.out.println(subStr1); // 输出:World!
String subStr2 = str1.substring(7, 12);
System.out.println(subStr2); // 输出:World
7. 转换大小写
String upperStr = str1.toUpperCase();
System.out.println(upperStr); // 输出:HELLO, WORLD!
String lowerStr = str1.toLowerCase();
System.out.println(lowerStr); // 输出:hello, world!
8. 去除空格
- trim() 方法:去除字符串前后的空格。
String str7 = " Hello, World! ";
String trimmedStr = str7.trim();
System.out.println(trimmedStr); // 输出:Hello, World!
9. 替换字符或子字符串
- replace() 方法:替换所有指定的字符。
String replacedStr = str1.replace('o', 'a');
System.out.println(replacedStr); // 输出:Hella, Warld!
- replaceAll() 方法:替换所有匹配正则表达式的子字符串。
String replacedAllStr = str1.replaceAll("World", "Java");
System.out.println(replacedAllStr); // 输出:Hello, Java!
10. 分割字符串
- split() 方法:将字符串分割为字符串数组。
String[] words = str1.split(" ");
for (String word : words) {
System.out.println(word);
}
// 输出:
// Hello,
// World!
示例代码
以下是一个完整的示例,展示了如何使用上述方法:
public class StringExample {
public static void main(String[] args) {
// 创建字符串
String str1 = "Hello, World!";
String str2 = new String("Hello, Java!");
// 获取字符串长度
System.out.println("Length of str1: " + str1.length());
// 字符串拼接
String str3 = str1.concat(" How are you?");
System.out.println("Concatenated String: " + str3);
// 字符串比较
System.out.println("str1 equals str2: " + str1.equals(str2));
System.out.println("str1 equalsIgnoreCase str2: " + str1.equalsIgnoreCase("hello, world!"));
// 获取字符
System.out.println("Character at index 0 in str1: " + str1.charAt(0));
// 查找子字符串
System.out.println("Index of 'World' in str1: " + str1.indexOf("World"));
System.out.println("Last index of 'o' in str1: " + str1.lastIndexOf("o"));
// 子字符串
System.out.println("Substring from index 7: " + str1.substring(7));
System.out.println("Substring from index 7 to 12: " + str1.substring(7, 12));
// 转换大小写
System.out.println("str1 to upper case: " + str1.toUpperCase());
System.out.println("str1 to lower case: " + str1.toLowerCase());
// 去除空格
String str4 = " Hello, World! ";
System.out.println("Trimmed str4: '" + str4.trim() + "'");
// 替换字符
System.out.println("Replace 'o' with 'a' in str1: " + str1.replace('o', 'a'));
System.out.println("Replace 'World' with 'Java' in str1: " + str1.replaceAll("World", "Java"));
// 分割字符串
String[] words = str1.split(" ");
System.out.println("Splitting str1:");
for (String word : words) {
System.out.println(word);
}
}
}
String
类是 Java 中非常重要的类,用于表示和操作字符串。它是不可变的,提供了丰富的方法来处理字符串。掌握这些方法可以帮助开发者更高效地进行字符串操作。
补充:重点说说字符串的比较
一个例子:
package com.xml.a;
public class test05 {
public static void main(String[] args) {
String a = "hello";
String b = "world!";
String c = "hello";
System.out.println(a == b); // false;字面值都不同,肯定false
System.out.println(a.equals(b)); //false;字面值都不同,肯定false
System.out.println(a == c); // true;字面值相同且引用相同的内存地址
System.out.println(a.equals(c)); //true;字面值相同,不要求引用相同的内存地址
a = "nihao"; // 实际上是创建了新的对象
System.out.println(a); // 输出:nihao;
// 重点
String x = new String("xiamingliang");
String y = new String("xiamingliang");
System.out.println(x == y); // false;字面值相同且引用相同的内存地址
System.out.println(x.equals(y)); // true;字面值相同,不要求引用相同的内存地址
}
}
在Java中,比较对象时使用==
和equals
会产生不同的结果,因为它们的比较方式不同。
String x = new String("xiamingliang"); String y = new String("xiamingliang");
代码分别创建了两个新的
String
对象。虽然它们的内容相同,但由于new
关键字的使用,它们被分配在不同的内存地址。
==
运算符
- 比较的是对象的引用。它检查两个引用是否指向同一个对象(即内存地址是否相同)。
x
和y
是两个不同的String
对象,虽然它们的内容相同,但它们在内存中的地址不同。因此,x == y
结果为false
。
equals
方法
- 比较的是对象的内容。它检查两个对象的内容是否相同。
String
类重写了equals
方法,使其比较字符串的字符序列是否相同。x.equals(y)
比较的是x
和y
的内容。由于x
和y
的内容都是"xiamingliang"
,所以结果为true
。
Java StringBuffer 和 StringBuilder 类
他们的区别与联系
当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。
和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
在使用 StringBuffer 类时,每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,所以如果需要对字符串进行修改推荐使用 StringBuffer。
StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。
StringBuilder 使用示例
package com.xml.a;
public class Test06 {
public static void main(String args[]){
StringBuilder sb = new StringBuilder(10);
sb.append("Hello..");
System.out.println(sb); // Hello..
sb.append("!");
System.out.println(sb); // Hello..!
sb.insert(6, "Java");
System.out.println(sb); // Hello.Java.!
sb.delete(5,8);
System.out.println(sb); // Hellova.!
}
}
然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。
StringBuffer 方法
以下是 StringBuffer 类支持的主要方法:
序号 | 方法描述 |
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
以下列表列出了 StringBuffer 类的其他常用方法:
序号 | 方法描述 |
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
第三节 表达式
Java 变量类型
在 Java 语言中,所有的变量在使用前必须声明。
声明变量的基本格式如下:
type name1 [ = value1][, name2 [= value2] ...] ;
格式说明:
- type -- 数据类型。
- namex-- 是变量名,可以使用逗号 , 隔开来声明多个同类型变量。
以下列出了一些变量的声明实例。注意有些包含了初始化过程。
int a, b, c; // 声明三个int型整数:a、 b、c
int d = 3, e = 4, f = 5; // 声明三个整数并赋予初值
byte z = 22; // 声明并初始化 z
String s = "hello"; // 声明并初始化字符串 s
double pi = 3.14159; // 声明了双精度浮点型变量 pi
char x = 'x'; // 声明变量 x 的值是字符 'x'。
Java 语言支持的变量类型有:
局部变量(Local Variables):
局部变量是在方法、构造函数或块内部声明的变量,它们在声明的方法、构造函数或块执行结束后被销毁,局部变量在声明时需要初始化,否则会导致编译错误。
package com.xml.a;
public class test01 {
public static void main(String[] x) {
int a;
//System.out.println(a); // 这样是不对的,局部变量在没有初始化前是不能使用的
a = 10;
System.out.println(a);
}
}
实例变量(Instance Variables):
实例变量是在类中声明,但在方法、构造函数或块之外,它们属于类的实例,每个类的实例都有自己的副本,如果不明确初始化,实例变量会被赋予默认值(数值类型为0,boolean类型为false,对象引用类型为null)。
package com.xml.a;
public class test01 {
public static void main(String[] x) {
int a;
//System.out.println(a); // 这样是不对的,局部变量在没有初始化前是不能使用的
a = 10;
System.out.println(a);
// 实例化一个对象
testx obj01 = new testx();
System.out.println(obj01.a); //这里实例化对象obj01访问的变量没有经过显式底初始化,但是因为它是示例变量,会被默认构造函数自动赋予初始值。
}
}
class testx{
int a;
}
静态变量或类变量(Class Variables):
类变量是在类中用 static 关键字声明的变量,它们属于类而不是实例,所有该类的实例共享同一个类变量的值,类变量在类加载时被初始化,而且只初始化一次。
静态变量的生命周期与程序的生命周期一样长,即它们在类加载时被创建,在整个程序运行期间都存在,直到程序结束才会被销毁。因此,静态变量可以用来存储整个程序都需要使用的数据,如配置信息、全局变量等。
package com.xml.a;
public class test01 {
public static void main(String[] x) {
int a;
//System.out.println(a); // 这样是不对的,局部变量在没有初始化前是不能使用的
a = 10;
System.out.println(a);
System.out.println("----------");
// 直接使用类中的静态变量,也叫类变量
System.out.println(testy.a); // 0;无需实例化,即可直接访问
// 实例化一个对象
testy obj02 = new testy();
System.out.println(obj02.a); // 0;虽然能访问,但是是不建议这样做的
// 修改静态变量的值
testy.a = 20;
System.out.println(testy.a); // 20
System.out.println(obj02.a); // 20;修改后之前初始化的实例该变量也会改变。虽然能访问,但是是不建议这样做的
// 修改后实例化一个对象
testy obj03 = new testy();
System.out.println(obj03.a); // 20;之后实例化的对象值也是新的,虽然能访问,但是是不建议这样做的
}
}
class testy{
static int a;
}
参数变量(Parameters):
参数是方法或构造函数声明中的变量,用于接收调用该方法或构造函数时传递的值,参数变量的作用域只限于方法内部。
public void exampleMethod(parameterType parameterName) { //这里的parameterName就是参数变量
// 参数变量
// ...
}
Java 中的参数变量是指在方法或构造函数中声明的变量,用于接收传递给方法或构造函数的值。参数变量与局部变量类似,但它们只在方法或构造函数被调用时存在,并且只能在方法或构造函数内部使用。
- parameterName -- 表示参数变量的名称。
- parameterType -- 表示参数变量的类型。
在调用方法时,我们必须为参数变量传递值,这些值可以是常量、变量或表达式。
方法参数变量的值传递方式有两种:值传递和引用传递。
- **值传递:**在方法调用时,传递的是实际参数的值的副本。当参数变量被赋予新的值时,只会修改副本的值,不会影响原始值。Java 中的基本数据类型都采用值传递方式传递参数变量的值。
- **引用传递:**在方法调用时,传递的是实际参数的引用(即内存地址)。当参数变量被赋予新的值时,会修改原始值的内容。Java 中的对象类型采用引用传递方式传递参数变量的值。
参数变量-值传递
示例:
package com.xml.a;
public class test03 {
public static void main(String[] args) {
int a = 10;
int b = 20;
System.out.println("Before swap: a = " + a + ", b = " + b);
swap(a, b);
System.out.println("After swap: a = " + a + ", b = " + b); // 值不变
}
public static void swap(int x, int y) {
int temp = x;
x = y;
y = temp;
}
}
结果:
Before swap: a = 10, b = 20
After swap: a = 10, b = 20
参数变量-引用传递
示例:
public class test02 {
public static void main(String[] x) {
String[] test = {"hello", "world"};
System.out.println("Before Swap:" + test[0] + ", " + test[1]);
swap(test);
System.out.println("Before Swap:" + test[0] + ", " + test[1]); // test发生了变化
}
public static void swap(String[] str) {
String temp = str[0];
str[0] = str[1];
str[1] = temp;
}
}
结果:
Before Swap:hello, world
After Swap:world, hello
常量和静态变量的区别
常量也是与类相关的,但它是用 final 关键字修饰的变量,一旦被赋值就不能再修改。与静态变量不同的是,常量在编译时就已经确定了它的值,而静态变量的值可以在运行时改变。另外,常量通常用于存储一些固定的值,如数学常数、配置信息等,而静态变量通常用于存储可变的数据,如计数器、全局状态等。
总之,静态变量是与类相关的变量,具有唯一性和共享性,可以用于存储整个程序都需要使用的数据,但需要注意初始化时机和与常量的区别。
Java 修饰符
Java语言提供了很多修饰符,主要分为以下两类:
- 访问修饰符
- 非访问修饰符
修饰符用来定义类、方法或者变量,通常放在语句的最前端。我们通过下面的例子来说明:
public class ClassName {
// ...
}
private boolean myFlag;
static final double weeks = 9.5;
protected static final int BOXWIDTH = 42;
public static void main(String[] arguments) {
// 方法体
}
访问控制修饰符
Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。
- default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
- private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
- public : 对所有类可见。使用对象:类、接口、变量、方法
- protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
我们可以通过以下表来说明访问权限:
修饰符 | 当前类 | 同一包内 | 子孙类(同一包) | 子孙类(不同包) | 其他包 |
| Y | Y | Y | Y | Y |
| Y | Y | Y | Y/N | N |
| Y | Y | Y | N | N |
| Y | N | N | N | N |
protected 需要从以下两个点来分析说明:
- 子类与基类在同一包中:被声明为 protected 的变量、方法和构造器能被同一个包中的任何其他类访问;
- 子类与基类不在同一包中:那么在子类中,子类实例可以访问其从基类继承而来的 protected 方法,而不能访问基类实例的protected方法。
访问控制和继承
请注意以下方法继承的规则:
- 父类中声明为 public 的方法在子类中也必须为 public。
- 父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private。
- 父类中声明为 private 的方法,不能够被子类继承。
非访问修饰符
为了实现一些其他的功能,Java 也提供了许多非访问修饰符。
- static 修饰符,用来修饰类方法和类变量。
- final 修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。
- abstract 修饰符,用来创建抽象类和抽象方法。
- synchronized 和 volatile 修饰符,主要用于线程的编程。
static 修饰符
- 静态变量:
static 关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。 静态变量也被称为类变量。局部变量不能被声明为 static 变量。 - 静态方法:
static 关键字用来声明独立于对象的静态方法。静态方法不能使用类的非静态变量。静态方法从参数列表得到数据,然后计算这些数据。
对类变量和方法的访问可以直接使用 classname.variablename 和 classname.methodname 的方式访问。
final 修饰符
final 变量:
final 表示"最后的、最终的"含义,变量一旦赋值后,不能被重新赋值。被 final 修饰的实例变量必须显式指定初始值。
final 修饰符通常和 static 修饰符一起使用来创建类常量。
final 方法
父类中的 final 方法可以被子类继承,但是不能被子类重写。
声明 final 方法的主要目的是防止该方法的内容被修改。
final 类
final 类不能被继承,没有类能够继承 final 类的任何特性。
abstract 修饰符
抽象类:
抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。
一个类不能同时被 abstract 和 final 修饰。如果一个类包含抽象方法,那么该类一定要声明为抽象类,否则将出现编译错误。
抽象类可以包含抽象方法和非抽象方法。
抽象方法:
抽象方法是一种没有任何实现的方法,该方法的具体实现由子类提供。
抽象方法不能被声明成 final 和 static。
任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类。
如果一个类包含若干个抽象方法,那么该类必须声明为抽象类。抽象类可以不包含抽象方法。
抽象方法的声明以分号结尾,例如:public abstract sample();。
synchronized 修饰符
synchronized 关键字声明的方法同一时间只能被一个线程访问。synchronized 修饰符可以应用于四个访问修饰符。
transient 修饰符(短暂的)
序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。
该修饰符包含在定义变量的语句中,用来预处理类和变量的数据类型。
volatile 修饰符(易变的)
volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
一个 volatile 对象引用可能是 null。
一个常见的用法示例:
public class MyRunnable implements Runnable
{
private volatile boolean active;
public void run()
{
active = true;
while (active) // 第一行
{
// 代码
}
}
public void stop()
{
active = false; // 第二行
}
}
Java 运算符
计算机的最基本用途之一就是执行数学运算,作为一门计算机语言,Java也提供了一套丰富的运算符来操纵变量。我们可以把运算符分成以下几组:
- 算术运算符
- 关系运算符
- 位运算符
- 逻辑运算符
- 赋值运算符
- 其他运算符
算术运算符
算术运算符用在数学表达式中,它们的作用和在数学中的作用一样。下表列出了所有的算术运算符。
表格中的实例假设整数变量A的值为10,变量B的值为20:
操作符 | 描述 | 例子 |
| 加法 - 相加运算符两侧的值 | A + B 等于 30 |
| 减法 - 左操作数减去右操作数 | A – B 等于 -10 |
| 乘法 - 相乘操作符两侧的值 | A * B等于200 |
| 除法 - 左操作数除以右操作数 | B / A等于2 |
| 取余 - 左操作数除以右操作数的余数 | B%A等于0 |
| 自增: 操作数的值增加1 | B++ 或 ++B 等于 21(区别详见下文) |
| 自减: 操作数的值减少1 | B-- 或 --B 等于 19(区别详见下文) |
自增自减运算符
1、自增(++)自减(--)运算符是一种特殊的算术运算符,在算术运算符中需要两个操作数来进行运算,而自增自减运算符是一个操作数。
2、前缀自增自减法(++a,--a): 先进行自增或者自减运算,再进行表达式运算。
3、后缀自增自减法(a++,a--): 先进行表达式运算,再进行自增或者自减运算。
public class selfAddMinus{
public static void main(String[] args){
int a = 5;//定义一个变量;
int b = 5;
int x = 2*++a; // 拆分为:a=a+1,x=2*a
int y = 2*b++; // 拆分为:y=2*b,b=b+1
System.out.println("自增运算符前缀运算后a="+a+",x="+x);
System.out.println("自增运算符后缀运算后b="+b+",y="+y);
}
}
运行结果为:
自增运算符前缀运算后a=6,x=12
自增运算符后缀运算后b=6,y=10
关系运算符
下表为Java支持的关系运算符。
表格中的实例整数变量A的值为10,变量B的值为20:
位运算符
运算符 | 描述 | 例子 |
== | 检查如果两个操作数的值是否相等,如果相等则条件为真。 | (A == B)为假。 |
!= | 检查如果两个操作数的值是否相等,如果值不相等则条件为真。 | (A != B) 为真。 |
> | 检查左操作数的值是否大于右操作数的值,如果是那么条件为真。 | (A> B)为假。 |
< | 检查左操作数的值是否小于右操作数的值,如果是那么条件为真。 | (A <B)为真。 |
>= | 检查左操作数的值是否大于或等于右操作数的值,如果是那么条件为真。 | (A> = B)为假。 |
<= | 检查左操作数的值是否小于或等于右操作数的值,如果是那么条件为真。 | (A <= B)为真。 |
Java定义了位运算符,应用于整数类型(int),长整型(long),短整型(short),字符型(char),和字节型(byte)等类型。
位运算符作用在所有的位上,并且按位运算。假设a = 60,b = 13;它们的二进制格式表示将如下:
A = 0011 1100
B = 0000 1101
-----------------
A&B = 0000 1100
A | B = 0011 1101
A ^ B = 0011 0001
~A= 1100 0011
下表列出了位运算符的基本运算,假设整数变量 A 的值为 60 和变量 B 的值为 13:
操作符 | 描述 | 例子 |
| 如果相对应位都是1,则结果为1,否则为0 | (A&B),得到12,即0000 1100 |
` | ` | 如果相对应位都是 0,则结果为 0,否则为 1 |
^ | 如果相对应位值相同,则结果为0,否则为1 | (A ^ B)得到49,即 0011 0001 |
| 按位取反运算符翻转操作数的每一位,即0变成1,1变成0。 | (〜A)得到-61,即1100 0011 |
| 按位左移运算符。左操作数按位左移右操作数指定的位数。 | A << 2得到240,即 1111 0000 |
| 按位右移运算符。左操作数按位右移右操作数指定的位数。 | A >> 2得到15即 1111 |
| 按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。 | A>>>2得到15即0000 1111 |
逻辑运算符
下表列出了逻辑运算符的基本运算,假设布尔变量A为真,变量B为假
操作符 | 描述 | 例子 |
| 称为逻辑与运算符。当且仅当两个操作数都为真,条件才为真。 | (A && B)为假。 |
` | ` | |
| 称为逻辑非运算符。用来反转操作数的逻辑状态。如果条件为true,则逻辑非运算符将得到false。 | !(A && B)为真。 |
短路逻辑运算符
当使用与逻辑运算符时,在两个操作数都为true时,结果才为true,但是当得到第一个操作为false时,其结果就必定是false,这时候就不会再判断第二个操作了。
public class LuoJi{
public static void main(String[] args){
int a = 5;//定义一个变量;
boolean b = (a<4)&&(a++<10); // 使用到了短路逻辑运算符(&&),首先判断 a<4 的结果为 false,则 b 的结果必定是 false,所以不再执行第二个操作 a++<10 的判断,所以 a 的值为 5。
System.out.println("使用短路逻辑运算符的结果为"+b);
System.out.println("a的结果为"+a);
}
}
赋值运算符
下面是Java语言支持的赋值运算符:
操作符 | 描述 | 例子 |
| 简单的赋值运算符,将右操作数的值赋给左侧操作数 | C = A + B将把A + B得到的值赋给C |
| 加和赋值操作符,它把左操作数和右操作数相加赋值给左操作数 | C + = A等价于C = C + A |
| 减和赋值操作符,它把左操作数和右操作数相减赋值给左操作数 | C - = A等价于C = C - A |
| 乘和赋值操作符,它把左操作数和右操作数相乘赋值给左操作数 | C * = A等价于C = C * A |
| 除和赋值操作符,它把左操作数和右操作数相除赋值给左操作数 | C / = A,C 与 A 同类型时等价于 C = C / A |
| 取模和赋值操作符,它把左操作数和右操作数取模后赋值给左操作数 | C%= A等价于C = C%A |
| 左移位赋值运算符 | C << = 2等价于C = C << 2 |
| 右移位赋值运算符 | C >> = 2等价于C = C >> 2 |
| 按位与赋值运算符 | C&= 2等价于C = C&2 |
| 按位异或赋值操作符 | C ^ = 2等价于C = C ^ 2 |
` | =` | 按位或赋值操作符 |
条件运算符(?:)
条件运算符也被称为三元运算符。该运算符有3个操作数,并且需要判断布尔表达式的值。该运算符的主要是决定哪个值应该赋值给变量。
public class Test {
public static void main(String[] args){
int a , b;
a = 10;
// 如果 a 等于 1 成立,则设置 b 为 20,否则为 30
b = (a == 1) ? 20 : 30;
System.out.println( "Value of b is : " + b );
// 如果 a 等于 10 成立,则设置 b 为 20,否则为 30
b = (a == 10) ? 20 : 30;
System.out.println( "Value of b is : " + b );
}
}
instanceof 运算符
该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)。
instanceof运算符使用格式如下:
( Object reference variable ) instanceof (class/interface type)
如果运算符左侧变量所指的对象,是操作符右侧类或接口(class/interface)的一个对象,那么结果为真。
下面是一个例子:
String name = "James";
boolean result = name instanceof String; // 由于 name 是 String 类型,所以返回真
如果被比较的对象兼容于右侧类型,该运算符仍然返回 true。
Java运算符优先级
当多个运算符出现在一个表达式中,谁先谁后呢?这就涉及到运算符的优先级别的问题。在一个多运算符的表达式中,运算符优先级不同会导致最后得出的结果差别甚大。
例如,,这个表达式如果按加号最优先计算,答案就是 18,如果按照乘号最优先,答案则是 14。
再如,;这里x得到13,而不是20,因为乘法运算符比加法运算符有较高的优先级,所以先计算3 * 2得到6,然后再加7。
下表中具有最高优先级的运算符在的表的最上面,最低优先级的在表的底部。
类别 | 操作符 | 关联性 |
后缀 | () [] . (点操作符) | 左到右 |
一元 | expr++ expr-- | 从左到右 |
一元 | ++expr --expr + - ~ ! | 从右到左 |
乘性 | * /% | 左到右 |
加性 | + - | 左到右 |
移位 | >> >>> << | 左到右 |
关系 | > >= < <= | 左到右 |
相等 | == != | 左到右 |
按位与 | & | 左到右 |
按位异或 | ^ | 左到右 |
按位或 | | | 左到右 |
逻辑与 | && | 左到右 |
逻辑或 | | | | 左到右 |
条件 | ?: | 从右到左 |
赋值 | = + = - = * = / =%= >> = << =&= ^ = | = | 从右到左 |
逗号 | , | 左到右 |