------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
泛型
好处:1.将运行时期的ClassCastException,转移到了编译时期
方便程序员解决问题,让运行时期问题减少。
2.避免了强制转换。
泛型格式:通过<>来定义要操作的引用数据类型。
通常在集合框架中很常用。
泛型类:
当类中需要操作的引用数据类型不确定的时候,
早期用Object来实现扩展。
现在运用泛型类。
当方法中需要操作的引用数据类型不确定的时候,
可以运用泛型方法。
特殊之处:
静态方法,不可以定义在泛型类中。
当静态方法引用类型的不确定时,可以将泛型定义在方法上。
简单演示泛型的应用
import java.util.*; class Fanxin { public static void main(String[] args) { TreeSet<String> ts = new TreeSet<String>(new MyCompare());//<>中为定义的泛型类型 ts.add("aaa"); //定义带泛型的TreeSet对象 ts.add("bbb"); ts.add("ccc"); ts.add("ddddd"); ts.add("aabbb"); ts.add("ad"); for (Iterator <String> it=ts.iterator();it.hasNext() ; )//<>中为定义的泛型类型 { String s = it.next(); System.out.println(s); } } } class MyCompare implements Comparator<String> //<>中为定义的泛型类型 { public int compare(String s1,String s2) { int num = new Integer(s1.length()).compareTo(new Integer(s2.length())); if(num==0) return s1.compareTo(s2); else return num; } }
泛型的通配符拓展应用
限定通配符的上边界:
正确:Vector<? extends Number> x = new Vector<Integer>();
错误:Vector<? extends Number> x = new Vector<String>();
限定通配符的下边界:
正确:Vector<? super Integer> x = new Vector<Number>();
错误:Vector<? super Integer> x = new Vector<Byte>();
提示:
限定通配符总是包括自己。
?只能用作引用,不能用它去给其他变量赋值
Vector<? extends Number> y = new Vector<Integer>();
Vector<Number> x = y;
上面的代码错误,原理与Vector<Object > x11 = new Vector<String>();相似,
只能通过强制类型转换方式来赋值。
泛型集合的综合应用案例
1、泛型中的 ? 通配符
问题:定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法如何定义呢?
错误方式:
public static void printCollection(Collection< Object> cols) { for (Object obj : cols) { System.out.println(obj); } cols.add(“string”); // 没错 cols = new HashSet< Date>(); // 会报告错误 }
正确方式:
public static void printCollection(Collection< ?> cols) { for (Object obj : cols) { System.out.println(obj); } cols.add(“String”); // 错误,因为它不知自己未来匹配就一定是String cols.size(); // 没错,此方法与类型参数没有关系 }
总结:使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用于引用,可以调用与参数化无关的方法,
不能调用与参数化有关的方法。
2、泛型中的 ? 通配符的扩展限定通配符的上边界:
正确: Vector<? extends Number> x = new Vector<Integer>();
错误: Vector<? extends Number> x = new Vector<String>();
限定通配符的下边界:
正确: Vector<? super Integer> x = new Vector<Number>();
错误: Vector<? super Integer> x = new Vector<Byte>();
提示: 限定通配符总是包括自己。
类型推断总结
编译器判断泛型方法的实际类型参数的过程称为类型推断,类型推断是相对于知觉推断的,其实现方法是一种非常复杂的过程。
根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:
1、当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据调用方法时该处的实际应用类型来确定,
这很容易凭着感觉推断出来,即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例:
Swap(new String[3],3,4)—static <E> void swap(E[] a,int I,int j)
2、当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型都对应同一种类型来确定,
这很容易凭着感觉推断出来,例:
add(3,5)—static <T> T add (T a,T b)
3、当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,
且没有使用返回值,这时候取多个参数中的最大交集类型,例如,下面语句实际对应的类型就是Number了,编译没有问题,只是运行时出问题:
Fill(new Integer[3] , 3.5f) – static<T> void fill (T[] a,T v)
4、在某个类型变量在真个参数列表中的所有参数和返回值中的多出被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,
并且使用返回值,这时候优先考虑返回值的类型,例如,下面语句实际对应类型就Integer了,编译将报告错误,交变量x的类型改为float,
对比eclipse报告的错误提示,接着再将变量x类型改为Number,则没有了错误:
Int x= (3,3.5f) – static <T> T add (T a, T b)
5、参数类型的类型推断具有传遍性,下面第一种情况推断实际参数类型为Object,编译没有问题,
而第二种情况则根据参数化的Vector类实例将类型变量直接确定为String类型,编译将出现问题:
copy(new Integer[5],new String[5]) -- static <T> void copy(T[] a , T [] b); copy (new Vector<String>(),new Integer[5]) -- static <T> void copy(Collection<T> a,T[] b);
代码:
import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; public class GenericTest { public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, Exception{ String s = autoConvert("abad"); } //编写一个泛型方法,自动将Object类型对象转换成其他类型。 public static <T> T autoConvert(Object obj){ return (T)obj; } //定义一个方法,可以将任意类型的数组中的所有元素填充为相应类型的某个对象 public static <T> void fillArray(T[] a,T obj){ for(int i=0;i<a.length;i++){ a[i] = obj; } } //采用自定义泛型方法的方式,打印出任意参数化类型的集合中的所有内容,在这种情况下,前面的通配符,方案,要比泛型方法有效,当同一个类型变量在方法签名的两处被使用,或者类型变量在方法体代码中也被使用而不是仅在签名的时候使用,才需要使用泛型方法、 public static <T> void printCollection (Collection<T> collections){ for(T collection : collections){ System.out.println(collection); } } //定义一个方法,吧任意参数类型的集合中的数据安全的复制到相应类型的数组中。 public static <T> void copy (Collection<T> collections,T[] array){ for(T collection : collections){ System.out.println(collection); } } }
基本数据类型对象包装类
为了方便操作基本数据类型值,对其封装成了对象,在对象定义类属性和行为丰富类该数据的操作
由于描述该对象的类就称其为基本数据类型对象包装类
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean
该包装对象主要用于基本类型的字符串之间的转换
基本类型--->字符串
1.基本类型数值+""
2.用String类中的静态方法valueOf(基本类型数值)
用Integer静态方法valueOf
字符串--->基本类型
1.使用包装类中的静态方法 xxx parseXxx("xxx类型的字符串")
int parseInt("int string")
long parseInt("long string")
boolean parseBoolean("boolean string");
2.如果字符串被Integer进行对象的封装
可以使用另一个静态方法,intValue(); [仅可以用于数值]
例:
Integer i = new Integer("123");
System.out.println(i.intValue())
整数具备不同的进制体现:
1.十进制--->二进制
toBinaryString(int);
2.十进制--->十六进制
toHexString(int);
3.十进制--->八进制
toOctalString(int);
4.其他进制--->s十进制
parseInt(String s,int i)[s代表要转换字符串,i代表字符串的进制]
parseInt("110",2);
parseIny("110",8);
parseInt("11a",16);
注意:Integer中的equals()方法已经被覆写成为对比字符串值
所以 Integer a = new Integer("3");
Integer b = new Integer(3); //可简写为 Integer b = 3 ; 自动装箱
sop(a==b); false
sop(a.equals(b); ture
a = a + 6 ; // a = a.intValue() + 6 ; 自动拆箱
//当自动装箱装入的是一个字节时,那么该数据会被共享而不会重新开辟空间。
Integer x = 126 ;
Integer y = 126 ;
此时x,y公用的一个地址 sop(x==y) 返回ture
因为126小于一个字节(128)
代码演示
需求:
对一个字符串中的数值进行从小到大的排序
"20 11 13 -9 17 29 67 54"
思路:
1:字符串中的数值均是用空格分割的,所以可以先用字符串对象的方法将大字符串分割为小字符串
2.将小字符串均变成int数
3.对比数值并排序
import java.util.*; class SortSt { public static void main(String[] args) { sortString("20 11 13 -9 17 29 67 54"); } public static void sortString(String str) { System.out.println("原顺序:"+str); //将大字符串分割为字符串数组 String [] sa = str.split(" "); //创建数值数组 int [] ia = new int [sa.length]; //将字符串数组转变为数值数组 for (int i = 0; i< sa.length; i++) { ia[i] = Integer.parseInt(sa[i]); } //将数值数组排序 System.out.print("排序后:"); Arrays.sort(ia); for (int i=0;i<ia.length ;i++ ) { if (i!=0) System.out.print(" "); System.out.print(ia[i]); } } }
运行结果: