java
枚举类
如果枚举类中保护了方法则最后一个枚举值要用;
构造函数必须是私有的。
-
枚举中定义的抽象方法,必须每个都要实现。
package learn; interface IColor{ String getColor(); } enum Color implements IColor{ Red(1){ public String getColor() { return "红色"; } }, Bule(2){ public String getColor() { return "蓝色"; } }, Green(3){ public String getColor() { return "绿色"; } }; private int index; private Color(int index) { this.index = index; } public int getIndex() { return index; } } public class MyEnumClass { public static void main(String[] arg) { for (Color c : Color.values()) { System.out.println(c.name() + "--->" + c.getColor() + "--->" + c.getIndex()); } } }
时间操作类
-
各个时间相关操作类
操作 1.8之前的版本 1.8新加的版本 获取当前时间对象 Date dt = new Date()
LocalDateTime dt = LocalDateTime.now()
时间对象转毫秒 long ms = dt.getTime()
Instant ins = Instant.now(); long ms = ins.toEpochMilli();
毫秒转时间对象 Date dt = new Date(ms)
Instant ins = Instant.ofEpochMilli(ms); LocalDateTime dt = LocalDateTime.ofInstant(ins, ZoneId.systemDefault();
时间对象转字符串 DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String s = df.format(dt)
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); String s = df.format(dt);
字符串转时间对象 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date dt = df.parse(s);
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); LocalDateTime dt = LocalDateTime.parse(s, df);
正则表达式
-
常用的表达式
表达式 说明 类别 \t tab制表符 单个字符的匹配 \n 空行 单个字符的匹配 [abc] a,b,c中的一个 单个字符的匹配 [^abc] 匹配不是a,b,c的字符 单个字符的匹配 [a-zA-z] 匹配a到z和A到Z的字符 单个字符的匹配 \d 0到9的任意一个数字 单个字符的匹配 \D 匹配不是0到9的任意一个字符 单个字符的匹配 \s 匹配任意一个空白字符 单个字符的匹配 \S 匹配任意一个不是空白的字符 单个字符的匹配 \w 匹配任意一个字符或数字或下划线,相当于[a-zA-Z_0-9] 单个字符的匹配 \W 匹配任意一个不是字符或数字或下划线的字符,相当于[^\w] 单个字符的匹配 ? 出现一次或零次 量词表达式 + 出现至少一次 量词表达式 * 出现任意次数 量词表达式 {n} 出现n次 量词表达式 {n, } 出现大于等于n次 量词表达式 {n, m} 出现n到m次 量词表达式 XY 两个表达式连接 逻辑表达式 X Y 两个表达式中的任意一个 逻辑表达式 (X) 表达式组 逻辑表达式 -
常用的表达式方法
方法 说明 String.matches(String regex) : boolean 是否匹配正则表达式 String.replaceAll(String regex, String replacement) : String 替换所有匹配项 String.replaceFirst(String regex, String replacement) : String 替换首个匹配项 String.spilt(String reges) : String[] 分割 java.util.regex包内的类(Pattern, Matcher) 提供分组功能 -
示例
package main.base; import java.util.regex.Matcher; import java.util.regex.Pattern; public class MyRegex { private static void checkEmail(String email) { String regex = "[a-zA-Z0-9]\\w*@[a-zA-Z0-9-_]+.(com|cn|com.cn|org)"; System.out.println(email + " is email:" + email.matches(regex)); } private static void checkPhone(String phone) { // 010123456 (010)-1234567 123456 String regex = "(\\d{3,4}|\\(\\d{3,4}\\)-)?\\d{6,7}"; System.out.println(phone + " is phone:" + phone.matches(regex)); } private static void checkIp(String ip) { String regex = "\\d{1,3}.\\d{1,3}.\\d{1,3}.\\d{1,3}"; System.out.println(ip + " is ip:" + ip.matches(regex)); } private static void group(String msg) { System.out.println("-----------"); String regex = "#\\{\\w+\\}"; Pattern pattern = Pattern.compile(regex); Matcher match = pattern.matcher(msg); while(match.find()) { System.out.println(match.group(0).replaceAll("(#\\{|\\})", "") + "、"); } System.out.println("-----------"); } public static void main(String[] args) { // TODO Auto-generated method stub checkEmail("liu@qq.com"); checkEmail("liu_200@qq_c-d.com.cn"); checkEmail("liu-2020@qq.com"); checkPhone("123456"); checkPhone("0101123456"); checkPhone("(0738)-1234567"); checkPhone("123456789"); checkPhone("010-123456"); checkIp("1.1.1.1"); checkIp("1.1.1"); group("INSERT INTO TABLE(name, age) VALUES(#{name}, #{age})"); } }
IO
-
如果不希望某个字段序列化,可以在声明时加入transient
private transient int age;
-
字节流(InputStream,OutputStream)和字符流(Reader,Writer)的区别:
- java最早的时候只提供了字节流
- 字符流可以方便的进行中文处理,但字节流处理时需要进行字节的编码和解码
- 网络传输或数据保存的时候,操作的都是字节流
- 字符流操作需要缓冲区处理数据,字节流操作时没有使用缓冲区,字符流会在关闭时候默认清空缓冲区,如果操作时没有关闭,则用户可以使用flush方法手工清空缓冲区。
常用流
class | 用途 | 子类 |
---|---|---|
OutputStream | 字节输出流 | FileOutputStream |
InputStream | 字节输入流 | FileInputStream |
Writer | 字符输出流 | FileWriter |
Reader | 字符输入流 | FileReader |
InputStreamReader | 字节输入流变为字符输入流,不建议使用,因为频繁地进行字符与字节间的相互转换 | |
OutputStreamWriter | 字节输出流变为字符输出流,不建议使用,因为频繁地进行字符与字节间的相互转换 | |
BufferedWriter | 字节输出流变为字符输出流,建议使用 | |
BufferedReader | 字节输入流变为字符输入流,建议使用 | |
ByteArrayOutputStream | 字节内存操作流 | |
ByteArrayInputStream | 字节内存操作流 | |
CharArrayWriter | 字符内存操作流 | |
CharArrayReader | 字符内存操作流 | |
PrintStream | 字节打印流,简化输出问题 | |
PrintWriter | 字符打印流 | |
SequenceInputStream | 合并流 | |
ObjectInputStream | 对象反序列化 | |
ObjectOutputStream | 对象序列化 |
文件流:
- FileOutputStream: 程序 ----> 文件
- FileInputStream: 程序 <---- 文件
内存操作流:
- ByteArrayInputStream: 程序 ----> 内存
- ByteArrayOutputStream: 程序 <---- 内存
范型
- 定义范型类时把类型参数放到类名称的后面,定义范型函数时需要把类型参数放到函数的返回值前面。
- 通配符
通配符 | 说明 |
---|---|
<?> | 类型未知,用来定义函数返回值或函数参数的 |
<? extends T> | 定义为T或T的子类,用来定义函数返回值或函数参数的 |
<? supper T> | 超类匹配,用来灵活写入,用来定义范型类型中类型参数的 |
异常
-
Exception和RuntimeException的区别
- Exception:强制程序必须处理的异常
- RuntimeException:是Exception异常的子类,可以由用户决定是否进行异常处理
throws标识的方法表示要处理的异常
java1.7之后新增try-with-resources结构的异常处理,类似try....catch....finally结构,可以自动调用try语句中定义的实例化对象的close方法释放资源。
volatile内存模型
volatile定义的是属性的,表示该变量不进行内存拷贝操作,而是直接进行变量的操作。把这个关键字用于同步是一种错误的使用,无法描述同步的处理,它只是一种直接内存处理,不进行副本的操作
-
内存模型:前提知识:cpu有L2缓冲,cpu直接从缓冲中读取变量
- 读取一个变量时先从cpu缓冲中读取,如果命中则读取该值;如果没有命中则从主内存到加载到cpu缓冲中,进行变量副本的拷贝,读取该值
- 变量赋值成功之后,更新cpu缓冲中的值,然后再把值刷新到主内存中
volatile关键字修饰的变量,直接操作主内存,也就是不进行副本的拷贝
多线程
-
线程的创建方式有两种:
- extend Thread
- implements Runnable
- implements Callable(可以获取线程的返回值,使用FutureTask)
这两种方法的区别:
- 继承Thread的方式下,线程类不能再继承其他父类,而使用Runable接口可以避免单继承带来的局限
- Runnable可以多个线程共享相同资源
Thread的start方法只能启动一次。
join方法会阻塞当前方法,直到join的线程执行完毕
wait、notify、notifyAll这3个方法只能在synchronized方法中调用。并且notify只能唤醒同一对象监视器中调用的wait线程。
结束现场的方法有suspend、resume、stop方法,但都不建议使用,因为suspend、resume可能会导致死锁发生,stop方法可能会导致共享的数据不完整。
内存模型:每个线程有自己的工作内存,读取变量的时候从主内存中读取到工作内存中,访问的是自己工作内存中的副本,最后在把工作内存中的副本刷新到主内存中。当一个变量声明为volatile时,线程在写入变量时会直接存到主内存中。但volatile并不保证操作的原子性。
-
延迟加载的建议
// 第一种:基于volatile的解决方案 public class Instance { private volatile static Instance instance; public static Instance getInstance(){ if (instance == null){ synchronized(SafeDoubleCheckedLocking.class){ if (instance == null){ instance = new Instance(); } } } return instance; } } // 第二种:基于类初始化的解决方案 public class Instance{ private static class InstanceHolder{ public static Instance instance = new Instance(); } public static Instance getInstance(){ return InstanceHolder.instance; } }
-
线程池
手动创建线程池的方法:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)
- corePoolSize:核心线程的数量(线程池维护的线程最少数量)
- maximumPoolSize:线程的最大数量
- keepAliveTime:线程所允许的空闲时间
- unit:线程空闲时间单位
- workQueue:缓冲队列,常用的是java.until.concurrent.ArrayBlockingQueue,其他还有:
* LinkedBlockingQueue
* SynchronousQueue
* DelayedWorkQueue
- handler:线程池对拒绝任务的处理策略:常用的值:
* ThreadPoolExecutor.AbortPolicy:抛出异常
* ThreadPoolExecutor.CallerRunPolicy:重试添加当前任务
* ThreadPoolExecutor.DiscardOldestPolicy:抛弃旧的任务
* ThreadPoolExecutor.DiscardPolicy:抛弃当前任务
而是通过 ThreadPoolExecutor 的方式,这样 的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors 返回的线程池对象的弊端如下: 1)FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。 2)CachedThreadPool 和 ScheduledThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
-
线程池的工作机制:
- 线程池数量小于corePoolSize时,即使有空闲线程也创建一个新的线程执行任务
- 线程池的数量等于corePoolSize时,但workQueue还未满时,新加的任务放入缓冲队列
- 线程池数量大于corePoolSize时,且workQueue满,并且线程数量小于maximumPoolSize时,创建一个新的线程处理任务
- 线程池数量大于corePoolSize时,且workQueue满,并且线程数量等于maximumPoolSize时,按照handler策略进行任务拒绝处理
-
线程池(Executors)
线程池类型(创建方法) 工作队列 适用场景 特点 newCachedThreadPool SynchronousQueue 并发执行大量短期任务 最大线程数是Integer.MAX_VALUE newFixedThreadPool LinkedBlockingQueue 执行长期任务 固定数目的线程池; newScheduledThreadPool DelayedWorkQueue 周期性或延迟执行的任务 定时及周期的执行线程池 newSingleThreadExecutor LinkedBlockingQueue 串行执行任务 corePoolSize和maximumPoolSize都为1 -
线程池的异常处理
- 在Run方法中捕获异常
- 利用submit执行任务,利用返回的Future对象的get方法获取异常
- 重新ThreadPoolExcutor.afterExecute方法
- 为工作线程设置UncaughtExceptionHandler(不推荐)
ThreadLocal类:将多个线程共享变量关联到具体的线程,调用set方法保存变量,调用get方法读取变量,调用remove方法删除。如果不调用remvoe方法将可能导致oom。
反射
-
获取反射的三种方式
- 通过实例化对象的getClass方法
- 通过类的静态属性 类名.class
通过类的名称 class.forName(类的全名)
对象的实例化:class对象的newInstance方法,会调用类的无参构造函数。
Field,Method:在调用private字段的get/set方法时会抛出非法访问的异常(lllegalAccessException),这个时候应该先调用setAccessible(true)以关闭java的检查机制。
classLoader
自定义classLoader的目的:可以让jvm加载不在CLASSPATH路径中的class文件。
自定义的classLoader会最后加载,因为jvm提供双亲加载模式。比如如果自定义了一个java.lang.String类,那这个类将不会加载。
动态代理
-
动态代理示例
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class MyDynamicProxy { public static void main(String[] args) { RealyNetMessage netMessage = new RealyNetMessage(); IMessage message = (IMessage)Proxy.newProxyInstance( RealyNetMessage.class.getClassLoader(), RealyNetMessage.class.getInterfaces(), new RealNetMessageProxy(netMessage)); message.send("动态代理"); } } interface IMessage{ public void send(String msg); } class RealyNetMessage implements IMessage{ @Override public void send(String msg) { System.out.println("发送消息:"+ msg); } } // InvocationHandler class RealNetMessageProxy implements InvocationHandler{ IMessage target; public RealNetMessageProxy(IMessage target){ this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("send".equals(method.getName())) { // 代理send方法 if (connect()) { Object returnData = method.invoke(target, args); // 注意:这里只能是目标对象 close(); return returnData; } } return null; } // 发送消息前链接 private boolean connect() { System.out.println("发送消息前链接"); return true; } // 发送完毕之后要释放资源 private void close() { System.out.println("发送完毕之后要释放资源"); } }
注解 Annotation
定义的时候使用@interface。定义属性的时候需要在变量名后加()。也可以在定义的时候使用default定义默认值,未定义默认值的在使用时需要指定值。
自定义的注解要添加@Retention(value=RetentionPolicy.RUNTIME)才能配合使用反射。
@Target可以定义注解使用的范围
如果希望注解能被子类继承,则要使用@Inherited定义注解
使用@Documented和@Retention(value=RetentionPolicy.RUNTIME)定义的注解将出现在javadoc文档中
-
常用内置注解
-
作用在代码的注解是
- @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
- @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
- @SuppressWarnings - 指示编译器去忽略注解中声明的警告。
-
作用在其他注解的注解(或者说 元注解)是:
- @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
- @Documented - 标记这些注解是否包含在用户文档中。
- @Target - 标记这个注解应该是哪种 Java 成员。
- @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
-
从 Java 7 开始,额外添加了 3 个注解:
- @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
- @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
- @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
@Retention(value=RetentionPolicy.RUNTIME) @interface LoginInfo{ public String name() default "a"; public String password(); }
-
函数式编程和lambda表达式(1.8新功能)
lambda表达式不是匿名内部类,是函数式接口。
函数式接口是接口中只能有一个抽象方法。
-
方法引用:只要方法的签名和函数式接口的定义相同,就可以直接引用该方法
方法引用 表示代码 静态方法 [类名]::[方法名] 实例方法 [类名]::[方法名] 构造方法 [类名]::new
AutoCloseable自动释放资源的必要条件(1.8新功能)
继承AutoCloseable接口
-
实例化该对象时需要使用try异常语句
class NetMessage implements AutoCloseable{ public void close() throws Exception{ System.out.println("[auto close]------"); } public void send(String msg) { System.out.println("发送消息:" + msg); } } public class MyAutoCloseable { public static void main(String[] args) { try(NetMessage nm = new NetMessage()){ nm.send("test"); } catch(Exception ex) { ex.printStackTrace(); } } }
Optional类(1.8新功能)
Optional封装了null的处理
-
常用方法:
操作 存数据 读数据 说明 数据可能为空 Optional.ofNullable Optional.orElse 数据不会为空 Optional.of Optional.get 如果存或读取的数据为空,则这两个方法都会抛异常
默认方法(1.8新功能)
JUnit单元测试
-
常用的注解
注解 说明 @Test 测试方法,方法不能有参数,不能用在static方法上 @BeforeEach 每次调用@Test前执行,用来实例化变量 @AfterEach 每次@Test执行之后调用,用来清理实例化变量 @BeforeAll 启动@Test前执行一次,用来初始化静态变量 @AfterAll @Test都执行完毕之后调用 @Disabled 不执行该测试方法 @EnableOnOs 在指定系统上运行 @DisabledOnOs 不在指定系统上不运行 @DisableOnJre 只在特定jre版本或以上运行 @EnableIf 万能条件测试 @ParameterizedTest 参数化测试,测试方法要有参数 -
参数化测试中指定参数值的方式有
- @ValueSource
- @CsvSource
- @CsvFileSource
- @MethodSource:允许定义一个同名的静态方法提供参数
-
异常测试
Assertions.assertThrows(RuntimeException.class, ()->{ myjunit.division(4, 0); });
maven
-
依赖关系
scope 说明 示例 compile 编译时需要用到该jar包(默认) commons-logging runtime 编译时不需要,但运行时需要用到 mysql test 编译Test时需要用到该jar包 junit provided 编译时需要用到,但运行时由JDK或某个服务器提供 servlet-api -
添加阿里镜像:linux:sudo find / -name *settings.xml找到maven的配置文件(也可以在eclipse中调整设置)
<settings> <mirrors> <mirror> <id>aliyun</id> <name>aliyun</name> <mirrorOf>central</mirrorOf> <!-- 国内推荐阿里云的Maven镜像 --> <url>http://maven.aliyun.com/nexus/content/groups/public/</url> </mirror> </mirrors> </settings>
-
maven常用构建
- mvn clean
- mvn clean compile
- mvn clean test
- mvn clean package
-
在线搜索包名:
-
maven实例
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> <scope>runtime</scope> </dependency>
jdbc
一条sql语句执行多次时,可以使用批处理
使用PreparedStatement预防sql注入
-
数据库事物的四大特性(ACID):
- Atomicity:原子性
- Consistency:一致性
- Isolation:隔离性
- Durability:持久性
-
并发问题:
- 脏读:读取另一个事物未提交的记录
- 不可重复读取:对同一条记录两次读取不一致,因为另一个事物对该记录做了修改
- 幻读:对同一张表的两次查询不一致,因为另一个事物插入了一条记录
-
事物隔离用来解决并发问题。数据库的隔离级别
Isolation Level 脏读(Dirty Read) 不可重复读(Non Repeatable Read) 幻读(Phantom Read) Read Uncommitted(读未提交) Yes Yes Yes Read Committed(读取已提交的) - Yes Yes Repeatable Read(可重复读) - - Yes Serializable(串行化) - - - mysql默认事物隔离为:Repeatable Read
-
jdbc设置隔离级别的方式:connection.setTransactionIsolation(int level),其中参数值有:(默认情况下没有必要设置)
- Connection.TRANSACTION_READ_UNCOMMITTED
- Connection.TRANSACTION_READ_COMMITTED
- Connection.TRANSACTION_REPEATABLE_READ
- Connection.TRANSACTION_SERIALIZABLE
jdbc连接池:jdbc默认不提供数据库连接池,但提供了连接池的接口javax.sql.DataSource;第三方实现的连接池必须继承DataSource。
-
mysql连接池
- C3P0(推荐)
- Apache Commons DBCP
- BoneCP
- Druid
- HikariCP
-
mysql代码示例
// mysql连接 void connect(){ Class.forName("com.mysql.cj.jdbc.Driver").newInstance(); // mysql8.0及以上,以下为:com.mysql.jdbc.Driver Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test?user=root&password=Xcy123456"); } // batch批处理 void batch(){ PreparedStatement pre = conn.preparedStatement("INSERT INTO coupons (user_id, type, expires) VALUES (?,?,?)"); for(int i = 0; i < 5; ++i){ pre.setString(1, ""); pre.setInt(1, 0); pre.setString(1, ""); pre.addBatch(); } pre.executeBatch(); // 执行批处理 } // Transaction 数据库事务PreparedStatement void transaction(){ Connection conn = openConnection(); try { // 关闭自动提交: conn.setAutoCommit(false); // 执行多条SQL语句: insert(); update(); delete(); // 提交事务: conn.commit(); } catch (SQLException e) { // 回滚事务: conn.rollback(); } finally { conn.setAutoCommit(true); conn.close(); } }