30天搞定Java--day25
每日一考和复习
每日一考
- Map存储数据的特点是什么?并指明key,value,entry存储数据的特点
1.双列数据,key-value类型
2.key不可重复,无序,存放在Set中
value可重复,存放在Collection中
entry存储key-value,无序,不可重复,存放在Set中
- 描述HashMap的底层实现原理(jdk 8版)
1.开始创建回声明一个空的Node数组,插入第一个元素时会初始化容量为16
2.当一条链路大于8并且数组总容量大于64时会把链路调整为红黑树,总容量小于64会扩容一倍,重新插入值
3.当容量大于临界值且新插入的位置有元素的情况下,会扩容一倍
- Map中常用实现类有哪些?各自有什么特点?
1.HashMap,线程不安全,效率比较高
2.LinkedMap,可以按照插入的顺序迭代
3.Properties,常用作配置文件,key和value都是字符串
4.TreeMap,遍历时输出内容排序
- 如何遍历Map中的key-value对,代码实现
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()) {
Object obj = iterator1.next();
//entrySet集合中的元素都是entry
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() + "---->" + entry.getValue());
}
- Collection和Collections的区别?
Collection是一个接口,继承了iterable接口,包含一些List、Set的通用方法
Collections是一个工具类,包含集合和Map的一些常用方法
泛型
为什么要有泛型
- 集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,JDK1.5之后使用泛型来解决
- 把元素的类型设计成一个参数,这个类型参数叫做泛型
- 所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数,也称为类型实参)
在集合中使用泛型
① 集合接口或集合类在jdk5.0时都修改为带泛型的结构
② 在实例化集合类时,可以指明具体的泛型类型
③ 指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型
比如:add(E e) —>实例化以后:add(Integer e)
④ 注意点:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换
⑤ 如果实例化时,没有指明泛型的类型。默认类型为java.lang.Object类型
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(78);
list.add(87);
list.add(99);
list.add(65);
//编译时,就会进行类型检查,保证数据的安全
list.add("Tom");
//方式一:
for(Integer score : list){
//避免了强转操作
int stuScore = score;
System.out.println(stuScore);
}
//方式二:
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
int stuScore = iterator.next();
System.out.println(stuScore);
}
//jdk7新特性:类型推断
Map<String, Integer> map = new HashMap<>();
//泛型的嵌套
Set<Map.Entry<String, Integer>> entry = map.entrySet();
Iterator<Map.Entry<String, Integer>> iterator = entry.iterator();
自定义泛型结构
-
自定义泛型类
如果定义了泛型类,实例化没有指明类的泛型,则认为此泛型类型为Object类型
要求:如果定义了类是带泛型的,建议在实例化时要指明类的泛型
public class Order<T> {
String orderName;
int orderId;
//类的内部结构就可以使用类的泛型
T orderT;
public Order() {
//编译不通过
T[] arr = new T[10];
//编译通过
T[] arr = (T[]) new Object[10];
}
public Order(String orderName, int orderId, T orderT) {
this.orderName = orderName;
this.orderId = orderId;
this.orderT = orderT;
}
}
class Person<T> {
// 使用T类型定义变量
private T info;
// 使用T类型定义一般方法
public T getInfo() {
return info;
}
public void setInfo(T info) {
this.info = info;
}
// 使用T类型定义构造器
public Person() {
}
public Person(T info) {
this.info = info;
}
}
静态方法中不能使用类的泛型
异常类不能是泛型的
//泛型不同的引用不能相互赋值
ArrayList<String> list1 = null;
ArrayList<Integer> list2 = new ArrayList<Integer>();
//报错
list1 = list2;
-
泛型方法
泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系
换句话说,泛型方法所属的类是不是泛型类都没有关系
泛型方法,可以声明为静态的。原因:泛型参数是在调用方法时确定的。并非在实例化类时确定
public static <E> List<E> copyFromArrayToList(E[] arr) {
ArrayList<E> list = new ArrayList<>();
for (E e : arr) {
list.add(e);
}
return list;
}
泛型在继承上的体现
父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:
- 子类不保留父类的泛型:按需实现
①没有类型 擦除
②具体类型 - 子类保留父类的泛型:泛型子类
①全部保留
②部分保留
子类除了指定或保留父类的泛型,还可以增加自己的泛型
class Father<T1, T2> {
}
// 子类不保留父类的泛型
// 1)没有类型 擦除
class Son1 extends Father {// 等价于class Son extends Father<Object,Object>{
}
// 2)具体类型
class Son2 extends Father<Integer, String> {
}
// 子类保留父类的泛型
// 1)全部保留
class Son3<T1, T2> extends Father<T1, T2> {
}
// 2)部分保留
class Son4<T2> extends Father<Integer, T2> {
}
如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口,G<B>并不是G<A>的子类型
比如:String是Object的子类,但是List<String >并不是List<Object>的子类
public void testGenericAndSubClass() {
Person[] persons = null;
Man[] mans = null;
// 而 Person[] 是 Man[] 的父类.
persons = mans;
Person p = mans[0];
// 在泛型的集合上
List<Person> personList = null;
List<Man> manList = null;
// personList = manList;(报错)
}
通配符的使用
通配符:?
类A是类B的父类,G<A>和G<B>是没有关系的,二者共同的父类是:G<?>
List<Object> list1 = null;
List<String> list2 = null;
List<?> list = null;
//编译通过
list = list1;
list = list2;
List<String> list3 = new ArrayList<>();
list3.add("AA");
list3.add("BB");
list3.add("CC");
list = list3;
//添加(写入):对于List<?>就不能向其内部添加数据,除了添加null之外
//报错
list.add("DD");
list.add(null);
//获取(读取):允许读取数据,读取的数据类型为Object。
Object o = list.get(0);
System.out.println(o);
}
- 有限制条件的通配符的使用
? extends A:
G<? extends A> 可以作为G<A>和G<B>的父类,其中B是A的子类
? super A:
G<? super A> 可以作为G<A>和G<B>的父类,其中B是A的父类
//Student是Person的子类
List<? extends Person> list1 = null;
List<? super Person> list2 = null;
List<Student> list3 = new ArrayList<Student>();
List<Person> list4 = new ArrayList<Person>();
List<Object> list5 = new ArrayList<Object>();
list1 = list3;
list1 = list4;
//报错
//list1 = list5;
//报错
//list2 = list3;
list2 = list4;
list2 = list5;
//读取数据:
list1 = list3;
Person p = list1.get(0);
//编译不通过
//Student s = list1.get(0);
list2 = list4;
Object obj = list2.get(0);
////编译不通过
//Person obj = list2.get(0);
//写入数据:
//编译不通过
//list1.add(new Student());
//编译通过
list2.add(new Person());
list2.add(new Student());
IO流
File类
File类的使用
- File类的一个对象,代表一个文件或一个文件目录(俗称:文件夹)
- File类声明在java.io包下
- File类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法,并未涉及到写入或读取文件内容的操作。如果需要读取或写入文件内容,必须使用IO流来完成
- 后续File类的对象常会作为参数传递到流的构造器中,指明读取或写入的"终点"
创建File类的实例
相对路径:相较于某个路径下,指明的路径。
绝对路径:包含盘符在内的文件或文件目录的路径
路径分隔符:① windows:\\ ② unix:/
//File类的三种构造器
//File(String filePath)
//File(String parentPath,String childPath)
//File(File parentFile,String childPath)
//构造器1
File file1 = new File("hello.txt");//相对于当前module
File file2 = new File("D:\\workspace_idea\\test\\he.txt");
System.out.println(file1);
System.out.println(file2);
//构造器2:
File file3 = new File("D:\\workspace_idea", "Java");
System.out.println(file3);
//构造器3:
File file4 = new File(file3, "hi.txt");
System.out.println(file4);
常用方法
public String getAbsolutePath():获取绝对路径
public String getPath():获取路径
public String getName():获取名称
public String getParent():获取上层文件目录路径。若无,返回null
public long length():获取文件长度(即:字节数)。不能获取目录的长度。
public long lastModified():获取最后一次的修改时间,毫秒值
File file1 = new File("hello.txt");
System.out.println(file1.getAbsolutePath());
System.out.println(file1.getPath());
System.out.println(file1.getName());
System.out.println(file1.getParent());
System.out.println(file1.length());
System.out.println(new Date(file1.lastModified()));
如下的两个方法适用于文件目录:
public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组
public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组
File file = new File("D:\\workspace_idea\\Java");
String[] list = file.list();
for (String s : list) {
System.out.println(s);
}
System.out.println();
File[] files = file.listFiles();
for (File f : files) {
System.out.println(f);
}
public boolean renameTo(File dest):把文件重命名为指定的文件路径
比如:file1.renameTo(file2)为例:
要想保证返回true,需要file1在硬盘中是存在的,且file2不能在硬盘中存在
File file1 = new File("hello.txt");
File file2 = new File("D:\\i\\hi.txt");
boolean renameTo = file2.renameTo(file1);
public boolean isDirectory():判断是否是文件目录
public boolean isFile():判断是否是文件
public boolean exists():判断是否存在
public boolean canRead():判断是否可读
public boolean canWrite():判断是否可写
public boolean isHidden():判断是否隐藏
File file1 = new File("hello.txt");
file1 = new File("hello1.txt");
System.out.println(file1.isDirectory());
System.out.println(file1.isFile());
System.out.println(file1.exists());
System.out.println(file1.canRead());
System.out.println(file1.canWrite());
System.out.println(file1.isHidden());
创建硬盘中对应的文件或文件目录
public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false
public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建。
public boolean mkdirs() :创建文件目录。如果此文件目录存在,就不创建了。如果上层文件目录不存在,一并创建
删除磁盘中的文件或文件目录
public boolean delete():删除文件或者文件夹
删除注意事项:Java中的删除不走回收站
//文件目录的创建
File file1 = new File("d:\\i\\i1\\i3");
boolean mkdir = file1.mkdir();
if (mkdir) {
System.out.println("创建成功1");
}
File file2 = new File("d:\\i\\i1\\i4");
boolean mkdir1 = file2.mkdirs();
if (mkdir1) {
System.out.println("创建成功2");
}
//要想删除成功,io4文件目录下不能有子目录或文件
File file3 = new File("D:\\i\\i1\\i4");
file3 = new File("D:\\i\\i1");
System.out.println(file3.delete());
}