一个类的静态成员在类的实例gc后,不会销毁。
对象引用强度
- 强引用Strong Reference
就是指在代码之中普遍存在的,类似:“Object objectRef = new Obejct”。只要强引用还存在,永远不会被GC清理。
- 软引用SoftReference
当Jvm内存不足时(内存溢出之前)会被回收。
SoftReference很适合用于实现缓存。
ReferenceQueue<Man> referenceQueue = new ReferenceQueue<Man>();
SoftReference<Man> softReference = new SoftReference<Man>(new Man(), referenceQueue);
/*If this reference object has been cleared, either by the program or by the garbage collector, then this method returns null.*/
softReference.get();
//This method is invoked only by Java code; when the garbage collector enqueues references it does so directly, without invoking this method.
softReference.enqueue(); //入队列,即表示当前Reference对象已回收。
//Tells whether or not this reference object has been enqueued
softReference.isEnqueued();
- 弱引用WeakReference
弱引用的对象,只能生存到下一次GC前,当GC工作时,无论内存是否足够,都会回收掉弱引用关联的对象。
GC时,清空Reference对象的referent属性(即执行Reference.clear()),再将Reference对象加入该对象关联的ReferenceQueue中。
ReferenceQueue<Man> queue = new ReferenceQueue<Man>();
WeakReference<Man> ref = new WeakReference<Man>(new Man(), queue);
Assert.assertNotNull(ref.get());
Object obj = null;
obj = queue.poll();
Assert.assertNull(obj);
System.gc();
Assert.assertNull(ref.get());
Thread.sleep(10);// 入队列操作较慢,要等一下。
obj = queue.poll();
Assert.assertNotNull(obj);
- 虚引用PhantomReference
一个虚引用的对象,随时都会被gc。为一个对象设置虚引用关联的唯一目的就是希望能在这个对象被GC回收时收到一个系统通知。
phantomReference.get方法永远返回null, 当从内存中删除时,调用isEnqueued会返回true
- 为什么要用ReferenceQueue
Object obj = new Object();
Ref ref = new Ref(obj);
对于以上代码,我们希望当一个对象被gc掉的时候通知用户线程,进行额外的处理时,就需要使用引用队列ReferenceQueue。当一个obj被gc掉之后,其相应的包装类ref对象会被放入queue中。我们可以从queue中获取到相应的对象信息,进行额外的处理。比如反向操作,数据清理等。
比如weakHashMap使用weakReference当作key来进行数据的存储,当key中的引用被gc掉之后,它不是自动将相应的entry给移除掉,而是我们调用get,size,put等方法时,weakHashMap从key的ReferenceQueue中获取引用信息,从而使得被gc掉的key值所对应的entry从map中被移除,然后再处理我们的业务调用。
类的初始化
对于有static final 修饰的变量的类,当引用该变量时,该类不会被构造。
对于有static变量的类,当第一次实例化该类,则静态变量会初始化,第二次不会。
父类静态属性 -> 父类静态块 -> 子类静态属性 -> 子类静态块 -> 父类属性 -> 父类块 -> 父类构造器 -> 子类属性 -> 子类块 -> 子类构造器
相同等级下先定义的先初始化。
class SuperClass{
public SuperClass(){
System.out.println("SuperClass of constructor");
m();
}
public void m(){
System.out.println("SuperClass.m()");
}
}
public class SubClassTest extends SuperClass {
private int i = 10;
public SubClassTest(){
System.out.println("SubClass of constructor");
super.m();
m();
}
public void m(){
System.out.println("SubClass.m(): i = " + i);
}
}
测试:
SuperClass t = new SubClassTest();
在生成对象时,父类调用的M()方法,不是父类的 M()方法,而是子类中被重写了的M()方法!!子类的private int i 也被父类访问到,和private的成员只能在本类使用的原则相违背。其实我们说的这条原则是编译期间所遵守的,在JAVA程序的编译期间,它只检查语法的合法性,在JAVA的JVM中,即运行期间,不管你声明的什么,对于JVM来说都是透明的,而动态引用是在运行期间执行的,所以能拿到SubClass的private成员,只是此时还没执行 i = 10,所以只能将i赋予初值0。
抽象类和接口
记住一个原则:接口目的是抽象,抽象类目的是复用;
接口可以继承接口,一个抽象类或具体类可以实现多个接口(对于模块功能扩展,不需要修改原有接口)。
抽象类在实现接口中可以不用实现接口的所有方法。
接口的方法都是抽象方法。抽象类比接口灵活(弹性),多层抽象类的继承可以让整个主体更有扩展性和层次性。
当一个类实现一个接口时,如果不能完全实现就把这个类写作抽象类,就跟一个类如果不能实现他所继承的抽象类的全部抽象方法,那这个类也要命名为抽象类。
接口中定义变量必须为public static final,一般默认,可省略。
局部内部类
就是在对象的方法内部定义的类。而该局部内部类中的方法访问该局部内部类所在的方法中的局部变量时,该局部变量要加上final。
原因是:编译程序实现上的困难:内部类对象的生命周期会超过局部变量的生命期。当该方法被调用时,该方法中的局部变量在栈中被创建,当方法调用结束时,退栈,这些局部变量全部死亡。而内部类生命期,与其它类一样,只有没有其它人再引用它时,它才能死亡。
为什么局部变量定义为final可以呢?因为final变量为不可改变,编译程序具体实现:将所有的局部内部类对象要访问的final型局部变量,拷贝到局部内部类中。
在Java8中,这种限制被新的概念“effectively final”取代了。它的意思是你可以声明为final,也可以不声明final但是要按照final来用,也就是一次赋值永不改变。即保证它加上final前缀后不会出编译错误。
注:不管变量是不是final,他的生命周期都在于{}中。
泛型
使得类型错误可以在编译时被捕获,而不是在运行时当作 ClassCastException 展示出来;消除强制类型转换,这使得代码更加可读。
类型擦除:
C#里面泛型无论在程序源码中、编译后的IL中(Intermediate Language,中间语言,这时候泛型是一个占位符)或是运行期的CLR中都是切实存在的,List<int>与List<String>就是两个不同的类型,它们在系统运行期生成,有自己的虚方法表和类型数据,这种实现称为类型膨胀,是真实泛型。
Java泛型只在程序源码中存在,在编译后的字节码文件中,就已经被替换为原来的原生类型(Raw Type),并且在相应的地方插入了强制转型代码,因此对于运行期的Java来说,ArrayList<int>与ArrayList<String>就是同一个类。这种实现方法称为类型擦除,是伪泛型。
所以以下代码method()无法重载:
public class GenericTypes {
public static void method(List<String> list) {
......
}
public static void method(List<Integer> list) {
......
}
}
方法的Signature属性是在字节码层面存储一个方法的特征签名,这个属性中保存了参数化类型的信息。虽然擦除法对方法体的源码的字节码进行类型擦除,但由于Signature,我们还是能通过反射API取得参数化类型。
对象引用
一个父类对象的引用指向一个子类对象,从编译角度,该引用只能调用父类中定义的方法和变量;如果子类中重写了父类中的一个方法,那么会调用子类中的这个方法;变量不能被重写(覆盖),”重写“的概念只针对方法。
对象引用在内存中两个对象都生成了,子类、父类变量都生成了。
class Father {
String name = "Father";
public void func1() {
func2(); // 若子类实现了func2(),则调用子类的func2()
}
public void func2() {
System.out.println("Father func2");
}
}
class Child extends Father {
String name = "Child";
public void func2() {
System.out.println("Child func2");
}
}
测试:
Child child1 = new Child();
Father child2 = new Child();
child2.func1(); // Child func2
// 与方法重载不同,属性是无法重载的,属性跟类型走
System.out.println(child1.name); // Child
System.out.println(child2.name); // Father
Enum
Enum用于数据结构是稳定的,而且数据个数是有限的“数据集”。
//其内部实现
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable
public enum Man {
//wangliqiu、centifocus必定是public static final。
wangliqiu, centifocus;
public String name;
public int age;
public String sex;
}
//类比以上enum
public static class Man {
public static final Man wangliqiu = new Man();
public static final Man centifocus = new Man();
public String name;
public int age;
public String sex;
}
示例:
public enum ColorEnum {
red, //
green, //
yellow, //
blue;
}
public static void main(String[] args) {
ColorEnum colorEnum = ColorEnum.blue;
switch (colorEnum) {
case red:
System.out.println("color is red");
break;
case blue:
System.out.println("color is blue");
break;
}
// 遍历
for (ColorEnum color : ColorEnum.values()) {
System.out.println(color);
}
System.out.println("枚举索引位置:" + ColorEnum.red.ordinal());// 0
// 枚举默认实现java.lang.Comparable接口,因为ENUM抽象类实现了。
System.out.println(ColorEnum.red.compareTo(ColorEnum.green));// -1
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。