1. 静态代理
1.1什么是代理
结构设计模式:代理模式---------动态代理 / 静态代理
代理就是代理角色帮助真实角色完成一些事情!
1.2 静态代理
通过代理角色帮助真实角色完成一些事情,真实角色只专注于自己的事情!
前提条件:代理角色和真实角色必须是同一个接口!
//测试类
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方法即可!
3.线程组
Thread类中:
public final ThreadGroup getThreadGroup():获取当前线程所属的线程组
ThreadGroup线程组:
public final String getName():获取线程组名称
ThreadGroun的构造方法:
public ThreadGroup(String name)
默认线程组就是 main 用户线程
当前线程将用户的任务执行完毕,就结束了,不会再回到线程组中
4.线程池
4.1概念
创建一个"池子",然后里面会规定固定的可重用的线程,这些线程使用完毕后,它不会立即被回收,而会将线程再次归还到线程池中,等待下一次利用!
(第三种方式实现线程的创建:使用线程池)
4.2具体应用
1) 工厂类:里面提供了一个静态方法:
public static ExecutorService newFixedThreadPool(int nThreads);创建一个固定的可重用的线程数的线程池
2) ExecutorService:接口它可以进行多个异步任务的执行
<T> Future<T> submit(Callable<T> task) :提交异步任务, 这些异步任务就需要执行
3) Callable中的call方法
V call() throws Exception :计算一个结果,如果不能这样做,就抛出一个异常
如果仅仅是要实现多个线程并发执行,那么调用sumbit的方式,返回值可以不写
如果是要计算具体的结果,那么就需要将Future返回,它表示异步计算的结果值是什么
两个线程分别进行求和----展示多个线程并发执行
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;
}
}
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() ;
}
}
两个线程分别进行求和!----计算结果 使用线程池完成
/*
* <T> Future<T> submit(Callable<T> task)
* Future:计算的结果 接口
* V get()获取结果
*/
public class MyCallable implements Callable<Integer> {
private int num ;
public MyCallable(int num){
this.num = num ;
}
@Override
public Integer call() throws Exception {
//定义最终结果变量
int sum = 0 ;
for(int x = 1 ; x <= num ; x ++){
sum += x ;
}
return sum;
}
}
public class ThreadTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建线程池
ExecutorService threadPool = Executors.newFixedThreadPool(2);
//提交异步任务进计算
Future<Integer> f1 = threadPool.submit(new MyCallable(100));
Future<Integer> f2 = threadPool.submit(new MyCallable(200));
Integer i1 = f1.get();
Integer i2 = f2.get();
System.out.println(i1) ;
System.out.println(i2) ;
//关闭线程池
threadPool.shutdown() ;
}
}
5. Lock接口
jdk5以后提供了一个juc包下(java.util.concurrent.locks )
Lock接口 比synchronized具有扩展性的功能
5.1 子实现类
1) ReentrantLock:可重入的互斥锁
2) public void lock()获取锁
3) public void unlock()释放锁
API举例
Lock lo = ...;
lo.lock();
try {
// access the resource protected by this lock
} finally {
lo.unlock();
}
5.2 处理异常
处理异常:
1) try...catch...finally(释放资源:代码一定会执行/除非执行之前jvm退出了) 捕获异常的标准格式
2) try...catch...catch...
3) try...finally
4) throws :抛出异常:抛出在方法声明上
6.Timer
6.1概念
Java.util.Timer :JavaSE的定时器,处理一次或者重复执行的任务(TimerTask:抽象类)
JavaEE的定时任务框架Quartz----后面可以"Spring"整合
6.2构造方法:
public Timer():创建一个新的定时器,不是守护线程!
6.3 成员方法:
1) public void cancel():取消定时器
2)public void schedule(TimerTask task,long delay):经过多少毫秒后执行一次定时任务
3) public void schedule(TimerTask task,long delay,long period)经过delay毫秒后指向定时任务,每经过period重复执行定时任务!
4) public void schedule(TimerTask task,Date time) :在指定时间的时候(精确到毫秒)执行定时任务
TimerTask:抽象类:不能实例化:自定义子类或者匿名内部类
5) public abstract void run():定时器要执行的操作
public class TimerDemo {
public static void main(String[] args) {
//创建一个定时器
//空参构造
Timer timer = new Timer( );
//开启定时任务
/* timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("bom...");
}
}, 3000);*/
// public void schedule(TimerTask task,long delay,long period)
timer.schedule(new MyTask(timer),2000,3000);
}
}
//定时任务
class MyTask extends TimerTask{
//声明定时器变量
private Timer t ;
public MyTask(Timer t){
this.t = t ;
}
@Override
public void run() {
System.out.println("bom...");
t.cancel() ;
}
}
7. 设计模式
7.1概述
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
设计模式不是一种方法和技术,而是一种思想
设计模式和具体的语言无关,学习设计模式就是要建立面向对象的思想,尽可能的面向接口编程,低耦合,高内聚,使设计的程序可复用
设计模式的几个要素:
1)名字 必须有一个简单,有意义的名字
2)问题 描述在何时使用模式
3)解决方案 描述设计的组成部分以及如何解决问题
4)效果 描述模式的效果以及优缺点
设计模式的分类:
1)创建型模式 对象的创建
2)结构型模式 对象的组成(结构)
3)行为型模式 对象的行为
创建型模式:简单工厂模式,工厂方法模式,抽象工厂模式,建造者模式,原型模式,单例模式。(6个)
结构型模式:外观模式、适配器模式、代理模式、装饰模式、桥接模式、组合模式、享元模式。(7个)
行为型模式:模版方法模式、观察者模式、状态模式、职责链模式、命令模式、访问者模式、策略模式、备忘录模式、迭代器模式、解释器模式。(10个)
7.2简单工厂模式
7.2.1概述
简单工厂模式:也称为"静态工厂方法模式",主要作用负责某些类的实例的创建过程!
优点:通过工厂类完成各个类的对象的创建
弊端:当有新的类型增加,需要不断的修改工厂类,维护性难度大!
//动物类
public class Animal {
public void eat(){
System.out.println("动物都需要吃饭...");
}
public void sleep(){
System.out.println("动物都需要休息...");
}
}
//猫类
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("狗躺着睡...");
}
}
//工厂类
public class Pig extends Animal{
@Override
public void eat() {
System.out.println("猪吃白菜...");
}
@Override
public void sleep() {
System.out.println("猪侧着睡");
}
}
//工厂类
public class AnimalFactory {
//构造方法私有化
private AnimalFactory(){}
//提供静态功能
/* public static Dog creatDog(){//创建狗
return new Dog() ;
}
public static Cat createCat(){//创建猫
return new Cat() ;
}
public static Pig creatPig(){//创建猪
return new Pig() ;
}*/
//优化;抽象类多态
public static Animal createAnimal(Object obj){ //向上转型:
if(obj instanceof Cat){
return new Cat() ;
}else if(obj instanceof Dog){
return new Dog() ;
}else if(obj instanceof Pig){
return new Pig() ;
}
return null ;
}
}
//测试类
public class SimpleFactory {
public static void main(String[] args) {
//具体类
Cat c = new Cat() ;
c.eat();
c.sleep();
Dog d = new Dog() ;
d.eat();
d.sleep();
System.out.println("--------------------------------") ;
//提供一个工厂类,负责这些动物的创建
//创建一只狗
/* Dog dog = AnimalFactory.creatDog();
dog.eat();
dog.sleep();
Cat cat = AnimalFactory.createCat();
cat.eat();
cat.sleep();
Pig pig = AnimalFactory.creatPig() ;
pig.eat();
pig.sleep();*/
//问题:随着新的类型增加,需要修改工厂类,而且代码都是写死的!
Animal animal = AnimalFactory.createAnimal(new Cat());
if(animal!=null){
animal.eat();
animal.sleep();
}
}
}
7.3 工厂方法模式
7.3.1概述
工厂方法模式中抽象工厂类负责定义创建对象的接口,具体对象的创建工作由继承抽象工厂的具体类实现。
优点:具体的工厂类负责创建当前具体的实例,结构层次很清晰,有具体类/工厂类/抽象工厂(接口)
弊端:有新的类型增加,需要编写外的代码,所有代码增加,导致工作量增加了
//工厂接口
public interface Factory { //抽象工厂(接口比抽象类还抽象)
Animal createAnimal() ; //抽象方法:创建动物
}
//猫的工厂类
public class CatFactory implements Factory {
@Override
public Animal createAnimal() { //抽象类多态
return new Cat();
}
}
//狗的工厂类
public class DogFactory implements Factory{
@Override
public Animal createAnimal() {
return new Dog() ;
}
}
//抽象的动物类
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("狗趴着睡...");
}
}
//测试类
public class FactoryMethod {
public static void main(String[] args) {
//抽象类多态
Animal a = new Cat() ;
a.eat() ;
a.sleep() ;
a = new Dog() ;
a.eat();
a.sleep() ;
System.out.println("--------------------------------------------") ;
//优化:使用工厂方法模式
Factory factory = new DogFactory() ;
Animal animal = factory.createAnimal();
animal.eat() ;
animal.sleep() ;
factory = new CatFactory() ;
Animal animal2 = factory.createAnimal();
animal2.eat() ;
animal2.sleep() ;
}
}
7.4单例设计模式
7.4.1概述
单例:一个类的实例在内存中有且仅有一个!
优点:在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
缺点:没有抽象层,因此扩展很难。职责过重,在一定程序上违背了单一职责
7.4.2 分两种情况
饿汉式:永远不会出现的一种单例模式 (类一加载就创建对象)
懒汉式:可能出现安全问题的一种单例模式 (用的时候,才去创建对象)
<1> 饿汉式:
当类一加载,就创建了当前类对象,而且始终只有一个
1)自定义类:具体类
2)无参构造方法私有化,目的:外界不能创建对象了
3)在当前类的成员位置:创建自己本身对象
4)在当前类中提供一个对外的公共的静态的功能,返回值是它本身
public class Student {//具体类
//成员位置:创建当前类的实例
// public static Student s = new Student() ; //
private static Student s = new Student() ;
//无参构造私有化
private Student(){}
//在当前类中提供一个对外的公共的静态的功能,返回值是它本身
public static Student getInstance(){
return s ;//静态只能访问静态
}
}
//测试类
public class SingleObjectPattern {
public static void main(String[] args) {
/* Student s1 = new Student() ;
Student s2 = new Student() ;
//多例了;对象不同
System.out.println(s1==s2);
System.out.println(s1.getClass()==s2.getClass());*/ //获取字节码文件对象*/
//Student.s = null ;//没有意义:直接赋值空对象,不安全
Student s1 = Student.getInstance();
Student s2 = Student.getInstance();
Student s3 = Student.getInstance() ;
System.out.println(s1==s2) ;
System.out.println(s2==s3) ;
}
}
<2>懒汉式:
用的时候,才去创建对象
1)自定义类:具体类
2)无参构造方法私有化,外界不能创建当前类对象
3)需要在成员位置,声明当前类型的变量
4)提供一个对外公共的并且静态的访问方法,返回值就是当前类本身
public class Teacher {//具体类
private static Teacher t ; //声明 默认值null
//无参构造方法私有化
private Teacher(){
}
//对外提供一个静态的公共访问方法,返回值是当前类本身
//每一个用户都需要请求getInstance():每一个用户相当于线程
//t1,t2,t3 都执行这个代码
/* public static Teacher getInstance(){ //静态的:锁对象:类名.class
synchronized (Teacher.class){//同步代码块
//先判断
//如果当前t变量为null,说明并没有创建当前类实例 (在使用的时候才创建)
if(t == null){
t = new Teacher() ; //创建对象
}
return t ;
}
}*/
//优化
public static synchronized Teacher getInstance(){ //静态的同步方法
//先判断
//如果当前t变量为null,说明并没有创建当前类实例 (在使用的时候才创建)
if(t == null){
t = new Teacher() ; //创建对象
}
return t ;
}
}
//测试类
public class SingleObjectPattern2 {
public static void main(String[] args) {
//测试
Teacher t1 = Teacher.getInstance() ;
Teacher t2 = Teacher.getInstance() ;
System.out.println(t1== t2) ;
}
}
8.Runtime类
8.1概述
Runtime类:每个Java程序都有自己的运行环境,通过Runtime创建的实例表示:当前系统的运行环境
使用到了单例模式之饿汉式
原码如下:
public class Runtime {
private static Runtime currentRuntime = new Runtime();
private Runtime() {}
public static Runtime getRuntime() { //创建当前类实例
return currentRuntime;
}
}
1) public static Runtime getRuntime() 创建当前类实例
2) public int availableProcessors()取计算机cpu的处理器数量
3) 操作指令:public Process exec(String command) throws IOException
System.out.println(runtime.exec("calc")); //打开计算器
System.out.println(runtime.exec("mspaint")); //打开计算器
4) 关机指令 runtime.exec("shutdown -s -t 300") ; //300秒后关机
9.递归
9.1概述
递归是一种算法:方法调用方法本身的一种现象! 而不是方法嵌套方法
9.2递归思想
1) 必须定义一个方法(函数)
2) 有一定的规律
3) 必须有出口条件(结束条件) ,如果没有结束条件就是死递归!
构造方法不存在递归.
需求:
有一对兔子,从出生后第三个月起每个月产生一对兔子,小兔子长到第三个月后每个月又产生一对兔子;如果兔子都不死,第二十个月兔子的对数是多少?
已知就是前两个月的兔子的对数都是1;
规律:
第一个月:1
第二个月:1
第三个月:2
第四个月:3
第五个月:5
第六个月:8
第七个月:13
...
...
几种方式:
1)数组方式解决
2)递归:定义方法/找规律/找出口条件
/*
* 规律:
* 已知第一个月和第二个月的兔子对数都是1
* 从三个月开始,每个月兔子对数是前两个月之和
*
* 递归方式
* 1)定义方法(第二十个月 :20) ---参数为n
* 2)如果n==1或者n==2 return 1 ; 如果n是第一个月或者n是第二个月,兔子对数1
* 3)如果不是第一个月或者第二个月: 从三个月开始,每个月兔子对数是前两个月之和
*
*/
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) ;
}
}
}
10.IO流---File类
10.1概述
IO之File:描述的一个文件/目录的抽象路径表现形式
10.2构造方法
1) File(File parent, String child) 从父抽象路径名和子路径名字符串创建新的 File实例
2) File(String parent, String child)
3) File(String pathname) :直接描述地址路径
File类的注意点:
- 一个File对象代表硬盘中实际存在的一个文件或者目录。
- File类构造方法不会给你检验这个文件或文件夹是否真实存在,因此无论该路径下是否存在文件或者目录,都不影响File对象的创建。
如果没有带盘符,创建的文件或者文件夹默认就是在当前项目下
//需求:使用File表示E盘下的demo文件夹的a.txt文件
public class FileDemo {
public static void main(String[] args) {
//方式1:File(String pathname)
File f1 = new File("E:\\demo\\a.txt") ; //常用:表示文件或者目录的路径
System.out.println(f1) ;//E:\demo\a.txt
//方式2:File(File parent, String child)
File f2 = new File("E:\\demo") ;
File f3 = new File(f2,"a.txt") ;
System.out.println(f3) ;
//方式3:File(String parent, String child)
File f4 = new File("E:\\demo","a.txt") ;
System.out.println(f4);
}
}
10.3成员方法
创建文件/创建文件夹相关的方法
1) public boolean createNewFile() throws IOException:创建文件,如果文件不存在,就会自动创建,创建了,返回true
2) public boolean mkdir():创建文件夹(目录),如果文件夹不存在,则自动创建;创建成功,返回true,否则false;
3) public boolean mkdirs():如果父目录不存在,会自动创建:针对带多级目录创建
4) public boolean delete(): 删除文件或者目录,如果File表示的目录,删除必须为空目录
5) public boolean renameTo(File dest) 重命名功能
10.4高级功能
1) public File[] listFiles():高级功能之一:获取的某个目录下的所有文件以及文件夹的File数组 (推荐)
2) public String[] list():获取的抽象路径名表示下的所有的文件以及文件夹的字符串数组
public class FileDemo {
public static void main(String[] args) throws IOException {
// 需求:我要在e盘目录下创建一个文件夹demo
File file = new File("e:\\demo");
System.out.println("mkdir:" + file.mkdir());
// 需求:我要在e盘目录demo下创建一个文件a.txt
File file2 = new File("e:\\demo\\a.txt");
System.out.println("createNewFile:" + file2.createNewFile());
需求:要删除当前D盘下所有的以.jpg结尾的文件
方法一:
/* 分析:
* 1)表示d盘
* 2)需要将d盘下的里面的文件以及文件夹的名称都获取到
*
* public File[] listFiles():高级功能之一:获取的某个目录下的所有文件以及文件夹的
* File数组 (推荐)
*
* public String[] list():获取的抽象路径名表示下的所有的文件以及文件夹的字符串数组
*
* 3)判断如果当前File数组对象不为空 ,遍历获取到了每一个File对象
* 3.1)判断当前File对象所表示的抽象路径名是否是一个文件
* boolean isDirectory():判断是否是文件夹
* boolean isFile() :判断是否是文件(此处用它)
* public String getName():获取当前抽象路径名表示的文件或者文件夹的名称
* (File获取功能里面获取名称)
*
* 4)满足是文件而且是一个.jpg结尾的文件-----直接删除这个文件
*
*/
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());
}
}
}
}
}
}
方法二:
文件名称过滤器
/*
* 另外的高级功能:使用文件名称过滤器作为参数传递:
* public String[] list(FilenameFilter filter)
* public File[] listFiles(FilenameFilter filter)
*
* FilenameFilter接口:里面有一个抽象方法
* boolean accept(File dir,String name) :是否将name:文件名称添加抽象类路径名所表示的
* 列表中,取决于返回值true,表示将name添加的文件过滤列表中,否则false,不添加!
*
* 1)表示盘符D盘
* 2)直接获取并同时获取满足条件的内容
* 3)遍历结果即可!
*/
public class FileTest2 {
public static void main(String[] args) {
//表示盘符D盘
File file = new File("D:\\") ;
//获取d盘下的所有的文件以及文件夹的字符串数组,而且里面添加满足的条件
// public String[] list(FilenameFilter filter) :方法的形式参数是一个接口,实际参数需要
//接口的子实现类对象
/* File[] fileArray = file.listFiles(new FilenameFilter() { //文件名称过滤器
@Override
public boolean accept(File dir, String name) {
//是否将name添加的文件列表中,取决于返回值
//创建File对象,表示dir
*//* File file = new File(dir,name) ;
boolean flag1 = file.isFile() ; //是文件
boolean flag2 = file.getName().endsWith(".jpg");
return flag1 && flag2 ;*//*
File destFile = new File(dir,name) ;
return destFile.isFile() && destFile.getName().endsWith(".jpg") ;
}
});*/
File[] fileArray = file.listFiles(//文件名称过滤器
(dir, name)-> {
//是否将name添加的文件列表中,取决于返回值
//创建File对象,表示dir
/* File file = new File(dir,name) ;
boolean flag1 = file.isFile() ; //是文件
boolean flag2 = file.getName().endsWith(".jpg");
return flag1 && flag2 ;*/
File destFile = new File(dir,name) ;
return destFile.isFile() && destFile.getName().endsWith(".jpg") ;
});
for(File f:fileArray){
//f表示 表示满足条件
System.out.println(f.getName()+"---"+f.delete());
}
}
}
10.5 IO流的分类
根据数据的流向分为:输入流 和 输出流。
输入流 :把数据从
其他设备
上读取到内存
中的流。输出流 :把数据从
内存
中写出到其他设备
上的流。按类型: 字节:先有
抽象类
字节输入 :InputStream
字节输出 :OutputStream
字符:后有
字符输入 :Reader
字符输出 :Writer
抽象类 它的子类
InputStream XXXInputStream
OutputStream XXXOutputStream
Reader XXXReader
Writer XXXWriter
什么情况下使用哪种流呢?
如果数据所在的文件通过windows自带的记事本打开并能读懂里面的内容,就用字符流。其他用字节流。如果你什么都不知道,就用字节流
想在当前项目下输出aaa.txt文件出来,里面写入一句话"hello,IO!"
1)创建文件输出流是用于将数据写入到输出流File ----FileOutputStream 是OutputStream的子类
2)写数据:
void write(byte[] b):写入一个字节数组
void write(byte[] b, int off, int len):写入字节数组的一部分
abstract void write(int b ):写入单个字节3)释放资源
释放的是底层的流指向的文件(流对象:底层是非java语言实现,需要创建系统资源)
public class FileOutputStreamDemo {
public static void main(String[] args) throws Exception {
//1)创建文件输出流
//public FileOutputStream(String name) throws FileNotFoundException
//底层创建:创建流对象fos内存执行输出的文件aaa.txt
FileOutputStream fos = new FileOutputStream("aaa.txt") ;
//2)写数据:
//abstract void write(int b ):写入单个字节
// fos.write(97) ;//97---a
// void write(byte[] b):写入一个字节数组
fos.write("hello,IO".getBytes());//将字符串类型转为字节数组
//3)释放资源
//底层:将fos流对象不再执行那个文件了
fos.close() ;
//fos.write(98);//Stream Closed
}
}
10.6 字节输出流--->换行操作
windows操作系统中:写入数据的时候,换行符号 "\r\n"
10.6.1FileOutputStream的构造方法
public FileOutputStream(String pathname,boolean append):如果第二个参数为true,则自动后面追加
10.7 处理异常
开发中,需要处理异常:
1) throws:抛出---抛在方法声明上,谁调用这个方法,谁就是调用者,通过调用者进行处理
2) 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
10.8 字节输入流读数据的方式
10.8.1构造方法:FileInputStream
public FileInputStream(String name) throws FileNotFoundException:创建文件字节输入流对象
成员方法:
read这些方法:都是阻塞式方法:只要文件没有读完,一直等待要读取!
1) abstract int read():一次读取一个字节,返回的读取的字节数
2) int read(byte[] b) :一次读取一个字节数组
1) 一次读取一个字节,将文件内容打印在控制台: 如果文件中有中文,出现乱码 原因就是我们对读取的字节数by----强转(char)by:一个英文对应一个字节,但是中文(idea中:utf-8):一个中文3个字节,拼接不上导致乱码 所以后期:如果是文本文件(使用记事本打开能读的懂)就直接使用字符流打印在控制台上
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") ;
//使用循环思想改进
/* int by = fis.read() ;
while(by!=-1){
System.out.print((char)by);
by = fis.read() ;
}*/
//在优化:将赋值,判断一块使用
//刚开始:还没还是读 字节数0
int by = 0 ;
while((by=fis.read())!=-1){
System.out.print((char)by);
}
//3)释放资源
fis.close() ;
}
}
2) int read(byte[] b) :一次读取一个字节数组
public class FileInputStreamDemo4 {
public static void main(String[] args) throws IOException {
//读取是当前项目下的fis2.txt文件
//创建文件字节输入流对象
FileInputStream fis = new FileInputStream("fis2.txt") ;
//优化代码:一般一次读取一个字节数组:数组的长度:1024或者是1024的整数倍
//最终版代码
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() ;
}
}
10.9 字节缓冲流
10.9.1 BufferedOutputStream:字节缓冲输出流(别名 "字节高效流")
这个流仅仅是在内部提供了一个缓冲区(字节数组),针对文件的输出并且同时写入数据使用的还是底层流OutputStream
构造方法:
public BufferedOutputStream(OutputStream out):构造一个默认的缓冲区大小,通过底层流进行输出(写入数据)
10.9.2 BufferedInputStream: 字节缓冲输入流
构造方法:
public 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();
}
}
10.10 合并流
使用合并流:可以实现a.txt/b.txt/c.txt----->复制到一个文件中
SequenceInpuStream----是InputStream的子类:它这个类只能操作源文件
构造函数:
public SequenceInputStream(Enumeration<? extends InputStream> e)
public SequenceInputStream(InputStream s1,InputStream s2):将两个字节输入流对象指向的文件进行合并
/* 需求:
* 将当前项目下的FileOutputStreamDemo.java/FileOutputStreamDemo2.java
* ----复制到D:\EE_2110\day26\code\\Copy.java
*
* 当两个以上的文件进行复制,
* public SequenceInputStream(Enumeration<? extends InputStream> e)
*/
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();
}
}
10.11 字符输入流:Reade
字符输入流:Reader:抽象类
子类: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() ;
}
}
10.12 字符输出流:Writer
字符流出现的原因:在字节流基础上,它会提供字符集,解决乱码问题!
* 如果使用记事本打开一个文件,(文本文件)---优先采用的就是字符流!
* 图片/音频/视频------>只能使用字节流
Writer:抽象类----->子类 :字符转换输出流OutputStreamWriter:里面包装的是字节流
构造方法
1) OutputStreamWriter(OutputStream out) :使用平台默认的编码字符集进行编码--->写入数据
2) OutputStreamWriter(OutputStream out,String charsetName )使用指定的编码字符集进行编码--->写入数据
写的功能:
1) write(int ch):写一个字符
2) write(char[] chs):写字符数组
3) write(char[] chs,int index,int len):写字符数组的一部分
4) writer(String str):写字符串
5) 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() ;
}
}
10.13 FileReader/FileWriter
FileReader(String pathname):使用平台默认字符集:进行解码
FileWriter(String pathname)使用平台默认字符集:进行编码
10.14 字符缓冲流
10.14.1字符缓冲输入流
BufferedReader:字符缓冲输入流 提供默认的缓冲区大小 缓冲区足够大
BufferedReader(Reader in)
特有功能:
String readLine() 读取一行内容
10.14.2字符缓冲输出流
BufferedWriter:字符缓冲输出流 (字符流的高效流)
仅仅提供缓冲区
BufferedWriter(Writer out) :提供默认缓冲区大小,默认值足够大:8192个长度(底层是一种字符数组)
特有功能:
public void newLine() throws IOException :写入行的分隔符号
10.14 系列化和反序列
什么是序列化---->ObjectOutputStream
就是在将Java对象(实体类:User/Product/Order...)变成一种流数据,他们里面的数据存储流中,就可以在网络(服务器集群中:共享)中传输!
构造方法: public ObjectOutputStream(OutputStream out)
成员方法: public final void writeObject(Object obj) throws IOException:写入一个实体对象
什么是反序列化---->ObjectInputStream
就是将流数据(存储的一些相关的实体类数据)-----还原成Java对象!
构造方法:public ObjectInputStream(InputStream in) throws IOException:将文件中的数据,进行读取
成员方法: public final Object readObject()throws IOException,ClassNotFoundException
public class Person implements Serializable {
//固定的序列化版本Id
private static final long serialVersionUID = 6815664471916807752L;
public transient String name ;//transient可以标记某个成员变量不参与序列化
int age ;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class ObjectStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// write() ;
read() ;
}
//反序列
private static void read() throws IOException, ClassNotFoundException {
//创建反序列化流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("oos.txt")) ;
// public final Object readObject()
Object object = ois.readObject();
System.out.println(object) ;
//关闭资源
ois.close() ;
}
//序列化
private static void write() throws IOException {
//将Person对象中里面成员信息永久存储到指定文件中:序列化,前提条件:Person类型必须实现标记接口序列化!
//文件中乱码,不影响读取,通过反序列化---可以还原回来
//创建序列化流对象
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("oos.txt")) ;
//创建Person类对象
Person p = new Person() ;
p.name = "高圆圆" ;
p.age = 42 ;
//public final void writeObject(Object obj)
oos.writeObject(p) ;
//释放资源
oos.close() ;
}
}
java.io.NotSerializableException: com.qf.objectstream_01.Person:未实现序列化接口的异常
只有支持java.io.Serializable接口的对象才能写入流中.需要在Person类实现接口(标记接口)
刚才已经实现了反序列:将流中数据---读取出来,解析成对象了
但是,当更改了Person类的成员的时候,再次反序列化报错
java.io.InvalidClassException: com.qf.objectstream_01.Person;
local class incompatible: stream classdesc serialVersionUID = 5810188958553179434,
local class serialVersionUID = -45582393571180500
导致类的数据在文本文件中的前后的版本ID不一样 (序列化的时候---需要给类进行签名!)transient可以标记某个成员变量不参与序列化
▲设置后,选中类按alt+enter键生成对应的序列化版本Id
10.15 Propertis类
10.15.1概述
java.util.Propertis:属性集合类(属性列表)是一个Map,但是没有泛型,键和值都是String
10.15.2特有功能:
public Object setProperty(String key,String value):添加键和值
public Set<String> stringPropertyNames():获取所有键的集合
public String getProperty(String key):获取键对应的值
public class PropertiesDemo {
public static void main(String[] args) {
//public PropertiesTest()
//创建属性集合类对象
Properties properties = new Properties() ;
/*//put方法
properties.put("张三","30") ;
properties.put("高圆圆","42") ;
properties.put("张佳宁","31") ;
properties.put("张三丰","50") ;
//遍历:keySet()---->获取所有的键
Set<Object> set = properties.keySet();
for(Object obj :set){
//通过键获取值
Object value = properties.get(obj);
System.out.println(obj+"---"+value);
}*/
//添加元素
properties.setProperty("1","张三") ;
properties.setProperty("2","李四") ;
properties.setProperty("3","王五") ;
properties.setProperty("4","赵六") ;
// System.out.println(properties);
Set<String> keySet = properties.stringPropertyNames();
for(String key:keySet){
String value = properties.getProperty(key);
System.out.println(key+"---"+value);
}
}
}
10.16 针对文本文件的读写复制操作有几种?
FileInputStream/FileOutputStream:一次读取一个字节
FileInputStream/FileOutputStream:一次读取一个字节数组
BufferedInputStream/BufferedOutputStream:一次读取一个字节
dInputStream/BufferedOutputStream:一次读取一个字节数组
InputStreamReader/OutputStreamWriter:解决乱码(编码和解码必须字符集统一:采用默认平台字符集)
一次读取一个字符
InputStreamReader/OutputStreamWriter: 一次读取一个字符数组
FileReader/FileWriter:一次读取一个字符
FileReader/FileWriter:一次读取一个字符数组
BuffferedRreader/BufferedWriter:一次读取一个字符
BuffferedRreader/BufferedWriter:一次读取一个字符数组
BuffferedRreader/BufferedWriter:一次读取一行/每次写一行然后换行,然后刷新
11. 网络编程
11.1概述
网络编程:就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换。
11.2网络通信三要素
IP地址:InetAddress 网络中设备的标识,不易记忆,可用主机名
端口号: 用于标识进程的逻辑地址,不同进程的标识
传输协议: 通讯的规则 常见协议:TCP,UDP
比如说:我要跟你说话.
第一个条件:我要先找到你 (IP)
第二个条件:你得有接收数据的地方 耳朵 (端口)
第三个条件:我跟你说话,你能接收到,咱按什么方式接收啊,我说英文你懂吗,说韩文你懂吗,不懂是吧,所以我还是说中文把. (协议)
port:端口号:360软件---可以查看计算机中所有软件的端口!
范围:0-65535
0-1024属于保留端口
常见端口:tomcat服务器 默认:8080 (极域软件:端口号8080)
mysql:3306
redis(NOSQL数据库)客户端端口:6575
key:value
11.3 IP地址
java.net.InetAddress :表示ip地址
windows系统:查看ipconfig
Linux系统:查看ifconfig
静态方法:
public static InetAddress getByName(String host)throws UnknownHostException通过主机名称获取ip地址对象
InetAddress的其他成员方法
public String getHostName():获取ip地址对象所描述的主机的名称(计算机名称)
public String getHostAddress():获取ip地址:以字符串形式展示的
public class InetAddressDemo {
public static void main(String[] args) throws UnknownHostException {
//DESKTOP-Q62EUJH:我自己的计算机的主机名称
//public static InetAddress getByName(String host)
// InetAddress inetAddress = InetAddress.getByName("DESKTOP-Q62EUJH");
InetAddress inetAddress = InetAddress.getByName("10.12.159.190");
//System.out.println(inetAddress) ;
//public String getHostAddress():
//public String getHostName()
System.out.println(inetAddress.getHostName());//主机名称:DESKTOP-Q62EUJH
System.out.println(inetAddress.getHostAddress());//10.12.159.190
}
}
11.3 协议
协议:
网络通信协议
TCP/IP协议
UDP协议UDP
1)属于不可靠协议
2)不需要建立连接通道,所以执行效率高,但是安全性低!
3)发送内容的时候,有大小限制!将数据封包传输,数据包最大64k。TCP/IP协议
1)属于可靠协议
2)需要建立连接通道,
服务器端监听客户端连接,如果客户端没有跟服务器端口进行绑定,服务器一直等待,执行效率低,但是安全性高
3)发送内容的时候,数据大小没有限制!
11.3.1 UDP协议
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() ;
//2)创建数据报包---- >包含ip和端口+里面的数据
//DatagramPacket
//public DatagramPacket(byte[] buf ,int length,InetAddress address,int port)
byte[] bytes = "hello,UDP,我来了".getBytes() ;
int length = bytes.length ;
InetAddress inetAddress = InetAddress.getByName("10.12.159.190");
int port = 6666 ;
DatagramPacket dp = new DatagramPacket(bytes,length,inetAddress,port) ;
//3)发送
//public void send(DatagramPacket p)
ds.send(dp) ;
//4)释放资源
ds.close() ;
}
}
UDP接收端的代码实现
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(6666) ;
//创建一个接收容器: 自定义一个缓冲区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():获取缓冲区中的长度
byte[] bytes2 = dp.getData();
int length2 = dp.getLength();
//获取实际内容
String str = new String(bytes2,0,length2) ;
//获取ip地址
// public InetAddress getAddress()
String ip = dp.getAddress().getHostAddress() ;
System.out.println("data from "+ip +"content is :"+str) ;
//释放资源
ds.close() ;
}
}
UDP发送端不断的键盘录入数据,接收端的接收数据并且将数据展示出来:
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() ;
}
}
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() ;
}
}
UDP的发送端和接收端在一个窗口中进行聊天:
public class SendThread implements Runnable {
private DatagramSocket ds ;
public SendThread(DatagramSocket ds) {
this.ds = ds ;
}
@Override
public void run() {
try {
//创建一个BufferedReader流对象:键盘录入
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in)) ;
String line = null ;
while((line=br.readLine())!=null){
//自定义结束条件
if("886".equals(line)) {
break;
}
DatagramPacket dp = new DatagramPacket(line.getBytes(),line.getBytes().length,
InetAddress.getByName("10.12.159.190"),8888) ;
//3)发送
//public void send(DatagramPacket p)
ds.send(dp) ;
}
} catch (IOException e) {
e.printStackTrace();
}
//4)释放资源
ds.close() ;
}
}
public class ReceiveThread implements Runnable {
private DatagramSocket ds ;
public ReceiveThread(DatagramSocket ds) {
this.ds = ds ;
}
@Override
public void run() {
try {
while(true){
//创建一个接收容器: 自定义一个缓冲区byte[]
byte[] bytes = new byte[1024] ;
int length = bytes.length ;
//(创建数据包报)
DatagramPacket dp = new DatagramPacket(bytes,length) ;
//3)接收
// public void receive(DatagramPacket p)
ds.receive(dp);
//获取实际内容
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) ;
}
} catch (IOException e) {
e.printStackTrace();
}
//释放资
//ds.close() ;
}
}
public class ChatRoomDemo {
public static void main(String[] args) throws SocketException {
//主线程中
//分别此处创建发送端的Socket以及接收端的Socket
//发送端的Socket
DatagramSocket sendDs = new DatagramSocket() ;
//接收端的Socket
DatagramSocket receiveDs = new DatagramSocket(8888) ;
//创建发送端的资源类对象
SendThread st = new SendThread(sendDs) ;
//创建接收端的资源类对象
ReceiveThread rt = new ReceiveThread(receiveDs) ;
//创建线程
Thread t1 = new Thread(st) ;
Thread t2 = new Thread(rt) ;
t1.start() ;
t2.start() ;
}
}
11.3.2 TCP协议
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() ;
}
}
TCP/IP服务器端的步骤实现
1)创建服务器端的Socket对象
2)监听客户端的连接 ---一旦监听到了,就获取到那个客户端的Socket
3)获取连接的客户端的通道内的输入流,读取数据
4)展示数据
5)释放资源
public class ServerDemo {
public static void main(String[] args) throws IOException {
//1)创建服务器端的Socket对象
//public ServerSocket(int port) throws IOException
ServerSocket ss = new ServerSocket(10010) ;
System.out.println("正在监听客户端连接...");
//2)监听客户端的连接 ---一旦监听到了,就获取到那个客户端的Socket
//public Socket accept() throws IOException:阻塞式方法,在连接之前,一直处于阻塞状态
Socket socket = ss.accept();
System.out.println("客户端已连接");
//3)获取连接的客户端的通道内的输入流,读取数据
//public InputStream getInputStream()
InputStream inputStream = socket.getInputStream();
//4)读:一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = inputStream.read(bytes);
String str = new String(bytes,0,len) ;
//public InetAddress getInetAddress():获取ip地址
String ip = socket.getInetAddress().getHostAddress();
//5)展示数据
System.out.println("data from "+ip +",content is:"+str);
//释放资源
ss.close() ;
}
}
1)TCP客户端发送数据,服务器端接收消息,并反馈:
public class ClientDemo {
public static void main(String[] args) throws IOException {
//1)创建客户端Socket
Socket socket = new Socket("10.12.159.190",8888) ;
//2)获取通道内的输出流
OutputStream outputStream = socket.getOutputStream();
//3)写
outputStream.write("hello,我来了".getBytes());
//需要读取服务器端的反馈的消息
//获取通道内的输入流对象
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024] ;
int len = inputStream.read(bytes);
System.out.println("serverMsg:"+new String(bytes,0,len));
//4)释放
socket.close() ;
}
}
public class ServerDemo {
public static void main(String[] args) throws IOException {
//1)服务器端的Socket
ServerSocket ss = new ServerSocket(8888) ;
//2)监听
Socket socket = ss.accept();
//3)获取通道内的输入流
InputStream inputStream = socket.getInputStream() ;
//4)一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int length = inputStream.read(bytes);
//展示客户端发的消息
String clientMsg = new String(bytes,0,length) ;
System.out.println("client-Msg:"+clientMsg);
//服务器端反馈
//获取通道内的输出流写过去
OutputStream outputStream = socket.getOutputStream();
outputStream.write("数据我已接收,感谢".getBytes());
//释放
ss.close() ;
}
}
2)TCP客户端不断键盘录入数据,服务器端不断接收数据,并展示,服务端不关闭:
public class ClientDemo {
public static void main(String[] args) throws IOException {
//创建socket
Socket socket = new Socket("10.12.159.190",12306) ;
//使用IO流的键盘录入
/* InputStream in = System.in ;
Reader reader = new InputStreamReader(in) ;
BufferedReader br = new BufferedReader(reader) ;
System.out.println("请您输入一个数据:");
String line = br.readLine();
System.out.println(line);*/
//一步走
BufferedReader br = new BufferedReader(new InputStreamReader(System.in )) ;
System.out.println("请你输入消息: ");
String line = null ;
while((line=br.readLine())!=null){
//自定义结束条件
if("over".equals(line)){
break ;
}
//获取通道内的输出流对象
OutputStream outputStream = socket.getOutputStream();
outputStream.write(line.getBytes());
}
//释放资源
socket.close() ;
}
}
public class ServerDemo {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(12306) ;
System.out.println("服务器正在客户端连接...");
Socket socket = ss.accept();
//监听
//获取通道内的输入流对象
InputStream inputStream = socket.getInputStream();
while(true){
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = inputStream.read(bytes);
//展示
String str = new String(bytes,0,len) ;
String ip = socket.getInetAddress().getHostAddress() ;
System.out.println("data from "+ip+",conent is-->"+str);
}
//服务端不关闭;
}
}
3)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() ;
}
}
4)TCP客户端的图片,服务器端将文件复制,到指定文件中(当前项目下):
public class ClientDemo {
public static void main(String[] args) throws IOException {
//创建socket
Socket socket = new Socket("10.12.159.190",12306) ;
//BuferedInputStream 读取d盘下的图片文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("d:\\高圆圆.jpg")) ;
//封装通道内的字节输出流
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()) ;
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = 0 ;
while((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len) ;
//刷新
bos.flush() ;
}
//释放资源
bis.close();
socket.close() ;
}
}
//发现问题:复制过来的图片缺失,文件大小不一致,图片文件本身就存在缓存数据,需要通过
//字节输出流,强制刷新流中缓存的字节数
//public void flush()
public class ServerDemo {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(12306) ;
//监听
Socket socket = ss.accept();
//读取通道内过来的数据:封装通道内的输入流
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream()) ;
//输出到当前项目mm.jpg
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("mm.jpg")) ;
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = 0;
while((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
//强制刷新
bos.flush() ;
}
bos.close() ;
ss.close() ;
}
}
5) TCP客户端的文本文件,服务器端将文件复制到指定文件(当前项目下),加入服务器端反馈
发现问题:
文件是已经完毕了,但是客户端并没有收到服务器端的反馈,服务器也没关闭; 出现了互相阻塞了
解决方案:
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() ;
}
}
12. Mysql
12.1数据库
关系型数数据库:写响应的sql语句
数据库:存储数据的仓库
一种关系型数据:mysql,oracel,SqlServer,Sqllite...mysql:轻量级 :中小型企业使用居多 (免费的)
oracle:大型企业使用的居多 (收费的) 买人家的数据库软件+买人家第三方服务+买服务器...
银行类的企业Oralce
12.2数据库的定义语句
-- 查询当前所有的数据库名称;
--show databases;
+--------------------+
| Database |
+--------------------+
| information_schema | 自带mysql的默认配置
| mysql | 里面包含了user 用户表 root用户:管理员
| performance_schema | mysql性能配置
| sakila | 下面默认的练习库
| sys |
| world |
+--------------------+
mysql8.0默认的字符集就是utf8 (在dos控制台上)
-- 创建自己的数据库
-- create database 库名;
create database ee_2110;
-- 判断再去创建:create database if not exists 库名;
create database if not exists ee_21111;
-- 删除库
-- drop database 库名
drop database ee_21111;
-- drop database if exists 库名;
drop database if exists ee_2106;
-- 查询创建数据库的默认的字符集格式
-- show create database ee_2110;
ee_2110 | CREATE DATABASE `ee_2110` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci
--mysql> show variables like '%character%'; 默认查询当前数据库中所有的带字符集的字符集格式
如果在dos控制台上:操作表(DML语句)出现了中文乱码
改动character_set_client--变成utf8
character_set_results -- 变成utf8
--mysql> set character_set_results=utf8;
Query OK, 0 rows affected, 1 warning (0.00 sec)--mysql> set character_set_results=utf8;
Query OK, 0 rows affected, 1 warning (0.00 sec)--mysql> show variables like '%character%';
修改数据库的默认的字符集
--mysql> alter database myjavaee character set gbk ;
Query OK, 1 row affected (0.01 sec)myjavaee | CREATE DATABASE `myjavaee` /*!40100 DEFAULT CHARACTER SET gbk */ /*!80016 DEFAULT ENCRYPTION='N' */
12.3DDL之表的增删查改
-- 创建表的前提,先选择库,进入到库中
mysql> use ee_2110;
Database changed
mysql>-- 查询这个库中的所有的表
mysql> show tables;
Empty set (0.00 sec) -- 为空
mysql>/*
创建表的语法
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
*/-- 创建了一个学生表
mysql> create table student(
-> id int,
-> name varchar(20),
-> gender varchar(5),
-> address varchar(50),
-> email varchar(50)
-> );
Query OK, 0 rows affected (0.04 sec)-- 查询表的结构
-- desc 表名;
mysql> desc student;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
| gender | varchar(5) | YES | | NULL | |
| address | varchar(50) | YES | | NULL | |
| email | varchar(50) | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+
5 rows in set (0.01 sec)-- 修改表的字段名称
-- alter table 表名 change 旧字段名称 新字段名称 数据类型;
mysql> alter table student change gender sex varchar(5) ;
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc student;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
| sex | varchar(5) | YES | | NULL | |
| address | varchar(50) | YES | | NULL | |
| email | varchar(50) | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+
5 rows in set (0.01 sec)-- 修改字段类型
-- alter table 表名 modify 字段名称 新的字段类型;
mysql> alter table student modify address varchar(100);
Query OK, 0 rows affected (0.08 sec)
Records: 0 Duplicates: 0 Warnings: 0mysql> desc student;
+---------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
| sex | varchar(5) | YES | | NULL | |
| address | varchar(100) | YES | | NULL | |
| email | varchar(50) | YES | | NULL | |
+---------+--------------+------+-----+---------+-------+
5 rows in set (0.00 sec)-- 添加一个新的列(新的字段)
-- alter table 表名 add 新的字段名称 类型;
mysql> alter table student add socre double(3,1);
Query OK, 0 rows affected, 1 warning (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 1mysql> desc student;
+---------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
| sex | varchar(5) | YES | | NULL | |
| address | varchar(100) | YES | | NULL | |
| email | varchar(50) | YES | | NULL | |
| socre | double(3,1) | YES | | NULL | |
+---------+--------------+------+-----+---------+-------+
6 rows in set (0.01 sec)-- 修改表:将某一个字段删除
-- alter table 表名 drop 字段名称;
mysql> alter table student drop email;
Query OK, 0 rows affected (0.05 sec)
Records: 0 Duplicates: 0 Warnings: 0mysql> desc student;
+---------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
| sex | varchar(5) | YES | | NULL | |
| address | varchar(100) | YES | | NULL | |
| socre | double(3,1) | YES | | NULL | |
+---------+--------------+------+-----+---------+-------+-- 复制一张新的表跟以前的表结构相同
-- create table 新的表名 like 以前的表名;
mysql> create table teacher like student ;
Query OK, 0 rows affected, 1 warning (0.05 sec)-- 修改的表的名称
-- alter table 以前的表名 rename to 新的表名;
mysql> alter table teacher rename to tea;
Query OK, 0 rows affected (0.03 sec)-- 删除表
-- drop table 表名 ;
-- drop table if exists 表名;
mysql> drop table tea;
Query OK, 0 rows affected (0.04 sec)
1) -- mysql的单行注释
2) /*
多行注释
*/3) #添加一些标记注释
-- 数据库的DML语句:操作语句 (操作表的记录:添加/删除/查询/修改)
-- 给表中插入数据insert into语句
-- 语法1: 一次性插入全部字段值 insert into 表名 values(字段名称1的值,字段名称2的值,....) ;
例: INSERT INTO student VALUES(1,'张三','男','西安市鄠邑区','1970-03-27') ;-- 语法2:插入部分字段
-- insert into 表名(字段名称1,字段名称2...) values(值1,值2,值3....) ;
例: INSERT INTO student(id,NAME,sex,address) VALUES(3,'刘','男','西安市') ;-- 语法3:一次性可以插入多条记录(不仅适用于插入全表字段/也可以用于部分字段插入)
-- insert into 表名 values(值1,值2,值3...),(值1,值2,值3...).... ;例: INSERT INTO student VALUES
(4,'王三','男','北京市昌平区','1980-04-30'),
(5,'马云','男','杭州市','1970-6-21'),
(6,'马化腾','男','深圳市','1970-7-30') ;-- 注意事项:插入字段值的时候,必须和当前字段的先后顺序对应上
例:INSERT INTO student VALUES(7,'男','赵又廷','西安市雁塔区','1975-11-20') ;
-- 不合法:必须按照先后顺序-- 修改表的语句update
-- 修改的时候需要使用where 条件语句
-- update 表名 set 字段名称 = 值 where 其他字段:后期都是id = 值 ;(修改单个字段)-- 修改刘的出生日期 1982-06-30
例: UPDATE student SET birthday = '1982-06-30' WHERE id = 3 ;-- 语法2:修改多个字段
-- update 表名 set 字段名称1 = 值1 ,字段名称2 = 值2,... where 条件语句
-- 将id为7的学生的姓名和性别都要更改
例:UPDATE student SET NAME = '赵又廷' ,sex = '男' WHERE id = 7 ;-- 批量修改 (不建议使用)
-- update 表名 set 字段名称 = 值;
例:UPDATE student SET address = '西安市雁塔区' ;-- 删除语句
-- delete from 表名 where 带条件删除
-- 删除id为7这条记录
例: DELETE FROM student WHERE id = 7 ;-- 如果不带条件,删除的是全表记录
-- delete from 表名
例:DELETE FROM student ;-- 另一种删除:删除全表记录
-- truncate table 表名;
例: TRUNCATE TABLE student ;/*
TRUNCATE TABLE 表名 ;它和 delete from 表名 ; 两者有什么区别
delete from 表名 ;仅仅只是删除表的记录,
不会删除这个表的结构,那么针对自增长约束的字段不影响
后者TRUNCATE TABLE 表名 ;删除了表,
它重新创建一张一模一样的空表,(表的结构存在)
前一些自增长的主键约束(数据库约束)者这个语法它会将影响*/
DROP TABLE student ;-- 重新创建一张学生表,并且id编号,带一个自增长主键约束
CREATE TABLE student(
id INT PRIMARY KEY AUTO_INCREMENT,-- id 自增长主键(唯一且非空)
NAME VARCHAR(20),
sex VARCHAR(5) ,
address VARCHAR(50),
birthday DATE
);INSERT INTO student(NAME,sex,address,birthday) VALUES('高圆圆','女','西安市','1979-02-27') ;
INSERT INTO student(NAME,sex,address,birthday) VALUES
('刘三','男','西安市','1981-02-27'),
('王四','男','西安市雁塔区','1995-10-30'),
('张五','女','北京市','1990-10-30'),
('李云','男','北京市','1981-10-30');-- delete from 表名
DELETE FROM student ;
-- truncate table 表名;
TRUNCATE TABLE student ;
-- 最基本的查的语句:查询全表 :
-- 开发中,不能写*,将全部字段名称写上面
SELECT * FROM student ;ALTER TABLE student RENAME TO stu ;
SHOW TABLES ;-- 创建一张学生表
CREATE TABLE student (
id INT, -- 编号
NAME VARCHAR(20), -- 姓名
age INT, -- 年龄
sex VARCHAR(5), -- 性别
address VARCHAR(100), -- 地址
math INT, -- 数学
english INT -- 英语
);
INSERT INTO student(id,NAME,age,sex,address,math,english) VALUES
(1,'马云',55,'男',' 杭州',66,78),
(2,'马化腾',45,'女','深圳',98,87),
(3,'马',55,'男','香港',56,77),
-- 查询语法:DQL语句 (数据库查询语句是最多的!)
-- select 全部字段名称 from student ;
-- 如果自己测试查询可以使用 *去代替所有(通配符号)
SELECT * FROM student ;-- 查询语句:查询指定字段
SELECT
id,
NAME,
age,
address
FROM
student ;
-- 查询语句:查询指定字段的时候指定别名 字段名称后面 as 别名名称 (as可以省略)
SELECT
id AS '编号',
NAME AS '姓名',
age AS '年龄',
sex AS '性别',
address AS '地址',
math AS '数学成绩',
english '英语成绩' -- as 省略了
FROM
student;
-- 一般查询时候,表的名称一旦很长,给表起别名;访问字段使用表的别名.字段名称 (多表查询使用多)
SELECT
s.`id` '编号',
s.`name` '姓名',
s.`age` '年龄',
s.`address` '地址'
FROM
student s; -- 给表起了个别名 s (as省略可以不写)
-- 查询地址这个字段
SELECT
address '地址'
FROM
student ;
-- 查询字段的时候去重
SELECT
DISTINCT(address) -- 数据库的DISTINCT(字段名称),可以实现字段去重
FROM student ;-- 查询所有的学生的数学成绩和英语成绩的总分
SELECT
id '学号',
NAME '姓名',
(math+english) '总分'
FROM
student ;-- 两个数据类型一致:int,求和 举例:英语和数学字段;
-- 如果有一个字段的值位null(空值),没有意义,数学+NULL = NULL
-- 数据库的函数 ifnull(字段名称,预期值) ;SELECT
id '学号',
NAME '姓名',
math+IFNULL(english,0) '总分'
FROM
student ;
SELECT * FROM student ;
-- 上面的基本查询语句
-- DQL语句其他查询 高级查询
-- where 条件后面跟表达式(关系运算符/逻辑运算符 || && )