第一章 封装

一.知识点

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$

固态:冰;液态:水;气态:蒸汽;

龙生九子.jpg

我们现在针对九个龙进行赛跑:下令:跑;

龙飞上天;爬行;水中游;

公司:老板下令,开始干活儿;

行政人员、程序猿、财务人员、市场人员;干活,不一样;

文学角度:一千个人就有一千个哈姆雷特;

专业来说:针对同样的消息(指令)得到的效果不同;这也是多态;

多态总结:三个条件;

1.需要有继承关系,实现继承;

2.子类重写父类的方法;

3.父类引用子类的对象;

1.2 代码

//回头加

1.3 向上转型和向下转型

向上转型.jpg

​ 向上转型:小转大

向下转型:大转小

注意事项:能够成功,父类对象被赋值的时候,赋值的类一定和将来要向下转型的类一致。

Pet p=new Dog(); //父类的对象; 这个是错的 Cat cat=(Cat) p; //子类的对象;

1567217268462.png

1.4 好处

​ 1.4.1 提高了代码的可扩展性

​ 1.4.2 使用起来比较灵活

​ 1.4.3 简化代码了,降低了代码的工作量,不用你复制粘贴了..

二.抽象类

​ 抽象:形象;

抽象一定是不形象的,不好理解的。

抽象类是范围比较大的类,例如:动物类、宠物类、汽车类等等。

具体的类:Dog类、Cat类;

特征:不能被实例化;

//抽象的方法,就是没有方法体的方法定义,抽象方法,必须被子类重写;

抽象类可以有不抽象的方法;

有抽象方法的类,一定是抽象类;

三.接口

3.1 概念

​ 官方文档:java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。 接口在实际中更多的作用是制定标准的。

3.2 为什么使用接口?

​ 现实生活有很多规范、规则、定义、标准、电脑有很多插槽(USB)接口,符合宽、高电脑定义,如果是规则、规范也需要去遵守。

主板示意图.jpg

主板.jpg

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

第二节:异常的分类

异常的分类.jpg

第三节:为什么要有异常,优点

JAVA:特征,健壮性,使程序更强大。

第四节:如何使用异常

异常步骤.jpg

try{

​ 我们的代码;

} finally{ //不管是否有异常异常,都会执行...

​ 结束

}

try{

​ 我们的代码;

} catch(异常分类参数 对象){

​ catch可以加多个

}catch(异常分类参数2 对象2){

多个catch捕获异常;

}

finally{ //不管是否有异常,都会执行...

​ 结束

}

第五节:自定义异常

public class PersonException extends Exception {
  public PersonException(String msg){
	super(msg);
  }
}

1567482394366.png

1567482421028.png

第六节 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集合体系

简易集合图-1567644887190.jpg

集合-1567644951591.png

Map-1567644973426.png

第二节:ArrayList和LinkedList 及其本质

针对这种长度不确定的数组--》动态数组,List接口的实现类ArrayList。

数组是集合的特例,是固定长度的数组。

1567652149451.png

代码:参考实例包

ArrayList注意事项:

特征:存放数据可以重复,有序的;

声明特别长的数组,也可以操作刚才的抖音案例。

10000个长度,但是都不发视频,浪费空间1万个存储空间。

LinkedList:链表数组,也是动态,具体代码。插入 删除效率高。查找效率低。

1567656383122.png

第三节:Set集合

特征:无序、不重复的数据。

HashSet

1567656371347.png

Set案例补充:

本科论文要查重,网页发表之后要看重复率。

1567818282111.png

杰卡德算法思想

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

1567820394831.png

键--值的方式,无序;

map.put("KO","大韩民国队"); //仍然键:KO,value后面的覆盖前面的。

map.keySet():这个方法,返回的是map里面所有键的集合.Set

针对Set就可以使用迭代器;

面试题:

问:Entry、HashMap、HashSet的存储原理

HashMap.Entry底层原理.png

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,包装类型.举例:

1567826720075.png

第六节:迭代器

Set接口方法

Iterator<E> **iterator**() 返回在此 set 中的元素上进行迭代的迭代器。

Iterator接口方法

boolean hasNext() 如果仍有元素可以迭代,则返回 true
E **next**() 返回迭代的下一个元素。
void **remove**() 从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。

1567821083187.png

增强型for循环;

第六章 IO

知识点:

1.File类

2.字节流

3.字符流

4.序列化

整体概念:

IO:

input/output,都需要依赖于文件,所以我们先讲File类。

6.1流概念

IO:Input/Output 输入/输出流。

流:概念;

水流:这个里面流动的介质是水,从高的地方到低的地方了。

电流:流动的电子,从介质的一端到另外一端。

IO流:是在计算中使用,传输的是数据,数据的本质单位是0 1 代码。传输的的字节/字符,之前说过

​ 字节:位的关系;1个字节=8位;八个二进制符号(0/1)。

流的分类:

​ 大小分类:字节流和字符流

​ 方向分类:输入流和输出流

二者也可以混起来:

​ 字节输入流、字符输出流 字节输出流 字符输入流

高级别还有流的扩充:缓冲流、打印流。

输入输出的方向.jpg

image-20200305145355679.png

再次强调,所谓的输入流/输出流,针对是程序。

流的分类.png

简化的IO.png

推荐大家使用,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();

1568076870467.png

为什么会报IOException的异常?

mkdir()创建目录

file.mkdir()--》d:/s2

然后再创建文件;

file.mkdirs()-->创建的多级目录;

面试题:递归遍历目录和文件

了解概念:递归;

一种通过重复将问题分解为同类的子问题而解决问题的方法。

image-20200305154147860.png

递归:简而言之,一个是方法本身自己调用自己;不是死循环吗?

​ 还要有一个退出的条件。

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

image-20200305161415208.png

数据源:File 文件 image-20200306144451531.png

6.3.1步骤:

1.步骤

​ 创建读写对象;File类;

2.进行读/写

109-->1101101

​ 64 +32 + 8+ 4 + 1 =109

如果已到达文件末尾,则返回 -1。

3.关闭

image-20200306145228693.png

image-20200305162142049.png

1568084669775.png

1568085181718.png 读的时候:代码 :

new String(b);没有指定长度;

1568085212992.png

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:字符流

三种方式实现字符流的读取操作,注意和书上的不同。

image-20200306150512939.png

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

image-20200306152131205.png

这时候菲菲出来了?为什么i胡???

image-20200306153859686.png

去掉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)

image-20200306155415185.png

image-20200306160639668.png

第七章 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 关闭

1568693290484.png

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语句的对象

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 语句,该语句可能为 INSERTUPDATEDELETE 语句,或者不返回任何内容的 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

1568256856663.png

换:针对贫困的同学,扶贫用针对本机.

String url="jdbc:mysql:///dormdb";

IP

String url="jdbc:mysql://127.0.0.1:3306/dormdb";

1568260296031.png

1567656371347.png

1568260320450.png

增删改代码:

1568260863714.png

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

1568693386978.png

数据集图示理解: 1568692057801.png 取值:

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方法 ResultSetMetaData对象.jpg 通用查询的思路理顺:

  • 多少列:结果集元数据对象得到。确定了行里面放的数据。

  • 使用什么样的数据结构List,List可以动态添加,适用不确定数据的情况。

  • 列不确定-》rowList,行不确定--》tableList

  • 如何知道得到查询结果集有多少行呢?动态的

    rowList到tableList的数据查询.jpg

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

1569466718398.png

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 经过编译成了一个字节码文件。

img

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

img

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型) Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。

没有公共的构造方法,方法共有64个太多了。下面用到哪个就详解哪个吧 img

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对象的 三种方式

image-20200519115135025.png

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