复习day8
一. 反射
反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制
作用
通过反射可以使程序代码访问装载到JVM 中的类的内部信息
a) 获取已装载类的属性信息
b) 获取已装载类的方法
c) 获取已装载类的构造方法信息
二. class
class实例 - 一个类无论被实例化多少次,它在JVM中的class实例永远只有1个.
所有被类加载器加载到内存中的类都是属于Class的对象 - Class类是用来描述类的类[用来描述类的元信息]
class获取
- Class.forName(“类的路径”);当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。
Class clz = Class.forName("java.lang.String");
- 类名.class。这种方法只适合在编译前就知道操作的 Class。
Class clz = String.class;
- 对象名.getClass()。
String str = new String("Hello"); Class clz = str.getClass();
- 如果是基本类型的包装类,可以调用包装类的Type属性来获得该包装类的Class对象。
三. 反射优缺点
优点:能够运行时动态获取类的实例,提高灵活性;可与动态编译结合
缺点:使用反射性能较低,需要解析字节码,将内存中的对象进行解析。其解决方案是:通过setAccessible(true)关闭JDK的安全检查来提升反射速度;多次创建一个类的实例时,有缓存会快很多;ReflflectASM工具类,通过字节码生成的方式加快反射速度
四. 反射应用
1.Spring框架
Spring 通过 XML 配置模式装载 Bean 的过程:
- 将程序内所有 XML 配置文件加载入内存中;
- Java类里面解析xml里面的内容,得到对应实体类的字节码字符串以及相关的属性信息;
- 使用反射机制,根据这个字符串获得某个类的Class实例;
- 动态配置实例的属性。
Spring这样做的好处是:
- 不用每一次都要在代码里面去new或者做其他的事情;
- 以后要改的话直接改配置文件,代码维护起来就很方便了;
- 有时为了适应某些需求,Java类里面不一定能直接调用另外的方法,可以通过反射机制来实现。
2. JDBC 的数据库的连接
在JDBC 的操作中,如果要想进行数据库的连接,则必须按照以下的几步完成
- 通过Class.forName()加载数据库的驱动程序 (通过反射加载,前提是引入相关了Jar包);
- 通过 DriverManager 类进行数据库的连接,连接的时候要输入数据库的连接地址、用户名、密码;
- 通过Connection 接口接收连接。
3.JSP
在JSP语法里面一共有三个与反射有关的标签,用于进行JavaBean的操作(简单java类).
定义JavaBean:<jsp:useBean id=”对象” class=”包.类” scope=”保存的范围”/>
其中id是指向对象的引用
scope的一共有四种范围:page,request,session,application;
设置属性:<jsp:setProperty property=”属性名称” param=”参数名称” value=”数据” name=”id内容”/>
name 属性表示要操作的对象名字(jsp:useBean标签中的id内容);
取得属性:<jsp:getProperty property=”属性名称” name=”id内容”>
五. 动态代理
动态代理 - 在运行时在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。
JDK
动态代理:通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口 。JDK 动态代理的核心是 InvocationHandler 接口和 Proxy 类 。
- 创建一个InvocationHandler的实例, 就是代理类对象, 负责接口方法的调用
- 通过Proxy.newInstance()创建一个目标类接口的实例, 他需要传入3个三参数
- 参数1: 加载器, (将目标类接口生成字节码加载进内存)
- 参数2: 目标类接口(用于生成字节码,也就是说$Proxy的生产仅仅需要接口数组就可以完成)
- 参数3: 代理类对象(用于回调invoke方法,实现方法的增强)
第一个参数其实并不是很重要, 他就是负责将目标类接口生成的字节码加载进内存, 然后反射调用构造方法,初始化创建$Poxy对象,将反射获取到的$Poxy对象返回.所以第二个参数这个目标类的接口才是关键.
CGLIB
动态代理:CGLIB
是通过继承的方式做的动态代理,因此如果某个类被标记为final
,那么它是无法使用CGLIB
做动态代理的glib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理
JDK动态代理和CGLIB动态代理的区别?
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final
- 静态代理 - 指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;
六. 注解开发和XML开发区别
注解简单概括:写起来比较简单、方便,看起来也简洁,但是修改麻烦。
Xml配置概括:写起来比较灵活、修改方便,但是写和维护麻烦。
二、XML
1、XML的优点
(1)xml是集中式的元数据,不需要和代码绑定的;在我们开发中,xml配置文件和代码类是区分开的。不需要绑定到代码中。
(2)使用xml配置可以让软件更具有扩展性。
(3)对象之间的关系一目了然。
(4)成熟的校验机制,来保证正确。可以使用Schema或者是DTD来对xml的正确性进行校验。
2、XML的缺点
(1)应用程序中如果使用了xml配置,需要解析xml的工具或者是是第三方类库的支持。
(2)解析xml的时候必然会占用资源,势必会影响到应用程序的性能;
(3)xml配置文件过多,会导致维护变得困难。
(4)在程序编译期间无法对其配置项的正确性进行验证,只能在运行期发现。
(5)开发的时候,既要维护代码又要维护配置文件,使得开发的效率降低。
三、注解
1、注解的优点
(1)注解的解析可以不依赖于第三方库,可以之间使用Java自带的反射。
(2)注解和代码在一起的,降低了维护两个地方的成本。
(3)注解如果有问题,在编译期间,就可以验证正确性,如果出错更容易找。
(4)使用注解开发能够提高开发效率。不用多个地方维护,不用考虑是否存在“潜规则”。
2、注解的缺点
(1)修改的话比较麻烦。如果需要对注解进行修改的话,就需要对整个项目重新编译。
(2)处理业务类之间的复杂关系,不如xml那样容易修改,也不及xml那样明了。
(3)在程序中注解太多的话,会影响代码质量,代码简洁会有影响。
(4)注解功能没有xml配置齐全。
注解:
优点:
简化配置,使用起来直观且容易,提升开发的效率,类型安全,容易检测出问题
缺点:
修改起来比xml麻烦,如果对项目不了解,可能给开发和维护带来麻烦
Xml:
优点:
把类与类之间松解偶;修改方便;容易扩展;容易和其他系统进行数据交互;对象之间的关系一目了然
缺点:
配置冗长,需要额外维护;影响开发效率;类型不安全,校验不出来,出错不好排查
注解简单概括:写起来比较简单、方便,看起来也简洁,但是修改麻烦
Xml配置概括:写起来比较灵活、修改方便,但是写和维护麻烦
使用@interface来修饰注解,自定义的注解默认都会继承java.lang.Annotation
@Target - 指定你这个注解可以在什么地方被使用
哪些地方 - 类,方法,参数,接口,局部变量上,属性,构造
值可以设置成枚举类型java.lang.annotation.ElementType中的枚举常量
七. JVM8.x和之前版本有和区别!
JDK1.8与1.7最大的区别是1.8将永久代取消,取而代之的是元空间,既然方法区是由永久代实现的,取消了永久代,那么方法区由谁来实现呢,在1.8中方法区是由元空间来实现,所以原来属于方法区的运行时常量池就属于元空间了。元空间属于本地内存,所以元空间的大小仅受本地内存限制,但是可以通过-XX:MaxMetaspaceSize进行增长上限的最大值设置,默认值为4G,元空间的初始空间大小可以通过-XX:MetaspaceSize进行设置,默认值为20.8M,还有一些其他参数可以进行设置,元空间大小会自动进行调整
八. 堆栈区别
栈
- 存放的是基本数据类型和对象的引用
- 速度仅次于寄存器
- 创建程序的时候,Java编译器必须知道存放在栈中的数据的大小和生命周期
- 当程序执行的时候,就会创建一个栈帧,并且压栈,当方法执行完毕之后,栈帧出栈并销毁
堆
- 存放new出来的对象
- 在堆中存放的数据有很大的灵活性。你只要是new出来的对象,就会自动的在堆中分配一个空间进行存储
- 但是,也需要付出相应的代价,在堆中存储分配比在栈中需要更多的时间
- 堆中的数据是被所有线程共享的,在JVM中只有一个堆
栈和堆比较
- 栈和堆都是用来存储数据的地方
- 栈的优势:存取速度快,仅次于cpu中的寄存器。但是缺乏灵活性,存在栈中的数据的大小和生命周期必须是确定的
- 堆的优势:可以动态的分配内存大小,生命周期也不必事先告诉编译器。Java垃圾收集器会自动的处理一些不再使用的数据。缺点是运行期间分配大小,存取速度慢
- 管理方式不同,栈由操作系统自动分配释放,无需我们手动控制;堆的申请和释放工作由程序员控制,容易产生内存泄漏;空间大小不同。每个进程拥有的栈大小要远远小于堆大小。生长方向不同。堆的生长方向向上,内存地址由低到高;栈的生长方向向下,内存地址由高到低
九. GC算法
JVM
什么是JVM
java虚拟机,能够使java程序在不同平台上运行,屏蔽了与具体平台的相关信息,使得Java语言[编译程序]只需生成在Java虚拟机上运行的目标代码([字节码])
java程序执行过程
.java文件经过javac编译器编译成.class字节码文件,在经过虚拟机将.class文件解析,在经过类加载器将类加载进内存,加载完毕之后,再把class文件翻译成机器能识别的语言,再由JVM执行。
JVM内存模型
方法区
存放的是每次执行完毕之后类中的名字,字段和方法名信息,静态变量等,里面还包括了一个常量池,方法区被线程共享,jdk1.7之前,堆里面方法区存放到永久区,jdk1.7,方法区直接放在堆里面了,永久区划分到堆里面,jdk1.8之后,方法区存放到元空间。
虚拟机栈
每次有方法执行时,压进去的都是一个栈帧,栈帧有保存的是每个方法的局部变量等信息,每次有方法要执行完毕时,再从虚拟机栈中弹出。
堆
当每次有一个新的对象产生时,会在堆里面动态分配一个新的空间用来保存数据,可以被线程共享。虽然栈和堆都是用来存储数据,但是栈的存储效率要比堆高,因为栈的空间是固定的,牺牲空间提高时间,而堆的效率要低一些,因为要动态分配和回收。
新生代和老年代
新生代主要分为三个部分:Eden伊甸园,form区域,to区域,Eden伊甸园存放的都是刚刚产生的新的对象,form区域和to区域就是用来存储存储一些存活较久的数据,当有对象检测到没有被别人引用的时候,就会成为垃圾对象,被回收,存活下来的数据从form区域复制到to区域,清空form区域,下一次的时候就会从to区域复制到form区域,清空to区域的内容。这三个区域的比列:8:1:1,因为新创建的对象比较多,存活下来的数据比较少。如果存活下来的对象在form和to之间来回复制了15仍然存活,放入到老年代。
如何判断一个对象是否存活?
引用计数:
给每一个对象设置一个引用计数器,当有一个地方引用该对象的时候,引用计数器就+1,引用失效时,引用计数器就-1;当引用计数器为0的时候,就说明这个对象没有被引用,也就是垃圾对象,等待回收;
缺点:无法解决循环引用的问题,当A引用B,B也引用A的时候,此时AB对象的引用都不为0,此时也就无法垃圾回收,所以一般主流虚拟机都不采用这个方法;可达性分析对象 - 从一个被称为GC Roots的对象向下搜索,如果一个对象到GC Roots没有任何引用链相连接时,说明此对象不可用,在java中可以作为GC Roots的对象有以下几种:
- 虚拟机栈中引用的对象
- 方法区类静态属性引用的变量
- 方法区常量池引用的对象
- 本地方法栈JNI引用的对象
但一个对象满足上述条件的时候,不会马上被回收,还需要进行两次标记;第一次标记:判断当前对象是否有finalize()方法并且该方法没有被执行过,若不存在则标记为垃圾对象,等待回收;若有的话,则进行第二次标记;第二次标记将当前对象放入F-Queue队列,并生成一个finalize线程去执行该方法,虚拟机不保证该方法一定会被执行,这是因为如果线程执行缓慢或进入了死锁,会导致回收系统的崩溃;如果执行了finalize方法之后仍然没有与GC Roots有直接或者间接的引用,则该对象会被回收
GC算法
标记清除法:
第一步:利用可达性去遍历内存,把存活对象和垃圾对象进行标记;
第二步:在遍历一遍,将所有标记的对象回收掉;
特点:效率不行,标记和清除的效率都不高;标记和清除后会产生大量的不连续的空间分片,可能会导致之后程序运行的时候需分配大对象而找不到连续分片而不得不触发一次GC;标记整理法:
第一步:利用可达性去遍历内存,把存活对象和垃圾对象进行标记;
第二步:将所有的存活的对象向一端移动,将端边界以外的对象都回收掉;
特点:适用于存活对象多,垃圾少的情况;需要整理的过程,无空间碎片产生;复制算法:
将内存按照容量大小分为大小相等的两块,每次只使用一块,当一块使用完了,就将还存活的对象移到另一块上,然后在把使用过的内存空间移除;
特点:不会产生空间碎片;内存使用率极低;分代收集算法:
根据内存对象的存活周期不同,将内存划分成几块,java虚拟机一般将内存分成新生代和老生代,在新生代中,有大量对象死去和少量对象存活,所以采用复制算法,只需要付出少量存活对象的复制成本就可以完成收集;老年代中因为对象的存活率极高,没有额外的空间对他进行分配担保,所以采用标记清理或者标记整理算法进行回收;
总结:新new出来的对象会进入到新生代中的伊甸园区域,然后经过GC检测之后,将活着的对象移动到From区域.
经过GC15次检测之后,仍然存活的对象才会移动到老年代中. [8:1:1]
十. JDK8新特性
- jvm 将永久代取消,取而代之的是元空间
- HashMap 新增红黑树
- 函数式接口:指的是只有一个函数的接口,这样的接口可以隐式转换为Lambda表达式
- Lambda表达式:它允许我们将函数当成参数传递给某个方法,或者把代码本身当作数据处理
- 新增的Stream API(java.util.stream)将生成环境的函数式编程引入了Java库中
- 引入了新的Date-Time API(JSR 310)来改进时间、日期的处理