用数组实现队列时要注意 溢出 现象,这时咱们能够采用循环数组的方式来解决,即将数组收尾相接。使用front指针指向队列首位,tail指针指向队列末位。java
由于生命周期不一样。局部变量在方法结束后就会被销毁,但内部类对象并不必定,这样就会致使内部类引用了一个不存在的变量。程序员
因此编译器会在内部类中生成一个局部变量的拷贝,这个拷贝的生命周期和内部类对象相同,就不会出现上述问题。面试
但这样就致使了其中一个变量被修改,两个变量值可能不一样的问题。为了解决这个问题,编译器就要求局部变量须要被final修饰,以保证两个变量值相同。正则表达式
在JDK8以后,编译器不要求内部类访问的局部变量必须被final修饰,但局部变量值不能被修改(不管是方法中仍是内部类中),不然会报编译错误。利用javap查看编译后的字节码能够发现,编译器已经加上了final。算法
根据代码的计算结果,s的值应该是-1371654655,这是因为Java中右侧值的计算默认是int类型。sql
NIO(Non-blocking IO)为全部的原始类型提供(Buffer)缓存支持,字符集编码解码解决方案。Channel :一个新的原始I/O 抽象。支持锁和内存映射文件的文件访问接口。提供多路(non-bloking) 非阻塞式的高伸缩性网络I/O 。
I编程
Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。Java IO面向流意味着每次从流中读一个或多个字节,直至读取全部字节,它们没有被缓存在任何地方。此外,它不能先后移动流中的数据。若是须要先后移动从流中读取的数据,须要先将它缓存到一个缓冲区。后端
Java NIO的缓冲导向方法略有不一样。数据读取到一个它稍后处理的缓冲区,须要时可在缓冲区中先后移动。这就增长了处理过程当中的灵活性。可是,还须要检查是否该缓冲区中包含全部您须要处理的数据。并且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里还没有处理的数据。设计模式
阻塞与非阻塞IO数组
Java IO的各类流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据彻底写入。该线程在此期间不能再干任何事情了。Java NIO的非阻塞模式,是线程向某通道发送请求读取数据,仅能获得目前可用的数据,若是目前没有数据可用时,就什么都不会获取,固然它不会保持线程阻塞。因此直至数据变的能够读取以前,该线程能够继续作其余的事情。非阻塞写也是如此。因此一个单独的线程如今能够管理多个输入和输出通道。
选择器(Selectors)
Java NIO 的 选择器容许一个单独的线程来监视多个输入通道,你能够注册多个通道使用一个选择器,而后使用一个单独的线程来“选择”通道:这些通道里已经有能够处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。
Java反射机制可让咱们在编译期(Compile Time)以外的运行期(Runtime)检查类,接口,变量以及方法的信息。反射还可让咱们在运行期实例化对象,调用方法,经过调用get/set方法获取变量的值。同时咱们也能够经过反射来获取泛型信息,以及注解。还有更高级的应用–动态代理和动态类加载(ClassLoader.loadclass())。
下面列举一些比较重要的方法:
getFields:获取全部 public 的变量。
getDeclaredFields:获取全部包括 private , protected 权限的变量。
setAccessible:设置为 true 能够跳过Java权限检查,从而访问private权限的变量。
getAnnotations:获取注解,能够用在类和方法上。
获取方法的泛型参数:
method = Myclass.class.getMethod("setStringList", List.class); Type[] genericParameterTypes = method.getGenericParameterTypes(); for(Type genericParameterType : genericParameterTypes){ if(genericParameterType instanceof ParameterizedType){ ParameterizedType aType = (ParameterizedType) genericParameterType; Type[] parameterArgTypes = aType.getActualTypeArguments(); for(Type parameterArgType : parameterArgTypes){ Class parameterArgClass = (Class) parameterArgType; System.out.println("parameterArgClass = " + parameterArgClass); } } }
动态代理:
//Main.java public static void main(String[] args) { HelloWorld helloWorld=new HelloWorldImpl(); InvocationHandler handler=new HelloWorldHandler(helloWorld); //建立动态代理对象 HelloWorld proxy=(HelloWorld)Proxy.newProxyInstance( helloWorld.getClass().getClassLoader(), helloWorld.getClass().getInterfaces(), handler); proxy.sayHelloWorld(); } //HelloWorldHandler.java public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; //调用以前 doBefore(); //调用原始对象的方法 result=method.invoke(obj, args); //调用以后 doAfter(); return result; }
经过反射获取方法注解的参数:
Class aClass = TheClass.class; Annotation[] annotations = aClass.getAnnotations(); for(Annotation annotation : annotations){ if(annotation instanceof MyAnnotation){ MyAnnotation myAnnotation = (MyAnnotation) annotation; System.out.println("name: " + myAnnotation.name()); System.out.println("value: " + myAnnotation.value()); } }
非静态内部类能定义静态方法吗?
public class OuterClass{ private static float f = 1.0f; class InnerClass{ public static float func(){return f;} } }
以上代码会出现编译错误,由于只有静态内部类才能定义静态方法。
使用方法的区别
Synchronized:在须要同步的对象中加入此控制,synchronized能够加在方法上,也能够加在特定代码块中,括号中表示须要锁的对象。
Lock:须要显示指定起始位置和终止位置。通常使用ReentrantLock类作为锁,多个线程中必需要使用一个ReentrantLock类作为对象才能保证锁的生效。且在加锁和解锁处须要经过lock()和unlock()显示指出。因此通常会在finally块中写unlock()以防死锁。
性能的区别
synchronized是tuo管给JVM执行的,而lock是java写的控制锁的代码。在Java1.5中,synchronize是性能低效的。由于这是一个重量级操做,须要调用操做接口,致使有可能加锁消耗的系统时间比加锁之外的操做还多。相比之下使用Java提供的Lock对象,性能更高一些。可是到了Java1.6,发生了变化。synchronize在语义上很清晰,能够进行不少优化,有适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。致使在Java1.6上synchronize的性能并不比Lock差。
Synchronized:采用的是CPU悲观锁机制,即线程得到的是独占锁。独占锁意味着 其余线程只能依靠阻塞来等待线程释放锁。而在CPU转换线程阻塞时会引发线程上下文切换,当有不少线程竞争锁的时候,会引发CPU频繁的上下文切换致使效率很低。
Lock:用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操做,若是由于冲突失败就重试,直到成功为止。乐观锁实现的机制就是CAS操做。咱们能够进一步研究ReentrantLock的源代码,会发现其中比较重要的得到锁的一个方法是compareAndSetState。这里其实就是调用的CPU提供的特殊指令。
ReentrantLock:具备更好的可伸缩性:好比时间锁等候、可中断锁等候、无块结构锁、多个条件变量或者锁投票。
folat类型的还有double类型的,这些小数类型在趋近于0的时候直接等于0的可能性很小,通常都是无限趋近于0,所以不能用==来判断。应该用|x-0|<err来判断,这里|x-0|表示绝对值,err表示限定偏差。
//用程序表示就是
fabs(x) < 0.00001f
内部类在声明的时候必须是 Outer.Inner a,就像int a 同样,至于静态内部类和非静态内部类new的时候有点区别:
Outer.Inner a = new Outer().new Inner()(非静态,先有Outer对象才能 new 内部类)
Outer.Inner a = new Outer.Inner()(静态内部类)
能够包含:字母、数字、$、_(下划线),不可用数字开头,不能是 Java 的关键字和保留字。
装饰模式:java.io
单例模式:Runtime类
简单工厂模式:Integer.valueOf方法
享元模式:String常量池、Integer.valueOf(int i)、Character.valueOf(char c)
迭代器模式:Iterator
职责链模式:ClassLoader的双亲委派模型
解释器模式:正则表达式java.util.regex.Pattern
JDK 1.7及之前:
ConcurrentHashMap容许多个修改操做并发进行,其关键在于使用了锁分离技术。它使用了多个锁来控制对hash表的不一样部分进行的修改。ConcurrentHashMap内部使用段(Segment)来表示这些不一样的部分,每一个段其实就是一个小的hash table,它们有本身的锁。只要多个修改操做发生在不一样的段上,它们就能够并发进行。
JDK 1.8:
Segment虽保留,但已经简化属性,仅仅是为了兼容旧版本。
插入时使用CAS算法:unsafe.compareAndSwapInt(this, valueOffset, expect, update)。CAS(Compare And Swap)意思是若是valueOffset位置包含的值与expect值相同,则更新valueOffset位置的值为update,并返回true,不然不更新,返回false。插入时不容许key或value为null
与Java8的HashMap有相通之处,底层依然由“数组”+链表+红黑树;
底层结构存放的是TreeBin对象,而不是TreeNode对象;
CAS做为知名无锁算法,那ConcurrentHashMap就没用锁了么?固然不是,当hash值与链表的头结点相同仍是会synchronized上锁,锁链表。
虽然递增操做++i是一种紧凑的语法,使其看上去只是一个操做,但这个操做并不是原子的,于是它并不会做为一个不可分割的操做来执行。实际上,它包含了三个独立的操做:读取count的值,将值加1,而后将计算结果写入count。这是一个“读取 - 修改 - 写入”的操做序列,而且其结果状态依赖于以前的状态。因此在多线程环境下存在问题。
要解决自增操做在多线程环境下线程不安全的问题,能够选择使用Java提供的原子类,如AtomicInteger或者使用synchronized同步方法。
new是一个关键字,它是调用new指令建立一个对象,而后调用构造方法来初始化这个对象,可使用带参数的构造器
newInstance()是Class的一个方法,在这个过程当中,是先取了这个类的不带参数的构造器Constructor,而后调用构造器的newInstance方法来建立对象。
Class.newInstance不能带参数,若是要带参数须要取得对应的构造器,而后调用该构造器的Constructor.newInstance(Object … initargs)方法
接口的默认方法和静态方法,JDK8容许咱们给接口添加一个非抽象的方法实现,只须要使用default关键字便可。也能够定义被static修饰的静态方法。
对HashMap进行了改进,当单个桶的元素个数大于6时就会将实现改成红黑树实现,以免构造重复的hashCode的***
多并发进行了优化。如ConcurrentHashMap实现由分段加锁、锁分离改成CAS实现。
JDK8拓宽了注解的应用场景,注解几乎可使用在任何元素上,而且容许在同一个地方屡次使用同一个注解
Lambda表达式
Xms 堆最小值
Xmx 堆最大值
Xmn: 新生代容量
XX:SurvivorRatio 新生代中Eden与Surivor空间比例
Xss 栈容量
XX:PermSize 方法区初始容量
XX:MaxPermSize 方法区最大容量
XX:+PrintGCDetails 收集器日志参数
重写loadClass()方法。
hashcode() 返回该对象的哈希码值,支持该方法是为哈希表提供一些优势,例如,java.util.Hashtable 提供的哈希表。
在 Java 应用程序执行期间,在同一对象上屡次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改(equals默认返回对象地址是否相等)。若是根据 equals(Object)方法,两个对象是相等的,那么在两个对象中的每一个对象上调用 hashCode 方法都必须生成相同的整数结果。
如下状况不是必需的:若是根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法一定会生成不一样的整数结果。可是,程序员应该知道,为不相等的对象生成不一样整数结果能够提升哈希表的性能。
实际上,由 Object 类定义的 hashCode 方法确实会针对不一样的对象返回不一样的整数。(这通常是经过将该对象的内部地址转换成一个整数来实现的,可是 JavaTM 编程语言不须要这种实现技巧I。)
hashCode的存在主要是用于查找的快捷性,如 Hashtable,HashMap等,hashCode 是用来在散列存储结构中肯定对象的存储地址的;
若是两个对象相同,就是适用于 equals(java.lang.Object) 方法,那么这两个对象的 hashCode 必定要相同;
若是对象的 equals 方法被重写,那么对象的 hashCode 也尽可能重写,而且产生 hashCode 使用的对象,必定要和 equals 方法中使用的一致,不然就会违反上面提到的第2点;
两个对象的hashCode相同,并不必定表示两个对象就相同,也就是不必定适用于equals(java.lang.Object) 方法,只可以说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”。
sleep()和yield()都会释放CPU。
sleep()使当前线程进入停滞状态,因此执行sleep()的线程在指定的时间内确定不会执行;yield()只是使当前线程从新回到可执行状态,因此执行yield()的线程有可能在进入到可执行状态后立刻又被执行。
sleep()可以使优先级低的线程获得执行的机会,固然也可让同优先级和高优先级的线程有执行的机会;yield()只能使同优先级的线程有执行的机会。
{}是预编译处理,${}是字符串替换。
Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
Mybatis在处理{}替换成变量的值。
使用#{}能够有效的防止SQL注入,提升系统安全性。
Dao接口,就是人们常说的Mapper接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名,就是映射文件中MappedStatement的id值,接口方法内的参数,就是传递给sql的参数。Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串做为key值,可惟必定位一个MappedStatement,举例:com.mybatis3.mappers.StudentDao.findStudentById,能够惟一找到namespace为com.mybatis3.mappers.StudentDao下面id = findStudentById的MappedStatement。在Mybatis中,每个<select>、<insert>、<update>、<delete>标签,都会被解析为一个MappedStatement对象。
Dao接口里的方法,是不能重载的,由于是全限名+方法名的保存和寻找策略。
Dao接口的工做原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生成代理proxy对象,代理对象proxy会拦截接口方法,转而执行MappedStatement所表明的sql,而后将sql执行结果返回。