泛型、IO流 和 网络编程
文章目录
- 🚏 泛型
- 🚀 泛型的概念
- 🚬 为什么要使用泛型
- 🚄 在集合中使用泛型
- 🚒 自定义泛型结构
- 🚬 泛型类
- 🚭 自定义泛型的子类1
- 🚭 自定义泛型的子类2
- 🚬 泛型接口
- 🚬 泛型方法
- 🚬 实际开发中使用泛型DAO
- 🚤 泛型在继承上的体现
- 🚗 通配符的使用
- 🚬 有限制条件的通配符的使用
- 🚏 IO流
- 🚀 File类的使用
- 🚬 File 类的使用
- 🚬 如何创建File类的实例
- 🚬 路径分隔符
- 🚬 File类的常用方法
- 🚭 File 类的获取功能
- 🚭 File 类的重命名功能
- 🚭 File 类的判断功能
- 🚭 File 类的创建功能
- 🚭 File 类的删除功能
- 🚭 File 类三个文件过滤器方法
- 🚬 练习:遍历指定目录所有文件名称,包括子文件目录中的文件。
😹 作者: gh-xiaohe
😻 gh-xiaohe的博客
😽 觉得博主文章写的不错的话,希望大家三连(✌关注,✌点赞,✌评论),多多支持一下!!!
🚏 泛型
泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换
🚀 泛型的概念
所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实际的类型参数,也称为类型实参)。
🚬 为什么要使用泛型
问题一:类型不安全
问题二:强转时,可能出现ClassCastException
🚄 在集合中使用泛型
- 总结:
- ① 集合接口或集合类在jdk5.0时都修改为带泛型的结构。
- ② 在实例化集合类时,可以指明具体的泛型类型
- ③ 指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如:方法、构造器、属性等)使用到类的泛型的位置,都指定为实例化的泛型类型。
- 比如:add(E e) —>实例化以后:add(Integer e)
- ④ 注意点:泛型的类型必须是类,不能是基本数据类型。需要使用到基本数据类型的位置,拿包装类替换
- ⑤ 如果实例化时,没有指明泛型的类型。默认类型为java.lang.Object类型。
未使用泛型
//在集合中使用泛型之前的情况:
@Test
public void test1(){
ArrayList list = new ArrayList();
//需求:存放学生的成绩
list.add(78);
list.add(76);
list.add(89);
list.add(88);
//问题一:类型不安全
// list.add("Tom");
for(Object score : list){
//问题二:强转时,可能出现ClassCastException
int stuScore = (Integer) score;
// int stuScore = (int) score;也可这样
System.out.println(stuScore);
}
}
在集合中使用泛型的情况:以ArrayList为例
//在集合中使用泛型的情况:以ArrayList为例
@Test
public void test2(){
//问什么不能写int:泛型的类型必须是类,不能是基本数据类型。需要用到基本数据类型的位置,拿包装类替换
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);
}
}
在集合中使用泛型的情况:以HashMap为例
@Test
public void test3(){
// Map<String,Integer> map = new HashMap<String,Integer>();
//jdk7新特性:类型推断
Map<String,Integer> map = new HashMap<>();
map.put("Tom",87);
map.put("Jerry",87);
map.put("Jack",67);
// map.put(123,"ABC");
//泛型的嵌套
Set<Map.Entry<String,Integer>> entry = map.entrySet();
Iterator<Map.Entry<String, Integer>> iterator = entry.iterator();
while(iterator.hasNext()){
Map.Entry<String, Integer> e = iterator.next();
String key = e.getKey();
Integer value = e.getValue();
System.out.println(key + "----" + value);
}
}
🚒 自定义泛型结构
如何自定义泛型结构:泛型类、泛型接口;泛型方法。
🚬 泛型类
public class Order<T> {
String orderName;
int orderId;
//类的内部结构就可以使用类的泛型
T orderT;
public Order(){
}
public Order(String orderName,int orderId,T orderT){
this.orderName = orderName;
this.orderId = orderId;
this.orderT = orderT;
}
@Override
public String toString() {
return "Order{" +
"orderName='" + orderName + '\'' +
", orderId=" + orderId +
", orderT=" + orderT +
'}';
}
}
@Test
public void test1(){
//如果定义了泛型类,实例化没有指明类的泛型,则认为此泛型类型为Object类型
//要求:如果大家定义了类是带泛型的,建议在实例化时要指明类的泛型。
Order order = new _Order();
order.setOrderT(123);
order.setOrderT("ABC");
//建议:实例化时指明类的泛型
Order<String> order1 = new Order<String>("orderAA",1001,"order:AA");
order1.setOrderT("AA:hello");
}
🚭 自定义泛型的子类1
由于子类在继承带泛型的父类时,指明了泛型类型。则实例化子类对象时,不再需要指明泛型。
SubOrder1:不是泛型类
public class SubOrder1 extends Order<Integer> { // SubOrder1:不是泛型类
}
@Test
public void test2(){
SubOrder1 sub1 = new SubOrder1();
//由于子类在继承带泛型的父类时,指明了泛型类型。则实例化子类对象时,不再需要指明泛型。
sub1.setOrderT(1122);
SubOrder2<String> sub2 = new SubOrder2<>();
sub2.setOrderT("order2...");
}
🚭 自定义泛型的子类2
SubOrder2: 仍然是泛型类
public class SubOrder2<T> extends Order<T> { // SubOrder2<T>:仍然是泛型类
}
@Test
public void test2(){
SubOrder2<String> sub2 = new SubOrder2<>();
sub2.setOrderT("order2...");
}
🚬 泛型接口
//1.接口中静态成员也不能使用泛型
//2.泛型接口的类型,在继承接口或者实现接口时确定
interface IUsb<U,R>{
int n = 10;
//U name; //name类型默认为 public static final,所以不能使用
//普通方法中可以使用接口泛型
R get(U u);
void hi(R r);
void run(R r1,R r2,U u1,U u2);
//在JDK 8中,可以在接口中,使用默认方法
default R method(U u){
return null;
}
}
//实现接口时,指定泛型类型
class BB implements IUsb<Integer,Float>{
@Override
public Float get(Integer integer) {
return null;
}
@Override
public void hi(Float aFloat) {
}
@Override
public void run(Float r1, Float r2, Integer u1, Integer u2) {
}
}
//继承接口时,指定泛型类型
interface IA extends IUsb<String,Double>{
}
class AA implements IA{
@Override
public Double get(String s) {
return null;
}
@Override
public void hi(Double aDouble) {
}
@Override
public void run(Double r1, Double r2, String u1, String u2) {
}
}
🚬 泛型方法
方法,也可以被泛型化,不管此时定义在其中的类是不是泛型类。 在泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型
泛型方法的格式:
- [ 访问权限] < 泛型> 型 返回类型 名 方法名([ 泛型标识 称 参数名称]) 抛出的异常
- 泛型方法,可以声明为静态的。原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。
错误写法:
- public static List copyFromArrayToList(E[] arr){ 编译器会认为有一个类叫E,而不是泛型
public class Order<T> {
// public static List<E> copyFromArrayToList(E[] arr){
// 错误写法:编译器会认为有一个类叫E,而不是泛型
public static <E> List<E> copyFromArrayToList(E[] arr){
ArrayList<E> list = new ArrayList<>();
for(E e : arr){
list.add(e);
}
return list;
}
}
//测试泛型方法
@Test
public void test4(){
Order<String> order = new Order<>();
Integer[] arr = new Integer[]{1,2,3,4};
//泛型方法在调用时,指明泛型参数的类型。
List<Integer> list = order.copyFromArrayToList(arr);
System.out.println(list);
}
🚬 实际开发中使用泛型DAO
DAO:data(base) access object DAO:可以理解为操作数据库的通用操作
DAO
public class DAO<T> {//表的共性操作的DAO
//添加一条记录
public void add(T t){
}
//删除一条记录
public boolean remove(int index){
return false;
}
//修改一条记录
public void update(int index,T t){
}
//查询一条记录
public T getIndex(int index){
return null;
}
//查询多条记录
public List<T> getForList(int index){
return null;
}
//泛型方法
//举例:获取表中一共有多少条记录?获取最大的员工入职时间?
public <E> E getValue(){
return null;
}
}
Customer 和 CustomerDAO 测试
public class Customer { //此类对应数据库中的customers表
}
public class CustomerDAO extends DAO<Customer>{//只能操作某一个表的DAO
}
public class Student {
}
public class StudentDAO extends DAO<Student> {//只能操作某一个表的DAO
}
@Test
public void test1(){
CustomerDAO dao1 = new CustomerDAO();
dao1.add(new Customer());
List<Customer> list = dao1.getForList(10);//查询多条记录
StudentDAO dao2 = new StudentDAO();
Student student = dao2.getIndex(1);//查询一条记录
}
🚤 泛型在继承上的体现
虽然类A是类B的父类,但是G 和G二者不具备子父类关系,二者是并列关系。
@Test public void test1(){ // 类层面的展示 Object obj = null; String str = null; obj = str; // 子类赋给父类的引用 多态 // 数组层面 Object[] arr1 = null; String[] arr2 = null; arr1 = arr2; } @Test public void test1(){ //此时的list1和list2的类型不具有子父类关系 List<Object> list1 = null; List<String> list2 = null; //编译不通过 list1 = list2; // 错误 // 类似于 错误 Date date = new Date(); String str = null; str = date; // 错误 /* 反证法: 假设list1 = list2; list1.add(123);导致混入非String的数据。出错。 所以是错误的 */ }
补充:类A是类B的父类,A
是 B 的父类 @Test public void test2(){ AbstractList<String> list1 = null; List<String> list2 = null; // List interface(接口) ArrayList<String> list3 = null; // 继承 AbstractList list1 = list3; list2 = list3; List<String> list4 = new ArrayList<>(); }
🚗 通配符的使用
通配符:?
- 类A是类B的父类,G和G是没有关系的,二者共同的父类是:G<?>
@Test
public void test3(){
List<Object> list1 = null;
List<String> list2 = null;
List<?> list = null; // 相当于 List<?> 作为 List<Object>、List<String> 的通用父类
list = list1;
list = list2;
//编译通过
print(list1);
print(list2);
}
public void print(List<?> list){
Iterator<?> iterator = list.iterator();
while(iterator.hasNext()){
Object obj = iterator.next();
System.out.println(obj);
}
}
使用通配符结构时
添加(写入):对于List<?>就不能向其内部添加数据,只能添加null(无意义)
获取(读取):允许读取数据,读取的数据类型为Object。
@Test
public void test3(){
List<String> list3 = new ArrayList<>();
list3.add("AA");
list3.add("BB");
list3.add("CC");
List<?> list = null;
list = list3;
//添加(写入):对于List<?>就不能向其内部添加数据。
//list.add(capture of ? e);
// list.add("DD"); 错误
// list.add('?'); 错误
//除了添加null之外。
list.add(null);
//获取(读取):允许读取数据,读取的数据类型为Object。
Object o = list.get(0);
System.out.println(o); // AA
}
🚬 有限制条件的通配符的使用
- ? extends A: 理解为小于等于
- G<? extends A> 可以作为G和G的父类,其中B是A的子类
- ? super A: 理解为大于等于
- G<? super A> 可以作为G和G的父类,其中B是A的父类
@Test
public void test4(){
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;
System.out.println("---------------------");
//读取数据:
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()); 子类可以赋给父类 父类不可以赋给子类
// list1.add(new Person());
// list1.add(new Object());
//编译通过
list2.add(new Person()); // Person 和 Person的子类能存放 多态
list2.add(new Student());
}
🚏 IO流
🚀 File类的使用
🚬 File 类的使用
- java.io.File类:文件和文件目录路径的抽象表示形式,与平台无关
- File 能新建、删除、重命名文件和目录,但 File 不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流。
- 想要在Java程序中表示一个真实存在的文件或目录,那么必须有一个File对象,但是Java程序中的一个File对象,可能没有一个真实存在的文件或目录。
- File对象可以作为参数传递给流的构造器
- 后续File类的对象常会作为参数传递到流的构造器中,指明读取或写入的"终点"
🚬 如何创建File类的实例
- 调用:构造器
- File(String filePath)以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。
- 绝对路径:是一个固定的路径,从盘符开始
- 相对路径:是相对于某个位置开始
- File(String parentPath,String childPath)以parent为父路径,child为子路径创建File对象.
- File(File parentFile,String childPath)根据一个父File对象和子文件路径创建File对象
- File(URI uri)通过将给定的 file: URI转换为抽象路径名来创建新的 File实例。
- File(String filePath)以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。
内存层面的对象,不需要硬盘中有这个文件
@Test
public void test1() {
// 内存层面的对象,不需要硬盘中有这个文件
//构造器1
File file1 = new File("hello1.txt");//相对于当前module
File file2 = new File("G:\\java存储文件\\_2java高级编程\\hello1.txt");
System.out.println(file1);
System.out.println(file2);
//构造器2: 上一层目录 文件(或者文件目录)
File file3 = new File("G:\\java\\java存储文件", "_2java高级编程");
System.out.println(file3);
//构造器3:
File file4 = new File(file3, "hi.txt");
System.out.println(file4);
}
🚬 路径分隔符
🚬 File类的常用方法
🚭 File 类的获取功能
- File 类的获取功能
- public String getAbsolutePath():获取绝对路径
- public String getPath():获取路径
- public String getName():获取名称
- public String getParent():获取上层文件目录路径。若无,返回null
- public long length():获取文件长度(即:字节数)。不能获取目录的长度。
- public long lastModified():获取最后一次的修改时间,毫秒值
- 适用于文件目录:
- public String[] list():获取指定目录下的所有文件或者文件目录的名称数组
- public File[] listFiles():获取指定目录下的所有文件或者文件目录的File数组
@Test
public void test2() {
System.out.println();
File file1 = new File("hello1.txt");//相对路径
File file2 = new File("G:\\java存储文件\\_2java高级编程\\hi.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()));
System.out.println();
System.out.println(file2.getAbsolutePath());
System.out.println(file2.getPath());
System.out.println(file2.getName());
System.out.println(file2.getParent());
System.out.println(file2.length());
System.out.println(file2.lastModified());
}
@Test
public void test3() {
File file = new File("G:\\java存储文件\\_2java高级编程\\_6IO流与网略编程"); //路径真实存在
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); // 目录下的所有文件目录
}
}
🚭 File 类的重命名功能
- File 类的重命名功能
- public boolean renameTo(File dest):把文件重命名为指定的文件路径
- 比如:file1.renameTo(file2)为例:
- 要想保证返回true,需要file1在硬盘中是存在的,且file2不能在硬盘中存在。 如果第一次保存成功了,再点击运行就回是false。
@Test
public void test4() {
File file1 = new File("hello1.txt");
File file2 = new File("E:\\java\\java存储文件\\_2java高级编程\\_6IO流\\_1File类的使用\\hi.txt");
boolean renameTo = file2.renameTo(file1);
System.out.println(renameTo);
}
🚭 File 类的判断功能
- File 类的判断功能
- public boolean isDirectory():判断是否是文件目录
- public boolean isFile():判断是否是文件
- public boolean exists():判断是否存在
- public boolean canRead():判断是否可读
- public boolean canWrite():判断是否可写
- public boolean isHidden():判断是否隐藏
@Test
public void test5() {
File 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());
System.out.println();
File file2 = new File("d:\\io"); // 文件不存在
System.out.println(file2.isDirectory());
System.out.println(file2.isFile());
System.out.println(file2.exists());
System.out.println(file2.canRead());
System.out.println(file2.canWrite());
System.out.println(file2.isHidden());
}
🚭 File 类的创建功能
- File 类的创建功能
- public boolean createNewFile():创建文件。若文件存在,则不创建,返回false
- public boolean mkdir():创建文件目录。如果此文件目录存在,就不创建了。此文件目录的上层目录不存在,也不创建。
- public boolean mkdirs():创建文件目录。如果上层文件目录不存在,一并创建
- 注意事项:如果你创建文件或者 文件 目录没有 写 盘符路径,那么,默认在项目路径下。
🚭 File 类的删除功能
- File 类的删除功能
- public boolean delete():删除文件或者文件夹
- 删除注意事项:
- Java中的删除不走回收站。
- 要删除一个文件目录,请注意该文件目录内不能包含文件或者文件目录
@Test
public void test6() throws IOException {
//文件的创建
File file1 = new File("hi.txt");
if (!file1.exists()) {
//文件的创建
file1.createNewFile();
System.out.println("创建成功");
} else {//文件存在
file1.delete();
System.out.println("删除成功");
}
}
@Test
public void test7() {
//文件目录的创建
File file1 = new File("d:\\io\\io1\\io3");
boolean mkdir = file1.mkdir();
if (mkdir) {
System.out.println("创建成功1");
}
// 文件目录上级 不存在 直接创建
File file2 = new File("d:\\io\\io1\\io4");
boolean mkdir1 = file2.mkdirs();
if (mkdir1) {
System.out.println("创建成功2");
}
//(文件目录的删除)要想删除成功,io4文件目录下不能有子目录或文件
File file3 = new File("D:\\io\\io1\\io4");
file3 = new File("D:\\io\\io1");
System.out.println(file3.delete());
}
🚭 File 类三个文件过滤器方法
- File 类文件过滤
- public String[] list(FilenameFilter filter):返回一个字符串数组,命名由此抽象路径名表示的目录中满足指定过滤器的文件和目录。
- public File[] listFiles(FileFilter filter):**返回一个抽象路径名数组,表示由此抽象路径名表示的满足指定过滤器的目录中的文件和目录。 **
- public File[] listFiles(FilenameFilter filter):**返回一个抽象路径名数组,表示由此抽象路径名表示的满足指定过滤器的目录中的文件和目录。 **
- FilenameFilter 测试指定的文件是否应该包含在文件列表中。
- 形参 :dir-找到文件的目录。名称–文件的名称。 存在ture
判断指定目录下是否有后缀名为.jpg的文件,如果有,就输出该文件名称
@Test
public void test1() {
File srcFile = new File("E:\\java\\io");
String[] fileNames = srcFile.list();
for (String fileName : fileNames) {
if (fileName.endsWith(".jpg")) {
System.out.println(fileName);
}
}
}
@Test
public void test2() {
File srcFile = new File("E:\\java\\io");
File[] listFiles = srcFile.listFiles();
for (File file : listFiles) {
if (file.getName().endsWith(".jpg")) {
System.out.println(file.getAbsolutePath());
}
}
}
/*
* File类提供了三个文件过滤器方法
* public String[] list(FilenameFilter filter)
* public File[] listFiles(FileFilter filter)
* public File[] listFiles(FilenameFilter filter)
*/
@Test
public void test3() {
File srcFile = new File("E:\\java\\io");
File[] subFiles = srcFile.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".jpg");
}
});
for (File file : subFiles) {
System.out.println(file.getAbsolutePath());
}
}
@Test
public void test4(){
File srcFile1 = new File("E:\\java\\io");
FilenameFilter name = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".jpg");
}
};
File[] files = srcFile1.listFiles(name);
for (File file : files){
System.out.println(file.getAbsolutePath());
}
}
🚬 练习:遍历指定目录所有文件名称,包括子文件目录中的文件。
拓展1:并计算指定目录占用空间的大小
拓展2:删除指定文件目录及其下的所有文件
递归
public class ListFilesTest {
public static void main(String[] args) {
// 递归:文件目录
/** 打印出指定目录所有文件名称,包括子文件目录中的文件 */
// 1.创建目录对象
File dir = new File("E:\\java\\io");
// 2.打印目录的子文件
printSubFile(dir);
System.out.println();
printSubFile(dir);
System.out.println();
listSubFiles(dir);
System.out.println();
listAllSubFiles(dir);
System.out.println();
//拓展1
System.out.println(getDirectorySize(dir));
System.out.println();
//拓展2
// deleteDirectory(dir);
}
public static void printSubFile(File dir) {
// 打印目录的子文件
File[] subfiles = dir.listFiles();
for (File f : subfiles) {
if (f.isDirectory()) {// 文件目录
printSubFile(f);
} else {// 文件
System.out.println(f.getAbsolutePath());
}
}
}
// 方式二:循环实现
// 列出file目录的下级内容,仅列出一级的话
// 使用File类的String[] list()比较简单
public static void listSubFiles(File file) {
if (file.isDirectory()) {
String[] all = file.list();
for (String s : all) {
System.out.println(s);
}
} else {
System.out.println(file + "是文件!");
}
}
// 列出file目录的下级,如果它的下级还是目录,接着列出下级的下级,依次类推
// 建议使用File类的File[] listFiles()
public static void listAllSubFiles(File file) {
if (file.isFile()) {
System.out.println(file);
} else {
File[] all = file.listFiles();
// 如果all[i]是文件,直接打印
// 如果all[i]是目录,接着再获取它的下一级
for (File f : all) {
listAllSubFiles(f);// 递归调用:自己调用自己就叫递归
}
}
}
// 拓展1:求指定目录所在空间的大小
// 求任意一个目录的总大小
public static long getDirectorySize(File file) {
// file是文件,那么直接返回file.length()
// file是目录,把它的下一级的所有大小加起来就是它的总大小
long size = 0;
if (file.isFile()) {
size += file.length();
} else {
File[] all = file.listFiles();// 获取file的下一级
// 累加all[i]的大小
for (File f : all) {
size += getDirectorySize(f);// f的大小;
}
}
return size;
}
// 拓展2:删除指定的目录
public static void deleteDirectory(File file) {
// 如果file是文件,直接delete
// 如果file是目录,先把它的下一级干掉,然后删除自己
if (file.isDirectory()) {
File[] all = file.listFiles();
// 循环删除的是file的下一级
for (File f : all) {// f代表file的每一个下级
deleteDirectory(f);
}
}
// 删除自己
file.delete();
}
}