反射
什么是反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
简单理解反射:
通过class文件对象(Class对象、字节码文件对象),去创建类对象、使用其构造方法、成员函数和成员方法,
然后通过反射得到的对象Method,Fielde等对象,反过来执行类中的方法和属性。
反射常用的api
三个方法获取类对象
获取class文件对象的方法:
- Objcet类的getClass()方法
- 数据类型的静态属性class
- Class类的方法forName(className)
分别使用以上三个方法获取Class对象,讲解过程中强调一下几点(及其原因):
- 每个类只有一个对应的字节码文件对象
- 或者说同一个类下的所有实例对象公用一个字节码文件对象
- 第三种方式传递的类名是全类名
学习了三种方法以后,我们在开发中到底用哪个呢?
- 开发中用第三种。自己写例子测试可以使用前两种。
- 因为第三种方式可以结合配置文件使用,提高了程序的扩展性
通过Class可以获取类中的方法对象
Method[] methods = c.getMethods();// 所有公共方法,包括父类的
Method[] methods = c.getDeclaredMethods();// 本类的所有方法
Method method = c.getMethod(方法名称,方法的形参类型)
获取方法对象的目的,就是反过来去执行类的中的方法。比如:
- 正确执行Demo类中的get()方法: new Demo().get()
- 通过反射去执行Demo类的方法:method.invoke(对象);这样更加灵活
反射得到类的实例化对象
Class clazz = Class类的方法forName(className)
Object obj = clazz.newInstance();//默认通过无参的构造,实例化该类的对象
比如:
- 获取Demo类中的对象: Demo demo = new Demo();
- 反射获取Demo类中的对象:Demo demo = Demo.class.newInstance();
总结:为什么要使用反射?
- 因为使用反射,能够让我们更加灵活的操作类的方法和属性,通常在框架里面我们会使用反射灵活的获取类的对象,操作类的方法。
- 反射可以获取构造器对象和成员变量对象,在这里就不一一列举了
总结:
- 我们以后的框架都是这种模式,提供你一个配置文件,几个键是已知的,知道你按照他的要求将这些键配起来,框架也就运行起来了。
- 所以框架是为了简化我们的开发的,而没有增加我们开发的复杂度
- Java基础学好了、javaweb理解了,后面的框架跟项目都不是个事
- 学习java基础让我们熟悉java的面向对象的思想和常用api的使用
- 学习javaweb让我们理解浏览器和服务器端的交互
- 学习框架简化我们开发项目的难度
底层使用的设计模式
设计模式介绍
一个系统的整个架构,怎么分块、分层、分包,开发完成后怎么集成、怎么测试,怎么让程序的拓展性更好,这些时候就需要考虑到设计模式。好多人不知道学了这些东西应该往哪里用,其实我们现在学的一些东西就已经用到了设计模式,只是我们不知道。所以现在我们先了解了解,回头可以好好研究研究。
我们先从字面上理解一下,什么是设计模式。
一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
举例说明上面的概念:建房子可以建各种各样的,一层平房、两层复式。如果你想建房子,看我的不错,可以把我的设计图纸拿走对比着去建,这就是现实生活中的设计模式。
刚才说了,房子有一层两层,平房复式,那么java中的设计模式也是有分类的。详细讲解并举例下面的三种分类
- 创建型:创建对象。有:工厂模式、单例模式
- 结构型:对象间的关系。有:装饰模式
- 行为型:对象能够做什么。模板模式。
- 增强型:对象除了做什么,还能额外做什么,有:代理模式
单列模式(多)
单列模式概述
单一实例模式(唯一实例模式)
- 就是指类在内存中至多只能有一个对象。
举列:打印机服务(在一台机子上),ATM取款(在一台机子上)
应用场景:数据库连接池对象(连接池对象唯一),Servlet对象(唯一)
作用:单列模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。 使用Singleton的好处还在于可以节省内存,因为它限制了实例的个数,有利于Java垃圾回收
单列模式的实现
单列模式实现的要求
- 外界不能够随意创建对象。通俗的来讲就是把构造方法私有
类本身要创建一个对象(自己内部创建)。
- 通过公共的方式把对象(单列的)提供给别人。
饿汉式单列模式:
- 概述:对于饿汉模式,我们可以这样理解:该单例类非常饿,迫切需要吃东西,所以它在类加载的时候就立即创建对象
public class Singleton {
private static Singleton singleton = new Singleton();//立即实例化对象
private Singleton() {}//私有化构造方法
public static Singleton getSignleton(){//对外提供方法得到单列对象
return singleton;
}
}
懒汉式单列模式:
- 概述:对于懒汉模式,我们可以这样理解:该单例类非常懒,只有在自身需要的时候才会行动,从来不知道及早做好准备。它在需要对象的时候,才判断是否已有对象,如果没有就立即创建一个对象,然后返回,如果已有对象就不再创建,立即返回。
懒汉模式只在外部对象第一次请求实例的时候才去创建
public class Singleton {
private static Singleton singleton = null;
private Singleton(){}
public static Singleton getSingleton(){
if(singleton == null){
singleton = new Singleton();//延迟创建对象
}
return singleton;
}
}
Spring框架中用到了单列模式,比如:
< bean id="userDao" class="com.rawchen.UserDaoImpl" scope="singleton" ></bean>
当然了,有单列模式,自然会有多列,就是可以创建多个对象,具体采用单列或者多列,
要看我们实际开发中的需要了。
工厂模式(多)
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。
这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
通俗的来讲就是:
- 原来需要我们自己做自己创建的对象现在不需要我们做了,我们只需要向工厂要,你需要什么,工厂给你什么(指的:通过工厂创建对象,降低耦合度)。
简单工厂的实现方式
比如:
我们需要猫狗这些动物的对象,因为这些对象有共性,所以我们抽取出来的了公共父类Animal。因为每次创建对象我们都自己new太麻烦,所以我们想让工厂帮我们做,所以又提供了一个AnimalFactory,并提供了createAnimal方法,我们想要什么对象,就给该方法传递相应的参数,他就能返回相应的对象。
简单工厂模式:
Animal
|--Dog
|--Cat
AnimalFactory
public static Animal createAnimal(String type)
/**
定义一个接口:动物接口
*/
public interface Animal {
public void eat();
}
//Cat类
public class Cat implements Animal {
@Override
public void eat() {
System.out.println("猫吃鱼---");
}
}
//Dog类
public class Dog implements Animal {
@Override
public void eat() {
System.out.println("狗吃骨头-------");
}
}
/*
工厂类对象:创建对象
之前自己创建对象,现在把对象的创建权交个工厂
*/
public class AnimalFactory {
/*
提供一个方法:创建对象
*/
public static Animal getBean(String type){
if("dog".equals(type)){
return new Dog();
}else if("cat".equals(type)){
return new Cat();
} else{
return null;
}
}
通过分析:
但是这种模式依然存在耦合关系,拓展性能不好(通过代码再详细讲解)。所以我们称之为简单工厂模式,
工厂里面也是通过new Dog()这样的方式创建对象的,使用起来不灵活。
工厂方法模式
- 实际开发是:通过反射+配置文件完成
在AnimalFactory工厂里添加一个方法如下:
/*
通过反射+配置文件完成
在这里,就不写配置文件了
实际开发中:class对象是通过配置文件获取的
比如:spring创建对象的方式
<bean id="cat" class="cn.itheima.Cat" />
<bean id="dog" class="cn.itheima.Dog" />
通过配置文件获取 class, 能够动态去创建对象
*/
public static Animal getBean(Class type) {
try{
Animal animal = (Animal) type.newInstance();
return animal;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
- 总结:这种工厂模式的好处是,把对象的创建权利交个了工厂,同时又看不到
耦合关系,便于程序后期的扩展和维护。
工厂和单列小结:
装饰者模式
用的不多,主要用于比较代理模式
- 装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
- 通俗的来讲,就是给接口(或者类)的 子类 创建一个装饰类对象,通过装饰类对象
增强接口中实现类中的功能。
书写装饰类要求:
- 装饰类和被装饰的类实现相同的接口
- 要重写接口的所有方法
- 在装饰类里面引入被装饰的类。
优点:
- 需要扩展一个类的功能
- 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
缺点:
- 实现接口,重写接口的所有方法
- 即使接口中的方法没有增强,也需要重写。
与继承的区别:
- 这种比继承更加灵活机动的特性,也同时意味着更加多的复杂性。
- 装饰模式会导致设计中出现许多小类,如果过度使用,会使程序变得很复杂。
装饰者模式和代理的比较总结:
需求:
代码比较:
装饰者模式的实现
需求:给手机打电话加一个彩铃功能
Phone(接口)
|--PhoneImpl(具体实现类)
|--PhoneDecorate(装饰者类)
代码实现:
public interface Phone {
public abstract void call();
}
public class PhoneImpl implements Phone {
@Override
public void call() {
System.out.println("用手机打电话");
}
}
public class PhoneDecorate implements Phone {
private Phone phone; //具体的手机,多态
public PhoneDecorate(Phone sendphone) {//构造方法,赋值
this.phone = sendphone
}
@Override
public void call() {
//加彩铃功能 增强功能
this.phone.call();//打电话
}
}
分析:
装饰者虽然对类中的方法能进行增强,稍微比继承的方式灵活一点。
但是 最大的缺点是,需要重写接口的所有方法,即使该方法不需要增强。
也要把这个方法重写了。
所以 这种模式在框架中应用较少。
动态代理模式(多)
动态代理就是给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问
通俗的来讲:
- 就是通过生成的代理对象 来 增强 原对象中的方法。
比如:
- spring进行事务管理就是通过动态代理来进行的。
实现方式:
- spring会给调用的对象 生成一个代理 对象 ,通过对代理对象的调用实现事务的管理。
动态代理分类
(1) JDK动态代理:sun官方提供的代理方式
通俗的来讲,如果使用JDK实现动态代理,必须提供接口,然后提供类实现接口
JDK就是通过接口给实现类进行代理的。
Spring默认使用JDK的动态代理
比如:
- UserService 接口
- UserSerivceImpl implement UserService接口 实现类
- Jdk会通过接口给UserSerivceImp生成代理对象,增强方法。
/*
定义一个接口:
通过代理:增强删除方法
在调用删除方法之前,添加操作时间,操作人(记录日志)
*/
public interface UserService {
public void deleteUses();//删除数据
public void selectUses();//查询数据
}
public class UserServiceImpl implements UserService {
@Override
public void deleteUses() {
System.out.println("执行删除数据---");
}
@Override
public void selectUses() {
}
}
public class Demo_JDK {
public static void main(String[] args) {
//1.创建UserService 实现类对象
final UserService service = new UserServiceImpl();
//2.创建代理对象,控制service对象(通俗的说增强里面的方法)
UserService proxy =(UserService) Proxy.newProxyInstance(Demo_JDK.class.getClassLoader(), service.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().contains("delete")){
//模拟增强效果
System.out.println("2019-5-22 12:24:36,小明执行删除操作");
}
return method.invoke(service,args);//如果没有增强,原路返回
}
} );
//3.调用方法
// proxy.selectUses();//没有增强,
proxy.deleteUses();//增强
}
}
JDK动态代理总结:
动态代理的执行流程:
(2) CGLIB动态代理:第三方的代理方式
特点:
- 基于类实现的(也可以基于接口实现)
通俗的来讲:如果有一个类,CGLIB会给该类生成代理对象,完成对方法的增强
比如:
UserService 类
CGLIB会给该类生成一个代理对象,增强类中的方法。
/*
定义一个类:
通过代理:增强删除方法
在调用删除方法之前,添加操作时间,操作人(记录日志)
*/
public class UserService {
public void deleteUses(){//删除数据
System.out.println("删除数据");
};
public void selectUses(){//查询数据
};
}
public class Cglib_Service implements MethodInterceptor{
//1.创建代理类对象:
private Enhancer eh = new Enhancer();
public Object getProxyObj(Class clazz){//clazz指的就是真实类的字节码对象
//1.1 引入真实类的clazz
eh.setSuperclass(clazz);
//1.2 回调方法对真实类进行反射: 把真实类的属性和方法反射成对应的对象:Field Method等
eh.setCallback(this);
//1.3 组装 :对真实类里面的属性和方法 进行重新组装,生成代理对象(代理类是真实类的子类)
Object proxy = eh.create();
return proxy;
}
/**
*
* @param objproxy: 代理对象
* @param method: sale
* @param params: 方法里面的参数
* @param methodProxy:CGLIB$sale$0():代理对象的方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object objproxy, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
//1.对删除方法进行增强
System.out.println("2019-5-22 15:36:36,小黑执行删除操作");
//2.调用真实类的方法:通过组装的方法对象去调用
Object obj = methodProxy.invokeSuper(objproxy,params);
return obj;
}
}
public class Demo_cglib {
public static void main(String[] args) {
//1创建UserService对象
UserService service = new UserService();
//2.通过cglib给 UserSerivce生成代理对象
Cglib_Service cglib = new Cglib_Service();
UserService proxyObj = (UserService) cglib.getProxyObj(service.getClass());
proxyObj.deleteUses();
}
}
总结:
动态代理相对于装饰者模式来说,更加灵活,不用重写接口中的方法,可以动态的完成
对方法的增强。
Spring进行事务管理时,会根据当前的是接口或者是类,来使用不同的代理方式。
JDK动态代理和CGLIB动态代理区别:
- JDK动态代理基于接口实现的。也就是说,如果使用JDK动态代理,必须提供接口
- CGLIB动态代理基于类实现的。也就是说,只需提供一个类即可,CGLIB会给这个类生成代理对象
Spring框架在使用代理模式时,会根据当前java文件是类或者是接口,然后采用不同的代理方式,在spring4.0以后的版本(自动整合了cglib代理)