精华内容
下载资源
问答
  • Java反射和设计模式

    2019-12-21 09:04:44
    Java反射

    本文是课上资料的总结非原创没有转载地址

    Java反射

    反射

    • 反射就是把Java类中的各种成分映射成一个个单独Java对象进行操作。
    • 本质:就是类的解剖技术。
    • 类中成分主要包括:成员变量,构造方法,包等。

    为什么需要反射

    • 需求:我公司定义了一组接口,然后第三方公司按照我公司的接口实现了一套功能,然后交给我们,但是我们公司的项目已经结束,如何实现动态加载第三方公司提供的功能?
    • 插件开发(反射技术)

    类对象

    • 类的对象:基于某个类new出来的对象,也称实例对象。
    • 类对象:类加载的产物,封装了一个类的所有信息(类名、父类、接口、属性、方法、构造方法)
      类对象

    反射相关的类

    • Class类——可获取类和类中的成员信息
    • Filed类——可访问类的属性
    • Method类——可调用类的方法
    • Constructor类——可调用类的构造方法

    常见操作

    获取类对象的方法(3种)

    1. 通过类的对象的getClass();方法,获取类对象
    Student s = new Student();
    Class<?> c = s.getClass();
    
    1. 通过“类名.class”获取类对象
    Class<?> c = 类名.class;
    
    1. 通过静态方法获取类对象(推荐使用,不依靠类)
    Class<?> c = Class.forName("包名.类名");
    

    获取类中的构造方法

    1. 获取类中所有公开的构造方法
    Constructor<?>[] constructors = c.getConstructors();
    
    1. 获取类中所有的构造方法(包括私有)
    Constructor<?>[] constructors = c.getDeclaredConstructors();
    
    1. 获取无参的构造方法
    Constructor<?> constructor = c.getConstructor();
    //	使用获取的构造方法(返回值是Object)
    Object s = constructor.newInstance();
    
    1. 获取带参的构造方法
    Constructor<?> constructor = class1.getConstructor(int.class, String.class, int.class, String.class);	//参数类型的类对象逗号分开
    Object s = constructor.newInstance(110, "张三", 20, "北京");	//	有参构造
    
    1. 简单方法
    Object s = c.newInstance();//  调用无参构造方法
    
    1. 获取私有的构造方法(破坏了封装性)
    /*
    直接使用是由构造方法,抛出IllegalAccessException非法访问异常
    解决方法:使访问权限失效
    */
    Constructor<?> constructor = c.getDeclaredConstructor(int.class);
    //  使访问权限失效
    constructor.setAccessible(true);
    Object s = constructor.newInstance(112);
    

    获取、调用类中的普通方法

    1. 获取类中所有公开的方法,包括继承的方法
    Method[] methods = c.getMethods();
    
    1. 获取类中所有的方法,不包括继承的方法,包括私有的方法
    Method[] methods = c.getDeclaredMethods();
    
    1. 获取指定方法名的方法
    Method method_show = c.getMethod("show");
    
    1. 调用无参方法
    method_show.invoke(s); //  等同于s.show();
    
    1. 调用带参方法
    Method method_show1 = c.getMethod("show", String.class);
    method_show1.invoke(s, "15458956245");
    
    1. 调用带返回值的
    Method method_getName = c.getMethod("getName");
    String name = (String) method_getName.invoke(s);
    
    1. 调用静态方法
    Method method_print = c.getMethod("print");
    method_print.invoke(null);  //  Student.print();
    
    1. 调用私有方法
    Method method_show2 = c.getDeclaredMethod("show");
    method_show2.setAccessible(true);
    method_show2.invoke(s);
    

    获取、设置类中的属性

    1. 获取类中所有私有属性(不包括继承)
    Field[] fields = c.getDeclaredFields();
    
    1. 根据属性名获取属性(私有、并设置值)
    Field stuNo = c.getDeclaredField("stuNo");
    stuNo.setAccessible(true);
    stuNo.set(s, 200);   //  相当于zhangsan.stuNo = 200;
    System.out.println(stuNo.get(s));    //  zhangsan.stuNo;
    

    特例(带有数组参数的方法)

    1. 错误格式
    • 抛出异常IllegalArgumentException(无效的参数)
    • 识别为可变参数
    Method method_printNames = c.getMethod("printNames", String[].class);
    method_printNames.invoke(s, new String[]{"张三", "李四"});  //  s.printNames();
    
    1. 正确格式
    • 强转为(Object)
    Method method_printNames = c.getMethod("printNames", String[].class);
    method_printNames.invoke(s, (Object) new String[]{"张三", "李四"});  //  s.printNames();
    

    反射优点和缺点

    • 优点
      1. 提高了Java程序的灵活性和扩展性,降低了耦合性,提高自适应能力
      2. 允许程序创建和控制任何类的对象,无需提前硬编码目标类
    • 缺点
      1. 性能问题
      2. 代码维护问题

    使用反射模拟实际插件开发

    • 定义一个接口CarService接口
    /**
      *	汽车接口
      */
    public interface CarService {
    	//	行驶
    	void run();
    	//	转向
    	void turn(String direction);
    }
    
    • 创建两个类实现CarService
    • Build Project项目(快捷键Ctrl+F9)
    • 找到根目录–>out–>下实现类的“.class”拷贝出来

    注意:此位置记住,后续还需把文件拷贝回来,拷贝出去是防止后续步骤误删此处文件

    • 根目录下创建CarClass.txt文件,并写入两个类的全类名(快捷键:选中文件Ctrl+Alt+Shift+C

    注意:一行一个

    • 删除实现类的“.java”文件
    • 把“.class”文件拷贝回原位置
    • 创建测试类Demo
    public class Demo {
        public static void main(String[] args) {
        	BufferedReader br = null;
            try{
    			br = new BufferedReader(new FileReader("CarClass.txt"));
    			String data = null;
    			while ((data = br.readLine()) != null) {
    				Class<?> myClass = Class.forName(data);
    				if (myClass != null) {
    					Object car = myClass.newInstance();
    					if (car instanceof CarService) {
    						CarService carService = (CarService) car;
    						carService.run();
    						carService.turn("西");
    					}
    				}
    			}
            } catch(Exception e) {
            	System.out.println("加载失败:" + e.getMessage());
            } finally {
            	br.close();
            }
        }
    }
    

    内省

    • 内省:采用反射机制实现对属性操作的一种机制
      • PropertyDescriptor 属性描述符,代表一个属性
      • BeanInfo 实体类信息,包含类的信息
      • Introspector 工具类
    • 实体类
    
    
    • 测试类
    public class Demo {
        public static void main(String[] args) throws Exception {
            //  1.使用反射创建一个Car对象
            Class<?> class1 = Class.forName("night.Car");
            Object mycar = class1.newInstance();
            System.out.println(mycar.toString());
    
            //  2.使用反射给属性赋值,不能使用字段
            //  基础方法
            /*Field field = class1.getDeclaredField("brand");
            Method setBrand = class1.getMethod("setBrand", field.getType());*/
    
            //  3.使用内省属性赋值(已知属性名)
            PropertyDescriptor pd1 = new PropertyDescriptor("brand", class1);
            Method setBrand = pd1.getWriteMethod();
            setBrand.invoke(mycar, "宝马");
            PropertyDescriptor pd2 = new PropertyDescriptor("color", class1);
            Method setColor = pd2.getWriteMethod();
            setColor.invoke(mycar, "红色");
            PropertyDescriptor pd3 = new PropertyDescriptor("price", class1);
            Method setPrice = pd3.getWriteMethod();
            setPrice.invoke(mycar, 100000);
            System.out.println(mycar.toString());
    
            //  4.使用BeanInfo获取类的信息
            BeanInfo beanInfo = Introspector.getBeanInfo(class1);
            PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor pd : pds) {
                System.out.println(pd.getName() + " : " + pd.getPropertyType());
            }
        }
    }
    
    

    设计模式介绍

    • 什么是设计模式
      • 一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
      • 简单理解:特定问题的固定解决方法。(意思:套路😂)
    • 好处:使用设计模式是为了可重用代码、让代码更容易被人理解、保证代码可靠性、重用性。

    设计模式

    • 在Gof的《设计模式》书中描述了23种设计模式,可分为三大类:
      • 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
      • 结构型模式,共七种:适配器模式、装饰者模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
      • 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

    单例设计模式

    • 单例(Singleton):只允许创建一个该类的对象。
      • 实现单例三个步骤
        1. 是优化构造方法
        2. 在类内部创建一个对象
        3. 在类中添加一个公开的方法,返回单例对象

    实现方式一:饿汉子(类加载时创建,天生线程安全)

    1. 基础形式
    /**
     * 使用饿汉子实现单例
     * 特点:
     * (1)类一加载就实例化,没有线程安全问题
     * (2)生命周期长
     */
    public class SingleTon {
        //  1.私有化构造方法
        private SingleTon() {
        }
        //  2.在内部创建这个对象
        private static final SingleTon INSTANCE = new SingleTon();
        //  3.公开的方法,返回这个对象
        public static SingleTon getInstance() {
            return INSTANCE;
        }
    }
    

    注意:可以被反射破解

    1. 改进(抛出异常)
    /**
     * 使用饿汉子实现单例
     * 特点:
     * (1)类一加载就实例化,没有线程安全问题
     * (2)生命周期长
     */
    public class SingleTon {
    	private static boolean flag = true;
        //  1.私有化构造方法
        public SingleTon() {
        	if (flag) {
        		flag = false;
        	} else {
        		throw new RuntimeException("禁止反射破解");
        	}
        }
        //  2.在内部创建这个对象
        private static final SingleTon INSTANCE = new SingleTon();
        //  3.公开的方法,返回这个对象
        public static SingleTon getInstance() {
            return INSTANCE;
        }
    }
    

    注意:无法完全解决反射破解问题

    实现方式二:懒汉子

    1. 基础形式
    /**
     * 懒汉子写法:
     * 特点:
     * (1)不使用,不会实例化
     * (2)有线程安全问题,生命周期短
     */
    public class SingleTon2 {
        //  1.私有化构造方法
        private SingleTon2() {
        }
        //  2.在内部创建对象
        private static SingleTon2 instance;
        //  3.创建一个方法返回这个对象
        public static SingleTon2 getInstance() {
            if (instance == null) {
                instance = new SingleTon2();
            }
            return instance;
        }
    }
    

    注意:
    多线程时可能出现同时在if里睡眠,导致多个线程同时new SingleTon2();

    1. 改进一(加锁)
    /**
     * 懒汉子写法:
     * 特点:
     * (1)不使用,不会实例化
     * (2)有线程安全问题,生命周期短
     */
    public class SingleTon2 {
        //  1.私有化构造方法
        private SingleTon2() {
        }
        //  2.在内部创建对象
        private static SingleTon2 instance;
        //  3.创建一个方法返回这个对象
        public static SingleTon2 getInstance() {
        	synchronized (SingleTon2.class) {
            	if (instance == null) {
                	instance = new SingleTon2();
            	}
            }
            return instance;
        }
    }
    

    注意:
    改进一,容易使多个线程同时进入锁(判断锁)导致形成重量级锁,影响程序执行效率

    1. 改进二(嵌套外层if)
    • 实例化对象的过程有三个步骤
      1. 在堆里开辟空间,属性赋值为默认值
      2. 初始化属性,并调用构造方法(这一条也可分为两步)
      3. 把地址赋值给变量

    注意:实例化对象时,正常执行步骤1–>2–>3,但JVM可能会优化成1–>3–>2

    /**
     * 懒汉子写法:
     * 特点:
     * (1)不使用,不会实例化
     * (2)有线程安全问题,生命周期短
     */
    public class SingleTon2 {
        //  1.私有化构造方法
        private SingleTon2() {
        }
        //  2.在内部创建对象
        private static SingleTon2 instance;
        //  3.创建一个方法返回这个对象
        public static SingleTon2 getInstance() {
        	if (instance == null) {	//	目的:提高执行效率
        		synchronized (SingleTon2.class) {
            		if (instance == null) {
                		instance = new SingleTon2();	//	[^1]
            		}
            	}
        	}
            return instance;
        }
    }
    

    注意:
    外层if只是提高效率
    [^1]: 如果此处实例化对象的步骤为1–>3–>2多线程有可能报空指针异常

    1. 改进三(volatile关键字)
    • volatile:
      1. 保证线程可见性
      2. 禁止指令重排序
    • 在创建对象是加入关键字volatile防止指令重排序
    • 并设置反射解决方式
    /**
     * 懒汉子写法:
     * 特点:
     * (1)不使用,不会实例化
     * (2)有线程安全问题,生命周期短
     */
    public class SingleTon2 {
        private static boolean flag = true;
        //  1.私有化构造方法
        public SingleTon2() {
        	if (flag) {
        		flag = false;
        	} else {
        		throw new RuntimeException("禁止反射破解");
        	}
        }
        //  2.在内部创建对象
        private volatile static SingleTon2 instance;
        //  3.创建一个方法返回这个对象
        public static SingleTon2 getInstance() {
        	if (instance == null) {	//	目的:提高执行效率
        		synchronized (SingleTon2.class) {
            		if (instance == null) {
                		instance = new SingleTon2();
            		}
            	}
        	}
            return instance;
        }
    }
    

    实现方式三:静态内部类

    • 特点:
      1. 生命周期解决了
      2. 线程安全也没有
    /**
     * 静态内部类写法
     * 特点:
     * (1)生命周期解决了
     * (2)线程安全也没有
     */
    public class SingleTon3 {
        private static boolean flag = true;
        private SingleTon3() {
            if (flag) {
                flag = false;
            } else {
                throw new RuntimeException("禁止反射破解");
            }
        }
        //  静态内部类(不使用不执行,调用静态内部类才执行)
        private static class Holder {
            private static final SingleTon3 INSTANCE = new SingleTon3();
        }
        public static SingleTon3 getInstance() {
            return Holder.INSTANCE;
        }
    }
    

    简单工厂模式(不属于23种GOF设计模式之一)

    • 简单工厂模式是属于传火箭型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一。简单工厂模式是由一一个工厂对象决定创建哪一种产品类的实例。
    • (可能因为太过简单)
    • 简单工厂四个角色:
      • 工厂角色:负责创建具体的产品
      • 父类产品:作为所有产品的父类,使用抽象类表示
      • 子类产品:具体的产品
      • 客户程序:使用工厂和产品的程序

    案例

    • 服装厂:生产服装
    • 分析:
      • 需要一个服装工厂类:工厂,类中具有生产服装的功能(创建服装对象)ClothesFactory
      • 服装类(父类产品):抽象类表示,Clothes
      • 服装子类(具体产品):普通类表示,裤子(Trousers)、T恤(TShirt)、夹克(Jacket)
      • 客户程序:使用工厂的程序。
    • 服装类(父类产品):抽象类表示,Clothes
    public abstract class Clothes {
        //  准备布料
        public abstract void prepare();
    
        //  制作
        public abstract void make();
    
        //  打包
        public abstract void box();
    }
    
    • 服装子类(具体产品):普通类表示,裤子(Trousers)、T恤(TShirt)、夹克(Jacket)
      裤子(Trousers)
    public class Trousers extends Clothes {
        @Override
        public void prepare() {
            System.out.println("开始准备裤子布料");
        }
    
        @Override
        public void make() {
            System.out.println("开始制作裤子。。。");
            System.out.println("。。。。。。");
            System.out.println("裤子制作完毕");
        }
    
        @Override
        public void box() {
            System.out.println("开始打包裤子。。。");
        }
    }
    

    T恤(TShirt)

    public class TShirt extends Clothes {
        @Override
        public void prepare() {
            System.out.println("开始准备T恤布料");
        }
    
        @Override
        public void make() {
            System.out.println("开始制作T恤。。。");
            System.out.println("。。。。。。");
            System.out.println("T恤制作完毕");
        }
    
        @Override
        public void box() {
            System.out.println("开始打包T恤。。。");
        }
    }
    

    夹克(Jacket)

    public class Jacket extends Clothes {
        @Override
        public void prepare() {
            System.out.println("开始准备夹克布料");
        }
    
        @Override
        public void make() {
            System.out.println("开始制作夹克。。。");
            System.out.println("。。。。。。");
            System.out.println("夹克制作完毕");
        }
    
        @Override
        public void box() {
            System.out.println("开始打包夹克。。。");
        }
    }
    
    • 配置文件(映射具体产品类的序号)
    • 此文件在项目根目录下,文件名为clothes.properties
    1=night.demo.Trousers
    2=night.demo.TShirt
    3=night.demo.Jacket
    
    • 服装工厂类:工厂,类中具有生产服装的功能(创建服装对象)ClothesFactory
    /**
     * (1)开闭原则:对于扩展时开放的,对于修改时关闭的。
     */
    public class ClothesFactory {
        private static Properties prop = new Properties();
        static {
            FileReader fr = null;
    
            try {
                fr = new FileReader("clothes.properties");
                prop.load(fr);
            } catch (Exception e) {
                System.out.println("初始化衣服失败!");
            } finally {
                try {
                    fr.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        //  静态方法
        public static Clothes creatClothes(int type) { //  type=1——T恤  2——裤子   3——夹克
            Clothes clothes = null;
            /*if (type==1) {
                clothes = new TShirt();
            } else if (type == 2) {
                clothes = new Trousers();
            } else if (type == 3) {
                clothes = new Jacket();
            }*/
            if (prop.containsKey(type)) {
                String className = prop.getProperty(type + "");
                try {
                    Class<?> class1 = Class.forName(className);
                    clothes = (Clothes) class1.newInstance();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (clothes != null) {
                clothes.prepare();
                clothes.make();
                clothes.box();
            }
            return clothes;
        }
    }
    
    • 客户程序:使用工厂的程序。
    public class Test {
        public static void main(String[] args) {
            System.out.println("------------欢迎来到服装厂-----------");
            Scanner sc = new Scanner(System.in);
            while (true) {
                System.out.println("-----------1-裤子     2-T恤    3-夹克    0-退出----------------");
                int choice = sc.nextInt();
                if (choice == 0) {
                    break;
                }
                Clothes clothes = ClothesFactory.creatClothes(choice);
                if (clothes != null) {
                    System.out.println("购买成功;");
                } else {
                    System.out.println("购买失败,请重新输入");
                }
            }
            System.out.println("欢迎下次光临!");
        }
    }
    
    展开全文
  •   代理设计模式最为核心的意义在于,所有的操作业务接口都设置两个子类,一个子类负责真实的业务实现,另外一个子类负责代理业务操作,如果没有这个代理业务,真实业务也无法进行处理。   现在假设说希望可以...

    内容学习于:edu.aliyun.com


    1. 静态代理设计模式弊端

      代理设计模式最为核心的意义在于,所有的操作业务接口都设置两个子类,一个子类负责真实的业务实现,另外一个子类负责代理业务操作,如果没有这个代理业务,真实业务也无法进行处理。
      现在假设说希望可以实现一个数据的处理操作,在进行数据处理的时候,要求进行合理的事务控制,在数据库里面永远都会存在有一个事务的概念,利用事务可以保证数据操作的完整性。

      如下图所示:在这里插入图片描述

    编写传统代理模式:

    interface IMemberService{//实现用户数据操作
        public void add();//实现用户数据追加
    }
    
    class MemberServiceImpl implements IMemberService{
    
        @Override
        public void add() {
            System.out.println("【真实业务主题】向数据库添加数据");
        }
    }
    
    class MemberServiceProxy implements IMemberService{
        private IMemberService realSubject;//真实操作业务
    
        public MemberServiceProxy(IMemberService realSubject){
            this.realSubject = realSubject;
        }
    
        public boolean connect(){
            System.out.println("【代理主题】进行数据库的访问连接...");
            return true;
        }
        public void close(){
            System.out.println("【代理主题】关闭数据库连接...");
        }
    
        public void transaction(){
            System.out.println("【代理主题】事务提交,进行数据更新处理");
        }
        @Override
        public void add() {
            if (this.connect()){
                this.realSubject.add();
                this.transaction();
                this.close();
            }
        }
    }
    
    class Factor {
        private Factor(){}
        public static IMemberService getInstance(){
            return new MemberServiceProxy(new MemberServiceImpl());
        }
    }
    
    public class JavaAPIDemo {
        public static void main(String[] args) throws Exception {
            IMemberService memberService = Factor.getInstance();
            memberService.add();
        }
    }
    

    结果:

    【代理主题】进行数据库的访问连接…
    【真实业务主题】向数据库添加数据
    【代理主题】事务提交,进行数据更新处理
    【代理主题】关闭数据库连接…

      但是现在需要进一步思考一个问题,在一个数据库里面,有可能会存在有上百张表,如果每一张数据表都存在有一个XxxService接口的时候,这个时候代理就会泛滥,而且泛滥的结果会发现所有的代理步骤几乎雷同。

      所以之前的静态代理设计模式设计上只能够满足于一个操作接口的需求,而无法满足于所有操作接口的需求(前提:操作的形式类似)。

    2. 动态代理设计模式

      如果要想解决好静态代理设计模式所存在的代码重复的操作问题,就只能够利用动态代理设计模式来解决,而所谓的动态代理设计模式指的是一组相关的操作接口的实现,可以设置统一的代理类。

      如下图所示:在这里插入图片描述
      动态代理类是在JDK 1.3 的时候添加到项目之中的,如果要想实现动态代理类需要“Invocation"接口和“Proxy类"的支持,首先来观察java.lang.reflect.InvocationHandle接口的定义:

    • public interface InvocationHandler {public 0bject invoke (Object proxy, Method method, Object[] args) throws Throwable ;

      此时对于代理设计需要解决的一个核心问题在于,如何可以让InvocationHandler子类和要代理操作的业务接口产生关联,所以此时就需要通过java.lang.eflect.Proxy类来进行关联的创建,创建关联的方法:

    • public static Object newProxyInstance(ClassL oader loader, Class<?>[] interfaces, InvocationHandler h)

      这种代理对象的创建是依据真实主题类的对象的加载器,和其实现的父接口动态创建的一个新的子类,该子类由JVM在运行的时候自行负责创建。

      如下图所示:在这里插入图片描述

    动态代理模式:

    interface IMemberService {//实现用户数据操作
    
        public void add();//实现用户数据追加
    }
    
    class MemberServiceImpl implements IMemberService {
    
        @Override
        public void add() {
            System.out.println("【真实业务主题】向数据库添加数据");
        }
    }
    
    class ServiceProxy implements InvocationHandler {
        private Object target;//真实操作业务
    
        /**
         * 绑定真实主题对象,同时返回代理实例
         * @param target 真正的接口操作对象,利用反射可以追踪其来源
         * @return 代理对象
         */
        public Object bind(Object target){
            this.target =  target;//保存真实业务对象
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),this);
        }
    
        public boolean connect() {
            System.out.println("【代理主题】进行数据库的访问连接...");
            return true;
        }
    
        public void close() {
            System.out.println("【代理主题】关闭数据库连接...");
        }
    
        public void transaction() {
            System.out.println("【代理主题】事务提交,进行数据更新处理");
        }
    
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object returnValue = null;
            if (this.connect()){
                returnValue = method.invoke(this.target,args);//调用真实主题
                this.transaction();
                this.close();
            }
            return returnValue;
        }
    }
    
    class Factor {
        private Factor() {
        }
    
        public static IMemberService getInstance() {
            return (IMemberService) new ServiceProxy().bind(new MemberServiceImpl());
        }
    }
    
    public class JavaAPIDemo {
        public static void main(String[] args) throws Exception {
            IMemberService memberService = Factor.getInstance();//得到一个jvm动态实例化子类对象
            memberService.add();
        }
    }
    

    结果:

    【代理主题】进行数据库的访问连接…
    【真实业务主题】向数据库添加数据
    【代理主题】事务提交,进行数据更新处理
    【代理主题】关闭数据库连接…

      此时的代码利用动态代理设计类动态的构建了接口的实现子类实例,并且利用InvocationHandler.invoke(实现标准的代码执行调用,在里面进行代理控制。

    3. CGLIB实现动态代理

      现在已经实现了一个代理设计,而且这种代理设计是Java官方推荐的唯一实现机制,但是这种实现机制有部分开发人员认为有问题,在面向对象的角度来讲,不一定非要使用接口来进行标准的定义,有些人认为类应该可以作为标准型的定义形式,所以这类人觉得如下的代码设计不好:

    • return Proxy. newProxyInstance(target. getClass (). getClassLoader(),
      target. getClass (). getInterfaces(), this) ;

      在使用Proxy类创建动态代理类实例的时候必须要求有接口“target.getClass().getInterfaces()",所以有一部分不认可此设计的人员就依据自己的习惯设计了一个基于类的代理操作,可以抛开接口存在的意义,实现代理模式,于是这样的背景下就产生了一个第三方应用开发包“cglib" (cglib-nodep-3.2. 10.jar)。

    通过cglib实现代理:

    class Message {
        public void send(String msg) {
            System.out.println("【真实业务】" + msg);
        }
    }
    
    class MessageProxy implements MethodInterceptor {//cglib是基于拦截器实现的
        private Object target;//真实操作对象
    
        public MessageProxy(Object target) {
            this.target = target;
        }
    
    
        public boolean connect() {
            System.out.println("【代理主题】进行访问连接...");
            return true;
        }
    
        public void close() {
            System.out.println("【代理主题】关闭连接...");
        }
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            Object returnValue = null;
            if (this.connect()) {
                returnValue = method.invoke(this.target, objects);
                this.close();
            }
            return returnValue;
        }
    }
    
    
    public class JavaAPIDemo {
        public static void main(String[] args) throws Exception {
            Message realObject = new Message();//真实业务主题
            Enhancer enhancer = new Enhancer();//负责代理控制的类
            enhancer.setSuperclass(realObject.getClass());//模拟一个操作父类
            enhancer.setCallback(new MessageProxy(realObject));//设置代理对象
            Message proxyObject = (Message) enhancer.create();//创建代理对象,类似于proxy
            proxyObject.send("www.mldn.cn");
        }
    }
    

      此时的操作代码在没有实现父接口的情况下实现类的代理设计,但是这样的设计只是一种传统JDK中的代理操作实现的补充,可以利用其打破原始的设计要求。

      一定要掌握Proxy + InvocationHandle接口的联合使用。

    展开全文
  • 反射机制 什么是Java反射 就是正在运行,动态获取这个类的所有信息。 反射机制的作用 1,反编译:.class-->.java 2.通过反射机制访问java对象的属性,方法,构造方法等; 反射机制的应用场景 Jdbc加载...

    反射机制

    什么是Java反射

    就是正在运行,动态获取这个类的所有信息。

    反射机制的作用

      1,反编译:.class-->.java

       2.通过反射机制访问java对象的属性,方法,构造方法等;

    反射机制的应用场景

    Jdbc 加载驱动-----

    Spring IOC

    框架

    反射机制获取类有三种方法

    //第一种方式:  

    Classc1 = Class.forName("Employee");  

    //第二种方式:  

    //java中每个类型都有class 属性.  

    Classc2 = Employee.class;  

       

    //第三种方式:  

    //java语言中任何一个java对象都有getClass 方法  

    Employeee = new Employee();  

    Classc3 = e.getClass(); //c3是运行时类 (e的运行时类是Employee)  

     

     

    反射创建对象的方式

    Class<?> forName = Class.forName("全限定类名");

    // 创建此Class 对象所表示的类的一个新实例 调用了User的无参数构造方法.

    Object newInstance = forName.newInstance();

    实例化有参构造函数

    Class<?> forName = Class.forName("全限定类名");

    Constructor<?> constructor = forName.getConstructor(String.class, String.class);

    User newInstance = (User) constructor.newInstance("123", "123");

     

    反射创建api

    方法名称

    作用

    getDeclaredMethods []

    获取该类的所有方法

    getReturnType()

    获取该类的返回值

    getParameterTypes()

    获取传入参数

    getDeclaredFields()

    获取该类的所有字段

    setAccessible

    允许访问私有成员

    使用反射为类私有属性赋值

    // 为user对象私有属性赋值

    Class<?> classUser = Class.forName("全限定类名");

    // 获取到当前的所有属性

    Field[] fields = classUser.getDeclaredFields();

    for (Field field : fields) {

    System.out.println(field.getName());

    }

    // 获取当前所有的方法

    Method[] declaredMethods = classUser.getDeclaredMethods();

    for (Method method : declaredMethods) {

    System.out.println(method.getName());

    }

    // 初始化对象

    User user = (User) classUser.newInstance();

    Field declaredField = classUser.getDeclaredField("id");

    // 标记为true 允许反射赋值

    declaredField.setAccessible(true);

    declaredField.set(user, "20");

    System.out.println("使用反射机制给id赋值为:"+user.getId());

    JDBC反射加载驱动

    public class DemoJDBC {

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

            // 加载驱动类

            Class.forName("com.mysql.jdbc.Driver");

        

            // 通过DriverManager获取数据库连接

            String url = "jdbc:mysql://192.168.1.150/test";

            String user = "teamtalk";

            String password = "123456";

            Connection connection = (Connection) DriverManager.getConnection(

                    url, user, password);

            

            PreparedStatement statement = (PreparedStatement) connection.prepareStatement(

                    "insert persion (name, age) value (?, ?)");

            statement.setString(1, "hdu");

            statement.setInt(2, 21);

            statement.executeUpdate();

            

            ResultSet resultSet = statement.executeQuery("select * from persion");

            // 操作ResultSet结果集

            while (resultSet.next()) {

                // 第一种获取字段方式

                System.out.println(resultSet.getString(1) + " " +

                        resultSet.getString(2) + " " + resultSet.getString(3));

            }

     

            // 关闭数据库连接

            resultSet.close();

            statement.close();

            connection.close();

        }

    }

     

    禁止使用反射机制初始化

    将构造函数为私有化

     

    设计模式

    设计模式分类

    创建型模式,共五种:工厂方法模式、抽象工厂模式单例模式、建造者模式、原型模式。

    结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

    行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

     

    设计模式的六大原则

    开闭原则(Open Close Principle)

    开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

     

    里氏代换原则(Liskov Substitution Principle)

    里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科

     

    依赖倒转原则(Dependence Inversion Principle)

    这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。

     

    接口隔离原则(Interface Segregation Principle)

    这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。

     

    迪米特法则(最少知道原则)(Demeter Principle)

    为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

     

    合成复用原则(Composite Reuse Principle)

    原则是尽量使用合成/聚合的方式,而不是使用继承。

     

    单例模式

    什么

     保证一个类只有一个实例,并且提供一个访问该全局访问点

    应用场景

    1. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧)

    2. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。

    3. 网站的计数器,一般也是采用单例模式实现,否则难以同步。

    4. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。

    5. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。

    6. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。

    7. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。

    8. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。

    9. HttpApplication 也是单位例的典型应用。熟悉ASP.Net(IIS)的整个请求生命周期的人应该知道HttpApplication也是单例模式,所有的HttpModule都共享一个HttpApplication实例.

     

    优缺点

    优点:

        1.在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,确保所有的对象都访问一个实例

        2.单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。

        3.提供了对唯一实例的受控访问。

        4.由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。

        5.允许可变数目的实例。

        6.避免对共享资源的多重占用。

    缺点:

        1.不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。

        2.由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。

        3.单例类的职责过重,在一定程度上违背了“单一职责原则”。

        4.滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。

    创建方式

    1. 饿汉:类初始化时,会立即加载该对象,线程天生安全,调用效率高。
    2. 懒汉: 类初始化时,不会初始化该对象,真正需要使用的时候才会创建该对象,具备懒加载功能。
    3. 静态内部方式:结合了懒汉式和饿汉式各自的优点,真正需要对象的时候才会加载,加载类是线程安全的。
    4. 枚举单例: 使用枚举实现单例模式 优点:实现简单、调用效率高,枚举本身就是单例,由jvm从根本上提供保障!避免通过反射和反序列化的漏洞, 缺点没有延迟加载。
    5. 双重检测锁方式 (因为JVM本质重排序的原因,可能会初始化多次,不推荐使用)

     

    饿汉

    //饿汉式

    public class SingletonDemo01 {

    // 类初始化时,会立即加载该对象,线程天生安全,调用效率高

    private static SingletonDemo01 singletonDemo01 = new SingletonDemo01();

     

    private SingletonDemo01() {

    System.out.println("SingletonDemo01初始化");

    }

     

    public static SingletonDemo01 getInstance() {

    System.out.println("getInstance");

    return singletonDemo01;

    }

     

    public static void main(String[] args) {

    SingletonDemo01 s1 = SingletonDemo01.getInstance();

    SingletonDemo01 s2 = SingletonDemo01.getInstance();

    System.out.println(s1 == s2);

    }

     

    }

     

     

     

    懒汉

    //懒汉式 

    public class SingletonDemo02 {

     

    //类初始化时,不会初始化该对象,真正需要使用的时候才会创建该对象。

    private static SingletonDemo02 singletonDemo02;

     

    private SingletonDemo02() {

       

    }

     

    public synchronized static SingletonDemo02 getInstance() {

    if (singletonDemo02 == null) {

    singletonDemo02 = new SingletonDemo02();

    }

    return singletonDemo02;

    }

     

    public static void main(String[] args) {

    SingletonDemo02 s1 = SingletonDemo02.getInstance();

    SingletonDemo02 s2 = SingletonDemo02.getInstance();

    System.out.println(s1 == s2);

    }

     

    }

     

     

     

    静态内部类

    // 静态内部类方式

    public class SingletonDemo03 {

    private SingletonDemo03() {

               System.out.println("初始化..");

    }

     

    public static class SingletonClassInstance {

    private static final SingletonDemo03 singletonDemo03 = new SingletonDemo03();

    }

     

    // 方法没有同步

    public static SingletonDemo03 getInstance() {

    System.out.println("getInstance");

    return SingletonClassInstance.singletonDemo03;

    }

     

    public static void main(String[] args) {

    SingletonDemo03 s1 = SingletonDemo03.getInstance();

    SingletonDemo03 s2 = SingletonDemo03.getInstance();

    System.out.println(s1 == s2);

    }

    }

     优势:兼顾了懒汉模式的内存优化(使用时才初始化)以及饿汉模式的安全性(不会被反射入侵)。

     

     劣势:需要两个类去做到这一点,虽然不会创建静态内部类的对象,但是其 Class 对象还是会被创建,而且是属于永久带的对象。

    枚举方式

    什么是枚举

    枚举本身是单例的,一般用于项目中定义常量。

    enum UserEnum {

    HTTP_200(200, "请求成功"),HTTP_500(500,"请求失败");

    private Integer code;

    private String name;

     

    UserEnum(Integer code, String name) {

    this.code = code;

    this.name = name;

    }

     

    public Integer getCode() {

    return code;

    }

     

    public void setCode(Integer code) {

    this.code = code;

    }

     

    public String getName() {

    return name;

    }

     

    public void setName(String name) {

    this.name = name;

    }

     

    }

     

    public class TestEnum {

     

    public static void main(String[] args) {

    System.out.println(UserEnum.HTTP_500.getCode());

    }

     

    }

     

     

     

    /使用枚举实现单例模式 优点:实现简单、枚举本身就是单例,由jvm从根本上提供保障!避免通过反射和反序列化的漏洞 缺点没有延迟加载

    public class User {

    public static User getInstance() {

    return SingletonDemo04.INSTANCE.getInstance();

    }

     

    private static enum SingletonDemo04 {

    INSTANCE;

    // 枚举元素为单例

    private User user;

     

    private SingletonDemo04() {

    System.out.println("SingletonDemo04");

    user = new User();

    }

     

    public User getInstance() {

    return user;

    }

    }

     

    public static void main(String[] args) {

    User u1 = User.getInstance();

    User u2 = User.getInstance();

    System.out.println(u1 == u2);

    }

    }

     

     

     

     

    重检测锁

    public class SingletonDemo04 {

    private SingletonDemo04 singletonDemo04;

     

    private SingletonDemo04() {

     

    }

     

    public SingletonDemo04 getInstance() {

    if (singletonDemo04 == null) {

    synchronized (this) {

    if (singletonDemo04 == null) {

    singletonDemo04 = new SingletonDemo04();

    }

    }

    }

    return singletonDemo04;

    }

     

    }

     

    防止反射漏洞攻击

    在构造函数中,只能允许初始化化一次即可。

    private static boolean flag = false;

     

    private SingletonDemo04() {

     

    if (flag == false) {

    flag = !flag;

    } else {

    throw new RuntimeException("单例模式被侵犯!");

    }

    }

     

    public static void main(String[] args) {

     

    }

     

     

    如何选择单例创建方式

    如果不需要延迟加载单例,可以使用枚举或者饿汉式,相对来说枚举性好于饿汉式。

    如果需要延迟加载,可以使用静态内部类或者懒汉式,相对来说静态内部类好于懒韩式。

     

     

    工厂模式

    什么工厂模式

     实现了创建者和调用者分离,工厂模式分为简单工厂、工厂方法、抽象工厂模式

     

    工厂模式好处

    工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。

    利用工厂模式可以降低程序的耦合性,为后期的维护修改提供了很大的便利。

    将选择实现类、创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。

     

    工厂模式分类

    简单工厂模式

    简单工厂模式相当于是一个工厂中有各种产品,创建在一个类中,客户无需知道具体产品的名称,只需要知道产品类所对应的参数即可。但是工厂的职责过重,而且当类型过多时不利于系统的扩展维护。

     

     

     

     

    public interface Car {

    public void run();

    }

     

    public class AoDi implements Car {

    public void run() {

    System.out.println("我是奥迪汽车..");

    }

    }

    public class JiLi implements Car {

     

    public void run() {

    System.out.println("我是吉利汽车...");

    }

    }

    public class CarFactory {

     

     public static Car createCar(String name) {

    if (StringUtils.isEmpty(name)) {

                 return null;

    }

    if(name.equals("奥迪")){

    return new AoDi();

    }

    if(name.equals("吉利")){

    return new JiLi();

    }

    return null;

    }

    }

    public class Client01 {

     

    public static void main(String[] args) {

    Car aodi  =CarFactory.createCar("奥迪");

    Car jili  =CarFactory.createCar("吉利");

    aodi.run();

    jili.run();

    }

     

    }

    单工厂的优点/缺点

    优点:简单工厂模式能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。明确区分了各自的职责和权力,有利于整个软件体系结构的优化。

    缺点:很明显工厂类集中了所有实例的创建逻辑,容易违反GRASPR的高内聚的责任分配原则

    工厂方法模式

    什么是工厂方法模式

     

    工厂方法模式Factory Method,又称多态性工厂模式。在工厂方法模式中,核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类去做。该核心类成为一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。

     

    public interface Car {

    public void run();

    }

     

    public class AoDi implements Car {

    public void run() {

    System.out.println("我是奥迪汽车..");

    }

    }

    public class JiLi implements Car {

     

    public void run() {

    System.out.println("我是吉利汽车...");

    }

    }

    public class JiLiFactory implements CarFactory {

     

    public Car createCar() {

     

    return new JiLi();

    }

     

    }

    public class AoDiFactory implements CarFactory {

     

    public Car createCar() {

     

    return new AoDi();

    }

    }

    public class Client {

     

    public static void main(String[] args) {

    Car aodi = new AoDiFactory().createCar();

    Car jili = new JiLiFactory().createCar();

    aodi.run();

    jili.run();

    }

     

    }

     

    抽象工厂模式

    什么抽象工厂模式

    抽象工厂简单地说是工厂的工厂,抽象工厂可以创建具体工厂,由具体工厂来产生具体产品。

     

     

     

     

    //发动机

    public interface Engine {

     

    void run();

     

    void start();

    }

     

    class EngineA implements Engine {

     

    public void run() {

          System.out.println("转的快!");

    }

     

    public void start() {

     System.out.println("启动快,自动档");

    }

     

    }

     

    class EngineB implements Engine {

     

    public void run() {

          System.out.println("转的慢!");

    }

     

    public void start() {

     System.out.println("启动快,手动档");

    }

     

    }

     

    //座椅

    public interface Chair {

       void run();

    }

     

     class ChairA implements Chair{

     

    public void run() {

    System.out.println("可以自动加热!");

    }

     

    }

     class ChairB implements Chair{

     

    public void run() {

    System.out.println("不能加热!");

    }

     

    }

    public interface CarFactory {

    // 创建发动机

    Engine createEngine();

    // 创建座椅

    Chair createChair();

    }

    public class JiLiFactory implements CarFactory  {

     

    public Engine createEngine() {

     

    return new EngineA();

    }

     

    public Chair createChair() {

     

    return new ChairA();

    }

     

    }

    public class Client002 {

     

     public static void main(String[] args) {

    CarFactory carFactory=new JiLiFactory();

    Engine engine=carFactory.createEngine();

    engine.run();

    engine.start();

     

    }

     

    }

     

     

     

     

     

     

    建造者模式

     

    模板方法

     

    原型模式

    适配模式

    装饰模式

    代理模式

     

    外观模式

    策略模式

    观察者模式

     

     

    展开全文
  • 行为型模式(11种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。 设计模式的六原则 开闭原则:对扩展开放,对修改关闭 ...

    在介绍设计模式之前,先贴一下设计模式的三种类型,和六大原则。

    • 创建型模式(5种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。
    • 结构型模式(7种):适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式。
    • 行为型模式(11种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

    设计模式的六原则

    • 开闭原则:对扩展开放,对修改关闭
    • 里氏代换原则:任何基类可以出现的地方,子类一定可以出现,(子类代换基类)
    • 依赖倒转原则:对接口编程,依赖于抽象,而不依赖于具体
    • 接口隔离原则:使用多个隔离的接口,比使用单个好,降低耦合度
    • 合成复用原则:尽量使用合成/聚合的方式,而不是使用继承
    • 迪米特法则(最少知道原则):实体之间尽量少发生相互作用,使模块相对独立

    下面开始介绍最简单常见的单例模式

    使用场景:一个类全局只有一个实例的情况。该实例由单例类自己创建,并提供访问该实例的方法。使用单例类一般是实例创建比较复杂的情况,且对象不保存状态信息(即没有可供修改的成员变量,否则很容易被修改)。可以将单例类理解成一个工具类,提供全局访问,只有一个实例。Spring中的bean默认使用的就是单例模式。

    下面开始介绍单例模式的六种实现

    饿汉模式

    饿汉模式是在类被加载的时候就创建了实例,即使这个单例都不一定会被使用。

    public class Singleton{
        private Singleton(){}    // 构造方法私有化
        private static final Singleton instance = new Singleton();       // 实例在类加载时就被创建 
        public static Singleton getInstance(){
            return instance;
        }
    }
    

    懒汉模式

    懒汉模式是在只有单例类被使用的时候才创建出来。

    public class Singleton {
    	private Singleton(){}
    	private static Singleton instance = null;	// 默认为null
    	public static Singleton getInstance(){
    		if (instance == null) {				// 方法被调用的时候才创建
    			instance = new Singleton();
    		}
            return instance;
        }
    }
    

    线程安全的懒汉模式

    在多线程环境下,上面的懒汉模式,很容易创建出多个实例。于是需要加锁。加一个synchronized就不会有多线程创建多个实例了。

    public class Singleton {
    	private Singleton(){}
    	private static Singleton instance = null;	// 默认为null
    	public static synchronized Singleton getInstance(){
    		if (instance == null) {				// 方法被调用的时候才创建
    			instance = new Singleton();
    		}
            return instance;
        }
    }
    

    双重检验锁 的懒汉模式

    既然是多线程环境下,直接在方法上加synchronized,那效率就很低了,肯定不满足要求。所以需要在内部创建对象的时候才加锁。

    但是看下面的代码,我们发现在获取到锁开始创建对象前,又进行了一次非空校验。这个应该很好理解吧,在创建时加锁只是让线程一个一个进去。两个线程同时访问,第一个获取到锁的线程进去创建了实例后,第二个进入,如果不判空,第二个还是会创建另一个实例。双重校验锁在平时写代码中很常见(划重点)

    然后其实只有一个应该注意的是 volatile 关键字,该关键字能够防止java指令重排序。instance = new Singleton();在jvm中包含多个操作(划分一块内存 ->在内存上创建对象 -> 将instance指向对应内存等),然后进过jvm重排序后,可能就是 (划分一块内存 -> 将instance指向对应内存等 -> 在内存上创建对象) 这样的话,就会返回一个没有实例化完全的对象了,比如null。

    public class Singleton {
        private Singleton(){}
        private static volatile Singleton instance = null;
        public static synchronized Singleton getInstance(){
            if (instance == null) {
                synchronized (Singleton.class) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
    

    静态内部类模式 (登记式)

    这种方法也是在使用时才实例化。但看代码里面貌似直接new了。这是利用了静态内部类的特性,只有在访问静态内部类时,它才会被加载。

    public class Singleton{
        private static class Inner{
            private static final Singleton instance= new Singleton();
        }
        private Singleton();
        public static Singleton getInstance(){        //调用该方法时,Inner类才会被加载,然后实例化instance
            return Inner.instance; 
        }
    }
    

    枚举类模式

    最简单粗暴的方式当然是我们的枚举类了。都不需要写getInstance方法,直接 Singleton.instance 访问。

    public enum Singleton{
        instance;
    }
    

    这样咋一看,感觉枚举类和饿汉模式差不多嘛。不不不,在反射面前,前面5中模式都是渣渣。只有枚举类不能通过反射修改构造方法。

    public static void main(String[] args)  {
       Class<Singleton> aClass = Singleton.class;
        try {
            Constructor<Singleton> constructor = aClass.getDeclaredConstructor();
            constructor.setAccessible(true);
            Singleton singleton = constructor.newInstance();
            Singleton singleton2 = constructor.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    

    然后看到有人说,加一个类变量,然后调用构造方法时判断其状态,如果时第二次就抛异常。比如下面这样

    public class Singleton {
        private static boolean init = false;
        private Singleton(){
            if (init) {
                throw new RuntimeException();
            }
            init = true;
            System.out.println("hahah");
        }
        private static Singleton instance = new Singleton();
    
        public static Singleton getInstance() {
            return instance;
        }
    }
    

    天真!以为反射只能修改构造方法的可见性?

    public static void main(String[] args)  {
        Class<Singleton> aClass = Singleton.class;
        try {
            Constructor<Singleton> constructor = aClass.getDeclaredConstructor();
            constructor.setAccessible(true);
            Field init = aClass.getDeclaredField("init");
            init.setAccessible(true);
            init.setBoolean(null, false);
            Singleton singleton = constructor.newInstance();
            init.setBoolean(null, false);
            Singleton singleton2 = constructor.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    展开全文
  • 复习工厂模式: 定义一个用于创建对象的接口,让子类决定实例化哪一个类 UML 代码 class User { private int _id; public int Id { get =&gt; _id; set =&gt; _id = value; } private string ...
  • 设计模式反射机制

    2019-10-28 09:14:14
    一、 什么是Java反射 就是正在运行,动态获取这个类的所有信息。 1、反射机制的作用 - 1,反编译:.class-->.java- 2.通过反射机制访问java对象的属性,方法,构造方法等; 2、反射机制的应用场景 Jdbc 加载驱动---...
  • Java反射&设计模式

    2019-12-16 23:56:43
    设计模式反射机制什么是Java反射反射机制的作用反射机制的应用场景反射机制获取类的三种方法反射创建对象的方式反射创建api使用反射为类私有属性赋值JDBC反射加载驱动禁止使用反射机制初始化设计模式设计模式分类...
  • 1. 反射机制 1.1 什么是Java反射 就是正在运行,动态获取这个类的所有信息。 反射机制的作用 反编译:.class–>.java 通过反射机制访问java对象的属性,方法,构造方法等; 1.2 反射机制的应用场景 Jdbc 加载...
  • 思维导图参考:【十六】Java反射思维导图 一、什么是类对象 类的对象: 基于某个类new出来的对象,也称为实例对象 类对象: 类加载的产物,封装了一个类的所有信息(类名、父类、接口、属性、方法、构造方法) ...
  • Java工厂模式和通用编程1. 工厂模式2. 通用编程 ...可通过反射进行工厂模式的设计,完成动态的对象创建。 public class TestNewInstanceForFile { public static void main(String[] args) thr...
  • 单例模式反射漏洞和反序列化漏洞 除了枚举式单例模式外,其余4种在单例模式提到的单例模式的实现方式都存在反射漏洞和反序列化漏洞。 package singleton; import java.io.FileInputStream; import java.io....
  • 反射 1.反射机制 Java的反射机制是指在运行状态中,对于任何一个类,都可以知道这个类所有属性和方法,对于任何一个对象,都可以用这个对象调用所有方法和属性,这种动态获取数据和操作对象的方法和属性的能力叫反射机制....
  • 反射方法 hasattr(obj,name) 判断该对象中是否包含名称name的属性 setattr(obj,name,value) 给名称为name的属性设置value值 getattr(obj,name) 获取名称为name的属性的具体数据 delattr(obj,name) 删除名称为...
  • 2、什么是Java反射 ? 正在运行,动态获取这个类的所有属性及方法。 3、反射机制的作用? 1,反编译:.class —> .java 2.通过反射机制访问java对象的属性,方法,构造方法等; 4、什么是SpringIOC底层...
  • 目录介绍传统工厂模式工厂模式结合反射机制总结 介绍 工厂模式是一种创建型模式,在工厂模式中,创建对象时不会对客户端暴露创建细节(将创建的代码封装到一个工厂类中),而是通过一个接口指向由工厂类创建并返回的...
  • Java高级特性反射与动态代理模式

    千次阅读 多人点赞 2020-10-06 11:36:46
    动态代理模式,也是Java中常见的一种设计模式,其中动态代理模式,也是用到了反射,所以我们反射和动态代理一起学一学,最后我们分析JDK的Proxy是怎么为我们生成代理类的!最后别忘了一键三连哦
  • 因为相比其他设计模式,实现一个单例模式是比较简单的。单例模式的意思就是一个类只有一个实例。 获取类的实例,我们往往采用new关键字,但是要保证一个类只能有一个实例,所以不能让使用这个类的开发人员利用new...
  • 本篇博客是有关在项目中通过spring来实现策略模式+反射,从而灵活扩展业务实现类。 spring本身已经将反射的功能很好的封装了。我们进一步封装一个关于反射的工具类 代码如下:SpringContextHolder 工具类 ...
  • 单例模式几乎是面试中必考的设计模式。单例到底有几种写法呢?...反射怎么获取单例模式?被获取后又该怎么更改?本文用奇淫技巧的方式,带你玩转单例模式。单例模式大全,反射拆解!你面试要的8种单例都在这!
  • 工厂设计模式: ...可通过反射进行工厂模式的设计,完成动态的对象创建。 创建对象的两种方式(以Student为例): Student s = new Student(); Class c = Student.class; Object o = c.newInstance(); Stude...
  • 1. 什么是Java反射机制 就是正在运行,动态获取这个类的所有信息。 2. 反射机制的作用 反编译:.class–>.java 通过反射机制访问java对象的属性,方法,构造方法等 3. 反射机制的应用场景 Jdbc 加载驱动 ...
  • UE4平面反射

    2020-09-24 13:26:58
    UE4 支持实时平面反射,它比屏幕空间反射(SSR)更加精确,但渲染开销较高。渲染开销较高的原因来自平面反射的工作原理。因为平面反射实际上将从反射方向再次对关卡进行渲染。 在渲染方面,屏幕空间反射(SSR)比...
  • 反射与代理设计模式 静态代理设计模式 我们先来看看传统的代理设计模式 如果按照设计要求来讲,必须是基于接口的设计,也就是说,要先定义一个核心的接口类。 package 阿里云大学java.java高级学习.反射练习; ...
  • '操作类型:0:增/1:删/2:改/3:关闭/4:移动' , `operationTime` datetime NOT NULL COMMENT '操作时间' , PRIMARY KEY ( `id` ) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 3、日志实体类 ...
  • c反射机制介绍_介绍反射

    千次阅读 2020-06-30 05:33:58
    在“ Java编程动力学,第1部分 ”中,我向您介绍了... 为了使即使对于已经了解反射基础知识的开发人员来说,也使事情变得有趣,我将介绍反射性能与直接访问的比较。 不要错过本系列的其余部分 第1部分,“ 类和...
  • 反射机制与单例

    2020-02-24 23:09:59
    什么是Java反射 就是正在运行,动态获取这个类的所有信息。 反射机制的作用 1,反编译:.class–>.java 2.通过反射机制访问java对象的属性,方法,构造方法等; 反射机制的应用场景 Jdbc 加载驱动----- Spring ...
  • 1、java反射 反射是指在运行状态中,对于任意的一个类,都可以获取这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取信息及动态调用对象的方法功能成为java语言的反射...
  • 就是违背了“系统对扩展开放,对修改关闭”的原则,因为当我新增加一个产品的时候必须修改工厂类,相应的工厂类就需要重新编译一遍。 具体代码, <pre name="code" class="csharp">namespace NewFeatureStudy { ...
  • 从java 反射看延迟加载(代理模式)(二)

    千次阅读 热门讨论 2016-10-30 11:41:35
    代理模式 代理模式使用代理对象完成用户请求,屏蔽用户对真实对象的访问。现实世界的代理人被授权执行当事人的一些事宜,无需当事人出面,从第三方的角度看,似乎当事人并不存在,因为他只和代理人通信。而事实上...
  • 我们在利用java的三层架构开发时,如果用Web层...对,我们可以通过利用“工厂模式+反射+配置文件”的模式来降低耦合度,从而只是通过修改配置文件就能实现我们的需求。 下面我们先通过案例的形式来分析一下传统模式

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 49,771
精华内容 19,908
关键字:

关闭反射模式