集合框架2:比较器、泛型基本语法、I/O操作

138 阅读5分钟

集合框架2

复习

1、Java集合框架(JCF)到底是什么?

Java为了能够更好的操作集合元素,预先设计的一系列具有继承或实现的关系的类与接口。其中主要包含了两部分的内容:

各种结构的集合类 --- 装元素的容器

操作集合的工具类 --- Collections等

2、JCF的继承结构是什么?

整个JCF当中的集合类的根接口是Collection。由它衍生出了各种存储结构的子接口:List、Set、Queue(队列,了解即可)、Map。然后每个子接口下面又有各种的实现类,他们都是我们可以使用的容器类。

3、List接口

特点--线性(有序),唯一一组像数组一样自带下标的。

常用类:ArrayList LinkedList

常用API:add、addAll、get、set、remove、removeAll、size

遍历方式:普通for、迭代器(了解接口)、for-each

ArrayList和LinkedLst的区分 --- 必须掌握

ArrayList、Vector的区分

4、Set接口

特点:不重复

常用类:HashSet

常用API:add addAll remove removeAll size

遍历方式:for-each

HashSet是怎样去重的?

调用equlas方法和hashcode方法判断两个对象是否同一个。要求:equals()返回true,且两个对象的hashcode要一致。

先人写好的常用类:String、包装类、时间日期等等,都已经实现了equals和hashcode的重写。

我们只需要在自定义类,且要把它的对象放入到Set集合中判断重复的时候才会有这个要求。这个要求就是:凡是重写了equls方法都应该重写hashcode方法,达到如果equls返回true的两个对象,他们的hashcode应该返回同样的值。

5、Map接口

特点:K-V对

常用类:HashMap

常用API:put、get、remove、size、keySet、values

遍历方式:遍历keySet、遍历values

key和value的关系?

对应关系,其中key不能重复。

HashMap和Hashtalbe的区别?

HashMap是线程不安全的,允许null做为键和值

Hashtable是线程安全的,不允许用null做键或值。

Properties需要知道

1、它是一个集合类,而且是Map集合,只是所有的Key和Value都是String

2、它可以操作一种特殊格式的文本文件---属性文件。

6、泛型

语法上,在声明集合类型变量的时候用"<>"规范该集合只能存放某种数据类型的元素。

Collections工具类

比较器

用于在类当中去定义该类对象比较大小的规则。

如果不去做这件事情,那么Collections当中所有跟比较有关的行为根本没有办法实现。

Comparable接口 -- 内部比较器

被比较对象自己内部实现的比较规则。先人定义的常用类都已经实现了该接口。

内部比较接口应该实现在被比较对象本身身上实现,然后重写该接口提供的compareTo方法。

compareTo方法的内部实现,记住:

根据在规则下确立的对象位置,分别返回负数、0和整数。

当前对象的位置在传入对象的位置之前,返回“负数”;之后返回“正数”。

Comparator -- 外部比较器

在比较的时候,通过参数指定比较规则,这个规则与内部比较器如果同时存在,以外部为准。

单独书写一个比较器类去实现外部比较器,只需要重写compare --- 接口中的其他方法都是default的。

compare方法的内部实现与上面的内部比较器是一样的,相当于用“第一个参数”的位置 减去 “第二个参数”的位置,分别得到负数、0或正数。

泛型的基本语法

泛型其实早就在其他编程语言当中就已经出现了的,Java其实是比较晚去实现它的语法(JDK1.5之后)。

泛型 在编程语言中的意义 其实比大家想象得要大。我们用专业的方式来描述它的话,它应该叫做“数据类型的参数化”。

也就是说有时候我们在定义一个类或方法的时候,并不能确定我要操作的数据类型是哪一个,需要使用者后期在使用的过程中告之。那么这个时候,我们就可以使用 --- “泛型”的语法来进行设计。

泛型类

假如在一个类的内部,需要使用到某种不确定的类型元素,那么我们就可以在类的声明处通过"<>"来定义泛型。

public class 类名<T>{

}

T只是一个标识符,可以自定义,但通常都写为T、E、K、V。

然后,在我们的这个类内部,只要需要用到这个类型的时候,就都可以用这个标识符来表达了。可以用它来申明属性,可以用它来声明形参,也可以用它来做返回类型,甚至是局部变量的类型。

在定义类的内容时,T由于没有具体的类型(这个还不确定),所以在Java中默认它是一个Object类型,所以可以用T定义的变量访问来自于Object的方法,但是不能访问某个具体子类的具体行为。注意,T在外部确定的时候,不能是基本数据类型

使用的时候:

MyClass<某个引用数据类型> mc = new MyClass<>();

mc对象中,所有用到T的地方,就都改变成了你在 "<>"内所规范的数据类型。

泛型接口

在接口中也可以定义泛型,定义语法与泛型类是一样的。

public interface MyInterface<T> {

    public void add(T t);

    public T delete();
}

它的实现类有三种情况: 1、实现类不做泛型设计,那么实现类中的T全部被“擦除”为Object

public class MyClass1  implements MyInterface{

    public void add(Object o) {
        System.out.println(o);
    }
    
    public Object delete() {
        return null;
    }
}

2、实现类根据接口指定的泛型类型

public class MyClass1  implements MyInterface<String>{

    public void add(String o) {
        System.out.println(o);
    }

    public String delete() {
        return null;
    }
}

3、实现类继续使用T,让它的调用者去确定T到底是谁?

public class MyClass1<T> implements MyInterface<T>{

    public void add(T o) {
        System.out.println(o);
    }

    public T delete() {
        return null;
    }
}

泛型方法

语法


public <T> 返回类型 方法名(T t){

}

在调用方法的时候,根据实际参数的类型确定T的类型。支持静态方法和非静态方法,以及多态参数。

Java的泛型的不足

Java的泛型和其他编程语言(特别是C#)语言的泛型比起来就是弟弟。 因为Java的泛型是假泛型。Java为了让它的JVM在运行的时候能够兼容之前的版本,所以Java泛型是不会在运行期做限制的,只是在编译期做了一个编译时的检查。

我们可以根据Java当中的“反射”技术,在运行期绕过它的泛型规范。

I/O操作

在所有的编程语言当中,I/O操作都是绕不过去的。其中I叫做“输入”--Input,O叫做“输出”--Output。

流模型

在Java的I/O设计当中,把输入输出设计成了所谓“流模型”。流模型描述的是,所有的数据传递都是在“数据源”与“目的地”之间,建立一个管道,然后数据像流水一样通过这跟管道进行传递。

所以在操作Java的I/0操作时,我们都必须要考虑好3个要点:源是谁?目的地是谁?管道是什么管道?

I/O流的分类

从传递方向上分为:输入流和输出流

从管道粗细上分为:字节流和字符流

由上面的两种分类组合成了I/O流当中的4大父类:

输入字节流:InputStream

输出字节流:OutputStream

输入字符流:Reader

输出字符流:Writer

这4大父类是4个抽象类,他们定义了所有流操作的统一的方法外观,然后让各自的子类去实现自己的内部细节。我们真正要用是用子类。

Java在设计子类的名称的时候,循序了一个很任性化的设计方案,那就是子类名很明确的表示了这个类的使用场景。

比如:FileInputStream -- 数据源是File,目的地是程序,管道是字节

FileReader -- 数据源是File,目的地是程序,管道是字符

FileOutputStream -- 数据源是程序,目的地是文件,管道是字节

FileWriter -- 数据源是程序,目的地是文件,管道是字符

I/O流的操作步骤

1、根据场景选择流类型;

2、new 出该类型的流对象;

3、调用read 或 write 方法

4、用完以后,关闭流对象。

提示各位:一旦涉及到输入输出操作,一定会有各种编译时异常需要我们提前处理。