java面向对象编程

第一章:封装和继承

第一节:封装

1.1 提高程序的安全性。

1.2 将公共的一些属性、方法,封装到一个通用类,这也是封装的提现。

冰箱,彩电,电脑

代码的封装:

将属性设置为私有,然后通过共有的已经设定好的方法暴露给使用者,让使用者按照自己设定好的方式访问该属性,比如性别和年龄

public class Dog {
	String name;
	private String sex;
	private int age;
	private double money;
}

测试类代码:

public static void main(String[] args) {
	Dog dog=new Dog();
   	dog.name="旺财";
   	dog.sex="人妖";
   	
	}
public class Cat {
    private String name;
    private  String sex;
    private int age;
    public void setAge(int age){
        if(age>20){
            System.out.println("猫咪的年龄超纲了");
        }else{
            this.age=age;
        }
    }

    public void setSex(String sex){
        if(sex.equals("公")||sex.equals("母")) {
            this.sex = sex;
        }else{
            System.out.println("猫咪的性别只能是公母");
        }
    }

    public String getSex(){
        return sex;
    }
    public void setName(String name){
        this.name=name;
    }

    public String getName(){
        return name;
    }

}

第二节:重载

System.out.println(String);

println() 
        通过写入行分隔符字符串终止当前行。 
 void println(boolean x) 
        打印 boolean 值,然后终止该行。 
 void println(char x) 
          打印字符,然后终止该行。 
 void println(char[] x) 
          打印字符数组,然后终止该行。 
 void println(double x) 
          打印双精度浮点数,然后终止该行。 
 void println(float x) 
          打印浮点数,然后终止该行。 
 void println(int x) 
          打印整数,然后终止该行。 
 void println(long x) 
          打印 long 整数,然后终止该行。 
 void println(Object x) 
          打印 Object,然后终止该行。 
 void println(String x) 
          打印 String,然后终止该行。 

一个类中的同名方法,参数不同:重载

第三节:继承

提高的程序复用性,代码的复用。能不能多继承。

类:爹类;娘类;儿子:只能继承一个类;

Dog

   public class Dog {
        private String name;
        private String color;
        private String sex;
        private int age;
        
        public void eat(){
            System.out.println("狗狗"+name+"在吃肉骨头");
        }
    }

Cat

    public class Cat {
        private String name;
        private String color;
        private String sex;
        private int age;
    
        public void eat(){
            System.out.println("猫咪"+name+"在吃咸鱼");
        }
    }

java中使用extends来继承父类

1、extends
public class Pet {
	public String name;
	private int age;
	protected String color;
	 String sex;
	
	public void eat() {
		System.out.println(age+"岁的,"+color+"色的"+sex+name+"在吃");
	}
}

子类代码:

public class Cat extends Pet{	
	public void test(){
      this.name;
      this.sex;
      this.color;
      this.age;
   }
   
   
}
public class Dog extends Pet{	
	
	
}
2、方法的覆盖

父类中有的方法,被子类重写,子类中覆盖注意事项:

方法的可访问行不能比父类差

方法签名必须和父类中的一样

方法不能比父类中抛出更多的异常

public class Pet {
	protected String name;
	protected int age;
	protected String sex;
	
	protected void eat() {
		System.out.println(age+"岁的,"+sex+name+"在吃");
	}
}

子类中的覆盖:

public class Dog extends Pet{	
   public void eat(){
        System.out.println("狗狗"+name +"在吃肉骨头");
    }
}
public class Cat extends Pet{	
	  public void eat(){
        super.eat();
        System.out.println("猫咪在吃鱼");
    }
}
3、继承中的构造方法

构造方法是不能继承的

子类中的构造方法第一句必须创建父类的对象,即必须创建父类对象后才能创建子类对象。

public class A {	
	public A() {
		System.out.println("构造了A对象");
	}
}

public class B extends A{
	public B() {		
		System.out.println("构造了B对象");
	}
}

创建子类对象

B b=new B();

输出:

构造了A对象
构造了B对象

继承中的构造方法

1、子类无法继承父类构造方法

2、子类构造方法中第一句一定是调用父类构造方法

3、如果子类没有构造方法,父类必须有无参构造方法,默认构造方法。

4、创建子类对象前,系统需要首先创建父类对象

5、无论是么时候,最好把默认构造方法写上。

4、Object类简介

equals

源码:

public boolean equals(Object obj) {
        return (this == obj);
    }
public class Student {
    private String name;
    private int age;
    public  boolean equals(Object obj){
        if(obj instanceof Student){
            Student stu= (Student) obj;
            if(stu.age==this.age&&stu.name.equals(this.name)){
                return true;
            }
        }
        return false;
    }
}

测试类:

public static void main(String[] args) {
        Student stu1=new Student("小明",14);
        Student stu2=new Student("小明",14);
        System.out.println(stu1.equals(stu2));

    }

toString:一般情况下,需要子类覆盖

public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

hashCode

第四节:final关键词

  • 修饰属性:常量,一般常量名称习惯用大些,和static一起使用,称之为静态常量。
  • 修饰方法:不能被子类覆盖
  • 修饰类:不能被继承

修饰属性(变量)即为常量,一旦被赋值,就不可改变。

示例:

public final class Person {
	String name;
	String sex;
	int age=19;	
	static final double classMoney=100;
	
	public Person(String name,String sex,int age) {
		this.name=name;		
		this.sex=sex;	
		this.age=age;
	}	
	
	public final void intro() {
		System.out.printf("我是%s,在%s班,性别:%s,年龄%d岁,身高%.2f米\n",
				name,sex,age);
	}
	
}

测试类

public class StuTest {
	public static void main(String[] args) {
		
	}
}

面向对象的三大特征:

封装

继承

多态

作业:需求说明(15分钟)

编写宠物父类Pet,提供属性:name,age,color,封装年龄age不能超过20岁,带参数的构造方法;方法eat;编写子类Dog,覆盖eat方法;提供子类“看门”guardDoor方法。编写子类Cat,覆盖eat方法;提供子类方法“抓老鼠”catchMouse。编写各自的构造方法,测试运行。

第二章:多态和接口

回顾:
  • 封装时属性的修饰符有哪些?
  • private属性能被子类继承吗,应该是什么修饰符的属性?
  • 构造方法和实例方法的区别是什么?
  • 父类的构造方法能被子类继承么?
  • 子类构造方法的第一句应该是什么?
本章内容:
  • 多态
  • 抽象类和抽象方法
  • 接口
  • 面向接口编程

第一节:多态

物理世界,水的形态:固态(冰)、液态(水)、气态(水蒸气),静止的。

多态,不仅仅是静止的多种形态,也包括各种不同的形态。

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

作用:多态是Java面向对象里面使用非常广泛的一个特性,可以极大的挑高程序的扩展性;

两种形式的多态:

同一类,狗类,闻--》骨头--》吃骨头; 闻--》猫--》抓她。

同一个方法:里面不同的对象,有不同的响应。

三个条件;

1.需要有继承关系,或者子类实现父类接口,实现继承;

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

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

1、养宠物示例
public class Pet {
	protected String name;	
	
	protected void eat() {
		System.out.println(name+"在吃");
	}
}

两个子类代码:

public class Dog extends Pet{	
	public void eat(){
        System.out.println("狗狗"+name +"在吃肉骨头");
    }

    public void guradDoor(String man){
        if(man.equals("生人")){
            System.out.println("汪汪");
        }else{
            System.out.println("摇尾巴");
        }
    }

    public Dog(String name) {
        super(name);
    }	
}
public class Cat extends Pet{
    public Cat(String name) {
        super(name);
    }
    public void eat(){
        super.eat();
        System.out.println("猫咪"+name+"在吃鱼");
    }
}

主人类:

public class Master {	
    String name;
    public void feed(Pet pet){
        System.out.println("主人"+name+"在喂宠物");
        pet.eat();
    }
    public Master(String name) {
        this.name = name;
    }	
}

测试类:

public static void main(String[] args) {		
		Pet dog=new Dog("旺财");
        Pet cat=new Cat("小强");
        Pet rab=new Rabbit("大黑");
        
        Master m=new Master("王海滨");
        m.feed(dog);
        m.feed(cat);
        m.feed(rab);
}

如果要继续饲养更多的宠物,就需要不断地修改Master类中的代码,添加不同宠物的喂养方法。

2、向上转型
Pet dog=new Dog("旺财");
Pet cat=new Cat("长江");
dog.guridDoor("生人");//编译错误

向上转型后该对象只能当成父类对象来用,原来子类中定义的属性和方法就被屏蔽掉不能被调用了。比如狗里有一个【看门】的方法,如果向上转型后作为【宠物】类就不能调用【看门】的方法。

3、多态的定义

向同一个父类的不同子类对象传达同一个消息,行为不同

4、实现多态的步骤

1、编写父类Pet

public class Pet {
	protected String name;		
	protected void eat() {
		SystA em.out.println(name+"在吃");
	}
}

2、编写子类

public class Dog extends Pet{	
	public void eat() {
		System.out.println(name+"在吃肉骨头");
	}
	
	public Dog(String name) {
		this.name=name;		
	}	
}
public class Cat extends Pet{	
	public Cat(String name) {
		this.name=name;		
	}
	
	public void eat() {	
		System.out.println(name+"在吃"+"咸鱼干");
	}		
}

3、编写主类

public class Master {
	public String name;	
	public Master(String name) {
		this.name=name;
	}	
	public void feed(Pet pet) {
		System.out.println(name+"在喂宠物");
		pet.eat();
	}	
}

4、编写测试类

public static void main(String[] args) {		
		Master m=new Master("潘旺");
		Pet dog=new Dog("旺财");
		Pet cat=new Cat("长江");
		m.feed(dog);
		m.feed(cat);

	}
5、向下转型

把父类的对象当成子类来用

		Pet pet1=new Dog("旺财");
		Pet pet2=new Cat("长江");
		Dog d=(Dog) pet1;
		//Dog e=(Dog) pet2;

第二节:抽象类

Person类,人类;假如就分黑人和白人,new Person(),那这个时候出来的是黑人呢?还是白人?不太清楚。

黑人类,白人类,都可以new一个对象出来。

有时候,一些类不能直接new出来。这个类是一个更为抽象的概念。

Pet:宠物

Animal:抽象概念 --->出来的什么不太清楚???

Dog--->new Dog---》狗

我们在使用子类的时候,子类更为具体,可以直接new出相应的实例。抽象类一般没有对应的实例存在,需要借助于子类来实现。

1、抽象方法

没有方法体的方法,方法前面需要加abstract,抽象方法必须由子类来具体实现

2、抽象类

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

2.2 抽象类前必须添加abstract;

2.3 抽象类不能被实例化。

2.4 抽象类不一定有抽象方法

子类继承了抽象类,需要实现抽象方法或者也标注成抽象类。

public abstract class Pet {
	protected String name;		
	protected abstract void eat();
}
public class Dog extends Pet{		
	public Dog(String name) {
		this.name=name;		
	}	
	protected void eat() {
		System.out.println("吃肉骨头");		
	}	
}

主人类及测试类代码

public class Master {
	public String name;	
	public Master(String name) {
		this.name=name;
	}	
	public void feed(Pet pet) {
		System.out.println(name+"在喂宠物");
		pet.eat();
	}	
}
	public static void main(String[] args) {		
		Master m=new Master("潘旺");		
		//Pet p=new Pet();//抽象类不能够被实例化
		Pet pet1=new Dog("旺财");
		Pet pet2=new Cat("长江");				
		m.feed(pet1);
		m.feed(pet2);
	}

第三节:接口

回顾:
  • 什么是多态?
  • Pet是Dog的父类这种代码写法是否正确:Pet p = new Dog( );
  • 抽象类是否必须有抽象方法?
  • 抽象类能否通过new进行实例化?
  • 子类继承了一个抽象的父类后必须实现父类的抽象方法么?
1、什么是接口

就是一组规范、规则、模板

2、接口的规则

接口、类定义的时候,首字母大写,一般这里接口名以I打头。

public interface Work {
	public static final int age=9;
	public abstract void start();
}
3、接口的使用

示例:USB接口及实现类

public interface USB {
    void work();
}

U盘实现类

public class UDisk implements USB {
    @Override
    public void work() {
        System.out.println("读写文件");
    }
}

风扇实现类

public class Fan implements USB {
    @Override
    public void work() {
        System.out.println("吹凉风");
    }
}

接口中的变量都是static,final

接口中的方法都是public,abstract

子类实现接口后需要具体实现抽象方法

JDK8提供了default,默认的实现,主要是为了解决超级大接口,都需要实现时太多的无用方法

示例:主人发朋友圈

第一步:定义显示接口

public interface Show {	
	public void pose() ;
}

第二步:定义Dog实现类

public class Dog  implements Show{	
	 @Override
    public void pose() {
        System.out.println("狗狗"+name+"摆个Pose");
    }
}

第三步:定义人实现类

public class Master implements Show {
    String name;
    public void feed(Pet pet){
        System.out.println("主人"+name+"在喂宠物");
        pet.eat();
    }

    public Master(String name) {        this.name = name;    }

    public void showFriend(Show show){
        System.out.println("主人"+name+"发朋友圈");
        show.pose();
    }

    @Override
    public void pose() {
        System.out.println("主人"+name+"好帅");
    }
}

第四步:测试类

public static void main(String[] args) {		
		Master m=new Master("潘旺");			
		Show dog=new Dog("花花");
		m.showFriend(dog);
		m.showFriend(m);		
}
4、接口实现多态的示例
小结:接口的特点

​ 1、接口不能实例化

​ 2、接口中的所有方法默认都是public abstract;接口中的成员变量:public static final :静态常量

​ 3、接口中不能有具体的方法实现,jdk1.8之后提供了default,允许有默认实现

​ 4、接口中不能有构造方法,静态方法,private

​ 5、非抽象的实现类(子类) 实现接口,必须实现接口中的所有的抽象方法

​ 6、多实现:实现类实现接口,可以实现多个接口:class Sub implements A,B,C

​ 7、接口可以继承接口:A extends B,C

第四节:面向接口编程

面向对象思想总结

  • 封装:
  • 继承:
  • 多态:
作业:(15分钟)

一、编写一个CS游戏:

  • Person类:

  • 属性:name,sort(类别:警、匪)、weapon武器接口

  • 方法:fight(战斗)

  • 武器接口实现类:

  • AK47、M16、等 警察和匪徒实现类

  • Main:创建警匪角色,让他们战斗,战斗中可以随时换武器。

    1、武器接口

public interface Weapon {	
	public void attack();
}

2、AK47和M416实现类

public class AK47 implements Weapon{
	String name="AK47";
	@Override
	public void attack() {		
		System.out.println("AK47爆头");
	}

	public String toString() {
		return name;
	}
}
public class M416 implements Weapon{
	String name="M416";	
	@Override
	public void attack() {
		System.out.println("M416打敌人1000血");		
	}

	@Override
	public String toString() {
		return  name;
	}	
}

3、人类

public class Person {
	String name;
	String sort;
	Weapon weap;
	
	public Person(String name,String sort) {
		this.name=name;
		this.sort=sort;
	}
	
	public void setWeapon(Weapon weap) {
		this.weap=weap;
		System.out.println(sort+name+"已换枪"+weap);
	}
	
	public void fight() {
		System.out.println(sort+name+"在战斗");
		weap.attack();
	}
}

4、测试类:

	public static void main(String[] args) {
		Person p=new Person("杨光磊","警察");
		Person c=new Person("赵孔龙","土匪");
		AK47 ak=new AK47();
		M416 m4=new M416();
		p.setWeapon(m4);
		c.setWeapon(ak);
		p.fight();
		c.fight();
		
		M416 mm=new M416();
		c.setWeapon(mm);
		c.fight();
	}

二、主人发朋友圈

第三章:异常处理和日志

回顾:
  • 什么是多态?

  • 抽象类和接口的区别

  • 面向接口编程的概念,有什么好处?

本章内容
  • 什么是异常
  • 异常的分类
  • 怎么处理异常

第一节:什么是异常

public static void main(String[] args) {
	Scanner in = new Scanner(System.in);
	System.out.print("请输入被除数:");
	int num1 = in.nextInt();
	System.out.print("请输入除数:");
	int num2 = in.nextInt();
	int jg = num1/num2;
	System.out.println(num1+"除以"+num2+"等于"+jg);
}

第二节:异常的处理

1、语法结构

try{

}catch(异常类型 异常变量){
	处理具体的异常
}finally{
	无论如何都要执行的代码
}

2、捕获

3、多重catch

public static void main(String[] args) {
        try {
            Scanner in = new Scanner(System.in);
            System.out.print("请输入被除数:");
            int num1 = in.nextInt();
            System.out.print("请输入除数:");
            int num2 = in.nextInt();
            int jg = num1 / num2;
            System.out.println(num1 + "除以" + num2 + "等于" + jg);
        }catch(ArithmeticException e){
            System.out.println("除数不能为零");
            System.out.println(e.getMessage());
            e.printStackTrace();
        }catch(InputMismatchException ex){
            System.out.println("请输入数字");
            System.out.println(ex.getMessage());
            ex.printStackTrace();
        }catch(Exception ee){
            System.out.println("你出错了");
        }finally{
            System.out.println("这是无论如何都要执行的finally代码");
        }
        System.out.println("程序结束");
    }

4、finally语句块

public static void main(String[] args){
			
	}

5、自定义异常

a、自定义异常类

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

b、在Person中设置sex时抛出异常

public class Person {

    private String name;
    private String sex;

    public String getName() {        return name;    }
    public void setName(String name) {        this.name = name;    }
    public String getSex() {        return sex;    }
    public void setSex(String sex) throws SexException {
        if(sex.equals("男")||sex.equals("女")){
            this.sex = sex;
        }else{
            throw new SexException("性别只能是男女");
        }
    }

    public Person() {    }
}

c、测试类代码:

 public static void main(String[] args) {
        try {
            Person p=new Person();
            p.setSex("人妖");
            p.setName("王海滨");
        } catch (SexException e) {
            System.out.println(e.getMessage());
        }
        System.out.println("程序结束");
    }

6、抛出异常

7、异常的分类

1560090423702.png

第三节:日志管理

eclipse中添加jar包到classPath:

1)在项目中新建文件夹【lib】—项目上右键-【Build Path】-【Configure Build path】-在对话框中选中【Libraries】选项-点击【Add JARS】按钮在弹出的【JAR Selection】对话框中选择当前项目中lib目录下的log4j-1.2.9.jar包-点击【确定】关闭【JAR Selection】在【java Build Path页面】单击【Apply and close】

1560827893319.png

IDEA中添加外部jar包

项目的src文件夹上右键-新建文件夹或包lib

将jar文件拷贝到lib目录中

lib目录上右键-【Add as LIbrary】

1、log4j

public static void main(String[] args) {	
		 Logger logger=Logger.getLogger("exctest");
        logger.debug("debug信息");
        try {
            Scanner in = new Scanner(System.in);
            logger.info("info级别");
            System.out.print("请输入被除数:");
            int num1 = in.nextInt();
            logger.warn("warn警告信息");
            System.out.print("请输入除数:");
            int num2 = in.nextInt();
            int jg = num1 / num2;
            System.out.println(num1 + "除以" + num2 + "等于" + jg);
        }
        catch (Exception e) {
            logger.error("错误信息");
            e.printStackTrace();
        }

        System.out.println("程序结束");
}

属性文件:

### 把日志信息输出到控制台  ###
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

### 把日志信息输出到文件:AAA.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=AAA.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=info,stdout,file

注意:在eclipse中有时候会出现属性文件无法编译输出的情况,这时改动properties文件对日志级别的改动不起作用。可以进入到该项目的硬盘目录中将编译好的log4j.properties文件删除掉,重新运行程序即可。

%m 输出代码中指定的消息
%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
%r 输出自应用启动到输出该log信息耗费的毫秒数
%c 输出所属的类目,通常就是所在类的全名
%t 输出产生该日志事件的线程名
%n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”
%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921

2、日志的信息级别:

Fatal:记录严重的错误,并且会导致应用程序退出

Error:记录严重的错误,但不会影响程序的继续运行

Warn:记录警告

Info:记录程序中比较有意义的信息

Debug:记录程序中的细节信息

作业:自定义异常:SexException(10分钟)

第四章:javaIO操作

回顾:

java中处理异常的几个关键词是什么?

在使用多个catch块捕获异常时,顺序是什么?

异常的分类有哪些?

本章节内容
  • File的使用
  • 输出输入流的使用
  • 字节流和字符流的使用

第一节:文件访问

1、File类

文件和目录路径名的抽象表示形式

exists(),isFile(),isDirectory(),list(),ListFile(),length(),rename(),getName(),getPath()

示例:显示指定文件夹下直接的文件和文件夹

    public static void showFile(String path){
        File file=new File(path);//根据路径参数创建File对象
        if(!file.exists()||file.isFile()) return;//如果File对象不存在或只是文件则退出
        File[] files = file.listFiles();//获得该目录下所有的File对象数组
        for(int i=0;i<files.length;i++){//遍历File[]
            if(files[i].isDirectory()){//如果当前元素是目录
                System.out.println(files[i].getPath()+"\t\t<目录>");//输出该目录的路径     
            }else{
                System.out.println(files[i].getName()+"\t\t"+files[i].length());//如果是文件,则输出文件名和文件大小(单位:字节byte)
            }
        }
    }

测试代码:

	 public static void main(String[] args) {
		showFile("e:/iodemo");
	}

对于文件夹目录结构:

image-20200731093237986.png

只要不断输入下一层文件夹路径,就可以输出该文件夹下的文件和目录

方法递归:

   public static void showFile(String path){
        File file=new File(path);//根据路径参数创建File对象
        if(!file.exists()||file.isFile()) return;//如果File对象不存在或只是文件则退出
        File[] files = file.listFiles();//获得该目录下所有的File对象数组
        for(int i=0;i<files.length;i++){//遍历File[]
            if(files[i].isDirectory()){//如果当前元素是目录
                System.out.println(files[i].getPath()+"\t\t<目录>");//输出该目录的路径
                showFile(files[i].getPath());//递归调用showFile()继续进行下一级目录的展示
            }else{
                System.out.println(files[i].getName()+"\t\t"+files[i].length());//如果是文件,则输出文件名和文件大小(单位:字节byte)
            }
        }
    }

作业:(15分钟)

1、自定义性别异常要加日志输出

2、列出给定文件夹下所有的路径和文件

第二节:流操作文件

1、什么是流

生活中的流,水流、电流、人流,流是一组物质从一处通过管道移动到另一处,流具有方向性;

程序中的流是指数据从一处移动到另一处,是一连串不间断的数据集合,即是一连串流动的字节。

2、流的分类

​ 功能来分:节点流和处理流

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

​ 类型来分:字节流和字符流

流分类.png

3、文件的读取和写入

观察读取小段文本文件:abcdefghijkm,每次只读取4个字节

	public static void main(String[] args) throws Exception{
		FileInputStream fis=new FileInputStream("e:/test.txt");		
		byte[] bs=new byte[4];
		int count=fis.read(bs);
		System.out.println(new String(bs)+">>>>"+count);
		count=fis.read(bs);
		System.out.println(new String(bs)+">>>>"+count);
		count=fis.read(bs);
		System.out.println(new String(bs)+">>>>"+count);
		count=fis.read(bs);
		System.out.println(new String(bs)+">>>>"+count);
		count=fis.read(bs);
		System.out.println(new String(bs)+">>>>"+count);
		fis.close();		
	}

1560393560075.png

观察结果:每次读取4个字节,m是文件结尾,由于只读了一个字母到字节数组中,数组后三个元素没有被新数据所覆盖,仍然保留上一次读取的字母,这样为了正确输出文件内容,可以把new String(bs),改为new String(bs,0,count),并且当读到文件结尾再继续读取时,就会返回-1,可以用这个作为循环读取的条件。

 public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream("e:/aaa.txt");
        byte[] bs=new byte[4];
        int count;
       /* int count=fis.read(bs);
        System.out.println(new String(bs)+"---->"+count);
        count=fis.read(bs);
        System.out.println(new String(bs)+"---->"+count);
         count=fis.read(bs);
        System.out.println(new String(bs)+"---->"+count);
         count=fis.read(bs);
        System.out.println(new String(bs)+"---->"+count);
         count=fis.read(bs);
        System.out.println(new String(bs)+"---->"+count);*/
        while((count=fis.read(bs))!=-1){
            System.out.println(new String(bs)+"---->"+count);
        }
        fis.close();
    }

1560393908537.png

加入输出流将文件拷贝到另一个地方

	public static void main(String[] args) throws Exception{		
					
	}

完整代码:

 private static void copyFile(String name1,String name2) throws IOException {
        FileInputStream fis = new FileInputStream(name1);
        FileOutputStream fos=new FileOutputStream(name2);
        byte[] bs=new byte[1024];
        int count;
        while((count=fis.read(bs))!=-1){           
            fos.write(bs,0,count);
        }
        fis.close();
        fos.close();
    }

由于FileInputStream和OutputStream是节点流,操作的效率很低,需要使用操作流BufferedInputStram和BufferedOutputStream对其进行包装,真实的代码应该修改如下:

	public static void main(String[] args) throws Exception{		
			
	}

将该操作抽取成方法

 private static void copyFile(String name1,String name2) throws IOException {
        FileInputStream fis = new FileInputStream(name1);
        BufferedInputStream bis=new BufferedInputStream(fis);
        FileOutputStream fos=new FileOutputStream(name2);
        BufferedOutputStream bos=new BufferedOutputStream(fos);
        byte[] bs=new byte[1024];
        int count;

        while((count=bis.read(bs))!=-1){
            bos.write(bs,0,count);
        }
        bis.close();//先关外层的包装类
        bos.close();
        fis.close();//再关内层的核心类
        fos.close();
    }

注意关闭顺序否则会出现以下异常

java.io.IOException: Stream Closed

作业:拷贝文件夹里的所有文件

第三节:其他流操作

1、字符流

Reader和Writer

BufferedReader和BufferedWriter

 public static void main(String[] args) throws Exception {
        FileInputStream fis=new FileInputStream("e:/aaa.txt");
        Reader reader=new InputStreamReader(fis);
        BufferedReader br=new BufferedReader(reader);
        char[] bs=new char[4];
        int count=br.read(bs);
        System.out.println(new String(bs));
    		br.close();
    		reader.close();
    		fis.close();

        FileOutputStream fos=new FileOutputStream("e:/ttt.txt");
        Writer writer=new OutputStreamWriter(fos);
        BufferedWriter bw=new BufferedWriter(writer);
        bw.write("奥斯卡的分水岭的分科撒地方卡士大夫了的课时费");
        //bw.flush();//清空缓冲区内容,写到文件中
        bw.close();//关闭之前会自动清空缓冲区
        writer.close();
        fos.close();
    }
2、对象的序列化

-Serializable接口和ObjectInputStream类

ObjectOutputStream 和 ObjectInputStream 分别与 FileOutputStream 和 FileInputStream 一起使用时,可以为应用程序提供对对象图形的持久存储。

    public static void main(String[] args) throws Exception {
        FileOutputStream fos = new FileOutputStream("e:/student.obj");
        ObjectOutputStream oos=new ObjectOutputStream(fos);
        Student stu1=new Student("张三丰",123);
        Student stu2=new Student("张无忌",23);
        Student stu3=new Student("张翠山",63);
        oos.writeObject(stu1);
        oos.writeObject(stu2);
        oos.writeObject(stu3);
        oos.close();
        fos.close();

        FileInputStream fis = new FileInputStream("e:/student.obj");
        ObjectInputStream ois=new ObjectInputStream(fis);
        Student s1= (Student) ois.readObject();
        System.out.println(s1);
        s1.intro();
        Student s2= (Student) ois.readObject();
        System.out.println(s2);
        s2.intro();
        Student s3= (Student) ois.readObject();
        System.out.println(s3);
        s3.intro();
       /* Student s4= (Student) ois.readObject();
        System.out.println(s4);*///读到文件结尾后再读会爆EOFException异常
        ois.close();
        fis.close();
    }

如果要存的类没有实现Serializable接口,会爆出下面异常

java.io.NotSerializableException: com.io.Student

Student类代码:

public class Student implements Serializable {

    String name;
    int age;

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

    public void intro(){
        System.out.println("你好我是:"+name+"今年"+age+"岁");
    }
    ...
}
3、PrintWriter
public static void main(String[] args) throws Exception {
       PrintWriter pw=new PrintWriter("e:/eeeee.txt");//能自己创建文件
       pw.write("案例大富科技绿山咖啡吉林省的反馈");
       pw.close();
    }
总结:

File类

File.separator

流的分类

InputStream和OutputStream

作业:

列出指定文件夹下的所有子文件夹和文件;

拷贝文件到另一个文件夹

第五章:集合框架

回顾:

流的分类?

为什么在流操作时一般不使用FileInputStream和FileOutputStream来直接操作,而要使用包装类?

本章内容:

Collection:List、Set;数组

相同数据类型的集合;问题:长度一般固定的;int[]arr=new int[100];

//实际使用的时候,人数不太确定,人数:20;空间浪费;10000000;

集合:长度不确定;并且集合里面的参数是Object类型。

Map

泛型

第一节:为什么要用集合框架

1、什么是集合框架

1560406003468.png

2、集合框架的作用

第二节:List接口的实现类

1、ArrayList:

Object数组,遍历的速度快,插入和删除的速度慢,是线程不同步的。

 public static void main(String[] args) throws Exception {
       List list=new ArrayList();
       list.add("你好");
       list.add(23);
       list.add(new Student("李四",88));
       list.add(1,new Date());
       //list.remove("你好");
       list.remove(2);
        System.out.println(list.contains("你好"));
       //list.clear();

       for(Object obj:list){
           System.out.println(obj);
       }
    }	
2、泛型:

泛型:对于宽泛数据类型的限制;C#2.20 提出,JDK1.5借鉴。

用来界定集合中元素的数据类型,用尖括号表示<>

public static void main(String[] args) {
	List<Student> list=new ArrayList<Student>();
}

作业:(15分钟)

拷贝文件

list添加,删除,遍历

list.containes()判断是否包含指定元素,的依据是list中对象的equals方法

2、LinkedList:

链表结构,插入和删除速度快,遍历速度慢

3、Vector:

和ArrayList相同,只是线程同步的

第二节:Set接口及实现类

和List接口都是Collection的子接口

1、HashSet
public static void main(String[] args) throws Exception {

        Student stu1=new Student("令狐冲",19);
        Student stu2=new Student("令狐冲",19);
        Student stu3=new Student("风清扬",79);
        Student stu4=new Student("岳不群",49);

        HashSet<Student> set=new HashSet<Student>();
        set.add(stu1);
        set.add(stu2);
        set.add(stu3);
        set.add(stu4);
        System.out.println(set.contains(stu2));
        for(Student stu:set){
            System.out.println(stu);
        }

     /*   Iterator it=set.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }*/
    }

覆盖Student中的equals和hashCode方法

    @Override
    public boolean equals(Object obj){
        if(obj instanceof  Student){
            Student stu= (Student) obj;
            if(stu.age==this.age&&stu.name.equals(this.name)){
                return true;
            }
        }
        return false;
    }
    @Override
    public int hashCode(){
        return name.hashCode()+age;
    }

再来观察执行结果

true
Student{name='岳不群', age=49}
Student{name='令狐冲', age=19}
Student{name='风清扬', age=79}
2、TreeSet

​ 排序Comparator和Comparable接口

示例1:Person实现Comparable接口

public class Student implements Comparable<Student>{	
	...
	@Override
    public int compareTo(Student stu) {       
        if(this.age>stu.age) return -1;
        if(this.age<stu.age) return 1;
        return 0;
    }
}

测试类:

public static void main(String[] args) throws Exception {
        Student stu1=new Student("令狐冲",19);
        Student stu2=new Student("令狐冲",19);
        Student stu3=new Student("风清扬",79);
        Student stu4=new Student("岳不群",49);

        TreeSet<Student> set= new TreeSet<Student>();
        set.add(stu1);
        set.add(stu2);
        set.add(stu3);
        set.add(stu4);

        System.out.println(set.contains(stu2));
        for(Student stu:set){
            System.out.println(stu);
        }

     /*   Iterator it=set.iterator();
       想一想:result.next
        while(it.hasNext()){
            System.out.println(it.next());
        }*/
    }

输出就是按照年龄的顺序来输出的

1560501837814.png

示例2:创建实现Comparator接口的比较器

public class StuComptor implements Comparator<Student> {
    @Override
    public int compare(Student stu1, Student stu2) {
        if(stu1.age>stu2.age)return 1;
        if(stu1.age<stu2.age)return -1;
        return 0;
    }
}

Person类可以不必实现Comparable接口,保持原有Person的POJO特性,利用比较器来完成排序

测试类代码:

		 public static void main(String[] args) throws Exception {

        Student stu1=new Student("令狐冲",19);
        Student stu2=new Student("令狐冲",19);
        Student stu3=new Student("风清扬",79);
        Student stu4=new Student("岳不群",49);

        TreeSet<Student> set= new TreeSet<Student>(new StuComptor());
        set.add(stu1);
        set.add(stu2);
        set.add(stu3);
        set.add(stu4);

        System.out.println(set.contains(stu2));
        for(Student stu:set){
            System.out.println(stu);
        }

     /*   Iterator it=set.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }*/
    }

3、LinkedHashSet

    public static void main(String[] args) throws Exception {

        Student stu1=new Student("令狐冲",19);
        Student stu2=new Student("令狐冲",19);
        Student stu3=new Student("风清扬",79);
        Student stu4=new Student("岳不群",49);

        LinkedHashSet<Student> set= new LinkedHashSet<Student>();
        set.add(stu1);
        set.add(stu2);
        set.add(stu3);
        set.add(stu4);
        
        System.out.println(set.contains(stu2));
        for(Student stu:set){
            System.out.println(stu);
        }

     /*   Iterator it=set.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }*/
    }

按照加入的顺序排列

true
Student{name='令狐冲', age=19}
Student{name='风清扬', age=79}
Student{name='岳不群', age=49}

第三节:Map接口及实现类

image-20200803093657060.png

1、HashMap:

线程不同步,键和值都可以为null;

public static void main(String[] args) {
        HashMap map=new HashMap(111,0.9f);
        map.put("one","111111");
        map.put("two","222222");
        map.put("three",null);
        map.put(null,"999999");
        map.put(null,"000000");

        map.put("four","444444");
        map.put("five","555555");
        map.remove("two");
        System.out.println(map.containsKey("five"));
        Set<Map.Entry> enset=map.entrySet();
        for(Map.Entry en:enset){
            System.out.println(en);
        }

        Set keySet=map.keySet();
        for (Object key:keySet){
            System.out.println(key+"--->"+map.get(key));
        }
    }
2、HashTable:

线程同步,锁定所有数据,键和值都不能为null;

public static void main(String args[]) throws Exception {  
		
			
}
3、ConcurrentHashMap(16,0.75)

线程同步,只锁定部分数据

4、LinkedHashMap
5、TreeMap

第四节:HashMap实现的基本原理

1、示例:

要查找一个数(取值范围为1-100)是否包含在集合[4,7,11,21,8,14,17]中,应该怎么办。

    private static boolean hashFind(int num) {        
        int[] arr={4,7,11,21,8,14,17,34};
        for(int i=0;i<arr.length;i++){
            if(arr[i]==num){
                 return true;
            }
        }
        return false;
    }

如果使用长度是100的数组

让集合中数值对应数组下标的元素赋值为:1

2、当取值范围无限扩大后

使用hash函数确定某个数应该的位置

image-20200803103008681.png

作业:将Person对象放入ArrayList中,必须有重复的对象,转换到HashSet中(不能有重复的对象),之后将集合中的Person对象导入到HashMap,key使用Person的名字,并遍历并展示出来



​ ​

	 public static void main(String args[]) throws Exception {  
			TreeSet<Person> set=new TreeSet<Person>();
		     set.add(new Person("张三",23));
		     set.add(new Person("李四",3));
		     set.add(new Person("王麻子",7));
		     set.add(new Person("找刘龙",58));
		     Map<String,Person> map=new HashMap<String, Person>();
		     ArrayList<Person> list=new ArrayList<Person>();
		     for(Person p:set) {
		    	 System.out.println(p);
		    	 map.put(p.getName(), p);
		    	 list.add(p);
		     }	
		     for(Object key:map.keySet()) {
		    	 System.out.println(key+">>"+map.get(key));
		     }
		     
		     for(Person p:list) {
		    	 System.out.println(p);
		     }
	}

第六章:java数据库连接基础

回顾:
  • List、Map是否都继承自Collection接口 ?
  • 请说明集合类ArrayList与 LinkedList的区别
  • HashMap与HashTable的区别?
本章内容:
  • 掌握JDBC的工作原理
  • 掌握如何获取数据库连接
  • 掌握如何对数据进行增、删、改、查

第一节:JDBC工作原理

1560586689679.png

1、JDBCAPI

DriverManager:驱动管理器

Connection:数据库连接

Statement:sql语句

ResultSet:返回结果集

2、JDBC的工作流程

1560587071496.png

3、 DriverManager:

JDBC驱动程序管理器:是JDBC的管理层,作用于用户和驱动程序之间。它跟踪可用的驱动程序,并在数据库和相应的驱动程序之间建立连接。 JDBC标准规定:所有的驱动程序类必须包含一个静态部分。这个静态部分在加载该实例时由DriverManager类进行注册。 用户在正常情况下将不会直接调用DriverManager.regiserDriver方法,而是在加载驱动程序时由驱动程序自动调用。 注册驱动程序:

Class.forName("com.mysql.cj.jdbc.Driver");
Class.forName("com.microsoft.sqlserver.jdbc. SQLServerDriver");
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Class.forName( "oracle.jdbc.driver.OracleDriver "); 

注册的驱动程序类名称必须在用户的classPath中。

4、Connection接口及对象:
DriverManger的方法:
  • static Connection getConnection(String url,String user,String password):

  • url: jdbc:<subprotocol>:<subname>

  • 协议: jdbc表示协议,它是唯一的,jdbc只有这一个协议。

  • 子协议: 主要用于识别数据库驱动程序,(不同的数据库驱动程序不同。)

  • 子名: 属于专门的驱动程序。数据源名。

  • 数据源名。

  • jdbc:odbc:news//ODBC桥接方式的URL
    jdbc:sqlserver://localhost:1433;DatabaseName=news//SQLServer的URL
    jdbc:oracle:thin:@localhost:1521:orcl  //oracle的URL
    jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai  
    

第二节:数据库增删改

1、数据库连接及增删改操作——纯粹代码

	//1.注册驱动
	
	//2.获得连接对象
	
	//3、获得语句对象
	
	//4、执行sql语句
	
	//5、关闭数据库连接
	
public static void main(String[] args) throws Exception {
        //1.注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2.获得连接对象
        String url="jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai";
        Connection con=DriverManager.getConnection(url,"root","admin");
        //3、获得语句对象
        Statement st=con.createStatement();
        //4、执行sql语句
        String sql="update bank set balance=90000 where id=6";
        int i = st.executeUpdate(sql);
        if(i>0){
            System.out.println("数据库操作成功");
        }
        //5、关闭数据库连接
        st.close();
        con.close();
    }

2、增删改操作——添加异常处理

 public static void main(String[] args) {
        Connection con= null;
        Statement st= null;
        try {
            //1.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //2.获得连接对象
            String url="jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai";
            con = DriverManager.getConnection(url,"root","admin");
            //3、获得语句对象
            st = con.createStatement();
            //4、执行sql语句
            String sql="update bank set balance=90000 where id=6";
            int i = st.executeUpdate(sql);
            if(i>0){
                System.out.println("数据库操作成功");
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally{
            try {//5、关闭数据库连接
                st.close();
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }

        }
        
    }

如果录入或修改有中文,则有的版本会出现中文乱码问题

可以在url后面加上:useUnicode=true&characterEncoding=UTF-8

第三节:数据库查询

1、结果集合ResultSet

public static void main(String[] args) throws Exception {
        //1.注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2.获得连接对象
        String url="jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai";
        Connection con=DriverManager.getConnection(url,"root","admin");
        //3、获得语句对象
        Statement st=con.createStatement();
        //4、执行sql语句
        String sql="select * from bank";
        ResultSet resultSet = st.executeQuery(sql);
        while(resultSet.next()){
            System.out.print(resultSet.getString(1)+"\t");
            System.out.print(resultSet.getString("account")+"\t");
            System.out.println(resultSet.getString(3)+"\t");
        }
        //5、关闭数据库连接
        resultSet.close();
        st.close();
        con.close();
    }

错误:

java.sql.SQLException: No suitable driver found for jdbc:mysql//localhost:3306/test?serverTimezone=Asia/Shanghai
URL写错了

2、查询结果封装成对象并装载集合

package com.jdbc;

public class Bank {
    private int id;
    private String account;
    private double balance;

    @Override
    public String toString() {
        return "Bank{" +
                "id=" + id +
                ", account='" + account + '\'' +
                ", balance=" + balance +
                '}';
    }

    public Bank(int id, String account, double balance) {
        this.id = id;
        this.account = account;
        this.balance = balance;
    }

    public Bank() {
    }
   ...
}


查询代码:

public static void main(String[] args) throws Exception {
        //1.注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2.获得连接对象
        String url="jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai";
        Connection con=DriverManager.getConnection(url,"root","admin");
        //3、获得语句对象
        Statement st=con.createStatement();
        //4、执行sql语句
        String sql="select * from bank";
        ResultSet resultSet = st.executeQuery(sql);
        List<Bank> list=new ArrayList<Bank>();
   	  //5、处理结果集
        while(resultSet.next()){
            Bank bank=new Bank(resultSet.getInt(1),
                    resultSet.getString(2),
                    resultSet.getDouble(3));
            list.add(bank);            
        }
        ///6、关闭数据库连接
        resultSet.close();
        st.close();
        con.close();

        for(Bank b:list){
            System.out.println(b);
        }
    }

总结:

JDBCAPI

DriverManager

Connection

Statement

ResultSet

步骤

1、注册驱动

2、获得连接

3、获得语句

4、执行语句

5、处理结果集(查询需要)

6、关闭各种接口对象

作业:完成指定数据库表的录入和查询(20分钟)

字段名 字段类型 备注
shopId int 商品编号;主键、自增
shopName varchar 商品名称
shopPrice double 商品价格
addDate date 生产日期

第七章:JDBC深入编程

回顾:
  • JDBC是什么意思?
  • 进行数据库操作代码的步骤?
本章内容:
  • PreparedStatement的使用方法
  • BaseDao的通用方法。
  • 自动主键的获得
  • JDBC事务处理

第一节:PreparedStatement接口

1、参数设置

package com.jdbc;

import java.sql.*;
import java.time.LocalDate;

/**
 * @author: wangxiangnan
 * @time: 2020/8/7 11:06
 * @description:
 */
public class JDBCMain {
    public static void main(String[] args) throws Exception {

        Class.forName("com.mysql.cj.jdbc.Driver");
        String url="jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai";

        Connection con=DriverManager.getConnection(url,"root","admin");
        String sql="insert into dept values(null,?,?)";

        PreparedStatement ps = con.prepareStatement(sql);
        ps.setString(1,"医学部3");
        //ps.setDate(2,java.sql.Date.valueOf("2001-12-16"));
        ps.setObject(2, LocalDate.now());
        ps.setObject(2,new java.util.Date());
       
        int count=ps.executeUpdate();
        if(count>0){
            System.out.println("录入成功");
        }else{
            System.out.println("录入失败");
        }

        ps.close();
        con.close();
    }
}

2、java.util.Date 和java.sql.Date以及Timestamp

public static void main(String[] args) {
		
}
Wed Jun 19 15:32:38 CST 2019
2019-06-19
2019-06-19 15:32:38.111

3、SQL注入攻击

String sql="select * from users where username=' ' and pass=' '";
String sql="select * from users where username='' or '0'='0' and pass='' or '0'='0'";
		' or '0'='0 ;
		' or '0'='0

4、抽取设置参数的通用方法

 public static void setParams(PreparedStatement ps,Object[] params) throws SQLException {         if(params==null)return;
        for(int i=0;i<params.length;i++){
            ps.setObject(i+1,params[i]);
        }
    }
#设置参数常见的错误
Exception in thread "main" java.sql.SQLException: Parameter index out of range (5 > number of parameters, which is 4).   --?少参数多
java.sql.SQLException: No value specified for parameter 6   ##?多参数少

测试

public static void main(String[] args) throws Exception{		
		//1.注册驱动
		
		//2.获得连接
		//3、获得语句对象		
				
		//4、执行语句
		//调用通用的设置参数方法			
		
		//5、关闭数据库连接			

}

第二节:数据库通用类

1、将最通用的方法加入到通用类中
  • getConnection()
  • closeAll();
  • setParameter();
  • executeUpdate();
  • executeQuery();
public class MyDB {
    public static String driver="com.mysql.cj.jdbc.Driver";
    public static String url="jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai";
    public static String user="root";
    public static String pass="admin";
    static{
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConn() throws SQLException {
        return DriverManager.getConnection(url,user,pass);
    }

    public static int update(String sql,Object[] params) throws SQLException {
        Connection con = null;
        PreparedStatement ps = null;
        try {
            con = getConn();
            ps = con.prepareStatement(sql);
            setParams(ps,params);
            return ps.executeUpdate();
        } finally {
            closeAll(null,ps,con);
        }
    }

public static void setParams(PreparedStatement ps,Object[] params) throws SQLException {
        if(params!=null){
            for(int i=0;i<params.length;i++){
                ps.setObject(i+1,params[i]);
            }
        }
    }

    public static void closeAll(ResultSet rs, Statement st, Connection con){
        if(rs!=null){
            try {
                rs.close();
                rs=null;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(st!=null){
            try {
                st.close();
                st=null;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(con!=null){
            try {
                con.close();
                con=null;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

测试代码:

	public static void main(String[] args) {
		String sql="insert into classes(className,createDate) values(?,?)";
		BaseDB.update(sql, new Object[] {"A888",new Date()});
	}

第八章:DAO模式

第一节:通用查询

要编写通用查询就要知道传进来的SQL语句的结果集有多少字段:

元数据:

1、ResultSetMetaData:

结果集的媒体元数据,可以获得结果集的底层数据库相关信息。

package java.sql;

public interface ResultSetMetaData extends Wrapper {

    int getColumnCount() throws SQLException;//获得列数
    boolean isAutoIncrement(int column) throws SQLException;
    boolean isCaseSensitive(int column) throws SQLException;
    boolean isSearchable(int column) throws SQLException;
    boolean isCurrency(int column) throws SQLException;
    int isNullable(int column) throws SQLException;
    int columnNoNulls = 0;
    int columnNullable = 1;
    int columnNullableUnknown = 2;
    boolean isSigned(int column) throws SQLException;
    int getColumnDisplaySize(int column) throws SQLException;
    String getColumnLabel(int column) throws SQLException;//获得字段别名
    String getColumnName(int column) throws SQLException;//获得字段名
    String getSchemaName(int column) throws SQLException;
    int getPrecision(int column) throws SQLException;
    int getScale(int column) throws SQLException;
    String getTableName(int column) throws SQLException;
    String getCatalogName(int column) throws SQLException;
    int getColumnType(int column) throws SQLException;
    String getColumnTypeName(int column) throws SQLException;
    boolean isReadOnly(int column) throws SQLException;
    boolean isWritable(int column) throws SQLException;
    boolean isDefinitelyWritable(int column) throws SQLException;
    //--------------------------JDBC 2.0-----------------------------------
    String getColumnClassName(int column) throws SQLException;
}
2、使用LIst<List>封装数据
public static List<List> query(String sql, Object[] params) throws SQLException {
        Connection con=null;
        List<List> tabList;//返回查询结果的表List
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            con = getConn();
            tabList = new ArrayList();//创建表List
            ps = con.prepareStatement(sql);
            setParams(ps,params);
            rs = ps.executeQuery();
            ResultSetMetaData rsmd=rs.getMetaData();//获得结果集的媒体元数据
            int count=rsmd.getColumnCount();//获得结果集的列数
            while(rs.next()){
                List rowList=new ArrayList();//创建行的集合
                for(int i=1;i<=count;i++){
                    rowList.add(rs.getObject(i));//将行中每列的数据加入到rowList中
                }
               tabList.add(rowList);//将加完数据的rowList加入到tabList中
            }
        } finally {
            closeAll(rs,ps,con);
        }
        return tabList;//返回tabList

    }
3、以Map封装数据的代码:
   public static List<Map> queryMap(String sql, Object[] params) throws SQLException {
        Connection con=null;
        List<Map> tabList;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            con = getConn();
            tabList = new ArrayList();
            ps = con.prepareStatement(sql);
            setParams(ps,params);
            rs = ps.executeQuery();
            ResultSetMetaData rsmd=rs.getMetaData();
            int count=rsmd.getColumnCount();
            while(rs.next()){
                //List rowList=new ArrayList();
                Map rowMap=new HashMap();
                for(int i=1;i<=count;i++){
                    rowMap.put(rsmd.getColumnName(i),rs.getObject(i));
                }
                tabList.add(rowMap);
            }
        } finally {
            closeAll(rs,ps,con);
        }
        return tabList;

    }
4、JDBC常见的错误:
com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
数据库服务未启动
java.sql.SQLException: No suitable driver found for jdbc:mysql//localhost:3306/school?serverTimezone=UTC
URL错误
java.sql.SQLSyntaxErrorException: Unknown database 'schoo'
未知的数据库“schoo”
java.sql.SQLException: The server time zone value '???ú±ê×??±??' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
serverTimezone=UTC  写错
java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES)
数据库密码错误
java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'selec * from classes' at line 1

SQL语句错误,要招关键词:near ,就在near的后面出的错
java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1

java.sql.SQLSyntaxErrorException: Table 'school.classs' doesn't exist
数据库表“school.classs ”不存在
java.sql.SQLException: No value specified for parameter 1
sql语句中的第一个参数没有给定参数,一般是没有设置参数

SQLIntegrityConstraintViolationException: Duplicate entry '654321' for key 'order_no'
违反了唯一约束
5、完整BaseDB代码:
public class BaseDB {
	public static String driver="com.mysql.cj.jdbc.Driver";
	public static String url="jdbc:mysql://localhost:3306/school?serverTimezone=UTC";
	public static String user="root";
	public static String  pass="admin";
	
	static {
		try {
			Class.forName(driver);
		} catch (ClassNotFoundException e) {			
			e.printStackTrace();
		}
	}
	
	public static List<Map> queryMap(String sql,Object[] params){
		Connection con = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		List list=new ArrayList();
		try {
			con = getConnection();
			ps = con.prepareStatement(sql);
			setParam(ps,params);
			rs = ps.executeQuery();
			ResultSetMetaData rsmd=rs.getMetaData();
			int count=rsmd.getColumnCount();
			
			while(rs.next()) {
				Map map=new LinkedHashMap();
				for(int i=1;i<=count;i++) {
					map.put(rsmd.getColumnName(i), rs.getObject(i));
					//System.out.print(rs.getObject(i)+"\t");
				}
				list.add(map);
				//System.out.println();
			}
		} catch (SQLException e) {			
			e.printStackTrace();
		}finally {
			closeAll(rs,ps,con);			
		}
		return list;
	}
	
	public static List<List> queryList(String sql,Object[] params){
		Connection con = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		List tabList=new ArrayList();
		try {
			con = getConnection();
			ps = con.prepareStatement(sql);
			setParam(ps,params);
			rs = ps.executeQuery();
			ResultSetMetaData rsmd=rs.getMetaData();
			int count=rsmd.getColumnCount();
			
			while(rs.next()) {				
				List rowList=new ArrayList();
				for(int i=1;i<=count;i++) {
					rowList.add(rs.getObject(i));
				}
				tabList.add(rowList);				
			}
		} catch (SQLException e) {			
			e.printStackTrace();
		}finally {
			closeAll(rs,ps,con);			
		}
		return tabList;
	}
	
	

	public static int update(String sql,Object[] params) {
		Connection con = null;
		PreparedStatement ps = null;
		try {
			con = getConnection();
			ps = con.prepareStatement(sql);
			setParam(ps,params);
			return ps.executeUpdate();
		} catch (SQLException e) {			
			e.printStackTrace();
		}finally {
			closeAll(null,ps,con);			
		}
		return -1;		
	}	
	
	public static Connection getConnection() throws SQLException {
		return DriverManager.getConnection(url, user, pass);
	}	
	
	public static void setParam(PreparedStatement ps,Object[] params) throws SQLException {
		if(params!=null) {
			for(int i=0;i<params.length;i++) {
				ps.setObject(i+1, params[i]);
			}
		}
	}
	
	public static void  closeAll(ResultSet rs,Statement st,Connection con) {
		if(rs!=null) {
			try {
				rs.close();
			} catch (SQLException e) {				
				e.printStackTrace();
			}
		}
		if(st!=null) {
			try {
				st.close();
			} catch (SQLException e) {				
				e.printStackTrace();
			}
		}
		if(con!=null) {
			try {
				con.close();
			} catch (SQLException e) {				
				e.printStackTrace();
			}
		}
	}

}

第二节:DAO模式

0)编写BaseDB。。。

a)定义DAO接口
public interface DeptDao {
    int addDept(Dept dept);
    int updateDept(Dept dept);
    int deleteDept(int deptno);
    Dept findDeptById(int deptno);
    List<Dept> findAllDept();
    List<Dept> dindDeptByName(String name);
}
b)定义实体类
public class Dept {

    private int deptno;
    private String dname;
    private java.sql.Date createTime;
	...
}
c)定义接口实现类
package com.jdbc;

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

/**
 * @author: wangxiangnan
 * @time: 2020/8/11 15:38
 * @description:
 */
public class DeptDaoImpl implements DeptDao {
    @Override
    public int addDept(Dept dept) {
        return 0;
    }

    @Override
    public int updateDept(Dept dept) {
        return 0;
    }

    @Override
    public int deleteDept(int deptno) {
        return 0;
    }

    @Override
    public Dept findDeptById(int deptno) {
        List<Map> list= null;
        try {
            list = MyDB.queryMap("select * from dept where deptno=?",new Object[]{deptno});
        } catch (SQLException e) {
            e.printStackTrace();
        }
        if(list.size()>0){
            Map map=list.get(0);
            Dept dept=new Dept((Integer)map.get("deptno"),
                    (String)map.get("dname"),
                    (java.sql.Date)map.get("createTime"));
            return dept;
        }
        return null;
    }

    @Override
    public List<Dept> findAllDept() {
        List<List> dlist= null;
        try {
            dlist = MyDB.query("select deptno,dname,createTime from dept",null);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        List<Dept> deptList=new ArrayList();
        for(List row:dlist){
            Dept dept=new Dept((Integer)row.get(0),(String)row.get(1),(java.sql.Date)row.get(2));
            deptList.add(dept);
        }
        return deptList;
    }

    @Override
    public List<Dept> dindDeptByName(String name) {

        return null;
    }
}
d)编写测试类
    public static void main(String[] args) throws Exception {
        DeptDao dao=new DeptDaoImpl();
        Dept dept=dao.findDeptById(2);
        System.out.println(dept);

        List<Dept> all=dao.findAllDept();
        System.out.println(all);
    }

如果遇到数据库中的decimal类型字段,只能转换成BigDecimal类型:如果实体类的属性是double类型的,需要如下转换:

((BigDecimal)row.get(1)).doubleValue()
e)通用转换实体方法

DAO实现类中,只要是查询就要用到将tabList中Map或List转换为实体的操作,出现大量重复代码,现在将它提取出来,供所有查询方法调用。

    private Dept transEntity(Map deptMap){
        if(deptMap.size()>0)
            return new Dept((Integer)deptMap.get("deptno"),
                            (String)deptMap.get("dname"),
                            (Date) deptMap.get("createTime"));
        return null;
    }
f)不定参数

参数数组使用起来有些麻烦,可以使用不定参数

不定参数的实质也是一个数组,可以将参数当成数组处理。

package com.jdbc;

import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author: wangxiangnan
 * @time: 2020/8/10 15:47
 * @description:
 */
public class MyDB {
    public static String driver="com.mysql.cj.jdbc.Driver";
    public static String url="jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai";
    public static String user="root";
    public static String pass="admin";
    static{
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConn() throws SQLException {
        return DriverManager.getConnection(url,user,pass);
    }

    public static List<Map> queryMap(String sql, Object... params) throws SQLException {
        Connection con=null;
        List<Map> tabList;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            con = getConn();
            tabList = new ArrayList();
            ps = con.prepareStatement(sql);
            setParams(ps,params);
            rs = ps.executeQuery();
            ResultSetMetaData rsmd=rs.getMetaData();
            int count=rsmd.getColumnCount();
            while(rs.next()){
                //List rowList=new ArrayList();
                Map rowMap=new HashMap();
                for(int i=1;i<=count;i++){
                    rowMap.put(rsmd.getColumnName(i),rs.getObject(i));
                }
                tabList.add(rowMap);
            }
        } finally {
            closeAll(rs,ps,con);
        }
        return tabList;

    }

    public static List<List> query(String sql, Object... params) throws SQLException {
        Connection con=null;
        List<List> tabList;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            con = getConn();
            tabList = new ArrayList();
            ps = con.prepareStatement(sql);
            setParams(ps,params);
            rs = ps.executeQuery();
            ResultSetMetaData rsmd=rs.getMetaData();
            int count=rsmd.getColumnCount();
            while(rs.next()){
                List rowList=new ArrayList();
                for(int i=1;i<=count;i++){
                    rowList.add(rs.getObject(i));
                }
               tabList.add(rowList);
            }
        } finally {
            closeAll(rs,ps,con);
        }
        return tabList;

    }

    public static int update(String sql,Object... params) throws SQLException {
        Connection con = null;
        PreparedStatement ps = null;
        try {
            con = getConn();
            ps = con.prepareStatement(sql);
            setParams(ps,params);
            return ps.executeUpdate();
        } finally {
            closeAll(null,ps,con);
        }

    }

    public static void setParams(PreparedStatement ps,Object... params) throws SQLException {
        if(params!=null){
            for(int i=0;i<params.length;i++){
                ps.setObject(i+1,params[i]);
            }
        }
    }

    public static void closeAll(ResultSet rs, Statement st, Connection con){
        if(rs!=null){
            try {
                rs.close();
                rs=null;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(st!=null){
            try {
                st.close();
                st=null;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(con!=null){
            try {
                con.close();
                con=null;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

这样在具体调用的时候就可以直接使用单个参数了

  @Override
    public int updateDept(Dept dept) throws SQLException {
        String sql="update dept set dname=?,createTime=? where deptno=?";
        return MyDB.update(sql,dept.getDname(),dept.getCreateTime(),dept.getDeptno());
    }

第三节:自增主键及事务

1、自增主键

在自动增长主键的表中插入一条数据,往往需要获得该数据的主键。

    public static int insertGetKey(String sql,Object[] params) throws SQLException {
        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            con=getConn();
            ps = con.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
            setParams(ps,params);
            int count = ps.executeUpdate();
            rs=ps.getGeneratedKeys();
            if(rs.next()){
                return rs.getInt(1);
            }
        }finally{
            closeAll(rs,ps,con);
        }
        return -1;
    }

所有通用的代码:

package com.jdbc;

public class BaseDB {
    public static String driver="com.mysql.cj.jdbc.Driver";
    public static String url="jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai";
    public static String user="root";
    public static String pass="admin";
    static{
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConn() throws SQLException {
        return DriverManager.getConnection(url,user,pass);
    }
  
    public static List<Map> queryMap(String sql, Object... params) throws SQLException {
        Connection con=null;
        List<Map> tabList;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            con = getConn();
            tabList = new ArrayList();
            ps = con.prepareStatement(sql);
            setParams(ps,params);
            rs = ps.executeQuery();
            ResultSetMetaData rsmd=rs.getMetaData();
            int count=rsmd.getColumnCount();
            while(rs.next()){
                //List rowList=new ArrayList();在这基础上修改
                Map rowMap=new HashMap();
                for(int i=1;i<=count;i++){
                    rowMap.put(rsmd.getColumnName(i),rs.getObject(i));
                }
                tabList.add(rowMap);
            }
        } finally {
            closeAll(rs,ps,con);
        }
        return tabList;

    }

    public static List<List> query(String sql, Object... params) throws SQLException {
        Connection con=null;
        List<List> tabList;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            con = getConn();
            tabList = new ArrayList();
            ps = con.prepareStatement(sql);
            setParams(ps,params);
            rs = ps.executeQuery();
            ResultSetMetaData rsmd=rs.getMetaData();
            int count=rsmd.getColumnCount();
            while(rs.next()){
                List rowList=new ArrayList();
                for(int i=1;i<=count;i++){
                    rowList.add(rs.getObject(i));
                }
               tabList.add(rowList);
            }
        } finally {
            closeAll(rs,ps,con);
        }
        return tabList;

    }

    public static int update(String sql,Object... params) throws SQLException {
        Connection con = null;
        PreparedStatement ps = null;
        try {
            con = getConn();
            ps = con.prepareStatement(sql);
            setParams(ps,params);
            return ps.executeUpdate();
        } finally {
            closeAll(null,ps,con);
        }

    }

    public static void setParams(PreparedStatement ps,Object... params) throws SQLException {
        if(params!=null){
            for(int i=0;i<params.length;i++){
                ps.setObject(i+1,params[i]);//不定参数也是数组
            }
        }
    }

    public static void closeAll(ResultSet rs, Statement st, Connection con){
        if(rs!=null){
            try {
                rs.close();
                rs=null;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(st!=null){
            try {
                st.close();
                st=null;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(con!=null){
            try {
                con.close();
                con=null;
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

示例:订单录入

测试类代码:

public class OrderMain {
    public static void main(String[] args) throws SQLException {
        String sql="insert into orders values(null,?,?,?)";
        int orderid=MyDB.insertGetKey(sql,"202008131235","京东平台", Date.valueOf("2020-08-11"));
        System.out.println(orderid);
        List<Object[]> plist=new ArrayList();//相当于外面传过来的参数
        plist.add(new Object[]{orderid,"华为手机",200,4567.0});
        plist.add(new Object[]{orderid,"小米手机",100,2567.0});
        plist.add(new Object[]{orderid,"苹果手机",300,6567.0});
        plist.add(new Object[]{orderid,"OPPO手机",500,3567.0});
        sql="insert into order_detail values(null,?,?,?,?)";
        for(Object[] params:plist){
            MyDB.update(sql,params);
        }
    }
}

Orders:

public class Orders {
    private int orderId;
    private String orderNO;
    private String customer;
    private Date orderDate;
...
}

OrderDetail

public class OrderDetail {
    private int detailId;
    private int orderId;
    private String goodsName;
    private int count;
    private double price;
    ...
}

OrderDao

public interface OrderDao {
    int addOrder(Orders order);
    List<Orders> findAllOrder();
}

OrderDaoImpl

public class OrdersDaoImpl implements OrderDao {
    @Override
    public int addOrder(Orders order) {
        String sql="insert into orders values(null,?,?,?)";
        return 0;
    }

    @Override
    public List<Orders> findAllOrder() {
        return null;
    }
}

OrderDetailDao

public interface OrderDetailDao {
    int addOrderDetail(OrderDetail od);
    List<OrderDetail> findAllOrderDetail();
}

OrderDetailDaoImpl

public class OrderDetailImpl implements  OrderDetailDao {
    @Override
    public int addOrderDetail(OrderDetail od) {
        String sql="insert into order_detail values(null,?,?,?,?)";
        return 0;
    }

    @Override
    public List<OrderDetail> findAllOrderDetail() {
        return null;
    }
}

测试类

public class JDBCMain {
    public static void main(String[] args) throws Exception {
        OrderDao od=new OrdersDaoImpl();
        OrderDetailDao odd=new OrderDetailImpl();

       int orderid=od.addOrder(new Orders("654321","淘宝商家1",Date.valueOf("2020-11-25")));
        List<Object[]> plist=new ArrayList();
        plist.add(new Object[]{orderid,"华为手机",200,4567.0});
        plist.add(new Object[]{orderid,"小米手机",100,2567.0});
        plist.add(new Object[]{orderid,"苹果手机",300,6567.0});
        plist.add(new Object[]{orderid,"OPPO手机",500,3567.0});

        for(Object[] params:plist){
            odd.addOrderDetail(
                    new OrderDetail((Integer)params[0],
                            (String)params[1],
                            (Integer)params[2],
                            (Double)params[3]));
        }
        System.out.println(od.findAllOrder());
        System.out.println(odd.findAllOrderDetail());
    }
2、事务处理
默认情况下,连接Connection是处于自动提交模式。

在自动提交模式下,每个SQL更新语句(insert,update,delete)成功执行完后就会自动提交到数据库中。

关闭自动提交模式

为了将多个数据库更新组合成一组更新,我们需要将自动提交模式关闭(使用setAutoCommit(boolean auto)方法)。

一旦关闭了自动提交模式,每个SQL语句都是一个事务的一部分,为使事务对数据库产生永久效果,需要使用 commit()方法来显式地进行提交。

在自动提交关闭后,不成功的提交会导致数据库进行隐式的回滚,所有的更新都会丢失。

也可以调用rollback();

public void trans(){
   Connection con=null;
   PreparedStatement pstmt=null;
   try {
      con=getConnection();
      //con.setAutoCommit(false);
      String sql1="insert into student values(?,?,?)";
      pstmt=con.prepareStatement(sql1);
      pstmt.setString(1,"孙悟空");
      pstmt.setString(2, "男");
      pstmt.setDate(3, java.sql.Date.valueOf("1953-5-13"));
      pstmt.executeUpdate();
      System.out.println("第一次录入成功");
      pstmt.setString(1,"观世音");
      pstmt.setString(2, "女");
      pstmt.setDate(3, java.sql.Date.valueOf("175300-5-13"));
      pstmt.executeUpdate();
      System.out.println("第二次录入成功");
      //con.commit();
   } catch (SQLException e) {
   	//con.rollback();
   }
}

JDBC的批处理
   private static void batchExecute() throws SQLException {
        long starttime=System.currentTimeMillis();
        System.out.println("开始:"+starttime);
        Connection con= BaseDB.getConnection(false);
        String sql="insert tb_test values(null,?,?,?)";
        PreparedStatement ps=con.prepareStatement(sql);
        for(int i=0;i<100006;i++){
            BaseDB.setParams(ps,new Object[]{"李"+i,i%100,Math.random()*100});
            ps.addBatch();
            if(i%1000==0){
                ps.executeBatch();
                ps.clearBatch();
                System.out.println(System.currentTimeMillis());
            }
        }
        ps.executeBatch();

        BaseDB.closeAll(null,ps,con);
        long endtime=System.currentTimeMillis();
        System.out.println("结束"+endtime);
        System.out.println("程序花费时间:" + (endtime-starttime)/1000 + "秒!!");
    }

第九章:项目案例

第一节:自动主键的获得

public static void main(String[] args) throws Exception{		
		//1.注册驱动
		String driver="com.mysql.cj.jdbc.Driver";
		String url="jdbc:mysql://127.0.0.1:3306/school?serverTimezone=UTC";
		
		//2.获得连接
		//3、获得语句对象		
		Class.forName(driver);
		Connection con = DriverManager.getConnection(url, "root", "admin");			
		//4、执行语句
		String cla="A777";
		java.util.Date date=new java.util.Date();
		String sql="insert into classes(className,createDate) values(?,?)";
		PreparedStatement st = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);			
		setParam(st,new Object[] {cla,date});		
		int count=st.executeUpdate();	
		ResultSet rs=st.getGeneratedKeys();
		if(rs.next()) {
			int key=rs.getInt(1);
			System.out.println("主键:"+key);
		}
			st.close();
			con.close();			
		}
public static int insertGetKey(String sql,Object[] params) {
		Connection con = null;
		PreparedStatement pstmt = null;		
		try {
			con = getConnection();
			pstmt = con.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
			setParam(pstmt,params);
			pstmt.executeUpdate();	
			ResultSet rs=pstmt.getGeneratedKeys();
			if(rs.next()) {
				System.out.println("主键:"+rs.getInt(1));
				return rs.getInt(1);
			}
		} catch (SQLException e) {			
			e.printStackTrace();
		} finally {
			closeAll(null,pstmt,con);
		}
		return -1;
	}
订单业务:

数据库表格:

-- ----------------------------
-- Table structure for `orders`
-- ----------------------------
DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders` (
  `orderId` int(11) NOT NULL AUTO_INCREMENT,
  `orderDate` date DEFAULT NULL,
  `orderAddress` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`orderId`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

CREATE TABLE `orderdetail` (
  `detailId` int(11) NOT NULL AUTO_INCREMENT,
  `shopName` varchar(20) DEFAULT NULL,
  `shopPrice` double DEFAULT NULL,
  `shopCount` double DEFAULT NULL,
  `fk_orderId` int(11) DEFAULT NULL,
  PRIMARY KEY (`detailId`),
  KEY `fk_orderId` (`fk_orderId`),
  CONSTRAINT `orderdetail_ibfk_1` FOREIGN KEY (`fk_orderId`) REFERENCES `orders` (`orderid`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;


public static void main(String[] args) {
		String sql="insert into orders values(null,?,?)";
		int key=BaseDB.insertGetKey(sql, new Object[] { new Date(),"AAA软件学院4号楼"});
		sql="insert into orderdetail values(null,?,?,?,?)";
		BaseDB.update(sql, new Object[] {"华为9001",8900.8,3,key});
		BaseDB.update(sql, new Object[] {"小米8848",1900.8,2,key});
		System.out.println("key===="+key);

	}

第二节:事务管理

public static void main(String[] args){		
		//1.注册驱动
		String driver="com.mysql.cj.jdbc.Driver";
		String url="jdbc:mysql://127.0.0.1:3306/school?serverTimezone=UTC";		
		//2.获得连接
		//3、获得语句对象		
		Connection con = null;
		PreparedStatement st = null;
		try {
			Class.forName(driver);
			con = DriverManager.getConnection(url, "root", "admin");	
			//4、执行语句
			con.setAutoCommit(false);//设置自动提交模式为false
			java.util.Date date=new java.util.Date();
			String sql="insert into classes(className,createDate) values(?,?)";
			st = con.prepareStatement(sql);			
			setParam(st,new Object[] {"A000",date});			
			st.executeUpdate();
			setParam(st,new Object[] {"A009",date});		
			st.executeUpdate();	
			setParam(st,new Object[] {"A9900","2019-5-12"});			
			st.executeUpdate();	
			con.commit();//提交数据库操作
		} catch (ClassNotFoundException e) {			
			e.printStackTrace();
		} catch (SQLException e) {
			try {
				con.rollback();//如果出错则回滚事务
			} catch (SQLException e1) {
				
				e1.printStackTrace();
			}
			e.printStackTrace();
		}finally {
			try {
				st.close();
				con.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}			
			
		}
		}

第三节:BBS论坛

1、数据库表结构



SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for `board`
-- ----------------------------
DROP TABLE IF EXISTS `board`;
CREATE TABLE `board` (
  `bid` int(11) NOT NULL AUTO_INCREMENT,
  `bname` varchar(50) NOT NULL,
  `state` int(11) DEFAULT NULL,
  PRIMARY KEY (`bid`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of board
-- ----------------------------
INSERT INTO `board` VALUES ('1', 'Python板块', '1');
INSERT INTO `board` VALUES ('2', 'java板块', '1');
INSERT INTO `board` VALUES ('3', 'C#板块', '1');

-- ----------------------------
-- Table structure for `reply`
-- ----------------------------
DROP TABLE IF EXISTS `reply`;
CREATE TABLE `reply` (
  `rid` int(11) NOT NULL AUTO_INCREMENT,
  `context` varchar(1000) NOT NULL,
  `ptime` datetime NOT NULL,
  `fk_uid` int(11) NOT NULL,
  `fk_tid` int(11) NOT NULL,
  PRIMARY KEY (`rid`),
  KEY `fk_uid` (`fk_uid`),
  KEY `fk_tid` (`fk_tid`),
  CONSTRAINT `reply_ibfk_1` FOREIGN KEY (`fk_uid`) REFERENCES `userinfo` (`uid`),
  CONSTRAINT `reply_ibfk_2` FOREIGN KEY (`fk_tid`) REFERENCES `topic` (`tid`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of reply
-- ----------------------------
INSERT INTO `reply` VALUES ('2', '这才是微软的救星啊', '2019-06-24 03:19:24', '2', '7');
INSERT INTO `reply` VALUES ('3', '从入门到放弃', '2019-06-24 03:45:06', '2', '4');
INSERT INTO `reply` VALUES ('4', '你净瞎说', '2019-06-24 03:59:54', '2', '4');
INSERT INTO `reply` VALUES ('5', '能精通么?', '2019-06-24 04:00:18', '2', '4');

-- ----------------------------
-- Table structure for `topic`
-- ----------------------------
DROP TABLE IF EXISTS `topic`;
CREATE TABLE `topic` (
  `tid` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(30) NOT NULL,
  `context` varchar(1000) NOT NULL,
  `ptime` datetime NOT NULL,
  `fk_uid` int(11) NOT NULL,
  `fk_bid` int(11) NOT NULL,
  PRIMARY KEY (`tid`),
  KEY `fk_uid` (`fk_uid`),
  KEY `fk_bid` (`fk_bid`),
  CONSTRAINT `topic_ibfk_1` FOREIGN KEY (`fk_uid`) REFERENCES `userinfo` (`uid`),
  CONSTRAINT `topic_ibfk_2` FOREIGN KEY (`fk_bid`) REFERENCES `board` (`bid`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of topic
-- ----------------------------
INSERT INTO `topic` VALUES ('3', 'Python入门', '人生苦短、我用Python', '2019-06-20 08:24:07', '3', '1');
INSERT INTO `topic` VALUES ('4', 'java从入门到精通', '这是一个永恒的话题', '2019-06-24 02:37:16', '2', '2');
INSERT INTO `topic` VALUES ('5', 'java框架技术', 'spring+Mybatis项目整合', '2019-06-24 02:43:13', '2', '2');
INSERT INTO `topic` VALUES ('6', 'Python的发展', '自从1994年那个漆黑的夜晚,一个荷兰人开启了无聊模式', '2019-06-24 02:52:26', '2', '1');
INSERT INTO `topic` VALUES ('7', 'C#由来', 'Delphi的设计师主持了C#的编写', '2019-06-24 02:53:40', '2', '3');
INSERT INTO `topic` VALUES ('8', 'JDBC入门', 'JDBC的API简介:DriverManager;Connection', '2019-06-24 04:35:10', '2', '2');
INSERT INTO `topic` VALUES ('9', '蟒蛇的前世今生', '杭州有个西湖,镇江有个金山寺,两者什么关系呢,这得从白蛇说起', '2019-06-24 04:39:45', '2', '1');

-- ----------------------------
-- Table structure for `userinfo`
-- ----------------------------
DROP TABLE IF EXISTS `userinfo`;
CREATE TABLE `userinfo` (
  `uid` int(11) NOT NULL AUTO_INCREMENT,
  `uname` varchar(20) NOT NULL,
  `upass` varchar(10) NOT NULL,
  `state` int(11) NOT NULL,
  `flag` int(11) NOT NULL,
  PRIMARY KEY (`uid`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of userinfo
-- ----------------------------
INSERT INTO `userinfo` VALUES ('1', '西门庆', '1234', '0', '0');
INSERT INTO `userinfo` VALUES ('2', '武大郎', '1234', '1', '0');
INSERT INTO `userinfo` VALUES ('3', 'admin', '1234', '1', '1');

2、项目案例的目录结构

1561425443883.png

3、com.util.BaseDB数据库通用类


public class BaseDB{
	static String driver="com.mysql.cj.jdbc.Driver";
	static String url="jdbc:mysql://localhost:3306/bbs?serverTimezone=UTC";
	static String user="root";
	static String pass="admin";
	static {
		try {
			Class.forName(driver);
		} catch (ClassNotFoundException e) {			
			e.printStackTrace();
		}
	}
	public Connection getConnection() throws SQLException{
		return DriverManager.getConnection(url,user,pass);
				
	}
	
	public ArrayList queryList(String sql,Object[] params) {
		Connection con = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		ArrayList allData = new ArrayList();
		try {
			con = getConnection();
			pstmt = con.prepareStatement(sql);
			setParams(pstmt,params);
			rs = pstmt.executeQuery();
			int colCount = rs.getMetaData().getColumnCount();
			while (rs.next()) {
				ArrayList rowData = new ArrayList();
				for (int i = 1; i <= colCount; i++) {
					rowData.add(rs.getObject(i));
				}
				allData.add(rowData);
			}
		} catch (SQLException e) {			
			e.printStackTrace();
		} finally {
			closeAll(rs,pstmt,con);
		}
		return allData;
	}
	
	public  List<Map> queryMap(String sql,Object[] params) {
		Connection con = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		ArrayList allData = new ArrayList();
		try {
			con = getConnection();
			pstmt = con.prepareStatement(sql);
			setParams(pstmt,params);
			rs = pstmt.executeQuery();
			int colCount = rs.getMetaData().getColumnCount();
			while (rs.next()) {
				Map map=new LinkedHashMap();
				for (int i = 1; i <= colCount; i++) {
					map.put(rs.getMetaData().getColumnName(i),rs.getObject(i));
				}
				allData.add(map);
			}
		} catch (SQLException e) {			
			e.printStackTrace();
		} finally {
			closeAll(rs,pstmt,con);
		}
		return allData;
	}
	
	
	public int update(String sql,Object[] params) {
		Connection con = null;
		PreparedStatement pstmt = null;		
		try {
			con = getConnection();
			pstmt = con.prepareStatement(sql);
			setParams(pstmt,params);
			return pstmt.executeUpdate();			
		} catch (SQLException e) {			
			e.printStackTrace();
		} finally {
			closeAll(null,pstmt,con);
		}
		return 0;
	}
	
	public void closeAll(ResultSet rs,Statement stmt,Connection con){
		try {
			if(rs!=null){
				rs.close();
			}
			if(stmt!=null){
				stmt.close();
			}
			if(con!=null){
				con.close();
			}
		} catch (SQLException e) {		
			e.printStackTrace();
		}
	}
	
	public void setParams(PreparedStatement ps,Object[] params)throws SQLException{
		if(params==null) return;
    	for(int i=0;i<params.length;i++){
    		Object v=params[i];
    		ps.setObject(i+1,v);
    	}
    }		
}

4、项目案例中的通用类,用来存贮当前登录的用户信息,当前用户信息为了每个模块测试方便先设定一个管理员身份的用户,如果测试一般用户的话可以从新赋值,正常情况下应该是用户在登录成功后给它来赋值的。通用的打印List<Map>的方法


/**
 * 该类用来存储一些在各个地方都要使用到的信息
 * @author Justice
 *
 */
public class UtilHelper {
	/**
	 * 用来标记当前登录的用户,为测试方便先假定是管理员,系统正常运行时是在登录成功后将登录用户赋值给crrUser的
	 */
	public static Userinfo currUser=new Userinfo(3,"admin",1);	
	
	/**
	 * 将List<Map>的数据展示到控制台
	 * @param list<Map>:存放查询数据的列表
	 *	@param bool:输出时是否打印列名
	 */
	public static void showList(List<Map> list,boolean bool,int grade) {		
		for(int i=0;i<grade;i++) {//按照输出的级别添加不同数量的\t
			System.out.print("\t");
		}
		if(list.size()>0) {
			if(bool) {
				for(Object key:list.get(0).keySet()) {
					System.out.print(key+"\t");
				}
			}
			System.out.println();
			for(Map map:list) {
				for(Object key:map.keySet()) {
					System.out.print(map.get(key)+"\t\t");
				}
				System.out.println();
			}
		}else {			
			System.out.println("没有找到你要的内容");
		}
	}
}

5、DAO层接口

BoardDao,UserDao,TopicDao,ReplyDao

以BoardDao为例

public interface BoardDao {
	List<Map> findAllBoard();
	Board findBorderByBname(String bname);	
	int addBoard(String boardName);
	int updateBoard(int bid,String boardName);
	int deleteBoard(int bid);
}

6、DAO接口实现类:BoardDaoImpl,该类仅仅实现了单个库表的操作

public class BoardDaoImpl implements BoardDao {
	BaseDB base=new BaseDB();
	@Override
	/**
	 * 查找所有的板块
	 * 返回的集合类型是List<Map>
	 */
	public List<Map> findAllBoard() {
		String sql="select * from board";
		return base.queryMap(sql, null);		
	}

	@Override
	public Board findBorderByBname(String bname) {
		String sql="select * from board where bname =?";
		List<Map> list= base.queryMap(sql, new Object[] {bname});
		Board board=null;
		if(list.size()>0) {
			Map map=list.get(0);
			board=new Board((Integer)map.get("bid"),map.get("bname").toString());
			
		}
		return board;
	}

	@Override
	public int addBoard(String boardName) {
		String sql="insert into board values(null,?,?)";
		return base.update(sql, new Object[] {boardName,1});
	}

	/**
	 * 根据板块id来修改板块的名称
	 * @param:boardName: 要修改的板块的新名称
	 */
	@Override	
	public int updateBoard(int bid,String boardName) {
		String sql="update board set bname=? where bid=?";
		return base.update(sql, new Object[] {boardName,bid});
	}

	/**
	 *该方法不是真正的删除,只是将板块的状态改为0(不可用)
	 */
	@Override
	public int deleteBoard(int bid) {
		String sql="update board set state=0 where bid="+bid;
		return base.update(sql, null);
	}

}

7、业务层实现,该层主要任务是业务实现,结合控制台的输入输出来完成具体的业务,结合每个实体单元的菜单导航业务。


public class BoardService {
	static BoardDao bd=new BoardDaoImpl();
	static Scanner sc=new Scanner(System.in);
	public void boardMenu() {
		while(true) {
			System.out.println("板块管理");
			System.out.println("---------------------");
			System.out.println("1.所有板块\t 2.添加板块\t 3.修改版块\t 4.删除板块\t 5.退出板块管理");
			System.out.println("---------------------");
			switch(sc.nextInt()) {
			case 1:
				showAllBoard();
				break;
			case 2:
				addBoard();
				break;
			case 3:
				updateBoard();
				break;
			case 4:
				deleteBoard();
				break;
			case 5:
				System.out.println("退出板块管理");
				return;
			}
		}
	}
	
	/**
	 * 将所有板块展示出来仅供浏览
	 */
	public void showAllBoard() {
		List<Map> list=bd.findAllBoard();
		UtilHelper.showList(list,true,0);
	}
	/**
	 * 根据输入的板块名称判断是否已经存在重名的板块,如果不存在重名则添加新板块
	 */
	private void addBoard() {
		System.out.println("请输入板块名称");
		String bname=sc.next();
		Board board=bd.findBorderByBname(bname);
		if(board!=null) {
			System.out.println("板块名称已经存在");
			return;
		}
		int count=bd.addBoard(bname);
		if(count>0) {
			System.out.println(bname+"板块添加完毕");
		}
	}
	
	/**
	 * 根据输入的板块id将该板块名称修改为新输入的名称
	 */
	private void updateBoard() {
		showAllBoard();
		System.out.println("请输入要修改的板块编号");
		int bid=sc.nextInt();
		System.out.println("请输入要修改的板块名称");
		String bname=sc.next();		
		int count=bd.updateBoard(bid,bname);
		if(count>0) {
			System.out.println(bname+"板块修改完毕");
		}
		if(count==0) {
			System.out.println(bname+"板块没有找到");
		}
	}
	
	/**
	 * 这里的删除仅仅是将板块的state设为0,即标记为不可用
	 */
	private void deleteBoard() {
		showAllBoard();
		System.out.println("请输入要删除的板块编号");
		int bid=sc.nextInt();			
		int count=bd.deleteBoard(bid);
		if(count>0) {
			System.out.println("板块删除完毕");
		}
		if(count==0) {
			System.out.println("没有找到删除的板块");
		}
	}
}

8、为了导航各种模块功能,需要一个导航菜单类,将每个模块的功能集中一起,形成导航菜单

/**
 * 该类用来完成将各个模块功能汇总起来并进行导航,有三个模块菜单
 * 1、登录、注册菜单  2、管理员菜单  3、一般用户菜单
 * @author Justice
 *
 */
public class Menu {
	static Scanner sc=new Scanner(System.in);
	static UserService us=new UserService();
	static BoardService bs=new BoardService();
	static TopicService ts=new TopicService();
	static ReplyService rs=new ReplyService();
	/**
	 * 用来进行登录和注册操作,只有正常登录后才能进入BBS系统,这是整个BBS系统的入口
	 */
	public static void loginMenu() {
		System.out.println("欢迎登录BBS");
		System.out.println("-------------------------------------");
		System.out.println("1.登录\t 2.注册\t 3.退出");
		System.out.println("-------------------------------------");
		switch(sc.nextInt()) {
		case 1://登录操作
			int userType=us.userLogin();
			if(userType==1) {
				managerMenu(UtilHelper.currUser);
			}
			if(userType==0) {
				userMenu(UtilHelper.currUser);
			}
			if(userType==-1) {
				loginMenu();
			}
			break;
		case 2://注册操作
			int rcount=us.userRegist();
			if(rcount==1) {
				loginMenu();
			}
			break;
		case 3:
			System.out.println("您已退出BBS系统");			
		}
	}
	
	/**
	 * 管理员菜单,导航模块有:1.用户管理 2.板块管理 3.主贴管理 4.回复管理 
	 * @param user
	 */
	public static void managerMenu(Userinfo user) {
		while(true) {
			System.out.println("欢迎你"+user.getUname()+"进入BBS后台管理系统");
			System.out.println("-------------------------------------");
			System.out.println("1.用户管理\t 2.板块管理\t 3.主贴管理\t 4.回复管理\t 5.退出系统");
			System.out.println("-------------------------------------");
			switch(sc.nextInt()) {
			case 1:
				us.userManagerMenu();
				break;
			case 2:
				bs.boardMenu();
				break;
			case 3:
				ts.topicMenu();
				break;
			case 4:
				rs.replyMenu();
				break;
			case 5:
				System.out.println("进入统计管理");
				break;
			case 6:
				System.out.println("退出系统");
				return;
			}
		}
	}
	
	/**
	 * 一般用户导航,只有两个功能模块:1.主贴  2.回复
	 * @param user
	 */
	public static void userMenu(Userinfo user) {
		while(true) {			
			System.out.println("欢迎你"+user.getUname()+"进入BBS系统");
			System.out.println("-------------------------------------");
			System.out.println("1.主贴\t  2.回复\t  3.退出系统");
			System.out.println("-------------------------------------");
			int sid=sc.nextInt();
			switch(sid) {
			case 1:
				ts.topicMenu();
				break;
			case 2:
				rs.replyMenu();
				break;
			case 3:
				System.out.println("退出系统");
				return;				
			}		
		}
	}

}