public class ArrayList {
private Object[] array;
private int size;
public void add(Object e) {...}
public void remove(int index) {...}
public Object get(int index) {...}
}
如果用上述ArrayList存储String类型,会有这么几个缺点:
需要强制转型;
不方便,易出错。
例如,代码必须这么写:
ArrayList list = new ArrayList();
list.add("Hello");
// 获取到Object,必须强制转型为String:
String first = (String) list.get(0);
很容易出现ClassCastException,因为容易“误转型”:
list.add(new Integer(123));
// ERROR: ClassCastException:
String second = (String) list.get(1);
要解决上述问题,我们可以为String单独编写一种ArrayList:
public class StringArrayList {
private String[] array;
private int size;
public void add(String e) {...}
public void remove(int index) {...}
public String get(int index) {...}
}
StringArrayList list = new StringArrayList();
list.add("Hello");
String first = list.get(0);
// 编译错误: 不允许放入非String类型:
list.add(new Integer(123));
问题暂时解决。
然而,新的问题是,如果要存储Integer,还需要为Integer单独编写一种ArrayList:
public class IntegerArrayList {
private Integer[] array;
private int size;
public void add(Integer e) {...}
public void remove(int index) {...}
public Integer get(int index) {...}
}
实际上,还需要为其他所有class单独编写一种ArrayList:
LongArrayList
DoubleArrayList
PersonArrayList
...
这是不可能的,JDK的class就有上千个,而且它还不知道其他人编写的class。
为了解决新的问题,我们必须把ArrayList变成一种模板:ArrayList,代码如下:
public class ArrayList {
private T[] array;
private int size;
public void add(T e) {...}
public void remove(int index) {...}
public T get(int index) {...}
}
对于上面的问题我们只需要将上述代码的 List list = new ArrayList() 改为 List list = new ArrayList();
List list = new ArrayList();
//list.add(22); //向集合中添加一个 Integer 类型的数据时,编译器会报错
list.add("Bob"); //向集合中添加一个 String 类型的数据
list.add("Tom"); //向集合中添加一个 String 类型的数据
//如果我们遍历的时候就想得到自己想要的数据类型
for(int i = 0 ; i < list.size() ; i++){
String obj = list.get(i); //这里就不需要强转了,前面添加的是什么类型,这里获取的就是什么类型
System.out.println(obj);
}
泛型是在编译阶段有效
List list1 = new ArrayList();
List list2 = new ArrayList();
Class c1 = list1.getClass();
Class c2 = list2.getClass();
System.out.println(c1==c2); //true
上述代码,由于我们知道反射是在运行时阶段,c1==c2为 true,说明了编译之后的 class 文件中是不包含任意的泛型信息的。如果不信,我们可以看 class 文件的反编译信息
java.util.List list1 = new ArrayList();
java.util.List list2 = new ArrayList();
Class c1 = list1.getClass();
Class c2 = list2.getClass();
System.out.println(c1.equals(c2));
public class Box {
private T box;
public T getBox(T t){
this.box = t;
return t;
}
public void getType(){
System.out.println("T的实际类型为:"+box.getClass().getName());
}
public static void main(String[] args) {
Box box = new Box();
System.out.println(box.getBox(1));
box.getType();
System.out.println(box.getBox("Tom"));
box.getType();
}
}
输出结果为:
1
T的实际类型为:java.lang.Integer
Tom
T的实际类型为:java.lang.String
泛型通配符
在泛型中,我们可以用 ? 来代替任意类型
public List wildCard(List> list){
return list;
}
public static void main(String[] args) {
GenericTest gt = new GenericTest();
//构造一个 Interger 类型的集合
List integer = new ArrayList();
integer.add(1);
System.out.println(gt.wildCard(integer));
//构造一个 String 类型的集合
List str = new ArrayList();
gt.wildCard(str);
//构造一个 Object 类型的集合
List
//通配符的下限,只能是 Number 或 Number的子类
public List wildCard(List extends Number> list){
return list;
}
public static void main(String[] args) {
GenericTest gt = new GenericTest();
//构造一个 Interger 类型的集合
List integer = new ArrayList();
integer.add(1);
System.out.println(gt.wildCard(integer));
//构造一个 String 类型的集合
List str = new ArrayList();
//gt.wildCard(str); //编译报错
//构造一个 Object 类型的集合
List obj = new ArrayList();
obj.add(1);
obj.add("a");
//System.out.println(gt.wildCard(obj)); //编译报错
}
①、下限: 语法(? super className),即只能为 className 或 className 的父类
//通配符的上限,只能是 Number 或 Number的父类
public List wildCard(List super Number> list){
return list;
}
public static void main(String[] args) {
GenericTest gt = new GenericTest();
//构造一个 Interger 类型的集合
List integer = new ArrayList();
integer.add(1);
//System.out.println(gt.wildCard(integer)); //编译报错
//构造一个 String 类型的集合
List str = new ArrayList();
//gt.wildCard(str); //编译报错
//构造一个 Object 类型的集合
List obj = new ArrayList();
obj.add(1);
obj.add("a");
System.out.println(gt.wildCard(obj));
}
泛型的注意事项
4.1、不能用基本类型来定义泛型,如 int、float
List list = new ArrayList(); //不能用 int 这样的基本类型定义泛型
// 编译器警告:
List list = new ArrayList();
list.add("Hello");
list.add("World");
String first = (String) list.get(0);
String second = (String) list.get(1);
此时,只能把当作Object使用,没有发挥泛型的优势。
当我们定义泛型类型后,List的泛型接口变为强类型List:
// 无编译器警告:
List list = new ArrayList();
list.add("Hello");
list.add("World");
// 无强制转型:
String first = list.get(0);
String second = list.get(1);
当我们定义泛型类型后,List的泛型接口变为强类型List:
List list = new ArrayList();
list.add(new Integer(123));
list.add(new Double(12.34));
Number first = list.get(0);
Number second = list.get(1);
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
Person[] ps = new Person[] {
new Person("Bob", 61),
new Person("Alice", 88),
new Person("Lily", 75),
};
Arrays.sort(ps);
System.out.println(Arrays.toString(ps));
}
}
class Person implements Comparable {
String name;
int score;
Person(String name, int score) {
this.name = name;
this.score = score;
}
public int compareTo(Person other) {
return this.name.compareTo(other.name);
}
public String toString() {
return this.name + "," + this.score;
}
}
运行上述代码,可以正确实现按name进行排序。
也可以修改比较逻辑,例如,按score从高到低排序。请自行修改测试。
使用泛型时,把泛型参数替换为需要的class类型,例如:ArrayList,ArrayList等;
可以省略编译器能自动推断出的类型,例如:List list = new ArrayList<>();;
public class Pair {
private String first;
private String last;
public Pair(String first, String last) {
this.first = first;
this.last = last;
}
public String getFirst() {
return first;
}
public String getLast() {
return last;
}
}
然后,标记所有的特定类型,这里是String:
public class Pair {
private String first;
private String last;
public Pair(String first, String last) {
this.first = first;
this.last = last;
}
public String getFirst() {
return first;
}
public String getLast() {
return last;
}
}
最后,把特定类型String替换为T,并申明:
public class Pair {
private T first;
private T last;
public Pair(T first, T last) {
this.first = first;
this.last = last;
}
public T getFirst() {
return first;
}
public T getLast() {
return last;
}
}
熟练后即可直接从T开始编写。
静态方法
编写泛型类时,要特别注意,泛型类型不能用于静态方法。例如:
public class Pair {
private T first;
private T last;
public Pair(T first, T last) {
this.first = first;
this.last = last;
}
public T getFirst() { ... }
public T getLast() { ... }
// 对静态方法使用:
public static Pair create(T first, T last) {
return new Pair(first, last);
}
}
上述代码会导致编译错误,我们无法在静态方法create()的方法参数和返回类型上使用泛型类型T。
有些同学在网上搜索发现,可以在static修饰符后面加一个,编译就能通过:
public class Pair {
private T first;
private T last;
public Pair(T first, T last) {
this.first = first;
this.last = last;
}
public T getFirst() { ... }
public T getLast() { ... }
// 可以编译通过:
public static Pair create(T first, T last) {
return new Pair(first, last);
}
}
public class Pair {
private T first;
private T last;
public Pair(T first, T last) {
this.first = first;
this.last = last;
}
public T getFirst() { ... }
public T getLast() { ... }
// 静态泛型方法应该使用其他类型区分:
public static Pair create(K first, K last) {
return new Pair(first, last);
}
}
这样才能清楚地将静态方法的泛型类型和实例类型的泛型类型区分开。
多个泛型类型
泛型还可以定义多种类型。例如,我们希望Pair不总是存储两个类型一样的对象,就可以使用类型:
public class Pair {
private T first;
private K last;
public Pair(T first, K last) {
this.first = first;
this.last = last;
}
public T getFirst() { ... }
public K getLast() { ... }
}
public class Pair {
private T first;
private T last;
public Pair(T first, T last) {
this.first = first;
this.last = last;
}
public T getFirst() {
return first;
}
public T getLast() {
return last;
}
}
而虚拟机根本不知道泛型。这是虚拟机执行的代码:
public class Pair {
private Object first;
private Object last;
public Pair(Object first, Object last) {
this.first = first;
this.last = last;
}
public Object getFirst() {
return first;
}
public Object getLast() {
return last;
}
}
因此,Java使用擦拭法实现泛型,导致了:
编译器把类型视为Object;
编译器根据实现安全的强制转型。
使用泛型的时候,我们编写的代码也是编译器看到的代码:
Pair p = new Pair<>("Hello", "world");
String first = p.getFirst();
String last = p.getLast();
而虚拟机执行的代码并没有泛型:
Pair p = new Pair("Hello", "world");
String first = (String) p.getFirst();
String last = (String) p.getLast();
class Pair {
private T first;
private T last;
public Pair(T first, T last) {
this.first = first;
this.last = last;
}
public T getFirst() {
return first;
}
public T getLast() {
return last;
}
}
Pair p1 = new Pair<>("Hello", "world");
Pair p2 = new Pair<>(123, 456);
Class c1 = p1.getClass();
Class c2 = p2.getClass();
System.out.println(c1==c2); // true
System.out.println(c1==Pair.class); // true
Class clazz = IntPair.class;
Type t = clazz.getGenericSuperclass();
if (t instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) t;
Type[] types = pt.getActualTypeArguments(); // 可能有多个泛型类型
Type firstType = types[0]; // 取第一个泛型类型
Class> typeClass = (Class>) firstType;
System.out.println(typeClass); // Integer
}
class Pair {
private T first;
private T last;
public Pair(T first, T last) {
this.first = first;
this.last = last;
}
public T getFirst() {
return first;
}
public T getLast() {
return last;
}
}
class IntPair extends Pair {
public IntPair(Integer first, Integer last) {
super(first, last);
}
}
public class PairHelper {
static int add(Pair p) {
Number first = p.getFirst();
Number last = p.getLast();
return first.intValue() + last.intValue();
}
}
上述代码是可以正常编译的。使用的时候,我们传入:
int sum = PairHelper.add(new Pair(1, 2));
注意:传入的类型是Pair,实际参数类型是(Integer, Integer)。
既然实际参数是Integer类型,试试传入Pair:
public static void main(String[] args) {
Pair p = new Pair<>(123, 456);
int n = add(p);
System.out.println(n);
}
static int add(Pair p) {
Number first = p.getFirst();
Number last = p.getLast();
return first.intValue() + last.intValue();
}
class Pair {
private T first;
private T last;
public Pair(T first, T last) {
this.first = first;
this.last = last;
}
public T getFirst() {
return first;
}
public T getLast() {
return last;
}
}
直接运行,会得到一个编译错误:
incompatible types: Pair cannot be converted to Pair
原因很明显,因为Pair不是Pair的子类,因此,add(Pair)不接受参数类型Pair。
但是从add()方法的代码可知,传入Pair是完全符合内部代码的类型规范,因为语句:
Number first = p.getFirst();
Number last = p.getLast();
public static void main(String[] args) {
Pair p = new Pair<>(123, 456);
int n = add(p);
System.out.println(n);
}
static int add(Pair extends Number> p) {
Number first = p.getFirst();
Number last = p.getLast();
return first.intValue() + last.intValue();
}
public static void main(String[] args) {
Pair p = new Pair<>(123, 456);
int n = add(p);
System.out.println(n);
}
static int add(Pair extends Number> p) {
Number first = p.getFirst();
Number last = p.getLast();
p.setFirst(new Integer(first.intValue() + 100));
p.setLast(new Integer(last.intValue() + 100));
return p.getFirst().intValue() + p.getFirst().intValue();
}
class Pair {
private T first;
private T last;
public Pair(T first, T last) {
this.first = first;
this.last = last;
}
public T getFirst() {
return first;
}
public T getLast() {
return last;
}
public void setFirst(T first) {
this.first = first;
}
public void setLast(T last) {
this.last = last;
}
}
不出意外,我们会得到一个编译错误:
incompatible types: Integer cannot be converted to CAP#1
where CAP#1 is a fresh type-variable:
CAP#1 extends Number from capture of ? extends Number
class Pair {
private T first;
private T last;
public Pair(T first, T last) {
this.first = first;
this.last = last;
}
public T getFirst() {
return first;
}
public T getLast() {
return last;
}
public void setFirst(T first) {
this.first = first;
}
public void setLast(T last) {
this.last = last;
}
}
super T>允许调用写方法set(T)传入T的引用,但不允许调用读方法T get()获取T的引用(获取Object除外)。
一个是允许读不允许写,另一个是允许写不允许读。
先记住上面的结论,我们来看Java标准库的Collections类定义的copy()方法:
public class Collections {
// 把src的每个元素复制到dest中:
public static void copy(List super T> dest, List extends T> src) {
for (int i=0; i
它的作用是把一个List的每个元素依次添加到另一个List中。它的第一个参数是List super T>,表示目标List,第二个参数List extends T>,表示要复制的List。我们可以简单地用for循环实现复制。在for循环中,我们可以看到,对于类型 extends T>的变量src,我们可以安全地获取类型T的引用,而对于类型 super T>的变量dest,我们可以安全地传入T的引用。
public class Collections {
// 把src的每个元素复制到dest中:
public static void copy(List super T> dest, List extends T> src) {
...
T t = dest.get(0); // compile error!
src.add(t); // compile error!
}
}
这个copy()方法的另一个好处是可以安全地把一个List添加到List,但是无法反过来添加:
// copy List to List ok:
List numList = ...;
List intList = ...;
Collections.copy(numList, intList);
// ERROR: cannot copy List to List:
Collections.copy(intList, numList);
public static void main(String[] args) {
Pair p = new Pair<>(123, 456);
Pair> p2 = p; // 安全地向上转型
System.out.println(p2.getFirst() + ", " + p2.getLast());
}
class Pair {
private T first;
private T last;
public Pair(T first, T last) {
this.first = first;
this.last = last;
}
public T getFirst() {
return first;
}
public T getLast() {
return last;
}
public void setFirst(T first) {
this.first = first;
}
public void setLast(T last) {
this.last = last;
}
}
java中最常用jar包的用途
jar包用途axis.jarSOAP引擎包commons-discovery-0.2.jar用来发现、查找和实现可插入式接口,提供一些一般类实例化、单件的生命周期管理的常用方法.jaxrpc.jarAxis运行所需要的组件包saaj.jar创建到端点的点到点连接的方法、创建并处理SOAP消息和附件的方法,以及接收和处理SOAP错误的方法. w
创建图表事件监听非常简单:首先是通过addEventListener('监听类型',js监听方法)添加事件监听,然后在js监听方法中定义具体监听逻辑。
以钻取操作为例,当用户点击图表某一个point的时候弹出point的name和value,代码如下:
<script>
//创建AnyChart
var chart = new AnyChart();
//添加钻取操作&quo
我们来看下面的例子:
create or replace view testview
as
select empno,ename from emp where ename like ‘M%’
with check option;
这里我们创建了一个视图,并使用了with check option来限制了视图。 然后我们来看一下视图包含的结果:
select * from testv