重载
基本类型的重载
当传入较小精确度的数值时,会调用拥有最接近该精确度形参的方法。
当传入较大精确度的数值时,需要至少窄化转换成所有重载方法中最大精确度的数值,否则编译器将报错。
不能通过方法的返回值来区分重载方法
构造器的重载
在构造器中可以调用其它重载构造器,但只能调用一个,且必须放在该构造器的首行。
初始化
静态成员的初始化
public class Initialization {
public static final void main(String[] args) {
new First();
}
}
class First{
Second second=new Second();
static Staticer1 staticer=new Staticer1();
public First() {
System.out.println("First");
}
}
class Second{
public Second() {
System.out.println("Second");
}
static Staticer2 staticer2=new Staticer2();
}
class Staticer1{
public Staticer1() {
System.out.println("Staticer1");
}
}
class Staticer2{
public Staticer2() {
System.out.println("Staticer2");
}
}
Staticer1
Staticer2
Second
First
静态优先,当静态成员所在类没有用到则不会初始化该静态成员
构造器外优先,构造器外面的成员初始化完才开始初始化构造器中的
成员变量会自动初始化,如定义数组如果没有初始化会默认全部至为0,若对象引用没有初始化会默认至为null
但局部变量不会,需要自己初始化
清理
引用计数:引用连接至对象时,引用计数加一,当离开作用域或被至为null时,则减一。
- 在整个生命周期中发生
- 若存在循环引用现象会导致对象需要被清理却计数不为0却的现象
-
看到P161后的补充:
public class OOer{ public static final void main(String[] args) { OOers2 os2=new OOers2(); OOers1[] os1= {new OOers1(os2),new OOers1(os2),new OOers1(os2),new OOers1(os2),new OOers1(os2)}; for(OOers1 o1:os1) { o1.dispose(); } } protected void dispose() { System.out.println("OOers dispose"); } } class OOers1{ private static int count=0; private final int id=count++; private OOers2 os2; public OOers1(OOers2 os2) { System.out.println("OOers1 Creating:"+this); this.os2=os2; os2.add(); } protected void dispose() { System.out.println("OOers1 dispose"); os2.dispose(); } public String toString() { return "OOers2 id:"+id; } } class OOers2{ private int flag=0; public void add() { ++flag; } protected void dispose() { if(--flag==0) { System.out.println("OOers2 dispose"); } } }
OOers1 Creating:OOers2 id:0
OOers1 Creating:OOers2 id:1
OOers1 Creating:OOers2 id:2
OOers1 Creating:OOers2 id:3
OOers1 Creating:OOers2 id:4
OOers1 dispose
OOers1 dispose
OOers1 dispose
OOers1 dispose
OOers1 dispose
OOers2 dispose循环引用:
public class Circular{ private First first=new First(); private Second second=new Second(); private void test(){ first.s=second; second.f=first; } }
first拥有Second的引用,而second也拥有First的引用。
当该类中的test方法执行完后,gc理应回收first和second对象,但要回收First得先回收second所拥有的First引用,而要回收second所拥有的First引用得先回收first拥有的Second的引用。
故循环引用问题让gc不能通过引用计数方法清理。
解决“交互自引用的对象组”问题的方法:对于“活”的对象一定能最终追溯到位于堆栈或静态存储区的引用。
- 对每个引用先找到其对象,然后追溯位于堆栈或静态存储区的所有引用
- 直到“根源于堆栈和静态存储区的引用“所形成的网络全部被访问完为止
-
找到”活“的对象后:
停止-复制:将程序停下,然后将”活“的对象复制到另一个堆中,没被转移过来的就是待清理的对象。
- 被复制过来的对象是相邻排列的
- 被复制过来的对象的所有引用都需要修正,堆和静态存储区的引用可以被直接修改,其它引用需要通过遍历来发现
- 由于对象需要在两个堆间转换,以及有时没有垃圾或较少垃圾时采用这种方法效率极低
-
标记-清扫:类似于停止-复制遍历所有引用,找到”活“的对象,但不复制只作标记,当所有标记都作完后就清理。
- 会导致对象没有相邻排列,需要重新整理对象
自适应:即结合停止-复制和标记-清扫来使用,当垃圾较多时运用第一种方法,当垃圾较少时用第二种方法。 -
即时编译器(JIT):将程序全部或部分代码编译成机器码(本应是JVM的工作)。
- 找到class文件将字节码全部翻译成机器码
- 惰性评估:只有在必要时才编译
-
析构函数 finalize
在垃圾回收器执行时调用被回收对象的finalize()方法,可以覆盖此方法来实现对一些JVM无法回收的资源的回收。一旦垃圾回收器准备释放对象占用的内存,将首先调用该对象的finalize()方法,而JVM在下一次垃圾回收动作发生时,才真正回收对象占用的内存空间。
public class Finalizesr { public static final void main(String[] args) { //Finalizesr1 f1=new Finalizesr1(); new Finalizesr1(); System.gc(); } } class Finalizesr1{ @Override protected void finalize() throws Throwable { System.out.println("I am killed"); super.finalize(); } }
若代码中用注释掉的语句替换掉下一条语句,则finalize不会被调用,因为该内存依然有引用指向它
可变参数
public class ChangeParam{ //此处参数已相当于String[] args public static final void main(String...args){ for(String str:args){ System.out.println(str); } } }
向main函数传参的方法为:
命令行下:java xxx a b c
在eclipse下:
run as->run configurations->Argument
然后在program arguments中输入要传入的参数,一行一个枚举类型
enum People{ Dog,Cat,Bird; } public class Enumer { public static final void main(String[] args) { EnumerSwitch enumerSwitch=new EnumerSwitch(EnumerSwitch.Animal.Cat); enumerSwitch.judge(); } } class EnumerSwitch{ Animal animal = null; protected static enum Animal{ Dog,Cat,Bird; } public EnumerSwitch(Animal animal) { this.animal=animal; } protected void judge() { switch (animal) { case Dog: System.out.println("Dog"); break; default: break; } } }
编译时常量与运行时常量
public class TestFinal1 { public static final void main(String...args) { System.out.println(TestFinal2.i); System.out.println(TestFinal2.j); } } class TestFinal2{ static { System.out.println("static"); } final static int i=5; final static int j="test".length(); }
5
static
4i为编译时常量,而j为运行时常量。
编译时常量:当类还没加载时就加载
运行时常量:当类加载完后才加载,而类加载完后首先执行static代码块,所以j会在static后面空白final
空白final指的是在定义常量时可以先不赋初值,但必须在域或构造方法中赋值。
多态
- 讲一个方法调用同一个方法主体关联起来被称为绑定。
- 在运行时根据对象的类型进行绑定称为后期绑定,运行时绑定或动态绑定。
- Java中除了static方法和final方法不是动态绑定的外其它的都是动态绑定。
-
多态的局限
- 无法覆盖私有方法
- 域和静态方法的调用多态无效
eg:
public class MainClass{ public String s="String_MainClass"; private void f() { System.out.println("MainClass"); } public static void sf(){ System.out.println("static_MainClass"); } public static final void main(String...args) { MainClass m=new ChildClass(); m.f(); System.out.println(s); m.sf(); } } public class ChildClass extends MainClass{ public String s="String_ChildClass"; public static void sf(){ System.out.println("static_ChildClass"); } public void f() { System.out.println("ChildClass"); } }
MainClass
String_MainClass
static_MainClasspublic class MainClass{ protected void f() { System.out.println("MainClass"); } public static final void main(String...args) { MainClass m=new ChildClass(); m.f(); } } public class ChildClass extends MainClass{ public void f() { System.out.println("ChildClass "); } }
ChildClass
构造器内部多态方法导致的问题
public class Conster extends Conster1{ private int r=1; protected void f() { System.out.println("Conster_f:"+r); } public static final void main(String...args) { Conster c=new Conster(); c.f(); } } class Conster1{ public Conster1() { System.out.println("run before f"); f(); System.out.println("run after f"); } protected void f() { System.out.println("Conster1_f"); } }
run before f
Conster_f:0
run after f
Conster_f:1该代码初始化的实际过程为:
- 在其他任何事情发生前,将分配给对象的存储空间初始化为二进制的零。
- 然后调用其基类的构造器,然后会调用在被覆盖的方法f,由步骤1可知此时的r并不是1而是0。
- 按照声明的顺序调用成员的初始化方法。
- 调用导出类的构造器主体。
总结:应该尽量避免在构造方法中调用其它方法,唯一安全的是调用final方法(private方法)。
协变返回类型
导出类覆盖基类的方法返回类型可以不同(在两个不同返回类型是继承关系的基础上)。
public class Xiebianer{ public static final void main(String...args) { Xiebianer2 xb2=new Xiebianer1(); System.out.println(xb2.xb()); } } class Xiebianer1 extends Xiebianer2{ Xiebianer3 xb() { return new Xiebianer3(); } } class Xiebianer2{ Xiebianer4 xb() { return new Xiebianer4(); } } class Xiebianer3 extends Xiebianer4{ public String toString() { return "Xiebianer3"; } } class Xiebianer4{ public String toString() { return "Xiebianer4"; } }
Xiebianer3
需要注意的是:如果覆盖方法的返回类型不同,那么导出类方法的返回类型与基类方法的返回类型的关系必须分别是导出类和基类的关系。
例如如果用Xiebianer4类型的方法去覆盖Xiebianer3类型的方法就会出错,因为:Xiebianer4是Xiebianer3的基类。//... class Xiebianer1 extends Xiebianer2{ Xiebianer4 xb() { return new Xiebianer4(); } } class Xiebianer2{ Xiebianer3 xb() { return new Xiebianer3(); } } //...
出错
“多重继承”
菱形问题:当有两个类a,b都继承了同一个类c,而类d又都继承了a,b,那类d就有两个祖类c,此时就会出现一些冲突内容,如a重写了c的f方法,而b也重写了f方法。该问题存在于支持多重继承语言中,如c++,python。
用多接口防止菱形问题
public interface IDoubler extends IDoubler1,IDoubler2{ public void f(); }
public interface IDoubler1 extends IDoubler3{ public void f1(); } public interface IDoubler2 extends IDoubler3{ public void f2(); } public interface IDoubler3 { public void f3(); }
public class DoInterfacer implements IDoubler{ @Override public void f1() { // TODO Auto-generated method stub System.out.println("f1"); } @Override public void f3() { // TODO Auto-generated method stub System.out.println("f3"); } @Override public void f2() { // TODO Auto-generated method stub System.out.println("f2"); } @Override public void f() { // TODO Auto-generated method stub System.out.println("f"); } }
public class ApplyIter{ IDoubler1 id1=new DoInterfacer(); public static final void main(String...args) { ApplyIter ai=new ApplyIter(); ai.id1.f1(); } }
f1
组合接口时名字冲突现象
public class Typer extends TypExtender implements I2{ } public interface I2 { public void f(); } public class TypExtender { public int f() { return 1; } }
public interface I1 extends I2,I3{ } public interface I2 { public void f(); } public interface I3 { public int f(); }
The return types are incompatible for the inherited methods I2.f(), TypExtender.f()
The return types are incompatible for the inherited methods I2.f(), I3.f()Readable与Scanner的用法
import java.io.IOException; import java.nio.CharBuffer; import java.util.Random; import java.util.Scanner; public class Readabler implements Readable{ private int count=0; public Readabler(int count) { this.count=count; } private Random r=new Random(55); public static final char[] str1="abcdddddddssssdefghijddddddddddddklmn".toCharArray(); public static final char[] str2="opqrstuvwxdddsssssssssssdddddyzqqqq".toCharArray(); @Override public int read(CharBuffer cb) throws IOException { if(count--==0) { return -1; } for(int i=0;i<5;i++) { cb.append(str1[r.nextInt(str1.length)]); cb.append(str2[r.nextInt(str2.length)]); } cb.append(" "); return count; } public static final void main(String[] args) { Scanner s=new Scanner(new Readabler(5)); while(s.hasNext()) { System.out.println(s.next()); } } }
接口的任何域都自动是static和final的,接口修饰符只能是public和default
工厂设计模式
直接get获取对象,而不需要new
interface IGraphic{ public void drawer(); } interface IGraphicFactory{ public IGraphic getIGraphic(); } class Circle implements IGraphic{ @Override public void drawer() { System.out.println("Circle"); } } class Bicycle implements IGraphic{ @Override public void drawer() { System.out.println("Bicycle"); } } class CircleFactory implements IGraphicFactory{ @Override public IGraphic getIGraphic() { return new Circle(); } } class BicycleFactory implements IGraphicFactory{ @Override public IGraphic getIGraphic() { return new Bicycle(); } } public class GraphicFactorer { public void draw(IGraphicFactory igf ) { IGraphic ig=igf.getIGraphic(); ig.drawer(); } public static final void main(String[] args) { GraphicFactorer gf=new GraphicFactorer(); gf.draw(new CircleFactory()); gf.draw(new BicycleFactory()); } }
Circle
Bicycle