原文地址:https://www.cloudcrossing.xyz/post/42/
1 泛型
1.1 泛型概述
泛型是一种把明确类型的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型。
格式:<数据类型>。(注意:该数据类型只能是引用类型)
好处:
- A:把运行时期的问题提前到了编译期间
- B:避免了强制类型转换
- C:优化了程序设计,解决了黄色警告线
import java.util.ArrayList;
import java.util.Iterator;
public class GenericDemo {
public static void main(String[] args) {
// 创建
ArrayList<String> array = new ArrayList<String>();
//ArrayList<String> array = new ArrayList<>(); //JDK7的新特性:泛型推断
// 添加元素
array.add("hello");
array.add("world");
array.add("java");
// 遍历
Iterator<String> it = array.iterator();
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
}
}
}
1.2 泛型应用
泛型一般是在集合中使用。
泛型类:把泛型定义在类上。
//ObjectTool.java 工具类
public class ObjectTool<T> {
private T obj;
public T getObj() { return obj; }
public void setObj(T obj) { this.obj = obj; }
}
//ObjectToolDemo.java 工具类测试类
public class ObjectToolDemo {
public static void main(String[] args) {
ObjectTool<String> ot = new ObjectTool<String>();
ot.setObj(new String("林青霞"));
String s = ot.getObj();
System.out.println("姓名是:" + s);
ObjectTool<Integer> ot2 = new ObjectTool<Integer>();
ot2.setObj(new Integer(27));
Integer i = ot2.getObj();
System.out.println("年龄是:" + i);
}
}
泛型方法:把泛型定义在方法上。
//ObjectTool.java 工具类
public class ObjectTool {
public <T> void show(T t) {
System.out.println(t);
}
}
//ObjectToolDemo.java 工具类测试类
public class ObjectToolDemo {
public static void main(String[] args) {
// 定义泛型方法后
ObjectTool ot = new ObjectTool();
ot.show("hello");
ot.show(100);
ot.show(true);
}
}
泛型接口:把泛型定义在接口上。
//Inter.java 接口
public interface Inter<T> {
public abstract void show(T t);
}
//InterImpl.java 接口实现类
public class InterImpl<T> implements Inter<T> {
@Override
public void show(T t) {
System.out.println(t);
}
}
//InterDemo.java 接口实现类测试类
public class InterDemo {
public static void main(String[] args) {
Inter<String> i = new InterImpl<String>();
i.show("hello");
Inter<Integer> ii = new InterImpl<Integer>();
ii.show(100);
}
}
泛型高级(通配符):
-
?
:任意类型,如果没有明确,那么就是Object以及任意的Java类 -
<? extends E>
:向下限定,E及其子类 -
<? super E>
:向上限定,E及其父类
import java.util.ArrayList;
import java.util.Collection;
/*
* 泛型高级(通配符)
* ?:任意类型,如果没有明确,那么就是Object以及任意的Java类了
* ? extends E:向下限定,E及其子类
* ? super E:向上限定,E极其父类
*/
public class GenericDemo {
public static void main(String[] args) {
// 泛型如果明确的写的时候,前后必须一致
Collection<Object> c1 = new ArrayList<Object>();
// Collection<Object> c2 = new ArrayList<Animal>();
// Collection<Object> c3 = new ArrayList<Dog>();
// Collection<Object> c4 = new ArrayList<Cat>();
// ?表示任意的类型都是可以的
Collection<?> c5 = new ArrayList<Object>();
Collection<?> c6 = new ArrayList<Animal>();
Collection<?> c7 = new ArrayList<Dog>();
Collection<?> c8 = new ArrayList<Cat>();
// ? extends E:向下限定,E及其子类
// Collection<? extends Animal> c9 = new ArrayList<Object>();
Collection<? extends Animal> c10 = new ArrayList<Animal>();
Collection<? extends Animal> c11 = new ArrayList<Dog>();
Collection<? extends Animal> c12 = new ArrayList<Cat>();
// ? super E:向上限定,E极其父类
Collection<? super Animal> c13 = new ArrayList<Object>();
Collection<? super Animal> c14 = new ArrayList<Animal>();
// Collection<? super Animal> c15 = new ArrayList<Dog>();
// Collection<? super Animal> c16 = new ArrayList<Cat>();
}
}
class Animal { }
class Dog extends Animal { }
class Cat extends Animal { }
2 JDK5新特性
2.1 增强for
增强for是for循环的一种。可用于简化数组和集合的遍历,但是增强for的目标不能为null,所以我们应先对其进行不为NULL的判断,然后在使用。
格式:
for(元素数据类型 变量 : 数组或者Collection集合) {
使用变量即可,该变量就是元素;
}
举例:
import java.util.ArrayList;
import java.util.List;
public class ForDemo {
public static void main(String[] args) {
// 定义一个int数组
int[] arr = { 1, 2, 3, 4, 5 };
for (int x : arr) {
System.out.println(x);
}
System.out.println("---------------");
// 定义一个字符串数组
String[] strArray = { "林青霞", "风清扬", "东方不败", "刘意" };
for (String s : strArray) {
System.out.println(s);
}
System.out.println("---------------");
// 定义一个集合
ArrayList<Student> array = new ArrayList<Student>();
Student s1 = new Student("林青霞", 27);
Student s2 = new Student("貂蝉", 22);
Student s3 = new Student("杨玉环", 24);
array.add(s1);
array.add(s2);
array.add(s3);
for (Student s : array) { //注意:要提前使用泛型确定集合的元素类型
System.out.println(s.getName() + "---" + s.getAge());
}
System.out.println("---------------");
List<String> list = null;
// 报错 NullPointerException 增强for的目标不能为 NULL
if (list != null) {
for (String s : list) {
System.out.println(s);
}
}
}
}
注意:增强for不能在遍历的时候修改元素,不然会报错 ConcurrentModificationException ,其实他就是用来替代迭代器的。
再来一个例子:集合的嵌套遍历。
import java.util.ArrayList;
public class ArrayListDemo {
public static void main(String[] args) {
// 创建大集合
ArrayList<ArrayList<Student>> bigArrayList = new ArrayList<ArrayList<Student>>();
// 创建第一个班级的学生集合
ArrayList<Student> firstArrayList = new ArrayList<Student>();
// 创建学生
Student s1 = new Student("唐僧", 30);
Student s2 = new Student("孙悟空", 29);
// 学生进班
firstArrayList.add(s1);
firstArrayList.add(s2);
firstArrayList.add(s3);
// 把第一个班级存储到学生系统中
bigArrayList.add(firstArrayList);
// 创建第二个班级的学生集合
ArrayList<Student> secondArrayList = new ArrayList<Student>();
// 创建学生
Student s11 = new Student("诸葛亮", 30);
Student s22 = new Student("司马懿", 28);
Student s33 = new Student("周瑜", 26);
// 学生进班
secondArrayList.add(s11);
secondArrayList.add(s22);
secondArrayList.add(s33);
// 把第二个班级存储到学生系统中
bigArrayList.add(secondArrayList);
// 创建第三个班级的学生集合
ArrayList<Student> thirdArrayList = new ArrayList<Student>();
// 创建学生
Student s111 = new Student("宋江", 40);
Student s222 = new Student("吴用", 35);
Student s333 = new Student("高俅", 30);
Student s444 = new Student("李师师", 22);
// 学生进班
thirdArrayList.add(s111);
thirdArrayList.add(s222);
thirdArrayList.add(s333);
thirdArrayList.add(s444);
// 把第三个班级存储到学生系统中
bigArrayList.add(thirdArrayList);
// 遍历集合
for (ArrayList<Student> array : bigArrayList) {
for (Student s : array) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
}
2.2 静态导入
格式:import static 包名... .类名.方法名;
静态导入可以直接导入到方法的级别。但是一般不实用静态导入。
注意事项:
- A:导入的方法必须是静态的
- B:如果有多个同名的静态方法,为了防止二义性,使用时必须加前缀
import static java.lang.Math.abs;
import static java.lang.Math.pow;
import static java.lang.Math.max;
//错误
//import static java.util.ArrayList.add;
public class StaticImportDemo {
public static void main(String[] args) {
System.out.println(java.lang.Math.abs(-100));
System.out.println(pow(2, 3));
System.out.println(max(20, 30));
}
public static void abs(String s){
System.out.println(s);
}
}
2.3 可变参数
如果在编写方法时,参数个数不明确时,就应该定义可变参数。
格式:修饰符 返回值类型 方法名(数据类型... 变量){}
注意:
- A:该变量名其实是一个数组名
- B:如果一个方法有多个参数,并且有可变参数,可变参数必须放在最后
public static int sum(int... a) {
int s = 0;
for(int x : a){
s +=x;
}
return s;
}
补充一个方法:public static <T> List<T> asList(T... a):把数组转成集合,但是集合长度不可变。
import java.util.Arrays;
import java.util.List;
public class ArraysDemo {
public static void main(String[] args) {
// 定义一个数组
// String[] strArray = { "hello", "world", "java" };
// List<String> list = Arrays.asList(strArray);
List<String> list = Arrays.asList("hello", "world", "java");
// list.add("javaee"); // 报错UnsupportedOperationException
// list.remove(1); // 报错UnsupportedOperationException
list.set(1, "javaee");
for (String s : list) {
System.out.println(s);
}
}
}
3 注册登录案例
首先思考三个问题:
- A:有哪些类呢?
- B:每个类有哪些东西呢?
- C:类与类之间的关系是什么呢?
分析:
- A:有哪些类呢?
- 用户类
- 测试类
- B:每个类有哪些东西呢?
- 用户类:
- 成员变量:用户名,密码
- 构造方法:无参构造
- 成员方法:getXxx()/setXxx()、登录、注册
- 测试类:
- main方法
- 用户类:
- C:类与类之间的关系是什么呢?
- 在测试类中创建用户操作类和用户基本描述类的对象,并使用其功能
假如用户类的内容比较多时,将来维护起来就比较麻烦,为了更清晰的分类,我们就把用户又划分成了两类:
- 用户基本描述类
- 成员变量:用户名,密码
- 构造方法:无参构造
- 成员方法:getXxx()/setXxx()
- 用户操作类
- 登录,注册
分包:
- A:功能划分
- B:模块划分
- C:先按模块划分,再按功能划分
现在我们选择按照功能划分:
- 用户基本描述类包 cn.cloud.pojo
package cn.cloud.pojo;
/**
* 这是用户基本描述类
* @author cloud
* @version V1.0
*/
public class User {
private String username;// 用户名
private String password;// 密码
public User() { }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
- 用户操作接口 cn.cloud.dao
package cn.cloud.dao;
import cn.cloud.pojo.User;
/**
* 这是针对用户进行操作的接口
* @author cloud
* @version V1.0
*/
public interface UserDao {
/**
* 这是用户登录功能
* @param username 用户名
* @param password 密码
* @return 返回登录是否成功
*/
public abstract boolean isLogin(String username, String password);
/**
* 这是用户注册功能
* @param user 要注册的用户信息
*/
public abstract void regist(User user);
}
- 用户操作类包 cn.cloud.dao.impl
package cn.cloud.dao.impl;
import java.util.ArrayList;
import cn.cloud.dao.UserDao;
import cn.cloud.pojo.User;
/**
* 这是用户操作的具体实现类(集合版)
* @author cloud
* @version V1.0
*/
public class UserDaoImpl implements UserDao {
// 为了让多个方法能够使用同一个集合,就把集合定义为成员变量
// 为了不让外人看到,用private
// 为了让多个对象共享同一个成员变量,用static
private static ArrayList<User> array = new ArrayList<User>();
@Override
public boolean isLogin(String username, String password) {
// 遍历集合,获取每一个用户,并判断该用户的用户名和密码是否和传递过来的匹配
boolean flag = false;
for (User u : array) {
if (u.getUsername().equals(username) && u.getPassword().equals(password)) {
flag = true;
break;
}
}
return flag;
}
@Override
public void regist(User user) {
// 把用户信息存储集合
// ArrayList<User> array = new ArrayList<User>();
array.add(user);
}
}
- 用户测试类 cn.cloud.test
package cn.cloud.test;
import java.util.Scanner;
import cn.cloud.dao.UserDao;
import cn.cloud.dao.impl.UserDaoImpl;
import cn.cloud.pojo.User;
/**
* 用户测试类
* @author cloud
* @version V1.0
* 新增加了两个小问题 A:多个对象共享同一个成员变量,用静态
* B:循环里面如果有switch,并且在switch里面有break,那么结束的不是循环,而是switch语句
*/
public class UserTest {
public static void main(String[] args) {
while (true) {
System.out.println("--------------欢迎光临--------------");
System.out.println("1 登录");
System.out.println("2 注册");
System.out.println("3 退出");
System.out.println("请输入你的选择:");
// 键盘录入选择,根据选择做不同的操作
Scanner sc = new Scanner(System.in);
// 为了后面的录入信息的方便,我所有的数据录入全部用字符接收
String choiceString = sc.nextLine();
// 为了switch中多个case使用的是同一个用户操作类
// 将其放在switch外面定义
UserDao ud = new UserDaoImpl(); //多态
switch (choiceString) {
case "1":
// 登录界面,请输入用户名和密码
System.out.println("--------------登录界面--------------");
System.out.println("请输入用户名:");
String username = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
// 调用登录功能
boolean flag = ud.isLogin(username, password);
if (flag) {
System.out.println("登录成功");
System.exit(0);
// break; //这里break结束的是switch
} else {
System.out.println("用户名或者密码有误,登录失败");
}
break;
case "2":
// 欢迎界面,请输入用户名和密码
System.out.println("--------------注册界面--------------");
System.out.println("请输入用户名:");
String newUsername = sc.nextLine();
System.out.println("请输入密码:");
String newPassword = sc.nextLine();
// 把用户名和密码封装到一个对象中
User user = new User();
user.setUsername(newUsername);
user.setPassword(newPassword);
// 调用注册功能
ud.regist(user);
System.out.println("注册成功");
break;
case "3":
default:
System.out.println("谢谢使用,欢迎下次再来");
System.exit(0);
break;
}
}
}
4 Set集合
4.1 Set集合的特点
Set集合是一个不包含重复元素的 collection。更确切地讲,set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2,并且最多包含一个 null 元素。
Collection集合:
- List:有序(存储顺序和取出顺序一致),可重复
- Set:无序(存储顺序和取出顺序不一致),唯一
public class SetDemo {
public static void main(String[] args) {
// 创建集合对象
Set<String> set = new HashSet<String>();
// 创建并添加元素
set.add("hello");
set.add("java");
set.add("world");
set.add("java");
set.add("world");
// 增强for
for (String s : set) {
System.out.println(s);
}
}
}
/*运行结果:
157
169
158
160*/
4.2 HashSet集合
HashSet类实现了 Set 接口,底层数据结构为哈希表(是一个元素为链表的数组)。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变;允许使用 null 元素。注意,HashSet类不是同步的。
HashSet存储自定义对象并遍历:
public class Student {
...
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
import java.util.HashSet;
public class HashSetDemo2 {
public static void main(String[] args) {
// 创建集合对象
HashSet<Student> hs = new HashSet<Student>();
// 创建学生对象
Student s1 = new Student("林青霞", 27);
Student s2 = new Student("柳岩", 22);
Student s3 = new Student("王祖贤", 30);
Student s4 = new Student("林青霞", 27);
Student s5 = new Student("林青霞", 20);
Student s6 = new Student("范冰冰", 22);
// 添加元素
hs.add(s1);
hs.add(s2);
hs.add(s3);
hs.add(s4);
hs.add(s5);
hs.add(s6);
// 遍历集合
for (Student s : hs) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
Set是如何保证元素唯一性的呢?
- 通过查看add方法的源码,可以知道哈希表底层依赖 两个方法:hashCode()和equals()
- 执行顺序:
- 首先比较哈希值是否相同
- 如果哈希值不同:就直接把元素添加到集合
- 如果哈希值相同:继续执行equals()方法
- 返回true:元素重复了,不添加
- 返回false:直接把元素添加到集合
- 如果类没有重写这两个方法(String类重写了),默认使用Object类的equals()方法。如果定义对象的成员变量值相同即为同一个对象时,就应该重写这两个方法,自动生成。
4.3 LinkedHashSet集合
LinkedHashSet是具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。
此实现与 HashSet 的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,即按照将元素插入到 set 中的顺序(插入顺序)进行迭代。
import java.util.LinkedHashSet;
public class LinkedHashSetDemo {
public static void main(String[] args) {
// 创建集合对象
LinkedHashSet<String> hs = new LinkedHashSet<String>();
// 创建并添加元素
hs.add("hello");
hs.add("world");
hs.add("java");
hs.add("world");
hs.add("java");
// 遍历
for (String s : hs) {
System.out.println(s);
}
}
}
4.4 TreeSet集合
TreeSet的底层数据结构是红黑树(一个自平衡的二叉树)。
其保证元素的排序方式:
- 自然排序(元素具备比较性):让元素所属的类实现Comparable接口
- 比较器排序(集合具备比较性):让集合构造方法接受Comparator的实现类对象
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
// 创建集合对象
// 自然顺序进行排序
TreeSet<Integer> ts = new TreeSet<Integer>();
// 创建元素并添加
ts.add(20);
ts.add(18);
ts.add(23);
ts.add(22);
ts.add(17);
ts.add(24);
ts.add(19);
ts.add(18);
ts.add(24);
// 遍历
for (Integer i : ts) {
System.out.println(i);
}
}
}
通过观察TreeSet的add()方法,比较的结果最终要看TreeMap的put()方法返回的值。而真正的比较是依赖于元素的compareTo()方法,而这个方法是定义在 Comparable里面的。所以,你要想重写该方法,就必须是实现 Comparable 接口。这个接口表示的就是自然排序。
下面看一个TreeSet存储自定义对象并保证排序和唯一的案例。
- A:怎么排序:自然排序,按照姓名的长度从小到大排序
- B:元素什么情况算唯一:成员变量值都相同即为同一个元素
如果一个类的元素要想能够进行自然排序,就必须实现自然排序接口(Comparable 接口)。
//学生类
public class Student implements Comparable<Student> {
...
@Override
public int compareTo(Student s) {
// 主要条件 姓名的长度
int num = this.name.length() - s.name.length();
// 姓名的长度相同,不代表姓名的内容相同
int num2 = num == 0 ? this.name.compareTo(s.name) : num;
// 姓名的长度和内容相同,不代表年龄相同,所以还得继续判断年龄
int num3 = num2 == 0 ? this.age - s.age : num2;
return num3;
}
}
import java.util.TreeSet;
public class TreeSetDemo2 {
public static void main(String[] args) {
// 创建集合对象
TreeSet<Student> ts = new TreeSet<Student>();
// 创建元素
Student s1 = new Student("linqingxia", 27);
Student s2 = new Student("zhangguorong", 29);
Student s3 = new Student("wanglihong", 23);
Student s4 = new Student("linqingxia", 27);
// 添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
// 遍历
for (Student s : ts) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
其保证元素的唯一性方式:根据比较的返回是否是0来决定。如果一个方法的参数是接口,那么真正要的是接口的实现类的对象。所以我们可以用匿名内部类来实现比较器。
import java.util.Comparator;
public class MyComparator implements Comparator<Student> {
@Override
public int compare(Student s1, Student s2) {
int num = s1.getName().length() - s2.getName().length();// 姓名长度
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;// 姓名内容
int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2;// 年龄
return num3;
}
}
public class TreeSetDemo {
public static void main(String[] args) {
// 创建集合对象
// TreeSet<Student> ts = new TreeSet<Student>(); //自然排序
// public TreeSet(Comparator comparator) //比较器排序
// TreeSet<Student> ts = new TreeSet<Student>(new MyComparator());
// 匿名内部类实现比较器
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = s1.getName().length() - s2.getName().length(); // 姓名长度
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num; // 姓名内容
int num3 = num2 == 0 ? s1.getAge() - s2.getAge() : num2; // 年龄
return num3;
}
});
// 创建元素
Student s1 = new Student("linqingxia", 27);
Student s2 = new Student("zhangguorong", 29);
Student s3 = new Student("wanglihong", 23);
// 添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
// 遍历
for (Student s : ts) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
网友评论