# 一、注解简介
/**
* 注解与注释的区别:
*
*/
// 单行注释 /* 多行注释 */ /** 文档注释 */
public @interface MyAnnot01 {
}
从 Java 5 版本之后可以在源代码中嵌入一些补充信息,这种补充信息称为注解(Annotation),是 Java 平台中非常重要的一部分。注解都是 @ 符号开头的,例如我们在学习方法重写时使用过的 @Override 注解。同 Class 和 Interface 一样,注解也属于一种类型。
Annotation 可以翻译为“注解”或“注释”,一般翻译为“注解”,因为“注释”一词已经用于说明“//”、“/**...*/”和“/*...*/”等符号了,这里的“注释”是英文 Comment 翻译。
注解并不能改变程序的运行结果,也不会影响程序运行的性能。有些注解可以在编译时给用户提示或警告,有的注解可以在运行时读写字节码文件信息。
注解常见的作用有以下几种:
1
、生成帮助文档。这是最常见的,也是 Java 最早提供的注解。常用的有 @see、@param 和 @return 等;
2
、跟踪代码依赖性,实现替代配置文件功能。比较常见的是 Spring 2.5 开始的基于注解配置。作用就是减少配置。现在的框架基本都使用了这种配置来减少配置文件的数量;
3
、在编译时进行格式检查。如把 @Override 注解放在方法前,如果这个方法并不是重写了父类方法,则编译时就能检查出。
注解可以元数据这个词来描述,即一种描述数据的数据。所以可以说注解就是源代码的元数据
无论是哪一种注解,本质上都一种数据类型,是一种接口类型。到 Java 8 为止 Java SE 提供了 11 个内置注解。其中有 5 个是基本注解,它们来自于 java.lang 包。有 6 个是元注解,它们来自于 java.lang.annotation 包,自定义注解会用到元注解。
提示:元注解就是负责注解其他的注解。
基本注解包括:@Override、@Deprecated、@SuppressWarnings、@SafeVarargs 和 @FunctionalInterface。
二、@Override注解
Java
中 @Override 注解是用来指定方法重写的,只能修饰方法并且只能用于方法重写,不能修饰其它的元素。它可以强制一个子类必须重写父类方法或者实现接口的方法。
代码实例:
|
该方法使用 @Override 注解。如果 toString() 不小心写成了 t0String()
那么程序会发生编译错误。会有如下的代码提示:
类型为 Person 的方法t0String()必须覆盖或实现超类型方法
所以 @Override 的作用是告诉编译器检查这个方法,保证父类要包含一个被该方法重写的方法
否则就会编译出错。这样可以帮助程序员避免一些低级错误。
当然如果代码中的方法前面不加 @Override 注解,即便是方法编辑错误了,编译器也不会有提示。这时 Object 父类的 toString() 方法并没有被重写,将会引起程序出现 Bug(缺陷)。
三、@Deprecated注解
Java
中 @Deprecated 可以用来注解类、接口、成员方法和成员变量等,用于表示某个元素(类、方法等)已过时。当其他程序使用已过时的元素时,编译器将会给出警告。
|
新建Demo01
,
创建对象调用add方法
|
|
四、@SuppressWarnings:抑制编译器警告
Java
中的 @SuppressWarnings 注解指示被该注解修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告,且会一直作用于该程序元素的所有子元素。例如,使用 @SuppressWarnings 修饰某个类取消显示某个编译器警告,同时又修饰该类里的某个方法取消显示另一个编译器警告,那么该方法将会同时取消显示这两个编译器警告。
@SuppressWarnings
注解主要用在取消一些编译器产生的警告对代码左侧行列的遮挡,有时候这样会挡住我们断点调试时打的断点。
如果你确认程序中的警告没有问题,可以不用理会。通常情况下,如果程序中使用没有泛型限制的集合将会引起编译器警告,为了避免这种编译器警告,可以使用 @SuppressWarnings 注解消除这些警告。
注解的使用有以下三种:
抑制单类型的警告:@SuppressWarnings("unchecked")
抑制多类型的警告:@SuppressWarnings("unchecked","rawtypes")
抑制所有类型的警告:@SuppressWarnings("unchecked")
抑制警告的关键字如下表所示。
|
关键字 | 用途 |
| ------------------------ | ------------------------------------------------------ |
| all |
抑制所有警告 |
| boxing |
抑制装箱、拆箱操作时候的警告 |
| cast |
抑制映射相关的警告 |
| dep-ann |
抑制启用注释的警告 |
| deprecation |
抑制过期方法警告 |
| fallthrough |
抑制在 switch 中缺失 breaks 的警告 |
| finally |
抑制 finally 模块没有返回的警告 |
| hiding |
抑制相对于隐藏变量的局部变量的警告 |
| incomplete-switch |
忽略不完整的 switch 语句 |
| nls |
忽略非 nls 格式的字符 |
| null |
忽略对 null 的操作 |
| rawtypes |
使用 generics 时忽略没有指定相应的类型 |
| restriction |
抑制禁止使用劝阻或禁止引用的警告 |
| serial |
忽略在 serializable 类中没有声明 serialVersionUID 变量 |
| static-access |
抑制不正确的静态访问方式警告 |
| synthetic-access |
抑制子类没有按最优方法访问内部类的警告 |
| unchecked |
抑制没有进行类型检查操作的警告 |
| unqualified-field-access |
抑制没有权限访问的域的警告 |
| unused |
抑制没被使用过的代码的警告 |
代码实现;
1
、抑制过期方法的警告
|
@SuppressWarnings("all")
抑制所有警告
五、@SafeVarargs注解
在介绍 @SafeVarargs 注解用法之前,先来看看如下代码:
```java
public class HelloWorld {
public static void main(String[] args) {
// 传递可变参数,参数是泛型集合
display(10, 20, 30);
// 传递可变参数,参数是非泛型集合
display("10", 20, 30); // 会有编译警告
}
public static void display(T... array) {
for (T arg : array) {
System.out.println(arg.getClass().getName() + ":" + arg);
}
}
}
```
代码第 10 行声明了一种可变参数方法 display,display 方法参数个数可以变化,它可以接受不确定数量的相同类型的参数。可以通过在参数类型名后面加入
`...`的方式来表示这是可变参数。可变参数方法中的参数类型相同,为此声明参数是需要指定泛型。
但是调用可变参数方法时,应该提供相同类型的参数,代码第 4 行调用时没有警告,而代码第 6 行调用时则会发生警告,这个警告是 unchecked(未检查不安全代码),就是因为将非泛型变量赋值给泛型变量所发生的。
可用 @SafeVarargs 注解抑制编译器警告,修改代码如下:
```java
public class HelloWorld {
public static void main(String[] args) {
// 传递可变参数,参数是泛型集合
display(10, 20, 30);
// 传递可变参数,参数是非泛型集合
display("10", 20, 30); // 没有@SafeVarargs会有编译警告
}
@SafeVarargs
public static void display(T... array) {
for (T arg : array) {
System.out.println(arg.getClass().getName() + ":" + arg);
}
}
}
```
上述代码在可变参数 display 前添加了 @SafeVarargs 注解,当然也可以使用 @SuppressWarnings("unchecked") 注解,但是两者相比较来说 @SafeVarargs 注解更适合。
注意:@SafeVarargs注解不适用于非 static 或非 final 声明的方法,对于未声明为 static 或 final 的方法,如果要抑制 unchecked 警告,可
六、@FunctionalInterface注解
在学习 Lambda 表达式时,我们提到如果接口中只有一个抽象方法(可以包含多个默认方法或多个 static 方法),那么该接口就是函数式接口。@FunctionalInterface 就是用来指定某个接口必须是函数式接口,所以 @FunInterface 只能修饰接口,不能修饰其它程序元素。
函数式接口就是为 Java 8 的 Lambda 表达式准备的,Java 8 允许使用 Lambda 表达式创建函数式接口的实例,因此 Java 8 专门增加了 @FunctionalInterface。
代码实例;//抑制函数式接口只能声明一个方法
首先
|
七、Java 元注解作用及使用
元注解是负责对其它注解进行说明的注解,自定义注解时可以使用元注解。Java 5 定义了 4 个注解,分别是 @Documented、@Target、@Retention 和 @Inherited。Java 8 又增加了 @Repeatable 和 @Native 两个注解。这些注解都可以在 java.lang.annotation 包中找到。下面主要介绍每个元注解的作用及使用。
## 1、@Documented
@Documented
是一个标记注解,没有成员变量。用 @Documented 注解修饰的注解类会被 JavaDoc 工具提取成文档。默认情况下,JavaDoc 是不包括注解的,但如果声明注解时指定了 @Documented,就会被 JavaDoc 之类的工具处理,所以注解类型信息就会被包括在生成的帮助文档中。
代码示例:自定义注解
|
完成在命令指令符中生成自己的API文档语法;Javadoc-encoding UTF-8 -charest UTF-8 *java
2、@Target
@Target 注解用来指定一个注解的使用范围,即被 @Target 修饰的注解可以用在什么地方。@Target 注解有一个成员变量(value)用来设置适用目标,value 是 java.lang.annotation.ElementType 枚举类型的数组,下表为 ElementType 常用的枚举常量。
| 名称 | 说明 |
| -------------- | ---------------------------------------- |
| CONSTRUCTOR | 用于构造方法 |
| FIELD | 用于成员变量(包括枚举常量) |
| LOCAL_VARIABLE | 用于局部变量 |
| METHOD | 用于方法 |
| PACKAGE | 用于包 |
| PARAMETER | 用于类型参数(JDK 1.8新增) |
| TYPE | 用于类、接口(包括注解类型)或 enum 声明 |
**例 2:**
自定义一个 MyTarget 注解,使用范围为方法,代码如下所示。
```java
@Target({ ElementType.METHOD })
public @interface MyTarget {
}
class Test {
@MyTarget
String name;
}
```
如上代码第 6 行会编译错误,错误信息为:
The annotation @MyTarget is disallowed for this location
提示此位置不允许使用注解 @MyDocumented,@MyTarget 不能修饰成员变量,只能修饰方法。
## 3、@Retention
@Retention 用于描述注解的生命周期,也就是该注解被保留的时间长短。@Retention 注解中的成员变量(value)用来设置保留策略,value 是 java.lang.annotation.RetentionPolicy 枚举类型,RetentionPolicy 有 3 个枚举常量,如下所示。
**SOURCE:**在源文件中有效(即源文件保留)
**CLASS:**在 class 文件中有效(即 class 保留)
**RUNTIME:**在运行时有效(即运行时保留)
生命周期大小排序为 SOURCE < CLASS < RUNTIME,前者能使用的地方后者一定也能使用。如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS 注解;如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,则可选用 SOURCE 注解。
@Inherited
@Inherited 是一个标记注解,用来指定该注解可以被继承。使用 @Inherited 注解的 Class 类,表示这个注解可以被用于该 Class 类的子类。就是说如果某个类使用了被 @Inherited 修饰的注解,则其子类将自动具有该注解。
**例 3:**
创建一个自定义注解,代码如下所示:
```java
@Target({ ElementType.TYPE })
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MyInherited {
}
```
测试类代码如下:
```java
@MyInherited
public class TestA {
public static void main(String[] args) {
System.out.println(TestA.class.getAnnotation(MyInherited.class));
System.out.println(TestB.class.getAnnotation(MyInherited.class));
System.out.println(TestC.class.getAnnotation(MyInherited.class));
}
}
class TestB extends TestA {
}
class TestC extends TestB {
}
```
运行结果为:
```
@MyInherited()
@MyInherited()
@MyInherited()
```
## 4、@Repeatable
@Repeatable 注解是 Java 8 新增加的,它允许在相同的程序元素中重复注解,在需要对同一种注解多次使用时,往往需要借助 @Repeatable 注解。Java 8 版本以前,同一个程序元素前最多只能有一个相同类型的注解,如果需要在同一个元素前使用多个相同类型的注解,则必须使用注解“容器”。
**例 4:**
Java 8 之前的做法:
```java
public @interface Roles {
Role[] roles();
}
public @interface Roles {
Role[] value();
}
public class RoleTest {
@Roles(roles = {@Role(roleName = "role1"), @Role(roleName = "role2")})
public String doString(){
return "这是C语言中国网Java教程";
}
}
```
Java 8 之后增加了重复注解,使用方式如下:
```java
public @interface Roles {
Role[] value();
}
@Repeatable(Roles.class)
public @interface Role {
String roleName();
}
public class RoleTest {
@Role(roleName = "role1")
@Role(roleName = "role2")
public String doString(){
return "这是C语言中文网Java教程";
}
}
```
不同的地方是,创建重复注解 Role 时加上了 @Repeatable 注解,指向存储注解 Roles,这样在使用时就可以直接重复使用 Role 注解。从上面例子看出,使用 @Repeatable 注解更符合常规思维,可读性强一点。
两种方法获得的效果相同。重复注解只是一种简化写法,这种简化写法是一种假象,多个重复注解其实会被作为“容器”注解的 value 成员的数组元素处理。
## 5、@Native
使用 @Native 注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。对于 @Native 注解不常使用,了解即可。
# 八、Java 自定义注解
声明自定义注解使用 @interface 关键字(interface 关键字前加 @ 符号)实现。定义注解与定义接口非常像,如下代码可定义一个简单形式的注解类型。
```java
// 定义一个简单的注解类型
public @interface Test {}
```
上述代码声明了一个 Test 注解。默认情况下,注解可以在程序的任何地方使用,通常用于修饰类、接口、方法和变量等。
定义注解和定义类相似,注解前面的访问修饰符和类一样有两种,分别是公有访问权限(public)和默认访问权限(默认不写)。一个源程序文件中可以声明多个注解,但只能有一个是公有访问权限的注解。且源程序文件命名和公有访问权限的注解名一致。
不包含任何成员变量的注解称为标记注解,例如上面声明的 Test 注解以及基本注解中的 @Override 注解都属于标记注解。根据需要,注解中可以定义成员变量,成员变量以无形参的方法形式来声明,其方法名和返回值定义了该成员变量的名字和类型。代码如下所示:
```java
public @interface MyTag {
// 定义带两个成员变量的注解
// 注解中的成员变量以方法的形式来定义
String name();
int age();
}
```
以上代码中声明了一个 MyTag 注解,定义了两个成员变量,分别是 name 和 age。成员变量也可以有访问权限修饰符,但是只能有公有权限和默认权限。
如果在注解里定义了成员变量,那么使用该注解时就应该为它的成员变量指定值,如下代码所示。
```java
public class Test {
// 使用带成员变量的注解时,需要为成员变量赋值
@MyTag(name="xx", age=6)
public void info() {
...
}
...
}
```
注解中的成员变量也可以有默认值,可使用 default 关键字。如下代码定义了 @MyTag 注解,该注解里包含了 name 和 age 两个成员变量。
```java
public @interface MyTag {
// 定义了两个成员变量的注解
// 使用default为两个成员变量指定初始值
String name() default "中关村";
int age() default 7;}
```
如果为注解的成员变量指定了默认值,那么使用该注解时就可以不为这些成员变量赋值,而是直接使用默认值。
```java
public class Test {
// 使用带成员变量的注解
// MyTag注释的成员变量有默认值,所以可以不为它的成员变量赋值
@MyTag
public void info() {
...
}
...
}
```
当然也可以在使用 MyTag 注解时为成员变量指定值,如果为 MyTag 的成员变量指定了值,则默认值不会起作用。
根据注解是否包含成员变量,可以分为如下两类。
1. 标记注解:没有定义成员变量的注解类型被称为标记注解。这种注解仅利用自身的存在与否来提供信息,如前面介绍的 @Override、@Test 等都是标记注解。
2. 元数据注解:包含成员变量的注解,因为它们可以接受更多的元数据,所以也被称为元数据注解。
Java 数据结构
Java工具包提供了强大的数据结构。在Java中的数据结构主要包括以下几种接口和类:
- 枚举(Enumeration)
- 位集合(BitSet)
- 向量(Vector)
- 栈(Stack)
- 字典(Dictionary)
- 哈希表(Hashtable)
- 属性(Properties)
# 一、枚举(Enumeration)
枚举(Enumeration)接口虽然它本身不属于数据结构,但它在其他数据结构的范畴里应用很广。 枚举(The Enumeration)接口定义了一种从数据结构中取回连续元素的方式。
例如,枚举定义了一个叫nextElement 的方法,该方法用来得到一个包含多元素的数据结构的下一个元素。
Enumeration接口中定义了一些方法,通过这些方法可以枚举(一次获得一个)对象集合中的元素。
这种传统接口已被迭代器取代,虽然Enumeration 还未被遗弃,但在现代代码中已经被很少使用了。尽管如此,它还是使用在诸如Vector和Properties这些传统类所定义的方法中,除此之外,还用在一些API类,并且在应用程序中也广泛被使用。 下表总结了一些Enumeration声明的方法:
| 方法 | **方法描述** |
| :----------------------------- | :----------------------------------------------------------: |
| **boolean hasMoreElements( )** | 测试此枚举是否包含更多的元素。 |
| **Object nextElement( )** | 如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。 |
**实例:**
以下实例演示了Enumeration的使用:
```java
import java.util.Vector;
import java.util.Enumeration;
public class EnumerationTester {
public static void main(String args[]) {
Enumeration
Vector
dayNames.add("Sunday");
dayNames.add("Monday");
dayNames.add("Tuesday");
dayNames.add("Wednesday");
dayNames.add("Thursday");
dayNames.add("Friday");
dayNames.add("Saturday");
days = dayNames.elements();
while (days.hasMoreElements()){
System.out.println(days.nextElement());
}
}
}
```
# 二、位集合(BitSet)
位集合类实现了一组可以单独设置和清除的位或标志。该类在处理一组布尔值的时候非常有用,只需要给每个值赋值一"位",然后对位进行适当的设置或清除,就可以对布尔值进行操作了。
一个Bitset类创建一种特殊类型的数组来保存位值。BitSet中数组大小会随需要增加。这和位向量(vector of bits)比较类似。这是一个传统的类,但它在Java 2中被完全重新设计。
BitSet定义了两个构造方法。
第一个构造方法创建一个默认的对象:
```java
BitSet()
```
第二个方法允许用户指定初始大小。所有位初始化为0。
```java
BitSet(int size)
```
BitSet中实现了Cloneable接口中定义的方法如下表所列:
| 方法 | 方法描述 |
| :------------------------------------------------ | :----------------------------------------------------------- |
| void and(BitSet set) | 对此目标位 set 和参数位 set 执行逻辑与操作。 |
| void andNot(BitSet set) | 清除此 BitSet 中所有的位,其相应的位在指定的 BitSet 中已设置。 |
| int cardinality( ) | 返回此 BitSet 中设置为 true 的位数。 |
| void clear( ) | 将此 BitSet 中的所有位设置为 false。 |
| void clear(int index) | 将索引指定处的位设置为 false。 |
| void clear(int startIndex, int endIndex) | 将指定的 startIndex(包括)到指定的 toIndex(不包括)范围内的位设置为 false。 |
| Object clone( ) | 复制此 BitSet,生成一个与之相等的新 BitSet。 |
| boolean equals(Object bitSet) | 将此对象与指定的对象进行比较。 |
| void flip(int index) | 将指定索引处的位设置为其当前值的补码。 |
| void flip(int startIndex, int endIndex) | 将指定的 fromIndex(包括)到指定的 toIndex(不包括)范围内的每个位设置为其当前值的补码。 |
| boolean get(int index) | 返回指定索引处的位值。 |
| BitSet get(int startIndex, int endIndex) | 返回一个新的 BitSet,它由此 BitSet 中从 fromIndex(包括)到 toIndex(不包括)范围内的位组成。 |
| int hashCode( ) | 返回此位 set 的哈希码值。 |
| boolean intersects(BitSet bitSet) | 如果指定的 BitSet 中有设置为 true 的位,并且在此 BitSet 中也将其设置为 true,则返回 true。 |
| boolean isEmpty( ) | 如果此 BitSet 中没有包含任何设置为 true 的位,则返回 true。 |
| int length( ) | 返回此 BitSet 的"逻辑大小":BitSet 中最高设置位的索引加 1。 |
| int nextClearBit(int startIndex) | 返回第一个设置为 false 的位的索引,这发生在指定的起始索引或之后的索引上。 |
| int nextSetBit(int startIndex) | 返回第一个设置为 true 的位的索引,这发生在指定的起始索引或之后的索引上。 |
| void or(BitSet bitSet) | 对此位 set 和位 set 参数执行逻辑或操作。 |
| void set(int index) | 将指定索引处的位设置为 true。 |
| void set(int index, boolean v) | 将指定索引处的位设置为指定的值。 |
| void set(int startIndex, int endIndex) | 将指定的 fromIndex(包括)到指定的 toIndex(不包括)范围内的位设置为 true。 |
| void set(int startIndex, int endIndex, boolean v) | 将指定的 fromIndex(包括)到指定的 toIndex(不包括)范围内的位设置为指定的值。 |
| int size( ) | 返回此 BitSet 表示位值时实际使用空间的位数。 |
| String toString( ) | 返回此位 set 的字符串表示形式。 |
| void xor(BitSet bitSet) | 对此位 set 和位 set 参数执行逻辑异或操作。 |
**实例:**
下面的程序说明这个数据结构支持的几个方法:
```java
import java.util.BitSet;
public class BitSetDemo {
public static void main(String args[]) {
BitSet bits1 = new BitSet(16);
BitSet bits2 = new BitSet(16);
// set some bits
for(int i=0; i<16; i++) {
if((i%2) == 0) bits1.set(i);
if((i%5) != 0) bits2.set(i);
}
System.out.println("Initial pattern in bits1: ");
System.out.println(bits1);
System.out.println("\nInitial pattern in bits2: ");
System.out.println(bits2);
// AND bits
bits2.and(bits1);
System.out.println("\nbits2 AND bits1: ");
System.out.println(bits2);
// OR bits
bits2.or(bits1);
System.out.println("\nbits2 OR bits1: ");
System.out.println(bits2);
// XOR bits
bits2.xor(bits1);
System.out.println("\nbits2 XOR bits1: ");
System.out.println(bits2);
}
}
```
# 三、向量(Vector)
向量(Vector)类和传统数组非常相似,但是Vector的大小能根据需要动态的变化。和数组一样,Vector对象的元素也能通过索引访问。使用Vector类最主要的好处就是在创建对象的时候不必给对象指定大小,它的大小会根据需要动态的变化。
Vector 类实现了一个动态数组。和 ArrayList 很相似,但是两者是不同的:
1、Vector 是同步访问的。
2、Vector 包含了许多传统的方法,这些方法不属于集合框架。
Vector 主要用在事先不知道数组的大小,或者只是需要一个可以改变大小的数组的情况。Vector 类支持 4 种构造方法。第一种构造方法创建一个默认的向量,默认大小为 10:
```
Vector()
```
第二种构造方法创建指定大小的向量。
```
Vector(int size)
```
第三种构造方法创建指定大小的向量,并且增量用 incr 指定。增量表示向量每次增加的元素数目。
```
Vector(int size,int incr)
```
第四种构造方法创建一个包含集合 c 元素的向量:
```
Vector(Collection c)
```
除了从父类继承的方法外 Vector 还定义了以下方法:
| 方法 | 方法描述 |
| :----------------------------------------------------- | :----------------------------------------------------------- |
| void add(int index, Object element) | 在此向量的指定位置插入指定的元素。 |
| boolean add(Object o) | 将指定元素添加到此向量的末尾。 |
| boolean addAll(Collection c) | 将指定 Collection 中的所有元素添加到此向量的末尾,按照指定 collection 的迭代器所返回的顺序添加这些元素。 |
| boolean addAll(int index, Collection c) | 在指定位置将指定 Collection 中的所有元素插入到此向量中。 |
| void addElement(Object obj) | 将指定的组件添加到此向量的末尾,将其大小增加 1。 |
| int capacity() | 返回此向量的当前容量。 |
| void clear() | 从此向量中移除所有元素。 |
| Object clone() | 返回向量的一个副本。 |
| boolean contains(Object elem) | 如果此向量包含指定的元素,则返回 true。 |
| boolean containsAll(Collection c) | 如果此向量包含指定 Collection 中的所有元素,则返回 true。 |
| void copyInto(Object[] anArray) | 将此向量的组件复制到指定的数组中。 |
| Object elementAt(int index) | 返回指定索引处的组件。 |
| Enumeration elements() | 返回此向量的组件的枚举。 |
| void ensureCapacity(int minCapacity) | 增加此向量的容量(如有必要),以确保其至少能够保存最小容量参数指定的组件数。 |
| boolean equals(Object o) | 比较指定对象与此向量的相等性。 |
| Object firstElement() | 返回此向量的第一个组件(位于索引 0) 处的项)。 |
| Object get(int index) | 返回向量中指定位置的元素。 |
| int hashCode() | 返回此向量的哈希码值。 |
| int indexOf(Object elem) | 返回此向量中第一次出现的指定元素的索引,如果此向量不包含该元素,则返回 -1。 |
| int indexOf(Object elem, int index) | 返回此向量中第一次出现的指定元素的索引,从 index 处正向搜索,如果未找到该元素,则返回 -1。 |
| void insertElementAt(Object obj, int index) | 将指定对象作为此向量中的组件插入到指定的 index 处。 |
| boolean isEmpty() | 测试此向量是否不包含组件。 |
| Object lastElement() | 返回此向量的最后一个组件。 |
| int lastIndexOf(Object elem) | 返回此向量中最后一次出现的指定元素的索引;如果此向量不包含该元素,则返回 -1。 |
| int lastIndexOf(Object elem, int index) | 返回此向量中最后一次出现的指定元素的索引,从 index 处逆向搜索,如果未找到该元素,则返回 -1。 |
| Object remove(int index) | 移除此向量中指定位置的元素。 |
| boolean remove(Object o) | 移除此向量中指定元素的第一个匹配项,如果向量不包含该元素,则元素保持不变。 |
| boolean removeAll(Collection c) | 从此向量中移除包含在指定 Collection 中的所有元素。 |
| void removeAllElements() | 从此向量中移除全部组件,并将其大小设置为零。 |
| boolean removeElement(Object obj) | 从此向量中移除变量的第一个(索引最小的)匹配项。 |
| void removeElementAt(int index) | 删除指定索引处的组件。 |
| protected void removeRange(int fromIndex, int toIndex) | 从此 List 中移除其索引位于 fromIndex(包括)与 toIndex(不包括)之间的所有元素。 |
| boolean retainAll(Collection c) | 在此向量中仅保留包含在指定 Collection 中的元素。 |
| Object set(int index, Object element) | 用指定的元素替换此向量中指定位置处的元素。 |
| void setElementAt(Object obj, int index) | 将此向量指定 index 处的组件设置为指定的对象。 |
| void setSize(int newSize) | 设置此向量的大小。 |
| int size() | 返回此向量中的组件数。 |
| List subList(int fromIndex, int toIndex) | 返回此 List 的部分视图,元素范围为从 fromIndex(包括)到 toIndex(不包括)。 |
| Object[] toArray() | 返回一个数组,包含此向量中以恰当顺序存放的所有元素。 |
| Object[] toArray(Object[] a) | 返回一个数组,包含此向量中以恰当顺序存放的所有元素;返回数组的运行时类型为指定数组的类型。 |
| String toString() | 返回此向量的字符串表示形式,其中包含每个元素的 String 表示形式。 |
| void trimToSize() | 对此向量的容量进行微调,使其等于向量的当前大小。 |
四、栈(Stack)
栈(Stack)实现了一个**后进先出**(LIFO)的数据结构。可以把栈理解为对象的垂直分布的栈,当你添加一个新元素时,就将新元素放在其他元素的顶部。
栈是Vector的一个子类,它实现了一个标准的后进先出的栈。
堆栈只定义了默认构造函数,用来创建一个空栈。 堆栈除了包括由Vector定义的所有方法,也定义了自己的一些方法。
```
Stack()
```
除了由Vector定义的所有方法,自己也定义了一些方法:
| 方法 | 方法描述 |
| :----------------------------- | :----------------------------------------------- |
| boolean empty() | 测试堆栈是否为空。 |
| Object peek( ) | 查看堆栈顶部的对象,但不从堆栈中移除它。 |
| Object pop( ) | 移除堆栈顶部的对象,并作为此函数的值返回该对象。 |
| Object push(Object element) 把 | 项压入堆栈顶部。 |
| int search(Object element) | 返回对象在堆栈中的位置,以 1 为基数。 |
**实例:**
下面的程序说明这个集合所支持的几种方法
```java
import java.util.*;
public class StackDemo {
static void showpush(Stack
st.push(new Integer(a));
System.out.println("push(" + a + ")");
System.out.println("stack: " + st);
}
static void showpop(Stack
System.out.print("pop -> ");
Integer a = (Integer) st.pop();
System.out.println(a);
System.out.println("stack: " + st);
}
public static void main(String args[]) {
Stack
System.out.println("stack: " + st);
showpush(st, 42);
showpush(st, 66);
showpush(st, 99);
showpop(st);
showpop(st);
showpop(st);
try {
showpop(st);
} catch (EmptyStackException e) {
System.out.println("empty stack");
}
}
}
六、哈希表(Hashtable)
Hashtable类提供了一种在用户定义键结构的基础上来组织数据的手段。例如,在地址列表的哈希表中,你可以根据邮政编码作为键来存储和排序数据,而不是通过人名。哈希表键的具体含义完全取决于哈希表的使用情景和它包含的数据。
Hashtable是原始的java.util的一部分, 是一个Dictionary具体的实现 。然而,Java 2 重构的Hashtable实现了Map接口,因此,Hashtable现在集成到了集合框架中。它和HashMap类很相似,但是它支持同步。像HashMap一样,Hashtable在哈希表中存储键/值对。当使用一个哈希表,要指定用作键的对象,以及要链接到该键的值。然后,该键经过哈希处理,所得到的散列码被用作存储在该表中值的索引。
Hashtable定义了四个构造方法。第一个是默认构造方法:
```
Hashtable()
```
第二个构造函数创建指定大小的哈希表:
```
Hashtable(int size)
```
第三个构造方法创建了一个指定大小的哈希表,并且通过fillRatio指定填充比例。填充比例必须介于0.0和1.0之间,它决定了哈希表在重新调整大小之前的充满程度:
```
Hashtable(int size,float fillRatio)
```
第四个构造方法创建了一个以M中元素为初始化元素的哈希表。哈希表的容量被设置为M的两倍。
```
Hashtable(Map m)
```
Hashtable中除了从Map接口中定义的方法外,还定义了以下方法:
| **方法** | **方法描述** |
| :--------------------------------------- | :----------------------------------------------------------- |
| **void clear( )** | 将此哈希表清空,使其不包含任何键。 |
| **Object clone( )** | 创建此哈希表的浅表副本。 |
| **boolean contains(Object value)** | 测试此映射表中是否存在与指定值关联的键。 |
| **boolean containsKey(Object key)** | 测试指定对象是否为此哈希表中的键。 |
| **boolean containsValue(Object value)** | 如果此 Hashtable 将一个或多个键映射到此值,则返回 true。 |
| **Enumeration elements( )** | 返回此哈希表中的值的枚举。 |
| **Object get(Object key)** | 返回指定键所映射到的值,如果此映射不包含此键的映射,则返回 null. 更确切地讲,如果此映射包含满足 (key.equals(k)) 的从键 k 到值 v 的映射,则此方法返回 v;否则,返回 null。 |
| **boolean isEmpty( )** | 测试此哈希表是否没有键映射到值。 |
| **Enumeration keys( )** | 返回此哈希表中的键的枚举。 |
| **Object put(Object key, Object value)** | 将指定 key 映射到此哈希表中的指定 value。 |
| **void rehash( )** | 增加此哈希表的容量并在内部对其进行重组,以便更有效地容纳和访问其元素。 |
| **Object remove(Object key)** | 从哈希表中移除该键及其相应的值。 |
| **int size( )** | 返回此哈希表中的键的数量。 |
| **String toString( )** | 返回此 Hashtable 对象的字符串表示形式,其形式为 ASCII 字符 ", " (逗号加空格)分隔开的、括在括号中的一组条目。 |
**实例:**
下面的程序说明这个数据结构支持的几个方法:
```java
import java.util.*;
public class HashTableDemo {
public static void main(String args[]) {
// Create a hash map
Hashtable balance = new Hashtable();
Enumeration names;
String str;
double bal;
balance.put("Zara", new Double(3434.34));
balance.put("Mahnaz", new Double(123.22));
balance.put("Ayan", new Double(1378.00));
balance.put("Daisy", new Double(99.22));
balance.put("Qadir", new Double(-19.08));
// Show all balances in hash table.
names = balance.keys();
while(names.hasMoreElements()) {
str = (String) names.nextElement();
System.out.println(str + ": " +
balance.get(str));
}
System.out.println();
// Deposit 1,000 into Zara's account
bal = ((Double)balance.get("Zara")).doubleValue();
balance.put("Zara", new Double(bal+1000));
System.out.println("Zara's new balance: " +
balance.get("Zara"));
}
}
```
属性:properties
Properties 继承于 Hashtable.Properties 类表示了一个持久的属性集.属性列表中每个键及其对应值都是一个字符串。Properties 类被许多Java类使用。例如,在获取环境变量时它就作为System.getProperties()方法的返回值。
Properties 继承于 Hashtable。表示一个持久的属性集.属性列表中每个键及其对应值都是一个字符串。Properties 类被许多 Java 类使用。例如,在获取环境变量时它就作为 System.getProperties() 方法的返回值。Properties 定义如下实例变量.这个变量持有一个 Properties 对象相关的默认属性列表。
```
Properties defaults;
```
Properties类定义了两个构造方法. 第一个构造方法没有默认值。
```
Properties()
```
第二个构造方法使用propDefault 作为默认值。两种情况下,属性列表都为空:
```
Properties(Properties propDefault)
```
除了从 Hashtable 中所定义的方法,Properties 还定义了以下方法:
| **方法** | **方法描述** |
| :--------------------------------------------------------- | :----------------------------------------------------------- |
| **String getProperty(String key)** | 用指定的键在此属性列表中搜索属性。 |
| **String getProperty(String key, String defaultProperty)** | 用指定的键在属性列表中搜索属性。 |
| **void list(PrintStream streamOut)** | 将属性列表输出到指定的输出流。 |
| **void list(PrintWriter streamOut)** | 将属性列表输出到指定的输出流。 |
| **void load(InputStream streamIn) throws IOException** | 从输入流中读取属性列表(键和元素对)。 |
| **Enumeration propertyNames( )** | 按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。 |
| **Object setProperty(String key, String value)** | 调用 Hashtable 的方法 put。 |
| **void store(OutputStream streamOut, String description)** | 以适合使用 load(InputStream)方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。 |
代码示例:
import java.io.*;
import java.util.Properties;
public class Demo01 {
public static void main(String[] args) throws Exception {
//创建properties对象
Properties p = new Properties();
FileOutputStream fow =new FileOutputStream("./dir/p1.properties");
OutputStreamWriter osw = new OutputStreamWriter(fow,"UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
p.setProperty("DC","我是陈狗");
PrintWriter w = new PrintWriter(bw,true);
p.store(w,"自定义properties文件数据");
}
} |
队列(Queue)
Queue是java中实现队列的接口,它总共只有6个方法,我们一般只用其中3个就可以了。Queue的实现类有LinkedList和PriorityQueue。最常用的实现类是LinkedList。
Queue的6个方法分类:
压入元素(添加):add()、offer()
相同:未超出容量,从队尾压入元素,返回压入的那个元素。
区别:在超出容量时,add()方法会对抛出异常,offer()返回false
弹出元素(删除):remove()、poll()
相同:容量大于0的时候,删除并返回队头被删除的那个元素。
区别:在容量为0的时候,remove()会抛出异常,poll()返回false
获取队头元素(不删除):element()、peek()
相同:容量大于0的时候,都返回队头元素。但是不删除。
区别:容量为0的时候,element()会抛出异常,peek()返回null。
队列除了基本的 Collection 操作外,还提供特有的插入、提取和检查操作(如上)。每个方法都存在两种形式:一种抛出异常(操作失败时),另一种返回一个特殊值(null 或 false,具体取决于操作)。插入操作的后一种形式是用于专门为有容量限制的 Queue 实现设计的;在大多数实现中,插入操作不会失败。
| | 抛出异常 | 返回特殊值 |
| ---- | --------- | ---------- |
| 插入 | add(e) | offer(e) |
| 删除 | remove() | poll() |
| 检查 | element() | peek() |
队列通常(但并非一定)以 FIFO(先进先出)的方式排序各个元素。不过优先级队列和 LIFO 队列(或堆栈)例外,前者根据提供的比较器或元素的自然顺序对元素进行排序,后者按 LIFO(后进先出)的方式对元素进行排序。无论使用哪种排序方式,队列的头 都是调用 remove() 或 poll() 所移除的元素。在 FIFO 队列中,所有的新元素都插入队列的末尾。其他种类的队列可能使用不同的元素放置规则。每个 Queue 实现必须指定其顺序属性。
如果可能,offer 方法可插入一个元素,否则返回 false。这与 Collection.add 方法不同,该方法只能通过抛出未经检查的异常使添加元素失败。offer 方法设计用于正常的失败情况,而不是出现异常的情况,例如在容量固定(有界)的队列中。
remove() 和 poll() 方法可移除和返回队列的头。到底从队列中移除哪个元素是队列排序策略的功能,而该策略在各种实现中是不同的。remove() 和 poll() 方法仅在队列为空时其行为有所不同:remove() 方法抛出一个异常,而 poll() 方法则返回 null。
element() 和 peek() 返回但不移除队列的头。
Queue 接口并未定义阻塞队列的方法,而这在并发编程中是很常见的。BlockingQueue 接口定义了那些等待元素出现或等待队列中有可用空间的方法,这些方法扩展了此接口。
Queue 实现通常不允许插入 null 元素,尽管某些实现(如 LinkedList)并不禁止插入 null。即使在允许 null 的实现中,也不应该将 null 插入到 Queue 中,因为 null 也用作 poll 方法的一个特殊返回值,表明队列不包含元素。
Queue 实现通常未定义 equals 和 hashCode 方法的基于元素的版本,而是从 Object 类继承了基于身份的版本,因为对于具有相同元素但有不同排序属性的队列而言,基于元素的相等性并非总是定义良好的。
看一个简单的示例:
```java
public class QueueTest {
public static void main(String[] args) {
Queue
queue.offer("元素A");
queue.offer("元素B");
queue.offer("元素C");
queue.offer("元素D");
queue.offer("元素E");
while (queue.size() > 0) {
String element = queue.poll();
System.out.println(element);
}
}
}
```
```java
import java.util.LinkedList;
import java.util.Queue;
public class Main {
public static void main(String[] args) {
//add()和remove()方法在失败的时候会抛出异常(不推荐)
Queue
//添加元素
queue.offer("a");
queue.offer("b");
queue.offer("c");
queue.offer("d");
queue.offer("e");
for(String q : queue){
System.out.println(q);
}
System.out.println("===");
System.out.println("poll="+queue.poll()); //返回第一个元素,并在队列中删除
for(String q : queue){
System.out.println(q);
}
System.out.println("===");
System.out.println("element="+queue.element()); //返回第一个元素
for(String q : queue){
System.out.println(q);
}
System.out.println("===");
System.out.println("peek="+queue.peek()); //返回第一个元素
for(String q : queue){
System.out.println(q);
}
}
}
```
**offer,add 区别:**
一些队列有大小限制,因此如果想在一个满的队列中加入一个新项,多出的项就会被拒绝。
这时新的 offer 方法就可以起作用了。它不是对调用 add() 方法抛出一个 unchecked 异常,而只是得到由 offer() 返回的 false。
**poll,remove 区别:**
remove() 和 poll() 方法都是从队列中删除第一个元素。remove() 的行为与 Collection 接口的版本相似, 但是新的 poll() 方法在用空集合调用时不是抛出异常,只是返回 null。因此新的方法更适合容易出现异常条件的情况。
**peek,element区别:**
element() 和 peek() 用于在队列的头部查询元素。与 remove() 方法类似,在队列为空时, element() 抛出一个异常,而 peek() 返回 null。
### 1.1、XML是什么?
XML(可扩展标记语言)是一种很流行的简单的基于文本的语言来用作应用程序之间的通信模式。它被认为是传输标准装置和存储数据。JAVA提供了极好的支持和丰富的库来解析,修改或查询XML文档。
XML是一种简单的基于文本的语言,它被设计为储存和运输以纯文本格式的数据。它代表着可扩展标记语言。以下是一些XML的显着特征。
XML是一种标记语言。
XML是一种标记语言就像HTML一样。
XML标签不是像HTML那样预定义。
可以定义自己的标签,这就是为什么它被称为可扩展的语言。
XML标签被设计成自描述性的。
XML是W3C推荐用于数据存储和传输。
### 1.2、XML能干什么?
描述数据、存储数据、传输(交换)数据。
**优缺点:**
**优势**
以下是XML提供的优势:
技术无关 - 作为普通文本,XML是技术独立。它可以用于由任何技术进行数据的存储和传输的目的。
人类可读 - XML使用简单的文本格式。它是人类可读和可以理解的。
可扩展性 - 在XML,自定义标签可以创建和很容易使用。
允许验证 - 使用XSD,DTD和XML结构可以很容易地验证。
**缺点**
下面是使用XML的缺点:
冗余的语法 - 通常XML文件中包含大量的重复计算。
冗余 - 作为一个冗长的语言,XML文件大小增加了传输和存储成本。
### 1.3、XML与HTML区别
1、目的不一样
2、XML 被设计用来描述数据,其焦点是数据的内容。
3、HTML 被设计用来展示数据,其焦点是数据的外观。
4、HTML可以不关闭标签(即标签可以不成对出现),但XML必须关闭标签(即标签必须成对出现)。
5、HTML中的标签标识文本如何展示,而XML中的标签标识文本是什么含义(什么类型的文本)。
**XML文档节点类型**
文档(document)
元素(element)
属性(attribute)
文本(PCDATA--parsed character data)
注释(comment)
DOCTYPE :主要验证文档内容的正确性
实体(ENTITIES)
CDATA(character data)
### 1.4、XML语法
1、声明:
2、根节点:必须只能有一个根节点
3、标签:标签必须有结束且区分大小写,标签必须顺序嵌套
4、属性:必须引号引起值
5、空格会被保留,HTML空格最多保留一个
6、命名规则:命名必须见名知意
a)名字可包含字母、数字以及其他的字符
b)名字不能以数字或者标点符号开始
c)名字不能以字符“xml”(或者XML、Xml)开始
7、名字不能包含空格
8、 不应在 XML 元素名称中使用 ":" ,这是由于它用于命名空间(namespaces)的保留字。
9、标签优先于属性。
10、XML 命名空间可提供避免元素命名冲突的方法。
11、CDATA:字符数据, ,字符数据不进行转义
12、实体:&实体;
```xml
```
### 1.5、Xml约束
#### 1.5.1、XML DTD 约束
DTD(DocType Definition 文档类型定义)的作用是定义 XML 文档的合法构建模块。它使用一系列的合法元素来定义文档结构。用于约定XML格式。
**1、DTD引用方式**
1.1、内部
例如:
```xml
]>
```
```xml
...
```
1.2、外部私有的 SYSTEM 一般是我们自己定义的,可能只是一个公司内部使用,
例如:
```xml
...
```
1.3、外部公有的 PUBLIC 一般是一些标准,可能非常多的人用,,首先根据“命名空间”去问环境要相应的dtd文件,如果有,直接提供,如果没有再根据dtd文件位置找。
例如:
```xml
"-//SunMicrosystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
```
```xml
]>
Don't forget me this weekend
```
**相关参考文档及DTD教程:**https://www.runoob.com/dtd/dtd-tutorial.html
#### 1.5.2、XML Schema 约束
XML Schema 是基于 XML 的 DTD 替代者。XML Schema 描述 XML 文档的结构。XML Schema 语言也称作 XML Schema 定义(XML Schema Definition,XSD)。
DTD不是通过XML语法定义文档结构, 不能定义数据类型和限制Schema通过XML语法定义文档结构,可以定义数据类型和限制
**约定XML格式**
定义可出现在文档中的元素
定义可出现在文档中的属性
定义哪个元素是子元素
定义子元素的次序
定义子元素的数目
定义元素是否为空,或者是否可包含文本
定义元素和属性的数据类型
定义元素和属性的默认值以及固定值
**1、为何使用Schema**
XML Schema 是 DTD 的继任者
XML Schema 可针对未来的需求进行扩展
XML Schema 更完善,功能更强大
XML Schema 基于 XML 编写
XML Schema 支持数据类型和限制
XML Schema 支持命名空间