week_06_动态代理,工厂方法,单例,File,IO,网络编程,反射,数据库语句

动态代理

  • 动态代理核心思想:
    再程序执行过程中通过一些特殊的方式产生代理
  • jdk动态代理:
    前提必须有一个接口
  • java.lang.reflect.Proxy:提供了创建动态代理类和实例的静态方法
  • public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
    参数1:当前针对该接口的类加载器
    参数2:代理类实现的接口列表的字节码文件对象
    参数3:基于代理的处理程序 接口
    针对用户数据库访问接口层
public interface UserDao {
    /**
     * 添加用户
     */
    void add() ;

    /**
     * 修改用户
     */
    void update() ;

    /**
     * 删除用户的功能
     */
    void delete() ;

    /**
     * 查询用户
     */
    void findAll() ;

}

针对用户的数据库访问接口实现层

public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        System.out.println("添加用户");
    }

    @Override
    public void update() {
        System.out.println("修改用户");
    }

    @Override
    public void delete() {
        System.out.println("删除用户");
    }

    @Override
    public void findAll() {
        System.out.println("查询所有用户");
    }
}

自定义一类来实现 代理角色的产生 (InvocationHandler:基于代理的处理接口)

public class MyInvocationHandler  implements InvocationHandler {


        //成员变量位置定义Object类型:对任意Java类型产生代理角色 (当前某个具体的实例)
    private Object target ;
    public MyInvocationHandler(Object target){
        this.target = target ;
    }
    /**
     *
     * @param proxy  代理实例
     * @param method  调用基于接口的代理方法
     * @param args   当前传递的实际参数
     * @return   返回代理角色
     * @throws Throwable  代理过程中可能产生代理异常
     *
     *
     */

    //method底层调用方法 invoke(当前类的实例,实际参数 args)
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("权限校验");

        Object returnObj = method.invoke(target, args);

        System.out.println("日志记录");


        return returnObj; //代理角色对象
    }
}

测试类

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

        //直接:通过接口多态:创建接口对象
        UserDao ud = new UserDaoImpl() ;  //ud产生代理

        ud.add();
        ud.delete();
        ud.update();
        ud.findAll();

        //需求: 不仅实现自己的主要功能:add,update,delete,findAll() ...
        //而且还需要在每一个功能使用之前,先进行权限校验,然后使用功能,需要产生日志记录
        System.out.println("-------------------------------------");

        UserDao ud2 = new UserDaoImpl2() ;
        ud2.add();
        ud2.update();
        ud2.delete();
        ud2.findAll();

        System.out.println("------------------------------------------");
        //通过jdk动态代理反射来完成代理角色的产生
        //Class --->public Class<?>[] getInterfaces()
        //创建基于代理处理接口的子实现类对象
        MyInvocationHandler invocationHandler = new MyInvocationHandler(ud) ;
        UserDao proxyClass = (UserDao) Proxy.newProxyInstance(UserDao.class.getClassLoader(),
              new Class[]{UserDao.class}, invocationHandler);
        proxyClass.add();
        proxyClass.delete();
        proxyClass.update();
        proxyClass.findAll();


    }
}

工厂方法模式

  • 优点:具体的动物创建,交给类工厂类来实现 (面向接口编程),便于功能维护(代码维护)
  • 弊端:代码量非常大.如果有新的类中增加,那么还必须提供对应具体的工厂类创建当前类实例!

Animal(父类)

public abstract class Animal {
    public abstract  void eat() ;
    public abstract  void sleep() ;
}

Cat,Dog(子类)

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("狗躺着睡觉...");
    }
}

Factory(工厂接口)

public interface Factory {
    public Animal createAnimal() ;
}

CatFactory,DogFactory(猫类工厂,狗类工厂)

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 class FactoryPattern {
    public static void main(String[] args) {

        //没有工厂方法之前:
        //使用抽象类多态
        Animal a = new Dog() ;
        a.eat();
        a.sleep();
        System.out.println("-------------------------------");
        a = new Cat() ;
        a.eat();
        a.sleep();

        //使用工厂方式模式来操作
        System.out.println("-------------------------------------");
        Factory factory = new DogFactory() ;
        Animal animal = factory.createAnimal();
        animal.eat();
        animal.sleep();

        System.out.println("---------------------------------------");
        factory = new CatFactory() ;
        Animal a2 = factory.createAnimal();
        a2.eat();
        a2.sleep();
    }
}

单例模式

  • 单例模式:
    始终在内存中创建一个实例

饿汉式

  • 饿汉式:永远不会出现问题的单例模式(最简单的一种单例)
  • 饿汉式
    1)构造方法私有:保证外界不能直接创建当前类对象
    2)在当前类的成员位置:创建当前类实例
    3)提供一个静态的功能,返回值就是当前类本身(需要当前类的实例)
public class Student {
    //成员变量
   // public  static Student s = new Student() ; //当前类实例
    private  static Student s = new Student() ; //当前类实例 (静态实例变量)
    private Student(){}

    //静态功能
    //返回是当前类本身
    public static Student getStudentInstance(){
        return s;
    }
}
public class Single_Pattern_01 {
    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());
        //class  com.qf.single_pattern.Student

        //调用公共方法
        Student s1 = Student.getStudentInstance();
        Student s2 = Student.getStudentInstance();
        Student s3 = Student.getStudentInstance();
        Student s4 = Student.getStudentInstance();
        System.out.println(s1==s2);
        System.out.println(s1==s3);
        System.out.println(s1==s4);
       // Student.s = null ; // 赋值null,堆内存不开辟空间(外界可以更改当前类地址值)
        //当前这个成员变量的实例s应该被私有修饰(保证安全性)
    }
}

懒汉式

  • 懒汉式:可能出现问题的一种单例模式
    1)构造方法私有化
    2)在当前成员变量的位置声明变量:数据类型就是当期类 (私有的,静态的)
    3)提供静态功能,返回值还是当前类本身,
    判断如果当前没有给当前类型变量为null,直接new 当期类的实例
    如果不为null,就返回当前类的变量!
    延迟加载或者懒加载----出现安全问题
public class Worker {

    //成员变量位置声明变量w
    private static Worker w;//默认值null
    //构造方法私有化
    private Worker() {} //外界不能直接new 对象

    //提供静态功能,返回值是当前类本身

    //w1,w2,w3 互相抢占getWorkerIntance的内容---会造成安全问题
   /*
    public static Worker getWorkerIntance(){
        //w1
        //先判断w变量为null
        if(w == null){ //当为null
            w = new Worker() ; //如果当前w变量为null,创建一个新的对象,返回
            return w;
        }
        return w ;
     }
    */

   //改进:同步代码块---->静态的同步方法
    public synchronized static Worker getWorkerIntance(){ //锁对象:Worker.class
       // synchronized (w){
            //w1
            //先判断w变量为null
            if(w == null){ //当为null
                w = new Worker() ; //如果当前w变量为null,创建一个新的对象,返回
                return w;
            }
            return w ;
        //}

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

        //使用Worker第一次调用getInstance方法
        Worker w1 = Worker.getWorkerIntance();   //第一个用户操作这个方法----属于一个线程
        //第二次
        Worker w2 = Worker.getWorkerIntance();      //第二个用户操作---->属于一个线程
        System.out.println(w1==w2);
        Worker w3 = Worker.getWorkerIntance();        //第三个用户操作--->属于一个线程
        System.out.println(w1==w3);
    }
}

Runtime

  • Runtime:当前获取的当前本地运行环境的实例
Runtime成员方法名字方法意思
public int availableProcessors()获取本地计算机的处理器的数量
public Process exec(String command)开启某个软件的进程(参数为字符串命令)
public class RuntimeDemo {
    public static void main(String[] args) throws IOException {

        //获取当前类的实例:Runtime  利用单例模式
        Runtime runtime = Runtime.getRuntime();
        System.out.println(runtime.availableProcessors());
        Process notepad = runtime.exec("notepad");
        System.out.println(runtime.exec("QQ"));

        //利用关机
        //exec("shutdown -s"):立即关机
        //exec("shutdown -s -t 300"):立即关机
        //exec("shutdown -a"):取消关机

        System.out.println(runtime.exec("shutdown -s -t 300"));
        System.out.println(runtime.exec("shutdown -a"));

    }
}

File

  • File 文件和目录(文件夹)路径名的抽象表示。

File的构造方法

构造方法名字方法意思
File(String pathname)参数就是指定的路径/如果没有指定路径(默认是在当前项目下
File(File parent, String child)从父抽象路径名和子路径名字符串创建新的 File实例。
public class FileDemo {
    public static void main(String[] args) throws IOException {
        //表示:E盘下的demo文件夹中的a.txt文件
        //File(String pathname) 方式1 (推荐)
       // File file = new File("e://demo//a.txt") ;只是表示这个路径,如果创建a.txt文件,系统找不到指定路径

        File file = new File("D:\\EE_2106\\day25\\code\\a.txt"); //绝对路径
        File file2 = new File("aaa.txt");//没有带路径,就默认在当前项目下(相对路径)
        File file3 = new File("D:\\EE_2106\\day25\\code\\demo") ;
        File file4 = new File("aaa\\bbb\\ccc\\ddd") ;

        //File(File parent, String child) 方式2
      /*  File file2 = new File("E://demo") ;
        File file3 = new File(file2,"a.txt") ;

        //File(String parent, String child):方式3
        File file4 = new File("E://demo","a.txt") ;*/
       // public boolean createNewFile()
        System.out.println(file.createNewFile());
        System.out.println(file2.createNewFile());
        System.out.println(file3.mkdir());
        System.out.println(file4.mkdirs());
        System.out.println(file3.delete());
        System.out.println(file.delete());
        }
   }

File的成员方法(创建/删除/重名)

成员方法名字方法意思
public boolean createNewFile() throws IOException表示创建文件 :如果不存在,则创建
public boolean mkdir()创建文件夹,如果不存在,则创建;否则就返回false
public boolean mkdirs()创建多个文件,如果父目录不存在,则创建
public boolean delete()删除文件或者文件夹(如果删除文件夹,文件夹必须为空目录)
public boolean renameTo(File dest)重命名
public class FileDemo {
    public static void main(String[] args) throws IOException {
     //D:\EE_2106\day25\code路径下logo.jpg :描述下这个地址File
       // File srcFile  = new File("D:\\EE_2106\\day25\\code\\logo.jpg") ;
        File srcFile = new File("D:\\EE_2106\\day25\\code\\mv.jpg") ;
        File destFile = new File("高圆圆.jpg") ;//当前项目路径下了
        //public boolean renameTo(File dest)
        System.out.println(srcFile.renameTo(destFile)) ;
    }
}

File的功能方法(判断/高级获取)

功能方法名字方法意思
public boolean canRead()是否可读
public boolean canWrite()是否可写
public boolean exists()是否存在
public boolean isFile()是否是文件
public boolean isDirectory()是否是文件夹
public boolean isHidden()是否隐藏
public long length()获取长度
public String getName()获取抽象路径 名所表示的文件或者目录的名称
public File[] listFiles()获取某个目录下的所有的文件以及文件夹的File数组
public String[] list()获取某个抽象路径名所表示的文件以及目录的字符串数组
public class FileDemo2 {
    public static void main(String[] args) {

        //创建File对象,描述当前项目下的aaa.txt文件
        File file = new File("aaa.txt") ;
        System.out.println(file.canRead());
        System.out.println(file.canWrite());
        System.out.println(file.exists());
        System.out.println(file.isDirectory());//false
        System.out.println(file.isFile());
        System.out.println(file.isHidden());
        System.out.println(file.length());
        System.out.println(file.getName());
        System.out.println("------------------------");

 
        //public String[] list():获取某个抽象路径名所表示的文件以及目录的字符串数组
        String[] strArray = file2.list();
        if(strArray!=null){
            for(String s:strArray){
                System.out.println(s);
            }
        }
    }
}

File面试题(获取D盘下的所有的文件夹以及文件的名称)

 //描述D盘
        File file2 = new File("d://") ;
        File[] fileArray = file2.listFiles();
        //防止空指针异常
        if(fileArray!=null){
            for(File f :fileArray){
                System.out.println(f.getName());
            }
        }

File面试题(获取D盘下所有的以.jpg结尾的文件)

public class FileTest {
    public static void main(String[] args) {
        //1)描述D盘
        File file = new File("D://") ;

        //2)获取盘符下的所有的文件以及文件夹的File数组
        File[] fileArray = file.listFiles();//这一步并没有直接获取到要的.jpg文件
        //后面一些判断
        if(fileArray!=null){
            for(File f:fileArray){

                //判断f是文件
                if(f.isFile()){
                    //是文件
                    //判断它的名称是否以.jpg结尾
                    if(f.getName().endsWith(".jpg")){
                        System.out.println(f.getName());
                    }
                }
            }
        }

        System.out.println("-------------------------------------------------------");

        //描述下D盘
        File srcFile = new File("D://") ;

        //获取当前D盘下的所有文件以及文件夹File数组,并进行文件名过滤
        //public File[] listFiles(FilenameFilter filter)
        File[] files = srcFile.listFiles(new FilenameFilter() {//接口的匿名内部类
            @Override
            public boolean accept(File dir, String name) {
                //返回true:表示将指定文件添加列表文件中
                //描述文件所表示抽象路径File
                File file = new File(dir, name);

                //两个条件:file是文件并且file的文件名称以".jpg结尾"
                return file.isFile() && file.getName().endsWith(".jpg");
            }
        });
        if(files!=null){
            for(File f :files){
                System.out.println(f.getName());
            }
        }
    }
}

方法递归

  • 方法递归:方法调用方法本身的一种现象,并非是方法嵌套方法
  • 前提条件
    1)必须有一个成员方法
    2)必须有方法递归的出口条件(结束条件),如果没有出口条件,就是死递归…
    3)还存在一定的规律
  • 注意事项:构造方法不存在递归

方法递归面试题(求5的阶乘!)

public class DiGuiDemo {

   /* public DiGuiDemo(){
        //DiGuiDemo() ;//构造方法不能使用递归
    }
*/
   public static void main(String[] args) {

       System.out.println("5的阶乘是:"+jieCheng(5));
   }

    //递归:需要考虑(一定要有规律)
    private static int jieCheng(int i) {
       if(i==1){
           return 1 ;
       }else{
           return i * jieCheng(i-1) ;
       }
    }
}

方法递归面试题(删除D盘某个demo文件夹中有很多个目录)

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

         //描述D盘的demo文件夹
         File srcFloder = new File("d://demo") ;
         //调用递归删除的方法
        deleteFloder(srcFloder) ;
    }


    public static void deleteFloder(File srcFloder) {
        //获取srcFloder下的所有的文件以及文件的File数组
        File[] fileArray = srcFloder.listFiles();
        if(fileArray!=null){
            for(File file:fileArray){
                //获取到每一个file对象
                //如果file是文件夹,继续回到deleteFloder(srcFloder) ; 删除文件夹
                if(file.isDirectory()){
                    deleteFloder(file) ;
                }else{
                    //判断是文件,必须以.java结尾 文件
                    if(file.getName().endsWith(".java")){
                        //删除的同时----获取名称
                        System.out.println(file.getName()+"------------"+file.delete());
                    }
                }

            }
            //删除文件夹
            System.out.println(srcFloder.getName()+"----"+srcFloder.delete());
        }
    }
}

IO流

  • IO流的分类:
  • 按流的方向:

输入和输出流

  • 按类型分:

字节和字符流:字节先出现,后面在有字符流

  • 再次流的方向划分

字节输入流:InputStream:表示输入字节流的所有类的超类(父类)
字节输出流:OutputStream:表示字节输出流的所有类的超类

字符输入流:Reader表示输入字符流的抽象类
字符输出流:Writer表示输出字符流的抽象类

字节输出流(OutputStream)

  • 实现步骤:

1)创建文件输出流对象 :FileOutputStream(String name) :推荐:可以指定参数地址 FileOutputStream(File file)

2)写数据
public void write(int b) throws IOException 写一个字节
public void write(byte[] bytes) throws IOException 写一个字节数组
public void write(byte[] bytes,int off,int len) throws IOException:写一部分字节数组

3)关闭资源

public class FileOutputStreamDemo {
    public static void main(String[] args) throws IOException {

        //创建文件输出流对象
        //指向某个盘符下的文件中(或者当前项目下)
        FileOutputStream fos = new FileOutputStream("my.txt") ;//文件需要被创建当前项目下

        //2)写数据
       /* fos.write(97);
        fos.write(98);
        fos.write(99);*/

       //写一个字节数组
      /*  byte[] bytes = {97,98,99,100,101,102} ;
       // fos.write(bytes);
        fos.write(bytes,2,2);*/
      for(int x = 0 ; x < 10 ; x ++){
          fos.write(("hello"+x).getBytes());

          //windows操作系统  "\r\n"代表换换行
          fos.write("\r\n".getBytes());
      }
        //3)关闭资源
        fos.close(); //释放fos流对象所指向的my.txt的系统资源
    }
}

在字节输出流中加入异常处理

public class FileOutputStreamDemo2 {

    public static void main(String[] args) {

       // method1() ;
        method2() ;//
    }

    //标准方式:try...catch...finally
    //统一处理
    private static void method2() {
        FileOutputStream fos = null ;
        //alt+ctrl+t--->
        try {
           fos = new FileOutputStream("fos2.txt") ;
            //写数据
            fos.write("hello,我来了".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //释放资源

                if(fos!=null){
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

        }
    }

    //分别try...catch...(不用,这种方式阅读性很差)
    private static void method1() {
        //在当前项目下输出fos.txt
        //创建字节输出流对象
        FileOutputStream fos = null ;
        try {
             fos = new FileOutputStream("fos.txt") ;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        //写数据
        try {
            fos.write("hello,IO".getBytes()) ;
        } catch (IOException e) {
            e.printStackTrace();
        }

        //关闭资源
        try {
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

子节输入流(InputStream)

  • 实现步骤:
  • 需要读取当前项目下的fis.txt文件

1)创建文件字节输入流对象,指向 fis.txt
public FileInputStream(String name) throws FileNotFoundException

2)读内容
read(byte[] bytes):一次读取一个字节数组
read():一次读取一个字节

3)释放资源

public class FileInputStreamDemo {
    public static void main(String[] args) {
        //创建一个字节输入流对象
        FileInputStream fis = null ;
        try {
          // fis  = new FileInputStream("fis.txt") ;
           fis  = new FileInputStream("DiGuiTest.java") ; //读取当前项目下的DiGuiTest.java
            //读取内容
          //使用循环优化:  结束条件:获取的字节数为-1

            //当前不知道循环多少次:while循环
           //将判断,赋值一块去使用 (模板代码)
           int by = 0 ; //字节数为0
           while((by=fis.read())!=-1) {
               System.out.print((char)by);
           }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //释放资源
            if(fis!=null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

子节输入流面试题(fis2.txt读取出来,并展示在控制台上)

  • public int read(byte[] b)throws IOException
    一次读取一个字节数组 ,返回值值的是每次读取的实际字节数
public class FileInputStreamDemo2 {
    public static void main(String[] args) {
        //创建字节输入流对象
        FileInputStream fis = null ;
        try {
            fis = new FileInputStream("fis2.txt") ;

            //一次读取一个字节数组
            //创建一个字节数组
          
         //模板
         //创建一个数组:长度:1024或者1024的整数倍
          byte[] buffer = new byte[1024] ;   //长度虽然1024个长度
          int len = 0 ;
          while((len=fis.read(buffer))!=-1){
              //每次获取的从0开始获取实际字节长度
              System.out.println(new String(buffer,0,len)) ;
          }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

(1,2)读写复制操作(基本字节流)(字节/字节数组)

读写复制操作:

  • 一次读取一个字节的方式
  • 一次读取一个字节数组
    一次读取一个字节数组
public class CopyFileDemo {
    public static void main(String[] args) {

        long start = System.currentTimeMillis() ;//时间毫秒值
        method2("DiGuiTest.java","D://Copy.java") ;

        long end  = System.currentTimeMillis() ;
        System.out.println("共耗时:"+(end-start)+"毫秒");
    }

    private static void method2(String srcFile, String destFile) {
        FileInputStream fis  = null ;
        FileOutputStream fos = null ;
        try {
            //  封装源文件:FileInputStraem(String pathname)
            //字节输入流
            fis = new FileInputStream(srcFile) ;
            //字节输出流
            // 封装目的地文件:FileOutputStream(String pathname)
            fos = new FileOutputStream(destFile) ;

            //读写复制操作
            //一次读取一个字节数组
            byte[] bytes = new byte[1024] ;
            int len = 0 ;
            while((len=fis.read(bytes))!=-1){
                //赋值
                //fos流对象中写
                //带上len的使用
                fos.write(bytes,0,len);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {

            try {
                fos.close();
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }  

**一次读取一个字节0

0
0
**

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

        long start = System.currentTimeMillis() ;//时间毫秒值

       // method("DiGuiTest.java","D://Copy.java") ;
         private static void method(String srcFile, String destFile) {
        FileInputStream fis  = null ;
        FileOutputStream fos = null ;
        try {
            //  封装源文件:FileInputStraem(String pathname)
            //字节输入流
            fis = new FileInputStream(srcFile) ;
            //字节输出流
            // 封装目的地文件:FileOutputStream(String pathname)
            fos = new FileOutputStream(destFile) ;

            //读写复制操作
            //一次读取一个字节
            int by = 0 ;
            while((by=fis.read())!=-1){
                //没有读完,继续复制 :写一个字节
                fos.write(by);

            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {

            try {
                fos.close();
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

字节缓冲输入流(BufferedInputStream)

构造方法名字方法意思
BufferedInputStream(InputStream in)默认缓冲区大写 (足够大了)
BufferedInputStream(InputStream in, int size)提供一个指定的缓冲区大小
public class BufferedInputStreamDemo {
    public static void main(String[] args) throws IOException {
        //创建一个字节缓冲输入流对象
        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));
        }

    }
}

字节缓冲输出流(BufferedOutputStream)

  • 特点:提供缓冲区大写:1024的8倍 还是通过底层流提高读速度(FileInputStream)
构造方法名字方法意思
BufferedOutputStream(OutputStream out)默认缓冲区大小 足够大了 8kb
BufferedOutputStream(OutputStream out, int size)创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
public class BufferedOutputStreamDemo {
    public static void main(String[] args) throws IOException {
        //BufferedOutputStream(OutputStream out)  //参数父类:抽象类
        //创建字节缓冲输出流
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt")) ;
      //写数据
        //写一个字节数组
        bos.write("hello.bufferedOutputStream".getBytes());

        //释放资源
        bos.close();
    }
}    

(3,4)读写复制操作(缓冲字节流)(字节/字节数组)

一次读取一个字节数组

public class CopyMp4 {
    public static void main(String[] args) {
    copyMp4("D://a.mp4","copy.mp4") ;
  //缓冲流一次读取一个字节数组
    private static void copyMp4_4(String srcFile, String destFile) {
        BufferedInputStream bis  = null ;
        BufferedOutputStream bos = null ;
        try {
            bis = new BufferedInputStream(new FileInputStream(srcFile)) ;
            bos = new BufferedOutputStream(new FileOutputStream(destFile)) ;
            //读写操作
            byte[] bytes = new byte[1024] ;
            int len = 0 ;
            while((len=bis.read(bytes))!=-1){
                bos.write(bytes,0,len);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                bos.close();
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }    

一次读取一个字节

public class CopyMp4 {
    public static void main(String[] args) {
    copyMp4("D://a.mp4","copy.mp4") ;
     //缓冲流一次读取一个字节
    private static void copyMp4_3(String srcFile, String destFile) {
        BufferedInputStream bis  = null ;
        BufferedOutputStream bos = null ;
        try {
            bis = new BufferedInputStream(new FileInputStream(srcFile)) ;
            bos = new BufferedOutputStream(new FileOutputStream(destFile)) ;
            //读写操作
            int by = 0 ;
            while((by=bis.read())!=-1){
                bos.write(by);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                bos.close();
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

字符转换输入流(InputStreamReader)

构造方法名字方法意思
InputStreamReader(InputStream in)使用平台默认解码集进行读
InputStreamReader(InputStream in,String charset)使用指定的解码集进行读
public class ReaderDemo {
    public static void main(String[] args) throws Exception {
        //创建字符转换输入流对象
        //InputStreamReader(InputStream in,String charset) :使用指定的解码集进行读
       // InputStreamReader isr = new InputStreamReader(new FileInputStream("osw.txt"),"UTF-8") ;

        //InputStreamReader(InputStream in)
        InputStreamReader isr = new InputStreamReader(new FileInputStream("osw.txt")) ;//平台默认的解码集进行读取
        //读一个字符数组
        char[] chs = new char[1024] ;
        //实际字符数
        int len = 0 ;
        while((len=isr.read(chs))!=-1){
            //打印控制台上:每次获取的实际长度
            System.out.println(new String(chs,0,len));
        }
        //释放资源
        isr.close();
    }
}

字符转换输出流(OutputStreamWriter)

  • 字符转换输出流: 字节输出流通向字符输出流的桥梁!
构造方法名字方法意思
OutputStreamWriter(OutputStream out)使用平台默认编码集写入数据
OutputStreamWriter(OutputStream out, String charsetName)使用指定的字符集进行编码 写入数据
public class WriterDemo {
    public static void main(String[] args) throws Exception {
        //创建字符缓冲输出流对象
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt")) ;//使用平台的默认编码集(utf-8)

        //写入数据
        /**
         * write(String str)
         * write(int ch):写入字符: 字符--->对应的ASCII码表
         * write(char[] ch)
         * write(char[] ch,int off,int len)
         * write(String str,int off,int len)
         */
        osw.write("hello,字符流我来了");
        osw.write(97);
        char[] chs = {'A','B','C','D','E'} ;
        osw.write(chs);
        osw.write(chs,2,2);
        //使用flush():刷新字符输出流
        osw.flush();

        //关闭资源,释放相关的资源
        osw.close();  //关闭之前,flush
        //osw.write(98);
    }
}

(5,6)读写复制操作(字符转换流)(字符/字符数组)

  • Reader/Writer子类:转换流
  • InputStreamReader(InputStream in)
  • OutputStreamWriter(OutputStream out)
    一次读取一个字符数组
public class CopyFile {
    public static void main(String[] args) throws Exception {

        //1)封装源文件:
        FileReader fileReader = new FileReader("ReaderDemo.java") ;
        //2)封装目文件
        FileWriter fileWriter = new FileWriter("D://Demo.java") ;
      //一次读取一个字符数组
        char[] charArray = new char[1024] ;
        int len = 0 ;
        while((len=fileReader.read(charArray))!=-1){
            //写入实际字符数
            fileWriter.write(charArray,0,len);
            fileWriter.flush();
        }
        //关闭
        fileWriter.close();
        fileReader.close();
    }
}   

一次读取一个字符

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

        //1)封装源文件:
        FileReader fileReader = new FileReader("ReaderDemo.java") ;
        //2)封装目文件
        FileWriter fileWriter = new FileWriter("D://Demo.java") ;
          //3)一次读取一个字符
        int by = 0 ; //实际字符数
        while((by=fileReader.read())!=-1){
            fileWriter.write(by) ;
            fileWriter.flush() ;
        }
     //关闭
        fileWriter.close();
        fileReader.close();
    }
}   

(7,8)读写复制操作(字符转换流便捷类)(字符/字符数组)

  • FileReader(File file)
  • FileReader(String pathname)
  • FileWriter(File file)
  • FileWriter(String filename)

字符缓冲输入流(BufferedReader)

构造方法名字方法意思
BufferedReader(Reader in)创建使用默认大小的输入缓冲区的缓冲字符输入流
BufferedReader(Reader in, int size)指定缓冲区大小
特有功能名字功能意思
public String readLine()一次读取一行内容
public class BufferedReaderDemo {
    public static void main(String[] args) throws IOException {
        //创建字符缓冲输入流对象  :使用键盘录入数据!(流的方式)
        //InputStreamReader(InputStream in):
        InputStream in = System.in ;//标准输入流


//        BufferedReader(Reader in)
      //  Reader reader = new InputStreamReader(in) ;

       // BufferedReader br = new BufferedReader(reader) ;
        //一步走
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in)) ;

        System.out.println("请您输入一个字符串数据:");
        //public String readLine()
        String line = br.readLine(); //阻塞式方法
        System.out.println("您输入的数据是:"+line);
      

 //定义变量
        String line2 = null ;
        while((line2=br2.readLine())!=null){
            System.out.println(line2);
        }
    }
}

字符缓冲输出流(BufferedWriter)

构造方法名字方法意思
BufferedWriter(Writer out)创建默认的缓冲区大小
BufferedWriter(Writer out, int sz)指定的大小
public void newLine() throws IOException写入行的分隔符号
public class BufferedWriterDemo {
    public static void main(String[] args) throws IOException {

        //输出bw.txt文件,并给里面写内容,而且去实现换行效果   write("\r\n")----现在可以使用newLine
        //创建字符缓冲输出流对象
        //BufferedWriter(Writer out) :
        BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));

        //写入数据
        //写字符串/写字符
        bw.write("hello");
        //public void newLine()
        bw.newLine();
        bw.write("world");
        bw.newLine();
        bw.write("javaEE");
        bw.newLine();
        //刷新流
        bw.flush();
        //释放资源
        bw.close();
    }
}

(9,10,11)读写复制操作(字符缓冲流)(字符/字符数组)

  • 特有功能:
    利用BufferedReader的readLine()读取一行
    利用BufferedWriter写一行,然后换行
public class CopyFile {
    public static void main(String[] args) {


        BufferedReader br = null ;
        BufferedWriter bw = null ;
        try {
            //封装源文件:前项目下的    ReaderDemo.java
             br = new BufferedReader(new FileReader("ReaderDemo.java")) ;
             bw = new BufferedWriter(new FileWriter("copy.java")) ;

             //使用特有功能读取一行内容
            String line = null ;
            while((line=br.readLine())!=null){
                //读取一行,bw写一行并换行,然后刷新
                bw.write(line);
                bw.newLine();
                bw.flush();;
            }

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                bw.close();
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

合并流(SequenceInputStream)

  • 字节流的逻辑串联!
  • 可以将两个或者是两个以上的文件进行读的操作 ,只能操作源文件

合并两个文件

构造方法名字方法意思
public SequenceInputStream(InputStream s1,InputStream s2)参数1和参数2:分别要读取的字节输入流对象
public class CopyMulFile {
    public static void main(String[] args) throws Exception {

        //创建两个字节输入流对象
        InputStream is = new FileInputStream("BufferedWriterDemo.java") ;
        InputStream is2 = new FileInputStream("copy.java") ;

        //public SequenceInputStream(InputStream s1,InputStream s2)
        //创建字节合并流
        SequenceInputStream sis = new SequenceInputStream(is,is2) ; //SequenceInputStream extends InputStream

        //封装目的地文件:
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.java")) ;


        //一次读取一个字节数组
        byte[] bytes = new byte[1024] ;
        int len = 0 ;
        while((len = sis.read(bytes))!=-1){
            bos.write(bytes,0,len);
        }
        //释放资源
        bos.close();
        sis.close();
    }
}

合并多个文件

方法名字方法意思
public SequenceInputStream(Enumeration<? extends InputStream> e)将两个以上的文件进行读取
public class CopyFileDemo2 {
    public static void main(String[] args) throws Exception {
        //public SequenceInputStream(Enumeration<? extends InputStream> e)

        //需要三个字节输入流对象
        InputStream is1 = new FileInputStream("BufferedWriterDemo.java") ;
        InputStream is2 = new FileInputStream("ReaderDemo.java") ;
        InputStream is3 = new FileInputStream("CopyMp4.java") ;

        //创建一个Vector集合
        Vector<InputStream> vector = new Vector<>() ;

        //添加流对象
        vector.add(is1) ;
        vector.add(is2) ;
        vector.add(is3) ;

        //public Enumeration<E> elements() ---- >类似于Collection的Iterator迭代器
        Enumeration<InputStream> enumeration = vector.elements();

        //创建合并流对象  封装源文件
        SequenceInputStream sis = new SequenceInputStream(enumeration);

        //创建字节缓冲输出流对象
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("d://hello.java")) ;

        //一次读取一个字节
        int by = 0 ;
        while((by=sis.read())!=-1){
            bos.write(by);
            bos.flush();
        }
        bos.close();
        sis.close();
    }
}

序列化(ObjectOutputStream)

  • 将某个实体对象写入到流对象中---->将一个对象变成 流数据
构造方法名字方法意思
protected ObjectOutputStream()
protected ObjectOutputStream(OutputStream out)将某个对象通过底层的基本字节输出进行写的动作
public final void writeObject(Object obj)
public class Person implements Serializable {

    //产生一个固定的序列化版本Id
    private static final long serialVersionUID = 8988325735017562383L; //常量值
    String name ; //姓名
    private transient int age  ; //年龄  //transient :想让某个字段不参与序列化:这个字段(成员变量就使用transient)
    public Person(){}

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

        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class ObjectStreamDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
       myWrite() ;//写
 //序列化
    //将Person p = new Person("高圆圆",42) ;---->变成流对象 进行传输
    private static void myWrite() throws IOException {
        //创建Person对象
        Person p = new Person("高圆圆",42) ;

        //protected ObjectOutputStream(OutputStream out):创建一个序列化流对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("oos.txt")) ;

        //将p对象写入到oos流对象中
        //public final void writeObject(Object obj)throws IOException
        oos.writeObject(p);

        //释放资源
        oos.close();
    }
}       

反序列化(ObjectInputStream)

  • 将流数据----还原成 “对象”
构造方法名字方法意思
public ObjectInputStream(InputStream in)
public final Object readObject()
public class Person implements Serializable {

    //产生一个固定的序列化版本Id
    private static final long serialVersionUID = 8988325735017562383L; //常量值
    String name ; //姓名
    private transient int age  ; //年龄  //transient :想让某个字段不参与序列化:这个字段(成员变量就使用transient)
    public Person(){}

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

        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class ObjectStreamDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
    myRead() ;//读
    }
   //反序列化
    private static void myRead() throws IOException, ClassNotFoundException {
        //创建反序列化流对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("oos.txt")) ;

        //读
        //public final Object readObject()
        Object object = ois.readObject();
        //关闭流
        ois.close();
        System.out.println(object);//toString()


        //java.io.StreamCorruptedException: invalid stream header: EFBFBDEF
        //当从对象流读取的控制信息违反内部一致性检查时抛出。
    } 
     //释放资源
        oos.close();
    }
}       

Propeties

  • Propeties:键和值都是字符串类型:可能需要在src(类所在的路径:类路径)作为一个配置文件
  • 表示一组持久的属性。 Properties可以保存到流中或从流中加载。
  • 属性列表中的每个键及其对应的值都是一个字符串。
功能名字功能意思
public Object setProperty(String key,String value)给属性列表中添加属性描述(key和value)
public Set stringPropertyNames()获取属性列表中的所有的键
public String getProperty(String key)在属性列表中通过键获取值

遍历

public class PropertiesDemo {
    public static void main(String[] args) {
        //Properties() :空参构造
        Properties properties = new Properties() ;

       // System.out.println(properties);
        //利用Map集合的功能去添加元素
        properties.put("高圆圆","赵又廷") ;
        properties.put("文章","姚笛") ;
        properties.put("文章","马伊琍") ;
        properties.put("王宝强","马蓉") ;
       // System.out.println(properties);

        //遍历
        Set<Object> keySet = properties.keySet();
        for(Object key :keySet){
            //通过key获取value
            Object value = properties.get(key);
            System.out.println(key+"---"+value);
        }

        System.out.println("---------------------------------------");

        /**
         * 推荐Properties作为集合类 的遍历方式
         *   public Object setProperty(String key,String value):给属性列表中添加属性描述(key和value)
         *    public Set<String> stringPropertyNames():获取属性列表中的所有的键
         *    public String getProperty(String key):在属性列表中通过键获取值
         */

        //创建一个空的属性列表
        Properties prop = new Properties() ;
        prop.setProperty("张三","30") ;
        prop.setProperty("李四","40") ;
        prop.setProperty("王五","35") ;
        prop.setProperty("赵六","45") ;
        //遍历:
        Set<String> set = prop.stringPropertyNames();//获取所有键
        for(String key:set){
            String value = prop.getProperty(key);
            System.out.println(key+"---"+value);
        }
    }
}

保存/加载

  • 将字节输入流或者字符输入中所在的文件中的内容加载属性列表中
    void load(Reader reader)
    void load(InputSteram in)

  • 将Properties里面存储的内容----->保存到某个目录的xxx配置文件中 保存
    public void store(Writer writer,String comments)
    public void store(OutputStream out,String comments)

  • 将属性列表中的内容保存到指定字节输出流/字符输出流所指向那个文件中

public class PropertiesDemo2 {
    public static void main(String[] args) throws IOException {
//        myLoad() ;
         myStore() ;
    }

    //将属性集合类中的内容 ---保存到指定文件中
    private static void myStore() throws IOException {
        Properties prop = new Properties() ;

        //添加key-value
        prop.setProperty("张三丰","56") ;
        prop.setProperty("吴奇隆","60") ;
        prop.setProperty("成龙","65") ;
        prop.setProperty("刘德华","70") ;

        //public void store(Writer writer,String comments):
        //参数2:给当前属性列表中加入一个描述
        //保存指定的文件中
        prop.store(new FileWriter("user.txt"),"name's list'");

        System.out.println("保存完毕");

    }

    //加载:需要将names.txt文件加载到属性列表中,然后遍历
    private static void myLoad() throws IOException {
        //创建一个空的属性列表
        Properties prop = new Properties() ;
        System.out.println(prop);

        //void load(Reader reader)
      //  Reader r = new FileReader("names.txt") ;//names.txt是在当前项目下路径下
        // prop.load(r) ;



        //读取src路径下的names.properties  (类路径)
        //使用步骤
        //1)获取当前类所在的字节码文件对象
        Class clazz = PropertiesDemo2.class ;
        //2)获取当前类所在的类加载器Class:public ClassLoader getClassLoader()
        ClassLoader classLoader = clazz.getClassLoader();
        //3)在类加载器中:获取当前资源文件(配置文件names.proprites)所在的输入流对象
        //public InputStream getResourceAsStream(String name) {
        InputStream inputStream = classLoader.getResourceAsStream("names.properties");
        //将inputStream加载到属性集合类中
//        void load(InputSteram in)
        prop.load(inputStream);

        System.out.println(prop);

        Set<String> keySet = prop.stringPropertyNames();
        for(String key:keySet){
            String value = prop.getProperty(key);
            System.out.println(key+"---"+value);
        }
    }
}

网络编程(Socket)

  • 特点:
    发送端/客户端
    接收端/服务器端 这两端必须存在Socket对象
  • 网络编程的三要素:
  • ip
  • port
  • 协议:
  • udp协议
    1)不需要建立连接通道
    2)属于一种不可靠连接
    3)发送文件大小限制的
    4)执行效率高 (不同步的)
  • TCP/Ip协议
    1)建立连接通道
    2)属于安全连接(可靠连接)
    3)发送文件(使用基本字节流),相对udp协议来说没有限制
    4)执行效率低(同步的)

InetAddress(互联网ip地址)

方法名字方法意思
public static InetAddress getByName(String host)参数:主机名称(计算机电脑名称)
public String getHostAddress()获取ip地址字符串形式
public class NetDemo {
    public static void main(String[] args) throws UnknownHostException {
        //如何获取自己电脑上的ip地址----》String形式!
        //10.12.156.107
        InetAddress inetAddress = InetAddress.getByName("DESKTOP-Q62EUJH");
        String ip = inetAddress.getHostAddress();
        System.out.println(ip);
        //10.12.156.107
    }
}

Update发送端

Udp编程的发送端

  • 1)创建Socket对象
  • 2)发送内容 :内容数据一种数据报文(报包)DatagramPacket
  • 3)释放资源
public class UdpSend {
    public static void main(String[] args) throws IOException {
        //1)创建Socket对象
        //DatagramSocket:发送和接收数据报数据包的套接字。
        //public DatagramSocket() throws SocketException
        DatagramSocket ds = new DatagramSocket() ;

        //2)创建一个数据报包对象DatagramPacket
        //DatagramPacket(byte[] buf, int length, InetAddress address, int port)
        //参数1:当前数据的字节数组
        //参数2:当前数据的长度
        //参数3:InetAddress:ip地址对象
        //参数4:绑定的端口号
        byte[] bytes = "hello,马三奇".getBytes() ;
        int length = bytes.length ;
               //public static InetAddress getByName(String host)
        InetAddress inetAddress = InetAddress.getByName("10.12.156.107");
        int port = 12306 ;
        DatagramPacket dp = new DatagramPacket(bytes,length,inetAddress,port) ;

        //3)发送数据报包
        //public void send(DatagramPacket p)
        ds.send(dp);

        //4)释放资源
        ds.close();
    }
}

发送端不断发送数据

  • 发送端不断键盘录入数据,接收端不断接收数据,然后展示内容
public class UdpSend {
    public static void main(String[] args) {

        DatagramSocket ds = null ;
        try {
            //创建发送端的Socket
            ds = new DatagramSocket() ;

            //键盘录入数据
            //IO流的方式

            BufferedReader br = new BufferedReader(new InputStreamReader(System.in)) ;

            //使用readLine()读取一行内容
            String line = null ;
            while((line=br.readLine())!=null){    //readLine():阻塞式方法

                //自定义结束条件
                if("886".equals(line)){
                    break ;
                }

                //创建数据报包
                DatagramPacket dp = new DatagramPacket(line.getBytes(),
                        line.getBytes().length,
                        InetAddress.getByName("10.12.156.107"),6666);

                //发送数据报包
                ds.send(dp);
            }

        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //释放资源
            ds.close();

        }
    }
}

Update接收端

Udp接收端

  • 1)创建Socket对象
  • 2)创建一个接收容器:数据报包:DatagramPacket
  • 3)接收
  • 4)解析容器的的实际数据大小
  • 5)展示发送端发送的数据
public class UdpReceive {
    public static void main(String[] args) throws IOException {
        //)创建Socket对象
        //public DatagramSocket(int port)
        DatagramSocket ds = new DatagramSocket(12306) ;
        //2)创建一个接收容器:数据报包:DatagramPacket  实际数据没有这么大
        //public DatagramPacket(byte[] buf, int length)
        //自定义字节缓冲区
        byte[] bytes = new byte[1024] ;
        int lentgth = bytes.length ;
        DatagramPacket dp = new DatagramPacket(bytes,lentgth);

        //3)public void receive(DatagramPacket p)
        ds.receive(dp);

        //4)解析实际数据
        //byte[] getData()   获取实际字节数
        //返回数据缓冲区。
        //int getLength()   获取实际长度
        byte[]  bytes2 = dp.getData();
        int length2 = dp.getLength();
        String receiverStr = new String(bytes2,0,length2) ;
        //获取ip地址
        //InetAddress getAddress()
        //InetAddress
                //public String getHostAddress():
        String ip = dp.getAddress().getHostAddress();

        //展示数据
        System.out.println("data from "+ip+" ,content is :"+receiverStr);
    }
}

接收端不断接收数据

  • 接收端端不断的接收数据
  • 接收端一般不关闭
  • 接收端只能开启一次,多次就出现 BindException:绑定一次
  • Address already in use: Cannot bind:端口号被占用!
public class UdpReceive {
    public static void main(String[] args) {

        //创建接收端的Socket
        try {
            DatagramSocket ds = new DatagramSocket(6666) ;

            while(true){
                //创建一个接收容器
                byte[] bytes = new byte[1024] ;
                DatagramPacket dp = new DatagramPacket(bytes,bytes.length) ;

                //接收
                ds.receive(dp);

                //解析实际数据
                String receiveStr = new String(dp.getData(), 0, dp.getLength());
                //获取ip
                String ip = dp.getAddress().getHostAddress();

                //展示数据
                System.out.println("data from "+ip+"data is--->"+receiveStr);
            }

        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //接收不关闭
            //接收socket对象一一直开着
        }
    }
}

TCP客户端

  • TCP客户端写数据
  • 1)创建TCP客户端的Socket对象
  • 2)写数据
  • 3)释放资源
public class ScoketDemo {
    public static void main(String[] args) throws IOException {
        //1)创建TCP客户端的Socket对象
       // public Socket(String host, int port)throws UnknownHostException, IOException
        //参数1:主机名称/或者ip地址字符串形式
        //参数2:指定的端口(0-65535(不包含0-1024))
        Socket s = new Socket("10.12.156.107",8888) ;

        //2)写数据
        //public OutputStream getOutputStream()throws IOException
        OutputStream out = s.getOutputStream();//获取通道内的输出流对象
        out.write("hello,TCP".getBytes());



        //读取服务器端反馈的数据

        //3)释放资源
        s.close();


    }

客户端不断录入数据

  • 1)TCP客户端不断键盘录入数据,服务器端不断接收数据,然后展示数据
  • 2)TCP客户端文本文件(XXX.java文件)----->服务器端将文件进行读写复制,输出到当前项目的Copy.java文件中
    BuferedReader
  • 3)TCP客户端文本文件(XXX.jpg文件)----->服务器端将文件进行读写复制,输出到当前项目的Copy.jpg文件中
    BufferedInputStream
public class ClientDemo {
    public static void main(String[] args) {

        //创建客户端的Socket
        Socket socket = null ;
        try {
           socket  = new Socket("10.12.156.107",10010) ;
           //创建BufferedReader
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in)) ;

            //录入一行
            //读取一行
            String line = null ;
            while((line=br.readLine())!=null){//null只是作为 一个文件是否读完

                if("over".equals(line)){
                    break ;
                }


                //获取通道内的输出流(字节流OutputStream)
                //封装通道内字节输出流
                //分步走
                //OutputStream outputStream = socket.getOutputStream();   //字节流---->字符流
                //创建字符缓冲输出流
               // BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream)) ;

                //一步走
                BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())) ;
                 //给封装的通道内的流对象bw写一行
                bw.write(line);
                bw.newLine();
                bw.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {

            //释放资源
                if(socket!=null){
                    try {
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
        }

        //需要获取服务器端的反馈数据 (已经读完完毕了)
    }
}

TCP服务端

  • TCP服务器端的实现步骤:

  • 1)创建服务器端的Socket对象
    public ServerSocket(int port)throws IOException

  • 2)监听客户端连
    public Socket accept()throws IOException 返回值就是当前监听到的客户端的Socket对象

  • 3)获取通道内的输入流
    public InputStream getInputStream() throws IOException

  • 4)读取数据:一次读取一个字节数组

  • 5)展示数据 而且获取ip地址
    //public InetAddress getInetAddress()

public class ServerDemo {
    public static void main(String[] args) throws IOException {

        // 1)创建服务器端的Socket对象
        //public ServerSocket(int port)throws IOException
        ServerSocket ss = new ServerSocket(8888) ;
        System.out.println("服务器正在等待连接...");

        //2)监听客户端连接
//        public Socket accept()throws IOException 返回值就是当前监听到的客户端的Socket对象
        Socket socket = ss.accept(); //阻塞式方法
        //3)取通道内的输入流
        //public InputStream getInputStream() throws IOException
        InputStream inputStream = socket.getInputStream();
        //4)读取数据:一次读取一个字节数组
        byte[] bytes = new byte[1024] ;
        int len = inputStream.read(bytes);
        //获取内容
        String clientStr = new String(bytes,0,len) ;


        //再去反馈数据

        //5)获取ip
        String ip = socket.getInetAddress().getHostAddress();
        //展示数据
        System.out.println("data from "+ip+" content is--->"+clientStr);

        //关闭

        ss.close();
    }
}

服务端不断读取数据

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

        //创建服务端的Socket,一直开启
        ServerSocket ss = null ;
        try {
           ss = new ServerSocket(10010) ;
            //ArrayList<Socket>:ArrayList存储一个列表:都是多个客户端 对象

           while(true){
               System.out.println("服务器正在等待连接");
               Socket socket = ss.accept();
               //不断监听,不断展示数据

               //获取通道内的输入流(InputStream)
               //封装通道内的输入流对象
               BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())) ;
               //一次读取一行
               String line = null ;
               while((line=br.readLine())!=null){ //阻塞式方法
                   // //展示数据
                   System.out.println(line);

               }


               //复制完毕之后,给客户端反馈

           }


        } catch (IOException e) {
            e.printStackTrace();
        }
        //服务器端不关闭
    }
}

TCP三次握手

  1. 第一次握手: 客户端首先连接服务器端(发送信号SYN(Synchronize Sequence Number))
  2. 第二次握手: 服务器端发送客户端应答(表示已经确认收到了)ACK(Acknowledge character)+SYN
  3. 第三次握手: 客户端再次发送确认字符以及同步序列编号,告诉服务器端已经收到了
    两端Socket—建立连接
    通过通道内的输入输出流 传输数据

客户端(获取通道内的输入流)

  //读取服务器端反馈的数据
        //获取通道内的输入流
        InputStream in = s.getInputStream();
        //读
        byte[] bytes = new byte[1024] ;
        int len = in.read(bytes);
        String fkMsg = new String(bytes,0,len) ;
        System.out.println("fkMsg:"+fkMsg);

服务端(获取通道内的输出流)

 //服务器给客户端再去反馈数据,
        //获取通道内的输出流
        OutputStream outputStream = socket.getOutputStream();
        //写入到通道内的流对象中
        outputStream.write("ok,数据已经收到".getBytes());
        outputStream.flush();

TCP复制文件(加入反馈)

  • 解决方案:
  • 1)自定义结束条件
    在客户端读完文件中,通知一下服务器端
    写入一行内容(“886/over”),服务器端只要读取到886或者over
  • 2)可以使用客户端Socket的一个方法 标记(通知服务器端,客户端已经没有数据输出了)
    public void shutdownOutput()throws IOException

客户端

  • 客户端的一个文本文件,服务器端进行复制到指定的某个文件中 ,复制完毕了,服务器端需要给客户端反馈!(反馈的消息,客户端能不能获取到)
  • 加入反馈操作:出现客户端和服务器端互相等待的情况—>但是文件已经复制完毕!
public class ClientTest {
    public static void main(String[] args) throws IOException {

        //将当前项目下的Test.java 客户端将文件写入到 服务器端,服务器端进行复制
        //创建客户端Socket对象
        Socket socket = new Socket("10.12.156.107",10086) ;

        //创建BufferedReader流对象,去操作当前项目下的Test.java文件
        BufferedReader br = new BufferedReader(new FileReader("Test.java")) ;

        //获取通道内字节输出流,写数据----封装通道内的字节流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())) ;

        //一次读取一行,然后给通道内的流对象中写入过去
        String line = null ;
        while((line=br.readLine())!=null){//阻塞式方法
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        方案1 :自定义结束条件:客户端通道内的流中已经没有数据了
        /*bw.write("over");
        bw.newLine();
        bw.flush();*/

        方案2:使用socket对象的方法
        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();
    }
}

服务端

  • 服务器端复制Test.java到当前项目下的Copy.java文件中
public class ServerTest {
    public static void main(String[] args) throws IOException {

        //创建服务器端Socket
        ServerSocket ss = new ServerSocket(10086) ;

        Socket socket = ss.accept();
        //创建BufferedReader去读取通道内写入过来的数据,封装通道内的流对象
        BufferedReader br  = new BufferedReader(new InputStreamReader(socket.getInputStream())) ;
        //创建字符缓冲输出流,写入数据,输出到当前项目下的Copy.java文件中
        BufferedWriter bw = new BufferedWriter(new FileWriter("copy.java")) ;

        //一次读取一行
        String line  = null ;
        while((line=br.readLine())!=null){//阻塞方法  :可能客户端的文件已经null了,但是服务器端不知道!
           /* if("over".equals(line)){
                break ;
            }*/

            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        //加入反馈操作
        //继续通道内输出流对象
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("数据已经收到了,已经复制完毕".getBytes());
        outputStream.flush();

        //释放资源
        bw.close();
        ss.close();
    }
}

TCP复制图片(加入反馈)

客户端

  • 客户端的图片文件,服务器端将图片进行复制,并反馈给客户端
public class ClientImgDemo {
    public static void main(String[] args) throws IOException {
        //创建客户端的Socket
         Socket socket = new Socket("10.12.156.107",12306) ;

         //创建BuferedInputStream 读取图片文件  :d://mm.jpg
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("d://mm.jpg")) ;
        //写入到通道内流中同时 封装通道内的流
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()) ;

        //一次读取一个字节数组
        byte[] buffer  = new byte[1024] ;
        int len = 0 ;
        while((len=bis.read(buffer))!=-1){
            //写
            bos.write(buffer,0,len);
            bos.flush(); //强制刷新,将缓冲的字节数全部写出
        }


        //通知服务器端,通道内输出流对象没有数据了
        socket.shutdownOutput();


        //读取反馈
        InputStream inputStream = socket.getInputStream();
        byte[] bytes = new byte[1024] ;
        int length = inputStream.read(bytes);
        System.out.println("反馈内容为:"+new String(bytes,0,length));

        //关闭资源
        bis.close();
        socket.close();

    }
}

服务端

  • 服务器端将图片进行复制,并反馈给客户端
  • 将图片文件写入到当前项目下的高圆圆.jpg
public class ServlerImgDemo {

    public static void main(String[] args)  throws IOException {
        ServerSocket ss  = new ServerSocket(12306) ;

        //监听
        Socket socket = ss.accept() ;
        //读取:封装通道内的字节输入流
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream()) ;
        //输出
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("高圆圆.jpg")) ;

        //一次读取一个字节数组
        byte[] bytes = new byte[1024] ;
        int len = 0 ;
        while((len=bis.read(bytes))!=-1){ //阻塞式方法
            bos.write(bytes,0,len);
            bos.flush();
        }

        //加入反馈
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("图片文件复制完毕".getBytes());
        outputStream.flush();


        //释放资源
        bos.close();
        ss.close();
    }
}

反射

  • 什么是反射?
    能够获取当前某个类的字节码文件对象Class,那么就可以获取当前类的构造器并且创建当前类实例,还可以获取当前类的成员变量并去赋值,或者获取当前类的成员方法并去调用!

  • 如何获取一个类的字节码文件对象?

  1. Object类的getClass()获取 获取当前某个类的实例(正在运行的类) 任意Java类型的class属性
    Class类的静态功能
  2. public static Class<?> forName(String className)
    Person类
public class Person {

    //成员变量
     private String name ;//姓名 私有
     public int age ; //年龄  默认
     String address ; //地址 默认修饰符

    //无参构造方法:公共的
    public Person(){}

    //提供带两个参数的构造方法,默认修饰符
    Person(String name,int age){
        this.name = name ;
        this.age = age ;
    }

    //提供三个参数的构造方法,私有的
    private Person(String name,int age,String addrss){
        this.name = name ;
        this.age = age ;
        this.address = addrss ;
    }

    //提供一些成员方法(非静态)
    public void show(){
        System.out.println("show Person");
    }
    private  String functioin(String str){
        return str ;
    }

    void method(String message,int num){
        System.out.println(message+num);
    }

    public int method2(){
        return 100 ;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                '}';
    }
}

测试类

public class ReflectDemo {

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

        //获取Person类的字节码文件对象:Class
        Person p = new Person() ;
        Class c1 = p.getClass();
        System.out.println(c1);//class 全限定名称
        Person p2  = new Person() ;
        Class c2 = p2.getClass() ;
        System.out.println(c2);
        System.out.println(c1==c2);
        System.out.println(p==p2);//两个对象


        System.out.println("----------------------------");

        Class c3 = Person.class ;
        System.out.println(c3);
        System.out.println(c1==c3);

        System.out.println("-----------------------------");
        //知道某个一类的全限定名称了,也可以获取当前类的字节码文件对象
       // Class c4 = Class.forName("Person"); com.qf.reflect_03.Person //参数字符串:全限定名称
        Class c4 = Class.forName("com.qf.reflect_03.Person"); //com.qf.reflect_03.Person //参数字符串:全限定名称
        System.out.println(c4);
        System.out.println(c1==c4);

        //第二种方式和第三种使用  推荐第三种       因为参数为String------>就可以存储在配置文件中

    }
}

通过反射创建实例

Person类

public class Person {

    //成员变量
     private String name ;//姓名 私有
     public int age ; //年龄  默认
     String address ; //地址 默认修饰符

    //无参构造方法:公共的
    public Person(){}

    //提供带两个参数的构造方法,默认修饰符
    Person(String name,int age){
        this.name = name ;
        this.age = age ;
    }

    //提供三个参数的构造方法,私有的
    private Person(String name,int age,String addrss){
        this.name = name ;
        this.age = age ;
        this.address = addrss ;
    }

    //提供一些成员方法(非静态)
    public void show(){
        System.out.println("show Person");
    }
    private  String functioin(String str){
        return str ;
    }

    void method(String message,int num){
        System.out.println(message+num);
    }

    public int method2(){
        return 100 ;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                '}';
    }
}

创建对象

public class ReflectDemo {
    public static void main(String[] args) throws
            ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

        //之前的写法
        Person p = new Person() ;
        System.out.println(p);


        System.out.println("-----------------------------------------");

        //使用反射的方式创建当前类实例
        //1)获取Person类的字节码文件对象
        Class personClass = Class.forName("com.qf.reflect_03.Person") ;//class com.qf.reflect_03.Person

        //2)获取构造器对象
        //Class类
        //public Constructor<?>[] getConstructors():获取当前字节码文件对象中(正在运行的这个类)里面所有的公共的构造方法所在的对象
       //public Constructor<?>[] getDeclaredConstructors():获取所有的构造器对象Constructor,返回构造器对象数组  包含私有化构造/默认的
      //  Constructor[] constructors = personClass.getConstructors();
       /* Constructor[] constructors = personClass.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);//public com.qf.reflect_03.Person()

            //private com.qf.reflect_03.Person(java.lang.String,int,java.lang.String)
            //com.qf.reflect_03.Person(java.lang.String,int)
            //public com.qf.reflect_03.Person()
        }*/


       //获取某一个构造器对象Constructor
        //public Constructor<T> getConstructor(Class<?>... parameterTypes) :// ... :jdk5以后 可变参数 (参数数量未知)
        //获取指定的公共的单个的构造器对象    参数:需要书写的是当前参数类型的字节码文件对象
        Constructor constructor = personClass.getConstructor();//java.lang.String

        //通过构造器对象创建当前类的实例
        //public T newInstance(Object... initargs): 参数为最终赋值的实际参数 (可变参数:实际参数未知)
        Object obj = constructor.newInstance();
        System.out.println(obj); //p


    }
}

给对象赋值

public class ReflectDemo2 {
    public static void main(String[] args) throws Exception {
        //之前的写法
//        Person p  = new Person("高圆圆",20) ;  //默认修饰符只能同一个包下访问

        //反射创建p对象
        //1)获取类的字节码文件对象
        Class clazz = Class.forName("com.qf.reflect_03.Person") ;

        //2)获取构造Constructor(构造方法所在对象)
        //Person(String name,int age){
        获取指定的构造函数对象   ,返回值都是单个构造函数
        //public Constructor<T> getDeclaredConstructor(类<?>... parameterTypes) :参数都是参数类型的字节码文件对象
        // 公共的构造函数对象:public Constructor<T> getConstructor(Class<?>... parameterTypes)

        Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);
        //通过构造器创建当前类的实例
        //getDeclaredConstructor
        //java.lang.IllegalAccessException: Class com.qf.reflect_04.ReflectDemo2  非法访问异常:因为当前构造函数式默认修饰符
        // can not access a member of class com.qf.reflect_03.Person with modifiers ""
        //取消Java语言访问检查(反射中使用到)
        //public void setAccessible(boolean flag)参数为true,取消Java语言访问检查
        constructor.setAccessible(true);

        Object obj = constructor.newInstance("高圆圆", 20);
        System.out.println(obj);//Person{name='高圆圆', age=20, address='null'}
    }
}

获取构造函数

public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        //获取Person类的字节码文件对象
        Class clazz  = Class.forName("com.qf.reflect_03.Person");

        //获取构造器对象
        //public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
        Constructor con = clazz.getDeclaredConstructor(String.class,int.class,String.class) ;

        //取消Java语言访问检查
        con.setAccessible(true);
        //通过构造器对象创建当前类实例
        Object obj = con.newInstance("高圆圆", 20, "西安市");
        System.out.println(obj);//Person{name='高圆圆', age=20, address='西安市'}
    }
}

获取成员变量

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        //没有使用反射之前
        Person p = new Person() ;
        System.out.println(p);
        p.age = 20 ;//对象名.属性;
        System.out.println(p);

        System.out.println("-----------------------------");

        //反射的方式
        //1)获取Person类的字节码文件
        Class clazz = Class.forName("com.qf.reflect_03.Person") ;

        //方式1:直接获取当前类的实例
        //public T newInstance()
        Object obj = clazz.newInstance();
        System.out.println(obj);


        //方式2:通过获取构造函数对象,创建当前类实例
      /*  Constructor constructor = clazz.getConstructor();
        Object obj = constructor.newInstance();
        System.out.println(obj);*/


        //2)Class类
        //获取所有的公共的成员变量Field
        //public Field[] getFields()throws SecurityException
        //public Field[] getDeclaredFields():所有的字段所在的Field对象
        // 这包括公共,受保护,默认(包)访问和私有字段,但不包括继承的字段。
//        Field[] fields = clazz.getFields() ;
        /*Field[] fields = clazz.getDeclaredFields() ;
        for (Field field : fields) {
            System.out.println(field);*/
            //public int com.qf.reflect_03.Person.age

            //private java.lang.String com.qf.reflect_03.Person.name
           // public int com.qf.reflect_03.Person.age
           // java.lang.String com.qf.reflect_03.Person.address
       // }

        //现在访问name   private String name ;
        //public Field getDeclaredField(String name):获取指定的单个的字段所在的Field对象
        //参数为"当前字段名称"
        Field nameField = clazz.getDeclaredField("name"); //默认:null

        //Field提供方法
        //public void set(Object obj,Object value):给绑定在当前对象上的字段进行赋值
        //参数1:创建当前类的实例
        //参数2:value 赋值的实际参数
        //取消Java语言访问检查
        nameField.setAccessible(true);
        //赋值
        nameField.set(obj,"高圆圆");
        System.out.println(obj);

        System.out.println("-----------------------------------------------");

        //public int age ;
        //获取当前age所在Field对象
        //public Field getField(String name):参数为字段名称
        Field ageField = clazz.getField("age");
        //直接赋值
        ageField.set(obj,20);
        System.out.println(obj);


        System.out.println("-----------------------------------------");
        //  String address ; //地址 默认修饰符
        //获取Field对象
        Field addressField = clazz.getDeclaredField("address");
        //取消Java语言访问检查
        addressField.setAccessible(true);
        //赋值
        addressField.set(obj,"西安市");
        System.out.println(obj);

    }

}

获取成员方法

public class ReflectDemo {
    public static void main(String[] args)  throws  Exception{
        //没有使用反射之前
        Person p = new Person() ;
        p.show();

        System.out.println("--------------------------");


        //直接反射方式获取
        //1)获取当前Person字节码文件对象
        Class clazz = Class.forName("com.qf.reflect_03.Person") ;
        Object obj =clazz.newInstance() ;//当前类实例

        //2)获取所有的成员方法:公共的(包含他父类的所有的公共的方法)
        //public Method[] getMethods()
        //public 方法[] getDeclaredMethods():通过此表示类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。
//        Method[] methods = clazz.getMethods();
       /* Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }*/


       //获取指定的方法 调用public void show()
       // public Method getMethod(String name,Class<?>... parameterTypes)
        //参数1:方法名
        //参数2:当前这个方法所携带的参数类型的Class
        Method showMethod = clazz.getMethod("show");
        //Method:底层调用方法
        //public Object invoke(Object obj,Object... args):将方法的实际参数绑定在当前类的实例上
        //参数1:就是当前类的实例
        //参数2:可变参数, 方法传递实际参数  若无参,则实际不用传递
        //返回值:如果当前方法本身有返回值,则返回Object

        showMethod.invoke(obj) ;


        System.out.println("--------------------------------------");
        //private  String functioin(String str){
        //        return str ;
        //    }

        //调用Person类中function
        //获取function方法的Method对象
        //public Method getDeclaredMetho(String name,Class<?> ..parameterTypes).
        Method functonMethod = clazz.getDeclaredMethod("functioin", String.class);
        //取消Java语言访问检查:私有方法
        functonMethod.setAccessible(true);
        Object returnObj = functonMethod.invoke(obj, "hello,洪学佳,别睡了...");
        System.out.println(returnObj);


        // void method(String message,int num){
        //        System.out.println(message+num);
        //    }
        //
        //    public int method2(){
        //        return 100 ;
        //    }


    }
}

反射的应用

  • 现在有一个ArrayList,里面有一些元素,如何给ArrayList添加String类型的元素呢?
public class ReflectTest {
    public static void main(String[] rgs) throws Exception {

        //有一个ArrayList集合对象
        ArrayList<Integer> arrayList  = new ArrayList<>() ;
        arrayList.add(100) ;
        arrayList.add(50) ;
        arrayList.add(200) ;
        System.out.println(arrayList);

       // arrayList.add("hello") ;

        //1)获取当前集合实例所在的字节码文件对象
        //通过Object的getClass()获取当前实例所在的类的字节码对象
        Class clazz = arrayList.getClass();

        //System.out.println(obj);
        //System.out.println(clazz);//class java.util.ArrayList
        //获取当前类中 add方法所在的Method
        //public boolean add(E e)  ://参数就是任意Java类型 (Element)
        Method addMethod = clazz.getMethod("add", Object.class);
        //调用方法
        addMethod.invoke(arrayList, "hello");
        addMethod.invoke(arrayList, "world");
        addMethod.invoke(arrayList, "javaEE");

        System.out.println(arrayList);
    }
}

反射的应用2

  • 需求;
    现在有一个学生类以及工人类,两类都有一个love方法
    ReflectTest2在测试类中进行测试
  • 1)起初,创建的一个学生对象,调用love方法
  • 2)代码更改,创建一个工人类对象,调用love方法
  • Java设计原则:
  • 开闭原则:对修改关闭,对扩展开放 (在现有代码进程上,想办法进行扩展…)
    如果提供配置文件,以后只需要修改配置文件中内容,而不更改当前的代码!

学生类

public class Student {
    public void love(){
        System.out.println("爱学习,爱Java,爱高圆圆...");
    }
}

工人类

public class Worker {
    public void love(){
        System.out.println("爱生活,爱drink...,爱足球");
    }
}

测试类

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

        //1)现在学生类对象
       /* Student s1 = new Student() ;
        s1.love();*/

        //需求改变:创建工人类对象
        /*Worker worker  = new Worker() ;
        worker.love();*/

        //优化:提供src(类路径下提供配置文件)myClass.properties
        //1)读取src下面的myClass.properties
        //获取资源文件所在的输入流对象
        InputStream inputStream = ReflectTest2.class
                .getClassLoader().
                getResourceAsStream("myClass.properties");

        //创建属性集合列表:空的
        Properties prop = new Properties() ;
        //将指定的配置文件所在的输入流加载属性列表中
        prop.load(inputStream);
        System.out.println(prop);

        //可以通过可以key获取value
        String className = prop.getProperty("className") ; //当前类的全限定名称
        String methodName = prop.getProperty("methodName") ;

        //通过反射创建当前类的字节码文件对象
        Class clazz = Class.forName(className) ;
        //创建当前类的实例 (解耦)
        Object obj = clazz.newInstance() ;
        //通过clazz字节码文件对象获取当前成员方法所在的Method类对象
        Method method = clazz.getMethod(methodName) ;
        method.invoke(obj) ;
    }
}

反射的应用3(网络聊天室)(加入多线程)

  • 按照聊天室需求:完成基本雏形,客户端不断发送消息,服务器不断读取并给客户端回复小写,客户端再次不断读取发送的消息,展示
  • 如果网络有延迟操作,可能出现 消息阻塞消息…
  • 现在代码不好的原因:
    不管是客户端还是服务器端,将读和写的过程 都放在主线程中,不好; 就会出现阻塞!
  • 使用多线程的思想去解决:
    将读放在子线程中,还是写放在子线程中!
    一般子线程中不能录入数据的,所以将读取消息放在子线程中…

客户端

TCP方式(两端不断发消息和读消息)

public class ChatRoomClient {

    public static void main(String[] args) {
        Socket socket = null ;
        try {
            //创建客户端Socket对象
             socket = new Socket("10.12.156.107",8888) ;

            //获取客户端通道内的输入和输出流
            InputStream in = socket.getInputStream();
            OutputStream out = socket.getOutputStream();

            //创建键盘录入对象
            Scanner sc = new Scanner(System.in) ;

            ClientThread ct = new ClientThread(in) ;
            ct.start();

            //不断发送消息
            while(true){

                //自定义结束条件 :over/886结束

                System.out.println("请输入消息:");

                //约定的消息格式: 接受者:消息内容:发送者
                //1:你好:0
                String msg = sc.nextLine() ;
                //通过的通道内的输出流写过去
                out.write(msg.getBytes());

                //读取服务器的回复的数据
                /*byte[] bytes = new byte[1024] ;
                int len = in.read(bytes);
                String serverMsg = new String(bytes,0,len  ) ;
                System.out.println(serverMsg);*/
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(socket!=null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

客户端读取消息的子线程

public class ClientThread extends Thread {
    private InputStream in ;
    public ClientThread(InputStream in){
        this.in = in ;
    }
    @Override
    public void run() {
        try {
            while(true){
                byte[] bytes = new byte[1024] ;
                int len = in.read(bytes);
                String serverMsg = new String(bytes,0,len  ) ;
                System.out.println(serverMsg);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务端

public class ChatRoomServer {
    public static void main(String[] args) {
        try{
            ServerSocket ss = new ServerSocket(8888) ;


            System.out.println("服务器端已经开始,等待客户端连接");
            int i = 1 ;
            //创建ArrayList集合里面存储的客户端Socket对象,每一个客户端都对应自己的Socket
            ArrayList<Socket> list = new ArrayList<>() ;
            //监听客户端连接  (不断监听客户端连接)
            while(true){

                Socket socket = ss.accept();
                System.out.println("第"+(i++)+"个客户端已连接...");
                list.add(socket) ;

                //获取当前客户端对象所在的通道内的输入和输出流
              /*  InputStream in = socket.getInputStream();
                OutputStream out = socket.getOutputStream();*/

                //开启服务器端读取消息子线程
                //ServerThread st = new ServerThread(in) ;
                ServerThread st = new ServerThread(socket,list) ;
                st.start();
            }


            //创建键盘录入对象
//            Scanner sc = new Scanner(System.in) ;

            //不断的读取消息

                //定义数组
               /* byte[] bytes = new byte[1024] ;
                int len = in.read(bytes);
                //获取实际内容
                String receiveMsg = new String(bytes,0,len) ;
                //展示数据
                System.out.println(receiveMsg);*/



         /*   while(true){*/

                /*//开启一个读取客户端消息 子线程
                ServerThread st = new ServerThread(in) ;
                st.start();*/

             /*   //服务器端在回复数据
                System.out.println("请回复消息:");
                String fkMsg = sc.nextLine() ;
                //通过通道内输出写过去
                out.write(fkMsg.getBytes());*/
           // }

        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

服务器端读取消息的子线程

public class ServerThread extends Thread {

   // private InputStream in ;
    //public ServerThread(InputStream in){
      //  this.in = in ;
    //}
    private ArrayList<Socket> list ;
    private Socket socket ; //客户端的Socket对象
    public ServerThread(Socket socket ,ArrayList<Socket> list){
        this.socket = socket ;
        this.list = list ;
    }


    @Override
    public void run() {
        try {
            while(true){
                //通过socket获取通道内输入输出流

                InputStream in  = socket.getInputStream();
                OutputStream out = socket.getOutputStream() ;
                byte[] bytes = new byte[1024] ;
                int len = in.read(bytes);
                //获取实际内容
                String receiveMsg = new String(bytes,0,len) ;
                //展示数据
                System.out.println(receiveMsg) ; //"1:你好:0"

                //将获取的消息内容进行拆分:  通过 ":"
                String[] strs = receiveMsg.split(":");
                //strs[0] : 接受者
                //strs[1] :消息内容
                //strs[2] :发送者

                //String --->strs[0]---->int Integer.parseInt(String)
                int receiver = Integer.parseInt(strs[0]);
                //获取通道内的Socket对象:接受者所在的通道内的
                Socket reverSocket = list.get(receiver);
                //获取接受者所在的通道的内的输出流对象,写他
                OutputStream receiverOutputStream    = reverSocket.getOutputStream();

                //转发消息格式: str[2]+"对你说" +strs[1]
                receiverOutputStream.write((strs[2]+"对你说:"+strs[1]).getBytes());


            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

DDL语句:数据库定义语句

库/表的增删查改

查询当前mysql下所有的数据库

mysql8.0 自带的 跟5.5自带的不一样

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |    mysql默认的一些配置
| mysql              |    mysql库里面包含user表用户表 (权限用户)
| performance_schema |    mysql性能相关的库
| sakila             |    提供其他三个库 sakila sys world  提供了一些例库   练习sql语句
| sys                |
| world              |
| zy                 |  自定义的库(开发者自定义)
+--------------------+
7 rows in set (0.00 sec)

创建数据库

create database 库名 ;

方式1
mysql> create database mydb_01;  
Query OK, 1 row affected (0.02 sec)

create database if not exists 库名;

方式2
mysql> create database if not exists mydb_02;
Query OK, 1 row affected (0.02 sec)

查看创建数据库的默认的字符集(了解)

show create database 库名;

mysql> show create database mydb_01;
+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| Database | Create Database                                                                                                                   |
+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| mydb_01  | CREATE DATABASE `mydb_01` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */ |
+----------+-----------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

修改数据库的字符集(了解)

alter database 库名 (default)可以省略 character set=字符集;

mysql> alter database mydb_01 default character set gbk ;
Query OK, 1 row affected (0.01 sec)
mysql> show create database mydb_01;
+----------+----------------------------------------------------------------------------------------------------+
| Database | Create Database                                                                                    |
+----------+----------------------------------------------------------------------------------------------------+
| mydb_01  | CREATE DATABASE `mydb_01` /*!40100 DEFAULT CHARACTER SET gbk */ /*!80016 DEFAULT ENCRYPTION='N' */ |
+----------+----------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql>

删除库

drop database 库名;

drop database if exists 库名;

mysql> drop database mydb_02;
Query OK, 0 rows affected (0.02 sec)

mysql> drop database if exists mydb_03;
Query OK, 0 rows affected (0.01 sec)

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mydb_01            |
| mysql              |
| performance_schema |
| sakila             |
| sys                |
| world              |
| zy                 |
+--------------------+

模糊查询mysql服务中所有的带character字符集的全局变量

show variables like '%character%' ;

mysql> show variables like '%character%' ;
+--------------------------+---------------------------------------------------------+
| Variable_name            | Value                                                   |
+--------------------------+---------------------------------------------------------+
| character_set_client     | gbk                                                      |
| character_set_connection | gbk                                                     |
| character_set_database   | utf8mb4                                                 |
| character_set_filesystem | binary                                                  |
| character_set_results    | gbk                                                     |
| character_set_server     | utf8mb4                                                 |
| character_set_system     | utf8mb3                                                 |
| character_sets_dir       | C:\Program Files\MySQL\MySQL Server 8.0\share\charsets\ |
+--------------------------+---------------------------------------------------------+
8 rows in set, 1 warning (0.01 sec)


如果出现中文乱码:
 character_set_client 客户端  utf8
  character_set_results  结果集 utf8



改动字符集:临时解决中文乱码
mysql> set character_set_client=utf8;
Query OK, 0 rows affected, 1 warning (0.01 sec)

mysql> set character_set_results=utf8;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> show variables like '%character%' ;
+--------------------------+---------------------------------------------------------+
| Variable_name            | Value                                                   |
+--------------------------+---------------------------------------------------------+
| character_set_client     | utf8mb3                                                 |
| character_set_connection | gbk                                                     |
| character_set_database   | ut
f8mb4                                                 |
| character_set_filesystem | binary                                                  |
| character_set_results    | utf8mb3                                                 |
| character_set_server     | utf8mb4                                                 |
| character_set_system     | utf8mb3                                                 |
| character_sets_dir       | C:\Program Files\MySQL\MySQL Server 8.0\share\charsets\ |
+--------------------------+---------------------------------------------------------+
8 rows in set, 1 warning (0.00 sec)

创建表

create table 表明(
	列名1 字段类型1,
    列名2 字段类型2,
    ...
    列名n 字段类型n
) ; 

数据库常用的字段类型
int------>默认int(11)  取的是当前实际长度 (推荐)
		id int,---编号			1 
		 
		
int(长度):指定长度
			id int(3)   		 1---->001 

varchar(字符长度):字符串类型数据   '' "" 不写 引号  ,习惯去使用''或者双引号
double(1,2):小数类型  
   举例
   		double(3,2) ----小数类型,3位数,小数点后保留2位
   					123.56
日期类型
  date  仅仅表示日期,不表示时分秒
  		"2021-8-12"
  datatime  不仅表示日期具体的时间
  		"2021-8-12 17:31:00"
  timestap:时间戳  
  		给表中插入数据, 显示当前插入/修改/删除/查询数据那一刻时间 (具体到秒"2021-8-12 18:00:01")
	
    
 注意事项:就是给那一个库中创建表  使用哪个库(选择数据库名)
 use 库名;
mysql> use mydb_01;
Database changed
mysql>


mysql> create table student(
    -> id int,
    -> name varchar(20),
    -> gender varchar(10),
    -> address varchar(50),
    -> age int
    -> )
    -> ;
Query OK, 0 rows affected (0.05 sec)
 

查询当前数据库中有哪些表

show tables ;

mysql> show tables;
+-------------------+
| Tables_in_mydb_01 |
+-------------------+
| student           |
+-------------------+
1 row in set (0.01 sec)

查询当前表的结构

desc 表名 ;

mysql> desc student;
+---------+-------------+------+-----+---------+-------+
| Field   | Type        | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id      | int         | YES  |     | NULL    |       |
| name    | varchar(20) | YES  |     | NULL    |       |
| gender  | varchar(10) | YES  |     | NULL    |       |
| address | varchar(50) | YES  |     | NULL    |       |
| age     | int         | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+
5 rows in set (0.01 sec)

修改表

给student表添加一列

alter table 表名 add 字段名称 字段类型;

mysql> alter table student add email varchar(50) ;
Query OK, 0 rows affected (0.04 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    |       |
| gender  | varchar(10) | YES  |     | NULL    |       |
| address | varchar(50) | YES  |     | NULL    |       |
| age     | int         | YES  |     | NULL    |       |
| email   | varchar(50) | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+
6 rows in set (0.01 sec)

修改表中的字段类型

alter table 表名 modify 字段名称 更改后的字段类型;

mysql> alter table student modify address varchar(30) ;
Query OK, 0 rows affected (0.07 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    |       |
| gender  | varchar(10) | YES  |     | NULL    |       |
| address | varchar(30) | YES  |     | NULL    |       |
| age     | int         | YES  |     | NULL    |       |
| email   | varchar(50) | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+
6 rows in set (0.00 sec)

修改表中的字段名称

alter table 表名 change 旧列名 新列名 字段类型;

mysql> alter table student change gender sex varchar(10) ;
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(10) | YES  |     | NULL    |       |
| address | varchar(30) | YES  |     | NULL    |       |
| age     | int         | YES  |     | NULL    |       |
| email   | varchar(50) | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+
6 rows in set (0.01 sec)

修改表:删除某一列字段

alter table 表名 drop 字段名称;

mysql> alter table student drop email;
Query OK, 0 rows affected (0.06 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(10) | YES  |     | NULL    |       |
| address | varchar(30) | YES  |     | NULL    |       |
| age     | int         | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+
5 rows in set (0.01 sec)

修改表的表名

alter table 表名 renameto 新表名;

mysql> alter table student rename to stu;
Query OK, 0 rows affected (0.03 sec)

mysql> show tables;
+-------------------+
| Tables_in_mydb_01 |
+-------------------+
| stu               |
| teacher           |
+-------------------+
2 rows in set (0.01 sec)

复制一张表结构和以前表的结果一样

create table 新表名 like 旧表名;

 
mysql> create table teacher like student; //字段复制
Query OK, 0 rows affected (0.03 sec)

mysql> show tables;
+-------------------+
| Tables_in_mydb_01 |
+-------------------+
| student           |
| teacher           |
+-------------------+
2 rows in set (0.01 sec)

删除表

drop tabble 表名;

drop table if exists 表名;

mysql> drop table if exists teacher;
Query OK, 0 rows affected (0.03 sec)

mysql> show tables;
+-------------------+
| Tables_in_mydb_01 |
+-------------------+
| stu               |
+-------------------+
1 row in set (0.00 sec)

DML语句:操作表的记录

插入语句

insert into 表名 values(值1,值2,值3…) ;插入全表数据 :那么每一个参数值必须和字段类型匹配! (不会报错,警告)

mysql> insert into stu values(1,'高圆圆','女','西安市',30) ;
Query OK, 1 row affected (0.01 sec)

最基本的查询:查询全表数据

select * from 表名;

mysql> insert into stu values(1,'高圆圆','女','西安市',30) ;
Query OK, 1 row affected (0.01 sec)

mysql> select * from stu ;
+------+--------+------+---------+------+
| id   | name   | sex  | address | age  |
+------+--------+------+---------+------+
|    1 | 高圆圆 || 西安市  |   30 |
+------+--------+------+---------+------+
1 row in set (0.01 sec)

如果中文乱码
改动下面的字符集编码即可
mysql> show variables like '%character%' ;
+--------------------------+---------------------------------------------------------+
| Variable_name            | Value                                                   |
+--------------------------+---------------------------------------------------------+
| character_set_client     | gbk                                                     |
| character_set_connection | gbk                                                     |
| character_set_database   | gbk                                                     |
| character_set_filesystem | binary                                                  |
| character_set_results    | gbk                                                     |
| character_set_server     | utf8mb4                                                 |
| character_set_system     | utf8mb3                                                 |
| character_sets_dir       | C:\Program Files\MySQL\MySQL Server 8.0\share\charsets\ |
+--------------------------+---------------------------------------------------------+
8 rows in set, 1 warning (0.00 sec)

mysql> set character_set_client = 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)

DML语句: 数据库操作语句,操作表记录

insert into 插入数据

 1)插入全表数据insert into 表名 values(1,2,.....);
INSERT INTO stu VALUES(2,'赵又廷','男','北京市',32) ;
-- 插入全表:一次性插入多条数据
-- 语法2) insert into 表名 values(值1,值2....),(值1,值2...),(值1,值2....) ;
INSERT INTO 
  stu 
  VALUES 
  (3,'文章','男','西安市',35),
  (4,'马伊琍','女','上海市',42),
  (5,'德玛西亚','男','艾欧尼亚',25) ;


-- 插入部分字段 :语法3  insert into 表名(部分字段1,字段2...) values(值1,值2...)
INSERT INTO 
stu(NAME,sex,address) VALUES('王宝强','男','咸阳市') ;

-- 注意:插入部分字段,必须字段数量和插入值的数量必须匹配
-- insert into stu(id,name,sex) values(6,'姚笛') ;
-- 不管插入部分字段还全部字段,那么必须要和当前类型一致
INSERT INTO 
 stu 
 
VALUES(6,'男','马嘉钰','西安市',20) ;

修改表的数据 update

 语法:按条件修改  update 表名 set 字段名称 =where 字段名称=; 修改一个字段
-- 修改name是王宝强的将他的id设置为7
UPDATE stu SET id = 7 WHERE NAME = '王宝强' ;
-- 修改id为7的age设置为37
UPDATE stu SET age = 37 WHERE id =  7 ; 

一次修改多个字段

-- update 表名 set 字段名称1 =值1 ,字段名称2 =值2 where 条件;
-- 将id为的6的姓名修改为 '张三丰' ,性别修改为'男' 
UPDATE 
	stu
SET 
	NAME = '张三丰',
	sex = '男'
WHERE	
	id =  6 ;
	
-- 如果不带条件修改:就是批量修改
-- 将修改name为 '高圆圆'
UPDATE stu SET NAME = '高圆圆' ;   -- 一般都是带条件进行修改!

删除

-- 带条件来删除
-- delete from 表名  where 字段名称 = 值; //删除指定的记录
-- 删除id = 6的这条记录
DELETE FROM stu WHERE id = 6 ;

-- 删除全表数据
-- delete from 表名;
DELETE FROM stu ;
-- truncate table  表名;
TRUNCATE TABLE stu ;


/*
	删除全表的语法
	delete from 表名
	truncate table  表名; 两个区别
	
	1)delete from 表名:只是删除全表数据;表的结构还存在,
		如果表中 存在主键并且自增长约束,那么不会受影响,下一次在插入数据
		继续在之前的基础上继续自增长!
		
	2)truncate table 表名 ; 
		将表中数据删除的同时删除了表,然后在创建一张一模一样空表,
		肯定影响自增长主键的值,再次插入数据,自增长从1开始...
		
		
		等价于
			drop table my_use;
			创建一个当前一模一样的表结构
	
*/


-- 创建一张新的表 my_user表
CREATE TABLE my_user(
	id INT PRIMARY KEY AUTO_INCREMENT, -- 用户编号 主键加增长
	NAME VARCHAR(20),
	age INT ,
	gender VARCHAR(5)
) ;

INSERT INTO my_user(NAME ,age ,gender) 
	VALUES('张三',20,'男'),('李四',22,'女'),('文章',25,'男')  ;
	
INSERT INTO my_user(NAME,age,gender) VALUES('王五',18,'男') ;
-- delete from my_user where id =  8 ;
	
-- 删除全表数据
DELETE FROM my_user ;	

-- 删除全部数据
TRUNCATE TABLE my_user ;

SELECT * FROM my_user;

DQL语句:查询语句

查询全表数据

SELECT * FROM stu ; 
-- 实际开发中,* (通配符),一般查询全表数据,需要带全部字段 
SELECT   -- 指定全部字段
	id ,
	NAME,
	sex,
	address,
	age
FROM  stu ;	

查询指定的字段

 select 字段名称列表 from 表名;
SELECT 
	id ,
	NAME ,
	address,
	age
FROM stu ;

-- 查询指定字段时可以通过as 关键字指定别名,as可以省略
SELECT
	id AS '学生编号',
	NAME AS '姓名',
	address AS '地址',
	age AS '年龄'
FROM stu ;
-- as 可以省略
SELECT
	id  '学生编号',
	NAME  '姓名',
	address  '地址',
	age  '年龄'
FROM stu ;

基本查询:字段去重

delete distinct

DQL带条件查询

条件查询(where)

 可以基本运算符:比较运算符(<,>,<=,>=,!=)/逻辑运算符(|| && /and /or)/赋值运算符 =
--  where后面多个or     in(集合数据)    在两个值之间 between 值1 and 值2
--  mysql 判断某个字段为null , is null  /is not null

-- 模糊查询 like
-- 聚合函数 count(列名)/max(列名)/min(列名)/sum(列名)/avg(列名)
-- 排序查询 order by
-- 分组查询 group by
-- 筛选查询 having
-- 分页查询limit


-- 创建一个学生表
CREATE TABLE student ( 
 id INT, -- 编号
 NAME VARCHAR(20), -- 姓名
 age INT, -- 年龄 
 sex VARCHAR(5), -- 性别 
 address VARCHAR(100), -- 地址 
 math INT, -- 数学
 english INT -- 英语 
 );
 
 DESC student ;
 INSERT INTO student(id,NAME,age,sex,address,math,english) VALUES 
 (1,'马云',55,'男',' 杭州',66,78),
 (2,'马化腾',45,'女','深圳',98,87),
 (3,'马景涛',55,'男','香港',56,77),
 (4,'柳岩 ',20,'女','湖南',76,65),
 (5,'柳青',20,'男','湖南',86,NULL),
 (6,'刘德华',57,'男','香港 ',99,99),
(7,'马德',22,'女','香港',99,99),
(8,'德玛西亚',18,'男','南京',56,65);

条件查询

-- 查询年龄大于20岁的人的全部信息
SELECT 
	id  '编号',
	NAME '姓名',
	age '年龄',
	sex '性别',
	address '地址',
	
	math '数学成绩',
	english '英语成绩'
FROM 
	student 
WHERE
	age > 20 ;
-- 查询年龄在20岁和30岁之间的学生信息
SELECT		
	*
FROM
	student
WHERE
	age >= 20 && age <=30 ; -- &&:Java的逻辑运算符
-- 另一种语法	
SELECT		
	*
FROM
	student
WHERE
	age >= 20 AND  age <=30 ;-- and mysql表示并列关系
-- 另一种语法:mysql 	在两个值之间 between 值1 and 值2
SELECT -- 查询部分字段 并且年龄在20-30
	NAME '姓名',
	age '年龄',
	address '地址',
	math '数学成绩',
	english '英语成绩'

FROM 
	student 
WHERE
	age 
BETWEEN 20 AND 30 ;

-- 查询年龄是18或者20或者30岁的学生的编号,姓名,年龄以及地址   (|| 或者 or)
SELECT
	
	id  '编号',
	NAME '姓名',
	age '年龄',
	address '地址'
FROM 
	student 
WHERE 
	age = 18 OR age = 20 OR age = 30 ;
	
-- mysql另一种语法 where 字段名称 in(多个值)
SELECT
	
	id  '编号',
	NAME '姓名',
	age '年龄',
	address '地址'
FROM 
	student 
WHERE 
	age 
IN(18,20,30) ;
	
 
 -- 查询英语成绩为null的学号编号,姓名,性别,地址.数学成绩信息
/* 
 select
	id ,
	name,
	sex,
	address,
	math
from
	student
where 
     english == null ; -- java中的用法可以这样用   == = (都不行)
*/				
SELECT
	id ,
	NAME,
	sex,
	address,
	math,
	english
FROM
	student
WHERE 
     english IS NULL ; -- mysql用法
     
-- 查询英语成绩不为null的人 ,is not null 的用法
    
 SELECT
	id ,
	NAME,
	sex,
	address,
	math,
	english
FROM
	student
WHERE 
     english IS NOT NULL ;   
     
 -- 查询英语和数学成绩总分的学生新
 SELECT 
	id 编号 ,
	NAME 姓名 ,
	sex 性别 ,	
	address	'地址',
	-- (math+english) as '总分'
	(math+IFNULL(english,0)) '总分'
	
FROM 
	student ;


-- mysql 内置函数ifnull(值1,值2) ; ifnull(english,0) :
-- 如果当前英语成绩为null,给默认值0

-- 查询地址
SELECT
	address  地址
FROM student ;
-- 查询字段,对字段去重 (distinct)
SELECT 
    DISTINCT address 地址 
    FROM student ;
    
    
 -- 查询年龄不是20岁的学生信息
 SELECT
	
	*
FROM  student
WHERE age != 20 ;  -- != Java中这种语法 !=
-- mysql中的不等于 <> 
SELECT 
	*
FROM 
	student
WHERE
	age <> 20 ;

模糊查询(like)

mysql服务中带字符集相关的变量 show variables    like '%character%' ; 
-- 模糊查询 like
-- select 字段列表  from 表名 where 字段名称 like  '%字符%' ;
/*
	% :包含的指定的字符  使用'%字符值%' 模糊查询包含指定字符的信息
	_ :代表单个字符(一个_下划线代表一个字符)
	两个相结合使用: '_%字符值%_'  三个字符:中间字符包含指定的值进行模糊查询
*/
-- 查询当前学生表中姓名包含马的学生信息
SELECT 
	*
FROM 
	student
WHERE
	NAME 
LIKE 
	'%马%' ;
-- 查询第二个字符包含化的学生信息
SELECT
	*
FROM
	student
WHERE 
	NAME
LIKE
	'_%化%' ;
	
-- 查询姓名是三个字符的人
SELECT 
	*
FROM 
	student
WHERE 
	NAME
LIKE
	'___' ;
	
-- 应用场景: 搜索框中输入关键字查询---使用到模糊查询

聚合函数查询(count,max,min,sum,avg)

查询结果:单行单列的数据
-- count(列名) :总记录数
-- max(列名): 最大值
-- min(列名字段):最小值
-- sum(字段名称):求和
-- avg(列名):平均分
-- select 聚合函数(列名) from 表名;

-- 查询当前student这个表的全部总记录数 
-- 如果使用english字段查询总记录数
SELECT 
	COUNT(english) -- 使用业务字段查询(可能某个值为null,不会进行记录)
FROM 
	student ;  -- 7条记录
-- 可以使用函数设置如果当前某个值null,给默认值
SELECT	
	COUNT(IFNULL(english,0)) 总记录数
FROM
	student;
/*
		count(列名)查询总记录数的时候,一般都使用非业务字段查询
		student	
			id(非业务字段)	name  age  gender  (业务字段)
		学校里面的学生表
				某个学生刚开始还在学习, 后面转学了 (非业务字段id---主键+自增长的)
			1		张三  20    男
			select	
				count(id)
			from 表名;
	
*/	
-- 建议id来查询
SELECT
	COUNT(id) 总条数
FROM 
	student ;
	
-- 查询数学的平均成绩 avg(列名)
SELECT
	AVG(math) '数学平均成绩'
FROM
	student ;	-- 79.5000
	
-- 查询英语成绩---总成绩(求和 sum(列名))
SELECT
	SUM(IFNULL(english,0)) 英语总分
	
FROM
	student ;
	
-- 查询英语成绩的最高成绩
SELECT
     
      MAX(IFNULL(english,0)) 英语最高分
FROM 
	student ;

-- 查询数学成绩最低分
SELECT
   MIN(math) 数学最低分
 FROM
	student ;
	 
-- 聚合函数使用最多的:count函数,avg函数

排序查询(order by)

排序查询:order by 字段名称  asc/desc (升序/降序)
-- select 字段列表 from 表名 order by 字段名 排序规则; -- 单个字段进排序

-- 数学成绩安装升序排序
SELECT
       *
FROM
	student 
ORDER BY math  ;  -- 如果字段名称后面没有带排序规则:则默认升序排序
-- 英语降序排序
SELECT 
	NAME ,
	age,
	sex,
	address,
	IFNULL(english,0) 英语成绩
FROM 
      student
ORDER BY english DESC ; -- 降序

-- 针对多个字段同时排序,当前第一字段值相等,则按照第二个字段的排序规则执行
-- select  字段列表  from 表名 order by 字段名称1 升序1 ,字段名称2 升序2;
-- 查询全表数据,数学成绩降序,英语成绩升序
SELECT
	*
FROM
	student 
ORDER BY   
	math DESC,
	english ASC ;

分组查询(group by)

分组查询:group by  
-- 分组group by 分组字段;
--  查询的时候可以查询分组字段, 
-- group by 后面不能使用聚合函数

-- 问题:如果group by 和where条件语句一块使用,先后顺序?   where之后 才能使用group by

-- 现在需要按照性别分组-----分组之后查询出总人数 
-- 性别-- 男/女 
SELECT
       -- 查询分组字段
       sex '性别',
       COUNT(id) '人数'

FROM
	student
GROUP BY 
	sex  ; -- 性别
	
-- 现在需要按照性别分组-----分组之后查询出总人数,数学的平均分
-- 条件:数学成绩不大于70分的人不参与分组;

/*	
SELECT
       -- 查询分组字段
       sex '性别',
       COUNT(id) '人数'

FROM
	student
GROUP BY 
	sex   -- 性别
where 
	math > 70 ;
*/

SELECT
	sex '性别', -- 查询分组字段
	COUNT(id) '总人数',
	AVG(math) '数学平均成绩'

FROM
	student
WHERE  	
	math > 70 -- 条件:数学成绩大于70分人参与分组
GROUP BY 
	sex ; -- 性别分组

筛选查询(having)

-- 现在需要按照性别分组-----分组之后查询出总人数,数学的平均分
-- 条件:数学成绩不大于70分的人不参与分组 
-- 筛选条件:总人数大于2的一组

-- having 必须置于group by 之后,where 置于 group by 之前
-- group by不能聚合函数,但是having后面可以聚合函数
SELECT
	sex '性别', -- 查询分组字段
	COUNT(id) '总人数',
	AVG(math) '数学平均成绩'

FROM
	student
WHERE  	
	math > 70 -- 条件:数学成绩大于70分人参与分组
GROUP BY 
	sex  -- 性别分组
	
HAVING  
	COUNT(id) > 2 ;
-- 优化	
SELECT
	sex 性别, -- 查询分组字段
	COUNT(id) 总人数,
	AVG(math) 数学平均成绩

FROM
	student
WHERE  	
	math > 70 -- 条件:数学成绩大于70分人参与分组
GROUP BY 
	sex  -- 性别分组
	
HAVING  
	总人数 > 2 ;	

分页查询(limit)

 select 字段列表 from 表名  limit 起始行数,每页显示多少条;

-- 给student表在插入三条记录
INSERT INTO  student VALUES(9,'德邦',19,'男','西安',78,58) ,
(10,'vn',20,'女','宝鸡',89,65),(11,'亚索',22,'男','西安',95,74);


-- 每页显示3条记录
-- 查询第一页的数据
-- limit 起始行数=(当前页码数-1)*每页显示的条数,每页显示条数;
SELECT * FROM student LIMIT 0,3 ;
-- 查询第二页的数据
SELECT * FROM student LIMIT 3 ,3 ;
-- 查询第三页数据
SELECT * FROM student LIMIT 6,3 ;
-- 第四页数据
SELECT * FROM student LIMIT 9,3 ;

数据库约束

-- 约束用户操作表的一种行为
-- 创建一个新的表
CREATE TABLE test(
	id INT , -- 编号
	NAME VARCHAR(10) , -- 姓名
	gender VARCHAR(2) DEFAULT '女'  -- 性别  -- 默认约束 防止出现非法数据null(没有插入造成null值)
	
) ;

1)默认约束(default)

默认约束,当前没有给那个字段设置值的时候,此时默认约束就会起作用

DROP TABLE test ;

INSERT INTO test VALUES(1,'张三','男') ;
-- 可能用户操作数据库的时候,插入非法数据(没有意义的数据)
-- insert into test values(2,null,'女') ;
-- 如果没有给某个字段赋值,默认值null
INSERT INTO test(id,NAME) VALUES(2,'高圆圆') ;


-- 通过sql语句修改表的类型,删除默认约束
ALTER TABLE  test MODIFY gender VARCHAR(2) ;

INSERT INTO test(id,NAME) VALUES(3,'文章') ;

-- 修改表加入默认约束
ALTER TABLE  test MODIFY gender VARCHAR(2)  DEFAULT '女' ;
 DELETE FROM test WHERE id = 3 ;

2)非空约束(NOT NULL)

DROP TABLE test ;
CREATE TABLE test(
	id INT ,
	NAME VARCHAR(10) NOT NULL  -- 非空约束
);

INSERT INTO test VALUES(1,NULL) ; -- 直接插入null值
-- insert into test (id) values(1) ; 没有给姓名赋值

INSERT INTO test VALUES(1,'') ; -- 存在值,只是空字符 和null不一样

-- 删除非空约束
ALTER TABLE test MODIFY NAME VARCHAR(10) ;
UPDATE test SET NAME = '高圆圆' WHERE id = 1 ;
INSERT INTO test VALUES(2,NULL) ; -- Column 'NAME' cannot be null

-- 修改表,加入非空约束
ALTER TABLE test MODIFY NAME VARCHAR(10) NOT NULL  ;
DELETE FROM test WHERE id = 2 ;

3)唯一约束(unique)

DROP TABLE test;
CREATE TABLE test(
	id INT ,
	NAME VARCHAR(10),
	phone VARCHAR(11) UNIQUE -- 唯一约束 :可以有null值,不能重复
) ;
INSERT INTO test VALUES(1,'张三','13666668888') ;
-- INSERT INTO test VALUES(1,'张三',null) ;
-- INSERT INTO test(id,name) values(2,'李四') ;
INSERT INTO test VALUES(2,'李四','13666668889') ;
-- INSERT INTO test VALUES(2,'李四','13666668888') ;
-- Duplicate entry '13666668888' for key 'test.phone'


-- 通过语句删除唯一约束
-- alter table test modify phone varchar(11) ; 错误语法
-- 删除唯一约束的sql  alter table test drop index 字段名称;
ALTER TABLE test DROP INDEX phone ;

INSERT INTO test VALUES(4,'赵六','13666668878') ;
DELETE FROM test WHERE id = 4 ;

-- 添加唯一约束
ALTER TABLE test MODIFY phone VARCHAR(11) UNIQUE ;

4)主键约束 (非空+唯一特点) (primary key)

 都会给当前非业务字段去设置主键(xxid)
DROP TABLE test  ;
CREATE TABLE test(
	id INT PRIMARY KEY ,   -- 非业务字段
	NAME VARCHAR(10),
	gender VARCHAR(2)
) ;

INSERT INTO test VALUES(1,'洪学佳','男'),(2,'马三奇','男') ;
--  insert into test values(1,'马嘉钰','男') ;id值重复 -- Duplicate entry '1' for key 'test.PRIMARY'
-- insert into test values(null,'雷郁','男') ;--  id直接插入null值 Column 'id' cannot be null
SELECT * FROM test ;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Django ORM 中,可以使用一些特殊的查询条件来过滤日期和时间类型的数据。下面是一些常用的查询条件: 1. `date`:筛选出指定日期的数据。 示例代码: ``` from datetime import date from myapp.models import MyModel MyModel.objects.filter(date_field=date.today()) ``` 2. `time`:筛选出指定时间的数据。 示例代码: ``` from datetime import time from myapp.models import MyModel MyModel.objects.filter(time_field=time(hour=9, minute=0)) ``` 3. `year`:筛选出指定年份的数据。 示例代码: ``` from myapp.models import MyModel MyModel.objects.filter(date_field__year=2021) ``` 4. `month`:筛选出指定月份的数据。 示例代码: ``` from myapp.models import MyModel MyModel.objects.filter(date_field__month=9) ``` 5. `day`:筛选出指定日期的数据。 示例代码: ``` from myapp.models import MyModel MyModel.objects.filter(date_field__day=15) ``` 6. `week_day`:筛选出指定星期几的数据,星期一为1,星期天为7。 示例代码: ``` from myapp.models import MyModel MyModel.objects.filter(date_field__week_day=1) ``` 除了以上的查询条件,还可以使用 `__range` 来筛选一段时间范围内的数据。例如: ``` from datetime import date from myapp.models import MyModel start_date = date(2021, 9, 1) end_date = date(2021, 9, 30) MyModel.objects.filter(date_field__range=(start_date, end_date)) ``` 以上是一些常用的查询条件,更多查询条件可以查阅 Django 官方文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值