Java反射机制

1、反射机制有什么用?

通过java语言中的反射机制可以操作字节码文件

可以读和写字节码文件

通过反射机制可以操作代码片段(class文件)

2、反射机制相关类在哪个包下

java.lang.reflect.*;

3、反射机制相关的重要类有哪些?

java.lang.Class:代表整个字节码,代表一个类型,整个类

java.lang.reflect.Method:代表字节码中方法字节码,类中方法

java.lang.reflect.Constructor:代表字节码中构造方法字节码,类中构造方法

java.lang.reflect.Field:代表字节码中属性字节码。类中属性

三种获取class方法:

  1. 第一种
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
Class.forName()
1.静态方法
2.参数是一个字符串,且是一个完整的类名
3.完整名必须带有包名。java.lang包不能省略
4.需要处理异常
*/
try{
Class c1 = Class.forName("java.lang.String"); //获取string类型的class字节码
Class c2 = Class.forName("java.util.Date");
Class c3 = Class.forName("java.lang.Integer");
Class c4 = Class.forName("java.lang.System");
} catch (ClassNotFoundException e) {
e.printStackTrace;
}

2.第二种

1
2
3
4
5
6
// Java中任何一个对象都有一个getClass方法
String s = "abc";
Class x = s.getClass(); // x代表String.class

Date time = new Date();
Class y = time.getClass();

3.第三种

1
2
3
4
// java中任何一个类型,包括基本数据类型,都有class属性
Class z = String.class; // z 代表String类型
Class e = int.class; // e 代表int类型
Class b = Date.class; // b 代表Date类型

获取到class能干什么?

1.可以实例化对象

1
2
3
4
5
6
7
8
9
10
11
try {
// 通过反射机制,获取class,可以实例化对象
Class c = Class.forName("re.User"); // c代表user类型

// newInstance() 这个方法会调用User这个类的无参构造方法,完成对象的创建
Object obj = c.newInstance();

System.out.println(obj);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}

2.实例化对象很灵活

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public static void main(String[] args) throws Exception {
// 通过io流读取classinfo.properties这个配置文件
FileReader fr = new FileReader("classinfo.properties");

/*
另一种写法,以流的形式返回
InputStream fr = Thread.currentThread()
.getContextClassLoader().getResourceAsStream("classinfo.properties");
*/

// 创建属性类对象map
Properties pro = new Properties();

// 加载
pro.load(fr);

// 关闭流
fr.close();

// 通过key获取value
String className = pro.getProperty("className");

// 通过反射机制实例化对象
Class c = Class.forName(className);

// 实例化对象
Object obj = c.newInstance();
System.out.println(obj);
}

如果只希望一个类的静态代码块执行,其他代码一律不执行,就可以使用以下方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class TestDemo{
public static void main(String[] args) {
try {
// Class.forName()这个方法执行会导致类加载
// 类加载时,静态代码块执行
Class.forName("re.Myclass");
} catch (ClassNotFoundException) {
e.printStackTrace();
}
}
}
class Myclass {
// 静态代码块在类加载时执行,并且只执行一次
static {
System.out.println("MyClass类的静态代码块执行了!");
}
}

获取类路径下文件的绝对路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) {
// 前提是此文件必须在src类路径下,才能获取到,起点是src
// 注意获取java文件时需要加.class
/*
解释:Thread.currentThread()获取当前线程对象。
getContextClassLoader()是线程对象的方法,
可以获取到当前线程的类加载器对象。
getResource()【获取资源】这是类加载器对象的方法,
当前线程的类加载器默认从类的根路径下加载资源。
*/
String path = Thread.currentThread().getContextClassLoader()
.getResource("classinfo.properties").getPath();
// 起点是src如:re/User.class
// 这种方式获取绝对路径是通用的
System.out.println(path);
}

资源绑定器

1
2
3
4
5
6
7
8
9
10
11
/*
java.util包下提供了一个资源绑定器,便于获取属性配置文件的内容
使用以下方式的时候,属性配饰文件必须是xxx.properties,且必须放到类路径下
*/
public static void main(String[] args) {
// 资源绑定器,这个文件必须是配置文件properties,且在类路径下,参数不能有后缀名
ResourceBundle rb = ResourceBundle.getBundle("classinfo");

String name = rb.getString("className");
System.out.println(name);
}

反射属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/*测试类*/
package re;
public class Demo {
public String name;
private int age;
protected String sex;
double moeny;
public static final float PI = 3.14f;
}

package re;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/*反射属性测试类*/
public class FieldTest {
public static void main(String[] args) throws Exception {
// 先获取整个类
Class c = Class.forName("re.Demo");

String className = c.getName();
System.out.println("完整类名:" + className);

String simpleName = c.getSimpleName();
System.out.println("简类名:" + simpleName + "\n");

// getFields()此方法,获取类中所有公开属性
Field[] f = c.getFields();

// 获取数组中第0位元素的名字
System.out.println(f[0].getName() + "\n");

// 获取类中所有属性的名字,getDeclaredFields()此方法返回Field数组
Field[] fs = c.getDeclaredFields();
for (Field field : fs) {
// 获取修饰符,getModifiers()返回一个整型,每一个数字对应一个修饰符代号
int i = field.getModifiers();
System.out.print("修饰符代号:" + i + "\t");
// 可以将这一个修饰符代号转换为字符串
System.out.print("对应修饰符名称:" + Modifier.toString(i) + "\t");
// 获取属性类型,getType()方法返回Class
Class fieldtype = field.getType();
// 通过Class可以调用getName,打印属性类型名称
System.out.print("完整类型名:" + fieldtype.getName() + "\t");
System.out.print("简单类型名:" + fieldtype.getSimpleName() + "\t");
// 获取属性名字
System.out.println("属性名:" + field.getName());
}
}
}

小案例:反编译类中属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*测试类*/
package re;
public class Demo {
public String name;
private int age;
protected String sex;
double moeny;
public static final float PI = 3.14f;
}

package re;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/*反编译测试类中属性*/
public class FieldTest01 {
public static void main(String[] args) throws Exception {
Class c = Class.forName("re.Demo");
StringBuilder s = new StringBuilder();
s.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName() + " {\n");
Field[] fs = c.getDeclaredFields();
for (Field f : fs) {
s.append("\t" + Modifier.toString(c.getModifiers()) + " " + f.getType().getSimpleName() + " " + f.getName() + ";" + "\n");
}
s.append("}");
System.out.println(s);
}
}

获取和设置属性值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Demo {
public String name;
private int age;
protected String sex;
double moeny;
public static final float PI = 3.14f;
}
// 重点
public static void main(String[] args) throws Exception {
// 通过反射机制给属性赋值和调佣(set和get)
Class c = Class.forName("Demo");
// 创建对象,底层调用了re.Demo类中无参构造方法实现的
Object obj = c.newInstance(); // obj为Demo对象
// getDeclaredField()方法给定参数(属性名),返回一个指定Field
Field f = c.getDeclaredField("name");
// get()方法给定参数(对象)获取Field的值,属性.get(对象)
System.out.println(f.get(obj));
f.set(obj, "张三"); // 设置Demo对象中name属性的值
System.out.println(f.get(obj));
// 设置私有属性的值
Field ageField = c.getDeclaredField("age");
// 打破封装,这种方式会使得私有属性在外部也能访问
ageField.setAccessible(true);
ageField.set(obj, 18);
System.out.println(ageField.get(obj));
}

可变长参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 语法:参数为数据类型...参数名
// 1,可变参数要求的是参数0~n个
// 2,最后一个,仅一个
// 例;
public static void main(String[] args) {
a();
a(23,4);
a(1);
b("我","是","中","国","人");
}
public static void a(int...age) {
System.out.println("a方法执行了!");
}
public static void b(String...value) {
// 可变长参数可以作为数组使用
for (String s : value) {
System.out.println(s);
}
}

反射方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package re;
public class User {
public boolean login(String name, String pwd) {
if ("xc".equals(name) && "123456".equals(pwd)) {
return true;
}
return false;
}
public void out() {
System.out.println("系统退出,谢谢使用!");
}
}

package re;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class MethodTest {
public static void main(String[] args) throws Exception {
Class c = Class.forName("re.User");
// 获取所有的方法(包括私有)
Method[] me = c.getDeclaredMethods();
for (Method method : me) {
// 获取访问修饰符
System.out.print(Modifier.toString(method.getModifiers()) + "\t");
// 获取返回值类型
System.out.print(method.getReturnType().getSimpleName() + "\t");
// 获取方法名
System.out.println(method.getName());
// 获取方法参数数据类型列表
Class[] type = method.getParameterTypes();
for (Class aClass : type) {
System.out.println(aClass.getSimpleName());
}
}
}
}

反编译方法签名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package re;
public class User {
public boolean login(String name, String pwd) {
if ("xc".equals(name) && "123456".equals(pwd)) {
return true;
}
return false;
}
public void out() {
System.out.println("系统退出,谢谢使用!");
}
}


package re;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class Demo2 {
public static void main(String[] args) throws Exception {
Class c = Class.forName("re.User");
StringBuilder sb = new StringBuilder();
sb.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName()+ " {\n");
Method[] md = c.getDeclaredMethods();
for (Method method : md) {
sb.append("\t");
sb.append(Modifier.toString(method.getModifiers()));
sb.append(" ");
sb.append(method.getReturnType().getSimpleName());
sb.append(" ");
sb.append(method.getName());
sb.append("(");
Class[] cs = method.getParameterTypes();
for (Class aClass : cs) {
sb.append(aClass.getSimpleName() + " ,");
}
if (cs.length != 0) {
// 删除指定下标上的字符
sb.deleteCharAt(sb.length() - 1);
}
sb.append(")");
sb.append("\n");
}
sb.append("}");
System.out.println(sb);
}
}

通过反射机制调用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package re;
import java.lang.reflect.Method;
// 重点
public class MethodTest2 {
public static void main(String[] args) throws Exception {
// 不使用反射机制调用方法
// 创建对象
User user = new User();
// 调用方法
boolean flag = user.login("xc", "123456");
System.out.println(flag ? "登陆成功" : "登录失败");

// 使用反射机制调用方法
Class c = Class.forName("re.User");
Object obj = c.newInstance();
// 获取Method
Method loginMethod = c.getDeclaredMethod("login", String.class, String.class);
// 调用方法
Object retrunValue = loginMethod.invoke(obj, "xc", "123");
// 强制转换类型
boolean flag2 = (Boolean) retrunValue;
System.out.println(flag2 ? "登陆成功" : "登录失败");
}
}

反编译构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package re;
/*测试类*/
public class Vip {
int no;
int age;
String name;
String birthday;

public Vip(int no) {
this.no = no;
}

public Vip(int no, int age) {
this.no = no;
this.age = age;
}

public Vip(int no, int age, String name) {
this.no = no;
this.age = age;
this.name = name;
}

public Vip(int no, int age, String name, String birthday) {
this.no = no;
this.age = age;
this.name = name;
this.birthday = birthday;
}
}



package re;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/*反编译构造方法*/
public class ConstructorTest {
public static void main(String[] args) throws Exception {
// 创建StringBuilder
StringBuilder sb = new StringBuilder();
// 获取Class
Class c = Class.forName("re.Vip");
// 获取修饰符列表
sb.append(Modifier.toString(c.getModifiers()));
sb.append(" class ");
// 获取简类名
sb.append(c.getSimpleName());
sb.append(" {\n");
// 获取属性(数组)
Field[] fd = c.getDeclaredFields();
for (Field field : fd) {
// 通过for循环遍历属性数组,获取属性数据类型,属性名
sb.append("\t" + field.getType().getSimpleName() + " " + field.getName() + ";\n");
}
sb.append("\n");
// 获取构造方法(数组)
Constructor[] cons = c.getDeclaredConstructors();
for (Constructor constructor : cons) {
sb.append("\t");
// 遍历数组获取修饰符列表,简答类名
sb.append(Modifier.toString(c.getModifiers()));
sb.append(" " + c.getSimpleName() + "(");
// 获取参数数据类型列表
Class[] par = constructor.getParameterTypes();
for (Class aClass : par) {
// 获取参数数据类型简名
sb.append(aClass.getSimpleName() + ",");
}
// 判断如果数组的长度不为零,就删除逗号
if (par.length != 0) {
sb.deleteCharAt(sb.length() - 1);
}
sb.append(") {}\n");
}
sb.append("}");
// 打印输出
System.out.println(sb);
}}
}

调用构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package re;
/*测试类*/
public class Vip {
int no;
int age;
String name;
String birthday;

public Vip(){}

public Vip(int no) {
this.no = no;
}

public Vip(int no, int age) {
this.no = no;
this.age = age;
}

public Vip(int no, int age, String name) {
this.no = no;
this.age = age;
this.name = name;
}

public Vip(int no, int age, String name, String birthday) {
this.no = no;
this.age = age;
this.name = name;
this.birthday = birthday;
}

@Override
public String toString() {
return "Vip{" +
"no=" + no +
", age=" + age +
", name='" + name + '\'' +
", birthday='" + birthday + '\'' +
'}';
}
}


package re;

import java.lang.reflect.Constructor;
/*调用构造方法实例化对象*/
public class ConstructorTest1 {
public static void main(String[] args) throws Exception {
// 不使用反射机制调用构造方法
Vip vip = new Vip();
Vip vip1 = new Vip(110,24,"xc", "2002-10-15");
System.out.println(vip1);

// 使用反射机制调用构造方法
Class c = Class.forName("re.Vip");
// 调用无参数构造方法
Object obj = c.newInstance();
System.out.println(obj);
// 调用有参数构造方法
// 第一步,获取要调用的构造方法
Constructor con = c.getDeclaredConstructor(int.class, int.class, String.class, String.class);
// 调用有参构造方法,创建对象
Object newobj = con.newInstance(110,24,"xc", "2002-10-15");
System.out.println(newobj);

// 调用无参数构造方法
Constructor con2 = c.getDeclaredConstructor();
// 调用无参构造方法,创建对象
Object newobj2 = con.newInstance();
System.out.println(newobj2);
}
}

获取父类和父接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) throws Exception {
// 获取父类和父接口
// String举例
Class c = Class.forName("java.lang.String");

// 获取String的父类
Class supers = c.getSuperclass();
System.out.println(supers.getName());

// 获取接口
Class[] interfaces = c.getInterfaces();
for (Class in : interfaces) {
System.out.println(in.getName());
}
}

注解

  1. 注解,也称作注释,英文单词Annotation

    注解可以干什么?


  2. 注解也是一种引用数据类型,编译之后也会生成xxx.class文件

  3. 怎么自定义注解?语法格式?

    [修饰符列表] @interface 注解类型名 {}

  4. 注解使用场景

    1,注解使用时的语法格式是:@注解类型名

    2,注解可以出现在类上,属性上,方法上,变量上等等…

    3,注解还可以出现在注解类型上

  5. jdk内置了哪些注解

    Annotation TypeAnnotation Type
    已过时的注释@Deprecated的程序元素是程序员不鼓励使用的程序元素,通常是因为它是危险的,或者因为存在更好的替代方法。
    FunctionalInterface使用的信息注释类型,以指示在接口类型声明旨在是一个 功能接口由Java语言规范所定义的。
    Override表示方法声明旨在覆盖超类型中的方法声明。
    SafeVarargs程序员断言注释方法或构造函数的正文不会对其varargs参数执行潜在的不安全操作。
    SuppressWarnings表示在注释元素(以及注释元素中包含的所有程序元素)中应该抑制命名的编译器警告。
  6. @Override标志性注解

    @Override注解只能用在方法,

    @Override这个注解是给编译器参考的,和运行阶段没有关系

    凡是Java中的方法有这个注解的,编译器都会进行编译检查,如果这个方法

    不是重写父类中的方法,编译器会编译时错误

  7. 元注解

    解释:用来标注“注解类型”的“注解”,被称为元注解

    常见的元注解:Target,Retention

    关于Target注解

    这是一个元注解,用来标注“注解类型”的“注解”
                            
    target注解用来标注“被标注的注解”可以出现在哪些位置上
    

    @Target(ElementType.METHOD):表示“被标注的注解”只能用在方法上

    @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})

    表示可以出现在,构造方法,属性,局部变量,方法,包,模块,参数,类上

    关于Retention注解

    这是一个元注解,用来标注“注解类型”的“注解”
                            
    Retention注解用来标注“被标注的注解”最终保存到哪
    

    @Retention(RetentionPolicy.SOURCE):表示该注解只保留在java源文件中

    @Retention(RetentionPolicy.CLASS):表示该注解被保存在class文件中

    @Retention(RetentionPolicy.RUNTIME):表示该注解被保存在class文件中,并且可以被反射机制读取

  8. Deprecated,已过时,在调用的时候会出现横线,用于向其他程序员传递一个信息

注解中定义属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package at;
/*
自定义注解:MyAnnotation
* */
public @interface MyAnnotation {
/*
* 通常在注解中可以定义属性,以下为MyAnnotation中的内部属性
* 非常像方法
* */
String name();

// 给属性指定默认值
int age() default 18;

// 邮箱属性
String[] emil();
}

package at;
public class AnnotationTest {
// 在使用时,如果一个注解中有属性,必须给属性赋值
// 除非有默认值,则可以不写
// 属性数组中只有一个元素时可以省略大括号
@MyAnnotation(name = "zhangsan", emil = "fx@outlook.com")
public AnnotationTest() {}
// 属性数组有多个元素,则不能省略大括号
@MyAnnotation(name = "zhangsan", emil = {"fx@outlook.com", "45@123.com"})
public AnnotationTest() {}
}

关于属性名为value时省略问题:

如果一个注解中只有一个属性名为value,那么可以省略,如果有多个属性,则不能省略

注解中的属性类型可以是:

byte,short,int,long。float,double。boolean,char,String,Class,枚举类型

以及上述每一种的数组形式

反射注解

获取类上的注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package at;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/*
自定义注解:MyAnnotation
* */
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
/*
* 通常在注解中可以定义属性,以下为MyAnnotation中的内部属性
* 非常像方法
* */
String name();

// 给属性指定默认值
int age() default 18;

String value();
}


package at;
/*注解*/
@MyAnnotation(name = "lisi", value = "12")
public class AnnotationTest {
// 在使用时,如果一个注解中有属性,必须给属性赋值
@MyAnnotation(name = "zhangsan", value = "12")
public AnnotationTest() {}
}

package at;
/*通过反射机制获取到注解的值*/
public class Test {
public static void main(String[] args) throws Exception {
Class c = Class.forName("at.AnnotationTest");
// 判断这个类中是否有MyAnnotation注解
if (c.isAnnotationPresent(MyAnnotation.class)) {
// 如果有则可以获取到注解对象
MyAnnotation m = (MyAnnotation) c.getAnnotation(MyAnnotation.class);
// 获取到注解对象则可以获取到值,如果不是value则可以点到其他属性
String name = m.value();
System.out.println(name);

String name1 = m.name();
System.out.println(name1);
}
}
}

获取方法上的注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package at;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Ma {
String user();
String password();
}


package at;
import java.lang.reflect.Method;
public class Test1 {
@Ma(user = "xc",password = "123")
public void todo() {}

public static void main(String[] args) throws Exception {
Class c = Class.forName("at.Test1");
Method m = c.getDeclaredMethod("todo");
if (m.isAnnotationPresent(Ma.class)) {
Ma ma = m.getAnnotation(Ma.class);
System.out.println(ma.user());
System.out.println(ma.password());
}
}
}

JDBC

使用Java链接数据库六步曲(非常重要)

  1. 注册驱动(作用:告诉Java程序,即将要连接的数据库是哪个品牌的数据库)
  2. 获取连接(表示JVM的进程和数据库进程之间的通道打开了,属于进程之间的通信,使用后要关闭对象)
  3. 获取数据库操作对象(专门执行SQL语句的对象)
  4. 执行SQL语句(DQL,DML)
  5. 处理查询结果集(只有当第四步执行的是select的语句的时候,才有第五步的查询结果集)
  6. 释放资源(使用完资源之后一定要关闭资源)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package day1;

import com.mysql.cj.jdbc.Driver;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

/*
* JDBC编程六部曲
* */
public class JdbcTest01 {
public static void main(String[] args) {
Statement smt = null;
Connection connection = null;
try {
// 1.注册驱动
DriverManager.registerDriver(new Driver());

// 2.获取连接
// 加在库名后面“?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false”
String url = "jdbc:mysql://localhost:3306/testdb";
String user = "root";
String password = "xcfx";
connection = DriverManager.getConnection(url,user,password);
// com.mysql.cj.jdbc.ConnectionImpl@34a3d150
System.out.println("数据库对象:" + connection);

// 3.获取数据库操作对象(Statement专门执行SQL语句的)
smt = connection.createStatement();

// 4.执行SQL语句
String sql2 = "update employee set Salary = 5000 where Name = '张三'";
String sql = "insert into employee values(default,'女','黑马',2000,'2000-5-3',1)";

// 此语句专门执行DML语句(insert delete update)
// 返回受影响的行数
int count = smt.executeUpdate(sql2);
System.out.println(count == 1 ? "操作成功" : "操作失败");

} catch (SQLException e) {
e.printStackTrace();
} finally {
// 6.关闭资源
// 要遵循从小到大依次关闭
if (smt != null) {
try {
smt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
try {
// 第二种方式注册驱动,通过反射机制导致类加载
Class.forName("com.mysql.cj.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/testdb", "root", "xcfx");
System.out.println(conn);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}

通过配置文件方式获取数据库信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package day1;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ResourceBundle;

public class JdbcTest03 {
public static void main(String[] args) {
// 资源绑定器
ResourceBundle bundle = ResourceBundle.getBundle("day1/configbase");
String driver = bundle.getString("driver");
String url = bundle.getString("url");
String user = bundle.getString("user");
String password = bundle.getString("password");

Connection conn = null;
Statement smtt = null;
try {
Class.forName(driver);

conn = DriverManager.getConnection(url,user,password);

smtt = conn.createStatement();

String sql = "update employee set Salary = 5000 where Name = '张三'";

int count = smtt.executeUpdate(sql);

System.out.println(count == 1 ? "操作成功" : "操作失败");
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
} finally {
if (smtt != null) {
try {
smtt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

}
}

配置文件

1
2
3
4
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/testdb
user=root
password=xcfx

处理查询结果集(遍历结果集)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package day1;

import java.sql.*;

// 处理查询结果集(遍历结果集)
public class JdbcTest04 {
public static void main(String[] args) {
Connection sc = null; // 连接对象
Statement smt = null; // 操作对象
ResultSet re = null; // 结果集
try {
// 1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.获取连接
sc = DriverManager.getConnection("jdbc:mysql://localhost:3306/testdb","root","xcfx");
// 3.获取数据库操作对象
smt = sc.createStatement();
// 4.执行SQL语句
String sql = "select * from employee";
// int executeUpdate(insert/update/delete)
// Resultset executeQuery(select)
re = smt.executeQuery(sql); // executeQuery专门执行dql语句的方法,返回单个resultset对象
// 5.处理查询结果集
while (re.next()) {
// getString方法可以以列表下表获取,也可以以列的名字获取
// 如果列有别名,则按照别名获取
// 除getstring以string类型取出数据外,还可以以其他类型取出数据
// 好处是可以直接做数学运算
int n1 = re.getInt(1);
String n2 = re.getString(2);
String n3 = re.getString("gender");
String n4 = re.getString("salary");
String n5 = re.getString("birthday");
String n6 = re.getString("starid");
System.out.println((n1 + 1) + "\t" + n2 + "\t" + n3 + "\t" + n4 + "\t" + n5 + "\t" + n6);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 6.释放资源(从小到大)
if (re != null) {
try {
re.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (smt != null) {
try {
smt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (sc != null) {
try {
sc.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}

模拟用户登录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package day1;

import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class JdbcTest05 {
public static void main(String[] args) {
// 界面初始化
Map<String,String> ui = intoUI();
// 验证用户名和密码
boolean loginSuccess = login(ui);
// 最后输出结果
System.out.println(loginSuccess ? "登陆成功" : "登录失败");
}

/**
* 用户登录
* @param ui 用户登录信息
* @return false表示失败,true表示成功
*/
private static boolean login(Map<String, String> ui) {
// JDBC代码
Connection conn = null;
Statement smt = null;
ResultSet re = null;
// 表示
boolean flag = false;
try {
// 1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","xcfx");
// 3.获取数据库操作对象
smt = conn.createStatement();
// 4.执行SQL语句
String sql = "select * from t_user where user = '"+ui.get("userName")+"' and pwd = '"+ui.get("pwd")+"'";
re = smt.executeQuery(sql);
// 5.处理查询结果集
if (re.next()) {
flag = true;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 6.释放资源
if (re != null) {
try {
re.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (smt != null) {
try {
smt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

return flag;
}

/**
* 初始化界面
* @return 用户输入的用户名或密码的信息
*/
private static Map<String, String> intoUI() {
Scanner sc = new Scanner(System.in);
System.out.print("用户名:");
String userName = sc.next();
System.out.print("密码:");
String pwd = sc.next();

Map<String,String> userlogin = new HashMap<String,String>();
userlogin.put("userName",userName);
userlogin.put("pwd",pwd);
return userlogin;
}
}

  • 上述代码存在SQL注入问题

    例:用户名:fa
    密码:fa’or’1’=’1
    登陆成功

    截图

如果上述SQL中包含关键字,那么此处已经完成了SQL语句的拼接,此时SQL会发送给DBMS,DBMS会进行SQL编译

截图

解决SQL注入问题

只要用户提供的信息不参与SQL语句的编译过程,问题就解决了

即使用户提供的信息中包含有SQL语句的关键字,但是没有参与编译,也不起作用

若想用户信息不参与SQL语句编译,那么必须使用java.sql.PreparedStatement

PreparedStatement接口继承了java.sql.Statement

PreparedStatement是属于预编译的数据库操作对象

PreparedStatement原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传”值”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package day1;

import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

/**
* 解决SQL注入问题
* 只要用户提供的信息不参与SQL语句的编译过程,问题就解决了
* 即使用户提供的信息中包含有SQL语句的关键字,但是没有参与编译,也不起作用
* 若想用户信息不参与SQL语句编译,那么必须使用java.sql.PreparedStatement
* PreparedStatement接口继承了java.sql.Statement
* PreparedStatement是属于预编译的数据库操作对象
* PreparedStatement原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传"值"
*/
public class JdbcTest06 {
public static void main(String[] args) {
// 界面初始化
Map<String,String> ui = intoUI();
// 验证用户名和密码
boolean loginSuccess = login(ui);
// 最后输出结果
System.out.println(loginSuccess ? "登陆成功" : "登录失败");
}

/**
* 用户登录
* @param ui 用户登录信息
* @return false表示失败,true表示成功
*/
private static boolean login(Map<String, String> ui) {
// JDBC代码
Connection conn = null;
PreparedStatement ps = null; // 这里使用PreparedStatement(预编译数据库操作对象)
ResultSet re = null;
// 表示
boolean flag = false;
try {
// 1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","xcfx");
// 3.获取预编译的数据库操作对象
// 这是SQL语句的框子(框架)一个?代表一个占位符,一个?接收一个值,?不能使用单引号括起来
String sql = "select * from t_user where user = ? and pwd = ?";
ps = conn.prepareStatement(sql);
// 给占位符?传值JDBC中所有下表都从1开始
ps.setString(1, ui.get("userName"));
ps.setString(2, ui.get("pwd"));
// 4.执行SQL语句
re = ps.executeQuery();
// 5.处理查询结果集
if (re.next()) {
flag = true;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 6.释放资源
if (re != null) {
try {
re.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return flag;
}

/**
* 初始化界面
* @return 用户输入的用户名或密码的信息
*/
private static Map<String, String> intoUI() {
Scanner sc = new Scanner(System.in);
System.out.print("用户名:");
String userName = sc.next();
System.out.print("密码:");
String pwd = sc.next();

Map<String,String> userlogin = new HashMap<String,String>();
userlogin.put("userName",userName);
userlogin.put("pwd",pwd);
return userlogin;
}
}

Statement和PreparedStatement对比

  1. Statement存在SQL注入问题

  2. Statement编译一次执行一次,PreparedStatement编译一次执行n次,效率略高

  3. PreparedStatement会在编译阶段做类型安全检查

  4. 什么时候用Statement?

    项目支持SQL注入或需要进行SQL语句拼接的情况下,必须使用Statement

Statement用法示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package day1;

import java.sql.*;
import java.util.Scanner;

public class JdbcTset07 {
public static void main(String[] args) {
// 模拟用户升序和降序
Scanner sc = new Scanner(System.in);
System.out.println("请输入desc/asc,desc表示升序,asc表示降序");
System.out.print("请输入:");
String keywords = sc.next();
// JDBC编程六步
Connection conn = null;
Statement smt = null;
ResultSet re = null;
try {
// 注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");

// 获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","xcfx");

// 获取数据库操作对象
smt = conn.createStatement();

// 执行SQL
String sql = "select * from t_user order by id " + keywords;
re = smt.executeQuery(sql);

// 遍历结果集
while (re.next()) {
System.out.println(re.getString("user") + "\t" + re.getString("pwd"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放资源
if (re != null) {
try {
re.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (smt != null) {
try {
smt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}

PreparedStatement完成增删改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package day1;

import java.sql.*;

// PreparedStatement完成增删改
public class JdbcTest08 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;

try {
// 注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");

// 获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","xcfx");

// 获取预编译数据库操作对象
String sql = "insert into t_user values(default,?,?)";
ps = conn.prepareStatement(sql);
ps.setString(1,"jack");
ps.setString(2,"hello");

// 执行SQL
int count = ps.executeUpdate();
System.out.println(count);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭资源
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}

JDBC事务机制

1,JDBC中的事务是自动提交的,什么是自动提交?

只要执行任意一条SQL语句,则自动提交一次。这是JDBC默认的事务行为

但是在实际的业务中,通常都是n条DML语句共同联合才能完成的,

必须保证这些DML语句在同一个事务中同时成功或同时失败

2,以下程序先验证JDBC事务是否是自动提交机制

结论,Jdbc中只要执行任意一条DML语句,就提交一次
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/-----JDB代码省略

// 获取预编译数据库操作对象
String sql = "update t_user set pwd=? where id=?";
ps = conn.prepareStatement(sql);
ps.setString(1,"jack");
ps.setInt(2,3); // 修改id为3的密码

// 执行SQL
int count = ps.executeUpdate();
System.out.println(count);

// 重新给占位符传值
ps = conn.prepareStatement(sql);
ps.setString(1,"meke");
ps.setInt(2,1); // 修改id为2的密码
count = ps.executeUpdate();

System.out.println(count);

模拟银行账户转账

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package day1;

import java.sql.*;

/**
* 本节点重点,
* 将自动提交机制修改为手动提交
* conn.setAutoCommit(false); 开启事务
*
* 如果程序能运行到这里说明上述代码没有异常,事务结束,手动提交
* conn.commit(); 提交事务
*
* if (conn != null) {
* try {
* // 回滚事务
* conn.rollback();
* } catch (SQLException ex) {
* ex.printStackTrace();
* }
* }
*/
public class JdbcTest10 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;

try {
// 注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");

// 获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","xcfx");
// 将自动提交机制修改为手动提交
conn.setAutoCommit(false); // 开启事务

// 获取预编译数据库操作对象
String sql = "update t_act set balance=? where actno=?";
ps = conn.prepareStatement(sql);

// 给?传值
ps.setDouble(1,10000);
ps.setInt(2,111);
int count = ps.executeUpdate(); // 执行SQL语句

// 此时再出现异常,则不会自动提交
// String s = null;
// s.toString();

// 再次修改
ps.setDouble(1,10000);
ps.setDouble(2,222);
count += ps.executeUpdate(); // 再次执行SQL语句

System.out.println(count == 2 ? "转账成功" : "转账失败");

// 如果程序能运行到这里说明上述代码没有异常,事务结束,手动提交
conn.commit(); // 提交事务
} catch (Exception e) {
if (conn != null) {
try {
// 回滚事务
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
e.printStackTrace();
} finally {
// 关闭资源
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}

表t_act

1
2
3
4
5
6
7
DROP TABLE IF EXISTS `t_act`;
CREATE TABLE `t_act` (
`actno` int NULL DEFAULT NULL,
`balance` double(8, 2) NULL DEFAULT NULL
)
INSERT INTO `t_act` VALUES (111, 20000.00);
INSERT INTO `t_act` VALUES (222, 0.00);

实现模糊查询

工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package util;

import java.sql.*;

// JDBC工具类
public class JDBCutil {

/**
* 工具类中的构造方法一般都是私有的
* 因为工具类中的方法都是静态的,不需要new对象,直接采用类名调用
*/
private JDBCutil() {}

// 静态代码块在类加载时执行,且执行一次
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

/**
* 获取数据库连接对象
* @return 返回连接对象
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","xcfx");
}

/**
* 关闭资源
* @param conn 连接对象
* @param ps 数据库操作对象
* @param rs 结果集对象
*/
public static void close(Connection conn, Statement ps, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package day1;

import util.JDBCutil;

import java.sql.*;

/**
* 这个程序有两个任务
* 1.测试JDBCutil工具类是否好用
* 2.实现模糊查询
*/
public class JdbcTest11 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 获取连接
conn = JDBCutil.getConnection();
// 获取预编译的数据库操作对象
String sql = "select * from t_user where user like ?";
ps = conn.prepareStatement(sql); // 编译SQL语句
ps.setString(1,"%x%"); // 给?传值
rs = ps.executeQuery(); // 执行SQL语句,返回结果集
// 遍历结果集
while (rs.next()) {
System.out.println(rs.getString("user") + "\t" + rs.getString("pwd"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放资源
JDBCutil.close(conn,ps,rs);
}
}
}

行级锁

在SQL语句select后加上for update,此数据就会锁住,其他事物不可修改

jdbc封装类(Dao)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package util;

import java.sql.*;
import java.util.ResourceBundle;

public class BaseDao {
static String driver;
static String url;
static String user;
static String pwd;

static{
// 资源绑定器
ResourceBundle bundle = ResourceBundle.getBundle("day1/configbase");
driver = bundle.getString("driver");
url = bundle.getString("url");
user = bundle.getString("user");
pwd = bundle.getString("password");
}

Connection conn;
PreparedStatement ps;
ResultSet rs;

void getConnection(){
try {
Class.forName(driver);
conn = DriverManager.getConnection(url,user,pwd);
} catch (Exception e) {
e.printStackTrace();
}
}

//增删改查
//查询 executeQuery
//增删改 executeUpdate
public ResultSet executeQuery(String sql, Object... params){
rs = null;
try {
getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
ps.setObject(i+1, params[i]);
}
rs = ps.executeQuery();
} catch (SQLException e) {
e.printStackTrace();
}
return rs;
}

public int executeUpdate(String sql, Object... params){
int num = 0;
try {
getConnection();
ps = conn.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
ps.setObject(i+1, params[i]);
}
num = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally{
closeAll();
}
return num;
}

public void closeAll(){
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

}
}

使用Dao类,完成CRUD操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package day2;

import util.BaseDao;

import java.sql.ResultSet;
import java.util.Scanner;

public class Test01 {
public static void main(String[] args) throws Exception {
BaseDao bao = new BaseDao();
ResultSet rs = null;
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("欢迎进入科目管理系统");
System.out.println("********************");
System.out.println("\t1.查询所有科目");
System.out.println("\t2.新增科目");
System.out.println("\t3.修改");
System.out.println("\t4.删除");
System.out.println("\t0.退出");
System.out.println("********************");
System.out.print("请选择,输入数字:");
int num = sc.nextInt();
switch (num) {
case 1:
String sql1 = "select * from subject";
rs = bao.executeQuery(sql1);
System.out.println("编号\t名称\t课时\t年级编号");
while (rs.next()) {
int n1 = rs.getInt(1);
String n2 = rs.getString("subjectname");
int n3 = rs.getInt("classhour");
int n4 = rs.getInt("gradeid");
System.out.println(n1 + "\t" + n2 + "\t" + n3 + "\t" + n4);
}
bao.closeAll();
break;
case 2:
System.out.print("请输入新增的科目名:");
String subjectname = sc.next();
System.out.print("请输入新增的课时:");
String classhour = sc.next();
System.out.print("请输入新增科目所在年级编号:");
String gradeid = sc.next();

String sql2 = "insert into subject values(null,?,?,?)";
int count = bao.executeUpdate(sql2,subjectname,classhour,gradeid);

System.err.println(count == 1 ? "成功" : "失败");
break;
case 3:
System.out.print("请输入科目编号:");
String no = sc.next();
System.out.print("请输入科目名称:");
String subname = sc.next();

// 获取预编译数据库操作对象
String sql3 = "update subject set subjectname=? where subjectid=?";

int count1 = bao.executeUpdate(sql3,subname,no);

System.err.println(count1 == 1 ? "成功" : "失败");
break;
case 4:
System.out.print("请输入要删除的科目编号:");
int subno = sc.nextInt();

String sql4 = "delete from `subject` where subjectid=?";

int count2 = bao.executeUpdate(sql4,subno);
System.err.println(count2 == 1 ? "成功" : "失败");
break;
case 0:
System.err.println("\n系统退出,谢谢使用!\n");
return;
default:
System.err.println("\n输入有误,请重新输入数字:\n");
}
}
}
}

Dao模式三层

为解决业务代码和数据访问代码的紧耦合给修改和维护代码带来的不便,推荐使用DAO模式封装JDBC

DAO模式全称是:data access object,数据库访问对象

组成部分

  1. DAO接口
  2. DAO实现类
  3. 实体类
  4. 数据库连接和关闭工具类

DAO模式的优势

隔离了数据访问代码和业务逻辑代码 隔离了不同数据库实现

具体实现步骤

  1. 新建实体类层 com.xx.bean

数据库中的每张表,对应为一个类。

每个字段,对应类中的一个属性字段。

注意:字段命名时,前2个字母要么都大写,要么都小写

  1. dao层 com.xx.dao

database.properties 配置文件在项目根目录下

dao层中有:BaseDao.java IBaseService.java 封装所有表共有的操作:增删改查

每张表创建一个接口类,比如: public interface PetDao extends IBaseService{}

如果某个类有额外的方法需要新增,比如登录验证,就定义在这个接口类中。

  1. dao的实现类层 com.xx.dao.impl

每张表创建一个接口的实现类,比如:

public class PetDaoImpl extends BaseDao implements PetDao {

​ 实现接口中的所有方法。

​ 注意:查询的方法,需要在finally中调用,closeAll();

}

  1. 测试类层 com.xx.test

PetDao pd = new PetDaoImpl();

pd.调用增删改查的方法即可。

案例

以myschool数据库为例,实现增删改查

Grade实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.myschool.bean;

public class Grade {
private Integer gradeid;
private String gradeidname;

public Grade(Integer gradeid, String gradeidname) {
super();
this.gradeid = gradeid;
this.gradeidname = gradeidname;
}

public Integer getGradeid() {
return gradeid;
}

public void setGradeid(Integer gradeid) {
this.gradeid = gradeid;
}

public String getGradeidname() {
return gradeidname;
}

public void setGradeidname(String gradeidname) {
this.gradeidname = gradeidname;
}

@Override
public String toString() {
return "Grade{" +
"gradeid=" + gradeid +
", gradeidname='" + gradeidname + '\'' +
'}';
}

}

Subject实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.myschool.bean;

public class Subject {
private Integer subjectid;
private String subjectname;
private Integer classhour;
private Integer gradeid;

public Subject() {
}

public Subject(Integer subjectid, String subjectname, Integer classhour, Integer gradeid) {
this.subjectid = subjectid;
this.subjectname = subjectname;
this.classhour = classhour;
this.gradeid = gradeid;
}

public Integer getSubjectid() {
return subjectid;
}

public void setSubjectid(Integer subjectid) {
this.subjectid = subjectid;
}

public String getSubjectname() {
return subjectname;
}

public void setSubjectname(String subjectname) {
this.subjectname = subjectname;
}

public Integer getClasshour() {
return classhour;
}

public void setClasshour(Integer classhour) {
this.classhour = classhour;
}

public Integer getGradeid() {
return gradeid;
}

public void setGradeid(Integer gradeid) {
this.gradeid = gradeid;
}

@Override
public String toString() {
return "Subject{" +
"subjectid=" + subjectid +
", subjectname='" + subjectname + '\'' +
", classhour=" + classhour +
", gradeid=" + gradeid +
'}';
}
}

封装类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.myschool.dao;

import java.util.List;

//封装所有表共有的操作:增删改查
public interface IBaseService<T> {
List<T> select();
T selectById(Object id);
boolean insert(T t);
boolean update(T t);
boolean delete(Object id);
int getTotal();
}

Grade接口

1
2
3
4
5
6
7
package com.myschool.dao;

import com.myschool.bean.Grade;

public interface GradeDao extends IBaseService<Grade>{
}

Subject接口

1
2
3
4
5
6
7
8
9
10
11
package com.myschool.dao;

import com.myschool.bean.Subject;

import java.util.List;

public interface SubjectDao extends IBaseService<Subject>{
List<Subject> order(Object id);
List<Subject> blurry(Object key);
}

Grade实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package com.myschool.dao.impl;

import com.myschool.bean.Grade;
import com.myschool.dao.BaseDao;
import com.myschool.dao.GradeDao;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class GradeDaoImpl extends BaseDao implements GradeDao {
@Override
public List<Grade> select() {
List<Grade> list = new ArrayList<>();
String sql = "select * from Grade";
ResultSet rs = executeQuery(sql);
try {
while (rs.next()) {
Grade g = new Grade(rs.getInt(1),rs.getString(2));
list.add(g);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
closeAll();
}
return list;
}

@Override
public Grade selectById(Object id) {
Grade g = null;
String sql = "select * from Grade where gradeId=?";
ResultSet rs = executeQuery(sql,id);
try {
while (rs.next()) {
g = new Grade(rs.getInt(1),rs.getString(2));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
closeAll();
}
return g;
}

@Override
public boolean insert(Grade grade) {
String sql = "insert into grade values (null,?)";
// 如果表中字段很多,接着,写下去
return executeUpdate(sql,grade.getGradeidname()) > 0;
}

@Override
public boolean update(Grade grade) {
String sql = "update grade set gradeName=? where gradeId=?";
return executeUpdate(sql,grade.getGradeidname(),grade.getGradeid()) > 0;
}

@Override
public boolean delete(Object id) {
String sql = "delete from grade where gradeId=?";
return executeUpdate(sql,id) > 0;
}

@Override
public int getTotal() {
int num = 0;
String sql = "select count(1) from grade";
ResultSet rs = executeQuery(sql);
try {
if (rs.next()) {
num = rs.getInt(1);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
closeAll();
}
return num;
}
}

Subject实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package com.myschool.dao.impl;

import com.myschool.bean.Subject;
import com.myschool.dao.BaseDao;
import com.myschool.dao.SubjectDao;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class SubjectDaoImpl extends BaseDao implements SubjectDao {
@Override
public List<Subject> select() {
List<Subject> list = new ArrayList<>();
String sql = "select * from Subject";
ResultSet rs = executeQuery(sql);
try {
while (rs.next()) {
Subject s = new Subject(rs.getInt(1),rs.getString(2),rs.getInt(3),rs.getInt(4));
list.add(s);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
closeAll();
}
return list;
}

@Override
public List<Subject> order(Object id) {
List<Subject> list = new ArrayList<>();
String sql = "select * from Subject order by subjectId " + id;
ResultSet rs = executeQuery(sql);
try {
while (rs.next()) {
Subject s = new Subject(rs.getInt(1),rs.getString(2),rs.getInt(3),rs.getInt(4));
list.add(s);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
closeAll();
}
return list;
}

@Override
public List<Subject> blurry(Object key) {
List<Subject> list = new ArrayList<>();
String sql = "select * from Subject where subjectName like ?";
ResultSet rs = executeQuery(sql,key);
try {
while (rs.next()) {
Subject s = new Subject(rs.getInt(1),rs.getString(2),rs.getInt(3),rs.getInt(4));
list.add(s);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
closeAll();
}
return list;
}

@Override
public Subject selectById(Object id) {
Subject s = null;
String sql = "select * from Subject where subjectId=?";
ResultSet rs = executeQuery(sql,id);
try {
while (rs.next()) {
s = new Subject(rs.getInt(1),rs.getString(2),rs.getInt(3),rs.getInt(4));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
closeAll();
}
return s;
}

@Override
public boolean insert(Subject subject) {
String sql = "insert into Subject values (null,?,?,?)";
return executeUpdate(sql,subject.getSubjectname(),subject.getClasshour(),subject.getGradeid()) > 0;
}

@Override
public boolean update(Subject subject) {
String sql = "update subject set subjectName=? where subjectId=?";
return executeUpdate(sql,subject.getSubjectname(),subject.getSubjectid()) > 0;
}

@Override
public boolean delete(Object id) {
String sql = "delete from Subject where subjectId=?";
return executeUpdate(sql,id) > 0;
}

@Override
public int getTotal() {
int num = 0;
String sql = "select count(1) from Subject";
ResultSet rs = executeQuery(sql);
try {
if (rs.next()) {
num = rs.getInt(1);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
closeAll();
}
return num;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package com.test;

import com.myschool.bean.Subject;
import com.myschool.dao.*;
import com.myschool.dao.impl.*;

import java.util.Scanner;

public class Test1 {
public static void main(String[] args) {
SubjectDao sd = new SubjectDaoImpl();
GradeDao gd = new GradeDaoImpl();
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("欢迎进入科目管理系统");
System.out.println("********************");
System.out.println("\t1.查询所有科目");
System.out.println("\t2.新增科目");
System.out.println("\t3.修改");
System.out.println("\t4.删除");
System.out.println("\t5.升降序");
System.out.println("\t6.模糊查询");
System.out.println("\t0.退出");
System.out.println("********************");
System.out.print("请选择,输入数字:");
int num = sc.nextInt();
switch (num) {
case 1:
// 1、查询所有科目
// 编号 名称 课时 年级编号
// 1 java 80 1
// 2 xx

System.out.println("编号 \t名称 \t课时 \t年级编号");
for (Subject s : sd.select()) {
System.out.println("\033[1;34m" + s.getSubjectid() + "\t"
+ s.getSubjectname() + " \t" + s.getClasshour()
+ "\t" + gd.selectById(s.getGradeid()).getGradeidname() + "\033[0m");
}
// s.getGradeid()
System.out.println();
break;
case 2:
// 2、新增科目
// 请输入新增的科目名:
// 请输入新增的课时:
// 请输入新增科目所在年级编号:
System.out.print("请输入新增的科目名:");
String subjectName = sc.next();
System.out.print("请输入新增的课时:");
int classHour = sc.nextInt();
System.out.print("请输入新增科目所在年级编号:");
int gradeId = sc.nextInt();

Subject subject = new Subject(null,subjectName,classHour,gradeId);
System.out.println(sd.insert(subject) ? "\033[1;32m成功\033[0m" : "\033[1;31m失败\033[0m");
System.out.println();
break;
case 3:
// 3、修改
// 请输入要修改的科目编号:
// 请输入修改后的科目名称:
System.out.print("请输入科目编号:");
int no = sc.nextInt();
System.out.print("请输入科目名称:");
String subName = sc.next();

Subject subject1 = new Subject(no,subName,null,null);
System.out.println(sd.update(subject1) ? "\033[1;32m成功\033[0m" : "\033[1;31m失败\033[0m");
System.out.println();
break;
case 4:
// 4、删除
// 请输入要删除的科目编号:
System.out.print("请输入要删除的科目编号:");
int number = sc.nextInt();

System.out.println(sd.delete(number) ? "\033[1;32m成功\033[0m" : "\033[1;31m失败\033[0m");
System.out.println();
break;
case 5:
// 5、升降序
// 输入关键字
System.out.print("请输入desc/asc,desc表示升序,asc表示降序:");
String keywords = sc.next();

System.out.println("编号\t名称\t课时\t年级编号");
for (Subject s : sd.order(keywords)) {
System.out.println("\033[1;33m" + s.getSubjectid() + "\t" + s.getSubjectname() +
"\t" + s.getClasshour() + "\t" + gd.selectById(s.getGradeid()).getGradeidname()
+ "\033[0m");
}
System.out.println();
break;
case 6:
// 6、实现模糊查询
// 输入关键字进行查询
System.out.print("请输入关键字(根据科目名查找):");
String like = sc.next();

for (Subject s : sd.blurry(like)) {
System.out.println("\033[1;35m" + s.getSubjectid() + "\t" + s.getSubjectname() +
"\t" + s.getClasshour() + "\t" + gd.selectById(s.getGradeid()).getGradeidname()
+ "\033[0m");
}
System.out.println();
break;
case 0:
// 0、退出
System.err.println("\n系统退出,谢谢使用!\n");
return;
default:
System.err.println("\n输入有误,请重新输入数字:\n");
}
}
}
}

第九章-课程总复习

网页模板内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<title>模板</title>
</head>
<body>
<table align="center" width="95%" border="1">
<tr>
<td width="10%"><b>标题:</b></td>
<td>{Title}</td>
</tr>
<tr>
<td width="10%"><b>作者:</b></td>
<td>{Author}</td>
</tr>
<tr>
<td width="10%"><b>时间:</b></td>
<td>{Date}</td>
</tr>
<tr>
<td width="10%"><b>内容:</b></td>
<td>{Body}</td>
</tr>
</table>
</body>
</html>

此处省略实体类和实现类,只保留测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package com.test;

import com.news.bean.*;
import com.news.dao.Impl.*;
import com.news.dao.*;

import java.io.*;

public class test1 {
public static void main(String[] args) {
NewsDao n = new NewsDaoImpl();

//读取:建立文件到内存流
BufferedReader reader = null;
//写入:建立内存到文件流
BufferedWriter writer = null;
try {
//创建输入,输出流对象
FileReader fr = new FileReader("T9/src/com/newstemplate/news.template");
reader = new BufferedReader(fr);

String line;
//循环读取并追加字符
StringBuilder sbf = new StringBuilder();
while ((line = reader.readLine()) != null) {
sbf.append(line).append("\n");
}
for (News ns : n.select()) {
//替换内容
String newString = sbf.toString().replace("{Title}",ns.getTitle()).
replace("{Author}", ns.getAuthor()).replace("{Date}", ns.getDate().toString())
.replace("{Body}", ns.getBody());

FileWriter fw = new FileWriter("T9/html/" + ns.getTitle() + ".html");
writer = new BufferedWriter(fw);
writer.write(newString); //写入文件
writer.flush(); // 一定要刷新缓存
}
System.out.println("ok!");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close();
}
if (writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

第十章-项目-宠物商店