基本变量
所有new出来的对象存储在堆中,而对于小而简单的变量,用new往往不是很有效,不如创建一个并非是引用的“自动”变量,这个变量直接存储“值”,并置于堆栈中。
基本类型具有的包装器类,使得可以在堆中创建一个对象,用来表示对应的基本类型。
数组
Java确保数组会被初始化(零值或者null),而且不能在它的范围之外被访问,否则抛出运行时异常。比如当创建一个数组对象时,实际上就是创建了一个引用数组,每个引用初始为null,编译器也能为基本数据类型的数组所占的内存全部清零。
无论使用哪种类型的数组,数组标识符其实只是一个引用,指向在堆中创建的一个真实对象,这个数组对象用以保存指向其他对象的引用,对象数组和基本类型数组在使用上几乎是相同的,唯一的区别就是对象数组保存的是引用,基本类型数组直接保存基本类型的值。只读成员length是数组对象唯一一个可以访问的字段,表示此数组对象可以存储多少元素,注意不是实际保存的元素个数。
多维数组本质上是嵌套数组,高维元素维护低微数组,低微数组的个数和内存自行分配管理
在Java中,数组是一种效率最高的存储和随机访问对象引用序列的方式。数组就是一个简单的线性序列,这使得元素访问非常快速,但是为这种速度所付出的代价是数组对象的大小被固定,并且在其生命周期中不可改变。
随着自动包装机制的出现,容器已经可以与数组几乎一样方便地用于基本类型中了,数组硕果仅存的优点就是效率,然而如果要解决更一般化的问题,数组就可能会受到过多的限制,因此在这些情形下你还是会使用容器。
字符串
String对象是不可变的,String类中每一个看起来会修改String值的方法,实际上都是创建了一个全新的String对象,以包含修改后的字符串内容,而最初的String对象则纹丝不动。
每当把String对象作为方法的参数时,都会复制一份引用,而该引用所指的对象一直待在单一的物理位置上,从未动过。局部引用的生命周期只在方法内,参数是为方法提供信息的,而不是想让该方法改变自己的。
如果需要在for循环拼装String,请务必手动使用StringBuilder,否则会有冗余的StringBuilder对象在for循环中被产生。StringBuffer是线程安全的实现。
正则表达式
- 除了\n和\t这种操作系统级,单\是不合法的,\\才能进入Java的正则解析控制域,代表转义,能转哪些由Java定,正向转还是逆向转都可能,比如\\d代表数字,\\+代表普通字符+,\\\\代表普通反斜杠字符(前两个解析后两个)
- 竖直线分隔表示"或",比如"abc".matches("((abc)|(def))+")
- [abc]表示包含a、b、c的任意一个字符,[^abc]表示除了a、b、c的任意一个字符
- matcher.find()是可以循环调用的,每一次find从目标字符串匹配出一次表达式并无视其它无关字符,而目标字符串可能可以匹配多次表达式,以表达式而不是目标字符串为中心;matches()则需要完全匹配。组的层次在这之下
- 组序号以左括号为准,A(B(C))D中,组0是ABCD,组1是BC,组2是C
- ?:匹配但不捕获,比如(?:abc|def|ig),可以匹配abc或def或ig但group不计入
文本替换,appendReplacement(StringBuffer sbuf,String replacement)执行渐进式替换,不像replaceFirst和replaceAll用固定字符串替换,这个允许自定义编程生成replacement。
String s = ”abd eaa“;
StringBuffer sbuf = new StringBuffer();
Patter p = Pattern.compile("[aeious]");
Matcher m = p.matcher(s):
while(m.find()) {
m.appendReplacement(sbuf, m.group().toUpperCase());
}
m.appendTail(sbuf);
枚举
在你创建enum时,这些enum都继承自Enum类,编译器会自动添加一些有用的特性。例如,它会创建toString()方法以便你可以很方便地显示某个enum实例的名字,编译器还会创建ordinary()方法用来表示某个特定enum常量的声明顺序,以及static values()方法用来按照enum常量的声明顺序,产生由这些常量值构成的数组。
下面的内容对于目前稍微有点超纲,因为笔者是在后面写完再过来的...
public abstract class Enum<E extends Enum<E>>
enum Explore { HERE, THERE }
// compile auto generate
final class Explore extends java.lang.Enum {
// that's why we use Explore.HERE
public static final Explore HERE;
public static final Explore THERE;
public static final Explore[] values();
public static Explore valueOf(java.lang.String);
static {};
}
可以通过Class对象获取所有enum实例,非枚举类调用则会报错。
enum Search { HITHER, YON }
public class UpcastEnum {
public static void main(String[] args) {
Search[] vals = Search.values();
Enum e = Search.HITHER; // Upcast
// e.values(); // No values() in Enum
for(Enum en : e.getClass().getEnumConstants()) {
System.out.println(en); // output: HITHER YON
}
}
}
所以enum无法继承其他类,但实现接口多重继承还是没问题的。
enum CartoonCharater implements Generator<CartoonCharater> {
SPLAPPY, SPANKY, PUNCHY, SILLY, BOUNCY, NUTTY, BOB;
private Random rand = new Random(47);
public CartoonCharater next() {
return values()[rand.nextInt(values().length)];
}
}
public class EnumImplementation {
public static <T> void printNext(Generator<T> rg) {
System.out.print(rg.next() + ". ");
}
public staic void main(String[] args) {
CartoonCharater cc = CartoonCharater.BOB;
for(int i = 0; i< 10; i++) {
printNext(cc);
}
}
}
Java允许程序员为enum实例编写方法,从而为每个enum实例赋予各自不同的行为,要实现常量相关的方法,你需要为enum定义一个或多个abstract方法,然后为每个enum实例实现该抽象方法。可以理解为枚举类为父类,枚举实例覆盖或实现父类方法。(笔者之前忘了这个特性,是通过在枚举中添加一个Function成员字段实现类似的效果,不过相对而言还是用继承更合适)
public enum ConstantSpecificMethod {
DATE_TIME {
String getinfo() {
return DateFormat.getDateInstance().format(new Date));
}
}
abstract String getInfo();
public static void main(String[] args) {
for(ConstantSpecificMethod csm: values()) {
System.out.println(csm.getInfo()):
}
}
}
Java引入了EnumSet,其内部使用long存储了Enum的ordinary(),一个成员占据一个bit。此外还提供了EnumMap,其内部使用数组实现,数组下标即为Enum的ordinary()。
注解
注解可以用来生成描述符文件,甚至或是新的类定义,并且有助于减轻编写样板代码的负担,每当你创建描述符性质的类或接口时,一旦其中包含了重复性的工作,那就可以考虑使用注解来简化与自动化该过程。
注解把元数据与源代码文件结合在一起,并利用Annotation API(AnnotatedElement反射接口,依托于Class对象所以并没有太多神秘色彩)为自己的注解构造处理工具,而不是保存在外部文档中,注解是真正的语言级的概念,一旦构造出来,就享有编译期的类型检查保护,程序员也拥有对源代码以及字节码检查强大的检查与操作能力。
注解的定义看起来很像接口的定义,事实上,和其他任何Java接口一样,注解也将会编译成class文件。注解的元素看起来就像是接口的方法,唯一的区别是你可以为其制定默认值。