0
点赞
收藏
分享

微信扫一扫

【Java11】Tcp,文件上传/UUID,Junit,反射/注解,自定义junit,类加载器,代理/模板/单例/工厂,XML,javase总结

流沙雨帘 2022-04-02 阅读 100

文章目录


1.Tcp单向通讯:" ".getBytes()

端游将页游好处和坏处相反。一个字节是2的8次方即256,所以0-255。ipv4组合方式共2的32(4*8=32)次方即43亿个。
在这里插入图片描述
ip相当于小区,port相当于门牌号
在这里插入图片描述
下面将三要素融合,https中s就是secure。
在这里插入图片描述
socket封装了io流,所以要关。高层socket关了,低层也会关。如下accept会阻塞。
在这里插入图片描述

package com.itheima01.tcp;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

public class TcpClient {
    public static void main(String[] args) throws IOException {        
        String host = "127.0.0.1"; //1.连接服务端(指定服务端的ip和port)自动连接
        int port = 10010;
        Socket socket = new Socket(host, port);
       
        OutputStream os = socket.getOutputStream();  //2.写出数据
        os.write(" hello, tcp".getBytes());
        socket.close();
    }
}
package com.itheima01.tcp;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(10010); //1.创建服务端Socket对象,并指定port。ip是路由器分配的
        System.out.println("1");        
        Socket socket = serverSocket.accept(); //2.获取客户端的连接(阻塞方法)
        System.out.println("2");        
        InputStream is = socket.getInputStream(); //3.假设接收到客户端连接 : 服务端读取数据  

        int length = -1;
        byte[] buffer = new byte[1024];
        while((length = is.read(buffer)) != -1){
            String msg = new String(buffer, 0, length);
            System.out.println("收到客户端的数据:" + msg);
        }
        socket.close();//关闭和客户端的连接
        serverSocket.close(); //关闭服务端
    }
}

2.Tcp双向通讯:socket中介

package com.itheima02.doubled;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
//准备,写,读
public class TcpClient {
    public static void main(String[] args) throws IOException {
        String host = "127.0.0.1";  //1.连接服务端 (指定服务端的ip和port) 自动连接
        int port = 10010;
        Socket socket = new Socket(host, port);

//11111111111111111111111111111111111111111111111111111111111111111111111  
        OutputStream os = socket.getOutputStream(); //2.写出数据
        os.write(" hello, tcp".getBytes());                
        socket.shutdownOutput(); //解决: 告知对方 写完了。没有关闭socket,关闭就连接不了

//1111111111111111111111111111111111111111111111111111111111111111111111 
        InputStream is = socket.getInputStream(); //3.客户端收到数据        
        int length=-1;
        byte[] buffer = new byte[1024];
        while((length = is.read(buffer)) != -1){
            String msg = new String(buffer, 0, length);
            System.out.println("收到服务端的数据:" + msg);
        }
        socket.close();
    }
}
package com.itheima02.doubled;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(10010); //1.创建服务端Socket对象,并指定port
        System.out.println("1");
        Socket socket = serverSocket.accept(); //2.获取客户端的连接(阻塞方法)
        System.out.println("2");

//111111111111111111111111111111111111111111111111111111111111111111111111111111111        
        InputStream is = socket.getInputStream(); //3.假设接收到客户端连接 : 服务端读取数据
        int length = -1;
        byte[] buffer = new byte[1024];
        /*
        *   问题: 如下read方法是一个阻塞方法
        *      以前: 读文件,读到末尾返回-1
        *      现在: 读socket中传输的数据 (像电话), 对方socket关掉就算是末尾了
        *   解决: 1. 对方在输出结束,可以直接关闭socket,这个数据就结束了,但是对方就不能再接收了
        *        2. 对方直接告知已经输出结束,socket不需要关
        */
        while((length = is.read(buffer)) != -1){//客户端socket.shutdownOutput();服务端就不会阻塞在这一行
            String msg = new String(buffer, 0, length);
            System.out.println("收到客户端的数据:" + msg);
        }  

//111111111111111111111111111111111111111111111111111111111111111111111111111111111111      
        OutputStream os = socket.getOutputStream(); //4.服务端写出数据  
        os.write(" 好,退下吧~~".getBytes()); 
        socket.close();//关闭和客户端的连接  //这里socket.shutdownOutput();可以不写,因为直接close了
        serverSocket.close(); // 关闭服务端
    }
}

3.案例_文件上传:ServerSocket

如下上面是服务端。
在这里插入图片描述

package com.itheima03.upload;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

public class UploadClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 10086);

//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111
        FileInputStream fis = new FileInputStream("柳岩.jpg");//读本地文件(da14模块下有个柳岩.jpg),写到服务器socket中        
        OutputStream os = socket.getOutputStream();    
                             
        int length = -1;
        byte[] buffer = new byte[1024];
        while((length = fis.read(buffer)) != -1){ //读
            os.write(buffer,0,length); //写
        }
        fis.close();
        socket.close();
    }
}
package com.itheima03.upload;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class UploadServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(10086);
        Socket socket = serverSocket.accept();	

//1111111111111111111111111111111111111111111111111111111111111111111111111111111	
        InputStream is = socket.getInputStream();         
        long time = System.currentTimeMillis();//系统当前时间毫秒值
        FileOutputStream fos = new FileOutputStream("c:/test/server/" + time + ".jpg");    
             
        int length = -1;
        byte[] buffer = new byte[1024];
        while((length = is.read(buffer)) != -1){ //读
            fos.write(buffer,0,length);  //写
        }
        fos.close();
        socket.close();
        serverSocket.close();
    }
}

4.多线程优化服务端与UUID:is和fos

package com.itheima04.good;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
/*
* 	服务器一般是24小时不关闭的,前面写的只能收到一张图片服务器就关闭了。解决方案: 
* 		1. 死循环
*          java.net.BindException: Address already in use: JVM_Bind。绑定异常(port重复占用)
* 
*       2. 想支持多个客户端的并发访问
*          解决: 引入多线程  代码加在阻塞之后
*          补充: 线程池 管理 线程(1. 避免线程频繁创建和销毁 2. 控制线程数量)
* 
*       2. 文件名 : 以系统当前时间命名(并发: 同一毫秒,有两个用户同时上传文件, 就会有用户文件覆盖)
*               1. 一般文件,不需要标识这个图片属于谁的 : 服务器用UUID                             
*               2. 文件名需要跟用户上传的文件名一致 : 需要另一个线程传输这个文件名(这个用户id + 文件名)
*/
public class UploadServer {
    public static void main(String[] args) throws IOException {        
        ServerSocket serverSocket = new ServerSocket(10086); //只创建一个服务端,一个端口不能被两个程序占用,不能在这行上面加while(true)
        while(true){  //死循环接收客户端请求            
            Socket socket = serverSocket.accept(); //虽然while(true),但这行会阻塞,单线程
            //有客户上门才开线程,不能无限开。
            new Thread(new Runnable() { //不能放accept()前面,不能无限开线程
                @Override
                public void run() {
                    try {
                        InputStream is = socket.getInputStream();                        
                        // long time = System.currentTimeMillis();//系统当前时间毫秒值
                        String name = UUID.randomUUID().toString().replace("-", ""); //下行time改为name
                        FileOutputStream fos = new FileOutputStream("c:/test/server/" + name + ".jpg");
                        
                        int length = -1;
                        byte[] buffer = new byte[1024];
                        while((length = is.read(buffer)) != -1){
                            fos.write(buffer,0,length);
                        }                        
                        fos.close(); //应该放在finally中
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
//         serverSocket.close(); // 服务器不能关闭,关了就不回到while(true)了
        }
    }
}
package com.itheima04.good;
import java.util.UUID;
// UUID: 通用唯一标识符(Universally Unique Identifier),永不重复
public class UuidDemo {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
//            UUID uuid = UUID.randomUUID();
//            String str = uuid.toString().replace("-", ""); 
            String str = UUID.randomUUID().toString().replace("-", ""); //这行同上两行
            System.out.println(str);
        }
    }
}

如下未把UUID的 - 替换为空。
在这里插入图片描述
如下BS案例:socket被写,页面被读。写一个浏览器程序难度和操作系统一样,鸿蒙os就是浏览器。
在这里插入图片描述

package com.itheima05.bs;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class BsServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(10090);
        Socket socket = serverSocket.accept(); //接收浏览器请求
        
//1111111111111111111111111111111111111111111111111111111111111111111111111111
        OutputStream os = socket.getOutputStream(); //写出页面
        os.write("HTTP/1.1 200 OK\r\n".getBytes()); // http协议的响应行  第一行
        os.write("Content-Type:text/html\r\n\r\n".getBytes()); //http协议的响应头 第二行,第三行为空白
        FileInputStream fis = new FileInputStream("index.html"); //已有,用来被读

        int length = -1;
        byte[] buffer = new byte[1024];
        while((length = fis.read(buffer)) != -1){
            os.write(buffer,0,length);  //第四行,响应体
        }
        fis.close();
        socket.close();
        serverSocket.close();
    }
}

在这里插入图片描述
在这里插入图片描述

5.Junit测试框架:手动加jar包

在这里插入图片描述

package com.itheima01.junit;
import org.junit.Test;
/*
*   使用Junit测试框架:1. 写一个方法,在方法声明上加上 注解 @Test
*       2. 导库 : jar包 (全称java archive : java档案包 ) -> 很多.class文件的压缩包
*          JDK : JVM + 很多jar包(这样开发者才能使用sun公司的很多类)
*          idea开发工具自动集成了 junit测试框架的jar包
*/
public class JunitDemo {
 /*   public static void main(String[] args) { //不用再写main方法了
        int i = 1/0;
        System.out.println(i);
    }*/
    @Test
    public void method01(){ 
        int i = 1/0;
        System.out.println(i); //出现异常,jvm要创建异常,打印异常信息io耗时
    }
} 

如下必须要文件类名和public class类名保持一致,一个类即一个文件。
在这里插入图片描述
如上产生如下jar包。
在这里插入图片描述
如上若失效解决:在工程目录下建一个与src并列的包test,并且右击test文件夹Mark Director as为Test Sources Root。File -> Project Structure->Modules再如下:
在这里插入图片描述

package com.itheima01.junit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.FileInputStream;
/*
*  Junit测试框架的使用规范
*       1. 写一个方法,在方法声明上加上 注解 @Test (如果当前项目没有集成Junit jar包, 需集成)
*       2. 方法声明
*               public void 方法名(空){  //里面必须为空,不能有参数
*                   要测试的代码
*               }
*       3. 使用:1. 把鼠标放在方法内, 可以使当前方法运行
*               2. 鼠标放在类中方法外, 可以使类中所有测试方法运行
*       4. 注解上:1. @Test : 核心注解
*                 2. @Before :  在每一个@Test注解之前都会运行(用在资源初始化上,可以少写代码)
*                 3. @After :  在每一个@Test注解之后都会运行(用在资源释放上)
*/
public class JunitDemo02 {
    FileInputStream fis;
    @Before
    public void method00(){
        System.out.println("在每个Test之前"); //注意 每个
    }
    @After
    public void method11(){
        System.out.println("在每个Test之后");
    }

//1111111111111111111111111111111111111111111111111111111111111111111111    
    @Test
    public void method01(){
        System.out.println("xx");
    }
    @Test
    public void method02(){
        System.out.println("yy");
    }
}

在这里插入图片描述

6.Class对象三种获取方式:Class.forName(" ");

package com.itheima02.clazz;
import org.junit.Test;
/*
*   反射前提: Class对象
*       1. 引用类型: Class(大写C和关键字class不一样)
*       2. 回顾: 当程序用到一个类的时候, 会加载这个类进内存(方法区)
*       3. 在运行时, java用Class对象来描述.class文件(就算是一个文件也用对象描述)
*              .class文件(硬盘) ->  Class对象(内存)
*       4. Class对象的特点:
*           1. Class对象的创建: 
* 				一个.class文件被加载进内存,JVM会创建这个类的Class对象。
* 				因为一个.class文件在使用的时候只需要被加载一次, 所以这个.class文件对应的Class对象也只会有一个!!!
* 
*           重点: Class对象(类对象)是JVM创建的, 开发者是无法创建的。(开发者可以new一个object,这叫实例对象)
* 				 一个类的Class对象一般只有一个 (使用: 同步锁 xxx.class对象:别人不能创建,天然唯一安全)
* 
*           2. 三种Class对象的获取(不是创建)
*               1. 类名.class
*               2. 对象名.getClass()
*               3. Class.forName(全限定名);  -> 加载配置文件
*/
public class ClassDemo {
    @Test
    public void method01(){  
        Class<Student> clazz = Student.class;            
        System.out.println(clazz); //class com.itheima02.clazz.Student //全类名或全限定名 : 包名 + 类名
        synchronized (ClassDemo.class){ //当前类名.class  -> 锁对象
           //这段代码能够运行,意味着当前类一定被加载进内存-> JVM会创建当前类的Class对象
          //ClassDemo.class改为string.class浪费内存,因为不一定用到string,没必要加载string类
        }
    }
    
    @Test
    public void method02(){
        Student s = new Student();
        Class<?> clazz = s.getClass();
        System.out.println(clazz); //class com.itheima02.clazz.Student 同上
        System.out.println(clazz == Student.class); //true
    }
    
    @Test
    public void method03() throws ClassNotFoundException {
        Class<?> clazz = Class.forName("com.itheima02.clazz.Student");
        System.out.println(clazz); //class com.itheima02.clazz.Student 同上
    }
}
package com.itheima02.clazz;
import java.util.Objects;

public class Student {
    private String name;    
    @Override  
    public boolean equals(Object o) {    
        if (this == o) return true;  //this表示student类
        // this.getClass() == o.getClass()
        // o instanceof Student
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return Objects.equals(name, student.name);
    }    
    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}

7.反射:clazz.newInstance()

在这里插入图片描述

package com.itheima03.reflect;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/*
 *  反射(reflect) : 在程序运行时操作类
 *       1. 运行时 :  .java源码 -> .class字节码(编译) -> runtime(运行)
 *       2. 操作 :   
 *       3. 类 :   .java  -> .class(二进制指令让jvm读) -> Class对象(运行时的类的样子)
 *            1. 构造方法 / 构造器  Constructor  
 *            2. 方法 Method   
 *            3. 属性 Field  
 *   A. 操作构造方法: 以前: 用构造方法来 创建对象
 */
public class ConstructorDemo {
    public static void main(String[] args) {
//        Person p = new Person(); //Person{name='null',age=0}
        Person p = new Person("张三");
        System.out.println(p); //Person{name='张三',age=0}
    }

//1111111111111111111111111111111111111111111111111111111111111111111111111111111111
    @Test
    public void method01() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 反射 : 操作构造方法
        //1. 获取Class对象
        //2. 再获取Class对象中的构造方法
        //3. 使用构造方法创建对象
        Class clazz = Person.class;
        
        /*
        *  Constructor<T> getConstructor(Class<?>... parameterTypes)
        *   1. 参数: 需要指定想要获取的构造方法的参数列表  类型
        *   2. 返回值: 返回构造方法的对象
        */
        //Constructor constructor = clazz.getConstructor(String.class,int.class);
        Constructor constructor = clazz.getConstructor(String.class);
        
        /*
        *   T newInstance(Object ... initargs)
        *       1. 参数: 使用构造方法创建对象时传入的实参 (必须跟上面参数列表类型一致)
        *       2. 返回值: 返回创建好的实例
        */
        Object obj = constructor.newInstance("张三");
        System.out.println(obj); //Person{name='张三',age=0}
    }

//11111111111111111111111111111111111111111111111111111111111111111111111111111111111
    @Test
    public void method02() throws NoSuchMethodException, IllegalAccessException,   InvocationTargetException, InstantiationException {  //简易的api
        Class clazz = Person.class;                
       /* Constructor constructor = clazz.getConstructor();
        Object obj = constructor.newInstance();
        System.out.println(obj); //Person{name='null',age=0}  */ 

 //Class对象获取空参构造,并创建实例,如下简单,等同于上面两行//(JavaBean要求: 1. private属性 2. public 空参构造 3. public get set方法)
        Object obj = clazz.newInstance();
        System.out.println(obj); //Person{name='null',age=0}
    }

//11111111111111111111111111111111111111111111111111111111111111111111111111111111111
    @Test
    public void method03() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //暴力反射: 知道(可以访问private方法)
        Class clazz = Person.class;
        /*
        *   getConstructor()  : 只能获取public修饰的构造
        *   getDeclaredConstructor() : 获取所有权限的构造(private也可以)
        */
//        Constructor constructor = clazz.getConstructor(String.class, int.class);
        Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);        
        constructor.setAccessible(true); //暴力反射: 权限设置public //上面能拿到私有,但是不能调用,只能person本类内部调用,所以加这行可以调用
        Object obj = constructor.newInstance("李四", 18); 
        System.out.println(obj); //Person{name='李四',age=18} 
    }
}
package com.itheima03.reflect;

public class Person {
    public String name;
    private int age;
    public Person(){
    }
    public Person(String name){
        this.name = name;
    }
    private Person(String name,int age){ //注意private,用暴力反射
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    public void speak(String msg){
        System.out.println("speak:" + msg);
//        return 1;
    }
    public void speak(double msg){
        System.out.println("speak:" + msg);
    }
    private void show(int year){
        System.out.println("show:" + year);
    }
}
package com.itheima03.reflect;
import org.junit.Test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
// B.方法的反射(反射中最重要的是构造方法和普通方法反射,没有属性)
public class MethodDemo {
    public static void main(String[] args) {
       Person p = new Person();
       // Person p = null; //speak方法设为静态,不报错
       p.speak("呵呵"); 
       System.out.println(number); //speak:呵呵
    }

//1111111111111111111111111111111111111111111111111111111111111111111111111111111111
    @Test
    public void method01() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //1. 获取Class对象
        //2. 获取方法 .getMethod
        //3. 调用方法 .invoke
        Person p = new Person();
        Class<?> clazz = p.getClass();
        /*
        *   Method getMethod(String name, Class<?>... parameterTypes)
        *   1 .参数
        *        1. name 方法名
        *        2. name 方法名的参数列表
        *   2. 返回值: 获取的这个方法
        */
        Method method = clazz.getMethod("speak", String.class);
        /*
        *   Object invoke(Object obj, Object... args)
        *   1. 参数
        *       1. obj : 调动此方法的对象,speak前加一个static,null也可以
        *       2. args : 调用此方法传入的实参
        *   2. 返回值: 调用此方法产生的返回值,如果此方法没有返回值,将会null
        * */
        Object result = method.invoke(p, "嘻嘻");
        //Object result = method.invoke(null, "嘻嘻"); //speak方法设为静态,不报错
        System.out.println(result); //speak:嘻嘻
    }

//11111111111111111111111111111111111111111111111111111111111111111111111111111111
    @Test
    public void method02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Person p = new Person();
        Class<? extends Person> clazz = p.getClass();
        Method show = clazz.getDeclaredMethod("show", int.class); //暴力反射   
        show.setAccessible(true); //临时修改权限
        show.invoke(p,15); //show:15
    }
}
package com.itheima03.reflect;
import org.junit.Test;
import java.lang.reflect.Field;
//属性的反射一般不用,关于属性值设置通过反射get和set方法,又回到了方法的反射
public class FieldDemo {
    public static void main(String[] args) {
        Person p = new Person();
        p.name = "啦啦";
        System.out.println(p); //Person{name='啦啦',age=0}
    }

//111111111111111111111111111111111111111111111111111111111111111111111111111111111
    @Test  //下面写那么多代码为了实现上面3行
    public void method01() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
        //1.获取Class对象
        //2.获取属性对象
        //3.操作属性,get / set
        Class<?> clazz = Class.forName("com.itheima03.reflect.Person");
        Object p = clazz.newInstance();
        /*
        * Field getField(String name)
        *   1. 参数: 属性名
        *   2. 返回值: 通过属性名找到的属性对象
        */
        Field age = clazz.getDeclaredField("age");
        age.setAccessible(true);
        /*
        * set(Object obj, Object value) 
        *   1. 设置属性的对象
        *   2. 设置的属性值
        */
        age.set(p,18); //很少用,一般get和set方法,又回到方法的反射
        System.out.println(p);//Person{name='null',age=18}
    }
}

8.注解与元注解:12种类型

package com.itheima04.annotation;
import java.util.Date;
/*
*       注解: annotation ( 注释;注解,java 四大类型)
*           1. 注释: 一般是给人看的代码解释性说明 (不参与编译, 运行)
*              注解: 给JVM看的代码 解释性说明(可能参与编译或运行)
*           2. 常见
*               1. @Override : 检测此方法是否是重写方法
*               2. @FunctionalInterface:此接口是否是函数式接口
*               3. @Deprecated : 加上方法上,声明此方法过时了(这个方法有更好的替代方案),但是还能用
*/
public class Demo01 {
    public static void main(String[] args) {
        new MyRunnable().run();
    }
}
class MyRunnable implements Runnable{
    @Deprecated
    @Override
    public void run() {
        System.out.println("---------");
    }
}
package com.itheima04.annotation; 
import java.util.Date;
/*
*   注解的基本语法
*       1. 关键字: @interface
*       2. 属性:  String name() default "yy"; (注解属性类型: 8大基本类型和String,枚举,注解,Class 。 以及这12种类型的数组。不包括Date等)
*       3. 注解没有方法
* 
*   注解的使用: 可以在代码上加注解
*   定义@Override :
*       1. 适用范围 : 方法上
*       2. 生命周期(从创建到销毁): 
*           SOURCE : 源码  (@Override写在这个时候,没必要到下面两个阶段,节省运行时内存)
*           CLASS : 编译后
*           RUNTIME : 运行时
*   比如: IO流(过河拆桥,用完后即时释放), 泛型(泛型只在编译阶段,运行时也被擦除)
*/
@MyAnno
public class Demo02 {
    @MyAnno
    public static void main(@MyAnno String[] args) {
        @MyAnno
        int i;
    }
}
@interface MyAnno{
    String name() default "yy";
    int[] array();
}

Player表示玩家,Data表示注册账号日期。
在这里插入图片描述

package com.itheima04.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*
 *    元注解: 用来描述注解的注解 (初始的注解)。 元数据 : 诠释数据的数据(如右击一个图片属性查看)  
 *      name = 张裕
 *      属性(元数据) = 值(数据)
 * 
 *    两个元注解:
 *      1. @Target :  适用范围
 *          1. ElementType.Type : 四大java类型上可用
 *          2. ElementType.Method : 方法上可用
 *          3. ElementType.Constructor : 构造上可用
 *          4. ElementType.Field:属性上可用
 * 
 *      2. @Retention : 保留(生命周期)。源码,编译,运行
 *          1. SOURCE :编译后target文件夹下YourAnno.class中没有@YourAnno,override注解就是保留在SOURCE源码
 *          2. CLASS :编译后target文件夹下YourAnno.class中有@YourAnno,和上面源码一样
 *          3. RUNTIME : 常用,加载到jvm里注解还在,可用反射clazz.getDeclaredAnnocation和   
 *                                 clazz.isAnnotationPresent(YourAnno.class)查看有没有。
 * 
 *   注解的使用:  1. 可以放在代码上
 *     	   2. 使用注解的时候,如果有属性没赋值,必须赋值
 *      3. 特殊: 如果注解中的属性有且仅有一个未赋值,并且属性名为value,在赋值的时候,value= 可省略
 */

//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 
//@YourAnno(name="yy")   //注解的使用
//@YourAnno(value="yy")  //把String value();放开未赋值,对应上面注释中注解使用中的2
//@YourAnno("yy")  //把String value();放开未赋值,对应上面注释中注解使用中的3
public class Demo03 {
    @YourAnno
    public static void main(String[] args) {
    }
}

//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
//@Target(value={ElementType.METHOD,ElementType.TYPE}) //枚举名.属性 //如下图的提示
//@Target({ElementType.METHOD,ElementType.TYPE}) //缩略写法,override只适用于方法,类型不能用,如下改进
@Target(ElementType.METHOD)//在注解中,如果数组只有一个元素,{}是可以省略的

//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
@Retention(RetentionPolicy.SOURCE) 
@interface YourAnno{
    String name() default "xx";
//    String value(); //属性,类似方法
}

在这里插入图片描述
如下函数里调用了MyAnno注解,形成联系。
在这里插入图片描述
推测springboot项目如何利用注解运行起来的呢比如spring的bean怎么初始化的呢为什么能在一个类上写一个@component,@Service, @controller这些注解就能将这个类作为单例spring bean注入到spring工厂中去呢
在这里插入图片描述
如下.run(传入本类)。
在这里插入图片描述
@SpringBootApplication默认扫描本包下所有类如下。
在这里插入图片描述
如下也new Svc1放入spring工厂,这就是注解和反射配合使用的方式。注解本身没有太大作用,注解源码就几行。发挥作用的是扫描注解的反射部分,针对注解的类型和注解里的字段进行相应的配置。
在这里插入图片描述

9.自定义junit测试框架并打jar包:method.isAnnotationPresent

package com.itheima05.custom;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/*
*   思路: @MyTest替换@Test
*       1. 自定义MyTest注解
*           1. 属性 : 无 (有属性必须要赋值,下面没用到MyTest里没有赋值)
*           2. 元注解:  1. 指定作用域: Method
*                      2. 生命周期: runtime(注解保留到运行时期才能碰到反射)
* 
*       2. 反射 : 在运行时操作类
*           需求: 在方法上 加了 MyTest注解的方法能够像main方法一样运行,所以用到反射
*           1. 获取TestDemo 的Class对象
*           2. 获取类中的所有public方法
*           3. 判断每个方法上是否有注解MyTest,如果有,就运行
*/
public class CustomDemo {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
        Class<?> clazz = Class.forName("com.itheima05.custom.TestDemo");
        Object obj = clazz.newInstance();                
        Method[] methods = clazz.getMethods(); //注意是getMethods最后有一个s,返回数组
        for (Method method : methods) {                
            boolean result = method.isAnnotationPresent(MyTest.class); // 方法上是否有指定注解,如果有返回true,这行和MyTest形成联系
            if(result){
			   // method.invoke(obj); //普通方法调用,第一个参数是调用此方法的对象 //这行不用线程
			   // 如下有异常时程序要不影响其他方法输出,所以每个方法用一个线程
                new Thread(() -> {
                    try {
                        method.invoke(obj);
                    }catch (Exception e) {
                        e.printStackTrace();
                    }
                }).start();
            }
        }
    }
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyTest{
}
package com.itheima05.custom;

public class TestDemo {
    public void method00(){
        System.out.println("方法0");
    }
    @MyTest
    public void method01(){
        System.out.println("测试方法一");
    }
    @MyTest
    public void method02(){
        int i = 1/0; //异常不能影响其他,所以多线程
        System.out.println("测试方法二");
    }
    @MyTest
    public void method03(){
        System.out.println("测试方法三");
    }
}

在这里插入图片描述
CustomDemo和MyTest两个类即CustomDemo.java文件可以打成jar包:New Project-Maven。
在这里插入图片描述
在这里插入图片描述
如下选中java文件夹右击New-Package,命名为com.nb.xunit包,将CustomDemo和MyTest两个文件复制到该包下。MyTest注解应该给public,不然只能在本包下使用。
在这里插入图片描述
在这里插入图片描述
如下操作后可以删除CustomDemo.java和MyTest.java。
在这里插入图片描述

10.类加载器:.class.getClassLoader(),引扩应,双亲委派

package com.itheima01.loader;
import org.junit.Test;
import sun.net.spi.nameservice.dns.DNSNameService;
/*
*       0. 类  .java文件   -> .class文件  -> Class对象(内存中)
*                源码           编译后         运行时
*               当.class文件被加载进内存,JVM会在堆中创建一个Class对象
* 
*       1. 类加载器 : 将.class文件加载进内存, 随之产生Class对象,看成输入流(读档)
* 
*       2. 三种类加载器:加载不同包下的类,像前面讲的不同的输入流
*           1. 引导类加载器 bootstrap (用c写的)。核心包下的类(rt.jar)
*           2. 扩展类加载器 extension。ext包下
*           3. 应用/系统 类加载器 application。 第三方编写的类
* 
*       Class对象.getClassLoader(); // 可以获取加载这个Class对象的类加载器
*
*   补充:  1. 三种类加载器存在继承关系的【不叫继承 (叫组合 composition,因为引导类加载器用C写的)】
*          2. 为什么一般情况下, 一个.class文件的Class对象只会有一个?
*             类加载器: 双亲委派机制(这机制保证一个类只会被一个类加载器加载,双亲:父类的父类)
* 
*            什么情况下, 一个.class文件的Class对象会有多个?
*            多个类加载器(程序员自定义类加载器,手动操作类加载器去加载)去加载同一个.class文件
*/
public class ClassLoaderDemo {
    public static void main(String[] args) {
        ClassLoader loader1 = ClassLoaderDemo.class.getClassLoader();        
        System.out.println(loader1); //sun.misc.Launcher$AppClassLoader@18b4aac2 //应用类加载器
        
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
        Class clazz = DNSNameService.class; // Ext扩展包下       
        ClassLoader loader2 = clazz.getClassLoader();
        System.out.println(loader2);  //sun.misc.Launcher$ExtClassLoader@45ee12a7 //扩展类加载器
        
//111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
        ClassLoader loader3 = String.class.getClassLoader();
        System.out.println(loader3); // null ,因为不是用java写的。 //引导类加载器
    }    
    @Test
    public void method01(){  //三种类加载器的关系:继承关系,组合关系,因为跨语言了
        ClassLoader loader1 = ClassLoaderDemo.class.getClassLoader();
        System.out.println(loader1);//sun.misc.Launcher$AppClassLoader@18b4aac2 //应用 
              
        ClassLoader loader2 = loader1.getParent(); //获取父加载器
        System.out.println(loader2);//sun.misc.Launcher$ExtClassLoader@452b3a41 //扩展
              
        ClassLoader loader3 = loader2.getParent();  
        System.out.println(loader3);//null //引导 (父父)
    }
}

11.代理租房:静态代理,中介可代理实现FD接口的多个房东。装饰者设计模式(IO流,可层层增加功能),迭代器设计模式(集合)

代理更多的是强调对对象的访问控制,比如访问A对象的查询功能时,访问B对象的更新功能时,访问C对象的删除功能时,都需要判断对象是否登陆,那么我需要将判断用户是否登陆的功能抽提出来,并对A对象、B对象和C对象进行代理,使访问它们时都需要去判断用户是否登陆。

就是将某个控制访问权限应用到多个对象上,使用的是代理对象(中介)在自己的构造方法里面new的一个被代理的对象,不是调用者传入的。

而装饰器更多的强调给对象加强功能,比如说要给只会唱歌的A对象添加跳舞功能,添加说唱功能等,简单地说就是将多个功能附加在一个对象上。装饰是使用的调用者从外部传入的被装饰对象,调用者只想要你把他给你的对象装饰(加强)一下。

package com.itheima02.proxy;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
/*
*   代理: 一个代理对象(中介) 拥有 被代理对象(房东) 的大部分权限, 控制外界(租客)对被代理对象的访问
*        1. 被代理类对象(房东)
*        2. 代理类对象(中介) 。代理对象 控制 外界对被代理类对象的 '直接'访问,找中介间接访问房东
* 
*   模型:  1. 中介 拥有 房东 的 大部分权限:代理类对象 拥有 被代理类对象的大部分方法
*            	解决: 不用继承,用接口
*         2. 中介要控制外界对房东的访问:中介要拥有房东的联系方式, 要有随时找得到房东
*               解决: 将房东作为中介的属性存在
* 
*   问题1:  我们没有限制对房东的直接访问, 用代理模式意义何在? 
* 			意义在于, 我们可以在不修改房东类的情况下, 限制访问条件
*           1. 假设 有个类 属于 某个jar包, 这个类改不了的
*           2. 我们又想改这个类的某些方法的访问条件 -> 代理模式
* 
*   问题2:  继承同样也是实现上述需求, 为什么要用代理模式? 代理模式的扩展性更强
*           继承: 只扩展了对FangDong类的访问限制
*           代理: 扩展了对Fd接口 所有实现类对象的 访问限制(如还代理其他房东)
* 
*       BufferedReader : 代理模式(又称装饰设计模式)
*     new BufferedReader(new FileReader(a.txt)); //里面new FileReader(a.txt)是房东
*    BufferedReader是中介,也可以代理除了 FileReader还有Reader其他实现类对象 (多态)
*/
public class ProxyDemo {
    public static void main(String[] args) throws FileNotFoundException {        
        FangDong fangDong = new FangDong(); //使用的时候: 先创建被代理类对象        
        ZhongJie zh = new ZhongJie(fangDong); //接着: 创建代理类对象        
        zh.zufang(999); //钱不够, 房子不租给你 //访问中介,实际上通过中介访问房东
    }
}
package com.itheima02.proxy;

class FdSon extends FangDong{ //继承:也没改FangDong类,但只扩展了对FangDong类的访问限制,只能单继承
    @Override
    public void zufang(int money) {
        if(money > 1000){
            super.zufang(money);
        }
    }
}

//11111111111111111111111111111111111111111111111111111111111111111111111111111
public class FangDong implements Fd{
    public void zufang(int money){
        System.out.println("房东出租房子: " + money);
    }
    public void maifang(int money){
        System.out.println("房东卖掉房子:" + money);
    }
    public void buyfang(int money){ //这权限不给中介
        System.out.println("房东买入房子作为投资 :" + money);
    }
}
interface Fd{ //房东类中的方法抽取
    void zufang(int money);
    void maifang(int money);
}
class ZhongJie implements Fd{ //fd接口也可以其他类实现,所以ZhongJie类构造里传入其他类相当于多继承
    Fd fd; //房东对象   定义并没有实例化像string num
    public ZhongJie(Fd fd){ //fd实例化,当我们创建中介对象的时候,必须要指定他所代理的房东对象
        this.fd = fd;
    }
    @Override
    public void zufang(int money) {
        if(money > 1000){ //租客找中介租房, 当钱>1000, 中介就会帮租客联系房东签合同
            fd.zufang(money);
        }else{
            System.out.println(" 钱不够, 房子不租给你");
        }
    }
    @Override
    public void maifang(int money) {
    }
}

//111111111111111111111111111111111111111111111111111111111111111111111111111111111
//下面为了解释proxy.zufang(1001)调用h.invoke 。下行Fd接口已通过Proxy.newProxyInstance方法中第二个参数传入了。
class MyProxy implements Fd{ //JVM底层动态创建中介类,Proxy.newProxyInstance方法结果
    InvocationHandler h;
    public MyProxy(InvocationHandler h){ //h是实现类对象,不是接口
        this.h = h;
    }
    @Override
    public void zufang(int money) { //动态变化内容, JVM不能写, 交给程序员如下        
//    Method method = this.getClass().getMethod("zufang",int.class); //方法的反射,区分zufang还是maifang
//    Object[] param = {money}; 

//      h.invoke(this,method,param);
    }
    @Override
    public void maifang(int money) {
//        h.invoke();
    }
}

12.动态代理:newProxyInstance(三参).zufang()调用h中invoke(三参)

package com.itheima03.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/*
*  动态代理(比上面多一个反射而已): 动态: 代理中介类不是静态定义,是动态生成的
*     1. 静态定义: 把这个类写死了即上面class ZhongJie
*     2. 动态生成: 这个类(中介类)压根就没写,在运行时由JVM动态生成(肯定涉及反射)。前提: 必须有接口 
*/
public class DynamicDemo { //Dynamic反义词static
    public static void main(String[] args) {
        FangDong fd = new FangDong(); //先看上房东房子        
        /*
         static Object newProxyInstance(ClassLoader loader, //这个方法最终目的: 创建一个中介类对象!
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
              1. loader : 类加载器(加载中介类的),这个中介类不存在,需要JVM动态生成
                    一般是应用类加载器(第三方类)。 直接 被代理类(房东类) 的类加载器
                    
              2. interfaces : 接口的Class对象(让中介类实现的)
                    就是 被代理类(房东类) 所实现的接口

          class对象在生成期间需要一个类加载器(将字节码文件加载生成class对象),上面1。
          这个中介类需要实现接口从而拥有被代理类方法,上面2。
          接下来需要重写接口里方法,下面3。
  
              3.InvocationHandler  h  : 调用处理器 (InvocationHandler是一个接口)
                   invoke方法可以看成中介类对接口所有方法的重写
        */
        ClassLoader loader = fd.getClass().getClassLoader();  //对应上面1      
        Class<?>[] interfaces = fd.getClass().getInterfaces();//获取此类实现的所有接口,对应上面2        
       // Class<?>[] interfaces = {Fd.class};  //效果同上行
       // Class<?>[] interfaces = {com.itheima03.dynamic.Fd.class}; //效果同上行 
       
// 实现接口后要重写方法,重写不了,因为中介类运行时才会存在,所以需要上面的3。
// System.out.println(loader); //sun..$APPClassLoader.. //应用类加载器
// System.out.println(Arrays.toString(interfaces)); //[interface.com.itheima03.dynamic.Fd] //就是FD接口

        InvocationHandler h = new InvocationHandler() {  //h是InvocationHandler接口实现类对象,只有这样的h才能传入Proxy.newProxyInstance这个方法
            /*
            *   invoke函数参数: 1. proxy : 当前代理类对象(几乎没用,因为下面有Fd proxy)
            *                2. method: 代理类对象当前调用的方法
            *               3. args: 代理类对象当前调用方法传入的参数列表
            */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(method.getName());
                System.out.println(Arrays.toString(args));
                return null;
            }
        };
        
//11111111111111111111111111111111111111111111如上是Proxy.newProxyInstance方法的三个参数获取
         Fd proxy = (Fd) Proxy.newProxyInstance(loader, interfaces, h); //这行=号后面相当于new MyProxy 
//如上行平时MyProxy proxy = new MyProxy(.) ,因为MyProxy是JVM随机命名,所以上行用Fd(这个Fd是FangDong类下面定义的)
		proxy.zufang(1001); //调用h.invoke即本文件上面 ,父类引用h,子类new InvocationHandler()
        //proxy.maifang(2001); //接口回调(callback)(接口形式的多态):相当于直接调用本文件上面invoke方法
    }
}

在这里插入图片描述

package com.itheima03.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/*
*   动态代理 比之 普通代理:  好处在于 不用事先定义类, 代理类在运行时动态生成 (反射)
*       运用场景: 被代理对象(房东) 有 10000万个方法, 你只想修改其中一个
*/
public class Demo02 {
    public static void main(String[] args) {
        FangDong fd = new FangDong(); //先看上房东房子
        ClassLoader loader = fd.getClass().getClassLoader();
        Class<?>[] interfaces = fd.getClass().getInterfaces();
        InvocationHandler h = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String name = method.getName();                
                if("zufang".equals(name)){
                    int money = (int) args[0]; // Object[] param = {money};
                    if(money > 1000){
                        fd.zufang(money);
                    }else{
                        System.out.println("钱不够...");
                    }                    
                }/*else if("maifang".equals(name)){
                }*/else{ 
                    method.invoke(fd,args); //中介类.其他9000个方法,走这行,即其他9000个方法依然交给房东fd自己处理
                }
                return null;
            }
        };

//1111111111111111111111111111111111111111111111111111111111111111111111111111111
        Fd proxy = (Fd) Proxy.newProxyInstance(loader,interfaces,h);
        proxy.zufang(888);
//        proxy.maifang(3000);
    }
}

13.模板:类里有抽象方法必须抽象类

package com.atguigu.test02.abstract_;
// 编写一个类,包含一个方法,可以统计 你执行任意代码的运行时间。
public class TestTemplate {
	public static void main(String[] args) {
		MyCalTime my = new MyCalTime();
		long time = my.getTime();				
		System.out.println("耗时:" + time + "毫秒");
	}}

//111111111111111111111111111111111111111111111111111111111111111111111
abstract class CalTime{ 	
	public final long getTime(){//可以计算任意一段代码的运行时间 //这里加final的目的是不希望子类重写,改写我的算法的结构
		long start = System.currentTimeMillis(); //(1)获取开始时系统时间	 			
		doWork();                                //(2)执行xxxx			
		long end = System.currentTimeMillis();	 //(3)获取结束时系统时间							
		return end - start;          //(4)计算时间差
	}	
	protected abstract void doWork(); //protected的目的,希望只是子类中进行访问和重写
 }

//111111111111111111111111111111111111111111111111111111111111111111
class MyCalTime extends CalTime{
	@Override
	protected void doWork() { //重写抽象方法
		long sum = 0;
		for(int i=1; i<=100000; i++){
			sum += i;
		}
		System.out.println("sum = " + sum);
	}	
}

14.单例:某个类只能有唯一的一个实例对象,多线程

package com.atguigu.test17;import org.junit.Test;

public class Test17 {
	@Test
	public void test1(){
		SingleEnum s1 = SingleEnum.INSTANCE;
		SingleEnum s2 = SingleEnum.INSTANCE;
		System.out.println(s1 == s2); //true 
	}	
	
	@Test
	public void test2(){
    // SingleEnum.test();
    //此时我并没有需要用到这个INSTANCE对象,但是它也创建出来SingleEnum对象,单例恶汉式
	}	
	
	@Test
	public void test3(){
		SingleClass s1 = SingleClass.INSTANCE;
		SingleClass s2 = SingleClass.INSTANCE;
		System.out.println(s1==s2); //true,地址一样,只有一个对象。
	}
		
	@Test
	public void test4(){
		Single s1 = Single.getInstance();
		Single s2 = Single.getInstance();
		System.out.println(s1 == s2); //true
	}	

	@Test
	public void test5(){
		LazyClass s1 = LazyClass.getInstance(); //getInstance()里必有new
		LazyClass s2 = LazyClass.getInstance();
		System.out.println(s2 == s1);
	}

	LazyClass s1;
	LazyClass s2;	
	@Test
	public void test6(){
		//匿名的内部类,继承Thread类。=后面是子类,然后多态
		Thread t1 = new Thread(){
			public void run(){
				s1 = LazyClass.getInstance();
			}
		};		
		Thread t2 = new Thread(){
			public void run(){
				s2 = LazyClass.getInstance();
			}
		};		
		t1.start();
		t2.start();		
		try {
		//这里用join的目的是,为了两个子线程都执行完,再执行主线程的System.out.println(s1);
			t1.join();
			t2.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}		
		System.out.println(s1);
		System.out.println(s2);
		System.out.println(s1 == s2);
	}	}
	
//11111111111111111111饿汉式: 不管我们使用者是否需要这个对象,它都上来先给你创建好这个唯一的对象。
//形式一:对应test1()
enum SingleEnum{
	INSTANCE; //单例,枚举只有一个   
    //public static void test(){ //用这个方法不用上面这个INSTANCE对象,对应test2() }
    }
	
//形式二:对应test3() 
class SingleClass{ //1.5之前老版的枚举 //用一个全局的静态的常量,来保存这个唯一的实例对象
	public static final SingleClass INSTANCE = new SingleClass();
	private SingleClass(){		
	}}
 
//形式三:对应test4()
class Single{  
    //用一个私有的静态的常量,来保存这个唯一的实例对象
	private static final Single INSTANCE = new Single(); 
	private Single(){		
	}
	public static Single getInstance(){ //提供一个静态方法,来返回这个常量对象
		return INSTANCE;
	}}
	
//111111111111111111111懒汉式: 延迟创建对象。当使用者来或者这个对象,要用到对象时,我再创建。
//形式一: 对应test6()
class LazyClass{ 
	private static LazyClass instance; //不加final,可以不用new出来  //加final必须new SingleClass()
	private LazyClass(){		
	}	
	public static LazyClass getInstance(){ 
		if(instance == null){//提高效率,已创建对象就没必要再进去,直接最后return instance; 未创建对象进入
			synchronized(LazyClass.class){ //当前类的Class对象,静态方法不能出现this
				if(instance == null){//安全判断
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					instance = new LazyClass();
				}
			}
		}
		return instance;
	}	

//形式二:内部类
class Lazy{
	private Lazy(){}	
	private static class Inner{
		public static final Lazy INSTANCE = new Lazy();//在内部类中,创建外部类的唯一对象
	}	
	public static Lazy getInstance(){ 
		return Inner.INSTANCE; //用到getInstance方法才会new Lazy(),也是懒汉式
	}
 }

15.工厂:工厂类(一个工厂),工厂接口(一车一工厂),clazz.newInstance()

package com.atguigu.test12;import org.junit.Test;
//1、简单工厂模式:优点:代码比较简洁。缺点:如果增加新的产品类型(奥迪),需要修改工厂类中getCar()。
//违反了面向对象的一个开发原则:对扩展开放,对修改关闭。public class TestFactory {
	@Test
	public void test03(){
		Car c = SimpleFactory2.getCar("奔驰");
		c.run();
	}		
		
	@Test
	public void test02(){
		Car c = SimpleFactory.getCar(); //静态,类名. 不用创建工厂类对象
		c.run();//这里也是运行宝马的run(),但是从头至尾没有出现BMW类,解耦合
	}		
		
	@Test
	public void test01(){  //没有工厂,如下创建对象和使用对象是同一个人完成		
		BMW b = new BMW();	//比如spring容器,new时要初始化很多配置信息才创建一个对象		
		b.run(); //使用对象,调用方法
	}}
	
//11111111111111111111111111111111111111111111111111111111如下没有工厂,中间不变
interface Car{  //对应test01()
	void run();
	}	
	
class BMW implements Car{
	@Override
	public void run() {
		System.out.println("宝马让你在车里面哭");
	}	
}	

class Benz implements Car{
	@Override
	public void run() {
		System.out.println("奔驰让你在车盖上哭");
	}
}

class Audi implements Car{ //多了一类车,下面工厂类需要再改变,这就是简单工厂模式
	@Override
	public void run() {
		System.out.println("奥迪让你在...");
	}
}

//1111111111111111111111111111111111111111111111111111111111如下工厂类  
class SimpleFactory{ //对应test02()
	public static Car getCar(){
		return new BMW();
	}
}

class SimpleFactory2{ //对应test03()
	public static Car getCar(String type){
		if("宝马".equals(type)){
			return new BMW();
		}else if("奔驰".equals(type)){
			return new Benz();
		}
		return null;
	}
}
package com.atguigu.test12;
/*
 * 2、工厂方法模式:(1)为了生产对象与使用对象分开 (2)如果增加新产品,就不需要修改原来的工厂类
 * 优点:遵循了增加新产品,不修改原来的类的原则。 缺点:类太多了,一种车一个工厂
 */
public class TestFactory2 {
	public static void main(String[] args) {
		BaoMaFactory bf = new BaoMaFactory();
		Che c = bf.getChe();
		c.run();
	}
}

//11111111111111111111111111111111111111111111111111111111如下没有工厂,中间不变
interface Che{  
	void run();
	}

class BaoMa implements Che{
	@Override
	public void run() {
		System.out.println("宝马");
	}
}

class BenChi implements Che{
	@Override
	public void run() {
		System.out.println("奔驰");
	}
}

class AoDi implements Che{
	@Override
	public void run() {
		System.out.println("奥迪");
	}
}

//1111111111111111111111111111111111111111111111111111111111111如下工厂接口
interface GongChang{
	Che getChe();
	}
	
class BaoMaFactory implements GongChang{ 
	@Override
	public Che getChe() {
		return new BaoMa();
	}	
}

class BenChiFactory implements  GongChang{ 
	@Override //只需要增加一个工厂类,专门生产奥迪车,不动上面代码
	public Che getChe() {
		return new BenChi();
	}	
}  

如下用反射结合上面的简单工厂和工厂方法:

package com.atguigu.test12;

public class TestFactory3 {
	public static void main(String[] args)throws Exception {
		Vehicle c = SimpleFactory3.getVehicle("com.atguigu.test12.QQ");
		c.run();   //qq车	
		Vehicle c2 = SimpleFactory3.getVehicle("com.atguigu.test12.Aoto");
		c2.run();  //奥拓
	}
}

//11111111111111111111111111111111111111111111111111111111111111111中间没变
interface Vehicle{    
	void run();
}

class QQ implements Vehicle{
	@Override
	public void run() {
		System.out.println("qq车");
	}	
}

class Aoto implements  Vehicle{
	@Override
	public void run() {
		System.out.println("奥拓");
	}	
}

//1111111111111111111111111111111111111111111111111111111111111111如下工厂类
class SimpleFactory3{
	public static Vehicle getVehicle(String className)throws Exception{
		Class clazz = Class.forName(className);
		return (Vehicle) clazz.newInstance(); //clazz.newInstance()返回Object强转为Vehicle类
	}
}

16.策略+工厂:去除if-else

16.1 未引入设计模式

1.超级会员折扣8折、普通会员折扣9折、普通用户没有折扣;
2.专属会员要在店铺下单金额大于30元时享受7折优惠;
3.用户超级会员到期,并到期时间在一周内,对用户的单笔订单按照超级会员进行折扣,且只折扣一次。

public BigDecimal calPrice(BigDecimal orderPrice, String buyerType){
    if(用户是专属会员){
        if(订单金额大于30){
            return 7折价格;
        }
    }
    
    if(用户是超级会员){
        return 8折价格;
    }
    
    if(用户是普通会员){
        if(该用户超级会员刚过期且尚未使用过临时折扣){
            临时折扣使用次数更新();
           return 8折价格;
        }
        return 9折价格;
    }
    return 原价;
 }

16.2 引入策略模式

策略/接口UserPayService

public interface UserPayService{
    public BigDicimal quote(BigDecimal orderPrice); //计算应付价格
  }

策略类ParticularlyVipPayService,SuperVipPayService ,VipPayService

public class ParticularlyVipPayService implements UserPayService{ //专属会员
    @Override
    public BigDicimal quote(BigDecimal orderPrice){
        if(消费金额大于30){
            return 7折价格;
        }
    } 
 }
 
public class SuperVipPayService implements UserPayService{ //超级会员
    @Override
    public BigDicimal quote(BigDecimal orderPrice){
        return 8折价格;
    } 
}

public class VipPayService implements UserPayService{ //普通会员
    @Override
    public BigDicimal quote(BigDecimal orderPrice){
        if(该用户超级会员刚过期且尚未使用过临时折扣){
            临时折扣使用次数更新();
           return 8折价格;
        }
        return 9折价格;
    } 
}

价格计算方法calPrice,如下可看出引入策略模式代码维护性和可读性有所增强,但是并未消除if-else。

public BigDecimal calPrice(BigDecimal orderPrice, User user){
    String vipType = user.getVipType();    
    
    if(vipType == 专属会员){        
        UserPayService strategy = Spring.getBean(ParticularlyVipPayService.class);
        return strategy.quote();  //伪代码
    }
    
    if (vipType == 超级会员) {
        UserPayService strategy = Spring.getBean(SuperVipPayService.class);
        return strategy.quote(orderPrice);
     }

     if (vipType == 普通会员) {
        UserPayService strategy = Spring.getBean(VipPayService.class);
        return strategy.quote(orderPrice);
     }
     
     return 原价;
  }

16.3 引入工厂模式

工厂类UserPayServiceStrategyFactory

public class UserPayServiceStrategyFactory{
    private static Map<String, UserPayService> services = new ConcurrentHashMap<String, UserPayService>();

    public static UserPayService getByUserType(String type){
        return services.get(type);
    }

    public static void register(String userType, UserPayService userPayService){
        Assert.notNull(userType, "userType can't be null");
        services.put(userType, userPayService);
    }
 }

计算价格方法calPrice,如下可看出引入工厂模式后,完全去除了代码中的if-else。

public BigDecimal calPrice(BigDecimal orderPrice, User user){
    String vipType = user.getVipType();
    UserPayService strategy = UserPayServiceStrategyFactory.getByUser(vipType);
    return strategy.quote(orderPrice);
 }

在Spring初始化时,需要将策略模式类都注册到工厂类中,将策略类进行改造,使用Spring的InitializingBean接口,在bean初始化后,执行afterPropertiesSet方法进行注册。

//专属会员
public class ParticularlyVipPayService implements UserPayService, InitializingBean{
    @Override
    public BigDicimal quote(BigDecimal orderPrice){
        if(消费金额大于30){
            return 7折价格;
        }
    }    
     
    @Override
    public void afterPropertiesSet(){
        UserPayServiceStrategyFactory.register("ParticularlyVip", this);
    }
}
 
//超级会员
public class SuperVipPayService implements UserPayService, InitializingBean{
    @Override
    public BigDicimal quote(BigDecimal orderPrice){
        return 8折价格;
    } 
    
    @Override
    public void afterPropertiesSet(){
        UserPayServiceStrategyFactory.register("SuperVip", this);
    }
}

//普通会员
public class VipPayService implements UserPayService, InitializingBean{
    @Override
    public BigDicimal quote(BigDecimal orderPrice){
        if(该用户超级会员刚过期且尚未使用过临时折扣){
            临时折扣使用次数更新();
           return 8折价格;
        }
        return 9折价格;
    } 

    @Override
    public void afterPropertiesSet(){
        UserPayServiceStrategyFactory.register("Vip", this);
    }
}

17.DTD约束:配置文件

用word/txt(和xml一样都没有格式约束)传输数据,但xml格式规范
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8" ?>
<!--  如上行文档声明 : 版本 和 编码 -->
<!--
    xml基本语法:
        1. 标签分类 :
              1. 成对标签 :  start tag  -  内容 - end tag
                               开标签             闭标签
                              <标签名>           </标签名>
              2. 单独标签 : 自关闭标签 <标签名/>           
        2. xml有且仅有一个根标签 root tag (第一个标签,xml中的所有内容都在内)
        3. 标签可以拥有属性的(开标签和自关闭标签)  key=value 即 id="003"
 
      了解:
        实体字符(转义字符):
                <  ->   &lt;    less than即缩写lt
                >   ->  &gt;    greater than
                &lt; &gt; 小于或大于就是不等于,前端支持,java不支持
                
        CDATA: unparsed character data 不解析的字符数据
        在这里的内容是不会被解析器所解析(看成注释)
-->
<list>
    <![CDATA[" if(x < > 1){ sout: xx} "]]>
    <hehe id="003"/>
        <book id="001">
            <name>java基础入门</name>
            <desc>人生必读第一本书</desc>
            <price>998</price>
            <extra>if(x &lt; &gt;  1){ sout: xx}</extra>
        </book>
        <book id="002">
            <name>java从入门到放弃</name>
            <desc>人生的最后一本</desc>
            <price>11</price>
        </book>
</list>
<?xml version="1.0" encoding="UTF-8" ?>
<!--
    可扩展: 开发者随意命名标签
          存数据  为了 以后 取数据(解析 parse)
          问题:会给数据解析带来麻烦
          解决: 约束(规则,规范)给xml指定约束, 其内部拥有什么内容,以及内容的编写格式都有要求
          规则: list里面必须是book标签(多个)
                book标签里还可以有name desc price等标签
                这些标签,里面有文本内容
                
          两种约束: DTD:  document type definition 文档类型定义 (了解)
                   Schema : 模式 (了解) 替代DTD,全称(XML Schema Definition,XSD)
-->
<?xml version="1.0" encoding="UTF-8" ?>
<!--
    DTD约束: 1. 根标签 必须是list
           2. list里有book (+ : 表示1个或多个book标签)
          3. book里有name desc price
          4. name desc price = PCDATA(parsed character data) 可以包含文本
          5. book 有一个属性 id 默认值 为 999
          如下写完约束后,会自动提示
-->
<!DOCTYPE list [
        <!ELEMENT list (book+)>
        <!ELEMENT book (name,desc,price)>
        <!ELEMENT name     (#PCDATA)>
        <!ELEMENT desc     (#PCDATA)>
        <!ELEMENT price     (#PCDATA)>
        <!ATTLIST book id CDATA "999">
        ]>

<!--111111111111111111111111111111111111111111111111111111111111111-->        
<list>
    <book id="001">
        <name>java基础入门</name>
        <desc>人生必读第一本书</desc>
        <price>998</price>
    </book>
    <book id="002">
        <name>java基础入门</name>
        <desc>人生必读第一本书</desc>
        <price>998</price>
    </book>
</list>

18.schema约束:命名空间

//05_book.xsd
<?xml version='1.0' encoding='UTF-8' ?>
<xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema'
        targetNamespace='http://www.itheima.com'
        elementFormDefault="unqualified">
    <!--
    	上面几行就是命名空间技术,需要被另一个文件引用即06_book.xml引用才可以生效。
    	schema约束: 约束规则用xml语法来书写的。
        element : 元素(开标签到闭标签之间的内容,我们统称为标签)
        complexType : 复杂类型(但凡元素有子标签或属性)
        sequence : 序列 (子元素的序列)
        maxOccurs: 最大出现次数,如下书标签最少出现一次,至多随意
    -->

<!--11111111111111111111111111111111111111111111111111111111111111111111111111111-->  
    <xs:element name='书架' >
        <xs:complexType>
            <xs:sequence maxOccurs='unbounded' minOccurs="1">
                <xs:element name='' >
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name='书名' type='xs:string' />
                            <xs:element name='作者' type='xs:string' />
                            <xs:element name='售价' type='xs:string' />
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>
//06_book.xml调用05_book.xsd
<?xml version="1.0" encoding="UTF-8"?>
<itheima:书架 xmlns:itheima="http://www.itheima.com"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://www.itheima.com 05_book.xsd">

<!--11111111111111111111111111111111111111111111111111111111111111111111111-->  
    <>
        <书名>呵呵</书名>
        <作者>啦啦</作者>
        <售价>嘻嘻</售价>
    </>
</itheima:书架>

如下xml用到在线的xsd,自动补全。
在这里插入图片描述

19.xml解析:dom4j

dom4J不是sun公司开发的,所以放入lib目录,add as Library相当于解压,sun公司开发的放External libraries。SAX解析读完一行解析完就可以当时释放解析内容,不耗内存但慢。SAX的变形pull。
在这里插入图片描述
在这里插入图片描述

package com.itheima04.parse;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.util.List;

public class ParseDemo {
    public static void main(String[] args) throws DocumentException {
        SAXReader reader = new SAXReader();
        //将下面这个文件读到内存中       
        Document document = reader.read("02_book.xml"); //document文档对象 表示  02_book.xml文件的        
        Element rootElement = document.getRootElement(); //解析过程: 爬树        
        List<Element> books = rootElement.elements("book"); //获取根元素 store的所有book子元素
 
        for (Element book : books) {                
            String name = book.elementText("name"); //获取子标签内部的文本数据
            String desc = book.elementText("desc");
            String price = book.elementText("price");                            
            String id = book.attributeValue("id"); //获取book标签的属性id的值
            System.out.println(id + "," + name + "," + desc + "," + price);
        }
    }
}

在这里插入图片描述

20.javase总结:IO流,集合

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
1. ArrayList底层是数组,默认长度为10,扩容系数1.5即下次15,ArrayList查询复杂度为o(1)。LinkedList底层双向链表,好处不用进行扩容,一直往后追加,坏处是要找第n个元素时间复杂度是o(n)。

2. HashMap底层也是数组,数组大小必须是2的幂次方倍【确保数组中的每一个位置发生hash冲突的概率相同】。LinkedHashMap底层和HashMap一样,只不过每个元素有一个指针指向下一个插入的元素,有序了。TreeMap底层红黑树实现,特点是按照key进行排序。HashTable线程安全(读写整个数组结构加了syn修饰即加了锁)(HashMap线程不安全,两者底层存储结构一样)。锁住整个结构代价大,所以出现了concurrent HashMap,只锁住数组的一个桶。HashSet将HashMap的value设为null。
在这里插入图片描述
源码穿透:JDK -> JVM -> glibc(系统调用的封装)->Linux内核。进程是资源分配的最小单位。
在这里插入图片描述

21.求一个长度为10的整型数组的最大元素,采用随机的方式赋值并输出各元素的值?

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

举报

相关推荐

0 条评论