当前位置: 首页 > news >正文

本地企业网站建设wordpress找不到根目录

本地企业网站建设,wordpress找不到根目录,wordpress 新变量,四川建设部网站2023年将会持续于B站、CSDN等各大平台更新#xff0c;可加入粉丝群与博主交流:838681355#xff0c;为了老板大G共同努力。 【商务合作请私信或进群联系群主】 六、集合、泛型、枚举 6.1 集合 Java 提供了集合类。集合类主要负责保存、盛装其他数据#xff0c;因此集合类…2023年将会持续于B站、CSDN等各大平台更新可加入粉丝群与博主交流:838681355为了老板大G共同努力。 【商务合作请私信或进群联系群主】 六、集合、泛型、枚举 6.1 集合 Java 提供了集合类。集合类主要负责保存、盛装其他数据因此集合类也被称为容器类。Java 所有的集合类都位于 java.util 包下提供了一个表示和操作对象集合的统一构架包含大量集合接口以及这些接口的实现类和操作它们的算法。集合类和数组不一样数组元素既可以是基本类型的值也可以是对象实际上保存的是对象的引用变量而集合里只能保存对象实际上只是保存对象的引用变量但通常习惯上认为集合里保存的是对象。集合类就像容器现实生活中容器的功能就是添加对象、删除对象、清空容器和判断容器是否为空等集合类为这些功能都提供了对应的方法。Java 集合类型分为 Collection 和 Map。JAVA集合接口的作用 接口名称作 用Iterator 接口集合的输出接口主要用于遍历输出即迭代访问Collection 集合中的元素Iterator 对象被称之为迭代器。迭代器接口是集合接口的父接口实现类实现 Collection 时就必须实现 Iterator 接口。Collection 接口是 List、Set 和 Queue 的父接口是存放一组单值的最大接口。所谓的单值是指集合中的每个元素都是一个对象。一般很少直接使用此接口直接操作。Queue 接口Queue 是 Java 提供的队列实现有点类似于 List。Dueue 接口是 Queue 的一个子接口为双向队列。List 接口是最常用的接口。是有序集合允许有相同的元素。使用 List 能够精确地控制每个元素插入的位置用户能够使用索引元素在 List 中的位置类似于数组下标来访问 List 中的元素与数组类似。Set 接口不能包含重复的元素。Map 接口是存放一对值的最大接口即接口中的每个元素都是一对以 key➡value 的形式保存。 对于 Set、List、Queue 和 Map 这 4 种集合Java 最常用的实现类分别是 HashSet、TreeSet、ArrayList、ArrayDueue、LinkedList 和 HashMap、TreeMap 等。表 2 介绍了集合中这些常用的实现类。 JAVA集合实现类的作用 类名称作用HashSet为优化査询速度而设计的 Set。它是基于 HashMap 实现的HashSet 底层使用 HashMap 来保存所有元素实现比较简单TreeSet实现了 Set 接口是一个有序的 Set这样就能从 Set 里面提取一个有序序列ArrayList一个用数组实现的 List能进行快速的随机访问效率高而且实现了可变大小的数组ArrayDueue是一个基于数组实现的双端队列按“先进先出”的方式操作集合元素LinkedList对顺序访问进行了优化但随机访问的速度相对较慢。此外它还有 addFirst()、addLast()、getFirst()、getLast()、removeFirst() 和 removeLast() 等方法能把它当成栈Stack或队列Queue来用HsahMap按哈希算法来存取键对象TreeMap可以对键对象进行排序 6.1.1 Collection接口 Collection 接口是 List、Set 和 Queue 接口的父接口通常情况下不被直接使用。Collection 接口定义了一些通用的方法通过这些方法可以实现对集合的基本操作。定义的方法既可用于操作 Set 集合也可用于操作 List 和 Queue 集合。Collection接口常用方法 方法名称说明boolean add(E e)向集合中添加一个元素如果集合对象被添加操作改变了则返回 true。E 是元素的数据类型boolean addAll(Collection c)向集合中添加集合 c 中的所有元素如果集合对象被添加操作改变了则返回 true。void clear()清除集合中的所有元素将集合长度变为 0。boolean contains(Object o)判断集合中是否存在指定元素boolean containsAll(Collection c)判断集合中是否包含集合 c 中的所有元素boolean isEmpty()判断集合是否为空Iteratoriterator()返回一个 Iterator 对象用于遍历集合中的元素boolean remove(Object o)从集合中删除一个指定元素当集合中包含了一个或多个元素 o 时该方法只删除第一个符合条件的元素该方法将返回 true。boolean removeAll(Collection c)从集合中删除所有在集合 c 中出现的元素相当于把调用该方法的集合减去集合 c。如果该操作改变了调用该方法的集合则该方法返回 true。boolean retainAll(Collection c)从集合中删除集合 c 里不包含的元素相当于把调用该方法的集合变成该集合和集合 c 的交集如果该操作改变了调用该方法的集合则该方法返回 true。int size()返回集合中元素的个数Object[] toArray()把集合转换为一个数组所有的集合元素变成对应的数组元素。 示例:用了 Collection 接口的 ArrayList 实现类来调用 Collection 的方法。add() 方法可以向 Collection 中添加一个元素而调用 addAll() 方法可以将指定 Collection 中的所有元素添加到另一个 Collection 中。 代码创建了两个集合 list1 和 list2然后调用 add() 方法向 list1 中添加了两个元素再调用 addAll() 方法将这两个元素添加到 list2 中。接下来又向 list2 中添加了一个元素最后输出 list2 集合中的所有元素如何使用 Collection 接口向集合中添加方法。具体实现代码如下public static void main(String[] args) {ArrayList list1 new ArrayList(); // 创建集合 list1ArrayList list2 new ArrayList(); // 创建集合 list2list1.add(one); // 向 list1 添加一个元素list1.add(two); // 向 list1 添加一个元素list2.addAll(list1); // 将 list1 的所有元素添加到 list2list2.add(three); // 向 list2 添加一个元素System.out.println(list2 集合中的元素如下);Iterator it1 list2.iterator();while (it1.hasNext()) {System.out.print(it1.next() 、);}}示例2: 创建一个案例演示 Collection 集合中 size()、remove() 和 removeAll() 方法的应用。具体代码如下public static void main(String[] args) {ArrayList list1 new ArrayList(); // 创建集合 list1ArrayList list2 new ArrayList(); // 创建集合 list2list1.add(one);list1.add(two);list1.add(three);System.out.println(list1 集合中的元素数量 list1.size()); // 输出list1中的元素数量list2.add(two);list2.add(four);list2.add(six);System.out.println(list2 集合中的元素数量 list2.size()); // 输出list2中的元素数量list2.remove(2); // 删除第 3 个元素System.out.println(\nremoveAll() 方法之后 list2 集合中的元素数量 list2.size());System.out.println(list2 集合中的元素如下);Iterator it1 list2.iterator();while (it1.hasNext()) {System.out.print(it1.next() 、);}list1.removeAll(list2);System.out.println(\nremoveAll() 方法之后 list1 集合中的元素数量 list1.size());System.out.println(list1 集合中的元素如下);Iterator it2 list1.iterator();while (it2.hasNext()) {System.out.print(it2.next() 、);}}6.1.2 List集合 List 是一个有序、可重复的集合集合中每个元素都有其对应的顺序索引。List 集合允许使用重复元素可以通过索引来访问指定位置的集合元素。List 实现了 Collection 接口它主要有两个常用的实现类ArrayList 类和 LinkedList 类。 6.1.2.1 ArrayList类 ArrayList 类实现了可变数组的大小存储在内的数据称为元素。它还提供了快速基于索引访问元素的方式对尾部成员的增加和删除支持较好。使用 ArrayList 创建的集合允许对集合中的元素进行快速的随机访问不过向 ArrayList 中插入与删除元素的速度相对较慢。ArrayList 类的常用构造方法有如下两种重载形式* ArrayList()构造一个初始容量为 10 的空列表。* ArrayList(Collection?extends Ec)构造一个包含指定 Collection 元素的列表这些元素是按照该 Collection 的迭代器返回它们的顺序排列的。ArrayList类方法 方法名称说明E get(int index)获取此集合中指定索引位置的元素E 为集合中元素的数据类型int index(Object o)返回此集合中第一次出现指定元素的索引如果此集合不包含该元 素则返回 -1int lastIndexOf(Object o)返回此集合中最后一次出现指定元素的索引如果此集合不包含该 元素则返回 -1E set(int index, Eelement)将此集合中指定索引位置的元素修改为 element 参数指定的对象。 此方法返回此集合中指定索引位置的原元素List subList(int fromlndex, int tolndex)返回一个新的集合新集合中包含 fromlndex 和 tolndex 索引之间 的所有元素。包含 fromlndex 处的元素不包含 tolndex 索引处的 元素 示例:setter/getter 方法 1创建一个商品类 Product在该类中定义 3 个属性和 toString() 方法分别实现 setter/getter 方法。代码的实现如下public class Product {// 商品类private int id; // 商品编号private String name; // 名称private float price; // 价格public Product(int id, String name, float price) {this.name name;this.id id;this.price price;}// 这里是上面3个属性的setter/getter方法这里省略public String toString() {return 商品编号 id 名称 name 价格 price;}}2创建一个测试类调用 Product 类的构造函数实例化三个对象并将 Product 对象保存至 ArrayList 集合中。最后遍历该集合输出商品信息。测试类的代码实现如下public class Test {public static void main(String[] args) {Product pd1 new Product(4, 木糖醇, 10);Product pd2 new Product(5, 洗发水, 12);Product pd3 new Product(3, 热水壶, 49);List list new ArrayList(); // 创建集合list.add(pd1);list.add(pd2);list.add(pd3);System.out.println(*************** 商品信息 ***************);for (int i 0; i list.size(); i) {// 循环遍历集合输出集合元素Product product (Product) list.get(i);System.out.println(product);}}}示例二: indexOf() 方法和 lastIndexOf() 创建一个 List 集合 list然后添加了 7 个元素由于索引从 0 开始所以最后一个元素的索引为 6public static void main(String[] args) {List list new ArrayList();list.add(One);list.add(|);list.add(Two);list.add(|);list.add(Three);list.add(|);list.add(Four);System.out.println(list 集合中的元素数量 list.size());System.out.println(list 集合中的元素如下);Iterator it list.iterator();while (it.hasNext()) {System.out.print(it.next() 、);}System.out.println(\n在 list 集合中丨第一次出现的位置是 list.indexOf(|));System.out.println(在 list 集合中丨最后一次出现的位置是 list.lastIndexOf(|));}示例三:subList() public static void main(String[] args) {List list new ArrayList();list.add(One);list.add(Two);list.add(Three);list.add(Four);list.add(Five);list.add(Six);list.add(Seven);System.out.println(list 集合中的元素数量 list.size());System.out.println(list 集合中的元素如下);Iterator it list.iterator();while (it.hasNext()) {System.out.print(it.next() 、);}List sublist new ArrayList();sublist list.subList(2, 5); // 从list集合中截取索引2~5的元素保存到sublist集合中System.out.println(\nsublist 集合中元素数量 sublist.size());System.out.println(sublist 集合中的元素如下);it sublist.iterator();while (it.hasNext()) {System.out.print(it.next() 、);} }6.1.2.2 LinkedList类 LinkedList 类采用链表结构保存对象这种结构的优点是便于向集合中插入或者删除元素。LinkList类常用方法 方法名称说明void addFirst(E e)将指定元素添加到此集合的开头void addLast(E e)将指定元素添加到此集合的末尾E getFirst()返回此集合的第一个元素E getLast()返回此集合的最后一个元素E removeFirst()删除此集合中的第一个元素E removeLast()删除此集合中的最后一个元素 示例: 先创建了 5 个 String 对象分别为 p1、p2、p3、p4 和 p5。同时将 p1、 p2、p3 和 p4 对象使用 add() 方法添加到 LinkedList 集合中使用 addLast() 方法将 p5 对象添加到 LinkedList 集合中。分别调用 LinkedList 类中的 getFirst() 方法和 getLast() 方法获取第一个和最后一个商品名称。最后使用 removeLast() 方法将最后一个商品信息删除并将剩余商品信息打印出来。LinkedListString 中的 String 是 Java 中的泛型用于指定集合中元素的数据类型例如这里指定元素类型为 String则该集合中不能添加非 String 类型的元素。public class Test {public static void main(String[] args) {LinkedListString products new LinkedListString(); // 创建集合对象String p1 new String(六角螺母);String p2 new String(10A 电缆线);String p3 new String(5M 卷尺);String p4 new String(4CM 原木方板);products.add(p1); // 将 p1 对象添加到 LinkedList 集合中products.add(p2); // 将 p2 对象添加到 LinkedList 集合中products.add(p3); // 将 p3 对象添加到 LinkedList 集合中products.add(p4); // 将 p4 对象添加到 LinkedList 集合中String p5 new String(标准文件夹小柜);products.addLast(p5); // 向集合的末尾添加p5对象System.out.print(*************** 商品信息 ***************);System.out.println(\n目前商品有);for (int i 0; i products.size(); i) {System.out.print(products.get(i) \t);}System.out.println(\n第一个商品的名称为 products.getFirst());System.out.println(最后一个商品的名称为 products.getLast());products.removeLast(); // 删除最后一个元素System.out.println(删除最后的元素目前商品有);for (int i 0; i products.size(); i) {System.out.print(products.get(i) \t);}} }6.1.2.3 ArrayList类和LinkedList类区别 * ArrayList 与 LinkedList 都是 List 接口的实现类因此都实现了 List 的所有未实现的方法只是实现的方式有所不同。* ArrayList 是基于动态数组数据结构的实现访问元素速度优于 LinkedList。LinkedList 是基于链表数据结构的实现占用的内存空间比较大但在批量插入或删除数据时优于 ArrayList。* 对于快速访问对象的需求使用 ArrayList 实现执行效率上会比较好。需要频繁向集合中插入和删除元素时使用 LinkedList 类比 ArrayList 类效果高。6.1.3 Set集合 Set 集合类似于一个罐子程序可以依次把多个对象“丢进”Set 集合而 Set 集合通常不能记住元素的添加顺序。也就是说 Set 集合中的对象不按特定的方式排序只是简单地把对象加入集合。Set 集合中不能包含重复的对象并且最多只允许包含一个 null 元素。Set 实现了 Collection 接口它主要有两个常用的实现类HashSet 类和 TreeSet类。 6.1.3.1 HashSet类 HashSet 是 Set 接口的典型实现大多数时候使用 Set 集合时就是使用这个实现类。HashSet 是按照 Hash 算法来存储集合中的元素。因此具有很好的存取和查找性能。HashSet 具有以下特点1. 不能保证元素的排列顺序顺序可能与添加顺序不同顺序也有可能发生变化。2. HashSet 不是同步的如果多个线程同时访问或修改一个 HashSet则必须通过代码来保证其同步。3. 集合元素值可以是 null。在 HashSet 类中实现了 Collection 接口中的所有方法。HashSet 类的常用构造方法重载形式如下。1. HashSet()构造一个新的空的 Set 集合。2. HashSet(Collection? extends Ec)构造一个包含指定 Collection 集合元素的新 Set 集合。其中“ ”中的 extends 表示 HashSet 的父类即指明该 Set 集合中存放的集合元素类型。c 表示其中的元素将被存放在此 Set 集合中。下面的代码演示了创建两种不同形式的 HashSet 对象。* HashSet hs new HashSet(); // 调用无参的构造函数创建HashSet对象* HashSetString hss new HashSetString(); // 创建泛型的 HashSet 集合对象示例: 使用 HashSet 创建一个 Set 集合并向该集合中添加 4 套教程。具体实现代码如下首先使用 HashSet 类的构造方法创建了一个 Set 集合接着创建了 4 个 String 类型的对象并将这些对象存储到 Set 集合中。使用 HashSet 类中的 iterator() 方法获取一个 Iterator 对象并调用其 hasNext() 方法遍历集合元素再将使用 next() 方法读取的元素强制转换为 String 类型。最后调用 HashSet 类中的 size() 方法获取集合元素个数。public static void main(String[] args) {HashSetString courseSet new HashSetString(); // 创建一个空的 Set 集合String course1 new String(Java入门教程);String course2 new String(Python基础教程);String course3 new String(C语言学习教程);String course4 new String(Golang入门教程);courseSet.add(course1); // 将 course1 存储到 Set 集合中courseSet.add(course2); // 将 course2 存储到 Set 集合中courseSet.add(course3); // 将 course3 存储到 Set 集合中courseSet.add(course4); // 将 course4 存储到 Set 集合中System.out.println(C语言中文网教程有);IteratorString it courseSet.iterator();while (it.hasNext()) {System.out.println(《 (String) it.next() 》); // 输出 Set 集合中的元素}System.out.println(有 courseSet.size() 套精彩教程); }6.1.3.2 TreeSet类 TreeSet 类同时实现了 Set 接口和 SortedSet 接口。SortedSet 接口是 Set 接口的子接口可以实现对集合进行自然排序。 TreeSet 只能对实现了 Comparable 接口的类对象进行排序。实现Comparable接口类对象比较方法 类比较方式包装类BigDecimal、Biglnteger、 Byte、Double、 Float、Integer、Long 及 Short)按数字大小比较Character按字符的 Unicode 值的数字大小比较String按字符串中字符的 Unicode 值的数字大小比较 TreeSet类常用方法 方法名称说明E first()返回此集合中的第一个元素。其中E 表示集合中元素的数据类型E last()返回此集合中的最后一个元素E poolFirst()获取并移除此集合中的第一个元素E poolLast()获取并移除此集合中的最后一个元素SortedSet subSet(E fromElement,E toElement)返回一个新的集合新集合包含原集合中 fromElement 对象与 toElement 对象之间的所有对象。包含 fromElement 对象不包含 toElement 对象SortedSet headSetE toElement〉返回一个新的集合新集合包含原集合中 toElement 对象之前的所有对象。 不包含 toElement 对象SortedSet tailSet(E fromElement)返回一个新的集合新集合包含原集合中 fromElement 对象之后的所有对 象。包含 fromElement 对象 示例: 使用 TreeSet 类来创建 Set 集合完成学生成绩查询功能。创建一个 TreeSet 集合对象 scores并向该集合中添加 5 个 Double 对象。接着使用 while 循环遍历 scores 集合对象输出该对象中的元素然后调用 TreeSet 类中的 contains() 方法获取该集合中是否存在指定的元素。最后分别调用 TreeSet 类中的 headSet() 方法和 tailSet() 方法获取不及格的成绩和 90 分以上的成绩。public class Test08 {public static void main(String[] args) {TreeSetDouble scores new TreeSetDouble(); // 创建 TreeSet 集合Scanner input new Scanner(System.in);System.out.println(------------学生成绩管理系统-------------);for (int i 0; i 5; i) {System.out.println(第 (i 1) 个学生成绩);double score input.nextDouble();// 将学生成绩转换为Double类型添加到TreeSet集合中scores.add(Double.valueOf(score));}IteratorDouble it scores.iterator(); // 创建 Iterator 对象System.out.println(学生成绩从低到高的排序为);while (it.hasNext()) {System.out.print(it.next() \t);}System.out.println(\n请输入要查询的成绩);double searchScore input.nextDouble();if (scores.contains(searchScore)) {System.out.println(成绩为 searchScore 的学生存在);} else {System.out.println(成绩为 searchScore 的学生不存在);}// 查询不及格的学生成绩SortedSetDouble score1 scores.headSet(60.0);System.out.println(\n不及格的成绩有);for (int i 0; i score1.toArray().length; i) {System.out.print(score1.toArray()[i] \t);}// 查询90分以上的学生成绩SortedSetDouble score2 scores.tailSet(90.0);System.out.println(\n90 分以上的成绩有);for (int i 0; i score2.toArray().length; i) {System.out.print(score2.toArray()[i] \t);}} }6.1.4 Map集合 Map 是一种键-值对key-value集合Map 集合中的每一个元素都包含一个键key对象和一个值value对象。用于保存具有映射关系的数据。Map 接口主要有两个实现类HashMap 类和 TreeMap 类。其中HashMap 类按哈希算法来存取键对象而 TreeMap 类可以对键对象进行排序。Map接口常用方法 void clear()删除该 Map 对象中的所有 key-value 对。boolean containsKey(Object key)查询 Map 中是否包含指定的 key如果包含则返回 true。boolean containsValue(Object value)查询 Map 中是否包含一个或多个 value如果包含则返回 true。V get(Object key)返回 Map 集合中指定键对象所对应的值。V 表示值的数据类型V put(K key, V value)向 Map 集合中添加键-值对如果当前 Map 中已有一个与该 key 相等的 key-value 对则新的 key-value 对会覆盖原来的 key-value 对。void putAll(Map m)将指定 Map 中的 key-value 对复制到本 Map 中。V remove(Object key)从 Map 集合中删除 key 对应的键-值对返回 key 对应的 value如果该 key 不存在则返回 nullboolean remove(Object key, Object value)这是 Java 8 新增的方法删除指定 key、value 所对应的 key-value 对。如果从该 Map 中成功地删除该 key-value 对该方法返回 true否则返回 false。Set entrySet()返回 Map 集合中所有键-值对的 Set 集合此 Set 集合中元素的数据类型为 Map.EntrySet keySet()返回 Map 集合中所有键对象的 Set 集合boolean isEmpty()查询该 Map 是否为空即不包含任何 key-value 对如果为空则返回 true。int size()返回该 Map 里 key-value 对的个数Collection values()返回该 Map 里所有 value 组成的 Collection Map 集合最典型的用法就是成对地添加、删除 key-value 对接下来即可判断该 Map 中是否包含指定 key也可以通过 Map 提供的 keySet() 方法获取所有 key 组成的集合进而遍历 Map 中所有的 key-value 对。使用 HashMap 来存储学生信息其键为学生学号值为姓名。毕业时需要用户输入学生的学号并根据学号进行删除操作。public class Test09 {public static void main(String[] args) {HashMap users new HashMap();users.put(11, 张浩太); // 将学生信息键值对存储到Map中users.put(22, 刘思诚);users.put(33, 王强文);users.put(44, 李国量);users.put(55, 王路路);System.out.println(******** 学生列表 ********);Iterator it users.keySet().iterator();while (it.hasNext()) {// 遍历 MapObject key it.next();Object val users.get(key);System.out.println(学号 key 姓名: val);}Scanner input new Scanner(System.in);System.out.println(请输入要删除的学号);int num input.nextInt();if (users.containsKey(String.valueOf(num))) { // 判断是否包含指定键users.remove(String.valueOf(num)); // 如果包含就删除} else {System.out.println(该学生不存在);}System.out.println(******** 学生列表 ********);it users.keySet().iterator();while (it.hasNext()) {Object key it.next();Object val users.get(key);System.out.println(学号 key 姓名 val);}}}6.1.4.1 遍历Map集合 Map 集合的遍历与 List 和 Set 集合不同。Map 有两组值因此遍历时可以只遍历值的集合也可以只遍历键的集合也可以同时遍历。Map 以及实现 Map 的接口类如 HashMap、TreeMap、LinkedHashMap、Hashtable 等都可以用以下几种方式遍历。6.1.4.2 for循环遍历 在 for 循环中使用 entries 实现 Map 的遍历最常见和最常用的。public static void main(String[] args) {MapString, String map new HashMapString, String();map.put(Java入门教程, http://c.biancheng.net/java/);map.put(C语言入门教程, http://c.biancheng.net/c/);for (Map.EntryString, String entry : map.entrySet()) {String mapKey entry.getKey();String mapValue entry.getValue();System.out.println(mapKey mapValue);}}6.1.4.3 for-each循环遍历 使用 for-each 循环遍历 key 或者 values一般适用于只需要 Map 中的 key 或者 value 时使用。性能上比 entrySet 较好。MapString, String map new HashMapString, String();map.put(Java入门教程, http://c.biancheng.net/java/);map.put(C语言入门教程, http://c.biancheng.net/c/);// 打印键集合for (String key : map.keySet()) {System.out.println(key);}// 打印值集合for (String value : map.values()) {System.out.println(value);}6.1.4.4 迭代器(Iterator)遍历 MapString, String map new HashMapString, String();map.put(Java入门教程, http://c.biancheng.net/java/);map.put(C语言入门教程, http://c.biancheng.net/c/);IteratorEntryString, String entries map.entrySet().iterator();while (entries.hasNext()) {EntryString, String entry entries.next();String key entry.getKey();String value entry.getValue();System.out.println(key : value);}6.1.4.5 键值遍历 通过键找值遍历这种方式的效率比较低因为本身从键取值是耗时的操作。for(String key : map.keySet()){String value map.get(key);System.out.println(key:value);}6.2 Collections类 Collections 类是 Java 提供的一个操作 Set、List 和 Map 等集合的工具类。Collections 类提供了许多操作集合的静态方法借助这些静态方法可以实现集合元素的排序、查找替换和复制等操作。6.2.1 排序(正向与逆向) Collections 提供了如下方法用于对 List 集合元素进行排序。* void reverse(List list)对指定 List 集合元素进行逆向排序。* void shuffle(List list)对 List 集合元素进行随机排序shuffle 方法模拟了“洗牌”动作。* void sort(List list)根据元素的自然顺序对指定 List 集合的元素按升序进行排序。* void sort(List list, Comparator c)根据指定 Comparator 产生的顺序对 List 集合元素进行排序。* void swap(List list, int i, int j)将指定 List 集合中的 i 处元素和 j 处元素进行交换。* void rotate(List list, int distance)当 distance 为正数时将 list 集合的后 distance 个元素“整体”移到前面当 distance 为负数时将 list 集合的前 distance 个元素“整体”移到后面。该方法不会改变集合的长度。示例:正向 编写一个程序对用户输入的 5 个商品价格进行排序后输出。这里要求使用 Collections 类中 sort() 方法按从低到高的顺序对其进行排序最后将排序后的成绩输出。public class Test1 {public static void main(String[] args) {Scanner input new Scanner(System.in);List prices new ArrayList();for (int i 0; i 5; i) {System.out.println(请输入第 (i 1) 个商品的价格);int p input.nextInt();prices.add(Integer.valueOf(p)); // 将录入的价格保存到List集合中}Collections.sort(prices); // 调用sort()方法对集合进行排序System.out.println(价格从低到高的排列为);for (int i 0; i prices.size(); i) {System.out.print(prices.get(i) \t);}}}示例:反向 使用 Collections 类的 reverse() 方法对保存到 List 集合中的 5 个商品名称进行反转排序并输出排序后的商品信息。public class Test2 {public static void main(String[] args) {Scanner input new Scanner(System.in);List students new ArrayList();System.out.println(******** 商品信息 ********);for (int i 0; i 5; i) {System.out.println(请输入第 (i 1) 个商品的名称);String name input.next();students.add(name); // 将录入的商品名称存到List集合中}Collections.reverse(students); // 调用reverse()方法对集合元素进行反转排序System.out.println(按录入时间的先后顺序进行降序排列为);for (int i 0; i 5; i) {System.out.print(students.get(i) \t);}}}6.2.2 查找、替换操作 Collections 还提供了如下常用的用于查找、替换集合元素的方法。* int binarySearch(List list, Object key)使用二分搜索法搜索指定的 List 集合以获得指定对象在 List 集合中的索引。如果要使该方法可以正常工作则必须保证 List 中的元素已经处于有序状态。* Object max(Collection coll)根据元素的自然顺序返回给定集合中的最大元素。* Object max(Collection coll, Comparator comp)根据 Comparator 指定的顺序返回给定集合中的最大元素。* Object min(Collection coll)根据元素的自然顺序返回给定集合中的最小元素。* Object min(Collection coll, Comparator comp)根据 Comparator 指定的顺序返回给定集合中的最小元素。* void fill(List list, Object obj)使用指定元素 obj 替换指定 List 集合中的所有元素。* int frequency(Collection c, Object o)返回指定集合中指定元素的出现次数。* int indexOfSubList(List source, List target)返回子 List 对象在父 List 对象中第一次出现的位置索引如果父 List 中没有出现这样的子 List则返回 -1。* int lastIndexOfSubList(List source, List target)返回子 List 对象在父 List 对象中最后一次出现的位置索引如果父 List 中没有岀现这样的子 List则返回 -1。* boolean replaceAll(List list, Object oldVal, Object newVal)使用一个新值 newVal 替换 List 对象的所有旧值 oldVal。6.2.2.1 fil()方法 编写一个程序要求用户输入 3 个商品名称然后使用 Collections 类中的 fill() 方法对商品信息进行重置操作即将所有名称都更改为“未填写”:public class Test3 {public static void main(String[] args) {Scanner input new Scanner(System.in);List products new ArrayList();System.out.println(******** 商品信息 ********);for (int i 0; i 3; i) {System.out.println(请输入第 (i 1) 个商品的名称);String name input.next();products.add(name); // 将用户录入的商品名称保存到List集合中}System.out.println(重置商品信息将所有名称都更改为未填写);Collections.fill(products, 未填写);System.out.println(重置后的商品信息为);for (int i 0; i products.size(); i) {System.out.print(products.get(i) \t);}}}6.2.2.2 replaceAll()方法 向 List 集合中添加 4 个数据然后调用 Collections 类中的 max() 和 min() 方法输出集合中的最大最小元素replaceAll() 替换元素frequency() 判断指定数据在 List 集合中出现的次数最后用 binarySearch() 进行二分法查询。public class Test4 {public static void main(String[] args) {ArrayList nums new ArrayList();nums.add(2);nums.add(-5);nums.add(3);nums.add(0);System.out.println(nums); // 输出[2, -5, 3, 0]System.out.println(Collections.max(nums)); // 输出最大元素将输出 3System.out.println(Collections.min(nums)); // 输出最小元素将输出-5Collections.replaceAll(nums, 0, 1);// 将 nums中的 0 使用 1 来代替System.out.println(nums); // 输出[2, -5, 3, 1]// 判断-5在List集合中出现的次数返回1System.out.println(Collections.frequency(nums, -5));Collections.sort(nums); // 对 nums集合排序System.out.println(nums); // 输出[-5, 1, 2, 3]// 只有排序后的List集合才可用二分法查询输出3System.out.println(Collections.binarySearch(nums, 3));}}6.2.3 复制 Collections 类的 copy() 静态方法用于将指定集合中的所有元素复制到另一个集合中。执行 copy() 方法后目标集合中每个已复制元素的索引将等同于源集合中该元素的索引。copy() 方法的语法格式如下void copy(List ? super T dest,List? extends T src)示例:在一个集合中保存了 5 个商品名称现在要使用 Collections 类中的 copy() 方法将其中的 3 个替换掉。具体实现的代码如下public class Test5 {public static void main(String[] args) {Scanner input new Scanner(System.in);List srcList new ArrayList();List destList new ArrayList();destList.add(苏打水);destList.add(木糖醇);destList.add(方便面);destList.add(火腿肠);destList.add(冰红茶);System.out.println(原有商品如下);for (int i 0; i destList.size(); i) {System.out.println(destList.get(i));}System.out.println(输入替换的商品名称);for (int i 0; i 3; i) {System.out.println(第 (i 1) 个商品);String name input.next();srcList.add(name);}// 调用copy()方法将当前商品信息复制到原有商品信息集合中Collections.copy(destList, srcList);System.out.println(当前商品有);for (int i 0; i destList.size(); i) {System.out.print(destList.get(i) \t);}}} 如上述代码首先创建了两个 List 对象 srcList 和 destList并向 destList 集合中添加了 5 个元素向 srcList 集合中添加了 3 个元素然后调用 Collections 类中 copy() 方法将 srcList 集合中的全部元素复制到 destList 集合中。由于 destList 集合中含有 5 个元素故最后两个元素不会被覆盖。6.3 Lambda表达式遍历Collection集合 Java 8 为 Iterable 接口新增了一个 forEach(Consumer action) 默认方法该方法所需参数的类型是一个函数式接口而 Iterable 接口是 Collection 接口的父接口因此 Collection 集合也可直接调用该方法。当程序调用 Iterable 的 forEach(Consumer action) 遍历集合元素时程序会依次将集合元素传给 Consumer 的 accept(T t) 方法该接口中唯一的抽象方法。正因为 Consumer 是函数式接口因此可以使用 Lambda 表达式来遍历集合元素。如下程序示范了使用 Lambda 表达式来遍历集合元素。public class CollectionEach {public static void main(String[] args) {// 创建一个集合Collection objs new HashSet();objs.add(C语言中文网Java教程);objs.add(C语言中文网C语言教程);objs.add(C语言中文网C教程);// 调用forEach()方法遍历集合objs.forEach(obj - System.out.println(迭代集合元素 obj));}}6.3.1 迭代器遍历集合元素 Iterator迭代器是一个接口它的作用就是遍历容器的所有元素也是 Java 集合框架的成员但它与 Collection 和 Map 系列的集合不一样Collection 和 Map 系列集合主要用于盛装其他对象而 Iterator 则主要用于遍历即迭代访问Collection 集合中的元素。Iterator 接口隐藏了各种 Collection 实现类的底层细节向应用程序提供了遍历 Collection 集合元素的统一编程接口。Iterator 接口里定义了如下 4 个方法:1. boolean hasNext()如果被迭代的集合元素还没有被遍历完则返回 true。2. Object next()返回集合里的下一个元素。3. void remove()删除集合里上一次 next 方法返回的元素。4. void forEachRemaining(Consumer action)这是 Java 8 为 Iterator 新增的默认方法该方法可使用 Lambda 表达式来遍历集合元素。示例:通过 Iterator 接口来遍历集合元素import java.util.Collection; import java.util.HashSet; import java.util.Iterator;public class IteratorTest {public static void main(String[] args) {// 创建一个集合Collection objs new HashSet();objs.add(C语言中文网Java教程);objs.add(C语言中文网C语言教程);objs.add(C语言中文网C教程);// 调用forEach()方法遍历集合// 获取books集合对应的迭代器Iterator it objs.iterator();while (it.hasNext()) {// it.next()方法返回的数据类型是Object类型因此需要强制类型转换String obj (String) it.next();System.out.println(obj);if (obj.equals(C语言中文网C语言教程)) {// 从集合中删除上一次next()方法返回的元素it.remove();}// 对book变量赋值不会改变集合元素本身obj C语言中文网Python语言教程;}System.out.println(objs);}}6.3.2 Lambda表达式遍历迭代器 Java 8 为 Iterator 引入了一个 forEachRemaining(Consumer action) 默认方法该方法所需的 Consumer 参数同样也是函数式接口。当程序调用 Iterator 的 forEachRemaining(Consumer action) 遍历集合元素时程序会依次将集合元素传给 Consumer 的 accept(T t) 方法该接口中唯一的抽象方法。java.util.function 中的 Function、Supplier、Consumer、Predicate 和其他函数式接口被广泛用在支持 Lambda 表达式的 API 中。“void accept(T t);”是 Consumer 的核心方法用来对给定的参数 T 执行定义操作。如下程序示范了使用 Lambda 表达式来遍历集合元素。public class IteratorEach {public static void main(String[] args) {// 创建一个集合Collection objs new HashSet();objs.add(C语言中文网Java教程);objs.add(C语言中文网C语言教程);objs.add(C语言中文网C教程);// 获取objs集合对应的迭代器Iterator it objs.iterator();// 使用Lambda表达式目标类型是Comsumer来遍历集合元素it.forEachRemaining(obj - System.out.println(迭代集合元素 obj));}}6.3.3 for-reach遍历Collection集合 使用 foreach 循环来迭代访问集合元素。public class ForeachTest {public static void main(String[] args) {// 创建一个集合Collection objs new HashSet();objs.add(C语言中文网Java教程);objs.add(C语言中文网C语言教程);objs.add(C语言中文网C教程);for (Object obj : objs) {// 此处的obj变量也不是集合元素本身String obj1 (String) obj;System.out.println(obj1);if (obj1.equals(C语言中文网Java教程)) {// 下面代码会引发 ConcurrentModificationException 异常objs.remove(obj);}}System.out.println(objs);}}6.4 Predicate操作Collection集合 Java 8 起为 Collection 集合新增了一个 removeIf(Predicate filter) 方法该方法将会批量删除符合 filter 条件的所有元素。该方法需要一个 Predicate 对象作为参数Predicate 也是函数式接口因此可使用 Lambda 表达式作为参数。示范了使用 Predicate 来过滤集合。public class ForeachTest {public static void main(String[] args) {// 创建一个集合Collection objs new HashSet();objs.add(new String(C语言中文网Java教程));objs.add(new String(C语言中文网C教程));objs.add(new String(C语言中文网C语言教程));objs.add(new String(C语言中文网Python教程));objs.add(new String(C语言中文网Go教程));// 使用Lambda表达式(目标类型是Predicate)过滤集合objs.removeIf(ele - ((String) ele).length() 12);System.out.println(objs);}}6.5 Stream操作Collection集合 Java 8 还新增了 Stream、IntStream、LongStream、DoubleStream 等流式 API这些 API 代表多个支持串行和并行聚集操作的元素。上面 4 个接口中Stream 是一个通用的流接口而 IntStream、LongStream、 DoubleStream 则代表元素类型为 int、long、double 的流。独立使用 Stream 的步骤如下1. 使用 Stream 或 XxxStream 的 builder() 类方法创建该 Stream 对应的 Builder。2. 重复调用 Builder 的 add() 方法向该流中添加多个元素。3. 调用 Builder 的 build() 方法获取对应的 Stream。4. 调用 Stream 的聚集方法。Stream 提供了大量的方法进行聚集操作这些方法既可以是“中间的”intermediate也可以是 末端的terminal。* 中间方法中间操作允许流保持打开状态并允许直接调用后续方法。上面程序中的 map() 方法就是中间方法。中间方法的返回值是另外一个流。* 末端方法末端方法是对流的最终操作。当对某个 Stream 执行末端方法后该流将会被“消耗”且不再可用。上面程序中的 sum()、count()、average() 等方法都是末端方法。除此之外关于流的方法还有如下两个特征。* 有状态的方法这种方法会给流增加一些新的属性比如元素的唯一性、元素的最大数量、保证元素以排序的方式被处理等。有状态的方法往往需要更大的性能开销。* 短路方法短路方法可以尽早结束对流的操作不必检查所有的元素。示例: 对于大部分聚集方法而言每个 Stream 只能执行一次。例如如下程序。创建了一个 IntStream接下来分别多次调用 IntStream 的聚集方法执行操作这样即可获取该流的相关信息。注意上面 5~13 行代码每次只能执行一行因此需要把其他代码注释掉。public class IntStreamTest {public static void main(String[] args) {IntStream is IntStream.builder().add(20).add(13).add(-2).add(18).build();// 下面调用聚集方法的代码每次只能执行一行System.out.println(is 所有元素的最大值 is.max().getAsInt());System.out.println(is 所有元素的最小值 is.min().getAsInt());System.out.println(is 所有元素的总和 is.sum());System.out.println(is 所有元素的总数 is.count());System.out.println(is 所有元素的平均值 is.average());System.out.println(is所有元素的平方是否都大于20: is.allMatch(ele - ele * ele 20));System.out.println(is是否包含任何元素的平方大于20 : is.anyMatch(ele - ele * ele 20));// 将is映射成一个新Stream,新Stream的每个元素是原Stream元素的2倍1IntStream newIs is.map(ele - ele * 2 1);// 使用方法引用的方式来遍历集合元素newIs.forEach(System.out::println); // 输岀 41 27 -3 37}}Stream常用中间方法 方法说明filter(Predicate predicate)过滤 Stream 中所有不符合 predicate 的元素mapToXxx(ToXxxFunction mapper)使用 ToXxxFunction 对流中的元素执行一对一的转换该方法返回的新流中包含了 ToXxxFunction 转换生成的所有元素。peek(Consumer action)依次对每个元素执行一些操作该方法返回的流与原有流包含相同的元素。该方法主要用于调试。distinct()该方法用于排序流中所有重复的元素判断元素重复的标准是使用 equals() 比较返回 true。这是一个有状态的方法。sorted()该方法用于保证流中的元素在后续的访问中处于有序状态。这是一个有状态的方法。limit(long maxSize)该方法用于保证对该流的后续访问中最大允许访问的元素个数。这是一个有状态的、短路方法。 Stream常用末端方法 方法说明forEach(Consumer action)遍历流中所有元素对每个元素执行actiontoArray()将流中所有元素转换为一个数组reduce()该方法有三个重载的版本都用于通过某种操作来合并流中的元素min()返回流中所有元素的最小值max()返回流中所有元素的最大值count()返回流中所有元素的数量anyMatch(Predicate predicate)判断流中是否至少包含一个元素符合 Predicate 条件。allMatch(Predicate predicate)判断流中是否每个元素都符合 Predicate 条件noneMatch(Predicate predicate)判断流中是否所有元素都不符合 Predicate 条件findFirst()返回流中的第一个元素findAny()返回流中的任意一个元素 6.6 JAVA9新增不可变集合 Java 9 版本以前假如要创建一个包含 6 个元素的 Set 集合程序需要先创建 Set 集合然后调用 6 次 add() 方法向 Set 集合中添加元素。Java 9 对此进行了简化程序直接调用 Set、List、Map 的 of() 方法即可创建包含 N 个元素的不可变集合这样一行代码就可创建包含 N 个元素的集合。不可变意味着程序不能向集合中添加元素也不能从集合中删除元素。示例: 如何创建不可变集合。public class Java9Collection {public static void main(String[] args) {// 创建包含4个元素的Set集合Set set Set.of(Java, Kotlin, Go, Swift);System.out.println(set);// 不可变集合下面代码导致运行时错误// set.add(Ruby);// 创建包含4个元素的List集合List list List.of(34, -25, 67, 231);System.out.println(list);// 不可变集合下面代码导致运行时错误// list.remove(1);// 创建包含3个key-value对的Map集合Map map Map.of(语文, 89, 数学, 82, 英语, 92);System.out.println(map);// 不可变集合下面代码导致运行时错误// map.remove(语文);// 使用Map.entry()方法显式构建key-value对Map map2 Map.ofEntries(Map.entry(语文, 89), Map.entry(数学, 82), Map.entry(英语, 92));System.out.println(map2);}}6.7 泛型 泛型可以在编译的时候检查类型安全并且所有的强制转换都是自动和隐式的提高了代码的重用率。6.7.1 泛型集合 泛型本质上是提供类型的“类型参数”也就是参数化类型。1首先需要创建一个表示图书的实体类 Book其中包括的图书信息有图书编号、图书名称和价格。Book 类的具体代码如下public class Book {private int Id; // 图书编号private String Name; // 图书名称private int Price; // 图书价格public Book(int id, String name, int price) { // 构造方法this.Id id;this.Name name;this.Price price;}public String toString() { // 重写 toString()方法return this.Id , this.Name this.Price;}}2使用 Book 作为类型创建 Map 和 List 两个泛型集合然后向集合中添加图书元素最后输出集合中的内容。具体代码如下public class Test14 {public static void main(String[] args) {// 创建3个Book对象Book book1 new Book(1, 唐诗三百首, 8);Book book2 new Book(2, 小星星, 12);Book book3 new Book(3, 成语大全, 22);MapInteger, Book books new HashMapInteger, Book(); // 定义泛型 Map 集合books.put(1001, book1); // 将第一个 Book 对象存储到 Map 中books.put(1002, book2); // 将第二个 Book 对象存储到 Map 中books.put(1003, book3); // 将第三个 Book 对象存储到 Map 中System.out.println(泛型Map存储的图书信息如下);for (Integer id : books.keySet()) {// 遍历键System.out.print(id ——);System.out.println(books.get(id)); // 不需要类型转换}ListBook bookList new ArrayListBook(); // 定义泛型的 List 集合bookList.add(book1);bookList.add(book2);bookList.add(book3);System.out.println(泛型List存储的图书信息如下);for (int i 0; i bookList.size(); i) {System.out.println(bookList.get(i)); // 这里不需要类型转换}}}6.7.2 泛型类 除了可以定义泛型集合之外还可以直接限定泛型类的类型参数。语法格式如下public class class_namedata_type1,data_type2,…{}其中class_name 表示类的名称data_ type1 等表示类型参数。Java 泛型支持声明一个以上的类型参数只需要将类型用逗号隔开即可。泛型类一般用于类中的属性类型不确定的情况下。在声明属性时使用下面的语句private data_type1 property_name1;private data_type2 property_name2;该语句中的 data_type1 与类声明中的 data_type1 表示的是同一种数据类型。 示例: 在实例化泛型类时需要指明泛型类中的类型参数并赋予泛型类属性相应类型的值。例如下面的示例代码创建了一个表示学生的泛型类该类中包括 3 个属性分别是姓名、年龄和性别。public class StuN, A, S {private N name; // 姓名private A age; // 年龄private S sex; // 性别// 创建类的构造函数public Stu(N name, A age, S sex) {this.name name;this.age age;this.sex sex;}// 下面是上面3个属性的setter/getter方法public N getName() {return name;}public void setName(N name) {this.name name;}public A getAge() {return age;}public void setAge(A age) {this.age age;}public S getSex() {return sex;}public void setSex(S sex) {this.sex sex;}}接着创建测试类。在测试类中调用 Stu 类的构造方法实例化 Stu 对象并给该类中的 3 个属性赋予初始值最终需要输出学生信息。测试类的代码实现如下public class Test14 {public static void main(String[] args) {StuString, Integer, Character stu new StuString, Integer, Character(张晓玲, 28, 女);String name stu.getName();Integer age stu.getAge();Character sex stu.getSex();System.out.println(学生信息如下);System.out.println(学生姓名 name 年龄 age 性别 sex);}}6.7.3 泛型方法 如果 static 方法需要使用泛型能力就必须使其成为泛型方法。定义泛型方法的语法格式如下[访问权限修饰符] [static] [final] 类型参数列表 返回值类型 方法名([形式参数列表]) 例如public static T List find(ClassT cs,int userId){}示例: 一般来说编写 Java 泛型方法其返回值类型至少有一个参数类型应该是泛型而且类型应该是一致的如果只有返回值类型或参数类型之一使用了泛型那么这个泛型方法的使用就被限制了。下面就来定义一个泛型方法具体介绍泛型方法的创建和使用。 使用泛型方法打印图书信息。定义泛型方法参数类型使用“T”来代替。在方法的主体中打印出图书信息。代码的实现如下 public class Test16 {public static T void List(T book) { // 定义泛型方法if (book ! null) {System.out.println(book);}}public static void main(String[] args) {Book stu new Book(1, 细学 Java 编程, 28);List(stu); // 调用泛型方法} }6.7.4 泛型高级用法 泛型的用法非常灵活除在集合、类和方法中使用外本节将从三个方面介绍泛型的高级用法包括限制泛型可用类型、使用类型通配符、继承泛型类和实现泛型接口。6.7.4.1 限制泛型可用类型 1. 限制泛型可用类型 在 Java 中默认可以使用任何类型来实例化一个泛型类对象。当然也可以对泛型类实例的类型进行限制anyClass 指某个接口或类。使用泛型限制后泛型类的类型必须实现或继承 anyClass 这个接口或类。无论 anyClass 是接口还是类在进行泛型限制时都必须使用 extends 关键字。语法格式如下class 类名称T extends anyClass在下面的示例代码中创建了一个 ListClass 类并对该类的类型限制为只能是实现 List 接口的类。// 限制ListClass的泛型类型必须实现List接口public class ListClassT extends List {public static void main(String[] args) {// 实例化使用ArrayList的泛型类ListClass正确ListClassArrayList lc1 new ListClassArrayList();// 实例化使用LinkedList的泛型类LlstClass正确ListClassLinkedList lc2 new ListClassLinkedList();// 实例化使用HashMap的泛型类ListClass错误因为HasMap没有实现List接口// ListClassHashMap lc3new ListClassHashMap();}}6.7.4.2 使用类型通配符 Java 中的泛型还支持使用类型通配符它的作用是在创建一个泛型类对象时限制这个泛型类的类型必须实现或继承某个接口或类。“? extends List”作为一个整体表示类型未知当需要使用泛型对象时可以单独实例化。使用泛型类型通配符的语法格式如下泛型类名称? extends Lista null;示例代码演示了类型通配符的使用。A? extends Lista null;a new AArrayList (); // 正确b new ALinkedList (); // 正确c new AHashMap (); // 错误6.7.4.3 继承泛型类和实现泛型接口 定义为泛型的类和接口也可以被继承和实现。例如下面的示例代码演示了如何继承泛型类。public class FatherClassT1{}public class SonClassT1,T2,T3 extents FatherClassT1{}如果要在 SonClass 类继承 FatherClass 类时保留父类的泛型类型需要在继承时指定否则直接使用 extends FatherClass 语句进行继承操作此时 T1、T2 和 T3 都会自动变为 Object所以一般情况下都将父类的泛型类型保留。下面的示例代码演示了如何在泛型中实现接口。interface interface1T1{}interface SubClassT1,T2,T3 implementsInterface1T2{}6.8 JAVA示例-图书信息查询 使用 Map 映射来存储类别和图书信息其键为 Category类别类型值为 ListBook 类型Book 类为图书类然后使用嵌套循环遍历输出每个类别所对应的多个图书信息。使用了泛型 List 和泛型 Map 分别存储图书类别和特定类别下的图书明细信息。1创建表示图书类别的 Category 类在该类中有两个属性id 和 name分别表示编号和类别名称并实现了它们的 setXxx() 和 getXxx() 方法具体内容如下public class Category {private int id; // 类别编号private String name; // 类别名称public Category(int id, String name) {this.id id;this.name name;}public String toString() {return 所属分类 this.name;}// 上面两个属性的setXxx()和getXxx()方法public int getId() {return id;}public void setId(int id) {this.id id;}public String getName() {return name;}public void setName(String name) {this.name name;}}2创建表示图书明细信息的 BookInfo 类在该类中包含 5 个属性id、name、price、author 和 startTime分别表示图书编号、名称、价格、作者和出版时间同样实现了它们的 setXxx() 和 getXxx() 方法具体内容如下public class BookInfo {private int id; // 编号private String name; // 名称private int price; // 价格private String author; // 作者private String startTime; // 出版时间public BookInfo(int id, String name, int price, String author, String startTime) {this.id id;this.name name;this.price price;this.author author;this.startTime startTime;}public String toString() {return this.id \t\t this.name \t\t this.price \t\t this.author \t\t this.startTime;}// 上面5个属性的 setXxx() 和 getXxx() 方法public int getId() {return id;}public void setId(int id) {this.id id;}public String getName() {return name;}public void setName(String name) {this.name name;}public int getPrice() {return price;}public void setPrice(int price) {this.id price;}public String getAuthor() {return author;}public void setAuthor(String author) {this.author author;}public String getStartTime() {return startTime;}public void setStartTime(String startTime) {this.startTime startTime;}}3创建 CategoryDao 类在该类中定义一个泛型的 Map 映射其键为 Category 类型的对象值为 ListBookInfo 类型的对象并定义 printCategoryInfo() 方法用于打印类别和图书明细信息。具体代码如下public class CategoryDao {// 定义泛型Map存储图书信息public static MapCategory, ListBookInfo categoryMap new HashMapCategory, ListBookInfo();public static void printDeptmentInfo() {for (Category cate : categoryMap.keySet()) {System.out.println(所属类别 cate.getName());ListBookInfo books categoryMap.get(cate);System.out.println(图书编号\t\t图书名称\t\t图书价格\t\t图书作者\t\t出版时间);for (int i 0; i books.size(); i) {BookInfo b books.get(i); // 获取图书System.out.println(b.getId() \t\t b.getName() \t\t b.getPrice() \t\t b.getAuthor() \t\t b.getStartTime());}System.out.println();}}}4创建测试类 Test17在该类中定义 4 个 Deptment 对象和 8 个 People 对象并将 8 个 People 对象分成 4 组存储到 4 个 List 集合中然后将 4 个 Deptment 对象和 4 个 List 集合按照——对应的关系存储到 DeptmentDao 类中的 peoplesMap 映射中。最后调用 DeptmentDao 类中的 printDeptmentInfo() 方法打印类别及对应的图书信息。具体的代码如下public class Test17 {public static void main(String[] args) {Category category1 new Category(1, 数据库); // 创建类别信息Category category2 new Category(2, 程序设计); // 创建类别信息Category category3 new Category(3, 平面设计); // 创建类别信息BookInfo book1 new BookInfo(1, 细说 Java 编程, 25, 张晓玲, 2012-01-01); // 创建图书信息BookInfo book2 new BookInfo(2, 影视后期处理宝典, 78, 刘水波, 2012-10-05); // 创建图书信息BookInfo book3 new BookInfo(3, MySQL 从入门到精通, 41, 王志亮, 2012-3-2); // 创建图书信息BookInfo book4 new BookInfo(4, Java 从入门到精通, 27, 陈奚静, 2012-11-01); // 创建图书信息BookInfo book5 new BookInfo(5, SQL Server 一百例, 68, 张晓玲, 2012-01-01); // 创建图书信息ListBookInfo pList1 new ArrayListBookInfo(); // 向类别 1 添加图书pList1.add(book1);pList1.add(book4);ListBookInfo pList2 new ArrayListBookInfo(); // 向类别 2 添加图书pList2.add(book3);pList2.add(book5);ListBookInfo pList3 new ArrayListBookInfo(); // 向类别 3 添加图书pList3.add(book2);CategoryDao.categoryMap.put(category1, pList1);CategoryDao.categoryMap.put(category2, pList2);CategoryDao.categoryMap.put(category3, pList3);CategoryDao.printDeptmentInfo();}}6.9 枚举 枚举是一个被命名的整型常数的集合用于声明一组带标识符的常数。枚举在曰常生活中很常见例如一个人的性别只能是“男”或者“女”一周的星期只能是 7 天中的一个等。类似这种当一个变量有几种固定可能的取值时就可以将它定义为枚举类型。6.9.1 声明枚举 声明枚举时必须使用 enum 关键字然后定义枚举的名称、可访问性、基础类型和成员等。enum-modifiers 表示枚举的修饰符主要包括 public、private 和 internalenumname 表示声明的枚举名称enum-base 表示基础类型enum-body 表示枚举的成员它是枚举类型的命名常数。枚举声明的语法如下enum-modifiers enum enumname:enum-base {enum-body,}示例: 下面代码定义了一个表示性别的枚举类型 SexEnum 和一个表示颜色的枚举类型 Color。public enum SexEnum {male,female;}public enum Color {RED,BLUE,GREEN,BLACK;}之后便可以通过枚举类型名直接引用常量如 SexEnum.male、Color.RED。使用枚举还可以使 switch 语句的可读性更强例如以下示例代码enum Signal {// 定义一个枚举类型GREEN,YELLOW,RED}public class TrafficLight {Signal color Signal.RED;public void change() {switch(color) {case RED:color Signal.GREEN;break;case YELLOW:color Signal.RED;break;case GREEN:color Signal.YELLOW;break;}}}6.9.2 枚举类 Java 中的每一个枚举都继承自 java.lang.Enum 类。当定义一个枚举类型时每一个枚举类型成员都可以看作是 Enum 类的实例这些枚举成员默认都被 final、public, static 修饰当使用枚举类型成员时直接使用枚举名称调用成员即可。所有枚举实例都可以调用 Enum 类的方法.Enum类常用方法 方法名称描述values()以数组形式返回枚举类型的所有成员valueOf()将普通字符串转换为枚举实例compareTo()比较两个枚举成员在定义时的顺序ordinal()获取枚举成员的索引位置 6.9.2.1 values()方法 示例: 通过调用枚举类型实例的 values( ) 方法可以将枚举的所有成员以数组形式返回也可以通过该方法获取枚举类型的成员。下面的示例创建一个包含 3 个成员的枚举类型 Signal然后调用 values() 方法输出这些成员。enum Signal {// 定义一个枚举类型GREEN,YELLOW,RED;}public static void main(String[] args) {for(int i 0;i Signal.values().length;i) {System.out.println(枚举成员Signal.values()[i]);}}6.9.2.2 valueOf()方法 创建一个示例调用 valueOf() 方法获取枚举的一个成员再调用 compareTo() 方法进行比较并输出结果。具体实现代码如下public class TestEnum {public enum Sex {// 定义一个枚举male,female;}public static void main(String[] args) {compare(Sex.valueOf(male)); // 比较}public static void compare(Sex s) {for(int i 0;i Sex.values().length;i) {System.out.println(s 与 Sex.values()[i] 的比较结果是 s.compareTo(Sex.values()[i]));}}}6.9.2.3 ordinal() 方法 通过调用枚举类型实例的ordinal() 方法可以获取一个成员在枚举中的索引位置。下面的示例创建一个包含 3 个成员的枚举类型 Signal然后调用 ordinal() 方法输出成员及对应索引位置。具体实现代码如下public class TestEnum1 {enum Signal {// 定义一个枚举类型GREEN,YELLOW,RED;}public static void main(String[] args) {for(int i 0;i Signal.values().length;i) {System.out.println(索引 Signal.values()[i].ordinal()值 Signal.values()[i]);}}}6.9.3 为枚举添加方法 Java 为枚举类型提供了一些内置的方法同时枚举常量也可以有自己的方法。此时要注意必须在枚举实例的最后一个成员后添加分号而且必须先定义枚举实例。 代码创建了一个枚举类型 WeekDay而且在该类型中添加了自定义的方法。enum WeekDay {Mon(Monday),Tue(Tuesday),Wed(Wednesday),Thu(Thursday),Fri(Friday),Sat(Saturday),Sun(Sunday);// 以上是枚举的成员必须先定义而且使用分号结束private final String day;private WeekDay(String day) {this.day day;}public static void printDay(int i) {switch(i) {case 1:System.out.println(WeekDay.Mon);break;case 2:System.out.println(WeekDay.Tue);break;case 3:System.out.println(WeekDay.Wed);break;case 4:System.out.println(WeekDay.Thu);break;case 5:System.out.println(WeekDay.Fri);break;case 6:System.out.println(WeekDay.Sat);break;case 7:System.out.println(WeekDay.Sun);break;default:System.out.println(wrong number!);}}public String getDay() {return day;}}上面代码创建了 WeekDay 枚举类型下面遍历该枚举中的所有成员并调用 printDay() 方法。示例代码如下public static void main(String[] args) {for(WeekDay day : WeekDay.values()) {System.out.println(day day.getDay());}WeekDay.printDay(5);}Java 中的 enum 还可以跟 Class 类一样覆盖基类的方法。下面示例代码创建的 Color 枚举类型覆盖了 toString() 方法。public class Test {public enum Color {RED(红色,1),GREEN(绿色,2),WHITE(白色,3),YELLOW(黄色,4);// 成员变量private String name;private int index;// 构造方法private Color(String name,int index) {this.name name;this.index index;}// 覆盖方法Overridepublic String toString() {return this.index - this.name;}}public static void main(String[] args) {System.out.println(Color.RED.toString()); // 输出1-红色}}6.9.3.1 EnumMap与EnumSet 为了更好地支持枚举类型java.util 中添加了两个新类EnumMap 和 EnumSet。使用它们可以更高效地操作枚举类型。 6.9.3.2 EnumMap 类 EnumMap 是专门为枚举类型量身定做的 Map 实现。虽然使用其他的 Map如 HashMap实现也能完成枚举类型实例到值的映射但是使用 EnumMap 会更加高效。HashMap 只能接收同一枚举类型的实例作为键值并且由于枚举类型实例的数量相对固定并且有限所以 EnumMap 使用数组来存放与枚举类型对应的值使得 EnumMap 的效率非常高。 下面是使用 EnumMap 的一个代码示例。枚举类型 DataBaseType 里存放了现在支持的所有数据库类型。针对不同的数据库一些数据库相关的方法需要返回不一样的值例如示例中 getURL() 方法。// 定义数据库类型枚举public enum DataBaseType {MYSQUORACLE,DB2,SQLSERVER}// 某类中定义的获取数据库URL的方法以及EnumMap的声明private EnumMapDataBaseType,Stringurls new EnumMapDataBaseType,String(DataBaseType.class);public DataBaseInfo() {urls.put(DataBaseType.DB2,jdbc:db2://localhost:5000/sample);urls.put(DataBaseType.MYSQL,jdbc:mysql://localhost/mydb);urls.put(DataBaseType.ORACLE,jdbc:oracle:thin:localhost:1521:sample);urls.put(DataBaseType.SQLSERVER,jdbc:microsoft:sqlserver://sql:1433;Databasemydb);}//根据不同的数据库类型返回对应的URL// param type DataBaseType 枚举类新实例// returnpublic String getURL(DataBaseType type) {return this.urls.get(type);}6.9.3.3 EnumSet类 EnumSet 是枚举类型的高性能 Set 实现它要求放入它的枚举常量必须属于同一枚举类型。EnumSet类常用方法 方法名称描述allOf(Class element type)创建一个包含指定枚举类型中所有枚举成员的 EnumSet 对象complementOf(EnumSet s)创建一个与指定 EnumSet 对象 s 相同的枚举类型 EnumSet 对象 并包含所有 s 中未包含的枚举成员copyOf(EnumSet s)创建一个与指定 EnumSet 对象 s 相同的枚举类型 EnumSet 对象 并与 s 包含相同的枚举成员noneOf(Class elementType)创建指定枚举类型的空 EnumSet 对象of(E first,e…rest)创建包含指定枚举成员的 EnumSet 对象range(E from ,E to)创建一个 EnumSet 对象该对象包含了 from 到 to 之间的所有枚 举成员 示例: numSet 作为 Set 接口实现它支持对包含的枚举常量的遍历。for(Operation op:EnumSet.range(Operation.PLUS,Operation.MULTIPLY)) {doSomeThing(op);}6.10 JAVA示例 6.10.1 一对多关系示例 如一个学校可以包含多个学生一个学生属于一个学校那么这就是一个典型的一对多关系可以通过集合进行关系的表示。下面是基于集合应用的一个示例这个示例将作为以后 Java EE 开发的基础。1定义学生类import java.util.HashSet;import java.util.Iterator;public class Student {private String name; // 定义student类private int age; // 定义name属性private School school; // 一个学生属于一个学校// 通过构造方法设置内容public Student(String name, int age) {this.setName(name);this.setAge(age);}public String getName() {return name;}public void setName(String name) {this.name name;}public int getAge() {return age;}public void setAge(int age) {this.age age;}public School getSchool() {return school;}public void setSchool(School school) {this.school school;}// 重写toString()方法public String toString() {return 学生姓名 this.name 年龄 this.age;}} 在以上的 Student 类中包含了一个 School 属性表示一个学生属于一个学校。在程序运行时只需要传入 School 类的引用就可以完成这样的关系。2定义学校类 import java.util.ArrayList; import java.util.List;public class School {private String name;private ListStudent allStudents; // 一个学校有多个学生public School() {this.allStudents new ArrayListStudent();// 实例化List集合}public School(String name) {this();this.setName(name);}public String getName() {return name;}public void setName(String name) {this.name name;}public ListStudent getAllStudents() {return allStudents;}public void setAllStudents(ListStudent allStudents) {this.allStudents allStudents;}// 重写toString()方法public String toString() {return 学校名称 this.name;}} 在定义学校类时定义了一个 List 类型的属性并指定其泛型类型是 Student 类型这样一来就表示在一个 School 对象中会包含多个 Student 类型的引用。3测试代码设置关系import java.util.Iterator;public class Test {public static void main(String[] args) {// 实例化学校对象School sch new School(清华大学);// 实例化学生对象Student s1 new Student(张三, 21);Student s2 new Student(李四, 22);Student s3 new Student(王五, 23);// 在学校中加入学生sch.getAllStudents().add(s1);sch.getAllStudents().add(s2);sch.getAllStudents().add(s3);// 一个学生属于一个学校s1.setSchool(sch);s2.setSchool(sch);s3.setSchool(sch);// 输出学校信息System.out.println(sch);// 实例化Iterator对象用于输出全部的学生信息IteratorStudent ite sch.getAllStudents().iterator();while (ite.hasNext()) {System.out.println(\t ite.next());}}}6.10.2 多对多关系示例 使用集合不仅可以表示一对一的关系也可以表示多对多的关系。例如一个学生可以选多门课程一门课程可以有多个学生参加那么这就是一个典型的多对多关系。要完成上面要求首先应该定义两个类分别是学生信息Student类、课程信息Course类。在学生类中存在一个集合保存全部的课程。同样在课程类中也要存在一个集合保存全部的学生。1定义学生类public class Student {private String name;private int age;private ListCourse allCourses; // 定义集合保存全部课程private Student() {this.allCourses new ArrayListCourse();// 实例化List集合}// 通过构造方法设置内容public Student(String name, int age) {// 调用无参构造this();this.setName(name);this.setAge(age);}public String getName() {return name;}public void setName(String name) {this.name name;}public int getAge() {return age;}public void setAge(int age) {this.age age;}public ListCourse getAllCourses() {return allCourses;}public void setAllCourses(ListCourse allCourses) {this.allCourses allCourses;}// 重写toString()方法public String toString() {return 学生姓名 this.name 年龄 this.age;}} 在学生类中存在一个 allCourses 的 List 集合这样在程序运行时一个学生类中可以保存多个 Course 对象。2定义课程类public class Course {private String name;private int credit;// 定义集合保存多个学生private ListStudent allStudents;private Course() {// 实例化List集合this.allStudents new ArrayListStudent();}public Course(String name, int credit) {this();this.setName(name);this.setCredit(credit);}public String getName() {return name;}public void setName(String name) {this.name name;}public int getCredit() {return credit;}public void setCredit(int credit) {this.credit credit;}public ListStudent getAllStudents() {return allStudents;}public void setAllStudents(ListStudent allStudents) {this.allStudents allStudents;}// 重写toString()方法public String toString() {return 课程名称 this.name 课程学分 this.credit;}} 课程类与学生类一样都定义了一个 List 集合用于保存多个学生信息。3测试程序public class TestMore {public static void main(String[] args) {// 实例化课程对象Course c1 new Course(英语, 3);Course c2 new Course(计算机, 5);// 实例化学生对象Student s1 new Student(张三, 20);Student s2 new Student(李四, 21);Student s3 new Student(王五, 22);Student s4 new Student(赵六, 23);Student s5 new Student(孙七, 24);Student s6 new Student(钱八, 25);// 第一门课程有3个人参加向课程中增加3个学生信息同时向学生中增加课程信息c1.getAllStudents().add(s1);c1.getAllStudents().add(s2);c1.getAllStudents().add(s6);s1.getAllCourses().add(c1);s2.getAllCourses().add(c1);s6.getAllCourses().add(c1);// 第二门课程有6个人参加向课程中增加6个学生信息同时向学生中添加课程信息// 向课程中增加学生信息c2.getAllStudents().add(s1);c2.getAllStudents().add(s2);c2.getAllStudents().add(s3);c2.getAllStudents().add(s4);c2.getAllStudents().add(s5);c2.getAllStudents().add(s6);// 像学生中增加课程信息s1.getAllCourses().add(c2);s2.getAllCourses().add(c2);s3.getAllCourses().add(c2);s4.getAllCourses().add(c2);s5.getAllCourses().add(c2);s6.getAllCourses().add(c2);// 输出一门课程的信息观察一门课程有多少个学生参加System.out.println(c1); // 输出第一门课程IteratorStudent iter1 c1.getAllStudents().iterator();// 迭代输出while (iter1.hasNext()) {Student s iter1.next();System.out.println(\t s);}// 输出一个学生参加的课程信息观察有多少门课程System.out.println(s6);IteratorCourse iter2 s6.getAllCourses().iterator();while (iter2.hasNext()) {// 取得所参加的课程Course c iter2.next();// 输出课程信息System.out.println(\t c);}}}七、反射机制 Java 反射机制是 Java 语言的一个重要特性。学习 Java 反射机制前应该先了解两个概念编译期和运行期。编译期是指把源码交给编译器编译成计算机可以执行的文件的过程。在 Java 中也就是把 Java 代码编成class文件的过程。编译期只是做了一些翻译功能并没有把代码放在内存中运行起来而只是把代码当成文本进行操作比如检查错误。运行期是把编译后的文件交给计算机执行直到程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来。Java 反射机制是在运行状态中对于任意一个类都能够知道这个类的所有属性和方法对于任意一个对象都能够调用它的任意方法和属性这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。简单来说反射机制指的是程序在运行时能够获取自身的信息。在 Java 中只要给定类的名字就可以通过反射机制来获得类的所有信息。Java 反射机制在服务器程序和中间件程序中得到了广泛运用。在服务器端往往需要根据客户的请求动态调用某一个对象的特定方法。此外在 ORM 中间件的实现中运用 Java 反射机制可以读取任意一个 JavaBean 的所有属性或者给这些属性赋值。Java 反射机制主要提供了以下功能这些功能都位于java.lang.reflect包。* 在运行时判断任意一个对象所属的类。* 在运行时构造任意一个类的对象。* 在运行时判断任意一个类所具有的成员变量和方法。* 在运行时调用任意一个对象的方法。* 生成动态代理。所有 Java 类均继承了 Object 类在 Object 类中定义了一个 getClass() 方法该方法返回同一个类型为 Class 的对象。例如下面的示例代码Class labelCls label1.getClass(); // label1为 JLabel 类的对象反射可访问常用信息 类型访问方法返回值类型说明包路径getPackage()Package 对象获取该类的存放路径类名称getName()String 对象获取该类的名称继承类getSuperclass()Class 对象获取该类继承的类实现接口getlnterfaces()Class 型数组获取该类实现的所有接口构造方法getConstructors()Constructor 型数组获取所有权限为 public 的构造方法getDeclaredContruectors()Constructor 对象获取当前对象的所有构造方法方法getMethods()Methods 型数组获取所有权限为 public 的方法getDeclaredMethods()Methods 对象获取当前对象的所有方法成员变量getFields()Field 型数组获取所有权限为 public 的成员变量getDeclareFileds()Field 对象获取当前对象的所有成员变量内部类getClasses()Class 型数组获取所有权限为 public 的内部类getDeclaredClasses()Class 型数组获取所有内部类内部类的声明类getDeclaringClass()Class 对象如果该类为内部类则返回它的成员类否则返回 null 7.1 反射机制优缺点 优点1. 能够运行时动态获取类的实例大大提高系统的灵活性和扩展性。2. 与 Java 动态编译相结合可以实现无比强大的功能。3. 对于 Java 这种先编译再运行的语言能够让我们很方便的创建灵活的代码这些代码可以在运行时装配无需在组件之间进行源代码的链接更加容易实现面向对象。缺点1. 反射会消耗一定的系统资源因此如果不需要动态地创建一个对象那么就不需要用反射2. 反射调用方法时可以忽略权限检查获取这个类的私有方法和属性因此可能会破坏类的封装性而导致安全问题。7.2 反射机制API 实现 Java 反射机制的类都位于 java.lang.reflect 包中java.lang.Class 类是 Java 反射机制 API 中的核心类。7.2.1 java.lang.Class类 java.lang.Class 类是实现反射的关键所在Class 类的一个实例表示 Java 的一种数据类型包括类、接口、枚举、注Annotation、数组、基本数据类型和 void。Class 没有公有的构造方法Class 实例是由 JVM 在类加载时自动创建的。程序代码中获得 Class 实例可以通过如下代码实现// 1. 通过类型class静态变量Class clz1 String.class;String str Hello;// 2. 通过对象的getClass()方法Class clz2 str.getClass();每一种类型包括类和接口等都有一个 class 静态变量可以获得 Class 实例。另外每一个对象都有 getClass() 方法可以获得 Class 实例该方法是由 Object 类提供的实例方法。Class 类提供了很多方法可以获得运行时对象的相关信息:public class ReflectionTest01 {public static void main(String[] args) {// 获得Class实例// 1.通过类型class静态变量Class clz1 String.class;String str Hello;// 2.通过对象的getClass()方法Class clz2 str.getClass();// 获得int类型Class实例Class clz3 int.class;// 获得Integer类型Class实例Class clz4 Integer.class;System.out.println(clz2类名称 clz2.getName());System.out.println(clz2是否为接口 clz2.isInterface());System.out.println(clz2是否为数组对象 clz2.isArray());System.out.println(clz2父类名称 clz2.getSuperclass().getName());System.out.println(clz2是否为基本类型 clz2.isPrimitive());System.out.println(clz3是否为基本类型 clz3.isPrimitive());System.out.println(clz4是否为基本类型 clz4.isPrimitive());}}7.2.2 java.lang.reflect包 java.lang.reflect 包提供了反射中用到类主要的类说明如下* Constructor 类提供类的构造方法信息。* Field 类提供类或接口中成员变量信息。* Method 类提供类或接口成员方法信息。* Array 类提供了动态创建和访问 Java 数组的方法。* Modifier 类提供类和成员访问修饰符信息。示例: 过 Class 的静态方法forName(String)创建某个类的运行时对象其中的参数是类全名字符串如果在类路径中找不到这个类则抛出 ClassNotFoundException 异常见代码第 17 行。代码第 7 行是通过 Class 的实例方法 getDeclaredMethods() 返回某个类的成员方法对象数组。代码第 9 行是遍历成员方法集合其中的元素是 Method 类型。代码第 11 行的method.getModifiers()方法返回访问权限修饰符常量代码是 int 类型例如 1 代表 public这些数字代表的含义可以通过Modifier.toString(int)方法转换为字符串。代码第 13 行通过 Method 的 getReturnType() 方法获得方法返回值类型然后再调用 getName() 方法返回该类型的名称。代码第 15 行method.getName()返回方法名称。public class ReflectionTest02 {public static void main(String[] args) {try {// 动态加载xx类的运行时对象Class c Class.forName(java.lang.String);// 获取成员方法集合Method[] methods c.getDeclaredMethods();// 遍历成员方法集合for (Method method : methods) {// 打印权限修饰符如public、protected、privateSystem.out.print(Modifier.toString(method.getModifiers()));// 打印返回值类型名称System.out.print( method.getReturnType().getName() );// 打印方法名称System.out.println(method.getName() (););}} catch (ClassNotFoundException e) {System.out.println(找不到指定类);}}}7.3 通过反射访问构造方法 为了能够动态获取对象构造方法的信息首先需要通过下列方法之一创建一个 Constructor 类型的对象或者数组。* getConstructors()* getConstructor(Class?…parameterTypes)* getDeclaredConstructors()* getDeclaredConstructor(Class?...parameterTypes)如果是访问指定的构造方法需要根据该构造方法的入口参数的类型来访问。例如访问一个入口参数类型依次为 int 和 String 类型的构造方法下面的两种方式均可以实现。1. objectClass.getDeclaredConstructor(int.class,String.class);2. objectClass.getDeclaredConstructor(new Class[]{int.class,String.class});下列代码判断对象 con 所代表的构造方法是否被 public 修饰以及以字符串形式获取该构造方法的所有修饰符。int modifiers con.getModifiers(); // 获取构造方法的修饰符整数boolean isPublic Modifier.isPublic(modifiers); // 判断修饰符整数是否为public string allModifiers Modifier.toString(modifiers);Constructor类常用方法 方法名称说明isVarArgs()查看该构造方法是否允许带可变数量的参数如果允许返回 true否则返回 falsegetParameterTypes()按照声明顺序以 Class 数组的形式获取该构造方法各个参数的类型getExceptionTypes()以 Class 数组的形式获取该构造方法可能抛出的异常类型newInstance(Object … initargs)通过该构造方法利用指定参数创建一个该类型的对象如果未设置参数则表示 采用默认无参的构造方法setAccessiable(boolean flag)如果该构造方法的权限为 private默认为不允许通过反射利用 netlnstance() 方法创建对象。如果先执行该方法并将入口参数设置为 true则允许创建对 象getModifiers()获得可以解析出该构造方法所采用修饰符的整数 Modifier类常用方法 静态方法名称说明isStatic(int mod)如果使用 static 修饰符修饰则返回 true否则返回 falseisPublic(int mod)如果使用 public 修饰符修饰则返回 true否则返回 falseisProtected(int mod)如果使用 protected 修饰符修饰则返回 true否则返回 falseisPrivate(int mod)如果使用 private 修饰符修饰则返回 true否则返回 falseisFinal(int mod)如果使用 final 修饰符修饰则返回 true否则返回 falsetoString(int mod)以字符串形式返回所有修饰符 示例: 1首先创建一个 Book 类表示图书信息。在该类中声明一个 String 型变量表示图书名称两个 int 型变量分别表示图书编号和价格并提供 3 个构造方法。Book 类的最终代码如下public class Book {String name; // 图书名称int id, price; // 图书编号和价格// 空的构造方法private Book() {}// 带两个参数的构造方法protected Book(String _name, int _id) {this.name _name;this.id _id;}// 带可变参数的构造方法public Book(String... strings) throws NumberFormatException {if (0 strings.length)id Integer.valueOf(strings[0]);if (1 strings.length)price Integer.valueOf(strings[1]);}// 输出图书信息public void print() {System.out.println(name name);System.out.println(id id);System.out.println(price price);}}2编写测试类 Test01在该类的 main() 方法中通过反射访问 Book 类中的所有构造方法并将该构造方法是否带可变类型参数、入口参数类型和可能拋出的异常类型信息输出到控制台。Test01 类的代码如下public class Test01 {public static void main(String[] args) {// 获取动态类BookClass book Book.class;// 获取Book类的所有构造方法Constructor[] declaredContructors book.getDeclaredConstructors();// 遍历所有构造方法for (int i 0; i declaredContructors.length; i) {Constructor con declaredContructors[i];// 判断构造方法的参数是否可变System.out.println(查看是否允许带可变数量的参数 con.isVarArgs());System.out.println(该构造方法的入口参数类型依次为);// 获取所有参数类型Class[] parameterTypes con.getParameterTypes();for (int j 0; j parameterTypes.length; j) {System.out.println( parameterTypes[j]);}System.out.println(该构造方法可能拋出的异常类型为);// 获取所有可能拋出的异常类型Class[] exceptionTypes con.getExceptionTypes();for (int j 0; j exceptionTypes.length; j) {System.out.println( parameterTypes[j]);}// 创建一个未实例化的Book类实例Book book1 null;while (book1 null) {try { // 如果该成员变量的访问权限为private则拋出异常if (i 1) {// 通过执行带两个参数的构造方法实例化book1book1 (Book) con.newInstance(Java 教程, 10);} else if (i 2) {// 通过执行默认构造方法实例化book1book1 (Book) con.newInstance();} else {// 通过执行可变数量参数的构造方法实例化book1Object[] parameters new Object[] { new String[] { 100, 200 } };book1 (Book) con.newInstance(parameters);}} catch (Exception e) {System.out.println(在创建对象时拋出异常下面执行 setAccessible() 方法);con.setAccessible(true); // 设置允许访问 private 成员}}book1.print();System.out.println(\n);}}}7.4 通过反射访问方法 要动态获取一个对象方法的信息首先需要通过下列方法之一创建一个 Method 类型的对象或者数组。* getMethods()* getMethods(String name,Class? …parameterTypes)* getDeclaredMethods()* getDeclaredMethods(String name,Class?...parameterTypes)如果是访问指定的构造方法需要根据该方法的入口参数的类型来访问。例如访问一个名称为 max入口参数类型依次为 int 和 String 类型的方法。下面的两种方式均可以实现objectClass.getDeclaredConstructor(max,int.class,String.class);objectClass.getDeclaredConstructor(max,new Class[]{int.class,String.class});Method类常用方法 静态方法名称说明getName()获取该方法的名称getParameterType()按照声明顺序以 Class 数组的形式返回该方法各个参数的类型getReturnType()以 Class 对象的形式获得该方法的返回值类型getExceptionTypes()以 Class 数组的形式获得该方法可能抛出的异常类型invoke(Object obj,Object…args)利用 args 参数执行指定对象 obj 中的该方法返回值为 Object 类型isVarArgs()查看该方法是否允许带有可变数量的参数如果允许返回 true否则返回 falsegetModifiers()获得可以解析出该方法所采用修饰符的整数 示例: 1首先创建一个 Book1 类并编写 4 个具有不同作用域的方法。Book1 类的最终代码如下public class Book1 {// static 作用域方法static void staticMethod() {System.out.println(执行staticMethod()方法);}// public 作用域方法public int publicMethod(int i) {System.out.println(执行publicMethod()方法);return 100 i;}// protected 作用域方法protected int protectedMethod(String s, int i) throws NumberFormatException {System.out.println(执行protectedMethod()方法);return Integer.valueOf(s) i;}// private 作用域方法private String privateMethod(String... strings) {System.out.println(执行privateMethod()方法);StringBuffer sb new StringBuffer();for (int i 0; i sb.length(); i) {sb.append(strings[i]);}return sb.toString();}}2编写测试类 Test02在该类的 main() 方法中通过反射访问 Book1 类中的所有方法并将该方法是否带可变类型参数、入口参数类型和可能拋出的异常类型信息输出到控制台。Test02 类的代码如下public class Test02 {public static void main(String[] args) {// 获取动态类Book1Book1 book new Book1();Class class1 book.getClass();// 获取Book1类的所有方法Method[] declaredMethods class1.getDeclaredMethods();for (int i 0; i declaredMethods.length; i) {Method method declaredMethods[i];System.out.println(方法名称为 method.getName());System.out.println(方法是否带有可变数量的参数 method.isVarArgs());System.out.println(方法的参数类型依次为);// 获取所有参数类型Class[] methodType method.getParameterTypes();for (int j 0; j methodType.length; j) {System.out.println( methodType[j]);}// 获取返回值类型System.out.println(方法的返回值类型为 method.getReturnType());System.out.println(方法可能抛出的异常类型有);// 获取所有可能抛出的异常Class[] methodExceptions method.getExceptionTypes();for (int j 0; j methodExceptions.length; j) {System.out.println( methodExceptions[j]);}boolean isTurn true;while (isTurn) {try { // 如果该成员变量的访问权限为private则抛出异常isTurn false;if (method.getName().equals(staticMethod)) { // 调用没有参数的方法method.invoke(book);} else if (method.getName().equals(publicMethod)) { // 调用一个参数的方法System.out.println(publicMethod(10)的返回值为 method.invoke(book, 10));} else if (method.getName().equals(protectedMethod)) { // 调用两个参数的方法System.out.println(protectedMethod(\10\,15)的返回值为 method.invoke(book, 10, 15));} else if (method.getName().equals(privateMethod)) { // 调用可变数量参数的方法Object[] parameters new Object[] { new String[] { J, A, V, A } };System.out.println(privateMethod()的返回值为 method.invoke(book, parameters));}} catch (Exception e) {System.out.println(在设置成员变量值时抛出异常下面执行setAccessible()方法);method.setAccessible(true); // 设置为允许访问private方法isTurn true;}}System.out.println(\n);}}}7.5 反射访问成员变量 通过下列任意一个方法访问成员变量时将返回 Field 类型的对象或数组。* getFields()* getField(String name)* getDeclaredFields()* getDeclaredField(String name)上述方法返回的 Field 对象代表一个成员变量。例如要访问一个名称为 price 的成员变量示例代码如下object.getDeciaredField(price);Field类常用方法 方法名称说明getName()获得该成员变量的名称getType()获取表示该成员变量的 Class 对象get(Object obj)获得指定对象 obj 中成员变量的值返回值为 Object 类型set(Object obj, Object value)将指定对象 obj 中成员变量的值设置为 valuegetlnt(0bject obj)获得指定对象 obj 中成员类型为 int 的成员变量的值setlnt(0bject obj, int i)将指定对象 obj 中成员变量的值设置为 isetFloat(Object obj, float f)将指定对象 obj 中成员变量的值设置为 fgetBoolean(Object obj)获得指定对象 obj 中成员类型为 boolean 的成员变量的值setBoolean(Object obj, boolean b)将指定对象 obj 中成员变量的值设置为 bgetFloat(Object obj)获得指定对象 obj 中成员类型为 float 的成员变量的值setAccessible(boolean flag)此方法可以设置是否忽略权限直接访问 private 等私有权限的成员变量getModifiers()获得可以解析出该方法所采用修饰符的整数 示例: 1首先创建一个 Book2 类在该类中依次声明一个 String、int、float 和 boolean 类型的成员并设置不同的访问作用域。Book2 类最终的代码如下public class Book2 {String name;public int id;private float price;protected boolean isLoan;}2编写测试类 Test03在该类的 main() 方法中通过反射访问 Book2 类中的所有成员并将该成员的名称和类型信息输出到控制台。Test03 类的代码如下import java.lang.reflect.Constructor;import java.lang.reflect.Field;public class Test03 {public static void main(String[] args) {Book2 book new Book2();// 获取动态类Book2Class class1 book.getClass();// 获取Book2类的所有成员Field[] declaredFields class1.getDeclaredFields();// 遍历所有的成员for(int i 0;i declaredFields.length;i) { // 获取类中的成员变量Field field declaredFields[i];System.out.println(成员名称为 field.getName());Class fieldType field.getType();System.out.println(成员类型为 fieldType);boolean isTurn true;while(isTurn) {try { // 如果该成员变量的访问权限为private则抛出异常isTurn false;System.out.println(修改前成员的值为 field.get(book));// 判断成员类型是否为intif(fieldType.equals(int.class)) {System.out.println(利用setInt()方法修改成员的值);field.setInt(book, 100);} else if(fieldType.equals(float.class)) { // 判断成员变量类型是否为floatSystem.out.println(利用setFloat()方法修改成员的值);field.setFloat(book, 29.815f);} else if(fieldType.equals(boolean.class)) { // 判断成员变量是否为booleanSystem.out.println(利用setBoolean()方法修改成员的值);field.setBoolean(book, true);} else {System.out.println(利用set()方法修改成员的值);field.set(book, Java编程);}System.out.println(修改后成员的值为 field.get(book));} catch (Exception e) {System.out.println(在设置成员变量值时抛出异常下面执行setAccessible()方法);field.setAccessible(true);isTurn true;}}System.out.println(\n);}}}7.6 远程方法中使用反射机制 反射机制在网络编程中的应用实现如何在客户端通过远程方法调用服务器端的方法。示例: 假定在服务器端有一个 HelloService 接口该接口具有 getTime() 和 echo() 方法具体代码如下import java.util.Date;public interface HelloService {public String echo(String msg);public Date getTime();}在服务器上创建一个 HelloServiceImpl 类并实现 HelloService 接口。HelloServiceImpl 类的代码如下import java.util.Date;public class HelloServiceImpl implements HelloService {Overridepublic String echo(String msg) {return echo: msg;}Overridepublic Date getTime() {return new Date();}} 上述代码所示在 HelloServiceImpl 类中对 echo() 方法和 getTime() 方法进行了重写。那么客户端如何调用服务器端 Hello-ServiceImpl 类中的 getTime() 和 echo() 方法呢具体方法是客户端需要把调用的方法名、方法参数类型、方法参数值以及方法所属的类名或接口名发送给服务器端。服务器端再调用相关对象的方法然后把方法的返回值发送给客户端。为了便于按照面向对象的方式来处理客户端与服务器端的通信可以把它们发送的信息用 Call 类来表示。一个 Call 对象表示客户端发起的一个远程调用它包括调用的类名或接口名、方法名、方法参数类型、方法参数值和方法执行结果。Call 类的实现代码如下 import java.io.Serializable;public class Call implements Serializable {private static final long serialVersionUID 6659953547331194808L;private String className; // 表示类名或接口名private String methodName; // 表示方法名private Class[] paramTypes; // 表示方法参数类型private Object[] params; // 表示方法参数值// 表示方法的执行结果// 如果方法正常执行则result为方法返回值如果方法抛出异常那么result为该异常。private Object result;public Call() {}public Call(String className, String methodName, Class[] paramTypes, Object[] params) {this.className className;this.methodName methodName;this.paramTypes paramTypes;this.params params;}public String getClassName() {return className;}public void setClassName(String className) {this.className className;}public String getMethodName() {return methodName;}public void setMethodName(String methodName) {this.methodName methodName;}public Class[] getParamTypes() {return paramTypes;}public void setParamTypes(Class[] paramTypes) {this.paramTypes paramTypes;}public Object[] getParams() {return params;}public void setParams(Object[] params) {this.params params;}public Object getResult() {return result;}public void setResult(Object result) {this.result result;}public String toString() {return className className methodName methodName;}}假设客户端为 SimpleClient服务器端为 SimpleServer。SimpleClient 调用 SimpleServer 的 HelloServiceImpl 对象中 echo() 方法的流程如下* SimpleClient 创建一个 Call 对象它包含调用 HelloService 接口的 echo() 方法的信息。* SimpleClient 通过对象输出流把 Call 对象发送给 SimpleServer。* SimpleServer 通过对象输入流读取 Call 对象运用反射机制调用 HelloServiceImpl 对象的 echo() 方法把 echo() 方法的执行结果保存到 Call 对象中。* SimpleServer 通过对象输出流把包含方法执行结果的 Call 对象发送给 SimpleClient。* SimpleClient 通过对象输入流读取 Call 对象从中获得方法执行结果。首先来看看客户端程序 SimpleClient 类的实现代码。 import java.io.*; import java.net.*; import java.util.*; import java.lang.reflect.*; import java.io.*; import java.net.*; import java.util.*;public class SimpleClient {public void invoke() throws Exception {Socket socket new Socket(localhost, 8000);OutputStream out socket.getOutputStream();ObjectOutputStream oos new ObjectOutputStream(out);InputStream in socket.getInputStream();ObjectInputStream ois new ObjectInputStream(in);// 创建一个远程调用对象Call call new Call(ch12.HelloService, echo, new Class[] { String.class }, new Object[] { Java });oos.writeObject(call); // 向服务器发送Call对象call (Call) ois.readObject(); // 接收包含了方法执行结果的Call对象System.out.println(call.getResult());ois.close();oos.close();socket.close();}public static void main(String args[]) throws Exception {new SimpleClient().invoke();}} 如上述代码所示客户端 SimpleClient 类的主要作用是建立与服务器的连接然后将带有调用信息的 Call 对象发送到服务器端。服务器端 SimpleServer 类在收到调用请求之后会使用反射机制动态调用指定对象的指定方法再将执行结果返回给客户端。SimpleServer 类的实现代码如下 import java.io.*; import java.net.*; import java.util.*; import java.lang.reflect.*;public class SimpleServer {private Map remoteObjects new HashMap(); // 存放远程对象的缓存/** 把一个远程对象放到缓存中 */public void register(String className, Object remoteObject) {remoteObjects.put(className, remoteObject);}public void service() throws Exception {ServerSocket serverSocket new ServerSocket(8000);System.out.println(服务器启动.);while (true) {Socket socket serverSocket.accept();InputStream in socket.getInputStream();ObjectInputStream ois new ObjectInputStream(in);OutputStream out socket.getOutputStream();ObjectOutputStream oos new ObjectOutputStream(out);Call call (Call) ois.readObject(); // 接收客户发送的Call对象System.out.println(call);call invoke(call); // 调用相关对象的方法oos.writeObject(call); // 向客户发送包含了执行结果的Call对象ois.close();oos.close();socket.close();}}public Call invoke(Call call) {Object result null;try {String className call.getClassName();String methodName call.getMethodName();Object[] params call.getParams();Class classType Class.forName(className);Class[] paramTypes call.getParamTypes();Method method classType.getMethod(methodName, paramTypes);Object remoteObject remoteObjects.get(className); // 从缓存中取出相关的远程对象if (remoteObject null) {throw new Exception(className 的远程对象不存在);} else {result method.invoke(remoteObject, params);}} catch (Exception e) {result e;}call.setResult(result); // 设置方法执行结果return call;}public static void main(String args[]) throws Exception {SimpleServer server new SimpleServer();// 把事先创建的HelloServiceImpl对象加入到服务器的缓存中server.register(ch13.HelloService, new HelloServiceImpl());server.service();}}由于这是一个网络程序首先需要运行服务器端 SimpleServer然后再运行客户端 SimpleClient。运行结果是在客户端看到输出“echoJava”这个结果是服务器端执行 HelloServicelmpl 对象的 echo() 方法的返回值。图 1 所示显示了 SimpleClient 与 SimpleServer 的通信过程。八、输入与输出流 根据数据流向的不同可以分为输入Input流和输出Output流两种。8.1 输入与输出流概念 Java 程序通过流来完成输入/输出所有的输入/输出以流的形式处理。因此要了解 I/O 系统首先要理解输入/输出流的概念。输入就是将数据从各种输入设备包括文件、键盘等中读取到内存中输出则正好相反是将数据写入到各种输出设备比如文件、显示器、磁盘等。例如键盘就是一个标准的输入设备而显示器就是一个标准的输出设备但是文件既可以作为输入设备又可以作为输出设备。数据流是 Java 进行 I/O 操作的对象它按照不同的标准可以分为不同的类别。* 按照流的方向主要分为输入流和输出流两大类。* 数据流按照数据单位的不同分为字节流和字符流。* 按照功能可以划分为节点流和处理流。8.1.1 输入流 Java 流相关的类都封装在 java.io 包中而且每个数据流都是一个对象。所有输入流类都是 InputStream 抽象类字节输入流和 Reader 抽象类字符输入流的子类。InputStream类常用方法 名称作用int read()从输入流读入一个 8 字节的数据将它转换成一个 0~ 255 的整数返回一个整数如果遇到输入流的结尾返回 -1int read(byte[] b)从输入流读取若干字节的数据保存到参数 b 指定的字节数组中返回的字节数表示读取的字节数如果遇到输入流的结尾返回 -1int read(byte[] b,int off,int len)从输入流读取若干字节的数据保存到参数 b 指定的字节数组中其中 off 是指在数组中开始保存数据位置的起始下标len 是指读取字节的位数。返回的是实际读取的字节数如果遇到输入流的结尾则返回 -1void close()关闭数据流当完成对数据流的操作之后需要关闭数据流int available()返回可以从数据源读取的数据流的位数。skip(long n)从输入流跳过参数 n 指定的字节数目boolean markSupported()判断输入流是否可以重复读取如果可以就返回 truevoid mark(int readLimit)如果输入流可以被重复读取从流的当前位置开始设置标记readLimit 指定可以设置标记的字节数void reset()使输入流重新定位到刚才被标记的位置这样可以重新读取标记过的数据 8.1.2 输出流 在 Java 中所有输出流类都是 OutputStream 抽象类字节输出流和 Writer 抽象类字符输出流的子类。OutputStream常用方法 名称作用int write(b)将指定字节的数据写入到输出流int write (byte[] b)将指定字节数组的内容写入输出流int write (byte[] b,int off,int len)将指定字节数组从 off 位置开始的 len 字节的内容写入输出流close()关闭数据流当完成对数据流的操作之后需要关闭数据流flush()刷新输出流强行将缓冲区的内容写入输出流 8.2 系统流 每个 Java 程序运行时都带有一个系统流系统流对应的类为 java.lang.System。Sytem 类封装了 Java 程序运行时的 3 个系统流分别通过 in、out 和 err 变量来引用。这 3 个系统流如下所示* System.in标准输入流默认设备是键盘。* System.out标准输出流默认设备是控制台。* System.err标准错误流默认设备是控制台。System.in 是 InputStream 类的一个对象,System.out 输出流主要用于将指定内容输出到控制台。System.out 和 System.error 是 PrintStream 类的对象。因为 PrintStream 是一个从 OutputStream 派生的输出流所以它还执行低级别的 write() 方法。因此除了 print() 和 println() 方法可以完成控制台输出以外System.out 还可以调用 write() 方法实现控制台输出。write() 方法的简单形式如下void write(int byteval) throws IOException示例: 使用 System.in 读取字节数组使用 System.out 输出字节数组。public class Test01 {public static void main(String[] args) {byte[] byteData new byte[100]; // 声明一个字节数组System.out.println(请输入英文);try {System.in.read(byteData);} catch (IOException e) {e.printStackTrace();}System.out.println(您输入的内容如下);for (int i 0; i byteData.length; i) {System.out.print((char) byteData[i]);}}}8.3 字符编码 Java 程序的开发中最常见的是 ISO8859-1、GBK/GB2312、Unicode、 UTF 编码。Java 中常见编码说明如下* ISO8859-1属于单字节编码最多只能表示 0~255 的字符范围。* GBK/GB2312中文的国标编码用来表示汉字属于双字节编码。GBK 可以表示简体中文和繁体中文而 GB2312 只能表示简体中文。GBK 兼容 GB2312。* Unicode是一种编码规范是为解决全球字符通用编码而设计的。UTF-8 和 UTF-16 是这种规范的一种实现此编码不兼容 ISO8859-1 编码。Java 内部采用此编码。* UTFUTF 编码兼容了 ISO8859-1 编码同时也可以用来表示所有的语言字符不过 UTF 编码是不定长编码每一个字符的长度为 1~6 个字节不等。一般在中文网页中使用此编码可以节省空间Java 中 System 类可以取得与系统有关的信息所以直接使用此类可以找到系统的默认编码。方法如下所示public static Properties getProperty()示例1: 查看 JVM 的默认编码代码如下public static void main(String[] args) {// 获取当前系统编码System.out.println(系统默认编码 System.getProperty(file.encoding));}示例2: 下面通过一个示例讲解乱码的产生。现在本地的默认编码是 GBK下面通过 ISO8859-1 编码对文字进行编码转换。如果要实现编码的转换可以使用 String 类中的 getBytes(String charset) 方法此方法可以设置指定的编码该方法的格式如下public byte[] getBytes(String charset);public class Test {public static void main(String[] args) throws Exception {File f new File(D: File.separator test.txt);// 实例化输出流OutputStream out new FileOutputStream(f);// 指定ISO8859-1编码byte b[] C语言中文网你好.getBytes(ISO8859-1);// 保存转码之后的数据out.write(b);// 关闭输出流out.close();}}8.4 File类 File 类是 java.io 包中唯一代表磁盘文件本身的对象也就是说如果希望在程序中操作文件和目录则都可以通过 File 类来完成。File 类定义了一些方法来操作文件如新建、删除、重命名文件和目录等。* File 类不能访问文件内容本身如果需要访问文件内容本身则需要使用输入/输出流。File类提供了如下三种形式构造方法。1. File(String path)如果 path 是实际存在的路径则该 File 对象表示的是目录如果 path 是文件名则该 File 对象表示的是文件。2. File(String path, String name)path 是路径名name 是文件名。3. File(File dir, String name)dir 是路径对象name 是文件名。File 类中有以下两个常用常量1. public static final String pathSeparator指的是分隔连续多个路径字符串的分隔符Windows 下指;。例如 java -cp test.jar;abc.jar HelloWorld。2. public static final String separator用来分隔同一个路径字符串中的目录的Windows 下指/。例如 C:/Program Files/Common FilesFile类常用方法 boolean canRead()测试应用程序是否能从指定的文件中进行读取boolean canWrite()测试应用程序是否能写当前文件boolean delete()删除当前对象指定的文件boolean exists()测试当前 File 是否存在String getAbsolutePath()返回由该对象表示的文件的绝对路径名String getName()返回表示当前对象的文件名或路径名如果是路径则返回最后一级子路径名String getParent()返回当前 File 对象所对应目录最后一级子目录的父目录名boolean isAbsolute()测试当前 File 对象表示的文件是否为一个绝对路径名。该方法消除了不同平台的差异可以直接判断 file 对象是否为绝对路径。在 UNIX/Linux/BSD 等系统上如果路径名开头是一条斜线/则表明该 File 对象对应一个绝对路径在 Windows 等系统上如果路径开头是盘符则说明它是一个绝对路径。boolean isDirectory()测试当前 File 对象表示的文件是否为一个路径boolean isFile()测试当前 File 对象表示的文件是否为一个“普通”文件long lastModified()返回当前 File 对象表示的文件最后修改的时间long length()返回当前 File 对象表示的文件长度String[] list()返回当前 File 对象指定的路径文件列表String[] list(FilenameFilter)返回当前 File 对象指定的目录中满足指定过滤器的文件列表boolean mkdir()创建一个目录它的路径名由当前 File 对象指定boolean mkdirs()创建一个目录它的路径名由当前 File 对象指定boolean renameTo(File)将当前 File 对象指定的文件更名为给定参数 File 指定的路径名 8.4.1 获取文件属性 在 Java 中获取文件属性信息的第一步是先创建一个 File 类对象并指向一个已存在的文件 然后调用表 1 中的方法进行操作。 示例: 假设有一个文件位于 C:\windows\notepad.exe。编写 Java 程序获取并显示该文件的长度、是否可写、最后修改日期以及文件路径等属性信息。实现代码如下public class Test02 {public static void main(String[] args) {String path C:/windows/; // 指定文件所在的目录File f new File(path, notepad.exe); // 建立File变量,并设定由f变量引用System.out.println(C:\\windows\\notepad.exe文件信息如下);System.out.println();System.out.println(文件长度 f.length() 字节);System.out.println(文件或者目录 (f.isFile() ? 是文件 : 不是文件));System.out.println(文件或者目录 (f.isDirectory() ? 是目录 : 不是目录));System.out.println(是否可读 (f.canRead() ? 可读取 : 不可读取));System.out.println(是否可写 (f.canWrite() ? 可写入 : 不可写入));System.out.println(是否隐藏 (f.isHidden() ? 是隐藏文件 : 不是隐藏文件));System.out.println(最后修改日期 new Date(f.lastModified()));System.out.println(文件名称 f.getName());System.out.println(文件路径 f.getPath());System.out.println(绝对路径 f.getAbsolutePath());}} 在上述代码中 File 类构造方法的第一个参数指定文件所在位置这里使用C:/作为文件的实际路径第二个参数指定文件名称。创建的 File 类对象为 f然后通过 f 调用方法获取相应的属性最终运行效果如下所示。 8.4.2 创建和删除文件 File 类不仅可以获取已知文件的属性信息还可以在指定路径创建文件以及删除一个文件。创建文件需要调用 createNewFile() 方法删除文件需要调用 delete() 方法。无论是创建还是删除文件通常都先调用 exists() 方法判断文件是否存在。 示例: 假设要在 C 盘上创建一个 test.txt 文件程序启动时会检测该文件是否存在如果不存在则创建如果存在则删除它再创建。实现代码如下public class Test03 {public static void main(String[] args) throws IOException {File f new File(C:\\test.txt); // 创建指向文件的File对象if (f.exists()) // 判断文件是否存在{f.delete(); // 存在则先删除}f.createNewFile(); // 再创建}}运行程序之后可以发现在 C 盘中已经创建好了 test.txt 文件。但是如果在不同的操作系统中路径的分隔符是不一样的例如* Windows 中使用反斜杠\表示目录的分隔符。* Linux 中使用正斜杠/表示目录的分隔符。程序的运行结果和前面程序一样但是此时的程序可以在任意的操作系统中使用。代码修改如下public static void main(String[] args) throws IOException {String path C: File.separator test.txt; // 拼凑出可以适应操作系统的路径File f new File(path);if (f.exists()) // 判断文件是否存在{f.delete(); // 存在则先删除}f.createNewFile(); // 再创建} 注意在操作文件时一定要使用 File.separator 表示分隔符。在程序的开发中往往会使用 Windows 开发环境因为在 Windows 操作系统中支持的开发工具较多使用方便而在程序发布时往往是直接在 Linux 或其它操作系统上部署所以这时如果不使用 File.separator则程序运行就有可能存在问题。关于这一点我们在以后的开发中一定要有所警惕。8.4.3 创建和删除目录 File 类除了对文件的创建和删除外还可以创建和删除目录。创建目录需要调用 mkdir() 方法删除目录需要调用 delete() 方法。无论是创建还是删除目录都可以调用 exists() 方法判断目录是否存在。 示例: 编写一个程序判断 C 盘根目录下是否存在 config 目录如果存在则先删除再创建。实现代码如下public class Test04 {public static void main(String[] args) {String path C:/config/; // 指定目录位置File f new File(path); // 创建File对象if (f.exists()) {f.delete();}f.mkdir(); // 创建目录}}8.4.4 遍历目录 通过遍历目录可以在指定的目录中查找文件或者显示所有的文件列表。File 类的 list() 方法提供了遍历目录功能8.4.4.1 String[] list() 该方法表示返回由 File 对象表示目录中所有文件和子目录名称组成的字符串数组如果调用的 File 对象不是目录则返回 null。提示list() 方法返回的数组中仅包含文件名称而不包含路径。但不保证所得数组中的相同字符串将以特定顺序出现特别是不保证它们按字母顺序出现。8.4.4.2 String[] list(FilenameFilter filter) 该方法的作用与 list() 方法相同不同的是返回数组中仅包含符合 filter 过滤器的文件和目录如果 filter 为 null则接受所有名称。 假设要遍历 C 盘根目录下的所有文件和目录并显示文件或目录名称、类型及大小。使用 list() 方法的实现代码如下 public class Test05 {public static void main(String[] args) {File f new File(C:/); // 建立File变量,并设定由f变量变数引用System.out.println(文件名称\t\t文件类型\t\t文件大小);System.out.println();String fileList[] f.list(); // 调用不带参数的list()方法for (int i 0; i fileList.length; i) { // 遍历返回的字符数组System.out.print(fileList[i] \t\t);System.out.print((new File(C:/, fileList[i])).isFile() ? 文件 \t\t : 文件夹 \t\t);System.out.println((new File(C:/, fileList[i])).length() 字节);}}}假设希望只列出目录下的某些文件这就需要调用带过滤器参数的 list() 方法。首先需要创建文件过滤器该过滤器必须实现 java.io.FilenameFilter 接口并在 accept() 方法中指定允许的文件类型。如下所示为允许 SYS、TXT 和 BAK 格式文件的过滤器实现代码 public class ImageFilter implements FilenameFilter {// 实现 FilenameFilter 接口Overridepublic boolean accept(File dir, String name) {// 指定允许的文件类型return name.endsWith(.sys) || name.endsWith(.txt) || name.endsWith(.bak);}}8.5 字节流使用 InputStream 是 Java 所有字节输入流类的父类OutputStream 是 Java 所有字节输出流类的父类. 使用它们的子类输入和输出字节流包括 ByteArrayInputStream 类、ByteArrayOutputStream 类、FileInputStream 类和 FileOutputStream 类。 8.5.1 字节输入流 InputStream 类及其子类的对象表示字节输入流InputStream 类的常用子类如下。* ByteArrayInputStream 类将字节数组转换为字节输入流从中读取字节。* FileInputStream 类从文件中读取数据。* PipedInputStream 类连接到一个 PipedOutputStream管道输出流。* SequenceInputStream 类将多个字节输入流串联成一个字节输入流。* ObjectInputStream 类将对象反序列化。注意在使用 mark() 方法和 reset() 方法之前需要判断该文件系统是否支持这两个方法以避免对程序造成影响。InputStream类常用方法 方法名及返回值类型说明int read()从输入流中读取一个 8 位的字节并把它转换为 0~255 的整数最后返回整数。 如果返回 -1则表示已经到了输入流的末尾。为了提高 I/O 操作的效率建议尽量 使用 read() 方法的另外两种形式int read(byte[] b)从输入流中读取若干字节并把它们保存到参数 b 指定的字节数组中。 该方法返回 读取的字节数。如果返回 -1则表示已经到了输入流的末尾int read(byte[] b, int off, int len)从输入流中读取若干字节并把它们保存到参数 b 指定的字节数组中。其中off 指 定在字节数组中开始保存数据的起始下标len 指定读取的字节数。该方法返回实际 读取的字节数。如果返回 -1则表示已经到了输入流的末尾void close()关闭输入流。在读操作完成后应该关闭输入流系统将会释放与这个输入流相关 的资源。注意InputStream 类本身的 close() 方法不执行任何操作但是它的许多 子类重写了 close() 方法int available()返回可以从输入流中读取的字节数long skip(long n)从输入流中跳过参数 n 指定数目的字节。该方法返回跳过的字节数void mark(int readLimit)在输入流的当前位置开始设置标记参数 readLimit 则指定了最多被设置标记的字 节数boolean markSupported()判断当前输入流是否允许设置标记是则返回 true否则返回 falsevoid reset()将输入流的指针返回到设置标记的起始处 8.5.2 字节输出流 OutputStream 类及其子类的对象表示一个字节输出流。OutputStream 类的常用子类如下。* ByteArrayOutputStream 类向内存缓冲区的字节数组中写数据。* FileOutputStream 类向文件中写数据。* PipedOutputStream 类连接到一个 PipedlntputStream管道输入流。* ObjectOutputStream 类将对象序列化。OutputStream类常用方法 方法名及返回值类型说明void write(int b)向输出流写入一个字节。这里的参数是 int 类型但是它允许使用表达式 而不用强制转换成 byte 类型。为了提高 I/O 操作的效率建议尽量使用 write() 方法的另外两种形式void write(byte[] b)把参数 b 指定的字节数组中的所有字节写到输出流中void write(byte[] b,int off,int len)把参数 b 指定的字节数组中的若干字节写到输出流中。其中off 指定字节 数组中的起始下标len 表示元素个数void close()关闭输出流。写操作完成后应该关闭输出流。系统将会释放与这个输出 流相关的资源。注意OutputStream 类本身的 close() 方法不执行任何操 作但是它的许多子类重写了 close() 方法void flush()为了提高效率在向输出流中写入数据时数据一般会先保存到内存缓冲 区中只有当缓冲区中的数据达到一定程度时缓冲区中的数据才会被写 入输出流中。使用 flush() 方法则可以强制将缓冲区中的数据写入输出流 并清空缓冲区 8.5.3 字节数组输入流 ByteArrayInputStream 类可以从内存的字节数组中读取数据该类有如下两种构造方法重载形式。1. ByteArrayInputStream(byte[] buf)创建一个字节数组输入流字节数组类型的数据源由参数 buf 指定。2. ByteArrayInputStream(byte[] buf,int offse,int length)创建一个字节数组输入流其中参数 buf 指定字节数组类型的数据源offset 指定在数组中开始读取数据的起始下标位置length 指定读取的元素个数。示例: 使用 ByteArrayInputStream 类编写一个案例实现从一个字节数组中读取数据再转换为 int 型进行输出。代码如下public class test08 {public static void main(String[] args) {byte[] b new byte[] { 1, -1, 25, -22, -5, 23 }; // 创建数组ByteArrayInputStream bais new ByteArrayInputStream(b, 0, 6); // 创建字节数组输入流int i bais.read(); // 从输入流中读取下一个字节并转换成int型数据while (i ! -1) { // 如果不返回-1则表示没有到输入流的末尾System.out.println(原值 (byte) i \t\t\t转换为int类型 i);i bais.read(); // 读取下一个}}}8.5.4 字节数组输出流 ByteArrayOutputStream 类可以向内存的字节数组中写入数据该类的构造方法有如下两种重载形式。1. ByteArrayOutputStream()创建一个字节数组输出流输出流缓冲区的初始容量大小为 32 字节。2. ByteArrayOutputStream(int size)创建一个字节数组输出流输出流缓冲区的初始容量大小由参数 size 指定。ByteArrayOutputStream 类中除了有前面介绍的字节输出流中的常用方法以外还有如下两个方法。1. intsize()返回缓冲区中的当前字节数。2. byte[] toByteArray()以字节数组的形式返回输出流中的当前内容。示例: 使用 ByteArrayOutputStream 类编写一个案例实现将字节数组中的数据输出代码如下所示。public class Test09 {public static void main(String[] args) {ByteArrayOutputStream baos new ByteArrayOutputStream();byte[] b new byte[] { 1, -1, 25, -22, -5, 23 }; // 创建数组baos.write(b, 0, 6); // 将字节数组b中的前4个字节元素写到输出流中System.out.println(数组中一共包含 baos.size() 字节); // 输出缓冲区中的字节数byte[] newByteArray baos.toByteArray(); // 将输出流中的当前内容转换成字节数组System.out.println(Arrays.toString(newByteArray)); // 输出数组中的内容}}8.5.5 文件输入流 FileInputStream 是 Java 流中比较常用的一种它表示从文件系统的某个文件中获取输入字节。通过使用 FileInputStream 可以访问文件中的一个字节、一批字节或整个文件。在创建 FileInputStream 类的对象时如果找不到指定的文件将拋出 FileNotFoundException 异常该异常必须捕获或声明拋出。FileInputStream 常用的构造方法主要有如下两种重载形式:1. FileInputStream(File file)通过打开一个到实际文件的连接来创建一个 FileInputStream该文件通过文件系统中的 File 对象 file 指定。2. FileInputStream(String name)通过打开一个到实际文件的链接来创建一个 FileInputStream该文件通过文件系统中的路径名 name 指定。示例:FileInputStream() 两个构造方法的使用:try {// 以File对象作为参数创建FileInputStream对象FileInputStream fis1 new FileInputStream(new File(F:/mxl.txt));// 以字符串值作为参数创建FilelnputStream对象FileInputStream fis2 new FileInputStream(F:/mxl.txt);} catch(FileNotFoundException e) {System.out.println(指定的文件找不到!);}示例2:使用 FileInputStream 类读取并输出该文件的内容 注意FileInputStream 类重写了父类 InputStream 中的 read() 方法、skip() 方法、available() 方法和 close() 方法不支持 mark() 方法和 reset() 方法。public class Test10 {public static void main(String[] args) {File f new File(D:/myJava/HelloJava.java);FileInputStream fis null;try {// 因为File没有读写的能力,所以需要有个InputStreamfis new FileInputStream(f);// 定义一个字节数组byte[] bytes new byte[1024];int n 0; // 得到实际读取到的字节数System.out.println(D:\\myJava\\HelloJava.java文件内容如下);// 循环读取while ((n fis.read(bytes)) ! -1) {String s new String(bytes, 0, n); // 将数组中从下标0到n的内容给sSystem.out.println(s);}} catch (Exception e) {e.printStackTrace();} finally {try {fis.close();} catch (IOException e) {e.printStackTrace();}}}}8.5.6 文件输出流 FileOutputStream 类继承自 OutputStream 类重写和实现了父类中的所有方法。FileOutputStream 类的对象表示一个文件字节输出流可以向流中写入一个字节或一批字节。在创建 FileOutputStream 类的对象时如果指定的文件不存在则创建一个新文件如果文件已存在则清除原文件的内容重新写入。FileOutputStream 类的构造方法主要有如下 4 种重载形式。1. FileOutputStream(File file)创建一个文件输出流参数 file 指定目标文件。2. FileOutputStream(File file,boolean append)创建一个文件输出流参数 file 指定目标文件append 指定是否将数据添加到目标文件的内容末尾如果为 true则在末尾添加如果为 false则覆盖原有内容其默认值为 false。3. FileOutputStream(String name)创建一个文件输出流参数 name 指定目标文件的文件路径信息。4. FileOutputStream(String name,boolean append)创建一个文件输出流参数 name 和 append 的含义同上。对文件输出流有如下四点说明1. 在 FileOutputStream 类的构造方法中指定目标文件时目标文件可以不存在。2. 目标文件的名称可以是任意的例如 D:\\abc、D:\\abc.de 和 D:\\abc.de.fg 等都可以可以使用记事本等工具打开并浏览这些文件中的内容。3. 目标文件所在目录必须存在否则会拋出 java.io.FileNotFoundException 异常。4. 目标文件的名称不能是已存在的目录。例如 D 盘下已存在 Java 文件夹那么就不能使用 Java 作为文件名即不能使用 D:\\Java否则抛出 java.io.FileNotFoundException 异常。示例:public class Test11 {public static void main(String[] args) {FileInputStream fis null; // 声明FileInputStream对象fisFileOutputStream fos null; // 声明FileOutputStream对象fostry {File srcFile new File(D:/myJava/HelloJava.java);fis new FileInputStream(srcFile); // 实例化FileInputStream对象File targetFile new File(D:/myJava/HelloJava.txt); // 创建目标文件对象该文件不存在fos new FileOutputStream(targetFile); // 实例化FileOutputStream对象byte[] bytes new byte[1024]; // 每次读取1024字节int i fis.read(bytes);while (i ! -1) {fos.write(bytes, 0, i); // 向D:\HelloJava.txt文件中写入内容i fis.read(bytes);}System.out.println(写入结束);} catch (Exception e) {e.printStackTrace();} finally {try {fis.close(); // 关闭FileInputStream对象fos.close(); // 关闭FileOutputStream对象} catch (IOException e) {e.printStackTrace();}}}}8.6 字符流使用(16位 unicode) Java 中字节流的功能十分强大几乎可以直接或间接地处理任何类型的输入/输出操作但利用它却不能直接操作 16 位的 Unicode 字符。这就要用到字符流。8.6.1 字符输入流 Reader 类是所有字符流输入类的父类该类定义了许多方法这些方法对所有子类都是有效的。Reader 类的常用子类如下。1. CharArrayReader 类将字符数组转换为字符输入流从中读取字符。2. StringReader 类将字符串转换为字符输入流从中读取字符。3. BufferedReader 类为其他字符输入流提供读缓冲区。4. PipedReader 类连接到一个 PipedWriter。5. InputStreamReader 类将字节输入流转换为字符输入流可以指定字符编码。 与 InputStream 类相同在 Reader 类中也包含 close()、mark()、skip() 和 reset() 等方法这些方法可以参考 InputStream 类的方法。Reader类中read()方法 方法名及返回值类型说明int read()从输入流中读取一个字符并把它转换为 0~65535 的整数。如果返回 -1 则表示 已经到了输入流的末尾。为了提高 I/O 操作的效率建议尽量使用下面两种 read() 方法int read(char[] cbuf)从输入流中读取若干个字符并把它们保存到参数 cbuf 指定的字符数组中。 该方 法返回读取的字符数如果返回 -1则表示已经到了输入流的末尾int read(char[] cbuf,int off,int len)从输入流中读取若干个字符并把它们保存到参数 cbuf 指定的字符数组中。其中 off 指定在字符数组中开始保存数据的起始下标len 指定读取的字符数。该方法返 回实际读取的字符数如果返回 -1则表示已经到了输入流的末尾 8.6.2 字符输出流 与 Reader 类相反Writer 类是所有字符输出流的父类该类中有许多方法这些方法对继承该类的所有子类都是有效的。Writer 类的常用子类如下。1. CharArrayWriter 类向内存缓冲区的字符数组写数据。2. StringWriter 类向内存缓冲区的字符串StringBuffer写数据。3. BufferedWriter 类为其他字符输出流提供写缓冲区。4. PipedWriter 类连接到一个 PipedReader。5. OutputStreamReader 类将字节输出流转换为字符输出流可以指定字符编码。注意Writer 类所有的方法在出错的情况下都会引发 IOException 异常。关闭一个流后再对其进行任何操作都会产生错误。字符输出流 方法名及返回值类型说明void write(int c)向输出流中写入一个字符void write(char[] cbuf)把参数 cbuf 指定的字符数组中的所有字符写到输出流中void write(char[] cbuf,int off,int len)把参数 cbuf 指定的字符数组中的若干字符写到输出流中。其中off 指定 字符数组中的起始下标len 表示元素个数void write(String str)向输出流中写入一个字符串void write(String str, int off,int len)向输出流中写入一个字符串中的部分字符。其中off 指定字符串中的起 始偏移量len 表示字符个数append(char c)将参数 c 指定的字符添加到输出流中append(charSequence esq)将参数 esq 指定的字符序列添加到输出流中append(charSequence esq,int start,int end)将参数 esq 指定的字符序列的子序列添加到输出流中。其中start 指定 子序列的第一个字符的索引end 指定子序列中最后一个字符后面的字符 的索引也就是说子序列的内容包含 start 索引处的字符但不包括 end 索引处的字符 8.6.3 字符文件输入流 为了读取方便Java 提供了用来读取字符文件的便捷类——FileReader。该类的构造方法有如下两种重载形式。1. FileReader(File file)在给定要读取数据的文件的情况下创建一个新的 FileReader 对象。其中file 表示要从中读取数据的文件。2. FileReader(String fileName)在给定从中读取数据的文件名的情况下创建一个新 FileReader 对象。其中fileName 表示要从中读取数据的文件的名称表示的是一个文件的完整路径。在用该类的构造方法创建 FileReader 读取对象时默认的字符编码及字节缓冲区大小都是由系统设定的。要自己指定这些值可以在 FilelnputStream 上构造一个 InputStreamReader。示例: 要将 D:\myJava\HelloJava.java 文件中的内容读取并输出到控制台使用 FileReader 类的实现代码如下 先创建了 FileReader 字符输入流对象 fr该对象指向 D:\myJava\HelloJava.java 文件然后定义变量 i 来接收调用 read() 方法的返回值即读取的字符。在 while 循环中每次读取一个字符赋给整型变量 i直到读取到文件末尾时退出循环当输入流读取到文件末尾时会返回值 -1public class Test12 {public static void main(String[] args) {FileReader fr null;try {fr new FileReader(D:/myJava/HelloJava.java); // 创建FileReader对象int i 0;System.out.println(D:\\myJava\\HelloJava.java文件内容如下);while ((i fr.read()) ! -1) { // 循环读取System.out.print((char) i); // 将读取的内容强制转换为char类型}} catch (Exception e) {System.out.print(e);} finally {try {fr.close(); // 关闭对象} catch (IOException e) {e.printStackTrace();}}}}8.6.4 字符文件输出流 Java 提供了写入字符文件的便捷类——FileWriter该类的构造方法有如下 4 种重载形式。1. FileWriter(File file)在指定 File 对象的情况下构造一个 FileWriter 对象。其中file 表示要写入数据的 File 对象。2. FileWriter(File file,boolean append)在指定 File 对象的情况下构造一个 FileWriter 对象如果 append 的值为 true则将字节写入文件末尾而不是写入文件开始处。3. FileWriter(String fileName)在指定文件名的情况下构造一个 FileWriter 对象。其中fileName 表示要写入字符的文件名表示的是完整路径。4. FileWriter(String fileName,boolean append)在指定文件名以及要写入文件的位置的情况下构造 FileWriter 对象。其中append 是一个 boolean 值如果为 true则将数据写入文件末尾而不是文件开始处。FileWriter 类的创建不依赖于文件存在与否如果关联文件不存在则会自动生成一个新的文件。在创建文件之前FileWriter 将在创建对象时打开它作为输出。如果试图打开一个只读文件将引发一个 IOException 异常。示例: 首先创建了一个指向 D:\myJava\book.txt 文件的字符文件输出流对象 fw然后使用 for 循环录入 4 个字符串并调用 write() 方法将字符串写入到指定的文件中。最后在 finally 语句中关闭字符文件输出流。 将用户输入的 4 个字符串保存到 D:\myJava\book.txt 文件中。在这里使用 FileWriter 类中的 write() 方法循环向指定文件中写入数据实现代码如下 public class Test13 {public static void main(String[] args) {Scanner input new Scanner(System.in);FileWriter fw null;try {fw new FileWriter(D:\\myJava\\book.txt); // 创建FileWriter对象for (int i 0; i 4; i) {System.out.println(请输入第 (i 1) 个字符串);String name input.next(); // 读取输入的名称fw.write(name \r\n); // 循环写入文件}System.out.println(录入完成);} catch (Exception e) {System.out.println(e.getMessage());} finally {try {fw.close(); // 关闭对象} catch (IOException e) {e.printStackTrace();}}}}8.6.5 字符缓冲区输入流 BufferedReader 类主要用于辅助其他字符输入流它带有缓冲区可以先将一批数据读到内存缓冲区。接下来的读操作就可以直接从缓冲区中获取数据而不需要每次都从数据源读取数据并进行字符编码转换这样就可以提高数据的读取效率。BufferedReader 类的构造方法有如下两种重载形式。1. BufferedReader(Reader in)创建一个 BufferedReader 来修饰参数 in 指定的字符输入流。2. BufferedReader(Reader in,int size)创建一个 BufferedReader 来修饰参数 in 指定的字符输入流参数 size 则用于指定缓冲区的大小单位为字符。示例: 首先分别创建了名称为 fr 的 FileReader 对象和名称为 br 的 BufferedReader 对象然后调用 BufferedReader 对象的 readLine() 方法逐行读取文件中的内容。如果读取的文件内容为 Null即表明已经读取到文件尾部此时退出循环不再进行读取操作。最后将字符文件输入流和带缓冲的字符输入流关闭。 使用 BufferedReader 类中的 readLine() 方法逐行读取 D:\myJava\Book.txt 文件中的内容并将读取的内容在控制台中打印输出代码如下public class Test13 {public static void main(String[] args) {FileReader fr null;BufferedReader br null;try {fr new FileReader(D:\\myJava\\book.txt); // 创建 FileReader 对象br new BufferedReader(fr); // 创建 BufferedReader 对象System.out.println(D:\\myJava\\book.txt 文件中的内容如下);String strLine ;while ((strLine br.readLine()) ! null) { // 循环读取每行数据System.out.println(strLine);}} catch (FileNotFoundException e1) {e1.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {try {fr.close(); // 关闭 FileReader 对象br.close();} catch (IOException e) {e.printStackTrace();}}}}8.6.6 字符缓冲区输出流 BufferedWriter 类主要用于辅助其他字符输出流它同样带有缓冲区可以先将一批数据写入缓冲区当缓冲区满了以后再将缓冲区的数据一次性写到字符输出流其目的是为了提高数据的写效率。BufferedWriter 类的构造方法有如下两种重载形式。1. BufferedWriter(Writer out)创建一个 BufferedWriter 来修饰参数 out 指定的字符输出流。2. BufferedWriter(Writer out,int size)创建一个 BufferedWriter 来修饰参数 out 指定的字符输出流参数 size 则用于指定缓冲区的大小单位为字符。该类除了可以给字符输出流提供缓冲区之外还提供了一个新的方法 newLine()该方法用于写入一个行分隔符。行分隔符字符串由系统属性 line.separator 定义并且不一定是单个新行\n符。提示BufferedWriter 类的使用与 FileWriter 类相同这里不再重述。8.7 转换流 InputStreamReader 用于将字节输入流转换为字符输入流其中 OutputStreamWriter 用于将字节输出流转换为字符输出流。示例1: 在 java.txt 中输出“C语言中文网”这 6 个字将 java.txt 保存为“UTF-8”的格式然后通过字节流的方式读取代码如下public static void main(String[] args) {try {FileInputStream fis new FileInputStream(D://java.txt);int b 0;while ((b fis.read()) ! -1) {System.out.print((char) b);}} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}输出结果为 C??????????我们发现中文都是乱码。下面用字节数组并通过字符串设定编码格式来显式内容代码如下public static void main(String[] args) {try {FileInputStream fis new FileInputStream(D://java.txt);byte b[] new byte[1024];int len 0;while ((len fis.read(b)) ! -1) {System.out.print(new String(b, 0, len, UTF-8));}} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}这时输出结果为 C语言中文网但是当存储的文字较多时会出现解码不正确的问题且字节长度无法根据解码内容自动设定此时就需要转换流来完成。代码如下public static void main(String[] args) {try {FileInputStream fis new FileInputStream(D://java.txt);InputStreamReader isr new InputStreamReader(fis, UTF-8);int b 0;while ((b isr.read()) ! -1) {System.out.print((char) b); // 输出结果为“C语言中文网”}} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}示例2: 下面以获取键盘输入为例来介绍转换流的用法。Java 使用 System.in 代表标准输出即键盘输入但这个标准输入流是 InputStream 类的实例使用不太方便而且键盘输入内容都是文本内容所以可以使用 InputStreamReader 将其转换成字符输入流普通的 Reader 读取输入内容时依然不太方便可以将普通的 Reader 再次包装成 BufferedReader利用 BufferedReader 的 readLine() 方法可以一次读取一行内容。程序如下所示:public static void main(String[] args) {try {// 将 System.in 对象转换成 Reader 对象InputStreamReader reader new InputStreamReader(System.in);// 将普通的Reader 包装成 BufferedReaderBufferedReader br new BufferedReader(reader);String line null;// 利用循环方式来逐行的读取while ((line br.readLine()) ! null) {// 如果读取的字符串为“exit”则程序退出if (line.equals(exit)) {System.exit(1);}// 打印读取的内容System.out.println(输入内容为 line);}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}8.8 JAVA示例-保存图书信息 1创建 Book 类在该类中包含 no、name 和 price 3 个属性分别表示图书编号、图书名称和图书单价。同时还包含两个方法 write() 和 read()分别用于将图书信息写入到磁盘文件中和从磁盘文件中读取图书信息并打印到控制台。此外在 Product 类中包含有该类的 toString() 方法和带有 3 个参数的构造方法具体的内容如下public class Book {private int no; // 编号private String name; // 名称private double price; // 单价public Book(int no, String name, double price) {this.no no;this.name name;this.price price;}public String toString() {return 图书编号 this.no 图书名称 this.name 图书单价 this.price \n;}public static void write(List books) {FileWriter fw null;try {fw new FileWriter(E:\\myJava\\books.txt); // 创建FileWriter对象for (int i 0; i books.size(); i) {fw.write(books.get(i).toString()); // 循环写入}} catch (Exception e) {System.out.println(e.getMessage());} finally {try {fw.close();} catch (IOException e) {e.printStackTrace();}}}public static void read() {FileReader fr null;BufferedReader br null;try {fr new FileReader(E:\\myJava\\books.txt);br new BufferedReader(fr); // 创建BufferedReader对象String str ;while ((str br.readLine()) ! null) { // 循环读取每行数据System.out.println(str); // 输出读取的内容}} catch (Exception e) {System.out.println(e.getMessage());} finally {try {br.close();fr.close();} catch (IOException e) {e.printStackTrace();}}}}如上述代码分别使用字符文件输出流 FileWriter 和字符缓冲区输入流 BufferedReader 完成对图书信息的存储和读取功能。2编写测试类 Test14创建两个 Book 对象并将这两个对象保存到 List 集合中再将 List 集合对象传递给 Book 类中的 write() 方法向 F:\product.txt 文件中写入图书信息。最后调用 Product 类中的 read() 方法读取该文件内容代码如下public class Test14 {public static void main(String[] args) {Book book1 new Book(1001, C语言中文网Java教程, 159);Book book2 new Book(1002, C语言中文网C教程, 259);List books new ArrayList();books.add(book1);books.add(book2);Book.write(books);System.out.println(********************图书信息******************);Book.read();}}
http://www.zqtcl.cn/news/314360/

相关文章:

  • 知名网站制作公南充建设机械网站
  • 网站建设实践鉴定微商小程序制作
  • 盗用别人网站图做网站快速排名优化推广手机
  • 安徽网站建设服务平台wordpress自定义统计
  • 微网站开发方案模板建站宝盒哪个牌子好
  • 低价做网站网站制作天津
  • 成都网站推广外包门户网站素材
  • wordpress 拿站网站制作北京海淀
  • 惠州网站建设行业wordpress文章阅读权限
  • 做地方网站需要什么部门批准网页版微信二维码
  • o2o网站运维建设方案宿州市网站建设
  • 上海网站排名优化公司马蜂窝是什么做的网站
  • 邢台网站建设网站淘宝小程序入口
  • 公司起名自动生成器网站优化方法
  • 太原网站开发团队wordpress 链接 插件
  • 阿克顿巴网站建设的目的智通人才招聘网
  • 网站快速优化成营销型网站制作
  • 网站建设mus18php 网站缓存文件
  • 建设企业网站企业网上银行助手下载泰安网签数据
  • 网站开发产权保护如何去掉2345网址导航
  • 网站数据库怎么备份自建网站步骤
  • 多语言版本的网站做网站西安
  • 蚌埠市住房建设部网站在线免费做网站
  • 天津网站建设网络企业网址模板
  • 如何做京东购物网站旅游前 做攻略有什么网站好用
  • 嘉兴网站推广优化公司wordpress显示系统
  • 网站建站定做空间里怎么放多个网站
  • 清远市企业网站seo联系方式深圳人才市场现场招聘信息
  • 网站制作在哪能看网站开发前台开发
  • 一般网站用什么数据库游戏源码搭建