java基础思维导图 面向对象三大特性封装 把一个对象的属性,和行为放到同一个类中就是封装继承 子类可以获得父类的属性和操作。 在java中常表示为isa的关系多态 是指一个事物,多种状态 比如一个动物可以是猫,狗 一个方法,可以有无参,1个参数,2个参数 也可使同一个方法,不同的表现编译时多态 主要值方法的重载运行时多态 继承重写向上转型类图 泛化实现聚合关联依赖知识点 数据类型String不可变的好处1。可以缓存hash值 因为String的hash值经常被使用,例如String用做HashMap的key。不可变的特性可以使得hash值也不可变,因此只需要进行一次计算。2。StringPool的需要 如果一个String对象已经被创建过了,那么就会从StringPool中取得引用。只有String是不可变的,才可能使用StringPool。3。安全性 String经常作为参数,String不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果String是可变的,那么在网络连接过程中,String被改变,改变String对象的那一方以为现在连接的是其它主机,而实际情况却不一定是。4。线程安全 String不可变性天生具备线程安全,可以在多个线程中安全地使用。String,StringBuffer andStringBuilder1。可变性String不可变 主要是因为string受到了final的修饰另外两个可变 基于动态数组, 基础长度16也就是需要扩容时,它会new一个新数组,长度为原来的2倍2。线程安全 String,StringBuffer线程安全StringBuilder线程不安全String。intern()运算继承Object通用方法关键字泛型机制详解注解基础 java内置注解元注解注解与反射接口自定义注解深入理解注解 java8提供了那些新的注解注解支持继承吗?注解实现的原理注解的应用场景 配置化到注解化框架的演进继承实现到注解实现Junit3到junit4自定义注解和AOP通过切面实现解耦异常机制详解反射机制详解 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。Java反射机制在框架设计中极为广泛,需要深入理解。反射基础 反射就是把java类中的各种成分映射成一个个java对象 例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。Class类 Class类,Class类也是一个实实在在的类,存在于JDK的java。lang包中。Class类的实例表示java应用运行时的类(classansenum)或接口(interfaceandannotation)(每个java类运行时都在JVM里表现为一个class对象,可通过类名。class、类型。getClass()、Class。forName(类名)等方法获取class对象)。数组同样也被映射为为class对象的一个类,所有具有相同元素类型和维数的数组都共享该Class对象。基本类型boolean,byte,char,short,int,long,float,double和关键字void同样表现为class对象。 Class类也是类的一种,与class关键字是不一样的。 手动编写的类被编译后会产生一个Class对象,其表示的是创建的类的类型信息,而且这个Class对象保存在同名。class的文件中(字节码文件) 每个通过关键字class标识的类,在内存中有且只有一个与之对应的Class对象来描述其类型信息,无论创建多少个实例对象,其依据的都是用一个Class对象。 Class类只存私有构造函数,因此对应Class对象只能有JVM创建和加载 Class类的对象作用是运行时提供或获得某个对象的类型信息,这点对于反射技术很重要(关于反射稍后分析)类加载类的加载机制流程 1。加载链接 2。连接3。初始化4。使用5。卸载类的加载 1。类编译2。类加载类的结构放在栈区类的实例放入堆区反射的使用 基于此我们如何通过反射获取Class类对象以及类中的成员变量、方法、构造方法等Class类对象的获取 在类加载的时候,jvm会创建一个class对象 class对象是可以说是反射中最常用的,获取class对象的方式的主要有三种 根据类名:类名。class 根据对象:对象。getClass() 根据全限定类名:Class。forName(全限定类名) TestpublicvoidclassTest()throwsException{获取Class对象的三种方式logger。info(根据类名:User。class);logger。info(根据对象:newUser()。getClass());logger。info(根据全限定类名:Class。forName(com。test。User));常用的方法logger。info(获取全限定类名:userClass。getName());logger。info(获取类名:userClass。getSimpleName());logger。info(实例化:userClass。newInstance());}Class类的方法 forName() (2)为了产生Class引用,forName()立即就进行了初始化。 ObjectgetClass() getName() getSimpleName() getCanonicalName() isInterface() getInterfaces() getSupercalss() newInstance() getFields() getDeclaredFieldsgetName、getCanonicalName与getSimpleName的区别 getSimpleName:只获取类名 getName:类的全限定名,jvm中Class的表示,可以用于动态加载Class对象,例如Class。forName。 getCanonicalName:返回更容易理解的表示,主要用于输出(toString)或log打印,大多数情况下和getName一样,但是在内部类、数组等类型的表示形式就不同了。Constructor类及其用法Field类及其用法 Field提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。 同样的道理,我们可以通过Class类的提供的方法来获取代表字段信息的Field对象,Class类与Field对象相关方法如下: 方法返回值方法名称方法说明 FieldgetDeclaredField(Stringname)获取指定name名称的(包含private修饰的)字段,不包括继承的字段 Field〔〕getDeclaredField()获取Class对象所表示的类或接口的所有(包含private修饰的)字段,不包括继承的字段 FieldgetField(Stringname)获取指定name名称、具有public修饰的字段,包含继承字段 Field〔〕getField()获取修饰符为public的字段,包含继承字段 voidset(Objectobj,Objectvalue)将指定对象变量上此Field对象表示的字段设置为指定的新值。 Objectget(Objectobj)返回指定对象上此Field表示的字段的值 ClassgetType()返回一个Class对象,它标识了此Field对象所表示字段的声明类型。 booleanisEnumConstant()如果此字段表示枚举类型的元素则返回true;否则返回false StringtoGenericString()返回一个描述此Field(包括其一般类型)的字符串 StringgetName()返回此Field对象表示的字段的名称 ClassgetDeclaringClass()返回表示类或接口的Class对象,该类或接口声明由此 Field对象表示的字段 voidsetAccessible(booleanflag)将此对象的accessible标志设置为指示的布尔值,即设置其可访问性Method类及其用法 Method提供关于类或接口上单独某个方法(以及如何访问该方法)的信息,所反映的方法可能是类方法或实例方法(包括抽象方法)。 方法返回值方法名称方法说明 MethodgetDeclaredMethod返回一个指定参数的Method对象,Stringname,ClassparameterTypes)该对象反映此Class对象所表示的类或接口的指定已声明方法。 Method〔〕getDeclaredMethod()返回Method对象的一个数组,这些对象反映此Class对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。 MethodgetMethod返回一个Method对象,它反映此Class对(Stringname,ClassparameterTypes)象所表示的类或接口的指定公共成员方法。 Method〔〕getMethods()返回一个包含某些Method对象的数组,这些对象反映此Class对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共member方法。反射机制的执行流程 反射获取类实例反射获取方法调用method。invoke()方法反射调用流程小结 最后,用几句话总结反射的实现原理:1。反射类及反射方法的获取,都是通过从列表中搜寻查找匹配的方法,所以查找性能会随类的大小方法多少而变化; 2。每个类都会有一个与之对应的Class实例,从而每个类都可以获取method反射方法,并作用到其他实例身上; 3。反射也是考虑了线程安全的,放心使用; 4。反射使用软引用relectionData缓存class信息,避免每次重新从jvm获取带来的开销; 5。反射调用多次生成新代理Accessor,而通过字节码生存的则考虑了卸载功能,所以会使用独立的类加载器; 6。当找到需要的方法,都会copy一份出来,而不是使用原来的实例,从而保证数据隔离; 7。调度反射方法,最终是由jvm执行invoke0()执行;SPI机制详解什么是SPI机制 SPI(ServiceProviderInterface),是JDK内置的一种服务提供发现机制,可以用来启用框架扩展和替换组件,主要是被框架的开发人员使用,比如java。sql。Driver接口,其他不同厂商可以针对同一接口做出不同的实现,MySQL和PostgreSQL都有不同的实现提供给用户,而Java的SPI机制可以为某个接口寻找服务实现。Java中SPI机制主要思想是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要,其核心思想就是解耦。 当服务的提供者提供了一种接口的实现之后,需要在classpath下的METAINFservices目录里创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类。当其他的程序需要这个服务的时候,就可以通过查找这个jar包(一般都是以jar包做依赖)的METAINFservices中的配置文件,配置文件中有接口的具体实现类名,可以根据这个类名进行加载实例化,就可以使用该服务了。JDK中查找服务的实现的工具类是:java。util。ServiceLoader。SPI机制的简单示例SPI机制的广泛应用JDBCDriverManager 在JDBC4。0之前,我们开发有连接数据库的时候,通常会用Class。forName(com。mysql。jdbc。Driver)这句先加载数据库相关的驱动,然后再进行获取连接等的操作。而JDBC4。0之后不需要用Class。forName(com。mysql。jdbc。Driver)来加载驱动,直接获取连接就可以了,现在这种方式就是使用了Java的SPI扩展机制来实现。1。JDBC接口定义 首先在java中定义了接口java。sql。Driver,并没有具体的实现,具体的实现都是由不同厂商来提供的。 2。mysql实现postgresql实现 在mysql的jar包mysqlconnectorjava6。0。6。jar中,可以找到METAINFservices目录,该目录下会有一个名字为java。sql。Driver的文件,文件内容是com。mysql。cj。jdbc。Driver,这里面的内容就是针对Java中定义的接口的实现。 同样在postgresql的jar包postgresql42。0。0。jar中,也可以找到同样的配置文件,文件内容是org。postgresql。Driver,这是postgresql对Java的java。sql。Driver的实现。 3。使用方法 上面说了,现在使用SPI扩展来加载具体的驱动,我们在Java中写连接数据库的代码的时候,不需要再使用Class。forName(com。mysql。jdbc。Driver)来加载驱动了,而是直接使用如下代码: Stringurljdbc:xxxx:xxxx:xxxxxxxx;ConnectionconnDriverManager。getConnection(url,username,password);。。 原理:上面的使用方法,就是我们普通的连接数据库的代码,并没有涉及到SPI的东西,但是有一点我们可以确定的是,我们没有写有关具体驱动的硬编码Class。forName(com。mysql。jdbc。Driver)! 上面的代码可以直接获取数据库连接进行操作,但是跟SPI有啥关系呢?上面代码没有了加载驱动的代码,我们怎么去确定使用哪个数据库连接的驱动呢?这里就涉及到使用Java的SPI扩展机制来查找相关驱动的东西了,关于驱动的查找其实都在DriverManager中,DriverManager是Java中的实现,用来获取数据库连接 其中loadInitialDrivers的作用是:从系统变量中获取有关驱动的定义。使用SPI来获取驱动的实现。遍历使用SPI获取到的具体实现,实例化各个实现类。根据第一步获取到的驱动列表来实例化具体实现类。 首先调用driversIterator。hasNext()方法,这里会搜索classpath下以及jar包中所有的METAINFservices目录下的java。sql。Driver文件,并找到文件中的实现类的名字,此时并没有实例化具体的实现类(ServiceLoader具体的源码实现在下面)。然后是调用driversIterator。next();方法,此时就会根据驱动名字具体实例化各个实现类了。现在驱动就被找到并实例化了。 SPI机制CommonLoggingSPI机制插件体系 其实最具spi思想的应该属于插件开发,我们项目中也用到的这种思想,后面再说,这里具体说一下eclipse的插件思想。 Eclipse使用OSGi作为插件系统的基础,动态添加新插件和停止现有插件,以动态的方式管理组件生命周期。一般来说,插件的文件结构必须在指定目录下包含以下三个文件: METAINFMANIFEST。MF:项目基本配置信息,版本、名称、启动器等build。properties:项目的编译配置信息,包括,源代码路径、输出路径plugin。xml:插件的操作配置信息,包含弹出菜单及点击菜单后对应的操作执行类等 当eclipse启动时,会遍历plugins文件夹中的目录,扫描每个插件的清单文件MANIFEST。MF,并建立一个内部模型来记录它所找到的每个插件的信息,就实现了动态添加新的插件。 这也意味着是eclipse制定了一系列的规则,像是文件结构、类型、参数等。插件开发者遵循这些规则去开发自己的插件,eclipse并不需要知道插件具体是怎样开发的,只需要在启动的时候根据配置文件解析、加载到系统里就好了,是spi思想的一种体现。SpI机制Spring中的SPI机制 在springboot的自动装配过程中,最终会加载METAINFspring。factories文件,而加载的过程是由SpringFactoriesLoader加载的。从CLASSPATH下的每个Jar包中搜寻所有METAINFspring。factories配置文件,然后将解析properties文件,找到指定名称的配置后返回。需要注意的是,其实这里不仅仅是会去ClassPath路径下查找,会扫描所有路径下的Jar包,只不过这个文件只会在Classpath下的jar包中。SPI机制深入理解SPI机制通常怎么使用 1。定义标准 定义标准,就是定义接口。比如接口java。sql。Driver 2。具体厂商或者框架开发者实现 厂商或者框架开发者开发具体的实现:在METAINFservices目录下定义一个名字为接口全限定名的文件,比如java。sql。Driver文件,文件内容是具体的实现名字,比如me。cxis。sql。MyDriver。写具体的实现me。cxis。sql。MyDriver,都是对接口Driver的实现。 3。程序猿使用 我们会引用具体厂商的jar包来实现我们的功能: ServiceLoaderloadedDriversServiceLoader。load(Driver。class);获取迭代器IteratordriversIteratorloadedDrivers。iterator();遍历while(driversIterator。hasNext()){driversIterator。next();可以做具体的业务逻辑}SPI与API的区别 第一个SPI和API的区别? SPI接口位于调用方所在的包中 概念上更依赖调用方。组织上位于调用方所在的包中。实现位于独立的包中。常见的例子是:插件模式的插件。API接口位于实现方所在的包中 概念上更接近实现方。组织上位于实现方所在的包中。实现和接口在一个包中。 第二个什么时候用API,什么时候用SPI? SPI机制实现原理SPI机制的缺陷 1。不能按需加载,需要遍历所有的实现,并实例化,然后在循环中才能找到我们需要的实现。如果不想用某些实现类,或者某些类实例化很耗时,它也被载入并实例化了,这就造成了浪费。 2。获取某个实现类的方式不够灵活,只能通过Iterator形式获取,不能根据某个参数来获取对应的实现类。 3。多个并发多线程使用ServiceLoader类的实例是不安全的。