一、泛型的概念及作用
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 一个简单的泛型类
泛型在集合中使用的非常广泛,泛型其实就是把类型参数化
public class Gen<E> {
private E atr;
public Gen() {
}
public E getAtr() {
return atr;
}
public void setAtr(E atr) {
this.atr = atr;
}
}
2.3 泛型方法
- 泛型方法使得该方法能够独立于类产生而变化
- 定义泛型方法,只需要将泛型参数列表置于返回值之前
// 这个方法的作用就是你传入什么参数,就返回什么数据
public <E> E getX(E x) {
return x;
}
注意: 泛型方法和泛型类没有直接的关系。要定义泛型方法,只需将泛型参数列表置于返回值前。
2.4 泛型接口
- 泛型接口与泛型类完全相同
public interface Test<T> {
public T getT(T t);
public String assume(T t);
}
三、集合框架
以下的内容都是基于 java.jutil 包下的内容进行讲解
Java 集合框架支持以下两种类型的容器
- 一种是为了存储一个元素的集合,称为集合(collection)
- 另一种是为了存储键/值对,成为映射 (map)
3.1 传统数组的特点
传统的数组具有如下特点:
- 类型单一
- 长度固定
- 通过下标存 和 取 遍历
3.2 集合的特点
- 集合是 Java 语言中非常重要的 API
- 用来存储多个数据
- 实现了不同的数据结构
- 在存储时不用担心下标
3.3 Java 集合框架的三大接口
- Collection:所有集合类的根接口
- Map:映射接口,存放键值对
- Iterator:遍历集合的迭代接口
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 方法的介绍
常用方法:
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 是不同步的(但是效率高,非线程安全的)
- 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+" "));
}
}
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+" "));
}
}
3.6.4 集合的遍历的三种方式
- 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");
}
}
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 类的常用方法:
- load() ; 加载文件;
- getProperty (key) ; 通过key值获得对应的 value 值
- setProperty (String key, String value ) 给 properties 文件中写值。
4.3 示例
首先创建一个 test.properties 文件,并加入如下内容 ( 以键值对的形式)
name = “COCO” age = 18 score = 98
创建测试类 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 操作在下一部分内容展示
这里有个配置文件的路径问题:我试过相对路径,不能实现,所以我就是用了绝对路径,如下是我的项目目录结构
4.4 运行结果
这里使用中文是会显示乱码的,所以如果要显示中文,需要在 io 中设置编码格式为 (utf-8 即可)