String对“+”的重载
对这块的理解应该分为两类,首先第一类是我们常见的那种String arg = “aa”+“bb”+“cc”;,属于创建多个StringBuilder对象,然后调用append()方法来拼接字符串,最后使用toString方法来生成一个新字符串。
但对于第二类而言,属于循环“+”这种形式,整体来说只创建一个StringBuilder对象来进行每次append()方法来拼接。eg:
StringBuilder sb = new StringBuilder();
for (int i=0;i<strs.length;i++) {
sb.append(strs[i]);
}
System.out.println(sb.toString());
String.valueOf和Integer.toString的区别(都是把int转换为String的方法)
首先先来说一下Interger.toString方法,基本的概念解释为该方法返回指定整数的有符号位的String对象,以10进制字符串形式返回,源码如下:
public static String toString(int i) {
//等于最小值直接返回最小值字符串,避免getChars方法遇到最小值发生错误
if (i == Integer.MIN_VALUE)
return "-2147483648";
//判断i的位数,若位负数增加1位用来保存符号位
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
char[] buf = new char[size];
//将i转换位buf符号数组
getChars(i, size, buf);//把当前字符串从i到size存入buf数组中
//因为buf数组是在方法内部生成,其他地方不会有其引用,所以直接将其引用给String内部的value保存,用来初始化String
return new String(buf, true);
}
接下来我们来看看Interger.toString方法,和上述方法不一样的是,该方法包含了大量的重载来实现:
public static String valueOf(int i)
作用:将int类型转换成String类型
源码:
public static String valueOf(int i) {
return Integer.toString(i);
}
这里我们可以看到,对于valueOf,这里调用了Interger.toString方法,当然对于该方法经过重载以后还能实现很多转String的,比如说object,数组,以及各种基本数据类型来进行向String的转换,所以一般推荐使用valueOf。
字符串的不可变性
String的底层实现在之前的博客里有详细说明,String底层是final,不能被改变,每次改变都会创建一个新的对象,如果你需要一个可以修改的StringBuilder是个不错的选择,不然你会有大量精力耗费在无聊的垃圾回收上。
自动拆装箱
先简单说一下,装箱就是把基本类型转换成包装器类型,而拆箱则是将其转换回来,然后我们来看看需要装箱的元素有哪些:
而根据源码来看,1、i >= 128 || i < -128 =====> new Integer(i) 2、i < 128 && i >= -128 =====> SMALL_VALUES[i + 128],也就是说如果创建的对象不在这个区间里面,那么所创建的一定是不同的对象(相反则指向同一个对象)
Integer的缓存机制(规则适用于整数区间 -128 到 +127)
这是Java5以后引入的一种节省空间,提高性能的机制,在源码里面我们发现,Integer类里面有一个IntegerCache 类(私有的静态类)用于缓存支持,并支持 -128 到 127 之间的自动装箱过程。
熟悉Java中各种关键字
transient:
在说这个关键字之前,我们先要了解一下序列化的概念(将对象转化为字节序列来表示),一般当我们使用缓存cache或者或者远程调用rpc(比如Dubbo)的时候,经常需要让我们实体类实现Serializable接口,目的就是为了让其实现可序列化,当然最后还是应该把序列化的Java对象给反序列化出来。
而这个关键字的作用就是让某些被修饰的成员属性变量不被序列化,当然作用也就是为了节省空间。
instanceof:
nstanceof 运算符是用来在运行时指出对象是否是特定类的一个实例。instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例(抽象的类具体化的一个例子),至于用法,举个例子:
result = object instanceof class
参数:
Result:布尔类型。
Object:必选项。任意对象表达式。
Class:必选项。任意已定义的对象类。
说明:
如果 Object 是 class 的一个实例,则 instanceof 运算符返回 true。如果 Object 不是指定类的一个实例或者Object 是 null,则返回 false。
volatile:
这个关键字是实现Java内存模型中可见性的,当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。(工作内存是各个线程独立相互不干扰的地方,就像Cache高速缓存这种),同时对于在多线程情况下发生的重排序,可以通过volatile关键字来保证一定的“有序性”(通过synchronized和Lock这些同步锁来保证),另外对于重排序,Java内存模型也有happens-before原则(先行先发生原则)来保证,具体原则我这里列出来,可以简单了解一下:
- 程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作
- 锁定规则:一个unLock操作先行发生于后面对同一个锁额lock操作
- volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作
- 传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C
- 线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作
- 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
- 线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行
- 对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始
synchronized:
这个关键字在之前的文章里已经有一些介绍了,为同步锁来防止多线程同时访问,这里就说说它的两个缺点:
第一大不足:由于我们没办法设置synchronized关键字在获取锁的时候等待时间,所以synchronized可能会导致线程为了加锁而无限期地处于阻塞状态。
第二大不足:使用synchronized关键字等同于使用了互斥锁,即其他线程都无法获得锁对象的访问权。这种策略对于读多写少的应用而言是很不利的,因为即使多个读者看似可以并发运行,但他们实际上还是串行的,并将最终导致并发性能的下降。
对于这个关键字,如果想深入了解源码实现,这个推荐大神的一个博文http://blog.csdn.net/javazejian/article/details/72828483
final:
简单说一点,final变量是只读的,不能引用不能重写不能继承,可以用于区修饰成员变量、本地变量、方法以及类,当然这里谈一下代码规范,按照Java代码惯例,final变量就是常量,而且通常常量名是要大写的,最后说一下使用final来修饰的好处:
- final关键字提高了性能。JVM和Java应用都会缓存final变量。
- final变量可以安全的在多线程环境下进行共享,而不需要额外的同步开销。
- 使用final关键字,JVM会对方法、变量及类进行优化。
static(静态修饰):
首先我们需要知道,在static方法内部不能调用非静态方法,而反过来是可以的,而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。这实际上正是static方法的主要用途。
static可以用来修饰类的成员方法、类的成员变量,最重要的一点再次重申,不依赖于任何对象就可以进行访问。(要注意的是,虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的)
而对于静态变量来说,差别则是静态变量被所有的对象所共享
最后说一下静态代码块,形成静态代码块一般用以优化性能,static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。