首先,这份笔记是我在有一定基础后系统学习SE时整理的笔记,其中不包含入门的知识点,主要是在学校学Java时没有注意的点。如果你有Java基础(起码得基本语法得信手沾来),或者说是通过学校上课学的SE,现在想自学进阶,那这份笔记对你而言绝对是好东西。如果你想看JavaSE的高级操作,可以直接看多线程,多线程也是我系统学习时整理资料比较多的点。没有网络编程部分,这块我打算后面单独写一篇。
要有一点基础才能看,毕竟不少零基础想自学的人,不想浪费这部分人的时间
变量
强制类型转换
变量类型容量从低到高
byte,short,char–>int–>long–>float–>double
由低到高自动类型转换,
由高到低需要强制类型转换。
强制类型转换需要注意可能会导致内存溢出
数字的值
JDK7新特性,数字变量的值可以用下划线"_"分割,而不会影响其值和输出结果
例:int i = 1_000_000;
变量作用域
类变量
实例变量:static修饰的成员变量,可在静态方法中调用
局部变量
public class Variable(){
static int i = 0; //实例变量
String str = "hello world"; //类变量
public void method(){
int j = 0; //局部变量
}
}
运算
多变量运算,值取精度最高的类型
位运算
左移<<
例:2 << 3 —> 2^3
右移>>算数平方根
三元运算
x ? y : z
x==true则执行y,否则执行z
字符串连接符
int a = 10;
int b = 20;
""+a+b; //1020 完全拼接
a+b+""; //30 先运算后拼接
关键字
final
final修饰引用数据类型,其地址不能被改变,但其中包含的属性值可以改变。
static
static修饰的属性和方法可直接通过类名调用
静态方法只可访问静态变量
this
当前类对象的引用
如果要是使用this调用构造方法的话,那么this必须出现在构造方法的第一行
supre
当前子类对象中的父类型特征
super不能用在静态方法中
JavaDoc
文档注释
@auther 作者
@version 版本
@since 指明需要的最低jdk版本
@param 参数名
@return 返回值
@throws 异常抛出情况
在方法体前输入/**Enter即可生成部分文档注释
生成文档
命令提示符
javadoc -encoding UTF-8 -charset UTF-8 ClassName.java
内部类
成员内部类
- 等同被看作成员变量
- 可访问外部类所有数据
- 内部不能有静态声明
静态内部类
- 可直接访问外部类的静态数据
- 等同被看作静态变量
局部内部类
- 不能用权限修饰符修饰
- 不能有静态声明
匿名内部类
//
继承
- 继承后需要重写至少一个父类构造器,并可覆盖/重写父类非私有属性和方法
- 子类可使用父类非私有属性和方法
- 被final修饰的类不能被继承
- 被final修饰的方法不能被子类重写
- 父类引用指向子类对象,Animal a = new Cat();
- 父类静态代码块–>子类静态代码块–>父类构造代码块–>父类构造方法–>子类构造代码块–>子类构造方法
抽象类和抽象方法
用abstract关键字修饰的类和方法,且不能被final修饰
public abstract class AbstractClassTest{
static final int a = 1; //常量
public abstract void method(); //抽象方法
protected abstract void method();
abstract void method();
public void method(){ //成员方法
}
}
抽象类
抽象类有构造器但不可直接创建对象
可以通过其子类创建抽象类对象
被实现类用来继承
通过匿名内部类创建,需要重写其中所有抽象方法
非抽象的子类继承抽象类后需要重写抽象类中所有的抽象方法
抽象子类继承抽象类可以选择性重写抽象类中的方法
内部可存在带有方法体的成员方法
也可存在常量
抽象方法
没有方法体
不能被private,final,static关键字修饰
抽象类中不一定存在抽象方法,但抽象方法一定存在于抽象类中
接口
public interface Itf{
void method();
}
给实现该接口的类指定需要实现的抽象方法
-
接口中只包含抽象方法和常量
-
内部的抽象方法,不需要加public abstract修饰,且权限修饰符只能是public
-
常量不需要加public static final
-
实现该接口的类需要重写其中所有的方法
-
接口不能被类继承,但是可以被接口继承,且这种继承是多继承
通过接口实现类继承接口方式
public class Main {
public static void main(String[] args) {
IEat iEat = new Eat();
iEat.eat("apple");
}
}
//函数式接口:只包含一个抽象方法的接口
@FunctionalInterface
interface IEat{
void eat(String food);
}
//接口实现类
class Eat implements IEat{
@Override
public void eat(String food) {
System.out.println("i eat "+food);
}
}
静态内部类方式
public class Main {
static class Eat implements IEat{
@Override
public void eat(String food) {
System.out.println("i eat "+food);
}
}
public static void main(String[] args) {
IEat iEat = new Eat();
iEat.eat("apple");
}
}
//函数式接口
@FunctionalInterface
interface IEat{
void eat(String food);
}
局部内部类方式
public class Main {
public static void main(String[] args) {
class Eat implements IEat{
@Override
public void eat(String food) {
System.out.println("i eat "+food);
}
}
IEat iEat = new Eat();
iEat.eat("apple");
}
}
//函数式接口
@FunctionalInterface
interface IEat{
void eat(String food);
}
匿名内部类方式
public class Main {
public static void main(String[] args) {
//1
IEat iEat = new IEat() {
@Override
public void eat(String food) {
System.out.println("i eat "+food);
}
};
iEat.eat("apple");
//2
new IEat() {
@Override
public void eat(String food) {
System.out.println("i eat "+food);
}
}.eat("a");
}
}
//函数式接口
@FunctionalInterface
interface IEat{
void eat(String food);
}
标识接口
接口中不指定任何方法,标识继承该接口的类,可配合instanceof判断是否是其实现类。
函数式接口
只包含一个抽象方法的接口
此接口可用@FunctionalInterface
注解标注
// 正确的函数式接口
@FunctionalInterface
public interface TestInterface {
// 抽象方法
public void sub();
// java.lang.Object中的方法不是抽象方法
public boolean equals(Object var1);
// default不是抽象方法
public default void defaultMethod(){
}
// static不是抽象方法
public static void staticMethod(){
}
}
Lambda表达式
只可用于函数式接口
public class Main {
public static void main(String[] args) {
IEat iEat = (String food)-> {
System.out.println("i eat "+food);
};
iEat.eat("apple");
}
}
//函数式接口
@FunctionalInterface
interface IEat{
void eat(String food);
}
lambda简化1
简化参数类型
public class Main {
public static void main(String[] args) {
IEat iEat = (food)-> {
System.out.println("i eat "+food);
};
iEat.eat("apple");
}
}
//函数式接口
@FunctionalInterface
interface IEat{
void eat(String food);
}
lambda简化2
简化参数括号
单参数才可简化括号
public class Main {
public static void main(String[] args) {
IEat iEat = food-> {
System.out.println("i eat "+food);
};
iEat.eat("apple");
}
}
//函数式接口
@FunctionalInterface
interface IEat{
void eat(String food);
}
lambda简化3
简化方法体括号
方法体必须为单行
public class Main {
public static void main(String[] args) {
IEat iEat = food-> System.out.println("i eat "+food);
iEat.eat("apple");
}
}
//函数式接口
@FunctionalInterface
interface IEat{
void eat(String food);
}
多线程
三种线程创建启动方式
-
继承Thread类,并重写run()。
public class MyThread extends Thread{ @Override public void run(){ ... } } // 启动方式 new MyThread().start();
-
实现Runnable接口,重写run()。
推荐使用,避免了单继承的局限性,方便同一个对象被多个线程使用。
public class MyRunnable implements Runnable{ @Override public void run(){ ... }}// 启动方式new Thread(new MyRunnable()).start();
-
实现Callable接口,重写call()。
public class MyCallable implements Callable<T>{ // 接口泛型可以直接指定好,不指定具体类型类名也要加上此泛型 @Override public T call() throws Exception { // call方法返回值必须和接口中的一样 return null; } } // 启动方式 MyCallable mycallable = new MyCallable(); ExecutorService executorService = Executors.newFixedThreadPool(1); // 创建执行服务,参数为线程池中的线程数量 Future<T> result = executorService.submit(mycallable); // 提交执行,submit()返回Future<T> try { System.out.println(result.get()); // 获取执行结果,即call()返回值 } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } executorService.shutdownNow(); // 关闭服务
线程停止
推荐使用标志位让线程自行停止。
t.stop();
t.interrupt();
t.destroy();
线程状态
线程一共有5种状态:
-
新生(创建线程对象)
-
就绪(调用start方法后就绪,但不是立即执行,需要等待CPU调度)
-
运行(被CPU调度后,执行线程体中的代码)
-
阻塞(sleep、wait、同步锁定后进入阻塞状态,即代码不继续执行,事件解除后重新进入就绪状态,等待CPU调度)
-
死亡(运行结束后,当线程到此生命周期时不能再被start)
Thread.State state = t.getState(); // Thread.State是一个枚举类
NEW:新生
RUNNABLE:运行
BLOCKED:阻塞
WAITING:等待
TIMED_WAITING:带时限的等待
TERMINATED:终结
线程休眠
sleep可以放大线程安全问题的发生性
try {
Thread.sleep(1000); //休眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
线程礼让
该线程礼让后会和其他线程重新竞争CPU资源,不一定礼让成功。
Thread.yield();
线程强制执行
强制执行的线程就像VIP插队
,让调用线程(包括主线程)进入阻塞状态,直到强制执行线程执行完毕才可继续执行。
让父线程等待子线程结束之后才能继续运行
Thread t = new Thread(new MyRunnable(), "实现Runnable接口方式创建的线程");
// 第二个参数为线程的名字,通过线程对象.getName()方法可以获取到
// 线程内部使用Thread.currentThread().getName()
try {
t.join(); // 如果这些代码都在main方法里,则是让主线程(父线程)等待线程t执行完毕。
} catch (InterruptedException e) {
e.printStackTrace();
}
线程优先级
线程优先级并不绝对,只是权重(被CPU调度的概率)更高,具体还得看CPU调度。因此有可能出现性能倒置
,即高优先级得不到CPU调度,反而低优先级得到CPU调度。
Thread t1 = new Thread(new MyRunnable());
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread(new MyRunnable());
Thread t4 = new Thread(new MyRunnable());
// 优先级必须在1-10之间,否则会报IllegalArgumentException异常
t1.setPriority(Thread.MAX_PRIORITY); // 最大优先级 10
t2.setPriority(Thread.NORM_PRIORITY); // 正常优先级,不设置优先级的话,默认为此优先级,主线程也是此优先级 5
t3.setPriority(Thread.MIN_PRIORITY); // 最小优先级 1
t4.setPriority(2); // 也可自定义优先级
System.out.println(t1.getPriority()); // 获取线程优先级
守护线程
线程分为用户线程和守护线程,虚拟机会确保用户线程执行完毕,而不需要等待守护线程执行完毕。(gc就是守护线程)
守护线程一般用于后台日志记录,监控内存等
Thread t = new Thread(new MyRunnable());
t.setDaemon(true); //设置该线程为守护线程,不设置默认为false,即一个用户线程。
t.start(); // 必须在线程启动之前将线程设置为守护线程,否则会报IllegalThreadStateException异常。
线程同步
虽可以保证线程安全性却会降低性能。
线程同步即使用锁让资源同一时间只可被一条线程访问。
锁
分为隐式锁和显式锁
隐式锁即synchronized,执行完后自动释放锁
显式锁即Lock(java.util.concurrent.Lock),手动获取,手动释放
同步方法
public synchronized void method(){ //同步方法,默认锁this
}
public synchronized static void method(){ //静态同步方法,默认锁.class
}
同步代码块
synchronized (object){ // 给对象加锁,基本数据类型需要包装类,也可锁.class类
// object也叫同步监视器
}
同步的可重入性
同一条线程可重复获取同一个对象锁。
// 三个方法都锁this
public synchronized void method1(){ //同步方法,默认锁this
method2();
}
public synchronized void method2(){ //同步方法,默认锁this
method3();
}
public synchronized void method3(){ //同步方法,默认锁this
...
}
Lock
显式锁,接口,位于java.util.concurrent
void lock(); // 锁
void unlock(); // 释放锁
boolean trylock(); // 调用时该锁未被其他线程持有,则获取锁并返回true,否则返回false
boolean tryLock(long time, TimeUnit unit) throws InterruptedException; // 和上一个方法一样,传入时间参数,在该时间内获取到锁则返回true,时间结束仍未获取到锁则返回false
ReentrantLock
重入锁,也叫递归锁,Lock的实现类之一,该锁可以被同一线程的外部方法获取后在内部方法中再次获取,因此可用于递归,也因此称为递归锁。
公平锁与非公平锁
都有一个等待队列
公平锁:当锁被某个线程持有时,优先加入等待该锁的队列,再根据请求的先后获取锁。
锁空闲时也要检查有没有其他线程在等待队列中,如果有则唤醒队首线程,并将自己挂起加入等待队列。
非公平锁:当锁被某个线程持有时,优先尝试获取锁,获取不到则加入请求该锁的队列,再根据请求的先后获取锁。
相对公平锁,后来的线程有一定几率避免挂起和对等待队列的检查,减少了线程切换的开销。
synchronized是非公平锁,需要竞争。
ReentrantLock可以实现公平锁,但其默认是非公平锁,向其构造器传入true便会构造成一个公平锁。
// ReentrantLock的另一种构造器,无参构造器默认构造非公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
读写锁
ReadWriteLock
public interface ReadWriteLock {
Lock readLock(); // 获取读锁
Lock writeLock(); // 获取写锁
}
其实现类是ReentrantReadWriteLock,可重入读写锁。
可以分别获取读锁和写锁,即读写分离,分为两个锁。
读锁:共享模式,可以被多个线程同时持有,但当写锁使用时,读锁被阻塞。
写锁:独占模式,同时只能被一个线程持有,当有读锁时,写锁可以获取到,但是被阻塞,当有写锁时,只有持有该写锁的线程可以获取到读锁。
死锁
死锁产生的必要条件:
- 互斥性:一个资源同一时间只能被一条线程访问。
- 请求与保持:一个线程因请求资源而阻塞时,正在使用该资源的线程保持不放。
- 不剥夺条件:一个线程未使用完该资源,不可强行剥夺。
- 循环等待条件:若干线程之间形成首位相连的资源等待关系。
线程之间的通信
wait():使当前线程进入等待状态。
- 因为wait方法是Object的方法,因此调用wait方法需要获取到此对象的监视器锁,否则会抛出非法监视器异常
IllegalMonitorStateException
- ^可以通过锁对象的wait方法来让线程等待。
- 调用wait方法后会释放锁,以便其他等待此锁的线程执行。
notify():可以随机唤醒当前线程池中的一条线程。
notifyAll():唤醒当前线程池中的所有线程。
- 被唤醒后并不能获取锁,还需要和其他线程竞争锁。
- 通过当前锁的对象调用该方法。锁this可以直接调用。
线程池
Executors:静态工厂,提供各种线程池对象的构建。
ExecutorService:各种线程池的统一接口。
FixedThreadPool
固定大小的线程池,构造器中传入corepoolsize即线程池中工作线程的数量。当提交的任务大于当前设置的最大值时将任务加入池队列,等池中工作线程结束后再执行。
ExecutorService es = Executors.newFixedThreadPool(2);
CachedThreadPool
缓存线程池,调用execute方法根据需要创建或复用线程,如果之前创建的线程可用则复用,不可用则开启新线程。
ExecutorService es = Executors.newCachedThreadPool();
SingleThreadPool
单线程线程池,只包含一条线程,以无界队列方式运行该线程。如果该线程意外终止,会创建新的线程执行后续任务。
ExecutorService es = Executors.newSingleThreadExecutor();
ScheduleThreadPool*
计划线程池,可以给定的延迟和间隔执行任务,与Timer+TimerTask类似。
// 通过静态工厂获取接口的实例化对象
ScheduledExecutorService es = Executors.newScheduledThreadPool(3);
// 通过类直接创建
ScheduledThreadPoolExecutor se = new ScheduledThreadPoolExecutor(3);
计划线程池对时间把握的精准度要高于Timer,因此需要计划任务时最好使用计划线程池。
ThreadPoolExecutor
类,Executors静态工厂中的new方法很多都是调用的此类构造器,通过向其构造器中传入参数实现自定义线程池。
两种向线程池中提交任务的方式
execute()
-
该方法位于Executor接口中,其是ExecutorService的父接口。
-
该方法没有返回值。
-
只能向其提交Runnable实现类。
submit()
-
该方法位于ExecutorService接口中,该接口是所有线程池的统一接口。
-
通过该方法提交的任务执行完毕后会返回一个Future。
-
可以提交Runnable或Callable的线程。(如果是Runnable,run返回值为void,Future.get()获取到的是null)
-
可以处理异常,通过对Future.get()进行异常捕获,然后处理。
线程池的关闭
shutdown():将线程池设置为SHUTDOWN状态,正在执行的线程和任务队列中未执行的线程依旧会执行完毕,但此时已不能再向线程池中提交任务。
shutdownNow():将线程池设置为STOP状态,立刻关闭当前线程池,正在执行的线程被中断,未执行的线程返回到List集合。
线程池状态
在ThreadPoolExecutor中定义了5种线程池的状态。
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
RUNNING:创建线程池后,处于此状态,此时线程池中的任务数量为0,能够接收任务,并对已添加的任务进行处理。
SHUTDOWN:调用shutdown方法后处于此状态,不能接收新任务,等待所有已提交任务执行完毕。
STOP:调用shutdownNow方法后处于此状态,不能接收新任务,并中断正在执行的任务。
TIDYING:当所有任务执行完毕,ctl变量记录的任务数量为0,处于此状态,接着会去执行terminated方法。
TERMINATED:执行完terminated方法后处于此状态。
数据库
通过JDBC连接MySQL
手动连接
public static Connection getConnection() {
Connection connection = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
// 现在的mysql已经不需要加载驱动了,直接通过驱动管理器就能获得Connection对象。
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/management" +
"?serverTimezone=UTC&characterEncoding=utf-8", "root", "123456");
} catch (SQLException | ClassNotFoundException e) {
e.printStackTrace();
}
return connection;
}
通过连接池连接
通过第三方连接池创建DataSource对象,基本上都可以通过Java代码设置数据源的设置,也可以通过读取配置文件来创建数据源。
C3P0
public static Connection getConnectionByC3P0(){ // 通过代码设置连接池属性
Connection connection = null;
try {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/management" +
"?serverTimezone=UTC&characterEncoding=utf-8");
dataSource.setUser("root");
dataSource.setPassword("123456");
connection = dataSource.getConnection();
} catch (SQLException | PropertyVetoException e) {
e.printStackTrace();
}
return connection;
}
HikariCP
public static Connection getConnectionByHikariCP(){ // 通过配置文件创建连接池
Connection connection = null;
try {
//HikariDataSource dataSource = new HikariDataSource();
InputStream in = DBUtil.class.getClassLoader().getResourceAsStream("HikariCP.properties");
Properties properties = new Properties();
properties.load(in);
HikariConfig hikariConfig = new HikariConfig(properties);
HikariDataSource dataSource = new HikariDataSource(hikariConfig);
connection = dataSource.getConnection();
} catch (SQLException | IOException e) {
e.printStackTrace();
}
return connection;
}
DBCP
apache开发的数据库连接池
public static Connection getConnectionByDBCP(){
InputStream in = DBUtil.class.getClassLoader().getResourceAsStream("dbcp.properties");
Properties properties = new Properties();
DataSource dataSource = null;
Connection connection = null;
try {
properties.load(in);
dataSource = BasicDataSourceFactory.createDataSource(properties);
connection = dataSource.getConnection();
} catch (Exception e) {
e.printStackTrace();
}
return connection;
}
Druid
阿里开发的数据库连接池
public static Connection getConnectionByDruid(){
Connection connection = null;
try {
InputStream in = DBUtil.class.getClassLoader().getResourceAsStream("druid.properties");
Properties properties = new Properties();
properties.load(in);
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
connection = dataSource.getConnection();
} catch (Exception e) {
e.printStackTrace();
}
return connection;
}
数据库增删改查
自行封装
了解即可*
基本没人直接用这折磨人的玩意了
举例:
查询 emp表所有数据并添加到Arraylist中
public List<Emp> selectByNameAndJob(String ename, String job, int start, int step) {
Connection connection = null;
PreparedStatement statement = null;
ResultSet res = null;
List<Emp> list = new ArrayList<>();
try {
connection = DBUtil.getConnection();
String sql = "select * from emp where ename like ? and job like ?";
statement = connection.prepareStatement(sql);
res = statement.executeQuery(); // 执行语句,获得结果集
while (res.next()) { // 遍历结果集
Emp emp = new Emp();
emp.setEmpno(res.getInt(1)); // 获取结果集中的数据
emp.setEname(res.getString(2));
emp.setJob(res.getString(3));
emp.setMgr(res.getInt(4));
emp.setHiredate(res.getDate(5));
emp.setSal(res.getDouble(6));
emp.setComm(res.getDouble(7));
emp.setDeptno(res.getInt(8));
list.add(emp);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection, statement, res);
}
return list;
}
修改emp表字段
public boolean update(String eName,int empno){
Connection connection = null;
PreparedStatement statement = null;
ResultSet res = null;
List<Emp> list = new ArrayList<>();
try {
connection = DBUtil.getConnection();
String sql = "update ename=? from emp where empno=?";
statement = connection.prepareStatement(sql);
statement.setString(1,eName);
statement.setInt(2,empno);
if (statement.executeUpdate() != 0){
return true;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection, statement, res);
}
return false;
}
删除emp表中字段
public boolean del(String eName){
Connection connection = null;
PreparedStatement statement = null;
ResultSet res = null;
try {
connection = DBUtil.getConnection();
String sql = "delete from emp where ename=?";
statement = connection.prepareStatement(sql);
statement.setString(1,eName);
if (statement.executeUpdate() != 0){
return true;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection, statement, res);
}
return false;
}
向emp表中添加字段
public boolean insert(Object...obj){ // 通过可边长参数赋值
Connection connection = null;
PreparedStatement statement = null;
ResultSet res = null;
try {
connection = DBUtil.getConnection();
String sql = "insert into emp values(?,?,?,?,?,?,?,?)";
statement = connection.prepareStatement(sql);
for (int i = 0; i < obj.length; i++) { // 遍历可边长参数,并给statement对象设置object
statement.setObject(i + 1, obj[i]);
}
if (statement.executeUpdate() != 0){
return true;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection, statement, res);
}
return false;
}
通过DBUtils执行数据库语句
导入DBUtils的jar包,数据库连接池的jar包,jdbc驱动的jar包
首先封装DBUtils工具类
public class DBUtil {
public static DataSource getDataSource(){ // 获取数据源
DataSource dataSource = null;
try {
Properties properties = new Properties();
InputStream in = DBUtil.class.getClassLoader().getResourceAsStream("druid.properties");
properties.load(in);
dataSource = DruidDataSourceFactory.createDataSource(properties); // Druid
} catch (Exception e) {
e.printStackTrace();
}
return dataSource;
}
public static QueryRunner getQueryRunner(){ // 通过数据源获得QueryRunner对象
return new QueryRunner(getDataSource());
}
}
通过QueryRunner执行查询语句时需要传入一个ResultSetHandler的对象,来处理查询到的结果。
-
BeanListHandler:把多条数据封装成List集合,每条数据封装成实体类对象
-
BeanHandler:查询出多列单行的数据,把数据封装到实体类对象属性里面
-
MapHandler:查询出多列单行的数据,把数据封装到Map集合中
-
ColumnListHandler:查询出来是多行单列
-
MapListHandler:查询多行多列,每一行数据封装到Map集合中,再把map存储到List集合中
-
ScalarHandler:查询的是单行单列
CRUD举例:
CRUD是4个单词的首字母,CRUD分别指增加(Create)、读取查询(Retrieve)、更新(Update)和删除(Delete)这4个单词的首字母。
查找单个记录的多个字段,并将查询结果封装到bean对象中:
public User findUserByUsernameAndPassword(String username, String password) {
String sql = "select * from user where username=? and password=?";
User user = null;
try {
user = queryRunner.query(sql, new BeanHandler<>(User.class), username, password);
} catch (SQLException e) {
e.printStackTrace();
}
return user;
}
查询一条记录的多个字段:
返回的结果为List<Map<字段名,值>>,每个map中存一个字段的字段名和该字段的值。
public List<Map<String, Object>> getAllRecordsByUserId(Integer id) {
List<Map<String, Object>> list = null;
String sql = "select goods.gname from record join goods where goods.id=record.gid and record.uid=?;";
try {
list = queryRunner.query(sql, new MapListHandler(), id);
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
查询单条数据的单个字段值:
public int countRecordByUserId(Integer id) {
String sql = "select count(*) from record where uid=?";
Number count = 0;
try {
count = queryRunner.query(sql, new ScalarHandler<>(), id);
} catch (SQLException e) {
e.printStackTrace();
}
return count.intValue();
}
需要注意的是当你查询的数据表的字段名和实体类的属性名不一样,需要使用接口RowProcessor来匹配
private RowProcessor getMap() {
Map<String, String> map = new HashMap<>();
map.put("s_id", "id");
map.put("s_sno", "sno");
map.put("s_sname", "sname");
map.put("s_ssex", "ssex");
map.put("s_sbirthday", "birthday");
map.put("s_clazz", "clazz");
BeanProcessor bean = new BeanProcessor(map);
RowProcessor processor = new BasicRowProcessor(bean);
return processor;
}
在创建BeanHandler和BeanListHandler时候,在构造方法中传的入RowProcessor的对象,也就是调用上面的方法来返回一个RowProcessor对象
修改:
private boolean reduceStock(String gname){
int stock = getStock(gname);
if (stock < 1){
return false;
}
stock -= 1;
String sql = "update goods set stock=? where gname=?";
int row = 0 ;
try {
row = queryRunner.update(sql, stock, gname); // queryrunner.update(sql,objs[]);
} catch (SQLException e) {
e.printStackTrace();
}
return row > 0;
}
添加:
public int buyGoodsByGnameAndUserId(String gname, Integer uid) {
String sql = "insert into record(uid, gid) values(?,(select id from goods where gname=?))";
int row = 0;
try {
row = queryRunner.update(sql, uid, gname);
} catch (SQLException e) {
e.printStackTrace();
}
return row;
}
删除:
public int delGoodsByGname(String gname) {
String sql = "delete from goods where gname=?))";
int row = 0;
try {
row = queryRunner.update(sql, gname);
} catch (SQLException e) {
e.printStackTrace();
}
return row;
}
好兄弟,都看到这了,觉得有用赶紧收藏关注,我会不定时分享一些轮子,学习笔记之类的东西,大家一起进步!
如果笔记中有什么错误,还请各位指正!我会第一时间修改!