本节考点:
一、Collection 和 Collections的区别
二、Collections、Arrays中折半查找、排序等方法的使用
3-1 工具类
3-1-1 Collections
Collections:它的出现给集合操作提供了更多的功能。这个类不需要创建对象,内部提供的都是静态方法。
Collection 和 Collections的区别:
Collections是个java.util下的类,是针对集合类的一个工具类,提供一系列静态方法,实现对集合的查找、排序、替换、线程安全化(将非同步的集合转换成同步的)等操作。
Collection是个java.util下的接口,它是各种集合结构的父接口,继承于它的接口主要有Set和List,提供了关于集合的一些操作,如插入、删除、判断一个元素是否其成员、遍历等。
静态方法:
Collections.sort(list);//list集合进行元素的自然顺序排序。
Collections.sort(list,new ComparatorByLen());//按指定的比较器方法排序。
Collections.max(list); //返回list中字典顺序最大的元素。
int index = Collections.binarySearch(list,"zz");//二分查找,返回角标。
Collections.reverseOrder();//逆向反转排序。
Collections.shuffle(list);//随机对list中的元素进行位置的置换。
将非同步集合转成同步集合的方法:
Collections中的 XXX synchronizedXXX(XXX);
List synchronizedList(list);
Map synchronizedMap(map);
原理:定义一个类,将集合所有的方法加同一把锁后返回。
示例:
3-1-2 Arrays
用于操作数组对象的工具类,里面都是静态方法。
asList方法:将数组转换成list集合。
String[] arr = {"abc","kk","qq"};
List<String> list = Arrays.asList(arr);//将arr数组转成list集合。
将数组转换成集合,有什么好处呢?用aslist方法,将数组变成集合;
可以通过list集合中的方法来操作数组中的元素:isEmpty()、contains、indexOf、set;
注意(局限性):数组是固定长度,不可以使用集合对象增加或者删除等,会改变数组长度的功能方法。比如add、remove、clear。(会报不支持操作异常UnsupportedOperationException);
如果数组中存储的引用数据类型,直接作为集合的元素可以直接用集合方法操作。
如果数组中存储的是基本数据类型,asList会将数组实体作为集合元素存在。
注意:
(1)该方法将一个数组变成集合后,不可以使用集合的增删方法,因为数组的长度是固定的!
如果增删,则发生UnsupportedOprationException(不支持操作异常)
(2)如果数组中的元素都是基本数据类型,则该数组变成集合时,会将该数组作为集合的一个
元素出入集合
(3)如果数组中的元素都是对象,如String,那么数组变成集合后,数组中的元素就直接转成
集合中的元素
集合变数组:用的是Collection接口中的方法:toArray();
如果给toArray传递的指定类型的数据长度小于了集合的size,那么toArray方法,会自定再创建一个该类型的数据,长度为集合的size。
如果传递的指定的类型的数组的长度大于了集合的size,那么toArray方法,就不会创建新数组,直接使用该数组即可,并将集合中的元素存储到数组中,其他为存储元素的位置默认值null。
所以,在传递指定类型数组时,最好的方式就是指定的长度和size相等的数组。
将集合变成数组后有什么好处?限定了对集合中的元素进行增删操作,只要获取这些元素即可。
示例:
3-1-3 Runtime
(1)每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。可以通过 getRuntime 方法获取当前运行时。 应用程序不能创建自己的 Runtime 类实例。
(2)该类没有构造函数,也就是它不能直接创建对象,但是它里里面的方法又不是静态的
,故它一定有一个方法返回本类对象
(3)故该类是单例设计模式,保证在内存中只有一个对象
(4)方法摘要:
Process exec(String command) 在单独的进程中执行指定的字符串命令
void gc() 运行垃圾回收器。
static Runtime getRuntime() 返回与当前 Java 应用程序相关的运行时对象
void exit(int status) 通过启动虚拟机的关闭序列,终止当前正在运行的 Java 虚拟机
示例:
3-1-4 Date
Date接口表示特定的瞬间,精确到毫秒构造方法
Date() 分配 Date 对象并初始化此对象,以表示分配它的时间(精确到毫秒)。
Date(long date) 分配Date对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)“ 即1970年1月1日00:00:00GMT)以来的指定毫秒数。
方法摘要:
int compareTo(Date anotherDate) 比较两个日期的顺序。
boolean equals(Object obj) 比较两个日期的相等性。
示例:
3-1-5 Calendar:
直接已知子类: GregorianCalendar构造方法:
protected Calendar() 构造一个带有默认时区和语言环境的 Calendar。
protected Calendar(TimeZone zone, Locale aLocale) 构造一个带有指定时区和语言环境的 Calendar。
方法摘要:
static Calendar getInstance() 使用默认时区和语言环境获得一个日历。
示例:
练习:获取任意年的二月有多少天。思路:根据指定年设置一个时间就是
c.set(year,2,1)//某一年的3月1日。
c.add(Calenar.DAY_OF_MONTH,-1);//3月1日,往前推一天,就是2月最后一天。
3-2 jdk1.5的新特性
3-2-1 静态导入
import语句可以导入一个类或某个包中的所有类import static语句导入一个类中的某个静态方法或所有静态方法
静态导入后,静态方法前面就不用写类名.方法的方式类调用
语法举例:
import static java.lang.Math.sin;//导入一个静态方法
import static java.lang.Math.*; //导入一个类中的所有静态方法
静态导入使用注意:
当类名重复时,需要制定具体的包名;
当方法重名时,需要制定具体所属的对象或者类
3-2-2 可变参数
Java1.5增加了新特性:可变参数:适用于参数个数不确定,类型确定的情况,java把可变参数当做数组处理。注意:可变参数必须位于最后一项。当可变参数个数多余一个时,必将有一个不是最后一项,所以只支持有一个可变参数。
因为参数个数不定,所以当其后边还有相同类型参数时,java无法区分传入的参数属于前一个可变参数还是后边的参数,所以只能让可变参数位于最后一项。
可变参数的特点:
(1)只能出现在参数列表的最后;
(2)...位于变量类型和变量名之间,前后有无空格都可以;
(3)调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中一数组的形式访问可变参数
示例:
3-2-3 增强for循环:
语法:for ( type 变量名:集合变量名 ) { … }
注意事项:
迭代变量必须在( )中定义!
集合变量可以是数组或实现了Iterable接口的集合类
举例:
public static int add(int x,int ...args) {
int sum = x;
for(int arg:args) {
sum += arg;
}
return sum;
}
增强for循环代替了迭代器使用的不爽,简化书写
增强for循环局限性:对集合或者数组进行遍历时,只能取元素,不能对集合进行操作
3-2-4 基本数据类型的自动装箱和拆箱
基本数据类型byte ---> Byte
short ---> Short
int ---> Integer
long ---> Long
float ---> Float
double ---> Double
char ---> Character
boolean ---> Boolean
例子:
装箱:自动把一个基本数据类型的数据装箱成一个该类型数据的对象引用
Integer i = 3;(jdk1.5之前这样写是不行的,编译报错)
拆箱:自动把一个基本数据类型的对象引用拆箱成一个基本数据类型的数据,再参与运算
Integer i = 12;
sop(i+4);
享元模式:
Integer num1 = 12;
Integer num2 = 12;
System.out.println(num1 == num2);//打印true
Integer num5 = Integer.valueOf(12);
Integer num6 = Integer.valueOf(12);
System.out.println(num5 == num6);//打印true
Integer num3 = 129;
Integer num4 = 129;
System.out.println(num3 == num4);//打印false
为什么前面的返回true而后面的运算返回false呢?
对于基本数据类型的整数,装箱成Integer对象时,如果该数值在一个字节内,(-128~127),一旦装箱成Integer对象后,就把它缓存到磁里面,当下次,又把该数值封装成Integer对象时,会先看磁里面有没有该对象,有就直接拿出来用,这样就节省了内存空间。因为比较小的整数,用的频率比较高,就没必要每个对象都分配一个内存空间。
这就是享元模式!比如26个英文字母,10个阿拉伯数字
3-2-5 枚举
为什么要有枚举?问题:要定义星期几或性别的变量,该怎么定义?
假设用1-7分别表示星期一到星期日,但有人可能会写成int weekday = 0;或即使使用常量方式也无法阻止意外。
枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就会报错。
枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标。
用普通类如何实现枚举的功能?定义一个Weekday类来模拟实现:
步骤:
私有化构造方法
每个元素分别用一个公有的静态成员变量表示(public static final)
可以有若干公有方法或抽象方法。采用抽象方法定义nextDay就将大量的if.else语句
转移成了一个个独立的类。
枚举的应用:
举例:定义一个Weekday的枚举。
扩展:枚举类的values,valueOf,name,toString,ordinal等方法
(记住,讲课时要先于自定义方法前介绍,讲课更流畅)
总结:枚举是一种特殊的类,其中的每个元素都是该类的一个实例对象。
例如可以调用WeekDay.SUN.getClass().getName和WeekDay.class.getName()。
枚举的高级应用:
枚举就相当于一个类,其中也可以定义构造方法、成员变量、普通方法和抽象方法。
枚举元素必须位于枚举体中的最开始部分,枚举元素列表的后要有分号与其他成员分隔。
把枚举中的成员方法或变量等放在枚举元素的前面,编译器报告错误。
带构造方法的枚举
构造方法必须定义成私有的
如果有多个构造方法,该如何选择哪个构造方法?
枚举元素MON和MON()的效果一样,都是调用默认的构造方法。
带方法的枚举
定义枚举TrafficLamp
实现普通的next方法
实现抽象的next方法:每个元素分别是由枚举类的子类来生成的实例对象,
这些子类采用类似内部类的方式进行定义。增加上表示时间的构造方法
枚举只有一个成员时,就可以作为一种单例的实现方式。
3-2-6 泛型
泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说明的集合时会去除掉“类型”信息,使程序运行效率不受影响,
对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。
由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,
就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再调用其add方法即可。
ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语:
整个称为ArrayList<E>泛型类型
ArrayList<E>中的E称为类型变量或类型参数
整个ArrayList<Integer>称为参数化的类型
ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
ArrayList<Integer>中的<>念着typeof
ArrayList称为原始类型
参数化类型与原始类型的兼容性:
参数化类型可以引用一个原始类型的对象,编译报告警告,
例如, Collection<String> c = new Vector();//可不可以,不就是编译器一句话的事吗?
原始类型可以引用一个参数化类型的对象,编译报告警告,
例如, Collection c = new Vector<String>();//原来的方法接受一个集合参数,新的类型也要能传进去
参数化类型不考虑类型参数的继承关系:
Vector<String> v = new Vector<Object>(); //错误!///不写<Object>没错,写了就是明知故犯
Vector<Object> v = new Vector<String>(); //也错误!
编译器不允许创建泛型变量的数组。即在创建数组实例时,
数组的元素不能使用参数化的类型,
例如,下面语句有错误:
Vector<Integer> vectorList[] = new Vector<Integer>[10];
泛型限定:
限定通配符的上边界:
正确: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>();相似,
只能通过强制类型转换方式来赋值。