给大三学弟的万字JavaSE查漏补缺笔记

首先,这份笔记是我在有一定基础后系统学习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

内部类

成员内部类

  1. 等同被看作成员变量
  2. 可访问外部类所有数据
  3. 内部不能有静态声明

静态内部类

  1. 可直接访问外部类的静态数据
  2. 等同被看作静态变量

局部内部类

  1. 不能用权限修饰符修饰
  2. 不能有静态声明

匿名内部类

//

继承

  1. 继承后需要重写至少一个父类构造器,并可覆盖/重写父类非私有属性和方法
  2. 子类可使用父类非私有属性和方法
  3. 被final修饰的类不能被继承
  4. 被final修饰的方法不能被子类重写
  5. 父类引用指向子类对象,Animal a = new Cat();
  6. 父类静态代码块–>子类静态代码块–>父类构造代码块–>父类构造方法–>子类构造代码块–>子类构造方法

抽象类和抽象方法

用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();
}

给实现该接口的类指定需要实现的抽象方法

  1. 接口中只包含抽象方法和常量

  2. 内部的抽象方法,不需要加public abstract修饰,且权限修饰符只能是public

  3. 常量不需要加public static final

  4. 实现该接口的类需要重写其中所有的方法

  5. 接口不能被类继承,但是可以被接口继承,且这种继承是多继承

通过接口实现类继承接口方式

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);
}

多线程

三种线程创建启动方式

  1. 继承Thread类,并重写run()。

    public class MyThread extends Thread{
        @Override
        public void run(){
            ...
        }
    }
    
    // 启动方式
    new MyThread().start();
    
  2. 实现Runnable接口,重写run()。

    推荐使用,避免了单继承的局限性,方便同一个对象被多个线程使用。

    public class MyRunnable implements Runnable{    @Override    public void run(){        ...    }}// 启动方式new Thread(new MyRunnable()).start();
    
  3. 实现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种状态:

  1. 新生(创建线程对象)

  2. 就绪(调用start方法后就绪,但不是立即执行,需要等待CPU调度)

  3. 运行(被CPU调度后,执行线程体中的代码)

  4. 阻塞(sleep、wait、同步锁定后进入阻塞状态,即代码不继续执行,事件解除后重新进入就绪状态,等待CPU调度)

  5. 死亡(运行结束后,当线程到此生命周期时不能再被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,可重入读写锁。

可以分别获取读锁和写锁,即读写分离,分为两个锁。

读锁:共享模式,可以被多个线程同时持有,但当写锁使用时,读锁被阻塞。

写锁:独占模式,同时只能被一个线程持有,当有读锁时,写锁可以获取到,但是被阻塞,当有写锁时,只有持有该写锁的线程可以获取到读锁。

死锁

死锁产生的必要条件:

  1. 互斥性:一个资源同一时间只能被一条线程访问。
  2. 请求与保持:一个线程因请求资源而阻塞时,正在使用该资源的线程保持不放。
  3. 不剥夺条件:一个线程未使用完该资源,不可强行剥夺。
  4. 循环等待条件:若干线程之间形成首位相连的资源等待关系。

线程之间的通信

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()
  1. 该方法位于Executor接口中,其是ExecutorService的父接口。

  2. 该方法没有返回值。

  3. 只能向其提交Runnable实现类。

submit()
  1. 该方法位于ExecutorService接口中,该接口是所有线程池的统一接口。

  2. 通过该方法提交的任务执行完毕后会返回一个Future。

  3. 可以提交Runnable或Callable的线程。(如果是Runnable,run返回值为void,Future.get()获取到的是null)

  4. 可以处理异常,通过对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;
}

好兄弟,都看到这了,觉得有用赶紧收藏关注,我会不定时分享一些轮子,学习笔记之类的东西,大家一起进步!
如果笔记中有什么错误,还请各位指正!我会第一时间修改!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值