第一章 封装
一.知识点
1.封装的概念
2.构造方法
3.方法的重载
4.this和super
二.重点
1.掌握使用封装
2.方法的重载
三.内容
1.面向对象三大特征:封装、继承、多态;
1.1.封装的概念:实际就是信息的隐藏过程。
1.2提供私有的属性,来隐藏类的具体细节,提高安全性。
1.32.来提供一个公开的方法,让外部的类来访问。
2.构造方法的使用
//构造方法
//特征:1.一般使用public,也可以使用private 2.没有返回值类型; 3.和类名一致;
private的构造方法比较特殊,即该构造方法只能被本类访问,即单例模式的使用。
有一个无参构造方法
有4个参数的构造方法
有3个参数的构造方法
有2个参数的构造方法
这种情况叫:构造方法的重载
3.适用于方法的重载,特征:
1.方法名一致;
2.参数的个数不同
3.参数的类型不同;
4.参数的顺序不同
构成方法重载的三个条件,2 3 4;跟方法的返回类型无关!
方法的重载再做个例子:
参数的类型不同;
涉及到几个类:
Dog类 狗
Monkey类 猴子
Master类 主人
Test测试类
由以上案例代码引申出来继承父类的概念,提取(抽象)出来了一个父类:Animal。
package com.aaa.chapter01;
/**
* 动物类:父类;
* 子类:Dog类、Monkey类,Cat类
*/
public class Animal {
//受保护的级别;可以被子类来访问;
protected String name;
protected int age;
//能不能放一个父类的构造方法;
//整出来一个父类的带两个参数的构造方法;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Dog类:extends Animal
public class Dog extends Animal{
public Dog(String name, int age) {
super(name,age);
}
}
猴子类:
public class Monkey extends Animal{
public Monkey(String name, int age) {
//super:父级 上级;超级;
//Object:超类;
super(name,age);
}
}
super:这时候指代父级、父类。
使用继承的好处,降低代码量,提升代码的重用性。
总结:研究
中国四大发明:火药 造纸术 指南针 活字印刷术
印刷术--》活字印刷术 ,提升在哪里???理解了活字印刷术,你就理解了面向对象。
三国时候:诗人:曹操;短歌行
喝酒唱歌 人生真爽 --》雕版--》九牛二虎之力,印刷了100份; 别人不能用。
对酒唱歌 人生真爽 --》 --》雕版 2.0 印刷???100份;
...
对酒当歌 人生几何
活字印刷术:
唱 歌 酒 对 人 生 真 爽
对 几 何
其他人都可以用:面向对象的复用(重用性)
扩展性
4.总结:
4.1.封装的粒度
在实现类构件的封装之前,有必要对封装“粒度”进行一下简单的讨论。粒度过大,集成度过大,必然会增加交叉干扰的机会,它会对适应性范围产生负面影响,大粒度封装并不意味着封装的技术水平高,只能表示把握命题的能力有限。
相反,如果粒度划分的太小,虽然在适应性方面会得到改善,但在类控件过渡到实例化过程中,自然会在属性定义、方法引用、参数传递等方面带来巨大的工作量,会使出现错误的几率大大增加。
确定粒度大小要把效率、功能性、设计难度、应用效果等各方面的因素进行综合评价,然后才有可能得到相对较好的应用效果。兼顾应用过程与设计过程之间的合理契合点,应当就是相对合理的粒度划分。针对此问题的参考原则是:
1.尽量保证每个功能具备相对完整及独立的特性;
2.明确功能状态之间的顺序关系、层次关系,尽量减少功能之间的交叉干扰;
3.控件或功能之间的参数尽量少,而且意义明确;
4.彼此之间尽量不要存在代码级的练习;
5.控件内部的字段、属性尽量少,且意义直观、明确,注释清晰,为属性填写提供支持;
6.在数据层面上尽量保证各控件之间的状态能够延误或有效组合。
4.2.this和super的区别
this代表类对象本身;
super代表对父类对象的默认引用;
super语句必须出现在子类构造方法的第一行?为什么,因为子类构造的时候,先去调用父类的构造方法。
4.3final修饰的类不能被继承
四.作业
课后作业1 3 4
第二章 多态
知识点:
1.多态
2.抽象类
3.接口
内容:
第二章 多态
1.1概念
多态:多种形态;
$H_2O$
固态:冰;液态:水;气态:蒸汽;
我们现在针对九个龙进行赛跑:下令:跑;
龙飞上天;爬行;水中游;
公司:老板下令,开始干活儿;
行政人员、程序猿、财务人员、市场人员;干活,不一样;
文学角度:一千个人就有一千个哈姆雷特;
专业来说:针对同样的消息(指令)得到的效果不同;这也是多态;
多态总结:三个条件;
1.需要有继承关系,实现继承;
2.子类重写父类的方法;
3.父类引用子类的对象;
1.2 代码
//回头加
1.3 向上转型和向下转型
向上转型:小转大
向下转型:大转小
注意事项:能够成功,父类对象被赋值的时候,赋值的类一定和将来要向下转型的类一致。
Pet p=new Dog(); //父类的对象; 这个是错的 Cat cat=(Cat) p; //子类的对象;
1.4 好处
1.4.1 提高了代码的可扩展性
1.4.2 使用起来比较灵活
1.4.3 简化代码了,降低了代码的工作量,不用你复制粘贴了..
二.抽象类
抽象:形象;
抽象一定是不形象的,不好理解的。
抽象类是范围比较大的类,例如:动物类、宠物类、汽车类等等。
具体的类:Dog类、Cat类;
特征:不能被实例化;
//抽象的方法,就是没有方法体的方法定义,抽象方法,必须被子类重写;
抽象类可以有不抽象的方法;
有抽象方法的类,一定是抽象类;
三.接口
3.1 概念
官方文档:java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。 接口在实际中更多的作用是制定标准的。
3.2 为什么使用接口?
现实生活有很多规范、规则、定义、标准、电脑有很多插槽(USB)接口,符合宽、高电脑定义,如果是规则、规范也需要去遵守。
3.3接口的特征
使用关键字interface,public(公共的)
public interface PCI {
}
实现类和接口之间是实现的关系。到底啥时候用子类继承自父类,啥时候用实现类实现接口呢?
3.3接口和抽象类的区别
抽象类是一个类,里面的属性可以不赋值,接口的属性必须赋值;
抽象类可以有非抽象的方法,就是带方法体的方法;接口里面都是抽象的方法;
抽象类里面的抽象方法,可以是protected或public的,但是接口里面的都是public的。
3.4 接口的优势
接口的出现,是为了完成之前C++的多重继承,一个子类可以继承多个父类,但是在Java里面一个子类只能继承一个父类。
继承多个父类这种效果,我们就通过了接口来实现。
3.5接口的使用
3.5.1 子类继承自父类并且实现某一个接口;
3.5.2 子类继承自父类并且实现多个接口;
3.5.3 接口可以继承接口
四.面向接口编程
Java是面向接口的编程,也就是说,我们开发项目更多的用的是接口。
第三章 异常
知识点:
内容:
一.什么是异常
二.异常的分类
三.异常的优点
四.如何使用异常
五.自定义异常
第一节:什么是异常
异常:就是报错?现实世界异常:上班路上,正常开车,但是出车祸了,突然调沟了,导致延误上班;上班的时候,公司没电了;来电了,开始干活了,电脑坏了。
异常:不正常的情况的出现。
编程:
qxw //1.IPO:输入 输出 处理 写程序也是这样;
//2.定义变量;
int num1,num2,result;
//3.java的输入Scanner;
Scanner input=new Scanner(System.in);
System.out.println("请输入除数:");
num1=input.nextInt();
System.out.println("请输入被除数:");
num2=input.nextInt();
result=num1/num2;
System.out.println("商:"+result);
第二节:异常的分类
第三节:为什么要有异常,优点
JAVA:特征,健壮性,使程序更强大。
第四节:如何使用异常
try{
我们的代码;
} finally{ //不管是否有异常异常,都会执行...
结束
}
try{
我们的代码;
} catch(异常分类参数 对象){
catch可以加多个
}catch(异常分类参数2 对象2){
多个catch捕获异常;
}
finally{ //不管是否有异常,都会执行...
结束
}
第五节:自定义异常
public class PersonException extends Exception {
public PersonException(String msg){
super(msg);
}
}
第六节 Log4j
一个日志管理工具,记录日志。
需要用到一个配置文件:log4j.properties
### 把日志信息输出到控制台 ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %m%n
### 把日志信息输出到文件:Anbo.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=Anbo.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %m%n
### 设置优先级别、以及输出源 ###
log4j.rootLogger=debug, stdout, file
项目中的使用:
第五章 集合框架
知识点:
1.JAVA集合体系:
2.ArrayList和LinkedList 及其本质
3.Set
4.Map及其存储本质
5.泛型
6.Iterator迭代器
内容:
第一节:JAVA集合体系
第二节:ArrayList和LinkedList 及其本质
针对这种长度不确定的数组--》动态数组,List接口的实现类ArrayList。
数组是集合的特例,是固定长度的数组。
代码:参考实例包
ArrayList注意事项:
特征:存放数据可以重复,有序的;
声明特别长的数组,也可以操作刚才的抖音案例。
10000个长度,但是都不发视频,浪费空间1万个存储空间。
LinkedList:链表数组,也是动态,具体代码。插入 删除效率高。查找效率低。
第三节:Set集合
特征:无序、不重复的数据。
HashSet
Set案例补充:
本科论文要查重,网页发表之后要看重复率。
杰卡德算法思想
package review04;
import java.util.HashSet;
import java.util.Set;
/**
* Created by 张晨光 on 2020/7/20 9:03
* 论文查重
*/
public class TestSet {
public static void main(String[] args) {
Set<String> s1 = new HashSet<>();
Set<String> s2 = new HashSet<>();
s1.add("大数据");
s1.add("教育");
s1.add("J2EE");
s1.add("高并发");
s1.add("领域");
s1.add("的");
s1.add("使用");
s1.add("使用");
s1.add("使用"); //set集合的数据,不允许重复;
//s2数据
s2.add("大数据");
s2.add("教育");
s2.add("领域");
s2.add("的");
s2.add("使用");
s2.add("研究");
System.out.println(s1);
System.out.println(s2);
//再加一个结果集合;
Set<String>result=new HashSet<>();
//将s1集合加到result
result.addAll(s1);
result.retainAll(s2); //将s2中的数据存到result,只存在的数据
int jiao=result.size();//获取交集数据的长度;
System.out.println(jiao);
System.out.println(result);
//如何算并集;还是利用result
result.clear(); //先清空一下集合;
result.addAll(s1);
result.addAll(s2); //现在将s1 s2集合,都放在result集合里面,会不会有重复数据
int bing=result.size();
System.out.println(bing);
System.out.println(result);
System.out.println(100*5/8);
}}
第四节:Map接口
Map:地图;以键-值对的方式来存储数据,是J2EE里面用的非常多的一种数据类型。
HashMap TreeMap;
Map map=new HashMap(); //Ctrl+Shift+O(欧,不是零)
map.put("CN", "中国龙队"); //put:用来存值;add之前
map.put("UA","美国鹰队");
map.put("EN","英国鸟队");
map.put("JP","小日本药丸队");
map.put("KO","韩国队");
//输出
System.out.println(map);
键--值的方式,无序;
map.put("KO","大韩民国队"); //仍然键:KO,value后面的覆盖前面的。
map.keySet():这个方法,返回的是map里面所有键的集合.Set
针对Set就可以使用迭代器;
面试题:
问:Entry、HashMap、HashSet的存储原理
HashSet的构造方法里面放置的是HashMap的对象,本质是HashMap()。
延伸内容:
List a225=new ArrayList();
List a315=new ArrayList();
Map<Integer,List>map33=new HashMap<Integer,List>();
//1:对应的一系列的值;
//2:一系列的值;
map33.put(1,a225);
map33.put(2,a315);
//Map是一个最复杂的数据类型;
//面试的时候,问的最多;
//Map<Integer,String>map22=new HashMap<Integer,String>();
第五节:泛型
list
boolean |
**add**(Object e) 向列表的尾部添加指定的元素(Object所有类的祖先类)。 |
---|---|
void |
**add**(int index, Object element) 在列表的指定位置插入指定元素(可选操作)。 |
List list=new ArrayList();
list.add("蔡徐坤");
list.add("易烊千玺");
list.add("王源"); //字符串
list.add(250); //整型
list.add(true); //布尔型
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
//泛型:对宽泛数据的类型限制。
格式:
<数据类型>或<数据类型,数据类型>
泛型的注意事项:
<不能是基本数据类型> 4类8个;
<都是引用数据类型> :Object的限制.
int-->Integer,包装类型.举例:
第六节:迭代器
Set接口方法
Iterator<E> |
**iterator**() 返回在此 set 中的元素上进行迭代的迭代器。 |
---|---|
Iterator接口方法
boolean |
hasNext() 如果仍有元素可以迭代,则返回 true 。 |
---|---|
E |
**next**() 返回迭代的下一个元素。 |
void |
**remove**() 从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。 |
增强型for循环;
第六章 IO
知识点:
1.File类
2.字节流
3.字符流
4.序列化
整体概念:
IO:
input/output,都需要依赖于文件,所以我们先讲File类。
6.1流概念
IO:Input/Output 输入/输出流。
流:概念;
水流:这个里面流动的介质是水,从高的地方到低的地方了。
电流:流动的电子,从介质的一端到另外一端。
IO流:是在计算中使用,传输的是数据,数据的本质单位是0 1 代码。传输的的字节/字符,之前说过
字节:位的关系;1个字节=8位;八个二进制符号(0/1)。
流的分类:
大小分类:字节流和字符流
方向分类:输入流和输出流
二者也可以混起来:
字节输入流、字符输出流 字节输出流 字符输入流
高级别还有流的扩充:缓冲流、打印流。
再次强调,所谓的输入流/输出流,针对是程序。
推荐大家使用,XMind做一下整理笔记。
6.2:File类
对文件进行操作,必须我们要知道有这样一个File类。
File类的构造方法:有一个参数,字符串类型,路径。
实际上构造方法有4个。
方法或常量 | 类型 | 描述 |
---|---|---|
public File(String filename) | 构造方法 | 创建File类对象并传入完整路径 |
public boolean createNewFile() | 方法 | 创建新文件 |
public boolean delete() | 方法 | 删除文件 |
public boolean exists() | 方法 | 判断文件是否存在 |
public boolean isDirectory() | 方法 | 判断给定的路径是否为目录 |
public long length() | 方法 | 返回文件的大小 |
public String[] list() | 方法 | 列出指定目录的全部内容,只列出名称 |
public File[] listFiles() | 方法 | 列出指定目录的全部File对象 |
public boolean mkdir()/mkdirs() | 方法 | 创建目录/多级目录 |
public boolean renameTo(File dest) | 方法 | 为已有的文件重命名 |
代码:
//1.声明一个对象;
File file=new File("d:/S2/A231.txt");
//2.调用刚才学习的方法;
if(file.exists())
file.delete();
else
file.createNewFile();
为什么会报IOException的异常?
mkdir()创建目录
file.mkdir()--》d:/s2
然后再创建文件;
file.mkdirs()-->创建的多级目录;
面试题:递归遍历目录和文件
了解概念:递归;
一种通过重复将问题分解为同类的子问题而解决问题的方法。
递归:简而言之,一个是方法本身自己调用自己;不是死循环吗?
还要有一个退出的条件。
package com.aaa.chapter06;
import java.io.File;
import java.util.Scanner;
public class DirectoryDemo {
public static void main(String[] args) {
//调用一个静态遍历方法;遍历哪个地方???
// Scanner Scanner 可以自己来输入地址;
listFiles("d:\\");
}
/**
* 根据我们在main方法里面输入的路径地址,来找下面的文件和目录.
* 我们这个方法的作用就是遍历目录;
* @param path
*/
private static void listFiles(String path) {
//1.访问文件对象;dir 目录的意思;
File dir=new File(path);
//2.再访问任何文件之前或目录之前,都要判断一下是否存在;
if(!dir.exists())
return;
if(dir.isFile()) {
System.out.println("是文件,就不再遍历了...");
System.out.println(dir);
return;
}
//3.都是目录,我们要得到当前dir下面的所有文件
File []files=dir.listFiles();
//files:有哪些可能的情况???会得到什么样的结果
//所有文件和子目录 之前的考虑; 结果为null
// System.out.println(files);
if(files==null){
return;
}else{
for(File f:files){
//System.out.println(f); //大家一定要在这个地方进行测试!!!输出的都是目录
if(f.isFile()){
System.out.println("文件:"+f.getName()+"..."+f.length()+"字节");
}else{
//当f是目录的时候,我们是不是需要继续进行查找、根据我们的分裂的列表;
System.out.println("目录:\\"+f.getPath());
//顺理成章的就调用了自身的这个方法,传递的参数就是子目录的路径.
listFiles(f.getPath());
}
}
}
}
}
6.3:字节流
InputStream:输入字节流;传输的数据单位是字节(byte),1个字节=8个bit(位:0/1)
OutputStream:输出字节流
传输的是字节 byte:注意不是bit
方向来:
读InputStream
写:OutputStream
数据源:File 文件
6.3.1步骤:
1.步骤
创建读写对象;File类;
2.进行读/写
109-->1101101
64 +32 + 8+ 4 + 1 =109
如果已到达文件末尾,则返回 -1。
3.关闭
读的时候:代码 :
new String(b);没有指定长度;
new string(b,0,len)
当我们读取或者写入的内容比较多的时候,需要加缓冲流。
字节流读取的原理代码:
int num1=is.read(); //从输入流中读取数据的下一个字节
// System.out.println(num1);
// int num2=is.read();
// System.out.println(num2);
// int num3=is.read();
// System.out.println(num3);
// int num4=is.read();
// System.out.println(num4);
// int num5=is.read();
// System.out.println(num5);
// int num6=is.read();
// System.out.println(num6);
//当我们读不到的时候,返回-1;
int num; //整型数字,现在要得到字节.
while((num=is.read())!=-1){
char c=(char) num;
System.out.print(c);
}
6.4:字符流
三种方式实现字符流的读取操作,注意和书上的不同。
6.4.1 步骤:
1.创建文件;
2.读取/写入;
3.关闭;
package com.aaa.chapter06;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class ReaderDemoTest {
public static void main(String[] args) throws IOException {
//如果使用InputStreamReader,是不是需要再new出来一个InputStream呢?
Reader reader=new FileReader("d:\\a231\\zh.txt");
//2.读取
int num1=reader.read();//读单个字符;
System.out.println((char) num1);
int num2=reader.read();//读单个字符;
System.out.println((char) num2);
int num3=reader.read();//读单个字符;
System.out.println((char) num3);
int num4=reader.read();//读单个字符;
System.out.println((char) num4);
//3.关闭
reader.close();
}
}
这时候菲菲出来了?为什么i胡???
去掉i胡加代码:
charbuf[2]=' ';
charbuf[3]=' ';
package com.aaa.chapter06;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class ReaderDemoTest {
public static void main(String[] args) throws IOException {
//如果使用InputStreamReader,是不是需要再new出来一个InputStream呢?
Reader reader=new FileReader("d:\\a231\\zh.txt");
BufferedReader br=new BufferedReader(reader);
//相当于一个包装的过程
//2.读取
/* int num1=reader.read();//读单个字符;
System.out.println((char) num1);
int num2=reader.read();//读单个字符;
System.out.println((char) num2);
int num3=reader.read();//读单个字符;
System.out.println((char) num3);
int num4=reader.read();//读单个字符;
System.out.println((char) num4);*/
//2.定义了一个char类型的数组,char数组;java:里面char是双字节还是单字节???
//双字节; 汉字也是双字节; 读取的内容长度,取决于char数组的长度;
char[]charbuf=new char[4];
//int len=reader.read(charbuf);
// System.out.println(charbuf); //4:mei胡
// System.out.println(len);
// charbuf[2]=' ';
// charbuf[3]=' ';
/*System.out.println(reader.read(charbuf,1,3));
System.out.println(charbuf);
System.out.println(charbuf[0]);//off:是读取字符过来之后,存放的位置;
System.out.println(charbuf[1]);
System.out.println(charbuf[2]);
System.out.println(charbuf[3]);*/
//使用循环来读取字符
int len; //将reader读取到charbuf这个字符数组,返回读取的字符长度,字符数;
while ((len=reader.read(charbuf))!=-1){
//不要直接输出char字符;2最后一次的时候个数;
System.out.print(new String(charbuf,0,len));
}
//3.关闭
reader.close();
}
}
6.3.2 总结
这个是需要理解的小难点;
1.读取单个字符read();
2.读取到字符数组read(char[])
3.读取到字符数组read(char[],off,len)
第七章 JDBC
知识点:
1.概念
2.步骤
3.连接
4.命令
5.问题
7.1 概念扫盲
jdbc:Java DataBase Connectivity(JAVA 数据库连接技术),Oracle公司提供的一系列连接数据库的接口、标准
驱动:听歌,有声卡驱动;玩游戏,有显卡驱动;听歌程序、游戏程序与计算机硬件之间的桥梁。
JAVA程序---》数据库,就需要数据库驱动程序。
数据库驱动:如果是mysql--》mysql驱动;
Oracle--》oracle驱动;
具体,我们这里,提现为jar包形式。
1.概念
JDBC:。
C#:Ado.net 微软连接数据库的技术;ODBC
宿舍系统;
2.步骤:
2.1 确定你已经安装了mysql数据库,注意不是Navicat等这些客户端,服务要启动。
2.2.数据库驱动jar包
5.0 mysql-connector-java-5.1.40.jar
new mysql-connector-java-8.0.19.jar
myEclipse方式项目Build path-->引用外部jar。
Idea:Project Strucuture--》Dependencies,导入jar。
2.3 JDBC代码步骤:
2.3.1 加载驱动:根据数据库的不同,加载不同的驱动;
2.3.2 连接数据库:Connection
2.3.3执行sql语句:语句对象 Statement对象/PreparedStatement
2.3.4 有结果: 查询结果/增删改的结果;
2.3.5 关闭
2.2.3认识 JDBC API的作用;
- DriverManager:
- Connection
- Statement
- ResultSet
7.2 DriverManager:驱动器管理类
//类加载器的方式
// ClassLoader.getSystemClassLoader().loadClass("com.mysql.cj.jdbc.Driver");
// Driver driver=new Driver(); //注意这个Driver是mysql下的
// DriverManager.registerDriver(driver);
//1.加载数据库的驱动,mysql的驱动;使用最多的方式
Class.forName("com.mysql.cj.jdbc.Driver"); //会抛异常:可能会找不到该类.???
static Connection getConnection(String url)
试图建立到给定数据库 URL 的连接。
static Connection getConnection(String url, Properties info)
试图建立到给定数据库 URL 的连接。
static Connection getConnection(String url, String user, String password)
试图建立到给定数据库 URL 的连接。
mysql:com.mysql.cj.jdbc.Driver类源码:
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
不推荐的方式:也写过来:
Driver driver=new Driver(); //注意这个Driver是mysql下的
DriverManager.registerDriver(driver);
1.本身Driver类就有一个注册Driver对象,new的实例,会new两次。
2.com.mysql.cj.jdbc.Driver,new的时候,new了一下mysql下的Driver类,假设,我们现在要用Oracle数据库,你是不是需要修改代码。new出来的对象,存在高耦合的现象,使用Class.forName("驱动类名"),可以降低耦合,解耦的作用。
7.3 Connection接口:数据库连接会话接口
与特定数据库的连接(会话)。在连接上下文中执行 SQL 语句并返回结果。
作用:
7.3.1 用户获取执行sql语句的对象
- Statement语句对象:createStatement()
- PreparedStatement预编译对象 prepareStatement(String sql)
- CallableStatement 存储过程语句对象:prepareCall(String sql)
7.3.2用于管理事务
7.4 Statement接口:用于执行静态 SQL 语句并返回它所生成结果的对象
7.4.1常规操作
用于执行静态 SQL 语句并返回它所生成结果的对象。
- 查询 ResultSet excuteQuery(sql);
- 增删改 int executeUpdate(sql)
ResultSet |
**executeQuery**(String sql) 执行给定的 SQL 语句,该语句返回单个 ResultSet 对象。 |
---|---|
int |
**executeUpdate**(String sql) 执行给定 SQL 语句,该语句可能为 INSERT 、UPDATE 或 DELETE 语句,或者不返回任何内容的 SQL 语句(如 SQL DDL 语句)。 |
(1) 对于 SQL 数据操作语言 (DML) 语句,返回行计数 (2) 对于什么都不返回的 SQL 语句,返回 0
2.代码:
try {
//1.引用类;com.mysql.jdbc下的Driver类;
//连接sqlserver/oracle用的jar就变了.
Class.forName("com.mysql.jdbc.Driver");
//localhost:本机;
String url="jdbc:mysql://DESKTOP-U8QOICP:3306/dormdb";
//String url="jdbc:mysql://DESKTOP-U8QOICP:3306/dormdb";
Connection conn=DriverManager.getConnection(url, "root", "root");
if(conn!=null)
System.out.println("success");
else
System.out.println("fail");
//出来地址的时候表示连接成功了
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
换了:
jdbc:mysql://DESKTOP-U8QOICP:3306/dormdb
换:针对贫困的同学,扶贫用针对本机.
String url="jdbc:mysql:///dormdb";
IP
String url="jdbc:mysql://127.0.0.1:3306/dormdb";
增删改代码:
7.4.2第二次的代码:增加,作业:删除 、修改、
package chapter07;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo03 {
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver"); //拿到Driver类;
String url="jdbc:mysql://127.0.0.1:3306/dormdb";
String user="root";
String password="root";
Connection conn=DriverManager.getConnection(url, user, password); //获得连接对象;
//通话;
Statement st=conn.createStatement();
String num="2244";
String name="张小敬";
String birth="";
//使用到了哪个类?
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
birth=sdf.format(new Date());//格式化当前的日期;
//使用+连接了 字符串变量;
String sql="insert into student(stuNum,stuName,birthday) values('"+num+"','"+name+"','"+birth+"')";
//大家下去之后,去做delete;语法:delete from 表名 where 条件
//更新:update 表名 set 列名1=值1,[列名2=值2,...n] where 条件;
int result=st.executeUpdate(sql); //这个方法可以执行增删改语句;
if(result>0)
System.out.println("success");
else
System.out.println("fail");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
7.4.3 SQL注入
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
还是查询,我们根据某些sql代码,增加自己的sql语句。
package com.aaa.chapter08;
import java.sql.*;
/**
* Created by 张晨光 on 2020/3/11 15:39
* SQL注入
*/
public class JDBCSqlSecurity {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver"); //1.加载驱动;
//2.连接数据库;
String url="jdbc:mysql://localhost:3306/School?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false";
String user="root"; //用户名;
String password="root"; //密码
Connection conn = DriverManager.getConnection(url, user, password);
//3.执行sql语句对象;
Statement st = conn.createStatement();
//模拟做一个登陆.大家也可以用Scanner
String name="aaa' OR '1'='1"; //输入 aaa ' OR '1'='1'
String pwd="aaa' OR '1'='1"; //
// String sql="select * from Student where name='值' and pwd='值'";
String sql="select * from Student where name='"+name+"' and pwd='"+pwd+"'";
//4.执行查询;返回的是一个结果集对象;next():true表示有数据;
ResultSet rs = st.executeQuery(sql);
if(rs.next()){
System.out.println("登陆成功222.。。。");
System.out.println(rs.getString(1)+"---"+rs.getString(2));
}
//5.关闭
rs.close();
st.close();
conn.close();
}
}
原因在于,我们使用的是sql的拼接字符串
拼接字符串一个是不好写,第二个不安全。
# select * from student
# 如果这时候有数据,则进到系统里面;
# select * from Student where name='aaa ' OR '1'='1' and pwd='888'
# false Or and false
select * from Student where name='aaa' OR '1'='1' and pwd='aaa' OR '1'='1'
#最后这一句,就相当于,select * from Student,我们通过加入2个sql代码段,攻克了网站。
7.5 接口 PreparedStatement 预编译的 SQL 语句的对象
public interface **PreparedStatement**extends Statement
子接口
青出于蓝而胜于蓝
常用方法:
返回类型 | 方法 | 含义 |
---|---|---|
ResultSet | executeQuery() | 在此 PreparedStatement 对象中执行 SQL 查询,并返回该查询生成的 ResultSet 对象 |
int | executeUpdate() | 在此 PreparedStatement 对象中执行 DML SQL(增删改) 语句,返回受影响的行数,对于无返回内容的 SQL 语句,比如 DDL 语句,返回0。DDL:数据定义语句,例如表的创建、表结构修改,表删除(Create Alter Drop)等;DML:就是增删改查;(Insert Update Delete Select) |
void | setXXX() | 设置输入参数的方法 |
PreparedStatement pstmt = conn.prepareStatement("UPDATE EMPLOYEES
SET SALARY = ? WHERE ID = ?");
//?问号就是占位符,第一个问号,第2个问号,这里面目前有2个问号。
pstmt.setBigDecimal(1, 153833.00) //给的哥问号设置值
pstmt.setInt(2, 110592) //setString() setDouble() setFloat() 给第二个问号设置值
int result=pstmt.executeUpdate();
防止sql注入的全部代码:
package com.aaa.chapter08;
import java.sql.*;
/**
* Created by 张晨光 on 2020/3/12 15:04
* 预编译语句对象
*/
public class JDBC83 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.加载驱动;
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获得连接对象
String url="jdbc:mysql://localhost:3306/School?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false";
String user="root";
String password="root";
Connection conn = DriverManager.getConnection(url, user, password);
//3.今天继续做查询;再使用预编译语句对象,不使用拼接的方式,改成占位符的方式
String name="aaa' OR '1'='1"; //输入 aaa ' OR '1'='1'
String pwd="aaa' OR '1'='1"; //
String sql="select * from Student where name=? and pwd =?";
//通过conn连接对象,得到执行sql语句的预编译语句对象;
//防止了sql注入,系统类自动对'进行了过滤操作;下面是过滤之后的sql语句
// select * from Student where name='aaa'' OR ''1''=''1' and pwd ='aaa'' OR ''1''=''1'
PreparedStatement pstmt = conn.prepareStatement(sql);
//给占位符赋值;set列类型方法(问号位置,值);大家试验一下
pstmt.setString(1,name);
pstmt.setString(2,pwd);
ResultSet rs=pstmt.executeQuery();
//4.遍历结果集对象;
while(rs.next()){
System.out.println(rs.getInt(1)+","+rs.getString("name")+rs.getString("birth"));
}
//5.释放资源;
rs.close();
pstmt.close();
conn.close();
}
}
预编译语句对象的修改代码:
package com.aaa.chapter08;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Date;
/**
* Created by 张晨光 on 2020/3/12 15:44
*/
public class JDBC84 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.加载驱动;
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获得连接对象
String url="jdbc:mysql://localhost:3306/School?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false";
String user="root";
String password="root";
Connection conn = DriverManager.getConnection(url, user, password);
//3.获得预编译语句对象,这个案例是增删改,这时候有3个问号
String sql="update student set age=?,pwd=?,birth=? where id=?";
PreparedStatement pstmt = conn.prepareStatement(sql);
//3.2给占位符赋值;这个知识点要求大家熟练掌握,C#String.format({0},{1})
pstmt.setInt(1,111);
pstmt.setString(2,"cccc");
//1.过期的方法;year-1900,month:0-11,day:13
//pstmt.setDate(3,new java.sql.Date(2020-1900,2,13)); //设置日期格式
//2.方法;
System.out.println(System.currentTimeMillis()); //当前时间的毫秒数;
// pstmt.setDate(3,new java.sql.Date(System.currentTimeMillis()));系统时间;
//pstmt.setDate(3,new java.sql.Date(System.currentTimeMillis()));
Date date=new Date(); //这个Date是java.util.Date;
pstmt.setDate(3,new java.sql.Date(date.getTime()));
pstmt.setInt(4,2);
//3.3执行一下
int result=pstmt.executeUpdate();
System.out.println(result);
//4.关闭资源;
pstmt.close();
conn.close();
}
}
昨天的问题,只有日期,没有时分秒,原因,new java.sql.Date(year,month,day),只有年、月、日,没有时分秒。
我们这时候可以使用new java.sql.Timestamp,可以精确到时分秒 纳秒级别。
为什么使用setObject(),我们之前有setInt()、setString()、setBoolean()等等,Object类型包括了其他所有的类型,setObject()可以代替其他任意的类型,这样也就引发了,我们下一个知识点,通用的类。
7.6接口 ResultSet:结果集
表示数据库结果集的数据表,通常通过执行查询数据库的语句生成。
ResultSet rs = stmt.executeQuery("SELECT a, b FROM TABLE2");
返回: 如果新的当前行有效,则返回 true;如果不存在下一行,则返回 false
数据集图示理解: 取值:
XXX:类型;String float int Object
getXXX(int columnIndex)
throws SQLException
查询参考代码:
package chapter07;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Demo04 {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement pst=null;
ResultSet rs=null; //结果集对象
String url="jdbc:mysql://127.0.0.1:3306/dormdb";
String user="root";
String password="root";
String sql="select stuNum,stuName,stuPass from student where stuName like ? ";
try {
Class.forName("com.mysql.jdbc.Driver");
conn=DriverManager.getConnection(url,user,password);
pst=conn.prepareStatement(sql);
pst.setString(1,"李%"); //这时候,是不是在李外面加了'';在这里加%
rs=pst.executeQuery();//查询用到都是这个方法;
//需要遍历; 有数据,则遍历;
while(rs.next()){
// System.out.println(rs.getString(1)+","+rs.getString(2)+","+rs.getString(3));
System.out.println(rs.getString("stuNum")+","+rs.getString(55)+","+rs.getString("stuPass"));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
System.out.println(e.getMessage());
}finally{//释放资源; try catch:健壮性;健康;强状;程序这个地方,有问题,会提示,异常处理。三重: 重写 重启 重装
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
pst.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
7.6.1安全性的验证案例:
先用Statement语句对象来实现。
Scanner input =new Scanner(System.in);
System.out.println("请输入教师姓名 :");
name=input.next();
next():这个方法,他碰到空格的时候,取一个值;
System.out.println("请输入教师姓名 :");
name=input.nextLine();
证明预编译语句对象很安全。
package chapter09;
import java.sql.*;
import java.util.Scanner;
public class Demo01 {
public static void main(String[] args) {
//1.Driver类;
//2.Connection连接类:红楼梦:贾链 --》 贾 链 语 执 事
Connection conn=null;
Statement st=null;
PreparedStatement pst=null; //预编译语句对象.
ResultSet rs=null;
try {
conn=BaseDao.getConnection();
//st=conn.createStatement(); //语句对象有了
//安全验证,sql注入;
String name;
String pwd;
Scanner input =new Scanner(System.in);
System.out.println("请输入教师姓名 :");
name=input.nextLine();
System.out.println("请输入密码:");
pwd=input.nextLine();
//
// String sql="select * from teacher where teachName='"+name+"' and teachPass='"+pwd+"'";
String sql="select * from teacher where teachName=? and teachpass=?";
pst=conn.prepareStatement(sql);
pst.setString(1,name);
pst.setString(2,pwd);
rs=pst.executeQuery(); //这个执行的时候,里面没有sql参数.
//System.out.println(sql);
//rs=st.executeQuery(sql); //查询的时候,需要结果;循环只有一种方式.
while(rs.next()){ //next:表示有数据,返回true,则可以继续执行。
System.out.println(rs.getString(1)+","+rs.getString(2));
}
} catch (SQLException e) { //大的法规:骑车撞人了.
e.printStackTrace();
}finally{ //不关机,不释放资源,没事儿,但是费钱;浪费资源.
BaseDao.closeAll(rs, pst, conn);
}
}
}
7.7 BaseDao 公共类
BaseDao作用,封装的CRUD里面,重复性的代码,降低我们写代码的工作量。
封装类里面提取各个类,重复性的代码,提取程属性和方法。
这个也是OOP的目标之一:可重用。
package com.aaa.chapter08;
import java.sql.*;
/**
* Created by 张晨光 on 2020/3/13 15:24
* 封装公共的类,包括了公共的查询连接、公共的增删改、关闭
*/
public class BaseDao {
//属性:连接对象、预编译语句对象
//因为后面的方法都需要用到这些对象,所以放到外面;
static Connection conn=null;
static PreparedStatement pstmt=null;
//外面:数据库连接的一些变量;好好理解一下松耦合,都变成字符串之后,好处。
static String driver="com.mysql.cj.jdbc.Driver";
static String url="jdbc:mysql://localhost:3306/School?useTimezone=true&serverTimezone=GMT&useUnicode=true&characterEncoding=utf8&useSSL=false";
static String user="root";
static String password="root";
//1.连接获得公共的连接静态方法
public static Connection getConnection(){
try {
Class.forName(driver);//这里也改下,松耦合
//使用之前定义好的conn来接受一下连接对象,其实就是给连接对象赋值
conn = DriverManager.getConnection(url, user, password);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
//2.公共的增删改;假设sql就是昨天的sql->update student set age=?,pwd=?,birth=? where id=?
//目前新的sql语句是这样的:delete from student where id=? 这时候?是一个
//所以,这时候引出来一个问题,就是动态的赋值,根据?的个数,
//又增加了一个参数:Object[]params
//给这个共同方法传值,我们知道有几个?,知道传几个值.
public static int executeUpdate(String sql,Object[]params){
conn=getConnection();//获得连接
int result=-1; //-1 是我们自己设定的.
try {
pstmt=conn.prepareStatement(sql);
//下面需要给?号赋值;
// pstmt.setInt(1,值1);
// pstmt.setString(2,值2 );
// pstmt.setString(3,值3);
// pstmt.setInt(4,值4);
for(int i=0;i<params.length;i++){
pstmt.setObject(i+1,params[i]);
}
result=pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally {
closeAll(pstmt,conn);
}
return result;//把结果返回一下;
}
//3.公共的关闭事件;先关最后出来的对象,后关最开始创建的对象;
//Conn-->预编译语句对象-->结果集对象
public static void closeAll(PreparedStatement pstmt,Connection conn){
//3.1 关闭预编译语句对象;
if(pstmt!=null) {
try {
pstmt.close();
pstmt=null; //让pstmt没有引用,这样直接可以垃圾回收...
} catch (SQLException e) {
e.printStackTrace();
}
}
//3.2关闭连接对象;
if(conn!=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
测试类:
package chapter09;
import java.sql.*;
public class TestBaseDao {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement pst=null;
//针对user表(项目)进行增加数据;
String sql="insert into user(uName,uPass,state,flag) values(?,?,?,?)";
Object[] params={"kangaoxiang","123456",1,0};
int result=BaseDao.update(sql, params);
if(result>0)
System.out.println("插入数据成功");
else
System.out.println("插入数据失败");
BaseDao.closeAll(null, pst, conn);
}
}
总结:
- jdbc连接数据库的步骤
- jdbc需要哪些接口和类
- jdbc的常用操作
- BaseDao的效率提升,通用的增删改
第八章 元数据和BaseDao查询
8.1 知识点
1.元数据
2.通用查询
3.项目把查询加进去
4.ORM思想
8.2 元数据
元:元气、元神。初始状态。
计算机里面:所有的数据,都有特征码,都有一些信息,会保存下来。我们可以通过这些信息,来得到具体的值。
数据库的操作也有很多元数据信息。
8.2.1 **DatabaseMetaData ** 关于数据库的整体综合信息
DatabaseMetaData是由Connection对象通过getMetaData方法获取而来,主要封装了是对数据库本身的一些整体综合信息,例如数据库的产品名称,数据库的版本号,数据库的URL,是否支持事务等等,能获取的信息比较多,具体可以参考DatabaseMetaData的API文档。 以下有一些关于DatabaseMetaData的常用方法: ·getDatabaseProductName:获取数据库的产品名称 ·getDatabaseProductVersion:获取数据库的版本号 ·getUserName:获取数据库的用户名 ·getURL:获取数据库连接的URL ·getDriverName:获取数据库的驱动名称 ·driverVersion:获取数据库的驱动版本号 ·isReadOnly:查看数据库是否只允许读操作 ·supportsTransactions:查看数据库是否支持事务
针对预编译语句对象:
ParameterMetaData ParameterMetaData getParameterMetaData()
获取此 PreparedStatement 对象的参数的编号、类型和属性。
Parameter:参数;ParameterMetaData :参数的元数据。
package com.aaa.chapter09;
import java.sql.*;
/**
* Created by 张晨光 on 2020/3/16 14:41
*/
public class DataBaseMetaDataTest {
public static void main(String[] args) throws SQLException {
Connection conn=BaseDao.getConnection();
String sql="select id 编号,name 姓名,age 年龄,pwd from student where name=? and pwd=?"; //?两个?
PreparedStatement pstmt=conn.prepareStatement(sql); //得到一个预编译语句对象;
/*
1.
DatabaseMetaData md = conn.getMetaData(); //获得数据库元数据的方法;
if(conn!=null) {
//查询一下数据库元数据的一些方法;
System.out.println(md.getDatabaseProductName()); //MySQL
System.out.println(md.getDatabaseProductVersion()); //8.0.11
System.out.println(md.getDatabaseMajorVersion()); //主要版本号:8
System.out.println(md.getDatabaseMinorVersion()); //次要版本号:0
System.out.println(md.getDriverName());
System.out.println(md.getDriverVersion());
System.out.println(md.getURL());
System.out.println(md.getUserName());
System.out.println(md.supportsTransactions());
//针对数据库进行连接,然后通过getMetaData()方法得到数据库的信息;
}*/
//2.得到预编译语句对象的元数据;
ParameterMetaData parameterMetaData = pstmt.getParameterMetaData();
// System.out.println(parameterMetaData.getParameterCount()); //么有执行显示:2
// Object[]params={"张鹏","zzz"}; //定义了一个Object类型的数组;
//BaseDao可以增加一个设置的方法;
//BaseDao.setParams(params);
//设置?的值;
pstmt.setString(1,"张鹏");
pstmt.setString(2,"zzz");
ResultSet rs = pstmt.executeQuery();
//? System.out.println(parameterMetaData.getParameterType(1));
// System.out.println(rs.getMetaData());
//3.结果集元数据
ResultSetMetaData rsmd = rs.getMetaData();
System.out.println(rsmd.getColumnCount()); //列数
//下面的方法都需要用到列?通过循环来操作得到具体的值;因为取值的时候,从第一列开始;
for(int i=1;i<=rsmd.getColumnCount();i++){
System.out.println(rsmd.getColumnName(i)+"\t\t"+rsmd.getColumnTypeName(i)+"\t\t"+rsmd.getColumnClassName(i)+"\t\t\t"+rsmd.getColumnLabel(i));//列名;
}
}
}
8.2.2 ResultSetMetaData 获取关于 ResultSet
对象中列的类型和属性信息的对象
ResultSetMetaData是由ResultSet对象通过getMetaData方法获取而来,主要是针对由数据库执行的SQL脚本命令获取的结果集对象ResultSet中提供的一些信息,比如结果集中的列数、指定列的名称、指定列的SQL类型等等,可以说这个是对于框架来说非常重要的一个对象。关于该结果集元数据对象的其他具体功能和方法请查阅有关ResultSetMetaData的API文档。
以下有一些关于ResultSetMetaData的常用方法: ·getColumnCount:获取结果集中列项目的个数 ·getColumnType(列编号):获取指定列的SQL类型对应于Java中Types类的字段 ·getColumnTypeName(列编号):获取指定列的SQL类型 ·getColumnClassName(列编号):获取指定列SQL类型对应于Java中的类型(包名加类名)
mysql在用两个方法获取sql语句名称时显然getColumnName不符合使用者的要求,取到的不是别名。但是oracle对于两种方法取到的值是一样的。因此一般情况下还是建议使用getColumnLabel方法 通用查询的思路理顺:
-
多少列:结果集元数据对象得到。确定了行里面放的数据。
-
使用什么样的数据结构List,List可以动态添加,适用不确定数据的情况。
-
列不确定-》rowList,行不确定--》tableList
-
如何知道得到查询结果集有多少行呢?动态的
8.3 查询
package com.aaa.chapter09;
import sun.misc.BASE64Decoder;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by 张晨光 on 2020/3/13 15:24
* 封装公共的类,包括了公共的查询连接、公共的增删改、关闭
*/
public class BaseDao {
//属性:连接对象、预编译语句对象
//因为后面的方法都需要用到这些对象,所以放到外面;
static Connection conn=null;
static PreparedStatement pstmt=null;
static ResultSet rs=null;
//外面:数据库连接的一些变量;好好理解一下松耦合,都变成字符串之后,好处。
static String driver="com.mysql.cj.jdbc.Driver";
static String url="jdbc:mysql://localhost:3306/School?useTimezone=true&serverTimezone=CTT&useUnicode=true&characterEncoding=utf8&useSSL=false";
static String user="root";
static String password="root";
//1.连接获得公共的连接静态方法
public static Connection getConnection(){
try {
Class.forName(driver);//这里也改下,松耦合
//使用之前定义好的conn来接受一下连接对象,其实就是给连接对象赋值
conn = DriverManager.getConnection(url, user, password);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
//2.公共的增删改;假设sql就是昨天的sql->update student set age=?,pwd=?,birth=? where id=?
//目前新的sql语句是这样的:delete from student where id=? 这时候?是一个
//所以,这时候引出来一个问题,就是动态的赋值,根据?的个数,
//又增加了一个参数:Object[]params
//给这个共同方法传值,我们知道有几个?,知道传几个值.Object...params也是数组的形式
//表示params这里面的值,可以0到n个。
public static int executeUpdate(String sql,Object...params){
conn=getConnection();//获得连接.
int result=-1; //-1 是我们自己设定的.
try {
pstmt=conn.prepareStatement(sql);
//下面需要给?号赋值;
// pstmt.setInt(1,值1);
// pstmt.setString(2,值2 );
// pstmt.setString(3,值3);
// pstmt.setInt(4,值4);
for(int i=0;i<params.length;i++){
pstmt.setObject(i+1,params[i]);
}
result=pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally {
closeAll(null,pstmt,conn);
}
return result;//把结果返回一下;
}
//3.增加一个设置的方法;将params这里面的数据设置到预编译语句对象的?
public static void setParams(Object[]params) throws SQLException {
if(params!=null) {
for (int i = 0; i < params.length; i++) {
pstmt.setObject(i + 1, params[i]);
}
}
}
//4.公共的查询方法;List+List
public static List findList(String sql,Object[]params){
List tableList=null;//我这里默认为null.
conn=getConnection();
try {
pstmt=conn.prepareStatement(sql); //得到查询sql的预编译语句对象;
setParams(params); //调用刚才定义的设置参数的方法;
ResultSet rs = pstmt.executeQuery(); //通过执行预编译语句对象得到结果集对象;
//再次通过rs得到结果集元数据对象;
ResultSetMetaData rsmd = rs.getMetaData();
int colCount=rsmd.getColumnCount(); //4列 假设一下;
//输出一下列名;
// for(int i=1;i<=colCount;i++){
// System.out.print(rsmd.getColumnLabel(i)+"\t\t\t");
// }
// System.out.println(); //换行 ;
tableList=new ArrayList(); //直接new一下;
while(rs.next()){
//为了适应不同的sql语句,查询的列数不同,所以说这里的列数不能是死的。
List rowList=new ArrayList();//这个rowList用来存储行数据;
for(int i=1;i<=colCount;i++){
//System.out.print(rs.getString(i)+"\t\t\t");
rowList.add(rs.getObject(i));
}//这一行数据添加完了
//System.out.println();
tableList.add(rowList);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
BaseDao.closeAll(rs,pstmt,conn);
}
return tableList;//如果我们查不到数据,则返回null
}
//4.2 公共的查询方法;List+HashMap<String,Object>
public static List<HashMap<String,Object>> findList2(String sql,Object[]params){
List<HashMap<String,Object>> tableList=null;//我这里默认为null.
conn=getConnection();
try {
pstmt=conn.prepareStatement(sql); //得到查询sql的预编译语句对象;
setParams(params); //调用刚才定义的设置参数的方法;
ResultSet rs = pstmt.executeQuery(); //通过执行预编译语句对象得到结果集对象;
//再次通过rs得到结果集元数据对象;
ResultSetMetaData rsmd = rs.getMetaData();
int colCount=rsmd.getColumnCount(); //4列 假设一下;
tableList=new ArrayList<HashMap<String, Object>>(); //直接new一下;
while(rs.next()){
//为了适应不同的sql语句,查询的列数不同,所以说这里的列数不能是死的。
//HashMap使用的是键值对的方式.
HashMap<String,Object>rowMap=new HashMap<>();
for(int i=1;i<=colCount;i++){
rowMap.put(rsmd.getColumnLabel(i),rs.getObject(i));
}//这一行数据添加完了
tableList.add(rowMap);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
BaseDao.closeAll(rs,pstmt,conn);
}
return tableList;//如果我们查不到数据,则返回null
}
//5.公共的关闭事件;先关最后出来的对象,后关最开始创建的对象;
//Conn-->预编译语句对象-->结果集对象
public static void closeAll(ResultSet rs,PreparedStatement pstmt,Connection conn){
try {
if (rs != null)
rs.close();
}catch (SQLException e){
e.printStackTrace();
}
//3.1 关闭预编译语句对象;
try {
if(pstmt!=null)
pstmt.close();
//pstmt=null; //让pstmt没有引用,这样直接可以垃圾回收...
} catch (SQLException e) {
e.printStackTrace();
}
//3.2关闭连接对象;
try {
if(conn!=null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
测试类:
package com.aaa.chapter09;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by 张晨光 on 2020/3/13 16:03
*/
public class BaseDaoTest {
public static void main(String[] args) {
// String sql="delete from student where age>? and address=?";//第一个参数
//// Object[]params={23,"日本东京"};
// String sql="insert student(name,gender,age)values(?,?,?)";
String sql="select id 编号,name 姓名,age from student where name=? or age>?";
Object[]params={"张鹏",23};
// String sql="select * from grade";
// Object[]params=null;
//List tableList=BaseDao.findList(sql,params);
//System.out.println(tableList.size()); 3行数据;
// for(int i=0;i<tableList.size();i++){
// List rowList= (List) tableList.get(i); //拿到第i行的数据;
// //输出每一行的数据;
// for(int j=0;j<rowList.size();j++){
// System.out.print(rowList.get(j)+"\t\t\t");
// }
// System.out.println();
// }
//调用List+HashMap的方式;
List<HashMap<String,Object>>tableList=BaseDao.findList2(sql,params);
for(int i=0;i<tableList.size();i++){ //这个和之前一样,因为外层都是List
HashMap<String,Object>rowMap=tableList.get(i);
//HashMap:EntrySet
for(Map.Entry<String,Object> entry:rowMap.entrySet()){
System.out.print(entry.getValue()+"\t\t\t");
}
System.out.println();
}
}
}
8.4 Properties类
Properties类来读取配置文件:
方法名 | 说 明 |
---|---|
String getProperty(String key) | 用指定的键在此属性列表中搜索属性。通过参数key得到其所对应的值 |
Object setProperty(String key,String value) | 调用Hashtable的方法put。通过调用基类的put()方法来设置键-值对 |
void load(InputStream inStream) | 从输入流中读取属性列表 (键和元素对)。通过对指定文件进行装载获取该文件中所有键-值对 |
void clear() | 清除所装载的键-值对,该方法由基类Hashtable提供 |
配置文件,扩展名都是以.properties结束。
数据库:db.properties
name::key: 钥匙:key
value:值
这种配置文件就是以键=值进行存储数据;
以井号('#'表示注释)
参考代码:
Properties params=new Properties();
String configFile = "database.properties";
InputStream is=BaseDao.class.getClassLoader().getResourceAsStream(configFile);
try {
params.load(is);
} catch (IOException e) {//….}
driver=params.getProperty("driver");
url=params.getProperty("url");
user=params.getProperty("user");
password=params.getProperty("password");
} //省略其他方法代码……}
第九章 反射
9.1 概念
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;public、protected、private。
OO(面向对象),private私有的,不能访问。这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。**
反射就是把java类中的各种成分映射成一个个的Java对象 例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把各个组成部分映射成一个个对象。
物理:有个反射的概念,通过镜子,可以知道物体的存在。看到一个镜像或名字等,知道物体在哪里。
(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述) 如图是类的正常加载过程:反射的原理在与class对象。 熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。
Student.java--->Student.class 经过编译成了一个字节码文件。
9.2 作用
在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法。当然,也不是所有的都适合反射,之前就遇到一个案例,通过反射得到的结果与预期不符。阅读源码发现,经过层层调用后在最终返回结果的地方对应用的权限进行了校验,对于没有权限的应用返回值是没有意义的缺省值,否则返回实际值起到保护用户的隐私目的。
反射是框架设计的灵魂
(使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码))
9.2.1 反编译:.class-->.java
9.2.2通过反射机制访问java对象的属性,方法,构造方法等;
User user=new User();--》形成的java文件-->XXX.class
将来赋值的时候,不是User类,是不是就报错了啊。存在紧耦合的状态,我们做OO的目的就是高内聚、松耦合,说白了,就是模块内部实现特定功能,模块与模块之间,关联度不大。
这种方式,是编译时
我们以后写程序,更多的应该是运行时给值。
9.3 反射机制的相关类
与Java反射相关的类如下:
类名 | 用途 |
---|---|
Class类 | 代表类的实体,在运行的Java应用程序中表示类和接口 |
Field类 | 代表类的成员变量(成员变量也称为类的属性) |
Method类 | 代表类的方法 |
Constructor类 | 代表类的构造方法 |
9.3.1 查看Class类在java中的api
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型) Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。
没有公共的构造方法,方法共有64个太多了。下面用到哪个就详解哪个吧
9.3.2 根据一个字符串得到一个类名
类名 | 方法 | 含义 |
---|---|---|
String | getClass | 表示此对象运行时类的 Class 对象 |
Class | forName | 具有指定名的类的 Class 对象 |
包装类 | 无 | 属性Type |
参考代码:
String str="今天是反射课程";
Class clz=str.getClass();//得到当前正在运行的类名;
System.out.println(clz);
Class clz2=Integer.TYPE; //包装类型,不同;包装类.Type
System.out.println(clz2);
System.out.println(Boolean.TYPE);
System.out.println(Double.TYPE);
System.out.println(Character.TYPE);
9.3.3 获取Class对象的 三种方式
获取Class对象的方式
-
Class.forName("全限定类名"):将字节码文件加载到内存里面,返回Class对象。
用于配置文件进行读取,这时候可以通过配置文件,来读取字符串(全限定类名),加载类。比如框架;
-
类名.class:通过类名的属性class获取
多用于参数的传递,比如后面要讲的通用查询;
-
对象.getClass():getClass()方法获取,这个方法在Object类中进行了定义。
多用于对象的获取字节码的方式;
package com.aaa.chapter07; /** * Created by 张晨光 on 2020/5/19 11:39 */ public class TestStudentClass { public static void main(String[] args) throws ClassNotFoundException { //1.创建一个Student类;全限定类名:包名+类名 Class clz1=Class.forName("com.aaa.chapter07.Student"); System.out.println(clz1); //2.类名.class Class clz2=Student.class; System.out.println(clz2); //3.对象.getClass Student student=new Student(); Class clz3=student.getClass(); System.out.println(clz3); //比较一下这三个对象; System.out.println(clz1==clz2); System.out.println(clz1==clz3); } }