- 对象调用方法会隐式传递this指针,因此可以在方法内部使用this调用成员方法,也可以省略。参数列表的this会由编译器自动添加,方法内调用同类实例方法也会自动添加。
2.可以在方法中返回this指针,这样可以使用方法链调用。
3.传递this指针只在需要的时候,例如A需要一个修饰过的类B对象,则b.process()。在该方法中b把自己传递给另一个工具类c.(this)。
4.只能在构造方法中调用一次this或者super的构造方法,并且放在第一句。
5.this还可表示‘本实例’这个概念。
6.静态static方法和普通成员方法的区别。
1.finalize方法不保证调用,即只在gc收集该对象时会调用,但是不应该把资源释放的任务放在finalize方法上。即该方法是完全捆绑在gc过程的发生的。如果内存足够大,对象创建的少,则这个方法可能永远不回收。写在方法中的释放语句就不会执行。数据库的链接不释放或者图像不会被擦除。finalize方法是用来释放‘本地方法’(例如调用c或者c++的方法)malloc后忘记delete的内存。在java中释放资源应该放在如finally或者普通方法中。
2.一条定理: 垃圾回收只与内存有关,垃圾回收只与内存有关,垃圾回收只与内存有关。 看看深入理解java虚拟机。
3.finalize方法有一种用法,就是在finalize方法中检查对象的状态是否合规,不合规就输出一条信息。即用来检查自定义的对象终结条件。即检查是否当对象释放时某个东西处于什么状态。(需要调用system.gcc出发GC。),如果这种用法需要调用父类的finalize检查,则super.finalize即可。
4.java的堆分配可以媲美C的堆栈分配,因为java只是单方向移动分配指针。配合垃圾收集算法,建立起近乎无限的内存分配模型。
5.不适用引用计数是因为防止循环引用从而释放不了对象。java使用从表面的所有引用变量入手,然后是对象的引用成员变量,循环深入,遍历整个可访问的对象网络。
6.停止-复制 方法,将进程暂停,将内存中一个区的对象移到另一个区,然后修正所有引用变量,比如堆栈和静态存储区(方法区)。完毕后内存空闲区域是连续的。
7.标记-清理 方法,适合垃圾较少或无垃圾的情况。并行的遍历并标记存活对象,完毕后再一起释放,此时内存空闲区域是不连续的。根据GC的实现这时也可以运行内存整理线程。也需要进程暂停。
8.对于小对象,实行 复制整理 策略,大对象占用一块的区域(多个小对象的集合)。块不进行复制,而是记录一个代数(generation count),有一个引用则代数增加。
9.实际上,这两种方式会自适应切换, 即当内存存在许多碎片时会切换到 停止-复制。 若大部分对象都存活,GC收集器效率很低,则切换为标记-清理。
10.综合上面的,即GC是一个 : 分代的,自适应的,停止-复制,标记-清理的收集器。
11.HotSpot虚拟机中还有其他的提升性能的技术,例如加载器相关 的JIT即即时编译器的技术,可以把程序全部或部分翻译为本地机器码(本来是JVM的工作)。当需要装载一个类时,编译器会先找到class文件,并读入内存,使用惰性编译,即只在必要时才编译,代码每次被执行时才编译,所以执行的越多,速度越快。避免了全部编译的增加时间花费并且编译后增大的机器码体积(字节码比机器码小)引发页面调度。
12.从堆中分配的变量初始时是清零的。局部变量不给值不过编译。
13.如果要为变量赋值,防止前向引用错误。即
int i = f();
int j = g(i);
int f(){reutrn 11;}
int g(int n){return n10;}
正确,但是下面错误,因为我的另外一篇博客写了,java的成员变量初始化是按照顺序初始化的。即使在类中定义分散开。
int j = g(i);
int i = f();
int f(){reutrn 11;}
int g(int n){return n10;}
- 只要牢记类的初始化顺序,就知道初始化值是不是空值还是有值。
- 因为static成员变量分配在静态区,而堆区都是分配时就已经清零的,所以static变量也有默认值。
16.利用让引用置null,可以测试不同内存区的清理算法和时机。
17.静态初始化只在必要时进行,即用到这个类的时候才加载(JIT技术),此时在方法区建立class对象,静态数据区。如果类根本不会被使用,JIT也就不会可能装载他的class文件进行编译,因此也就不能被初始化。即使是有main的类,因为要执行main,也要先执行当前类的静态初始化。只要加载到内存方法区了,就不会再执行静态有关的块了。如果代码以后不用这个类,将会被Full GC收集。
18.根据上面的描述,可以知道构造方法是静态的。
19.int[] 仅仅是个引用而已。初始化可以使用 int []a={1,2,3,4};java不允许 int[5] 这种声明。
20.包装类的数组可以使用两种立刻赋值方式:
Integer [] a = {
new Integer(5),
new Integer(6),
3, //自动装箱,并且那个‘,’可以带上
}
Integer [] b = new Integer[]{
new Integer(5),
new Integer(6),
3, //自动装箱,并且那个‘,’可以带上
}
21.可以直接把new Object[]{1,2,"a"}作为参数传给方法,此时因为栈上引用的存在不会被GC。
22.print方法会调用输出对象的toString()方法,如果是基本数据类型会自动装箱。如果没重写该方法则调用Object的原始方法,则会打印类名和对象地址。
23.参数数量可变的写法: f(Object... args) { for(Object o: args){....}}。f(int... args){ args.getClass();}等等。然后就不用像第21条那样非得传入一个数组对象了。但是这只是编译器的语法糖,且支持传入的基本类型而方法声明的是包装类的自动装箱。如果你传入的本来是一个数组,则编译器就不会帮你组成数组了。
24.创建枚举:
public enum Season{SPRING,SUMMER=100,AUTUMN,WINTER}
编译器会自动添加每一个具名的toString方法。还有ordinal方法,表示某个具名的声明顺序(从0开始,也可以自己制定一个开始),还有一个static values方法,产生按照声明顺序组成的枚举引用数组。
使用枚举: Season s = Season.SPRING;
枚举用在switch中很方便,case可以直接使用具名。