Java基础知识总结
Java语言特性
基本数据类型
变量就是通过申请内存来存储值。
内存管理系统会根据变量的类型为变量分配存储空间,分配的空间只能用来存储该类型的数据。
因此,可以通过定义不同类型的变量在内存中存储整数、小数或者字符。
Java的两大数据类型:
- 内置数据类型
- 引用数据类型
内置数据类型
内置数据类型分四大类,共计八种,其中包括六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。
byte:
- byte 数据类型是8位、有符号的,以二进制补码表示的整数;
- 最小值:-128(-2^7);
- 最大值: 127(2^7-1);
- byte 类型用在大型数组中节约空间,主要代替整数,因为 byte 变量占用的空间 - 只有 int 类型的四分之一;
- 包装类:java.lang.Byte
- 例子:byte a = 100,byte b = -50。
short:
- short 数据类型是 16 位、有符号的以二进制补码表示的整数
- 最小值:-32768(-2^15);
- 最大值: 32767(2^15 - 1);
- Short 数据类型也可以像 byte 那样节省空间。一个short变量是int型变量所占空间的二分之一;
- 包装类:java.lang.Short
- 例子:short s1 = 10000,short s2 = -20000。
int:
- int 数据类型是32位、有符号的以二进制补码表示的整数;
- 最小值:-2,147,483,648(-2^31);
- 最大值: 2,147,483,647(2^31 - 1);
- 一般地整型变量默认为 int 类型;
- 包装类:java.lang.Integer
- 例子: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);
- 这种类型主要使用在需要比较大整数的系统上;
- 包装类:java.lang.Long
- 例子: long a = 100000L,Long b = -200000L。
注:"L"理论上不分大小写,但是若写成"l"容易与数字"1"混淆,不容易分辩。所以最好大写。
float:
- float 数据类型是单精度、32位、符合IEEE 754标准的浮点数;
- float 在储存大型浮点数组的时候可节省内存空间;
- 最小值: 1.4E-45;
- 最大值: 3.4028235E38;
- 浮点数不能用来表示精确的值,如货币;
- 包装类:java.lang.Float
- 例子:float f = 234.5f。
double:
- double 数据类型是双精度、64 位、符合IEEE 754标准的浮点数;
- 浮点数的默认类型为double类型;
- 最小值: 4.9E-324;
- 最大值: 1.7976931348623157E308;
- double类型同样不能表示精确的值,如货币;
- 包装类:java.lang.Double
- 例子:double d = 123.4。
boolean:
- boolean数据类型只有两个取值:true 和 false;
- 这种类型只作为一种标志来记录 true/false 情况
- 包装类:java.lang.Boolean
- 例子:boolean b= true。
char:
- char类型是一个单一的 16 位 Unicode 字符;
- 最小值: \u0000(即为0);
- 最大值: \uffff(即为65,535);
- char 数据类型可以储存任何字符;
- 包装类:java.lang.Character
- 例子:char c= ‘A’;
flocat和double的最小值和最大值都是以科学记数法的形式输出的,结尾的"E+数字"表示E之前的数字要乘以10的多少次方。比如3.14E3就是3.14 × 10^3 =3140,3.14E-3 就是 3.14 x 10^-3 =0.00314。
补充:Java中还存在另外一种基本类型void,对应的包装类为 java.lang.Void,但是我们无法直接对它进行操作。
类型默认值
Java各个类型的默认值:
数据类型 | 默认值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0d |
char | ‘u0000’ |
boolean | false |
String(or any object) | null |
引用数据类型
- 在Java中,引用类型的变量非常类似于C/C++的指针。引用类型指向一个对象,指向对象的变量是引用变量。变量一旦声明后,类型就不能被改变了。
- 对象、数组都是引用数据类型。
- 所有引用类型的默认值都是 null。
- 一个引用变量可以用来引用任何与之兼容的类型。
- 例子:Student student = new Student(“zhangsan”);
Java关键字、注释
Java关键字:
下面列出了Java关键字,这些关键字不能用于常量、变量和任何标识符的名称。
类别 | 关键字 | 说明 |
---|---|---|
访问控制 | private | 私有的 |
- | protected | 受保护的 |
- | public | 公共的 |
- | default | 默认 |
类、方法和变量修饰符 | abstract | 声明抽象 |
- | class | 类 |
- | extends | 扩充,继承 |
- | final | 最终值,不可改变的 |
- | implements | 实现(接口) |
- | interface | 接口 |
- | native | 本地,原声方法(非Java实现) |
- | new | 新,创建 |
- | static | 静态 |
- | strictfp | 严格,精准 |
- | synchronized | 线程,同步 |
- | transient | 短暂 |
- | volatile | 易失 |
程序控制语句 | break | 跳出循环 |
- | case | 定义一个值以供switch选择 |
- | continue | 继续 |
- | default | 默认 |
- | do | 运行 |
- | else | 否则 |
- | for | 循环 |
- | if | 如果 |
- | instanceof | 实例 |
- | return | 返回 |
- | switch | 根据值选择执行 |
- | while | 循环 |
错误处理 | assert | 断言表达式是否为真 |
- | catch | 捕捉异常 |
- | finally | 有没有异常都执行 |
- | throw | 抛出一个异常对象 |
- | throws | 声明一个异常可能被抛出 |
- | try | 捕获异常 |
包相关 | import | 引入 |
- | package | 包 |
基本类型 | byte | 字节型 |
- | short | 短整型 |
- | int | 整型 |
- | long | 长整型 |
- | float | 单精度浮点 |
- | double | 双精度浮点 |
- | char | 字符型 |
- | boolean | 布尔型 |
变量引用 | super | 父类,超类 |
- | this | 本类 |
- | void | 无返回值 |
保留关键字 | goto | 是关键字,但不能使用 |
- | const | 是关键字,但不能使用 |
- | null | 空 |
注释:
解释说明的,在程序中不运行,主要增加程序的可读性
注释分类:
- 单行注释
//
- 多行注释
/* 注释内容 */
- 文档注释
/** 注释内容 */
public class HelloWorld {
/** 这是一个Java程序
*它将打印Hello World
*这是一个文档注释的示例
*/
public static void main(String[] args){
// 这是单行注释的示例
/* 这个是
多行注释的示例
*/
System.out.println("Hello World");
}
}
转义字符序列、标识符
转义字符序列:
符号 | 字符含义 |
---|---|
\n | 换行(0x0a) |
\r | 回车(0x0d) |
\f | 换页符(0x0c) |
\b | 退格(0x08) |
\0 | 空字符(0x0) |
\s | 空格(0x20) |
\t | 制表符 |
\" | 双引号 |
\' | 单引号 |
\\ | 反斜杠 |
\ddd | 八进制字符(ddd) |
\uxxxx | 16进制Unicode字符(xxx) |
标识符:
Java语言中,类名、变量名、方法名称、参数名都被称为标识符。
标识符命名规则:
- 所有的标识符都应该以字母(A-Z 或者 a-z)、下划线(_)或美元符($)开头。
- 首字符其后可以是字母(A-Z 或者 a-z)、数字、下划线(_)或美元符($)的任何字符组成。
- 标识符区分大小写。
- 不能使用关键字。
- 合法标识符举例:_abc、_1_abc、Abc、
$Hello
、A$B。- 非法标识符举例:#xyz、5a、2020、-salary。
标识符命名规范:
建议:
- 标识符见名知意,也就是编写单词,例如:name、age。
- 类名的每个单词首字母大写,例如:Hello、HelloWorld、Student。
- 变量名、参数名、方法名等采用驼峰命名法,例如:name、firstName、getAge。
- 标识符不要超过15个字符。
修饰符
修饰符用来修饰定义类、方法、或者变量,通常放在语句的最前端。
Java语言提供了很多修饰符,主要分为以下两类:
- 访问修饰符
- 非访问修饰符
访问控制修饰符(public,protected,default,private)
Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java支持4种不同的访问权限:
- public:对所有类可见。使用对象:类、接口、变量、方法
- protected:对同一包内的类和其所有子类可见。使用对象:变量、方法。注意:不能修饰类(外部类)。
- default:默认修饰符(即什么都不写),在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
- private:在同一类内可见。使用对象:变量、方法。注意:不能修饰类(外部类)。
可以通过下表来记住各个修饰符的访问权限(从上至下呈三角形):
修饰符 | 当前类 | 同一包内 | 子孙类(不同包) | 所有类(不同包) |
---|---|---|---|---|
private | Y | N | N | N |
default(默认) | Y | Y | N | N |
protected | Y | Y | Y | N |
public | Y | Y | Y | Y |
公有访问修饰符-public
被声明为public的类、方法、构造方法和接口能够被任何其他类访问。
以下类和函数使用了公有访问控制:
package com.bjpowernode.packageA;
//如果几个分布在不同包中的public类相互访问,则需要导入相应public类所在的包。
import com.bjpowernode.packageB.B;
public class A{
public static void main(String[] args) {
//创建 packageB包下 public类 B的实例对象
B b = new B();
}
}
补充:
- Java程序的main()方法必须设置成公有的,否则Java解释器将不能运行该类。
- 由于类的继承性,类中所有的公有方法和变量都能被其子类继承。
受保护的访问修饰符-protected
protected可以修饰数据成员,构造方法,方法成员,但不能修饰类(内部类除外)。
protected需要从以下两个点来分析说明:
- 当子类与父类在同一包中:被声明为protected的变量、方法和构造器能被同一个包中的任何其他类访问。
- 当子类与父类不在同一个包中:在子类中,可以通过子类对象来访问父类的protected变量和方法,但不能通过父类对象和其他子类的对象来访问父类的protected变量和方法。
通过代码演示如下(示例):
在包(parent)下创建父类 Animal
package com.parent
//创建一个父类为 Animal
public class Animal{
//定义二个属性(id),(name)分别被 public 和 protected 修饰。
public int id = 0;
protected String name = "animal";
public Animal(){
}
public Animal(int id,String name){
this.id = id;
this.name = name;
}
}
测试1:在不同包(child)下的子类中通过子类对象与父类对象分别访问父类的 protected 属性。
package com.child;
import com.parent.Animal;
//创建子类 Bird
public class Bird extends Animal {
public static void main(String[] args) {
//创建子类对象
Bird bird = new Bird();
//创建父类对象
Animal animal = new Animal();
System.out.println(bird.id);//结果: 0
System.out.println(bird.name);//结果: animal
System.out.println(animal.id);//结果: 0
//animal.name 处直接报红,因为: 在子类中通过父类对象 animal去访问父类的 protected 属性是访问不到的。
System.out.println(animal.name);//编译失败
}
}
测试2:在不同包(child)下的子类中通过其他子类的对象来访问父类的 protected 属性。
package com.child;
import com.parent.Animal;
//创建子类 Dog
public class Dog extends Animal {
}
package com.child;
import com.parent.Animal;
public class Bird extends Animal {
public static void main(String[] args) {
//创建其他子类的对象
Dog dog = new Dog();
System.out.println(dog.id);//结果: 0
//animal.name 处直接报红,因为: 在子类中通过其他子类的对象 dog 去访问父类的 protected 属性是访问不到的。
System.out.println(dog.name);//编译失败
}
}
补充:接口及接口的成员变量和成员方法不能声明为protected。
默认访问修饰符-default
变量和方法的声明可以不使用任何访问修饰符,默认有 default 修饰。由 default 修饰的变量和方法,对同一包内的类是可见的。
补充:接口里的变量都隐式声明为public static final,而接口里的方法默认访问权限为public。
私有访问修饰符-private
私有访问修饰符是最严格的访问级别,所以被声明为 private 的方法、变量和构造方法只能被所属类访问,并且类和接口不能声明为 private。
private 访问修饰符的使用主要用来隐藏类的实现细节和保护类的数据。
通过代码演示如下(示例):
public class Student {
//将 name 变量设置为私有变量
private String name = "zhangsan";
//构造 Getter() 和 Setter() 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class teacher{
public static void main(String[] args) {
//创建 Student 实例
Student student = new Student();
//通过 student 实例中的 get() 方法返回私有变量 name 的值
System.out.println(student.getName());//结果: zhangsan
//通过 student 实例中的 set() 方法设置私有变量 name 的值
student.setName("lisi");
//再次返回
System.out.println(student.getName());//结果: lisi
}
}
当类中的变量设置为私有变量时,其他类就不能直接得到和设置该变量的值。为了使其他类能够操作该变量,我们可以通过两个 public 方法:Getter() 和 Setter() 来返回和设置该变量值。
访问控制和继承:
- 父类中声明为 public 的方法在子类中也必须为 public。
- 父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private。
- 父类在声明为 private 的方法,子类不能继承。
非访问修饰符(static,final,abstract,synchronized,volatile,transient)
为实现一些其他的功能,Java也提供了许多的非访问修饰符。
- static 修饰符,用来修饰类方法和类变量。
- final 修饰符,用来修饰类、方法和变量(final 修饰的类不能被继承,修饰的方法不能被重写,修饰的变量为常量,不可修改)。
- abstract 修饰符,用来创建抽象类和抽象方法。
- synchronized 和 volatile 修饰符,主要用于线程的编程。
static修饰符:
- 静态变量:static 关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。 静态变量也被称为类变量。局部变量不能被声明为 static 变量。
- 静态方法:static 关键字用来声明独立于对象的静态方法。静态方法里不能使用类中的非静态变量。静态方法从参数列表得到数据,然后计算这些数据。
- 独立于对象:static修饰的方法和变量是属于整个类的类方法和类变量,而不属于某个具体对象。
- 对类变量和静态方法的访问可以直接使用 classname.variablename 和 classname.methodname 的方式访问。
- main方法是静态方法。在Java的每个Application程序中,都必须有且只能有一个main方法,它是Application程序运行的入口点(main方法是程序的起点,main方法结束则程序结束)
final修饰符:
final 表示最终的,可以修饰类、修饰属性、修饰方法。
- final 修饰 类,该类不可以被继承。
没有类能够继承 final类的任何特性。
- final 修饰 变量,该值一旦赋值后不能被重新赋值。被 final 修饰的实例变量必须显式指定初始值。
非静态 final 的赋值方式有三种:定义初始化、非静态代码块、构造方法。
静态 final 的赋值方式有两种:定义初始化、静态代码块。
通过代码演示如下(示例):
//定义初始化时进行赋值
public class A {
final int a = 10;
}
//非静态代码块 和 静态代码块 中赋值
class B{
final int x;
final static int y;
{
x = 10;
}
static {
y = 20;
}
}
//构造器中赋值
class C{
final int num;
public a(int num) {
this.num = num;
}
}
- final 修饰 方法,该方法可以被子类继承,但不可以被子类重写(但可以重载)。
声明 final 方法的主要目的是防止该方法的内容被修改。
final修饰符通常与static修饰符一起使用来创建类常量,因为在方法区只开辟一块空间,并且一直到程序结束,相对会节省空间,使用 类名.常量名称 进行访问。
abstract修饰符:
abstract关键词修饰类时该类为抽象类、修饰方法时该方法为抽象方法。
抽象类:
1 .抽象类不能被实例化对象,声明抽象类的目的就是为了将来对该类进行扩充。
2 .一个类不能同时被 abstract 和 final 修饰,因为被 final 修饰的类不能被继承。
3 .抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
通过代码演示如下(示例):
//抽象类
abstract class A{
private int id;
private String name;
private String age;
//抽象方法
public abstract void student();
public abstract void teacher();
}
抽象方法:
1 . 抽象方法是一种没有任何实现的方法(即没有方法体,并以分号结尾),该方法的具体实现由子类提供(即重写)。
2 .抽象方法不能被声明成 final 和 static。因为被 final 和 static 修饰的方法不能被子类继承和覆盖。
3 .抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
4 .如果一个类包含若干个抽象方法,那么该类必须声明为抽象类。抽象类可以不包含抽象方法。
通过代码演示如下(示例):
//创建抽象类 Animal
public abstract class Animal{
abstract void name(); //抽象方法
}
//创建类 cat 继承抽象类
class Cat extends Animal{
//实现抽象方法
void name(){
.........
}
}
抽象类和接口的区别和具体的特性可以参考该文章:
接口与抽象类的知识点总结(JDK1.7与1.8前后对比)
synchronized修饰符:
- synchronized 关键字声明的方法同一时间只能被一个线程访问。
- synchronized 修饰符可以应用于四个访问修饰符。
通过代码演示如下(示例):
//定义方法 a()
public synchronized void a(){
.......
}
volatile修饰符:
volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
通过代码演示如下(示例):
public class MyRunnable implements Runnable
{
//使用 volatile 修饰成员变量
private volatile boolean active;
public void run()
{
active = true;
while (active) // 第一行
{
// 代码
}
}
public void stop()
{
active = false; // 第二行
}
}
通常情况下,在一个线程调用 run() 方法(在 Runnable 开启的线程),在另一个线程调用 stop() 方法。 如果 第一行 中缓冲区的 active 值被使用,那么在 第二行 的 active 值为 false 时循环不会停止。
但是以上代码中我们使用了 volatile 修饰 active,所以该循环会停止。
volatile 修饰符具体的使用场景可以参考该文章:
volatile的使用及其原理
transient修饰符:
- 序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。
- 该修饰符包含在定义变量的语句中,用来预处理类和变量的数据类型。
通过代码演示如下(示例):
public transient int a = 10; // 不会持久化
public int b; // 持久化
transient修饰符具体的应用可以参考该文章:
TRANSIENT修饰符的使用
运算符
Java语言中提供了一套丰富的运算符来操纵变量,我们可以把运算符分成以下几组:
- 算术运算符
- 关系运算符
- 位运算符
- 逻辑运算符
- 赋值运算符
- 其他运算符
算术运算符:
算术运算符的作用和在数学中的作用是一样的,下表列出了所有的算术运算符:
操作符 | 描述 | 例子 |
---|---|---|
+ | 加法 - 相加运算符两侧的值 | 10 + 20 = 30 |
- | 减法 - 左操作数减去右操作数 | 10 - 20 = -10 |
* | 乘法 - 相乘操作符两侧的值 | 10 * 20 = 200 |
/ | 除法 - 左操作数除以右操作数 | 20 / 10 = 2 |
% | 取余 - 左操作数除以右操作数的余数 | 20 % 10 =0 |
++ | 自增:操作数的值增加1 | 分 前置 ++ 与 后置 ++ (区别详见下文) |
-- | 自减:操作数的值减少1 | 分 前置 -- 与 后置 -- (区别详见下文) |
自增(++
)自减(--
)运算符是一种特殊的算术运算符,在算术运算符中需要两个操作数来进行运算,而自增自减运算符是一个操作数。
通过代码演示如下(示例):
public class Test {
public static void main(String[] args) {
int a = 10;
int b = 20;
System.out.println("a + b = " + (a + b) );//结果: a + b = 30
System.out.println("a - b = " + (a - b) );//结果: a - b = -10
System.out.println("a * b = " + (a * b) );//结果: a * b = 200
System.out.println("b / a = " + (b / a) );//结果: b / a = 2
System.out.println("b % a = " + (b % a) );//结果: b % a = 0
//前置++: 先自身加1,然后再做其他运算
int x = 10;
int y = ++x;
System.out.println("x = " + x);// x = 11
System.out.println("y = " + y);// y = 11
//后置++: 先做其他运算,然后再自身加1
x = 10;
y = x++;
System.out.println("x = " + x);// x = 11
System.out.println("y = " + y);// y = 10
//前置--: 先自身减1,然后再做其他运算
x = 10;
y = --x;
System.out.println("x = " + x);// x = 9
System.out.println("y = " + y);// y = 9
//后置--: 先做其他运算,然后再自身减1
x = 10;
y = x--;
System.out.println("x = " + x);// x = 9
System.out.println("y = " + y);// y = 10
}
}
总结:自增与自减的运算原理都一致,主要区别在于 前置 与 后置。
- 前置:先自身加1或减1,然后再做其他运算。
- 后置:先做其他运算,然后再自身加1或减1。
关系运算符:
下表为Java支持的关系运算符:
运算符 | 描述 | 例子 |
---|---|---|
== | 检查如果两个操作数的值是否相等,如果相等则条件为真 | 10 == 20 为假 |
!= | 检查如果两个操作数的值是否相等,如果值不相等则条件为真 | 10 != 20 为真 |
> | 检查左操作数的值是否大于右操作数的值,如果是那么条件为真 | 10 > 20 = 为假 |
< | 检查左操作数的值是否小于右操作数的值,如果是那么条件为真 | 10 < 20 = 为真 |
>= | 检查左操作数的值是否大于或等于右操作数的值,如果是那么条件为真 | 10 >= 20 为假 |
<= | 检查左操作数的值是否小于或等于右操作数的值,如果是那么条件为真 | 10 <= 20 为真 |
通过代码演示如下(示例):
public class Test {
public static void main(String[] args) {
System.out.println("10 == 20 : " + (10 == 20));//结果: false
System.out.println("10 != 20 : " + (10 != 20));//结果: true
System.out.println("10 > 20 : " + (10 > 20));//结果: false
System.out.println("10 < 20 : " + (10 < 20));//结果: true
System.out.println("20 >= 10 : " + (20 >= 10));//结果: true
System.out.println("20 <= 10 : " + (20 <= 10));//结果: false
}
}
位运算符:
Java定义了位运算符,应用于整数类型(int),长整型(long),短整型(short),字符型(char)和字节型(byte)等类型。
位运算符作用在所有的位上,并且按位运算。
下表列出了位运算符的基本运算:
假设整数变量A的值为60和变量B的值为13:
A = 0011 1100
B = 0000 1101
操作符 | 描述 | 例子 |
---|---|---|
& | 按位与运算符:如果相对应位都是1,则结果为1,否则为0 | (A&B),得到12,即0000 1100 |
| | 按位或运算符:如果相对应位都是 0,则结果为 0,否则为 1 | (A | B)得到61,即 0011 1101 |
^ | 按位异或运算符:如果相对应位值相同,则结果为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 |
通过代码演示如下(示例):
public class Test {
public static void main(String[] args) {
int a = 60; /* 60 = 0011 1100 */
int b = 13; /* 13 = 0000 1101 */
int c = 0;
c = a & b; /* 12 = 0000 1100 */
System.out.println("a & b = " + c );//结果: a & b = 12
c = a | b; /* 61 = 0011 1101 */
System.out.println("a | b = " + c );//结果: a | b = 61
c = a ^ b; /* 49 = 0011 0001 */
System.out.println("a ^ b = " + c );//结果: a ^ b = 49
c = ~a; /*-61 = 1100 0011 */
System.out.println("~a = " + c );//结果: ~a = -61
c = a << 2; /* 240 = 1111 0000 */
System.out.println("a << 2 = " + c );//结果: a << 2 = 240
c = a >> 2; /* 15 = 1111 */
System.out.println("a >> 2 = " + c );//结果: a >> 2 = 15
c = a >>> 2; /* 15 = 0000 1111 */
System.out.println("a >>> 2 = " + c );//结果: a >>> 2 = 15
}
}
逻辑运算符:
下表列出了逻辑运算符的基本运算,假设布尔变量A为 true,变量B为 false。
操作符 | 描述 | 例子 |
---|---|---|
& | 逻辑与运算符:如果二个条件都为true,则结果为true,否则为false | (A & B)为 false |
| | 逻辑或运算符:如果有一个条件为true,则结果为true,否则为false | (A | B)为 true |
^ | 逻辑异或运算符:如果二个条件的值不同时,则结果为true,否则为false | (A ^ B)为 true |
! | 逻辑非运算符:对结果(boolean)类型的值进行取反 | !(A & B)为 true |
&& | 短路与运算符:类似于逻辑与& ,都表示并且,当使用短路与&& 时,并且第一个条件为false时,则结果直接为false | (A && B)为 false |
|| | 短路或操作符:类似于逻辑或| ,都表示或者,当使用短路或|| 时,并且第一个条件为true时,则结果直接为true | (A|| B)为 true |
通过代码演示如下(示例):
public class Test {
public static void main(String[] args) {
boolean a = true;
boolean b = false;
System.out.println("a & b = " + (a&b));//结果: fasle
System.out.println("a | b = " + (a|b));//结果: true
System.out.println("a ^ b = " + (a^b));//结果: true
System.out.println("!(a & b) = " + !(a & b));//结果: true
System.out.println("a && b = " + (a&&b));//结果: fasle
System.out.println("a || b = " + (a||b) );//结果: true
}
}
建议:使用短路与&&和短路或||,执行速度可能更快,效率更高。
赋值运算符:
下面是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 |
>>= | 右移位赋值运算符 | C >> = 2等价于C = C >> 2 |
通过代码演示如下(示例):
public class Test {
public static void main(String[] args) {
int a = 15;
int b = 20;
int c = 30;
System.out.println(c += a); // 30 += 15 结果:45
c = 30;
System.out.println(c -= a); // 30 -= 15 结果:15
c = 30;
System.out.println(c *= a); // 30 *= 15 结果:450
c = 30;
System.out.println(c /= a); // 30 /= 15 结果:2
c = 30;
System.out.println(c %= a); // 30 %= 15 结果:0
c = 30;
System.out.println(c &= a); // 30 &= 15 结果:14
c = 30;
System.out.println(c |= a); // 30 |= 15 结果:31
c = 30;
System.out.println(c ^= a); // 30 ^= 15 结果:17
c = 30;
System.out.println(c <<= 2); // 30 <<= 2 结果:120
c = 30;
System.out.println(c >>= 2); // 30 >>= 2 结果:7
}
}
条件运算符:
条件运算符也被称为三目运算符。
语法格式:表达式 ?代码1 : 代码2
当表达式里的结果为true(真)时,执行代码1,否则执行代码2。
当使用三目运算符应用赋值时,根据判断布尔表达式的值来决定哪个值应该赋值给变量。
通过代码演示如下(示例):
public class Test {
public static void main(String[] args){
//三目运算符
System.out.println(2 > 1 ? "a" : "b");//结果: a
System.out.println(2 < 1 ? "a" : "b");//结果: b
//三目运算符赋值
int a = 10;
int b;
// 如果表达式(a == 1)为真,则赋值 b = 20,否则 b = 30
b = (a == 1) ? 20 : 30;
System.out.println( "b = " + b );//结果: b = 30
b = (a == 10) ? 20 : 30;
System.out.println( "b = " + b );//结果: b = 20
//三目运算符的嵌套(当有三种或三种以上情况时,可以使用三目运算符嵌套完成)
System.out.println(2 < 1 ? "a" : 2 < 2 ? "b" : "c");//结果: c
}
}
instanceof 运算符:
该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)。
语法格式:
( Object reference variable ) instanceof (class/interface type)
通过代码演示如下(示例):
public class Dog extends Animal {
public static void main(String[] args){
//如果运算符左侧变量所指的对象,是操作符右侧类或接口(class/interface)的一个对象,那么结果为真。
String name = "James";
boolean result = name instanceof String;
System.out.println(result); // 结果: true ,由于 name 是 String 类型,所以返回真
//如果被比较的对象兼容于右侧类型,该运算符仍然返回 true。
Animal a = new Dog();
result = a instanceof Dog;
System.out.println(result); // 结果: true
}
}
class Animal {
}
Java运算符优先级:
当一个表达式中出现多个运算符时会涉及到运算符的优先级别的问题,运算符优先级不同会导致最后得出的结果差别甚大。
下表是Java运算符优先级的排序(从上至下,优先级依次递减):
类别 | 操作符 | 关联性 |
---|---|---|
后缀 | () 、[] 、. (点操作符) | 左到右 |
一元 | expr++ 、expr-- | 从左到右 |
一元 | ++ expr、-- expr、+ 、- 、~ 、! | 从右到左 |
乘性 | * 、/ 、% | 左到右 |
加性 | + 、- | 左到右 |
移位 | >> 、>>> 、<< | 左到右 |
关系 | > 、>= 、< 、<= | 左到右 |
相等 | == 、!= | 左到右 |
按位与 | & | 左到右 |
按位异或 | ^ | 左到右 |
按位或 | | | 左到右 |
逻辑与 | && | 左到右 |
逻辑或 | || | 左到右 |
条件 | ? : | 从右到左 |
赋值 | = 、+= 、-= 、*= 、/= 、%= 、>>= 、<<= 、&= 、^= 、|= | 从右到左 |
逗号 | , | 左到右 |
条件语句
1. if 选择结构
当布尔表达式结果为true(真)时,则执行 if 语句中的代码块,否则执行 if 语句块后面的代码。
//if 语句 语法格式:
if(布尔表达式){
//如果布尔表达式为true,则执行该 代码块
代码块
}
通过代码演示如下(示例):
public class Test {
public static void main(String args[]){
int i = 1;
//if 选择结构
if( i == 1 ){
System.out.print("如果变量 i == 1 为true,则输出该条语句,否则执行 if 语句块后面的代码");
}
}
}
注意: 在Java中,如果代码块中只有一条语句时,则大括号是可以省略的;
2. if … else选择结构
当 if 语句中的布尔表达式结果为true(真)时,则执行 if 语句中的代码块1,否则执行 else 语句中的代码块。
//if ... else 语法格式:
if(布尔表达式){
代码块
}else{
代码块
}
通过代码演示如下(示例):
public class Test {
public static void main(String args[]) {
int i = 1;
//if...else 选择结构
if (i == 1) {
System.out.print("如果变量 i == 1 为true,则输出该条语句");
} else {
System.out.println("如果变量 i == 1 为false,则输出该条语句");
}
}
}
注意:else不能单独使用,必须结合if使用
3. 多重if选择结构
当有两种或两种以上情况时,则可以使用多重if选择结构完成
//多重if 语法格式:
//布尔表达式1->布尔表达式3从上至下进行执行,如果结果为true则执行相应的代码块。如果结果都为false,则执行 else 语句中的代码块。
if(布尔表达式1){
代码块1
}else if(布尔表达式2){
代码块2
}else if(布尔表达式3){
代码块3
}else{
代码块4
}
通过代码演示如下(示例):
public class Test {
public static void main(String args[]){
int x = 3;
//多重if 语句
if( x == 1 ){
System.out.print("x = 1");
}else if( x == 2 ){
System.out.print("x = 2");
}else if( x == 3 ){
System.out.print("x = 3");//结果: x = 3
}else{
System.out.print("else 语句,如果上面的结果都为false,则执行该语句");
}
}
}
注意:
- 多重 if 选择结构中的 else if {} 可以编写任意多个,但必须在 else 语句之前。
- if 语句至多有1个 else 语句,else 语句在所有的 else if 语句之后。
- 一旦其中一个 else if 语句检测为 true,其他的 else if 以及 else 语句都将跳过执行。
4. if 选择结构的嵌套
在一个 if 或者 else if 语句中使用 if 或者 else if 语句。
//if 嵌套 语法格式:
if(){
if(){
}else if(){
}
}else{
if(){
}else{
}
}
通过代码演示如下(示例):
public class Test {
public static void main(String args[]) {
char sex = '男';
String hero = "赵云";
//if 嵌套
if (sex == '女') {
if (hero.equals("貂蝉")) {
System.out.println("性别: 女 , 名字: 貂蝉");
} else if (hero.equals("小乔")) {
System.out.println("性别: 女 , 名字: 小乔");
}
} else if (sex == '男') {
if (hero.equals("吕布")) {
System.out.println("性别: 男 , 名字: 吕布");
} else if (hero.equals("赵云")) {
System.out.println("性别: 男 , 名字: 赵云");
}
}
//编译运行结果: 性别: 男 , 名字: 赵云
}
}
5. switch case
当做等值操作时,可以使用switch case完成。
//switch case 语法格式:
switch(expression){
case value:
…..
break;//可选
case value:
…..
break;//可选
//可以编写任意数量的 case 语句
default: //可选
……
}
switch case 语句规则:
- switch语句中的变量类型可以是:byte、short、int 或者 char。从 Java SE 7 开始,switch 支持字符串 String 类型了,同时 case 标签必须为字符串常量或字面量。
- switch 语句可以编写任意数量的 case 语句。
- case 语句中值的数据类型必须与变量的数据类型相同,而且只能是常量或者字面量。
- 当变量的值与 case 语句的值匹配时,则执行该 case 语句中的语句,直到遇到 break 语句才会跳出 switch 语句。如若没有 break 语句出现,程序会继续执行下一条 case 语句,直到出现 break 语句。
- switch 语句可以包含一个 default 语句,该语句一般是 switch 语句的最后一个(可以在任何位置,但建议在最后一个)。default 在没有 case 语句的值和变量值匹配时执行。default 语句不需要 break 语句。
通过代码演示如下(示例):
public class Test {
public static void main(String args[]) {
String hero = "赵云";
//switch case
switch (hero){
case "貂蝉":
System.out.println("貂蝉");
break;
case "小乔":
System.out.println("小乔");
break;
case "吕布":
System.out.println("吕布");
break;
case "赵云":
System.out.println("赵云");
break;
default:
System.out.println("请选择你的英雄!");
}
System.out.println("你的英雄为:" + hero);
/*
编译运行结果:
赵云
你的英雄为:赵云
*/
}
}
switch case 执行时,一定会先进行匹配,匹配成功则会执行该 case 中的语句,再根据是否有break,判断是否继续向下执行,或是跳出判断。
当 case 语句块中没有编写 break 语句时(默认 default 编写在最后一个的情况下):
如果匹配不成功,则返回默认 default 的值,JVM 并不会按顺序输出每一个 case 对应的返回值。
通过代码演示如下(示例):
public class Test {
public static void main(String args[]) {
String hero = "刘备";
switch (hero){
case "貂蝉":
System.out.println("貂蝉");
case "赵云":
System.out.println("赵云");
case "小乔":
System.out.println("小乔");
case "吕布":
System.out.println("吕布");
default:
System.out.println("请选择你的英雄!");
}
/*
编译运行结果:
请选择你的英雄!
*/
}
}
如果匹配成功,并且后续所有 case 语句块中都没有编写 break 语句,那么程序会从当前 case 开始,将后续所有 case 的值都输出(包括 default 中的值)。
通过代码演示如下(示例):
public class Test {
public static void main(String args[]) {
String hero = "赵云";
switch (hero){
case "貂蝉":
System.out.println("貂蝉");
case "赵云":
System.out.println("赵云");
case "小乔":
System.out.println("小乔");
case "吕布":
System.out.println("吕布");
default:
System.out.println("请选择你的英雄!");
}
/*
编译运行结果:
赵云
小乔
吕布
请选择你的英雄!
*/
}
}
如果匹配成功,但后续的 case 语句块中编写了 break 语句,那么程序会从当前 case 开始输出直到遇到 break 语句后跳出判断。
通过代码演示如下(示例):
public class Test {
public static void main(String args[]) {
String hero = "赵云";
switch (hero){
case "貂蝉":
System.out.println("貂蝉");
case "赵云":
System.out.println("赵云");
case "小乔":
System.out.println("小乔");
case "吕布":
System.out.println("吕布");
break;
default:
System.out.println("请选择你的英雄!");
}
/*
编译运行结果:
赵云
小乔
吕布
*/
}
}
循环结构
顺序结构的程序语句只能被执行一次,如果需要做重复事情时,可以使用循环结构完成。
Java中有三种主要的循环结构:
- while 循环
- do…while 循环
- for 循环
- 增强 for 循环
while 循环:
while 是最基本循环,是没有固定循环次数的(只要布尔表达式为 true,循环就会一直执行下去)。
//while 循环 语法格式:
while (布尔表达式) {
循环内容
}
通过代码演示如下(示例):
public class Test {
public static void main(String args[]) {
int i = 1;
//通过 while 循环 输出5次 Hello World!
while( i < 6 ) {
System.out.println("Hello World! -- " + i);
i++;
}
/*
编译运行结果:
Hello World! -- 1
Hello World! -- 2
Hello World! -- 3
Hello World! -- 4
Hello World! -- 5
*/
}
}
do…while 循环:
do…while 循环也是没有固定循环次数的(只要布尔表达式为 true,循环就会一直执行下去),但与 while 循环不同的地方在于:do…while 循环是先执行循环内容,再通过布尔表达式的值判断是否执行下一次循环。
使用do…while 循环时,即使不满足条件,也至少会执行一次。
//do...while 循环 语法格式:
do {
循环内容
} while (布尔表达式);
通过代码演示如下(示例):
public class Test {
public static void main(String args[]) {
int i = 1;
//通过 do...while 循环 输出5次 Hello World!
do {
System.out.print("Hello World! -- " + i);
i++;
System.out.println("\t当前 i = " + i);
} while (i < 6);
/*
编译运行结果:
Hello World! -- 1 当前 i = 2
Hello World! -- 2 当前 i = 3
Hello World! -- 3 当前 i = 4
Hello World! -- 4 当前 i = 5
Hello World! -- 5 当前 i = 6
*/
}
}
for 循环:
for 循环的循环次数是在执行前就确定的。
//for 循环 语法格式:
for (初始化;布尔表达式;更新循环控制变量) {
循环内容
}
for 循环执行流程:
- 完成变量的初始化。
- 检测布尔表达式的值,如果为 true,执行循环体。如果为 false,结束当前整个循环,开始执行循环体后面的语句。
- 执行一次循环,然后更新循环控制变量。
- 再次判断布尔表达式的值,循环执行上面的过程(从步骤2开始)。
通过代码演示如下(示例):
public class Test {
public static void main(String args[]) {
//通过for 循环输出 5次Hello World
for (int i = 1; i < 6; i++) {
System.out.print("第" + i + "次循环\t");
System.out.println("Hello World! -- " + i);
}
/*
编程运行结果:
第1次循环 Hello World! -- 1
第2次循环 Hello World! -- 2
第3次循环 Hello World! -- 3
第4次循环 Hello World! -- 4
第5次循环 Hello World! -- 5
*/
}
}
增强 for 循环:
一种主要用于数组的增强型 for 循环。
//增强 for 循环 语法格式:
for (声明语句 : 表达式) {
循环内容
}
声明语句:声明一个新的局部变量,该变量的数据类型必须和数组元素的数据类型匹配。
表达式:要访问的数组名,或者是一个返回值为数组的方法。
通过代码演示如下(示例):
public class Test {
public static void main(String args[]){
int [] numbers = {11, 22, 33, 44, 55};
String [] names ={"张三", "李四", "王五", "赵六"};
for(int x : numbers ){
System.out.print( x );
System.out.print(",");
}
System.out.println();
for( String name : names ) {
System.out.print( name );
System.out.print(",");
}
/*
编译运行结果:
11,22,33,44,55,
张三,李四,王五,赵六,
*/
}
}
break、continue关键字区别
break关键字:
- 表示中断
- 使用场景:循环语句 或 switch 语句
- 作用:当遇到 break 语句,则结束当前整个语句块,程序将继续执行循环下面的语句。
通过代码演示如下(示例):
public class Test {
public static void main(String args[]) {
int [] numbers = {10, 20, 30, 40, 50};
for(int x : numbers ) {
//当 x 等于 30 时跳出循环
if( x == 30 ) {
break;
}
System.out.print( x );
System.out.print("\n");
}
/*
编译运行结果:
10
20
*/
}
}
continue关键字:
- 表示继续
- 使用场景:适用于任何循环控制结构
- 作用:当遇到 continue 语句,则结束本次循环,程序立刻跳到下一次循环的迭代。
通过代码演示如下(示例):
public class Test {
public static void main(String args[]) {
int [] numbers = {10, 20, 30, 40, 50};
for(int x : numbers ) {
//当 x 等于 30 时结束本次循环,程序立刻跳到下一次循环的迭代。
if( x == 30 ) {
continue;
}
System.out.print( x );
System.out.print("\n");
}
/*
编译运行结果:
10
20
40
50
*/
}
}
常用类
Scanner 类
通过 Scanner 类,我们可以获取用户的键盘输入。
//创建 Scannery 对象的基本语法:
Scanner scanner = new Scanner(System.in);//从键盘接收数据
Scanner 类常用方法:next() - 可以通过使用各种next方法,将得到的标记转换成不同类型的值。
修改器和类型 | 方法 | 说明 |
---|---|---|
byte | nextByte() | 将输入信息的下一个标记扫描为一个 byte |
byte | nextByte(int radix) | 将输入信息的下一个标记扫描为一个 byte |
short | nextShort() | 将输入信息的下一个标记扫描为一个 short |
short | nextShort(int radix) | 将输入信息的下一个标记扫描为一个 short |
int | nextInt() | 将输入信息的下一个标记扫描为一个 int |
int | nextInt(int radix) | 将输入信息的下一个标记扫描为一个 int |
long | nextLong() | 将输入信息的下一个标记扫描为一个 long |
long | nextLong(int radix) | 将输入信息的下一个标记扫描为一个 long |
float | nextFloat() | 将输入信息的下一个标记扫描为一个 float |
double | nextDouble() | 将输入信息的下一个标记扫描为一个 double |
boolean | nextBoolean() | 扫描解释为一个布尔值的输入标记并返回该值 |
String | next() | 查找并返回来自此扫描器的下一个完整标记 |
String | nextLine() | 此扫描器执行当前行,并返回跳过的输入信息 |
String | next(Pattern pattern) | 如果下一个标记与指定模式匹配,则返回下一个标记 |
String | next(String pattern) | 如果下一个标记与从指定字符串构造的模式匹配,则返回下一个标记 |
next() 与 nextLine() 区别:
next():
- 一定要读取到有效字符后才可以结束输入。
- 对输入有效字符之前遇到的空白,next() 方法会自动将其去掉。
- 只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。
- next() 不能得到带有空格的字符串。
nextLine():
- 以Enter为结束符,也就是说 nextLine() 方法返回的是输入回车之前的所有字符。
- 可以获得空白。
通过代码演示 next 方法,如下(示例):
import java.util.Scanner; //第一步:导入包
public class nextTest {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);//第二步:创建 Scanner 对象
System.out.println("next方式接收(中间不能加空格或符号):");//第三步:友好提示
if (scan.hasNext()) { //判断是否还有输入
String str1 = scan.next();
System.out.println("输入的数据为:" + str1);
}
/*
编译运行结果:
next方式接收(中间不能加空格或符号):
我大意了啊 没有闪~!@#$%^&*()
输入的数据为:我大意了啊
*/
}
}
通过代码演示 nextLine 方法,如下(示例):
import java.util.Scanner; //第一步:导入包
public class nextLineTest {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);//第二步:创建 Scanner 对象
System.out.println("nextLine方式接收(中间能加空格或符号):");//第三步:友好提示
if (scan.hasNextLine()) { //判断是否还有输入
String str2 = scan.nextLine();//第四步:接收键盘输入
System.out.println("输入的数据为:" + str2);
}
/*
编译运行结果:
next方式接收(中间不能加空格或符号):
我大意了啊 没有闪~!@#$%^&*()
输入的数据为:我大意了啊 没有闪~!@#$%^&*()
*/
}
}
Scanner 类常用方法:hasNext() - 可以通过使用各种hasNext方法,判断是否还有输入的数据。
修改器和类型 | 方法 | 说明 |
---|---|---|
boolean | hasNextByte() | 如果通过使用 nextByte() 方法,此扫描器输入信息中的下一个标记可以解释为默认基数中的一个字节值,则返回 true |
boolean | hasNextByte(int radix) | 如果通过使用 nextByte() 方法,此扫描器输入信息中的下一个标记可以解释为指定基数中的一个字节值,则返回 true |
boolean | hasNextShort() | 如果通过使用 nextShort() 方法,此扫描器输入信息中的下一个标记可以解释为默认基数中的一个 short 值,则返回 true |
boolean | hasNextShort(int radix) | 如果通过使用 nextShort() 方法,此扫描器输入信息中的下一个标记可以解释为指定基数中的一个 short 值,则返回 true |
boolean | hasNextInt() | 如果通过使用 nextInt() 方法,此扫描器输入信息中的下一个标记可以解释为默认基数中的一个 int 值,则返回 true |
boolean | hasNextInt(int radix) | 如果通过使用 nextInt() 方法,此扫描器输入信息中的下一个标记可以解释为指定基数中的一个 int 值,则返回 true |
boolean | hasNextLong() | 如果通过使用 nextLong() 方法,此扫描器输入信息中的下一个标记可以解释为默认基数中的一个 long 值,则返回 true |
boolean | hasNextLong(int radix) | 如果通过使用 nextLong() 方法,此扫描器输入信息中的下一个标记可以解释为指定基数中的一个 long 值,则返回 true |
boolean | hasNextFloat() | 如果通过使用 nextFloat() 方法,此扫描器输入信息中的下一个标记可以解释为默认基数中的一个 float 值,则返回 true |
boolean | hasNextDouble() | 如果通过使用 nextDouble() 方法,此扫描器输入信息中的下一个标记可以解释为默认基数中的一个 double 值,则返回 true |
boolean | hasNextBoolean() | 如果通过使用一个从字符串 "true |
boolean | hasNext() | 如果此扫描器的输入中有另一个标记,则返回 true |
boolean | hasNextLine() | 如果在此扫描器的输入中存在另一行,则返回 true |
boolean | hasNext(Pattern pattern) | 如果下一个完整标记与指定模式匹配,则返回 true |
boolean | hasNext(String pattern) | 如果下一个标记与从指定字符串构造的模式匹配,则返回 true |
通过代码演示如下(示例):
import java.util.Scanner; //第一步:导入包
public class Test {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);//第二步:创建 Scanner 对象
int i = 0;
float f = 0.0f;
System.out.print("输入整数:");//第三步:友好提示
if (scan.hasNextInt()) { //判断输入的是否是整数
i = scan.nextInt();//第四步:接收键盘输入
System.out.println("整数数据:" + i);
} else {//提示输入错误的信息
System.out.println("输入的不是整数!");
}
System.out.print("输入小数:");//第三步:友好提示
if (scan.hasNextFloat()) {// 判断输入的是否是小数
f = scan.nextFloat();//第四步:接收键盘输入
System.out.println("小数数据:" + f);
} else {//提示输入错误的信息
System.out.println("输入的不是小数!");
}
/*
编译运行结果:
输入整数:123
整数数据:123
输入小数:12.3
小数数据:12.3
*/
}
}
String 类
字符串就是一串字符序列。Java提供了 String,StringBuffer,StringBuilder 等类来封装字符串,并提供了一系列方法来操作字符串。
String:
String 字符串是不可变的,即一旦创建 String 对象后,包含在这个对象中的字符序列是不可改变的,直到这个对象被销毁。
1、创建 String 对象
可以直接给 String 类型的变量赋值字符串字面量,也可以通过 String 构造方法创建 String 对象。String 类中有很多构造方法,这些方法提供不同的参数来初始化字符串。
String 类的常用构造方法:
方法 | 说明 |
---|---|
String() | 无参构造,创建一个空串 |
String(byte[] bytes) | 以当前默认编码把指定的字节数组转换为字符串 |
String(byte[] bytes, Charset charset) | 把bytes字节数组以指定的编码charset转换为字符串 |
String(byte[] bytes, int offset, int length) | 把bytes字节数组中从offset开始的length个字节转换为字符串 |
String(byte[] bytes, String charsetName) | 把bytes字节数组以指定的编码charsetName转换为字符串 |
String(char[] value) | 把字符数组value中的所有字符都转换为字符串 |
String(char[] value, int offset, int count) | 把字符数组value中从offset开始的count个字符转换为字符串 |
String(String original) | 根据现在的original字符串创建一个新的字符串 |
String(StringBuffer buffer) | 根据StringBuffer对象创建String字符串对象 |
String(StringBuilder builder) | 根据StringBuilder 对象创建String字符串对象 |
通过代码演示创建 String 对象的几种常见方式(示例):
public class Test {
public static void main(String[] args) {
//1)直接赋值字符串字面量
String s1 = "hello";
//2)通过无参构造创建 String 对象, 相当于""空串
String s2 = new String(); //new运算符创建一个对象 ,把对象的引用赋值给s2
System.out.println( s2 == null ); //结果:false
System.out.println( s2.length() ); //结果:0, 字符串有length()方法返回字符串中字符的数量
//3)String(byte[] ), 根据字节数组创建 String 对象
byte [] bytes = { 65,66,67,68,69,70 };
String s3 = new String(bytes); //把bytes数组中所有的 字节 转换为 字符串
System.out.println( s3 ); //结果:ABCDEF
s3 = new String(bytes, 0 , 4 ); //把bytes数组中从0开始的4个 字节 转换为 字符串
System.out.println( s3 ); //结果:ABCD
//4)String( char [] ) 根据字符数组转换为字符串
char [] contents = { 'A', 'b' , '5' , '汉', 100, 30066 };
String s4 = new String(contents); //把字符数组中所有的 字符 都转换为 字符串
System.out.println( s4 ); //结果:Ab5汉d畲
s4 = new String(contents, 0 , 5); //把字符数组中从0开始的5个 字符 转换为 字符串
System.out.println( s4 ); //结果:Ab5汉d
//注意, 不管是数组,还是当前 String,只要涉及索引值,都要注意索引值不能越界
// s4 = new String(contents, 5 , 5 ); //StringIndexOutOfBoundsException
//5)String(String)
String s5 = new String(s1);
System.out.println( s5 ); //结果:hello
System.out.println( s5 == s1 ); //结果:false
}
}
String 类常用方法:
返回值 | 方法 | 说明 |
---|---|---|
char | charAt(int index) | 返回当前字符串中index位置的字符 |
int | compareTo(String anotherString) | 比较当前字符串与参数字符串的大小. 如果第一个字符串大返回正数, 参数字符串大返回负数,一样返回0 |
int | compareToIgnoreCase(String str) | 忽略大小写后再比较大小. |
String | concat(String str) | 在当前字符串 后面连接str字符串,返回连接之后 的新串. |
boolean | contains(CharSequence s) | 判断当前字符串中是否包含s子串 |
boolean | endsWith(String suffix) | 判断当前字符串是否以suffxi结尾 |
boolean | equals(Object anObject) | 比较两个字符串是否一样 |
boolean | equalsIgnoreCase(String anotherString) | 忽略大小写后再判断是否一样 |
byte[] | getBytes() | 返回字符串在默认编码下对应的字节数组 |
byte[] | getBytes(String charsetName) | 返回字符串在指定的charsetName编码下对应的字节数组 |
void | getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) | 把当前字符串[ srcBegin, srcEnd ) 范围内的字符复制到dst数组中从dstBegin开始的位置 |
int | hashCode() | 返回此字符串的哈希码 |
int | indexOf(int ch) | 返回字符ch在当前字符串中第一次出现的索引值 |
int | indexOf(int ch, int fromIndex) | 返回字符ch在当前字符串中第一次出现的索引值,从指定的索引开始搜索。 |
int | indexOf(String str) | 返回字符串str在当前字符串中第一次出现的索引值 |
int | indexOf(String str, int fromIndex) | 返回字符串str在当前字符串中第一次出现的索引值,从指定的索引开始搜索。 |
boolean | isEmpty() | 判断字符串是否为空串 |
int | lastIndexOf(int ch) | 返回字符ch在当前字符串中最后一次出现的索引值 |
int | lastIndexOf(int ch, int fromIndex) | 返回字符ch在当前字符串中最后一次出现的索引值,从指定的索引开始搜索。 |
int | lastIndexOf(String str) | 返回字符串str在当前字符串中最后一次出现的索引值 |
int | lastIndexOf(String str, int fromIndex) | 返回字符串str在当前字符串中最后一次出现的索引值,从指定的索引开始搜索。 |
int | length() | 返回字符串中字符的个数 |
boolean | matches(String regex) | 判断当前字符串是否匹配指定的regex正则表达式 |
String | replaceAll(String regex, String replacement) | 把当前字符串中符合regex正则表达式的字符串使用replacement替换,返回替换后的新串 |
String | replaceFirst(String regex, String replacement) | 把当前字符串中第一个符合regex正则表达式的字符串使用replacement替换,返回替换后的新串 |
String[] | split(String regex) | 使用regex正则表达式分割当前字符串.返回分割后子串组成的数组 |
boolean | startsWith(String prefix) | 判断当前字符串是否以prefix开始 |
String | substring(int beginIndex) | 返回当前字符串从beginIndex开始的子串 |
String | substring(int beginIndex, int endIndex) | 返回当前字符串[beginIndex, endIndex ) 范围内的子串 |
char[] | toCharArray() | 把字符串转换为字符数组 |
String | toLowerCase() | 把字符串中的大写字母转换为小写字母 |
String | toString() | 返回此序列中数据的字符串表示形式 |
String | toUpperCase() | 把字符串中的小写字母转换为大写字母 |
String | trim() | 去掉前后的空白字符 |
static String | valueOf(boolean b) | 把其他类型的数据转换为字符串 |
正则表达式常用的模式符:
模式符 | 说明 |
---|---|
[abc] | 匹配a,b,c三个字符中的一个 |
. | 任意字符 |
\d | 数字 |
\s | 空白符 |
\w | 单词字符[a-zA-Z0-9_] |
X? | 匹配X0次或1次 |
X* | 匹配X任意次 |
X+ | 匹配X至少1次 |
X{n} | 匹配X正好n次 |
X{n,} | 匹配X至少n次 |
X{n,m} | 匹配X至少n次,最多m次 |
开发中常用的正则表达式可以参考该文章:
Java正则表达式大全
通过代码演示 String 类中几种常用方法(示例):
public class Test {
public static void main(String[] args){
/*
示例 1: 遍历字符串的每个字符
方法:
length() : 返回字符串中字符的数量
charAt(index) : 返回字符串指定位置的字符
*/
String text = "爱吃香菜的胡先森";
for (int i = 0; i < text.length(); i++) {
System.out.print(text.charAt(i));
}
System.out.println(); //结果:爱吃香菜的胡先森
/*
示例 2: 比较字符串大小
方法:
compareTo(String) : 比较当前字符串与参数字符串的大小. 如果第一个字符串大返回正数, 参数字符串大返回负数,一样返回0
*/
System.out.println("hello".compareTo("hehe")); //结果:4
System.out.println("hello".compareTo("helloworld")); //结果:-5
System.out.println("张三".compareTo("李四")); //结果:-2094
System.out.println("lisi".compareTo("lisi")); //结果:0
System.out.println("hello".compareTo("HELLO")); //结果:32
//在比较字符串大小时,还可以忽略大小写,如:
System.out.println("hello".compareToIgnoreCase("HELLO")); //结果:0
/*
示例 3: 取子串,把一个路径中的文件夹,文件名与扩展名分别取出来
方法:
substring() : 方法可以从字符串中提取子串
substring( from ) : 返回当前字符串从from开始一直到最后的子串
substring( from, to) : 返回当前字符串[from, to) 范围内的子串
indexOf() : 返回字符串或字符在当前字符串中第一次出现的索引值
lastIndexOf() : 返回字符串或字符在当前字符串中最后一次出现的索引值
*/
String path = "D:\\course\\03-JavaSE\\code\\day06\\src\\string\\Test02.java";
//需要确定\\最后一次出现的索引值,
int lastSlash = path.lastIndexOf("\\");
//需要确定小点的位置
int dot = path.indexOf('.');
//调用方法取子串
String folder = path.substring(0, lastSlash); //文件夹
String filename = path.substring(lastSlash + 1, dot); //文件名
String suffix = path.substring(dot + 1); //扩展名
System.out.println(folder); //结果:D:\course\03-JavaSE\code\day06\src\string
System.out.println(filename); //结果:Test02
System.out.println(suffix); //结果:java
/*
示例 4: 字符串与字节数组之间的转换
方法:
getBytes() : 返回字符串在当前默认编码下对应的字节数组
getBytes(String charsetName) : 返回字符串在指定的charsetName编码下对应的字节数组
构造方法:
String(byte[] bytes) : 以当前默认编码把指定的字节数组转换为字符串
String(byte[] bytes, String charsetName) : 把bytes字节数组以指定的编码charsetName转换为字符串
*/
text = "爱吃香菜的胡先森";
//返回text字符串在默认编码下对应的字节数组(此处默认编码为UTF8编码,一个英文占1字节,一个汉字占3字节)
byte[] bytes = text.getBytes();
System.out.println(Arrays.toString(bytes));//结果:[-25, -120, -79, -27, -112, -125, -23, -90, -103, -24, -113, -100, -25, -102, -124, -24, -125, -95, -27, -123, -120, -26, -93, -82]
//调用构造方法,把字节数组中的字节转换为字符串
String str = new String(bytes);
System.out.println(str); //结果:爱吃香菜的胡先森
//返回text字符串在GBK编码下对应的字节数组(GBK编码中,一个英文字符占1字节, 一个汉字占2字节)
bytes = text.getBytes("GBK"); //调用getBytes(String)方法时,需要对getBytes()方法的受检异常进行预处理
System.out.println(Arrays.toString(bytes)); //结果:[-80, -82, -77, -44, -49, -29, -78, -53, -75, -60, -70, -6, -49, -56, -55, -83]
//调用构造方法,把bytes数组中的字节以GBK编码转换为字符串
str = new String(bytes, "GBK");
System.out.println(str); //结果:爱吃香菜的胡先森
/*
示例 5: 字符串与字符数组的相互转换
方法:
toCharArray() : 把当前字符串转换为字符数组
String.valueOf(char[]) : 把字符数组转换为字符串
构造方法:
String(char[] value) : 把字符数组value中的所有字符都转换为字符串
*/
//把字符串 text 转换为字符数组
char[] charArray = text.toCharArray();
System.out.println(Arrays.toString(charArray)); //结果:[爱, 吃, 香, 菜, 的, 胡, 先, 森]
// 1.调用构造方法,把字符数组转换为字符串
str = new String(charArray);
System.out.println(str); //结果:爱吃香菜的胡先森
// 2.调用方法,把字符数组转换为字符串
str = String.valueOf(charArray);
System.out.println(str); //结果:爱吃香菜的胡先森
/*
示例 6: 字符串与基本类型的相互转换
valueOf()方法的重载 : 把int, double, boolean,char,char[]等类型的数据转换为字符串
Integer.parseInt(text), Double.parseDouble(text)等方法 : 把数值字符串转换为int整数,double小数
*/
//把整数转换为字符串
int num = 456;
str = String.valueOf(num);
System.out.println(str); //结果:456
//把字符串转换为整数
str = "258";
num = Integer.parseInt(str);
System.out.println(num); //结果:258
/*
示例 7: 字符串替换与分隔
matches(String regex) : 判断当前字符串是否匹配指定的regex正则表达式
replaceAll(String regex, String replacement) : 把当前字符串中符合regex正则表达式的字符串使用replacement替换,返回替换后的新串
split(String regex) : 使用regex正则表达式分割当前字符串.返回分割后子串组成的数组
*/
String pattern = "\\w{6,20}@\\w{2,}\\.(com|cn|net)";
String email = "hehe@qq.com";
//判断字符串 email 是否匹配指定的regex正则表达式
System.out.println(email.matches(pattern)); //结果:false
//把当前字符串中的数字替换 为星号*:
text = "hello1234world";
text = text.replaceAll("\\d", "*");
System.out.println(text); //结果:hello****world
//把text字符串中的每个单词给拆分出来, 字符串中单词与单词使用空格或者逗号分隔
text = "A small step forward,A big step of civilization";
String[] words = text.split("[ ,]");
for (String str2 : words) {
System.out.print(str2 + "\t"); //结果:A small step forward A big step of civilization
}
}
}
2、字符串常量池:
在Java中所有使用双引号引起来的字符串字面量存储在字符串常量池中,采用享元模式。
通过代码演示如下(示例):
public class Test {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");
String s4 = new String("hello");
System.out.println(s1 == s2); //结果:true
System.out.println(s3 == s4); //结果:false
System.out.println(s3.equals(s4)); //结果:true
//javac编译器对字符串常量的连接会进行优化.如:
String s5 = "he" + "llo";
System.out.println(s1 == s5); //true, 即s1与s5引用了常量池中同一个常量
final String s6 = "he";
String s7 = s6 + "llo"; //s4是final常量,javac也会优化
System.out.println(s1 == s7); //true, 即s1与s7引用了常量池中同一个常量
}
}
分析:
- 在执行 String s1 = “hello”; 时,系统先检查字符串常量池中是否有”hello”字符串对象,如果没有则把"hello"字面量保存到字符串常量池中,把该常量的引用赋值给s1; 再执行 String s2 = “hello”; 时,系统直接把字符串常量池中"hello"的引用赋值给s2,即s1与s2都引用了字符串常量池中同一个常量。
==
在判断基本数据类型时,比较的是值,因为它们没有地址。而==
在比较对象时,比较的却是对象的地址。一般对于对象,比较它们的值是否相等时,可以使用 equals()。- 在执行 String s5 = “he” + “llo”; 时,javac编译器会把这个字符串常量的连接优化为 “hello”。
- s4 是final常量,javac也会优化。
3、String 对象是不可变的:
String 字符串是不可变的,一旦创建String对象后,包含在这个对象中的字符序列是不可改变的,直到这个对象被销毁。
String 的 concat(),toLowerCase(),trim() 等方法都是返回一个新的字符串对象,原来的字符串对象不变。
通过代码演示如下(示例):
String s1 = "hello";
s1 = "world";
是否改了String对象? 答案是没有。String s1 = “hello”; 这条语句中,String是类名,是一种引用数据类型。
s1是对象名其实就是一个变量名; "hello"才是String对象。我们说 String 对象是不可变的 ,是指不能把"hello"变为"world"。s1 = “world” 执行这条语句时,系统是在常量池中又添加了一个"world"常量,把该常量的引用赋值给s1。字符串常量池中的”hello”对象依然存在。
使用+进行字符串连接时,总是会生成一个新的字符串对象。
通过代码演示如下(示例):
String s2 = s1 + "world";
在Java底层,使用+进行字符串连接时,系统会创建一个 StringBuilder 对象,调用 StringBuilder 对象的 append() 方法进行字符串连接,连接完成后,根据 StringBuilder 对象的字符数组创建一个新的String对象返回。
通过代码演示如下(示例):
String str = "";
for( int i = 0 ; i < 10 ; i++ ){
str += i;
}
当
i==0
时, str= str + 0,会生成一个新的字符串对象"0",
当i==1
时,str= str + 1,会生成一个新的字符串对象"01",还需要释放原来"0";
当i==2
时,str= str + 2,会生成一个新的字符串对象"012",还需要释放原来"01"; 当i==3
时, str= str + 3,会生成一个新的字符串对象"0123", 还需要释放原来"012"。
在字符串连接时,会创建一个新的字符串对象,还需要系统释放原来的字符串对象, 增加了JVM的负担.
StringBuilder/StringBuffer
StringBuffer/StringBuilder 字符串对象是可变的,当创建 StringBuilder 对象后,通过 append(),reverse() 等方法可以改变这个字符串对象中的字符序列。最后可以调用 StringBuilder 对象的 toString() 方法转换为 String 对象。
StringBuilder 与 StringBuffer 的区别:
- StringBuilder 和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
- 如果应用程序不要求线程安全,多数情况下建议使用 StringBuilder 类,因为 StringBuilder 相对于 StringBuffer 有速度优势。
通过代码演示如下(示例):
public class Test {
public static void main(String[] args){
//使用 StringBuilder 拼接字符串(非线程安全的)
StringBuilder stringBuilder = new StringBuilder();
for( int i = 0 ; i<10; i++){
stringBuilder.append(i); //调用append()连接时,不会创建新的StringBuilder对象
}
//使用 StringBuffer 拼接字符串(线程安全)
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("爱吃");
stringBuffer.append("香菜的");
stringBuffer.append("胡先森");
System.out.println(stringBuilder); //结果:0123456789
System.out.println(stringBuffer); //结果:爱吃香菜的胡先森
}
}
StringBuffer 类的主要方法:
方法 | 说明 |
---|---|
append(String s) | 将指定的字符串追加到此字符序列 |
reverse() | 将此字符序列用其反转形式取代 |
delete(int start, int end) | 移除此序列的子字符串中的字符 |
insert(int offset, int i) | 将 int 参数的字符串表示形式插入此序列中 |
replace(int start, int end, String str) | 使用给定 String 中的字符替换此序列的子字符串中的字符 |
下面的列表里的方法和 String 类的方法类似:
返回值 | 方法 | 说明 |
---|---|---|
int | capacity() | 返回当前容量 |
char | charAt(int index) | 返回此序列中指定索引处的 char 值 |
void | ensureCapacity(int minimumCapacity) | 确保容量至少等于指定的最小值 |
void | getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) | 将字符从此序列复制到目标字符数组 dst |
int | indexOf(String str) | 返回第一次出现的指定子字符串在该字符串中的索引 |
int | indexOf(String str, int fromIndex) | 从指定的索引处开始,返回第一次出现的指定子字符串在该字符串中的索引 |
int | lastIndexOf(String str) | 返回最右边出现的指定子字符串在此字符串中的索引 |
int | lastIndexOf(String str, int fromIndex) | 返回 String 对象中子字符串最后出现的位置 |
int | length() | 返回长度(字符数) |
void | setCharAt(int index, char ch) | 将给定索引处的字符设置为 ch |
void | setLength(int newLength) | 设置字符序列的长度 |
CharSequence | subSequence(int start, int end) | 返回一个新的字符序列,该字符序列是此序列的子序列 |
String | substring(int start) | 返回一个新的 String,它包含此字符序列当前所包含的字符子序列 |
String | substring(int start, int end) | 返回一个新的 String,它包含此序列当前所包含的字符子序列 |
String | toString() | 返回此序列中数据的字符串表示形式 |
日期时间
Date:
java.util.Date 类中提供了与系统无关的用于处理日期与时间的封装,可以调用无参构造方法获得当前时间。
Date 类提供两个构造函数来实例化 Date 对象。
通过代码演示如下(示例):
public class Test {
public static void main(String[] args) {
//1. Date(),使用当前日期和时间来初始化对象
Date date1 = new Date();
System.out.println(date1); //结果:Thu Nov 26 16:12:18 CST 2020
//2. Date(long)创建对象,参数是从1970年1月1日开始的毫秒数
long millis = System.currentTimeMillis();//获得当前日期距离1970-1-1 00:00:00 经过的毫秒数
Date date2 = new Date( millis + 1000*60*5 );//根据毫秒数创建Date日期对象
System.out.println( date2 ); //结果:Thu Nov 26 16:17:18 CST 2020
}
}
Date 类的常用方法:
方法 | 说明 |
---|---|
boolean after(Date date) | 若当调用此方法的Date对象在指定日期之后返回true,否则返回false |
boolean before(Date date) | 若当调用此方法的Date对象在指定日期之前返回true,否则返回false |
Object clone( ) | 返回此对象的副本 |
int compareTo(Date date) | 比较当调用此方法的Date对象和指定日期。两者相等时候返回0。调用对象在指定日期之前则返回负数。调用对象在指定日期之后则返回正数 |
int compareTo(Object obj) | 若obj是Date类型则操作等同于compareTo(Date) 。否则它抛出ClassCastException |
boolean equals(Object date) | 当调用此方法的Date对象和指定日期相等时候返回true,否则返回false |
long getTime( ) | 返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数 |
int hashCode( ) | 返回此对象的哈希码值 |
void setTime(long time) | 用自1970年1月1日00:00:00 GMT以后time毫秒数设置时间和日期 |
String toString( ) | 把此 Date 对象转换为以下形式的 String: dow mon dd hh:mm:ss zzz yyyy,其中: dow 是一周中的某一天 (Sun, Mon, Tue, Wed, Thu, Fri, Sat) |
SimpleDateFormat:
SimpleDateFormat 是一个以语言环境敏感的方式来格式化和分析日期的类。SimpleDateFormat 允许你选择任何用户自定义日期时间格式来运行。
通过代码演示如下(示例):
public class Test {
public static void main(String[] args) throws ParseException {
//把date转换为指定格式的字符串
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String text = sdf.format(new Date());
System.out.println(text); //结果:2020-11-26 16:33:01
//也可以把日期字符串转换为Date对象
text = "2068年5月8日 8:28:58";
SimpleDateFormat anotherSdf = new SimpleDateFormat("yyyy年M月d日 H:mm:ss");
Date date = anotherSdf.parse(text); //把字符串转换为Date对象,如果格式与字符串不匹配就会产生异常
System.out.println(date); //结果:Tue May 08 08:28:58 CST 2068
}
}
LocalDateTime:
Date 日期不是线程安全的,java8新增了 java.time 包,定义一组线程安全的日期类。在多线程环境中使用 LocalDateTime 类获得当前日期。
LocalDateTime 类的构造方法使用 private 修饰为私有的,不能创建LocalDateTime对象,该类中有一个静态方法 now(),该方法返回当前日期对象。
通过代码演示如下(示例):
public class Test {
public static void main(String[] args) {
//返回线程安全的日期
//LocalDateTime类有一个静态方法now()返回当前日期
LocalDateTime dateTime = LocalDateTime.now();
System.out.println(dateTime); //2020-11-26T17:33:31.899
//使用DateTimeFormatter对LocalDateTime进行格式化
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");//先创建DateTimeFormatter对象,指定日期格式
String txt = dateTime.format(dtf);//调用LocalDateTime对象的format(DateTimeFormatter)方法实现对日期的格式化
System.out.println(txt);//2020-11-26 17:33:31
//把字符串转换为日期
txt = "2068年4月16日 8:28:58"; //表示日期的字符串
DateTimeFormatter another = DateTimeFormatter.ofPattern("yyyy年M月dd日 H:mm:ss");
//调用LocalDateTime类的静态方法parse(text,DateTimeFormatter)可以把txt字符串转换为日期
LocalDateTime localDateTime = LocalDateTime.parse(txt, another);
System.out.println(localDateTime); //2068-04-16T08:28:58
}
}
Math 类
java.lang.Math 类,定义了一组与数学函数相关的操作,包括三角函数、对数操作、指数操作等。
Math 类构造方法是 private 私有的,不能创建Math对象,Math 类的所有方法都是静态方法,可以通过类名直接调用。
Math 类中常用的方法:
方法 | 说明 |
---|---|
abs() | 返回参数的绝对值 |
ceil() | 返回大于等于( >= )给定参数的的最小整数,类型为双精度浮点型 |
floor() | 返回小于等于(<=)给定参数的最大整数 |
rint() | 返回与参数最接近的整数。返回类型为double |
round() | 它表示四舍五入,算法为 Math.floor(x+0.5),即将原来的数字加上 0.5 后再向下取整,所以,Math.round(11.5) 的结果为12,Math.round(-11.5) 的结果为-11 |
min() | 返回两个参数中的最小值 |
max() | 返回两个参数中的最大值 |
exp() | 返回自然数底数e的参数次方 |
log() | 返回参数的自然数底数的对数值 |
pow() | 返回第一个参数的第二个参数次方 |
sqrt() | 求参数的算术平方根 |
sin() | 求指定double类型参数的正弦值 |
cos() | 求指定double类型参数的余弦值 |
tan() | 求指定double类型参数的正切值 |
asin() | 求指定double类型参数的反正弦值 |
acos() | 求指定double类型参数的反余弦值 |
atan() | 求指定double类型参数的反正切值 |
atan2() | 将笛卡尔坐标转换为极坐标,并返回极坐标的角度值 |
toDegrees() | 将参数转化为角度 |
toRadians() | 将角度转换为弧度 |
random() | 返回一个随机数 |
通过代码演示Math 类中常用的方法(示例):
public class Test {
public static void main (String []args)
{
System.out.println("90 度的正弦值:" + Math.sin(Math.PI/2)); //结果: 90 度的正弦值:1.0
System.out.println("0度的余弦值:" + Math.cos(0)); //结果: 0度的余弦值:1.0
System.out.println("60度的正切值:" + Math.tan(Math.PI/3)); //结果: 60度的正切值:1.7320508075688767
System.out.println("1的反正切值: " + Math.atan(1)); //结果: 1的反正切值: 0.7853981633974483
System.out.println("π/2的角度值:" + Math.toDegrees(Math.PI/2)); //结果: π/2的角度值:90.0
System.out.println(Math.PI); //结果: 3.141592653589793
//random() 返回一个随机数
Random random = new Random(); //1)先创建Random对象
for(int i = 0 ; i < 10; i++){
System.out.println( random.nextDouble()); //2)生成随机小数
}
for(int i = 0 ; i < 10; i++){
System.out.println( random.nextInt()); //3)生成随机整数
}
for(int i = 0 ; i < 10; i++){
System.out.println( random.nextInt(100)); //生成[0,100)范围内的随机整数
}
}
}
DecimalFormal:
java.text.DecimalFormat类可以对数字格式化
public class Test {
public static void main(String[] args) {
//通过DecimalFormal构造方法指定数字格式串,数字格式符有:#代表任意数字;0代表任意数字,不足的位数会补0; . 小点就是小数点; , 逗号表示千分位
DecimalFormat df = new DecimalFormat("###,###.0000");
String text = df.format(12345.78);
System.out.println(text); //结果:12,345.7800
System.out.println(df.format(123456789.123456789));//结果:123,456,789.1235
}
}
BigInteger/BigDecimal:
java.math.BigInteger表示大的整数,BigDecimal表示大的小数。当表示的数据非常大时,或者要求计算的精度很高时,如财务计算,可以使用BigInteger/BigDecimal。
public class Test {
public static void main(String[] args) {
//double采用近似值保持,不准确,所以对精度要求较高时使用BigDecimal
System.out.println(2 - 1.1); //结果:0.8999999999999999
System.out.println(1 - 0.1); //结果:0.9
BigInteger i1 = new BigInteger("7984651398465134651346513465");
BigInteger i2 = new BigInteger("465194651948651946514651");
//相加调用add(),相减调用subtract(),相乘调用multiply(),相除调用divide()
BigInteger i3 = i1.add(i2);
System.out.println(i3);//结果:7985116593117083303293028116
System.out.println(i1.subtract(i2));//结果:7984186203813185999399998814
System.out.println(i1.multiply(i2));//结果:3714417128240305341196028238440857247337587391275715
System.out.println(i1.divide(i2));//结果:17164
//注意小数相除
BigDecimal d1 = new BigDecimal("7946519486519846598465148651.9465");
BigDecimal d2 = new BigDecimal("4651389486548651365852.745");
//小数相除时,经常会出现java.lang.ArithmeticException算术异常,因为相除时可能会出现除不断的情况
// BigDecimal d3 = d1.divide(d2);//java.lang.ArithmeticException算术异常
//相除时,经常会指定小数的位数,和尾数的处理方法
BigDecimal d3 = d1.divide(d2, 20,RoundingMode.DOWN);
System.out.println(d3);//结果:1708418.42195764908641109940
}
}
包装类
包装类概念:
Java为了使编程更加方便,Java为每个内置数据类型都提供了对应的包装类。
基本类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
包装类的继承关系如下:
所有的包装类(Integer、Long、Byte、Double、Float、Short)都是抽象类 Number 的子类。
创建包装类对象:
所有的包装类,都可以根据基本类型创建包装类对象。
通过代码演示如下(示例):
Integer i1 = new Integer(456);
Double d1 = new Double(3.14);
Character c1 = new Character('A');
Boolean b1 = new Boolean(true);
//除了Character外,其他的包装类可以根据String字符串创建包装类对象.
Integer i2 = new Integer("369");
//注意: 字符串要符合数字格式
//i2 = new Integer("afdasfa"); //运行后产生java.lang.NumberFormatException异常
Double d2 = new Double("456.78");
d2 = new Double("23432e+23"); //科学计数法
System.out.println( d2 ); //结果:2.3432E27
//布尔类型,把非"true"的字符串都转换为false
Boolean b2 = new Boolean("TRue");
System.out.println( b2 ); //结果:true
b2 = new Boolean("asdflkjdasf");
System.out.println( b2 ); //结果:false
包装类的常用方法:
方法 | 说明 |
---|---|
xxxValue() | 将 number 对象转换为 xxx 数据类型的值并返回 |
compare() | 比较两个内置数据类型值的大小(第一个数大返回1,相等返回0,第二个数大返回-1) |
compareTo() | 将 number 对象与参数 比较(第一个数大返回1,相等返回0,第二个数大返回-1) |
equals(Object obj) | 判断 number 对象是否与参数相等 |
parseXXX(String s) | 可以把字符串 s 转换为 XXX 数据类型的值并返回 |
valueOf() | 把内置数据类型或 String 字符串转换为 Number 对象 |
通过代码演示包装类中常用方法(示例):
public class Test {
public static void main(String[] args) {
//xxxValue()
Integer i1 = new Integer(129);
byte b = i1.byteValue(); //结果:b = -127
short i = i1.shortValue(); //结果:i = 129
long l = i1.longValue(); //结果:l = 129
float v = i1.floatValue(); //结果:v = 129.0
double v1 = i1.doubleValue(); //结果:v1 = 129.0
//compare()
System.out.println(Integer.compare(10, 20)); //结果:-1
System.out.println(Double.compare(45.7, 32.6)); //结果:1
System.out.println(Boolean.compare(false, false)); //结果:0
System.out.println(Boolean.compare(true, false)); //结果:1
//compareTo()
i1 = new Integer(456);
Integer i2 = new Integer("456");
System.out.println(i1.compareTo(i2)); //结果:0
System.out.println(i1.compareTo(4122));
//equals()
i1 = new Integer(456);
i2 = new Integer("456");
System.out.println(i1.equals(i2)); //结果:true
//parseXXX()
String text = "456";
int num = Integer.parseInt(text);
double num2 = Double.parseDouble(text); //把字符串转换为小数
System.out.println(num); //结果:456
System.out.println(num2); //结果:456.0
//valueOf()
i1 = Integer.valueOf(456); //结果:i1 = 456
i2 = Integer.valueOf("789"); //结果:i2 = 789
Double d1 = Double.valueOf(123); //结果:d1 = 123.0
Double d2 = Double.valueOf("111.2"); //结果:d2 = 111.2
}
}
装箱与拆箱:
装箱是指把基本类型转换为包装类对象;拆箱是指把包装类对象转换为基本类型;Java可以自动进行装箱与拆箱。
public class Test {
public static void main(String[] args) {
Integer i1 = 369; //自动装箱, 把整数369自动转换为Integer对象
int num = i1; //自动拆箱, 系统自动的把包装类对象i1转换为int类型数据
Integer i2 = 258; //系统根据整数258创建一个Integer对象,把该对象的引用赋值给i2
Integer i3 = 258; //系统根据整数258创建一个Integer对象,把该对象的引用赋值给i3
System.out.println( i2 == i3 ); //结果:false
Integer i4 = 69;
Integer i5 = 69;
System.out.println( i4 == i5 ); //结果:true
}
}
分析:
==
比较二个对象时,比较的是对象的引用地址,而不是对象的值。- Java认为 -128~127 之间的整数使用最频繁,这个范围内的整数装箱后,存储在共享池中,采用 享元模式。(执行 Integer i4 = 69; 语句时,系统根据69创建Integer对象,存储共享池中,把对象的引用赋值给i4,再执行Integer i5 = 69; 时,直接把共享池中69那个包装类对象的引用赋值给i5,即 i4 和 i5 引用了共享池中同一个Integer对象)
方法
什么是方法?
Java方法时语句的集合,它们在一起执行一个功能。
- 方法是解决一类问题的步骤的有序组合
- 方法包含于类或者对象中
- 方法在程序中被创建,在其他地方被引用
方法的定义:
当方法的返回值类型为 void 关键字时,该方法为无返回值方法;当方法中有未知数时,则可以编写为带参数的方法;方法根据是否带有返回值,是否带有参数分为:
- 无参无返回值类型的方法
- 无参带返回值类型的方法
- 有参无返回值类型的方法
- 有参带返回值类型的方法
//定义方法的语法格式
修饰符 返回值类型 方法名(参数类型 参数名){
...
方法体
...
return 返回值;
}
- 修饰符:修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。
- 返回值类型 :方法可能会返回值。returnValueType 是方法返回值的数据类型。有些方法执行所需的操作,但没有返回值。在这种情况下,returnValueType 是关键字void。
- 方法名:是方法的实际名称。方法名和参数表共同构成方法签名。
- 参数类型:参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
- 方法体:方法体包含具体的语句,定义该方法的功能。
通过代码演示如下(示例):
//方法: 返回 两个整数中值更大的那个整数
public static int max(int num1,int num2) {
return num1 > num2 ? num1 : num2;
}
方法的调用:
当程序调用一个方法时,程序的控制权交给了被调用的方法。当被调用方法的返回语句执行或者到达方法体闭括号时候交还控制权给程序。
Java支持两种调用方法的方式,根据方法是否返回值来选择:
- 当方法返带返回值的时候,方法调用通常被当做一个值。
- 当方法无返回值(void)的时候,方法调用一定是一条语句。
通过代码演示如下(示例):
public class TestMax {
/** 主方法 */
public static void main(String[] args) {
int i = 5;
int j = 2;
int k = max(i, j);
System.out.println( i + " 和 " + j + " 比较,最大值是:" + k); //结果:5 和 2 比较,最大值是:5
}
/** 返回两个整数变量较大的值 */
public static int max(int num1, int num2) {
return num1 > num2 ? num1 : num2;
}
}
参数的传递(传值 / 传址):
Java传递的参数有两种方式:
- 普通类型如 int,String 等为传值,传值方式只是把值传入参数,在方法里的任何动作与源无关,源的值不变。
- Java对象,数组,集合等均为传址,传址方式是把源对象的地址传入方法,在方法里的动作都是直接操作源对象,所以能改变其值。
通过代码演示参数的传值与传址(示例):
class A {
int id = 1;
String name = "张三";
}
public class Test {
public static void main(String[] args) {
int num1 = 1;
int num2 = 2;
System.out.println("交换前 num1 的值为:" + num1 + " ,num2 的值为:" + num2);
// 调用 swap() 方法
swap(num1, num2);
System.out.println("交换后 num1 的值为:" + num1 + " ,num2 的值为:" + num2);
A a = new A();
System.out.println("改变前 id 的值为:" + a.id + " ,name 的值为:" + a.name);
//调用 change() 方法
change(a);
System.out.println("改变后 id 的值为:" + a.id + " ,name 的值为:" + a.name);
}
// 交换 n1 与 n2的值
public static void swap(int n1, int n2) {
int temp = n1;
n1 = n2;
n2 = temp;
}
//改变类 A 中的属性;
public static void change(A a){
a.id = 2;
a.name = "李四";
}
/*
编译运行结果:
交换前 num1 的值为:1 ,num2 的值为:2
交换后 num1 的值为:1 ,num2 的值为:2
改变前 id 的值为:1 ,name 的值为:张三
改变后 id 的值为:2 ,name 的值为:李四
*/
}
可变参数:
Java支持传递同类型的可变参数给一个方法,在一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
方法的可变参数的声明如下所示:(示例):
//在方法声明中,在指定参数类型后加一个省略号(...)
typeName... parameterName
实例演示(示例):
public class Test {
public static void main(String[] args) {
// 调用可变参数的方法
printMax(34, 3, 3, 2, 56.5);
printMax(new double[]{1, 2, 3});
}
public static void printMax(double... numbers) {
if (numbers.length == 0) {
System.out.println("No argument passed");
return;
}
//循环遍历可变参数 numbers
for (int i = 0; i < numbers.length; i++) {
System.out.print(numbers[i] + ",");
}
System.out.println();
}
/*
编译运行结果:
34.0,3.0,3.0,2.0,56.5,
1.0,2.0,3.0,
*/
}
方法的重载:
在同一个类中,方法的重载的方法名必须相同,参数列表必须不同(类型不同、顺序不同、个数不同),与返回值类型无关。
例如:上面使用的max方法当前仅仅适用于int型数据,但我们可以创建另一个相同名字但参数不同的方法,构成方法重装。
通过代码演示如下(示例):
public static double max(double num1, double num2) {
return num1 > num2 ? num1 : num2;
}
方法重载可以让程序使用更灵活,更清晰易读。
当构成方法重载后,在调用方法时,会自动根据实参找相匹配的方法执行。
方法中变量与参数的作用域:
方法内定义的变量被称为局部变量,局部变量的作用范围从声明开始,直到它的块结束。局部变量必须声明才可以使用。
方法的参数范围涵盖整个方法,参数实际上也是一个局部变量。
for 循环的初始化部分声明的变量,其作用范围在整个循环,但循环体内声明的变量适用范围是从它声明到循环体结束。
可以在一个方法里,不同的非嵌套块中多次声明一个具有相同的名称局部变量,但你不能在嵌套块内两次声明局部变量。
构造方法以及构造方法的重载:
构造方法:
- 意义:当一个对象被创建时,构造方法用来初始化该对象。
- 作用:通过会使用构造方法给一个类的实例变量赋初值,或者执行其他必要的步骤来创建一个完整的对象。
- 规则:构造方法的名字与类名一致,并且没有返回值。
- 注意事项:不管是否自定义构造方法,所有的类都有一个默认的无参的构造方法,称为隐式构造方法(默认构造方法的访问修饰符和类的访问修改符相同);一旦定义了自己的构造方法,默认构造方法就会失效,称为显示构造方法。
构造方法的重载:在同一个类中,重载的构造方法的方法名必须相同,参数列表必须不同(类型不同、顺序不同、个数不同)。当构造构造方法重载后,在调用构造方法时,会根据实参自动找相匹配的执行。
this 与 super 关键字
this 关键字:
- this 代表当前这个对象(也就是说当前谁调用这个方法则这个对象就是谁)。
- this 关键字可以访问本类中的实例变量、实例方法、构造方法,还可以访问父类中的实例变量和父类中的实例方法。
- 如果使用 this 关键字访问父类中的实例变量或实例方法时,先在当前类中找该实例变量或实例方法,如果没有找到则再去父类中找实例变量或实例方法。
super 关键字:
- super 表示父类或者超类,只能在子类中使用。
- super 关键字只能在子类中访问父类中的实例变量、实例方法、构造方法。
this 与 super 注意事项:
- 如果在子类中没有重写父类的方法,在子类中访问父类中的方法时,使用 this 或者 super 都可以访问父类中的实例方法。
- 如果在子类中重写父类中的方法,并且在子类中访问父类中的方法,则必须使用 super。
== 与 equals
==
:
- 如果基本数据类型使用
==
则比较的是真正的值是否一致- 如果引用数据类型使用
==
则比较的是地址是否一致,也就是判断是否是同一个对象。
equals:
- 在Java中如果两个字符串比较内容是否一致则使用 equals。
原因:String类已经重写toString方法,实现的功能是比较两个字符串的内容是否一致。- Object类中的equals方法,依然比较的是两个对象的内存地址是否一致。
- 如果子类重写父类的equals方法,则比较两个对象中各个属性值是否一致。
异常处理
异常概念:
在程序运行过程中,如果环境检测出一个不可能执行的操作,就会出现运行时错误。
比如说,当对象为null,访问对象的实例成员会产生空指针异常 NullPointerException,把非数字字符串转换为数字会产生数字格式不正确异常 NumberFormatException,日期转换异常 ParseException 等。
异常就是一个表示阻止执行正常进行的错误或情况,简单说异常就是程序运行过程中出现的不正常现象.
Java把经常出现的一些异常现象进行了抽象,就形成了异常类。
- Throwable类:是所有异常的根。
- Error类:描述的是内部系统错误,这样的错误很少发生,如果发生,除了通知用户终止终止程序外,几乎什么也不能做;
- Exception类:描述的是由程序或外部环境所引发的错误,这些错误可以被程序捕获或处理。
Exception 异常分为两大类:编译时异常(checked 检查异常)与运行时异常(unchecked 免检异常)。从类继承结构上看,如果异常类继承了 RuntimeException 类就是运行时异常,如果不是 RuntimeException 类的子类就是编译时异常。
- IOException:编译时异常。必须进行预处理,否则编译错误。
- RuntimeException:运行时异常。程序设计错误,如错误的类型转换,访问数组元素时下标越界等,它反映出程序设计的逻辑错误,Java不允许编写代码捕获或声明免检异常,需要由程序员通过规范的代码避免这类异常。
可以简单的理解为,如果是因为程序员编码不规范出现的异常就是运行时异常;如果是因为用户使用不当,或者用户环境引发的异常就是编译时异常。
Java中的内置异常类:
Java 语言定义了一些异常类在 java.lang 标准包中。
标准运行时异常类的子类是最常见的异常类。由于 java.lang 包是默认加载到所有的 Java 程序的,所以大部分从运行时异常类继承而来的异常都可以直接使用。
Java 根据各个类库也定义了一些其他的异常,下面的表中列出了 Java 的非检查性异常。
异常 | 说明 |
---|---|
ArithmeticException | 当出现异常的运算条件时,抛出此异常。例如,一个整数"除以零"时,抛出此类的一个实例 |
ArrayIndexOutOfBoundsException | 用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引 |
ArrayStoreException | 试图将错误类型的对象存储到一个对象数组时抛出的异常 |
ClassCastException | 当试图将对象强制转换为不是实例的子类时,抛出该异常 |
IllegalArgumentException | 抛出的异常表明向方法传递了一个不合法或不正确的参数 |
IllegalMonitorStateException | 抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程 |
IllegalStateException | 在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下 |
IllegalThreadStateException | 线程没有处于请求操作所要求的适当状态时抛出的异常 |
IndexOutOfBoundsException | 指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出 |
NegativeArraySizeException | 如果应用程序试图创建大小为负的数组,则抛出该异常 |
NullPointerException | 当应用程序试图在需要对象的地方使用 null 时,抛出该异常 |
NumberFormatException | 当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常 |
SecurityException | 由安全管理器抛出的异常,指示存在安全侵犯 |
StringIndexOutOfBoundsException | 此异常由 String 方法抛出,指示索引或者为负,或者超出字符串的大小 |
UnsupportedOperationException | 当不支持请求的操作时,抛出该异常 |
下面的表中列出了 Java 定义在 java.lang 包中的检查性异常类。
异常 | 说明 |
---|---|
ClassNotFoundException | 应用程序试图加载类时,找不到相应的类,抛出该异常。 |
CloneNotSupportedException | 当调用 Object 类中的 clone 方法克隆对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。 |
IllegalAccessException | 拒绝访问一个类的时候,抛出该异常。 |
InstantiationException | 当试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。 |
InterruptedException | 一个线程被另一个线程中断,抛出该异常。 |
NoSuchFieldException | 请求的变量不存在 |
NoSuchMethodException | 请求的方法不存在 |
异常方法:
下面的列表是 Throwable 类的主要方法:
方法 | 说明 |
---|---|
public String getMessage() | 返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了。 |
public Throwable getCause() | 返回一个Throwable 对象代表异常原因。 |
public String toString() | 使用getMessage()的结果返回类的串级名字。 |
public void printStackTrace() | 打印toString()结果和栈层次到System.err,即错误输出流。 |
public StackTraceElement [] getStackTrace() | 返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底。 |
public Throwable fillInStackTrace() | 用当前的调用栈层次填充Throwable 对象栈层次,添加到栈层次任何先前信息中。 |
异常的捕获处理(try/catch):
使用 try 和 catch 关键字可以捕获异常。try/catch 代码块中的代码称为保护代码,放在异常可能发生的地方。
// try/catch 语法格式:
try {
// 需要预处理的受检异常的代码
} catch (ExceptionName e) {
//捕获异常,进行预处理
} finally {
//经常把释放系统资源的代码放在finally代码块中
}
说明:
- try 语句后面添加任意数量的 catch 块,但 catch 不能独立于 try 存在。
- 如果 try 代码块中没有出现异常,则跳过catch子句。
- 如果try代码块的某条语句抛出一个异常,Java会跳过try代码块中剩余的语句,然后查找处理这个异常的catch子句,跳转到对应的catch子句执行。
- 异常对象包含了关于异常的有价值的信息,在catch子句中捕获了异常对象e,可以调用 getMessage() 方法返回异常的信息,调用 printStackTrace() 打印异常的跟踪信息。
- finally子句不是必需的,不管是否有异常产生总是会执行。
异常捕获处理的作用:当try代码块的业务逻辑产生了异常后,程序会立即跳转到catch子句执行,程序不会中断,可以提高程序的健壮性。
受检异常的抛出处理(throws/throw):
在Java中,当前执行的代码必属于某个方法,在方法的声明位置通过throws声明它可能抛出的受检异常类型,这称为声明异常,或称为异常的抛出处理, 这样方法的调用者会被告知有异常.如:
public void method() throws IOException{}
在 定义 方法时, 通过throws声明抛出的异常,该异常类没有继承RuntimeException类, 它就是受检异常. 在 调用 该方法时, 必须对方法的受检异常进行预处理,否则编译语法错误. 预处理是指当程序运行后万一产生了异常应该怎么办, 即程序员对用户在使用该程序时可能产生的异常提出一种预处理方案, 注意: 并不是说在编译阶段发生了异常 , 所有的异常都是 在运行阶段才可能产生.
都看到这了,点个赞再走呗!^.^