泛型、不确定参数、File类和递归


一、泛型

1.1 泛型的概述

广泛的类型,在类的定义与方法的声明阶段,需要使用其他引用类型作为参数进行使用,但是需要使用的参数类型只有实际使用时才会知道具体是什么类型(例如集合的使用),虽然也可以使用Object类进行存储(使用多态的概念),但是在使用时如果想使用自己特有的属性方法必须向下转型,在向下转型时可能造成错误(Object是所有类的父类),为了解决这一问题,在jdk5以后引入了泛型这一概念,用于声明时无法确定类型的替代,在使用时必须传入对应的类型进行转换,会自动将声明时类所有使用泛型的位置替换为指定的类型

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

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

1.2 优点

  • 在声明创建对象时指定泛型,将方法中所有对应替换为指定类型,避免了类型转换的异常
  • 将运行时的类型转换异常变为编译时(编译失败)

1.3 泛型的定义和使用

泛型: 用来灵活地将数据类型应用到不同的方法接口当中。将数据类型作为参数进行传递。

1.3.1 定义和使用含有泛型的类

书写在类名之后,使用< >包裹通常使用大写的单个字母,在使用类创建对象时填入泛型类型,在当前对象中会自动将所有对应泛型类型变为指定类型

  • 定义格式

修饰符 class 类名<代表泛型的变量> { }

  • 举例
class ArrayList<E>{ 
    public boolean add(E e){ }

    public E get(int index){ }
   	....
}
  • 自定义泛型类
//书写在类名中
public class GenericClass<A> {
	private A fanxing;

	public A getFanxing() {
		return fanxing;
	}

	public void setFanxing(A fanxing) {
		this.fanxing = fanxing;
	}
}
  • 泛型类使用
		 GenericClass<String> g=new GenericClass<>();
		 g.setFanxing("自定义泛型类");
		 System.out.println(g.getFanxing());

1.3.2 定义和使用含有泛型的方法

有些方法在进行执行时可能需要根据参数的类型决定返回值或进行声明的类型,这时候需要在对应方法中定义泛型

需要在传入参数时,通过参数的类型决定泛型的类型

  • 定义格式

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

  • 定义泛型方法
public class MyGenericMethod {	  
    public <MVP> void show(MVP mvp) {
    	System.out.println(mvp.getClass());
    }
    
    public <MVP> MVP show2(MVP mvp) {	
    	return mvp;
    }
}
  • 自定义泛型方法和使用
//泛型方法
public class GenericMethod {
	public<A> A get(A a){
		return a;
	}
 
}
class GenericMethodTest{
	public static void main(String[] args) {
		GenericMethod g=new GenericMethod();
		  Integer integer = g.get(1);
		  //自动根据参数的类型决定返回值的类型
	}
}

1.3.3 定义和使用含有泛型的接口

书写在接口中与书写在类中一样,只不过需要实现类实现方法,所以在使用时需要额外注意(如果类被继承使用方式一样)

  • 定义格式

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

  • 定义泛型接口
public interface MyGenericInterface<E>{
	public abstract void add(E e);
	public abstract E getE();  
}
  • 泛型接口的使用

在实现或继承接口、父类时,如果存在泛型要么指定泛型的类型,要不就继续声明泛型

public interface MyGenericInterface<E> {
	public abstract void add(E e);
	public abstract E getE();  
}
//实现时直接书写泛型类型
//在实现方法时 会直接使用指定的泛型重写
class My1 implements MyGenericInterface<String>{
	@Override
	public void add(String e) {
	}
	@Override
	public String getE() {
		return null;
	}
}
//在实现类声明相同泛型 创建对象时使用
//实现的方法根据泛型定义类型
class My2<E> implements  MyGenericInterface<E>{
	@Override
	public void add(E e) {
	}
	@Override
	public E getE() {
		return null;
	}
	
}

1.4 使用泛型的注意事项

1.4.1 书写的注意事项

  • 通常书写在<>中
  • 使用大写的单个字母替代
  • 多个泛型使用逗号分隔

1.4.2 使用的注意事项

  • 泛型只能声明对象(因为其不是一个确定的类)
  • 泛型不能调用构造方法(泛型不是一个类,没有构造方法)
  • 泛型在类名定义使用范围为当前类,在方法中定义范围为当前方法
  • 泛型不能被static修饰(被static修饰为类属性,无需创建对象使用)
  • 泛型不能被final修饰(final修饰全局变量必须进行初始化赋值)

1.5 泛型通配符

1.5.1 默认通配符———基本使用

在使用拥有泛型的类与接口时,如果不确定使用的类型可以不书写<>泛型,但是会默认使用默认通配符<?>使用Object当做泛型的类型使用

public static void main(String[] args) {
    Collection<Intger> list1 = new ArrayList<Integer>();
    getElement(list1);
    Collection<String> list2 = new ArrayList<String>();
    getElement(list2);
}
public static void getElement(Collection<?> coll){}
//?代表可以接收任意类型

在getElement方法需要传入一个集合对象,当没有确定泛型时,任意集合对象都可以进行存储,但是如果定义了类型,那么只能传入指定泛型类型的对象

1.5.2 默认通配符———受限泛型

之前设置泛型的时候,实际上是可以任意设置的,只要是类就可以设置。但是在JAVA的泛型中可以指定一个泛型的上限下限

  • 泛型的上限

    • 格式

    类型名称 <? extends 类 > 对象名称

    • 意义

    只能接收该类型及该类型的子类

  • 泛型的下限

    • 格式

    类型名称 <? super 类 > 对象名称

    • 意义

    只能接收该类型及该类型的父类型

比如:现已知Object类,String 类,Number类,Integer类,其中Number是Integer的父类

public static void main(String[] args) {
    Collection<Integer> list1 = new ArrayList<Integer>();
    Collection<String> list2 = new ArrayList<String>();
    Collection<Number> list3 = new ArrayList<Number>();
    Collection<Object> list4 = new ArrayList<Object>();
    
    getElement1(list1);
    getElement1(list2);//报错
    getElement1(list3);
    getElement1(list4);//报错
  
    getElement2(list1);//报错
    getElement2(list2);//报错
    getElement2(list3);
    getElement2(list4);
  
}
// 泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类
public static void getElement1(Collection<? extends Number> coll){}
// 泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类
public static void getElement2(Collection<? super Number> coll){}

1.5.3 钻石通配符

钻石通配符: 指的是在创建对象时没有指定泛型类型而是书写<>进行自动识别,钻石通配符会自动获取声明变量的泛型类型进行使用

 Collection<Integer> list1 = new ArrayList<Integer>();//未使用钻石通配符
 Collection<Integer> list2 = new ArrayList<>();//使用钻石通配符

二、不确定参数

2.1 概念

在定义方法时,无法确定方法的参数有多少个,只能确定参数的类型,这个时候可以使用不确定参数进行书写,在调用方法时可以无限填入参数,会自动根据填入的参数创建数组在方法中使用。

2.2 书写

在这里插入图片描述

不确定参数的书写必须指定数据类型,并且必须书写在方法参数列表的最后一项,并且一个方法只能使用一个不确定参数

在没有使用不确定参数之前

	public static void main(String[] args) {
		 ArrayList<String> list=new ArrayList<>();
		 String [] arr={"a","b","c"};
		 a(list,arr);
		 System.out.println(list);
	}	
	//书写方法将数组数据添加至集合
	public static void a(List<String> list,String[] arr ){
		for (String string : arr) {
			list.add(string);
		}
	}

在使用不确定参数之后

	public static void main(String[] args) {
		 ArrayList<String> list=new ArrayList<>();
		 a(list,"a","b","c","d");
		 System.out.println(list);
	}	
	//书写方法将数组数据添加至集合
	public static void a(List<String> list,String... arr ){
		//不确定参数本质就是一个数组
		//只不过在调用时可以使用数组创建填值的形式进行数组的添加
		for (String string : arr) {
			list.add(string);
		}
	}

三、File类

3.1 概念

java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作。
在java中使用File类的对象代表实际运行系统中的文件以及文件夹

3.2 构造方法

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

代码演示

		//new File(PathURL);
		//直接使用代表文件的全路径创建file对象
		File f1=new File("E://test/a.txt");
		
		//使用路径拼接的形式代表file对象
		//new File(pURL,FName)
		//pURL保存当前指定文件的文件夹
		//FName保存文件的文件名
		File f2=new File("E://test","a.txt");
		
		//使用已有的file当父文件夹
		//new File(pFile,FName);
		File f=new File("E://test");
		File f3=new File(f, "a.txt");
		
		System.out.println(f1.equals(f2));//true
		//file重写了equals方法,只要两个对象代表文件的路径相同那么返回true

注意事项

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

3.3 常用方法

3.3.1 获取方法

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

代码演示

	System.out.println(f3.getAbsolutePath());//获取当前对象绝对路径
	System.out.println(f3.getPath());//获取当前对象路径(创建路径)
	System.out.println(f3.getName());//获取当前对象代表文件名
	System.out.println(f3.length());//获取当前文件字节大小(只能获取文件大小)
	System.out.println(f3.getParent());//获取当前文件所在文件夹的字符串
	System.out.println(f3.getParentFile());//获取当前文件所在文件夹对象

3.3.2 判断方法

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

代码演示

	System.out.println(f3.exists());//当前对象所代表文件是否存在 
	System.out.println(f3.isHidden());//当前对象所代表文件是否隐藏
	System.out.println(f3.isDirectory());//当前对象所代表是否是文件夹
	System.out.println(f3.isFile());//当前对象所代表是否是文件

3.3.3 创建删除方法

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

代码演示

	//createNewFile创建当前file对象代表的文件
	//如果指定文件在系统中已经存在 则返回false
	boolean createNewFile = f2.createNewFile();
	System.out.println(createNewFile);
	File f4=new File("E://test2/a.txt");
	//f4.createNewFile();
	//注意:
	//createNewFile只能创建文件
	//创建时指定文件已存在则返回false
	//创建的文件要求必须存在父文件夹(所保存文件的文件夹必须存在)
	 
	boolean delete =f.delete();
	System.out.println(delete);
	File aaaa=new File("E://test/aaaa");
	System.out.println(aaaa.delete());
	//注意:
	//delete方法既能删除文件也能删除文件夹
	//在删除文件时需要保证文件中没有文件以及其他文件夹
	
	File f5=new File("E://test/a/b");
	f5.mkdir();
	//注意:
	//创建时必须保证父文件夹存在
	
	//创建多级目录 如果父目录不存在则创建
	f5.mkdirs();

3.3.4 目录的遍历

方法名说明
public String[] list()返回一个String数组,表示该File目录中的所有子文件或目录。
public File[] listFiles()返回一个File数组,表示该File目录中的所有的子文件或目录。
		File file=new File("E://code/JavaLogic/src/com/yunhe/day0617");
		//获取当前文件中所有文件的名字 以字符串数组的形式返回
		String[] list = file.list();
		for (String string : list) {
			System.out.println(string);
		}
		
		//获取当前文件夹中所有文件的对象以文件数组的形式返回
		File[] listFiles = file.listFiles();
		for (File file2 : listFiles) {
			System.out.println(file2.length());
		}

注意事项

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

四、递归

4.1 递归的概念

递归:指在当前方法内调用自己的这种现象。

4.2 递归的分类

递归分为两种,直接递归和间接递归。

  • 直接递归称为方法自身调用自己。
  • 间接递归可以A方法调用B方法,B方法调用C方法,C方法调用A方法。

4.3 递归的注意事项

  • 递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出。
  • 在递归中虽然有限定条件,但是递归次数不能太多。否则也会发生栈内存溢出。
  • 构造方法,禁止递归

4.4 递归案例

4.4.1 计算1 ~ n的和

public class DiGuiDemo {
	public static void main(String[] args) {
		//计算1~num的和,使用递归完成
		int num = 5;
      	// 调用求和的方法
		int sum = getSum(num);
      	// 输出结果
		System.out.println(sum);
		
	}
  	/*
  	  通过递归算法实现.
  	  参数列表:int 
  	  返回值类型: int 
  	*/
	public static int getSum(int num) {
      	/* 
      	   num为1时,方法返回1,
      	   相当于是方法的出口,num总有是1的情况
      	*/
		if(num == 1){
			return 1;
		}
      	/*
          num不为1时,方法返回 num +(num-1)的累和
          递归调用getSum方法
        */
		return num + getSum(num-1);
	}
}

在这里插入图片描述

4.4.2 递归求阶乘

public class DiGuiDemo {
  	//计算n的阶乘,使用递归完成
    public static void main(String[] args) {
        int n = 3;
      	// 调用求阶乘的方法
        int value = getValue(n);
      	// 输出结果
        System.out.println("阶乘为:"+ value);
    }
	/*
  	  通过递归算法实现.
  	  参数列表:int 
  	  返回值类型: int 
  	*/
    public static int getValue(int n) {
      	// 1的阶乘为1
        if (n == 1) {
            return 1;
        }
      	/*
      	  n不为1时,方法返回 n! = n*(n-1)!
          递归调用getValue方法
      	*/
        return n * getValue(n - 1);
    }
}

4.4.3 打印多级目录

public class DiGuiDemo2 {
    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();
      	// 循环打印
      	/*
      	  判断:
      	  当是文件时,打印绝对路径.
      	  当是目录时,继续调用打印目录的方法,形成递归调用.
      	*/
        for (File file : files) {
    		// 判断
            if (file.isFile()) {
              	// 是文件,输出文件绝对路径
                System.out.println("文件名:"+ file.getAbsolutePath());
            } else {
              	// 是目录,输出目录绝对路径
                System.out.println("目录:"+file.getAbsolutePath());
              	// 继续遍历,调用printDir,形成递归
                printDir(file);
            }
        }
    }
}

4.4.4 文件搜索

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();
      	
      	// 循环打印
        for (File file : files) {
            if (file.isFile()) {
              	// 是文件,判断文件名并输出文件绝对路径
                if (file.getName().endsWith(".java")) {
                    System.out.println("文件名:" + file.getAbsolutePath());
                }
            } else {
                // 是目录,继续遍历,形成递归
                printDir(file);
            }
        }
    }
}

每日一点点进步
不进则退

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

璃尔 °

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值