写在前面 随着开发、测试任务进入尾声,大家都在整理一些项目发布前的一些准备工作,其中一个重要的工作就是为之前写的一些sql语句建立索引,这高并发、高访问量的环境下是非常有必要的,建立一个好的索引能够极大地提高sql语句的查询效率,那么问题来了,到底什么是索引,怎样才能建立一个好…
mybaits问题
Mybatis insert into tab1 select column from tab2 命令未正常结束
因为我们是使用的oracle数据库,oracle是不支持自增的主键的, mybatis mapper 配置如下
1 | <insert id="insert" parameterType="com.xx" > |
程序运行起来mybatis报了一个异常sqlException 说命令未正确结束!查询了mybatis文档说明
useGeneratedKeys 允许 JDBC 支持自动生成主键,需要驱动兼容。 如果设置为 true
则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)。 true | false False
最后修改成了问题解决,但是有一点疑惑为啥默认这个就是false,为啥还要指定为false才可以呢?1
2
3
4<insert id="insert" parameterType="com.xx" useGeneratedKeys="false">
insert into tab1 (ID, CREATEDATE, STATUS)
(select ID, CREATEDATE, STATUS from tab2)
</insert>
Java中的多态性理解(注意与C++区分)
Java中除了static方法和final方法(private方法本质上属于final方法,因为不能被子类访问)之外,其它所有的方法都是动态绑定,这意味着通常情况下,我们不必判定是否应该进行动态绑定—它会自动发生。
- final方法会使编译器生成更有效的代码,这也是为什么说声明为final方法能在一定程度上提高性能(效果不明显)。
- 如果某个方法是静态的,它的行为就不具有多态性:
1 | class StaticSuper { |
输出 Base staticGet() Derived dynamicGet()
- 构造函数并不具有多态性,它们实际上是static方法,只不过该static声明是隐式的。因此,构造函数不能够被override。
- 在父类构造函数内部调用具有多态行为的函数将导致无法预测的结果,因为此时子类对象还没初始化,此时调用子类方法不会得到我们想要的结果。
1 | class Glyph { |
输出 Glyph() before draw() RoundGlyph.draw(). radius = 0 Glyph() after draw() RoundGlyph.RoundGlyph(). radius = 5
为什么会这样输出?这就要明确掌握Java中构造函数的调用顺序:
1. 在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制0;
2. 调用基类构造函数。从根开始递归下去,因为多态性此时调用子类覆盖后的draw()方法(要在调用RoundGlyph构造函数之前调用),由于步骤1的缘故,我们此时会发现radius的值为0;
3. 按声明顺序调用成员的初始化方法;
4. 最后调用子类的构造函数。
只有非private方法才可以被覆盖,但是还需要密切注意覆盖private方法的现象,这时虽然编译器不会报错,但是也不会按照我们所期望的来执行,即覆盖private方法对子类来说是一个新的方法而非重载方法。因此,在子类中,新方法名最好不要与基类的private方法采取同一名字(虽然没关系,但容易误解,以为能够覆盖基类的private方法)。
Java类中属性域的访问操作都由编译器解析,因此不是多态的。父类和子类的同名属性都会分配不同的存储空间,如下:
1 | // Direct field access is determined at compile time. |
输出:sup.filed = 0, sup.getField() = 1 sub.filed = 1, sub.getField() = 1, sub.getSuperField() = 0
Sub子类实际上包含了两个称为field的域,然而在引用Sub中的field时所产生的默认域并非Super版本的field域,因此为了得到Super.field,必须显式地指明super.field。
Timer TimerTask 问题
Timer在执行所有定时任务时只会创建一个线程。如果某个任务的执行时间过长,那么将破坏其他TimerTask的定时精确性。
例如周期每10ms跑一次的TimerTask,而另外一个需要执行40ms,那么这个周期任务或者在40ms任务执行完成连续调用4次,要不就是丢失4次调用(取决于它是基于固定速率来调度还是基于固定延时来调度)。
Timer的另外一个问题是,如果TimerTask抛出一个未检查的异常,那么Timer线程并不捕获异常,因此当TimerTask抛出未检查的异常时将终止定时线程。这种情况下,Timer也不会恢复线程的执行,而是会错误地认为整个Timer都被取消了。
因此已经被调度但未执行的TimerTask将不会再执行,新的任务也不能调度。这个问题称为“线程泄漏” 【Thread Leakage】
java内存区域划分
JVM内存区域划分
文章来源 一点咨询
前言
Java程序的运行是通过 “Java虚拟机” 来实现的。通过类加载器将class字节码文件加载进JVM,然后根据预定的规则执行。Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些内存区域被统一叫做运行时数据区。Java运行时数据区大致可以划分为5个部分。如下图所示。在这里要特别指出,我们现在说的JVM内存划分是概念模型。具体到每个JVM的具体实现可能会有所不同。具体JVM的实现我只会提到HotSpot虚拟机的实现细节。
程序计数器
程序计数器是一块较小的内存空间,它可以看成是当前线程所执行的字节码的行号指示器。程序计数器记录线程当前要执行的下一条字节码指令的地址。由于Java是多线程的,所以为了多线程之间的切换与恢复,每一个线程都需要单独的程序计数器,各线程之间互不影响。这类内存区域被称为“线程私有”的内存区域。
由于程序计数器只存储一个字节码指令地址,故此内存区域没有规定任何OutOfMemoryError情况。虚拟机栈
Java虚拟机栈也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
一个栈帧就代表了一个方法执行的内存模型,虚拟机栈中存储的就是当前执行的所有方法的栈帧(包括正在执行的和等待执行的)。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机中入栈到出栈的过程。我们平时所说的“局部变量存储在栈中”就是指方法中的局部变量存储在代表该方法的栈帧的局部变量表中。而方法的执行正是从局部变量表中获取数据,放至操作数栈上,然后在操作数栈上进行运算,再将运算结果放入局部变量表中,最后将操作数栈顶的数据返回给方法的调用者的过程。(关于栈帧和基于栈的方法执行,我会在之后写两篇文章专门介绍。敬请期待☺) 虚拟机栈可能出现两种异常:由线程请求的栈深度过大超出虚拟机所允许的深度而引起的StackOverflowError异常;以及由虚拟机栈无法提供足够的内存而引起的OutOfMemoryError异常。本地方法栈
本地方法栈与虚拟机栈类似,他们的区别在于:本地方法栈用于执行本地方法(Native方法);虚拟机栈用于执行普通的Java方法。在HotSpot虚拟机中,就将本地方法栈与虚拟机栈做在了一起。
本地方法栈可能抛出的异常同虚拟机栈一样。堆
Java堆是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例:所有的对象实例以及数组都要在堆上分配(The heap is the runtime data area from which memory for all class instances and arrays is allocated)。但Class对象比较特殊,它虽然是对象,但是存放在方法区里。在下面的方法区一节会介绍。Java堆是垃圾收集器(GC)管理的主要区域。现在的收集器基本都采用分代收集算法:新生代和老年代。而对于不同的”代“采用的垃圾回收算法也不一样。一般新生代使用复制算法;老年代使用标记整理算法。对于不同的”代“,一般使用不同的垃圾收集器,新生代垃圾收集器和老年代垃圾收集器配合工作。(关于垃圾收集算法、垃圾收集器以及堆中具体的分代等知识,我之后会专门写几篇博客来介绍。再次敬请期待☺)
Java堆可以是物理上不连续的内存空间,只要逻辑上连续即可。Java堆可能抛出OutOfMemoryError异常。方法区
方法区与Java堆一样,是各个线程共享的内存区域。它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
所有的字节码被加载之后,字节码中的信息:类信息、类中的方法信息、常量信息、类中的静态变量等都会存放在方法区。正如其名字一样:方法区中存放的就是类和方法的所有信息。此外,如果一个类被加载了,就会在方法去生成一个代表该类的Class对象(唯一一种不在堆上生成的对象实例)该对象将作为程序访问方法区中该类的信息的外部接口。有了该对象的存在,才有了反射的实现。 在Java7之前,HotSpot虚拟机中将GC分代收集扩展到了方法区,使用永久代来实现了方法区。这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载。但是在之后的HotSpot虚拟机实现中,逐渐开始将方法区从永久代移除。Java7中已经将运行时常量池从永久代移除,在Java 堆(Heap)中开辟了一块区域存放运行时常量池。而在Java8中,已经彻底没有了永久代,将方法区直接放在一个与堆不相连的本地内存区域,这个区域被叫做元空间。
运行时常量池
运行时常量池是方法区的一部分,关于运行时常量池的介绍,请参考我的
直接内存
JDK1.4中引用了NIO,并引用了Channel与Buffer,可以使用Native函数库直接分配堆外内存,并通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。
如上文介绍的:Java8以及之后的版本中方法区已经从原来的JVM运行时数据区中被开辟到了一个元空间的直接内存区域。
spring复习
Spring Ioc 依赖查找&注入
Spring Ioc 依赖查找&注入
根据bean 名称进行查找
- 实时查找 beanFactory.getBean(Striing);
- 延时查找 ObjectFactory
根据bean类型进行查找
- 单个bean对象 beanFactory.getBean(Person.class);
- 集合bean对象
根据bean名称+类型查找
根据java注解查找
- 单个bean对象
- 集合bean对象
根据bean名称注入
根据bean类型注入
- 单个bean对象
- 集合bean对象
注入容器内建Bean对象
例如 Environment
bean名称 | bean实例 | 使用场景 |
---|---|---|
Environment | Environment对象 | 外部化配置和profiles |
systemProperties | java.util.properties对象 | java系统属性 |
systemEnvironment | java.util.Map 对象 | 操作系统环境变量 |
messageSource | MessageSource对象 | 国际化文案 |
lifecycleProcessor | lifecycleProcessor对象 | lifecycle bean 处理器 |
applicationEventMulticast | applicationEventMulticast | spriing事件管理器 |
注入非Bean对象 beanFactory
注入类型
- 实时注入
- 延迟注入
BeanFactory 和ApplicationContext 谁才是真正的ioc 容器?
ApplicationContext 除了ioc 容器角色,还有
- 面向切面aop
- 配置元信息 configuration metadata
- 资源管理 resources
- 事件 events
- 国际化i18n
- 注解 annotations
- environment 抽象(environment abstraction)
Spring bean
BeanDefinition 元信息
属性 (Property) | 说明 |
---|---|
Class | bean全类名 |
Name | bean的名称或者Id |
Scope | bean的作用域 (singleton,prototype) |
constructor arguments | bean构造器参数(用于依赖注入) |
properties | bean 属性设置(用于依赖注入) |
autowring mode | 自动绑定模式(byName、byType) |
Lazy initialization mode | 延迟初始化模块(延迟、非延迟) |
initialization method | bean初始化回调方法 |
Destruction method | bean 销毁回调方法 |
BeanDefinition 构建
实例化springbean
- 通过serverLoaderFactoryBean
- 通过AutowireCapableBeanFactory#createBean(java.lang.class,int boolean)
- 通过BeanDefinitionRegistry#registerBeanDefinition(String,BeanDefinition)
初始化SpringBean
- @PostConstruct 标注方法
- 实现InitializingBean 接口的afterPropertiesSet()方法
- 自定义初始化方法
- xml配置
- Java 注解 @Bean(init-method=”init”)
- Java Api AbstractBeanDefinition#setInitMethodName(String);
- xml配置
[^这三种实现顺序为: postContruct —>iinitializing—>自定义方法]:
大家都知道创建一个javaweb项目都需要从web.xml开始配置,那么项目中要使用spring,spring
容器是怎么启动的呢,需要在web.xml中进行配置,
也就是DispatcherServlet 看起,从名字上就能看出来他也是一个servlet,那么他必须有init
(),doGet(),doPost(),destory()方法。我们肯定从init方法开始,发现没有init方法呢?
这个时候看这个DispatcherServlet 类,ctrl+H 查看下类继承的关系,发现他有父类,
frameworkServlet,HttpServletBean,HttpServlet .