1. 泛型
- 由于集合可以存储不同类型的数据,所以取元素时有可能会导致类型转换错误
- JDK1.5增加了新特性泛型,为了减少操作集合时出错的几率
- 集合一旦声明了泛型,便只能存储同一类型的对象了
1.1 好处
- 将运行时期出现问题ClassCastException,转移到了编译时期。方便于程序员解决问题。让运行时问题减少,安全。
- 避免了强制转换麻烦。
泛型格式:通过<>来定义要操作的引用数据类型。
- 在使用java提供的对象时,什么时候写泛型呢?
- 通常在集合框架中很常见,只要见到<>就要定义泛型。其实<> 就是用来接收类型的。当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。
泛型方法
你可以写一个泛型方法,该方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。
下面是定义泛型方法的规则:
- 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的
<E>
)。 - 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
- 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
- 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。
/**
* 泛型方法的基本介绍
* @param tClass 传入的泛型实参
* @return T 返回值为T类型
* 说明:
* 1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
* 2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
* 3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
* 4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
*/
public <E> E genericMethod(Class<E> tClass){
E instance = null;
try {
instance = tClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return instance;
}
有界的类型参数:
可能有时候,你会想限制那些被允许传递到一个类型参数的类型种类范围。例如,一个操作数字的方法可能只希望接受Number
或者Number
子类的实例。这就是有界类型参数的目的。
要声明一个有界的类型参数,首先列出类型参数的名称,后跟extends关键字,最后紧跟它的上界。
// 比较三个值并返回最大值
public static <T extends Comparable<T>> T maximum(T x, T y, T z)
{
T max = x; // 假设x是初始最大值
if ( y.compareTo( max ) > 0 ){
max = y; //y 更大
}
if ( z.compareTo( max ) > 0 ){
max = z; // 现在 z 更大
}
return max; // 返回最大对象
}
泛型类
泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。
和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。
public class Box<T> {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
public static void main(String[] args) {
Box<Integer> integerBox = new Box<Integer>();
Box<String> stringBox = new Box<String>();
integerBox.add(new Integer(10));
stringBox.add(new String("菜鸟教程"));
System.out.printf("整型值为 :%d\n\n", integerBox.get());
System.out.printf("字符串为 :%s\n", stringBox.get());
}
}
类型通配符
- 类型通配符一般是使用
?
代替具体的类型参数。例如 List<?> 在逻辑上是List<String>,List<Integer>
等所有List<具体类型实参>
的父类。
public class GenericTest {
public static void main(String[] args) {
List<String> name = new ArrayList<String>();
List<Integer> age = new ArrayList<Integer>();
List<Number> number = new ArrayList<Number>();
name.add("icon");
age.add(18);
number.add(314);
getData(name);
getData(age);
getData(number);
}
public static void getData(List<?> data) {
System.out.println("data :" + data.get(0));
}
}
解析: 因为getData()
方法的参数是List
类型的,所以name,age,number
都可以作为这个方法的实参,这就是通配符的作用
- 类型通配符上限通过形如
List
来定义,如此定义就是通配符泛型值接受Number
及其下层子类类型。
public class GenericTest {
public static void main(String[] args) {
List<String> name = new ArrayList<String>();
List<Integer> age = new ArrayList<Integer>();
List<Number> number = new ArrayList<Number>();
name.add("icon");
age.add(18);
number.add(314);
//getUperNumber(name);//1
getUperNumber(age);//2
getUperNumber(number);//3
}
public static void getData(List<?> data) {
System.out.println("data :" + data.get(0));
}
public static void getUperNumber(List<? extends Number> data) {
System.out.println("data :" + data.get(0));
}
}
解析: 在(//1)处会出现错误,因为getUperNumber()
方法中的参数已经限定了参数泛型上限为Number
,所以泛型为String
是不在这个范围之内,所以会报错
- 类型通配符下限通过形如
List<? super Number>
来定义,表示类型只能接受Number及其三层父类类型,如 Object 类型的实例。
2. 增强For循环
for(数据类型 变量名 : 被遍历的集合(Collection)或者数组)
{
}
对集合进行遍历。只能获取集合元素。但是不能对集合进行操作。
迭代器除了遍历,还可以进行remove集合中元素的动作。
如果是用ListIterator,还可以在遍历过程中对集合进行增删改查的动作。
2.1 传统for和高级for有什么区别呢?
- 高级for有一个局限性。必须有被遍历的目标。
- 建议在遍历数组的时候,还是希望是用传统for。因为传统for可以定义脚标。
3. 可变参数
方法的可变参数。
在使用时注意:可变参数一定要定义在参数列表最后面。
可变参数。
- 其实就是上一种数组参数的简写形式。
- 不用每一次都手动的建立数组对象。
- 只要将要操作的元素作为参数传递即可。
- 隐式将这些参数封装成了数组。
4. 静态导入
StaticImport 静态导入。
当类名重名时,需要指定具体的包名。
当方法重名是,指定具备所属的对象或者类。
import java.util.*;
import static java.util.Arrays.*;//导入的是Arrays这个类中的所有静态成员。
import static java.util.Collections.*;
import static java.lang.System.*;//导入了System类中所有静态成员。