java入门

2022/5/5 14:29:38

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

目录
  • 1.基础语法
  • 基本数据类型
    • 整数类型
    • 浮点类型
    • 字符类型
    • 布尔类型
    • 类型转换
  • 运算符
    • 算术运算符
    • 关系运算符
    • 逻辑运算符
    • 赋值运算符
    • 三元运算符
    • 字符串运算符
    • 位运算符
  • 分支
    • if 语句语法
    • switch 语句语法
  • 循环
    • while循环
    • do while循环
    • for循环
    • 三种循环方式的区别
    • break与continue关键字
  • 数组
    • 数组的声明
    • 数组的初始化
    • 数组的访问
    • 数组的遍历
    • 多维数组
  • 2.面向对象
  • 封装
  • 继承
    • 重写
    • this关键字
    • super关键字
  • 多态
  • 接口和抽象类
    • 抽象类
    • 接口
  • static和final关键字
    • static
    • final
  • 3.常用API
  • String
  • 包装类
    • 基本类型与包装类之间的转换
    • 包装类的常用API
    • 包装类是不可变类
    • 包装类的父类
    • 包装类中的静态常量
  • 数学工具
    • Math
    • Random
    • Random
    • BigDecimal
    • NumberFormat
  • 日期工具
    • Date
    • Calendar
    • DateFormat
    • SimpleDateFormat
  • 异常处理
    • 异常的继承体系
    • 捕获异常
    • 声明抛出异常
    • 抛出异常
    • 异常的处理原则
  • 4.集合类
  • Set
  • List
  • Queue
  • Map
    • HashMap
    • TreeMap
  • Collections

1.基础语法

基本数据类型

Java有8种基本数据类型,分别是byte、short、int、long、float、double、char、boolean。这8种类型又可以划分为4个小类,每一个类型占据不同的内存空间,详细见下表:

img

整数类型

整数类型的最高位存放正负符号,1表示负、0表示非负,其余位置存放整数值。所以,根据不同整数类型的位数,我们可以推算出它的数据范围:

img

浮点类型

浮点类型采用科学计数法来表示数值,对于任意的小数,我们都可以采用统一的科学计数形式表示它。

0.0123 = 0.123 * 10^-1

12.345 = 0.12345 * 10^2

-123.45 = -0.12345 * 10^3

这种表示数字的形式包含三个要素:符号、指数、尾数。

img

浮点类型,就是将小数转换为科学计数法的形式,再将一串二进制的整数分为三段,分别存储小数的符号、尾数、指数。所以,float和double的表数范围如下:

img

字符类型

字符类型的本质也是整数,它采用16位的二进制整数来对字符进行编码,在Unicode字符集下,16位整数可以表示出65535个字符。其最小值为u0000,最大值为uffff。

布尔类型

布尔类型只包含两个值:true、false。所以实际上它只需要一位的存储空间即可,但Java虚拟机并没有规定布尔类型的具体实现,很多虚拟机实际是采用int来实现的布尔类型,这是出于性能的考虑。

类型转换

除了布尔类型之外,其余7种类型均为数字,所以它们之间可以相互转换。而转换的关键是数据类型的范围,下图给出了7种类型由小到大的数据范围。

img

由小到大地转换是没有风险的,可以直接转,这叫自动类型转换。由大到小转换则存在风险,很有可能会在转换时出现数据溢出的情况,所以需要进行强制转换。

// 自动类型转换

int a = 5;

double b = a;

// 强制类型转换

double c = 3.14;

int d = (int) c;

运算符

Java支持7类运算符:算术运算符、关系运算符、逻辑运算符、赋值运算符、三元运算符、字符串运算符、位运算符。

算术运算符

img

关系运算符

img

逻辑运算符

img

赋值运算符

img

三元运算符

// 语法

布尔表达式 ? 表达式1 : 表达式2

// 示例

age < 18 ? "未成年" : "已成年"

字符串运算符

// 语法

字符串 + 任意类型数据

// 示例

int age = 20;

String str = "我今年" + age + "岁";

位运算符

img

分支

Java的分支语句有两种形式:if语句、switch语句。

if 语句语法

// 第一种形式

if (逻辑表达式) {

...

}

// 第二种形式

if (逻辑表达式) {

...

} else {

...

}

// 第三种形式(else if 可以出现任意多次)

if (逻辑表达式) {

...

} else if (逻辑表达式) {

...

} else {

...

}

switch 语句语法

switch (表达式) {

case 值1: {

...

break;

}

case 值2: {

...

break;

}

default: {

...

}

}

// 1. 表达式可以返回如下类型:byte, short, int, char, String, Enum

// 2. case语句可以出现任意多次

// 3. default语句可以省略

// 4. 内层的花括号均可以省略

循环

Java中的循环有三种形式:while循环、do while循环、for循环。

while循环

语法:

while (循环条件) {

...

[迭代语句]

}

语法:

img

do while循环

语法:

do {

...

[迭代语句]

} while (循环条件);

流程:

img

for循环

语法:

for (初始化语句; 循环条件; 迭代语句) {

...

}

流程:

img

三种循环方式的区别

  1. for循环:结构稳定,不容易遗漏任何循环要素,适合处理循环次数固定的场景。
  2. while循环:先判断循环条件,再执行循环体,适合处理“当...”的业务场景。
  3. do while循环:先执行循环体,再判断循环条件,适合处理“直到...”的业务场景。

break与continue关键字

- break用于结束循环,强制跳出循环体。一旦遇到break,系统将结束该循环,开始执行循环之后的代码。

- continue用于忽略本次循环剩下的语句,接着开始下一次循环,并不会终止循环。

数组

数组是一种常见的数据结构,用于存储多个数据。在Java中,数组中的元素具有相同的数据类型,数组本身是一种引用数据类型。数组中的元素按线性方式排列,可以通过编号来访问数组中的每个元素。一旦初始化完成,数组在内存中所占的空间就固定下来,即数组的长度不可改变。数组既可以存基本类型的数据,也可以存引用类型的数据,只要类型相同即可。

数组的声明

type[] arrayName;

type arrayName[];

数组的初始化

// 静态初始化

// 动态初始化

arrayName = new type[length];

数组的访问

// 访问数组的元素

arrayName[index]; // index是数组的索引,是一个从0开始的整数。

// 获取数组的长度

arrayName.length;

数组的遍历

// for循环

for (int i = 0; i < array.length; i++) {

System.out.println(array[i]);

}

// foreach循环

for (type variableName : array | collection) {

System.out.println(variableName);

}

多维数组

// 声明二维数组

type[][] arrayName;

// 初始化二维数组

arrayName = new type[] [] { {...}, {...}, ... }; // 静态初始化

arrayName = new type[outerLength] [innerLength]; // 动态初始化

// 访问二维数组

arrayName[outerIndex] [innerIndex]; // 访问数组的元素

arrayName.length; // 访问外层数组的长度

arrayName[outerIndex].length; // 访问内层数组的长度

2.面向对象

封装

封装可以隐藏类的实现细节,让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里加入控制逻辑,限制对成员变量的不合理访问。通过封装,我们还可以在访问数据时进行检查,从而有利于保证对象信息的完整性。另外,合理封装的程序便于修改,可以提高代码的可维护性。

封装的实现依赖于访问修饰符,Java提供了3种访问修饰符,分别是private、protected、public。这三种访问修饰符以及不加修饰符的情况,一共形成了4种访问级别:private、default、protected、public。访问修饰符与类中成员的访问级别如下表所示:

img

常见的封装方式是,将一个类中的成员变量私有化,即使用private修饰成员变量,从而避免它被直接访问到。然后为该成员变量增加赋值(set)方法和取值(get)方法,并使用public修饰这两个方法,使得外界可以通过这两个访问来访问该成员变量。在这两个方法中,我们可以增加任意的数据处理逻辑。

class Foo {

private int count;

public void setCount(int count) {

​ this.count = count;

}

public int getCount() {

​ return count;

}

}

继承

Java采用extends关键字实现继承,实现继承的类叫子类,被继承的类叫父类。任何类都只能有一个直接的父类,和无数个间接的父类,而多个类可以继承于同一个父类。若一个类没有显式地指定父类,则这个类默认继承于java.lang.Object类。

重写

子类继承父类后,还可以从写父类中的方法。所谓重写,是指在子类中,定义与父类同名的方法,用来覆盖父类中这个方法的逻辑。在重写时,要遵守如下规范:

\1. 子类方法的名称、参数列表与父类相同。

\2. 子类方法的返回类型与父类相同或更小。

\3. 子类方法声明的异常与父类相同或更小。

\4. 子类方法的访问权限与父类相同或更大。

this关键字

this关键字用于指代当前对象,它包含如下作用:调用当前对象的构造方法、调用当前对象的成员变量、调用当前对象的成员方法。在成员变量和形参的名称产生冲突时,可以通过this来区分二者:

class Foo {

private int count;

public Foo(int count) {

​ this.count = count;

}

public void setCount(int count) {

​ this.count = count;

}

public int getCount() {

​ return count;

}

}

super关键字

super关键字包含如下作用:调用父类的成员变量、调用父类的成员方法、调用父类的构造方法。注意,super关键字也是指代当前对象,但用于调用该对象在父类中的成员。通过super调用构造方法时,并不会创建一个新对象。父类构造方法会在子类构造方法之前调用,并且总会被调用一次。

class Base {

void method1() {}

}

class Sub extends Base {

void method1() {}

void method2() { super.method1(); }

}

多态

在程序运行时,相同类型的变量可以呈现出不同的行为特征,这就是多态。多态的作用在于,它可以提高程序的扩展性。需要注意,在编写Java代码时,我们只能调用声明变量时所用的类型中包含的成员变量与方法。

class Base { }

class Sub1 extends Base { }

class Sub2 extends Base { }

class Sub3 extends Base { }

class Foo {

// 调用该方法时,可以传入Base的实例,也可以传入其任意子类的实例。

public void doSomething(Base base) { }

}

在上面的示例中,方法的参数为父类Base,而实际传入的可能是子类的实例。如果在方法内部,需要判定该实例具体属于哪一个子类,则可以采用instanceof关键字。instanceof的语法如下,它的含义是判断变量是否符合后面的类型,或者符合后面的类型的子类、实现类。

变量 instanceof 类型

如果判定实例属于某一个具体的子类,那么我们就可以将其强制转型为这个子类型。注意,这种强制类型转换,只能在具有继承关系的两个类型之间进行,若试图将符类型对象转换为子类型,则这个对象必须实际上是子类的实例才行。

(类型) 变量;

接口和抽象类

抽象类

使用abstract关键字修饰的类叫抽象类,使用abstract关键字修饰的方法叫抽象方法,抽象方法不能有方法体。抽象类有如下规范:

\1. 抽象类中可以没有抽象方法,但是包含抽象方法的类必须声明为抽象类。

\2. 若子类是抽象类,可以不实现父类的抽象方法,否则必须实现父类的抽象方法。

\3. 抽象类允许有构造方法,可以在子类中调用,但是不能调用它来实例化抽象类。

接口

接口是从多个相似的类中抽象出来的规范,体现了规范和实现分离的设计哲学。接口不提供任何实现,它不能包含普通方法,接口内部定义的所有的方法都是抽象方法。而Java 8对接口进行了改进,允许在接口中定义默认方法和静态方法,这两类方法是可以提供方法实现的。

- 接口的定义:

[修饰符] interface 接口名 extends 父接口1, 父接口2, ... { }

- 接口的成员:

接口中可以定义成员变量、成员方法(抽象方法、默认方法、静态方法):

a. 接口中所有的成员都是public访问权限,而public修饰符可以省略。

b. 接口中的成员变量都是静态常量,而static final关键字可以省略。

c. 接口中的普通成员方法必须是抽象的,而abstract关键字可以省略。

d. 接口中的默认方法必须使用default修饰,静态方法必须使用static修饰,均不能省略。

- 接口的使用

// 1. 一个类可以实现一个或多个接口,使用implements关键字。

// 2. 实现与继承类似,可以获得被实现接口里定义的常量、方法。

// 3. 如果这个类不是抽象类,那么它就必须实现这些接口里所定义的全部抽象方法。

[修饰符] class 类名 extends 父类 implements 接口1, 接口2, ... { }

static和final关键字

static

Java类中可以包含成员变量、成员方法、构造方法、初始化块、内部类,static可以修饰其中的成员变量、成员方法、初始化块、内部类。被static修饰的成员是类的成员,该成员属于类,不属于单个对象。需要注意的是,类成员不能访问实例成员,因为可能出现类成员已经初始化完成,但实例成员还未初始化的情况。

\1. 类变量:以static修饰的成员变量叫类变量(静态变量),类变量属于类,它随类的信息存储在方法区中,而不是随着对象存储在堆中。类变量可以通过类名来访问,也可以通过对象名来访问,建议通过类名来访问。

\2. 类方法:以static修饰的方法叫类方法(静态方法),类方法属于类,可以通过类名访问,也可以通过对象名访问,建议通过类名来访问。

\3. 静态块:以static修饰的初始化块叫静态初始化块,静态块属于类,它在类加载的时候被隐式调用一次,之后便再也不会被调用了。

\4. 静态内部类:被static修饰的成员内部类叫静态内部类,静态内部类可以包含静态成员,也可以包含非静态成员。静态内部类不能访问外部类的实例成员,只能访问它的静态成员。外部类的所有方法、初始化块都能访问其内部定义的静态内部类。

final

final关键字可以用于修饰类、方法、变量。被final修饰的类不可以被继承,被final修饰的方法不可以被重写,被final修饰的变量,一旦获得了初始值,就不可以被修改。

对于基本类型的变量,若final修饰的是静态变量,则可以在声明变量时指定初始值,也可以在静态初始化块中指定初始值。若final修饰的是实例变量,可以在声明变量时指定初始值,也可以在普通初始化块或构造方法中指定初始值。若final修饰的是局部变量,可以在声明变量时指定初始值,也可以在后面的代码中指定初始值。

对于引用类型的变量,由于保存的仅仅是一个引用(地址),所以final只能保证这个引用类型变量所引用的地址不会改变,但这个对象的内容是可以发生改变的。

3.常用API

String

String类是final的,所以它不存在子类。另外,String类是不可变类,就是在创建String对象后,其内部的字符序列是无法修改的。通常需要采用如下三种手段,来保证一个类的不可变:

  1. 使用private和final修饰成员变量。
  2. 提供带参数的构造器,用于初始化上述成员变量。
  3. 仅提供获取成员变量的方法,不提供修改的方法。

String类中定义了大量处理字符串的方法,下表列举了比较常用的方法。其中,有很多方法是修改字符串的方法,其底层并不是真正改变了原始字符串,而是生成了新的字符串,这是字符串不可变的本意。

img

此外,String类还提供了一些支持正则表达式的方法,用于增强字符串的处理能力:

  1. boolean matches(String regex)判断此字符串是否与指定的正则表达式(regex)匹配。
  2. String[] split(String regex)以正则表达式(regex)匹配的内容作为分隔符,将此字符串分隔成多个子串。
  3. String replaceFirst(String regex, String replacement)将此字符串中,第一个与正则表达式(regex)匹配的子串替换成目标(replacement)。
  4. String replaceAll(String regex, String replacement)将此字符串中,每一个与正则表达式(regex)匹配的子串替换成目标(replacement)。

包装类

我们都知道,Java是面向对象的编程语言,在Java中一切皆对象。但是八种基本数据类型却是例外的,它们不具备对象的特性。为了解决这个问题,Java为每个基本类型都定义了一个对应的引用类型,它们就是对应的基本类型的包装类了。

img

基本类型与包装类之间的转换

- JDK 1.5之前:通过包装类提供的构造器,可以将基本类型转为包装类型的对象。通过包装类提供的xxxValue()方法,可以获得该对象中包装的数据。

- JDK 1.5之后:JDK 1.5提供了自动装箱、自动拆箱功能。所谓自动装箱,就是可以把一个基本类型的数据直接赋值给对应的包装类型。所谓自动拆箱,就是可以把一个包装类型的对象直接赋值给对应的基本类型。

包装类的常用API

- 字符串转为基本类型(以Integer为例)

public Integer (String s) { }

public static int parseInt(String s) { }

// 注意,Character类中没有定义上述构造方法和静态方法。

- 比较两个数据的大小(以Integer为例)

public static int compare(int x, int y) { }

包装类是不可变类

包装类都是不可变类,就是说该类被实例化后,它的实例变量是不可改变的。通常需要采用如下三种手段,来保证一个类的不可变:

  1. 使用private和final修饰成员变量。

  2. 提供带参数的构造器,用于初始化上述成员变量。

  3. 仅提供获取成员变量的方法,不提供修改的方法。

包装类的父类

img

包装类中的静态常量

  1. MAX_VALUE,表示该类型的最大值。

  2. MIN_VALUE,表示该类型的最小值。

Byte、Short、Integer、Long、Float、Double、Character均定义了上述常量。

数学工具

Math

算术运算符用于处理基本的数学运算,而Math类用于处理复杂的数学运算。

  1. Math类不能被实例化,它的所有成员都是静态的,直接通过类名来访问。

  2. Math类提供了两个静态常量,分别是E(自然对数的基数)和PI(圆周率)。

  3. Math类提供了对数运算、指数运算、三角函数运算等一系列支持数学运算的方法。

Random

Random类专门用于生成一个伪随机数。

  1. 伪随机数就是基于一个“种子”,采用特定算法计算而来的数字。

  2. 每次计算的结果将作为新的“种子”,用于生成下一个伪随机数。

  3. Random类有两个构造器:

- 无参构造器,使用默认的种子,即当前时间的毫秒数。

- 有参构造器,使用指定的种子,需要调用者显式地传入long型的整数。

Random

Random类专门用于生成一个伪随机数。

  1. 伪随机数就是基于一个“种子”,采用特定算法计算而来的数字。

  2. 每次计算的结果将作为新的“种子”,用于生成下一个伪随机数。

  3. Random类有两个构造器:

- 无参构造器,使用默认的种子,即当前时间的毫秒数。

- 有参构造器,使用指定的种子,需要调用者显式地传入long型的整数。

BigDecimal

浮点数是不精确的,若要精确地表示和计算浮点数,则需要使用BigDecimal类。该类提供了一系列用于精确计算的方法,例如:add()、subtract()、multiply()、divide()、pow()等。有多种创建BigDecimal实例的办法,它们的区别如下:

// 建议使用该构造器,它的结果是可以预知的。

public BigDeclmal(String val) { }

// 不建议使用该构造器,因为它的参数是一个近似值。

public BigDecimal(double val) { }

// 若必须使用double型参数创建对象,建议使用这个静态方法来创建实例。

public static BigDecimal valueOf(double val) { }

NumberFormat

NumberFormat用于实现数值的格式化。它提供了format()方法,可以将传入的数值格式化为字符串。还提供了parse()方法,可以将传入的格式字符串解析成数值。有多种创建NumberFormat实例的办法,区别如下:

// 返回默认地区的货币格式器

public final static NumberFormat getCurrencyInstance() { }

// 返回默认地区的数值格式器

public final static NumberFormat getNumberInstance() { }

// 返回默认地区的百分数格式器

public final static NumberFormat getPercentInstance() { }

// 此外,上述方法均有重载方法,可以显示地传入参数以指定地区。

日期工具

Date

Date类用来处理日期和时间,但是该类的大部分构造器、方法均以过时。

- 常用的构造方法

// 创建代表当前时间的Date对象,底层调用System类获取当前时间毫秒数。

public Date() { }

// 根据指定的时间毫秒数创建Date对象,参数为时间的毫秒数。

public Date(long date) { }

- 常用的成员方法

// 判断该时间是否在指定时间之后

public boolean after(Date when) { }

// 判断该时间是否在指定时间之前

public boolean before(Date when) { }

// 返回该时间的毫秒数

public long getTime() { }

// 以毫秒数的形式,设置该Date对象所代表的时间。

public void setTime(long time) { }

Calendar

相比于Date类,Calendar类可以更好地处理日期和时间。Calendar是一个抽象类,所以不能通过构造器创建Calendar对象。Calendar类提供了静态方法getInstance(),用以创建实例。

- Calendar类提供了与Date互转的方法

// 返回Date

public final Date getTime() { }

// 将Date转为Calendar

public final void setTime(Date date) { }

- Calendar类常用的成员方法

// 返回指定字段的值

public int get(int field) {}

// 给指定的字段设置一个值

public void set(int field, int amount) {}

// 设置年月日的值

public void set(int year, int month, int date) {}

// 设置年月日时分秒的值

public void set(int year, int month, int date, int hourOfDay, int minute, int second) {}

- Calendar类常用的静态变量

YEAR, MONTH, DATE, HOUR, MINITE, SECOND, MILLISECOND。

DateFormat

DateFormat用于实现日期的格式化,它是一个抽象类,提供了如下静态方法以创建实例:

// 返回日期格式器

public final static DateFormat getDateInstance() {}

// 返回时间格式器

public final static DateFormat getTimeInstance() {}

// 返回日期时间格式器

public final static DateFormat getDateTimeInstance() {}

同时,DateFormat提供了如下常用的成员方法:

// 将传入的日期格式化为字符串

public final String format(Date date) {}

// 将传入的格式字符串解析为日期

public Date parse(String source) throws ParseException {}

SimpleDateFormat

SimpleDateFormat是DateFormat的子类,提供了更简单的格式化方案,该类提供了如下常用的构造器,常用的成员方法则与DateFormat一致。

// pattern是一个日期模板字符串,如“yyyy-MM-dd HH:mm:ss”

public SimpleDateFormat(String pattern) {}

异常处理

Java语言具有如下的异常处理机制:当程序运行出现意外情形时,系统会自动生成一个异常对象来通知程序。在程序中,我们可以使用特定的语句来捕获异常对象,读取对象中的信息,进而做出处理。也可以使用特定的语句抛出异常对象,将这个异常对象交给程序的调用者处理。

异常的继承体系

img

- 所有的非正常情况被分为两类:

错误(Error)和异常(Exception),其中Error代表虚拟机相关问题,一般无法处理,也无需处理。

- 所有的 Exception 被分为两类

a. RuntimeException,代表运行时异常,程序可以显式处理这种异常,也可以不处理,而是交给顶层调用者统一处理。

b. 非运行时异常(Checked异常),程序必须显式地处理这种异常,否则在编译阶段会发生错误,导致程序无法通过编译。

捕获异常

语法:

try {

​ 业务逻辑代码

}

catch (AException e) {

​ A异常的处理代码

}

catch (BException e) {

​ B异常的处理代码

}

...

finally {

​ 回收资源代码

}

说明:

  1. 无论哪行代码发生异常,系统都会生成一个异常对象,这与try...catch...语句没有关系;

  2. 若程序没有对这个异常对象做任何处理,则程序在此退出;

  3. 创建异常对象后,JVM会寻找可以处理它的catch块,并将异常对象交给这个catch块去处理;

  4. 程序应该先处理小异常、再处理大异常,即将处理父类异常的catch块放在处理子类异常的catch块之后;

  5. finally块中代码总是会被执行,它必须位于try、catch之后,通常用来释放资源;

  6. try是必须的,catch、finally是可选的,但catch、finally二者之中至少要出现一个。

声明抛出异常

语法:

throws ExceptionClass1, ExceptionClass2, ...

说明:

  1. throws语句用于标识某方法可能抛出的异常,它必须位于方法签名之后;

  2. throws语句声明抛出异常后,程序中旧无需使用try语句捕获该异常了;

  3. 在重写时,子类方法声明抛出的异常类型不能比父类方法声明抛出的异常类型大。

抛出异常

语法:

throw ExceptionInstance;

说明:

  1. throw语句用于在程序中主动抛出一个异常;

  2. throw语句抛出的不是异常类型,而是一个异常实例;

  3. 对于主动抛出的异常,也可以采用try块捕获,或者采用throws语句向外抛出。

异常的处理原则

  1. 不要多度的使用异常:

不要用异常处理代替错误处理代码,不要用异常处理代替流程控制语句;

  1. 不要忽略捕获的异常:

对于捕获到的异常,要进行合适的修复,对于不能处理的部分,应该抛出新的异常;

  1. 不要直接捕获所有的异常:

应对不同的异常做出有针对性的处理,而捕获所有的异常,容易压制(丢失)异常;

  1. 不要使用过于庞大的try块:

庞大的try块会导致业务过于复杂,不利于分析异常的原因,也不利于程序的阅读及维护。

4.集合类

Set

Set接口代表无序不可重复集合。Set集合通常记不住元素的添加顺序,并且Set集合不允许包含相同的元素,向Set中加入相同元素时会失败,即方法返回false。Set接口常用的实现类有:HashSet、TreeSet。

HashSet是Set接口的典型实现,它具有以下特点:

  1. HashSet不能保证元素的排列顺序;

  2. HashSet集合元素的值可以是 null;

  3. HashSet是非线程安全的,多线程环境下须通过代码来保证其同步。

TreeSet可以保证元素的排列顺序,它比HashSet多了一些方法:

  1. 返回集合中第一个/最后一个元素;

  2. 返回集合中位于指定元素之前/之后的元素;

  3. 返回集合中某个限定范围内的元素组成的子集。

TreeSet采用红黑树的数据结构来存储元素,它支持两种排序方式:自然排序、定制排序。

  1. 自然排序:

- 添加时,调用元素的compareTo方法比较元素的大小,并按照升序排列元素;

- 添加到TreeSet中的对象必须实现Comparable接口,该接口定义了compareTo方法;

- Java提供的很多类型均已经实现了Comparable接口,如包装类、String、Date等。

  1. 定制排序:

- 创建TreeSet时,传入Comparator接口的实例;

- Comparator接口定义了compare方法,用于比较两个对象的大小;

- TreeSet不再调用compareTo方法,转而调用compare方法比较大小。

List

List接口代表有序集合,它提供了根据索引来访问集合的方法:

  1. 将元素插入到集合中指定的索引处;

  2. 将指定索引处的元素从集合中删除;

  3. 从集合中返回指定索引处的元素;

  4. 返回某个元素在集合中的索引值;

  5. 从集合中,返回起始索引和结束索引之间的元素所组成的子集。

List还提供了一个listIterator方法,以返回一个ListIterator对象。ListIterator是一个接口,它继承于Iterator接口,并增加了如下的方法:

boolean hasPrevious(); // 判断迭代器指向的元素是否有上一个元素

E previous(); // 返回该迭代器所指向的元素的上一个元素

boolean hasNext(); // 判断迭代器指向的元素是否有下一个元素

E next(); // 返回该迭代器所指向的元素的下一个元素

void add(E e); // 在迭代器所指向的元素位置插入一个元素

List接口常用实现类有ArrayList、LinkedList,ArrayList是采用数组实现有序集合,LinkedList是采用链表实现的有序集合。数组和链表的区别在于:

- 数组需要占据连续的内存空间,访问效率高、增删效率低;

- 链表不必占据连续的内存空间,增删效率高、访问效率低,它以指针维护元素的顺序,即上一个元素会指向下一个元素。

Queue

Queue接口用于模拟队列,它是一种先进先出(FIFO)的容器,常用的方法见下方表格:

img

Queue接口包含如下子接口和实现类:

img

  1. PriorityQueue是一种不标准的队列实现,它不是按照加入的顺序来保存元素,而是按照元素的大小排序来保存元素;

  2. Deque接口代表双端队列,它允许你从队列头/尾的任何一端,来进行入队/出队操作,甚至还支持入栈/出栈的操作;

  3. ArrayDeque、LinkedList是Deque接口的实现类,前者采用数组实现双端队列,而后者采用链表结构实现双端队列

Map

Map接口用于保存具有映射关系的数据(key-value),key和value之间存在单向一对一关系,通过指定的key,总能找到确定的value。Map的key不允许重复,同一个Map的任何两个key通过equals比较总返回false。

如下图,Map接口具有如下实现类,其中较为常用的是HashMap和TreeMap。

img

HashMap

HashMap是Map接口的典型实现:

  1. HashMap是非线程安全的,其性能高于Hashtable;

  2. HashMap允许使用null作为key/value,而Hashtable不允许存入null。

TreeMap

TreeMap是一个红黑树的数据结构,在存储键值对时,它按照key对键值对排序。

  1. 自然排序:对key进行比较,并根据key按照由小到大的顺序排列键值对。所有的key应该是同一个类型,且必须实现Comparable接口。

  2. 定制排序:创建TreeMap时,传入一个Comparator类型的对象,该对象负责对所有的key进行比较,此时不要求key实现Comparable接口。

Collections

Collections是一个操作集合的工具类,它提供了4类集合操作:

  1. 强大的排序功能:针对List集合提供了众多排序方法;

  2. 查找与替换功能:针对Collection提供了众多查找和替换元素的方法;

  3. 创建不可变集合:提供3类方法(空的/唯一/只读)来创建一个不可变的集合;

  4. 线程同步的集合:将指定的集合包装成线程同步的集合,以解决线程安全问题。



这篇关于java入门的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程