JAVAEE工程师零基础学习教程之泛型类和File类

时间:2023-01-08 17:57:06

day15_JAVAOOP

课程目标

1. 【理解】什么是泛型
2. 【掌握】泛型的基本使用
3. 【理解】什么是Collections工具类
4. 【理解】什么是File类
5. 【掌握】File类的常用功能
6. 【掌握】打印多级目录案例
7. 【掌握】文件搜索案例

泛型

泛型概述

​ 在前面学习集合时,我们都知道集合中是可以存放任意对象的,只要把对象存储集合后,那么这时他们都会被提升成Object类型。当我们在取出每一个对象,并且进行相应的操作,这时必须采用类型转换。

大家观察下面代码:

public class GenericDemo {
    public static void main(String[] args) {        
        Collection coll = new ArrayList();
        coll.add("abc");
        coll.add("cba");
        coll.add(5);//由于集合没有做任何限定,任何类型都可以给其中存放
        
        Iterator it = coll.iterator();
        while(it.hasNext()){
            //需要打印每个字符串的长度,就要把迭代出来的对象转成String类型
            String str = (String) it.next();
            System.out.println(str.length());
        }
    }
}

程序在运行时发生了问题java.lang.ClassCastException。为什么会发生类型转换异常呢? 我们来分析下:由于集合中什么类型的元素都可以存储。导致取出时强转引发运行时 ClassCastException。

  • 怎么来解决这个问题呢?

    集合中可以存储各种对象,但实际上通常集合只存储同一类型对象。例如都是存储字符串对象。因此在JDK5之后,新增了泛型(Generic)语法,让你在设计API时可以指定类或方法支持泛型,这样我们使用API的时候也变得更为简洁,并得到了编译时期的语法检查。

  • 泛型:可以在类或方法中预支地使用未知的类型。

一般在创建对象时,将未知的类型确定具体的类型。当没有指定泛型时,默认类型为Object类型。

使用泛型的好处

泛型带来了哪些好处呢?

  • 将运行时期的ClassCastException,转移到了编译时期变成了编译失败。
  • 避免了类型强转的麻烦。

通过我们如下代码体验一下:

public class GenericDemo2 {
	public static void main(String[] args) {
        Collection<String> list = new ArrayList<String>();
        list.add("abc");
        list.add("cba");
        // list.add(5);//当集合明确类型后,存放类型不一致就会编译报错
        // 集合已经明确具体存放的元素类型,那么在使用迭代器的时候,迭代器也同样会知道具体遍历元素类型
        Iterator<String> it = list.iterator();
        while(it.hasNext()){
            String str = it.next();
            //当使用Iterator<String>控制元素类型后,就不需要强转了。获取到的元素直接就是String类型
            System.out.println(str.length());
        }
	}
}

tips:泛型是数据类型的一部分,我们将类名与泛型合并一起看做数据类型。

泛型的定义与使用

泛型:是一种把类型明确的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型。参数化类型,把类型当作参数一样的传递。

我们在集合中会大量使用到泛型,这里来完整地学习泛型知识。泛型,用来灵活地将数据类型应用到不同的类、方法、接口当中。将数据类型作为参数进行传递。

通常情况下,T,E,K,V,?是这样约定的:

  • ?表示不确定的 java 类型
  • T (type) 表示具体的一个java类型
  • K V (key value) 分别代表java键值中的Key Value
  • E (element) 代表Element

泛型类

  • 定义格式

    <数据类型> 此处的数据类型只能是引用类型,基本数据类型测试。

    好处:

    A:把运行时期的问题提前到了编译期间​								
    

    B:避免了强制类型转换

  • 自定义泛型类
    /*
     * 泛型类:把泛型定义在类上
     */
    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<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(27);
            Integer i = ot2.getObj();
            System.out.println("年龄是:" + i);
        }
    }
    

泛型方法

  • 定义格式

    修饰符 <代表泛型的变量> 返回值类型 方法名 (参数){ }

  • 定义泛型方法
    /*
     * 泛型方法:把泛型定义在方法上
     */
    public class ObjectTool {
        public <T> Object show(T t) {
            //System.out.println(t);
            return t;
        }    
         //定义一个泛型方法
        //返回类型不应该明确,因为泛型方法的类型都不明确
        //建议: Object 或者  T(泛型)
        public <T> T show(T t){
            System.out.println(t);
            return  t;
        }
    }
    
  • 泛型方法的使用

    调用方法时,确定泛型的类型

    public class ObjectToolDemo {
        public static void main(String[] args) {
            // 定义泛型方法后
            ObjectTool ot = new ObjectTool();
            String s = (String) ot.show("hello");
            Integer i = (Integer) ot.show(200);
            Boolean b = (Boolean) ot.show(true);
            System.out.println(s);
            System.out.println(i);
            System.out.println(b);
            
            ObjectToolMethod otm = new ObjectToolMethod();
            String aa = otm.show("aa");
            Integer show = otm.show(11);
            System.out.println(aa);
            System.out.println(show);
        }
    }
    

泛型接口

  • 定义格式

    修饰符 interface接口名<代表泛型的变量> { }

  • 定义泛型接口
    /*
     * 泛型接口:把泛型定义在接口上
     */
    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) {
          //第一种情况:实现类已经明确类型,实例化对象时必须与实现类中的类型一致
            InterDemo<String> i = new InteImpl();//我在实现的时候,已经明确类型--String
            i.show("aaa");
            i.show("bbb");
          
            //第二种情况:实现类也没有明确类型
            InterDemo<Integer> ii = new InteImpl2<>();//我在实现的时间也没有给出明确
            ii.show(11);
            ii.show(22);
    
            InterDemo<String> ii2 = new InteImpl2<>();//我在实现的时间也没有给出明确
            ii2.show("11");
            ii2.show("22");
        }
    }
    

泛型通配符(了解)

什么是泛型通配符

​ 当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。

通配符基本使用

泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。

此时只能接受数据,不能往该集合中存储数据。

通配符高级使用

/*
 * 泛型高级(通配符)
 * ?:任意类型,如果没有明确,那么就是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及其子类,表示包括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极其父类,表示包括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>();
	}
}
public class Animal {
}
public class Dog extends Animal {
}
public class Cat extends Animal {
}

静态导入-了解

/*
 * 静态导入:
 * 格式:import static 包名….类名.方法名;
 * 可以直接导入到方法的级别
 *
 * 静态导入的注意事项:
 * 		A:方法必须是静态的
 * 		B:如果有多个同名的静态方法,容易不知道使用谁?这个时候要使用,必须加前缀。由此可见,意义不大,所以一般不用,但是要能看懂。
 */
import static java.lang.Math.abs;
import static java.lang.Math.pow;
import static java.lang.Math.max;
public class StaticImportDemo {
    public static void main(String[] args) {
        // System.out.println(java.lang.Math.abs(-100));
        // System.out.println(java.lang.Math.pow(2, 3));
        // System.out.println(java.lang.Math.max(20, 30));
        // 太复杂,我们就引入到import

        // System.out.println(Math.abs(-100));
        // System.out.println(Math.pow(2, 3));
        // System.out.println(Math.max(20, 30));
        // 太复杂,有更简单

        System.out.println(abs(-100));
        System.out.println(pow(2, 3));
        System.out.println(max(20, 30));
    }
}

可变参

        //可变参数(T...a)
        Arrays.asList("hello","world","yiyan", 123, true);
        //注意事项
        List<String> list1 = Arrays.asList("hello", "world", "java");
        // list1.add("jack");//本质还是数组长度,类型不能改变
        // list1.add("rose");
        //可以修改元素
        list1.set(1,"rose");
        System.out.println(list1);

数组和集合互转

/*
 * public static <T> List<T> asList(T... a):把数组转成集合
 * 注意事项:
 * 		虽然可以把数组转成集合,但是集合的长度不能改变。
 */
public class ArraysDemo {
    public static void main(String[] args) {
        //数组 -- list集合
        String[] str = {"hello","world","java"};
        List<String> list = Arrays.asList(str);
        System.out.println(list);

         System.out.println("====================");
        //数组 -- set集合 方式一
        String[] s = {"a","b","c"};
        //先转list
        List<String> list = Arrays.asList(s);
        //再转set  
        Set<String> set = new HashSet<>(list);
        set.forEach(System.out::println);
        
        //数组 -- set集合 方式二
        String[] str2 = {"hello","world","java"};
        Set<String> set = Arrays.stream(str2).collect(Collectors.toSet());
        System.out.println(set);
    }
}
	public static void main(String[] args) {
		//list集合 --> 数组
		List<String> list = new ArrayList<String>();
		list.add("hello");
		list.add("world");		
		//定义数组的长度
		String[] str = new String[list.size()];
		list.toArray(str);//添加元素
		//System.out.println(str[0]);
		//System.out.println(str[1]);
		for(String s : str){
			System.out.println(s);
		}        
         System.out.println("====================");        
        //set集合  -- 数组
        Set<String> set = new HashSet<>();
        set.add("abc");
        set.add("cba");
        set.add("cba");
        //先转成list
        List<String> lists = new ArrayList<>(set);
        //list转数组
        String[] str4 = new String[lists.size()];
        lists.toArray(str4);
        
	    System.out.println(str4[0]);
        System.out.println(str4[1]);
        
        for(String s : str4){
            System.out.println(s);
        }
	}

List和set互转

    public static void main(String[] args) {
        //list --> set
        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("b");

        Set<String> set = new HashSet<>(list);
        set.forEach(System.out::println);
        System.out.println("====================");
        //set --> list
        Set<String> sets = new HashSet<>();
        sets.add("a");
        sets.add("b");
        sets.add("c");

        List<String> lists = new ArrayList<>(sets);
        lists.forEach(System.out::println);
    }

Collections

什么是Collections

Collections是针对集合操作的工具类

Collections常用的方法

方法名 说明
public static void sort(List<T> list) 将指定的列表按升序排序
public static void reverse(List<?> list) 反转指定列表中元素的顺序
<T extends Object & Comparable<? super T>> T max() 获取集合中最大值

Collections代码演示

public class CollectionsDemo01 {
    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);
        // 将指定的列表按升序排序
        Collections.sort(list);
        //反转指定列表中元素的顺序
        Collections.reverse(list);
        //获取集合是最大值
        Integer max = Collections.max(list);
        System.out.println(max);//50

        //获取集合是最小值
        Integer min = Collections.min(list);
        System.out.println(min);//10

        System.out.println(list);
    }
}

collection和collectons的区别?

Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式。

Collections 是一个操作集合的工具类。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架。

File类

什么是File类

java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作。

File类构造方法

方法名 说明
public File(String pathname) 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
public File(String parent, String child) 父路径名字符串和子路径名字符串创建新的 File实例。
public File(File parent, String child) 父抽象路径名和子路径名字符串创建新的 File实例。

代码演示

public class FileDemo{
    public static void main(String[] args){
        // 文件路径名
        String pathname = "D:\\aaa.txt";
        File file1 = new File(pathname); 

        // 文件路径名
        String pathname2 = "D:\\aaa\\bbb.txt";
        File file2 = new File(pathname2); 

        // 通过父路径和子路径字符串
        String parent = "d:\\aaa";
        String child = "bbb.txt";
        File file3 = new File(parent, child);

        // 通过父级File对象和子路径字符串
        File parentDir = new File("d:\\aaa");
        String child = "bbb.txt";
        File file4 = new File(parentDir, child);
    }
}

==注意事项==

  1. 一个File对象代表硬盘中实际存在的一个文件或者目录。
  2. 无论该路径下是否存在文件或者目录,都不影响File对象的创建。

获取功能方法

方法名 说明
public String getAbsolutePath() 返回此File的绝对路径名字符串。
public String getPath() 将此File转换为路径名字符串
public String getName() 返回由此File表示的文件或目录的名称。
public long length() 返回由此File表示的文件的长度。

代码演示

public class FileGet {
    public static void main(String[] args) {
        File f = new File("d:/aaa/bbb.java");     
        System.out.println("文件绝对路径:"+f.getAbsolutePath());
        System.out.println("文件路径:"+f.getPath());
        System.out.println("文件名称:"+f.getName());
        System.out.println("文件长度:"+f.length()+"字节");

        File f2 = new File("d:/aaa");     
        System.out.println("目录绝对路径:"+f2.getAbsolutePath());
        System.out.println("目录构造路径:"+f2.getPath());
        System.out.println("目录名称:"+f2.getName());
        System.out.println("目录长度:"+f2.length());
    }
}

绝对路径和相对路径

  • 绝对路径:从盘符开始的路径,这是一个完整的路径。
  • 相对路径:相对于项目目录的路径,这是一个便捷的路径,开发中经常使用。
public class FilePath {
    public static void main(String[] args) {
      	// D盘下的bbb.java文件
        File f = new File("D:\\bbb.java");
        System.out.println(f.getAbsolutePath());
      	
		// 项目下的bbb.java文件
        File f2 = new File("day15\\bbb.java");
        System.out.println(f2.getAbsolutePath());
    }
}

判断功能的方法

方法名 说明
public boolean exists() 此File表示的文件或目录是否实际存在。
public boolean isDirectory() 此File表示的是否为目录。

代码演示

public class FileIs {
    public static void main(String[] args) {
        File file1 = new File("a.txt");

        System.out.println("是不是一个目录:"+file1.isDirectory());
        System.out.println("是不是一个文件:"+file1.isFile());
        System.out.println("是否能写:"+file1.canWrite());
        System.out.println("是否能读:"+file1.canRead());
        System.out.println("是否存在:"+file1.exists());
        System.out.println("是隐藏:"+file1.isHidden());
    }
}

创建&删除功能的方法

方法名 说明
public boolean createNewFile() 当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。
public boolean delete() 删除由此File表示的文件或目录。
public boolean mkdir() 创建由此File表示的目录。
public boolean mkdirs() 创建由此File表示的目录,包括任何必需但不存在的父目录。

代码演示

public class FileCreateDelete {
    public static void main(String[] args) throws IOException {
        //项目的根目录下
        File file1 = new File("a.txt");
        System.out.println(file1.createNewFile());

        //删除文件
        File file2 = new File("a.txt");
        System.out.println(file2.delete());

        //创建文件夹
        File file3 = new File("aa");
        System.out.println( file3.mkdir());

        //删除文件夹
        File file4 = new File("aa");
        System.out.println(file4.delete());

        //创建多级文件夹
        File file5 = new File("aa\\bb\\cc");
        System.out.println( file5.mkdirs());

        //删除多级文件夹
        //注意:只能删除最里的一级目录
        File file6 = new File("aa\\bb\\cc");
        System.out.println(file6.delete());
    }
}

==注意事项==

delete方法,如果此File表示目录,则目录必须为空才能删除。(如果要删除目录,目录中不能有文件,否则删不了)

重命名功能

/*
 * 重命名功能:public boolean renameTo(File dest)
 *        如果路径名相同,就是改名。
 *        如果路径名不同,就是改名并剪切。
 *
 * 路径以盘符开始:绝对路径    c:\\a.txt
 * 路径不以盘符开始:相对路径   a.txt
 */
public static void main(String[] args) throws IOException {
        //数据数据源,前提得这个数据存在
        File srcFile = new File("D:\\江一燕.jpg");
        //数据数据的目的地
        File newFile = new File("D:\\我和江一燕.jpg");//相同路径是改名
        // File newFile2 = new File("e:\\我和江一燕.jpg");//不同路径是剪切并改名
        //实现改名
        System.out.println(srcFile.renameTo(newFile));
    }
}

目录的获取

方法名 说明
public String[] list() 返回一个String数组,表示该File目录中的所有子文件或目录。
public File[] listFiles() 返回一个File数组,表示该File目录中的所有的子文件或目录。
public class FileFor {
    public static void main(String[] args) {
        //创建file
        File file = new File("d:\\");
        //获取e盘下所有的文件及目录
        String[] list = file.list();
        for(String s : list){
            System.out.println(s);
        }
        System.out.println("------------------");

        //创建file
        File file2 = new File("d:\\");
        //获取e盘下的所有file文件
        File[] files = file2.listFiles();
        for(File f : files){
            System.out.println(f);
        }
    }
}

==注意事项==

调用listFiles方法的File对象,表示的必须是实际存在的目录,否则返回null,无法进行遍历。

作业:判断d盘目录下是否有后缀名为.jpg的文件,如果有,就输出此文件名称 思路:获取d盘下所有目录,判断目录下是文件还是目录,如果文件,判断哪个文件后缀.jpg,如果是,把文件名输出。

综合案例

打印多级目录

  • 分析

    多级目录的打印,就是当目录的嵌套。遍历之前,无从知道到底有多少级目录,所以我们还是要使用递归实现。

  • 代码实现
    public class DiGuiDemo2 {
        public static void main(String[] args) {
          	// 创建File对象
            File dir  = new File("E:\workspase\507\day13");
          	// 调用打印目录方法
            printDir(dir);
        }
        public static void  printDir(File dir) {
          	// 获取子文件和目录
            File[] files = dir.listFiles();
          	// 循环打印
          	/*
          	  判断:
          	  当是文件时,打印绝对路径.
          	  当是目录时,继续调用打印目录的方法,形成递归调用.
          	*/
            for (File file : files) {
        		// 判断
                if (file.isFile()) {
                  	// 是文件,输出文件绝对路径
                    System.out.println("文件名:"+ file.getAbsolutePath());
                } else {
                  	// 是目录,输出目录绝对路径
                    System.out.println("目录:"+file.getAbsolutePath());
                  	// 继续遍历,调用printDir,形成递归
                    printDir(file);
                }
            }
        }
    }
    

文件搜索

  • 需求

    搜索D:\aaa 目录中的.java 文件。

  • 需求分析

    目录搜索,无法判断多少级目录,所以使用递归,遍历所有目录。

    遍历目录时,获取的子文件,通过文件名称的后缀,判断是否符合条件。

  • 代码实现
    public class DiGuiDemo3 {
        public static void main(String[] args) {
            // 创建File对象
            File dir  = new File("D:\\aaa");
          	// 调用打印目录方法
            printDir(dir);
        }
        public static void printDir(File dir) {
          	// 获取子文件和目录
            File[] files = dir.listFiles();      	
          	// 循环打印 if(files==null)return;避免空指针异常;
            for (File file : files) {
                if (file.isFile()) {
                  	// 是文件,判断文件名并输出文件绝对路径
                    if (file.getName().endsWith(".java")) {
                        System.out.println("文件名:" + file.getAbsolutePath());
                    }
                } else {
                    // 是目录,继续遍历,形成递归
                    printDir(file);
                }
            }
        }
    }