集合Container
容器|集合:存储多个数据
数组:存储多个数据
一段连续的内存空间
数组的特点:
1.引用数据类型
2.定长,长度一旦确定无法更改
3.有序,有索引
4.存储的数据类型相同
集合:
存储任意引用数据类型
集合的长度可变,可以根据需求动态的增删数据,长度随之改变
容器中的接口层次结构
Collection : 单个值的集合
Set 无序不可重复
List 有序可重复,有索引
Map : 键值对的集合(KEY-VALUE)
Collection 集合层次结构中的根接口。 集合表示一组对象,称为其元素 。
常用方法
遍历方式
foreach
iterator迭代器
代码:
public static void main(String[] args) {
//1.创建集合对象
Collection collection = new ArrayList();
Collection col = new ArrayList();
//2)常用方法
//boolean add(E e) 确保此集合包含指定的元素(可选操作)。
collection.add(123);
collection.add(false);
collection.add("你好");
collection.add('a');
collection.add(null);
collection.add(1.234);
col.add("aaa");
col.add("bbb");
System.out.println(col.add("ccc"));
System.out.println(collection);
System.out.println(col);
//boolean addAll(Collection<? extends E> c) 将指定集合中的所有元素添加到此集合中(可选操作)。
collection.addAll(col);
System.out.println(collection);
System.out.println(col);
//void clear() 从此集合中删除所有元素(可选操作)。
//col.clear();
//boolean isEmpty() 如果此集合不包含任何元素,则返回 true 。
//System.out.println(col.isEmpty());
//boolean contains(Object o) 如果此collection包含指定的元素,则返回 true 。
System.out.println(collection.contains("你好"));
//boolean containsAll(Collection<?> c) 如果此集合包含指定集合中的所有元素,则返回 true 。
System.out.println(collection.containsAll(col));
//boolean remove(Object o) 从此集合中移除指定元素的单个实例(如果存在)(可选操作)。
System.out.println(collection.remove(123));
System.out.println(collection);
//boolean removeAll(Collection<?> c) 删除此集合的所有元素,这些元素也包含在指定的集合中(可选操作)。
//boolean retainAll(Collection<?> c) 仅保留此集合中包含在指定集合中的元素(可选操作)。
col.add("ddd");
System.out.println(collection);
System.out.println(col);
//System.out.println(collection.retainAll(col));
System.out.println(collection);
//int size() 返回此集合中的元素数。
//Object[] toArray() 返回包含此集合中所有元素的数组。
//System.out.println(Arrays.toString(collection.toArray()));
System.out.println("-----------foreach----------------");
for(Object obj:collection){
System.out.println(obj);
}
System.out.println("------------iterator迭代器-------------");
//1)获取迭代器实例,用来遍历指定的集合
//Iterator<E> iterator() 返回此集合中元素的迭代器。
Iterator it = collection.iterator();
//2)判断是否存在下一个数据
while(it.hasNext()){
//3)获取下一个数据
System.out.println(it.next());
}
}
泛型: jdk1.5
参数化类型 :
数据类型作为参数传递
只能配置引用数据类型
<>定义泛型
泛型的行为发生在编译期间,运行期间发型配置的所有内容无效,泛型擦除
要求先定义泛型,才能使用泛型
泛型类 : 类型的后面定义泛型,在使用类型的时候可以通过泛型传递具体的类型,类中可以进行使用
泛型方法
泛型的优点:
代码简单简洁
增强程序健壮性,避免类型转换异常的出现
增强稳定性与可读性
代码:
public static void main(String[] args) {
Collection<String> col=new ArrayList <>();
col.add("123");
col.add("abc");
col.add("def");
System.out.println(col);
for (String str:col){
System.out.println(str);
}
Iterator it= col.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
}
List
List:有序的,可重复的
Set:无序的,不可重复的
List : 有序集合(也称为序列 )。 该接口的用户可以精确控制列表中每个元素的插入位置。
新增: 一些列根据索引操作的方法
遍历方式 :
for普通for循环
foreach 增强for循环
iterator迭代器
代码:
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
//添加
list.add(3);
list.add(2);
list.add(1);
System.out.println(list);
//void add(int index, E element) 将指定元素插入此列表中的指定位置(可选操作)。 --只能添加不能修改
//boolean add(E e) 将指定的元素追加到此列表的末尾(可选操作)。
list.add(3,6);
list.add(4,2);
System.out.println(list);
list.add(2,100);
System.out.println(list);
//E set(int index, E element) 用指定的元素替换此列表中指定位置的元素(可选操作)。 --只能修改不能添加
System.out.println(list.set(2,10));
System.out.println(list);
//boolean addAll(int index, Collection<? extends E> c) 将指定集合中的所有元素插入到指定位置的此列表中(可选操作)。
//E get(int index) 返回此列表中指定位置的元素。
System.out.println(list.get(2));
//int indexOf(Object o) 返回此列表中第一次出现的指定元素的索引,如果此列表不包含该元素,则返回-1。
System.out.println(list.indexOf(2));
//int lastIndexOf(Object o) 返回此列表中指定元素最后一次出现的索引,如果此列表不包含该元素,则返回-1。
System.out.println(list.lastIndexOf(2));
//static <E> List<E> of(E... elements) 返回包含任意数量元素的不可修改列表。
List<String> list2 = List.of("aaa","bbb","ccc");
System.out.println(list2);
//list2.set(1,"eee"); //java.lang.UnsupportedOperationException
System.out.println(list2.get(1));
//E remove(int index) 删除此列表中指定位置的元素(可选操作)。 -->优先识别
//boolean remove(Object o) 从该列表中删除指定元素的第一个匹配项(如果存在)(可选操作)。
System.out.println(list.remove(3));
System.out.println(list);
//List<E> subList(int fromIndex, int toIndex) 返回指定的 fromIndex (包含)和 toIndex (不包括)之间的此列表部分的视图。
System.out.println(list.subList(1,3));;
System.out.println("------------------ for普通for循环-----------------");
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
ArrayList与 Vector
ArrayList
有序,可重复的
ArrayList :实现所以可选列表操作,并允许所以元素,包括null。
底层结构:数组 Object[] elementData;
特点:根据索引查询遍历效率较高,增删效率低
应用场景: 适合应用在大量做查询,少量做增删的位置
扩容问题:
初始容量 : 默认初始容量为10 ,在第一次添加add的时候进行构建 private static final int DEFAULT_CAPACITY = 10;
扩容机制 : 每次扩容原容量的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1);
新增功能: void forEach(Consumer<? super E> action)
遍历 :
普通for
增强for
iterator迭代器
listIterator迭代器
Vector
底层结构 : 数组
特点 : 与ArrayList相同
区别:
1) Vector是同步的|线程安全
ArrayList不同步|线程不安全,相对效率较高
2) Vector每次扩容原容量的2倍
ArrayList每次扩容原容量的1.5倍,相对ArrayList更有利于节省内存
总结 : 如果不需要线程安全实现,建议使用ArrayList代替Ve.1
代码:
public static void main(String[] args) {
ArrayList <Double> list=new ArrayList<>();
list.add(1.1);
list.add(2.2);
list.add(3.3);
list.add(null);
list.add(null);
System.out.println(list);
//for each
list.forEach(System.out::println);
}
LinkedList
LinkedList: 实现所有可选列表操作,并允许所有元素(包括null )。
底层结构: 双向链表
特点: 查询效率低,增删效率高
应用场景:单个数据值的集合中,允许数据有序,且可重复,在大量做增删,少量做查询的时候时候适合使用链表
新增功能: 新增了一些与链表头尾相关的方法
注意: 请注意,此实现不同步。
定义链表结构,存储javabean类型的数据,测试使用,遍历使用
存储javabean类型数据时候,要求javabean类型重写equals方法,否则使用一些判断比较的方法时候回默认调用equals方法
代码:
public static void main(String[] args) {
LinkedList <String> list=new LinkedList();
list.add("bbb");
list.add("ccc");
list.add("ddd");
System.out.println(list);
list.addFirst("aaa");
list.addLast("ddd");
list.addLast(null);
System.out.println(list);
LinkedList <Person> person=new LinkedList<>();
person.add(new Person("张三",28));
person.add(new Person("李四",38));
person.add(new Person("王五",16));
System.out.println(person);
System.out.println(person.contains(new Person("张三",28)));
person.forEach(System.out::println);
}
}
class Person{
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = 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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
Set
set:无序的,不可重复|去重
无序:存放的顺序与内部真实存储的顺序不一致
去重:集合不包含元素对e1和e2,使得e1.equals(e2)和最多一个null值
新增功能: static Set of(E… elements) 返回包含任意数量元素的不可修改集。
遍历方式:
foreach
iterator迭代器
代码:
public static void main(String[] args) {
Set<String> set=new HashSet<>();
set.add("aaa");
set.add("bbb");
set.add("ccc");
set.add("ccc");
set.add("ccc");
set.add("ddd");
set.add("ddd");
set.add("ddd");
set.add("ddd");
set.add(null);
set.add(null);
set.add(null);
System.out.println(set);
//遍历
System.out.println("-----------for each---------------------");
for (String str:set){
System.out.println(str);
}
System.out.println("-----------iterator迭代器---------------------");
Iterator <String> it =set.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
}
TreeSet
底层结构 : 红黑树
特点 : 查询效率较高,自动把数据做升序排序
底层是由TreeMap维护的
新增功能: 新增了一些与比较大小相关的方法
遍历方式 :
foreach
iterator迭代器
注意 : TreeSet需要存储相同类型数据,因为会默认存在比较排序
代码:
public class Class002_TreeSet {
public static void main(String[] args) {
TreeSet<Double> tree = new TreeSet();
tree.add(3.2);
tree.add(2.2);
tree.add(5.2);
tree.add(1.2);
tree.add(.2);
System.out.println(tree);
TreeSet<String> tree2 = new TreeSet();
tree2.add("bc");
tree2.add("ab");
tree2.add("a");
tree2.add("abc");
System.out.println(tree2);
//E ceiling(E e) 返回此set中大于或等于给定元素的 null元素,如果没有这样的元素,则 null 。
System.out.println(tree.ceiling(2.3));
//E floor(E e) 返回此set中小于或等于给定元素的最大元素,如果没有这样的元素,则 null 。
System.out.println(tree.floor(2.3));
//E first() 返回此集合中当前的第一个(最低)元素。
System.out.println(tree.first());
//E last() 返回此集合中当前的最后一个(最高)元素。
//E higher(E e) 返回此集合中的最小元素严格大于给定元素,如果没有这样的元素,则 null 。
System.out.println(tree.higher(2.2));
//E lower(E e) 返回此集合中的最大元素严格小于给定元素,如果没有这样的元素,则 null 。
//E pollFirst() 检索并删除第一个(最低)元素,如果此组为空,则返回 null 。
//E pollLast() 检索并删除最后一个(最高)元素,如果此集合为空,则返回 null 。
System.out.println(tree.pollFirst());
System.out.println(tree);
}
}
比较器
TreeSet存储javabean类型的数据
去重与排序: 都是根据比较规则实现的,与equals没有关系
比较规则:
内部比较器|内部比较规则|自然排序 : 比较规则定义在javabean类型的内部
javabean类型实现Comparable接口,重写compareTo(T o)方法,在方法中定义比较规则
外部比较器|外部比较规则|定制排序 : 比较规则定义在javabean类型的外部
定义一个实现类,实现Comparator接口,重写int compare(T o1, T o2),在方法中定义比较规则
Arrays.sort(数组) 默认升序排序
static void sort(T[] a, Comparator<? super T> c) 根据指定比较器引发的顺序对指定的对象数组进行排序。
代码:
public class Class003_Comparable {
public static void main(String[] args) {
//TreeSet<Employee> tree = new TreeSet<>(); //根据Employee默认比较规则,自然排序方式做比较排序
//TreeSet(Comparator<? super E> comparator) 构造一个新的空树集,根据指定的比较器进行排序。
//TreeSet<Employee> tree = new TreeSet<>(new Test());
//匿名内部类 : 简化没有类自己作用的实现类
Comparator<Employee> com = new Comparator<Employee>() {//实现类的类体
@Override
public int compare(Employee o1, Employee o2) {
return Double.compare(o1.getSalary(),o2.getSalary());
}
};
//Lambda : 简化匿名内部类对象,要求必须为函数式接口才能简化
//com = (o1,o2)->o1.getId()-o2.getId();
//TreeSet<Employee> tree = new TreeSet<>(com);
//lambda表达式作为实参传递 -> 可以把行为作为参数传递
TreeSet<Employee> tree = new TreeSet<>((o1,o2)->o1.getId()-o2.getId());
tree.add(new Employee(02,"zhangsan",10000));
tree.add(new Employee(01,"lisi",12000));
tree.add(new Employee(03,"wangwu",8000));
System.out.println(tree);
}
}
//外部比较器
class Test implements Comparator<Employee>{
/*
调用compare方法做两个数据的比较
compare(t1,t2)
返回值:
=0 t1==t2
<0 t1<t2
>0 t1>t2
*/
@Override
public int compare(Employee o1, Employee o2) {
return Double.compare(o2.getSalary(),o1.getSalary());
}
}
HashSet
List 有序 可重复
Set 无序 不可重复
TreeSet : 默认升序排序
**HashSet ******
底层结构: 哈希表(数组+链表+红黑树)
特点: 查询,增删效率高 ,去重,无序
底层是由HashMap维护的
遍历: foreach iterator迭代器
新增方法 : 无
此类允许null元素。
请注意,此实现不同步。
练习: 定义HashSet存储javabean类型得到数据,实现去重,测试使用
去重 : 数据的类型要求重写hashCode与equals方法
代码:
public class Class004_HashSet {
public static void main(String[] args) {
System.out.println("abc".hashCode());
System.out.println(new Person("zhangsan",18).hashCode());
System.out.println(new Person("zhangsan",18).hashCode());
System.out.println(new Integer(105).hashCode());
HashSet<String> set = new HashSet();
set.add("");
}
}
class Person{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
Map
Map : 无序的,去重的
键值对数据的集合
键值对->映射关系
键值对: K-V
K键 : 无序的,去重的|唯一的 —> Set
V值 : 无序的,可重复 —> Collection
K-V可以为任意引用数据类型
特点:
一个key只能对应一个Value
key相同value覆盖
遍历方式:
1.values 获取所有键值对的值
Collection<V> values() 返回此映射中包含的值的Collection视图。
2.keySet 获取所有键值对的key,根据key获取value
Set<K> keySet() 返回此映射中包含的键的Set视图。
3.entrySet 获取所有的键值对,每一个键值对都是一个Entry类型->表示一个键值对
Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射的Set视图。
代码:
public class Class001_Map {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<>();
//V put(K key, V value) 将指定的值与此映射中的指定键相关联(可选操作)。
map.put(101,"胡歌");
map.put(103,"彭于晏");
map.put(102,"吴彦祖");
map.put(null,null);
map.put(105,null);
System.out.println(map.put(104,"王渊"));
System.out.println(map);
System.out.println( map.put(102,"金城武"));
System.out.println(map);
//V get(Object key) 返回指定键映射到的值,如果此映射不包含键的映射,则返回 null 。
System.out.println(map.get(103));
//void clear() 从此映射中删除所有映射(可选操作)。
//boolean isEmpty() 如果此映射不包含键 - 值映射,则返回 true 。
//boolean containsKey(Object key) 如果此映射包含指定键的映射,则返回 true 。
System.out.println(map.containsKey(null));
//boolean containsValue(Object value) 如果此映射将一个或多个键映射到指定值,则返回 true 。
System.out.println(map.containsValue(null));
//static <K,V> Map<K,V> of(K k1, V v1) 返回包含单个映射的不可修改的映射。
Map<String,Boolean> map2 = Map.of("xixi",true,"haha",false);
//map2.put("heihei",false); //java.lang.UnsupportedOperationException
System.out.println(map2);
//V remove(Object key) 如果存在,则从该映射中移除键的映射(可选操作)。
System.out.println(map.remove(101));
//default V replace(K key, V value) 仅当指定键当前映射到某个值时,才替换该条目的条目。
System.out.println(map.replace(105,"宋承宪"));
System.out.println(map);
//int size() 返回此映射中键 - 值映射的数量。
System.out.println(map.size());
//遍历
System.out.println("-----------------values-----------------");
Collection<String> values = map.values();
for(String value:values){
System.out.println(value);
}
System.out.println("-----------------keySet-----------------");
Set<Integer> set = map.keySet();
Iterator<Integer> it = set.iterator();
while(it.hasNext()){
Integer key = it.next();
System.out.println(key+"--->"+map.get(key));
}
System.out.println("---------------entrySet-----------------");
Set<Map.Entry<Integer,String>> entrys = map.entrySet();
for(Map.Entry<Integer,String> entry:entrys){
System.out.println(entry.getKey()+"--->"+entry.getValue());
}
}
}
TreeMap
底层:红黑树
存储键值对类型的数据,自动升序排序,去重的
去重,排序:根据键值对的key实现,与value本身无关
TreeSet底层是由TreeMap
注意:此实现不同步
测试: 使用TreeMap存储键值对数据,key要求为javabean类型教师数据,value存储教授学科,测试存储练习
是否可以根据key实现去重
测试是否为升序排序,要求根据教师编号做升序排序
去重|排序:
根据key的类型的比较规则
key的数据类型实现内部比较器
传递外部比较规则
代码:
public static void main(String[] args) {
TreeMap <Teacher, String> map= new TreeMap<>((o1,o2)->o2.getNo()-o1.getNo());
map.put(new Teacher(1001,"张三","Java"),"Java");
map.put(new Teacher(1002,"李四","web"),"web");
map.put(new Teacher(1003,"王二","大数据"),"大数据");
map.put(new Teacher(1004,"麻子","C++"),"C++");
map.put(new Teacher(1005,"刘芒","ios"),"ios");
System.out.println(map);
}
}
class Teacher implements Comparable<Teacher> {
private int no;
private String name;
private String subject;
public Teacher() {
}
public Teacher(int no, String name, String subject) {
this.no = no;
this.name = name;
this.subject = subject;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
@Override
public String toString() {
return "Teacher{" +
"no=" + no +
", name='" + name + '\'' +
", subject='" + subject + '\'' +
'}';
}
@Override
public int compareTo(Teacher o) {
return this.no-o.no;
}
HashMap
基于哈希表的Map接口的实现。 此实现提供了所有可选的映射操作,并允许null值和null键。
HashSet 底层是由HashMap
底层结构 : 哈希表(数组+链表+红黑树)
哈希表
数组 : 节点数组Node[] --> 要求数组的长度为2的整数次幂
Node : int hash,Object key,Object value,,Node next
每个索引位置存储的为一个单向链表的首节点(尾插法)
当链表的长度>8,数组的长度>64,会把链表优化成为红黑树
当链表的长度>8,但是数组的长度不大于64,这时候会实现扩容(数组的扩容)
初始容量: 哈希表中的数组默认的初始长度 16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
数组的容量最大容量 : static final int MAXIMUM_CAPACITY = 1 << 30;
加载因子: 0.75 一般不建议改变
默认加载因子 : static final float DEFAULT_LOAD_FACTOR = 0.75f;
扩容阀值 threshold : 扩容的临界值 数据的个数size>数组的长度*加载因子 就会扩容
扩容机制: 原容量的2倍 int newCap = oldCap << 1
新增功能: 无
HashMap的哈希表存储数据的过程:
1.根据key计算哈希值
通过key的hashCode方法的返回值进一步进行hash算法的运算,得到的整数
int hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
2.调用putVal方法实现添加数据(hash,key,value)
1)判断是否是第一次调用put方法做添加 if ((tab = table) == null || (n = tab.length) == 0)
如果是第一次添加,直接调用resize()实现扩容
2)计算位桶的索引 int index = (n - 1) & hash
3)判断哈希表结构的数组table[index]是否存在数据,
如果不存在数据,证明没有头节点,创建新节点,放入当前数组的对应索引位置作为头节点
table[index] = new Node<>(hash, key, value, next);
size数据的个数+1,判断是否>扩容的阀值,如果大于需要调用resize方法进行扩容,如果不大于,不需要扩容直接返回null
if (++size > threshold) resize();
return null;
如果存在数据,作为链表的头结点,遍历这个链表,拿到每一个节点的key与hash值判断是否与要添加的key和hash相同,如果相同,value覆盖
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
value覆盖之后,返回被覆盖的value
V oldValue = e.value;
e.value = value;
return oldValue;
去重 : 根据key做去重,要求key的数据类型重写hashCode与equals方法
Hashtable
Hashtable 与 HashMap之间的区别:
共同点:都是map接口的实现类,底层结构都是哈希表
异同点:
1.继承体系不同
2.线程是否安全不同
HashMap线程不安全的|不同步
Hashtable 线程安全的|同步
3.扩容机制不同
HashMap扩容机制 : 每次扩容原容量的2倍
int newCap = oldCap << 1
Hashtable扩容机制 : 原容量的2倍+1
int newCapacity = (oldCapacity << 1) + 1;
4.键值对数据null值的要求不同
HashMap 可以存储null值的key与value
Hashtable key与value都不为null
5.计算hash值与位桶索引index的算法不同
HashMap :
int hash = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
int index = (n - 1) & hash
Hashtable :
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
如何处理HashMap线程不安全问题:
1.使用Hashtable
2.使用Collections工具类中static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) 返回由指定映射支持的同步(线程安全)映射。
3.juc高级并发编程包 ConcurrentHashMap<K,V>-> 线程安全的哈希表
Collections
操作集合的工具类
静态工厂
void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。
void shuffle(List) //对List容器内的元素进行随机排列
void reverse(List) //对List容器内的元素进行逆续排列
void fill(List, Object) //用一个特定的对象重写整个List容器
nt binarySearch(List, Object)//对于顺序的List容器,采用折半查找的方法查找特定对象
Properties
Properties类表示一组持久的属性。 Properties可以保存到流中或从流中加载。
属性列表中的每个键及其对应的值都是一个字符串。
可以通过Properties实现软编码,便于后期维护
从配置文件中读取键值对数据:
1.定义配置文件xxx.properties
2.定义Properties对象
3.调用load(),与从流中加载数据
4.根据key获取value->getProperty
代码:
public class Class001_Properties {
public static void main(String[] args) throws IOException {
Properties pro = new Properties();
//Object setProperty(String key, String value) 调用 Hashtable方法 put 。
//pro.setProperty("username","xixixixi");
//pro.setProperty("password","123456");
System.out.println(pro);
//String getProperty(String key) 在此属性列表中搜索具有指定键的属性。
//System.out.println(pro.get("username"));
//void load(InputStream inStream) 从输入字节流中读取属性列表(键和元素对)。
pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("login.properties"));
//从配置文件中读取数据
System.out.println(pro.getProperty("username"));
System.out.println(pro.getProperty("password"));
}
}