一、泛型的概念及作用

1.1 Java 中的泛型

  • 泛型是JavaSE 1.5的新特性,泛型的本质参数化类型,也就是说所操作的数据类型被指定为一个参数。
  • 这种参数类型可以用在类、接口和方法的创建中,分别称为
    • 泛型类
    • 泛型接口
    • 泛型方法。
  • Java语言引入泛型的好处是安全简单。

1.2 泛型示例

  • Class ArrayList: 声明一个泛型类ArrayL ist, 其中E可以使用任意一-个具体类型替代,泛型类型往往使用一个大写字母表示。
  • public boolean add(E o): E是泛型,也就是说,使用add方法时,可以为其传递任意一种类型的参数。其中此处的E是在类中定义的

1.3 泛型的作用

Java 中泛型的作用 (简单安全)

  • 编译时检查类型安全,并且所有的强制转换都是自动 和 隐式的,提高代码的重用率
  • 消除代码中的强制类型转换,同时获得一个附加的类型检查层,该检查层可以防止有人将错误类型的值保存在集合中

二、泛型类,泛型方法、泛型接口

2.1 定义简单的泛型类

  • 声明类后使用 (E可以是任何其他字母),即可以指定该类是一个泛型类
  • 类型参数可以在该类中需要数据类型的地方使用,如属性声明,方法声明

2.2 一个简单的泛型类

泛型在集合中使用的非常广泛,泛型其实就是把类型参数化

  1. public class Gen<E> {
  2. private E atr;
  3. public Gen() {
  4. }
  5. public E getAtr() {
  6. return atr;
  7. }
  8. public void setAtr(E atr) {
  9. this.atr = atr;
  10. }
  11. }

image.png

2.3 泛型方法

  • 泛型方法使得该方法能够独立于类产生而变化
  • 定义泛型方法,只需要将泛型参数列表置于返回值之前
  1. // 这个方法的作用就是你传入什么参数,就返回什么数据
  2. public <E> E getX(E x) {
  3. return x;
  4. }

注意: 泛型方法和泛型类没有直接的关系。要定义泛型方法,只需将泛型参数列表置于返回值前。

2.4 泛型接口

  • 泛型接口与泛型类完全相同
public interface Test<T> {
     public T getT(T t);
    public String assume(T t);
}

三、集合框架

以下的内容都是基于 java.jutil 包下的内容进行讲解

Java 集合的补充

Java 集合框架支持以下两种类型的容器

  • 一种是为了存储一个元素的集合,称为集合(collection)
  • 另一种是为了存储键/值对,成为映射 (map)

映射是一种使用键 (key) 快速搜索元素的高效数据结构。

3.1 传统数组的特点

传统的数组具有如下特点:

  1. 类型单一
  2. 长度固定
  3. 通过下标存 和 取 遍历

3.2 集合的特点

  • 集合是 Java 语言中非常重要的 API
  • 用来存储多个数据
  • 实现了不同的数据结构
  • 在存储时不用担心下标

3.3 Java 集合框架的三大接口

  • Collection:所有集合类的根接口
  • Map:映射接口,存放键值对
  • Iterator:遍历集合的迭代接口

image.png

Java 的集合框架是由很多接口、抽象类、具体类组成的,都位于 java.util 包中

3.4 Collection 接口

  • Collection 接口

Collection 意即集合,是所有集合类的根接口,同时 Collection 接口是一个泛型接口

3.4.1 Collection 接口方法

  • add(E): 添加对象;
  • Iterator iterator(): 生成迭代器对象,进而可以迭代集合中的元素
  • int size(); 获取集合中元素数量

3.4.2 Collection 的子接口

  • List:一种用于存储有序元素的集合
  • Set:一个用于存储一组不重复元素的集合
  • Stack: 用于存储采用后进先出处理对象的集合
  • Queue:用于存储采用先进先出方式处理的对象的集合
  • PorityQueue 用于存储按照优先级顺序处理的对象

    队列,jdk1. 5版本新增接口,Queue实现 了“ 先进先出(FIF0)的存储结构。Queue 是CoIlection的子接口,具有所有集合基本操作,除此之外,Queue还提供了一些新的插入、提取、查询等方法。

3.4.3 Collection 类

  • 是集合类的工具类,与数组的工具类 Arrays 类似
  • 定义了大量静态方法

    • 同步集合对象的方法
    • 对List排序的方法
  • Collection 方法的介绍

    • 同步集合的方法
    • Set synchronizedSet (Set s)
      • 返回由指定的 set 支持的同步(线程安全)的 set
    • Map synchronizedMap(Map m) 返回由指定 map支持的同步(线程安全)的 map
    • 对 List 排序的方法
      • sort list> 根据元素的自然顺序,对指定列表按升序进行排序

        3.5 Map 接口

        Map 接口中保存的是键值对 Map, key 值不允许重复,如果充足则覆盖

常用方法:

Map m =new HashMap();
m.put(key,value);   // 存入一个键值对
m.get(Object key);  // 根据键取值,如果没有则返回 null
m.size(); // 返回 Map 对象中键值对的数量
...

3.5.1 Map 接口的实现类

  • HashMap
    • 常用的 Map 集合类
    • key 值的 hashCode 和 equals 保证元素唯一性
  • TreeMap
    • 不仅可以保证 key 不重复
    • 还可以对 value 数据进行排序
  • HashTable
    • 和 HashMap 基本差不多
    • 它的键 和 值不可以为空

3.5.2 HashMap 的简单使用

TreeMap 和 HashMap 几乎一致,所以就不演示了

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

public class MapTest {
    public static void main(String[] args) {
        /**
         * 存储无序
         *
         * */
        Map m = new HashMap();
        Map m1 = new Hashtable();
        // 两者区别
        // 以键值对存储数据
        // 存
        m.put("a",1);
        m.put(22,"sass");
        m.put("b",3);
        m.put("b",4); // key 重复的会覆盖原有的 values, 值可以相同
        m.put("c",4);
        m.put(null,"a"); // 键可以出现一个空
        m.put(null,null);
        m.put("d",null); // 值可以为多个空

        Set s = m.keySet(); // 所有的键可以使用 set 来存,就可以直接遍历 set 了
        // HashMap
        //m1.put(null,"b"); // Hashtable 键不能为空, 值都不能为空 (一个简单那区别
        // 取
        System.out.println(m); // 打印键值对
        System.out.println(m.get("a")); // 根据键打印值
        System.out.println(m.get("f")); // 打印不存在的键会返回 null
        System.out.println(m.keySet()); // 打印所有键
        System.out.println(m.values()); // 打印所有的值

        // 移除
//        m.remove("a");  // 删除指定内容
//        System.out.println(m);
        // 遍历 所有的键
        System.out.println("===== 遍历键 =====");
        for(Object o:m.keySet()) {
            System.out.println(o);
        }
        System.out.println("===== 遍历值 =====");
        // 遍历所有的值
        for (Object o:m.values()) {
            System.out.println(o);
        }

        //foreach遍历 jdk1.8新特性
        m.foreach((k,v)->{
            //键 值一起遍历
            System.out.println(k+y);
        });
    }
}

3.6 List 接口

  • 继承了 Collection 接口,并且扩展出属于自己的方法,List 集合中的元素都是与索引有关系的,因此 List 集合扩展的方法都是与索引有关系的。
  • List 还有个自动装箱的特点 (当我们没有使用泛型指定集合中的数据类型时,它会自动将我们传入的任何数据转换为 Object,然后取出时还是 Object,如果我们还需要继续使用该数据类型的方法,就需要对取出的数据类型进行强制类型转换)

例如: add(int index, E) 添加元素在对应的索引位置上

3.6.1 List 的实现类

  • List 是个接口有三个实现的类
    • ArrayList:
      • 数组列表,数据采用数组的方式进行存储,使用连续内存的存储,ArrayList 是 java 语言中可变长度数组的实现
    • LinkedList:
      • 称为链表,该集合类型实现了 “链表”的数据结构。值得一提的是, LinkedList 不仅实现了 List 接口,还是先了 Queue 接口,可以说链表同时也是作为一个队列对象的使用。使用方式和 ArrayList 相似
    • Vector:
      • jdk 1.0 中的集合,后来修改为实现 List 接口。Vector 的功能几乎都可以被 ArrayList 替代,主要区别是 Vector 是同步的(线程中的概念),而ArrayList 是不同步的(但是效率高,非线程安全的)

3.6.2 ArrayList 讲解

常用方法

方法 功能
add(Object o); 往集合中添加数据
remove(Object o); 删除集合中的数据
add(int index, Object element); 给集合中的某一个索引位置添加元素
get(int index); 获取集合中某个位置的信息
size(); 获取list 的长度

示例

import java.util.ArrayList;
import java.util.List;

public class TestArrayList {
    public static void main(String[] args) {
        /**
         * 不使用泛型的话,就可以传入任何数据,这里就不掩饰了
         * 我这里使用泛型处理
         * */
        List<Integer> list = new ArrayList<Integer>();
//        list.add("a"); // 如果不传入整数,就会报错

          // 增加数据
            list.add(1);
            list.add(2);
            list.add(3);
            System.out.println(list);
            list.add(1,-1); // 数据插到下标为1的元素的后面
            System.out.println(list);

            // 取数据
            int index = list.get(0); // 取出下标为1的元素
            System.out.println(index);

            // 移除元素, 在 int 中,默认移除的都是下标元素
            // remove 可以删除指定元素,在这里体现的不是很明显
            System.out.println("========== 删除前 ========\n"+list);
            list.remove(0);
            System.out.println("========== 删除后 ========\n"+list);

            System.out.println("====  遍历一  ====");
            // 遍历数组
            for (int i=0;i<list.size();i++) {
                System.out.print(list.get(i)+" ");
            }
            System.out.println("");
            System.out.println("====  遍历二 ====");
            for (Integer li: list) {
                System.out.print(li+" ");
            }
                System.out.println("");
            System.out.println("==== Lambda表达式遍历集合====");
            list.forEach(s -> System.out.print(s+" "));
    }
}

image.png

3.6.3 LinkedList 讲解

  • LinkedList是以链表的方式存放的, 每个节点上存放的都是数据信息
  • LinkedList 是链表的同时,也是一个队列
import java.util.LinkedList;

public class TestLinkedList {
    public static void main(String[] args) {
        /**
         * 链表
         * 手写链表 (单向链表、双向链表)
         * */
        LinkedList list = new LinkedList();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        // 这里省略若干,大部分的方法和 ArrayList 是一致的

        System.out.println(list);
        list.addFirst("e"); // 插队
        System.out.println(list);
        list.addLast("s"); // 插入尾部
        System.out.println(list);
        Object o =list.peek();
        System.out.println("获取头部元素:"+o); // 获取头部元素

        System.out.println("========= 元素遍历 ==========");
        // 遍历元素
        for (Object li:list) {
            System.out.print(li+" );
        }
        System.out.print("");
         System.out.println("==== Lambda表达式遍历====");
        list.forEach(s -> System.out.print(s+" "));

    }
}

image.png

3.6.4 集合的遍历的三种方式

  1. 普通的 for 循环
  2. foreach (增加的for 循环)
  3. 迭代器遍历(Iterator)【在 Set 中会使用】 :通过集合返回迭代器

    3.7 Set 接口

  • Set 接口继承了 Collection 接口
    • Set 中所存储的元素不能重复,并且是无序的
    • Set 中是没有索引的
  • 如果需要遍历可以可以使用 Iterator 或者 增强的 for 循环

3.7.1 Set 集合接口的实现类

  • Set接口有两个实现类
    • HashSet:底层是哈希码值,基于 HashMap 实现的。如果彼此调用 equals 方法,返回的都是 false
    • TreeSet:元素不重复,并且元素实现了排序(通过实现 Set另外的子接口 SortedSet 接口实现排序)。存储对象必须实现 Comparable 接口 。

3.7.2 HashSet

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class TestHashSet {
    public static void main(String[] args) {

        Set s = new HashSet();

        // 存
        s.add("a");
        s.add("a"); // 只会显示一个 a
        s.add("sss");
        s.add(1);
        s.add(2);
        s.add("你好");
        s.add(true);
        System.out.println(s);
        /**
         * set 没有所以你的概念,因此只能使用迭代器 或者 增加的 for 循环来遍历
         * */
        // 取
        System.out.println("=============== iterator 迭代 ===========");
        Iterator it = s.iterator();
        // 迭代过程不可以移除数据
        while (it.hasNext()) { // 查看是否有下一个
            System.out.println(it.next());
        }

        System.out.println("=========== 增强的 for 循环 ==============");
        // foreach (迭代的过程不能移除元素)
        for (Object o: s) {
            System.out.println(o);
        }
        System.out.println("=========== Lambda表达式遍历===========");
        s.forEach(ss -> System.out.println(ss));
        // 移除
        s.remove("aaa");
    }
}

image.png

3.7.3 TreeSet

import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;


class Student implements Comparable<Student> {
    private String name;
    private int age;
    private double socres;

    public Student(String name, int age, double socres) {
        this.name = name;
        this.age = age;
        this.socres = socres;
    }

    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 double getSocres() {
        return socres;
    }

    public void setSocres(double socres) {
        this.socres = socres;
    }


    @Override
    public int compareTo(Student o) {
        // 根据成绩排序
        if (o.getSocres() == this.socres) {
            return 1;
        }
        return 0;
    }
}

public class TestTreeSet {
    public static void main(String[] args) {
        Student s[] = new Student[3];
        s[0] = new Student("小张",18,95.5);
        s[1] = new Student("小红",19,90);
        s[2] = new Student("小吕",20, 100);
        Set<Student> s1 = new TreeSet<Student>();
        s1.add(s[0]);
        s1.add(s[1]);
        s1.add(s[2]);
        System.out.println("=============== iterator 迭代 ===========");
        Iterator it = s1.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
        System.out.println("=========== 增强的 for 循环 ==============");        
        for (Student stu: s1) {
            System.out.println(stu.getName() + " "+ stu.getAge() + " "+stu.getSocres());
        }
    }
}

四、Properties 类

4.1 properties 类的功能

Properties类 是 Hashtable类 的子类,所以也间接地实现了Map接口。在实际应用中,常使用Properties类对属性文件进行处理。

4.2 properties 类的常用方法:

  1. load() ; 加载文件;
  2. getProperty (key) ; 通过key值获得对应的 value 值
  3. setProperty (String key, String value ) 给 properties 文件中写值。

4.3 示例

  1. 首先创建一个 test.properties 文件,并加入如下内容 ( 以键值对的形式)

    name = “COCO” age = 18 score = 98

  2. 创建测试类 TestProperties

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;

public class TestProperties {
    public static void main(String[] args) {
        Properties prop = new Properties();
        try {
            prop.load(new FileInputStream(new File("src/day8/test.properties")));
        }  catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("name:"+prop.getProperty("name"));
        System.out.println("age"+prop.getProperty("age"));
        System.out.println("score:"+prop.getProperty("score"));
    }
}

这里用到了文件的 io 操作,文件 io 操作在下一部分内容展示
这里有个配置文件的路径问题:我试过相对路径,不能实现,所以我就是用了绝对路径,如下是我的项目目录结构

image.png

4.4 运行结果

这里使用中文是会显示乱码的,所以如果要显示中文,需要在 io 中设置编码格式为 (utf-8 即可)
image.png