笔记

  • 一·Java程序基础
    • 代码练习合集
      • 1.Hello,world
      • 2.BMI
      • 3.Grade
      • 4.InputNum
      • 5.Gamecaiquan
      • 6.Main
      • 7.Reserve_arrays
      • 8.Sum_dowhile
      • 9.Sum
      • 10.Sumnum
      • 11.Unicode
      • 12.PrimaryStudent
  • 二·面向对象编程
    • 代码练习合集
      • 1.Tax
      • 2.Tax_abstract
      • 3.Tax_interface
      • 4.Static
      • 5.Insert(StringBuilder)
      • 6.Select(StringJoiner)
  • 三.异常处理
    • 代码练习合集
      • 1.Exception_catch(捕获异常)
      • 2.Exception_throw(抛出异常)
      • 3.Exception_custom(自定义异常)
      • 4.JDK Logging
      • 5.Commons_logging(配合commons-logging-1.2.jar命令行执行)`
  • 4.反射
    • 代码练习合集
      • 1.Reflect_field
      • 2.Reflect_method
  • 5.注解
    • 代码练习合集
      • 1. Annotation_range_check
  • 6.泛型
    • 代码练习合集
      • 1.List
      • 2.复写equals方法
      • 3.Map
      • 4.Set
      • 5.Priority_queue
  • 8.IO
    • 代码练习合集
      • 1.File
  • 9.时间与日期
    • 练习代码合集
      • 1.Flight_time
  • 10.单元测试
    • 代码练习合集
      • 1.Junit测试
      • 2.Fixture
      • 3.异常测试
  • 11.正则表达式
    • 代码练习合集
      • 1.电话匹配练习
      • 2.分组匹配
      • 3.搜索和替换
  • 12.加密与安全
    • 代码练习合集
      • 1.:使用BouncyCastle提供的RipeMD160
  • 13.多线程
  • 14.Maven基础
  • 15.JDBC编程
    • 代码练习合集
      • 1.
/*** Learn Java from https://www.liaoxuefeng.com/* * @author liaoxuefeng*/

一·Java程序基础

1.一个Java源码只能定义一个public类型的class,并且class名称和文件名要完全一致;2.使用javac可以将.java源码编译成.class字节码;3.使用java可以运行一个已编译的Java程序,参数是类名。4.类用大写英文字母开头5.方法用小写英文字母开头6./* 多行注释 */7.快速格式化代码 Ctrl+Shift+f8.final(常量修饰符) int A(常量名大写);9.变量类型太长,可使用var定义变量,编译器自动补全10.超出范围的强制转型可能会得到错误的结果11.\u#### 表示一个Unicode编码的字符12.可以使用+连接任意字符串和其他数据类型13.注意要区分空值null和空字符串"",空字符串是一个有效的字符串对象,它不等于null
【当String = ""+A+B,A,B均不是String类型,需要在前面加个空字符串】14.例数组使用: int[] ns = new int[5](数组变量初始化必须使用new int[5]表示创建一个可容纳5个int元素的数组。)也可以在定义数组时直接指定初始化的元素---int[] ns = new int[] { 68, 79, 91, 85, 62 };还可以进一步简写为:int[] ns = { 68, 79, 91, 85, 62 };15.要判断引用类型的变量内容是否相等,必须使用equals()方法:if (s1.equals(s2))16.JAVA14 switch新语法: switch(A):case "A" -> System.out.println("XXXX");新语法使用->,如果有多条语句,需要用{}括起来。不要写break语句,因为新语法只会执行匹配的语句,没有穿透效应。还可以直接返回值:case "apple" -> 1; 或用yield返回一个值作为switch语句的返回值:int code = 1;yield code; // switch语句返回值17.for each循环能够遍历所有“可迭代”的数据类型-------------------------->for (int n : ns) 在for (int n : ns)循环中,变量n直接拿到ns数组的元素,而不是索引     System.out.println(n);18.打印二维数组ns[][]------>for (int[] arr : ns) {for (int n : arr) {System.out.print(n);System.out.print(', ');}System.out.println();}19.命令行参数类型是String[]数组;命令行参数由JVM接收用户输入并传给main方法;如何解析命令行参数需要由程序自己实现。

代码练习合集

1.Hello,world

public class Hello {public static void main(String[] args) {System.out.println("Helo,world!");}
}

2.BMI

//计算体质指数BMI
import java.util.Scanner;public class BMI {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);System.out.print("请输入体重(单位kg):");float weight = scanner.nextFloat();System.out.print("请输入身高(单位m):");float high = scanner.nextFloat();double yourBMI = weight/Math.pow(high,2);if((float)yourBMI < 18.5) {System.out.printf("BMI为:%1f,过轻!",yourBMI);}else if(yourBMI <= 25) {System.out.printf("BMI为:%.1f,正常!",yourBMI);}else if(yourBMI <= 28) {System.out.printf("BMI为:%.1f,过重!",yourBMI);}else if(yourBMI <= 32) {System.out.printf("BMI为:%.1f,肥胖!",yourBMI);}else {System.out.printf("BMI为:%.1f,非常肥胖!",yourBMI);}}
}

3.Grade

//输出成绩提高百分比
import java.util.Scanner;public class Grade {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);System.out.print("请输入去年的成绩:");int lastgrade = scanner.nextInt();System.out.print("请输入今年的成绩:");int nowgrade = scanner.nextInt();float up = (float)(nowgrade-lastgrade)/lastgrade*100;System.out.printf(up >= 0 ? "成绩提高%.2f%%\n": "成绩下降%.2f%%\n",Math.abs(up));       }
}

4.InputNum

public class InputNum {public static void main(String[] args) {int x = 100;System.out.println(x);}
}

5.Gamecaiquan


//石头剪刀布游戏
import java.util.Scanner;
import java.util.Random; //引入随机数public class Gamecaiquan {public static void main(String[] args) {Scanner scanner = new Scanner(System.in); // 创建Scanner对象Random rand = new Random(); // 创建Random对象System.out.print("选择数字--》石头(1)or剪刀(2)or布(3):");int own = scanner.nextInt();int sys = rand.nextInt(3) + 1;switch (own) {case 1 -> {if (sys == 1) {System.out.println("平手!");} else if (sys == 2) {System.out.println("获胜!");} else {System.out.println("失败!");}}case 2 -> {if (sys == 2) {System.out.println("平手!");} else if (sys == 3) {System.out.println("获胜!");} else {System.out.println("失败!");}}case 3 -> {if (sys == 3) {System.out.println("平手!");} else if (sys == 1) {System.out.println("获胜!");} else {System.out.println("失败!");}}            default -> System.out.println("输入不合规范!");}}
}

6.Main

//计算一元二次方程两个解
public class Main {public static void main(String[] args) {double a = 1.0,b = 3.0,c = -4.0;double r1,r2;System.out.println("r1 = " + (-b+Math.sqrt(b*b-4*a*c))/(2*a));System.out.println("r2 = " + (-b-Math.sqrt(b*b-4*a*c))/(2*a));}
}

7.Reserve_arrays

//倒序遍历数组bingdyin
public class Reserve_arrays {public static void main(String[] args) {int[] ns = {1,4,9,16,25};for(int i = ns.length - 1;i >= 0;i-- ) {System.out.println(ns[i]);}}
}

8.Sum_dowhile

//使用do while计算从m到n的和
public class Sum_dowhile {public static void main(String[] args) {int sum = 0,m = 20,n = 100;do{sum = sum + m;m++;}while(m <= n);System.out.println(sum);}
}

9.Sum

//使用while计算从m到n的和
public class Sum {public static void main(String[] args) {int sum = 0,m = 20,n = 100;while(m <= n) {sum = sum + m;m++;}System.out.println(sum);}
}

10.Sumnum

//计算前n个自然数的和
public class Sumnum {public static void main(String[] args) {int x = 100;int i, sum = 0;for (i = 1; i <= x; i++) {sum = sum + i;}System.out.println(sum);}
}

11.Unicode

//用Unicode编码拼写字符串
public class Unicode {public static void main(String[] args) {int a = 72,b = 105,c = 65281;char a1 = '\u0048';char b1 = '\u0069';char c1 = '\uff01';String s ="" + a1 + b1 + c1;System.out.println(s);}
}

12.PrimaryStudent

//判断指定年龄是否是小学生
public class PrimaryStudent {public static void main(String[] args) {int age = 7;boolean isPrimaryStudent = (age >= 6) && (age <= 12);System.out.println(isPrimaryStudent ? "Yes" : "No");}
}

二·面向对象编程

1.定义private方法的理由是内部方法是可以调用private方法,不允许外部调用;在方法内部,可以使用一个隐含的变量this,它始终指向当前实例。2.调用构造方法,必须用new操作符;可以定义多个构造方法,在通过new操作符调用的时候,编译器通过构造方法的参数数量、位置和类型自动区分;一个构造方法可以调用其他构造方法,这样做的目的是便于代码复用。调用其他构造方法的语法是this(…)。3.方法重载是指多个方法的方法名相同,但各自的参数不同;重载方法应该完成类似的功能,参考String的indexOf();重载方法返回值类型应该相同。4.为了让子类可以访问父类的字段,我们需要把private改为protected;protected关键字可以把字段和方法的访问权限控制在继承树内部,一个protected字段和方法可以被其子类,以及子类的子类所访问。5.从Java 14开始,判断instanceof后,可以直接转型为指定变量,避免再次强制转型public class Main {public static void main(String[] args) {Object obj = "hello";if (obj instanceof String s) {// 可以直接使用变量s:System.out.println(s.toUpperCase());}}}6.Java只允许单继承,所有类最终的根类是Object;子类的构造方法可以通过super()调用父类的构造方法;可以安全地向上转型为更抽象的类型;可以强制向下转型,最好借助instanceof判断;子类和父类的关系是is,has关系不能用继承。7.final 可以实现不允许修改方法不允许修改字段父类加final则不允许继承8.通过abstract定义的方法是抽象方法,它只有定义,没有实现。抽象方法定义了子类必须实现的接口规范;定义了抽象方法的class必须被定义为抽象类,从抽象类继承的子类必须实现抽象方法;如果不实现抽象方法,则该子类仍是一个抽象类;面向抽象编程使得调用者只关心抽象方法的定义,不关心子类的具体实现。9.Java的接口(interface)定义了纯抽象规范,一个类可以实现多个接口;接口也是数据类型,适用于向上转型和向下转型;接口的所有方法都是抽象方法,接口不能定义实例字段;接口可以定义default方法(JDK>=1.8)。10.包没有父子关系。java.util和java.util.zip是不同的包,两者没有任何继承关系。 11. 静态字段属于所有实例“共享”的字段,实际上是属于class的字段;调用静态方法不需要实例,无法访问this,但可以访问静态字段和其他静态方法;静态方法常用于工具类和辅助方法。12.Java内建的package机制是为了避免class命名冲突;JDK的核心类使用java.lang包,编译器会自动导入;JDK的其它常用类定义在java.util.*,java.math.*,java.text.*,……;包名推荐使用倒置的域名,例如org.apache。13.Java内建的访问权限包括public、protected、private和package权限;Java在方法内部定义的变量是局部变量,局部变量的作用域从变量声明开始,到一个块结束;final修饰符不是访问权限,它可以修饰class、field和method;一个.java文件只能包含一个public类,但可以包含多个非public类14.JVM通过环境变量classpath决定搜索class的路径和顺序;不推荐设置系统环境变量classpath,始终建议通过-cp命令传入;jar包相当于目录,可以包含很多.class文件,方便下载和使用;MANIFEST.MF文件可以提供jar包的信息,如Main-Class,这样可以直接运行jar包。15.Java 9引入的模块目的是为了管理依赖;使用模块可以按需打包JRE;使用模块对类的访问权限有了进一步限制。
16.Java字符串String是不可变对象;字符串操作不改变原字符串内容,而是返回新字符串;常用的字符串操作:提取子串、查找、替换、大小写转换等;Java使用Unicode编码表示String和char;转换编码就是将String和byte[]转换,需要指定编码;转换为byte[]时,始终优先考虑UTF-8编码。17.StringBuilder是可变对象,用来高效拼接字符串;StringBuilder可以支持链式操作,实现链式操作的关键是返回实例本身;StringBuffer是StringBuilder的线程安全版本,现在很少使用。18.Java核心库提供的包装类型可以把基本类型包装为class;自动装箱和自动拆箱都是在编译期完成的(JDK>=1.5);装箱和拆箱会影响执行效率,且拆箱时可能发生NullPointerException;包装类型的比较必须使用equals();整数和浮点数的包装类型都继承自Number;包装类型提供了大量实用方法。19.JavaBean是一种符合命名规范的class,它通过getter和setter来定义属性;属性是一种通用的叫法,并非Java语法规定;可以利用IDE快速生成getter和setter;使用Introspector.getBeanInfo()可以获取属性列表20.Java使用enum定义枚举类型,它被编译器编译为final class Xxx extends Enum { … };通过name()获取常量定义的字符串,注意不要使用toString();通过ordinal()返回常量定义的顺序(无实质意义);可以为enum编写构造方法、字段和方法enum的构造方法要声明为private,字段强烈建议声明为final;enum适合用在switch语句中。21.从Java 14开始,提供新的record关键字,可以非常方便地定义Data Class:使用record定义的是不变类;可以编写Compact Constructor对参数进行验证;可以定义静态方法。22.BigInteger用于表示任意大小的整数;BigInteger是不变类,并且继承自Number;将BigInteger转换成基本类型时可使用longValueExact()等方法保证结果准确23.BigDecimal用于表示精确的小数,常用于财务计算;比较BigDecimal的值是否相等,必须使用compareTo()而不能使用equals()。24.Java提供的常用工具类有:Math:数学计算Random:生成伪随机数SecureRandom:生成安全的随机数

代码练习合集

1.Tax

//计算工资及稿费税收
public class Tax {public static void main(String[] args) {/** 个人所得税=(工资-5000)*0.3 稿费税收 = (《4000)---》(稿费-800)*0.2*0.7 or* (>4000)---->稿费*0.8*0.2*0.7*/Income[] incomes = new Income[] { new Income(7000), new Gaofei(12000) };System.out.println(totalTax(incomes));}public static double totalTax(Income... incomes) {double total = 0;for (Income income : incomes) {total = total + income.getTax();}return total;}
}class Income {protected double income;public Income(double income) {this.income = income;}public double getTax() {return (income - 5000) * 0.3;}
}class Gaofei extends Income {public Gaofei(double income) {super(income);}@Overridepublic double getTax() {if (income < 4000)return (income - 800) * 0.7 * 0.2;return income * 0.8 * 0.2 * 0.7;}
}

2.Tax_abstract

//用抽象类计算工资及稿费税收
public class Tax_abstract {public static void main(String[] args) {/** 个人所得税=(工资-5000)*0.3 稿费税收 = (《4000)---》(稿费-800)*0.2*0.7 or* (>4000)---->稿费*0.8*0.2*0.7*/Income1[] incomes = new Income1[] { new Gongzi(3000), new Gaofei1(12000) };System.out.println(totalTax(incomes));}public static double totalTax(Income1... incomes) {double total = 0;for (Income1 income : incomes) {total = total + income.getTax();}return total;}
}abstract class Income1{protected double income;public Income1(double income) {this.income = income;}public abstract double getTax();
}class Gongzi extends Income1{public Gongzi(double income) {super(income);}@Overridepublic double getTax() {if(income < 5000)return 0;return (income - 5000)*0.3;}
}class Gaofei1 extends Income1{public Gaofei1(double income) {super(income);}@Overridepublic double getTax() {if (income < 4000)return (income - 800) * 0.7 * 0.2;return income * 0.8 * 0.2 * 0.7;}
}

3.Tax_interface

//用接口计算工资及稿费税收
public class Tax_interface {public static void main(String[] args) {/** 个人所得税=(工资-5000)*0.3 稿费税收 = (《4000)---》(稿费-800)*0.2*0.7 or* (>4000)---->稿费*0.8*0.2*0.7*/Income2[] incomes = new Income2[] { new Gongzi2(6000), new Gaofei2(3333) };System.out.println(totalTax(incomes));}public static double totalTax(Income2... incomes) {double total = 0;for (Income2 income : incomes) {total = total + income.getTax();}return total;}
}interface Income2{double getTax();
}class Gongzi2 implements Income2{protected double income;public Gongzi2(double income) {this.income = income;}public double getTax() {if(income < 5000)return 0;return (income - 5000)*0.3;}
}class Gaofei2 implements Income2{protected double income;public Gaofei2(double income) {this.income = income;}public double getTax() {if (income < 4000)return (income - 800) * 0.7 * 0.2;return income * 0.8 * 0.2 * 0.7;}
}

4.Static

//统计实例创建的个数
public class Static {public static void main(String[] args) {person p1 = new person("A");System.out.println(person.getCount());person p2 = new person("B");System.out.println(person.getCount());person p3 = new person("C");System.out.println(person.getCount());}
}class person{public static int count = 0;String name;public person(String name) {this.name = name;count += 1;}public static int getCount() {return count;}
}

5.Insert(StringBuilder)

public class Insert {public static void main(String[] args) {String[] fields = { "name", "position", "salary" };String table = "employee";String insert = buildInsertSql(table, fields);System.out.println(insert);String s = "INSERT INTO employee (name, position, salary) VALUES (?, ?, ?)";System.out.println(s.equals(insert) ? "测试成功" : "测试失败");}static String buildInsertSql(String table, String[] fields) {StringBuilder sb = new StringBuilder(1024);sb.append("INSERT INTO " + table + " (");for (int i = 0; i < 2; i++) {sb.append(fields[i] + ", ");}sb.append(fields[2] + ") VALUES (?, ?, ?)");return sb.toString();
}}

6.Select(StringJoiner)

import java.util.StringJoiner;public class Select {public static void main(String[] args) {String[] fields = {"name", "position", "salary"};String table = "employee";String select = buildSelectSql(table,fields);System.out.println(select);System.out.println("SELECT name, position, salary FROM employee".equalsIgnoreCase(select) ? "测试成功" : "测试失败");}static String buildSelectSql(String table,String[] fields) {var sj = new StringJoiner(", ","SELECT "," FROM " + table);for(String field : fields) {sj.add(field);}return sj.toString();}
}

三.异常处理

1.Java使用异常来表示错误,并通过try ... catch捕获异常;  Java的异常是class,并且从Throwable继承;Error是无需捕获的严重错误,Exception是应该捕获的可处理的错误;RuntimeException无需强制捕获,非RuntimeException(Checked Exception)需强制捕获,或者用throws声明;不推荐捕获了异常但不进行任何处理。2.使用try ... catch ... finally时:多个catch语句的匹配顺序非常重要,子类必须放在前面;finally语句保证了有无异常都会执行,它是可选的;一个catch语句也可以匹配多个非继承关系的异常。3.调用printStackTrace()可以打印异常的传播栈,对于调试非常有用:捕获异常并再次抛出新的异常时,应该持有原始异常信息;通常不要在finally中抛出异常。如果在finally中抛出异常,应该原始异常加入到原有异常中。调用方可通过Throwable.getSuppressed()获取所有添加的Suppressed Exception。4.抛出异常时,尽量复用JDK已定义的异常类型;自定义异常体系时,推荐从RuntimeException派生“根异常”,再派生出业务异常;自定义异常时,应该提供多种构造方法。5.NullPointerException是Java代码常见的逻辑错误,应当早暴露,早修复;可以启用Java 14的增强异常信息来查看NullPointerException的详细错误信息。6.断言是一种调试方式,断言失败会抛出AssertionError,只能在开发和测试阶段启用断言;对可恢复的错误不能使用断言,而应该抛出异常;断言很少被使用,更好的方法是编写单元测试。7.日志是为了替代System.out.println(),可以定义格式,重定向到文件等;日志可以存档,便于追踪问题;日志记录可以按级别分类,便于打开或关闭某些级别;可以根据配置文件调整日志,无需修改代码;Java标准库提供了java.util.logging来实现日志功能8.Commons Logging是使用最广泛的日志模块;Commons Logging的API非常简单;Commons Logging可以自动检测并使用其他日志模块。9.通过Commons Logging实现日志,不需要修改代码即可使用Log4j;使用Log4j只需要把log4j2.xml和相关jar放入classpath;如果要更换Log4j,只需要移除log4j2.xml和相关jar;只有扩展Log4j时,才需要引用Log4j的接口(例如,将日志加密写入数据库的功能,需要自己开发)10.SLF4J和Logback可以取代Commons Logging和Log4j;始终使用SLF4J的接口写入日志,使用Logback只需要配置,不需要修改代码。

代码练习合集

1.Exception_catch(捕获异常)


public class Exception_catch{public static void main(String[] args) {String a = "12";String b = "x9";try {int c = stringToInt(a);int d = stringToInt(b);System.out.println(c * d);}catch(NumberFormatException e) {System.out.println("Bad input");}catch(Exception e){System.out.println("Bad input");}finally {System.out.println("END");}}static int stringToInt(String s){return Integer.parseInt(s);}
}

2.Exception_throw(抛出异常)

public class Exception_throw {public static void main(String[] args) {try {System.out.println(tax(2000, 0.1));System.out.println(tax(-200, 0.1));System.out.println(tax(2000, -0.1));} catch (IllegalArgumentException e) {e.printStackTrace();} finally {System.out.println("END");}}static double tax(int salary, double rate) {if (salary < 0 || rate < 0) {throw new IllegalArgumentException("传入参数不能为负");} // TODO: 如果传入的参数为负,则抛出IllegalArgumentExceptionreturn salary * rate;}
}

3.Exception_custom(自定义异常)

public class Exception_custom {public static void main(String[] args) {try {String token = login("admin", "pass");System.out.println("Token: " + token);} catch (UserNotFoundException e1) {e1.printStackTrace();System.out.println("测试失败:用户名异常");} catch (LoginFailedException e2) {e2.printStackTrace();System.out.println("测试失败:密码异常");} finally {System.out.println("测试结束");}}static String login(String username, String password) {if (username.equals("admin")) {if (password.equals("password")) {return "测试成功";} else {// 抛出LoginFailedException:throw new LoginFailedException("Bad username or password.");}} else {// 抛出UserNotFoundException:throw new UserNotFoundException("User not found.");}}
}class BaseException extends RuntimeException {public BaseException() {super();}public BaseException(String message, Throwable cause) {super(message, cause);}public BaseException(String message) {super(message);}public BaseException(Throwable cause) {super(cause);}}class LoginFailedException extends BaseException {public LoginFailedException(String password) {super(password);}
}
class UserNotFoundException extends BaseException {public UserNotFoundException(String username) {super(username);}
}

4.JDK Logging

import java.io.UnsupportedEncodingException;
import java.util.logging.Logger;public class Logging {public static void main(String[] args) {Logger logger = Logger.getLogger(Logging.class.getName());logger.info("Start process...");try {"".getBytes("invalidCharsetName");} catch (UnsupportedEncodingException e) {logger.severe(e.toString());// TODO: 使用logger.severe()打印异常e.printStackTrace();}logger.info("Process end.");}
}

5.Commons_logging(配合commons-logging-1.2.jar命令行执行)`

package Exception;import java.io.UnsupportedEncodingException;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;public class Main {static final Log log = LogFactory.getLog(Main.class);public static void main(String[] args) {log.info("Start process...");try {"".getBytes("invalidCharsetName");} catch (UnsupportedEncodingException e) {log.error("UnsupportedEncodingException",e);e.printStackTrace();// TODO: 使用log.error(String, Throwable)打印异常}log.info("Process end.");}
}

4.反射

1.JVM为每个加载的class及interface创建了对应的Class实例来保存class及interface的所有信息;获取一个class对应的Class实例后,就可以获取该class的所有信息;通过Class实例获取class信息的方法称为反射(Reflection);JVM总是动态加载class,可以在运行期根据条件来控制加载class。2.Java的反射API提供的Field类封装了字段的所有信息:通过Class实例的方法可以获取Field实例:getField(),getFields(),getDeclaredField(),getDeclaredFields();通过Field实例可以获取字段信息:getName(),getType(),getModifiers();通过Field实例可以读取或设置某个对象的字段,如果存在访问限制,要首先调用setAccessible(true)来访问非public字段。通过反射读写字段是一种非常规方法,它会破坏对象的封装。3.Java的反射API提供的Method对象封装了方法的所有信息:通过Class实例的方法可以获取Method实例:getMethod(),getMethods(),getDeclaredMethod(),getDeclaredMethods();通过Method实例可以获取方法信息:getName(),getReturnType(),getParameterTypes(),getModifiers();通过Method实例可以调用某个对象的方法:Object invoke(Object instance, Object... parameters);通过设置setAccessible(true)来访问非public方法;通过反射调用方法时,仍然遵循多态原则。4.Constructor对象封装了构造方法的所有信息;通过Class实例的方法可以获取Constructor实例:getConstructor(),getConstructors(),getDeclaredConstructor(),getDeclaredConstructors();通过Constructor实例可以创建一个实例对象:newInstance(Object... parameters); 通过设置setAccessible(true)来访问非public构造方法。5.通过Class对象可以获取继承关系:Class getSuperclass():获取父类类型;Class[] getInterfaces():获取当前类实现的所有接口。通过Class对象的isAssignableFrom()方法可以判断一个向上转型是否可以实现。6.Java标准库提供了动态代理功能,允许在运行期动态创建一个接口的实例;动态代理是通过Proxy创建代理对象,然后将接口方法“代理”给InvocationHandler完成的。

代码练习合集

1.Reflect_field

import java.lang.reflect.Field;public class Main {public static void main(String[] args) throws Exception {String name = "Xiao Ming";int age = 20;Person p = new Person();Class c = p.getClass();// TODO: 利用反射给name和age字段赋值:Field f1 = c.getDeclaredField("name");f1.setAccessible(true);f1.set(p, name);Field f2 = c.getDeclaredField("age");f2.setAccessible(true);f2.set(p, age);System.out.println(p.getName()); // "Xiao Ming"System.out.println(p.getAge()); // 20}
}
public class Person {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}}

2.Reflect_method

package method;import java.lang.reflect.*;public class Main {public static void main(String[] args) throws Exception {String name = "Xiao Ming";int age = 20;Person p = new Person();// TODO: 利用反射调用setName和setAge方法:Method m1 = p.getClass().getDeclaredMethod("setName", String.class);m1.setAccessible(true);m1.invoke(p, name);Method m2 = p.getClass().getDeclaredMethod("setAge",int.class);m2.setAccessible(true);m2.invoke(p, age);System.out.println(p.getName()); // "Xiao Ming"System.out.println(p.getAge()); // 20}
}
package method;public class Person {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}}

5.注解

1.注解(Annotation)是Java语言用于工具处理的标注:注解可以配置参数,没有指定配置的参数使用默认值;如果参数名称是value,且只有一个参数,那么可以省略参数名称。2.Java使用@interface定义注解:可定义多个参数和默认值,核心参数使用value名称;必须设置@Target来指定Annotation可以应用的范围;应当设置@Retention(RetentionPolicy.RUNTIME)便于运行期读取该Annotation。3.可以在运行期通过反射读取RUNTIME类型的注解,注意千万不要漏写@Retention(RetentionPolicy.RUNTIME),否则运行期无法读取到该注解。可以通过程序处理注解来实现相应的功能:对JavaBean的属性值按规则进行检查;JUnit会自动运行@Test标记的测试方法。

代码练习合集

1. Annotation_range_check

import java.lang.reflect.Field;public class Main {public static void main(String[] args) throws Exception {Person p1 = new Person("Bob", "Beijing", 20);Person p2 = new Person("", "Shanghai", 20);Person p3 = new Person("Alice", "Shanghai", 199);for (Person p : new Person[] { p1, p2, p3 }) {try {check(p);System.out.println("Person " + p + " checked ok.");} catch (IllegalArgumentException e) {System.out.println("Person " + p + " checked failed: " + e);}}}static void check(Person person) throws IllegalArgumentException, ReflectiveOperationException {for (Field field : person.getClass().getFields()) {Range range = field.getAnnotation(Range.class);if (range != null) {Object value = field.get(person);// TODO:if(value instanceof String) {String s = (String) value;if(s.length() < range.min() ||s.length() > range.max()) {throw new IllegalArgumentException("Invalid field:" + field.getName());}}if(value instanceof Integer) {int t = (int) value;if(t < range.min() ||t > range.max()) {throw new IllegalArgumentException("Invalid field:" + field.getName());}}}}}
}
public class Person {@Range(min = 1, max = 20)public String name;@Range(max = 10)public String city;@Range(min = 1, max = 100)public int age;public Person(String name, String city, int age) {this.name = name;this.city = city;this.age = age;}@Overridepublic String toString() {return String.format("{Person: name=%s, city=%s, age=%d}", name, city, age);}
}
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Retention;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Range {int min() default 0;int max() default 255;}

6.泛型

1.泛型就是编写模板代码来适应任意类型;泛型的好处是使用时不必对类型进行强制转换,它通过编译器对类型进行检查;注意泛型的继承关系:可以把ArrayList<Integer>向上转型为List<Integer>(T不能变!),但不能把ArrayList<Integer>向上转型为ArrayList<Number>(T不能变成父类)。2.编写泛型时,需要定义泛型类型<T>;静态方法不能引用泛型类型<T>,必须定义其他类型(例如<K>)来实现静态泛型方法;泛型可以同时定义多种类型,例如Map<K, V>。3.Java的泛型是采用擦拭法实现的;擦拭法决定了泛型<T>:不能是基本类型,例如:int;不能获取带泛型类型的Class,例如:Pair<String>.class;不能判断带泛型类型的类型,例如:x instanceof Pair<String>;不能实例化T类型,例如:new T()。型方法要防止重复定义方法,例如:public boolean equals(T obj);子类可以获取父类的泛型类型<T>。4.使用类似<? extends Number>通配符作为方法参数时表示:方法内部可以调用获取Number引用的方法,例如:Number n = obj.getFirst();;方法内部无法调用传入Number引用的方法(null除外),例如:obj.setFirst(Number n);。即一句话总结:使用extends通配符表示可以读,不能写。使用类似<T extends Number>定义泛型类时表示:泛型类型限定为Number以及Number的子类。5.使用类似<? super Integer>通配符作为方法参数时表示:方法内部可以调用传入Integer引用的方法,例如:obj.setFirst(Integer n);;方法内部无法调用获取Integer引用的方法(Object除外),例如:Integer n = obj.getFirst();。即使用super通配符表示只能写不能读。使用extends和super通配符要遵循PECS原则。无限定通配符<?>很少使用,可以用<T>替换,同时它是所有<T>类型的超类。6.部分反射API是泛型,例如:Class<T>,Constructor<T>;可以声明带泛型的数组,但不能直接创建带泛型的数组,必须强制转型;可以通过Array.newInstance(Class<T>, int)创建T[]数组,需要强制转型;同时使用泛型和可变参数时需要特别小心。# 7.集合1.List是按索引顺序访问的长度可变的有序表,优先使用ArrayList而不是LinkedList;可以直接使用for each遍历List;List可以和Array相互转换。2.在List中查找元素时,List的实现类通过元素的equals()方法比较两个元素是否相等,因此,放入的元素必须正确覆写equals()方法,Java标准库提供的String、Integer等已经覆写了equals()方法;编写equals()方法可借助Objects.equals()判断。如果不在List中查找元素,就不必覆写equals()方法  3.Map是一种映射表,可以通过key快速查找value。可以通过for each遍历keySet(),也可以通过for each遍历entrySet(),直接获取key-value。最常用的一种Map实现是HashMap。4.要正确使用HashMap,作为key的类必须正确覆写equals()和hashCode()方法;一个类如果覆写了equals(),就必须覆写hashCode(),并且覆写规则是:如果equals()返回true,则hashCode()返回值必须相等;如果equals()返回false,则hashCode()返回值尽量不要相等。实现hashCode()方法可以通过Objects.hashCode()辅助方法实现5.如果Map的key是enum类型,推荐使用EnumMap,既保证速度,也不浪费空间。使用EnumMap的时候,根据面向抽象编程的原则,应持有Map接口。6.SortedMap在遍历时严格按照Key的顺序遍历,最常用的实现类是TreeMap;作为SortedMap的Key必须实现Comparable接口,或者传入Comparator;要严格按照compare()规范实现比较逻辑,否则,TreeMap将不能正常工作。7.Java集合库提供的Properties用于读写配置文件.properties。.properties文件可以使用UTF-8编码。可以从文件系统、classpath或其他任何地方读取.properties文件。读写Properties时,注意仅使用getProperty()和setProperty()方法,不要调用继承而来的get()和put()等方法。8.Set用于存储不重复的元素集合:放入HashSet的元素与作为HashMap的key要求相同;放入TreeSet的元素与作为TreeMap的Key要求相同;利用Set可以去除重复元素;遍历SortedSet按照元素的排序顺序遍历,也可以自定义排序算法。9.队列Queue实现了一个先进先出(FIFO)的数据结构:通过add()/offer()方法将元素添加到队尾;通过remove()/poll()从队首获取元素并删除;通过element()/peek()从队首获取元素但不删除。要避免把null添加到队列。10.PriorityQueue实现了一个优先队列:从队首获取元素时,总是获取优先级最高的元素。PriorityQueue默认按元素比较的顺序排序(必须实现Comparable接口),也可以通过Comparator自定义排序算法(元素就不必实现Comparable接口)。11.Deque实现了一个双端队列(Double Ended Queue),它可以:将元素添加到队尾或队首:addLast()/offerLast()/addFirst()/offerFirst();从队首/队尾获取元素并删除:removeFirst()/pollFirst()/removeLast()/pollLast();从队首/队尾获取元素但不删除:getFirst()/peekFirst()/getLast()/peekLast();总是调用xxxFirst()/xxxLast()以便与Queue的方法区分开;避免把null添加到队列。12.栈(Stack)是一种后进先出(LIFO)的数据结构,操作栈的元素的方法有:把元素压栈:push(E);把栈顶的元素“弹出”:pop(E);取栈顶元素但不弹出:peek(E)。在Java中,我们用Deque可以实现Stack的功能,注意只调用push()/pop()/peek()方法,避免调用Deque的其他方法。最后,不要使用遗留类Stack。13.Iterator是一种抽象的数据访问模型。使用Iterator模式进行迭代的好处有:对任何集合都采用同一种访问模型;调用者对集合内部结构一无所知;集合类返回的Iterator对象知道如何迭代。Java提供了标准的迭代器模型,即集合类实现java.util.Iterable接口,返回java.util.Iterator实例。  14.Collections类提供了一组工具方法来方便使用集合类:创建空集合;创建单元素集合;创建不可变集合;排序/洗牌等操作

代码练习合集

1.List

import java.util.*;public class Main {public static void main(String[] args) {// 构造从start到end的序列:final int start = 10;final int end = 20;List<Integer> list = new ArrayList<>();for (int i = start; i <= end; i++) {list.add(i);}// 洗牌算法shuffle可以随机交换List中的元素位置:Collections.shuffle(list);// 随机删除List中的一个元素:int removed = list.remove((int) (Math.random() * list.size()));int found = findMissingNumber(start, end, list);System.out.println(list.toString());System.out.println("missing number: " + found);System.out.println(removed == found ? "测试成功" : "测试失败");}static int findMissingNumber(int start, int end, List<Integer> list) {int sum = 0;for (Integer i : list) {sum += i;}return (start + end)*(end - start + 1) / 2 - sum;}
}

2.复写equals方法

import java.util.List;
import java.util.Objects;public class Equals {public static void main(String[] args) {List<Person> list = List.of(new Person("Xiao", "Ming", 18), new Person("Xiao", "Hong", 25),new Person("Bob", "Smith", 20));boolean exist = list.contains(new Person("Bob", "Smith", 20));System.out.println(exist ? "测试成功!" : "测试失败!");}
}class Person {String firstName;String lastName;int age;public Person(String firstName, String lastName, int age) {this.firstName = firstName;this.lastName = lastName;this.age = age;}public boolean equals(Object o) {if (o instanceof Person) {Person p = (Person) o;return Objects.equals(this.firstName, p.firstName) && Objects.equals(this.lastName, p.lastName)&& this.age == p.age;}return false;}
}

3.Map

 import java.util.*;public class Map {public static void main(String[] args) {List<Student> list = List.of(new Student("Bob", 78), new Student("Alice", 85), new Student("Brush", 66),new Student("Newton", 99));var holder = new Students(list);System.out.println(holder.getScore("Bob") == 78 ? "测试成功!" : "测试失败!");System.out.println(holder.getScore("Alice") == 85 ? "测试成功!" : "测试失败!");System.out.println(holder.getScore("Tom") == -1 ? "测试成功!" : "测试失败!");}}class Students {List<Student> list;Map<String, Integer> cache;Students(List<Student> list) {this.list = list;cache = new HashMap<>();}/*** 根据name查找score,找到返回score,未找到返回-1*/int getScore(String name) {// 先在Map中查找:Integer score = this.cache.get(name);//Map没有直接去List查找,找到后存到cache并输出if (score == null) {score = this.findInList(name);if(score != null) {cache.put(name,score);}}return score == null ? -1 : score.intValue();}Integer findInList(String name) {for (var ss : this.list) {if (ss.name.equals(name)) {return ss.score;}}return null;}}class Student {String name;int score;Student(String name, int score) {this.name = name;this.score = score;}}

4.Set

import java.util.*;public class Main {public static void main(String[] args) {List<Message> received = List.of(new Message(1, "Hello!"),new Message(2, "发工资了吗?"),new Message(2, "发工资了吗?"),new Message(3, "去哪吃饭?"),new Message(3, "去哪吃饭?"),new Message(4, "Bye"));List<Message> displayMessages = process(received);for (Message message : displayMessages) {System.out.println(message.text);}}static List<Message> process(List<Message> received) {List<Message> newReceived = new ArrayList<>();Set<Integer> set = new TreeSet<>();// TODO: 按sequence去除重复消息for (Message ss : received) {if(set.add(ss.sequence)) {newReceived.add(ss);}}return newReceived;}}class Message {public final int sequence;public final String text;public Message(int sequence, String text) {this.sequence = sequence;this.text = text;}
}

5.Priority_queue

import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;public class Main {public static void main(String[] args) {Queue<User> q = new PriorityQueue<>(new UserComparator());// 添加3个元素到队列:q.offer(new User("Bob", "A10"));q.offer(new User("Alice", "A2"));q.offer(new User("Boss", "V1"));System.out.println(q.poll()); // Boss/V1System.out.println(q.poll()); // Bob/A1System.out.println(q.poll()); // Alice/A2System.out.println(q.poll()); // null,因为队列为空}
}class UserComparator implements Comparator<User> {public int compare(User u1, User u2) {if (u1.number.charAt(0) == u2.number.charAt(0)) {int u1Num = Integer.parseInt(u1.number.substring(1));int u2Num = Integer.parseInt(u2.number.substring(1));return Integer.compare(u1Num,u2Num);}if (u1.number.charAt(0) == 'V') {// u1的号码是V开头,优先级高:return -1;} else {return 1;}}
}class User {public final String name;public final String number;public User(String name, String number) {this.name = name;this.number = number;}public String toString() {return name + "/" + number;}
}

8.IO

1.iO流是一种流式的数据输入/输出模型:二进制数据以byte为最小单位在InputStream/OutputStream中单向流动;字符数据以char为最小单位在Reader/Writer中单向流动。Java标准库的java.io包提供了同步IO功能:字节流接口:InputStream/OutputStream;字符流接口:Reader/Writer。2.ava标准库的java.io.File对象表示一个文件或者目录:创建File对象本身不涉及IO操作;可以获取路径/绝对路径/规范路径:getPath()/getAbsolutePath()/getCanonicalPath();可以获取目录的文件和子目录:list()/listFiles();可以创建或删除文件和目录。3.Java标准库的java.io.InputStream定义了所有输入流的超类:FileInputStream实现了文件流输入;ByteArrayInputStream在内存中模拟一个字节流输入。使用try(resource)来保证InputStream正确关闭。4.Java标准库的java.io.OutputStream定义了所有输出流的超类:FileOutputStream实现了文件流输出;ByteArrayOutputStream在内存中模拟一个字节流输出。某些情况下需要手动调用OutputStream的flush()方法来强制输出缓冲区。总是使用try(resource)来保证OutputStream正确关闭。5.Java的IO标准库使用Filter模式为InputStream和OutputStream增加功能:可以把一个InputStream和任意个FilterInputStream组合;可以把一个OutputStream和任意个FilterOutputStream组合。Filter模式可以在运行期动态增加功能(又称Decorator模式)。6.ZipInputStream可以读取zip格式的流,ZipOutputStream可以把多份数据写入zip包;配合FileInputStream和FileOutputStream就可以读写zip文件7.把资源存储在classpath中可以避免文件路径依赖;Class对象的getResourceAsStream()可以从classpath中读取指定资源;根据classpath读取资源时,需要检查返回的InputStream是否为null。8.可序列化的Java对象必须实现java.io.Serializable接口,类似Serializable这样的空接口被称为“标记接口”(Marker Interface);反序列化时不调用构造方法,可设置serialVersionUID作为版本号(非必需);Java的序列化机制仅适用于Java,如果需要与其它语言交换数据,必须使用通用的序列化方法,例如JSON。9.Reader定义了所有字符输入流的超类:FileReader实现了文件字符流输入,使用时需要指定编码;CharArrayReader和StringReader可以在内存中模拟一个字符流输入。Reader是基于InputStream构造的:可以通过InputStreamReader在指定编码的同时将任何InputStream转换为Reader。总是使用try (resource)保证Reader正确关闭。10.Writer定义了所有字符输出流的超类:FileWriter实现了文件字符流输出;CharArrayWriter和StringWriter在内存中模拟一个字符流输出。使用try (resource)保证Writer正确关闭。Writer是基于OutputStream构造的,可以通过OutputStreamWriter将OutputStream转换为Writer,转换时需要指定编码。 11.PrintStream是一种能接收各种数据类型的输出,打印数据时比较方便:System.out是标准输出;System.err是标准错误输出。PrintWriter是基于Writer的输出

代码练习合集

1.File

import java.io.File;public class Main {public static void main(String[] args) {File file = new File("C:\\Users\\74886\\Desktop\\宇宙机");print(file, 0);}private static void print(File file, int level){for(int i = 0; i < level; ++i)System.out.print("  ");if(file.isDirectory()){System.out.println(file.getName() + "/");}else if(file.isFile())System.out.println(file.getName());File[] fList = file.listFiles();if(fList != null){for (File f:fList)print(f, level+1);}}
}

9.时间与日期

1.在编写日期和时间的程序前,我们要准确理解日期、时间和时刻的概念。由于存在本地时间,我们需要理解时区的概念,并且必须牢记由于夏令时的存在,同一地区用GMT/UTC和城市表示的时区可能导致时间不同。计算机通过Locale来针对当地用户习惯格式化日期、时间、数字、货币等。2.计算机表示的时间是以整数表示的时间戳存储的,即Epoch Time,Java使用long型来表示以毫秒为单位的时间戳,通过System.currentTimeMillis()获取当前时间戳。Java有两套日期和时间的API:旧的Date、Calendar和TimeZone;新的LocalDateTime、ZonedDateTime、ZoneId等。分别位于java.util和java.time包中。3.Java 8引入了新的日期和时间API,它们是不变类,默认按ISO 8601标准格式化和解析;使用LocalDateTime可以非常方便地对日期和时间进行加减,或者调整日期和时间,它总是返回新对象;使用isBefore()和isAfter()可以判断日期和时间的先后;使用Duration和Period可以表示两个日期和时间的“区间间隔”。4.-[ZonedDateTime是带时区的日期和时间,可用于时区转换;ZonedDateTime和LocalDateTime可以相互转换。5.对ZonedDateTime或LocalDateTime进行格式化,需要使用DateTimeFormatter类;DateTimeFormatter可以通过格式化字符串和Locale对日期和时间进行定制输出。6.Instant表示高精度时间戳,它可以和ZonedDateTime以及long互相转换。7.处理日期和时间时,尽量使用新的java.time包;在数据库中存储时间戳时,尽量使用long型时间戳,它具有省空间,效率高,不依赖数据库的优点。

练习代码合集

1.Flight_time

import java.time.*;public class Main {public static void main(String[] args) {LocalDateTime departureAtBeijing = LocalDateTime.of(2019, 9, 15, 13, 0, 0);int hours = 13;int minutes = 20;LocalDateTime arrivalAtNewYork = calculateArrivalAtNY(departureAtBeijing, hours, minutes);System.out.println(departureAtBeijing + " -> " + arrivalAtNewYork);// test:if (!LocalDateTime.of(2019, 10, 15, 14, 20, 0).equals(calculateArrivalAtNY(LocalDateTime.of(2019, 10, 15, 13, 0, 0), 13, 20))) {System.err.println("测试失败!");} else if (!LocalDateTime.of(2019, 11, 15, 13, 20, 0).equals(calculateArrivalAtNY(LocalDateTime.of(2019, 11, 15, 13, 0, 0), 13, 20))) {System.err.println("测试失败!");}}static LocalDateTime calculateArrivalAtNY(LocalDateTime bj, int h, int m) {ZonedDateTime zbj = bj.atZone(ZoneId.of("Asia/Shanghai"));ZonedDateTime zny = zbj.withZoneSameInstant(ZoneId.of("America/New_York"));LocalDateTime lny = zny.toLocalDateTime();LocalDateTime lny2 = lny.plusHours(13).plusMinutes(20);return lny2;}
}

10.单元测试

1.JUnit是一个单元测试框架,专门用于运行我们编写的单元测试:一个JUnit测试包含若干@Test方法,并使用Assertions进行断言,注意浮点数assertEquals()要指定delta。2.编写Fixture是指针对每个@Test方法,编写@BeforeEach方法用于初始化测试资源,编写@AfterEach用于清理测试资源;必要时,可以编写@BeforeAll和@AfterAll,使用静态变量来初始化耗时的资源,并且在所有@Test方法的运行前后仅执行一次。3.测试异常可以使用assertThrows(),期待捕获到指定类型的异常;对可能发生的每种类型的异常都必须进行测试。4.条件测试是根据某些注解在运行期让JUnit自动忽略某些测试。5.使用参数化测试,可以提供一组测试数据,对一个测试方法反复测试。参数既可以在测试代码中写死,也可以通过@CsvFileSource放到外部的CSV文件中。

代码练习合集

1.Junit测试

public class Main {public static long fact(long n) {long r = 1;for (long i = 1; i <= n; i++) {r = r * i;}return r;}}
import static org.junit.jupiter.api.Assertions.*;import org.junit.jupiter.api.Test;public class MainTest {@Testvoid testFact() {assertEquals(1, Main.fact(1));assertEquals(2, Main.fact(2));assertEquals(6, Main.fact(3));assertEquals(362800, Main.fact(10));assertEquals(2432902008176640000L, Main.fact(20));}
}

2.Fixture

public class Calculator {private long n = 0;public long add(long x) {n = n + x;return n;}public long sub(long x) {n = n - x;return n;}
}
import static org.junit.jupiter.api.Assertions.*;import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;public class CalculatorTest {Calculator calculator;@BeforeEachpublic void setUp() {this.calculator = new Calculator();}@AfterEachpublic void tearDown() {this.calculator = null;}@Testvoid testAdd() {assertEquals(100, this.calculator.add(100));assertEquals(150, this.calculator.add(50));assertEquals(130, this.calculator.add(-20));}@Testvoid testSub() {assertEquals(-100, this.calculator.sub(100));assertEquals(-150, this.calculator.sub(50));assertEquals(-130, this.calculator.sub(-20));}
}public class CalculatorTest {}

3.异常测试

public class Factorial {public static long fact(long n) {if (n < 0) {throw new IllegalArgumentException();}if(n > 20) {throw new ArithmeticException();}long r = 1;for (long i = 1; i <= n; i++) {r = r * i;}return r;}}
import static org.junit.jupiter.api.Assertions.*;import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;public class FactorialTest {@Testvoid testFact() {assertEquals(1, Factorial.fact(0));assertEquals(1, Factorial.fact(1));assertEquals(2, Factorial.fact(2));assertEquals(6, Factorial.fact(3));assertEquals(3628800, Factorial.fact(10));assertEquals(2432902008176640000L, Factorial.fact(20)); }@Testvoid testNegative() {assertThrows(IllegalArgumentException.class, new Executable() {@Overridepublic void execute() throws Throwable {Factorial.fact(-1);}});assertThrows(IllegalArgumentException.class, () -> {Factorial.fact(-1);});}@Testvoid testLargeInput() {assertThrows(ArithmeticException.class, () -> {Factorial.fact(21);});}
}

11.正则表达式

1.正则表达式是用字符串描述的一个匹配规则,使用正则表达式可以快速判断给定的字符串是否符合匹配规则。Java标准库java.util.regex内建了正则表达式引擎。2.单个字符的匹配规则如下:
正则表达式               规则              可以匹配
A                      指定字符                 A
\u548c              指定Unicode字符            和
.                       任意字符            a,b,&,0
\d                      数字0~9                   0~9
\w              大小写字母,数字和下划线 a~z,A~Z,0~9,_
\s                  空格、Tab键                 空格,Tab
\D                      非数字             a,A,&,_,……
\W                      非\w             &,@,中,……
\S                      非\s             a,A,&,_,……多个字符的匹配规则如下:
正则表达式               规则              可以匹配
A*                      任意个数字符      空,A,AA,AAA,……
A+                     至少1个字符          A,AA,AAA,……
A?                      0个或1个字符     空,A
A{3}                    指定个数字符      AAA
A{2,3}                  指定范围个数字符    AA,AAA
A{2,}                   至少n个字符          AA,AAA,AAAA,……
A{0,3}                  最多n个字符          空,A,AA,AAA3.
复杂匹配规则主要有:
正则表达式               规则              可以匹配
^                       开头              字符串开头
$                       结尾              字符串结束
[ABC]                   […]内任意字符        A,B,C
[A-F0-9xy]              指定范围的字符     A,……,F,0,……,9,x,y
[^A-F]                  指定范围外的任意字符  非A~F
AB|CD|EF                AB或CD或EF            AB,CD,EF4.正则表达式用(...)分组可以通过Matcher对象快速提取子串:group(0)表示匹配的整个字符串;group(1)表示第1个子串,group(2)表示第2个子串,以此类推。5.正则表达式匹配默认使用贪婪匹配,可以使用?表示对某一规则进行非贪婪匹配。注意区分?的含义:\d??。6.使用正则表达式可以:分割字符串:String.split()搜索子串:Matcher.find()替换字符串:String.replaceAll()

代码练习合集

1.电话匹配练习

import java.util.*;public class Main {public static void main(String[] args) throws Exception {String re = "0\\d{2,3}-[1-9]\\d{6,7}";for (String s : List.of("010-12345678", "020-9999999", "0755-7654321")) {if (!s.matches(re)) {System.out.println("测试失败: " + s);return;}}for (String s : List.of("010 12345678", "A20-9999999", "0755-7654.321")) {if (s.matches(re)) {System.out.println("测试失败: " + s);return;}}System.out.println("测试成功!");}
}

2.分组匹配

import java.util.regex.Matcher;
import java.util.regex.Pattern;public class Time {/*** 从"21:05:19"中提取时,分,秒,否则抛出IllegalArgumentException*/public static int[] parseTime(String s) {// FIXME:if (s == null) {throw new IllegalArgumentException();}Pattern pattern = Pattern.compile("([0-1]\\d|2[0-3]):([0-5][0-9]):([0-5][0-9])");Matcher matcher = pattern.matcher(s);if (matcher.matches()) {int hour = Integer.parseInt(matcher.group(1));int minute = Integer.parseInt(matcher.group(2));int second = Integer.parseInt(matcher.group(3));return new int[] { hour, minute, second };} else {throw new IllegalArgumentException();}}}
import static org.junit.jupiter.api.Assertions.*;import org.junit.jupiter.api.Test;class TimeTest {@Testpublic void testParseTime() {assertArrayEquals(new int[] { 0, 0, 0 }, Time.parseTime("00:00:00"));assertArrayEquals(new int[] { 1, 2, 3 }, Time.parseTime("01:02:03"));assertArrayEquals(new int[] { 10, 20, 30 }, Time.parseTime("10:20:30"));assertArrayEquals(new int[] { 12, 34, 56 }, Time.parseTime("12:34:56"));assertArrayEquals(new int[] { 23, 59, 59 }, Time.parseTime("23:59:59"));}@Testpublic void testParseTimeFailed() {assertThrows(IllegalArgumentException.class, () -> {Time.parseTime(null);});assertThrows(IllegalArgumentException.class, () -> {Time.parseTime("");});assertThrows(IllegalArgumentException.class, () -> {Time.parseTime("24:00:00");});assertThrows(IllegalArgumentException.class, () -> {Time.parseTime("23:60:59");});assertThrows(IllegalArgumentException.class, () -> {Time.parseTime("10:1:2");});}
}

3.搜索和替换

import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** Learn Java from https://www.liaoxuefeng.com/* * @author liaoxuefeng*/
public class Template {final String template;final Pattern pattern = Pattern.compile("\\$\\{(\\w+)\\}");public Template(String template) {this.template = template;}public String render(Map<String, Object> data) {Matcher m = pattern.matcher(template);// TODO:StringBuilder sb = new StringBuilder();while (m.find()) {m.appendReplacement(sb, data.get(m.group(1)).toString());}m.appendTail(sb);return sb.toString();}}
import static org.junit.jupiter.api.Assertions.*;import java.util.HashMap;
import java.util.Map;import org.junit.jupiter.api.Test;class TemplateTest {@Testpublic void testIsValidTel() {Template t = new Template("Hello, ${name}! You are learning ${lang}!");Map<String, Object> data = new HashMap<>();data.put("name", "Bob");data.put("lang", "Java");`在这里插入代码片`assertEquals("Hello, Bob! You are learning Java!", t.render(data));}
}

12.加密与安全

1.URL编码和Base64编码都是编码算法,它们不是加密算法;URL编码的目的是把任意文本数据编码为%前缀表示的文本,便于浏览器和服务器处理;Base64编码的目的是把任意二进制数据编码为文本,但编码后数据量会增加1/3。2.哈希算法可用于验证数据完整性,具有防篡改检测的功能;常用的哈希算法有MD5、SHA-1等;用哈希存储口令时要考虑彩虹表攻击。3.BouncyCastle是一个开源的第三方算法提供商;BouncyCastle提供了很多Java标准库没有提供的哈希算法和加密算法;使用第三方算法前需要通过Security.addProvider()注册。4.Hmac算法是一种标准的基于密钥的哈希算法,可以配合MD5、SHA-1等哈希算法,计算的摘要长度和原摘要算法长度相同。5.对称加密算法使用同一个密钥进行加密和解密,常用算法有DES、AES和IDEA等;密钥长度由算法设计决定,AES的密钥长度是128/192/256位;使用对称加密算法需要指定算法名称、工作模式和填充模式。6.PBE算法通过用户口令和安全的随机salt计算出Key,然后再进行加密;Key通过口令和安全的随机salt计算得出,大大提高了安全性;PBE算法内部使用的仍然是标准对称加密算法(例如AES)。7.非对称加密就是加密和解密使用的不是相同的密钥,只有同一个公钥-私钥对才能正常加解密;只使用非对称加密算法不能防止中间人攻击。8.数字签名就是用发送方的私钥对原始数据进行签名,只有用发送方公钥才能通过签名验证。数字签名用于:防止伪造;防止抵赖;检测篡改。常用的数字签名算法包括:MD5withRSA/SHA1withRSA/SHA256withRSA/SHA1withDSA/SHA256withDSA/SHA512withDSA/ECDSA等。9.数字证书就是集合了多种密码学算法,用于实现数据加解密、身份认证、签名等多种功能的一种安全标准。数字证书采用链式签名管理,顶级的Root CA证书已内置在操作系统中。数字证书存储的是公钥,可以安全公开,而私钥必须严格保密。

代码练习合集

1.:使用BouncyCastle提供的RipeMD160

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.Security;import org.bouncycastle.jce.provider.BouncyCastleProvider;public class Main {public static void main(String[] args) throws Exception {// 注册BouncyCastle:Security.addProvider(new BouncyCastleProvider());// 按名称正常调用:MessageDigest md = MessageDigest.getInstance("RipeMD160");md.update("HelloWorld".getBytes("UTF-8"));byte[] result = md.digest();System.out.println(new BigInteger(1, result).toString(16));}
}

13.多线程

1.Java用Thread对象表示一个线程,通过调用start()启动一个新线程;一个线程对象只能调用一次start()方法;线程的执行代码写在run()方法中;线程调度由操作系统决定,程序本身无法决定调度顺序;Thread.sleep()可以把当前线程暂停一段时间2.Java线程对象Thread的状态包括:New、Runnable、Blocked、Waiting、Timed Waiting和Terminated;通过对另一个线程对象调用join()方法可以等待其执行结束;可以指定等待时间,超过等待时间线程仍然没有结束就不再等待;对已经运行结束的线程调用join()方法会立刻返回。3.对目标线程调用interrupt()方法可以请求中断一个线程,目标线程通过检测isInterrupted()标志获取自身是否已中断。如果目标线程处于等待状态,该线程会捕获到InterruptedException;目标线程检测到isInterrupted()为true或者捕获了InterruptedException都应该立刻结束自身线程;通过标志位判断需要正确使用volatile关键字;volatile关键字解决了共享变量在线程间的可见性问题。4.守护线程是为其他线程服务的线程;所有非守护线程都执行完毕后,虚拟机退出;守护线程不能持有需要关闭的资源(如打开文件等)。5.多线程同时读写共享变量时,会造成逻辑错误,因此需要通过synchronized同步;同步的本质就是给指定对象加锁,加锁后才能继续执行后续代码;注意加锁对象必须是同一个实例;对JVM定义的单个原子操作不需要同步。6.用synchronized修饰方法可以把整个方法变为同步代码块,synchronized方法加锁对象是this;通过合理的设计和数据封装可以让一个类变为“线程安全”;一个类没有特殊说明,默认不是thread-safe;多线程能否安全访问某个非线程安全的实例,需要具体问题具体分析。7.Java的synchronized锁是可重入锁;死锁产生的条件是多线程各自持有不同的锁,并互相试图获取对方已持有的锁,导致无限等待;避免死锁的方法是多线程获取锁的顺序要一致8.wait和notify用于多线程协调运行:在synchronized内部可以调用wait()使线程进入等待状态;必须在已获得的锁对象上调用wait()方法;在synchronized内部可以调用notify()或notifyAll()唤醒其他等待线程;必须在已获得的锁对象上调用notify()或notifyAll()方法;已唤醒的线程还需要重新获得锁后才能继续执行。9.ReentrantLock可以替代synchronized进行同步;ReentrantLock获取锁更安全;必须先获取到锁,再进入try {...}代码块,最后使用finally保证释放锁;可以使用tryLock()尝试获取锁10.Condition可以替代wait和notify;Condition对象必须从Lock对象获取。11.使用ReadWriteLock可以提高读取效率:ReadWriteLock只允许一个线程写入;ReadWriteLock允许多个线程在没有写入时同时读取;ReadWriteLock适合读多写少的场景。12.StampedLock提供了乐观读锁,可取代ReadWriteLock以进一步提升并发性能;ThreadLocal表示线程的“局部变量”,它确保每个线程的ThreadLocal变量都是各自独立的;ThreadLocal适合在一个线程的处理流程中保持上下文(避免了同一参数在所有方法中传递);使用ThreadLocal要用try ... finally结构,并在finally中清除。StampedLock是不可重入锁。13.使用java.util.concurrent包提供的线程安全的并发集合可以大大简化多线程编程:多线程同时读写并发集合是安全的;尽量使用Java标准库提供的并发集合,避免自己编写同步代码。14.使用java.util.concurrent.atomic提供的原子操作可以简化多线程编程:原子操作实现了无锁的线程安全;适用于计数器,累加器等。15.JDK提供了ExecutorService实现了线程池功能:线程池内部维护一组线程,可以高效执行大量小任务;Executors提供了静态方法创建不同类型的ExecutorService;必须调用shutdown()关闭ExecutorService;ScheduledThreadPool可以定期调度多个任务。16.对线程池提交一个Callable任务,可以获得一个Future对象;可以用Future在将来某个时刻获取结果。17.CompletableFuture可以指定异步处理流程:thenAccept()处理正常结果;exceptional()处理异常结果;thenApplyAsync()用于串行化另一个CompletableFuture;anyOf()和allOf()用于并行化多个CompletableFuture。18.Fork/Join是一种基于“分治”的算法:通过分解任务,并行执行,最后合并结果得到最终结果。ForkJoinPool线程池可以把一个大任务分拆成小任务并行执行,任务类必须继承自RecursiveTask或RecursiveAction。使用Fork/Join模式可以进行并行计算以提高效率。19.ThreadLocal表示线程的“局部变量”,它确保每个线程的ThreadLocal变量都是各自独立的;ThreadLocal适合在一个线程的处理流程中保持上下文(避免了同一参数在所有方法中传递);使用ThreadLocal要用try ... finally结构,并在finally中清除。

14.Maven基础

1.Maven是一个Java项目的管理和构建工具:Maven使用pom.xml定义项目内容,并使用预设的目录结构;在Maven中声明一个依赖项可以自动下载并导入classpath;Maven使用groupId,artifactId和version唯一定位一个依赖。2.Maven通过解析依赖关系确定项目所需的jar包,常用的4种scope有:compile(默认),test,runtime和provided;Maven从中央仓库下载所需的jar包并缓存在本地;可以通过镜像仓库加速下载。3.Maven通过lifecycle、phase和goal来提供标准的构建流程。最常用的构建命令是指定phase,然后让Maven执行到指定的phase:mvn cleanmvn clean compilemvn clean testmvn clean package通常情况,我们总是执行phase默认绑定的goal,因此不必指定goal。4.Maven通过自定义插件可以执行项目构建时需要的额外功能,使用自定义插件必须在pom.xml中声明插件及配置;插件会在某个phase被执行时执行;插件的配置和用法需参考插件的官方文档。5.Maven支持模块化管理,可以把一个大项目拆成几个模块:可以通过继承在parent的pom.xml统一定义重复配置;可以通过<modules>编译多个模块6.使用Maven Wrapper,可以为一个项目指定特定的Maven版本。

15.JDBC编程

1.使用JDBC的好处是:各数据库厂商使用相同的接口,Java代码不需要针对不同数据库分别开发;Java程序编译期仅依赖java.sql包,不依赖具体数据库的jar包;可随时替换底层数据库,访问数据库的Java代码基本不变。2.JDBC接口的Connection代表一个JDBC连接;使用JDBC查询时,总是使用PreparedStatement进行查询而不是Statement;查询结果总是ResultSet,即使使用聚合查询也不例外。3.使用JDBC执行INSERT、UPDATE和DELETE都可视为更新操作;更新操作使用PreparedStatement的executeUpdate()进行,返回受影响的行数。4.数据库事务(Transaction)具有ACID特性:Atomicity:原子性Consistency:一致性Isolation:隔离性Durability:持久性JDBC提供了事务的支持,使用Connection可以开启、提交或回滚事务。5.使用JDBC的batch操作会大大提高执行效率,对内容相同,参数不同的SQL,要优先考虑batch操作。6.数据库连接池是一种复用Connection的组件,它可以避免反复创建新连接,提高JDBC代码的运行效率;可以配置连接池的详细参数并监控连接池。

代码练习合集

1.

JAVA SE 学习笔记相关推荐

  1. Java SE 学习笔记5 Java阶段复习

    计算机.Java基础 一.计算机 1.硬件介绍 2.中央处理器 3.比特(bit)和字节(byte) 4.内存 5.存储设备 6.输入和输出设备 二.Java介绍 1.常用的dos命令 2.java语 ...

  2. JAVA SE学习笔记(七):终章:Java程序设计基础笔记(全10万字)

    Java程序设计入门 ​                                              copyright © 2020 by 宇智波Akali 目录 文章目录 第1章 J ...

  3. 尚学堂java SE学习笔记(未完待续)

    1.关于递归,一定要注意函数调用顺序! 图1 如上图:在执行f(n-1)+f(n-2)的过程中,先执行f(n-1)一直到f(n-1)有返回值才执行f(n-2). 2.  图2 注意成员变量和局部变量的 ...

  4. Java SE 学习笔记 第二记

    2012-07-15 1.同一个类中的多个重载构造方法的互相调用,要用this(params)调用,不能直接显示调用构造方法,并且调用的时候this()必须放在构造方法中的首行,也就是this()之前 ...

  5. java se学习笔记-基于尚硅谷谈斌老师教程

    *第一小节 ****java语言介绍 创始人: 詹姆斯.高斯林 时间:1995年,96年发布第一个版本 隶属公司: 起初是SUN(斯坦福大学网络公司), 后被Oracle公司收购 java语言特点 免 ...

  6. 当年我们一起追过的Java,Java SE 个人笔记

    目录 JDK的安装和配置运行Java程序 java基本语法 面向对象 异常 多线程l API(Application Programming Interface)各种功能的类说明 集合类 IO输入输出 ...

  7. Java NIO 学习笔记(三)----Selector

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  8. 可能是最全面的 Java G1学习笔记

    转载自 可能是最全面的 Java G1学习笔记 引子 最近遇到很多朋友过来咨询G1调优的问题,我自己去年有专门学过一次G1,但是当时只是看了个皮毛,因此自己也有不少问题.总体来讲,对于G1我有几个疑惑 ...

  9. 深入浅出 Java CMS 学习笔记

    转载自  深入浅出 Java CMS 学习笔记 引子 带着问题去学习一个东西,才会有目标感,我先把一直以来自己对CMS的一些疑惑罗列了下,希望这篇学习笔记能解决掉这些疑惑,希望也能对你有所帮助. 1. ...

最新文章

  1. 机器人抓取领域性能评估标准
  2. OJ在线编程----常见输入输出练习场
  3. 经典C语言程序100例之九四
  4. SAP 电商云 UI 持续集成里 workflow 触发条件一览
  5. JEECG UI标签库做成单独开源项目规划
  6. 金额逾千万!浪潮智能存储G2中标华中科技大学脑科学研究项目
  7. iphone型号表_iPhone手机型号信息大全 (最新)
  8. 【转载】google搜索从入门到精通
  9. java jco_JAVA通过JCo连接SAP
  10. 游戏加加导致cpu降频
  11. SEODONG Medical推出创新干眼症治疗仪器,深受海外消费者好评
  12. 相比于 Java 10 的 var, 更期待 Java 11 支持多行字符串
  13. js室内地图开发_微信小程序室内地图导航开发-微信小程序JS加载esmap地图
  14. SAP SEGW 事物码里的 ABAP 类型和 EDM 类型映射的一个具体例子
  15. 第二部分 项目管理标准
  16. win10计算机维护,手把手教您win10系统打开安全和维护功能的问题
  17. Altium设置菜单之系统菜单
  18. 工业强国机械制造增长新方向 工业机器人产业发展迅速
  19. LINUX系统编程 LINUX 虚拟内存
  20. 一起来看流星雨剧情简介/剧情介绍/剧情分集介绍第二十七集

热门文章

  1. R语言实训项目——信用卡客户风险分析
  2. 概率统计Python计算:单个正态总体均值双侧假设的T检验
  3. linux运行搜狗拼音,Ubuntu安装搜狗拼音输入法-sogoupinyin
  4. linux系统没有硬盘分区,Linux系统入门学习:硬盘分区的陷阱及应对
  5. 技嘉GIGABYTE AERO 15SA风扇噪音过大加油修理方法
  6. C语言if和汇编jcc程序对比,逆向基础笔记六 汇编跳转和比较指令
  7. 国外cpa广告联盟EMU-lead-cpa联盟详解(1)英文独立站的搭建
  8. 前天说灰度拉盘,昨天说灰度砸盘,今天我们给你个准信
  9. 编译Android8,【分享】为MPSoC编译Android 8的主要流程和命令
  10. webpackjsonp 还原_电催化CO2还原领域综述梳理,速来学习!