反射+与设计模式

一、反射

1、概念

Java的反射机制是在编译时,并不确定哪个类被加载,而是在运行时才加载,这样的特点就是反射

2、功能

在运行时获取一个对象的类信息:访问修饰符、成员、方法、构造及超类的信息。

  • 动态生成对象:反射机制可以在运行时生成对象,这样就可以根据参数的不同,动态的创建不同的类的实例对象。

  • 动态调用方法:通过反射机制可以调用类中的方法,不论这些方法是否是公共的,也不论这些方法的参数个数和类型是什么,反射机制都具有这样的能力。

  • 动态修改属性:利用反射机制可以获取到类中的所有成员变量,并可以对其进行修改

3、反射的应用
  1. 在使用IDE(如Eclipse,IDEA)时,当我们输入一个对象或类并想调用它的属性或方法时,一按点号,编译器就会自动列出它的属性或方法,这里就会用到反射反射最重要的用途就是开发各种通用框架。

  2. 框架(Spring,Mybatis,Hibernate)都是配置化的(比如通过XML文件配置JavaBean,Action之类的),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象

4、常用类和API

要使用Java反射机制,就得使用java.lang.Class这个类,它是Java反射机制的起源。

当一个类被加载后,Java虚拟机就会自动产生一个Class对象,通过该对象就能获得该对象的方法、成员及构造等信息

在Java中,主要由以下类实现反射,这些类都位于java.lang.reflect包中:

含义
Method代表类的方法
Constructor代表类的构造方法
Field代表类的成员变量
5、获得Class实例的方式

获得Class实例的有四种方式:

第一种:使用Class类的静态方法forName(),用类的全名获得一个Class实例。

Class clazz = Class.forName(“java.util.Hashtable”);

第二种:利用对象调用getClass()方法获得对象的Class实例。

String s = "hello";
Class clazz = s.getClass();

第三种:通过的类的class属性获得该类的Class实例。

Class clazz = java.util.Hashtable.class;

第四种:对于基本数据类型的封装类,可以采用Type属性获得对应数据类型的Class实例。

Class clazz = Integer.Type;

二、相关类及API

1、Class类
1.1、常用方法
方法名备注
public String getName()返回完整类名带包名
public String getSimpleName()返回类名
public Field[] getFields()返回类中public修饰的属性
public Field[] getDeclaredFields()返回类中所有的属性
public Field getDeclaredField(String name)根据属性名name获取指定的属性
public native int getModifiers()获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Method[] getDeclaredMethods()返回类中所有的实例方法
public Method getDeclaredMethod(String name, Class<?>… parameterTypes)根据方法名name和方法形参获取指定方法
public Constructor<?>[] getDeclaredConstructors()返回类中所有的构造方法
public Constructor getDeclaredConstructor(Class<?>… parameterTypes)根据方法形参获取指定的构造方法
public native Class<? super T> getSuperclass()返回调用类的父类
public Class<?>[] getInterfaces()返回调用类实现的接口集合
public T newInstance()创建对象
1.2、示例
public class Main {
​
    public static void main(String[] args) {
        try {
            Class<?> clazz = Class.forName("java.util.ArrayList");
            System.out.println(clazz.getName());
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
​
}
1.3、newInstance()

通过反射实例化对象

public class MethodTest {
    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        try {
            Class clazz = Class.forName("java.util.ArrayList");
            //得到对象
            Object object = clazz.newInstance();
            //强转为ArrayList类型
            ArrayList list = (ArrayList)object;
            list.add("hello");
            System.out.println(list.get(0));
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }   
​
}
2、Method类
2.1、常用方法
方法名备注
public String getName()返回方法名
public int getModifiers()获取方法的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Class<?> getReturnType()以Class类型,返回方法类型【一般配合Class类的getSimpleName()方法使用】
public Class<?>[] getParameterTypes()返回方法的修饰符列表(一个方法的参数可能会有多个。)【结果集一般配合Class类的getSimpleName()方法使用】
public Object invoke(Object obj, Object… args)调用方法
2.2、得到所有方法
public class MethodTest {
    public static void main(String[] args) {
        try {
            Class clazz = Class.forName("java.util.ArrayList");
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                System.out.println(method.getName());
            }
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }   
​
}
2.3、invoke方法

invoke方法:对带有指定参数的指定对象调用由此 Method 对象表示的基础方法

方法.invoke(对象, 实参);

public class MethodTest {
    public static void main(String[] args) throws Exception {
        
        Class clazz = Operator.class;
        //创建对象
        Object object = clazz.newInstance();
        //得到方法
        Method method = clazz.getMethod("add", Integer.class, Integer.class);
        //Method method = clazz.getMethod("add", int.class,int.class);基本数据类型写法
        //调用方法
        Object result = method.invoke(object, 1, 1);
        System.out.println(result);
​
    }
​
}
​
class Operator {
    public int add(Integer a, Integer b) {
        return a + b;
    }
​
    public int sub(Integer a, Integer b) {
        return a - b;
    }
}
3、Field类
3.1、常用方法
方法名备注
public String getName()返回属性名
public int getModifiers()获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Class<?> getType()以Class类型,返回属性类型【一般配合Class类的getSimpleName()方法使用】
public void set(Object obj, Object value)设置属性值
public Object get(Object obj)读取属性值
public void setAccessible(true)允许访问私有属性
3.2、迭代所有属性

注意这里使用的是getDeclaredFields

public class FieldTest {
    public static void main(String[] args) {
        try {
            Class clazz = Class.forName("java.util.ArrayList");
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                System.out.println(field.getName());
            }
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }   
​
}
3.3、给属性赋值

属性.set(对象, 值);

属性.get(对象);

注意这里使用的是getDeclaredFields

public static void main(String[] args) throws Exception {
        try {
            //Class<Employee> clazz = Employee.class; 也可以考虑引入泛型,更直观一些
            Class clazz = Class.forName("com.reflect.Employee");
            //创建对象
            Object obj = clazz.newInstance();
            //得到name字段
            Field empField = clazz.getDeclaredField("empId");
            //允许访问私有属性
            empField.setAccessible(true);
            // 给name属性赋值
            empField.set(obj, "1001");
            //输出EmpId的值
            System.out.println(empField.get(obj));
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }   
4、Constructor类
4.1、常用方法
方法名备注
public String getName()返回构造方法名
public int getModifiers()获取构造方法的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Class<?>[] getParameterTypes()返回构造方法的修饰符列表(一个方法的参数可能会有多个。)【结果集一般配合Class类的getSimpleName()方法使用】
public T newInstance(Object … initargs)创建对象【参数为创建对象的数据】
4.2、示例
    public static void main(String[] args) throws Exception  {
        try {
            Class clazz = Class.forName("com.reflect.Employee");
            
            //获取无参构造方法
            //Constructor con = clazz.getDeclaredConstructor();
            // 第一步:先获取到这个有参数的构造方法
            Constructor cons= clazz.getDeclaredConstructor(String.class,String.class,float.class);
            // 第二步:调用构造方法创建对象
            Object obj = cons.newInstance("1001","tom",8000f);
            System.out.println(obj);
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }   

三、Properties

1、简介

Properties 类表示了一个持久的属性集,是一个Map体系集合类,因为其继承于Hashtable,而Hashtable继承于Dictionary类,实现了Map接口,所以Properties也保留了其相关特性。

Properties特点:

  • Properties是Hashtable<Object,Object>的子类;

  • Properties类表示了一个可持久的属性集;

  • Properties可以保存在流中或从流中加载;

  • Properties中每个键和对应的值都是一个字符串(String类型);

  • Properties有一个特殊的作用:专门用来加载xxx.properties配置文件。

2、properties配置文件作用

properties配置主要的作用是通过修改配置文件可以方便地修改代码中的参数,实现不用改class文件即可灵活变更参数。

在Java中,其配置文件常为.properties文件,格式为文本文件,文件的内容的格式是“键=值”的格式,文本注释信息可以用"#"来注释

jdbc.properties文件示例

db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/store?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
db.username=root
db.password=root

java运行中java文件会变成class文件,之后无法通过反编译找到原样的代码,这样的话,如果java类中某个参数变更,就很难灵活的实现参数修改,这个时候properties 文件就能很灵活的实现配置,减少代码的维护成本和提高开发效率

3、常用方法

它提供了几个主要的方法:

方法说明
getProperty ( String key)用指定的键在此属性列表中搜索属性。也就是通过参数 key ,得到 key 所对应的 value
load ( InputStream is)从输入流中读取属性列表(键和元素对)。通过对指定的文件进行装载来获取该文件中的所有键 - 值对。以供 getProperty ( String key) 来搜索
setProperty ( String key, String value)调用 Hashtable 的方法 put 。他通过调用基类的put方法来设置 键 - 值对
store ( OutputStream out, String comments)以适合使用 load 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。与 load 方法相反,该方法将键 - 值对写入到指定的文件中去
clear ()清除所有装载的 键 - 值对。该方法在基类中提供
4、Java读取Properties文件

Java读取Properties文件的方法有很多,常见有以下几种方式:

使用方式文件位置
new BufferedInputStream(new FileInputStream("source.properties"))项目根目录
Class.getClassLoader().getResourceAsStream("source.properties")src下(ClassPath根)
Class.getResourceAsStream(String path)此类所在的包下取资源

path不能以’/‘开头

aa.properties文件

age=22
name=\u5F20\u4E09

读取资源文件中内容

InputStream  is = Main.class.getClassLoader().getResourceAsStream("aa.properties");
properties.load(is);
Object obj = properties.get("age");
Object name = properties.get("name");

二、设计模式

一、简介

1、什么是设计模式

设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。

  • 使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。

  • 每个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。

  • 设计模式的贯穿思想:针对接口编程,最大限度的适应变化,实现代码重用

2、设计模式基本要素
  • 模式名称(pattern name):一个助记名, 它用一两个词来描述模式的问题、解决方案和效果。

  • 问题(problem):描述了应该在何时使用模式它解释了设计问题和问题存在的前因后果,它可能描述了特定的设计问题,如怎样用对象表示算法等。

  • 解决方案(solution):描述了设计的组成成分,它们之间的相互关系及各自的职责和协作方式。

  • 效果(consequences) : 描述了模式应用的效果及使用模式应权衡的问题。

3、设计模式的分类

我们根据两条准则对模式进行分类。

3.1、第一是目的准则

即模式是用来完成什么工作的

模式依据其目的可分为:

  • 创建型(Creational) 创建型模式与对象的创建有关

  • 结构型 (Structural) 结构型模式处理类或对象的组合

  • 行为型(Behavioral) 行为型模式对类或对象怎样交互和怎样分配职责进行描述

3.2、第二是范围准则

指定模式主要是用于类还是用于对象。

  • 类模式:处理类和子类之间的关系,这些关系通过继承建立,是静态的,在编译时刻便确定下来了。

  • 对象模式:处理对象间的关系,这些关系在运行时刻是可以变化的,更具动态性。

从某种意义上来说,几乎所有模式都使用继承机制,所以“类模式”只指那些集中于处理类间关系的模式,而大部分模式都属于对象模式的范畴

3.3、创建模式

创建模式(Creational Pattern):是对类的实例化过程的抽象化

一些系统在创建对象时,需要动态地决定怎样创建对象、创建哪些对象,创建模式描述了怎样构造和封装这些动态的决定:

  • 工厂方法(Factory Method)

  • 抽象工厂(Abstract Factory)

  • 单例(Singletion)

  • 原型(Prototype)

  • 建造者(Builder)

3.4、结构模式

结构模式(Structural Pattern):描述如何将类或者对象结合在一起形成更大的结构,结构模式描述了两种不同的东西:类、类的实例

  • 适配器(Adapter)

  • 桥接(Bridge)

  • 组合(Composite)

  • 装饰(Decorator)

  • 外观(Facade)

  • 享元(Flyweight)

  • 代理(Proxy)

3.5、行为模式

在不同的对象之间划分责任和算法,而且解决对象之间如何相互作用

  • 迭代(Iterator)

  • 模板方法(Template Method)

  • 职责链(Chain of Responsibility)

  • 访问者(Visitor)

  • 中介者(Mediator)

  • 观察者(Observer)

  • 备忘录(Memento)

  • 状态(State)

  • 策略(Strategy)

  • 命令(Command)

  • 解释器(Interpreter)

二、工厂模式

1、概念

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

工厂模式又叫静态工厂方法模式、简单工厂

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象

2、结构

模式的核心是工厂类。这个类含有必要的判断逻辑,可以决定在什么时候创建那一个产品类的实例。而客户端则可以免除直接创建产品对象的责任,而仅仅负责“消费”产品。简单工厂模式通过这种做法实现了对责任的分割

  • 工厂类(Creator)角色:担任这个角色的是工厂模式的核心,含有与应用紧密相关的商业逻辑。工厂类在客户端的直接调用下创建产品对象,它往往由一个具体Java类实现。

  • 抽象产品(Product)角色:担任这个角色的类是由工厂方法模式所创建的对象的父类,或它们共同拥有的接口。抽象产品角色可以用一个Java接口或者java抽象类实现。

  • 具体产品(Concrete Product)角色:工厂方法模式所创建的任何对象都是这个角色的实例,具体产品角色有一个具体java类实现

image-20221028115830655

3、创建测试接口及实现类
public class FruitFactory {
    public static IFruit getFruit(String type) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        IFruit fruit = (IFruit)Class.forName("com.woniuxy.factory."+type).newInstance();
        return fruit;
    }
    public static IFruit getFruit2(String type) {
        switch(type) {
        case "Banana":
            return new Banana();
        case "Apple":
            return new Apple();
        case "Orange":
            return new Orange();            
        }
        return null;
    }
}

三、单例(Singleton)模式

1、概念
  • 某个类只有一个示例

  • 自行创建该实例

  • 以统一公共的方式向整个系统提供该实例

2、应用场景

许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。

比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。

这种方式简化了在复杂环境下的配置管理。

Java中JDBC的DataSoucre只需要一个实例,但是如果我们想创建多个实例语法上是没有问题的(虽然会造成代码的冗余)。

因此单例模式本质上就是:借助编程语言自身的语法特性,强制限制某个类,不能创建多个实例

3、分类
  • 饿汉式

  • 懒汉式

4、饿汉式

直接创建对象, EagerSingleton

public class EagerSingleton {
    //某个类只有一个示例
    private EagerSingleton() {
        System.out.println("aa");
    }
    //自行创建该实例
    private static final EagerSingleton singleton = new EagerSingleton();
    //以统一公共的方式向系统 提供该实例
    public static EagerSingleton getSingleton() {
        return singleton;
    }
    public void eat() {
        System.out.println("吃");
    }
}
5、懒汉式

先判断,再创建对象,LazySingleton

package com.woniuxy.singleton;
public class LazySingleton {
    private LazySingleton() {
    }
    //只声明,先不创建对象
    private static LazySingleton lazySingleton= null;
    /**
     * 先判断是否为空,如果为空再创建对象
     * @return
     */
    public static LazySingleton getSingleton() {
        if(lazySingleton==null) {
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }
    public void show() {
        System.out.println("hello");
    }
}
6、区别

在多线程环境下饿汉式是线程安全的,懒汉式会出现多线程的问题

四、Prototype模式

1、概念

Prototype模式即原型模式,原型模式其实是一个比较单纯的设计模式,其目的就是利用一个原型对象,快速地生成一批对象,从而避免重复的新建过程。

工作原理是:通过将一个原型对象传给一个要新建的对象,新建对象通过请求原型对象拷贝它们,然后自己来实施创建

2、适应场景

主要适用于以下情况:

  • 构建函数相对比较复杂

  • 构建函数不能被不相关的人调用

  • 一批对象中绝大部分对象的属性一致,而只有极少数不同

3、结构图

image-20230722221307108

  • Prototype:声明一个克隆自身的接口或抽象类

  • ConcretePrototype:实现一个克隆自身的操作

  • Client:让一个原型来克隆自身从而创建一个新的对象

4、代码实现
  • 创建Prototype类

package com.it.demo.prototype;
public abstract class Prototype implements Cloneable{
    public Prototype clone() throws CloneNotSupportedException {
        return (Prototype) super.clone();
    }
    public abstract void setId(String id);
}
  • 创建ConcretrPrototype

package com.it.demo.prototype;
public class Student extends Prototype {
    private String stuName;
    private float chiness;
    private float english;
    private float maths;
    private float moral;
    public String getStuName() {
        return stuName;
    }
    public void setStuName(String stuName) {
        this.stuName = stuName;
    }
    public float getChiness() {
        return chiness;
    }
    public void setChiness(float chiness) {
        this.chiness = chiness;
    }
    public float getEnglish() {
        return english;
    }
    public void setEnglish(float english) {
        this.english = english;
    }
    public float getMaths() {
        return maths;
    }
    public void setMaths(float maths) {
        this.maths = maths;
    }
    public float getMoral() {
        return moral;
    }
    public void setMoral(float moral) {
        this.moral = moral;
    }
    public String toString() {
        return "[" + this.stuName + "]" + this.chiness + "\t" + this.english
                + "\t" + this.maths + "\t" + this.moral;
    }
    public void setId(String id) {
        this.stuName = id;
    }
}
  • School

package com.it.demo.prototype;
public class School {
    public void showOther(Prototype pro) {
        try {
            Prototype other = pro.clone();
            other.setId("sun liang");
            System.out.println(other);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        Student goodStu = new Student();
        goodStu.setChiness(80);
        goodStu.setEnglish(85);
        goodStu.setMaths(90);
        goodStu.setMoral(99);
        School sch = new School();
        sch.showOther(goodStu);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值