大家好,我是Morning,在CSDN写文,分享一些Java基础知识,一些自己认为在学习过程中比较重要的东西,致力于帮助初学者入门,希望可以帮助你进步。感兴趣的欢迎关注博主,和博主一起学习Java知识。大家还可以去专栏查看之前的文章,希望未来能和大家共同探讨技术。
原型模式
以某个对象为原型,克隆出新的对象。
在创建一个复杂对象时,如果我们已经有这个对象了,那么我们不需要再次经过一些复杂的步骤创建对象,直接将已有的对象进行克隆就好了。提高创建对象的效率。
给原型对象所对应的类实现Object类中的clone方法,如果不对这个方法进行修改,那么这个克隆是一个浅克隆,就是克隆出来的对象中的引用类型属性的引用指向原型对象中属性所指向的内存地址,这样的话,当你修改一个对象中的值时,另一个对象中也会发生变化,这样的现象不是我们所理想的,那么我们就要修改clone中的代码,将类(需要克隆的类)中的引用类型属性也进行克隆,这样就是一个深克隆。
代码实现:
//需要克隆的类
public class Dome1 implements Cloneable{
int a;
StringBuffer s ;
public Dome1(int a, StringBuffer s) {
this.a = a;
this.s = s;
}
//浅克隆
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public StringBuffer getS() {
return s;
}
public void setS(StringBuffer s) {
this.s = s;
}
@Override
public String toString() {
return "dome1{" +
"a=" + a +
", s=" + s +
'}';
}
}
//测试类
public class text {
public static void main(String[] args) throws CloneNotSupportedException {
StringBuffer stringBuffer = new StringBuffer("abc");
Dome1 dome1 = new Dome1(1,stringBuffer);
Dome1 dome2 = (Dome1) dome1.clone();
System.out.println("dome1 => "+dome1);
System.out.println("dome2 => "+dome2);
System.out.println("===================");
stringBuffer.append("d");
dome1.setA(2);
System.out.println("dome1 => "+dome1);
System.out.println("dome2 => "+dome2);
}
}
上面测试类执行结果如下:
修改clone方法后:
//深克隆
@Override
protected Object clone() throws CloneNotSupportedException {
Object object = super.clone();
Dome1 dome = (Dome1)object;
StringBuffer stringBuffer = new StringBuffer(this.s);
//给它一个新的指向
dome.s = stringBuffer;
return object;
}
测试类运行结果如下:
代理模式
一个类不直接去访问另一个类,有一个代理类来帮助它操作,例如:现在的社会,一般来说,你要租房的时候,不会去直接找房东,而是通过一个中介,房东也可以省好多的事情(宣传,沟通),这就是一个代理的思想。
构成代理模式的成员:
- 一个抽象角色,一般就是一个抽象类或者接口,在上述的例子中,租房这件事情就是一个抽象角色,中介和房东都有出租房屋的需求,所以中介可以代理房东。
- 真实角色:被代理的类
- 代理角色:代理真实角色的类
- 客户角色:访问代理对象的类
静态代理
代码:
//抽象角色
public interface RentHouse {
public void rentHouse();
}
//真实角色
public class HouseOwner implements RentHouse{
@Override
public void rentHouse() {
System.out.println("我是房东!!");
}
}
//代理角色
public class Proxy implements RentHouse{
private HouseOwner houseOwner;
public Proxy() {
}
public Proxy(HouseOwner houseOwner) {
this.houseOwner = houseOwner;
}
@Override
public void rentHouse() {
//可以在这里进行一些额外的操作
houseOwner.rentHouse();
}
}
//客户角色
public class Tenant {
public static void main(String[] args) {
HouseOwner houseOwner = new HouseOwner();
//不用代理
//houseOwner.rentHouse();
//使用代理
Proxy proxy = new Proxy(houseOwner);
proxy.rentHouse();
}
}
好处:
- 真实角色专注与自己的业务,不需要关注一些公共的业务
- 代理角色可以帮助真实角色处理一些公共业务,这使真实角色更加纯粹。实现了业务的分工。
- 方便扩展公共业务。
缺点:
- 一个真实角色就需要一个代理角色。代码量翻倍。开发效率低
- 代理类和被代理类必须实现同一个接口,可扩展性低。
动态代理
动态代理的代理类是动态生成的,不是我们直接写好的。
JDK代理
实现方式是拦截器+反射
代码实现:
/*InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的
调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。*/
//MyProxy类就是处理程序
public class MyProxy implements InvocationHandler {
//真实对象,接收任何的目标类对象
Object object;
public MyProxy(Object object) {
this.object = object;
}
/*
方法三个参数:调用方法的实例,目标类中要调用的方法,方法中的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("之前要添加的业务");
method.invoke(object);
System.out.println("之后要添加的业务");
return proxy;
}
}
//被代理类实现的接口
public interface Huawei {
void sell();
}
//被代理类
public class HuaweiPhone implements Huawei {
@Override
public void sell() {
System.out.println("卖华为手机!");
}
}
//测试方法
public static void main(String[] args) {
HuaweiPhone phone = new HuaweiPhone();
//自己创建的代理类对象
InvocationHandler myProxy = new MyProxy(phone);
//创建真正的动态代理对象
Huawei huawei = (Huawei) Proxy.newProxyInstance
//对生成的代理类进行加载的对象 给代理对象提供的接口 最终调用方法的对象
(MyProxy.class.getClassLoader(), HuaweiPhone.class.getInterfaces(), myProxy);
//代理对象调用接口中的方法,最终调用MyProxy中的invoke方法
huawei.sell();
}
这样就可以实现,我们不需要手动的让代理类和被代理类实现同一个接口这一目的。
当出现新的需要代理的对象时:
public interface Xiaomi {
void sell();
}
public class XiaomiPhone implements Xiaomi {
@Override
public void sell() {
System.out.println("卖小米手机!");
}
}
//使用时只需做出如下修改
public static void main(String[] args) {
XiaomiPhone phone = new XiaomiPhone();
InvocationHandler myProxy = new MyProxy(phone);
Xiaomi xiaomi = (Xiaomi) Proxy.newProxyInstance
(MyProxy.class.getClassLoader(), XiaomiPhone.class.getInterfaces(), myProxy);
xiaomi.sell();
}
CGLIB代理
实现方式是基于ASM(Java字节码操作框架)实现的
//代理类
public class CGLibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class<?> clazz) {
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
/*
* 拦截所有目标类方法的调用
* 参数:
* obj 目标实例对象
* method 目标方法的反射对象
* args 方法的参数
* proxy 代理类的实例
*/
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("之前添加的业务");
Object obj1 = proxy.invokeSuper(obj, args);
System.out.println("之后添加的业务");
return obj1;
}
}
//目标类
public class TargetClass {
public void method() {
System.out.println("目标类方法");
}
}
public static void main(String[] args) {
CGLibProxy proxy = new CGLibProxy();
TargetClass target = (TargetClass) proxy.getProxy(TargetClass.class);
target.method();
}
CGLIB 代理,是通过 Enhancer 对象把动态代理类设置为被代理类的子类来实现动态代理的。所以目标类(被代理类)不能被final 修饰,如果被 final 修饰,动态代理类的构建就会出错。
JDK代理是由java提供的,每一次jdk的升级后,JDK代理效率都会提升。在jdk8之前,大量调用时CGLIB代理效率高。在jdk8之后,JDK效率高于CGLIB代理。spring中,俩种代理方式都有,当Bean实现接口时,就是用JDK代理;Bean没有实现接口时,使用CGLIB代理。
oop的七大原则
在结尾给大家提一下面向对象的七大原则:
开闭原则
对扩展开放,对修改关闭,就是说当需求发生改变时,我们尽量不去对原有的模块进行修改,而是去扩展,扩展的内容不会对原有的模块造成影响。
里氏替换原则
保证父类的功能在子类中仍然成立,继承父类时尽量的去添加新的功能,不去该父类的功能。多态运用此原则
依赖倒置原则
面向接口编程。
单一职责原则
一个方法就只有一个职责,不要把这个方法搞得很乱,很复杂
接口隔离原则
把接口细化,为每个类建立他们需要的专用接口。
迪米特法则
只与朋友交谈,不和陌生人说话(A类和C类要建立联系时,通过一个共同已经有联系的B来进行)。降低类之间的耦合性,提高模块之间的独立性。缺点就是产生中介类,增加系统的复杂性。
合成复用原则
尽量先使用组合或者聚合等关联关系来实现,然后才考虑使用继承的关系来实现。继承就有这个原则
好了,本次的分享到这里就结束了。感谢您的阅读。博主会在日后给大家分享其他的Java知识,和大家一起探讨,有兴趣的可以关注博主。文中有什么不当的地方,欢迎大家在评论区指出,大家一起探讨、学习。🤞🤞🤞