1.设计模式:
结构型设计模式:代理模式
静态代理
动态代理
代理定义:代理角色帮助真是角色完成一些事情.
静态代理:
通过代理角色帮助真实角色完成一些事情,真实角色只专注于自己的事情.
前提条件:代理角色和真实角色必须是同一个接口.
举例:结婚这件事情
真实角色:you
代理角色:WeddingCompany:婚庆公司
//测试类
public class ProxyDemo {
public static void main(String[] args) {
//没有使用静态代理之前
//接口多态
//Mary mary = new You() ;
//mary.mary() ;
//具体类
You y = new You() ; // MyRunnable my = new MyRunnable() ;
y.mary() ;
System.out.println("---------------------------------") ;
//加入代理
WeddingCompany weddingCompany = new WeddingCompany(y) ; //Thread t = new Thread(my) ;
weddingCompany.mary() ;
}
}
//接口:结婚接口
interface Mary{
void mary() ;//结婚
}
//真实角色:You
class You implements Mary{
@Override
public void mary() {
System.out.println("要结婚了,很开心...") ;
}
}
//代理角色: 目的:对你mary方法进行增强 要实现结婚的接口
class WeddingCompany implements Mary{
private You you ;
public WeddingCompany(You you){
this.you = you ;
}
@Override
public void mary() {
System.out.println("结婚之前,布置婚礼现场...");
you.mary() ;//真实角色专注于自己的事情
System.out.println("结婚之后,婚庆公司需要结算尾款...");
}
}
2.等待唤醒机制:解决死锁问题代码进行优化 ("信号灯法")
1)将数据:Student类的成员加入私有化
2)在Student类中提供set方法,对学生数据进行赋值,加入同步方法
3)在生产者资源类中,只需要调用当前set方法进行赋值
4)在Student类中提供get方法,获取学生数据,加入同步方法
5)在GetThread:消费者资源类中,调用get方法即可!
举例:
public class ThreadDemo {
public static void main(String[] args) {
//创建一个学生对象
Student s = new Student() ;
//创建生产者资源类对象
SetThread st = new SetThread(s) ;
//创建消费者资源类对象s
GetThread gt = new GetThread(s) ;
//创建线程类对象
Thread t1 = new Thread(st) ;
Thread t2 = new Thread(gt) ;
//启动线程
t1.start() ;
t2.start() ;
}
}
public class Student {
private String name ; //姓名
private int age ;//年龄
private boolean flag ; //默认没有数据,通过这标记信号:表示是否存在数据
/**
给学生数据进行赋值
@param name 姓名
@param age 年龄
*/
public synchronized void set(String name,int age){
//如果方法一进来就是一个同步代码块,需要将synchronized定义在方法声明上,锁对象this
//判断:
//如果当前生产者没有数据,先等待产生数据
if (this.flag) {
try {
this.wait(); //wait()方法为什么定义Object类中? 因为锁对象可以是任意的Java类对象 (包括Object)
// //wait()一旦被调用,会立即释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//赋值
this.name = name ;
this.age = age ;
//改变标记:有数据了
//改变标记
this.flag = true ;
//调用通知对方:唤醒对方线程
this.notify() ;
}
/**
获取学生数据
*/
public synchronized void get(){
• //如果当前消费者存在数据
• if (!this.flag) {
• //等待将产生的数据消费掉
• try {
• this.wait();
• } catch (InterruptedException e) {
• e.printStackTrace();
• }
• }
• System.out.println(this.name + "---" + this.age);
//改变标记
this.flag= false ;
//通知生成者线程,产生数据
this.notify() ;
}
}
public class SetThread implements Runnable {
//Student s = new Student() ;
private Student s ; //声明学生变量s
public SetThread(Student s){
this.s = s ;
}
//统计变量
private int x = 0 ;
@Override
public void run() {
while(true){
if(x % 2 ==0){
s.set("高圆圆",42);
}else {
s.set("赵又廷",45);
}
x++ ;//原子性操作
}
}
}
public class GetThread implements Runnable {
//声明学生变量s
private Student s ;
public GetThread(Student s) {
this.s = s ;
}
//Student s= new Student() ;
@Override
public void run() {
//使用学生数据
//System.out.println(s.name+"-"+s.age) ;
//不断的去使用数据
while(true){
s.get();
}
}
}
3.线程池
工厂类:里面有一个静态方法:
public static ExecutorService newFixedThreadPool(int nThreads):创建一个固定的可重复的线程数的线程池
ExecutorService:接口,它可以进行多个异步任务的执行
举例:
public class ThreadPoolTest {
public static void main(String[] args) {
//public static ExecutorService newFixedThreadPool(int nThreads)
//创建线程池
ExecutorService threadPool = Executors.newFixedThreadPool(2);
//<T> Future<T> submit(Callable<T> task)
//接口多态
Callable callable = new MyCallable() ;
//两个线程分别执行异步任务
threadPool.submit(callable) ;
threadPool.submit(callable) ;
//使用完毕,关闭线程池
//线程需要归还到线程池中
//void shutdown()
threadPool.shutdown() ;
}
}
public class MyCallable implements Callable {
//不计算结果,仅仅展示:多个线程并发执行
@Override
public Object call() throws Exception {
for(int x = 0 ; x < 100 ; x ++){
System.out.println(Thread.currentThread().getName()+":"+x);
}
return null;
}
}
4.wait()和notify()为什么定义在Object类中,不定义在Thread类中
wait()和notify():代表线程处于等待或者线程唤醒,他们的调用是要通过同步机制中的锁对象来访问的,(锁对象可以是任意的Java类对象)
当前某个线程持有锁的时候,调用wait(),处于等待过程,但是立即释放锁,然后可以通过锁对象.notify()唤醒对方线程,解决死锁问题;
等待唤醒机制:
生产者线程不断的产生数据,没有数据了,等待先产生数据,有数据了之后,需要通知消费者线程来使用数据;
消费者线程不断的使用数据,有数据了,先等待使用数据,当数据使用完毕,没有数据了,需要通知生产者线程产生数据;
5.sleep()和wait()方法的区别 (面试题)
1)来源不同
sleep()来自于Thread类中,表示线程睡眠 sleep(long time):时间毫秒值
wait()来自于Object类中,表示线程等待,需要被锁对象来访问
2)是否会释放锁
sleep(long time):属于一个普通方法,调用该方法,不会去释放锁,只是导致线处于睡眠(线程阻塞),当线程的睡眠时间到了,就继续执行线程;
wait():属于锁方法,被锁对象访问之后,会立即释放锁,才能够使用同步等待唤醒机制解决死锁问题,通过锁对象调用notify(),唤醒对方线程;
3)这两个方法都会抛出异常(中断异常),都是属于本地方法,底层非Java语言实现
当线程睡眠过程中,睡眠还没到,导致的睡眠状态被打断,就会出现InterruptedException
当线程处于等待状态,被中断,就抛出这个异常!
public static native void sleep(long millis) throws InterruptedException
public final native void wait(long timeout) throws InterruptedException;
6.Lock接口
比synchronized具有扩展性的功能
子实现类ReetrantLock:可重入的互斥锁
public void lock()获取锁
public void unlock()释放锁
处理异常:try...cath...finally.
throws:抛出异常:抛出在方法声明上;
举例:模拟电影院三个窗口买100张票
package com.qf.jhq;
/**
* className:LockTest
* package:com.qf.jhq
* date2021/10/26 19:10
* Author:ly
*/
public class LockTest {
public static void main(String[] args) {
SellTick s=new SellTick();
Thread t1=new Thread(s,"窗口1");
Thread t2=new Thread(s,"窗口2");
Thread t3=new Thread(s,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
package com.qf.jhq;
import java.util.concurrent.locks.ReentrantLock;
/**
* className: SellTick
* package: com.qf.jhq
* date 2021/10/26 19:12
* Author: ly
*/
public class SellTick implements Runnable{
//电影票数
private static int ticks=100;
// 无参构造私有化;
SellTick(){}
// 创建Lock锁
ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
//模拟一直有票
while (true) {
lock.lock();//调用锁方法
try {
if(ticks>0){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在售卖"+(ticks--)+"票");
}
}finally {
lock.unlock();
}
}
}
}
7.Timer
java.util.Timer:javaSE的定时器:处理一次或者重复执行的任务(TimerTask:抽象类)
构造方法:
public Timer():创建一个新的定时器,不是守护线程
成员方法:
public void cancel():取消定时器
public void schedule(TimerTask task,long delay):经过多少毫秒后执行一次定时任务
public void schedule(TimerTask task,long delay,long period):经过delay毫秒后指向定时任务,每经过period重复执行定时任务
public void schedule(TimerTask task,Date time):在指定时间的时候(精确到毫秒)执行定时任务.
TimerTask:抽象类,不能实例化:自定义子类或者匿名内部类
public abstract void run()定时器要执行的操作.
8.设计模式
8.1简单工厂模式
也称为"静态工厂方法模式",主要的作用是负责某些类的实例的创建过程.
优点:
通过工厂类完成各个类的对象的创建
缺点:
当有新的类行增加,需要不断的修稿工厂类,维护性难度大.
举例:动物,猫,狗
package factory;
/**
* className: SimpleFactory
* package: factory
* date 2021/10/26 20:09
* Author: ly
*/
public class SimpleFactory {
public static void main(String[] args) {
//优化前
// Cat c = AnimalFactory.creatCat();
// c.eat();
// c.sleep();
// Dog d = AnimalFactory.creatDog();
// d.eat();
// d.sleep();
//优化后
Animal a = AnimalFactory.creatAnimal(new Cat());
if(a!=null){
a.eat();
a.sleep();
}
Animal a2 = AnimalFactory.creatAnimal(new Dog());
if(a2!=null){
a2.sleep();
a2.eat();
}
}
}
public abstract class Animal {
public abstract void eat();
public abstract void sleep();
}
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫喜欢吃鱼");
}
@Override
public void sleep() {
System.out.println("猫喜欢躺着睡");
}
}
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗喜欢啃骨头");
}
@Override
public void sleep() {
System.out.println("狗喜欢趴着睡");
}
}package factory;
/**
* className: AnimalFactory
* package: factory
* date 2021/10/26 20:12
* Author: ly
*/
public class AnimalFactory {
//无参构造私有化
public AnimalFactory() {
}
//用方法创建默猫的对象
// public static Cat creatCat(){
// return new Cat();
// }
//调用方法创建狗的对象
// public static Dog creatDog(){
// return new Dog();
// }
//优化后
//调用方法创建动物的对象
public static Animal creatAnimal(Object o) {//向上转型
if (o instanceof Cat) {//判断当前类是不是猫的对象
return new Cat();
} else if (o instanceof Dog) {//判断当前类是不是狗的对象
return new Dog();
}
return null;
}
8.2工厂方法模式
工厂方法模式中抽象工厂类负责定义创建对象的接口,具体对象的创建工作由继承抽象工厂的具体类实现.
优点:
集体的工厂类负责创建当前具体的实例,结构层次很清晰,有具体类/工厂类/抽象类接口;
缺点:
有新的类型增加,需要编写外外的代码,所有代码增加,导致工作也增加了
举例 猫 狗
package factory1;
/**
* className: Factory
* package: factory1
* date 2021/10/26 20:40
* Author: ly
*/
public class Factory {
public static void main(String[] args) {
FactoryMethod d=new DogFactory();
d.creatAnimal().eat();
d.creatAnimal().sleep();
d=new CatFactory();
d.creatAnimal().sleep();
d.creatAnimal().eat();
}
}
public interface FactoryMethod {
Animal creatAnimal();
}
public abstract class Animal {
public abstract void eat();
public abstract void sleep();
}
public class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗喜欢啃骨头");
}
@Override
public void sleep() {
System.out.println("狗喜欢趴着睡");
}
}
public class CatFactory implements FactoryMethod {
@Override
public Animal creatAnimal() {
return new Cat();
}
}
public class DogFactory implements FactoryMethod {
@Override
public Animal creatAnimal() {
return new Dog();
}
}
8.3单例模式
定义:一个类的实例在内存中有且只有一个.
分为两种情况:
饿汉式:永远不会出现问题的一种单例模式.
1)自定义类:具体类
2)无参构造方法私有化,目的:外界不能创建对象了
3)在当前类的成员位置:创建自己本身对象
4)在当前类中提供一个对外的公共的静态的功能,返回值是它本身
举例:
package com.qf.jhq;
public class StudentTest {
public static void main(String[] args) {
Student s1 = Student.getInstance();
Student s2 = Student.getInstance();
System.out.println(s1==s2);
}
}
package com.qf.jhq;
//单例模型-饿汉式
public class Student {
private static Student s=new Student();
private Student(){}
public static Student getInstance(){
return s;
}
}
懒汉式:可能出现安全问题的一种单例模型.
1)自定义类:具体类
2)无参构造方法私有化,外界不能创建当前类对象
3)需要在成员位置,声明当前类型的变量
4)提供一个对外公共的并且静态的访问方法,返回值就是当前类本身
懒汉式可能会出现的问题:
(1)懒加载(延迟加载)Mybatis框架:延迟加载
(2)可能会存在一种多线程问题
举例:
package com.qf.jhq;
public class StudentTest2 {
public static void main(String[] args) {
Student2 s3 = Student2.getInstance();
Student2 s4 = Student2.getInstance();
System.out.println(s3==s4);
}
}
package com.qf.jhq;
public class Student2 {
private static Student2 s1;
private Student2(){}
public static Student2 getInstance(){
if(s1==null){
s1=new Student2();
}
return s1;
}
}
9.Runtime类:
每个java程序都有自己的运行环境,通过Runtime创建的实例表示:当前系统的运行环境
使用到了饿汉式
源码如下:
public class Runtime {
private static Runtime currentRuntime = new Runtime();
private Runtime() {}
public static Runtime getRuntime() { //创建当前类实例
return currentRuntime;
}
}
10.递归:
定义:方法调用方法本身的一种现象!而不是方法嵌套方法
递归思想:
(1)必须定义一个方法(函数)
(2)有一定的规律
(3)必须有出口条件(结束条件),如果没有结束条件就是死递归.
举例: 有一对兔子,从出生后第三个月起每个月产生一对兔子,小兔子长到第三个月后每个月又产生一对兔子;如果兔子都不死,第二十个月兔子的对数是多少? (不死神兔)
已知就是前两个月的兔子的对数都是1;
规律:
第一个月:1
第二个月:1
第三个月:2
第四个月:3
第五个月:5
第六个月:8
第七个月:13
几种方式:
1)数组方式解决
2)递归:定义方法/找规律/找出口条件
规律:
已知第一个月和第二个月的兔子对数都是1
从三个月开始,每个月兔子对数是前两个月之和
public class DiGuiTest2 {
public static void main(String[] args) {
//数组方式
//已知前两个的兔子对数都是1
//创建一个数组,长度20
int[] arr = new int[20] ;
arr[0] = 1 ;
arr[1] = 1 ;
//第三个月,从三个月开始,每个月兔子对数是前两个月之和
for(int x = 2; x <arr.length; x++){
arr[x] = arr[x-1] + arr[x-2] ;
}
System.out.println("第二十个月的兔子对数是:"+arr[19]);//6765
System.out.println("----------------------------------------") ;
System.out.println("第二十个月的兔子对数是:"+getRabbit(20)) ;
}
//定义一个方法
public static int getRabbit(int n){//第n个月
//出口条件
//n==1或者n==2: 兔子的对数1
if(n==1|| n==2){
return 1 ;
}else{
return getRabbit(n-1) + getRabbit(n-2) ;
}
}
}
11.IO流之File
描述的一个文件/目录的抽象路径名的形式.
File(File parent, String child)
从父抽象路径名和子路径名字符串创建新的 File实例。
File(String pathname) :直接描述地址路径
File(String parent, String child)
成员方法:
创建文件/创建文件夹相关的方法:
public boolean createNewFile() throws IOException:创建文件,如果文件不存在,就会自动创建,创建了,返回true
public boolean mkdir():创建文件夹,如果文件夹不存在,则自动创建,创建成功返回true,否则返回false;
public boolean mkdirs():如果父目录不存在,不会自动创建:针对带多级目录创建;
public boolean delete():删除文件或者目录,如果File表示的目录,删除必须为空目录.
如果没有带盘符,创建的文件默认就是当前项目下.
高级功能:
public File[] listFiles():获取某个目录下的所有文件以及文件夹的File数组
public String[] list():获取的抽象路径名表示下的所有文件以及文件夹中的字符串数组.
FilenameFilter接口:里面有一个抽象方法
boolean accept(File dir,String name) :是否将name:文件名称添加抽象类路径名所表示的列表中,取决于返回值
true,表示将name添加的文件过滤列表中,否则false,不添加!
举例:删除当前D盘下的所有的以.jpg结尾的文件.
使用文件名称过滤器作为参数传递:
public String[] list(FilenameFilter filter)
public File[] listFiles(FilenameFilter filter)
public class FileTest {
public static void main(String[] args) {
// 1)表示d盘
File file = new File("d:\\") ;
//2)需要将d盘下的里面的文件以及文件夹的名称都获取到
File[] fileArray = file.listFiles() ;
//3)先判断fileArray是否为空,防止空指针
if(fileArray!=null){
//遍历
for(File f :fileArray){
//f就是获取到的每一个file对象: 文件还是文件夹? 判断
//1)判断当前File对象所表示的抽象路径名是否是一个文件
if(f.isFile()){
//是文件
//需要满足,它的文件名称必须以.jpg结尾
if(f.getName().endsWith(".jpg")){
//就满足,删除文件
System.out.println(f.getName()+"----"+f.delete());
}
}
}
}
}
}
12.字节输出流
如何在写入文件的时候,进行换行操作呢?
Windows操作系统中:写入数据的时候,换行符号"\r\n;
12.1FileOutputStream
FileOutputStream的构造方法
public FileOutputStream(String pathname,boolean append):如果第二个参数为true,则自动后面追加
开发中,需要处理异常:
throws:抛出---抛在方法声明上,谁调用这个方法,谁就是调用者,通过调用者进行处理
try...catch...finally:捕获----通过业务逻辑代码(业务层代码:service层)
业务层数据service----->来源于dao层代码(数据访问对象: JDBC)
dao层代码抛出,业务service代码调用dao层代码,需要捕获异常!
try{
可能出现问题的代码;---一旦这有问题:Jvm 就会创建异常对象
} catch (异常类名 e) { //如果和当前异常类名匹配了
//处理异常
//System.out.println("数组角标越界了") ;
e.printStackTrace() ;//可以看到你自己写的代码哪块出错了以及底层原码
}finally{
//释放资源
}
try...catch
try...catch...catch...catch
try...finally
读数据的方式:
构造方法 FileInputStream
public FileInputStream(String name) throws FileNotFoundException:创建文件字节输入流对象
成员方法:
read这些方法:都是阻塞式方法:只要文件没有读完,一直等待要读取!
abstract int read():一次读取一个字节,返回的读取的字节数
一次读取一个字节,将文件内容打印在控制台: 如果文件中有中文,出现乱码
原因就是我们对读取的字节数by----强转(char)by :一个英文对应一个字节,但是中文(idea中:utf-8):一个中文8个字节,拼接不上导致乱码
举例:
public class FileInputStreamDemo3 {
public static void main(String[] args) throws IOException {
//创建文件字节输入流对象
FileInputStream fis = new FileInputStream("fis.txt") ;//当前项目下的fis.txt
//读取的是当前项目下的java文件FileOutputStreamDemo2.java
FileInputStream fis = new FileInputStream("FileOutputStreamDemo2.java") ;
//刚开始:还没还是读 字节数0
int by = 0 ;
while((by=fis.read())!=-1){
System.out.print((char)by);
}
//3)释放资源
fis.close() ;
}
}
int read(byte[] b) :一次读取一个字节数组
举例:
public class FileInputStreamDemo4 {
public static void main(String[] args) throws IOException {
//读取是当前项目下的fis2.txt文件
//创建文件字节输入流对象
FileInputStream fis = new FileInputStream("fis2.txt") ;
byte[] bytes = new byte[1024] ;//提供字节缓冲区
//实际的长度:根据内容判断 :获取实际字节数
int len = 0 ;
//判断和获取一块使用
while((len=fis.read(bytes))!=-1){
System.out.println(new String(bytes,0,len)); //每次从0开始获取实际字节数---转换成字符串
}
//释放资源
fis.close() ;
}
}
12.2BufferedOutputStream和BufferedInputStream
字节缓冲输出流BufferedOutputStream:又称字节高效流
这个流仅仅是在内部提供了一个缓冲区(字节数组),针对文件的输出并且同时写入数据使用的还是底层OutputStream.
构造方法:
public BufferedOutputStream(OutputStream out):构造一个默认的缓冲区大小,通过底层流进行输出(写入数据)
buf = new byte[size];//创建字节数组:作为缓冲区,长度8192字节缓冲输入流:BufferedInputStream
构造方法
BufferedInputStream(InputStream in)
成员方法:使用InputStream的read的功能:
一次读取一个字节/一次读取一个字节数组
举例:
public class BufferedOutputSteamDemo {
public static void main(String[] args) throws IOException {
//write();
//读
read() ;
}
//读取 当前项目下的bos.txt的内容
private static void read() throws IOException {
//创建字节缓冲输入流对象
//BufferedInputStream(InputStream in)
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("bos.txt")) ;
//一次读取一个字节
/* int by = 0 ;
while((by=bis.read())!=-1){
//展示出来
System.out.print((char)by);
}*/
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = 0 ;
while((len=bis.read(bytes))!=-1){
//展示
System.out.println(new String(bytes,0,len));
}
//释放资源
bis.close() ;
}
private static void write() throws IOException {
//创建字节缓冲输出流对象
//public BufferedOutputStream(OutputStream out):形式参数是一个抽象类,需要子类对象
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt")) ;
//写数据:使用的底层流的OutputStream的方法
bos.write("hello,BufferedStream".getBytes());
//释放资源
bos.close();
}
}
12.3SequenceInpuStream
合并流:可以实现a.txt/b.txt/c.txt---->复制到一个文件中.
是InputStream的子类:只能操作源文件.
构造函数:
public SequenceInputStream(Enumeration<? extends InputStream> e)
public SequenceInputStream(InputStream s1,InputStream s2):将两个字节输入流对象指向的文件进行合并
举例:
public class SequenceInpuStreamTest {
public static void main(String[] args) throws IOException {
//method1();
method2() ;//多个文件进行复制(两个以上的文件)
}
private static void method2() throws IOException {
//当前项目下的FileOutputStreamDemo.java/FileOutputStreamDemo2.java/InputAndOutputStreamCopy.java
//复制到D:\EE_2110\day26\code\\MyCopy.java
// public SequenceInputStream(Enumeration<? extends InputStream> e)
//创建Vector集合<InputStream>
Vector<InputStream> v = new Vector<>() ;
v.add(new FileInputStream("FileOutputStreamDemo.java")) ;
v.add(new FileInputStream("FileOutputStreamDemo2.java")) ;
v.add(new FileInputStream("InputAndOutputStreamCopy.java")) ;
//类似于迭代器:获取Enumeration枚举组件接口对象
Enumeration<InputStream> enumeration = v.elements();
//直接创建合并流对象
SequenceInputStream sis = new SequenceInputStream(enumeration) ;
//封装目的地文件
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("D:\\EE_2110\\day26\\code\\MyCopy.java")) ;
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = 0 ;
while((len=sis.read(bytes))!=-1){
bos.write(bytes,0,len) ;
bos.flush() ;
}
//释放
bos.close() ;
sis.close();
}
private static void method1() throws IOException {
//创建两个字节文件输入流对象:指向两个文件
InputStream in = new FileInputStream("FileOutputStreamDemo.java") ;
InputStream in2 = new FileInputStream("FileOutputStreamDemo2.java") ;
//封装到合并流中
SequenceInputStream sis = new SequenceInputStream(in,in2) ;
//目的地文件D:\EE_2110\day26\code\Copy.java
//BufferedOutputStream流
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("D:\\EE_2110\\day26\\code\\Copy.java")) ;
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = 0 ;
while((len=sis.read(bytes))!=-1){
bos.write(bytes,0,len) ;
bos.flush() ;
}
//释放
bos.close() ;
sis.close();
}
}
12.4Reader
抽象类,字符输入流:
子类:InputStreamReader:字符缓冲输入流:通向字节输入流的桥梁
构造方法
InputStreamReader(InputStream in) :使用平台默认的字符集进行解码,里面包装的字节流
InputStreamReader(InputStream in,String charsetName):使用指定的字符集进行解码
读取:
读一个字符read()
读一个字符数组read(char[] chs)
举例:
public class InputStreamReaderDemo {
public static void main(String[] args) throws IOException {
//需要读取oos.txt
//创建字符缓冲输入流对象
//InputStreamReader(InputStream in)
InputStreamReader isr = new InputStreamReader(
new FileInputStream("oos.txt"),"GBK") ;
//一次读取一个字符
/* int ch = 0 ;//字符数 ()
while((ch=isr.read())!=-1){
System.out.print((char)ch);
}*/
//一次读取一个字符数组
char[] chs = new char[1024] ;
int len = 0 ;
while((len=isr.read(chs))!=-1){
System.out.print((new String(chs,0,len))) ;
}
//释放资源
isr.close() ;
}
}
12.5Writer
子类 :字符转换输出流OutputStreamWriter:里面包装的是字节流
构造方法
OutputStreamWriter(OutputStream out) :使用平台默认的编码字符集进行编码--->写入数据
OutputStreamWriter(OutputStream out,String charsetName )使用指定的编码字符集进行编码--->写入数据
写的功能:
write(int ch):写一个字符
write(char[] chs):写字符数组
write(char[] chs,int index,int len):写字符数组的一部分
writer(String str):写字符串
writer(String str,int index,int len):写字符串的一部分
举例:
public class OutputStreamWriterDemo {
public static void main(String[] args) throws IOException {
//创建字符输出流对象
//OutputStreamWriter(OutputStream out)
// Writer out = new OutputStreamWriter(new FileOutputStream("out.txt")) ;
/*OutputStreamWriter oos = new OutputStreamWriter(
new FileOutputStream("oos.txt")) ;*///默认平台 utf-8 编码
OutputStreamWriter oos = new OutputStreamWriter(
new FileOutputStream("oos.txt"),"GBK") ;// 指定GBK 编码
oos.write("hello,字符流");
//释放资源
oos.close() ;
}
12.6FileReader和FileWriter
Read和Write的子类:字符转换流.
使用FileReader和FileWriter可以直接操作文件.
FileReader(String pathname):使用平台默认字符集:进行解码
FileWriter(String pathname)使用平台默认字符集:进行编码
字符流针对文本文件操作:
当前项目 FileOutputStreamDemo.java------>拷贝到当前项目下;My.java
图片文件: 字节流
四种:基本字节流以及字节缓冲流
举例:
public class CopyFileTest {
public static void main(String[] args) {
//捕获异常
//创建字符输入流对象:操作源文件
FileReader fr = null ;
FileWriter fw = null ;
try {
fr = new FileReader("FileOutputStreamDemo.java") ;
fw = new FileWriter("My.java") ;
//读写操作
//一次读取一个字符数组
char[] chs = new char[1024] ;
int len = 0 ;//实际字符数
while((len=fr.read(chs))!=-1){
//写
fw.write(chs,0,len) ;
fw.flush() ;
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(fw!=null){
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fr!=null){
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
17.序列化和反序列化
序列化定义:在将java对象变成一种流数据,他们里面的数据存储流中,就可以在网络中传输.
构造方法:
public ObjectOutputStream)(OutputStream out)
成员方法:
public final void writeObject(Object obj)throws IOException:写入一个实体对象 .
反序列化定义: 就是将流数据(存储的一些相关的实体类数据)--还原成java对象
public ObjectInputStream(inputStream in)throws IOException:将文件中的数据,进行读取
public final Object readObject()throws IOException,ClassNotFoundException
18.Propertis
属性集合类(属性列表)是一个Map,但是没有泛型,键和值都是String
特有功能:
public Object setProperty(String key,String value):添加键和值
public Set<String>stringPropertyNames():获取所有的键的集合
public String getProperty(String key):获取键对应的值
举例:有一个文本文件中存储了几个名字,要随机获得随机人的名字
public class Test2 {
public static void main(String[] args) throws IOException {
//文件中有内容了,将文件内容读取出来,保存在集合List中,做任何操作
BufferedReader br = new BufferedReader(new FileReader("lucy_name.txt")) ;
//创建一个集合ArrayList
ArrayList<String> array = new ArrayList<>() ;
//一次读取一行
String line = null ;
while((line=br.readLine())!=null){
array.add(line) ;
}
// System.out.println(array);//进行遍历
//创建随机数生成器
Random random = new Random() ;
int index = random.nextInt(array.size());
System.out.println("当前幸运的人是:"+array.get(index));
//如何读取src(类路径下)的配置文件.properties
method() ;
}
private static void method() throws IOException {
//文件在哪个了中要读:
//1)获取当前了类的字节码文件对象
/*Class c = Test2.class ;
//2)获取类加载器
ClassLoader classLoader = c.getClassLoader();
//3)通过类加载获取当前src目录下面的配置文件所在的输入流对象
InputStream inputStream = classLoader.getResourceAsStream("name.properties");*/
InputStream inputStream =
Test2.class.getClassLoader().getResourceAsStream("name.properties");
//创建Properties
Properties prop = new Properties() ;
prop.load(inputStream);
System.out.println(prop) ;
}
}
19.网络编程
ip地址:使用的一种"点分十进制法"
10.12.159.190
A类: 国家部门
前一个号段:网络号段
后面三个号段:主机号段
B类: 大学/教育部门:
前两个号段:网络号段
后面两个号段:主机号段
C类:私人地址 (家庭/单位...)
前三个号段:网络号段
后面这个号段:主机号段
windows系统:查看ipconfig
Linux系统:查看ifconfig
port:端口号:360软件---可以查看计算机中所有软件的端口!
范围:0-65535
0-1024属于保留端口
常见端口:tomcat服务器 默认:8080 (极域软件:端口号8080)
mysql:3306
redis(NOSQL数据库)客户端端口:6575
key:value
协议:
网络通信协议
TCP/IP协议
UDP协议
UDP
1)属于不可靠协议
2)不需要建立连接通道,所以执行效率高,但是安全性低!
3)发送内容的时候,有大写数据!
TCP/IP协议
1)属于可靠协议
2)需要建立连接通道,
服务器端监听客户端连接,如果客户端没有跟服务器端口进行绑定,服务器一直等待,执行效率低,但是安全性高
3)发送内容的时候,数据大小没有限制!
InetAddress:表示地址
静态方法:
public static inetAddress getByNmae(String host)throws UnkonownHostException:通过主机名称获取ip地址对象
public String getHostName():获取ip地址对象所描述的主机的名称(计算机名称)
public String getHostAddress():获取ip地址:以字符串形式展示
UDP实现客户端发送数据,服务端接收数据:
客户端:
* 1)创建UDP发送端的Socket对象
* 2)创建数据报包
* 3)发送
* 4)释放资源
* socket对象.close()
*
*/
public class SendDemo {
public static void main(String[] args) throws Exception {
//1)创建UDP发送端的Socket对象
//public DatagramSocket() throws SocketException
DatagramSocket ds = new DatagramSocket() ;
//创建一个BufferedReader流对象:键盘录入
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in)) ;
String line = null ;
while((line=br.readLine())!=null){
//自定义结束条件
if("886".equals(line)){
break ;
}
//2)创建数据报包---- >包含ip和端口+里面的数据
//DatagramPacket
//public DatagramPacket(byte[] buf ,int length,InetAddress address,int port)
DatagramPacket dp = new DatagramPacket(line.getBytes(),line.getBytes().length,
InetAddress.getByName("10.12.159.190"),10086) ;
//3)发送
//public void send(DatagramPacket p)
ds.send(dp) ;
}
//4)释放资源
ds.close() ;
}
}
服务端:
1)创建接收端的Socket对象
* 2)创建一个接收容器: 自定义一个缓冲区byte[] (创建数据包报)
* 3)接收
* 4)解析真实数据
* 5)展示ip和发送的内容
*
*/
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
//1)创建接收端的Socket对象DatagramSocket
//public DatagramSocket(int port)
DatagramSocket ds = new DatagramSocket(10086) ;
while(true){
//创建一个接收容器: 自定义一个缓冲区byte[]
byte[] bytes = new byte[1024] ;
int length = bytes.length ;
//(创建数据包报)
//public DatagramPacket(byte[] buf,int length)
DatagramPacket dp = new DatagramPacket(bytes,length) ;
//3)接收
// public void receive(DatagramPacket p)
ds.receive(dp);
//4)解析接收容器中的真实内容
//public byte[] getData():获取缓冲区中的真实的字节数组
//public int getLength():获取缓冲区中的长度
//获取实际内容
String str = new String(dp.getData(),0,dp.getLength()) ;
//获取ip地址
// public InetAddress getAddress()
String ip = dp.getAddress().getHostAddress() ;
System.out.println("data from "+ip +",content is :"+str) ;
}
//释放资
// ds.close() ;
}
}
TCP/IP实现客户端发送数据,服务端接收数据
客户端:
1)创建客户端的Socket对象
* 2)获取客户端所在的通道内的字节输出流OutputStream
* 3)给客户端通道内的流中写入数据
* 4)释放客户端的资源对象
*
* 客户端不断的键盘录入数据,服务器端不断的接收数据,展示数据
*/
public class ClientDemo {
public static void main(String[] args) throws IOException {
//1)创建客户端的Socket对象
//public Socket(String host, int port)
Socket socket = new Socket("10.12.159.190",10010) ;
//只要服务器端一开,监听端口10010,客户端就会和服务器端进行握手通信
//2)获取客户端所在的通道内的字节输出流OutputStream
//public OutputStream getOutputStream()
OutputStream out = socket.getOutputStream();
//3)写数据:给通道流中写入数据
out.write("hello,TCP,我来了".getBytes());
//4)释放资源
socket.close() ;
}
}
服务端 接收数据
1)创建服务器端的Socket对象
* 2)监听客户端的连接 ---一旦监听到了,就获取到那个客户端的Socket
* 3)获取连接的客户端的通道内的输入流,读取数据
* 4)展示数据
* 5)释放资源
*/
public class ClientDemo {
public static void main(String[] args) throws IOException {
//1)创建客户端的Socket对象
//public Socket(String host, int port)
Socket socket = new Socket("10.12.159.190",10010) ;
//只要服务器端一开,监听端口10010,客户端就会和服务器端进行握手通信
//2)获取客户端所在的通道内的字节输出流OutputStream
//public OutputStream getOutputStream()
OutputStream out = socket.getOutputStream();
//3)写数据:给通道流中写入数据
out.write("hello,TCP,我来了".getBytes());
//4)释放资源
socket.close() ;
}
}
例题: TCP客户端的文本文件,服务器端将文件复制,到指定文件中.
public class ClientDemo {
public static void main(String[] args) throws IOException {
//创建socket
Socket socket = new Socket("10.12.159.190",12306) ;
//读取当前项目下的Copy.java文件
//使用BufferedReader 封装源文件
BufferedReader br = new BufferedReader(new FileReader("Copy.java")) ;
//封装通道内的字节输出流
//分步走
/*OutputStream outputStream = socket.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream)) ;*/
//一步走
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(
socket.getOutputStream())) ;
//一次读取一行进行读写复制,将文件中的内容写到封装通道 的流对象中 bw
String line = null ;
while((line=br.readLine())!=null){
bw.write(line) ;
bw.newLine() ;
bw.flush() ;
}
//释放资源
br.close() ;
socket.close() ;
}
}
服务器端将文件复制,到指定文件中(当前项目下)
public class ServerDemo {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(12306) ;
//监听
Socket socket = ss.accept();
//获取当前客户端的Socket通道内的输入流InputStream
//分步走
/*InputStream inputStream = socket.getInputStream();
//封装BufferedReader
BufferedReader br = new BufferedReader(
new InputStreamReader(inputStream)) */;
//一步走
//封装通道内的字节输入流
BufferedReader br = new BufferedReader(
new InputStreamReader(
socket.getInputStream())) ;
//将读取的内容,输出在当前项目下的My.java文件中
BufferedWriter bw = new BufferedWriter(new FileWriter("My.java")) ;
//一次读取一行
String line = null ;
while((line=br.readLine())!=null){
bw.write(line) ;
bw.newLine() ;
bw.flush();
}
//是否资源
bw.close() ;
ss.close() ;
}
}
TCP客户端的文本文件,服务器端将文件复制到指定文件(当前项目下),加入服务器端反馈...
* 发现问题:
* 文件是已经完毕了,但是客户端并没有收到服务器端的反馈,服务器也没关闭; 出现了互相阻塞了
* 针对 文件的复制,如果是文本文件,我们通过BufferedReader的readLine的返回值是否为null,判断文件是否复制完毕
* null.表示文件复制完了,但是对于Server服务器端来说,他不知道通道内的流中是否还有数据需要在写过来;等待客户端通知
* 我们的服务器端,文件已经复制完毕;客户端在等着服务器端的反馈的消息,所以就出现了互相等待;
* 解决方案:
* 1)自定义结束条件,只要服务器端读取到这个自定义内容,就打破了阻塞,可以给客户端反馈
* 如果当前这个文件:一开头"over",直接服务器端读取到这个就结束了,可能没复制完毕!
* 2)直接使用客户端的功能完成:
* public void shutdownOutput():禁用客户端的输出流;不会写在流中写入数据了,服务器端就得到了通知,没有数据写过了
*
public class ClientDemo {
public static void main(String[] args) throws IOException {
//创建socket
Socket socket = new Socket("10.12.159.190",12306) ;
//读取当前项目下的Copy.java文件
//使用BufferedReader 封装源文件
BufferedReader br = new BufferedReader(new FileReader("Copy.java")) ;
//封装通道内的字节输出流
//分步走
/*OutputStream outputStream = socket.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream)) ;*/
//一步走
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(
socket.getOutputStream())) ;
//一次读取一行进行读写复制,将文件中的内容写到封装通道 的流对象中 bw
String line = null ;
while((line=br.readLine())!=null){ //readLine()阻塞式方法
bw.write(line) ;
bw.newLine() ;
bw.flush() ;
}
//方式1
//自定义结束条件
/* bw.write("over") ;
bw.newLine() ;
bw.flush() ;*/
socket.shutdownOutput();
//读取服务器端的的反馈
//获取客户端所在的通道内的字节输入流
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024] ;
int len = inputStream.read(bytes);
String fkMsg = new String(bytes,0,len) ;
System.out.println("接收的服务器端反馈的数据是:"+fkMsg);
//释放资源
br.close() ;
socket.close() ;
}
}
服务器端将文件复制到指定文件(当前项目下),加入服务器端反馈...
*/
public class ServerDemo {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(12306) ;
//监听
Socket socket = ss.accept(); //监听:阻塞式方法
//获取当前客户端的Socket通道内的输入流InputStream
//分步走
/*InputStream inputStream = socket.getInputStream();
//封装BufferedReader
BufferedReader br = new BufferedReader(
new InputStreamReader(inputStream)) */;
//一步走
//封装通道内的字节输入流
BufferedReader br = new BufferedReader(
new InputStreamReader(
socket.getInputStream())) ;
//将读取的内容,输出在当前项目下的My.java文件中
BufferedWriter bw = new BufferedWriter(new FileWriter("Hello.java")) ;
//一次读取一行
String line = null ;
while((line=br.readLine())!=null){ //阻塞式方法
/* if("over".equals(line)){
break ;
}*/
bw.write(line) ;
bw.newLine() ;
bw.flush();
}
//服务器端需要进行反馈
//获取通道内的输出流
OutputStream outputStream = socket.getOutputStream();
outputStream.write("文件已经复制完毕;".getBytes());
//是否资源
bw.close() ;
ss.close() ;
}
}
20.MySQL数据库
1.数据库的定义语句:DDL库的增删查改
-- 查询当前所有的数据库名称
show databases;
-- 创建自己的数据库
create database 库名;
-- 删除库
drop database 库名;
-- 查询创建数据库的默认的字符集格式
show create database 库名;
-- 创建表的前提,先选择库,进入到库中;
use 库名;
-- 查询这个库中所有的表
show tables;
-- 创建表的语法
创建表的语法
create table 表名(
字段名称1 字段类型1,
字段名称2 字段类型2,
字段名称3 字段类型3
...
) ;
字段类型:mysql varchar(字符个数) ---代表String 字符串
int类型:整数类型
int:默认的字符个数int(11):当前真实age年龄的长度 (所占的实际的字符数) 整数默认int(11)
举例: age int,
7
int(指定字符个数):int(4)
age int(4)
0007
date :日期 仅仅是日期
datetiem :日期+时间
timestamp:时间戳 (即时时间)
当你在执行给这个字段插入某个指定的时候,当前的那个日期+时间
double(3,2) :这个字段值是3位数,小数后保留2位
举例:3.56
double
999.0
-- 查询表的结构
desc 表名;
-- 修改表的字段名称
alter table 表名 change 旧字段名字 新字段名称 数据类型;
-- 修改字段类型
alter table 表名 modify 字段名称 新的字段类型;
-- 添加新的字段
alter table 表名 add 新的字段名称 类型;
-- 修改表:将某一个字段删除
alter table 表名 drop 字段名称;
-- 复制一张新的表跟以前的表的结构相同
create table 新的表名 like 需要复制的表名;
-- 修改表的名称
alter table 需要修改的表名 rename to 新的表名;
-- 删除表
-- drop table 表名;
2.数据库的操作语句:DML语句:
-- 给表中插入数据
语法1:一次性插入全部字段值 insert into 表名 values(字段名称值);
INSERT INTO student VALUES(1,'张三','男','西安市鄠邑区','1970-03-27') ;
语法2:插入部分字段 insert into 表名(字段名称1,字段名称2...),
insert into 表名(字段名1,字段名2...) values(值1,值2,值...);
INSERT INTO student(id,NAME,sex,address) VALUES(3,'文章','男','西安市未央区')
语法3:一次性可以插入多条记录;
insert into表名 values(值1,值2,值3...)(值1,值2...);
INSERT INTO student VALUES
(4,'王宝强','男','北京市昌平区','1980-04-30'),
(5,'马云','男','杭州市','1970-6-21'),
(6,'马化腾','男','深圳市','1970-7-30') ;
注意:插入的顺序必须对应.
-- 修改表的语句update:
-- 修改的时候使用where语句
update 表名 set 字段名称 = 值 where 其他字段:
UPDATE student SET birthday = '1982-06-30' WHERE id = 3 ;
--修改多个字段
update 表名 set 字段名称1 = 值1,字段名称2 = 值2...where 条件语句
UPDATE student SET NAME = '赵又廷' ,sex = '男' WHERE id = 7 ;
-- 批量修改
update 表名 set 字段名称 = 值;
UPDATE student SET address = '西安市雁塔区' ;
-- 删除语句
delete from 表名 where 带条件删除
DELETE FROM student WHERE id = 7 ;
-- 删除全表记录
truncate table 表名;
TRUNCATE TABLE student ;
查询语法:DQL语句
select 全部字段名 from 表名;
-- 查询字段时去重
ELECT
DISTINCT(字段) -- 数据库的DISTINCT(字段名称),可以实现字段去重
FROM 表名 ;
-- 高级查询
-- where后面跟表达式(关系运算符/逻辑运算符 ||(or) &&(and) between.. and..)
例题 -- 查询年龄大于20岁的所有的学生的id,姓名,年龄,性别,地址
SELECT
id '编号',
NAME '姓名',
age '年龄',
sex '性别',
address '地址'
FROM
student
WHERE
age >20 ; -- 比较运算符
-- 查询学生年龄不等于20岁的学生的信息 Java中的语法!= 或者<>
SELECT
id,
NAME,
age,
sex,
address
FROM
student
WHERE
age <> 20 ;
--查询年龄在20-30岁之间的学生信息 (Java中逻辑符号呢?&& 可以,数据库and ,使用between 值1 and 值2
SELECT
id,
NAME,
age,
sex,
address,
math,
english
FROM
student
WHERE
age BETWEEN 20 AND 30 ;
-- 要查询英语成绩为null的学生信息
-- 数据库中:某个字段名称 is null /某个字段名称 is not null
SELECT
id,
NAME,
age ,
address,
english
FROM
student
WHERE
english IS NULL ;
要查询年龄是18或者20,或者30的学生信息
SELECT
id ,
NAME,
age,
sex,
address
FROM
student
WHERE
age =18 OR age = 20 OR age = 30 ; -- || 或者 or
优化
SELECT
*
FROM
student
WHERE
age IN(18,20,30) ;
模糊查询 关键字 like
-- select 字段列表 from 表名 where 字段名称 like '包含%符号或者_下划线符号'
-- 查询学生姓名中包含马的学生
SELECT
id,
NAME,
age,
sex,
address
FROM
student
WHERE
NAME LIKE '%马%' ;
-- 姓名中第二个字符是化的学生新'%_化%'
SELECT
*
FROM
student
WHERE
NAME LIKE '%_化%' ;
-- 聚合函数-- 一般查询都是单行单列的数据
-- count(字段名称) 查询总记录数
-- count(字段名称:一般情况使用的非业务字段
-- max(字段名称) 求某个列中的最大值
-- min(字段名称) ...最小值
-- sum(字段名称) 针对某个字段列表进行求和
-- avg(字段):针对某个字段列求平均分
-- count(字段名称) 查询student表中的总记录数
-- 如果使用业务字段:使用英语字段来查询
-- (它不会null字段,只统计有内容的字段
SELECT
COUNT(IFNULL(english,0)) '总记录数' -- ifnull(字段名称,预期值)
FROM
student ;
-- count(表中的id字段:非业务字段,开发中都会设置为主键并且自增长)
SELECT
COUNT(id) '总人数'
FROM
student ;
-- 查询数学成绩的最高分
SELECT
MAX(math)
FROM
student ;
-- -- 查询英语成绩的最低分
SELECT
MIN(IFNULL(english,0)) '最低分'
FROM
student ;
-- 求数学成绩的平均分
SELECT
AVG(math) '数学平均分'
FROM
student ;
排序查询
-- select 字段列表 from 表名 order by 字段名称 排序方式;
-- 排序方式:asc(默认(当字段名称后面没有带排序方式):升序) ,desc
-- 将学生成绩按照升序排序
SELECT
*
FROM
student
ORDER BY
math ; -- 不带排序方式:默认 asc
-- 将英语成绩按照降序排序
SELECT
*
FROM
student
ORDER BY
english DESC ;
-- 分组查询 group by
-- 分组查询的时候,可以查询分组的字段
-- 按照性别分组,查询他们的数学的平均分以及总人数
SELECT
sex '性别',-- 可以查询分组字段
COUNT(id) '总人数',
AVG(math) '数学平均分'
FROM
student
GROUP BY
sex ; -- 性别分组
-- 筛选(过滤) having
-- 按照性别分组,查询他们的数学的平均分以及总人数
-- 1):数学成绩不大于70分的人不参与分组
-- 2):筛选出总人数大于2的一组
SELECT
sex 性别,
AVG(math) 数学平均分,
COUNT(id) 总人数
FROM
student
WHERE
math > 70 -- 条件
GROUP BY
sex -- 分组
-- 筛选出总人数大于2的一组
HAVING
总人数 > 2 ;
-- 分页查询 limit (mysql和oracle有方言差异这个这个语法上)
-- select 字段列表 from 表名 limit 起始行数(起始索引),每页显示的条数;
-- 起始行数(起始索引):从0开始计算 = (当前页码-1)*每页显示的条数
-- 查询当前学生表中第一页数据,每页显示2条数据;
SELECT
*
FROM
student
LIMIT 0,2 ;
-- 查询第二页的数据
-- 起始行数= (2-1)*2
SELECT
*
FROM
student
LIMIT 2,2 ;
-- 查询第三页数据 : 每页显示2条
SELECT
*
FROM
student
LIMIT 4, 2;
-- 查询第四页数据
SELECT
*
FROM
student
LIMIT 6,2 ;
-- 数据库的约束
-- 约束:约束用户操作表时候的行为,防止无意义的数据在表中存在!
-- 默认约束 default
-- 创建一张表stu_test
CREATE TABLE stu_test(
id INT ,
NAME VARCHAR(20),
gender VARCHAR(5) DEFAULT '女' -- 创建的时候给性别添加默认约束,default
);
INSERT INTO stu_test VALUES(1,'张三','男'),(2,'李四','女') ;
-- 如果插入部分字段的时候,没有插入的字段就是NULL,没有意义 ,需要加入约束这种非法行为
INSERT INTO stu_test(id,NAME) VALUES(3,'高圆圆') ; -- 加入约束,不插入默认约束就起作用
-- 非空约束 not null
CREATE TABLE stu_test(
id INT ,
NAME VARCHAR(20) NOT NULL -- 非空约束
) ;
INSERT INTO stu_test VALUES(1,'高圆圆'),(2,'文章') ;
-- 插入数据的时候,直接插入null值
-- insert into stu_test values(3,null) ;-- Column 'name' cannot be null :非空约束起作用
-- INSERT INTO stu_test(id) VALUES(3) ;
-- 通过修改表:删除非空约束
ALTER TABLE stu_test MODIFY NAME VARCHAR(20) ;
INSERT INTO stu_test VALUES(3,NULL) ;
-- 通过修改表:添加非空约束
ALTER TABLE stu_test MODIFY NAME VARCHAR(20) NOT NULL ;
DELETE FROM stu_test WHERE id = 3 ;
-- 唯一约束 unique
-- 创建一张表
CREATE TABLE stu_test(
id INT ,
NAME VARCHAR(20),
phone VARCHAR(11) UNIQUE -- 添加唯一约束
) ;
INSERT INTO stu_test VALUES(1,'高圆圆','13688889999'),
(2,'王聪','13866668888') ;
INSERT INTO stu_test VALUES(3,'赵又廷','13688889999') ;
-- Duplicate entry '13688889999' for key 'stu_test.phone' :值重复了,唯一约束起作用
-- 修改表:删除唯一约束 (drop index 唯一约束的字段名称)
-- alter table stu_test modify phone varchar(11) ;
ALTER TABLE stu_test DROP INDEX phone ;
DELETE FROM stu_test WHERE id = 3 ;
-- 添加唯一约束
ALTER TABLE stu_test MODIFY phone VARCHAR(11) UNIQUE ;
-- 主键约束 primary key
-- 自增长约束 auto_incrment
CREATE TABLE stu_test(
id INT PRIMARY KEY AUTO_INCREMENT,-- 自增长的主键
NAME VARCHAR(20),
gender VARCHAR(5)
) ;
-- 添加数据
INSERT INTO stu_test(NAME,gender) VALUES('螳螂','男'),('咔沙','女'),('皇子','男') ;
-- id 给定值,下次它会随着之前的值继续自增
INSERT INTO stu_test VALUES(6,'小法','男') ;
INSERT INTO stu_test(NAME,gender) VALUES('盲僧','男') ;
-- mysql自带 的函数 select last_insert_id() :
-- 查询最后一次自增长主键的值(后期mybatis框架中使用:获取最后一次自增长主键的id值)
SELECT LAST_INSERT_ID() ;
SELECT * FROM stu_test ;
-- 外键约束 foreign key
-- 外键约束基础,可以进行级联操作(级联修改和级联删除)CASCADE
-- 级联操作:CASCADE
-- 用户在操作主表的时候,跟主表相关的从表的数据也随之更改;
-- 如果不使用操作:针对修改或者删除,都应该先执行从表,然后主表;
-- 级联删除和级联修改 on delete cascade /on update cascade
-- 通过sql语句:外键删除
ALTER TABLE employee DROP FOREIGN KEY dept_emp_fk ;
-- 添加外键约束的同时,添加级联删除和级联修改
ALTER TABLE employee ADD
CONSTRAINT dept_emp_fk -- 声明 跟的外键名称
FOREIGN KEY (dept_id) -- 作用在外键上
REFERENCES dept(id) -- 关联与主表的主键id
ON DELETE CASCADE
ON UPDATE CASCADE ;
SELECT * FROM dept ; -- 主表
SELECT * FROM employee ;-- 从表的外键关联与主键的主键
-- 将运维部的编号--变成4号部门
UPDATE dept SET id = 4 WHERE id = 3 ;
-- 直接部门表的4号部门,那么在4号部门的员工也随着 删除了
DELETE FROM dept WHERE id = 4 ;
-- 数据库的备份和还原
-- 数据库的备份:需要将数据库进行存储(计算机磁盘上或者是硬盘)
-- 方式1:命令行的方式
-- 方式2:图形界面化的方式:简单/直观/易于上手
-- 进入dos控制台:mysqldump -uroot -p密码 数据库 > 磁盘地址...
-- 举例
/*
不需要登录:
mysqldump -uroot -p123456 ee_2110 > D:\EE_2110\day30\code\mydb.sql
*/
-- 还原过程
-- 方式1:命令行方式
-- mysql8---进入mysql自动控制台或者dos登录mysql
-- 先将数据库删除掉,重新创建一个新的库,使用这个use 库名
-- souce 将备份的地址 (自动将原先备份的库中的表数据全部执行)
-- 方式2:图形界面化
-- 在sqlYog或者navicat里面都可以用,直接将库选中, 删除,删除之后,
-- 在创建的新库右键---选择 import---> Execute Sql Scripts:执行sql脚本
-- 选择备份的地址,直接执行sql文件!
CREATE DATABASE JavaEE_2110 ;
TRUNCATE TABLE 表名 ;它和 delete from 表名 ; 两者有什么区别
delete from 表名 ;仅仅只是删除表的记录,
不会删除这个表的结构,那么针对自增长约束的字段不影响
后者TRUNCATE TABLE 表名 ;删除了表,
它重新创建一张一模一样的空表,(表的结构存在)
前一些自增长的主键约束(数据库约束)者这个语法它会将影响