Java的进阶学习
注解
Java注解(Annotation)又称为Java标注,是JDK5.0引入的一种注释机制,可以用在类、方法、变量、参数成员上,在编译期间,会被编译到字节码文件中,运行时通过反射机制获得注解内容进行解析。
内置注解
Java语言已经定义的注解。
例如:
@Override:检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没该方法会直接报编译错误。
@Deprecated:标记过时方法。如果使用该方法,会报编译警告。
@SuppressWarnings:指示编译器去忽略注解中声明的警告。
@FunctionalInterface:用于指示被修饰的接口是函数式接口
元注解
注解的注释
元注解是java API提供的,是用于修饰注解的注解,通常用在注解的定义上。
@Retention - 标识这个注解怎么保存,是只在代码中,还是编入 class 文件中,定义注解在什么时候生效。
或者是在运行时可以通过反射访问。
@Documented - 标记这些注解是否包含在用户文档中。
@Target - 标记这个注解应该是哪种 Java 成员。
@Inherited - 标记这个注解是继承于哪个注解类(默认注解并没有继承于任何子类)
@Repeatable - 标识某注解可以在同一个声明上使用多次。
对象克隆
对象克隆,从已有的对象克隆出来一个新的对象,新的对象可以拥有已有的对象中的数据。
克隆对象时,基本数据类型可以直接把值克隆到新的对象中,深浅克隆的判断依据最主要是关联的其他对象是否被新克隆出一个对象来。
如何实现克隆
1、浅克隆
克隆一个对象只把关联对象的地址克隆了一下,并没又创建新的关联对象。
一种方式:实现Cloneable接口重写clone()方法
public class Person implements Cloneable{/*** * 重写object类中的clone* @return* @throws CloneNotSupportedException*/@Overrideprotected Person clone() throws CloneNotSupportedException {Person person = (Person)super.clone();return person;}
}
2、深克隆
克隆对象,把关联的对象也进行克隆,克隆出一个新的关联对象。
两种克隆方式:
1、关联的对象也要实现Cloneable接口,重写clone()方法,不管多少级,只要需要深克隆那么就需要多少级克隆。
2、对象序列化和反序列化
对象序列化:将对象信息输出
对象反序列化:将输出的对象信息,重新输入,创建新的对象
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;}
在这里总结Object中的方法:
equals():==比较地址,但是一般类中都重写了该方法,用来比较对象内容是否相等。
toString()、hashCode()获得对象内存,一般类进行了重写,用来获得对象内容计算出的哈希值、wait()、notify()、notifyAll()、getClass()、clone()、finalize()对象被回收前只调用一次。
设计模式
什么是设计模式:
最早的设计模式是建筑领域的,后来发展到软件开发领域。
广大的开发前辈们,在实际的开发中把重复出现的问题的解决方案进行优化。最终得到一套最优的解决方案可以拿来反复使用。
学习设计模式的原因
1、可以提高程序员的思维能力,编程能力和设计能力。
2、使程序设计更加标准,是软件开发效率大大提升。
3、使设计的代码可重复性提高、可读性强、可靠性高、灵活性好、可维护性强。
建模语言
统一建模语言(UML)是一种软件设计阶段思路的语言,使用类图的方式,将类与类之间的关系进行描述。
类图:类和接口关系。
类与类的关系
1、依赖关系
在一个类中的方法中使用到了另一个类。例如把另一个类当作参数传入,具有临时性,关系较弱,方法执行完之后,就关系解除。
2、关联关系
在一个类中把另一个类当作自己的成员。例如人类类(Person)中关联一个地址类(Address)
关联关系可以根据强弱关系分为:一般关联、聚合、组合。
聚合:就像学校和老师,学校不存在了,老师可以独立的存在
组合:头和嘴,如果头不存在了,嘴也没有存在的意义
3、继承关系
类之间的继承关系
4、实现关系
类和接口的关系
面向对象设计原则
在面向对象的设计过程中,首先需要考虑的是如何提高一个软件系统的可维护性和可复用性。这时,遵从面向对象的设计原则,可以在进行设计方案是减少错误设计的产生,从不同的角度提升一个软件结构的设计水平。
1、单一职责原则
一个类只负责一件事情,不要让类做过多的事情,否则类内部的功能耦合度太高,修改一些功能时,会互相影响。
2、开闭原则
在程序扩展新的功能时,尽量不要修改原有的代码,尽可能扩展新的类来实现功能,对外扩展开放,对内修改封闭
3、依赖倒置原则
上层不应该依赖细节(实现),上层应该是抽象的,进行功能上的定义即可,变动小,底层应该依赖于抽象,底层在抽象的指导下进行细节上的具体功能实现,上层是抽象的,底层是实现的细节
降低了类与类之间的耦合度。
4、接口隔离原则
不要把所有的功能定义到一个接口中,应该使用多个接口,把不同的功能定义在不同的接口中
5、组合/聚合复用原则
优先考虑使用组合/聚合(关联),实现代码的复用,其次考虑继承。
例如:在类B中想使用类A中的某一功能,可以使用继承,但是类的耦合度高,所有可以使用关联关系代替继承,也可以使用更弱的依赖关系实现。
6、里氏替换原则
在使用继承时,如果父类中的方法是非抽象的,那么子类继承父类后,对父类非抽象方法进行重写时需要注意小心,在使用父类的时候,如果换成子类对象,由于子类重写了父类的方法,从而导致有可能实现不同,导致结构不同。
在实际的开放中,父类中的方法尽可能定义为抽象的
7、迪米特原则
自己和朋友交谈,不和陌生人说话,两个类之间如果没有直接关系,那么就不要互相调用,可以通过一个第三方调用。
例如:明星和粉丝,公司直接的交流,可以通过经纪人来完成。
面向对象的设计模式
设计模式一共有23种,分为三大类。下面对常见的设计模式进行介绍
1、创建型模式
1、单例模式:
一个类在一个项目中只能创建一个对象
JDK中的源码Runtime类就是一个单例类,利用Runtime类可以启动新的进程或进行相关运行环境的操作。比如:取得内存空间以及释放垃圾空间。
Runtime类属于典型的单例设计
单例模式有两种写法:
1、饿汉式(急切式)单例
在类加载时直接把单例对象创建好,调用方法时就已经创建了的对象即可,不存在线程安全问题,只是在类加载之处可能还用不到,但是就已经创建出来,生命周期长。
2、懒汉式单例
在类加载时不着急创捷对象,在第一次调用方法时创建唯一的对象,这种写法存在线程安全问题。
懒汉式单例双重检索+volatile
volatile是为了解决乱序性,禁止指令重排序。
private volatile static Window window=null;
/**
Window window= new Window();
这行代码,从指令上来讲,可以有三条指令
1.new 申请内存空间
2.初始化对象
3.把对象地址赋给左边的变量,如果正常按这个顺序执行是没有问题的,但是在执行第二步的过程中把第三步提前行了,第二步还有可能没有初始化完成。其他线程可能会拿到没有初始化完成的对象。
所以单例成员需要使用volatile修饰,禁止指令的 重排序。
*/
public static Window getInstance(){if(window==null){synchronized (Window.class){if(window==null){window = new Window();}}}return window;
}
2、 工厂模式
把对象的创建和使用分离,提供一个工厂类,专门负责对象的创建,一个工厂可以创建一类的产品。
简单工厂模式:
简单工厂并不是 23 种设计模式之一,只是对工厂模式的引入。
角色分类:
1、工厂角色
2、抽象产品(接口/抽象类):用来组织关系的
3、具体产品角色:需要继承或实现抽象产品
客户端-->工厂-->对象
简单工厂违背了开闭原则,在增加一个新的产品时需要修改工厂代码
工厂方法模式:
首先将工厂也进行了抽象,一个抽象工厂负责生产一类产品,定义规则,为每一种具体的产品都提供一专门的工厂负责生产。
这样的好处是:添加产品时,不需要修改原来的工厂,直接扩展一个新的工厂即可。遵守了开闭原则。
工厂方法中,一个工厂只负责一类产品,但是产品增多了以后,会增加代码量(弊端)
角色:
抽象工厂角色
具体工厂角色
抽象产品角色
具体产品角色
3、抽象工厂
抽象工厂模式中,工厂不在负责单一的产品生产,而是工厂可以生产同一家公司的产品,在一个工厂中可以可以生产同一家公司的多个产品,这样的话就不需要太多冗余的工厂类。
3、原型模式
如果我们需要创建多个对象时,每次new+构造方法执行速度太慢(构造方法中的逻辑每次都执行一次),那么我们就可以先创建一个对象,在已有的对象基础上方进行对象克隆(复制),提高创建对象的效率。
例如:
手写5分简历,太浪费时间,写好一份,复印4次,就可以得到多个,效率高在这里我们就要提到克隆对象深浅克隆
2、结构型模式
1、代理模式
案例:汽车厂只管造汽车,不直接把汽车出售给客户,而是通过4s店进行出售,4s就是这个代理商。代理商就可以在中间向客户介绍汽车信息,买完汽车之后,还可以帮助客户办理手续。
代理模式:为目标对象提供一个代理对象不让目标对象直接与客户进行交互,而是通过代理对象。
代理商在中间增加了一些额外的功能
好处:
1、代理对象可以为目标对象提供保护
2、代理对象可以为目标提供扩展功能
3、代理对象可以将客户端与目标对象分离,在一定的程度上,降低了系统的耦合度。
代码结构:
抽象主题类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
真实主题类:目标对象,实现了抽象主题中的具体业务,是代理对象 所代表的真实对象,是最终要引用的对象。
代理类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
代理类又分为静态代理和动态代理
静态代理:
在一些简单场景下可以使用,因为一个代理类只能为那些实现某个接口的目标类代理。
如果为实现了其他的接口的目标类代理就必须重新创建一个新的代理类,在复杂的场景下就不太适合了。
优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
缺点:一个代理类只能代理一个接口,工作量太大,代理类是运行前编码已 经完成的;必须先有接口,再有代理;接口一旦发生变量,代理类也要修改。
动态代理:
可以在运行时,根据不同的类生产不同类的代理对象,可以为所有的任意类创建代理对象。
动态代理又可以分为:jdk代理和Cglib代理
jdk代理:
是通过Java的反射机制实现的,在运行时可以动态的获得目标类的接口中的方法信息,从而为目标类生产代理对象。
只要写一个代理对象生产器,就可以为所有的类生产代理对象,但是jdk实现代理时,目标类必须要实现一个接口
Cgilb代理:
是spring中的一种实现,使用cglib代理方式,目标类可以不用实现接口。
采用底层的字节码生产技术,为我们的目标类生产子类对象,采用方法拦截技术,在调用方法时,会进入到拦截中,获得所调用的方法
目标类不能是final修饰的类,目标类中的方法如果为final/static修饰的也不能增强。
spring中两种动态代理都进行的实现,可以根据不同的情况进行自动的选择:
1、单例对象,没有实现接口的类,可以实现cglib
2、原型对象(创建多个对象)实现接口的类,可以使用jdk代理
3、行为模式
1、模板方法模式:
jdbcTemplate执行sql时,步骤也是固定的:1.连接数据库,2.发生sql,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("评分");}
}
具体子类:实现抽象类中的抽象方法的类,有不同的实现方式,就可以有多个子类。
new具体的子类对象,用具体的子类对象调用父类的模板方法,把父类中的具体方法与自己实现的抽象方法一起执行。
场景:适合流程相对比较固定的,其中有变化方法交给子类实现。
2、策略模式
将不同的实现算法进行封装, 将功能的实现与使用相分离,在使用时,可以用不同的策略实现类进行替换。
代码结构:
抽象策略类:给出具体策略类的接口
具体策略类:实现抽象策略类的接口,提供具体的行为。
环境类:持有一个具体策略类的引用,最终给客户端调用。