注解
注解
@Override 类似一个标签, 作用在方法上, 表示此方法是父类中重写而来的.
注解是java中的标准, 可以用在类, 方法, 变量, 参数成员上
在编译期间, 会被编译到字节码文件中, 运行是通过反射机制获得注解内容, 进行解析.
内置注解
java中内定的注解
例如@Override
@Deprecated -标记过时方法. 如果使用该方法, 会报编译警告.
@SuppressWarnings - 指示编译器去忽略注解中声明的警告
@Functionallnterface 用于指示被修饰的接口是函数式接口.
元注解
注解的注解, 用于定义注解的注解
@Target({TYPE, FIELD, METHOD}) 定义此注解应该作用在哪些成员目标上(定义注解在哪些位置有效)
@Retention() 定义注解在什么时候生效
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
自定义一个注解
定义注解
使用注解
解析注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotNull {
//注解属性
String message() default "";
int minlength() default 0;
String lengthmessage() default "";
}
public class User {
private int num=0;
@NotNull(message = "姓名不能为空",minlength = 3,lengthmessage = "姓名长度不能小于3位")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
public class User {
private int num=0;
@NotNull(message = "姓名不能为空",minlength = 3,lengthmessage = "姓名长度不能小于3位")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
public class Test {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, Exception {
User user = new User();
user.setName("ji");
//通过反射解析User类中注解
Field[] fields = user.getClass().getDeclaredFields();//拿到类中所有的成员变量 连同私有的也可以获取
//循环所有的属性
for (Field field : fields) {
NotNull notNull = field.getAnnotation(NotNull.class);//获取属性上面 名字为NotNull注解
if (notNull != null) {
//通过属性,生成对应的get方法
Method m = user.getClass().getMethod("get" + getMethodName(field.getName()));
//调用方法 obj就是get方法的返回值
Object obj=m.invoke(user);
if (obj==null) {
System.err.println(field.getName() +notNull.message());
throw new NullPointerException(notNull.message());
}else{
if(String.valueOf(obj).length()<(notNull.minlength())){
System.err.println(field.getName() +notNull.lengthmessage());
throw new NullPointerException(notNull.lengthmessage());
}
}
}
}
}
/**
* 把一个字符串的第一个字母大写
*/
private static String getMethodName(String fildeName) throws Exception {
byte[] items = fildeName.getBytes();
items[0] = (byte) ((char) items[0] - 'a' + 'A');
return new String(items);
}
}
对象克隆
**对象克隆:**从一个已有的对象克隆出来一个新的对象,新对象可以拥有已有对象的数据
克隆时基本数据类型, 直接可以把值克隆到新对象中, 深浅克隆的判断依据主要是关联的其他对象是否被新克隆一个对象.
浅克隆:
克隆一个对象,只把关联对象的地址克隆一下,并没有创建新的关联对象.
public class Person implements Cloneable{
int num;
String name;
Address address;
public Person() {
}
public Person(int num, String name) {
this.num = num;
this.name = name;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
protected Person clone() throws CloneNotSupportedException {
Person person = (Person)super.clone();
return person;
}
@Override
public String toString() {
return "Person{" +
"num=" + num +
", name='" + name + '\'' +
", address=" + address +
'}';
}
}
public class Address {
String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address();
address.setAddress("汉中");
Person p1 = new Person(100,"jim");
p1.setAddress(address);
Person p2 =p1.clone();
p2.setName("tom");
address.setAddress("西安");
/*
此次输出的结果就是浅克隆: 因为克隆对象中关联的address对象,没有创建新的对象,只是在克隆时,把address对象的地址克隆过来了
*/
System.out.println(p1);//Person{num=100, name='jim', address=Address{address='西安'}}
System.out.println(p2);//Person{num=100, name='tom', address=Address{address='西安'}} 属于浅克隆,Address对象只复制他的地址
}
}
深克隆
克隆一个对象,把关联的对象也进行克隆,克隆出一个新的关联对象.
如何实现克隆:
public class Person implements Cloneable{
/*
重写Object类中clone()方法
*/
@Override
protected Person clone() throws CloneNotSupportedException {
Person person = (Person)super.clone();//调用父类中的clone方法,克隆对象
return person;
}
}
测试
Person p1 = new Person(100,"jim");
Person p2 = p1.clone(); //克隆一个新的对象
System.out.println(p1==p2);//false
如何实现深克隆
**方式1:**关联的对象也要实现Cloneable接口, 重写clone(),不管有多少级,只要需要深克隆,那么就需要多级克隆.
public class Person implements Cloneable{
int num;
String name;
Address address;
public Person() {
}
public Person(int num, String name) {
this.num = num;
this.name = name;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
protected Person clone() throws CloneNotSupportedException {
Person person = (Person)super.clone();
person.address = (Address)person.address.clone(); //深度复制 联同person中关联的对象也一同克隆.
return person;
}
@Override
public String toString() {
return "Person{" +
"num=" + num +
", name='" + name + '\'' +
", address=" + address +
'}';
}
}
public class Address implements Cloneable{
String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
@Override
protected Address clone() throws CloneNotSupportedException {
return (Address)super.clone();
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address();
address.setAddress("汉中");
Person p1 = new Person(100,"jim");
p1.setAddress(address);
Person p2 =p1.clone();
p2.setName("tom");
address.setAddress("西安");
System.out.println(p1);//西安
System.out.println(p2);//汉中
}
}
**方式2:**对象序列化,反序列化
对象序列化-- 将对象信息输出
对象反序列化–将输出的对象信息, 重新输入,创建新的对象
public class Person implements Serializable {
int num;
String name;
Address address;
public Person() {
}
public Person(int num, String name) {
this.num = num;
this.name = name;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
/**
* 自定义克隆方法
* @return
*/
public Person myclone() {
Person person = null;
try { // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
// 将流序列化成对象
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
person = (Person) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return person;
}
@Override
public String toString() {
return "Person{" +
"num=" + num +
", name='" + name + '\'' +
", address=" + address +
'}';
}
}
public class Address implements Serializable {
String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address();
address.setAddress("汉中");
Person p1 = new Person(100,"jim");
p1.setAddress(address);
Person p2 =p1.myclone();
p2.setName("tom");
address.setAddress("西安");
System.out.println(p1);//西安
System.out.println(p2);//汉中
}
}
设计模式
什么是设计模式
最早的设计模式是建筑领域的,后来发展到软件开发领域.
就是广大的开发前辈们,在实际的开发中把重复出现的问题的解决方案进行优化,最终得到一套最优解决方案(模版).
可以被拿来反复使用.
为什么要学习设计模式
设计模式是好的经验, 学习好的经验
可以提高我们的编程能力和设计能力
可以提高软件设计的标准化,提高开发效率
可以提高代码的复用性,可扩展性
可以更好的理解阅读源码(看懂底层复杂的源码结构)
建模语言
统一建模语言(Unified Modeling Language,UML),是一种软件设计阶段, 用于表达软件设计思路的语言(表达思路的语言)
使用类图的方式, 将类与类之间的关系进行描述
类图: 类和接口关系
类与类的关系
依赖关系
在一个类中的方法中使用到了另一个类,例如把另一个类当做参数传入.(就是一个类用到了另一个类的方法)
具有临时性,关系较弱, 方法执行完后,关系就解除了.
一般也称为: use -a
例如人类中又一个打电话的方法, 在打电话方法中要用到手机类,只是在打电话时,临时使用(手机).
关联关系
在一股类中把另一个类当做自己的成员
例如人类中关联了一个地址类
关联关系可以根据强弱关系分为: 一般关系, 聚合,组合
聚合: 学校和老师关系, 学校不存在了,老师可以独立的存在
面向对象设计原则
单一职责原则
一个类只负责一件事情, 不要让一个类做过多的事情,否则功能耦合度太高,修改一些功能时, 相互会有影响.
尽可能的降低类的粒度.
举例:用户信息类
public class UserInfo {
private int id;
private String userName;
private String gender;
private String phone;
private List<Address> addressList;//用户类中关联用户地址
}
public class Address {
private String province;//地址相关
private String city;//地址相关
private String county;//地址相关
private String detailAddress;//地址相关
}
开闭原则
在程序扩展新功能时, 尽量不要修改原有的代码,尽可能通过扩展新的类来实现功能.
/*
开闭原则案例
*/
class CarDemo{
public static void main(String[] args) {
Car bmw = new BMWCarFactory().createCar();
Car aodi = new AodiCarFactory().createCar();
}
}
//抽象工厂
abstract class CarFactory{
abstract Car createCar();
}
class BMWCarFactory extends CarFactory{
@Override
Car createCar() {
return new BMW("宝马汽车");
}
}
class AodiCarFactory extends CarFactory{
@Override
Car createCar() {
return new Aodi("奥迪汽车");
}
}
//抽象汽车
abstract class Car{
String name;
}
class BMW extends Car{
public BMW(String name) {
this.name = name;
}
}
class Aodi extends Car{
public Aodi(String name) {
this.name = name;
}
}
依赖倒置原则
上层不应该依赖于细节(实现),上层应该是抽象的, 进行功能的定义即可,变动小 (细节容易变,不稳定)
底层应该依赖于抽象,底层在抽象的指导下进行细节上的具体功能实现(在抽象的规约下,实现具体的功能)
上层应该是抽象, 底层是具体实现,底层依赖于上层的抽象
public class ComputerDemo{
public static void main(String[] args) {
IntelCPU intelCPU = new IntelCPU();
IntelMemory intelMemory = new IntelMemory();
Computer computer = new Computer(intelCPU, intelMemory);
computer.startRun();
AmdCPU amdCpu = new AmdCPU();
AmdMemory amdMemory = new AmdMemory();
Computer computer1 = new Computer(amdCpu, amdMemory);
computer1.startRun();
}
}
/*
计算机类
*/
class Computer {
private CPU cpu;
private Memory memory;
public Computer(CPU cpu, Memory memory) {
this.cpu = cpu;
this.memory = memory;
}
public void startRun(){
cpu.calculator();
memory.storage();
}
}
//抽象cpu
interface CPU{
void calculator();
}
interface Memory{
void storage();
}
//英特尔CPU
class IntelCPU implements CPU{
@Override
public void calculator(){
System.out.println("英特尔处理器运算");
}
}
//AMDCPU
class AmdCPU implements CPU{
@Override
public void calculator() {
System.out.println("AMD处理器运算");
}
}
//英特尔内存
class IntelMemory implements Memory{
@Override
public void storage(){
System.out.println("英特尔内存存储");
}
}
//AMD内存
class AmdMemory implements Memory{
@Override
public void storage() {
System.out.println("Amd内存存储");
}
}
接口隔离原则
不要把所有的功能都定义到一个接口中, 应该使用多个接口,把不同的功能定义在不同的接口中
比如: 不会把飞,跑, 游,都定义到一个接口中, 如果一个狗需要跑实现了这个接口, 就要实现飞,跑, 游,功能但是狗不需要飞和游的功能, 造成代码冗余
组合/聚合复用原则
优先使用组合/聚合(关联), 实现代码的复用, 其次才考虑继承.
在类B中,想使用类A中的某些功能,可以使用继承,但是会使得类的体系耦合性高
所以也可以使用关联关系,替代继承关系, 还可以使用更弱的依赖关系实现
组合/聚合复用原则案例1 使用依赖实现复用
*/
public class A {
public void method01() {
}
public void method02() {
}
}
class B extends A {
public void method() {
}
}
class Test {
public static void main(String[] args) {
new B().method01();
new B().method02();
}
}
/*
组合/聚合复用原则案例2 使用组合/聚合实现复用
*/
public class A {
public void method01(){
}
public void method02(){
}
}
class B{
A a;
public void setA(A a){
this.a = a;
}
public void use(){
a.method01();
a.method02();
}
}
class Test{
public static void main(String[] args) {
A a = new A();
B b = new B();
b.setA(a);
b.use();
}
}
/*
组合/聚合复用原则案例3 使用依赖实现复用
*/
public class A {
public void method01(){
}
public void method02(){
}
}
class B{
public void use(A a){//依赖
a.method01();
a.method02();
}
}
里氏替换原则
在使用继承时,如果父类中是非抽象的,那么子类继承父类后,对父类中的非抽象方法进行重写,需要注意小心(谨慎),在使用父类的时候,如果换成子类对象,由于子类对象重写的父类方法, 从而有可能实现不相同,导致结果不对(出错率高)
class Calculator {
//加法
public int add(int a,int b){
return a+b;
}
}
/*
超级计算器子类
*/
class SuperCalculator extends Calculator{
//重写了父类加法
@Override
public int add(int a, int b) {
return a+b-5;//只是表示子类继承父类后,重写了父类的方法,对方法进行了修改
}
}
public class CalculatorDemo{
public static void main(String[] args) {
Calculator p = new Calculator();
int r1 = p.add(10,5);
System.out.println("r1="+r1);//15
Calculator c = new SuperCalculator();
int r2 = c.add(10,5);
System.out.println("r2="+r2);//10
}
}
实际开发, 父类的功能尽可能定义为抽象
/*
里氏替换原则演示案例
计算器父类
*/
abstract class Calculator {
//加法 将被子类可能重写的方法定义抽象方法
public abstract int add(int a,int b);
}
/*
超级计算器子类
*/
class SuperCalculator extends Calculator {
//重写了父类加法
@Override
public int add(int a, int b) {
return a+b-5;
}
}
public class CalculatorDemo{
public static void main(String[] args) {
Calculator p = new SuperCalculator();
SuperCalculator c = new SuperCalculator();
int r1 = p.add(10,5);
System.out.println("r1="+r1);
}
}
迪米特原则
只和朋友交谈,不和陌生人说话,
两个类之间如果没有直接联系,那么就不会相互之间调用,可以通过一个第三方调用
例如:
明星和粉丝,公司之间的交流, 可以通过经纪人来完成
public class DemeterDemo {
public static void main(String[] args) {
Star star = new Star("C罗");
Fans fans = new Fans("李磊");
Company company = new Company("皇家马德里");
Agent agent = new Agent("门德斯",star,fans,company);
agent.meeting();
agent.business();
}
}
//经纪人
class Agent{
String name;
Star star;
Fans fans;
Company company;
public Agent(String name, Star star, Fans fans, Company company) {
this.name = name;
this.star = star;
this.fans = fans;
this.company = company;
}
//粉丝见面会
public void meeting(){
System.out.println("粉丝 "+ fans.name+"与明星 "+ star.name +"见面了");
}
//洽谈业务
public void business(){
System.out.println(company.name +" 公司 正在与 明星 "+star.name +" 洽谈业务");
}
}
//明星
class Star{
String name;
public Star(String name) {
this.name = name;
}
}
//粉丝
class Fans{
String name;
public Fans(String name) {
this.name = name;
}
}
//公司
class Company{
String name;
public Company(String name) {
this.name = name;
}
}
面向对象语言设计模式总共有23种
分为3大类:
1.创建型模式
单例模式
单例模式
解决一个类在一个项目中,只能创建一个对象.
单例模式有两种写法:
1.饿汉式(急切式)单例
在类加载时,直接把唯一的对象创建好,调用方法时, 直接返回即可, 不存在线程安全问题.
只是在类加载之初可能用不到,生命周期长.
public class Window {
/*
在类加载时, 只创建一个对象
*/
private static Window window = new Window();
/*
构造方法私有化, 在类之外就访问不到构造方法
*/
private Window() {
}
/*
向外界提供一个方法, 获得new出来的唯一对象
*/
public static Window getInstance(){
return window;
}
public static void test(){
}
}
// 饿汉式
public class test {
public static void main(String[] args) {
// new Window();
// new Window();
// new Window();
//在类加载的时候就会创建对象, 对外提供获取对象的方法
Window.test();
System.out.println(Window.getInstance());
System.out.println(Window.getInstance());
System.out.println(Window.getInstance());
System.out.println(Window.getInstance());
}
}
2.懒汉式单例
在类加载时,不着急创建对象,在第一次调用方法时, 才会创建唯一的对象.
这种写法存在安全问题.
public class Window {
private static Window window = null;
private Window() {
}
public synchronized static Window getInstance() {
//后面进来的线程拿到的window肯定都不为空,可以多个线程进来拿取对象,并行执行(效率提高了),开头是并发的
if (window == null){//这个只是为了第一次需要这个对象的时候创建对象
synchronized (Window.class){
if (window == null){//只有一个线程进来进行创建对象
window = new Window();
}
}
}
return window;
}
/*
为方法加锁, 但是这种效率低
window对象创建好后每次线程进入的时候只能一个线程进入效率低
*/
/*public synchronized static Window getInstance() {
if (window == null){
window = new Window();
}
return window;
}
*/
/*
//此种写法存在线程安全问题
//如果有两个线程同时进入if代码块就会创建2个对象
public static Window getInstance() {
if (window == null){
window = new Window();
}
return window;
}
*/
public static void test() {
}
}
public class Test {
public static void main(String[] args) {
Window.test();
System.out.println(Window.getInstance());
System.out.println(Window.getInstance());
System.out.println(Window.getInstance());
}
}
双重检索+volatile
public class Window {
/*
volatile修饰的变量, 不同线程中修改了立即可见, 禁止指令的重排序
Window window = new Window();
上面这行代码, 从指令的角度上来讲, 可以有三条指令
1.new 申请内存空间
2. 初始化对象
3. 把对象地址赋给左边的引用变量 如果正常按这个顺序执行没有问题的.
但是在执行的过程中, 有可能在执行第2步执行, 同时把第3步提前执行了, 此时对象还可能没有初始化完成
其他线程可能会拿到一个没有初始化完成的对象(半成品对象)
所以单例成员需要使用volatile修饰, 禁止指令的重排序
*/
private volatile static Window window=null;
private Window(){
}
/*
双重检索,效率提高
*/
public static Window getInstance(){
if(window==null){
synchronized (Window.class){
if(window==null){
window = new Window();
}
}
}
return window;
}
工厂模式
把对象的创建和使用分离, 提供一个工厂类专门负责对象的创建,
一个工程可以创建一类产品
简单工厂
简单工厂并不是设计模式,这只是对工厂模式的引入(只是为了引入工厂模式)
工厂角色 : 全能的工厂 (啥类型的产品都可以生产)
抽象产品(接口/抽象类): 用来组织关系
具体产品: 需要继承/实现抽象产品
客户端---->工厂----->对象
客户端调用工厂方法来获取工厂中的对象,实现对象创建和调用的分离
public interface Car {
void run();
}
/*
工厂
*/
public class CarFactory {
public static Car createCar(String name){
if(name.equals("aodi")){
return new Aodi();
}
if(name.equals("bmw")){
return new Bmw();
}
if(name.equals("dazhong")){
return new DaZhong();
}
return null;
}
}
/*
具体产品
*/
public class Aodi implements Car{
@Override
public void run() {
System.out.println("奥迪汽车行驶");
}
}
/*
具体产品
*/
public class Bmw implements Car{
@Override
public void run() {
System.out.println("宝马汽车行驶");
}
}
public class DaZhong implements Car{
@Override
public void run() {
System.out.println("大众汽车行驶");
}
}
/*
工厂
*/
public class CarFactory {
public static Car createCar(String name){
if(name.equals("aodi")){
return new Aodi();
}
if(name.equals("bmw")){
return new Bmw();
}
if(name.equals("dazhong")){
return new DaZhong();
}
return null;
}
}
public class Test {
public static void main(String[] args) {
//Aodi aodi1 = new Aodi(); 自己new对象
Car bmw = CarFactory.createCar("bmw");
Car aodi = CarFactory.createCar("aodi");
bmw.run();
aodi.run();
}
}
工厂方法
首先将工厂也进行抽象, 一个抽象工负责一类产品, 定义工厂规则.
为每一种具体的产品都提供一个专门的工厂生产.
这样后来添加新产品时, 不需要修改原来的工厂类, 直接扩展一个新的工厂即可.
遵守了开闭原则
工厂方法中,一个工厂只负责一类产品生产,但是产品多了以后, 会增加代码量(弊端)
```java
public interface Car {
void run();
}
```
public interface CarFactory {
Car createCar();
}
public class Aodi implements Car {
@Override
public void run() {
System.out.println("奥迪汽车行驶");
}
}
public class AodiFactory implements CarFactory {
@Override
public Car createCar() {
return new Aodi();
}
}
public class Bc implements Car{
@Override
public void run() {
System.out.println("奔驰汽车形式");
}
}
public class BCFactory implements CarFactory{
@Override
public Car createCar() {
return new Bc();
}
}
public class Test {
public static void main(String[] args) {
CarFactory aodicarFactory = new AodiFactory();
Car aodi = aodicarFactory.createCar();
aodi.run();
CarFactory bmwcarFactory = new BmwFactory();
Car bmw = bmwcarFactory.createCar();
bmw.run();
Car car = new BCFactory().createCar();
car.run();
}
}
抽象工厂
抽象工厂模式中, 工厂不再只负责单一产品的生产,
而是工厂可以生产同一家公司(家族),在一个工厂中可以生产同一家(家族)的多个产品(例如: 小米家族手机, 小米家族汽车),这样就不需要太过冗余的工厂类
public interface Car {
void run();
}
public interface Phone {
void call();
}
public interface AbstractFactory {
Car getCar();
Phone getPhone();
}
public class AodiCar implements Car{
@Override
public void run() {
System.out.println("奥迪汽车行驶");
}
}
public class AodiPhone implements Phone {
@Override
public void call() {
System.out.println("奥迪手机打电话");
}
}
public class AodiFactory implements AbstractFactory {
@Override
public Car getCar() {
return new AodiCar();
}
@Override
public Phone getPhone() {
return new AodiPhone();
}
}
public class BmwCar implements Car{
@Override
public void run() {
System.out.println("宝马汽车行驶");
}
}
public class BmwPhone implements Phone {
@Override
public void call() {
System.out.println("宝马手机打电话");
}
}
public class BmwFactory implements AbstractFactory {
@Override
public Car getCar() {
return new BmwCar();
}
@Override
public Phone getPhone() {
return new BmwPhone();
}
}
public class Test {
public static void main(String[] args) {
AbstractFactory aodiFactory = new AodiFactory();
Car aodiCar = aodiFactory.getCar();
Phone aodiphone = aodiFactory.getPhone();
aodiCar.run();
aodiphone.call();
AbstractFactory bmwFactory = new BmwFactory();
Car bmwCar = bmwFactory.getCar();
Phone bmwPhone = bmwFactory.getPhone();
bmwCar.run();
bmwPhone.call();
}
}
原型模式
如果我们需要创建多个对象时,每次new+构造方法执行速度慢(原因:每次都要执行构造方法中的逻辑(耗费时间))
那么我们就可以先闯几年一个对象,在已有对象的基础上进行对象克隆(拷贝),提高创建对象的效率
例如: 要手写5份简历, 太浪费时间了
写好一份后复印4次, 就可以得到多个对象, 效率高.
对象克隆
如何实现对象克隆:
1.类实现Cloneable接口 重写Object类中的clone()
2.序列化, 反序列化
注意深克隆和浅克隆
2.结构型模式
代理模式
案例: 汽车厂只管造汽车, 不直接把汽车卖给个人客户.
把卖汽车交给代理商(4S店)卖给普通个人客户
代理商就可以在中间向客户介绍汽车信息, 卖完汽车后, 还可以帮助客户办理手续
代理商在中间增加了一些额外的功能(方法增强).
汽车厂自己卖车, 汽车厂不仅要卖车,还要介绍汽车, 还要帮助办理手续,都要自己完成, 压力大
sell(){
介绍汽车
汽车厂卖车
办理手续
}
sell(){
汽车厂卖车
}
//代理
sell(){
介绍汽车
sell();
办理手续
}
代理模式: 为目标对象(汽车厂)提供一个代理对象,不让目标对象之间与客户进行交互.
而是通过代理对象与客户间隔交互
3.行为模式
好处: 代理对象可以为目标对象提供保护
代理对象可以为目标对象扩展功能
代理对象可以降低目标对象和客户之间的耦合度
(汽车厂和客户没有粘连)(转心干一件事,干多个事,不能精通)
代码结构
抽象主题(接口/抽象类): 汽车厂和代理做同一件事, 所以进行抽象, 都是卖汽车
真实主题(目标对象/汽车厂)
代理对象
代理模式又分为静态代理和动态代理
静态代理:
静态代理在一些简单的场景下可以使用,
因为一个代理类, 只能为那些实现了某个接口的目标类实现代理(跟代理类的接口一样),
如果要为其他接口的目标类实现代理,就必须重新创建新的代理类
在复杂场景下不太合适了
(静态代理只能代理跟代理类实现一样接口的实现类(目标类) ,如果要代理其他的接口的实现类, 那必须重新创建一个代理类,跟这个类实现了同一个接口)
/*
抽象操作定义 卖东西
*/
public interface Sell {
void sell();
}
public class CarFactoryImpl implements Sell {
@Override
public void sell() {
//System.out.println("介绍汽车");
System.out.println("汽车厂卖汽车"); //汽车厂真实的只是卖汽车这个动作.
//System.out.println("办理手续");
}
}
public class PhoneFactory implements Sell{
@Override
public void sell() {
System.out.println("手机厂卖手机");
}
}
/*
静态代理,实际中很少使用静态代理,因为其代理类实现的接口必须与目标类实现接口一致,扩展起来就比较麻烦
*/
public class StaticProxy implements Sell{
Sell sell;//接收真实主题(目标对象)
Create create;
public StaticProxy(Sell sell) {
this.sell = sell;//接收一个真实主题
}
public StaticProxy(Create create){
this.create = create;
}
@Override
public void sell() {
System.out.println("产品介绍");
try {
sell.sell();//真实主题
}catch (Exception e){
System.out.println("");
}
System.out.println("办理手续");
}
}
public class Test {
public static void main(String[] args) {
// CarFactoryImpl carFactory1 = new CarFactoryImpl();
/// carFactory1.sell();
Sell carFactory = new CarFactoryImpl();//真实主题
//创建汽车厂代理对象
StaticProxy staticProxy = new StaticProxy(carFactory);
staticProxy.sell();//客户看似调用的是代理方法
PhoneFactory phoneFactory = new PhoneFactory();
StaticProxy staticProxy1 = new StaticProxy(phoneFactory);
staticProxy1.sell();
}
}
动态代理
动态代理可以在运行时,根据不同的类生成代理对象,可以为所有的任意类提供代理
动态代理分为:
jdk代理
jdk代理是通过反射机制实现的, 在运行时,可以动态的获得目标类的接口,获得接口中的方法信息,从而为目标类生成代理对象
只要写一个代理对象生成器, 就可以为所有的类生成代理对象,
但是jdk代理方式实现时, 目标类必须要实现一个接口, 不实现接口,就不能使用jdk代理
return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
/*
抽象操作定义 卖东西
*/
public interface SellCar {
void sell();
}
/*
汽车厂
*/
public class CarFactoryImpl implements SellCar {
@Override
public void sell() {
System.out.println("汽车厂卖汽车");
}
}
public class Test {
public static void main(String[] args) {
CarFactoryImpl carFactory = new CarFactoryImpl();//创建真实主题
DynamicProxy dtproxy = new DynamicProxy(carFactory);//创建代理对象生成器
//这才是真正的创建动态代理对象 获取目标类所实现的接口
SellCar carfactory = (SellCar)dtproxy.getProxy();
carfactory.sell();//使用代理对象调用接口中的方法,获取当前调用的方法,最终调用invoke方法
}
}
cglib代理
cglib代理师spring中的一个实现, 使用cglib代理,目标类可以不用实现接口
采用底层的字节码生成技术,为我们的目标类生成子类对象, 采用方法拦截技术,在调用方法时, 会进入到拦截中, 获得所调用的父类方法即可
目标类不能是final修饰的, 目标类中的方法如果为final, static修饰 就不能进行增强
//具体主题
public class CarFactoryImpl {
public void sell() {
System.out.println("汽车厂卖汽车");
}
}
/*
* 动态代理生成器类
*/
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 Test {
public static void main(String[] args) {
CGLibProxy proxy = new CGLibProxy();
CarFactoryImpl carFactory = (CarFactoryImpl) proxy.getProxy(CarFactoryImpl.class);
carFactory.sell();
}
}
spring中两种代理方式都进行了实现, 可以根据不同的情况进行自动选择
1.单例对象, 没有实现接口的类,可以使用cglib代理
2.原型对象(创建多个对象) ,实现接口的类,可以使用jdk代理
3.行为模式
模版方法模式
模版方法模式:
将程序中一些固定流程的步骤进行提取(公共步骤进行抽取)(银行取款, 存款, 转账,抽号, 排队, 操作,评分),
在抽象父类中提供一个模版方法(固定流程的步骤),在模版方法中,按顺序把固定步骤(固定流程(就是模版))的方法进行调用(执行)
其中有一些方法时公共的都一样,在父类中进行实现即可,
其中还有一些方法实现不同, 定义为抽象的, 就需要创建不同的子类来继承重写(进行不同的实现),
使用时,子类对象只要调用模版方法, 相同的功能调用抽象父类中的方法,不同的调用子类中重写的方法.
符合开闭原则,增加功能时,只需要扩展新的类即可
public abstract class AbstractBank {
//办理业务方法 -- 模板方法
public void handle(){
this.offerNumber();//抽号
this.lineup();//排队
this.business();//具体的操作
this.score();//平分
}
//抽号
public void offerNumber(){
System.out.println("抽号");
}
//排队
public void lineup(){
System.out.println("排队");
}
//办理具体业务--抽象方法,由具体子类实现
public abstract void business();
//评分
public void score(){
System.out.println("评分");
}
//其他
}
/*
存钱业务
*/
public class StoreBusiness extends AbstractBank {
//办理的具体业务
public void business() {
System.out.println("我要存钱");
}
}
/*
转账业务类
*/
public class TransferBusiness extends AbstractBank{
//转账
public void business() {
System.out.println("我要转账");
}
}
public class Test {
public static void main(String[] args) {
StoreBusiness storeBusiness = new StoreBusiness();
storeBusiness.handle();
System.out.println("===================================");
TransferBusiness transferBusiness = new TransferBusiness();
transferBusiness.handle();
}
}
策略模式
可以将条件选择策略的实现,转为策略对象,传入哪个策略就使用哪个策略
不同的情况封装到不同的类里面,使用哪个就传入哪个策略,避免写大量的不优雅的if -else条件判断
不同的策略不同的对象,
public interface Strategy {//抽象策略类
void show();
}
/*
为春节准备的促销活动A
*/
public class StrategyA implements Strategy {//具体策略类
public void show() {
System.out.println("春节活动: 买一送一");
}
}
/*
为中秋准备的促销活动B
*/
public class StrategyB implements Strategy {//具体策略类
public void show() {
System.out.println("中秋活动: 满200元减50元");
}
}
/*
为国庆准备的促销活动C
*/
public class StrategyC implements Strategy {//具体策略类
public void show() {
System.out.println("国庆活动:满1000元加一元换购任意200元以下商品");
}
}
public class Test {
public static void main(String[] args) {
/*
if(a==1){
if(){
if(){
}
}
}
if(a==2){
if(){
if(){
}
}
}
if(a==3){
}*/
SalesMan salesManA = new SalesMan(new StrategyA());
salesManA.salesManShow();
SalesMan salesManB = new SalesMan(new StrategyB());
salesManB.salesManShow();
SalesMan salesManC = new SalesMan(new StrategyC());
salesManC.salesManShow();
}
}