欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java 基础语法学习总结

程序员文章站 2022-03-16 15:01:09
一、异常错误与异常java中对于程序出现的异常情况分为两种:错误(Error)异常(Exception)Error:错误通常是系统级别的问题,比如说JVM内存溢出(*Error),JVM系统错误等,这些问题是程序员无法修复的问题,程序运行时出现的无法被程序员从业务上解决的问题,这些问题一般是系统级别的。错误不是我们关注的范畴Exception:异常通常是程序再运行期间,或者编译期间由编译器抛出的一些,可以被程序员处理的代码上的问题,比如(NullPointerExcep...

一、异常

错误与异常

java中对于程序出现的异常情况分为两种:

  1. 错误(Error)
  2. 异常(Exception)
    Error:错误通常是系统级别的问题,比如说JVM内存溢出(*Error),JVM系统错误等,这些问题是程序员无法修复的问题,程序运行时出现的无法被程序员从业务上解决的问题,这些问题一般是系统级别的。错误不是我们关注的范畴
    Exception:异常通常是程序再运行期间,或者编译期间由编译器抛出的一些,可以被程序员处理的代码上的问题,比如(NullPointerExcepotion/ArrayIndexOutOfBoundsException),异常是程序员开发中需要解决的问题

Throwable

Throwable是Java中错误和异常的*父类,以下是Throwable和Error,Exception之间的关系
Java 基础语法学习总结

java中的所有错误从Error类继承,并且绝大多数类名称后缀以Error结尾
Java中的所有异常从Exception类继承,都是以Exception作为后缀结尾

异常概述

Exception:异常,一般在程序运行期间,或者编译期间由编译器抛出的异常信息,这些异常情况可以由程序员进行处理(抛出,捕获);java中的异常根据类型划分又分为两种类型:

  • 运行时异常(RuntimeException)
  • 检查异常(一般异常)

运行时异常

运行时异常一般在程序运行期间,出现了对应异常情况之后由JVM抛出,并且将异常的堆栈信息输出到控制台(或日志文件),java中的所有运行时异常都是从java.lang.RuntimeException继承而来。常见的运行时异常:

异常类型 说明
java.long.ArithmeticException 算数异常(比如被零除)
java.lang.NullPonterException 空指针异常(调用方法,属性的对象位null时)
java.lang.ArrayIndexOutOfBoundsException 数组索引越界
java.lang.ClassCastException 类型转换异常
java.util.InputMismatchException 输入的数据类型不匹配读取的异常

运行时异常即程序运行时才会产生的异常

检查异常

检查异常也称之为一般异常,或者编译期异常,这种类型异常通常在编译期间由编译器提示需要进行显式的处理:
Java 基础语法学习总结
常见的检查异常:

异常类型 说明
java.lang.ClassNotFoundException 未找到异常
java.io.FileNotFoundException 文件未找到异常
java.io.IOException IO异常
java.sql.SQLException 访问数据库的异常
java.text.ParseException 解析异常

检查异常是在程序编译时产生的

异常处理

异常既然产生则有必要进行合理的处理,Java中对于异常的处理分为两种方式:

  1. 异常抛出(throw/throws)
  2. 异常捕获(try/catch/finally)

Java程序中一旦出现异常,则出现异常问题的所在代码行之后的代码无法再执行

异常抛出

异常的抛出指的是将有可能出现的异常通过方法的结构向外抛出,交给下一级的调用者处理

/**
	 * 	抛出异常
	 * @throws ClassNotFoundException
	 */
public static void e1() throws ClassNotFoundException{	
    Class.forName("java.lang.Strin");		
}

抛出异常常见的关键字:

  • throws:用于方法的声明中,抛出有可能出现的异常
  • throw:用于语句块中,抛出指定类型的异常对象,throw一旦执行,则一定会出现该类型异常

语法区别:

  • throws
【修饰符】 返回值类型 方法名(【参数列表】) throws 异常类型名称{
    //方法体
}
public static void e1() throws ClassNotFoundException{	
	Class.forName("java.lang.String");		
}
  • throw
方法体{
    throw  异常类型对象
}
public static void main(String[] args) throws IOException  {	
    int i = 0;
    if(i == 0) {	
        //抛出异常对象
        throw new IOException();
    }
    System.out.println("hello");
}

Java 基础语法学习总结

对于存在继承关系的异常抛出问题

父类的结构:

public class Animal {
	
	public void eat(){
		System.out.println("吃东西");
	}
}

子类的结构:

public class Dog extends Animal{

    //编译错误
	public void eat() throws ClassNotFoundException{
		Class.forName("");
	}
}

对于以上程序:
子类Dog对父类Animal中的方法eat()方法进行了重写,但是由于父类方法没有抛出任何的异常,此时子类无法进行任何检查的抛出,否则会不兼容父类方法定义,因此以上程序在子类中会出现编译错误
Java 基础语法学习总结
解决方案由两种:

  1. 子类方法中对于异常捕获
  2. 在父类方法的声明上加上对应的异常类型抛出定义:throws ClassNotFoundException
    Java 基础语法学习总结

父类方法可以抛出比子类方法抛出的范围更大的异常,比如直接throws Exception

注意事项:
父类方法未抛出任何异常情况下,子类只能抛出运行时异常。
游离快和静态语句块中不能抛出任何异常,因为外界无法直接调用这两种语句块

异常捕获

异常的捕获即,将有可能出现异常的代码片段使用try语句块进行包裹,然后使用catch语句块将有可能产生的异常类型进行捕获,并作出处理。

异常捕获常见的关键字:

  • try
  • catch
  • finally

语法结构:

try{
    //有可能出现异常的代码片段
}catch(异常类型 变量名){
    //处理异常
}finally{
    //不论是否出现异常,始终执行
}
try {
    m1();
    PrintStream ps = new PrintStream("a/test/details.log");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} catch (FileNotFoundException e) {
    System.out.println("fileNotFound");
    e.printStackTrace();
} catch (IOException e) {
    System.out.println("IO");
    e.printStackTrace();
}

程序执行到try语句块,在执行期间如果出现了对应catch的异常类型,则直接进入catch语句块,如果catch语句块中没有合适的异常解决方案,则由JVM进行统一处理(打印异常的堆栈信息)

finally

finally一般用于异常捕获之后执行最终的处理工作,比如,清理资源,关闭流,关闭连接;finally中的代码无论是否出现异常,始终会执行。

try {
    //打开资源
    System.out.println("打开文件");
    System.out.println(10/2); 
}catch(Exception e) {
    e.printStackTrace();
}finally {
    //无论是否异常始终执行
    System.out.println("关闭文件");			
}

try,catch,finally的组织方式可以有多种:

//方法一:
try {

}finally {

}

//方法二:
try {

}catch(Exception e) {

}

//方法三:
try {

}catch(RuntimeException re) {

}catch(Exception e) {

}

//方法四:
try {

}catch(Exception e) {

}finally {

}

关于异常的常见面试题
1. 请你说出 final、finalize和finally的区别?
final是一个关键字用于修饰类,属性,方法
finalize是Object类中提供的一个方法,用于在jvm对对象清理时,对于当前对象执行一些最终的处理工作的
2. java中是否会存在内存溢出的问题?(指针)
理论上java不会存在内存泄漏问题,因为jvm提供了GC(垃圾回收:garbage collection)机制,会在适当的时候自动回收内存空间,不需要由程序员手动处理;但是如果使用第三方资源(比如:打开一个文件,打开了网络通道,打开数据库连接等)并且未及时的清理以及回收,将会导致内存泄漏。
3. 异常处理中finally和return的结合使用?
如果try语句块中有使用return,并且try语句块中没有任何异常时,程序首先会执行finally然后再执行return;但是对于基本类型的数据,finally的赋值是不会生效的,但是finally中操作引用类型的属性可以生效

//程序正常执行,返回 20;finally中的赋值无效
public static int m2() {
    int i = 10;
    try {
        i = 20;
        return i;		
    }catch(Exception e){
        e.printStackTrace();
    }finally {
        i = 30;
        System.out.println("finally");
    }
    return i;
}

//程序正常执行,返回对象中的name属性值被修改为“李四”;finally中的赋值生效
public static User m3() {
    User u = new User();
    try {
        u.name = "张三";
        return u;
    }catch(Exception e) {
        e.printStackTrace();
    } finally {
        u.name = "李四";
    }
    return u;
}

异常定位

Java 基础语法学习总结

自定义异常

概述
以上我们已经熟悉了java中的异常分类以及处理方式,其中异常分类主要包含检查异常和运行时异常,但是以上所有异常都是有JDK预定义好的异常类型,比如:空指针,索引越界,类型转换失败等代码语法方面的异常,并没有与实际项目相关一些业务方面的异常,比如:订单创建失败,用户权限不足,余额不足等异常情况;
因此,针对以上的需求,当预定的异常无法满足所有需要时,我们可以通过对JDK的异常进行扩展,自定义异常,以满足实际项目的需求。

自定义异常的使用

java中创建自定义异常十分简单,只需要对现有的异常类型,扩展即可,比如常见的方式为:继承Exception,声明一个无参的以及一个包含字符串类型参数的构造器即可。异常的定义通常用于标记程序运行时的异常情况,并不需要在异常中进行任何的业务逻辑处理,因此自定义异常中也无需定义任何的方法。

案例:

public class MyException extends Exception{

	public MyException() {
		super();
	}
	
	public MyException(String msg) {
		super(msg);
	}
}

自定义异常综合案例:

一有个银行账户A和账户B,现在需要从账户A转账到账户B,转账需要检查账户的余额是否足够,如果余额不足,则抛出一个 MoneyLessException,请实现!

账户类(Account.java)

public class Account {

	private int id;
	private String name;
	private double money;
	
    //构造器(略)
	//setter/getter(略)
    //toString(略)
}

账户管理类(AccountManager.java)

public class AccountManager {

	/**
	 * 	将指定账户中的余额转移指定数目到另一个账户中
	 * @param a1  	账户A
	 * @param a2 	账户B
	 * @param money 需要转账的金额
	 * @throws MoneyLessException
	 */
	public void transfer(Account a1,Account a2,double money) throws MoneyLessException {
		
		if(a1.getMoney() < money) {
			//余额不足
			throw new MoneyLessException("余额不足:"+(a1.getMoney() - money));
		}
		a1.setMoney(a1.getMoney() - money);
		a2.setMoney(a2.getMoney() + money);
		
		System.out.println(a1);
		System.out.println(a2);
	}
}

自定义异常类(MoneyLessException.java)

/**
 * 	余额不足异常
 * @author mrchai
 *
 */
public class MoneyLessException extends Exception {

	public MoneyLessException() {
		super();
	}
	
	public MoneyLessException(String msg) {
		super(msg);
	}
}

测试类(Test.java)

public class Test {

	public static void main(String[] args) throws MoneyLessException {
		
		Account a1 = new Account(1, "A", 500);
		Account a2 = new Account(2, "B", 100);
		
		AccountManager am = new AccountManager();
		am.transfer(a1, a2, 100);
	}

}

常用类之BigDecimal与DecimalFormat

BigDecimal

java.math.BigDecimal类从java.math.Number类继承而来,用于表示精度较高的数值类型的封装类型,一般用于精度要求较高的程序中,比如银行账户的金额属性,

常见构造器:

  • java.math.BigDecimal(String s)
  • java.math.BigDecimal(double d)
  • java.math.BigDecimal(long l)
  • java.math.BigDecimal(int i)

常见方法:

  • add(BigDecimal b):与另一个BigDecimal执行相加运算
  • subtract(BigDecimal b):与另一个BigDecimal执行相减运算
  • multiply(BigDecimal b):与另一个BigDecimal执行相乘运算
  • divide(BigDecimal b):与另一个BigDecimal执行相除运算
double d1 = 0.1;
double d2 = 0.2;
//		System.out.println(d1 + d2); 
//在涉及到一些敏感的浮点数运算时,不适合直接使用float和double,精度丢失

BigDecimal b1 = new BigDecimal("0.1");
BigDecimal b2 = new BigDecimal("0.2");

//相加 a + b =>  a.add(b)
System.out.println(b1.add(b2));  
System.out.println(b1.add(b2,MathContext.DECIMAL128)); 

//相减 a - b => a.subtract(b) 
System.out.println(b1.subtract(b2));

//相乘 a * b => a.multiply(b)
System.out.println(b1.multiply(b2));

//相除 a / b => a.divide(b) 
System.out.println(b1.divide(b2));

对于相除运算的使用:

BigDecimal d1 = new BigDecimal("10");
BigDecimal d2 = new BigDecimal("3");
//当两个数值相除为无限循环数时会出现算术异常
//向上取整
System.out.println(d1.divide(d2,RoundingMode.CEILING));
//向下取整
System.out.println(d1.divide(d2,RoundingMode.FLOOR));

//向上保留指定位小数点
System.out.println(d1.divide(d2, 2, BigDecimal.ROUND_CEILING));
//向下保留指定位小数点
System.out.println(d1.divide(d2, 2, BigDecimal.ROUND_FLOOR));

NumberFormat & DecimalFormat

java.text.NumberFormat 和java.text.DecimalFormat是用于进行数值格式化的类,可以对数值的显示位数进行格式化处理,以及对浮点数进行金额或者百分比的格式表现;DecimalFormat是NumberFormat的子类。

java.text.NumberFormat是一个抽象类,内部提供了几个静态方法用于直接获取NumberFormat对象,而这些静态方法的实现通过java.text.DecimalFormat实现。

NumberFormat常见方法:

  • format(double d):将一个double值按指定的格式转换为String
  • parse(String s):将一个String类型的数值解析为double类型
double d = 150500.491987;
//获取用于进行货币格式化(本地环境)的数值格式化对象
NumberFormat fmt = NumberFormat.getCurrencyInstance();
//将浮点数转换为字符串类型的固定格式
String s = fmt.format(d);
System.out.println(s);//¥150,500.49

//获取整数类型的格式化对象
fmt = NumberFormat.getIntegerInstance();
System.out.println(fmt.format(d));//150,500

//获取标准的数值类型格式化对象(对于小数点最大保留后三位)
fmt = NumberFormat.getNumberInstance();
System.out.println(fmt.format(d));//150,500.492

//获取进行百分比格式化的对象
fmt = NumberFormat.getPercentInstance();
System.out.println(fmt.format(0.456789));//46%

由于NumberFormat提供的方法可能无法满足一些个性化的格式需求,比如需要将double值转换为百分比,并保留小数点后两位,NumberFormat中提供的格式化方法getPersentInstence()只能保留整数位;因此,我们需要能够自定格式的工具,所以java中提供的java.text.DecimalFormat就能够满足需求了:

//创建一个数值格式化对象
DecimalFormat fmt = new DecimalFormat("##.##%");
System.out.println(fmt.format(0.456789)); // 45.68%

fmt = new DecimalFormat("\u00A4##,###,###.#");
System.out.println(fmt.format(5423423467890.345)); //¥5,423,423,467,890.3

//NumberFormat&DecimalFormat不仅能够将double值按指定的格式格式化为String,
//同时,也能将String类型表示的数字解析为需要的数值类型
String s = "50.67%";
//将以上字符串转换为double值   0.5067
fmt = new DecimalFormat("##.##%");
Number num = fmt.parse(s);
System.out.println(num.doubleValue()); 

二、常用类

Objects类

Object类是所有java类的顶层父类(祖宗类),但是Objects是从Java7开始新增的一个对于java对象进行空指针安全操作的工具类

  • Objects类是一个工具类
  • Objects对外不提供构造器,因此无法创建对象
  • Objects类中的所有方法都是static
  • 提供的是对于对象的空指针安全操作的方法
User u1 = new User(1, "softeem", "123456");
User u2 = null;

//		if(u2 != null) {		
//			System.out.println(u2.equals(u1));
//		}
//空指针安全的对象比较
System.out.println(Objects.equals(u1, u2));
//空指针安全的toString
System.out.println(Objects.toString(u2)); // 对象若为null则输出“null”字符串
System.out.println(Objects.toString(u2,"0")); //对象若为null则输出“0”

//空指针安全的hashCode
System.out.println(Objects.hashCode(u2));
//为空判断
System.out.println(Objects.isNull(u2));
//不为空判断
System.out.println(Objects.nonNull(u2));
//检查对象不为空,若为空则抛出NullPointerException
System.out.println(Objects.requireNonNull(u2));

Random类

Math类中提供了一个random()的方法,用于随机一个从0.0~1.0之间的浮点数,当实际需求有个性化的要求时,只能通过计算获取其他类型的随机数,实际操作相对比较麻烦;因此,Java中还提供了另一个专门用于生成各种需求的随机数类型,比如,随机整数,随机浮点数,随机布尔值。
构造器:

  • Random()

常见方法:

  • nextBoolean():随机一个布尔值
  • nextDouble():随机一个0.0~1.0之间的double值
  • nextInt():随机一个int范围内的整数值
  • nextInt(int bounds):随机一个从0~bounds-1位的整数值
public class RandomDemo {

    private static final class RandomGeneratorHolder{
        static final Random randomNumberGenerator = new Random();
    }

    private static final String SOURCE = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

    //随机指定长度位验证码
    public static String genCode(int count) {
        String code = "";
        for(int i = 0;i < count;i++) {
            int index = RandomGeneratorHolder.randomNumberGenerator.nextInt(SOURCE.length());
            code += SOURCE.charAt(index);
        }
        return code;
    }

    public static void main(String[] args) {

        Random r = new Random();
        //随机布尔值
        boolean f = r.nextBoolean();
        System.out.println(f);

        //随机一个从0.0~1.0之间的浮点数
        double d = r.nextDouble();
        System.out.println(d);

        int i = r.nextInt();
        System.out.println(i);

        //随机一个5以内的整数
        i = r.nextInt(5);
        System.out.println(i);

        //随机一个5(含)~10(含)之间的整数
        i = r.nextInt(6) + 5;
        System.out.println(i);

        //随机生成指定长度的验证码,包含0-9A-Za-z之间的字符
        String code = genCode(6);
        System.out.println(code);
    }
}

File类

File类是来自于java.io包中的一个用于处理本机操作系统中的文件,文件可以是目录也可以是一个标准文件;File类是用于在Java中实现跟本机文件系统进行关联的操作类。其中包含一些常见的文件操作:创建文件/目录,查看文件的状态,删除文件等。

常见属性

常量 解释
pathSeparator 获取与本机系统相关的路径分隔符(windows是";"符号,Linux是”:“符号)
separator 获取与本机系统相关的目录分隔符(windows是”\“,Linux是”/“)

构造器

File(File parent, String child) 使用父目录所表示的File对象,结合子文件名构建新的File对象
File(String pathname) 根据提供的文件路径构建一个File对象
File(String parent, String child) 从父路径名字符串和子路径名字符串创建新的 File实例。
File(URI uri) 通过将给定的 file: URI转换为抽象路径名来创建新的 File实例。

常用方法

  • createNewFile() 创建新文件(标准文件)
  • exists() 判断File对象是否存在
  • delete() 删除File对象所表示的文件或者目录(空目录)
  • deleteOnExit() 当JVM结束时删除
  • getName() 获取File对象所表示的文件名称
  • getParent() 获取File所指的文件或者目录的父路径
  • isFile() 判断当前File所指的是否是标准文件
  • isDirectory() 判断当前File所指的是否是目录
  • isHidden() 判断当前File对象是否是隐藏目录
  • lastModified() 获取最后修改时间
  • length() 获取File所表示文件大小(目录为4096)
  • list() 获取File所表示目录下所有子文件的名称数组
  • listFiles() 获取File对象所表示目录下的所有子File数组
  • listFiles(FileFilter filter) 通过文件过滤器将File所表示目录中符合条件的File对象过滤出来
  • listRoots() 获取当前电脑可用的磁盘根
  • mkdir() 创建当前File所表示的目录(一级)
  • mkdirs() 创建当前File所表示的目录(多级)
  • renameTo(File file) 重命名文件

测试一:

public class FileDemo1 {

	public static void main(String[] args) throws IOException {
		
		//获取与本机系统相关的路径分割符
		System.out.println(File.pathSeparator); // windows是;		linux是:
		
		//获取与本机系统相关的名称(目录)分割符
		System.out.println(File.separator); //windows是 \ 			linux是 /
//		System.out.println(File.separatorChar);
		
		//通过java程序操作本地文件,在java中就必须存在文件对象(逻辑)
		
		//根据提供的文件的绝对路径(abstract path)获取文件对象
		File file = new File("D:/music_db.sql");
		//根据提供的文件相对路径(relative path)获取文件对象
		File file2 = new File("test/a.txt");
		
		//输出File对象所表示文件的绝对路径
		System.out.println(file.getAbsolutePath());
		System.out.println(file2.getAbsolutePath());
		
		//输出File对象所有表示文件的相对路径
		System.out.println(file.getPath());
		System.out.println(file2.getPath());
		
		//判断文件的可用性:可执行,可读,可写
		System.out.println(file2.canExecute());
		System.out.println(file2.canRead());
		System.out.println(file2.canWrite());
		
		//判断文件是否存在
		System.out.println("文件(或目录)是否存在-->"+file2.exists());
		
		file2 = new File("test/com");
		//创建新文件(标准文件)前提:父目录必须存在,否则IO异常
		boolean b = file2.createNewFile();
		System.out.println("文件创建结果--->"+b);
		
		file2 = new File("D:\\带班资料\\2020\\j2009\\code\\part1-javabase\\lesson17\\test\\org\\softeem\\demo");
		//创建目录(创建一级目录)
		System.out.println("一级目录创建结果--->"+file2.mkdir());
		System.out.println("创建多级目录--->"+file2.mkdirs()); 
		
//		Random r = new Random();
//		for (int i = 0; i < 1000; i++) {
//			File f3 = new File(file2, r.nextInt(10000)+""+i);
//			if(f3.mkdirs()) {
//				System.out.println(f3.getAbsolutePath()+"创建成功!");
//			}
//		}
	}

}

测试二:

public class FileDemo2 {

	public static void main(String[] args) throws IOException, InterruptedException {

		// 创建临时文件
//		File f = File.createTempFile("softeem", ".log",new File("test"));
//		System.out.println(f.getAbsolutePath());

//		if(f.exists()) {
//			立即删除文件
//			boolean b = f.delete();
//			System.out.println("删除结果:"+b);
//		}

		File f2 = new File("test/a.txt");
		// 删除目录的前提是目录下没有任何子文件或者子目录
//		System.out.println("删除目录-->"+f2.delete());
//		当jvm结束才删除 
//		f2.deleteOnExit();

//		System.out.println(5/0);

		// 休眠5秒
//		Thread.sleep(10000); 
		// 返回当前文件所在磁盘的剩余空间(字节)
		long size = f2.getFreeSpace();
		System.out.println(size / (1024 * 1024 * 1024) + "GB");
		
		//获取当前文件所在磁盘的总空间大小
		size = f2.getTotalSpace();
		System.out.println(size / (1024 * 1024 * 1024) + "GB");
		
		//获取file对象所表示的文件名称
		System.out.println(f2.getName());
		//获取File对象所表示文件所在的父目录名称(String)
		System.out.println(f2.getParent());
		//获取File对象所表示文件所在的父目录File对象(File)
		System.out.println(f2.getParentFile());
		
		

		// 实现一个程序:要求能清理指定目录下的所有字节码文件(清理缓存)包括多余的空目录
		// 1. 如何获取一个目录下的所有子文件或者子目录(递归)
		// 2. 如何获取所有的字节码文件(以 .class 结尾的文件,String类)
	}

}

文件过滤

File类中提供了几个用于列出指定目录下所有子文件的方法:

  • list()
  • listFiles()

以上两个方法不会对文件进行任何的限制和过滤,直接全部遍历,但是以上两个方法还提供了对应的重载方法

  • list(FilenameFilter filter)
  • listFiles(FileFilter filter)
  • listFiles(FilenameFilter filter)

过滤方式一

以listFiles(FileFilter filter)为例,使用方法如下:

File f = new File("D:\\素材\\音乐\\music素材");

//创建过滤器对象
FileFilter filter = new FileFilter() {
    //回调函数
    @Override
    public boolean accept(File f) {
        return f.getName().endsWith(".mp3"); 
    }
};
File[] files = f.listFiles(filter);

for (File file : files) {
    System.out.println(file.getName());
}

以上程序执行之后会获取所有的mp3文件,实现原理如下:
Java 基础语法学习总结
以上程序的实现,使用了匿名内部类,回调机制

过滤方式二:

创建文件过滤器:

/**
 * 	文件过滤器
 * @author mrchai
 */
public class MyFileFilter implements FileFilter {

	private String suffix;
	
	public MyFileFilter(String suffix) {
		this.suffix = suffix;
	}
	
	/**
	 * 	实现过滤规则
	 */
	@Override
	public boolean accept(File f) {	
		return f.getName().endsWith(suffix);
	}

}

测试类:

public class FileDemo5 {

	public static void main(String[] args) {
		
		File f = new File("D:\\素材\\音乐\\music素材");
		//创建过滤器对象
		FileFilter filter = new MyFileFilter(".jpg");
        //使用过滤器进行文件过滤
		File[] files = f.listFiles(filter);
		
		for (File file : files) {
			System.out.println(file.getName());
		}

	}

}

文件递归遍历

在对系统文件加遍历时往往会遇到一种情况:需要将一个目录下的所有子文件全部获取,但是该目录下可能还存在子目录,以及多个子目录嵌套的情况;而具体的嵌套层次是未知的,因此无法使用传统的循环语句进行遍历,此时,最好的方式可以通过递归实现遍历:

public class FileDemo6 {

    /**
	 * 	读取指定目录中的所有子文件
	 * @param dir 源目录
	 */
    public static void readDir(File dir) {
        //获取目录下所有的文件对象(数组)
        File[] files = dir.listFiles();
        //判断数组对象是否为null
        if(Objects.nonNull(files)) {
            //遍历所有的File对象
            for(File file:files) {
                //判断当前File对象所表示的是否一个目录
                if(file.isDirectory()) {
                    //如果是目录,则递归遍历
                    readDir(file);
                }
                //输出文件(或者目录)名称
                System.out.println(file.getName());

            }
        }
    }


    public static void main(String[] args) {
        //源目录
        File dir = new File("D:\\java");
        //读取
        readDir(dir);
    }

}

三、常用类(如期类&正则表达式)

日期处理类

在程序开发中日期的处理十分常见,比如:订单的创建时间,用户注册时间,某项具体操作的触发时间等;因此java中也提供了丰富用于日期时间处理的工具类:

  • java.util.Date(jdk1.0)
  • java.util.Calendar(jdk1.1)
  • java.time.LocalDate(jdk8)
  • java.time.LocalTime(jdk8)
  • java.time.LocalDateTime(jdk8)

Date类

java.util.Date是一个传统的java用于处理日期时间的类,由于版本更新,内部有很多构造器包括方法均已标记为过时,取而代之的是java.util.Calendar类;

常用构造器

  • Date():获取当前系统时间所表示的日期对象
  • Date(long time):根据提供的时间毫秒数,构建一个日期对象(从1970年1月1日 00:00:00开始)
Date d = new Date();

//		d = new Date(120,11,1);

d = new Date(System.currentTimeMillis());
System.out.println(d);

//获取十分钟之后的日期对象  1sec = 1000 
d = new Date(System.currentTimeMillis() + 600000);
System.out.println(d);

常用方法

  • after(Date d)
  • before(Date d)
  • compareTo(Date d)
  • getTime()
//判断当前日期对象是否是参数日期对象之后的日期
System.out.println(d1.after(d2));

System.out.println(d1.before(d2));


//实现一个小程序,判断两个学生的年龄,输出较大学生的信息?
//学生:学号,姓名,生日(1999/11/11)

Date d3 = new Date();
System.out.println(d3.equals(d1));
//获取当前日期对象所表示的时间毫秒数
System.out.println(d3.getTime());
System.out.println(d1.getTime());

//		System.out.println(d3.getYear());

日期格式化(DateFormat & SimpleDateFormat)

DateFormat类来自java.text包下的用于进行日期时间格式化处理的类,是一个抽象类,内部提供了一系列用于获取格式化对象的静态方法:

  • getDateInstance()
  • getTimeInstance()
  • getDateTimeInstance()
  • getInstance()

SimpleDateFormat

DateFormat中提供的静态方法可以用户获取各种丰富的格式化对象,但是针对一些需求DateFormat并不能完全满足,比如需要如下格式日期:

  • 20201120
  • 2020年11月20日 09时48分50秒
  • 10:11:35

通过观察DateFormat中getInstance()源码得知,内部使用了一个子类SimpleDateFormat实现,SimpleDateFormat可以通过定制化的匹配模式来匹配不同的日期时间输出格式:

Java 基础语法学习总结
实例参考:
Java 基础语法学习总结
使用方法

Date d = new Date();

//20201120
DateFormat fmt = new SimpleDateFormat("yyyyMMdd");
time = fmt.format(d);
System.out.println(time);

//2020年11月20日 09时48分50秒
fmt = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒 SSS毫秒");
time = fmt.format(d);
System.out.println(time);

//[10:05:55]
fmt = new SimpleDateFormat("[HH:mm:ss]");
System.out.println(fmt.format(d));

/********将String类型的日期时间转换为Date类型对象********/
String dateTime = "1999/11/11 09:10:11";
fmt = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
//将字符串类型的时间根据指定的格式解析为Date对象
Date date = fmt.parse(dateTime);
System.out.println(date);

Calendar

从JDK1.1开始java.util.Date中的很多方法和构造器标记为过时,同时jdk新增了对应的替代方案:java.util.Calendar,是一个抽象类,有一个直接子类:java.util.GregorianCalendar对其继承,并且实现其中的抽象方法,常见的Calendar实例获取方式为直接使用内部提供的静态方法getInstance()

具体使用如下:

//以当前系统时间为基础获取日历实例  +8
Calendar c = Calendar.getInstance();		
System.out.println(c);

常见字段

  • YEAR
  • MONTH
  • DAY_OF_MONTH
  • HOUR
  • MINITE
  • SECOND
  • DAY_OF_WEEK

常见方法

  • get(int field)
  • set(int field,int value)
  • getActualMaximum(int field)

具体用法

//以当前系统时间为基础获取日历实例  +8
Calendar c = Calendar.getInstance();		
System.out.println(c);

//设置日历到2019年11月20日?
//		c.set(2019, 10, 20);
//设置日历对象中指定字段的值
c.set(Calendar.YEAR, 2019);

System.out.println("获取日历表示的年份:"+c.get(Calendar.YEAR));  
System.out.println("获取日历表示的月份(月份从0开始):"+c.get(Calendar.MONTH));  
System.out.println("获取日历表示的日期"+c.get(Calendar.DAY_OF_MONTH));  
System.out.println("获取日历表示的小时"+c.get(Calendar.HOUR_OF_DAY));  
System.out.println("获取日历表示的分钟"+c.get(Calendar.MINUTE));  
System.out.println("获取日历表示的秒钟"+c.get(Calendar.SECOND));  
System.out.println("获取日历表示的毫秒"+c.get(Calendar.MILLISECOND)); 

System.out.println("获取当前日历表示的日期是今年的第多少天:"+c.get(Calendar.DAY_OF_YEAR));

System.out.println("获取日历所有表示的时间毫秒数"+c.getTimeInMillis());

//设置月份为12月
c.set(Calendar.MONTH, 11);
//获取当前日期所表示字段的可能最大值
int maxDay = c.getActualMaximum(Calendar.DAY_OF_MONTH);
System.out.println(maxDay); //31

int maxMonth = c.getActualMaximum(Calendar.MONTH);
System.out.println(maxMonth);//11  (0~11)
//获取小时(24小时制)的最大值
int maxHour = c.getActualMaximum(Calendar.HOUR_OF_DAY);
System.out.println(maxHour); //23	(0~23)
//获取小时(12小时制)的最大值
maxHour = c.getActualMaximum(Calendar.HOUR);
System.out.println(maxHour);

//计算从你出生到现在一共生活了多少天?(生命倒计时)  1000 * 60 * 60 * 24
c.set(1999, 10, 11); 
long start = c.getTimeInMillis();
long now = System.currentTimeMillis();

long days = (now - start) / (1000 * 60 * 60 * 24);
System.out.println(days);

//将日历的日期设置为1号(设置为这个月的第一天)
c.set(Calendar.DAY_OF_MONTH, 1);

System.out.println(c.get(Calendar.DAY_OF_WEEK));

GregorianCalendar gc = (GregorianCalendar)c;
//判断指定年份是否是闰年(该方法为GregorianCalendar特有,因此需要将Calendar强制转换)
boolean leapYear = gc.isLeapYear(2020);
System.out.println(leapYear);

//将Calendar转换为java.util.Date
Date date = c.getTime();
System.out.println(date);

正则表达式

正则表达式( Regular expression)是一组由字母和符号组成的特殊文本, 它可以用来从文本中找出满足你想要的格式的句子。正则表达式最早源自于perl语言(脚本语言);正则表达式的功能十分强大,可以用于进行文本的匹配,检索,替换;常见于一些网络爬虫。

java中对于正则表达式的处理主要由一下三个类实现:

  • java.lang.String
  • java.util.regex.Pattern
  • java.util.regex.Macher

简单案例:

String a = "13567845635";
//判断两个字符串是否完全一致
System.out.println(a.equals("13567845634"));
//判断当前String对象是否匹配给定的正则表达式( 匹配手机号格式字符串)
System.out.println(a.matches("^1\\d{10}$"));

匹配元字符

元字符 说明
. 匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线或汉字
\s 匹配任意的空白符
\d 匹配数字
\b 匹配单词的开始或结束
^ 匹配字符串的开始
$ 匹配字符串的结束
//.用于匹配出回车换行之外的其他任意单个字符(数字,字母,符号)
System.out.println("!".matches("."));  //true

//匹配所有的数字,字母,不支持符号
System.out.println("a".matches("\\w")); //true

//匹配除了数字,字母外的其他符号
System.out.println("\t".matches("\\W"));//true

//匹配空格
System.out.println(" ".matches("\\s"));//true

//匹配非空格
System.out.println(" ".matches("\\S"));//false

//匹配数字
System.out.println("0".matches("\\d"));//true
//匹配非数字
System.out.println("a".matches("\\D"));//true

//边界匹配,匹配是否以指定字符开头
System.out.println("abc".matches("\\babc")); //true
System.out.println("a".matches("^a")); //true

//边界匹配,匹配是否以指定的字符结尾
System.out.println("d".matches("d$")); //false

匹配长度

语法 说明
* 重复零次或更多次
+ 重复一次或更多次
重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次
//匹配输入长度为4的数字要求必须以1开头

System.out.println("1234".matches("^1\\d{3}"));

//验证输入的是否是qq号  5-11位长度数字,不能以0开始
System.out.println("12334352324".matches("^[1-9]\\d{4,10}"));

// \\w
System.out.println("a".matches("[0-9a-zA-Z]")); 

//匹配不能少于5位长度的数字
System.out.println("12387912".matches("\\d{5,}"));

//匹配不能超过6位长度的数字
System.out.println("12346".matches("\\d{0,6}"));

//匹配字母0个或多个
System.out.println("".matches("[a-zA-Z]*")); 

//匹配字母1个或多个
System.out.println("a".matches("[a-zA-Z]+"));

//匹配字母0个或1个
System.out.println("ac".matches("[a-zA-Z]?"));

Pattern&Matcher

String content = "adfasdffdiu1353457839417812341023u901223423418712312317823sdfsd";
//手机号正则表达式
String regex = "1\\d{10}"; 
//编译正则表达式获取匹配模式对象
Pattern p = Pattern.compile(regex);
//对指定的输入内容进行匹配并且获取匹配器
Matcher m = p.matcher(content);
//直接匹配
//System.out.println(m.matches());
//搜索是否存在匹配的组
while(m.find()) {	
    //取出当前匹配到的组
    String s = m.group();
    System.out.println(s);

正则表达式在线测试网站

  • https://regexr.com/
  • https://regex101.com/

四、常用类(综合实践)

1、微信红包(拼手气红包)

基于BigDecimal类实现微信红包算法的功能,比如设置红包总金额,然后设置需要生成的红包个数,为每个红包随机指定金额,最低不能低于0.01元,要求:

  1. 每个红包金额随机指定
  2. 每个红包金额不能低于0.01元
  3. 要求每个红包的金额之和恰好等于总金额
  4. 如果平均每个红包的金额不足0.01元时抛出一个RedPacketException,提示每个红包金额不能少于0.01元

红包类

/**
 * 	红包类
 * @author mrchai
 *
 */
public class RedPacket {

	/**红包ID*/
	private int id;
	/**红包金额*/
	private BigDecimal money;
	
	public RedPacket() {
	}

	public RedPacket(int id, BigDecimal money) {
		super();
		this.id = id;
		this.money = money;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public BigDecimal getMoney() {
		return money;
	}

	public void setMoney(BigDecimal money) {
		this.money = money;
	}

	@Override
	public String toString() {
		return id+"号用户获得"+money+"元";
	}
	
}

红包异常类

/**
 * 	红包异常
 * @author mrchai
 */
public class RedpacketException extends Exception{

	public RedpacketException() {
		// TODO Auto-generated constructor stub
	}
	
	public RedpacketException(String msg) {
		super(msg);
	}
}

红包管理

public class RedPacketManage {

    /** 设置每个红包最小金额 */
    static final BigDecimal MIN = new BigDecimal("0.01");

    /*
	 * @double total 总金额
	 * @int count 红包个数
	 * @return 返回生成的所有红包金额集合
	 */
    public static ArrayList<RedPacket> genRedPacket(double total, int count) throws RedpacketException {
        // 声明临时变量用于存储所有随机的红包对象
        ArrayList<RedPacket> packets = new ArrayList<RedPacket>();
        // 计算每个红包分配最低金额一共需要多少钱
        double min = MIN.multiply(new BigDecimal(count)).setScale(2, BigDecimal.ROUND_HALF_EVEN).doubleValue();
        if (min > total) {
            // 红包金额不够分配时,抛出异常
            throw new RedpacketException("每个红包金额不能少于0.01元");
        } else if (min == total) {
            // 红包金额恰好每人只够分配0.01元,则平均分配
            for (int i = 0; i < count; i++) {
                // 创建红包对象
                RedPacket item = new RedPacket(i + 1, new BigDecimal("0.01"));
                // 将红包加入集合
                packets.add(item);
            }
        } else {
            // 当总金额大于每人最少金额之和时,随机分配
            // 将总金额包装为BigDecimal
            BigDecimal totalMoney = new BigDecimal(total);
            //声明临时变量统计当前分配的金额总数
            BigDecimal now = new BigDecimal(0);
            // 获取每个红包的比例
            double[] scale = randomScale(count);
            // 为前count-1个红包分配金额
            for (int i = 0; i < count - 1; i++) {
                // 获取当前比例红包需要分配的金额
                BigDecimal item = totalMoney.multiply(new BigDecimal(scale[i]))
                    .setScale(2, BigDecimal.ROUND_HALF_EVEN);
                packets.add(new RedPacket(i + 1, item));
                //累计已分配金额总数
                now = now.add(item);
            }
            // 剩余的金额给最后一个
            //获取剩余的金额
            BigDecimal last = totalMoney.subtract(now);
            packets.add(new RedPacket(count, last));
        }
        return packets;
    }

    /**
	 * 	随机红包金额比例
	 * @param count 红包的份数
	 * @return 每份红包的比例数组
	 */
    private static double[] randomScale(int count) {
        // 临时数组存储所有红包的金额比例
        double[] scale = new double[count];
        Random r = new Random();
        double total = 0.0;
        for (int i = 0; i < count; i++) {
            // 为每一个元素设置一个1-100随机数
            scale[i] = r.nextInt(100) + 1;
            // 累计所有随机的数值
            total += scale[i];
        }
        // 循环计算每个红包的金额比例
        for (int i = 0; i < count; i++) {
            scale[i] = scale[i] / total;
        }
        return scale;
    }

}

测试类

p ublic static void main(String[] args) throws RedpacketException {
    ArrayList<RedPacket> list = genRedPacket(5, 10);
    BigDecimal t = new BigDecimal(0);
    for (RedPacket rp : list) {
        System.out.println(rp);
        t= t.add(rp.getMoney());
    }
    System.out.println(t);
}

运行结果

1号用户获得0.702号用户获得0.133号用户获得0.464号用户获得0.505号用户获得0.596号用户获得0.927号用户获得0.028号用户获得0.119号用户获得0.6510号用户获得0.925.00

2、斗地主发牌

参考斗地主的游戏规则,完成一个发牌的功能(54张牌,考虑点数,花色;三名玩家,其中地主比其他玩家多3张牌)

牌类

/**
 * 3-10 J Q K A 2 Queen King 牌类
 * 
 * @author mrchai
 */
public class Card {

	/** 牌面值 */
	private String name;
	/** 花色 */
	private String flower;
	/** 大小点数 */
	private int num;

	public Card() {
	}

	public Card(String name, String flower, int num) {
		super();
		this.name = name;
		this.flower = flower;
		this.num = num;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getFlower() {
		return flower;
	}

	public void setFlower(String flower) {
		this.flower = flower;
	}

	public int getNum() {
		return num;
	}

	public void setNum(int num) {
		this.num = num;
	}

	@Override
	public String toString() {
		if (Objects.nonNull(flower)) {
			return name + "-" + num + "-" + flower;
		}
		return name + "-" + num;
	}

}

玩家类

/**
 * 	玩家类
 * @author mrchai
 */
public class Player {

	/**玩家id*/
	private int id;
	/**玩家姓名*/
	private String name;
	/**是否地主*/
	private boolean boss;
	/**牌集合*/
	private ArrayList<Card> cards=new ArrayList<Card>();
	
	public Player() {
	}
	
	public Player(int id, String name, boolean boss, ArrayList<Card> cards) {
		super();
		this.id = id;
		this.name = name;
		this.boss = boss;
		this.cards = cards;
	}

	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public boolean isBoss() {
		return boss;
	}
	public void setBoss(boolean boss) {
		this.boss = boss;
	}
	public ArrayList<Card> getCards() {
		return cards;
	}
	public void setCards(ArrayList<Card> cards) {
		this.cards = cards;
	}

	@Override
	public String toString() {
		return name+(boss?"(地主)":"")+":"+cards;
	}
	
}

游戏管理类

public class GameManage {

	/**声明所有牌的集合*/
	private static ArrayList<Card> all = new ArrayList<>();
	/**用于生成牌的牌面值*/
	private static String[] names = {"3","4","5","6","7","8","9","10","J","Q","K","A","2","Queen","King"};
	/**用于生成牌的花色*/
	private static String[] flowers = {"红桃","方块","梅花","黑桃"};
	/**所有玩家集合*/
	private ArrayList<Player> players = new ArrayList<Player>();
	/**声明随机数生成器*/
	private static Random randomGen = new Random();
	
	static {
		/******初始化所有牌******/
		//笛卡尔积
		int i = 0;
		for (; i < names.length-2; i++) {
			for (int j = 0; j < flowers.length; j++) {
				Card c = new Card(names[i], flowers[j], i);
				all.add(c);
			}
		}
		//将大小王加入
		all.add(new Card(names[names.length-2],null,i++));
		all.add(new Card(names[names.length-1],null,i++));
	}
	
	/**
	 * 	添加玩家
	 */
	public void addPlayer() {
		Scanner sc = new Scanner(System.in);
		System.out.println("请输入玩家1名称:");
		String name1 = sc.nextLine();
		System.out.println("请输入玩家2名称:");
		String name2 = sc.nextLine();
		System.out.println("请输入玩家3名称:");
		String name3 = sc.nextLine();
		
		Player p1 = new Player();
		p1.setId(1);
		p1.setName(name1);
		
		Player p2 = new Player();
		p2.setId(2);
		p2.setName(name2);
		
		Player p3 = new Player();
		p3.setId(3);
		p3.setName(name3);
		//将三名玩家加入集合
		players.add(p1);
		players.add(p2);
		players.add(p3);
	}
	
	/**
	 * 	随机地主
	 */
	public void randomBoss() {
		//添加玩家
		addPlayer();
		//随机地主索引
		int i = randomGen.nextInt(players.size());
		//设置指定位置的玩家为地主
		players.get(i).setBoss(true);
	}
	
	/**
	 * 	发牌
	 */
	public ArrayList<Player> sendCard() {
		//随机地主
		randomBoss();
		//对每一名玩家遍历
		for (Player p : players) {
			//先为每一位玩家随机发17张牌
			for (int i = 0; i < 17; i++) {
				//随机一张牌的索引
				int cardIndex = randomGen.nextInt(all.size());
				//获取随机索引位置的牌
				Card card = all.get(cardIndex);
				//将随机的牌加入当前遍历玩家的集合
				p.getCards().add(card);
				//从源集合中移除这张牌
				all.remove(card);
			}
		} 
		//最后三张牌给地主
		for (Player p : players) {
			if(p.isBoss()) {
				//将all集合中的所有元素加入地主的集合
				p.getCards().addAll(all);
			}
		}
		return players;
	}
	
}

测试类

public static void main(String[] args) {
    ArrayList<Player> players = new GameManage().sendCard();
    for (Player p : players) {
        System.out.println(p);
    }
}

运行结果

请输入玩家1名称:
张三
请输入玩家2名称:
李四
请输入玩家3名称:
王五
张三:[A-11-梅花, 4-1-梅花, 9-6-红桃, 8-5-梅花, A-11-方块, 8-5-黑桃, 6-3-红桃, 6-3-方块, K-10-方块, Queen-13, 10-7-梅花, K-10-梅花, Q-9-梅花, 2-12-红桃, Q-9-红桃, 6-3-梅花, 10-7-黑桃]
李四:[5-2-黑桃, 9-6-黑桃, A-11-黑桃, 4-1-红桃, 5-2-红桃, 3-0-红桃, 5-2-方块, A-11-红桃, 6-3-黑桃, 5-2-梅花, 7-4-红桃, K-10-黑桃, 8-5-方块, 4-1-黑桃, 3-0-方块, Q-9-方块, 8-5-红桃]
王五(地主):[10-7-方块, 2-12-黑桃, 7-4-梅花, 2-12-方块, 2-12-梅花, 4-1-方块, 7-4-黑桃, Q-9-黑桃, J-8-梅花, 7-4-方块, 3-0-梅花, J-8-方块, 3-0-黑桃, J-8-黑桃, 10-7-红桃, J-8-红桃, King-14, 9-6-方块, 9-6-梅花, K-10-红桃]

本文地址:https://blog.csdn.net/qq_44298719/article/details/109895912

相关标签: java学习 java