Day16
List 的子类ArrayList,Vector,LinkedLis
ArrayList
用法跟List相同
Vector
Vector的特有功能:
添加功能
public void addElement(Object obj)---------被add()替代
获取功能
public Object elementAt(int index)-----------被get()替代
public Enumeration elements()---------------被Iterator iterator()迭代器替代
boolean hasMoreElements()------------------被hasNext()替代
Object nextElement()----------------------------被next()替代
LinkedList
添加功能
public void addFirst(Object e) 在第一个位置添加元素
public void addLast(Object e) 在最后一个位置添加元素
获取功能
public Object getFirst() 获取第一个元素
public Obejct getLast()获取最后一个元素
删除功能
public Object removeFirst()删除第一个元素并返回第一个元素
public Object removeLast()删除最后一个元素并返回最后一个元素
注意:contains()方法的底层依赖的是equals()方法
/*ArrayList去除集合中字符串的重复值(字符串的内容相同)
* 分析:
* 创建集合对象
* 添加多个字符串元素(包含内容相同的)
* 创建新集合
* 遍历旧集合,获取得到每一个元素
* 拿这个元素到新集合去找,看有没有
* 有:不搭理它
* 没有:就添加到新集合
* 遍历新集合
*/
import java.util.ArrayList;
public class SanGe {
public static void main(String[] args) {
//创建集合对象
ArrayList al = new ArrayList();
//添加多个字符串元素(包含内容相同的)
al.add("he");
al.add("nihao");
al.add("nihao");
al.add("he");
al.add("nihao");
al.add("world");
al.add("world");
//创建新集合
ArrayList newAl = new ArrayList();
//遍历旧集合,获取得到每一个元素
for (int i = 0; i < al.size(); i++) {
//拿这个元素到新集合去找,看有没有
if (!newAl.contains(al.get(i))){
//没有:就添加到新集合
newAl.add(al.get(i));
}
}
//遍历新集合
for (int i = 0; i < newAl.size(); i++) {
System.out.println(newAl.get(i)+" ");
}
}
}
ArrarList去重升级版
/*
* 需求:ArrayList去除集合中字符串的重复值(字符串的内容相同)
* 要求:不能创建新的集合,就在以前的集合上做
*/
public class ArrayListDemo2 {
public static void main(String[] args) {
// 创建集合对象
ArrayList array = new ArrayList();
// 添加多个字符串元素(包含内容相同的)
al.add("he");
al.add("nihao");
al.add("nihao");
al.add("he");
al.add("nihao");
al.add("world");
al.add("world");
// 由选择排序思想引入,我们就可以通过这种思想做这个题目
// 拿0索引的依次和后面的比较,有就把后的干掉
// 同理,拿1索引...
for (int i = 0; i < array.size() - 1; i++) {
for (int j = i + 1; j < array.size(); j++) {
if (array.get(i).equals(array.get(j))) {
array.remove(j);
j--;
}
}
}
// 遍历集合
Iterator it = array.iterator();
while (it.hasNext()) {
String s = (String) it.next();
System.out.println(s);
}
}
}
泛型
是一种把类型明确的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型,参数化类型,把类型当作参数一样的传递
格式:
<数据类型>
此处的数据类型只能是引用类型
好处:
把运行时期的问题提前到了编译期间
避免了强制类型转换
优化了程序设计,解决了黄色警告线
泛型在哪些地方使用,看API,如果类,接口,抽象类后面跟的有<E>就说要使用泛型,一般来说就是在集合中使用
public class GenericDemo {
public static void main(String[] args) {
// 创建
ArrayList<String> array = new ArrayList<String>();
// 添加元素
array.add("hello");
array.add("world");
array.add("java");
// 遍历
Iterator<String> it = array.iterator();
while (it.hasNext()) {
// String s = (String) it.next();
//因为Iterator后边跟了String,默认就是String类型,不需要强转
String s = it.next();
System.out.println(s);
}
}
}
泛型应用
泛型类
把泛型定义在类上
格式:public class 类名<泛型类型1,…>
注意:泛型类型必须是引用类型
泛型方法
把泛型定义在方法上
格式:public <泛型类型> 返回类型 方法名(泛型类型 .)
泛型接口
把泛型定义在接口上
格式:public interface 接口名<泛型类型1…>
泛型类
/*
* 泛型类:把泛型定义在类上
*/
public class ObjectTool<T> {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
/*
* 泛型类的测试
*/
public class ObjectToolDemo {
public static void main(String[] args) {
// ObjectTool ot = new ObjectTool();
//
// ot.setObj(new String("张三"));
// String s = (String) ot.getObj();
// System.out.println("姓名是:" + s);
//
// ot.setObj(new Integer(30));
// Integer i = (Integer) ot.getObj();
// System.out.println("年龄是:" + i);
// ot.setObj(new String("李四"));
// // ClassCastException
// Integer ii = (Integer) ot.getObj();
// System.out.println("姓名是:" + ii);
System.out.println("-------------");
ObjectTool<String> ot = new ObjectTool<String>();
// ot.setObj(new Integer(27)); //这个时候编译期间就过不去
ot.setObj(new String("张三"));
String s = ot.getObj();
System.out.println("姓名是:" + s);
ObjectTool<Integer> ot2 = new ObjectTool<Integer>();
// ot2.setObj(new String("李四"));//这个时候编译期间就过不去
ot2.setObj(new Integer(27));
Integer i = ot2.getObj();
System.out.println("年龄是:" + i);
}
}
泛型方法
/*
* 泛型方法:把泛型定义在方法上
*/
public class ObjectTool {
//谁调用就传谁的类型给show方法
public <T> void show(T t) {
System.out.println(t);
}
}
public class ObjectToolDemo {
public static void main(String[] args) {
// 定义泛型方法后
ObjectTool ot = new ObjectTool();
ot.show("hello");//String调用,show就接受String类型
ot.show(100);
ot.show(true);
}
}
泛型接口
/*
* 泛型接口:把泛型定义在接口上
*/
public interface Inter<T> {
public abstract void show(T t);
}
//实现类在实现接口的时候
//第一种情况:已经知道该是什么类型的了
//public class InterImpl implements Inter<String> {
//
// @Override
// public void show(String t) {
// System.out.println(t);
// }
// }
//第二种情况:还不知道是什么类型的
public class InterImpl<T> implements Inter<T> {
@Override
public void show(T t) {
System.out.println(t);
}
}
//测试类
public class InterDemo {
public static void main(String[] args) {
// 第一种情况的测试
// Inter<String> i = new InterImpl();
// i.show("hello");
// 第二种情况的测试
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极其父类
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 {
}
增强for
是for循环的一种
格式:
for(元素数据类型 变量 : 数组或者Collection集合) {
使用变量即可,该变量就是元素
}
好处:简化了数组和集合的遍历,在集合方面就是替代了迭代器
弊端: 增强for的目标不能为null,可以先对增强for的目标先进行不为null的判断,然后在使用
public class ForDemo {
public static void main(String[] args) {
// 定义一个int数组
int[] arr = { 1, 2, 3, 4, 5 };
for (int x = 0; x < arr.length; x++) {
System.out.println(arr[x]);
}
System.out.println("---------------");
// 增强for
for (int x : arr) {
System.out.println(x);
}
System.out.println("---------------");
// 定义一个字符串数组
String[] strArray = { "张三", "李四", "王五", "刘六" };
// 增强for
for (String s : strArray) {
System.out.println(s);
}
System.out.println("---------------");
// 定义一个集合
ArrayList<String> array = new ArrayList<String>();
array.add("hello");
array.add("world");
array.add("java");
// 增强for
for (String s : array) {
System.out.println(s);
}
System.out.println("---------------");
List<String> list = null;
// NullPointerException
// 这个s是我们从list里面获取出来的,在获取前,它肯定还好做一个判断
// 说白了,这就是迭代器的功能
if (list != null) {
for (String s : list) {
System.out.println(s);
}
}
}
}
静态导入
格式:import static 包名….类名.方法名; 可以直接导入到方法的级别
静态导入的注意事项:
方法必须是静态的
如果有多个同名的静态方法,容易不知道使用谁?这个时候要使用,必须加前缀
可变参数
定义方法的时候不知道该定义多少个参数
格式:
修饰符 返回值类型 方法名(数据类型… 变量名){
方法体语句;
}
注意:
这里的变量其实是一个数组
如果一个方法有可变参数,并且有多个参数,那么,可变参数肯定是最后一个
public class ArgsDemo {
public static void main(String[] args) {
// 2个数据求和
int a = 10;
int b = 20;
int result = sum(a, b);
System.out.println("result:" + result);
// 3个数据的求和
int c = 30;
result = sum(a, b, c);
System.out.println("result:" + result);
// 4个数据的求和
int d = 30;
result = sum(a, b, c, d);
System.out.println("result:" + result);
// 需求:我要写一个求和的功能,到底是几个数据求和呢,我不太清楚,但是我知道在调用的时候我肯定就知道了
// 为了解决这个问题,Java就提供了一个东西:可变参数
result = sum(a, b, c, d, 40);
System.out.println("result:" + result);
result = sum(a, b, c, d, 40, 50);
System.out.println("result:" + result);
}
public static int sum(int... a) {
int sum1 = 0;
for(int x : a){
sum1 +=x;
}
return sum1;
}
// public static int sum(int a, int b, int c, int d) {
// return a + b + c + d;
// }
//
// public static int sum(int a, int b, int c) {
// return a + b + c;
// }
//
// public static int sum(int a, int b) {
// return a + b;
// }
}
asLis
public static <T> List<T> asList(T... a):把数组转成集合
注意事项:
虽然可以把数组转成集合,但是集合的长度不能改变
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");
// UnsupportedOperationException
// list.add("javaee");
// UnsupportedOperationException
// list.remove(1);
list.set(1, "javaee");
for (String s : list) {
System.out.println(s);
}
}
}
集合嵌套的存储和遍历元素
需求:
我们班有学生,每一个学生是不是一个对象,所以我们可以使用一个集合表示我们班级的学生,ArrayList<Student>
但是呢,我们旁边是不是还有班级,每个班级是不是也是一个ArrayList<Student>
而我现在有多个ArrayList<Student>,也要用集合存储,ArrayList<ArrayList<Student>>
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);
Student s3 = new Student("猪八戒", 28);
Student s4 = new Student("沙僧", 27);
Student s5 = new Student("白龙马", 26);
// 学生进班
firstArrayList.add(s1);
firstArrayList.add(s2);
firstArrayList.add(s3);
firstArrayList.add(s4);
firstArrayList.add(s5);
// 把第一个班级存储到学生系统中
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());
}
}
}
}
获取不重复的随机数
/*
* 获取10个1-20之间的随机数,要求不能重复
*
* 用数组实现,但是数组的长度是固定的,长度不好确定。
* 所以我们使用集合实现。
*
* 分析:
* 创建产生随机数的对象
* 创建一个存储随机数的集合。
* 定义一个统计变量。从0开始。
* 判断统计遍历是否小于10
* 是:先产生一个随机数,判断该随机数在集合中是否存在。
* 如果不存在:就添加,统计变量++。
* 如果存在:就不搭理它。
* 否:不搭理它
* 遍历集合
*/
public class RandomDemo {
public static void main(String[] args) {
// 创建产生随机数的对象
Random r = new Random();
// 创建一个存储随机数的集合。
ArrayList<Integer> array = new ArrayList<Integer>();
// 定义一个统计变量。从0开始。
int count = 0;
// 判断统计遍历是否小于10
while (count < 10) {
//先产生一个随机数
int number = r.nextInt(20) + 1;
//判断该随机数在集合中是否存在。
if(!array.contains(number)){
//如果不存在:就添加,统计变量++。
array.add(number);
count++;
}
}
//遍历集合
for(Integer i : array){
System.out.println(i);
}
}
}
Day17
集合版登录
//user类
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;
}
}
//定义接口,登录和注册
public interface UserDao {
//用户登录功能,username用户名,password密码,return 返回登录是否成功
public abstract boolean isLogin(String username, String password);
//用户注册功能,user,要注册的用户信息
public abstract void regist(User user);
}
//接口的实现类
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);
}
}
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语句的多个地方要使用,我就定义到外面
UserDao ud = new UserDaoImpl();
// 经过简单的思考,我选择了switch
switch (choiceString) {
case "1":
// 登录界面,请输入用户名和密码
System.out.println("--------------登录界面--------------");
System.out.println("请输入用户名:");
String username = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
// 调用登录功能
// UserDao ud = new UserDaomImpl();
boolean flag = ud.isLogin(username, password);
if (flag) {
System.out.println("登录成功");
} 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);
// 调用注册功能
// 多态
// UserDao ud = new UserDaoImpl();
// 具体类使用
// UserDaoImpl udi = new UserDaoImpl();
ud.regist(user);
System.out.println("注册成功");
break;
case "3":
default:
System.out.println("谢谢使用,欢迎下次再来");
System.exit(0);
break;
}
}
}
}
Sat
Collection
List
有序(存储顺序和取出顺序一致),可重复
Set
无序(存储顺序和取出顺序不一致),唯一
HashSet
它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变
注意:虽然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");
//只会输出hello,world,java,顺序不固定
// 增强for
for (String s : set) {
System.out.println(s);
}
}
}
问题:为什么存储字符串的时候,字符串内容相同的只存储了一个呢?
通过查看add方法的源码,我们知道这个方法底层依赖 两个方法:hashCode()和equals()
步骤:
首先比较哈希值
如果相同,继续走,比较地址值或者走equals()
如果不同,就直接添加到集合中
按照方法的步骤来说:
先看hashCode()值是否相同
相同:继续走equals()方法
返回true: 说明元素重复,就不添加
返回false:说明元素不重复,就添加到集合
不同:就直接把元素添加到集合
如果类没有重写这两个方法,默认使用的Object(),一般来说不同相同,而String类重写了hashCode()和equals()方法,所以,它就可以把内容相同的字符串去掉,只留下一个
public class Student {
private String name;
private int age;
public Student(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 boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student)) return false;
Student student = (Student) o;
return getAge() == student.getAge() && Objects.equals(getName(), student.getName());
}
@Override
public int hashCode() {
return Objects.hash(getName(), getAge());
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class StrudentTest {
public static void main(String[] args) {
HashSet<Student> hs = new HashSet<Student>();
Student s1 = new Student("张三",17);
Student s2 = new Student("李四",15);
Student s3 = new Student("张三",17);
Student s4 = new Student("王五",15);
Student s5 = new Student("张",18);
hs.add(s1);
hs.add(s2);
hs.add(s3);
hs.add(s4);
hs.add(s5);
for (Student s :hs){
System.out.println(s);
}
}
}
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);
}
}
}
TreeSet
能够对元素按照某种规则进行排序
排序有两种方式
自然排序的构造方法TreeSet<Student> ts = new TreeSet<Student>()
比较器排序的构造方法public TreeSet(Comparator comparator)
TreeSet集合的特点:排序和唯一
通过观察TreeSet的add()方法,我们知道最终要看TreeMap的put()方法
compareTo方法
字符串与对象进行比较 int compareTo(Object o) o -- 要比较的对象
按字典顺序比较两个字符串 int compareTo(String anotherString) anotherString -- 要比较的字符串
返回值是整型,它是先比较对应字符的大小(ASCII码顺序),如果第一个字符和参数的第一个字符不等,结束比较,返回他们之间的长度差值,如果第一个字符和参数的第一个字符相等,则以第二个字符和参数的第二个字符做比较,以此类推,直至比较的字符或被比较的字符有一方结束
如果参数字符串等于此字符串,则返回值 0
如果此字符串小于字符串参数,则返回一个小于 0 的值
如果此字符串大于字符串参数,则返回一个大于 0 的值
说明:
如果第一个字符和参数的第一个字符不等,结束比较,返回第一个字符的ASCII码差值
如果第一个字符和参数的第一个字符相等,则以第二个字符和参数的第二个字符做比较,以此类推,直至不等为止,返回该字符的ASCII码差值, 如果两个字符串不一样长,可对应字符又完全一样,则返回两个字符串的长度差值
自然排序
public class TreeSetDemo {
public static void main(String[] args) {
// 创建集合对象
// 自然顺序进行排序
TreeSet<Integer> ts = new TreeSet<Integer>();
// 创建元素并添加
// 20,18,23,22,17,24,19,18,24
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方法(自然排序)存储自定义对象,首先要将对象所属的类去实现Comparable接口,然后根据给出的排序条件去重写Comparable接口的compareTo方法满足排序的条件,还要注意隐含的条件
/*
* 需求:请按照姓名的长度排序
*/
//如果一个类的元素要想能够进行自然排序,元素所属的类就必须实现自然排序接口,根据条件重写compareTo方法
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
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 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;
}
}
public class TreeSetDemo {
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);
Student s5 = new Student("liushishi", 22);
Student s6 = new Student("wuqilong", 40);
Student s7 = new Student("fengqingy", 22);
Student s8 = new Student("linqingxia", 29);
// 添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
ts.add(s8);
// 遍历
for (Student s : ts) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
比较器排序
/*
* 需求:请按照姓名的长度排序
*
* TreeSet集合保证元素排序和唯一性的原理
* 唯一性:是根据比较的返回是否是0来决定。
* 排序:
* A:自然排序(元素具备比较性)
* 让元素所属的类实现自然排序接口 Comparable
* B:比较器排序(集合具备比较性)
* 让集合的构造方法接收一个比较器接口的子类对象 Comparator
*/
public class TreeSetDemo {
public static void main(String[] args) {
// 创建集合对象
// 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);
Student s4 = new Student("linqingxia", 27);
Student s5 = new Student("liushishi", 22);
Student s6 = new Student("wuqilong", 40);
Student s7 = new Student("fengqingy", 22);
Student s8 = new Student("linqingxia", 29);
// 添加元素
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
ts.add(s8);
// 遍历
for (Student s : ts) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
获取10个1-20之间的随机数集合版
public class RandomTest {
public static void main(String[] args) {
HashSet<Integer> hs = new HashSet<Integer>();
Random r = new Random();
while (hs.size() < 10) {
int num = r.nextInt(20) + 1;
hs.add(num);
}
for (Integer i : hs) {
System.out.println(i);
}
}
}
Day18
Map
特点:将键映射到值的对象,一个映射不能包含重复的键;每个键最多只能映射到一个值,参考学号和学生,一个学号对应一个学生,学号不可以重复,学生的姓名可以重复,学号为键,学生为值
Map集合和Collection集合的区别
Map集合存储元素是成对出现的,Map集合的键是唯一的,值是可重复的
Collection集合存储元素是单独出现的,Collection的儿子Set是唯一的,List是可重复的
Map集合的功能概述:
添加功能
V put(K key,V value):添加元素,如果键是第一次存储,就直接存储元素,返回null,如果键不是第一次存在,就用值把以前的值替换掉,返回以前的值
删除功能
void clear():移除所有的键值对元素
V remove(Object key):根据键删除键值对元素,并把值返回
判断功能
boolean containsKey(Object key):判断集合是否包含指定的键
boolean containsValue(Object value):判断集合是否包含指定的值
boolean isEmpty():判断集合是否为空
获取功能
Set<Map.Entry<K,V>> entrySet()返回的是键值对对象的集合
V get(Object key):根据键获取值
Set<K> keySet():获取集合中所有键的集合
Collection<V> values():获取集合中所有值的集合
长度功能
int size():返回集合中的键值对的对数
public class MapDemo {
public static void main(String[] args) {
// 创建集合对象
Map<String, String> map = new HashMap<String, String>();
// 添加元素
// V put(K key,V value):添加元素
// System.out.println("put:" + map.put("文章", "马伊俐"));
// System.out.println("put:" + map.put("文章", "姚笛"));
map.put("邓超", "孙俪");
map.put("黄晓明", "杨颖");
map.put("周杰伦", "蔡依林");
map.put("刘恺威", "杨幂");
// void clear():移除所有的键值对元素
// map.clear();
// V remove(Object key):根据键删除键值对元素,并把值返回
// System.out.println("remove:" + map.remove("黄晓明"));
// System.out.println("remove:" + map.remove("黄晓波"));
// boolean containsKey(Object key):判断集合是否包含指定的键
// System.out.println("containsKey:" + map.containsKey("黄晓明"));
// System.out.println("containsKey:" + map.containsKey("黄晓波"));
// boolean isEmpty():判断集合是否为空
// System.out.println("isEmpty:"+map.isEmpty());
//int size():返回集合中的键值对的对数
System.out.println("size:"+map.size());
// 输出集合名称
System.out.println("map:" + map);
}
}
Map集合的遍历
方式一
获取所有的键
遍历键的集合,获取得到每一个键
根据键去找值
方式二
获取所有键值对对象的集合
遍历键值对对象的集合,得到每一个键值对对象
根据键值对对象获取键和值
public class MapPrint {
public static void main(String[] args) {
//创建集合
Map<Integer, String> map = new HashMap<>();
//添加元素
map.put(201801, "张三");
map.put(201802, "李四");
map.put(201803, "王五");
map.put(201804, "赵六");
map.put(201805, "付七");
//遍历集合方式一
//先获取所有的键的集合
Set<Integer> set = map.keySet();
//遍历键的集合,得到每一个键
for (Integer key : set) {
//根据键去找值
String value = map.get(key);
//输出键和值
System.out.println(key+value);
}
//遍历集合方式二
//Set<Map.Entry<K,V>> entrySet():返回的是键值对对象的集合
//获取键值对对象
Set <Map.Entry<Integer,String>> sm = map.entrySet();
// 遍历键值对对象的集合,得到每一个键值对对象
for (Map.Entry<Integer,String> me : sm){
// 根据键值对对象获取键和值
Integer key = me.getKey();
String value = me.getValue();
System.out.println(key+value);
}
}
}
HashMap
/*
* HashMap<Student,String>
* 键:Student
* 要求:如果两个对象的成员变量值都相同,则为同一个对象需要重写方法
* 值:String
*/
public class HashMapDemo4 {
public static void main(String[] args) {
// 创建集合对象
HashMap<Student, String> hm = new HashMap<Student, String>();
// 创建学生对象
Student s1 = new Student("貂蝉", 27);
Student s2 = new Student("王昭君", 30);
Student s3 = new Student("西施", 33);
Student s4 = new Student("杨玉环", 35);
Student s5 = new Student("貂蝉", 27);
// 添加元素
hm.put(s1, "8888");
hm.put(s2, "6666");
hm.put(s3, "5555");
hm.put(s4, "7777");
hm.put(s5, "9999");
// 遍历
Set<Student> set = hm.keySet();
for (Student key : set) {
String value = hm.get(key);
System.out.println(key.getName() + "---" + key.getAge() + "---"
+ value);
}
}
}
public class Student {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
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 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;
}
}
LinkedHashMap
是Map接口的哈希表和链接列表实现,具有可预知的迭代顺序
由哈希表保证键的唯一性
由链表保证键盘的有序(存储和取出的顺序一致)
TreeMap
是基于红黑树的Map接口的实现\
public class TreeMapDemo2 {
public static void main(String[] args) {
// 创建集合对象
TreeMap<Student, String> tm = new TreeMap<Student, String>(
new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
// 主要条件
int num = s1.getAge() - s2.getAge();
// 次要条件
int num2 = num == 0 ? s1.getName().compareTo(
s2.getName()) : num;
return num2;
}
});
// 创建学生对象
Student s1 = new Student("潘安", 30);
Student s2 = new Student("柳下惠", 35);
Student s3 = new Student("唐伯虎", 33);
Student s4 = new Student("燕青", 32);
Student s5 = new Student("唐伯虎", 33);
// 存储元素
tm.put(s1, "宋朝");
tm.put(s2, "元朝");
tm.put(s3, "明朝");
tm.put(s4, "清朝");
tm.put(s5, "汉朝");
// 遍历
Set<Student> set = tm.keySet();
for (Student key : set) {
String value = tm.get(key);
System.out.println(key.getName() + "---" + key.getAge() + "---"+ value);
}
}
}
键盘录入一串字符,例如"aababcabcdabcde",获取字符串中每一个字母出现的次数要求结果:a(5)b(4)c(3)d(2)e(1)
/*
* 定义一个字符串,键盘获取
* 定义一个TreeMap集合
* 键:Character
* 值:Integer
* 把字符串转换为字符数组
* 遍历字符数组,得到每一个字符
* 拿刚才得到的字符作为键到集合中去找值,看返回值
* 是null:说明该键不存在,就把该字符作为键,1作为值存储
* 不是null:说明该键存在,就把值加1,然后重写存储该键和值
* 定义字符串缓冲区变量,或者直接遍历集合
* 遍历集合,得到键和值,进行按照要求拼接
* 把字符串缓冲区转换为字符串输出
*
* */
public class StrudentTest {
public static void main(String[] args) {
//创建键盘获取对象
Scanner sc = new Scanner(System.in);
//定义字符串接受键盘录入
String s = sc.nextLine();
//创建集合
TreeMap<Character,Integer> tm = new TreeMap<>();
//将键盘获取的字符串转换成字符数组
char chs[] = s.toCharArray();
//遍历字符数组
for (Character ch :chs){
//得到每一个元素
Integer i = tm.get(ch);
//比较
if (i == null){
tm.put(ch,1);
}else{
i++;
tm.put(ch,i);
}
}
//遍历集合
Set<Character> set = tm.keySet();
for (Character c :set){
Integer value = tm.get(c);
System.out.print(c+"("+value+")");//如果直接输出c+value,会把字符转换成ascll码表的值相加
}
System.out.println("------------");
//创建字符缓冲区对象
StringBuilder sb= new StringBuilder();
//遍历集合,得到键和值,进行按照要求拼接
Set<Character> set2 = tm.keySet();
for(Character key : set2){
Integer value = tm.get(key);
sb.append(key).append("(").append(value).append(")");
}
//把字符串缓冲区转换为字符串输出
String result = sb.toString();
System.out.println("result:"+result);
}
}
集合嵌套的遍历
HashMap嵌套HashMap
/*
*需求 :乒乓球教学班分为基础班和进阶班
* 基础班 张三 20
* 李四 22
* 进阶班 王五 18
* 赵六 19
* 利用集合嵌套输出
*分析:乒乓球教学班作为一个集合<基础班 (张三,20)> ==<String HashMap<String,Integer>>
* 基础班作为一个集合<String,Integer>
* 进阶班作为一个集合<String,Integer>
* */
public class Test {
public static void main(String[] args) {
//创建大集合乒乓教学班
HashMap<String,HashMap<String,Integer>> ppmap = new HashMap<String,HashMap<String,Integer>>();
//创建基础班集合
HashMap<String,Integer> jcmap = new HashMap<String ,Integer>();
//添加基础班元素
jcmap.put("张三",20);
jcmap.put("李四",22);
//添加大集合乒乓球教学班元素
ppmap.put("基础班",jcmap);
//创建进阶班集合
HashMap<String,Integer> jjmap = new HashMap<String ,Integer>();
//添加基础班元素
jjmap.put("王五",18);
jjmap.put("赵六",19);
//添加大集合乒乓球教学班元素
ppmap.put("进阶班",jjmap);
//遍历
//先得到大集合所有的主键
Set<String> ppmapset = ppmap.keySet();
//增强for遍历大循环
for (String ppmapkey :ppmapset){
HashMap<String,Integer> ppvalue = ppmap.get(ppmapkey);
System.out.println(ppmapkey);
//遍历内集合基础班,进阶班
Set<String> ppvalueset = ppvalue.keySet();
for (String key : ppvalueset){
Integer value = ppvalue.get(key);
System.out.println('\t'+key + " "+value);
}
}
}
}
HashMap嵌套ArrayList
*需求:
*假设HashMap集合的元素是ArrayList有3个
*每一个ArrayList集合的值是字符串
*结果:
* 三国演义
* 吕布
* 周瑜
* 笑傲江湖
* 令狐冲
* 林平之
* 神雕侠侣
* 郭靖
* 杨过
*/
public class HashMapIncludeArrayListDemo {
public static void main(String[] args) {
// 创建集合对象
HashMap<String, ArrayList<String>> hm = new HashMap<String, ArrayList<String>>();
// 创建元素集合1
ArrayList<String> array1 = new ArrayList<String>();
array1.add("吕布");
array1.add("周瑜");
hm.put("三国演义", array1);
// 创建元素集合2
ArrayList<String> array2 = new ArrayList<String>();
array2.add("令狐冲");
array2.add("林平之");
hm.put("笑傲江湖", array2);
// 创建元素集合3
ArrayList<String> array3 = new ArrayList<String>();
array3.add("郭靖");
array3.add("杨过");
hm.put("神雕侠侣", array3);
//遍历集合
Set<String> set = hm.keySet();
for(String key : set){
System.out.println(key);
ArrayList<String> value = hm.get(key);
for(String s : value){
System.out.println("\t"+s);
}
}
}
}
ArrayList集合嵌套HashMap
/*
* 需求:
*假设ArrayList集合的元素是HashMap有3个
*每一个HashMap集合的键和值都是字符串
*结果:
* 周瑜---小乔
* 吕布---貂蝉
*
* 郭靖---黄蓉
* 杨过---小龙女
*
* 令狐冲---任盈盈
* 林平之---岳灵珊
*/
public class ArrayListIncludeHashMapDemo {
public static void main(String[] args) {
// 创建集合对象
ArrayList<HashMap<String, String>> array = new ArrayList<HashMap<String, String>>();
// 创建元素1
HashMap<String, String> hm1 = new HashMap<String, String>();
hm1.put("周瑜", "小乔");
hm1.put("吕布", "貂蝉");
// 把元素添加到array里面
array.add(hm1);
// 创建元素1
HashMap<String, String> hm2 = new HashMap<String, String>();
hm2.put("郭靖", "黄蓉");
hm2.put("杨过", "小龙女");
// 把元素添加到array里面
array.add(hm2);
// 创建元素1
HashMap<String, String> hm3 = new HashMap<String, String>();
hm3.put("令狐冲", "任盈盈");
hm3.put("林平之", "岳灵珊");
// 把元素添加到array里面
array.add(hm3);
// 遍历
for (HashMap<String, String> hm : array) {
Set<String> set = hm.keySet();
for (String key : set) {
String value = hm.get(key);
System.out.println(key + "---" + value);
}
}
}
}
Hashtable和HashMap的区别
Hashtable:线程安全,效率低,不允许null键和null值
HashMap:线程不安全,效率高,允许null键和null值
HashMap是1.2版本用来替代Hashtable的,用法相同,只是允许使用空键和空值
List,Set,Map等接口是否都继承子Map接口
List,Set不是继承自Map接口,它们继承自Collection接口
Map接口本身就是一个顶层接口
Collections
是针对集合进行操作的工具类,都是静态方法
方法:
public static <T> void sort(List<T> list):排序 默认情况下是自然顺序。
public static <T> int binarySearch(List<?> list,T key):二分查找
public static <T> T max(Collection<?> coll):最大值
public static void reverse(List<?> list):反转
public static void shuffle(List<?> list):随机置换
Collection和Collections的区别
Collection:是单列集合的顶层接口,有子接口List和Set
Collections:是针对集合操作的工具类,有对集合进行排序和二分查找的方法
//Collections方法的用法
public class CollectionsDemo {
public static void main(String[] args) {
// 创建集合对象
List<Integer> list = new ArrayList<Integer>();
// 添加元素
list.add(30);
list.add(20);
list.add(50);
list.add(10);
list.add(40);
System.out.println("list:" + list);
// public static <T> void sort(List<T> list):排序 默认情况下是自然顺序。
// Collections.sort(list);
// System.out.println("list:" + list);
// [10, 20, 30, 40, 50]
// public static <T> int binarySearch(List<?> list,T key):二分查找
// System.out
// .println("binarySearch:" + Collections.binarySearch(list, 30));
// System.out.println("binarySearch:"
// + Collections.binarySearch(list, 300));
// public static <T> T max(Collection<?> coll):最大值
// System.out.println("max:"+Collections.max(list));
// public static void reverse(List<?> list):反转
// Collections.reverse(list);
// System.out.println("list:" + list);
//public static void shuffle(List<?> list):随机置换
Collections.shuffle(list);
System.out.println("list:" + list);
}
}
Collections存储自定义对象的排序
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
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 int compareTo(Student s) {
int num = this.age - s.age;
int num2 = num == 0 ? this.name.compareTo(s.name) : num;
return num2;
}
}
public class CollectionsDemo {
public static void main(String[] args) {
// 创建集合对象
List<Student> list = new ArrayList<Student>();
// 创建学生对象
Student s1 = new Student("林青霞", 27);
Student s2 = new Student("风清扬", 30);
Student s3 = new Student("刘晓曲", 28);
Student s4 = new Student("武鑫", 29);
Student s5 = new Student("林青霞", 27);
// 添加元素对象
list.add(s1);
list.add(s2);
list.add(s3);
list.add(s4);
list.add(s5);
// 排序
// 自然排序
// Collections.sort(list);
// 比较器排序
// 如果同时有自然排序和比较器排序,以比较器排序为主
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = s2.getAge() - s1.getAge();
int num2 = num == 0 ? s1.getName().compareTo(s2.getName())
: num;
return num2;
}
});
// 遍历集合
for (Student s : list) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
斗地主发牌案例
/*
*需求 :实现斗地主发牌
* 分析:创建一个HashMap集合,存储牌的编号和牌(0 -- ♠3,1 -- ♥3,2 -- ♣3,3 -- ♦3.....按照斗地主的大小顺序存)
* 创建一个ArrayList集合存储牌的编号,方便使用public static void shuffle(List<?> list)随机置换功能
* 创建花色和点数数组
* 从编号0开始往HashMap中存储元素,并把编号放到ArrayList中
* 洗牌,对ArrayList进行置换功能,打乱顺序
* 发牌,发的是牌的编号,用TreeSet接收,方便排序
* 看牌,遍历TreeSet集合得到编号,到HashMap中找对应的值
* */
public class Test {
public static void main(String[] args) {
//创建一个HashMap集合
HashMap<Integer,String> hm = new HashMap<Integer,String>();
//创建一个ArrayList
ArrayList<Integer> array = new ArrayList<Integer>();
//创建花色和点数数组
String []colors = {"♥","♠","♦","♣"};
//要根据斗地主的规则来定义顺序,方便存储
String []numbers = {"3","4","5","6","7", "8","9","10","J","Q","K","A","2",};
//定义索引,并遍历花色和点数数组,存储元素
int index = 0;
for (String num :numbers){
for (String color :colors){
//将花色和点数拼接
String poker = color.concat(num);
//把编号和牌放入HashMap集合
hm.put(index,poker);
//将编号放入ArrayList
array.add(index);
index++;
}
}
//存储大小王
hm.put(index,"小王");
array.add(index);
index++;
hm.put(index,"大王");
array.add(index);
//洗牌,将ArrayList中的编号打乱
Collections.shuffle(array);
//发牌,定义TreeSet接收,方便排序
//定义三个玩家和底牌的集合
TreeSet<Integer> zhangSan =new TreeSet<Integer>();
TreeSet<Integer> liSi =new TreeSet<Integer>();
TreeSet<Integer> zhaoWu =new TreeSet<Integer>();
TreeSet<Integer> diPai =new TreeSet<Integer>();
//遍历ArrayList集合,让每个玩家得到编号以及底牌
for (int i = 0; i < array.size(); i++) {
if (i >= array.size()-3){
diPai.add(array.get(i));
}else if (i % 3 ==0){
zhangSan.add(array.get(i));
}else if (i % 3 ==1){
liSi.add(array.get(i));
}else if (i%3 ==2){
zhaoWu.add(array.get(i));
}
}
//看牌
lookPoker("张三",zhangSan,hm);
lookPoker("李四",liSi,hm);
lookPoker("赵五",zhaoWu,hm);
lookPoker("底牌",diPai,hm);
}
//看牌,遍历TreeSet集合得到编号,到HashMap中找对应的值,需要遍历三个集合所以定义为方法
public static void lookPoker(String name,TreeSet<Integer> set,HashMap<Integer,String> hm){
System.out.print(name+"的牌是");
for (Integer in :set){
System.out.print(hm.get(in)+" ");
}
System.out.println();
}
}
集合知识点总结
Collection(单列集合)分为List和Set
List(有序,可重复)分为ArrayList,Vector,LinkedList
ArrayList
底层数据结构是数组,查询快,增删慢
线程不安全,效率高
Vector
底层数据结构是数组,查询快,增删慢
线程安全,效率低
LinkedList
底层数据结构是链表,查询慢,增删快
线程不安全,效率高
Set(无序,唯一)分为HashSet,LinkedHashSet,TreeSet
HashSet
底层数据结构是哈希表
哈希表依赖两个方法:hashCode()和equals()
执行顺序:
首先判断hashCode()值是否相同
是:继续执行equals(),看其返回值
是true:说明元素重复,不添加
是false:就直接添加到集合
否:就直接添加到集合
最终:
自动生成hashCode()和equals()即可
LinkedHashSet
底层数据结构由链表和哈希表组成
由链表保证元素有序
由哈希表保证元素唯一
TreeSet
底层数据结构是红黑树。(是一种自平衡的二叉树)
如何保证元素唯一性呢?根据比较的返回值是否是0来决定
如何保证元素的排序呢?
两种方式
自然排序(元素具备比较性)
让元素所属的类实现Comparable接口
比较器排序(集合具备比较性)
让集合接收一个Comparator的实现类对象
Map(双列集合)分为HashMap,LinkedHashMap,Hashtable,TreeMap
Map集合的数据结构仅仅针对键有效,与值无关
存储的是键值对形式的元素,键唯一,值可重复
HashMap
底层数据结构是哈希表。线程不安全,效率高
哈希表依赖两个方法:hashCode()和equals()
执行顺序:
首先判断hashCode()值是否相同
是:继续执行equals(),看其返回值
是true:说明元素重复,不添加
是false:就直接添加到集合
否:就直接添加到集合
最终:
自动生成hashCode()和equals()即可
LinkedHashMap
底层数据结构由链表和哈希表组成,由链表保证元素有序,由哈希表保证元素唯一
Hashtable
底层数据结构是哈希表。线程安全,效率低
哈希表依赖两个方法:hashCode()和equals()
执行顺序:
首先判断hashCode()值是否相同
是:继续执行equals(),看其返回值
是true:说明元素重复,不添加
是false:就直接添加到集合
否:就直接添加到集合
最终:
自动生成hashCode()和equals()即可
TreeMap
底层数据结构是红黑树。(是一种自平衡的二叉树)
如何保证元素唯一性呢?根据比较的返回值是否是0来决定
如何保证元素的排序呢?
两种方式
自然排序(元素具备比较性)
让元素所属的类实现Comparable接口
比较器排序(集合具备比较性)
让集合接收一个Comparator的实现类对象
到底使用那种集合要看需求
是否是键值对象形式:
是:Map
键是否需要排序:
是:TreeMap
否:HashMap
不知道,就使用HashMap
否:Collection
元素是否唯一:
是:Set
元素是否需要排序:
是:TreeSet
否:HashSet
不知道,就使用HashSet
否:List
要安全吗:
是:Vector(其实我们也不用它,后面我们讲解了多线程以后,我在给你回顾用谁)
否:ArrayList或者LinkedList
增删多:LinkedList
查询多:ArrayList
不知道,就使用ArrayList
不知道,就使用ArrayList
集合的常见方法及遍历方式
Collection:
方法
add() 甜加
remove()删除
contains()判断
iterator()迭代器
size()长度
遍历:
增强for
迭代器
List(方法继承自Collection)
get()
遍历:
普通for
增强for
迭代器
Set(方法继承自Collection)
遍历
增强for
迭代器
Map:
put() 添加
remove() 删除
containskey()判断键
containsValue()判断值
keySet()获取键
get()
value()
entrySet()
size()长度
遍历:
根据键找值
根据键值对对象分别找键和值
ArrayList,LinkedList,HashSet,HashMap(掌握)
存储字符串和自定义对象数据并遍历
Day19
IO流
异常:程序出现了不正常的情况
程序的异常:Throwable
严重问题:Error 我们不处理。这种问题一般都是很严重的,比如说内存溢出
问题:Exception
编译期问题:不是RuntimeException的异常 必须进行处理的,因为你不处理,编译就不能通过
运行期问题:RuntimeException 这种问题我们也不处理,因为是你的问题,而且这个问题出现肯定是我们的代码不够严谨,需要修正代码的
如何程序出现了问题,我们没有做任何处理,最终jvm会做出默认的处理,把异常的名称,原因及出现的问题等信息输出在控制台,同时会结束程序
异常处理
-try...catch...finally
-throws 抛出
try...catch...finally的处理格式:
try {
可能出现问题的代码;
}catch(异常名 变量) {
针对问题的处理;
}finally {
释放资源;
}
变形格式:
try {
可能出现问题的代码;
}catch(异常名 变量) {
针对问题的处理;
}
注意:
try里面的代码越少越好
catch里面必须有内容,哪怕是给出一个简单的提示
public class ExceptionDemo {
public static void main(String[] args) {
// 第一阶段
int a = 10;
// int b = 2;
int b = 0;
try {
System.out.println(a / b);
} catch (ArithmeticException ae) {
System.out.println("除数不能为0");
}
// 第二阶段
System.out.println("over");
}
}
两个及以上的异常的处理
-每一个写一个try...catch
-写一个try,多个catch
try{
...
}catch(异常类名 变量名) {
...
}
catch(异常类名 变量名) {
...
}
...
注意事项
能明确的尽量明确,不要用大的来处理
平级关系的异常谁前谁后无所谓,如果出现了子父关系,父必须在后面
注意:
一但try里面出了问题,就会在这里把问题给抛出去,然后和catch里面的问题进行匹配
一但有匹配的,就执行catch里面的处理,然后结束了try...catch
继续执行后面的语句
public class ExceptionDemo2 {
public static void main(String[] args) {
// method1();
// method2();
// method3();
method4();
}
public static void method4() {
int a = 10;
int b = 0;
int[] arr = { 1, 2, 3 };
// Exception在最后
try {
System.out.println(a / b);
System.out.println(arr[3]);
System.out.println("这里出现了一个异常,你不太清楚是谁,该怎么办呢?");
} catch (ArithmeticException e) {
System.out.println("除数不能为0");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("你访问了不该的访问的索引");
} catch (Exception e) {
System.out.println("出问题了");
}
// Exception在前面是不可以的
// try {
// System.out.println(a / b);
// System.out.println(arr[3]);
// System.out.println("这里出现了一个异常,你不太清楚是谁,该怎么办呢?");
// } catch (Exception e) {
// System.out.println("出问题了");
// } catch (ArithmeticException e) {
// System.out.println("除数不能为0");
// } catch (ArrayIndexOutOfBoundsException e) {
// System.out.println("你访问了不该的访问的索引");
// }
System.out.println("over");
}
// 两个异常的处理
public static void method3() {
int a = 10;
int b = 0;
int[] arr = { 1, 2, 3 };
try {
System.out.println(arr[3]);
System.out.println(a / b);
// System.out.println(arr[3]);
} catch (ArithmeticException e) {
System.out.println("除数不能为0");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("你访问了不该的访问的索引");
}
System.out.println("over");
}
// 两个异常
public static void method2() {
int a = 10;
int b = 0;
try {
System.out.println(a / b);
} catch (ArithmeticException e) {
System.out.println("除数不能为0");
}
int[] arr = { 1, 2, 3 };
try {
System.out.println(arr[3]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("你访问了不该的访问的索引");
}
System.out.println("over");
}
// 一个异常
public static void method1() {
// 第一阶段
int a = 10;
// int b = 2;
int b = 0;
try {
System.out.println(a / b);
} catch (ArithmeticException ae) {
System.out.println("除数不能为0");
}
// 第二阶段
System.out.println("over");
}
}
JDK7出现了一个新的异常处理方案
try{
...
}catch(异常名1 | 异常名2 | ... 变量 ) {
...
}
注意:这个方法虽然简洁,但是也不够好
处理方式是一致的(实际开发中,好多时候可能就是针对同类型的问题,给出同一个处理)
多个异常间必须是平级关系
编译时异常和运行时异常的区别
-编译期异常:Java程序必须显示处理,否则程序就会发生错误,无法通过编译
-运行期异常:无需显示处理,也可以和编译时异常一样处理
异常中要了解的几个方法
public String getMessage():异常的消息字符串
public String toString():返回异常的简单信息描述
此对象的类的 name(全路径名)
": "(冒号和一个空格)
调用此对象 getLocalizedMessage()方法的结果 (默认返回的是getMessage()的内容)
printStackTrace() 获取异常类名和异常信息,以及异常出现在程序中的位置,返回值void,把信息输出在控制台
public class ExceptionDemo {
public static void main(String[] args) {
String s = "2014-11-20";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
Date d = sdf.parse(s); // 创建了一个ParseException对象,然后抛出去,和catch里面进行匹配
System.out.println(d);
} catch (ParseException e) { // ParseException e = new ParseException();
// ParseException
// e.printStackTrace();
// getMessage()
// System.out.println(e.getMessage());
// Unparseable date: "2014-11-20"
// toString()
// System.out.println(e.toString());
// java.text.ParseException: Unparseable date: "2014-11-20"
e.printStackTrace();
}
System.out.println("over");
}
}
抛出
格式
throws 异常类名
注意:这个格式必须跟在方法的括号后面,尽量不要在main方法上抛出异常
编译期异常抛出,将来调用者必须处理
运行期异常抛出,将来调用可以不用处理
public class ExceptionDemo {
public static void main(String[] args) {
System.out.println("今天天气很好");
try {
method();
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println("但是就是不该有雾霾");
method2();
}
// 运行期异常的抛出
public static void method2() throws ArithmeticException {
int a = 10;
int b = 0;
System.out.println(a / b);
}
// 编译期异常的抛出
// 在方法声明上抛出,是为了告诉调用者,你注意了,我有问题。
public static void method() throws ParseException {
String s = "2014-11-20";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse(s);
System.out.println(d);
}
}
throw
在功能方法内部出现某种情况,程序不能继续运行,需要进行跳转时,就用throw把异常对象抛出
throws和throw的区别
throws
用在方法声明后面,跟的是异常类名
可以跟多个异常类名,用逗号隔开
表示抛出异常,由该方法的调用者来处理
throws表示出现异常的一种可能性,并不一定会发生这些异常
throw
用在方法体内,跟的是异常对象名
只能抛出一个异常对象名
表示抛出异常,由方法体内的语句处理
throw则是抛出了异常,执行throw则一定抛出了某种异常
如何选择
原则:如果该功能内部可以将问题处理,用try,如果处理不了,交由调用者处理,这是用throws
区别:
后续程序需要继续运行就try
后续程序不需要继续运行就throws
public class ExceptionDemo {
public static void main(String[] args) {
// method();
try {
method2();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void method() {
int a = 10;
int b = 0;
if (b == 0) {
throw new ArithmeticException();
} else {
System.out.println(a / b);
}
}
public static void method2() throws Exception {
int a = 10;
int b = 0;
if (b == 0) {
throw new Exception();
} else {
System.out.println(a / b);
}
}
}
finally
被finally控制的语句体一定会执行
注意:如果在执行到finally之前jvm退出了(比如System.exit(0))就不能执行了
格式
try...catch...finally...
用于释放资源,在IO流操作和数据库操作中会见到
public class FinallyDemo {
public static void main(String[] args) {
String s = "2014-11-20";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = null;
try {
// System.out.println(10 / 0);
d = sdf.parse(s);
} catch (ParseException e) {
e.printStackTrace();
//System.exit(0);加了这个finally就不能执行了
} finally {
System.out.println("这里的代码是可以执行的");
}
System.out.println(d);
}
}
面试题
final,finally和finalize的区别
final:最终的意思,可以修饰类,成员变量,成员方法
修饰类,类不能被继承
修饰变量,变量是常量
修饰方法,方法不能被重写
finally:是异常处理的一部分,用于释放资源
一般来说,代码肯定会执行,特殊情况:在执行到finally之前jvm退出了
finalize:是Object类的一个方法,用于垃圾回收
如果catch里面有return语句,请问finally里面的代码还会执行吗,如果会,请问是在return前,还是return后
会,前,准确的说,应该是在中间
try...catch...finally的格式变形
try...catch...finally
try...catch
try...catch...catch...
try...catch...catch...finally
try...finally,这种做法的目前是为了释放资源
自定义异常
要想你的类是一个异常类,就必须继承自Exception或者RuntimeException
两种方式:
继承Exception
继承RuntimeException
//异常类
public class MyException extends Exception {
public MyException() {
}
public MyException(String message) {
super(message);//用于给出消息提示
}
}
public class Teacher {
public void check(int score) throws MyException {
if (score > 100 || score < 0) {
throw new MyException("分数必须在0-100之间");
} else {
System.out.println("分数没有问题");
}
}
/*
* 自定义异常测试类
*/
public class StudentDemo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入学生成绩:");
int score = sc.nextInt();
Teacher t = new Teacher();
try {
t.check(score);
} catch (MyException e) {
e.printStackTrace();
}
}
}
异常注意事项
子类重写父类方法时,子类的方法必须抛出相同的异常或父类异常的子类
如果父类抛出了多个异常,子类重写父类时,只能抛出相同的异常或者是他的子集,子类不能抛出父类没有的异常
如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内有异常发生,那么子类只能try,不能throws
File类
我们要想实现IO的操作,就必须知道硬盘上文件的表现形式,而Java就提供了一个类File供我们使用
File:文件和目录(文件夹)路径名的抽象表示形式
构造方法:
File(String pathname):根据一个路径得到File对象
File(String parent, String child):根据一个目录和一个子文件/目录得到File对象
File(File parent, String child):根据一个父File对象和一个子文件/目录得到File对象
public class FileDemo {
public static void main(String[] args) {
// File(String pathname):根据一个路径得到File对象
// 把e:\\demo\\a.txt封装成一个File对象
File file = new File("E:\\demo\\a.txt");
// File(String parent, String child):根据一个目录和一个子文件/目录得到File对象
File file2 = new File("E:\\demo", "a.txt");
// File(File parent, String child):根据一个父File对象和一个子文件/目录得到File对象
File file3 = new File("e:\\demo");
File file4 = new File(file3, "a.txt");
// 以上三种方式其实效果一样
}
}
File类的创建功能
public boolean createNewFile():创建文件返回true 如果存在这样的文件,就不创建了返回false
public boolean mkdir():创建文件夹返回true 如果存在这样的文件夹,就不创建了返回false
public boolean mkdirs():创建文件夹,如果父文件夹不存在,会帮你创建出来,返回true ,如果存在这样的文件夹,就不创建了返回false
注意:确定好要创建文件还是文件夹,mkdirs虽然方便,但是只能创建文件夹,例File file = new File("e:\aaa\a.txt"); System.out.println("mkdirs:" + file.mkdirs());会创建e盘的aaa文件夹以及aaa文件夹下的a.txt文件夹,只有createNewFile()是创建文件的
如果创建文件或者文件夹时没有加盘符,不会报错,默认在当前项目路径下
public class FileDemo {
public static void main(String[] args) throws IOException {
// 需求:我要在e盘目录下创建一个文件夹demo
File file = new File("e:\\demo");
System.out.println("mkdir:" + file.mkdir());
// 需求:我要在e盘目录demo下创建一个文件a.txt
File file2 = new File("e:\\demo\\a.txt");
System.out.println("createNewFile:" + file2.createNewFile());
// 需求:我要在e盘目录test下创建一个文件b.txt
// Exception in thread "main" java.io.IOException: 系统找不到指定的路径。
// 注意:要想在某个目录下创建内容,该目录首先必须存在。
// File file3 = new File("e:\\test\\b.txt");
// System.out.println("createNewFile:" + file3.createNewFile());
// 需求:我要在e盘目录test下创建aaa目录
// File file4 = new File("e:\\test\\aaa");
// System.out.println("mkdir:" + file4.mkdir());
// File file5 = new File("e:\\test");
// File file6 = new File("e:\\test\\aaa");
// System.out.println("mkdir:" + file5.mkdir());
// System.out.println("mkdir:" + file6.mkdir());
// 其实我们有更简单的方法
File file7 = new File("e:\\aaa\\bbb\\ccc\\ddd");
System.out.println("mkdirs:" + file7.mkdirs());
// 看下面的这个东西:
File file8 = new File("e:\\aaa\\a.txt");
System.out.println("mkdirs:" + file8.mkdirs());
}
}
File类的删除功能
public boolean delete(),删除文件或文件夹,逐级删除
注意:
如果你创建文件或者文件夹忘了写盘符路径,那么,默认在项目路径下
Java中的删除不走回收站
要删除一个文件夹,请注意该文件夹内不能包含文件或者文件夹
public class FileDemo {
public static void main(String[] args) throws IOException {
// 创建文件
// File file = new File("e:\\a.txt");
// System.out.println("createNewFile:" + file.createNewFile());
// 我不小心写成这个样子了
File file = new File("a.txt");
System.out.println("createNewFile:" + file.createNewFile());
// 继续玩几个
File file2 = new File("aaa\\bbb\\ccc");
System.out.println("mkdirs:" + file2.mkdirs());
// 删除功能:我要删除a.txt这个文件
File file3 = new File("a.txt");
System.out.println("delete:" + file3.delete());
// 删除功能:我要删除ccc这个文件夹
File file4 = new File("aaa\\bbb\\ccc");
System.out.println("delete:" + file4.delete());
// 删除功能:我要删除aaa文件夹
// File file5 = new File("aaa");
// System.out.println("delete:" + file5.delete());
File file6 = new File("aaa\\bbb");
File file7 = new File("aaa");
System.out.println("delete:" + file6.delete());
System.out.println("delete:" + file7.delete());
}
}
File类的重命名功能
public boolean renameTo(File dest)
如果路径名相同,就是改名
如果路径名不同,就是改名并剪切
路径以盘符开始:绝对路径 c:\a.txt
路径不以盘符开始:相对路径 a.txt
public class FileDemo {
public static void main(String[] args) {
// 创建一个文件对象
// File file = new File("张三.jpg");
// // 需求:我要修改这个文件的名称为"李四.jpg"
// File newFile = new File("东方不败.jpg");
// System.out.println("renameTo:" + file.renameTo(newFile));
File file2 = new File("李四.jpg");
File newFile2 = new File("e:\\张三.jpg");
System.out.println("renameTo:" + file2.renameTo(newFile2));
}
}
File判断功能
public boolean isDirectory():判断是否是目录
public boolean isFile():判断是否是文件
public boolean exists():判断是否存在
public boolean canRead():判断是否可读
public boolean canWrite():判断是否可写
public boolean isHidden():判断是否隐藏
public class FileDemo {
public static void main(String[] args) {
// 创建文件对象
File file = new File("a.txt");
System.out.println("isDirectory:" + file.isDirectory());// false
System.out.println("isFile:" + file.isFile());// true
System.out.println("exists:" + file.exists());// true
System.out.println("canRead:" + file.canRead());// true
System.out.println("canWrite:" + file.canWrite());// true
System.out.println("isHidden:" + file.isHidden());// false
}
}
File类获取功能
public String getAbsolutePath():获取绝对路径
public String getPath():获取相对路径
public String getName():获取名称
public long length():获取长度,字节数
public long lastModified():获取最后一次的修改时间,毫秒值
public String[] list():获取指定目录下的所有文件或者文件夹的名称数组
public File[] listFiles():获取指定目录下的所有文件或者文件夹的File数组,返回的是File对象,可以调用File类的其他方法
public class FileDemo {
public static void main(String[] args) {
// 创建文件对象
File file = new File("demo\\test.txt");
System.out.println("getAbsolutePath:" + file.getAbsolutePath());
System.out.println("getPath:" + file.getPath());
System.out.println("getName:" + file.getName());
System.out.println("length:" + file.length());
System.out.println("lastModified:" + file.lastModified());
// 1416471971031,将毫秒数转换为时间
Date d = new Date(1416471971031L);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String s = sdf.format(d);
System.out.println(s);
}
}
public class FileDemo {
public static void main(String[] args) {
// 指定一个目录
File file = new File("e:\\");
// public String[] list():获取指定目录下的所有文件或者文件夹的名称数组
String[] strArray = file.list();
for (String s : strArray) {
System.out.println(s);
}
System.out.println("------------");
// public File[] listFiles():获取指定目录下的所有文件或者文件夹的File数组
File[] fileArray = file.listFiles();
for (File f : fileArray) {
System.out.println(f.getName());
}
}
}
判断E盘目录下是否有后缀名为.jpg的文件,如果有,就输出此文件名称
* 分析:
* A:封装e判断目录
* B:获取该目录下所有文件或者文件夹的File数组
* C:遍历该File数组,得到每一个File对象,然后判断
* D:是否是文件
* 是:继续判断是否以.jpg结尾
* 是:就输出该文件名称
* 否:不管
* 否:不管
*/
public class FileDemo {
public static void main(String[] args) {
// 封装e判断目录
File file = new File("e:\\");
// 获取该目录下所有文件或者文件夹的File数组
File[] fileArray = file.listFiles();
// 遍历该File数组,得到每一个File对象,然后判断
for (File f : fileArray) {
// 是否是文件
if (f.isFile()) {
// 继续判断是否以.jpg结尾
if (f.getName().endsWith(".jpg")) {
// 就输出该文件名称
System.out.println(f.getName());
}
}
}
}
}
文件名称过滤器
public String[] list(FilenameFilter filter)
public File[] listFiles(FilenameFilter filter)
/*
* 判断E盘目录下是否有后缀名为.jpg的文件,如果有,就输出此文件名称
* 先获取所有的,然后遍历的时候,依次判断,如果满足条件就输出(上面 FileDemo做的)
* 获取的时候就已经是满足条件的了,然后输出即可
*
* 要想实现这个效果,就必须学习一个接口:文件名称过滤器
* public String[] list(FilenameFilter filter)
* public File[] listFiles(FilenameFilter filter)
*/
public class FileDemo2 {
public static void main(String[] args) {
// 封装e判断目录
File file = new File("e:\\");
// 获取该目录下所有文件或者文件夹的String数组
// public String[] list(FilenameFilter filter)
String[] strArray = file.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
// return false;不输出
// return true;全部输出
// 通过这个测试,我们就知道了,到底把这个文件或者文件夹的名称加不加到数组中,取决于这里的返回值是true还是false
// 所以,这个的true或者false应该是我们通过某种判断得到的
// System.out.println(dir + "---" + name);
// File file = new File(dir, name);
// // System.out.println(file);
// boolean flag = file.isFile();判断你是否是文件,如果是就输出
// boolean flag2 = name.endsWith(".jpg");//判断是不是以jpg结尾
// return flag && flag2;
return new File(dir, name).isFile() && name.endsWith(".jpg");
}
});
// 遍历
for (String s : strArray) {
System.out.println(s);
}
}
}
/*
* 需求:把E:\评书\三国演义下面的视频名称修改为
* 00?_介绍.avi
*
* 思路:
* A:封装目录
* B:获取该目录下所有的文件的File数组
* C:遍历该File数组,得到每一个File对象
* D:拼接一个新的名称,然后重命名即可。
*/
public class FileDemo {
public static void main(String[] args) {
// 封装目录
File srcFolder = new File("E:\\评书\\三国演义");
// 获取该目录下所有的文件的File数组
File[] fileArray = srcFolder.listFiles();
// 遍历该File数组,得到每一个File对象
for (File file : fileArray) {
// System.out.println(file);
// E:\评书\三国演义\三国演义_001_[评书网-今天很高兴,明天就IO了]_桃园三结义.avi
// 改后:E:\评书\三国演义\001_桃园三结义.avi
String name = file.getName(); // 三国演义_001_[评书网-今天很高兴,明天就IO了]_桃园三结义.avi
int index = name.indexOf("_");
String numberString = name.substring(index + 1, index + 4);
// System.out.println(numberString);
// int startIndex = name.lastIndexOf('_');
// int endIndex = name.lastIndexOf('.');
// String nameString = name.substring(startIndex + 1, endIndex);
// System.out.println(nameString);
int endIndex = name.lastIndexOf('_');
String nameString = name.substring(endIndex);
String newName = numberString.concat(nameString); // 001_桃园三结义.avi
// System.out.println(newName);
File newFile = new File(srcFolder, newName); // E:\\评书\\三国演义\\001_桃园三结义.avi
// 重命名即可
file.renameTo(newFile);
}
}
}
Day20
递归
方法定义中调用方法本身的现象
注意事项:
递归一定要有出口,否则就是死递归
递归的次数不能太多,否则就内存溢出
构造方法不能递归使用
使用递归的条件
-做递归要写一个方法
返回值类型,参数列表
-出口条件
-规律
用递归实现5的阶乘
/*
* 做递归要写一个方法:
* 返回值类型:int
* 参数列表:int n
* 出口条件:
* if(n == 1) {return 1;}
* 规律:
* if(n != 1) {return n*方法名(n-1);}
*/
public static int jieCheng(int n){
if(n==1){
return 1;
}else {
return n*jieCheng(n-1);
}
}
}
有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问第十二个月的兔子对数为多少
/* 分析:我们要想办法找规律
* 兔子对数
* 第一个月: 1
* 第二个月: 1
* 第三个月: 2
* 第四个月: 3
* 第五个月: 5
* 第六个月: 8
* ...
*
* 由此可见兔子对象的数据是:
* 1,1,2,3,5,8...
* 规则:
* 从第三项开始,每一项是前两项之和
* 而且说明前两项是已知的
* 可以用,数组实现,递归实现
*/
public static void main(String[] args) {
// 定义一个数组
int[] arr = new int[20];
arr[0] = 1;
arr[1] = 1;
// arr[2] = arr[0] + arr[1];
// arr[3] = arr[1] + arr[2];
// ...
for (int x = 2; x < arr.length; x++) {
arr[x] = arr[x - 2] + arr[x - 1];
}
System.out.println(arr[19]);
/*
* 方法: 返回值类型:int 参数列表:int n 出口条件: 第一个月是1,第二个月是1 规律: 从第三个月开始,每一个月是前两个月之和
*/
public static int fib(int n) {
if (n == 1 || n == 2) {
return 1;
} else {
return fib(n - 1) + fib(n - 2);
}
}
输出固定位置下的所有java文件
/*需求:请大家把E:\JavaSE目录下所有的java结尾的文件的绝对路径给输出在控制台
*
* 分析:
* 封装目录
* 获取该目录下所有的文件或者文件夹的File数组
* 遍历该File数组,得到每一个File对象
* 判断该File对象是否是文件夹
* 是:回到第二步
* 否:继续判断是否以.java结尾
* 是:就输出该文件的绝对路径
* 否:不搭理它
*/
public class FilePathDemo {
public static void main(String[] args) {
// 封装目录
File srcFolder = new File("E:\\JavaSE");
// 递归功能实现
getAllJavaFilePaths(srcFolder);
}
private static void getAllJavaFilePaths(File srcFolder) {
// 获取该目录下所有的文件或者文件夹的File数组
File[] fileArray = srcFolder.listFiles();
// 遍历该File数组,得到每一个File对象
for (File file : fileArray) {
// 判断该File对象是否是文件夹
if (file.isDirectory()) {
getAllJavaFilePaths(file);
} else {
// 继续判断是否以.java结尾
if (file.getName().endsWith(".java")) {
// 就输出该文件的绝对路径
System.out.println(file.getAbsolutePath());
}
}
}
}
}
删除指定目录的所有文件
/*
* 需求:递归删除带内容的目录
*
* 目录我已经给定:demo
*
* 分析:
* 封装目录
* 获取该目录下的所有文件或者文件夹的File数组
* 遍历该File数组,得到每一个File对象
* 判断该File对象是否是文件夹
* 是:回到第二步
* 否:就删除
*/
public class FileDeleteDemo {
public static void main(String[] args) {
// 封装目录
File srcFolder = new File("demo");
// 递归实现
deleteFolder(srcFolder);
}
private static void deleteFolder(File srcFolder) {
// 获取该目录下的所有文件或者文件夹的File数组
File[] fileArray = srcFolder.listFiles();
if (fileArray != null) {
// 遍历该File数组,得到每一个File对象
for (File file : fileArray) {
// 判断该File对象是否是文件夹
if (file.isDirectory()) {
deleteFolder(file);
} else {
System.out.println(file.getName() + "---" + file.delete());
}
}
//删除文件夹
System.out.println(srcFolder.getName() + "---" + srcFolder.delete());
}
}
}
IO流
用来上传和下载文件
IO流的分类:
流向:
输入流 读取数据
输出流 写出数据
数据类型:
字节流
字节输入流 读取数据 InputStream
字节输出流 写出数据 OutputStream
字符流
字符输入流 读取数据 Reader
字符输出流 写出数据 Writer
注意:一般我们在探讨IO流的时候,如果没有明确说明按哪种分类来说,默认情况下是按照数据类型来分的。
OutputStream的子类
FileOutputStream
构造方法
FileOutputStream(File file)
FileOutputStream(String name)
FileOutputStream(String name,boolean append)创建一个向具有指定 name 的文件中写入数据的输出文件流。如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处
成员方法
public void write(int b):写一个字节
public void write(byte[] b):写一个字节数组
public void write(byte[] b,int off,int len):写一个字节数组的一部分,从off开始,写len个
字节输出流操作步骤:
创建字节输出流对象
写数据
释放资源
创建字节输出流对象
两种方式(效果相同)
FileOutputStream(File file)构造方法
File file = new File("fos.txt");
FileOutputStream fos = new FileOutputStream(file);
FileOutputStream(String name)构造方法
FileOutputStream fos = new FileOutputStream("fos.txt");
创建字节输出流对象做了那几件事
-调用系统功能去创建文件
-创建fos对象
-把fos对象指向这个文件
释放资源close()
关闭此文件输出流并释放与此流有关的所有系统资源
为什么一定要close()呢
让流对象变成垃圾,这样就可以被垃圾回收器回收了
通知系统去释放跟该文件相关的资源
public class FileOutputStreamDemo {
public static void main(String[] args) throws IOException {
// 创建字节输出流对象
// File file = new File("fos.txt");
// FileOutputStream fos = new FileOutputStream(file);
FileOutputStream fos = new FileOutputStream("fos.txt");
// 调用write()方法
//fos.write(97); //97 -- 底层二进制数据 -- 通过记事本打开 -- 找97对应的字符值 -- a
//public void write(byte[] b):写一个字节数组
byte[] bys={97,98,99,100,101};
fos.write(bys);
//public void write(byte[] b,int off,int len):写一个字节数组的一部分
fos.write(bys,1,3);
//释放资源
fos.close();
}
}
如何实现数据的换行
写入换行符号即可
不同的系统对换行符号识别是不一样
windows:\r\n
linux:\n
Mac:\r
而一些常见的个高级记事本,是可以识别任意换行符号的
public class FileOutputStreamDemo3 {
public static void main(String[] args) throws IOException {
// 创建字节输出流对象
// FileOutputStream fos = new FileOutputStream("fos.txt");
// 创建一个向具有指定 name 的文件中写入数据的输出文件流,如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处
//文件追加
FileOutputStream fos = new FileOutputStream("fos.txt", true);
// 写数据
for (int x = 0; x < 10; x++) {
fos.write(("hello" + x).getBytes());
//换行
fos.write("\r\n".getBytes());
}
// 释放资源
fos.close();
}
}
/*
* 加入异常处理的字节输出流操作
*/
public class FileOutputStreamDemo4 {
public static void main(String[] args) {
// 为了在finally里面能够看到该对象就必须定义到外面,为了访问不出问题,还必须给初始化值
FileOutputStream fos = null;
try {
// fos = new FileOutputStream("z:\\fos4.txt");
fos = new FileOutputStream("fos4.txt");
fos.write("java".getBytes());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 如果fos不是null,才需要close()
if (fos != null) {
// 为了保证close()一定会执行,就放到这里了
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
InputStream的子类
FileInputStream
字节输入流操作步骤:
创建字节输入流对象
调用read()方法读取数据,并把数据显示在控制台
释放资源
读取数据的方式:
int read():一次读取一个字节
int read(byte[] b):一次读取一个字节数组
public class FileInputStreamDemo {
public static void main(String[] args) throws IOException {
// FileInputStream(String name)
// FileInputStream fis = new FileInputStream("fis.txt");
FileInputStream fis = new FileInputStream("FileOutputStreamDemo.java");
// // 调用read()方法读取数据,并把数据显示在控制台
// // 第一次读取
// int by = fis.read();
// System.out.println(by);
// System.out.println((char) by);
//
// // 第二次读取
// by = fis.read();
// System.out.println(by);
// System.out.println((char) by);
//
// // 第三次读取
// by = fis.read();
// System.out.println(by);
// System.out.println((char) by);
// // 我们发现代码的重复度很高,所以我们要用循环改进
// // 而用循环,最麻烦的事情是如何控制循环判断条件呢?
// // 第四次读取
// by = fis.read();
// System.out.println(by);
// // 第五次读取
// by = fis.read();
// System.out.println(by);
// //通过测试,我们知道如果你读取的数据是-1,就说明已经读取到文件的末尾了
// 用循环改进
// int by = fis.read();
// while (by != -1) {
// System.out.print((char) by);
// by = fis.read();
// }
// 最终版代码
int by = 0;
// 读取,赋值,判断
while ((by = fis.read()) != -1) {
System.out.print((char) by);
}
// 释放资源
fis.close();
}
}
复制文件的字符
/*
* 复制文本文件
*
* 数据源:从哪里来
* a.txt -- 读取数据 -- FileInputStream
*
* 目的地:到哪里去
* b.txt -- 写数据 -- FileOutputStream
*
* java.io.FileNotFoundException: a.txt (系统找不到指定的文件)
*
* 这一次复制中文没有出现任何问题,为什么呢
* 上一次我们出现问题的原因在于我们每次获取到一个字节数据,就把该字节数据转换为了字符数据,然后输出到控制台
* 而这一次呢?确实通过IO流读取数据,写到文本文件,你读取一个字节,我就写入一个字节,你没有做任何的转换
* 它会自己做转换
*/
public class CopyFileDemo {
public static void main(String[] args) throws IOException {
// 封装数据源
FileInputStream fis = new FileInputStream("a.txt");
// 封装目的地
FileOutputStream fos = new FileOutputStream("b.txt");
int by = 0;
while ((by = fis.read()) != -1) {
fos.write(by);
}
// 释放资源(先关谁都行)
fos.close();
fis.close();
}
}
计算机是如何识别什么时候该把两个字节转换为一个中文呢
在计算机中中文的存储分两个字节:
第一个字节肯定是负数
第二个字节常见的是负数,可能有正数。但是没影响
把c盘下的a.txt的内容复制到d盘下的b.txt中
图片视频都可
/*
* 需求:把c盘下的a.txt的内容复制到d盘下的b.txt中
*
* 数据源:
* c:\\a.txt -- 读取数据-- FileInputStream
* 目的地:
* d:\\b.txt -- 写出数据 -- FileOutputStream
*/
public class CopyFileDemo2 {
public static void main(String[] args) throws IOException {
// 封装数据源
FileInputStream fis = new FileInputStream("c:\\a.txt");
// 封装目的地
FileOutputStream fos = new FileOutputStream("d:\\b.txt");
// 复制数据
int by = 0;
while ((by = fis.read()) != -1) {
fos.write(by);
}
// 释放资源
fos.close();
fis.close();
}
}
一次读取一个字节数组
/*
* 一次读取一个字节数组:int read(byte[] b)
* 返回值其实是实际读取的字节个数
*/
public class FileInputStreamDemo2 {
public static void main(String[] args) throws IOException {
// 创建字节输入流对象
// FileInputStream fis = new FileInputStream("fis2.txt");
FileInputStream fis = new FileInputStream("FileOutputStreamDemo.java");
// 读取数据
// 定义一个字节数组
// 第一次读取
// byte[] bys = new byte[5];
// int len = fis.read(bys);
// // System.out.println(len);
// // System.out.println(new String(bys));
// // System.out.println(new String(bys, 0, len));
// System.out.print(new String(bys, 0, len));
//
// // 第二次读取
// len = fis.read(bys);
// // System.out.println(len);
// // System.out.println(new String(bys));
// // System.out.println(new String(bys, 0, len));
// System.out.print(new String(bys, 0, len));
//
// // 第三次读取
// len = fis.read(bys);
// // System.out.println(len);
// // System.out.println(new String(bys));
// // System.out.println(new String(bys, 0, len));
// System.out.print(new String(bys, 0, len));
//
// // 第四次读取
// len = fis.read(bys);
// // System.out.println(len);
// // System.out.println(new String(bys, 0, len));
// System.out.print(new String(bys, 0, len));
// // 代码重复了,用循环改进
// // 但是,我不知道结束条件
// // len = fis.read(bys);
// // System.out.println(len);
// // len = fis.read(bys);
// // System.out.println(len);
// // 如果读取到的实际长度是-1,就说明没有数据了
// byte[] bys = new byte[115]; // 0
// int len = 0;
// while ((len = fis.read(bys)) != -1) {
// System.out.print(new String(bys, 0, len));
// // System.out.print(new String(bys)); //千万要带上len的使用
// }
// 最终版代码
// 数组的长度一般是1024或者1024的整数倍
byte[] bys = new byte[1024];
int len = 0;
while ((len = fis.read(bys)) != -1) {
System.out.print(new String(bys, 0, len));
}
// 释放资源
fis.close();
}
}
需求:把c:\a.txt内容复制到d:\b.txt中,改进版
public class CopyFileDemo {
public static void main(String[] args) throws IOException {
// 封装数据源
FileInputStream fis = new FileInputStream("c:\\a.txt");
FileOutputStream fos = new FileOutputStream("d:\\b.txt");
// 复制数据
byte[] bys = new byte[1024];
int len = 0;
while ((len = fis.read(bys)) != -1) {
fos.write(bys, 0, len);
}
// 释放资源
fos.close();
fis.close();
}
}
缓冲区类(高效类)
写数据:BufferedOutputStream
读数据:BufferedInputStream
BufferedOutputStream(OutputStream out)
BufferedInputStream(InputStream in)
为什么不传递一个具体的文件或者文件路径,而是传递一个OutputStream对象呢?
原因很简单,字节缓冲区流仅仅提供缓冲区,为高效而设计的,但是呢,真正的读写操作还得靠基本的流对象实现
写数据
public class BufferedOutputStreamDemo {
public static void main(String[] args) throws IOException {
// BufferedOutputStream(OutputStream out)
// FileOutputStream fos = new FileOutputStream("bos.txt");
// BufferedOutputStream bos = new BufferedOutputStream(fos);
// 简单写法
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));
// 写数据
bos.write("hello".getBytes());
// 释放资源
bos.close();
}
}
读取数据
public class BufferedInputStreamDemo {
public static void main(String[] args) throws IOException {
// BufferedInputStream(InputStream in)
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
"bos.txt"));
// 读取数据,第一种方式
// int by = 0;
// while ((by = bis.read()) != -1) {
// System.out.print((char) by);
// }
// System.out.println("---------");
//第二种方式
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1) {
System.out.print(new String(bys, 0, len));
}
// 释放资源
bis.close();
}
}
//注意:虽然我们有两种方式可以读取,但是,请注意,这两种方式针对同一个对象在一个代码中只能使用一个
**四种方式比较**
基本字节流一次读写一个字节
基本字节流一次读写一个字节数组
高效字节流一次读写一个字节
高效字节流一次读写一个字节数组
/*
* 需求:把e:\\哥有老婆.mp4复制到当前项目目录下的copy.mp4中
*
* 字节流四种方式复制文件:
* 基本字节流一次读写一个字节: 共耗时:117235毫秒
* 基本字节流一次读写一个字节数组: 共耗时:156毫秒
* 高效字节流一次读写一个字节: 共耗时:1141毫秒
* 高效字节流一次读写一个字节数组: 共耗时:47毫秒
*/
public class CopyMp4Demo {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
// method1("e:\\哥有老婆.mp4", "copy1.mp4");
// method2("e:\\哥有老婆.mp4", "copy2.mp4");
// method3("e:\\哥有老婆.mp4", "copy3.mp4");
method4("e:\\哥有老婆.mp4", "copy4.mp4");
long end = System.currentTimeMillis();
System.out.println("共耗时:" + (end - start) + "毫秒");
}
// 高效字节流一次读写一个字节数组:
public static void method4(String srcString, String destString)
throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
srcString));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(destString));
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
}
bos.close();
bis.close();
}
// 高效字节流一次读写一个字节:
public static void method3(String srcString, String destString)
throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
srcString));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(destString));
int by = 0;
while ((by = bis.read()) != -1) {
bos.write(by);
}
bos.close();
bis.close();
}
// 基本字节流一次读写一个字节数组
public static void method2(String srcString, String destString)
throws IOException {
FileInputStream fis = new FileInputStream(srcString);
FileOutputStream fos = new FileOutputStream(destString);
byte[] bys = new byte[1024];
int len = 0;
while ((len = fis.read(bys)) != -1) {
fos.write(bys, 0, len);
}
fos.close();
fis.close();
}
// 基本字节流一次读写一个字节
public static void method1(String srcString, String destString)
throws IOException {
FileInputStream fis = new FileInputStream(srcString);
FileOutputStream fos = new FileOutputStream(destString);
int by = 0;
while ((by = fis.read()) != -1) {
fos.write(by);
}
fos.close();
fis.close();
}
}